// ghost.zh // Version 2.8.0 // 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 = 00000000001b; const int EWF_ROTATE = 00000000010b; const int EWF_ROTATE_360 = 00000000100b; const int EWF_SHADOW = 00000001000b; const int EWF_FLICKER = 00000010000b; const int EWF_NO_COLLISION = 00000100000b; const int EWF_FAKE_Z = 00001000000b; // Internal EWeapon flags const int __EWFI_DEAD = 00010000000b; const int __EWFI_DUMMY = 00100000000b; const int __EWFI_IS_GHZH_EWPN = 01000000000b; const int __EWFI_DUMMY_CHECK = 01100000000b; const int __EWFI_DEATH_EFFECT_DONE = 10000000000b; const int __EWFI_INTERNAL = 11110000000b; // 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(Donjon())wpn->Script = 2; 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->Dir == 0)wpn->Flip = 0; //else if(wpn->Dir == 1)wpn->Flip = 2; //else if(wpn->Dir == 2)wpn->Flip = 5; //else if(wpn->Dir == 3)wpn->Flip = 4; if((flags&EWF_NO_COLLISION)!=0) wpn->CollDetection=false; 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(Donjon())wpn->Script = 2; 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(wpn->Dir == 0)wpn->Flip = 0; //else if(wpn->Dir == 1)wpn->Flip = 2; //else if(wpn->Dir == 2)wpn->Flip = 5; //else if(wpn->Dir == 3)wpn->Flip = 4; 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; // Move it to make sure it's not deleted wpn->X=32; wpn->Y=32; 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 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; } // 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; } // Kill an eweapon, triggering any death effects void KillEWeapon(eweapon wpn) { wpn->Misc[__EWI_FLAGS]|=__EWFI_DEAD; } // 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; } // 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; } const int __EWI_FLAGS = 15; // Every index but this one can be used by non-ghost.zh EWeapons const int __EWI_ID = 3; const int __EWI_XPOS = 4; const int __EWI_YPOS = 5; const int __EWI_WORK = 6; const int __EWI_WORK_2 = 7; // Only used by a few movement types const int __EWI_MOVEMENT = 8; const int __EWI_MOVEMENT_ARG = 9; const int __EWI_MOVEMENT_ARG_2 = 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; // These are only used by dummy EWeapons; // they can use the same values as __EWI_XPOS and __EWI_YPOS const int __EWI_DUMMY_SOUND = 2; const int __EWI_DUMMY_STEP = 4; const int __EWI_DUMMY_SPRITE = 5;