// The constants and functions in this file are provided only for backward
// compatibility. They should not be used in new scripts.

const int GHI_FLAGS             = 9;
const int GHI_KNOCKBACK_COUNTER = 10;
const int GHI_FLASH_COUNTER     = 11;
const int GHI_PREV_X            = 12;
const int GHI_PREV_Y            = 13;
const int GHI_PREV_HP           = 14;

const int GHF_GOT_HIT  = 01000000000b;
const int GHF_IN_USE   = 10000000000b;
const int GHF_INTERNAL = 11000000000b;
const int GHI_IN_USE = 15;

const int EWF_DEAD         = 010000b;
const int EWF_IS_GHZH_EWPN = 100000b;
const int EWF_INTERNAL     = 110000b;
const int EWI_XPOS         = 5;
const int EWI_YPOS         = 6;
const int EWI_WORK         = 7;
const int EWI_WORK_2       = 8;
const int EWI_MOVEMENT     = 9;
const int EWI_MOVEMENT_ARG = 10; 
const int EWI_LIFESPAN     = 11;
const int EWI_LIFESPAN_ARG = 12;
const int EWI_ON_DEATH     = 13;
const int EWI_ON_DEATH_ARG = 14;
const int EWI_FLAGS        = 15;

void GhostInit(ffc this, npc ghost)
{
    if(ghost->ASpeed==256 && GH_BLANK_TILE>0)
        ghost->OriginalTile=GH_BLANK_TILE;
    ghost->Extend=3;
    ghost->TileWidth=this->TileWidth;
    ghost->TileHeight=this->TileHeight;
    ghost->HitWidth=16*this->TileWidth;
    ghost->HitHeight=16*this->TileHeight;
    ghost->X=this->X;
    ghost->Y=this->Y;
    ghost->CSet=this->CSet;
    ghost->Misc[GHI_FLASH_COUNTER]=0;
    ghost->Misc[GHI_PREV_HP]=ghost->HP;
    ghost->Misc[GHI_IN_USE]=1;
}

npc GhostInitCreate(ffc this, int enemyID)
{
    npc ghost=Screen->CreateNPC(enemyID);
    GhostInit(this, ghost);
    return ghost;
}

npc GhostInitWait(ffc this, int enemyIndex, bool useEnemyPos)
{
    int combo=this->Data;
    this->Data=0;
    npc ghost;
    
    for(int i=0; i<4; i++)
    {
        if(Screen->NumNPCs()>=enemyIndex)
        {
            ghost=Screen->LoadNPC(enemyIndex);
            ghost->Extend=3;
            ghost->TileWidth=this->TileWidth;
            ghost->TileHeight=this->TileHeight;
            if(ghost->ASpeed==256 && GH_BLANK_TILE>0)
                ghost->OriginalTile=GH_BLANK_TILE;
            ghost->Extend=3;
            ghost->TileWidth=this->TileWidth;
            ghost->TileHeight=this->TileHeight;
            ghost->HitWidth=16*this->TileWidth;
            ghost->HitHeight=16*this->TileHeight;
            ghost->CSet=this->CSet;
            ghost->Misc[GHI_FLASH_COUNTER]=0;
            ghost->Misc[GHI_PREV_HP]=ghost->HP;
            ghost->Misc[GHI_IN_USE]=1;
            this->Data=combo;
            
            if(useEnemyPos)
            {
                this->X=ghost->X;
                this->Y=ghost->Y;
            }
            else
            {
                ghost->X=this->X;
                ghost->Y=this->Y;
            }
            
            return ghost;
        }
        Waitframe();
    }
    // Timed out
    Quit();
}

npc GhostInitWait2(ffc this, int enemyID, bool useEnemyPos)
{
    int combo=this->Data;
    this->Data=0;
    npc ghost;
    
    for(int i=0; i<4; i++)
    {
        // Cycle through enemies to find the right one
        for(int j=1; j<=Screen->NumNPCs(); j++)
        {
            ghost=Screen->LoadNPC(j);
            // Wrong ID?
            if(ghost->ID!=enemyID)
                continue;
            // Already in use?
            if((ghost->Misc[GHI_IN_USE])!=0)
                continue;
            
            // Found it; initialize
            if(ghost->ASpeed==256 && GH_BLANK_TILE>0)
                ghost->OriginalTile=GH_BLANK_TILE;
            ghost->Extend=3;
            ghost->TileWidth=this->TileWidth;
            ghost->TileHeight=this->TileHeight;
            ghost->HitWidth=16*this->TileWidth;
            ghost->HitHeight=16*this->TileHeight;
            ghost->CSet=this->CSet;
            ghost->Misc[GHI_FLASH_COUNTER]=0;
            ghost->Misc[GHI_PREV_HP]=ghost->HP;
            ghost->Misc[GHI_IN_USE]=1;
            this->Data=combo;
            
            if(useEnemyPos)
            {
                this->X=ghost->X;
                this->Y=ghost->Y;
            }
            else
            {
                ghost->X=this->X;
                ghost->Y=this->Y;
            }
            
            return ghost;
        }
        Waitframe();
    }
    // Timed out
    Quit();
}

npc GhostInitSpawn(ffc this, int enemyID)
{
    npc ghost=SpawnNPC(enemyID);
    if(ghost->ASpeed==256 && GH_BLANK_TILE>0)
        ghost->OriginalTile=GH_BLANK_TILE;
    ghost->Extend=3;
    ghost->TileWidth=this->TileWidth;
    ghost->TileHeight=this->TileHeight;
    ghost->HitWidth=16*this->TileWidth;
    ghost->HitHeight=16*this->TileHeight;
    ghost->CSet=this->CSet;
    ghost->Misc[GHI_FLASH_COUNTER]=0;
    ghost->Misc[GHI_PREV_HP]=ghost->HP;
    ghost->Misc[GHI_IN_USE]=1;
    
    return ghost;
}

void GhostInit(ffc this, npc ghost, int flags)
{
    GhostInit(this, ghost);
    SetFlags(this, ghost, flags);
}

npc GhostInitCreate(ffc this, int enemyID, int flags)
{
    npc ghost=GhostInitCreate(this, enemyID);
    SetFlags(this, ghost, flags);
    return ghost;
}

npc GhostInitWait(ffc this, int enemyIndex, bool useEnemyPos, int flags)
{
    npc ghost=GhostInitWait(this, enemyIndex, useEnemyPos);
    SetFlags(this, ghost, flags);
    return ghost;
}

npc GhostInitWait2(ffc this, int enemyID, bool useEnemyPos, int flags)
{
    npc ghost=GhostInitWait2(this, enemyID, useEnemyPos);
    SetFlags(this, ghost, flags);
    return ghost;
}

npc GhostInitSpawn(ffc this, int enemyID, int flags)
{
    npc ghost=GhostInitSpawn(this, enemyID);
    SetFlags(this, ghost, flags);
    return ghost;
}

void SetFlags(ffc this, npc ghost, int flags)
{
    ghost->Misc[GHI_FLAGS]=flags|(ghost->Misc[GHI_FLAGS]&GHF_INTERNAL);
    if((flags&GHF_4WAY)!=0)
        this->Data=this->Data+ghost->Dir;
}

void DrawSpawnAnimation(ffc this, npc ghost)
{
    int i;
    int j;
    lweapon graphic;
    
    int combo=this->Data;
    this->Data=0;
    ghost->CollDetection=false;
    ghost->DrawXOffset=32768;
    
    for(i=0; i<this->TileWidth; i++)
    {
        for(j=0; j<this->TileHeight; j++)
        {
            graphic=Screen->CreateLWeapon(LW_SCRIPT10);
            graphic->CollDetection=false;
            graphic->UseSprite(GH_SPAWN_SPRITE);
            graphic->X=this->X+16*i;
            graphic->Y=this->Y+16*j;
            
            if(graphic->NumFrames==0)
                graphic->NumFrames=3;
            if(graphic->ASpeed==0)
                graphic->ASpeed=4;
            
            graphic->DeadState=graphic->NumFrames*graphic->ASpeed;
        }
    }
    
    Waitframes(graphic->NumFrames*graphic->ASpeed);
    
    this->Data=combo;
    ghost->CollDetection=true;
    ghost->DrawXOffset=0;
}

bool GhostWaitframeM(ffc this, npc ghost, float x, float y, float z, bool clearOnDeath, bool quitOnDeath)
{
    // Set direction
    if((ghost->Misc[GHI_FLAGS]&GHF_SET_DIRECTION)!=0 && (this->X!=x || (this->Y+ghost->Z)!=y))
    {
        // Figure out the base combo, in case GHF_4WAY is set
        int baseCombo=this->Data-ghost->Dir;
        
        if(Abs(this->X-x)>Abs((this->Y+ghost->Z)-y))
        {
            if(this->X>x)
                ghost->Dir=DIR_LEFT;
            else
                ghost->Dir=DIR_RIGHT;
        }
        else
        {
            if(this->Y+ghost->Y>y)
                ghost->Dir=DIR_UP;
            else
                ghost->Dir=DIR_DOWN;
        }
        
        // Change combo to match
        if((ghost->Misc[GHI_FLAGS]&GHF_4WAY)!=0)
            this->Data=baseCombo+ghost->Dir;
    }
    
    // Draw over if high enough

    if((ghost->Misc[GHI_FLAGS]&GHF_SET_OVERLAY)!=0)
    {
        if(z>=GH_DRAW_OVER_THRESHOLD && !this->Flags[FFCF_OVERLAY])
            this->Flags[FFCF_OVERLAY]=true;
        else if(z<GH_DRAW_OVER_THRESHOLD && this->Flags[FFCF_OVERLAY])
            this->Flags[FFCF_OVERLAY]=false;
    }
    
    if((ghost->Misc[GHI_FLAGS]&GHF_NO_FALL)!=0)
        ghost->Jump=0;
    
    // Set position
    ghost->X=x;
    ghost->Y=y;
    ghost->Z=z;
    this->X=Max(-64, Min(256, x)); // The FFC shouldn't go too far offscreen, or it will disappear
    this->Y=Max(-64, Min(176, y-z));
    
    Waitframe();
    
    // Dead?
    if(!ghost->isValid())
    {
        if(clearOnDeath)
            this->Data=0;
        if(quitOnDeath)
            Quit();
        return false;
    }
    
    if(ghost->HP<=0)
    {
        if(clearOnDeath)
        {
            ghost->TileWidth=1;
            ghost->TileHeight=1;
            ghost->X=this->X+8*(this->TileWidth-1);
            ghost->Y=this->Y+8*(this->TileHeight-1);
            this->Data=0;
        }
        
        if(quitOnDeath)
            Quit();
        return false;
    }
    
    // Hit?
    CheckHit(this, ghost);
    if((ghost->Misc[GHI_FLAGS]&GHF_STUN)!=0)
        CheckStun(this, ghost);
    
    return true;
}

bool GhostWaitframeN(ffc this, npc ghost, bool clearOnDeath, bool quitOnDeath)
{
    GhostWaitframeM(this, ghost, ghost->X, ghost->Y, ghost->Z, clearOnDeath, quitOnDeath);
}

bool GhostWaitframeF(ffc this, npc ghost, bool clearOnDeath, bool quitOnDeath)
{
    GhostWaitframeM(this, ghost, this->X, this->Y, 0, clearOnDeath, quitOnDeath);
}

bool GhostWaitframesM(ffc this, npc ghost, float x, float y, float z, bool clearOnDeath, bool quitOnDeath, int numFrames)
{
    for(; numFrames>0; numFrames--)
    {
        if(!GhostWaitframeM(this, ghost, x, y, z, clearOnDeath, quitOnDeath))
            return false;
    }
    return true;
}

bool GhostWaitframesN(ffc this, npc ghost, bool clearOnDeath, bool quitOnDeath, int numFrames)
{
    for(; numFrames>0; numFrames--)
    {
        if(!GhostWaitframeM(this, ghost, ghost->X, ghost->Y, ghost->Z, clearOnDeath, quitOnDeath))
            return false;
    }
    return true;
}

bool GhostWaitframesF(ffc this, npc ghost, bool clearOnDeath, bool quitOnDeath, int numFrames)
{
    for(; numFrames>0; numFrames--)
    {
        if(!GhostWaitframeM(this, ghost, this->X, this->Y, 0, clearOnDeath, quitOnDeath))
            return false;
    }
    return true;
}

void CheckHit(ffc this, npc ghost)
{
    // Just got hit - set flash and knockback counters
    if(ghost->HP<ghost->Misc[GHI_PREV_HP])
    {
        ghost->Misc[GHI_FLAGS]|=GHF_GOT_HIT;
        ghost->Misc[GHI_PREV_HP]=ghost->HP;
        ghost->Misc[GHI_FLASH_COUNTER]=32;
        
        // Set knockback
        if((ghost->Misc[GHI_FLAGS]&GHF_KNOCKBACK)!=0)
        {
            int xDiff=Link->X-ghost->X;
            int yDiff=Link->Y-ghost->Y;
            
            // The direction can't be checked the proper way, so guess based on Link's position
            
            // If close, use Link's direction
            if(Sqrt(xDiff*xDiff+yDiff*yDiff)<32)
            {
                if((Link->Dir&10b)==(ghost->Dir&10b)) // Both horizontal or vertical
                    ghost->Misc[GHI_KNOCKBACK_COUNTER]=Link->Dir<<5|16;
            }
            // If far, use relative positions
            else
            {
                if(Abs(xDiff)<Abs(yDiff)) // Up or down
                {
                    if(ghost->Dir==DIR_UP || ghost->Dir==DIR_DOWN)
                    {
                        if(yDiff>0)
                            ghost->Misc[GHI_KNOCKBACK_COUNTER]=(DIR_UP<<5)|16;
                        else
                            ghost->Misc[GHI_KNOCKBACK_COUNTER]=(DIR_DOWN<<5)|16;
                    }
                }
                else // Left or right
                {
                    if(ghost->Dir==DIR_LEFT || ghost->Dir==DIR_RIGHT)
                    {
                        if(xDiff>0)
                            ghost->Misc[GHI_KNOCKBACK_COUNTER]=(DIR_LEFT<<5)|16;
                        else
                            ghost->Misc[GHI_KNOCKBACK_COUNTER]=(DIR_RIGHT<<5)|16;
                    }
                }
            }
        }
    }
    // Not hit; unset flag
    else if((ghost->Misc[GHI_FLAGS]&GHF_GOT_HIT)!=0)
        ghost->Misc[GHI_FLAGS]&=(~GHF_GOT_HIT);
    
    // Flash
    if(ghost->Misc[GHI_FLASH_COUNTER]>1)
    {
        this->CSet=9-(ghost->Misc[GHI_FLASH_COUNTER]&3);
        ghost->Misc[GHI_FLASH_COUNTER]--;
    }
    // Done flashing
    else if(ghost->Misc[GHI_FLASH_COUNTER]==1)
    {
        ghost->Misc[GHI_FLASH_COUNTER]=0;
        this->CSet=ghost->CSet;
    }
    
    // Handle knockback
    if(ghost->Misc[GHI_KNOCKBACK_COUNTER]!=0)
    {
        int dir=ghost->Misc[GHI_KNOCKBACK_COUNTER]>>5;
        int counter=ghost->Misc[GHI_KNOCKBACK_COUNTER]&31;
        
        // Get knocked back
        if(CanMove(this, ghost, dir, 4, 0))
        {
            counter--;
            if(counter>0)
                ghost->Misc[GHI_KNOCKBACK_COUNTER]=(dir<<5)|counter;
            else
                ghost->Misc[GHI_KNOCKBACK_COUNTER]=0;
            
            if(dir==DIR_UP)
            {
                this->Y-=4;
                ghost->Y-=4;
            }
            else if(dir==DIR_DOWN)
            {
                this->Y+=4;
                ghost->Y+=4;
            }
            else if(dir==DIR_LEFT)
            {
                this->X-=4;
                ghost->X-=4;
            }
            else // Right
            {
                this->X+=4;
                ghost->X+=4;
            }
        }
        
        // Can't go any farther; end knockback
        else
        {
            ghost->Misc[GHI_KNOCKBACK_COUNTER]=0;
            
            if(dir==DIR_UP)
            {
                ghost->Y&=~7;
                this->Y=ghost->Y-ghost->Z;
            }
            else if(dir==DIR_DOWN)
            {
                if(ghost->Y%8!=0)
                {
                    ghost->Y=(ghost->Y&~7)+8;
                    this->Y=ghost->Y-ghost->Z;
                }
            }
            else if(dir==DIR_LEFT)
            {
                ghost->X&=~7;
                this->X=ghost->X;
            }
            else // Right
            {
                if(ghost->X%8!=0)
                {
                    ghost->X=(ghost->X&~7)+8;
                    this->X=ghost->X;
                }
            }
        }
    }
}

bool CheckStun(ffc this, npc ghost)
{
    if(ghost->Stun>0)
    {
        // Stop all movement
        float vx=this->Vx;
        float vy=this->Vy;
        float ax=this->Ax;
        float ay=this->Ay;
        
        this->Vx=0;
        this->Vy=0;
        this->Ax=0;
        this->Ay=0;
        
        // Do nothing except get hit until recovered
        while(ghost->Stun>0)
        {
            Waitframe();
            if(!ghost->isValid() || ghost->HP<=0)
                return false;
            CheckHit(this, ghost);
            
            // If the ghost shouldn't get knocked back, make sure it doesn't
            if((ghost->Misc[GHI_FLAGS]&GHF_KNOCKBACK)==0)
            {
                ghost->X=this->X;
                ghost->Y=this->Y+ghost->Z;
            }
        }
        
        // Restore movement
        this->Vx=vx;
        this->Vy=vy;
        this->Ax=ax;
        this->Ay=ay;
    }
    return true;
}

bool CanMove(ffc this, npc ghost, int dir, float step, int imprecision)
{
    int y=this->Y+ghost->Z;
    
    if(dir==DIR_UP)
    {
        // Screen edges
        if((Screen->Flags[SF_ROOMTYPE]&010b)!=0 && y-step<32)
            return false;
        else if(y-step<0)
            return false;
        
        int y2=y-step;
        int flags=ghost->Misc[GHI_FLAGS];
        
        // Check every 8 pixels for solid, pit, or water
        for(int i=imprecision; i<this->TileWidth*16-imprecision; i+=8)
        {
            if(!CanMovePixel(this->X+i, y2, flags))
                return false;
        }
        
        // One last pixel...
        if(!CanMovePixel(this->X+16*this->TileWidth-1-imprecision, y2, flags))
            return false;
        
        return true;
    }
    else if(dir==DIR_DOWN)
    {
        if((Screen->Flags[SF_ROOMTYPE]&010b)!=0 && y+16*this->TileHeight+step>=144)
            return false;
        else if(y+16*this->TileHeight+step>=176)
            return false;
        
        int y2=y+16*this->TileHeight+step;
        int flags=ghost->Misc[GHI_FLAGS];
        
        for(int i=imprecision; i<this->TileWidth*16-imprecision; i+=8)
        {
            if(!CanMovePixel(this->X+i, y2, flags))
                return false;
        }
        
        if(!CanMovePixel(this->X+16*this->TileWidth-1-imprecision, y2, flags))
            return false;
        
        return true;
    }
    else if(dir==DIR_LEFT)
    {
        if((Screen->Flags[SF_ROOMTYPE]&010b)!=0 && this->X-step<32)
            return false;
        else if(this->X-step<0)
            return false;
        
        int x2=this->X-step;
        int flags=ghost->Misc[GHI_FLAGS];
        
        for(int i=imprecision; i<this->TileHeight*16-imprecision; i+=8)
        {
            if(!CanMovePixel(x2, y+i, flags))
                return false;
        }
        
        if(!CanMovePixel(x2, y+16*this->TileHeight-1-imprecision, flags))
            return false;
        
        return true;
    }
    else if(dir==DIR_RIGHT)
    {
        if((Screen->Flags[SF_ROOMTYPE]&010b)!=0 && this->X+16*this->TileWidth+step>=224)
            return false;
        else if(this->X+16*this->TileWidth+step>=256)
            return false;
        
        int x2=this->X+16*this->TileWidth+step;
        int flags=ghost->Misc[GHI_FLAGS];
        
        for(int i=imprecision; i<this->TileHeight*16-imprecision; i+=8)
        {
            if(!CanMovePixel(x2, y+i, flags))
                return false;
        }
        
        if(!CanMovePixel(x2, y+16*this->TileHeight-1-imprecision, flags))
            return false;
        
        return true;
    }
    else // Invalid direction
        return false;
}

bool CanMovePixel(int x, int y, int flags)
{
    int combo=ComboAt(x, y);
    
    // "No enemy" flags and combos
    if(Screen->ComboT[combo]==CT_NOENEMY)
        return false;
    if(Screen->ComboF[combo]==CF_NOENEMY)
        return false;
    if(Screen->ComboI[combo]==CF_NOENEMY)
        return false;
    
    // Water and pit walkability override solidity checking
    if(IsWater(combo))
    {
        if((flags&GHF_IGNORE_WATER)==0)
            return false;
    }
    else if(IsPit(combo))
    {
        if((flags&GHF_IGNORE_PITS)==0)
            return false;
    }
    else if(Screen->isSolid(x, y))
    {
        if((flags&GHF_IGNORE_SOLIDITY)==0)
            return false;
    }
    
    return true;
}

void Move(ffc this, npc ghost, float xStep, float yStep, int imprecision)
{
    if((ghost->Misc[GHI_FLAGS]&GHF_SET_DIRECTION)!=0)
    {
        // Figure out the base combo, in case GHF_4WAY is set
        int baseCombo=this->Data-ghost->Dir;
        
        if(Abs(yStep)>Abs(xStep))
        {
            if(yStep<0)
                ghost->Dir=DIR_UP;
            else
                ghost->Dir=DIR_DOWN;
        }
        else
        {
            if(xStep<0)
                ghost->Dir=DIR_LEFT;
            else
                ghost->Dir=DIR_RIGHT;
        }
        
        if((ghost->Misc[GHI_FLAGS]&GHF_4WAY)!=0)
            this->Data=baseCombo+ghost->Dir;
    }

    if(yStep<0)
    {
        if(CanMove(this, ghost, DIR_UP, -yStep, imprecision))
        {
            this->Y+=yStep;
            ghost->Y+=yStep;
        }
    }
    else if(yStep>0)
    {
        if(CanMove(this, ghost, DIR_DOWN, yStep, imprecision))
        {
            this->Y+=yStep;
            ghost->Y+=yStep;
        }
    }
    if(xStep<0)
    {
        if(CanMove(this, ghost, DIR_LEFT, -xStep, imprecision))
        {
            this->X+=xStep;
            ghost->X+=xStep;
        }
    }
    else if(xStep>0)
    {
        if(CanMove(this, ghost, DIR_RIGHT, xStep, imprecision))
        {
            this->X+=xStep;
            ghost->X+=xStep;
        }
    }
}

void MoveAtAngle(ffc this, npc ghost, float angle, float step, int imprecision)
{
    Move(this, ghost, VectorX(step, angle), VectorY(step, angle), imprecision);
}

void MoveTowardLink(ffc this, npc ghost, float step, int imprecision)
{
    float angle=Angle(this->X, this->Y, Link->X, Link->Y);
    Move(this, ghost, VectorX(step, angle), VectorY(step, angle), imprecision);
}

void Transform(ffc this, npc ghost, int combo, int cset, int tileWidth, int tileHeight)
{
    int diff;
    
    if(combo>=0)
        this->Data=combo;

    if(cset>=0)
    {
        this->CSet=cset;
        ghost->CSet=cset;
    }
    
    if(tileWidth>0)
    {
        diff=8*(this->TileWidth-tileWidth);
        this->X=this->X+diff;
        ghost->TileWidth=this->TileWidth;
        this->TileWidth=tileWidth;
        ghost->X=ghost->X+diff;
    }
    
    if(tileHeight>0)
    {
        diff=8*(this->TileHeight-tileHeight);
        this->Y=this->Y+diff;
        ghost->TileHeight=this->TileHeight;
        this->TileHeight=tileHeight;
        ghost->Y=ghost->Y+diff;
    }
    
    SetOffsets(this, ghost, 0, 0, 0, 0);
}

void SwapGhost(npc oldGhost, npc newGhost, bool copyHP)
{
    newGhost->X=oldGhost->X;
    newGhost->Y=oldGhost->Y;
    newGhost->Z=oldGhost->Z;
    newGhost->Jump=oldGhost->Jump;
    newGhost->Dir=oldGhost->Dir;
    
    newGhost->Extend=oldGhost->Extend;
    newGhost->TileWidth=oldGhost->TileWidth;
    newGhost->TileHeight=oldGhost->TileHeight;
    newGhost->HitWidth=oldGhost->HitWidth;
    newGhost->HitHeight=oldGhost->HitHeight;
    newGhost->HitXOffset=oldGhost->HitXOffset;
    newGhost->HitYOffset=oldGhost->HitYOffset;
    newGhost->CSet=oldGhost->CSet;
    newGhost->CollDetection=oldGhost->CollDetection;
    
    for(int i=0; i<16; i++)
        newGhost->Misc[i]=oldGhost->Misc[i];
    
    if(copyHP)
        newGhost->HP=oldGhost->HP;
    
    // Move the old ghost out of the way
    oldGhost->CollDetection=false;
    oldGhost->HitXOffset=32768;
}

void ReplaceGhost(npc oldGhost, npc newGhost, bool copyHP)
{
    SwapGhost(oldGhost, newGhost, copyHP);
    
    oldGhost->X=384;
    oldGhost->HP=-1000;
}

void SetOffsets(ffc this, npc ghost, float top, float bottom, float left, float right)
{
    if(top>0 && top<1)
        top=Round(top*this->TileHeight*16);
    if(bottom>0 && bottom<1)
        bottom=Round(bottom*this->TileHeight*16);
    if(left>0 && left<1)
        left=Round(left*this->TileWidth*16);
    if(right>0 && right<1)
        right=Round(right*this->TileWidth*16);
    
    ghost->HitXOffset=left;
    ghost->HitYOffset=top;
    ghost->HitWidth=16*this->TileWidth-(left+right);
    ghost->HitHeight=16*this->TileHeight-(top+bottom);
}

void Set4WayCombo(ffc this, npc ghost, int newCombo)
{
    this->Data=newCombo+ghost->Dir;
}

void SetHP(ffc this, npc ghost, int newHP)
{
    ghost->HP=newHP;
    ghost->Misc[GHI_PREV_HP]=newHP;
}

void SetAllDefenses(npc ghost, int defType)
{
    for(int i=0; i<17; i++)
        ghost->Defense[i]=defType;
}

void SetCSet(ffc this, npc ghost, int newCSet)
{
    // Don't change the FFC's color if it's not already the normal one
    if(this->CSet==ghost->CSet)
    {
        ghost->CSet=newCSet;
        this->CSet=ghost->CSet;
    }
    else
        ghost->CSet=newCSet;
}

void RevertCSet(ffc this, npc ghost)
{
    this->CSet=ghost->CSet;
}

bool GotHit(npc ghost)
{
    return (ghost->Misc[GHI_FLAGS]&GHF_GOT_HIT)!=0;
}

eweapon FireEWeapon(int weaponID, int x, int y, float angle, int step, int damage, bool blockable, int sprite, bool rotate, int sound)
{
    int flags=0;
    if(!blockable)
        flags|=EWF_UNBLOCKABLE;
    if(rotate)
        flags|=EWF_ROTATE;
    return FireEWeapon(weaponID, x, y, angle, step, damage, sprite, sound, flags);
}

eweapon FireAimedEWeapon(int weaponID, int x, int y, float angle, int step, int damage, bool blockable, int sprite, bool rotate, int sound)
{
    int flags=0;
    if(!blockable)
        flags|=EWF_UNBLOCKABLE;
    if(rotate)
        flags|=EWF_ROTATE;
    return FireEWeapon(weaponID, x, y, ArcTan(Link->X-x, Link->Y-y)+angle, step, damage, sprite, sound, flags);
}

eweapon FireNonAngularEWeapon(int weaponID, int x, int y, int direction, int step, int damage, int sprite, bool rotate, int sound)
{
    int flags=0;
    if(rotate)
        flags|=EWF_ROTATE;
    return FireNonAngularEWeapon(weaponID, x, y, direction, step, damage, sprite, sound, flags);
}

void SetFlags(ffc this, int flags)
{
    SetFlags(this, Screen->LoadNPC(1), flags);
}

npc GhostInitWait2(ffc this, int enemyID, int which, bool useEnemyPos)
{
    return GhostInitWait2(this, enemyID, useEnemyPos);
}

npc GhostInitWait2(ffc this, int enemyID, int which, bool useEnemyPos, int flags)
{
    return GhostInitWait2(this, enemyID, useEnemyPos, flags);
}
