//------------------------------------------------------------------------- /* Copyright (C) 1997, 2005 - 3D Realms Entertainment This file is part of Shadow Warrior version 1.2 Shadow Warrior is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Original Source: 1997 - Frank Maddin and Jim Norwood Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms */ //------------------------------------------------------------------------- #include "ns.h" #include "build.h" #include "names2.h" #include "panel.h" #include "misc.h" #include "interpso.h" #include "tags.h" #include "break.h" #include "network.h" #include "pal.h" #include "ai.h" #include "weapon.h" #include "sector.h" #include "sprite.h" #include "misc.h" #include "player.h" #include "gstrings.h" BEGIN_SW_NS struct MISSILE_PLACEMENT { int dist_over, dist_out; DAngle ang; }; void SpawnZombie2(DSWActor*); Collision move_ground_missile(DSWActor* actor, const DVector2& change, double ceildist, double flordist, uint32_t cliptype, int numtics); void DoPlayerBeginDie(PLAYER*); void VehicleSetSmoke(SECTOR_OBJECT* sop, ANIMATOR* animator); void ScaleSpriteVector(DSWActor* actor, int scalex, int scaley, int scalez); void ScaleSpriteVector(DSWActor* actor, int scale); ANIMATOR DoBettyBeginDeath; ANIMATOR DoSkullBeginDeath; ANIMATOR DoRipperGrow; constexpr DAngle FacingAngle = mapangle(150); // // Damage Amounts defined in damage.h // #define DAMAGE_TABLE DAMAGE_DATA DamageData[] = { #include "damage.h" }; #undef DAMAGE_TABLE FOOT_TYPE FootMode=WATER_FOOT; bool left_foot = false; int FinishTimer = 0; // This is how many bullet shells have been spawned since the beginning of the game. int ShellCount = 0; //int Zombies = 0; int StarQueueHead=0; TObjPtr StarQueue[MAX_STAR_QUEUE]; int HoleQueueHead=0; TObjPtr HoleQueue[MAX_HOLE_QUEUE]; int WallBloodQueueHead=0; TObjPtr WallBloodQueue[MAX_WALLBLOOD_QUEUE]; int FloorBloodQueueHead=0; TObjPtr FloorBloodQueue[MAX_FLOORBLOOD_QUEUE]; int GenericQueueHead=0; TObjPtr GenericQueue[MAX_GENERIC_QUEUE]; int LoWangsQueueHead=0; TObjPtr LoWangsQueue[MAX_LOWANGS_QUEUE]; void SpawnBreakStaticFlames(DSWActor* actor); bool GlobalSkipZrange = false; int WeaponIsAmmo = BIT(WPN_STAR) | BIT(WPN_SWORD) | BIT(WPN_MINE) | BIT(WPN_FIST); short target_ang; ANIMATOR DoStar; ANIMATOR DoCrossBolt; ANIMATOR DoSuicide, DoUziSmoke; ANIMATOR DoShrapJumpFall; ANIMATOR DoFastShrapJumpFall; int SpawnSmokePuff(DSWActor* actor); bool WarpToSurface(DVector3& pos, sectortype** sect); bool WarpToUnderwater(DVector3& pos, sectortype** sect); bool TestDontStickSector(sectortype* hit_sect); ANIMATOR SpawnShrapX; bool WeaponMoveHit(DSWActor* actor); void SpawnMidSplash(DSWActor* actor); int SopDamage(SECTOR_OBJECT* sop,short amt); int SopCheckKill(SECTOR_OBJECT* sop); int QueueStar(DSWActor*); int DoBlurExtend(DSWActor* actor, int16_t interval, int16_t blur_num); int SpawnDemonFist(DSWActor*); void SpawnTankShellExp(DSWActor*); void SpawnMicroExp(DSWActor*); void SpawnExpZadjust(DSWActor* actor, DSWActor* expActor, double upper_zsize, double lower_zsize); int BulletHitSprite(DSWActor* actor, DSWActor* hitActor, const DVector3& pos, short ID); int SpawnSplashXY(const DVector3& pos, sectortype*); DSWActor* SpawnBoatSparks(PLAYER* pp, sectortype* hit_sect, walltype* hit_wall, const DVector3& hitpos, DAngle hit_ang); short StatDamageList[STAT_DAMAGE_LIST_SIZE] = { STAT_SO_SP_CHILD, STAT_ENEMY, STAT_ENEMY_SKIP4, STAT_PLAYER0, STAT_PLAYER1, STAT_PLAYER2, STAT_PLAYER3, STAT_PLAYER4, STAT_PLAYER5, STAT_PLAYER6, STAT_PLAYER7, STAT_PLAYER_UNDER0, STAT_PLAYER_UNDER1, STAT_PLAYER_UNDER2, STAT_PLAYER_UNDER3, STAT_PLAYER_UNDER4, STAT_PLAYER_UNDER5, STAT_PLAYER_UNDER6, STAT_PLAYER_UNDER7, // MINE MUST BE LAST STAT_MINE_STUCK }; ////////////////////// // // SPECIAL STATES // ////////////////////// // state for sprites that are not restored STATE s_NotRestored[] = { {2323, 100, NullAnimator, &s_NotRestored[0]} }; STATE s_Suicide[] = { {1, 100, DoSuicide, &s_Suicide[0]} }; STATE s_DeadLoWang[] = { {1160, 100, NullAnimator, &s_DeadLoWang[0]}, }; ////////////////////// // // BREAKABLE STATES // ////////////////////// ANIMATOR DoDefaultStat; #define BREAK_LIGHT_RATE 18 STATE s_BreakLight[] = { {BREAK_LIGHT_ANIM + 0, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[1]}, {BREAK_LIGHT_ANIM + 1, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[2]}, {BREAK_LIGHT_ANIM + 2, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[3]}, {BREAK_LIGHT_ANIM + 3, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[4]}, {BREAK_LIGHT_ANIM + 4, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[5]}, {BREAK_LIGHT_ANIM + 5, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[5]} }; #define BREAK_BARREL_RATE 18 STATE s_BreakBarrel[] = { {BREAK_BARREL + 4, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[1]}, {BREAK_BARREL + 5, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[2]}, {BREAK_BARREL + 6, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[3]}, {BREAK_BARREL + 7, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[4]}, {BREAK_BARREL + 8, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[5]}, {BREAK_BARREL + 9, BREAK_BARREL_RATE, DoDefaultStat, &s_BreakBarrel[5]}, }; #define BREAK_PEDISTAL_RATE 28 STATE s_BreakPedistal[] = { {BREAK_PEDISTAL + 1, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[1]}, {BREAK_PEDISTAL + 2, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[2]}, {BREAK_PEDISTAL + 3, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[3]}, {BREAK_PEDISTAL + 4, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[3]}, }; #define BREAK_BOTTLE1_RATE 18 STATE s_BreakBottle1[] = { {BREAK_BOTTLE1 + 1, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[1]}, {BREAK_BOTTLE1 + 2, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[2]}, {BREAK_BOTTLE1 + 3, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[3]}, {BREAK_BOTTLE1 + 4, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[4]}, {BREAK_BOTTLE1 + 5, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[5]}, {BREAK_BOTTLE1 + 6, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[5]}, }; #define BREAK_BOTTLE2_RATE 18 STATE s_BreakBottle2[] = { {BREAK_BOTTLE2 + 1, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[1]}, {BREAK_BOTTLE2 + 2, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[2]}, {BREAK_BOTTLE2 + 3, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[3]}, {BREAK_BOTTLE2 + 4, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[4]}, {BREAK_BOTTLE2 + 5, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[5]}, {BREAK_BOTTLE2 + 6, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[5]}, }; #define PUFF_RATE 8 ANIMATOR DoPuff; STATE s_Puff[] = { {PUFF + 0, PUFF_RATE, DoPuff, &s_Puff[1]}, {PUFF + 1, PUFF_RATE, DoPuff, &s_Puff[2]}, {PUFF + 2, PUFF_RATE, DoPuff, &s_Puff[3]}, {PUFF + 3, PUFF_RATE, DoPuff, &s_Puff[4]}, {PUFF + 4, PUFF_RATE, DoPuff, &s_Puff[5]}, {PUFF + 5, PUFF_RATE, DoPuff, &s_Puff[6]}, {PUFF + 5, 100, DoSuicide, &s_Puff[0]} }; #define RAIL_PUFF_R0 3969 #define RAIL_PUFF_R1 3985 #define RAIL_PUFF_R2 4001 #define RAIL_PUFF_RATE 6 ANIMATOR DoRailPuff; STATE s_RailPuff[3][17] = { { {RAIL_PUFF_R0 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][1]}, {RAIL_PUFF_R0 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][2]}, {RAIL_PUFF_R0 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][3]}, {RAIL_PUFF_R0 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][4]}, {RAIL_PUFF_R0 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][5]}, {RAIL_PUFF_R0 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][6]}, {RAIL_PUFF_R0 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][7]}, {RAIL_PUFF_R0 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][8]}, {RAIL_PUFF_R0 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][9]}, {RAIL_PUFF_R0 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][10]}, {RAIL_PUFF_R0 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][11]}, {RAIL_PUFF_R0 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][12]}, {RAIL_PUFF_R0 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][13]}, {RAIL_PUFF_R0 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][14]}, {RAIL_PUFF_R0 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][15]}, {RAIL_PUFF_R0 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][16]}, {RAIL_PUFF_R0 + 15, 100, DoSuicide, &s_RailPuff[0][0]}, }, { {RAIL_PUFF_R1 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][1]}, {RAIL_PUFF_R1 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][2]}, {RAIL_PUFF_R1 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][3]}, {RAIL_PUFF_R1 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][4]}, {RAIL_PUFF_R1 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][5]}, {RAIL_PUFF_R1 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][6]}, {RAIL_PUFF_R1 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][7]}, {RAIL_PUFF_R1 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][8]}, {RAIL_PUFF_R1 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][9]}, {RAIL_PUFF_R1 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][10]}, {RAIL_PUFF_R1 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][11]}, {RAIL_PUFF_R1 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][12]}, {RAIL_PUFF_R1 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][13]}, {RAIL_PUFF_R1 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][14]}, {RAIL_PUFF_R1 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][15]}, {RAIL_PUFF_R1 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][16]}, {RAIL_PUFF_R1 + 15, 100, DoSuicide, &s_RailPuff[1][0]}, }, { {RAIL_PUFF_R2 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][1]}, {RAIL_PUFF_R2 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][2]}, {RAIL_PUFF_R2 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][3]}, {RAIL_PUFF_R2 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][4]}, {RAIL_PUFF_R2 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][5]}, {RAIL_PUFF_R2 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][6]}, {RAIL_PUFF_R2 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][7]}, {RAIL_PUFF_R2 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][8]}, {RAIL_PUFF_R2 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][9]}, {RAIL_PUFF_R2 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][10]}, {RAIL_PUFF_R2 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][11]}, {RAIL_PUFF_R2 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][12]}, {RAIL_PUFF_R2 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][13]}, {RAIL_PUFF_R2 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][14]}, {RAIL_PUFF_R2 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][15]}, {RAIL_PUFF_R2 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][16]}, {RAIL_PUFF_R2 + 15, 100, DoSuicide, &s_RailPuff[2][0]}, } }; STATE* sg_RailPuff[] = { &s_RailPuff[0][0], &s_RailPuff[1][0], &s_RailPuff[2][0], &s_RailPuff[1][0], &s_RailPuff[0][0] }; #define LASER_PUFF 3201 #define LASER_PUFF_RATE 8 STATE s_LaserPuff[] = { {LASER_PUFF + 0, LASER_PUFF_RATE, nullptr, &s_LaserPuff[1]}, //{LASER_PUFF + 1, LASER_PUFF_RATE, nullptr, &s_LaserPuff[2]}, //{LASER_PUFF + 2, LASER_PUFF_RATE, nullptr, &s_LaserPuff[3]}, {LASER_PUFF + 0, 100, DoSuicide, &s_LaserPuff[0]} }; #define TRACER 3201 #define TRACER_RATE 6 ANIMATOR DoTracer; STATE s_Tracer[] = { {TRACER + 0, TRACER_RATE, DoTracer, &s_Tracer[1]}, {TRACER + 1, TRACER_RATE, DoTracer, &s_Tracer[2]}, {TRACER + 2, TRACER_RATE, DoTracer, &s_Tracer[3]}, {TRACER + 3, TRACER_RATE, DoTracer, &s_Tracer[4]}, {TRACER + 4, TRACER_RATE, DoTracer, &s_Tracer[5]}, {TRACER + 5, TRACER_RATE, DoTracer, &s_Tracer[0]} }; #define EMP 2058 #define EMP_RATE 6 ANIMATOR DoEMP; STATE s_EMP[] = { {EMP + 0, EMP_RATE, DoEMP, &s_EMP[1]}, {EMP + 1, EMP_RATE, DoEMP, &s_EMP[2]}, {EMP + 2, EMP_RATE, DoEMP, &s_EMP[0]} }; ANIMATOR DoEMPBurst; STATE s_EMPBurst[] = { {EMP + 0, EMP_RATE, DoEMPBurst, &s_EMPBurst[1]}, {EMP + 1, EMP_RATE, DoEMPBurst, &s_EMPBurst[2]}, {EMP + 2, EMP_RATE, DoEMPBurst, &s_EMPBurst[0]} }; STATE s_EMPShrap[] = { {EMP + 0, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[1]}, {EMP + 1, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[2]}, {EMP + 2, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[0]}, }; #define TANK_SHELL 3201 #define TANK_SHELL_RATE 6 ANIMATOR DoTankShell; STATE s_TankShell[] = { {TRACER + 0, 200, DoTankShell, &s_TankShell[0]} }; ANIMATOR DoVehicleSmoke; #define VEHICLE_SMOKE_RATE 18 STATE s_VehicleSmoke[] = { {PUFF + 0, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[1]}, {PUFF + 1, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[2]}, {PUFF + 2, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[3]}, {PUFF + 3, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[4]}, {PUFF + 4, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[5]}, {PUFF + 5, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[6]}, {PUFF + 5, 100, DoSuicide, &s_VehicleSmoke[6]} }; ANIMATOR DoWaterSmoke, SpawnWaterSmoke; #define WATER_SMOKE_RATE 18 STATE s_WaterSmoke[] = { {PUFF + 0, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[1]}, {PUFF + 1, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[2]}, {PUFF + 2, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[3]}, {PUFF + 3, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[4]}, {PUFF + 4, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[5]}, {PUFF + 5, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[6]}, {PUFF + 5, 100, DoSuicide, &s_WaterSmoke[6]} }; constexpr double UZI_SPARK_REPEAT = 0.375; constexpr double UZI_SMOKE_REPEAT = 0.375; #define UZI_SMOKE_RATE 16 // Was 9 STATE s_UziSmoke[] = { {UZI_SMOKE + 0, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[1]}, {UZI_SMOKE + 1, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[2]}, {UZI_SMOKE + 2, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[3]}, {UZI_SMOKE + 3, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[4]}, {UZI_SMOKE + 3, 100, DoSuicide, &s_UziSmoke[0]}, }; #define SHOTGUN_SMOKE_RATE 16 #define SHOTGUN_SMOKE UZI_SMOKE+1 ANIMATOR DoShotgunSmoke; STATE s_ShotgunSmoke[] = { {UZI_SMOKE + 0, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[1]}, {UZI_SMOKE + 1, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[2]}, {UZI_SMOKE + 2, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[3]}, {UZI_SMOKE + 3, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[4]}, {UZI_SMOKE + 3, 100, DoSuicide, &s_ShotgunSmoke[0]}, }; #define UZI_BULLET_RATE 100 #define UZI_BULLET 717 // actually a bubble ANIMATOR DoUziBullet; STATE s_UziBullet[] = { {UZI_BULLET + 0, UZI_BULLET_RATE, DoUziBullet, &s_UziBullet[0]}, }; #define UZI_SPARK_RATE 8 STATE s_UziSpark[] = { {UZI_SPARK + 0, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[1]}, {UZI_SPARK + 1, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[2]}, {UZI_SPARK + 2, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[3]}, {UZI_SPARK + 3, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[4]}, {UZI_SPARK + 4, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[5]}, {UZI_SPARK + 4, 100, DoSuicide, &s_UziSpark[0]}, }; STATE s_UziPowerSpark[] = { {UZI_SPARK + 0, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[1]}, {UZI_SPARK + 1, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[2]}, {UZI_SPARK + 2, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[3]}, {UZI_SPARK + 3, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[4]}, {UZI_SPARK + 4, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[5]}, {UZI_SPARK + 4, 100, DoSuicide, &s_UziSpark[0]}, }; #define BUBBLE 716 #define BUBBLE_RATE 100 ANIMATOR DoBubble; STATE s_Bubble[] = { {BUBBLE + 0, BUBBLE_RATE, DoBubble, &s_Bubble[0]} }; //#define SPLASH 2190 #define SPLASH 772 #define SPLASH_RATE 10 STATE s_Splash[] = { {SPLASH + 0, SPLASH_RATE, NullAnimator, &s_Splash[1]}, {SPLASH + 1, SPLASH_RATE, NullAnimator, &s_Splash[2]}, {SPLASH + 2, SPLASH_RATE, NullAnimator, &s_Splash[3]}, {SPLASH + 3, SPLASH_RATE, NullAnimator, &s_Splash[4]}, {SPLASH + 4, SPLASH_RATE, NullAnimator, &s_Splash[5]}, {SPLASH + 4, 100, DoSuicide, &s_Splash[0]} }; #define CROSSBOLT 2230 #define CROSSBOLT_RATE 100 STATE s_CrossBolt[5][1] = { { {CROSSBOLT + 0, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[0][0]}, }, { {CROSSBOLT + 2, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[1][0]}, }, { {CROSSBOLT + 3, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[2][0]}, }, { {CROSSBOLT + 4, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[3][0]}, }, { {CROSSBOLT + 1, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[4][0]}, } }; STATE* sg_CrossBolt[] = { &s_CrossBolt[0][0], &s_CrossBolt[1][0], &s_CrossBolt[2][0], &s_CrossBolt[3][0], &s_CrossBolt[4][0] }; #undef STAR #define STAR 2102 #define STAR_RATE 6 STATE s_Star[] = { {STAR + 0, STAR_RATE, DoStar, &s_Star[1]}, {STAR + 1, STAR_RATE, DoStar, &s_Star[2]}, {STAR + 2, STAR_RATE, DoStar, &s_Star[3]}, {STAR + 3, STAR_RATE, DoStar, &s_Star[0]} }; STATE s_StarStuck[] = { {STAR + 0, STAR_RATE, NullAnimator, &s_StarStuck[0]}, }; #define STAR_DOWN 2066 STATE s_StarDown[] = { {STAR_DOWN + 0, STAR_RATE, DoStar, &s_StarDown[1]}, {STAR_DOWN + 1, STAR_RATE, DoStar, &s_StarDown[2]}, {STAR_DOWN + 2, STAR_RATE, DoStar, &s_StarDown[3]}, {STAR_DOWN + 3, STAR_RATE, DoStar, &s_StarDown[0]} }; STATE s_StarDownStuck[] = { {STAR + 0, STAR_RATE, NullAnimator, &s_StarDownStuck[0]}, }; ////////////////////// // // LAVA BOSS // ////////////////////// #define LAVA_BOULDER_RATE 6 ANIMATOR DoLavaBoulder,DoShrapDamage,DoVulcanBoulder; STATE s_LavaBoulder[] = { {LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[1]}, {LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[2]}, {LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[3]}, {LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[4]}, {LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[5]}, {LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[6]}, {LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[7]}, {LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[0]}, }; #define LAVA_SHARD (LAVA_BOULDER+1) STATE s_LavaShard[] = { {LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[1]}, {LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[2]}, {LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[3]}, {LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[4]}, {LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[5]}, {LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[6]}, {LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[7]}, {LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[0]}, }; STATE s_VulcanBoulder[] = { {LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[1]}, {LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[2]}, {LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[3]}, {LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[4]}, {LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[5]}, {LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[6]}, {LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[7]}, {LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[0]}, }; ////////////////////// // // GRENADE // ////////////////////// #if 0 ANIMATOR DoGrenade; #undef GRENADE #define GRENADE 5000 #define GRENADE_RATE 8 STATE s_Grenade[] = { {FIREBALL + 0, GRENADE_RATE, DoGrenade, &s_Grenade[1]}, {FIREBALL + 1, GRENADE_RATE, DoGrenade, &s_Grenade[2]}, {FIREBALL + 2, GRENADE_RATE, DoGrenade, &s_Grenade[3]}, {FIREBALL + 3, GRENADE_RATE, DoGrenade, &s_Grenade[0]} }; #else #define GRENADE_FRAMES 1 #define GRENADE_R0 2110 #define GRENADE_R1 GRENADE_R0 + (GRENADE_FRAMES * 1) #define GRENADE_R2 GRENADE_R0 + (GRENADE_FRAMES * 2) #define GRENADE_R3 GRENADE_R0 + (GRENADE_FRAMES * 3) #define GRENADE_R4 GRENADE_R0 + (GRENADE_FRAMES * 4) #undef GRENADE #define GRENADE GRENADE_R0 #define GRENADE_RATE 8 ANIMATOR DoGrenade; STATE s_Grenade[5][1] = { { {GRENADE_R0 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[0][0]}, }, { {GRENADE_R1 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[1][0]}, }, { {GRENADE_R2 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[2][0]}, }, { {GRENADE_R3 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[3][0]}, }, { {GRENADE_R4 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[4][0]}, } }; STATE* sg_Grenade[] = { &s_Grenade[0][0], &s_Grenade[1][0], &s_Grenade[2][0], &s_Grenade[3][0], &s_Grenade[4][0] }; #endif ////////////////////// // // MINE // ////////////////////// ANIMATOR DoMine,DoMineStuck; #undef MINE #define MINE 2223 #define MINE_SHRAP 5011 #define MINE_RATE 16 STATE s_MineStuck[] = { {MINE + 0, MINE_RATE, DoMineStuck, &s_MineStuck[0]}, }; STATE s_Mine[] = { {MINE + 0, MINE_RATE, DoMine, &s_Mine[1]}, {MINE + 1, MINE_RATE, DoMine, &s_Mine[0]}, }; ANIMATOR DoMineSpark; STATE s_MineSpark[] = { {UZI_SPARK + 0, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[1]}, {UZI_SPARK + 1, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[2]}, {UZI_SPARK + 2, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[3]}, {UZI_SPARK + 3, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[4]}, {UZI_SPARK + 4, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[5]}, {UZI_SPARK + 4, 100, DoSuicide, &s_MineSpark[0]}, }; ////////////////////// // // METEOR // ////////////////////// #define METEOR_R0 2098 #define METEOR_R1 2090 #define METEOR_R2 2094 #define METEOR_R3 2090 #define METEOR_R4 2098 #define METEOR STAR #define METEOR_RATE 8 ANIMATOR DoMeteor; STATE s_Meteor[5][4] = { { {METEOR_R0 + 0, METEOR_RATE, DoMeteor, &s_Meteor[0][1]}, {METEOR_R0 + 1, METEOR_RATE, DoMeteor, &s_Meteor[0][2]}, {METEOR_R0 + 2, METEOR_RATE, DoMeteor, &s_Meteor[0][3]}, {METEOR_R0 + 3, METEOR_RATE, DoMeteor, &s_Meteor[0][0]}, }, { {METEOR_R1 + 0, METEOR_RATE, DoMeteor, &s_Meteor[1][1]}, {METEOR_R1 + 1, METEOR_RATE, DoMeteor, &s_Meteor[1][2]}, {METEOR_R1 + 2, METEOR_RATE, DoMeteor, &s_Meteor[1][3]}, {METEOR_R1 + 3, METEOR_RATE, DoMeteor, &s_Meteor[1][0]}, }, { {METEOR_R2 + 0, METEOR_RATE, DoMeteor, &s_Meteor[2][1]}, {METEOR_R2 + 1, METEOR_RATE, DoMeteor, &s_Meteor[2][2]}, {METEOR_R2 + 2, METEOR_RATE, DoMeteor, &s_Meteor[2][3]}, {METEOR_R2 + 3, METEOR_RATE, DoMeteor, &s_Meteor[2][0]}, }, { {METEOR_R3 + 0, METEOR_RATE, DoMeteor, &s_Meteor[3][1]}, {METEOR_R3 + 1, METEOR_RATE, DoMeteor, &s_Meteor[3][2]}, {METEOR_R3 + 2, METEOR_RATE, DoMeteor, &s_Meteor[3][3]}, {METEOR_R3 + 3, METEOR_RATE, DoMeteor, &s_Meteor[3][0]}, }, { {METEOR_R4 + 0, METEOR_RATE, DoMeteor, &s_Meteor[4][1]}, {METEOR_R4 + 1, METEOR_RATE, DoMeteor, &s_Meteor[4][2]}, {METEOR_R4 + 2, METEOR_RATE, DoMeteor, &s_Meteor[4][3]}, {METEOR_R4 + 3, METEOR_RATE, DoMeteor, &s_Meteor[4][0]}, } }; STATE* sg_Meteor[] = { &s_Meteor[0][0], &s_Meteor[1][0], &s_Meteor[2][0], &s_Meteor[3][0], &s_Meteor[4][0] }; #define METEOR_EXP 2115 #define METEOR_EXP_RATE 7 STATE s_MeteorExp[] = { {METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[1]}, {METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[2]}, {METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[3]}, {METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[4]}, {METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[5]}, {METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[6]}, {METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_MeteorExp[6]} }; #define MIRV_METEOR METEOR_R0 ANIMATOR DoMirvMissile; STATE s_MirvMeteor[5][4] = { { {METEOR_R0 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][1]}, {METEOR_R0 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][2]}, {METEOR_R0 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][3]}, {METEOR_R0 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][0]}, }, { {METEOR_R1 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][1]}, {METEOR_R1 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][2]}, {METEOR_R1 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][3]}, {METEOR_R1 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][0]}, }, { {METEOR_R2 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][1]}, {METEOR_R2 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][2]}, {METEOR_R2 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][3]}, {METEOR_R2 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][0]}, }, { {METEOR_R3 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][1]}, {METEOR_R3 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][2]}, {METEOR_R3 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][3]}, {METEOR_R3 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][0]}, }, { {METEOR_R4 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][1]}, {METEOR_R4 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][2]}, {METEOR_R4 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][3]}, {METEOR_R4 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][0]}, } }; STATE* sg_MirvMeteor[] = { &s_MirvMeteor[0][0], &s_MirvMeteor[1][0], &s_MirvMeteor[2][0], &s_MirvMeteor[3][0], &s_MirvMeteor[4][0] }; STATE s_MirvMeteorExp[] = { {METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[1]}, {METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[2]}, {METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[3]}, {METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[4]}, {METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[5]}, {METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[6]}, {METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_MirvMeteorExp[6]} }; #define SERP_METEOR METEOR_R0+1 ANIMATOR DoSerpMeteor; STATE s_SerpMeteor[5][4] = { { {2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][1]}, {2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][2]}, {2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][3]}, {2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][0]}, }, { {2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][1]}, {2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][2]}, {2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][3]}, {2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][0]}, }, { {2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][1]}, {2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][2]}, {2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][3]}, {2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][0]}, }, { {2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][1]}, {2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][2]}, {2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][3]}, {2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][0]}, }, { {2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][1]}, {2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][2]}, {2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][3]}, {2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][0]}, } }; STATE* sg_SerpMeteor[] = { &s_SerpMeteor[0][0], &s_SerpMeteor[1][0], &s_SerpMeteor[2][0], &s_SerpMeteor[3][0], &s_SerpMeteor[4][0] }; STATE s_SerpMeteorExp[] = { {METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[1]}, {METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[2]}, {METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[3]}, {METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[4]}, {METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[5]}, {METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[6]}, {METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_SerpMeteorExp[6]} }; ////////////////////// // // SPEAR // ////////////////////// #define SPEAR_RATE 8 ANIMATOR DoSpear; STATE s_Spear[5][1] = { { {SPEAR_R0 + 0, SPEAR_RATE, DoSpear, s_Spear[0]}, }, { {SPEAR_R1 + 0, SPEAR_RATE, DoSpear, s_Spear[1]}, }, { {SPEAR_R2 + 0, SPEAR_RATE, DoSpear, s_Spear[2]}, }, { {SPEAR_R3 + 0, SPEAR_RATE, DoSpear, s_Spear[3]}, }, { {SPEAR_R4 + 0, SPEAR_RATE, DoSpear, s_Spear[4]}, } }; STATE* sg_Spear[] = { s_Spear[0], s_Spear[1], s_Spear[2], s_Spear[3], s_Spear[4] }; ////////////////////// // // ROCKET // ////////////////////// #define ROCKET_FRAMES 1 #define ROCKET_R0 2206 #define ROCKET_R1 ROCKET_R0 + (ROCKET_FRAMES * 2) #define ROCKET_R2 ROCKET_R0 + (ROCKET_FRAMES * 3) #define ROCKET_R3 ROCKET_R0 + (ROCKET_FRAMES * 4) #define ROCKET_R4 ROCKET_R0 + (ROCKET_FRAMES * 1) ANIMATOR DoRocket; #define ROCKET_RATE 8 STATE s_Rocket[5][1] = { { {ROCKET_R0 + 0, ROCKET_RATE, DoRocket, &s_Rocket[0][0]}, }, { {ROCKET_R1 + 0, ROCKET_RATE, DoRocket, &s_Rocket[1][0]}, }, { {ROCKET_R2 + 0, ROCKET_RATE, DoRocket, &s_Rocket[2][0]}, }, { {ROCKET_R3 + 0, ROCKET_RATE, DoRocket, &s_Rocket[3][0]}, }, { {ROCKET_R4 + 0, ROCKET_RATE, DoRocket, &s_Rocket[4][0]}, } }; STATE* sg_Rocket[] = { &s_Rocket[0][0], &s_Rocket[1][0], &s_Rocket[2][0], &s_Rocket[3][0], &s_Rocket[4][0] }; ////////////////////// // // BUNNY ROCKET // ////////////////////// #define BUNNYROCKET_FRAMES 5 #define BUNNYROCKET_R0 4550 #define BUNNYROCKET_R1 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 1) #define BUNNYROCKET_R2 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 2) #define BUNNYROCKET_R3 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 3) #define BUNNYROCKET_R4 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 4) ANIMATOR DoRocket; #define BUNNYROCKET_RATE 8 STATE s_BunnyRocket[5][1] = { { {BUNNYROCKET_R0 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[0][0]}, }, { {BUNNYROCKET_R1 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[1][0]}, }, { {BUNNYROCKET_R2 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[2][0]}, }, { {BUNNYROCKET_R3 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[3][0]}, }, { {BUNNYROCKET_R4 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[4][0]}, } }; STATE* sg_BunnyRocket[] = { &s_BunnyRocket[0][0], &s_BunnyRocket[1][0], &s_BunnyRocket[2][0], &s_BunnyRocket[3][0], &s_BunnyRocket[4][0] }; ANIMATOR DoRail; #define RAIL_RATE 8 STATE s_Rail[5][1] = { { {ROCKET_R0 + 0, RAIL_RATE, DoRail, &s_Rail[0][0]}, }, { {ROCKET_R1 + 0, RAIL_RATE, DoRail, &s_Rail[1][0]}, }, { {ROCKET_R2 + 0, RAIL_RATE, DoRail, &s_Rail[2][0]}, }, { {ROCKET_R3 + 0, RAIL_RATE, DoRail, &s_Rail[3][0]}, }, { {ROCKET_R4 + 0, RAIL_RATE, DoRail, &s_Rail[4][0]}, } }; STATE* sg_Rail[] = { &s_Rail[0][0], &s_Rail[1][0], &s_Rail[2][0], &s_Rail[3][0], &s_Rail[4][0] }; ANIMATOR DoLaser; #define LASER_RATE 8 STATE s_Laser[] = { {ROCKET_R0 + 0, LASER_RATE, DoLaser, &s_Laser[0]} }; ////////////////////// // // MICRO // ////////////////////// #define MICRO_FRAMES 1 #define MICRO_R0 2206 #define MICRO_R1 MICRO_R0 + (MICRO_FRAMES * 2) #define MICRO_R2 MICRO_R0 + (MICRO_FRAMES * 3) #define MICRO_R3 MICRO_R0 + (MICRO_FRAMES * 4) #define MICRO_R4 MICRO_R0 + (MICRO_FRAMES * 1) ANIMATOR DoMicro; #define MICRO_RATE 8 #if 0 #define PUFF 1748 #define PUFF_RATE 8 ANIMATOR DoMicroPuffSuicide; STATE s_MicroPuff[] = { {PUFF + 0, PUFF_RATE, DoPuff, &s_MicroPuff[1]}, {PUFF + 1, PUFF_RATE, DoPuff, &s_MicroPuff[2]}, {PUFF + 2, PUFF_RATE, DoPuff, &s_MicroPuff[3]}, {PUFF + 3, PUFF_RATE, DoPuff, &s_MicroPuff[4]}, {PUFF + 4, PUFF_RATE, DoPuff, &s_MicroPuff[5]}, {PUFF + 5, PUFF_RATE, DoPuff, &s_MicroPuff[6]}, {PUFF + 5, 100, DoMicroPuffSuicide, &s_MicroPuff[6]} }; #endif STATE s_Micro[5][1] = { { {MICRO_R0 + 0, MICRO_RATE, DoMicro, &s_Micro[0][0]}, }, { {MICRO_R1 + 0, MICRO_RATE, DoMicro, &s_Micro[1][0]}, }, { {MICRO_R2 + 0, MICRO_RATE, DoMicro, &s_Micro[2][0]}, }, { {MICRO_R3 + 0, MICRO_RATE, DoMicro, &s_Micro[3][0]}, }, { {MICRO_R4 + 0, MICRO_RATE, DoMicro, &s_Micro[4][0]}, } }; STATE* sg_Micro[] = { &s_Micro[0][0], &s_Micro[1][0], &s_Micro[2][0], &s_Micro[3][0], &s_Micro[4][0] }; ANIMATOR DoMicroMini; STATE s_MicroMini[5][1] = { { {MICRO_R0 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[0][0]}, }, { {MICRO_R1 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[1][0]}, }, { {MICRO_R2 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[2][0]}, }, { {MICRO_R3 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[3][0]}, }, { {MICRO_R4 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[4][0]}, } }; STATE* sg_MicroMini[] = { &s_MicroMini[0][0], &s_MicroMini[1][0], &s_MicroMini[2][0], &s_MicroMini[3][0], &s_MicroMini[4][0] }; ////////////////////// // // BOLT THINMAN // ////////////////////// #define BOLT_THINMAN_RATE 8 ANIMATOR DoBoltThinMan; STATE s_BoltThinMan[5][1] = { { {BOLT_THINMAN_R0 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[0][0]}, }, { {BOLT_THINMAN_R1 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[1][0]}, }, { {BOLT_THINMAN_R2 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[2][0]}, }, { {BOLT_THINMAN_R3 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[3][0]}, }, { {BOLT_THINMAN_R4 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[4][0]}, } }; STATE* sg_BoltThinMan[] = { &s_BoltThinMan[0][0], &s_BoltThinMan[1][0], &s_BoltThinMan[2][0], &s_BoltThinMan[3][0], &s_BoltThinMan[4][0] }; #define BOLT_SEEKER_RATE 8 ANIMATOR DoBoltSeeker; STATE s_BoltSeeker[5][1] = { { {BOLT_THINMAN_R0 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[0][0]}, }, { {BOLT_THINMAN_R1 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[1][0]}, }, { {BOLT_THINMAN_R2 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[2][0]}, }, { {BOLT_THINMAN_R3 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[3][0]}, }, { {BOLT_THINMAN_R4 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[4][0]}, } }; STATE* sg_BoltSeeker[] = { &s_BoltSeeker[0][0], &s_BoltSeeker[1][0], &s_BoltSeeker[2][0], &s_BoltSeeker[3][0], &s_BoltSeeker[4][0] }; #define BOLT_FATMAN STAR #define BOLT_FATMAN_RATE 8 ANIMATOR DoBoltFatMan; STATE s_BoltFatMan[] = { {BOLT_FATMAN + 0, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[1]}, {BOLT_FATMAN + 1, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[2]}, {BOLT_FATMAN + 2, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[3]}, {BOLT_FATMAN + 3, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[0]} }; #define BOLT_SHRAPNEL STAR #define BOLT_SHRAPNEL_RATE 8 ANIMATOR DoBoltShrapnel; STATE s_BoltShrapnel[] = { {BOLT_SHRAPNEL + 0, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[1]}, {BOLT_SHRAPNEL + 1, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[2]}, {BOLT_SHRAPNEL + 2, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[3]}, {BOLT_SHRAPNEL + 3, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[0]} }; #define COOLG_FIRE 2430 //#define COOLG_FIRE 1465 #define COOLG_W_FIRE_RATE 8 ANIMATOR DoCoolgFire; STATE s_CoolgFire[] = { {2031 + 0, COOLG_W_FIRE_RATE, DoCoolgFire, &s_CoolgFire[1]}, {2031 + 1, COOLG_W_FIRE_RATE, DoCoolgFire, &s_CoolgFire[2]}, {2031 + 2, COOLG_W_FIRE_RATE, DoCoolgFire, &s_CoolgFire[3]}, {2031 + 3, COOLG_W_FIRE_RATE, DoCoolgFire, &s_CoolgFire[0]} }; #define COOLG_FIRE_DONE 2410 //#define COOLG_FIRE_DONE 1466 //#define COOLG_FIRE_DONE_RATE 8 #define COOLG_FIRE_DONE_RATE 3 STATE s_CoolgFireDone[] = { {COOLG_FIRE_DONE + 0, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[1]}, {COOLG_FIRE_DONE + 1, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[2]}, {COOLG_FIRE_DONE + 2, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[3]}, {COOLG_FIRE_DONE + 3, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[4]}, {COOLG_FIRE_DONE + 4, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[5]}, {COOLG_FIRE_DONE + 4, COOLG_FIRE_DONE_RATE, DoSuicide, &s_CoolgFireDone[5]} }; ANIMATOR DoCoolgDrip; #define COOLG_DRIP 1720 STATE s_CoolgDrip[] = { {COOLG_DRIP + 0, 100, DoCoolgDrip, &s_CoolgDrip[0]} }; #define GORE_FLOOR_SPLASH_RATE 8 #define GORE_FLOOR_SPLASH 1710 STATE s_GoreFloorSplash[] = { {GORE_FLOOR_SPLASH + 0, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[1]}, {GORE_FLOOR_SPLASH + 1, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[2]}, {GORE_FLOOR_SPLASH + 2, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[3]}, {GORE_FLOOR_SPLASH + 3, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[4]}, {GORE_FLOOR_SPLASH + 4, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[5]}, {GORE_FLOOR_SPLASH + 5, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[6]}, {GORE_FLOOR_SPLASH + 5, GORE_FLOOR_SPLASH_RATE, DoSuicide, &s_GoreFloorSplash[6]} }; #define GORE_SPLASH_RATE 8 #define GORE_SPLASH 2410 STATE s_GoreSplash[] = { {GORE_SPLASH + 0, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[1]}, {GORE_SPLASH + 1, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[2]}, {GORE_SPLASH + 2, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[3]}, {GORE_SPLASH + 3, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[4]}, {GORE_SPLASH + 4, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[5]}, {GORE_SPLASH + 5, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[6]}, {GORE_SPLASH + 5, GORE_SPLASH_RATE, DoSuicide, &s_GoreSplash[6]} }; ////////////////////////////////////////////// // // HEART ATTACK & PLASMA // ////////////////////////////////////////////// #define PLASMA 1562 //2058 #define PLASMA_FOUNTAIN 2058+1 #define PLASMA_RATE 8 #define PLASMA_FOUNTAIN_TIME (3*120); ANIMATOR DoPlasma; // regular bolt from heart STATE s_Plasma[] = { {PLASMA + 0, PLASMA_RATE, DoPlasma, &s_Plasma[1]}, {PLASMA + 1, PLASMA_RATE, DoPlasma, &s_Plasma[2]}, {PLASMA + 2, PLASMA_RATE, DoPlasma, &s_Plasma[0]} }; ANIMATOR DoPlasmaFountain; // follows actor spewing blood #define PLASMA_Drip 1562 //2420 STATE s_PlasmaFountain[] = { {PLASMA_Drip + 0, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[1]}, {PLASMA_Drip + 1, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[2]}, {PLASMA_Drip + 2, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[3]}, {PLASMA_Drip + 3, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[0]}, }; #define PLASMA_Drip_RATE 12 STATE s_PlasmaDrip[] = { {PLASMA_Drip + 0, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[1]}, {PLASMA_Drip + 1, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[2]}, {PLASMA_Drip + 2, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[3]}, {PLASMA_Drip + 3, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[4]}, {PLASMA_Drip + 4, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[5]}, {PLASMA_Drip + 5, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[6]}, {PLASMA_Drip + 7, PLASMA_Drip_RATE, DoSuicide, &s_PlasmaDrip[6]} }; #define PLASMA_DONE 2061 #define PLASMA_DONE_RATE 15 ANIMATOR DoPlasmaDone; STATE s_PlasmaDone[] = { {PLASMA + 0, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[1]}, {PLASMA + 2, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[2]}, {PLASMA + 1, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[0]} }; #define TELEPORT_EFFECT 3240 #define TELEPORT_EFFECT_RATE 6 ANIMATOR DoTeleportEffect; STATE s_TeleportEffect[] = { {TELEPORT_EFFECT + 0, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[1]}, {TELEPORT_EFFECT + 1, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[2]}, {TELEPORT_EFFECT + 2, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[3]}, {TELEPORT_EFFECT + 3, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[4]}, {TELEPORT_EFFECT + 4, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[5]}, {TELEPORT_EFFECT + 5, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[6]}, {TELEPORT_EFFECT + 6, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[7]}, {TELEPORT_EFFECT + 7, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[8]}, {TELEPORT_EFFECT + 8, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[9]}, {TELEPORT_EFFECT + 9, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[10]}, {TELEPORT_EFFECT + 10, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[11]}, {TELEPORT_EFFECT + 11, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[12]}, {TELEPORT_EFFECT + 12, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[13]}, {TELEPORT_EFFECT + 13, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[14]}, {TELEPORT_EFFECT + 14, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[15]}, {TELEPORT_EFFECT + 15, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[16]}, {TELEPORT_EFFECT + 16, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[17]}, {TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[18]}, {TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, DoSuicide, &s_TeleportEffect[18]}, }; // Spawn a RIPPER teleport effect ANIMATOR DoTeleRipper; STATE s_TeleportEffect2[] = { {TELEPORT_EFFECT + 0, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[1]}, {TELEPORT_EFFECT + 1, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[2]}, {TELEPORT_EFFECT + 2, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[3]}, {TELEPORT_EFFECT + 3, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[4]}, {TELEPORT_EFFECT + 4, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[5]}, {TELEPORT_EFFECT + 5, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[6]}, {TELEPORT_EFFECT + 6, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[7]}, {TELEPORT_EFFECT + 7, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[8]}, {TELEPORT_EFFECT + 8, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[9]}, {TELEPORT_EFFECT + 9, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[10]}, {TELEPORT_EFFECT + 10, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[11]}, {TELEPORT_EFFECT + 11, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[12]}, {TELEPORT_EFFECT + 12, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[13]}, {TELEPORT_EFFECT + 13, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[14]}, {TELEPORT_EFFECT + 14, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[15]}, {TELEPORT_EFFECT + 15, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[16]}, {TELEPORT_EFFECT + 16, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[17]}, {TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[18]}, {TELEPORT_EFFECT + 17, SF_QUICK_CALL, DoTeleRipper, &s_TeleportEffect2[19]}, {TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, DoSuicide, &s_TeleportEffect2[19]}, }; ANIMATOR DoElectro; #define ELECTRO_SNAKE 2073 #define ELECTRO_PLAYER (ELECTRO) #define ELECTRO_ENEMY (ELECTRO + 1) #define ELECTRO_SHARD (ELECTRO + 2) STATE s_Electro[] = { {ELECTRO + 0, 12, DoElectro, &s_Electro[1]}, {ELECTRO + 1, 12, DoElectro, &s_Electro[2]}, {ELECTRO + 2, 12, DoElectro, &s_Electro[3]}, {ELECTRO + 3, 12, DoElectro, &s_Electro[0]} }; STATE s_ElectroShrap[] = { {ELECTRO + 0, 12, DoShrapDamage, &s_ElectroShrap[1]}, {ELECTRO + 1, 12, DoShrapDamage, &s_ElectroShrap[2]}, {ELECTRO + 2, 12, DoShrapDamage, &s_ElectroShrap[3]}, {ELECTRO + 3, 12, DoShrapDamage, &s_ElectroShrap[0]} }; ////////////////////// // // EXPS // ////////////////////// #define GRENADE_EXP 3121 #define MINE_EXP GRENADE_EXP+1 #define GRENADE_EXP_RATE 6 #if 0 STATE s_GrenadeExp[] = { {GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[1]}, {GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[2]}, {GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[3]}, {GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[4]}, {GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[5]}, {GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[6]}, {GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[7]}, {GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[8]}, {GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[9]}, {GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[10]}, {GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[11]}, {GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[12]}, {GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[13]}, {GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[14]}, {GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[15]}, {GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[16]}, {GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[17]}, {GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[18]}, {GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[19]}, {GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[20]}, {GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[21]}, {GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[22]}, {GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeExp[22]} }; #else STATE s_GrenadeSmallExp[] = { {GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[1]}, {GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[2]}, {GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[3]}, {GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[4]}, {GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[5]}, {GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[6]}, {GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[7]}, {GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[8]}, {GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[9]}, {GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[10]}, {GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[11]}, {GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[12]}, {GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[13]}, {GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[14]}, {GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[15]}, {GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[16]}, {GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[17]}, {GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[18]}, {GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[19]}, {GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[20]}, {GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[21]}, {GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[22]}, {GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeSmallExp[22]} }; ANIMATOR SpawnGrenadeSmallExp; STATE s_GrenadeExp[] = { {GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[1]}, {GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[2]}, {GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[3]}, {GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[4]}, {GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[5]}, {GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[6]}, {GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[7]}, {GRENADE_EXP + 6, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[8]}, {GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[9]}, {GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[10]}, {GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[11]}, {GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[12]}, {GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[13]}, {GRENADE_EXP + 12, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[14]}, {GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[15]}, {GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[16]}, {GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[17]}, {GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[18]}, {GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[19]}, {GRENADE_EXP + 17, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[20]}, {GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[21]}, {GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[22]}, {GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[23]}, {GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[24]}, {GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[25]}, {GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeExp[25]} }; #endif #define MINE_EXP GRENADE_EXP+1 ANIMATOR DoMineExp, DoMineExpMine; STATE s_MineExp[] = { {GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[1]}, {GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[2]}, {GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[3]}, {GRENADE_EXP + 3, 0|SF_QUICK_CALL, DoMineExp, &s_MineExp[4]}, {GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[5]}, {GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[6]}, {GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[7]}, {GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[8]}, {GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[9]}, {GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[10]}, {GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[11]}, {GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[12]}, {GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[13]}, {GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[14]}, {GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[15]}, {GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[16]}, {GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[17]}, {GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[18]}, {GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[19]}, {GRENADE_EXP + 17, 0|SF_QUICK_CALL, DoMineExpMine, &s_MineExp[20]}, {GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[21]}, {GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[22]}, {GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[23]}, {GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[24]}, {GRENADE_EXP + 21, 100, DoSuicide, &s_MineExp[24]} }; #define EXP_RATE_W 7 #define BOLT_EXP EXP #define FIREBALL_EXP EXP+1 #define BASIC_EXP EXP+2 #define SECTOR_EXP EXP+3 #define MICRO_EXP EXP+5 #define TRACER_EXP EXP+6 #define TANK_SHELL_EXP EXP+7 STATE s_BasicExp[] = { {EXP + 0, EXP_RATE_W, NullAnimator, &s_BasicExp[1]}, {EXP + 1, EXP_RATE_W, NullAnimator, &s_BasicExp[2]}, {EXP + 2, EXP_RATE_W, NullAnimator, &s_BasicExp[3]}, {EXP + 3, EXP_RATE_W, NullAnimator, &s_BasicExp[4]}, {EXP + 4, EXP_RATE_W, NullAnimator, &s_BasicExp[5]}, {EXP + 5, EXP_RATE_W, NullAnimator, &s_BasicExp[6]}, {EXP + 6, EXP_RATE_W, NullAnimator, &s_BasicExp[7]}, {EXP + 7, EXP_RATE_W, NullAnimator, &s_BasicExp[8]}, {EXP + 8, EXP_RATE_W, NullAnimator, &s_BasicExp[9]}, {EXP + 9, EXP_RATE_W, NullAnimator, &s_BasicExp[10]}, {EXP + 10, EXP_RATE_W, NullAnimator, &s_BasicExp[11]}, {EXP + 11, EXP_RATE_W, NullAnimator, &s_BasicExp[12]}, {EXP + 12, EXP_RATE_W, NullAnimator, &s_BasicExp[13]}, {EXP + 13, EXP_RATE_W, NullAnimator, &s_BasicExp[14]}, {EXP + 14, EXP_RATE_W, NullAnimator, &s_BasicExp[15]}, {EXP + 15, EXP_RATE_W, NullAnimator, &s_BasicExp[16]}, {EXP + 16, EXP_RATE_W, NullAnimator, &s_BasicExp[17]}, {EXP + 17, EXP_RATE_W, NullAnimator, &s_BasicExp[18]}, {EXP + 18, EXP_RATE_W, NullAnimator, &s_BasicExp[19]}, {EXP + 19, EXP_RATE_W, NullAnimator, &s_BasicExp[20]}, {EXP + 20, 100, DoSuicide, &s_BasicExp[0]} }; #define MICRO_EXP_RATE 3 ANIMATOR DoExpDamageTest; STATE s_MicroExp[] = { {EXP + 0, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[1]}, {EXP + 0, SF_QUICK_CALL, DoExpDamageTest, &s_MicroExp[2]}, {EXP + 1, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[3]}, {EXP + 2, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[4]}, {EXP + 3, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[5]}, {EXP + 4, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[6]}, {EXP + 5, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[7]}, {EXP + 6, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[8]}, {EXP + 7, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[9]}, {EXP + 8, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[10]}, {EXP + 9, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[11]}, {EXP + 10, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[12]}, {EXP + 11, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[13]}, {EXP + 12, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[14]}, {EXP + 13, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[15]}, {EXP + 14, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[16]}, {EXP + 15, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[17]}, {EXP + 16, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[18]}, {EXP + 17, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[19]}, {EXP + 18, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[20]}, {EXP + 19, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[21]}, {EXP + 20, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[22]}, {EXP + 20, 100, DoSuicide, &s_MicroExp[22]} }; #define BIG_GUN_FLAME_RATE 15 STATE s_BigGunFlame[] = { // first 3 frames {EXP + 0, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[1]}, {EXP + 1, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[2]}, {EXP + 2, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[3]}, // last 4 frames frames {EXP + 17, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[4]}, {EXP + 18, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[5]}, {EXP + 19, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[6]}, {EXP + 20, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[7]}, {EXP + 20, 100, DoSuicide, &s_BigGunFlame[0]} }; STATE s_BoltExp[] = { {EXP + 0, EXP_RATE_W, NullAnimator, &s_BoltExp[1]}, {EXP + 0, SF_QUICK_CALL, NullAnimator, &s_BoltExp[2]}, {EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_BoltExp[3]}, {EXP + 1, EXP_RATE_W, NullAnimator, &s_BoltExp[4]}, {EXP + 2, EXP_RATE_W, NullAnimator, &s_BoltExp[5]}, {EXP + 3, EXP_RATE_W, NullAnimator, &s_BoltExp[6]}, {EXP + 4, EXP_RATE_W, NullAnimator, &s_BoltExp[7]}, {EXP + 5, EXP_RATE_W, NullAnimator, &s_BoltExp[8]}, {EXP + 6, EXP_RATE_W, NullAnimator, &s_BoltExp[9]}, {EXP + 7, EXP_RATE_W, NullAnimator, &s_BoltExp[10]}, {EXP + 7, SF_QUICK_CALL, SpawnShrapX, &s_BoltExp[11]}, {EXP + 8, EXP_RATE_W, NullAnimator, &s_BoltExp[12]}, {EXP + 9, EXP_RATE_W, NullAnimator, &s_BoltExp[13]}, {EXP + 10, EXP_RATE_W, NullAnimator, &s_BoltExp[14]}, {EXP + 11, EXP_RATE_W, NullAnimator, &s_BoltExp[15]}, {EXP + 12, EXP_RATE_W, NullAnimator, &s_BoltExp[16]}, {EXP + 13, EXP_RATE_W, NullAnimator, &s_BoltExp[17]}, {EXP + 14, EXP_RATE_W, NullAnimator, &s_BoltExp[18]}, {EXP + 15, EXP_RATE_W, NullAnimator, &s_BoltExp[19]}, {EXP + 16, EXP_RATE_W, NullAnimator, &s_BoltExp[20]}, {EXP + 17, EXP_RATE_W, NullAnimator, &s_BoltExp[21]}, {EXP + 18, EXP_RATE_W, NullAnimator, &s_BoltExp[22]}, {EXP + 19, EXP_RATE_W, NullAnimator, &s_BoltExp[23]}, {EXP + 20, EXP_RATE_W, NullAnimator, &s_BoltExp[24]}, {EXP + 20, 100, DoSuicide, &s_BoltExp[0]} }; STATE s_TankShellExp[] = { {EXP + 0, EXP_RATE_W, NullAnimator, &s_TankShellExp[1]}, {EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TankShellExp[2]}, {EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_TankShellExp[3]}, {EXP + 1, EXP_RATE_W, NullAnimator, &s_TankShellExp[4]}, {EXP + 2, EXP_RATE_W, NullAnimator, &s_TankShellExp[5]}, {EXP + 3, EXP_RATE_W, NullAnimator, &s_TankShellExp[6]}, {EXP + 4, EXP_RATE_W, NullAnimator, &s_TankShellExp[7]}, {EXP + 5, EXP_RATE_W, NullAnimator, &s_TankShellExp[8]}, {EXP + 6, EXP_RATE_W, NullAnimator, &s_TankShellExp[9]}, {EXP + 7, EXP_RATE_W, NullAnimator, &s_TankShellExp[10]}, {EXP + 7, SF_QUICK_CALL, SpawnShrapX, &s_TankShellExp[11]}, {EXP + 8, EXP_RATE_W, NullAnimator, &s_TankShellExp[12]}, {EXP + 9, EXP_RATE_W, NullAnimator, &s_TankShellExp[13]}, {EXP + 10, EXP_RATE_W, NullAnimator, &s_TankShellExp[14]}, {EXP + 11, EXP_RATE_W, NullAnimator, &s_TankShellExp[15]}, {EXP + 12, EXP_RATE_W, NullAnimator, &s_TankShellExp[16]}, {EXP + 13, EXP_RATE_W, NullAnimator, &s_TankShellExp[17]}, {EXP + 14, EXP_RATE_W, NullAnimator, &s_TankShellExp[18]}, {EXP + 15, EXP_RATE_W, NullAnimator, &s_TankShellExp[19]}, {EXP + 16, EXP_RATE_W, NullAnimator, &s_TankShellExp[20]}, {EXP + 17, EXP_RATE_W, NullAnimator, &s_TankShellExp[21]}, {EXP + 18, EXP_RATE_W, NullAnimator, &s_TankShellExp[22]}, {EXP + 19, EXP_RATE_W, NullAnimator, &s_TankShellExp[23]}, {EXP + 20, EXP_RATE_W, NullAnimator, &s_TankShellExp[24]}, {EXP + 20, 100, DoSuicide, &s_TankShellExp[0]} }; #define TRACER_EXP_RATE 4 STATE s_TracerExp[] = { {EXP + 0, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[1]}, {EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TracerExp[2]}, {EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TracerExp[3]}, {EXP + 1, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[4]}, {EXP + 2, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[5]}, {EXP + 3, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[6]}, {EXP + 4, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[7]}, {EXP + 5, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[8]}, {EXP + 6, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[9]}, {EXP + 7, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[10]}, {EXP + 7, SF_QUICK_CALL, NullAnimator, &s_TracerExp[11]}, {EXP + 8, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[12]}, {EXP + 9, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[13]}, {EXP + 10, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[14]}, {EXP + 11, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[15]}, {EXP + 12, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[16]}, {EXP + 13, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[17]}, {EXP + 14, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[18]}, {EXP + 15, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[19]}, {EXP + 16, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[20]}, {EXP + 17, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[21]}, {EXP + 18, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[22]}, {EXP + 19, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[23]}, {EXP + 20, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[24]}, {EXP + 20, 100, DoSuicide, &s_TracerExp[0]} }; #define EXP_RATE_W 7 ANIMATOR DoSectorExp; STATE s_SectorExp[] = { {EXP + 0, EXP_RATE_W, DoSectorExp, &s_SectorExp[1]}, {EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_SectorExp[2]}, {EXP + 0, SF_QUICK_CALL, DoSectorExp, &s_SectorExp[3]}, {EXP + 1, EXP_RATE_W, DoSectorExp, &s_SectorExp[4]}, {EXP + 2, EXP_RATE_W, DoSectorExp, &s_SectorExp[5]}, {EXP + 3, EXP_RATE_W, DoSectorExp, &s_SectorExp[6]}, {EXP + 4, EXP_RATE_W, DoSectorExp, &s_SectorExp[7]}, {EXP + 5, EXP_RATE_W, DoSectorExp, &s_SectorExp[8]}, {EXP + 6, EXP_RATE_W, DoSectorExp, &s_SectorExp[9]}, {EXP + 7, EXP_RATE_W, DoSectorExp, &s_SectorExp[10]}, {EXP + 7, SF_QUICK_CALL, DoSectorExp, &s_SectorExp[11]}, {EXP + 8, EXP_RATE_W, DoSectorExp, &s_SectorExp[12]}, {EXP + 9, EXP_RATE_W, DoSectorExp, &s_SectorExp[13]}, {EXP + 10, EXP_RATE_W, DoSectorExp, &s_SectorExp[14]}, {EXP + 11, EXP_RATE_W, DoSectorExp, &s_SectorExp[15]}, {EXP + 12, EXP_RATE_W, DoSectorExp, &s_SectorExp[16]}, {EXP + 13, EXP_RATE_W, DoSectorExp, &s_SectorExp[17]}, {EXP + 14, EXP_RATE_W, DoSectorExp, &s_SectorExp[18]}, {EXP + 15, EXP_RATE_W, DoSectorExp, &s_SectorExp[19]}, {EXP + 16, EXP_RATE_W, DoSectorExp, &s_SectorExp[20]}, {EXP + 17, EXP_RATE_W, DoSectorExp, &s_SectorExp[21]}, {EXP + 18, EXP_RATE_W, DoSectorExp, &s_SectorExp[22]}, {EXP + 19, EXP_RATE_W, DoSectorExp, &s_SectorExp[23]}, {EXP + 20, EXP_RATE_W, DoSectorExp, &s_SectorExp[24]}, {EXP + 20, 100, DoSuicide, &s_SectorExp[0]} }; #define FIREBALL_DISS 3196 #define FIREBALL_DISS_RATE 8 STATE s_FireballExp[] = { {FIREBALL_DISS + 0, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[1]}, {FIREBALL_DISS + 1, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[2]}, {FIREBALL_DISS + 2, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[3]}, {FIREBALL_DISS + 3, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[4]}, {FIREBALL_DISS + 4, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[5]}, {FIREBALL_DISS + 4, 100, DoSuicide, &s_FireballExp[0]} }; #define NAP_EXP (3072) #define NAP_EXP_RATE 6 STATE s_NapExp[] = { {NAP_EXP + 0, NAP_EXP_RATE, NullAnimator, &s_NapExp[1]}, {NAP_EXP + 0, 0 | SF_QUICK_CALL, DoDamageTest, &s_NapExp[2]}, {NAP_EXP + 1, NAP_EXP_RATE, NullAnimator, &s_NapExp[3]}, {NAP_EXP + 2, NAP_EXP_RATE, NullAnimator, &s_NapExp[4]}, {NAP_EXP + 3, NAP_EXP_RATE, NullAnimator, &s_NapExp[5]}, {NAP_EXP + 4, NAP_EXP_RATE, NullAnimator, &s_NapExp[6]}, {NAP_EXP + 5, NAP_EXP_RATE, NullAnimator, &s_NapExp[7]}, {NAP_EXP + 6, NAP_EXP_RATE, NullAnimator, &s_NapExp[8]}, {NAP_EXP + 7, NAP_EXP_RATE, NullAnimator, &s_NapExp[9]}, {NAP_EXP + 8, NAP_EXP_RATE, NullAnimator, &s_NapExp[10]}, {NAP_EXP + 9, NAP_EXP_RATE, NullAnimator, &s_NapExp[11]}, {NAP_EXP + 10, NAP_EXP_RATE, NullAnimator, &s_NapExp[12]}, {NAP_EXP + 11, NAP_EXP_RATE, NullAnimator, &s_NapExp[13]}, {NAP_EXP + 12, NAP_EXP_RATE, NullAnimator, &s_NapExp[14]}, {NAP_EXP + 13, NAP_EXP_RATE, NullAnimator, &s_NapExp[15]}, {NAP_EXP + 14, NAP_EXP_RATE, NullAnimator, &s_NapExp[16]}, {NAP_EXP + 15, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[17]}, {NAP_EXP + 16, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[18]}, {NAP_EXP + 17, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[19]}, {NAP_EXP + 18, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[20]}, {NAP_EXP + 19, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[21]}, {NAP_EXP + 20, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[22]}, {NAP_EXP + 21, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[23]}, {NAP_EXP + 21, NAP_EXP_RATE-2, DoSuicide, &s_NapExp[23]} }; ANIMATOR DoFireballFlames; #define FLAME_RATE 6 STATE s_FireballFlames[] = { {FIREBALL_FLAMES + 0, FLAME_RATE, DoFireballFlames, &s_FireballFlames[1]}, {FIREBALL_FLAMES + 1, FLAME_RATE, DoFireballFlames, &s_FireballFlames[2]}, {FIREBALL_FLAMES + 2, FLAME_RATE, DoFireballFlames, &s_FireballFlames[3]}, {FIREBALL_FLAMES + 3, FLAME_RATE, DoFireballFlames, &s_FireballFlames[4]}, {FIREBALL_FLAMES + 4, FLAME_RATE, DoFireballFlames, &s_FireballFlames[5]}, {FIREBALL_FLAMES + 5, FLAME_RATE, DoFireballFlames, &s_FireballFlames[6]}, {FIREBALL_FLAMES + 6, FLAME_RATE, DoFireballFlames, &s_FireballFlames[7]}, {FIREBALL_FLAMES + 7, FLAME_RATE, DoFireballFlames, &s_FireballFlames[8]}, {FIREBALL_FLAMES + 8, FLAME_RATE, DoFireballFlames, &s_FireballFlames[9]}, {FIREBALL_FLAMES + 9, FLAME_RATE, DoFireballFlames, &s_FireballFlames[10]}, {FIREBALL_FLAMES +10, FLAME_RATE, DoFireballFlames, &s_FireballFlames[11]}, {FIREBALL_FLAMES +11, FLAME_RATE, DoFireballFlames, &s_FireballFlames[12]}, {FIREBALL_FLAMES +12, FLAME_RATE, DoFireballFlames, &s_FireballFlames[13]}, {FIREBALL_FLAMES +13, FLAME_RATE, DoFireballFlames, &s_FireballFlames[0]}, }; ANIMATOR DoBreakFlames; #define FLAME_RATE 6 STATE s_BreakFlames[] = { {FIREBALL_FLAMES + 0, FLAME_RATE, DoBreakFlames, &s_BreakFlames[1]}, {FIREBALL_FLAMES + 1, FLAME_RATE, DoBreakFlames, &s_BreakFlames[2]}, {FIREBALL_FLAMES + 2, FLAME_RATE, DoBreakFlames, &s_BreakFlames[3]}, {FIREBALL_FLAMES + 3, FLAME_RATE, DoBreakFlames, &s_BreakFlames[4]}, {FIREBALL_FLAMES + 4, FLAME_RATE, DoBreakFlames, &s_BreakFlames[5]}, {FIREBALL_FLAMES + 5, FLAME_RATE, DoBreakFlames, &s_BreakFlames[6]}, {FIREBALL_FLAMES + 6, FLAME_RATE, DoBreakFlames, &s_BreakFlames[7]}, {FIREBALL_FLAMES + 7, FLAME_RATE, DoBreakFlames, &s_BreakFlames[8]}, {FIREBALL_FLAMES + 8, FLAME_RATE, DoBreakFlames, &s_BreakFlames[9]}, {FIREBALL_FLAMES + 9, FLAME_RATE, DoBreakFlames, &s_BreakFlames[10]}, {FIREBALL_FLAMES +10, FLAME_RATE, DoBreakFlames, &s_BreakFlames[11]}, {FIREBALL_FLAMES +11, FLAME_RATE, DoBreakFlames, &s_BreakFlames[12]}, {FIREBALL_FLAMES +12, FLAME_RATE, DoBreakFlames, &s_BreakFlames[13]}, {FIREBALL_FLAMES +13, FLAME_RATE, DoBreakFlames, &s_BreakFlames[0]}, }; ////////////////////// // // FIREBALL // ////////////////////// #if 1 ANIMATOR DoFireball; #define FIREBALL_RATE 8 #define GORO_FIREBALL FIREBALL+1 STATE s_Fireball[] = { {FIREBALL + 0, 12, DoFireball, &s_Fireball[1]}, {FIREBALL + 1, 12, DoFireball, &s_Fireball[2]}, {FIREBALL + 2, 12, DoFireball, &s_Fireball[3]}, {FIREBALL + 3, 12, DoFireball, &s_Fireball[0]} }; #else #define GORO_FIREBALL FIREBALL_R0 #define FIREBALL_RATE 6 ANIMATOR DoFireball; STATE s_Fireball[5][4] = { { {FIREBALL_R0 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[0][1]}, {FIREBALL_R0 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[0][2]}, {FIREBALL_R0 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[0][3]}, {FIREBALL_R0 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[0][0]}, }, { {FIREBALL_R1 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[1][1]}, {FIREBALL_R1 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[1][2]}, {FIREBALL_R1 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[1][3]}, {FIREBALL_R1 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[1][0]}, }, { {FIREBALL_R2 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[2][1]}, {FIREBALL_R2 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[2][2]}, {FIREBALL_R2 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[2][3]}, {FIREBALL_R2 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[2][0]}, }, { {FIREBALL_R3 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[3][1]}, {FIREBALL_R3 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[3][2]}, {FIREBALL_R3 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[3][3]}, {FIREBALL_R3 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[3][0]}, }, { {FIREBALL_R4 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[4][1]}, {FIREBALL_R4 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[4][2]}, {FIREBALL_R4 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[4][3]}, {FIREBALL_R4 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[4][0]}, } }; STATE* sg_Fireball[] = { s_Fireball[0], s_Fireball[1], s_Fireball[2], s_Fireball[3], s_Fireball[4] }; #endif #if 0 ANIMATOR DoRing; STATE s_Ring[5][4] = { { {FIREBALL_R0 + 0, FIREBALL_RATE, DoRing, &s_Ring[0][1]}, {FIREBALL_R0 + 1, FIREBALL_RATE, DoRing, &s_Ring[0][2]}, {FIREBALL_R0 + 2, FIREBALL_RATE, DoRing, &s_Ring[0][3]}, {FIREBALL_R0 + 3, FIREBALL_RATE, DoRing, &s_Ring[0][0]}, }, { {FIREBALL_R1 + 0, FIREBALL_RATE, DoRing, &s_Ring[1][1]}, {FIREBALL_R1 + 1, FIREBALL_RATE, DoRing, &s_Ring[1][2]}, {FIREBALL_R1 + 2, FIREBALL_RATE, DoRing, &s_Ring[1][3]}, {FIREBALL_R1 + 3, FIREBALL_RATE, DoRing, &s_Ring[1][0]}, }, { {FIREBALL_R2 + 0, FIREBALL_RATE, DoRing, &s_Ring[2][1]}, {FIREBALL_R2 + 1, FIREBALL_RATE, DoRing, &s_Ring[2][2]}, {FIREBALL_R2 + 2, FIREBALL_RATE, DoRing, &s_Ring[2][3]}, {FIREBALL_R2 + 3, FIREBALL_RATE, DoRing, &s_Ring[2][0]}, }, { {FIREBALL_R3 + 0, FIREBALL_RATE, DoRing, &s_Ring[3][1]}, {FIREBALL_R3 + 1, FIREBALL_RATE, DoRing, &s_Ring[3][2]}, {FIREBALL_R3 + 2, FIREBALL_RATE, DoRing, &s_Ring[3][3]}, {FIREBALL_R3 + 3, FIREBALL_RATE, DoRing, &s_Ring[3][0]}, }, { {FIREBALL_R4 + 0, FIREBALL_RATE, DoRing, &s_Ring[4][1]}, {FIREBALL_R4 + 1, FIREBALL_RATE, DoRing, &s_Ring[4][2]}, {FIREBALL_R4 + 2, FIREBALL_RATE, DoRing, &s_Ring[4][3]}, {FIREBALL_R4 + 3, FIREBALL_RATE, DoRing, &s_Ring[4][0]}, } }; STATE* sg_Ring[] = { s_Ring[0], s_Ring[1], s_Ring[2], s_Ring[3], s_Ring[4] }; #else ANIMATOR DoRing; STATE s_Ring[] = { {FIREBALL + 0, 12, DoRing, &s_Ring[1]}, {FIREBALL + 1, 12, DoRing, &s_Ring[2]}, {FIREBALL + 2, 12, DoRing, &s_Ring[3]}, {FIREBALL + 3, 12, DoRing, &s_Ring[0]} }; #endif STATE s_Ring2[] = { {2031 + 0, 12, DoRing, &s_Ring2[1]}, {2031 + 1, 12, DoRing, &s_Ring2[2]}, {2031 + 2, 12, DoRing, &s_Ring2[3]}, {2031 + 3, 12, DoRing, &s_Ring2[0]} }; ANIMATOR DoNapalm; STATE s_Napalm[] = { {FIREBALL + 0, 12, DoNapalm, &s_Napalm[1]}, {FIREBALL + 1, 12, DoNapalm, &s_Napalm[2]}, {FIREBALL + 2, 12, DoNapalm, &s_Napalm[3]}, {FIREBALL + 3, 12, DoNapalm, &s_Napalm[0]} }; ANIMATOR DoBloodWorm; #if 1 #define BLOOD_WORM 2106 STATE s_BloodWorm[] = { {BLOOD_WORM + 0, 12, DoBloodWorm, &s_BloodWorm[1]}, {BLOOD_WORM + 1, 12, DoBloodWorm, &s_BloodWorm[2]}, {BLOOD_WORM + 2, 12, DoBloodWorm, &s_BloodWorm[3]}, {BLOOD_WORM + 3, 12, DoBloodWorm, &s_BloodWorm[4]}, {BLOOD_WORM + 2, 12, DoBloodWorm, &s_BloodWorm[5]}, {BLOOD_WORM + 1, 12, DoBloodWorm, &s_BloodWorm[0]} }; #else #define BLOOD_WORM FIREBALL+5 STATE s_BloodWorm[] = { {FIREBALL + 0, 12, DoBloodWorm, &s_BloodWorm[1]}, {FIREBALL + 1, 12, DoBloodWorm, &s_BloodWorm[2]}, {FIREBALL + 2, 12, DoBloodWorm, &s_BloodWorm[3]}, {FIREBALL + 3, 12, DoBloodWorm, &s_BloodWorm[0]} }; #endif #if 1 #define PLASMA_EXP BLOOD_WORM+1 #define PLASMA_EXP_RATE 8 STATE s_PlasmaExp[] = { {BLOOD_WORM + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[1]}, {BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[2]}, {BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[3]}, {BLOOD_WORM + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[4]}, {BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[5]}, {BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[6]}, {BLOOD_WORM + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[7]}, {BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[8]}, {BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[9]}, {BLOOD_WORM + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[10]}, {BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[11]}, {BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[12]}, {BLOOD_WORM + 0, PLASMA_EXP_RATE, DoSuicide, &s_PlasmaExp[12]}, }; #else #define PLASMA_EXP (NAP_EXP+1) #define PLASMA_EXP_RATE 4 STATE s_PlasmaExp[] = { {PLASMA_EXP + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[1]}, {PLASMA_EXP + 0, 0 | SF_QUICK_CALL, DoDamageTest, &s_PlasmaExp[2]}, {PLASMA_EXP + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[3]}, {PLASMA_EXP + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[4]}, {PLASMA_EXP + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[5]}, {PLASMA_EXP + 4, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[6]}, {PLASMA_EXP + 5, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[7]}, {PLASMA_EXP + 6, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[8]}, {PLASMA_EXP + 7, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[9]}, {PLASMA_EXP + 8, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[10]}, {PLASMA_EXP + 9, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[11]}, {PLASMA_EXP + 10, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[12]}, {PLASMA_EXP + 11, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[13]}, {PLASMA_EXP + 9, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[14]}, {PLASMA_EXP + 8, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[15]}, {PLASMA_EXP + 7, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[16]}, {PLASMA_EXP + 6, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[17]}, {PLASMA_EXP + 5, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[18]}, {PLASMA_EXP + 4, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[19]}, {PLASMA_EXP + 3, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[20]}, {PLASMA_EXP + 2, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[21]}, {PLASMA_EXP + 1, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[22]}, {PLASMA_EXP + 1, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[23]}, {PLASMA_EXP + 1, PLASMA_EXP_RATE-2, DoSuicide, &s_PlasmaExp[23]} }; #endif ANIMATOR DoMirv; STATE s_Mirv[] = { {FIREBALL + 0, 12, DoMirv, &s_Mirv[1]}, {FIREBALL + 1, 12, DoMirv, &s_Mirv[2]}, {FIREBALL + 2, 12, DoMirv, &s_Mirv[3]}, {FIREBALL + 3, 12, DoMirv, &s_Mirv[0]} }; ANIMATOR DoMirvMissile; STATE s_MirvMissile[] = { {FIREBALL + 0, 12, DoMirvMissile, &s_MirvMissile[1]}, {FIREBALL + 1, 12, DoMirvMissile, &s_MirvMissile[2]}, {FIREBALL + 2, 12, DoMirvMissile, &s_MirvMissile[3]}, {FIREBALL + 3, 12, DoMirvMissile, &s_MirvMissile[0]} }; //#define Vomit1 1740 //#define Vomit2 1741 #define Vomit1 1719 #define Vomit2 1721 //#define VomitSplash 1742 #define VomitSplash 1711 #define Vomit_RATE 16 ANIMATOR DoVomit,DoVomitSplash; STATE s_Vomit1[] = { {Vomit1 + 0, Vomit_RATE, DoVomit, &s_Vomit1[0]} }; STATE s_Vomit2[] = { {Vomit2 + 0, Vomit_RATE, DoVomit, &s_Vomit2[0]} }; STATE s_VomitSplash[] = { {VomitSplash + 0, Vomit_RATE, DoVomitSplash, &s_VomitSplash[0]} }; #define GORE_Head 1670 #define GORE_Head_RATE 16 STATE s_GoreHead[] = { {GORE_Head + 0, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[1]}, {GORE_Head + 1, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[2]}, {GORE_Head + 2, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[3]}, {GORE_Head + 3, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[4]}, {GORE_Head + 4, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[5]}, {GORE_Head + 5, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[6]}, {GORE_Head + 6, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[7]}, {GORE_Head + 7, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[8]}, {GORE_Head + 8, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[9]}, {GORE_Head + 9, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[10]}, {GORE_Head + 10, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[11]}, {GORE_Head + 11, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[0]}, }; #define GORE_Leg 1689 #define GORE_Leg_RATE 16 STATE s_GoreLeg[] = { {GORE_Leg + 0, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[1]}, {GORE_Leg + 1, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[2]}, {GORE_Leg + 2, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[0]}, }; #define GORE_Eye 1692 #define GORE_Eye_RATE 16 STATE s_GoreEye[] = { {GORE_Eye + 0, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[1]}, {GORE_Eye + 1, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[2]}, {GORE_Eye + 2, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[3]}, {GORE_Eye + 3, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[0]}, }; #define GORE_Torso 1696 #define GORE_Torso_RATE 16 STATE s_GoreTorso[] = { {GORE_Torso + 0, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[1]}, {GORE_Torso + 1, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[2]}, {GORE_Torso + 2, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[3]}, {GORE_Torso + 3, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[4]}, {GORE_Torso + 4, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[5]}, {GORE_Torso + 5, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[6]}, {GORE_Torso + 6, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[7]}, {GORE_Torso + 7, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[0]}, }; #define GORE_Arm 1550 #define GORE_Arm_RATE 16 STATE s_GoreArm[] = { {GORE_Arm + 0, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[1]}, {GORE_Arm + 1, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[2]}, {GORE_Arm + 2, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[3]}, {GORE_Arm + 3, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[4]}, {GORE_Arm + 4, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[5]}, {GORE_Arm + 5, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[6]}, {GORE_Arm + 6, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[7]}, {GORE_Arm + 7, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[8]}, {GORE_Arm + 8, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[9]}, {GORE_Arm + 9, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[10]}, {GORE_Arm + 10, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[11]}, {GORE_Arm + 11, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[0]}, }; #define GORE_Lung 903 #define GORE_Lung_RATE 16 STATE s_GoreLung[] = { {GORE_Lung + 0, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[1]}, {GORE_Lung + 1, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[2]}, {GORE_Lung + 2, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[3]}, {GORE_Lung + 3, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[4]}, {GORE_Lung + 4, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[5]}, {GORE_Lung + 5, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[6]}, {GORE_Lung + 6, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[7]}, {GORE_Lung + 7, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[8]}, {GORE_Lung + 8, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[9]}, {GORE_Lung + 9, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[10]}, {GORE_Lung + 10, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[11]}, {GORE_Lung + 11, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[0]}, }; #define GORE_Liver 918 #define GORE_Liver_RATE 16 STATE s_GoreLiver[] = { {GORE_Liver + 0, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[1]}, {GORE_Liver + 1, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[2]}, {GORE_Liver + 2, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[3]}, {GORE_Liver + 3, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[4]}, {GORE_Liver + 4, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[5]}, {GORE_Liver + 5, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[6]}, {GORE_Liver + 6, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[7]}, {GORE_Liver + 7, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[8]}, {GORE_Liver + 8, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[9]}, {GORE_Liver + 9, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[10]}, {GORE_Liver + 10, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[11]}, {GORE_Liver + 11, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[0]}, }; #define GORE_SkullCap 933 #define GORE_SkullCap_RATE 16 STATE s_GoreSkullCap[] = { {GORE_SkullCap + 0, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[1]}, {GORE_SkullCap + 1, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[2]}, {GORE_SkullCap + 2, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[3]}, {GORE_SkullCap + 3, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[4]}, {GORE_SkullCap + 4, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[5]}, {GORE_SkullCap + 5, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[6]}, {GORE_SkullCap + 6, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[7]}, {GORE_SkullCap + 7, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[8]}, {GORE_SkullCap + 8, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[9]}, {GORE_SkullCap + 9, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[10]}, {GORE_SkullCap + 10, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[11]}, {GORE_SkullCap + 11, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[0]}, }; #define GORE_ChunkS 2430 #define GORE_ChunkS_RATE 16 STATE s_GoreChunkS[] = { {GORE_ChunkS + 0, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[1]}, {GORE_ChunkS + 1, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[2]}, {GORE_ChunkS + 2, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[3]}, {GORE_ChunkS + 3, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[0]}, }; #define GORE_Drip 1562 //2430 #define GORE_Drip_RATE 16 STATE s_GoreDrip[] = { {GORE_Drip + 0, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[1]}, {GORE_Drip + 1, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[2]}, {GORE_Drip + 2, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[3]}, {GORE_Drip + 3, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[0]}, }; STATE s_FastGoreDrip[] = { {GORE_Drip + 0, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[1]}, {GORE_Drip + 1, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[2]}, {GORE_Drip + 2, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[3]}, {GORE_Drip + 3, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[0]}, }; /////////////////////////////////////////////// // // This GORE mostly for the Accursed Heads // /////////////////////////////////////////////// #define GORE_Flame 847 #define GORE_Flame_RATE 8 STATE s_GoreFlame[] = { {GORE_Flame + 0, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[1]}, {GORE_Flame + 1, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[2]}, {GORE_Flame + 2, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[3]}, {GORE_Flame + 3, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[4]}, {GORE_Flame + 4, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[5]}, {GORE_Flame + 5, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[6]}, {GORE_Flame + 6, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[7]}, {GORE_Flame + 7, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[0]}, }; ANIMATOR DoTracerShrap; STATE s_TracerShrap[] = { {GORE_Flame + 0, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[1]}, {GORE_Flame + 1, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[2]}, {GORE_Flame + 2, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[3]}, {GORE_Flame + 3, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[4]}, {GORE_Flame + 4, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[5]}, {GORE_Flame + 5, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[6]}, {GORE_Flame + 6, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[7]}, {GORE_Flame + 7, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[0]}, }; #define UZI_SHELL 2152 #define UZISHELL_RATE 8 //ANIMATOR DoShellShrap; STATE s_UziShellShrap[] = { {UZI_SHELL + 0, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[1]}, {UZI_SHELL + 1, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[2]}, {UZI_SHELL + 2, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[3]}, {UZI_SHELL + 3, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[4]}, {UZI_SHELL + 4, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[5]}, {UZI_SHELL + 5, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[0]}, }; STATE s_UziShellShrapStill1[] = { {UZI_SHELL + 0, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill1[0]} }; STATE s_UziShellShrapStill2[] = { {UZI_SHELL + 1, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill2[0]} }; STATE s_UziShellShrapStill3[] = { {UZI_SHELL + 2, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill3[0]} }; STATE s_UziShellShrapStill4[] = { {UZI_SHELL + 3, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill4[0]} }; STATE s_UziShellShrapStill5[] = { {UZI_SHELL + 4, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill5[0]} }; STATE s_UziShellShrapStill6[] = { {UZI_SHELL + 5, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill6[0]} }; #define SHOT_SHELL 2180 #define SHOTSHELL_RATE 8 STATE s_ShotgunShellShrap[] = { {SHOT_SHELL + 0, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[1]}, {SHOT_SHELL + 1, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[2]}, {SHOT_SHELL + 2, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[3]}, {SHOT_SHELL + 3, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[4]}, {SHOT_SHELL + 4, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[5]}, {SHOT_SHELL + 5, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[6]}, {SHOT_SHELL + 6, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[7]}, {SHOT_SHELL + 7, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[0]}, }; STATE s_ShotgunShellShrapStill1[] = { {SHOT_SHELL + 1, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill1[0]} }; STATE s_ShotgunShellShrapStill2[] = { {SHOT_SHELL + 3, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill2[0]} }; STATE s_ShotgunShellShrapStill3[] = { {SHOT_SHELL + 7, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill3[0]} }; #define GORE_FlameChunkA 839 #define GORE_FlameChunkA_RATE 8 STATE s_GoreFlameChunkA[] = { {GORE_FlameChunkA + 0, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[1]}, {GORE_FlameChunkA + 1, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[2]}, {GORE_FlameChunkA + 2, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[3]}, {GORE_FlameChunkA + 3, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[0]}, }; #define GORE_FlameChunkB 843 #define GORE_FlameChunkB_RATE 8 STATE s_GoreFlameChunkB[] = { {GORE_FlameChunkB + 0, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[1]}, {GORE_FlameChunkB + 1, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[2]}, {GORE_FlameChunkB + 2, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[3]}, {GORE_FlameChunkB + 3, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[0]}, }; ///////////////////////////////////////////////////////////////////// // // General Breaking Shrapnel // ///////////////////////////////////////////////////////////////////// #define COIN_SHRAP 2530 #define CoinShrap_RATE 12 STATE s_CoinShrap[] = { {COIN_SHRAP + 0, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[1]}, {COIN_SHRAP + 1, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[2]}, {COIN_SHRAP + 2, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[3]}, {COIN_SHRAP + 3, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[0]}, }; #define MARBEL 5096 #define Marbel_RATE 12 STATE s_Marbel[] = { {MARBEL, Marbel_RATE, DoShrapJumpFall, &s_Marbel[0]}, }; // // Glass // #define GLASS_SHRAP_A 3864 #define GlassShrapA_RATE 12 STATE s_GlassShrapA[] = { {GLASS_SHRAP_A + 0, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[1]}, {GLASS_SHRAP_A + 1, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[2]}, {GLASS_SHRAP_A + 2, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[3]}, {GLASS_SHRAP_A + 3, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[4]}, {GLASS_SHRAP_A + 4, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[5]}, {GLASS_SHRAP_A + 5, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[6]}, {GLASS_SHRAP_A + 6, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[7]}, {GLASS_SHRAP_A + 7, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[0]}, }; #define GLASS_SHRAP_B 3872 #define GlassShrapB_RATE 12 STATE s_GlassShrapB[] = { {GLASS_SHRAP_B + 0, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[1]}, {GLASS_SHRAP_B + 1, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[2]}, {GLASS_SHRAP_B + 2, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[3]}, {GLASS_SHRAP_B + 3, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[4]}, {GLASS_SHRAP_B + 4, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[5]}, {GLASS_SHRAP_B + 5, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[6]}, {GLASS_SHRAP_B + 6, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[7]}, {GLASS_SHRAP_B + 7, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[0]}, }; #define GLASS_SHRAP_C 3880 #define GlassShrapC_RATE 12 STATE s_GlassShrapC[] = { {GLASS_SHRAP_C + 0, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[1]}, {GLASS_SHRAP_C + 1, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[2]}, {GLASS_SHRAP_C + 2, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[3]}, {GLASS_SHRAP_C + 3, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[4]}, {GLASS_SHRAP_C + 4, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[5]}, {GLASS_SHRAP_C + 5, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[6]}, {GLASS_SHRAP_C + 6, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[7]}, {GLASS_SHRAP_C + 7, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[0]}, }; // // Wood // #define WOOD_SHRAP_A 3924 #define WoodShrapA_RATE 12 STATE s_WoodShrapA[] = { {WOOD_SHRAP_A + 0, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[1]}, {WOOD_SHRAP_A + 1, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[2]}, {WOOD_SHRAP_A + 2, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[3]}, {WOOD_SHRAP_A + 3, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[4]}, {WOOD_SHRAP_A + 4, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[5]}, {WOOD_SHRAP_A + 5, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[6]}, {WOOD_SHRAP_A + 6, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[7]}, {WOOD_SHRAP_A + 7, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[0]}, }; #define WOOD_SHRAP_B 3932 #define WoodShrapB_RATE 12 STATE s_WoodShrapB[] = { {WOOD_SHRAP_B + 0, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[1]}, {WOOD_SHRAP_B + 1, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[2]}, {WOOD_SHRAP_B + 2, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[3]}, {WOOD_SHRAP_B + 3, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[4]}, {WOOD_SHRAP_B + 4, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[5]}, {WOOD_SHRAP_B + 5, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[6]}, {WOOD_SHRAP_B + 6, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[7]}, {WOOD_SHRAP_B + 7, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[0]}, }; #define WOOD_SHRAP_C 3941 #define WoodShrapC_RATE 12 STATE s_WoodShrapC[] = { {WOOD_SHRAP_C + 0, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[1]}, {WOOD_SHRAP_C + 1, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[2]}, {WOOD_SHRAP_C + 2, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[3]}, {WOOD_SHRAP_C + 3, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[4]}, {WOOD_SHRAP_C + 4, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[5]}, {WOOD_SHRAP_C + 5, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[6]}, {WOOD_SHRAP_C + 6, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[7]}, {WOOD_SHRAP_C + 7, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[0]}, }; // // Stone // #define STONE_SHRAP_A 3840 #define StoneShrapA_RATE 12 STATE s_StoneShrapA[] = { {STONE_SHRAP_A + 0, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[1]}, {STONE_SHRAP_A + 1, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[2]}, {STONE_SHRAP_A + 2, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[3]}, {STONE_SHRAP_A + 3, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[4]}, {STONE_SHRAP_A + 4, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[5]}, {STONE_SHRAP_A + 5, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[6]}, {STONE_SHRAP_A + 6, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[7]}, {STONE_SHRAP_A + 7, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[0]}, }; #define STONE_SHRAP_B 3848 #define StoneShrapB_RATE 12 STATE s_StoneShrapB[] = { {STONE_SHRAP_B + 0, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[1]}, {STONE_SHRAP_B + 1, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[2]}, {STONE_SHRAP_B + 2, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[3]}, {STONE_SHRAP_B + 3, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[4]}, {STONE_SHRAP_B + 4, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[5]}, {STONE_SHRAP_B + 5, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[6]}, {STONE_SHRAP_B + 6, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[7]}, {STONE_SHRAP_B + 7, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[0]}, }; #define STONE_SHRAP_C 3856 #define StoneShrapC_RATE 12 STATE s_StoneShrapC[] = { {STONE_SHRAP_C + 0, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[1]}, {STONE_SHRAP_C + 1, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[2]}, {STONE_SHRAP_C + 2, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[3]}, {STONE_SHRAP_C + 3, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[4]}, {STONE_SHRAP_C + 4, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[5]}, {STONE_SHRAP_C + 5, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[6]}, {STONE_SHRAP_C + 6, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[7]}, {STONE_SHRAP_C + 7, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[0]}, }; // // Metal // #define METAL_SHRAP_A 3888 #define MetalShrapA_RATE 12 STATE s_MetalShrapA[] = { {METAL_SHRAP_A + 0, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[1]}, {METAL_SHRAP_A + 1, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[2]}, {METAL_SHRAP_A + 2, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[3]}, {METAL_SHRAP_A + 3, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[4]}, {METAL_SHRAP_A + 4, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[5]}, {METAL_SHRAP_A + 5, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[6]}, {METAL_SHRAP_A + 6, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[7]}, {METAL_SHRAP_A + 7, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[0]}, }; #define METAL_SHRAP_B 3896 #define MetalShrapB_RATE 12 STATE s_MetalShrapB[] = { {METAL_SHRAP_B + 0, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[1]}, {METAL_SHRAP_B + 1, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[2]}, {METAL_SHRAP_B + 2, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[3]}, {METAL_SHRAP_B + 3, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[4]}, {METAL_SHRAP_B + 4, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[5]}, {METAL_SHRAP_B + 5, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[6]}, {METAL_SHRAP_B + 6, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[7]}, {METAL_SHRAP_B + 7, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[0]}, }; #define METAL_SHRAP_C 3904 #define MetalShrapC_RATE 12 STATE s_MetalShrapC[] = { {METAL_SHRAP_C + 0, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[1]}, {METAL_SHRAP_C + 1, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[2]}, {METAL_SHRAP_C + 2, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[3]}, {METAL_SHRAP_C + 3, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[4]}, {METAL_SHRAP_C + 4, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[5]}, {METAL_SHRAP_C + 5, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[6]}, {METAL_SHRAP_C + 6, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[7]}, {METAL_SHRAP_C + 7, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[0]}, }; // // Paper // #define PAPER_SHRAP_A 3924 #define PaperShrapA_RATE 12 STATE s_PaperShrapA[] = { {PAPER_SHRAP_A + 0, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[1]}, {PAPER_SHRAP_A + 1, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[2]}, {PAPER_SHRAP_A + 2, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[3]}, {PAPER_SHRAP_A + 3, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[0]}, }; #define PAPER_SHRAP_B 3932 #define PaperShrapB_RATE 12 STATE s_PaperShrapB[] = { {PAPER_SHRAP_B + 0, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[1]}, {PAPER_SHRAP_B + 1, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[2]}, {PAPER_SHRAP_B + 2, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[3]}, {PAPER_SHRAP_B + 3, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[0]}, }; #define PAPER_SHRAP_C 3941 #define PaperShrapC_RATE 12 STATE s_PaperShrapC[] = { {PAPER_SHRAP_C + 0, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[1]}, {PAPER_SHRAP_C + 1, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[2]}, {PAPER_SHRAP_C + 2, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[3]}, {PAPER_SHRAP_C + 3, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[0]}, }; bool MissileHitMatch(DSWActor* weapActor, int WeaponNum, DSWActor* hitActor) { if (WeaponNum <= -1) { ASSERT(weapActor != nullptr); WeaponNum = weapActor->user.WeaponNum; // can be hit by SO only if (SP_TAG7(hitActor) == 4) { if ((weapActor->user.Flags2 & SPR2_SO_MISSILE)) { DoMatchEverything(nullptr, hitActor->spr.hitag, -1); return true; } else { return false; } } } if (SP_TAG7(hitActor) == 0) { switch (WeaponNum) { case WPN_RAIL: case WPN_MICRO: case WPN_NAPALM: case WPN_ROCKET: DoMatchEverything(nullptr, hitActor->spr.hitag, -1); return true; } } else if (SP_TAG7(hitActor) == 1) { switch (WeaponNum) { case WPN_MICRO: case WPN_RAIL: case WPN_HOTHEAD: case WPN_NAPALM: case WPN_ROCKET: DoMatchEverything(nullptr, hitActor->spr.hitag, -1); return true; } } else if (SP_TAG7(hitActor) == 2) { switch (WeaponNum) { case WPN_MICRO: case WPN_RAIL: case WPN_HOTHEAD: case WPN_NAPALM: case WPN_ROCKET: case WPN_UZI: case WPN_SHOTGUN: DoMatchEverything(nullptr, hitActor->spr.hitag, -1); return true; } } else if (SP_TAG7(hitActor) == 3) { switch (WeaponNum) { case WPN_STAR: case WPN_SWORD: case WPN_FIST: case WPN_MICRO: case WPN_RAIL: case WPN_HOTHEAD: case WPN_NAPALM: case WPN_ROCKET: case WPN_UZI: case WPN_SHOTGUN: DoMatchEverything(nullptr, hitActor->spr.hitag, -1); return true; } } return false; #if 0 WPN_STAR WPN_UZI WPN_SHOTGUN WPN_MICRO WPN_GRENADE WPN_MINE WPN_RAIL WPN_HEART WPN_HOTHEAD WPN_NAPALM WPN_RING WPN_ROCKET WPN_SWORD WPN_FIST #endif } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnShrapX(DSWActor* actor) { //For shrap that has no Weapon to send over SpawnShrap(actor, nullptr); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoLavaErupt(DSWActor* actor) { short i,pnum; PLAYER* pp; bool found = false; if (TEST_BOOL1(actor)) { TRAVERSE_CONNECT(pnum) { pp = Player + pnum; if (pp->insector() && (pp->cursector->extra & SECTFX_TRIGGER)) { SWSectIterator it(pp->cursector); while (auto itActor = it.Next()) { if (itActor->spr.statnum == STAT_TRIGGER && SP_TAG7(itActor) == 0 && SP_TAG5(itActor) == 1) { found = true; break; } } if (found) break; } } if (!found) return 0; } if (!(actor->user.Flags & SPR_ACTIVE)) { // inactive if ((actor->user.WaitTics -= synctics) <= 0) { actor->user.Flags |= (SPR_ACTIVE); actor->user.Counter = 0; actor->user.WaitTics = SP_TAG9(actor) * 120L; } } else { // active if ((actor->user.WaitTics -= synctics) <= 0) { // Stop for this long actor->user.Flags &= ~(SPR_ACTIVE); actor->user.Counter = 0; actor->user.WaitTics = SP_TAG10(actor) * 120L; } // Counter controls the volume of lava erupting // starts out slow and increases to a max actor->user.Counter += synctics; if (actor->user.Counter > SP_TAG2(actor)) actor->user.Counter = SP_TAG2(actor); if ((RANDOM_P2(1024<<6)>>6) < actor->user.Counter) { switch (SP_TAG3(actor)) { case 0: SpawnShrapX(actor); break; case 1: InitVulcanBoulder(actor); break; } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnShrap(DSWActor* parentActor, DSWActor* secondaryActor, int means, BREAK_INFO* breakinfo) { ///////////////////////////////////////// // // BREAK shrap types // ///////////////////////////////////////// // Individual shraps can be copied to this and then values can be changed static SHRAP CustomShrap[20]; static SHRAP CoinShrap[] = { {s_CoinShrap, COIN_SHRAP, 5, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP GlassShrap[] = { {s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP WoodShrap[] = { {s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP StoneShrap[] = { {s_StoneShrapA, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_StoneShrapB, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_StoneShrapC, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP PaperShrap[] = { {s_PaperShrapA, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_PaperShrapB, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_PaperShrapC, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP MetalShrap[] = { {s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP MetalMix[] = { {s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP WoodMix[] = { {s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP PaperMix[] = { {s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_PaperShrapA, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, true, 2048}, {s_PaperShrapB, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, true, 2048}, {s_PaperShrapC, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP Marbels[] = { {s_Marbel, MARBEL, 5, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP BloodShrap[] = { {s_BloodShrap, BLOOD_SHRAP, 8, Z_MID, 200, 600, 100, 500, true, 2048}, {nullptr}, }; #endif //// // END - BREAK shrap types //// static SHRAP EMPShrap[] = { {s_EMPShrap, EMP, 1, Z_MID, 500, 1100, 300, 600, false, 128}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP StdShrap[] = { {s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, true, 2048}, {s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, true, 2048}, {s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, true, 2048}, {s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 250, 500, true, 2048}, {s_FastGoreDrip, GORE_Drip, 8, Z_BOT, 600, 800, 50, 70, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP HeartAttackShrap[] = // fewer gibs because of the plasma fountain sprites { {s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 1100, 300, 600, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 500, 1100, 300, 500, true, 2048}, {s_GoreArm, GORE_Arm, 2, Z_MID, 500, 1100, 350, 600, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP SkelGore[] = { {s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, true, 2048}, {s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, true, 2048}, {s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 100, 150, true, 2048}, {s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, true, 2048}, {s_GoreLeg, GORE_Leg, 2, Z_BOT, 200, 400, 250, 500, true, 2048}, {s_GoreChunkS, GORE_ChunkS, 4, Z_BOT, 200, 400, 250, 400, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP UpperGore[] = { {s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, true, 2048}, {s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, true, 2048}, {s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 100, 150, true, 2048}, {s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP LowerGore[] = { {s_GoreLeg, GORE_Leg, 4, Z_BOT, 300, 500, 100, 200, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #endif static SHRAP SmallGore[] = { {s_GoreDrip, GORE_Drip, 3, Z_TOP, 600, 800, 50, 70, false, 2048}, {s_FastGoreDrip, GORE_Drip, 3, Z_BOT, 600, 800, 70, 100, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP FlamingGore[] = { {s_GoreFlame, GORE_Drip, 2, Z_TOP, 600, 800, 100, 200, false, 2048}, {s_GoreFlameChunkB, GORE_Drip, 4, Z_MID, 300, 500, 100, 200, false, 2048}, {s_GoreFlame, GORE_Drip, 2, Z_BOT, 100, 200, 100, 200, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP BoltExpShrap[] = { {s_GoreFlame, GORE_Drip, 4, Z_MID, 300, 700, 300, 600, true, 2048}, {s_GoreFlame, GORE_Drip, 4, Z_BOT, 300, 700, 300, 600, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP TracerExpShrap[] = { {s_TracerShrap, GORE_Drip, 3, Z_MID, 300, 700, 300, 600, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP FireballExpShrap1[] = { {s_GoreFlame, GORE_Drip, 1, Z_MID, 100, 300, 100, 200, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP FireballExpShrap2[] = { {s_GoreFlame, GORE_Drip, 2, Z_MID, 100, 300, 100, 200, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP* FireballExpShrap[] = { FireballExpShrap1, FireballExpShrap2 }; #endif // state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel, // random_disperse, ang_range; static SHRAP ElectroShrap[] = { {s_ElectroShrap, ELECTRO_SHARD, 12, Z_TOP, 200, 600, 100, 500, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; // state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel, // random_disperse, ang_range; static SHRAP LavaShrap1[] = { {s_GoreFlame, GORE_Drip, 1, Z_TOP, 400, 1400, 100, 400, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP LavaShrap2[] = { {s_GoreFlameChunkB, GORE_Drip, 1, Z_TOP, 400, 1400, 100, 400, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP *LavaShrapTable[] = { LavaShrap1, LavaShrap2 }; static SHRAP LavaBoulderShrap[] = { {s_LavaShard, LAVA_SHARD, 16, Z_MID, 400, 900, 200, 600, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP SectorSquishGore[] = { {s_FastGoreDrip, GORE_Drip, 24, Z_MID, -400, -200, 600, 800, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #endif // // PLAYER SHRAP // // state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel, // random_disperse, ang_range; static SHRAP PlayerGoreFall[] = { {s_GoreSkullCap,GORE_SkullCap, 1, Z_TOP, 200, 300, 100, 200, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 200, 300, 100, 200, true, 2048}, {s_GoreLung, GORE_Lung, 1, Z_MID, 200, 300, 100, 200, true, 2048}, {s_GoreDrip, GORE_Drip, 10, Z_MID, 200, 300, 100, 200, false, 2048}, {s_GoreArm, GORE_Arm, 1, Z_MID, 200, 300, 100, 200, true, 2048}, {s_FastGoreDrip, GORE_Drip, 10, Z_BOT, 200, 300, 100, 200, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP PlayerGoreFly[] = { {s_GoreSkullCap,GORE_SkullCap, 1, Z_TOP, 500, 1100, 300, 600, true, 2048}, {s_GoreTorso, GORE_Torso, 1, Z_MID, 500, 1100, 300, 500, true, 2048}, {s_GoreLiver, GORE_Liver, 1, Z_MID, 200, 300, 100, 200, true, 2048}, {s_GoreArm, GORE_Arm, 1, Z_MID, 500, 1100, 350, 600, true, 2048}, {s_FastGoreDrip, GORE_Drip, 16, Z_MID, 500, 1100, 350, 600, false, 2048}, {s_FastGoreDrip, GORE_Drip, 16, Z_BOT, 500, 1100, 350, 600, false, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP PlayerDeadHead[] = { {s_GoreDrip, GORE_Drip, 2, Z_TOP, 150, 400, 40, 80, true, 2048}, {s_GoreDrip, GORE_Drip, 2, Z_MID, 150, 400, 40, 80, true, 2048}, {nullptr,0,0,0,0,0,0,0,0,0}, }; // state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel, // random_disperse, ang_range; static SHRAP PlayerHeadHurl1[] = { {s_Vomit1, Vomit1, 1, Z_BOT, 250, 400, 100, 200, true, 256}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #define WALL_FLOOR_SHRAP 4097 ANIMATOR DoShrapWallFloor; #if 0 static SHRAP SectorExpShrap[] = { {nullptr, WALL_FLOOR_SHRAP, 1, Z_BOT, 550, 800, 200, 400, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #endif double hZ[3]; DAngle dangl = nullAngle; SHRAP* p = SmallGore; short shrap_shade = -15; short shrap_xsize = 48, shrap_ysize = 48; short retval = true; short shrap_pal = PALETTE_DEFAULT; short jump_grav = ACTOR_GRAVITY; DAngle start_angl = nullAngle; DSWActor* ShrapOwner = nullptr; int shrap_bounce = false; short WaitTics = 64; // for FastShrap short shrap_type; int shrap_rand_zamt = 0; DAngle shrap_angl = parentActor->spr.Angles.Yaw; short shrap_delta_size = 0; short shrap_amt = 0; if (Prediction) return 0; // Don't spawn shrapnel in invalid sectors gosh dern it! if (!parentActor->insector()) { return 0; } if (breakinfo) { shrap_type = breakinfo->shrap_type; shrap_amt = breakinfo->shrap_amt; goto AutoShrap; } else if ((parentActor->spr.extra & SPRX_BREAKABLE)) { // if no user if (!parentActor->hasU()) { // Jump to shrap type shrap_type = SP_TAG8(parentActor); goto UserShrap; } else { // has a user - is programmed change_actor_stat(parentActor, STAT_MISC); parentActor->spr.extra &= ~(SPRX_BREAKABLE); parentActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); } } ASSERT(parentActor->hasU()); switch (parentActor->user.ID) { case ST1: switch (parentActor->spr.hitag) { case SPAWN_SPOT: { if (parentActor->user.LastDamage) shrap_type = SP_TAG3(parentActor); else shrap_type = SP_TAG6(parentActor); UserShrap: shrap_delta_size = (int8_t)SP_TAG10(parentActor); shrap_rand_zamt = SP_TAG9(parentActor); // Hey, better limit this in case mappers go crazy, like I did. :) // Kills frame rate! shrap_amt = SP_TAG8(parentActor); if (shrap_amt > 5) shrap_amt = 5; AutoShrap: switch (shrap_type) { case SHRAP_NONE: return false; case SHRAP_GLASS: PlaySound(DIGI_BREAKGLASS, parentActor, v3df_dontpan | v3df_doppler); p = GlassShrap; if (shrap_amt) { memcpy(CustomShrap, GlassShrap, sizeof(GlassShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_GENERIC: case SHRAP_STONE: PlaySound(DIGI_BREAKSTONES,parentActor,v3df_dontpan|v3df_doppler); p = StoneShrap; if (shrap_amt) { memcpy(CustomShrap, StoneShrap, sizeof(StoneShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 8 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_WOOD: PlaySound(DIGI_BREAKINGWOOD,parentActor,v3df_dontpan|v3df_doppler); p = WoodShrap; if (shrap_amt) { memcpy(CustomShrap, WoodShrap, sizeof(WoodShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_BLOOD: shrap_xsize = shrap_ysize = 16 + shrap_delta_size; break; case SHRAP_GIBS: PlaySound(DIGI_GIBS1,parentActor,v3df_dontpan|v3df_doppler); p = SmallGore; shrap_xsize = shrap_ysize = 34; shrap_bounce = false; break; case SHRAP_TREE_BARK: PlaySound(DIGI_BREAKINGWOOD,parentActor,v3df_dontpan|v3df_doppler); p = WoodShrap; if (shrap_amt) { memcpy(CustomShrap, WoodShrap, sizeof(WoodShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_PAPER: p = PaperShrap; if (shrap_amt) { memcpy(CustomShrap, PaperShrap, sizeof(PaperShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; break; case SHRAP_METAL: PlaySound(DIGI_BREAKMETAL,parentActor,v3df_dontpan|v3df_doppler); p = MetalShrap; if (shrap_amt) { memcpy(CustomShrap, MetalShrap, sizeof(MetalShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_COIN: PlaySound(DIGI_COINS,parentActor,v3df_dontpan|v3df_doppler); p = CoinShrap; if (shrap_amt) { memcpy(CustomShrap, CoinShrap, sizeof(CoinShrap)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_METALMIX: PlaySound(DIGI_BREAKMETAL,parentActor,v3df_dontpan|v3df_doppler); p = MetalMix; if (shrap_amt) { memcpy(CustomShrap, MetalMix, sizeof(MetalMix)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_MARBELS: { p = Marbels; if (shrap_amt) { memcpy(CustomShrap, Marbels, sizeof(Marbels)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 10 + shrap_delta_size; shrap_bounce = true; } break; case SHRAP_WOODMIX: PlaySound(DIGI_BREAKINGWOOD,parentActor,v3df_dontpan|v3df_doppler); p = WoodMix; if (shrap_amt) { memcpy(CustomShrap, WoodMix, sizeof(WoodMix)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = true; break; case SHRAP_PAPERMIX: PlaySound(DIGI_BREAKINGWOOD,parentActor,v3df_dontpan|v3df_doppler); p = PaperMix; if (shrap_amt) { memcpy(CustomShrap, PaperMix, sizeof(PaperMix)); CustomShrap->num = shrap_amt; p = CustomShrap; } shrap_xsize = shrap_ysize = 16 + shrap_delta_size; shrap_bounce = false; break; case SHRAP_SO_SMOKE: return false; case SHRAP_EXPLOSION: { auto spnum = SpawnLargeExp(parentActor); double size = spnum->spr.scale.X + shrap_delta_size * REPEAT_SCALE; spnum->spr.scale = DVector2(size, size); return false; } case SHRAP_LARGE_EXPLOSION: { auto spnum = SpawnLargeExp(parentActor); double size = spnum->spr.scale.X + shrap_delta_size * REPEAT_SCALE; spnum->spr.scale = DVector2(size, size); InitPhosphorus(spnum); return false; } default: { return false; } } break; } default: p = LavaShrapTable[RANDOM_P2(2<<8)>>8]; } break; case BREAK_BARREL: PlaySound(DIGI_BREAKDEBRIS,parentActor,v3df_dontpan|v3df_doppler); p = WoodShrap; shrap_xsize = shrap_ysize = 24; shrap_bounce = true; ChangeState(parentActor, s_BreakBarrel); break; case BREAK_LIGHT: PlaySound(DIGI_BREAKGLASS,parentActor,v3df_dontpan|v3df_doppler); p = GlassShrap; shrap_xsize = shrap_ysize = 24; shrap_bounce = true; ChangeState(parentActor, s_BreakLight); break; case BREAK_PEDISTAL: PlaySound(DIGI_BREAKSTONES,parentActor,v3df_dontpan|v3df_doppler); p = StoneShrap; shrap_xsize = shrap_ysize = 24; shrap_bounce = true; ChangeState(parentActor, s_BreakPedistal); break; case BREAK_BOTTLE1: PlaySound(DIGI_BREAKGLASS,parentActor,v3df_dontpan|v3df_doppler); p = GlassShrap; shrap_xsize = shrap_ysize = 8; shrap_bounce = true; ChangeState(parentActor, s_BreakBottle1); break; case BREAK_BOTTLE2: PlaySound(DIGI_BREAKGLASS,parentActor,v3df_dontpan|v3df_doppler); p = GlassShrap; shrap_xsize = shrap_ysize = 8; shrap_bounce = true; ChangeState(parentActor, s_BreakBottle2); break; case BREAK_MUSHROOM: PlaySound(DIGI_BREAKDEBRIS,parentActor,v3df_dontpan|v3df_doppler); p = StoneShrap; shrap_xsize = shrap_ysize = 4; shrap_bounce = true; SetSuicide(parentActor); // kill next iteration break; case BOLT_EXP: return false; // p = BoltExpShrap; // break; case TANK_SHELL_EXP: return false; // p = BoltExpShrap; // break; case TRACER_EXP: return false; // p = TracerExpShrap; // shrap_xsize = shrap_ysize = 20; // WaitTics = 10; // break; case BOLT_THINMAN_R1: p = MetalShrap; if (shrap_amt) { memcpy(CustomShrap, MetalShrap, sizeof(MetalShrap)); CustomShrap->num = 1; p = CustomShrap; } shrap_xsize = shrap_ysize = 10; break; case LAVA_BOULDER: PlaySound(DIGI_BREAKSTONES,parentActor,v3df_dontpan|v3df_doppler); p = LavaBoulderShrap; ShrapOwner = GetOwner(parentActor); shrap_xsize = shrap_ysize = 24; shrap_bounce = true; break; case SECTOR_EXP: //p = SectorExpShrap; //break; return false; case GRENADE_EXP: //p = SectorExpShrap; //break; return false; case FIREBALL_EXP: return false; // p = FireballExpShrap[RANDOM_P2(2<<8)>>8]; // shrap_pal = parentActor->user.spal; break; case ELECTRO_PLAYER: case ELECTRO_ENEMY: ShrapOwner = GetOwner(parentActor); p = ElectroShrap; shrap_xsize = shrap_ysize = 20; break; case COOLIE_RUN_R0: if (means == WPN_NM_SECTOR_SQUISH) break; // return (false); break; case NINJA_DEAD: return false; break; case NINJA_Head_R0: { extern STATE* sg_PlayerHeadHurl[]; if (parentActor->user.Rot == sg_PlayerHeadHurl) { p = PlayerHeadHurl1; } else { p = PlayerDeadHead; shrap_xsize = shrap_ysize = 16+8; shrap_bounce = true; } break; } case GIRLNINJA_RUN_R0: p = StdShrap; break; case NINJA_RUN_R0: { p = StdShrap; if (parentActor->user.PlayerP) { PLAYER* pp = parentActor->user.PlayerP; if (pp->DeathType == PLAYER_DEATH_CRUMBLE) p = PlayerGoreFall; else p = PlayerGoreFly; } break; } case GORO_RUN_R0: p = StdShrap; shrap_xsize = shrap_ysize = 64; break; case COOLG_RUN_R0: p = UpperGore; break; case RIPPER_RUN_R0: p = StdShrap; if (parentActor->user.spal != 0) shrap_xsize = shrap_ysize = 64; else shrap_xsize = shrap_ysize = 32; break; case RIPPER2_RUN_R0: p = StdShrap; if (parentActor->user.spal != 0) shrap_xsize = shrap_ysize = 64; else shrap_xsize = shrap_ysize = 32; break; case SERP_RUN_R0: p = StdShrap; //return (false); break; case SUMO_RUN_R0: p = StdShrap; break; case SKEL_RUN_R0: p = SkelGore; shrap_pal = PALETTE_SKEL_GORE; break; case HORNET_RUN_R0: p = SmallGore; shrap_pal = PALETTE_SKEL_GORE; break; case SKULL_R0: case SKULL_R0 + 1: p = FlamingGore; break; case SKULL_SERP: return false; case BETTY_R0: case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: PlaySound(DIGI_BREAKGLASS,parentActor,v3df_dontpan|v3df_doppler); p = MetalShrap; shrap_xsize = shrap_ysize = 10; break; case ZILLA_RUN_R0: p = MetalShrap; shrap_xsize = shrap_ysize = 10; break; case EMP: p = EMPShrap; shrap_xsize = shrap_ysize = 8; shrap_bounce = false; break; } // second sprite involved // most of the time is is the weapon if (secondaryActor != nullptr) { if (secondaryActor->user.PlayerP && secondaryActor->user.PlayerP->sop_control) { p = StdShrap; } else switch (secondaryActor->user.ID) { case PLASMA_FOUNTAIN: p = HeartAttackShrap; break; } } hZ[Z_TOP] = ActorZOfTop(parentActor); // top hZ[Z_BOT] = ActorZOfBottom(parentActor); // bottom hZ[Z_MID] = (hZ[0] + hZ[2]) * 0.5; // mid for (; p->id; p++) { auto ang_range = mapangle(p->ang_range); if (!p->random_disperse) { //dang = (2048 / p->num); start_angl = shrap_angl - (ang_range * 0.5); dangl = (ang_range / p->num); } for (int i = 0; i < p->num; i++) { auto actor = SpawnActor(STAT_SKIP4, p->id, p->state, parentActor->sector(), DVector3(parentActor->spr.pos.XY(), hZ[p->zlevel]), shrap_angl, 512); if (p->random_disperse) { actor->spr.Angles.Yaw = shrap_angl + mapangle(RANDOM_P2(p->ang_range << 5) >> 5) - (ang_range * 0.5); } else { actor->spr.Angles.Yaw = start_angl + (dangl * i); } // for FastShrap int vz = abs(actor->user.jump_speed * 4) - RandomRange(abs(actor->user.jump_speed) * 8) * 2; actor->user.change.Z = vz * zmaptoworld; actor->user.WaitTics = WaitTics + RandomRange(WaitTics/2); switch (actor->user.ID) { case GORE_Drip: shrap_bounce = false; break; case GORE_Lung: shrap_xsize = 20; shrap_ysize = 20; shrap_bounce = false; break; case GORE_Liver: shrap_xsize = 20; shrap_ysize = 20; shrap_bounce = false; break; case GORE_SkullCap: shrap_xsize = 24; shrap_ysize = 24; shrap_bounce = true; break; case GORE_Arm: shrap_xsize = 21; shrap_ysize = 21; shrap_bounce = false; break; case GORE_Head: shrap_xsize = 26; shrap_ysize = 30; shrap_bounce = true; break; case Vomit1: shrap_bounce = false; actor->spr.pos.Z -= 4; shrap_xsize = (12 + (RANDOM_P2(32<<8)>>8)); shrap_ysize = (12 + (RANDOM_P2(32<<8)>>8)); actor->user.pos.X = shrap_xsize * REPEAT_SCALE; // notreallypos actor->user.pos.Y = shrap_ysize * REPEAT_SCALE; actor->user.Counter = (RANDOM_P2(2048<<5)>>5); move_missile(actor, DVector3(actor->spr.Angles.Yaw.ToVector() * 16, 0), 8, 8, CLIPMASK_MISSILE, MISSILEMOVETICS); if (RANDOM_P2(1024)<700) actor->user.ID = 0; break; case EMP: shrap_bounce = false; actor->spr.pos.Z -= 4; shrap_xsize = 5 + (RANDOM_P2(4<<8)>>8); shrap_ysize = 5 + (RANDOM_P2(4<<8)>>8); actor->user.pos.X = shrap_xsize * REPEAT_SCALE; // notreallypos actor->user.pos.Y = shrap_ysize * REPEAT_SCALE; break; } actor->spr.shade = int8_t(shrap_shade); actor->spr.scale = DVector2(shrap_xsize * REPEAT_SCALE, shrap_xsize* REPEAT_SCALE); actor->clipdist = 1; if (ShrapOwner != nullptr) { SetOwner(ShrapOwner, actor); } if (shrap_rand_zamt) { actor->spr.pos.Z += RandomRange(shrap_rand_zamt) - (shrap_rand_zamt/2); } actor->spr.pal = actor->user.spal = uint8_t(shrap_pal); actor->vel.X = ((p->min_vel*2) + RandomRange(p->max_vel - p->min_vel)) * maptoworld; actor->user.floor_dist = 2; actor->user.ceiling_dist = 2; actor->user.jump_speed = p->min_jspeed; actor->user.jump_speed += RandomRange(p->max_jspeed - p->min_jspeed); actor->user.jump_speed = -actor->user.jump_speed; DoBeginJump(actor); actor->user.jump_grav = jump_grav; UpdateChangeXY(actor); if (!shrap_bounce) actor->user.Flags |= (SPR_BOUNCE); } } return retval; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DoShrapMove(DSWActor* actor) { actor->user.coll = move_missile(actor, DVector3(actor->user.change.XY(), 0), actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoVomit(DSWActor* actor) { actor->user.Counter = NORM_ANGLE(actor->user.Counter + (30*MISSILEMOVETICS)); // notreallypos auto v = actor->user.pos + mapangle(actor->user.Counter).ToVector() * 12 * REPEAT_SCALE; actor->spr.scale = v; if (actor->user.Flags & (SPR_JUMPING)) { DoJump(actor); DoJump(actor); DoShrapMove(actor); } else if (actor->user.Flags & (SPR_FALLING)) { DoFall(actor); DoFall(actor); DoShrapMove(actor); } else { ChangeState(actor, s_VomitSplash); DoFindGroundPoint(actor); MissileWaterAdjust(actor); actor->spr.pos.Z = actor->user.loz; actor->user.WaitTics = 60; // notreallypos actor->user.pos.XY() = actor->spr.scale; return 0; } if (actor->user.coll.type == kHitSprite) { if ((actor->user.coll.actor()->spr.extra & SPRX_PLAYER_OR_ENEMY)) { DoDamage(actor->user.coll.actor(), actor); } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoVomitSplash(DSWActor* actor) { if ((actor->user.WaitTics-=MISSILEMOVETICS) < 0) { KillActor(actor); return 0; } return 0; } int DoFastShrapJumpFall(DSWActor* actor) { actor->spr.pos += actor->user.change * 2; actor->user.WaitTics -= MISSILEMOVETICS; if (actor->user.WaitTics <= 0) KillActor(actor); return 0; } int DoTracerShrap(DSWActor* actor) { actor->spr.pos += actor->user.change; actor->user.WaitTics -= MISSILEMOVETICS; if (actor->user.WaitTics <= 0) KillActor(actor); return 0; } int DoShrapJumpFall(DSWActor* actor) { if (actor->user.Flags & (SPR_JUMPING)) { DoShrapVelocity(actor); } else if (actor->user.Flags & (SPR_FALLING)) { DoShrapVelocity(actor); } else { if (!(actor->user.Flags & SPR_BOUNCE)) { DoShrapVelocity(actor); return 0; } if (actor->user.ID == GORE_Drip) ChangeState(actor, s_GoreFloorSplash); else ShrapKillSprite(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoShrapDamage(DSWActor* actor) { if (actor->user.Flags & (SPR_JUMPING)) { DoJump(actor); DoShrapMove(actor); } else if (actor->user.Flags & (SPR_FALLING)) { DoFall(actor); DoShrapMove(actor); } else { if (!(actor->user.Flags & SPR_BOUNCE)) { actor->user.Flags |= (SPR_BOUNCE); actor->user.jump_speed = -300; actor->vel.X *= 0.25; DoBeginJump(actor); return 0; } KillActor(actor); return 0; } if (actor->user.coll.type == kHitSprite) { WeaponMoveHit(actor); KillActor(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnBlood(DSWActor* actor, DSWActor* weapActor, DAngle hit_angle, const DVector3* hit_pos) { DVector3 hitpos; int i; if (hit_pos) hitpos = *hit_pos; else hitpos = {}; // state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel, // random_disperse, ang_range; static SHRAP UziBlood[] = { {s_GoreDrip, GORE_Drip, 1, Z_MID, 100, 250, 10, 20, true, 512}, // 70,200 vels //{s_GoreSplash, PLASMA_Drip, 1, Z_BOT, 0, 0, 0, 0, false, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP SmallBlood[] = { {s_GoreDrip, GORE_Drip, 1, Z_TOP, 100, 250, 10, 20, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP PlasmaFountainBlood[] = { {s_PlasmaDrip, PLASMA_Drip, 1, Z_TOP, 200, 500, 100, 300, true, 16}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP SomeBlood[] = { {s_GoreDrip, GORE_Drip, 1, Z_TOP, 100, 250, 10, 20, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP MoreBlood[] = { {s_GoreDrip, GORE_Drip, 2, Z_TOP, 100, 250, 10, 20, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #endif static SHRAP ExtraBlood[] = { {s_GoreDrip, GORE_Drip, 4, Z_TOP, 100, 250, 10, 20, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; static SHRAP HariKariBlood[] = { {s_FastGoreDrip, GORE_Drip, 32, Z_TOP, 200, 650, 70, 100, true, 1024}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #if 0 static SHRAP SwordPowerup[] = { {s_ElectroShrap, ELECTRO_SHARD, 16, Z_TOP, 75, 200, 70, 150, true, 512}, {nullptr,0,0,0,0,0,0,0,0,0}, }; #endif DAngle dangl = nullAngle; SHRAP* p = UziBlood; short shrap_shade = -15; short shrap_xsize = 20, shrap_ysize = 20; short retval = true; short shrap_pal = PALETTE_DEFAULT; DAngle start_angle = nullAngle; switch (actor->user.ID) { case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: case ZILLA_RUN_R0: return 0; // Don't put blood on trashcan } // second sprite involved // most of the time is the weapon if (weapActor != nullptr) { switch (weapActor->user.ID) { case NINJA_RUN_R0: //sword // if sprite and weapon are the same it must be HARIII-KARIII if (actor == weapActor) { p = HariKariBlood; hit_angle = actor->spr.Angles.Yaw; hitpos = ActorVectOfTop(actor).plusZ(ActorSizeZ(actor) * (1./16.)); } else { p = ExtraBlood; hit_angle = weapActor->spr.Angles.Yaw + DAngle180; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = ActorZOfTop(weapActor) + (ActorSizeZ(weapActor) * 0.25); } break; case SERP_RUN_R0: p = ExtraBlood; hit_angle = weapActor->spr.Angles.Yaw + DAngle180; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25); break; case BLADE1: case BLADE2: case BLADE3: case 5011: p = SmallBlood; hit_angle = AngToSprite(actor, weapActor) + DAngle180; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = weapActor->spr.pos.Z + (ActorSizeZ(weapActor) * 0.5); break; case STAR1: case CROSSBOLT: p = SomeBlood; hit_angle = weapActor->spr.Angles.Yaw + DAngle180; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = weapActor->spr.pos.Z; break; case PLASMA_FOUNTAIN: p = PlasmaFountainBlood; hit_angle = weapActor->spr.Angles.Yaw; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25); break; default: p = SomeBlood; hit_angle = weapActor->spr.Angles.Yaw + DAngle180; hitpos.XY() = actor->spr.pos.XY(); hitpos.Z = ActorZOfTop(weapActor) + (ActorSizeZ(weapActor) * 0.25); break; } } else { hit_angle += DAngle180; } for (; p->state; p++) { auto ang_range = mapangle(p->ang_range); if (!p->random_disperse) { start_angle = hit_angle - (ang_range * 0.5) + DAngle180; dangl = (ang_range / p->num); } for (i = 0; i < p->num; i++) { auto actorNew = SpawnActor(STAT_SKIP4, p->id, p->state, actor->sector(), hitpos, hit_angle, 0); switch (actorNew->user.ID) { case ELECTRO_SHARD: shrap_xsize = 7; shrap_ysize = 7; break; case PLASMA_Drip: // Don't do central blood splats for every hitscan if (RANDOM_P2(1024) < 950) { actorNew->spr.scale = DVector2(0, 0); } if (RANDOM_P2(1024) < 512) actorNew->spr.cstat |= (CSTAT_SPRITE_XFLIP); //shrap_xsize = 96; //shrap_ysize = 75; //shrap_xsize = 10; //shrap_ysize = 10; break; } if (p->random_disperse) { actorNew->spr.Angles.Yaw = hit_angle + mapangle((RANDOM_P2(p->ang_range<<5)>>5) - (p->ang_range >> 1)); } else { actorNew->spr.Angles.Yaw = start_angle + (dangl * i); } actorNew->user.Flags |= (SPR_BOUNCE); actorNew->spr.shade = int8_t(shrap_shade); actorNew->spr.scale = DVector2(shrap_xsize* REPEAT_SCALE, shrap_xsize* REPEAT_SCALE); actorNew->clipdist = 1; actorNew->spr.pal = actorNew->user.spal = uint8_t(shrap_pal); actorNew->vel.X = (p->min_vel + RandomRange(p->max_vel - p->min_vel)) * maptoworld; // special case // blood coming off of actors should have the acceleration of the actor // so add it in actorNew->vel.X += actor->vel.X; actorNew->user.ceiling_dist = actorNew->user.floor_dist = 2; actorNew->user.jump_speed = p->min_jspeed; actorNew->user.jump_speed += RandomRange(p->max_jspeed - p->min_jspeed); actorNew->user.jump_speed = -actorNew->user.jump_speed; UpdateChangeXY(actorNew); // for FastShrap actorNew->user.change.Z = (abs(actorNew->user.jump_speed*4) - RandomRange(abs(actorNew->user.jump_speed)*8)) * JUMP_FACTOR; actorNew->user.WaitTics = 64 + RANDOM_P2(32); actor->user.Flags |= (SPR_BOUNCE); DoBeginJump(actorNew); } } return retval; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool VehicleMoveHit(DSWActor* actor) { SECTOR_OBJECT* sop; SECTOR_OBJECT* hsop; bool TestKillSectorObject(SECTOR_OBJECT*); if (actor->user.coll.type == kHitNone) return false; sop = actor->user.sop_parent; // sprite controlling sop DSWActor* ctrlr = sop->controller; if (!ctrlr) return false; switch (actor->user.coll.type) { case kHitSector: { sectortype* sectp = actor->user.coll.hitSector; if ((sectp->extra & SECTFX_SECTOR_OBJECT)) { // shouldn't ever really happen } return true; } case kHitSprite: { auto hitActor = actor->user.coll.actor(); if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hitActor, actor->user.ID); return true; } if ((hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) { if (hitActor != GetOwner(ctrlr)) { DoDamage(hitActor, ctrlr); return true; } } else { if (hitActor->spr.statnum == STAT_MINE_STUCK) { DoDamage(hitActor, actor); return true; } } return true; } case kHitWall: { auto wph = actor->user.coll.hitWall; if ((wph->extra & WALLFX_SECTOR_OBJECT)) { // sector object collision if ((hsop = DetectSectorObjectByWall(wph))) { SopDamage(hsop, sop->ram_damage); if (hsop->max_damage <= 0) SopCheckKill(hsop); else DoSpawnSpotsForDamage(hsop->match_event); } } return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool WeaponMoveHit(DSWActor* actor) { switch (actor->user.coll.type) { default: break; case kHitVoid: SetSuicide(actor); return true; case kHitSector: { sectortype* sectp; SECTOR_OBJECT* sop; sectp = actor->user.coll.hitSector; ASSERT(sectp->extra != -1); // hit floor - closer to floor than ceiling if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { // hit a floor sprite if (actor->user.lowActor) { if (actor->user.lowActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(actor, -1, actor->user.lowActor)) return true; } return true; } if (sectp->hasU() && FixedToInt(sectp->depth_fixed) > 0) { SpawnSplash(actor); return true; } } // hit ceiling else { // hit a floor sprite if (actor->user.highActor) { if (actor->user.highActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(actor, -1, actor->user.highActor)) return true; } } } if ((sectp->extra & SECTFX_SECTOR_OBJECT)) { if ((sop = DetectSectorObject(sectp))) { DoDamage(sop->sp_child, actor); return true; } } if ((sectp->ceilingstat & CSTAT_SECTOR_SKY) && sectp->ceilingpicnum != FAF_MIRROR_PIC) { if (abs(actor->spr.pos.Z - sectp->ceilingz) < ActorSizeZ(actor)) { SetSuicide(actor); return true; } } return true; } case kHitSprite: { auto hitActor = actor->user.coll.actor(); ASSERT(hitActor->spr.extra != -1); if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hitActor, actor->user.ID); return true; } if ((hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) { // make sure you didn't hit the Owner of the missile if (hitActor != GetOwner(actor)) { if (actor->user.ID == STAR1) { extern STATE s_TrashCanPain[]; switch (hitActor->user.ID) { case TRASHCAN: PlaySound(DIGI_TRASHLID, actor, v3df_none); PlaySound(DIGI_STARCLINK, actor, v3df_none); if (hitActor->user.WaitTics <= 0) { hitActor->user.WaitTics = SEC(2); ChangeState(hitActor,s_TrashCanPain); } break; case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case ZILLA_RUN_R0: case 623: { PlaySound(DIGI_STARCLINK, actor, v3df_none); } break; } } DoDamage(hitActor, actor); return true; } } else { if (hitActor->spr.statnum == STAT_MINE_STUCK) { DoDamage(hitActor, actor); return true; } } if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(actor, -1, hitActor)) return true; } if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { if (hitActor->spr.lotag || hitActor->spr.hitag) { ShootableSwitch(hitActor); return true; } } return true; } case kHitWall: { auto wph = actor->user.coll.hitWall; SECTOR_OBJECT* sop; ASSERT(wph->extra != -1); if ((wph->extra & WALLFX_SECTOR_OBJECT)) { if ((sop = DetectSectorObjectByWall(wph))) { if (sop->max_damage != -999) DoDamage(sop->sp_child, actor); return true; } } if (wph->lotag == TAG_WALL_BREAK) { HitBreakWall(wph, actor->spr.pos, actor->spr.Angles.Yaw, actor->user.ID); actor->user.coll.setNone(); return true; } // clipmove does not correctly return the sprite for WALL sprites // on walls, so look with hitscan HitInfo hit{}; hitscan(actor->spr.pos, actor->sector(), DVector3(actor->spr.Angles.Yaw.ToVector() * 1024, actor->vel.Z), hit, CLIPMASK_MISSILE); if (!hit.hitSector) { return false; } if (hit.actor()) { auto hitActor = hit.actor(); if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(actor, -1, hitActor)) return true; } if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { if (hitActor->spr.lotag || hitActor->spr.hitag) { ShootableSwitch(hitActor); return true; } } } return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoUziSmoke(DSWActor* actor) { actor->spr.pos.Z -= 0.78125; // !JIM! Make them float up return 0; } int DoShotgunSmoke(DSWActor* actor) { actor->spr.pos.Z -= 0.78125; // !JIM! Make them float up return 0; } int DoMineSpark(DSWActor* actor) { if (actor->spr.picnum != 0) { DoDamageTest(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFireballFlames(DSWActor* actor) { bool jumping = false; // if no Owner then stay where you are DSWActor* attach = actor->user.attachActor; if (attach != nullptr) { actor->spr.pos = ActorVectOfMiddle(attach); if ((attach->spr.extra & SPRX_BURNABLE)) { if ((actor->user.Counter2 & 1) == 0) { attach->spr.shade++; if (attach->spr.shade > 10) attach->spr.shade = 10; } } } else { if (actor->user.Flags & (SPR_JUMPING)) { DoJump(actor); jumping = true; } else if (actor->user.Flags & (SPR_FALLING)) { DoFall(actor); jumping = true; } else { if (actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed) > 0) { if (abs(actor->sector()->floorz - actor->spr.pos.Z) <= 4) { KillActor(actor); return 0; } } if (TestDontStickSector(actor->sector())) { KillActor(actor); return 0; } } } if (!jumping) { if ((actor->user.WaitTics += MISSILEMOVETICS) > 4 * 120) { // shrink and go away actor->spr.scale.X -= REPEAT_SCALE; actor->spr.scale.Y -= REPEAT_SCALE; if (actor->spr.scale.X <= 0) { if (actor->user.attachActor != nullptr) { actor->user.attachActor->user.flameActor = nullptr; actor->user.attachActor->user.Flags2 &= ~SPR2_FLAMEDIE; } KillActor(actor); return 0; } } else { // grow until the right size if (actor->spr.scale.X <= actor->user.Counter * REPEAT_SCALE) { actor->spr.scale.X += (3 * REPEAT_SCALE); actor->spr.scale.Y += (3 * REPEAT_SCALE); } } } actor->user.Counter2++; if (actor->user.Counter2 > 9) { actor->user.Counter2 = 0; DoFlamesDamageTest(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBreakFlames(DSWActor* actor) { bool jumping = false; if (actor->user.Flags & (SPR_JUMPING)) { DoJump(actor); jumping = true; } else if (actor->user.Flags & (SPR_FALLING)) { DoFall(actor); jumping = true; } else { if (actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed) > 0) { if (abs(actor->sector()->floorz - actor->spr.pos.Z) <= 4) { KillActor(actor); return 0; } } if (TestDontStickSector(actor->sector())) { KillActor(actor); return 0; } } if (!jumping) { if ((actor->user.WaitTics += MISSILEMOVETICS) > 4 * 120) { // shrink and go away actor->spr.scale.X -= REPEAT_SCALE; actor->spr.scale.Y -= REPEAT_SCALE; if (actor->spr.scale.X <= 0) { if (actor->user.attachActor != nullptr) { actor->user.attachActor->user.flameActor = nullptr; actor->user.attachActor->user.Flags2 &= ~SPR2_FLAMEDIE; } KillActor(actor); return 0; } } else { // grow until the right size if (actor->spr.scale.X <= actor->user.Counter * REPEAT_SCALE) { actor->spr.scale.X += (3 * REPEAT_SCALE); actor->spr.scale.Y += (3 * REPEAT_SCALE); } if (actor->user.WaitTics + MISSILEMOVETICS > 4 * 120) { SpawnBreakStaticFlames(actor); } } } actor->user.Counter2++; if (actor->user.Counter2 > 9) { actor->user.Counter2 = 0; DoFlamesDamageTest(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SetSuicide(DSWActor* actor) { if (actor->hasU()) { actor->user.Flags |= (SPR_SUICIDE); actor->user.RotNum = 0; } ChangeState(actor, s_Suicide); return 0; } int DoActorScale(DSWActor* actor) { actor->user.scale_speed = 70; actor->user.scale_value = int(actor->spr.scale.X * INV_REPEAT_SCALE) << 8; actor->user.scale_tgt = int(actor->spr.scale.X * INV_REPEAT_SCALE) + 25; if (actor->user.scale_tgt > 256) { actor->user.scale_speed = 0; actor->user.scale_tgt = 256; } return 0; } int DoRipperGrow(DSWActor* actor) { actor->user.scale_speed = 70; actor->user.scale_value = int(actor->spr.scale.X * INV_REPEAT_SCALE) << 8; actor->user.scale_tgt = int(actor->spr.scale.X * INV_REPEAT_SCALE) + 20; if (actor->user.scale_tgt > 128) { actor->user.scale_speed = 0; actor->user.scale_tgt = 128; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void UpdateSinglePlayKills(DSWActor* actor) { // single play and coop kill count if (gNet.MultiGameType != MULTI_GAME_COMMBAT && actor->hasU()) { if (actor->user.Flags & (SPR_SUICIDE)) { return; } switch (actor->user.ID) { case COOLIE_RUN_R0: case NINJA_RUN_R0: case NINJA_CRAWL_R0: case GORO_RUN_R0: case 1441: case COOLG_RUN_R0: case EEL_RUN_R0: case SUMO_RUN_R0: case ZILLA_RUN_R0: //case TOILETGIRL_R0: //case WASHGIRL_R0: //case BUNNY_RUN_R0: case RIPPER_RUN_R0: case RIPPER2_RUN_R0: case SERP_RUN_R0: case LAVA_RUN_R0: case SKEL_RUN_R0: case HORNET_RUN_R0: case GIRLNINJA_RUN_R0: case SKULL_R0: case BETTY_R0: // always give kills to the first player Player->Kills++; break; } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorChooseDeath(DSWActor* actor, DSWActor* weapActor) { if (!actor->hasU()) return false; if (actor->user.Health > 0) return false; UpdateSinglePlayKills(actor); if (actor->user.Attrib) PlaySpriteSound(actor,attr_die,v3df_follow); switch (actor->user.ID) { case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: case TOILETGIRL_R0: case WASHGIRL_R0: case CARGIRL_R0: case MECHANICGIRL_R0: case SAILORGIRL_R0: case PRUNEGIRL_R0: case TRASHCAN: case GORO_RUN_R0: case RIPPER2_RUN_R0: break; case BUNNY_RUN_R0: { Bunny_Count--; // Bunny died, decrease the population } break; default: ActorCoughItem(actor); break; } switch (actor->user.ID) { case BETTY_R0: { DoBettyBeginDeath(actor); break; } case SKULL_R0: { DoSkullBeginDeath(actor); break; } case TOILETGIRL_R0: case WASHGIRL_R0: case CARGIRL_R0: case MECHANICGIRL_R0: case SAILORGIRL_R0: case PRUNEGIRL_R0: case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: { if ((actor->user.ID == TOILETGIRL_R0 || actor->user.ID == CARGIRL_R0 || actor->user.ID == MECHANICGIRL_R0 || actor->user.ID == SAILORGIRL_R0 || actor->user.ID == PRUNEGIRL_R0 || actor->user.ID == WASHGIRL_R0) && weapActor->hasU() && weapActor->user.ID == NINJA_RUN_R0 && weapActor->user.PlayerP) { PLAYER* pp = weapActor->user.PlayerP; if (pp && !(pp->Flags & PF_DIVING)) // JBF: added null test pp->Bloody = true; PlaySound(DIGI_TOILETGIRLSCREAM, actor, v3df_none); } if (SpawnShrap(actor, weapActor)) SetSuicide(actor); break; } // These are player zombies case ZOMBIE_RUN_R0: InitBloodSpray(actor,true,105); InitBloodSpray(actor,true,105); if (SpawnShrap(actor, weapActor)) SetSuicide(actor); break; default: switch (weapActor->user.ID) { case NINJA_RUN_R0: //sword if (weapActor->user.PlayerP) { PLAYER* pp = weapActor->user.PlayerP; if (weapActor->user.WeaponNum == WPN_FIST && StdRandomRange(1000)>500 && pp == Player+myconnectindex) { int choosesnd = StdRandomRange(6); if (choosesnd == 0) PlayerSound(DIGI_KUNGFU, v3df_follow|v3df_dontpan,pp); else if (choosesnd == 1) PlayerSound(DIGI_PAYINGATTENTION, v3df_follow|v3df_dontpan,pp); else if (choosesnd == 2) PlayerSound(DIGI_EATTHIS, v3df_follow|v3df_dontpan,pp); else if (choosesnd == 3) PlayerSound(DIGI_TAUNTAI4, v3df_follow|v3df_dontpan,pp); else if (choosesnd == 4) PlayerSound(DIGI_TAUNTAI5, v3df_follow|v3df_dontpan,pp); else if (choosesnd == 5) PlayerSound(DIGI_HOWYOULIKEMOVE, v3df_follow|v3df_dontpan,pp); //PlayerSound(TauntAIVocs[choosesnd],&pp->posx, // &pp->posy,&pp->posy,v3df_dontpan|v3df_follow,pp); } else if (weapActor->user.WeaponNum == WPN_SWORD && StdRandomRange(1000)>500 && pp == Player+myconnectindex) { short choose_snd; choose_snd = StdRandomRange(1000); if (choose_snd > 750) PlayerSound(DIGI_SWORDGOTU1, v3df_follow|v3df_dontpan,pp); else if (choose_snd > 575) PlayerSound(DIGI_SWORDGOTU2, v3df_follow|v3df_dontpan,pp); else if (choose_snd > 250) PlayerSound(DIGI_SWORDGOTU3, v3df_follow|v3df_dontpan,pp); else PlayerSound(DIGI_CANBEONLYONE, v3df_follow|v3df_dontpan,pp); } if (!(pp->Flags & PF_DIVING)) pp->Bloody = true; } if (actor->user.WeaponNum == WPN_FIST) DoActorDie(actor, weapActor, 0); else if (actor->user.ID == NINJA_RUN_R0 || RandomRange(1000) < 500) DoActorDie(actor, weapActor, 0); else { // Can't gib bosses! if (actor->user.ID == SERP_RUN_R0 || actor->user.ID == SUMO_RUN_R0 || actor->user.ID == ZILLA_RUN_R0) { DoActorDie(actor, weapActor, 0); break; } // Gib out the ones you can't cut in half // Blood fountains InitBloodSpray(actor,true,-1); if (SpawnShrap(actor, weapActor)) { SetSuicide(actor); } else DoActorDie(actor, weapActor, 0); } break; case BOLT_THINMAN_R0: case BOLT_THINMAN_R1: case BOLT_THINMAN_R2: case NAP_EXP: case BOLT_EXP: case TANK_SHELL_EXP: case SECTOR_EXP: case FIREBALL_EXP: case GRENADE_EXP: case MINE_EXP: case SKULL_R0: case BETTY_R0: #if 0 if (RANDOM_P2(1024) < 256 && weapActor->user.Radius != NUKE_RADIUS) { if (weapActor->user.ID == BOLT_THINMAN_R1 && weapActor->user.Radius == RAIL_RADIUS) { SpawnShrapX(weapActor); // Do rail gun shrap } DoActorDie(actor, weapActor, 0); } else #endif { int choosesnd = 0; // For the Nuke, do residual radiation if he gibs if (weapActor->user.Radius == NUKE_RADIUS) SpawnFireballFlames(actor, nullptr); // Random chance of taunting the AI's here if (RandomRange(1000) > 400) { PLAYER* pp; auto own = GetOwner(weapActor); if (own && own->hasU()) { pp = own->user.PlayerP; if (pp) { choosesnd=StdRandomRange(MAX_TAUNTAI<<8)>>8; if (pp == Player+myconnectindex) PlayerSound(TauntAIVocs[choosesnd],v3df_dontpan|v3df_follow,pp); } } } // These guys cough items only if gibbed if (actor->user.ID == GORO_RUN_R0 || actor->user.ID == RIPPER2_RUN_R0) ActorCoughItem(actor); // Blood fountains InitBloodSpray(actor,true,-1); // Bosses do not gib if (actor->user.ID == SERP_RUN_R0 || actor->user.ID == SUMO_RUN_R0 || actor->user.ID == ZILLA_RUN_R0) { DoActorDie(actor, weapActor, 0); return true; } if (SpawnShrap(actor, weapActor)) { SetSuicide(actor); } else DoActorDie(actor, weapActor, 0); } break; default: DoActorDie(actor, weapActor, 0); break; } break; } return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorHealth(DSWActor* actor, short amt) { extern int FinishAnim; if (actor->user.ID == TRASHCAN && amt > -75) { actor->user.LastDamage = 100; return true; } actor->user.Flags |= (SPR_ATTACKED); actor->user.Health += amt; if (actor->user.ID == SERP_RUN_R0 && actor->spr.pal != 16 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SERPENT)) { if (actor->user.Health < actor->user.MaxHealth/2) { FinishAnim = ANIM_SERP; // Hack for Last Warrior which apparently needs to continue with the next level here. if (!(currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SERPENT_NEXT)) { ChangeLevel(nullptr, g_nextskill, true); return true; } else FinishTimer = 1; } } if (actor->user.ID == SUMO_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SUMO)) { if (actor->user.Health <= 0) { FinishTimer = 7*120; FinishAnim = ANIM_SUMO; } } if (actor->user.ID == ZILLA_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_ZILLA)) { if (actor->user.Health <= 0) //if (actor->user.Health < actor->user.MaxHealth) { FinishTimer = 15*120; FinishAnim = ANIM_ZILLA; } } if (actor->user.Attrib && RANDOM_P2(1024) > 850) PlaySpriteSound(actor,attr_pain,v3df_follow|v3df_dontpan); // keep track of the last damage if (amt < 0) actor->user.LastDamage = -amt; // Do alternate Death2 if it exists if (actor->user.ActorActionSet && actor->user.ActorActionSet->Death2) // JBF: added null check { #define DEATH2_HEALTH_VALUE 15 if (actor->user.Health <= DEATH2_HEALTH_VALUE) { // If he's dead, possibly choose a special death type #if 1 // Problematic code, REMOVED. switch (actor->user.ID) { case NINJA_RUN_R0: { extern STATE* sg_NinjaGrabThroat[]; extern STATE* sg_NinjaHariKari[]; if (actor->user.Flags2 & (SPR2_DYING)) return true; if (actor->user.Flags & (SPR_FALLING | SPR_JUMPING | SPR_CLIMBING)) return true; if (!(actor->user.Flags2 & SPR2_DYING)) { short rnd; rnd = RANDOM_P2(1024<<4)>>4; if (rnd < 950) return true; actor->user.Flags2 |= (SPR2_DYING); // Only let it check this once! actor->user.WaitTics = SEC(1) + SEC(RandomRange(2)); actor->user.Health = 60; PlaySound(DIGI_NINJACHOKE, actor, v3df_follow); InitPlasmaFountain(nullptr, actor); InitBloodSpray(actor,false,105); actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle() + DAngle90; actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP); if (sw_ninjahack) NewStateGroup(actor, sg_NinjaHariKari); else NewStateGroup(actor, sg_NinjaGrabThroat); } break; } } #endif } } return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SopDamage(SECTOR_OBJECT* sop, short amt) { auto actor = sop->sp_child; // does not have damage if (sop->max_damage == -9999) return false; sop->max_damage += amt; // keep track of the last damage if (amt < 0) actor->user.LastDamage = -amt; return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SopCheckKill(SECTOR_OBJECT* sop) { bool killed = false; if ((sop->flags & SOBJ_BROKEN)) return false; // does not have damage if (sop->max_damage == -9999) return killed; if (sop->max_damage <= 0) { // returns whether so was collapsed(killed) killed = TestKillSectorObject(sop); if (!killed) { VehicleSetSmoke(sop, SpawnVehicleSmoke); sop->flags |= (SOBJ_BROKEN); } } return killed; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorPain(DSWActor* actor) { // uzi/shotgun damages switch (actor->user.ID) { case TOILETGIRL_R0: case WASHGIRL_R0: case CARGIRL_R0: case MECHANICGIRL_R0: case SAILORGIRL_R0: case PRUNEGIRL_R0: actor->user.FlagOwner = 1; break; } if (RandomRange(1000) < 875 || actor->user.WaitTics > 0) return false; if (!(actor->user.Flags & (SPR_JUMPING | SPR_FALLING))) { if (actor->user.ActorActionSet && actor->user.ActorActionSet->Pain) { ActorLeaveTrack(actor); actor->user.WaitTics = 60; NewStateGroup(actor, actor->user.ActorActionSet->Pain); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorPainPlasma(DSWActor* actor) { if (!(actor->user.Flags & (SPR_JUMPING | SPR_FALLING | SPR_ELECTRO_TOLERANT))) { if (actor->user.ActorActionSet && actor->user.ActorActionSet->Pain) { actor->user.WaitTics = PLASMA_FOUNTAIN_TIME; NewStateGroup(actor, actor->user.ActorActionSet->Pain); return true; } else { actor->user.Vis = PLASMA_FOUNTAIN_TIME; InitActorPause(actor); } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorStdMissile(DSWActor* actor, DSWActor* weapActor) { assert(weapActor != nullptr); // Attack the player that is attacking you // Only if hes still alive auto own = GetOwner(weapActor); if (own && own->hasU()) { if (own->user.PlayerP && GetOwner(actor) != own) { actor->user.targetActor = own; } } // Reset the weapons target before dying DSWActor* goal = weapActor->user.WpnGoalActor; if (goal != nullptr) { // attempt to see if it was killed ASSERT(goal->insector()); if (goal->hasU()) goal->user.Flags &= ~(SPR_TARGETED); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ActorDamageSlide(DSWActor* actor, int damage, DAngle angle) { if (actor->user.Flags & (SPR_CLIMBING)) return false; damage = abs(damage); if (!damage) return false; if (damage <= 10) { DoActorBeginSlide(actor, angle, 4); return true; } else if (damage <= 20) { DoActorBeginSlide(actor, angle, 8); return true; } else { int slide_vel = (damage * 6) - (actor->user.MaxHealth); if (slide_vel < -1000) slide_vel = -1000; DoActorBeginSlide(actor, angle, slide_vel / 16.); return true; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int PlayerDamageSlide(PLAYER* pp, int damage, DAngle angle) { damage = abs(damage); if (!damage) return false; if (damage <= 5) { //nudge //pp->slide_xvect = angle.ToVector() * 0.5; //return(true); return false; } else if (damage <= 10) { //nudge pp->slide_vect = angle.ToVector() * 2; return true; } else if (damage <= 20) { //bigger nudge pp->slide_vect = angle.ToVector() * 8; return true; } else { pp->slide_vect = angle.ToVector() * damage * 0.75; return true; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int GetDamage(DSWActor* actor, DSWActor* weapActor, int DamageNdx) { auto d = &DamageData[DamageNdx]; // if ndx does radius if (d->radius > 0 && weapActor) { int damage_per_pixel, damage_force, damage_amt; int dist = int((weapActor->spr.pos.XY() - actor->spr.pos.XY()).Length() * worldtoint); // take off the box around the player or else you'll never get // the max_damage; dist -= int(actor->clipdist * worldtoint); if (dist < 0) dist = 0; if ((unsigned)dist < d->radius) { damage_per_pixel = IntToFixed(d->damage_hi)/d->radius; //the closer your distance is to 0 the more damage damage_force = (d->radius - dist); damage_amt = -FixedToInt(damage_force * damage_per_pixel); //return(damage_amt); // formula: damage_amt = 75% + random(25%) return (damage_amt >> 1) + (damage_amt >> 2) + RandomRange(damage_amt >> 2); } else { return 0; } } return -(d->damage_lo + RandomRange(d->damage_hi - d->damage_lo)); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int PlayerCheckDeath(PLAYER* pp, DSWActor* weapActor) { DSWActor* actor = pp->actor; // Store off what player was struck by pp->HitBy = weapActor; if (actor->user.Health <= 0 && !(pp->Flags & PF_DEAD)) { // pick a death type if (actor->user.LastDamage >= PLAYER_DEATH_EXPLODE_DAMMAGE_AMT) pp->DeathType = PLAYER_DEATH_EXPLODE; else if (actor->user.LastDamage >= PLAYER_DEATH_CRUMBLE_DAMMAGE_AMT) pp->DeathType = PLAYER_DEATH_CRUMBLE; else pp->DeathType = PLAYER_DEATH_FLIP; if (weapActor == nullptr) { pp->KillerActor = nullptr; DoPlayerBeginDie(pp); return true; } if (weapActor != nullptr && (weapActor->user.ID == RIPPER_RUN_R0 || weapActor->user.ID == RIPPER2_RUN_R0)) pp->DeathType = PLAYER_DEATH_RIPPER; if (weapActor != nullptr && weapActor->user.ID == CALTROPS) pp->DeathType = PLAYER_DEATH_FLIP; if (weapActor != nullptr && weapActor->user.ID == NINJA_RUN_R0 && weapActor->user.PlayerP) { pp->DeathType = PLAYER_DEATH_FLIP; weapActor->user.PlayerP->Bloody = true; } // keep track of who killed you for death purposes // need to check all Killer variables when an enemy dies if (pp->KillerActor == nullptr) { auto own = GetOwner(weapActor); if (own) pp->KillerActor = own; else if ((weapActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) pp->KillerActor = weapActor; } // start the death process DoPlayerBeginDie(pp); // for death direction actor->user.slide_ang = (actor->spr.pos - weapActor->spr.pos).Angle(); // for death velocity actor->user.slide_vel = actor->user.LastDamage * (5 / 16.); return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool PlayerTakeDamage(PLAYER* pp, DSWActor* weapActor) { if (weapActor == nullptr) return true; DSWActor* actor = pp->actor; auto weapOwner = GetOwner(weapActor); if (gNet.MultiGameType == MULTI_GAME_NONE) { // ZOMBIE special case for single play if (weapActor->user.ID == ZOMBIE_RUN_R0) { // if weapons Owner the player if (weapOwner == pp->actor) return false; } return true; } if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE) { // everything hurts you if (gNet.HurtTeammate) return true; // if weapon IS the YOURSELF take damage if (weapActor->user.PlayerP == pp) return true; // if the weapons Owner is YOURSELF take damage if (weapOwner && weapOwner->hasU() && weapOwner->user.PlayerP == pp) return true; // if weapon IS the player no damage if (weapActor->user.PlayerP) return false; // if the weapons Owner is a player if (weapOwner && weapOwner->hasU() && weapOwner->user.PlayerP) return false; } else if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.TeamPlay) { // everything hurts you if (gNet.HurtTeammate) return true; // if weapon IS the YOURSELF take damage if (weapActor->user.PlayerP == pp) return true; // if the weapons Owner is YOURSELF take damage if (weapOwner && weapOwner->hasU() && weapOwner->user.PlayerP == pp) return true; if (weapActor->user.PlayerP) { // if both on the same team then no damage if (weapActor->user.spal == actor->user.spal) return false; } // if the weapons Owner is a player if (weapOwner && weapOwner->hasU() && weapOwner->user.PlayerP) { // if both on the same team then no damage if (weapOwner->user.spal == actor->user.spal) return false; } } return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int StarBlood(DSWActor* actor, DSWActor* weapActor) { short blood_num = 1; short i; if (actor->user.Health <= 0) blood_num = 4; for (i = 0; i < blood_num; i++) SpawnBlood(actor, weapActor); return 0; } /* !AIC KEY - This is where damage is assesed when missiles hit actors and other objects. */ //--------------------------------------------------------------------------- // // this was done wrong multiple times below, resulting in spurious crashes. // //--------------------------------------------------------------------------- bool OwnerIs(DSWActor* actor, int pic) { auto Own = GetOwner(actor); if (Own == nullptr || !Own->hasU()) return false; return Own->user.ID == pic; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoDamage(DSWActor* actor, DSWActor* weapActor) { int damage=0; ASSERT(actor->hasU()); // don't hit a dead player if (actor->user.PlayerP && (actor->user.PlayerP->Flags & PF_DEAD)) { SpawnBlood(actor, weapActor); return 0; } if (!weapActor || !weapActor->hasU() || (actor->user.Flags & SPR_SUICIDE)) return 0; if ((weapActor->user.Flags & SPR_SUICIDE)) return 0; if (actor->user.Attrib && RANDOM_P2(1024) > 850) PlaySpriteSound(actor,attr_pain,v3df_follow); if (actor->user.Flags & (SPR_DEAD)) { SpawnBlood(actor, weapActor); return 0; } // special case for shooting mines if (actor->spr.statnum == STAT_MINE_STUCK) { SpawnMineExp(actor); KillActor(actor); return 0; } // weapon is drivable object manned by player if (weapActor->user.PlayerP && weapActor->user.PlayerP->sop) { switch (weapActor->user.PlayerP->sop->track) { case SO_VEHICLE: damage = -200; if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { PLAYER* pp = Player + screenpeek; ActorHealth(actor, damage); if (actor->user.Health <= 0) { int choosesnd=0; // Random chance of taunting the AI's here if (StdRandomRange(1024) > 512 && pp == Player+myconnectindex) { choosesnd=RandomRange(MAX_TAUNTAI); PlayerSound(TauntAIVocs[choosesnd],v3df_dontpan|v3df_follow,pp); } SpawnShrap(actor, weapActor); SetSuicide(actor); return 0; } } break; } } // weapon is the actor - no missile used - example swords, axes, etc switch (weapActor->user.ID) { case NINJA_RUN_R0: ASSERT(weapActor->user.PlayerP); if (weapActor->user.WeaponNum == WPN_SWORD) damage = GetDamage(actor, weapActor, WPN_SWORD); else { damage = GetDamage(actor, weapActor, WPN_FIST); // Add damage for stronger attacks! switch (weapActor->user.PlayerP->WpnKungFuMove) { case 1: damage -= 2 - RandomRange(7); break; case 2: damage -= 5 - RandomRange(10); break; } PlaySound(DIGI_CGTHIGHBONE,weapActor,v3df_follow|v3df_dontpan); } if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case SKEL_RUN_R0: case COOLG_RUN_R0: case GORO_RUN_R0: damage = GetDamage(actor, weapActor, DMG_SKEL_SLASH); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case HORNET_RUN_R0: PlaySound(DIGI_HORNETSTING, actor, v3df_follow|v3df_dontpan); damage = GetDamage(actor, weapActor, DMG_HORNET_STING); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } break; case EEL_RUN_R0: damage = GetDamage(actor, weapActor, DMG_RIPPER_SLASH); damage /= 3; if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case RIPPER_RUN_R0: damage = GetDamage(actor, weapActor, DMG_RIPPER_SLASH); damage /= 3; // Little rippers aren't as tough. if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); if (PlayerCheckDeath(actor->user.PlayerP, weapActor)) { PlaySound(DIGI_RIPPERHEARTOUT,actor->user.PlayerP,v3df_dontpan|v3df_doppler); DoRipperRipHeart(weapActor); } } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case RIPPER2_RUN_R0: damage = GetDamage(actor, weapActor, DMG_RIPPER_SLASH); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); if (PlayerCheckDeath(actor->user.PlayerP, weapActor)) { PlaySound(DIGI_RIPPERHEARTOUT,actor->user.PlayerP,v3df_dontpan|v3df_doppler); DoRipper2RipHeart(weapActor); } } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case BUNNY_RUN_R0: damage = GetDamage(actor, weapActor, DMG_RIPPER_SLASH); damage /= 3; if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); if (PlayerCheckDeath(actor->user.PlayerP, weapActor)) { DoBunnyRipHeart(weapActor); } } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case SERP_RUN_R0: damage = GetDamage(actor, weapActor, DMG_SERP_SLASH); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage/4, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); SpawnBlood(actor, weapActor); SpawnBlood(actor, weapActor); break; case BLADE1: case BLADE2: case BLADE3: case 5011: if (weapActor->user.ID == 5011) damage = -(3 + (RandomRange(4<<8)>>8)); else damage = GetDamage(actor, weapActor, DMG_BLADE); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; if ((actor->user.BladeDamageTics -= synctics) < 0) { actor->user.BladeDamageTics = DAMAGE_BLADE_TIME; PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } } else { if ((actor->user.BladeDamageTics -= ACTORMOVETICS) < 0) { actor->user.BladeDamageTics = DAMAGE_BLADE_TIME; ActorHealth(actor, damage); } ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); break; case STAR1: case CROSSBOLT: damage = GetDamage(actor, weapActor, WPN_STAR); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { // Is the player blocking? if (actor->user.PlayerP->WpnKungFuMove == 3) damage /= 3; PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } StarBlood(actor, weapActor); weapActor->user.ID = 0; SetSuicide(weapActor); break; case SPEAR_R0: damage = GetDamage(actor, weapActor, DMG_SPEAR_TRAP); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); weapActor->user.ID = 0; SetSuicide(weapActor); break; case LAVA_BOULDER: damage = GetDamage(actor, weapActor, DMG_LAVA_BOULDER); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); weapActor->user.ID = 0; SetSuicide(weapActor); break; case LAVA_SHARD: damage = GetDamage(actor, weapActor, DMG_LAVA_SHARD); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } SpawnBlood(actor, weapActor); weapActor->user.ID = 0; SetSuicide(weapActor); break; case UZI_SMOKE: case UZI_SMOKE+2: if (weapActor->user.ID == UZI_SMOKE) damage = GetDamage(actor, weapActor, WPN_UZI); else damage = GetDamage(actor, weapActor, WPN_UZI)/3; // Enemy Uzi, 1/3 damage if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { //PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); PlayerDamageSlide(actor->user.PlayerP, damage/2, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); switch (actor->user.ID) { case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: case ZILLA_RUN_R0: break; default: if (RandomRange(1000) > 900) InitBloodSpray(actor,false,105); if (RandomRange(1000) > 900) SpawnMidSplash(actor); break; } } //SpawnBlood(actor, weapActor); // reset id so no more damage is taken weapActor->user.ID = 0; break; case SHOTGUN_SMOKE: damage = GetDamage(actor, weapActor, WPN_SHOTGUN); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } //SpawnBlood(actor, weapActor); switch (actor->user.ID) { case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: case ZILLA_RUN_R0: break; default: if (RandomRange(1000) > 950) SpawnMidSplash(actor); break; } // reset id so no more damage is taken weapActor->user.ID = 0; break; case MIRV_METEOR: //damage = -DAMAGE_MIRV_METEOR; damage = GetDamage(actor, weapActor, DMG_MIRV_METEOR); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); } SetSuicide(weapActor); break; case SERP_METEOR: //damage = -DAMAGE_SERP_METEOR; damage = GetDamage(actor, weapActor, DMG_SERP_METEOR); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); } SetSuicide(weapActor); break; case BOLT_THINMAN_R0: damage = GetDamage(actor, weapActor, WPN_ROCKET); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); } if (weapActor->user.Radius == NUKE_RADIUS) SpawnNuclearExp(weapActor); else SpawnBoltExp(weapActor); SetSuicide(weapActor); break; case BOLT_THINMAN_R1: //damage = -(2000 + (65 + RandomRange(40))); // -2000 makes armor not count damage = -(65 + RandomRange(40)); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { // this is special code to prevent the Zombie from taking out the Bosses to quick // if rail gun weapon Owner is not player auto own = GetOwner(weapActor); if (own && own->hasU() && !own->user.PlayerP) { // if actor is a boss if (actor->user.ID == ZILLA_RUN_R0 || actor->user.ID == SERP_RUN_R0 || actor->user.ID == SUMO_RUN_R0) damage /= 2; } ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage>>1, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } weapActor->user.ID = 0; // No more damage SpawnTracerExp(weapActor); SetSuicide(weapActor); break; case BOLT_THINMAN_R2: damage = (GetDamage(actor, weapActor, WPN_ROCKET)/2); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); } if (weapActor->user.Radius == NUKE_RADIUS) SpawnNuclearExp(weapActor); else SpawnBoltExp(weapActor); SetSuicide(weapActor); break; case BOLT_THINMAN_R4: damage = GetDamage(actor, weapActor, DMG_GRENADE_EXP); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } SpawnBunnyExp(weapActor); SetSuicide(weapActor); break; case SUMO_RUN_R0: damage = GetDamage(actor, weapActor, DMG_FLASHBOMB); damage /= 3; if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; case BOLT_EXP: damage = GetDamage(actor, weapActor, DMG_BOLT_EXP); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; case BLOOD_WORM: // Don't hurt blood worm zombies! if (actor->user.ID == ZOMBIE_RUN_R0) break; damage = GetDamage(actor, weapActor, WPN_HEART); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { //PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); if (PlayerCheckDeath(actor->user.PlayerP, weapActor)) { // degrade blood worm life weapActor->user.Counter3 += (4*120)/MISSILEMOVETICS; } } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } // degrade blood worm life weapActor->user.Counter3 += (2*120)/MISSILEMOVETICS; break; case TANK_SHELL_EXP: damage = GetDamage(actor, weapActor, DMG_TANK_SHELL_EXP); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; case MUSHROOM_CLOUD: case GRENADE_EXP: if (weapActor->user.Radius == NUKE_RADIUS) // Special Nuke stuff damage = (GetDamage(actor, weapActor, DMG_NUCLEAR_EXP)); else damage = GetDamage(actor, weapActor, DMG_GRENADE_EXP); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { // Don't let it hurt the SUMO if (OwnerIs(weapActor, SUMO_RUN_R0)) break; ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; case MICRO_EXP: damage = GetDamage(actor, weapActor, DMG_MINE_EXP); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; case MINE_EXP: { damage = GetDamage(actor, weapActor, DMG_MINE_EXP); if (OwnerIs(weapActor, SERP_RUN_R0)) { damage /= 6; } if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { auto own = GetOwner(weapActor); if (own && own->hasU()) { // Don't let serp skulls hurt the Serpent God if (OwnerIs(weapActor, SERP_RUN_R0)) break; // Don't let it hurt the SUMO if (OwnerIs(weapActor, SUMO_RUN_R0)) break; } if (actor->user.ID == TRASHCAN) ActorHealth(actor, -500); else ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } // reset id so no more damage is taken weapActor->user.ID = 0; break; } #if 0 case MINE_SHRAP: damage = GetDamage(actor, weapActor, DMG_MINE_SHRAP); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.angle); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.angle); ActorChooseDeath(actor, weapActor); } // reset id so no more damage is taken weapActor->user.ID = 0; break; #endif case NAP_EXP: { damage = GetDamage(actor, weapActor, DMG_NAPALM_EXP); // Sumo Nap does less if (OwnerIs(weapActor, SUMO_RUN_R0)) damage /= 4; if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { // Don't let it hurt the SUMO if (OwnerIs(weapActor, SUMO_RUN_R0)) break; ActorHealth(actor, damage); ActorChooseDeath(actor, weapActor); } SetSuicide(weapActor); break; } case Vomit1: case Vomit2: damage = GetDamage(actor, weapActor, DMG_VOMIT); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } SetSuicide(weapActor); break; case COOLG_FIRE: damage = GetDamage(actor, weapActor, DMG_COOLG_FIRE); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } // actor->user.ID = 0; SetSuicide(weapActor); break; // Skull Exp case SKULL_R0: case BETTY_R0: damage = GetDamage(actor, weapActor, DMG_SKULL_EXP); if (actor->user.sop_parent) { if (actor->user.sop_parent->flags & (SOBJ_DIE_HARD)) break; SopDamage(actor->user.sop_parent, damage); SopCheckKill(actor->user.sop_parent); break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, AngToPlayer(actor->user.PlayerP, weapActor)); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorDamageSlide(actor, damage, AngToSprite(actor, weapActor)); ActorChooseDeath(actor, weapActor); } break; // Serp ring of skull case SKULL_SERP: //DoSkullBeginDeath(Weapon); break; case FIREBALL1: { damage = GetDamage(actor, weapActor, WPN_HOTHEAD); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } auto own = GetOwner(weapActor); if (own && own->hasU()) // For SerpGod Ring own->user.Counter--; SpawnFireballFlames(weapActor, actor); SetSuicide(weapActor); break; } case FIREBALL: case GORO_FIREBALL: damage = GetDamage(actor, weapActor, DMG_GORO_FIREBALL); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { PlayerDamageSlide(actor->user.PlayerP, damage, weapActor->spr.Angles.Yaw); if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } if (actor->user.PlayerP->Armor) PlaySound(DIGI_ARMORHIT,actor->user.PlayerP,v3df_dontpan|v3df_follow|v3df_doppler); } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorDamageSlide(actor, damage, weapActor->spr.Angles.Yaw); ActorChooseDeath(actor, weapActor); } SpawnGoroFireballExp(weapActor); SetSuicide(weapActor); break; case FIREBALL_FLAMES: damage = -DamageData[DMG_FIREBALL_FLAMES].damage_lo; if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorChooseDeath(actor, weapActor); } break; case RADIATION_CLOUD: damage = GetDamage(actor, weapActor, DMG_RADIATION_CLOUD); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { PLAYER* pp = actor->user.PlayerP; PlayerSound(DIGI_GASHURT, v3df_dontpan|v3df_follow|v3df_doppler,pp); PlayerUpdateHealth(actor->user.PlayerP, damage-1000); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { // Don't let it hurt the SUMO if (OwnerIs(weapActor, SUMO_RUN_R0)) break; ActorHealth(actor, damage); ActorPain(actor); ActorChooseDeath(actor, weapActor); } // actor->user.ID = 0; weapActor->user.ID = 0; break; case PLASMA: //damage = GetDamage(actor, weapActor, WPN_HEART); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { //PlayerUpdateHealth(actor->user.PlayerP, damage); //PlayerCheckDeath(actor->user.PlayerP, weapActor); } else { if (actor->user.ID == SKULL_R0 || actor->user.ID == BETTY_R0) { ActorHealth(actor, damage); ActorStdMissile(actor, weapActor); ActorChooseDeath(actor, weapActor); SetSuicide(weapActor); break; } else if (actor->user.ID == RIPPER_RUN_R0) { DoRipperGrow(actor); break; } ActorPainPlasma(actor); } InitPlasmaFountain(weapActor, actor); SetSuicide(weapActor); break; case CALTROPS: { damage = GetDamage(actor, weapActor, DMG_MINE_SHRAP); if (actor->user.sop_parent) { break; } else if (actor->user.PlayerP) { if (PlayerTakeDamage(actor->user.PlayerP, weapActor)) { if (RANDOM_P2(1024<<4)>>4 < 800) PlayerSound(DIGI_STEPONCALTROPS, v3df_follow|v3df_dontpan, actor->user.PlayerP); PlayerUpdateHealth(actor->user.PlayerP, damage); PlayerCheckDeath(actor->user.PlayerP, weapActor); } } else { ActorHealth(actor, damage); ActorPain(actor); ActorStdMissile(actor, weapActor); ActorChooseDeath(actor, weapActor); } SetSuicide(weapActor); break; } } // If player take alot of damage, make him yell if (actor->hasU() && actor->user.PlayerP) { if (damage <= -40 && RandomRange(1000) > 700) PlayerSound(DIGI_SONOFABITCH, v3df_dontpan|v3df_follow, actor->user.PlayerP); else if (damage <= -40 && RandomRange(1000) > 700) PlayerSound(DIGI_PAINFORWEAK, v3df_dontpan|v3df_follow, actor->user.PlayerP); else if (damage <= -10) PlayerSound(PlayerPainVocs[RandomRange(MAX_PAIN)], v3df_dontpan|v3df_follow, actor->user.PlayerP); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- const char *DeathString(DSWActor* actor) { if (!actor->hasU()) return " "; switch (actor->user.ID) { case NINJA_RUN_R0: return " "; case ZOMBIE_RUN_R0: return GStrings("Zombie"); case BLOOD_WORM: return GStrings("Blood Worm"); case SKEL_RUN_R0: return GStrings("Skeletor Priest"); case COOLG_RUN_R0: return GStrings("Coolie Ghost"); case GORO_RUN_R0: return GStrings("Guardian"); case HORNET_RUN_R0: return GStrings("Hornet"); case RIPPER_RUN_R0: return GStrings("Ripper Hatchling"); case RIPPER2_RUN_R0: return GStrings("Ripper"); case BUNNY_RUN_R0: return GStrings("Killer Rabbit"); case SERP_RUN_R0: return GStrings("Serpent god"); case GIRLNINJA_RUN_R0: return GStrings("Girl Ninja"); case BLADE1: case BLADE2: case BLADE3: case 5011: return GStrings("blade"); case STAR1: if (sw_darts) return GStrings("dart"); else return GStrings("shuriken"); case CROSSBOLT: return GStrings("crossbow bolt"); case SPEAR_R0: return GStrings("spear"); case LAVA_BOULDER: case LAVA_SHARD: return GStrings("lava boulder"); case UZI_SMOKE: return GStrings("Uzi"); case UZI_SMOKE+2: return GStrings("Evil Ninja Uzi"); case SHOTGUN_SMOKE: return GStrings("shotgun"); case MIRV_METEOR: case SERP_METEOR: return GStrings("meteor"); case BOLT_THINMAN_R0: return GStrings("rocket"); case BOLT_THINMAN_R1: return GStrings("rail gun"); case BOLT_THINMAN_R2: return GStrings("enemy rocket"); case BOLT_THINMAN_R4: // BunnyRocket return GStrings("bunny rocket"); case BOLT_EXP: return GStrings("explosion"); case TANK_SHELL_EXP: return GStrings("tank shell"); case MUSHROOM_CLOUD: return GStrings("nuclear bomb"); case GRENADE_EXP: return GStrings("40mm grenade"); case MICRO_EXP: return GStrings("micro missile"); case MINE_EXP: //case MINE_SHRAP: return GStrings("sticky bomb"); case NAP_EXP: return GStrings("napalm"); case Vomit1: case Vomit2: return GStrings("vomit"); case COOLG_FIRE: return GStrings("Coolie Ghost phlem"); case SKULL_R0: return GStrings("Accursed Head"); case BETTY_R0: return GStrings("Bouncing Betty"); case SKULL_SERP: return GStrings("Serpent god Protector"); case FIREBALL1: case FIREBALL: case GORO_FIREBALL: case FIREBALL_FLAMES: return GStrings("flames"); case RADIATION_CLOUD: return GStrings("radiation"); case CALTROPS: return GStrings("useitem 7"); } return ""; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoDamageTest(DSWActor* actor) { int i; unsigned stat; for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius() * 2) continue; if (actor == itActor) continue; if (!(itActor->spr.cstat & CSTAT_SPRITE_BLOCK)) continue; // !JIM! Put in a cansee so that you don't take damage through walls and such // For speed's sake, try limiting check only to radius weapons! if (actor->user.Radius > 200) { if (!FAFcansee(ActorUpperVect(itActor), itActor->sector(),actor->spr.pos,actor->sector())) continue; } if (GetOwner(actor) != itActor && SpriteOverlap(actor, itActor)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static void DoHitscanDamage(DSWActor* weaponActor, DSWActor* hitActor) { if (hitActor == nullptr) return; unsigned stat; // this routine needs some sort of sprite generated from the hitscan // such as a smoke or spark sprite - reason is because of DoDamage() for (stat = 0; stat < SIZ(StatDamageList); stat++) { if (hitActor->spr.statnum == StatDamageList[stat]) { DoDamage(hitActor, weaponActor); break; } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFlamesDamageTest(DSWActor* actor) { int i; unsigned stat; for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { switch (itActor->user.ID) { case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: continue; } double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius() * 2) continue; if (actor == itActor) continue; if (!(itActor->spr.cstat & (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN))) continue; if (actor->spr.cstat & (CSTAT_SPRITE_INVISIBLE)) continue; if (actor->user.Radius > 200) // Note: No weaps have bigger radius than 200 cept explosion stuff { if (FAFcansee(ActorVectOfMiddle(itActor),itActor->sector(),ActorVectOfMiddle(actor),actor->sector())) { DoDamage(itActor, actor); } } else if (SpriteOverlap(actor, itActor)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- walltype* PrevWall(walltype* wall_num) { for(auto&wal : wall_num->sectorp()->walls) { if (wal.point2Wall() == wall_num) return &wal; } return wall_num; // should never happen unless the sector is malformed. } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- short StatBreakList[] = { STAT_DEFAULT, STAT_BREAKABLE, STAT_NO_STATE, STAT_DEAD_ACTOR, }; void TraverseBreakableWalls(sectortype* start_sect, const DVector3& pos, DAngle angle, double radius) { int k; DVector2 mid; int break_count; // limit radius if (radius > 125) radius = 125; break_count = 0; BFSSectorSearch search(start_sect); while (auto sect = search.GetNext()) { for(auto& wal : sect->walls) { // see if this wall should be broken if (wal.lotag == TAG_WALL_BREAK) { // find midpoint mid = wal.center(); // don't need to go further if wall is too far out double dist = (mid - pos.XY()).Length(); if (dist > radius) continue; sectortype* sectp = nullptr; DVector3 hitpos; DAngle wall_ang; if (WallBreakPosition(&wal, §p, hitpos, wall_ang)) { if (sectp != nullptr && FAFcansee(pos, start_sect, hitpos, sectp)) { HitBreakWall(&wal, DVector3(INT32_MAX, INT32_MAX, INT32_MAX), angle, 0); break_count++; if (break_count > 4) { return; } } } } if (wal.twoSided()) search.Add(wal.nextSector()); } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoExpDamageTest(DSWActor* actor) { short i, stat; int max_stat; short break_count; DSWActor* found_act = nullptr; double found_dist = 999999; int DoWallMoveMatch(short match); // crack sprites if (actor->user.ID != MUSHROOM_CLOUD) WeaponExplodeSectorInRange(actor); // Just like DoDamageTest() except that it doesn't care about the Owner max_stat = SIZ(StatDamageList); // don't check for mines if the weapon is a mine if (actor->spr.statnum == STAT_MINE_STUCK) max_stat--; for (stat = 0; stat < max_stat; stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius() * 2) continue; if (itActor == actor) continue; if (StatDamageList[stat] == STAT_SO_SP_CHILD) { DoDamage(itActor, actor); } else { double d = (itActor->spr.pos - actor->spr.pos).Length(); if (d > actor->user.fRadius() + itActor->user.fRadius()) continue; // added hitscan block because mines no long clip against actors/players if (!(itActor->spr.cstat & (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN))) continue; // Second parameter MUST have blocking bits set or cansee won't work // added second check for FAF water - hitscans were hitting ceiling if (!FAFcansee(actor->spr.pos, actor->sector(), ActorUpperVect(itActor), itActor->sector()) && !FAFcansee(actor->spr.pos, actor->sector(), ActorLowerVect(itActor), itActor->sector())) continue; DoDamage(itActor, actor); } } } if (actor->user.ID == MUSHROOM_CLOUD) return 0; // Central Nuke doesn't break stuff // Only secondaries do that TraverseBreakableWalls(actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, actor->user.Radius); break_count = 0; max_stat = SIZ(StatBreakList); // Breakable stuff for (stat = 0; stat < max_stat; stat++) { SWStatIterator it(StatBreakList[stat]); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius()) continue; dist = (ActorVectOfMiddle(itActor) - actor->spr.pos).Length(); if (dist > actor->user.fRadius()) continue; if (!FAFcansee(ActorVectOfMiddle(itActor), itActor->sector(), actor->spr.pos, actor->sector())) continue; if ((itActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(itActor, actor->user.ID); break_count++; if (break_count > 6) break; } } } if (actor->user.ID == BLOOD_WORM) return 0; // wall damaging SWStatIterator it(STAT_WALL_MOVE); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius() * 0.25) continue; if (TEST_BOOL1(actor)) continue; if (!CanSeeWallMove(actor, SP_TAG2(actor))) continue; if (dist < found_dist) { found_dist = dist; found_act = itActor; } } if (found_act) { if (SP_TAG2(found_act) == 0) { // just do one DoWallMove(found_act); } else { if (DoWallMoveMatch(SP_TAG2(found_act))) { DoSpawnSpotsForDamage(SP_TAG2(found_act)); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMineExpMine(DSWActor* actor) { int i; SWStatIterator it(STAT_MINE_STUCK); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos.XY() - actor->spr.pos.XY()).Length(); if (dist > actor->user.fRadius() * 2) continue; if (itActor == actor) continue; if (!(itActor->spr.cstat & CSTAT_SPRITE_BLOCK_HITSCAN)) continue; // Explosions are spherical, not planes, so let's check that way, well cylindrical at least. double zdist = abs(itActor->spr.pos.Z - actor->spr.pos.Z); if (SpriteOverlap(actor, itActor) || zdist < actor->user.fRadius() * 2) { DoDamage(itActor, actor); // only explode one mine at a time break; } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoStar(DSWActor* actor) { const int STAR_STICK_RNUM = 400; const int STAR_BOUNCE_RNUM = 600; if (actor->user.Flags & (SPR_UNDERWATER)) { actor->user.motion_blur_num = 0; ScaleSpriteVector(actor, 54000); auto velsq = actor->user.change.XY().LengthSquared(); if (velsq > 6.25 * 6.25) { if ((RANDOM_P2(1024 << 4) >> 4) < 128) SpawnBubble(actor); } actor->spr.pos.Z += 0.5 * MISSILEMOVETICS; DoActorZrange(actor); MissileWaterAdjust(actor); if (actor->spr.pos.Z > actor->user.loz) { KillActor(actor); return true; } } else { auto velsq = actor->user.change.XY().LengthSquared(); if (velsq < 50 * 50) { actor->user.Counter += 50; actor->user.addCounterToChange(); } } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone && !(actor->user.Flags & SPR_UNDERWATER)) { switch (actor->user.coll.type) { default: break; case kHitWall: { short nw,wall_ang; walltype* wph; wph = actor->user.coll.hitWall; if (wph->lotag == TAG_WALL_BREAK) { HitBreakWall(wph, actor->spr.pos, actor->spr.Angles.Yaw, actor->user.ID); actor->user.coll.setNone(); break; } // special case with MissileSetPos - don't queue star // from this routine if (actor->user.Flags & (SPR_SET_POS_DONT_KILL)) break; // chance of sticking if (!(actor->user.Flags & SPR_BOUNCE) && RANDOM_P2(1024) < STAR_STICK_RNUM) { actor->user.motion_blur_num = 0; ChangeState(actor, s_StarStuck); actor->spr.scale.X += (-0.25); actor->spr.scale.Y += (-0.25); actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actor->clipdist = 1; actor->user.ceiling_dist = 2; actor->user.floor_dist = 2; // treat this just like a KillSprite but don't kill QueueStar(actor); return 0; } // chance of bouncing if (RANDOM_P2(1024) < STAR_BOUNCE_RNUM) break; WallBounce(actor, wph->delta().Angle() + DAngle90); ScaleSpriteVector(actor, 36000); actor->user.Flags |= (SPR_BOUNCE); actor->user.motion_blur_num = 0; actor->user.coll.setNone(); break; } case kHitSector: { bool did_hit_wall; auto hit_sect = actor->user.coll.hitSector; if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { if (hit_sect->hasU() && FixedToInt(hit_sect->depth_fixed) > 0) { SpawnSplash(actor); KillActor(actor); return true; // hit water - will be taken care of in WeaponMoveHit //break; } } if (actor->user.lowActor) if (actor->user.lowActor->spr.lotag == TAG_SPRITE_HIT_MATCH) break; if (actor->user.highActor) if (actor->user.highActor->spr.lotag == TAG_SPRITE_HIT_MATCH) break; ScaleSpriteVector(actor, 58000); auto velsq = actor->user.change.XY().LengthSquared(); if (velsq < 31.25 * 31.25) break; // will be killed below - actor != 0 // 32000 to 96000 int xscale = 64000 + (RandomRange(64000) - 32000); int yscale = 64000 + (RandomRange(64000) - 32000); int zscale; if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) zscale = 50000; // floor else zscale = 40000; // ceiling ScaleSpriteVector(actor, xscale, yscale, zscale); if (SlopeBounce(actor, &did_hit_wall)) { if (did_hit_wall) { // chance of sticking if (RANDOM_P2(1024) < STAR_STICK_RNUM) break; // chance of bouncing if (RANDOM_P2(1024) < STAR_BOUNCE_RNUM) break; actor->user.Flags |= (SPR_BOUNCE); actor->user.motion_blur_num = 0; actor->user.coll.setNone(); } else { // hit a sloped sector < 45 degrees actor->user.Flags |= (SPR_BOUNCE); actor->user.motion_blur_num = 0; actor->user.coll.setNone(); } // BREAK HERE - LOOOK !!!!!!!!!!!!!!!!!!!!!!!! break; // hit a slope } actor->user.Flags |= (SPR_BOUNCE); actor->user.motion_blur_num = 0; actor->user.coll.setNone(); actor->user.change.Z = -actor->user.change.Z; // 32000 to 96000 xscale = 64000 + (RandomRange(64000) - 32000); yscale = 64000 + (RandomRange(64000) - 32000); if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) zscale = 50000; // floor else zscale = 40000; // ceiling ScaleSpriteVector(actor, xscale, yscale, zscale); break; } } } if (actor->user.coll.type != kHitNone) { if (actor->user.coll.type == kHitSprite && actor->user.coll.actor()->hasU()) { auto su = actor->user.coll.actor(); if (su->user.ID == TRASHCAN || su->user.ID == ZILLA_RUN_R0) PlaySound(DIGI_STARCLINK, actor, v3df_none); } if (actor->user.coll.type != kHitSprite) // Don't clank on sprites PlaySound(DIGI_STARCLINK, actor, v3df_none); if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoCrossBolt(DSWActor* actor) { DoBlurExtend(actor, 0, 2); actor->user.coll = move_missile(actor, actor->user.change, 16, 16, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoPlasmaDone(DSWActor* actor) { actor->spr.scale.X += (actor->user.Counter * REPEAT_SCALE); actor->spr.scale.Y += (-0.0625); actor->user.Counter += 2; if (actor->spr.scale.Y < 0.09375) { KillActor(actor); return 0; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* PickEnemyTarget(DSWActor* actor, DAngle aware_range) { TARGET_SORT* ts; DoPickTarget(actor, aware_range, false); for (ts = TargetSort; ts < &TargetSort[TargetSortCount]; ts++) { if (ts->actor != nullptr) { if (ts->actor == GetOwner(actor) || GetOwner(ts->actor) == GetOwner(actor)) continue; return ts->actor; } } return nullptr; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int MissileSeek(DSWActor* actor, int16_t delay_tics, DAngle aware_range/*, int16_t dang_shift, int16_t turn_limit, int16_t z_limit*/) { if (actor->user.WaitTics <= delay_tics) actor->user.WaitTics += MISSILEMOVETICS; if (actor->user.WpnGoalActor == nullptr) { if (actor->user.WaitTics > delay_tics) { DSWActor* hitActor; if (actor->user.Flags2 & (SPR2_DONT_TARGET_OWNER)) { if ((hitActor = PickEnemyTarget(actor, aware_range)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } } else if ((hitActor = DoPickTarget(actor, aware_range, false)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } } } DSWActor* goal = actor->user.WpnGoalActor; if (goal != nullptr) { // move to correct angle auto ang2tgt = (goal->spr.pos - actor->spr.pos).Angle(); auto delta_ang = clamp(deltaangle(ang2tgt, actor->spr.Angles.Yaw), -DAngle45 / 8, DAngle45 / 8); actor->spr.Angles.Yaw -= delta_ang; double zh = ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25); auto vel = clamp((zh - actor->spr.pos.Z)* 0.5, -16., 16.); actor->vel.Z = vel; UpdateChange(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SetAngleFromChange(DSWActor* actor) { actor->spr.Angles.Yaw = actor->user.change.Angle(); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int VectorMissileSeek(DSWActor* actor, int16_t delay_tics, int16_t turn_speed, DAngle aware_range1, DAngle aware_range2) { if (actor->user.WaitTics <= delay_tics) actor->user.WaitTics += MISSILEMOVETICS; if (actor->user.WpnGoalActor == nullptr) { if (actor->user.WaitTics > delay_tics) { DSWActor* hitActor; if (actor->user.Flags2 & (SPR2_DONT_TARGET_OWNER)) { if ((hitActor = PickEnemyTarget(actor, aware_range1)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } else if ((hitActor = PickEnemyTarget(actor, aware_range2)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } } else { if ((hitActor = DoPickTarget(actor, aware_range1, false)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } else if ((hitActor = DoPickTarget(actor, aware_range2, false)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } } } } DSWActor* goal = actor->user.WpnGoalActor; if (goal != nullptr) { auto delta = (goal->spr.pos.XY() - actor->spr.pos.XY()); double zdiff = ActorZOfTop(goal) + (ActorSizeZ(goal) * 0.25) - actor->spr.pos.Z; double dist = g_sqrt(delta.LengthSquared() + zdiff * zdiff); auto oc = actor->user.change; auto vel = actor->vel.X / (16 * dist); actor->user.change = DVector3(delta, zdiff) * vel; // the large turn_speed is the slower the turn actor->user.change += (oc * (turn_speed-1)) / turn_speed; SetAngleFromChange(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int VectorWormSeek(DSWActor* actor, int16_t delay_tics, DAngle aware_range1, DAngle aware_range2) { if (actor->user.WaitTics <= delay_tics) actor->user.WaitTics += MISSILEMOVETICS; if (actor->user.WpnGoalActor == nullptr) { if (actor->user.WaitTics > delay_tics) { DSWActor* hitActor; if ((hitActor = DoPickTarget(actor, aware_range1, false)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } else if ((hitActor = DoPickTarget(actor, aware_range2, false)) != nullptr) { actor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); } } } DSWActor* goal = actor->user.WpnGoalActor; if (goal != nullptr) { auto delta = (goal->spr.pos.XY() - actor->spr.pos.XY()); double zdiff = ActorZOfTop(goal) + (ActorSizeZ(goal) * 0.25) - actor->spr.pos.Z; double dist = g_sqrt(delta.LengthSquared() + zdiff * zdiff); auto oc = actor->user.change; auto vel = actor->vel.X / (16 * dist); actor->user.change = DVector3(delta, zdiff) * vel + oc * (7. / 8); SetAngleFromChange(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBlurExtend(DSWActor* actor, int16_t interval, int16_t blur_num) { if (actor->user.motion_blur_num >= blur_num) return 0; actor->user.Counter2++; if (actor->user.Counter2 > interval) actor->user.Counter2 = 0; if (!actor->user.Counter2) { actor->user.motion_blur_num++; if (actor->user.motion_blur_num > blur_num) actor->user.motion_blur_num = blur_num; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitPlasmaFountain(DSWActor* wActor, DSWActor* sActor) { auto actorNew = SpawnActor(STAT_MISSILE, PLASMA_FOUNTAIN, s_PlasmaFountain, sActor->sector(), ActorVectOfBottom(sActor), sActor->spr.Angles.Yaw, 0); actorNew->spr.shade = -40; if (wActor) SetOwner(GetOwner(wActor), actorNew); SetAttach(sActor, actorNew); actorNew->spr.scale.Y = (0); actorNew->clipdist = 0.5; actorNew->user.WaitTics = 120+60; actorNew->user.Radius = 50; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoPlasmaFountain(DSWActor* actor) { // if no Owner then die if (actor->user.attachActor == nullptr) { KillActor(actor); return 0; } else { DSWActor* attachActor = actor->user.attachActor; if (!attachActor) return 0; // move with sprite SetActorZ(actor, attachActor->spr.pos); actor->spr.Angles.Yaw = attachActor->spr.Angles.Yaw; actor->user.Counter++; if (actor->user.Counter > 3) actor->user.Counter = 0; if (!actor->user.Counter) { SpawnBlood(attachActor, actor); if (RandomRange(1000) > 600) InitBloodSpray(attachActor, false, 105); } } // kill the fountain if ((actor->user.WaitTics-=MISSILEMOVETICS) <= 0) { actor->user.WaitTics = 0; auto bak_cstat = actor->spr.cstat; actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK); actor->spr.cstat = bak_cstat; KillActor(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoPlasma(DSWActor* actor) { auto oldv = actor->spr.pos; DoBlurExtend(actor, 0, 4); auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X; double daz = actor->vel.Z; actor->user.coll = move_missile(actor, DVector3(vec, daz), 16, 16, CLIPMASK_MISSILE, MISSILEMOVETICS); { // this sprite is supposed to go through players/enemys // if hit a player/enemy back up and do it again with blocking reset if (actor->user.coll.type == kHitSprite) { auto hitActor = actor->user.coll.actor(); if ((hitActor->spr.cstat & CSTAT_SPRITE_BLOCK) && !(hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { auto hcstat = hitActor->spr.cstat; if (hitActor->hasU() && hitActor != actor->user.WpnGoalActor) { actor->spr.pos = oldv; hitActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.coll = move_missile(actor, DVector3(vec, daz), 16, 16, CLIPMASK_MISSILE, MISSILEMOVETICS); hitActor->spr.cstat = hcstat; } } } } MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { if (actor->user.Flags & (SPR_SUICIDE)) { KillActor(actor); return true; } else { actor->user.Counter = 4; ChangeState(actor, s_PlasmaDone); } return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoCoolgFire(DSWActor* actor) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { PlaySound(DIGI_CGMAGICHIT, actor, v3df_follow); ChangeState(actor, s_CoolgFireDone); auto own = GetOwner(actor); if (own && own->hasU() && own->user.ID != RIPPER_RUN_R0) // JBF: added range check SpawnDemonFist(actor); // Just a red magic circle flash return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoEelFire(DSWActor* actor) { if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); return false; } void ScaleSpriteVector(DSWActor* actor, int scalex, int scaley, int scalez) { actor->user.change.X *= FixedToFloat(scalex); actor->user.change.Y *= FixedToFloat(scaley); actor->user.change.Z *= FixedToFloat(scalez); } void ScaleSpriteVector(DSWActor* actor, int scale) { actor->user.change *= FixedToFloat(scale); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WallBounce(DSWActor* actor, DAngle ang) { actor->user.bounce++; double k = ang.Cos() * ang.Sin() * 2; double l = (ang * 2).Cos(); auto davec = -actor->user.change.XY(); actor->user.change.X = davec.Y * k + davec.X * l; actor->user.change.Y = davec.X * k - davec.Y * l; auto old_ang = actor->spr.Angles.Yaw; SetAngleFromChange(actor); // hack to prevent missile from sticking to a wall // if (old_ang == actor->spr.Angles.Yaw) { actor->user.change.XY() = -actor->user.change.XY(); SetAngleFromChange(actor); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool SlopeBounce(DSWActor* actor, bool* hit_wall) { double hiz, loz; int slope; auto hit_sector = actor->user.coll.hitSector; calcSlope(hit_sector, actor->spr.pos, &hiz, &loz); // detect the ceiling and the hit_wall if (actor->spr.pos.Z < ((hiz + loz) * 0.5)) { if (!(hit_sector->ceilingstat & CSTAT_SECTOR_SLOPE)) slope = 0; else slope = hit_sector->ceilingheinum; } else { if (!(hit_sector->floorstat & CSTAT_SECTOR_SLOPE)) slope = 0; else slope = hit_sector->floorheinum; } if (!slope) return false; // if greater than a 45 degree angle if (abs(slope) > SLOPEVAL_FACTOR) *hit_wall = true; else *hit_wall = false; // get angle of the first wall of the sector auto wallp = hit_sector->walls.Data(); DAngle daang = wallp->delta().Angle(); // k is now the slope of the ceiling or floor // normal vector of the slope double dax = slope * daang.Sin(); double day = slope * -daang.Cos(); double daz = 256; // 4096/256 = 45 degrees // reflection code double k = actor->user.change.X * dax + actor->user.change.Y * day + actor->user.change.Z * daz; double l = (dax * dax) + (day * day) + (daz * daz); k /= l; actor->user.change.X = dax * k; actor->user.change.Y = day * k; actor->user.change.Z = daz * k * 16; SetAngleFromChange(actor); return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- extern STATE s_Phosphorus[]; int DoGrenade(DSWActor* actor) { short i; if (actor->user.Flags & (SPR_UNDERWATER)) { ScaleSpriteVector(actor, 50000); actor->user.Counter += 20; actor->user.addCounterToChange(); } else { actor->user.Counter += 20; actor->user.addCounterToChange(); } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { switch (actor->user.coll.type) { case kHitVoid: KillActor(actor); return true; case kHitSprite: { short wall_ang; PlaySound(DIGI_40MMBNCE, actor, v3df_dontpan); auto hitActor = actor->user.coll.actor(); // special case so grenade can ring gong if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (SP_TAG8(hitActor) & BIT(3)) DoMatchEverything(nullptr, hitActor->spr.hitag, -1); } if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { WallBounce(actor, hitActor->spr.Angles.Yaw); ScaleSpriteVector(actor, 32000); } else { if (actor->user.Counter2 == 1) // It's a phosphorus grenade! { for (i=0; i<5; i++) { actor->spr.Angles.Yaw = RandomAngle(); InitPhosphorus(actor); } } SpawnGrenadeExp(actor); KillActor(actor); return true; } break; } case kHitWall: { short nw,wall_ang; walltype* wph; wph = actor->user.coll.hitWall; if (wph->lotag == TAG_WALL_BREAK) { HitBreakWall(wph, actor->spr.pos, actor->spr.Angles.Yaw, actor->user.ID); actor->user.coll.setNone(); break; } PlaySound(DIGI_40MMBNCE, actor, v3df_dontpan); WallBounce(actor, wph->delta().Angle() + DAngle90); ScaleSpriteVector(actor, 22000); break; } case kHitSector: { bool did_hit_wall; if (SlopeBounce(actor, &did_hit_wall)) { if (did_hit_wall) { // hit a wall ScaleSpriteVector(actor, 22000); // 28000 actor->user.coll.setNone(); actor->user.Counter = 0; } else { // hit a sector if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { // hit a floor if (!(actor->user.Flags & SPR_BOUNCE)) { actor->user.Flags |= (SPR_BOUNCE); ScaleSpriteVector(actor, 40000); // 18000 actor->user.coll.setNone(); actor->user.change.Z /= 4; actor->user.Counter = 0; } else { if (actor->user.Counter2 == 1) // It's a phosphorus grenade! { for (i=0; i<5; i++) { actor->spr.Angles.Yaw = RandomAngle(); InitPhosphorus(actor); } } SpawnGrenadeExp(actor); KillActor(actor); return true; } } else { // hit a ceiling ScaleSpriteVector(actor, 22000); } } } else { // hit floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { if (actor->user.Flags & (SPR_UNDERWATER)) actor->user.Flags |= (SPR_BOUNCE); // no bouncing underwater if (actor->user.lo_sectp && actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed)) actor->user.Flags |= (SPR_BOUNCE); // no bouncing on shallow water if (!(actor->user.Flags & SPR_BOUNCE)) { actor->user.Flags |= (SPR_BOUNCE); actor->user.coll.setNone(); actor->user.Counter = 0; actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 40000); // 18000 actor->user.change.Z /= 4; PlaySound(DIGI_40MMBNCE, actor, v3df_dontpan); } else { if (actor->user.Counter2 == 1) // It's a phosphorus grenade! { for (i=0; i<5; i++) { actor->spr.Angles.Yaw = RandomAngle(); InitPhosphorus(actor); } } SpawnGrenadeExp(actor); KillActor(actor); return true; } } else // hit something above { actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 22000); PlaySound(DIGI_40MMBNCE, actor, v3df_dontpan); } } break; } } } if (actor->user.bounce > 10) { SpawnGrenadeExp(actor); KillActor(actor); return true; } // if you haven't bounced or your going slow do some puffs if (!(actor->user.Flags & (SPR_BOUNCE|SPR_UNDERWATER))) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_Puff, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 6.25); SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->opos = actor->opos; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.change = actor->user.change; ScaleSpriteVector(actorNew, 22000); if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoVulcanBoulder(DSWActor* actor) { actor->user.Counter += 40; actor->user.addCounterToChange(); actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); auto velsq = actor->user.change.XY().LengthSquared(); if (velsq < 1.875 * 1.875) { SpawnLittleExp(actor); KillActor(actor); return true; } if (actor->user.coll.type != kHitNone) { switch (actor->user.coll.type) { case kHitVoid: KillActor(actor); return true; case kHitSprite: { short wall_ang; auto hitActor = actor->user.coll.actor(); if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { WallBounce(actor, hitActor->spr.Angles.Yaw); ScaleSpriteVector(actor, 40000); } else { // hit an actor SpawnLittleExp(actor); KillActor(actor); return true; } break; } case kHitWall: { short nw,wall_ang; walltype* wph; wph = actor->user.coll.hitWall; if (wph->lotag == TAG_WALL_BREAK) { HitBreakWall(wph, actor->spr.pos, actor->spr.Angles.Yaw, actor->user.ID); actor->user.coll.setNone(); break; } WallBounce(actor, wph->delta().Angle() + DAngle90); ScaleSpriteVector(actor, 40000); break; } case kHitSector: { bool did_hit_wall; if (SlopeBounce(actor, &did_hit_wall)) { if (did_hit_wall) { // hit a sloped sector - treated as a wall because of large slope ScaleSpriteVector(actor, 30000); actor->user.coll.setNone(); actor->user.Counter = 0; } else { // hit a sloped sector if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { // hit a floor ScaleSpriteVector(actor, 30000, 30000, 12000); actor->user.coll.setNone(); actor->user.Counter = 0; // limit to a reasonable bounce value if (actor->user.change.Z > 32) actor->user.change.Z = 32; } else { // hit a sloped ceiling actor->user.coll.setNone(); actor->user.Counter = 0; ScaleSpriteVector(actor, 30000); } } } else { // hit unsloped floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { actor->user.coll.setNone(); actor->user.Counter = 0; ScaleSpriteVector(actor, 20000, 20000, 32000); // limit to a reasonable bounce value if (actor->user.change.Z > 24) actor->user.change.Z = 24; actor->user.change.Z = -actor->user.change.Z; } else // hit unsloped ceiling { actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 30000); } } break; } } } return false; } bool OwnerIsPlayer(DSWActor* actor) { auto own = GetOwner(actor); return (own && own->hasU() && own->user.PlayerP != nullptr); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMineRangeTest(DSWActor* actor, double range) { unsigned stat; bool ownerisplayer = false; ownerisplayer = OwnerIsPlayer(actor); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { double dist = (itActor->spr.pos - actor->spr.pos).Length(); if (dist > range) continue; if (actor == itActor) continue; if (!(itActor->spr.cstat & CSTAT_SPRITE_BLOCK)) continue; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; if (itActor->user.ID == GIRLNINJA_RUN_R0 && !ownerisplayer) continue; if (!FAFcansee(ActorUpperVect(actor),itActor->sector(),actor->spr.pos,actor->sector())) continue; return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMineStuck(DSWActor* actor) { constexpr int MINE_DETONATE_STATE = 99; // if no Owner then die DSWActor* attachActor = actor->user.attachActor; if (attachActor != nullptr) { ASSERT(attachActor->hasU()); // Is it attached to a dead actor? Blow it up if so. if ((attachActor->user.Flags & SPR_DEAD) && actor->user.Counter2 < MINE_DETONATE_STATE) { actor->user.Counter2 = MINE_DETONATE_STATE; actor->user.WaitTics = SEC(1)/2; } SetActorZ(actor, attachActor->spr.pos.plusZ(-actor->user.pos.Z)); actor->spr.pos.Z = attachActor->spr.pos.Z - (ActorSizeZ(attachActor) * 0.5); } // not activated yet if (!(actor->user.Flags & SPR_ACTIVE)) { if ((actor->user.WaitTics -= (MISSILEMOVETICS*2)) > 0) return false; // activate it //actor->user.WaitTics = 65536; actor->user.WaitTics = 32767; actor->user.Counter2 = 0; actor->user.Flags |= (SPR_ACTIVE); } // limit the number of times DoMineRangeTest is called actor->user.Counter++; if (actor->user.Counter > 1) actor->user.Counter = 0; if (actor->user.Counter2 != MINE_DETONATE_STATE) { if ((actor->user.Counter2++) > 30) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.WaitTics = 32767; // Keep reseting tics to make it stay forever actor->user.Counter2 = 0; } } if (!actor->user.Counter) { // not already in detonate state if (actor->user.Counter2 < MINE_DETONATE_STATE) { // if something came into range - detonate if (DoMineRangeTest(actor, 187.5)) { // move directly to detonate state actor->user.Counter2 = MINE_DETONATE_STATE; actor->user.WaitTics = SEC(1)/2; } } } actor->user.WaitTics -= (MISSILEMOVETICS * 2); // start beeping with pauses // quick and dirty beep countdown code switch (actor->user.Counter2) { #if 0 case 0: if (actor->user.WaitTics < SEC(45)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 1: if (actor->user.WaitTics < SEC(38)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 2: if (actor->user.WaitTics < SEC(30)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 3: if (actor->user.WaitTics < SEC(20)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 4: if (actor->user.WaitTics < SEC(15)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 5: if (actor->user.WaitTics < SEC(12)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 6: if (actor->user.WaitTics < SEC(10)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; case 7: if (actor->user.WaitTics < SEC(8)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2++; } break; #endif case 30: if (actor->user.WaitTics < SEC(6)) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); actor->user.Counter2 = MINE_DETONATE_STATE; } break; case MINE_DETONATE_STATE: if (actor->user.WaitTics < 0) { PlaySound(DIGI_MINEBEEP, actor, v3df_dontpan); SpawnMineExp(actor); KillActor(actor); return false; } break; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SetMineStuck(DSWActor* actor) { // stuck actor->user.Flags |= (SPR_BOUNCE); // not yet active for 1 sec actor->user.Flags &= ~(SPR_ACTIVE); actor->user.WaitTics = SEC(3); //actor->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.Counter = 0; change_actor_stat(actor, STAT_MINE_STUCK); ChangeState(actor, s_MineStuck); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMine(DSWActor* actor) { if (actor->user.Flags & (SPR_UNDERWATER)) { // decrease velocity ScaleSpriteVector(actor, 50000); actor->user.Counter += 20; actor->user.addCounterToChange(); } else { //actor->user.Counter += 75; actor->user.Counter += 40; actor->user.addCounterToChange(); } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { // check to see if you hit a sprite switch (actor->user.coll.type) { case kHitVoid: KillActor(actor); return 0; case kHitSprite: { auto hitActor = actor->user.coll.actor(); SetMineStuck(actor); // Set the Z position actor->spr.pos.Z = hitActor->spr.pos.Z - (ActorSizeZ(hitActor) * 0.5); // If it's not alive, don't stick it if (hitActor->hasU() && hitActor->user.Health <= 0) return false; // JBF: added null check // check to see if sprite is player or enemy if ((hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) { PLAYER* pp; // attach weapon to sprite SetAttach(hitActor, actor); actor->user.pos.Z = hitActor->spr.pos.Z - actor->spr.pos.Z; auto own = GetOwner(actor); if (own && own->hasU()) { if (own->user.PlayerP) { pp = own->user.PlayerP; if (RandomRange(1000) > 800) PlayerSound(DIGI_STICKYGOTU1, v3df_follow|v3df_dontpan,pp); else if (RandomRange(1000) > 800) PlayerSound(DIGI_STICKYGOTU2, v3df_follow|v3df_dontpan,pp); else if (RandomRange(1000) > 800) PlayerSound(DIGI_STICKYGOTU3, v3df_follow|v3df_dontpan,pp); else if (RandomRange(1000) > 800) PlayerSound(DIGI_STICKYGOTU4, v3df_follow|v3df_dontpan,pp); } } } else { if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { actor->user.Flags2 |= (SPR2_ATTACH_WALL); } else if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR)) { // hit floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) actor->user.Flags2 |= (SPR2_ATTACH_FLOOR); else actor->user.Flags2 |= (SPR2_ATTACH_CEILING); } else { SpawnMineExp(actor); KillActor(actor); return false; } } break; } case kHitWall: { auto hit_wall = actor->user.coll.hitWall; if (hit_wall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit_wall, actor->spr.pos, actor->spr.Angles.Yaw, actor->user.ID); actor->user.coll.setNone(); break; } SetMineStuck(actor); actor->user.Flags2 |= (SPR2_ATTACH_WALL); if ((hit_wall->extra & WALLFX_SECTOR_OBJECT)) { } if ((hit_wall->extra & WALLFX_DONT_STICK)) { SpawnMineExp(actor); KillActor(actor); return false; } break; } case kHitSector: { auto hit_sect = actor->user.coll.hitSector; SetMineStuck(actor); // hit floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) actor->user.Flags2 |= (SPR2_ATTACH_FLOOR); else actor->user.Flags2 |= (SPR2_ATTACH_CEILING); if ((hit_sect->extra & SECTFX_SECTOR_OBJECT)) { SpawnMineExp(actor); KillActor(actor); return false; } break; } } actor->user.coll.setNone(); } return false; } int DoPuff(DSWActor* actor) { actor->spr.pos += actor->user.change; return 0; } int DoRailPuff(DSWActor* actor) { actor->spr.scale.X += (0.0625); actor->spr.scale.Y += (0.0625); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBoltThinMan(DSWActor* actor) { DoBlurExtend(actor, 0, 4); auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X; double daz = actor->vel.Z; actor->user.coll = move_missile(actor, DVector3(vec, daz), CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.Flags & (SPR_SUICIDE)) return true; if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnBoltExp(actor); KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoTracer(DSWActor* actor) { for (int i = 0; i < 4; i++) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } } actor->spr.cstat &= ~(CSTAT_SPRITE_INVISIBLE); return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoEMP(DSWActor* actor) { for (int i = 0; i < 4; i++) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (RandomRange(1000) > 500) { actor->spr.scale = DVector2(0.8125, 0.15626); } else { actor->spr.scale = DVector2(0.125, 0.59375); } if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } } actor->spr.cstat &= ~(CSTAT_SPRITE_INVISIBLE); return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoEMPBurst(DSWActor* actor) { DSWActor* attachActor = actor->user.attachActor; if (attachActor != nullptr) { SetActorZ(actor, attachActor->spr.pos.plusZ(-actor->user.pos.Z)); actor->spr.Angles.Yaw = attachActor->spr.Angles.Yaw + DAngle180; } // not activated yet if (!(actor->user.Flags & SPR_ACTIVE)) { // activate it actor->user.WaitTics = SEC(7); actor->user.Flags |= (SPR_ACTIVE); } if (RandomRange(1000) > 500) { actor->spr.scale = DVector2(0.8125, 0.15626); } else { actor->spr.scale = DVector2(0.125, 0.59375); } if ((RANDOM_P2(1024<<6)>>6) < 700) { SpawnShrapX(actor); } actor->user.WaitTics -= (MISSILEMOVETICS * 2); if (actor->user.WaitTics < 0) { // Spawn a big radius burst of sparks here and check for final damage amount KillActor(actor); return false; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoTankShell(DSWActor* actor) { short i; for (i = 0; i < 4; i++) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnTankShellExp(actor); KillActor(actor); return true; } } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoTracerStart(DSWActor* actor) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoLaser(DSWActor* actor) { short spawn_count = 0; if (SW_SHAREWARE) return false; // JBF: verify while (true) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnBoltExp(actor); KillActor(actor); return true; } } spawn_count++; if (spawn_count < 256) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_LaserPuff, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.25, 0.25); actorNew->spr.pal = actorNew->user.spal = PALETTE_RED_LIGHTING; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.change.Zero(); } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoLaserStart(DSWActor* actor) { if (SW_SHAREWARE) return false; // JBF: verify actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnBoltExp(actor); KillActor(actor); return true; } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoRail(DSWActor* actor) { short spawn_count = 0; if (SW_SHAREWARE) return false; // JBF: verify while (true) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor) && actor->user.coll.type != kHitNone) // beware of side effects of WeaponMoveHit! { if (actor->user.coll.type == kHitSprite) { auto hitActor = actor->user.coll.actor(); if (hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY) { auto cstat_save = hitActor->spr.cstat; hitActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE); DoRail(actor); hitActor->spr.cstat = cstat_save; return true; } else { SpawnTracerExp(actor); SpawnShrapX(actor); KillActor(actor); return true; } } else { SpawnTracerExp(actor); SpawnShrapX(actor); KillActor(actor); return true; } } } spawn_count++; if (spawn_count < 128) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, &s_RailPuff[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 1.25); actorNew->vel.X += RandomRangeF(140 / 16.) - RandomRangeF(140 / 16.); actorNew->vel.Z += RandomRangeF(140 / 256.) - RandomRangeF(140 / 256.); actorNew->user.RotNum = 5; NewStateGroup(actorNew, sg_RailPuff); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.15625, 0.15625); actorNew->opos = actor->opos; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.change = actor->user.change; ScaleSpriteVector(actorNew, 1500); if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoRailStart(DSWActor* actor) { if (SW_SHAREWARE) return false; // JBF: verify actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnTracerExp(actor); SpawnShrapX(actor); KillActor(actor); return true; } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoRocket(DSWActor* actor) { if ((actor->user.FlagOwner -= ACTORMOVETICS)<=0 && actor->user.spal == 20) { double dist = (actor->spr.pos.XY() - actor->user.targetActor->spr.pos.XY()).Length(); actor->user.FlagOwner = int(dist * 0.25); // Special warn sound attached to each seeker spawned PlaySound(DIGI_MINEBEEP, actor, v3df_follow); } if (actor->user.Flags & (SPR_FIND_PLAYER)) { VectorMissileSeek(actor, 30, 16, DAngle22_5, DAngle90 + DAngle45); } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.Flags & (SPR_SUICIDE)) return true; if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor) && actor->user.coll.type != kHitNone) { if (actor->user.ID == BOLT_THINMAN_R4) { SpawnBunnyExp(actor); } else if (actor->user.Radius == NUKE_RADIUS) SpawnNuclearExp(actor); else SpawnBoltExp(actor); KillActor(actor); return true; } } if (!actor->user.Counter) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_Puff, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 6.25); SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->opos = actor->opos; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.change = actor->user.change; ScaleSpriteVector(actorNew, 20000); if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMicroMini(DSWActor* actor) { short i; for (i = 0; i < 3; i++) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnMicroExp(actor); KillActor(actor); return true; } } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnExtraMicroMini(DSWActor* actor) { auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, actor->vel.X); SetOwner(GetOwner(actor), actorNew); actorNew->spr.scale = DVector2(actor->spr.scale.X, actor->spr.scale.X); actorNew->spr.shade = actor->spr.shade; actorNew->copy_clipdist(actor); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_MicroMini[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = actor->user.Radius; actorNew->user.ceiling_dist = actor->user.ceiling_dist; actorNew->user.floor_dist = actor->user.floor_dist; actorNew->spr.cstat = actor->spr.cstat; actorNew->spr.Angles.Yaw += RandomAngle(11.25) - DAngle22_5/2; actorNew->vel.Z = -actor->vel.Z; actorNew->vel.Z += RandomRangeF(16) - 8; UpdateChange(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMicro(DSWActor* actor) { if (SW_SHAREWARE) return false; // JBF: verify actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (!actor->user.Counter) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_Puff, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 6.25); SetOwner(GetOwner(actor), actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.34375, 0.3125); actorNew->opos = actor->opos; actorNew->vel.Z = -actor->vel.Z; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.change = actor->user.change; ScaleSpriteVector(actorNew, 20000); if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); // last smoke if ((actor->user.WaitTics -= MISSILEMOVETICS) <= 0) { SetActorZ(actorNew, actorNew->spr.pos); NewStateGroup(actor, &sg_MicroMini[0]); actor->spr.scale = DVector2(0.15625, 0.15625); actor->spr.cstat &= ~(CSTAT_SPRITE_INVISIBLE); SpawnExtraMicroMini(actor); return true; } } // hit something if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnMicroExp(actor); KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoUziBullet(DSWActor* actor) { // call move_sprite twice for each movement // otherwize the moves are in too big an increment for (int i = 0; i < 2; i++) { auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X * 0.5; double daz = actor->vel.Z * 0.5; auto spos = actor->spr.pos.XY(); actor->user.coll = move_missile(actor, DVector3(vec, daz), actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); actor->user.Dist += (spos - actor->spr.pos.XY()). Length(); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 128) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { WeaponMoveHit(actor); auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT, UZI_SMOKE_REPEAT); SetOwner(GetOwner(actor), actorNew); actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 8; actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); if (!(actor->user.Flags & SPR_UNDERWATER)) { actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, actorNew->sector(), actorNew->spr.pos, nullAngle, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT, UZI_SPARK_REPEAT); SetOwner(GetOwner(actor), actorNew); actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); } KillActor(actor); return true; } else if (actor->user.Dist > 500) { KillActor(actor); return 0; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBoltSeeker(DSWActor* actor) { MissileSeek(actor, 30, DAngle90 + DAngle45/*, 4, 48, 6*/); DoBlurExtend(actor, 0, 4); auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X; double daz = actor->vel.Z; actor->user.coll = move_missile(actor, DVector3(vec, daz), CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnBoltExp(actor); KillActor(actor); return true; } } return false; } int DoBoltShrapnel(DSWActor* actor) { return 0; } int DoBoltFatMan(DSWActor* actor) { return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoElectro(DSWActor* actor) { DoBlurExtend(actor, 0, 4); // only seek on Electro's after a hit on an actor if (actor->user.Counter > 0) MissileSeek(actor, 30, DAngle90/*, 3, 52, 2*/); auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X; double daz = actor->vel.Z; actor->user.coll = move_missile(actor, DVector3(vec, daz), CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.Flags & (SPR_SUICIDE)) return true; if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { switch (actor->user.coll.type) { case kHitSprite: { auto hitActor = actor->user.coll.actor(); if (!(hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY) || hitActor->user.ID == SKULL_R0 || hitActor->user.ID == BETTY_R0) SpawnShrap(actor, nullptr); break; } default: SpawnShrap(actor, nullptr); break; } KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoLavaBoulder(DSWActor* actor) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.Flags & (SPR_SUICIDE)) return true; if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnShrap(actor, nullptr); KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoSpear(DSWActor* actor) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.Flags & (SPR_SUICIDE)) return true; if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnCoolieExp(DSWActor* actor) { ASSERT(actor->hasU()); actor->user.Counter = RandomRange(120); // This is the wait til birth time! auto vect = actor->spr.pos + (actor->spr.Angles.Yaw + DAngle180).ToVector() * 4; vect.Z -= ActorSizeZ(actor) * 0.75; PlaySound(DIGI_COOLIEEXPLODE, actor, v3df_none); auto actorNew = SpawnActor(STAT_MISSILE, BOLT_EXP, s_BoltExp, actor->sector(), vect, actor->spr.Angles.Yaw, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Radius = DamageData[DMG_BOLT_EXP].radius; DoExpDamageTest(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnFireballFlames(DSWActor* actor, DSWActor* enemyActor) { if (actor->user.Flags & (SPR_UNDERWATER)) return; if (enemyActor != nullptr) { // test for already burned if ((enemyActor->spr.extra & SPRX_BURNABLE) && enemyActor->spr.shade > 40) return; if (!enemyActor->hasU()) { ASSERT(true == false); return; } auto flameActor = enemyActor->user.flameActor; if (flameActor != nullptr) { double sizez = ActorSizeZ(enemyActor) + (ActorSizeZ(enemyActor) * 0.25); if ((enemyActor->spr.extra & SPRX_BURNABLE)) return; if (flameActor->user.Counter >= GetRepeatFromHeight(flameActor, sizez)) { // keep flame only slightly bigger than the enemy itself flameActor->user.Counter = GetRepeatFromHeight(flameActor, sizez); } else { //increase max size flameActor->user.Counter += GetRepeatFromHeight(flameActor, 8); } // Counter is max size if (flameActor->user.Counter >= 230) { // this is far too big flameActor->user.Counter = 230; } if (flameActor->user.WaitTics < 2*120) flameActor->user.WaitTics = 2*120; // allow it to grow again return; } } auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL_FLAMES, s_FireballFlames, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness if (enemyActor != nullptr) enemyActor->user.flameActor = actorNew; actorNew->spr.scale = DVector2(0.25, 0.25); if (enemyActor != nullptr) { // large flame for trees and such if ((enemyActor->spr.extra & SPRX_BURNABLE)) { double sizez = ActorSizeZ(enemyActor) + (ActorSizeZ(enemyActor) * 0.25); actorNew->user.Counter = GetRepeatFromHeight(actorNew, sizez); } else { actorNew->user.Counter = GetRepeatFromHeight(actorNew, ActorSizeZ(enemyActor) * 0.5); } } else { actorNew->user.Counter = 48; // max flame size } SetOwner(GetOwner(actor), actorNew); actorNew->spr.shade = -40; actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); //actorNew->user.Radius = DamageData[DMG_FIREBALL_FLAMES].radius; actorNew->user.Radius = 200; if (enemyActor != nullptr) { SetAttach(enemyActor, actorNew); } else { if (TestDontStickSector(actorNew->sector())) { KillActor(actorNew); return; } actorNew->user.floor_dist = actorNew->user.ceiling_dist = 0; DoFindGround(actorNew); actorNew->user.jump_speed = 0; DoBeginJump(actorNew); } PlaySound(DIGI_FIRE1,actorNew,v3df_dontpan|v3df_doppler); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnBreakFlames(DSWActor* actor) { auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL_FLAMES+1, s_BreakFlames, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.25, 0.25); actorNew->user.Counter = 48; // max flame size actorNew->spr.shade = -40; if (actor->hasU()) actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Radius = 200; actorNew->user.floor_dist = actorNew->user.ceiling_dist = 0; DoFindGround(actorNew); actorNew->user.jump_speed = 0; DoBeginJump(actorNew); PlaySound(DIGI_FIRE1,actorNew,v3df_dontpan|v3df_doppler); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnBreakStaticFlames(DSWActor* actor) { auto actorNew = SpawnActor(STAT_STATIC_FIRE, FIREBALL_FLAMES, nullptr, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); if (RandomRange(1000) > 500) actorNew->spr.picnum = 3143; else actorNew->spr.picnum = 3157; actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -40; actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Radius = 200; actorNew->user.floor_dist = actorNew->user.ceiling_dist = 0; actorNew->spr.pos.Z = getflorzofslopeptr(actorNew->sector(), actorNew->spr.pos); PlaySound(DIGI_FIRE1,actorNew,v3df_dontpan|v3df_doppler); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnFireballExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->user.Flags & (SPR_SUICIDE)) return; PlaySound(DIGI_SMALLEXP, actor, v3df_none); auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL_EXP, s_FireballExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.8125, 0.8125); SetOwner(GetOwner(actor), actorNew); actorNew->spr.shade = -40; actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.Flags |= (actor->user.Flags & (SPR_UNDERWATER)); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); // // All this stuff assures that explosions do not go into floors & // ceilings // SpawnExpZadjust(actor, actorNew, 15, 15); if (RANDOM_P2(1024) < 150) SpawnFireballFlames(actorNew, nullptr); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnGoroFireballExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->user.Flags & (SPR_SUICIDE)) return; PlaySound(DIGI_MEDIUMEXP, actor, v3df_none); auto actorNew = SpawnActor(STAT_MISSILE, 0, s_FireballExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.25, 0.25); SetOwner(GetOwner(actor), actorNew); actorNew->spr.shade = -40; actorNew->spr.pal = actorNew->user.spal = actor->user.spal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); // // All this stuff assures that explosions do not go into floors & // ceilings // SpawnExpZadjust(actor, actorNew, 15, 15); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnBoltExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return; PlaySound(DIGI_BOLTEXPLODE, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, BOLT_EXP, s_BoltExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.1825, 1.1825); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); expActor->user.Radius = DamageData[DMG_BOLT_EXP].radius; SpawnExpZadjust(actor, expActor, 40, 40); DoExpDamageTest(expActor); SetExpQuake(actor); // !JIM! made rocket launcher shake things SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnBunnyExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return -1; PlaySound(DIGI_BUNNYDIE3, actor, v3df_none); actor->user.ID = BOLT_EXP; // Change id InitBloodSpray(actor, true, -1); InitBloodSpray(actor, true, -1); InitBloodSpray(actor, true, -1); DoExpDamageTest(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnTankShellExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return; PlaySound(DIGI_BOLTEXPLODE, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, TANK_SHELL_EXP, s_TankShellExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.5, 1.5); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); expActor->user.Radius = DamageData[DMG_TANK_SHELL_EXP].radius; SpawnExpZadjust(actor, expActor, 40, 40); DoExpDamageTest(expActor); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); } void SpawnNuclearSecondaryExp(DSWActor* actor, DAngle ang) { ASSERT(actor->hasU()); auto expActor = SpawnActor(STAT_MISSILE, GRENADE_EXP, s_GrenadeExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 32); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -128; expActor->spr.scale = DVector2(3.40625, 2.375); expActor->copy_clipdist(actor); expActor->user.ceiling_dist = (16); expActor->user.floor_dist = (16); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); //ang = RANDOM_P2(2048); double const vel = (128+8) + RandomRangeF(128); expActor->user.change.XY() = ang.ToVector() * vel; expActor->user.Radius = 200; // was NUKE_RADIUS expActor->user.coll = move_missile(expActor, DVector3(expActor->user.change.XY(), 0), expActor->user.ceiling_dist,expActor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); if ((expActor->spr.pos - actor->spr.pos).Length() < 64) { KillActor(expActor); return; } SpawnExpZadjust(actor, expActor, 50, 10); InitChemBomb(expActor); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnNuclearExp(DSWActor* actor) { DAngle ang=nullAngle; PLAYER* pp = nullptr; short rnd_rng; ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return; PlaySound(DIGI_NUCLEAREXP, actor, v3df_dontpan | v3df_doppler); auto own = GetOwner(actor); if (own && own->hasU()) { pp = own->user.PlayerP; rnd_rng = RandomRange(1000); if (rnd_rng > 990) PlayerSound(DIGI_LIKEHIROSHIMA, v3df_follow|v3df_dontpan,pp); else if (rnd_rng > 980) PlayerSound(DIGI_LIKENAGASAKI, v3df_follow|v3df_dontpan,pp); else if (rnd_rng > 970) PlayerSound(DIGI_LIKEPEARL, v3df_follow|v3df_dontpan,pp); } // Spawn big mushroom cloud auto expActor = SpawnActor(STAT_MISSILE, MUSHROOM_CLOUD, s_NukeMushroom, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(own, expActor); expActor->spr.shade = -128; expActor->spr.scale = DVector2(4, 4); expActor->copy_clipdist(actor); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.spal = expActor->spr.pal = PALETTE_PLAYER1; // Set nuke puff to gray InitChemBomb(expActor); // Do central explosion expActor = SpawnActor(STAT_MISSILE, MUSHROOM_CLOUD, s_GrenadeExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); SetOwner(own, expActor); expActor->spr.shade = -128; expActor->spr.scale = DVector2(3.40625, 2.375); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); expActor->user.Radius = NUKE_RADIUS; SpawnExpZadjust(actor, expActor, 30, 30); DoExpDamageTest(expActor); // Nuclear effects SetNuclearQuake(actor); SetFadeAmt(pp, -80, 1); // Nuclear flash // Secondary blasts ang = RandomAngle(); SpawnNuclearSecondaryExp(expActor, ang); ang = ang + DAngle90 + RandomAngle(45); SpawnNuclearSecondaryExp(expActor, ang); ang = ang + DAngle90 + RandomAngle(45); SpawnNuclearSecondaryExp(expActor, ang); ang = ang + DAngle90 + RandomAngle(45); SpawnNuclearSecondaryExp(expActor, ang); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnTracerExp(DSWActor* actor) { DSWActor* expActor; ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return ; if (actor->user.ID == BOLT_THINMAN_R1) expActor = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R1, s_TracerExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); else expActor = SpawnActor(STAT_MISSILE, TRACER_EXP, s_TracerExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(0.0625, 0.0625); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (actor->user.ID == BOLT_THINMAN_R1) { expActor->user.Radius = DamageData[DMG_BASIC_EXP].radius; DoExpDamageTest(expActor); } else expActor->user.Radius = DamageData[DMG_BOLT_EXP].radius; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnMicroExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return ; auto expActor = SpawnActor(STAT_MISSILE, MICRO_EXP, s_MicroExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(0.5, 0.5); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_YFLIP); expActor->user.Radius = DamageData[DMG_BOLT_EXP].radius; // // All this stuff assures that explosions do not go into floors & // ceilings // SpawnExpZadjust(actor, expActor, 20, 20); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AddSpriteToSectorObject(DSWActor* actor, SECTOR_OBJECT* sop) { unsigned sn; // make sure it has a user if (!actor->hasU()) { SpawnUser(actor, 0, nullptr); } // find a free place on this list for (sn = 0; sn < SIZ(sop->so_actors); sn++) { if (sop->so_actors[sn] == nullptr) break; } if (sn >= SIZ(sop->so_actors) - 1) return; sop->so_actors[sn] = actor; so_setspriteinterpolation(sop, actor); actor->user.Flags |= (SPR_ON_SO_SECTOR|SPR_SO_ATTACHED); actor->user.pos.XY() = sop->pmid.XY() - actor->spr.pos.XY(); actor->user.pos.Z = sop->mid_sector->floorz - actor->spr.pos.Z; actor->user.sang = actor->spr.Angles.Yaw; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnBigGunFlames(DSWActor* actor, DSWActor* Operator, SECTOR_OBJECT* sop, bool smallflames) { unsigned sn; auto expActor = SpawnActor(STAT_MISSILE, MICRO_EXP, s_BigGunFlame, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(Operator, expActor); expActor->spr.shade = -40; if (smallflames) { expActor->spr.scale = DVector2(0.1875, 0.1875); } else { expActor->spr.scale = DVector2(0.53125, 0.53125); } expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) > 512) expActor->spr.cstat |= (CSTAT_SPRITE_YFLIP); // place all sprites on list for (sn = 0; sn < SIZ(sop->so_actors); sn++) { if (sop->so_actors[sn] == nullptr) break; } if (sn >= SIZ(sop->so_actors) - 1) return; sop->so_actors[sn] = expActor; so_setspriteinterpolation(sop, expActor); expActor->user.Flags |= (actor->user.Flags & (SPR_ON_SO_SECTOR|SPR_SO_ATTACHED)); if (actor->user.Flags & (SPR_ON_SO_SECTOR)) { // move with sector its on expActor->spr.pos.Z = actor->sector()->floorz - actor->user.pos.Z; expActor->backupz(); } else { // move with the mid sector expActor->spr.pos.Z = sop->mid_sector->floorz - actor->user.pos.Z; expActor->backupz(); } expActor->user.pos = actor->user.pos; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnGrenadeSecondaryExp(DSWActor* actor, DAngle ang) { ASSERT(actor->hasU()); auto expActor = SpawnActor(STAT_MISSILE, GRENADE_EXP, s_GrenadeSmallExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 64); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(0.5, 0.5); expActor->copy_clipdist(actor); expActor->user.ceiling_dist = (16); expActor->user.floor_dist = (16); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); //ang = RANDOM_P2(2048); double vel = (96) + RandomRangeF(64); expActor->user.change.XY() = ang.ToVector() * vel; expActor->user.coll = move_missile(expActor, DVector3(expActor->user.change.XY(), 0), expActor->user.ceiling_dist,expActor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); if ((expActor->spr.pos - actor->spr.pos).Length() < 64) { KillActor(expActor); return; } SpawnExpZadjust(actor, expActor, 50, 10); expActor->backuppos(); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnGrenadeSmallExp(DSWActor* actor) { SpawnGrenadeSecondaryExp(actor, RandomAngle()); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnGrenadeExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return; PlaySound(DIGI_30MMEXPLODE, actor, v3df_none); if (RandomRange(1000) > 990) { auto own = GetOwner(actor); if (own != nullptr && own->hasU() && own->user.PlayerP) { PlayerSound(DIGI_LIKEFIREWORKS, v3df_follow|v3df_dontpan, own->user.PlayerP); } } auto pos = actor->spr.pos; if (actor->user.ID == ZILLA_RUN_R0) { pos.X += (RandomRange(1000)-RandomRange(1000)) * maptoworld; pos.Y += (RandomRange(1000) - RandomRange(1000)) * maptoworld; pos.Z = ActorZOfMiddle(actor) + (RandomRange(1000)-RandomRange(1000)) * zmaptoworld; } auto expActor = SpawnActor(STAT_MISSILE, GRENADE_EXP, s_GrenadeExp, actor->sector(), pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.5, 1.5); expActor->copy_clipdist(actor); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_GRENADE_EXP].radius; // // All this stuff assures that explosions do not go into floors & // ceilings // SpawnExpZadjust(actor, expActor, 100, 30); DoExpDamageTest(expActor); SetExpQuake(expActor); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 0); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnExpZadjust(DSWActor* actor, DSWActor* expActor, double upper_zsize, double lower_zsize) { double tos_z, bos_z; ASSERT(expActor->hasU()); if (actor->hasU()) { tos_z = expActor->spr.pos.Z - upper_zsize; bos_z = expActor->spr.pos.Z + lower_zsize; if (tos_z <= actor->user.hiz + 4) { expActor->spr.pos.Z = actor->user.hiz + upper_zsize; expActor->spr.cstat |= (CSTAT_SPRITE_YFLIP); } else if (bos_z > actor->user.loz) { expActor->spr.pos.Z = actor->user.loz - lower_zsize; } } else { double cz,fz; calcSlope(expActor->sector(), expActor->spr.pos, &cz, &fz); tos_z = expActor->spr.pos.Z - upper_zsize; bos_z = expActor->spr.pos.Z + lower_zsize; if (tos_z <= cz + 4) { expActor->spr.pos.Z = cz + upper_zsize; expActor->spr.cstat |= (CSTAT_SPRITE_YFLIP); } else if (bos_z > fz) { expActor->spr.pos.Z = fz - lower_zsize; } } expActor->backupz(); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnMineExp(DSWActor* actor) { ASSERT(actor->hasU()); if (actor->hasU() && (actor->user.Flags & SPR_SUICIDE)) return; change_actor_stat(actor, STAT_MISSILE); PlaySound(DIGI_MINEBLOW, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, MINE_EXP, s_MineExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(GetOwner(actor), expActor); expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.6875, 1.6875); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_MINE_EXP].radius; // // All this stuff assures that explosions do not go into floors & // ceilings // SpawnExpZadjust(actor, expActor, 100, 20); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); SetExpQuake(expActor); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMineExp(DSWActor* actor) { DoExpDamageTest(actor); return 0; } int DoSectorExp(DSWActor* actor) { actor->spr.pos += actor->user.change.XY(); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* SpawnSectorExp(DSWActor* actor) { short explosion; ASSERT(actor->hasU()); if (actor->user.Flags & (SPR_SUICIDE)) return nullptr; PlaySound(DIGI_30MMEXPLODE, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, GRENADE_EXP, s_SectorExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.40625, 1.40625); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_SECTOR_EXP].radius; DoExpDamageTest(expActor); SetExpQuake(expActor); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); return expActor; } // called from SpawnShrap DSWActor* SpawnLargeExp(DSWActor* actor) { PlaySound(DIGI_30MMEXPLODE, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, GRENADE_EXP, s_SectorExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness expActor->spr.shade = -40; expActor->spr.scale = DVector2(1.40625, 1.40625); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_SECTOR_EXP].radius; SpawnExpZadjust(actor, expActor, 50, 50); // Should not cause other sectors to explode DoExpDamageTest(expActor); SetExpQuake(expActor); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); return expActor; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnMeteorExp(DSWActor* actor) { DSWActor* expActor; ASSERT(actor->hasU()); if (actor->user.Flags & (SPR_SUICIDE)) return; if (actor->user.spal == 25) // Serp ball { expActor = SpawnActor(STAT_MISSILE, METEOR_EXP, s_TeleportEffect2, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); } else { PlaySound(DIGI_MEDIUMEXP, actor, v3df_none); expActor = SpawnActor(STAT_MISSILE, METEOR_EXP, s_MeteorExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); } expActor->spr.hitag = LUMINOUS; //Always full brightness expActor->spr.shade = -40; if (actor->spr.scale.Y < 1) { // small expActor->spr.scale = DVector2(1, 1); } else { // large - boss expActor->spr.scale = DVector2(1.25, 1.25); } expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_BASIC_EXP].radius; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnLittleExp(DSWActor* actor) { short explosion; PlaySound(DIGI_HEADSHOTHIT, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, BOLT_EXP, s_SectorExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness expActor->spr.shade = -127; expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_BASIC_EXP].radius; DoExpDamageTest(expActor); SpawnVis(nullptr, expActor->sector(), expActor->spr.pos, 16); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFireball(DSWActor* actor) { if (actor->user.Flags & (SPR_UNDERWATER)) { actor->spr.scale.X -= REPEAT_SCALE; actor->spr.scale.Y -= REPEAT_SCALE; if (actor->spr.scale.X <= 0.578125) { SpawnSmokePuff(actor); KillActor(actor); return true; } } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.coll.type != kHitNone) { bool hit_burn = false; if (WeaponMoveHit(actor)) { switch (actor->user.coll.type) { case kHitSprite: { auto hitActor = actor->user.coll.actor(); if ((hitActor->spr.extra & SPRX_BURNABLE)) { if (!hitActor->hasU()) SpawnUser(hitActor, hitActor->spr.picnum, nullptr); SpawnFireballFlames(actor, hitActor); hit_burn = true; } break; } } if (!hit_burn) { if (actor->user.ID == GORO_FIREBALL) SpawnGoroFireballExp(actor); else SpawnFireballExp(actor); } KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFindGround(DSWActor* actor) { Collision ceilhit, florhit; // recursive routine to find the ground - either sector or floor sprite // skips over enemy and other types of sprites auto save_cstat = actor->spr.cstat; actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); FAFgetzrange(actor->spr.pos, actor->sector(), &actor->user.hiz, &ceilhit, &actor->user.loz, &florhit, actor->clipdist - GETZRANGE_CLIP_ADJ, CLIPMASK_PLAYER); actor->spr.cstat = save_cstat; switch (florhit.type) { case kHitSprite: { auto florActor = florhit.actor(); if ((florActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR)) { // found a sprite floor actor->user.lowActor = florActor; actor->user.lo_sectp = nullptr; return true; } else { // reset the blocking bit of what you hit and try again - // recursive auto bak_cstat = florActor->spr.cstat; florActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); DoFindGround(actor); florActor->spr.cstat = bak_cstat; } return false; } case kHitSector: { actor->user.lo_sectp = florhit.hitSector; actor->user.lowActor = nullptr; return true; } default: ASSERT(true == false); break; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFindGroundPoint(DSWActor* actor) { Collision ceilhit, florhit; // recursive routine to find the ground - either sector or floor sprite // skips over enemy and other types of sprites auto save_cstat = actor->spr.cstat; actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); FAFgetzrangepoint(actor->spr.pos, actor->sector(), &actor->user.hiz, &ceilhit, &actor->user.loz, &florhit); actor->spr.cstat = save_cstat; switch (florhit.type) { case kHitSprite: { auto florActor = florhit.actor(); if ((florActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR)) { // found a sprite floor actor->user.lowActor = florActor; actor->user.lo_sectp = nullptr; return true; } else { // reset the blocking bit of what you hit and try again - // recursive auto bak_cstat = florActor->spr.cstat; florActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); DoFindGroundPoint(actor); florActor->spr.cstat = bak_cstat; } return false; } case kHitSector: { actor->user.lo_sectp = florhit.hitSector; actor->user.lowActor = nullptr; return true; } default: ASSERT(true == false); break; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoNapalm(DSWActor* actor) { DoBlurExtend(actor, 1, 7); if (actor->user.Flags & (SPR_UNDERWATER)) { actor->spr.scale.X -= REPEAT_SCALE; actor->spr.scale.Y -= REPEAT_SCALE; if (actor->spr.scale.X <= 0.46875) { SpawnSmokePuff(actor); KillActor(actor); return true; } } auto oldv = actor->spr.pos; actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); { // this sprite is suPlayerosed to go through players/enemys // if hit a player/enemy back up and do it again with blocking reset if (actor->user.coll.type == kHitSprite) { auto hitActor = actor->user.coll.actor(); if ((hitActor->spr.cstat & CSTAT_SPRITE_BLOCK) && !(hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { auto hcstat = hitActor->spr.cstat; actor->spr.pos = oldv; hitActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); hitActor->spr.cstat = hcstat; } } } actor->user.Counter++; if (actor->user.Counter > 2) actor->user.Counter = 0; if (!actor->user.Counter) { PlaySound(DIGI_NAPPUFF, actor, v3df_none); auto expActor = SpawnActor(STAT_MISSILE, NAP_EXP, s_NapExp, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness SetOwner(actor, expActor); expActor->spr.shade = -40; expActor->spr.cstat = actor->spr.cstat; expActor->spr.scale = DVector2(0.75, 1); expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); if (RANDOM_P2(1024) < 512) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); expActor->spr.cstat &= ~(CSTAT_SPRITE_TRANSLUCENT); expActor->user.Radius = 1500; DoFindGroundPoint(expActor); MissileWaterAdjust(expActor); expActor->spr.pos.Z = expActor->user.loz; expActor->backupz(); if (actor->user.Flags & (SPR_UNDERWATER)) expActor->user.Flags |= SPR_UNDERWATER; ASSERT(expActor->spr.picnum == 3072); ASSERT(expActor->user.Tics == 0); } if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBloodWorm(DSWActor* actor) { actor->user.coll = move_ground_missile(actor, actor->user.change.XY(), actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); if (actor->user.coll.type != kHitNone) { actor->user.change.XY() = -actor->user.change.XY(); actor->user.coll.setNone(); actor->spr.Angles.Yaw += DAngle180; return true; } MissileHitDiveArea(actor); if (!actor->user.z_tgt) { // stay alive for 10 seconds if (++actor->user.Counter3 > 3) { InitBloodSpray(actor, false, 1); InitBloodSpray(actor, false, 1); InitBloodSpray(actor, false, 1); // Kill any old zombies you own SWStatIterator it(STAT_ENEMY); while (auto itActor = it.Next()) { if (!itActor->hasU()) continue; if (itActor->user.ID == ZOMBIE_RUN_R0 && GetOwner(itActor) == GetOwner(actor)) { InitBloodSpray(itActor, true, 105); InitBloodSpray(itActor, true, 105); SetSuicide(itActor); break; } } SpawnZombie2(actor); KillActor(actor); return true; } } DAngle ang = actor->spr.Angles.Yaw + DAngle90; double amt = (RandomRangeF(128) - 64) * 0.5; auto bpos = actor->spr.pos.XY(); actor->spr.pos += ang.ToVector() * 1024 * amt; auto sect = actor->sector(); updatesectorz(actor->spr.pos, §); if (sect) { GlobalSkipZrange = true; InitBloodSpray(actor, false, 1); GlobalSkipZrange = false; } actor->spr.pos.XY() = bpos; return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMeteor(DSWActor* actor) { return false; } int DoSerpMeteor(DSWActor* actor) { auto oldv = actor->spr.pos; actor->spr.scale.X += (MISSILEMOVETICS * 2 * REPEAT_SCALE); if (actor->spr.scale.X > 1.25) actor->spr.scale.X = (1.25); actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); if (actor->user.coll.type != kHitNone) { // this sprite is supposed to go through players/enemys // if hit a player/enemy back up and do it again with blocking reset if (actor->user.coll.type == kHitSprite) { auto hitActor = actor->user.coll.actor(); if (hitActor->hasU() && hitActor->user.ID >= SKULL_R0 && hitActor->user.ID <= SKULL_SERP) { auto hcstat = hitActor->spr.cstat; actor->spr.pos = oldv; hitActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); hitActor->spr.cstat = hcstat; } } if (WeaponMoveHit(actor)) { SpawnMeteorExp(actor); KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMirvMissile(DSWActor* actor) { actor->spr.scale.X += (MISSILEMOVETICS * 2 * REPEAT_SCALE); if (actor->spr.scale.X > 1.25) actor->spr.scale.X = (1.25); actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); if (actor->user.coll.type != kHitNone) { if (WeaponMoveHit(actor)) { SpawnMeteorExp(actor); KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoMirv(DSWActor* actor) { actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS); MissileHitDiveArea(actor); if (actor->user.Flags & (SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256) SpawnBubble(actor); actor->user.Counter++; actor->user.Counter &= 1; if (!actor->user.Counter) { int i; static const DAngle angs[] = { DAngle90, -DAngle90 }; for (i = 0; i < 2; i++) { auto actorNew = SpawnActor(STAT_MISSILE, MIRV_METEOR, &sg_MirvMeteor[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw + angs[i], 50); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_MirvMeteor[0]); actorNew->user.StateEnd = s_MirvMeteorExp; SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->clipdist = 2; actorNew->vel.Z = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.ceiling_dist = (16); actorNew->user.floor_dist = (16); actorNew->user.Dist = 12.5; //actorNew->user.Dist = 0; UpdateChange(actorNew); if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } } if (actor->user.coll.type != kHitNone) { SpawnMeteorExp(actor); KillActor(actor); return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool MissileSetPos(DSWActor* actor, ANIMATOR* DoWeapon, int dist) { bool retval = false; // backup values auto oldc = actor->user.change; double oldvel = actor->vel.X; double oldzvel = actor->vel.Z; // make missile move in smaller increments actor->vel.X = dist * (6. / MISSILEMOVETICS) * inttoworld; // not going to change 36 calls for this... actor->vel.Z *= (6. / MISSILEMOVETICS); // some Weapon Animators use this UpdateChange(actor); actor->user.Flags |= (SPR_SET_POS_DONT_KILL); if ((*DoWeapon)(actor)) retval = true; actor->user.Flags &= ~(SPR_SET_POS_DONT_KILL); // reset values actor->user.change = oldc; actor->vel.X = oldvel; actor->vel.Z = oldzvel; // update for interpolation actor->backuppos(); return retval; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool TestMissileSetPos(DSWActor* actor, ANIMATOR* DoWeapon, int dist, double zvel) { bool retval = false; // backup values auto oldc = actor->user.change; double oldvel = actor->vel.X; double oldzvel = actor->vel.Z; // make missile move in smaller increments actor->vel.X = dist * ( 3. / 8. / MISSILEMOVETICS); zvel *= (6. / MISSILEMOVETICS); // some Weapon Animators use this UpdateChangeXY(actor); actor->user.change.Z = zvel; actor->user.Flags |= (SPR_SET_POS_DONT_KILL); if ((*DoWeapon)(actor)) retval = true; actor->user.Flags &= ~(SPR_SET_POS_DONT_KILL); // reset values actor->user.change = oldc; actor->vel.X = oldvel; actor->vel.Z = oldzvel; // update for interpolation actor->backuppos(); return retval; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- constexpr int RINGMOVETICS = (MISSILEMOVETICS * 2); constexpr double RING_OUTER_DIST = 200; constexpr double RING_INNER_DIST = 50; int DoRing(DSWActor* actor) { auto own = GetOwner(actor); if (!own) return 0; // this would crash. PLAYER* pp = own->user.PlayerP; double cz,fz; if (actor->user.Flags & (SPR_UNDERWATER)) { actor->spr.scale.X -= REPEAT_SCALE; actor->spr.scale.Y -= REPEAT_SCALE; if (actor->spr.scale.X <= 0.46875) { SpawnSmokePuff(actor); KillActor(actor); return true; } } double z; // move the center with the player if (pp) z = pp->actor->getOffsetZ() + 20; else z = ActorZOfMiddle(own) + 30; actor->spr.pos = DVector3(own->spr.pos.XY(), z); // go out until its time to come back in if (actor->user.Counter2 == false) { actor->user.Dist += 0.5 * RINGMOVETICS; if (actor->user.Dist > RING_OUTER_DIST) { actor->user.Counter2 = true; } } else { actor->user.Dist -= 0.5 * RINGMOVETICS; if (actor->user.Dist <= RING_INNER_DIST) { if (!pp) own->user.Counter--; KillActor(actor); return 0; } } // rotate the ring actor->spr.Angles.Yaw += mapangle(5 * RINGMOVETICS); // put it out there actor->spr.pos += actor->spr.Angles.Yaw.ToVector() * actor->user.Dist; if (pp) actor->spr.pos.Z -= actor->user.Dist * pp->horizon.ZzHORIZON.Tan() * 2.; // horizon math sucks... SetActor(actor, actor->spr.pos); ASSERT(actor->insector()); calcSlope(actor->sector(), actor->spr.pos, &cz, &fz); // bound the sprite by the sectors ceiling and floor if (actor->spr.pos.Z > fz) { actor->spr.pos.Z = fz; } if (actor->spr.pos.Z < cz + ActorSizeZ(actor)) { actor->spr.pos.Z = (cz + ActorSizeZ(actor)); } // Done last - check for damage DoDamageTest(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitSpellRing(PLAYER* pp) { short missiles; short max_missiles = 16; short ammo; DAngle ang; ammo = NAPALM_MIN_AMMO; if (pp->WpnAmmo[WPN_HOTHEAD] < ammo) return; else PlayerUpdateAmmo(pp, WPN_HOTHEAD, -ammo); DAngle ang_diff = DAngle360 / max_missiles; DAngle ang_start = pp->angle.ang - DAngle180; if (!SW_SHAREWARE) PlaySound(DIGI_RFWIZ, pp, v3df_none); if (!pp->insector()) return; for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++) { auto actorNew = SpawnActor(STAT_MISSILE_SKIP4, FIREBALL1, s_Ring, pp->cursector, pp->actor->getPosWithOffsetZ(), ang, 0); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->vel.X = 31.25; SetOwner(pp->actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->vel.Z = 0; actorNew->user.pos.Z = 20; actorNew->user.Dist = RING_INNER_DIST; actorNew->user.Counter = max_missiles; actorNew->user.Counter2 = 0; actorNew->user.ceiling_dist = 10; actorNew->user.floor_dist = 10; // put it out there actorNew->spr.pos += actorNew->spr.Angles.Yaw.ToVector() * actorNew->user.Dist; actorNew->spr.pos.Z += pp->actor->getOffsetZ() + 20 - (actorNew->user.Dist * pp->horizon.ZzHORIZON.Tan() * 2.); // horizon math sucks... actorNew->spr.Angles.Yaw += DAngle90; actorNew->backuppos(); if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoSerpRing(DSWActor* actor) { double cz,fz; auto own = GetOwner(actor); // if Owner does not exist or he's dead on the floor // kill off all of his skull children if (own == nullptr || own->user.RotNum < 5) { UpdateSinglePlayKills(actor); DoSkullBeginDeath(actor); // +2 does not spawn shrapnel actor->user.ID = SKULL_SERP; return 0; } double zz = actor->spr.pos.Z + actor->vel.Z; if (zz > own->spr.pos.Z - actor->user.pos.Z) zz = own->spr.pos.Z - actor->user.pos.Z; // move the center with the player actor->spr.pos = DVector3(own->spr.pos.XY(), zz); // go out until its time to come back in if (actor->user.Counter2 == false) { actor->user.Dist += 0.5 * RINGMOVETICS; if (actor->user.Dist > actor->user.TargetDist) actor->user.Counter2 = true; } // rotate the ring actor->user.slide_ang += mapangle(actor->spr.yint); // rotate the heads if (actor->user.Flags & (SPR_BOUNCE)) actor->spr.Angles.Yaw += mapangle(28 * RINGMOVETICS); else actor->spr.Angles.Yaw -= mapangle(28 * RINGMOVETICS); // put it out there actor->spr.pos += actor->user.slide_ang.ToVector() * actor->user.Dist; SetActor(actor, actor->spr.pos); ASSERT(actor->insector()); calcSlope(actor->sector(), actor->spr.pos, &cz, &fz); // bound the sprite by the sectors ceiling and floor if (actor->spr.pos.Z > fz) { actor->spr.pos.Z = fz; } if (actor->spr.pos.Z < cz + ActorSizeZ(actor)) { actor->spr.pos.Z = cz + ActorSizeZ(actor); } if (actor->user.Counter2 > 0) { DSWActor* tActor = own->user.targetActor; if (!tActor->hasU() || !tActor->user.PlayerP || !(tActor->user.PlayerP->Flags & PF_DEAD)) { actor->user.targetActor = own->user.targetActor; double dist = (actor->spr.pos.XY() - actor->user.targetActor->spr.pos.XY()).Length(); // if ((dist ok and random ok) OR very few skulls left) if ((dist < 1125 && (RANDOM_P2(2048<<5)>>5) < 16) || own->user.Counter < 4) { auto sect = actor->sector(); updatesector(actor->spr.pos, §); // if (valid sector and can see target) if (sect != nullptr && CanSeePlayer(actor)) { extern STATE* sg_SkullJump[]; actor->user.ID = SKULL_R0; actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); actor->vel.X = dist * (3. / 64) + RandomRangeF(16); actor->user.jump_speed = -800; change_actor_stat(actor, STAT_ENEMY); NewStateGroup(actor, sg_SkullJump); DoBeginJump(actor); return 0; } } } } return 0; } int InitLavaFlame(DSWActor* actor) { return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SetZVelFromTarget(DSWActor* actorNew, DSWActor* actor, bool setchange = false, double offset = 0) { // find the distance to the target (player) double dist = (actorNew->spr.pos.XY() - actor->user.targetActor->spr.pos.XY()).Length(); if (dist != 0) { double zdist = (ActorUpperZ(actor->user.targetActor) - actorNew->spr.pos.Z - offset) / dist; double change = zdist * actorNew->vel.X; actorNew->vel.Z = change; if (setchange) actorNew->user.change.Z = change; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitLavaThrow(DSWActor* actor) { short w; // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); double nz = ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, LAVA_BOULDER, s_LavaBoulder, actor->sector(), DVector3(actor->spr.pos.XY(), nz), actor->spr.Angles.Yaw, NINJA_BOLT_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(1.125, 1.125); actorNew->spr.shade = -15; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; if (RANDOM_P2(1024) > 512) actorNew->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) > 512) actorNew->spr.cstat |= (CSTAT_SPRITE_YFLIP); actorNew->user.Radius = 200; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 16; actorNew->user.ceiling_dist = (14); actorNew->user.floor_dist = (14); UpdateChange(actorNew); MissileSetPos(actorNew, DoLavaBoulder, 1200); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitVulcanBoulder(DSWActor* actor) { DAngle nang; double zsize; int zvel, zvel_rand; int delta; int vel; if (SP_TAG7(actor)) { delta = SP_TAG5(actor); nang = actor->spr.Angles.Yaw + mapangle((RandomRange(delta) - (delta >> 1))); } else { nang = RandomAngle(); } if (SP_TAG6(actor)) vel = SP_TAG6(actor); else vel = 800; // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, LAVA_BOULDER, s_VulcanBoulder, actor->sector(), actor->spr.pos.plusZ(-40), nang, ((vel/2 + vel/4) + RandomRange(vel/4)) / 16.); SetOwner(actor, actorNew); double scale = 0.125 + RandomRange(72) * REPEAT_SCALE; actorNew->spr.scale = DVector2(scale, scale); actorNew->spr.shade = -40; actorNew->spr.Angles.Yaw = nang; actorNew->user.Counter = 0; zsize = ActorSizeZ(actorNew); actorNew->user.Radius = 200; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 16; actorNew->user.ceiling_dist = zsize/2; actorNew->user.floor_dist = zsize/2; if (RANDOM_P2(1024) > 512) actorNew->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) > 512) actorNew->spr.cstat |= (CSTAT_SPRITE_YFLIP); if (SP_TAG7(actor)) { zvel = SP_TAG7(actor); zvel_rand = SP_TAG8(actor); } else { zvel = 50; zvel_rand = 40; } UpdateChangeXY(actorNew); actorNew->user.change.Z = -zvel -RandomRange(zvel_rand); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSerpRing(DSWActor* actor) { DAngle ang, ang_diff, ang_start; int missiles; int max_missiles; const int SERP_RING_DIST = 175; extern STATE s_SkullExplode[]; extern STATE s_SkullRing[5][1]; extern STATE* sg_SkullRing[]; max_missiles = 12; actor->user.Counter = max_missiles; ang_diff = DAngle360 / max_missiles; ang_start = actor->spr.Angles.Yaw - DAngle180; PlaySound(DIGI_SERPSUMMONHEADS, actor, v3df_none); for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++) { auto actorNew = SpawnActor(STAT_SKIP4, SKULL_SERP, &s_SkullRing[0][0], actor->sector(), actor->spr.pos, ang, 0); actorNew->vel.X = 31.25; SetOwner(actor, actorNew); actorNew->spr.shade = -20; actorNew->spr.scale = DVector2(1, 1); actorNew->spr.yint = 2*RINGMOVETICS; actorNew->vel.Z = 3; actorNew->spr.pal = 0; actorNew->spr.pos.Z = ActorZOfTop(actor) - 20; actorNew->user.pos.Z = 50; // ang around the serp is now slide_ang actorNew->user.slide_ang = actorNew->spr.Angles.Yaw; // randomize the head turning angle actorNew->spr.Angles.Yaw = RandomAngle(); // control direction of spinning actor->user.Flags ^= SPR_BOUNCE; actorNew->user.Flags |= (actor->user.Flags & (SPR_BOUNCE)); actorNew->user.Dist = 37.5; actorNew->user.TargetDist = SERP_RING_DIST; actorNew->user.Counter2 = 0; actorNew->user.StateEnd = s_SkullExplode; actorNew->user.Rot = sg_SkullRing; // defaults do change the statnum EnemyDefaults(actorNew, nullptr, nullptr); change_actor_stat(actorNew, STAT_SKIP4); actorNew->spr.extra &= ~(SPRX_PLAYER_OR_ENEMY); actorNew->clipdist = 12; actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.Radius = 400; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitSpellNapalm(PLAYER* pp) { DSWActor* plActor = pp->actor; unsigned i; short ammo; static const MISSILE_PLACEMENT mp[] = { {600 * 6, 400, DAngle90}, {0, 1100, nullAngle}, {600 * 6, 400, -DAngle90}, }; ammo = NAPALM_MIN_AMMO; if (pp->WpnAmmo[WPN_HOTHEAD] < ammo) return; else PlayerUpdateAmmo(pp, WPN_HOTHEAD, -ammo); PlaySound(DIGI_NAPFIRE, pp, v3df_none); if (!pp->insector()) return; for (i = 0; i < SIZ(mp); i++) { auto actor = SpawnActor(STAT_MISSILE, FIREBALL1, s_Napalm, pp->cursector, pp->actor->getPosWithOffsetZ().plusZ(12), pp->angle.ang, NAPALM_VELOCITY*2); actor->spr.hitag = LUMINOUS; //Always full brightness if (i==0) // Only attach sound to first projectile { PlaySound(DIGI_NAPWIZ, actor, v3df_follow); } SetOwner(pp->actor, actor); actor->spr.shade = -40; actor->spr.scale = DVector2(0.5, 0.5); actor->clipdist = 0; setFreeAimVelocity(actor->vel.X, actor->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); actor->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.Flags2 |= (SPR2_BLUR_TAPER_FAST); actor->user.floor_dist = (1); actor->user.ceiling_dist = (1); actor->user.Dist = 12.5; auto oclipdist = plActor->clipdist; plActor->clipdist = 0.25; if (mp[i].dist_over != 0) { actor->spr.Angles.Yaw += mp[i].ang; HelpMissileLateral(actor, mp[i].dist_over); actor->spr.Angles.Yaw -= mp[i].ang; } UpdateChange(actor); if (MissileSetPos(actor, DoNapalm, mp[i].dist_out)) { plActor->clipdist = oclipdist; KillActor(actor); continue; } if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actor)) actor->user.Flags |= (SPR_UNDERWATER); plActor->clipdist = oclipdist; actor->user.Counter = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyNapalm(DSWActor* actor) { unsigned i; static const MISSILE_PLACEMENT mp[] = { {600 * 6, 400, DAngle90}, {0, 1100, nullAngle}, {600 * 6, 400, -DAngle90}, }; PlaySound(DIGI_NAPFIRE, actor, v3df_none); for (i = 0; i < SIZ(mp); i++) { auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL1, s_Napalm, actor->sector(), DVector3(actor->spr.pos, ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25)), actor->spr.Angles.Yaw, NAPALM_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness if (i==0) // Only attach sound to first projectile { PlaySound(DIGI_NAPWIZ, actorNew, v3df_follow); } if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->clipdist = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Flags2 |= (SPR2_BLUR_TAPER_FAST); actorNew->user.floor_dist = (1); actorNew->user.ceiling_dist = (1); actorNew->user.Dist = 12.5; auto oclipdist = actor->clipdist; actor->clipdist = 0.25; if (mp[i].dist_over != 0) { actorNew->spr.Angles.Yaw += mp[i].ang; HelpMissileLateral(actorNew, mp[i].dist_over); actorNew->spr.Angles.Yaw -= mp[i].ang; } // find the distance to the target (player) SetZVelFromTarget(actorNew, actor); UpdateChange(actorNew); MissileSetPos(actorNew, DoNapalm, mp[i].dist_out); actor->clipdist = oclipdist; actor->user.Counter = 0; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSpellMirv(PLAYER* pp) { PlaySound(DIGI_MIRVFIRE, pp, v3df_none); if (!pp->insector()) return 0; auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL1, s_Mirv, pp->cursector, pp->actor->getPosWithOffsetZ().plusZ(12), pp->angle.ang, MIRV_VELOCITY); PlaySound(DIGI_MIRVWIZ, actorNew, v3df_follow); SetOwner(pp->actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(1.125, 1.125); actorNew->clipdist = 2; setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.floor_dist = (16); actorNew->user.ceiling_dist = (16); actorNew->user.Dist = 12.5; DSWActor* plActor = pp->actor; auto oclipdist = plActor->clipdist; plActor->clipdist = 0; UpdateChange(actorNew); MissileSetPos(actorNew, DoMirv, 600); plActor->clipdist = oclipdist; actorNew->user.Counter = 0; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyMirv(DSWActor* actor) { PlaySound(DIGI_MIRVFIRE, actor, v3df_none); auto actorNew = SpawnActor(STAT_MISSILE, MIRV_METEOR, s_Mirv, actor->sector(), DVector3(actor->spr.pos, ActorZOfTop(actor) + (ActorSizeZ(actor) * 0.25)), actor->spr.Angles.Yaw, MIRV_VELOCITY); PlaySound(DIGI_MIRVWIZ, actorNew, v3df_follow); SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(1.125, 1.125); actorNew->clipdist = 2; actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.floor_dist = (16); actorNew->user.ceiling_dist = (16); actorNew->user.Dist = 12.5; UpdateChange(actorNew); MissileSetPos(actorNew, DoMirv, 600); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSwordAttack(PLAYER* pp) { DSWActor* plActor = pp->actor; unsigned stat; DAngle face; PlaySound(DIGI_SWORDSWOOSH, pp, v3df_dontpan | v3df_doppler); if (pp->Flags & (PF_DIVING)) { DSWActor* bubble; static const DAngle dangs[] = { -DAngle45, -DAngle22_5, nullAngle, DAngle22_5, DAngle45 }; for (size_t i = 0; i < countof(dangs); i++) { if (RandomRange(1000) < 500) continue; // Don't spawn bubbles every time bubble = SpawnBubble(pp->actor); if (bubble != nullptr) { bubble->spr.Angles.Yaw = pp->angle.ang; // check me out later. auto random_amt = RandomAngle(DAngle22_5 / 4) - DAngle22_5 / 8; // back it up a bit to get it out of your face auto ang = bubble->spr.Angles.Yaw + dangs[i] + random_amt; auto vec = ang.ToVector() * 240; move_missile(bubble, DVector3(vec, 0), plActor->user.ceiling_dist, plActor->user.floor_dist, CLIPMASK_PLAYER, 1); } } } for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (!itActor->hasU()) break; if (itActor->user.PlayerP == pp) break; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double dist = (pp->actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); face = mapangle(200); if (dist < CloseRangeDist(itActor, plActor, 62.5) && PlayerFacingRange(pp, itActor, face)) { if (SpriteOverlapZ(pp->actor, itActor, 20)) { if (FAFcansee(ActorVectOfMiddle(itActor), itActor->sector(), ActorVectOfMiddle(plActor), plActor->sector())) DoDamage(itActor, pp->actor); } } } } // all this is to break glass { HitInfo hit{}; double dax = 1024., daz = 0; DAngle daang = pp->angle.ang; setFreeAimVelocity(dax, daz, pp->horizon.ZzHORIZON, 1000. - (RandomRangeF(24000 / 256.) - 12000 / 256.)); FAFhitscan(pp->actor->getPosWithOffsetZ(), pp->cursector, DVector3(pp->angle.ang.ToVector() * dax, daz), hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) return 0; if ((pp->actor->getPosWithOffsetZ() - hit.hitpos).Length() < 43.75) { if (hit.actor() != nullptr) { extern STATE s_TrashCanPain[]; auto hitActor = hit.actor(); if (hitActor->hasU()) // JBF: added null check { switch (hitActor->user.ID) { case ZILLA_RUN_R0: SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_SWORDCLANK, hit.hitpos, v3df_none); break; case TRASHCAN: if (hitActor->user.WaitTics <= 0) { hitActor->user.WaitTics = SEC(2); ChangeState(hitActor, s_TrashCanPain); } SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_SWORDCLANK, hit.hitpos, v3df_none); PlaySound(DIGI_TRASHLID, hitActor, v3df_none); break; case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_SWORDCLANK, hit.hitpos, v3df_none); break; } } if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_STAR, hitActor)) return 0; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hitActor, 0); } // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hitActor); } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, pp->angle.ang, plActor->user.ID); } // hit non breakable wall - do sound and puff else { SpawnSwordSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, daang); PlaySound(DIGI_SWORDCLANK, hit.hitpos, v3df_none); } } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitFistAttack(PLAYER* pp) { DSWActor* plActor = pp->actor; unsigned stat; double reach; DAngle face; PlaySound(DIGI_STAR, pp, v3df_dontpan|v3df_doppler); if (pp->Flags & (PF_DIVING)) { DSWActor* bubble; static const DAngle dangs[] = { -DAngle22_5, DAngle22_5 }; for (size_t i = 0; i < countof(dangs); i++) { bubble = SpawnBubble(pp->actor); if (bubble != nullptr) { bubble->spr.Angles.Yaw = pp->angle.ang; // check me out later. auto random_amt = RandomAngle(DAngle22_5 / 4) - DAngle22_5 / 8; // back it up a bit to get it out of your face auto angle = bubble->spr.Angles.Yaw + dangs[i] + random_amt; auto vec = angle.ToVector() * 240; move_missile(bubble, DVector3(vec, 0), plActor->user.ceiling_dist, plActor->user.floor_dist, CLIPMASK_PLAYER, 1); } } } for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor->user.PlayerP == pp) break; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double dist = (pp->actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); bool iactive = pp->InventoryActive[2]; if (iactive) // Shadow Bombs give you demon fist { face = mapangle(190); reach = 143.75; } else { reach = 62.5; face = mapangle(200); } if (dist < CloseRangeDist(itActor, plActor, reach) && PlayerFacingRange(pp, itActor, face)) { if (SpriteOverlapZ(pp->actor, itActor, 20) || iactive) { if (FAFcansee(ActorVectOfMiddle(itActor), itActor->sector(), ActorVectOfMiddle(plActor), plActor->sector())) DoDamage(itActor, plActor); if (iactive) { SpawnDemonFist(itActor); } } } } } // all this is to break glass { HitInfo hit{}; double dax = 1024., daz = 0; auto daang = pp->angle.ang; setFreeAimVelocity(dax, daz, pp->horizon.ZzHORIZON, 1000. - (RandomRangeF(24000 / 256.) - 12000 / 256.)); FAFhitscan(pp->actor->getPosWithOffsetZ(), pp->cursector, DVector3(pp->angle.ang.ToVector() * dax, daz), hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) return 0; if ((pp->actor->getPosWithOffsetZ() - hit.hitpos).Length() < 43.75) { if (hit.actor() != nullptr) { extern STATE s_TrashCanPain[]; auto hitActor = hit.actor(); if (hitActor->hasU()) // JBF: added null check { switch (hitActor->user.ID) { case ZILLA_RUN_R0: SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_ARMORHIT, hit.hitpos, v3df_none); break; case TRASHCAN: if (hitActor->user.WaitTics <= 0) { hitActor->user.WaitTics = SEC(2); ChangeState(hitActor, s_TrashCanPain); } SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_ARMORHIT, hit.hitpos, v3df_none); PlaySound(DIGI_TRASHLID, hitActor, v3df_none); break; case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_ARMORHIT, hit.hitpos, v3df_none); break; } } if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_STAR, hitActor)) return 0; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hitActor,0); } // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hitActor); } switch (hitActor->spr.picnum) { case 5062: case 5063: case 4947: SpawnSwordSparks(pp, hit.hitSector, nullptr, hit.hitpos, daang); PlaySound(DIGI_ARMORHIT, hit.hitpos, v3df_none); if (RandomRange(1000) > 700) PlayerUpdateHealth(pp,1); // Give some health hitActor->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); break; } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, pp->angle.ang, plActor->user.ID); } // hit non breakable wall - do sound and puff else { SpawnSwordSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, daang); PlaySound(DIGI_ARMORHIT, hit.hitpos, v3df_none); if (PlayerTakeDamage(pp, nullptr)) { PlayerUpdateHealth(pp, -(RandomRange(2<<8)>>8)); PlayerCheckDeath(pp, nullptr); } } } } return 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSumoNapalm(DSWActor* actor) { short dist; short ang; static const MISSILE_PLACEMENT mp[] = { {0, 1100, nullAngle}, }; PlaySound(DIGI_NAPFIRE, actor, v3df_none); DAngle angle = actor->spr.Angles.Yaw; for (int j = 0; j < 4; j++) { for (size_t i = 0; i < countof(mp); i++) { auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL1, s_Napalm, actor->sector(), DVector3(actor->spr.pos.XY(), ActorZOfTop(actor)), angle, NAPALM_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness if (i == 0) // Only attach sound to first projectile { PlaySound(DIGI_NAPWIZ, actorNew, v3df_follow); } SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->clipdist = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Flags2 |= (SPR2_BLUR_TAPER_FAST); actorNew->user.floor_dist = (1); actorNew->user.ceiling_dist = (1); actorNew->user.Dist = 12.5; auto oclipdist = actor->clipdist; actor->clipdist = 0.25; if (mp[i].dist_over != 0) { HelpMissileLateral(actorNew, mp[i].dist_over); } // find the distance to the target (player) SetZVelFromTarget(actorNew, actor); UpdateChange(actorNew); MissileSetPos(actorNew, DoNapalm, mp[i].dist_out); actor->clipdist = oclipdist; actor->user.Counter = 0; } angle += DAngle90; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSumoSkull(DSWActor* actor) { extern STATE s_SkullExplode[]; extern STATE s_SkullWait[5][1]; extern STATE* sg_SkullWait[]; extern ATTRIBUTE SkullAttrib; PlaySound(DIGI_SERPSUMMONHEADS, actor, v3df_none); auto actorNew = SpawnActor(STAT_ENEMY, SKULL_R0, &s_SkullWait[0][0], actor->sector(), ActorVectOfMiddle(actor), actor->spr.Angles.Yaw, 0); actorNew->vel.X = 31.25; SetOwner(actor, actorNew); actorNew->spr.shade = -20; actorNew->spr.scale = DVector2(1, 1); actorNew->spr.pal = 0; // randomize the head turning angle actorNew->spr.Angles.Yaw = RandomAngle(); // control direction of spinning actor->user.Flags ^= SPR_BOUNCE; actorNew->user.Flags |= (actor->user.Flags & (SPR_BOUNCE)); actorNew->user.StateEnd = s_SkullExplode; actorNew->user.Rot = sg_SkullWait; actorNew->user.Attrib = &SkullAttrib; DoActorSetSpeed(actor, NORM_SPEED); actorNew->user.Counter = RANDOM_P2(2048); actorNew->user.pos.Z = actorNew->spr.pos.Z; actorNew->user.Health = 100; // defaults do change the statnum EnemyDefaults(actorNew, nullptr, nullptr); actorNew->spr.extra |= SPRX_PLAYER_OR_ENEMY; actorNew->clipdist = 12; actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.Radius = 400; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSumoStompAttack(DSWActor* actor) { unsigned stat; short reach; PlaySound(DIGI_30MMEXPLODE, actor, v3df_dontpan|v3df_doppler); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor != actor->user.targetActor) break; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(itActor, actor, 1024)) { if (FAFcansee(ActorVectOfMiddle(itActor), itActor->sector(), ActorVectOfMiddle(actor), actor->sector())) DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitMiniSumoClap(DSWActor* actor) { auto targetActor = actor->user.targetActor; if (!targetActor) return 0; double dist = (actor->spr.pos.XY() - targetActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(targetActor, actor, 62.5)) { if (SpriteOverlapZ(actor, targetActor, 20)) { if (FAFcansee(ActorVectOfMiddle(targetActor), targetActor->sector(), ActorVectOfMiddle(actor), actor->sector())) { PlaySound(DIGI_CGTHIGHBONE, actor, v3df_follow | v3df_dontpan); DoDamage(targetActor, actor); } } } else if (dist < CloseRangeDist(targetActor, actor, 625)) { if (FAFcansee(ActorVectOfMiddle(targetActor), targetActor->sector(), ActorVectOfMiddle(actor), actor->sector())) { PlaySound(DIGI_30MMEXPLODE, actor, v3df_none); SpawnFireballFlames(actor, targetActor); } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int WeaponAutoAim(DSWActor* actor, DSWActor* mislActor, DAngle ang, bool test) { if (actor->hasU() && actor->user.PlayerP) { if (Autoaim(actor->user.PlayerP->pnum) != 1) { return -1; } } DSWActor* hitActor; if ((hitActor = DoPickTarget(actor, ang, test)) != nullptr) { mislActor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); auto delta = hitActor->spr.pos.XY() - mislActor->spr.pos.XY(); mislActor->spr.Angles.Yaw = delta.Angle(); double dist = delta.Length(); if (dist != 0) { double zh; double tos = ActorZOfTop(hitActor); double diff = mislActor->spr.pos.Z - tos; double siz = ActorSizeZ(hitActor); // hit_sprite is below if (diff < -50) zh = tos + (siz * 0.5); else // hit_sprite is above if (diff > 50) zh = tos + (siz * 0.125); else zh = tos + (siz * 0.25); mislActor->vel.Z = mislActor->vel.X * (zh - mislActor->spr.pos.Z) / dist; } return 0; } return -1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int WeaponAutoAimZvel(DSWActor* actor, DSWActor* missileActor, double *zvel, DAngle ang, bool test) { if (actor->hasU() && actor->user.PlayerP) { if (!Autoaim(actor->user.PlayerP->pnum)) { return -1; } } DSWActor* hitActor; if ((hitActor = DoPickTarget(actor, ang, test)) != nullptr) { missileActor->user.WpnGoalActor = hitActor; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); missileActor->spr.Angles.Yaw = (hitActor->spr.pos.XY() - missileActor->spr.pos.XY()).Angle(); double dist = (missileActor->spr.pos.XY() - hitActor->spr.pos.XY()).Length(); if (dist != 0) { double zh; double tos = ActorZOfTop(hitActor); double diff = missileActor->spr.pos.Z - tos; double siz = ActorSizeZ(hitActor); // hit_sprite is below if (diff < -50) zh = tos + (siz * 0.5); else // hit_sprite is above if (diff > 50) zh = tos + (siz * 0.125); else zh = tos + (siz * 0.25); *zvel = (missileActor->vel.X * (zh - missileActor->spr.pos.Z)) / dist; } return 0; } return -1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* AimHitscanToTarget(DSWActor* actor, double *z, DAngle *ang, double z_ratio) { DSWActor* hitActor = actor->user.targetActor; if (hitActor == nullptr) return nullptr; hitActor->user.Flags |= (SPR_TARGETED); hitActor->user.Flags |= (SPR_ATTACKED); auto delta = hitActor->spr.pos.XY() - actor->spr.pos.XY(); *ang = delta.Angle(); // find the distance to the target double dist = delta.Length(); if (dist != 0) { double zh = ActorUpperZ(hitActor); // This doesn't look like it makes much sense... auto vect = ang->ToVector() * 1024; if (delta.X != 0) *z = vect.X * (zh - *z) / delta.X; else if (delta.Y != 0) *z = vect.Y * (zh - *z) / delta.Y; else *z = 0; // so actors won't shoot straight up at you // need to be a bit of a distance away // before they have a valid shot if (abs(*z / dist) > z_ratio) { return nullptr; } } return hitActor; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* WeaponAutoAimHitscan(DSWActor* actor, double *z, DAngle *ang, bool test) { if (actor->hasU() && actor->user.PlayerP) { if (!Autoaim(actor->user.PlayerP->pnum)) { return nullptr; } } DSWActor* picked; if ((picked = DoPickTarget(actor, *ang, test)) != nullptr) { picked->user.Flags |= (SPR_TARGETED); picked->user.Flags |= (SPR_ATTACKED); *ang = (picked->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // find the distance to the target double dist = (actor->spr.pos.XY() - picked->spr.pos.XY()).Length(); if (dist != 0) { double zh = (ActorZOfTop(picked) + (ActorSizeZ(picked) * 0.5)); auto vect = ang->ToVector() * 1024; if (picked->spr.pos.X - actor->spr.pos.X != 0) *z = vect.X * (zh - *z) / (picked->spr.pos.X - actor->spr.pos.X); else if (picked->spr.pos.Y - actor->spr.pos.Y != 0) *z = vect.Y * (zh - *z) / (picked->spr.pos.Y - actor->spr.pos.Y); else *z = 0; } } return picked; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponHitscanShootFeet(DSWActor* actor, DSWActor* hitActor, double *zvect) { auto delta = hitActor->spr.pos.XY() - actor->spr.pos.XY(); DAngle ang = delta.Angle(); // find the distance to the target double dist = delta.Length(); if (dist != 0) { double zh = ActorZOfBottom(hitActor) + 20; double z = actor->spr.pos.Z; auto vect = ang.ToVector() * 1024; if (delta.X != 0) *zvect = vect.X * (zh - z) / delta.X; else if (delta.Y != 0) *zvect = vect.Y * (zh - z) / delta.Y; else *zvect = 0; } } int InitStar(PLAYER* pp) { DSWActor* plActor = pp->actor; static DAngle dang[] = { mapangle(-12), mapangle(12) }; const double STAR_REPEAT = 0.40625; const int STAR_HORIZ_ADJ = 100; PlayerUpdateAmmo(pp, plActor->user.WeaponNum, -3); PlaySound(DIGI_STAR, pp, v3df_dontpan|v3df_doppler); if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, STAR1, s_Star, pp->cursector, pos, pp->angle.ang, STAR_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(STAR_REPEAT, STAR_REPEAT); actorNew->spr.shade = -25; actorNew->clipdist = 2; // zvel was overflowing with this calculation - had to move to a local long var double zvel = 0; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, (HORIZ_MULT + STAR_HORIZ_ADJ) * 0.5); actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->user.WeaponNum = plActor->user.WeaponNum; actorNew->user.Radius = 100; actorNew->user.Counter = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); // zvel had to be tweaked alot for this weapon // MissileSetPos seemed to be pushing the sprite too far up or down when // the horizon was tilted. Never figured out why. actorNew->vel.Z = zvel * 0.5; double act2zvel = actorNew->vel.Z; if (MissileSetPos(actorNew, DoStar, 1000)) { KillActor(actorNew); return 0; } if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5/4, false) != -1) { zvel = actorNew->vel.Z; } UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); actorNew->backuppos(); for (size_t i = 0; i < countof(dang); i++) { auto actorNew2 = SpawnActor(STAT_MISSILE, STAR1, s_Star, pp->cursector,pos, actorNew->spr.Angles.Yaw + dang[i], actorNew->vel.X); SetOwner(GetOwner(actorNew), actorNew2); actorNew2->spr.scale = DVector2(STAR_REPEAT, STAR_REPEAT); actorNew2->spr.shade = actorNew->spr.shade; actorNew2->spr.extra = actorNew->spr.extra; actorNew2->copy_clipdist(actorNew); actorNew2->user.WeaponNum = actorNew->user.WeaponNum; actorNew2->user.Radius = actorNew->user.Radius; actorNew2->user.ceiling_dist = actorNew->user.ceiling_dist; actorNew2->user.floor_dist = actorNew->user.floor_dist; actorNew2->user.Flags2 = actorNew->user.Flags2 & ~(SPR2_FLAMEDIE); // mask out any new flags here for safety. if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew2)) actorNew2->user.Flags |= SPR_UNDERWATER; actorNew2->vel.Z = act2zvel; if (MissileSetPos(actorNew2, DoStar, 1000)) { KillActor(actorNew2); return 0; } // move the same as middle star actorNew2->user.change.Z = actorNew->user.change.Z; UpdateChangeXY(actorNew2); actorNew2->backuppos(); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitHeartAttack(PLAYER* pp) { DSWActor* plActor = pp->actor; short i = 0; static const MISSILE_PLACEMENT mp[] = { {0, 1100, nullAngle}, }; PlayerUpdateAmmo(pp, WPN_HEART, -1); if (!pp->insector()) return; auto actorNew = SpawnActor(STAT_MISSILE_SKIP4, BLOOD_WORM, s_BloodWorm, pp->cursector, pp->actor->getPosWithOffsetZ().plusZ(12), pp->angle.ang, BLOOD_WORM_VELOCITY*2); actorNew->spr.hitag = LUMINOUS; //Always full brightness SetOwner(pp->actor, actorNew); actorNew->spr.shade = -10; actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->clipdist = 0; setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Flags2 |= (SPR2_DONT_TARGET_OWNER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); actorNew->user.floor_dist = (1); actorNew->user.ceiling_dist = (1); actorNew->user.Dist = 12.5; auto oclipdist = plActor->clipdist; plActor->clipdist = 0.25; UpdateChange(actorNew); MissileSetPos(actorNew, DoBloodWorm, mp[i].dist_out); plActor->clipdist = oclipdist; actorNew->user.Counter = 0; actorNew->user.Counter2 = 0; actorNew->user.Counter3 = 0; actorNew->user.WaitTics = 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ContinueHitscan(PLAYER* pp, sectortype* sect, const DVector3& start, DAngle ang, const DVector3& vect) { HitInfo hit{}; DSWActor* actor = pp->actor; FAFhitscan(start, sect, vect, hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) return 0; if (hit.actor() == nullptr && hit.hitWall == nullptr) { if (abs(hit.hitpos.Z - hit.hitSector->ceilingz) <= 1) { hit.hitpos.Z += 16; if ((hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)) return 0; } else if (abs(hit.hitpos.Z - hit.hitSector->floorz) <= 1) { } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, ang, actor->user.ID); return 0; } QueueHole(hit.hitSector, hit.hitWall, hit.hitpos); } // hit a sprite? if (hit.actor() != nullptr) { auto hitActor = hit.actor(); if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_SHOTGUN, hit.actor())) return 0; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hit.actor(),0); return 0; } if (BulletHitSprite(pp->actor, hit.actor(), hit.hitpos, 0)) return 0; // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hit.actor()); } } auto j = SpawnShotgunSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, ang); DoHitscanDamage(j, hit.actor()); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitShotgun(PLAYER* pp) { DSWActor* actor = pp->actor; HitInfo hit{}; short cstat = 0; PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); PlaySound(DIGI_RIOTFIRE2, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (pp->WpnShotgunAuto) { switch (pp->WpnShotgunType) { case 1: pp->WpnShotgunAuto--; } } auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z); double dax = 1024.; double daz = pos.Z; DAngle daang = DAngle22_5 * 0.5; if (WeaponAutoAimHitscan(pp->actor, &daz, &daang, false) == nullptr) { setFreeAimVelocity(dax, daz, pp->horizon.ZzHORIZON, 1000.); daang = pp->angle.ang; } double ndaz; DAngle ndaang; for (int i = 0; i < 12; i++) { if (pp->WpnShotgunType == 0) { ndaz = daz + RandomRangeF(120) - 45; ndaang = daang + mapangle(RandomRange(30) - 15); } else { ndaz = daz + RandomRangeF(200) - 65; ndaang = daang + mapangle(RandomRange(70) - 30); } DVector3 vect(ndaang.ToVector() * dax, ndaz); FAFhitscan(pos, pp->cursector, vect, hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) { continue; } if (hit.actor() == nullptr && hit.hitWall == nullptr) { if (abs(hit.hitpos.Z - hit.hitSector->ceilingz) <= 1) { hit.hitpos.Z += 16; cstat |= (CSTAT_SPRITE_YFLIP); if ((hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)) continue; if (SectorIsUnderwaterArea(hit.hitSector)) { WarpToSurface(hit.hitpos, &hit.hitSector); ContinueHitscan(pp, hit.hitSector, hit.hitpos, ndaang, vect); continue; } } else if (abs(hit.hitpos.Z - hit.hitSector->floorz) <= 1) { if ((hit.hitSector->extra & SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE) { SpawnSplashXY(hit.hitpos, hit.hitSector); if (SectorIsDiveArea(hit.hitSector)) { WarpToUnderwater(hit.hitpos, &hit.hitSector); ContinueHitscan(pp, hit.hitSector, hit.hitpos, ndaang, vect); } continue; } } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { continue; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, ndaang, actor->user.ID); continue; } QueueHole(hit.hitSector, hit.hitWall, hit.hitpos); } // hit a sprite? if (hit.actor() != nullptr) { auto hitActor = hit.actor(); if (hitActor->hasU() && hitActor->user.ID == TRASHCAN) // JBF: added null check { extern STATE s_TrashCanPain[]; PlaySound(DIGI_TRASHLID, hitActor, v3df_none); if (hitActor->user.WaitTics <= 0) { hitActor->user.WaitTics = SEC(2); ChangeState(hitActor,s_TrashCanPain); } } if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_SHOTGUN, hitActor)) continue; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hitActor,0); continue; } if (BulletHitSprite(pp->actor, hitActor, hit.hitpos, SHOTGUN_SMOKE)) continue; // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hitActor); } } auto j = SpawnShotgunSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, ndaang); DoHitscanDamage(j, hit.actor()); } DoPlayerBeginRecoil(pp, SHOTGUN_RECOIL_AMT); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitLaser(PLAYER* pp) { DSWActor* actor = pp->actor; DoPlayerBeginRecoil(pp, RAIL_RECOIL_AMT); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler); if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, s_Laser, pp->cursector, pos, pp->angle.ang, 18.75); actorNew->spr.hitag = LUMINOUS; //Always full brightness SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; actorNew->clipdist = 4; // the slower the missile travels the less of a zvel it needs setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, 16.); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 900); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // the slower the missile travels the less of a zvel it needs // move it 1200 dist in increments - works better if (MissileSetPos(actorNew, DoLaserStart, 300)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } if (MissileSetPos(actorNew, DoLaserStart, 300)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } if (MissileSetPos(actorNew, DoLaserStart, 300)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } if (MissileSetPos(actorNew, DoLaserStart, 300)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } actor->clipdist = oclipdist; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(5); } UpdateChange(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitRail(PLAYER* pp) { DSWActor* actor = pp->actor; double zvel; if (SW_SHAREWARE) return false; // JBF: verify DoPlayerBeginRecoil(pp, RAIL_RECOIL_AMT); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); PlaySound(DIGI_RAILFIRE, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 11); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], pp->cursector, pos, pp->angle.ang, 75); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, (HORIZ_MULT + 17) * 0.5); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rail[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = RAIL_RADIUS; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // at certain angles the clipping box was big enough to block the // initial positioning auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->clipdist = 2; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 700); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); if (TestMissileSetPos(actorNew, DoRailStart, 1200, zvel)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } actor->clipdist = oclipdist; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(4); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitZillaRail(DSWActor* actor) { if (SW_SHAREWARE) return false; // JBF: verify PlaySound(DIGI_RAILFIRE, actor, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; auto pos = ActorVectOfTop(actor); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], actor->sector(), pos, actor->spr.Angles.Yaw, 75); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; double zvel = (100 * (HORIZ_MULT+17)) / 256.; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rail[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = RAIL_RADIUS; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // at certain angles the clipping box was big enough to block the // initial positioning auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->clipdist = 2; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 700); actorNew->spr.Angles.Yaw -= DAngle90; if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); if (TestMissileSetPos(actorNew, DoRailStart, 1200, zvel)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } actor->clipdist = oclipdist; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(4); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitRocket(PLAYER* pp) { DSWActor* actor = pp->actor; double zvel; DoPlayerBeginRecoil(pp, ROCKET_RECOIL_AMT); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); auto const WpnRocketHeat = pp->WpnRocketHeat; if (WpnRocketHeat) { switch (pp->WpnRocketType) { case 1: pp->WpnRocketHeat--; break; } } PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], pp->cursector, pos, pp->angle.ang, ROCKET_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(1.40626, 1.40625); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, (HORIZ_MULT + 35) * 0.5); actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 2000; actorNew->user.ceiling_dist = (3); actorNew->user.floor_dist = (3); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // Set default palette actorNew->spr.pal = actorNew->user.spal = 17; // White if (WpnRocketHeat) { switch (pp->WpnRocketType) { case 1: actorNew->user.Flags |= (SPR_FIND_PLAYER); actorNew->spr.pal = actorNew->user.spal = 20; // Yellow break; } } // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 900); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // cancel smoke trail actorNew->user.Counter = 1; if (TestMissileSetPos(actorNew, DoRocket, 1200, zvel)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } // inable smoke trail actorNew->user.Counter = 0; actor->clipdist = oclipdist; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(5); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitBunnyRocket(PLAYER* pp) { DSWActor* actor = pp->actor; double zvel; DoPlayerBeginRecoil(pp, ROCKET_RECOIL_AMT); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); auto const WpnRocketHeat = pp->WpnRocketHeat; if (WpnRocketHeat) { switch (pp->WpnRocketType) { case 1: pp->WpnRocketHeat--; break; } } PlaySound(DIGI_BUNNYATTACK, pp, v3df_dontpan|v3df_doppler); if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R4, &s_BunnyRocket[0][0], pp->cursector, pos, pp->angle.ang, ROCKET_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(1, 1); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, (HORIZ_MULT + 35) * 0.5); actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_BunnyRocket[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 2000; actorNew->user.ceiling_dist = (3); actorNew->user.floor_dist = (3); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); if (WpnRocketHeat) { switch (pp->WpnRocketType) { case 1: actorNew->user.Flags |= (SPR_FIND_PLAYER); break; } } // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 900); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // cancel smoke trail actorNew->user.Counter = 1; if (TestMissileSetPos(actorNew, DoRocket, 1200, zvel)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } // inable smoke trail actorNew->user.Counter = 0; actor->clipdist = oclipdist; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(5); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; actorNew->user.spal = actorNew->spr.pal = PALETTE_PLAYER1; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitNuke(PLAYER* pp) { DSWActor* actor = pp->actor; double zvel; if (pp->WpnRocketNuke > 0) pp->WpnRocketNuke = 0; // Bye Bye little nukie. else return 0; DoPlayerBeginRecoil(pp, NUKE_RECOIL_AMT); PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], pp->cursector, pos, pp->angle.ang, 700/16.); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(2, 2); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, (HORIZ_MULT + 36) * 0.5); actorNew->clipdist = 4; // Set to red palette actorNew->spr.pal = actorNew->user.spal = 19; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = NUKE_RADIUS; actorNew->user.ceiling_dist = 3; actorNew->user.floor_dist = 3; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 900); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // cancel smoke trail actorNew->user.Counter = 1; if (TestMissileSetPos(actorNew, DoRocket, 1200, zvel)) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } // inable smoke trail actorNew->user.Counter = 0; actor->clipdist = oclipdist; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(5); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; PlayerDamageSlide(pp, -40, pp->angle.ang + DAngle180); // Recoil slide return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyNuke(DSWActor* actor) { PlaySound(DIGI_RIOTFIRE, actor, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; auto npos = actor->spr.pos.plusZ(40); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], actor->sector(), npos, actor->spr.Angles.Yaw, 700/16.); if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(2, 2); actorNew->spr.shade = -15; double zvel = (100 * (HORIZ_MULT-36)) / 256.; // Ugh... actorNew->clipdist = 4; // Set to red palette actorNew->spr.pal = actorNew->user.spal = 19; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = NUKE_RADIUS; actorNew->user.ceiling_dist = 3; actorNew->user.floor_dist = 3; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 500); actorNew->spr.Angles.Yaw -= DAngle90; if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // cancel smoke trail actorNew->user.Counter = 1; if (TestMissileSetPos(actorNew, DoRocket, 1200, zvel)) { KillActor(actorNew); return 0; } // enable smoke trail actorNew->user.Counter = 0; actorNew->vel.Z = zvel * 0.5; if (WeaponAutoAim(actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(5); } else zvel = actorNew->vel.Z; // Let autoaiming set zvel now UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitMicro(PLAYER* pp) { DSWActor* actor = pp->actor; short i; DAngle angle; TARGET_SORT* ts = TargetSort; DSWActor* picked = nullptr; const int MAX_MICRO = 1; DoPickTarget(pp->actor, DAngle45, false); if (TargetSortCount > MAX_MICRO) TargetSortCount = MAX_MICRO; if (!pp->insector()) return 0; double vel = 75., zvel = 0; setFreeAimVelocity(vel, zvel, pp->horizon.ZzHORIZON, HORIZ_MULTF); for (i = 0; i < MAX_MICRO; i++) { if (ts < &TargetSort[TargetSortCount] && ts->actor != nullptr) { picked = ts->actor; angle = (picked->spr.pos.XY() - pp->actor->spr.pos.XY()).Angle(); ts++; } else { picked = nullptr; angle = pp->angle.ang; } auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 4 + RandomRange(20)); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], pp->cursector, pos, angle, vel); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.375, 0.375); actorNew->spr.shade = -15; actorNew->vel.Z = zvel; actorNew->clipdist = 4; // randomize zvelocity actorNew->vel.Z += RandomRangeF(8) - 5; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Micro[0]); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (2); actorNew->user.floor_dist = (2); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); actorNew->user.WaitTics = 10 + RandomRange(40); // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; const int MICRO_LATERAL = 5000; HelpMissileLateral(actorNew, 1000 + (RandomRange(MICRO_LATERAL) - (MICRO_LATERAL / 2))); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); // cancel smoke trail actorNew->user.Counter = 1; if (MissileSetPos(actorNew, DoMicro, 700)) { actor->clipdist = oclipdist; KillActor(actorNew); continue; } // inable smoke trail actorNew->user.Counter = 0; actor->clipdist = oclipdist; const int MICRO_ANG = 400; if (picked) { double dist = (actorNew->spr.pos.XY() - picked->spr.pos.XY()).Length(); if (dist != 0) { double zh = ActorZOfTop(picked) + (ActorSizeZ(picked) * 0.25); actorNew->vel.Z = (actorNew->vel.X * (zh - actorNew->spr.pos.Z)) / dist; } actorNew->user.WpnGoalActor = ts->actor; picked->user.Flags |= (SPR_TARGETED); picked->user.Flags |= (SPR_ATTACKED); } else { actorNew->spr.Angles.Yaw += mapangle((RandomRange(MICRO_ANG) - (MICRO_ANG / 2)) - 16); } UpdateChange(actorNew); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitRipperSlash(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_RIPPER2ATTACK, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; double d = (actor->spr.pos - itActor->spr.pos).Length(); if (d > itActor->user.fRadius() * 2) continue; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 37.5) && FacingRange(itActor, actor,FacingAngle)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitBunnySlash(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_BUNNYATTACK, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 37.5) && FacingRange(itActor, actor,FacingAngle)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSerpSlash(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_SERPSWORDATTACK, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 50) && FacingRange(itActor, actor,FacingAngle)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool WallSpriteInsideSprite(DSWActor* wactor, DSWActor* actor) { DVector2 out[2]; GetWallSpritePosition(&wactor->spr, wactor->spr.pos, out); return IsCloseToLine(actor->spr.pos.XY(), out[0], out[1], actor->clipdist) != EClose::Outside; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBladeDamage(DSWActor* actor) { int i; unsigned stat; for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double d = (actor->spr.pos - itActor->spr.pos).Length(); if (d > 125) continue; if (WallSpriteInsideSprite(actor, itActor)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoStaticFlamesDamage(DSWActor* actor) { int i; unsigned stat; for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double d = (actor->spr.pos - itActor->spr.pos).Length(); if (d > 125) continue; if (SpriteOverlap(actor, itActor)) // If sprites are overlapping, cansee will fail! DoDamage(itActor, actor); else if (actor->user.Radius > 200) { if (FAFcansee(ActorVectOfMiddle(actor), actor->sector(), ActorVectOfMiddle(itActor), itActor->sector())) DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitCoolgBash(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_CGTHIGHBONE, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; // don't set off mine if (!(itActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) continue; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 37.5) && FacingRange(itActor, actor,FacingAngle)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSkelSlash(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_SPBLADE, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 37.5) && FacingRange(itActor, actor,FacingAngle)) { DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitGoroChop(DSWActor* actor) { int i; unsigned stat; PlaySound(DIGI_GRDSWINGAXE, actor, v3df_none); for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) break; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 48.75) && FacingRange(itActor, actor,FacingAngle)) { PlaySound(DIGI_GRDAXEHIT, actor, v3df_none); DoDamage(itActor, actor); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitHornetSting(DSWActor* actor) { DoDamage(actor->user.coll.actor(), actor); InitActorReposition(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSerpSpell(DSWActor* actor) { int dist; short i; static const DAngle lat_ang[] = { DAngle90, -DAngle90 }; static const DAngle delta_ang[] = { mapangle(-10), mapangle(10) }; for (i = 0; i < 2; i++) { actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); auto actorNew = SpawnActor(STAT_MISSILE, SERP_METEOR, &sg_SerpMeteor[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 93.75); actorNew->spr.pos.Z = ActorZOfTop(actor); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_SerpMeteor[0]); actorNew->user.StateEnd = s_MirvMeteorExp; SetOwner(actor, actorNew); actorNew->spr.shade = -40; PlaySound(DIGI_SERPMAGICLAUNCH, actor, v3df_none); actorNew->user.spal = actorNew->spr.pal = 27; // Bright Green actorNew->spr.scale = DVector2(1, 1); actorNew->clipdist = 2; actorNew->vel.Z = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.ceiling_dist = (16); actorNew->user.floor_dist = (16); actorNew->user.Dist = 12.5; auto oclipdist = actor->clipdist; actor->clipdist = 0.25; actorNew->spr.Angles.Yaw += lat_ang[i]; HelpMissileLateral(actorNew, 4200); actorNew->spr.Angles.Yaw -= lat_ang[i]; // find the distance to the target (player) SetZVelFromTarget(actorNew, actor); actorNew->spr.Angles.Yaw += delta_ang[i]; UpdateChange(actorNew); MissileSetPos(actorNew, DoMirvMissile, 400); actor->clipdist = oclipdist; if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnDemonFist(DSWActor* actor) { if (actor->user.Flags & (SPR_SUICIDE)) return -1; auto expActor = SpawnActor(STAT_MISSILE, 0, s_TeleportEffect, actor->sector(), ActorVectOfMiddle(actor), actor->spr.Angles.Yaw, 0); expActor->spr.hitag = LUMINOUS; //Always full brightness expActor->spr.shade = -40; expActor->spr.scale = DVector2(0.5, 0.5); expActor->user.spal = expActor->spr.pal = 25; expActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); expActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); expActor->user.Radius = DamageData[DMG_BASIC_EXP].radius; if (RANDOM_P2(1024<<8)>>8 > 600) expActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024<<8)>>8 > 600) expActor->spr.cstat |= (CSTAT_SPRITE_YFLIP); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSerpMonstSpell(DSWActor* actor) { int dist; short i; static const DAngle lat_ang[] = { DAngle90, -DAngle90 }; static const DAngle delta_ang[] = { mapangle(-10), mapangle(10) }; PlaySound(DIGI_MISSLFIRE, actor, v3df_none); for (i = 0; i < 1; i++) { actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); auto actorNew = SpawnActor(STAT_MISSILE, SERP_METEOR, &sg_SerpMeteor[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 31.25); actorNew->user.spal = actorNew->spr.pal = 25; // Bright Red actorNew->spr.pos.Z = ActorZOfTop(actor); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_SerpMeteor[0]); actorNew->user.StateEnd = s_TeleportEffect2; SetOwner(actor, actorNew); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(1.90625, 1.8125); actorNew->clipdist = 2; actorNew->vel.Z = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.ceiling_dist = (16); actorNew->user.floor_dist = (16); actorNew->user.Dist = 12.5; auto oclipdist = actor->clipdist; actor->clipdist = 0.25; actorNew->spr.Angles.Yaw += lat_ang[i]; HelpMissileLateral(actorNew, 4200); actorNew->spr.Angles.Yaw -= lat_ang[i]; // find the distance to the target (player) SetZVelFromTarget(actorNew, actor); actorNew->spr.Angles.Yaw += delta_ang[i]; UpdateChange(actorNew); MissileSetPos(actorNew, DoMirvMissile, 400); actor->clipdist = oclipdist; if (actor->user.Flags & (SPR_UNDERWATER)) actorNew->user.Flags |= (SPR_UNDERWATER); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoTeleRipper(DSWActor* actor) { PlaySound(DIGI_ITEM_SPAWN, actor, v3df_none); Ripper2Hatch(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyRocket(DSWActor* actor) { int dist; PlaySound(DIGI_NINJARIOTATTACK, actor, v3df_none); // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R2, &s_Rocket[0][0], actor->sector(), actor->spr.pos.plusZ(-(ActorSizeZ(actor) * 0.5)-16), actor->user.targetActor->spr.Angles.Yaw, NINJA_BOLT_VELOCITY); // Set default palette actorNew->spr.pal = actorNew->user.spal = 17; // White if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.4375, 0.4375); actorNew->spr.shade = -15; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.Radius = 200; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); UpdateChange(actorNew); if (actor->user.spal == PAL_XLAT_LT_TAN) { actorNew->user.Flags |= (SPR_FIND_PLAYER); actorNew->spr.pal = actorNew->user.spal = 20; // Yellow } MissileSetPos(actorNew, DoBoltThinMan, 400); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyRail(DSWActor* actor) { int dist; short pnum=0; if (SW_SHAREWARE) return false; // JBF: verify // if co-op don't hurt teammate if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE && actor->user.ID == ZOMBIE_RUN_R0) { PLAYER* pp; // Check all players TRAVERSE_CONNECT(pnum) { pp = &Player[pnum]; if (actor->user.targetActor == pp->actor) return 0; } } PlaySound(DIGI_RAILFIRE, actor, v3df_dontpan|v3df_doppler); // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // add a bit of randomness if (RANDOM_P2(1024) < 512) actor->spr.Angles.Yaw += mapangle(RANDOM_P2(128) - 64); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], actor->sector(), actor->spr.pos.plusZ(-(ActorSizeZ(actor) * 0.5) - 8), actor->spr.Angles.Yaw, 75); if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; actorNew->vel.Z = 0; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rail[0]); actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->clipdist = 4; UpdateChange(actorNew); if (TestMissileSetPos(actorNew, DoRailStart, 600, actorNew->vel.Z)) { KillActor(actorNew); return 0; } // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitZillaRocket(DSWActor* actor) { int dist; short w; static const MISSILE_PLACEMENT mp[] = { {600 * 6, 400, DAngle90}, {900 * 6, 400, DAngle90}, {1100 * 6, 400, DAngle90}, {600 * 6, 400, -DAngle90}, {900 * 6, 400, -DAngle90}, {1100 * 6, 400, -DAngle90}, }; PlaySound(DIGI_NINJARIOTATTACK, actor, v3df_none); // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); for (int i = 0; i < (int)SIZ(mp); i++) { // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R2, &s_Rocket[0][0], actor->sector(), actor->spr.pos.plusZ(-(ActorSizeZ(actor) * 0.5) - 16), actor->user.targetActor->spr.Angles.Yaw, NINJA_BOLT_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.4375, 0.4375); actorNew->spr.shade = -15; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.Radius = 200; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); UpdateChange(actorNew); // Zilla has seekers! if (i != 1 && i != 4) actorNew->spr.pal = actorNew->user.spal = 17; // White else { actorNew->user.Flags |= (SPR_FIND_PLAYER); actorNew->spr.pal = actorNew->user.spal = 20; // Yellow } if (mp[i].dist_over != 0) { actorNew->spr.Angles.Yaw += mp[i].ang; HelpMissileLateral(actorNew, mp[i].dist_over); actorNew->spr.Angles.Yaw -= mp[i].ang; } MissileSetPos(actorNew, DoBoltThinMan, mp[i].dist_out); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyStar(DSWActor* actor) { int dist; // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, STAR1, s_Star, actor->sector(), ActorVectOfMiddle(actor), actor->user.targetActor->spr.Angles.Yaw, NINJA_STAR_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.25, 0.25); actorNew->spr.shade = -25; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 4; UpdateChange(actorNew); MissileSetPos(actorNew, DoStar, 400); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); PlaySound(DIGI_STAR, actor, v3df_none); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyCrossbow(DSWActor* actor) { int dist; // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, CROSSBOLT, &s_CrossBolt[0][0], actor->sector(), ActorVectOfMiddle(actor).plusZ(-14), actor->user.targetActor->spr.Angles.Yaw, 50); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.25, 0.40625); actorNew->spr.shade = -25; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_CrossBolt[0]); UpdateChange(actorNew); actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); MissileSetPos(actorNew, DoStar, 400); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, true); PlaySound(DIGI_STAR, actor, v3df_none); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSkelSpell(DSWActor* actor) { PlaySound(DIGI_SPELEC, actor, v3df_none); // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, ELECTRO_ENEMY, s_Electro, actor->sector(), actor->spr.pos.plusZ(-(ActorSizeZ(actor) * 0.5)), actor->user.targetActor->spr.Angles.Yaw, SKEL_ELECTRO_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.scale.X += (-0.3125); actorNew->spr.scale.Y += (-0.3125); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 4; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, false, ActorSizeZ(actor) * 0.5); UpdateChange(actorNew); MissileSetPos(actorNew, DoElectro, 400); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitCoolgFire(DSWActor* actor) { // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); // Spawn a shot // Inserting and setting up variables PlaySound(DIGI_CGMAGIC, actor, v3df_follow); auto actorNew = SpawnActor(STAT_MISSILE, COOLG_FIRE, s_CoolgFire, actor->sector(), actor->spr.pos.plusZ(-16), actor->user.targetActor->spr.Angles.Yaw, COOLG_FIRE_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.hitag = LUMINOUS; actorNew->spr.scale = DVector2(0.28125, 0.28125); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; actorNew->clipdist = 2; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); if (actor->user.ID == RIPPER_RUN_R0) actorNew->user.spal = actorNew->spr.pal = 27; // Bright Green else actorNew->user.spal = actorNew->spr.pal = 25; // Bright Red PlaySound(DIGI_MAGIC1, actorNew, v3df_follow|v3df_doppler); // find the distance to the target (player) SetZVelFromTarget(actorNew, actor, false, -16); UpdateChange(actorNew); auto vec = actor->spr.Angles.Yaw.ToVector() * 45.5; move_missile(actorNew, DVector3(vec, 0), actorNew->user.ceiling_dist, actorNew->user.floor_dist, 0, 3); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoCoolgDrip(DSWActor* actor) { actor->user.Counter += 220; actor->spr.pos.Z += actor->user.Counter * zmaptoworld; if (actor->spr.pos.Z > actor->user.loz - actor->user.floor_dist) { actor->spr.pos.Z = actor->user.loz - actor->user.floor_dist; actor->spr.scale = DVector2(0.5, 0.5); ChangeState(actor, s_GoreFloorSplash); if (actor->user.spal == PALETTE_BLUE_LIGHTING) PlaySound(DIGI_DRIP, actor, v3df_none); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitCoolgDrip(DSWActor* actor) { short w; auto actorNew = SpawnActor(STAT_MISSILE, COOLG_DRIP, s_CoolgDrip, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.3125, 0.3125); actorNew->spr.shade = -5; actorNew->vel.Z = 0; actorNew->clipdist = 1; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); DoFindGroundPoint(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int GenerateDrips(DSWActor* actor) { short w = 0; if ((actor->user.WaitTics-=ACTORMOVETICS) <= 0) { if (actor->spr.lotag == 0) actor->user.WaitTics = RANDOM_P2(256<<8)>>8; else actor->user.WaitTics = (actor->spr.lotag * 120) + SEC(RandomRange(3<<8)>>8); if (TEST_BOOL2(actor)) { auto ww = SpawnBubble(actor); return 1; } auto actorNew = SpawnActor(STAT_SHRAP, COOLG_DRIP, s_CoolgDrip, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.3125, 0.3125); actorNew->spr.shade = -10; actorNew->vel.Z = 0; actorNew->clipdist = 1; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); if (TEST_BOOL1(actor)) actorNew->user.spal = actorNew->spr.pal = PALETTE_BLUE_LIGHTING; DoFindGroundPoint(actor); } return 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEelFire(DSWActor* actor) { unsigned stat; for (stat = 0; stat < SIZ(StatDamageList); stat++) { SWStatIterator it(StatDamageList[stat]); while (auto itActor = it.Next()) { if (itActor == actor) continue; if (itActor != actor->user.targetActor) continue; if ((actor->spr.pos - itActor->spr.pos).Length() > itActor->user.fRadius() * 2) continue; double dist = (actor->spr.pos.XY() - itActor->spr.pos.XY()).Length(); if (dist < CloseRangeDist(actor, itActor, 37.5) && FacingRange(itActor, actor,FacingAngle)) { PlaySound(DIGI_GIBS1, actor, v3df_none); DoDamage(itActor, actor); } else InitActorReposition(actor); } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitFireballTrap(DSWActor* actor) { short w; PlaySound(DIGI_FIREBALL1, actor, v3df_none); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL, s_Fireball, actor->sector(), actor->spr.pos.plusZ(-ActorSizeZ(actor)), actor->spr.Angles.Yaw, FIREBALL_TRAP_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness SetOwner(actor, actorNew); actorNew->spr.scale.X += (-0.3125); actorNew->spr.scale.Y += (-0.3125); actorNew->spr.shade = -40; actorNew->clipdist = 2; actorNew->vel.Z = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.WeaponNum = WPN_HOTHEAD; UpdateChange(actorNew); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitBoltTrap(DSWActor* actor) { short w; PlaySound(DIGI_RIOTFIRE, actor, v3df_none); // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], actor->sector(), actor->spr.pos.plusZ(-ActorSizeZ(actor)), actor->spr.Angles.Yaw, BOLT_TRAP_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -15; actorNew->vel.Z = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rocket[0]); actorNew->user.Radius = 200; UpdateChange(actorNew); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void InitSpearTrap(DSWActor* actor) { // Spawn a shot auto actorNew = SpawnActor(STAT_MISSILE, CROSSBOLT, &s_CrossBolt[0][0], actor->sector(), ActorVectOfMiddle(actor), actor->spr.Angles.Yaw, 750/16.); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.25, 0.40625); actorNew->spr.shade = -25; actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_CrossBolt[0]); UpdateChange(actorNew); actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); PlaySound(DIGI_STAR, actor, v3df_none); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoSuicide(DSWActor* actor) { KillActor(actor); return 0; } int DoDefaultStat(DSWActor* actor) { change_actor_stat(actor, STAT_DEFAULT); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTracerUzi(PLAYER* pp) { if (!pp->insector()) return 0; DSWActor* actor = pp->actor; static const short lat_dist[] = {800,-800}; double nz = 8 + (pp->horizon.ZzHORIZON.Tan() * 36.); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, 0, s_Tracer, pp->cursector, pp->actor->getPosWithOffsetZ().plusZ(nz), pp->angle.ang, TRACER_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.15625, 0.15625); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (3); actorNew->user.floor_dist = (3); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); DSWActor* plActor = pp->actor; auto oclipdist = plActor->clipdist; plActor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; if (pp->Flags & (PF_TWO_UZI) && pp->WpnUziType == 0) HelpMissileLateral(actorNew, lat_dist[RANDOM_P2(2<<8)>>8]); else HelpMissileLateral(actorNew, lat_dist[0]); actorNew->spr.Angles.Yaw -= DAngle90; if (MissileSetPos(actorNew, DoTracerStart, 800)) { plActor->clipdist = oclipdist; KillActor(actorNew); return 0; } setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, actorNew->vel.X); plActor->clipdist = oclipdist; WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false); // a bit of randomness actorNew->spr.Angles.Yaw += mapangle(RandomRange(30) - 15); UpdateChange(actorNew); if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTracerTurret(DSWActor* actor, DSWActor* Operator, DAngle horiz) { // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, 0, s_Tracer, actor->sector(), actor->spr.pos.plusZ(horiz.Tan() * 36.), actor->spr.Angles.Yaw, TRACER_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness if (Operator!= nullptr) SetOwner(Operator, actorNew); actorNew->spr.scale = DVector2(0.15625, 0.15625); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 0.5; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, horiz, actorNew->vel.X); WeaponAutoAim(actor, actorNew, DAngle22_5 / 4, false); // a bit of randomness actorNew->spr.Angles.Yaw += mapangle(RandomRange(30) - 15); UpdateChange(actorNew); if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTracerAutoTurret(DSWActor* actor, const DVector3& change) { // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, 0, s_Tracer, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, TRACER_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.15625, 0.15625); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 0.5; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); actorNew->user.change = change; if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int BulletHitSprite(DSWActor* actor, DSWActor* hitActor, const DVector3& hit_pos, short ID) { short id; // hit a NPC or PC? if ((hitActor->spr.extra & SPRX_PLAYER_OR_ENEMY)) { // spawn a red splotch // !FRANK! this if was incorrect - its not who is HIT, its who is SHOOTING //if(!hitActor->user.PlayerP) if (actor->user.PlayerP) id = UZI_SMOKE; else if (actor->user.Flags & (SPR_SO_ATTACHED)) id = UZI_SMOKE; else // Spawn NPC uzi with less damage id = UZI_SMOKE+2; if (ID>0) id = ID; auto actorNew = SpawnActor(STAT_MISSILE, id, s_UziSmoke, §or[0], hit_pos, actor->spr.Angles.Yaw, 0); actorNew->spr.shade = -40; if (hitActor->user.PlayerP) actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); switch (hitActor->user.ID) { case TRASHCAN: case PACHINKO1: case PACHINKO2: case PACHINKO3: case PACHINKO4: case 623: case ZILLA_RUN_R0: actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT, UZI_SMOKE_REPEAT); if (RANDOM_P2(1024) > 800) SpawnShrapX(hitActor); break; default: actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT / 3, UZI_SMOKE_REPEAT / 3); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); //actorNew->user.spal = actorNew->spr.pal = PALETTE_RED_LIGHTING; break; } SetOwner(actor, actorNew); actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; SetActorZ(actorNew, hit_pos); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); if ((RANDOM_P2(1024<<5)>>5) < 512+128) { if (!hitActor->user.PlayerP) SpawnBlood(hitActor, nullptr, actor->spr.Angles.Yaw, &hit_pos); else { auto hpp = hit_pos.plusZ(20); SpawnBlood(hitActor, nullptr, actor->spr.Angles.Yaw, &hpp); } // blood comes out the other side? if ((RANDOM_P2(1024<<5)>>5) < 256) { if (!hitActor->user.PlayerP) SpawnBlood(hitActor, nullptr, actor->spr.Angles.Yaw + DAngle180, &hit_pos); if (hitActor->user.ID != TRASHCAN && hitActor->user.ID != ZILLA_RUN_R0) QueueWallBlood(hitActor, actor->spr.Angles.Yaw); //QueueWallBlood needs bullet angle. } } DoHitscanDamage(actorNew, hitActor); return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool HitscanSpriteAdjust(DSWActor* actor, walltype* hit_wall) { if (hit_wall) { actor->spr.Angles.Yaw = hit_wall->normalAngle(); } DAngle ang = actor->spr.Angles.Yaw; auto vect = ang.ToVector(); // must have this auto sect = actor->sector(); Collision coll; clipmove(actor->spr.pos, §, vect, 4, 4., 4., CLIPMASK_MISSILE, coll); if (actor->sector() != sect) ChangeActorSect(actor, sect); return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitUzi(PLAYER* pp) { DSWActor* actor = pp->actor; HitInfo hit{}; ESpriteFlags cstat = 0; uint8_t pal = 0; static int uziclock=0; int clockdiff=0; bool FireSnd = false; const int UZIFIRE_WAIT = 20; void InitUziShell(PLAYER*); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); if (uziclock > PlayClock) { uziclock = PlayClock; FireSnd = true; } clockdiff = PlayClock - uziclock; if (clockdiff > UZIFIRE_WAIT) { uziclock = PlayClock; FireSnd = true; } if (FireSnd) PlaySound(DIGI_UZIFIRE, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (RANDOM_P2(1024) < 400) InitTracerUzi(pp); double nz = (pp->actor->getOffsetZ() + pp->bob_z); double dax = 1024.; double daz = nz; DAngle daang = DAngle22_5 / 4; if (WeaponAutoAimHitscan(pp->actor, &daz, &daang, false) != nullptr) { daang += mapangle(RandomRange(24) - 12); daz += RandomRangeF(10000/256.) - 5000/256.; } else { daang = pp->angle.ang + mapangle(RandomRange(24) - 12); setFreeAimVelocity(dax, daz, pp->horizon.ZzHORIZON, 1000. - (RandomRangeF(24000/256.) - 12000/256.)); } DVector3 vect(daang.ToVector() * dax, daz); FAFhitscan(DVector3(pp->actor->spr.pos.XY(), nz), pp->cursector, vect, hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) { return 0; } SetVisHigh(); // check to see what you hit if (hit.actor() == nullptr && hit.hitWall == nullptr) { if (abs(hit.hitpos.Z - hit.hitSector->ceilingz) <= 1) { hit.hitpos.Z += 16; cstat |= (CSTAT_SPRITE_YFLIP); if ((hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)) return 0; if (SectorIsUnderwaterArea(hit.hitSector)) { WarpToSurface(hit.hitpos, &hit.hitSector); ContinueHitscan(pp, hit.hitSector, hit.hitpos, daang, vect); return 0; } } else if (abs(hit.hitpos.Z - hit.hitSector->floorz) <= 1) { if ((hit.hitSector->extra & SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE) { SpawnSplashXY(hit.hitpos, hit.hitSector); if (SectorIsDiveArea(hit.hitSector)) { WarpToUnderwater(hit.hitpos, &hit.hitSector); ContinueHitscan(pp, hit.hitSector, hit.hitpos, daang, vect); return 0; } return 0; } } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, daang, actor->user.ID); return 0; } QueueHole(hit.hitSector, hit.hitWall, hit.hitpos); } // hit a sprite? if (hit.actor() != nullptr) { auto hitActor = hit.actor(); if (hitActor->hasU()) // JBF: added null check if (hitActor->user.ID == TRASHCAN) { extern STATE s_TrashCanPain[]; PlaySound(DIGI_TRASHLID, hitActor, v3df_none); if (hitActor->user.WaitTics <= 0) { hitActor->user.WaitTics = SEC(2); ChangeState(hitActor,s_TrashCanPain); } } if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_UZI, hitActor)) return 0; } if ((hitActor->spr.extra & SPRX_BREAKABLE) && HitBreakSprite(hitActor,0)) { return 0; } if (BulletHitSprite(pp->actor, hitActor, hit.hitpos, 0)) return 0; // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hitActor); } } auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit.hitSector, hit.hitpos, daang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT, UZI_SMOKE_REPEAT); SetOwner(pp->actor, actorNew); actorNew->spr.cstat |= (cstat | CSTAT_SPRITE_YCENTER); actorNew->clipdist = 0.5; HitscanSpriteAdjust(actorNew, hit.hitWall); DoHitscanDamage(actorNew, hit.actor()); actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit.hitSector, hit.hitpos, daang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT, UZI_SPARK_REPEAT); SetOwner(pp->actor, actorNew); actorNew->user.spal = actorNew->spr.pal = pal; actorNew->spr.cstat |= (cstat | CSTAT_SPRITE_YCENTER); actorNew->clipdist = 0.5; HitscanSpriteAdjust(actorNew, hit.hitWall); if (RANDOM_P2(1024) < 100) { PlaySound(DIGI_RICHOCHET1,actorNew, v3df_none); } else if (RANDOM_P2(1024) < 100) PlaySound(DIGI_RICHOCHET2,actorNew, v3df_none); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTankShell(DSWActor* actor, PLAYER* pp) { if (!SW_SHAREWARE) PlaySound(DIGI_CANNON, pp, v3df_dontpan|v3df_doppler); auto actorNew = SpawnActor(STAT_MISSILE, 0, s_TankShell, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, TANK_SHELL_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.125, 0.125); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, actorNew->vel.X); WeaponAutoAim(actor, actorNew, DAngle22_5 / 2, false); // a bit of randomness actorNew->spr.Angles.Yaw += mapangle(RandomRange(30) - 15); actorNew->norm_ang(); UpdateChange(actorNew); if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretMicro(DSWActor* actor, PLAYER* pp) { DSWActor* plActor = pp->actor; TARGET_SORT* ts = TargetSort; DSWActor* picked = nullptr; DAngle angle; if (SW_SHAREWARE) return false; // JBF: verify auto npos = actor->spr.pos; const int MAX_TURRET_MICRO = 10; DoPickTarget(plActor, DAngle45, false); if (TargetSortCount > MAX_TURRET_MICRO) TargetSortCount = MAX_TURRET_MICRO; for (int i = 0; i < MAX_TURRET_MICRO; i++) { if (ts < &TargetSort[TargetSortCount] && ts->actor != nullptr) { picked = ts->actor; angle = (picked->spr.pos - npos).Angle(); ts++; } else { picked = nullptr; angle = actor->spr.Angles.Yaw; } npos.Z = actor->spr.pos.Z + (RandomRangeF(20) - 10); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], actor->sector(), actor->spr.pos.plusZ(10 + RandomRangeF(20)), angle, 75); SetOwner(plActor, actorNew); actorNew->spr.scale = DVector2(0.375, 0.375); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF - RandomRangeF(8) + 5); actorNew->clipdist = 4; actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Micro[0]); actorNew->user.WeaponNum = plActor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = 2; actorNew->user.floor_dist = 2; actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); actorNew->user.WaitTics = 10 + RandomRange(40); const int MICRO_ANG = 400; if (picked) { double dist = (actorNew->spr.pos.XY() - picked->spr.pos.XY()).Length(); if (dist != 0) { double zh = ActorZOfTop(picked) + (ActorSizeZ(picked) * 0.25); actorNew->vel.Z = (actorNew->vel.X * (zh - actorNew->spr.pos.Z)) / dist; } actorNew->user.WpnGoalActor = ts->actor; picked->user.Flags |= (SPR_TARGETED); picked->user.Flags |= (SPR_ATTACKED); } else { actorNew->spr.Angles.Yaw += mapangle((RandomRange(MICRO_ANG) - (MICRO_ANG / 2)) - 16); } UpdateChange(actorNew); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretRocket(DSWActor* actor, PLAYER* pp) { if (SW_SHAREWARE) return false; // JBF: verify auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, ROCKET_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, actorNew->vel.X); WeaponAutoAim(actor, actorNew, DAngle22_5 / 2, false); // a bit of randomness //actorNew->spr.angle += RandomRange(30) - 15; UpdateChange(actorNew); if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretFireball(DSWActor* actor, PLAYER* pp) { if (SW_SHAREWARE) return false; // JBF: verify auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL, s_Fireball, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, FIREBALL_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->spr.shade = -40; actorNew->vel.Z = 0; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 50; actorNew->user.ceiling_dist = (4); actorNew->user.floor_dist = (4); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, actorNew->vel.X); WeaponAutoAim(actor, actorNew, DAngle22_5 / 2, false); // a bit of randomness actorNew->spr.Angles.Yaw += mapangle(RandomRange(30) - 15); actorNew->norm_ang(); UpdateChange(actorNew); if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretRail(DSWActor* actor, PLAYER* pp) { if (SW_SHAREWARE) return false; // JBF: verify if (!pp->insector()) return 0; // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], pp->cursector, actor->spr.pos, actor->spr.Angles.Yaw, 75); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Rail[0]); actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->clipdist = 4; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->norm_ang(); } UpdateChange(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretLaser(DSWActor* actor, PLAYER* pp) { if (SW_SHAREWARE) return false; // JBF: verify if (!pp->insector()) return 0; // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, BOLT_THINMAN_R0, s_Laser, pp->cursector, actor->spr.pos, actor->spr.Angles.Yaw, 18.75); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.8125, 0.8125); actorNew->spr.shade = -15; // the slower the missile travels the less of a zvel it needs setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, 16.); actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (1); actorNew->user.floor_dist = (1); actorNew->user.Flags2 |= (SPR2_SO_MISSILE); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->clipdist = 4; if (WeaponAutoAim(actor, actorNew, DAngle22_5 / 4, false) == -1) { actorNew->norm_ang(); } UpdateChange(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSobjMachineGun(DSWActor* actor, PLAYER* pp) { HitInfo hit{}; short cstat = 0; DSWActor* spark; PlaySound(DIGI_BOATFIRE, pp, v3df_dontpan|v3df_doppler); auto npos = actor->spr.pos; double dax = 1024.; double daz = npos.Z; if (RANDOM_P2(1024) < 200) InitTracerTurret(actor, pp->actor, pp->horizon.ZzHORIZON); DAngle daang = DAngle22_5 / 2; if (WeaponAutoAimHitscan(actor, &daz, &daang, false) != nullptr) { daz += RandomRangeF(30) - 15; } else { setFreeAimVelocity(dax, daz, DAngle::fromDeg(min(pp->horizon.ZzHORIZON.Degrees(), 11.0515)), 1000 - RandomRangeF(80) + 40); daang = actor->spr.Angles.Yaw; } FAFhitscan(npos, actor->sector(), DVector3(daang.ToVector() * dax, daz), hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) { return 0; } if (hit.actor() == nullptr && hit.hitWall == nullptr) { if (abs(hit.hitpos.Z - hit.hitSector->ceilingz) <= 1) { hit.hitpos.Z += 16; cstat |= (CSTAT_SPRITE_YFLIP); if ((hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)) return 0; } else if (abs(hit.hitpos.Z - hit.hitSector->floorz) <= 1) { if ((hit.hitSector->extra & SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE) { SpawnSplashXY(hit.hitpos, hit.hitSector); return 0; } } } // hit a sprite? if (hit.actor() != nullptr) { auto hitActor = hit.actor(); if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { // spawn sparks here and pass the sprite as SO_MISSILE spark = SpawnBoatSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, daang); spark->user.Flags2 |= SPR2_SO_MISSILE; if (MissileHitMatch(spark, -1, hit.actor())) return 0; return 0; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hit.actor(), 0); return 0; } if (BulletHitSprite(pp->actor, hit.actor(), hit.hitpos, 0)) return 0; // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hit.actor()); } } spark = SpawnBoatSparks(pp, hit.hitSector, hit.hitWall, hit.hitpos, daang); DoHitscanDamage(spark, hit.actor()); return 0; } int InitSobjGun(PLAYER* pp) { short i; bool first = false; for (i = 0; pp->sop->so_actors[i] != nullptr; i++) { DSWActor* actor = pp->sop->so_actors[i]; if (!actor) continue; if (actor->spr.statnum == STAT_SO_SHOOT_POINT) { // match when firing if (SP_TAG2(actor)) { DoMatchEverything(pp, SP_TAG2(actor), -1); if (TEST_BOOL1(actor)) { SP_TAG2(actor) = 0; } } // inert shoot point if ((uint8_t)SP_TAG3(actor) == 255) return 0; if (!first) { first = true; if (SP_TAG6(actor)) DoSoundSpotMatch(SP_TAG6(actor), 1, SOUND_OBJECT_TYPE); } switch (SP_TAG3(actor)) { case 32: case 0: SpawnVis(actor, nullptr, {}, 8); SpawnBigGunFlames(actor, pp->actor, pp->sop, false); SetGunQuake(actor); InitTankShell(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 80; else pp->FirePause = SP_TAG5(actor); break; case 1: SpawnVis(actor, nullptr, {}, 32); SpawnBigGunFlames(actor, pp->actor, pp->sop, true); InitSobjMachineGun(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 10; else pp->FirePause = SP_TAG5(actor); break; case 2: if (SW_SHAREWARE) break; SpawnVis(actor, nullptr, {}, 32); InitTurretLaser(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 120; else pp->FirePause = SP_TAG5(actor); break; case 3: if (SW_SHAREWARE) break; SpawnVis(actor, nullptr, {}, 32); InitTurretRail(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 120; else pp->FirePause = SP_TAG5(actor); break; case 4: if (SW_SHAREWARE) break; SpawnVis(actor, nullptr, {}, 32); InitTurretFireball(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 20; else pp->FirePause = SP_TAG5(actor); break; case 5: if (SW_SHAREWARE) break; SpawnVis(actor, nullptr, {}, 32); InitTurretRocket(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 100; else pp->FirePause = SP_TAG5(actor); break; case 6: if (SW_SHAREWARE) break; SpawnVis(actor, nullptr, {}, 32); InitTurretMicro(actor, pp); if (!SP_TAG5(actor)) pp->FirePause = 100; else pp->FirePause = SP_TAG5(actor); break; } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* SpawnBoatSparks(PLAYER* pp, sectortype* hit_sect, walltype* hit_wall, const DVector3& hitpos, DAngle hit_ang) { auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT + 0.1875, UZI_SMOKE_REPEAT + 0.1875); SetOwner(pp->actor, actorNew); actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.hitag = LUMINOUS; //Always full brightness // Sprite starts out with center exactly on wall. // This moves it back enough to see it at all angles. actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT + 0.15626, UZI_SPARK_REPEAT + 0.15625); SetOwner(pp->actor, actorNew); actorNew->user.spal = actorNew->spr.pal = PALETTE_DEFAULT; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); if (RANDOM_P2(1024) < 100) PlaySound(DIGI_RICHOCHET1, actorNew, v3df_none); return actorNew; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnSwordSparks(PLAYER* pp, sectortype* hit_sect, walltype* hit_wall, const DVector3& hitpos, DAngle hit_ang) { DSWActor* actor = pp->actor; auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.34375, 0.3125); SetOwner(pp->actor, actorNew); actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.hitag = LUMINOUS; //Always full brightness // Sprite starts out with center exactly on wall. // This moves it back enough to see it at all angles. actorNew->clipdist = 2; if (hit_wall != nullptr) HitscanSpriteAdjust(actorNew, hit_wall); actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(0.34375, 0.3125); SetOwner(pp->actor, actorNew); actorNew->user.spal = actorNew->spr.pal = PALETTE_DEFAULT; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); if (actor->user.WeaponNum == WPN_FIST) actorNew->spr.cstat |= (CSTAT_SPRITE_INVISIBLE); actorNew->clipdist = 2; if (hit_wall != nullptr) HitscanSpriteAdjust(actorNew, hit_wall); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* SpawnTurretSparks(sectortype* hit_sect, walltype* hit_wall, const DVector3& hitpos, DAngle hit_ang) { auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT + 0.1875, UZI_SMOKE_REPEAT + 0.1875); actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.hitag = LUMINOUS; //Always full brightness // Sprite starts out with center exactly on wall. // This moves it back enough to see it at all angles. actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT + 0.15626, UZI_SPARK_REPEAT + 0.15625); actorNew->user.spal = actorNew->spr.pal = PALETTE_DEFAULT; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); if (RANDOM_P2(1024) < 100) PlaySound(DIGI_RICHOCHET1, actorNew, v3df_none); return actorNew; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* SpawnShotgunSparks(PLAYER* pp, sectortype* hit_sect, walltype* hit_wall, const DVector3& hitpos, DAngle hit_ang) { const double SHOTGUN_SMOKE_REPEAT = 0.28125; auto actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hitpos, hit_ang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT, UZI_SPARK_REPEAT); SetOwner(pp->actor, actorNew); actorNew->user.spal = actorNew->spr.pal = PALETTE_DEFAULT; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); actorNew = SpawnActor(STAT_MISSILE, SHOTGUN_SMOKE, s_ShotgunSmoke, hit_sect, hitpos, hit_ang, 0); actorNew->spr.scale = DVector2(SHOTGUN_SMOKE_REPEAT, SHOTGUN_SMOKE_REPEAT); SetOwner(pp->actor, actorNew); actorNew->spr.cstat |= (CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER); actorNew->spr.hitag = LUMINOUS; //Always full brightness // Sprite starts out with center exactly on wall. // This moves it back enough to see it at all angles. actorNew->clipdist = 2; HitscanSpriteAdjust(actorNew, hit_wall); return actorNew; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitTurretMgun(SECTOR_OBJECT* sop) { HitInfo hit{}; short cstat = 0; PlaySound(DIGI_BOATFIRE, sop->pmid, v3df_dontpan|v3df_doppler); for (int i = 0; sop->so_actors[i] != nullptr; i++) { DAngle daang; DSWActor* actor = sop->so_actors[i]; if (!actor) continue; if (actor->spr.statnum == STAT_SO_SHOOT_POINT) { auto npos = actor->spr.pos; double daz = npos.Z; // if its not operated by a player if (sop->Animator) { // only auto aim for Z daang = DAngle90; auto hitt = WeaponAutoAimHitscan(actor, &daz, &daang, false); hit.hitActor = hitt; if (hitt != nullptr) { DAngle delta = absangle(actor->spr.Angles.Yaw, daang); if (delta > DAngle22_5) { // don't shoot if greater than 128 return 0; } else if (delta > mapangle(24)) { // always shoot the ground when tracking // and not close WeaponHitscanShootFeet(actor, hitt, &daz); daang = actor->spr.Angles.Yaw + RandomAngle(22.5 / 4) - DAngle22_5 / 8; } else { // randomize the z for shots daz += RandomRangeF(120) - 60; // never auto aim the angle daang = actor->spr.Angles.Yaw + RandomAngle(22.5 / 2) - DAngle22_5 / 4; } } } else { daang = DAngle22_5 / 2; if (WeaponAutoAimHitscan(actor, &daz, &daang, false) != nullptr) { daz += RandomRangeF(30) - 15; } } DVector3 vect(daang.ToVector() * 1024, daz); FAFhitscan(npos, actor->sector(), vect, hit, CLIPMASK_MISSILE); if (RANDOM_P2(1024) < 400) { InitTracerAutoTurret(sop->so_actors[i], vect); } if (hit.hitSector == nullptr) continue; if (hit.actor() == nullptr && hit.hitWall == nullptr) { if (abs(hit.hitpos.Z - hit.hitSector->ceilingz) <= 1) { hit.hitpos.Z += 16; cstat |= (CSTAT_SPRITE_YFLIP); if ((hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)) continue; } else if (abs(hit.hitpos.Z - hit.hitSector->floorz) <= 1) { if ((hit.hitSector->extra & SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE) { SpawnSplashXY(hit.hitpos, hit.hitSector); continue; } } } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, daang, 0); continue; } QueueHole(hit.hitSector, hit.hitWall, hit.hitpos); } // hit a sprite? if (hit.actor() != nullptr) { auto hitActor = hit.actor(); if (hitActor->spr.lotag == TAG_SPRITE_HIT_MATCH) { if (MissileHitMatch(nullptr, WPN_UZI, hit.actor())) continue; } if ((hitActor->spr.extra & SPRX_BREAKABLE)) { HitBreakSprite(hit.actor(), 0); continue; } if (BulletHitSprite(actor, hit.actor(), hit.hitpos, 0)) continue; // hit a switch? if ((hitActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (hitActor->spr.lotag || hitActor->spr.hitag)) { ShootableSwitch(hit.actor()); } } auto j = SpawnTurretSparks(hit.hitSector, hit.hitWall, hit.hitpos, daang); DoHitscanDamage(j, hit.actor()); } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyUzi(DSWActor* actor) { DAngle daang; HitInfo hit{}; double zh; void InitUziShell(PLAYER*); static short alternate; // Make sprite shade brighter actor->user.Vis = 128; SetActorZ(actor, actor->spr.pos); if (actor->user.ID == ZILLA_RUN_R0) { zh = ActorZOfTop(actor) + 20; } else { zh = ActorSizeZ(actor) * 0.5; } double daz = actor->spr.pos.Z - zh; if (AimHitscanToTarget(actor, &daz, &daang, 200) != nullptr) { // set angle to player and also face player when attacking actor->spr.Angles.Yaw = daang; daang += mapangle(RandomRange(24) - 12); daz += RandomRange(40 * 256) / 256. - 20; } else { // couldn't shoot target for some reason // don't bother wasting processing 50% of the time if (RANDOM_P2(1024) < 512) return 0; daz = 0; daang = actor->spr.Angles.Yaw + RandomAngle(DAngle22_5) - DAngle22_5/2; } // todo: confirm the ToVector factor. FAFhitscan(actor->spr.pos.plusZ(-zh), actor->sector(), DVector3(daang.ToVector() * 1024, daz), hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) return 0; if (RANDOM_P2(1024<<4)>>4 > 700) { if (actor->user.ID == TOILETGIRL_R0 || actor->user.ID == WASHGIRL_R0 || actor->user.ID == CARGIRL_R0) SpawnShell(actor,-3); else SpawnShell(actor,-2); // Enemy Uzi shell } if ((alternate++)>2) alternate = 0; if (!alternate) { if (actor->spr.pal == PALETTE_PLAYER3 || actor->spr.pal == PALETTE_PLAYER5 || actor->spr.pal == PAL_XLAT_LT_GREY || actor->spr.pal == PAL_XLAT_LT_TAN) PlaySound(DIGI_M60, actor, v3df_none); else PlaySound(DIGI_NINJAUZIATTACK, actor, v3df_none); } if (hit.hitWall != nullptr) { if (hit.hitWall->twoSided()) { if ((hit.hitWall->nextSector()->ceilingstat & CSTAT_SECTOR_SKY)) { if (hit.hitpos.Z < hit.hitWall->nextSector()->ceilingz) { return 0; } } } if (hit.hitWall->lotag == TAG_WALL_BREAK) { HitBreakWall(hit.hitWall, hit.hitpos, daang, actor->user.ID); return 0; } QueueHole(hit.hitSector, hit.hitWall, hit.hitpos); } if (hit.actor() != nullptr) { if (BulletHitSprite(actor, hit.actor(), hit.hitpos, 0)) return 0; } auto actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE+2, s_UziSmoke, hit.hitSector, hit.hitpos, daang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT, UZI_SMOKE_REPEAT); if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->user.WaitTics = 63; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 2; actorNew = SpawnActor(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit.hitSector, hit.hitpos, daang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SMOKE_REPEAT, UZI_SMOKE_REPEAT); SetOwner(actor, actorNew); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 0.5; HitscanSpriteAdjust(actorNew, hit.hitWall); DoHitscanDamage(actorNew, hit.actor()); actorNew = SpawnActor(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit.hitSector, hit.hitpos, daang, 0); actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(UZI_SPARK_REPEAT, UZI_SPARK_REPEAT); SetOwner(actor, actorNew); actorNew->user.spal = actorNew->spr.pal; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->clipdist = 0.5; HitscanSpriteAdjust(actorNew, hit.hitWall); if (RANDOM_P2(1024) < 100) { PlaySound(DIGI_RICHOCHET1,actorNew, v3df_none); } else if (RANDOM_P2(1024) < 100) PlaySound(DIGI_RICHOCHET2,actorNew, v3df_none); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitGrenade(PLAYER* pp) { DSWActor* actor = pp->actor; double zvel; bool auto_aim = false; DoPlayerBeginRecoil(pp, GRENADE_RECOIL_AMT); PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); PlaySound(DIGI_30MMFIRE, pp, v3df_dontpan|v3df_doppler); // Make sprite shade brighter actor->user.Vis = 128; if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, GRENADE, &s_Grenade[0][0], pp->cursector, pos, pp->angle.ang, GRENADE_VELOCITY); // don't throw it as far if crawling if (pp->Flags & (PF_CRAWLING)) { actorNew->vel.Z *= 0.75; } actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Grenade[0]); actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -15; //actorNew->clipdist = 5; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (3); actorNew->user.floor_dist = (3); actorNew->user.Counter = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK); if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 800); actorNew->spr.Angles.Yaw -= DAngle90; // don't do smoke for this movement actorNew->user.Flags |= (SPR_BOUNCE); MissileSetPos(actorNew, DoGrenade, 1000); actorNew->user.Flags &= ~(SPR_BOUNCE); actor->clipdist = oclipdist; zvel = actorNew->vel.Z; if (WeaponAutoAim(pp->actor, actorNew, DAngle22_5 / 4, false) >= 0) { auto_aim = true; } actorNew->vel.Z = zvel; UpdateChange(actorNew); if (!auto_aim) { // adjust xvel according to player velocity actorNew->user.change += pp->vect; } actorNew->user.Counter2 = true; // Phosphorus Grenade return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitSpriteGrenade(DSWActor* actor) { PlaySound(DIGI_30MMFIRE, actor, v3df_dontpan|v3df_doppler); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, GRENADE, &s_Grenade[0][0], actor->sector(), actor->spr.pos.plusZ(-40), actor->spr.Angles.Yaw, GRENADE_VELOCITY); actorNew->user.RotNum = 5; NewStateGroup(actorNew, &sg_Grenade[0]); actorNew->user.Flags |= (SPR_XFLIP_TOGGLE); if (actor->user.ID == ZOMBIE_RUN_R0) SetOwner(GetOwner(actor), actorNew); else SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -15; //actorNew->clipdist = 5; actorNew->clipdist = 2; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (3); actorNew->user.floor_dist = (3); actorNew->user.Counter = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat |= (CSTAT_SPRITE_BLOCK); actorNew->vel.Z = 2000. / 256.; UpdateChange(actorNew); actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 800); actorNew->spr.Angles.Yaw -= DAngle90; // don't do smoke for this movement actorNew->user.Flags |= (SPR_BOUNCE); MissileSetPos(actorNew, DoGrenade, 400); actorNew->user.Flags &= ~(SPR_BOUNCE); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitMine(PLAYER* pp) { DSWActor* actor = pp->actor; PlayerUpdateAmmo(pp, actor->user.WeaponNum, -1); PlaySound(DIGI_MINETHROW, pp, v3df_dontpan|v3df_doppler); if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 8); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, MINE, s_Mine, pp->cursector, pos, pp->angle.ang, MINE_VELOCITY); SetOwner(pp->actor, actorNew); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -15; actorNew->clipdist = 8; setFreeAimVelocity(actorNew->vel.X, actorNew->vel.Z, pp->horizon.ZzHORIZON, HORIZ_MULTF); actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = (5); actorNew->user.floor_dist = (5); actorNew->user.Counter = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.spal = actorNew->spr.pal = actor->user.spal; // Set sticky color if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); MissileSetPos(actorNew, DoMine, 800); UpdateChange(actorNew, 0.5); double dot = pp->vect.dot(pp->angle.ang.ToVector()); // don't adjust for strafing // not really sure what to do here as the original formula was very likely to overflow, creating a Q0.32 value. if (abs(dot) > 10000./0xffffffff) { // adjust xvel according to player velocity actorNew->user.change += 2 * pp->vect; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyMine(DSWActor* actor) { PlaySound(DIGI_MINETHROW, actor, v3df_dontpan|v3df_doppler); // Spawn a shot // Inserting and setting up variables auto actorNew = SpawnActor(STAT_MISSILE, MINE, s_Mine, actor->sector(), actor->spr.pos.plusZ(-40), actor->spr.Angles.Yaw, MINE_VELOCITY); SetOwner(actor, actorNew); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.scale = DVector2(0.5, 0.5); actorNew->spr.shade = -15; actorNew->clipdist = 8; actorNew->user.WeaponNum = actor->user.WeaponNum; actorNew->user.Radius = 200; actorNew->user.ceiling_dist = 5; actorNew->user.floor_dist = 5; actorNew->user.Counter = 0; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actorNew->user.spal = actorNew->spr.pal = actor->user.spal; // Set sticky color if (SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); MissileSetPos(actorNew, DoMine, 300); actorNew->spr.Angles.Yaw -= DAngle90; MissileSetPos(actorNew, DoMine, 300); actorNew->spr.Angles.Yaw += DAngle90; actorNew->user.change. Z = -5000 / 256.; UpdateChangeXY(actorNew); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int HelpMissileLateral(DSWActor* actor, int dist) { auto old_xvel = actor->vel.X; auto oclipdist = actor->clipdist; actor->vel.X = dist * maptoworld; // not worth changing 28 call locations... auto vec = actor->spr.Angles.Yaw.ToVector() * actor->vel.X; actor->clipdist = 2; actor->user.coll = move_missile(actor, DVector3(vec, 0), 16, 16, 0, 1); actor->vel.X = old_xvel; actor->clipdist = oclipdist; actor->backuppos(); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitFireball(PLAYER* pp) { DSWActor* actor = pp->actor; PlayerUpdateAmmo(pp, WPN_HOTHEAD, -1); PlaySound(DIGI_HEADFIRE, pp, v3df_none); // Make sprite shade brighter actor->user.Vis = 128; if (!pp->insector()) return 0; auto pos = pp->actor->getPosWithOffsetZ().plusZ(pp->bob_z + 15); auto actorNew = SpawnActor(STAT_MISSILE, FIREBALL1, s_Fireball, pp->cursector, pos, pp->angle.ang, FIREBALL_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.625, 0.625); actorNew->spr.shade = -40; actorNew->clipdist = 2; SetOwner(pp->actor, actorNew); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.Radius = 100; actorNew->user.ceiling_dist = (6); actorNew->user.floor_dist = (6); double zvel = 0.; setFreeAimVelocity(actorNew->vel.X, zvel, pp->horizon.ZzHORIZON, 120.); // at certain angles the clipping box was big enough to block the // initial positioning of the fireball. auto oclipdist = actor->clipdist; actor->clipdist = 0; actorNew->spr.Angles.Yaw += DAngle90; HelpMissileLateral(actorNew, 2100); actorNew->spr.Angles.Yaw -= DAngle90; if (pp->Flags & (PF_DIVING) || SpriteInUnderwaterArea(actorNew)) actorNew->user.Flags |= (SPR_UNDERWATER); if (TestMissileSetPos(actorNew, DoFireball, 1200, zvel * (1375. / 2048.))) { actor->clipdist = oclipdist; KillActor(actorNew); return 0; } actor->clipdist = oclipdist; actorNew->vel.Z = 0.5; if (WeaponAutoAimZvel(pp->actor, actorNew, &zvel, DAngle22_5 / 4, false) == -1) { actorNew->spr.Angles.Yaw -= mapangle(9); } UpdateChangeXY(actorNew); actorNew->user.change.Z = zvel; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int InitEnemyFireball(DSWActor* actor) { static DAngle lat_ang[] = { DAngle90, -DAngle90 }; DSWActor* targetActor = actor->user.targetActor; if (!targetActor) return 0; PlaySound(DIGI_FIREBALL1, actor, v3df_none); // get angle to player and also face player when attacking actor->spr.Angles.Yaw = (targetActor->spr.pos.XY() - actor->spr.pos.XY()).Angle(); double size_z = ActorSizeZ(actor) * 0.625; double nz = actor->spr.pos.Z - size_z + 4; auto change = actor->spr.Angles.Yaw.ToVector() * GORO_FIREBALL_VELOCITY; double lastvel = 0; for (int i = 0; i < 2; i++) { auto actorNew = SpawnActor(STAT_MISSILE, GORO_FIREBALL, s_Fireball, actor->sector(), DVector3(actor->spr.pos, nz), actor->spr.Angles.Yaw, GORO_FIREBALL_VELOCITY); actorNew->spr.hitag = LUMINOUS; //Always full brightness actorNew->spr.scale = DVector2(0.34375, 0.3125); actorNew->spr.shade = -40; SetOwner(actor, actorNew); actorNew->vel.Z = 0; actorNew->clipdist = 1; actorNew->spr.Angles.Yaw += lat_ang[i]; HelpMissileLateral(actorNew, 500); actorNew->spr.Angles.Yaw -= lat_ang[i]; actorNew->user.change.XY() = change; MissileSetPos(actorNew, DoFireball, 700); if (i == 0) { // find the distance to the target (player) double dist = (actorNew->spr.pos.XY() - targetActor->spr.pos.XY()).Length(); // Determine target Z value double targ_z = targetActor->spr.pos.Z - ActorSizeZ(actor) * 0.5; // (velocity * difference between the target and the throwing star) / // distance if (dist != 0) { actorNew->vel.Z = ((GORO_FIREBALL_VELOCITY * (targ_z - actorNew->spr.pos.Z)) / dist); actorNew->user.change.Z = actorNew->vel.Z; } // back up first one lastvel = actorNew->vel.Z; } else { // use the first calculations so the balls stay together actorNew->user.change.Z = lastvel; actorNew->vel.Z = lastvel; } } return 0; } /////////////////////////////////////////////////////////////////////////////// // for hitscans or other uses /////////////////////////////////////////////////////////////////////////////// bool WarpToUnderwater(DVector3& pos, sectortype** psectu) { int i; auto sectu = *psectu; bool Found = false; DVector2 spos; DSWActor* overActor = nullptr; DSWActor* underActor = nullptr; // 0 not valid for water match tags if (!sectu->hasU() || sectu->number == 0) return false; // search for DIVE_AREA "over" sprite for reference point SWStatIterator it(STAT_DIVE_AREA); while ((overActor = it.Next())) { if ((overActor->sector()->extra & SECTFX_DIVE_AREA) && overActor->sector()->hasU() && overActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); Found = false; // search for UNDERWATER "under" sprite for reference point it.Reset(STAT_UNDERWATER); while ((underActor = it.Next())) { if ((underActor->sector()->extra & SECTFX_UNDERWATER) && underActor->sector()->hasU() && underActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); // get the offset from the sprite spos = overActor->spr.pos.XY() - pos.XY(); // update to the new x y position pos.XY() = underActor->spr.pos - spos; auto over = overActor->sector(); auto under = underActor->sector(); if (GetOverlapSector(pos.XY(), &over, &under) == 2) { *psectu = under; } else { *psectu = under; } pos.Z = underActor->sector()->ceilingz+ 1; return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool WarpToSurface(DVector3& pos, sectortype** psectu) { int i; auto sectu = *psectu; DSWActor* overActor = nullptr; DSWActor* underActor = nullptr; bool Found = false; // 0 not valid for water match tags if (!sectu->hasU() || sectu->number == 0) return false; // search for UNDERWATER "under" sprite for reference point SWStatIterator it(STAT_UNDERWATER); while ((underActor = it.Next())) { if ((underActor->sector()->extra & SECTFX_UNDERWATER) && underActor->sector()->hasU() && underActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); Found = false; // search for DIVE_AREA "over" sprite for reference point it.Reset(STAT_DIVE_AREA); while ((overActor = it.Next())) { if ((overActor->sector()->extra & SECTFX_DIVE_AREA) && overActor->sector()->hasU() && overActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); // get the offset from the under sprite DVector2 spos = underActor->spr.pos.XY() - pos.XY(); // update to the new x y position pos.XY() = overActor->spr.pos.XY() - spos; auto over = overActor->sector(); auto under = underActor->sector(); if (GetOverlapSector(pos.XY(), &over, &under)) { *psectu = over; } pos.Z = overActor->sector()->floorz- 2; return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool SpriteWarpToUnderwater(DSWActor* actor) { int i; auto sectu = actor->sector(); bool Found = false; DSWActor* overActor = nullptr; DSWActor* underActor = nullptr; // 0 not valid for water match tags if (!sectu->hasU() || sectu->number == 0) return false; // search for DIVE_AREA "over" sprite for reference point SWStatIterator it(STAT_DIVE_AREA); while ((overActor = it.Next())) { if ((overActor->sector()->extra & SECTFX_DIVE_AREA) && overActor->sector()->hasU() && overActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); Found = false; // search for UNDERWATER "under" sprite for reference point it.Reset(STAT_UNDERWATER); while ((underActor = it.Next())) { if ((underActor->sector()->extra & SECTFX_UNDERWATER) && underActor->sector()->hasU() && underActor->sector()->number == sectu->number) { Found = true; break; } } ASSERT(Found); // update to the new x y position actor->spr.pos.XY() += (underActor->spr.pos.XY() - overActor->spr.pos.XY()); auto over = overActor->sector(); auto under = underActor->sector(); if (GetOverlapSector(actor->spr.pos, &over, &under) == 2) { ChangeActorSect(actor, under); } else { ChangeActorSect(actor, over); } actor->spr.pos.Z = underActor->sector()->ceilingz + actor->user.ceiling_dist+ 1; actor->backuppos(); return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool SpriteWarpToSurface(DSWActor* actor) { auto sectu = actor->sector(); int sx, sy; DSWActor* overActor = nullptr; DSWActor* underActor = nullptr; bool Found = false; // 0 not valid for water match tags if (!sectu->hasU() || sectu->number == 0) return false; // search for UNDERWATER "under" sprite for reference point SWStatIterator it(STAT_UNDERWATER); while ((underActor = it.Next())) { if ((underActor->sector()->extra & SECTFX_UNDERWATER) && underActor->sector()->hasU() && underActor->sector()->number == sectu->number) { Found = true; break; } } if (!Found) return false; if (underActor->spr.lotag == 0) return false; Found = false; // search for DIVE_AREA "over" sprite for reference point it.Reset(STAT_DIVE_AREA); while ((overActor = it.Next())) { if ((overActor->sector()->extra & SECTFX_DIVE_AREA) && overActor->sector()->hasU() && overActor->sector()->number == sectu->number) { Found = true; break; } } if (!Found) return false; // get the offset from the under sprite // update to the new x y position actor->spr.pos += overActor->spr.pos.XY() - underActor->spr.pos.XY(); auto over = overActor->sector(); auto under = underActor->sector(); if (GetOverlapSector(actor->spr.pos, &over, &under)) { ChangeActorSect(actor, over); } actor->spr.pos.Z = overActor->sector()->floorz - 2; // set z range and wade depth so we know how high to set view DoActorZrange(actor); MissileWaterAdjust(actor); actor->backuppos(); return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnSplash(DSWActor* actor) { auto sectu = actor->sector(); sectortype* sectp = actor->sector(); if (Prediction) return 0; if (sectu && ((sectp->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_NONE)) return 0; if (sectu && (sectp->floorstat & CSTAT_SECTOR_SKY)) return 0; PlaySound(DIGI_SPLASH1, actor, v3df_none); DoActorZrange(actor); MissileWaterAdjust(actor); auto actorNew = SpawnActor(STAT_MISSILE, SPLASH, s_Splash, actor->sector(), DVector3(actor->spr.pos.XY(), actor->user.loz), actor->spr.Angles.Yaw, 0); if (sectu && (sectp->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) actorNew->user.spal = actorNew->spr.pal = PALETTE_RED_LIGHTING; actorNew->spr.scale = DVector2(0.703125, 0.65625); actorNew->spr.shade = actor->sector()->floorshade - 10; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnSplashXY(const DVector3& pos, sectortype* sectp) { if (Prediction) return 0; if (sectp->hasU() && ((sectp->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_NONE)) return 0; if (sectp->hasU() && (sectp->floorstat & CSTAT_SECTOR_SKY)) return 0; auto actorNew = SpawnActor(STAT_MISSILE, SPLASH, s_Splash, sectp, pos, nullAngle, 0); if (sectp->hasU() && (sectp->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) actorNew->user.spal = actorNew->spr.pal = PALETTE_RED_LIGHTING; actorNew->spr.scale = DVector2(0.703125, 0.65625); actorNew->spr.shade = actorNew->sector()->floorshade - 10; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool MissileHitDiveArea(DSWActor* actor) { // correctly set underwater bit for missiles // in Stacked water areas. if (FAF_ConnectArea(actor->sector())) { if (SectorIsUnderwaterArea(actor->sector())) actor->user.Flags |= (SPR_UNDERWATER); else actor->user.Flags &= ~(SPR_UNDERWATER); } if (actor->user.coll.type == kHitSector) { auto hit_sect = actor->user.coll.hitSector; if (SpriteInDiveArea(actor)) { // make sure you are close to the floor if (actor->spr.pos.Z < (actor->user.hiz + actor->user.loz) * 0.5) return false; // Check added by Jim because of sprite bridge over water if (actor->spr.pos.Z < hit_sect->floorz - 20) return false; actor->user.Flags |= (SPR_UNDERWATER); SpawnSplash(actor); SpriteWarpToUnderwater(actor); actor->user.coll.setNone(); PlaySound(DIGI_PROJECTILEWATERHIT, actor, v3df_none); return true; } else if (SpriteInUnderwaterArea(actor)) { // make sure you are close to the ceiling if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) return false; actor->user.Flags &= ~(SPR_UNDERWATER); if (!SpriteWarpToSurface(actor)) { return false; } SpawnSplash(actor); actor->user.coll.setNone(); return true; } } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* SpawnBubble(DSWActor* actor) { if (Prediction) return nullptr; auto actorNew = SpawnActor(STAT_MISSILE, BUBBLE, s_Bubble, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); double scale = (8 + (RANDOM_P2(8 << 8) >> 8)) * REPEAT_SCALE; actorNew->spr.scale = DVector2(scale, scale); // notreallypos actorNew->user.pos.XY() = actorNew->spr.scale; actorNew->user.ceiling_dist = 1; actorNew->user.floor_dist = 1; actorNew->spr.shade = actor->sector()->floorshade - 10; actorNew->user.WaitTics = 120 * 120; actorNew->vel.Z = 2; actorNew->clipdist = 0.75; actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->user.Flags |= (SPR_UNDERWATER); actorNew->spr.shade = -60; // Make em brighter return actorNew; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoVehicleSmoke(DSWActor* actor) { actor->spr.pos.XY() += actor->user.change.XY(); actor->spr.pos.Z -= actor->vel.Z; return false; } int DoWaterSmoke(DSWActor* actor) { actor->spr.pos.Z -= actor->vel.Z; return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnVehicleSmoke(DSWActor* actor) { if (MoveSkip2 != 0) return false; auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_VehicleSmoke, actor->sector(), actor->spr.pos.plusZ(-RANDOM_P2F(8, 8)), actor->spr.Angles.Yaw, 0); actorNew->user.WaitTics = 1*120; actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(1, 1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) < 512) actorNew->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) < 512) actorNew->spr.cstat |= (CSTAT_SPRITE_YFLIP); actorNew->spr.Angles.Yaw = RandomAngle(); actorNew->vel.X = RandomRangeF(2); UpdateChangeXY(actorNew); actorNew->vel.Z = 4 + RandomRangeF(4); return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int SpawnSmokePuff(DSWActor* actor) { auto actorNew = SpawnActor(STAT_MISSILE, PUFF, s_WaterSmoke, actor->sector(), actor->spr.pos.plusZ(-RANDOM_P2F(8, 8)), actor->spr.Angles.Yaw, 0); actorNew->user.WaitTics = 1*120; actorNew->spr.shade = -40; actorNew->spr.scale = DVector2(1, 1); actorNew->spr.cstat |= (CSTAT_SPRITE_YCENTER); actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); if (RANDOM_P2(1024) < 512) actorNew->spr.cstat |= (CSTAT_SPRITE_XFLIP); if (RANDOM_P2(1024) < 512) actorNew->spr.cstat |= (CSTAT_SPRITE_YFLIP); actorNew->spr.Angles.Yaw = RandomAngle(); actorNew->vel.X = RandomRangeF(2); UpdateChangeXY(actorNew); actorNew->vel.Z = 4 + RandomRangeF(4); return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBubble(DSWActor* actor) { actor->spr.pos.Z -= actor->vel.Z; actor->vel.Z += 0.25; if (actor->vel.Z > 3) actor->vel.Z = 3; // notreallypos actor->user.pos.X += REPEAT_SCALE; actor->user.pos.Y += REPEAT_SCALE; if (actor->user.pos.X > 0.5) { actor->user.pos.X = 0.5; actor->user.pos.Y = 0.5; } actor->spr.scale.X = (actor->user.pos.X + ((RANDOM_P2(8 << 8) >> 8) - 4) * REPEAT_SCALE); actor->spr.scale.Y = (actor->user.pos.Y + ((RANDOM_P2(8 << 8) >> 8) - 4) * REPEAT_SCALE); if (actor->spr.pos.Z < actor->sector()->ceilingz) { if (SectorIsUnderwaterArea(actor->user.hi_sectp)) { if (!SpriteWarpToSurface(actor)) { KillActor(actor); return true; } actor->user.Flags &= ~(SPR_UNDERWATER); // stick around above water for this long actor->user.WaitTics = (RANDOM_P2(64 << 8) >> 8); } else { KillActor(actor); return true; } } if (!(actor->user.Flags & SPR_UNDERWATER)) { if ((actor->user.WaitTics -= MISSILEMOVETICS) <= 0) { KillActor(actor); return true; } } else // just in case its stuck somewhere kill it after a while { if ((actor->user.WaitTics -= MISSILEMOVETICS) <= 0) { KillActor(actor); return true; } } return false; } //--------------------------------------------------------------------------- // // this needs to be called before killsprite // whenever killing a sprite that you aren't completely sure what it is, like // with the drivables, copy sectors, break sprites, etc // //--------------------------------------------------------------------------- void SpriteQueueDelete(DSWActor* actor) { size_t i; for (i = 0; i < MAX_STAR_QUEUE; i++) if (StarQueue[i] == actor) StarQueue[i] = nullptr; for (i = 0; i < MAX_HOLE_QUEUE; i++) if (HoleQueue[i] == actor) HoleQueue[i] = nullptr; for (i = 0; i < MAX_WALLBLOOD_QUEUE; i++) if (WallBloodQueue[i] == actor) WallBloodQueue[i] = nullptr; for (i = 0; i < MAX_FLOORBLOOD_QUEUE; i++) if (FloorBloodQueue[i] == actor) FloorBloodQueue[i] = nullptr; for (i = 0; i < MAX_GENERIC_QUEUE; i++) if (GenericQueue[i] == actor) GenericQueue[i] = nullptr; for (i = 0; i < MAX_LOWANGS_QUEUE; i++) if (LoWangsQueue[i] == actor) LoWangsQueue[i] = nullptr; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void QueueReset(void) { size_t i; StarQueueHead=0; HoleQueueHead=0; WallBloodQueueHead=0; FloorBloodQueueHead=0; GenericQueueHead=0; LoWangsQueueHead=0; for (i = 0; i < MAX_STAR_QUEUE; i++) StarQueue[i] = nullptr; for (i = 0; i < MAX_HOLE_QUEUE; i++) HoleQueue[i] = nullptr; for (i = 0; i < MAX_WALLBLOOD_QUEUE; i++) WallBloodQueue[i] = nullptr; for (i = 0; i < MAX_FLOORBLOOD_QUEUE; i++) FloorBloodQueue[i] = nullptr; for (i = 0; i < MAX_GENERIC_QUEUE; i++) GenericQueue[i] = nullptr; for (i = 0; i < MAX_LOWANGS_QUEUE; i++) LoWangsQueue[i] = nullptr; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool TestDontStick(DSWActor* actor, walltype* hit_wall) { if (hit_wall == nullptr) { ASSERT(actor != nullptr); if (actor->user.coll.type != kHitWall) return true; // ain't got a wall here. hit_wall = actor->user.coll.hitWall; } if ((hit_wall->extra & WALLFX_DONT_STICK)) return true; // if blocking red wallo if ((hit_wall->cstat & CSTAT_WALL_BLOCK) && hit_wall->twoSided()) return true; return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool TestDontStickSector(sectortype* hit_sect) { if ((hit_sect->extra & (SECTFX_DYNAMIC_AREA|SECTFX_SECTOR_OBJECT))) return true; return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int QueueStar(DSWActor* actor) { if (TestDontStick(actor, nullptr)) { KillActor(actor); return -1; } // can and should kill the user portion of the star if (StarQueue[StarQueueHead] == nullptr) { // new star actor->clearUser(); change_actor_stat(actor, STAT_STAR_QUEUE); StarQueue[StarQueueHead] = actor; } else { // move old star to new stars place auto osp = StarQueue[StarQueueHead]; osp->spr.pos = actor->spr.pos; ChangeActorSect(osp, actor->sector()); KillActor(actor); actor = osp; } StarQueueHead = (StarQueueHead+1) & (MAX_STAR_QUEUE-1); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void QueueHole(sectortype* hit_sect, walltype* hit_wall, const DVector3& pos) { short w,nw; DSWActor* spawnedActor; if (TestDontStick(nullptr, hit_wall)) return; if (HoleQueue[HoleQueueHead] == nullptr) HoleQueue[HoleQueueHead] = spawnedActor = insertActor(hit_sect, STAT_HOLE_QUEUE); else spawnedActor = HoleQueue[HoleQueueHead]; HoleQueueHead = (HoleQueueHead+1) & (MAX_HOLE_QUEUE-1); spawnedActor->spr.scale = DVector2(0.25, 0.25); spawnedActor->spr.cstat = 0; spawnedActor->spr.pal = 0; spawnedActor->spr.shade = 0; spawnedActor->spr.extra = 0; spawnedActor->clipdist = 0; spawnedActor->spr.xoffset = spawnedActor->spr.yoffset = 0; spawnedActor->spr.pos = pos; spawnedActor->spr.picnum = 2151; ChangeActorSect(spawnedActor, hit_sect); ASSERT(spawnedActor->spr.statnum != MAXSTATUS); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_WALL); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ONE_SIDE); spawnedActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); spawnedActor->spr.cstat2 |= CSTAT2_SPRITE_DECAL; spawnedActor->spr.Angles.Yaw = hit_wall->normalAngle(); // move it back some auto vec = spawnedActor->spr.Angles.Yaw.ToVector(); auto sect = spawnedActor->sector(); Collision coll; clipmove(spawnedActor->spr.pos, §, vec, 0., 0., 0., CLIPMASK_MISSILE, coll, 1); if (spawnedActor->sector() != sect) ChangeActorSect(spawnedActor, sect); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- enum { FLOORBLOOD_RATE = 30 }; ANIMATOR DoFloorBlood; STATE s_FloorBlood1[] = { {FLOORBLOOD1, SF_QUICK_CALL, DoFloorBlood, &s_FloorBlood1[1]}, {FLOORBLOOD1, FLOORBLOOD_RATE, NullAnimator, &s_FloorBlood1[0]}, }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int QueueFloorBlood(DSWActor* actor) { sectortype* sectp = actor->sector(); DSWActor* spawnedActor = nullptr; if ((sectp->extra & SECTFX_SINK)||(sectp->extra & SECTFX_CURRENT)) return -1; // No blood in water or current areas if (actor->user.Flags & (SPR_UNDERWATER) || SpriteInUnderwaterArea(actor) || SpriteInDiveArea(actor)) return -1; // No blood underwater! if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER) return -1; // No prints liquid areas! if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) return -1; // Not in lave either if (TestDontStickSector(actor->sector())) return -1; // Not on special sectors you don't if (FloorBloodQueue[FloorBloodQueueHead] != nullptr) KillActor(FloorBloodQueue[FloorBloodQueueHead]); FloorBloodQueue[FloorBloodQueueHead] = spawnedActor = SpawnActor(STAT_SKIP4, FLOORBLOOD1, s_FloorBlood1, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); FloorBloodQueueHead = (FloorBloodQueueHead+1) & (MAX_FLOORBLOOD_QUEUE-1); // Stupid hack to fix the blood under the skull to not show through // x,y repeat of floor blood MUST be smaller than the sprite above it or clipping probs. if (actor->user.ID == GORE_Head) spawnedActor->spr.hitag = 9995; else spawnedActor->spr.hitag = 0; spawnedActor->spr.scale = DVector2(0.125, 0.125); spawnedActor->spr.cstat = 0; spawnedActor->spr.pal = 0; spawnedActor->spr.shade = 0; spawnedActor->spr.extra = 0; spawnedActor->clipdist = 0; spawnedActor->spr.xoffset = spawnedActor->spr.yoffset = 0; spawnedActor->spr.pos = actor->spr.pos.plusZ(1); spawnedActor->spr.Angles.Yaw = RandomAngle(); // Just make it any old angle spawnedActor->spr.shade -= 5; // Brighten it up just a bit spawnedActor->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_FLOOR); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ONE_SIDE); spawnedActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); actor->user.Flags &= ~(SPR_SHADOW); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- enum { FOOTPRINT1 = 2490, FOOTPRINT2 = 2491, FOOTPRINT3 = 2492, FOOTPRINT_RATE = 30, }; ANIMATOR DoFootPrint; STATE s_FootPrint1[] = { {FOOTPRINT1, FOOTPRINT_RATE, NullAnimator, &s_FootPrint1[0]}, }; STATE s_FootPrint2[] = { {FOOTPRINT2, FOOTPRINT_RATE, NullAnimator, &s_FootPrint2[0]}, }; STATE s_FootPrint3[] = { {FOOTPRINT3, FOOTPRINT_RATE, NullAnimator, &s_FootPrint3[0]}, }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int QueueFootPrint(DSWActor* actor) { DSWActor* spawnedActor; short rnd_num=0; bool Found=false; sectortype* sectp = actor->sector(); if ((sectp->extra & SECTFX_SINK)||(sectp->extra & SECTFX_CURRENT)) return -1; // No blood in water or current areas if (actor->user.PlayerP) { if ((actor->user.PlayerP->Flags & PF_DIVING)) Found = true; // Stupid masked floor stuff! Damn your weirdness! if ((actor->user.PlayerP->cursector->ceilingstat & CSTAT_SECTOR_SKY)) Found = true; if ((actor->user.PlayerP->cursector->floorstat & CSTAT_SECTOR_SKY)) Found = true; } if (actor->user.Flags & (SPR_UNDERWATER) || SpriteInUnderwaterArea(actor) || Found || SpriteInDiveArea(actor)) return -1; // No prints underwater! if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER) return -1; // No prints liquid areas! if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) return -1; // Not in lave either if (TestDontStickSector(actor->sector())) return -1; // Not on special sectors you don't if (!isAwayFromWall(actor, 5.25)) return -1; // not if it goes ouzside the sector // So, are we like, done checking now!? if (FloorBloodQueue[FloorBloodQueueHead] != nullptr) KillActor(FloorBloodQueue[FloorBloodQueueHead]); rnd_num = RandomRange(1024); if (rnd_num > 683) FloorBloodQueue[FloorBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, FOOTPRINT1, s_FootPrint1, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); else if (rnd_num > 342) FloorBloodQueue[FloorBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, FOOTPRINT2, s_FootPrint2, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); else FloorBloodQueue[FloorBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, FOOTPRINT3, s_FootPrint3, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); FloorBloodQueueHead = (FloorBloodQueueHead+1) & (MAX_FLOORBLOOD_QUEUE-1); // Decrease footprint count if (actor->user.PlayerP) actor->user.PlayerP->NumFootPrints--; spawnedActor->spr.hitag = 0; spawnedActor->spr.scale = DVector2(0.75, 0.84375); spawnedActor->spr.cstat = 0; spawnedActor->spr.pal = 0; spawnedActor->spr.shade = 0; spawnedActor->spr.extra = 0; spawnedActor->clipdist = 0; spawnedActor->spr.xoffset = spawnedActor->spr.yoffset = 0; spawnedActor->spr.pos = actor->spr.pos; spawnedActor->spr.Angles.Yaw = actor->spr.Angles.Yaw; spawnedActor->user.Flags &= ~(SPR_SHADOW); switch (FootMode) { case BLOOD_FOOT: spawnedActor->user.spal = spawnedActor->spr.pal = PALETTE_PLAYER3; // Turn blue to blood red break; default: spawnedActor->user.spal = spawnedActor->spr.pal = PALETTE_PLAYER1; // Gray water break; } // Alternate the feet left_foot = !left_foot; if (left_foot) spawnedActor->spr.cstat |= (CSTAT_SPRITE_XFLIP); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_FLOOR); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ONE_SIDE); spawnedActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- enum { WALLBLOOD1 = 2500, WALLBLOOD2 = 2501, WALLBLOOD3 = 2502, WALLBLOOD4 = 2503, WALLBLOOD_RATE = 1, }; ANIMATOR DoWallBlood; STATE s_WallBlood1[] = { {WALLBLOOD1, SF_QUICK_CALL, DoWallBlood, &s_WallBlood1[1]}, {WALLBLOOD1, WALLBLOOD_RATE, NullAnimator, &s_WallBlood1[0]}, }; STATE s_WallBlood2[] = { {WALLBLOOD2, SF_QUICK_CALL, DoWallBlood, &s_WallBlood2[1]}, {WALLBLOOD2, WALLBLOOD_RATE, NullAnimator, &s_WallBlood2[0]}, }; STATE s_WallBlood3[] = { {WALLBLOOD3, SF_QUICK_CALL, DoWallBlood, &s_WallBlood3[1]}, {WALLBLOOD3, WALLBLOOD_RATE, NullAnimator, &s_WallBlood3[0]}, }; STATE s_WallBlood4[] = { {WALLBLOOD4, SF_QUICK_CALL, DoWallBlood, &s_WallBlood4[1]}, {WALLBLOOD4, WALLBLOOD_RATE, NullAnimator, &s_WallBlood4[0]}, }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- DSWActor* QueueWallBlood(DSWActor* actor, DAngle bang) { short w,nw; DSWActor* spawnedActor; short rndnum; HitInfo hit{}; if (actor->user.Flags & (SPR_UNDERWATER) || SpriteInUnderwaterArea(actor) || SpriteInDiveArea(actor)) return nullptr; // No blood underwater! double daz = RandomRange(128) * 8 - 512; DAngle dang = bang + RandomAngle(22.5) - DAngle22_5 / 2; DVector3 vect(dang.ToVector() * 1024, daz); FAFhitscan(actor->spr.pos.plusZ(-30), actor->sector(), vect, hit, CLIPMASK_MISSILE); if (hit.hitSector == nullptr) return nullptr; const double WALLBLOOD_DIST_MAX = 156.25; if ((hit.hitpos.XY() - actor->spr.pos.XY()).Length() > WALLBLOOD_DIST_MAX) return nullptr; // hit a sprite? if (hit.actor() != nullptr) return nullptr; // Don't try to put blood on a sprite if (hit.hitWall != nullptr) // Don't check if blood didn't hit a wall, otherwise the ASSERT fails! { if (TestDontStick(nullptr, hit.hitWall)) return nullptr; } else return nullptr; if (WallBloodQueue[WallBloodQueueHead] != nullptr) KillActor(WallBloodQueue[WallBloodQueueHead]); // Randomly choose a wall blood sprite rndnum = RandomRange(1024); if (rndnum > 768) { WallBloodQueue[WallBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, WALLBLOOD1, s_WallBlood1, hit.hitSector, hit.hitpos, bang, 0); } else if (rndnum > 512) { WallBloodQueue[WallBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, WALLBLOOD2, s_WallBlood2, hit.hitSector, hit.hitpos, bang, 0); } else if (rndnum > 128) { WallBloodQueue[WallBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, WALLBLOOD3, s_WallBlood3, hit.hitSector, hit.hitpos, bang, 0); } else { WallBloodQueue[WallBloodQueueHead] = spawnedActor = SpawnActor(STAT_WALLBLOOD_QUEUE, WALLBLOOD4, s_WallBlood4, hit.hitSector, hit.hitpos, bang, 0); } WallBloodQueueHead = (WallBloodQueueHead+1) & (MAX_WALLBLOOD_QUEUE-1); spawnedActor->spr.scale = DVector2(0.46875, 0.625); spawnedActor->spr.cstat = 0; spawnedActor->spr.pal = 0; spawnedActor->spr.shade = 0; spawnedActor->spr.extra = 0; spawnedActor->clipdist = 0; spawnedActor->spr.xoffset = spawnedActor->spr.yoffset = 0; spawnedActor->spr.pos = hit.hitpos; spawnedActor->spr.shade -= 5; // Brighten it up just a bit spawnedActor->tempwall = hit.hitWall; // pass hitinfo.wall spawnedActor->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_WALL); spawnedActor->spr.cstat |= (CSTAT_SPRITE_ONE_SIDE); spawnedActor->spr.cstat |= (CSTAT_SPRITE_YCENTER); spawnedActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); spawnedActor->spr.Angles.Yaw = hit.hitWall->delta().Angle() + DAngle90; // move it back some auto vec = spawnedActor->spr.Angles.Yaw.ToVector(); auto sect = spawnedActor->sector(); Collision coll; clipmove(spawnedActor->spr.pos, §, vec, 0., 0., 0., CLIPMASK_MISSILE, coll, 1); if (spawnedActor->sector() != sect) ChangeActorSect(spawnedActor, sect); return spawnedActor; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFloorBlood(DSWActor* actor) { constexpr double FEET_IN_BLOOD_DIST = 18.75; short pnum; PLAYER* pp; double scale; if (actor->spr.hitag == 9995) scale = 0.1875; else scale = 0.625; // Make pool of blood seem to grow if (actor->spr.scale.X < scale && actor->spr.scale.X > 0.0625) { actor->spr.scale.X += (REPEAT_SCALE); } if (actor->spr.scale.Y < scale && actor->spr.scale.X < scale && actor->spr.scale.X > 0.0625) { actor->spr.scale.Y += (REPEAT_SCALE); } // See if any players stepped in blood if (actor->spr.scale.X > 0.0625 && actor->spr.scale.Y > 0.0625) { TRAVERSE_CONNECT(pnum) { pp = &Player[pnum]; double dist = (actor->spr.pos.XY() - pp->actor->spr.pos.XY()).Length(); if (dist < FEET_IN_BLOOD_DIST) { if (pp->NumFootPrints <= 0 || FootMode != BLOOD_FOOT) { pp->NumFootPrints = RandomRange(10)+3; FootMode = BLOOD_FOOT; } // If blood has already grown to max size, we can shrink it if (actor->spr.scale.X == 0.625 && actor->spr.scale.Y > 0.15625) { actor->spr.scale.Y += (-0.15625); if (actor->spr.scale.Y <= 0.15625) // Shrink it down and don't use it anymore actor->spr.scale = DVector2(0.0625, 0.0625); } } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoWallBlood(DSWActor* actor) { // Make blood drip down the wall if (actor->spr.scale.Y < 1.25) { actor->spr.scale.Y += (REPEAT_SCALE) / 30.; actor->spr.pos.Z += 0.5 / 30.; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void QueueGeneric(DSWActor* actor, short pic) { if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER) { KillActor(actor); return; } if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) { KillActor(actor); return; } if (TestDontStickSector(actor->sector())) { KillActor(actor); return; } auto scale = actor->spr.scale; // can and should kill the user portion if (GenericQueue[GenericQueueHead] == nullptr) { actor->clearUser(); change_actor_stat(actor, STAT_GENERIC_QUEUE); GenericQueue[GenericQueueHead] = actor; } else { // move old sprite to new sprite's place auto osp = GenericQueue[GenericQueueHead]; osp->spr.pos = actor->spr.pos; ChangeActorSect(osp, actor->sector()); KillActor(actor); actor = GenericQueue[GenericQueueHead]; ASSERT(actor->spr.statnum != MAXSTATUS); } actor->spr.picnum = pic; actor->spr.scale = scale; actor->spr.cstat = 0; switch (actor->spr.picnum) { case 900: case 901: case 902: case 915: case 916: case 917: case 930: case 931: case 932: case GORE_Head: change_actor_stat(actor,STAT_DEFAULT); // Breakable actor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE); actor->spr.extra |= (SPRX_BREAKABLE); break; default: actor->spr.cstat &= ~(CSTAT_SPRITE_BREAKABLE); actor->spr.extra &= ~(SPRX_BREAKABLE); break; } GenericQueueHead = (GenericQueueHead+1) & (MAX_GENERIC_QUEUE-1); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoShrapVelocity(DSWActor* actor) { if (actor->user.Flags & (SPR_UNDERWATER) || SpriteInUnderwaterArea(actor)) { ScaleSpriteVector(actor, 20000); actor->user.Counter += 8*4; // These are MoveSkip4 now actor->user.addCounterToChange(); } else { actor->user.Counter += 60*4; actor->user.addCounterToChange(); } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2); MissileHitDiveArea(actor); { switch (actor->user.coll.type) { case kHitVoid: KillActor(actor); return true; case kHitSprite: { short wall_ang; auto hit_sprite = actor->user.coll.actor(); WallBounce(actor, hit_sprite->spr.Angles.Yaw); ScaleSpriteVector(actor, 32000); break; } case kHitWall: { WallBounce(actor, actor->user.coll.hitWall->delta().Angle() + DAngle90); ScaleSpriteVector(actor, 32000); break; } case kHitSector: { bool did_hit_wall; if (SlopeBounce(actor, &did_hit_wall)) { if (did_hit_wall) { // hit a wall ScaleSpriteVector(actor, 28000); actor->user.coll.setNone(); actor->user.Counter = 0; } else { // hit a sector if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { // hit a floor if (!(actor->user.Flags & SPR_BOUNCE)) { actor->user.Flags |= (SPR_BOUNCE); ScaleSpriteVector(actor, 18000); actor->user.coll.setNone(); actor->user.Counter = 0; } else { if (actor->user.ID == GORE_Drip) ChangeState(actor, s_GoreFloorSplash); else ShrapKillSprite(actor); return true; } } else { // hit a ceiling ScaleSpriteVector(actor, 22000); } } } else { // hit floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { actor->spr.pos.Z = actor->user.loz; if (actor->user.Flags & (SPR_UNDERWATER)) actor->user.Flags |= (SPR_BOUNCE); // no bouncing underwater if (actor->user.lo_sectp && actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed)) actor->user.Flags |= (SPR_BOUNCE); // no bouncing on shallow water if (!(actor->user.Flags & SPR_BOUNCE)) { actor->user.Flags |= (SPR_BOUNCE); actor->user.coll.setNone(); actor->user.Counter = 0; actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 18000); switch (actor->user.ID) { case UZI_SHELL: PlaySound(DIGI_SHELL, actor, v3df_none); break; case SHOT_SHELL: PlaySound(DIGI_SHOTSHELLSPENT, actor, v3df_none); break; } } else { if (actor->user.ID == GORE_Drip) ChangeState(actor, s_GoreFloorSplash); else ShrapKillSprite(actor); return true; } } else // hit something above { actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 22000); } } break; } } } // just outright kill it if its boucing around alot if (actor->user.bounce > 10) { KillActor(actor); return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int ShrapKillSprite(DSWActor* actor) { short rnd_num; rnd_num = RandomRange(1024); ASSERT(actor->hasU()); switch (actor->user.ID) { case UZI_SHELL: if (rnd_num > 854) { QueueGeneric(actor,UZI_SHELL + 0); } else if (rnd_num > 684) { QueueGeneric(actor,UZI_SHELL + 1); } else if (rnd_num > 514) { QueueGeneric(actor,UZI_SHELL + 2); } else if (rnd_num > 344) { QueueGeneric(actor,UZI_SHELL + 3); } else if (rnd_num > 174) { QueueGeneric(actor,UZI_SHELL + 4); } else { QueueGeneric(actor,UZI_SHELL + 5); } return 0; break; case SHOT_SHELL: if (rnd_num > 683) { QueueGeneric(actor,SHOT_SHELL + 1); } else if (rnd_num > 342) { QueueGeneric(actor,SHOT_SHELL + 3); } else { QueueGeneric(actor,SHOT_SHELL + 7); } return 0; break; case GORE_Lung: if (RandomRange(1000) > 500) break; SetActorSizeX(actor); SpawnFloorSplash(actor); if (RandomRange(1000) < 500) PlaySound(DIGI_GIBS1, actor, v3df_none); else PlaySound(DIGI_GIBS2, actor, v3df_none); if (rnd_num > 683) { QueueGeneric(actor,900); } else if (rnd_num > 342) { QueueGeneric(actor,901); } else { QueueGeneric(actor,902); } return 0; break; case GORE_Liver: if (RandomRange(1000) > 500) break; SetActorSizeX(actor); SpawnFloorSplash(actor); if (RandomRange(1000) < 500) PlaySound(DIGI_GIBS1, actor, v3df_none); else PlaySound(DIGI_GIBS2, actor, v3df_none); if (rnd_num > 683) { QueueGeneric(actor,915); } else if (rnd_num > 342) { QueueGeneric(actor,916); } else { QueueGeneric(actor,917); } return 0; break; case GORE_SkullCap: if (RandomRange(1000) > 500) break; SetActorSizeX(actor); SpawnFloorSplash(actor); if (rnd_num > 683) { QueueGeneric(actor,930); } else if (rnd_num > 342) { QueueGeneric(actor,931); } else { QueueGeneric(actor,932); } return 0; break; case GORE_Head: if (RandomRange(1000) > 500) break; SetActorSizeX(actor); QueueFloorBlood(actor); QueueGeneric(actor,GORE_Head); return 0; break; } // If it wasn't in the switch statement, kill it. KillActor(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool CheckBreakToughness(BREAK_INFO* break_info, int ID) { if ((break_info->flags & BF_TOUGH)) { switch (ID) { case LAVA_BOULDER: case MIRV_METEOR: case SERP_METEOR: case BOLT_THINMAN_R0: case BOLT_THINMAN_R1: case BOLT_THINMAN_R2: case BOLT_EXP: case TANK_SHELL_EXP: case GRENADE_EXP: case MICRO_EXP: case MINE_EXP: case NAP_EXP: case SKULL_R0: case BETTY_R0: case SKULL_SERP: case FIREBALL1: case GORO_FIREBALL: return true; // All the above stuff will break tough things } return false; // False means it won't break with current weapon } return true; // It wasn't tough, go ahead and break it } int DoItemFly(DSWActor* actor) { if (actor->user.Flags & (SPR_UNDERWATER)) { ScaleSpriteVector(actor, 50000); actor->user.Counter += 20*2; actor->user.addCounterToChange(); } else { actor->user.Counter += 60*2; actor->user.addCounterToChange(); } actor->user.coll = move_missile(actor, actor->user.change, actor->user.ceiling_dist, actor->user.floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2); MissileHitDiveArea(actor); { switch (actor->user.coll.type) { case kHitSprite: { short wall_ang; auto hit_sprite = actor->user.coll.actor(); if ((hit_sprite->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) { WallBounce(actor, hit_sprite->spr.Angles.Yaw); ScaleSpriteVector(actor, 32000); } else { actor->user.change.XY() = -actor->user.change.XY(); } break; } case kHitWall: { WallBounce(actor, actor->user.coll.hitWall->delta().Angle() + DAngle90); ScaleSpriteVector(actor, 32000); break; } case kHitSector: { // hit floor if (actor->spr.pos.Z > ((actor->user.hiz + actor->user.loz) * 0.5)) { actor->spr.pos.Z = actor->user.loz; actor->user.Counter = 0; actor->vel.X = 0; actor->user.change.Zero(); return false; } else // hit something above { actor->user.change.Z = -actor->user.change.Z; ScaleSpriteVector(actor, 22000); } break; } } } return true; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void QueueLoWangs(DSWActor* actor) { DSWActor* spawnedActor; if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER) { return; } if ((actor->sector()->extra & SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA) { return; } if (TestDontStickSector(actor->sector())) { return; } if (LoWangsQueue[LoWangsQueueHead] == nullptr) { LoWangsQueue[LoWangsQueueHead] = spawnedActor = SpawnActor(STAT_GENERIC_QUEUE, actor->spr.picnum, s_DeadLoWang, actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 0); } else { // move old sprite to new sprite's place SetActorZ(LoWangsQueue[LoWangsQueueHead], actor->spr.pos); spawnedActor = LoWangsQueue[LoWangsQueueHead]; ASSERT(spawnedActor->spr.statnum != MAXSTATUS); } // Point passed in sprite to ps spawnedActor->spr.cstat = 0; spawnedActor->spr.scale = actor->spr.scale; spawnedActor->spr.shade = actor->spr.shade; spawnedActor->user.spal = spawnedActor->spr.pal = actor->spr.pal; change_actor_stat(spawnedActor, STAT_DEFAULT); // Breakable spawnedActor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE); spawnedActor->spr.extra |= SPRX_BREAKABLE; spawnedActor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN); LoWangsQueueHead = (LoWangsQueueHead+1) & (MAX_LOWANGS_QUEUE-1); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- #include "saveable.h" static saveable_code saveable_weapon_code[] = { SAVE_CODE(SpawnShrapX), SAVE_CODE(DoLavaErupt), SAVE_CODE(DoVomit), SAVE_CODE(DoVomitSplash), SAVE_CODE(DoFastShrapJumpFall), SAVE_CODE(DoTracerShrap), SAVE_CODE(DoShrapJumpFall), SAVE_CODE(DoShrapDamage), SAVE_CODE(DoUziSmoke), SAVE_CODE(DoShotgunSmoke), SAVE_CODE(DoMineSpark), SAVE_CODE(DoFireballFlames), SAVE_CODE(DoBreakFlames), SAVE_CODE(DoActorScale), SAVE_CODE(DoRipperGrow), SAVE_CODE(DoDamageTest), SAVE_CODE(DoStar), SAVE_CODE(DoCrossBolt), SAVE_CODE(DoPlasmaDone), SAVE_CODE(DoPlasmaFountain), SAVE_CODE(DoPlasma), SAVE_CODE(DoCoolgFire), SAVE_CODE(DoEelFire), SAVE_CODE(DoGrenade), SAVE_CODE(DoVulcanBoulder), SAVE_CODE(DoMineStuck), SAVE_CODE(DoMine), SAVE_CODE(DoPuff), SAVE_CODE(DoRailPuff), SAVE_CODE(DoBoltThinMan), SAVE_CODE(DoTracer), SAVE_CODE(DoEMP), SAVE_CODE(DoEMPBurst), SAVE_CODE(DoTankShell), SAVE_CODE(DoTracerStart), SAVE_CODE(DoLaser), SAVE_CODE(DoLaserStart), SAVE_CODE(DoRail), SAVE_CODE(DoRailStart), SAVE_CODE(DoRocket), SAVE_CODE(DoMicroMini), SAVE_CODE(SpawnExtraMicroMini), SAVE_CODE(DoMicro), SAVE_CODE(DoUziBullet), SAVE_CODE(DoBoltSeeker), SAVE_CODE(DoBoltShrapnel), SAVE_CODE(DoBoltFatMan), SAVE_CODE(DoElectro), SAVE_CODE(DoLavaBoulder), SAVE_CODE(DoSpear), SAVE_CODE(SpawnGrenadeSmallExp), SAVE_CODE(SpawnGrenadeExp), SAVE_CODE(SpawnMineExp), SAVE_CODE(DoSectorExp), SAVE_CODE(SpawnSectorExp), SAVE_CODE(SpawnLargeExp), SAVE_CODE(SpawnMeteorExp), SAVE_CODE(SpawnLittleExp), SAVE_CODE(DoFireball), SAVE_CODE(DoFindGround), SAVE_CODE(DoFindGroundPoint), SAVE_CODE(DoNapalm), SAVE_CODE(DoBloodWorm), SAVE_CODE(DoMeteor), SAVE_CODE(DoSerpMeteor), SAVE_CODE(DoMirvMissile), SAVE_CODE(DoMirv), SAVE_CODE(DoRing), SAVE_CODE(DoSerpRing), SAVE_CODE(InitLavaFlame), SAVE_CODE(InitLavaThrow), SAVE_CODE(InitVulcanBoulder), SAVE_CODE(InitSerpRing), SAVE_CODE(InitSerpRing), SAVE_CODE(InitSpellNapalm), SAVE_CODE(InitEnemyNapalm), SAVE_CODE(InitSpellMirv), SAVE_CODE(InitEnemyMirv), SAVE_CODE(InitSwordAttack), SAVE_CODE(InitFistAttack), SAVE_CODE(InitSumoNapalm), SAVE_CODE(InitSumoSkull), SAVE_CODE(InitSumoStompAttack), SAVE_CODE(InitMiniSumoClap), SAVE_CODE(InitZillaRail), SAVE_CODE(InitEnemyNuke), SAVE_CODE(InitRipperSlash), SAVE_CODE(InitBunnySlash), SAVE_CODE(InitSerpSlash), SAVE_CODE(InitCoolgBash), SAVE_CODE(InitSkelSlash), SAVE_CODE(InitGoroChop), SAVE_CODE(InitHornetSting), SAVE_CODE(InitSerpSpell), SAVE_CODE(InitSerpMonstSpell), SAVE_CODE(DoTeleRipper), SAVE_CODE(InitEnemyRocket), SAVE_CODE(InitEnemyRail), SAVE_CODE(InitZillaRocket), SAVE_CODE(InitEnemyStar), SAVE_CODE(InitEnemyCrossbow), SAVE_CODE(InitSkelSpell), SAVE_CODE(InitCoolgFire), SAVE_CODE(InitCoolgDrip), SAVE_CODE(GenerateDrips), SAVE_CODE(InitEelFire), SAVE_CODE(InitFireballTrap), SAVE_CODE(InitBoltTrap), SAVE_CODE(InitEnemyCrossbow), SAVE_CODE(InitSpearTrap), SAVE_CODE(DoSuicide), SAVE_CODE(DoDefaultStat), SAVE_CODE(InitEnemyUzi), SAVE_CODE(InitSpriteGrenade), SAVE_CODE(InitEnemyMine), SAVE_CODE(InitEnemyFireball), SAVE_CODE(DoVehicleSmoke), SAVE_CODE(DoWaterSmoke), SAVE_CODE(SpawnVehicleSmoke), SAVE_CODE(SpawnSmokePuff), SAVE_CODE(DoBubble), SAVE_CODE(DoFloorBlood), SAVE_CODE(DoWallBlood), SAVE_CODE(DoItemFly), }; static saveable_data saveable_weapon_data[] = { SAVE_DATA(s_NotRestored), SAVE_DATA(s_Suicide), SAVE_DATA(s_DeadLoWang), SAVE_DATA(s_BreakLight), SAVE_DATA(s_BreakBarrel), SAVE_DATA(s_BreakPedistal), SAVE_DATA(s_BreakBottle1), SAVE_DATA(s_BreakBottle2), SAVE_DATA(s_Puff), SAVE_DATA(s_RailPuff), SAVE_DATA(sg_RailPuff), SAVE_DATA(s_LaserPuff), SAVE_DATA(s_Tracer), SAVE_DATA(s_EMP), SAVE_DATA(s_EMPBurst), SAVE_DATA(s_EMPShrap), SAVE_DATA(s_TankShell), SAVE_DATA(s_VehicleSmoke), SAVE_DATA(s_WaterSmoke), SAVE_DATA(s_UziSmoke), SAVE_DATA(s_ShotgunSmoke), SAVE_DATA(s_UziBullet), SAVE_DATA(s_UziSpark), SAVE_DATA(s_UziPowerSpark), SAVE_DATA(s_Bubble), SAVE_DATA(s_Splash), SAVE_DATA(s_CrossBolt), SAVE_DATA(sg_CrossBolt), SAVE_DATA(s_Star), SAVE_DATA(s_StarStuck), SAVE_DATA(s_StarDown), SAVE_DATA(s_StarDownStuck), SAVE_DATA(s_LavaBoulder), SAVE_DATA(s_LavaShard), SAVE_DATA(s_VulcanBoulder), SAVE_DATA(s_Grenade), SAVE_DATA(s_Grenade), SAVE_DATA(sg_Grenade), SAVE_DATA(s_MineStuck), SAVE_DATA(s_Mine), SAVE_DATA(s_MineSpark), SAVE_DATA(s_Meteor), SAVE_DATA(sg_Meteor), SAVE_DATA(s_MeteorExp), SAVE_DATA(s_MirvMeteor), SAVE_DATA(sg_MirvMeteor), SAVE_DATA(s_MirvMeteorExp), SAVE_DATA(s_SerpMeteor), SAVE_DATA(sg_SerpMeteor), SAVE_DATA(s_SerpMeteorExp), SAVE_DATA(s_Spear), SAVE_DATA(sg_Spear), SAVE_DATA(s_Rocket), SAVE_DATA(sg_Rocket), SAVE_DATA(s_BunnyRocket), SAVE_DATA(sg_BunnyRocket), SAVE_DATA(s_Rail), SAVE_DATA(sg_Rail), SAVE_DATA(s_Laser), //SAVE_DATA(s_MicroPuff), SAVE_DATA(s_Micro), SAVE_DATA(sg_Micro), SAVE_DATA(s_MicroMini), SAVE_DATA(sg_MicroMini), SAVE_DATA(s_BoltThinMan), SAVE_DATA(sg_BoltThinMan), SAVE_DATA(s_BoltSeeker), SAVE_DATA(sg_BoltSeeker), SAVE_DATA(s_BoltFatMan), SAVE_DATA(s_BoltShrapnel), SAVE_DATA(s_CoolgFire), SAVE_DATA(s_CoolgFireDone), SAVE_DATA(s_CoolgDrip), SAVE_DATA(s_GoreFloorSplash), SAVE_DATA(s_GoreSplash), SAVE_DATA(s_Plasma), SAVE_DATA(s_PlasmaFountain), SAVE_DATA(s_PlasmaDrip), SAVE_DATA(s_PlasmaDone), SAVE_DATA(s_TeleportEffect), SAVE_DATA(s_TeleportEffect2), SAVE_DATA(s_Electro), SAVE_DATA(s_ElectroShrap), SAVE_DATA(s_GrenadeExp), SAVE_DATA(s_GrenadeSmallExp), SAVE_DATA(s_GrenadeExp), SAVE_DATA(s_MineExp), SAVE_DATA(s_BasicExp), SAVE_DATA(s_MicroExp), SAVE_DATA(s_BigGunFlame), SAVE_DATA(s_BoltExp), SAVE_DATA(s_TankShellExp), SAVE_DATA(s_TracerExp), SAVE_DATA(s_SectorExp), SAVE_DATA(s_FireballExp), SAVE_DATA(s_NapExp), SAVE_DATA(s_FireballFlames), SAVE_DATA(s_BreakFlames), SAVE_DATA(s_Fireball), SAVE_DATA(s_Fireball), //SAVE_DATA(sg_Fireball), SAVE_DATA(s_Ring), //SAVE_DATA(sg_Ring), SAVE_DATA(s_Ring), SAVE_DATA(s_Ring2), SAVE_DATA(s_Napalm), SAVE_DATA(s_BloodWorm), SAVE_DATA(s_BloodWorm), SAVE_DATA(s_PlasmaExp), SAVE_DATA(s_PlasmaExp), SAVE_DATA(s_Mirv), SAVE_DATA(s_MirvMissile), SAVE_DATA(s_Vomit1), SAVE_DATA(s_Vomit2), SAVE_DATA(s_VomitSplash), SAVE_DATA(s_GoreHead), SAVE_DATA(s_GoreLeg), SAVE_DATA(s_GoreEye), SAVE_DATA(s_GoreTorso), SAVE_DATA(s_GoreArm), SAVE_DATA(s_GoreLung), SAVE_DATA(s_GoreLiver), SAVE_DATA(s_GoreSkullCap), SAVE_DATA(s_GoreChunkS), SAVE_DATA(s_GoreDrip), SAVE_DATA(s_FastGoreDrip), SAVE_DATA(s_GoreFlame), SAVE_DATA(s_TracerShrap), SAVE_DATA(s_UziShellShrap), SAVE_DATA(s_UziShellShrapStill1), SAVE_DATA(s_UziShellShrapStill2), SAVE_DATA(s_UziShellShrapStill3), SAVE_DATA(s_UziShellShrapStill4), SAVE_DATA(s_UziShellShrapStill5), SAVE_DATA(s_UziShellShrapStill6), SAVE_DATA(s_ShotgunShellShrap), SAVE_DATA(s_ShotgunShellShrapStill1), SAVE_DATA(s_ShotgunShellShrapStill2), SAVE_DATA(s_ShotgunShellShrapStill3), SAVE_DATA(s_GoreFlameChunkA), SAVE_DATA(s_GoreFlameChunkB), SAVE_DATA(s_CoinShrap), SAVE_DATA(s_Marbel), SAVE_DATA(s_GlassShrapA), SAVE_DATA(s_GlassShrapB), SAVE_DATA(s_GlassShrapC), SAVE_DATA(s_WoodShrapA), SAVE_DATA(s_WoodShrapB), SAVE_DATA(s_WoodShrapC), SAVE_DATA(s_StoneShrapA), SAVE_DATA(s_StoneShrapB), SAVE_DATA(s_StoneShrapC), SAVE_DATA(s_MetalShrapA), SAVE_DATA(s_MetalShrapB), SAVE_DATA(s_MetalShrapC), SAVE_DATA(s_PaperShrapA), SAVE_DATA(s_PaperShrapB), SAVE_DATA(s_PaperShrapC), SAVE_DATA(s_FloorBlood1), SAVE_DATA(s_FootPrint1), SAVE_DATA(s_FootPrint2), SAVE_DATA(s_FootPrint3), SAVE_DATA(s_WallBlood1), SAVE_DATA(s_WallBlood2), SAVE_DATA(s_WallBlood3), SAVE_DATA(s_WallBlood4), }; saveable_module saveable_weapon = { // code saveable_weapon_code, SIZ(saveable_weapon_code), // data saveable_weapon_data, SIZ(saveable_weapon_data) }; END_SW_NS