// ghost.zh
// Version 2.6.1

// Arguments to SetEWeaponMovement()
const int EWM_SINE_WAVE         = 1;
const int EWM_HOMING            = 3;
const int EWM_HOMING_REAIM      = 4;
const int EWM_RANDOM            = 5;
const int EWM_RANDOM_REAIM      = 6;
const int EWM_VEER              = 7;
const int EWM_THROW             = 15;
const int EWM_FALL              = 19;
const int EWM_DRIFT             = 20;
const int EWM_DRIFT_WAIT        = 28;

// Flags used by certain EWeapon movement types
const int EWMF_DIE = 01b;
const int EWMF_BOUNCE = 10b;

// Arguments to SetEWeaponLifespan()
const int EWL_TIMER        = 1;
const int EWL_NEAR_LINK    = 2;
const int EWL_SLOW_TO_HALT = 3;

// Arguments to SetEWeaponDeathEffect()
const int EWD_VANISH             = 1;
const int EWD_AIM_AT_LINK        = 2;
const int EWD_EXPLODE            = 3;
const int EWD_SBOMB_EXPLODE      = 4;
const int EWD_4_FIREBALLS_HV     = 5;
const int EWD_4_FIREBALLS_DIAG   = 6;
const int EWD_4_FIREBALLS_RANDOM = 7;
const int EWD_8_FIREBALLS        = 8;
const int EWD_4_FIRES_HV         = 9;
const int EWD_4_FIRES_DIAG       = 10;
const int EWD_4_FIRES_RANDOM     = 11;
const int EWD_8_FIRES            = 12;
const int EWD_SPAWN_NPC          = 13;
const int EWD_FIRE               = 14;
const int EWD_RUN_SCRIPT         = 15;

// Prototype-based version
const int EWD_EVEN   = 1;
const int EWD_RANDOM = 2;
const int EWD_AIMED  = 3;

// EWeapon flags
const int EWF_UNBLOCKABLE    = 0000000001b;
const int EWF_ROTATE         = 0000000010b;
const int EWF_SHADOW         = 0000000100b;
const int EWF_FLICKER        = 0000001000b;
const int EWF_NO_COLLISION   = 0000010000b;

// Internal EWeapon flags
const int __EWFI_DEAD              = 000100000b;
const int __EWFI_DUMMY             = 001000000b;
const int __EWFI_IS_GHZH_EWPN      = 010000000b;
const int __EWFI_DUMMY_CHECK       = 011000000b;
const int __EWFI_DEATH_EFFECT_DONE = 100000000b;
const int __EWFI_INTERNAL          = 111100000b;



// Fire an eweapon
eweapon FireEWeapon(int weaponID, int x, int y, float angle, int step, int damage, int sprite, int sound, int flags)
{
    if(sprite<0)
        sprite=GetDefaultEWeaponSprite(weaponID);
    
    if(sound<0)
        sound=GetDefaultEWeaponSound(weaponID);
    
    eweapon wpn=Screen->CreateEWeapon(weaponID);
    wpn->X=x;
    wpn->Y=y;
    wpn->Step=step;
    wpn->Damage=damage;
    wpn->Angular=true;
    wpn->Angle=angle;
    
    if(sprite>=0)
        wpn->UseSprite(sprite);
    
    wpn->Misc[__EWI_FLAGS]=flags|__EWFI_IS_GHZH_EWPN;
    
    SetEWeaponDir(wpn); // After flags so unblockability is detected
    
    if((wpn->Misc[__EWI_FLAGS]&EWF_ROTATE)!=0)
        SetEWeaponRotation(wpn);
    
    Game->PlaySound(sound);
    
    return wpn;
}


// Fire an eweapon aimed based on Link's position
eweapon FireAimedEWeapon(int weaponID, int x, int y, float angle, int step, int damage, int sprite, int sound, int flags)
{
    return FireEWeapon(weaponID, x, y, ArcTan(Link->X-x, Link->Y-y)+angle, step, damage, sprite, sound, flags);
}


// Fire a non-angular eweapon
eweapon FireNonAngularEWeapon(int weaponID, int x, int y, int dir, int step, int damage, int sprite, int sound, int flags)
{
    if(sprite<0)
        sprite=GetDefaultEWeaponSprite(weaponID);
    
    if(sound<0)
        sound=GetDefaultEWeaponSound(weaponID);
    
    eweapon wpn=Screen->CreateEWeapon(weaponID);
    wpn->X=x;
    wpn->Y=y;
    wpn->Step=step;
    wpn->Damage=damage;
    wpn->Angular=false;
    
    if((flags&EWF_UNBLOCKABLE)!=0)
        wpn->Dir=__UnblockableDir(dir);
    else
        wpn->Dir=dir;
        
    if(sprite>=0)
        wpn->UseSprite(sprite);

    if((flags&EWF_NO_COLLISION)!=0)
        wpn->CollDetection=false;

    if((flags&EWF_ROTATE)!=0)
        SetEWeaponRotation(wpn);
    
    wpn->Misc[__EWI_FLAGS]=flags|__EWFI_IS_GHZH_EWPN;
    
    Game->PlaySound(sound);
    return wpn;
}


// Fire an eweapon larger than 1x1
eweapon FireBigEWeapon(int weaponID, int x, int y, float angle, int step, int damage, int sprite, int sound, int flags, int width, int height)
{
    eweapon wpn=FireEWeapon(weaponID, x, y, angle, step, damage, sprite, sound, flags);
    wpn->Extend=3;
    wpn->TileWidth=width;
    wpn->TileHeight=height;
    wpn->HitWidth=16*width;
    wpn->HitHeight=16*height;
    return wpn;
}


// Fire an eweapon larger than 1x1 aimed based on Link's position
eweapon FireBigAimedEWeapon(int weaponID, int x, int y, float angle, int step, int damage, int sprite, int sound, int flags, int width, int height)
{
    eweapon wpn=FireEWeapon(weaponID, x, y, ArcTan(Link->X-x, Link->Y-y)+angle, step, damage, sprite, sound, flags);
    wpn->Extend=3;
    wpn->TileWidth=width;
    wpn->TileHeight=height;
    wpn->HitWidth=16*width;
    wpn->HitHeight=16*height;
    return wpn;
}


// Fire a non-angular eweapon larger than 1x1
eweapon FireBigNonAngularEWeapon(int weaponID, int x, int y, int dir, int step, int damage, int sprite, int sound, int flags, int width, int height)
{
    eweapon wpn=FireNonAngularEWeapon(weaponID, x, y, dir, step, damage, sprite, sound, flags);
    wpn->Extend=3;
    wpn->TileWidth=width;
    wpn->TileHeight=height;
    wpn->HitWidth=16*width;
    wpn->HitHeight=16*height;
    return wpn;
}


// Create a dummy eweapon to use as a prototype
eweapon CreateDummyEWeapon(int weaponID, int step, int damage, int sprite, int sound, int flags)
{
    eweapon wpn=Screen->CreateEWeapon(weaponID);
    eweapon checkWpn;
    int minID;
    wpn->Step=0;
    wpn->Damage=damage;
    wpn->Misc[__EWI_DUMMY_STEP]=step;
    wpn->Misc[__EWI_DUMMY_SOUND]=sound;
    wpn->Misc[__EWI_DUMMY_SPRITE]=sprite;

    // Give the weapon a unique ID number so it can be found later
    for(int i=Screen->NumEWeapons(); i>0; i--)
    {
        checkWpn=Screen->LoadEWeapon(i);
        if((checkWpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY_CHECK)==__EWFI_DUMMY_CHECK)
            continue;
        minID=Min(minID, checkWpn->Misc[__EWI_ID]);
    }
    wpn->Misc[__EWI_ID]=minID-1;

    wpn->Misc[__EWI_FLAGS]=flags|__EWFI_IS_GHZH_EWPN|__EWFI_DUMMY;

    wpn->CollDetection=false;
    wpn->DrawXOffset=32768;

    return wpn;
}


// Create a dummy eweapon larger than 1x1
eweapon CreateBigDummyEWeapon(int weaponID, int step, int damage, int sprite, int sound, int flags, int width, int height)
{
    eweapon wpn=CreateDummyEWeapon(weaponID, step, damage, sprite, sound, flags);
    wpn->Extend=3;

    wpn->TileWidth=width;
    wpn->TileHeight=height;
    // No point setting the hitbox here
    return wpn;
}


// Set an eweapon's movement type
void SetEWeaponMovement(eweapon wpn, int type, int arg, int arg2)
{
    // Dummies normally use __EWI_XPOS and __EWI_YPOS for other purposes,
    // so don't overwrite them
    if((wpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY)==0)
    {
        wpn->Misc[__EWI_XPOS]=wpn->X;
        wpn->Misc[__EWI_YPOS]=wpn->Y;
    }
    wpn->Misc[__EWI_WORK]=0;
    wpn->Misc[__EWI_MOVEMENT]=type;
    wpn->Misc[__EWI_MOVEMENT_ARG]=arg;
    wpn->Misc[__EWI_MOVEMENT_ARG_2]=arg2;
    wpn->Misc[__EWI_FLAGS]|=__EWFI_IS_GHZH_EWPN;

    if(type==EWM_HOMING_REAIM || type==EWM_RANDOM_REAIM)
    {
        // Dummy? Use its stored step instead of its real step
        if((wpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY)!=0)
            wpn->Misc[__EWI_WORK_2]=wpn->Misc[__EWI_DUMMY_STEP];
        else
            wpn->Misc[__EWI_WORK_2]=wpn->Step;
    }
    else if(type==EWM_THROW)
    {
        wpn->Misc[__EWI_WORK_2]=wpn->Z;

        // Necessary upward velocity to reach Link for thrown weapons
        if(arg<=0)
        {
            // Special case for dummy weapons
            if((wpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY)!=0)
                wpn->Misc[__EWI_MOVEMENT_ARG]=-1;

            // A rough approximation, but close enough
            else
            {
                float time=Distance(wpn->X, wpn->Y, Link->X, Link->Y)/(wpn->Step/100);
                wpn->Misc[__EWI_MOVEMENT_ARG]=GH_GRAVITY*time/2;
            }
        }
    }
    else if(type==EWM_FALL)
    {
        wpn->Z=arg;
        wpn->Misc[__EWI_WORK_2]=arg;
        wpn->Misc[__EWI_WORK]=GH_GRAVITY;
    }
}


// Set an eweapon's lifespan
void SetEWeaponLifespan(eweapon wpn, int type, int arg)
{
    wpn->Misc[__EWI_LIFESPAN]=type;
    wpn->Misc[__EWI_LIFESPAN_ARG]=arg;
    wpn->Misc[__EWI_FLAGS]|=__EWFI_IS_GHZH_EWPN;
}


// Set an eweapon to use a standard death effect
void SetEWeaponDeathEffect(eweapon wpn, int type, int arg)
{
    // Make sure the script number is valid
    if(type==EWD_RUN_SCRIPT)
    {
        if(arg<1 || arg>511)
            type=EWD_VANISH;
    }
    
    wpn->Misc[__EWI_ON_DEATH]=type;
    wpn->Misc[__EWI_ON_DEATH_ARG]=arg;
    wpn->Misc[__EWI_FLAGS]|=__EWFI_IS_GHZH_EWPN;
}


// Set an eweapon to spawn more eweapons on death
void SetEWeaponDeathEffect(eweapon wpn, eweapon prototype, int numShots, int spreadType, float angle)
{
    // Combining four variables into two... Ugly, but doable
    wpn->Misc[__EWI_ON_DEATH]=prototype->Misc[__EWI_ID]+(numShots%1000)/10000;
    wpn->Misc[__EWI_ON_DEATH_ARG]=spreadType*100+(WrapAngle(angle)+6.2832);
    wpn->Misc[__EWI_FLAGS]|=__EWFI_IS_GHZH_EWPN;
}


// Update a weapon's movement, lifespan, and death effects
void UpdateEWeapon(eweapon wpn)
{
    if((__ghzhData[__GH_GLOBAL_FLAGS]&__GHGF_SUSPEND)!=0)
        return;
    
    // Start flags

    // Flicker
    if((wpn->Misc[__EWI_FLAGS]&EWF_FLICKER)!=0)
    {
        // Weapons can't be made invisible; use DrawXOffset instead
        if(wpn->DrawXOffset<1000)
            wpn->DrawXOffset+=1000;
        else
            wpn->DrawXOffset-=1000;
    }

    // Shadow
    if((wpn->Misc[__EWI_FLAGS]&EWF_SHADOW)!=0)
    {
        if(wpn->Z>0)
            DrawEWeaponShadow(wpn);
    }

    // End flags

    // Is the weapon still active?
    if((wpn->Misc[__EWI_FLAGS]&__EWFI_DEAD)==0)
    {
        // Start movement updates
        if(wpn->Misc[__EWI_MOVEMENT]!=0)
        {
            // Split in half to reduce checks
            if(wpn->Misc[__EWI_MOVEMENT]<=EWM_RANDOM_REAIM)
            {
                if(wpn->Misc[__EWI_MOVEMENT]==EWM_SINE_WAVE)
                    __UpdateEWMSineWave(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_HOMING)
                    __UpdateEWMHoming(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_HOMING_REAIM)
                    __UpdateEWMHomingReaim(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_RANDOM)
                    __UpdateEWMRandom(wpn);
                else // EWM_RANDOM_REAIM
                    __UpdateEWMRandomReaim(wpn);
            }
            else // wpn->Misc[__EWI_MOVEMENT]>EWM_RANDOM_REAIM
            {
                if(wpn->Misc[__EWI_MOVEMENT]==EWM_VEER)
                    __UpdateEWMVeer(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_THROW)
                    __UpdateEWMThrow(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_FALL)
                    __UpdateEWMFall(wpn);
                else if(wpn->Misc[__EWI_MOVEMENT]==EWM_DRIFT)
                    __UpdateEWMDrift(wpn);
                else //EWM_DRIFT_WAIT
                    __UpdateEWMDriftWait(wpn);
            }
        } // End movement updates
        
        
        // Start lifespan updates
        if(wpn->Misc[__EWI_LIFESPAN]!=0)
        {
            if(wpn->Misc[__EWI_LIFESPAN]==EWL_TIMER)
            {
                wpn->Misc[__EWI_LIFESPAN_ARG]-=1;
                if(wpn->Misc[__EWI_LIFESPAN_ARG]<=0)
                    KillEWeapon(wpn);
            }
            else if(wpn->Misc[__EWI_LIFESPAN]==EWL_NEAR_LINK)
            {
                if(Distance(wpn->X, wpn->Y, Link->X, Link->Y)<wpn->Misc[__EWI_LIFESPAN_ARG])
                    KillEWeapon(wpn);
            }
            else if(wpn->Misc[__EWI_LIFESPAN]==EWL_SLOW_TO_HALT)
            {
                wpn->Step=Max(0, wpn->Step-wpn->Misc[__EWI_LIFESPAN_ARG]);
                if(wpn->Step<=0)
                    KillEWeapon(wpn);
            }
        } // End lifespan updates
    }

    // Start death effects
    else if(wpn->Misc[__EWI_ON_DEATH]!=0 && (wpn->Misc[__EWI_FLAGS]&__EWFI_DEATH_EFFECT_DONE)==0)
    {
        if(wpn->Misc[__EWI_ON_DEATH]<0)
            __DoEWeaponDeathPrototype(wpn);

        // Split in half to reduce checks
        else if(wpn->Misc[__EWI_ON_DEATH]<8)
        {
            if(wpn->Misc[__EWI_ON_DEATH]==EWD_VANISH)
                wpn->DeadState=0;
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_AIM_AT_LINK)
                __DoEWeaponDeathAimAtLink(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_EXPLODE)
                __DoEWeaponDeathExplode(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_SBOMB_EXPLODE)
                __DoEWeaponDeathSBombExplode(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIREBALLS_HV)
                __DoEWeaponDeath4FireballsHV(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIREBALLS_DIAG)
                __DoEWeaponDeath4FireballsDiag(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIREBALLS_RANDOM)
                __DoEWeaponDeath4FireballsRand(wpn);
        }
        else // wpn->Misc[__EWI_ON_DEATH]>=8
        {
            if(wpn->Misc[__EWI_ON_DEATH]==EWD_8_FIREBALLS)
                __DoEWeaponDeath8Fireballs(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIRES_HV)
                __DoEWeaponDeath4FiresHV(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIRES_DIAG)
                __DoEWeaponDeath4FiresDiag(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_4_FIRES_RANDOM)
                __DoEWeaponDeath4FiresRand(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_8_FIRES)
                __DoEWeaponDeath8Fires(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_SPAWN_NPC)
                __DoEWeaponDeathSpawnNPC(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_FIRE)
                __DoEWeaponDeathSingleFire(wpn);
            else if(wpn->Misc[__EWI_ON_DEATH]==EWD_RUN_SCRIPT)
                __DoEWeaponDeathRunScript(wpn);
        }
    } // End death effects
}

// Set the weapon's direction based on its angle;
// Can also makes weapons unblockable
void SetEWeaponDir(eweapon wpn)
{
    float angle=wpn->Angle%6.2832;
    int dir;
    
    if(angle<0)
        angle+=6.2832;
    
    if(angle<0.3927 || angle>5.8905)
        dir=DIR_RIGHT;
    else if(angle<1.1781)
        dir=DIR_RIGHTDOWN;
    else if(angle<1.9635)
        dir=DIR_DOWN;
    else if(angle<2.7489)
        dir=DIR_LEFTDOWN;
    else if(angle<3.5343)
        dir=DIR_LEFT;
    else if(angle<4.3197)
        dir=DIR_LEFTUP;
    else if(angle<5.1051)
        dir=DIR_UP;
    else
        dir=DIR_RIGHTUP;
    
    if((wpn->Misc[__EWI_FLAGS]&EWF_UNBLOCKABLE)!=0)
        dir=__UnblockableDir(dir);
    
    wpn->Dir=dir;
}


// Flip the weapon's sprite to match its direction
void SetEWeaponRotation(eweapon wpn)
{
    if(wpn->Angular)
    {
        float angle=wpn->Angle%6.2832;
        if(angle<0)
            angle+=6.2832;

        if(angle<0.7854 || angle>5.4978) // Right
            wpn->Flip=4;
        else if(angle<=2.3562) // Down
            wpn->Flip=3;
        else if(angle<3.927) // Left
            wpn->Flip=7;
        else // Up
            wpn->Flip=0;
    }
    else
    {
        int dir=__NormalizeDir(wpn->Dir);
        if(dir==DIR_UP || dir==DIR_RIGHTUP || dir==DIR_LEFTUP)
            wpn->Flip=0;
        else if(dir==DIR_DOWN || dir==DIR_RIGHTDOWN || dir==DIR_LEFTDOWN)
            wpn->Flip=3;
        else if(dir==DIR_LEFT)
            wpn->Flip=7;
        else // Right
            wpn->Flip=4;
    }
}


// Flip the weapon's sprite to match the given direction
void SetEWeaponRotation(eweapon wpn, int dir)
{
    dir=__NormalizeDir(dir);
    if(dir==DIR_UP || dir==DIR_RIGHTUP || dir==DIR_LEFTUP)
        wpn->Flip=0;
    else if(dir==DIR_DOWN || dir==DIR_RIGHTDOWN || dir==DIR_LEFTDOWN)
        wpn->Flip=3;
    else if(dir==DIR_LEFT)
        wpn->Flip=7;
    else // Right
        wpn->Flip=4;
}


// Kill an eweapon, triggering any death effects
void KillEWeapon(eweapon wpn)
{
    wpn->Misc[__EWI_FLAGS]|=__EWFI_DEAD;
}


// Draw a shadow under an eweapon
void DrawEWeaponShadow(eweapon wpn)
{
    if(GH_SHADOW_FLICKER>0 && (__ghzhData[__GH_GLOBAL_FLAGS]&__GHGF_FLICKER)!=0)
        return;
    
    int x=CenterX(wpn)-8+wpn->DrawXOffset;
    int y=wpn->Y+(wpn->TileHeight-1)*16+wpn->DrawYOffset;

    if(GH_SHADOW_TRANSLUCENT>0)
        Screen->DrawTile(1, x, y, GH_SHADOW_TILE+__ghzhData[__GH_SHADOW_FRAME],
                         1, 1, GH_SHADOW_CSET, -1, -1, 0, 0, 0, 0, true, 64);
    else
        Screen->DrawTile(1, x, y, GH_SHADOW_TILE+__ghzhData[__GH_SHADOW_FRAME],
                         1, 1, GH_SHADOW_CSET, -1, -1, 0, 0, 0, 0, true, 128);
}


// Get the standard sprite for this weapon type
int GetDefaultEWeaponSprite(int weaponID)
{
    if(weaponID==EW_FIREBALL || weaponID==EW_FIREBALL2)
        return 17;
    else if(weaponID==EW_ROCK)
        return 18;
    else if(weaponID==EW_ARROW)
        return 19;
    else if(weaponID==EW_FIRE)
        return 35;
    else if(weaponID==EW_FIRE2)
        return 81;
    else if(weaponID==EW_FIRETRAIL)
        return 80;
    else if(weaponID==EW_MAGIC)
        return 21;
    else if(weaponID==EW_BEAM)
        return 20;
    else if(weaponID==EW_WIND)
        return 36;
    else if(weaponID==EW_BOMB)
        return 76;
    else if(weaponID==EW_SBOMB)
        return 77;
    else if(weaponID==EW_BRANG)
    {
        // The sprite depends on what boomerang Link has, so check his inventory
        int maxLevel=0;
        itemdata id;

        for(int i=0; i<256; i++)
        {
            if(!Link->Item[i])
                continue;

            id=Game->LoadItemData(i);

            if(id->Family!=IC_BRANG)
                continue;

            if(id->Level>maxLevel)
            {
                maxLevel=id->Level;
                if(maxLevel>=3) // Any higher won't matter
                    break;
            }

        }

        if(maxLevel<=1)
            return 4;
        else if(maxLevel==2)
            return 5;
        else
            return 6;
    }
    else
        return 0;
}


// Find the sound normally made by weapons of this type
int GetDefaultEWeaponSound(int weaponID)
{
     if(weaponID==EW_FIREBALL || weaponID==EW_FIREBALL2)
        return 40;
    else if(weaponID==EW_MAGIC || weaponID==EW_WIND)
        return 32;
    else if(weaponID==EW_FIRE || weaponID==EW_FIRE2 || weaponID==EW_FIRETRAIL)
        return 13;
    else if(weaponID==EW_ROCK)
        return 51;
    else
        return 0;
}


// Use this in a script started on eweapon death to find the weapon
// that created it
eweapon GetAssociatedEWeapon(int weaponID)
{
    eweapon wpn;
    
    for(int i=Screen->NumEWeapons(); i>0; i--)
    {
        wpn=Screen->LoadEWeapon(i);
        if((wpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY_CHECK)!=__EWFI_IS_GHZH_EWPN) // Filter out dummies
            continue;

        if(wpn->Misc[__EWI_ID]==weaponID)
            return wpn;
    }
    
    // Couldn't find it; return an uninitialized weapon
    eweapon invalidWpn;
    return invalidWpn;
}


// Is this a ghost.zh-controlled weapon?
bool IsGhostZHEWeapon(eweapon wpn)
{
    return (wpn->Misc[__EWI_FLAGS]&__EWFI_IS_GHZH_EWPN)!=0;
}


// Is this a dummy weapon?
bool IsDummyEWeapon(eweapon wpn)
{
    return (wpn->Misc[__EWI_FLAGS]&__EWFI_DUMMY_CHECK)==__EWFI_DUMMY_CHECK;
}


// Make a copy of the given eweapon, which should be a dummy
void __CopyEWeapon(eweapon prototype, int centerX, int centerY, float angle)
{
    eweapon wpn=Screen->CreateEWeapon(prototype->ID);
    if(prototype->Misc[__EWI_DUMMY_SPRITE]>0)
        wpn->UseSprite(prototype->Misc[__EWI_DUMMY_SPRITE]);

    if(prototype->Extend==3)
    {
        wpn->Extend=3;
        wpn->TileWidth=prototype->TileWidth;
        wpn->TileHeight=prototype->TileHeight;
        wpn->HitWidth=16*prototype->TileWidth;
        wpn->HitHeight=16*prototype->TileHeight;
        wpn->X=centerX-8*wpn->TileWidth;
        wpn->Y=centerY-8*wpn->TileWidth;
    }
    else
    {
        wpn->X=centerX-8;
        wpn->Y=centerY-8;
    }

    wpn->Step=prototype->Misc[__EWI_DUMMY_STEP];
    wpn->Damage=prototype->Damage;
    wpn->Angular=true;
    wpn->Angle=angle;
    SetEWeaponDir(wpn);

    wpn->Misc[__EWI_XPOS]=wpn->X;
    wpn->Misc[__EWI_YPOS]=wpn->Y;
    wpn->Misc[__EWI_MOVEMENT]=prototype->Misc[__EWI_MOVEMENT];
    wpn->Misc[__EWI_MOVEMENT_ARG_2]=prototype->Misc[__EWI_MOVEMENT_ARG_2];

    // Special case for thrown weapons with automatic velocity
    if(prototype->Misc[__EWI_MOVEMENT]==EWM_THROW && prototype->Misc[__EWI_MOVEMENT_ARG]==-1)
    {
        float time=Distance(centerX, centerY, Link->X+8, Link->Y+8)/(wpn->Step/100);
        wpn->Misc[__EWI_MOVEMENT_ARG]=GH_GRAVITY*time/2;
    }
    else
        wpn->Misc[__EWI_MOVEMENT_ARG]=prototype->Misc[__EWI_MOVEMENT_ARG];

    wpn->Misc[__EWI_LIFESPAN]=prototype->Misc[__EWI_LIFESPAN];
    wpn->Misc[__EWI_LIFESPAN_ARG]=prototype->Misc[__EWI_LIFESPAN_ARG];
    wpn->Misc[__EWI_ON_DEATH]=prototype->Misc[__EWI_ON_DEATH];
    wpn->Misc[__EWI_ON_DEATH_ARG]=prototype->Misc[__EWI_ON_DEATH_ARG];
    wpn->Misc[__EWI_FLAGS]=(prototype->Misc[__EWI_FLAGS]&(~__EWFI_DUMMY));

    if((wpn->Misc[__EWI_FLAGS]&EWF_NO_COLLISION)!=0)
        wpn->CollDetection=false;

    if((wpn->Misc[__EWI_FLAGS]&EWF_ROTATE)!=0)
        SetEWeaponRotation(wpn);
}


// Get the unblockable version (8-15) of a direction
int __UnblockableDir(int dir)
{
    if(dir==DIR_UP)
        return 8;
    if(dir==DIR_DOWN)
        return 12;
    if(dir==DIR_LEFT)
        return 14;
    if(dir==DIR_RIGHT)
        return 10;
    if(dir==DIR_LEFTUP)
        return 15;
    if(dir==DIR_RIGHTUP)
        return 9;
    if(dir==DIR_LEFTDOWN)
        return 13;
    if(dir==DIR_RIGHTDOWN)
        return 11;
    
    // Should never get here
    return dir;
}
