tempGhostData is created just before it's assigned, at the beginning of Ghost_WaitframeLight(), and the pointer is cleared at the end of that function. The array only exists until the script resumes in the next frame; Set and GetEnemyProperty() are meant to be called from other scripts during that time.
It's a bit complicated, but there's nothing out of the ordinary going on there. As long as array pointers are still integers, it should be fine as long as they're not greater than 65535.
Ah-ha.
Moosh filed a report, bringing it to my attention, and when I tried using it within a ghost script to test the issue, I could easily replicate it.
Essentially, it doesn't have proper access to the pointer that it is trying to use, if called at the wrong time, and it floods the log with invalid array errors.
The behaviour is still spotty. If it's only intended to be used after the array generated by ghost_waitframe is called, that's fine-enough.
I think it breaks when a script with a higher priority than the ghosted ffc enemy that you are trying to affect calls it, as at that time the array pointer isn't established.
I sanity guarded it as follows:
void SetEnemyProperty(npc enemy, int property, float newValue) { //LogPrint("Setting ghost property: %d\n", property); if((enemy->Misc[__GHI_NPC_DATA]&0x10000)!=0) { float ptr=enemy->Misc[__GHI_NPC_DATA]&0xFFFF; //LogPrint("ptr is: %f \n", ptr); if ( ptr > 0 ) { //LogPrint("ptr size is: %f \n", SizeOfArray(ptr)); if ( SizeOfArray(ptr) == 24 ) { //TraceS("SET Pointer validated.\n"); ptr[property]=newValue; } } //else TraceS("SET Pointer not validated.\n"); } switch(property) { case ENPROP_X: enemy->X=newValue; return; case ENPROP_Y: enemy->Y=newValue; return; case ENPROP_Z: enemy->Z=newValue; return; case ENPROP_JUMP: enemy->Jump=newValue; return; case ENPROP_DIR: enemy->Dir=newValue; return; case ENPROP_HP: enemy->HP=newValue; return; default: enemy->CSet=newValue; return; } } float GetEnemyProperty(npc enemy, int property) { if((enemy->Misc[__GHI_NPC_DATA]&0x10000)!=0) { float ptr=enemy->Misc[__GHI_NPC_DATA]&0xFFFF; //LogPrint("ptr is: %f \n", ptr); if ( ptr > 0 ) { //LogPrint("ptr size is: %f \n", SizeOfArray(ptr)); if ( SizeOfArray(ptr) == 24 ) { return ptr[property]; } } else { //TraceS("GET Pointer not validated.\n"); switch(property) { case ENPROP_X: return enemy->X; case ENPROP_Y: return enemy->Y; case ENPROP_Z: return enemy->Z; case ENPROP_JUMP: return enemy->Jump; case ENPROP_DIR: return enemy->Dir; case ENPROP_HP: return enemy->HP; default: return enemy->CSet; } } } else { switch(property) { case ENPROP_X: return enemy->X; case ENPROP_Y: return enemy->Y; case ENPROP_Z: return enemy->Z; case ENPROP_JUMP: return enemy->Jump; case ENPROP_DIR: return enemy->Dir; case ENPROP_HP: return enemy->HP; default: return enemy->CSet; } } }I used switch-case here purely for efficiency. I was thinking of going in and making the whole header more efficient in 2.55 (ZASM output), but this same if statements would work in 2.50.x, to verify the array pointer, prior to trying to access it.