From 0df2cb80cf03d7259746834220d209b306a8c503 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 4 Sep 2008 23:15:36 +0000 Subject: Add GPLed Heretic/Hexen source. Subversion-branch: /branches/raven-branch Subversion-revision: 1195 --- src/hexen/a_action.c | 1319 +++++ src/hexen/am_data.h | 107 + src/hexen/am_map.c | 1400 +++++ src/hexen/am_map.h | 143 + src/hexen/ct_chat.c | 497 ++ src/hexen/ct_chat.h | 15 + src/hexen/d_net.c | 927 ++++ src/hexen/defs.inc | 52 + src/hexen/drcoord.h | 29 + src/hexen/dstrings.h | 205 + src/hexen/f_finale.c | 376 ++ src/hexen/g_game.c | 2014 +++++++ src/hexen/h2_main.c | 934 ++++ src/hexen/h2def.h | 1459 +++++ src/hexen/i_cdmus.c | 1023 ++++ src/hexen/i_cdmus.h | 26 + src/hexen/i_cyber.c | 277 + src/hexen/i_header.h | 77 + src/hexen/i_ibm.c | 2782 ++++++++++ src/hexen/i_ibm_a.asm | 135 + src/hexen/i_sound.c | 369 ++ src/hexen/i_sound.h | 68 + src/hexen/in_lude.c | 597 +++ src/hexen/info.c | 13862 ++++++++++++++++++++++++++++++++++++++++++++++++ src/hexen/info.h | 3601 +++++++++++++ src/hexen/linear.asm | 349 ++ src/hexen/m_misc.c | 878 +++ src/hexen/mn_menu.c | 1743 ++++++ src/hexen/oldd_net.c | 790 +++ src/hexen/p_acs.c | 1782 +++++++ src/hexen/p_anim.c | 473 ++ src/hexen/p_ceilng.c | 294 + src/hexen/p_doors.c | 314 ++ src/hexen/p_enemy.c | 5308 ++++++++++++++++++ src/hexen/p_floor.c | 931 ++++ src/hexen/p_inter.c | 2252 ++++++++ src/hexen/p_lights.c | 351 ++ src/hexen/p_local.h | 372 ++ src/hexen/p_map.c | 2296 ++++++++ src/hexen/p_maputl.c | 1056 ++++ src/hexen/p_mobj.c | 2441 +++++++++ src/hexen/p_plats.c | 266 + src/hexen/p_pspr.c | 2456 +++++++++ src/hexen/p_setup.c | 1218 +++++ src/hexen/p_sight.c | 395 ++ src/hexen/p_spec.c | 1152 ++++ src/hexen/p_spec.h | 563 ++ src/hexen/p_switch.c | 146 + src/hexen/p_telept.c | 177 + src/hexen/p_things.c | 533 ++ src/hexen/p_tick.c | 142 + src/hexen/p_user.c | 1652 ++++++ src/hexen/po_man.c | 1482 ++++++ src/hexen/r_bsp.c | 504 ++ src/hexen/r_data.c | 676 +++ src/hexen/r_draw.c | 576 ++ src/hexen/r_local.h | 538 ++ src/hexen/r_main.c | 860 +++ src/hexen/r_plane.c | 559 ++ src/hexen/r_segs.c | 651 +++ src/hexen/r_things.c | 1033 ++++ src/hexen/sb_bar.c | 2240 ++++++++ src/hexen/sc_man.c | 485 ++ src/hexen/sn_sonix.c | 506 ++ src/hexen/sounds.c | 306 ++ src/hexen/sounds.h | 311 ++ src/hexen/soundst.h | 81 + src/hexen/st_start.c | 347 ++ src/hexen/st_start.h | 28 + src/hexen/sv_save.c | 1756 ++++++ src/hexen/tables.c | 2074 ++++++++ src/hexen/template.c | 32 + src/hexen/textdefs.h | 167 + src/hexen/v_video.c | 261 + src/hexen/vgaview.h | 34 + src/hexen/w_wad.c | 596 +++ src/hexen/xddefs.h | 231 + src/hexen/z_zone.c | 396 ++ 78 files changed, 79354 insertions(+) create mode 100644 src/hexen/a_action.c create mode 100644 src/hexen/am_data.h create mode 100644 src/hexen/am_map.c create mode 100644 src/hexen/am_map.h create mode 100644 src/hexen/ct_chat.c create mode 100644 src/hexen/ct_chat.h create mode 100644 src/hexen/d_net.c create mode 100644 src/hexen/defs.inc create mode 100644 src/hexen/drcoord.h create mode 100644 src/hexen/dstrings.h create mode 100644 src/hexen/f_finale.c create mode 100644 src/hexen/g_game.c create mode 100644 src/hexen/h2_main.c create mode 100644 src/hexen/h2def.h create mode 100644 src/hexen/i_cdmus.c create mode 100644 src/hexen/i_cdmus.h create mode 100644 src/hexen/i_cyber.c create mode 100644 src/hexen/i_header.h create mode 100644 src/hexen/i_ibm.c create mode 100644 src/hexen/i_ibm_a.asm create mode 100644 src/hexen/i_sound.c create mode 100644 src/hexen/i_sound.h create mode 100644 src/hexen/in_lude.c create mode 100644 src/hexen/info.c create mode 100644 src/hexen/info.h create mode 100644 src/hexen/linear.asm create mode 100644 src/hexen/m_misc.c create mode 100644 src/hexen/mn_menu.c create mode 100644 src/hexen/oldd_net.c create mode 100644 src/hexen/p_acs.c create mode 100644 src/hexen/p_anim.c create mode 100644 src/hexen/p_ceilng.c create mode 100644 src/hexen/p_doors.c create mode 100644 src/hexen/p_enemy.c create mode 100644 src/hexen/p_floor.c create mode 100644 src/hexen/p_inter.c create mode 100644 src/hexen/p_lights.c create mode 100644 src/hexen/p_local.h create mode 100644 src/hexen/p_map.c create mode 100644 src/hexen/p_maputl.c create mode 100644 src/hexen/p_mobj.c create mode 100644 src/hexen/p_plats.c create mode 100644 src/hexen/p_pspr.c create mode 100644 src/hexen/p_setup.c create mode 100644 src/hexen/p_sight.c create mode 100644 src/hexen/p_spec.c create mode 100644 src/hexen/p_spec.h create mode 100644 src/hexen/p_switch.c create mode 100644 src/hexen/p_telept.c create mode 100644 src/hexen/p_things.c create mode 100644 src/hexen/p_tick.c create mode 100644 src/hexen/p_user.c create mode 100644 src/hexen/po_man.c create mode 100644 src/hexen/r_bsp.c create mode 100644 src/hexen/r_data.c create mode 100644 src/hexen/r_draw.c create mode 100644 src/hexen/r_local.h create mode 100644 src/hexen/r_main.c create mode 100644 src/hexen/r_plane.c create mode 100644 src/hexen/r_segs.c create mode 100644 src/hexen/r_things.c create mode 100644 src/hexen/sb_bar.c create mode 100644 src/hexen/sc_man.c create mode 100644 src/hexen/sn_sonix.c create mode 100644 src/hexen/sounds.c create mode 100644 src/hexen/sounds.h create mode 100644 src/hexen/soundst.h create mode 100644 src/hexen/st_start.c create mode 100644 src/hexen/st_start.h create mode 100644 src/hexen/sv_save.c create mode 100644 src/hexen/tables.c create mode 100644 src/hexen/template.c create mode 100644 src/hexen/textdefs.h create mode 100644 src/hexen/v_video.c create mode 100644 src/hexen/vgaview.h create mode 100644 src/hexen/w_wad.c create mode 100644 src/hexen/xddefs.h create mode 100644 src/hexen/z_zone.c (limited to 'src/hexen') diff --git a/src/hexen/a_action.c b/src/hexen/a_action.c new file mode 100644 index 00000000..6508e472 --- /dev/null +++ b/src/hexen/a_action.c @@ -0,0 +1,1319 @@ + +//************************************************************************** +//** +//** a_action.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: a_action.c,v $ +//** $Revision: 1.86 $ +//** $Date: 96/03/22 13:15:33 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- +extern fixed_t FloatBobOffsets[64]; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- +int orbitTableX[256]= +{ +983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490, +964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025, +908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310, +817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690, +694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725, +545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010, +375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010, +191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745, +-375, -24495, -48600, -72690, -96720, -120705, -144600, -168420, +-192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135, +-376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230, +-546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105, +-695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925, +-817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845, +-908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235, +-964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740, +-983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490, +-964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025, +-908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310, +-817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690, +-694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725, +-545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010, +-375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010, +-191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745, +375, 24495, 48600, 72690, 96720, 120705, 144600, 168420, +192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135, +376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230, +546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105, +695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925, +817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845, +908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235, +964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740 +}; + +int orbitTableY[256]= +{ +375, 24495, 48600, 72690, 96720, 120705, 144600, 168420, +192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135, +376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230, +546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105, +695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925, +817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845, +908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235, +964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740, +983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490, +964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025, +908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310, +817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690, +694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725, +545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010, +375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010, +191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745, +-375, -24495, -48600, -72690, -96720, -120705, -144600, -168420, +-192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135, +-376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230, +-546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105, +-695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925, +-817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845, +-908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235, +-964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740, +-983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490, +-964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025, +-908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310, +-817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690, +-694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725, +-545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010, +-375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010, +-191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745 +}; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// +// Environmental Action routines +// +//-------------------------------------------------------------------------- + +//========================================================================== +// +// A_DripBlood +// +//========================================================================== + +/* +void A_DripBlood(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<11), + actor->y+((P_Random()-P_Random())<<11), actor->z, MT_BLOOD); + mo->momx = (P_Random()-P_Random())<<10; + mo->momy = (P_Random()-P_Random())<<10; + mo->flags2 |= MF2_LOGRAV; +} +*/ + +//============================================================================ +// +// A_PotteryExplode +// +//============================================================================ + +void A_PotteryExplode(mobj_t *actor) +{ + mobj_t *mo=NULL; + int i; + + for(i = (P_Random()&3)+3; i; i--) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_POTTERYBIT1); + P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%5)); + if(mo) + { + mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4); + mo->momx = (P_Random()-P_Random())<<(FRACBITS-6); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-6); + } + } + S_StartSound(mo, SFX_POTTERY_EXPLODE); + if(actor->args[0]) + { // Spawn an item + if(!nomonsters + || !(mobjinfo[TranslateThingType[actor->args[0]]].flags&MF_COUNTKILL)) + { // Only spawn monsters if not -nomonsters + P_SpawnMobj(actor->x, actor->y, actor->z, + TranslateThingType[actor->args[0]]); + } + } + P_RemoveMobj(actor); +} + +//============================================================================ +// +// A_PotteryChooseBit +// +//============================================================================ + +void A_PotteryChooseBit(mobj_t *actor) +{ + P_SetMobjState(actor, actor->info->deathstate+(P_Random()%5)+1); + actor->tics = 256+(P_Random()<<1); +} + +//============================================================================ +// +// A_PotteryCheck +// +//============================================================================ + +void A_PotteryCheck(mobj_t *actor) +{ + int i; + mobj_t *pmo; + + if(!netgame) + { + pmo = players[consoleplayer].mo; + if(P_CheckSight(actor, pmo) && (abs(R_PointToAngle2(pmo->x, + pmo->y, actor->x, actor->y)-pmo->angle) <= ANGLE_45)) + { // Previous state (pottery bit waiting state) + P_SetMobjState(actor, actor->state-&states[0]-1); + } + else + { + return; + } + } + else + { + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + pmo = players[i].mo; + if(P_CheckSight(actor, pmo) && (abs(R_PointToAngle2(pmo->x, + pmo->y, actor->x, actor->y)-pmo->angle) <= ANGLE_45)) + { // Previous state (pottery bit waiting state) + P_SetMobjState(actor, actor->state-&states[0]-1); + return; + } + } + } +} + +//============================================================================ +// +// A_CorpseBloodDrip +// +//============================================================================ + +void A_CorpseBloodDrip(mobj_t *actor) +{ + if(P_Random() > 128) + { + return; + } + P_SpawnMobj(actor->x, actor->y, actor->z+actor->height/2, + MT_CORPSEBLOODDRIP); +} + +//============================================================================ +// +// A_CorpseExplode +// +//============================================================================ + +void A_CorpseExplode(mobj_t *actor) +{ + mobj_t *mo; + int i; + + for(i = (P_Random()&3)+3; i; i--) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT); + P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3)); + if(mo) + { + mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4); + mo->momx = (P_Random()-P_Random())<<(FRACBITS-6); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-6); + } + } + // Spawn a skull + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT); + P_SetMobjState(mo, S_CORPSEBIT_4); + if(mo) + { + mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4); + mo->momx = (P_Random()-P_Random())<<(FRACBITS-6); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-6); + S_StartSound(mo, SFX_FIRED_DEATH); + } + P_RemoveMobj(actor); +} + +//============================================================================ +// +// A_LeafSpawn +// +//============================================================================ + +void A_LeafSpawn(mobj_t *actor) +{ + mobj_t *mo; + int i; + + for(i = (P_Random()&3)+1; i; i--) + { + mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<14), actor->y+ + ((P_Random()-P_Random())<<14), actor->z+(P_Random()<<14), + MT_LEAF1+(P_Random()&1)); + if(mo) + { + P_ThrustMobj(mo, actor->angle, (P_Random()<<9)+3*FRACUNIT); + mo->target = actor; + mo->special1 = 0; + } + } +} + +//============================================================================ +// +// A_LeafThrust +// +//============================================================================ + +void A_LeafThrust(mobj_t *actor) +{ + if(P_Random() > 96) + { + return; + } + actor->momz += (P_Random()<<9)+FRACUNIT; +} + +//============================================================================ +// +// A_LeafCheck +// +//============================================================================ + +void A_LeafCheck(mobj_t *actor) +{ + actor->special1++; + if(actor->special1 >= 20) + { + P_SetMobjState(actor, S_NULL); + return; + } + if(P_Random() > 64) + { + if(!actor->momx && !actor->momy) + { + P_ThrustMobj(actor, actor->target->angle, + (P_Random()<<9)+FRACUNIT); + } + return; + } + P_SetMobjState(actor, S_LEAF1_8); + actor->momz = (P_Random()<<9)+FRACUNIT; + P_ThrustMobj(actor, actor->target->angle, (P_Random()<<9)+2*FRACUNIT); + actor->flags |= MF_MISSILE; +} + +/* +#define ORBIT_RADIUS (15*FRACUNIT) +void GenerateOrbitTable(void) +{ + int angle; + + for (angle=0; angle<256; angle++) + { + orbitTableX[angle] = FixedMul(ORBIT_RADIUS, finecosine[angle<<5]); + orbitTableY[angle] = FixedMul(ORBIT_RADIUS, finesine[angle<<5]); + } + + printf("int orbitTableX[256]=\n{\n"); + for (angle=0; angle<256; angle+=8) + { + printf("%d, %d, %d, %d, %d, %d, %d, %d,\n", + orbitTableX[angle], + orbitTableX[angle+1], + orbitTableX[angle+2], + orbitTableX[angle+3], + orbitTableX[angle+4], + orbitTableX[angle+5], + orbitTableX[angle+6], + orbitTableX[angle+7]); + } + printf("};\n\n"); + + printf("int orbitTableY[256]=\n{\n"); + for (angle=0; angle<256; angle+=8) + { + printf("%d, %d, %d, %d, %d, %d, %d, %d,\n", + orbitTableY[angle], + orbitTableY[angle+1], + orbitTableY[angle+2], + orbitTableY[angle+3], + orbitTableY[angle+4], + orbitTableY[angle+5], + orbitTableY[angle+6], + orbitTableY[angle+7]); + } + printf("};\n"); +} +*/ + +// New bridge stuff +// Parent +// special1 true == removing from world +// +// Child +// target pointer to center mobj +// args[0] angle of ball + +void A_BridgeOrbit(mobj_t *actor) +{ + if (actor->target->special1) + { + P_SetMobjState(actor, S_NULL); + } + actor->args[0]+=3; + actor->x = actor->target->x + orbitTableX[actor->args[0]]; + actor->y = actor->target->y + orbitTableY[actor->args[0]]; + actor->z = actor->target->z; +} + + +void A_BridgeInit(mobj_t *actor) +{ + byte startangle; + mobj_t *ball1, *ball2, *ball3; + fixed_t cx,cy,cz; + +// GenerateOrbitTable(); + + cx = actor->x; + cy = actor->y; + cz = actor->z; + startangle = P_Random(); + actor->special1 = 0; + + // Spawn triad into world + ball1 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); + ball1->args[0] = startangle; + ball1->target = actor; + + ball2 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); + ball2->args[0] = (startangle+85)&255; + ball2->target = actor; + + ball3 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); + ball3->args[0] = (startangle+170)&255; + ball3->target = actor; + + A_BridgeOrbit(ball1); + A_BridgeOrbit(ball2); + A_BridgeOrbit(ball3); +} + +void A_BridgeRemove(mobj_t *actor) +{ + actor->special1 = true; // Removing the bridge + actor->flags &= ~MF_SOLID; + P_SetMobjState(actor, S_FREE_BRIDGE1); +} + + +//========================================================================== +// +// A_GhostOn +// +//========================================================================== + +/* +void A_GhostOn(mobj_t *actor) +{ + actor->flags |= MF_SHADOW; +} +*/ + +//========================================================================== +// +// A_GhostOff +// +//========================================================================== + +/* +void A_GhostOff(mobj_t *actor) +{ + actor->flags &= ~MF_SHADOW; +} +*/ + +//========================================================================== +// +// A_HideThing +// +//========================================================================== + +void A_HideThing(mobj_t *actor) +{ + actor->flags2 |= MF2_DONTDRAW; +} + +//========================================================================== +// +// A_UnHideThing +// +//========================================================================== + +void A_UnHideThing(mobj_t *actor) +{ + actor->flags2 &= ~MF2_DONTDRAW; +} + +//========================================================================== +// +// A_SetShootable +// +//========================================================================== + +void A_SetShootable(mobj_t *actor) +{ + actor->flags2 &= ~MF2_NONSHOOTABLE; + actor->flags |= MF_SHOOTABLE; +} + +//========================================================================== +// +// A_UnSetShootable +// +//========================================================================== + +void A_UnSetShootable(mobj_t *actor) +{ + actor->flags2 |= MF2_NONSHOOTABLE; + actor->flags &= ~MF_SHOOTABLE; +} + +//========================================================================== +// +// A_SetAltShadow +// +//========================================================================== + +void A_SetAltShadow(mobj_t *actor) +{ + actor->flags &= ~MF_SHADOW; + actor->flags |= MF_ALTSHADOW; +} + +//========================================================================== +// +// A_UnSetAltShadow +// +//========================================================================== + +/* +void A_UnSetAltShadow(mobj_t *actor) +{ + actor->flags &= ~MF_ALTSHADOW; +} +*/ + +//-------------------------------------------------------------------------- +// +// Sound Action Routines +// +//-------------------------------------------------------------------------- + +//========================================================================== +// +// A_ContMobjSound +// +//========================================================================== + +void A_ContMobjSound(mobj_t *actor) +{ + switch(actor->type) + { + case MT_SERPENTFX: + S_StartSound(actor, SFX_SERPENTFX_CONTINUOUS); + break; + case MT_HAMMER_MISSILE: + S_StartSound(actor, SFX_FIGHTER_HAMMER_CONTINUOUS); + break; + case MT_QUAKE_FOCUS: + S_StartSound(actor, SFX_EARTHQUAKE); + break; + default: + break; + } +} + +//========================================================================== +// +// PROC A_ESound +// +//========================================================================== + +void A_ESound(mobj_t *mo) +{ + int sound; + + switch(mo->type) + { + case MT_SOUNDWIND: + sound = SFX_WIND; + break; + default: + sound = SFX_NONE; + break; + } + S_StartSound(mo, sound); +} + + + +//========================================================================== +// Summon Minotaur -- see p_enemy for variable descriptions +//========================================================================== + + +void A_Summon(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *master; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_MINOTAUR); + if (mo) + { + if(P_TestMobjLocation(mo) == false || !actor->special1) + { // Didn't fit - change back to artifact + P_SetMobjState(mo, S_NULL); + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SUMMONMAULATOR); + if (mo) mo->flags2 |= MF2_DROPPED; + return; + } + + memcpy((void *)mo->args, &leveltime, sizeof(leveltime)); + master = (mobj_t *)actor->special1; + if (master->flags&MF_CORPSE) + { // Master dead + mo->special1 = 0; // No master + } + else + { + mo->special1 = actor->special1; // Pointer to master (mobj_t *) + P_GivePower(master->player, pw_minotaur); + } + + // Make smoke puff + P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE); + S_StartSound(actor, SFX_MAULATOR_ACTIVE); + } +} + + + +//========================================================================== +// Fog Variables: +// +// args[0] Speed (0..10) of fog +// args[1] Angle of spread (0..128) +// args[2] Frequency of spawn (1..10) +// args[3] Lifetime countdown +// args[4] Boolean: fog moving? +// special1 Internal: Counter for spawn frequency +// special2 Internal: Index into floatbob table +// +//========================================================================== + +void A_FogSpawn(mobj_t *actor) +{ + mobj_t *mo=NULL; + angle_t delta; + + if (actor->special1-- > 0) return; + + actor->special1 = actor->args[2]; // Reset frequency count + + switch(P_Random()%3) + { + case 0: + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHS); + break; + case 1: + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHM); + break; + case 2: + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHL); + break; + } + + if (mo) + { + delta = actor->args[1]; + if (delta==0) delta=1; + mo->angle = actor->angle + (((P_Random()%delta)-(delta>>1))<<24); + mo->target = actor; + if (actor->args[0] < 1) actor->args[0] = 1; + mo->args[0] = (P_Random() % (actor->args[0]))+1; // Random speed + mo->args[3] = actor->args[3]; // Set lifetime + mo->args[4] = 1; // Set to moving + mo->special2 = P_Random()&63; + } +} + + +void A_FogMove(mobj_t *actor) +{ + int speed = actor->args[0]<args[4])) return; + + if (actor->args[3]-- <= 0) + { + P_SetMobjStateNF(actor, actor->info->deathstate); + return; + } + + if ((actor->args[3] % 4) == 0) + { + weaveindex = actor->special2; + actor->z += FloatBobOffsets[weaveindex]>>1; + actor->special2 = (weaveindex+1)&63; + } + + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(speed, finecosine[angle]); + actor->momy = FixedMul(speed, finesine[angle]); +} + +//=========================================================================== +// +// A_PoisonBagInit +// +//=========================================================================== + +void A_PoisonBagInit(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z+28*FRACUNIT, + MT_POISONCLOUD); + if(!mo) + { + return; + } + mo->momx = 1; // missile objects must move to impact other objects + mo->special1 = 24+(P_Random()&7); + mo->special2 = 0; + mo->target = actor->target; + mo->radius = 20*FRACUNIT; + mo->height = 30*FRACUNIT; + mo->flags &= ~MF_NOCLIP; +} + +//=========================================================================== +// +// A_PoisonBagCheck +// +//=========================================================================== + +void A_PoisonBagCheck(mobj_t *actor) +{ + if(!--actor->special1) + { + P_SetMobjState(actor, S_POISONCLOUD_X1); + } + else + { + return; + } +} + +//=========================================================================== +// +// A_PoisonBagDamage +// +//=========================================================================== + +void A_PoisonBagDamage(mobj_t *actor) +{ + int bobIndex; + + extern void A_Explode(mobj_t *actor); + + A_Explode(actor); + + bobIndex = actor->special2; + actor->z += FloatBobOffsets[bobIndex]>>4; + actor->special2 = (bobIndex+1)&63; +} + +//=========================================================================== +// +// A_PoisonShroom +// +//=========================================================================== + +void A_PoisonShroom(mobj_t *actor) +{ + actor->tics = 128+(P_Random()<<1); +} + +//=========================================================================== +// +// A_CheckThrowBomb +// +//=========================================================================== + +void A_CheckThrowBomb(mobj_t *actor) +{ + if(abs(actor->momx) < 1.5*FRACUNIT && abs(actor->momy) < 1.5*FRACUNIT + && actor->momz < 2*FRACUNIT + && actor->state == &states[S_THROWINGBOMB6]) + { + P_SetMobjState(actor, S_THROWINGBOMB7); + actor->z = actor->floorz; + actor->momz = 0; + actor->flags2 &= ~MF2_FLOORBOUNCE; + actor->flags &= ~MF_MISSILE; + } + if(!--actor->health) + { + P_SetMobjState(actor, actor->info->deathstate); + } +} + +//=========================================================================== +// Quake variables +// +// args[0] Intensity on richter scale (2..9) +// args[1] Duration in tics +// args[2] Radius for damage +// args[3] Radius for tremor +// args[4] TID of map thing for focus of quake +// +//=========================================================================== + +//=========================================================================== +// +// A_LocalQuake +// +//=========================================================================== + +boolean A_LocalQuake(byte *args, mobj_t *actor) +{ + mobj_t *focus, *target; + int lastfound=0; + int success=false; + + actor=actor; // suppress warning + + // Find all quake foci + do + { + target = P_FindMobjFromTID(args[4], &lastfound); + if (target) + { + focus = P_SpawnMobj(target->x, + target->y, + target->z, MT_QUAKE_FOCUS); + if (focus) + { + focus->args[0] = args[0]; + focus->args[1] = args[1]>>1; // decremented every 2 tics + focus->args[2] = args[2]; + focus->args[3] = args[3]; + focus->args[4] = args[4]; + success = true; + } + } + }while(target != NULL); + + return(success); +} + + +//=========================================================================== +// +// A_Quake +// +//=========================================================================== +int localQuakeHappening[MAXPLAYERS]; + +void A_Quake(mobj_t *actor) +{ + angle_t an; + player_t *player; + mobj_t *victim; + int richters = actor->args[0]; + int playnum; + fixed_t dist; + + if (actor->args[1]-- > 0) + { + for (playnum=0; playnum < MAXPLAYERS; playnum++) + { + player = &players[playnum]; + if (!playeringame[playnum]) continue; + + victim = player->mo; + dist = P_AproxDistance(actor->x - victim->x, + actor->y - victim->y) >> (FRACBITS+6); + // Tested in tile units (64 pixels) + if (dist < actor->args[3]) // In tremor radius + { + localQuakeHappening[playnum] = richters; + } + // Check if in damage radius + if ((dist < actor->args[2]) && + (victim->z <= victim->floorz)) + { + if (P_Random() < 50) + { + P_DamageMobj(victim, NULL, NULL, HITDICE(1)); + } + // Thrust player around + an = victim->angle + ANGLE_1*P_Random(); + P_ThrustMobj(victim,an,richters<<(FRACBITS-1)); + } + } + } + else + { + for (playnum=0; playnum < MAXPLAYERS; playnum++) + { + localQuakeHappening[playnum] = false; + } + P_SetMobjState(actor, S_NULL); + } +} + + + + +//=========================================================================== +// +// Teleport other stuff +// +//=========================================================================== + +#define TELEPORT_LIFE 1 + +void A_TeloSpawnA(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX2); + if (mo) + { + mo->special1 = TELEPORT_LIFE; // Lifetime countdown + mo->angle = actor->angle; + mo->target = actor->target; + mo->momx = actor->momx>>1; + mo->momy = actor->momy>>1; + mo->momz = actor->momz>>1; + } +} + +void A_TeloSpawnB(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX3); + if (mo) + { + mo->special1 = TELEPORT_LIFE; // Lifetime countdown + mo->angle = actor->angle; + mo->target = actor->target; + mo->momx = actor->momx>>1; + mo->momy = actor->momy>>1; + mo->momz = actor->momz>>1; + } +} + +void A_TeloSpawnC(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX4); + if (mo) + { + mo->special1 = TELEPORT_LIFE; // Lifetime countdown + mo->angle = actor->angle; + mo->target = actor->target; + mo->momx = actor->momx>>1; + mo->momy = actor->momy>>1; + mo->momz = actor->momz>>1; + } +} + +void A_TeloSpawnD(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX5); + if (mo) + { + mo->special1 = TELEPORT_LIFE; // Lifetime countdown + mo->angle = actor->angle; + mo->target = actor->target; + mo->momx = actor->momx>>1; + mo->momy = actor->momy>>1; + mo->momz = actor->momz>>1; + } +} + +void A_CheckTeleRing(mobj_t *actor) +{ + if (actor->special1-- <= 0) + { + P_SetMobjState(actor, actor->info->deathstate); + } +} + + + + +// Dirt stuff + +void P_SpawnDirt(mobj_t *actor, fixed_t radius) +{ + fixed_t x,y,z; + int dtype=0; + mobj_t *mo; + angle_t angle; + + angle = P_Random()<<5; // <<24 >>19 + x = actor->x + FixedMul(radius,finecosine[angle]); + y = actor->y + FixedMul(radius,finesine[angle]); +// x = actor->x + ((P_Random()-P_Random())%radius)<y + ((P_Random()-P_Random()<z + (P_Random()<<9) + FRACUNIT; + switch(P_Random()%6) + { + case 0: + dtype = MT_DIRT1; + break; + case 1: + dtype = MT_DIRT2; + break; + case 2: + dtype = MT_DIRT3; + break; + case 3: + dtype = MT_DIRT4; + break; + case 4: + dtype = MT_DIRT5; + break; + case 5: + dtype = MT_DIRT6; + break; + } + mo = P_SpawnMobj(x,y,z,dtype); + if (mo) + { + mo->momz = P_Random()<<10; + } +} + + + + +//=========================================================================== +// +// Thrust floor stuff +// +// Thrust Spike Variables +// special1 pointer to dirt clump mobj +// special2 speed of raise +// args[0] 0 = lowered, 1 = raised +// args[1] 0 = normal, 1 = bloody +//=========================================================================== + +void A_ThrustInitUp(mobj_t *actor) +{ + actor->special2 = 5; // Raise speed + actor->args[0] = 1; // Mark as up + actor->floorclip = 0; + actor->flags = MF_SOLID; + actor->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP; + actor->special1 = 0L; +} + +void A_ThrustInitDn(mobj_t *actor) +{ + mobj_t *mo; + actor->special2 = 5; // Raise speed + actor->args[0] = 0; // Mark as down + actor->floorclip = actor->info->height; + actor->flags = 0; + actor->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP|MF2_DONTDRAW; + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_DIRTCLUMP); + actor->special1 = (int)mo; +} + + +void A_ThrustRaise(mobj_t *actor) +{ + if (A_RaiseMobj(actor)) + { // Reached it's target height + actor->args[0] = 1; + if (actor->args[1]) + P_SetMobjStateNF(actor, S_BTHRUSTINIT2_1); + else + P_SetMobjStateNF(actor, S_THRUSTINIT2_1); + } + + // Lose the dirt clump + if ((actor->floorclip < actor->height) && actor->special1) + { + P_RemoveMobj( (mobj_t *)actor->special1 ); + actor->special1 = 0; + } + + // Spawn some dirt + if (P_Random()<40) + P_SpawnDirt(actor, actor->radius); + actor->special2++; // Increase raise speed +} + +void A_ThrustLower(mobj_t *actor) +{ + if (A_SinkMobj(actor)) + { + actor->args[0] = 0; + if (actor->args[1]) + P_SetMobjStateNF(actor, S_BTHRUSTINIT1_1); + else + P_SetMobjStateNF(actor, S_THRUSTINIT1_1); + } +} + +void A_ThrustBlock(mobj_t *actor) +{ + actor->flags |= MF_SOLID; +} + +void A_ThrustImpale(mobj_t *actor) +{ + // Impale all shootables in radius + PIT_ThrustSpike(actor); +} + +//=========================================================================== +// +// A_SoAExplode - Suit of Armor Explode +// +//=========================================================================== + +void A_SoAExplode(mobj_t *actor) +{ + mobj_t *mo; + int i; + + for(i = 0; i < 10; i++) + { + mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12), + actor->y+((P_Random()-128)<<12), + actor->z+(P_Random()*actor->height/256), MT_ZARMORCHUNK); + P_SetMobjState(mo, mo->info->spawnstate+i); + if(mo) + { + mo->momz = ((P_Random()&7)+5)*FRACUNIT; + mo->momx = (P_Random()-P_Random())<<(FRACBITS-6); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-6); + } + } + if(actor->args[0]) + { // Spawn an item + if(!nomonsters + || !(mobjinfo[TranslateThingType[actor->args[0]]].flags&MF_COUNTKILL)) + { // Only spawn monsters if not -nomonsters + P_SpawnMobj(actor->x, actor->y, actor->z, + TranslateThingType[actor->args[0]]); + } + } + S_StartSound(mo, SFX_SUITOFARMOR_BREAK); + P_RemoveMobj(actor); +} + +//=========================================================================== +// +// A_BellReset1 +// +//=========================================================================== + +void A_BellReset1(mobj_t *actor) +{ + actor->flags |= MF_NOGRAVITY; + actor->height <<= 2; +} + +//=========================================================================== +// +// A_BellReset2 +// +//=========================================================================== + +void A_BellReset2(mobj_t *actor) +{ + actor->flags |= MF_SHOOTABLE; + actor->flags &= ~MF_CORPSE; + actor->health = 5; +} + + +//=========================================================================== +// +// A_FlameCheck +// +//=========================================================================== + +void A_FlameCheck(mobj_t *actor) +{ + if(!actor->args[0]--) // Called every 8 tics + { + P_SetMobjState(actor, S_NULL); + } +} + + +//=========================================================================== +// Bat Spawner Variables +// special1 frequency counter +// special2 +// args[0] frequency of spawn (1=fastest, 10=slowest) +// args[1] spread angle (0..255) +// args[2] +// args[3] duration of bats (in octics) +// args[4] turn amount per move (in degrees) +// +// Bat Variables +// special2 lifetime counter +// args[4] turn amount per move (in degrees) +//=========================================================================== + +void A_BatSpawnInit(mobj_t *actor) +{ + actor->special1 = 0; // Frequency count +} + +void A_BatSpawn(mobj_t *actor) +{ + mobj_t *mo; + int delta; + angle_t angle; + + // Countdown until next spawn + if (actor->special1-- > 0) return; + actor->special1 = actor->args[0]; // Reset frequency count + + delta = actor->args[1]; + if (delta==0) delta=1; + angle = actor->angle + (((P_Random()%delta)-(delta>>1))<<24); + mo = P_SpawnMissileAngle(actor, MT_BAT, angle, 0); + if (mo) + { + mo->args[0] = P_Random()&63; // floatbob index + mo->args[4] = actor->args[4]; // turn degrees + mo->special2 = actor->args[3]<<3; // Set lifetime + mo->target = actor; + } +} + + +void A_BatMove(mobj_t *actor) +{ + angle_t newangle; + fixed_t speed; + + if (actor->special2 < 0) + { + P_SetMobjState(actor, actor->info->deathstate); + } + actor->special2 -= 2; // Called every 2 tics + + if (P_Random()<128) + { + newangle = actor->angle + ANGLE_1*actor->args[4]; + } + else + { + newangle = actor->angle - ANGLE_1*actor->args[4]; + } + + // Adjust momentum vector to new direction + newangle >>= ANGLETOFINESHIFT; + speed = FixedMul(actor->info->speed, P_Random()<<10); + actor->momx = FixedMul(speed, finecosine[newangle]); + actor->momy = FixedMul(speed, finesine[newangle]); + + if (P_Random()<15) + S_StartSound(actor, SFX_BAT_SCREAM); + + // Handle Z movement + actor->z = actor->target->z + 2*FloatBobOffsets[actor->args[0]]; + actor->args[0] = (actor->args[0]+3)&63; +} + +//=========================================================================== +// +// A_TreeDeath +// +//=========================================================================== + +void A_TreeDeath(mobj_t *actor) +{ + if(!(actor->flags2&MF2_FIREDAMAGE)) + { + actor->height <<= 2; + actor->flags |= MF_SHOOTABLE; + actor->flags &= ~(MF_CORPSE+MF_DROPOFF); + actor->health = 35; + return; + } + else + { + P_SetMobjState(actor, actor->info->meleestate); + } +} + +//=========================================================================== +// +// A_NoGravity +// +//=========================================================================== + +void A_NoGravity(mobj_t *actor) +{ + actor->flags |= MF_NOGRAVITY; +} diff --git a/src/hexen/am_data.h b/src/hexen/am_data.h new file mode 100644 index 00000000..cf48ddb9 --- /dev/null +++ b/src/hexen/am_data.h @@ -0,0 +1,107 @@ + +//************************************************************************** +//** +//** am_data.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: am_data.h,v $ +//** $Revision: 1.2 $ +//** $Date: 96/01/06 18:37:22 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#ifndef __AMDATA_H__ +#define __AMDATA_H__ + +#pragma once + +// a line drawing of the player pointing right, starting from the middle. + +#define R ((8*PLAYERRADIUS)/7) + +mline_t player_arrow[] = { + { { -R+R/4, 0 }, { 0, 0} }, // center line. + { { -R+R/4, R/8 }, { R, 0} }, // blade + { { -R+R/4, -R/8 }, { R, 0 } }, + { { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece + { { -R+R/8, -R/4 }, { -R+R/8, R/4 } }, + { { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors + { { -R+R/8, R/4 }, { -R+R/4, R/4} }, + { { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel + { { -R-R/4, R/8 }, { -R+R/8, R/8 } }, + { { -R-R/4, -R/8}, { -R+R/8, -R/8 } } + }; + +/* +mline_t keysquare[] = { + { { 0, 0 }, { R/4, -R/2 } }, + { { R/4, -R/2 }, { R/2, -R/2 } }, + { { R/2, -R/2 }, { R/2, R/2 } }, + { { R/2, R/2 }, { R/4, R/2 } }, + { { R/4, R/2 }, { 0, 0 } }, // handle part type thing + { { 0, 0 }, { -R, 0 } }, // stem + { { -R, 0 }, { -R, -R/2 } }, // end lockpick part + { { -3*R/4, 0 }, { -3*R/4, -R/4 } } + }; +*/ + +/*mline_t player_arrow[] = { + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } + }; +*/ +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) +#define NUMKEYSQUARELINES (sizeof(keysquare)/sizeof(mline_t)) + +/* +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = { + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/6 } }, // -----> + { { R, 0 }, { R-R/2, -R/6 } }, + { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> + { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> + { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, + { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> + { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, + { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, + { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> + { { -R/6, -R/6 }, { 0, -R/6 } }, + { { 0, -R/6 }, { 0, R/4 } }, + { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> + { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, + { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } + }; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) +*/ + + +/* +#define R (FRACUNIT) +mline_t triangle_guy[] = { + { { -.867*R, -.5*R }, { .867*R, -.5*R } }, + { { .867*R, -.5*R } , { 0, R } }, + { { 0, R }, { -.867*R, -.5*R } } + }; +#undef R +#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) +*/ + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = { + { { -.5*R, -.7*R }, { R, 0 } }, + { { R, 0 }, { -.5*R, .7*R } }, + { { -.5*R, .7*R }, { -.5*R, -.7*R } } + }; +#undef R +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + +#endif diff --git a/src/hexen/am_map.c b/src/hexen/am_map.c new file mode 100644 index 00000000..be96477d --- /dev/null +++ b/src/hexen/am_map.c @@ -0,0 +1,1400 @@ + +//************************************************************************** +//** +//** am_map.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: am_map.c,v $ +//** $Revision: 1.22 $ +//** $Date: 96/01/06 18:37:23 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "am_map.h" +#include "am_data.h" +#include + +#define NUMALIAS 3 // Number of antialiased lines. + +int cheating = 0; +static int grid = 0; + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +boolean automapactive = false; +static int finit_width = SCREENWIDTH; +static int finit_height = SCREENHEIGHT-SBARHEIGHT-3; +static int f_x, f_y; // location of window on screen +static int f_w, f_h; // size of window on screen +static int lightlev; // used for funky strobing effect +static byte *fb; // pseudo-frame buffer +static int amclock; + +static mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) + +static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) + +// width/height of window on map (map coords) +static fixed_t m_w, m_h; +static fixed_t min_x, min_y; // based on level size +static fixed_t max_x, max_y; // based on level size +static fixed_t max_w, max_h; // max_x-min_x, max_y-min_y +static fixed_t min_w, min_h; // based on player size +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// old location used by the Follower routine +static mpoint_t f_oldloc; + +// used by MTOF to scale from map-to-frame-buffer coords +static fixed_t scale_mtof = INITSCALEMTOF; +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) +static fixed_t scale_ftom; + +static player_t *plr; // the player represented by an arrow +static vertex_t oldplr; + +//static patch_t *marknums[10]; // numbers used for marking by the automap +//static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are +//static int markpointnum = 0; // next point to be assigned + +static int followplayer = 1; // specifies whether to follow the player around + +static char cheat_kills[] = { 'k', 'i', 'l', 'l', 's' }; +static boolean ShowKills = 0; +static unsigned ShowKillsCount = 0; + +extern boolean viewactive; + +static byte antialias[NUMALIAS][8]= +{ + { 83, 84, 85, 86, 87, 88, 89, 90 }, + { 96, 96, 95, 94, 93, 92, 91, 90 }, + { 107, 108, 109, 110, 111, 112, 89, 90 } +}; + +/* +static byte *aliasmax[NUMALIAS] = { + &antialias[0][7], &antialias[1][7], &antialias[2][7] +};*/ + +static byte *maplump; // pointer to the raw data for the automap background. +static short mapystart=0; // y-value for the start of the map bitmap...used in + //the parallax stuff. +static short mapxstart=0; //x-value for the bitmap. + +//byte screens[][SCREENWIDTH*SCREENHEIGHT]; +//void V_MarkRect (int x, int y, int width, int height); + +// Functions + +void DrawWuLine(int X0, int Y0, int X1, int Y1, byte *BaseColor, + int NumLevels, unsigned short IntensityBits); + +void AM_DrawDeathmatchStats(void); +static void DrawWorldTimer(void); + +// Calculates the slope and slope according to the x-axis of a line +// segment in map coordinates (with the upright y-axis n' all) so +// that it can be used with the brain-dead drawing stuff. + +// Ripped out for Heretic +/* +void AM_getIslope(mline_t *ml, islope_t *is) +{ + int dx, dy; + + dy = ml->a.y - ml->b.y; + dx = ml->b.x - ml->a.x; + if (!dy) is->islp = (dx<0?-MAXINT:MAXINT); + else is->islp = FixedDiv(dx, dy); + if (!dx) is->slp = (dy<0?-MAXINT:MAXINT); + else is->slp = FixedDiv(dy, dx); +} +*/ + +void AM_activateNewScale(void) +{ + m_x += m_w/2; + m_y += m_h/2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w/2; + m_y -= m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +void AM_restoreScaleAndLoc(void) +{ + + m_w = old_m_w; + m_h = old_m_h; + if (!followplayer) + { + m_x = old_m_x; + m_y = old_m_y; + } else { + m_x = plr->mo->x - m_w/2; + m_y = plr->mo->y - m_h/2; + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w< max_x) max_x = vertexes[i].x; + if (vertexes[i].y < min_y) min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) max_y = vertexes[i].y; + } + max_w = max_x - min_x; + max_h = max_y - min_y; + min_w = 2*PLAYERRADIUS; + min_h = 2*PLAYERRADIUS; + + a = FixedDiv(f_w< max_x) + { + m_x = max_x - m_w/2; + m_paninc.x=0; + } + else if (m_x + m_w/2 < min_x) + { + m_x = min_x - m_w/2; + m_paninc.x=0; + } + if (m_y + m_h/2 > max_y) + { + m_y = max_y - m_h/2; + m_paninc.y=0; + } + else if (m_y + m_h/2 < min_y) + { + m_y = min_y - m_h/2; + m_paninc.y=0; + } +/* + mapxstart += MTOF(m_paninc.x+FRACUNIT/2); + mapystart -= MTOF(m_paninc.y+FRACUNIT/2); + if(mapxstart >= finit_width) + mapxstart -= finit_width; + if(mapxstart < 0) + mapxstart += finit_width; + if(mapystart >= finit_height) + mapystart -= finit_height; + if(mapystart < 0) + mapystart += finit_height; +*/ + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +void AM_initVariables(void) +{ + int pnum; + thinker_t *think; + mobj_t *mo; + + //static event_t st_notify = { ev_keyup, AM_MSGENTERED }; + + automapactive = true; + fb = screen; + + f_oldloc.x = MAXINT; + amclock = 0; + lightlev = 0; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum=0;pnummo->x; + oldplr.y = plr->mo->y; + m_x = plr->mo->x - m_w/2; + m_y = plr->mo->y - m_h/2; + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // load in the location of keys, if in baby mode + +// memset(KeyPoints, 0, sizeof(vertex_t)*3); + if(gameskill == sk_baby) + { + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) + { //not a mobj + continue; + } + mo = (mobj_t *)think; + } + } + + // inform the status bar of the change +//c ST_Responder(&st_notify); +} + +void AM_loadPics(void) +{ + maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC); +} + + +/* +void AM_clearMarks(void) +{ + int i; + for (i=0;i max_scale_mtof) scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + +static boolean stopped = true; + +void AM_Stop (void) +{ + //static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED }; + +// AM_unloadPics(); + automapactive = false; +// ST_Responder(&st_notify); + stopped = true; + BorderNeedRefresh = true; +} + +void AM_Start (void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) AM_Stop(); + stopped = false; + if(gamestate != GS_LEVEL) + { + return; // don't show automap if we aren't in a game! + } + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_initVariables(); + AM_loadPics(); +} + +// set the window scale to the maximum size + +void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// set the window scale to the minimum size + +void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +boolean AM_Responder (event_t *ev) +{ + int rc; + static int cheatstate=0; + static int bigstate=0; + + rc = false; + if (!automapactive) + { + if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY + && gamestate == GS_LEVEL) + { + AM_Start (); + SB_state = -1; + viewactive = false; + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + switch(ev->data1) + { + case AM_PANRIGHTKEY: // pan right + if (!followplayer) m_paninc.x = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANLEFTKEY: // pan left + if (!followplayer) m_paninc.x = -FTOM(F_PANINC); + else rc = false; + break; + case AM_PANUPKEY: // pan up + if (!followplayer) m_paninc.y = FTOM(F_PANINC); + else rc = false; + break; + case AM_PANDOWNKEY: // pan down + if (!followplayer) m_paninc.y = -FTOM(F_PANINC); + else rc = false; + break; + case AM_ZOOMOUTKEY: // zoom out + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + break; + case AM_ZOOMINKEY: // zoom in + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + break; + case AM_ENDKEY: + bigstate = 0; + viewactive = true; + AM_Stop (); + SB_state = -1; + break; + case AM_GOBIGKEY: + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else AM_restoreScaleAndLoc(); + break; + case AM_FOLLOWKEY: + followplayer = !followplayer; + f_oldloc.x = MAXINT; + P_SetMessage(plr, + followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true); + break; + default: + cheatstate=0; + rc = false; + } + + if(cheat_kills[ShowKillsCount] == ev->data1 && netgame && deathmatch) + { + ShowKillsCount++; + if(ShowKillsCount == 5) + { + ShowKillsCount = 0; + rc = false; + ShowKills ^= 1; + } + } + else + { + ShowKillsCount = 0; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + switch (ev->data1) + { + case AM_PANRIGHTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANLEFTKEY: + if (!followplayer) m_paninc.x = 0; + break; + case AM_PANUPKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_PANDOWNKEY: + if (!followplayer) m_paninc.y = 0; + break; + case AM_ZOOMOUTKEY: + case AM_ZOOMINKEY: + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + break; + } + } + return rc; +} + +void AM_changeWindowScale(void) +{ + + // Change the scaling multipliers + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale(); + else AM_activateNewScale(); +} + +void AM_doFollowPlayer(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { +// m_x = FTOM(MTOF(plr->mo->x - m_w/2)); +// m_y = FTOM(MTOF(plr->mo->y - m_h/2)); +// m_x = plr->mo->x - m_w/2; +// m_y = plr->mo->y - m_h/2; + m_x = FTOM(MTOF(plr->mo->x)) - m_w/2; + m_y = FTOM(MTOF(plr->mo->y)) - m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // do the parallax parchment scrolling. +/* + dmapx = (MTOF(plr->mo->x)-MTOF(f_oldloc.x)); //fixed point + dmapy = (MTOF(f_oldloc.y)-MTOF(plr->mo->y)); + + if(f_oldloc.x == MAXINT) //to eliminate an error when the user first + dmapx=0; //goes into the automap. + mapxstart += dmapx; + mapystart += dmapy; + + while(mapxstart >= finit_width) + mapxstart -= finit_width; + while(mapxstart < 0) + mapxstart += finit_width; + while(mapystart >= finit_height) + mapystart -= finit_height; + while(mapystart < 0) + mapystart += finit_height; +*/ + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + } +} + +// Ripped out for Heretic +/* +void AM_updateLightLev(void) +{ + static nexttic = 0; +//static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; + static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; + static int litelevelscnt = 0; + + // Change light level + if (amclock>nexttic) + { + lightlev = litelevels[litelevelscnt++]; + if (litelevelscnt == sizeof(litelevels)/sizeof(int)) litelevelscnt = 0; + nexttic = amclock + 6 - (amclock % 6); + } +} +*/ + +void AM_Ticker (void) +{ + + if (!automapactive) return; + + amclock++; + + if (followplayer) AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) AM_changeWindowLoc(); + // Update light level +// AM_updateLightLev(); + +} + +void AM_clearFB(int color) +{ + int i, j; + int dmapx; + int dmapy; + + if(followplayer) + { + dmapx = (MTOF(plr->mo->x)-MTOF(oldplr.x)); //fixed point + dmapy = (MTOF(oldplr.y)-MTOF(plr->mo->y)); + + oldplr.x = plr->mo->x; + oldplr.y = plr->mo->y; +// if(f_oldloc.x == MAXINT) //to eliminate an error when the user first +// dmapx=0; //goes into the automap. + mapxstart += dmapx>>1; + mapystart += dmapy>>1; + + while(mapxstart >= finit_width) + mapxstart -= finit_width; + while(mapxstart < 0) + mapxstart += finit_width; + while(mapystart >= finit_height) + mapystart -= finit_height; + while(mapystart < 0) + mapystart += finit_height; + } + else + { + mapxstart += (MTOF(m_paninc.x)>>1); + mapystart -= (MTOF(m_paninc.y)>>1); + if(mapxstart >= finit_width) + mapxstart -= finit_width; + if(mapxstart < 0) + mapxstart += finit_width; + if(mapystart >= finit_height) + mapystart -= finit_height; + if(mapystart < 0) + mapystart += finit_height; + } + + //blit the automap background to the screen. + j=mapystart*finit_width; + for(i = 0; i < SCREENHEIGHT-SBARHEIGHT; i++) + { + memcpy(screen+i*finit_width, maplump+j+mapxstart, + finit_width-mapxstart); + memcpy(screen+i*finit_width+finit_width-mapxstart, maplump+j, + mapxstart); + j += finit_width; + if(j >= finit_height*finit_width) + j=0; + } + +// memcpy(screen, maplump, finit_width*finit_height); +// memset(fb, color, f_w*f_h); +} + +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If I need the speed, will +// hash algorithm to the common cases. + +boolean AM_clipMline(mline_t *ml, fline_t *fl) +{ + enum { LEFT=1, RIGHT=2, BOTTOM=4, TOP=8 }; + register outcode1 = 0, outcode2 = 0, outside; + fpoint_t tmp; + int dx, dy; + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) (oc) |= TOP; \ + else if ((my) >= f_h) (oc) |= BOTTOM; \ + if ((mx) < 0) (oc) |= LEFT; \ + else if ((mx) >= f_w) (oc) |= RIGHT + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) outcode1 = TOP; + else if (ml->a.y < m_y) outcode1 = BOTTOM; + if (ml->b.y > m_y2) outcode2 = TOP; + else if (ml->b.y < m_y) outcode2 = BOTTOM; + if (outcode1 & outcode2) return false; // trivially outside + + if (ml->a.x < m_x) outcode1 |= LEFT; + else if (ml->a.x > m_x2) outcode1 |= RIGHT; + if (ml->b.x < m_x) outcode2 |= LEFT; + else if (ml->b.x > m_x2) outcode2 |= RIGHT; + if (outcode1 & outcode2) return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + if (outcode1 & outcode2) return false; + + while (outcode1 | outcode2) + { + // may be partially inside box + // find an outside point + if (outcode1) outside = outcode1; + else outside = outcode2; + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y))/dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy; + tmp.y = f_h-1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx; + tmp.x = f_w-1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(-fl->a.x))/dx; + tmp.x = 0; + } + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } else { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + if (outcode1 & outcode2) return false; // trivially outside + } + + return true; +} +#undef DOOUTCODE + +// Classic Bresenham w/ whatever optimizations I need for speed + +void AM_drawFline(fline_t *fl, int color) +{ + register int x, y, dx, dy, sx, sy, ax, ay, d; + //static fuck = 0; + + switch(color) + { + case WALLCOLORS: + DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, + &antialias[0][0], 8, 3); + break; + case FDWALLCOLORS: + DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, + &antialias[1][0], 8, 3); + break; + case CDWALLCOLORS: + DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, + &antialias[2][0], 8, 3); + break; + default: + { + // For debugging only + if ( fl->a.x < 0 || fl->a.x >= f_w + || fl->a.y < 0 || fl->a.y >= f_h + || fl->b.x < 0 || fl->b.x >= f_w + || fl->b.y < 0 || fl->b.y >= f_h) + { + //fprintf(stderr, "fuck %d \r", fuck++); + return; + } + + #define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO! + + dx = fl->b.x - fl->a.x; + ax = 2 * (dx<0 ? -dx : dx); + sx = dx<0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy<0 ? -dy : dy); + sy = dy<0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax/2; + while (1) + { + DOT(x,y,color); + if (x == fl->b.x) return; + if (d>=0) + { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } else { + d = ax - ay/2; + while (1) + { + DOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } + } + } +} + +/* Wu antialiased line drawer. + * (X0,Y0),(X1,Y1) = line to draw + * BaseColor = color # of first color in block used for antialiasing, the + * 100% intensity version of the drawing color + * NumLevels = size of color block, with BaseColor+NumLevels-1 being the + * 0% intensity version of the drawing color + * IntensityBits = log base 2 of NumLevels; the # of bits used to describe + * the intensity of the drawing color. 2**IntensityBits==NumLevels + */ +void PUTDOT(short xx,short yy,byte *cc, byte *cm) +{ + static int oldyy; + static int oldyyshifted; + byte *oldcc=cc; + + if(xx < 32) + cc += 7-(xx>>2); + else if(xx > (finit_width - 32)) + cc += 7-((finit_width-xx) >> 2); +// if(cc==oldcc) //make sure that we don't double fade the corners. +// { + if(yy < 32) + cc += 7-(yy>>2); + else if(yy > (finit_height - 32)) + cc += 7-((finit_height-yy) >> 2); +// } + if(cc > cm && cm != NULL) + { + cc = cm; + } + else if(cc > oldcc+6) // don't let the color escape from the fade table... + { + cc=oldcc+6; + } + if(yy == oldyy+1) + { + oldyy++; + oldyyshifted += 320; + } + else if(yy == oldyy-1) + { + oldyy--; + oldyyshifted -= 320; + } + else if(yy != oldyy) + { + oldyy = yy; + oldyyshifted = yy*320; + } + fb[oldyyshifted+xx] = *(cc); +// fb[(yy)*f_w+(xx)]=*(cc); +} + +void DrawWuLine(int X0, int Y0, int X1, int Y1, byte *BaseColor, + int NumLevels, unsigned short IntensityBits) +{ + unsigned short IntensityShift, ErrorAdj, ErrorAcc; + unsigned short ErrorAccTemp, Weighting, WeightingComplementMask; + short DeltaX, DeltaY, Temp, XDir; + + /* Make sure the line runs top to bottom */ + if (Y0 > Y1) { + Temp = Y0; Y0 = Y1; Y1 = Temp; + Temp = X0; X0 = X1; X1 = Temp; + } + /* Draw the initial pixel, which is always exactly intersected by + the line and so needs no weighting */ + PUTDOT(X0, Y0, &BaseColor[0], NULL); + + if ((DeltaX = X1 - X0) >= 0) { + XDir = 1; + } else { + XDir = -1; + DeltaX = -DeltaX; /* make DeltaX positive */ + } + /* Special-case horizontal, vertical, and diagonal lines, which + require no weighting because they go right through the center of + every pixel */ + if ((DeltaY = Y1 - Y0) == 0) { + /* Horizontal line */ + while (DeltaX-- != 0) { + X0 += XDir; + PUTDOT(X0, Y0, &BaseColor[0], NULL); + } + return; + } + if (DeltaX == 0) { + /* Vertical line */ + do { + Y0++; + PUTDOT(X0, Y0, &BaseColor[0], NULL); + } while (--DeltaY != 0); + return; + } + //diagonal line. + if (DeltaX == DeltaY) { + do { + X0 += XDir; + Y0++; + PUTDOT(X0, Y0, &BaseColor[0], NULL); + } while (--DeltaY != 0); + return; + } + /* Line is not horizontal, diagonal, or vertical */ + ErrorAcc = 0; /* initialize the line error accumulator to 0 */ + /* # of bits by which to shift ErrorAcc to get intensity level */ + IntensityShift = 16 - IntensityBits; + /* Mask used to flip all bits in an intensity weighting, producing the + result (1 - intensity weighting) */ + WeightingComplementMask = NumLevels - 1; + /* Is this an X-major or Y-major line? */ + if (DeltaY > DeltaX) { + /* Y-major line; calculate 16-bit fixed-point fractional part of a + pixel that X advances each time Y advances 1 pixel, truncating the + result so that we won't overrun the endpoint along the X axis */ + ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY; + /* Draw all pixels other than the first and last */ + while (--DeltaY) { + ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ + ErrorAcc += ErrorAdj; /* calculate error for next pixel */ + if (ErrorAcc <= ErrorAccTemp) { + /* The error accumulator turned over, so advance the X coord */ + X0 += XDir; + } + Y0++; /* Y-major, so always advance Y */ + /* The IntensityBits most significant bits of ErrorAcc give us the + intensity weighting for this pixel, and the complement of the + weighting for the paired pixel */ + Weighting = ErrorAcc >> IntensityShift; + PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); + PUTDOT(X0 + XDir, Y0, + &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); + } + /* Draw the final pixel, which is always exactly intersected by the line + and so needs no weighting */ + PUTDOT(X1, Y1, &BaseColor[0], NULL); + return; + } + /* It's an X-major line; calculate 16-bit fixed-point fractional part of a + pixel that Y advances each time X advances 1 pixel, truncating the + result to avoid overrunning the endpoint along the X axis */ + ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX; + /* Draw all pixels other than the first and last */ + while (--DeltaX) { + ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ + ErrorAcc += ErrorAdj; /* calculate error for next pixel */ + if (ErrorAcc <= ErrorAccTemp) { + /* The error accumulator turned over, so advance the Y coord */ + Y0++; + } + X0 += XDir; /* X-major, so always advance X */ + /* The IntensityBits most significant bits of ErrorAcc give us the + intensity weighting for this pixel, and the complement of the + weighting for the paired pixel */ + Weighting = ErrorAcc >> IntensityShift; + PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); + PUTDOT(X0, Y0 + 1, + &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); + + } + /* Draw the final pixel, which is always exactly intersected by the line + and so needs no weighting */ + PUTDOT(X1, Y1, &BaseColor[0], NULL); +} + +void AM_drawMline(mline_t *ml, int color) +{ + static fline_t fl; + + if (AM_clipMline(ml, &fl)) + AM_drawFline(&fl, color); // draws it on frame buffer using fb coords + +} + +void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + + // Figure out start of vertical gridlines + start = m_x; + if ((start-bmaporgx)%(MAPBLOCKUNITS<x; + l.a.y = lines[i].v1->y; + l.b.x = lines[i].v2->x; + l.b.y = lines[i].v2->y; + if (cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & LINE_NEVERSEE) && !cheating) + continue; + if (!lines[i].backsector) + { + AM_drawMline(&l, WALLCOLORS+lightlev); + } else { + if (lines[i].flags & ML_SECRET) // secret door + { + if (cheating) AM_drawMline(&l, 0); + else AM_drawMline(&l, WALLCOLORS+lightlev); + } + else if(lines[i].special == 13 || lines[i].special == 83) + { // Locked door line -- all locked doors are greed + AM_drawMline(&l, GREENKEY); + } + else if(lines[i].special == 70 || lines[i].special == 71) + { // intra-level teleports are blue + AM_drawMline(&l, BLUEKEY); + } + else if(lines[i].special == 74 || lines[i].special == 75) + { // inter-level teleport/game-winning exit -- both are red + AM_drawMline(&l, BLOODRED); + } + else if (lines[i].backsector->floorheight + != lines[i].frontsector->floorheight) { + AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change + } else if (lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight) { + AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change + } else if (cheating) { + AM_drawMline(&l, TSWALLCOLORS+lightlev); + } + } + } else if (plr->powers[pw_allmap]) + { + if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3); + } + } + +} + +void AM_rotate(fixed_t *x, fixed_t *y, angle_t a) +{ + fixed_t tmpx; + + tmpx = FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT]) + - FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]); + *y = FixedMul(*x,finesine[a>>ANGLETOFINESHIFT]) + + FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]); + *x = tmpx; +} + +void AM_drawLineCharacter(mline_t *lineguy, int lineguylines, fixed_t scale, + angle_t angle, int color, fixed_t x, fixed_t y) +{ + int i; + mline_t l; + + for (i=0;imo->angle, + WHITE, plr->mo->x, plr->mo->y); + return; + } + + for(i = 0; i < MAXPLAYERS; i++) + { + their_color++; + p = &players[i]; + if(deathmatch && !singledemo && p != plr) + { + continue; + } + if (!playeringame[i]) continue; + color = their_colors[their_color]; + AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, + color, p->mo->x, p->mo->y); + } +} + +void AM_drawThings(int colors, int colorrange) +{ + int i; + mobj_t *t; + + for (i=0;iangle, colors+lightlev, t->x, t->y); + t = t->snext; + } + } +} + +/* +void AM_drawMarks(void) +{ + int i, fx, fy, w, h; + + for (i=0;iwidth); + h = SHORT(marknums[i]->height); + fx = CXMTOF(markpoints[i].x); + fy = CYMTOF(markpoints[i].y); + if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) + V_DrawPatch(fx, fy, marknums[i]); + } + } +} +*/ +/* +void AM_drawkeys(void) +{ + if(KeyPoints[0].x != 0 || KeyPoints[0].y != 0) + { + AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, YELLOWKEY, + KeyPoints[0].x, KeyPoints[0].y); + } + if(KeyPoints[1].x != 0 || KeyPoints[1].y != 0) + { + AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, GREENKEY, + KeyPoints[1].x, KeyPoints[1].y); + } + if(KeyPoints[2].x != 0 || KeyPoints[2].y != 0) + { + AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, BLUEKEY, + KeyPoints[2].x, KeyPoints[2].y); + } +} +*/ + +/* +void AM_drawCrosshair(int color) +{ + fb[(f_w*(f_h+1))/2] = color; // single point for now +} +*/ + +void AM_Drawer (void) +{ + if (!automapactive) return; + + UpdateState |= I_FULLSCRN; + AM_clearFB(BACKGROUND); + if (grid) AM_drawGrid(GRIDCOLORS); + AM_drawWalls(); + AM_drawPlayers(); + DrawWorldTimer(); + + if (cheating==2) AM_drawThings(THINGCOLORS, THINGRANGE); + +// AM_drawCrosshair(XHAIRCOLORS); +// AM_drawMarks(); +// if(gameskill == sk_baby) AM_drawkeys(); + + MN_DrTextA(P_GetMapName(gamemap), 38, 144); + if(ShowKills && netgame && deathmatch) + { + AM_DrawDeathmatchStats(); + } +// I_Update(); +// V_MarkRect(f_x, f_y, f_w, f_h); + +} + +//=========================================================================== +// +// AM_DrawDeathmatchStats +// +//=========================================================================== + +// 8-player note: Proper player color names here, too + +char *PlayerColorText[MAXPLAYERS] = +{ + "BLUE:", + "RED:", + "YELLOW:", + "GREEN:", + "JADE:", + "WHITE:", + "HAZEL:", + "PURPLE:" +}; + +void AM_DrawDeathmatchStats(void) +{ + int i, j, k, m; + int fragCount[MAXPLAYERS]; + int order[MAXPLAYERS]; + char textBuffer[80]; + int yPosition; + + for(i = 0; i < MAXPLAYERS; i++) + { + fragCount[i] = 0; + order[i] = -1; + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + else + { + for(j = 0; j < MAXPLAYERS; j++) + { + if(playeringame[j]) + { + fragCount[i] += players[i].frags[j]; + } + } + for(k = 0; k < MAXPLAYERS; k++) + { + if(order[k] == -1) + { + order[k] = i; + break; + } + else if(fragCount[i] > fragCount[order[k]]) + { + for(m = MAXPLAYERS-1; m > k; m--) + { + order[m] = order[m-1]; + } + order[k] = i; + break; + } + } + } + } + yPosition = 15; + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[order[i]]) + { + continue; + } + else + { + MN_DrTextA(PlayerColorText[order[i]], 8, yPosition); + sprintf(textBuffer, "%d", fragCount[order[i]]); + MN_DrTextA(textBuffer, 80, yPosition); + yPosition += 10; + } + } +} + +//=========================================================================== +// +// DrawWorldTimer +// +//=========================================================================== + +static void DrawWorldTimer(void) +{ + int days; + int hours; + int minutes; + int seconds; + int worldTimer; + char timeBuffer[15]; + char dayBuffer[20]; + + worldTimer = players[consoleplayer].worldTimer; + + worldTimer /= 35; + days = worldTimer/86400; + worldTimer -= days*86400; + hours = worldTimer/3600; + worldTimer -= hours*3600; + minutes = worldTimer/60; + worldTimer -= minutes*60; + seconds = worldTimer; + + sprintf(timeBuffer, "%.2d : %.2d : %.2d", hours, minutes,seconds); + MN_DrTextA(timeBuffer, 240, 8); + + if (days) + { + if (days==1) + { + sprintf(dayBuffer, "%.2d DAY", days); + } + else + { + sprintf(dayBuffer, "%.2d DAYS", days); + } + MN_DrTextA(dayBuffer, 240, 20); + if (days >= 5) + { + MN_DrTextA("YOU FREAK!!!", 230, 35); + } + } +} diff --git a/src/hexen/am_map.h b/src/hexen/am_map.h new file mode 100644 index 00000000..f37608b5 --- /dev/null +++ b/src/hexen/am_map.h @@ -0,0 +1,143 @@ + +//************************************************************************** +//** +//** am_map.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: am_map.h,v $ +//** $Revision: 1.3 $ +//** $Date: 95/12/31 19:47:21 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +#pragma once + +// For use if I do walls with outsides/insides +#define REDS 12*8 +#define REDRANGE 1//16 +#define BLUES (256-4*16+8) +#define BLUERANGE 1//8 +#define GREENS (33*8) +#define GREENRANGE 1//16 +#define GRAYS (5*8) +#define GRAYSRANGE 1//16 +#define BROWNS (14*8) +#define BROWNRANGE 1//16 +#define YELLOWS 10*8 +#define YELLOWRANGE 1 +#define BLACK 0 +#define WHITE 4*8 +#define PARCH 13*8-1 +#define BLOODRED 177 +#define BLUEKEY 157 +#define YELLOWKEY 137 +#define GREENKEY 198 + +// Automap colors + +#define AM_PLR1_COLOR 157 // Blue +#define AM_PLR2_COLOR 177 // Red +#define AM_PLR3_COLOR 137 // Yellow +#define AM_PLR4_COLOR 198 // Green +#define AM_PLR5_COLOR 215 // Jade +#define AM_PLR6_COLOR 32 // White +#define AM_PLR7_COLOR 106 // Hazel +#define AM_PLR8_COLOR 234 // Purple + +#define BACKGROUND PARCH +#define YOURCOLORS WHITE +#define YOURRANGE 0 +#define WALLCOLORS REDS +#define WALLRANGE REDRANGE +#define TSWALLCOLORS GRAYS +#define TSWALLRANGE GRAYSRANGE +#define FDWALLCOLORS BROWNS +#define FDWALLRANGE BROWNRANGE +#define CDWALLCOLORS YELLOWS +#define CDWALLRANGE YELLOWRANGE +#define THINGCOLORS GREENS +#define THINGRANGE GREENRANGE +#define SECRETWALLCOLORS WALLCOLORS +#define SECRETWALLRANGE WALLRANGE +#define GRIDCOLORS (GRAYS + GRAYSRANGE/2) +#define GRIDRANGE 0 +#define XHAIRCOLORS GRAYS + +// drawing stuff +#define FB 0 + +#define KEY_TAB 9 +#define AM_PANDOWNKEY KEY_DOWNARROW +#define AM_PANUPKEY KEY_UPARROW +#define AM_PANRIGHTKEY KEY_RIGHTARROW +#define AM_PANLEFTKEY KEY_LEFTARROW +//#define AM_PANDOWNKEY SC_DOWNARROW +//#define AM_PANUPKEY SC_UPARROW +//#define AM_PANRIGHTKEY SC_RIGHTARROW +//#define AM_PANLEFTKEY SC_LEFTARROW + +#define AM_ZOOMINKEY '=' +//#define AM_ZOOMINKEY 13 +//#define AM_ZOOMOUTKEY 12 + #define AM_ZOOMOUTKEY '-' +#define AM_STARTKEY KEY_TAB +#define AM_ENDKEY KEY_TAB +#define AM_GOBIGKEY '0' +//#define AM_GOBIGKEY 11 +//#define AM_FOLLOWKEY 33 +//#define AM_GRIDKEY 34 +#define AM_FOLLOWKEY 'f' +#define AM_GRIDKEY 'g' + +#define AM_NUMMARKPOINTS 10 + +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + +#define INITSCALEMTOF (.2*FRACUNIT) // scale on entry +// how much the automap moves window per tic in frame-buffer coordinates +#define F_PANINC 4 // moves 140 pixels in 1 second +// how much zoom-in per tic +#define M_ZOOMIN ((int) (1.02*FRACUNIT)) // goes to 2x in 1 second +// how much zoom-out per tic +#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // pulls out to 0.5x in 1 second + +// translates between frame-buffer and map distances +#define FTOM(x) FixedMul(((x)<<16),scale_ftom) +#define MTOF(x) (FixedMul((x),scale_mtof)>>16) +// translates between frame-buffer and map coordinates +#define CXMTOF(x) (f_x + MTOF((x)-m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) + +// the following is crap +#define LINE_NEVERSEE ML_DONTDRAW + +typedef struct +{ + int x, y; +} fpoint_t; + +typedef struct +{ + fpoint_t a, b; +} fline_t; + +typedef vertex_t mpoint_t; + +typedef struct +{ + mpoint_t a, b; +} mline_t; + +typedef struct +{ + fixed_t slp, islp; +} islope_t; + +// extern int f_x, f_y, f_w, f_h; + +#endif diff --git a/src/hexen/ct_chat.c b/src/hexen/ct_chat.c new file mode 100644 index 00000000..a4225631 --- /dev/null +++ b/src/hexen/ct_chat.c @@ -0,0 +1,497 @@ + +//************************************************************************** +//** +//** ct_chat.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: ct_chat.c,v $ +//** $Revision: 1.12 $ +//** $Date: 96/01/16 10:35:26 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include +#include +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +#define NUMKEYS 256 + +#define QUEUESIZE 128 +#define MESSAGESIZE 128 +#define MESSAGELEN 265 + +// 8-player note: Change this stuff (CT_PLR_*, and the key mappings) +enum +{ + CT_PLR_BLUE = 1, + CT_PLR_RED, + CT_PLR_YELLOW, + CT_PLR_GREEN, + CT_PLR_PLAYER5, + CT_PLR_PLAYER6, + CT_PLR_PLAYER7, + CT_PLR_PLAYER8, + CT_PLR_ALL +}; + +#define CT_KEY_BLUE 'b' +#define CT_KEY_RED 'r' +#define CT_KEY_YELLOW 'y' +#define CT_KEY_GREEN 'g' +#define CT_KEY_PLAYER5 'j' // Jade +#define CT_KEY_PLAYER6 'w' // White +#define CT_KEY_PLAYER7 'h' // Hazel +#define CT_KEY_PLAYER8 'p' // Purple +#define CT_KEY_ALL 't' +#define CT_ESCAPE 6 + +// Public data + +void CT_Init(void); +void CT_Drawer(void); +boolean CT_Responder(event_t *ev); +void CT_Ticker(void); +char CT_dequeueChatChar(void); + +boolean chatmodeon; + +// Private data + +void CT_queueChatChar(char ch); +void CT_ClearChatMessage(int player); +void CT_AddChar(int player, char c); +void CT_BackSpace(int player); + +int head; +int tail; +byte ChatQueue[QUEUESIZE]; +int chat_dest[MAXPLAYERS]; +char chat_msg[MAXPLAYERS][MESSAGESIZE]; +char plr_lastmsg[MAXPLAYERS][MESSAGESIZE+9]; +int msgptr[MAXPLAYERS]; +int msglen[MAXPLAYERS]; + +boolean cheated; + +static int FontABaseLump; + +char *CT_FromPlrText[MAXPLAYERS] = +{ + "BLUE: ", + "RED: ", + "YELLOW: ", + "GREEN: ", + "JADE: ", + "WHITE: ", + "HAZEL: ", + "PURPLE: " +}; + +char *chat_macros[10]; + +boolean altdown; +boolean shiftdown; + +extern boolean usearti; + +//=========================================================================== +// +// CT_Init +// +// Initialize chat mode data +//=========================================================================== + +void CT_Init(void) +{ + int i; + + head = 0; //initialize the queue index + tail = 0; + chatmodeon = false; + memset(ChatQueue, 0, QUEUESIZE); + for(i = 0; i < MAXPLAYERS; i++) + { + chat_dest[i] = 0; + msgptr[i] = 0; + memset(plr_lastmsg[i], 0, MESSAGESIZE); + memset(chat_msg[i], 0, MESSAGESIZE); + } + FontABaseLump = W_GetNumForName("FONTA_S")+1; + return; +} + +//=========================================================================== +// +// CT_Stop +// +//=========================================================================== + +void CT_Stop(void) +{ + chatmodeon = false; + return; +} + +//=========================================================================== +// +// CT_Responder +// +//=========================================================================== + +boolean CT_Responder(event_t *ev) +{ + char *macro; + + int sendto; + + if(!netgame) + { + return false; + } + if(ev->data1 == KEY_RALT) + { + altdown = (ev->type == ev_keydown); + return false; + } + if(ev->data1 == KEY_RSHIFT) + { + shiftdown = (ev->type == ev_keydown); + return false; + } + if(gamestate != GS_LEVEL || ev->type != ev_keydown) + { + return false; + } + if(!chatmodeon) + { + sendto = 0; + if(ev->data1 == CT_KEY_ALL) + { + sendto = CT_PLR_ALL; + } + else if(ev->data1 == CT_KEY_GREEN) + { + sendto = CT_PLR_GREEN; + } + else if(ev->data1 == CT_KEY_YELLOW) + { + sendto = CT_PLR_YELLOW; + } + else if(ev->data1 == CT_KEY_RED) + { + sendto = CT_PLR_RED; + } + else if(ev->data1 == CT_KEY_BLUE) + { + sendto = CT_PLR_BLUE; + } + else if(ev->data1 == CT_KEY_PLAYER5) + { + sendto = CT_PLR_PLAYER5; + } + else if(ev->data1 == CT_KEY_PLAYER6) + { + sendto = CT_PLR_PLAYER6; + } + else if(ev->data1 == CT_KEY_PLAYER7) + { + sendto = CT_PLR_PLAYER7; + } + else if(ev->data1 == CT_KEY_PLAYER8) + { + sendto = CT_PLR_PLAYER8; + } + if(sendto == 0 || (sendto != CT_PLR_ALL && !playeringame[sendto-1]) + || sendto == consoleplayer+1) + { + return false; + } + CT_queueChatChar(sendto); + chatmodeon = true; + return true; + } + else + { + if(altdown) + { + if(ev->data1 >= '0' && ev->data1 <= '9') + { + if(ev->data1 == '0') + { // macro 0 comes after macro 9 + ev->data1 = '9'+1; + } + macro = chat_macros[ev->data1-'1']; + CT_queueChatChar(KEY_ENTER); //send old message + CT_queueChatChar(chat_dest[consoleplayer]); // chose the dest. + while(*macro) + { + CT_queueChatChar(toupper(*macro++)); + } + CT_queueChatChar(KEY_ENTER); //send it off... + CT_Stop(); + return true; + } + } + if(ev->data1 == KEY_ENTER) + { + CT_queueChatChar(KEY_ENTER); + usearti = false; + CT_Stop(); + return true; + } + else if(ev->data1 == KEY_ESCAPE) + { + CT_queueChatChar(CT_ESCAPE); + CT_Stop(); + return true; + } + else if(ev->data1 >= 'a' && ev->data1 <= 'z') + { + CT_queueChatChar(ev->data1-32); + return true; + } + else if(shiftdown) + { + if(ev->data1 == '1') + { + CT_queueChatChar('!'); + return true; + } + else if(ev->data1 == '/') + { + CT_queueChatChar('?'); + return true; + } + } + if(ev->data1 == ' ' || ev->data1 == ',' || ev->data1 == '.' + || (ev->data1 >= '0' && ev->data1 <= '9') || ev->data1 == '\'' + || ev->data1 == KEY_BACKSPACE || ev->data1 == '-' || ev->data1 == '=') + { + CT_queueChatChar(ev->data1); + return true; + } + } + return false; +} + +//=========================================================================== +// +// CT_Ticker +// +//=========================================================================== + +void CT_Ticker(void) +{ + int i; + int j; + char c; + int numplayers; + + for(i=0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + if((c = players[i].cmd.chatchar) != 0) + { + if(c <= CT_PLR_ALL) + { + chat_dest[i] = c; + continue; + } + else if(c == CT_ESCAPE) + { + CT_ClearChatMessage(i); + } + else if(c == KEY_ENTER) + { + numplayers = 0; + for(j = 0; j < MAXPLAYERS; j++) + { + numplayers += playeringame[j]; + } + CT_AddChar(i, 0); // set the end of message character + if(numplayers > 2) + { + strcpy(plr_lastmsg[i], CT_FromPlrText[i]); + strcat(plr_lastmsg[i], chat_msg[i]); + } + else + { + strcpy(plr_lastmsg[i], chat_msg[i]); + } + if(i != consoleplayer && (chat_dest[i] == consoleplayer+1 + || chat_dest[i] == CT_PLR_ALL) && *chat_msg[i]) + { + P_SetMessage(&players[consoleplayer], plr_lastmsg[i], + true); + S_StartSound(NULL, SFX_CHAT); + } + else if(i == consoleplayer && (*chat_msg[i])) + { + if(numplayers <= 1) + { + P_SetMessage(&players[consoleplayer], + "THERE ARE NO OTHER PLAYERS IN THE GAME!", true); + S_StartSound(NULL, SFX_CHAT); + } + } + CT_ClearChatMessage(i); + } + else if(c == KEY_BACKSPACE) + { + CT_BackSpace(i); + } + else + { + CT_AddChar(i, c); + } + } + } + return; +} + +//=========================================================================== +// +// CT_Drawer +// +//=========================================================================== + +void CT_Drawer(void) +{ + int i; + int x; + patch_t *patch; + + if(chatmodeon) + { + x = 25; + for(i = 0; i < msgptr[consoleplayer]; i++) + { + if(chat_msg[consoleplayer][i] < 33) + { + x += 6; + } + else + { + patch=W_CacheLumpNum(FontABaseLump+ + chat_msg[consoleplayer][i]-33, PU_CACHE); + V_DrawPatch(x, 10, patch); + x += patch->width; + } + } + V_DrawPatch(x, 10, W_CacheLumpName("FONTA59", PU_CACHE)); + BorderTopRefresh = true; + UpdateState |= I_MESSAGES; + } +} + +//=========================================================================== +// +// CT_queueChatChar +// +//=========================================================================== + +void CT_queueChatChar(char ch) +{ + if((tail+1)&(QUEUESIZE-1) == head) + { // the queue is full + return; + } + ChatQueue[tail] = ch; + tail = (tail+1)&(QUEUESIZE-1); +} + +//=========================================================================== +// +// CT_dequeueChatChar +// +//=========================================================================== + +char CT_dequeueChatChar(void) +{ + byte temp; + + if(head == tail) + { // queue is empty + return 0; + } + temp = ChatQueue[head]; + head = (head+1)&(QUEUESIZE-1); + return temp; +} + +//=========================================================================== +// +// CT_AddChar +// +//=========================================================================== + +void CT_AddChar(int player, char c) +{ + patch_t *patch; + + if(msgptr[player]+1 >= MESSAGESIZE || msglen[player] >= MESSAGELEN) + { // full. + return; + } + chat_msg[player][msgptr[player]] = c; + msgptr[player]++; + if(c < 33) + { + msglen[player] += 6; + } + else + { + patch = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + msglen[player] += patch->width; + } +} + +//=========================================================================== +// +// CT_BackSpace +// +// Backs up a space, when the user hits (obviously) backspace +//=========================================================================== + +void CT_BackSpace(int player) +{ + patch_t *patch; + char c; + + if(msgptr[player] == 0) + { // message is already blank + return; + } + msgptr[player]--; + c = chat_msg[player][msgptr[player]]; + if(c < 33) + { + msglen[player] -= 6; + } + else + { + patch = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + msglen[player] -= patch->width; + } + chat_msg[player][msgptr[player]] = 0; +} + +//=========================================================================== +// +// CT_ClearChatMessage +// +// Clears out the data for the chat message, but the player's message +// is still saved in plrmsg. +//=========================================================================== + +void CT_ClearChatMessage(int player) +{ + memset(chat_msg[player], 0, MESSAGESIZE); + msgptr[player] = 0; + msglen[player] = 0; +} diff --git a/src/hexen/ct_chat.h b/src/hexen/ct_chat.h new file mode 100644 index 00000000..0f8b2826 --- /dev/null +++ b/src/hexen/ct_chat.h @@ -0,0 +1,15 @@ +// +// Chat mode stuff +// + +#define CT_PLR_GREEN 1 +#define CT_PLR_YELLOW 2 +#define CT_PLR_RED 3 +#define CT_PLR_BLUE 4 +#define CT_PLR_ALL 5 + +#define CT_KEY_GREEN 'g' +#define CT_KEY_YELLOW 'y' +#define CT_KEY_RED 'r' +#define CT_KEY_BLUE 'b' +#define CT_KEY_ALL 't' diff --git a/src/hexen/d_net.c b/src/hexen/d_net.c new file mode 100644 index 00000000..3a0aebeb --- /dev/null +++ b/src/hexen/d_net.c @@ -0,0 +1,927 @@ + +//************************************************************************** +//** +//** d_net.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: d_net.c,v $ +//** $Revision: 1.16 $ +//** $Date: 96/01/01 03:39:44 $ +//** $Author: bgokey $ +//** +//** This version has the fixed ticdup code. +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include // for atoi() + +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_KILL 0x10000000 // kill game +#define NCMD_CHECKSUM 0x0fffffff + + +doomcom_t *doomcom; +doomdata_t *netbuffer; // points inside doomcom + + +/* +============================================================================== + + NETWORKING + +gametic is the tic about to (or currently being) run +maketic is the tick that hasn't had control made for it yet +nettics[] has the maketics for all players + +a gametic cannot be run until nettics[] > gametic for all players + +============================================================================== +*/ + +#define RESENDCOUNT 10 +#define PL_DRONE 0x80 // bit flag in doomdata->player + +ticcmd_t localcmds[BACKUPTICS]; + +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +int nettics[MAXNETNODES]; +boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +boolean remoteresend[MAXNETNODES]; // set when local needs tics +int resendto[MAXNETNODES]; // set when remote needs tics +int resendcount[MAXNETNODES]; + +int nodeforplayer[MAXPLAYERS]; + +int maketic; +int lastnettic, skiptics; +int ticdup; +int maxsend; // BACKUPTICS/(2*ticdup)-1 + +void H2_ProcessEvents (void); +void G_BuildTiccmd (ticcmd_t *cmd); +void H2_DoAdvanceDemo (void); +extern void ST_NetProgress(void); +extern void ST_NetDone(void); + +boolean reboundpacket; +doomdata_t reboundstore; + + +int NetbufferSize (void) +{ + return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]); +} + +unsigned NetbufferChecksum (void) +{ + unsigned c; + int i,l; + + c = 0x1234567; + +#if defined(NeXT) || defined(NORMALUNIX) + return 0; // byte order problems +#endif + + l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; + for (i=0 ; iretransmitfrom)[i] * (i+1); + + return c & NCMD_CHECKSUM; +} + +int ExpandTics (int low) +{ + int delta; + + delta = low - (maketic&0xff); + + if (delta >= -64 && delta <= 64) + return (maketic&~0xff) + low; + if (delta > 64) + return (maketic&~0xff) - 256 + low; + if (delta < -64) + return (maketic&~0xff) + 256 + low; + + I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic); + return 0; +} + + +//============================================================================ + + +/* +============== += += HSendPacket += +============== +*/ + +void HSendPacket (int node, int flags) +{ + netbuffer->checksum = NetbufferChecksum () | flags; + + if (!node) + { + reboundstore = *netbuffer; + reboundpacket = true; + return; + } + + if (demoplayback) + return; + + if (!netgame) + I_Error ("Tried to transmit to another node"); + + doomcom->command = CMD_SEND; + doomcom->remotenode = node; + doomcom->datalength = NetbufferSize (); + +if (debugfile) +{ + int i; + int realretrans; + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics (netbuffer->retransmitfrom); + else + realretrans = -1; + fprintf (debugfile,"send (%i + %i, R %i) [%i] " + ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength); + for (i=0 ; idatalength ; i++) + fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); + fprintf (debugfile,"\n"); +} + + I_NetCmd (); +} + +//========================================================================== +// +// NET_SendFrags +// +//========================================================================== + +void NET_SendFrags(player_t *player) +{ + int i; + int frags; + + netbuffer->checksum = NetbufferChecksum(); + + if (demoplayback) + { + return; + } + if (!netgame) + { + I_Error ("Tried to transmit to another node"); + } + + frags = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + frags += player->frags[i]; + } + doomcom->command = CMD_FRAG; + doomcom->remotenode = frags; + doomcom->datalength = NetbufferSize (); + + I_NetCmd (); +} + +/* +============== += += HGetPacket += += Returns false if no packet is waiting += +============== +*/ + +boolean HGetPacket (void) +{ + if (reboundpacket) + { + *netbuffer = reboundstore; + doomcom->remotenode = 0; + reboundpacket = false; + return true; + } + + if (!netgame) + return false; + if (demoplayback) + return false; + + doomcom->command = CMD_GET; + I_NetCmd (); + if (doomcom->remotenode == -1) + return false; + + if (doomcom->datalength != NetbufferSize ()) + { + if (debugfile) + fprintf (debugfile,"bad packet length %i\n",doomcom->datalength); + return false; + } + + if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) ) + { + if (debugfile) + fprintf (debugfile,"bad packet checksum\n"); + return false; + } + +if (debugfile) +{ + int realretrans; + int i; + + if (netbuffer->checksum & NCMD_SETUP) + fprintf (debugfile,"setup packet\n"); + else + { + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics (netbuffer->retransmitfrom); + else + realretrans = -1; + fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode, + ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength); + for (i=0 ; idatalength ; i++) + fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); + fprintf (debugfile,"\n"); + } +} + return true; +} + + +/* +=================== += += GetPackets += +=================== +*/ + +char exitmsg[80]; + +void GetPackets (void) +{ + int netconsole; + int netnode; + ticcmd_t *src, *dest; + int realend; + int realstart; + + while (HGetPacket ()) + { + if (netbuffer->checksum & NCMD_SETUP) + continue; // extra setup packet + + netconsole = netbuffer->player & ~PL_DRONE; + netnode = doomcom->remotenode; + // + // to save bytes, only the low byte of tic numbers are sent + // Figure out what the rest of the bytes are + // + realstart = ExpandTics (netbuffer->starttic); + realend = (realstart+netbuffer->numtics); + + // + // check for exiting the game + // + if (netbuffer->checksum & NCMD_EXIT) + { + if (!nodeingame[netnode]) + continue; + nodeingame[netnode] = false; + playeringame[netconsole] = false; + strcpy (exitmsg, "PLAYER 1 LEFT THE GAME"); + exitmsg[7] += netconsole; + P_SetMessage(&players[consoleplayer], exitmsg, true); + S_StartSound(NULL, SFX_CHAT); +// players[consoleplayer].message = exitmsg; +// if (demorecording) +// G_CheckDemoStatus (); + continue; + } + + // + // check for a remote game kill + // + if (netbuffer->checksum & NCMD_KILL) + I_Error ("Killed by network driver"); + + nodeforplayer[netconsole] = netnode; + + // + // check for retransmit request + // + if ( resendcount[netnode] <= 0 + && (netbuffer->checksum & NCMD_RETRANSMIT) ) + { + resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); +if (debugfile) +fprintf (debugfile,"retransmit from %i\n", resendto[netnode]); + resendcount[netnode] = RESENDCOUNT; + } + else + resendcount[netnode]--; + + // + // check for out of order / duplicated packet + // + if (realend == nettics[netnode]) + continue; + + if (realend < nettics[netnode]) + { +if (debugfile) +fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics); + continue; + } + + // + // check for a missed packet + // + if (realstart > nettics[netnode]) + { + // stop processing until the other system resends the missed tics +if (debugfile) +fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]); + remoteresend[netnode] = true; + continue; + } + +// +// update command store from the packet +// +{ + int start; + + remoteresend[netnode] = false; + + start = nettics[netnode] - realstart; + src = &netbuffer->cmds[start]; + + while (nettics[netnode] < realend) + { + dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS]; + nettics[netnode]++; + *dest = *src; + src++; + } + } +} + +} + +/* +============= += += NetUpdate += += Builds ticcmds for console player += sends out a packet +============= +*/ + +int gametime; + +void NetUpdate (void) +{ + int nowtime; + int newtics; + int i,j; + int realstart; + int gameticdiv; + +// +// check time +// + nowtime = I_GetTime ()/ticdup; + newtics = nowtime - gametime; + gametime = nowtime; + + if (newtics <= 0) // nothing new to update + goto listen; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + + netbuffer->player = consoleplayer; + +// +// build new ticcmds for console player +// + gameticdiv = gametic/ticdup; + for (i=0 ; i= BACKUPTICS/2-1) + break; // can't hold any more +//printf ("mk:%i ",maketic); + G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]); + maketic++; + } + + + if (singletics) + return; // singletic update is syncronous + +// +// send the packet to the other nodes +// + for (i=0 ; inumnodes ; i++) + if (nodeingame[i]) + { + netbuffer->starttic = realstart = resendto[i]; + netbuffer->numtics = maketic - realstart; + if (netbuffer->numtics > BACKUPTICS) + I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS"); + + resendto[i] = maketic - doomcom->extratics; + + for (j=0 ; j< netbuffer->numtics ; j++) + netbuffer->cmds[j] = + localcmds[(realstart+j)%BACKUPTICS]; + + if (remoteresend[i]) + { + netbuffer->retransmitfrom = nettics[i]; + HSendPacket (i, NCMD_RETRANSMIT); + } + else + { + netbuffer->retransmitfrom = 0; + HSendPacket (i, 0); + } + } + +// +// listen for other packets +// +listen: + + GetPackets (); +} + + +/* +===================== += += CheckAbort += +===================== +*/ + +void CheckAbort (void) +{ + event_t *ev; + int stoptic; + + stoptic = I_GetTime () + 2; + while (I_GetTime() < stoptic) + I_StartTic (); + + I_StartTic (); + for ( ; eventtail != eventhead + ; eventtail = (++eventtail)&(MAXEVENTS-1) ) + { + ev = &events[eventtail]; + if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) + I_Error ("Network game synchronization aborted."); + } +} + +/* +===================== += += D_ArbitrateNetStart += +===================== +*/ + +void D_ArbitrateNetStart (void) +{ + int i; + boolean gotinfo[MAXNETNODES]; + boolean gotClass[MAXNETNODES]; +#ifdef __WATCOMC__ + int nextTic; + extern volatile int ticcount; + + nextTic = ticcount+8; +#endif + + autostart = true; + + memset (gotClass,0,sizeof(gotClass)); + memset (gotinfo,0,sizeof(gotinfo)); + gotClass[doomcom->consoleplayer] = true; + do + { + i = 0; + + CheckAbort(); + while(HGetPacket()) + { // Check for any incoming packets + if(netbuffer->checksum&NCMD_SETUP && netbuffer->starttic >= 64) + { + + PlayerClass[netbuffer->player] = netbuffer->starttic&0x3f; + if(!gotClass[netbuffer->player]) + { + gotClass[netbuffer->player] = true; + ST_NetProgress(); + ST_Message("\n"); + } + if(netbuffer->retransmitfrom) + { // that node has received info from all other nodes + gotinfo[netbuffer->player] = true; + } + } + } +#ifdef __WATCOMC__ + if(ticcount <= nextTic) + { // only send packets every half second + continue; + } + nextTic = ticcount+8; +#endif + // Keep sending out packets containing the console class + for(i = 0; i < doomcom->numnodes; i++) + { + netbuffer->player = doomcom->consoleplayer; + netbuffer->starttic = PlayerClass[doomcom->consoleplayer]+64; + netbuffer->retransmitfrom = gotinfo[doomcom->consoleplayer]; + netbuffer->numtics = 0; + HSendPacket(i, NCMD_SETUP); + } + for(i = 0; i < doomcom->numnodes; i++) + { // Make sure that all nodes have sent class info + if (!gotClass[i]) + { + ST_Message("."); + break; + } + } + if(i < doomcom->numnodes) + { + continue; + } + else + { // consoleplayer has received all player classes + if(gotinfo[doomcom->consoleplayer]) + { + CheckAbort(); + } + else + { + gotinfo[doomcom->consoleplayer] = true; + ST_Message("All player classes received, ready to proceed\n"); + ST_NetDone(); + } + } + for (i = 0; i < doomcom->numnodes; i++) + { // Make sure that all nodes are ready to proceed + if (!gotinfo[i]) + { + break; + } + } + } while(i < doomcom->numnodes); + + memset (gotinfo,0,sizeof(gotinfo)); + + if (doomcom->consoleplayer) + { // listen for setup info from key player +// ST_Message ("listening for network start info...\n"); + while (1) + { + CheckAbort (); + if (!HGetPacket ()) + continue; + if(netbuffer->checksum & NCMD_SETUP && netbuffer->starttic < 64) + { + if (netbuffer->player != VERSION) + I_Error ("Different HEXEN versions cannot play a net game!"); + startskill = netbuffer->retransmitfrom & 15; + deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6; + nomonsters = (netbuffer->retransmitfrom & 0x20) > 0; + respawnparm = (netbuffer->retransmitfrom & 0x10) > 0; + startmap = netbuffer->starttic & 0x3f; + startepisode = 1; + return; + } + } + } + else + { // key player, send the setup info +// ST_Message ("sending network start info...\n"); + do + { + CheckAbort (); + for (i=0 ; inumnodes ; i++) + { + netbuffer->retransmitfrom = startskill; + if (deathmatch) + netbuffer->retransmitfrom |= (deathmatch<<6); + if (nomonsters) + netbuffer->retransmitfrom |= 0x20; + if (respawnparm) + netbuffer->retransmitfrom |= 0x10; + netbuffer->starttic = startmap&0x3f; + netbuffer->player = VERSION; + netbuffer->numtics = 0; + HSendPacket (i, NCMD_SETUP); + } + +#if 1 + for(i = 10 ; i && HGetPacket(); --i) + { + if((netbuffer->player&0x7f) < MAXNETNODES) + gotinfo[netbuffer->player&0x7f] = true; + } +#else + while (HGetPacket ()) + { + gotinfo[netbuffer->player&0x7f] = true; + } +#endif + + for (i=1 ; inumnodes ; i++) + if (!gotinfo[i]) + break; + } while (i < doomcom->numnodes); + } +} + +/* +=================== += += D_CheckNetGame += += Works out player numbers among the net participants +=================== +*/ + +extern int viewangleoffset; + +void D_CheckNetGame (void) +{ + int i; + int pClass; + + for (i=0 ; iid != DOOMCOM_ID) + I_Error ("Doomcom buffer invalid!"); + netbuffer = &doomcom->data; + consoleplayer = displayplayer = doomcom->consoleplayer; + pClass = PCLASS_FIGHTER; + if(i = M_CheckParm("-class")) + { + pClass = atoi(myargv[i+1]); + if(pClass > PCLASS_MAGE || pClass < PCLASS_FIGHTER) + { + I_Error("Invalid player class: %d\n", pClass); + } + ST_Message("\nPlayer Class: %d\n", pClass); + } + PlayerClass[consoleplayer] = pClass; + if (netgame) + D_ArbitrateNetStart (); +//ST_Message ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); + +// read values out of doomcom + ticdup = doomcom->ticdup; + maxsend = BACKUPTICS/(2*ticdup)-1; + if (maxsend<1) + maxsend = 1; + + for (i=0 ; inumplayers ; i++) + playeringame[i] = true; + for (i=0 ; inumnodes ; i++) + nodeingame[i] = true; + +//ST_Message ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes); + +} + +/* +================== += += D_QuitNetGame += += Called before quitting to leave a net game without hanging the += other players += +================== +*/ + +void D_QuitNetGame (void) +{ + int i, j; + + if (debugfile) + fclose (debugfile); + + if (!netgame || !usergame || consoleplayer == -1 || demoplayback) + return; + +// send a bunch of packets for security + netbuffer->player = consoleplayer; + netbuffer->numtics = 0; + for (i=0 ; i<4 ; i++) + { + for (j=1 ; jnumnodes ; j++) + if (nodeingame[j]) + HSendPacket (j, NCMD_EXIT); + I_WaitVBL (1); + } +} + + + +/* +=============== += += TryRunTics += +=============== +*/ + +int frametics[4], frameon; +int frameskip[4]; +int oldnettics; +extern boolean advancedemo; + +void TryRunTics (void) +{ + int i; + int lowtic; + int entertic; + static int oldentertics; + int realtics, availabletics; + int counts; + int numplaying; + +// +// get real tics +// + entertic = I_GetTime ()/ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + +// +// get available tics +// + NetUpdate (); + + lowtic = MAXINT; + numplaying = 0; + for (i=0 ; inumnodes ; i++) + if (nodeingame[i]) + { + numplaying++; + if (nettics[i] < lowtic) + lowtic = nettics[i]; + } + availabletics = lowtic - gametic/ticdup; + + +// +// decide how many tics to run +// + if (realtics < availabletics-1) + counts = realtics+1; + else if (realtics < availabletics) + counts = realtics; + else + counts = availabletics; + if (counts < 1) + counts = 1; + + frameon++; + +if (debugfile) + fprintf (debugfile,"=======real: %i avail: %i game: %i\n",realtics, availabletics,counts); + + if (!demoplayback) + { + //============================================================================= + // + // ideally nettics[0] should be 1 - 3 tics above lowtic + // if we are consistantly slower, speed up time + // + for (i=0 ; i nettics[nodeforplayer[i]]); + oldnettics = nettics[0]; + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; + // printf ("+"); + } + } + //============================================================================= + } // demoplayback + + // + // wait for new tics if needed + // + while (lowtic < gametic/ticdup + counts) + { + + NetUpdate (); + lowtic = MAXINT; + + for (i=0 ; inumnodes ; i++) + if (nodeingame[i] && nettics[i] < lowtic) + lowtic = nettics[i]; + + if (lowtic < gametic/ticdup) + I_Error ("TryRunTics: lowtic < gametic"); + + // don't stay in here forever -- give the menu a chance to work + if (I_GetTime ()/ticdup - entertic >= 20) + { + MN_Ticker (); + return; + } + } + +// +// run the count * ticdup dics +// + while (counts--) + { + for (i=0 ; i lowtic) + I_Error ("gametic>lowtic"); + if (advancedemo) + H2_DoAdvanceDemo (); + MN_Ticker (); + G_Ticker (); + gametic++; + // + // modify command for duplicated tics + // + if (i != ticdup-1) + { + ticcmd_t *cmd; + int buf; + int j; + + buf = (gametic/ticdup)%BACKUPTICS; + for (j=0 ; jchatchar = 0; + if (cmd->buttons & BT_SPECIAL) + cmd->buttons = 0; + } + } + } + NetUpdate (); // check for new console commands + } +} diff --git a/src/hexen/defs.inc b/src/hexen/defs.inc new file mode 100644 index 00000000..eb83dc27 --- /dev/null +++ b/src/hexen/defs.inc @@ -0,0 +1,52 @@ +SKIPPRIMITIVES = 0 ; set to 1 to skip unwound drawing + + +SCREEN = 0a0000h +SCREENWIDTH = 320 +SCREENHEIGHT = 200 +PLANEWIDTH = 80 +PLANESIZE = 80*200 + +PEL_WRITE_ADR = 03c8h +PEL_DATA = 03c9h + +SC_INDEX = 03C4h +SC_MAPMASK = 2 + +OP_RET = 0c3h +OP_MOVAL = 08ah +OP_MOVDEST = 088h + + + .DATA + +EXTRN _dc_colormap:DWORD +EXTRN _tinttable:DWORD +EXTRN _dc_x:DWORD +EXTRN _dc_yl:DWORD +EXTRN _dc_yh:DWORD +EXTRN _dc_iscale:DWORD +EXTRN _dc_texturemid:DWORD +EXTRN _dc_source:DWORD + +EXTRN _ylookup:DWORD +EXTRN _columnofs:DWORD + + +EXTRN _ds_y:DWORD +EXTRN _ds_x1:DWORD +EXTRN _ds_x2:DWORD +EXTRN _ds_colormap:DWORD +EXTRN _ds_xfrac:DWORD +EXTRN _ds_yfrac:DWORD +EXTRN _ds_xstep:DWORD +EXTRN _ds_ystep:DWORD +EXTRN _ds_source:DWORD + +PUSHR MACRO + pushad +ENDM + +POPR MACRO + popad +ENDM diff --git a/src/hexen/drcoord.h b/src/hexen/drcoord.h new file mode 100644 index 00000000..7ef9a22a --- /dev/null +++ b/src/hexen/drcoord.h @@ -0,0 +1,29 @@ + +//************************************************************************** +//** +//** DRCoord.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: DRCoord.h,v $ +//** $Revision: 1.1 $ +//** $Date: 95/05/11 00:19:30 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#import + +@interface DRCoord:Object +{ + id players_i; + id console_i; + id skill_i; + id episode_i; + id map_i; +} + +- newGame: sender; +- scale1: sender; +- scale2: sender; +- scale4: sender; + +@end diff --git a/src/hexen/dstrings.h b/src/hexen/dstrings.h new file mode 100644 index 00000000..982867b0 --- /dev/null +++ b/src/hexen/dstrings.h @@ -0,0 +1,205 @@ + +//************************************************************************** +//** +//** DStrings.H +//** +//************************************************************************** + +// MN_menu.c --------------------------------------------------------------- + +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define TXT_PAUSED "PAUSED" +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +#define QLPROMPT "do you want to quickload the game named"\ + "\n\n'%s'?\n\n"PRESSYN +#define NEWGAME "you can't start a new game\n"\ + "while in a network game.\n\n"PRESSKEY +#define NIGHTMARE "are you sure? this skill level\n"\ + "isn't even remotely fair.\n\n"PRESSYN +#define SWSTRING "this is the shareware version of doom.\n\n"\ + "you need to order the entire trilogy.\n\n"PRESSKEY +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define NETEND "you can't end a netgame!\n\n"PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +#define DOSY "(press y to quit to dos.)" +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +// P_inter.c --------------------------------------------------------------- + +// Keys + +#define TXT_GOTBLUEKEY "BLUE KEY" +#define TXT_GOTYELLOWKEY "YELLOW KEY" +#define TXT_GOTGREENKEY "GREEN KEY" + +// Artifacts + +#define TXT_ARTIHEALTH "QUARTZ FLASK" +#define TXT_ARTIFLY "WINGS OF WRATH" +#define TXT_ARTIINVULNERABILITY "RING OF INVINCIBILITY" +#define TXT_ARTITOMEOFPOWER "TOME OF POWER" +#define TXT_ARTIINVISIBILITY "SHADOWSPHERE" +#define TXT_ARTIEGG "MORPH OVUM" +#define TXT_ARTISUPERHEALTH "MYSTIC URN" +#define TXT_ARTITORCH "TORCH" +#define TXT_ARTIFIREBOMB "TIME BOMB OF THE ANCIENTS" +#define TXT_ARTITELEPORT "CHAOS DEVICE" + +// Items + +#define TXT_ITEMHEALTH "CRYSTAL VIAL" +#define TXT_ITEMBAGOFHOLDING "BAG OF HOLDING" +#define TXT_ITEMSHIELD1 "SILVER SHIELD" +#define TXT_ITEMSHIELD2 "ENCHANTED SHIELD" +#define TXT_ITEMSUPERMAP "MAP SCROLL" + +// Ammo + +#define TXT_AMMOGOLDWAND1 "WAND CRYSTAL" +#define TXT_AMMOGOLDWAND2 "CRYSTAL GEODE" +#define TXT_AMMOMACE1 "MACE SPHERES" +#define TXT_AMMOMACE2 "PILE OF MACE SPHERES" +#define TXT_AMMOCROSSBOW1 "ETHEREAL ARROWS" +#define TXT_AMMOCROSSBOW2 "QUIVER OF ETHEREAL ARROWS" +#define TXT_AMMOBLASTER1 "CLAW ORB" +#define TXT_AMMOBLASTER2 "ENERGY ORB" +#define TXT_AMMOSKULLROD1 "LESSER RUNES" +#define TXT_AMMOSKULLROD2 "GREATER RUNES" +#define TXT_AMMOPHOENIXROD1 "FLAME ORB" +#define TXT_AMMOPHOENIXROD2 "INFERNO ORB" + +// Weapons + +#define TXT_WPNMACE "FIREMACE" +#define TXT_WPNCROSSBOW "ETHEREAL CROSSBOW" +#define TXT_WPNBLASTER "DRAGON CLAW" +#define TXT_WPNSKULLROD "HELLSTAFF" +#define TXT_WPNPHOENIXROD "PHOENIX ROD" +#define TXT_WPNGAUNTLETS "GAUNTLETS OF THE NECROMANCER" + +// SB_bar.c ---------------------------------------------------------------- + +#define TXT_CHEATGODON "GOD MODE ON" +#define TXT_CHEATGODOFF "GOD MODE OFF" +#define TXT_CHEATNOCLIPON "NO CLIPPING ON" +#define TXT_CHEATNOCLIPOFF "NO CLIPPING OFF" +#define TXT_CHEATWEAPONS "ALL WEAPONS" +#define TXT_CHEATFLIGHTON "FLIGHT ON" +#define TXT_CHEATFLIGHTOFF "FLIGHT OFF" +#define TXT_CHEATPOWERON "POWER ON" +#define TXT_CHEATPOWEROFF "POWER OFF" +#define TXT_CHEATHEALTH "FULL HEALTH" +#define TXT_CHEATKEYS "ALL KEYS" +#define TXT_CHEATSOUNDON "SOUND DEBUG ON" +#define TXT_CHEATSOUNDOFF "SOUND DEBUG OFF" +#define TXT_CHEATTICKERON "TICKER ON" +#define TXT_CHEATTICKEROFF "TICKER OFF" +#define TXT_CHEATARTIFACTS1 "CHOOSE AN ARTIFACT ( A - J )" +#define TXT_CHEATARTIFACTS2 "HOW MANY ( 1 - 9 )" +#define TXT_CHEATARTIFACTS3 "YOU GOT IT" +#define TXT_CHEATARTIFACTSFAIL "BAD INPUT" +#define TXT_CHEATWARP "LEVEL WARP" +#define TXT_CHEATSCREENSHOT "SCREENSHOT" +#define TXT_CHEATCHICKENON "CHICKEN ON" +#define TXT_CHEATCHICKENOFF "CHICKEN OFF" +#define TXT_CHEATMASSACRE "MASSACRE" +#define TXT_CHEATIDDQD "TRYING TO CHEAT, EH? NOW YOU DIE!" +#define TXT_CHEATIDKFA "CHEATER - YOU DON'T DESERVE WEAPONS" + +// P_doors.c --------------------------------------------------------------- + +#define TXT_NEEDBLUEKEY "YOU NEED A BLUE KEY TO OPEN THIS DOOR" +#define TXT_NEEDGREENKEY "YOU NEED A GREEN KEY TO OPEN THIS DOOR" +#define TXT_NEEDYELLOWKEY "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" + +// G_game.c ---------------------------------------------------------------- + +#define TXT_GAMESAVED "GAME SAVED" + +// M_misc.c ---------------------------------------------------------------- + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +// AM_map.c ---------------------------------------------------------------- + +#define AMSTR_FOLLOWON "FOLLOW MODE ON" +#define AMSTR_FOLLOWOFF "FOLLOW MODE OFF" + +// F_finale.c -------------------------------------------------------------- + +#define E1TEXT "with the destruction of the iron\n"\ + "liches and their minions, the last\n"\ + "of the undead are cleared from this\n"\ + "plane of existence.\n\n"\ + "those creatures had to come from\n"\ + "somewhere, though, and you have the\n"\ + "sneaky suspicion that the fiery\n"\ + "portal of hell's maw opens onto\n"\ + "their home dimension.\n\n"\ + "to make sure that more undead\n"\ + "(or even worse things) don't come\n"\ + "through, you'll have to seal hell's\n"\ + "maw from the other side. of course\n"\ + "this means you may get stuck in a\n"\ + "very unfriendly world, but no one\n"\ + "ever said being a Heretic was easy!" + +#define E2TEXT "the mighty maulotaurs have proved\n"\ + "to be no match for you, and as\n"\ + "their steaming corpses slide to the\n"\ + "ground you feel a sense of grim\n"\ + "satisfaction that they have been\n"\ + "destroyed.\n\n"\ + "the gateways which they guarded\n"\ + "have opened, revealing what you\n"\ + "hope is the way home. but as you\n"\ + "step through, mocking laughter\n"\ + "rings in your ears.\n\n"\ + "was some other force controlling\n"\ + "the maulotaurs? could there be even\n"\ + "more horrific beings through this\n"\ + "gate? the sweep of a crystal dome\n"\ + "overhead where the sky should be is\n"\ + "certainly not a good sign...." + +#define E3TEXT "the death of d'sparil has loosed\n"\ + "the magical bonds holding his\n"\ + "creatures on this plane, their\n"\ + "dying screams overwhelming his own\n"\ + "cries of agony.\n\n"\ + "your oath of vengeance fulfilled,\n"\ + "you enter the portal to your own\n"\ + "world, mere moments before the dome\n"\ + "shatters into a million pieces.\n\n"\ + "but if d'sparil's power is broken\n"\ + "forever, why don't you feel safe?\n"\ + "was it that last shout just before\n"\ + "his death, the one that sounded\n"\ + "like a curse? or a summoning? you\n"\ + "can't really be sure, but it might\n"\ + "just have been a scream.\n\n"\ + "then again, what about the other\n"\ + "serpent riders?" diff --git a/src/hexen/f_finale.c b/src/hexen/f_finale.c new file mode 100644 index 00000000..53820868 --- /dev/null +++ b/src/hexen/f_finale.c @@ -0,0 +1,376 @@ + +//************************************************************************** +//** +//** f_finale.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: f_finale.c,v $ +//** $Revision: 1.7 $ +//** $Date: 96/01/05 23:33:26 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "soundst.h" +#include "p_local.h" +#include + +// MACROS ------------------------------------------------------------------ + +#define TEXTSPEED 3 +#define TEXTWAIT 250 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void TextWrite(void); +static void DrawPic(void); +static void InitializeFade(boolean fadeIn); +static void DeInitializeFade(void); +static void FadePic(void); +static char *GetFinaleText(int sequence); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern boolean automapactive; +extern boolean viewactive; + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int FinaleStage; +static int FinaleCount; +static int FinaleEndCount; +static int FinaleLumpNum; +static int FontABaseLump; +static char *FinaleText; + +static fixed_t *Palette; +static fixed_t *PaletteDelta; +static byte *RealPalette; + +// CODE -------------------------------------------------------------------- + +//=========================================================================== +// +// F_StartFinale +// +//=========================================================================== + +void F_StartFinale (void) +{ + gameaction = ga_nothing; + gamestate = GS_FINALE; + viewactive = false; + automapactive = false; + P_ClearMessage(&players[consoleplayer]); + + FinaleStage = 0; + FinaleCount = 0; + FinaleText = GetFinaleText(0); + FinaleEndCount = 70; + FinaleLumpNum = W_GetNumForName("FINALE1"); + FontABaseLump = W_GetNumForName("FONTA_S")+1; + InitializeFade(1); + +// S_ChangeMusic(mus_victor, true); + S_StartSongName("hall", false); // don't loop the song +} + +//=========================================================================== +// +// F_Responder +// +//=========================================================================== + +boolean F_Responder(event_t *event) +{ + return false; +} + +//=========================================================================== +// +// F_Ticker +// +//=========================================================================== + +void F_Ticker (void) +{ + FinaleCount++; + if(FinaleStage < 5 && FinaleCount >= FinaleEndCount) + { + FinaleCount = 0; + FinaleStage++; + switch(FinaleStage) + { + case 1: // Text 1 + FinaleEndCount = strlen(FinaleText)*TEXTSPEED+TEXTWAIT; + break; + case 2: // Pic 2, Text 2 + FinaleText = GetFinaleText(1); + FinaleEndCount = strlen(FinaleText)*TEXTSPEED+TEXTWAIT; + FinaleLumpNum = W_GetNumForName("FINALE2"); + S_StartSongName("orb", false); + break; + case 3: // Pic 2 -- Fade out + FinaleEndCount = 70; + DeInitializeFade(); + InitializeFade(0); + break; + case 4: // Pic 3 -- Fade in + FinaleLumpNum = W_GetNumForName("FINALE3"); + FinaleEndCount = 71; + DeInitializeFade(); + InitializeFade(1); + S_StartSongName("chess", true); + break; + case 5: // Pic 3 , Text 3 + FinaleText = GetFinaleText(2); + DeInitializeFade(); + break; + default: + break; + } + return; + } + if(FinaleStage == 0 || FinaleStage == 3 || FinaleStage == 4) + { + FadePic(); + } +} + +//=========================================================================== +// +// TextWrite +// +//=========================================================================== + +static void TextWrite (void) +{ + int count; + char *ch; + int c; + int cx, cy; + patch_t *w; + + memcpy(screen, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), + SCREENWIDTH*SCREENHEIGHT); + if(FinaleStage == 5) + { // Chess pic, draw the correct character graphic + if(netgame) + { + V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE)); + } + else if(PlayerClass[consoleplayer]) + { + V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc") + +PlayerClass[consoleplayer]-1, PU_CACHE)); + } + } + // Draw the actual text + if(FinaleStage == 5) + { + cy = 135; + } + else + { + cy = 5; + } + cx = 20; + ch = FinaleText; + count = (FinaleCount-10)/TEXTSPEED; + if (count < 0) + { + count = 0; + } + for(; count; count--) + { + c = *ch++; + if(!c) + { + break; + } + if(c == '\n') + { + cx = 20; + cy += 9; + continue; + } + if(c < 32) + { + continue; + } + c = toupper(c); + if(c == 32) + { + cx += 5; + continue; + } + w = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + if(cx+w->width > SCREENWIDTH) + { + break; + } + V_DrawPatch(cx, cy, w); + cx += w->width; + } +} + +//=========================================================================== +// +// InitializeFade +// +//=========================================================================== + +static void InitializeFade(boolean fadeIn) +{ + unsigned i; + + Palette = Z_Malloc(768*sizeof(fixed_t), PU_STATIC, 0); + PaletteDelta = Z_Malloc(768*sizeof(fixed_t), PU_STATIC, 0); + RealPalette = Z_Malloc(768*sizeof(byte), PU_STATIC, 0); + + if(fadeIn) + { + memset(RealPalette, 0, 768*sizeof(byte)); + for(i = 0; i < 768; i++) + { + Palette[i] = 0; + PaletteDelta[i] = FixedDiv((*((byte *)W_CacheLumpName("playpal", + PU_CACHE)+i))<>FRACBITS; + } + I_SetPalette(RealPalette); +} + +//=========================================================================== +// +// DrawPic +// +//=========================================================================== + +static void DrawPic(void) +{ + memcpy(screen, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), + SCREENWIDTH*SCREENHEIGHT); + if(FinaleStage == 4 || FinaleStage == 5) + { // Chess pic, draw the correct character graphic + if(netgame) + { + V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE)); + } + else if(PlayerClass[consoleplayer]) + { + V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc") + +PlayerClass[consoleplayer]-1, PU_CACHE)); + } + } +} + +//=========================================================================== +// +// F_Drawer +// +//=========================================================================== + +void F_Drawer(void) +{ + switch(FinaleStage) + { + case 0: // Fade in initial finale screen + DrawPic(); + break; + case 1: + case 2: + TextWrite(); + break; + case 3: // Fade screen out + DrawPic(); + break; + case 4: // Fade in chess screen + DrawPic(); + break; + case 5: + TextWrite(); + break; + } + UpdateState |= I_FULLSCRN; +} + +//========================================================================== +// +// GetFinaleText +// +//========================================================================== + +static char *GetFinaleText(int sequence) +{ + char *msgLumpName; + int msgSize; + int msgLump; + static char *winMsgLumpNames[] = + { + "win1msg", + "win2msg", + "win3msg" + }; + + msgLumpName = winMsgLumpNames[sequence]; + msgLump = W_GetNumForName(msgLumpName); + msgSize = W_LumpLength(msgLump); + if(msgSize >= MAX_INTRMSN_MESSAGE_SIZE) + { + I_Error("Finale message too long (%s)", msgLumpName); + } + W_ReadLump(msgLump, ClusterMessage); + ClusterMessage[msgSize] = 0; // Append terminator + return ClusterMessage; +} diff --git a/src/hexen/g_game.c b/src/hexen/g_game.c new file mode 100644 index 00000000..f22138bd --- /dev/null +++ b/src/hexen/g_game.c @@ -0,0 +1,2014 @@ + +//************************************************************************** +//** +//** g_game.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: g_game.c,v $ +//** $Revision: 1.68 $ +//** $Date: 96/01/12 13:18:07 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +#define AM_STARTKEY 9 + +// External functions + +extern void R_InitSky(int map); +extern void P_PlayerNextArtifact(player_t *player); + +// Functions + +boolean G_CheckDemoStatus (void); +void G_ReadDemoTiccmd (ticcmd_t *cmd); +void G_WriteDemoTiccmd (ticcmd_t *cmd); +void G_InitNew (skill_t skill, int episode, int map); + +void G_DoReborn (int playernum); + +void G_DoLoadLevel(void); +void G_DoInitNew(void); +void G_DoNewGame(void); +void G_DoLoadGame(void); +void G_DoPlayDemo(void); +void G_DoTeleportNewMap(void); +void G_DoCompleted(void); +void G_DoVictory(void); +void G_DoWorldDone(void); +void G_DoSaveGame(void); +void G_DoSingleReborn(void); + +void H2_PageTicker(void); +void H2_AdvanceDemo(void); + +extern boolean mn_SuicideConsole; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +//boolean respawnmonsters; +int gameepisode; +int gamemap; +int prevmap; + +boolean paused; +boolean sendpause; // send a pause event next tic +boolean sendsave; // send a save event next tic +boolean usergame; // ok to save / end game + +boolean timingdemo; // if true, exit with report on completion +int starttime; // for comparative timing purposes + +boolean viewactive; + +boolean deathmatch; // only if started as net death +boolean netgame; // only true if packets are broadcast +boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; +pclass_t PlayerClass[MAXPLAYERS]; + +// Position indicator for cooperative net-play reborn +int RebornPosition; + +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int levelstarttic; // gametic at level start + +char demoname[32]; +boolean demorecording; +boolean demoplayback; +byte *demobuffer, *demo_p; +boolean singledemo; // quit after playing a demo from cmdline + +boolean precache = true; // if true, load all graphics at start + +short consistancy[MAXPLAYERS][BACKUPTICS]; + +// +// controls (have defaults) +// +int key_right, key_left, key_up, key_down; +int key_strafeleft, key_straferight, key_jump; +int key_fire, key_use, key_strafe, key_speed; +int key_flyup, key_flydown, key_flycenter; +int key_lookup, key_lookdown, key_lookcenter; +int key_invleft, key_invright, key_useartifact; + +int mousebfire; +int mousebstrafe; +int mousebforward; +int mousebjump; + +int joybfire; +int joybstrafe; +int joybuse; +int joybspeed; +int joybjump; + +int LeaveMap; +static int LeavePosition; + +//#define MAXPLMOVE 0x32 // Old Heretic Max move + +fixed_t MaxPlayerMove[NUMCLASSES] = { 0x3C, 0x32, 0x2D, 0x31 }; +fixed_t forwardmove[NUMCLASSES][2] = +{ + { 0x1D, 0x3C }, + { 0x19, 0x32 }, + { 0x16, 0x2E }, + { 0x18, 0x31 } +}; + +fixed_t sidemove[NUMCLASSES][2] = +{ + { 0x1B, 0x3B }, + { 0x18, 0x28 }, + { 0x15, 0x25 }, + { 0x17, 0x27 } +}; + +fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn +#define SLOWTURNTICS 6 + +#define NUMKEYS 256 +boolean gamekeydown[NUMKEYS]; +int turnheld; // for accelerative turning +int lookheld; + + +boolean mousearray[4]; +boolean *mousebuttons = &mousearray[1]; + // allow [-1] +int mousex, mousey; // mouse values are used once +int dclicktime, dclickstate, dclicks; +int dclicktime2, dclickstate2, dclicks2; + +int joyxmove, joyymove; // joystick values are repeated +boolean joyarray[5]; +boolean *joybuttons = &joyarray[1]; // allow [-1] + +int savegameslot; +char savedescription[32]; + +int inventoryTics; + +#ifdef __WATCOMC__ +extern externdata_t *i_ExternData; +#endif + +static skill_t TempSkill; +static int TempEpisode; +static int TempMap; + +//============================================================================= +/* +==================== += += G_BuildTiccmd += += Builds a ticcmd from all of the available inputs or reads it from the += demo buffer. += If recording a demo, write it out +==================== +*/ + +extern boolean inventory; +extern int curpos; +extern int inv_ptr; + +extern int isCyberPresent; // is CyberMan present? +boolean usearti = true; +void I_ReadCyberCmd (ticcmd_t *cmd); + +void G_BuildTiccmd (ticcmd_t *cmd) +{ + int i; + boolean strafe, bstrafe; + int speed, tspeed, lspeed; + int forward, side; + int look, arti; + int flyheight; + int pClass; + + extern boolean artiskip; + +#ifdef __WATCOMC__ + int angleDelta; + static int oldAngle; + extern int newViewAngleOff; + static int externInvKey; + extern boolean automapactive; + event_t ev; +#endif + + pClass = players[consoleplayer].class; + memset (cmd,0,sizeof(*cmd)); + +// cmd->consistancy = +// consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS]; + + cmd->consistancy = + consistancy[consoleplayer][maketic%BACKUPTICS]; + if (isCyberPresent) + I_ReadCyberCmd (cmd); + +//printf ("cons: %i\n",cmd->consistancy); + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + speed = gamekeydown[key_speed] || joybuttons[joybspeed] + || joybuttons[joybspeed]; +#ifdef __WATCOMC__ + if(useexterndriver) + { + speed |= (i_ExternData->buttons&EBT_SPEED); + strafe |= (i_ExternData->buttons&EBT_STRAFE); + } +#endif + + forward = side = look = arti = flyheight = 0; + +// +// use two stage accelerative turning on the keyboard and joystick +// + if (joyxmove < 0 || joyxmove > 0 + || gamekeydown[key_right] || gamekeydown[key_left]) + turnheld += ticdup; + else + turnheld = 0; + if (turnheld < SLOWTURNTICS) + tspeed = 2; // slow turn + else + tspeed = speed; + + if(gamekeydown[key_lookdown] || gamekeydown[key_lookup]) + { + lookheld += ticdup; + } + else + { + lookheld = 0; + } + if(lookheld < SLOWTURNTICS) + { + lspeed = 1; // 3; + } + else + { + lspeed = 2; // 5; + } + +// +// let movement keys cancel each other out +// + if(strafe) + { + if (gamekeydown[key_right]) + { + side += sidemove[pClass][speed]; + } + if (gamekeydown[key_left]) + { + side -= sidemove[pClass][speed]; + } + if (joyxmove > 0) + { + side += sidemove[pClass][speed]; + } + if (joyxmove < 0) + { + side -= sidemove[pClass][speed]; + } + } + else + { + if (gamekeydown[key_right]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left]) + cmd->angleturn += angleturn[tspeed]; + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + { + forward += forwardmove[pClass][speed]; + } + if (gamekeydown[key_down]) + { + forward -= forwardmove[pClass][speed]; + } + if (joyymove < 0) + { + forward += forwardmove[pClass][speed]; + } + if (joyymove > 0) + { + forward -= forwardmove[pClass][speed]; + } + if (gamekeydown[key_straferight]) + { + side += sidemove[pClass][speed]; + } + if (gamekeydown[key_strafeleft]) + { + side -= sidemove[pClass][speed]; + } + + // Look up/down/center keys + if(gamekeydown[key_lookup]) + { + look = lspeed; + } + if(gamekeydown[key_lookdown]) + { + look = -lspeed; + } +#ifdef __WATCOMC__ + if(gamekeydown[key_lookcenter] && !useexterndriver) + { + look = TOCENTER; + } +#else + if(gamekeydown[key_lookcenter]) + { + look = TOCENTER; + } +#endif + +#ifdef __WATCOMC__ + if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL || + gamestate == GS_INTERMISSION)) + { + if(i_ExternData->moveForward) + { + forward += i_ExternData->moveForward; + if(speed) + { + forward <<= 1; + } + } + if(i_ExternData->angleTurn) + { + if(strafe) + { + side += i_ExternData->angleTurn; + } + else + { + cmd->angleturn += i_ExternData->angleTurn; + } + } + if(i_ExternData->moveSideways) + { + side += i_ExternData->moveSideways; + if(speed) + { + side <<= 1; + } + } + if(i_ExternData->buttons&EBT_CENTERVIEW) + { + look = TOCENTER; + oldAngle = 0; + } + else if(i_ExternData->pitch) + { + angleDelta = i_ExternData->pitch-oldAngle; + if(abs(angleDelta) < 35) + { + look = angleDelta/5; + } + else + { + look = 7*(angleDelta > 0 ? 1 : -1); + } + if(look == TOCENTER) + { + look++; + } + oldAngle += look*5; + } + if(i_ExternData->flyDirection) + { + if(i_ExternData->flyDirection > 0) + { + flyheight = 5; + } + else + { + flyheight = -5; + } + } + if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000) + { + newViewAngleOff = i_ExternData->angleHead; + } + if(i_ExternData->buttons&EBT_FIRE) + { + cmd->buttons |= BT_ATTACK; + } + if(i_ExternData->buttons&EBT_OPENDOOR) + { + cmd->buttons |= BT_USE; + } + if(i_ExternData->buttons&EBT_PAUSE) + { + cmd->buttons = BT_SPECIAL|BTS_PAUSE; + i_ExternData->buttons &= ~EBT_PAUSE; + } + if(externInvKey&EBT_USEARTIFACT) + { + ev.type = ev_keyup; + ev.data1 = key_useartifact; + H2_PostEvent(&ev); + externInvKey &= ~EBT_USEARTIFACT; + } + else if(i_ExternData->buttons&EBT_USEARTIFACT) + { + externInvKey |= EBT_USEARTIFACT; + ev.type = ev_keydown; + ev.data1 = key_useartifact; + H2_PostEvent(&ev); + } + if(externInvKey&EBT_INVENTORYRIGHT) + { + ev.type = ev_keyup; + ev.data1 = key_invright; + H2_PostEvent(&ev); + externInvKey &= ~EBT_INVENTORYRIGHT; + } + else if(i_ExternData->buttons&EBT_INVENTORYRIGHT) + { + externInvKey |= EBT_INVENTORYRIGHT; + ev.type = ev_keydown; + ev.data1 = key_invright; + H2_PostEvent(&ev); + } + if(externInvKey&EBT_INVENTORYLEFT) + { + ev.type = ev_keyup; + ev.data1 = key_invleft; + H2_PostEvent(&ev); + externInvKey &= ~EBT_INVENTORYLEFT; + } + else if(i_ExternData->buttons&EBT_INVENTORYLEFT) + { + externInvKey |= EBT_INVENTORYLEFT; + ev.type = ev_keydown; + ev.data1 = key_invleft; + H2_PostEvent(&ev); + } + if(i_ExternData->buttons&EBT_FLYDROP) + { + flyheight = TOCENTER; + } + if(gamestate == GS_LEVEL) + { + if(externInvKey&EBT_MAP) + { // automap + ev.type = ev_keyup; + ev.data1 = AM_STARTKEY; + H2_PostEvent(&ev); + externInvKey &= ~EBT_MAP; + } + else if(i_ExternData->buttons&EBT_MAP) + { + externInvKey |= EBT_MAP; + ev.type = ev_keydown; + ev.data1 = AM_STARTKEY; + H2_PostEvent(&ev); + } + } + if(i_ExternData->buttons&EBT_WEAPONCYCLE) + { + int curWeapon; + player_t *pl; + + pl = &players[consoleplayer]; + curWeapon = pl->readyweapon; + for(curWeapon = (curWeapon+1)&3; curWeapon != pl->readyweapon; + curWeapon = (curWeapon+1)&3) + { + if(pl->weaponowned[curWeapon]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= curWeapon<buttons&EBT_JUMP) + { + cmd->arti |= AFLAG_JUMP; + } + } +#endif + + // Fly up/down/drop keys + if(gamekeydown[key_flyup]) + { + flyheight = 5; // note that the actual flyheight will be twice this + } + if(gamekeydown[key_flydown]) + { + flyheight = -5; + } + if(gamekeydown[key_flycenter]) + { + flyheight = TOCENTER; +#ifdef __WATCOMC__ + if(!useexterndriver) + { + look = TOCENTER; + } +#else + look = TOCENTER; +#endif + } + // Use artifact key + if(gamekeydown[key_useartifact]) + { + if(gamekeydown[key_speed] && artiskip) + { + if(players[consoleplayer].inventory[inv_ptr].type != arti_none) + { // Skip an artifact + gamekeydown[key_useartifact] = false; + P_PlayerNextArtifact(&players[consoleplayer]); + } + } + else + { + if(inventory) + { + players[consoleplayer].readyArtifact = + players[consoleplayer].inventory[inv_ptr].type; + inventory = false; + cmd->arti = 0; + usearti = false; + } + else if(usearti) + { + cmd->arti |= + players[consoleplayer].inventory[inv_ptr].type&AFLAG_MASK; + usearti = false; + } + } + } + if(gamekeydown[key_jump] || mousebuttons[mousebjump] + || joybuttons[joybjump]) + { + cmd->arti |= AFLAG_JUMP; + } + if(mn_SuicideConsole) + { + cmd->arti |= AFLAG_SUICIDE; + mn_SuicideConsole = false; + } + + // Artifact hot keys + if(gamekeydown[KEY_BACKSPACE] && !cmd->arti) + { + gamekeydown[KEY_BACKSPACE] = false; // Use one of each artifact + cmd->arti = NUMARTIFACTS; + } + else if(gamekeydown[KEY_BACKSLASH] && !cmd->arti + && (players[consoleplayer].mo->health < MAXHEALTH)) + { + gamekeydown[KEY_BACKSLASH] = false; + cmd->arti = arti_health; + } + else if(gamekeydown[KEY_ZERO] && !cmd->arti) + { + gamekeydown[KEY_ZERO] = false; + cmd->arti = arti_poisonbag; + } + else if(gamekeydown[KEY_NINE] && !cmd->arti) + { + gamekeydown[KEY_NINE] = false; + cmd->arti = arti_blastradius; + } + else if(gamekeydown[KEY_EIGHT] && !cmd->arti) + { + gamekeydown[KEY_EIGHT] = false; + cmd->arti = arti_teleport; + } + else if(gamekeydown[KEY_SEVEN] && !cmd->arti) + { + gamekeydown[KEY_SEVEN] = false; + cmd->arti = arti_teleportother; + } + else if(gamekeydown[KEY_SIX] && !cmd->arti) + { + gamekeydown[KEY_SIX] = false; + cmd->arti = arti_egg; + } + else if(gamekeydown[KEY_FIVE] && !cmd->arti + && !players[consoleplayer].powers[pw_invulnerability]) + { + gamekeydown[KEY_FIVE] = false; + cmd->arti = arti_invulnerability; + } + +// +// buttons +// + cmd->chatchar = CT_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] + || joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse] ) + { + cmd->buttons |= BT_USE; + dclicks = 0; // clear double clicks if hit use button + } + + for(i = 0; i < NUMWEAPONS; i++) + { + if(gamekeydown['1'+i]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i< 1 ) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) + dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + { + dclicktime += ticdup; + if (dclicktime > 20) + { + dclicks = 0; + dclickstate = 0; + } + } + +// +// strafe double click +// + bstrafe = mousebuttons[mousebstrafe] +|| joybuttons[joybstrafe]; + if (bstrafe != dclickstate2 && dclicktime2 > 1 ) + { + dclickstate2 = bstrafe; + if (dclickstate2) + dclicks2++; + if (dclicks2 == 2) + { + cmd->buttons |= BT_USE; + dclicks2 = 0; + } + else + dclicktime2 = 0; + } + else + { + dclicktime2 += ticdup; + if (dclicktime2 > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + } + + if (strafe) + { + side += mousex*2; + } + else + { + cmd->angleturn -= mousex*0x8; + } + forward += mousey; + mousex = mousey = 0; + + if (forward > MaxPlayerMove[pClass]) + { + forward = MaxPlayerMove[pClass]; + } + else if (forward < -MaxPlayerMove[pClass]) + { + forward = -MaxPlayerMove[pClass]; + } + if (side > MaxPlayerMove[pClass]) + { + side = MaxPlayerMove[pClass]; + } + else if (side < -MaxPlayerMove[pClass]) + { + side = -MaxPlayerMove[pClass]; + } + if(players[consoleplayer].powers[pw_speed] + && !players[consoleplayer].morphTics) + { // Adjust for a player with a speed artifact + forward = (3*forward)>>1; + side = (3*side)>>1; + } + cmd->forwardmove += forward; + cmd->sidemove += side; + if(players[consoleplayer].playerstate == PST_LIVE) + { + if(look < 0) + { + look += 16; + } + cmd->lookfly = look; + } + if(flyheight < 0) + { + flyheight += 16; + } + cmd->lookfly |= flyheight<<4; + +// +// special buttons +// + if (sendpause) + { + sendpause = false; + cmd->buttons = BT_SPECIAL | BTS_PAUSE; + } + + if (sendsave) + { + sendsave = false; + cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<type == ev_keyup && ev->data1 == key_useartifact) + { // flag to denote that it's okay to use an artifact + if(!inventory) + { + plr->readyArtifact = plr->inventory[inv_ptr].type; + } + usearti = true; + } + + // Check for spy mode player cycle + if(gamestate == GS_LEVEL && ev->type == ev_keydown + && ev->data1 == KEY_F12 && !deathmatch) + { // Cycle the display player + do + { + displayplayer++; + if(displayplayer == MAXPLAYERS) + { + displayplayer = 0; + } + } while(!playeringame[displayplayer] + && displayplayer != consoleplayer); + return(true); + } + + if(CT_Responder(ev)) + { // Chat ate the event + return(true); + } + if(gamestate == GS_LEVEL) + { + if(SB_Responder(ev)) + { // Status bar ate the event + return(true); + } + if(AM_Responder(ev)) + { // Automap ate the event + return(true); + } + } + + switch(ev->type) + { + case ev_keydown: + if(ev->data1 == key_invleft) + { + inventoryTics = 5*35; + if(!inventory) + { + inventory = true; + break; + } + inv_ptr--; + if(inv_ptr < 0) + { + inv_ptr = 0; + } + else + { + curpos--; + if(curpos < 0) + { + curpos = 0; + } + } + return(true); + } + if(ev->data1 == key_invright) + { + inventoryTics = 5*35; + if(!inventory) + { + inventory = true; + break; + } + inv_ptr++; + if(inv_ptr >= plr->inventorySlotNum) + { + inv_ptr--; + if(inv_ptr < 0) + inv_ptr = 0; + } + else + { + curpos++; + if(curpos > 6) + { + curpos = 6; + } + } + return(true); + } + if(ev->data1 == KEY_PAUSE && !MenuActive) + { + sendpause = true; + return(true); + } + if(ev->data1 < NUMKEYS) + { + gamekeydown[ev->data1] = true; + } + return(true); // eat key down events + + case ev_keyup: + if(ev->data1 < NUMKEYS) + { + gamekeydown[ev->data1] = false; + } + return(false); // always let key up events filter down + + case ev_mouse: + mousebuttons[0] = ev->data1&1; + mousebuttons[1] = ev->data1&2; + mousebuttons[2] = ev->data1&4; + mousex = ev->data2*(mouseSensitivity+5)/10; + mousey = ev->data3*(mouseSensitivity+5)/10; + return(true); // eat events + + case ev_joystick: + joybuttons[0] = ev->data1&1; + joybuttons[1] = ev->data1&2; + joybuttons[2] = ev->data1&4; + joybuttons[3] = ev->data1&8; + joyxmove = ev->data2; + joyymove = ev->data3; + return(true); // eat events + + default: + break; + } + return(false); +} + + +//========================================================================== +// +// G_Ticker +// +//========================================================================== + +void G_Ticker(void) +{ + int i, buf; + ticcmd_t *cmd=NULL; + +// +// do player reborns if needed +// + for (i=0 ; i BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + { + I_Error ("consistency failure (%i should be %i)",cmd->consistancy, consistancy[i][buf]); + } + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = rndindex; + } + } + +// +// check for special buttons +// + for (i=0 ; i>BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + // turn inventory off after a certain amount of time + if(inventory && !(--inventoryTics)) + { + players[consoleplayer].readyArtifact = + players[consoleplayer].inventory[inv_ptr].type; + inventory = false; + cmd->arti = 0; + } +// +// do main actions +// +// +// do main actions +// + switch (gamestate) + { + case GS_LEVEL: + P_Ticker (); + SB_Ticker (); + AM_Ticker (); + CT_Ticker(); + break; + case GS_INTERMISSION: + IN_Ticker (); + break; + case GS_FINALE: + F_Ticker(); + break; + case GS_DEMOSCREEN: + H2_PageTicker (); + break; + } +} + + +/* +============================================================================== + + PLAYER STRUCTURE FUNCTIONS + +also see P_SpawnPlayer in P_Things +============================================================================== +*/ + +//========================================================================== +// +// G_PlayerExitMap +// +// Called when the player leaves a map. +// +//========================================================================== + +void G_PlayerExitMap(int playerNumber) +{ + int i; + player_t *player; + int flightPower; + + player = &players[playerNumber]; + +// if(deathmatch) +// { +// // Strip all but one of each type of artifact +// for(i = 0; i < player->inventorySlotNum; i++) +// { +// player->inventory[i].count = 1; +// } +// player->artifactCount = player->inventorySlotNum; +// } +// else + + // Strip all current powers (retain flight) + flightPower = player->powers[pw_flight]; + memset(player->powers, 0, sizeof(player->powers)); + player->powers[pw_flight] = flightPower; + + if(deathmatch) + { + player->powers[pw_flight] = 0; + } + else + { + if(P_GetMapCluster(gamemap) != P_GetMapCluster(LeaveMap)) + { // Entering new cluster + // Strip all keys + player->keys = 0; + + // Strip flight artifact + for(i = 0; i < 25; i++) + { + player->powers[pw_flight] = 0; + P_PlayerUseArtifact(player, arti_fly); + } + player->powers[pw_flight] = 0; + } + } + + if(player->morphTics) + { + player->readyweapon = player->mo->special1; // Restore weapon + player->morphTics = 0; + } + player->messageTics = 0; + player->lookdir = 0; + player->mo->flags &= ~MF_SHADOW; // Remove invisibility + player->extralight = 0; // Remove weapon flashes + player->fixedcolormap = 0; // Remove torch + player->damagecount = 0; // No palette changes + player->bonuscount = 0; + player->poisoncount = 0; + if(player == &players[consoleplayer]) + { + SB_state = -1; // refresh the status bar + viewangleoffset = 0; + } +} + +//========================================================================== +// +// G_PlayerReborn +// +// Called after a player dies. Almost everything is cleared and +// initialized. +// +//========================================================================== + +void G_PlayerReborn(int player) +{ + player_t *p; + int frags[MAXPLAYERS]; + int killcount, itemcount, secretcount; + uint worldTimer; + + memcpy(frags, players[player].frags, sizeof(frags)); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + worldTimer = players[player].worldTimer; + + p = &players[player]; + memset(p, 0, sizeof(*p)); + + memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + players[player].worldTimer = worldTimer; + players[player].class = PlayerClass[player]; + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = MAXHEALTH; + p->readyweapon = p->pendingweapon = WP_FIRST; + p->weaponowned[WP_FIRST] = true; + p->messageTics = 0; + p->lookdir = 0; + localQuakeHappening[player] = false; + if(p == &players[consoleplayer]) + { + SB_state = -1; // refresh the status bar + inv_ptr = 0; // reset the inventory pointer + curpos = 0; + viewangleoffset = 0; + } +} + +/* +==================== += += G_CheckSpot += += Returns false if the player cannot be respawned at the given mapthing_t spot += because something is occupying it +==================== +*/ + +void P_SpawnPlayer (mapthing_t *mthing); + +boolean G_CheckSpot (int playernum, mapthing_t *mthing) +{ + fixed_t x,y; + subsector_t *ss; + unsigned an; + mobj_t *mo; + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + players[playernum].mo->flags2 &= ~MF2_PASSMOBJ; + if (!P_CheckPosition (players[playernum].mo, x, y) ) + { + players[playernum].mo->flags2 |= MF2_PASSMOBJ; + return false; + } + players[playernum].mo->flags2 |= MF2_PASSMOBJ; + +// spawn a teleport fog + ss = R_PointInSubsector (x,y); + an = ( ANG45 * (mthing->angle/45) ) >> ANGLETOFINESHIFT; + + mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an], + ss->sector->floorheight+TELEFOGHEIGHT, MT_TFOG); + if (players[consoleplayer].viewz != 1) + S_StartSound (mo, SFX_TELEPORT); // don't start sound on first frame + + return true; +} + +/* +==================== += += G_DeathMatchSpawnPlayer += += Spawns a player at one of the random death match spots += called at level load and each death +==================== +*/ + +void G_DeathMatchSpawnPlayer (int playernum) +{ + int i,j; + int selections; + + selections = deathmatch_p - deathmatchstarts; + + // This check has been moved to p_setup.c:P_LoadThings() + //if (selections < 8) + // I_Error ("Only %i deathmatch spots, 8 required", selections); + + for (j=0 ; j<20 ; j++) + { + i = P_Random() % selections; + if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) + { + deathmatchstarts[i].type = playernum+1; + P_SpawnPlayer (&deathmatchstarts[i]); + return; + } + } + +// no good spot, so the player will probably get stuck + P_SpawnPlayer (&playerstarts[0][playernum]); +} + +//========================================================================== +// +// G_DoReborn +// +//========================================================================== + +void G_DoReborn(int playernum) +{ + int i; + boolean oldWeaponowned[NUMWEAPONS]; + int oldKeys; + int oldPieces; + boolean foundSpot; + int bestWeapon; + + if(G_CheckDemoStatus()) + { + return; + } + if(!netgame) + { + if(SV_RebornSlotAvailable()) + { // Use the reborn code if the slot is available + gameaction = ga_singlereborn; + } + else + { // Start a new game if there's no reborn info + gameaction = ga_newgame; + } + } + else + { // Net-game + players[playernum].mo->player = NULL; // Dissassociate the corpse + + if(deathmatch) + { // Spawn at random spot if in death match + G_DeathMatchSpawnPlayer(playernum); + return; + } + + // Cooperative net-play, retain keys and weapons + oldKeys = players[playernum].keys; + oldPieces = players[playernum].pieces; + for(i = 0; i < NUMWEAPONS; i++) + { + oldWeaponowned[i] = players[playernum].weaponowned[i]; + } + + foundSpot = false; + if(G_CheckSpot(playernum, + &playerstarts[RebornPosition][playernum])) + { // Appropriate player start spot is open + P_SpawnPlayer(&playerstarts[RebornPosition][playernum]); + foundSpot = true; + } + else + { + // Try to spawn at one of the other player start spots + for(i = 0; i < MAXPLAYERS; i++) + { + if(G_CheckSpot(playernum, &playerstarts[RebornPosition][i])) + { // Found an open start spot + + // Fake as other player + playerstarts[RebornPosition][i].type = playernum+1; + P_SpawnPlayer(&playerstarts[RebornPosition][i]); + + // Restore proper player type + playerstarts[RebornPosition][i].type = i+1; + + foundSpot = true; + break; + } + } + } + + if(foundSpot == false) + { // Player's going to be inside something + P_SpawnPlayer(&playerstarts[RebornPosition][playernum]); + } + + // Restore keys and weapons + players[playernum].keys = oldKeys; + players[playernum].pieces = oldPieces; + for(bestWeapon = 0, i = 0; i < NUMWEAPONS; i++) + { + if(oldWeaponowned[i]) + { + bestWeapon = i; + players[playernum].weaponowned[i] = true; + } + } + players[playernum].mana[MANA_1] = 25; + players[playernum].mana[MANA_2] = 25; + if(bestWeapon) + { // Bring up the best weapon + players[playernum].pendingweapon = bestWeapon; + } + } +} + +void G_ScreenShot (void) +{ + gameaction = ga_screenshot; +} + +//========================================================================== +// +// G_StartNewInit +// +//========================================================================== + +void G_StartNewInit(void) +{ + SV_InitBaseSlot(); + SV_ClearRebornSlot(); + P_ACSInitNewGame(); + // Default the player start spot group to 0 + RebornPosition = 0; +} + +//========================================================================== +// +// G_StartNewGame +// +//========================================================================== + +void G_StartNewGame(skill_t skill) +{ + int realMap; + + G_StartNewInit(); + realMap = P_TranslateMap(1); + if(realMap == -1) + { + realMap = 1; + } + G_InitNew(TempSkill, 1, realMap); +} + +//========================================================================== +// +// G_TeleportNewMap +// +// Only called by the warp cheat code. Works just like normal map to map +// teleporting, but doesn't do any interlude stuff. +// +//========================================================================== + +void G_TeleportNewMap(int map, int position) +{ + gameaction = ga_leavemap; + LeaveMap = map; + LeavePosition = position; +} + +//========================================================================== +// +// G_DoTeleportNewMap +// +//========================================================================== + +void G_DoTeleportNewMap(void) +{ + SV_MapTeleport(LeaveMap, LeavePosition); + gamestate = GS_LEVEL; + gameaction = ga_nothing; + RebornPosition = LeavePosition; +} + +/* +boolean secretexit; +void G_ExitLevel (void) +{ + secretexit = false; + gameaction = ga_completed; +} +void G_SecretExitLevel (void) +{ + secretexit = true; + gameaction = ga_completed; +} +*/ + +//========================================================================== +// +// G_Completed +// +// Starts intermission routine, which is used only during hub exits, +// and DeathMatch games. +//========================================================================== + +void G_Completed(int map, int position) +{ + gameaction = ga_completed; + LeaveMap = map; + LeavePosition = position; +} + +void G_DoCompleted(void) +{ + int i; + + gameaction = ga_nothing; + if(G_CheckDemoStatus()) + { + return; + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + G_PlayerExitMap(i); + } + } + if(LeaveMap == -1 && LeavePosition == -1) + { + gameaction = ga_victory; + return; + } + else + { + gamestate = GS_INTERMISSION; + IN_Start(); + } + +/* + int i; + static int afterSecret[3] = { 7, 5, 5 }; + + gameaction = ga_nothing; + if(G_CheckDemoStatus()) + { + return; + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + G_PlayerFinishLevel(i); + } + } + prevmap = gamemap; + if(secretexit == true) + { + gamemap = 9; + } + else if(gamemap == 9) + { // Finished secret level + gamemap = afterSecret[gameepisode-1]; + } + else if(gamemap == 8) + { + gameaction = ga_victory; + return; + } + else + { + gamemap++; + } + gamestate = GS_INTERMISSION; + IN_Start(); +*/ +} + +//============================================================================ +// +// G_WorldDone +// +//============================================================================ + +void G_WorldDone(void) +{ + gameaction = ga_worlddone; +} + +//============================================================================ +// +// G_DoWorldDone +// +//============================================================================ + +void G_DoWorldDone(void) +{ + gamestate = GS_LEVEL; + G_DoLoadLevel(); + gameaction = ga_nothing; + viewactive = true; +} + +//========================================================================== +// +// G_DoSingleReborn +// +// Called by G_Ticker based on gameaction. Loads a game from the reborn +// save slot. +// +//========================================================================== + +void G_DoSingleReborn(void) +{ + gameaction = ga_nothing; + SV_LoadGame(SV_GetRebornSlot()); + SB_SetClassData(); +} + +//========================================================================== +// +// G_LoadGame +// +// Can be called by the startup code or the menu task. +// +//========================================================================== + +static int GameLoadSlot; + +void G_LoadGame(int slot) +{ + GameLoadSlot = slot; + gameaction = ga_loadgame; +} + +//========================================================================== +// +// G_DoLoadGame +// +// Called by G_Ticker based on gameaction. +// +//========================================================================== + +void G_DoLoadGame(void) +{ + gameaction = ga_nothing; + SV_LoadGame(GameLoadSlot); + if(!netgame) + { // Copy the base slot to the reborn slot + SV_UpdateRebornSlot(); + } + SB_SetClassData(); +} + +//========================================================================== +// +// G_SaveGame +// +// Called by the menu task. is a 24 byte text string. +// +//========================================================================== + +void G_SaveGame(int slot, char *description) +{ + savegameslot = slot; + strcpy(savedescription, description); + sendsave = true; +} + +//========================================================================== +// +// G_DoSaveGame +// +// Called by G_Ticker based on gameaction. +// +//========================================================================== + +void G_DoSaveGame(void) +{ + SV_SaveGame(savegameslot, savedescription); + gameaction = ga_nothing; + savedescription[0] = 0; + P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true); +} + +//========================================================================== +// +// G_DeferredNewGame +// +//========================================================================== + +void G_DeferredNewGame(skill_t skill) +{ + TempSkill = skill; + gameaction = ga_newgame; +} + +//========================================================================== +// +// G_DoNewGame +// +//========================================================================== + +void G_DoNewGame(void) +{ + G_StartNewGame(TempSkill); + gameaction = ga_nothing; +} + +/* +==================== += += G_InitNew += += Can be called by the startup code or the menu task += consoleplayer, displayplayer, playeringame[] should be set +==================== +*/ + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + TempSkill = skill; + TempEpisode = episode; + TempMap = map; + gameaction = ga_initnew; +} + +void G_DoInitNew(void) +{ + SV_InitBaseSlot(); + G_InitNew(TempSkill, TempEpisode, TempMap); + gameaction = ga_nothing; +} + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + if(paused) + { + paused = false; + S_ResumeSound(); + } + if(skill < sk_baby) + { + skill = sk_baby; + } + if(skill > sk_nightmare) + { + skill = sk_nightmare; + } + if(map < 1) + { + map = 1; + } + if(map > 99) + { + map = 99; + } + M_ClearRandom(); + // Force players to be initialized upon first level load + for(i = 0; i < MAXPLAYERS; i++) + { + players[i].playerstate = PST_REBORN; + players[i].worldTimer = 0; + } + + // Set up a bunch of globals + usergame = true; // will be set false if a demo + paused = false; + demorecording = false; + demoplayback = false; + viewactive = true; + gameepisode = episode; + gamemap = map; + gameskill = skill; + BorderNeedRefresh = true; + + // Initialize the sky + R_InitSky(map); + + // Give one null ticcmd_t + //gametic = 0; + //maketic = 1; + //for (i=0 ; iforwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + cmd->angleturn = ((unsigned char)*demo_p++)<<8; + cmd->buttons = (unsigned char)*demo_p++; + cmd->lookfly = (unsigned char)*demo_p++; + cmd->arti = (unsigned char)*demo_p++; +} + +void G_WriteDemoTiccmd (ticcmd_t *cmd) +{ + if (gamekeydown['q']) // press q to end demo recording + G_CheckDemoStatus (); + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + *demo_p++ = cmd->angleturn>>8; + *demo_p++ = cmd->buttons; + *demo_p++ = cmd->lookfly; + *demo_p++ = cmd->arti; + demo_p -= 6; + G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same +} + + + +/* +=================== += += G_RecordDemo += +=================== +*/ + +void G_RecordDemo (skill_t skill, int numplayers, int episode, int map, char *name) +{ + int i; + + G_InitNew (skill, episode, map); + usergame = false; + strcpy (demoname, name); + strcat (demoname, ".lmp"); + demobuffer = demo_p = Z_Malloc (0x20000,PU_STATIC,NULL); + *demo_p++ = skill; + *demo_p++ = episode; + *demo_p++ = map; + + for (i=0 ; i +#include +#include +#endif +#include +#include +#include +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define CONFIG_FILE_NAME "hexen.cfg" +#define MAXWADFILES 20 + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + char *name; + void (*func)(char **args, int tag); + int requiredArgs; + int tag; +} execOpt_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +void R_ExecuteSetViewSize(void); +void D_CheckNetGame(void); +void G_BuildTiccmd(ticcmd_t *cmd); +void F_Drawer(void); +boolean F_Responder(event_t *ev); +void I_StartupKeyboard(void); +void I_StartupJoystick(void); +void I_ShutdownKeyboard(void); +void S_InitScript(void); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void H2_ProcessEvents(void); +void H2_DoAdvanceDemo(void); +void H2_AdvanceDemo(void); +void H2_StartTitle(void); +void H2_PageTicker(void); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void DrawMessage(void); +static void PageDrawer(void); +static void HandleArgs(void); +static void CheckRecordFrom(void); +static void AddWADFile(char *file); +static void DrawAndBlit(void); +static void ExecOptionFILE(char **args, int tag); +static void ExecOptionSCRIPTS(char **args, int tag); +static void ExecOptionDEVMAPS(char **args, int tag); +static void ExecOptionSKILL(char **args, int tag); +static void ExecOptionPLAYDEMO(char **args, int tag); +static void ExecOptionMAXZONE(char **args, int tag); +static void CreateSavePath(void); +static void WarpCheck(void); + +#ifdef TIMEBOMB +static void DoTimeBomb(void); +#endif + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern boolean automapactive; +extern boolean MenuActive; +extern boolean askforquit; +extern char *SavePath; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +boolean DevMaps; // true = Map development mode +char *DevMapsDir = ""; // development maps directory +boolean shareware; // true if only episode 1 present +boolean nomonsters; // checkparm of -nomonsters +boolean respawnparm; // checkparm of -respawn +boolean randomclass; // checkparm of -randclass +boolean debugmode; // checkparm of -debug +boolean ravpic; // checkparm of -ravpic +boolean cdrom; // true if cd-rom mode active +boolean cmdfrag; // true if a CMD_FRAG packet should be sent out +boolean singletics; // debug flag to cancel adaptiveness +boolean artiskip; // whether shift-enter skips an artifact +int maxzone = 0x800000; // Maximum allocated for zone heap (8meg default) +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; +boolean advancedemo; +FILE *debugfile; +event_t events[MAXEVENTS]; +int eventhead; +int eventtail; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int WarpMap; +static int demosequence; +static int pagetic; +static char *pagename; +#ifdef __NeXT__ +static char *wadfiles[MAXWADFILES] = +{ + "/Novell/H2/source/hexen.wad" +}; +#else +static char *wadfiles[MAXWADFILES] = +{ + "hexen.wad" +}; +#endif +static execOpt_t ExecOptions[] = +{ + { "-file", ExecOptionFILE, 1, 0 }, + { "-scripts", ExecOptionSCRIPTS, 1, 0 }, + { "-devmaps", ExecOptionDEVMAPS, 1, 0 }, + { "-skill", ExecOptionSKILL, 1, 0 }, + { "-playdemo", ExecOptionPLAYDEMO, 1, 0 }, + { "-timedemo", ExecOptionPLAYDEMO, 1, 0 }, + { "-maxzone", ExecOptionMAXZONE, 1, 0 }, + { NULL, NULL, 0, 0 } // Terminator +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// H2_Main +// +//========================================================================== +void InitMapMusicInfo(void); + +void H2_Main(void) +{ + int p; + + M_FindResponseFile(); + setbuf(stdout, NULL); + startepisode = 1; + autostart = false; + startskill = sk_medium; + startmap = 1; + shareware = false; // Always false for Hexen + + HandleArgs(); + + // Initialize subsystems + + ST_Message("V_Init: allocate screens.\n"); + V_Init(); + + // Load defaults before initing other systems + ST_Message("M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults(CONFIG_FILE_NAME); + + // Now that the savedir is loaded from .CFG, make sure it exists + CreateSavePath(); + + // HEXEN MODIFICATION: + // There is a realloc() in W_AddFile() that might fail if the zone + // heap has been previously allocated, so we need to initialize the + // WAD files BEFORE the zone memory initialization. + ST_Message("W_Init: Init WADfiles.\n"); + W_InitMultipleFiles(wadfiles); + +#ifdef TIMEBOMB + DoTimeBomb(); +#endif + + ST_Message("Z_Init: Init zone memory allocation daemon.\n"); + Z_Init(); + +#ifdef __WATCOMC__ + I_StartupKeyboard(); + I_StartupJoystick(); +#endif + + ST_Message("MN_Init: Init menu system.\n"); + MN_Init(); + + ST_Message("CT_Init: Init chat mode data.\n"); + CT_Init(); + + InitMapMusicInfo(); // Init music fields in mapinfo + +#ifdef __WATCOMC__ + ST_Message("S_InitScript\n"); + S_InitScript(); +#endif + + ST_Message("SN_InitSequenceScript: Registering sound sequences.\n"); + SN_InitSequenceScript(); + ST_Message("I_Init: Setting up machine state.\n"); + I_Init(); + + ST_Message("ST_Init: Init startup screen.\n"); + ST_Init(); + + S_StartSongName("orb", true); + + // Show version message now, so it's visible during R_Init() + ST_Message("Executable: "VERSIONTEXT".\n"); + + ST_Message("R_Init: Init Hexen refresh daemon"); + R_Init(); + ST_Message("\n"); + + if (M_CheckParm("-net")) ST_NetProgress(); // Console player found + + ST_Message("P_Init: Init Playloop state.\n"); + P_Init(); + + // Check for command line warping. Follows P_Init() because the + // MAPINFO.TXT script must be already processed. + WarpCheck(); + + if(autostart) + { + ST_Message("Warp to Map %d (\"%s\":%d), Skill %d\n", + WarpMap, P_GetMapName(startmap), startmap, startskill+1); + } + + ST_Message("D_CheckNetGame: Checking network game status.\n"); + D_CheckNetGame(); + + ST_Message("SB_Init: Loading patches.\n"); + SB_Init(); + + CheckRecordFrom(); + + p = M_CheckParm("-record"); + if(p && p < myargc-1) + { + G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p+1]); + H2_GameLoop(); // Never returns + } + + p = M_CheckParm("-playdemo"); + if(p && p < myargc-1) + { + singledemo = true; // Quit after one demo + G_DeferedPlayDemo(myargv[p+1]); + H2_GameLoop(); // Never returns + } + + p = M_CheckParm("-timedemo"); + if(p && p < myargc-1) + { + G_TimeDemo(myargv[p+1]); + H2_GameLoop(); // Never returns + } + + p = M_CheckParm("-loadgame"); + if(p && p < myargc-1) + { + G_LoadGame(atoi(myargv[p+1])); + } + + if(gameaction != ga_loadgame) + { + UpdateState |= I_FULLSCRN; + BorderNeedRefresh = true; + if(autostart || netgame) + { + G_StartNewInit(); + G_InitNew(startskill, startepisode, startmap); + } + else + { + H2_StartTitle(); + } + } + H2_GameLoop(); // Never returns +} + +//========================================================================== +// +// HandleArgs +// +//========================================================================== + +static void HandleArgs(void) +{ + int p; + execOpt_t *opt; + + nomonsters = M_ParmExists("-nomonsters"); + respawnparm = M_ParmExists("-respawn"); + randomclass = M_ParmExists("-randclass"); + ravpic = M_ParmExists("-ravpic"); + artiskip = M_ParmExists("-artiskip"); + debugmode = M_ParmExists("-debug"); + deathmatch = M_ParmExists("-deathmatch"); + cdrom = M_ParmExists("-cdrom"); + cmdfrag = M_ParmExists("-cmdfrag"); + + // Process command line options + for(opt = ExecOptions; opt->name != NULL; opt++) + { + p = M_CheckParm(opt->name); + if(p && p < myargc-opt->requiredArgs) + { + opt->func(&myargv[p], opt->tag); + } + } + + // Look for an external device driver + I_CheckExternDriver(); +} + +//========================================================================== +// +// WarpCheck +// +//========================================================================== + +static void WarpCheck(void) +{ + int p; + int map; + + p = M_CheckParm("-warp"); + if(p && p < myargc-1) + { + WarpMap = atoi(myargv[p+1]); + map = P_TranslateMap(WarpMap); + if(map == -1) + { // Couldn't find real map number + startmap = 1; + ST_Message("-WARP: Invalid map number.\n"); + } + else + { // Found a valid startmap + startmap = map; + autostart = true; + } + } + else + { + WarpMap = 1; + startmap = P_TranslateMap(1); + if(startmap == -1) + { + startmap = 1; + } + } +} + +//========================================================================== +// +// ExecOptionSKILL +// +//========================================================================== + +static void ExecOptionSKILL(char **args, int tag) +{ + startskill = args[1][0]-'1'; + autostart = true; +} + +//========================================================================== +// +// ExecOptionFILE +// +//========================================================================== + +static void ExecOptionFILE(char **args, int tag) +{ + int p; + + p = M_CheckParm("-file"); + while(++p != myargc && myargv[p][0] != '-') + { + AddWADFile(myargv[p]); + } +} + + +//========================================================================== +// +// ExecOptionPLAYDEMO +// +//========================================================================== + +static void ExecOptionPLAYDEMO(char **args, int tag) +{ + char file[256]; + + sprintf(file, "%s.lmp", args[1]); + AddWADFile(file); + ST_Message("Playing demo %s.lmp.\n", args[1]); +} + +//========================================================================== +// +// ExecOptionSCRIPTS +// +//========================================================================== + +static void ExecOptionSCRIPTS(char **args, int tag) +{ + sc_FileScripts = true; + sc_ScriptsDir = args[1]; +} + +//========================================================================== +// +// ExecOptionDEVMAPS +// +//========================================================================== + +static void ExecOptionDEVMAPS(char **args, int tag) +{ + DevMaps = true; + ST_Message("Map development mode enabled:\n"); + ST_Message("[config ] = %s\n", args[1]); + SC_OpenFileCLib(args[1]); + SC_MustGetStringName("mapsdir"); + SC_MustGetString(); + ST_Message("[mapsdir ] = %s\n", sc_String); + DevMapsDir = malloc(strlen(sc_String)+1); + strcpy(DevMapsDir, sc_String); + SC_MustGetStringName("scriptsdir"); + SC_MustGetString(); + ST_Message("[scriptsdir] = %s\n", sc_String); + sc_FileScripts = true; + sc_ScriptsDir = malloc(strlen(sc_String)+1); + strcpy(sc_ScriptsDir, sc_String); + while(SC_GetString()) + { + if(SC_Compare("file")) + { + SC_MustGetString(); + AddWADFile(sc_String); + } + else + { + SC_ScriptError(NULL); + } + } + SC_Close(); +} + + +long superatol(char *s) +{ + long int n=0, r=10, x, mul=1; + char *c=s; + + for (; *c; c++) + { + x = (*c & 223) - 16; + + if (x == -3) + { + mul = -mul; + } + else if (x == 72 && r == 10) + { + n -= (r=n); + if (!r) r=16; + if (r<2 || r>36) return -1; + } + else + { + if (x>10) x-=39; + if (x >= r) return -1; + n = (n*r) + x; + } + } + return(mul*n); +} + + +static void ExecOptionMAXZONE(char **args, int tag) +{ + int size; + + size = superatol(args[1]); + if (size < MINIMUM_HEAP_SIZE) size = MINIMUM_HEAP_SIZE; + if (size > MAXIMUM_HEAP_SIZE) size = MAXIMUM_HEAP_SIZE; + maxzone = size; +} + +//========================================================================== +// +// H2_GameLoop +// +//========================================================================== + +void H2_GameLoop(void) +{ + if(M_CheckParm("-debugfile")) + { + char filename[20]; + sprintf(filename, "debug%i.txt", consoleplayer); + debugfile = fopen(filename,"w"); + } + I_InitGraphics(); + while(1) + { + // Frame syncronous IO operations + I_StartFrame(); + + // Process one or more tics + if(singletics) + { + I_StartTic(); + H2_ProcessEvents(); + G_BuildTiccmd(&netcmds[consoleplayer][maketic%BACKUPTICS]); + if(advancedemo) + { + H2_DoAdvanceDemo(); + } + G_Ticker(); + gametic++; + maketic++; + } + else + { + // Will run at least one tic + TryRunTics(); + } + + // Move positional sounds + S_UpdateSounds(players[displayplayer].mo); + + DrawAndBlit(); + } +} + +//========================================================================== +// +// H2_ProcessEvents +// +// Send all the events of the given timestamp down the responder chain. +// +//========================================================================== + +void H2_ProcessEvents(void) +{ + event_t *ev; + + for(; eventtail != eventhead; eventtail = (++eventtail)&(MAXEVENTS-1)) + { + ev = &events[eventtail]; + if(F_Responder(ev)) + { + continue; + } + if(MN_Responder(ev)) + { + continue; + } + G_Responder(ev); + } +} + +//========================================================================== +// +// H2_PostEvent +// +// Called by the I/O functions when input is detected. +// +//========================================================================== + +void H2_PostEvent(event_t *ev) +{ + events[eventhead] = *ev; + eventhead = (++eventhead)&(MAXEVENTS-1); +} + +//========================================================================== +// +// DrawAndBlit +// +//========================================================================== + +static void DrawAndBlit(void) +{ + // Change the view size if needed + if(setsizeneeded) + { + R_ExecuteSetViewSize(); + } + + // Do buffered drawing + switch(gamestate) + { + case GS_LEVEL: + if(!gametic) + { + break; + } + if(automapactive) + { + AM_Drawer(); + } + else + { + R_RenderPlayerView(&players[displayplayer]); + } + CT_Drawer(); + UpdateState |= I_FULLVIEW; + SB_Drawer(); + break; + case GS_INTERMISSION: + IN_Drawer(); + break; + case GS_FINALE: + F_Drawer(); + break; + case GS_DEMOSCREEN: + PageDrawer(); + break; + } + + if(paused && !MenuActive && !askforquit) + { + if(!netgame) + { + V_DrawPatch(160, viewwindowy+5, W_CacheLumpName("PAUSED", + PU_CACHE)); + } + else + { + V_DrawPatch(160, 70, W_CacheLumpName("PAUSED", + PU_CACHE)); + } + } + + // Draw current message + DrawMessage(); + + // Draw Menu + MN_Drawer(); + + // Send out any new accumulation + NetUpdate(); + + // Flush buffered stuff to screen + I_Update(); +} + +//========================================================================== +// +// DrawMessage +// +//========================================================================== + +static void DrawMessage(void) +{ + player_t *player; + + player = &players[consoleplayer]; + if(player->messageTics <= 0 || !player->message) + { // No message + return; + } + if(player->yellowMessage) + { + MN_DrTextAYellow(player->message, + 160-MN_TextAWidth(player->message)/2, 1); + } + else + { + MN_DrTextA(player->message, 160-MN_TextAWidth(player->message)/2, 1); + } +} + +//========================================================================== +// +// H2_PageTicker +// +//========================================================================== + +void H2_PageTicker(void) +{ + if(--pagetic < 0) + { + H2_AdvanceDemo(); + } +} + +//========================================================================== +// +// PageDrawer +// +//========================================================================== + +static void PageDrawer(void) +{ + V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE)); + if(demosequence == 1) + { + V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE)); + } + UpdateState |= I_FULLSCRN; +} + +//========================================================================== +// +// H2_AdvanceDemo +// +// Called after each demo or intro demosequence finishes. +// +//========================================================================== + +void H2_AdvanceDemo(void) +{ + advancedemo = true; +} + +//========================================================================== +// +// H2_DoAdvanceDemo +// +//========================================================================== + +void H2_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; // don't reborn + advancedemo = false; + usergame = false; // can't save/end game here + paused = false; + gameaction = ga_nothing; + demosequence = (demosequence+1)%7; + switch(demosequence) + { + case 0: + pagetic = 280; + gamestate = GS_DEMOSCREEN; + pagename = "TITLE"; + S_StartSongName("hexen", true); + break; + case 1: + pagetic = 210; + gamestate = GS_DEMOSCREEN; + pagename = "TITLE"; + break; + case 2: + BorderNeedRefresh = true; + UpdateState |= I_FULLSCRN; + G_DeferedPlayDemo("demo1"); + break; + case 3: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 4: + BorderNeedRefresh = true; + UpdateState |= I_FULLSCRN; + G_DeferedPlayDemo("demo2"); + break; + case 5: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 6: + BorderNeedRefresh = true; + UpdateState |= I_FULLSCRN; + G_DeferedPlayDemo("demo3"); + break; + } +} + +//========================================================================== +// +// H2_StartTitle +// +//========================================================================== + +void H2_StartTitle(void) +{ + gameaction = ga_nothing; + demosequence = -1; + H2_AdvanceDemo(); +} + +//========================================================================== +// +// CheckRecordFrom +// +// -recordfrom +// +//========================================================================== + +static void CheckRecordFrom(void) +{ + int p; + + p = M_CheckParm("-recordfrom"); + if(!p || p > myargc-2) + { // Bad args + return; + } + G_LoadGame(atoi(myargv[p+1])); + G_DoLoadGame(); // Load the gameskill etc info from savegame + G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p+2]); + H2_GameLoop(); // Never returns +} + +//========================================================================== +// +// AddWADFile +// +//========================================================================== + +static void AddWADFile(char *file) +{ + int i; + char *new; + + ST_Message("Adding external file: %s\n", file); + i = 0; + while(wadfiles[i]) + { + i++; + } + new = malloc(strlen(file)+1); + strcpy(new, file); + wadfiles[i] = new; +} + +#ifdef __WATCOMC__ +/* +void CleanExit(void) +{ + union REGS regs; + + I_ShutdownKeyboard(); + regs.x.eax = 0x3; + int386(0x10, ®s, ®s); + printf("Exited from HEXEN: Beyond Heretic.\n"); + exit(1); +} +*/ +#endif + +//========================================================================== +// +// FixedDiv +// +//========================================================================== + +fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + if((abs(a)>>14) >= abs(b)) + { + return((a^b)<0 ? MININT : MAXINT); + } + return(FixedDiv2(a, b)); +} + + +//========================================================================== +// +// CreateSavePath +// +//========================================================================== + +static void CreateSavePath(void) +{ + char creationPath[121]; + int len; + + if(cdrom == true) + { + SavePath = "c:\\hexndata\\"; + } + len = strlen(SavePath); + if (len >= 120) I_Error("Save path too long\n"); + strcpy(creationPath, SavePath); +#ifdef __WATCOMC__ + creationPath[len-1] = 0; + mkdir(creationPath); +#endif +} + +#ifdef TIMEBOMB +//========================================================================== +// +// DoTimeBomb +// +//========================================================================== + +static void DoTimeBomb(void) +{ +#ifdef __WATCOMC__ + time_t timeOfDay; + struct tm timeBuffer; + + timeOfDay = time(NULL); + _localtime(&timeOfDay, &timeBuffer); + if(timeBuffer.tm_year != TIMEBOMB_YEAR + || timeBuffer.tm_yday < TIMEBOMB_STARTDATE + || timeBuffer.tm_yday > TIMEBOMB_ENDDATE) + { + I_Error("W_InitWadfiles: Wad file doesn't have IWAD or PWAD id\n"); + } + + printf("\n===============================================================================\n"); + printf(" Hexen: Beyond Heretic\n\n"); + printf(" Beta -- Do Not Distribute!\n"); + printf(" Press any key to continue.\n"); + printf("===============================================================================\n"); + + getch(); + printf("\n"); +#endif +} +#endif diff --git a/src/hexen/h2def.h b/src/hexen/h2def.h new file mode 100644 index 00000000..5b8b3a11 --- /dev/null +++ b/src/hexen/h2def.h @@ -0,0 +1,1459 @@ + +//************************************************************************** +//** +//** h2def.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: h2def.h,v $ +//** $Revision: 1.128 $ +//** $Date: 96/01/16 10:35:31 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#ifndef __H2DEF__ +#define __H2DEF__ +#include +#include +#include +#include "st_start.h" +#ifdef __WATCOMC__ +#include +#define strcasecmp strcmpi +#define strncasecmp strnicmp +#endif + +#define VERSION 110 +#define VERSION_TEXT "v1.1" + +// Uncomment, to enable all timebomb stuff +//#define TIMEBOMB +#define TIMEBOMB_YEAR 95 // years since 1900 +#define TIMEBOMB_STARTDATE 268 // initial date (9/26) +#define TIMEBOMB_ENDDATE 301 // end date (10/29) + +// if rangecheck is undefined, most parameter validation debugging code +// will not be compiled +#ifndef NORANGECHECKING +#define RANGECHECK +#endif + +// Past distributions +#ifndef VER_ID +#define VER_ID "DVL" +#endif +//#define VERSIONTEXT "ID V1.2" +//#define VERSIONTEXT "RETAIL STORE BETA" // 9/26/95 +//#define VERSIONTEXT "DVL BETA 10 05 95" // Used for GT for testing +//#define VERSIONTEXT "DVL BETA 10 07 95" // Just an update for Romero +//#define VERSIONTEXT "FINAL 1.0 (10 13 95)" // Just an update for Romero +#ifdef RANGECHECK +#define VERSIONTEXT "Version 1.1 +R "__DATE__" ("VER_ID")" +#else +#define VERSIONTEXT "Version 1.1 "__DATE__" ("VER_ID")" +#endif + +// all exterior data is defined here +#include "xddefs.h" + +// all important printed strings +#include "textdefs.h" + +// header generated by multigen utility +#include "info.h" + +extern byte *destview, *destscreen; // PC direct to screen pointers + +// +// most key data are simple ascii (uppercased) +// +#define KEY_RIGHTARROW 0xae +#define KEY_LEFTARROW 0xac +#define KEY_UPARROW 0xad +#define KEY_DOWNARROW 0xaf +#define KEY_ESCAPE 27 +#define KEY_ENTER 13 +#define KEY_F1 (0x80+0x3b) +#define KEY_F2 (0x80+0x3c) +#define KEY_F3 (0x80+0x3d) +#define KEY_F4 (0x80+0x3e) +#define KEY_F5 (0x80+0x3f) +#define KEY_F6 (0x80+0x40) +#define KEY_F7 (0x80+0x41) +#define KEY_F8 (0x80+0x42) +#define KEY_F9 (0x80+0x43) +#define KEY_F10 (0x80+0x44) +#define KEY_F11 (0x80+0x57) +#define KEY_F12 (0x80+0x58) + +#define KEY_BACKSPACE 127 +#define KEY_PAUSE 0xff + +#define KEY_EQUALS 0x3d +#define KEY_MINUS 0x2d + +#define KEY_RSHIFT (0x80+0x36) +#define KEY_RCTRL (0x80+0x1d) +#define KEY_RALT (0x80+0x38) + +#define KEY_LALT KEY_RALT + +#define KEY_FIVE 0x35 +#define KEY_SIX 0x36 +#define KEY_SEVEN 0x37 +#define KEY_EIGHT 0x38 +#define KEY_NINE 0x39 +#define KEY_ZERO 0x30 +#define KEY_BACKSLASH 0x5C + + +#define MAXCHAR ((char)0x7f) +#define MAXSHORT ((short)0x7fff) +#define MAXINT ((int)0x7fffffff) /* max pos 32-bit int */ +#define MAXLONG ((long)0x7fffffff) + +#define MINCHAR ((char)0x80) +#define MINSHORT ((short)0x8000) +#define MININT ((int)0x80000000) /* max negative 32-bit integer */ +#define MINLONG ((long)0x80000000) + +#define FINEANGLES 8192 +#define FINEMASK (FINEANGLES-1) +#define ANGLETOFINESHIFT 19 // 0x100000000 to 0x2000 + +/* +=============================================================================== + + GLOBAL TYPES + +=============================================================================== +*/ + +//#define NUMARTIFCTS 28 +#define MAXPLAYERS 8 +#define TICRATE 35 // number of tics / second +#define TICSPERSEC 35 + +#define MINIMUM_HEAP_SIZE 0x800000 // 8 meg +#define MAXIMUM_HEAP_SIZE 0x2000000 // 32 meg + +#define FRACBITS 16 +#define FRACUNIT (1<type] + int tics; // state tic counter + state_t *state; + int damage; // For missiles + int flags; + int flags2; // Heretic flags + int special1; // Special info + int special2; // Special info + int health; + int movedir; // 0-7 + int movecount; // when 0, select a new dir + struct mobj_s *target; // thing being chased/attacked (or NULL) + // also the originator for missiles + int reactiontime; // if non 0, don't attack yet + // used by player to freeze a bit after + // teleporting + int threshold; // if > 0, the target will be chased + // no matter what (even if shot) + struct player_s *player; // only valid if type == MT_PLAYER + int lastlook; // player number last looked for + fixed_t floorclip; // value to use for floor clipping + int archiveNum; // Identity during archive + short tid; // thing identifier + byte special; // special + byte args[5]; // special arguments +} mobj_t; + +// each sector has a degenmobj_t in it's center for sound origin purposes +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x,y,z; +} degenmobj_t; + +// Most damage defined using HITDICE +#define HITDICE(a) ((1+(P_Random()&7))*a) + +// +// frame flags +// +#define FF_FULLBRIGHT 0x8000 // flag in thing->frame +#define FF_FRAMEMASK 0x7fff + +// --- mobj.flags --- + +#define MF_SPECIAL 1 // call P_SpecialThing when touched +#define MF_SOLID 2 +#define MF_SHOOTABLE 4 +#define MF_NOSECTOR 8 // don't use the sector links + // (invisible but touchable) +#define MF_NOBLOCKMAP 16 // don't use the blocklinks + // (inert but displayable) +#define MF_AMBUSH 32 +#define MF_JUSTHIT 64 // try to attack right back +#define MF_JUSTATTACKED 128 // take at least one step before attacking +#define MF_SPAWNCEILING 256 // hang from ceiling instead of floor +#define MF_NOGRAVITY 512 // don't apply gravity every tic + +// movement flags +#define MF_DROPOFF 0x400 // allow jumps from high places +#define MF_PICKUP 0x800 // for players to pick up items +#define MF_NOCLIP 0x1000 // player cheat +#define MF_SLIDE 0x2000 // keep info about sliding along walls +#define MF_FLOAT 0x4000 // allow moves to any height, no gravity +#define MF_TELEPORT 0x8000 // don't cross lines or look at heights +#define MF_MISSILE 0x10000 // don't hit same species, explode on block + +#define MF_ALTSHADOW 0x20000 // alternate fuzzy draw +#define MF_SHADOW 0x40000 // use fuzzy draw (shadow demons / invis) +#define MF_NOBLOOD 0x80000 // don't bleed when shot (use puff) +#define MF_CORPSE 0x100000 // don't stop moving halfway off a step +#define MF_INFLOAT 0x200000 // floating to a height for a move, don't + // auto float to target's height + +#define MF_COUNTKILL 0x400000 // count towards intermission kill total +#define MF_ICECORPSE 0x800000 // a frozen corpse (for blasting) + +#define MF_SKULLFLY 0x1000000 // skull in flight +#define MF_NOTDMATCH 0x2000000 // don't spawn in death match (key cards) + +//#define MF_TRANSLATION 0xc000000 // if 0x4 0x8 or 0xc, use a translation +#define MF_TRANSLATION 0x1c000000 // use a translation table (>>MF_TRANSHIFT) +#define MF_TRANSSHIFT 26 // table for player colormaps + + +// --- mobj.flags2 --- + +#define MF2_LOGRAV 0x00000001 // alternate gravity setting +#define MF2_WINDTHRUST 0x00000002 // gets pushed around by the wind + // specials +#define MF2_FLOORBOUNCE 0x00000004 // bounces off the floor +#define MF2_BLASTED 0x00000008 // missile will pass through ghosts +#define MF2_FLY 0x00000010 // fly mode is active +#define MF2_FLOORCLIP 0x00000020 // if feet are allowed to be clipped +#define MF2_SPAWNFLOAT 0x00000040 // spawn random float z +#define MF2_NOTELEPORT 0x00000080 // does not teleport +#define MF2_RIP 0x00000100 // missile rips through solid + // targets +#define MF2_PUSHABLE 0x00000200 // can be pushed by other moving + // mobjs +#define MF2_SLIDE 0x00000400 // slides against walls +#define MF2_ONMOBJ 0x00000800 // mobj is resting on top of another + // mobj +#define MF2_PASSMOBJ 0x00001000 // Enable z block checking. If on, + // this flag will allow the mobj to + // pass over/under other mobjs. +#define MF2_CANNOTPUSH 0x00002000 // cannot push other pushable mobjs +#define MF2_DROPPED 0x00004000 // dropped by a demon +#define MF2_BOSS 0x00008000 // mobj is a major boss +#define MF2_FIREDAMAGE 0x00010000 // does fire damage +#define MF2_NODMGTHRUST 0x00020000 // does not thrust target when + // damaging +#define MF2_TELESTOMP 0x00040000 // mobj can stomp another +#define MF2_FLOATBOB 0x00080000 // use float bobbing z movement +#define MF2_DONTDRAW 0x00100000 // don't generate a vissprite +#define MF2_IMPACT 0x00200000 // an MF_MISSILE mobj can activate + // SPAC_IMPACT +#define MF2_PUSHWALL 0x00400000 // mobj can push walls +#define MF2_MCROSS 0x00800000 // can activate monster cross lines +#define MF2_PCROSS 0x01000000 // can activate projectile cross lines +#define MF2_CANTLEAVEFLOORPIC 0x02000000 // stay within a certain floor type +#define MF2_NONSHOOTABLE 0x04000000 // mobj is totally non-shootable, + // but still considered solid +#define MF2_INVULNERABLE 0x08000000 // mobj is invulnerable +#define MF2_DORMANT 0x10000000 // thing is dormant +#define MF2_ICEDAMAGE 0x20000000 // does ice damage +#define MF2_SEEKERMISSILE 0x40000000 // is a seeker (for reflection) +#define MF2_REFLECTIVE 0x80000000 // reflects missiles + +//============================================================================= + +// ===== Player Class Types ===== +typedef enum +{ + PCLASS_FIGHTER, + PCLASS_CLERIC, + PCLASS_MAGE, + PCLASS_PIG, + NUMCLASSES +} pclass_t; + +typedef enum +{ + PST_LIVE, // playing + PST_DEAD, // dead on the ground + PST_REBORN // ready to restart +} playerstate_t; + +// psprites are scaled shapes directly on the view screen +// coordinates are given for a 320*200 view screen +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + +typedef struct +{ + state_t *state; // a NULL state means not active + int tics; + fixed_t sx, sy; +} pspdef_t; + +/* Old Heretic key type +typedef enum +{ + key_yellow, + key_green, + key_blue, + NUMKEYS +} keytype_t; +*/ + +typedef enum +{ + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + NUMKEYS +} keytype_t; + +typedef enum +{ + ARMOR_ARMOR, + ARMOR_SHIELD, + ARMOR_HELMET, + ARMOR_AMULET, + NUMARMOR +} armortype_t; + +typedef enum +{ + WP_FIRST, + WP_SECOND, + WP_THIRD, + WP_FOURTH, + NUMWEAPONS, + WP_NOCHANGE +} weapontype_t; + +typedef enum +{ + MANA_1, + MANA_2, + NUMMANA, + MANA_BOTH, + MANA_NONE +} manatype_t; + +#define MAX_MANA 200 + +#define WPIECE1 1 +#define WPIECE2 2 +#define WPIECE3 4 + +typedef struct +{ + manatype_t mana; + int upstate; + int downstate; + int readystate; + int atkstate; + int holdatkstate; + int flashstate; +} weaponinfo_t; + +extern weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES]; + +typedef enum +{ + arti_none, + arti_invulnerability, + arti_health, + arti_superhealth, + arti_healingradius, + arti_summon, + arti_torch, + arti_egg, + arti_fly, + arti_blastradius, + arti_poisonbag, + arti_teleportother, + arti_speed, + arti_boostmana, + arti_boostarmor, + arti_teleport, + // Puzzle artifacts + arti_firstpuzzitem, + arti_puzzskull = arti_firstpuzzitem, + arti_puzzgembig, + arti_puzzgemred, + arti_puzzgemgreen1, + arti_puzzgemgreen2, + arti_puzzgemblue1, + arti_puzzgemblue2, + arti_puzzbook1, + arti_puzzbook2, + arti_puzzskull2, + arti_puzzfweapon, + arti_puzzcweapon, + arti_puzzmweapon, + arti_puzzgear1, + arti_puzzgear2, + arti_puzzgear3, + arti_puzzgear4, + NUMARTIFACTS +} artitype_t; + +typedef enum +{ + pw_None, + pw_invulnerability, + pw_allmap, + pw_infrared, + pw_flight, + pw_shield, + pw_health2, + pw_speed, + pw_minotaur, + NUMPOWERS +} powertype_t; + +#define INVULNTICS (30*35) +#define INVISTICS (60*35) +#define INFRATICS (120*35) +#define IRONTICS (60*35) +#define WPNLEV2TICS (40*35) +#define FLIGHTTICS (60*35) +#define SPEEDTICS (45*35) +#define MORPHTICS (40*35) +#define MAULATORTICS (25*35) + +#define MESSAGETICS (4*35) +#define BLINKTHRESHOLD (4*35) + +#define NUMINVENTORYSLOTS NUMARTIFACTS + +typedef struct +{ + int type; + int count; +} inventory_t; + +/* +================ += += player_t += +================ +*/ + +typedef struct player_s +{ + mobj_t *mo; + playerstate_t playerstate; + ticcmd_t cmd; + + pclass_t class; // player class type + + fixed_t viewz; // focal origin above r.z + fixed_t viewheight; // base height above floor for viewz + fixed_t deltaviewheight; // squat speed + fixed_t bob; // bounded/scaled total momentum + + int flyheight; + int lookdir; + boolean centering; + int health; // only used between levels, mo->health + // is used during levels + int armorpoints[NUMARMOR]; + + inventory_t inventory[NUMINVENTORYSLOTS]; + artitype_t readyArtifact; + int artifactCount; + int inventorySlotNum; + int powers[NUMPOWERS]; + int keys; + int pieces; // Fourth Weapon pieces + signed int frags[MAXPLAYERS]; // kills of other players + weapontype_t readyweapon; + weapontype_t pendingweapon; // wp_nochange if not changing + boolean weaponowned[NUMWEAPONS]; + int mana[NUMMANA]; + int attackdown, usedown; // true if button down last tic + int cheats; // bit flags + + int refire; // refired shots are less accurate + + int killcount, itemcount, secretcount; // for intermission + char message[80]; // hint messages + int messageTics; // counter for showing messages + short ultimateMessage; + short yellowMessage; + int damagecount, bonuscount;// for screen flashing + int poisoncount; // screen flash for poison damage + mobj_t *poisoner; // NULL for non-player mobjs + mobj_t *attacker; // who did damage (NULL for floors) + int extralight; // so gun flashes light up areas + int fixedcolormap; // can be set to REDCOLORMAP, etc + int colormap; // 0-3 for which color to draw player + pspdef_t psprites[NUMPSPRITES]; // view sprites (gun, etc) + int morphTics; // player is a pig if > 0 + uint jumpTics; // delay the next jump for a moment + unsigned int worldTimer; // total time the player's been playing +} player_t; + +#define CF_NOCLIP 1 +#define CF_GODMODE 2 +#define CF_NOMOMENTUM 4 // not really a cheat, just a debug aid + + +#define BACKUPTICS 12 + +typedef struct +{ + unsigned checksum; // high bit is retransmit request + byte retransmitfrom; // only valid if NCMD_RETRANSMIT + byte starttic; + byte player, numtics; + ticcmd_t cmds[BACKUPTICS]; +} doomdata_t; + +typedef struct +{ + long id; + short intnum; // DOOM executes an int to execute commands + +// communication between DOOM and the driver + short command; // CMD_SEND or CMD_GET + short remotenode; // dest for send, set by get (-1 = no packet) + short datalength; // bytes in doomdata to be sent + +// info common to all nodes + short numnodes; // console is allways node 0 + short ticdup; // 1 = no duplication, 2-5 = dup for slow nets + short extratics; // 1 = send a backup tic in every packet + short deathmatch; // 1 = deathmatch + short savegame; // -1 = new game, 0-5 = load savegame + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + +// info specific to this node + short consoleplayer; + short numplayers; + short angleoffset; // 1 = left, 0 = center, -1 = right + short drone; // 1 = drone + +// packet data to be sent + doomdata_t data; +} doomcom_t; + +#define DOOMCOM_ID 0x12345678l + +extern doomcom_t *doomcom; +extern doomdata_t *netbuffer; // points inside doomcom + +#define MAXNETNODES 16 // max computers in a game + +#define CMD_SEND 1 +#define CMD_GET 2 +#define CMD_FRAG 3 + +#define SBARHEIGHT 39 // status bar height at bottom of screen + +void NET_SendFrags(player_t *player); + +/* +=============================================================================== + + GLOBAL VARIABLES + +=============================================================================== +*/ + +#define TELEFOGHEIGHT (32*FRACUNIT) + +#define MAXEVENTS 64 + +extern event_t events[MAXEVENTS]; +extern int eventhead; +extern int eventtail; + +extern fixed_t finesine[5*FINEANGLES/4]; +extern fixed_t *finecosine; + +extern gameaction_t gameaction; + +extern boolean paused; + +extern boolean shareware; // true if other episodes not present + +extern boolean DevMaps; // true = map development mode +extern char *DevMapsDir; // development maps directory + +extern boolean nomonsters; // checkparm of -nomonsters + +extern boolean respawnparm; // checkparm of -respawn + +extern boolean randomclass; // checkparm of -randclass + +extern boolean debugmode; // checkparm of -debug + +extern boolean usergame; // ok to save / end game + +extern boolean ravpic; // checkparm of -ravpic + +extern boolean altpal; // checkparm to use an alternate palette routine + +extern boolean cdrom; // true if cd-rom mode active ("-cdrom") + +extern boolean deathmatch; // only if started as net death + +extern boolean netgame; // only true if >1 player + +extern boolean cmdfrag; // true if a CMD_FRAG packet should be sent out every + // kill + +extern boolean playeringame[MAXPLAYERS]; +extern pclass_t PlayerClass[MAXPLAYERS]; + +extern int consoleplayer; // player taking events and displaying + +extern int displayplayer; + +extern int viewangleoffset; // ANG90 = left side, ANG270 = right + +extern player_t players[MAXPLAYERS]; + +extern boolean singletics; // debug flag to cancel adaptiveness + +extern boolean DebugSound; // debug flag for displaying sound info + +extern boolean demoplayback; +extern int maxzone; // Maximum chunk allocated for zone heap + +extern int Sky1Texture; +extern int Sky2Texture; + +extern gamestate_t gamestate; +extern skill_t gameskill; +//extern boolean respawnmonsters; +extern int gameepisode; +extern int gamemap; +extern int prevmap; +extern int levelstarttic; // gametic at level start +extern int leveltime; // tics in game play for par + +extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +extern int ticdup; + +//#define MAXNETNODES 8 + +extern ticcmd_t localcmds[BACKUPTICS]; +extern int rndindex; +extern int gametic, maketic; +extern int nettics[MAXNETNODES]; + +#define MAXDEATHMATCHSTARTS 16 +extern mapthing_t *deathmatch_p; +extern mapthing_t deathmatchstarts[MAXDEATHMATCHSTARTS]; + +// Position indicator for cooperative net-play reborn +extern int RebornPosition; + +#define MAX_PLAYER_STARTS 8 +extern mapthing_t playerstarts[MAX_PLAYER_STARTS][MAXPLAYERS]; + +extern int viewwindowx; +extern int viewwindowy; +extern int viewwidth; +extern int scaledviewwidth; +extern int viewheight; + +extern int mouseSensitivity; + +extern boolean precache; // if true, load all graphics at level load + +extern byte *screen; // off screen work buffer, from V_video.c + +extern boolean singledemo; // quit after playing a demo from cmdline + +extern FILE *debugfile; +extern int bodyqueslot; +extern skill_t startskill; +extern int startepisode; +extern int startmap; +extern boolean autostart; + +/* +=============================================================================== + + GLOBAL FUNCTIONS + +=============================================================================== +*/ + + +fixed_t FixedMul (fixed_t a, fixed_t b); +fixed_t FixedDiv (fixed_t a, fixed_t b); +fixed_t FixedDiv2 (fixed_t a, fixed_t b); + +#ifdef __WATCOMC__ +#pragma aux FixedMul = \ + "imul ebx", \ + "shrd eax,edx,16" \ + parm [eax] [ebx] \ + value [eax] \ + modify exact [eax edx] + +#pragma aux FixedDiv2 = \ + "cdq", \ + "shld edx,eax,16", \ + "sal eax,16", \ + "idiv ebx" \ + parm [eax] [ebx] \ + value [eax] \ + modify exact [eax edx] +#endif + +#ifdef __BIG_ENDIAN__ +short ShortSwap(short); +long LongSwap(long); +#define SHORT(x) ShortSwap(x) +#define LONG(x) LongSwap(x) +#else +#define SHORT(x) (x) +#define LONG(x) (x) +#endif + + +//----------- +//MEMORY ZONE +//----------- +// tags < 100 are not overwritten until freed +#define PU_STATIC 1 // static entire execution time +#define PU_SOUND 2 // static while playing +#define PU_MUSIC 3 // static while playing +#define PU_DAVE 4 // anything else Dave wants static +#define PU_LEVEL 50 // static until level exited +#define PU_LEVSPEC 51 // a special thinker in a level +// tags >= 100 are purgable whenever needed +#define PU_PURGELEVEL 100 +#define PU_CACHE 101 + + +void Z_Init (void); +void *Z_Malloc (int size, int tag, void *ptr); +void Z_Free (void *ptr); +void Z_FreeTags (int lowtag, int hightag); +//void Z_DumpHeap (int lowtag, int hightag); +//void Z_FileDumpHeap (FILE *f); +void Z_CheckHeap (void); +void Z_ChangeTag2 (void *ptr, int tag); +//int Z_FreeMemory (void); + +typedef struct memblock_s +{ + int size; // including the header and possibly tiny fragments + void **user; // NULL if a free block + int tag; // purgelevel + int id; // should be ZONEID + struct memblock_s *next, *prev; +} memblock_t; + +#define Z_ChangeTag(p,t) \ +{ \ +if (( (memblock_t *)( (byte *)(p) - sizeof(memblock_t)))->id!=0x1d4a11) \ + I_Error("Z_CT at "__FILE__":%i",__LINE__); \ +Z_ChangeTag2(p,t); \ +}; + +//------- +//WADFILE +//------- +typedef struct +{ + char name[8]; + int handle,position,size; +} lumpinfo_t; + +extern lumpinfo_t *lumpinfo; +extern int numlumps; + +void W_InitMultipleFiles(char **filenames); +void W_OpenAuxiliary(char *filename); +void W_CloseAuxiliaryFile(void); +void W_CloseAuxiliary(void); +void W_UsePrimary(void); +void W_UseAuxiliary(void); +int W_CheckNumForName(char *name); +int W_GetNumForName(char *name); +int W_LumpLength(int lump); +void W_ReadLump(int lump, void *dest); +void *W_CacheLumpNum(int lump, int tag); +void *W_CacheLumpName(char *name, int tag); + +//---------- +//BASE LEVEL +//---------- +void H2_Main(void); +// not a globally visible function, just included for source reference +// calls all startup code +// parses command line options +// if not overrided, calls N_AdvanceDemo + +void H2_GameLoop(void); +// not a globally visible function, just included for source reference +// called by H2_Main, never exits +// manages timing and IO +// calls all ?_Responder, ?_Ticker, and ?_Drawer functions +// calls I_GetTime, I_StartFrame, and I_StartTic + +void H2_PostEvent(event_t *ev); +// called by IO functions when input is detected + +void NetUpdate (void); +// create any new ticcmds and broadcast to other players + +void D_QuitNetGame (void); +// broadcasts special packets to other players to notify of game exit + +void TryRunTics (void); + +//--------- +//SYSTEM IO +//--------- +#if 1 +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 +#else +#define SCREENWIDTH 560 +#define SCREENHEIGHT 375 +#endif + +byte *I_ZoneBase (int *size); +// called by startup code to get the ammount of memory to malloc +// for the zone management + +int I_GetTime (void); +// called by H2_GameLoop +// returns current time in tics + +void I_StartFrame (void); +// called by H2_GameLoop +// called before processing any tics in a frame (just after displaying a frame) +// time consuming syncronous operations are performed here (joystick reading) +// can call H2_PostEvent + +void I_StartTic (void); +// called by H2_GameLoop +// called before processing each tic in a frame +// quick syncronous operations are performed here +// can call H2_PostEvent + +// asyncronous interrupt functions should maintain private ques that are +// read by the syncronous functions to be converted into events + +void I_Init (void); +// called by H2_Main +// determines the hardware configuration and sets up the video mode + +void I_InitGraphics (void); + +void I_InitNetwork (void); +void I_NetCmd (void); + +void I_CheckExternDriver(void); + +void I_Error (char *error, ...); +// called by anything that can generate a terminal error +// bad exit with diagnostic message + +void I_Quit (void); +// called by M_Responder when quit is selected +// clean exit, displays sell blurb + +void I_SetPalette (byte *palette); +// takes full 8 bit values + +void I_Update(void); +// Copy buffer to video + +void I_WipeUpdate(wipe_t wipe); +// Copy buffer to video with wipe effect + +void I_WaitVBL(int count); +// wait for vertical retrace or pause a bit + +void I_BeginRead (void); +void I_EndRead (void); + +byte *I_AllocLow (int length); +// allocates from low memory under dos, just mallocs under unix + +void I_Tactile (int on, int off, int total); + +#ifdef __WATCOMC__ +extern boolean useexterndriver; + +#define EBT_FIRE 1 +#define EBT_OPENDOOR 2 +#define EBT_SPEED 4 +#define EBT_STRAFE 8 +#define EBT_MAP 0x10 +#define EBT_INVENTORYLEFT 0x20 +#define EBT_INVENTORYRIGHT 0x40 +#define EBT_USEARTIFACT 0x80 +#define EBT_FLYDROP 0x100 +#define EBT_CENTERVIEW 0x200 +#define EBT_PAUSE 0x400 +#define EBT_WEAPONCYCLE 0x800 +#define EBT_JUMP 0x1000 + +typedef struct +{ + short vector; // Interrupt vector + + signed char moveForward; // forward/backward (maxes at 50) + signed char moveSideways; // strafe (maxes at 24) + short angleTurn; // turning speed (640 [slow] 1280 [fast]) + short angleHead; // head angle (+2080 [left] : 0 [center] : -2048 [right]) + signed char pitch; // look up/down (-110 : +90) + signed char flyDirection; // flyheight (+1/-1) + unsigned short buttons; // EBT_* flags +} externdata_t; +#endif + +//---- +//GAME +//---- + +void G_DeathMatchSpawnPlayer (int playernum); + +void G_InitNew (skill_t skill, int episode, int map); + +void G_DeferedInitNew (skill_t skill, int episode, int map); +// can be called by the startup code or M_Responder +// a normal game starts at map 1, but a warp test can start elsewhere + +void G_DeferredNewGame(skill_t skill); + +void G_DeferedPlayDemo (char *demo); + +void G_LoadGame(int slot); +// can be called by the startup code or M_Responder +// calls P_SetupLevel or W_EnterWorld +void G_DoLoadGame (void); + +void G_SaveGame (int slot, char *description); +// called by M_Responder + +void G_RecordDemo (skill_t skill, int numplayers, int episode + , int map, char *name); +// only called by startup code + +void G_PlayDemo (char *name); +void G_TimeDemo (char *name); + +void G_TeleportNewMap(int map, int position); + +void G_Completed(int map, int position); +//void G_ExitLevel (void); +//void G_SecretExitLevel (void); + +void G_StartNewGame(skill_t skill); +void G_StartNewInit(void); + +void G_WorldDone (void); + +void G_Ticker (void); +boolean G_Responder (event_t *ev); + +void G_ScreenShot (void); + +//------- +//SV_SAVE +//------- + +#define HXS_VERSION_TEXT "HXS Ver 2.37" +#define HXS_VERSION_TEXT_LENGTH 16 +#define HXS_DESCRIPTION_LENGTH 24 + +void SV_SaveGame(int slot, char *description); +void SV_SaveMap(boolean savePlayers); +void SV_LoadGame(int slot); +void SV_MapTeleport(int map, int position); +void SV_LoadMap(void); +void SV_InitBaseSlot(void); +void SV_UpdateRebornSlot(void); +void SV_ClearRebornSlot(void); +boolean SV_RebornSlotAvailable(void); +int SV_GetRebornSlot(void); + +//----- +//PLAY +//----- + +void P_Ticker (void); +// called by C_Ticker +// can call G_PlayerExited +// carries out all thinking of monsters and players + +void P_SetupLevel (int episode, int map, int playermask, skill_t skill); +// called by W_Ticker + +void P_Init (void); +// called by startup code + +int P_GetMapCluster(int map); +int P_TranslateMap(int map); +int P_GetMapCDTrack(int map); +int P_GetMapWarpTrans(int map); +int P_GetMapNextMap(int map); +int P_GetMapSky1Texture(int map); +int P_GetMapSky2Texture(int map); +char *P_GetMapName(int map); +fixed_t P_GetMapSky1ScrollDelta(int map); +fixed_t P_GetMapSky2ScrollDelta(int map); +boolean P_GetMapDoubleSky(int map); +boolean P_GetMapLightning(int map); +boolean P_GetMapFadeTable(int map); +char *P_GetMapSongLump(int map); +void P_PutMapSongLump(int map, char *lumpName); +int P_GetCDStartTrack(void); +int P_GetCDEnd1Track(void); +int P_GetCDEnd2Track(void); +int P_GetCDEnd3Track(void); +int P_GetCDIntermissionTrack(void); +int P_GetCDTitleTrack(void); + +//------- +//REFRESH +//------- + +extern boolean setsizeneeded; + +extern boolean BorderNeedRefresh; +extern boolean BorderTopRefresh; + +extern int UpdateState; +// define the different areas for the dirty map +#define I_NOUPDATE 0 +#define I_FULLVIEW 1 +#define I_STATBAR 2 +#define I_MESSAGES 4 +#define I_FULLSCRN 8 + +void R_RenderPlayerView (player_t *player); +// called by G_Drawer + +void R_Init (void); +// called by startup code + +void R_DrawViewBorder (void); +void R_DrawTopBorder (void); +// if the view size is not full screen, draws a border around it + +void R_SetViewSize (int blocks, int detail); +// called by M_Responder + +int R_FlatNumForName (char *name); + +int R_TextureNumForName (char *name); +int R_CheckTextureNumForName (char *name); +// called by P_Ticker for switches and animations +// returns the texture number for the texture name + + +//---- +//MISC +//---- +extern int myargc; +extern char **myargv; +extern int localQuakeHappening[MAXPLAYERS]; + +int M_CheckParm(char *check); +// returns the position of the given parameter in the arg list (0 if not found) +boolean M_ParmExists(char *check); + +void M_ExtractFileBase(char *path, char *dest); + +void M_ForceUppercase(char *text); +// Changes a string to uppercase + +int M_Random (void); +// returns a number from 0 to 255 + +extern unsigned char rndtable[256]; +extern int prndindex; +#define P_Random() rndtable[(++prndindex)&0xff] +// as M_Random, but used only by the play simulation + +void M_ClearRandom (void); +// fix randoms for demos + +void M_FindResponseFile(void); + +void M_ClearBox (fixed_t *box); +void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y); +// bounding box functions + +boolean M_WriteFile(char const *name, void *source, int length); +int M_ReadFile(char const *name, byte **buffer); +int M_ReadFileCLib(char const *name, byte **buffer); + +void M_ScreenShot (void); + +void M_LoadDefaults(char *fileName); + +void M_SaveDefaults (void); + +int M_DrawText (int x, int y, boolean direct, char *string); + +//------------------------------ +// SC_man.c +//------------------------------ + +void SC_Open(char *name); +void SC_OpenLump(char *name); +void SC_OpenFile(char *name); +void SC_OpenFileCLib(char *name); +void SC_Close(void); +boolean SC_GetString(void); +void SC_MustGetString(void); +void SC_MustGetStringName(char *name); +boolean SC_GetNumber(void); +void SC_MustGetNumber(void); +void SC_UnGet(void); +//boolean SC_Check(void); +boolean SC_Compare(char *text); +int SC_MatchString(char **strings); +int SC_MustMatchString(char **strings); +void SC_ScriptError(char *message); + +extern char *sc_String; +extern int sc_Number; +extern int sc_Line; +extern boolean sc_End; +extern boolean sc_Crossed; +extern boolean sc_FileScripts; +extern char *sc_ScriptsDir; + +//------------------------------ +// SN_sonix.c +//------------------------------ + +enum +{ + SEQ_PLATFORM, + SEQ_PLATFORM_HEAVY, // same script as a normal platform + SEQ_PLATFORM_METAL, + SEQ_PLATFORM_CREAK, // same script as a normal platform + SEQ_PLATFORM_SILENCE, + SEQ_PLATFORM_LAVA, + SEQ_PLATFORM_WATER, + SEQ_PLATFORM_ICE, + SEQ_PLATFORM_EARTH, + SEQ_PLATFORM_METAL2, + SEQ_DOOR_STONE, + SEQ_DOOR_HEAVY, + SEQ_DOOR_METAL, + SEQ_DOOR_CREAK, + SEQ_DOOR_SILENCE, + SEQ_DOOR_LAVA, + SEQ_DOOR_WATER, + SEQ_DOOR_ICE, + SEQ_DOOR_EARTH, + SEQ_DOOR_METAL2, + SEQ_ESOUND_WIND, + SEQ_NUMSEQ +}; + +typedef enum +{ + SEQTYPE_STONE, + SEQTYPE_HEAVY, + SEQTYPE_METAL, + SEQTYPE_CREAK, + SEQTYPE_SILENCE, + SEQTYPE_LAVA, + SEQTYPE_WATER, + SEQTYPE_ICE, + SEQTYPE_EARTH, + SEQTYPE_METAL2, + SEQTYPE_NUMSEQ +} seqtype_t; + +void SN_InitSequenceScript(void); +void SN_StartSequence(mobj_t *mobj, int sequence); +void SN_StartSequenceName(mobj_t *mobj, char *name); +void SN_StopSequence(mobj_t *mobj); +void SN_UpdateActiveSequences(void); +void SN_StopAllSequences(void); +int SN_GetSequenceOffset(int sequence, int *sequencePtr); +void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume, + int currentSoundID); + + +typedef struct seqnode_s seqnode_t; +struct seqnode_s +{ + int *sequencePtr; + int sequence; + mobj_t *mobj; + int currentSoundID; + int delayTics; + int volume; + int stopSound; + seqnode_t *prev; + seqnode_t *next; +}; + +extern int ActiveSequences; +extern seqnode_t *SequenceListHead; + +//---------------------- +// Interlude (IN_lude.c) +//---------------------- + +#define MAX_INTRMSN_MESSAGE_SIZE 1024 + +extern boolean intermission; +extern char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE]; + +void IN_Start(void); +void IN_Ticker(void); +void IN_Drawer(void); + +//---------------------- +// Chat mode (CT_chat.c) +//---------------------- + +void CT_Init(void); +void CT_Drawer(void); +boolean CT_Responder(event_t *ev); +void CT_Ticker(void); +char CT_dequeueChatChar(void); + +extern boolean chatmodeon; + +//-------------------- +// Finale (F_finale.c) +//-------------------- + +void F_Drawer(void); +void F_Ticker(void); +void F_StartFinale(void); + +//---------------------- +// STATUS BAR (SB_bar.c) +//---------------------- + +extern int inv_ptr; +extern int curpos; +extern int SB_state; +void SB_Init(void); +void SB_SetClassData(void); +boolean SB_Responder(event_t *event); +void SB_Ticker(void); +void SB_Drawer(void); +void Draw_TeleportIcon(void); +void Draw_SaveIcon(void); +void Draw_LoadIcon(void); + +//----------------- +// MENU (MN_menu.c) +//----------------- + +void MN_Init(void); +void MN_ActivateMenu(void); +void MN_DeactivateMenu(void); +boolean MN_Responder(event_t *event); +void MN_Ticker(void); +void MN_Drawer(void); +void MN_DrTextA(char *text, int x, int y); +void MN_DrTextAYellow(char *text, int x, int y); +int MN_TextAWidth(char *text); +void MN_DrTextB(char *text, int x, int y); +int MN_TextBWidth(char *text); + +//------ +// VIDEO +//------ + +extern int dirtybox[4]; +extern byte gammatable[5][256]; +extern int usegamma; + +void V_Init(void); // Allocates buffer screens, call before R_Init +void V_DrawPatch(int x, int y, patch_t *patch); +void V_DrawFuzzPatch(int x, int y, patch_t *patch); +void V_DrawAltFuzzPatch(int x, int y, patch_t *patch); +void V_DrawShadowedPatch(int x, int y, patch_t *patch); +void V_DrawRawScreen(byte *raw); + +#include "sounds.h" + +#endif // __H2DEF__ diff --git a/src/hexen/i_cdmus.c b/src/hexen/i_cdmus.c new file mode 100644 index 00000000..dc2e91bc --- /dev/null +++ b/src/hexen/i_cdmus.c @@ -0,0 +1,1023 @@ + +//************************************************************************** +//** +//** i_cdmus.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include "h2def.h" +#include "i_sound.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_AUDIO_TRACKS 25 +#define MULTIPLEX_INT 0x2f +#define CDROM_GETDRIVECOUNT 0x1500 +#define CDROM_SENDDEVICEREQ 0x1510 +#define CDROM_GETVERSION 0x150c +#define HSG_MODE 0 +#define RED_MODE 1 +#define DRC_IOCTLINPUT 0x03 +#define DRC_IOCTLOUTPUT 0x0c +#define DRC_PLAYAUDIO 0x84 +#define DRC_STOPAUDIO 0x85 +#define DRC_RESUMEAUDIO 0x88 + +#define DPMI_INT 0x31 +#define DPMI_ALLOCREALMEM 0x0100 +#define DPMI_FREEREALMEM 0x0101 +#define DPMI_SIMREALINT 0x0300 + +// IOCTL input commands + +#define ADRDEVHEAD 0 // Return Address of Device Header +#define HEADLOCATION 1 // Location of Head +#define RESERVED 2 // Reserved +#define ERRSTATISTICS 3 // Error Statistics +#define AUDIOCHANINFO 4 // Audio Channel Info +#define READDRVBYTES 5 // Read Drive Bytes +#define DEVICESTATUS 6 // Device Status +#define GETSECTORSIZE 7 // Return Sector Size +#define GETVOLSIZE 8 // Return Volume Size +#define MEDIACHANGED 9 // Media Changed +#define AUDIODISKINFO 10 // Audio Disk Info +#define AUDIOTRACKINFO 11 // Audio Track Info +#define AUDIOQCHANINFO 12 // Audio Q-Channel Info +#define AUDIOSUBINFO 13 // Audio Sub-Channel Info +#define UPCCODE 14 // UPC Code +#define AUDIOSTATUSINFO 15 // Audio Status Info + +// IOCTL output commands + +#define EJECTDISK 0 // Eject Disk +#define DOORLOCK 1 // Lock/Unlock Door +#define RESETDRIVE 2 // Reset Drive +#define AUDIOCHANCONTROL 3 // Audio Channel Control +#define WRITEDEVCONTROL 4 // Write Device Control String +#define CLOSETRAY 5 // Close Tray + +// TYPES ------------------------------------------------------------------- + +typedef signed char S_BYTE; +typedef unsigned char U_BYTE; +typedef signed short S_WORD; +typedef unsigned short U_WORD; +typedef signed int S_LONG; +typedef unsigned int U_LONG; + +typedef struct { + U_LONG size; + void **address; + U_WORD *segment; + U_WORD *selector; +} DOSChunk_t; + +typedef struct { + U_LONG edi; + U_LONG esi; + U_LONG ebp; + U_LONG reserved; + U_LONG ebx; + U_LONG edx; + U_LONG ecx; + U_LONG eax; + U_WORD flags; + U_WORD es; + U_WORD ds; + U_WORD fs; + U_WORD gs; + U_WORD ip; + U_WORD cs; + U_WORD sp; + U_WORD ss; +} RegBlock_t; + +typedef struct { + short lengthMin; + short lengthSec; + int redStart; + int sectorStart; + int sectorLength; +} AudioTrack_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static U_WORD InputIOCTL(S_WORD request, U_LONG pctrlblk); +static U_WORD OutputIOCTL(S_WORD request, U_LONG pctrlblk); +static U_LONG RedToSectors(U_LONG red); +static int AllocIOCTLBuffers(void); +static void DPMI_SimRealInt(U_LONG intr, RegBlock_t *rBlock); +static void *DPMI_AllocRealMem(U_LONG size, U_WORD *segment, + U_WORD *selector); +static void DPMI_FreeRealMem(U_WORD selector); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int cd_Error; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int cd_DriveCount; +static int cd_FirstDrive; +static int cd_CurDrive; +static U_WORD cd_Version; +static int cd_FirstTrack; +static int cd_LastTrack; +static int cd_TrackCount; +static U_WORD cd_LeadOutMin; +static U_WORD cd_LeadOutSec; +static U_LONG cd_LeadOutRed; +static U_LONG cd_LeadOutSector; +static U_LONG cd_IOCTLBufferTotal; +static AudioTrack_t cd_AudioTracks[MAX_AUDIO_TRACKS]; + +static int OkInit = 0; + +static RegBlock_t RegBlock; + +static struct PlayReq_s { // CD-ROM Play Audio Device Request Struct + U_BYTE headerSize; + U_BYTE subUnitCode; + U_BYTE command; // = DRC_PLAYAUDIO + U_WORD status; + U_BYTE reserved[8]; + U_BYTE addressMode; + U_LONG startSector; + U_LONG numberToRead; +} *cd_PlayReq; +static U_WORD cd_PlayReqSeg; +static U_WORD cd_PlayReqSel; + +static struct StopReq_s { // CD-ROM Stop Audio Device Request Struct + U_BYTE headerSize; + U_BYTE subUnitCode; + U_BYTE command; // = DRC_STOPAUDIO + U_WORD status; + U_BYTE reserved[8]; +} *cd_StopReq; +static U_WORD cd_StopReqSeg; +static U_WORD cd_StopReqSel; + +static struct ResumeReq_s { // CD-ROM Resume Audio Device Request Struct + U_BYTE headerSize; + U_BYTE subUnitCode; + U_BYTE command; // = DRC_RESUMEAUDIO + U_WORD status; + U_BYTE reserved[8]; +} *cd_ResumeReq; +static U_WORD cd_ResumeReqSeg; +static U_WORD cd_ResumeReqSel; + +// IOCTL Input command data buffer structures + +static struct IOCTLIn_s { // IOCTL Input Struct + U_BYTE headerSize; + U_BYTE subUnitCode; + U_BYTE command; // = DRC_IOCTLINPUT + U_WORD status; + U_BYTE reserved[8]; + U_BYTE mediaDescriptor; + U_LONG ctrlBlkAddr; + U_WORD tranSize; + U_WORD startSector; + U_LONG volPtr; +} *cd_IOCTLIn; +static U_WORD cd_IOCTLInSeg; +static U_WORD cd_IOCTLInSel; + +static struct RAddrDevHead_s { + U_BYTE code; // ADRDEVHEAD + U_LONG devHdrAddr; // Address of device header +} *cd_RAddrDevHead; +static U_WORD cd_RAddrDevHeadSeg; +static U_WORD cd_RAddrDevHeadSel; + +static struct LocHead_s { + U_BYTE code; // HEADLOCATION + U_BYTE addrMode; // Addressing mode + U_LONG headLocation; // Location of drive head +} *cd_LocHead; +static U_WORD cd_LocHeadSeg; +static U_WORD cd_LocHeadSel; + +static struct ErrStat_s { + U_BYTE code; // ERRSTATISTICS + U_BYTE errVal; // Error statistics +} *cd_ErrStat; +static U_WORD cd_ErrStatSeg; +static U_WORD cd_ErrStatSel; + +static struct AudChanInfo_s { + U_BYTE code; // AUDIOCHANINFO + U_BYTE inChanOut0; // Input chan(0,1,2,or 3) for output chan 0 + U_BYTE volumeOut0; // Volume control (0-0xff) for output chan 0 + U_BYTE inChanOut1; // Input chan(0,1,2,or 3) for output chan 1 + U_BYTE volumeOut1; // Volume control (0-0xff) for output chan 1 + U_BYTE inChanOut2; // Input chan(0,1,2,or 3) for output chan 2 + U_BYTE volumeOut2; // Volume control (0-0xff) for output chan 2 + U_BYTE inChanOut3; // Input chan(0,1,2,or 3) for output chan 3 + U_BYTE volumeOut3; // Volume control (0-0xff) for output chan 3 +} *cd_AudChanInfo; +static U_WORD cd_AudChanInfoSeg; +static U_WORD cd_AudChanInfoSel; + +static struct RDrvBytes_s { + U_BYTE code; // READDRVBYTES + U_BYTE numBytes; // Number of bytes to read + U_BYTE rBuffer[128]; // Read buffer +} *cd_RDrvBytes; +static U_WORD cd_RDrvBytesSeg; +static U_WORD cd_RDrvBytesSel; + +static struct DevStat_s { + U_BYTE code; // DEVICESTATUS + U_LONG devParams; // Device parameters +} *cd_DevStat; +static U_WORD cd_DevStatSeg; +static U_WORD cd_DevStatSel; + +static struct SectSize_s { + U_BYTE code; // GETSECTORSIZE + U_BYTE readMode; // Read mode + U_WORD sectorSize; // Sector size +} *cd_SectSize; +static U_WORD cd_SectSizeSeg; +static U_WORD cd_SectSizeSel; + +static struct VolSize_s { + U_BYTE code; // GETVOLSIZE + U_LONG volumeSize; // Volume size +} *cd_VolSize; +static U_WORD cd_VolSizeSeg; +static U_WORD cd_VolSizeSel; + +static struct MedChng_s { + U_BYTE code; // MEDIACHANGED + U_BYTE changed; // Media byte +} *cd_MedChng; +static U_WORD cd_MedChngSeg; +static U_WORD cd_MedChngSel; + +static struct DiskInfo_s { + U_BYTE code; // AUDIODISKINFO + U_BYTE lowTrack; // Lowest track number + U_BYTE highTrack; // Highest track number + U_LONG startLeadOut; // Starting point of the lead-out track +} *cd_DiskInfo; +static U_WORD cd_DiskInfoSeg; +static U_WORD cd_DiskInfoSel; + +static struct TrackInfo_s { + U_BYTE code; // AUDIOTRACKINFO + U_BYTE track; // Track number + U_LONG start; // Starting point of the track + U_BYTE ctrlInfo; // Track control information +} *cd_TrackInfo; +static U_WORD cd_TrackInfoSeg; +static U_WORD cd_TrackInfoSel; + +static struct QInfo_s { + U_BYTE code; // AUDIOQCHANINFO + U_BYTE control; // CONTROL and ADR byte + U_BYTE tno; // Track number (TNO) + U_BYTE index; // (POINT) or Index(X) + U_BYTE min; // (MIN) Running time within a track + U_BYTE sec; // (SEC) " " " " " + U_BYTE frame; // (FRAME) " " " " " + U_BYTE zero; // (ZERO) " " " " " + U_BYTE aMin; // (AMIN) or (PMIN) Running time on disk + U_BYTE aSec; // (ASEC) or (PSEC) " " " " + U_BYTE aFrame; // (AFRAME) or (PFRAME)" " " " +} *cd_QInfo; +static U_WORD cd_QInfoSeg; +static U_WORD cd_QInfoSel; + +static struct SubChanInfo_s { + U_BYTE code; // AUDIOSUBINFO + U_LONG startSectAddr; // Starting sector address + U_LONG transAddr; // Transfer address + U_LONG numSects; // Number of sectors to read +} *cd_SubChanInfo; +static U_WORD cd_SubChanInfoSeg; +static U_WORD cd_SubChanInfoSel; + +static struct UPCCode_s { + U_BYTE code; // UPCCODE + U_BYTE control; // CONTROL and ADR byte + U_BYTE upc[7]; // UPC/EAN code + U_BYTE zero; // Zero + U_BYTE aFrame; // Aframe +} *cd_UPCCode; +static U_WORD cd_UPCCodeSeg; +static U_WORD cd_UPCCodeSel; + +static struct AudStat_s { + U_BYTE code; // AUDIOSTATUSINFO + U_WORD status; // Audio status bits + U_LONG startPlay; // Starting location of last Play/Resume + U_LONG endPlay; // Ending location for last Play/Resume +} *cd_AudStat; +static U_WORD cd_AudStatSeg; +static U_WORD cd_AudStatSel; + +// IOCTL Output command data buffer structures + +static struct IOCTLOut_s { // IOCTL Output struct + U_BYTE headerSize; + U_BYTE subUnitCode; + U_BYTE command; // = DRC_IOCTLOUTPUT + U_WORD status; + U_BYTE reserved[8]; + U_BYTE mediaDescriptor; + U_LONG ctrlBlkAddr; + U_WORD tranSize; + U_WORD startSector; + U_LONG volPtr; +} *cd_IOCTLOut; +static U_WORD cd_IOCTLOutSeg; +static U_WORD cd_IOCTLOutSel; + +static struct Eject_s { + U_BYTE code; // EJECTDISK +} *cd_Eject; +static U_WORD cd_EjectSeg; +static U_WORD cd_EjectSel; + +static struct LockDoor_s { + U_BYTE code; // DOORLOCK + U_BYTE lock; // Lock function : 0 = unlock, 1 = lock +} *cd_LockDoor; +static U_WORD cd_LockDoorSeg; +static U_WORD cd_LockDoorSel; + +static struct ResetDrv_s { + U_BYTE code; // RESETDRIVE +} *cd_ResetDrv; +static U_WORD cd_ResetDrvSeg; +static U_WORD cd_ResetDrvSel; + +static struct AudInfo_s { + U_BYTE code; // AUDIOCHANCONTROL + U_BYTE inChanOut0; // Input chan(0,1,2,or 3) for output chan 0 + U_BYTE volumeOut0; // Volume control (0-0xff) for output chan 0 + U_BYTE inChanOut1; // Input chan(0,1,2,or 3) for output chan 1 + U_BYTE volumeOut1; // Volume control (0-0xff) for output chan 1 + U_BYTE inChanOut2; // Input chan(0,1,2,or 3) for output chan 2 + U_BYTE volumeOut2; // Volume control (0-0xff) for output chan 2 + U_BYTE inChanOut3; // Input chan(0,1,2,or 3) for output chan 3 + U_BYTE volumeOut3; // Volume control (0-0xff) for output chan 3 +} *cd_AudInfo; +static U_WORD cd_AudInfoSeg; +static U_WORD cd_AudInfoSel; + +static struct WDrvBytes_s { + U_BYTE code; // WRITEDEVCONTROL + U_BYTE buf[5]; // Write buffer - size ?? +} *cd_WDrvBytes; +static U_WORD cd_WDrvBytesSeg; +static U_WORD cd_WDrvBytesSel; + +static struct CloseTray_s { + U_BYTE code; // CLOSETRAY +} *cd_CloseTray; +static U_WORD cd_CloseTraySeg; +static U_WORD cd_CloseTraySel; + +static U_WORD InCtrlBlkSize[16] = { + 0x05, 0x06, 0x00, 0x00, + 0x09, 0x82, 0x05, 0x04, + 0x05, 0x02, 0x07, 0x07, + 0x0b, 0x0d, 0x0b, 0x0b +}; + +static U_WORD OutCtrlBlkSize[6] = { + 0x01, 0x02, + 0x01, 0x09, + 0x06, 0x01 +}; + +// Structures for allocating conventional memory + +static DOSChunk_t DOSChunks[] = { + { + sizeof(struct PlayReq_s), + &cd_PlayReq, &cd_PlayReqSeg, &cd_PlayReqSel + }, + { + sizeof(struct StopReq_s), + &cd_StopReq, &cd_StopReqSeg, &cd_StopReqSel + }, + { + sizeof(struct ResumeReq_s), + &cd_ResumeReq, &cd_ResumeReqSeg, &cd_ResumeReqSel + }, + { + sizeof(struct IOCTLOut_s), + &cd_IOCTLOut, &cd_IOCTLOutSeg, &cd_IOCTLOutSel + }, + { + sizeof(struct Eject_s), + &cd_Eject, &cd_EjectSeg, &cd_EjectSel + }, + { + sizeof(struct LockDoor_s), + &cd_LockDoor, &cd_LockDoorSeg, &cd_LockDoorSel + }, + { + sizeof(struct ResetDrv_s), + &cd_ResetDrv, &cd_ResetDrvSeg, &cd_ResetDrvSel + }, + { + sizeof(struct AudInfo_s), + &cd_AudInfo, &cd_AudInfoSeg, &cd_AudInfoSel + }, + { + sizeof(struct WDrvBytes_s), + &cd_WDrvBytes, &cd_WDrvBytesSeg, &cd_WDrvBytesSel + }, + { + sizeof(struct CloseTray_s), + &cd_CloseTray, &cd_CloseTraySeg, &cd_CloseTraySel + }, + { + sizeof(struct IOCTLIn_s), + &cd_IOCTLIn, &cd_IOCTLInSeg, &cd_IOCTLInSel + }, + { + sizeof(struct RAddrDevHead_s), + &cd_RAddrDevHead, &cd_RAddrDevHeadSeg, &cd_RAddrDevHeadSel + }, + { + sizeof(struct LocHead_s), + &cd_LocHead, &cd_LocHeadSeg, &cd_LocHeadSel + }, + { + sizeof(struct ErrStat_s), + &cd_ErrStat, &cd_ErrStatSeg, &cd_ErrStatSel + }, + { + sizeof(struct AudChanInfo_s), + &cd_AudChanInfo, &cd_AudChanInfoSeg, &cd_AudChanInfoSel + }, + { + sizeof(struct RDrvBytes_s), + &cd_RDrvBytes, &cd_RDrvBytesSeg, &cd_RDrvBytesSel + }, + { + sizeof(struct DevStat_s), + &cd_DevStat, &cd_DevStatSeg, &cd_DevStatSel + }, + { + sizeof(struct SectSize_s), + &cd_SectSize, &cd_SectSizeSeg, &cd_SectSizeSel + }, + { + sizeof(struct VolSize_s), + &cd_VolSize, &cd_VolSizeSeg, &cd_VolSizeSel + }, + { + sizeof(struct MedChng_s), + &cd_MedChng, &cd_MedChngSeg, &cd_MedChngSel + }, + { + sizeof(struct DiskInfo_s), + &cd_DiskInfo, &cd_DiskInfoSeg, &cd_DiskInfoSel + }, + { + sizeof(struct TrackInfo_s), + &cd_TrackInfo, &cd_TrackInfoSeg, &cd_TrackInfoSel + }, + { + sizeof(struct QInfo_s), + &cd_QInfo, &cd_QInfoSeg, &cd_QInfoSel + }, + { + sizeof(struct SubChanInfo_s), + &cd_SubChanInfo, &cd_SubChanInfoSeg, &cd_SubChanInfoSel + }, + { + sizeof(struct UPCCode_s), + &cd_UPCCode, &cd_UPCCodeSeg, &cd_UPCCodeSel + }, + { + sizeof(struct AudStat_s), + &cd_AudStat, &cd_AudStatSeg, &cd_AudStatSel + }, + { + 0, NULL, NULL, NULL + } +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// I_CDMusInit +// +// Initializes the CD audio system. Must be called before using any +// other I_CDMus functions. +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +int I_CDMusInit(void) +{ + int i; + int sect; + int maxTrack; + S_BYTE startMin1 = 0; + S_BYTE startSec1 = 0; + S_BYTE startMin2 = 0; + S_BYTE startSec2 = 0; + S_BYTE lengthMin = 0; + S_BYTE lengthSec = 0; + + if(OkInit != 1) + { // Only execute if uninitialized + + // Get number of CD-ROM drives and first drive + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_GETDRIVECOUNT; + RegBlock.ebx = 0; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + cd_DriveCount = RegBlock.ebx; + + // MSCDEX not installed if number of drives = 0 + if(cd_DriveCount == 0) + { + cd_Error = CDERR_NOTINSTALLED; + return -1; + } + cd_FirstDrive = RegBlock.ecx; + cd_CurDrive = cd_FirstDrive; + + // Allocate the IOCTL buffers + if(AllocIOCTLBuffers() == -1) + { + cd_Error = CDERR_IOCTLBUFFMEM; + return -1; + } + + // Get MSCDEX version + // Major version in upper byte, minor version in lower byte + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_GETVERSION; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + cd_Version = RegBlock.ebx; + + // Check device status to make sure we can read Audio CD's + InputIOCTL(DEVICESTATUS, cd_DevStatSeg); + if((cd_DevStat->devParams & 0x0010) == 0) + { + cd_Error = CDERR_NOAUDIOSUPPORT; + return -1; + } + } + + // Force audio to stop playing + I_CDMusStop(); + + // Make sure we have the current TOC + InputIOCTL(MEDIACHANGED, cd_MedChngSeg); + + // Set track variables + InputIOCTL(AUDIODISKINFO, cd_DiskInfoSeg); + cd_FirstTrack = cd_DiskInfo->lowTrack; + cd_LastTrack = cd_DiskInfo->highTrack; + if(cd_FirstTrack == 0 && cd_FirstTrack == cd_LastTrack) + { + cd_Error = CDERR_NOAUDIOTRACKS; + return -1; + } + cd_TrackCount = cd_LastTrack-cd_FirstTrack+1; + cd_LeadOutMin = cd_DiskInfo->startLeadOut>>16 & 0xFF; + cd_LeadOutSec = cd_DiskInfo->startLeadOut>>8 & 0xFF; + cd_LeadOutRed = cd_DiskInfo->startLeadOut; + cd_LeadOutSector = RedToSectors(cd_DiskInfo->startLeadOut); + + // Create Red Book start, sector start, and sector length + // for all tracks + sect = cd_LeadOutSector; + for(i = cd_LastTrack; i >= cd_FirstTrack; i--) + { + cd_TrackInfo->track = i; + InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg); + if(i < MAX_AUDIO_TRACKS) + { + cd_AudioTracks[i].redStart = cd_TrackInfo->start; + cd_AudioTracks[i].sectorStart = + RedToSectors(cd_TrackInfo->start); + cd_AudioTracks[i].sectorLength = + sect-RedToSectors(cd_TrackInfo->start); + } + sect = RedToSectors(cd_TrackInfo->start); + } + + // Create track lengths in minutes and seconds + if(cd_LastTrack >= MAX_AUDIO_TRACKS) + { + maxTrack = MAX_AUDIO_TRACKS-1; + } + else + { + maxTrack = cd_LastTrack; + } + cd_TrackInfo->track = cd_FirstTrack; + InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg); + startMin1 = (cd_TrackInfo->start >> 16); + startSec1 = (cd_TrackInfo->start >> 8); + for(i = cd_FirstTrack; i <= maxTrack; i++) + { + cd_TrackInfo->track = i+1; + if(i < cd_LastTrack) + { + InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg); + startMin2 = (cd_TrackInfo->start >> 16); + startSec2 = (cd_TrackInfo->start >> 8); + } + else + { + startMin2 = cd_LeadOutRed>>16; + startSec2 = cd_LeadOutRed>>8; + } + lengthSec = startSec2 - startSec1; + lengthMin = startMin2 - startMin1; + if(lengthSec < 0) + { + lengthSec += 60; + lengthMin--; + } + cd_AudioTracks[i].lengthMin = lengthMin; + cd_AudioTracks[i].lengthSec = lengthSec; + startMin1 = startMin2; + startSec1 = startSec2; + } + + // Clip high tracks + cd_LastTrack = maxTrack; + + OkInit = 1; + return 0; +} + +//========================================================================== +// +// I_CDMusPlay +// +// Play an audio CD track. +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +int I_CDMusPlay(int track) +{ + int start; + int len; + + if(track < cd_FirstTrack || track > cd_LastTrack) + { + cd_Error = CDERR_BADTRACK; + return(-1); + } + I_CDMusStop(); + start = cd_AudioTracks[track].redStart; + len = cd_AudioTracks[track].sectorLength; + cd_PlayReq->addressMode = RED_MODE; + cd_PlayReq->startSector = start; + cd_PlayReq->numberToRead = len; + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_SENDDEVICEREQ; + RegBlock.ecx = cd_CurDrive; + RegBlock.ebx = 0; + RegBlock.es = cd_PlayReqSeg; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + if(cd_PlayReq->status&0x8000) + { + cd_Error = CDERR_DEVREQBASE+(cd_PlayReq->status)&0x00ff; + return(-1); + } + return(0); +} + +//========================================================================== +// +// I_CDMusStop +// +// Stops the playing of an audio CD. +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +int I_CDMusStop(void) +{ + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_SENDDEVICEREQ; + RegBlock.ecx = cd_CurDrive; + RegBlock.ebx = 0; + RegBlock.es = cd_StopReqSeg; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + if(cd_StopReq->status&0x8000) + { + cd_Error = CDERR_DEVREQBASE+(cd_StopReq->status)&0x00ff; + return -1; + } + return 0; +} + +//========================================================================== +// +// I_CDMusResume +// +// Resumes the playing of an audio CD. +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +int I_CDMusResume(void) +{ + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_SENDDEVICEREQ; + RegBlock.ecx = cd_CurDrive; + RegBlock.ebx = 0; + RegBlock.es = cd_ResumeReqSeg; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + if(cd_ResumeReq->status&0x8000) + { + cd_Error = CDERR_DEVREQBASE+(cd_ResumeReq->status)&0x00ff; + return -1; + } + return 0; +} + +//========================================================================== +// +// I_CDMusSetVolume +// +// Sets the CD audio volume (0 - 255). +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +int I_CDMusSetVolume(int volume) +{ + + if(!OkInit) + { + cd_Error = CDERR_NOTINSTALLED; + return -1; + } + + // Read current channel info + InputIOCTL(AUDIOCHANINFO, cd_AudChanInfoSeg); + + // Change the volumes + cd_AudChanInfo->volumeOut0 = + cd_AudChanInfo->volumeOut1 = + cd_AudChanInfo->volumeOut2 = + cd_AudChanInfo->volumeOut3 = volume; + + // Write modified channel info + OutputIOCTL(AUDIOCHANCONTROL, cd_AudChanInfoSeg); + + return 0; +} + +//========================================================================== +// +// I_CDMusFirstTrack +// +// Returns: the number of the first track. +// +//========================================================================== + +int I_CDMusFirstTrack(void) +{ + return cd_FirstTrack; +} + +//========================================================================== +// +// I_CDMusLastTrack +// +// Returns: the number of the last track. +// +//========================================================================== + +int I_CDMusLastTrack(void) +{ + return cd_LastTrack; +} + +//========================================================================== +// +// I_CDMusTrackLength +// +// Returns: Length of the given track in seconds, or -1 (error, in +// cd_Error). +// +//========================================================================== + +int I_CDMusTrackLength(int track) +{ + if(track < cd_FirstTrack || track > cd_LastTrack) + { + cd_Error = CDERR_BADTRACK; + return -1; + } + return cd_AudioTracks[track].lengthMin*60 + +cd_AudioTracks[track].lengthSec; +} + +//========================================================================== +// +// AllocIOCTLBuffers +// +// Allocates conventional memory for the IOCTL input and output buffers. +// Sets cd_IOCTLBufferTotal to the total allocated. +// +// Returns: 0 (ok) or -1 (error, in cd_Error). +// +//========================================================================== + +static int AllocIOCTLBuffers(void) +{ + int i; + int size; + DOSChunk_t *ck; + + cd_IOCTLBufferTotal = 0; + for(i = 0; DOSChunks[i].size != 0; i++) + { + ck = &DOSChunks[i]; + size = ck->size; + cd_IOCTLBufferTotal += (size+15)&0xfffffff0; + *ck->address = DPMI_AllocRealMem(size, ck->segment, ck->selector); + if(*ck->address == NULL) + { + return -1; + } + memset(*ck->address, 0, size); + } + cd_IOCTLIn->headerSize = sizeof(struct IOCTLIn_s); + cd_IOCTLIn->command = DRC_IOCTLINPUT; + cd_IOCTLOut->headerSize = sizeof(struct IOCTLOut_s); + cd_IOCTLOut->command = DRC_IOCTLOUTPUT; + cd_PlayReq->headerSize = sizeof(struct PlayReq_s); + cd_PlayReq->command = DRC_PLAYAUDIO; + cd_StopReq->headerSize = sizeof(struct StopReq_s); + cd_StopReq->command = DRC_STOPAUDIO; + cd_ResumeReq->headerSize = sizeof(struct ResumeReq_s); + cd_ResumeReq->command = DRC_RESUMEAUDIO; + return 0; +} + +//========================================================================== +// +// InputIOCTL +// +// Sends an IOCTL input device request command. +// +// Returns: the status of the request. +// +//========================================================================== + +static U_WORD InputIOCTL(S_LONG request, U_WORD buffSeg) +{ + U_BYTE *code; + + code = (U_BYTE *)(buffSeg<<4); + *code = (U_BYTE)request; + cd_IOCTLIn->ctrlBlkAddr = buffSeg<<16; + cd_IOCTLIn->tranSize = InCtrlBlkSize[request]; + memset(&RegBlock, 0, sizeof(RegBlock)); + RegBlock.eax = CDROM_SENDDEVICEREQ; + RegBlock.ecx = cd_CurDrive; + RegBlock.ebx = 0; + RegBlock.es = cd_IOCTLInSeg; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + return cd_IOCTLIn->status; +} + +//========================================================================== +// +// OutputIOCTL +// +// Sends an IOCTL output device request command. +// +// Returns: the status of the request. +// +//========================================================================== + +static U_WORD OutputIOCTL(S_LONG request, U_WORD buffSeg) +{ + U_BYTE *code; + + code = (U_BYTE *)(buffSeg<<4); + *code = (U_BYTE)request; + cd_IOCTLOut->ctrlBlkAddr = buffSeg<<16; + cd_IOCTLOut->tranSize = OutCtrlBlkSize[request]; + RegBlock.eax = CDROM_SENDDEVICEREQ; + RegBlock.ecx = cd_CurDrive; + RegBlock.ebx = 0; + RegBlock.es = cd_IOCTLOutSeg; + DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock); + return cd_IOCTLOut->status; +} + +//========================================================================== +// +// RedToSectors +// +// Converts Red Book addresses to HSG sectors. +// Sectors = Minutes * 60 * 75 + Seconds * 75 + Frame - 150 +// +// Returns: HSG sectors. +// +//========================================================================== + +static U_LONG RedToSectors(U_LONG red) +{ + U_LONG sector; + + sector = ((red&0x00ff0000) >> 16) * 60 * 75; + sector += ((red&0x0000ff00) >> 8) * 75; + sector += (red&0x000000ff); + return sector-150; +} + +//========================================================================== +// +// DPMI_SimRealInt +// +//========================================================================== + +static void DPMI_SimRealInt(U_LONG intr, RegBlock_t *rBlock) +{ + union REGS regs; + struct SREGS sRegs; + + regs.x.eax = DPMI_SIMREALINT; + regs.x.ebx = intr; + regs.x.ecx = 0; + regs.x.edi = FP_OFF((void far *)rBlock); + sRegs.es = FP_SEG((void far *)rBlock); + sRegs.ds = FP_SEG((void far *)rBlock); + int386x(DPMI_INT, ®s, ®s, &sRegs); +} + +//========================================================================== +// +// DPMI_AllocRealMem +// +//========================================================================== + +static void *DPMI_AllocRealMem(U_LONG size, U_WORD *segment, + U_WORD *selector) +{ + union REGS inRegs; + union REGS outRegs; + + inRegs.x.eax = DPMI_ALLOCREALMEM; + inRegs.x.ebx = (size+15)/16; + int386(DPMI_INT, &inRegs, &outRegs); + if(outRegs.x.cflag) + { + return NULL; + } + *segment = outRegs.x.eax&0xffff; + *selector = outRegs.x.edx&0xffff; + return (void *)(outRegs.x.eax<<4); +} + +//========================================================================== +// +// DPMI_FreeRealMem +// +//========================================================================== + +static void DPMI_FreeRealMem(U_WORD selector) +{ + union REGS regs; + + regs.x.eax = DPMI_FREEREALMEM; + regs.x.edx = selector; + int386(DPMI_INT, ®s, ®s); +} diff --git a/src/hexen/i_cdmus.h b/src/hexen/i_cdmus.h new file mode 100644 index 00000000..0c7d729a --- /dev/null +++ b/src/hexen/i_cdmus.h @@ -0,0 +1,26 @@ + +// i_cdmus.h + +#ifndef __ICDMUS__ +#define __ICDMUS__ + +#define CDERR_NOTINSTALLED 10 // MSCDEX not installed +#define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio +#define CDERR_NOAUDIOTRACKS 12 // Current CD has no audio tracks +#define CDERR_BADDRIVE 20 // Bad drive number +#define CDERR_BADTRACK 21 // Bad track number +#define CDERR_IOCTLBUFFMEM 22 // Not enough low memory for IOCTL +#define CDERR_DEVREQBASE 100 // DevReq errors + +extern int cd_Error; + +int I_CDMusInit(void); +int I_CDMusPlay(int track); +int I_CDMusStop(void); +int I_CDMusResume(void); +int I_CDMusSetVolume(int volume); +int I_CDMusFirstTrack(void); +int I_CDMusLastTrack(void); +int I_CDMusTrackLength(int track); + +#endif diff --git a/src/hexen/i_cyber.c b/src/hexen/i_cyber.c new file mode 100644 index 00000000..fc6cf240 --- /dev/null +++ b/src/hexen/i_cyber.c @@ -0,0 +1,277 @@ +// I_cyber.c + +#include +#include +#include +#include "st_start.h" // For ST_Message() + + +// Prototypes +unsigned char *I_AllocLow (int length); + + +/* +==================================================== + +Doom control structure + +The keybaord and joystick will add to the values set by the cyberman, +to a maximum of 0x19000 for forwardmove and sidemove. Angleturn is +not bounded at all. + +parm normal fast +----- ------ ---- +forwardmove 0xc800 0x19000 +sidemove 0xc000 0x14000 +angleturn 0x2800000 0x5000000 + +The keyboard and joystick have a 1/3 second slow turn of 0x1400000 under +normal speed to help aiming. + + + +==================================================== +*/ +/* old ticcmd_t +typedef struct +{ + char forwardmove; // *2048 for move + char sidemove; // *2048 for move + short angleturn; // <<16 for angle delta + short consistancy; // checks for net game + unsigned char chatchar; + unsigned char buttons; +} ticcmd_t; +*/ +// ticcmd_t as it appears in h2def.h +typedef struct +{ + char forwardmove; + char sidemove; + short angleturn; + short consistancy; + unsigned char chatchar; + unsigned char buttons; + unsigned char lookfly; + unsigned char arti; +}ticcmd_t; + + +#define BT_ATTACK 1 +#define BT_USE 2 +#define BT_CHANGE 4 // if true, the next 3 bits hold weapon num +#define BT_WEAPONMASK (8+16+32) +#define BT_WEAPONSHIFT 3 + +//================================================== +// +// CyberMan detection and usage info +// +//================================================== +#define DPMI_INT 0x31 +#define MOUSE_INT 0x33 + +#define DOSMEMSIZE 64 // enough for any SWIFT structure + +typedef struct { + short x; + short y; + short z; + short pitch; + short roll; + short yaw; + short buttons; +} SWIFT_3DStatus; + +// DPMI real mode interrupt structure +static struct rminfo { + long EDI; + long ESI; + long EBP; + long reserved_by_system; + long EBX; + long EDX; + long ECX; + long EAX; + short flags; + short ES,DS,FS,GS,IP,CS,SP,SS; +} RMI; + +typedef struct { + unsigned char deviceType; + unsigned char majorVersion; + unsigned char minorVersion; + unsigned char absRelFlags; + unsigned char centeringFlags; + unsigned char reserved[5]; +} StaticDeviceData; + +// values for deviceType: +#define DEVTYPE_CYBERMAN 1 + +short selector; +unsigned short segment; // segment of DOS memory block +SWIFT_3DStatus *cyberstat; +int isCyberPresent; // is CyberMan present? + + +static union REGS regs; +static struct SREGS sregs; + + +extern int mousepresent; + +//=========================================================== +// +// I_StartupCyberMan +// +// If a cyberman is present, init it and set isCyberPresent to 1 +//=========================================================== +void I_StartupCyberMan(void) +{ + StaticDeviceData *pbuf; + + ST_Message(" CyberMan: "); + isCyberPresent = 0; + + cyberstat = (SWIFT_3DStatus *)I_AllocLow (DOSMEMSIZE); + segment = (int)cyberstat>>4; + + pbuf = (StaticDeviceData *)cyberstat; + memset(pbuf, 0, sizeof (StaticDeviceData)); + + + // Use DPMI call 300h to issue mouse interrupt + memset(&RMI, 0, sizeof(RMI)); + RMI.EAX = 0x53C1; // SWIFT: Get Static Device Data + RMI.ES = segment; + RMI.EDX = 0; + memset(&sregs, 0, sizeof (sregs)); + regs.w.ax = 0x0300; // DPMI: simulate interrupt + regs.w.bx = MOUSE_INT; + regs.w.cx = 0; + regs.x.edi = FP_OFF(&RMI); + sregs.es = FP_SEG(&RMI); + int386x( DPMI_INT, ®s, ®s, &sregs ); + + if ((short)RMI.EAX != 1) + { + // SWIFT functions not present + ST_Message("Wrong mouse driver - no SWIFT support (AX=%04x).\n", + (unsigned)(short)RMI.EAX); + } + else if (pbuf->deviceType != DEVTYPE_CYBERMAN) + { + // no SWIFT device, or not CyberMan + if (pbuf->deviceType == 0) + { + ST_Message("no SWIFT device connected.\n"); + } + else + { + ST_Message("SWIFT device is not a CyberMan! (type=%d)\n", + pbuf->deviceType); + } + } + else + { + ST_Message("CyberMan %d.%02d connected.\n", + pbuf->majorVersion, pbuf->minorVersion); + isCyberPresent = 1; + mousepresent = 0; + } +} + + + +/* +=============== += += I_ReadCyberCmds += +=============== +*/ + + +int oldpos; + +void I_ReadCyberCmd (ticcmd_t *cmd) +{ + int delta; + + // Use DPMI call 300h to issue mouse interrupt + memset(&RMI, 0, sizeof(RMI)); + RMI.EAX = 0x5301; // SWIFT: Get Position and Buttons + RMI.ES = segment; + RMI.EDX = 0; + memset(&sregs, 0, sizeof (sregs)); + regs.w.ax = 0x0300; // DPMI: simulate interrupt + regs.w.bx = MOUSE_INT; + regs.w.cx = 0; + regs.x.edi = FP_OFF(&RMI); + sregs.es = FP_SEG(&RMI); + int386x( DPMI_INT, ®s, ®s, &sregs ); + + if (cyberstat->y < -7900) + cmd->forwardmove = 0xc800/2048; + else if (cyberstat->y > 7900) + cmd->forwardmove = -0xc800/2048; + + if (cyberstat->buttons & 4) + cmd->buttons |= BT_ATTACK; + if (cyberstat->buttons & 2) + cmd->buttons |= BT_USE; + + delta = cyberstat->x - oldpos; + oldpos = cyberstat->x; + + if (cyberstat->buttons & 1) + { // strafe + if (cyberstat->x < -7900) + cmd->sidemove = -0xc800/2048; + else if (cyberstat->x > 7900) + cmd->sidemove = 0xc800/2048; + else + cmd->sidemove = delta*40/2048; + } + else + { + if (cyberstat->x < -7900) + cmd->angleturn = 0x280; + else if (cyberstat->x > 7900) + cmd->angleturn = -0x280; + else + cmd->angleturn = -delta*0xa/16; + + } + +} + + +void I_Tactile (int on, int off, int total) +{ + if (!isCyberPresent) + return; + + on /= 5; + off /= 5; + total /= 40; + if (on > 255) + on = 255; + if (off > 255) + off = 255; + if (total > 255) + total = 255; + + memset(&RMI, 0, sizeof(RMI)); + RMI.EAX = 0x5330; // SWIFT: Get Position and Buttons + RMI.EBX = on*256+off; + RMI.ECX = total; + memset(&sregs, 0, sizeof (sregs)); + regs.w.ax = 0x0300; // DPMI: simulate interrupt + regs.w.bx = MOUSE_INT; + regs.w.cx = 0; + regs.x.edi = FP_OFF(&RMI); + sregs.es = FP_SEG(&RMI); + int386x( DPMI_INT, ®s, ®s, &sregs ); +} diff --git a/src/hexen/i_header.h b/src/hexen/i_header.h new file mode 100644 index 00000000..f07034db --- /dev/null +++ b/src/hexen/i_header.h @@ -0,0 +1,77 @@ +#ifndef __I_HEADER_H__ +#define __I_HEADER_H__ + +#include "h2def.h" + +//-------- +//SOUND IO +//-------- +#define FREQ_LOW 0x40 +#define FREQ_NORM 0x80 +#define FREQ_HIGH 0xff + +void I_SetMasterVolume(int volume); + +void I_TurnOffSfx(void); +void I_TurnOnSfx(void); +void I_TurnOffMusic(void); +void I_TurnOnMusic(void); + +// MUSIC I/O +// + +int I_RegisterSong(void *songdata); +// called by anything that wants to register a song lump with the sound lib +// calls Paul's function of the similar name to register music only. +// note that the song data is the same for any sound card and is paul's +// MUS format. Returns a handle which will be passed to all other music +// functions. + +void I_UnregisterSong(int handle); +// called by anything which is finished with a song and no longer needs +// the sound library to be aware of it. All songs should be stopped +// before calling this, but it will double check and stop it if necessary. + +void I_LoopSong(int handle); +// called by anything that wishes to start music. +// plays a song, and when the song is done, starts playing it again in +// an endless loop. the start is faded in over three seconds. + +void I_FadeOutSong(int handle, int fotime); +// called by anything that wishes to stop music. +// fades out the song over milliseconds. + +void I_StopSong(int handle); +// called by anything that wishes to stop music. +// stops a song abruptly. + +// SFX I/O +// + +void *I_GetSoundEffect (char *soundname); +// called by routines which wish to play a sound effect at some later +// time. Pass it the lump name of a sound effect WITHOUT the sfx +// prefix. This means the maximum name length is 7 letters/digits. +// The prefixes for different sound cards are 'S','M','A', and 'P'. +// They refer to the card type. The routine will cache in the +// appropriate sound effect when it is played. + +void I_UngetSoundEffect (void *soundset); +// called by routines which wish to no longer use the sounds at all +// frees up the associated structure. It stops any currently playing +// sound effects. + +void I_StartSound (channel_t *c, int vol, int sep, int pitch, int priority); +// Starts a sound in a particular sound channel + +void I_UpdateSoundParams(channel_t *c, int vol, int sep, int pitch); +// Updates the volume, separation, and pitch of a sound channel + +void I_StopSound(channel_t *c); +// Stops a sound channel + +int I_SoundIsPlaying(channel_t *c); +// called by S_*()'s to see if a channel is still playing. Returns 0 +// if no longer playing, 1 if playing. + +#endif diff --git a/src/hexen/i_ibm.c b/src/hexen/i_ibm.c new file mode 100644 index 00000000..20ed3220 --- /dev/null +++ b/src/hexen/i_ibm.c @@ -0,0 +1,2782 @@ + +//************************************************************************** +//** +//** I_IBM.C +//** +//************************************************************************** + +#include +#include +#include +#include +#include +#include "h2def.h" +#include "r_local.h" +#include "p_local.h" // for P_AproxDistance +#include "sounds.h" +#include "i_sound.h" +#include "soundst.h" +#include "st_start.h" +#include "dmx.h" +#include "dpmiapi.h" + +// Macros + +#define DEFAULT_ARCHIVEPATH "o:\\sound\\archive\\" +#define PRIORITY_MAX_ADJUST 10 +#define DIST_ADJUST (MAX_SND_DIST/PRIORITY_MAX_ADJUST) + +#define DPMI_INT 0x31 + +#define SEQ_ADDR 0x3C4 +#define SEQ_DATA 0x3C5 +#define REG_MAPMASK 0x02 + +#define MASK_PLANE0 0x01 +#define MASK_PLANE1 0x02 +#define MASK_PLANE2 0x04 +#define MASK_PLANE3 0x08 + +#define P0OFFSET 38400*0 +#define P1OFFSET 38400*1 +#define P2OFFSET 38400*2 +#define P3OFFSET 38400*3 + +#define VID_INT 0x10 +#define VB_SYNC {while(!(inp(0x3da)&8)); while(inp(0x3da)&8);} +#define BITPLANE(p) (outp(SEQ_ADDR,REG_MAPMASK),outp(SEQ_DATA,(p))) + +//#define NOKBD +//#define NOTIMER + +// Public Data + +int DisplayTicker = 0; + +// Code + +void main(int argc, char **argv) +{ + myargc = argc; + myargv = argv; + H2_Main(); +} + +void I_StartupNet (void); +void I_ShutdownNet (void); +void I_ReadExternDriver(void); + +typedef struct +{ + unsigned edi, esi, ebp, reserved, ebx, edx, ecx, eax; + unsigned short flags, es, ds, fs, gs, ip, cs, sp, ss; +} dpmiregs_t; + +extern dpmiregs_t dpmiregs; + +void I_ReadMouse (void); + +extern int usemouse, usejoystick; + +extern void **lumpcache; + +int i_Vector; +externdata_t *i_ExternData; +boolean useexterndriver; + +boolean i_CDMusic; +int i_CDTrack; +int i_CDCurrentTrack; +int i_CDMusicLength; +int oldTic; + +/* +=============================================================================== + + MUSIC & SFX API + +=============================================================================== +*/ + +//static channel_t channel[MAX_CHANNELS]; + +//static int rs; //the current registered song. +//int mus_song = -1; +//int mus_lumpnum; +//void *mus_sndptr; +//byte *soundCurve; + +extern sfxinfo_t S_sfx[]; +extern musicinfo_t S_music[]; + +static channel_t Channel[MAX_CHANNELS]; +static int RegisteredSong; //the current registered song. +static int NextCleanup; +static boolean MusicPaused; +static int Mus_Song = -1; +static int Mus_LumpNum; +static void *Mus_SndPtr; +static byte *SoundCurve; + +static boolean UseSndScript; +static char ArchivePath[128]; + +extern int snd_MusicDevice; +extern int snd_SfxDevice; +extern int snd_MaxVolume; +extern int snd_MusicVolume; +extern int snd_Channels; + +extern int startepisode; +extern int startmap; + +// int AmbChan; + +//========================================================================== +// +// S_Start +// +//========================================================================== + +void S_Start(void) +{ + S_StopAllSound(); + S_StartSong(gamemap, true); +} + +//========================================================================== +// +// S_StartSong +// +//========================================================================== + +void S_StartSong(int song, boolean loop) +{ + char *songLump; + int track; + + if(i_CDMusic) + { // Play a CD track, instead + if(i_CDTrack) + { // Default to the player-chosen track + track = i_CDTrack; + } + else + { + track = P_GetMapCDTrack(gamemap); + } + if(track == i_CDCurrentTrack && i_CDMusicLength > 0) + { + return; + } + if(!I_CDMusPlay(track)) + { + if(loop) + { + i_CDMusicLength = 35*I_CDMusTrackLength(track); + oldTic = gametic; + } + else + { + i_CDMusicLength = -1; + } + i_CDCurrentTrack = track; + } + } + else + { + if(song == Mus_Song) + { // don't replay an old song + return; + } + if(RegisteredSong) + { + I_StopSong(RegisteredSong); + I_UnRegisterSong(RegisteredSong); + if(UseSndScript) + { + Z_Free(Mus_SndPtr); + } + else + { + Z_ChangeTag(lumpcache[Mus_LumpNum], PU_CACHE); + } + #ifdef __WATCOMC__ + _dpmi_unlockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size); + #endif + RegisteredSong = 0; + } + songLump = P_GetMapSongLump(song); + if(!songLump) + { + return; + } + if(UseSndScript) + { + char name[128]; + sprintf(name, "%s%s.lmp", ArchivePath, songLump); + M_ReadFile(name, (byte **)&Mus_SndPtr); + } + else + { + Mus_LumpNum = W_GetNumForName(songLump); + Mus_SndPtr = W_CacheLumpNum(Mus_LumpNum, PU_MUSIC); + } + #ifdef __WATCOMC__ + _dpmi_lockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size); + #endif + RegisteredSong = I_RegisterSong(Mus_SndPtr); + I_PlaySong(RegisteredSong, loop); // 'true' denotes endless looping. + Mus_Song = song; + } +} + +//========================================================================== +// +// S_StartSongName +// +//========================================================================== + +void S_StartSongName(char *songLump, boolean loop) +{ + int cdTrack; + + if(!songLump) + { + return; + } + if(i_CDMusic) + { + cdTrack = 0; + + if(!strcmp(songLump, "hexen")) + { + cdTrack = P_GetCDTitleTrack(); + } + else if(!strcmp(songLump, "hub")) + { + cdTrack = P_GetCDIntermissionTrack(); + } + else if(!strcmp(songLump, "hall")) + { + cdTrack = P_GetCDEnd1Track(); + } + else if(!strcmp(songLump, "orb")) + { + cdTrack = P_GetCDEnd2Track(); + } + else if(!strcmp(songLump, "chess") && !i_CDTrack) + { + cdTrack = P_GetCDEnd3Track(); + } +/* Uncomment this, if Kevin writes a specific song for startup + else if(!strcmp(songLump, "start")) + { + cdTrack = P_GetCDStartTrack(); + } +*/ + if(!cdTrack || (cdTrack == i_CDCurrentTrack && i_CDMusicLength > 0)) + { + return; + } + if(!I_CDMusPlay(cdTrack)) + { + if(loop) + { + i_CDMusicLength = 35*I_CDMusTrackLength(cdTrack); + oldTic = gametic; + } + else + { + i_CDMusicLength = -1; + } + i_CDCurrentTrack = cdTrack; + i_CDTrack = false; + } + } + else + { + if(RegisteredSong) + { + I_StopSong(RegisteredSong); + I_UnRegisterSong(RegisteredSong); + if(UseSndScript) + { + Z_Free(Mus_SndPtr); + } + else + { + Z_ChangeTag(lumpcache[Mus_LumpNum], PU_CACHE); + } + #ifdef __WATCOMC__ + _dpmi_unlockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size); + #endif + RegisteredSong = 0; + } + if(UseSndScript) + { + char name[128]; + sprintf(name, "%s%s.lmp", ArchivePath, songLump); + M_ReadFile(name, (byte **)&Mus_SndPtr); + } + else + { + Mus_LumpNum = W_GetNumForName(songLump); + Mus_SndPtr = W_CacheLumpNum(Mus_LumpNum, PU_MUSIC); + } + #ifdef __WATCOMC__ + _dpmi_lockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size); + #endif + RegisteredSong = I_RegisterSong(Mus_SndPtr); + I_PlaySong(RegisteredSong, loop); // 'true' denotes endless looping. + Mus_Song = -1; + } +} + +//========================================================================== +// +// S_GetSoundID +// +//========================================================================== + +int S_GetSoundID(char *name) +{ + int i; + + for(i = 0; i < NUMSFX; i++) + { + if(!strcmp(S_sfx[i].tagName, name)) + { + return i; + } + } + return 0; +} + +//========================================================================== +// +// S_StartSound +// +//========================================================================== + +void S_StartSound(mobj_t *origin, int sound_id) +{ + S_StartSoundAtVolume(origin, sound_id, 127); +} + +//========================================================================== +// +// S_StartSoundAtVolume +// +//========================================================================== + +void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume) +{ + int dist, vol; + int i; + int priority; + int sep; + int angle; + int absx; + int absy; + + static int sndcount = 0; + int chan; + + if(sound_id == 0 || snd_MaxVolume == 0) + return; + if(origin == NULL) + { + origin = players[displayplayer].mo; + } + if(volume == 0) + { + return; + } + + // calculate the distance before other stuff so that we can throw out + // sounds that are beyond the hearing range. + absx = abs(origin->x-players[displayplayer].mo->x); + absy = abs(origin->y-players[displayplayer].mo->y); + dist = absx+absy-(absx > absy ? absy>>1 : absx>>1); + dist >>= FRACBITS; + if(dist >= MAX_SND_DIST) + { + return; // sound is beyond the hearing range... + } + if(dist < 0) + { + dist = 0; + } + priority = S_sfx[sound_id].priority; + priority *= (PRIORITY_MAX_ADJUST-(dist/DIST_ADJUST)); + if(!S_StopSoundID(sound_id, priority)) + { + return; // other sounds have greater priority + } + for(i=0; iplayer) + { + i = snd_Channels; + break; // let the player have more than one sound. + } + if(origin == Channel[i].mo) + { // only allow other mobjs one sound + S_StopSound(Channel[i].mo); + break; + } + } + if(i >= snd_Channels) + { + for(i = 0; i < snd_Channels; i++) + { + if(Channel[i].mo == NULL) + { + break; + } + } + if(i >= snd_Channels) + { + // look for a lower priority sound to replace. + sndcount++; + if(sndcount >= snd_Channels) + { + sndcount = 0; + } + for(chan = 0; chan < snd_Channels; chan++) + { + i = (sndcount+chan)%snd_Channels; + if(priority >= Channel[i].priority) + { + chan = -1; //denote that sound should be replaced. + break; + } + } + if(chan != -1) + { + return; //no free channels. + } + else //replace the lower priority sound. + { + if(Channel[i].handle) + { + if(I_SoundIsPlaying(Channel[i].handle)) + { + I_StopSound(Channel[i].handle); + } + if(S_sfx[Channel[i].sound_id].usefulness > 0) + { + S_sfx[Channel[i].sound_id].usefulness--; + } + } + } + } + } + if(S_sfx[sound_id].lumpnum == 0) + { + S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]); + } + if(S_sfx[sound_id].snd_ptr == NULL) + { + if(UseSndScript) + { + char name[128]; + sprintf(name, "%s%s.lmp", ArchivePath, S_sfx[sound_id].lumpname); + M_ReadFile(name, (byte **)&S_sfx[sound_id].snd_ptr); + } + else + { + S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum, + PU_SOUND); + } + #ifdef __WATCOMC__ + _dpmi_lockregion(S_sfx[sound_id].snd_ptr, + lumpinfo[S_sfx[sound_id].lumpnum].size); + #endif + } + + vol = (SoundCurve[dist]*(snd_MaxVolume*8)*volume)>>14; + if(origin == players[displayplayer].mo) + { + sep = 128; +// vol = (volume*(snd_MaxVolume+1)*8)>>7; + } + else + { + angle = R_PointToAngle2(players[displayplayer].mo->x, + players[displayplayer].mo->y, Channel[i].mo->x, Channel[i].mo->y); + angle = (angle-viewangle)>>24; + sep = angle*2-128; + if(sep < 64) + sep = -sep; + if(sep > 192) + sep = 512-sep; +// vol = SoundCurve[dist]; + } + + if(S_sfx[sound_id].changePitch) + { + Channel[i].pitch = (byte)(127+(M_Random()&7)-(M_Random()&7)); + } + else + { + Channel[i].pitch = 127; + } + Channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, vol, + sep, Channel[i].pitch, 0); + Channel[i].mo = origin; + Channel[i].sound_id = sound_id; + Channel[i].priority = priority; + Channel[i].volume = volume; + if(S_sfx[sound_id].usefulness < 0) + { + S_sfx[sound_id].usefulness = 1; + } + else + { + S_sfx[sound_id].usefulness++; + } +} + +//========================================================================== +// +// S_StopSoundID +// +//========================================================================== + +boolean S_StopSoundID(int sound_id, int priority) +{ + int i; + int lp; //least priority + int found; + + if(S_sfx[sound_id].numchannels == -1) + { + return(true); + } + lp = -1; //denote the argument sound_id + found = 0; + for(i=0; i= Channel[i].priority) + { // if we're gonna kill one, then this'll be it + lp = i; + priority = Channel[i].priority; + } + } + } + if(found < S_sfx[sound_id].numchannels) + { + return(true); + } + else if(lp == -1) + { + return(false); // don't replace any sounds + } + if(Channel[lp].handle) + { + if(I_SoundIsPlaying(Channel[lp].handle)) + { + I_StopSound(Channel[lp].handle); + } + if(S_sfx[Channel[lp].sound_id].usefulness > 0) + { + S_sfx[Channel[lp].sound_id].usefulness--; + } + Channel[lp].mo = NULL; + } + return(true); +} + +//========================================================================== +// +// S_StopSound +// +//========================================================================== + +void S_StopSound(mobj_t *origin) +{ + int i; + + for(i=0;i 0) + { + S_sfx[Channel[i].sound_id].usefulness--; + } + Channel[i].handle = 0; + Channel[i].mo = NULL; + } + } +} + +//========================================================================== +// +// S_StopAllSound +// +//========================================================================== + +void S_StopAllSound(void) +{ + int i; + + //stop all sounds + for(i=0; i < snd_Channels; i++) + { + if(Channel[i].handle) + { + S_StopSound(Channel[i].mo); + } + } + memset(Channel, 0, 8*sizeof(channel_t)); +} + +//========================================================================== +// +// S_SoundLink +// +//========================================================================== + +void S_SoundLink(mobj_t *oldactor, mobj_t *newactor) +{ + int i; + + for(i=0;iid == 0x1d4a11) + { // taken directly from the Z_ChangeTag macro + Z_ChangeTag2(lumpcache[S_sfx[i].lumpnum], + PU_CACHE); +#ifdef __WATCOMC__ + _dpmi_unlockregion(S_sfx[i].snd_ptr, + lumpinfo[S_sfx[i].lumpnum].size); +#endif + } + } + S_sfx[i].usefulness = -1; + S_sfx[i].snd_ptr = NULL; + } + } + } + NextCleanup = gametic+35*30; // every 30 seconds + } + for(i=0;i 0) + { + S_sfx[Channel[i].sound_id].usefulness--; + } + Channel[i].handle = 0; + Channel[i].mo = NULL; + Channel[i].sound_id = 0; + } + if(Channel[i].mo == NULL || Channel[i].sound_id == 0 + || Channel[i].mo == listener) + { + continue; + } + else + { + absx = abs(Channel[i].mo->x-listener->x); + absy = abs(Channel[i].mo->y-listener->y); + dist = absx+absy-(absx > absy ? absy>>1 : absx>>1); + dist >>= FRACBITS; + + if(dist >= MAX_SND_DIST) + { + S_StopSound(Channel[i].mo); + continue; + } + if(dist < 0) + { + dist = 0; + } + //vol = SoundCurve[dist]; + vol = (SoundCurve[dist]*(snd_MaxVolume*8)*Channel[i].volume)>>14; + if(Channel[i].mo == listener) + { + sep = 128; + } + else + { + angle = R_PointToAngle2(listener->x, listener->y, + Channel[i].mo->x, Channel[i].mo->y); + angle = (angle-viewangle)>>24; + sep = angle*2-128; + if(sep < 64) + sep = -sep; + if(sep > 192) + sep = 512-sep; + } + I_UpdateSoundParams(Channel[i].handle, vol, sep, + Channel[i].pitch); + priority = S_sfx[Channel[i].sound_id].priority; + priority *= PRIORITY_MAX_ADJUST-(dist/DIST_ADJUST); + Channel[i].priority = priority; + } + } +} + +//========================================================================== +// +// S_Init +// +//========================================================================== + +void S_Init(void) +{ + SoundCurve = W_CacheLumpName("SNDCURVE", PU_STATIC); +// SoundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL); + I_StartupSound(); + if(snd_Channels > 8) + { + snd_Channels = 8; + } + I_SetChannels(snd_Channels); + I_SetMusicVolume(snd_MusicVolume); + + // Attempt to setup CD music + if(snd_MusicDevice == snd_CDMUSIC) + { + ST_Message(" Attempting to initialize CD Music: "); + if(!cdrom) + { + i_CDMusic = (I_CDMusInit() != -1); + } + else + { // The user is trying to use the cdrom for both game and music + i_CDMusic = false; + } + if(i_CDMusic) + { + ST_Message("initialized.\n"); + } + else + { + ST_Message("failed.\n"); + } + } +} + +//========================================================================== +// +// S_GetChannelInfo +// +//========================================================================== + +void S_GetChannelInfo(SoundInfo_t *s) +{ + int i; + ChanInfo_t *c; + + s->channelCount = snd_Channels; + s->musicVolume = snd_MusicVolume; + s->soundVolume = snd_MaxVolume; + for(i = 0; i < snd_Channels; i++) + { + c = &s->chan[i]; + c->id = Channel[i].sound_id; + c->priority = Channel[i].priority; + c->name = S_sfx[c->id].lumpname; + c->mo = Channel[i].mo; + c->distance = P_AproxDistance(c->mo->x-viewx, c->mo->y-viewy) + >>FRACBITS; + } +} + +//========================================================================== +// +// S_GetSoundPlayingInfo +// +//========================================================================== + +boolean S_GetSoundPlayingInfo(mobj_t *mobj, int sound_id) +{ + int i; + + for(i = 0; i < snd_Channels; i++) + { + if(Channel[i].sound_id == sound_id && Channel[i].mo == mobj) + { + if(I_SoundIsPlaying(Channel[i].handle)) + { + return true; + } + } + } + return false; +} + +//========================================================================== +// +// S_SetMusicVolume +// +//========================================================================== + +void S_SetMusicVolume(void) +{ + if(i_CDMusic) + { + I_CDMusSetVolume(snd_MusicVolume*16); // 0-255 + } + else + { + I_SetMusicVolume(snd_MusicVolume); + } + if(snd_MusicVolume == 0) + { + if(!i_CDMusic) + { + I_PauseSong(RegisteredSong); + } + MusicPaused = true; + } + else if(MusicPaused) + { + if(!i_CDMusic) + { + I_ResumeSong(RegisteredSong); + } + MusicPaused = false; + } +} + +//========================================================================== +// +// S_ShutDown +// +//========================================================================== + +void S_ShutDown(void) +{ + extern int tsm_ID; + if(tsm_ID != -1) + { + I_StopSong(RegisteredSong); + I_UnRegisterSong(RegisteredSong); + I_ShutdownSound(); + } + if(i_CDMusic) + { + I_CDMusStop(); + } +} + +//========================================================================== +// +// S_InitScript +// +//========================================================================== + +void S_InitScript(void) +{ + int p; + int i; + + strcpy(ArchivePath, DEFAULT_ARCHIVEPATH); + if(!(p = M_CheckParm("-devsnd"))) + { + UseSndScript = false; + SC_OpenLump("sndinfo"); + } + else + { + UseSndScript = true; + SC_OpenFile(myargv[p+1]); + } + while(SC_GetString()) + { + if(*sc_String == '$') + { + if(!stricmp(sc_String, "$ARCHIVEPATH")) + { + SC_MustGetString(); + strcpy(ArchivePath, sc_String); + } + else if(!stricmp(sc_String, "$MAP")) + { + SC_MustGetNumber(); + SC_MustGetString(); + if(sc_Number) + { + P_PutMapSongLump(sc_Number, sc_String); + } + } + continue; + } + else + { + for(i = 0; i < NUMSFX; i++) + { + if(!strcmp(S_sfx[i].tagName, sc_String)) + { + SC_MustGetString(); + if(*sc_String != '?') + { + strcpy(S_sfx[i].lumpname, sc_String); + } + else + { + strcpy(S_sfx[i].lumpname, "default"); + } + break; + } + } + if(i == NUMSFX) + { + SC_MustGetString(); + } + } + } + SC_Close(); + + for(i = 0; i < NUMSFX; i++) + { + if(!strcmp(S_sfx[i].lumpname, "")) + { + strcpy(S_sfx[i].lumpname, "default"); + } + } +} + +//========================================================================== +// +// I_UpdateCDMusic +// +// Updates playing time for current track, and restarts the track, if +// needed +// +//========================================================================== + +void I_UpdateCDMusic(void) +{ + extern boolean MenuActive; + + if(MusicPaused || i_CDMusicLength < 0 + || (paused && !MenuActive)) + { // Non-looping song/song paused + return; + } + i_CDMusicLength -= gametic-oldTic; + oldTic = gametic; + if(i_CDMusicLength <= 0) + { + S_StartSong(gamemap, true); + } +} + +/* +============================================================================ + + CONSTANTS + +============================================================================ +*/ + +#define SC_INDEX 0x3C4 +#define SC_RESET 0 +#define SC_CLOCK 1 +#define SC_MAPMASK 2 +#define SC_CHARMAP 3 +#define SC_MEMMODE 4 + +#define CRTC_INDEX 0x3D4 +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISPEND 1 +#define CRTC_H_BLANK 2 +#define CRTC_H_ENDBLANK 3 +#define CRTC_H_RETRACE 4 +#define CRTC_H_ENDRETRACE 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_ROWSCAN 8 +#define CRTC_MAXSCANLINE 9 +#define CRTC_CURSORSTART 10 +#define CRTC_CURSOREND 11 +#define CRTC_STARTHIGH 12 +#define CRTC_STARTLOW 13 +#define CRTC_CURSORHIGH 14 +#define CRTC_CURSORLOW 15 +#define CRTC_V_RETRACE 16 +#define CRTC_V_ENDRETRACE 17 +#define CRTC_V_DISPEND 18 +#define CRTC_OFFSET 19 +#define CRTC_UNDERLINE 20 +#define CRTC_V_BLANK 21 +#define CRTC_V_ENDBLANK 22 +#define CRTC_MODE 23 +#define CRTC_LINECOMPARE 24 + + +#define GC_INDEX 0x3CE +#define GC_SETRESET 0 +#define GC_ENABLESETRESET 1 +#define GC_COLORCOMPARE 2 +#define GC_DATAROTATE 3 +#define GC_READMAP 4 +#define GC_MODE 5 +#define GC_MISCELLANEOUS 6 +#define GC_COLORDONTCARE 7 +#define GC_BITMASK 8 + +#define ATR_INDEX 0x3c0 +#define ATR_MODE 16 +#define ATR_OVERSCAN 17 +#define ATR_COLORPLANEENABLE 18 +#define ATR_PELPAN 19 +#define ATR_COLORSELECT 20 + +#define STATUS_REGISTER_1 0x3da + +#define PEL_WRITE_ADR 0x3c8 +#define PEL_READ_ADR 0x3c7 +#define PEL_DATA 0x3c9 +#define PEL_MASK 0x3c6 + +// boolean grmode; + +//================================================== +// +// joystick vars +// +//================================================== + +boolean joystickpresent; +extern unsigned joystickx, joysticky; +boolean I_ReadJoystick (void); // returns false if not connected + + +//================================================== + +#define VBLCOUNTER 34000 // hardware tics to a frame + + +#define TIMERINT 8 +#define KEYBOARDINT 9 + +#define CRTCOFF (_inbyte(STATUS_REGISTER_1)&1) +#define CLI _disable() +#define STI _enable() + +#define _outbyte(x,y) (outp(x,y)) +#define _outhword(x,y) (outpw(x,y)) + +#define _inbyte(x) (inp(x)) +#define _inhword(x) (inpw(x)) + +#define MOUSEB1 1 +#define MOUSEB2 2 +#define MOUSEB3 4 + +boolean mousepresent; +//static int tsm_ID = -1; // tsm init flag + +//=============================== + +int ticcount; + +// REGS stuff used for int calls +union REGS regs; +struct SREGS segregs; + +boolean novideo; // if true, stay in text mode for debugging + +#define KBDQUESIZE 32 +byte keyboardque[KBDQUESIZE]; +int kbdtail, kbdhead; + +#define KEY_LSHIFT 0xfe + +#define KEY_INS (0x80+0x52) +#define KEY_DEL (0x80+0x53) +#define KEY_PGUP (0x80+0x49) +#define KEY_PGDN (0x80+0x51) +#define KEY_HOME (0x80+0x47) +#define KEY_END (0x80+0x4f) + +#define SC_RSHIFT 0x36 +#define SC_LSHIFT 0x2a + +byte scantokey[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', KEY_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13 , KEY_RCTRL,'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + 39 , '`', KEY_LSHIFT,92, 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT,'*', + KEY_RALT,' ', 0 , KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, // 3 + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0 , 0 , KEY_HOME, + KEY_UPARROW,KEY_PGUP,'-',KEY_LEFTARROW,'5',KEY_RIGHTARROW,'+',KEY_END, //4 + KEY_DOWNARROW,KEY_PGDN,KEY_INS,KEY_DEL,0,0, 0, KEY_F11, + KEY_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 + }; + +//========================================================================== + +//-------------------------------------------------------------------------- +// +// FUNC I_GetTime +// +// Returns time in 1/35th second tics. +// +//-------------------------------------------------------------------------- + +int I_GetTime (void) +{ +#ifdef NOTIMER + ticcount++; +#endif + return(ticcount); +} + +//-------------------------------------------------------------------------- +// +// PROC I_ColorBorder +// +//-------------------------------------------------------------------------- + +/* +void I_ColorBorder(void) +{ + int i; + + I_WaitVBL(1); + _outbyte(PEL_WRITE_ADR, 0); + for(i = 0; i < 3; i++) + { + _outbyte(PEL_DATA, 63); + } +} +*/ + +//-------------------------------------------------------------------------- +// +// PROC I_UnColorBorder +// +//-------------------------------------------------------------------------- + +/* +void I_UnColorBorder(void) +{ + int i; + + I_WaitVBL(1); + _outbyte(PEL_WRITE_ADR, 0); + for(i = 0; i < 3; i++) + { + _outbyte(PEL_DATA, 0); + } +} +*/ + +/* +============================================================================ + + USER INPUT + +============================================================================ +*/ + +//-------------------------------------------------------------------------- +// +// PROC I_WaitVBL +// +//-------------------------------------------------------------------------- + +void I_WaitVBL(int vbls) +{ + int stat; + + if(novideo) + { + return; + } + while(vbls--) + { + do + { + stat = inp(STATUS_REGISTER_1); + if(stat&8) + { + break; + } + } while(1); + do + { + stat = inp(STATUS_REGISTER_1); + if((stat&8) == 0) + { + break; + } + } while(1); + } +} + +//-------------------------------------------------------------------------- +// +// PROC I_SetPalette +// +// Palette source must use 8 bit RGB elements. +// +//-------------------------------------------------------------------------- + +void I_SetPalette(byte *palette) +{ + int i; + + if(novideo) + { + return; + } + I_WaitVBL(1); + _outbyte(PEL_WRITE_ADR, 0); + for(i = 0; i < 768; i++) + { + _outbyte(PEL_DATA, (gammatable[usegamma][*palette++])>>2); + } +} + +/* +============================================================================ + + GRAPHICS MODE + +============================================================================ +*/ + +byte *pcscreen, *destscreen, *destview; + + +/* +============== += += I_Update += +============== +*/ + +int UpdateState; +extern int screenblocks; + +void I_Update (void) +{ + int i; + byte *dest; + int tics; + static int lasttic; + +// +// blit screen to video +// + if(DisplayTicker) + { + if(screenblocks > 9 || UpdateState&(I_FULLSCRN|I_MESSAGES)) + { + dest = (byte *)screen; + } + else + { + dest = (byte *)pcscreen; + } + tics = ticcount-lasttic; + lasttic = ticcount; + if(tics > 20) + { + tics = 20; + } + for(i = 0; i < tics; i++) + { + *dest = 0xff; + dest += 2; + } + for(i = tics; i < 20; i++) + { + *dest = 0x00; + dest += 2; + } + } + + //memset(pcscreen, 255, SCREENHEIGHT*SCREENWIDTH); + + if(UpdateState == I_NOUPDATE) + { + return; + } + if(UpdateState&I_FULLSCRN) + { + memcpy(pcscreen, screen, SCREENWIDTH*SCREENHEIGHT); + UpdateState = I_NOUPDATE; // clear out all draw types + } + if(UpdateState&I_FULLVIEW) + { + if(UpdateState&I_MESSAGES && screenblocks > 7) + { + for(i = 0; i < + (viewwindowy+viewheight)*SCREENWIDTH; i += SCREENWIDTH) + { + memcpy(pcscreen+i, screen+i, SCREENWIDTH); + } + UpdateState &= ~(I_FULLVIEW|I_MESSAGES); + } + else + { + for(i = viewwindowy*SCREENWIDTH+viewwindowx; i < + (viewwindowy+viewheight)*SCREENWIDTH; i += SCREENWIDTH) + { + memcpy(pcscreen+i, screen+i, viewwidth); + } + UpdateState &= ~I_FULLVIEW; + } + } + if(UpdateState&I_STATBAR) + { + memcpy(pcscreen+SCREENWIDTH*(SCREENHEIGHT-SBARHEIGHT), + screen+SCREENWIDTH*(SCREENHEIGHT-SBARHEIGHT), + SCREENWIDTH*SBARHEIGHT); + UpdateState &= ~I_STATBAR; + } + if(UpdateState&I_MESSAGES) + { + memcpy(pcscreen, screen, SCREENWIDTH*28); + UpdateState &= ~I_MESSAGES; + } + + +// memcpy(pcscreen, screen, SCREENHEIGHT*SCREENWIDTH); +} + +//-------------------------------------------------------------------------- +// +// PROC I_InitGraphics +// +//-------------------------------------------------------------------------- + +void I_InitGraphics(void) +{ + if(novideo) + { + return; + } + //grmode = true; + regs.w.ax = 0x13; + int386(0x10, (const union REGS *)®s, ®s); + pcscreen = destscreen = (byte *)0xa0000; + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); +} + +//-------------------------------------------------------------------------- +// +// PROC I_ShutdownGraphics +// +//-------------------------------------------------------------------------- + +void I_ShutdownGraphics(void) +{ + byte mode; + + // don't reset mode if it didn't get set + mode = *(byte *)0x449; + if(mode == 0x12 || mode == 0x13) + { + regs.w.ax = 3; + int386(0x10, ®s, ®s); // back to text mode + } +} + +//-------------------------------------------------------------------------- +// +// PROC I_ReadScreen +// +// Reads the screen currently displayed into a linear buffer. +// +//-------------------------------------------------------------------------- + +/* +void I_ReadScreen(byte *scr) +{ + memcpy(scr, screen, SCREENWIDTH*SCREENHEIGHT); +} +*/ + +//=========================================================================== + +/* +=================== += += I_StartTic += +// called by D_DoomLoop +// called before processing each tic in a frame +// can call D_PostEvent +// asyncronous interrupt functions should maintain private ques that are +// read by the syncronous functions to be converted into events +=================== +*/ + +/* + OLD STARTTIC STUFF + +void I_StartTic (void) +{ + int k; + event_t ev; + + + I_ReadMouse (); + +// +// keyboard events +// + while (kbdtail < kbdhead) + { + k = keyboardque[kbdtail&(KBDQUESIZE-1)]; + +// if (k==14) +// I_Error ("exited"); + + kbdtail++; + + // extended keyboard shift key bullshit + if ( (k&0x7f)==KEY_RSHIFT ) + { + if ( keyboardque[(kbdtail-2)&(KBDQUESIZE-1)]==0xe0 ) + continue; + k &= 0x80; + k |= KEY_RSHIFT; + } + + if (k==0xe0) + continue; // special / pause keys + if (keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0xe1) + continue; // pause key bullshit + + if (k==0xc5 && keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0x9d) + { + ev.type = ev_keydown; + ev.data1 = KEY_PAUSE; + D_PostEvent (&ev); + continue; + } + + if (k&0x80) + ev.type = ev_keyup; + else + ev.type = ev_keydown; + k &= 0x7f; + + ev.data1 = k; + //ev.data1 = scantokey[k]; + + D_PostEvent (&ev); + } +} +*/ + +#define SC_UPARROW 0x48 +#define SC_DOWNARROW 0x50 +#define SC_LEFTARROW 0x4b +#define SC_RIGHTARROW 0x4d + +void I_StartTic (void) +{ + int k; + event_t ev; + + + I_ReadMouse (); + +// +// keyboard events +// + while (kbdtail < kbdhead) + { + k = keyboardque[kbdtail&(KBDQUESIZE-1)]; + kbdtail++; + + // extended keyboard shift key bullshit + if ( (k&0x7f)==SC_LSHIFT || (k&0x7f)==SC_RSHIFT ) + { + if ( keyboardque[(kbdtail-2)&(KBDQUESIZE-1)]==0xe0 ) + continue; + k &= 0x80; + k |= SC_RSHIFT; + } + + if (k==0xe0) + continue; // special / pause keys + if (keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0xe1) + continue; // pause key bullshit + + if (k==0xc5 && keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0x9d) + { + ev.type = ev_keydown; + ev.data1 = KEY_PAUSE; + H2_PostEvent (&ev); + continue; + } + + if (k&0x80) + ev.type = ev_keyup; + else + ev.type = ev_keydown; + k &= 0x7f; + switch (k) + { + case SC_UPARROW: + ev.data1 = KEY_UPARROW; + break; + case SC_DOWNARROW: + ev.data1 = KEY_DOWNARROW; + break; + case SC_LEFTARROW: + ev.data1 = KEY_LEFTARROW; + break; + case SC_RIGHTARROW: + ev.data1 = KEY_RIGHTARROW; + break; + default: + ev.data1 = scantokey[k]; + break; + } + H2_PostEvent (&ev); + } + +} + + +/* +void I_ReadKeys (void) +{ + int k; + + + while (1) + { + while (kbdtail < kbdhead) + { + k = keyboardque[kbdtail&(KBDQUESIZE-1)]; + kbdtail++; + printf ("0x%x\n",k); + if (k == 1) + I_Quit (); + } + } +} +*/ + +/* +=============== += += I_StartFrame += +=============== +*/ + +void I_StartFrame (void) +{ + I_JoystickEvents (); + if(useexterndriver) + { + DPMIInt(i_Vector); + } +} + +/* +============================================================================ + + TIMER INTERRUPT + +============================================================================ +*/ + +/* +void I_ColorBlack (int r, int g, int b) +{ +_outbyte (PEL_WRITE_ADR,0); +_outbyte(PEL_DATA,r); +_outbyte(PEL_DATA,g); +_outbyte(PEL_DATA,b); +} +*/ + + +/* +================ += += I_TimerISR += +================ +*/ + +int I_TimerISR (void) +{ + ticcount++; + return 0; +} + +/* +============================================================================ + + KEYBOARD + +============================================================================ +*/ + +void (__interrupt __far *oldkeyboardisr) () = NULL; + +int lastpress; + +/* +================ += += I_KeyboardISR += +================ +*/ + +void __interrupt I_KeyboardISR (void) +{ +// Get the scan code + + keyboardque[kbdhead&(KBDQUESIZE-1)] = lastpress = _inbyte(0x60); + kbdhead++; + +// acknowledge the interrupt + + _outbyte(0x20,0x20); +} + + + +/* +=============== += += I_StartupKeyboard += +=============== +*/ + +void I_StartupKeyboard (void) +{ +#ifndef NOKBD + oldkeyboardisr = _dos_getvect(KEYBOARDINT); + _dos_setvect (0x8000 | KEYBOARDINT, I_KeyboardISR); +#endif + +//I_ReadKeys (); +} + + +void I_ShutdownKeyboard (void) +{ + if (oldkeyboardisr) + _dos_setvect (KEYBOARDINT, oldkeyboardisr); + *(short *)0x41c = *(short *)0x41a; // clear bios key buffer +} + + + +/* +============================================================================ + + MOUSE + +============================================================================ +*/ + + +int I_ResetMouse (void) +{ + regs.w.ax = 0; // reset + int386 (0x33, ®s, ®s); + return regs.w.ax; +} + + + +/* +================ += += StartupMouse += +================ +*/ + +void I_StartupCyberMan(void); + +void I_StartupMouse (void) +{ + // + // General mouse detection + // + mousepresent = 0; + if ( M_CheckParm ("-nomouse") || !usemouse ) + return; + + if (I_ResetMouse () != 0xffff) + { + ST_Message("Mouse: not present\n"); + return; + } + ST_Message("Mouse: detected\n"); + + mousepresent = 1; + + I_StartupCyberMan(); +} + + +/* +================ += += ShutdownMouse += +================ +*/ + +void I_ShutdownMouse (void) +{ + if (!mousepresent) + return; + + I_ResetMouse (); +} + + +/* +================ += += I_ReadMouse += +================ +*/ + +void I_ReadMouse (void) +{ + event_t ev; + +// +// mouse events +// + if (!mousepresent) + return; + + ev.type = ev_mouse; + + memset (&dpmiregs,0,sizeof(dpmiregs)); + dpmiregs.eax = 3; // read buttons / position + DPMIInt (0x33); + ev.data1 = dpmiregs.ebx; + + dpmiregs.eax = 11; // read counters + DPMIInt (0x33); + ev.data2 = (short)dpmiregs.ecx; + ev.data3 = -(short)dpmiregs.edx; + + H2_PostEvent (&ev); +} + +/* +============================================================================ + + JOYSTICK + +============================================================================ +*/ + +int joyxl, joyxh, joyyl, joyyh; + +boolean WaitJoyButton (void) +{ + int oldbuttons, buttons; + + oldbuttons = 0; + do + { + I_WaitVBL (1); + buttons = ((inp(0x201) >> 4)&1)^1; + if (buttons != oldbuttons) + { + oldbuttons = buttons; + continue; + } + + if ( (lastpress& 0x7f) == 1 ) + { + joystickpresent = false; + return false; + } + } while ( !buttons); + + do + { + I_WaitVBL (1); + buttons = ((inp(0x201) >> 4)&1)^1; + if (buttons != oldbuttons) + { + oldbuttons = buttons; + continue; + } + + if ( (lastpress& 0x7f) == 1 ) + { + joystickpresent = false; + return false; + } + } while ( buttons); + + return true; +} + + + +/* +=============== += += I_StartupJoystick += +=============== +*/ + +int basejoyx, basejoyy; + +void I_StartupJoystick (void) +{ + int centerx, centery; + + joystickpresent = 0; + if ( M_CheckParm ("-nojoy") || !usejoystick ) + return; + + if (!I_ReadJoystick ()) + { + joystickpresent = false; + ST_Message("joystick not found\n "); + return; + } + ST_Message("joystick found\n"); + joystickpresent = true; + + ST_RealMessage("CENTER the joystick and press button 1:"); + if (!WaitJoyButton ()) + return; + I_ReadJoystick (); + centerx = joystickx; + centery = joysticky; + + ST_RealMessage("\nPush the joystick to the UPPER LEFT corner and press button 1:"); + if (!WaitJoyButton ()) + return; + I_ReadJoystick (); + joyxl = (centerx + joystickx)/2; + joyyl = (centerx + joysticky)/2; + + ST_RealMessage("\nPush the joystick to the LOWER RIGHT corner and press button 1:"); + if (!WaitJoyButton ()) + return; + I_ReadJoystick (); + joyxh = (centerx + joystickx)/2; + joyyh = (centery + joysticky)/2; + ST_RealMessage("\n"); +} + +/* +=============== += += I_JoystickEvents += +=============== +*/ + +void I_JoystickEvents (void) +{ + event_t ev; + +// +// joystick events +// + if (!joystickpresent) + return; + + I_ReadJoystick (); + ev.type = ev_joystick; + ev.data1 = ((inp(0x201) >> 4)&15)^15; + + if (joystickx < joyxl) + ev.data2 = -1; + else if (joystickx > joyxh) + ev.data2 = 1; + else + ev.data2 = 0; + if (joysticky < joyyl) + ev.data3 = -1; + else if (joysticky > joyyh) + ev.data3 = 1; + else + ev.data3 = 0; + + H2_PostEvent (&ev); +} + + + +/* +============================================================================ + + DPMI STUFF + +============================================================================ +*/ + +#define REALSTACKSIZE 1024 + +dpmiregs_t dpmiregs; + +unsigned realstackseg; + +void I_DivException (void); +int I_SetDivException (void); + +/* +void DPMIFarCall (void) +{ + segread (&segregs); + regs.w.ax = 0x301; + regs.w.bx = 0; + regs.w.cx = 0; + regs.x.edi = (unsigned)&dpmiregs; + segregs.es = segregs.ds; + int386x( DPMI_INT, ®s, ®s, &segregs ); +} +*/ + +void DPMIInt (int i) +{ + dpmiregs.ss = realstackseg; + dpmiregs.sp = REALSTACKSIZE-4; + + segread (&segregs); + regs.w.ax = 0x300; + regs.w.bx = i; + regs.w.cx = 0; + regs.x.edi = (unsigned)&dpmiregs; + segregs.es = segregs.ds; + int386x( DPMI_INT, ®s, ®s, &segregs ); +} + + +/* +============== += += I_StartupDPMI += +============== +*/ + +void I_StartupDPMI (void) +{ +// extern char __begtext; +// extern char ___argc; +// int n,d; + +// +// allocate a decent stack for real mode ISRs +// + realstackseg = (int)I_AllocLow (1024) >> 4; + +// +// lock the entire program down +// + +// _dpmi_lockregion (&__begtext, &___argc - &__begtext); + + +// +// catch divide by 0 exception +// +#if 0 + segread(&segregs); + regs.w.ax = 0x0203; // DPMI set processor exception handler vector + regs.w.bx = 0; // int 0 + regs.w.cx = segregs.cs; + regs.x.edx = (int)&I_DivException; + printf ("%x : %x\n",regs.w.cx, regs.x.edx); + int386( DPMI_INT, ®s, ®s); +#endif + +#if 0 + n = I_SetDivException (); + printf ("return: %i\n",n); + n = 100; + d = 0; + printf ("100 / 0 = %i\n",n/d); + +exit (1); +#endif +} + + + +/* +============================================================================ + + TIMER INTERRUPT + +============================================================================ +*/ + +void (__interrupt __far *oldtimerisr) (); + + +/* +void IO_ColorBlack (int r, int g, int b) +{ +_outbyte (PEL_WRITE_ADR,0); +_outbyte(PEL_DATA,r); +_outbyte(PEL_DATA,g); +_outbyte(PEL_DATA,b); +} +*/ + + +/* +================ += += IO_TimerISR += +================ +*/ + +//void __interrupt IO_TimerISR (void) + +void __interrupt __far IO_TimerISR (void) +{ + ticcount++; + _outbyte(0x20,0x20); // Ack the interrupt +} + +/* +===================== += += IO_SetTimer0 += += Sets system timer 0 to the specified speed += +===================== +*/ + +void IO_SetTimer0(int speed) +{ + if (speed > 0 && speed < 150) + I_Error ("INT_SetTimer0: %i is a bad value",speed); + + _outbyte(0x43,0x36); // Change timer 0 + _outbyte(0x40,speed); + _outbyte(0x40,speed >> 8); +} + + + +/* +=============== += += IO_StartupTimer += +=============== +*/ + +/* +void IO_StartupTimer (void) +{ + oldtimerisr = _dos_getvect(TIMERINT); + + _dos_setvect (0x8000 | TIMERINT, IO_TimerISR); + IO_SetTimer0 (VBLCOUNTER); +} +*/ + +void IO_ShutdownTimer (void) +{ + if (oldtimerisr) + { + IO_SetTimer0 (0); // back to 18.4 ips + _dos_setvect (TIMERINT, oldtimerisr); + } +} + +//=========================================================================== + + +/* +=============== += += I_Init += += hook interrupts and set graphics mode += +=============== +*/ + +void I_Init (void) +{ + extern void I_StartupTimer(void); + + novideo = M_CheckParm("novideo"); + ST_Message(" I_StartupDPMI\n"); + I_StartupDPMI(); + ST_Message(" I_StartupMouse "); + I_StartupMouse(); +// tprintf("I_StartupJoystick ",1); +// I_StartupJoystick(); +// tprintf("I_StartupKeyboard ",1); +// I_StartupKeyboard(); + ST_Message(" S_Init... "); + S_Init(); + //IO_StartupTimer(); + S_Start(); +} + + +/* +=============== += += I_Shutdown += += return to default system state += +=============== +*/ + +void I_Shutdown (void) +{ + I_ShutdownGraphics (); + IO_ShutdownTimer (); + S_ShutDown (); + I_ShutdownMouse (); + I_ShutdownKeyboard (); + + IO_SetTimer0 (0); +} + + +/* +================ += += I_Error += +================ +*/ + +void I_Error (char *error, ...) +{ + union REGS regs; + + va_list argptr; + + D_QuitNetGame (); + I_Shutdown (); + va_start (argptr,error); + regs.x.eax = 0x3; + int386(0x10, ®s, ®s); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + exit (1); +} + +//-------------------------------------------------------------------------- +// +// I_Quit +// +// Shuts down net game, saves defaults, prints the exit text message, +// goes to text mode, and exits. +// +//-------------------------------------------------------------------------- + +void I_Quit(void) +{ + D_QuitNetGame(); + M_SaveDefaults(); + I_Shutdown(); + +// scr = (byte *)W_CacheLumpName("ENDTEXT", PU_CACHE); +/* + memcpy((void *)0xb8000, scr, 80*25*2); + regs.w.ax = 0x0200; + regs.h.bh = 0; + regs.h.dl = 0; + regs.h.dh = 23; + int386(0x10, (const union REGS *)®s, ®s); // Set text pos + _settextposition(24, 1); +*/ + printf("\nHexen: Beyond Heretic\n"); + exit(0); +} + +/* +=============== += += I_ZoneBase += +=============== +*/ + +byte *I_ZoneBase (int *size) +{ + int meminfo[32]; + int heap; + byte *ptr; + + memset (meminfo,0,sizeof(meminfo)); + segread(&segregs); + segregs.es = segregs.ds; + regs.w.ax = 0x500; // get memory info + regs.x.edi = (int)&meminfo; + int386x( 0x31, ®s, ®s, &segregs ); + + heap = meminfo[0]; + ST_Message (" DPMI memory: 0x%x, ",heap); + ST_Message ("Maxzone: 0x%x\n", maxzone); + + do + { + heap -= 0x10000; // leave 64k alone + if (heap > maxzone) + heap = maxzone; + ptr = malloc (heap); + } while (!ptr); + + ST_Message (" 0x%x allocated for zone, ", heap); + ST_Message ("ZoneBase: 0x%X\n", (int)ptr); + + if (heap < 0x180000) + I_Error (" Insufficient DPMI memory!"); +#if 0 + regs.w.ax = 0x501; // allocate linear block + regs.w.bx = heap>>16; + regs.w.cx = heap&0xffff; + int386( 0x31, ®s, ®s); + if (regs.w.cflag) + I_Error (" Couldn't allocate DPMI memory!"); + + block = (regs.w.si << 16) + regs.w.di; +#endif + + *size = heap; + return ptr; +} + +/* +============= += += I_AllocLow += +============= +*/ + +byte *I_AllocLow (int length) +{ + byte *mem; + + // DPMI call 100h allocates DOS memory + segread(&segregs); + regs.w.ax = 0x0100; // DPMI allocate DOS memory + regs.w.bx = (length+15) / 16; + int386( DPMI_INT, ®s, ®s); +// segment = regs.w.ax; +// selector = regs.w.dx; + if (regs.w.cflag != 0) + I_Error ("I_AllocLow: DOS alloc of %i failed, %i free", + length, regs.w.bx*16); + + + mem = (void *) ((regs.x.eax & 0xFFFF) << 4); + + memset (mem,0,length); + return mem; +} + +/* +============================================================================ + + NETWORKING + +============================================================================ +*/ + +/* // FUCKED LINES +typedef struct +{ + char priv[508]; +} doomdata_t; +*/ // FUCKED LINES + +#define DOOMCOM_ID 0x12345678l + +/* // FUCKED LINES +typedef struct +{ + long id; + short intnum; // DOOM executes an int to execute commands + +// communication between DOOM and the driver + short command; // CMD_SEND or CMD_GET + short remotenode; // dest for send, set by get (-1 = no packet) + short datalength; // bytes in doomdata to be sent + +// info common to all nodes + short numnodes; // console is allways node 0 + short ticdup; // 1 = no duplication, 2-5 = dup for slow nets + short extratics; // 1 = send a backup tic in every packet + short deathmatch; // 1 = deathmatch + short savegame; // -1 = new game, 0-5 = load savegame + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + +// info specific to this node + short consoleplayer; + short numplayers; + short angleoffset; // 1 = left, 0 = center, -1 = right + short drone; // 1 = drone + +// packet data to be sent + doomdata_t data; +} doomcom_t; +*/ // FUCKED LINES + +extern doomcom_t *doomcom; + +/* +==================== += += I_InitNetwork += +==================== +*/ + +void I_InitNetwork (void) +{ + int i; + + i = M_CheckParm ("-net"); + if (!i) + { + // + // single player game + // + doomcom = malloc (sizeof (*doomcom) ); + memset (doomcom, 0, sizeof(*doomcom) ); + netgame = false; + doomcom->id = DOOMCOM_ID; + doomcom->numplayers = doomcom->numnodes = 1; + doomcom->deathmatch = false; + doomcom->consoleplayer = 0; + doomcom->ticdup = 1; + doomcom->extratics = 0; + return; + } + + netgame = true; + doomcom = (doomcom_t *)atoi(myargv[i+1]); +//DEBUG +doomcom->skill = startskill; +doomcom->episode = startepisode; +doomcom->map = startmap; +doomcom->deathmatch = deathmatch; + +} + +void I_NetCmd (void) +{ + if (!netgame) + I_Error ("I_NetCmd when not in netgame"); + DPMIInt (doomcom->intnum); +} + +//========================================================================= +// +// I_CheckExternDriver +// +// Checks to see if a vector, and an address for an external driver +// have been passed. +//========================================================================= + +void I_CheckExternDriver(void) +{ + int i; + + if(!(i = M_CheckParm("-externdriver"))) + { + return; + } + i_ExternData = (externdata_t *)atoi(myargv[i+1]); + i_Vector = i_ExternData->vector; + + useexterndriver = true; +} + +//========================================================================= +//========================================================================= +// Hi-Res (mode 12) stuff +//========================================================================= +//========================================================================= + + +//========================================================================== +// +// SetVideoModeHR - Set video mode to 640x480x16 +// +//========================================================================== + + +void SetVideoModeHR(void) +{ + union REGS regs; + regs.x.eax = 0x12; + int386(VID_INT, ®s, ®s); +} + + +//========================================================================== +// +// ClearScreenHR - Clear the screen to color 0 +// +//========================================================================== + +void ClearScreenHR(void) +{ + BITPLANE(MASK_PLANE0|MASK_PLANE1|MASK_PLANE2|MASK_PLANE3); + memset((char *)0xa0000,0,38400); +} + + +//========================================================================== +// +// SlamHR - copy 4-plane buffer to screen +// +//========================================================================== + +void SlamHR(char *buffer) +{ + BITPLANE(MASK_PLANE0); + memcpy((char *)0xA0000, buffer+P0OFFSET, 38400); + BITPLANE(MASK_PLANE1); + memcpy((char *)0xA0000, buffer+P1OFFSET, 38400); + BITPLANE(MASK_PLANE2); + memcpy((char *)0xA0000, buffer+P2OFFSET, 38400); + BITPLANE(MASK_PLANE3); + memcpy((char *)0xA0000, buffer+P3OFFSET, 38400); +} + + +//========================================================================== +// +// SlamHR - copy 4-plane buffer to screen +// +// X and Width should be a multiple of 8 +// src should be 4 planes of block size, back to back +//========================================================================== + +void SlamBlockHR(int x, int y, int w, int h, char *src) +{ + int srcwid = w>>3; + char *dest = ((char *)0xA0000) + (y*(640/8)) + (x>>3); + char *dst; + int i; + + VB_SYNC; + + BITPLANE(MASK_PLANE0); + dst = dest; + for ( i=0; i +#include "h2def.h" +#include "dmx.h" +#include "sounds.h" +#include "i_sound.h" + +/* +=============== += += I_StartupTimer += +=============== +*/ + +int tsm_ID = -1; + +void I_StartupTimer (void) +{ +#ifndef NOTIMER + extern int I_TimerISR(void); + + ST_Message(" I_StartupTimer()\n"); + // installs master timer. Must be done before StartupTimer()! + TSM_Install(SND_TICRATE); + tsm_ID = TSM_NewService (I_TimerISR, 35, 255, 0); // max priority + if (tsm_ID == -1) + { + I_Error("Can't register 35 Hz timer w/ DMX library"); + } +#endif +} + +void I_ShutdownTimer (void) +{ + TSM_DelService(tsm_ID); + TSM_Remove(); +} + +/* + * + * SOUND HEADER & DATA + * + * + */ + +// sound information +#if 0 +const char *dnames[] = {"None", + "PC_Speaker", + "Adlib", + "Sound_Blaster", + "ProAudio_Spectrum16", + "Gravis_Ultrasound", + "MPU", + "AWE32" + }; +#endif + +const char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', + 'M', 'M', 'S' }; + +int snd_Channels; +int snd_DesiredMusicDevice, snd_DesiredSfxDevice; +int snd_MusicDevice, // current music card # (index to dmxCodes) + snd_SfxDevice, // current sfx card # (index to dmxCodes) + snd_MaxVolume, // maximum volume for sound + snd_MusicVolume; // maximum volume for music +int dmxCodes[NUM_SCARDS]; // the dmx code for a given card + +int snd_SBport, snd_SBirq, snd_SBdma; // sound blaster variables +int snd_Mport; // midi variables + +extern boolean snd_MusicAvail, // whether music is available + snd_SfxAvail; // whether sfx are available + +void I_PauseSong(int handle) +{ + MUS_PauseSong(handle); +} + +void I_ResumeSong(int handle) +{ + MUS_ResumeSong(handle); +} + +void I_SetMusicVolume(int volume) +{ + MUS_SetMasterVolume(volume*8); +// snd_MusicVolume = volume; +} + +void I_SetSfxVolume(int volume) +{ + snd_MaxVolume = volume; // THROW AWAY? +} + +/* + * + * SONG API + * + */ + +int I_RegisterSong(void *data) +{ + int rc = MUS_RegisterSong(data); +#ifdef SNDDEBUG + if (rc<0) ST_Message(" MUS_Reg() returned %d\n", rc); +#endif + return rc; +} + +void I_UnRegisterSong(int handle) +{ + int rc = MUS_UnregisterSong(handle); +#ifdef SNDDEBUG + if (rc < 0) ST_Message(" MUS_Unreg() returned %d\n", rc); +#endif +} + +int I_QrySongPlaying(int handle) +{ + int rc = MUS_QrySongPlaying(handle); +#ifdef SNDDEBUG + if (rc < 0) ST_Message(" MUS_QrySP() returned %d\n", rc); +#endif + return rc; +} + +// Stops a song. MUST be called before I_UnregisterSong(). + +void I_StopSong(int handle) +{ + int rc; + rc = MUS_StopSong(handle); +#ifdef SNDDEBUG + if (rc < 0) ST_Message(" MUS_StopSong() returned %d\n", rc); +#endif +/* + // Fucking kluge pause + { + int s; + extern volatile int ticcount; + for (s=ticcount ; ticcount - s < 10 ; ); + } +*/ +} + +void I_PlaySong(int handle, boolean looping) +{ + int rc; + rc = MUS_ChainSong(handle, looping ? handle : -1); +#ifdef SNDDEBUG + if (rc < 0) ST_Message(" MUS_ChainSong() returned %d\n", rc); +#endif + rc = MUS_PlaySong(handle, snd_MusicVolume); +#ifdef SNDDEBUG + if (rc < 0) ST_Message(" MUS_PlaySong() returned %d\n", rc); +#endif + +} + +/* + * + * SOUND FX API + * + */ + +// Gets lump nums of the named sound. Returns pointer which will be +// passed to I_StartSound() when you want to start an SFX. Must be +// sure to pass this to UngetSoundEffect() so that they can be +// freed! + + +int I_GetSfxLumpNum(sfxinfo_t *sound) +{ + return W_GetNumForName(sound->lumpname); + +} + +int I_StartSound (int id, void *data, int vol, int sep, int pitch, int priority) +{ + return SFX_PlayPatch(data, pitch, sep, vol, 0, 0); +} + +void I_StopSound(int handle) +{ +// extern volatile long gDmaCount; +// long waittocount; + SFX_StopPatch(handle); +// waittocount = gDmaCount + 2; +// while (gDmaCount < waittocount) ; +} + +int I_SoundIsPlaying(int handle) +{ + return SFX_Playing(handle); +} + +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch) +{ + SFX_SetOrigin(handle, pitch, sep, vol); +} + +/* + * + * SOUND STARTUP STUFF + * + * + */ + +// +// Why PC's Suck, Reason #8712 +// + +void I_sndArbitrateCards(void) +{ + char tmp[160]; + boolean gus, adlib, pc, sb, midi; + int i, rc, mputype, p, opltype, wait, dmxlump; + + snd_MusicDevice = snd_DesiredMusicDevice; + snd_SfxDevice = snd_DesiredSfxDevice; + + // check command-line parameters- overrides config file + // + if (M_CheckParm("-nosound")) snd_MusicDevice = snd_SfxDevice = snd_none; + if (M_CheckParm("-nosfx")) snd_SfxDevice = snd_none; + if (M_CheckParm("-nomusic")) snd_MusicDevice = snd_none; + + if (snd_MusicDevice > snd_MPU && snd_MusicDevice <= snd_MPU3) + snd_MusicDevice = snd_MPU; + if (snd_MusicDevice == snd_SB) + snd_MusicDevice = snd_Adlib; + if (snd_MusicDevice == snd_PAS) + snd_MusicDevice = snd_Adlib; + + // figure out what i've got to initialize + // + gus = snd_MusicDevice == snd_GUS || snd_SfxDevice == snd_GUS; + sb = snd_SfxDevice == snd_SB || snd_MusicDevice == snd_SB; + adlib = snd_MusicDevice == snd_Adlib ; + pc = snd_SfxDevice == snd_PC; + midi = snd_MusicDevice == snd_MPU; + + // initialize whatever i've got + // + if (gus) + { + if (GF1_Detect()) ST_Message(" Dude. The GUS ain't responding.\n"); + else + { + dmxlump = W_GetNumForName("dmxgus"); + GF1_SetMap(W_CacheLumpNum(dmxlump, PU_CACHE), lumpinfo[dmxlump].size); + } + + } + if (sb) + { + if(debugmode) + { + ST_Message(" Sound cfg p=0x%x, i=%d, d=%d\n", + snd_SBport, snd_SBirq, snd_SBdma); + } + if (SB_Detect(&snd_SBport, &snd_SBirq, &snd_SBdma, 0)) + { + ST_Message(" SB isn't responding at p=0x%x, i=%d, d=%d\n", + snd_SBport, snd_SBirq, snd_SBdma); + } + else SB_SetCard(snd_SBport, snd_SBirq, snd_SBdma); + + if(debugmode) + { + ST_Message(" SB_Detect returned p=0x%x, i=%d, d=%d\n", + snd_SBport, snd_SBirq, snd_SBdma); + } + } + + if (adlib) + { + if (AL_Detect(&wait,0)) + { + ST_Message(" Dude. The Adlib isn't responding.\n"); + } + else + { + AL_SetCard(wait, W_CacheLumpName("genmidi", PU_STATIC)); + } + } + + if (midi) + { + if (debugmode) + { + ST_Message(" cfg p=0x%x\n", snd_Mport); + } + + if (MPU_Detect(&snd_Mport, &i)) + { + ST_Message(" The MPU-401 isn't reponding @ p=0x%x.\n", snd_Mport); + } + else MPU_SetCard(snd_Mport); + } + +} + +// inits all sound stuff + +void I_StartupSound (void) +{ + int rc, i; + + if (debugmode) + ST_Message("I_StartupSound: Hope you hear a pop.\n"); + + // initialize dmxCodes[] + dmxCodes[0] = 0; + dmxCodes[snd_PC] = AHW_PC_SPEAKER; + dmxCodes[snd_Adlib] = AHW_ADLIB; + dmxCodes[snd_SB] = AHW_SOUND_BLASTER; + dmxCodes[snd_PAS] = AHW_MEDIA_VISION; + dmxCodes[snd_GUS] = AHW_ULTRA_SOUND; + dmxCodes[snd_MPU] = AHW_MPU_401; + dmxCodes[snd_MPU2] = AHW_MPU_401; + dmxCodes[snd_MPU3] = AHW_MPU_401; + dmxCodes[snd_AWE] = AHW_AWE32; + dmxCodes[snd_CDMUSIC] = 0; + + // inits sound library timer stuff + I_StartupTimer(); + + // pick the sound cards i'm going to use + // + I_sndArbitrateCards(); + + if (debugmode) + { + ST_Message(" Music device #%d & dmxCode=%d,", snd_MusicDevice, + dmxCodes[snd_MusicDevice]); + ST_Message(" Sfx device #%d & dmxCode=%d\n", snd_SfxDevice, + dmxCodes[snd_SfxDevice]); + } + + // inits DMX sound library + ST_Message(" Calling DMX_Init..."); + rc = DMX_Init(SND_TICRATE, SND_MAXSONGS, dmxCodes[snd_MusicDevice], + dmxCodes[snd_SfxDevice]); + + if (debugmode) + { + ST_Message(" DMX_Init() returned %d\n", rc); + } + +} + +// shuts down all sound stuff + +void I_ShutdownSound (void) +{ + DMX_DeInit(); + I_ShutdownTimer(); +} + +void I_SetChannels(int channels) +{ + WAV_PlayMode(channels, SND_SAMPLERATE); +} diff --git a/src/hexen/i_sound.h b/src/hexen/i_sound.h new file mode 100644 index 00000000..d4466978 --- /dev/null +++ b/src/hexen/i_sound.h @@ -0,0 +1,68 @@ +#ifndef __SOUND__ +#define __SOUND__ + +#define SND_TICRATE 140 // tic rate for updating sound +#define SND_MAXSONGS 40 // max number of songs in game +#define SND_SAMPLERATE 11025 // sample rate of sound effects + +typedef enum +{ + snd_none, + snd_PC, + snd_Adlib, + snd_SB, + snd_PAS, + snd_GUS, + snd_MPU, + snd_MPU2, + snd_MPU3, + snd_AWE, + snd_CDMUSIC, + NUM_SCARDS +} cardenum_t; + +void I_PauseSong(int handle); +void I_ResumeSong(int handle); +void I_SetMusicVolume(int volume); +void I_SetSfxVolume(int volume); +int I_RegisterSong(void *data); +void I_UnRegisterSong(int handle); +int I_QrySongPlaying(int handle); +void I_StopSong(int handle); +void I_PlaySong(int handle, int looping); +int I_GetSfxLumpNum(sfxinfo_t *sound); +int I_StartSound (int id, void *data, int vol, int sep, int pitch, int priority); +void I_StopSound(int handle); +int I_SoundIsPlaying(int handle); +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); +void I_sndArbitrateCards(void); +void I_StartupSound (void); +void I_ShutdownSound (void); +void I_SetChannels(int channels); + +#endif + +#ifndef __ICDMUS__ +#define __ICDMUS__ + +#define CDERR_NOTINSTALLED 10 // MSCDEX not installed +#define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio +#define CDERR_NOAUDIOTRACKS 12 // Current CD has no audio tracks +#define CDERR_BADDRIVE 20 // Bad drive number +#define CDERR_BADTRACK 21 // Bad track number +#define CDERR_IOCTLBUFFMEM 22 // Not enough low memory for IOCTL +#define CDERR_DEVREQBASE 100 // DevReq errors + +extern int cd_Error; + +int I_CDMusInit(void); +int I_CDMusPlay(int track); +int I_CDMusStop(void); +int I_CDMusResume(void); +int I_CDMusSetVolume(int volume); +int I_CDMusFirstTrack(void); +int I_CDMusLastTrack(void); +int I_CDMusTrackLength(int track); + +#endif + diff --git a/src/hexen/in_lude.c b/src/hexen/in_lude.c new file mode 100644 index 00000000..3fa0fadf --- /dev/null +++ b/src/hexen/in_lude.c @@ -0,0 +1,597 @@ + +//************************************************************************** +//** +//** in_lude.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: in_lude.c,v $ +//** $Revision: 1.19 $ +//** $Date: 96/01/05 23:33:19 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include + +// MACROS ------------------------------------------------------------------ + +#define TEXTSPEED 3 +#define TEXTWAIT 140 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + SINGLE, + COOPERATIVE, + DEATHMATCH +} gametype_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void IN_Start(void); +void IN_Ticker(void); +void IN_Drawer(void); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void WaitStop(void); +static void Stop(void); +static void LoadPics(void); +static void UnloadPics(void); +static void CheckForSkip(void); +static void InitStats(void); +static void DrDeathTally(void); +static void DrNumber(int val, int x, int y, int wrapThresh); +static void DrNumberBold(int val, int x, int y, int wrapThresh); +static void DrawHubText(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +boolean intermission; +char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE]; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static boolean skipintermission; +static int interstate = 0; +static int intertime = -1; +static gametype_t gametype; +static int cnt; +static int slaughterboy; // in DM, the player with the most kills +static patch_t *patchINTERPIC; +static patch_t *FontBNumbers[10]; +static patch_t *FontBNegative; +static patch_t *FontBSlash; +static patch_t *FontBPercent; +static int FontABaseLump; +static int FontBLump; +static int FontBLumpBase; + +static signed int totalFrags[MAXPLAYERS]; + +static int HubCount; +static char *HubText; + +// CODE -------------------------------------------------------------------- + +//======================================================================== +// +// IN_Start +// +//======================================================================== + +extern void AM_Stop (void); + +void IN_Start(void) +{ + int i; + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + InitStats(); + LoadPics(); + intermission = true; + interstate = 0; + skipintermission = false; + intertime = 0; + AM_Stop(); + for(i = 0; i < MAXPLAYERS; i++) + { + players[i].messageTics = 0; + players[i].message[0] = 0; + } + SN_StopAllSequences(); +} + +//======================================================================== +// +// WaitStop +// +//======================================================================== + +void WaitStop(void) +{ + if(!--cnt) + { + Stop(); +// gamestate = GS_LEVEL; +// G_DoLoadLevel(); + gameaction = ga_leavemap; +// G_WorldDone(); + } +} + +//======================================================================== +// +// Stop +// +//======================================================================== + +static void Stop(void) +{ + intermission = false; + UnloadPics(); + SB_state = -1; + BorderNeedRefresh = true; +} + +//======================================================================== +// +// InitStats +// +// Initializes the stats for single player mode +//======================================================================== + +static char *ClusMsgLumpNames[] = +{ + "clus1msg", + "clus2msg", + "clus3msg", + "clus4msg", + "clus5msg" +}; + +static void InitStats(void) +{ + int i; + int j; + int oldCluster; + signed int slaughterfrags; + int posnum; + int slaughtercount; + int playercount; + char *msgLumpName; + int msgSize; + int msgLump; + + extern int LeaveMap; + + if(!deathmatch) + { + gametype = SINGLE; + HubCount = 0; + oldCluster = P_GetMapCluster(gamemap); + if(oldCluster != P_GetMapCluster(LeaveMap)) + { + if(oldCluster >= 1 && oldCluster <= 5) + { + msgLumpName = ClusMsgLumpNames[oldCluster-1]; + msgLump = W_GetNumForName(msgLumpName); + msgSize = W_LumpLength(msgLump); + if(msgSize >= MAX_INTRMSN_MESSAGE_SIZE) + { + I_Error("Cluster message too long (%s)", msgLumpName); + } + W_ReadLump(msgLump, ClusterMessage); + ClusterMessage[msgSize] = 0; // Append terminator + HubText = ClusterMessage; + HubCount = strlen(HubText)*TEXTSPEED+TEXTWAIT; + S_StartSongName("hub", true); + } + } + } + else + { + gametype = DEATHMATCH; + slaughterboy = 0; + slaughterfrags = -9999; + posnum = 0; + playercount = 0; + slaughtercount = 0; + for(i=0; i slaughterfrags) + { + slaughterboy = 1<cmd.buttons&BT_ATTACK) + { + if(!player->attackdown) + { + skipintermission = 1; + } + player->attackdown = true; + } + else + { + player->attackdown = false; + } + if(player->cmd.buttons&BT_USE) + { + if(!player->usedown) + { + skipintermission = 1; + } + player->usedown = true; + } + else + { + player->usedown = false; + } + } + } + if(deathmatch && intertime < 140) + { // wait for 4 seconds before allowing a skip + if(skipintermission == 1) + { + triedToSkip = true; + skipintermission = 0; + } + } + else + { + if(triedToSkip) + { + skipintermission = 1; + triedToSkip = false; + } + } +} + +//======================================================================== +// +// IN_Drawer +// +//======================================================================== + +void IN_Drawer(void) +{ + if(!intermission) + { + return; + } + if(interstate) + { + return; + } + UpdateState |= I_FULLSCRN; + memcpy(screen, (byte *)patchINTERPIC, SCREENWIDTH*SCREENHEIGHT); + + if(gametype == SINGLE) + { + if(HubCount) + { + DrawHubText(); + } + } + else + { + DrDeathTally(); + } +} + +//======================================================================== +// +// DrDeathTally +// +//======================================================================== + +#define TALLY_EFFECT_TICKS 20 +#define TALLY_FINAL_X_DELTA (23*FRACUNIT) +#define TALLY_FINAL_Y_DELTA (13*FRACUNIT) +#define TALLY_START_XPOS (178*FRACUNIT) +#define TALLY_STOP_XPOS (90*FRACUNIT) +#define TALLY_START_YPOS (132*FRACUNIT) +#define TALLY_STOP_YPOS (83*FRACUNIT) +#define TALLY_TOP_X 85 +#define TALLY_TOP_Y 9 +#define TALLY_LEFT_X 7 +#define TALLY_LEFT_Y 71 +#define TALLY_TOTALS_X 291 + +static void DrDeathTally(void) +{ + int i, j; + fixed_t xPos, yPos; + fixed_t xDelta, yDelta; + fixed_t xStart, scale; + int x, y; + boolean bold; + static boolean showTotals; + int temp; + + V_DrawPatch(TALLY_TOP_X, TALLY_TOP_Y, + W_CacheLumpName("tallytop", PU_CACHE)); + V_DrawPatch(TALLY_LEFT_X, TALLY_LEFT_Y, + W_CacheLumpName("tallylft", PU_CACHE)); + if(intertime < TALLY_EFFECT_TICKS) + { + showTotals = false; + scale = (intertime*FRACUNIT)/TALLY_EFFECT_TICKS; + xDelta = FixedMul(scale, TALLY_FINAL_X_DELTA); + yDelta = FixedMul(scale, TALLY_FINAL_Y_DELTA); + xStart = TALLY_START_XPOS-FixedMul(scale, + TALLY_START_XPOS-TALLY_STOP_XPOS); + yPos = TALLY_START_YPOS-FixedMul(scale, + TALLY_START_YPOS-TALLY_STOP_YPOS); + } + else + { + xDelta = TALLY_FINAL_X_DELTA; + yDelta = TALLY_FINAL_Y_DELTA; + xStart = TALLY_STOP_XPOS; + yPos = TALLY_STOP_YPOS; + } + if(intertime >= TALLY_EFFECT_TICKS && showTotals == false) + { + showTotals = true; + S_StartSound(NULL, SFX_PLATFORM_STOP); + } + y = yPos>>FRACBITS; + for(i = 0; i < MAXPLAYERS; i++) + { + xPos = xStart; + for(j = 0; j < MAXPLAYERS; j++, xPos += xDelta) + { + x = xPos>>FRACBITS; + bold = (i == consoleplayer || j == consoleplayer); + if(playeringame[i] && playeringame[j]) + { + if(bold) + { + DrNumberBold(players[i].frags[j], x, y, 100); + } + else + { + DrNumber(players[i].frags[j], x, y, 100); + } + } + else + { + temp = MN_TextAWidth("--")/2; + if(bold) + { + MN_DrTextAYellow("--", x-temp, y); + } + else + { + MN_DrTextA("--", x-temp, y); + } + } + } + if(showTotals && playeringame[i] + && !((slaughterboy&(1<>FRACBITS; + } +} + +//========================================================================== +// +// DrNumber +// +//========================================================================== + +static void DrNumber(int val, int x, int y, int wrapThresh) +{ + char buff[8] = "XX"; + + if(!(val < -9 && wrapThresh < 1000)) + { + sprintf(buff, "%d", val >= wrapThresh ? val%wrapThresh : val); + } + MN_DrTextA(buff, x-MN_TextAWidth(buff)/2, y); +} + +//========================================================================== +// +// DrNumberBold +// +//========================================================================== + +static void DrNumberBold(int val, int x, int y, int wrapThresh) +{ + char buff[8] = "XX"; + + if(!(val < -9 && wrapThresh < 1000)) + { + sprintf(buff, "%d", val >= wrapThresh ? val%wrapThresh : val); + } + MN_DrTextAYellow(buff, x-MN_TextAWidth(buff)/2, y); +} + +//=========================================================================== +// +// DrawHubText +// +//=========================================================================== + +static void DrawHubText(void) +{ + int count; + char *ch; + int c; + int cx, cy; + patch_t *w; + + cy = 5; + cx = 10; + ch = HubText; + count = (intertime-10)/TEXTSPEED; + if (count < 0) + { + count = 0; + } + for(; count; count--) + { + c = *ch++; + if(!c) + { + break; + } + if(c == '\n') + { + cx = 10; + cy += 9; + continue; + } + if(c < 32) + { + continue; + } + c = toupper(c); + if(c == 32) + { + cx += 5; + continue; + } + w = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + if(cx+w->width > SCREENWIDTH) + { + break; + } + V_DrawPatch(cx, cy, w); + cx += w->width; + } +} diff --git a/src/hexen/info.c b/src/hexen/info.c new file mode 100644 index 00000000..a0d0bf4e --- /dev/null +++ b/src/hexen/info.c @@ -0,0 +1,13862 @@ +#include "h2def.h" +// generated by stateco + +char *sprnames[NUMSPRITES] = { +"MAN1","ACLO","TLGL","FBL1","XPL1","ARRW","DART","RIPP","CFCF","BLAD", +"SHRD","FFSM","FFLG","PTN1","PTN2","SOAR","INVU","SUMN","TSPK","TELO", +"TRNG","ROCK","FOGS","FOGM","FOGL","SGSA","SGSB","PORK","EGGM","FHFX", +"SPHL","STWN","GMPD","ASKU","ABGM","AGMR","AGMG","AGG2","AGMB","AGB2", +"ABK1","ABK2","ASK2","AFWP","ACWP","AMWP","AGER","AGR2","AGR3","AGR4", +"TRCH","PSBG","ATLP","THRW","SPED","BMAN","BRAC","BLST","HRAD","SPSH", +"LVAS","SLDG","STTW","RCK1","RCK2","RCK3","RCK4","CDLR","TRE1","TRDT", +"TRE2","TRE3","STM1","STM2","STM3","STM4","MSH1","MSH2","MSH3","MSH4", +"MSH5","MSH6","MSH7","MSH8","SGMP","SGM1","SGM2","SGM3","SLC1","SLC2", +"SLC3","MSS1","MSS2","SWMV","CPS1","CPS2","TMS1","TMS2","TMS3","TMS4", +"TMS5","TMS6","TMS7","CPS3","STT2","STT3","STT4","STT5","GAR1","GAR2", +"GAR3","GAR4","GAR5","GAR6","GAR7","GAR8","GAR9","BNR1","TRE4","TRE5", +"TRE6","TRE7","LOGG","ICT1","ICT2","ICT3","ICT4","ICM1","ICM2","ICM3", +"ICM4","RKBL","RKBS","RKBK","RBL1","RBL2","RBL3","VASE","POT1","POT2", +"POT3","PBIT","CPS4","CPS5","CPS6","CPB1","CPB2","CPB3","CPB4","BDRP", +"BDSH","BDPL","CNDL","LEF1","LEF3","LEF2","TWTR","WLTR","BARL","SHB1", +"SHB2","BCKT","SHRM","FBUL","FSKL","BRTR","SUIT","BBLL","CAND","IRON", +"XMAS","CDRN","CHNS","TST1","TST2","TST3","TST4","TST5","TST6","TST7", +"TST8","TST9","TST0","TELE","TSMK","FPCH","WFAX","FAXE","WFHM","FHMR", +"FSRD","FSFX","CMCE","WCSS","CSSF","WCFM","CFLM","CFFX","CHLY","SPIR", +"MWND","WMLG","MLNG","MLFX","MLF2","MSTF","MSP1","MSP2","WFR1","WFR2", +"WFR3","WCH1","WCH2","WCH3","WMS1","WMS2","WMS3","WPIG","WMCS","CONE", +"SHEX","BLOD","GIBS","PLAY","FDTH","BSKL","ICEC","CLER","MAGE","PIGY", +"CENT","CTXD","CTFX","CTDP","DEMN","DEMA","DEMB","DEMC","DEMD","DEME", +"DMFX","DEM2","DMBA","DMBB","DMBC","DMBD","DMBE","D2FX","WRTH","WRT2", +"WRBL","MNTR","FX12","FX13","MNSM","SSPT","SSDV","SSXD","SSFX","BISH", +"BPFX","DRAG","DRFX","ARM1","ARM2","ARM3","ARM4","MAN2","MAN3","KEY1", +"KEY2","KEY3","KEY4","KEY5","KEY6","KEY7","KEY8","KEY9","KEYA","KEYB", +"ETTN","ETTB","FDMN","FDMB","ICEY","ICPR","ICWS","SORC","SBMP","SBS4", +"SBMB","SBS3","SBMG","SBS1","SBS2","SBFX","RADE","WATR","KORX","ABAT" +}; + +void A_FreeTargMobj (); +void A_FlameCheck (); +void A_HideThing (); +void A_UnHideThing (); +void A_RestoreSpecialThing1 (); +void A_RestoreSpecialThing2 (); +void A_RestoreArtifact (); +void A_Summon (); +void A_ThrustInitUp (); +void A_ThrustInitDn (); +void A_ThrustRaise (); +void A_ThrustBlock (); +void A_ThrustImpale (); +void A_ThrustLower (); +void A_TeloSpawnC (); +void A_TeloSpawnB (); +void A_TeloSpawnA (); +void A_TeloSpawnD (); +void A_CheckTeleRing (); +void A_FogSpawn (); +void A_FogMove (); +void A_Quake (); +void A_ContMobjSound (); +void A_Scream (); +void A_Explode (); +void A_PoisonBagInit (); +void A_PoisonBagDamage (); +void A_PoisonBagCheck (); +void A_CheckThrowBomb (); +void A_NoGravity (); +void A_PotteryExplode (); +void A_PotteryChooseBit (); +void A_PotteryCheck (); +void A_CorpseBloodDrip (); +void A_CorpseExplode (); +void A_LeafSpawn (); +void A_LeafThrust (); +void A_LeafCheck (); +void A_BridgeInit (); +void A_BridgeOrbit (); +void A_TreeDeath (); +void A_PoisonShroom (); +void A_Pain (); +void A_SoAExplode (); +void A_BellReset1 (); +void A_BellReset2 (); +void A_NoBlocking (); +void A_Light0 (); +void A_WeaponReady (); +void A_Lower (); +void A_Raise (); +void A_FPunchAttack (); +void A_ReFire (); +void A_FAxeAttack (); +void A_FHammerAttack (); +void A_FHammerThrow (); +void A_FSwordAttack (); +void A_FSwordFlames (); +void A_CMaceAttack (); +void A_CStaffInitBlink (); +void A_CStaffCheckBlink (); +void A_CStaffCheck (); +void A_CStaffAttack (); +void A_CStaffMissileSlither (); +void A_CFlameAttack (); +void A_CFlameRotate (); +void A_CFlamePuff (); +void A_CFlameMissile (); +void A_CHolyAttack (); +void A_CHolyPalette (); +void A_CHolySeek (); +void A_CHolyCheckScream (); +void A_CHolyTail (); +void A_CHolySpawnPuff (); +void A_CHolyAttack2 (); +void A_MWandAttack (); +void A_LightningReady (); +void A_MLightningAttack (); +void A_LightningZap (); +void A_LightningClip (); +void A_LightningRemove (); +void A_LastZap (); +void A_ZapMimic (); +void A_MStaffAttack (); +void A_MStaffPalette (); +void A_MStaffWeave (); +void A_MStaffTrack (); +void A_SnoutAttack (); +void A_FireConePL1 (); +void A_ShedShard (); +void A_AddPlayerCorpse (); +void A_SkullPop (); +void A_FreezeDeath (); +void A_FreezeDeathChunks (); +void A_CheckBurnGone (); +void A_CheckSkullFloor (); +void A_CheckSkullDone (); +void A_SpeedFade (); +void A_IceSetTics (); +void A_IceCheckHeadDone (); +void A_PigPain (); +void A_PigLook (); +void A_PigChase (); +void A_FaceTarget (); +void A_PigAttack (); +void A_QueueCorpse (); +void A_Look (); +void A_Chase (); +void A_CentaurAttack (); +void A_CentaurAttack2 (); +void A_SetReflective (); +void A_CentaurDefend (); +void A_UnSetReflective (); +void A_CentaurDropStuff (); +void A_CheckFloor (); +void A_DemonAttack1 (); +void A_DemonAttack2 (); +void A_DemonDeath (); +void A_Demon2Death (); +void A_WraithRaiseInit (); +void A_WraithRaise (); +void A_WraithInit (); +void A_WraithLook (); +void A_WraithChase (); +void A_WraithFX3 (); +void A_WraithMelee (); +void A_WraithMissile (); +void A_WraithFX2 (); +void A_MinotaurFade1 (); +void A_MinotaurFade2 (); +void A_MinotaurLook (); +void A_MinotaurChase (); +void A_MinotaurRoam (); +void A_MinotaurAtk1 (); +void A_MinotaurDecide (); +void A_MinotaurAtk2 (); +void A_MinotaurAtk3 (); +void A_MinotaurCharge (); +void A_SmokePuffExit (); +void A_MinotaurFade0 (); +void A_MntrFloorFire (); +void A_SerpentChase (); +void A_SerpentHumpDecide (); +void A_SerpentUnHide (); +void A_SerpentRaiseHump (); +void A_SerpentLowerHump (); +void A_SerpentHide (); +void A_SerpentBirthScream (); +void A_SetShootable (); +void A_SerpentCheckForAttack (); +void A_UnSetShootable (); +void A_SerpentDiveSound (); +void A_SerpentWalk (); +void A_SerpentChooseAttack (); +void A_SerpentMeleeAttack (); +void A_SerpentMissileAttack (); +void A_SerpentHeadPop (); +void A_SerpentSpawnGibs (); +void A_SerpentHeadCheck (); +void A_FloatGib (); +void A_DelayGib (); +void A_SinkGib (); +void A_BishopDecide (); +void A_BishopDoBlur (); +void A_BishopSpawnBlur (); +void A_BishopChase (); +void A_BishopAttack (); +void A_BishopAttack2 (); +void A_BishopPainBlur (); +void A_BishopPuff (); +void A_SetAltShadow (); +void A_BishopMissileWeave (); +void A_BishopMissileSeek (); +void A_DragonInitFlight (); +void A_DragonFlap (); +void A_DragonFlight (); +void A_DragonAttack (); +void A_DragonPain (); +void A_DragonCheckCrash (); +void A_DragonFX2 (); +void A_ESound (); +void A_EttinAttack (); +void A_DropMace (); +void A_FiredRocks (); +void A_UnSetInvulnerable (); +void A_FiredChase (); +void A_FiredAttack (); +void A_FiredSplotch (); +void A_SmBounce (); +void A_IceGuyLook (); +void A_IceGuyChase (); +void A_IceGuyAttack (); +void A_IceGuyDie (); +void A_IceGuyMissilePuff (); +void A_IceGuyMissileExplode (); +void A_ClassBossHealth (); +void A_FastChase (); +void A_FighterAttack (); +void A_ClericAttack (); +void A_MageAttack (); +void A_SorcSpinBalls (); +void A_SpeedBalls (); +void A_SpawnFizzle (); +void A_SorcBossAttack (); +void A_SorcBallOrbit (); +void A_SorcBallPop (); +void A_BounceCheck (); +void A_SorcFX1Seek (); +void A_SorcFX2Split (); +void A_SorcFX2Orbit (); +void A_SorcererBishopEntry (); +void A_SpawnBishop (); +void A_SorcFX4Check (); +void A_KoraxStep2 (); +void A_KoraxChase (); +void A_KoraxStep (); +void A_KoraxDecide (); +void A_KoraxMissile (); +void A_KoraxCommand (); +void A_KoraxBonePop (); +void A_KSpiritRoam (); +void A_KBoltRaise (); +void A_KBolt (); +void A_BatSpawnInit (); +void A_BatSpawn (); +void A_BatMove (); + +state_t states[NUMSTATES] = { +{SPR_MAN1,0,-1,NULL,S_NULL,0,0}, // S_NULL +{SPR_ACLO,4,1050,A_FreeTargMobj,S_NULL,0,0}, // S_FREETARGMOBJ +{SPR_TLGL,0,-1,NULL,S_NULL,0,0}, // S_MAPSPOT +{SPR_FBL1,32768,4,NULL,S_FIREBALL1_2,0,0}, // S_FIREBALL1_1 +{SPR_FBL1,32769,4,NULL,S_FIREBALL1_1,0,0}, // S_FIREBALL1_2 +{SPR_XPL1,32768,4,NULL,S_FIREBALL1_X2,0,0}, // S_FIREBALL1_X1 +{SPR_XPL1,32769,4,NULL,S_FIREBALL1_X3,0,0}, // S_FIREBALL1_X2 +{SPR_XPL1,32770,4,NULL,S_FIREBALL1_X4,0,0}, // S_FIREBALL1_X3 +{SPR_XPL1,32771,4,NULL,S_FIREBALL1_X5,0,0}, // S_FIREBALL1_X4 +{SPR_XPL1,32772,4,NULL,S_FIREBALL1_X6,0,0}, // S_FIREBALL1_X5 +{SPR_XPL1,32773,4,NULL,S_NULL,0,0}, // S_FIREBALL1_X6 +{SPR_ARRW,0,-1,NULL,S_NULL,0,0}, // S_ARROW_1 +{SPR_ARRW,0,1,NULL,S_NULL,0,0}, // S_ARROW_X1 +{SPR_DART,0,-1,NULL,S_NULL,0,0}, // S_DART_1 +{SPR_DART,0,1,NULL,S_NULL,0,0}, // S_DART_X1 +{SPR_DART,0,-1,NULL,S_NULL,0,0}, // S_POISONDART_1 +{SPR_DART,0,1,NULL,S_NULL,0,0}, // S_POISONDART_X1 +{SPR_RIPP,0,3,NULL,S_RIPPERBALL_2,0,0}, // S_RIPPERBALL_1 +{SPR_RIPP,1,3,NULL,S_RIPPERBALL_3,0,0}, // S_RIPPERBALL_2 +{SPR_RIPP,2,3,NULL,S_RIPPERBALL_1,0,0}, // S_RIPPERBALL_3 +{SPR_CFCF,32784,4,NULL,S_RIPPERBALL_X2,0,0}, // S_RIPPERBALL_X1 +{SPR_CFCF,32785,3,NULL,S_RIPPERBALL_X3,0,0}, // S_RIPPERBALL_X2 +{SPR_CFCF,32786,4,NULL,S_RIPPERBALL_X4,0,0}, // S_RIPPERBALL_X3 +{SPR_CFCF,32787,3,NULL,S_RIPPERBALL_X5,0,0}, // S_RIPPERBALL_X4 +{SPR_CFCF,32788,4,NULL,S_RIPPERBALL_X6,0,0}, // S_RIPPERBALL_X5 +{SPR_CFCF,32789,3,NULL,S_RIPPERBALL_X7,0,0}, // S_RIPPERBALL_X6 +{SPR_CFCF,32790,4,NULL,S_RIPPERBALL_X8,0,0}, // S_RIPPERBALL_X7 +{SPR_CFCF,32791,3,NULL,S_RIPPERBALL_X9,0,0}, // S_RIPPERBALL_X8 +{SPR_CFCF,32792,4,NULL,S_RIPPERBALL_X10,0,0}, // S_RIPPERBALL_X9 +{SPR_CFCF,32793,3,NULL,S_NULL,0,0}, // S_RIPPERBALL_X10 +{SPR_BLAD,0,-1,NULL,S_NULL,0,0}, // S_PRJ_BLADE1 +{SPR_BLAD,0,1,NULL,S_NULL,0,0}, // S_PRJ_BLADE_X1 +{SPR_SHRD,32768,3,NULL,S_ICESHARD2,0,0}, // S_ICESHARD1 +{SPR_SHRD,32769,3,NULL,S_ICESHARD3,0,0}, // S_ICESHARD2 +{SPR_SHRD,32770,3,NULL,S_ICESHARD1,0,0}, // S_ICESHARD3 +{SPR_FFSM,32768,3,NULL,S_FLAME_TSMALL2,0,0}, // S_FLAME_TSMALL1 +{SPR_FFSM,32769,3,NULL,S_FLAME_TSMALL3,0,0}, // S_FLAME_TSMALL2 +{SPR_FFSM,32770,2,A_FlameCheck,S_FLAME_TSMALL4,0,0}, // S_FLAME_TSMALL3 +{SPR_FFSM,32770,2,NULL,S_FLAME_TSMALL5,0,0}, // S_FLAME_TSMALL4 +{SPR_FFSM,32771,3,NULL,S_FLAME_TSMALL6,0,0}, // S_FLAME_TSMALL5 +{SPR_FFSM,32772,3,A_FlameCheck,S_FLAME_TSMALL1,0,0}, // S_FLAME_TSMALL6 +{SPR_FFLG,32768,4,NULL,S_FLAME_TLARGE2,0,0}, // S_FLAME_TLARGE1 +{SPR_FFLG,32769,4,A_FlameCheck,S_FLAME_TLARGE3,0,0}, // S_FLAME_TLARGE2 +{SPR_FFLG,32770,4,NULL,S_FLAME_TLARGE4,0,0}, // S_FLAME_TLARGE3 +{SPR_FFLG,32771,4,A_FlameCheck,S_FLAME_TLARGE5,0,0}, // S_FLAME_TLARGE4 +{SPR_FFLG,32772,4,NULL,S_FLAME_TLARGE6,0,0}, // S_FLAME_TLARGE5 +{SPR_FFLG,32773,4,A_FlameCheck,S_FLAME_TLARGE7,0,0}, // S_FLAME_TLARGE6 +{SPR_FFLG,32774,4,NULL,S_FLAME_TLARGE8,0,0}, // S_FLAME_TLARGE7 +{SPR_FFLG,32775,4,A_FlameCheck,S_FLAME_TLARGE9,0,0}, // S_FLAME_TLARGE8 +{SPR_FFLG,32776,4,NULL,S_FLAME_TLARGE10,0,0}, // S_FLAME_TLARGE9 +{SPR_FFLG,32777,4,A_FlameCheck,S_FLAME_TLARGE11,0,0}, // S_FLAME_TLARGE10 +{SPR_FFLG,32778,4,NULL,S_FLAME_TLARGE12,0,0}, // S_FLAME_TLARGE11 +{SPR_FFLG,32779,4,A_FlameCheck,S_FLAME_TLARGE13,0,0}, // S_FLAME_TLARGE12 +{SPR_FFLG,32780,4,NULL,S_FLAME_TLARGE14,0,0}, // S_FLAME_TLARGE13 +{SPR_FFLG,32781,4,A_FlameCheck,S_FLAME_TLARGE15,0,0}, // S_FLAME_TLARGE14 +{SPR_FFLG,32782,4,NULL,S_FLAME_TLARGE16,0,0}, // S_FLAME_TLARGE15 +{SPR_FFLG,32783,4,A_FlameCheck,S_FLAME_TLARGE5,0,0}, // S_FLAME_TLARGE16 +{SPR_FFSM,0,2,NULL,S_FLAME_SDORM2,0,0}, // S_FLAME_SDORM1 +{SPR_FFSM,1,2,A_HideThing,S_FLAME_SDORM3,0,0}, // S_FLAME_SDORM2 +{SPR_FFSM,2,200,NULL,S_FLAME_SDORM3,0,0}, // S_FLAME_SDORM3 +{SPR_FFSM,32768,3,NULL,S_FLAME_SMALL2,0,0}, // S_FLAME_SMALL1 +{SPR_FFSM,32768,3,A_UnHideThing,S_FLAME_SMALL3,0,0}, // S_FLAME_SMALL2 +{SPR_FFSM,32768,3,NULL,S_FLAME_SMALL4,0,0}, // S_FLAME_SMALL3 +{SPR_FFSM,32769,3,NULL,S_FLAME_SMALL5,0,0}, // S_FLAME_SMALL4 +{SPR_FFSM,32770,3,NULL,S_FLAME_SMALL6,0,0}, // S_FLAME_SMALL5 +{SPR_FFSM,32771,3,NULL,S_FLAME_SMALL7,0,0}, // S_FLAME_SMALL6 +{SPR_FFSM,32772,3,NULL,S_FLAME_SMALL3,0,0}, // S_FLAME_SMALL7 +{SPR_FFLG,3,2,NULL,S_FLAME_LDORM2,0,0}, // S_FLAME_LDORM1 +{SPR_FFLG,2,2,NULL,S_FLAME_LDORM3,0,0}, // S_FLAME_LDORM2 +{SPR_FFLG,1,2,NULL,S_FLAME_LDORM4,0,0}, // S_FLAME_LDORM3 +{SPR_FFLG,0,2,A_HideThing,S_FLAME_LDORM5,0,0}, // S_FLAME_LDORM4 +{SPR_FFLG,0,200,NULL,S_FLAME_LDORM5,0,0}, // S_FLAME_LDORM5 +{SPR_FFLG,32768,2,NULL,S_FLAME_LARGE2,0,0}, // S_FLAME_LARGE1 +{SPR_FFLG,32768,2,A_UnHideThing,S_FLAME_LARGE3,0,0}, // S_FLAME_LARGE2 +{SPR_FFLG,32768,4,NULL,S_FLAME_LARGE4,0,0}, // S_FLAME_LARGE3 +{SPR_FFLG,32769,4,NULL,S_FLAME_LARGE5,0,0}, // S_FLAME_LARGE4 +{SPR_FFLG,32770,4,NULL,S_FLAME_LARGE6,0,0}, // S_FLAME_LARGE5 +{SPR_FFLG,32771,4,NULL,S_FLAME_LARGE7,0,0}, // S_FLAME_LARGE6 +{SPR_FFLG,32772,4,NULL,S_FLAME_LARGE8,0,0}, // S_FLAME_LARGE7 +{SPR_FFLG,32773,4,NULL,S_FLAME_LARGE9,0,0}, // S_FLAME_LARGE8 +{SPR_FFLG,32774,4,NULL,S_FLAME_LARGE10,0,0}, // S_FLAME_LARGE9 +{SPR_FFLG,32775,4,NULL,S_FLAME_LARGE11,0,0}, // S_FLAME_LARGE10 +{SPR_FFLG,32776,4,NULL,S_FLAME_LARGE12,0,0}, // S_FLAME_LARGE11 +{SPR_FFLG,32777,4,NULL,S_FLAME_LARGE13,0,0}, // S_FLAME_LARGE12 +{SPR_FFLG,32778,4,NULL,S_FLAME_LARGE14,0,0}, // S_FLAME_LARGE13 +{SPR_FFLG,32779,4,NULL,S_FLAME_LARGE15,0,0}, // S_FLAME_LARGE14 +{SPR_FFLG,32780,4,NULL,S_FLAME_LARGE16,0,0}, // S_FLAME_LARGE15 +{SPR_FFLG,32781,4,NULL,S_FLAME_LARGE17,0,0}, // S_FLAME_LARGE16 +{SPR_FFLG,32782,4,NULL,S_FLAME_LARGE18,0,0}, // S_FLAME_LARGE17 +{SPR_FFLG,32783,4,NULL,S_FLAME_LARGE7,0,0}, // S_FLAME_LARGE18 +{SPR_PTN1,0,3,NULL,S_ITEM_PTN1_2,0,0}, // S_ITEM_PTN1_1 +{SPR_PTN1,1,3,NULL,S_ITEM_PTN1_3,0,0}, // S_ITEM_PTN1_2 +{SPR_PTN1,2,3,NULL,S_ITEM_PTN1_1,0,0}, // S_ITEM_PTN1_3 +{SPR_ACLO,4,1400,NULL,S_HIDESPECIAL2,0,0}, // S_HIDESPECIAL1 +{SPR_ACLO,0,4,A_RestoreSpecialThing1,S_HIDESPECIAL3,0,0}, // S_HIDESPECIAL2 +{SPR_ACLO,1,4,NULL,S_HIDESPECIAL4,0,0}, // S_HIDESPECIAL3 +{SPR_ACLO,0,4,NULL,S_HIDESPECIAL5,0,0}, // S_HIDESPECIAL4 +{SPR_ACLO,1,4,NULL,S_HIDESPECIAL6,0,0}, // S_HIDESPECIAL5 +{SPR_ACLO,2,4,NULL,S_HIDESPECIAL7,0,0}, // S_HIDESPECIAL6 +{SPR_ACLO,1,4,NULL,S_HIDESPECIAL8,0,0}, // S_HIDESPECIAL7 +{SPR_ACLO,2,4,NULL,S_HIDESPECIAL9,0,0}, // S_HIDESPECIAL8 +{SPR_ACLO,3,4,NULL,S_HIDESPECIAL10,0,0}, // S_HIDESPECIAL9 +{SPR_ACLO,2,4,NULL,S_HIDESPECIAL11,0,0}, // S_HIDESPECIAL10 +{SPR_ACLO,3,4,A_RestoreSpecialThing2,S_NULL,0,0}, // S_HIDESPECIAL11 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI1_2,0,0}, // S_DORMANTARTI1_1 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_3,0,0}, // S_DORMANTARTI1_2 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI1_4,0,0}, // S_DORMANTARTI1_3 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_5,0,0}, // S_DORMANTARTI1_4 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_6,0,0}, // S_DORMANTARTI1_5 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_7,0,0}, // S_DORMANTARTI1_6 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_8,0,0}, // S_DORMANTARTI1_7 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_9,0,0}, // S_DORMANTARTI1_8 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_10,0,0}, // S_DORMANTARTI1_9 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_11,0,0}, // S_DORMANTARTI1_10 +{SPR_ACLO,0,1400,A_HideThing,S_DORMANTARTI1_12,0,0}, // S_DORMANTARTI1_11 +{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI1_13,0,0}, // S_DORMANTARTI1_12 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_14,0,0}, // S_DORMANTARTI1_13 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_15,0,0}, // S_DORMANTARTI1_14 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_16,0,0}, // S_DORMANTARTI1_15 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_17,0,0}, // S_DORMANTARTI1_16 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_18,0,0}, // S_DORMANTARTI1_17 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_19,0,0}, // S_DORMANTARTI1_18 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI1_20,0,0}, // S_DORMANTARTI1_19 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_21,0,0}, // S_DORMANTARTI1_20 +{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0}, // S_DORMANTARTI1_21 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_2,0,0}, // S_DORMANTARTI2_1 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_3,0,0}, // S_DORMANTARTI2_2 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_4,0,0}, // S_DORMANTARTI2_3 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_5,0,0}, // S_DORMANTARTI2_4 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_6,0,0}, // S_DORMANTARTI2_5 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_7,0,0}, // S_DORMANTARTI2_6 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_8,0,0}, // S_DORMANTARTI2_7 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_9,0,0}, // S_DORMANTARTI2_8 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_10,0,0}, // S_DORMANTARTI2_9 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_11,0,0}, // S_DORMANTARTI2_10 +{SPR_ACLO,0,4200,A_HideThing,S_DORMANTARTI2_12,0,0}, // S_DORMANTARTI2_11 +{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI2_13,0,0}, // S_DORMANTARTI2_12 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_14,0,0}, // S_DORMANTARTI2_13 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_15,0,0}, // S_DORMANTARTI2_14 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_16,0,0}, // S_DORMANTARTI2_15 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_17,0,0}, // S_DORMANTARTI2_16 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_18,0,0}, // S_DORMANTARTI2_17 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_19,0,0}, // S_DORMANTARTI2_18 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_20,0,0}, // S_DORMANTARTI2_19 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_21,0,0}, // S_DORMANTARTI2_20 +{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0}, // S_DORMANTARTI2_21 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_2,0,0}, // S_DORMANTARTI3_1 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_3,0,0}, // S_DORMANTARTI3_2 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_4,0,0}, // S_DORMANTARTI3_3 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_5,0,0}, // S_DORMANTARTI3_4 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_6,0,0}, // S_DORMANTARTI3_5 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_7,0,0}, // S_DORMANTARTI3_6 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_8,0,0}, // S_DORMANTARTI3_7 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_9,0,0}, // S_DORMANTARTI3_8 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_10,0,0}, // S_DORMANTARTI3_9 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_11,0,0}, // S_DORMANTARTI3_10 +{SPR_ACLO,0,21000,A_HideThing,S_DORMANTARTI3_12,0,0}, // S_DORMANTARTI3_11 +{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI3_13,0,0}, // S_DORMANTARTI3_12 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_14,0,0}, // S_DORMANTARTI3_13 +{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_15,0,0}, // S_DORMANTARTI3_14 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_16,0,0}, // S_DORMANTARTI3_15 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_17,0,0}, // S_DORMANTARTI3_16 +{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_18,0,0}, // S_DORMANTARTI3_17 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_19,0,0}, // S_DORMANTARTI3_18 +{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_20,0,0}, // S_DORMANTARTI3_19 +{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_21,0,0}, // S_DORMANTARTI3_20 +{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0}, // S_DORMANTARTI3_21 +{SPR_ACLO,3,3,NULL,S_DEADARTI2,0,0}, // S_DEADARTI1 +{SPR_ACLO,2,3,NULL,S_DEADARTI3,0,0}, // S_DEADARTI2 +{SPR_ACLO,3,3,NULL,S_DEADARTI4,0,0}, // S_DEADARTI3 +{SPR_ACLO,2,3,NULL,S_DEADARTI5,0,0}, // S_DEADARTI4 +{SPR_ACLO,1,3,NULL,S_DEADARTI6,0,0}, // S_DEADARTI5 +{SPR_ACLO,2,3,NULL,S_DEADARTI7,0,0}, // S_DEADARTI6 +{SPR_ACLO,1,3,NULL,S_DEADARTI8,0,0}, // S_DEADARTI7 +{SPR_ACLO,0,3,NULL,S_DEADARTI9,0,0}, // S_DEADARTI8 +{SPR_ACLO,1,3,NULL,S_DEADARTI10,0,0}, // S_DEADARTI9 +{SPR_ACLO,0,3,NULL,S_NULL,0,0}, // S_DEADARTI10 +{SPR_PTN2,0,4,NULL,S_ARTI_PTN2_2,0,0}, // S_ARTI_PTN2_1 +{SPR_PTN2,1,4,NULL,S_ARTI_PTN2_3,0,0}, // S_ARTI_PTN2_2 +{SPR_PTN2,2,4,NULL,S_ARTI_PTN2_1,0,0}, // S_ARTI_PTN2_3 +{SPR_SOAR,0,5,NULL,S_ARTI_SOAR2,0,0}, // S_ARTI_SOAR1 +{SPR_SOAR,1,5,NULL,S_ARTI_SOAR3,0,0}, // S_ARTI_SOAR2 +{SPR_SOAR,2,5,NULL,S_ARTI_SOAR4,0,0}, // S_ARTI_SOAR3 +{SPR_SOAR,1,5,NULL,S_ARTI_SOAR1,0,0}, // S_ARTI_SOAR4 +{SPR_INVU,0,3,NULL,S_ARTI_INVU2,0,0}, // S_ARTI_INVU1 +{SPR_INVU,1,3,NULL,S_ARTI_INVU3,0,0}, // S_ARTI_INVU2 +{SPR_INVU,2,3,NULL,S_ARTI_INVU4,0,0}, // S_ARTI_INVU3 +{SPR_INVU,3,3,NULL,S_ARTI_INVU1,0,0}, // S_ARTI_INVU4 +{SPR_SUMN,0,350,NULL,S_ARTI_SUMMON,0,0}, // S_ARTI_SUMMON +{SPR_SUMN,0,4,NULL,S_SUMMON_FX1_1,0,0}, // S_SUMMON_FX1_1 +{SPR_SUMN,0,4,NULL,S_SUMMON_FX2_2,0,0}, // S_SUMMON_FX2_1 +{SPR_SUMN,0,4,NULL,S_SUMMON_FX2_3,0,0}, // S_SUMMON_FX2_2 +{SPR_SUMN,0,4,A_Summon,S_NULL,0,0}, // S_SUMMON_FX2_3 +{SPR_TSPK,0,3,NULL,S_THRUSTINIT2_2,0,0}, // S_THRUSTINIT2_1 +{SPR_TSPK,0,4,A_ThrustInitUp,S_THRUSTBLOCK,0,0}, // S_THRUSTINIT2_2 +{SPR_TSPK,1,3,NULL,S_BTHRUSTINIT2_2,0,0}, // S_BTHRUSTINIT2_1 +{SPR_TSPK,1,4,A_ThrustInitUp,S_BTHRUSTBLOCK,0,0}, // S_BTHRUSTINIT2_2 +{SPR_TSPK,0,3,NULL,S_THRUSTINIT1_2,0,0}, // S_THRUSTINIT1_1 +{SPR_TSPK,0,4,A_ThrustInitDn,S_THRUSTSTAY,0,0}, // S_THRUSTINIT1_2 +{SPR_TSPK,1,3,NULL,S_BTHRUSTINIT1_2,0,0}, // S_BTHRUSTINIT1_1 +{SPR_TSPK,1,4,A_ThrustInitDn,S_BTHRUSTSTAY,0,0}, // S_BTHRUSTINIT1_2 +{SPR_TSPK,0,8,A_ThrustRaise,S_THRUSTRAISE2,0,0}, // S_THRUSTRAISE1 +{SPR_TSPK,0,6,A_ThrustRaise,S_THRUSTRAISE3,0,0}, // S_THRUSTRAISE2 +{SPR_TSPK,0,4,A_ThrustRaise,S_THRUSTRAISE4,0,0}, // S_THRUSTRAISE3 +{SPR_TSPK,0,3,A_ThrustBlock,S_THRUSTIMPALE,0,0}, // S_THRUSTRAISE4 +{SPR_TSPK,1,8,A_ThrustRaise,S_BTHRUSTRAISE2,0,0}, // S_BTHRUSTRAISE1 +{SPR_TSPK,1,6,A_ThrustRaise,S_BTHRUSTRAISE3,0,0}, // S_BTHRUSTRAISE2 +{SPR_TSPK,1,4,A_ThrustRaise,S_BTHRUSTRAISE4,0,0}, // S_BTHRUSTRAISE3 +{SPR_TSPK,1,3,A_ThrustBlock,S_BTHRUSTIMPALE,0,0}, // S_BTHRUSTRAISE4 +{SPR_TSPK,0,2,A_ThrustImpale,S_THRUSTRAISE,0,0}, // S_THRUSTIMPALE +{SPR_TSPK,1,2,A_ThrustImpale,S_BTHRUSTRAISE,0,0}, // S_BTHRUSTIMPALE +{SPR_TSPK,0,2,A_ThrustRaise,S_THRUSTRAISE,0,0}, // S_THRUSTRAISE +{SPR_TSPK,1,2,A_ThrustRaise,S_BTHRUSTRAISE,0,0}, // S_BTHRUSTRAISE +{SPR_TSPK,0,10,NULL,S_THRUSTBLOCK,0,0}, // S_THRUSTBLOCK +{SPR_TSPK,1,10,NULL,S_BTHRUSTBLOCK,0,0}, // S_BTHRUSTBLOCK +{SPR_TSPK,0,2,A_ThrustLower,S_THRUSTLOWER,0,0}, // S_THRUSTLOWER +{SPR_TSPK,1,2,A_ThrustLower,S_BTHRUSTLOWER,0,0}, // S_BTHRUSTLOWER +{SPR_TSPK,0,-1,NULL,S_THRUSTSTAY,0,0}, // S_THRUSTSTAY +{SPR_TSPK,1,-1,NULL,S_BTHRUSTSTAY,0,0}, // S_BTHRUSTSTAY +{SPR_TELO,0,5,NULL,S_ARTI_TELOTHER2,0,0}, // S_ARTI_TELOTHER1 +{SPR_TELO,1,5,NULL,S_ARTI_TELOTHER3,0,0}, // S_ARTI_TELOTHER2 +{SPR_TELO,2,5,NULL,S_ARTI_TELOTHER4,0,0}, // S_ARTI_TELOTHER3 +{SPR_TELO,3,5,NULL,S_ARTI_TELOTHER1,0,0}, // S_ARTI_TELOTHER4 +{SPR_TRNG,32772,5,NULL,S_TELO_FX2,0,0}, // S_TELO_FX1 +{SPR_TRNG,32771,4,NULL,S_TELO_FX3,0,0}, // S_TELO_FX2 +{SPR_TRNG,32770,3,A_TeloSpawnC,S_TELO_FX4,0,0}, // S_TELO_FX3 +{SPR_TRNG,32769,3,A_TeloSpawnB,S_TELO_FX5,0,0}, // S_TELO_FX4 +{SPR_TRNG,32768,3,A_TeloSpawnA,S_TELO_FX6,0,0}, // S_TELO_FX5 +{SPR_TRNG,32769,3,A_TeloSpawnB,S_TELO_FX7,0,0}, // S_TELO_FX6 +{SPR_TRNG,32770,3,A_TeloSpawnC,S_TELO_FX8,0,0}, // S_TELO_FX7 +{SPR_TRNG,32771,3,A_TeloSpawnD,S_TELO_FX3,0,0}, // S_TELO_FX8 +{SPR_TRNG,32772,3,NULL,S_NULL,0,0}, // S_TELO_FX9 +{SPR_TRNG,32769,4,NULL,S_TELO_FX2_2,0,0}, // S_TELO_FX2_1 +{SPR_TRNG,32770,4,NULL,S_TELO_FX2_3,0,0}, // S_TELO_FX2_2 +{SPR_TRNG,32771,4,NULL,S_TELO_FX2_4,0,0}, // S_TELO_FX2_3 +{SPR_TRNG,32770,4,NULL,S_TELO_FX2_5,0,0}, // S_TELO_FX2_4 +{SPR_TRNG,32769,4,NULL,S_TELO_FX2_6,0,0}, // S_TELO_FX2_5 +{SPR_TRNG,32768,4,A_CheckTeleRing,S_TELO_FX2_1,0,0}, // S_TELO_FX2_6 +{SPR_TRNG,32770,4,NULL,S_TELO_FX3_2,0,0}, // S_TELO_FX3_1 +{SPR_TRNG,32771,4,NULL,S_TELO_FX3_3,0,0}, // S_TELO_FX3_2 +{SPR_TRNG,32770,4,NULL,S_TELO_FX3_4,0,0}, // S_TELO_FX3_3 +{SPR_TRNG,32769,4,NULL,S_TELO_FX3_5,0,0}, // S_TELO_FX3_4 +{SPR_TRNG,32768,4,NULL,S_TELO_FX3_6,0,0}, // S_TELO_FX3_5 +{SPR_TRNG,32769,4,A_CheckTeleRing,S_TELO_FX3_1,0,0}, // S_TELO_FX3_6 +{SPR_TRNG,32771,4,NULL,S_TELO_FX4_2,0,0}, // S_TELO_FX4_1 +{SPR_TRNG,32770,4,NULL,S_TELO_FX4_3,0,0}, // S_TELO_FX4_2 +{SPR_TRNG,32769,4,NULL,S_TELO_FX4_4,0,0}, // S_TELO_FX4_3 +{SPR_TRNG,32768,4,NULL,S_TELO_FX4_5,0,0}, // S_TELO_FX4_4 +{SPR_TRNG,32769,4,NULL,S_TELO_FX4_6,0,0}, // S_TELO_FX4_5 +{SPR_TRNG,32770,4,A_CheckTeleRing,S_TELO_FX4_1,0,0}, // S_TELO_FX4_6 +{SPR_TRNG,32770,4,NULL,S_TELO_FX5_2,0,0}, // S_TELO_FX5_1 +{SPR_TRNG,32769,4,NULL,S_TELO_FX5_3,0,0}, // S_TELO_FX5_2 +{SPR_TRNG,32768,4,NULL,S_TELO_FX5_4,0,0}, // S_TELO_FX5_3 +{SPR_TRNG,32769,4,NULL,S_TELO_FX5_5,0,0}, // S_TELO_FX5_4 +{SPR_TRNG,32770,4,NULL,S_TELO_FX5_6,0,0}, // S_TELO_FX5_5 +{SPR_TRNG,32771,4,A_CheckTeleRing,S_TELO_FX5_1,0,0}, // S_TELO_FX5_6 +{SPR_ROCK,3,20,NULL,S_DIRT1_1,0,0}, // S_DIRT1_1 +{SPR_ROCK,3,10,NULL,S_NULL,0,0}, // S_DIRT1_D +{SPR_ROCK,4,20,NULL,S_DIRT2_1,0,0}, // S_DIRT2_1 +{SPR_ROCK,4,10,NULL,S_NULL,0,0}, // S_DIRT2_D +{SPR_ROCK,5,20,NULL,S_DIRT3_1,0,0}, // S_DIRT3_1 +{SPR_ROCK,5,10,NULL,S_NULL,0,0}, // S_DIRT3_D +{SPR_ROCK,6,20,NULL,S_DIRT4_1,0,0}, // S_DIRT4_1 +{SPR_ROCK,6,10,NULL,S_NULL,0,0}, // S_DIRT4_D +{SPR_ROCK,7,20,NULL,S_DIRT5_1,0,0}, // S_DIRT5_1 +{SPR_ROCK,7,10,NULL,S_NULL,0,0}, // S_DIRT5_D +{SPR_ROCK,8,20,NULL,S_DIRT6_1,0,0}, // S_DIRT6_1 +{SPR_ROCK,8,10,NULL,S_NULL,0,0}, // S_DIRT6_D +{SPR_TSPK,2,20,NULL,S_DIRTCLUMP1,0,0}, // S_DIRTCLUMP1 +{SPR_ROCK,0,20,NULL,S_ROCK1_1,0,0}, // S_ROCK1_1 +{SPR_ROCK,0,10,NULL,S_NULL,0,0}, // S_ROCK1_D +{SPR_ROCK,1,20,NULL,S_ROCK2_1,0,0}, // S_ROCK2_1 +{SPR_ROCK,1,10,NULL,S_NULL,0,0}, // S_ROCK2_D +{SPR_ROCK,2,20,NULL,S_ROCK3_1,0,0}, // S_ROCK3_1 +{SPR_ROCK,2,10,NULL,S_NULL,0,0}, // S_ROCK3_D +{SPR_MAN1,0,20,A_FogSpawn,S_SPAWNFOG1,0,0}, // S_SPAWNFOG1 +{SPR_FOGS,0,7,A_FogMove,S_FOGPATCHS2,0,0}, // S_FOGPATCHS1 +{SPR_FOGS,1,7,A_FogMove,S_FOGPATCHS3,0,0}, // S_FOGPATCHS2 +{SPR_FOGS,2,7,A_FogMove,S_FOGPATCHS4,0,0}, // S_FOGPATCHS3 +{SPR_FOGS,3,7,A_FogMove,S_FOGPATCHS5,0,0}, // S_FOGPATCHS4 +{SPR_FOGS,4,7,A_FogMove,S_FOGPATCHS1,0,0}, // S_FOGPATCHS5 +{SPR_FOGS,4,5,NULL,S_NULL,0,0}, // S_FOGPATCHS0 +{SPR_FOGM,0,7,A_FogMove,S_FOGPATCHM2,0,0}, // S_FOGPATCHM1 +{SPR_FOGM,1,7,A_FogMove,S_FOGPATCHM3,0,0}, // S_FOGPATCHM2 +{SPR_FOGM,2,7,A_FogMove,S_FOGPATCHM4,0,0}, // S_FOGPATCHM3 +{SPR_FOGM,3,7,A_FogMove,S_FOGPATCHM5,0,0}, // S_FOGPATCHM4 +{SPR_FOGM,4,7,A_FogMove,S_FOGPATCHM1,0,0}, // S_FOGPATCHM5 +{SPR_FOGS,0,5,NULL,S_FOGPATCHMA,0,0}, // S_FOGPATCHM0 +{SPR_FOGS,1,5,NULL,S_FOGPATCHMB,0,0}, // S_FOGPATCHMA +{SPR_FOGS,2,5,NULL,S_FOGPATCHMC,0,0}, // S_FOGPATCHMB +{SPR_FOGS,3,5,NULL,S_FOGPATCHMD,0,0}, // S_FOGPATCHMC +{SPR_FOGS,4,5,NULL,S_FOGPATCHS0,0,0}, // S_FOGPATCHMD +{SPR_FOGL,0,7,A_FogMove,S_FOGPATCHL2,0,0}, // S_FOGPATCHL1 +{SPR_FOGL,1,7,A_FogMove,S_FOGPATCHL3,0,0}, // S_FOGPATCHL2 +{SPR_FOGL,2,7,A_FogMove,S_FOGPATCHL4,0,0}, // S_FOGPATCHL3 +{SPR_FOGL,3,7,A_FogMove,S_FOGPATCHL5,0,0}, // S_FOGPATCHL4 +{SPR_FOGL,4,7,A_FogMove,S_FOGPATCHL1,0,0}, // S_FOGPATCHL5 +{SPR_FOGM,0,4,NULL,S_FOGPATCHLA,0,0}, // S_FOGPATCHL0 +{SPR_FOGM,1,4,NULL,S_FOGPATCHLB,0,0}, // S_FOGPATCHLA +{SPR_FOGM,2,4,NULL,S_FOGPATCHLC,0,0}, // S_FOGPATCHLB +{SPR_FOGM,3,4,NULL,S_FOGPATCHLD,0,0}, // S_FOGPATCHLC +{SPR_FOGM,4,4,NULL,S_FOGPATCHM0,0,0}, // S_FOGPATCHLD +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE2,0,0}, // S_QUAKE_ACTIVE1 +{SPR_MAN1,0,1,A_ContMobjSound,S_QUAKE_ACTIVE3,0,0}, // S_QUAKE_ACTIVE2 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE4,0,0}, // S_QUAKE_ACTIVE3 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE5,0,0}, // S_QUAKE_ACTIVE4 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE6,0,0}, // S_QUAKE_ACTIVE5 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE7,0,0}, // S_QUAKE_ACTIVE6 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE8,0,0}, // S_QUAKE_ACTIVE7 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE9,0,0}, // S_QUAKE_ACTIVE8 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE0,0,0}, // S_QUAKE_ACTIVE9 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEA,0,0}, // S_QUAKE_ACTIVE0 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEB,0,0}, // S_QUAKE_ACTIVEA +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEC,0,0}, // S_QUAKE_ACTIVEB +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVED,0,0}, // S_QUAKE_ACTIVEC +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEE,0,0}, // S_QUAKE_ACTIVED +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEF,0,0}, // S_QUAKE_ACTIVEE +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEG,0,0}, // S_QUAKE_ACTIVEF +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEH,0,0}, // S_QUAKE_ACTIVEG +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEI,0,0}, // S_QUAKE_ACTIVEH +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEJ,0,0}, // S_QUAKE_ACTIVEI +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEK,0,0}, // S_QUAKE_ACTIVEJ +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEL,0,0}, // S_QUAKE_ACTIVEK +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEM,0,0}, // S_QUAKE_ACTIVEL +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEN,0,0}, // S_QUAKE_ACTIVEM +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEO,0,0}, // S_QUAKE_ACTIVEN +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEP,0,0}, // S_QUAKE_ACTIVEO +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEQ,0,0}, // S_QUAKE_ACTIVEP +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVER,0,0}, // S_QUAKE_ACTIVEQ +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVES,0,0}, // S_QUAKE_ACTIVER +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVET,0,0}, // S_QUAKE_ACTIVES +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEU,0,0}, // S_QUAKE_ACTIVET +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEV,0,0}, // S_QUAKE_ACTIVEU +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEW,0,0}, // S_QUAKE_ACTIVEV +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEX,0,0}, // S_QUAKE_ACTIVEW +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEY,0,0}, // S_QUAKE_ACTIVEX +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEZ,0,0}, // S_QUAKE_ACTIVEY +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT1,0,0}, // S_QUAKE_ACTIVEZ +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT2,0,0}, // S_QUAKE_ACT1 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT3,0,0}, // S_QUAKE_ACT2 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT4,0,0}, // S_QUAKE_ACT3 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT5,0,0}, // S_QUAKE_ACT4 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT6,0,0}, // S_QUAKE_ACT5 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT7,0,0}, // S_QUAKE_ACT6 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT8,0,0}, // S_QUAKE_ACT7 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT9,0,0}, // S_QUAKE_ACT8 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT0,0,0}, // S_QUAKE_ACT9 +{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE1,0,0}, // S_QUAKE_ACT0 +{SPR_SGSA,0,4,NULL,S_SGSHARD1_2,0,0}, // S_SGSHARD1_1 +{SPR_SGSA,1,4,NULL,S_SGSHARD1_3,0,0}, // S_SGSHARD1_2 +{SPR_SGSA,2,4,NULL,S_SGSHARD1_4,0,0}, // S_SGSHARD1_3 +{SPR_SGSA,3,4,NULL,S_SGSHARD1_5,0,0}, // S_SGSHARD1_4 +{SPR_SGSA,4,4,NULL,S_SGSHARD1_1,0,0}, // S_SGSHARD1_5 +{SPR_SGSA,4,30,NULL,S_NULL,0,0}, // S_SGSHARD1_D +{SPR_SGSA,5,4,NULL,S_SGSHARD2_2,0,0}, // S_SGSHARD2_1 +{SPR_SGSA,6,4,NULL,S_SGSHARD2_3,0,0}, // S_SGSHARD2_2 +{SPR_SGSA,7,4,NULL,S_SGSHARD2_4,0,0}, // S_SGSHARD2_3 +{SPR_SGSA,8,4,NULL,S_SGSHARD2_5,0,0}, // S_SGSHARD2_4 +{SPR_SGSA,9,4,NULL,S_SGSHARD2_1,0,0}, // S_SGSHARD2_5 +{SPR_SGSA,9,30,NULL,S_NULL,0,0}, // S_SGSHARD2_D +{SPR_SGSA,10,4,NULL,S_SGSHARD3_2,0,0}, // S_SGSHARD3_1 +{SPR_SGSA,11,4,NULL,S_SGSHARD3_3,0,0}, // S_SGSHARD3_2 +{SPR_SGSA,12,4,NULL,S_SGSHARD3_4,0,0}, // S_SGSHARD3_3 +{SPR_SGSA,13,4,NULL,S_SGSHARD3_5,0,0}, // S_SGSHARD3_4 +{SPR_SGSA,14,4,NULL,S_SGSHARD3_1,0,0}, // S_SGSHARD3_5 +{SPR_SGSA,14,30,NULL,S_NULL,0,0}, // S_SGSHARD3_D +{SPR_SGSA,15,4,NULL,S_SGSHARD4_2,0,0}, // S_SGSHARD4_1 +{SPR_SGSA,16,4,NULL,S_SGSHARD4_3,0,0}, // S_SGSHARD4_2 +{SPR_SGSA,17,4,NULL,S_SGSHARD4_4,0,0}, // S_SGSHARD4_3 +{SPR_SGSA,18,4,NULL,S_SGSHARD4_5,0,0}, // S_SGSHARD4_4 +{SPR_SGSA,19,4,NULL,S_SGSHARD4_1,0,0}, // S_SGSHARD4_5 +{SPR_SGSA,19,30,NULL,S_NULL,0,0}, // S_SGSHARD4_D +{SPR_SGSA,20,4,NULL,S_SGSHARD5_2,0,0}, // S_SGSHARD5_1 +{SPR_SGSA,21,4,NULL,S_SGSHARD5_3,0,0}, // S_SGSHARD5_2 +{SPR_SGSA,22,4,NULL,S_SGSHARD5_4,0,0}, // S_SGSHARD5_3 +{SPR_SGSA,23,4,NULL,S_SGSHARD5_5,0,0}, // S_SGSHARD5_4 +{SPR_SGSA,24,4,NULL,S_SGSHARD5_1,0,0}, // S_SGSHARD5_5 +{SPR_SGSA,24,30,NULL,S_NULL,0,0}, // S_SGSHARD5_D +{SPR_SGSB,0,4,NULL,S_SGSHARD6_1,0,0}, // S_SGSHARD6_1 +{SPR_SGSB,0,30,NULL,S_NULL,0,0}, // S_SGSHARD6_D +{SPR_SGSB,1,4,NULL,S_SGSHARD7_1,0,0}, // S_SGSHARD7_1 +{SPR_SGSB,1,30,NULL,S_NULL,0,0}, // S_SGSHARD7_D +{SPR_SGSB,2,4,NULL,S_SGSHARD8_1,0,0}, // S_SGSHARD8_1 +{SPR_SGSB,2,30,NULL,S_NULL,0,0}, // S_SGSHARD8_D +{SPR_SGSB,3,4,NULL,S_SGSHARD9_1,0,0}, // S_SGSHARD9_1 +{SPR_SGSB,3,30,NULL,S_NULL,0,0}, // S_SGSHARD9_D +{SPR_SGSB,4,4,NULL,S_SGSHARD0_1,0,0}, // S_SGSHARD0_1 +{SPR_SGSB,4,30,NULL,S_NULL,0,0}, // S_SGSHARD0_D +{SPR_PORK,0,5,NULL,S_ARTI_EGGC2,0,0}, // S_ARTI_EGGC1 +{SPR_PORK,1,5,NULL,S_ARTI_EGGC3,0,0}, // S_ARTI_EGGC2 +{SPR_PORK,2,5,NULL,S_ARTI_EGGC4,0,0}, // S_ARTI_EGGC3 +{SPR_PORK,3,5,NULL,S_ARTI_EGGC5,0,0}, // S_ARTI_EGGC4 +{SPR_PORK,4,5,NULL,S_ARTI_EGGC6,0,0}, // S_ARTI_EGGC5 +{SPR_PORK,5,5,NULL,S_ARTI_EGGC7,0,0}, // S_ARTI_EGGC6 +{SPR_PORK,6,5,NULL,S_ARTI_EGGC8,0,0}, // S_ARTI_EGGC7 +{SPR_PORK,7,5,NULL,S_ARTI_EGGC1,0,0}, // S_ARTI_EGGC8 +{SPR_EGGM,0,4,NULL,S_EGGFX2,0,0}, // S_EGGFX1 +{SPR_EGGM,1,4,NULL,S_EGGFX3,0,0}, // S_EGGFX2 +{SPR_EGGM,2,4,NULL,S_EGGFX4,0,0}, // S_EGGFX3 +{SPR_EGGM,3,4,NULL,S_EGGFX5,0,0}, // S_EGGFX4 +{SPR_EGGM,4,4,NULL,S_EGGFX1,0,0}, // S_EGGFX5 +{SPR_FHFX,32776,3,NULL,S_EGGFXI1_2,0,0}, // S_EGGFXI1_1 +{SPR_FHFX,32777,3,NULL,S_EGGFXI1_3,0,0}, // S_EGGFXI1_2 +{SPR_FHFX,32778,3,NULL,S_EGGFXI1_4,0,0}, // S_EGGFXI1_3 +{SPR_FHFX,32779,3,NULL,S_NULL,0,0}, // S_EGGFXI1_4 +{SPR_SPHL,0,350,NULL,S_ARTI_SPHL1,0,0}, // S_ARTI_SPHL1 +{SPR_STWN,0,-1,NULL,S_NULL,0,0}, // S_ZWINGEDSTATUENOSKULL +{SPR_STWN,1,-1,NULL,S_NULL,0,0}, // S_ZWINGEDSTATUENOSKULL2 +{SPR_GMPD,0,-1,NULL,S_NULL,0,0}, // S_ZGEMPEDESTAL1 +{SPR_GMPD,1,-1,NULL,S_NULL,0,0}, // S_ZGEMPEDESTAL2 +{SPR_ASKU,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZSKULL +{SPR_ABGM,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMBIG +{SPR_AGMR,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMRED +{SPR_AGMG,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMGREEN1 +{SPR_AGG2,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMGREEN2 +{SPR_AGMB,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMBLUE1 +{SPR_AGB2,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZGEMBLUE2 +{SPR_ABK1,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZBOOK1 +{SPR_ABK2,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZBOOK2 +{SPR_ASK2,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZSKULL2 +{SPR_AFWP,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZFWEAPON +{SPR_ACWP,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZCWEAPON +{SPR_AMWP,0,-1,NULL,S_NULL,0,0}, // S_ARTIPUZZMWEAPON +{SPR_AGER,32768,4,NULL,S_ARTIPUZZGEAR_2,0,0}, // S_ARTIPUZZGEAR_1 +{SPR_AGER,32769,4,NULL,S_ARTIPUZZGEAR_3,0,0}, // S_ARTIPUZZGEAR_2 +{SPR_AGER,32770,4,NULL,S_ARTIPUZZGEAR_4,0,0}, // S_ARTIPUZZGEAR_3 +{SPR_AGER,32771,4,NULL,S_ARTIPUZZGEAR_5,0,0}, // S_ARTIPUZZGEAR_4 +{SPR_AGER,32772,4,NULL,S_ARTIPUZZGEAR_6,0,0}, // S_ARTIPUZZGEAR_5 +{SPR_AGER,32773,4,NULL,S_ARTIPUZZGEAR_7,0,0}, // S_ARTIPUZZGEAR_6 +{SPR_AGER,32774,4,NULL,S_ARTIPUZZGEAR_8,0,0}, // S_ARTIPUZZGEAR_7 +{SPR_AGER,32775,4,NULL,S_ARTIPUZZGEAR_1,0,0}, // S_ARTIPUZZGEAR_8 +{SPR_AGR2,32768,4,NULL,S_ARTIPUZZGEAR2_2,0,0}, // S_ARTIPUZZGEAR2_1 +{SPR_AGR2,32769,4,NULL,S_ARTIPUZZGEAR2_3,0,0}, // S_ARTIPUZZGEAR2_2 +{SPR_AGR2,32770,4,NULL,S_ARTIPUZZGEAR2_4,0,0}, // S_ARTIPUZZGEAR2_3 +{SPR_AGR2,32771,4,NULL,S_ARTIPUZZGEAR2_5,0,0}, // S_ARTIPUZZGEAR2_4 +{SPR_AGR2,32772,4,NULL,S_ARTIPUZZGEAR2_6,0,0}, // S_ARTIPUZZGEAR2_5 +{SPR_AGR2,32773,4,NULL,S_ARTIPUZZGEAR2_7,0,0}, // S_ARTIPUZZGEAR2_6 +{SPR_AGR2,32774,4,NULL,S_ARTIPUZZGEAR2_8,0,0}, // S_ARTIPUZZGEAR2_7 +{SPR_AGR2,32775,4,NULL,S_ARTIPUZZGEAR2_1,0,0}, // S_ARTIPUZZGEAR2_8 +{SPR_AGR3,32768,4,NULL,S_ARTIPUZZGEAR3_2,0,0}, // S_ARTIPUZZGEAR3_1 +{SPR_AGR3,32769,4,NULL,S_ARTIPUZZGEAR3_3,0,0}, // S_ARTIPUZZGEAR3_2 +{SPR_AGR3,32770,4,NULL,S_ARTIPUZZGEAR3_4,0,0}, // S_ARTIPUZZGEAR3_3 +{SPR_AGR3,32771,4,NULL,S_ARTIPUZZGEAR3_5,0,0}, // S_ARTIPUZZGEAR3_4 +{SPR_AGR3,32772,4,NULL,S_ARTIPUZZGEAR3_6,0,0}, // S_ARTIPUZZGEAR3_5 +{SPR_AGR3,32773,4,NULL,S_ARTIPUZZGEAR3_7,0,0}, // S_ARTIPUZZGEAR3_6 +{SPR_AGR3,32774,4,NULL,S_ARTIPUZZGEAR3_8,0,0}, // S_ARTIPUZZGEAR3_7 +{SPR_AGR3,32775,4,NULL,S_ARTIPUZZGEAR3_1,0,0}, // S_ARTIPUZZGEAR3_8 +{SPR_AGR4,32768,4,NULL,S_ARTIPUZZGEAR4_2,0,0}, // S_ARTIPUZZGEAR4_1 +{SPR_AGR4,32769,4,NULL,S_ARTIPUZZGEAR4_3,0,0}, // S_ARTIPUZZGEAR4_2 +{SPR_AGR4,32770,4,NULL,S_ARTIPUZZGEAR4_4,0,0}, // S_ARTIPUZZGEAR4_3 +{SPR_AGR4,32771,4,NULL,S_ARTIPUZZGEAR4_5,0,0}, // S_ARTIPUZZGEAR4_4 +{SPR_AGR4,32772,4,NULL,S_ARTIPUZZGEAR4_6,0,0}, // S_ARTIPUZZGEAR4_5 +{SPR_AGR4,32773,4,NULL,S_ARTIPUZZGEAR4_7,0,0}, // S_ARTIPUZZGEAR4_6 +{SPR_AGR4,32774,4,NULL,S_ARTIPUZZGEAR4_8,0,0}, // S_ARTIPUZZGEAR4_7 +{SPR_AGR4,32775,4,NULL,S_ARTIPUZZGEAR4_1,0,0}, // S_ARTIPUZZGEAR4_8 +{SPR_TRCH,32768,3,NULL,S_ARTI_TRCH2,0,0}, // S_ARTI_TRCH1 +{SPR_TRCH,32769,3,NULL,S_ARTI_TRCH3,0,0}, // S_ARTI_TRCH2 +{SPR_TRCH,32770,3,NULL,S_ARTI_TRCH1,0,0}, // S_ARTI_TRCH3 +{SPR_PSBG,0,20,NULL,S_FIREBOMB2,0,0}, // S_FIREBOMB1 +{SPR_PSBG,0,10,NULL,S_FIREBOMB3,0,0}, // S_FIREBOMB2 +{SPR_PSBG,0,10,NULL,S_FIREBOMB4,0,0}, // S_FIREBOMB3 +{SPR_PSBG,1,4,NULL,S_FIREBOMB5,0,0}, // S_FIREBOMB4 +{SPR_PSBG,2,4,A_Scream,S_FIREBOMB6,0,0}, // S_FIREBOMB5 +{SPR_XPL1,32768,4,A_Explode,S_FIREBOMB7,0,0}, // S_FIREBOMB6 +{SPR_XPL1,32769,4,NULL,S_FIREBOMB8,0,0}, // S_FIREBOMB7 +{SPR_XPL1,32770,4,NULL,S_FIREBOMB9,0,0}, // S_FIREBOMB8 +{SPR_XPL1,32771,4,NULL,S_FIREBOMB10,0,0}, // S_FIREBOMB9 +{SPR_XPL1,32772,4,NULL,S_FIREBOMB11,0,0}, // S_FIREBOMB10 +{SPR_XPL1,32773,4,NULL,S_NULL,0,0}, // S_FIREBOMB11 +{SPR_ATLP,0,4,NULL,S_ARTI_ATLP2,0,0}, // S_ARTI_ATLP1 +{SPR_ATLP,1,4,NULL,S_ARTI_ATLP3,0,0}, // S_ARTI_ATLP2 +{SPR_ATLP,2,4,NULL,S_ARTI_ATLP4,0,0}, // S_ARTI_ATLP3 +{SPR_ATLP,1,4,NULL,S_ARTI_ATLP1,0,0}, // S_ARTI_ATLP4 +{SPR_PSBG,0,-1,NULL,S_NULL,0,0}, // S_ARTI_PSBG1 +{SPR_PSBG,32768,18,NULL,S_POISONBAG2,0,0}, // S_POISONBAG1 +{SPR_PSBG,32769,4,NULL,S_POISONBAG3,0,0}, // S_POISONBAG2 +{SPR_PSBG,2,3,NULL,S_POISONBAG4,0,0}, // S_POISONBAG3 +{SPR_PSBG,2,1,A_PoisonBagInit,S_NULL,0,0}, // S_POISONBAG4 +{SPR_PSBG,3,1,NULL,S_POISONCLOUD2,0,0}, // S_POISONCLOUD1 +{SPR_PSBG,3,1,A_Scream,S_POISONCLOUD3,0,0}, // S_POISONCLOUD2 +{SPR_PSBG,3,2,A_PoisonBagDamage,S_POISONCLOUD4,0,0}, // S_POISONCLOUD3 +{SPR_PSBG,4,2,A_PoisonBagDamage,S_POISONCLOUD5,0,0}, // S_POISONCLOUD4 +{SPR_PSBG,4,2,A_PoisonBagDamage,S_POISONCLOUD6,0,0}, // S_POISONCLOUD5 +{SPR_PSBG,4,2,A_PoisonBagDamage,S_POISONCLOUD7,0,0}, // S_POISONCLOUD6 +{SPR_PSBG,5,2,A_PoisonBagDamage,S_POISONCLOUD8,0,0}, // S_POISONCLOUD7 +{SPR_PSBG,5,2,A_PoisonBagDamage,S_POISONCLOUD9,0,0}, // S_POISONCLOUD8 +{SPR_PSBG,5,2,A_PoisonBagDamage,S_POISONCLOUD10,0,0}, // S_POISONCLOUD9 +{SPR_PSBG,6,2,A_PoisonBagDamage,S_POISONCLOUD11,0,0}, // S_POISONCLOUD10 +{SPR_PSBG,6,2,A_PoisonBagDamage,S_POISONCLOUD12,0,0}, // S_POISONCLOUD11 +{SPR_PSBG,6,2,A_PoisonBagDamage,S_POISONCLOUD13,0,0}, // S_POISONCLOUD12 +{SPR_PSBG,7,2,A_PoisonBagDamage,S_POISONCLOUD14,0,0}, // S_POISONCLOUD13 +{SPR_PSBG,7,2,A_PoisonBagDamage,S_POISONCLOUD15,0,0}, // S_POISONCLOUD14 +{SPR_PSBG,7,2,A_PoisonBagDamage,S_POISONCLOUD16,0,0}, // S_POISONCLOUD15 +{SPR_PSBG,8,2,A_PoisonBagDamage,S_POISONCLOUD17,0,0}, // S_POISONCLOUD16 +{SPR_PSBG,8,1,A_PoisonBagDamage,S_POISONCLOUD18,0,0}, // S_POISONCLOUD17 +{SPR_PSBG,8,1,A_PoisonBagCheck,S_POISONCLOUD4,0,0}, // S_POISONCLOUD18 +{SPR_PSBG,7,7,NULL,S_POISONCLOUD_X2,0,0}, // S_POISONCLOUD_X1 +{SPR_PSBG,6,7,NULL,S_POISONCLOUD_X3,0,0}, // S_POISONCLOUD_X2 +{SPR_PSBG,5,6,NULL,S_POISONCLOUD_X4,0,0}, // S_POISONCLOUD_X3 +{SPR_PSBG,3,6,NULL,S_NULL,0,0}, // S_POISONCLOUD_X4 +{SPR_THRW,0,4,A_CheckThrowBomb,S_THROWINGBOMB2,0,0}, // S_THROWINGBOMB1 +{SPR_THRW,1,3,A_CheckThrowBomb,S_THROWINGBOMB3,0,0}, // S_THROWINGBOMB2 +{SPR_THRW,2,3,A_CheckThrowBomb,S_THROWINGBOMB4,0,0}, // S_THROWINGBOMB3 +{SPR_THRW,3,3,A_CheckThrowBomb,S_THROWINGBOMB5,0,0}, // S_THROWINGBOMB4 +{SPR_THRW,4,3,A_CheckThrowBomb,S_THROWINGBOMB6,0,0}, // S_THROWINGBOMB5 +{SPR_THRW,5,3,A_CheckThrowBomb,S_THROWINGBOMB1,0,0}, // S_THROWINGBOMB6 +{SPR_THRW,6,6,A_CheckThrowBomb,S_THROWINGBOMB8,0,0}, // S_THROWINGBOMB7 +{SPR_THRW,5,4,A_CheckThrowBomb,S_THROWINGBOMB9,0,0}, // S_THROWINGBOMB8 +{SPR_THRW,7,6,A_CheckThrowBomb,S_THROWINGBOMB10,0,0}, // S_THROWINGBOMB9 +{SPR_THRW,5,4,A_CheckThrowBomb,S_THROWINGBOMB11,0,0}, // S_THROWINGBOMB10 +{SPR_THRW,6,6,A_CheckThrowBomb,S_THROWINGBOMB12,0,0}, // S_THROWINGBOMB11 +{SPR_THRW,5,3,A_CheckThrowBomb,S_THROWINGBOMB12,0,0}, // S_THROWINGBOMB12 +{SPR_CFCF,32784,4,A_NoGravity,S_THROWINGBOMB_X2,0,0}, // S_THROWINGBOMB_X1 +{SPR_CFCF,32785,3,A_Scream,S_THROWINGBOMB_X3,0,0}, // S_THROWINGBOMB_X2 +{SPR_CFCF,32786,4,A_Explode,S_THROWINGBOMB_X4,0,0}, // S_THROWINGBOMB_X3 +{SPR_CFCF,32787,3,NULL,S_THROWINGBOMB_X5,0,0}, // S_THROWINGBOMB_X4 +{SPR_CFCF,32788,4,NULL,S_THROWINGBOMB_X6,0,0}, // S_THROWINGBOMB_X5 +{SPR_CFCF,32790,3,NULL,S_THROWINGBOMB_X7,0,0}, // S_THROWINGBOMB_X6 +{SPR_CFCF,32791,4,NULL,S_THROWINGBOMB_X8,0,0}, // S_THROWINGBOMB_X7 +{SPR_CFCF,32793,3,NULL,S_NULL,0,0}, // S_THROWINGBOMB_X8 +{SPR_SPED,32768,3,NULL,S_ARTI_BOOTS2,0,0}, // S_ARTI_BOOTS1 +{SPR_SPED,32769,3,NULL,S_ARTI_BOOTS3,0,0}, // S_ARTI_BOOTS2 +{SPR_SPED,32770,3,NULL,S_ARTI_BOOTS4,0,0}, // S_ARTI_BOOTS3 +{SPR_SPED,32771,3,NULL,S_ARTI_BOOTS5,0,0}, // S_ARTI_BOOTS4 +{SPR_SPED,32772,3,NULL,S_ARTI_BOOTS6,0,0}, // S_ARTI_BOOTS5 +{SPR_SPED,32773,3,NULL,S_ARTI_BOOTS7,0,0}, // S_ARTI_BOOTS6 +{SPR_SPED,32774,3,NULL,S_ARTI_BOOTS8,0,0}, // S_ARTI_BOOTS7 +{SPR_SPED,32775,3,NULL,S_ARTI_BOOTS1,0,0}, // S_ARTI_BOOTS8 +{SPR_BMAN,32768,-1,NULL,S_NULL,0,0}, // S_ARTI_MANA +{SPR_BRAC,32768,4,NULL,S_ARTI_ARMOR2,0,0}, // S_ARTI_ARMOR1 +{SPR_BRAC,32769,4,NULL,S_ARTI_ARMOR3,0,0}, // S_ARTI_ARMOR2 +{SPR_BRAC,32770,4,NULL,S_ARTI_ARMOR4,0,0}, // S_ARTI_ARMOR3 +{SPR_BRAC,32771,4,NULL,S_ARTI_ARMOR5,0,0}, // S_ARTI_ARMOR4 +{SPR_BRAC,32772,4,NULL,S_ARTI_ARMOR6,0,0}, // S_ARTI_ARMOR5 +{SPR_BRAC,32773,4,NULL,S_ARTI_ARMOR7,0,0}, // S_ARTI_ARMOR6 +{SPR_BRAC,32774,4,NULL,S_ARTI_ARMOR8,0,0}, // S_ARTI_ARMOR7 +{SPR_BRAC,32775,4,NULL,S_ARTI_ARMOR1,0,0}, // S_ARTI_ARMOR8 +{SPR_BLST,32768,4,NULL,S_ARTI_BLAST2,0,0}, // S_ARTI_BLAST1 +{SPR_BLST,32769,4,NULL,S_ARTI_BLAST3,0,0}, // S_ARTI_BLAST2 +{SPR_BLST,32770,4,NULL,S_ARTI_BLAST4,0,0}, // S_ARTI_BLAST3 +{SPR_BLST,32771,4,NULL,S_ARTI_BLAST5,0,0}, // S_ARTI_BLAST4 +{SPR_BLST,32772,4,NULL,S_ARTI_BLAST6,0,0}, // S_ARTI_BLAST5 +{SPR_BLST,32773,4,NULL,S_ARTI_BLAST7,0,0}, // S_ARTI_BLAST6 +{SPR_BLST,32774,4,NULL,S_ARTI_BLAST8,0,0}, // S_ARTI_BLAST7 +{SPR_BLST,32775,4,NULL,S_ARTI_BLAST1,0,0}, // S_ARTI_BLAST8 +{SPR_HRAD,32768,4,NULL,S_ARTI_HEALRAD2,0,0}, // S_ARTI_HEALRAD1 +{SPR_HRAD,32769,4,NULL,S_ARTI_HEALRAD3,0,0}, // S_ARTI_HEALRAD2 +{SPR_HRAD,32770,4,NULL,S_ARTI_HEALRAD4,0,0}, // S_ARTI_HEALRAD3 +{SPR_HRAD,32771,4,NULL,S_ARTI_HEALRAD5,0,0}, // S_ARTI_HEALRAD4 +{SPR_HRAD,32772,4,NULL,S_ARTI_HEALRAD6,0,0}, // S_ARTI_HEALRAD5 +{SPR_HRAD,32773,4,NULL,S_ARTI_HEALRAD7,0,0}, // S_ARTI_HEALRAD6 +{SPR_HRAD,32774,4,NULL,S_ARTI_HEALRAD8,0,0}, // S_ARTI_HEALRAD7 +{SPR_HRAD,32775,4,NULL,S_ARTI_HEALRAD9,0,0}, // S_ARTI_HEALRAD8 +{SPR_HRAD,32776,4,NULL,S_ARTI_HEALRAD0,0,0}, // S_ARTI_HEALRAD9 +{SPR_HRAD,32777,4,NULL,S_ARTI_HEALRADA,0,0}, // S_ARTI_HEALRAD0 +{SPR_HRAD,32778,4,NULL,S_ARTI_HEALRADB,0,0}, // S_ARTI_HEALRADA +{SPR_HRAD,32779,4,NULL,S_ARTI_HEALRADC,0,0}, // S_ARTI_HEALRADB +{SPR_HRAD,32780,4,NULL,S_ARTI_HEALRADD,0,0}, // S_ARTI_HEALRADC +{SPR_HRAD,32781,4,NULL,S_ARTI_HEALRADE,0,0}, // S_ARTI_HEALRADD +{SPR_HRAD,32782,4,NULL,S_ARTI_HEALRADF,0,0}, // S_ARTI_HEALRADE +{SPR_HRAD,32783,4,NULL,S_ARTI_HEALRAD1,0,0}, // S_ARTI_HEALRADF +{SPR_SPSH,0,8,NULL,S_SPLASH2,0,0}, // S_SPLASH1 +{SPR_SPSH,1,8,NULL,S_SPLASH3,0,0}, // S_SPLASH2 +{SPR_SPSH,2,8,NULL,S_SPLASH4,0,0}, // S_SPLASH3 +{SPR_SPSH,3,16,NULL,S_NULL,0,0}, // S_SPLASH4 +{SPR_SPSH,3,10,NULL,S_NULL,0,0}, // S_SPLASHX +{SPR_SPSH,4,5,NULL,S_SPLASHBASE2,0,0}, // S_SPLASHBASE1 +{SPR_SPSH,5,5,NULL,S_SPLASHBASE3,0,0}, // S_SPLASHBASE2 +{SPR_SPSH,6,5,NULL,S_SPLASHBASE4,0,0}, // S_SPLASHBASE3 +{SPR_SPSH,7,5,NULL,S_SPLASHBASE5,0,0}, // S_SPLASHBASE4 +{SPR_SPSH,8,5,NULL,S_SPLASHBASE6,0,0}, // S_SPLASHBASE5 +{SPR_SPSH,9,5,NULL,S_SPLASHBASE7,0,0}, // S_SPLASHBASE6 +{SPR_SPSH,10,5,NULL,S_NULL,0,0}, // S_SPLASHBASE7 +{SPR_LVAS,32768,5,NULL,S_LAVASPLASH2,0,0}, // S_LAVASPLASH1 +{SPR_LVAS,32769,5,NULL,S_LAVASPLASH3,0,0}, // S_LAVASPLASH2 +{SPR_LVAS,32770,5,NULL,S_LAVASPLASH4,0,0}, // S_LAVASPLASH3 +{SPR_LVAS,32771,5,NULL,S_LAVASPLASH5,0,0}, // S_LAVASPLASH4 +{SPR_LVAS,32772,5,NULL,S_LAVASPLASH6,0,0}, // S_LAVASPLASH5 +{SPR_LVAS,32773,5,NULL,S_NULL,0,0}, // S_LAVASPLASH6 +{SPR_LVAS,32774,5,NULL,S_LAVASMOKE2,0,0}, // S_LAVASMOKE1 +{SPR_LVAS,32775,5,NULL,S_LAVASMOKE3,0,0}, // S_LAVASMOKE2 +{SPR_LVAS,32776,5,NULL,S_LAVASMOKE4,0,0}, // S_LAVASMOKE3 +{SPR_LVAS,32777,5,NULL,S_LAVASMOKE5,0,0}, // S_LAVASMOKE4 +{SPR_LVAS,32778,5,NULL,S_NULL,0,0}, // S_LAVASMOKE5 +{SPR_SLDG,0,8,NULL,S_SLUDGECHUNK2,0,0}, // S_SLUDGECHUNK1 +{SPR_SLDG,1,8,NULL,S_SLUDGECHUNK3,0,0}, // S_SLUDGECHUNK2 +{SPR_SLDG,2,8,NULL,S_SLUDGECHUNK4,0,0}, // S_SLUDGECHUNK3 +{SPR_SLDG,3,8,NULL,S_NULL,0,0}, // S_SLUDGECHUNK4 +{SPR_SLDG,3,6,NULL,S_NULL,0,0}, // S_SLUDGECHUNKX +{SPR_SLDG,4,6,NULL,S_SLUDGESPLASH2,0,0}, // S_SLUDGESPLASH1 +{SPR_SLDG,5,6,NULL,S_SLUDGESPLASH3,0,0}, // S_SLUDGESPLASH2 +{SPR_SLDG,6,6,NULL,S_SLUDGESPLASH4,0,0}, // S_SLUDGESPLASH3 +{SPR_SLDG,7,6,NULL,S_NULL,0,0}, // S_SLUDGESPLASH4 +{SPR_STTW,0,-1,NULL,S_NULL,0,0}, // S_ZWINGEDSTATUE1 +{SPR_RCK1,0,-1,NULL,S_NULL,0,0}, // S_ZROCK1_1 +{SPR_RCK2,0,-1,NULL,S_NULL,0,0}, // S_ZROCK2_1 +{SPR_RCK3,0,-1,NULL,S_NULL,0,0}, // S_ZROCK3_1 +{SPR_RCK4,0,-1,NULL,S_NULL,0,0}, // S_ZROCK4_1 +{SPR_CDLR,0,4,NULL,S_ZCHANDELIER2,0,0}, // S_ZCHANDELIER1 +{SPR_CDLR,1,4,NULL,S_ZCHANDELIER3,0,0}, // S_ZCHANDELIER2 +{SPR_CDLR,2,4,NULL,S_ZCHANDELIER1,0,0}, // S_ZCHANDELIER3 +{SPR_CDLR,3,-1,NULL,S_NULL,0,0}, // S_ZCHANDELIER_U +{SPR_TRE1,0,-1,NULL,S_NULL,0,0}, // S_ZTREEDEAD1 +{SPR_TRE1,0,-1,NULL,S_NULL,0,0}, // S_ZTREE +{SPR_TRDT,0,-1,NULL,S_NULL,0,0}, // S_ZTREEDESTRUCTIBLE1 +{SPR_TRDT,1,5,NULL,S_ZTREEDES_D2,0,0}, // S_ZTREEDES_D1 +{SPR_TRDT,2,5,A_Scream,S_ZTREEDES_D3,0,0}, // S_ZTREEDES_D2 +{SPR_TRDT,3,5,NULL,S_ZTREEDES_D4,0,0}, // S_ZTREEDES_D3 +{SPR_TRDT,4,5,NULL,S_ZTREEDES_D5,0,0}, // S_ZTREEDES_D4 +{SPR_TRDT,5,5,NULL,S_ZTREEDES_D6,0,0}, // S_ZTREEDES_D5 +{SPR_TRDT,6,-1,NULL,S_NULL,0,0}, // S_ZTREEDES_D6 +{SPR_TRDT,32775,5,NULL,S_ZTREEDES_X2,0,0}, // S_ZTREEDES_X1 +{SPR_TRDT,32776,5,NULL,S_ZTREEDES_X3,0,0}, // S_ZTREEDES_X2 +{SPR_TRDT,32777,5,NULL,S_ZTREEDES_X4,0,0}, // S_ZTREEDES_X3 +{SPR_TRDT,32778,5,NULL,S_ZTREEDES_X5,0,0}, // S_ZTREEDES_X4 +{SPR_TRDT,32779,5,NULL,S_ZTREEDES_X6,0,0}, // S_ZTREEDES_X5 +{SPR_TRDT,32780,5,A_Explode,S_ZTREEDES_X7,0,0}, // S_ZTREEDES_X6 +{SPR_TRDT,32781,5,NULL,S_ZTREEDES_X8,0,0}, // S_ZTREEDES_X7 +{SPR_TRDT,14,5,NULL,S_ZTREEDES_X9,0,0}, // S_ZTREEDES_X8 +{SPR_TRDT,15,5,NULL,S_ZTREEDES_X10,0,0}, // S_ZTREEDES_X9 +{SPR_TRDT,16,-1,NULL,S_NULL,0,0}, // S_ZTREEDES_X10 +{SPR_TRE2,0,-1,NULL,S_NULL,0,0}, // S_ZTREESWAMP182_1 +{SPR_TRE3,0,-1,NULL,S_NULL,0,0}, // S_ZTREESWAMP172_1 +{SPR_STM1,0,-1,NULL,S_NULL,0,0}, // S_ZSTUMPBURNED1 +{SPR_STM2,0,-1,NULL,S_NULL,0,0}, // S_ZSTUMPBARE1 +{SPR_STM3,0,-1,NULL,S_NULL,0,0}, // S_ZSTUMPSWAMP1_1 +{SPR_STM4,0,-1,NULL,S_NULL,0,0}, // S_ZSTUMPSWAMP2_1 +{SPR_MSH1,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMLARGE1_1 +{SPR_MSH2,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMLARGE2_1 +{SPR_MSH3,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMLARGE3_1 +{SPR_MSH4,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMSMALL1_1 +{SPR_MSH5,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMSMALL2_1 +{SPR_MSH6,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMSMALL3_1 +{SPR_MSH7,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMSMALL4_1 +{SPR_MSH8,0,-1,NULL,S_NULL,0,0}, // S_ZSHROOMSMALL5_1 +{SPR_SGMP,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEPILLAR1 +{SPR_SGM1,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITELARGE1 +{SPR_SGM2,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEMEDIUM1 +{SPR_SGM3,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITESMALL1 +{SPR_SLC1,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITELARGE1 +{SPR_SLC2,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITEMEDIUM1 +{SPR_SLC3,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITESMALL1 +{SPR_MSS1,0,-1,NULL,S_NULL,0,0}, // S_ZMOSSCEILING1_1 +{SPR_MSS2,0,-1,NULL,S_NULL,0,0}, // S_ZMOSSCEILING2_1 +{SPR_SWMV,0,-1,NULL,S_NULL,0,0}, // S_ZSWAMPVINE1 +{SPR_CPS1,0,-1,NULL,S_NULL,0,0}, // S_ZCORPSEKABOB1 +{SPR_CPS2,0,-1,NULL,S_NULL,0,0}, // S_ZCORPSESLEEPING1 +{SPR_TMS1,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONERIP1 +{SPR_TMS2,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONESHANE1 +{SPR_TMS3,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONEBIGCROSS1 +{SPR_TMS4,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONEBRIANR1 +{SPR_TMS5,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONECROSSCIRCLE1 +{SPR_TMS6,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONESMALLCROSS1 +{SPR_TMS7,0,-1,NULL,S_NULL,0,0}, // S_ZTOMBSTONEBRIANP1 +{SPR_CPS3,0,-1,NULL,S_NULL,0,0}, // S_CORPSEHANGING_1 +{SPR_STT2,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEGREENTALL_1 +{SPR_STT3,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEBLUETALL_1 +{SPR_STT4,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEGREENSHORT_1 +{SPR_STT5,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEBLUESHORT_1 +{SPR_GAR1,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLESTRIPETALL_1 +{SPR_GAR2,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEDARKREDTALL_1 +{SPR_GAR3,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEREDTALL_1 +{SPR_GAR4,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLETANTALL_1 +{SPR_GAR5,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLERUSTTALL_1 +{SPR_GAR6,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEDARKREDSHORT_1 +{SPR_GAR7,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLEREDSHORT_1 +{SPR_GAR8,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLETANSHORT_1 +{SPR_GAR9,0,-1,NULL,S_NULL,0,0}, // S_ZSTATUEGARGOYLERUSTSHORT_1 +{SPR_BNR1,0,-1,NULL,S_NULL,0,0}, // S_ZBANNERTATTERED_1 +{SPR_TRE4,0,-1,NULL,S_NULL,0,0}, // S_ZTREELARGE1 +{SPR_TRE5,0,-1,NULL,S_NULL,0,0}, // S_ZTREELARGE2 +{SPR_TRE6,0,-1,NULL,S_NULL,0,0}, // S_ZTREEGNARLED1 +{SPR_TRE7,0,-1,NULL,S_NULL,0,0}, // S_ZTREEGNARLED2 +{SPR_LOGG,0,-1,NULL,S_NULL,0,0}, // S_ZLOG +{SPR_ICT1,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITEICELARGE +{SPR_ICT2,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITEICEMEDIUM +{SPR_ICT3,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITEICESMALL +{SPR_ICT4,0,-1,NULL,S_NULL,0,0}, // S_ZSTALACTITEICETINY +{SPR_ICM1,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEICELARGE +{SPR_ICM2,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEICEMEDIUM +{SPR_ICM3,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEICESMALL +{SPR_ICM4,0,-1,NULL,S_NULL,0,0}, // S_ZSTALAGMITEICETINY +{SPR_RKBL,0,-1,NULL,S_NULL,0,0}, // S_ZROCKBROWN1 +{SPR_RKBS,0,-1,NULL,S_NULL,0,0}, // S_ZROCKBROWN2 +{SPR_RKBK,0,-1,NULL,S_NULL,0,0}, // S_ZROCKBLACK +{SPR_RBL1,0,-1,NULL,S_NULL,0,0}, // S_ZRUBBLE1 +{SPR_RBL2,0,-1,NULL,S_NULL,0,0}, // S_ZRUBBLE2 +{SPR_RBL3,0,-1,NULL,S_NULL,0,0}, // S_ZRUBBLE3 +{SPR_VASE,0,-1,NULL,S_NULL,0,0}, // S_ZVASEPILLAR +{SPR_POT1,0,-1,NULL,S_NULL,0,0}, // S_ZPOTTERY1 +{SPR_POT2,0,-1,NULL,S_NULL,0,0}, // S_ZPOTTERY2 +{SPR_POT3,0,-1,NULL,S_NULL,0,0}, // S_ZPOTTERY3 +{SPR_POT1,0,0,A_PotteryExplode,S_NULL,0,0}, // S_ZPOTTERY_EXPLODE +{SPR_PBIT,0,-1,NULL,S_NULL,0,0}, // S_POTTERYBIT_1 +{SPR_PBIT,1,-1,NULL,S_NULL,0,0}, // S_POTTERYBIT_2 +{SPR_PBIT,2,-1,NULL,S_NULL,0,0}, // S_POTTERYBIT_3 +{SPR_PBIT,3,-1,NULL,S_NULL,0,0}, // S_POTTERYBIT_4 +{SPR_PBIT,4,-1,NULL,S_NULL,0,0}, // S_POTTERYBIT_5 +{SPR_PBIT,5,0,A_PotteryChooseBit,S_NULL,0,0}, // S_POTTERYBIT_EX0 +{SPR_PBIT,5,140,NULL,S_POTTERYBIT_EX1_2,0,0}, // S_POTTERYBIT_EX1 +{SPR_PBIT,5,1,A_PotteryCheck,S_NULL,0,0}, // S_POTTERYBIT_EX1_2 +{SPR_PBIT,6,140,NULL,S_POTTERYBIT_EX2_2,0,0}, // S_POTTERYBIT_EX2 +{SPR_PBIT,6,1,A_PotteryCheck,S_NULL,0,0}, // S_POTTERYBIT_EX2_2 +{SPR_PBIT,7,140,NULL,S_POTTERYBIT_EX3_2,0,0}, // S_POTTERYBIT_EX3 +{SPR_PBIT,7,1,A_PotteryCheck,S_NULL,0,0}, // S_POTTERYBIT_EX3_2 +{SPR_PBIT,8,140,NULL,S_POTTERYBIT_EX4_2,0,0}, // S_POTTERYBIT_EX4 +{SPR_PBIT,8,1,A_PotteryCheck,S_NULL,0,0}, // S_POTTERYBIT_EX4_2 +{SPR_PBIT,9,140,NULL,S_POTTERYBIT_EX5_2,0,0}, // S_POTTERYBIT_EX5 +{SPR_PBIT,9,1,A_PotteryCheck,S_NULL,0,0}, // S_POTTERYBIT_EX5_2 +{SPR_CPS4,0,-1,NULL,S_NULL,0,0}, // S_ZCORPSELYNCHED1 +{SPR_CPS5,0,140,A_CorpseBloodDrip,S_ZCORPSELYNCHED2,0,0}, // S_ZCORPSELYNCHED2 +{SPR_CPS6,0,-1,NULL,S_NULL,0,0}, // S_ZCORPSESITTING +{SPR_CPS6,0,1,A_CorpseExplode,S_NULL,0,0}, // S_ZCORPSESITTING_X +{SPR_CPB1,0,-1,NULL,S_NULL,0,0}, // S_CORPSEBIT_1 +{SPR_CPB2,0,-1,NULL,S_NULL,0,0}, // S_CORPSEBIT_2 +{SPR_CPB3,0,-1,NULL,S_NULL,0,0}, // S_CORPSEBIT_3 +{SPR_CPB4,0,-1,NULL,S_NULL,0,0}, // S_CORPSEBIT_4 +{SPR_BDRP,0,-1,NULL,S_NULL,0,0}, // S_CORPSEBLOODDRIP +{SPR_BDSH,0,3,NULL,S_CORPSEBLOODDRIP_X2,0,0}, // S_CORPSEBLOODDRIP_X1 +{SPR_BDSH,1,3,NULL,S_CORPSEBLOODDRIP_X3,0,0}, // S_CORPSEBLOODDRIP_X2 +{SPR_BDSH,2,2,NULL,S_CORPSEBLOODDRIP_X4,0,0}, // S_CORPSEBLOODDRIP_X3 +{SPR_BDSH,3,2,NULL,S_NULL,0,0}, // S_CORPSEBLOODDRIP_X4 +{SPR_BDPL,0,-1,NULL,S_NULL,0,0}, // S_BLOODPOOL +{SPR_CNDL,32768,4,NULL,S_ZCANDLE2,0,0}, // S_ZCANDLE1 +{SPR_CNDL,32769,4,NULL,S_ZCANDLE3,0,0}, // S_ZCANDLE2 +{SPR_CNDL,32770,4,NULL,S_ZCANDLE1,0,0}, // S_ZCANDLE3 +{SPR_MAN1,0,20,A_LeafSpawn,S_ZLEAFSPAWNER,0,0}, // S_ZLEAFSPAWNER +{SPR_LEF1,0,4,NULL,S_LEAF1_2,0,0}, // S_LEAF1_1 +{SPR_LEF1,1,4,NULL,S_LEAF1_3,0,0}, // S_LEAF1_2 +{SPR_LEF1,2,4,NULL,S_LEAF1_4,0,0}, // S_LEAF1_3 +{SPR_LEF1,3,4,A_LeafThrust,S_LEAF1_5,0,0}, // S_LEAF1_4 +{SPR_LEF1,4,4,NULL,S_LEAF1_6,0,0}, // S_LEAF1_5 +{SPR_LEF1,5,4,NULL,S_LEAF1_7,0,0}, // S_LEAF1_6 +{SPR_LEF1,6,4,NULL,S_LEAF1_8,0,0}, // S_LEAF1_7 +{SPR_LEF1,7,4,A_LeafThrust,S_LEAF1_9,0,0}, // S_LEAF1_8 +{SPR_LEF1,8,4,NULL,S_LEAF1_10,0,0}, // S_LEAF1_9 +{SPR_LEF1,0,4,NULL,S_LEAF1_11,0,0}, // S_LEAF1_10 +{SPR_LEF1,1,4,NULL,S_LEAF1_12,0,0}, // S_LEAF1_11 +{SPR_LEF1,2,4,A_LeafThrust,S_LEAF1_13,0,0}, // S_LEAF1_12 +{SPR_LEF1,3,4,NULL,S_LEAF1_14,0,0}, // S_LEAF1_13 +{SPR_LEF1,4,4,NULL,S_LEAF1_15,0,0}, // S_LEAF1_14 +{SPR_LEF1,5,4,NULL,S_LEAF1_16,0,0}, // S_LEAF1_15 +{SPR_LEF1,6,4,A_LeafThrust,S_LEAF1_17,0,0}, // S_LEAF1_16 +{SPR_LEF1,7,4,NULL,S_LEAF1_18,0,0}, // S_LEAF1_17 +{SPR_LEF1,8,4,NULL,S_NULL,0,0}, // S_LEAF1_18 +{SPR_LEF3,3,10,A_LeafCheck,S_LEAF_X1,0,0}, // S_LEAF_X1 +{SPR_LEF2,0,4,NULL,S_LEAF2_2,0,0}, // S_LEAF2_1 +{SPR_LEF2,1,4,NULL,S_LEAF2_3,0,0}, // S_LEAF2_2 +{SPR_LEF2,2,4,NULL,S_LEAF2_4,0,0}, // S_LEAF2_3 +{SPR_LEF2,3,4,A_LeafThrust,S_LEAF2_5,0,0}, // S_LEAF2_4 +{SPR_LEF2,4,4,NULL,S_LEAF2_6,0,0}, // S_LEAF2_5 +{SPR_LEF2,5,4,NULL,S_LEAF2_7,0,0}, // S_LEAF2_6 +{SPR_LEF2,6,4,NULL,S_LEAF2_8,0,0}, // S_LEAF2_7 +{SPR_LEF2,7,4,A_LeafThrust,S_LEAF2_9,0,0}, // S_LEAF2_8 +{SPR_LEF2,8,4,NULL,S_LEAF2_10,0,0}, // S_LEAF2_9 +{SPR_LEF2,0,4,NULL,S_LEAF2_11,0,0}, // S_LEAF2_10 +{SPR_LEF2,1,4,NULL,S_LEAF2_12,0,0}, // S_LEAF2_11 +{SPR_LEF2,2,4,A_LeafThrust,S_LEAF2_13,0,0}, // S_LEAF2_12 +{SPR_LEF2,3,4,NULL,S_LEAF2_14,0,0}, // S_LEAF2_13 +{SPR_LEF2,4,4,NULL,S_LEAF2_15,0,0}, // S_LEAF2_14 +{SPR_LEF2,5,4,NULL,S_LEAF2_16,0,0}, // S_LEAF2_15 +{SPR_LEF2,6,4,A_LeafThrust,S_LEAF2_17,0,0}, // S_LEAF2_16 +{SPR_LEF2,7,4,NULL,S_LEAF2_18,0,0}, // S_LEAF2_17 +{SPR_LEF2,8,4,NULL,S_NULL,0,0}, // S_LEAF2_18 +{SPR_TWTR,32768,4,NULL,S_ZTWINEDTORCH_2,0,0}, // S_ZTWINEDTORCH_1 +{SPR_TWTR,32769,4,NULL,S_ZTWINEDTORCH_3,0,0}, // S_ZTWINEDTORCH_2 +{SPR_TWTR,32770,4,NULL,S_ZTWINEDTORCH_4,0,0}, // S_ZTWINEDTORCH_3 +{SPR_TWTR,32771,4,NULL,S_ZTWINEDTORCH_5,0,0}, // S_ZTWINEDTORCH_4 +{SPR_TWTR,32772,4,NULL,S_ZTWINEDTORCH_6,0,0}, // S_ZTWINEDTORCH_5 +{SPR_TWTR,32773,4,NULL,S_ZTWINEDTORCH_7,0,0}, // S_ZTWINEDTORCH_6 +{SPR_TWTR,32774,4,NULL,S_ZTWINEDTORCH_8,0,0}, // S_ZTWINEDTORCH_7 +{SPR_TWTR,32775,4,NULL,S_ZTWINEDTORCH_1,0,0}, // S_ZTWINEDTORCH_8 +{SPR_TWTR,8,-1,NULL,S_NULL,0,0}, // S_ZTWINEDTORCH_UNLIT +{SPR_TLGL,0,2,NULL,S_BRIDGE2,0,0}, // S_BRIDGE1 +{SPR_TLGL,0,2,A_BridgeInit,S_BRIDGE3,0,0}, // S_BRIDGE2 +{SPR_TLGL,0,-1,NULL,S_NULL,0,0}, // S_BRIDGE3 +{SPR_TLGL,0,2,NULL,S_FREE_BRIDGE2,0,0}, // S_FREE_BRIDGE1 +{SPR_TLGL,0,300,NULL,S_NULL,0,0}, // S_FREE_BRIDGE2 +{SPR_TLGL,0,2,NULL,S_BBALL2,0,0}, // S_BBALL1 +{SPR_TLGL,0,5,A_BridgeOrbit,S_BBALL2,0,0}, // S_BBALL2 +{SPR_WLTR,32768,5,NULL,S_ZWALLTORCH2,0,0}, // S_ZWALLTORCH1 +{SPR_WLTR,32769,5,NULL,S_ZWALLTORCH3,0,0}, // S_ZWALLTORCH2 +{SPR_WLTR,32770,5,NULL,S_ZWALLTORCH4,0,0}, // S_ZWALLTORCH3 +{SPR_WLTR,32771,5,NULL,S_ZWALLTORCH5,0,0}, // S_ZWALLTORCH4 +{SPR_WLTR,32772,5,NULL,S_ZWALLTORCH6,0,0}, // S_ZWALLTORCH5 +{SPR_WLTR,32773,5,NULL,S_ZWALLTORCH7,0,0}, // S_ZWALLTORCH6 +{SPR_WLTR,32774,5,NULL,S_ZWALLTORCH8,0,0}, // S_ZWALLTORCH7 +{SPR_WLTR,32775,5,NULL,S_ZWALLTORCH1,0,0}, // S_ZWALLTORCH8 +{SPR_WLTR,8,-1,NULL,S_NULL,0,0}, // S_ZWALLTORCH_U +{SPR_BARL,0,-1,NULL,S_NULL,0,0}, // S_ZBARREL1 +{SPR_SHB1,0,-1,NULL,S_NULL,0,0}, // S_ZSHRUB1 +{SPR_SHB1,0,1,A_TreeDeath,S_ZSHRUB1,0,0}, // S_ZSHRUB1_DIE +{SPR_SHB1,32769,7,NULL,S_ZSHRUB1_X2,0,0}, // S_ZSHRUB1_X1 +{SPR_SHB1,32770,6,A_Scream,S_ZSHRUB1_X3,0,0}, // S_ZSHRUB1_X2 +{SPR_SHB1,32771,5,NULL,S_NULL,0,0}, // S_ZSHRUB1_X3 +{SPR_SHB2,0,-1,NULL,S_NULL,0,0}, // S_ZSHRUB2 +{SPR_SHB2,0,1,A_TreeDeath,S_ZSHRUB2,0,0}, // S_ZSHRUB2_DIE +{SPR_SHB2,32769,7,NULL,S_ZSHRUB2_X2,0,0}, // S_ZSHRUB2_X1 +{SPR_SHB2,32770,6,A_Scream,S_ZSHRUB2_X3,0,0}, // S_ZSHRUB2_X2 +{SPR_SHB2,32771,5,A_Explode,S_ZSHRUB2_X4,0,0}, // S_ZSHRUB2_X3 +{SPR_SHB2,32772,5,NULL,S_NULL,0,0}, // S_ZSHRUB2_X4 +{SPR_BCKT,0,-1,NULL,S_NULL,0,0}, // S_ZBUCKET1 +{SPR_SHRM,0,5,A_PoisonShroom,S_ZPOISONSHROOM_P2,0,0}, // S_ZPOISONSHROOM1 +{SPR_SHRM,0,6,NULL,S_ZPOISONSHROOM_P2,0,0}, // S_ZPOISONSHROOM_P1 +{SPR_SHRM,1,8,A_Pain,S_ZPOISONSHROOM1,0,0}, // S_ZPOISONSHROOM_P2 +{SPR_SHRM,2,5,NULL,S_ZPOISONSHROOM_X2,0,0}, // S_ZPOISONSHROOM_X1 +{SPR_SHRM,3,5,NULL,S_ZPOISONSHROOM_X3,0,0}, // S_ZPOISONSHROOM_X2 +{SPR_SHRM,4,5,A_PoisonBagInit,S_ZPOISONSHROOM_X4,0,0}, // S_ZPOISONSHROOM_X3 +{SPR_SHRM,5,-1,NULL,S_NULL,0,0}, // S_ZPOISONSHROOM_X4 +{SPR_FBUL,32768,4,NULL,S_ZFIREBULL2,0,0}, // S_ZFIREBULL1 +{SPR_FBUL,32769,4,NULL,S_ZFIREBULL3,0,0}, // S_ZFIREBULL2 +{SPR_FBUL,32770,4,NULL,S_ZFIREBULL4,0,0}, // S_ZFIREBULL3 +{SPR_FBUL,32771,4,NULL,S_ZFIREBULL5,0,0}, // S_ZFIREBULL4 +{SPR_FBUL,32772,4,NULL,S_ZFIREBULL6,0,0}, // S_ZFIREBULL5 +{SPR_FBUL,32773,4,NULL,S_ZFIREBULL7,0,0}, // S_ZFIREBULL6 +{SPR_FBUL,32774,4,NULL,S_ZFIREBULL1,0,0}, // S_ZFIREBULL7 +{SPR_FBUL,32777,4,NULL,S_ZFIREBULL_DEATH2,0,0}, // S_ZFIREBULL_DEATH +{SPR_FBUL,32776,4,NULL,S_ZFIREBULL_U,0,0}, // S_ZFIREBULL_DEATH2 +{SPR_FBUL,7,-1,NULL,S_NULL,0,0}, // S_ZFIREBULL_U +{SPR_FBUL,32776,4,NULL,S_ZFIREBULL_BIRTH2,0,0}, // S_ZFIREBULL_BIRTH +{SPR_FBUL,32777,4,NULL,S_ZFIREBULL1,0,0}, // S_ZFIREBULL_BIRTH2 +{SPR_FSKL,32768,4,NULL,S_ZFIRETHING2,0,0}, // S_ZFIRETHING1 +{SPR_FSKL,32769,3,NULL,S_ZFIRETHING3,0,0}, // S_ZFIRETHING2 +{SPR_FSKL,32770,4,NULL,S_ZFIRETHING4,0,0}, // S_ZFIRETHING3 +{SPR_FSKL,32771,3,NULL,S_ZFIRETHING5,0,0}, // S_ZFIRETHING4 +{SPR_FSKL,32772,4,NULL,S_ZFIRETHING6,0,0}, // S_ZFIRETHING5 +{SPR_FSKL,32773,3,NULL,S_ZFIRETHING7,0,0}, // S_ZFIRETHING6 +{SPR_FSKL,32774,4,NULL,S_ZFIRETHING8,0,0}, // S_ZFIRETHING7 +{SPR_FSKL,32775,3,NULL,S_ZFIRETHING9,0,0}, // S_ZFIRETHING8 +{SPR_FSKL,32776,4,NULL,S_ZFIRETHING1,0,0}, // S_ZFIRETHING9 +{SPR_BRTR,32768,4,NULL,S_ZBRASSTORCH2,0,0}, // S_ZBRASSTORCH1 +{SPR_BRTR,32769,4,NULL,S_ZBRASSTORCH3,0,0}, // S_ZBRASSTORCH2 +{SPR_BRTR,32770,4,NULL,S_ZBRASSTORCH4,0,0}, // S_ZBRASSTORCH3 +{SPR_BRTR,32771,4,NULL,S_ZBRASSTORCH5,0,0}, // S_ZBRASSTORCH4 +{SPR_BRTR,32772,4,NULL,S_ZBRASSTORCH6,0,0}, // S_ZBRASSTORCH5 +{SPR_BRTR,32773,4,NULL,S_ZBRASSTORCH7,0,0}, // S_ZBRASSTORCH6 +{SPR_BRTR,32774,4,NULL,S_ZBRASSTORCH8,0,0}, // S_ZBRASSTORCH7 +{SPR_BRTR,32775,4,NULL,S_ZBRASSTORCH9,0,0}, // S_ZBRASSTORCH8 +{SPR_BRTR,32776,4,NULL,S_ZBRASSTORCH10,0,0}, // S_ZBRASSTORCH9 +{SPR_BRTR,32777,4,NULL,S_ZBRASSTORCH11,0,0}, // S_ZBRASSTORCH10 +{SPR_BRTR,32778,4,NULL,S_ZBRASSTORCH12,0,0}, // S_ZBRASSTORCH11 +{SPR_BRTR,32779,4,NULL,S_ZBRASSTORCH13,0,0}, // S_ZBRASSTORCH12 +{SPR_BRTR,32780,4,NULL,S_ZBRASSTORCH1,0,0}, // S_ZBRASSTORCH13 +{SPR_SUIT,0,-1,NULL,S_NULL,0,0}, // S_ZSUITOFARMOR +{SPR_SUIT,0,1,A_SoAExplode,S_NULL,0,0}, // S_ZSUITOFARMOR_X1 +{SPR_SUIT,1,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK1 +{SPR_SUIT,2,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK2 +{SPR_SUIT,3,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK3 +{SPR_SUIT,4,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK4 +{SPR_SUIT,5,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK5 +{SPR_SUIT,6,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK6 +{SPR_SUIT,7,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK7 +{SPR_SUIT,8,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK8 +{SPR_SUIT,9,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK9 +{SPR_SUIT,10,-1,NULL,S_NULL,0,0}, // S_ZARMORCHUNK10 +{SPR_BBLL,5,-1,NULL,S_NULL,0,0}, // S_ZBELL +{SPR_BBLL,0,4,A_BellReset1,S_ZBELL_X2,0,0}, // S_ZBELL_X1 +{SPR_BBLL,1,4,NULL,S_ZBELL_X3,0,0}, // S_ZBELL_X2 +{SPR_BBLL,2,4,NULL,S_ZBELL_X4,0,0}, // S_ZBELL_X3 +{SPR_BBLL,3,5,A_Scream,S_ZBELL_X5,0,0}, // S_ZBELL_X4 +{SPR_BBLL,2,4,NULL,S_ZBELL_X6,0,0}, // S_ZBELL_X5 +{SPR_BBLL,1,4,NULL,S_ZBELL_X7,0,0}, // S_ZBELL_X6 +{SPR_BBLL,0,3,NULL,S_ZBELL_X8,0,0}, // S_ZBELL_X7 +{SPR_BBLL,4,4,NULL,S_ZBELL_X9,0,0}, // S_ZBELL_X8 +{SPR_BBLL,5,5,NULL,S_ZBELL_X10,0,0}, // S_ZBELL_X9 +{SPR_BBLL,6,6,A_Scream,S_ZBELL_X11,0,0}, // S_ZBELL_X10 +{SPR_BBLL,5,5,NULL,S_ZBELL_X12,0,0}, // S_ZBELL_X11 +{SPR_BBLL,4,4,NULL,S_ZBELL_X13,0,0}, // S_ZBELL_X12 +{SPR_BBLL,0,4,NULL,S_ZBELL_X14,0,0}, // S_ZBELL_X13 +{SPR_BBLL,1,5,NULL,S_ZBELL_X15,0,0}, // S_ZBELL_X14 +{SPR_BBLL,2,5,NULL,S_ZBELL_X16,0,0}, // S_ZBELL_X15 +{SPR_BBLL,3,6,A_Scream,S_ZBELL_X17,0,0}, // S_ZBELL_X16 +{SPR_BBLL,2,5,NULL,S_ZBELL_X18,0,0}, // S_ZBELL_X17 +{SPR_BBLL,1,5,NULL,S_ZBELL_X19,0,0}, // S_ZBELL_X18 +{SPR_BBLL,0,4,NULL,S_ZBELL_X20,0,0}, // S_ZBELL_X19 +{SPR_BBLL,4,5,NULL,S_ZBELL_X21,0,0}, // S_ZBELL_X20 +{SPR_BBLL,5,5,NULL,S_ZBELL_X22,0,0}, // S_ZBELL_X21 +{SPR_BBLL,6,7,A_Scream,S_ZBELL_X23,0,0}, // S_ZBELL_X22 +{SPR_BBLL,5,5,NULL,S_ZBELL_X24,0,0}, // S_ZBELL_X23 +{SPR_BBLL,4,5,NULL,S_ZBELL_X25,0,0}, // S_ZBELL_X24 +{SPR_BBLL,0,5,NULL,S_ZBELL_X26,0,0}, // S_ZBELL_X25 +{SPR_BBLL,1,6,NULL,S_ZBELL_X27,0,0}, // S_ZBELL_X26 +{SPR_BBLL,2,6,NULL,S_ZBELL_X28,0,0}, // S_ZBELL_X27 +{SPR_BBLL,3,7,A_Scream,S_ZBELL_X29,0,0}, // S_ZBELL_X28 +{SPR_BBLL,2,6,NULL,S_ZBELL_X30,0,0}, // S_ZBELL_X29 +{SPR_BBLL,1,6,NULL,S_ZBELL_X31,0,0}, // S_ZBELL_X30 +{SPR_BBLL,0,5,NULL,S_ZBELL_X32,0,0}, // S_ZBELL_X31 +{SPR_BBLL,4,6,NULL,S_ZBELL_X33,0,0}, // S_ZBELL_X32 +{SPR_BBLL,5,6,NULL,S_ZBELL_X34,0,0}, // S_ZBELL_X33 +{SPR_BBLL,6,7,A_Scream,S_ZBELL_X35,0,0}, // S_ZBELL_X34 +{SPR_BBLL,5,6,NULL,S_ZBELL_X36,0,0}, // S_ZBELL_X35 +{SPR_BBLL,4,6,NULL,S_ZBELL_X37,0,0}, // S_ZBELL_X36 +{SPR_BBLL,0,6,NULL,S_ZBELL_X38,0,0}, // S_ZBELL_X37 +{SPR_BBLL,1,6,NULL,S_ZBELL_X39,0,0}, // S_ZBELL_X38 +{SPR_BBLL,2,6,NULL,S_ZBELL_X40,0,0}, // S_ZBELL_X39 +{SPR_BBLL,1,7,NULL,S_ZBELL_X41,0,0}, // S_ZBELL_X40 +{SPR_BBLL,0,8,NULL,S_ZBELL_X42,0,0}, // S_ZBELL_X41 +{SPR_BBLL,4,12,NULL,S_ZBELL_X43,0,0}, // S_ZBELL_X42 +{SPR_BBLL,0,10,NULL,S_ZBELL_X44,0,0}, // S_ZBELL_X43 +{SPR_BBLL,1,12,NULL,S_ZBELL_X45,0,0}, // S_ZBELL_X44 +{SPR_BBLL,0,12,NULL,S_ZBELL_X46,0,0}, // S_ZBELL_X45 +{SPR_BBLL,4,14,NULL,S_ZBELL_X47,0,0}, // S_ZBELL_X46 +{SPR_BBLL,0,1,A_BellReset2,S_ZBELL,0,0}, // S_ZBELL_X47 +{SPR_CAND,32768,5,NULL,S_ZBLUE_CANDLE2,0,0}, // S_ZBLUE_CANDLE1 +{SPR_CAND,32769,5,NULL,S_ZBLUE_CANDLE3,0,0}, // S_ZBLUE_CANDLE2 +{SPR_CAND,32770,5,NULL,S_ZBLUE_CANDLE4,0,0}, // S_ZBLUE_CANDLE3 +{SPR_CAND,32771,5,NULL,S_ZBLUE_CANDLE5,0,0}, // S_ZBLUE_CANDLE4 +{SPR_CAND,32772,5,NULL,S_ZBLUE_CANDLE1,0,0}, // S_ZBLUE_CANDLE5 +{SPR_IRON,0,-1,NULL,S_NULL,0,0}, // S_ZIRON_MAIDEN +{SPR_XMAS,0,-1,NULL,S_NULL,0,0}, // S_ZXMAS_TREE +{SPR_XMAS,0,4,A_TreeDeath,S_ZXMAS_TREE,0,0}, // S_ZXMAS_TREE_DIE +{SPR_XMAS,32769,6,NULL,S_ZXMAS_TREE_X2,0,0}, // S_ZXMAS_TREE_X1 +{SPR_XMAS,32770,6,A_Scream,S_ZXMAS_TREE_X3,0,0}, // S_ZXMAS_TREE_X2 +{SPR_XMAS,32771,5,NULL,S_ZXMAS_TREE_X4,0,0}, // S_ZXMAS_TREE_X3 +{SPR_XMAS,32772,5,A_Explode,S_ZXMAS_TREE_X5,0,0}, // S_ZXMAS_TREE_X4 +{SPR_XMAS,32773,5,NULL,S_ZXMAS_TREE_X6,0,0}, // S_ZXMAS_TREE_X5 +{SPR_XMAS,32774,4,NULL,S_ZXMAS_TREE_X7,0,0}, // S_ZXMAS_TREE_X6 +{SPR_XMAS,7,5,NULL,S_ZXMAS_TREE_X8,0,0}, // S_ZXMAS_TREE_X7 +{SPR_XMAS,8,4,A_NoBlocking,S_ZXMAS_TREE_X9,0,0}, // S_ZXMAS_TREE_X8 +{SPR_XMAS,9,4,NULL,S_ZXMAS_TREE_X10,0,0}, // S_ZXMAS_TREE_X9 +{SPR_XMAS,10,-1,NULL,S_NULL,0,0}, // S_ZXMAS_TREE_X10 +{SPR_CDRN,32769,4,NULL,S_ZCAULDRON2,0,0}, // S_ZCAULDRON1 +{SPR_CDRN,32770,4,NULL,S_ZCAULDRON3,0,0}, // S_ZCAULDRON2 +{SPR_CDRN,32771,4,NULL,S_ZCAULDRON4,0,0}, // S_ZCAULDRON3 +{SPR_CDRN,32772,4,NULL,S_ZCAULDRON5,0,0}, // S_ZCAULDRON4 +{SPR_CDRN,32773,4,NULL,S_ZCAULDRON6,0,0}, // S_ZCAULDRON5 +{SPR_CDRN,32774,4,NULL,S_ZCAULDRON7,0,0}, // S_ZCAULDRON6 +{SPR_CDRN,32775,4,NULL,S_ZCAULDRON1,0,0}, // S_ZCAULDRON7 +{SPR_CDRN,0,-1,NULL,S_NULL,0,0}, // S_ZCAULDRON_U +{SPR_CHNS,0,-1,NULL,S_NULL,0,0}, // S_ZCHAINBIT32 +{SPR_CHNS,1,-1,NULL,S_NULL,0,0}, // S_ZCHAINBIT64 +{SPR_CHNS,2,-1,NULL,S_NULL,0,0}, // S_ZCHAINEND_HEART +{SPR_CHNS,3,-1,NULL,S_NULL,0,0}, // S_ZCHAINEND_HOOK1 +{SPR_CHNS,4,-1,NULL,S_NULL,0,0}, // S_ZCHAINEND_HOOK2 +{SPR_CHNS,5,-1,NULL,S_NULL,0,0}, // S_ZCHAINEND_SPIKE +{SPR_CHNS,6,-1,NULL,S_NULL,0,0}, // S_ZCHAINEND_SKULL +{SPR_TST1,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT1 +{SPR_TST2,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT2 +{SPR_TST3,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT3 +{SPR_TST4,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT4 +{SPR_TST5,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT5 +{SPR_TST6,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT6 +{SPR_TST7,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT7 +{SPR_TST8,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT8 +{SPR_TST9,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT9 +{SPR_TST0,0,-1,NULL,S_NULL,0,0}, // S_TABLE_SHIT10 +{SPR_TELE,32768,6,NULL,S_TFOG2,0,0}, // S_TFOG1 +{SPR_TELE,32769,6,NULL,S_TFOG3,0,0}, // S_TFOG2 +{SPR_TELE,32770,6,NULL,S_TFOG4,0,0}, // S_TFOG3 +{SPR_TELE,32771,6,NULL,S_TFOG5,0,0}, // S_TFOG4 +{SPR_TELE,32772,6,NULL,S_TFOG6,0,0}, // S_TFOG5 +{SPR_TELE,32773,6,NULL,S_TFOG7,0,0}, // S_TFOG6 +{SPR_TELE,32774,6,NULL,S_TFOG8,0,0}, // S_TFOG7 +{SPR_TELE,32775,6,NULL,S_TFOG9,0,0}, // S_TFOG8 +{SPR_TELE,32774,6,NULL,S_TFOG10,0,0}, // S_TFOG9 +{SPR_TELE,32773,6,NULL,S_TFOG11,0,0}, // S_TFOG10 +{SPR_TELE,32772,6,NULL,S_TFOG12,0,0}, // S_TFOG11 +{SPR_TELE,32771,6,NULL,S_TFOG13,0,0}, // S_TFOG12 +{SPR_TELE,32770,6,NULL,S_NULL,0,0}, // S_TFOG13 +{SPR_TSMK,0,4,NULL,S_TELESMOKE2,0,0}, // S_TELESMOKE1 +{SPR_TSMK,1,3,NULL,S_TELESMOKE3,0,0}, // S_TELESMOKE2 +{SPR_TSMK,2,4,NULL,S_TELESMOKE4,0,0}, // S_TELESMOKE3 +{SPR_TSMK,3,3,NULL,S_TELESMOKE5,0,0}, // S_TELESMOKE4 +{SPR_TSMK,4,4,NULL,S_TELESMOKE6,0,0}, // S_TELESMOKE5 +{SPR_TSMK,5,3,NULL,S_TELESMOKE7,0,0}, // S_TELESMOKE6 +{SPR_TSMK,6,4,NULL,S_TELESMOKE8,0,0}, // S_TELESMOKE7 +{SPR_TSMK,7,3,NULL,S_TELESMOKE9,0,0}, // S_TELESMOKE8 +{SPR_TSMK,8,4,NULL,S_TELESMOKE10,0,0}, // S_TELESMOKE9 +{SPR_TSMK,9,3,NULL,S_TELESMOKE11,0,0}, // S_TELESMOKE10 +{SPR_TSMK,10,4,NULL,S_TELESMOKE12,0,0}, // S_TELESMOKE11 +{SPR_TSMK,11,3,NULL,S_TELESMOKE13,0,0}, // S_TELESMOKE12 +{SPR_TSMK,12,4,NULL,S_TELESMOKE14,0,0}, // S_TELESMOKE13 +{SPR_TSMK,13,3,NULL,S_TELESMOKE15,0,0}, // S_TELESMOKE14 +{SPR_TSMK,14,4,NULL,S_TELESMOKE16,0,0}, // S_TELESMOKE15 +{SPR_TSMK,15,3,NULL,S_TELESMOKE17,0,0}, // S_TELESMOKE16 +{SPR_TSMK,16,4,NULL,S_TELESMOKE18,0,0}, // S_TELESMOKE17 +{SPR_TSMK,17,3,NULL,S_TELESMOKE19,0,0}, // S_TELESMOKE18 +{SPR_TSMK,18,4,NULL,S_TELESMOKE20,0,0}, // S_TELESMOKE19 +{SPR_TSMK,19,3,NULL,S_TELESMOKE21,0,0}, // S_TELESMOKE20 +{SPR_TSMK,20,4,NULL,S_TELESMOKE22,0,0}, // S_TELESMOKE21 +{SPR_TSMK,21,3,NULL,S_TELESMOKE23,0,0}, // S_TELESMOKE22 +{SPR_TSMK,22,4,NULL,S_TELESMOKE24,0,0}, // S_TELESMOKE23 +{SPR_TSMK,23,3,NULL,S_TELESMOKE25,0,0}, // S_TELESMOKE24 +{SPR_TSMK,24,4,NULL,S_TELESMOKE26,0,0}, // S_TELESMOKE25 +{SPR_TSMK,25,3,NULL,S_TELESMOKE1,0,0}, // S_TELESMOKE26 +{SPR_FPCH,0,0,A_Light0,S_NULL,0,0}, // S_LIGHTDONE +{SPR_FPCH,0,1,A_WeaponReady,S_PUNCHREADY,0,0}, // S_PUNCHREADY +{SPR_FPCH,0,1,A_Lower,S_PUNCHDOWN,0,0}, // S_PUNCHDOWN +{SPR_FPCH,0,1,A_Raise,S_PUNCHUP,0,0}, // S_PUNCHUP +{SPR_FPCH,1,5,NULL,S_PUNCHATK1_2,5,40}, // S_PUNCHATK1_1 +{SPR_FPCH,2,4,NULL,S_PUNCHATK1_3,5,40}, // S_PUNCHATK1_2 +{SPR_FPCH,3,4,A_FPunchAttack,S_PUNCHATK1_4,5,40}, // S_PUNCHATK1_3 +{SPR_FPCH,2,4,NULL,S_PUNCHATK1_5,5,40}, // S_PUNCHATK1_4 +{SPR_FPCH,1,5,A_ReFire,S_PUNCHREADY,5,40}, // S_PUNCHATK1_5 +{SPR_FPCH,3,4,NULL,S_PUNCHATK2_2,5,40}, // S_PUNCHATK2_1 +{SPR_FPCH,4,4,NULL,S_PUNCHATK2_3,5,40}, // S_PUNCHATK2_2 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_4,15,50}, // S_PUNCHATK2_3 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_5,25,60}, // S_PUNCHATK2_4 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_6,35,70}, // S_PUNCHATK2_5 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_7,45,80}, // S_PUNCHATK2_6 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_8,55,90}, // S_PUNCHATK2_7 +{SPR_FPCH,4,1,NULL,S_PUNCHATK2_9,65,100}, // S_PUNCHATK2_8 +{SPR_FPCH,4,10,NULL,S_PUNCHREADY,0,150}, // S_PUNCHATK2_9 +{SPR_FHFX,18,4,NULL,S_PUNCHPUFF2,0,0}, // S_PUNCHPUFF1 +{SPR_FHFX,19,4,NULL,S_PUNCHPUFF3,0,0}, // S_PUNCHPUFF2 +{SPR_FHFX,20,4,NULL,S_PUNCHPUFF4,0,0}, // S_PUNCHPUFF3 +{SPR_FHFX,21,4,NULL,S_PUNCHPUFF5,0,0}, // S_PUNCHPUFF4 +{SPR_FHFX,22,4,NULL,S_NULL,0,0}, // S_PUNCHPUFF5 +{SPR_WFAX,0,-1,NULL,S_NULL,0,0}, // S_AXE +{SPR_FAXE,0,1,A_WeaponReady,S_FAXEREADY,0,0}, // S_FAXEREADY +{SPR_FAXE,0,1,A_Lower,S_FAXEDOWN,0,0}, // S_FAXEDOWN +{SPR_FAXE,0,1,A_Raise,S_FAXEUP,0,0}, // S_FAXEUP +{SPR_FAXE,1,4,NULL,S_FAXEATK_2,15,32}, // S_FAXEATK_1 +{SPR_FAXE,2,3,NULL,S_FAXEATK_3,15,32}, // S_FAXEATK_2 +{SPR_FAXE,3,2,NULL,S_FAXEATK_4,15,32}, // S_FAXEATK_3 +{SPR_FAXE,3,1,A_FAxeAttack,S_FAXEATK_5,-5,70}, // S_FAXEATK_4 +{SPR_FAXE,3,2,NULL,S_FAXEATK_6,-25,90}, // S_FAXEATK_5 +{SPR_FAXE,4,1,NULL,S_FAXEATK_7,15,32}, // S_FAXEATK_6 +{SPR_FAXE,4,2,NULL,S_FAXEATK_8,10,54}, // S_FAXEATK_7 +{SPR_FAXE,4,7,NULL,S_FAXEATK_9,10,150}, // S_FAXEATK_8 +{SPR_FAXE,0,1,A_ReFire,S_FAXEATK_10,0,60}, // S_FAXEATK_9 +{SPR_FAXE,0,1,NULL,S_FAXEATK_11,0,52}, // S_FAXEATK_10 +{SPR_FAXE,0,1,NULL,S_FAXEATK_12,0,44}, // S_FAXEATK_11 +{SPR_FAXE,0,1,NULL,S_FAXEATK_13,0,36}, // S_FAXEATK_12 +{SPR_FAXE,0,1,NULL,S_FAXEREADY,0,0}, // S_FAXEATK_13 +{SPR_FAXE,11,1,A_WeaponReady,S_FAXEREADY_G1,0,0}, // S_FAXEREADY_G +{SPR_FAXE,11,1,A_WeaponReady,S_FAXEREADY_G2,0,0}, // S_FAXEREADY_G1 +{SPR_FAXE,11,1,A_WeaponReady,S_FAXEREADY_G3,0,0}, // S_FAXEREADY_G2 +{SPR_FAXE,12,1,A_WeaponReady,S_FAXEREADY_G4,0,0}, // S_FAXEREADY_G3 +{SPR_FAXE,12,1,A_WeaponReady,S_FAXEREADY_G5,0,0}, // S_FAXEREADY_G4 +{SPR_FAXE,12,1,A_WeaponReady,S_FAXEREADY_G,0,0}, // S_FAXEREADY_G5 +{SPR_FAXE,11,1,A_Lower,S_FAXEDOWN_G,0,0}, // S_FAXEDOWN_G +{SPR_FAXE,11,1,A_Raise,S_FAXEUP_G,0,0}, // S_FAXEUP_G +{SPR_FAXE,13,4,NULL,S_FAXEATK_G2,15,32}, // S_FAXEATK_G1 +{SPR_FAXE,14,3,NULL,S_FAXEATK_G3,15,32}, // S_FAXEATK_G2 +{SPR_FAXE,15,2,NULL,S_FAXEATK_G4,15,32}, // S_FAXEATK_G3 +{SPR_FAXE,15,1,A_FAxeAttack,S_FAXEATK_G5,-5,70}, // S_FAXEATK_G4 +{SPR_FAXE,15,2,NULL,S_FAXEATK_G6,-25,90}, // S_FAXEATK_G5 +{SPR_FAXE,16,1,NULL,S_FAXEATK_G7,15,32}, // S_FAXEATK_G6 +{SPR_FAXE,16,2,NULL,S_FAXEATK_G8,10,54}, // S_FAXEATK_G7 +{SPR_FAXE,16,7,NULL,S_FAXEATK_G9,10,150}, // S_FAXEATK_G8 +{SPR_FAXE,0,1,A_ReFire,S_FAXEATK_G10,0,60}, // S_FAXEATK_G9 +{SPR_FAXE,0,1,NULL,S_FAXEATK_G11,0,52}, // S_FAXEATK_G10 +{SPR_FAXE,0,1,NULL,S_FAXEATK_G12,0,44}, // S_FAXEATK_G11 +{SPR_FAXE,0,1,NULL,S_FAXEATK_G13,0,36}, // S_FAXEATK_G12 +{SPR_FAXE,0,1,NULL,S_FAXEREADY_G,0,0}, // S_FAXEATK_G13 +{SPR_FAXE,32785,4,NULL,S_AXEPUFF_GLOW2,0,0}, // S_AXEPUFF_GLOW1 +{SPR_FAXE,32786,4,NULL,S_AXEPUFF_GLOW3,0,0}, // S_AXEPUFF_GLOW2 +{SPR_FAXE,32787,4,NULL,S_AXEPUFF_GLOW4,0,0}, // S_AXEPUFF_GLOW3 +{SPR_FAXE,32788,4,NULL,S_AXEPUFF_GLOW5,0,0}, // S_AXEPUFF_GLOW4 +{SPR_FAXE,32789,4,NULL,S_AXEPUFF_GLOW6,0,0}, // S_AXEPUFF_GLOW5 +{SPR_FAXE,32790,4,NULL,S_AXEPUFF_GLOW7,0,0}, // S_AXEPUFF_GLOW6 +{SPR_FAXE,32791,4,NULL,S_NULL,0,0}, // S_AXEPUFF_GLOW7 +{SPR_FAXE,5,3,NULL,S_AXEBLOOD2,0,0}, // S_AXEBLOOD1 +{SPR_FAXE,6,3,NULL,S_AXEBLOOD3,0,0}, // S_AXEBLOOD2 +{SPR_FAXE,7,3,NULL,S_AXEBLOOD4,0,0}, // S_AXEBLOOD3 +{SPR_FAXE,8,3,NULL,S_AXEBLOOD5,0,0}, // S_AXEBLOOD4 +{SPR_FAXE,9,3,NULL,S_AXEBLOOD6,0,0}, // S_AXEBLOOD5 +{SPR_FAXE,10,3,NULL,S_NULL,0,0}, // S_AXEBLOOD6 +{SPR_WFHM,0,-1,NULL,S_NULL,0,0}, // S_HAMM +{SPR_FHMR,0,1,A_WeaponReady,S_FHAMMERREADY,0,0}, // S_FHAMMERREADY +{SPR_FHMR,0,1,A_Lower,S_FHAMMERDOWN,0,0}, // S_FHAMMERDOWN +{SPR_FHMR,0,1,A_Raise,S_FHAMMERUP,0,0}, // S_FHAMMERUP +{SPR_FHMR,1,6,NULL,S_FHAMMERATK_2,5,0}, // S_FHAMMERATK_1 +{SPR_FHMR,2,3,A_FHammerAttack,S_FHAMMERATK_3,5,0}, // S_FHAMMERATK_2 +{SPR_FHMR,3,3,NULL,S_FHAMMERATK_4,5,0}, // S_FHAMMERATK_3 +{SPR_FHMR,4,2,NULL,S_FHAMMERATK_5,5,0}, // S_FHAMMERATK_4 +{SPR_FHMR,4,10,A_FHammerThrow,S_FHAMMERATK_6,5,150}, // S_FHAMMERATK_5 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_7,0,60}, // S_FHAMMERATK_6 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_8,0,55}, // S_FHAMMERATK_7 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_9,0,50}, // S_FHAMMERATK_8 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_10,0,45}, // S_FHAMMERATK_9 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_11,0,40}, // S_FHAMMERATK_10 +{SPR_FHMR,0,1,NULL,S_FHAMMERATK_12,0,35}, // S_FHAMMERATK_11 +{SPR_FHMR,0,1,NULL,S_FHAMMERREADY,0,0}, // S_FHAMMERATK_12 +{SPR_FHFX,32768,2,NULL,S_HAMMER_MISSILE_2,0,0}, // S_HAMMER_MISSILE_1 +{SPR_FHFX,32769,2,A_ContMobjSound,S_HAMMER_MISSILE_3,0,0}, // S_HAMMER_MISSILE_2 +{SPR_FHFX,32770,2,NULL,S_HAMMER_MISSILE_4,0,0}, // S_HAMMER_MISSILE_3 +{SPR_FHFX,32771,2,NULL,S_HAMMER_MISSILE_5,0,0}, // S_HAMMER_MISSILE_4 +{SPR_FHFX,32772,2,NULL,S_HAMMER_MISSILE_6,0,0}, // S_HAMMER_MISSILE_5 +{SPR_FHFX,32773,2,NULL,S_HAMMER_MISSILE_7,0,0}, // S_HAMMER_MISSILE_6 +{SPR_FHFX,32774,2,NULL,S_HAMMER_MISSILE_8,0,0}, // S_HAMMER_MISSILE_7 +{SPR_FHFX,32775,2,NULL,S_HAMMER_MISSILE_1,0,0}, // S_HAMMER_MISSILE_8 +{SPR_FHFX,32776,3,NULL,S_HAMMER_MISSILE_X2,0,0}, // S_HAMMER_MISSILE_X1 +{SPR_FHFX,32777,3,NULL,S_HAMMER_MISSILE_X3,0,0}, // S_HAMMER_MISSILE_X2 +{SPR_FHFX,32778,3,A_Explode,S_HAMMER_MISSILE_X4,0,0}, // S_HAMMER_MISSILE_X3 +{SPR_FHFX,32779,3,NULL,S_HAMMER_MISSILE_X5,0,0}, // S_HAMMER_MISSILE_X4 +{SPR_FHFX,32780,3,NULL,S_HAMMER_MISSILE_X6,0,0}, // S_HAMMER_MISSILE_X5 +{SPR_FHFX,13,3,NULL,S_HAMMER_MISSILE_X7,0,0}, // S_HAMMER_MISSILE_X6 +{SPR_FHFX,32782,3,NULL,S_HAMMER_MISSILE_X8,0,0}, // S_HAMMER_MISSILE_X7 +{SPR_FHFX,32783,3,NULL,S_HAMMER_MISSILE_X9,0,0}, // S_HAMMER_MISSILE_X8 +{SPR_FHFX,32784,3,NULL,S_HAMMER_MISSILE_X10,0,0}, // S_HAMMER_MISSILE_X9 +{SPR_FHFX,32785,3,NULL,S_NULL,0,0}, // S_HAMMER_MISSILE_X10 +{SPR_FHFX,18,4,NULL,S_HAMMERPUFF2,0,0}, // S_HAMMERPUFF1 +{SPR_FHFX,19,4,NULL,S_HAMMERPUFF3,0,0}, // S_HAMMERPUFF2 +{SPR_FHFX,20,4,NULL,S_HAMMERPUFF4,0,0}, // S_HAMMERPUFF3 +{SPR_FHFX,21,4,NULL,S_HAMMERPUFF5,0,0}, // S_HAMMERPUFF4 +{SPR_FHFX,22,4,NULL,S_NULL,0,0}, // S_HAMMERPUFF5 +{SPR_FSRD,32768,1,A_WeaponReady,S_FSWORDREADY1,0,0}, // S_FSWORDREADY +{SPR_FSRD,32768,1,A_WeaponReady,S_FSWORDREADY2,0,0}, // S_FSWORDREADY1 +{SPR_FSRD,32768,1,A_WeaponReady,S_FSWORDREADY3,0,0}, // S_FSWORDREADY2 +{SPR_FSRD,32768,1,A_WeaponReady,S_FSWORDREADY4,0,0}, // S_FSWORDREADY3 +{SPR_FSRD,32769,1,A_WeaponReady,S_FSWORDREADY5,0,0}, // S_FSWORDREADY4 +{SPR_FSRD,32769,1,A_WeaponReady,S_FSWORDREADY6,0,0}, // S_FSWORDREADY5 +{SPR_FSRD,32769,1,A_WeaponReady,S_FSWORDREADY7,0,0}, // S_FSWORDREADY6 +{SPR_FSRD,32769,1,A_WeaponReady,S_FSWORDREADY8,0,0}, // S_FSWORDREADY7 +{SPR_FSRD,32770,1,A_WeaponReady,S_FSWORDREADY9,0,0}, // S_FSWORDREADY8 +{SPR_FSRD,32770,1,A_WeaponReady,S_FSWORDREADY10,0,0}, // S_FSWORDREADY9 +{SPR_FSRD,32770,1,A_WeaponReady,S_FSWORDREADY11,0,0}, // S_FSWORDREADY10 +{SPR_FSRD,32770,1,A_WeaponReady,S_FSWORDREADY,0,0}, // S_FSWORDREADY11 +{SPR_FSRD,32768,1,A_Lower,S_FSWORDDOWN,0,0}, // S_FSWORDDOWN +{SPR_FSRD,32768,1,A_Raise,S_FSWORDUP,0,0}, // S_FSWORDUP +{SPR_FSRD,32771,3,NULL,S_FSWORDATK_2,5,36}, // S_FSWORDATK_1 +{SPR_FSRD,32772,3,NULL,S_FSWORDATK_3,5,36}, // S_FSWORDATK_2 +{SPR_FSRD,32773,2,NULL,S_FSWORDATK_4,5,36}, // S_FSWORDATK_3 +{SPR_FSRD,32774,3,A_FSwordAttack,S_FSWORDATK_5,5,36}, // S_FSWORDATK_4 +{SPR_FSRD,32775,2,NULL,S_FSWORDATK_6,5,36}, // S_FSWORDATK_5 +{SPR_FSRD,32776,2,NULL,S_FSWORDATK_7,5,36}, // S_FSWORDATK_6 +{SPR_FSRD,32776,10,NULL,S_FSWORDATK_8,5,150}, // S_FSWORDATK_7 +{SPR_FSRD,32768,1,NULL,S_FSWORDATK_9,5,60}, // S_FSWORDATK_8 +{SPR_FSRD,32769,1,NULL,S_FSWORDATK_10,5,55}, // S_FSWORDATK_9 +{SPR_FSRD,32770,1,NULL,S_FSWORDATK_11,5,50}, // S_FSWORDATK_10 +{SPR_FSRD,32768,1,NULL,S_FSWORDATK_12,5,45}, // S_FSWORDATK_11 +{SPR_FSRD,32769,1,NULL,S_FSWORDREADY,5,40}, // S_FSWORDATK_12 +{SPR_FSFX,32768,3,NULL,S_FSWORD_MISSILE2,0,0}, // S_FSWORD_MISSILE1 +{SPR_FSFX,32769,3,NULL,S_FSWORD_MISSILE3,0,0}, // S_FSWORD_MISSILE2 +{SPR_FSFX,32770,3,NULL,S_FSWORD_MISSILE1,0,0}, // S_FSWORD_MISSILE3 +{SPR_FSFX,32771,4,NULL,S_FSWORD_MISSILE_X2,0,0}, // S_FSWORD_MISSILE_X1 +{SPR_FSFX,32772,3,A_FSwordFlames,S_FSWORD_MISSILE_X3,0,0}, // S_FSWORD_MISSILE_X2 +{SPR_FSFX,32773,4,A_Explode,S_FSWORD_MISSILE_X4,0,0}, // S_FSWORD_MISSILE_X3 +{SPR_FSFX,32774,3,NULL,S_FSWORD_MISSILE_X5,0,0}, // S_FSWORD_MISSILE_X4 +{SPR_FSFX,32775,4,NULL,S_FSWORD_MISSILE_X6,0,0}, // S_FSWORD_MISSILE_X5 +{SPR_FSFX,32776,3,NULL,S_FSWORD_MISSILE_X7,0,0}, // S_FSWORD_MISSILE_X6 +{SPR_FSFX,32777,4,NULL,S_FSWORD_MISSILE_X8,0,0}, // S_FSWORD_MISSILE_X7 +{SPR_FSFX,32778,3,NULL,S_FSWORD_MISSILE_X9,0,0}, // S_FSWORD_MISSILE_X8 +{SPR_FSFX,32779,3,NULL,S_FSWORD_MISSILE_X10,0,0}, // S_FSWORD_MISSILE_X9 +{SPR_FSFX,32780,3,NULL,S_NULL,0,0}, // S_FSWORD_MISSILE_X10 +{SPR_FSFX,32781,3,NULL,S_FSWORD_FLAME2,0,0}, // S_FSWORD_FLAME1 +{SPR_FSFX,32782,3,NULL,S_FSWORD_FLAME3,0,0}, // S_FSWORD_FLAME2 +{SPR_FSFX,32783,3,NULL,S_FSWORD_FLAME4,0,0}, // S_FSWORD_FLAME3 +{SPR_FSFX,32784,3,NULL,S_FSWORD_FLAME5,0,0}, // S_FSWORD_FLAME4 +{SPR_FSFX,32785,3,NULL,S_FSWORD_FLAME6,0,0}, // S_FSWORD_FLAME5 +{SPR_FSFX,32786,3,NULL,S_FSWORD_FLAME7,0,0}, // S_FSWORD_FLAME6 +{SPR_FSFX,32787,3,NULL,S_FSWORD_FLAME8,0,0}, // S_FSWORD_FLAME7 +{SPR_FSFX,32788,3,NULL,S_FSWORD_FLAME9,0,0}, // S_FSWORD_FLAME8 +{SPR_FSFX,32789,3,NULL,S_FSWORD_FLAME10,0,0}, // S_FSWORD_FLAME9 +{SPR_FSFX,32790,3,NULL,S_NULL,0,0}, // S_FSWORD_FLAME10 +{SPR_CMCE,0,1,A_WeaponReady,S_CMACEREADY,0,0}, // S_CMACEREADY +{SPR_CMCE,0,1,A_Lower,S_CMACEDOWN,0,0}, // S_CMACEDOWN +{SPR_CMCE,0,1,A_Raise,S_CMACEUP,0,0}, // S_CMACEUP +{SPR_CMCE,1,2,NULL,S_CMACEATK_2,60,20}, // S_CMACEATK_1 +{SPR_CMCE,1,1,NULL,S_CMACEATK_3,30,33}, // S_CMACEATK_2 +{SPR_CMCE,1,2,NULL,S_CMACEATK_4,8,45}, // S_CMACEATK_3 +{SPR_CMCE,2,1,NULL,S_CMACEATK_5,8,45}, // S_CMACEATK_4 +{SPR_CMCE,3,1,NULL,S_CMACEATK_6,8,45}, // S_CMACEATK_5 +{SPR_CMCE,4,1,NULL,S_CMACEATK_7,8,45}, // S_CMACEATK_6 +{SPR_CMCE,4,1,A_CMaceAttack,S_CMACEATK_8,-11,58}, // S_CMACEATK_7 +{SPR_CMCE,5,1,NULL,S_CMACEATK_9,8,45}, // S_CMACEATK_8 +{SPR_CMCE,5,2,NULL,S_CMACEATK_10,-8,74}, // S_CMACEATK_9 +{SPR_CMCE,5,1,NULL,S_CMACEATK_11,-20,96}, // S_CMACEATK_10 +{SPR_CMCE,5,8,NULL,S_CMACEATK_12,-33,160}, // S_CMACEATK_11 +{SPR_CMCE,0,2,A_ReFire,S_CMACEATK_13,8,75}, // S_CMACEATK_12 +{SPR_CMCE,0,1,NULL,S_CMACEATK_14,8,65}, // S_CMACEATK_13 +{SPR_CMCE,0,2,NULL,S_CMACEATK_15,8,60}, // S_CMACEATK_14 +{SPR_CMCE,0,1,NULL,S_CMACEATK_16,8,55}, // S_CMACEATK_15 +{SPR_CMCE,0,2,NULL,S_CMACEATK_17,8,50}, // S_CMACEATK_16 +{SPR_CMCE,0,1,NULL,S_CMACEREADY,8,45}, // S_CMACEATK_17 +{SPR_WCSS,0,-1,NULL,S_NULL,0,0}, // S_CSTAFF +{SPR_CSSF,2,4,NULL,S_CSTAFFREADY1,0,0}, // S_CSTAFFREADY +{SPR_CSSF,1,3,A_CStaffInitBlink,S_CSTAFFREADY2,0,0}, // S_CSTAFFREADY1 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY3,0,0}, // S_CSTAFFREADY2 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY4,0,0}, // S_CSTAFFREADY3 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY5,0,0}, // S_CSTAFFREADY4 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY6,0,0}, // S_CSTAFFREADY5 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY7,0,0}, // S_CSTAFFREADY6 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY8,0,0}, // S_CSTAFFREADY7 +{SPR_CSSF,0,1,A_WeaponReady,S_CSTAFFREADY9,0,0}, // S_CSTAFFREADY8 +{SPR_CSSF,0,1,A_CStaffCheckBlink,S_CSTAFFREADY2,0,0}, // S_CSTAFFREADY9 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFBLINK2,0,0}, // S_CSTAFFBLINK1 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFBLINK3,0,0}, // S_CSTAFFBLINK2 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFBLINK4,0,0}, // S_CSTAFFBLINK3 +{SPR_CSSF,2,1,A_WeaponReady,S_CSTAFFBLINK5,0,0}, // S_CSTAFFBLINK4 +{SPR_CSSF,2,1,A_WeaponReady,S_CSTAFFBLINK6,0,0}, // S_CSTAFFBLINK5 +{SPR_CSSF,2,1,A_WeaponReady,S_CSTAFFBLINK7,0,0}, // S_CSTAFFBLINK6 +{SPR_CSSF,2,1,A_WeaponReady,S_CSTAFFBLINK8,0,0}, // S_CSTAFFBLINK7 +{SPR_CSSF,2,1,A_WeaponReady,S_CSTAFFBLINK9,0,0}, // S_CSTAFFBLINK8 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFBLINK10,0,0}, // S_CSTAFFBLINK9 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFBLINK11,0,0}, // S_CSTAFFBLINK10 +{SPR_CSSF,1,1,A_WeaponReady,S_CSTAFFREADY2,0,0}, // S_CSTAFFBLINK11 +{SPR_CSSF,1,3,NULL,S_CSTAFFDOWN2,0,0}, // S_CSTAFFDOWN +{SPR_CSSF,2,4,NULL,S_CSTAFFDOWN3,0,0}, // S_CSTAFFDOWN2 +{SPR_CSSF,2,1,A_Lower,S_CSTAFFDOWN3,0,0}, // S_CSTAFFDOWN3 +{SPR_CSSF,2,1,A_Raise,S_CSTAFFUP,0,0}, // S_CSTAFFUP +{SPR_CSSF,0,1,A_CStaffCheck,S_CSTAFFATK_2,0,45}, // S_CSTAFFATK_1 +{SPR_CSSF,9,1,A_CStaffAttack,S_CSTAFFATK_3,0,50}, // S_CSTAFFATK_2 +{SPR_CSSF,9,2,NULL,S_CSTAFFATK_4,0,50}, // S_CSTAFFATK_3 +{SPR_CSSF,9,2,NULL,S_CSTAFFATK_5,0,45}, // S_CSTAFFATK_4 +{SPR_CSSF,0,2,NULL,S_CSTAFFATK_6,0,40}, // S_CSTAFFATK_5 +{SPR_CSSF,0,2,NULL,S_CSTAFFREADY2,0,36}, // S_CSTAFFATK_6 +{SPR_CSSF,10,10,NULL,S_CSTAFFREADY2,0,36}, // S_CSTAFFATK2_1 +{SPR_CSSF,32771,1,A_CStaffMissileSlither,S_CSTAFF_MISSILE2,0,0}, // S_CSTAFF_MISSILE1 +{SPR_CSSF,32771,1,A_CStaffMissileSlither,S_CSTAFF_MISSILE3,0,0}, // S_CSTAFF_MISSILE2 +{SPR_CSSF,32772,1,A_CStaffMissileSlither,S_CSTAFF_MISSILE4,0,0}, // S_CSTAFF_MISSILE3 +{SPR_CSSF,32772,1,A_CStaffMissileSlither,S_CSTAFF_MISSILE1,0,0}, // S_CSTAFF_MISSILE4 +{SPR_CSSF,32773,4,NULL,S_CSTAFF_MISSILE_X2,0,0}, // S_CSTAFF_MISSILE_X1 +{SPR_CSSF,32774,4,NULL,S_CSTAFF_MISSILE_X3,0,0}, // S_CSTAFF_MISSILE_X2 +{SPR_CSSF,32775,3,NULL,S_CSTAFF_MISSILE_X4,0,0}, // S_CSTAFF_MISSILE_X3 +{SPR_CSSF,32776,3,NULL,S_NULL,0,0}, // S_CSTAFF_MISSILE_X4 +{SPR_FHFX,18,4,NULL,S_CSTAFFPUFF2,0,0}, // S_CSTAFFPUFF1 +{SPR_FHFX,19,4,NULL,S_CSTAFFPUFF3,0,0}, // S_CSTAFFPUFF2 +{SPR_FHFX,20,4,NULL,S_CSTAFFPUFF4,0,0}, // S_CSTAFFPUFF3 +{SPR_FHFX,21,4,NULL,S_CSTAFFPUFF5,0,0}, // S_CSTAFFPUFF4 +{SPR_FHFX,22,4,NULL,S_NULL,0,0}, // S_CSTAFFPUFF5 +{SPR_WCFM,32768,4,NULL,S_CFLAME2,0,0}, // S_CFLAME1 +{SPR_WCFM,32769,4,NULL,S_CFLAME3,0,0}, // S_CFLAME2 +{SPR_WCFM,32770,4,NULL,S_CFLAME4,0,0}, // S_CFLAME3 +{SPR_WCFM,32771,4,NULL,S_CFLAME5,0,0}, // S_CFLAME4 +{SPR_WCFM,32772,4,NULL,S_CFLAME6,0,0}, // S_CFLAME5 +{SPR_WCFM,32773,4,NULL,S_CFLAME7,0,0}, // S_CFLAME6 +{SPR_WCFM,32774,4,NULL,S_CFLAME8,0,0}, // S_CFLAME7 +{SPR_WCFM,32775,4,NULL,S_CFLAME1,0,0}, // S_CFLAME8 +{SPR_CFLM,0,1,A_WeaponReady,S_CFLAMEREADY2,0,0}, // S_CFLAMEREADY1 +{SPR_CFLM,0,1,A_WeaponReady,S_CFLAMEREADY3,0,0}, // S_CFLAMEREADY2 +{SPR_CFLM,0,1,A_WeaponReady,S_CFLAMEREADY4,0,0}, // S_CFLAMEREADY3 +{SPR_CFLM,0,1,A_WeaponReady,S_CFLAMEREADY5,0,0}, // S_CFLAMEREADY4 +{SPR_CFLM,1,1,A_WeaponReady,S_CFLAMEREADY6,0,0}, // S_CFLAMEREADY5 +{SPR_CFLM,1,1,A_WeaponReady,S_CFLAMEREADY7,0,0}, // S_CFLAMEREADY6 +{SPR_CFLM,1,1,A_WeaponReady,S_CFLAMEREADY8,0,0}, // S_CFLAMEREADY7 +{SPR_CFLM,1,1,A_WeaponReady,S_CFLAMEREADY9,0,0}, // S_CFLAMEREADY8 +{SPR_CFLM,2,1,A_WeaponReady,S_CFLAMEREADY10,0,0}, // S_CFLAMEREADY9 +{SPR_CFLM,2,1,A_WeaponReady,S_CFLAMEREADY11,0,0}, // S_CFLAMEREADY10 +{SPR_CFLM,2,1,A_WeaponReady,S_CFLAMEREADY12,0,0}, // S_CFLAMEREADY11 +{SPR_CFLM,2,1,A_WeaponReady,S_CFLAMEREADY1,0,0}, // S_CFLAMEREADY12 +{SPR_CFLM,0,1,A_Lower,S_CFLAMEDOWN,0,0}, // S_CFLAMEDOWN +{SPR_CFLM,0,1,A_Raise,S_CFLAMEUP,0,0}, // S_CFLAMEUP +{SPR_CFLM,0,2,NULL,S_CFLAMEATK_2,0,40}, // S_CFLAMEATK_1 +{SPR_CFLM,3,2,NULL,S_CFLAMEATK_3,0,50}, // S_CFLAMEATK_2 +{SPR_CFLM,3,2,NULL,S_CFLAMEATK_4,0,36}, // S_CFLAMEATK_3 +{SPR_CFLM,32772,4,NULL,S_CFLAMEATK_5,0,0}, // S_CFLAMEATK_4 +{SPR_CFLM,32773,4,A_CFlameAttack,S_CFLAMEATK_6,0,0}, // S_CFLAMEATK_5 +{SPR_CFLM,32772,4,NULL,S_CFLAMEATK_7,0,0}, // S_CFLAMEATK_6 +{SPR_CFLM,6,2,NULL,S_CFLAMEATK_8,0,40}, // S_CFLAMEATK_7 +{SPR_CFLM,6,2,NULL,S_CFLAMEREADY1,0,0}, // S_CFLAMEATK_8 +{SPR_CFFX,32781,5,NULL,S_CFLAMEFLOOR2,0,0}, // S_CFLAMEFLOOR1 +{SPR_CFFX,32782,4,NULL,S_CFLAMEFLOOR3,0,0}, // S_CFLAMEFLOOR2 +{SPR_CFFX,32783,3,NULL,S_NULL,0,0}, // S_CFLAMEFLOOR3 +{SPR_CFFX,32768,3,NULL,S_FLAMEPUFF2,0,0}, // S_FLAMEPUFF1 +{SPR_CFFX,32769,3,NULL,S_FLAMEPUFF3,0,0}, // S_FLAMEPUFF2 +{SPR_CFFX,32770,3,NULL,S_FLAMEPUFF4,0,0}, // S_FLAMEPUFF3 +{SPR_CFFX,32771,4,NULL,S_FLAMEPUFF5,0,0}, // S_FLAMEPUFF4 +{SPR_CFFX,32772,3,NULL,S_FLAMEPUFF6,0,0}, // S_FLAMEPUFF5 +{SPR_CFFX,32773,4,NULL,S_FLAMEPUFF7,0,0}, // S_FLAMEPUFF6 +{SPR_CFFX,32774,3,NULL,S_FLAMEPUFF8,0,0}, // S_FLAMEPUFF7 +{SPR_CFFX,32775,4,NULL,S_FLAMEPUFF9,0,0}, // S_FLAMEPUFF8 +{SPR_CFFX,32776,3,NULL,S_FLAMEPUFF10,0,0}, // S_FLAMEPUFF9 +{SPR_CFFX,32777,4,NULL,S_FLAMEPUFF11,0,0}, // S_FLAMEPUFF10 +{SPR_CFFX,32778,3,NULL,S_FLAMEPUFF12,0,0}, // S_FLAMEPUFF11 +{SPR_CFFX,32779,4,NULL,S_FLAMEPUFF13,0,0}, // S_FLAMEPUFF12 +{SPR_CFFX,32780,3,NULL,S_NULL,0,0}, // S_FLAMEPUFF13 +{SPR_CFFX,32768,3,NULL,S_FLAMEPUFF2_2,0,0}, // S_FLAMEPUFF2_1 +{SPR_CFFX,32769,3,NULL,S_FLAMEPUFF2_3,0,0}, // S_FLAMEPUFF2_2 +{SPR_CFFX,32770,3,NULL,S_FLAMEPUFF2_4,0,0}, // S_FLAMEPUFF2_3 +{SPR_CFFX,32771,4,NULL,S_FLAMEPUFF2_5,0,0}, // S_FLAMEPUFF2_4 +{SPR_CFFX,32772,3,NULL,S_FLAMEPUFF2_6,0,0}, // S_FLAMEPUFF2_5 +{SPR_CFFX,32773,4,NULL,S_FLAMEPUFF2_7,0,0}, // S_FLAMEPUFF2_6 +{SPR_CFFX,32774,3,NULL,S_FLAMEPUFF2_8,0,0}, // S_FLAMEPUFF2_7 +{SPR_CFFX,32775,4,NULL,S_FLAMEPUFF2_9,0,0}, // S_FLAMEPUFF2_8 +{SPR_CFFX,32776,3,NULL,S_FLAMEPUFF2_10,0,0}, // S_FLAMEPUFF2_9 +{SPR_CFFX,32770,3,NULL,S_FLAMEPUFF2_11,0,0}, // S_FLAMEPUFF2_10 +{SPR_CFFX,32771,4,NULL,S_FLAMEPUFF2_12,0,0}, // S_FLAMEPUFF2_11 +{SPR_CFFX,32772,3,NULL,S_FLAMEPUFF2_13,0,0}, // S_FLAMEPUFF2_12 +{SPR_CFFX,32773,4,NULL,S_FLAMEPUFF2_14,0,0}, // S_FLAMEPUFF2_13 +{SPR_CFFX,32774,3,NULL,S_FLAMEPUFF2_15,0,0}, // S_FLAMEPUFF2_14 +{SPR_CFFX,32775,4,NULL,S_FLAMEPUFF2_16,0,0}, // S_FLAMEPUFF2_15 +{SPR_CFFX,32776,3,NULL,S_FLAMEPUFF2_17,0,0}, // S_FLAMEPUFF2_16 +{SPR_CFFX,32777,4,NULL,S_FLAMEPUFF2_18,0,0}, // S_FLAMEPUFF2_17 +{SPR_CFFX,32778,3,NULL,S_FLAMEPUFF2_19,0,0}, // S_FLAMEPUFF2_18 +{SPR_CFFX,32779,4,NULL,S_FLAMEPUFF2_20,0,0}, // S_FLAMEPUFF2_19 +{SPR_CFFX,32780,3,NULL,S_NULL,0,0}, // S_FLAMEPUFF2_20 +{SPR_CFCF,32768,4,NULL,S_CIRCLE_FLAME2,0,0}, // S_CIRCLE_FLAME1 +{SPR_CFCF,32769,2,A_CFlameRotate,S_CIRCLE_FLAME3,0,0}, // S_CIRCLE_FLAME2 +{SPR_CFCF,32770,2,NULL,S_CIRCLE_FLAME4,0,0}, // S_CIRCLE_FLAME3 +{SPR_CFCF,32771,1,NULL,S_CIRCLE_FLAME5,0,0}, // S_CIRCLE_FLAME4 +{SPR_CFCF,32772,2,NULL,S_CIRCLE_FLAME6,0,0}, // S_CIRCLE_FLAME5 +{SPR_CFCF,32773,2,A_CFlameRotate,S_CIRCLE_FLAME7,0,0}, // S_CIRCLE_FLAME6 +{SPR_CFCF,32774,1,NULL,S_CIRCLE_FLAME8,0,0}, // S_CIRCLE_FLAME7 +{SPR_CFCF,32775,2,NULL,S_CIRCLE_FLAME9,0,0}, // S_CIRCLE_FLAME8 +{SPR_CFCF,32776,2,NULL,S_CIRCLE_FLAME10,0,0}, // S_CIRCLE_FLAME9 +{SPR_CFCF,32777,1,A_CFlameRotate,S_CIRCLE_FLAME11,0,0}, // S_CIRCLE_FLAME10 +{SPR_CFCF,32778,2,NULL,S_CIRCLE_FLAME12,0,0}, // S_CIRCLE_FLAME11 +{SPR_CFCF,32779,3,NULL,S_CIRCLE_FLAME13,0,0}, // S_CIRCLE_FLAME12 +{SPR_CFCF,32780,3,NULL,S_CIRCLE_FLAME14,0,0}, // S_CIRCLE_FLAME13 +{SPR_CFCF,32781,2,A_CFlameRotate,S_CIRCLE_FLAME15,0,0}, // S_CIRCLE_FLAME14 +{SPR_CFCF,32782,3,NULL,S_CIRCLE_FLAME16,0,0}, // S_CIRCLE_FLAME15 +{SPR_CFCF,32783,2,NULL,S_NULL,0,0}, // S_CIRCLE_FLAME16 +{SPR_CFCF,32784,3,NULL,S_CIRCLE_FLAME_X2,0,0}, // S_CIRCLE_FLAME_X1 +{SPR_CFCF,32785,3,NULL,S_CIRCLE_FLAME_X3,0,0}, // S_CIRCLE_FLAME_X2 +{SPR_CFCF,32786,3,A_Explode,S_CIRCLE_FLAME_X4,0,0}, // S_CIRCLE_FLAME_X3 +{SPR_CFCF,32787,3,NULL,S_CIRCLE_FLAME_X5,0,0}, // S_CIRCLE_FLAME_X4 +{SPR_CFCF,32788,3,NULL,S_CIRCLE_FLAME_X6,0,0}, // S_CIRCLE_FLAME_X5 +{SPR_CFCF,32789,3,NULL,S_CIRCLE_FLAME_X7,0,0}, // S_CIRCLE_FLAME_X6 +{SPR_CFCF,32790,3,NULL,S_CIRCLE_FLAME_X8,0,0}, // S_CIRCLE_FLAME_X7 +{SPR_CFCF,32791,3,NULL,S_CIRCLE_FLAME_X9,0,0}, // S_CIRCLE_FLAME_X8 +{SPR_CFCF,32792,3,NULL,S_CIRCLE_FLAME_X10,0,0}, // S_CIRCLE_FLAME_X9 +{SPR_CFCF,32793,3,NULL,S_NULL,0,0}, // S_CIRCLE_FLAME_X10 +{SPR_CFFX,32768,4,NULL,S_CFLAME_MISSILE2,0,0}, // S_CFLAME_MISSILE1 +{SPR_CFFX,0,1,A_CFlamePuff,S_FLAMEPUFF1,0,0}, // S_CFLAME_MISSILE2 +{SPR_CFFX,32768,1,A_CFlameMissile,S_FLAMEPUFF1,0,0}, // S_CFLAME_MISSILE_X +{SPR_CHLY,0,1,A_WeaponReady,S_CHOLYREADY,0,0}, // S_CHOLYREADY +{SPR_CHLY,0,1,A_Lower,S_CHOLYDOWN,0,0}, // S_CHOLYDOWN +{SPR_CHLY,0,1,A_Raise,S_CHOLYUP,0,0}, // S_CHOLYUP +{SPR_CHLY,32768,1,NULL,S_CHOLYATK_2,0,40}, // S_CHOLYATK_1 +{SPR_CHLY,32769,1,NULL,S_CHOLYATK_3,0,40}, // S_CHOLYATK_2 +{SPR_CHLY,32770,2,NULL,S_CHOLYATK_4,0,43}, // S_CHOLYATK_3 +{SPR_CHLY,32771,2,NULL,S_CHOLYATK_5,0,43}, // S_CHOLYATK_4 +{SPR_CHLY,32772,2,NULL,S_CHOLYATK_6,0,45}, // S_CHOLYATK_5 +{SPR_CHLY,32773,6,A_CHolyAttack,S_CHOLYATK_7,0,48}, // S_CHOLYATK_6 +{SPR_CHLY,32774,2,A_CHolyPalette,S_CHOLYATK_8,0,40}, // S_CHOLYATK_7 +{SPR_CHLY,32774,2,A_CHolyPalette,S_CHOLYATK_9,0,40}, // S_CHOLYATK_8 +{SPR_CHLY,32774,2,A_CHolyPalette,S_CHOLYREADY,0,36}, // S_CHOLYATK_9 +{SPR_SPIR,0,2,A_CHolySeek,S_HOLY_FX2,0,0}, // S_HOLY_FX1 +{SPR_SPIR,0,2,A_CHolySeek,S_HOLY_FX3,0,0}, // S_HOLY_FX2 +{SPR_SPIR,1,2,A_CHolySeek,S_HOLY_FX4,0,0}, // S_HOLY_FX3 +{SPR_SPIR,1,2,A_CHolyCheckScream,S_HOLY_FX1,0,0}, // S_HOLY_FX4 +{SPR_SPIR,3,4,NULL,S_HOLY_FX_X2,0,0}, // S_HOLY_FX_X1 +{SPR_SPIR,4,4,A_Scream,S_HOLY_FX_X3,0,0}, // S_HOLY_FX_X2 +{SPR_SPIR,5,4,NULL,S_HOLY_FX_X4,0,0}, // S_HOLY_FX_X3 +{SPR_SPIR,6,4,NULL,S_HOLY_FX_X5,0,0}, // S_HOLY_FX_X4 +{SPR_SPIR,7,4,NULL,S_HOLY_FX_X6,0,0}, // S_HOLY_FX_X5 +{SPR_SPIR,8,4,NULL,S_NULL,0,0}, // S_HOLY_FX_X6 +{SPR_SPIR,2,1,A_CHolyTail,S_HOLY_TAIL1,0,0}, // S_HOLY_TAIL1 +{SPR_SPIR,3,-1,NULL,S_NULL,0,0}, // S_HOLY_TAIL2 +{SPR_SPIR,10,3,NULL,S_HOLY_PUFF2,0,0}, // S_HOLY_PUFF1 +{SPR_SPIR,11,3,NULL,S_HOLY_PUFF3,0,0}, // S_HOLY_PUFF2 +{SPR_SPIR,12,3,NULL,S_HOLY_PUFF4,0,0}, // S_HOLY_PUFF3 +{SPR_SPIR,13,3,NULL,S_HOLY_PUFF5,0,0}, // S_HOLY_PUFF4 +{SPR_SPIR,14,3,NULL,S_NULL,0,0}, // S_HOLY_PUFF5 +{SPR_SPIR,32783,3,A_CHolySpawnPuff,S_HOLY_MISSILE2,0,0}, // S_HOLY_MISSILE1 +{SPR_SPIR,32783,3,A_CHolySpawnPuff,S_HOLY_MISSILE3,0,0}, // S_HOLY_MISSILE2 +{SPR_SPIR,32783,3,A_CHolySpawnPuff,S_HOLY_MISSILE4,0,0}, // S_HOLY_MISSILE3 +{SPR_SPIR,32783,3,A_CHolySpawnPuff,S_HOLY_MISSILE_X,0,0}, // S_HOLY_MISSILE4 +{SPR_SPIR,32783,1,A_CHolyAttack2,S_NULL,0,0}, // S_HOLY_MISSILE_X +{SPR_SPIR,16,3,NULL,S_HOLY_MISSILE_P2,0,0}, // S_HOLY_MISSILE_P1 +{SPR_SPIR,17,3,NULL,S_HOLY_MISSILE_P3,0,0}, // S_HOLY_MISSILE_P2 +{SPR_SPIR,18,3,NULL,S_HOLY_MISSILE_P4,0,0}, // S_HOLY_MISSILE_P3 +{SPR_SPIR,19,3,NULL,S_HOLY_MISSILE_P5,0,0}, // S_HOLY_MISSILE_P4 +{SPR_SPIR,20,3,NULL,S_NULL,0,0}, // S_HOLY_MISSILE_P5 +{SPR_MWND,0,1,A_WeaponReady,S_MWANDREADY,0,0}, // S_MWANDREADY +{SPR_MWND,0,1,A_Lower,S_MWANDDOWN,0,0}, // S_MWANDDOWN +{SPR_MWND,0,1,A_Raise,S_MWANDUP,0,0}, // S_MWANDUP +{SPR_MWND,0,6,NULL,S_MWANDATK_2,0,0}, // S_MWANDATK_1 +{SPR_MWND,32769,6,A_MWandAttack,S_MWANDATK_3,0,48}, // S_MWANDATK_2 +{SPR_MWND,0,3,NULL,S_MWANDATK_4,0,40}, // S_MWANDATK_3 +{SPR_MWND,0,3,A_ReFire,S_MWANDREADY,0,36}, // S_MWANDATK_4 +{SPR_MWND,32772,4,NULL,S_MWANDPUFF2,0,0}, // S_MWANDPUFF1 +{SPR_MWND,32773,3,NULL,S_MWANDPUFF3,0,0}, // S_MWANDPUFF2 +{SPR_MWND,32774,4,NULL,S_MWANDPUFF4,0,0}, // S_MWANDPUFF3 +{SPR_MWND,32775,3,NULL,S_MWANDPUFF5,0,0}, // S_MWANDPUFF4 +{SPR_MWND,32776,4,NULL,S_NULL,0,0}, // S_MWANDPUFF5 +{SPR_MWND,2,4,NULL,S_MWANDSMOKE2,0,0}, // S_MWANDSMOKE1 +{SPR_MWND,3,4,NULL,S_MWANDSMOKE3,0,0}, // S_MWANDSMOKE2 +{SPR_MWND,2,4,NULL,S_MWANDSMOKE4,0,0}, // S_MWANDSMOKE3 +{SPR_MWND,3,4,NULL,S_NULL,0,0}, // S_MWANDSMOKE4 +{SPR_MWND,32770,4,NULL,S_MWAND_MISSILE2,0,0}, // S_MWAND_MISSILE1 +{SPR_MWND,32771,4,NULL,S_MWAND_MISSILE1,0,0}, // S_MWAND_MISSILE2 +{SPR_WMLG,32768,4,NULL,S_MW_LIGHTNING2,0,0}, // S_MW_LIGHTNING1 +{SPR_WMLG,32769,4,NULL,S_MW_LIGHTNING3,0,0}, // S_MW_LIGHTNING2 +{SPR_WMLG,32770,4,NULL,S_MW_LIGHTNING4,0,0}, // S_MW_LIGHTNING3 +{SPR_WMLG,32771,4,NULL,S_MW_LIGHTNING5,0,0}, // S_MW_LIGHTNING4 +{SPR_WMLG,32772,4,NULL,S_MW_LIGHTNING6,0,0}, // S_MW_LIGHTNING5 +{SPR_WMLG,32773,4,NULL,S_MW_LIGHTNING7,0,0}, // S_MW_LIGHTNING6 +{SPR_WMLG,32774,4,NULL,S_MW_LIGHTNING8,0,0}, // S_MW_LIGHTNING7 +{SPR_WMLG,32775,4,NULL,S_MW_LIGHTNING1,0,0}, // S_MW_LIGHTNING8 +{SPR_MLNG,32768,1,A_WeaponReady,S_MLIGHTNINGREADY2,0,0}, // S_MLIGHTNINGREADY +{SPR_MLNG,32768,1,A_WeaponReady,S_MLIGHTNINGREADY3,0,0}, // S_MLIGHTNINGREADY2 +{SPR_MLNG,32768,1,A_WeaponReady,S_MLIGHTNINGREADY4,0,0}, // S_MLIGHTNINGREADY3 +{SPR_MLNG,32768,1,A_WeaponReady,S_MLIGHTNINGREADY5,0,0}, // S_MLIGHTNINGREADY4 +{SPR_MLNG,32768,1,A_WeaponReady,S_MLIGHTNINGREADY6,0,0}, // S_MLIGHTNINGREADY5 +{SPR_MLNG,32768,1,A_LightningReady,S_MLIGHTNINGREADY7,0,0}, // S_MLIGHTNINGREADY6 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY8,0,0}, // S_MLIGHTNINGREADY7 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY9,0,0}, // S_MLIGHTNINGREADY8 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY10,0,0}, // S_MLIGHTNINGREADY9 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY11,0,0}, // S_MLIGHTNINGREADY10 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY12,0,0}, // S_MLIGHTNINGREADY11 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY13,0,0}, // S_MLIGHTNINGREADY12 +{SPR_MLNG,32770,1,A_WeaponReady,S_MLIGHTNINGREADY14,0,0}, // S_MLIGHTNINGREADY13 +{SPR_MLNG,32770,1,A_WeaponReady,S_MLIGHTNINGREADY15,0,0}, // S_MLIGHTNINGREADY14 +{SPR_MLNG,32770,1,A_WeaponReady,S_MLIGHTNINGREADY16,0,0}, // S_MLIGHTNINGREADY15 +{SPR_MLNG,32770,1,A_WeaponReady,S_MLIGHTNINGREADY17,0,0}, // S_MLIGHTNINGREADY16 +{SPR_MLNG,32770,1,A_WeaponReady,S_MLIGHTNINGREADY18,0,0}, // S_MLIGHTNINGREADY17 +{SPR_MLNG,32770,1,A_LightningReady,S_MLIGHTNINGREADY19,0,0}, // S_MLIGHTNINGREADY18 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY20,0,0}, // S_MLIGHTNINGREADY19 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY21,0,0}, // S_MLIGHTNINGREADY20 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY22,0,0}, // S_MLIGHTNINGREADY21 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY23,0,0}, // S_MLIGHTNINGREADY22 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY24,0,0}, // S_MLIGHTNINGREADY23 +{SPR_MLNG,32769,1,A_WeaponReady,S_MLIGHTNINGREADY,0,0}, // S_MLIGHTNINGREADY24 +{SPR_MLNG,32768,1,A_Lower,S_MLIGHTNINGDOWN,0,0}, // S_MLIGHTNINGDOWN +{SPR_MLNG,32768,1,A_Raise,S_MLIGHTNINGUP,0,0}, // S_MLIGHTNINGUP +{SPR_MLNG,32771,3,NULL,S_MLIGHTNINGATK_2,0,0}, // S_MLIGHTNINGATK_1 +{SPR_MLNG,32772,3,NULL,S_MLIGHTNINGATK_3,0,0}, // S_MLIGHTNINGATK_2 +{SPR_MLNG,32773,4,A_MLightningAttack,S_MLIGHTNINGATK_4,0,0}, // S_MLIGHTNINGATK_3 +{SPR_MLNG,32774,4,NULL,S_MLIGHTNINGATK_5,0,0}, // S_MLIGHTNINGATK_4 +{SPR_MLNG,32775,3,NULL,S_MLIGHTNINGATK_6,0,0}, // S_MLIGHTNINGATK_5 +{SPR_MLNG,32776,3,NULL,S_MLIGHTNINGATK_7,0,0}, // S_MLIGHTNINGATK_6 +{SPR_MLNG,32776,6,NULL,S_MLIGHTNINGATK_8,0,199}, // S_MLIGHTNINGATK_7 +{SPR_MLNG,32770,2,NULL,S_MLIGHTNINGATK_9,0,55}, // S_MLIGHTNINGATK_8 +{SPR_MLNG,32769,2,NULL,S_MLIGHTNINGATK_10,0,50}, // S_MLIGHTNINGATK_9 +{SPR_MLNG,32769,2,NULL,S_MLIGHTNINGATK_11,0,45}, // S_MLIGHTNINGATK_10 +{SPR_MLNG,32769,2,NULL,S_MLIGHTNINGREADY,0,40}, // S_MLIGHTNINGATK_11 +{SPR_MLFX,32768,2,A_LightningZap,S_LIGHTNING_CEILING2,0,0}, // S_LIGHTNING_CEILING1 +{SPR_MLFX,32769,2,A_LightningClip,S_LIGHTNING_CEILING3,0,0}, // S_LIGHTNING_CEILING2 +{SPR_MLFX,32770,2,A_LightningClip,S_LIGHTNING_CEILING4,0,0}, // S_LIGHTNING_CEILING3 +{SPR_MLFX,32771,2,A_LightningClip,S_LIGHTNING_CEILING1,0,0}, // S_LIGHTNING_CEILING4 +{SPR_MLF2,32768,2,A_LightningRemove,S_LIGHTNING_C_X2,0,0}, // S_LIGHTNING_C_X1 +{SPR_MLF2,32769,3,NULL,S_LIGHTNING_C_X3,0,0}, // S_LIGHTNING_C_X2 +{SPR_MLF2,32770,3,NULL,S_LIGHTNING_C_X4,0,0}, // S_LIGHTNING_C_X3 +{SPR_MLF2,32771,3,NULL,S_LIGHTNING_C_X5,0,0}, // S_LIGHTNING_C_X4 +{SPR_MLF2,32772,3,NULL,S_LIGHTNING_C_X6,0,0}, // S_LIGHTNING_C_X5 +{SPR_MLF2,32778,3,NULL,S_LIGHTNING_C_X7,0,0}, // S_LIGHTNING_C_X6 +{SPR_MLF2,32779,3,NULL,S_LIGHTNING_C_X8,0,0}, // S_LIGHTNING_C_X7 +{SPR_MLF2,32780,3,NULL,S_LIGHTNING_C_X9,0,0}, // S_LIGHTNING_C_X8 +{SPR_ACLO,4,35,NULL,S_LIGHTNING_C_X10,0,0}, // S_LIGHTNING_C_X9 +{SPR_MLF2,32781,3,NULL,S_LIGHTNING_C_X11,0,0}, // S_LIGHTNING_C_X10 +{SPR_MLF2,32782,3,NULL,S_LIGHTNING_C_X12,0,0}, // S_LIGHTNING_C_X11 +{SPR_MLF2,32783,4,NULL,S_LIGHTNING_C_X13,0,0}, // S_LIGHTNING_C_X12 +{SPR_MLF2,32784,3,NULL,S_LIGHTNING_C_X14,0,0}, // S_LIGHTNING_C_X13 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_C_X15,0,0}, // S_LIGHTNING_C_X14 +{SPR_MLF2,32784,4,NULL,S_LIGHTNING_C_X16,0,0}, // S_LIGHTNING_C_X15 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_C_X17,0,0}, // S_LIGHTNING_C_X16 +{SPR_MLF2,32782,3,NULL,S_LIGHTNING_C_X18,0,0}, // S_LIGHTNING_C_X17 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_C_X19,0,0}, // S_LIGHTNING_C_X18 +{SPR_MLF2,32783,1,A_HideThing,S_FREETARGMOBJ,0,0}, // S_LIGHTNING_C_X19 +{SPR_MLFX,32772,2,A_LightningZap,S_LIGHTNING_FLOOR2,0,0}, // S_LIGHTNING_FLOOR1 +{SPR_MLFX,32773,2,A_LightningClip,S_LIGHTNING_FLOOR3,0,0}, // S_LIGHTNING_FLOOR2 +{SPR_MLFX,32774,2,A_LightningClip,S_LIGHTNING_FLOOR4,0,0}, // S_LIGHTNING_FLOOR3 +{SPR_MLFX,32775,2,A_LightningClip,S_LIGHTNING_FLOOR1,0,0}, // S_LIGHTNING_FLOOR4 +{SPR_MLF2,32773,2,A_LightningRemove,S_LIGHTNING_F_X2,0,0}, // S_LIGHTNING_F_X1 +{SPR_MLF2,32774,3,NULL,S_LIGHTNING_F_X3,0,0}, // S_LIGHTNING_F_X2 +{SPR_MLF2,32775,3,NULL,S_LIGHTNING_F_X4,0,0}, // S_LIGHTNING_F_X3 +{SPR_MLF2,32776,3,NULL,S_LIGHTNING_F_X5,0,0}, // S_LIGHTNING_F_X4 +{SPR_MLF2,32777,3,NULL,S_LIGHTNING_F_X6,0,0}, // S_LIGHTNING_F_X5 +{SPR_MLF2,32778,3,NULL,S_LIGHTNING_F_X7,0,0}, // S_LIGHTNING_F_X6 +{SPR_MLF2,32779,3,NULL,S_LIGHTNING_F_X8,0,0}, // S_LIGHTNING_F_X7 +{SPR_MLF2,32780,3,NULL,S_LIGHTNING_F_X9,0,0}, // S_LIGHTNING_F_X8 +{SPR_ACLO,4,20,NULL,S_LIGHTNING_F_X10,0,0}, // S_LIGHTNING_F_X9 +{SPR_MLF2,32781,3,NULL,S_LIGHTNING_F_X11,0,0}, // S_LIGHTNING_F_X10 +{SPR_MLF2,32782,3,NULL,S_LIGHTNING_F_X12,0,0}, // S_LIGHTNING_F_X11 +{SPR_MLF2,32783,4,NULL,S_LIGHTNING_F_X13,0,0}, // S_LIGHTNING_F_X12 +{SPR_MLF2,32784,3,NULL,S_LIGHTNING_F_X14,0,0}, // S_LIGHTNING_F_X13 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_F_X15,0,0}, // S_LIGHTNING_F_X14 +{SPR_MLF2,32784,4,A_LastZap,S_LIGHTNING_F_X16,0,0}, // S_LIGHTNING_F_X15 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_F_X17,0,0}, // S_LIGHTNING_F_X16 +{SPR_MLF2,32782,3,NULL,S_LIGHTNING_F_X18,0,0}, // S_LIGHTNING_F_X17 +{SPR_MLF2,32783,3,NULL,S_LIGHTNING_F_X19,0,0}, // S_LIGHTNING_F_X18 +{SPR_MLF2,32783,1,A_HideThing,S_FREETARGMOBJ,0,0}, // S_LIGHTNING_F_X19 +{SPR_MLFX,32776,2,A_ZapMimic,S_LIGHTNING_ZAP2,0,0}, // S_LIGHTNING_ZAP1 +{SPR_MLFX,32777,2,A_ZapMimic,S_LIGHTNING_ZAP3,0,0}, // S_LIGHTNING_ZAP2 +{SPR_MLFX,32778,2,A_ZapMimic,S_LIGHTNING_ZAP4,0,0}, // S_LIGHTNING_ZAP3 +{SPR_MLFX,32779,2,A_ZapMimic,S_LIGHTNING_ZAP5,0,0}, // S_LIGHTNING_ZAP4 +{SPR_MLFX,32780,2,A_ZapMimic,S_LIGHTNING_ZAP1,0,0}, // S_LIGHTNING_ZAP5 +{SPR_MLFX,32781,2,NULL,S_LIGHTNING_ZAP_X2,0,0}, // S_LIGHTNING_ZAP_X1 +{SPR_MLFX,32782,2,NULL,S_LIGHTNING_ZAP_X3,0,0}, // S_LIGHTNING_ZAP_X2 +{SPR_MLFX,32783,2,NULL,S_LIGHTNING_ZAP_X4,0,0}, // S_LIGHTNING_ZAP_X3 +{SPR_MLFX,32784,2,NULL,S_LIGHTNING_ZAP_X5,0,0}, // S_LIGHTNING_ZAP_X4 +{SPR_MLFX,32785,2,NULL,S_LIGHTNING_ZAP_X6,0,0}, // S_LIGHTNING_ZAP_X5 +{SPR_MLFX,32786,2,NULL,S_LIGHTNING_ZAP_X7,0,0}, // S_LIGHTNING_ZAP_X6 +{SPR_MLFX,32787,2,NULL,S_LIGHTNING_ZAP_X8,0,0}, // S_LIGHTNING_ZAP_X7 +{SPR_MLFX,32788,2,NULL,S_NULL,0,0}, // S_LIGHTNING_ZAP_X8 +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY2,0,0}, // S_MSTAFFREADY +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY3,0,0}, // S_MSTAFFREADY2 +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY4,0,0}, // S_MSTAFFREADY3 +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY5,0,0}, // S_MSTAFFREADY4 +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY6,0,0}, // S_MSTAFFREADY5 +{SPR_MSTF,0,1,A_WeaponReady,S_MSTAFFREADY7,0,0}, // S_MSTAFFREADY6 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY8,0,0}, // S_MSTAFFREADY7 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY9,0,0}, // S_MSTAFFREADY8 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY10,0,0}, // S_MSTAFFREADY9 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY11,0,0}, // S_MSTAFFREADY10 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY12,0,0}, // S_MSTAFFREADY11 +{SPR_MSTF,1,1,A_WeaponReady,S_MSTAFFREADY13,0,0}, // S_MSTAFFREADY12 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY14,0,0}, // S_MSTAFFREADY13 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY15,0,0}, // S_MSTAFFREADY14 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY16,0,0}, // S_MSTAFFREADY15 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY17,0,0}, // S_MSTAFFREADY16 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY18,0,0}, // S_MSTAFFREADY17 +{SPR_MSTF,2,1,A_WeaponReady,S_MSTAFFREADY19,0,0}, // S_MSTAFFREADY18 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY20,0,0}, // S_MSTAFFREADY19 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY21,0,0}, // S_MSTAFFREADY20 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY22,0,0}, // S_MSTAFFREADY21 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY23,0,0}, // S_MSTAFFREADY22 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY24,0,0}, // S_MSTAFFREADY23 +{SPR_MSTF,3,1,A_WeaponReady,S_MSTAFFREADY25,0,0}, // S_MSTAFFREADY24 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY26,0,0}, // S_MSTAFFREADY25 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY27,0,0}, // S_MSTAFFREADY26 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY28,0,0}, // S_MSTAFFREADY27 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY29,0,0}, // S_MSTAFFREADY28 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY30,0,0}, // S_MSTAFFREADY29 +{SPR_MSTF,4,1,A_WeaponReady,S_MSTAFFREADY31,0,0}, // S_MSTAFFREADY30 +{SPR_MSTF,5,1,A_WeaponReady,S_MSTAFFREADY32,0,0}, // S_MSTAFFREADY31 +{SPR_MSTF,5,1,A_WeaponReady,S_MSTAFFREADY33,0,0}, // S_MSTAFFREADY32 +{SPR_MSTF,5,1,A_WeaponReady,S_MSTAFFREADY34,0,0}, // S_MSTAFFREADY33 +{SPR_MSTF,5,1,A_WeaponReady,S_MSTAFFREADY35,0,0}, // S_MSTAFFREADY34 +{SPR_MSTF,5,1,A_WeaponReady,S_MSTAFFREADY,0,0}, // S_MSTAFFREADY35 +{SPR_MSTF,0,1,A_Lower,S_MSTAFFDOWN,0,0}, // S_MSTAFFDOWN +{SPR_MSTF,0,1,A_Raise,S_MSTAFFUP,0,0}, // S_MSTAFFUP +{SPR_MSTF,6,4,NULL,S_MSTAFFATK_2,0,40}, // S_MSTAFFATK_1 +{SPR_MSTF,32775,4,A_MStaffAttack,S_MSTAFFATK_3,0,48}, // S_MSTAFFATK_2 +{SPR_MSTF,32775,2,A_MStaffPalette,S_MSTAFFATK_4,0,48}, // S_MSTAFFATK_3 +{SPR_MSTF,8,2,A_MStaffPalette,S_MSTAFFATK_5,0,48}, // S_MSTAFFATK_4 +{SPR_MSTF,8,2,A_MStaffPalette,S_MSTAFFATK_6,0,48}, // S_MSTAFFATK_5 +{SPR_MSTF,8,1,NULL,S_MSTAFFATK_7,0,40}, // S_MSTAFFATK_6 +{SPR_MSTF,9,5,NULL,S_MSTAFFREADY,0,36}, // S_MSTAFFATK_7 +{SPR_MSP1,32768,3,A_MStaffWeave,S_MSTAFF_FX1_2,0,0}, // S_MSTAFF_FX1_1 +{SPR_MSP1,32769,3,A_MStaffWeave,S_MSTAFF_FX1_3,0,0}, // S_MSTAFF_FX1_2 +{SPR_MSP1,32770,3,A_MStaffWeave,S_MSTAFF_FX1_4,0,0}, // S_MSTAFF_FX1_3 +{SPR_MSP1,32771,3,A_MStaffWeave,S_MSTAFF_FX1_5,0,0}, // S_MSTAFF_FX1_4 +{SPR_MSP1,32772,3,A_MStaffWeave,S_MSTAFF_FX1_6,0,0}, // S_MSTAFF_FX1_5 +{SPR_MSP1,32773,3,A_MStaffWeave,S_MSTAFF_FX1_1,0,0}, // S_MSTAFF_FX1_6 +{SPR_MSP1,32774,4,NULL,S_MSTAFF_FX_X2,0,0}, // S_MSTAFF_FX_X1 +{SPR_MSP1,32775,5,A_Explode,S_MSTAFF_FX_X3,0,0}, // S_MSTAFF_FX_X2 +{SPR_MSP1,32776,4,NULL,S_MSTAFF_FX_X4,0,0}, // S_MSTAFF_FX_X3 +{SPR_MSP1,32777,5,NULL,S_MSTAFF_FX_X5,0,0}, // S_MSTAFF_FX_X4 +{SPR_MSP1,32778,4,NULL,S_MSTAFF_FX_X6,0,0}, // S_MSTAFF_FX_X5 +{SPR_MSP1,32779,5,NULL,S_MSTAFF_FX_X7,0,0}, // S_MSTAFF_FX_X6 +{SPR_MSP1,32780,4,NULL,S_MSTAFF_FX_X8,0,0}, // S_MSTAFF_FX_X7 +{SPR_MSP1,32781,5,NULL,S_MSTAFF_FX_X9,0,0}, // S_MSTAFF_FX_X8 +{SPR_MSP1,32782,4,NULL,S_MSTAFF_FX_X10,0,0}, // S_MSTAFF_FX_X9 +{SPR_MSP1,32783,4,NULL,S_NULL,0,0}, // S_MSTAFF_FX_X10 +{SPR_MSP2,32768,2,A_MStaffTrack,S_MSTAFF_FX2_2,0,0}, // S_MSTAFF_FX2_1 +{SPR_MSP2,32769,2,A_MStaffTrack,S_MSTAFF_FX2_3,0,0}, // S_MSTAFF_FX2_2 +{SPR_MSP2,32770,2,A_MStaffTrack,S_MSTAFF_FX2_4,0,0}, // S_MSTAFF_FX2_3 +{SPR_MSP2,32771,2,A_MStaffTrack,S_MSTAFF_FX2_1,0,0}, // S_MSTAFF_FX2_4 +{SPR_MSP2,32772,4,NULL,S_MSTAFF_FX2_X2,0,0}, // S_MSTAFF_FX2_X1 +{SPR_MSP2,32773,5,A_Explode,S_MSTAFF_FX2_X3,0,0}, // S_MSTAFF_FX2_X2 +{SPR_MSP2,32774,5,NULL,S_MSTAFF_FX2_X4,0,0}, // S_MSTAFF_FX2_X3 +{SPR_MSP2,32775,5,NULL,S_MSTAFF_FX2_X5,0,0}, // S_MSTAFF_FX2_X4 +{SPR_MSP2,32776,4,NULL,S_NULL,0,0}, // S_MSTAFF_FX2_X5 +{SPR_WFR1,32768,-1,NULL,S_NULL,0,0}, // S_FSWORD1 +{SPR_WFR2,32768,-1,NULL,S_NULL,0,0}, // S_FSWORD2 +{SPR_WFR3,32768,-1,NULL,S_NULL,0,0}, // S_FSWORD3 +{SPR_WCH1,32768,-1,NULL,S_NULL,0,0}, // S_CHOLY1 +{SPR_WCH2,32768,-1,NULL,S_NULL,0,0}, // S_CHOLY2 +{SPR_WCH3,32768,-1,NULL,S_NULL,0,0}, // S_CHOLY3 +{SPR_WMS1,32768,-1,NULL,S_NULL,0,0}, // S_MSTAFF1 +{SPR_WMS2,32768,-1,NULL,S_NULL,0,0}, // S_MSTAFF2 +{SPR_WMS3,32768,-1,NULL,S_NULL,0,0}, // S_MSTAFF3 +{SPR_WPIG,0,1,A_WeaponReady,S_SNOUTREADY,0,0}, // S_SNOUTREADY +{SPR_WPIG,0,1,A_Lower,S_SNOUTDOWN,0,0}, // S_SNOUTDOWN +{SPR_WPIG,0,1,A_Raise,S_SNOUTUP,0,0}, // S_SNOUTUP +{SPR_WPIG,0,4,A_SnoutAttack,S_SNOUTATK2,0,0}, // S_SNOUTATK1 +{SPR_WPIG,1,8,A_SnoutAttack,S_SNOUTREADY,0,0}, // S_SNOUTATK2 +{SPR_WMCS,32768,8,NULL,S_COS2,0,0}, // S_COS1 +{SPR_WMCS,32769,8,NULL,S_COS3,0,0}, // S_COS2 +{SPR_WMCS,32770,8,NULL,S_COS1,0,0}, // S_COS3 +{SPR_CONE,0,1,A_WeaponReady,S_CONEREADY,0,0}, // S_CONEREADY +{SPR_CONE,0,1,A_Lower,S_CONEDOWN,0,0}, // S_CONEDOWN +{SPR_CONE,0,1,A_Raise,S_CONEUP,0,0}, // S_CONEUP +{SPR_CONE,1,3,NULL,S_CONEATK1_2,0,0}, // S_CONEATK1_1 +{SPR_CONE,2,4,NULL,S_CONEATK1_3,0,0}, // S_CONEATK1_2 +{SPR_CONE,3,3,NULL,S_CONEATK1_4,0,0}, // S_CONEATK1_3 +{SPR_CONE,4,5,NULL,S_CONEATK1_5,0,0}, // S_CONEATK1_4 +{SPR_CONE,5,3,A_FireConePL1,S_CONEATK1_6,0,0}, // S_CONEATK1_5 +{SPR_CONE,6,3,NULL,S_CONEATK1_7,0,0}, // S_CONEATK1_6 +{SPR_CONE,0,9,NULL,S_CONEATK1_8,0,0}, // S_CONEATK1_7 +{SPR_CONE,0,10,A_ReFire,S_CONEREADY,0,0}, // S_CONEATK1_8 +{SPR_SHRD,32768,2,NULL,S_SHARDFX1_2,0,0}, // S_SHARDFX1_1 +{SPR_SHRD,32768,3,A_ShedShard,S_SHARDFX1_3,0,0}, // S_SHARDFX1_2 +{SPR_SHRD,32769,3,NULL,S_SHARDFX1_4,0,0}, // S_SHARDFX1_3 +{SPR_SHRD,32770,3,NULL,S_SHARDFX1_1,0,0}, // S_SHARDFX1_4 +{SPR_SHEX,32768,5,NULL,S_SHARDFXE1_2,0,0}, // S_SHARDFXE1_1 +{SPR_SHEX,32769,5,NULL,S_SHARDFXE1_3,0,0}, // S_SHARDFXE1_2 +{SPR_SHEX,32770,5,NULL,S_SHARDFXE1_4,0,0}, // S_SHARDFXE1_3 +{SPR_SHEX,32771,5,NULL,S_SHARDFXE1_5,0,0}, // S_SHARDFXE1_4 +{SPR_SHEX,32772,5,NULL,S_NULL,0,0}, // S_SHARDFXE1_5 +{SPR_BLOD,2,8,NULL,S_BLOOD2,0,0}, // S_BLOOD1 +{SPR_BLOD,1,8,NULL,S_BLOOD3,0,0}, // S_BLOOD2 +{SPR_BLOD,0,8,NULL,S_NULL,0,0}, // S_BLOOD3 +{SPR_BLOD,2,8,NULL,S_BLOODSPLATTER2,0,0}, // S_BLOODSPLATTER1 +{SPR_BLOD,1,8,NULL,S_BLOODSPLATTER3,0,0}, // S_BLOODSPLATTER2 +{SPR_BLOD,0,8,NULL,S_NULL,0,0}, // S_BLOODSPLATTER3 +{SPR_BLOD,0,6,NULL,S_NULL,0,0}, // S_BLOODSPLATTERX +{SPR_GIBS,0,-1,NULL,S_NULL,0,0}, // S_GIBS1 +{SPR_PLAY,0,-1,NULL,S_NULL,0,0}, // S_FPLAY +{SPR_PLAY,0,4,NULL,S_FPLAY_RUN2,0,0}, // S_FPLAY_RUN1 +{SPR_PLAY,1,4,NULL,S_FPLAY_RUN3,0,0}, // S_FPLAY_RUN2 +{SPR_PLAY,2,4,NULL,S_FPLAY_RUN4,0,0}, // S_FPLAY_RUN3 +{SPR_PLAY,3,4,NULL,S_FPLAY_RUN1,0,0}, // S_FPLAY_RUN4 +{SPR_PLAY,4,8,NULL,S_FPLAY_ATK2,0,0}, // S_FPLAY_ATK1 +{SPR_PLAY,5,8,NULL,S_FPLAY,0,0}, // S_FPLAY_ATK2 +{SPR_PLAY,6,4,NULL,S_FPLAY_PAIN2,0,0}, // S_FPLAY_PAIN +{SPR_PLAY,6,4,A_Pain,S_FPLAY,0,0}, // S_FPLAY_PAIN2 +{SPR_PLAY,7,6,NULL,S_FPLAY_DIE2,0,0}, // S_FPLAY_DIE1 +{SPR_PLAY,8,6,A_Scream,S_FPLAY_DIE3,0,0}, // S_FPLAY_DIE2 +{SPR_PLAY,9,6,NULL,S_FPLAY_DIE4,0,0}, // S_FPLAY_DIE3 +{SPR_PLAY,10,6,NULL,S_FPLAY_DIE5,0,0}, // S_FPLAY_DIE4 +{SPR_PLAY,11,6,A_NoBlocking,S_FPLAY_DIE6,0,0}, // S_FPLAY_DIE5 +{SPR_PLAY,12,6,NULL,S_FPLAY_DIE7,0,0}, // S_FPLAY_DIE6 +{SPR_PLAY,13,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_FPLAY_DIE7 +{SPR_PLAY,14,5,A_Scream,S_FPLAY_XDIE2,0,0}, // S_FPLAY_XDIE1 +{SPR_PLAY,15,5,A_SkullPop,S_FPLAY_XDIE3,0,0}, // S_FPLAY_XDIE2 +{SPR_PLAY,17,5,A_NoBlocking,S_FPLAY_XDIE4,0,0}, // S_FPLAY_XDIE3 +{SPR_PLAY,18,5,NULL,S_FPLAY_XDIE5,0,0}, // S_FPLAY_XDIE4 +{SPR_PLAY,19,5,NULL,S_FPLAY_XDIE6,0,0}, // S_FPLAY_XDIE5 +{SPR_PLAY,20,5,NULL,S_FPLAY_XDIE7,0,0}, // S_FPLAY_XDIE6 +{SPR_PLAY,21,5,NULL,S_FPLAY_XDIE8,0,0}, // S_FPLAY_XDIE7 +{SPR_PLAY,22,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_FPLAY_XDIE8 +{SPR_PLAY,23,5,A_FreezeDeath,S_FPLAY_ICE2,0,0}, // S_FPLAY_ICE +{SPR_PLAY,23,1,A_FreezeDeathChunks,S_FPLAY_ICE2,0,0}, // S_FPLAY_ICE2 +{SPR_FDTH,32768,5,NULL,S_PLAY_F_FDTH2,0,0}, // S_PLAY_F_FDTH1 +{SPR_FDTH,32769,4,NULL,S_PLAY_FDTH3,0,0}, // S_PLAY_F_FDTH2 +{SPR_FDTH,32770,5,NULL,S_PLAY_C_FDTH2,0,0}, // S_PLAY_C_FDTH1 +{SPR_FDTH,32771,4,NULL,S_PLAY_FDTH3,0,0}, // S_PLAY_C_FDTH2 +{SPR_FDTH,32772,5,NULL,S_PLAY_M_FDTH2,0,0}, // S_PLAY_M_FDTH1 +{SPR_FDTH,32773,4,NULL,S_PLAY_FDTH3,0,0}, // S_PLAY_M_FDTH2 +{SPR_FDTH,32774,5,NULL,S_PLAY_FDTH4,0,0}, // S_PLAY_FDTH3 +{SPR_FDTH,32775,4,A_Scream,S_PLAY_FDTH5,0,0}, // S_PLAY_FDTH4 +{SPR_FDTH,32776,5,NULL,S_PLAY_FDTH6,0,0}, // S_PLAY_FDTH5 +{SPR_FDTH,32777,4,NULL,S_PLAY_FDTH7,0,0}, // S_PLAY_FDTH6 +{SPR_FDTH,32778,5,NULL,S_PLAY_FDTH8,0,0}, // S_PLAY_FDTH7 +{SPR_FDTH,32779,4,NULL,S_PLAY_FDTH9,0,0}, // S_PLAY_FDTH8 +{SPR_FDTH,32780,5,NULL,S_PLAY_FDTH10,0,0}, // S_PLAY_FDTH9 +{SPR_FDTH,32781,4,NULL,S_PLAY_FDTH11,0,0}, // S_PLAY_FDTH10 +{SPR_FDTH,32782,5,NULL,S_PLAY_FDTH12,0,0}, // S_PLAY_FDTH11 +{SPR_FDTH,32783,4,NULL,S_PLAY_FDTH13,0,0}, // S_PLAY_FDTH12 +{SPR_FDTH,32784,5,NULL,S_PLAY_FDTH14,0,0}, // S_PLAY_FDTH13 +{SPR_FDTH,32785,4,NULL,S_PLAY_FDTH15,0,0}, // S_PLAY_FDTH14 +{SPR_FDTH,32786,5,A_NoBlocking,S_PLAY_FDTH16,0,0}, // S_PLAY_FDTH15 +{SPR_FDTH,32787,4,NULL,S_PLAY_FDTH17,0,0}, // S_PLAY_FDTH16 +{SPR_FDTH,32788,5,NULL,S_PLAY_FDTH18,0,0}, // S_PLAY_FDTH17 +{SPR_FDTH,32789,4,NULL,S_PLAY_FDTH19,0,0}, // S_PLAY_FDTH18 +{SPR_ACLO,4,35,A_CheckBurnGone,S_PLAY_FDTH19,0,0}, // S_PLAY_FDTH19 +{SPR_ACLO,4,8,NULL,S_NULL,0,0}, // S_PLAY_FDTH20 +{SPR_BSKL,0,5,A_CheckSkullFloor,S_BLOODYSKULL2,0,0}, // S_BLOODYSKULL1 +{SPR_BSKL,1,5,A_CheckSkullFloor,S_BLOODYSKULL3,0,0}, // S_BLOODYSKULL2 +{SPR_BSKL,2,5,A_CheckSkullFloor,S_BLOODYSKULL4,0,0}, // S_BLOODYSKULL3 +{SPR_BSKL,3,5,A_CheckSkullFloor,S_BLOODYSKULL5,0,0}, // S_BLOODYSKULL4 +{SPR_BSKL,5,5,A_CheckSkullFloor,S_BLOODYSKULL6,0,0}, // S_BLOODYSKULL5 +{SPR_BSKL,6,5,A_CheckSkullFloor,S_BLOODYSKULL7,0,0}, // S_BLOODYSKULL6 +{SPR_BSKL,7,5,A_CheckSkullFloor,S_BLOODYSKULL1,0,0}, // S_BLOODYSKULL7 +{SPR_BSKL,8,16,A_CheckSkullDone,S_BLOODYSKULLX1,0,0}, // S_BLOODYSKULLX1 +{SPR_BSKL,8,1050,NULL,S_NULL,0,0}, // S_BLOODYSKULLX2 +{SPR_PLAY,0,5,NULL,S_PLAYER_SPEED2,0,0}, // S_PLAYER_SPEED1 +{SPR_PLAY,0,3,A_SpeedFade,S_NULL,0,0}, // S_PLAYER_SPEED2 +{SPR_ICEC,0,10,NULL,S_ICECHUNK2,0,0}, // S_ICECHUNK1 +{SPR_ICEC,1,10,A_IceSetTics,S_ICECHUNK3,0,0}, // S_ICECHUNK2 +{SPR_ICEC,2,10,A_IceSetTics,S_ICECHUNK4,0,0}, // S_ICECHUNK3 +{SPR_ICEC,3,10,A_IceSetTics,S_NULL,0,0}, // S_ICECHUNK4 +{SPR_ICEC,0,10,A_IceCheckHeadDone,S_ICECHUNK_HEAD,0,0}, // S_ICECHUNK_HEAD +{SPR_ICEC,0,1050,NULL,S_NULL,0,0}, // S_ICECHUNK_HEAD2 +{SPR_CLER,0,-1,NULL,S_NULL,0,0}, // S_CPLAY +{SPR_CLER,0,4,NULL,S_CPLAY_RUN2,0,0}, // S_CPLAY_RUN1 +{SPR_CLER,1,4,NULL,S_CPLAY_RUN3,0,0}, // S_CPLAY_RUN2 +{SPR_CLER,2,4,NULL,S_CPLAY_RUN4,0,0}, // S_CPLAY_RUN3 +{SPR_CLER,3,4,NULL,S_CPLAY_RUN1,0,0}, // S_CPLAY_RUN4 +{SPR_CLER,4,6,NULL,S_CPLAY_ATK2,0,0}, // S_CPLAY_ATK1 +{SPR_CLER,5,6,NULL,S_CPLAY_ATK3,0,0}, // S_CPLAY_ATK2 +{SPR_CLER,6,6,NULL,S_CPLAY,0,0}, // S_CPLAY_ATK3 +{SPR_CLER,7,4,NULL,S_CPLAY_PAIN2,0,0}, // S_CPLAY_PAIN +{SPR_CLER,7,4,A_Pain,S_CPLAY,0,0}, // S_CPLAY_PAIN2 +{SPR_CLER,8,6,NULL,S_CPLAY_DIE2,0,0}, // S_CPLAY_DIE1 +{SPR_CLER,10,6,A_Scream,S_CPLAY_DIE3,0,0}, // S_CPLAY_DIE2 +{SPR_CLER,11,6,NULL,S_CPLAY_DIE4,0,0}, // S_CPLAY_DIE3 +{SPR_CLER,11,6,NULL,S_CPLAY_DIE5,0,0}, // S_CPLAY_DIE4 +{SPR_CLER,12,6,A_NoBlocking,S_CPLAY_DIE6,0,0}, // S_CPLAY_DIE5 +{SPR_CLER,13,6,NULL,S_CPLAY_DIE7,0,0}, // S_CPLAY_DIE6 +{SPR_CLER,14,6,NULL,S_CPLAY_DIE8,0,0}, // S_CPLAY_DIE7 +{SPR_CLER,15,6,NULL,S_CPLAY_DIE9,0,0}, // S_CPLAY_DIE8 +{SPR_CLER,16,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_CPLAY_DIE9 +{SPR_CLER,17,5,A_Scream,S_CPLAY_XDIE2,0,0}, // S_CPLAY_XDIE1 +{SPR_CLER,18,5,NULL,S_CPLAY_XDIE3,0,0}, // S_CPLAY_XDIE2 +{SPR_CLER,19,5,A_NoBlocking,S_CPLAY_XDIE4,0,0}, // S_CPLAY_XDIE3 +{SPR_CLER,20,5,NULL,S_CPLAY_XDIE5,0,0}, // S_CPLAY_XDIE4 +{SPR_CLER,21,5,NULL,S_CPLAY_XDIE6,0,0}, // S_CPLAY_XDIE5 +{SPR_CLER,22,5,NULL,S_CPLAY_XDIE7,0,0}, // S_CPLAY_XDIE6 +{SPR_CLER,23,5,NULL,S_CPLAY_XDIE8,0,0}, // S_CPLAY_XDIE7 +{SPR_CLER,24,5,NULL,S_CPLAY_XDIE9,0,0}, // S_CPLAY_XDIE8 +{SPR_CLER,25,5,NULL,S_CPLAY_XDIE10,0,0}, // S_CPLAY_XDIE9 +{SPR_CLER,26,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_CPLAY_XDIE10 +{SPR_CLER,27,5,A_FreezeDeath,S_CPLAY_ICE2,0,0}, // S_CPLAY_ICE +{SPR_CLER,27,1,A_FreezeDeathChunks,S_CPLAY_ICE2,0,0}, // S_CPLAY_ICE2 +{SPR_MAGE,0,-1,NULL,S_NULL,0,0}, // S_MPLAY +{SPR_MAGE,0,4,NULL,S_MPLAY_RUN2,0,0}, // S_MPLAY_RUN1 +{SPR_MAGE,1,4,NULL,S_MPLAY_RUN3,0,0}, // S_MPLAY_RUN2 +{SPR_MAGE,2,4,NULL,S_MPLAY_RUN4,0,0}, // S_MPLAY_RUN3 +{SPR_MAGE,3,4,NULL,S_MPLAY_RUN1,0,0}, // S_MPLAY_RUN4 +{SPR_MAGE,4,8,NULL,S_MPLAY_ATK2,0,0}, // S_MPLAY_ATK1 +{SPR_MAGE,32773,8,NULL,S_MPLAY,0,0}, // S_MPLAY_ATK2 +{SPR_MAGE,6,4,NULL,S_MPLAY_PAIN2,0,0}, // S_MPLAY_PAIN +{SPR_MAGE,6,4,A_Pain,S_MPLAY,0,0}, // S_MPLAY_PAIN2 +{SPR_MAGE,7,6,NULL,S_MPLAY_DIE2,0,0}, // S_MPLAY_DIE1 +{SPR_MAGE,8,6,A_Scream,S_MPLAY_DIE3,0,0}, // S_MPLAY_DIE2 +{SPR_MAGE,9,6,NULL,S_MPLAY_DIE4,0,0}, // S_MPLAY_DIE3 +{SPR_MAGE,10,6,NULL,S_MPLAY_DIE5,0,0}, // S_MPLAY_DIE4 +{SPR_MAGE,11,6,A_NoBlocking,S_MPLAY_DIE6,0,0}, // S_MPLAY_DIE5 +{SPR_MAGE,12,6,NULL,S_MPLAY_DIE7,0,0}, // S_MPLAY_DIE6 +{SPR_MAGE,13,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_MPLAY_DIE7 +{SPR_MAGE,14,5,A_Scream,S_MPLAY_XDIE2,0,0}, // S_MPLAY_XDIE1 +{SPR_MAGE,15,5,NULL,S_MPLAY_XDIE3,0,0}, // S_MPLAY_XDIE2 +{SPR_MAGE,17,5,A_NoBlocking,S_MPLAY_XDIE4,0,0}, // S_MPLAY_XDIE3 +{SPR_MAGE,18,5,NULL,S_MPLAY_XDIE5,0,0}, // S_MPLAY_XDIE4 +{SPR_MAGE,19,5,NULL,S_MPLAY_XDIE6,0,0}, // S_MPLAY_XDIE5 +{SPR_MAGE,20,5,NULL,S_MPLAY_XDIE7,0,0}, // S_MPLAY_XDIE6 +{SPR_MAGE,21,5,NULL,S_MPLAY_XDIE8,0,0}, // S_MPLAY_XDIE7 +{SPR_MAGE,22,5,NULL,S_MPLAY_XDIE9,0,0}, // S_MPLAY_XDIE8 +{SPR_MAGE,23,-1,A_AddPlayerCorpse,S_NULL,0,0}, // S_MPLAY_XDIE9 +{SPR_MAGE,24,5,A_FreezeDeath,S_MPLAY_ICE2,0,0}, // S_MPLAY_ICE +{SPR_MAGE,24,1,A_FreezeDeathChunks,S_MPLAY_ICE2,0,0}, // S_MPLAY_ICE2 +{SPR_PIGY,0,-1,NULL,S_NULL,0,0}, // S_PIGPLAY +{SPR_PIGY,0,3,NULL,S_PIGPLAY_RUN2,0,0}, // S_PIGPLAY_RUN1 +{SPR_PIGY,1,3,NULL,S_PIGPLAY_RUN3,0,0}, // S_PIGPLAY_RUN2 +{SPR_PIGY,2,3,NULL,S_PIGPLAY_RUN4,0,0}, // S_PIGPLAY_RUN3 +{SPR_PIGY,3,3,NULL,S_PIGPLAY_RUN1,0,0}, // S_PIGPLAY_RUN4 +{SPR_PIGY,0,12,NULL,S_PIGPLAY,0,0}, // S_PIGPLAY_ATK1 +{SPR_PIGY,3,4,A_PigPain,S_PIGPLAY,0,0}, // S_PIGPLAY_PAIN +{SPR_PIGY,1,10,A_PigLook,S_PIG_LOOK1,0,0}, // S_PIG_LOOK1 +{SPR_PIGY,0,3,A_PigChase,S_PIG_WALK2,0,0}, // S_PIG_WALK1 +{SPR_PIGY,1,3,A_PigChase,S_PIG_WALK3,0,0}, // S_PIG_WALK2 +{SPR_PIGY,2,3,A_PigChase,S_PIG_WALK4,0,0}, // S_PIG_WALK3 +{SPR_PIGY,3,3,A_PigChase,S_PIG_WALK1,0,0}, // S_PIG_WALK4 +{SPR_PIGY,3,4,A_PigPain,S_PIG_WALK1,0,0}, // S_PIG_PAIN +{SPR_PIGY,0,5,A_FaceTarget,S_PIG_ATK2,0,0}, // S_PIG_ATK1 +{SPR_PIGY,0,10,A_PigAttack,S_PIG_WALK1,0,0}, // S_PIG_ATK2 +{SPR_PIGY,4,4,A_Scream,S_PIG_DIE2,0,0}, // S_PIG_DIE1 +{SPR_PIGY,5,3,A_NoBlocking,S_PIG_DIE3,0,0}, // S_PIG_DIE2 +{SPR_PIGY,6,4,A_QueueCorpse,S_PIG_DIE4,0,0}, // S_PIG_DIE3 +{SPR_PIGY,7,3,NULL,S_PIG_DIE5,0,0}, // S_PIG_DIE4 +{SPR_PIGY,8,4,NULL,S_PIG_DIE6,0,0}, // S_PIG_DIE5 +{SPR_PIGY,9,4,NULL,S_PIG_DIE7,0,0}, // S_PIG_DIE6 +{SPR_PIGY,10,4,NULL,S_PIG_DIE8,0,0}, // S_PIG_DIE7 +{SPR_PIGY,11,-1,NULL,S_NULL,0,0}, // S_PIG_DIE8 +{SPR_PIGY,12,5,A_FreezeDeath,S_PIG_ICE2,0,0}, // S_PIG_ICE +{SPR_PIGY,12,1,A_FreezeDeathChunks,S_PIG_ICE2,0,0}, // S_PIG_ICE2 +{SPR_CENT,0,10,A_Look,S_CENTAUR_LOOK2,0,0}, // S_CENTAUR_LOOK1 +{SPR_CENT,1,10,A_Look,S_CENTAUR_LOOK1,0,0}, // S_CENTAUR_LOOK2 +{SPR_CENT,0,4,A_Chase,S_CENTAUR_WALK2,0,0}, // S_CENTAUR_WALK1 +{SPR_CENT,1,4,A_Chase,S_CENTAUR_WALK3,0,0}, // S_CENTAUR_WALK2 +{SPR_CENT,2,4,A_Chase,S_CENTAUR_WALK4,0,0}, // S_CENTAUR_WALK3 +{SPR_CENT,3,4,A_Chase,S_CENTAUR_WALK1,0,0}, // S_CENTAUR_WALK4 +{SPR_CENT,7,5,A_FaceTarget,S_CENTAUR_ATK2,0,0}, // S_CENTAUR_ATK1 +{SPR_CENT,8,4,A_FaceTarget,S_CENTAUR_ATK3,0,0}, // S_CENTAUR_ATK2 +{SPR_CENT,9,7,A_CentaurAttack,S_CENTAUR_WALK1,0,0}, // S_CENTAUR_ATK3 +{SPR_CENT,4,10,A_FaceTarget,S_CENTAUR_MISSILE2,0,0}, // S_CENTAUR_MISSILE1 +{SPR_CENT,32773,8,A_CentaurAttack2,S_CENTAUR_MISSILE3,0,0}, // S_CENTAUR_MISSILE2 +{SPR_CENT,4,10,A_FaceTarget,S_CENTAUR_MISSILE4,0,0}, // S_CENTAUR_MISSILE3 +{SPR_CENT,32773,8,A_CentaurAttack2,S_CENTAUR_WALK1,0,0}, // S_CENTAUR_MISSILE4 +{SPR_CENT,6,6,A_Pain,S_CENTAUR_PAIN2,0,0}, // S_CENTAUR_PAIN1 +{SPR_CENT,6,6,A_SetReflective,S_CENTAUR_PAIN3,0,0}, // S_CENTAUR_PAIN2 +{SPR_CENT,4,15,A_CentaurDefend,S_CENTAUR_PAIN4,0,0}, // S_CENTAUR_PAIN3 +{SPR_CENT,4,15,A_CentaurDefend,S_CENTAUR_PAIN5,0,0}, // S_CENTAUR_PAIN4 +{SPR_CENT,4,15,A_CentaurDefend,S_CENTAUR_PAIN6,0,0}, // S_CENTAUR_PAIN5 +{SPR_CENT,4,1,A_UnSetReflective,S_CENTAUR_WALK1,0,0}, // S_CENTAUR_PAIN6 +{SPR_CENT,10,4,NULL,S_CENTAUR_DEATH2,0,0}, // S_CENTAUR_DEATH1 +{SPR_CENT,11,4,A_Scream,S_CENTAUR_DEATH3,0,0}, // S_CENTAUR_DEATH2 +{SPR_CENT,12,4,NULL,S_CENTAUR_DEATH4,0,0}, // S_CENTAUR_DEATH3 +{SPR_CENT,13,4,NULL,S_CENTAUR_DEATH5,0,0}, // S_CENTAUR_DEATH4 +{SPR_CENT,14,4,A_NoBlocking,S_CENTAUR_DEATH6,0,0}, // S_CENTAUR_DEATH5 +{SPR_CENT,15,4,NULL,S_CENTAUR_DEATH7,0,0}, // S_CENTAUR_DEATH6 +{SPR_CENT,16,4,NULL,S_CENTAUR_DEATH8,0,0}, // S_CENTAUR_DEATH7 +{SPR_CENT,17,4,A_QueueCorpse,S_CENTAUR_DEATH9,0,0}, // S_CENTAUR_DEATH8 +{SPR_CENT,18,4,NULL,S_CENTAUR_DEATH0,0,0}, // S_CENTAUR_DEATH9 +{SPR_CENT,19,-1,NULL,S_NULL,0,0}, // S_CENTAUR_DEATH0 +{SPR_CTXD,0,4,NULL,S_CENTAUR_DEATH_X2,0,0}, // S_CENTAUR_DEATH_X1 +{SPR_CTXD,1,4,A_NoBlocking,S_CENTAUR_DEATH_X3,0,0}, // S_CENTAUR_DEATH_X2 +{SPR_CTXD,2,4,A_CentaurDropStuff,S_CENTAUR_DEATH_X4,0,0}, // S_CENTAUR_DEATH_X3 +{SPR_CTXD,3,3,A_Scream,S_CENTAUR_DEATH_X5,0,0}, // S_CENTAUR_DEATH_X4 +{SPR_CTXD,4,4,A_QueueCorpse,S_CENTAUR_DEATH_X6,0,0}, // S_CENTAUR_DEATH_X5 +{SPR_CTXD,5,3,NULL,S_CENTAUR_DEATH_X7,0,0}, // S_CENTAUR_DEATH_X6 +{SPR_CTXD,6,4,NULL,S_CENTAUR_DEATH_X8,0,0}, // S_CENTAUR_DEATH_X7 +{SPR_CTXD,7,3,NULL,S_CENTAUR_DEATH_X9,0,0}, // S_CENTAUR_DEATH_X8 +{SPR_CTXD,8,4,NULL,S_CENTAUR_DEATH_X10,0,0}, // S_CENTAUR_DEATH_X9 +{SPR_CTXD,9,3,NULL,S_CENTAUR_DEATH_X11,0,0}, // S_CENTAUR_DEATH_X10 +{SPR_CTXD,10,-1,NULL,S_NULL,0,0}, // S_CENTAUR_DEATH_X11 +{SPR_CENT,20,5,A_FreezeDeath,S_CENTAUR_ICE2,0,0}, // S_CENTAUR_ICE +{SPR_CENT,20,1,A_FreezeDeathChunks,S_CENTAUR_ICE2,0,0}, // S_CENTAUR_ICE2 +{SPR_CTFX,32768,-1,NULL,S_NULL,0,0}, // S_CENTAUR_FX1 +{SPR_CTFX,32769,4,NULL,S_CENTAUR_FX_X2,0,0}, // S_CENTAUR_FX_X1 +{SPR_CTFX,32770,3,NULL,S_CENTAUR_FX_X3,0,0}, // S_CENTAUR_FX_X2 +{SPR_CTFX,32771,4,NULL,S_CENTAUR_FX_X4,0,0}, // S_CENTAUR_FX_X3 +{SPR_CTFX,32772,3,NULL,S_CENTAUR_FX_X5,0,0}, // S_CENTAUR_FX_X4 +{SPR_CTFX,32773,2,NULL,S_NULL,0,0}, // S_CENTAUR_FX_X5 +{SPR_CTDP,0,3,A_CheckFloor,S_CENTAUR_SHIELD2,0,0}, // S_CENTAUR_SHIELD1 +{SPR_CTDP,1,3,A_CheckFloor,S_CENTAUR_SHIELD3,0,0}, // S_CENTAUR_SHIELD2 +{SPR_CTDP,2,3,A_CheckFloor,S_CENTAUR_SHIELD4,0,0}, // S_CENTAUR_SHIELD3 +{SPR_CTDP,3,3,A_CheckFloor,S_CENTAUR_SHIELD5,0,0}, // S_CENTAUR_SHIELD4 +{SPR_CTDP,4,3,A_CheckFloor,S_CENTAUR_SHIELD6,0,0}, // S_CENTAUR_SHIELD5 +{SPR_CTDP,5,3,A_CheckFloor,S_CENTAUR_SHIELD3,0,0}, // S_CENTAUR_SHIELD6 +{SPR_CTDP,6,4,NULL,S_CENTAUR_SHIELD_X2,0,0}, // S_CENTAUR_SHIELD_X1 +{SPR_CTDP,7,4,A_QueueCorpse,S_CENTAUR_SHIELD_X3,0,0}, // S_CENTAUR_SHIELD_X2 +{SPR_CTDP,8,4,NULL,S_CENTAUR_SHIELD_X4,0,0}, // S_CENTAUR_SHIELD_X3 +{SPR_CTDP,9,-1,NULL,S_NULL,0,0}, // S_CENTAUR_SHIELD_X4 +{SPR_CTDP,10,3,A_CheckFloor,S_CENTAUR_SWORD2,0,0}, // S_CENTAUR_SWORD1 +{SPR_CTDP,11,3,A_CheckFloor,S_CENTAUR_SWORD3,0,0}, // S_CENTAUR_SWORD2 +{SPR_CTDP,12,3,A_CheckFloor,S_CENTAUR_SWORD4,0,0}, // S_CENTAUR_SWORD3 +{SPR_CTDP,13,3,A_CheckFloor,S_CENTAUR_SWORD5,0,0}, // S_CENTAUR_SWORD4 +{SPR_CTDP,14,3,A_CheckFloor,S_CENTAUR_SWORD6,0,0}, // S_CENTAUR_SWORD5 +{SPR_CTDP,15,3,A_CheckFloor,S_CENTAUR_SWORD7,0,0}, // S_CENTAUR_SWORD6 +{SPR_CTDP,16,3,A_CheckFloor,S_CENTAUR_SWORD3,0,0}, // S_CENTAUR_SWORD7 +{SPR_CTDP,17,4,NULL,S_CENTAUR_SWORD_X2,0,0}, // S_CENTAUR_SWORD_X1 +{SPR_CTDP,18,4,A_QueueCorpse,S_CENTAUR_SWORD_X3,0,0}, // S_CENTAUR_SWORD_X2 +{SPR_CTDP,19,-1,NULL,S_NULL,0,0}, // S_CENTAUR_SWORD_X3 +{SPR_DEMN,0,10,A_Look,S_DEMN_LOOK2,0,0}, // S_DEMN_LOOK1 +{SPR_DEMN,0,10,A_Look,S_DEMN_LOOK1,0,0}, // S_DEMN_LOOK2 +{SPR_DEMN,0,4,A_Chase,S_DEMN_CHASE2,0,0}, // S_DEMN_CHASE1 +{SPR_DEMN,1,4,A_Chase,S_DEMN_CHASE3,0,0}, // S_DEMN_CHASE2 +{SPR_DEMN,2,4,A_Chase,S_DEMN_CHASE4,0,0}, // S_DEMN_CHASE3 +{SPR_DEMN,3,4,A_Chase,S_DEMN_CHASE1,0,0}, // S_DEMN_CHASE4 +{SPR_DEMN,4,6,A_FaceTarget,S_DEMN_ATK1_2,0,0}, // S_DEMN_ATK1_1 +{SPR_DEMN,5,8,A_FaceTarget,S_DEMN_ATK1_3,0,0}, // S_DEMN_ATK1_2 +{SPR_DEMN,6,6,A_DemonAttack1,S_DEMN_CHASE1,0,0}, // S_DEMN_ATK1_3 +{SPR_DEMN,4,5,A_FaceTarget,S_DEMN_ATK2_2,0,0}, // S_DEMN_ATK2_1 +{SPR_DEMN,5,6,A_FaceTarget,S_DEMN_ATK2_3,0,0}, // S_DEMN_ATK2_2 +{SPR_DEMN,6,5,A_DemonAttack2,S_DEMN_CHASE1,0,0}, // S_DEMN_ATK2_3 +{SPR_DEMN,4,4,NULL,S_DEMN_PAIN2,0,0}, // S_DEMN_PAIN1 +{SPR_DEMN,4,4,A_Pain,S_DEMN_CHASE1,0,0}, // S_DEMN_PAIN2 +{SPR_DEMN,7,6,NULL,S_DEMN_DEATH2,0,0}, // S_DEMN_DEATH1 +{SPR_DEMN,8,6,NULL,S_DEMN_DEATH3,0,0}, // S_DEMN_DEATH2 +{SPR_DEMN,9,6,A_Scream,S_DEMN_DEATH4,0,0}, // S_DEMN_DEATH3 +{SPR_DEMN,10,6,A_NoBlocking,S_DEMN_DEATH5,0,0}, // S_DEMN_DEATH4 +{SPR_DEMN,11,6,A_QueueCorpse,S_DEMN_DEATH6,0,0}, // S_DEMN_DEATH5 +{SPR_DEMN,12,6,NULL,S_DEMN_DEATH7,0,0}, // S_DEMN_DEATH6 +{SPR_DEMN,13,6,NULL,S_DEMN_DEATH8,0,0}, // S_DEMN_DEATH7 +{SPR_DEMN,14,6,NULL,S_DEMN_DEATH9,0,0}, // S_DEMN_DEATH8 +{SPR_DEMN,15,-1,NULL,S_NULL,0,0}, // S_DEMN_DEATH9 +{SPR_DEMN,7,6,NULL,S_DEMN_XDEATH2,0,0}, // S_DEMN_XDEATH1 +{SPR_DEMN,8,6,A_DemonDeath,S_DEMN_XDEATH3,0,0}, // S_DEMN_XDEATH2 +{SPR_DEMN,9,6,A_Scream,S_DEMN_XDEATH4,0,0}, // S_DEMN_XDEATH3 +{SPR_DEMN,10,6,A_NoBlocking,S_DEMN_XDEATH5,0,0}, // S_DEMN_XDEATH4 +{SPR_DEMN,11,6,A_QueueCorpse,S_DEMN_XDEATH6,0,0}, // S_DEMN_XDEATH5 +{SPR_DEMN,12,6,NULL,S_DEMN_XDEATH7,0,0}, // S_DEMN_XDEATH6 +{SPR_DEMN,13,6,NULL,S_DEMN_XDEATH8,0,0}, // S_DEMN_XDEATH7 +{SPR_DEMN,14,6,NULL,S_DEMN_XDEATH9,0,0}, // S_DEMN_XDEATH8 +{SPR_DEMN,15,-1,NULL,S_NULL,0,0}, // S_DEMN_XDEATH9 +{SPR_DEMN,16,5,A_FreezeDeath,S_DEMON_ICE2,0,0}, // S_DEMON_ICE +{SPR_DEMN,16,1,A_FreezeDeathChunks,S_DEMON_ICE2,0,0}, // S_DEMON_ICE2 +{SPR_DEMA,0,4,NULL,S_DEMONCHUNK1_2,0,0}, // S_DEMONCHUNK1_1 +{SPR_DEMA,0,10,A_QueueCorpse,S_DEMONCHUNK1_3,0,0}, // S_DEMONCHUNK1_2 +{SPR_DEMA,0,20,NULL,S_DEMONCHUNK1_3,0,0}, // S_DEMONCHUNK1_3 +{SPR_DEMA,0,-1,NULL,S_NULL,0,0}, // S_DEMONCHUNK1_4 +{SPR_DEMB,0,4,NULL,S_DEMONCHUNK2_2,0,0}, // S_DEMONCHUNK2_1 +{SPR_DEMB,0,10,A_QueueCorpse,S_DEMONCHUNK2_3,0,0}, // S_DEMONCHUNK2_2 +{SPR_DEMB,0,20,NULL,S_DEMONCHUNK2_3,0,0}, // S_DEMONCHUNK2_3 +{SPR_DEMB,0,-1,NULL,S_NULL,0,0}, // S_DEMONCHUNK2_4 +{SPR_DEMC,0,4,NULL,S_DEMONCHUNK3_2,0,0}, // S_DEMONCHUNK3_1 +{SPR_DEMC,0,10,A_QueueCorpse,S_DEMONCHUNK3_3,0,0}, // S_DEMONCHUNK3_2 +{SPR_DEMC,0,20,NULL,S_DEMONCHUNK3_3,0,0}, // S_DEMONCHUNK3_3 +{SPR_DEMC,0,-1,NULL,S_NULL,0,0}, // S_DEMONCHUNK3_4 +{SPR_DEMD,0,4,NULL,S_DEMONCHUNK4_2,0,0}, // S_DEMONCHUNK4_1 +{SPR_DEMD,0,10,A_QueueCorpse,S_DEMONCHUNK4_3,0,0}, // S_DEMONCHUNK4_2 +{SPR_DEMD,0,20,NULL,S_DEMONCHUNK4_3,0,0}, // S_DEMONCHUNK4_3 +{SPR_DEMD,0,-1,NULL,S_NULL,0,0}, // S_DEMONCHUNK4_4 +{SPR_DEME,0,4,NULL,S_DEMONCHUNK5_2,0,0}, // S_DEMONCHUNK5_1 +{SPR_DEME,0,10,A_QueueCorpse,S_DEMONCHUNK5_3,0,0}, // S_DEMONCHUNK5_2 +{SPR_DEME,0,20,NULL,S_DEMONCHUNK5_3,0,0}, // S_DEMONCHUNK5_3 +{SPR_DEME,0,-1,NULL,S_NULL,0,0}, // S_DEMONCHUNK5_4 +{SPR_DMFX,32768,4,NULL,S_DEMONFX_MOVE2,0,0}, // S_DEMONFX_MOVE1 +{SPR_DMFX,32769,4,NULL,S_DEMONFX_MOVE3,0,0}, // S_DEMONFX_MOVE2 +{SPR_DMFX,32770,4,NULL,S_DEMONFX_MOVE1,0,0}, // S_DEMONFX_MOVE3 +{SPR_DMFX,32771,4,NULL,S_DEMONFX_BOOM2,0,0}, // S_DEMONFX_BOOM1 +{SPR_DMFX,32772,4,NULL,S_DEMONFX_BOOM3,0,0}, // S_DEMONFX_BOOM2 +{SPR_DMFX,32773,3,NULL,S_DEMONFX_BOOM4,0,0}, // S_DEMONFX_BOOM3 +{SPR_DMFX,32774,3,NULL,S_DEMONFX_BOOM5,0,0}, // S_DEMONFX_BOOM4 +{SPR_DMFX,32775,3,NULL,S_NULL,0,0}, // S_DEMONFX_BOOM5 +{SPR_DEM2,0,10,A_Look,S_DEMN2_LOOK2,0,0}, // S_DEMN2_LOOK1 +{SPR_DEM2,0,10,A_Look,S_DEMN2_LOOK1,0,0}, // S_DEMN2_LOOK2 +{SPR_DEM2,0,4,A_Chase,S_DEMN2_CHASE2,0,0}, // S_DEMN2_CHASE1 +{SPR_DEM2,1,4,A_Chase,S_DEMN2_CHASE3,0,0}, // S_DEMN2_CHASE2 +{SPR_DEM2,2,4,A_Chase,S_DEMN2_CHASE4,0,0}, // S_DEMN2_CHASE3 +{SPR_DEM2,3,4,A_Chase,S_DEMN2_CHASE1,0,0}, // S_DEMN2_CHASE4 +{SPR_DEM2,4,6,A_FaceTarget,S_DEMN2_ATK1_2,0,0}, // S_DEMN2_ATK1_1 +{SPR_DEM2,5,8,A_FaceTarget,S_DEMN2_ATK1_3,0,0}, // S_DEMN2_ATK1_2 +{SPR_DEM2,6,6,A_DemonAttack1,S_DEMN2_CHASE1,0,0}, // S_DEMN2_ATK1_3 +{SPR_DEM2,4,5,A_FaceTarget,S_DEMN2_ATK2_2,0,0}, // S_DEMN2_ATK2_1 +{SPR_DEM2,5,6,A_FaceTarget,S_DEMN2_ATK2_3,0,0}, // S_DEMN2_ATK2_2 +{SPR_DEM2,6,5,A_DemonAttack2,S_DEMN2_CHASE1,0,0}, // S_DEMN2_ATK2_3 +{SPR_DEM2,4,4,NULL,S_DEMN2_PAIN2,0,0}, // S_DEMN2_PAIN1 +{SPR_DEM2,4,4,A_Pain,S_DEMN2_CHASE1,0,0}, // S_DEMN2_PAIN2 +{SPR_DEM2,7,6,NULL,S_DEMN2_DEATH2,0,0}, // S_DEMN2_DEATH1 +{SPR_DEM2,8,6,NULL,S_DEMN2_DEATH3,0,0}, // S_DEMN2_DEATH2 +{SPR_DEM2,9,6,A_Scream,S_DEMN2_DEATH4,0,0}, // S_DEMN2_DEATH3 +{SPR_DEM2,10,6,A_NoBlocking,S_DEMN2_DEATH5,0,0}, // S_DEMN2_DEATH4 +{SPR_DEM2,11,6,A_QueueCorpse,S_DEMN2_DEATH6,0,0}, // S_DEMN2_DEATH5 +{SPR_DEM2,12,6,NULL,S_DEMN2_DEATH7,0,0}, // S_DEMN2_DEATH6 +{SPR_DEM2,13,6,NULL,S_DEMN2_DEATH8,0,0}, // S_DEMN2_DEATH7 +{SPR_DEM2,14,6,NULL,S_DEMN2_DEATH9,0,0}, // S_DEMN2_DEATH8 +{SPR_DEM2,15,-1,NULL,S_NULL,0,0}, // S_DEMN2_DEATH9 +{SPR_DEM2,7,6,NULL,S_DEMN2_XDEATH2,0,0}, // S_DEMN2_XDEATH1 +{SPR_DEM2,8,6,A_Demon2Death,S_DEMN2_XDEATH3,0,0}, // S_DEMN2_XDEATH2 +{SPR_DEM2,9,6,A_Scream,S_DEMN2_XDEATH4,0,0}, // S_DEMN2_XDEATH3 +{SPR_DEM2,10,6,A_NoBlocking,S_DEMN2_XDEATH5,0,0}, // S_DEMN2_XDEATH4 +{SPR_DEM2,11,6,A_QueueCorpse,S_DEMN2_XDEATH6,0,0}, // S_DEMN2_XDEATH5 +{SPR_DEM2,12,6,NULL,S_DEMN2_XDEATH7,0,0}, // S_DEMN2_XDEATH6 +{SPR_DEM2,13,6,NULL,S_DEMN2_XDEATH8,0,0}, // S_DEMN2_XDEATH7 +{SPR_DEM2,14,6,NULL,S_DEMN2_XDEATH9,0,0}, // S_DEMN2_XDEATH8 +{SPR_DEM2,15,-1,NULL,S_NULL,0,0}, // S_DEMN2_XDEATH9 +{SPR_DMBA,0,4,NULL,S_DEMON2CHUNK1_2,0,0}, // S_DEMON2CHUNK1_1 +{SPR_DMBA,0,10,A_QueueCorpse,S_DEMON2CHUNK1_3,0,0}, // S_DEMON2CHUNK1_2 +{SPR_DMBA,0,20,NULL,S_DEMON2CHUNK1_3,0,0}, // S_DEMON2CHUNK1_3 +{SPR_DMBA,0,-1,NULL,S_NULL,0,0}, // S_DEMON2CHUNK1_4 +{SPR_DMBB,0,4,NULL,S_DEMON2CHUNK2_2,0,0}, // S_DEMON2CHUNK2_1 +{SPR_DMBB,0,10,A_QueueCorpse,S_DEMON2CHUNK2_3,0,0}, // S_DEMON2CHUNK2_2 +{SPR_DMBB,0,20,NULL,S_DEMON2CHUNK2_3,0,0}, // S_DEMON2CHUNK2_3 +{SPR_DMBB,0,-1,NULL,S_NULL,0,0}, // S_DEMON2CHUNK2_4 +{SPR_DMBC,0,4,NULL,S_DEMON2CHUNK3_2,0,0}, // S_DEMON2CHUNK3_1 +{SPR_DMBC,0,10,A_QueueCorpse,S_DEMON2CHUNK3_3,0,0}, // S_DEMON2CHUNK3_2 +{SPR_DMBC,0,20,NULL,S_DEMON2CHUNK3_3,0,0}, // S_DEMON2CHUNK3_3 +{SPR_DMBC,0,-1,NULL,S_NULL,0,0}, // S_DEMON2CHUNK3_4 +{SPR_DMBD,0,4,NULL,S_DEMON2CHUNK4_2,0,0}, // S_DEMON2CHUNK4_1 +{SPR_DMBD,0,10,A_QueueCorpse,S_DEMON2CHUNK4_3,0,0}, // S_DEMON2CHUNK4_2 +{SPR_DMBD,0,20,NULL,S_DEMON2CHUNK4_3,0,0}, // S_DEMON2CHUNK4_3 +{SPR_DMBD,0,-1,NULL,S_NULL,0,0}, // S_DEMON2CHUNK4_4 +{SPR_DMBE,0,4,NULL,S_DEMON2CHUNK5_2,0,0}, // S_DEMON2CHUNK5_1 +{SPR_DMBE,0,10,NULL,S_DEMON2CHUNK5_3,0,0}, // S_DEMON2CHUNK5_2 +{SPR_DMBE,0,20,NULL,S_DEMON2CHUNK5_3,0,0}, // S_DEMON2CHUNK5_3 +{SPR_DMBE,0,-1,NULL,S_NULL,0,0}, // S_DEMON2CHUNK5_4 +{SPR_D2FX,32768,4,NULL,S_DEMON2FX_MOVE2,0,0}, // S_DEMON2FX_MOVE1 +{SPR_D2FX,32769,4,NULL,S_DEMON2FX_MOVE3,0,0}, // S_DEMON2FX_MOVE2 +{SPR_D2FX,32770,4,NULL,S_DEMON2FX_MOVE4,0,0}, // S_DEMON2FX_MOVE3 +{SPR_D2FX,32771,4,NULL,S_DEMON2FX_MOVE5,0,0}, // S_DEMON2FX_MOVE4 +{SPR_D2FX,32772,4,NULL,S_DEMON2FX_MOVE6,0,0}, // S_DEMON2FX_MOVE5 +{SPR_D2FX,32773,4,NULL,S_DEMON2FX_MOVE1,0,0}, // S_DEMON2FX_MOVE6 +{SPR_D2FX,32774,4,NULL,S_DEMON2FX_BOOM2,0,0}, // S_DEMON2FX_BOOM1 +{SPR_D2FX,32775,4,NULL,S_DEMON2FX_BOOM3,0,0}, // S_DEMON2FX_BOOM2 +{SPR_D2FX,32776,4,NULL,S_DEMON2FX_BOOM4,0,0}, // S_DEMON2FX_BOOM3 +{SPR_D2FX,32777,4,NULL,S_DEMON2FX_BOOM5,0,0}, // S_DEMON2FX_BOOM4 +{SPR_D2FX,32778,3,NULL,S_DEMON2FX_BOOM6,0,0}, // S_DEMON2FX_BOOM5 +{SPR_D2FX,32779,3,NULL,S_NULL,0,0}, // S_DEMON2FX_BOOM6 +{SPR_WRTH,0,2,A_WraithRaiseInit,S_WRAITH_RAISE2,0,0}, // S_WRAITH_RAISE1 +{SPR_WRTH,0,2,A_WraithRaise,S_WRAITH_RAISE3,0,0}, // S_WRAITH_RAISE2 +{SPR_WRTH,0,2,A_FaceTarget,S_WRAITH_RAISE4,0,0}, // S_WRAITH_RAISE3 +{SPR_WRTH,1,2,A_WraithRaise,S_WRAITH_RAISE5,0,0}, // S_WRAITH_RAISE4 +{SPR_WRTH,1,2,A_WraithRaise,S_WRAITH_RAISE2,0,0}, // S_WRAITH_RAISE5 +{SPR_WRTH,0,10,NULL,S_WRAITH_INIT2,0,0}, // S_WRAITH_INIT1 +{SPR_WRTH,1,5,A_WraithInit,S_WRAITH_LOOK1,0,0}, // S_WRAITH_INIT2 +{SPR_WRTH,0,15,A_WraithLook,S_WRAITH_LOOK2,0,0}, // S_WRAITH_LOOK1 +{SPR_WRTH,1,15,A_WraithLook,S_WRAITH_LOOK1,0,0}, // S_WRAITH_LOOK2 +{SPR_WRTH,0,4,A_WraithChase,S_WRAITH_CHASE2,0,0}, // S_WRAITH_CHASE1 +{SPR_WRTH,1,4,A_WraithChase,S_WRAITH_CHASE3,0,0}, // S_WRAITH_CHASE2 +{SPR_WRTH,2,4,A_WraithChase,S_WRAITH_CHASE4,0,0}, // S_WRAITH_CHASE3 +{SPR_WRTH,3,4,A_WraithChase,S_WRAITH_CHASE1,0,0}, // S_WRAITH_CHASE4 +{SPR_WRTH,4,6,A_FaceTarget,S_WRAITH_ATK1_2,0,0}, // S_WRAITH_ATK1_1 +{SPR_WRTH,5,6,A_WraithFX3,S_WRAITH_ATK1_3,0,0}, // S_WRAITH_ATK1_2 +{SPR_WRTH,6,6,A_WraithMelee,S_WRAITH_CHASE1,0,0}, // S_WRAITH_ATK1_3 +{SPR_WRTH,4,6,A_FaceTarget,S_WRAITH_ATK2_2,0,0}, // S_WRAITH_ATK2_1 +{SPR_WRTH,5,6,NULL,S_WRAITH_ATK2_3,0,0}, // S_WRAITH_ATK2_2 +{SPR_WRTH,6,6,A_WraithMissile,S_WRAITH_CHASE1,0,0}, // S_WRAITH_ATK2_3 +{SPR_WRTH,0,2,NULL,S_WRAITH_PAIN2,0,0}, // S_WRAITH_PAIN1 +{SPR_WRTH,7,6,A_Pain,S_WRAITH_CHASE1,0,0}, // S_WRAITH_PAIN2 +{SPR_WRTH,8,4,NULL,S_WRAITH_DEATH1_2,0,0}, // S_WRAITH_DEATH1_1 +{SPR_WRTH,9,4,A_Scream,S_WRAITH_DEATH1_3,0,0}, // S_WRAITH_DEATH1_2 +{SPR_WRTH,10,4,NULL,S_WRAITH_DEATH1_4,0,0}, // S_WRAITH_DEATH1_3 +{SPR_WRTH,11,4,NULL,S_WRAITH_DEATH1_5,0,0}, // S_WRAITH_DEATH1_4 +{SPR_WRTH,12,4,A_NoBlocking,S_WRAITH_DEATH1_6,0,0}, // S_WRAITH_DEATH1_5 +{SPR_WRTH,13,4,A_QueueCorpse,S_WRAITH_DEATH1_7,0,0}, // S_WRAITH_DEATH1_6 +{SPR_WRTH,14,4,NULL,S_WRAITH_DEATH1_8,0,0}, // S_WRAITH_DEATH1_7 +{SPR_WRTH,15,5,NULL,S_WRAITH_DEATH1_9,0,0}, // S_WRAITH_DEATH1_8 +{SPR_WRTH,16,5,NULL,S_WRAITH_DEATH1_0,0,0}, // S_WRAITH_DEATH1_9 +{SPR_WRTH,17,-1,NULL,S_NULL,0,0}, // S_WRAITH_DEATH1_0 +{SPR_WRT2,0,5,NULL,S_WRAITH_DEATH2_2,0,0}, // S_WRAITH_DEATH2_1 +{SPR_WRT2,1,5,A_Scream,S_WRAITH_DEATH2_3,0,0}, // S_WRAITH_DEATH2_2 +{SPR_WRT2,2,5,NULL,S_WRAITH_DEATH2_4,0,0}, // S_WRAITH_DEATH2_3 +{SPR_WRT2,3,5,NULL,S_WRAITH_DEATH2_5,0,0}, // S_WRAITH_DEATH2_4 +{SPR_WRT2,4,5,A_NoBlocking,S_WRAITH_DEATH2_6,0,0}, // S_WRAITH_DEATH2_5 +{SPR_WRT2,5,5,A_QueueCorpse,S_WRAITH_DEATH2_7,0,0}, // S_WRAITH_DEATH2_6 +{SPR_WRT2,6,5,NULL,S_WRAITH_DEATH2_8,0,0}, // S_WRAITH_DEATH2_7 +{SPR_WRT2,7,-1,NULL,S_NULL,0,0}, // S_WRAITH_DEATH2_8 +{SPR_WRT2,8,5,A_FreezeDeath,S_WRAITH_ICE2,0,0}, // S_WRAITH_ICE +{SPR_WRT2,8,1,A_FreezeDeathChunks,S_WRAITH_ICE2,0,0}, // S_WRAITH_ICE2 +{SPR_WRBL,32768,3,NULL,S_WRTHFX_MOVE2,0,0}, // S_WRTHFX_MOVE1 +{SPR_WRBL,32769,3,A_WraithFX2,S_WRTHFX_MOVE3,0,0}, // S_WRTHFX_MOVE2 +{SPR_WRBL,32770,3,NULL,S_WRTHFX_MOVE1,0,0}, // S_WRTHFX_MOVE3 +{SPR_WRBL,32771,4,NULL,S_WRTHFX_BOOM2,0,0}, // S_WRTHFX_BOOM1 +{SPR_WRBL,32772,4,A_WraithFX2,S_WRTHFX_BOOM3,0,0}, // S_WRTHFX_BOOM2 +{SPR_WRBL,32773,4,NULL,S_WRTHFX_BOOM4,0,0}, // S_WRTHFX_BOOM3 +{SPR_WRBL,32774,3,A_WraithFX2,S_WRTHFX_BOOM5,0,0}, // S_WRTHFX_BOOM4 +{SPR_WRBL,32775,3,A_WraithFX2,S_WRTHFX_BOOM6,0,0}, // S_WRTHFX_BOOM5 +{SPR_WRBL,32776,3,NULL,S_NULL,0,0}, // S_WRTHFX_BOOM6 +{SPR_WRBL,32777,4,NULL,S_WRTHFX_SIZZLE2,0,0}, // S_WRTHFX_SIZZLE1 +{SPR_WRBL,32778,4,NULL,S_WRTHFX_SIZZLE3,0,0}, // S_WRTHFX_SIZZLE2 +{SPR_WRBL,32779,4,NULL,S_WRTHFX_SIZZLE4,0,0}, // S_WRTHFX_SIZZLE3 +{SPR_WRBL,32780,4,NULL,S_WRTHFX_SIZZLE5,0,0}, // S_WRTHFX_SIZZLE4 +{SPR_WRBL,32781,4,NULL,S_WRTHFX_SIZZLE6,0,0}, // S_WRTHFX_SIZZLE5 +{SPR_WRBL,32782,4,NULL,S_WRTHFX_SIZZLE7,0,0}, // S_WRTHFX_SIZZLE6 +{SPR_WRBL,32783,4,NULL,S_NULL,0,0}, // S_WRTHFX_SIZZLE7 +{SPR_WRBL,32784,4,NULL,S_WRTHFX_DROP2,0,0}, // S_WRTHFX_DROP1 +{SPR_WRBL,32785,4,NULL,S_WRTHFX_DROP3,0,0}, // S_WRTHFX_DROP2 +{SPR_WRBL,32786,4,NULL,S_WRTHFX_DROP1,0,0}, // S_WRTHFX_DROP3 +{SPR_WRBL,32786,4,NULL,S_NULL,0,0}, // S_WRTHFX_DEAD1 +{SPR_WRBL,19,4,NULL,S_WRTHFX_ADROP2,0,0}, // S_WRTHFX_ADROP1 +{SPR_WRBL,20,4,NULL,S_WRTHFX_ADROP3,0,0}, // S_WRTHFX_ADROP2 +{SPR_WRBL,21,4,NULL,S_WRTHFX_ADROP4,0,0}, // S_WRTHFX_ADROP3 +{SPR_WRBL,22,4,NULL,S_WRTHFX_ADROP1,0,0}, // S_WRTHFX_ADROP4 +{SPR_WRBL,22,10,NULL,S_NULL,0,0}, // S_WRTHFX_ADEAD1 +{SPR_WRBL,23,7,NULL,S_WRTHFX_BDROP2,0,0}, // S_WRTHFX_BDROP1 +{SPR_WRBL,24,7,NULL,S_WRTHFX_BDROP3,0,0}, // S_WRTHFX_BDROP2 +{SPR_WRBL,25,7,NULL,S_WRTHFX_BDROP1,0,0}, // S_WRTHFX_BDROP3 +{SPR_WRBL,25,35,NULL,S_NULL,0,0}, // S_WRTHFX_BDEAD1 +{SPR_MNTR,0,15,NULL,S_MNTR_SPAWN2,0,0}, // S_MNTR_SPAWN1 +{SPR_MNTR,0,15,A_MinotaurFade1,S_MNTR_SPAWN3,0,0}, // S_MNTR_SPAWN2 +{SPR_MNTR,0,3,A_MinotaurFade2,S_MNTR_LOOK1,0,0}, // S_MNTR_SPAWN3 +{SPR_MNTR,0,10,A_MinotaurLook,S_MNTR_LOOK2,0,0}, // S_MNTR_LOOK1 +{SPR_MNTR,1,10,A_MinotaurLook,S_MNTR_LOOK1,0,0}, // S_MNTR_LOOK2 +{SPR_MNTR,0,5,A_MinotaurChase,S_MNTR_WALK2,0,0}, // S_MNTR_WALK1 +{SPR_MNTR,1,5,A_MinotaurChase,S_MNTR_WALK3,0,0}, // S_MNTR_WALK2 +{SPR_MNTR,2,5,A_MinotaurChase,S_MNTR_WALK4,0,0}, // S_MNTR_WALK3 +{SPR_MNTR,3,5,A_MinotaurChase,S_MNTR_WALK1,0,0}, // S_MNTR_WALK4 +{SPR_MNTR,0,5,A_MinotaurRoam,S_MNTR_ROAM2,0,0}, // S_MNTR_ROAM1 +{SPR_MNTR,1,5,A_MinotaurRoam,S_MNTR_ROAM3,0,0}, // S_MNTR_ROAM2 +{SPR_MNTR,2,5,A_MinotaurRoam,S_MNTR_ROAM4,0,0}, // S_MNTR_ROAM3 +{SPR_MNTR,3,5,A_MinotaurRoam,S_MNTR_ROAM1,0,0}, // S_MNTR_ROAM4 +{SPR_MNTR,6,10,A_FaceTarget,S_MNTR_ATK1_2,0,0}, // S_MNTR_ATK1_1 +{SPR_MNTR,7,7,A_FaceTarget,S_MNTR_ATK1_3,0,0}, // S_MNTR_ATK1_2 +{SPR_MNTR,8,12,A_MinotaurAtk1,S_MNTR_WALK1,0,0}, // S_MNTR_ATK1_3 +{SPR_MNTR,6,10,A_MinotaurDecide,S_MNTR_ATK2_2,0,0}, // S_MNTR_ATK2_1 +{SPR_MNTR,9,4,A_FaceTarget,S_MNTR_ATK2_3,0,0}, // S_MNTR_ATK2_2 +{SPR_MNTR,10,9,A_MinotaurAtk2,S_MNTR_WALK1,0,0}, // S_MNTR_ATK2_3 +{SPR_MNTR,6,10,A_FaceTarget,S_MNTR_ATK3_2,0,0}, // S_MNTR_ATK3_1 +{SPR_MNTR,7,7,A_FaceTarget,S_MNTR_ATK3_3,0,0}, // S_MNTR_ATK3_2 +{SPR_MNTR,8,12,A_MinotaurAtk3,S_MNTR_WALK1,0,0}, // S_MNTR_ATK3_3 +{SPR_MNTR,8,12,NULL,S_MNTR_ATK3_1,0,0}, // S_MNTR_ATK3_4 +{SPR_MNTR,5,2,A_MinotaurCharge,S_MNTR_ATK4_1,0,0}, // S_MNTR_ATK4_1 +{SPR_MNTR,4,3,NULL,S_MNTR_PAIN2,0,0}, // S_MNTR_PAIN1 +{SPR_MNTR,4,6,A_Pain,S_MNTR_WALK1,0,0}, // S_MNTR_PAIN2 +{SPR_MNTR,4,6,NULL,S_MNTR_DIE2,0,0}, // S_MNTR_DIE1 +{SPR_MNTR,4,2,A_Scream,S_MNTR_DIE3,0,0}, // S_MNTR_DIE2 +{SPR_MNTR,4,5,A_SmokePuffExit,S_MNTR_DIE4,0,0}, // S_MNTR_DIE3 +{SPR_MNTR,4,5,NULL,S_MNTR_DIE5,0,0}, // S_MNTR_DIE4 +{SPR_MNTR,4,5,A_NoBlocking,S_MNTR_DIE6,0,0}, // S_MNTR_DIE5 +{SPR_MNTR,4,5,NULL,S_MNTR_DIE7,0,0}, // S_MNTR_DIE6 +{SPR_MNTR,4,5,A_MinotaurFade1,S_MNTR_DIE8,0,0}, // S_MNTR_DIE7 +{SPR_MNTR,4,5,A_MinotaurFade0,S_MNTR_DIE9,0,0}, // S_MNTR_DIE8 +{SPR_MNTR,4,10,NULL,S_NULL,0,0}, // S_MNTR_DIE9 +{SPR_FX12,32768,6,NULL,S_MNTRFX1_2,0,0}, // S_MNTRFX1_1 +{SPR_FX12,32769,6,NULL,S_MNTRFX1_1,0,0}, // S_MNTRFX1_2 +{SPR_FX12,32770,5,NULL,S_MNTRFXI1_2,0,0}, // S_MNTRFXI1_1 +{SPR_FX12,32771,5,NULL,S_MNTRFXI1_3,0,0}, // S_MNTRFXI1_2 +{SPR_FX12,32772,5,NULL,S_MNTRFXI1_4,0,0}, // S_MNTRFXI1_3 +{SPR_FX12,32773,5,NULL,S_MNTRFXI1_5,0,0}, // S_MNTRFXI1_4 +{SPR_FX12,32774,5,NULL,S_MNTRFXI1_6,0,0}, // S_MNTRFXI1_5 +{SPR_FX12,32775,5,NULL,S_NULL,0,0}, // S_MNTRFXI1_6 +{SPR_FX13,0,2,A_MntrFloorFire,S_MNTRFX2_1,0,0}, // S_MNTRFX2_1 +{SPR_FX13,32776,4,A_Explode,S_MNTRFXI2_2,0,0}, // S_MNTRFXI2_1 +{SPR_FX13,32777,4,NULL,S_MNTRFXI2_3,0,0}, // S_MNTRFXI2_2 +{SPR_FX13,32778,4,NULL,S_MNTRFXI2_4,0,0}, // S_MNTRFXI2_3 +{SPR_FX13,32779,4,NULL,S_MNTRFXI2_5,0,0}, // S_MNTRFXI2_4 +{SPR_FX13,32780,4,NULL,S_NULL,0,0}, // S_MNTRFXI2_5 +{SPR_FX13,32771,4,NULL,S_MNTRFX3_2,0,0}, // S_MNTRFX3_1 +{SPR_FX13,32770,4,NULL,S_MNTRFX3_3,0,0}, // S_MNTRFX3_2 +{SPR_FX13,32769,5,NULL,S_MNTRFX3_4,0,0}, // S_MNTRFX3_3 +{SPR_FX13,32770,5,NULL,S_MNTRFX3_5,0,0}, // S_MNTRFX3_4 +{SPR_FX13,32771,5,NULL,S_MNTRFX3_6,0,0}, // S_MNTRFX3_5 +{SPR_FX13,32772,5,NULL,S_MNTRFX3_7,0,0}, // S_MNTRFX3_6 +{SPR_FX13,32773,4,NULL,S_MNTRFX3_8,0,0}, // S_MNTRFX3_7 +{SPR_FX13,32774,4,NULL,S_MNTRFX3_9,0,0}, // S_MNTRFX3_8 +{SPR_FX13,32775,4,NULL,S_NULL,0,0}, // S_MNTRFX3_9 +{SPR_MNSM,0,3,NULL,S_MINOSMOKE2,0,0}, // S_MINOSMOKE1 +{SPR_MNSM,1,3,NULL,S_MINOSMOKE3,0,0}, // S_MINOSMOKE2 +{SPR_MNSM,2,3,NULL,S_MINOSMOKE4,0,0}, // S_MINOSMOKE3 +{SPR_MNSM,3,3,NULL,S_MINOSMOKE5,0,0}, // S_MINOSMOKE4 +{SPR_MNSM,4,3,NULL,S_MINOSMOKE6,0,0}, // S_MINOSMOKE5 +{SPR_MNSM,5,3,NULL,S_MINOSMOKE7,0,0}, // S_MINOSMOKE6 +{SPR_MNSM,6,3,NULL,S_MINOSMOKE8,0,0}, // S_MINOSMOKE7 +{SPR_MNSM,7,3,NULL,S_MINOSMOKE9,0,0}, // S_MINOSMOKE8 +{SPR_MNSM,8,3,NULL,S_MINOSMOKE0,0,0}, // S_MINOSMOKE9 +{SPR_MNSM,9,3,NULL,S_MINOSMOKEA,0,0}, // S_MINOSMOKE0 +{SPR_MNSM,10,3,NULL,S_MINOSMOKEB,0,0}, // S_MINOSMOKEA +{SPR_MNSM,11,3,NULL,S_MINOSMOKEC,0,0}, // S_MINOSMOKEB +{SPR_MNSM,12,3,NULL,S_MINOSMOKED,0,0}, // S_MINOSMOKEC +{SPR_MNSM,13,3,NULL,S_MINOSMOKEE,0,0}, // S_MINOSMOKED +{SPR_MNSM,14,3,NULL,S_MINOSMOKEF,0,0}, // S_MINOSMOKEE +{SPR_MNSM,15,3,NULL,S_MINOSMOKEG,0,0}, // S_MINOSMOKEF +{SPR_MNSM,16,3,NULL,S_NULL,0,0}, // S_MINOSMOKEG +{SPR_MNSM,0,3,NULL,S_MINOSMOKEX2,0,0}, // S_MINOSMOKEX1 +{SPR_MNSM,1,3,NULL,S_MINOSMOKEX3,0,0}, // S_MINOSMOKEX2 +{SPR_MNSM,2,3,NULL,S_MINOSMOKEX4,0,0}, // S_MINOSMOKEX3 +{SPR_MNSM,3,3,NULL,S_MINOSMOKEX5,0,0}, // S_MINOSMOKEX4 +{SPR_MNSM,4,3,NULL,S_MINOSMOKEX6,0,0}, // S_MINOSMOKEX5 +{SPR_MNSM,5,3,NULL,S_MINOSMOKEX7,0,0}, // S_MINOSMOKEX6 +{SPR_MNSM,6,3,NULL,S_MINOSMOKEX8,0,0}, // S_MINOSMOKEX7 +{SPR_MNSM,7,3,NULL,S_MINOSMOKEX9,0,0}, // S_MINOSMOKEX8 +{SPR_MNSM,8,3,NULL,S_MINOSMOKEX0,0,0}, // S_MINOSMOKEX9 +{SPR_MNSM,9,3,NULL,S_MINOSMOKEXA,0,0}, // S_MINOSMOKEX0 +{SPR_MNSM,8,3,NULL,S_MINOSMOKEXB,0,0}, // S_MINOSMOKEXA +{SPR_MNSM,7,3,NULL,S_MINOSMOKEXC,0,0}, // S_MINOSMOKEXB +{SPR_MNSM,6,3,NULL,S_MINOSMOKEXD,0,0}, // S_MINOSMOKEXC +{SPR_MNSM,5,3,NULL,S_MINOSMOKEXE,0,0}, // S_MINOSMOKEXD +{SPR_MNSM,4,3,NULL,S_MINOSMOKEXF,0,0}, // S_MINOSMOKEXE +{SPR_MNSM,3,3,NULL,S_MINOSMOKEXG,0,0}, // S_MINOSMOKEXF +{SPR_MNSM,2,3,NULL,S_MINOSMOKEXH,0,0}, // S_MINOSMOKEXG +{SPR_MNSM,1,3,NULL,S_MINOSMOKEXI,0,0}, // S_MINOSMOKEXH +{SPR_MNSM,0,3,NULL,S_NULL,0,0}, // S_MINOSMOKEXI +{SPR_SSPT,7,10,A_Look,S_SERPENT_LOOK1,0,0}, // S_SERPENT_LOOK1 +{SPR_SSPT,7,1,A_SerpentChase,S_SERPENT_SWIM2,0,0}, // S_SERPENT_SWIM1 +{SPR_SSPT,7,1,A_SerpentChase,S_SERPENT_SWIM3,0,0}, // S_SERPENT_SWIM2 +{SPR_SSPT,7,2,A_SerpentHumpDecide,S_SERPENT_SWIM1,0,0}, // S_SERPENT_SWIM3 +{SPR_SSPT,7,3,A_SerpentUnHide,S_SERPENT_HUMP2,0,0}, // S_SERPENT_HUMP1 +{SPR_SSPT,4,3,A_SerpentRaiseHump,S_SERPENT_HUMP3,0,0}, // S_SERPENT_HUMP2 +{SPR_SSPT,5,3,A_SerpentRaiseHump,S_SERPENT_HUMP4,0,0}, // S_SERPENT_HUMP3 +{SPR_SSPT,6,3,A_SerpentRaiseHump,S_SERPENT_HUMP5,0,0}, // S_SERPENT_HUMP4 +{SPR_SSPT,4,3,A_SerpentRaiseHump,S_SERPENT_HUMP6,0,0}, // S_SERPENT_HUMP5 +{SPR_SSPT,5,3,A_SerpentRaiseHump,S_SERPENT_HUMP7,0,0}, // S_SERPENT_HUMP6 +{SPR_SSPT,6,3,NULL,S_SERPENT_HUMP8,0,0}, // S_SERPENT_HUMP7 +{SPR_SSPT,4,3,NULL,S_SERPENT_HUMP9,0,0}, // S_SERPENT_HUMP8 +{SPR_SSPT,5,3,NULL,S_SERPENT_HUMP10,0,0}, // S_SERPENT_HUMP9 +{SPR_SSPT,6,3,A_SerpentLowerHump,S_SERPENT_HUMP11,0,0}, // S_SERPENT_HUMP10 +{SPR_SSPT,4,3,A_SerpentLowerHump,S_SERPENT_HUMP12,0,0}, // S_SERPENT_HUMP11 +{SPR_SSPT,5,3,A_SerpentLowerHump,S_SERPENT_HUMP13,0,0}, // S_SERPENT_HUMP12 +{SPR_SSPT,6,3,A_SerpentLowerHump,S_SERPENT_HUMP14,0,0}, // S_SERPENT_HUMP13 +{SPR_SSPT,4,3,A_SerpentLowerHump,S_SERPENT_HUMP15,0,0}, // S_SERPENT_HUMP14 +{SPR_SSPT,5,3,A_SerpentHide,S_SERPENT_SWIM1,0,0}, // S_SERPENT_HUMP15 +{SPR_SSPT,0,1,A_UnHideThing,S_SERPENT_SURFACE2,0,0}, // S_SERPENT_SURFACE1 +{SPR_SSPT,0,1,A_SerpentBirthScream,S_SERPENT_SURFACE3,0,0}, // S_SERPENT_SURFACE2 +{SPR_SSPT,1,3,A_SetShootable,S_SERPENT_SURFACE4,0,0}, // S_SERPENT_SURFACE3 +{SPR_SSPT,2,3,NULL,S_SERPENT_SURFACE5,0,0}, // S_SERPENT_SURFACE4 +{SPR_SSPT,3,4,A_SerpentCheckForAttack,S_SERPENT_DIVE1,0,0}, // S_SERPENT_SURFACE5 +{SPR_SSDV,0,4,NULL,S_SERPENT_DIVE2,0,0}, // S_SERPENT_DIVE1 +{SPR_SSDV,1,4,NULL,S_SERPENT_DIVE3,0,0}, // S_SERPENT_DIVE2 +{SPR_SSDV,2,4,NULL,S_SERPENT_DIVE4,0,0}, // S_SERPENT_DIVE3 +{SPR_SSDV,3,4,A_UnSetShootable,S_SERPENT_DIVE5,0,0}, // S_SERPENT_DIVE4 +{SPR_SSDV,4,3,A_SerpentDiveSound,S_SERPENT_DIVE6,0,0}, // S_SERPENT_DIVE5 +{SPR_SSDV,5,3,NULL,S_SERPENT_DIVE7,0,0}, // S_SERPENT_DIVE6 +{SPR_SSDV,6,4,NULL,S_SERPENT_DIVE8,0,0}, // S_SERPENT_DIVE7 +{SPR_SSDV,7,4,NULL,S_SERPENT_DIVE9,0,0}, // S_SERPENT_DIVE8 +{SPR_SSDV,8,3,NULL,S_SERPENT_DIVE10,0,0}, // S_SERPENT_DIVE9 +{SPR_SSDV,9,3,A_SerpentHide,S_SERPENT_SWIM1,0,0}, // S_SERPENT_DIVE10 +{SPR_SSPT,8,5,A_SerpentWalk,S_SERPENT_WALK2,0,0}, // S_SERPENT_WALK1 +{SPR_SSPT,9,5,A_SerpentWalk,S_SERPENT_WALK3,0,0}, // S_SERPENT_WALK2 +{SPR_SSPT,8,5,A_SerpentWalk,S_SERPENT_WALK4,0,0}, // S_SERPENT_WALK3 +{SPR_SSPT,9,5,A_SerpentCheckForAttack,S_SERPENT_DIVE1,0,0}, // S_SERPENT_WALK4 +{SPR_SSPT,11,5,NULL,S_SERPENT_PAIN2,0,0}, // S_SERPENT_PAIN1 +{SPR_SSPT,11,5,A_Pain,S_SERPENT_DIVE1,0,0}, // S_SERPENT_PAIN2 +{SPR_SSPT,10,6,A_FaceTarget,S_SERPENT_ATK2,0,0}, // S_SERPENT_ATK1 +{SPR_SSPT,11,5,A_SerpentChooseAttack,S_SERPENT_MELEE1,0,0}, // S_SERPENT_ATK2 +{SPR_SSPT,13,5,A_SerpentMeleeAttack,S_SERPENT_DIVE1,0,0}, // S_SERPENT_MELEE1 +{SPR_SSPT,13,5,A_SerpentMissileAttack,S_SERPENT_DIVE1,0,0}, // S_SERPENT_MISSILE1 +{SPR_SSPT,14,4,NULL,S_SERPENT_DIE2,0,0}, // S_SERPENT_DIE1 +{SPR_SSPT,15,4,A_Scream,S_SERPENT_DIE3,0,0}, // S_SERPENT_DIE2 +{SPR_SSPT,16,4,A_NoBlocking,S_SERPENT_DIE4,0,0}, // S_SERPENT_DIE3 +{SPR_SSPT,17,4,NULL,S_SERPENT_DIE5,0,0}, // S_SERPENT_DIE4 +{SPR_SSPT,18,4,NULL,S_SERPENT_DIE6,0,0}, // S_SERPENT_DIE5 +{SPR_SSPT,19,4,NULL,S_SERPENT_DIE7,0,0}, // S_SERPENT_DIE6 +{SPR_SSPT,20,4,NULL,S_SERPENT_DIE8,0,0}, // S_SERPENT_DIE7 +{SPR_SSPT,21,4,NULL,S_SERPENT_DIE9,0,0}, // S_SERPENT_DIE8 +{SPR_SSPT,22,4,NULL,S_SERPENT_DIE10,0,0}, // S_SERPENT_DIE9 +{SPR_SSPT,23,4,NULL,S_SERPENT_DIE11,0,0}, // S_SERPENT_DIE10 +{SPR_SSPT,24,4,NULL,S_SERPENT_DIE12,0,0}, // S_SERPENT_DIE11 +{SPR_SSPT,25,4,NULL,S_NULL,0,0}, // S_SERPENT_DIE12 +{SPR_SSXD,0,4,NULL,S_SERPENT_XDIE2,0,0}, // S_SERPENT_XDIE1 +{SPR_SSXD,1,4,A_SerpentHeadPop,S_SERPENT_XDIE3,0,0}, // S_SERPENT_XDIE2 +{SPR_SSXD,2,4,A_NoBlocking,S_SERPENT_XDIE4,0,0}, // S_SERPENT_XDIE3 +{SPR_SSXD,3,4,NULL,S_SERPENT_XDIE5,0,0}, // S_SERPENT_XDIE4 +{SPR_SSXD,4,4,NULL,S_SERPENT_XDIE6,0,0}, // S_SERPENT_XDIE5 +{SPR_SSXD,5,3,NULL,S_SERPENT_XDIE7,0,0}, // S_SERPENT_XDIE6 +{SPR_SSXD,6,3,NULL,S_SERPENT_XDIE8,0,0}, // S_SERPENT_XDIE7 +{SPR_SSXD,7,3,A_SerpentSpawnGibs,S_NULL,0,0}, // S_SERPENT_XDIE8 +{SPR_SSPT,26,5,A_FreezeDeath,S_SERPENT_ICE2,0,0}, // S_SERPENT_ICE +{SPR_SSPT,26,1,A_FreezeDeathChunks,S_SERPENT_ICE2,0,0}, // S_SERPENT_ICE2 +{SPR_SSFX,32768,3,A_ContMobjSound,S_SERPENT_FX2,0,0}, // S_SERPENT_FX1 +{SPR_SSFX,32769,3,NULL,S_SERPENT_FX3,0,0}, // S_SERPENT_FX2 +{SPR_SSFX,32768,3,NULL,S_SERPENT_FX4,0,0}, // S_SERPENT_FX3 +{SPR_SSFX,32769,3,NULL,S_SERPENT_FX1,0,0}, // S_SERPENT_FX4 +{SPR_SSFX,32770,4,NULL,S_SERPENT_FX_X2,0,0}, // S_SERPENT_FX_X1 +{SPR_SSFX,32771,4,NULL,S_SERPENT_FX_X3,0,0}, // S_SERPENT_FX_X2 +{SPR_SSFX,32772,4,NULL,S_SERPENT_FX_X4,0,0}, // S_SERPENT_FX_X3 +{SPR_SSFX,32773,4,NULL,S_SERPENT_FX_X5,0,0}, // S_SERPENT_FX_X4 +{SPR_SSFX,32774,4,NULL,S_SERPENT_FX_X6,0,0}, // S_SERPENT_FX_X5 +{SPR_SSFX,32775,4,NULL,S_NULL,0,0}, // S_SERPENT_FX_X6 +{SPR_SSXD,8,4,A_SerpentHeadCheck,S_SERPENT_HEAD2,0,0}, // S_SERPENT_HEAD1 +{SPR_SSXD,9,4,A_SerpentHeadCheck,S_SERPENT_HEAD3,0,0}, // S_SERPENT_HEAD2 +{SPR_SSXD,10,4,A_SerpentHeadCheck,S_SERPENT_HEAD4,0,0}, // S_SERPENT_HEAD3 +{SPR_SSXD,11,4,A_SerpentHeadCheck,S_SERPENT_HEAD5,0,0}, // S_SERPENT_HEAD4 +{SPR_SSXD,12,4,A_SerpentHeadCheck,S_SERPENT_HEAD6,0,0}, // S_SERPENT_HEAD5 +{SPR_SSXD,13,4,A_SerpentHeadCheck,S_SERPENT_HEAD7,0,0}, // S_SERPENT_HEAD6 +{SPR_SSXD,14,4,A_SerpentHeadCheck,S_SERPENT_HEAD8,0,0}, // S_SERPENT_HEAD7 +{SPR_SSXD,15,4,A_SerpentHeadCheck,S_SERPENT_HEAD1,0,0}, // S_SERPENT_HEAD8 +{SPR_SSXD,18,-1,NULL,S_SERPENT_HEAD_X1,0,0}, // S_SERPENT_HEAD_X1 +{SPR_SSXD,16,6,NULL,S_SERPENT_GIB1_2,0,0}, // S_SERPENT_GIB1_1 +{SPR_SSXD,16,6,A_FloatGib,S_SERPENT_GIB1_3,0,0}, // S_SERPENT_GIB1_2 +{SPR_SSXD,16,8,A_FloatGib,S_SERPENT_GIB1_4,0,0}, // S_SERPENT_GIB1_3 +{SPR_SSXD,16,8,A_FloatGib,S_SERPENT_GIB1_5,0,0}, // S_SERPENT_GIB1_4 +{SPR_SSXD,16,12,A_FloatGib,S_SERPENT_GIB1_6,0,0}, // S_SERPENT_GIB1_5 +{SPR_SSXD,16,12,A_FloatGib,S_SERPENT_GIB1_7,0,0}, // S_SERPENT_GIB1_6 +{SPR_SSXD,16,232,A_DelayGib,S_SERPENT_GIB1_8,0,0}, // S_SERPENT_GIB1_7 +{SPR_SSXD,16,12,A_SinkGib,S_SERPENT_GIB1_9,0,0}, // S_SERPENT_GIB1_8 +{SPR_SSXD,16,12,A_SinkGib,S_SERPENT_GIB1_10,0,0}, // S_SERPENT_GIB1_9 +{SPR_SSXD,16,8,A_SinkGib,S_SERPENT_GIB1_11,0,0}, // S_SERPENT_GIB1_10 +{SPR_SSXD,16,8,A_SinkGib,S_SERPENT_GIB1_12,0,0}, // S_SERPENT_GIB1_11 +{SPR_SSXD,16,8,A_SinkGib,S_NULL,0,0}, // S_SERPENT_GIB1_12 +{SPR_SSXD,17,6,NULL,S_SERPENT_GIB2_2,0,0}, // S_SERPENT_GIB2_1 +{SPR_SSXD,17,6,A_FloatGib,S_SERPENT_GIB2_3,0,0}, // S_SERPENT_GIB2_2 +{SPR_SSXD,17,8,A_FloatGib,S_SERPENT_GIB2_4,0,0}, // S_SERPENT_GIB2_3 +{SPR_SSXD,17,8,A_FloatGib,S_SERPENT_GIB2_5,0,0}, // S_SERPENT_GIB2_4 +{SPR_SSXD,17,12,A_FloatGib,S_SERPENT_GIB2_6,0,0}, // S_SERPENT_GIB2_5 +{SPR_SSXD,17,12,A_FloatGib,S_SERPENT_GIB2_7,0,0}, // S_SERPENT_GIB2_6 +{SPR_SSXD,17,232,A_DelayGib,S_SERPENT_GIB2_8,0,0}, // S_SERPENT_GIB2_7 +{SPR_SSXD,17,12,A_SinkGib,S_SERPENT_GIB2_9,0,0}, // S_SERPENT_GIB2_8 +{SPR_SSXD,17,12,A_SinkGib,S_SERPENT_GIB2_10,0,0}, // S_SERPENT_GIB2_9 +{SPR_SSXD,17,8,A_SinkGib,S_SERPENT_GIB2_11,0,0}, // S_SERPENT_GIB2_10 +{SPR_SSXD,17,8,A_SinkGib,S_SERPENT_GIB2_12,0,0}, // S_SERPENT_GIB2_11 +{SPR_SSXD,17,8,A_SinkGib,S_NULL,0,0}, // S_SERPENT_GIB2_12 +{SPR_SSXD,19,6,NULL,S_SERPENT_GIB3_2,0,0}, // S_SERPENT_GIB3_1 +{SPR_SSXD,19,6,A_FloatGib,S_SERPENT_GIB3_3,0,0}, // S_SERPENT_GIB3_2 +{SPR_SSXD,19,8,A_FloatGib,S_SERPENT_GIB3_4,0,0}, // S_SERPENT_GIB3_3 +{SPR_SSXD,19,8,A_FloatGib,S_SERPENT_GIB3_5,0,0}, // S_SERPENT_GIB3_4 +{SPR_SSXD,19,12,A_FloatGib,S_SERPENT_GIB3_6,0,0}, // S_SERPENT_GIB3_5 +{SPR_SSXD,19,12,A_FloatGib,S_SERPENT_GIB3_7,0,0}, // S_SERPENT_GIB3_6 +{SPR_SSXD,19,232,A_DelayGib,S_SERPENT_GIB3_8,0,0}, // S_SERPENT_GIB3_7 +{SPR_SSXD,19,12,A_SinkGib,S_SERPENT_GIB3_9,0,0}, // S_SERPENT_GIB3_8 +{SPR_SSXD,19,12,A_SinkGib,S_SERPENT_GIB3_10,0,0}, // S_SERPENT_GIB3_9 +{SPR_SSXD,19,8,A_SinkGib,S_SERPENT_GIB3_11,0,0}, // S_SERPENT_GIB3_10 +{SPR_SSXD,19,8,A_SinkGib,S_SERPENT_GIB3_12,0,0}, // S_SERPENT_GIB3_11 +{SPR_SSXD,19,8,A_SinkGib,S_NULL,0,0}, // S_SERPENT_GIB3_12 +{SPR_BISH,0,10,A_Look,S_BISHOP_LOOK1,0,0}, // S_BISHOP_LOOK1 +{SPR_BISH,0,1,A_BishopDecide,S_BISHOP_WALK1,0,0}, // S_BISHOP_DECIDE +{SPR_BISH,0,2,A_BishopDoBlur,S_BISHOP_BLUR2,0,0}, // S_BISHOP_BLUR1 +{SPR_BISH,0,4,A_BishopSpawnBlur,S_BISHOP_BLUR2,0,0}, // S_BISHOP_BLUR2 +{SPR_BISH,0,2,A_Chase,S_BISHOP_WALK2,0,0}, // S_BISHOP_WALK1 +{SPR_BISH,0,2,A_BishopChase,S_BISHOP_WALK3,0,0}, // S_BISHOP_WALK2 +{SPR_BISH,0,2,NULL,S_BISHOP_WALK4,0,0}, // S_BISHOP_WALK3 +{SPR_BISH,1,2,A_BishopChase,S_BISHOP_WALK5,0,0}, // S_BISHOP_WALK4 +{SPR_BISH,1,2,A_Chase,S_BISHOP_WALK6,0,0}, // S_BISHOP_WALK5 +{SPR_BISH,1,2,A_BishopChase,S_BISHOP_DECIDE,0,0}, // S_BISHOP_WALK6 +{SPR_BISH,0,3,A_FaceTarget,S_BISHOP_ATK2,0,0}, // S_BISHOP_ATK1 +{SPR_BISH,32771,3,A_FaceTarget,S_BISHOP_ATK3,0,0}, // S_BISHOP_ATK2 +{SPR_BISH,32772,3,A_FaceTarget,S_BISHOP_ATK4,0,0}, // S_BISHOP_ATK3 +{SPR_BISH,32773,3,A_BishopAttack,S_BISHOP_ATK5,0,0}, // S_BISHOP_ATK4 +{SPR_BISH,32773,5,A_BishopAttack2,S_BISHOP_ATK5,0,0}, // S_BISHOP_ATK5 +{SPR_BISH,2,6,A_Pain,S_BISHOP_PAIN2,0,0}, // S_BISHOP_PAIN1 +{SPR_BISH,2,6,A_BishopPainBlur,S_BISHOP_PAIN3,0,0}, // S_BISHOP_PAIN2 +{SPR_BISH,2,6,A_BishopPainBlur,S_BISHOP_PAIN4,0,0}, // S_BISHOP_PAIN3 +{SPR_BISH,2,6,A_BishopPainBlur,S_BISHOP_PAIN5,0,0}, // S_BISHOP_PAIN4 +{SPR_BISH,2,0,NULL,S_BISHOP_WALK1,0,0}, // S_BISHOP_PAIN5 +{SPR_BISH,6,6,NULL,S_BISHOP_DEATH2,0,0}, // S_BISHOP_DEATH1 +{SPR_BISH,32775,6,A_Scream,S_BISHOP_DEATH3,0,0}, // S_BISHOP_DEATH2 +{SPR_BISH,32776,5,A_NoBlocking,S_BISHOP_DEATH4,0,0}, // S_BISHOP_DEATH3 +{SPR_BISH,32777,5,A_Explode,S_BISHOP_DEATH5,0,0}, // S_BISHOP_DEATH4 +{SPR_BISH,32778,5,NULL,S_BISHOP_DEATH6,0,0}, // S_BISHOP_DEATH5 +{SPR_BISH,32779,4,NULL,S_BISHOP_DEATH7,0,0}, // S_BISHOP_DEATH6 +{SPR_BISH,32780,4,NULL,S_BISHOP_DEATH8,0,0}, // S_BISHOP_DEATH7 +{SPR_BISH,13,4,A_BishopPuff,S_BISHOP_DEATH9,0,0}, // S_BISHOP_DEATH8 +{SPR_BISH,14,4,A_QueueCorpse,S_BISHOP_DEATH10,0,0}, // S_BISHOP_DEATH9 +{SPR_BISH,15,-1,NULL,S_NULL,0,0}, // S_BISHOP_DEATH10 +{SPR_BISH,23,5,A_FreezeDeath,S_BISHOP_ICE2,0,0}, // S_BISHOP_ICE +{SPR_BISH,23,1,A_FreezeDeathChunks,S_BISHOP_ICE2,0,0}, // S_BISHOP_ICE2 +{SPR_BISH,16,5,NULL,S_BISHOP_PUFF2,0,0}, // S_BISHOP_PUFF1 +{SPR_BISH,17,5,NULL,S_BISHOP_PUFF3,0,0}, // S_BISHOP_PUFF2 +{SPR_BISH,18,5,NULL,S_BISHOP_PUFF4,0,0}, // S_BISHOP_PUFF3 +{SPR_BISH,19,5,NULL,S_BISHOP_PUFF5,0,0}, // S_BISHOP_PUFF4 +{SPR_BISH,20,6,NULL,S_BISHOP_PUFF6,0,0}, // S_BISHOP_PUFF5 +{SPR_BISH,21,6,NULL,S_BISHOP_PUFF7,0,0}, // S_BISHOP_PUFF6 +{SPR_BISH,22,5,NULL,S_NULL,0,0}, // S_BISHOP_PUFF7 +{SPR_BISH,0,16,NULL,S_BISHOPBLUR2,0,0}, // S_BISHOPBLUR1 +{SPR_BISH,0,8,A_SetAltShadow,S_NULL,0,0}, // S_BISHOPBLUR2 +{SPR_BISH,2,8,NULL,S_NULL,0,0}, // S_BISHOPPAINBLUR1 +{SPR_BPFX,32768,1,A_BishopMissileWeave,S_BISHFX1_2,0,0}, // S_BISHFX1_1 +{SPR_BPFX,32769,1,A_BishopMissileWeave,S_BISHFX1_3,0,0}, // S_BISHFX1_2 +{SPR_BPFX,32768,1,A_BishopMissileWeave,S_BISHFX1_4,0,0}, // S_BISHFX1_3 +{SPR_BPFX,32769,1,A_BishopMissileWeave,S_BISHFX1_5,0,0}, // S_BISHFX1_4 +{SPR_BPFX,32769,0,A_BishopMissileSeek,S_BISHFX1_1,0,0}, // S_BISHFX1_5 +{SPR_BPFX,32770,4,NULL,S_BISHFXI1_2,0,0}, // S_BISHFXI1_1 +{SPR_BPFX,32771,4,NULL,S_BISHFXI1_3,0,0}, // S_BISHFXI1_2 +{SPR_BPFX,32772,4,NULL,S_BISHFXI1_4,0,0}, // S_BISHFXI1_3 +{SPR_BPFX,32773,4,NULL,S_BISHFXI1_5,0,0}, // S_BISHFXI1_4 +{SPR_BPFX,32774,3,NULL,S_BISHFXI1_6,0,0}, // S_BISHFXI1_5 +{SPR_BPFX,32775,3,NULL,S_NULL,0,0}, // S_BISHFXI1_6 +{SPR_DRAG,3,10,A_Look,S_DRAGON_LOOK1,0,0}, // S_DRAGON_LOOK1 +{SPR_DRAG,2,5,NULL,S_DRAGON_INIT2,0,0}, // S_DRAGON_INIT +{SPR_DRAG,1,5,NULL,S_DRAGON_INIT3,0,0}, // S_DRAGON_INIT2 +{SPR_DRAG,0,5,A_DragonInitFlight,S_DRAGON_WALK1,0,0}, // S_DRAGON_INIT3 +{SPR_DRAG,1,3,A_DragonFlap,S_DRAGON_WALK2,0,0}, // S_DRAGON_WALK1 +{SPR_DRAG,1,3,A_DragonFlight,S_DRAGON_WALK3,0,0}, // S_DRAGON_WALK2 +{SPR_DRAG,2,3,A_DragonFlight,S_DRAGON_WALK4,0,0}, // S_DRAGON_WALK3 +{SPR_DRAG,2,3,A_DragonFlight,S_DRAGON_WALK5,0,0}, // S_DRAGON_WALK4 +{SPR_DRAG,3,3,A_DragonFlight,S_DRAGON_WALK6,0,0}, // S_DRAGON_WALK5 +{SPR_DRAG,3,3,A_DragonFlight,S_DRAGON_WALK7,0,0}, // S_DRAGON_WALK6 +{SPR_DRAG,2,3,A_DragonFlight,S_DRAGON_WALK8,0,0}, // S_DRAGON_WALK7 +{SPR_DRAG,2,3,A_DragonFlight,S_DRAGON_WALK9,0,0}, // S_DRAGON_WALK8 +{SPR_DRAG,1,3,A_DragonFlight,S_DRAGON_WALK10,0,0}, // S_DRAGON_WALK9 +{SPR_DRAG,1,3,A_DragonFlight,S_DRAGON_WALK11,0,0}, // S_DRAGON_WALK10 +{SPR_DRAG,0,3,A_DragonFlight,S_DRAGON_WALK12,0,0}, // S_DRAGON_WALK11 +{SPR_DRAG,0,3,A_DragonFlight,S_DRAGON_WALK1,0,0}, // S_DRAGON_WALK12 +{SPR_DRAG,4,8,A_DragonAttack,S_DRAGON_WALK1,0,0}, // S_DRAGON_ATK1 +{SPR_DRAG,5,10,A_DragonPain,S_DRAGON_WALK1,0,0}, // S_DRAGON_PAIN1 +{SPR_DRAG,6,5,A_Scream,S_DRAGON_DEATH2,0,0}, // S_DRAGON_DEATH1 +{SPR_DRAG,7,4,A_NoBlocking,S_DRAGON_DEATH3,0,0}, // S_DRAGON_DEATH2 +{SPR_DRAG,8,4,NULL,S_DRAGON_DEATH4,0,0}, // S_DRAGON_DEATH3 +{SPR_DRAG,9,4,A_DragonCheckCrash,S_DRAGON_DEATH4,0,0}, // S_DRAGON_DEATH4 +{SPR_DRAG,10,5,NULL,S_DRAGON_CRASH2,0,0}, // S_DRAGON_CRASH1 +{SPR_DRAG,11,5,NULL,S_DRAGON_CRASH3,0,0}, // S_DRAGON_CRASH2 +{SPR_DRAG,12,-1,NULL,S_NULL,0,0}, // S_DRAGON_CRASH3 +{SPR_DRFX,32768,4,NULL,S_DRAGON_FX1_2,0,0}, // S_DRAGON_FX1_1 +{SPR_DRFX,32769,4,NULL,S_DRAGON_FX1_3,0,0}, // S_DRAGON_FX1_2 +{SPR_DRFX,32770,4,NULL,S_DRAGON_FX1_4,0,0}, // S_DRAGON_FX1_3 +{SPR_DRFX,32771,4,NULL,S_DRAGON_FX1_5,0,0}, // S_DRAGON_FX1_4 +{SPR_DRFX,32772,4,NULL,S_DRAGON_FX1_6,0,0}, // S_DRAGON_FX1_5 +{SPR_DRFX,32773,4,NULL,S_DRAGON_FX1_1,0,0}, // S_DRAGON_FX1_6 +{SPR_DRFX,32774,4,NULL,S_DRAGON_FX1_X2,0,0}, // S_DRAGON_FX1_X1 +{SPR_DRFX,32775,4,NULL,S_DRAGON_FX1_X3,0,0}, // S_DRAGON_FX1_X2 +{SPR_DRFX,32776,4,NULL,S_DRAGON_FX1_X4,0,0}, // S_DRAGON_FX1_X3 +{SPR_DRFX,32777,4,A_DragonFX2,S_DRAGON_FX1_X5,0,0}, // S_DRAGON_FX1_X4 +{SPR_DRFX,32778,3,NULL,S_DRAGON_FX1_X6,0,0}, // S_DRAGON_FX1_X5 +{SPR_DRFX,32779,3,NULL,S_NULL,0,0}, // S_DRAGON_FX1_X6 +{SPR_CFCF,32784,1,NULL,S_DRAGON_FX2_2,0,0}, // S_DRAGON_FX2_1 +{SPR_CFCF,32784,4,A_UnHideThing,S_DRAGON_FX2_3,0,0}, // S_DRAGON_FX2_2 +{SPR_CFCF,32785,3,A_Scream,S_DRAGON_FX2_4,0,0}, // S_DRAGON_FX2_3 +{SPR_CFCF,32786,4,NULL,S_DRAGON_FX2_5,0,0}, // S_DRAGON_FX2_4 +{SPR_CFCF,32787,3,A_Explode,S_DRAGON_FX2_6,0,0}, // S_DRAGON_FX2_5 +{SPR_CFCF,32788,4,NULL,S_DRAGON_FX2_7,0,0}, // S_DRAGON_FX2_6 +{SPR_CFCF,32789,3,NULL,S_DRAGON_FX2_8,0,0}, // S_DRAGON_FX2_7 +{SPR_CFCF,32790,4,NULL,S_DRAGON_FX2_9,0,0}, // S_DRAGON_FX2_8 +{SPR_CFCF,32791,3,NULL,S_DRAGON_FX2_10,0,0}, // S_DRAGON_FX2_9 +{SPR_CFCF,32792,4,NULL,S_DRAGON_FX2_11,0,0}, // S_DRAGON_FX2_10 +{SPR_CFCF,32793,3,NULL,S_NULL,0,0}, // S_DRAGON_FX2_11 +{SPR_ARM1,0,-1,NULL,S_NULL,0,0}, // S_ARMOR_1 +{SPR_ARM2,0,-1,NULL,S_NULL,0,0}, // S_ARMOR_2 +{SPR_ARM3,0,-1,NULL,S_NULL,0,0}, // S_ARMOR_3 +{SPR_ARM4,0,-1,NULL,S_NULL,0,0}, // S_ARMOR_4 +{SPR_MAN1,32768,4,NULL,S_MANA1_2,0,0}, // S_MANA1_1 +{SPR_MAN1,32769,4,NULL,S_MANA1_3,0,0}, // S_MANA1_2 +{SPR_MAN1,32770,4,NULL,S_MANA1_4,0,0}, // S_MANA1_3 +{SPR_MAN1,32771,4,NULL,S_MANA1_5,0,0}, // S_MANA1_4 +{SPR_MAN1,32772,4,NULL,S_MANA1_6,0,0}, // S_MANA1_5 +{SPR_MAN1,32773,4,NULL,S_MANA1_7,0,0}, // S_MANA1_6 +{SPR_MAN1,32774,4,NULL,S_MANA1_8,0,0}, // S_MANA1_7 +{SPR_MAN1,32775,4,NULL,S_MANA1_9,0,0}, // S_MANA1_8 +{SPR_MAN1,32776,4,NULL,S_MANA1_1,0,0}, // S_MANA1_9 +{SPR_MAN2,32768,4,NULL,S_MANA2_2,0,0}, // S_MANA2_1 +{SPR_MAN2,32769,4,NULL,S_MANA2_3,0,0}, // S_MANA2_2 +{SPR_MAN2,32770,4,NULL,S_MANA2_4,0,0}, // S_MANA2_3 +{SPR_MAN2,32771,4,NULL,S_MANA2_5,0,0}, // S_MANA2_4 +{SPR_MAN2,32772,4,NULL,S_MANA2_6,0,0}, // S_MANA2_5 +{SPR_MAN2,32773,4,NULL,S_MANA2_7,0,0}, // S_MANA2_6 +{SPR_MAN2,32774,4,NULL,S_MANA2_8,0,0}, // S_MANA2_7 +{SPR_MAN2,32775,4,NULL,S_MANA2_9,0,0}, // S_MANA2_8 +{SPR_MAN2,32776,4,NULL,S_MANA2_10,0,0}, // S_MANA2_9 +{SPR_MAN2,32777,4,NULL,S_MANA2_11,0,0}, // S_MANA2_10 +{SPR_MAN2,32778,4,NULL,S_MANA2_12,0,0}, // S_MANA2_11 +{SPR_MAN2,32779,4,NULL,S_MANA2_13,0,0}, // S_MANA2_12 +{SPR_MAN2,32780,4,NULL,S_MANA2_14,0,0}, // S_MANA2_13 +{SPR_MAN2,32781,4,NULL,S_MANA2_15,0,0}, // S_MANA2_14 +{SPR_MAN2,32782,4,NULL,S_MANA2_16,0,0}, // S_MANA2_15 +{SPR_MAN2,32783,4,NULL,S_MANA2_1,0,0}, // S_MANA2_16 +{SPR_MAN3,32768,4,NULL,S_MANA3_2,0,0}, // S_MANA3_1 +{SPR_MAN3,32769,4,NULL,S_MANA3_3,0,0}, // S_MANA3_2 +{SPR_MAN3,32770,4,NULL,S_MANA3_4,0,0}, // S_MANA3_3 +{SPR_MAN3,32771,4,NULL,S_MANA3_5,0,0}, // S_MANA3_4 +{SPR_MAN3,32772,4,NULL,S_MANA3_6,0,0}, // S_MANA3_5 +{SPR_MAN3,32773,4,NULL,S_MANA3_7,0,0}, // S_MANA3_6 +{SPR_MAN3,32774,4,NULL,S_MANA3_8,0,0}, // S_MANA3_7 +{SPR_MAN3,32775,4,NULL,S_MANA3_9,0,0}, // S_MANA3_8 +{SPR_MAN3,32776,4,NULL,S_MANA3_10,0,0}, // S_MANA3_9 +{SPR_MAN3,32777,4,NULL,S_MANA3_11,0,0}, // S_MANA3_10 +{SPR_MAN3,32778,4,NULL,S_MANA3_12,0,0}, // S_MANA3_11 +{SPR_MAN3,32779,4,NULL,S_MANA3_13,0,0}, // S_MANA3_12 +{SPR_MAN3,32780,4,NULL,S_MANA3_14,0,0}, // S_MANA3_13 +{SPR_MAN3,32781,4,NULL,S_MANA3_15,0,0}, // S_MANA3_14 +{SPR_MAN3,32782,4,NULL,S_MANA3_16,0,0}, // S_MANA3_15 +{SPR_MAN3,32783,4,NULL,S_MANA3_1,0,0}, // S_MANA3_16 +{SPR_KEY1,0,-1,NULL,S_NULL,0,0}, // S_KEY1 +{SPR_KEY2,0,-1,NULL,S_NULL,0,0}, // S_KEY2 +{SPR_KEY3,0,-1,NULL,S_NULL,0,0}, // S_KEY3 +{SPR_KEY4,0,-1,NULL,S_NULL,0,0}, // S_KEY4 +{SPR_KEY5,0,-1,NULL,S_NULL,0,0}, // S_KEY5 +{SPR_KEY6,0,-1,NULL,S_NULL,0,0}, // S_KEY6 +{SPR_KEY7,0,-1,NULL,S_NULL,0,0}, // S_KEY7 +{SPR_KEY8,0,-1,NULL,S_NULL,0,0}, // S_KEY8 +{SPR_KEY9,0,-1,NULL,S_NULL,0,0}, // S_KEY9 +{SPR_KEYA,0,-1,NULL,S_NULL,0,0}, // S_KEYA +{SPR_KEYB,0,-1,NULL,S_NULL,0,0}, // S_KEYB +{SPR_TLGL,0,1,NULL,S_SND_WIND2,0,0}, // S_SND_WIND1 +{SPR_TLGL,0,200,A_ESound,S_SND_WIND2,0,0}, // S_SND_WIND2 +{SPR_TLGL,0,85,A_ESound,S_SND_WATERFALL,0,0}, // S_SND_WATERFALL +{SPR_ETTN,0,10,A_Look,S_ETTIN_LOOK2,0,0}, // S_ETTIN_LOOK1 +{SPR_ETTN,0,10,A_Look,S_ETTIN_LOOK1,0,0}, // S_ETTIN_LOOK2 +{SPR_ETTN,0,5,A_Chase,S_ETTIN_CHASE2,0,0}, // S_ETTIN_CHASE1 +{SPR_ETTN,1,5,A_Chase,S_ETTIN_CHASE3,0,0}, // S_ETTIN_CHASE2 +{SPR_ETTN,2,5,A_Chase,S_ETTIN_CHASE4,0,0}, // S_ETTIN_CHASE3 +{SPR_ETTN,3,5,A_Chase,S_ETTIN_CHASE1,0,0}, // S_ETTIN_CHASE4 +{SPR_ETTN,7,7,A_Pain,S_ETTIN_CHASE1,0,0}, // S_ETTIN_PAIN1 +{SPR_ETTN,4,6,A_FaceTarget,S_ETTIN_ATK1_2,0,0}, // S_ETTIN_ATK1_1 +{SPR_ETTN,5,6,A_FaceTarget,S_ETTIN_ATK1_3,0,0}, // S_ETTIN_ATK1_2 +{SPR_ETTN,6,8,A_EttinAttack,S_ETTIN_CHASE1,0,0}, // S_ETTIN_ATK1_3 +{SPR_ETTN,8,4,NULL,S_ETTIN_DEATH1_2,0,0}, // S_ETTIN_DEATH1_1 +{SPR_ETTN,9,4,NULL,S_ETTIN_DEATH1_3,0,0}, // S_ETTIN_DEATH1_2 +{SPR_ETTN,10,4,A_Scream,S_ETTIN_DEATH1_4,0,0}, // S_ETTIN_DEATH1_3 +{SPR_ETTN,11,4,A_NoBlocking,S_ETTIN_DEATH1_5,0,0}, // S_ETTIN_DEATH1_4 +{SPR_ETTN,12,4,A_QueueCorpse,S_ETTIN_DEATH1_6,0,0}, // S_ETTIN_DEATH1_5 +{SPR_ETTN,13,4,NULL,S_ETTIN_DEATH1_7,0,0}, // S_ETTIN_DEATH1_6 +{SPR_ETTN,14,4,NULL,S_ETTIN_DEATH1_8,0,0}, // S_ETTIN_DEATH1_7 +{SPR_ETTN,15,4,NULL,S_ETTIN_DEATH1_9,0,0}, // S_ETTIN_DEATH1_8 +{SPR_ETTN,16,-1,NULL,S_NULL,0,0}, // S_ETTIN_DEATH1_9 +{SPR_ETTB,0,4,NULL,S_ETTIN_DEATH2_2,0,0}, // S_ETTIN_DEATH2_1 +{SPR_ETTB,1,4,A_NoBlocking,S_ETTIN_DEATH2_3,0,0}, // S_ETTIN_DEATH2_2 +{SPR_ETTB,2,4,A_DropMace,S_ETTIN_DEATH2_4,0,0}, // S_ETTIN_DEATH2_3 +{SPR_ETTB,3,4,A_Scream,S_ETTIN_DEATH2_5,0,0}, // S_ETTIN_DEATH2_4 +{SPR_ETTB,4,4,A_QueueCorpse,S_ETTIN_DEATH2_6,0,0}, // S_ETTIN_DEATH2_5 +{SPR_ETTB,5,4,NULL,S_ETTIN_DEATH2_7,0,0}, // S_ETTIN_DEATH2_6 +{SPR_ETTB,6,4,NULL,S_ETTIN_DEATH2_8,0,0}, // S_ETTIN_DEATH2_7 +{SPR_ETTB,7,4,NULL,S_ETTIN_DEATH2_9,0,0}, // S_ETTIN_DEATH2_8 +{SPR_ETTB,8,4,NULL,S_ETTIN_DEATH2_0,0,0}, // S_ETTIN_DEATH2_9 +{SPR_ETTB,9,4,NULL,S_ETTIN_DEATH2_A,0,0}, // S_ETTIN_DEATH2_0 +{SPR_ETTB,10,4,NULL,S_ETTIN_DEATH2_B,0,0}, // S_ETTIN_DEATH2_A +{SPR_ETTB,11,-1,NULL,S_NULL,0,0}, // S_ETTIN_DEATH2_B +{SPR_ETTN,17,5,A_FreezeDeath,S_ETTIN_ICE2,0,0}, // S_ETTIN_ICE1 +{SPR_ETTN,17,1,A_FreezeDeathChunks,S_ETTIN_ICE2,0,0}, // S_ETTIN_ICE2 +{SPR_ETTB,12,5,A_CheckFloor,S_ETTIN_MACE2,0,0}, // S_ETTIN_MACE1 +{SPR_ETTB,13,5,A_CheckFloor,S_ETTIN_MACE3,0,0}, // S_ETTIN_MACE2 +{SPR_ETTB,14,5,A_CheckFloor,S_ETTIN_MACE4,0,0}, // S_ETTIN_MACE3 +{SPR_ETTB,15,5,A_CheckFloor,S_ETTIN_MACE1,0,0}, // S_ETTIN_MACE4 +{SPR_ETTB,16,5,NULL,S_ETTIN_MACE6,0,0}, // S_ETTIN_MACE5 +{SPR_ETTB,17,5,A_QueueCorpse,S_ETTIN_MACE7,0,0}, // S_ETTIN_MACE6 +{SPR_ETTB,18,-1,NULL,S_NULL,0,0}, // S_ETTIN_MACE7 +{SPR_FDMN,32791,5,NULL,S_FIRED_LOOK1,0,0}, // S_FIRED_SPAWN1 +{SPR_FDMN,32772,10,A_Look,S_FIRED_LOOK2,0,0}, // S_FIRED_LOOK1 +{SPR_FDMN,32773,10,A_Look,S_FIRED_LOOK3,0,0}, // S_FIRED_LOOK2 +{SPR_FDMN,32774,10,A_Look,S_FIRED_LOOK1,0,0}, // S_FIRED_LOOK3 +{SPR_FDMN,32772,8,NULL,S_FIRED_LOOK5,0,0}, // S_FIRED_LOOK4 +{SPR_FDMN,32773,6,NULL,S_FIRED_LOOK6,0,0}, // S_FIRED_LOOK5 +{SPR_FDMN,32774,5,NULL,S_FIRED_LOOK7,0,0}, // S_FIRED_LOOK6 +{SPR_FDMN,32773,8,NULL,S_FIRED_LOOK8,0,0}, // S_FIRED_LOOK7 +{SPR_FDMN,32772,6,NULL,S_FIRED_LOOK9,0,0}, // S_FIRED_LOOK8 +{SPR_FDMN,32773,7,A_FiredRocks,S_FIRED_LOOK0,0,0}, // S_FIRED_LOOK9 +{SPR_FDMN,32775,5,NULL,S_FIRED_LOOKA,0,0}, // S_FIRED_LOOK0 +{SPR_FDMN,32776,5,NULL,S_FIRED_LOOKB,0,0}, // S_FIRED_LOOKA +{SPR_FDMN,32777,5,A_UnSetInvulnerable,S_FIRED_WALK1,0,0}, // S_FIRED_LOOKB +{SPR_FDMN,32768,5,A_FiredChase,S_FIRED_WALK2,0,0}, // S_FIRED_WALK1 +{SPR_FDMN,32769,5,A_FiredChase,S_FIRED_WALK3,0,0}, // S_FIRED_WALK2 +{SPR_FDMN,32770,5,A_FiredChase,S_FIRED_WALK1,0,0}, // S_FIRED_WALK3 +{SPR_FDMN,32771,6,A_Pain,S_FIRED_WALK1,0,0}, // S_FIRED_PAIN1 +{SPR_FDMN,32778,3,A_FaceTarget,S_FIRED_ATTACK2,0,0}, // S_FIRED_ATTACK1 +{SPR_FDMN,32778,5,A_FiredAttack,S_FIRED_ATTACK3,0,0}, // S_FIRED_ATTACK2 +{SPR_FDMN,32778,5,A_FiredAttack,S_FIRED_ATTACK4,0,0}, // S_FIRED_ATTACK3 +{SPR_FDMN,32778,5,A_FiredAttack,S_FIRED_WALK1,0,0}, // S_FIRED_ATTACK4 +{SPR_FDMN,32771,4,A_FaceTarget,S_FIRED_DEATH2,0,0}, // S_FIRED_DEATH1 +{SPR_FDMN,32779,4,A_Scream,S_FIRED_DEATH3,0,0}, // S_FIRED_DEATH2 +{SPR_FDMN,32779,4,A_NoBlocking,S_FIRED_DEATH4,0,0}, // S_FIRED_DEATH3 +{SPR_FDMN,32779,200,NULL,S_NULL,0,0}, // S_FIRED_DEATH4 +{SPR_FDMN,12,5,A_FaceTarget,S_FIRED_XDEATH2,0,0}, // S_FIRED_XDEATH1 +{SPR_FDMN,13,5,A_NoBlocking,S_FIRED_XDEATH3,0,0}, // S_FIRED_XDEATH2 +{SPR_FDMN,14,5,A_FiredSplotch,S_NULL,0,0}, // S_FIRED_XDEATH3 +{SPR_FDMN,17,5,A_FreezeDeath,S_FIRED_ICE2,0,0}, // S_FIRED_ICE1 +{SPR_FDMN,17,1,A_FreezeDeathChunks,S_FIRED_ICE2,0,0}, // S_FIRED_ICE2 +{SPR_FDMN,15,3,NULL,S_FIRED_CORPSE2,0,0}, // S_FIRED_CORPSE1 +{SPR_FDMN,15,6,A_QueueCorpse,S_FIRED_CORPSE3,0,0}, // S_FIRED_CORPSE2 +{SPR_FDMN,24,-1,NULL,S_NULL,0,0}, // S_FIRED_CORPSE3 +{SPR_FDMN,16,3,NULL,S_FIRED_CORPSE5,0,0}, // S_FIRED_CORPSE4 +{SPR_FDMN,16,6,A_QueueCorpse,S_FIRED_CORPSE6,0,0}, // S_FIRED_CORPSE5 +{SPR_FDMN,25,-1,NULL,S_NULL,0,0}, // S_FIRED_CORPSE6 +{SPR_FDMN,18,4,NULL,S_FIRED_RDROP1,0,0}, // S_FIRED_RDROP1 +{SPR_FDMN,18,5,A_SmBounce,S_FIRED_RDEAD1_2,0,0}, // S_FIRED_RDEAD1_1 +{SPR_FDMN,18,200,NULL,S_NULL,0,0}, // S_FIRED_RDEAD1_2 +{SPR_FDMN,19,4,NULL,S_FIRED_RDROP2,0,0}, // S_FIRED_RDROP2 +{SPR_FDMN,19,5,A_SmBounce,S_FIRED_RDEAD2_2,0,0}, // S_FIRED_RDEAD2_1 +{SPR_FDMN,19,200,NULL,S_NULL,0,0}, // S_FIRED_RDEAD2_2 +{SPR_FDMN,20,4,NULL,S_FIRED_RDROP3,0,0}, // S_FIRED_RDROP3 +{SPR_FDMN,20,5,A_SmBounce,S_FIRED_RDEAD3_2,0,0}, // S_FIRED_RDEAD3_1 +{SPR_FDMN,20,200,NULL,S_NULL,0,0}, // S_FIRED_RDEAD3_2 +{SPR_FDMN,21,4,NULL,S_FIRED_RDROP4,0,0}, // S_FIRED_RDROP4 +{SPR_FDMN,21,5,A_SmBounce,S_FIRED_RDEAD4_2,0,0}, // S_FIRED_RDEAD4_1 +{SPR_FDMN,21,200,NULL,S_NULL,0,0}, // S_FIRED_RDEAD4_2 +{SPR_FDMN,22,4,NULL,S_FIRED_RDROP5,0,0}, // S_FIRED_RDROP5 +{SPR_FDMN,22,5,A_SmBounce,S_FIRED_RDEAD5_2,0,0}, // S_FIRED_RDEAD5_1 +{SPR_FDMN,22,200,NULL,S_NULL,0,0}, // S_FIRED_RDEAD5_2 +{SPR_FDMB,32768,5,NULL,S_FIRED_FX6_1,0,0}, // S_FIRED_FX6_1 +{SPR_FDMB,32769,5,NULL,S_FIRED_FX6_3,0,0}, // S_FIRED_FX6_2 +{SPR_FDMB,32770,5,NULL,S_FIRED_FX6_4,0,0}, // S_FIRED_FX6_3 +{SPR_FDMB,32771,5,NULL,S_FIRED_FX6_5,0,0}, // S_FIRED_FX6_4 +{SPR_FDMB,32772,5,NULL,S_NULL,0,0}, // S_FIRED_FX6_5 +{SPR_ICEY,0,10,A_IceGuyLook,S_ICEGUY_LOOK,0,0}, // S_ICEGUY_LOOK +{SPR_ICEY,0,-1,NULL,S_ICEGUY_LOOK,0,0}, // S_ICEGUY_DORMANT +{SPR_ICEY,0,4,A_Chase,S_ICEGUY_WALK2,0,0}, // S_ICEGUY_WALK1 +{SPR_ICEY,1,4,A_IceGuyChase,S_ICEGUY_WALK3,0,0}, // S_ICEGUY_WALK2 +{SPR_ICEY,2,4,A_Chase,S_ICEGUY_WALK4,0,0}, // S_ICEGUY_WALK3 +{SPR_ICEY,3,4,A_Chase,S_ICEGUY_WALK1,0,0}, // S_ICEGUY_WALK4 +{SPR_ICEY,4,3,A_FaceTarget,S_ICEGUY_ATK2,0,0}, // S_ICEGUY_ATK1 +{SPR_ICEY,5,3,A_FaceTarget,S_ICEGUY_ATK3,0,0}, // S_ICEGUY_ATK2 +{SPR_ICEY,32774,8,A_IceGuyAttack,S_ICEGUY_ATK4,0,0}, // S_ICEGUY_ATK3 +{SPR_ICEY,5,4,A_FaceTarget,S_ICEGUY_WALK1,0,0}, // S_ICEGUY_ATK4 +{SPR_ICEY,0,1,A_Pain,S_ICEGUY_WALK1,0,0}, // S_ICEGUY_PAIN1 +{SPR_ICEY,0,1,A_IceGuyDie,S_NULL,0,0}, // S_ICEGUY_DEATH +{SPR_ICPR,32768,3,A_IceGuyMissilePuff,S_ICEGUY_FX2,0,0}, // S_ICEGUY_FX1 +{SPR_ICPR,32769,3,A_IceGuyMissilePuff,S_ICEGUY_FX3,0,0}, // S_ICEGUY_FX2 +{SPR_ICPR,32770,3,A_IceGuyMissilePuff,S_ICEGUY_FX1,0,0}, // S_ICEGUY_FX3 +{SPR_ICPR,32771,4,NULL,S_ICEGUY_FX_X2,0,0}, // S_ICEGUY_FX_X1 +{SPR_ICPR,32772,4,A_IceGuyMissileExplode,S_ICEGUY_FX_X3,0,0}, // S_ICEGUY_FX_X2 +{SPR_ICPR,32773,4,NULL,S_ICEGUY_FX_X4,0,0}, // S_ICEGUY_FX_X3 +{SPR_ICPR,32774,4,NULL,S_ICEGUY_FX_X5,0,0}, // S_ICEGUY_FX_X4 +{SPR_ICPR,32775,3,NULL,S_NULL,0,0}, // S_ICEGUY_FX_X5 +{SPR_ICPR,8,3,NULL,S_ICEFX_PUFF2,0,0}, // S_ICEFX_PUFF1 +{SPR_ICPR,9,3,NULL,S_ICEFX_PUFF3,0,0}, // S_ICEFX_PUFF2 +{SPR_ICPR,10,3,NULL,S_ICEFX_PUFF4,0,0}, // S_ICEFX_PUFF3 +{SPR_ICPR,11,2,NULL,S_ICEFX_PUFF5,0,0}, // S_ICEFX_PUFF4 +{SPR_ICPR,12,2,NULL,S_NULL,0,0}, // S_ICEFX_PUFF5 +{SPR_ICPR,32781,3,NULL,S_ICEGUY_FX2_2,0,0}, // S_ICEGUY_FX2_1 +{SPR_ICPR,32782,3,NULL,S_ICEGUY_FX2_3,0,0}, // S_ICEGUY_FX2_2 +{SPR_ICPR,32783,3,NULL,S_ICEGUY_FX2_1,0,0}, // S_ICEGUY_FX2_3 +{SPR_ICPR,32784,50,NULL,S_NULL,0,0}, // S_ICEGUY_BIT1 +{SPR_ICPR,32785,50,NULL,S_NULL,0,0}, // S_ICEGUY_BIT2 +{SPR_ICWS,0,2,NULL,S_ICEGUY_WISP1_2,0,0}, // S_ICEGUY_WISP1_1 +{SPR_ICWS,1,2,NULL,S_ICEGUY_WISP1_3,0,0}, // S_ICEGUY_WISP1_2 +{SPR_ICWS,2,2,NULL,S_ICEGUY_WISP1_4,0,0}, // S_ICEGUY_WISP1_3 +{SPR_ICWS,3,2,NULL,S_ICEGUY_WISP1_5,0,0}, // S_ICEGUY_WISP1_4 +{SPR_ICWS,4,2,NULL,S_ICEGUY_WISP1_6,0,0}, // S_ICEGUY_WISP1_5 +{SPR_ICWS,5,2,NULL,S_ICEGUY_WISP1_7,0,0}, // S_ICEGUY_WISP1_6 +{SPR_ICWS,6,2,NULL,S_ICEGUY_WISP1_8,0,0}, // S_ICEGUY_WISP1_7 +{SPR_ICWS,7,2,NULL,S_ICEGUY_WISP1_9,0,0}, // S_ICEGUY_WISP1_8 +{SPR_ICWS,8,2,NULL,S_NULL,0,0}, // S_ICEGUY_WISP1_9 +{SPR_ICWS,9,2,NULL,S_ICEGUY_WISP2_2,0,0}, // S_ICEGUY_WISP2_1 +{SPR_ICWS,10,2,NULL,S_ICEGUY_WISP2_3,0,0}, // S_ICEGUY_WISP2_2 +{SPR_ICWS,11,2,NULL,S_ICEGUY_WISP2_4,0,0}, // S_ICEGUY_WISP2_3 +{SPR_ICWS,12,2,NULL,S_ICEGUY_WISP2_5,0,0}, // S_ICEGUY_WISP2_4 +{SPR_ICWS,13,2,NULL,S_ICEGUY_WISP2_6,0,0}, // S_ICEGUY_WISP2_5 +{SPR_ICWS,14,2,NULL,S_ICEGUY_WISP2_7,0,0}, // S_ICEGUY_WISP2_6 +{SPR_ICWS,15,2,NULL,S_ICEGUY_WISP2_8,0,0}, // S_ICEGUY_WISP2_7 +{SPR_ICWS,16,2,NULL,S_ICEGUY_WISP2_9,0,0}, // S_ICEGUY_WISP2_8 +{SPR_ICWS,17,2,NULL,S_NULL,0,0}, // S_ICEGUY_WISP2_9 +{SPR_PLAY,0,2,NULL,S_FIGHTER2,0,0}, // S_FIGHTER +{SPR_PLAY,0,3,A_ClassBossHealth,S_FIGHTERLOOK,0,0}, // S_FIGHTER2 +{SPR_PLAY,0,5,A_Look,S_FIGHTERLOOK,0,0}, // S_FIGHTERLOOK +{SPR_PLAY,0,4,A_FastChase,S_FIGHTER_RUN2,0,0}, // S_FIGHTER_RUN1 +{SPR_PLAY,1,4,A_FastChase,S_FIGHTER_RUN3,0,0}, // S_FIGHTER_RUN2 +{SPR_PLAY,2,4,A_FastChase,S_FIGHTER_RUN4,0,0}, // S_FIGHTER_RUN3 +{SPR_PLAY,3,4,A_FastChase,S_FIGHTER_RUN1,0,0}, // S_FIGHTER_RUN4 +{SPR_PLAY,4,8,A_FaceTarget,S_FIGHTER_ATK2,0,0}, // S_FIGHTER_ATK1 +{SPR_PLAY,5,8,A_FighterAttack,S_FIGHTER_RUN1,0,0}, // S_FIGHTER_ATK2 +{SPR_PLAY,6,4,NULL,S_FIGHTER_PAIN2,0,0}, // S_FIGHTER_PAIN +{SPR_PLAY,6,4,A_Pain,S_FIGHTER_RUN1,0,0}, // S_FIGHTER_PAIN2 +{SPR_PLAY,7,6,NULL,S_FIGHTER_DIE2,0,0}, // S_FIGHTER_DIE1 +{SPR_PLAY,8,6,A_Scream,S_FIGHTER_DIE3,0,0}, // S_FIGHTER_DIE2 +{SPR_PLAY,9,6,NULL,S_FIGHTER_DIE4,0,0}, // S_FIGHTER_DIE3 +{SPR_PLAY,10,6,NULL,S_FIGHTER_DIE5,0,0}, // S_FIGHTER_DIE4 +{SPR_PLAY,11,6,A_NoBlocking,S_FIGHTER_DIE6,0,0}, // S_FIGHTER_DIE5 +{SPR_PLAY,12,6,NULL,S_FIGHTER_DIE7,0,0}, // S_FIGHTER_DIE6 +{SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_FIGHTER_DIE7 +{SPR_PLAY,14,5,A_Scream,S_FIGHTER_XDIE2,0,0}, // S_FIGHTER_XDIE1 +{SPR_PLAY,15,5,A_SkullPop,S_FIGHTER_XDIE3,0,0}, // S_FIGHTER_XDIE2 +{SPR_PLAY,17,5,A_NoBlocking,S_FIGHTER_XDIE4,0,0}, // S_FIGHTER_XDIE3 +{SPR_PLAY,18,5,NULL,S_FIGHTER_XDIE5,0,0}, // S_FIGHTER_XDIE4 +{SPR_PLAY,19,5,NULL,S_FIGHTER_XDIE6,0,0}, // S_FIGHTER_XDIE5 +{SPR_PLAY,20,5,NULL,S_FIGHTER_XDIE7,0,0}, // S_FIGHTER_XDIE6 +{SPR_PLAY,21,5,NULL,S_FIGHTER_XDIE8,0,0}, // S_FIGHTER_XDIE7 +{SPR_PLAY,22,-1,NULL,S_NULL,0,0}, // S_FIGHTER_XDIE8 +{SPR_PLAY,23,5,A_FreezeDeath,S_FIGHTER_ICE2,0,0}, // S_FIGHTER_ICE +{SPR_PLAY,23,1,A_FreezeDeathChunks,S_FIGHTER_ICE2,0,0}, // S_FIGHTER_ICE2 +{SPR_CLER,0,2,NULL,S_CLERIC2,0,0}, // S_CLERIC +{SPR_CLER,0,3,A_ClassBossHealth,S_CLERICLOOK,0,0}, // S_CLERIC2 +{SPR_CLER,0,5,A_Look,S_CLERICLOOK,0,0}, // S_CLERICLOOK +{SPR_CLER,0,4,A_FastChase,S_CLERIC_RUN2,0,0}, // S_CLERIC_RUN1 +{SPR_CLER,1,4,A_FastChase,S_CLERIC_RUN3,0,0}, // S_CLERIC_RUN2 +{SPR_CLER,2,4,A_FastChase,S_CLERIC_RUN4,0,0}, // S_CLERIC_RUN3 +{SPR_CLER,3,4,A_FastChase,S_CLERIC_RUN1,0,0}, // S_CLERIC_RUN4 +{SPR_CLER,4,8,A_FaceTarget,S_CLERIC_ATK2,0,0}, // S_CLERIC_ATK1 +{SPR_CLER,5,8,A_FaceTarget,S_CLERIC_ATK3,0,0}, // S_CLERIC_ATK2 +{SPR_CLER,6,10,A_ClericAttack,S_CLERIC_RUN1,0,0}, // S_CLERIC_ATK3 +{SPR_CLER,7,4,NULL,S_CLERIC_PAIN2,0,0}, // S_CLERIC_PAIN +{SPR_CLER,7,4,A_Pain,S_CLERIC_RUN1,0,0}, // S_CLERIC_PAIN2 +{SPR_CLER,8,6,NULL,S_CLERIC_DIE2,0,0}, // S_CLERIC_DIE1 +{SPR_CLER,10,6,A_Scream,S_CLERIC_DIE3,0,0}, // S_CLERIC_DIE2 +{SPR_CLER,11,6,NULL,S_CLERIC_DIE4,0,0}, // S_CLERIC_DIE3 +{SPR_CLER,11,6,NULL,S_CLERIC_DIE5,0,0}, // S_CLERIC_DIE4 +{SPR_CLER,12,6,A_NoBlocking,S_CLERIC_DIE6,0,0}, // S_CLERIC_DIE5 +{SPR_CLER,13,6,NULL,S_CLERIC_DIE7,0,0}, // S_CLERIC_DIE6 +{SPR_CLER,14,6,NULL,S_CLERIC_DIE8,0,0}, // S_CLERIC_DIE7 +{SPR_CLER,15,6,NULL,S_CLERIC_DIE9,0,0}, // S_CLERIC_DIE8 +{SPR_CLER,16,-1,NULL,S_NULL,0,0}, // S_CLERIC_DIE9 +{SPR_CLER,17,5,A_Scream,S_CLERIC_XDIE2,0,0}, // S_CLERIC_XDIE1 +{SPR_CLER,18,5,NULL,S_CLERIC_XDIE3,0,0}, // S_CLERIC_XDIE2 +{SPR_CLER,19,5,A_NoBlocking,S_CLERIC_XDIE4,0,0}, // S_CLERIC_XDIE3 +{SPR_CLER,20,5,NULL,S_CLERIC_XDIE5,0,0}, // S_CLERIC_XDIE4 +{SPR_CLER,21,5,NULL,S_CLERIC_XDIE6,0,0}, // S_CLERIC_XDIE5 +{SPR_CLER,22,5,NULL,S_CLERIC_XDIE7,0,0}, // S_CLERIC_XDIE6 +{SPR_CLER,23,5,NULL,S_CLERIC_XDIE8,0,0}, // S_CLERIC_XDIE7 +{SPR_CLER,24,5,NULL,S_CLERIC_XDIE9,0,0}, // S_CLERIC_XDIE8 +{SPR_CLER,25,5,NULL,S_CLERIC_XDIE10,0,0}, // S_CLERIC_XDIE9 +{SPR_CLER,26,-1,NULL,S_NULL,0,0}, // S_CLERIC_XDIE10 +{SPR_CLER,27,5,A_FreezeDeath,S_CLERIC_ICE2,0,0}, // S_CLERIC_ICE +{SPR_CLER,27,1,A_FreezeDeathChunks,S_CLERIC_ICE2,0,0}, // S_CLERIC_ICE2 +{SPR_MAGE,0,2,NULL,S_MAGE2,0,0}, // S_MAGE +{SPR_MAGE,0,3,A_ClassBossHealth,S_MAGELOOK,0,0}, // S_MAGE2 +{SPR_MAGE,0,5,A_Look,S_MAGELOOK,0,0}, // S_MAGELOOK +{SPR_MAGE,0,4,A_FastChase,S_MAGE_RUN2,0,0}, // S_MAGE_RUN1 +{SPR_MAGE,1,4,A_FastChase,S_MAGE_RUN3,0,0}, // S_MAGE_RUN2 +{SPR_MAGE,2,4,A_FastChase,S_MAGE_RUN4,0,0}, // S_MAGE_RUN3 +{SPR_MAGE,3,4,A_FastChase,S_MAGE_RUN1,0,0}, // S_MAGE_RUN4 +{SPR_MAGE,4,8,A_FaceTarget,S_MAGE_ATK2,0,0}, // S_MAGE_ATK1 +{SPR_MAGE,32773,8,A_MageAttack,S_MAGE_RUN1,0,0}, // S_MAGE_ATK2 +{SPR_MAGE,6,4,NULL,S_MAGE_PAIN2,0,0}, // S_MAGE_PAIN +{SPR_MAGE,6,4,A_Pain,S_MAGE_RUN1,0,0}, // S_MAGE_PAIN2 +{SPR_MAGE,7,6,NULL,S_MAGE_DIE2,0,0}, // S_MAGE_DIE1 +{SPR_MAGE,8,6,A_Scream,S_MAGE_DIE3,0,0}, // S_MAGE_DIE2 +{SPR_MAGE,9,6,NULL,S_MAGE_DIE4,0,0}, // S_MAGE_DIE3 +{SPR_MAGE,10,6,NULL,S_MAGE_DIE5,0,0}, // S_MAGE_DIE4 +{SPR_MAGE,11,6,A_NoBlocking,S_MAGE_DIE6,0,0}, // S_MAGE_DIE5 +{SPR_MAGE,12,6,NULL,S_MAGE_DIE7,0,0}, // S_MAGE_DIE6 +{SPR_MAGE,13,-1,NULL,S_NULL,0,0}, // S_MAGE_DIE7 +{SPR_MAGE,14,5,A_Scream,S_MAGE_XDIE2,0,0}, // S_MAGE_XDIE1 +{SPR_MAGE,15,5,NULL,S_MAGE_XDIE3,0,0}, // S_MAGE_XDIE2 +{SPR_MAGE,17,5,A_NoBlocking,S_MAGE_XDIE4,0,0}, // S_MAGE_XDIE3 +{SPR_MAGE,18,5,NULL,S_MAGE_XDIE5,0,0}, // S_MAGE_XDIE4 +{SPR_MAGE,19,5,NULL,S_MAGE_XDIE6,0,0}, // S_MAGE_XDIE5 +{SPR_MAGE,20,5,NULL,S_MAGE_XDIE7,0,0}, // S_MAGE_XDIE6 +{SPR_MAGE,21,5,NULL,S_MAGE_XDIE8,0,0}, // S_MAGE_XDIE7 +{SPR_MAGE,22,5,NULL,S_MAGE_XDIE9,0,0}, // S_MAGE_XDIE8 +{SPR_MAGE,23,-1,NULL,S_NULL,0,0}, // S_MAGE_XDIE9 +{SPR_MAGE,24,5,A_FreezeDeath,S_MAGE_ICE2,0,0}, // S_MAGE_ICE +{SPR_MAGE,24,1,A_FreezeDeathChunks,S_MAGE_ICE2,0,0}, // S_MAGE_ICE2 +{SPR_SORC,0,3,NULL,S_SORC_SPAWN2,0,0}, // S_SORC_SPAWN1 +{SPR_SORC,0,2,A_SorcSpinBalls,S_SORC_LOOK1,0,0}, // S_SORC_SPAWN2 +{SPR_SORC,0,10,A_Look,S_SORC_LOOK1,0,0}, // S_SORC_LOOK1 +{SPR_SORC,0,5,A_Chase,S_SORC_WALK2,0,0}, // S_SORC_WALK1 +{SPR_SORC,1,5,A_Chase,S_SORC_WALK3,0,0}, // S_SORC_WALK2 +{SPR_SORC,2,5,A_Chase,S_SORC_WALK4,0,0}, // S_SORC_WALK3 +{SPR_SORC,3,5,A_Chase,S_SORC_WALK1,0,0}, // S_SORC_WALK4 +{SPR_SORC,6,8,NULL,S_SORC_PAIN2,0,0}, // S_SORC_PAIN1 +{SPR_SORC,6,8,A_Pain,S_SORC_WALK1,0,0}, // S_SORC_PAIN2 +{SPR_SORC,32773,6,A_FaceTarget,S_SORC_ATK2_2,0,0}, // S_SORC_ATK2_1 +{SPR_SORC,32773,6,A_SpeedBalls,S_SORC_ATK2_3,0,0}, // S_SORC_ATK2_2 +{SPR_SORC,32773,6,A_FaceTarget,S_SORC_ATK2_3,0,0}, // S_SORC_ATK2_3 +{SPR_SORC,32772,6,NULL,S_SORC_ATTACK2,0,0}, // S_SORC_ATTACK1 +{SPR_SORC,32772,6,A_SpawnFizzle,S_SORC_ATTACK3,0,0}, // S_SORC_ATTACK2 +{SPR_SORC,32772,5,A_FaceTarget,S_SORC_ATTACK2,0,0}, // S_SORC_ATTACK3 +{SPR_SORC,32772,2,NULL,S_SORC_ATTACK5,0,0}, // S_SORC_ATTACK4 +{SPR_SORC,32772,2,A_SorcBossAttack,S_SORC_WALK1,0,0}, // S_SORC_ATTACK5 +{SPR_SORC,32775,5,NULL,S_SORC_DIE2,0,0}, // S_SORC_DIE1 +{SPR_SORC,32776,5,A_FaceTarget,S_SORC_DIE3,0,0}, // S_SORC_DIE2 +{SPR_SORC,32777,5,A_Scream,S_SORC_DIE4,0,0}, // S_SORC_DIE3 +{SPR_SORC,32778,5,NULL,S_SORC_DIE5,0,0}, // S_SORC_DIE4 +{SPR_SORC,32779,5,NULL,S_SORC_DIE6,0,0}, // S_SORC_DIE5 +{SPR_SORC,32780,5,NULL,S_SORC_DIE7,0,0}, // S_SORC_DIE6 +{SPR_SORC,32781,5,NULL,S_SORC_DIE8,0,0}, // S_SORC_DIE7 +{SPR_SORC,32782,5,NULL,S_SORC_DIE9,0,0}, // S_SORC_DIE8 +{SPR_SORC,32783,5,NULL,S_SORC_DIE0,0,0}, // S_SORC_DIE9 +{SPR_SORC,32784,5,NULL,S_SORC_DIEA,0,0}, // S_SORC_DIE0 +{SPR_SORC,32785,5,NULL,S_SORC_DIEB,0,0}, // S_SORC_DIEA +{SPR_SORC,32786,5,NULL,S_SORC_DIEC,0,0}, // S_SORC_DIEB +{SPR_SORC,32787,5,NULL,S_SORC_DIED,0,0}, // S_SORC_DIEC +{SPR_SORC,32788,5,A_NoBlocking,S_SORC_DIEE,0,0}, // S_SORC_DIED +{SPR_SORC,32789,5,NULL,S_SORC_DIEF,0,0}, // S_SORC_DIEE +{SPR_SORC,32790,5,NULL,S_SORC_DIEG,0,0}, // S_SORC_DIEF +{SPR_SORC,32791,5,NULL,S_SORC_DIEH,0,0}, // S_SORC_DIEG +{SPR_SORC,32792,5,NULL,S_SORC_DIEI,0,0}, // S_SORC_DIEH +{SPR_SORC,32793,-1,NULL,S_NULL,0,0}, // S_SORC_DIEI +{SPR_SBMP,0,2,A_SorcBallOrbit,S_SORCBALL1_2,0,0}, // S_SORCBALL1_1 +{SPR_SBMP,1,2,A_SorcBallOrbit,S_SORCBALL1_3,0,0}, // S_SORCBALL1_2 +{SPR_SBMP,2,2,A_SorcBallOrbit,S_SORCBALL1_4,0,0}, // S_SORCBALL1_3 +{SPR_SBMP,3,2,A_SorcBallOrbit,S_SORCBALL1_5,0,0}, // S_SORCBALL1_4 +{SPR_SBMP,4,2,A_SorcBallOrbit,S_SORCBALL1_6,0,0}, // S_SORCBALL1_5 +{SPR_SBMP,5,2,A_SorcBallOrbit,S_SORCBALL1_7,0,0}, // S_SORCBALL1_6 +{SPR_SBMP,6,2,A_SorcBallOrbit,S_SORCBALL1_8,0,0}, // S_SORCBALL1_7 +{SPR_SBMP,7,2,A_SorcBallOrbit,S_SORCBALL1_9,0,0}, // S_SORCBALL1_8 +{SPR_SBMP,8,2,A_SorcBallOrbit,S_SORCBALL1_0,0,0}, // S_SORCBALL1_9 +{SPR_SBMP,9,2,A_SorcBallOrbit,S_SORCBALL1_A,0,0}, // S_SORCBALL1_0 +{SPR_SBMP,10,2,A_SorcBallOrbit,S_SORCBALL1_B,0,0}, // S_SORCBALL1_A +{SPR_SBMP,11,2,A_SorcBallOrbit,S_SORCBALL1_C,0,0}, // S_SORCBALL1_B +{SPR_SBMP,12,2,A_SorcBallOrbit,S_SORCBALL1_D,0,0}, // S_SORCBALL1_C +{SPR_SBMP,13,2,A_SorcBallOrbit,S_SORCBALL1_E,0,0}, // S_SORCBALL1_D +{SPR_SBMP,14,2,A_SorcBallOrbit,S_SORCBALL1_F,0,0}, // S_SORCBALL1_E +{SPR_SBMP,15,2,A_SorcBallOrbit,S_SORCBALL1_1,0,0}, // S_SORCBALL1_F +{SPR_SBMP,0,5,A_SorcBallPop,S_SORCBALL1_D2,0,0}, // S_SORCBALL1_D1 +{SPR_SBMP,1,2,A_BounceCheck,S_SORCBALL1_D2,0,0}, // S_SORCBALL1_D2 +{SPR_SBS4,3,5,A_Explode,S_SORCBALL1_D6,0,0}, // S_SORCBALL1_D5 +{SPR_SBS4,4,5,NULL,S_SORCBALL1_D7,0,0}, // S_SORCBALL1_D6 +{SPR_SBS4,5,6,NULL,S_SORCBALL1_D8,0,0}, // S_SORCBALL1_D7 +{SPR_SBS4,6,6,NULL,S_SORCBALL1_D9,0,0}, // S_SORCBALL1_D8 +{SPR_SBS4,7,6,NULL,S_NULL,0,0}, // S_SORCBALL1_D9 +{SPR_SBMB,0,2,A_SorcBallOrbit,S_SORCBALL2_2,0,0}, // S_SORCBALL2_1 +{SPR_SBMB,1,2,A_SorcBallOrbit,S_SORCBALL2_3,0,0}, // S_SORCBALL2_2 +{SPR_SBMB,2,2,A_SorcBallOrbit,S_SORCBALL2_4,0,0}, // S_SORCBALL2_3 +{SPR_SBMB,3,2,A_SorcBallOrbit,S_SORCBALL2_5,0,0}, // S_SORCBALL2_4 +{SPR_SBMB,4,2,A_SorcBallOrbit,S_SORCBALL2_6,0,0}, // S_SORCBALL2_5 +{SPR_SBMB,5,2,A_SorcBallOrbit,S_SORCBALL2_7,0,0}, // S_SORCBALL2_6 +{SPR_SBMB,6,2,A_SorcBallOrbit,S_SORCBALL2_8,0,0}, // S_SORCBALL2_7 +{SPR_SBMB,7,2,A_SorcBallOrbit,S_SORCBALL2_9,0,0}, // S_SORCBALL2_8 +{SPR_SBMB,8,2,A_SorcBallOrbit,S_SORCBALL2_0,0,0}, // S_SORCBALL2_9 +{SPR_SBMB,9,2,A_SorcBallOrbit,S_SORCBALL2_A,0,0}, // S_SORCBALL2_0 +{SPR_SBMB,10,2,A_SorcBallOrbit,S_SORCBALL2_B,0,0}, // S_SORCBALL2_A +{SPR_SBMB,11,2,A_SorcBallOrbit,S_SORCBALL2_C,0,0}, // S_SORCBALL2_B +{SPR_SBMB,12,2,A_SorcBallOrbit,S_SORCBALL2_D,0,0}, // S_SORCBALL2_C +{SPR_SBMB,13,2,A_SorcBallOrbit,S_SORCBALL2_E,0,0}, // S_SORCBALL2_D +{SPR_SBMB,14,2,A_SorcBallOrbit,S_SORCBALL2_F,0,0}, // S_SORCBALL2_E +{SPR_SBMB,15,2,A_SorcBallOrbit,S_SORCBALL2_1,0,0}, // S_SORCBALL2_F +{SPR_SBMB,0,5,A_SorcBallPop,S_SORCBALL2_D2,0,0}, // S_SORCBALL2_D1 +{SPR_SBMB,1,2,A_BounceCheck,S_SORCBALL2_D2,0,0}, // S_SORCBALL2_D2 +{SPR_SBS3,3,5,A_Explode,S_SORCBALL2_D6,0,0}, // S_SORCBALL2_D5 +{SPR_SBS3,4,5,NULL,S_SORCBALL2_D7,0,0}, // S_SORCBALL2_D6 +{SPR_SBS3,5,6,NULL,S_SORCBALL2_D8,0,0}, // S_SORCBALL2_D7 +{SPR_SBS3,6,6,NULL,S_SORCBALL2_D9,0,0}, // S_SORCBALL2_D8 +{SPR_SBS3,7,6,NULL,S_NULL,0,0}, // S_SORCBALL2_D9 +{SPR_SBMG,0,2,A_SorcBallOrbit,S_SORCBALL3_2,0,0}, // S_SORCBALL3_1 +{SPR_SBMG,1,2,A_SorcBallOrbit,S_SORCBALL3_3,0,0}, // S_SORCBALL3_2 +{SPR_SBMG,2,2,A_SorcBallOrbit,S_SORCBALL3_4,0,0}, // S_SORCBALL3_3 +{SPR_SBMG,3,2,A_SorcBallOrbit,S_SORCBALL3_5,0,0}, // S_SORCBALL3_4 +{SPR_SBMG,4,2,A_SorcBallOrbit,S_SORCBALL3_6,0,0}, // S_SORCBALL3_5 +{SPR_SBMG,5,2,A_SorcBallOrbit,S_SORCBALL3_7,0,0}, // S_SORCBALL3_6 +{SPR_SBMG,6,2,A_SorcBallOrbit,S_SORCBALL3_8,0,0}, // S_SORCBALL3_7 +{SPR_SBMG,7,2,A_SorcBallOrbit,S_SORCBALL3_9,0,0}, // S_SORCBALL3_8 +{SPR_SBMG,8,2,A_SorcBallOrbit,S_SORCBALL3_0,0,0}, // S_SORCBALL3_9 +{SPR_SBMG,9,2,A_SorcBallOrbit,S_SORCBALL3_A,0,0}, // S_SORCBALL3_0 +{SPR_SBMG,10,2,A_SorcBallOrbit,S_SORCBALL3_B,0,0}, // S_SORCBALL3_A +{SPR_SBMG,11,2,A_SorcBallOrbit,S_SORCBALL3_C,0,0}, // S_SORCBALL3_B +{SPR_SBMG,12,2,A_SorcBallOrbit,S_SORCBALL3_D,0,0}, // S_SORCBALL3_C +{SPR_SBMG,13,2,A_SorcBallOrbit,S_SORCBALL3_E,0,0}, // S_SORCBALL3_D +{SPR_SBMG,14,2,A_SorcBallOrbit,S_SORCBALL3_F,0,0}, // S_SORCBALL3_E +{SPR_SBMG,15,2,A_SorcBallOrbit,S_SORCBALL3_1,0,0}, // S_SORCBALL3_F +{SPR_SBMG,0,5,A_SorcBallPop,S_SORCBALL3_D2,0,0}, // S_SORCBALL3_D1 +{SPR_SBMG,1,2,A_BounceCheck,S_SORCBALL3_D2,0,0}, // S_SORCBALL3_D2 +{SPR_SBS3,3,5,A_Explode,S_SORCBALL3_D6,0,0}, // S_SORCBALL3_D5 +{SPR_SBS3,4,5,NULL,S_SORCBALL3_D7,0,0}, // S_SORCBALL3_D6 +{SPR_SBS3,5,6,NULL,S_SORCBALL3_D8,0,0}, // S_SORCBALL3_D7 +{SPR_SBS3,6,6,NULL,S_SORCBALL3_D9,0,0}, // S_SORCBALL3_D8 +{SPR_SBS3,7,6,NULL,S_NULL,0,0}, // S_SORCBALL3_D9 +{SPR_SBS1,32768,2,NULL,S_SORCFX1_2,0,0}, // S_SORCFX1_1 +{SPR_SBS1,32769,3,A_SorcFX1Seek,S_SORCFX1_3,0,0}, // S_SORCFX1_2 +{SPR_SBS1,32770,3,A_SorcFX1Seek,S_SORCFX1_4,0,0}, // S_SORCFX1_3 +{SPR_SBS1,32771,3,A_SorcFX1Seek,S_SORCFX1_1,0,0}, // S_SORCFX1_4 +{SPR_FHFX,32786,2,A_Explode,S_SORCFX1_D2,0,0}, // S_SORCFX1_D1 +{SPR_FHFX,32786,6,NULL,S_SORCFX1_D3,0,0}, // S_SORCFX1_D2 +{SPR_FHFX,32786,6,NULL,S_NULL,0,0}, // S_SORCFX1_D3 +{SPR_SBS2,32768,3,A_SorcFX2Split,S_SORCFX2_SPLIT1,0,0}, // S_SORCFX2_SPLIT1 +{SPR_SBS2,32768,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT2,0,0}, // S_SORCFX2_ORBIT1 +{SPR_SBS2,32769,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT3,0,0}, // S_SORCFX2_ORBIT2 +{SPR_SBS2,32770,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT4,0,0}, // S_SORCFX2_ORBIT3 +{SPR_SBS2,32771,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT5,0,0}, // S_SORCFX2_ORBIT4 +{SPR_SBS2,32772,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT6,0,0}, // S_SORCFX2_ORBIT5 +{SPR_SBS2,32773,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT7,0,0}, // S_SORCFX2_ORBIT6 +{SPR_SBS2,32774,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT8,0,0}, // S_SORCFX2_ORBIT7 +{SPR_SBS2,32775,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT9,0,0}, // S_SORCFX2_ORBIT8 +{SPR_SBS2,32776,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT0,0,0}, // S_SORCFX2_ORBIT9 +{SPR_SBS2,32777,2,A_SorcFX2Orbit,S_SORCFX2_ORBITA,0,0}, // S_SORCFX2_ORBIT0 +{SPR_SBS2,32778,2,A_SorcFX2Orbit,S_SORCFX2_ORBITB,0,0}, // S_SORCFX2_ORBITA +{SPR_SBS2,32779,2,A_SorcFX2Orbit,S_SORCFX2_ORBITC,0,0}, // S_SORCFX2_ORBITB +{SPR_SBS2,32780,2,A_SorcFX2Orbit,S_SORCFX2_ORBITD,0,0}, // S_SORCFX2_ORBITC +{SPR_SBS2,32781,2,A_SorcFX2Orbit,S_SORCFX2_ORBITE,0,0}, // S_SORCFX2_ORBITD +{SPR_SBS2,32782,2,A_SorcFX2Orbit,S_SORCFX2_ORBITF,0,0}, // S_SORCFX2_ORBITE +{SPR_SBS2,32783,2,A_SorcFX2Orbit,S_SORCFX2_ORBIT1,0,0}, // S_SORCFX2_ORBITF +{SPR_SBS2,0,10,NULL,S_NULL,0,0}, // S_SORCFX2T1 +{SPR_SBS3,32768,2,NULL,S_SORCFX3_2,0,0}, // S_SORCFX3_1 +{SPR_SBS3,32769,2,NULL,S_SORCFX3_3,0,0}, // S_SORCFX3_2 +{SPR_SBS3,32770,2,NULL,S_SORCFX3_1,0,0}, // S_SORCFX3_3 +{SPR_SBS3,32768,4,NULL,S_BISHMORPHA,0,0}, // S_BISHMORPH1 +{SPR_BISH,15,4,A_SorcererBishopEntry,S_BISHMORPHB,0,0}, // S_BISHMORPHA +{SPR_BISH,14,4,NULL,S_BISHMORPHC,0,0}, // S_BISHMORPHB +{SPR_BISH,13,4,NULL,S_BISHMORPHD,0,0}, // S_BISHMORPHC +{SPR_BISH,12,3,NULL,S_BISHMORPHE,0,0}, // S_BISHMORPHD +{SPR_BISH,11,3,NULL,S_BISHMORPHF,0,0}, // S_BISHMORPHE +{SPR_BISH,10,3,NULL,S_BISHMORPHG,0,0}, // S_BISHMORPHF +{SPR_BISH,9,3,NULL,S_BISHMORPHH,0,0}, // S_BISHMORPHG +{SPR_BISH,8,3,NULL,S_BISHMORPHI,0,0}, // S_BISHMORPHH +{SPR_BISH,7,3,NULL,S_BISHMORPHJ,0,0}, // S_BISHMORPHI +{SPR_BISH,6,3,A_SpawnBishop,S_NULL,0,0}, // S_BISHMORPHJ +{SPR_SBS3,3,3,NULL,S_SORCFX3_EXP2,0,0}, // S_SORCFX3_EXP1 +{SPR_SBS3,4,3,NULL,S_SORCFX3_EXP3,0,0}, // S_SORCFX3_EXP2 +{SPR_SBS3,5,3,NULL,S_SORCFX3_EXP4,0,0}, // S_SORCFX3_EXP3 +{SPR_SBS3,6,3,NULL,S_SORCFX3_EXP5,0,0}, // S_SORCFX3_EXP4 +{SPR_SBS3,7,3,NULL,S_NULL,0,0}, // S_SORCFX3_EXP5 +{SPR_SBS4,32768,2,A_SorcFX4Check,S_SORCFX4_2,0,0}, // S_SORCFX4_1 +{SPR_SBS4,32769,2,A_SorcFX4Check,S_SORCFX4_3,0,0}, // S_SORCFX4_2 +{SPR_SBS4,32770,2,A_SorcFX4Check,S_SORCFX4_1,0,0}, // S_SORCFX4_3 +{SPR_SBS4,32771,2,NULL,S_SORCFX4_D2,0,0}, // S_SORCFX4_D1 +{SPR_SBS4,32772,2,A_Explode,S_SORCFX4_D3,0,0}, // S_SORCFX4_D2 +{SPR_SBS4,32773,2,NULL,S_SORCFX4_D4,0,0}, // S_SORCFX4_D3 +{SPR_SBS4,32774,2,NULL,S_SORCFX4_D5,0,0}, // S_SORCFX4_D4 +{SPR_SBS4,32775,2,NULL,S_NULL,0,0}, // S_SORCFX4_D5 +{SPR_SBFX,32768,4,NULL,S_SORCSPARK2,0,0}, // S_SORCSPARK1 +{SPR_SBFX,32769,4,NULL,S_SORCSPARK3,0,0}, // S_SORCSPARK2 +{SPR_SBFX,32770,4,NULL,S_SORCSPARK4,0,0}, // S_SORCSPARK3 +{SPR_SBFX,32771,4,NULL,S_SORCSPARK5,0,0}, // S_SORCSPARK4 +{SPR_SBFX,32772,4,NULL,S_SORCSPARK6,0,0}, // S_SORCSPARK5 +{SPR_SBFX,32773,4,NULL,S_SORCSPARK7,0,0}, // S_SORCSPARK6 +{SPR_SBFX,32774,4,NULL,S_NULL,0,0}, // S_SORCSPARK7 +{SPR_RADE,0,4,NULL,S_BLASTEFFECT2,0,0}, // S_BLASTEFFECT1 +{SPR_RADE,1,4,NULL,S_BLASTEFFECT3,0,0}, // S_BLASTEFFECT2 +{SPR_RADE,2,4,NULL,S_BLASTEFFECT4,0,0}, // S_BLASTEFFECT3 +{SPR_RADE,3,4,NULL,S_BLASTEFFECT5,0,0}, // S_BLASTEFFECT4 +{SPR_RADE,4,4,NULL,S_BLASTEFFECT6,0,0}, // S_BLASTEFFECT5 +{SPR_RADE,5,4,NULL,S_BLASTEFFECT7,0,0}, // S_BLASTEFFECT6 +{SPR_RADE,6,4,NULL,S_BLASTEFFECT8,0,0}, // S_BLASTEFFECT7 +{SPR_RADE,7,4,NULL,S_BLASTEFFECT9,0,0}, // S_BLASTEFFECT8 +{SPR_RADE,8,4,NULL,S_NULL,0,0}, // S_BLASTEFFECT9 +{SPR_WATR,0,5,NULL,S_WATERDRIP1,0,0}, // S_WATERDRIP1 +{SPR_KORX,0,5,A_Look,S_KORAX_LOOK1,0,0}, // S_KORAX_LOOK1 +{SPR_KORX,0,3,A_KoraxStep2,S_KORAX_CHASE2,0,0}, // S_KORAX_CHASE1 +{SPR_KORX,0,3,A_KoraxChase,S_KORAX_CHASE3,0,0}, // S_KORAX_CHASE2 +{SPR_KORX,0,3,A_KoraxChase,S_KORAX_CHASE4,0,0}, // S_KORAX_CHASE3 +{SPR_KORX,0,3,A_KoraxChase,S_KORAX_CHASE5,0,0}, // S_KORAX_CHASE4 +{SPR_KORX,1,3,A_KoraxStep,S_KORAX_CHASE6,0,0}, // S_KORAX_CHASE5 +{SPR_KORX,1,3,A_KoraxChase,S_KORAX_CHASE7,0,0}, // S_KORAX_CHASE6 +{SPR_KORX,1,3,A_KoraxChase,S_KORAX_CHASE8,0,0}, // S_KORAX_CHASE7 +{SPR_KORX,1,3,A_KoraxChase,S_KORAX_CHASE9,0,0}, // S_KORAX_CHASE8 +{SPR_KORX,2,3,A_KoraxStep2,S_KORAX_CHASE0,0,0}, // S_KORAX_CHASE9 +{SPR_KORX,2,3,A_KoraxChase,S_KORAX_CHASEA,0,0}, // S_KORAX_CHASE0 +{SPR_KORX,2,3,A_KoraxChase,S_KORAX_CHASEB,0,0}, // S_KORAX_CHASEA +{SPR_KORX,2,3,A_KoraxChase,S_KORAX_CHASEC,0,0}, // S_KORAX_CHASEB +{SPR_KORX,3,3,A_KoraxStep,S_KORAX_CHASED,0,0}, // S_KORAX_CHASEC +{SPR_KORX,3,3,A_KoraxChase,S_KORAX_CHASEE,0,0}, // S_KORAX_CHASED +{SPR_KORX,3,3,A_KoraxChase,S_KORAX_CHASEF,0,0}, // S_KORAX_CHASEE +{SPR_KORX,3,3,A_KoraxChase,S_KORAX_CHASE1,0,0}, // S_KORAX_CHASEF +{SPR_KORX,7,5,A_Pain,S_KORAX_PAIN2,0,0}, // S_KORAX_PAIN1 +{SPR_KORX,7,5,NULL,S_KORAX_CHASE2,0,0}, // S_KORAX_PAIN2 +{SPR_KORX,32772,2,A_FaceTarget,S_KORAX_ATTACK2,0,0}, // S_KORAX_ATTACK1 +{SPR_KORX,32772,5,A_KoraxDecide,S_KORAX_ATTACK2,0,0}, // S_KORAX_ATTACK2 +{SPR_KORX,32772,4,A_FaceTarget,S_KORAX_MISSILE2,0,0}, // S_KORAX_MISSILE1 +{SPR_KORX,32773,8,A_KoraxMissile,S_KORAX_MISSILE3,0,0}, // S_KORAX_MISSILE2 +{SPR_KORX,32772,8,NULL,S_KORAX_CHASE2,0,0}, // S_KORAX_MISSILE3 +{SPR_KORX,32772,5,A_FaceTarget,S_KORAX_COMMAND2,0,0}, // S_KORAX_COMMAND1 +{SPR_KORX,32790,10,A_FaceTarget,S_KORAX_COMMAND3,0,0}, // S_KORAX_COMMAND2 +{SPR_KORX,32774,15,A_KoraxCommand,S_KORAX_COMMAND4,0,0}, // S_KORAX_COMMAND3 +{SPR_KORX,32790,10,NULL,S_KORAX_COMMAND5,0,0}, // S_KORAX_COMMAND4 +{SPR_KORX,32772,5,NULL,S_KORAX_CHASE2,0,0}, // S_KORAX_COMMAND5 +{SPR_KORX,8,5,NULL,S_KORAX_DEATH2,0,0}, // S_KORAX_DEATH1 +{SPR_KORX,9,5,A_FaceTarget,S_KORAX_DEATH3,0,0}, // S_KORAX_DEATH2 +{SPR_KORX,10,5,A_Scream,S_KORAX_DEATH4,0,0}, // S_KORAX_DEATH3 +{SPR_KORX,11,5,NULL,S_KORAX_DEATH5,0,0}, // S_KORAX_DEATH4 +{SPR_KORX,12,5,NULL,S_KORAX_DEATH6,0,0}, // S_KORAX_DEATH5 +{SPR_KORX,13,5,NULL,S_KORAX_DEATH7,0,0}, // S_KORAX_DEATH6 +{SPR_KORX,14,5,NULL,S_KORAX_DEATH8,0,0}, // S_KORAX_DEATH7 +{SPR_KORX,15,5,NULL,S_KORAX_DEATH9,0,0}, // S_KORAX_DEATH8 +{SPR_KORX,16,10,NULL,S_KORAX_DEATH0,0,0}, // S_KORAX_DEATH9 +{SPR_KORX,17,5,A_KoraxBonePop,S_KORAX_DEATHA,0,0}, // S_KORAX_DEATH0 +{SPR_KORX,18,5,A_NoBlocking,S_KORAX_DEATHB,0,0}, // S_KORAX_DEATHA +{SPR_KORX,19,5,NULL,S_KORAX_DEATHC,0,0}, // S_KORAX_DEATHB +{SPR_KORX,20,5,NULL,S_KORAX_DEATHD,0,0}, // S_KORAX_DEATHC +{SPR_KORX,21,-1,NULL,S_NULL,0,0}, // S_KORAX_DEATHD +{SPR_SPIR,0,5,A_KSpiritRoam,S_KSPIRIT_ROAM2,0,0}, // S_KSPIRIT_ROAM1 +{SPR_SPIR,1,5,A_KSpiritRoam,S_KSPIRIT_ROAM1,0,0}, // S_KSPIRIT_ROAM2 +{SPR_SPIR,3,5,NULL,S_KSPIRIT_DEATH2,0,0}, // S_KSPIRIT_DEATH1 +{SPR_SPIR,4,5,NULL,S_KSPIRIT_DEATH3,0,0}, // S_KSPIRIT_DEATH2 +{SPR_SPIR,5,5,NULL,S_KSPIRIT_DEATH4,0,0}, // S_KSPIRIT_DEATH3 +{SPR_SPIR,6,5,NULL,S_KSPIRIT_DEATH5,0,0}, // S_KSPIRIT_DEATH4 +{SPR_SPIR,7,5,NULL,S_KSPIRIT_DEATH6,0,0}, // S_KSPIRIT_DEATH5 +{SPR_SPIR,8,5,NULL,S_NULL,0,0}, // S_KSPIRIT_DEATH6 +{SPR_MLFX,32776,2,NULL,S_KBOLT2,0,0}, // S_KBOLT1 +{SPR_MLFX,32777,2,A_KBoltRaise,S_KBOLT3,0,0}, // S_KBOLT2 +{SPR_MLFX,32776,2,A_KBolt,S_KBOLT4,0,0}, // S_KBOLT3 +{SPR_MLFX,32777,2,A_KBolt,S_KBOLT5,0,0}, // S_KBOLT4 +{SPR_MLFX,32778,2,A_KBolt,S_KBOLT6,0,0}, // S_KBOLT5 +{SPR_MLFX,32779,2,A_KBolt,S_KBOLT7,0,0}, // S_KBOLT6 +{SPR_MLFX,32780,2,A_KBolt,S_KBOLT3,0,0}, // S_KBOLT7 +{SPR_MAN1,0,2,NULL,S_SPAWNBATS2,0,0}, // S_SPAWNBATS1 +{SPR_MAN1,0,2,A_BatSpawnInit,S_SPAWNBATS3,0,0}, // S_SPAWNBATS2 +{SPR_MAN1,0,2,A_BatSpawn,S_SPAWNBATS3,0,0}, // S_SPAWNBATS3 +{SPR_MAN1,0,-1,NULL,S_NULL,0,0}, // S_SPAWNBATS_OFF +{SPR_ABAT,0,2,A_BatMove,S_BAT2,0,0}, // S_BAT1 +{SPR_ABAT,1,2,A_BatMove,S_BAT3,0,0}, // S_BAT2 +{SPR_ABAT,2,2,A_BatMove,S_BAT1,0,0}, // S_BAT3 +{SPR_ABAT,0,2,NULL,S_NULL,0,0} // S_BAT_DEATH +}; + + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + +{ // MT_MAPSPOT +9001, // doomednum +S_MAPSPOT, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MAPSPOTGRAVITY +9013, // doomednum +S_MAPSPOT, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_DONTDRAW // flags2 + }, + +{ // MT_FIREBALL1 +-1, // doomednum +S_FIREBALL1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIREBALL1_X1, // deathstate +S_NULL, // xdeathstate +SFX_FIREBALL, // deathsound +2*FRACUNIT, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_ARROW +-1, // doomednum +S_ARROW_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ARROW_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +6*FRACUNIT, // speed +8*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DART +-1, // doomednum +S_DART_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DART_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +6*FRACUNIT, // speed +8*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_POISONDART +-1, // doomednum +S_POISONDART_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_POISONDART_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +6*FRACUNIT, // speed +8*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_RIPPERBALL +-1, // doomednum +S_RIPPERBALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_RIPPERBALL_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +6*FRACUNIT, // speed +8*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_RIP // flags2 + }, + +{ // MT_PROJECTILE_BLADE +-1, // doomednum +S_PRJ_BLADE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_PRJ_BLADE_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +6*FRACUNIT, // speed +6*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +3, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ICESHARD +-1, // doomednum +S_ICESHARD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SHARDFXE1_1, // deathstate +S_NULL, // xdeathstate +SFX_MAGE_SHARDS_EXPLODE, // deathsound +25*FRACUNIT, // speed +13*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_ICEDAMAGE // flags2 + }, + +{ // MT_FLAME_SMALL_TEMP +10500, // doomednum +S_FLAME_TSMALL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FLAME_LARGE_TEMP +10502, // doomednum +S_FLAME_TLARGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FLAME_SMALL +10501, // doomednum +S_FLAME_SMALL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_NOTELEPORT|MF2_DONTDRAW // flags2 + }, + +{ // MT_FLAME_LARGE +10503, // doomednum +S_FLAME_LARGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_NOTELEPORT|MF2_DONTDRAW // flags2 + }, + +{ // MT_HEALINGBOTTLE +81, // doomednum +S_ITEM_PTN1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_HEALTHFLASK +82, // doomednum +S_ARTI_PTN2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_ARTIFLY +83, // doomednum +S_ARTI_SOAR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_ARTIINVULNERABILITY +84, // doomednum +S_ARTI_INVU1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_SUMMONMAULATOR +86, // doomednum +S_ARTI_SUMMON, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_SUMMON_FX +-1, // doomednum +S_SUMMON_FX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SUMMON_FX2_1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +20*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_THRUSTFLOOR_UP +10091, // doomednum +S_THRUSTINIT2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +128*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_THRUSTFLOOR_DOWN +10090, // doomednum +S_THRUSTINIT1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +128*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP|MF2_DONTDRAW // flags2 + }, + +{ // MT_TELEPORTOTHER +10040, // doomednum +S_ARTI_TELOTHER1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_TELOTHER_FX1 +-1, // doomednum +S_TELO_FX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_TELO_FX9, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +20*FRACUNIT, // speed +16*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +10001, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_TELOTHER_FX2 +-1, // doomednum +S_TELO_FX2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_TELO_FX9, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +16*FRACUNIT, // speed +16*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +10001, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_TELOTHER_FX3 +-1, // doomednum +S_TELO_FX3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_TELO_FX9, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +16*FRACUNIT, // speed +16*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +10001, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_TELOTHER_FX4 +-1, // doomednum +S_TELO_FX4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_TELO_FX9, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +16*FRACUNIT, // speed +16*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +10001, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_TELOTHER_FX5 +-1, // doomednum +S_TELO_FX5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_TELO_FX9, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +16*FRACUNIT, // speed +16*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +10001, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DIRT1 +-1, // doomednum +S_DIRT1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT1_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DIRT2 +-1, // doomednum +S_DIRT2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT2_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DIRT3 +-1, // doomednum +S_DIRT3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT3_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DIRT4 +-1, // doomednum +S_DIRT4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT4_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_DIRT5 +-1, // doomednum +S_DIRT5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT5_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_DIRT6 +-1, // doomednum +S_DIRT6_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DIRT6_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_DIRTCLUMP +-1, // doomednum +S_DIRTCLUMP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ROCK1 +-1, // doomednum +S_ROCK1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ROCK1_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ROCK2 +-1, // doomednum +S_ROCK2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ROCK2_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ROCK3 +-1, // doomednum +S_ROCK3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ROCK3_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FOGSPAWNER +10000, // doomednum +S_SPAWNFOG1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +MF2_DONTDRAW|MF2_FLOATBOB // flags2 + }, + +{ // MT_FOGPATCHS +10001, // doomednum +S_FOGPATCHS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FOGPATCHS0, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_FLOAT|MF_NOGRAVITY|MF_SHADOW|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FOGPATCHM +10002, // doomednum +S_FOGPATCHM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FOGPATCHM0, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_FLOAT|MF_NOGRAVITY|MF_SHADOW|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FOGPATCHL +10003, // doomednum +S_FOGPATCHL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FOGPATCHL0, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_FLOAT|MF_NOGRAVITY|MF_SHADOW|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_QUAKE_FOCUS +-1, // doomednum +S_QUAKE_ACTIVE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +MF2_DONTDRAW // flags2 + }, + +{ // MT_SGSHARD1 +-1, // doomednum +S_SGSHARD1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD1_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD2 +-1, // doomednum +S_SGSHARD2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD2_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD3 +-1, // doomednum +S_SGSHARD3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD3_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD4 +-1, // doomednum +S_SGSHARD4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD4_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD5 +-1, // doomednum +S_SGSHARD5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD5_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD6 +-1, // doomednum +S_SGSHARD6_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD6_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD7 +-1, // doomednum +S_SGSHARD7_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD7_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD8 +-1, // doomednum +S_SGSHARD8_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD8_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD9 +-1, // doomednum +S_SGSHARD9_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD9_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SGSHARD0 +-1, // doomednum +S_SGSHARD0_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SGSHARD0_D, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_ARTIEGG +30, // doomednum +S_ARTI_EGGC1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_EGGFX +-1, // doomednum +S_EGGFX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +0, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_EGGFXI1_1, // deathstate +S_NULL, // xdeathstate +0, // deathsound +18*FRACUNIT, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ARTISUPERHEAL +32, // doomednum +S_ARTI_SPHL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_ZWINGEDSTATUENOSKULL +9011, // doomednum +S_ZWINGEDSTATUENOSKULL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZGEMPEDESTAL +9012, // doomednum +S_ZGEMPEDESTAL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZSKULL +9002, // doomednum +S_ARTIPUZZSKULL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMBIG +9003, // doomednum +S_ARTIPUZZGEMBIG, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMRED +9004, // doomednum +S_ARTIPUZZGEMRED, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMGREEN1 +9005, // doomednum +S_ARTIPUZZGEMGREEN1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMGREEN2 +9009, // doomednum +S_ARTIPUZZGEMGREEN2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMBLUE1 +9006, // doomednum +S_ARTIPUZZGEMBLUE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEMBLUE2 +9010, // doomednum +S_ARTIPUZZGEMBLUE2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZBOOK1 +9007, // doomednum +S_ARTIPUZZBOOK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZBOOK2 +9008, // doomednum +S_ARTIPUZZBOOK2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZSKULL2 +9014, // doomednum +S_ARTIPUZZSKULL2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZFWEAPON +9015, // doomednum +S_ARTIPUZZFWEAPON, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZCWEAPON +9016, // doomednum +S_ARTIPUZZCWEAPON, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZMWEAPON +9017, // doomednum +S_ARTIPUZZMWEAPON, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEAR +9018, // doomednum +S_ARTIPUZZGEAR_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEAR2 +9019, // doomednum +S_ARTIPUZZGEAR2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEAR3 +9020, // doomednum +S_ARTIPUZZGEAR3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTIPUZZGEAR4 +9021, // doomednum +S_ARTIPUZZGEAR4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARTITORCH +33, // doomednum +S_ARTI_TRCH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_FIREBOMB +-1, // doomednum +S_FIREBOMB1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_FLECHETTE_EXPLODE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOGRAVITY|MF_ALTSHADOW, // flags +MF2_FIREDAMAGE // flags2 + }, + +{ // MT_ARTITELEPORT +36, // doomednum +S_ARTI_ATLP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_ARTIPOISONBAG +8000, // doomednum +S_ARTI_PSBG1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_POISONBAG +-1, // doomednum +S_POISONBAG1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOGRAVITY|MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_POISONCLOUD +-1, // doomednum +S_POISONCLOUD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_POISONSHROOM_DEATH, // deathsound +0, // speed +1, // radius +1, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_NOGRAVITY|MF_NOBLOCKMAP|MF_SHADOW|MF_NOCLIP|MF_DROPOFF, // flags +MF2_NODMGTHRUST // flags2 + }, + +{ // MT_THROWINGBOMB +-1, // doomednum +S_THROWINGBOMB1, // spawnstate +48, // spawnhealth +S_NULL, // seestate +SFX_FLECHETTE_BOUNCE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_THROWINGBOMB_X1, // deathstate +S_NULL, // xdeathstate +SFX_FLECHETTE_EXPLODE, // deathsound +12*FRACUNIT, // speed +8*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_FLOORBOUNCE|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_SPEEDBOOTS +8002, // doomednum +S_ARTI_BOOTS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_BOOSTMANA +8003, // doomednum +S_ARTI_MANA, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_BOOSTARMOR +8041, // doomednum +S_ARTI_ARMOR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_BLASTRADIUS +10110, // doomednum +S_ARTI_BLAST1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_HEALRADIUS +10120, // doomednum +S_ARTI_HEALRAD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_SPLASH +-1, // doomednum +S_SPLASH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SPLASHX, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_LOGRAV|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_SPLASHBASE +-1, // doomednum +S_SPLASHBASE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_LAVASPLASH +-1, // doomednum +S_LAVASPLASH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_LAVASMOKE +-1, // doomednum +S_LAVASMOKE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_SLUDGECHUNK +-1, // doomednum +S_SLUDGECHUNK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SLUDGECHUNKX, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_LOGRAV|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_SLUDGESPLASH +-1, // doomednum +S_SLUDGESPLASH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_MISC0 +5, // doomednum +S_ZWINGEDSTATUE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC1 +6, // doomednum +S_ZROCK1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC2 +7, // doomednum +S_ZROCK2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC3 +9, // doomednum +S_ZROCK3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC4 +15, // doomednum +S_ZROCK4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC5 +17, // doomednum +S_ZCHANDELIER1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +60*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC6 +8063, // doomednum +S_ZCHANDELIER_U, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +60*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC7 +24, // doomednum +S_ZTREEDEAD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +96*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC8 +25, // doomednum +S_ZTREE, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +128*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_TREEDESTRUCTIBLE +8062, // doomednum +S_ZTREEDESTRUCTIBLE1, // spawnstate +70, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZTREEDES_D1, // deathstate +S_NULL, // xdeathstate +SFX_TREE_BREAK, // deathsound +0, // speed +15*FRACUNIT, // radius +180*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_MISC9 +26, // doomednum +S_ZTREESWAMP182_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +150*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC10 +27, // doomednum +S_ZTREESWAMP172_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +120*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC11 +28, // doomednum +S_ZSTUMPBURNED1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC12 +29, // doomednum +S_ZSTUMPBARE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC13 +37, // doomednum +S_ZSTUMPSWAMP1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC14 +38, // doomednum +S_ZSTUMPSWAMP2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC15 +39, // doomednum +S_ZSHROOMLARGE1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC16 +40, // doomednum +S_ZSHROOMLARGE2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC17 +41, // doomednum +S_ZSHROOMLARGE3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC18 +42, // doomednum +S_ZSHROOMSMALL1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC19 +44, // doomednum +S_ZSHROOMSMALL2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC20 +45, // doomednum +S_ZSHROOMSMALL3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC21 +46, // doomednum +S_ZSHROOMSMALL4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC22 +47, // doomednum +S_ZSHROOMSMALL5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC23 +48, // doomednum +S_ZSTALAGMITEPILLAR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +138*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC24 +49, // doomednum +S_ZSTALAGMITELARGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +48*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC25 +50, // doomednum +S_ZSTALAGMITEMEDIUM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +6*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC26 +51, // doomednum +S_ZSTALAGMITESMALL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +36*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC27 +52, // doomednum +S_ZSTALACTITELARGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +66*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC28 +56, // doomednum +S_ZSTALACTITEMEDIUM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +6*FRACUNIT, // radius +50*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC29 +57, // doomednum +S_ZSTALACTITESMALL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC30 +58, // doomednum +S_ZMOSSCEILING1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC31 +59, // doomednum +S_ZMOSSCEILING2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +24*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC32 +60, // doomednum +S_ZSWAMPVINE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +52*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC33 +61, // doomednum +S_ZCORPSEKABOB1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +92*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC34 +62, // doomednum +S_ZCORPSESLEEPING1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC35 +63, // doomednum +S_ZTOMBSTONERIP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +46*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC36 +64, // doomednum +S_ZTOMBSTONESHANE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +46*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC37 +65, // doomednum +S_ZTOMBSTONEBIGCROSS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +46*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC38 +66, // doomednum +S_ZTOMBSTONEBRIANR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +52*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC39 +67, // doomednum +S_ZTOMBSTONECROSSCIRCLE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +52*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC40 +68, // doomednum +S_ZTOMBSTONESMALLCROSS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +46*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC41 +69, // doomednum +S_ZTOMBSTONEBRIANP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +46*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC42 +71, // doomednum +S_CORPSEHANGING_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +6*FRACUNIT, // radius +75*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC43 +72, // doomednum +S_ZSTATUEGARGOYLEGREENTALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC44 +73, // doomednum +S_ZSTATUEGARGOYLEBLUETALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC45 +74, // doomednum +S_ZSTATUEGARGOYLEGREENSHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC46 +76, // doomednum +S_ZSTATUEGARGOYLEBLUESHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC47 +8044, // doomednum +S_ZSTATUEGARGOYLESTRIPETALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC48 +8045, // doomednum +S_ZSTATUEGARGOYLEDARKREDTALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC49 +8046, // doomednum +S_ZSTATUEGARGOYLEREDTALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC50 +8047, // doomednum +S_ZSTATUEGARGOYLETANTALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC51 +8048, // doomednum +S_ZSTATUEGARGOYLERUSTTALL_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +108*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC52 +8049, // doomednum +S_ZSTATUEGARGOYLEDARKREDSHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC53 +8050, // doomednum +S_ZSTATUEGARGOYLEREDSHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC54 +8051, // doomednum +S_ZSTATUEGARGOYLETANSHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC55 +8052, // doomednum +S_ZSTATUEGARGOYLERUSTSHORT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +14*FRACUNIT, // radius +62*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC56 +77, // doomednum +S_ZBANNERTATTERED_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +120*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC57 +78, // doomednum +S_ZTREELARGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZTREELARGE1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +180*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC58 +79, // doomednum +S_ZTREELARGE2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZTREELARGE2, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +180*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC59 +80, // doomednum +S_ZTREEGNARLED1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +22*FRACUNIT, // radius +100*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC60 +87, // doomednum +S_ZTREEGNARLED2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +22*FRACUNIT, // radius +100*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC61 +88, // doomednum +S_ZLOG, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +25*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC62 +89, // doomednum +S_ZSTALACTITEICELARGE, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +66*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC63 +90, // doomednum +S_ZSTALACTITEICEMEDIUM, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +50*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC64 +91, // doomednum +S_ZSTALACTITEICESMALL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC65 +92, // doomednum +S_ZSTALACTITEICETINY, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC66 +93, // doomednum +S_ZSTALAGMITEICELARGE, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +66*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC67 +94, // doomednum +S_ZSTALAGMITEICEMEDIUM, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +50*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC68 +95, // doomednum +S_ZSTALAGMITEICESMALL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC69 +96, // doomednum +S_ZSTALAGMITEICETINY, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC70 +97, // doomednum +S_ZROCKBROWN1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +17*FRACUNIT, // radius +72*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC71 +98, // doomednum +S_ZROCKBROWN2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +50*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC72 +99, // doomednum +S_ZROCKBLACK, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_MISC73 +100, // doomednum +S_ZRUBBLE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC74 +101, // doomednum +S_ZRUBBLE2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC75 +102, // doomednum +S_ZRUBBLE3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_MISC76 +103, // doomednum +S_ZVASEPILLAR, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +54*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_POTTERY1 +104, // doomednum +S_ZPOTTERY1, // spawnstate +15, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZPOTTERY_EXPLODE, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_DROPOFF, // flags +MF2_SLIDE|MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ // flags2 + }, + +{ // MT_POTTERY2 +105, // doomednum +S_ZPOTTERY2, // spawnstate +15, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZPOTTERY_EXPLODE, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +25*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_DROPOFF, // flags +MF2_SLIDE|MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ // flags2 + }, + +{ // MT_POTTERY3 +106, // doomednum +S_ZPOTTERY3, // spawnstate +15, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZPOTTERY_EXPLODE, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +25*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_DROPOFF, // flags +MF2_SLIDE|MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ // flags2 + }, + +{ // MT_POTTERYBIT1 +-1, // doomednum +S_POTTERYBIT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_POTTERYBIT_EX0, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_MISC77 +108, // doomednum +S_ZCORPSELYNCHED1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +11*FRACUNIT, // radius +95*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ZLYNCHED_NOHEART +109, // doomednum +S_ZCORPSELYNCHED2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +100*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC78 +110, // doomednum +S_ZCORPSESITTING, // spawnstate +30, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZCORPSESITTING_X, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +35*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_CORPSEBIT +-1, // doomednum +S_CORPSEBIT_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +MF2_TELESTOMP // flags2 + }, + +{ // MT_CORPSEBLOODDRIP +-1, // doomednum +S_CORPSEBLOODDRIP, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CORPSEBLOODDRIP_X1, // deathstate +S_NULL, // xdeathstate +SFX_DRIP, // deathsound +0, // speed +FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_MISSILE, // flags +MF2_LOGRAV // flags2 + }, + +{ // MT_BLOODPOOL +111, // doomednum +S_BLOODPOOL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_MISC79 +119, // doomednum +S_ZCANDLE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC80 +113, // doomednum +S_ZLEAFSPAWNER, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +MF2_DONTDRAW // flags2 + }, + +{ // MT_LEAF1 +-1, // doomednum +S_LEAF1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_LEAF_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_LEAF2 +-1, // doomednum +S_LEAF2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_LEAF_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_ZTWINEDTORCH +116, // doomednum +S_ZTWINEDTORCH_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZTWINEDTORCH_UNLIT +117, // doomednum +S_ZTWINEDTORCH_UNLIT, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +10*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_BRIDGE +118, // doomednum +S_BRIDGE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +32*FRACUNIT, // radius +2*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_NOGRAVITY, // flags +MF2_DONTDRAW // flags2 + }, + +{ // MT_BRIDGEBALL +-1, // doomednum +S_BBALL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ZWALLTORCH +54, // doomednum +S_ZWALLTORCH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ZWALLTORCH_UNLIT +55, // doomednum +S_ZWALLTORCH_U, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ZBARREL +8100, // doomednum +S_ZBARREL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZSHRUB1 +8101, // doomednum +S_ZSHRUB1, // spawnstate +20, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_ZSHRUB1_X1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZSHRUB1_DIE, // deathstate +S_NULL, // xdeathstate +SFX_TREE_EXPLODE, // deathsound +0, // speed +8*FRACUNIT, // radius +24*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_ZSHRUB2 +8102, // doomednum +S_ZSHRUB2, // spawnstate +10, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_ZSHRUB2_X1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZSHRUB2_DIE, // deathstate +S_NULL, // xdeathstate +SFX_TREE_EXPLODE, // deathsound +0, // speed +16*FRACUNIT, // radius +40*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_ZBUCKET +8103, // doomednum +S_ZBUCKET1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +72*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ZPOISONSHROOM +8104, // doomednum +S_ZPOISONSHROOM1, // spawnstate +30, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_ZPOISONSHROOM_P1, // painstate +255, // painchance +SFX_POISONSHROOM_PAIN, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZPOISONSHROOM_X1, // deathstate +S_NULL, // xdeathstate +SFX_POISONSHROOM_DEATH, // deathsound +0, // speed +6*FRACUNIT, // radius +20*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SHOOTABLE|MF_SOLID|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_ZFIREBULL +8042, // doomednum +S_ZFIREBULL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +80*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZFIREBULL_UNLIT +8043, // doomednum +S_ZFIREBULL_U, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +80*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_FIRETHING +8060, // doomednum +S_ZFIRETHING1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_BRASSTORCH +8061, // doomednum +S_ZBRASSTORCH1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +6*FRACUNIT, // radius +35*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZSUITOFARMOR +8064, // doomednum +S_ZSUITOFARMOR, // spawnstate +60, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZSUITOFARMOR_X1, // deathstate +S_NULL, // xdeathstate +SFX_SUITOFARMOR_BREAK, // deathsound +0, // speed +16*FRACUNIT, // radius +72*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_ZARMORCHUNK +-1, // doomednum +S_ZARMORCHUNK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +0, // flags +0 // flags2 + }, + +{ // MT_ZBELL +8065, // doomednum +S_ZBELL, // spawnstate +5, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZBELL_X1, // deathstate +S_NULL, // xdeathstate +SFX_BELLRING, // deathsound +0, // speed +56*FRACUNIT, // radius +120*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZBLUE_CANDLE +8066, // doomednum +S_ZBLUE_CANDLE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_ZIRON_MAIDEN +8067, // doomednum +S_ZIRON_MAIDEN, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +60*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZXMAS_TREE +8068, // doomednum +S_ZXMAS_TREE, // spawnstate +20, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_ZXMAS_TREE_X1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ZXMAS_TREE_DIE, // deathstate +S_NULL, // xdeathstate +SFX_TREE_EXPLODE, // deathsound +0, // speed +11*FRACUNIT, // radius +130*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags +0 // flags2 + }, + +{ // MT_ZCAULDRON +8069, // doomednum +S_ZCAULDRON1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +26*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZCAULDRON_UNLIT +8070, // doomednum +S_ZCAULDRON_U, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +12*FRACUNIT, // radius +26*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID, // flags +0 // flags2 + }, + +{ // MT_ZCHAINBIT32 +8071, // doomednum +S_ZCHAINBIT32, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINBIT64 +8072, // doomednum +S_ZCHAINBIT64, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINEND_HEART +8073, // doomednum +S_ZCHAINEND_HEART, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINEND_HOOK1 +8074, // doomednum +S_ZCHAINEND_HOOK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINEND_HOOK2 +8075, // doomednum +S_ZCHAINEND_HOOK2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINEND_SPIKE +8076, // doomednum +S_ZCHAINEND_SPIKE, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_ZCHAINEND_SKULL +8077, // doomednum +S_ZCHAINEND_SKULL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +32*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SPAWNCEILING, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT1 +8500, // doomednum +S_TABLE_SHIT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT2 +8501, // doomednum +S_TABLE_SHIT2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT3 +8502, // doomednum +S_TABLE_SHIT3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT4 +8503, // doomednum +S_TABLE_SHIT4, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT5 +8504, // doomednum +S_TABLE_SHIT5, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT6 +8505, // doomednum +S_TABLE_SHIT6, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT7 +8506, // doomednum +S_TABLE_SHIT7, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT8 +8507, // doomednum +S_TABLE_SHIT8, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT9 +8508, // doomednum +S_TABLE_SHIT9, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TABLE_SHIT10 +8509, // doomednum +S_TABLE_SHIT10, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_TFOG +-1, // doomednum +S_TFOG1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MISC81 +140, // doomednum +S_TELESMOKE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_TELEPORTMAN +14, // doomednum +S_NULL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +0 // flags2 + }, + +{ // MT_PUNCHPUFF +-1, // doomednum +S_PUNCHPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_FIGHTER_PUNCH_HITTHING, // seesound +8, // reactiontime +SFX_FIGHTER_PUNCH_HITWALL, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_FW_AXE +8010, // doomednum +S_AXE, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_AXEPUFF +-1, // doomednum +S_HAMMERPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_FIGHTER_AXE_HITTHING, // seesound +8, // reactiontime +SFX_FIGHTER_HAMMER_HITWALL, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_AXEPUFF_GLOW +-1, // doomednum +S_AXEPUFF_GLOW1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_FIGHTER_AXE_HITTHING, // seesound +8, // reactiontime +SFX_FIGHTER_HAMMER_HITWALL, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_AXEBLOOD +-1, // doomednum +S_AXEBLOOD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_AXEBLOOD6, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_FW_HAMMER +123, // doomednum +S_HAMM, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_HAMMER_MISSILE +-1, // doomednum +S_HAMMER_MISSILE_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_HAMMER_MISSILE_X1, // deathstate +S_NULL, // xdeathstate +SFX_FIGHTER_HAMMER_EXPLODE, // deathsound +25*FRACUNIT, // speed +14*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +10, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_HAMMERPUFF +-1, // doomednum +S_HAMMERPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_FIGHTER_HAMMER_HITTHING, // seesound +8, // reactiontime +SFX_FIGHTER_HAMMER_HITWALL, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_FSWORD_MISSILE +-1, // doomednum +S_FSWORD_MISSILE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FSWORD_MISSILE_X1, // deathstate +S_NULL, // xdeathstate +SFX_FIGHTER_SWORD_EXPLODE, // deathsound +30*FRACUNIT, // speed +16*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +8, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_FSWORD_FLAME +-1, // doomednum +S_FSWORD_FLAME1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_CW_SERPSTAFF +10, // doomednum +S_CSTAFF, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_CSTAFF_MISSILE +-1, // doomednum +S_CSTAFF_MISSILE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CSTAFF_MISSILE_X1, // deathstate +S_NULL, // xdeathstate +SFX_CLERIC_CSTAFF_EXPLODE, // deathsound +22*FRACUNIT, // speed +12*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +5, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_CSTAFFPUFF +-1, // doomednum +S_CSTAFFPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_CLERIC_CSTAFF_HITTHING, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_CW_FLAME +8009, // doomednum +S_CFLAME1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_CFLAMEFLOOR +-1, // doomednum +S_CFLAMEFLOOR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_FLAMEPUFF +-1, // doomednum +S_FLAMEPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_CLERIC_FLAME_EXPLODE, // seesound +8, // reactiontime +SFX_CLERIC_FLAME_EXPLODE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +FRACUNIT, // radius +FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_FLAMEPUFF2 +-1, // doomednum +S_FLAMEPUFF2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_CLERIC_FLAME_EXPLODE, // seesound +8, // reactiontime +SFX_CLERIC_FLAME_EXPLODE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +FRACUNIT, // radius +FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_CIRCLEFLAME +-1, // doomednum +S_CIRCLE_FLAME1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CIRCLE_FLAME_X1, // deathstate +S_NULL, // xdeathstate +SFX_CLERIC_FLAME_CIRCLE, // deathsound +0, // speed +6*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_CFLAME_MISSILE +-1, // doomednum +S_CFLAME_MISSILE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CFLAME_MISSILE_X, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +200*FRACUNIT, // speed +14*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +8, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_DONTDRAW|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_HOLY_FX +-1, // doomednum +S_HOLY_FX1, // spawnstate +105, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_HOLY_FX_X1, // deathstate +S_NULL, // xdeathstate +SFX_SPIRIT_DIE, // deathsound +12*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +3, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_SEEKERMISSILE|MF2_RIP|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_HOLY_TAIL +-1, // doomednum +S_HOLY_TAIL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +FRACUNIT, // radius +FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP|MF_ALTSHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_HOLY_PUFF +-1, // doomednum +S_HOLY_PUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_HOLY_MISSILE +-1, // doomednum +S_HOLY_MISSILE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_HOLY_MISSILE_X, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +30*FRACUNIT, // speed +15*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_HOLY_MISSILE_PUFF +-1, // doomednum +S_HOLY_MISSILE_P1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_MWANDPUFF +-1, // doomednum +S_MWANDPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_CANNOTPUSH|MF2_NODMGTHRUST // flags2 + }, + +{ // MT_MWANDSMOKE +-1, // doomednum +S_MWANDSMOKE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +MF2_NOTELEPORT|MF2_CANNOTPUSH|MF2_NODMGTHRUST // flags2 + }, + +{ // MT_MWAND_MISSILE +-1, // doomednum +S_MWAND_MISSILE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MWANDPUFF1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +184*FRACUNIT, // speed +12*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_RIP|MF2_IMPACT|MF2_PCROSS|MF2_NODMGTHRUST|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_MW_LIGHTNING +8040, // doomednum +S_MW_LIGHTNING1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_LIGHTNING_CEILING +-1, // doomednum +S_LIGHTNING_CEILING1, // spawnstate +144, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_LIGHTNING_C_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +25*FRACUNIT, // speed +16*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +8, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_LIGHTNING_FLOOR +-1, // doomednum +S_LIGHTNING_FLOOR1, // spawnstate +144, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_LIGHTNING_F_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +25*FRACUNIT, // speed +16*FRACUNIT, // radius +40*FRACUNIT, // height +100, // mass +8, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_LIGHTNING_ZAP +-1, // doomednum +S_LIGHTNING_ZAP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_LIGHTNING_ZAP_X8, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +35*FRACUNIT, // height +100, // mass +2, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE|MF_DROPOFF, // flags +0 // flags2 + }, + +{ // MT_MSTAFF_FX +-1, // doomednum +S_MSTAFF_FX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MSTAFF_FX_X1, // deathstate +S_NULL, // xdeathstate +SFX_MAGE_STAFF_EXPLODE, // deathsound +20*FRACUNIT, // speed +16*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +6, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE|MF2_RIP|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_MSTAFF_FX2 +-1, // doomednum +S_MSTAFF_FX2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MSTAFF_FX2_X1, // deathstate +S_NULL, // xdeathstate +SFX_MAGE_STAFF_EXPLODE, // deathsound +17*FRACUNIT, // speed +20*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE|MF2_IMPACT|MF2_PCROSS|MF2_SEEKERMISSILE // flags2 + }, + +{ // MT_FW_SWORD1 +12, // doomednum +S_FSWORD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_FW_SWORD2 +13, // doomednum +S_FSWORD2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_FW_SWORD3 +16, // doomednum +S_FSWORD3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_CW_HOLY1 +18, // doomednum +S_CHOLY1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_CW_HOLY2 +19, // doomednum +S_CHOLY2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_CW_HOLY3 +20, // doomednum +S_CHOLY3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_MW_STAFF1 +21, // doomednum +S_MSTAFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_MW_STAFF2 +22, // doomednum +S_MSTAFF2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_MW_STAFF3 +23, // doomednum +S_MSTAFF3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_SNOUTPUFF +-1, // doomednum +S_PUNCHPUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_MW_CONE +53, // doomednum +S_COS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_SHARDFX1 +-1, // doomednum +S_SHARDFX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SHARDFXE1_1, // deathstate +S_NULL, // xdeathstate +SFX_MAGE_SHARDS_EXPLODE, // deathsound +25*FRACUNIT, // speed +13*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_ICEDAMAGE // flags2 + }, + +{ // MT_BLOOD +-1, // doomednum +S_BLOOD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +0 // flags2 + }, + +{ // MT_BLOODSPLATTER +-1, // doomednum +S_BLOODSPLATTER1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_BLOODSPLATTERX, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +4*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_GIBS +-1, // doomednum +S_GIBS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_PLAYER_FIGHTER +-1, // doomednum +S_FPLAY, // spawnstate +100, // spawnhealth +S_FPLAY_RUN1, // seestate +SFX_NONE, // seesound +0, // reactiontime +SFX_NONE, // attacksound +S_FPLAY_PAIN, // painstate +255, // painchance +SFX_PLAYER_FIGHTER_PAIN, // painsound +S_NULL, // meleestate +S_FPLAY_ATK1, // missilestate +S_NULL, // crashstate +S_FPLAY_DIE1, // deathstate +S_FPLAY_XDIE1, // xdeathstate +SFX_NONE, // deathsound +0, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags +MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_SLIDE|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL // flags2 + }, + +{ // MT_BLOODYSKULL +-1, // doomednum +S_BLOODYSKULL1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +4*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF, // flags +MF2_LOGRAV|MF2_CANNOTPUSH // flags2 + }, + +{ // MT_PLAYER_SPEED +-1, // doomednum +S_PLAYER_SPEED1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_ALTSHADOW, // flags +0 // flags2 + }, + +{ // MT_ICECHUNK +-1, // doomednum +S_ICECHUNK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF, // flags +MF2_LOGRAV|MF2_CANNOTPUSH|MF2_FLOORCLIP // flags2 + }, + +{ // MT_PLAYER_CLERIC +-1, // doomednum +S_CPLAY, // spawnstate +100, // spawnhealth +S_CPLAY_RUN1, // seestate +SFX_NONE, // seesound +0, // reactiontime +SFX_NONE, // attacksound +S_CPLAY_PAIN, // painstate +255, // painchance +SFX_PLAYER_CLERIC_PAIN, // painsound +S_NULL, // meleestate +S_CPLAY_ATK1, // missilestate +S_NULL, // crashstate +S_CPLAY_DIE1, // deathstate +S_CPLAY_XDIE1, // xdeathstate +SFX_NONE, // deathsound +0, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags +MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_SLIDE|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL // flags2 + }, + +{ // MT_PLAYER_MAGE +-1, // doomednum +S_MPLAY, // spawnstate +100, // spawnhealth +S_MPLAY_RUN1, // seestate +SFX_NONE, // seesound +0, // reactiontime +SFX_NONE, // attacksound +S_MPLAY_PAIN, // painstate +255, // painchance +SFX_PLAYER_MAGE_PAIN, // painsound +S_NULL, // meleestate +S_MPLAY_ATK1, // missilestate +S_NULL, // crashstate +S_MPLAY_DIE1, // deathstate +S_MPLAY_XDIE1, // xdeathstate +SFX_NONE, // deathsound +0, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags +MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_SLIDE|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL // flags2 + }, + +{ // MT_PIGPLAYER +-1, // doomednum +S_PIGPLAY, // spawnstate +100, // spawnhealth +S_PIGPLAY_RUN1, // seestate +SFX_NONE, // seesound +0, // reactiontime +SFX_NONE, // attacksound +S_PIGPLAY_PAIN, // painstate +255, // painchance +SFX_PIG_PAIN, // painsound +S_NULL, // meleestate +S_PIGPLAY_ATK1, // missilestate +S_NULL, // crashstate +S_PIG_DIE1, // deathstate +S_NULL, // xdeathstate +SFX_PIG_DEATH, // deathsound +0, // speed +16*FRACUNIT, // radius +24*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_NOTDMATCH, // flags +MF2_WINDTHRUST|MF2_SLIDE|MF2_PASSMOBJ|MF2_FLOORCLIP|MF2_TELESTOMP|MF2_PUSHWALL // flags2 + }, + +{ // MT_PIG +-1, // doomednum +S_PIG_LOOK1, // spawnstate +25, // spawnhealth +S_PIG_WALK1, // seestate +SFX_PIG_ACTIVE1, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_PIG_PAIN, // painstate +128, // painchance +SFX_PIG_PAIN, // painsound +S_PIG_ATK1, // meleestate +0, // missilestate +S_NULL, // crashstate +S_PIG_DIE1, // deathstate +S_NULL, // xdeathstate +SFX_PIG_DEATH, // deathsound +10, // speed +12*FRACUNIT, // radius +22*FRACUNIT, // height +60, // mass +0, // damage +SFX_PIG_ACTIVE1, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP // flags2 + }, + +{ // MT_CENTAUR +107, // doomednum +S_CENTAUR_LOOK1, // spawnstate +200, // spawnhealth +S_CENTAUR_WALK1, // seestate +SFX_CENTAUR_SIGHT, // seesound +8, // reactiontime +SFX_CENTAUR_ATTACK, // attacksound +S_CENTAUR_PAIN1, // painstate +135, // painchance +SFX_CENTAUR_PAIN, // painsound +S_CENTAUR_ATK1, // meleestate +0, // missilestate +S_NULL, // crashstate +S_CENTAUR_DEATH1, // deathstate +S_CENTAUR_DEATH_X1, // xdeathstate +SFX_CENTAUR_DEATH, // deathsound +13, // speed +20*FRACUNIT, // radius +64*FRACUNIT, // height +120, // mass +0, // damage +SFX_CENTAUR_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_CENTAURLEADER +115, // doomednum +S_CENTAUR_LOOK1, // spawnstate +250, // spawnhealth +S_CENTAUR_WALK1, // seestate +SFX_CENTAUR_SIGHT, // seesound +8, // reactiontime +SFX_CENTAUR_ATTACK, // attacksound +S_CENTAUR_PAIN1, // painstate +96, // painchance +SFX_CENTAUR_PAIN, // painsound +S_CENTAUR_ATK1, // meleestate +S_CENTAUR_MISSILE1, // missilestate +S_NULL, // crashstate +S_CENTAUR_DEATH1, // deathstate +S_CENTAUR_DEATH_X1, // xdeathstate +SFX_CENTAUR_DEATH, // deathsound +10, // speed +20*FRACUNIT, // radius +64*FRACUNIT, // height +120, // mass +0, // damage +SFX_CENTAUR_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_CENTAUR_FX +-1, // doomednum +S_CENTAUR_FX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CENTAUR_FX_X1, // deathstate +S_NULL, // xdeathstate +SFX_CENTAUR_MISSILE_EXPLODE, // deathsound +20*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS // flags2 + }, + +{ // MT_CENTAUR_SHIELD +-1, // doomednum +S_CENTAUR_SHIELD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CENTAUR_SHIELD_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_CENTAUR_SWORD +-1, // doomednum +S_CENTAUR_SWORD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_CENTAUR_SWORD_X1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DEMON +31, // doomednum +S_DEMN_LOOK1, // spawnstate +250, // spawnhealth +S_DEMN_CHASE1, // seestate +SFX_DEMON_SIGHT, // seesound +8, // reactiontime +SFX_DEMON_ATTACK, // attacksound +S_DEMN_PAIN1, // painstate +50, // painchance +SFX_DEMON_PAIN, // painsound +S_DEMN_ATK1_1, // meleestate +S_DEMN_ATK2_1, // missilestate +S_NULL, // crashstate +S_DEMN_DEATH1, // deathstate +S_DEMN_XDEATH1, // xdeathstate +SFX_DEMON_DEATH, // deathsound +13, // speed +32*FRACUNIT, // radius +64*FRACUNIT, // height +220, // mass +0, // damage +SFX_DEMON_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_DEMONCHUNK1 +-1, // doomednum +S_DEMONCHUNK1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONCHUNK1_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMONCHUNK2 +-1, // doomednum +S_DEMONCHUNK2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONCHUNK2_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMONCHUNK3 +-1, // doomednum +S_DEMONCHUNK3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONCHUNK3_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMONCHUNK4 +-1, // doomednum +S_DEMONCHUNK4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONCHUNK4_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMONCHUNK5 +-1, // doomednum +S_DEMONCHUNK5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONCHUNK5_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMONFX1 +-1, // doomednum +S_DEMONFX_MOVE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMONFX_BOOM1, // deathstate +S_NULL, // xdeathstate +SFX_DEMON_MISSILE_EXPLODE, // deathsound +15*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +5, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_DEMON2 +8080, // doomednum +S_DEMN2_LOOK1, // spawnstate +250, // spawnhealth +S_DEMN2_CHASE1, // seestate +SFX_DEMON_SIGHT, // seesound +8, // reactiontime +SFX_DEMON_ATTACK, // attacksound +S_DEMN2_PAIN1, // painstate +50, // painchance +SFX_DEMON_PAIN, // painsound +S_DEMN2_ATK1_1, // meleestate +S_DEMN2_ATK2_1, // missilestate +S_NULL, // crashstate +S_DEMN2_DEATH1, // deathstate +S_DEMN2_XDEATH1, // xdeathstate +SFX_DEMON_DEATH, // deathsound +13, // speed +32*FRACUNIT, // radius +64*FRACUNIT, // height +220, // mass +0, // damage +SFX_DEMON_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_DEMON2CHUNK1 +-1, // doomednum +S_DEMON2CHUNK1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2CHUNK1_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMON2CHUNK2 +-1, // doomednum +S_DEMON2CHUNK2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2CHUNK2_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMON2CHUNK3 +-1, // doomednum +S_DEMON2CHUNK3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2CHUNK3_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMON2CHUNK4 +-1, // doomednum +S_DEMON2CHUNK4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2CHUNK4_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMON2CHUNK5 +-1, // doomednum +S_DEMON2CHUNK5_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2CHUNK5_4, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_DEMON2FX1 +-1, // doomednum +S_DEMON2FX_MOVE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DEMON2FX_BOOM1, // deathstate +S_NULL, // xdeathstate +SFX_DEMON_MISSILE_EXPLODE, // deathsound +15*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +5, // damage +SFX_NONE, // activesound +MF_MISSILE|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_WRAITHB +10011, // doomednum +S_WRAITH_LOOK1, // spawnstate +150, // spawnhealth +S_WRAITH_RAISE1, // seestate +SFX_WRAITH_SIGHT, // seesound +8, // reactiontime +SFX_WRAITH_ATTACK, // attacksound +S_WRAITH_PAIN1, // painstate +25, // painchance +SFX_WRAITH_PAIN, // painsound +S_WRAITH_ATK1_1, // meleestate +S_WRAITH_ATK2_1, // missilestate +S_NULL, // crashstate +S_WRAITH_DEATH1_1, // deathstate +S_WRAITH_DEATH2_1, // xdeathstate +SFX_WRAITH_DEATH, // deathsound +11, // speed +20*FRACUNIT, // radius +68*FRACUNIT, // height +75, // mass +10, // damage +SFX_WRAITH_ACTIVE, // activesound +MF_DROPOFF|MF_NOGRAVITY|MF_FLOAT|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP|MF2_DONTDRAW // flags2 + }, + +{ // MT_WRAITH +34, // doomednum +S_WRAITH_INIT1, // spawnstate +150, // spawnhealth +S_WRAITH_CHASE1, // seestate +SFX_WRAITH_SIGHT, // seesound +8, // reactiontime +SFX_WRAITH_ATTACK, // attacksound +S_WRAITH_PAIN1, // painstate +25, // painchance +SFX_WRAITH_PAIN, // painsound +S_WRAITH_ATK1_1, // meleestate +S_WRAITH_ATK2_1, // missilestate +S_NULL, // crashstate +S_WRAITH_DEATH1_1, // deathstate +S_WRAITH_DEATH2_1, // xdeathstate +SFX_WRAITH_DEATH, // deathsound +11, // speed +20*FRACUNIT, // radius +55*FRACUNIT, // height +75, // mass +10, // damage +SFX_WRAITH_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_DROPOFF|MF_NOGRAVITY|MF_FLOAT, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP // flags2 + }, + +{ // MT_WRAITHFX1 +-1, // doomednum +S_WRTHFX_MOVE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_WRTHFX_BOOM1, // deathstate +S_NULL, // xdeathstate +SFX_WRAITH_MISSILE_EXPLODE, // deathsound +14*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +5, // mass +5, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_FLOORCLIP|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_WRAITHFX2 +-1, // doomednum +S_WRTHFX_SIZZLE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +2*FRACUNIT, // radius +5*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_WRAITHFX3 +-1, // doomednum +S_WRTHFX_DROP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_WRTHFX_DEAD1, // deathstate +S_NULL, // xdeathstate +SFX_DRIP, // deathsound +0, // speed +2*FRACUNIT, // radius +5*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_WRAITHFX4 +-1, // doomednum +S_WRTHFX_ADROP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_WRTHFX_ADEAD1, // deathstate +S_NULL, // xdeathstate +SFX_DRIP, // deathsound +0, // speed +2*FRACUNIT, // radius +5*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_WRAITHFX5 +-1, // doomednum +S_WRTHFX_BDROP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_WRTHFX_BDEAD1, // deathstate +S_NULL, // xdeathstate +SFX_DRIP, // deathsound +0, // speed +2*FRACUNIT, // radius +5*FRACUNIT, // height +5, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_MINOTAUR +9, // doomednum +S_MNTR_SPAWN1, // spawnstate +2500, // spawnhealth +S_MNTR_WALK1, // seestate +SFX_MAULATOR_SIGHT, // seesound +8, // reactiontime +SFX_MAULATOR_HAMMER_SWING, // attacksound +S_MNTR_PAIN1, // painstate +25, // painchance +SFX_MAULATOR_PAIN, // painsound +S_MNTR_ATK1_1, // meleestate +S_MNTR_ATK2_1, // missilestate +S_NULL, // crashstate +S_MNTR_DIE1, // deathstate +S_NULL, // xdeathstate +SFX_MAULATOR_DEATH, // deathsound +16, // speed +28*FRACUNIT, // radius +100*FRACUNIT, // height +800, // mass +7, // damage +SFX_MAULATOR_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_SHADOW, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP // flags2 + }, + +{ // MT_MNTRFX1 +-1, // doomednum +S_MNTRFX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MNTRFXI1_1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +20*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +3, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_MNTRFX2 +-1, // doomednum +S_MNTRFX2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MNTRFXI2_1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +14*FRACUNIT, // speed +5*FRACUNIT, // radius +12*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_MNTRFX3 +-1, // doomednum +S_MNTRFX3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +0, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_MNTRFXI2_1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_MNTRSMOKE +-1, // doomednum +S_MINOSMOKE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_MNTRSMOKEEXIT +-1, // doomednum +S_MINOSMOKEX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SERPENT +121, // doomednum +S_SERPENT_LOOK1, // spawnstate +90, // spawnhealth +S_SERPENT_SWIM1, // seestate +SFX_SERPENT_SIGHT, // seesound +8, // reactiontime +SFX_SERPENT_ATTACK, // attacksound +S_SERPENT_PAIN1, // painstate +96, // painchance +SFX_SERPENT_PAIN, // painsound +S_SERPENT_SURFACE1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SERPENT_DIE1, // deathstate +S_SERPENT_XDIE1, // xdeathstate +SFX_SERPENT_DEATH, // deathsound +12, // speed +32*FRACUNIT, // radius +70*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_COUNTKILL|MF_NOBLOOD, // flags +MF2_PASSMOBJ|MF2_DONTDRAW|MF2_CANTLEAVEFLOORPIC|MF2_NONSHOOTABLE|MF2_MCROSS // flags2 + }, + +{ // MT_SERPENTLEADER +120, // doomednum +S_SERPENT_LOOK1, // spawnstate +90, // spawnhealth +S_SERPENT_SWIM1, // seestate +SFX_SERPENT_SIGHT, // seesound +8, // reactiontime +SFX_SERPENT_ATTACK, // attacksound +S_SERPENT_PAIN1, // painstate +96, // painchance +SFX_SERPENT_PAIN, // painsound +S_SERPENT_SURFACE1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SERPENT_DIE1, // deathstate +S_SERPENT_XDIE1, // xdeathstate +SFX_SERPENT_DEATH, // deathsound +12, // speed +32*FRACUNIT, // radius +70*FRACUNIT, // height +200, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_COUNTKILL|MF_NOBLOOD, // flags +MF2_PASSMOBJ|MF2_DONTDRAW|MF2_CANTLEAVEFLOORPIC|MF2_NONSHOOTABLE|MF2_MCROSS // flags2 + }, + +{ // MT_SERPENTFX +-1, // doomednum +S_SERPENT_FX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +0, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SERPENT_FX_X1, // deathstate +S_NULL, // xdeathstate +SFX_SERPENTFX_HIT, // deathsound +15*FRACUNIT, // speed +8*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +4, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SERPENT_HEAD +-1, // doomednum +S_SERPENT_HEAD1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +MF2_LOGRAV // flags2 + }, + +{ // MT_SERPENT_GIB1 +-1, // doomednum +S_SERPENT_GIB1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +3*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_SERPENT_GIB2 +-1, // doomednum +S_SERPENT_GIB2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +3*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_SERPENT_GIB3 +-1, // doomednum +S_SERPENT_GIB3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +3*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_BISHOP +114, // doomednum +S_BISHOP_LOOK1, // spawnstate +130, // spawnhealth +S_BISHOP_WALK1, // seestate +SFX_BISHOP_SIGHT, // seesound +8, // reactiontime +SFX_BISHOP_ATTACK, // attacksound +S_BISHOP_PAIN1, // painstate +110, // painchance +SFX_BISHOP_PAIN, // painsound +0, // meleestate +S_BISHOP_ATK1, // missilestate +S_NULL, // crashstate +S_BISHOP_DEATH1, // deathstate +S_NULL, // xdeathstate +SFX_BISHOP_DEATH, // deathsound +10, // speed +22*FRACUNIT, // radius +65*FRACUNIT, // height +100, // mass +0, // damage +SFX_BISHOP_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_FLOAT|MF_NOGRAVITY|MF_NOBLOOD, // flags +MF2_PASSMOBJ|MF2_PUSHWALL|MF2_TELESTOMP // flags2 + }, + +{ // MT_BISHOP_PUFF +-1, // doomednum +S_BISHOP_PUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SHADOW|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_BISHOPBLUR +-1, // doomednum +S_BISHOPBLUR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_BISHOPPAINBLUR +-1, // doomednum +S_BISHOPPAINBLUR1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW, // flags +0 // flags2 + }, + +{ // MT_BISH_FX +-1, // doomednum +S_BISHFX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_BISHFXI1_1, // deathstate +S_NULL, // xdeathstate +SFX_BISHOP_MISSILE_EXPLODE, // deathsound +10*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_SEEKERMISSILE // flags2 + }, + +{ // MT_DRAGON +254, // doomednum +S_DRAGON_LOOK1, // spawnstate +640, // spawnhealth +S_DRAGON_INIT, // seestate +SFX_DRAGON_SIGHT, // seesound +8, // reactiontime +SFX_DRAGON_ATTACK, // attacksound +S_DRAGON_PAIN1, // painstate +128, // painchance +SFX_DRAGON_PAIN, // painsound +S_NULL, // meleestate +S_DRAGON_ATK1, // missilestate +S_NULL, // crashstate +S_DRAGON_DEATH1, // deathstate +S_NULL, // xdeathstate +SFX_DRAGON_DEATH, // deathsound +10*FRACUNIT, // speed +20*FRACUNIT, // radius +65*FRACUNIT, // height +MAXINT, // mass +0, // damage +SFX_DRAGON_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_FLOAT|MF_NOGRAVITY|MF_NOBLOOD, // flags +MF2_PASSMOBJ|MF2_BOSS // flags2 + }, + +{ // MT_DRAGON_FX +-1, // doomednum +S_DRAGON_FX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_DRAGON_FX1_X1, // deathstate +S_NULL, // xdeathstate +SFX_DRAGON_FIREBALL_EXPLODE, // deathsound +24*FRACUNIT, // speed +12*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +6, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_DRAGON_FX2 +-1, // doomednum +S_DRAGON_FX2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_DRAGON_FIREBALL_EXPLODE, // deathsound +0, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP, // flags +MF2_NOTELEPORT|MF2_FIREDAMAGE|MF2_DONTDRAW // flags2 + }, + +{ // MT_ARMOR_1 +8005, // doomednum +S_ARMOR_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARMOR_2 +8006, // doomednum +S_ARMOR_2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARMOR_3 +8007, // doomednum +S_ARMOR_3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_ARMOR_4 +8008, // doomednum +S_ARMOR_4, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL|MF_NOGRAVITY, // flags +0 // flags2 + }, + +{ // MT_MANA1 +122, // doomednum +S_MANA1_1, // spawnstate +10, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_MANA2 +124, // doomednum +S_MANA2_1, // spawnstate +10, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_MANA3 +8004, // doomednum +S_MANA3_1, // spawnstate +20, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +8*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +MF2_FLOATBOB // flags2 + }, + +{ // MT_KEY1 +8030, // doomednum +S_KEY1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY2 +8031, // doomednum +S_KEY2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY3 +8032, // doomednum +S_KEY3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY4 +8033, // doomednum +S_KEY4, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY5 +8034, // doomednum +S_KEY5, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY6 +8035, // doomednum +S_KEY6, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY7 +8036, // doomednum +S_KEY7, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY8 +8037, // doomednum +S_KEY8, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEY9 +8038, // doomednum +S_KEY9, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEYA +8039, // doomednum +S_KEYA, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_KEYB +8200, // doomednum +S_KEYB, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +8*FRACUNIT, // radius +20*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SPECIAL, // flags +0 // flags2 + }, + +{ // MT_SOUNDWIND +1410, // doomednum +S_SND_WIND1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +0 // flags2 + }, + +{ // MT_SOUNDWATERFALL +41, // doomednum +S_SND_WATERFALL, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR, // flags +0 // flags2 + }, + +{ // MT_ETTIN +10030, // doomednum +S_ETTIN_LOOK1, // spawnstate +175, // spawnhealth +S_ETTIN_CHASE1, // seestate +SFX_ETTIN_SIGHT, // seesound +8, // reactiontime +SFX_ETTIN_ATTACK, // attacksound +S_ETTIN_PAIN1, // painstate +60, // painchance +SFX_ETTIN_PAIN, // painsound +S_ETTIN_ATK1_1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ETTIN_DEATH1_1, // deathstate +S_ETTIN_DEATH2_1, // xdeathstate +SFX_ETTIN_DEATH, // deathsound +13, // speed +25*FRACUNIT, // radius +68*FRACUNIT, // height +175, // mass +3, // damage +SFX_ETTIN_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PUSHWALL|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_ETTIN_MACE +-1, // doomednum +S_ETTIN_MACE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ETTIN_MACE5, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_FIREDEMON +10060, // doomednum +S_FIRED_SPAWN1, // spawnstate +80, // spawnhealth +S_FIRED_LOOK4, // seestate +SFX_FIRED_SPAWN, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_FIRED_PAIN1, // painstate +1, // painchance +SFX_FIRED_PAIN, // painsound +S_NULL, // meleestate +S_FIRED_ATTACK1, // missilestate +S_FIRED_XDEATH1, // crashstate +S_FIRED_DEATH1, // deathstate +S_FIRED_XDEATH1, // xdeathstate +SFX_FIRED_DEATH, // deathsound +13, // speed +20*FRACUNIT, // radius +68*FRACUNIT, // height +75, // mass +1, // damage +SFX_FIRED_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_DROPOFF|MF_NOGRAVITY|MF_FLOAT, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_INVULNERABLE|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_FIREDEMON_SPLOTCH1 +-1, // doomednum +S_FIRED_CORPSE1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_FIREDEMON_SPLOTCH2 +-1, // doomednum +S_FIRED_CORPSE4, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_DROPOFF|MF_CORPSE, // flags +MF2_NOTELEPORT|MF2_FLOORCLIP // flags2 + }, + +{ // MT_FIREDEMON_FX1 +-1, // doomednum +S_FIRED_RDROP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_RDEAD1_1, // deathstate +S_FIRED_RDEAD1_2, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +5*FRACUNIT, // height +16, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIREDEMON_FX2 +-1, // doomednum +S_FIRED_RDROP2, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_RDEAD2_1, // deathstate +S_FIRED_RDEAD2_2, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +5*FRACUNIT, // height +16, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIREDEMON_FX3 +-1, // doomednum +S_FIRED_RDROP3, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_RDEAD3_1, // deathstate +S_FIRED_RDEAD3_2, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +5*FRACUNIT, // height +16, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIREDEMON_FX4 +-1, // doomednum +S_FIRED_RDROP4, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_RDEAD4_1, // deathstate +S_FIRED_RDEAD4_2, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +5*FRACUNIT, // height +16, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIREDEMON_FX5 +-1, // doomednum +S_FIRED_RDROP5, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_RDEAD5_1, // deathstate +S_FIRED_RDEAD5_2, // xdeathstate +SFX_NONE, // deathsound +0, // speed +3*FRACUNIT, // radius +5*FRACUNIT, // height +16, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIREDEMON_FX6 +-1, // doomednum +S_FIRED_FX6_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_FIRED_FX6_2, // deathstate +S_NULL, // xdeathstate +SFX_FIRED_MISSILE_HIT, // deathsound +10*FRACUNIT, // speed +10*FRACUNIT, // radius +6*FRACUNIT, // height +15, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS|MF2_FLOORCLIP|MF2_FIREDAMAGE // flags2 + }, + +{ // MT_ICEGUY +8020, // doomednum +S_ICEGUY_LOOK, // spawnstate +120, // spawnhealth +S_ICEGUY_WALK1, // seestate +SFX_ICEGUY_SIGHT, // seesound +8, // reactiontime +SFX_ICEGUY_ATTACK, // attacksound +S_ICEGUY_PAIN1, // painstate +144, // painchance +SFX_NONE, // painsound +0, // meleestate +S_ICEGUY_ATK1, // missilestate +S_NULL, // crashstate +S_ICEGUY_DEATH, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +14, // speed +22*FRACUNIT, // radius +75*FRACUNIT, // height +150, // mass +0, // damage +SFX_ICEGUY_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_NOBLOOD, // flags +MF2_PASSMOBJ|MF2_PUSHWALL|MF2_ICEDAMAGE|MF2_MCROSS|MF2_TELESTOMP // flags2 + }, + +{ // MT_ICEGUY_FX +-1, // doomednum +S_ICEGUY_FX1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_ICEGUY_FX_X1, // deathstate +S_NULL, // xdeathstate +SFX_ICEGUY_FX_EXPLODE, // deathsound +14*FRACUNIT, // speed +8*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags +MF2_NOTELEPORT|MF2_ICEDAMAGE // flags2 + }, + +{ // MT_ICEFX_PUFF +-1, // doomednum +S_ICEFX_PUFF1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +FRACUNIT, // radius +FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SHADOW|MF_DROPOFF, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ICEGUY_FX2 +-1, // doomednum +S_ICEGUY_FX2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +10*FRACUNIT, // speed +4*FRACUNIT, // radius +4*FRACUNIT, // height +100, // mass +1, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_LOGRAV|MF2_ICEDAMAGE // flags2 + }, + +{ // MT_ICEGUY_BIT +-1, // doomednum +S_ICEGUY_BIT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +FRACUNIT, // radius +FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_ICEGUY_WISP1 +-1, // doomednum +S_ICEGUY_WISP1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_ICEGUY_WISP2 +-1, // doomednum +S_ICEGUY_WISP2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_FIGHTER_BOSS +10100, // doomednum +S_FIGHTER, // spawnstate +800, // spawnhealth +S_FIGHTER_RUN1, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_FIGHTER_PAIN, // painstate +50, // painchance +SFX_PLAYER_FIGHTER_PAIN, // painsound +S_FIGHTER_ATK1, // meleestate +S_FIGHTER_ATK1, // missilestate +S_NULL, // crashstate +S_FIGHTER_DIE1, // deathstate +S_FIGHTER_XDIE1, // xdeathstate +SFX_PLAYER_FIGHTER_CRAZY_DEATH, // deathsound +25, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL|MF2_MCROSS // flags2 + }, + +{ // MT_CLERIC_BOSS +10101, // doomednum +S_CLERIC, // spawnstate +800, // spawnhealth +S_CLERIC_RUN1, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_CLERIC_PAIN, // painstate +50, // painchance +SFX_PLAYER_CLERIC_PAIN, // painsound +S_CLERIC_ATK1, // meleestate +S_CLERIC_ATK1, // missilestate +S_NULL, // crashstate +S_CLERIC_DIE1, // deathstate +S_CLERIC_XDIE1, // xdeathstate +SFX_PLAYER_CLERIC_CRAZY_DEATH, // deathsound +25, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL|MF2_MCROSS // flags2 + }, + +{ // MT_MAGE_BOSS +10102, // doomednum +S_MAGE, // spawnstate +800, // spawnhealth +S_MAGE_RUN1, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_MAGE_PAIN, // painstate +50, // painchance +SFX_PLAYER_MAGE_PAIN, // painsound +S_MAGE_ATK1, // meleestate +S_MAGE_ATK1, // missilestate +S_NULL, // crashstate +S_MAGE_DIE1, // deathstate +S_MAGE_XDIE1, // xdeathstate +SFX_PLAYER_MAGE_CRAZY_DEATH, // deathsound +25, // speed +16*FRACUNIT, // radius +64*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL|MF2_MCROSS // flags2 + }, + +{ // MT_SORCBOSS +10080, // doomednum +S_SORC_SPAWN1, // spawnstate +5000, // spawnhealth +S_SORC_WALK1, // seestate +SFX_SORCERER_SIGHT, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_SORC_PAIN1, // painstate +10, // painchance +SFX_SORCERER_PAIN, // painsound +S_NULL, // meleestate +S_SORC_ATK2_1, // missilestate +S_NULL, // crashstate +S_SORC_DIE1, // deathstate +S_NULL, // xdeathstate +SFX_SORCERER_DEATHSCREAM, // deathsound +16, // speed +40*FRACUNIT, // radius +110*FRACUNIT, // height +500, // mass +9, // damage +SFX_SORCERER_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_NOBLOOD, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_BOSS|MF2_MCROSS // flags2 + }, + +{ // MT_SORCBALL1 +-1, // doomednum +S_SORCBALL1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_SORCERER_BALLBOUNCE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_SORCBALL1_D1, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCBALL1_D5, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +10*FRACUNIT, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SORCBALL2 +-1, // doomednum +S_SORCBALL2_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_SORCERER_BALLBOUNCE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_SORCBALL2_D1, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCBALL2_D5, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +10*FRACUNIT, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SORCBALL3 +-1, // doomednum +S_SORCBALL3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_SORCERER_BALLBOUNCE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_SORCBALL3_D1, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCBALL3_D5, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +10*FRACUNIT, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SORCFX1 +-1, // doomednum +S_SORCFX1_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_SORCERER_BALLBOUNCE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCFX1_D1, // deathstate +S_SORCFX1_D1, // xdeathstate +SFX_NONE, // deathsound +7*FRACUNIT, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE, // flags +MF2_NOTELEPORT|MF2_FLOORBOUNCE // flags2 + }, + +{ // MT_SORCFX2 +-1, // doomednum +S_SORCFX2_SPLIT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCFX2T1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +15*FRACUNIT, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SORCFX2_T1 +-1, // doomednum +S_SORCFX2T1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_ALTSHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SORCFX3 +-1, // doomednum +S_SORCFX3_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_SORCERER_BISHOPSPAWN, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_BISHMORPH1, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +15*FRACUNIT, // speed +22*FRACUNIT, // radius +65*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SORCFX3_EXPLOSION +-1, // doomednum +S_SORCFX3_EXP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_ALTSHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SORCFX4 +-1, // doomednum +S_SORCFX4_1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_SORCFX4_D1, // deathstate +S_NULL, // xdeathstate +SFX_SORCERER_BALLEXPLODE, // deathsound +12*FRACUNIT, // speed +10*FRACUNIT, // radius +10*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_SORCSPARK1 +-1, // doomednum +S_SORCSPARK1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +5*FRACUNIT, // radius +5*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF, // flags +MF2_NOTELEPORT|MF2_LOGRAV // flags2 + }, + +{ // MT_BLASTEFFECT +-1, // doomednum +S_BLASTEFFECT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_ALTSHADOW, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_WATER_DRIP +-1, // doomednum +S_WATERDRIP1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_DRIP, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +1, // mass +0, // damage +SFX_NONE, // activesound +MF_MISSILE, // flags +MF2_LOGRAV|MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX +10200, // doomednum +S_KORAX_LOOK1, // spawnstate +5000, // spawnhealth +S_KORAX_CHASE2, // seestate +SFX_KORAX_SIGHT, // seesound +8, // reactiontime +SFX_KORAX_ATTACK, // attacksound +S_KORAX_PAIN1, // painstate +20, // painchance +SFX_KORAX_PAIN, // painsound +S_NULL, // meleestate +S_KORAX_ATTACK1, // missilestate +S_NULL, // crashstate +S_KORAX_DEATH1, // deathstate +S_NULL, // xdeathstate +SFX_KORAX_DEATH, // deathsound +10, // speed +65*FRACUNIT, // radius +115*FRACUNIT, // height +2000, // mass +15, // damage +SFX_KORAX_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags +MF2_FLOORCLIP|MF2_PUSHWALL|MF2_MCROSS|MF2_TELESTOMP|MF2_BOSS // flags2 + }, + +{ // MT_KORAX_SPIRIT1 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX_SPIRIT2 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX_SPIRIT3 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX_SPIRIT4 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX_SPIRIT5 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_KORAX_SPIRIT6 +-1, // doomednum +S_KSPIRIT_ROAM1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +8*FRACUNIT, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_DROPOFF|MF_NOGRAVITY|MF_ALTSHADOW|MF_MISSILE|MF_NOCLIP, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_DEMON_MASH +-1, // doomednum +S_DEMN_LOOK1, // spawnstate +250, // spawnhealth +S_DEMN_CHASE1, // seestate +SFX_DEMON_SIGHT, // seesound +8, // reactiontime +SFX_DEMON_ATTACK, // attacksound +S_DEMN_PAIN1, // painstate +50, // painchance +SFX_DEMON_PAIN, // painsound +S_DEMN_ATK1_1, // meleestate +S_DEMN_ATK2_1, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_DEMON_DEATH, // deathsound +13, // speed +32*FRACUNIT, // radius +64*FRACUNIT, // height +220, // mass +0, // damage +SFX_DEMON_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALTSHADOW|MF_NOBLOOD, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_PUSHWALL|MF2_BLASTED // flags2 + }, + +{ // MT_DEMON2_MASH +-1, // doomednum +S_DEMN2_LOOK1, // spawnstate +250, // spawnhealth +S_DEMN2_CHASE1, // seestate +SFX_DEMON_SIGHT, // seesound +8, // reactiontime +SFX_DEMON_ATTACK, // attacksound +S_DEMN2_PAIN1, // painstate +50, // painchance +SFX_DEMON_PAIN, // painsound +S_DEMN2_ATK1_1, // meleestate +S_DEMN2_ATK2_1, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_DEMON_DEATH, // deathsound +13, // speed +32*FRACUNIT, // radius +64*FRACUNIT, // height +220, // mass +0, // damage +SFX_DEMON_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALTSHADOW|MF_NOBLOOD, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_PUSHWALL|MF2_BLASTED // flags2 + }, + +{ // MT_ETTIN_MASH +-1, // doomednum +S_ETTIN_LOOK1, // spawnstate +175, // spawnhealth +S_ETTIN_CHASE1, // seestate +SFX_ETTIN_SIGHT, // seesound +8, // reactiontime +SFX_ETTIN_ATTACK, // attacksound +S_ETTIN_PAIN1, // painstate +60, // painchance +SFX_ETTIN_PAIN, // painsound +S_ETTIN_ATK1_1, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_ETTIN_DEATH, // deathsound +13, // speed +25*FRACUNIT, // radius +68*FRACUNIT, // height +175, // mass +3, // damage +SFX_ETTIN_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALTSHADOW|MF_NOBLOOD, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_PUSHWALL|MF2_BLASTED // flags2 + }, + +{ // MT_CENTAUR_MASH +-1, // doomednum +S_CENTAUR_LOOK1, // spawnstate +200, // spawnhealth +S_CENTAUR_WALK1, // seestate +SFX_CENTAUR_SIGHT, // seesound +8, // reactiontime +SFX_CENTAUR_ATTACK, // attacksound +S_CENTAUR_PAIN1, // painstate +135, // painchance +SFX_CENTAUR_PAIN, // painsound +S_CENTAUR_ATK1, // meleestate +0, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_CENTAUR_DEATH, // deathsound +13, // speed +20*FRACUNIT, // radius +64*FRACUNIT, // height +120, // mass +0, // damage +SFX_CENTAUR_ACTIVE, // activesound +MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALTSHADOW|MF_NOBLOOD, // flags +MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_MCROSS|MF2_PUSHWALL|MF2_BLASTED // flags2 + }, + +{ // MT_KORAX_BOLT +-1, // doomednum +S_KBOLT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +15*FRACUNIT, // radius +35*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE|MF_DROPOFF, // flags +MF2_NOTELEPORT // flags2 + }, + +{ // MT_BAT_SPAWNER +10225, // doomednum +S_SPAWNBATS1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_NULL, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +0, // speed +20*FRACUNIT, // radius +16*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY, // flags +MF2_DONTDRAW // flags2 + }, + +{ // MT_BAT +-1, // doomednum +S_BAT1, // spawnstate +1000, // spawnhealth +S_NULL, // seestate +SFX_NONE, // seesound +8, // reactiontime +SFX_NONE, // attacksound +S_NULL, // painstate +0, // painchance +SFX_NONE, // painsound +S_NULL, // meleestate +S_NULL, // missilestate +S_NULL, // crashstate +S_BAT_DEATH, // deathstate +S_NULL, // xdeathstate +SFX_NONE, // deathsound +5*FRACUNIT, // speed +3*FRACUNIT, // radius +3*FRACUNIT, // height +100, // mass +0, // damage +SFX_NONE, // activesound +MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE, // flags +MF2_PASSMOBJ|MF2_NOTELEPORT // flags2 + } +}; + diff --git a/src/hexen/info.h b/src/hexen/info.h new file mode 100644 index 00000000..fbb94e75 --- /dev/null +++ b/src/hexen/info.h @@ -0,0 +1,3601 @@ +// generated by stateco + +typedef enum { +SPR_MAN1, +SPR_ACLO, +SPR_TLGL, +SPR_FBL1, +SPR_XPL1, +SPR_ARRW, +SPR_DART, +SPR_RIPP, +SPR_CFCF, +SPR_BLAD, +SPR_SHRD, +SPR_FFSM, +SPR_FFLG, +SPR_PTN1, +SPR_PTN2, +SPR_SOAR, +SPR_INVU, +SPR_SUMN, +SPR_TSPK, +SPR_TELO, +SPR_TRNG, +SPR_ROCK, +SPR_FOGS, +SPR_FOGM, +SPR_FOGL, +SPR_SGSA, +SPR_SGSB, +SPR_PORK, +SPR_EGGM, +SPR_FHFX, +SPR_SPHL, +SPR_STWN, +SPR_GMPD, +SPR_ASKU, +SPR_ABGM, +SPR_AGMR, +SPR_AGMG, +SPR_AGG2, +SPR_AGMB, +SPR_AGB2, +SPR_ABK1, +SPR_ABK2, +SPR_ASK2, +SPR_AFWP, +SPR_ACWP, +SPR_AMWP, +SPR_AGER, +SPR_AGR2, +SPR_AGR3, +SPR_AGR4, +SPR_TRCH, +SPR_PSBG, +SPR_ATLP, +SPR_THRW, +SPR_SPED, +SPR_BMAN, +SPR_BRAC, +SPR_BLST, +SPR_HRAD, +SPR_SPSH, +SPR_LVAS, +SPR_SLDG, +SPR_STTW, +SPR_RCK1, +SPR_RCK2, +SPR_RCK3, +SPR_RCK4, +SPR_CDLR, +SPR_TRE1, +SPR_TRDT, +SPR_TRE2, +SPR_TRE3, +SPR_STM1, +SPR_STM2, +SPR_STM3, +SPR_STM4, +SPR_MSH1, +SPR_MSH2, +SPR_MSH3, +SPR_MSH4, +SPR_MSH5, +SPR_MSH6, +SPR_MSH7, +SPR_MSH8, +SPR_SGMP, +SPR_SGM1, +SPR_SGM2, +SPR_SGM3, +SPR_SLC1, +SPR_SLC2, +SPR_SLC3, +SPR_MSS1, +SPR_MSS2, +SPR_SWMV, +SPR_CPS1, +SPR_CPS2, +SPR_TMS1, +SPR_TMS2, +SPR_TMS3, +SPR_TMS4, +SPR_TMS5, +SPR_TMS6, +SPR_TMS7, +SPR_CPS3, +SPR_STT2, +SPR_STT3, +SPR_STT4, +SPR_STT5, +SPR_GAR1, +SPR_GAR2, +SPR_GAR3, +SPR_GAR4, +SPR_GAR5, +SPR_GAR6, +SPR_GAR7, +SPR_GAR8, +SPR_GAR9, +SPR_BNR1, +SPR_TRE4, +SPR_TRE5, +SPR_TRE6, +SPR_TRE7, +SPR_LOGG, +SPR_ICT1, +SPR_ICT2, +SPR_ICT3, +SPR_ICT4, +SPR_ICM1, +SPR_ICM2, +SPR_ICM3, +SPR_ICM4, +SPR_RKBL, +SPR_RKBS, +SPR_RKBK, +SPR_RBL1, +SPR_RBL2, +SPR_RBL3, +SPR_VASE, +SPR_POT1, +SPR_POT2, +SPR_POT3, +SPR_PBIT, +SPR_CPS4, +SPR_CPS5, +SPR_CPS6, +SPR_CPB1, +SPR_CPB2, +SPR_CPB3, +SPR_CPB4, +SPR_BDRP, +SPR_BDSH, +SPR_BDPL, +SPR_CNDL, +SPR_LEF1, +SPR_LEF3, +SPR_LEF2, +SPR_TWTR, +SPR_WLTR, +SPR_BARL, +SPR_SHB1, +SPR_SHB2, +SPR_BCKT, +SPR_SHRM, +SPR_FBUL, +SPR_FSKL, +SPR_BRTR, +SPR_SUIT, +SPR_BBLL, +SPR_CAND, +SPR_IRON, +SPR_XMAS, +SPR_CDRN, +SPR_CHNS, +SPR_TST1, +SPR_TST2, +SPR_TST3, +SPR_TST4, +SPR_TST5, +SPR_TST6, +SPR_TST7, +SPR_TST8, +SPR_TST9, +SPR_TST0, +SPR_TELE, +SPR_TSMK, +SPR_FPCH, +SPR_WFAX, +SPR_FAXE, +SPR_WFHM, +SPR_FHMR, +SPR_FSRD, +SPR_FSFX, +SPR_CMCE, +SPR_WCSS, +SPR_CSSF, +SPR_WCFM, +SPR_CFLM, +SPR_CFFX, +SPR_CHLY, +SPR_SPIR, +SPR_MWND, +SPR_WMLG, +SPR_MLNG, +SPR_MLFX, +SPR_MLF2, +SPR_MSTF, +SPR_MSP1, +SPR_MSP2, +SPR_WFR1, +SPR_WFR2, +SPR_WFR3, +SPR_WCH1, +SPR_WCH2, +SPR_WCH3, +SPR_WMS1, +SPR_WMS2, +SPR_WMS3, +SPR_WPIG, +SPR_WMCS, +SPR_CONE, +SPR_SHEX, +SPR_BLOD, +SPR_GIBS, +SPR_PLAY, +SPR_FDTH, +SPR_BSKL, +SPR_ICEC, +SPR_CLER, +SPR_MAGE, +SPR_PIGY, +SPR_CENT, +SPR_CTXD, +SPR_CTFX, +SPR_CTDP, +SPR_DEMN, +SPR_DEMA, +SPR_DEMB, +SPR_DEMC, +SPR_DEMD, +SPR_DEME, +SPR_DMFX, +SPR_DEM2, +SPR_DMBA, +SPR_DMBB, +SPR_DMBC, +SPR_DMBD, +SPR_DMBE, +SPR_D2FX, +SPR_WRTH, +SPR_WRT2, +SPR_WRBL, +SPR_MNTR, +SPR_FX12, +SPR_FX13, +SPR_MNSM, +SPR_SSPT, +SPR_SSDV, +SPR_SSXD, +SPR_SSFX, +SPR_BISH, +SPR_BPFX, +SPR_DRAG, +SPR_DRFX, +SPR_ARM1, +SPR_ARM2, +SPR_ARM3, +SPR_ARM4, +SPR_MAN2, +SPR_MAN3, +SPR_KEY1, +SPR_KEY2, +SPR_KEY3, +SPR_KEY4, +SPR_KEY5, +SPR_KEY6, +SPR_KEY7, +SPR_KEY8, +SPR_KEY9, +SPR_KEYA, +SPR_KEYB, +SPR_ETTN, +SPR_ETTB, +SPR_FDMN, +SPR_FDMB, +SPR_ICEY, +SPR_ICPR, +SPR_ICWS, +SPR_SORC, +SPR_SBMP, +SPR_SBS4, +SPR_SBMB, +SPR_SBS3, +SPR_SBMG, +SPR_SBS1, +SPR_SBS2, +SPR_SBFX, +SPR_RADE, +SPR_WATR, +SPR_KORX, +SPR_ABAT, +NUMSPRITES +} spritenum_t; + +typedef enum { +S_NULL, +S_FREETARGMOBJ, +S_MAPSPOT, +S_FIREBALL1_1, +S_FIREBALL1_2, +S_FIREBALL1_X1, +S_FIREBALL1_X2, +S_FIREBALL1_X3, +S_FIREBALL1_X4, +S_FIREBALL1_X5, +S_FIREBALL1_X6, +S_ARROW_1, +S_ARROW_X1, +S_DART_1, +S_DART_X1, +S_POISONDART_1, +S_POISONDART_X1, +S_RIPPERBALL_1, +S_RIPPERBALL_2, +S_RIPPERBALL_3, +S_RIPPERBALL_X1, +S_RIPPERBALL_X2, +S_RIPPERBALL_X3, +S_RIPPERBALL_X4, +S_RIPPERBALL_X5, +S_RIPPERBALL_X6, +S_RIPPERBALL_X7, +S_RIPPERBALL_X8, +S_RIPPERBALL_X9, +S_RIPPERBALL_X10, +S_PRJ_BLADE1, +S_PRJ_BLADE_X1, +S_ICESHARD1, +S_ICESHARD2, +S_ICESHARD3, +S_FLAME_TSMALL1, +S_FLAME_TSMALL2, +S_FLAME_TSMALL3, +S_FLAME_TSMALL4, +S_FLAME_TSMALL5, +S_FLAME_TSMALL6, +S_FLAME_TLARGE1, +S_FLAME_TLARGE2, +S_FLAME_TLARGE3, +S_FLAME_TLARGE4, +S_FLAME_TLARGE5, +S_FLAME_TLARGE6, +S_FLAME_TLARGE7, +S_FLAME_TLARGE8, +S_FLAME_TLARGE9, +S_FLAME_TLARGE10, +S_FLAME_TLARGE11, +S_FLAME_TLARGE12, +S_FLAME_TLARGE13, +S_FLAME_TLARGE14, +S_FLAME_TLARGE15, +S_FLAME_TLARGE16, +S_FLAME_SDORM1, +S_FLAME_SDORM2, +S_FLAME_SDORM3, +S_FLAME_SMALL1, +S_FLAME_SMALL2, +S_FLAME_SMALL3, +S_FLAME_SMALL4, +S_FLAME_SMALL5, +S_FLAME_SMALL6, +S_FLAME_SMALL7, +S_FLAME_LDORM1, +S_FLAME_LDORM2, +S_FLAME_LDORM3, +S_FLAME_LDORM4, +S_FLAME_LDORM5, +S_FLAME_LARGE1, +S_FLAME_LARGE2, +S_FLAME_LARGE3, +S_FLAME_LARGE4, +S_FLAME_LARGE5, +S_FLAME_LARGE6, +S_FLAME_LARGE7, +S_FLAME_LARGE8, +S_FLAME_LARGE9, +S_FLAME_LARGE10, +S_FLAME_LARGE11, +S_FLAME_LARGE12, +S_FLAME_LARGE13, +S_FLAME_LARGE14, +S_FLAME_LARGE15, +S_FLAME_LARGE16, +S_FLAME_LARGE17, +S_FLAME_LARGE18, +S_ITEM_PTN1_1, +S_ITEM_PTN1_2, +S_ITEM_PTN1_3, +S_HIDESPECIAL1, +S_HIDESPECIAL2, +S_HIDESPECIAL3, +S_HIDESPECIAL4, +S_HIDESPECIAL5, +S_HIDESPECIAL6, +S_HIDESPECIAL7, +S_HIDESPECIAL8, +S_HIDESPECIAL9, +S_HIDESPECIAL10, +S_HIDESPECIAL11, +S_DORMANTARTI1_1, +S_DORMANTARTI1_2, +S_DORMANTARTI1_3, +S_DORMANTARTI1_4, +S_DORMANTARTI1_5, +S_DORMANTARTI1_6, +S_DORMANTARTI1_7, +S_DORMANTARTI1_8, +S_DORMANTARTI1_9, +S_DORMANTARTI1_10, +S_DORMANTARTI1_11, +S_DORMANTARTI1_12, +S_DORMANTARTI1_13, +S_DORMANTARTI1_14, +S_DORMANTARTI1_15, +S_DORMANTARTI1_16, +S_DORMANTARTI1_17, +S_DORMANTARTI1_18, +S_DORMANTARTI1_19, +S_DORMANTARTI1_20, +S_DORMANTARTI1_21, +S_DORMANTARTI2_1, +S_DORMANTARTI2_2, +S_DORMANTARTI2_3, +S_DORMANTARTI2_4, +S_DORMANTARTI2_5, +S_DORMANTARTI2_6, +S_DORMANTARTI2_7, +S_DORMANTARTI2_8, +S_DORMANTARTI2_9, +S_DORMANTARTI2_10, +S_DORMANTARTI2_11, +S_DORMANTARTI2_12, +S_DORMANTARTI2_13, +S_DORMANTARTI2_14, +S_DORMANTARTI2_15, +S_DORMANTARTI2_16, +S_DORMANTARTI2_17, +S_DORMANTARTI2_18, +S_DORMANTARTI2_19, +S_DORMANTARTI2_20, +S_DORMANTARTI2_21, +S_DORMANTARTI3_1, +S_DORMANTARTI3_2, +S_DORMANTARTI3_3, +S_DORMANTARTI3_4, +S_DORMANTARTI3_5, +S_DORMANTARTI3_6, +S_DORMANTARTI3_7, +S_DORMANTARTI3_8, +S_DORMANTARTI3_9, +S_DORMANTARTI3_10, +S_DORMANTARTI3_11, +S_DORMANTARTI3_12, +S_DORMANTARTI3_13, +S_DORMANTARTI3_14, +S_DORMANTARTI3_15, +S_DORMANTARTI3_16, +S_DORMANTARTI3_17, +S_DORMANTARTI3_18, +S_DORMANTARTI3_19, +S_DORMANTARTI3_20, +S_DORMANTARTI3_21, +S_DEADARTI1, +S_DEADARTI2, +S_DEADARTI3, +S_DEADARTI4, +S_DEADARTI5, +S_DEADARTI6, +S_DEADARTI7, +S_DEADARTI8, +S_DEADARTI9, +S_DEADARTI10, +S_ARTI_PTN2_1, +S_ARTI_PTN2_2, +S_ARTI_PTN2_3, +S_ARTI_SOAR1, +S_ARTI_SOAR2, +S_ARTI_SOAR3, +S_ARTI_SOAR4, +S_ARTI_INVU1, +S_ARTI_INVU2, +S_ARTI_INVU3, +S_ARTI_INVU4, +S_ARTI_SUMMON, +S_SUMMON_FX1_1, +S_SUMMON_FX2_1, +S_SUMMON_FX2_2, +S_SUMMON_FX2_3, +S_THRUSTINIT2_1, +S_THRUSTINIT2_2, +S_BTHRUSTINIT2_1, +S_BTHRUSTINIT2_2, +S_THRUSTINIT1_1, +S_THRUSTINIT1_2, +S_BTHRUSTINIT1_1, +S_BTHRUSTINIT1_2, +S_THRUSTRAISE1, +S_THRUSTRAISE2, +S_THRUSTRAISE3, +S_THRUSTRAISE4, +S_BTHRUSTRAISE1, +S_BTHRUSTRAISE2, +S_BTHRUSTRAISE3, +S_BTHRUSTRAISE4, +S_THRUSTIMPALE, +S_BTHRUSTIMPALE, +S_THRUSTRAISE, +S_BTHRUSTRAISE, +S_THRUSTBLOCK, +S_BTHRUSTBLOCK, +S_THRUSTLOWER, +S_BTHRUSTLOWER, +S_THRUSTSTAY, +S_BTHRUSTSTAY, +S_ARTI_TELOTHER1, +S_ARTI_TELOTHER2, +S_ARTI_TELOTHER3, +S_ARTI_TELOTHER4, +S_TELO_FX1, +S_TELO_FX2, +S_TELO_FX3, +S_TELO_FX4, +S_TELO_FX5, +S_TELO_FX6, +S_TELO_FX7, +S_TELO_FX8, +S_TELO_FX9, +S_TELO_FX2_1, +S_TELO_FX2_2, +S_TELO_FX2_3, +S_TELO_FX2_4, +S_TELO_FX2_5, +S_TELO_FX2_6, +S_TELO_FX3_1, +S_TELO_FX3_2, +S_TELO_FX3_3, +S_TELO_FX3_4, +S_TELO_FX3_5, +S_TELO_FX3_6, +S_TELO_FX4_1, +S_TELO_FX4_2, +S_TELO_FX4_3, +S_TELO_FX4_4, +S_TELO_FX4_5, +S_TELO_FX4_6, +S_TELO_FX5_1, +S_TELO_FX5_2, +S_TELO_FX5_3, +S_TELO_FX5_4, +S_TELO_FX5_5, +S_TELO_FX5_6, +S_DIRT1_1, +S_DIRT1_D, +S_DIRT2_1, +S_DIRT2_D, +S_DIRT3_1, +S_DIRT3_D, +S_DIRT4_1, +S_DIRT4_D, +S_DIRT5_1, +S_DIRT5_D, +S_DIRT6_1, +S_DIRT6_D, +S_DIRTCLUMP1, +S_ROCK1_1, +S_ROCK1_D, +S_ROCK2_1, +S_ROCK2_D, +S_ROCK3_1, +S_ROCK3_D, +S_SPAWNFOG1, +S_FOGPATCHS1, +S_FOGPATCHS2, +S_FOGPATCHS3, +S_FOGPATCHS4, +S_FOGPATCHS5, +S_FOGPATCHS0, +S_FOGPATCHM1, +S_FOGPATCHM2, +S_FOGPATCHM3, +S_FOGPATCHM4, +S_FOGPATCHM5, +S_FOGPATCHM0, +S_FOGPATCHMA, +S_FOGPATCHMB, +S_FOGPATCHMC, +S_FOGPATCHMD, +S_FOGPATCHL1, +S_FOGPATCHL2, +S_FOGPATCHL3, +S_FOGPATCHL4, +S_FOGPATCHL5, +S_FOGPATCHL0, +S_FOGPATCHLA, +S_FOGPATCHLB, +S_FOGPATCHLC, +S_FOGPATCHLD, +S_QUAKE_ACTIVE1, +S_QUAKE_ACTIVE2, +S_QUAKE_ACTIVE3, +S_QUAKE_ACTIVE4, +S_QUAKE_ACTIVE5, +S_QUAKE_ACTIVE6, +S_QUAKE_ACTIVE7, +S_QUAKE_ACTIVE8, +S_QUAKE_ACTIVE9, +S_QUAKE_ACTIVE0, +S_QUAKE_ACTIVEA, +S_QUAKE_ACTIVEB, +S_QUAKE_ACTIVEC, +S_QUAKE_ACTIVED, +S_QUAKE_ACTIVEE, +S_QUAKE_ACTIVEF, +S_QUAKE_ACTIVEG, +S_QUAKE_ACTIVEH, +S_QUAKE_ACTIVEI, +S_QUAKE_ACTIVEJ, +S_QUAKE_ACTIVEK, +S_QUAKE_ACTIVEL, +S_QUAKE_ACTIVEM, +S_QUAKE_ACTIVEN, +S_QUAKE_ACTIVEO, +S_QUAKE_ACTIVEP, +S_QUAKE_ACTIVEQ, +S_QUAKE_ACTIVER, +S_QUAKE_ACTIVES, +S_QUAKE_ACTIVET, +S_QUAKE_ACTIVEU, +S_QUAKE_ACTIVEV, +S_QUAKE_ACTIVEW, +S_QUAKE_ACTIVEX, +S_QUAKE_ACTIVEY, +S_QUAKE_ACTIVEZ, +S_QUAKE_ACT1, +S_QUAKE_ACT2, +S_QUAKE_ACT3, +S_QUAKE_ACT4, +S_QUAKE_ACT5, +S_QUAKE_ACT6, +S_QUAKE_ACT7, +S_QUAKE_ACT8, +S_QUAKE_ACT9, +S_QUAKE_ACT0, +S_SGSHARD1_1, +S_SGSHARD1_2, +S_SGSHARD1_3, +S_SGSHARD1_4, +S_SGSHARD1_5, +S_SGSHARD1_D, +S_SGSHARD2_1, +S_SGSHARD2_2, +S_SGSHARD2_3, +S_SGSHARD2_4, +S_SGSHARD2_5, +S_SGSHARD2_D, +S_SGSHARD3_1, +S_SGSHARD3_2, +S_SGSHARD3_3, +S_SGSHARD3_4, +S_SGSHARD3_5, +S_SGSHARD3_D, +S_SGSHARD4_1, +S_SGSHARD4_2, +S_SGSHARD4_3, +S_SGSHARD4_4, +S_SGSHARD4_5, +S_SGSHARD4_D, +S_SGSHARD5_1, +S_SGSHARD5_2, +S_SGSHARD5_3, +S_SGSHARD5_4, +S_SGSHARD5_5, +S_SGSHARD5_D, +S_SGSHARD6_1, +S_SGSHARD6_D, +S_SGSHARD7_1, +S_SGSHARD7_D, +S_SGSHARD8_1, +S_SGSHARD8_D, +S_SGSHARD9_1, +S_SGSHARD9_D, +S_SGSHARD0_1, +S_SGSHARD0_D, +S_ARTI_EGGC1, +S_ARTI_EGGC2, +S_ARTI_EGGC3, +S_ARTI_EGGC4, +S_ARTI_EGGC5, +S_ARTI_EGGC6, +S_ARTI_EGGC7, +S_ARTI_EGGC8, +S_EGGFX1, +S_EGGFX2, +S_EGGFX3, +S_EGGFX4, +S_EGGFX5, +S_EGGFXI1_1, +S_EGGFXI1_2, +S_EGGFXI1_3, +S_EGGFXI1_4, +S_ARTI_SPHL1, +S_ZWINGEDSTATUENOSKULL, +S_ZWINGEDSTATUENOSKULL2, +S_ZGEMPEDESTAL1, +S_ZGEMPEDESTAL2, +S_ARTIPUZZSKULL, +S_ARTIPUZZGEMBIG, +S_ARTIPUZZGEMRED, +S_ARTIPUZZGEMGREEN1, +S_ARTIPUZZGEMGREEN2, +S_ARTIPUZZGEMBLUE1, +S_ARTIPUZZGEMBLUE2, +S_ARTIPUZZBOOK1, +S_ARTIPUZZBOOK2, +S_ARTIPUZZSKULL2, +S_ARTIPUZZFWEAPON, +S_ARTIPUZZCWEAPON, +S_ARTIPUZZMWEAPON, +S_ARTIPUZZGEAR_1, +S_ARTIPUZZGEAR_2, +S_ARTIPUZZGEAR_3, +S_ARTIPUZZGEAR_4, +S_ARTIPUZZGEAR_5, +S_ARTIPUZZGEAR_6, +S_ARTIPUZZGEAR_7, +S_ARTIPUZZGEAR_8, +S_ARTIPUZZGEAR2_1, +S_ARTIPUZZGEAR2_2, +S_ARTIPUZZGEAR2_3, +S_ARTIPUZZGEAR2_4, +S_ARTIPUZZGEAR2_5, +S_ARTIPUZZGEAR2_6, +S_ARTIPUZZGEAR2_7, +S_ARTIPUZZGEAR2_8, +S_ARTIPUZZGEAR3_1, +S_ARTIPUZZGEAR3_2, +S_ARTIPUZZGEAR3_3, +S_ARTIPUZZGEAR3_4, +S_ARTIPUZZGEAR3_5, +S_ARTIPUZZGEAR3_6, +S_ARTIPUZZGEAR3_7, +S_ARTIPUZZGEAR3_8, +S_ARTIPUZZGEAR4_1, +S_ARTIPUZZGEAR4_2, +S_ARTIPUZZGEAR4_3, +S_ARTIPUZZGEAR4_4, +S_ARTIPUZZGEAR4_5, +S_ARTIPUZZGEAR4_6, +S_ARTIPUZZGEAR4_7, +S_ARTIPUZZGEAR4_8, +S_ARTI_TRCH1, +S_ARTI_TRCH2, +S_ARTI_TRCH3, +S_FIREBOMB1, +S_FIREBOMB2, +S_FIREBOMB3, +S_FIREBOMB4, +S_FIREBOMB5, +S_FIREBOMB6, +S_FIREBOMB7, +S_FIREBOMB8, +S_FIREBOMB9, +S_FIREBOMB10, +S_FIREBOMB11, +S_ARTI_ATLP1, +S_ARTI_ATLP2, +S_ARTI_ATLP3, +S_ARTI_ATLP4, +S_ARTI_PSBG1, +S_POISONBAG1, +S_POISONBAG2, +S_POISONBAG3, +S_POISONBAG4, +S_POISONCLOUD1, +S_POISONCLOUD2, +S_POISONCLOUD3, +S_POISONCLOUD4, +S_POISONCLOUD5, +S_POISONCLOUD6, +S_POISONCLOUD7, +S_POISONCLOUD8, +S_POISONCLOUD9, +S_POISONCLOUD10, +S_POISONCLOUD11, +S_POISONCLOUD12, +S_POISONCLOUD13, +S_POISONCLOUD14, +S_POISONCLOUD15, +S_POISONCLOUD16, +S_POISONCLOUD17, +S_POISONCLOUD18, +S_POISONCLOUD_X1, +S_POISONCLOUD_X2, +S_POISONCLOUD_X3, +S_POISONCLOUD_X4, +S_THROWINGBOMB1, +S_THROWINGBOMB2, +S_THROWINGBOMB3, +S_THROWINGBOMB4, +S_THROWINGBOMB5, +S_THROWINGBOMB6, +S_THROWINGBOMB7, +S_THROWINGBOMB8, +S_THROWINGBOMB9, +S_THROWINGBOMB10, +S_THROWINGBOMB11, +S_THROWINGBOMB12, +S_THROWINGBOMB_X1, +S_THROWINGBOMB_X2, +S_THROWINGBOMB_X3, +S_THROWINGBOMB_X4, +S_THROWINGBOMB_X5, +S_THROWINGBOMB_X6, +S_THROWINGBOMB_X7, +S_THROWINGBOMB_X8, +S_ARTI_BOOTS1, +S_ARTI_BOOTS2, +S_ARTI_BOOTS3, +S_ARTI_BOOTS4, +S_ARTI_BOOTS5, +S_ARTI_BOOTS6, +S_ARTI_BOOTS7, +S_ARTI_BOOTS8, +S_ARTI_MANA, +S_ARTI_ARMOR1, +S_ARTI_ARMOR2, +S_ARTI_ARMOR3, +S_ARTI_ARMOR4, +S_ARTI_ARMOR5, +S_ARTI_ARMOR6, +S_ARTI_ARMOR7, +S_ARTI_ARMOR8, +S_ARTI_BLAST1, +S_ARTI_BLAST2, +S_ARTI_BLAST3, +S_ARTI_BLAST4, +S_ARTI_BLAST5, +S_ARTI_BLAST6, +S_ARTI_BLAST7, +S_ARTI_BLAST8, +S_ARTI_HEALRAD1, +S_ARTI_HEALRAD2, +S_ARTI_HEALRAD3, +S_ARTI_HEALRAD4, +S_ARTI_HEALRAD5, +S_ARTI_HEALRAD6, +S_ARTI_HEALRAD7, +S_ARTI_HEALRAD8, +S_ARTI_HEALRAD9, +S_ARTI_HEALRAD0, +S_ARTI_HEALRADA, +S_ARTI_HEALRADB, +S_ARTI_HEALRADC, +S_ARTI_HEALRADD, +S_ARTI_HEALRADE, +S_ARTI_HEALRADF, +S_SPLASH1, +S_SPLASH2, +S_SPLASH3, +S_SPLASH4, +S_SPLASHX, +S_SPLASHBASE1, +S_SPLASHBASE2, +S_SPLASHBASE3, +S_SPLASHBASE4, +S_SPLASHBASE5, +S_SPLASHBASE6, +S_SPLASHBASE7, +S_LAVASPLASH1, +S_LAVASPLASH2, +S_LAVASPLASH3, +S_LAVASPLASH4, +S_LAVASPLASH5, +S_LAVASPLASH6, +S_LAVASMOKE1, +S_LAVASMOKE2, +S_LAVASMOKE3, +S_LAVASMOKE4, +S_LAVASMOKE5, +S_SLUDGECHUNK1, +S_SLUDGECHUNK2, +S_SLUDGECHUNK3, +S_SLUDGECHUNK4, +S_SLUDGECHUNKX, +S_SLUDGESPLASH1, +S_SLUDGESPLASH2, +S_SLUDGESPLASH3, +S_SLUDGESPLASH4, +S_ZWINGEDSTATUE1, +S_ZROCK1_1, +S_ZROCK2_1, +S_ZROCK3_1, +S_ZROCK4_1, +S_ZCHANDELIER1, +S_ZCHANDELIER2, +S_ZCHANDELIER3, +S_ZCHANDELIER_U, +S_ZTREEDEAD1, +S_ZTREE, +S_ZTREEDESTRUCTIBLE1, +S_ZTREEDES_D1, +S_ZTREEDES_D2, +S_ZTREEDES_D3, +S_ZTREEDES_D4, +S_ZTREEDES_D5, +S_ZTREEDES_D6, +S_ZTREEDES_X1, +S_ZTREEDES_X2, +S_ZTREEDES_X3, +S_ZTREEDES_X4, +S_ZTREEDES_X5, +S_ZTREEDES_X6, +S_ZTREEDES_X7, +S_ZTREEDES_X8, +S_ZTREEDES_X9, +S_ZTREEDES_X10, +S_ZTREESWAMP182_1, +S_ZTREESWAMP172_1, +S_ZSTUMPBURNED1, +S_ZSTUMPBARE1, +S_ZSTUMPSWAMP1_1, +S_ZSTUMPSWAMP2_1, +S_ZSHROOMLARGE1_1, +S_ZSHROOMLARGE2_1, +S_ZSHROOMLARGE3_1, +S_ZSHROOMSMALL1_1, +S_ZSHROOMSMALL2_1, +S_ZSHROOMSMALL3_1, +S_ZSHROOMSMALL4_1, +S_ZSHROOMSMALL5_1, +S_ZSTALAGMITEPILLAR1, +S_ZSTALAGMITELARGE1, +S_ZSTALAGMITEMEDIUM1, +S_ZSTALAGMITESMALL1, +S_ZSTALACTITELARGE1, +S_ZSTALACTITEMEDIUM1, +S_ZSTALACTITESMALL1, +S_ZMOSSCEILING1_1, +S_ZMOSSCEILING2_1, +S_ZSWAMPVINE1, +S_ZCORPSEKABOB1, +S_ZCORPSESLEEPING1, +S_ZTOMBSTONERIP1, +S_ZTOMBSTONESHANE1, +S_ZTOMBSTONEBIGCROSS1, +S_ZTOMBSTONEBRIANR1, +S_ZTOMBSTONECROSSCIRCLE1, +S_ZTOMBSTONESMALLCROSS1, +S_ZTOMBSTONEBRIANP1, +S_CORPSEHANGING_1, +S_ZSTATUEGARGOYLEGREENTALL_1, +S_ZSTATUEGARGOYLEBLUETALL_1, +S_ZSTATUEGARGOYLEGREENSHORT_1, +S_ZSTATUEGARGOYLEBLUESHORT_1, +S_ZSTATUEGARGOYLESTRIPETALL_1, +S_ZSTATUEGARGOYLEDARKREDTALL_1, +S_ZSTATUEGARGOYLEREDTALL_1, +S_ZSTATUEGARGOYLETANTALL_1, +S_ZSTATUEGARGOYLERUSTTALL_1, +S_ZSTATUEGARGOYLEDARKREDSHORT_1, +S_ZSTATUEGARGOYLEREDSHORT_1, +S_ZSTATUEGARGOYLETANSHORT_1, +S_ZSTATUEGARGOYLERUSTSHORT_1, +S_ZBANNERTATTERED_1, +S_ZTREELARGE1, +S_ZTREELARGE2, +S_ZTREEGNARLED1, +S_ZTREEGNARLED2, +S_ZLOG, +S_ZSTALACTITEICELARGE, +S_ZSTALACTITEICEMEDIUM, +S_ZSTALACTITEICESMALL, +S_ZSTALACTITEICETINY, +S_ZSTALAGMITEICELARGE, +S_ZSTALAGMITEICEMEDIUM, +S_ZSTALAGMITEICESMALL, +S_ZSTALAGMITEICETINY, +S_ZROCKBROWN1, +S_ZROCKBROWN2, +S_ZROCKBLACK, +S_ZRUBBLE1, +S_ZRUBBLE2, +S_ZRUBBLE3, +S_ZVASEPILLAR, +S_ZPOTTERY1, +S_ZPOTTERY2, +S_ZPOTTERY3, +S_ZPOTTERY_EXPLODE, +S_POTTERYBIT_1, +S_POTTERYBIT_2, +S_POTTERYBIT_3, +S_POTTERYBIT_4, +S_POTTERYBIT_5, +S_POTTERYBIT_EX0, +S_POTTERYBIT_EX1, +S_POTTERYBIT_EX1_2, +S_POTTERYBIT_EX2, +S_POTTERYBIT_EX2_2, +S_POTTERYBIT_EX3, +S_POTTERYBIT_EX3_2, +S_POTTERYBIT_EX4, +S_POTTERYBIT_EX4_2, +S_POTTERYBIT_EX5, +S_POTTERYBIT_EX5_2, +S_ZCORPSELYNCHED1, +S_ZCORPSELYNCHED2, +S_ZCORPSESITTING, +S_ZCORPSESITTING_X, +S_CORPSEBIT_1, +S_CORPSEBIT_2, +S_CORPSEBIT_3, +S_CORPSEBIT_4, +S_CORPSEBLOODDRIP, +S_CORPSEBLOODDRIP_X1, +S_CORPSEBLOODDRIP_X2, +S_CORPSEBLOODDRIP_X3, +S_CORPSEBLOODDRIP_X4, +S_BLOODPOOL, +S_ZCANDLE1, +S_ZCANDLE2, +S_ZCANDLE3, +S_ZLEAFSPAWNER, +S_LEAF1_1, +S_LEAF1_2, +S_LEAF1_3, +S_LEAF1_4, +S_LEAF1_5, +S_LEAF1_6, +S_LEAF1_7, +S_LEAF1_8, +S_LEAF1_9, +S_LEAF1_10, +S_LEAF1_11, +S_LEAF1_12, +S_LEAF1_13, +S_LEAF1_14, +S_LEAF1_15, +S_LEAF1_16, +S_LEAF1_17, +S_LEAF1_18, +S_LEAF_X1, +S_LEAF2_1, +S_LEAF2_2, +S_LEAF2_3, +S_LEAF2_4, +S_LEAF2_5, +S_LEAF2_6, +S_LEAF2_7, +S_LEAF2_8, +S_LEAF2_9, +S_LEAF2_10, +S_LEAF2_11, +S_LEAF2_12, +S_LEAF2_13, +S_LEAF2_14, +S_LEAF2_15, +S_LEAF2_16, +S_LEAF2_17, +S_LEAF2_18, +S_ZTWINEDTORCH_1, +S_ZTWINEDTORCH_2, +S_ZTWINEDTORCH_3, +S_ZTWINEDTORCH_4, +S_ZTWINEDTORCH_5, +S_ZTWINEDTORCH_6, +S_ZTWINEDTORCH_7, +S_ZTWINEDTORCH_8, +S_ZTWINEDTORCH_UNLIT, +S_BRIDGE1, +S_BRIDGE2, +S_BRIDGE3, +S_FREE_BRIDGE1, +S_FREE_BRIDGE2, +S_BBALL1, +S_BBALL2, +S_ZWALLTORCH1, +S_ZWALLTORCH2, +S_ZWALLTORCH3, +S_ZWALLTORCH4, +S_ZWALLTORCH5, +S_ZWALLTORCH6, +S_ZWALLTORCH7, +S_ZWALLTORCH8, +S_ZWALLTORCH_U, +S_ZBARREL1, +S_ZSHRUB1, +S_ZSHRUB1_DIE, +S_ZSHRUB1_X1, +S_ZSHRUB1_X2, +S_ZSHRUB1_X3, +S_ZSHRUB2, +S_ZSHRUB2_DIE, +S_ZSHRUB2_X1, +S_ZSHRUB2_X2, +S_ZSHRUB2_X3, +S_ZSHRUB2_X4, +S_ZBUCKET1, +S_ZPOISONSHROOM1, +S_ZPOISONSHROOM_P1, +S_ZPOISONSHROOM_P2, +S_ZPOISONSHROOM_X1, +S_ZPOISONSHROOM_X2, +S_ZPOISONSHROOM_X3, +S_ZPOISONSHROOM_X4, +S_ZFIREBULL1, +S_ZFIREBULL2, +S_ZFIREBULL3, +S_ZFIREBULL4, +S_ZFIREBULL5, +S_ZFIREBULL6, +S_ZFIREBULL7, +S_ZFIREBULL_DEATH, +S_ZFIREBULL_DEATH2, +S_ZFIREBULL_U, +S_ZFIREBULL_BIRTH, +S_ZFIREBULL_BIRTH2, +S_ZFIRETHING1, +S_ZFIRETHING2, +S_ZFIRETHING3, +S_ZFIRETHING4, +S_ZFIRETHING5, +S_ZFIRETHING6, +S_ZFIRETHING7, +S_ZFIRETHING8, +S_ZFIRETHING9, +S_ZBRASSTORCH1, +S_ZBRASSTORCH2, +S_ZBRASSTORCH3, +S_ZBRASSTORCH4, +S_ZBRASSTORCH5, +S_ZBRASSTORCH6, +S_ZBRASSTORCH7, +S_ZBRASSTORCH8, +S_ZBRASSTORCH9, +S_ZBRASSTORCH10, +S_ZBRASSTORCH11, +S_ZBRASSTORCH12, +S_ZBRASSTORCH13, +S_ZSUITOFARMOR, +S_ZSUITOFARMOR_X1, +S_ZARMORCHUNK1, +S_ZARMORCHUNK2, +S_ZARMORCHUNK3, +S_ZARMORCHUNK4, +S_ZARMORCHUNK5, +S_ZARMORCHUNK6, +S_ZARMORCHUNK7, +S_ZARMORCHUNK8, +S_ZARMORCHUNK9, +S_ZARMORCHUNK10, +S_ZBELL, +S_ZBELL_X1, +S_ZBELL_X2, +S_ZBELL_X3, +S_ZBELL_X4, +S_ZBELL_X5, +S_ZBELL_X6, +S_ZBELL_X7, +S_ZBELL_X8, +S_ZBELL_X9, +S_ZBELL_X10, +S_ZBELL_X11, +S_ZBELL_X12, +S_ZBELL_X13, +S_ZBELL_X14, +S_ZBELL_X15, +S_ZBELL_X16, +S_ZBELL_X17, +S_ZBELL_X18, +S_ZBELL_X19, +S_ZBELL_X20, +S_ZBELL_X21, +S_ZBELL_X22, +S_ZBELL_X23, +S_ZBELL_X24, +S_ZBELL_X25, +S_ZBELL_X26, +S_ZBELL_X27, +S_ZBELL_X28, +S_ZBELL_X29, +S_ZBELL_X30, +S_ZBELL_X31, +S_ZBELL_X32, +S_ZBELL_X33, +S_ZBELL_X34, +S_ZBELL_X35, +S_ZBELL_X36, +S_ZBELL_X37, +S_ZBELL_X38, +S_ZBELL_X39, +S_ZBELL_X40, +S_ZBELL_X41, +S_ZBELL_X42, +S_ZBELL_X43, +S_ZBELL_X44, +S_ZBELL_X45, +S_ZBELL_X46, +S_ZBELL_X47, +S_ZBLUE_CANDLE1, +S_ZBLUE_CANDLE2, +S_ZBLUE_CANDLE3, +S_ZBLUE_CANDLE4, +S_ZBLUE_CANDLE5, +S_ZIRON_MAIDEN, +S_ZXMAS_TREE, +S_ZXMAS_TREE_DIE, +S_ZXMAS_TREE_X1, +S_ZXMAS_TREE_X2, +S_ZXMAS_TREE_X3, +S_ZXMAS_TREE_X4, +S_ZXMAS_TREE_X5, +S_ZXMAS_TREE_X6, +S_ZXMAS_TREE_X7, +S_ZXMAS_TREE_X8, +S_ZXMAS_TREE_X9, +S_ZXMAS_TREE_X10, +S_ZCAULDRON1, +S_ZCAULDRON2, +S_ZCAULDRON3, +S_ZCAULDRON4, +S_ZCAULDRON5, +S_ZCAULDRON6, +S_ZCAULDRON7, +S_ZCAULDRON_U, +S_ZCHAINBIT32, +S_ZCHAINBIT64, +S_ZCHAINEND_HEART, +S_ZCHAINEND_HOOK1, +S_ZCHAINEND_HOOK2, +S_ZCHAINEND_SPIKE, +S_ZCHAINEND_SKULL, +S_TABLE_SHIT1, +S_TABLE_SHIT2, +S_TABLE_SHIT3, +S_TABLE_SHIT4, +S_TABLE_SHIT5, +S_TABLE_SHIT6, +S_TABLE_SHIT7, +S_TABLE_SHIT8, +S_TABLE_SHIT9, +S_TABLE_SHIT10, +S_TFOG1, +S_TFOG2, +S_TFOG3, +S_TFOG4, +S_TFOG5, +S_TFOG6, +S_TFOG7, +S_TFOG8, +S_TFOG9, +S_TFOG10, +S_TFOG11, +S_TFOG12, +S_TFOG13, +S_TELESMOKE1, +S_TELESMOKE2, +S_TELESMOKE3, +S_TELESMOKE4, +S_TELESMOKE5, +S_TELESMOKE6, +S_TELESMOKE7, +S_TELESMOKE8, +S_TELESMOKE9, +S_TELESMOKE10, +S_TELESMOKE11, +S_TELESMOKE12, +S_TELESMOKE13, +S_TELESMOKE14, +S_TELESMOKE15, +S_TELESMOKE16, +S_TELESMOKE17, +S_TELESMOKE18, +S_TELESMOKE19, +S_TELESMOKE20, +S_TELESMOKE21, +S_TELESMOKE22, +S_TELESMOKE23, +S_TELESMOKE24, +S_TELESMOKE25, +S_TELESMOKE26, +S_LIGHTDONE, +S_PUNCHREADY, +S_PUNCHDOWN, +S_PUNCHUP, +S_PUNCHATK1_1, +S_PUNCHATK1_2, +S_PUNCHATK1_3, +S_PUNCHATK1_4, +S_PUNCHATK1_5, +S_PUNCHATK2_1, +S_PUNCHATK2_2, +S_PUNCHATK2_3, +S_PUNCHATK2_4, +S_PUNCHATK2_5, +S_PUNCHATK2_6, +S_PUNCHATK2_7, +S_PUNCHATK2_8, +S_PUNCHATK2_9, +S_PUNCHPUFF1, +S_PUNCHPUFF2, +S_PUNCHPUFF3, +S_PUNCHPUFF4, +S_PUNCHPUFF5, +S_AXE, +S_FAXEREADY, +S_FAXEDOWN, +S_FAXEUP, +S_FAXEATK_1, +S_FAXEATK_2, +S_FAXEATK_3, +S_FAXEATK_4, +S_FAXEATK_5, +S_FAXEATK_6, +S_FAXEATK_7, +S_FAXEATK_8, +S_FAXEATK_9, +S_FAXEATK_10, +S_FAXEATK_11, +S_FAXEATK_12, +S_FAXEATK_13, +S_FAXEREADY_G, +S_FAXEREADY_G1, +S_FAXEREADY_G2, +S_FAXEREADY_G3, +S_FAXEREADY_G4, +S_FAXEREADY_G5, +S_FAXEDOWN_G, +S_FAXEUP_G, +S_FAXEATK_G1, +S_FAXEATK_G2, +S_FAXEATK_G3, +S_FAXEATK_G4, +S_FAXEATK_G5, +S_FAXEATK_G6, +S_FAXEATK_G7, +S_FAXEATK_G8, +S_FAXEATK_G9, +S_FAXEATK_G10, +S_FAXEATK_G11, +S_FAXEATK_G12, +S_FAXEATK_G13, +S_AXEPUFF_GLOW1, +S_AXEPUFF_GLOW2, +S_AXEPUFF_GLOW3, +S_AXEPUFF_GLOW4, +S_AXEPUFF_GLOW5, +S_AXEPUFF_GLOW6, +S_AXEPUFF_GLOW7, +S_AXEBLOOD1, +S_AXEBLOOD2, +S_AXEBLOOD3, +S_AXEBLOOD4, +S_AXEBLOOD5, +S_AXEBLOOD6, +S_HAMM, +S_FHAMMERREADY, +S_FHAMMERDOWN, +S_FHAMMERUP, +S_FHAMMERATK_1, +S_FHAMMERATK_2, +S_FHAMMERATK_3, +S_FHAMMERATK_4, +S_FHAMMERATK_5, +S_FHAMMERATK_6, +S_FHAMMERATK_7, +S_FHAMMERATK_8, +S_FHAMMERATK_9, +S_FHAMMERATK_10, +S_FHAMMERATK_11, +S_FHAMMERATK_12, +S_HAMMER_MISSILE_1, +S_HAMMER_MISSILE_2, +S_HAMMER_MISSILE_3, +S_HAMMER_MISSILE_4, +S_HAMMER_MISSILE_5, +S_HAMMER_MISSILE_6, +S_HAMMER_MISSILE_7, +S_HAMMER_MISSILE_8, +S_HAMMER_MISSILE_X1, +S_HAMMER_MISSILE_X2, +S_HAMMER_MISSILE_X3, +S_HAMMER_MISSILE_X4, +S_HAMMER_MISSILE_X5, +S_HAMMER_MISSILE_X6, +S_HAMMER_MISSILE_X7, +S_HAMMER_MISSILE_X8, +S_HAMMER_MISSILE_X9, +S_HAMMER_MISSILE_X10, +S_HAMMERPUFF1, +S_HAMMERPUFF2, +S_HAMMERPUFF3, +S_HAMMERPUFF4, +S_HAMMERPUFF5, +S_FSWORDREADY, +S_FSWORDREADY1, +S_FSWORDREADY2, +S_FSWORDREADY3, +S_FSWORDREADY4, +S_FSWORDREADY5, +S_FSWORDREADY6, +S_FSWORDREADY7, +S_FSWORDREADY8, +S_FSWORDREADY9, +S_FSWORDREADY10, +S_FSWORDREADY11, +S_FSWORDDOWN, +S_FSWORDUP, +S_FSWORDATK_1, +S_FSWORDATK_2, +S_FSWORDATK_3, +S_FSWORDATK_4, +S_FSWORDATK_5, +S_FSWORDATK_6, +S_FSWORDATK_7, +S_FSWORDATK_8, +S_FSWORDATK_9, +S_FSWORDATK_10, +S_FSWORDATK_11, +S_FSWORDATK_12, +S_FSWORD_MISSILE1, +S_FSWORD_MISSILE2, +S_FSWORD_MISSILE3, +S_FSWORD_MISSILE_X1, +S_FSWORD_MISSILE_X2, +S_FSWORD_MISSILE_X3, +S_FSWORD_MISSILE_X4, +S_FSWORD_MISSILE_X5, +S_FSWORD_MISSILE_X6, +S_FSWORD_MISSILE_X7, +S_FSWORD_MISSILE_X8, +S_FSWORD_MISSILE_X9, +S_FSWORD_MISSILE_X10, +S_FSWORD_FLAME1, +S_FSWORD_FLAME2, +S_FSWORD_FLAME3, +S_FSWORD_FLAME4, +S_FSWORD_FLAME5, +S_FSWORD_FLAME6, +S_FSWORD_FLAME7, +S_FSWORD_FLAME8, +S_FSWORD_FLAME9, +S_FSWORD_FLAME10, +S_CMACEREADY, +S_CMACEDOWN, +S_CMACEUP, +S_CMACEATK_1, +S_CMACEATK_2, +S_CMACEATK_3, +S_CMACEATK_4, +S_CMACEATK_5, +S_CMACEATK_6, +S_CMACEATK_7, +S_CMACEATK_8, +S_CMACEATK_9, +S_CMACEATK_10, +S_CMACEATK_11, +S_CMACEATK_12, +S_CMACEATK_13, +S_CMACEATK_14, +S_CMACEATK_15, +S_CMACEATK_16, +S_CMACEATK_17, +S_CSTAFF, +S_CSTAFFREADY, +S_CSTAFFREADY1, +S_CSTAFFREADY2, +S_CSTAFFREADY3, +S_CSTAFFREADY4, +S_CSTAFFREADY5, +S_CSTAFFREADY6, +S_CSTAFFREADY7, +S_CSTAFFREADY8, +S_CSTAFFREADY9, +S_CSTAFFBLINK1, +S_CSTAFFBLINK2, +S_CSTAFFBLINK3, +S_CSTAFFBLINK4, +S_CSTAFFBLINK5, +S_CSTAFFBLINK6, +S_CSTAFFBLINK7, +S_CSTAFFBLINK8, +S_CSTAFFBLINK9, +S_CSTAFFBLINK10, +S_CSTAFFBLINK11, +S_CSTAFFDOWN, +S_CSTAFFDOWN2, +S_CSTAFFDOWN3, +S_CSTAFFUP, +S_CSTAFFATK_1, +S_CSTAFFATK_2, +S_CSTAFFATK_3, +S_CSTAFFATK_4, +S_CSTAFFATK_5, +S_CSTAFFATK_6, +S_CSTAFFATK2_1, +S_CSTAFF_MISSILE1, +S_CSTAFF_MISSILE2, +S_CSTAFF_MISSILE3, +S_CSTAFF_MISSILE4, +S_CSTAFF_MISSILE_X1, +S_CSTAFF_MISSILE_X2, +S_CSTAFF_MISSILE_X3, +S_CSTAFF_MISSILE_X4, +S_CSTAFFPUFF1, +S_CSTAFFPUFF2, +S_CSTAFFPUFF3, +S_CSTAFFPUFF4, +S_CSTAFFPUFF5, +S_CFLAME1, +S_CFLAME2, +S_CFLAME3, +S_CFLAME4, +S_CFLAME5, +S_CFLAME6, +S_CFLAME7, +S_CFLAME8, +S_CFLAMEREADY1, +S_CFLAMEREADY2, +S_CFLAMEREADY3, +S_CFLAMEREADY4, +S_CFLAMEREADY5, +S_CFLAMEREADY6, +S_CFLAMEREADY7, +S_CFLAMEREADY8, +S_CFLAMEREADY9, +S_CFLAMEREADY10, +S_CFLAMEREADY11, +S_CFLAMEREADY12, +S_CFLAMEDOWN, +S_CFLAMEUP, +S_CFLAMEATK_1, +S_CFLAMEATK_2, +S_CFLAMEATK_3, +S_CFLAMEATK_4, +S_CFLAMEATK_5, +S_CFLAMEATK_6, +S_CFLAMEATK_7, +S_CFLAMEATK_8, +S_CFLAMEFLOOR1, +S_CFLAMEFLOOR2, +S_CFLAMEFLOOR3, +S_FLAMEPUFF1, +S_FLAMEPUFF2, +S_FLAMEPUFF3, +S_FLAMEPUFF4, +S_FLAMEPUFF5, +S_FLAMEPUFF6, +S_FLAMEPUFF7, +S_FLAMEPUFF8, +S_FLAMEPUFF9, +S_FLAMEPUFF10, +S_FLAMEPUFF11, +S_FLAMEPUFF12, +S_FLAMEPUFF13, +S_FLAMEPUFF2_1, +S_FLAMEPUFF2_2, +S_FLAMEPUFF2_3, +S_FLAMEPUFF2_4, +S_FLAMEPUFF2_5, +S_FLAMEPUFF2_6, +S_FLAMEPUFF2_7, +S_FLAMEPUFF2_8, +S_FLAMEPUFF2_9, +S_FLAMEPUFF2_10, +S_FLAMEPUFF2_11, +S_FLAMEPUFF2_12, +S_FLAMEPUFF2_13, +S_FLAMEPUFF2_14, +S_FLAMEPUFF2_15, +S_FLAMEPUFF2_16, +S_FLAMEPUFF2_17, +S_FLAMEPUFF2_18, +S_FLAMEPUFF2_19, +S_FLAMEPUFF2_20, +S_CIRCLE_FLAME1, +S_CIRCLE_FLAME2, +S_CIRCLE_FLAME3, +S_CIRCLE_FLAME4, +S_CIRCLE_FLAME5, +S_CIRCLE_FLAME6, +S_CIRCLE_FLAME7, +S_CIRCLE_FLAME8, +S_CIRCLE_FLAME9, +S_CIRCLE_FLAME10, +S_CIRCLE_FLAME11, +S_CIRCLE_FLAME12, +S_CIRCLE_FLAME13, +S_CIRCLE_FLAME14, +S_CIRCLE_FLAME15, +S_CIRCLE_FLAME16, +S_CIRCLE_FLAME_X1, +S_CIRCLE_FLAME_X2, +S_CIRCLE_FLAME_X3, +S_CIRCLE_FLAME_X4, +S_CIRCLE_FLAME_X5, +S_CIRCLE_FLAME_X6, +S_CIRCLE_FLAME_X7, +S_CIRCLE_FLAME_X8, +S_CIRCLE_FLAME_X9, +S_CIRCLE_FLAME_X10, +S_CFLAME_MISSILE1, +S_CFLAME_MISSILE2, +S_CFLAME_MISSILE_X, +S_CHOLYREADY, +S_CHOLYDOWN, +S_CHOLYUP, +S_CHOLYATK_1, +S_CHOLYATK_2, +S_CHOLYATK_3, +S_CHOLYATK_4, +S_CHOLYATK_5, +S_CHOLYATK_6, +S_CHOLYATK_7, +S_CHOLYATK_8, +S_CHOLYATK_9, +S_HOLY_FX1, +S_HOLY_FX2, +S_HOLY_FX3, +S_HOLY_FX4, +S_HOLY_FX_X1, +S_HOLY_FX_X2, +S_HOLY_FX_X3, +S_HOLY_FX_X4, +S_HOLY_FX_X5, +S_HOLY_FX_X6, +S_HOLY_TAIL1, +S_HOLY_TAIL2, +S_HOLY_PUFF1, +S_HOLY_PUFF2, +S_HOLY_PUFF3, +S_HOLY_PUFF4, +S_HOLY_PUFF5, +S_HOLY_MISSILE1, +S_HOLY_MISSILE2, +S_HOLY_MISSILE3, +S_HOLY_MISSILE4, +S_HOLY_MISSILE_X, +S_HOLY_MISSILE_P1, +S_HOLY_MISSILE_P2, +S_HOLY_MISSILE_P3, +S_HOLY_MISSILE_P4, +S_HOLY_MISSILE_P5, +S_MWANDREADY, +S_MWANDDOWN, +S_MWANDUP, +S_MWANDATK_1, +S_MWANDATK_2, +S_MWANDATK_3, +S_MWANDATK_4, +S_MWANDPUFF1, +S_MWANDPUFF2, +S_MWANDPUFF3, +S_MWANDPUFF4, +S_MWANDPUFF5, +S_MWANDSMOKE1, +S_MWANDSMOKE2, +S_MWANDSMOKE3, +S_MWANDSMOKE4, +S_MWAND_MISSILE1, +S_MWAND_MISSILE2, +S_MW_LIGHTNING1, +S_MW_LIGHTNING2, +S_MW_LIGHTNING3, +S_MW_LIGHTNING4, +S_MW_LIGHTNING5, +S_MW_LIGHTNING6, +S_MW_LIGHTNING7, +S_MW_LIGHTNING8, +S_MLIGHTNINGREADY, +S_MLIGHTNINGREADY2, +S_MLIGHTNINGREADY3, +S_MLIGHTNINGREADY4, +S_MLIGHTNINGREADY5, +S_MLIGHTNINGREADY6, +S_MLIGHTNINGREADY7, +S_MLIGHTNINGREADY8, +S_MLIGHTNINGREADY9, +S_MLIGHTNINGREADY10, +S_MLIGHTNINGREADY11, +S_MLIGHTNINGREADY12, +S_MLIGHTNINGREADY13, +S_MLIGHTNINGREADY14, +S_MLIGHTNINGREADY15, +S_MLIGHTNINGREADY16, +S_MLIGHTNINGREADY17, +S_MLIGHTNINGREADY18, +S_MLIGHTNINGREADY19, +S_MLIGHTNINGREADY20, +S_MLIGHTNINGREADY21, +S_MLIGHTNINGREADY22, +S_MLIGHTNINGREADY23, +S_MLIGHTNINGREADY24, +S_MLIGHTNINGDOWN, +S_MLIGHTNINGUP, +S_MLIGHTNINGATK_1, +S_MLIGHTNINGATK_2, +S_MLIGHTNINGATK_3, +S_MLIGHTNINGATK_4, +S_MLIGHTNINGATK_5, +S_MLIGHTNINGATK_6, +S_MLIGHTNINGATK_7, +S_MLIGHTNINGATK_8, +S_MLIGHTNINGATK_9, +S_MLIGHTNINGATK_10, +S_MLIGHTNINGATK_11, +S_LIGHTNING_CEILING1, +S_LIGHTNING_CEILING2, +S_LIGHTNING_CEILING3, +S_LIGHTNING_CEILING4, +S_LIGHTNING_C_X1, +S_LIGHTNING_C_X2, +S_LIGHTNING_C_X3, +S_LIGHTNING_C_X4, +S_LIGHTNING_C_X5, +S_LIGHTNING_C_X6, +S_LIGHTNING_C_X7, +S_LIGHTNING_C_X8, +S_LIGHTNING_C_X9, +S_LIGHTNING_C_X10, +S_LIGHTNING_C_X11, +S_LIGHTNING_C_X12, +S_LIGHTNING_C_X13, +S_LIGHTNING_C_X14, +S_LIGHTNING_C_X15, +S_LIGHTNING_C_X16, +S_LIGHTNING_C_X17, +S_LIGHTNING_C_X18, +S_LIGHTNING_C_X19, +S_LIGHTNING_FLOOR1, +S_LIGHTNING_FLOOR2, +S_LIGHTNING_FLOOR3, +S_LIGHTNING_FLOOR4, +S_LIGHTNING_F_X1, +S_LIGHTNING_F_X2, +S_LIGHTNING_F_X3, +S_LIGHTNING_F_X4, +S_LIGHTNING_F_X5, +S_LIGHTNING_F_X6, +S_LIGHTNING_F_X7, +S_LIGHTNING_F_X8, +S_LIGHTNING_F_X9, +S_LIGHTNING_F_X10, +S_LIGHTNING_F_X11, +S_LIGHTNING_F_X12, +S_LIGHTNING_F_X13, +S_LIGHTNING_F_X14, +S_LIGHTNING_F_X15, +S_LIGHTNING_F_X16, +S_LIGHTNING_F_X17, +S_LIGHTNING_F_X18, +S_LIGHTNING_F_X19, +S_LIGHTNING_ZAP1, +S_LIGHTNING_ZAP2, +S_LIGHTNING_ZAP3, +S_LIGHTNING_ZAP4, +S_LIGHTNING_ZAP5, +S_LIGHTNING_ZAP_X1, +S_LIGHTNING_ZAP_X2, +S_LIGHTNING_ZAP_X3, +S_LIGHTNING_ZAP_X4, +S_LIGHTNING_ZAP_X5, +S_LIGHTNING_ZAP_X6, +S_LIGHTNING_ZAP_X7, +S_LIGHTNING_ZAP_X8, +S_MSTAFFREADY, +S_MSTAFFREADY2, +S_MSTAFFREADY3, +S_MSTAFFREADY4, +S_MSTAFFREADY5, +S_MSTAFFREADY6, +S_MSTAFFREADY7, +S_MSTAFFREADY8, +S_MSTAFFREADY9, +S_MSTAFFREADY10, +S_MSTAFFREADY11, +S_MSTAFFREADY12, +S_MSTAFFREADY13, +S_MSTAFFREADY14, +S_MSTAFFREADY15, +S_MSTAFFREADY16, +S_MSTAFFREADY17, +S_MSTAFFREADY18, +S_MSTAFFREADY19, +S_MSTAFFREADY20, +S_MSTAFFREADY21, +S_MSTAFFREADY22, +S_MSTAFFREADY23, +S_MSTAFFREADY24, +S_MSTAFFREADY25, +S_MSTAFFREADY26, +S_MSTAFFREADY27, +S_MSTAFFREADY28, +S_MSTAFFREADY29, +S_MSTAFFREADY30, +S_MSTAFFREADY31, +S_MSTAFFREADY32, +S_MSTAFFREADY33, +S_MSTAFFREADY34, +S_MSTAFFREADY35, +S_MSTAFFDOWN, +S_MSTAFFUP, +S_MSTAFFATK_1, +S_MSTAFFATK_2, +S_MSTAFFATK_3, +S_MSTAFFATK_4, +S_MSTAFFATK_5, +S_MSTAFFATK_6, +S_MSTAFFATK_7, +S_MSTAFF_FX1_1, +S_MSTAFF_FX1_2, +S_MSTAFF_FX1_3, +S_MSTAFF_FX1_4, +S_MSTAFF_FX1_5, +S_MSTAFF_FX1_6, +S_MSTAFF_FX_X1, +S_MSTAFF_FX_X2, +S_MSTAFF_FX_X3, +S_MSTAFF_FX_X4, +S_MSTAFF_FX_X5, +S_MSTAFF_FX_X6, +S_MSTAFF_FX_X7, +S_MSTAFF_FX_X8, +S_MSTAFF_FX_X9, +S_MSTAFF_FX_X10, +S_MSTAFF_FX2_1, +S_MSTAFF_FX2_2, +S_MSTAFF_FX2_3, +S_MSTAFF_FX2_4, +S_MSTAFF_FX2_X1, +S_MSTAFF_FX2_X2, +S_MSTAFF_FX2_X3, +S_MSTAFF_FX2_X4, +S_MSTAFF_FX2_X5, +S_FSWORD1, +S_FSWORD2, +S_FSWORD3, +S_CHOLY1, +S_CHOLY2, +S_CHOLY3, +S_MSTAFF1, +S_MSTAFF2, +S_MSTAFF3, +S_SNOUTREADY, +S_SNOUTDOWN, +S_SNOUTUP, +S_SNOUTATK1, +S_SNOUTATK2, +S_COS1, +S_COS2, +S_COS3, +S_CONEREADY, +S_CONEDOWN, +S_CONEUP, +S_CONEATK1_1, +S_CONEATK1_2, +S_CONEATK1_3, +S_CONEATK1_4, +S_CONEATK1_5, +S_CONEATK1_6, +S_CONEATK1_7, +S_CONEATK1_8, +S_SHARDFX1_1, +S_SHARDFX1_2, +S_SHARDFX1_3, +S_SHARDFX1_4, +S_SHARDFXE1_1, +S_SHARDFXE1_2, +S_SHARDFXE1_3, +S_SHARDFXE1_4, +S_SHARDFXE1_5, +S_BLOOD1, +S_BLOOD2, +S_BLOOD3, +S_BLOODSPLATTER1, +S_BLOODSPLATTER2, +S_BLOODSPLATTER3, +S_BLOODSPLATTERX, +S_GIBS1, +S_FPLAY, +S_FPLAY_RUN1, +S_FPLAY_RUN2, +S_FPLAY_RUN3, +S_FPLAY_RUN4, +S_FPLAY_ATK1, +S_FPLAY_ATK2, +S_FPLAY_PAIN, +S_FPLAY_PAIN2, +S_FPLAY_DIE1, +S_FPLAY_DIE2, +S_FPLAY_DIE3, +S_FPLAY_DIE4, +S_FPLAY_DIE5, +S_FPLAY_DIE6, +S_FPLAY_DIE7, +S_FPLAY_XDIE1, +S_FPLAY_XDIE2, +S_FPLAY_XDIE3, +S_FPLAY_XDIE4, +S_FPLAY_XDIE5, +S_FPLAY_XDIE6, +S_FPLAY_XDIE7, +S_FPLAY_XDIE8, +S_FPLAY_ICE, +S_FPLAY_ICE2, +S_PLAY_F_FDTH1, +S_PLAY_F_FDTH2, +S_PLAY_C_FDTH1, +S_PLAY_C_FDTH2, +S_PLAY_M_FDTH1, +S_PLAY_M_FDTH2, +S_PLAY_FDTH3, +S_PLAY_FDTH4, +S_PLAY_FDTH5, +S_PLAY_FDTH6, +S_PLAY_FDTH7, +S_PLAY_FDTH8, +S_PLAY_FDTH9, +S_PLAY_FDTH10, +S_PLAY_FDTH11, +S_PLAY_FDTH12, +S_PLAY_FDTH13, +S_PLAY_FDTH14, +S_PLAY_FDTH15, +S_PLAY_FDTH16, +S_PLAY_FDTH17, +S_PLAY_FDTH18, +S_PLAY_FDTH19, +S_PLAY_FDTH20, +S_BLOODYSKULL1, +S_BLOODYSKULL2, +S_BLOODYSKULL3, +S_BLOODYSKULL4, +S_BLOODYSKULL5, +S_BLOODYSKULL6, +S_BLOODYSKULL7, +S_BLOODYSKULLX1, +S_BLOODYSKULLX2, +S_PLAYER_SPEED1, +S_PLAYER_SPEED2, +S_ICECHUNK1, +S_ICECHUNK2, +S_ICECHUNK3, +S_ICECHUNK4, +S_ICECHUNK_HEAD, +S_ICECHUNK_HEAD2, +S_CPLAY, +S_CPLAY_RUN1, +S_CPLAY_RUN2, +S_CPLAY_RUN3, +S_CPLAY_RUN4, +S_CPLAY_ATK1, +S_CPLAY_ATK2, +S_CPLAY_ATK3, +S_CPLAY_PAIN, +S_CPLAY_PAIN2, +S_CPLAY_DIE1, +S_CPLAY_DIE2, +S_CPLAY_DIE3, +S_CPLAY_DIE4, +S_CPLAY_DIE5, +S_CPLAY_DIE6, +S_CPLAY_DIE7, +S_CPLAY_DIE8, +S_CPLAY_DIE9, +S_CPLAY_XDIE1, +S_CPLAY_XDIE2, +S_CPLAY_XDIE3, +S_CPLAY_XDIE4, +S_CPLAY_XDIE5, +S_CPLAY_XDIE6, +S_CPLAY_XDIE7, +S_CPLAY_XDIE8, +S_CPLAY_XDIE9, +S_CPLAY_XDIE10, +S_CPLAY_ICE, +S_CPLAY_ICE2, +S_MPLAY, +S_MPLAY_RUN1, +S_MPLAY_RUN2, +S_MPLAY_RUN3, +S_MPLAY_RUN4, +S_MPLAY_ATK1, +S_MPLAY_ATK2, +S_MPLAY_PAIN, +S_MPLAY_PAIN2, +S_MPLAY_DIE1, +S_MPLAY_DIE2, +S_MPLAY_DIE3, +S_MPLAY_DIE4, +S_MPLAY_DIE5, +S_MPLAY_DIE6, +S_MPLAY_DIE7, +S_MPLAY_XDIE1, +S_MPLAY_XDIE2, +S_MPLAY_XDIE3, +S_MPLAY_XDIE4, +S_MPLAY_XDIE5, +S_MPLAY_XDIE6, +S_MPLAY_XDIE7, +S_MPLAY_XDIE8, +S_MPLAY_XDIE9, +S_MPLAY_ICE, +S_MPLAY_ICE2, +S_PIGPLAY, +S_PIGPLAY_RUN1, +S_PIGPLAY_RUN2, +S_PIGPLAY_RUN3, +S_PIGPLAY_RUN4, +S_PIGPLAY_ATK1, +S_PIGPLAY_PAIN, +S_PIG_LOOK1, +S_PIG_WALK1, +S_PIG_WALK2, +S_PIG_WALK3, +S_PIG_WALK4, +S_PIG_PAIN, +S_PIG_ATK1, +S_PIG_ATK2, +S_PIG_DIE1, +S_PIG_DIE2, +S_PIG_DIE3, +S_PIG_DIE4, +S_PIG_DIE5, +S_PIG_DIE6, +S_PIG_DIE7, +S_PIG_DIE8, +S_PIG_ICE, +S_PIG_ICE2, +S_CENTAUR_LOOK1, +S_CENTAUR_LOOK2, +S_CENTAUR_WALK1, +S_CENTAUR_WALK2, +S_CENTAUR_WALK3, +S_CENTAUR_WALK4, +S_CENTAUR_ATK1, +S_CENTAUR_ATK2, +S_CENTAUR_ATK3, +S_CENTAUR_MISSILE1, +S_CENTAUR_MISSILE2, +S_CENTAUR_MISSILE3, +S_CENTAUR_MISSILE4, +S_CENTAUR_PAIN1, +S_CENTAUR_PAIN2, +S_CENTAUR_PAIN3, +S_CENTAUR_PAIN4, +S_CENTAUR_PAIN5, +S_CENTAUR_PAIN6, +S_CENTAUR_DEATH1, +S_CENTAUR_DEATH2, +S_CENTAUR_DEATH3, +S_CENTAUR_DEATH4, +S_CENTAUR_DEATH5, +S_CENTAUR_DEATH6, +S_CENTAUR_DEATH7, +S_CENTAUR_DEATH8, +S_CENTAUR_DEATH9, +S_CENTAUR_DEATH0, +S_CENTAUR_DEATH_X1, +S_CENTAUR_DEATH_X2, +S_CENTAUR_DEATH_X3, +S_CENTAUR_DEATH_X4, +S_CENTAUR_DEATH_X5, +S_CENTAUR_DEATH_X6, +S_CENTAUR_DEATH_X7, +S_CENTAUR_DEATH_X8, +S_CENTAUR_DEATH_X9, +S_CENTAUR_DEATH_X10, +S_CENTAUR_DEATH_X11, +S_CENTAUR_ICE, +S_CENTAUR_ICE2, +S_CENTAUR_FX1, +S_CENTAUR_FX_X1, +S_CENTAUR_FX_X2, +S_CENTAUR_FX_X3, +S_CENTAUR_FX_X4, +S_CENTAUR_FX_X5, +S_CENTAUR_SHIELD1, +S_CENTAUR_SHIELD2, +S_CENTAUR_SHIELD3, +S_CENTAUR_SHIELD4, +S_CENTAUR_SHIELD5, +S_CENTAUR_SHIELD6, +S_CENTAUR_SHIELD_X1, +S_CENTAUR_SHIELD_X2, +S_CENTAUR_SHIELD_X3, +S_CENTAUR_SHIELD_X4, +S_CENTAUR_SWORD1, +S_CENTAUR_SWORD2, +S_CENTAUR_SWORD3, +S_CENTAUR_SWORD4, +S_CENTAUR_SWORD5, +S_CENTAUR_SWORD6, +S_CENTAUR_SWORD7, +S_CENTAUR_SWORD_X1, +S_CENTAUR_SWORD_X2, +S_CENTAUR_SWORD_X3, +S_DEMN_LOOK1, +S_DEMN_LOOK2, +S_DEMN_CHASE1, +S_DEMN_CHASE2, +S_DEMN_CHASE3, +S_DEMN_CHASE4, +S_DEMN_ATK1_1, +S_DEMN_ATK1_2, +S_DEMN_ATK1_3, +S_DEMN_ATK2_1, +S_DEMN_ATK2_2, +S_DEMN_ATK2_3, +S_DEMN_PAIN1, +S_DEMN_PAIN2, +S_DEMN_DEATH1, +S_DEMN_DEATH2, +S_DEMN_DEATH3, +S_DEMN_DEATH4, +S_DEMN_DEATH5, +S_DEMN_DEATH6, +S_DEMN_DEATH7, +S_DEMN_DEATH8, +S_DEMN_DEATH9, +S_DEMN_XDEATH1, +S_DEMN_XDEATH2, +S_DEMN_XDEATH3, +S_DEMN_XDEATH4, +S_DEMN_XDEATH5, +S_DEMN_XDEATH6, +S_DEMN_XDEATH7, +S_DEMN_XDEATH8, +S_DEMN_XDEATH9, +S_DEMON_ICE, +S_DEMON_ICE2, +S_DEMONCHUNK1_1, +S_DEMONCHUNK1_2, +S_DEMONCHUNK1_3, +S_DEMONCHUNK1_4, +S_DEMONCHUNK2_1, +S_DEMONCHUNK2_2, +S_DEMONCHUNK2_3, +S_DEMONCHUNK2_4, +S_DEMONCHUNK3_1, +S_DEMONCHUNK3_2, +S_DEMONCHUNK3_3, +S_DEMONCHUNK3_4, +S_DEMONCHUNK4_1, +S_DEMONCHUNK4_2, +S_DEMONCHUNK4_3, +S_DEMONCHUNK4_4, +S_DEMONCHUNK5_1, +S_DEMONCHUNK5_2, +S_DEMONCHUNK5_3, +S_DEMONCHUNK5_4, +S_DEMONFX_MOVE1, +S_DEMONFX_MOVE2, +S_DEMONFX_MOVE3, +S_DEMONFX_BOOM1, +S_DEMONFX_BOOM2, +S_DEMONFX_BOOM3, +S_DEMONFX_BOOM4, +S_DEMONFX_BOOM5, +S_DEMN2_LOOK1, +S_DEMN2_LOOK2, +S_DEMN2_CHASE1, +S_DEMN2_CHASE2, +S_DEMN2_CHASE3, +S_DEMN2_CHASE4, +S_DEMN2_ATK1_1, +S_DEMN2_ATK1_2, +S_DEMN2_ATK1_3, +S_DEMN2_ATK2_1, +S_DEMN2_ATK2_2, +S_DEMN2_ATK2_3, +S_DEMN2_PAIN1, +S_DEMN2_PAIN2, +S_DEMN2_DEATH1, +S_DEMN2_DEATH2, +S_DEMN2_DEATH3, +S_DEMN2_DEATH4, +S_DEMN2_DEATH5, +S_DEMN2_DEATH6, +S_DEMN2_DEATH7, +S_DEMN2_DEATH8, +S_DEMN2_DEATH9, +S_DEMN2_XDEATH1, +S_DEMN2_XDEATH2, +S_DEMN2_XDEATH3, +S_DEMN2_XDEATH4, +S_DEMN2_XDEATH5, +S_DEMN2_XDEATH6, +S_DEMN2_XDEATH7, +S_DEMN2_XDEATH8, +S_DEMN2_XDEATH9, +S_DEMON2CHUNK1_1, +S_DEMON2CHUNK1_2, +S_DEMON2CHUNK1_3, +S_DEMON2CHUNK1_4, +S_DEMON2CHUNK2_1, +S_DEMON2CHUNK2_2, +S_DEMON2CHUNK2_3, +S_DEMON2CHUNK2_4, +S_DEMON2CHUNK3_1, +S_DEMON2CHUNK3_2, +S_DEMON2CHUNK3_3, +S_DEMON2CHUNK3_4, +S_DEMON2CHUNK4_1, +S_DEMON2CHUNK4_2, +S_DEMON2CHUNK4_3, +S_DEMON2CHUNK4_4, +S_DEMON2CHUNK5_1, +S_DEMON2CHUNK5_2, +S_DEMON2CHUNK5_3, +S_DEMON2CHUNK5_4, +S_DEMON2FX_MOVE1, +S_DEMON2FX_MOVE2, +S_DEMON2FX_MOVE3, +S_DEMON2FX_MOVE4, +S_DEMON2FX_MOVE5, +S_DEMON2FX_MOVE6, +S_DEMON2FX_BOOM1, +S_DEMON2FX_BOOM2, +S_DEMON2FX_BOOM3, +S_DEMON2FX_BOOM4, +S_DEMON2FX_BOOM5, +S_DEMON2FX_BOOM6, +S_WRAITH_RAISE1, +S_WRAITH_RAISE2, +S_WRAITH_RAISE3, +S_WRAITH_RAISE4, +S_WRAITH_RAISE5, +S_WRAITH_INIT1, +S_WRAITH_INIT2, +S_WRAITH_LOOK1, +S_WRAITH_LOOK2, +S_WRAITH_CHASE1, +S_WRAITH_CHASE2, +S_WRAITH_CHASE3, +S_WRAITH_CHASE4, +S_WRAITH_ATK1_1, +S_WRAITH_ATK1_2, +S_WRAITH_ATK1_3, +S_WRAITH_ATK2_1, +S_WRAITH_ATK2_2, +S_WRAITH_ATK2_3, +S_WRAITH_PAIN1, +S_WRAITH_PAIN2, +S_WRAITH_DEATH1_1, +S_WRAITH_DEATH1_2, +S_WRAITH_DEATH1_3, +S_WRAITH_DEATH1_4, +S_WRAITH_DEATH1_5, +S_WRAITH_DEATH1_6, +S_WRAITH_DEATH1_7, +S_WRAITH_DEATH1_8, +S_WRAITH_DEATH1_9, +S_WRAITH_DEATH1_0, +S_WRAITH_DEATH2_1, +S_WRAITH_DEATH2_2, +S_WRAITH_DEATH2_3, +S_WRAITH_DEATH2_4, +S_WRAITH_DEATH2_5, +S_WRAITH_DEATH2_6, +S_WRAITH_DEATH2_7, +S_WRAITH_DEATH2_8, +S_WRAITH_ICE, +S_WRAITH_ICE2, +S_WRTHFX_MOVE1, +S_WRTHFX_MOVE2, +S_WRTHFX_MOVE3, +S_WRTHFX_BOOM1, +S_WRTHFX_BOOM2, +S_WRTHFX_BOOM3, +S_WRTHFX_BOOM4, +S_WRTHFX_BOOM5, +S_WRTHFX_BOOM6, +S_WRTHFX_SIZZLE1, +S_WRTHFX_SIZZLE2, +S_WRTHFX_SIZZLE3, +S_WRTHFX_SIZZLE4, +S_WRTHFX_SIZZLE5, +S_WRTHFX_SIZZLE6, +S_WRTHFX_SIZZLE7, +S_WRTHFX_DROP1, +S_WRTHFX_DROP2, +S_WRTHFX_DROP3, +S_WRTHFX_DEAD1, +S_WRTHFX_ADROP1, +S_WRTHFX_ADROP2, +S_WRTHFX_ADROP3, +S_WRTHFX_ADROP4, +S_WRTHFX_ADEAD1, +S_WRTHFX_BDROP1, +S_WRTHFX_BDROP2, +S_WRTHFX_BDROP3, +S_WRTHFX_BDEAD1, +S_MNTR_SPAWN1, +S_MNTR_SPAWN2, +S_MNTR_SPAWN3, +S_MNTR_LOOK1, +S_MNTR_LOOK2, +S_MNTR_WALK1, +S_MNTR_WALK2, +S_MNTR_WALK3, +S_MNTR_WALK4, +S_MNTR_ROAM1, +S_MNTR_ROAM2, +S_MNTR_ROAM3, +S_MNTR_ROAM4, +S_MNTR_ATK1_1, +S_MNTR_ATK1_2, +S_MNTR_ATK1_3, +S_MNTR_ATK2_1, +S_MNTR_ATK2_2, +S_MNTR_ATK2_3, +S_MNTR_ATK3_1, +S_MNTR_ATK3_2, +S_MNTR_ATK3_3, +S_MNTR_ATK3_4, +S_MNTR_ATK4_1, +S_MNTR_PAIN1, +S_MNTR_PAIN2, +S_MNTR_DIE1, +S_MNTR_DIE2, +S_MNTR_DIE3, +S_MNTR_DIE4, +S_MNTR_DIE5, +S_MNTR_DIE6, +S_MNTR_DIE7, +S_MNTR_DIE8, +S_MNTR_DIE9, +S_MNTRFX1_1, +S_MNTRFX1_2, +S_MNTRFXI1_1, +S_MNTRFXI1_2, +S_MNTRFXI1_3, +S_MNTRFXI1_4, +S_MNTRFXI1_5, +S_MNTRFXI1_6, +S_MNTRFX2_1, +S_MNTRFXI2_1, +S_MNTRFXI2_2, +S_MNTRFXI2_3, +S_MNTRFXI2_4, +S_MNTRFXI2_5, +S_MNTRFX3_1, +S_MNTRFX3_2, +S_MNTRFX3_3, +S_MNTRFX3_4, +S_MNTRFX3_5, +S_MNTRFX3_6, +S_MNTRFX3_7, +S_MNTRFX3_8, +S_MNTRFX3_9, +S_MINOSMOKE1, +S_MINOSMOKE2, +S_MINOSMOKE3, +S_MINOSMOKE4, +S_MINOSMOKE5, +S_MINOSMOKE6, +S_MINOSMOKE7, +S_MINOSMOKE8, +S_MINOSMOKE9, +S_MINOSMOKE0, +S_MINOSMOKEA, +S_MINOSMOKEB, +S_MINOSMOKEC, +S_MINOSMOKED, +S_MINOSMOKEE, +S_MINOSMOKEF, +S_MINOSMOKEG, +S_MINOSMOKEX1, +S_MINOSMOKEX2, +S_MINOSMOKEX3, +S_MINOSMOKEX4, +S_MINOSMOKEX5, +S_MINOSMOKEX6, +S_MINOSMOKEX7, +S_MINOSMOKEX8, +S_MINOSMOKEX9, +S_MINOSMOKEX0, +S_MINOSMOKEXA, +S_MINOSMOKEXB, +S_MINOSMOKEXC, +S_MINOSMOKEXD, +S_MINOSMOKEXE, +S_MINOSMOKEXF, +S_MINOSMOKEXG, +S_MINOSMOKEXH, +S_MINOSMOKEXI, +S_SERPENT_LOOK1, +S_SERPENT_SWIM1, +S_SERPENT_SWIM2, +S_SERPENT_SWIM3, +S_SERPENT_HUMP1, +S_SERPENT_HUMP2, +S_SERPENT_HUMP3, +S_SERPENT_HUMP4, +S_SERPENT_HUMP5, +S_SERPENT_HUMP6, +S_SERPENT_HUMP7, +S_SERPENT_HUMP8, +S_SERPENT_HUMP9, +S_SERPENT_HUMP10, +S_SERPENT_HUMP11, +S_SERPENT_HUMP12, +S_SERPENT_HUMP13, +S_SERPENT_HUMP14, +S_SERPENT_HUMP15, +S_SERPENT_SURFACE1, +S_SERPENT_SURFACE2, +S_SERPENT_SURFACE3, +S_SERPENT_SURFACE4, +S_SERPENT_SURFACE5, +S_SERPENT_DIVE1, +S_SERPENT_DIVE2, +S_SERPENT_DIVE3, +S_SERPENT_DIVE4, +S_SERPENT_DIVE5, +S_SERPENT_DIVE6, +S_SERPENT_DIVE7, +S_SERPENT_DIVE8, +S_SERPENT_DIVE9, +S_SERPENT_DIVE10, +S_SERPENT_WALK1, +S_SERPENT_WALK2, +S_SERPENT_WALK3, +S_SERPENT_WALK4, +S_SERPENT_PAIN1, +S_SERPENT_PAIN2, +S_SERPENT_ATK1, +S_SERPENT_ATK2, +S_SERPENT_MELEE1, +S_SERPENT_MISSILE1, +S_SERPENT_DIE1, +S_SERPENT_DIE2, +S_SERPENT_DIE3, +S_SERPENT_DIE4, +S_SERPENT_DIE5, +S_SERPENT_DIE6, +S_SERPENT_DIE7, +S_SERPENT_DIE8, +S_SERPENT_DIE9, +S_SERPENT_DIE10, +S_SERPENT_DIE11, +S_SERPENT_DIE12, +S_SERPENT_XDIE1, +S_SERPENT_XDIE2, +S_SERPENT_XDIE3, +S_SERPENT_XDIE4, +S_SERPENT_XDIE5, +S_SERPENT_XDIE6, +S_SERPENT_XDIE7, +S_SERPENT_XDIE8, +S_SERPENT_ICE, +S_SERPENT_ICE2, +S_SERPENT_FX1, +S_SERPENT_FX2, +S_SERPENT_FX3, +S_SERPENT_FX4, +S_SERPENT_FX_X1, +S_SERPENT_FX_X2, +S_SERPENT_FX_X3, +S_SERPENT_FX_X4, +S_SERPENT_FX_X5, +S_SERPENT_FX_X6, +S_SERPENT_HEAD1, +S_SERPENT_HEAD2, +S_SERPENT_HEAD3, +S_SERPENT_HEAD4, +S_SERPENT_HEAD5, +S_SERPENT_HEAD6, +S_SERPENT_HEAD7, +S_SERPENT_HEAD8, +S_SERPENT_HEAD_X1, +S_SERPENT_GIB1_1, +S_SERPENT_GIB1_2, +S_SERPENT_GIB1_3, +S_SERPENT_GIB1_4, +S_SERPENT_GIB1_5, +S_SERPENT_GIB1_6, +S_SERPENT_GIB1_7, +S_SERPENT_GIB1_8, +S_SERPENT_GIB1_9, +S_SERPENT_GIB1_10, +S_SERPENT_GIB1_11, +S_SERPENT_GIB1_12, +S_SERPENT_GIB2_1, +S_SERPENT_GIB2_2, +S_SERPENT_GIB2_3, +S_SERPENT_GIB2_4, +S_SERPENT_GIB2_5, +S_SERPENT_GIB2_6, +S_SERPENT_GIB2_7, +S_SERPENT_GIB2_8, +S_SERPENT_GIB2_9, +S_SERPENT_GIB2_10, +S_SERPENT_GIB2_11, +S_SERPENT_GIB2_12, +S_SERPENT_GIB3_1, +S_SERPENT_GIB3_2, +S_SERPENT_GIB3_3, +S_SERPENT_GIB3_4, +S_SERPENT_GIB3_5, +S_SERPENT_GIB3_6, +S_SERPENT_GIB3_7, +S_SERPENT_GIB3_8, +S_SERPENT_GIB3_9, +S_SERPENT_GIB3_10, +S_SERPENT_GIB3_11, +S_SERPENT_GIB3_12, +S_BISHOP_LOOK1, +S_BISHOP_DECIDE, +S_BISHOP_BLUR1, +S_BISHOP_BLUR2, +S_BISHOP_WALK1, +S_BISHOP_WALK2, +S_BISHOP_WALK3, +S_BISHOP_WALK4, +S_BISHOP_WALK5, +S_BISHOP_WALK6, +S_BISHOP_ATK1, +S_BISHOP_ATK2, +S_BISHOP_ATK3, +S_BISHOP_ATK4, +S_BISHOP_ATK5, +S_BISHOP_PAIN1, +S_BISHOP_PAIN2, +S_BISHOP_PAIN3, +S_BISHOP_PAIN4, +S_BISHOP_PAIN5, +S_BISHOP_DEATH1, +S_BISHOP_DEATH2, +S_BISHOP_DEATH3, +S_BISHOP_DEATH4, +S_BISHOP_DEATH5, +S_BISHOP_DEATH6, +S_BISHOP_DEATH7, +S_BISHOP_DEATH8, +S_BISHOP_DEATH9, +S_BISHOP_DEATH10, +S_BISHOP_ICE, +S_BISHOP_ICE2, +S_BISHOP_PUFF1, +S_BISHOP_PUFF2, +S_BISHOP_PUFF3, +S_BISHOP_PUFF4, +S_BISHOP_PUFF5, +S_BISHOP_PUFF6, +S_BISHOP_PUFF7, +S_BISHOPBLUR1, +S_BISHOPBLUR2, +S_BISHOPPAINBLUR1, +S_BISHFX1_1, +S_BISHFX1_2, +S_BISHFX1_3, +S_BISHFX1_4, +S_BISHFX1_5, +S_BISHFXI1_1, +S_BISHFXI1_2, +S_BISHFXI1_3, +S_BISHFXI1_4, +S_BISHFXI1_5, +S_BISHFXI1_6, +S_DRAGON_LOOK1, +S_DRAGON_INIT, +S_DRAGON_INIT2, +S_DRAGON_INIT3, +S_DRAGON_WALK1, +S_DRAGON_WALK2, +S_DRAGON_WALK3, +S_DRAGON_WALK4, +S_DRAGON_WALK5, +S_DRAGON_WALK6, +S_DRAGON_WALK7, +S_DRAGON_WALK8, +S_DRAGON_WALK9, +S_DRAGON_WALK10, +S_DRAGON_WALK11, +S_DRAGON_WALK12, +S_DRAGON_ATK1, +S_DRAGON_PAIN1, +S_DRAGON_DEATH1, +S_DRAGON_DEATH2, +S_DRAGON_DEATH3, +S_DRAGON_DEATH4, +S_DRAGON_CRASH1, +S_DRAGON_CRASH2, +S_DRAGON_CRASH3, +S_DRAGON_FX1_1, +S_DRAGON_FX1_2, +S_DRAGON_FX1_3, +S_DRAGON_FX1_4, +S_DRAGON_FX1_5, +S_DRAGON_FX1_6, +S_DRAGON_FX1_X1, +S_DRAGON_FX1_X2, +S_DRAGON_FX1_X3, +S_DRAGON_FX1_X4, +S_DRAGON_FX1_X5, +S_DRAGON_FX1_X6, +S_DRAGON_FX2_1, +S_DRAGON_FX2_2, +S_DRAGON_FX2_3, +S_DRAGON_FX2_4, +S_DRAGON_FX2_5, +S_DRAGON_FX2_6, +S_DRAGON_FX2_7, +S_DRAGON_FX2_8, +S_DRAGON_FX2_9, +S_DRAGON_FX2_10, +S_DRAGON_FX2_11, +S_ARMOR_1, +S_ARMOR_2, +S_ARMOR_3, +S_ARMOR_4, +S_MANA1_1, +S_MANA1_2, +S_MANA1_3, +S_MANA1_4, +S_MANA1_5, +S_MANA1_6, +S_MANA1_7, +S_MANA1_8, +S_MANA1_9, +S_MANA2_1, +S_MANA2_2, +S_MANA2_3, +S_MANA2_4, +S_MANA2_5, +S_MANA2_6, +S_MANA2_7, +S_MANA2_8, +S_MANA2_9, +S_MANA2_10, +S_MANA2_11, +S_MANA2_12, +S_MANA2_13, +S_MANA2_14, +S_MANA2_15, +S_MANA2_16, +S_MANA3_1, +S_MANA3_2, +S_MANA3_3, +S_MANA3_4, +S_MANA3_5, +S_MANA3_6, +S_MANA3_7, +S_MANA3_8, +S_MANA3_9, +S_MANA3_10, +S_MANA3_11, +S_MANA3_12, +S_MANA3_13, +S_MANA3_14, +S_MANA3_15, +S_MANA3_16, +S_KEY1, +S_KEY2, +S_KEY3, +S_KEY4, +S_KEY5, +S_KEY6, +S_KEY7, +S_KEY8, +S_KEY9, +S_KEYA, +S_KEYB, +S_SND_WIND1, +S_SND_WIND2, +S_SND_WATERFALL, +S_ETTIN_LOOK1, +S_ETTIN_LOOK2, +S_ETTIN_CHASE1, +S_ETTIN_CHASE2, +S_ETTIN_CHASE3, +S_ETTIN_CHASE4, +S_ETTIN_PAIN1, +S_ETTIN_ATK1_1, +S_ETTIN_ATK1_2, +S_ETTIN_ATK1_3, +S_ETTIN_DEATH1_1, +S_ETTIN_DEATH1_2, +S_ETTIN_DEATH1_3, +S_ETTIN_DEATH1_4, +S_ETTIN_DEATH1_5, +S_ETTIN_DEATH1_6, +S_ETTIN_DEATH1_7, +S_ETTIN_DEATH1_8, +S_ETTIN_DEATH1_9, +S_ETTIN_DEATH2_1, +S_ETTIN_DEATH2_2, +S_ETTIN_DEATH2_3, +S_ETTIN_DEATH2_4, +S_ETTIN_DEATH2_5, +S_ETTIN_DEATH2_6, +S_ETTIN_DEATH2_7, +S_ETTIN_DEATH2_8, +S_ETTIN_DEATH2_9, +S_ETTIN_DEATH2_0, +S_ETTIN_DEATH2_A, +S_ETTIN_DEATH2_B, +S_ETTIN_ICE1, +S_ETTIN_ICE2, +S_ETTIN_MACE1, +S_ETTIN_MACE2, +S_ETTIN_MACE3, +S_ETTIN_MACE4, +S_ETTIN_MACE5, +S_ETTIN_MACE6, +S_ETTIN_MACE7, +S_FIRED_SPAWN1, +S_FIRED_LOOK1, +S_FIRED_LOOK2, +S_FIRED_LOOK3, +S_FIRED_LOOK4, +S_FIRED_LOOK5, +S_FIRED_LOOK6, +S_FIRED_LOOK7, +S_FIRED_LOOK8, +S_FIRED_LOOK9, +S_FIRED_LOOK0, +S_FIRED_LOOKA, +S_FIRED_LOOKB, +S_FIRED_WALK1, +S_FIRED_WALK2, +S_FIRED_WALK3, +S_FIRED_PAIN1, +S_FIRED_ATTACK1, +S_FIRED_ATTACK2, +S_FIRED_ATTACK3, +S_FIRED_ATTACK4, +S_FIRED_DEATH1, +S_FIRED_DEATH2, +S_FIRED_DEATH3, +S_FIRED_DEATH4, +S_FIRED_XDEATH1, +S_FIRED_XDEATH2, +S_FIRED_XDEATH3, +S_FIRED_ICE1, +S_FIRED_ICE2, +S_FIRED_CORPSE1, +S_FIRED_CORPSE2, +S_FIRED_CORPSE3, +S_FIRED_CORPSE4, +S_FIRED_CORPSE5, +S_FIRED_CORPSE6, +S_FIRED_RDROP1, +S_FIRED_RDEAD1_1, +S_FIRED_RDEAD1_2, +S_FIRED_RDROP2, +S_FIRED_RDEAD2_1, +S_FIRED_RDEAD2_2, +S_FIRED_RDROP3, +S_FIRED_RDEAD3_1, +S_FIRED_RDEAD3_2, +S_FIRED_RDROP4, +S_FIRED_RDEAD4_1, +S_FIRED_RDEAD4_2, +S_FIRED_RDROP5, +S_FIRED_RDEAD5_1, +S_FIRED_RDEAD5_2, +S_FIRED_FX6_1, +S_FIRED_FX6_2, +S_FIRED_FX6_3, +S_FIRED_FX6_4, +S_FIRED_FX6_5, +S_ICEGUY_LOOK, +S_ICEGUY_DORMANT, +S_ICEGUY_WALK1, +S_ICEGUY_WALK2, +S_ICEGUY_WALK3, +S_ICEGUY_WALK4, +S_ICEGUY_ATK1, +S_ICEGUY_ATK2, +S_ICEGUY_ATK3, +S_ICEGUY_ATK4, +S_ICEGUY_PAIN1, +S_ICEGUY_DEATH, +S_ICEGUY_FX1, +S_ICEGUY_FX2, +S_ICEGUY_FX3, +S_ICEGUY_FX_X1, +S_ICEGUY_FX_X2, +S_ICEGUY_FX_X3, +S_ICEGUY_FX_X4, +S_ICEGUY_FX_X5, +S_ICEFX_PUFF1, +S_ICEFX_PUFF2, +S_ICEFX_PUFF3, +S_ICEFX_PUFF4, +S_ICEFX_PUFF5, +S_ICEGUY_FX2_1, +S_ICEGUY_FX2_2, +S_ICEGUY_FX2_3, +S_ICEGUY_BIT1, +S_ICEGUY_BIT2, +S_ICEGUY_WISP1_1, +S_ICEGUY_WISP1_2, +S_ICEGUY_WISP1_3, +S_ICEGUY_WISP1_4, +S_ICEGUY_WISP1_5, +S_ICEGUY_WISP1_6, +S_ICEGUY_WISP1_7, +S_ICEGUY_WISP1_8, +S_ICEGUY_WISP1_9, +S_ICEGUY_WISP2_1, +S_ICEGUY_WISP2_2, +S_ICEGUY_WISP2_3, +S_ICEGUY_WISP2_4, +S_ICEGUY_WISP2_5, +S_ICEGUY_WISP2_6, +S_ICEGUY_WISP2_7, +S_ICEGUY_WISP2_8, +S_ICEGUY_WISP2_9, +S_FIGHTER, +S_FIGHTER2, +S_FIGHTERLOOK, +S_FIGHTER_RUN1, +S_FIGHTER_RUN2, +S_FIGHTER_RUN3, +S_FIGHTER_RUN4, +S_FIGHTER_ATK1, +S_FIGHTER_ATK2, +S_FIGHTER_PAIN, +S_FIGHTER_PAIN2, +S_FIGHTER_DIE1, +S_FIGHTER_DIE2, +S_FIGHTER_DIE3, +S_FIGHTER_DIE4, +S_FIGHTER_DIE5, +S_FIGHTER_DIE6, +S_FIGHTER_DIE7, +S_FIGHTER_XDIE1, +S_FIGHTER_XDIE2, +S_FIGHTER_XDIE3, +S_FIGHTER_XDIE4, +S_FIGHTER_XDIE5, +S_FIGHTER_XDIE6, +S_FIGHTER_XDIE7, +S_FIGHTER_XDIE8, +S_FIGHTER_ICE, +S_FIGHTER_ICE2, +S_CLERIC, +S_CLERIC2, +S_CLERICLOOK, +S_CLERIC_RUN1, +S_CLERIC_RUN2, +S_CLERIC_RUN3, +S_CLERIC_RUN4, +S_CLERIC_ATK1, +S_CLERIC_ATK2, +S_CLERIC_ATK3, +S_CLERIC_PAIN, +S_CLERIC_PAIN2, +S_CLERIC_DIE1, +S_CLERIC_DIE2, +S_CLERIC_DIE3, +S_CLERIC_DIE4, +S_CLERIC_DIE5, +S_CLERIC_DIE6, +S_CLERIC_DIE7, +S_CLERIC_DIE8, +S_CLERIC_DIE9, +S_CLERIC_XDIE1, +S_CLERIC_XDIE2, +S_CLERIC_XDIE3, +S_CLERIC_XDIE4, +S_CLERIC_XDIE5, +S_CLERIC_XDIE6, +S_CLERIC_XDIE7, +S_CLERIC_XDIE8, +S_CLERIC_XDIE9, +S_CLERIC_XDIE10, +S_CLERIC_ICE, +S_CLERIC_ICE2, +S_MAGE, +S_MAGE2, +S_MAGELOOK, +S_MAGE_RUN1, +S_MAGE_RUN2, +S_MAGE_RUN3, +S_MAGE_RUN4, +S_MAGE_ATK1, +S_MAGE_ATK2, +S_MAGE_PAIN, +S_MAGE_PAIN2, +S_MAGE_DIE1, +S_MAGE_DIE2, +S_MAGE_DIE3, +S_MAGE_DIE4, +S_MAGE_DIE5, +S_MAGE_DIE6, +S_MAGE_DIE7, +S_MAGE_XDIE1, +S_MAGE_XDIE2, +S_MAGE_XDIE3, +S_MAGE_XDIE4, +S_MAGE_XDIE5, +S_MAGE_XDIE6, +S_MAGE_XDIE7, +S_MAGE_XDIE8, +S_MAGE_XDIE9, +S_MAGE_ICE, +S_MAGE_ICE2, +S_SORC_SPAWN1, +S_SORC_SPAWN2, +S_SORC_LOOK1, +S_SORC_WALK1, +S_SORC_WALK2, +S_SORC_WALK3, +S_SORC_WALK4, +S_SORC_PAIN1, +S_SORC_PAIN2, +S_SORC_ATK2_1, +S_SORC_ATK2_2, +S_SORC_ATK2_3, +S_SORC_ATTACK1, +S_SORC_ATTACK2, +S_SORC_ATTACK3, +S_SORC_ATTACK4, +S_SORC_ATTACK5, +S_SORC_DIE1, +S_SORC_DIE2, +S_SORC_DIE3, +S_SORC_DIE4, +S_SORC_DIE5, +S_SORC_DIE6, +S_SORC_DIE7, +S_SORC_DIE8, +S_SORC_DIE9, +S_SORC_DIE0, +S_SORC_DIEA, +S_SORC_DIEB, +S_SORC_DIEC, +S_SORC_DIED, +S_SORC_DIEE, +S_SORC_DIEF, +S_SORC_DIEG, +S_SORC_DIEH, +S_SORC_DIEI, +S_SORCBALL1_1, +S_SORCBALL1_2, +S_SORCBALL1_3, +S_SORCBALL1_4, +S_SORCBALL1_5, +S_SORCBALL1_6, +S_SORCBALL1_7, +S_SORCBALL1_8, +S_SORCBALL1_9, +S_SORCBALL1_0, +S_SORCBALL1_A, +S_SORCBALL1_B, +S_SORCBALL1_C, +S_SORCBALL1_D, +S_SORCBALL1_E, +S_SORCBALL1_F, +S_SORCBALL1_D1, +S_SORCBALL1_D2, +S_SORCBALL1_D5, +S_SORCBALL1_D6, +S_SORCBALL1_D7, +S_SORCBALL1_D8, +S_SORCBALL1_D9, +S_SORCBALL2_1, +S_SORCBALL2_2, +S_SORCBALL2_3, +S_SORCBALL2_4, +S_SORCBALL2_5, +S_SORCBALL2_6, +S_SORCBALL2_7, +S_SORCBALL2_8, +S_SORCBALL2_9, +S_SORCBALL2_0, +S_SORCBALL2_A, +S_SORCBALL2_B, +S_SORCBALL2_C, +S_SORCBALL2_D, +S_SORCBALL2_E, +S_SORCBALL2_F, +S_SORCBALL2_D1, +S_SORCBALL2_D2, +S_SORCBALL2_D5, +S_SORCBALL2_D6, +S_SORCBALL2_D7, +S_SORCBALL2_D8, +S_SORCBALL2_D9, +S_SORCBALL3_1, +S_SORCBALL3_2, +S_SORCBALL3_3, +S_SORCBALL3_4, +S_SORCBALL3_5, +S_SORCBALL3_6, +S_SORCBALL3_7, +S_SORCBALL3_8, +S_SORCBALL3_9, +S_SORCBALL3_0, +S_SORCBALL3_A, +S_SORCBALL3_B, +S_SORCBALL3_C, +S_SORCBALL3_D, +S_SORCBALL3_E, +S_SORCBALL3_F, +S_SORCBALL3_D1, +S_SORCBALL3_D2, +S_SORCBALL3_D5, +S_SORCBALL3_D6, +S_SORCBALL3_D7, +S_SORCBALL3_D8, +S_SORCBALL3_D9, +S_SORCFX1_1, +S_SORCFX1_2, +S_SORCFX1_3, +S_SORCFX1_4, +S_SORCFX1_D1, +S_SORCFX1_D2, +S_SORCFX1_D3, +S_SORCFX2_SPLIT1, +S_SORCFX2_ORBIT1, +S_SORCFX2_ORBIT2, +S_SORCFX2_ORBIT3, +S_SORCFX2_ORBIT4, +S_SORCFX2_ORBIT5, +S_SORCFX2_ORBIT6, +S_SORCFX2_ORBIT7, +S_SORCFX2_ORBIT8, +S_SORCFX2_ORBIT9, +S_SORCFX2_ORBIT0, +S_SORCFX2_ORBITA, +S_SORCFX2_ORBITB, +S_SORCFX2_ORBITC, +S_SORCFX2_ORBITD, +S_SORCFX2_ORBITE, +S_SORCFX2_ORBITF, +S_SORCFX2T1, +S_SORCFX3_1, +S_SORCFX3_2, +S_SORCFX3_3, +S_BISHMORPH1, +S_BISHMORPHA, +S_BISHMORPHB, +S_BISHMORPHC, +S_BISHMORPHD, +S_BISHMORPHE, +S_BISHMORPHF, +S_BISHMORPHG, +S_BISHMORPHH, +S_BISHMORPHI, +S_BISHMORPHJ, +S_SORCFX3_EXP1, +S_SORCFX3_EXP2, +S_SORCFX3_EXP3, +S_SORCFX3_EXP4, +S_SORCFX3_EXP5, +S_SORCFX4_1, +S_SORCFX4_2, +S_SORCFX4_3, +S_SORCFX4_D1, +S_SORCFX4_D2, +S_SORCFX4_D3, +S_SORCFX4_D4, +S_SORCFX4_D5, +S_SORCSPARK1, +S_SORCSPARK2, +S_SORCSPARK3, +S_SORCSPARK4, +S_SORCSPARK5, +S_SORCSPARK6, +S_SORCSPARK7, +S_BLASTEFFECT1, +S_BLASTEFFECT2, +S_BLASTEFFECT3, +S_BLASTEFFECT4, +S_BLASTEFFECT5, +S_BLASTEFFECT6, +S_BLASTEFFECT7, +S_BLASTEFFECT8, +S_BLASTEFFECT9, +S_WATERDRIP1, +S_KORAX_LOOK1, +S_KORAX_CHASE1, +S_KORAX_CHASE2, +S_KORAX_CHASE3, +S_KORAX_CHASE4, +S_KORAX_CHASE5, +S_KORAX_CHASE6, +S_KORAX_CHASE7, +S_KORAX_CHASE8, +S_KORAX_CHASE9, +S_KORAX_CHASE0, +S_KORAX_CHASEA, +S_KORAX_CHASEB, +S_KORAX_CHASEC, +S_KORAX_CHASED, +S_KORAX_CHASEE, +S_KORAX_CHASEF, +S_KORAX_PAIN1, +S_KORAX_PAIN2, +S_KORAX_ATTACK1, +S_KORAX_ATTACK2, +S_KORAX_MISSILE1, +S_KORAX_MISSILE2, +S_KORAX_MISSILE3, +S_KORAX_COMMAND1, +S_KORAX_COMMAND2, +S_KORAX_COMMAND3, +S_KORAX_COMMAND4, +S_KORAX_COMMAND5, +S_KORAX_DEATH1, +S_KORAX_DEATH2, +S_KORAX_DEATH3, +S_KORAX_DEATH4, +S_KORAX_DEATH5, +S_KORAX_DEATH6, +S_KORAX_DEATH7, +S_KORAX_DEATH8, +S_KORAX_DEATH9, +S_KORAX_DEATH0, +S_KORAX_DEATHA, +S_KORAX_DEATHB, +S_KORAX_DEATHC, +S_KORAX_DEATHD, +S_KSPIRIT_ROAM1, +S_KSPIRIT_ROAM2, +S_KSPIRIT_DEATH1, +S_KSPIRIT_DEATH2, +S_KSPIRIT_DEATH3, +S_KSPIRIT_DEATH4, +S_KSPIRIT_DEATH5, +S_KSPIRIT_DEATH6, +S_KBOLT1, +S_KBOLT2, +S_KBOLT3, +S_KBOLT4, +S_KBOLT5, +S_KBOLT6, +S_KBOLT7, +S_SPAWNBATS1, +S_SPAWNBATS2, +S_SPAWNBATS3, +S_SPAWNBATS_OFF, +S_BAT1, +S_BAT2, +S_BAT3, +S_BAT_DEATH, +NUMSTATES +} statenum_t; + +typedef struct +{ + spritenum_t sprite; + long frame; + long tics; + void (*action) (); + statenum_t nextstate; + long misc1, misc2; +} state_t; + +extern state_t states[NUMSTATES]; +extern char *sprnames[NUMSPRITES]; + + + +typedef enum { +MT_MAPSPOT, +MT_MAPSPOTGRAVITY, +MT_FIREBALL1, +MT_ARROW, +MT_DART, +MT_POISONDART, +MT_RIPPERBALL, +MT_PROJECTILE_BLADE, +MT_ICESHARD, +MT_FLAME_SMALL_TEMP, +MT_FLAME_LARGE_TEMP, +MT_FLAME_SMALL, +MT_FLAME_LARGE, +MT_HEALINGBOTTLE, +MT_HEALTHFLASK, +MT_ARTIFLY, +MT_ARTIINVULNERABILITY, +MT_SUMMONMAULATOR, +MT_SUMMON_FX, +MT_THRUSTFLOOR_UP, +MT_THRUSTFLOOR_DOWN, +MT_TELEPORTOTHER, +MT_TELOTHER_FX1, +MT_TELOTHER_FX2, +MT_TELOTHER_FX3, +MT_TELOTHER_FX4, +MT_TELOTHER_FX5, +MT_DIRT1, +MT_DIRT2, +MT_DIRT3, +MT_DIRT4, +MT_DIRT5, +MT_DIRT6, +MT_DIRTCLUMP, +MT_ROCK1, +MT_ROCK2, +MT_ROCK3, +MT_FOGSPAWNER, +MT_FOGPATCHS, +MT_FOGPATCHM, +MT_FOGPATCHL, +MT_QUAKE_FOCUS, +MT_SGSHARD1, +MT_SGSHARD2, +MT_SGSHARD3, +MT_SGSHARD4, +MT_SGSHARD5, +MT_SGSHARD6, +MT_SGSHARD7, +MT_SGSHARD8, +MT_SGSHARD9, +MT_SGSHARD0, +MT_ARTIEGG, +MT_EGGFX, +MT_ARTISUPERHEAL, +MT_ZWINGEDSTATUENOSKULL, +MT_ZGEMPEDESTAL, +MT_ARTIPUZZSKULL, +MT_ARTIPUZZGEMBIG, +MT_ARTIPUZZGEMRED, +MT_ARTIPUZZGEMGREEN1, +MT_ARTIPUZZGEMGREEN2, +MT_ARTIPUZZGEMBLUE1, +MT_ARTIPUZZGEMBLUE2, +MT_ARTIPUZZBOOK1, +MT_ARTIPUZZBOOK2, +MT_ARTIPUZZSKULL2, +MT_ARTIPUZZFWEAPON, +MT_ARTIPUZZCWEAPON, +MT_ARTIPUZZMWEAPON, +MT_ARTIPUZZGEAR, +MT_ARTIPUZZGEAR2, +MT_ARTIPUZZGEAR3, +MT_ARTIPUZZGEAR4, +MT_ARTITORCH, +MT_FIREBOMB, +MT_ARTITELEPORT, +MT_ARTIPOISONBAG, +MT_POISONBAG, +MT_POISONCLOUD, +MT_THROWINGBOMB, +MT_SPEEDBOOTS, +MT_BOOSTMANA, +MT_BOOSTARMOR, +MT_BLASTRADIUS, +MT_HEALRADIUS, +MT_SPLASH, +MT_SPLASHBASE, +MT_LAVASPLASH, +MT_LAVASMOKE, +MT_SLUDGECHUNK, +MT_SLUDGESPLASH, +MT_MISC0, +MT_MISC1, +MT_MISC2, +MT_MISC3, +MT_MISC4, +MT_MISC5, +MT_MISC6, +MT_MISC7, +MT_MISC8, +MT_TREEDESTRUCTIBLE, +MT_MISC9, +MT_MISC10, +MT_MISC11, +MT_MISC12, +MT_MISC13, +MT_MISC14, +MT_MISC15, +MT_MISC16, +MT_MISC17, +MT_MISC18, +MT_MISC19, +MT_MISC20, +MT_MISC21, +MT_MISC22, +MT_MISC23, +MT_MISC24, +MT_MISC25, +MT_MISC26, +MT_MISC27, +MT_MISC28, +MT_MISC29, +MT_MISC30, +MT_MISC31, +MT_MISC32, +MT_MISC33, +MT_MISC34, +MT_MISC35, +MT_MISC36, +MT_MISC37, +MT_MISC38, +MT_MISC39, +MT_MISC40, +MT_MISC41, +MT_MISC42, +MT_MISC43, +MT_MISC44, +MT_MISC45, +MT_MISC46, +MT_MISC47, +MT_MISC48, +MT_MISC49, +MT_MISC50, +MT_MISC51, +MT_MISC52, +MT_MISC53, +MT_MISC54, +MT_MISC55, +MT_MISC56, +MT_MISC57, +MT_MISC58, +MT_MISC59, +MT_MISC60, +MT_MISC61, +MT_MISC62, +MT_MISC63, +MT_MISC64, +MT_MISC65, +MT_MISC66, +MT_MISC67, +MT_MISC68, +MT_MISC69, +MT_MISC70, +MT_MISC71, +MT_MISC72, +MT_MISC73, +MT_MISC74, +MT_MISC75, +MT_MISC76, +MT_POTTERY1, +MT_POTTERY2, +MT_POTTERY3, +MT_POTTERYBIT1, +MT_MISC77, +MT_ZLYNCHED_NOHEART, +MT_MISC78, +MT_CORPSEBIT, +MT_CORPSEBLOODDRIP, +MT_BLOODPOOL, +MT_MISC79, +MT_MISC80, +MT_LEAF1, +MT_LEAF2, +MT_ZTWINEDTORCH, +MT_ZTWINEDTORCH_UNLIT, +MT_BRIDGE, +MT_BRIDGEBALL, +MT_ZWALLTORCH, +MT_ZWALLTORCH_UNLIT, +MT_ZBARREL, +MT_ZSHRUB1, +MT_ZSHRUB2, +MT_ZBUCKET, +MT_ZPOISONSHROOM, +MT_ZFIREBULL, +MT_ZFIREBULL_UNLIT, +MT_FIRETHING, +MT_BRASSTORCH, +MT_ZSUITOFARMOR, +MT_ZARMORCHUNK, +MT_ZBELL, +MT_ZBLUE_CANDLE, +MT_ZIRON_MAIDEN, +MT_ZXMAS_TREE, +MT_ZCAULDRON, +MT_ZCAULDRON_UNLIT, +MT_ZCHAINBIT32, +MT_ZCHAINBIT64, +MT_ZCHAINEND_HEART, +MT_ZCHAINEND_HOOK1, +MT_ZCHAINEND_HOOK2, +MT_ZCHAINEND_SPIKE, +MT_ZCHAINEND_SKULL, +MT_TABLE_SHIT1, +MT_TABLE_SHIT2, +MT_TABLE_SHIT3, +MT_TABLE_SHIT4, +MT_TABLE_SHIT5, +MT_TABLE_SHIT6, +MT_TABLE_SHIT7, +MT_TABLE_SHIT8, +MT_TABLE_SHIT9, +MT_TABLE_SHIT10, +MT_TFOG, +MT_MISC81, +MT_TELEPORTMAN, +MT_PUNCHPUFF, +MT_FW_AXE, +MT_AXEPUFF, +MT_AXEPUFF_GLOW, +MT_AXEBLOOD, +MT_FW_HAMMER, +MT_HAMMER_MISSILE, +MT_HAMMERPUFF, +MT_FSWORD_MISSILE, +MT_FSWORD_FLAME, +MT_CW_SERPSTAFF, +MT_CSTAFF_MISSILE, +MT_CSTAFFPUFF, +MT_CW_FLAME, +MT_CFLAMEFLOOR, +MT_FLAMEPUFF, +MT_FLAMEPUFF2, +MT_CIRCLEFLAME, +MT_CFLAME_MISSILE, +MT_HOLY_FX, +MT_HOLY_TAIL, +MT_HOLY_PUFF, +MT_HOLY_MISSILE, +MT_HOLY_MISSILE_PUFF, +MT_MWANDPUFF, +MT_MWANDSMOKE, +MT_MWAND_MISSILE, +MT_MW_LIGHTNING, +MT_LIGHTNING_CEILING, +MT_LIGHTNING_FLOOR, +MT_LIGHTNING_ZAP, +MT_MSTAFF_FX, +MT_MSTAFF_FX2, +MT_FW_SWORD1, +MT_FW_SWORD2, +MT_FW_SWORD3, +MT_CW_HOLY1, +MT_CW_HOLY2, +MT_CW_HOLY3, +MT_MW_STAFF1, +MT_MW_STAFF2, +MT_MW_STAFF3, +MT_SNOUTPUFF, +MT_MW_CONE, +MT_SHARDFX1, +MT_BLOOD, +MT_BLOODSPLATTER, +MT_GIBS, +MT_PLAYER_FIGHTER, +MT_BLOODYSKULL, +MT_PLAYER_SPEED, +MT_ICECHUNK, +MT_PLAYER_CLERIC, +MT_PLAYER_MAGE, +MT_PIGPLAYER, +MT_PIG, +MT_CENTAUR, +MT_CENTAURLEADER, +MT_CENTAUR_FX, +MT_CENTAUR_SHIELD, +MT_CENTAUR_SWORD, +MT_DEMON, +MT_DEMONCHUNK1, +MT_DEMONCHUNK2, +MT_DEMONCHUNK3, +MT_DEMONCHUNK4, +MT_DEMONCHUNK5, +MT_DEMONFX1, +MT_DEMON2, +MT_DEMON2CHUNK1, +MT_DEMON2CHUNK2, +MT_DEMON2CHUNK3, +MT_DEMON2CHUNK4, +MT_DEMON2CHUNK5, +MT_DEMON2FX1, +MT_WRAITHB, +MT_WRAITH, +MT_WRAITHFX1, +MT_WRAITHFX2, +MT_WRAITHFX3, +MT_WRAITHFX4, +MT_WRAITHFX5, +MT_MINOTAUR, +MT_MNTRFX1, +MT_MNTRFX2, +MT_MNTRFX3, +MT_MNTRSMOKE, +MT_MNTRSMOKEEXIT, +MT_SERPENT, +MT_SERPENTLEADER, +MT_SERPENTFX, +MT_SERPENT_HEAD, +MT_SERPENT_GIB1, +MT_SERPENT_GIB2, +MT_SERPENT_GIB3, +MT_BISHOP, +MT_BISHOP_PUFF, +MT_BISHOPBLUR, +MT_BISHOPPAINBLUR, +MT_BISH_FX, +MT_DRAGON, +MT_DRAGON_FX, +MT_DRAGON_FX2, +MT_ARMOR_1, +MT_ARMOR_2, +MT_ARMOR_3, +MT_ARMOR_4, +MT_MANA1, +MT_MANA2, +MT_MANA3, +MT_KEY1, +MT_KEY2, +MT_KEY3, +MT_KEY4, +MT_KEY5, +MT_KEY6, +MT_KEY7, +MT_KEY8, +MT_KEY9, +MT_KEYA, +MT_KEYB, +MT_SOUNDWIND, +MT_SOUNDWATERFALL, +MT_ETTIN, +MT_ETTIN_MACE, +MT_FIREDEMON, +MT_FIREDEMON_SPLOTCH1, +MT_FIREDEMON_SPLOTCH2, +MT_FIREDEMON_FX1, +MT_FIREDEMON_FX2, +MT_FIREDEMON_FX3, +MT_FIREDEMON_FX4, +MT_FIREDEMON_FX5, +MT_FIREDEMON_FX6, +MT_ICEGUY, +MT_ICEGUY_FX, +MT_ICEFX_PUFF, +MT_ICEGUY_FX2, +MT_ICEGUY_BIT, +MT_ICEGUY_WISP1, +MT_ICEGUY_WISP2, +MT_FIGHTER_BOSS, +MT_CLERIC_BOSS, +MT_MAGE_BOSS, +MT_SORCBOSS, +MT_SORCBALL1, +MT_SORCBALL2, +MT_SORCBALL3, +MT_SORCFX1, +MT_SORCFX2, +MT_SORCFX2_T1, +MT_SORCFX3, +MT_SORCFX3_EXPLOSION, +MT_SORCFX4, +MT_SORCSPARK1, +MT_BLASTEFFECT, +MT_WATER_DRIP, +MT_KORAX, +MT_KORAX_SPIRIT1, +MT_KORAX_SPIRIT2, +MT_KORAX_SPIRIT3, +MT_KORAX_SPIRIT4, +MT_KORAX_SPIRIT5, +MT_KORAX_SPIRIT6, +MT_DEMON_MASH, +MT_DEMON2_MASH, +MT_ETTIN_MASH, +MT_CENTAUR_MASH, +MT_KORAX_BOLT, +MT_BAT_SPAWNER, +MT_BAT, +NUMMOBJTYPES} mobjtype_t; + +typedef struct { + int doomednum; + int spawnstate; + int spawnhealth; + int seestate; + int seesound; + int reactiontime; + int attacksound; + int painstate; + int painchance; + int painsound; + int meleestate; + int missilestate; + int crashstate; + int deathstate; + int xdeathstate; + int deathsound; + int speed; + int radius; + int height; + int mass; + int damage; + int activesound; + int flags; + int flags2; +} mobjinfo_t; + +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + diff --git a/src/hexen/linear.asm b/src/hexen/linear.asm new file mode 100644 index 00000000..d57c0cfa --- /dev/null +++ b/src/hexen/linear.asm @@ -0,0 +1,349 @@ + .386 + .MODEL small + INCLUDE defs.inc + + +;============================================================================ +; +; unwound vertical scaling code +; +; eax light table pointer, 0 lowbyte overwritten +; ebx all 0, low byte overwritten +; ecx fractional step value +; edx fractional scale value +; esi start of source pixels +; edi bottom pixel in screenbuffer to blit into +; +; ebx should be set to 0 0 0 dh to feed the pipeline +; +; The graphics wrap vertically at 128 pixels +;============================================================================ + +.DATA + +EXTRN _centery:DWORD + +SCALEDEFINE MACRO number + dd vscale&number +ENDM + + ALIGN 4 +scalecalls LABEL +LINE = 0 +REPT SCREENHEIGHT+1 + SCALEDEFINE %LINE +LINE = LINE+1 +ENDM + +FUZZSCALEDEFINE MACRO number + dd fuzzvscale&number +ENDM + + ALIGN 4 +fuzzscalecalls LABEL +LINE = 0 +REPT SCREENHEIGHT+1 + FUZZSCALEDEFINE %LINE +LINE = LINE+1 +ENDM + +calladdr dd ? + +;================================= + + +.CODE + +;================ +; +; R_DrawColumn +; +;================ + +PROC R_DrawColumn_ +PUBLIC R_DrawColumn_ + PUSHR + + mov ebp,[_dc_yh] + mov ebx,ebp + mov edi,[_ylookup+ebx*4] + mov ebx,[_dc_x] + add edi,[_columnofs + ebx*4] + + mov eax,[_dc_yl] + sub ebp,eax ; ebp = pixel count + or ebp,ebp + js done + + mov ecx,[_dc_iscale] + + sub eax,[_centery] + imul ecx + mov edx,[_dc_texturemid] + add edx,eax + shl edx,9 ; 7 significant bits, 25 frac + + shl ecx,9 ; 7 significant bits, 25 frac + mov esi,[_dc_source] + + mov eax,[_dc_colormap] + + xor ebx,ebx + shld ebx,edx,7 ; get address of first location + call [scalecalls+4+ebp*4] + +done: + POPR + ret + +;============ HIGH DETAIL ============ + +SCALELABEL MACRO number +vscale&number: +ENDM + +LINE = SCREENHEIGHT +REPT SCREENHEIGHT-1 + SCALELABEL %LINE + mov al,[esi+ebx] ; get source pixel + add edx,ecx ; calculate next location + mov al,[eax] ; translate the color +; xor ebx,ebx +; shld ebx,edx,7 ; get address of next location + mov ebx,edx + shr ebx,25 + mov [edi-(LINE-1)*SCREENWIDTH],al ; draw a pixel to the buffer +LINE = LINE-1 +ENDM +vscale1: + mov al,[esi+ebx] + mov al,[eax] + mov [edi],al +vscale0: + ret + +ENDP + + +;================ +; +; R_DrawFuzz +; +;================ + +PROC R_DrawFuzzColumn_ +PUBLIC R_DrawFuzzColumn_ + PUSHR + + mov ebp,[_dc_yh] + mov ebx,ebp + mov edi,[_ylookup+ebx*4] + mov ebx,[_dc_x] + add edi,[_columnofs + ebx*4] + + mov eax,[_dc_yl] + sub ebp,eax ; ebp = pixel count + or ebp,ebp + js fuzzdone + + mov ecx,[_dc_iscale] + + sub eax,[_centery] + imul ecx + mov edx,[_dc_texturemid] + add edx,eax + shl edx,9 ; 7 significant bits, 25 frac + + shl ecx,9 ; 7 significant bits, 25 frac + mov esi,[_dc_source] + mov eax,[_dc_colormap] + xor ebx,ebx + shld ebx,edx,7 ;get address of first location + + mov ebp, [fuzzscalecalls+4+ebp*4] + mov calladdr, ebp + mov ebp, ecx + xor ecx, ecx + + call [calladdr] + +fuzzdone: + POPR + ret + + +FUZZSCALELABEL MACRO number +fuzzvscale&number: +ENDM + +LINE = SCREENHEIGHT +REPT SCREENHEIGHT-1 + FUZZSCALELABEL %LINE + mov al, byte ptr [esi+ebx] ; get source pixel + add edx, ebp ; calculate next location + + mov cl, byte ptr [edi-(LINE-1)*SCREENWIDTH] + mov ch, [eax] + add ecx, [_tinttable] + mov ebx, edx + shr ebx, 25 + mov al, [ecx] + mov [edi-(LINE-1)*SCREENWIDTH],al ; draw a pixel to the buffer + xor ecx, ecx +LINE = LINE-1 +ENDM +fuzzvscale1: + mov al,[esi+ebx] + mov cl, byte ptr [edi-(LINE-1)*SCREENWIDTH] + mov ch, [eax] + add ecx, [_tinttable] + mov al, [ecx] + mov [edi],al +fuzzvscale0: + ret + +ENDP + +;============================================================================ +; +; unwound horizontal texture mapping code +; +; eax lighttable +; ebx scratch register +; ecx position 6.10 bits x, 6.10 bits y +; edx step 6.10 bits x, 6.10 bits y +; esi start of block +; edi dest +; ebp fff to mask bx +; +; ebp should by preset from ebx / ecx before calling +;============================================================================ + +OP_SHLD = 0fh + + +.DATA + + +MAPDEFINE MACRO number + dd hmap&number +ENDM + + ALIGN 4 +mapcalls LABEL +LINE = 0 +REPT SCREENWIDTH+1 + MAPDEFINE %LINE +LINE = LINE+1 +ENDM + + +callpoint dd 0 +returnpoint dd 0 + + +.CODE + +;================ +; +; R_DrawSpan +; +; Horizontal texture mapping +; +;================ + + +PROC R_DrawSpan_ +PUBLIC R_DrawSpan_ + PUSHR + +IFE SKIPPRIMITIVES + + mov eax,[_ds_x1] + mov ebx,[_ds_x2] + mov eax,[mapcalls+eax*4] + mov [callpoint],eax ; spot to jump into unwound + mov eax,[mapcalls+4+ebx*4] + mov [returnpoint],eax ; spot to patch a ret at + mov BYTE PTR [eax], OP_RET + +; +; build composite position +; + mov ecx,[_ds_xfrac] + shl ecx,10 + and ecx,0ffff0000h + mov eax,[_ds_yfrac] + shr eax,6 + and eax,0ffffh + or ecx,eax + +; +; build composite step +; + mov edx,[_ds_xstep] + shl edx,10 + and edx,0ffff0000h + mov eax,[_ds_ystep] + shr eax,6 + and eax,0ffffh + or edx,eax + + mov esi,[_ds_source] + + mov edi,[_ds_y] + mov edi,[_ylookup+edi*4] + add edi,[_columnofs] + + mov eax,[_ds_colormap] + +; +; feed the pipeline and jump in +; + mov ebp,0fffh ; used to mask off slop high bits from position + shld ebx,ecx,22 ; shift y units in + shld ebx,ecx,6 ; shift x units in + and ebx,ebp ; mask off slop bits + call [callpoint] + + mov ebx,[returnpoint] + mov BYTE PTR [ebx],OP_MOVAL ; remove the ret patched in + +ENDIF + POPR + ret + + +;============= HIGH DETAIL ============ + +.CODE + +MAPLABEL MACRO number +hmap&number: +ENDM + +LINE = 0 +PCOL = 0 +REPT SCREENWIDTH/4 +PLANE = 0 +REPT 4 + MAPLABEL %LINE +LINE = LINE + 1 + + mov al,[esi+ebx] ; get source pixel + shld ebx,ecx,22 ; shift y units in + shld ebx,ecx,6 ; shift x units in + mov al,[eax] ; translate color + and ebx,ebp ; mask off slop bits + add ecx,edx ; position += step + mov [edi+PLANE+PCOL*4],al ; write pixel +PLANE = PLANE + 1 +ENDM +PCOL = PCOL + 1 +ENDM +hmap320: + ret + +ENDP + +END diff --git a/src/hexen/m_misc.c b/src/hexen/m_misc.c new file mode 100644 index 00000000..21ea963a --- /dev/null +++ b/src/hexen/m_misc.c @@ -0,0 +1,878 @@ + +//************************************************************************** +//** +//** m_misc.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: m_misc.c,v $ +//** $Revision: 1.18 $ +//** $Date: 96/01/07 16:51:18 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#ifdef __NeXT__ + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif +#include +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define MALLOC_CLIB 1 +#define MALLOC_ZONE 2 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static int ReadFile(char const *name, byte **buffer, int mallocType); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- +extern char *SavePath; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int myargc; +char **myargv; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// M_CheckParm +// +// Checks for the given parameter in the program's command line arguments. +// Returns the argument number (1 to argc-1) or 0 if not present. +// +//========================================================================== + +int M_CheckParm(char *check) +{ + int i; + + for(i = 1; i < myargc; i++) + { + if(!strcasecmp(check, myargv[i])) + { + return i; + } + } + return 0; +} + +//========================================================================== +// +// M_ParmExists +// +// Returns true if the given parameter exists in the program's command +// line arguments, false if not. +// +//========================================================================== + +boolean M_ParmExists(char *check) +{ + return M_CheckParm(check) != 0 ? true : false; +} + +//========================================================================== +// +// M_ExtractFileBase +// +//========================================================================== + +void M_ExtractFileBase(char *path, char *dest) +{ + char *src; + int length; + + src = path+strlen(path)-1; + + // Back up until a \ or the start + while(src != path && *(src-1) != '\\' && *(src-1) != '/') + { + src--; + } + + // Copy up to eight characters + memset(dest, 0, 8); + length = 0; + while(*src && *src != '.') + { + if(++length == 9) + { + I_Error("Filename base of %s > 8 chars", path); + } + *dest++ = toupper((int)*src++); + } +} + +/* +=============== += += M_Random += += Returns a 0-255 number += +=============== +*/ + + +// This is the new flat distribution table +unsigned char rndtable[256] = { + 201, 1,243, 19, 18, 42,183,203,101,123,154,137, 34,118, 10,216, + 135,246, 0,107,133,229, 35,113,177,211,110, 17,139, 84,251,235, + 182,166,161,230,143, 91, 24, 81, 22, 94, 7, 51,232,104,122,248, + 175,138,127,171,222,213, 44, 16, 9, 33, 88,102,170,150,136,114, + 62, 3,142,237, 6,252,249, 56, 74, 30, 13, 21,180,199, 32,132, + 187,234, 78,210, 46,131,197, 8,206,244, 73, 4,236,178,195, 70, + 121, 97,167,217,103, 40,247,186,105, 39, 95,163, 99,149,253, 29, + 119, 83,254, 26,202, 65,130,155, 60, 64,184,106,221, 93,164,196, + 112,108,179,141, 54,109, 11,126, 75,165,191,227, 87,225,156, 15, + 98,162,116, 79,169,140,190,205,168,194, 41,250, 27, 20, 14,241, + 50,214, 72,192,220,233, 67,148, 96,185,176,181,215,207,172, 85, + 89, 90,209,128,124, 2, 55,173, 66,152, 47,129, 59, 43,159,240, + 239, 12,189,212,144, 28,200, 77,219,198,134,228, 45, 92,125,151, + 5, 53,255, 52, 68,245,160,158, 61, 86, 58, 82,117, 37,242,145, + 69,188,115, 76, 63,100, 49,111,153, 80, 38, 57,174,224, 71,231, + 23, 25, 48,218,120,147,208, 36,226,223,193,238,157,204,146, 31 +}; + + +int rndindex = 0; +int prndindex = 0; + +int M_Random (void) +{ + rndindex = (rndindex+1)&0xff; + return rndtable[rndindex]; +} + +void M_ClearRandom (void) +{ + rndindex = prndindex = 0; +} + + +void M_ClearBox (fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = MININT; + box[BOXBOTTOM] = box[BOXLEFT] = MAXINT; +} + +void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y) +{ + if (xbox[BOXRIGHT]) + box[BOXRIGHT] = x; + if (ybox[BOXTOP]) + box[BOXTOP] = y; +} + +/* +================== += += M_WriteFile += +================== +*/ + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +boolean M_WriteFile (char const *name, void *source, int length) +{ + int handle, count; + + handle = open (name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (handle == -1) + return false; + count = write (handle, source, length); + close (handle); + + if (count < length) + return false; + + return true; +} + +//========================================================================== +// +// M_ReadFile +// +// Read a file into a buffer allocated using Z_Malloc(). +// +//========================================================================== + +int M_ReadFile(char const *name, byte **buffer) +{ + return ReadFile(name, buffer, MALLOC_ZONE); +} + +//========================================================================== +// +// M_ReadFileCLib +// +// Read a file into a buffer allocated using malloc(). +// +//========================================================================== + +int M_ReadFileCLib(char const *name, byte **buffer) +{ + return ReadFile(name, buffer, MALLOC_CLIB); +} + +//========================================================================== +// +// ReadFile +// +//========================================================================== + +static int ReadFile(char const *name, byte **buffer, int mallocType) +{ + int handle, count, length; + struct stat fileinfo; + byte *buf; + + handle = open(name, O_RDONLY|O_BINARY, 0666); + if(handle == -1) + { + I_Error("Couldn't read file %s", name); + } + if(fstat(handle, &fileinfo) == -1) + { + I_Error("Couldn't read file %s", name); + } + length = fileinfo.st_size; + if(mallocType == MALLOC_ZONE) + { // Use zone memory allocation + buf = Z_Malloc(length, PU_STATIC, NULL); + } + else + { // Use c library memory allocation + buf = malloc(length); + if(buf == NULL) + { + I_Error("Couldn't malloc buffer %d for file %s.", + length, name); + } + } + count = read(handle, buf, length); + close(handle); + if(count < length) + { + I_Error("Couldn't read file %s", name); + } + *buffer = buf; + return length; +} + +//--------------------------------------------------------------------------- +// +// PROC M_FindResponseFile +// +//--------------------------------------------------------------------------- + +#define MAXARGVS 100 + +void M_FindResponseFile(void) +{ + int i; + + for(i = 1; i < myargc; i++) + { + if(myargv[i][0] == '@') + { + FILE *handle; + int size; + int k; + int index; + int indexinfile; + char *infile; + char *file; + char *moreargs[20]; + char *firstargv; + + // READ THE RESPONSE FILE INTO MEMORY + handle = fopen(&myargv[i][1], "rb"); + if(!handle) + { + printf("\nNo such response file!"); + exit(1); + } + ST_Message("Found response file %s!\n",&myargv[i][1]); + fseek (handle,0,SEEK_END); + size = ftell(handle); + fseek (handle,0,SEEK_SET); + file = malloc (size); + fread (file,size,1,handle); + fclose (handle); + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + for (index = 0,k = i+1; k < myargc; k++) + moreargs[index++] = myargv[k]; + + firstargv = myargv[0]; + myargv = malloc(sizeof(char *)*MAXARGVS); + memset(myargv,0,sizeof(char *)*MAXARGVS); + myargv[0] = firstargv; + + infile = file; + indexinfile = k = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do + { + myargv[indexinfile++] = infile+k; + while(k < size && + + ((*(infile+k)>= ' '+1) && (*(infile+k)<='z'))) + k++; + *(infile+k) = 0; + while(k < size && + ((*(infile+k)<= ' ') || (*(infile+k)>'z'))) + k++; + } while(k < size); + + for (k = 0;k < index;k++) + myargv[indexinfile++] = moreargs[k]; + myargc = indexinfile; + // DISPLAY ARGS + if(M_CheckParm("-debug")) + { + ST_Message("%d command-line args:\n", myargc); + for(k = 1; k < myargc; k++) + { + ST_Message("%s\n", myargv[k]); + } + } + break; + } + } +} + +//--------------------------------------------------------------------------- +// +// PROC M_ForceUppercase +// +// Change string to uppercase. +// +//--------------------------------------------------------------------------- + +void M_ForceUppercase(char *text) +{ + char c; + + while((c = *text) != 0) + { + if(c >= 'a' && c <= 'z') + { + *text++ = c-('a'-'A'); + } + else + { + text++; + } + } +} + +/* +============================================================================== + + DEFAULTS + +============================================================================== +*/ + +int usemouse; +int usejoystick; + +extern int key_right, key_left, key_up, key_down; +extern int key_strafeleft, key_straferight, key_jump; +extern int key_fire, key_use, key_strafe, key_speed; +extern int key_flyup, key_flydown, key_flycenter; +extern int key_lookup, key_lookdown, key_lookcenter; +extern int key_invleft, key_invright, key_useartifact; + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousebjump; + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; +extern int joybjump; + +extern boolean messageson; + +extern int viewwidth, viewheight; + +int mouseSensitivity; + +extern int screenblocks; + +extern char *chat_macros[10]; + +typedef struct +{ + char *name; + int *location; + int defaultvalue; + int scantranslate; // PC scan code hack + int untranslated; // lousy hack +} default_t; + +#ifndef __NeXT__ +extern int snd_Channels; +extern int snd_DesiredMusicDevice, snd_DesiredSfxDevice; +extern int snd_MusicDevice, // current music card # (index to dmxCodes) + snd_SfxDevice; // current sfx card # (index to dmxCodes) + +extern int snd_SBport, snd_SBirq, snd_SBdma; // sound blaster variables +extern int snd_Mport; // midi variables +#endif + +default_t defaults[] = +{ + { "mouse_sensitivity", &mouseSensitivity, 5 }, + +#ifndef __NeXT__ + { "sfx_volume", &snd_MaxVolume, 10}, + { "music_volume", &snd_MusicVolume, 10}, +#endif + +#ifdef __WATCOMC__ +#define SC_UPARROW 0x48 +#define SC_DOWNARROW 0x50 +#define SC_LEFTARROW 0x4b +#define SC_RIGHTARROW 0x4d +#define SC_RCTRL 0x1d +#define SC_RALT 0x38 +#define SC_RSHIFT 0x36 +#define SC_SPACE 0x39 +#define SC_COMMA 0x33 +#define SC_PERIOD 0x34 +#define SC_PAGEUP 0x49 +#define SC_INSERT 0x52 +#define SC_HOME 0x47 +#define SC_PAGEDOWN 0x51 +#define SC_DELETE 0x53 +#define SC_END 0x4f +#define SC_ENTER 0x1c +#define SC_SLASH 0X35 + + { "key_right", &key_right, SC_RIGHTARROW, 1 }, + { "key_left", &key_left, SC_LEFTARROW, 1 }, + { "key_up", &key_up, SC_UPARROW, 1 }, + { "key_down", &key_down, SC_DOWNARROW, 1 }, + { "key_strafeleft", &key_strafeleft, SC_COMMA, 1 }, + { "key_straferight", &key_straferight, SC_PERIOD, 1 }, + { "key_jump", &key_jump, SC_SLASH, 1}, + { "key_flyup", &key_flyup, SC_PAGEUP, 1 }, + { "key_flydown", &key_flydown, SC_INSERT, 1 }, + { "key_flycenter", &key_flycenter, SC_HOME, 1 }, + { "key_lookup", &key_lookup, SC_PAGEDOWN, 1 }, + { "key_lookdown", &key_lookdown, SC_DELETE, 1 }, + { "key_lookcenter", &key_lookcenter, SC_END, 1 }, + { "key_invleft", &key_invleft, 0x1a, 1 }, + { "key_invright", &key_invright, 0x1b, 1 }, + { "key_useartifact", &key_useartifact, SC_ENTER, 1 }, + + { "key_fire", &key_fire, SC_RCTRL, 1 }, + { "key_use", &key_use, SC_SPACE, 1 }, + { "key_strafe", &key_strafe, SC_RALT, 1 }, + { "key_speed", &key_speed, SC_RSHIFT, 1 }, +#endif + +#ifdef __NeXT__ + { "key_right", &key_right, KEY_RIGHTARROW }, + { "key_left", &key_left, KEY_LEFTARROW }, + { "key_up", &key_up, KEY_UPARROW }, + { "key_down", &key_down, KEY_DOWNARROW }, + { "key_strafeleft", &key_strafeleft, ',' }, + { "key_straferight", &key_straferight, '.' }, + { "key_jump", &key_jump, '/'}, + { "key_flyup", &key_flyup, 'u' }, + { "key_flydown", &key_flydown, 'j' }, + { "key_flycenter", &key_flycenter, 'k' }, + { "key_lookup", &key_lookup, 'm' }, + { "key_lookdown", &key_lookdown, 'b' }, + { "key_lookcenter", &key_lookcenter, 'n' }, + { "key_invleft", &key_invleft, '[' }, + { "key_invright", &key_invright, ']' }, + { "key_useartifact", &key_useartifact, 13 }, + + { "key_fire", &key_fire, ' ', 1 }, + { "key_use", &key_use, 'x', 1 }, + { "key_strafe", &key_strafe, 'c', 1 }, + { "key_speed", &key_speed, 'z', 1 }, +#endif + + { "use_mouse", &usemouse, 1 }, + { "mouseb_fire", &mousebfire, 0 }, + { "mouseb_strafe", &mousebstrafe, 1 }, + { "mouseb_forward", &mousebforward, 2 }, + { "mouseb_jump", &mousebjump, -1 }, + + { "use_joystick", &usejoystick, 0 }, + { "joyb_fire", &joybfire, 0 }, + { "joyb_strafe", &joybstrafe, 1 }, + { "joyb_use", &joybuse, 3 }, + { "joyb_speed", &joybspeed, 2 }, + { "joyb_jump", &joybjump, -1 }, + + { "screenblocks", &screenblocks, 10 }, + +#ifndef __NeXT__ + { "snd_channels", &snd_Channels, 3 }, + { "snd_musicdevice", &snd_DesiredMusicDevice, 0 }, + { "snd_sfxdevice", &snd_DesiredSfxDevice, 0 }, + { "snd_sbport", &snd_SBport, 544 }, + { "snd_sbirq", &snd_SBirq, -1 }, + { "snd_sbdma", &snd_SBdma, -1 }, + { "snd_mport", &snd_Mport, -1 }, +#endif + + { "usegamma", &usegamma, 0 }, + +#ifdef __NeXT__ + #define DEFAULT_SAVEPATH "hexndata/" +#endif +#ifdef __WATCOMC__ + #define DEFAULT_SAVEPATH "hexndata\\" +#endif + { "savedir", (int *) &SavePath, (int) DEFAULT_SAVEPATH }, + + { "messageson", (int *) &messageson, 1 }, + + { "chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 }, + { "chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 }, + { "chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 }, + { "chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 }, + { "chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 }, + { "chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 }, + { "chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 }, + { "chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 }, + { "chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 }, + { "chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 } +}; + +int numdefaults; +char defaultfile[128]; + +/* +============== += += M_SaveDefaults += +============== +*/ + +void M_SaveDefaults (void) +{ + int i,v; + FILE *f; + + f = fopen (defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + for (i=0 ; i -0xfff + && defaults[i].defaultvalue < 0xfff) + { + v = *defaults[i].location; + fprintf (f,"%s\t\t%i\n",defaults[i].name,v); + } else { + fprintf (f,"%s\t\t\"%s\"\n",defaults[i].name, + * (char **) (defaults[i].location)); + } + } + + fclose (f); +} + +//========================================================================== +// +// M_LoadDefaults +// +//========================================================================== + +extern byte scantokey[128]; + +void M_LoadDefaults(char *fileName) +{ + int i; + int len; + FILE *f; + char def[80]; + char strparm[100]; + char *newstring; + int parm; + boolean isstring; + + // Set everything to base values + numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for(i = 0; i < numdefaults; i++) + { + *defaults[i].location = defaults[i].defaultvalue; + } + + // Check for a custom config file + i = M_CheckParm("-config"); + if(i && i < myargc-1) + { + strcpy(defaultfile, myargv[i+1]); + ST_Message("config file: %s\n", defaultfile); + } + else if(cdrom) + { + sprintf(defaultfile, "c:\\hexndata\\%s", fileName); + } + else + { + strcpy(defaultfile, fileName); + } + + // Scan the config file + f = fopen(defaultfile, "r"); + if(f) + { + while(!feof(f)) + { + isstring = false; + if(fscanf(f, "%79s %[^\n]\n", def, strparm) == 2) + { + if(strparm[0] == '"') + { + // Get a string default + isstring = true; + len = strlen(strparm); + newstring = (char *)malloc(len); + if (newstring == NULL) I_Error("can't malloc newstring"); + strparm[len-1] = 0; + strcpy(newstring, strparm+1); + } + else if(strparm[0] == '0' && strparm[1] == 'x') + { + sscanf(strparm+2, "%x", &parm); + } + else + { + sscanf(strparm, "%i", &parm); + } + for(i = 0; i < numdefaults; i++) + { + if(!strcmp(def, defaults[i].name)) + { + if(!isstring) + { + *defaults[i].location = parm; + } + else + { + *defaults[i].location = (int)newstring; + } + break; + } + } + } + } + fclose (f); + } + +#ifdef __WATCOMC__ + // Translate the key scancodes + for(i = 0; i < numdefaults; i++) + { + if(defaults[i].scantranslate) + { + parm = *defaults[i].location; + defaults[i].untranslated = parm; + *defaults[i].location = scantokey[parm]; + } + } +#endif +} + +/* +============================================================================== + + SCREEN SHOTS + +============================================================================== +*/ + + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + +/* +============== += += WritePCXfile += +============== +*/ + +void WritePCXfile (char *filename, byte *data, int width, int height, byte *palette) +{ + int i, length; + pcx_t *pcx; + byte *pack; + + pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = SHORT(width-1); + pcx->ymax = SHORT(height-1); + pcx->hres = SHORT(width); + pcx->vres = SHORT(height); + memset (pcx->palette,0,sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = SHORT(width); + pcx->palette_type = SHORT(2); // not a grey scale + memset (pcx->filler,0,sizeof(pcx->filler)); + +// +// pack the image +// + pack = &pcx->data; + + for (i=0 ; i +#include "h2def.h" +#include "p_local.h" +#include "r_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define LEFT_DIR 0 +#define RIGHT_DIR 1 +#define ITEM_HEIGHT 20 +#define SELECTOR_XOFFSET (-28) +#define SELECTOR_YOFFSET (-1) +#define SLOTTEXTLEN 16 +#define ASCII_CURSOR '[' + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + ITT_EMPTY, + ITT_EFUNC, + ITT_LRFUNC, + ITT_SETMENU, + ITT_INERT +} ItemType_t; + +typedef enum +{ + MENU_MAIN, + MENU_CLASS, + MENU_SKILL, + MENU_OPTIONS, + MENU_OPTIONS2, + MENU_FILES, + MENU_LOAD, + MENU_SAVE, + MENU_NONE +} MenuType_t; + +typedef struct +{ + ItemType_t type; + char *text; + void (*func)(int option); + int option; + MenuType_t menu; +} MenuItem_t; + +typedef struct +{ + int x; + int y; + void (*drawFunc)(void); + int itemCount; + MenuItem_t *items; + int oldItPos; + MenuType_t prevMenu; +} Menu_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void InitFonts(void); +static void SetMenu(MenuType_t menu); +static void SCQuitGame(int option); +static void SCClass(int option); +static void SCSkill(int option); +static void SCMouseSensi(int option); +static void SCSfxVolume(int option); +static void SCMusicVolume(int option); +static void SCScreenSize(int option); +static boolean SCNetCheck(int option); +static void SCNetCheck2(int option); +static void SCLoadGame(int option); +static void SCSaveGame(int option); +static void SCMessages(int option); +static void SCEndGame(int option); +static void SCInfo(int option); +static void DrawMainMenu(void); +static void DrawClassMenu(void); +static void DrawSkillMenu(void); +static void DrawOptionsMenu(void); +static void DrawOptions2Menu(void); +static void DrawFileSlots(Menu_t *menu); +static void DrawFilesMenu(void); +static void MN_DrawInfo(void); +static void DrawLoadMenu(void); +static void DrawSaveMenu(void); +static void DrawSlider(Menu_t *menu, int item, int width, int slot); +void MN_LoadSlotText(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern int detailLevel; +extern int screenblocks; +extern char *SavePath; +extern int key_speed, key_strafe; +extern boolean gamekeydown[256]; // The NUMKEYS macro is local to g_game + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +boolean MenuActive; +int InfoType; +boolean messageson; +boolean mn_SuicideConsole; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int FontABaseLump; +static int FontAYellowBaseLump; +static int FontBBaseLump; +static int MauloBaseLump; +static Menu_t *CurrentMenu; +static int CurrentItPos; +static int MenuPClass; +static int MenuTime; +static boolean soundchanged; + +boolean askforquit; +boolean typeofask; +static boolean FileMenuKeySteal; +static boolean slottextloaded; +static char SlotText[6][SLOTTEXTLEN+2]; +static char oldSlotText[SLOTTEXTLEN+2]; +static int SlotStatus[6]; +static int slotptr; +static int currentSlot; +static int quicksave; +static int quickload; + +static MenuItem_t MainItems[] = +{ + { ITT_SETMENU, "NEW GAME", SCNetCheck2, 1, MENU_CLASS }, + { ITT_SETMENU, "OPTIONS", NULL, 0, MENU_OPTIONS }, + { ITT_SETMENU, "GAME FILES", NULL, 0, MENU_FILES }, + { ITT_EFUNC, "INFO", SCInfo, 0, MENU_NONE }, + { ITT_EFUNC, "QUIT GAME", SCQuitGame, 0, MENU_NONE } +}; + +static Menu_t MainMenu = +{ + 110, 56, + DrawMainMenu, + 5, MainItems, + 0, + MENU_NONE +}; + +static MenuItem_t ClassItems[] = +{ + { ITT_EFUNC, "FIGHTER", SCClass, 0, MENU_NONE }, + { ITT_EFUNC, "CLERIC", SCClass, 1, MENU_NONE }, + { ITT_EFUNC, "MAGE", SCClass, 2, MENU_NONE } +}; + +static Menu_t ClassMenu = +{ + 66, 66, + DrawClassMenu, + 3, ClassItems, + 0, + MENU_MAIN +}; + +static MenuItem_t FilesItems[] = +{ + { ITT_SETMENU, "LOAD GAME", SCNetCheck2, 2, MENU_LOAD }, + { ITT_SETMENU, "SAVE GAME", NULL, 0, MENU_SAVE } +}; + +static Menu_t FilesMenu = +{ + 110, 60, + DrawFilesMenu, + 2, FilesItems, + 0, + MENU_MAIN +}; + +static MenuItem_t LoadItems[] = +{ + { ITT_EFUNC, NULL, SCLoadGame, 0, MENU_NONE }, + { ITT_EFUNC, NULL, SCLoadGame, 1, MENU_NONE }, + { ITT_EFUNC, NULL, SCLoadGame, 2, MENU_NONE }, + { ITT_EFUNC, NULL, SCLoadGame, 3, MENU_NONE }, + { ITT_EFUNC, NULL, SCLoadGame, 4, MENU_NONE }, + { ITT_EFUNC, NULL, SCLoadGame, 5, MENU_NONE } +}; + +static Menu_t LoadMenu = +{ + 70, 30, + DrawLoadMenu, + 6, LoadItems, + 0, + MENU_FILES +}; + +static MenuItem_t SaveItems[] = +{ + { ITT_EFUNC, NULL, SCSaveGame, 0, MENU_NONE }, + { ITT_EFUNC, NULL, SCSaveGame, 1, MENU_NONE }, + { ITT_EFUNC, NULL, SCSaveGame, 2, MENU_NONE }, + { ITT_EFUNC, NULL, SCSaveGame, 3, MENU_NONE }, + { ITT_EFUNC, NULL, SCSaveGame, 4, MENU_NONE }, + { ITT_EFUNC, NULL, SCSaveGame, 5, MENU_NONE } +}; + +static Menu_t SaveMenu = +{ + 70, 30, + DrawSaveMenu, + 6, SaveItems, + 0, + MENU_FILES +}; + +static MenuItem_t SkillItems[] = +{ + { ITT_EFUNC, NULL, SCSkill, sk_baby, MENU_NONE }, + { ITT_EFUNC, NULL, SCSkill, sk_easy, MENU_NONE }, + { ITT_EFUNC, NULL, SCSkill, sk_medium, MENU_NONE }, + { ITT_EFUNC, NULL, SCSkill, sk_hard, MENU_NONE }, + { ITT_EFUNC, NULL, SCSkill, sk_nightmare, MENU_NONE } +}; + +static Menu_t SkillMenu = +{ + 120, 44, + DrawSkillMenu, + 5, SkillItems, + 2, + MENU_CLASS +}; + +static MenuItem_t OptionsItems[] = +{ + { ITT_EFUNC, "END GAME", SCEndGame, 0, MENU_NONE }, + { ITT_EFUNC, "MESSAGES : ", SCMessages, 0, MENU_NONE }, + { ITT_LRFUNC, "MOUSE SENSITIVITY", SCMouseSensi, 0, MENU_NONE }, + { ITT_EMPTY, NULL, NULL, 0, MENU_NONE }, + { ITT_SETMENU, "MORE...", NULL, 0, MENU_OPTIONS2 } +}; + +static Menu_t OptionsMenu = +{ + 88, 30, + DrawOptionsMenu, + 5, OptionsItems, + 0, + MENU_MAIN +}; + +static MenuItem_t Options2Items[] = +{ + { ITT_LRFUNC, "SCREEN SIZE", SCScreenSize, 0, MENU_NONE }, + { ITT_EMPTY, NULL, NULL, 0, MENU_NONE }, + { ITT_LRFUNC, "SFX VOLUME", SCSfxVolume, 0, MENU_NONE }, + { ITT_EMPTY, NULL, NULL, 0, MENU_NONE }, + { ITT_LRFUNC, "MUSIC VOLUME", SCMusicVolume, 0, MENU_NONE }, + { ITT_EMPTY, NULL, NULL, 0, MENU_NONE } +}; + +static Menu_t Options2Menu = +{ + 90, 20, + DrawOptions2Menu, + 6, Options2Items, + 0, + MENU_OPTIONS +}; + +static Menu_t *Menus[] = +{ + &MainMenu, + &ClassMenu, + &SkillMenu, + &OptionsMenu, + &Options2Menu, + &FilesMenu, + &LoadMenu, + &SaveMenu +}; + +#ifdef __WATCOMC__ +static char *GammaText[] = +{ + TXT_GAMMA_LEVEL_OFF, + TXT_GAMMA_LEVEL_1, + TXT_GAMMA_LEVEL_2, + TXT_GAMMA_LEVEL_3, + TXT_GAMMA_LEVEL_4 +}; +#endif + +// CODE -------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// PROC MN_Init +// +//--------------------------------------------------------------------------- + +void MN_Init(void) +{ + InitFonts(); + MenuActive = false; +// messageson = true; // Set by defaults in .CFG + MauloBaseLump = W_GetNumForName("FBULA0"); // ("M_SKL00"); +} + +//--------------------------------------------------------------------------- +// +// PROC InitFonts +// +//--------------------------------------------------------------------------- + +static void InitFonts(void) +{ + FontABaseLump = W_GetNumForName("FONTA_S")+1; + FontAYellowBaseLump = W_GetNumForName("FONTAY_S")+1; + FontBBaseLump = W_GetNumForName("FONTB_S")+1; +} + +//--------------------------------------------------------------------------- +// +// PROC MN_DrTextA +// +// Draw text using font A. +// +//--------------------------------------------------------------------------- + +void MN_DrTextA(char *text, int x, int y) +{ + char c; + patch_t *p; + + while((c = *text++) != 0) + { + if(c < 33) + { + x += 5; + } + else + { + p = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + V_DrawPatch(x, y, p); + x += p->width-1; + } + } +} + +//========================================================================== +// +// MN_DrTextAYellow +// +//========================================================================== + +void MN_DrTextAYellow(char *text, int x, int y) +{ + char c; + patch_t *p; + + while((c = *text++) != 0) + { + if(c < 33) + { + x += 5; + } + else + { + p = W_CacheLumpNum(FontAYellowBaseLump+c-33, PU_CACHE); + V_DrawPatch(x, y, p); + x += p->width-1; + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC MN_TextAWidth +// +// Returns the pixel width of a string using font A. +// +//--------------------------------------------------------------------------- + +int MN_TextAWidth(char *text) +{ + char c; + int width; + patch_t *p; + + width = 0; + while((c = *text++) != 0) + { + if(c < 33) + { + width += 5; + } + else + { + p = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE); + width += p->width-1; + } + } + return(width); +} + +//--------------------------------------------------------------------------- +// +// PROC MN_DrTextB +// +// Draw text using font B. +// +//--------------------------------------------------------------------------- + +void MN_DrTextB(char *text, int x, int y) +{ + char c; + patch_t *p; + + while((c = *text++) != 0) + { + if(c < 33) + { + x += 8; + } + else + { + p = W_CacheLumpNum(FontBBaseLump+c-33, PU_CACHE); + V_DrawPatch(x, y, p); + x += p->width-1; + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC MN_TextBWidth +// +// Returns the pixel width of a string using font B. +// +//--------------------------------------------------------------------------- + +int MN_TextBWidth(char *text) +{ + char c; + int width; + patch_t *p; + + width = 0; + while((c = *text++) != 0) + { + if(c < 33) + { + width += 5; + } + else + { + p = W_CacheLumpNum(FontBBaseLump+c-33, PU_CACHE); + width += p->width-1; + } + } + return(width); +} + +//--------------------------------------------------------------------------- +// +// PROC MN_Ticker +// +//--------------------------------------------------------------------------- + +void MN_Ticker(void) +{ + if(MenuActive == false) + { + return; + } + MenuTime++; +} + +//--------------------------------------------------------------------------- +// +// PROC MN_Drawer +// +//--------------------------------------------------------------------------- + +char *QuitEndMsg[] = +{ + "ARE YOU SURE YOU WANT TO QUIT?", + "ARE YOU SURE YOU WANT TO END THE GAME?", + "DO YOU WANT TO QUICKSAVE THE GAME NAMED", + "DO YOU WANT TO QUICKLOAD THE GAME NAMED", + "ARE YOU SURE YOU WANT TO SUICIDE?" +}; + + +#define BETA_FLASH_TEXT "BETA" + +void MN_Drawer(void) +{ + int i; + int x; + int y; + MenuItem_t *item; + char *selName; + +#ifdef TIMEBOMB + // Beta blinker *** + if(leveltime&16) + { + MN_DrTextA( BETA_FLASH_TEXT, + 160-(MN_TextAWidth(BETA_FLASH_TEXT)>>1), 12); + } +#endif // TIMEBOMB + + if(MenuActive == false) + { + if(askforquit) + { + MN_DrTextA(QuitEndMsg[typeofask-1], 160- + MN_TextAWidth(QuitEndMsg[typeofask-1])/2, 80); + if(typeofask == 3) + { + MN_DrTextA(SlotText[quicksave-1], 160- + MN_TextAWidth(SlotText[quicksave-1])/2, 90); + MN_DrTextA("?", 160+ + MN_TextAWidth(SlotText[quicksave-1])/2, 90); + } + if(typeofask == 4) + { + MN_DrTextA(SlotText[quickload-1], 160- + MN_TextAWidth(SlotText[quickload-1])/2, 90); + MN_DrTextA("?", 160+ + MN_TextAWidth(SlotText[quicksave-1])/2, 90); + } + UpdateState |= I_FULLSCRN; + } + return; + } + else + { + UpdateState |= I_FULLSCRN; + if(InfoType) + { + MN_DrawInfo(); + return; + } + if(screenblocks < 10) + { + BorderNeedRefresh = true; + } + if(CurrentMenu->drawFunc != NULL) + { + CurrentMenu->drawFunc(); + } + x = CurrentMenu->x; + y = CurrentMenu->y; + item = CurrentMenu->items; + for(i = 0; i < CurrentMenu->itemCount; i++) + { + if(item->type != ITT_EMPTY && item->text) + { + MN_DrTextB(item->text, x, y); + } + y += ITEM_HEIGHT; + item++; + } + y = CurrentMenu->y+(CurrentItPos*ITEM_HEIGHT)+SELECTOR_YOFFSET; + selName = MenuTime&16 ? "M_SLCTR1" : "M_SLCTR2"; + V_DrawPatch(x+SELECTOR_XOFFSET, y, + W_CacheLumpName(selName, PU_CACHE)); + } +} + +//--------------------------------------------------------------------------- +// +// PROC DrawMainMenu +// +//--------------------------------------------------------------------------- + +static void DrawMainMenu(void) +{ + int frame; + + frame = (MenuTime/5)%7; + V_DrawPatch(88, 0, W_CacheLumpName("M_HTIC", PU_CACHE)); +// Old Gold skull positions: (40, 10) and (232, 10) + V_DrawPatch(37, 80, W_CacheLumpNum(MauloBaseLump+(frame+2)%7, + PU_CACHE)); + V_DrawPatch(278, 80, W_CacheLumpNum(MauloBaseLump+frame, PU_CACHE)); +} + +//========================================================================== +// +// DrawClassMenu +// +//========================================================================== + +static void DrawClassMenu(void) +{ + pclass_t class; + static char *boxLumpName[3] = + { + "m_fbox", + "m_cbox", + "m_mbox" + }; + static char *walkLumpName[3] = + { + "m_fwalk1", + "m_cwalk1", + "m_mwalk1" + }; + + MN_DrTextB("CHOOSE CLASS:", 34, 24); + class = (pclass_t)CurrentMenu->items[CurrentItPos].option; + V_DrawPatch(174, 8, W_CacheLumpName(boxLumpName[class], PU_CACHE)); + V_DrawPatch(174+24, 8+12, + W_CacheLumpNum(W_GetNumForName(walkLumpName[class]) + +((MenuTime>>3)&3), PU_CACHE)); +} + +//--------------------------------------------------------------------------- +// +// PROC DrawSkillMenu +// +//--------------------------------------------------------------------------- + +static void DrawSkillMenu(void) +{ + MN_DrTextB("CHOOSE SKILL LEVEL:", 74, 16); +} + +//--------------------------------------------------------------------------- +// +// PROC DrawFilesMenu +// +//--------------------------------------------------------------------------- + +static void DrawFilesMenu(void) +{ +// clear out the quicksave/quickload stuff + quicksave = 0; + quickload = 0; + P_ClearMessage(&players[consoleplayer]); +} + +//--------------------------------------------------------------------------- +// +// PROC DrawLoadMenu +// +//--------------------------------------------------------------------------- + +static void DrawLoadMenu(void) +{ + MN_DrTextB("LOAD GAME", 160-MN_TextBWidth("LOAD GAME")/2, 10); + if(!slottextloaded) + { + MN_LoadSlotText(); + } + DrawFileSlots(&LoadMenu); +} + +//--------------------------------------------------------------------------- +// +// PROC DrawSaveMenu +// +//--------------------------------------------------------------------------- + +static void DrawSaveMenu(void) +{ + MN_DrTextB("SAVE GAME", 160-MN_TextBWidth("SAVE GAME")/2, 10); + if(!slottextloaded) + { + MN_LoadSlotText(); + } + DrawFileSlots(&SaveMenu); +} + +//=========================================================================== +// +// MN_LoadSlotText +// +// For each slot, looks for save games and reads the description field. +// +//=========================================================================== + +void MN_LoadSlotText(void) +{ + int slot; + FILE *fp; + char name[100]; + char versionText[HXS_VERSION_TEXT_LENGTH]; + char description[HXS_DESCRIPTION_LENGTH]; + boolean found; + + for(slot = 0; slot < 6; slot++) + { + found = false; + sprintf(name, "%shex%d.hxs", SavePath, slot); + fp = fopen(name, "rb"); + if(fp) + { + fread(description, HXS_DESCRIPTION_LENGTH, 1, fp); + fread(versionText, HXS_VERSION_TEXT_LENGTH, 1, fp); + fclose(fp); + if(!strcmp(versionText, HXS_VERSION_TEXT)) + { + found = true; + } + } + if(found) + { + memcpy(SlotText[slot], description, SLOTTEXTLEN); + SlotStatus[slot] = 1; + } + else + { + memset(SlotText[slot], 0, SLOTTEXTLEN); + SlotStatus[slot] = 0; + } + } + slottextloaded = true; +} + +//--------------------------------------------------------------------------- +// +// PROC DrawFileSlots +// +//--------------------------------------------------------------------------- + +static void DrawFileSlots(Menu_t *menu) +{ + int i; + int x; + int y; + + x = menu->x; + y = menu->y; + for(i = 0; i < 6; i++) + { + V_DrawPatch(x, y, W_CacheLumpName("M_FSLOT", PU_CACHE)); + if(SlotStatus[i]) + { + MN_DrTextA(SlotText[i], x+5, y+5); + } + y += ITEM_HEIGHT; + } +} + +//--------------------------------------------------------------------------- +// +// PROC DrawOptionsMenu +// +//--------------------------------------------------------------------------- + +static void DrawOptionsMenu(void) +{ + if(messageson) + { + MN_DrTextB("ON", 196, 50); + } + else + { + MN_DrTextB("OFF", 196, 50); + } + DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity); +} + +//--------------------------------------------------------------------------- +// +// PROC DrawOptions2Menu +// +//--------------------------------------------------------------------------- + +static void DrawOptions2Menu(void) +{ + DrawSlider(&Options2Menu, 1, 9, screenblocks-3); + DrawSlider(&Options2Menu, 3, 16, snd_MaxVolume); + DrawSlider(&Options2Menu, 5, 16, snd_MusicVolume); +} + +//--------------------------------------------------------------------------- +// +// PROC SCQuitGame +// +//--------------------------------------------------------------------------- + +static void SCQuitGame(int option) +{ + MenuActive = false; + askforquit = true; + typeofask = 1; //quit game + if(!netgame && !demoplayback) + { + paused = true; + } +} + +//--------------------------------------------------------------------------- +// +// PROC SCEndGame +// +//--------------------------------------------------------------------------- + +static void SCEndGame(int option) +{ + if(demoplayback) + { + return; + } + if(SCNetCheck(3)) + { + MenuActive = false; + askforquit = true; + typeofask = 2; //endgame + if(!netgame && !demoplayback) + { + paused = true; + } + } +} + +//--------------------------------------------------------------------------- +// +// PROC SCMessages +// +//--------------------------------------------------------------------------- + +static void SCMessages(int option) +{ + messageson ^= 1; + if(messageson) + { + P_SetMessage(&players[consoleplayer], "MESSAGES ON", true); + } + else + { + P_SetMessage(&players[consoleplayer], "MESSAGES OFF", true); + } + S_StartSound(NULL, SFX_CHAT); +} + +//=========================================================================== +// +// SCNetCheck +// +//=========================================================================== + +static boolean SCNetCheck(int option) +{ + if(!netgame) + { + return true; + } + switch(option) + { + case 1: // new game + P_SetMessage(&players[consoleplayer], + "YOU CAN'T START A NEW GAME IN NETPLAY!", true); + break; + case 2: // load game + P_SetMessage(&players[consoleplayer], + "YOU CAN'T LOAD A GAME IN NETPLAY!", true); + break; + case 3: // end game + P_SetMessage(&players[consoleplayer], + "YOU CAN'T END A GAME IN NETPLAY!", true); + break; + } + MenuActive = false; + S_StartSound(NULL, SFX_CHAT); + return false; +} + +//=========================================================================== +// +// SCNetCheck2 +// +//=========================================================================== + +static void SCNetCheck2(int option) +{ + SCNetCheck(option); + return; +} + +//--------------------------------------------------------------------------- +// +// PROC SCLoadGame +// +//--------------------------------------------------------------------------- + +static void SCLoadGame(int option) +{ + if(!SlotStatus[option]) + { // Don't try to load from an empty slot + return; + } + G_LoadGame(option); + MN_DeactivateMenu(); + BorderNeedRefresh = true; + if(quickload == -1) + { + quickload = option+1; + P_ClearMessage(&players[consoleplayer]); + } +} + +//--------------------------------------------------------------------------- +// +// PROC SCSaveGame +// +//--------------------------------------------------------------------------- + +static void SCSaveGame(int option) +{ + char *ptr; + + if(!FileMenuKeySteal) + { + FileMenuKeySteal = true; + strcpy(oldSlotText, SlotText[option]); + ptr = SlotText[option]; + while(*ptr) + { + ptr++; + } + *ptr = '['; + *(ptr+1) = 0; + SlotStatus[option]++; + currentSlot = option; + slotptr = ptr-SlotText[option]; + return; + } + else + { + G_SaveGame(option, SlotText[option]); + FileMenuKeySteal = false; + MN_DeactivateMenu(); + } + BorderNeedRefresh = true; + if(quicksave == -1) + { + quicksave = option+1; + P_ClearMessage(&players[consoleplayer]); + } +} + +//========================================================================== +// +// SCClass +// +//========================================================================== + +static void SCClass(int option) +{ + if(netgame) + { + P_SetMessage(&players[consoleplayer], + "YOU CAN'T START A NEW GAME FROM WITHIN A NETGAME!", true); + return; + } + MenuPClass = option; + switch(MenuPClass) + { + case PCLASS_FIGHTER: + SkillMenu.x = 120; + SkillItems[0].text = "SQUIRE"; + SkillItems[1].text = "KNIGHT"; + SkillItems[2].text = "WARRIOR"; + SkillItems[3].text = "BERSERKER"; + SkillItems[4].text = "TITAN"; + break; + case PCLASS_CLERIC: + SkillMenu.x = 116; + SkillItems[0].text = "ALTAR BOY"; + SkillItems[1].text = "ACOLYTE"; + SkillItems[2].text = "PRIEST"; + SkillItems[3].text = "CARDINAL"; + SkillItems[4].text = "POPE"; + break; + case PCLASS_MAGE: + SkillMenu.x = 112; + SkillItems[0].text = "APPRENTICE"; + SkillItems[1].text = "ENCHANTER"; + SkillItems[2].text = "SORCERER"; + SkillItems[3].text = "WARLOCK"; + SkillItems[4].text = "ARCHIMAGE"; + break; + } + SetMenu(MENU_SKILL); +} + +//--------------------------------------------------------------------------- +// +// PROC SCSkill +// +//--------------------------------------------------------------------------- + +static void SCSkill(int option) +{ + extern int SB_state; + + PlayerClass[consoleplayer] = MenuPClass; + G_DeferredNewGame(option); + SB_SetClassData(); + SB_state = -1; + MN_DeactivateMenu(); +} + +//--------------------------------------------------------------------------- +// +// PROC SCMouseSensi +// +//--------------------------------------------------------------------------- + +static void SCMouseSensi(int option) +{ + if(option == RIGHT_DIR) + { + if(mouseSensitivity < 9) + { + mouseSensitivity++; + } + } + else if(mouseSensitivity) + { + mouseSensitivity--; + } +} + +//--------------------------------------------------------------------------- +// +// PROC SCSfxVolume +// +//--------------------------------------------------------------------------- + +static void SCSfxVolume(int option) +{ + if(option == RIGHT_DIR) + { + if(snd_MaxVolume < 15) + { + snd_MaxVolume++; + } + } + else if(snd_MaxVolume) + { + snd_MaxVolume--; + } + soundchanged = true; // we'll set it when we leave the menu +} + +//--------------------------------------------------------------------------- +// +// PROC SCMusicVolume +// +//--------------------------------------------------------------------------- + +static void SCMusicVolume(int option) +{ + if(option == RIGHT_DIR) + { + if(snd_MusicVolume < 15) + { + snd_MusicVolume++; + } + } + else if(snd_MusicVolume) + { + snd_MusicVolume--; + } + S_SetMusicVolume(); +} + +//--------------------------------------------------------------------------- +// +// PROC SCScreenSize +// +//--------------------------------------------------------------------------- + +static void SCScreenSize(int option) +{ + if(option == RIGHT_DIR) + { + if(screenblocks < 11) + { + screenblocks++; + } + } + else if(screenblocks > 3) + { + screenblocks--; + } + R_SetViewSize(screenblocks, detailLevel); +} + +//--------------------------------------------------------------------------- +// +// PROC SCInfo +// +//--------------------------------------------------------------------------- + +static void SCInfo(int option) +{ + InfoType = 1; + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + if(!netgame && !demoplayback) + { + paused = true; + } +} + +//--------------------------------------------------------------------------- +// +// FUNC MN_Responder +// +//--------------------------------------------------------------------------- + +boolean MN_Responder(event_t *event) +{ + int key; + int i; + MenuItem_t *item; + extern boolean automapactive; + static boolean shiftdown; + extern void H2_StartTitle(void); + extern void G_CheckDemoStatus(void); + char *textBuffer; + + if(event->data1 == KEY_RSHIFT) + { + shiftdown = (event->type == ev_keydown); + } + if(event->type != ev_keydown) + { + return(false); + } + key = event->data1; + if(InfoType) + { + if(shareware) + { + InfoType = (InfoType+1)%5; + } + else + { + InfoType = (InfoType+1)%4; + } + if(key == KEY_ESCAPE) + { + InfoType = 0; + } + if(!InfoType) + { + if(!netgame && !demoplayback) + { + paused = false; + } + MN_DeactivateMenu(); + SB_state = -1; //refresh the statbar + BorderNeedRefresh = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + return(true); //make the info screen eat the keypress + } + + if(ravpic && key == KEY_F1) + { + G_ScreenShot(); + return(true); + } + + if(askforquit) + { + switch(key) + { + case 'y': + if(askforquit) + { + switch(typeofask) + { + case 1: + G_CheckDemoStatus(); + I_Quit(); + break; + case 2: + P_ClearMessage(&players[consoleplayer]); + typeofask = 0; + askforquit = false; + paused = false; + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + H2_StartTitle(); // go to intro/demo mode. + break; + case 3: + P_SetMessage(&players[consoleplayer], + "QUICKSAVING....", false); + FileMenuKeySteal = true; + SCSaveGame(quicksave-1); + askforquit = false; + typeofask = 0; + BorderNeedRefresh = true; + return true; + case 4: + P_SetMessage(&players[consoleplayer], + "QUICKLOADING....", false); + SCLoadGame(quickload-1); + askforquit = false; + typeofask = 0; + BorderNeedRefresh = true; + return true; + case 5: + askforquit = false; + typeofask = 0; + BorderNeedRefresh = true; + mn_SuicideConsole = true; + return true; + break; + default: + return true; // eat the 'y' keypress + } + } + return false; + case 'n': + case KEY_ESCAPE: + if(askforquit) + { + players[consoleplayer].messageTics = 0; + askforquit = false; + typeofask = 0; + paused = false; + UpdateState |= I_FULLSCRN; + BorderNeedRefresh = true; + return true; + } + return false; + } + return false; // don't let the keys filter thru + } + if(MenuActive == false && !chatmodeon) + { + switch(key) + { + case KEY_MINUS: + if(automapactive) + { // Don't screen size in automap + return(false); + } + SCScreenSize(LEFT_DIR); + S_StartSound(NULL, SFX_PICKUP_KEY); + BorderNeedRefresh = true; + UpdateState |= I_FULLSCRN; + return(true); + case KEY_EQUALS: + if(automapactive) + { // Don't screen size in automap + return(false); + } + SCScreenSize(RIGHT_DIR); + S_StartSound(NULL, SFX_PICKUP_KEY); + BorderNeedRefresh = true; + UpdateState |= I_FULLSCRN; + return(true); +#ifdef __NeXT__ + case 'q': + MenuActive = false; + askforquit = true; + typeofask = 5; // suicide + return true; +#endif +#ifndef __NeXT__ + case KEY_F1: // help screen + SCInfo(0); // start up info screens + MenuActive = true; + return(true); + case KEY_F2: // save game + if(gamestate == GS_LEVEL && !demoplayback) + { + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &SaveMenu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + slottextloaded = false; //reload the slot text, when needed + } + return true; + case KEY_F3: // load game + if(SCNetCheck(2)) + { + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &LoadMenu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + slottextloaded = false; //reload the slot text, when needed + } + return true; + case KEY_F4: // volume + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &Options2Menu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + slottextloaded = false; //reload the slot text, when needed + return true; + case KEY_F5: + MenuActive = false; + askforquit = true; + typeofask = 5; // suicide + return true; + case KEY_F6: // quicksave + if(gamestate == GS_LEVEL && !demoplayback) + { + if(!quicksave || quicksave == -1) + { + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &SaveMenu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + slottextloaded = false; //reload the slot text + quicksave = -1; + P_SetMessage(&players[consoleplayer], + "CHOOSE A QUICKSAVE SLOT", true); + } + else + { + askforquit = true; + typeofask = 3; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_CHAT); + } + } + return true; + case KEY_F7: // endgame + if(SCNetCheck(3)) + { + if(gamestate == GS_LEVEL && !demoplayback) + { + S_StartSound(NULL, SFX_CHAT); + SCEndGame(0); + } + } + return true; + case KEY_F8: // toggle messages + SCMessages(0); + return true; + case KEY_F9: // quickload + if(SCNetCheck(2)) + { + if(!quickload || quickload == -1) + { + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &LoadMenu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + slottextloaded = false; // reload the slot text + quickload = -1; + P_SetMessage(&players[consoleplayer], + "CHOOSE A QUICKLOAD SLOT", true); + } + else + { + askforquit = true; + if(!netgame && !demoplayback) + { + paused = true; + } + typeofask = 4; + S_StartSound(NULL, SFX_CHAT); + } + } + return true; + case KEY_F10: // quit + if(gamestate == GS_LEVEL || gamestate == GS_FINALE) + { + SCQuitGame(0); + S_StartSound(NULL, SFX_CHAT); + } + return true; + case KEY_F11: // F11 - gamma mode correction + usegamma++; + if(usegamma > 4) + { + usegamma = 0; + } + SB_PaletteFlash(true); // force change + P_SetMessage(&players[consoleplayer], GammaText[usegamma], + false); + return true; + case KEY_F12: // F12 - reload current map (devmaps mode) + if(netgame || DevMaps == false) + { + return false; + } + if(gamekeydown[key_speed]) + { // Monsters ON + nomonsters = false; + } + if(gamekeydown[key_strafe]) + { // Monsters OFF + nomonsters = true; + } + G_DeferedInitNew(gameskill, gameepisode, gamemap); + P_SetMessage(&players[consoleplayer], TXT_CHEATWARP, + false); + return true; +#endif + } + + } + + if(MenuActive == false) + { + if(key == KEY_ESCAPE || gamestate == GS_DEMOSCREEN || demoplayback) + { + MN_ActivateMenu(); + return(true); + } + return(false); + } + if(!FileMenuKeySteal) + { + item = &CurrentMenu->items[CurrentItPos]; + switch(key) + { + case KEY_DOWNARROW: + do + { + if(CurrentItPos+1 > CurrentMenu->itemCount-1) + { + CurrentItPos = 0; + } + else + { + CurrentItPos++; + } + } while(CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); + S_StartSound(NULL, SFX_FIGHTER_HAMMER_HITWALL); + return(true); + break; + case KEY_UPARROW: + do + { + if(CurrentItPos == 0) + { + CurrentItPos = CurrentMenu->itemCount-1; + } + else + { + CurrentItPos--; + } + } while(CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); + S_StartSound(NULL, SFX_FIGHTER_HAMMER_HITWALL); + return(true); + break; + case KEY_LEFTARROW: + if(item->type == ITT_LRFUNC && item->func != NULL) + { + item->func(LEFT_DIR); + S_StartSound(NULL, SFX_PICKUP_KEY); + } + return(true); + break; + case KEY_RIGHTARROW: + if(item->type == ITT_LRFUNC && item->func != NULL) + { + item->func(RIGHT_DIR); + S_StartSound(NULL, SFX_PICKUP_KEY); + } + return(true); + break; + case KEY_ENTER: + if(item->type == ITT_SETMENU) + { + if(item->func != NULL) + { + item->func(item->option); + } + SetMenu(item->menu); + } + else if(item->func != NULL) + { + CurrentMenu->oldItPos = CurrentItPos; + if(item->type == ITT_LRFUNC) + { + item->func(RIGHT_DIR); + } + else if(item->type == ITT_EFUNC) + { + item->func(item->option); + } + } + S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); + return(true); + break; + case KEY_ESCAPE: + MN_DeactivateMenu(); + return(true); + case KEY_BACKSPACE: + S_StartSound(NULL, SFX_PICKUP_KEY); + if(CurrentMenu->prevMenu == MENU_NONE) + { + MN_DeactivateMenu(); + } + else + { + SetMenu(CurrentMenu->prevMenu); + } + return(true); + default: + for(i = 0; i < CurrentMenu->itemCount; i++) + { + if(CurrentMenu->items[i].text) + { + if(toupper(key) + == toupper(CurrentMenu->items[i].text[0])) + { + CurrentItPos = i; + return(true); + } + } + } + break; + } + return(false); + } + else + { // Editing file names + textBuffer = &SlotText[currentSlot][slotptr]; + if(key == KEY_BACKSPACE) + { + if(slotptr) + { + *textBuffer-- = 0; + *textBuffer = ASCII_CURSOR; + slotptr--; + } + return(true); + } + if(key == KEY_ESCAPE) + { + memset(SlotText[currentSlot], 0, SLOTTEXTLEN+2); + strcpy(SlotText[currentSlot], oldSlotText); + SlotStatus[currentSlot]--; + MN_DeactivateMenu(); + return(true); + } + if(key == KEY_ENTER) + { + SlotText[currentSlot][slotptr] = 0; // clear the cursor + item = &CurrentMenu->items[CurrentItPos]; + CurrentMenu->oldItPos = CurrentItPos; + if(item->type == ITT_EFUNC) + { + item->func(item->option); + if(item->menu != MENU_NONE) + { + SetMenu(item->menu); + } + } + return(true); + } + if(slotptr < SLOTTEXTLEN && key != KEY_BACKSPACE) + { + if((key >= 'a' && key <= 'z')) + { + *textBuffer++ = key-32; + *textBuffer = ASCII_CURSOR; + slotptr++; + return(true); + } + if(((key >= '0' && key <= '9') || key == ' ' + || key == ',' || key == '.' || key == '-') + && !shiftdown) + { + *textBuffer++ = key; + *textBuffer = ASCII_CURSOR; + slotptr++; + return(true); + } + if(shiftdown && key == '1') + { + *textBuffer++ = '!'; + *textBuffer = ASCII_CURSOR; + slotptr++; + return(true); + } + } + return(true); + } + return(false); +} + +//--------------------------------------------------------------------------- +// +// PROC MN_ActivateMenu +// +//--------------------------------------------------------------------------- + +void MN_ActivateMenu(void) +{ + if(MenuActive) + { + return; + } + if(paused) + { + S_ResumeSound(); + } + MenuActive = true; + FileMenuKeySteal = false; + MenuTime = 0; + CurrentMenu = &MainMenu; + CurrentItPos = CurrentMenu->oldItPos; + if(!netgame && !demoplayback) + { + paused = true; + } + S_StartSound(NULL, SFX_PLATFORM_STOP); + slottextloaded = false; //reload the slot text, when needed +} + +//--------------------------------------------------------------------------- +// +// PROC MN_DeactivateMenu +// +//--------------------------------------------------------------------------- + +void MN_DeactivateMenu(void) +{ + CurrentMenu->oldItPos = CurrentItPos; + MenuActive = false; + if(!netgame) + { + paused = false; + } + S_StartSound(NULL, SFX_PLATFORM_STOP); + P_ClearMessage(&players[consoleplayer]); +} + +//--------------------------------------------------------------------------- +// +// PROC MN_DrawInfo +// +//--------------------------------------------------------------------------- + +void MN_DrawInfo(void) +{ + I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + memcpy(screen, (byte *)W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType, + PU_CACHE), SCREENWIDTH*SCREENHEIGHT); +// V_DrawPatch(0, 0, W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType, +// PU_CACHE)); +} + + +//--------------------------------------------------------------------------- +// +// PROC SetMenu +// +//--------------------------------------------------------------------------- + +static void SetMenu(MenuType_t menu) +{ + CurrentMenu->oldItPos = CurrentItPos; + CurrentMenu = Menus[menu]; + CurrentItPos = CurrentMenu->oldItPos; +} + +//--------------------------------------------------------------------------- +// +// PROC DrawSlider +// +//--------------------------------------------------------------------------- + +static void DrawSlider(Menu_t *menu, int item, int width, int slot) +{ + int x; + int y; + int x2; + int count; + + x = menu->x+24; + y = menu->y+2+(item*ITEM_HEIGHT); + V_DrawPatch(x-32, y, W_CacheLumpName("M_SLDLT", PU_CACHE)); + for(x2 = x, count = width; count--; x2 += 8) + { + V_DrawPatch(x2, y, W_CacheLumpName(count&1 ? "M_SLDMD1" + : "M_SLDMD2", PU_CACHE)); + } + V_DrawPatch(x2, y, W_CacheLumpName("M_SLDRT", PU_CACHE)); + V_DrawPatch(x+4+slot*8, y+7, W_CacheLumpName("M_SLDKB", PU_CACHE)); +} diff --git a/src/hexen/oldd_net.c b/src/hexen/oldd_net.c new file mode 100644 index 00000000..73bba9c1 --- /dev/null +++ b/src/hexen/oldd_net.c @@ -0,0 +1,790 @@ +// I_pcnet.m + +#include "DoomDef.h" + +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_CHECKSUM 0x0fffffff + +/* +if more space needs to be crunched out of the protocol... + +1 drone +2 player +8 tic +5 numtics + +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 // a retransmit will have 0 tics +#define NCMD_DRONE 0x20000000 +#define NCMD_PLAYER 0x18000000 +#define NCMD_PLAYERSHIFT 27 +#define NCMD_TIC 0x00ff0000 +#define NCMD_TICSHIFT 16 +#define NCMD_NUMTICS 0x0000ff00 +#define NCMD_NUMTICSSHIFT 8 +#define NCMD_CHECKSUM 0x000000ff + +*/ + + + + + +doomcom_t *doomcom; +doomdata_t *netbuffer; // points inside doomcom + + +/* +============================================================================== + + NETWORKING + +gametic is the tic about to (or currently being) run +maketic is the tick that hasn't had control made for it yet +nettics[] has the maketics for all players + +a gametic cannot be run until nettics[] > gametic for all players + +============================================================================== +*/ + +#define RESENDCOUNT 10 +#define PL_DRONE 0x80 // bit flag in doomdata->player + +ticcmd_t localcmds[BACKUPTICS]; + +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +int nettics[MAXNETNODES]; +boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +boolean remoteresend[MAXNETNODES]; // set when local needs tics +int resendto[MAXNETNODES]; // set when remote needs tics +int resendcount[MAXNETNODES]; + +int nodeforplayer[MAXPLAYERS]; + +int gametime; +int maketic; +int lastnettic, skiptics; +int ticdup; + +void D_ProcessEvents (void); +void G_BuildTiccmd (ticcmd_t *cmd); +void D_DoAdvanceDemo (void); + +boolean reboundpacket; +doomdata_t reboundstore; + + +int NetbufferSize (void) +{ + return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]); +} + +unsigned NetbufferChecksum (void) +{ + unsigned c; + int i,l; + + c = 0x1234567; + +#ifdef NeXT + return 0; // byte order problems +#endif + + l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; + for (i=0 ; iretransmitfrom)[i] * (i+1); + + return c & NCMD_CHECKSUM; +} + +int ExpandTics (int low) +{ + int delta; + + delta = low - (maketic&0xff); + + if (delta >= -64 && delta <= 64) + return (maketic&~0xff) + low; + if (delta > 64) + return (maketic&~0xff) - 256 + low; + if (delta < -64) + return (maketic&~0xff) + 256 + low; + + I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic); + return 0; +} + + +//============================================================================ + + +/* +============== += += HSendPacket += +============== +*/ + +void HSendPacket (int node, int flags) +{ + netbuffer->checksum = NetbufferChecksum () | flags; + + if (!node) + { + reboundstore = *netbuffer; + reboundpacket = true; + return; + } + + if (!netgame) + I_Error ("Tried to transmit to another node"); + + doomcom->command = CMD_SEND; + doomcom->remotenode = node; + doomcom->datalength = NetbufferSize (); + +if (debugfile) +{ + int i; + int realretrans; + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics (netbuffer->retransmitfrom); + else + realretrans = -1; + fprintf (debugfile,"send (%i + %i, R %i) [%i] " + ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength); + for (i=0 ; idatalength ; i++) + fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); + fprintf (debugfile,"\n"); +} + + I_NetCmd (); +} + +/* +============== += += HGetPacket += += Returns false if no packet is waiting += +============== +*/ + +boolean HGetPacket (void) +{ + if (reboundpacket) + { + *netbuffer = reboundstore; + doomcom->remotenode = 0; + reboundpacket = false; + return true; + } + + if (!netgame) + return false; + + doomcom->command = CMD_GET; + I_NetCmd (); + if (doomcom->remotenode == -1) + return false; + + if (doomcom->datalength != NetbufferSize ()) + { + if (debugfile) + fprintf (debugfile,"bad packet length %i\n",doomcom->datalength); + return false; + } + + if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) ) + { + if (debugfile) + fprintf (debugfile,"bad packet checksum\n"); + return false; + } + +if (debugfile) +{ + int realretrans; + int i; + + if (netbuffer->checksum & NCMD_SETUP) + fprintf (debugfile,"setup packet\n"); + else + { + if (netbuffer->checksum & NCMD_RETRANSMIT) + realretrans = ExpandTics (netbuffer->retransmitfrom); + else + realretrans = -1; + fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode, + ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength); + for (i=0 ; idatalength ; i++) + fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); + fprintf (debugfile,"\n"); + } +} + return true; +} + + +/* +=================== += += GetPackets += +=================== +*/ + +char exitmsg[80]; + +void GetPackets (void) +{ + int netconsole; + int netnode; + int netdrone; + int j; + ticcmd_t *src, *dest; + int dupedstart, dupedend; + int skiptics; + int realstart; + + while (HGetPacket ()) + { + if (netbuffer->checksum & NCMD_SETUP) + continue; // extra setup packet + + netdrone = netbuffer->player & PL_DRONE; + netconsole = netbuffer->player & ~PL_DRONE; + netnode = doomcom->remotenode; + // + // to save bytes, only the low byte of tic numbers are sent + // Figure out what the rest of the bytes are + // + realstart = ExpandTics (netbuffer->starttic); + dupedstart = realstart*doomcom->ticdup; + dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup; + + // + // check for exiting the game + // + if (netbuffer->checksum & NCMD_EXIT) + { + if (!nodeingame[netnode]) + continue; + nodeingame[netnode] = false; + if (!netdrone) + { + playeringame[netconsole] = false; + strcpy (exitmsg, "Player 1 left the game"); + exitmsg[7] += netconsole; + players[consoleplayer].message = exitmsg; + } + continue; + } + + // + // drone packets are just notifications + // + if (netdrone) + { + nettics[netnode] = dupedend; + continue; + } + + nodeforplayer[netconsole] = netnode; + + // + // check for retransmit request + // + if ( resendcount[netnode] <= 0 + && (netbuffer->checksum & NCMD_RETRANSMIT) ) + { + resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); +if (debugfile) +fprintf (debugfile,"retransmit from %i\n", resendto[netnode]); + resendcount[netnode] = RESENDCOUNT; + } + else + resendcount[netnode]--; + + // + // check for out of order / duplicated packet + // + if (dupedend == nettics[netnode]) + continue; + + if (dupedend < nettics[netnode]) + { +if (debugfile) +fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics); + continue; + } + + // + // check for a missed packet + // + if (dupedstart > nettics[netnode]) + { + // stop processing until the other system resends the missed tics +if (debugfile) +fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]); + remoteresend[netnode] = true; + continue; + } + +// +// update command store from the packet +// + remoteresend[netnode] = false; + + skiptics = nettics[netnode]/doomcom->ticdup - realstart; + src = &netbuffer->cmds[skiptics]; + + while (nettics[netnode] < dupedend) + { + for (j=0 ; jticdup ; j++) + { + dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS]; + nettics[netnode]++; + *dest = *src; + src->chatchar = 0; + if (src->buttons & BT_SPECIAL) + src->buttons = 0; + } + src++; + } + } +} + +/* +============= += += NetUpdate += += Builds ticcmds for console player += sends out a packet +============= +*/ + +void NetUpdate (void) +{ + int nowtime; + int newtics; + int i,j; + int gameticdiv; + int realstart; + + if (singletics) + return; // singletic update is syncronous + +// +// check time +// + nowtime = I_GetTime ()/doomcom->ticdup; + newtics = nowtime - gametime; + gametime = nowtime; + if (newtics <= 0) // nothing new to update + goto listen; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + + netbuffer->player = consoleplayer; + if (doomcom->drone) + netbuffer->player |= PL_DRONE; + +// +// drone packets +// + if (doomcom->drone) + { + I_StartTic (); + D_ProcessEvents (); + goto sendit; + } + +// +// build new ticcmds for console player +// + gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup; + for (i=0 ; i= BACKUPTICS/2 /* /doomcom->ticdup */- 1) + { + newtics = i; + break; // can't hold any more + } +//printf ("mk:%i ",maketic); + G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]); + maketic++; + } + +// +// send the packet to the other nodes +// +sendit: + for (i=0 ; inumnodes ; i++) + if (nodeingame[i]) + { + if (doomcom->drone) + { + netbuffer->starttic = realstart = maketic + BACKUPTICS/2; + netbuffer->numtics = 0; + } + else + { + netbuffer->starttic = realstart = resendto[i]; + netbuffer->numtics = maketic - realstart; + resendto[i] = maketic - doomcom->extratics; + } + + if (netbuffer->numtics > BACKUPTICS) + I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS"); + + for (j=0 ; j< netbuffer->numtics ; j++) + netbuffer->cmds[j] = + localcmds[(realstart+j)%BACKUPTICS]; + + if (remoteresend[i]) + { + netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup; + HSendPacket (i, NCMD_RETRANSMIT); + } + else + { + netbuffer->retransmitfrom = 0; + HSendPacket (i, 0); + } + } + +// +// listen for other packets +// +listen: + + GetPackets (); +} + + +/* +===================== += += CheckAbort += +===================== +*/ + +void CheckAbort (void) +{ + event_t *ev; + + I_WaitVBL(2); + + I_StartTic (); + for ( ; eventtail != eventhead + ; eventtail = (++eventtail)&(MAXEVENTS-1) ) + { + ev = &events[eventtail]; + if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) + I_Error ("Network game synchronization aborted."); + } +} + +/* +===================== += += D_ArbitrateNetStart += +===================== +*/ + +void D_ArbitrateNetStart (void) +{ + int i; + boolean gotinfo[MAXNETNODES]; + + autostart = true; + memset (gotinfo,0,sizeof(gotinfo)); + + if (doomcom->consoleplayer) + { // listen for setup info from key player + printf ("listening for network start info...\n"); + while (1) + { + CheckAbort (); + if (!HGetPacket ()) + continue; + if (netbuffer->checksum & NCMD_SETUP) + { + if (netbuffer->player != VERSION) + I_Error ("Different DOOM versions cannot play a net game!"); + startskill = netbuffer->retransmitfrom & 15; + deathmatch = (netbuffer->retransmitfrom & 0x80) > 0; + nomonsters = (netbuffer->retransmitfrom & 0x40) > 0; + respawnparm = (netbuffer->retransmitfrom & 0x20) > 0; + startmap = netbuffer->starttic & 15; + startepisode = netbuffer->starttic >> 4; + return; + } + } + } + else + { // key player, send the setup info + printf ("sending network start info...\n"); + do + { + CheckAbort (); + for (i=0 ; inumnodes ; i++) + { + netbuffer->retransmitfrom = startskill; + if (deathmatch) + netbuffer->retransmitfrom |= 0x80; + if (nomonsters) + netbuffer->retransmitfrom |= 0x40; + if (respawnparm) + netbuffer->retransmitfrom |= 0x20; + netbuffer->starttic = startepisode * 16 + startmap; + netbuffer->player = VERSION; + netbuffer->numtics = 0; + HSendPacket (i, NCMD_SETUP); + } + + while (HGetPacket ()) + { + gotinfo[netbuffer->player&0x7f] = true; + } + + for (i=1 ; inumnodes ; i++) + if (!gotinfo[i]) + break; + } while (i < doomcom->numnodes); + } +} + +/* +=================== += += D_CheckNetGame += += Works out player numbers among the net participants +=================== +*/ + +extern int viewangleoffset; + +void D_CheckNetGame (void) +{ + int i; + + for (i=0 ; iid != DOOMCOM_ID) + I_Error ("Doomcom buffer invalid!"); + netbuffer = &doomcom->data; + consoleplayer = displayplayer = doomcom->consoleplayer; + if (netgame) + D_ArbitrateNetStart (); +printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); + +// read values out of doomcom + ticdup = doomcom->ticdup; + + for (i=0 ; inumplayers ; i++) + playeringame[i] = true; + for (i=0 ; inumnodes ; i++) + nodeingame[i] = true; + +printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes); + +} + +/* +================== += += D_QuitNetGame += += Called before quitting to leave a net game without hanging the += other players += +================== +*/ + +void D_QuitNetGame (void) +{ + int i, j; + + if (debugfile) + fclose (debugfile); + + if (!netgame || !usergame || consoleplayer == -1) + return; + +// send a bunch of packets for security + netbuffer->player = consoleplayer; + if (doomcom->drone) + netbuffer->player |= PL_DRONE; + netbuffer->numtics = 0; + for (i=0 ; i<4 ; i++) + { + for (j=1 ; jnumnodes ; j++) + if (nodeingame[j]) + HSendPacket (j, NCMD_EXIT); + I_WaitVBL (1); + } +} + + + +/* +=============== += += TryRunTics += +=============== +*/ + +int frametics[4], frameon; +int frameskip[4]; +int oldnettics; +extern boolean advancedemo; + +void TryRunTics (void) +{ + int i; + int lowtic, nextlowest; + int entertic; + int static oldentertics; + int realtics, availabletics; + int counts; + int numplaying; + +// +// get real tics +// + entertic = I_GetTime (); + realtics = entertic - oldentertics; + oldentertics = entertic; + +// +// get available tics +// + NetUpdate (); + + lowtic = nextlowest = MAXINT; + numplaying = 0; + for (i=0 ; inumnodes ; i++) + if (nodeingame[i]) + { + numplaying++; + if (nettics[i] < lowtic) + { + nextlowest = lowtic; + lowtic = nettics[i]; + } + else if (nettics[i] < nextlowest) + nextlowest = nettics[i]; + } + availabletics = lowtic - gametic; + + +// +// decide how many tics to run +// + if (realtics < availabletics-1) + counts = realtics+1; + else if (realtics < availabletics) + counts = realtics; + else + counts = availabletics; + if (counts < 1) + counts = 1; + + frameon++; + +if (debugfile) + fprintf (debugfile,"=======real: %i avail: %i game: %i\n",realtics, availabletics,counts); + +//============================================================================= +// +// ideally nettics[0] should be 1 - 3 tics above lowtic +// if we are consistantly slower, speed up time +// drones should never hold up the other players +// + for (i=0 ; i nettics[nodeforplayer[i]]); + oldnettics = nettics[0]; + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; +// printf ("+"); + } + } +//============================================================================= + +// +// wait for new tics if needed +// + while (lowtic < gametic + counts) + { + + NetUpdate (); + lowtic = MAXINT; + + for (i=0 ; inumnodes ; i++) + if (nodeingame[i] && nettics[i] < lowtic) + lowtic = nettics[i]; + + if (lowtic < gametic) + I_Error ("TryRunTics: lowtic < gametic"); + + // don't stay in here forever -- give the menu a chance to work + if (I_GetTime () - entertic >= 20) + return; + } + + +// +// run the tics +// + while (counts--) + { + G_Ticker (); + NetUpdate (); // check for new console commands + gametic++; + } +} diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c new file mode 100644 index 00000000..30909db8 --- /dev/null +++ b/src/hexen/p_acs.c @@ -0,0 +1,1782 @@ + +//************************************************************************** +//** +//** p_acs.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_acs.c,v $ +//** $Revision: 1.31 $ +//** $Date: 95/10/09 16:25:17 $ +//** $Author: cjr $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" + +// MACROS ------------------------------------------------------------------ + +#define SCRIPT_CONTINUE 0 +#define SCRIPT_STOP 1 +#define SCRIPT_TERMINATE 2 +#define OPEN_SCRIPTS_BASE 1000 +#define PRINT_BUFFER_SIZE 256 +#define GAME_SINGLE_PLAYER 0 +#define GAME_NET_COOPERATIVE 1 +#define GAME_NET_DEATHMATCH 2 +#define TEXTURE_TOP 0 +#define TEXTURE_MIDDLE 1 +#define TEXTURE_BOTTOM 2 +#define S_DROP ACScript->stackPtr-- +#define S_POP ACScript->stack[--ACScript->stackPtr] +#define S_PUSH(x) ACScript->stack[ACScript->stackPtr++] = x + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + int marker; + int infoOffset; + int code; +} acsHeader_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void StartOpenACS(int number, int infoIndex, int *address); +static void ScriptFinished(int number); +static boolean TagBusy(int tag); +static boolean AddToACSStore(int map, int number, byte *args); +static int GetACSIndex(int number); +static void Push(int value); +static int Pop(void); +static int Top(void); +static void Drop(void); + +static int CmdNOP(void); +static int CmdTerminate(void); +static int CmdSuspend(void); +static int CmdPushNumber(void); +static int CmdLSpec1(void); +static int CmdLSpec2(void); +static int CmdLSpec3(void); +static int CmdLSpec4(void); +static int CmdLSpec5(void); +static int CmdLSpec1Direct(void); +static int CmdLSpec2Direct(void); +static int CmdLSpec3Direct(void); +static int CmdLSpec4Direct(void); +static int CmdLSpec5Direct(void); +static int CmdAdd(void); +static int CmdSubtract(void); +static int CmdMultiply(void); +static int CmdDivide(void); +static int CmdModulus(void); +static int CmdEQ(void); +static int CmdNE(void); +static int CmdLT(void); +static int CmdGT(void); +static int CmdLE(void); +static int CmdGE(void); +static int CmdAssignScriptVar(void); +static int CmdAssignMapVar(void); +static int CmdAssignWorldVar(void); +static int CmdPushScriptVar(void); +static int CmdPushMapVar(void); +static int CmdPushWorldVar(void); +static int CmdAddScriptVar(void); +static int CmdAddMapVar(void); +static int CmdAddWorldVar(void); +static int CmdSubScriptVar(void); +static int CmdSubMapVar(void); +static int CmdSubWorldVar(void); +static int CmdMulScriptVar(void); +static int CmdMulMapVar(void); +static int CmdMulWorldVar(void); +static int CmdDivScriptVar(void); +static int CmdDivMapVar(void); +static int CmdDivWorldVar(void); +static int CmdModScriptVar(void); +static int CmdModMapVar(void); +static int CmdModWorldVar(void); +static int CmdIncScriptVar(void); +static int CmdIncMapVar(void); +static int CmdIncWorldVar(void); +static int CmdDecScriptVar(void); +static int CmdDecMapVar(void); +static int CmdDecWorldVar(void); +static int CmdGoto(void); +static int CmdIfGoto(void); +static int CmdDrop(void); +static int CmdDelay(void); +static int CmdDelayDirect(void); +static int CmdRandom(void); +static int CmdRandomDirect(void); +static int CmdThingCount(void); +static int CmdThingCountDirect(void); +static int CmdTagWait(void); +static int CmdTagWaitDirect(void); +static int CmdPolyWait(void); +static int CmdPolyWaitDirect(void); +static int CmdChangeFloor(void); +static int CmdChangeFloorDirect(void); +static int CmdChangeCeiling(void); +static int CmdChangeCeilingDirect(void); +static int CmdRestart(void); +static int CmdAndLogical(void); +static int CmdOrLogical(void); +static int CmdAndBitwise(void); +static int CmdOrBitwise(void); +static int CmdEorBitwise(void); +static int CmdNegateLogical(void); +static int CmdLShift(void); +static int CmdRShift(void); +static int CmdUnaryMinus(void); +static int CmdIfNotGoto(void); +static int CmdLineSide(void); +static int CmdScriptWait(void); +static int CmdScriptWaitDirect(void); +static int CmdClearLineSpecial(void); +static int CmdCaseGoto(void); +static int CmdBeginPrint(void); +static int CmdEndPrint(void); +static int CmdPrintString(void); +static int CmdPrintNumber(void); +static int CmdPrintCharacter(void); +static int CmdPlayerCount(void); +static int CmdGameType(void); +static int CmdGameSkill(void); +static int CmdTimer(void); +static int CmdSectorSound(void); +static int CmdAmbientSound(void); +static int CmdSoundSequence(void); +static int CmdSetLineTexture(void); +static int CmdSetLineBlocking(void); +static int CmdSetLineSpecial(void); +static int CmdThingSound(void); +static int CmdEndPrintBold(void); + +static void ThingCount(int type, int tid); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int ACScriptCount; +byte *ActionCodeBase; +acsInfo_t *ACSInfo; +int MapVars[MAX_ACS_MAP_VARS]; +int WorldVars[MAX_ACS_WORLD_VARS]; +acsstore_t ACSStore[MAX_ACS_STORE+1]; // +1 for termination marker + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static acs_t *ACScript; +static int *PCodePtr; +static byte SpecArgs[8]; +static int ACStringCount; +static char **ACStrings; +static char PrintBuffer[PRINT_BUFFER_SIZE]; +static acs_t *NewScript; + +static int (*PCodeCmds[])(void) = +{ + CmdNOP, + CmdTerminate, + CmdSuspend, + CmdPushNumber, + CmdLSpec1, + CmdLSpec2, + CmdLSpec3, + CmdLSpec4, + CmdLSpec5, + CmdLSpec1Direct, + CmdLSpec2Direct, + CmdLSpec3Direct, + CmdLSpec4Direct, + CmdLSpec5Direct, + CmdAdd, + CmdSubtract, + CmdMultiply, + CmdDivide, + CmdModulus, + CmdEQ, + CmdNE, + CmdLT, + CmdGT, + CmdLE, + CmdGE, + CmdAssignScriptVar, + CmdAssignMapVar, + CmdAssignWorldVar, + CmdPushScriptVar, + CmdPushMapVar, + CmdPushWorldVar, + CmdAddScriptVar, + CmdAddMapVar, + CmdAddWorldVar, + CmdSubScriptVar, + CmdSubMapVar, + CmdSubWorldVar, + CmdMulScriptVar, + CmdMulMapVar, + CmdMulWorldVar, + CmdDivScriptVar, + CmdDivMapVar, + CmdDivWorldVar, + CmdModScriptVar, + CmdModMapVar, + CmdModWorldVar, + CmdIncScriptVar, + CmdIncMapVar, + CmdIncWorldVar, + CmdDecScriptVar, + CmdDecMapVar, + CmdDecWorldVar, + CmdGoto, + CmdIfGoto, + CmdDrop, + CmdDelay, + CmdDelayDirect, + CmdRandom, + CmdRandomDirect, + CmdThingCount, + CmdThingCountDirect, + CmdTagWait, + CmdTagWaitDirect, + CmdPolyWait, + CmdPolyWaitDirect, + CmdChangeFloor, + CmdChangeFloorDirect, + CmdChangeCeiling, + CmdChangeCeilingDirect, + CmdRestart, + CmdAndLogical, + CmdOrLogical, + CmdAndBitwise, + CmdOrBitwise, + CmdEorBitwise, + CmdNegateLogical, + CmdLShift, + CmdRShift, + CmdUnaryMinus, + CmdIfNotGoto, + CmdLineSide, + CmdScriptWait, + CmdScriptWaitDirect, + CmdClearLineSpecial, + CmdCaseGoto, + CmdBeginPrint, + CmdEndPrint, + CmdPrintString, + CmdPrintNumber, + CmdPrintCharacter, + CmdPlayerCount, + CmdGameType, + CmdGameSkill, + CmdTimer, + CmdSectorSound, + CmdAmbientSound, + CmdSoundSequence, + CmdSetLineTexture, + CmdSetLineBlocking, + CmdSetLineSpecial, + CmdThingSound, + CmdEndPrintBold +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_LoadACScripts +// +//========================================================================== + +void P_LoadACScripts(int lump) +{ + int i; + int *buffer; + acsHeader_t *header; + acsInfo_t *info; + + header = W_CacheLumpNum(lump, PU_LEVEL); + ActionCodeBase = (byte *)header; + buffer = (int *)((byte *)header+header->infoOffset); + ACScriptCount = *buffer++; + if(ACScriptCount == 0) + { // Empty behavior lump + return; + } + ACSInfo = Z_Malloc(ACScriptCount*sizeof(acsInfo_t), PU_LEVEL, 0); + memset(ACSInfo, 0, ACScriptCount*sizeof(acsInfo_t)); + for(i = 0, info = ACSInfo; i < ACScriptCount; i++, info++) + { + info->number = *buffer++; + info->address = (int *)((byte *)ActionCodeBase+*buffer++); + info->argCount = *buffer++; + if(info->number >= OPEN_SCRIPTS_BASE) + { // Auto-activate + info->number -= OPEN_SCRIPTS_BASE; + StartOpenACS(info->number, i, info->address); + info->state = ASTE_RUNNING; + } + else + { + info->state = ASTE_INACTIVE; + } + } + ACStringCount = *buffer++; + ACStrings = (char **)buffer; + for(i = 0; i < ACStringCount; i++) + { + ACStrings[i] += (int)ActionCodeBase; + } + memset(MapVars, 0, sizeof(MapVars)); +} + +//========================================================================== +// +// StartOpenACS +// +//========================================================================== + +static void StartOpenACS(int number, int infoIndex, int *address) +{ + acs_t *script; + + script = Z_Malloc(sizeof(acs_t), PU_LEVSPEC, 0); + memset(script, 0, sizeof(acs_t)); + script->number = number; + + // World objects are allotted 1 second for initialization + script->delayCount = 35; + + script->infoIndex = infoIndex; + script->ip = address; + script->thinker.function = T_InterpretACS; + P_AddThinker(&script->thinker); +} + +//========================================================================== +// +// P_CheckACSStore +// +// Scans the ACS store and executes all scripts belonging to the current +// map. +// +//========================================================================== + +void P_CheckACSStore(void) +{ + acsstore_t *store; + + for(store = ACSStore; store->map != 0; store++) + { + if(store->map == gamemap) + { + P_StartACS(store->script, 0, store->args, NULL, NULL, 0); + if(NewScript) + { + NewScript->delayCount = 35; + } + store->map = -1; + } + } +} + +//========================================================================== +// +// P_StartACS +// +//========================================================================== + +static char ErrorMsg[128]; + +boolean P_StartACS(int number, int map, byte *args, mobj_t *activator, + line_t *line, int side) +{ + int i; + acs_t *script; + int infoIndex; + aste_t *statePtr; + + NewScript = NULL; + if(map && map != gamemap) + { // Add to the script store + return AddToACSStore(map, number, args); + } + infoIndex = GetACSIndex(number); + if(infoIndex == -1) + { // Script not found + //I_Error("P_StartACS: Unknown script number %d", number); + sprintf(ErrorMsg, "P_STARTACS ERROR: UNKNOWN SCRIPT %d", number); + P_SetMessage(&players[consoleplayer], ErrorMsg, true); + } + statePtr = &ACSInfo[infoIndex].state; + if(*statePtr == ASTE_SUSPENDED) + { // Resume a suspended script + *statePtr = ASTE_RUNNING; + return true; + } + if(*statePtr != ASTE_INACTIVE) + { // Script is already executing + return false; + } + script = Z_Malloc(sizeof(acs_t), PU_LEVSPEC, 0); + memset(script, 0, sizeof(acs_t)); + script->number = number; + script->infoIndex = infoIndex; + script->activator = activator; + script->line = line; + script->side = side; + script->ip = ACSInfo[infoIndex].address; + script->thinker.function = T_InterpretACS; + for(i = 0; i < ACSInfo[infoIndex].argCount; i++) + { + script->vars[i] = args[i]; + } + *statePtr = ASTE_RUNNING; + P_AddThinker(&script->thinker); + NewScript = script; + return true; +} + +//========================================================================== +// +// AddToACSStore +// +//========================================================================== + +static boolean AddToACSStore(int map, int number, byte *args) +{ + int i; + int index; + + index = -1; + for(i = 0; ACSStore[i].map != 0; i++) + { + if(ACSStore[i].script == number + && ACSStore[i].map == map) + { // Don't allow duplicates + return false; + } + if(index == -1 && ACSStore[i].map == -1) + { // Remember first empty slot + index = i; + } + } + if(index == -1) + { // Append required + if(i == MAX_ACS_STORE) + { + I_Error("AddToACSStore: MAX_ACS_STORE (%d) exceeded.", + MAX_ACS_STORE); + } + index = i; + ACSStore[index+1].map = 0; + } + ACSStore[index].map = map; + ACSStore[index].script = number; + *((int *)ACSStore[index].args) = *((int *)args); + return true; +} + +//========================================================================== +// +// P_StartLockedACS +// +//========================================================================== + + +boolean P_StartLockedACS(line_t *line, byte *args, mobj_t *mo, int side) +{ + int i; + int lock; + byte newArgs[5]; + char LockedBuffer[80]; + + extern char *TextKeyMessages[11]; + + lock = args[4]; + if(!mo->player) + { + return false; + } + if(lock) + { + if(!(mo->player->keys&(1<<(lock-1)))) + { + sprintf(LockedBuffer, "YOU NEED THE %s\n", + TextKeyMessages[lock-1]); + P_SetMessage(mo->player, LockedBuffer, true); + S_StartSound(mo, SFX_DOOR_LOCKED); + return false; + } + } + for(i = 0; i < 4; i++) + { + newArgs[i] = args[i]; + } + newArgs[4] = 0; + return P_StartACS(newArgs[0], newArgs[1], &newArgs[2], mo, + line, side); +} + +//========================================================================== +// +// P_TerminateACS +// +//========================================================================== + +boolean P_TerminateACS(int number, int map) +{ + int infoIndex; + + infoIndex = GetACSIndex(number); + if(infoIndex == -1) + { // Script not found + return false; + } + if(ACSInfo[infoIndex].state == ASTE_INACTIVE + || ACSInfo[infoIndex].state == ASTE_TERMINATING) + { // States that disallow termination + return false; + } + ACSInfo[infoIndex].state = ASTE_TERMINATING; + return true; +} + +//========================================================================== +// +// P_SuspendACS +// +//========================================================================== + +boolean P_SuspendACS(int number, int map) +{ + int infoIndex; + + infoIndex = GetACSIndex(number); + if(infoIndex == -1) + { // Script not found + return false; + } + if(ACSInfo[infoIndex].state == ASTE_INACTIVE + || ACSInfo[infoIndex].state == ASTE_SUSPENDED + || ACSInfo[infoIndex].state == ASTE_TERMINATING) + { // States that disallow suspension + return false; + } + ACSInfo[infoIndex].state = ASTE_SUSPENDED; + return true; +} + +//========================================================================== +// +// P_Init +// +//========================================================================== + +void P_ACSInitNewGame(void) +{ + memset(WorldVars, 0, sizeof(WorldVars)); + memset(ACSStore, 0, sizeof(ACSStore)); +} + +//========================================================================== +// +// T_InterpretACS +// +//========================================================================== + +void T_InterpretACS(acs_t *script) +{ + int cmd; + int action; + + if(ACSInfo[script->infoIndex].state == ASTE_TERMINATING) + { + ACSInfo[script->infoIndex].state = ASTE_INACTIVE; + ScriptFinished(ACScript->number); + P_RemoveThinker(&ACScript->thinker); + return; + } + if(ACSInfo[script->infoIndex].state != ASTE_RUNNING) + { + return; + } + if(script->delayCount) + { + script->delayCount--; + return; + } + ACScript = script; + PCodePtr = ACScript->ip; + do + { + cmd = *PCodePtr++; + action = PCodeCmds[cmd](); + } while(action == SCRIPT_CONTINUE); + ACScript->ip = PCodePtr; + if(action == SCRIPT_TERMINATE) + { + ACSInfo[script->infoIndex].state = ASTE_INACTIVE; + ScriptFinished(ACScript->number); + P_RemoveThinker(&ACScript->thinker); + } +} + +//========================================================================== +// +// P_TagFinished +// +//========================================================================== + +void P_TagFinished(int tag) +{ + int i; + + if(TagBusy(tag) == true) + { + return; + } + for(i = 0; i < ACScriptCount; i++) + { + if(ACSInfo[i].state == ASTE_WAITINGFORTAG + && ACSInfo[i].waitValue == tag) + { + ACSInfo[i].state = ASTE_RUNNING; + } + } +} + +//========================================================================== +// +// P_PolyobjFinished +// +//========================================================================== + +void P_PolyobjFinished(int po) +{ + int i; + + if(PO_Busy(po) == true) + { + return; + } + for(i = 0; i < ACScriptCount; i++) + { + if(ACSInfo[i].state == ASTE_WAITINGFORPOLY + && ACSInfo[i].waitValue == po) + { + ACSInfo[i].state = ASTE_RUNNING; + } + } +} + +//========================================================================== +// +// ScriptFinished +// +//========================================================================== + +static void ScriptFinished(int number) +{ + int i; + + for(i = 0; i < ACScriptCount; i++) + { + if(ACSInfo[i].state == ASTE_WAITINGFORSCRIPT + && ACSInfo[i].waitValue == number) + { + ACSInfo[i].state = ASTE_RUNNING; + } + } +} + +//========================================================================== +// +// TagBusy +// +//========================================================================== + +static boolean TagBusy(int tag) +{ + int sectorIndex; + + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + if(sectors[sectorIndex].specialdata) + { + return true; + } + } + return false; +} + +//========================================================================== +// +// GetACSIndex +// +// Returns the index of a script number. Returns -1 if the script number +// is not found. +// +//========================================================================== + +static int GetACSIndex(int number) +{ + int i; + + for(i = 0; i < ACScriptCount; i++) + { + if(ACSInfo[i].number == number) + { + return i; + } + } + return -1; +} + +//========================================================================== +// +// Push +// +//========================================================================== + +static void Push(int value) +{ + ACScript->stack[ACScript->stackPtr++] = value; +} + +//========================================================================== +// +// Pop +// +//========================================================================== + +static int Pop(void) +{ + return ACScript->stack[--ACScript->stackPtr]; +} + +//========================================================================== +// +// Top +// +//========================================================================== + +static int Top(void) +{ + return ACScript->stack[ACScript->stackPtr-1]; +} + +//========================================================================== +// +// Drop +// +//========================================================================== + +static void Drop(void) +{ + ACScript->stackPtr--; +} + +//========================================================================== +// +// P-Code Commands +// +//========================================================================== + +static int CmdNOP(void) +{ + return SCRIPT_CONTINUE; +} + +static int CmdTerminate(void) +{ + return SCRIPT_TERMINATE; +} + +static int CmdSuspend(void) +{ + ACSInfo[ACScript->infoIndex].state = ASTE_SUSPENDED; + return SCRIPT_STOP; +} + +static int CmdPushNumber(void) +{ + Push(*PCodePtr++); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec1(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = Pop(); + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec2(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[1] = Pop(); + SpecArgs[0] = Pop(); + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec3(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[2] = Pop(); + SpecArgs[1] = Pop(); + SpecArgs[0] = Pop(); + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec4(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[3] = Pop(); + SpecArgs[2] = Pop(); + SpecArgs[1] = Pop(); + SpecArgs[0] = Pop(); + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec5(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[4] = Pop(); + SpecArgs[3] = Pop(); + SpecArgs[2] = Pop(); + SpecArgs[1] = Pop(); + SpecArgs[0] = Pop(); + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec1Direct(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = *PCodePtr++; + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec2Direct(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = *PCodePtr++; + SpecArgs[1] = *PCodePtr++; + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec3Direct(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = *PCodePtr++; + SpecArgs[1] = *PCodePtr++; + SpecArgs[2] = *PCodePtr++; + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec4Direct(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = *PCodePtr++; + SpecArgs[1] = *PCodePtr++; + SpecArgs[2] = *PCodePtr++; + SpecArgs[3] = *PCodePtr++; + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdLSpec5Direct(void) +{ + int special; + + special = *PCodePtr++; + SpecArgs[0] = *PCodePtr++; + SpecArgs[1] = *PCodePtr++; + SpecArgs[2] = *PCodePtr++; + SpecArgs[3] = *PCodePtr++; + SpecArgs[4] = *PCodePtr++; + P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, + ACScript->side, ACScript->activator); + return SCRIPT_CONTINUE; +} + +static int CmdAdd(void) +{ + Push(Pop()+Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdSubtract(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop()-operand2); + return SCRIPT_CONTINUE; +} + +static int CmdMultiply(void) +{ + Push(Pop()*Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdDivide(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop()/operand2); + return SCRIPT_CONTINUE; +} + +static int CmdModulus(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop()%operand2); + return SCRIPT_CONTINUE; +} + +static int CmdEQ(void) +{ + Push(Pop() == Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdNE(void) +{ + Push(Pop() != Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdLT(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop() < operand2); + return SCRIPT_CONTINUE; +} + +static int CmdGT(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop() > operand2); + return SCRIPT_CONTINUE; +} + +static int CmdLE(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop() <= operand2); + return SCRIPT_CONTINUE; +} + +static int CmdGE(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop() >= operand2); + return SCRIPT_CONTINUE; +} + +static int CmdAssignScriptVar(void) +{ + ACScript->vars[*PCodePtr++] = Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdAssignMapVar(void) +{ + MapVars[*PCodePtr++] = Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdAssignWorldVar(void) +{ + WorldVars[*PCodePtr++] = Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdPushScriptVar(void) +{ + Push(ACScript->vars[*PCodePtr++]); + return SCRIPT_CONTINUE; +} + +static int CmdPushMapVar(void) +{ + Push(MapVars[*PCodePtr++]); + return SCRIPT_CONTINUE; +} + +static int CmdPushWorldVar(void) +{ + Push(WorldVars[*PCodePtr++]); + return SCRIPT_CONTINUE; +} + +static int CmdAddScriptVar(void) +{ + ACScript->vars[*PCodePtr++] += Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdAddMapVar(void) +{ + MapVars[*PCodePtr++] += Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdAddWorldVar(void) +{ + WorldVars[*PCodePtr++] += Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdSubScriptVar(void) +{ + ACScript->vars[*PCodePtr++] -= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdSubMapVar(void) +{ + MapVars[*PCodePtr++] -= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdSubWorldVar(void) +{ + WorldVars[*PCodePtr++] -= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdMulScriptVar(void) +{ + ACScript->vars[*PCodePtr++] *= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdMulMapVar(void) +{ + MapVars[*PCodePtr++] *= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdMulWorldVar(void) +{ + WorldVars[*PCodePtr++] *= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdDivScriptVar(void) +{ + ACScript->vars[*PCodePtr++] /= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdDivMapVar(void) +{ + MapVars[*PCodePtr++] /= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdDivWorldVar(void) +{ + WorldVars[*PCodePtr++] /= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdModScriptVar(void) +{ + ACScript->vars[*PCodePtr++] %= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdModMapVar(void) +{ + MapVars[*PCodePtr++] %= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdModWorldVar(void) +{ + WorldVars[*PCodePtr++] %= Pop(); + return SCRIPT_CONTINUE; +} + +static int CmdIncScriptVar(void) +{ + ACScript->vars[*PCodePtr++]++; + return SCRIPT_CONTINUE; +} + +static int CmdIncMapVar(void) +{ + MapVars[*PCodePtr++]++; + return SCRIPT_CONTINUE; +} + +static int CmdIncWorldVar(void) +{ + WorldVars[*PCodePtr++]++; + return SCRIPT_CONTINUE; +} + +static int CmdDecScriptVar(void) +{ + ACScript->vars[*PCodePtr++]--; + return SCRIPT_CONTINUE; +} + +static int CmdDecMapVar(void) +{ + MapVars[*PCodePtr++]--; + return SCRIPT_CONTINUE; +} + +static int CmdDecWorldVar(void) +{ + WorldVars[*PCodePtr++]--; + return SCRIPT_CONTINUE; +} + +static int CmdGoto(void) +{ + PCodePtr = (int *)(ActionCodeBase+*PCodePtr); + return SCRIPT_CONTINUE; +} + +static int CmdIfGoto(void) +{ + if(Pop()) + { + PCodePtr = (int *)(ActionCodeBase+*PCodePtr); + } + else + { + PCodePtr++; + } + return SCRIPT_CONTINUE; +} + +static int CmdDrop(void) +{ + Drop(); + return SCRIPT_CONTINUE; +} + +static int CmdDelay(void) +{ + ACScript->delayCount = Pop(); + return SCRIPT_STOP; +} + +static int CmdDelayDirect(void) +{ + ACScript->delayCount = *PCodePtr++; + return SCRIPT_STOP; +} + +static int CmdRandom(void) +{ + int low; + int high; + + high = Pop(); + low = Pop(); + Push(low+(P_Random()%(high-low+1))); + return SCRIPT_CONTINUE; +} + +static int CmdRandomDirect(void) +{ + int low; + int high; + + low = *PCodePtr++; + high = *PCodePtr++; + Push(low+(P_Random()%(high-low+1))); + return SCRIPT_CONTINUE; +} + +static int CmdThingCount(void) +{ + int tid; + + tid = Pop(); + ThingCount(Pop(), tid); + return SCRIPT_CONTINUE; +} + +static int CmdThingCountDirect(void) +{ + int type; + + type = *PCodePtr++; + ThingCount(type, *PCodePtr++); + return SCRIPT_CONTINUE; +} + +static void ThingCount(int type, int tid) +{ + int count; + int searcher; + mobj_t *mobj; + mobjtype_t moType; + thinker_t *think; + + if(!(type+tid)) + { // Nothing to count + return; + } + moType = TranslateThingType[type]; + count = 0; + searcher = -1; + if(tid) + { // Count TID things + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if(type == 0) + { // Just count TIDs + count++; + } + else if(moType == mobj->type) + { + if(mobj->flags&MF_COUNTKILL && mobj->health <= 0) + { // Don't count dead monsters + continue; + } + count++; + } + } + } + else + { // Count only types + for(think = thinkercap.next; think != &thinkercap; + think = think->next) + { + if(think->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mobj = (mobj_t *)think; + if(mobj->type != moType) + { // Doesn't match + continue; + } + if(mobj->flags&MF_COUNTKILL && mobj->health <= 0) + { // Don't count dead monsters + continue; + } + count++; + } + } + Push(count); +} + +static int CmdTagWait(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = Pop(); + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG; + return SCRIPT_STOP; +} + +static int CmdTagWaitDirect(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = *PCodePtr++; + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG; + return SCRIPT_STOP; +} + +static int CmdPolyWait(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = Pop(); + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY; + return SCRIPT_STOP; +} + +static int CmdPolyWaitDirect(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = *PCodePtr++; + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY; + return SCRIPT_STOP; +} + +static int CmdChangeFloor(void) +{ + int tag; + int flat; + int sectorIndex; + + flat = R_FlatNumForName(ACStrings[Pop()]); + tag = Pop(); + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + sectors[sectorIndex].floorpic = flat; + } + return SCRIPT_CONTINUE; +} + +static int CmdChangeFloorDirect(void) +{ + int tag; + int flat; + int sectorIndex; + + tag = *PCodePtr++; + flat = R_FlatNumForName(ACStrings[*PCodePtr++]); + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + sectors[sectorIndex].floorpic = flat; + } + return SCRIPT_CONTINUE; +} + +static int CmdChangeCeiling(void) +{ + int tag; + int flat; + int sectorIndex; + + flat = R_FlatNumForName(ACStrings[Pop()]); + tag = Pop(); + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + sectors[sectorIndex].ceilingpic = flat; + } + return SCRIPT_CONTINUE; +} + +static int CmdChangeCeilingDirect(void) +{ + int tag; + int flat; + int sectorIndex; + + tag = *PCodePtr++; + flat = R_FlatNumForName(ACStrings[*PCodePtr++]); + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + sectors[sectorIndex].ceilingpic = flat; + } + return SCRIPT_CONTINUE; +} + +static int CmdRestart(void) +{ + PCodePtr = ACSInfo[ACScript->infoIndex].address; + return SCRIPT_CONTINUE; +} + +static int CmdAndLogical(void) +{ + Push(Pop() && Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdOrLogical(void) +{ + Push(Pop() || Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdAndBitwise(void) +{ + Push(Pop()&Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdOrBitwise(void) +{ + Push(Pop()|Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdEorBitwise(void) +{ + Push(Pop()^Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdNegateLogical(void) +{ + Push(!Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdLShift(void) +{ + int operand2; + + operand2 = Pop(); + Push(Pop()<>operand2); + return SCRIPT_CONTINUE; +} + +static int CmdUnaryMinus(void) +{ + Push(-Pop()); + return SCRIPT_CONTINUE; +} + +static int CmdIfNotGoto(void) +{ + if(Pop()) + { + PCodePtr++; + } + else + { + PCodePtr = (int *)(ActionCodeBase+*PCodePtr); + } + return SCRIPT_CONTINUE; +} + +static int CmdLineSide(void) +{ + Push(ACScript->side); + return SCRIPT_CONTINUE; +} + +static int CmdScriptWait(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = Pop(); + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT; + return SCRIPT_STOP; +} + +static int CmdScriptWaitDirect(void) +{ + ACSInfo[ACScript->infoIndex].waitValue = *PCodePtr++; + ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT; + return SCRIPT_STOP; +} + +static int CmdClearLineSpecial(void) +{ + if(ACScript->line) + { + ACScript->line->special = 0; + } + return SCRIPT_CONTINUE; +} + +static int CmdCaseGoto(void) +{ + if(Top() == *PCodePtr++) + { + PCodePtr = (int *)(ActionCodeBase+*PCodePtr); + Drop(); + } + else + { + PCodePtr++; + } + return SCRIPT_CONTINUE; +} + +static int CmdBeginPrint(void) +{ + *PrintBuffer = 0; + return SCRIPT_CONTINUE; +} + +static int CmdEndPrint(void) +{ + player_t *player; + + if(ACScript->activator && ACScript->activator->player) + { + player = ACScript->activator->player; + } + else + { + player = &players[consoleplayer]; + } + P_SetMessage(player, PrintBuffer, true); + return SCRIPT_CONTINUE; +} + +static int CmdEndPrintBold(void) +{ + int i; + + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + P_SetYellowMessage(&players[i], PrintBuffer, true); + } + } + return SCRIPT_CONTINUE; +} + +static int CmdPrintString(void) +{ + strcat(PrintBuffer, ACStrings[Pop()]); + return SCRIPT_CONTINUE; +} + +static int CmdPrintNumber(void) +{ + char tempStr[16]; + + sprintf(tempStr, "%d", Pop()); + strcat(PrintBuffer, tempStr); + return SCRIPT_CONTINUE; +} + +static int CmdPrintCharacter(void) +{ + char *bufferEnd; + + bufferEnd = PrintBuffer+strlen(PrintBuffer); + *bufferEnd++ = Pop(); + *bufferEnd = 0; + return SCRIPT_CONTINUE; +} + +static int CmdPlayerCount(void) +{ + int i; + int count; + + count = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + count += playeringame[i]; + } + Push(count); + return SCRIPT_CONTINUE; +} + +static int CmdGameType(void) +{ + int gametype; + + if(netgame == false) + { + gametype = GAME_SINGLE_PLAYER; + } + else if(deathmatch) + { + gametype = GAME_NET_DEATHMATCH; + } + else + { + gametype = GAME_NET_COOPERATIVE; + } + Push(gametype); + return SCRIPT_CONTINUE; +} + +static int CmdGameSkill(void) +{ + Push(gameskill); + return SCRIPT_CONTINUE; +} + +static int CmdTimer(void) +{ + Push(leveltime); + return SCRIPT_CONTINUE; +} + +static int CmdSectorSound(void) +{ + int volume; + mobj_t *mobj; + + mobj = NULL; + if(ACScript->line) + { + mobj = (mobj_t *)&ACScript->line->frontsector->soundorg; + } + volume = Pop(); + S_StartSoundAtVolume(mobj, S_GetSoundID(ACStrings[Pop()]), volume); + return SCRIPT_CONTINUE; +} + +static int CmdThingSound(void) +{ + int tid; + int sound; + int volume; + mobj_t *mobj; + int searcher; + + volume = Pop(); + sound = S_GetSoundID(ACStrings[Pop()]); + tid = Pop(); + searcher = -1; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + S_StartSoundAtVolume(mobj, sound, volume); + } + return SCRIPT_CONTINUE; +} + +static int CmdAmbientSound(void) +{ + int volume; + + volume = Pop(); + S_StartSoundAtVolume(NULL, S_GetSoundID(ACStrings[Pop()]), volume); + return SCRIPT_CONTINUE; +} + +static int CmdSoundSequence(void) +{ + mobj_t *mobj; + + mobj = NULL; + if(ACScript->line) + { + mobj = (mobj_t *)&ACScript->line->frontsector->soundorg; + } + SN_StartSequenceName(mobj, ACStrings[Pop()]); + return SCRIPT_CONTINUE; +} + +static int CmdSetLineTexture(void) +{ + line_t *line; + int lineTag; + int side; + int position; + int texture; + int searcher; + + texture = R_TextureNumForName(ACStrings[Pop()]); + position = Pop(); + side = Pop(); + lineTag = Pop(); + searcher = -1; + while((line = P_FindLine(lineTag, &searcher)) != NULL) + { + if(position == TEXTURE_MIDDLE) + { + sides[line->sidenum[side]].midtexture = texture; + } + else if(position == TEXTURE_BOTTOM) + { + sides[line->sidenum[side]].bottomtexture = texture; + } + else + { // TEXTURE_TOP + sides[line->sidenum[side]].toptexture = texture; + } + } + return SCRIPT_CONTINUE; +} + +static int CmdSetLineBlocking(void) +{ + line_t *line; + int lineTag; + boolean blocking; + int searcher; + + blocking = Pop() ? ML_BLOCKING : 0; + lineTag = Pop(); + searcher = -1; + while((line = P_FindLine(lineTag, &searcher)) != NULL) + { + line->flags = (line->flags&~ML_BLOCKING)|blocking; + } + return SCRIPT_CONTINUE; +} + +static int CmdSetLineSpecial(void) +{ + line_t *line; + int lineTag; + int special, arg1, arg2, arg3, arg4, arg5; + int searcher; + + arg5 = Pop(); + arg4 = Pop(); + arg3 = Pop(); + arg2 = Pop(); + arg1 = Pop(); + special = Pop(); + lineTag = Pop(); + searcher = -1; + while((line = P_FindLine(lineTag, &searcher)) != NULL) + { + line->special = special; + line->arg1 = arg1; + line->arg2 = arg2; + line->arg3 = arg3; + line->arg4 = arg4; + line->arg5 = arg5; + } + return SCRIPT_CONTINUE; +} diff --git a/src/hexen/p_anim.c b/src/hexen/p_anim.c new file mode 100644 index 00000000..f0605b84 --- /dev/null +++ b/src/hexen/p_anim.c @@ -0,0 +1,473 @@ + +//************************************************************************** +//** +//** p_anim.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_anim.c,v $ +//** $Revision: 1.9 $ +//** $Date: 95/07/17 18:50:37 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" + +// MACROS ------------------------------------------------------------------ + +#define ANIM_SCRIPT_NAME "ANIMDEFS" +#define MAX_ANIM_DEFS 20 +#define MAX_FRAME_DEFS 96 +#define ANIM_FLAT 0 +#define ANIM_TEXTURE 1 +#define SCI_FLAT "flat" +#define SCI_TEXTURE "texture" +#define SCI_PIC "pic" +#define SCI_TICS "tics" +#define SCI_RAND "rand" + +#define LIGHTNING_SPECIAL 198 +#define LIGHTNING_SPECIAL2 199 +#define SKYCHANGE_SPECIAL 200 + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + int index; + int tics; +} frameDef_t; + +typedef struct +{ + int type; + int index; + int tics; + int currentFrameDef; + int startFrameDef; + int endFrameDef; +} animDef_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void P_LightningFlash(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern fixed_t Sky1ColumnOffset; +extern fixed_t Sky2ColumnOffset; +extern int Sky1Texture; +extern boolean DoubleSky; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +fixed_t Sky1ScrollDelta; +fixed_t Sky2ScrollDelta; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static animDef_t AnimDefs[MAX_ANIM_DEFS]; +static frameDef_t FrameDefs[MAX_FRAME_DEFS]; +static int AnimDefCount; +static boolean LevelHasLightning; +static int NextLightningFlash; +static int LightningFlash; +static int *LightningLightLevels; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_AnimateSurfaces +// +//========================================================================== + +void P_AnimateSurfaces(void) +{ + int i; + animDef_t *ad; + line_t *line; + + // Animate flats and textures + for(i = 0; i < AnimDefCount; i++) + { + ad = &AnimDefs[i]; + ad->tics--; + if(ad->tics == 0) + { + if(ad->currentFrameDef == ad->endFrameDef) + { + ad->currentFrameDef = ad->startFrameDef; + } + else + { + ad->currentFrameDef++; + } + ad->tics = FrameDefs[ad->currentFrameDef].tics; + if(ad->tics > 255) + { // Random tics + ad->tics = (ad->tics>>16) + +P_Random()%((ad->tics&0xff00)>>8); + } + if(ad->type == ANIM_FLAT) + { + flattranslation[ad->index] = + FrameDefs[ad->currentFrameDef].index; + } + else + { // Texture + texturetranslation[ad->index] = + FrameDefs[ad->currentFrameDef].index; + } + } + } + + // Update scrolling textures + for(i = 0; i < numlinespecials; i++) + { + line = linespeciallist[i]; + switch(line->special) + { + case 100: // Scroll_Texture_Left + sides[line->sidenum[0]].textureoffset += line->arg1<<10; + break; + case 101: // Scroll_Texture_Right + sides[line->sidenum[0]].textureoffset -= line->arg1<<10; + break; + case 102: // Scroll_Texture_Up + sides[line->sidenum[0]].rowoffset += line->arg1<<10; + break; + case 103: // Scroll_Texture_Down + sides[line->sidenum[0]].rowoffset -= line->arg1<<10; + break; + } + } + + // Update sky column offsets + Sky1ColumnOffset += Sky1ScrollDelta; + Sky2ColumnOffset += Sky2ScrollDelta; + + if(LevelHasLightning) + { + if(!NextLightningFlash || LightningFlash) + { + P_LightningFlash(); + } + else + { + NextLightningFlash--; + } + } +} + +//========================================================================== +// +// P_LightningFlash +// +//========================================================================== + +static void P_LightningFlash(void) +{ + int i; + sector_t *tempSec; + int *tempLight; + boolean foundSec; + int flashLight; + + if(LightningFlash) + { + LightningFlash--; + if(LightningFlash) + { + tempLight = LightningLightLevels; + tempSec = sectors; + for(i = 0; i < numsectors; i++, tempSec++) + { + if(tempSec->ceilingpic == skyflatnum + || tempSec->special == LIGHTNING_SPECIAL + || tempSec->special == LIGHTNING_SPECIAL2) + { + if(*tempLight < tempSec->lightlevel-4) + { + tempSec->lightlevel -= 4; + } + tempLight++; + } + } + } + else + { // remove the alternate lightning flash special + tempLight = LightningLightLevels; + tempSec = sectors; + for(i = 0; i < numsectors; i++, tempSec++) + { + if(tempSec->ceilingpic == skyflatnum + || tempSec->special == LIGHTNING_SPECIAL + || tempSec->special == LIGHTNING_SPECIAL2) + { + tempSec->lightlevel = *tempLight; + tempLight++; + } + } + Sky1Texture = P_GetMapSky1Texture(gamemap); + } + return; + } + LightningFlash = (P_Random()&7)+8; + flashLight = 200+(P_Random()&31); + tempSec = sectors; + tempLight = LightningLightLevels; + foundSec = false; + for(i = 0; i < numsectors; i++, tempSec++) + { + if(tempSec->ceilingpic == skyflatnum + || tempSec->special == LIGHTNING_SPECIAL + || tempSec->special == LIGHTNING_SPECIAL2) + { + *tempLight = tempSec->lightlevel; + if(tempSec->special == LIGHTNING_SPECIAL) + { + tempSec->lightlevel += 64; + if(tempSec->lightlevel > flashLight) + { + tempSec->lightlevel = flashLight; + } + } + else if(tempSec->special == LIGHTNING_SPECIAL2) + { + tempSec->lightlevel += 32; + if(tempSec->lightlevel > flashLight) + { + tempSec->lightlevel = flashLight; + } + } + else + { + tempSec->lightlevel = flashLight; + } + if(tempSec->lightlevel < *tempLight) + { + tempSec->lightlevel = *tempLight; + } + tempLight++; + foundSec = true; + } + } + if(foundSec) + { + Sky1Texture = P_GetMapSky2Texture(gamemap); // set alternate sky + S_StartSound(NULL, SFX_THUNDER_CRASH); + } + // Calculate the next lighting flash + if(!NextLightningFlash) + { + if(P_Random() < 50) + { // Immediate Quick flash + NextLightningFlash = (P_Random()&15)+16; + } + else + { + if(P_Random() < 128 && !(leveltime&32)) + { + NextLightningFlash = ((P_Random()&7)+2)*35; + } + else + { + NextLightningFlash = ((P_Random()&15)+5)*35; + } + } + } +} + +//========================================================================== +// +// P_ForceLightning +// +//========================================================================== + +void P_ForceLightning(void) +{ + NextLightningFlash = 0; +} + +//========================================================================== +// +// P_InitLightning +// +//========================================================================== + +void P_InitLightning(void) +{ + int i; + int secCount; + + if(!P_GetMapLightning(gamemap)) + { + LevelHasLightning = false; + LightningFlash = 0; + return; + } + LightningFlash = 0; + secCount = 0; + for(i = 0; i < numsectors; i++) + { + if(sectors[i].ceilingpic == skyflatnum + || sectors[i].special == LIGHTNING_SPECIAL + || sectors[i].special == LIGHTNING_SPECIAL2) + { + secCount++; + } + } + if(secCount) + { + LevelHasLightning = true; + } + else + { + LevelHasLightning = false; + return; + } + LightningLightLevels = (int *)Z_Malloc(secCount*sizeof(int), PU_LEVEL, + NULL); + NextLightningFlash = ((P_Random()&15)+5)*35; // don't flash at level start +} + +//========================================================================== +// +// P_InitFTAnims +// +// Initialize flat and texture animation lists. +// +//========================================================================== + +void P_InitFTAnims(void) +{ + int base; + int mod; + int fd; + animDef_t *ad; + boolean ignore; + boolean done; + + fd = 0; + ad = AnimDefs; + AnimDefCount = 0; + SC_Open(ANIM_SCRIPT_NAME); + while(SC_GetString()) + { + if(AnimDefCount == MAX_ANIM_DEFS) + { + I_Error("P_InitFTAnims: too many AnimDefs."); + } + if(SC_Compare(SCI_FLAT)) + { + ad->type = ANIM_FLAT; + } + else if(SC_Compare(SCI_TEXTURE)) + { + ad->type = ANIM_TEXTURE; + } + else + { + SC_ScriptError(NULL); + } + SC_MustGetString(); // Name + ignore = false; + if(ad->type == ANIM_FLAT) + { + if(W_CheckNumForName(sc_String) == -1) + { + ignore = true; + } + else + { + ad->index = R_FlatNumForName(sc_String); + } + } + else + { // Texture + if(R_CheckTextureNumForName(sc_String) == -1) + { + ignore = true; + } + else + { + ad->index = R_TextureNumForName(sc_String); + } + } + ad->startFrameDef = fd; + done = false; + while(done == false) + { + if(SC_GetString()) + { + if(SC_Compare(SCI_PIC)) + { + if(fd == MAX_FRAME_DEFS) + { + I_Error("P_InitFTAnims: too many FrameDefs."); + } + SC_MustGetNumber(); + if(ignore == false) + { + FrameDefs[fd].index = ad->index+sc_Number-1; + } + SC_MustGetString(); + if(SC_Compare(SCI_TICS)) + { + SC_MustGetNumber(); + if(ignore == false) + { + FrameDefs[fd].tics = sc_Number; + fd++; + } + } + else if(SC_Compare(SCI_RAND)) + { + SC_MustGetNumber(); + base = sc_Number; + SC_MustGetNumber(); + if(ignore == false) + { + mod = sc_Number-base+1; + FrameDefs[fd].tics = (base<<16)+(mod<<8); + fd++; + } + } + else + { + SC_ScriptError(NULL); + } + } + else + { + SC_UnGet(); + done = true; + } + } + else + { + done = true; + } + } + if((ignore == false) && (fd-ad->startFrameDef < 2)) + { + I_Error("P_InitFTAnims: AnimDef has framecount < 2."); + } + if(ignore == false) + { + ad->endFrameDef = fd-1; + ad->currentFrameDef = ad->endFrameDef; + ad->tics = 1; // Force 1st game tic to animate + AnimDefCount++; + ad++; + } + } + SC_Close(); +} diff --git a/src/hexen/p_ceilng.c b/src/hexen/p_ceilng.c new file mode 100644 index 00000000..fdf09ef0 --- /dev/null +++ b/src/hexen/p_ceilng.c @@ -0,0 +1,294 @@ + +//************************************************************************** +//** +//** p_ceilng.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_ceilng.c,v $ +//** $Revision: 1.17 $ +//** $Date: 95/09/11 22:06:25 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +//================================================================== +//================================================================== +// +// CEILINGS +// +//================================================================== +//================================================================== + +ceiling_t *activeceilings[MAXCEILINGS]; + +//================================================================== +// +// T_MoveCeiling +// +//================================================================== +void T_MoveCeiling (ceiling_t *ceiling) +{ + result_e res; + + switch(ceiling->direction) + { +// case 0: // IN STASIS +// break; + case 1: // UP + res = T_MovePlane(ceiling->sector,ceiling->speed, + ceiling->topheight, false, 1, ceiling->direction); + if (res == RES_PASTDEST) + { + SN_StopSequence((mobj_t *)&ceiling->sector->soundorg); + switch(ceiling->type) + { + case CLEV_CRUSHANDRAISE: + ceiling->direction = -1; + ceiling->speed = ceiling->speed*2; + break; + default: + P_RemoveActiveCeiling(ceiling); + break; + } + } + break; + case -1: // DOWN + res = T_MovePlane(ceiling->sector,ceiling->speed, + ceiling->bottomheight, ceiling->crush, 1, ceiling->direction); + if(res == RES_PASTDEST) + { + SN_StopSequence((mobj_t *)&ceiling->sector->soundorg); + switch(ceiling->type) + { + case CLEV_CRUSHANDRAISE: + case CLEV_CRUSHRAISEANDSTAY: + ceiling->direction = 1; + ceiling->speed = ceiling->speed/2; + break; + default: + P_RemoveActiveCeiling(ceiling); + break; + } + } + else if(res == RES_CRUSHED) + { + switch(ceiling->type) + { + case CLEV_CRUSHANDRAISE: + case CLEV_LOWERANDCRUSH: + case CLEV_CRUSHRAISEANDSTAY: + //ceiling->speed = ceiling->speed/4; + break; + default: + break; + } + } + break; + } +} + +//================================================================== +// +// EV_DoCeiling +// Move a ceiling up/down and all around! +// +//================================================================== +int EV_DoCeiling (line_t *line, byte *arg, ceiling_e type) +{ + int secnum,rtn; + sector_t *sec; + ceiling_t *ceiling; + + secnum = -1; + rtn = 0; + +/* Old Ceiling stasis code + // + // Reactivate in-stasis ceilings...for certain types. + // + switch(type) + { + case CLEV_CRUSHANDRAISE: + P_ActivateInStasisCeiling(line); + default: + break; + } +*/ + while ((secnum = P_FindSectorFromTag(arg[0], secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + // + // new door thinker + // + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + P_AddThinker (&ceiling->thinker); + sec->specialdata = ceiling; + ceiling->thinker.function = T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = 0; + ceiling->speed = arg[1]*(FRACUNIT/8); + switch(type) + { + case CLEV_CRUSHRAISEANDSTAY: + ceiling->crush = arg[2]; // arg[2] = crushing value + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + ceiling->direction = -1; + break; + case CLEV_CRUSHANDRAISE: + ceiling->topheight = sec->ceilingheight; + case CLEV_LOWERANDCRUSH: + ceiling->crush = arg[2]; // arg[2] = crushing value + case CLEV_LOWERTOFLOOR: + ceiling->bottomheight = sec->floorheight; + if(type != CLEV_LOWERTOFLOOR) + { + ceiling->bottomheight += 8*FRACUNIT; + } + ceiling->direction = -1; + break; + case CLEV_RAISETOHIGHEST: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + break; + case CLEV_LOWERBYVALUE: + ceiling->bottomheight = sec->ceilingheight-arg[2]*FRACUNIT; + ceiling->direction = -1; + break; + case CLEV_RAISEBYVALUE: + ceiling->topheight = sec->ceilingheight+arg[2]*FRACUNIT; + ceiling->direction = 1; + break; + case CLEV_MOVETOVALUETIMES8: + { + int destHeight = arg[2]*FRACUNIT*8; + + if(arg[3]) + { + destHeight = -destHeight; + } + if(sec->ceilingheight <= destHeight) + { + ceiling->direction = 1; + ceiling->topheight = destHeight; + if(sec->ceilingheight == destHeight) + { + rtn = 0; + } + } + else if(sec->ceilingheight > destHeight) + { + ceiling->direction = -1; + ceiling->bottomheight = destHeight; + } + break; + } + default: + rtn = 0; + break; + } + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + if(rtn) + { + SN_StartSequence((mobj_t *)&ceiling->sector->soundorg, + SEQ_PLATFORM+ceiling->sector->seqType); + } + } + return rtn; +} + +//================================================================== +// +// Add an active ceiling +// +//================================================================== +void P_AddActiveCeiling(ceiling_t *c) +{ + int i; + for (i = 0; i < MAXCEILINGS;i++) + if (activeceilings[i] == NULL) + { + activeceilings[i] = c; + return; + } +} + +//================================================================== +// +// Remove a ceiling's thinker +// +//================================================================== +void P_RemoveActiveCeiling(ceiling_t *c) +{ + int i; + + for (i = 0;i < MAXCEILINGS;i++) + if (activeceilings[i] == c) + { + activeceilings[i]->sector->specialdata = NULL; + P_RemoveThinker (&activeceilings[i]->thinker); + P_TagFinished(activeceilings[i]->sector->tag); + activeceilings[i] = NULL; + break; + } +} + +#if 0 +//================================================================== +// +// Restart a ceiling that's in-stasis +// +//================================================================== +void P_ActivateInStasisCeiling(line_t *line) +{ + int i; + + for (i = 0;i < MAXCEILINGS;i++) + if (activeceilings[i] && (activeceilings[i]->tag == line->arg1) && + (activeceilings[i]->direction == 0)) + { + activeceilings[i]->direction = activeceilings[i]->olddirection; + activeceilings[i]->thinker.function = T_MoveCeiling; + SN_StartSequence((mobj_t *)&activeceilings[i]->sector->soundorg, + SEQ_PLATFORM+activeceilings[i]->sector->seqType); + } +} +#endif + +//================================================================== +// +// EV_CeilingCrushStop +// Stop a ceiling from crushing! +// +//================================================================== + +int EV_CeilingCrushStop(line_t *line, byte *args) +{ + int i; + int rtn; + + rtn = 0; + for (i = 0;i < MAXCEILINGS;i++) + { + if(activeceilings[i] && activeceilings[i]->tag == args[0]) + { + rtn = 1; + SN_StopSequence((mobj_t*)&activeceilings[i]->sector->soundorg); + activeceilings[i]->sector->specialdata = NULL; + P_RemoveThinker (&activeceilings[i]->thinker); + P_TagFinished(activeceilings[i]->sector->tag); + activeceilings[i] = NULL; + break; + } + } + return rtn; +} diff --git a/src/hexen/p_doors.c b/src/hexen/p_doors.c new file mode 100644 index 00000000..7bdf03fc --- /dev/null +++ b/src/hexen/p_doors.c @@ -0,0 +1,314 @@ + +//************************************************************************** +//** +//** p_doors.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_doors.c,v $ +//** $Revision: 1.13 $ +//** $Date: 96/01/06 03:23:47 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +//================================================================== +//================================================================== +// +// VERTICAL DOORS +// +//================================================================== +//================================================================== + +//================================================================== +// +// T_VerticalDoor +// +//================================================================== +void T_VerticalDoor(vldoor_t *door) +{ + result_e res; + + switch(door->direction) + { + case 0: // WAITING + if(!--door->topcountdown) + switch(door->type) + { + case DREV_NORMAL: + door->direction = -1; // time to go back down + SN_StartSequence((mobj_t *)&door->sector->soundorg, + SEQ_DOOR_STONE+door->sector->seqType); + break; + case DREV_CLOSE30THENOPEN: + door->direction = 1; + break; + default: + break; + } + break; + case 2: // INITIAL WAIT + if(!--door->topcountdown) + { + switch(door->type) + { + case DREV_RAISEIN5MINS: + door->direction = 1; + door->type = DREV_NORMAL; + break; + default: + break; + } + } + break; + case -1: // DOWN + res = T_MovePlane(door->sector, door->speed, + door->sector->floorheight, false, 1, door->direction); + if(res == RES_PASTDEST) + { + SN_StopSequence((mobj_t *)&door->sector->soundorg); + switch(door->type) + { + case DREV_NORMAL: + case DREV_CLOSE: + door->sector->specialdata = NULL; + P_TagFinished(door->sector->tag); + P_RemoveThinker(&door->thinker); // unlink and free + break; + case DREV_CLOSE30THENOPEN: + door->direction = 0; + door->topcountdown = 35*30; + break; + default: + break; + } + } + else if(res == RES_CRUSHED) + { + switch(door->type) + { + case DREV_CLOSE: // DON'T GO BACK UP! + break; + default: + door->direction = 1; + break; + } + } + break; + case 1: // UP + res = T_MovePlane(door->sector, door->speed, + door->topheight, false, 1, door->direction); + if(res == RES_PASTDEST) + { + SN_StopSequence((mobj_t *)&door->sector->soundorg); + switch(door->type) + { + case DREV_NORMAL: + door->direction = 0; // wait at top + door->topcountdown = door->topwait; + break; + case DREV_CLOSE30THENOPEN: + case DREV_OPEN: + door->sector->specialdata = NULL; + P_TagFinished(door->sector->tag); + P_RemoveThinker (&door->thinker); // unlink and free + break; + default: + break; + } + } + break; + } +} + +//---------------------------------------------------------------------------- +// +// EV_DoDoor +// +// Move a door up/down +// +//---------------------------------------------------------------------------- + +int EV_DoDoor(line_t *line, byte *args, vldoor_e type) +{ + int secnum; + int retcode; + sector_t *sec; + vldoor_t *door; + fixed_t speed; + + speed = args[1]*FRACUNIT/8; + secnum = -1; + retcode = 0; + while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + if(sec->specialdata) + { + continue; + } + // Add new door thinker + retcode = 1; + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + door->thinker.function = T_VerticalDoor; + door->sector = sec; + switch(type) + { + case DREV_CLOSE: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + break; + case DREV_CLOSE30THENOPEN: + door->topheight = sec->ceilingheight; + door->direction = -1; + break; + case DREV_NORMAL: + case DREV_OPEN: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + break; + default: + break; + } + door->type = type; + door->speed = speed; + door->topwait = args[2]; // line->arg3 + SN_StartSequence((mobj_t *)&door->sector->soundorg, + SEQ_DOOR_STONE+door->sector->seqType); + } + return(retcode); +} + +//================================================================== +// +// EV_VerticalDoor : open a door manually, no tag value +// +//================================================================== +boolean EV_VerticalDoor(line_t *line, mobj_t *thing) +{ + int secnum; + sector_t *sec; + vldoor_t *door; + int side; + + side = 0; // only front sides can be used + + // if the sector has an active thinker, use it + sec = sides[line->sidenum[side^1]].sector; + secnum = sec-sectors; + if(sec->specialdata) + { + return false; +/* + door = sec->specialdata; + switch(line->special) + { // only for raise doors + case 12: + if(door->direction == -1) + { + door->direction = 1; // go back up + } + else + { + if(!thing->player) + { // Monsters don't close doors + return; + } + door->direction = -1; // start going down immediately + } + return; + } +*/ + } + // + // new door thinker + // + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 1; + switch(line->special) + { + case 11: + door->type = DREV_OPEN; + line->special = 0; + break; + case 12: + case 13: + door->type = DREV_NORMAL; + break; + default: + door->type = DREV_NORMAL; + break; + } + door->speed = line->arg2*(FRACUNIT/8); + door->topwait = line->arg3; + + // + // find the top and bottom of the movement range + // + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + SN_StartSequence((mobj_t *)&door->sector->soundorg, + SEQ_DOOR_STONE+door->sector->seqType); + return true; +} + +//================================================================== +// +// Spawn a door that closes after 30 seconds +// +//================================================================== + +/* +void P_SpawnDoorCloseIn30(sector_t *sec) +{ + vldoor_t *door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + sec->special = 0; + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = DREV_NORMAL; + door->speed = VDOORSPEED; + door->topcountdown = 30*35; +} +*/ + +//================================================================== +// +// Spawn a door that opens after 5 minutes +// +//================================================================== + +/* +void P_SpawnDoorRaiseIn5Mins(sector_t *sec, int secnum) +{ + vldoor_t *door; + + door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); + P_AddThinker(&door->thinker); + sec->specialdata = door; + sec->special = 0; + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = DREV_RAISEIN5MINS; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5*60*35; +} +*/ diff --git a/src/hexen/p_enemy.c b/src/hexen/p_enemy.c new file mode 100644 index 00000000..f9ad16eb --- /dev/null +++ b/src/hexen/p_enemy.c @@ -0,0 +1,5308 @@ + +//************************************************************************** +//** +//** p_enemy.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_enemy.c,v $ +//** $Revision: 1.170 $ +//** $Date: 96/01/06 03:23:28 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// Macros +// Types +// Private Data +// External Data +extern fixed_t FloatBobOffsets[64]; + + +//---------------------------------------------------------------------------- +// +// PROC P_RecursiveSound +// +//---------------------------------------------------------------------------- + +mobj_t *soundtarget; + +void P_RecursiveSound(sector_t *sec, int soundblocks) +{ + int i; + line_t *check; + sector_t *other; + + // Wake up all monsters in this sector + if(sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) + { // Already flooded + return; + } + sec->validcount = validcount; + sec->soundtraversed = soundblocks+1; + sec->soundtarget = soundtarget; + for(i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + if(!(check->flags&ML_TWOSIDED)) + { + continue; + } + P_LineOpening(check); + if(openrange <= 0) + { // Closed door + continue; + } + if(sides[check->sidenum[0]].sector == sec) + { + other = sides[check->sidenum[1]].sector; + } + else + { + other = sides[check->sidenum[0]].sector; + } + if(check->flags&ML_SOUNDBLOCK) + { + if(!soundblocks) + { + P_RecursiveSound(other, 1); + } + } + else + { + P_RecursiveSound(other, soundblocks); + } + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_NoiseAlert +// +// If a monster yells at a player, it will alert other monsters to the +// player. +// +//---------------------------------------------------------------------------- + +void P_NoiseAlert(mobj_t *target, mobj_t *emmiter) +{ + soundtarget = target; + validcount++; + P_RecursiveSound(emmiter->subsector->sector, 0); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_CheckMeleeRange +// +//---------------------------------------------------------------------------- + +boolean P_CheckMeleeRange(mobj_t *actor) +{ + mobj_t *mo; + fixed_t dist; + + if(!actor->target) + { + return(false); + } + mo = actor->target; + dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y); + if(dist >= MELEERANGE) + { + return(false); + } + if(!P_CheckSight(actor, mo)) + { + return(false); + } + if(mo->z > actor->z+actor->height) + { // Target is higher than the attacker + return(false); + } + else if(actor->z > mo->z+mo->height) + { // Attacker is higher + return(false); + } + return(true); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_CheckMeleeRange2 +// +//---------------------------------------------------------------------------- + +boolean P_CheckMeleeRange2(mobj_t *actor) +{ + mobj_t *mo; + fixed_t dist; + + if(!actor->target) + { + return(false); + } + mo = actor->target; + dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y); + if(dist >= MELEERANGE*2 || dist < MELEERANGE) + { + return(false); + } + if(!P_CheckSight(actor, mo)) + { + return(false); + } + if(mo->z > actor->z+actor->height) + { // Target is higher than the attacker + return(false); + } + else if(actor->z > mo->z+mo->height) + { // Attacker is higher + return(false); + } + return(true); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_CheckMissileRange +// +//---------------------------------------------------------------------------- + +boolean P_CheckMissileRange(mobj_t *actor) +{ + fixed_t dist; + + if(!P_CheckSight(actor, actor->target)) + { + return(false); + } + if(actor->flags&MF_JUSTHIT) + { // The target just hit the enemy, so fight back! + actor->flags &= ~MF_JUSTHIT; + return(true); + } + if(actor->reactiontime) + { // Don't attack yet + return(false); + } + dist = (P_AproxDistance(actor->x-actor->target->x, + actor->y-actor->target->y)>>FRACBITS)-64; + if(!actor->info->meleestate) + { // No melee attack, so fire more frequently + dist -= 128; + } + if(dist > 200) + { + dist = 200; + } + if(P_Random() < dist) + { + return(false); + } + return(true); +} + +/* +================ += += P_Move += += Move in the current direction += returns false if the move is blocked +================ +*/ + +fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; +fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; + +#define MAXSPECIALCROSS 8 +extern line_t *spechit[MAXSPECIALCROSS]; +extern int numspechit; + +boolean P_Move(mobj_t *actor) +{ + fixed_t tryx, tryy; + line_t *ld; + boolean good; + + if(actor->flags2&MF2_BLASTED) return(true); + if(actor->movedir == DI_NODIR) + { + return(false); + } + tryx = actor->x+actor->info->speed*xspeed[actor->movedir]; + tryy = actor->y+actor->info->speed*yspeed[actor->movedir]; + if(!P_TryMove(actor, tryx, tryy)) + { // open any specials + if(actor->flags&MF_FLOAT && floatok) + { // must adjust height + if(actor->z < tmfloorz) + { + actor->z += FLOATSPEED; + } + else + { + actor->z -= FLOATSPEED; + } + actor->flags |= MF_INFLOAT; + return(true); + } + if(!numspechit) + { + return false; + } + actor->movedir = DI_NODIR; + good = false; + while(numspechit--) + { + ld = spechit[numspechit]; + // if the special isn't a door that can be opened, return false + if(P_ActivateLine(ld, actor, 0, SPAC_USE)) + { + good = true; + } +/* Old version before use/cross/impact specials were combined + if(P_UseSpecialLine(actor, ld)) + { + good = true; + } +*/ + } + return(good); + } + else + { + actor->flags &= ~MF_INFLOAT; + } + if(!(actor->flags&MF_FLOAT)) + { + if(actor->z > actor->floorz) + { + P_HitFloor(actor); + } + actor->z = actor->floorz; + } + return(true); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_TryWalk +// +// Attempts to move actor in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor returns FALSE. +// If move is either clear of block only by a door, returns TRUE and sets. +// If a door is in the way, an OpenDoor call is made to start it opening. +// +//---------------------------------------------------------------------------- + +boolean P_TryWalk(mobj_t *actor) +{ + if(!P_Move(actor)) + { + return(false); + } + actor->movecount = P_Random()&15; + return(true); +} + +/* +================ += += P_NewChaseDir += +================ +*/ + +dirtype_t opposite[] = +{DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, +DI_NORTH, DI_NORTHWEST, DI_NODIR}; + +dirtype_t diags[] = {DI_NORTHWEST,DI_NORTHEAST,DI_SOUTHWEST,DI_SOUTHEAST}; + +void P_NewChaseDir (mobj_t *actor) +{ + fixed_t deltax,deltay; + dirtype_t d[3]; + dirtype_t tdir, olddir, turnaround; + + if (!actor->target) + I_Error ("P_NewChaseDir: called with no target"); + + olddir = actor->movedir; + turnaround=opposite[olddir]; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + if (deltax>10*FRACUNIT) + d[1]= DI_EAST; + else if (deltax<-10*FRACUNIT) + d[1]= DI_WEST; + else + d[1]=DI_NODIR; + if (deltay<-10*FRACUNIT) + d[2]= DI_SOUTH; + else if (deltay>10*FRACUNIT) + d[2]= DI_NORTH; + else + d[2]=DI_NODIR; + +// try direct route + if (d[1] != DI_NODIR && d[2] != DI_NODIR) + { + actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; + if (actor->movedir != turnaround && P_TryWalk(actor)) + return; + } + +// try other directions + if (P_Random() > 200 || abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + if (d[1]==turnaround) + d[1]=DI_NODIR; + if (d[2]==turnaround) + d[2]=DI_NODIR; + + if (d[1]!=DI_NODIR) + { + actor->movedir = d[1]; + if (P_TryWalk(actor)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=DI_NODIR) + { + actor->movedir =d[2]; + if (P_TryWalk(actor)) + return; + } + +/* there is no direct path to the player, so pick another direction */ + + if (olddir!=DI_NODIR) + { + actor->movedir =olddir; + if (P_TryWalk(actor)) + return; + } + + if (P_Random()&1) /*randomly determine direction of search*/ + { + for (tdir=DI_EAST ; tdir<=DI_SOUTHEAST ; tdir++) + { + if (tdir!=turnaround) + { + actor->movedir =tdir; + if ( P_TryWalk(actor) ) + return; + } + } + } + else + { + for (tdir=DI_SOUTHEAST ; tdir >= DI_EAST;tdir--) + { + if (tdir!=turnaround) + { + actor->movedir =tdir; + if ( P_TryWalk(actor) ) + return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir =turnaround; + if ( P_TryWalk(actor) ) + return; + } + + actor->movedir = DI_NODIR; // can't move +} + +//--------------------------------------------------------------------------- +// +// FUNC P_LookForMonsters +// +//--------------------------------------------------------------------------- + +#define MONS_LOOK_RANGE (16*64*FRACUNIT) +#define MONS_LOOK_LIMIT 64 + +boolean P_LookForMonsters(mobj_t *actor) +{ + int count; + mobj_t *mo; + thinker_t *think; + + if(!P_CheckSight(players[0].mo, actor)) + { // Player can't see monster + return(false); + } + count = 0; + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mo = (mobj_t *)think; + if(!(mo->flags&MF_COUNTKILL) || (mo == actor) || (mo->health <= 0)) + { // Not a valid monster + continue; + } + if(P_AproxDistance(actor->x-mo->x, actor->y-mo->y) + > MONS_LOOK_RANGE) + { // Out of range + continue; + } + if(P_Random() < 16) + { // Skip + continue; + } + if(count++ > MONS_LOOK_LIMIT) + { // Stop searching + return(false); + } + if(!P_CheckSight(actor, mo)) + { // Out of sight + continue; + } + if (actor->type == MT_MINOTAUR) + { + if ((mo->type == MT_MINOTAUR) && + (mo->target != ((player_t *)actor->special1)->mo)) + { + continue; + } + } + // Found a target monster + actor->target = mo; + return(true); + } + return(false); +} + +/* +================ += += P_LookForPlayers += += If allaround is false, only look 180 degrees in front += returns true if a player is targeted +================ +*/ + +boolean P_LookForPlayers(mobj_t *actor, boolean allaround) +{ + int c; + int stop; + player_t *player; + sector_t *sector; + angle_t an; + fixed_t dist; + + if(!netgame && players[0].health <= 0) + { // Single player game and player is dead, look for monsters + return(P_LookForMonsters(actor)); + } + sector = actor->subsector->sector; + c = 0; + stop = (actor->lastlook-1)&3; + for( ; ; actor->lastlook = (actor->lastlook+1)&3 ) + { + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2 || actor->lastlook == stop) + return false; // done looking + + player = &players[actor->lastlook]; + if (player->health <= 0) + continue; // dead + if (!P_CheckSight (actor, player->mo)) + continue; // out of sight + + if (!allaround) + { + an = R_PointToAngle2 (actor->x, actor->y, + player->mo->x, player->mo->y) - actor->angle; + if (an > ANG90 && an < ANG270) + { + dist = P_AproxDistance (player->mo->x - actor->x, + player->mo->y - actor->y); + // if real close, react anyway + if (dist > MELEERANGE) + continue; // behind back + } + } + if(player->mo->flags&MF_SHADOW) + { // Player is invisible + if((P_AproxDistance(player->mo->x-actor->x, + player->mo->y-actor->y) > 2*MELEERANGE) + && P_AproxDistance(player->mo->momx, player->mo->momy) + < 5*FRACUNIT) + { // Player is sneaking - can't detect + return(false); + } + if(P_Random() < 225) + { // Player isn't sneaking, but still didn't detect + return(false); + } + } + if (actor->type == MT_MINOTAUR) + { + if(((player_t *)(actor->special1)) == player) + { + continue; // Don't target master + } + } + + actor->target = player->mo; + return(true); + } + return(false); +} + +/* +=============================================================================== + + ACTION ROUTINES + +=============================================================================== +*/ + +/* +============== += += A_Look += += Stay in state until a player is sighted += +============== +*/ + +void A_Look (mobj_t *actor) +{ + mobj_t *targ; + + actor->threshold = 0; // any shot will wake up + targ = actor->subsector->sector->soundtarget; + if (targ && (targ->flags & MF_SHOOTABLE) ) + { + actor->target = targ; + if ( actor->flags & MF_AMBUSH ) + { + if (P_CheckSight (actor, actor->target)) + goto seeyou; + } + else + goto seeyou; + } + + + if (!P_LookForPlayers (actor, false) ) + return; + +// go into chase state +seeyou: + if (actor->info->seesound) + { + int sound; + + sound = actor->info->seesound; + if(actor->flags2&MF2_BOSS) + { // Full volume + S_StartSound(NULL, sound); + } + else + { + S_StartSound(actor, sound); + } + } + P_SetMobjState(actor, actor->info->seestate); +} + + +/* +============== += += A_Chase += += Actor has a melee attack, so it tries to close as fast as possible += +============== +*/ + +void A_Chase(mobj_t *actor) +{ + int delta; + + if(actor->reactiontime) + { + actor->reactiontime--; + } + + // Modify target threshold + if(actor->threshold) + { + actor->threshold--; + } + + if(gameskill == sk_nightmare) + { // Monsters move faster in nightmare mode + actor->tics -= actor->tics/2; + if(actor->tics < 3) + { + actor->tics = 3; + } + } + +// +// turn towards movement direction if not there yet +// + if(actor->movedir < 8) + { + actor->angle &= (7<<29); + delta = actor->angle-(actor->movedir << 29); + if(delta > 0) + { + actor->angle -= ANG90/2; + } + else if(delta < 0) + { + actor->angle += ANG90/2; + } + } + + if(!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { // look for a new target + if(P_LookForPlayers(actor, true)) + { // got a new target + return; + } + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + +// +// don't attack twice in a row +// + if(actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare) + P_NewChaseDir (actor); + return; + } + +// +// check for melee attack +// + if (actor->info->meleestate && P_CheckMeleeRange (actor)) + { + if(actor->info->attacksound) + { + S_StartSound (actor, actor->info->attacksound); + } + P_SetMobjState (actor, actor->info->meleestate); + return; + } + +// +// check for missile attack +// + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare && actor->movecount) + goto nomissile; + if (!P_CheckMissileRange (actor)) + goto nomissile; + P_SetMobjState (actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } +nomissile: + +// +// possibly choose another target +// + if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) + { + if (P_LookForPlayers(actor,true)) + return; // got a new target + } + +// +// chase towards player +// + if (--actor->movecount<0 || !P_Move (actor)) + { + P_NewChaseDir (actor); + } + +// +// make active sound +// + if(actor->info->activesound && P_Random() < 3) + { + if(actor->type == MT_BISHOP && P_Random() < 128) + { + S_StartSound(actor, actor->info->seesound); + } + else if(actor->type == MT_PIG) + { + S_StartSound(actor, SFX_PIG_ACTIVE1+(P_Random()&1)); + } + else if(actor->flags2&MF2_BOSS) + { + S_StartSound(NULL, actor->info->activesound); + } + else + { + S_StartSound(actor, actor->info->activesound); + } + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_FaceTarget +// +//---------------------------------------------------------------------------- + +void A_FaceTarget(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + actor->flags &= ~MF_AMBUSH; + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, + actor->target->y); + if(actor->target->flags&MF_SHADOW) + { // Target is a ghost + actor->angle += (P_Random()-P_Random())<<21; + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_Pain +// +//---------------------------------------------------------------------------- + +void A_Pain(mobj_t *actor) +{ + if(actor->info->painsound) + { + S_StartSound(actor, actor->info->painsound); + } +} + +//============================================================================ +// +// A_SetInvulnerable +// +//============================================================================ + +void A_SetInvulnerable(mobj_t *actor) +{ + actor->flags2 |= MF2_INVULNERABLE; +} + +//============================================================================ +// +// A_UnSetInvulnerable +// +//============================================================================ + +void A_UnSetInvulnerable(mobj_t *actor) +{ + actor->flags2 &= ~MF2_INVULNERABLE; +} + +//============================================================================ +// +// A_SetReflective +// +//============================================================================ + +void A_SetReflective(mobj_t *actor) +{ + actor->flags2 |= MF2_REFLECTIVE; + + if ((actor->type == MT_CENTAUR) || + (actor->type == MT_CENTAURLEADER)) + { + A_SetInvulnerable(actor); + } +} + +//============================================================================ +// +// A_UnSetReflective +// +//============================================================================ + +void A_UnSetReflective(mobj_t *actor) +{ + actor->flags2 &= ~MF2_REFLECTIVE; + + if ((actor->type == MT_CENTAUR) || + (actor->type == MT_CENTAURLEADER)) + { + A_UnSetInvulnerable(actor); + } +} + + +//---------------------------------------------------------------------------- +// +// FUNC P_UpdateMorphedMonster +// +// Returns true if the pig morphs. +// +//---------------------------------------------------------------------------- + +boolean P_UpdateMorphedMonster(mobj_t *actor, int tics) +{ + mobj_t *fog; + fixed_t x; + fixed_t y; + fixed_t z; + mobjtype_t moType; + mobj_t *mo; + mobj_t oldMonster; + + actor->special1 -= tics; + if(actor->special1 > 0) + { + return(false); + } + moType = actor->special2; + switch(moType) + { + case MT_WRAITHB: // These must remain morphed + case MT_SERPENT: + case MT_SERPENTLEADER: + case MT_MINOTAUR: + return(false); + default: + break; + } + x = actor->x; + y = actor->y; + z = actor->z; + oldMonster = *actor; // Save pig vars + + P_RemoveMobjFromTIDList(actor); + P_SetMobjState(actor, S_FREETARGMOBJ); + mo = P_SpawnMobj(x, y, z, moType); + + if(P_TestMobjLocation(mo) == false) + { // Didn't fit + P_RemoveMobj(mo); + mo = P_SpawnMobj(x, y, z, oldMonster.type); + mo->angle = oldMonster.angle; + mo->flags = oldMonster.flags; + mo->health = oldMonster.health; + mo->target = oldMonster.target; + mo->special = oldMonster.special; + mo->special1 = 5*35; // Next try in 5 seconds + mo->special2 = moType; + mo->tid = oldMonster.tid; + memcpy(mo->args, oldMonster.args, 5); + P_InsertMobjIntoTIDList(mo, oldMonster.tid); + return(false); + } + mo->angle = oldMonster.angle; + mo->target = oldMonster.target; + mo->tid = oldMonster.tid; + mo->special = oldMonster.special; + memcpy(mo->args, oldMonster.args, 5); + P_InsertMobjIntoTIDList(mo, oldMonster.tid); + fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + return(true); +} + +//---------------------------------------------------------------------------- +// +// PROC A_PigLook +// +//---------------------------------------------------------------------------- + +void A_PigLook(mobj_t *actor) +{ + if(P_UpdateMorphedMonster(actor, 10)) + { + return; + } + A_Look(actor); +} + +//---------------------------------------------------------------------------- +// +// PROC A_PigChase +// +//---------------------------------------------------------------------------- + +void A_PigChase(mobj_t *actor) +{ + if(P_UpdateMorphedMonster(actor, 3)) + { + return; + } + A_Chase(actor); +} + +//============================================================================ +// +// A_PigAttack +// +//============================================================================ + +void A_PigAttack(mobj_t *actor) +{ + if(P_UpdateMorphedMonster(actor, 18)) + { + return; + } + if(!actor->target) + { + return; + } + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, 2+(P_Random()&1)); + S_StartSound(actor, SFX_PIG_ATTACK); + } +} + +//============================================================================ +// +// A_PigPain +// +//============================================================================ + +void A_PigPain(mobj_t *actor) +{ + A_Pain(actor); + if(actor->z <= actor->floorz) + { + actor->momz = 3.5*FRACUNIT; + } +} + + + +void FaceMovementDirection(mobj_t *actor) +{ + switch(actor->movedir) + { + case DI_EAST: + actor->angle = 0<<24; + break; + case DI_NORTHEAST: + actor->angle = 32<<24; + break; + case DI_NORTH: + actor->angle = 64<<24; + break; + case DI_NORTHWEST: + actor->angle = 96<<24; + break; + case DI_WEST: + actor->angle = 128<<24; + break; + case DI_SOUTHWEST: + actor->angle = 160<<24; + break; + case DI_SOUTH: + actor->angle = 192<<24; + break; + case DI_SOUTHEAST: + actor->angle = 224<<24; + break; + } +} + + +//---------------------------------------------------------------------------- +// +// Minotaur variables +// +// special1 pointer to player that spawned it (mobj_t) +// special2 internal to minotaur AI +// args[0] args[0]-args[3] together make up minotaur start time +// args[1] | +// args[2] | +// args[3] V +// args[4] charge duration countdown +//---------------------------------------------------------------------------- + +void A_MinotaurFade0(mobj_t *actor) +{ + actor->flags &= ~MF_ALTSHADOW; + actor->flags |= MF_SHADOW; +} + +void A_MinotaurFade1(mobj_t *actor) +{ + // Second level of transparency + actor->flags &= ~MF_SHADOW; + actor->flags |= MF_ALTSHADOW; +} + +void A_MinotaurFade2(mobj_t *actor) +{ + // Make fully visible + actor->flags &= ~MF_SHADOW; + actor->flags &= ~MF_ALTSHADOW; +} + + +//---------------------------------------------------------------------------- +// +// A_MinotaurRoam - +// +// +//---------------------------------------------------------------------------- + +void A_MinotaurLook(mobj_t *actor); + +void A_MinotaurRoam(mobj_t *actor) +{ + unsigned int *starttime = (unsigned int *)actor->args; + + actor->flags &= ~MF_SHADOW; // In case pain caused him to + actor->flags &= ~MF_ALTSHADOW; // skip his fade in. + + if ((leveltime - *starttime) >= MAULATORTICS) + { + P_DamageMobj(actor,NULL,NULL,10000); + return; + } + + if (P_Random()<30) + A_MinotaurLook(actor); // adjust to closest target + + if (P_Random()<6) + { + //Choose new direction + actor->movedir = P_Random() % 8; + FaceMovementDirection(actor); + } + if (!P_Move(actor)) + { + // Turn + if (P_Random() & 1) + actor->movedir = (++actor->movedir)%8; + else + actor->movedir = (actor->movedir+7)%8; + FaceMovementDirection(actor); + } +} + + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurLook +// +// Look for enemy of player +//---------------------------------------------------------------------------- +#define MINOTAUR_LOOK_DIST (16*54*FRACUNIT) + +void A_MinotaurLook(mobj_t *actor) +{ + mobj_t *mo=NULL; + player_t *player; + thinker_t *think; + fixed_t dist; + int i; + mobj_t *master = (mobj_t *)(actor->special1); + + actor->target = NULL; + if (deathmatch) // Quick search for players + { + for (i=0; imo; + if (mo == master) continue; + if (mo->health <= 0) continue; + dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y); + if (dist > MINOTAUR_LOOK_DIST) continue; + actor->target = mo; + break; + } + } + + if (!actor->target) // Near player monster search + { + if (master && (master->health>0) && (master->player)) + mo = P_RoughMonsterSearch(master, 20); + else + mo = P_RoughMonsterSearch(actor, 20); + actor->target = mo; + } + + if (!actor->target) // Normal monster search + { + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) continue; + mo = (mobj_t *)think; + if (!(mo->flags&MF_COUNTKILL)) continue; + if (mo->health <= 0) continue; + if (!(mo->flags&MF_SHOOTABLE)) continue; + dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y); + if (dist > MINOTAUR_LOOK_DIST) continue; + if ((mo == master) || (mo == actor)) continue; + if ((mo->type == MT_MINOTAUR) && + (mo->special1 == actor->special1)) continue; + actor->target = mo; + break; // Found mobj to attack + } + } + + if (actor->target) + { + P_SetMobjStateNF(actor, S_MNTR_WALK1); + } + else + { + P_SetMobjStateNF(actor, S_MNTR_ROAM1); + } +} + + + + +void A_MinotaurChase(mobj_t *actor) +{ + unsigned int *starttime = (unsigned int *)actor->args; + + actor->flags &= ~MF_SHADOW; // In case pain caused him to + actor->flags &= ~MF_ALTSHADOW; // skip his fade in. + + if ((leveltime - *starttime) >= MAULATORTICS) + { + P_DamageMobj(actor,NULL,NULL,10000); + return; + } + + if (P_Random()<30) + A_MinotaurLook(actor); // adjust to closest target + + if (!actor->target || (actor->target->health <= 0) || + !(actor->target->flags&MF_SHOOTABLE)) + { // look for a new target + P_SetMobjState(actor, S_MNTR_LOOK1); + return; + } + + FaceMovementDirection(actor); + actor->reactiontime=0; + + // Melee attack + if (actor->info->meleestate && P_CheckMeleeRange(actor)) + { + if(actor->info->attacksound) + { + S_StartSound (actor, actor->info->attacksound); + } + P_SetMobjState (actor, actor->info->meleestate); + return; + } + + // Missile attack + if (actor->info->missilestate && P_CheckMissileRange(actor)) + { + P_SetMobjState (actor, actor->info->missilestate); + return; + } + + // chase towards target + if (!P_Move(actor)) + { + P_NewChaseDir(actor); + } + + // Active sound + if(actor->info->activesound && P_Random() < 6) + { + S_StartSound(actor, actor->info->activesound); + } + +} + + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurAtk1 +// +// Melee attack. +// +//---------------------------------------------------------------------------- + +void A_MinotaurAtk1(mobj_t *actor) +{ + if (!actor->target) return; + + S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING); + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(4)); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurDecide +// +// Choose a missile attack. +// +//---------------------------------------------------------------------------- + +#define MNTR_CHARGE_SPEED (23*FRACUNIT) + +void A_MinotaurDecide(mobj_t *actor) +{ + angle_t angle; + mobj_t *target = actor->target; + int dist; + + if (!target) return; + dist = P_AproxDistance(actor->x-target->x, actor->y-target->y); + + if(target->z+target->height > actor->z + && target->z+target->height < actor->z+actor->height + && dist < 16*64*FRACUNIT + && dist > 1*64*FRACUNIT + && P_Random() < 230) + { // Charge attack + // Don't call the state function right away + P_SetMobjStateNF(actor, S_MNTR_ATK4_1); + actor->flags |= MF_SKULLFLY; + A_FaceTarget(actor); + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]); + actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]); + actor->args[4] = 35/2; // Charge duration + } + else if(target->z == target->floorz + && dist < 9*64*FRACUNIT + && P_Random() < 100) + { // Floor fire attack + P_SetMobjState(actor, S_MNTR_ATK3_1); + actor->special2 = 0; + } + else + { // Swing attack + A_FaceTarget(actor); + // Don't need to call P_SetMobjState because the current state + // falls through to the swing attack + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurCharge +// +//---------------------------------------------------------------------------- + +void A_MinotaurCharge(mobj_t *actor) +{ + mobj_t *puff; + + if (!actor->target) return; + + if(actor->args[4] > 0) + { + puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PUNCHPUFF); + puff->momz = 2*FRACUNIT; + actor->args[4]--; + } + else + { + actor->flags &= ~MF_SKULLFLY; + P_SetMobjState(actor, actor->info->seestate); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurAtk2 +// +// Swing attack. +// +//---------------------------------------------------------------------------- + +void A_MinotaurAtk2(mobj_t *actor) +{ + mobj_t *mo; + angle_t angle; + fixed_t momz; + + if(!actor->target) return; + + S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING); + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(3)); + return; + } + mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1); + if(mo) + { + //S_StartSound(mo, sfx_minat2); + momz = mo->momz; + angle = mo->angle; + P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/8), momz); + P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/8), momz); + P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/16), momz); + P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/16), momz); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_MinotaurAtk3 +// +// Floor fire attack. +// +//---------------------------------------------------------------------------- + +void A_MinotaurAtk3(mobj_t *actor) +{ + mobj_t *mo; + player_t *player; + + if(!actor->target) + { + return; + } + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(3)); + if((player = actor->target->player) != NULL) + { // Squish the player + player->deltaviewheight = -16*FRACUNIT; + } + } + else + { + mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2); + if(mo != NULL) + { + S_StartSound(mo, SFX_MAULATOR_HAMMER_HIT); + } + } + if(P_Random() < 192 && actor->special2 == 0) + { + P_SetMobjState(actor, S_MNTR_ATK3_4); + actor->special2 = 1; + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_MntrFloorFire +// +//---------------------------------------------------------------------------- + +void A_MntrFloorFire(mobj_t *actor) +{ + mobj_t *mo; + + actor->z = actor->floorz; + mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<10), + actor->y+((P_Random()-P_Random())<<10), ONFLOORZ, MT_MNTRFX3); + mo->target = actor->target; + mo->momx = 1; // Force block checking + P_CheckMissileSpawn(mo); +} + + +//---------------------------------------------------------------------------- +// +// PROC A_Scream +// +//---------------------------------------------------------------------------- + +void A_Scream(mobj_t *actor) +{ + int sound; + + S_StopSound(actor); + if(actor->player) + { + if(actor->player->morphTics) + { + S_StartSound(actor, actor->info->deathsound); + } + else + { + // Handle the different player death screams + if(actor->momz <= -39*FRACUNIT) + { // Falling splat + sound = SFX_PLAYER_FALLING_SPLAT; + } + else if(actor->health > -50) + { // Normal death sound + switch(actor->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PLAYER_FIGHTER_NORMAL_DEATH; + break; + case PCLASS_CLERIC: + sound = SFX_PLAYER_CLERIC_NORMAL_DEATH; + break; + case PCLASS_MAGE: + sound = SFX_PLAYER_MAGE_NORMAL_DEATH; + break; + default: + sound = SFX_NONE; + break; + } + } + else if(actor->health > -100) + { // Crazy death sound + switch(actor->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PLAYER_FIGHTER_CRAZY_DEATH; + break; + case PCLASS_CLERIC: + sound = SFX_PLAYER_CLERIC_CRAZY_DEATH; + break; + case PCLASS_MAGE: + sound = SFX_PLAYER_MAGE_CRAZY_DEATH; + break; + default: + sound = SFX_NONE; + break; + } + } + else + { // Extreme death sound + switch(actor->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PLAYER_FIGHTER_EXTREME1_DEATH; + break; + case PCLASS_CLERIC: + sound = SFX_PLAYER_CLERIC_EXTREME1_DEATH; + break; + case PCLASS_MAGE: + sound = SFX_PLAYER_MAGE_EXTREME1_DEATH; + break; + default: + sound = SFX_NONE; + break; + } + sound += P_Random()%3; // Three different extreme deaths + } + S_StartSound(actor, sound); + } + } + else + { + S_StartSound(actor, actor->info->deathsound); + } +} + +//--------------------------------------------------------------------------- +// +// PROC P_DropItem +// +//--------------------------------------------------------------------------- + +/* +void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance) +{ + mobj_t *mo; + + if(P_Random() > chance) + { + return; + } + mo = P_SpawnMobj(source->x, source->y, + source->z+(source->height>>1), type); + mo->momx = (P_Random()-P_Random())<<8; + mo->momy = (P_Random()-P_Random())<<8; + mo->momz = FRACUNIT*5+(P_Random()<<10); + mo->flags2 |= MF2_DROPPED; + mo->health = special; +} +*/ + +//---------------------------------------------------------------------------- +// +// PROC A_NoBlocking +// +//---------------------------------------------------------------------------- + +void A_NoBlocking(mobj_t *actor) +{ + actor->flags &= ~MF_SOLID; + + // Check for monsters dropping things +/* switch(actor->type) + { + // Add the monster dropped items here + case MT_MUMMYLEADERGHOST: + P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84); + break; + default: + break; + } +*/ +} + +//---------------------------------------------------------------------------- +// +// PROC A_Explode +// +// Handles a bunch of exploding things. +// +//---------------------------------------------------------------------------- + +void A_Explode(mobj_t *actor) +{ + int damage; + int distance; + boolean damageSelf; + + damage = 128; + distance = 128; + damageSelf = true; + switch(actor->type) + { + case MT_FIREBOMB: // Time Bombs + actor->z += 32*FRACUNIT; + actor->flags &= ~MF_SHADOW; + break; + case MT_MNTRFX2: // Minotaur floor fire + damage = 24; + break; + case MT_BISHOP: // Bishop radius death + damage = 25+(P_Random()&15); + break; + case MT_HAMMER_MISSILE: // Fighter Hammer + damage = 128; + damageSelf = false; + break; + case MT_FSWORD_MISSILE: // Fighter Runesword + damage = 64; + damageSelf = false; + break; + case MT_CIRCLEFLAME: // Cleric Flame secondary flames + damage = 20; + damageSelf = false; + break; + case MT_SORCBALL1: // Sorcerer balls + case MT_SORCBALL2: + case MT_SORCBALL3: + distance = 255; + damage = 255; + actor->args[0] = 1; // don't play bounce + break; + case MT_SORCFX1: // Sorcerer spell 1 + damage = 30; + break; + case MT_SORCFX4: // Sorcerer spell 4 + damage = 20; + break; + case MT_TREEDESTRUCTIBLE: + damage = 10; + break; + case MT_DRAGON_FX2: + damage = 80; + damageSelf = false; + break; + case MT_MSTAFF_FX: + damage = 64; + distance = 192; + damageSelf = false; + break; + case MT_MSTAFF_FX2: + damage = 80; + distance = 192; + damageSelf = false; + break; + case MT_POISONCLOUD: + damage = 4; + distance = 40; + break; + case MT_ZXMAS_TREE: + case MT_ZSHRUB2: + damage = 30; + distance = 64; + break; + default: + break; + } + P_RadiusAttack(actor, actor->target, damage, distance, damageSelf); + if(actor->z <= actor->floorz+(distance<type != MT_POISONCLOUD) + { + P_HitFloor(actor); + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_Massacre +// +// Kills all monsters. +// +//---------------------------------------------------------------------------- + +int P_Massacre(void) +{ + int count; + mobj_t *mo; + thinker_t *think; + + count = 0; + for(think = thinkercap.next; think != &thinkercap; + think = think->next) + { + if(think->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mo = (mobj_t *)think; + if((mo->flags&MF_COUNTKILL) && (mo->health > 0)) + { + mo->flags2 &= ~(MF2_NONSHOOTABLE+MF2_INVULNERABLE); + mo->flags |= MF_SHOOTABLE; + P_DamageMobj(mo, NULL, NULL, 10000); + count++; + } + } + return count; +} + + + +//---------------------------------------------------------------------------- +// +// PROC A_SkullPop +// +//---------------------------------------------------------------------------- + +void A_SkullPop(mobj_t *actor) +{ + mobj_t *mo; + player_t *player; + + if(!actor->player) + { + return; + } + actor->flags &= ~MF_SOLID; + mo = P_SpawnMobj(actor->x, actor->y, actor->z+48*FRACUNIT, + MT_BLOODYSKULL); + //mo->target = actor; + mo->momx = (P_Random()-P_Random())<<9; + mo->momy = (P_Random()-P_Random())<<9; + mo->momz = FRACUNIT*2+(P_Random()<<6); + // Attach player mobj to bloody skull + player = actor->player; + actor->player = NULL; + actor->special1 = player->class; + mo->player = player; + mo->health = actor->health; + mo->angle = actor->angle; + player->mo = mo; + player->lookdir = 0; + player->damagecount = 32; +} + +//---------------------------------------------------------------------------- +// +// PROC A_CheckSkullFloor +// +//---------------------------------------------------------------------------- + +void A_CheckSkullFloor(mobj_t *actor) +{ + if(actor->z <= actor->floorz) + { + P_SetMobjState(actor, S_BLOODYSKULLX1); + S_StartSound(actor, SFX_DRIP); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_CheckSkullDone +// +//---------------------------------------------------------------------------- + +void A_CheckSkullDone(mobj_t *actor) +{ + if(actor->special2 == 666) + { + P_SetMobjState(actor, S_BLOODYSKULLX2); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_CheckBurnGone +// +//---------------------------------------------------------------------------- + +void A_CheckBurnGone(mobj_t *actor) +{ + if(actor->special2 == 666) + { + P_SetMobjState(actor, S_PLAY_FDTH20); + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_FreeTargMobj +// +//---------------------------------------------------------------------------- + +void A_FreeTargMobj(mobj_t *mo) +{ + mo->momx = mo->momy = mo->momz = 0; + mo->z = mo->ceilingz+4*FRACUNIT; + mo->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_SOLID|MF_COUNTKILL); + mo->flags |= MF_CORPSE|MF_DROPOFF|MF_NOGRAVITY; + mo->flags2 &= ~(MF2_PASSMOBJ|MF2_LOGRAV); + mo->flags2 |= MF2_DONTDRAW; + mo->player = NULL; + mo->health = -1000; // Don't resurrect +} + + +//---------------------------------------------------------------------------- +// +// CorpseQueue Routines +// +//---------------------------------------------------------------------------- + +// Corpse queue for monsters - this should be saved out +#define CORPSEQUEUESIZE 64 +mobj_t *corpseQueue[CORPSEQUEUESIZE]; +int corpseQueueSlot; + +// throw another corpse on the queue +void A_QueueCorpse(mobj_t *actor) +{ + mobj_t *corpse; + + if(corpseQueueSlot >= CORPSEQUEUESIZE) + { // Too many corpses - remove an old one + corpse = corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE]; + if (corpse) P_RemoveMobj(corpse); + } + corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE] = actor; + corpseQueueSlot++; +} + +// Remove a mobj from the queue (for resurrection) +void A_DeQueueCorpse(mobj_t *actor) +{ + int slot; + + for (slot=0; slotnext) + { + if(think->function != P_MobjThinker) continue; + mo = (mobj_t *)think; + if (!(mo->flags&MF_CORPSE)) continue; // Must be a corpse + if (mo->flags&MF_ICECORPSE) continue; // Not ice corpses + // Only corpses that call A_QueueCorpse from death routine + switch(mo->type) + { + case MT_CENTAUR: + case MT_CENTAURLEADER: + case MT_DEMON: + case MT_DEMON2: + case MT_WRAITH: + case MT_WRAITHB: + case MT_BISHOP: + case MT_ETTIN: + case MT_PIG: + case MT_CENTAUR_SHIELD: + case MT_CENTAUR_SWORD: + case MT_DEMONCHUNK1: + case MT_DEMONCHUNK2: + case MT_DEMONCHUNK3: + case MT_DEMONCHUNK4: + case MT_DEMONCHUNK5: + case MT_DEMON2CHUNK1: + case MT_DEMON2CHUNK2: + case MT_DEMON2CHUNK3: + case MT_DEMON2CHUNK4: + case MT_DEMON2CHUNK5: + case MT_FIREDEMON_SPLOTCH1: + case MT_FIREDEMON_SPLOTCH2: + A_QueueCorpse(mo); // Add corpse to queue + break; + default: + break; + } + } +} + + +//---------------------------------------------------------------------------- +// +// PROC A_AddPlayerCorpse +// +//---------------------------------------------------------------------------- + +#define BODYQUESIZE 32 +mobj_t *bodyque[BODYQUESIZE]; +int bodyqueslot; + +void A_AddPlayerCorpse(mobj_t *actor) +{ + if(bodyqueslot >= BODYQUESIZE) + { // Too many player corpses - remove an old one + P_RemoveMobj(bodyque[bodyqueslot%BODYQUESIZE]); + } + bodyque[bodyqueslot%BODYQUESIZE] = actor; + bodyqueslot++; +} + +//============================================================================ +// +// A_SerpentUnHide +// +//============================================================================ + +void A_SerpentUnHide(mobj_t *actor) +{ + actor->flags2 &= ~MF2_DONTDRAW; + actor->floorclip = 24*FRACUNIT; +} + +//============================================================================ +// +// A_SerpentHide +// +//============================================================================ + +void A_SerpentHide(mobj_t *actor) +{ + actor->flags2 |= MF2_DONTDRAW; + actor->floorclip = 0; +} +//============================================================================ +// +// A_SerpentChase +// +//============================================================================ + +void A_SerpentChase(mobj_t *actor) +{ + int delta; + int oldX, oldY, oldFloor; + + if(actor->reactiontime) + { + actor->reactiontime--; + } + + // Modify target threshold + if(actor->threshold) + { + actor->threshold--; + } + + if(gameskill == sk_nightmare) + { // Monsters move faster in nightmare mode + actor->tics -= actor->tics/2; + if(actor->tics < 3) + { + actor->tics = 3; + } + } + +// +// turn towards movement direction if not there yet +// + if(actor->movedir < 8) + { + actor->angle &= (7<<29); + delta = actor->angle-(actor->movedir << 29); + if(delta > 0) + { + actor->angle -= ANG90/2; + } + else if(delta < 0) + { + actor->angle += ANG90/2; + } + } + + if(!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { // look for a new target + if(P_LookForPlayers(actor, true)) + { // got a new target + return; + } + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + +// +// don't attack twice in a row +// + if(actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare) + P_NewChaseDir (actor); + return; + } + +// +// check for melee attack +// + if (actor->info->meleestate && P_CheckMeleeRange (actor)) + { + if(actor->info->attacksound) + { + S_StartSound (actor, actor->info->attacksound); + } + P_SetMobjState (actor, actor->info->meleestate); + return; + } + +// +// possibly choose another target +// + if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) + { + if (P_LookForPlayers(actor,true)) + return; // got a new target + } + +// +// chase towards player +// + oldX = actor->x; + oldY = actor->y; + oldFloor = actor->subsector->sector->floorpic; + if (--actor->movecount<0 || !P_Move (actor)) + { + P_NewChaseDir (actor); + } + if(actor->subsector->sector->floorpic != oldFloor) + { + P_TryMove(actor, oldX, oldY); + P_NewChaseDir (actor); + } + +// +// make active sound +// + if(actor->info->activesound && P_Random() < 3) + { + S_StartSound(actor, actor->info->activesound); + } +} + +//============================================================================ +// +// A_SerpentRaiseHump +// +// Raises the hump above the surface by raising the floorclip level +//============================================================================ + +void A_SerpentRaiseHump(mobj_t *actor) +{ + actor->floorclip -= 4*FRACUNIT; +} + +//============================================================================ +// +// A_SerpentLowerHump +// +//============================================================================ + +void A_SerpentLowerHump(mobj_t *actor) +{ + actor->floorclip += 4*FRACUNIT; +} + +//============================================================================ +// +// A_SerpentHumpDecide +// +// Decided whether to hump up, or if the mobj is a serpent leader, +// to missile attack +//============================================================================ + +void A_SerpentHumpDecide(mobj_t *actor) +{ + if(actor->type == MT_SERPENTLEADER) + { + if(P_Random() > 30) + { + return; + } + else if(P_Random() < 40) + { // Missile attack + P_SetMobjState(actor, S_SERPENT_SURFACE1); + return; + } + } + else if(P_Random() > 3) + { + return; + } + if(!P_CheckMeleeRange(actor)) + { // The hump shouldn't occur when within melee range + if(actor->type == MT_SERPENTLEADER && P_Random() < 128) + { + P_SetMobjState(actor, S_SERPENT_SURFACE1); + } + else + { + P_SetMobjState(actor, S_SERPENT_HUMP1); + S_StartSound(actor, SFX_SERPENT_ACTIVE); + } + } +} + +//============================================================================ +// +// A_SerpentBirthScream +// +//============================================================================ + +void A_SerpentBirthScream(mobj_t *actor) +{ + S_StartSound(actor, SFX_SERPENT_BIRTH); +} + +//============================================================================ +// +// A_SerpentDiveSound +// +//============================================================================ + +void A_SerpentDiveSound(mobj_t *actor) +{ + S_StartSound(actor, SFX_SERPENT_ACTIVE); +} + +//============================================================================ +// +// A_SerpentWalk +// +// Similar to A_Chase, only has a hardcoded entering of meleestate +//============================================================================ + +void A_SerpentWalk(mobj_t *actor) +{ + int delta; + + if(actor->reactiontime) + { + actor->reactiontime--; + } + + // Modify target threshold + if(actor->threshold) + { + actor->threshold--; + } + + if(gameskill == sk_nightmare) + { // Monsters move faster in nightmare mode + actor->tics -= actor->tics/2; + if(actor->tics < 3) + { + actor->tics = 3; + } + } + +// +// turn towards movement direction if not there yet +// + if(actor->movedir < 8) + { + actor->angle &= (7<<29); + delta = actor->angle-(actor->movedir << 29); + if(delta > 0) + { + actor->angle -= ANG90/2; + } + else if(delta < 0) + { + actor->angle += ANG90/2; + } + } + + if(!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { // look for a new target + if(P_LookForPlayers(actor, true)) + { // got a new target + return; + } + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + +// +// don't attack twice in a row +// + if(actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare) + P_NewChaseDir (actor); + return; + } + +// +// check for melee attack +// + if (actor->info->meleestate && P_CheckMeleeRange (actor)) + { + if (actor->info->attacksound) + { + S_StartSound (actor, actor->info->attacksound); + } + P_SetMobjState(actor, S_SERPENT_ATK1); + return; + } +// +// possibly choose another target +// + if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) + { + if (P_LookForPlayers(actor,true)) + return; // got a new target + } + +// +// chase towards player +// + if (--actor->movecount<0 || !P_Move (actor)) + { + P_NewChaseDir (actor); + } +} + +//============================================================================ +// +// A_SerpentCheckForAttack +// +//============================================================================ + +void A_SerpentCheckForAttack(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + if(actor->type == MT_SERPENTLEADER) + { + if(!P_CheckMeleeRange(actor)) + { + P_SetMobjState(actor, S_SERPENT_ATK1); + return; + } + } + if(P_CheckMeleeRange2(actor)) + { + P_SetMobjState(actor, S_SERPENT_WALK1); + } + else if(P_CheckMeleeRange(actor)) + { + if(P_Random() < 32) + { + P_SetMobjState(actor, S_SERPENT_WALK1); + } + else + { + P_SetMobjState(actor, S_SERPENT_ATK1); + } + } +} + +//============================================================================ +// +// A_SerpentChooseAttack +// +//============================================================================ + +void A_SerpentChooseAttack(mobj_t *actor) +{ + if(!actor->target || P_CheckMeleeRange(actor)) + { + return; + } + if(actor->type == MT_SERPENTLEADER) + { + P_SetMobjState(actor, S_SERPENT_MISSILE1); + } +} + +//============================================================================ +// +// A_SerpentMeleeAttack +// +//============================================================================ + +void A_SerpentMeleeAttack(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(5)); + S_StartSound(actor, SFX_SERPENT_MELEEHIT); + } + if(P_Random() < 96) + { + A_SerpentCheckForAttack(actor); + } +} + +//============================================================================ +// +// A_SerpentMissileAttack +// +//============================================================================ + +void A_SerpentMissileAttack(mobj_t *actor) +{ + mobj_t *mo; + + if(!actor->target) + { + return; + } + mo = P_SpawnMissile(actor, actor->target, MT_SERPENTFX); +} + +//============================================================================ +// +// A_SerpentHeadPop +// +//============================================================================ + +void A_SerpentHeadPop(mobj_t *actor) +{ + P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, MT_SERPENT_HEAD); +} + +//============================================================================ +// +// A_SerpentSpawnGibs +// +//============================================================================ + +void A_SerpentSpawnGibs(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12), + actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT, + MT_SERPENT_GIB1); + if(mo) + { + mo->momx = (P_Random()-128)<<6; + mo->momy = (P_Random()-128)<<6; + mo->floorclip = 6*FRACUNIT; + } + mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12), + actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT, + MT_SERPENT_GIB2); + if(mo) + { + mo->momx = (P_Random()-128)<<6; + mo->momy = (P_Random()-128)<<6; + mo->floorclip = 6*FRACUNIT; + } + mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12), + actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT, + MT_SERPENT_GIB3); + if(mo) + { + mo->momx = (P_Random()-128)<<6; + mo->momy = (P_Random()-128)<<6; + mo->floorclip = 6*FRACUNIT; + } +} + +//============================================================================ +// +// A_FloatGib +// +//============================================================================ + +void A_FloatGib(mobj_t *actor) +{ + actor->floorclip -= FRACUNIT; +} + +//============================================================================ +// +// A_SinkGib +// +//============================================================================ + +void A_SinkGib(mobj_t *actor) +{ + actor->floorclip += FRACUNIT; +} + +//============================================================================ +// +// A_DelayGib +// +//============================================================================ + +void A_DelayGib(mobj_t *actor) +{ + actor->tics -= P_Random()>>2; +} + +//============================================================================ +// +// A_SerpentHeadCheck +// +//============================================================================ + +void A_SerpentHeadCheck(mobj_t *actor) +{ + if(actor->z <= actor->floorz) + { + if(P_GetThingFloorType(actor) >= FLOOR_LIQUID) + { + P_HitFloor(actor); + P_SetMobjState(actor, S_NULL); + } + else + { + P_SetMobjState(actor, S_SERPENT_HEAD_X1); + } + } +} + +//============================================================================ +// +// A_CentaurAttack +// +//============================================================================ + +void A_CentaurAttack(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, P_Random()%7+3); + } +} + +//============================================================================ +// +// A_CentaurAttack2 +// +//============================================================================ + +void A_CentaurAttack2(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + P_SpawnMissile(actor, actor->target, MT_CENTAUR_FX); + S_StartSound(actor, SFX_CENTAURLEADER_ATTACK); +} + +//============================================================================ +// +// A_CentaurDropStuff +// +// Spawn shield/sword sprites when the centaur pulps //============================================================================ + +void A_CentaurDropStuff(mobj_t *actor) +{ + mobj_t *mo; + angle_t angle; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_CENTAUR_SHIELD); + if(mo) + { + angle = actor->angle+ANG90; + mo->momz = FRACUNIT*8+(P_Random()<<10); + mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_CENTAUR_SWORD); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = FRACUNIT*8+(P_Random()<<10); + mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } +} + +//============================================================================ +// +// A_CentaurDefend +// +//============================================================================ + +void A_CentaurDefend(mobj_t *actor) +{ + A_FaceTarget(actor); + if(P_CheckMeleeRange(actor) && P_Random() < 32) + { + A_UnSetInvulnerable(actor); + P_SetMobjState(actor, actor->info->meleestate); + } +} + +//============================================================================ +// +// A_BishopAttack +// +//============================================================================ + +void A_BishopAttack(mobj_t *actor) +{ + if(!actor->target) + { + return; + } + S_StartSound(actor, actor->info->attacksound); + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(4)); + return; + } + actor->special1 = (P_Random()&3)+5; +} + +//============================================================================ +// +// A_BishopAttack2 +// +// Spawns one of a string of bishop missiles +//============================================================================ + +void A_BishopAttack2(mobj_t *actor) +{ + mobj_t *mo; + + if(!actor->target || !actor->special1) + { + actor->special1 = 0; + P_SetMobjState(actor, S_BISHOP_WALK1); + return; + } + mo = P_SpawnMissile(actor, actor->target, MT_BISH_FX); + if(mo) + { + mo->special1 = (int)actor->target; + mo->special2 = 16; // High word == x/y, Low word == z + } + actor->special1--; +} + +//============================================================================ +// +// A_BishopMissileWeave +// +//============================================================================ + +void A_BishopMissileWeave(mobj_t *actor) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + + weaveXY = actor->special2>>16; + weaveZ = actor->special2&0xFFFF; + angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + newX = actor->x-FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<1); + newY = actor->y-FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<1); + weaveXY = (weaveXY+2)&63; + newX += FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<1); + newY += FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<1); + P_TryMove(actor, newX, newY); + actor->z -= FloatBobOffsets[weaveZ]; + weaveZ = (weaveZ+2)&63; + actor->z += FloatBobOffsets[weaveZ]; + actor->special2 = weaveZ+(weaveXY<<16); +} + +//============================================================================ +// +// A_BishopMissileSeek +// +//============================================================================ + +void A_BishopMissileSeek(mobj_t *actor) +{ + P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*3); +} + +//============================================================================ +// +// A_BishopDecide +// +//============================================================================ + +void A_BishopDecide(mobj_t *actor) +{ + if(P_Random() < 220) + { + return; + } + else + { + P_SetMobjState(actor, S_BISHOP_BLUR1); + } +} + +//============================================================================ +// +// A_BishopDoBlur +// +//============================================================================ + +void A_BishopDoBlur(mobj_t *actor) +{ + actor->special1 = (P_Random()&3)+3; // Random number of blurs + if(P_Random() < 120) + { + P_ThrustMobj(actor, actor->angle+ANG90, 11*FRACUNIT); + } + else if(P_Random() > 125) + { + P_ThrustMobj(actor, actor->angle-ANG90, 11*FRACUNIT); + } + else + { // Thrust forward + P_ThrustMobj(actor, actor->angle, 11*FRACUNIT); + } + S_StartSound(actor, SFX_BISHOP_BLUR); +} + +//============================================================================ +// +// A_BishopSpawnBlur +// +//============================================================================ + +void A_BishopSpawnBlur(mobj_t *actor) +{ + mobj_t *mo; + + if(!--actor->special1) + { + actor->momx = 0; + actor->momy = 0; + if(P_Random() > 96) + { + P_SetMobjState(actor, S_BISHOP_WALK1); + } + else + { + P_SetMobjState(actor, S_BISHOP_ATK1); + } + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOPBLUR); + if(mo) + { + mo->angle = actor->angle; + } +} + +//============================================================================ +// +// A_BishopChase +// +//============================================================================ + +void A_BishopChase(mobj_t *actor) +{ + actor->z -= FloatBobOffsets[actor->special2]>>1; + actor->special2 = (actor->special2+4)&63; + actor->z += FloatBobOffsets[actor->special2]>>1; +} + +//============================================================================ +// +// A_BishopPuff +// +//============================================================================ + +void A_BishopPuff(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z+40*FRACUNIT, + MT_BISHOP_PUFF); + if(mo) + { + mo->momz = FRACUNIT/2; + } +} + +//============================================================================ +// +// A_BishopPainBlur +// +//============================================================================ + +void A_BishopPainBlur(mobj_t *actor) +{ + mobj_t *mo; + + if(P_Random() < 64) + { + P_SetMobjState(actor, S_BISHOP_BLUR1); + return; + } + mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<12), actor->y + +((P_Random()-P_Random())<<12), actor->z+((P_Random()-P_Random())<<11), + MT_BISHOPPAINBLUR); + if(mo) + { + mo->angle = actor->angle; + } +} + +//============================================================================ +// +// DragonSeek +// +//============================================================================ + +static void DragonSeek(mobj_t *actor, angle_t thresh, angle_t turnMax) +{ + int dir; + int dist; + angle_t delta; + angle_t angle; + mobj_t *target; + int search; + int i; + int bestArg; + angle_t bestAngle; + angle_t angleToSpot, angleToTarget; + mobj_t *mo; + + target = (mobj_t *)actor->special1; + if(target == NULL) + { + return; + } + dir = P_FaceMobj(actor, target, &delta); + if(delta > thresh) + { + delta >>= 1; + if(delta > turnMax) + { + delta = turnMax; + } + } + if(dir) + { // Turn clockwise + actor->angle += delta; + } + else + { // Turn counter clockwise + actor->angle -= delta; + } + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[angle]); + actor->momy = FixedMul(actor->info->speed, finesine[angle]); + if(actor->z+actor->height < target->z + || target->z+target->height < actor->z) + { + dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); + dist = dist/actor->info->speed; + if(dist < 1) + { + dist = 1; + } + actor->momz = (target->z-actor->z)/dist; + } + else + { + dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); + dist = dist/actor->info->speed; + } + if(target->flags&MF_SHOOTABLE && P_Random() < 64) + { // attack the destination mobj if it's attackable + mobj_t *oldTarget; + + if(abs(actor->angle-R_PointToAngle2(actor->x, actor->y, + target->x, target->y)) < ANGLE_45/2) + { + oldTarget = actor->target; + actor->target = target; + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(10)); + S_StartSound(actor, SFX_DRAGON_ATTACK); + } + else if(P_Random() < 128 && P_CheckMissileRange(actor)) + { + P_SpawnMissile(actor, target, MT_DRAGON_FX); + S_StartSound(actor, SFX_DRAGON_ATTACK); + } + actor->target = oldTarget; + } + } + if(dist < 4) + { // Hit the target thing + if(actor->target && P_Random() < 200) + { + bestArg = -1; + bestAngle = ANGLE_MAX; + angleToTarget = R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y); + for(i = 0; i < 5; i++) + { + if(!target->args[i]) + { + continue; + } + search = -1; + mo = P_FindMobjFromTID(target->args[i], &search); + angleToSpot = R_PointToAngle2(actor->x, actor->y, + mo->x, mo->y); + if(abs(angleToSpot-angleToTarget) < bestAngle) + { + bestAngle = abs(angleToSpot-angleToTarget); + bestArg = i; + } + } + if(bestArg != -1) + { + search = -1; + actor->special1 = (int)P_FindMobjFromTID(target->args[bestArg], + &search); + } + } + else + { + do + { + i = (P_Random()>>2)%5; + } while(!target->args[i]); + search = -1; + actor->special1 = (int)P_FindMobjFromTID(target->args[i], &search); + } + } +} + +//============================================================================ +// +// A_DragonInitFlight +// +//============================================================================ + +void A_DragonInitFlight(mobj_t *actor) +{ + int search; + + search = -1; + do + { // find the first tid identical to the dragon's tid + actor->special1 = (int)P_FindMobjFromTID(actor->tid, &search); + if(search == -1) + { + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + } while(actor->special1 == (int)actor); + P_RemoveMobjFromTIDList(actor); +} + +//============================================================================ +// +// A_DragonFlight +// +//============================================================================ + +void A_DragonFlight(mobj_t *actor) +{ + angle_t angle; + + DragonSeek(actor, 4*ANGLE_1, 8*ANGLE_1); + if(actor->target) + { + if(!(actor->target->flags&MF_SHOOTABLE)) + { // target died + actor->target = NULL; + return; + } + angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, + actor->target->y); + if(abs(actor->angle-angle) < ANGLE_45/2 && P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(8)); + S_StartSound(actor, SFX_DRAGON_ATTACK); + } + else if(abs(actor->angle-angle) <= ANGLE_1*20) + { + P_SetMobjState(actor, actor->info->missilestate); + S_StartSound(actor, SFX_DRAGON_ATTACK); + } + } + else + { + P_LookForPlayers(actor, true); + } +} + +//============================================================================ +// +// A_DragonFlap +// +//============================================================================ + +void A_DragonFlap(mobj_t *actor) +{ + A_DragonFlight(actor); + if(P_Random() < 240) + { + S_StartSound(actor, SFX_DRAGON_WINGFLAP); + } + else + { + S_StartSound(actor, actor->info->activesound); + } +} + +//============================================================================ +// +// A_DragonAttack +// +//============================================================================ + +void A_DragonAttack(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMissile(actor, actor->target, MT_DRAGON_FX); +} + +//============================================================================ +// +// A_DragonFX2 +// +//============================================================================ + +void A_DragonFX2(mobj_t *actor) +{ + mobj_t *mo; + int i; + int delay; + + delay = 16+(P_Random()>>3); + for(i = 1+(P_Random()&3); i; i--) + { + mo = P_SpawnMobj(actor->x+((P_Random()-128)<<14), + actor->y+((P_Random()-128)<<14), actor->z+((P_Random()-128)<<12), + MT_DRAGON_FX2); + if(mo) + { + mo->tics = delay+(P_Random()&3)*i*2; + mo->target = actor->target; + } + } +} + +//============================================================================ +// +// A_DragonPain +// +//============================================================================ + +void A_DragonPain(mobj_t *actor) +{ + A_Pain(actor); + if(!actor->special1) + { // no destination spot yet + P_SetMobjState(actor, S_DRAGON_INIT); + } +} + +//============================================================================ +// +// A_DragonCheckCrash +// +//============================================================================ + +void A_DragonCheckCrash(mobj_t *actor) +{ + if(actor->z <= actor->floorz) + { + P_SetMobjState(actor, S_DRAGON_CRASH1); + } +} + +//============================================================================ +// Demon AI +//============================================================================ + +// +// A_DemonAttack1 (melee) +// +void A_DemonAttack1(mobj_t *actor) +{ + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(2)); + } +} + + +// +// A_DemonAttack2 (missile) +// +void A_DemonAttack2(mobj_t *actor) +{ + mobj_t *mo; + int fireBall; + + if(actor->type == MT_DEMON) + { + fireBall = MT_DEMONFX1; + } + else + { + fireBall = MT_DEMON2FX1; + } + mo = P_SpawnMissile(actor, actor->target, fireBall); + if (mo) + { + mo->z += 30*FRACUNIT; + S_StartSound(actor, SFX_DEMON_MISSILE_FIRE); + } +} + +// +// A_DemonDeath +// + +void A_DemonDeath(mobj_t *actor) +{ + mobj_t *mo; + angle_t angle; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMONCHUNK1); + if(mo) + { + angle = actor->angle+ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMONCHUNK2); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMONCHUNK3); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMONCHUNK4); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMONCHUNK5); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } +} + +//=========================================================================== +// +// A_Demon2Death +// +//=========================================================================== + +void A_Demon2Death(mobj_t *actor) +{ + mobj_t *mo; + angle_t angle; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMON2CHUNK1); + if(mo) + { + angle = actor->angle+ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMON2CHUNK2); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMON2CHUNK3); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMON2CHUNK4); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, + MT_DEMON2CHUNK5); + if(mo) + { + angle = actor->angle-ANG90; + mo->momz = 8*FRACUNIT; + mo->momx = FixedMul((P_Random()<<10)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<10)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + } +} + + + +// +// A_SinkMobj +// Sink a mobj incrementally into the floor +// + +boolean A_SinkMobj(mobj_t *actor) +{ + if (actor->floorclip < actor->info->height) + { + switch(actor->type) + { + case MT_THRUSTFLOOR_DOWN: + case MT_THRUSTFLOOR_UP: + actor->floorclip += 6*FRACUNIT; + break; + default: + actor->floorclip += FRACUNIT; + break; + } + return false; + } + return true; +} + +// +// A_RaiseMobj +// Raise a mobj incrementally from the floor to +// + +boolean A_RaiseMobj(mobj_t *actor) +{ + int done = true; + + // Raise a mobj from the ground + if (actor->floorclip > 0) + { + switch(actor->type) + { + case MT_WRAITHB: + actor->floorclip -= 2*FRACUNIT; + break; + case MT_THRUSTFLOOR_DOWN: + case MT_THRUSTFLOOR_UP: + actor->floorclip -= actor->special2*FRACUNIT; + break; + default: + actor->floorclip -= 2*FRACUNIT; + break; + } + if (actor->floorclip <= 0) + { + actor->floorclip = 0; + done=true; + } + else + { + done = false; + } + } + return done; // Reached target height +} + + +//============================================================================ +// Wraith Variables +// +// special1 Internal index into floatbob +// special2 +//============================================================================ + +// +// A_WraithInit +// + +void A_WraithInit(mobj_t *actor) +{ + actor->z += 48<special1 = 0; // index into floatbob +} + +void A_WraithRaiseInit(mobj_t *actor) +{ + actor->flags2 &= ~MF2_DONTDRAW; + actor->flags2 &= ~MF2_NONSHOOTABLE; + actor->flags |= MF_SHOOTABLE|MF_SOLID; + actor->floorclip = actor->info->height; +} + +void A_WraithRaise(mobj_t *actor) +{ + if (A_RaiseMobj(actor)) + { + // Reached it's target height + P_SetMobjState(actor,S_WRAITH_CHASE1); + } + + P_SpawnDirt(actor, actor->radius); +} + + +void A_WraithMelee(mobj_t *actor) +{ + int amount; + + // Steal health from target and give to player + if(P_CheckMeleeRange(actor) && (P_Random()<220)) + { + amount = HITDICE(2); + P_DamageMobj(actor->target, actor, actor, amount); + actor->health += amount; + } +} + +void A_WraithMissile(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMissile(actor, actor->target, MT_WRAITHFX1); + if (mo) + { + S_StartSound(actor, SFX_WRAITH_MISSILE_FIRE); + } +} + + +// +// A_WraithFX2 - spawns sparkle tail of missile +// + +void A_WraithFX2(mobj_t *actor) +{ + mobj_t *mo; + angle_t angle; + int i; + + for (i=0; i<2; i++) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX2); + if(mo) + { + if (P_Random()<128) + { + angle = actor->angle+(P_Random()<<22); + } + else + { + angle = actor->angle-(P_Random()<<22); + } + mo->momz = 0; + mo->momx = FixedMul((P_Random()<<7)+FRACUNIT, + finecosine[angle>>ANGLETOFINESHIFT]); + mo->momy = FixedMul((P_Random()<<7)+FRACUNIT, + finesine[angle>>ANGLETOFINESHIFT]); + mo->target = actor; + mo->floorclip = 10*FRACUNIT; + } + } +} + + +// Spawn an FX3 around the actor during attacks +void A_WraithFX3(mobj_t *actor) +{ + mobj_t *mo; + int numdropped=P_Random()%15; + int i; + + for (i=0; ix, actor->y, actor->z, MT_WRAITHFX3); + if(mo) + { + mo->x += (P_Random()-128)<<11; + mo->y += (P_Random()-128)<<11; + mo->z += (P_Random()<<10); + mo->target = actor; + } + } +} + +// Spawn an FX4 during movement +void A_WraithFX4(mobj_t *actor) +{ + mobj_t *mo; + int chance = P_Random(); + int spawn4,spawn5; + + if (chance < 10) + { + spawn4 = true; + spawn5 = false; + } + else if (chance < 20) + { + spawn4 = false; + spawn5 = true; + } + else if (chance < 25) + { + spawn4 = true; + spawn5 = true; + } + else + { + spawn4 = false; + spawn5 = false; + } + + if (spawn4) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX4); + if(mo) + { + mo->x += (P_Random()-128)<<12; + mo->y += (P_Random()-128)<<12; + mo->z += (P_Random()<<10); + mo->target = actor; + } + } + if (spawn5) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX5); + if(mo) + { + mo->x += (P_Random()-128)<<11; + mo->y += (P_Random()-128)<<11; + mo->z += (P_Random()<<10); + mo->target = actor; + } + } +} + + +void A_WraithLook(mobj_t *actor) +{ +// A_WraithFX4(actor); // too expensive + A_Look(actor); +} + + +void A_WraithChase(mobj_t *actor) +{ + int weaveindex = actor->special1; + actor->z += FloatBobOffsets[weaveindex]; + actor->special1 = (weaveindex+2)&63; +// if (actor->floorclip > 0) +// { +// P_SetMobjState(actor, S_WRAITH_RAISE2); +// return; +// } + A_Chase(actor); + A_WraithFX4(actor); +} + + + +//============================================================================ +// Ettin AI +//============================================================================ + +void A_EttinAttack(mobj_t *actor) +{ + if(P_CheckMeleeRange(actor)) + { + P_DamageMobj(actor->target, actor, actor, HITDICE(2)); + } +} + + +void A_DropMace(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, + actor->z+(actor->height>>1), MT_ETTIN_MACE); + if (mo) + { + mo->momx = (P_Random()-128)<<11; + mo->momy = (P_Random()-128)<<11; + mo->momz = FRACUNIT*10+(P_Random()<<10); + mo->target = actor; + } +} + + +//============================================================================ +// Fire Demon AI +// +// special1 index into floatbob +// special2 whether strafing or not +//============================================================================ + +void A_FiredSpawnRock(mobj_t *actor) +{ + mobj_t *mo; + int x,y,z; + int rtype; + + switch(P_Random()%5) + { + case 0: + rtype = MT_FIREDEMON_FX1; + break; + case 1: + rtype = MT_FIREDEMON_FX2; + break; + case 2: + rtype = MT_FIREDEMON_FX3; + break; + case 3: + rtype = MT_FIREDEMON_FX4; + break; + case 4: + rtype = MT_FIREDEMON_FX5; + break; + } + + x = actor->x + ((P_Random()-128) << 12); + y = actor->y + ((P_Random()-128) << 12); + z = actor->z + ((P_Random()) << 11); + mo = P_SpawnMobj(x,y,z,rtype); + if (mo) + { + mo->target = actor; + mo->momx = (P_Random()-128)<<10; + mo->momy = (P_Random()-128)<<10; + mo->momz = (P_Random()<<10); + mo->special1 = 2; // Number bounces + } + + // Initialize fire demon + actor->special2 = 0; + actor->flags &= ~MF_JUSTATTACKED; +} + +void A_FiredRocks(mobj_t *actor) +{ + A_FiredSpawnRock(actor); + A_FiredSpawnRock(actor); + A_FiredSpawnRock(actor); + A_FiredSpawnRock(actor); + A_FiredSpawnRock(actor); +} + +void A_FiredAttack(mobj_t *actor) +{ + mobj_t *mo; + mo = P_SpawnMissile(actor, actor->target, MT_FIREDEMON_FX6); + if (mo) S_StartSound(actor, SFX_FIRED_ATTACK); +} + +void A_SmBounce(mobj_t *actor) +{ + // give some more momentum (x,y,&z) + actor->z = actor->floorz + FRACUNIT; + actor->momz = (2*FRACUNIT) + (P_Random()<<10); + actor->momx = P_Random()%3<momy = P_Random()%3<special1; + mobj_t *target = actor->target; + angle_t ang; + fixed_t dist; + + if(actor->reactiontime) actor->reactiontime--; + if(actor->threshold) actor->threshold--; + + // Float up and down + actor->z += FloatBobOffsets[weaveindex]; + actor->special1 = (weaveindex+2)&63; + + // Insure it stays above certain height + if (actor->z < actor->floorz + (64*FRACUNIT)) + { + actor->z += 2*FRACUNIT; + } + + if(!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { // Invalid target + P_LookForPlayers(actor,true); + return; + } + + // Strafe + if (actor->special2 > 0) + { + actor->special2--; + } + else + { + actor->special2 = 0; + actor->momx = actor->momy = 0; + dist = P_AproxDistance(actor->x - target->x, actor->y - target->y); + if (dist < FIREDEMON_ATTACK_RANGE) + { + if (P_Random()<30) + { + ang = R_PointToAngle2(actor->x, actor->y, target->x, target->y); + if (P_Random()<128) + ang += ANGLE_90; + else + ang -= ANGLE_90; + ang>>=ANGLETOFINESHIFT; + actor->momx = FixedMul(8*FRACUNIT, finecosine[ang]); + actor->momy = FixedMul(8*FRACUNIT, finesine[ang]); + actor->special2 = 3; // strafe time + } + } + } + + FaceMovementDirection(actor); + + // Normal movement + if (!actor->special2) + { + if (--actor->movecount<0 || !P_Move (actor)) + { + P_NewChaseDir (actor); + } + } + + // Do missile attack + if (!(actor->flags&MF_JUSTATTACKED)) + { + if (P_CheckMissileRange(actor) && (P_Random()<20)) + { + P_SetMobjState (actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + } + else + { + actor->flags &= ~MF_JUSTATTACKED; + } + + // make active sound + if(actor->info->activesound && P_Random() < 3) + { + S_StartSound(actor, actor->info->activesound); + } +} + +void A_FiredSplotch(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH1); + if (mo) + { + mo->momx = (P_Random()-128)<<11; + mo->momy = (P_Random()-128)<<11; + mo->momz = FRACUNIT*3 + (P_Random()<<10); + } + mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH2); + if (mo) + { + mo->momx = (P_Random()-128)<<11; + mo->momy = (P_Random()-128)<<11; + mo->momz = FRACUNIT*3 + (P_Random()<<10); + } +} + + +//============================================================================ +// +// A_IceGuyLook +// +//============================================================================ + +void A_IceGuyLook(mobj_t *actor) +{ + fixed_t dist; + fixed_t an; + + A_Look(actor); + if(P_Random() < 64) + { + dist = ((P_Random()-128)*actor->radius)>>7; + an = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + + P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]), + actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT, + MT_ICEGUY_WISP1+(P_Random()&1)); + } +} + +//============================================================================ +// +// A_IceGuyChase +// +//============================================================================ + +void A_IceGuyChase(mobj_t *actor) +{ + fixed_t dist; + fixed_t an; + mobj_t *mo; + + A_Chase(actor); + if(P_Random() < 128) + { + dist = ((P_Random()-128)*actor->radius)>>7; + an = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + + mo = P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]), + actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT, + MT_ICEGUY_WISP1+(P_Random()&1)); + if(mo) + { + mo->momx = actor->momx; + mo->momy = actor->momy; + mo->momz = actor->momz; + mo->target = actor; + } + } +} + +//============================================================================ +// +// A_IceGuyAttack +// +//============================================================================ + +void A_IceGuyAttack(mobj_t *actor) +{ + fixed_t an; + + if(!actor->target) + { + return; + } + an = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1, + finecosine[an]), actor->y+FixedMul(actor->radius>>1, + finesine[an]), actor->z+40*FRACUNIT, actor, actor->target, + MT_ICEGUY_FX); + an = (actor->angle-ANG90)>>ANGLETOFINESHIFT; + P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1, + finecosine[an]), actor->y+FixedMul(actor->radius>>1, + finesine[an]), actor->z+40*FRACUNIT, actor, actor->target, + MT_ICEGUY_FX); + S_StartSound(actor, actor->info->attacksound); +} + +//============================================================================ +// +// A_IceGuyMissilePuff +// +//============================================================================ + +void A_IceGuyMissilePuff(mobj_t *actor) +{ + mobj_t *mo; + mo = P_SpawnMobj(actor->x, actor->y, actor->z+2*FRACUNIT, MT_ICEFX_PUFF); +} + +//============================================================================ +// +// A_IceGuyDie +// +//============================================================================ + +void A_IceGuyDie(mobj_t *actor) +{ + void A_FreezeDeathChunks(mobj_t *actor); + + actor->momx = 0; + actor->momy = 0; + actor->momz = 0; + actor->height <<= 2; + A_FreezeDeathChunks(actor); +} + +//============================================================================ +// +// A_IceGuyMissileExplode +// +//============================================================================ + +void A_IceGuyMissileExplode(mobj_t *actor) +{ + mobj_t *mo; + int i; + + for(i = 0; i < 8; i++) + { + mo = P_SpawnMissileAngle(actor, MT_ICEGUY_FX2, i*ANG45, -0.3*FRACUNIT); + if(mo) + { + mo->target = actor->target; + } + } +} + + + + + + + + + +//============================================================================ +// +// Sorcerer stuff +// +// Sorcerer Variables +// special1 Angle of ball 1 (all others relative to that) +// special2 which ball to stop at in stop mode (MT_???) +// args[0] Denfense time +// args[1] Number of full rotations since stopping mode +// args[2] Target orbit speed for acceleration/deceleration +// args[3] Movement mode (see SORC_ macros) +// args[4] Current ball orbit speed +// Sorcerer Ball Variables +// special1 Previous angle of ball (for woosh) +// special2 Countdown of rapid fire (FX4) +// args[0] If set, don't play the bounce sound when bouncing +//============================================================================ + +#define SORCBALL_INITIAL_SPEED 7 +#define SORCBALL_TERMINAL_SPEED 25 +#define SORCBALL_SPEED_ROTATIONS 5 +#define SORC_DEFENSE_TIME 255 +#define SORC_DEFENSE_HEIGHT 45 +#define BOUNCE_TIME_UNIT (35/2) +#define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds +#define SORCFX4_SPREAD_ANGLE 20 + +#define SORC_DECELERATE 0 +#define SORC_ACCELERATE 1 +#define SORC_STOPPING 2 +#define SORC_FIRESPELL 3 +#define SORC_STOPPED 4 +#define SORC_NORMAL 5 +#define SORC_FIRING_SPELL 6 + +#define BALL1_ANGLEOFFSET 0 +#define BALL2_ANGLEOFFSET (ANGLE_MAX/3) +#define BALL3_ANGLEOFFSET ((ANGLE_MAX/3)*2) + +void A_SorcBallOrbit(mobj_t *actor); +void A_SorcSpinBalls(mobj_t *actor); +void A_SpeedBalls(mobj_t *actor); +void A_SlowBalls(mobj_t *actor); +void A_StopBalls(mobj_t *actor); +void A_AccelBalls(mobj_t *actor); +void A_DecelBalls(mobj_t *actor); +void A_SorcBossAttack(mobj_t *actor); +void A_SpawnFizzle(mobj_t *actor); +void A_CastSorcererSpell(mobj_t *actor); +void A_SorcUpdateBallAngle(mobj_t *actor); +void A_BounceCheck(mobj_t *actor); +void A_SorcFX1Seek(mobj_t *actor); +void A_SorcOffense1(mobj_t *actor); +void A_SorcOffense2(mobj_t *actor); + + +// Spawn spinning balls above head - actor is sorcerer +void A_SorcSpinBalls(mobj_t *actor) +{ + mobj_t *mo; + fixed_t z; + + A_SlowBalls(actor); + actor->args[0] = 0; // Currently no defense + actor->args[3] = SORC_NORMAL; + actor->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed + actor->special1 = ANGLE_1; + z = actor->z - actor->floorclip + actor->info->height; + + mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL1); + if (mo) + { + mo->target = actor; + mo->special2 = SORCFX4_RAPIDFIRE_TIME; + } + mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL2); + if (mo) mo->target = actor; + mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL3); + if (mo) mo->target = actor; +} + + +// +// A_SorcBallOrbit() ========================================== +// + +void A_SorcBallOrbit(mobj_t *actor) +{ + int x,y; + angle_t angle, baseangle; + int mode = actor->target->args[3]; + mobj_t *parent = (mobj_t *)actor->target; + int dist = parent->radius - (actor->radius<<1); + angle_t prevangle = actor->special1; + + if (actor->target->health <= 0) + P_SetMobjState(actor, actor->info->painstate); + + baseangle = (angle_t)parent->special1; + switch(actor->type) + { + case MT_SORCBALL1: + angle = baseangle + BALL1_ANGLEOFFSET; + break; + case MT_SORCBALL2: + angle = baseangle + BALL2_ANGLEOFFSET; + break; + case MT_SORCBALL3: + angle = baseangle + BALL3_ANGLEOFFSET; + break; + default: + I_Error("corrupted sorcerer"); + break; + } + actor->angle = angle; + angle >>= ANGLETOFINESHIFT; + + switch(mode) + { + case SORC_NORMAL: // Balls rotating normally + A_SorcUpdateBallAngle(actor); + break; + case SORC_DECELERATE: // Balls decelerating + A_DecelBalls(actor); + A_SorcUpdateBallAngle(actor); + break; + case SORC_ACCELERATE: // Balls accelerating + A_AccelBalls(actor); + A_SorcUpdateBallAngle(actor); + break; + case SORC_STOPPING: // Balls stopping + if ((parent->special2 == actor->type) && + (parent->args[1] > SORCBALL_SPEED_ROTATIONS) && + (abs(angle - (parent->angle>>ANGLETOFINESHIFT)) < (30<<5))) + { + // Can stop now + actor->target->args[3] = SORC_FIRESPELL; + actor->target->args[4] = 0; + // Set angle so ball angle == sorcerer angle + switch(actor->type) + { + case MT_SORCBALL1: + parent->special1 = (int)(parent->angle - + BALL1_ANGLEOFFSET); + break; + case MT_SORCBALL2: + parent->special1 = (int)(parent->angle - + BALL2_ANGLEOFFSET); + break; + case MT_SORCBALL3: + parent->special1 = (int)(parent->angle - + BALL3_ANGLEOFFSET); + break; + default: + break; + } + } + else + { + A_SorcUpdateBallAngle(actor); + } + break; + case SORC_FIRESPELL: // Casting spell + if (parent->special2 == actor->type) + { + // Put sorcerer into special throw spell anim + if (parent->health > 0) + P_SetMobjStateNF(parent, S_SORC_ATTACK1); + + if (actor->type==MT_SORCBALL1 && P_Random()<200) + { + S_StartSound(NULL, SFX_SORCERER_SPELLCAST); + actor->special2 = SORCFX4_RAPIDFIRE_TIME; + actor->args[4] = 128; + parent->args[3] = SORC_FIRING_SPELL; + } + else + { + A_CastSorcererSpell(actor); + parent->args[3] = SORC_STOPPED; + } + } + break; + case SORC_FIRING_SPELL: + if (parent->special2 == actor->type) + { + if (actor->special2-- <= 0) + { + // Done rapid firing + parent->args[3] = SORC_STOPPED; + // Back to orbit balls + if (parent->health > 0) + P_SetMobjStateNF(parent, S_SORC_ATTACK4); + } + else + { + // Do rapid fire spell + A_SorcOffense2(actor); + } + } + break; + case SORC_STOPPED: // Balls stopped + default: + break; + } + + if ((angle < prevangle) && (parent->args[4]==SORCBALL_TERMINAL_SPEED)) + { + parent->args[1]++; // Bump rotation counter + // Completed full rotation - make woosh sound + S_StartSound(actor, SFX_SORCERER_BALLWOOSH); + } + actor->special1 = angle; // Set previous angle + x = parent->x + FixedMul(dist, finecosine[angle]); + y = parent->y + FixedMul(dist, finesine[angle]); + actor->x = x; + actor->y = y; + actor->z = parent->z - parent->floorclip + parent->info->height; +} + + +// +// Set balls to speed mode - actor is sorcerer +// +void A_SpeedBalls(mobj_t *actor) +{ + actor->args[3] = SORC_ACCELERATE; // speed mode + actor->args[2] = SORCBALL_TERMINAL_SPEED; // target speed +} + + +// +// Set balls to slow mode - actor is sorcerer +// +void A_SlowBalls(mobj_t *actor) +{ + actor->args[3] = SORC_DECELERATE; // slow mode + actor->args[2] = SORCBALL_INITIAL_SPEED; // target speed +} + + +// +// Instant stop when rotation gets to ball in special2 +// actor is sorcerer +// +void A_StopBalls(mobj_t *actor) +{ + int chance = P_Random(); + actor->args[3] = SORC_STOPPING; // stopping mode + actor->args[1] = 0; // Reset rotation counter + + if ((actor->args[0] <= 0) && (chance < 200)) + { + actor->special2 = MT_SORCBALL2; // Blue + } + else if((actor->health < (actor->info->spawnhealth >> 1)) && + (chance < 200)) + { + actor->special2 = MT_SORCBALL3; // Green + } + else + { + actor->special2 = MT_SORCBALL1; // Yellow + } + + +} + + +// +// Increase ball orbit speed - actor is ball +// +void A_AccelBalls(mobj_t *actor) +{ + mobj_t *sorc = actor->target; + + if (sorc->args[4] < sorc->args[2]) + { + sorc->args[4]++; + } + else + { + sorc->args[3] = SORC_NORMAL; + if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED) + { + // Reached terminal velocity - stop balls + A_StopBalls(sorc); + } + } +} + + +// Decrease ball orbit speed - actor is ball +void A_DecelBalls(mobj_t *actor) +{ + mobj_t *sorc = actor->target; + + if (sorc->args[4] > sorc->args[2]) + { + sorc->args[4]--; + } + else + { + sorc->args[3] = SORC_NORMAL; + } +} + + +// Update angle if first ball - actor is ball +void A_SorcUpdateBallAngle(mobj_t *actor) +{ + if (actor->type == MT_SORCBALL1) + { + actor->target->special1 += ANGLE_1*actor->target->args[4]; + } +} + + +// actor is ball +void A_CastSorcererSpell(mobj_t *actor) +{ + mobj_t *mo; + int spell = actor->type; + angle_t ang1,ang2; + fixed_t z; + mobj_t *parent = actor->target; + + S_StartSound(NULL, SFX_SORCERER_SPELLCAST); + + // Put sorcerer into throw spell animation + if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK4); + + switch(spell) + { + case MT_SORCBALL1: // Offensive + A_SorcOffense1(actor); + break; + case MT_SORCBALL2: // Defensive + z = parent->z - parent->floorclip + + SORC_DEFENSE_HEIGHT*FRACUNIT; + mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCFX2); + parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE; + parent->args[0] = SORC_DEFENSE_TIME; + if (mo) mo->target = parent; + break; + case MT_SORCBALL3: // Reinforcements + ang1 = actor->angle - ANGLE_45; + ang2 = actor->angle + ANGLE_45; + if(actor->health < (actor->info->spawnhealth/3)) + { // Spawn 2 at a time + mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT); + if (mo) mo->target = parent; + mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang2, 4*FRACUNIT); + if (mo) mo->target = parent; + } + else + { + if (P_Random() < 128) + ang1 = ang2; + mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT); + if (mo) mo->target = parent; + } + break; + default: + break; + } +} + +/* +void A_SpawnReinforcements(mobj_t *actor) +{ + mobj_t *parent = actor->target; + mobj_t *mo; + angle_t ang; + + ang = ANGLE_1 * P_Random(); + mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT); + if (mo) mo->target = parent; +} +*/ + +// actor is ball +void A_SorcOffense1(mobj_t *actor) +{ + mobj_t *mo; + angle_t ang1,ang2; + mobj_t *parent=(mobj_t *)actor->target; + + ang1 = actor->angle + ANGLE_1*70; + ang2 = actor->angle - ANGLE_1*70; + mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang1, 0); + if (mo) + { + mo->target = parent; + mo->special1 = (int)parent->target; + mo->args[4] = BOUNCE_TIME_UNIT; + mo->args[3] = 15; // Bounce time in seconds + } + mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang2, 0); + if (mo) + { + mo->target = parent; + mo->special1 = (int)parent->target; + mo->args[4] = BOUNCE_TIME_UNIT; + mo->args[3] = 15; // Bounce time in seconds + } +} + + +// Actor is ball +void A_SorcOffense2(mobj_t *actor) +{ + angle_t ang1; + mobj_t *mo; + int delta, index; + mobj_t *parent = actor->target; + mobj_t *dest = parent->target; + int dist; + + index = actor->args[4] << 5; + actor->args[4] += 15; + delta = (finesine[index])*SORCFX4_SPREAD_ANGLE; + delta = (delta>>FRACBITS)*ANGLE_1; + ang1 = actor->angle + delta; + mo = P_SpawnMissileAngle(parent, MT_SORCFX4, ang1, 0); + if (mo) + { + mo->special2 = 35*5/2; // 5 seconds + dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y); + dist = dist/mo->info->speed; + if(dist < 1) dist = 1; + mo->momz = (dest->z-mo->z)/dist; + } +} + + +// Resume ball spinning +void A_SorcBossAttack(mobj_t *actor) +{ + actor->args[3] = SORC_ACCELERATE; + actor->args[2] = SORCBALL_INITIAL_SPEED; +} + + +// spell cast magic fizzle +void A_SpawnFizzle(mobj_t *actor) +{ + fixed_t x,y,z; + fixed_t dist = 5*FRACUNIT; + angle_t angle = actor->angle >> ANGLETOFINESHIFT; + fixed_t speed = actor->info->speed; + angle_t rangle; + mobj_t *mo; + int ix; + + x = actor->x + FixedMul(dist,finecosine[angle]); + y = actor->y + FixedMul(dist,finesine[angle]); + z = actor->z - actor->floorclip + (actor->height>>1); + for (ix=0; ix<5; ix++) + { + mo = P_SpawnMobj(x,y,z,MT_SORCSPARK1); + if (mo) + { + rangle = angle + ((P_Random()%5) << 1); + mo->momx = FixedMul(P_Random()%speed,finecosine[rangle]); + mo->momy = FixedMul(P_Random()%speed,finesine[rangle]); + mo->momz = FRACUNIT*2; + } + } +} + + +//============================================================================ +// Yellow spell - offense +//============================================================================ + +void A_SorcFX1Seek(mobj_t *actor) +{ + A_BounceCheck(actor); + P_SeekerMissile(actor,ANGLE_1*2,ANGLE_1*6); +} + + +//============================================================================ +// Blue spell - defense +//============================================================================ +// +// FX2 Variables +// special1 current angle +// special2 +// args[0] 0 = CW, 1 = CCW +// args[1] +//============================================================================ + +// Split ball in two +void A_SorcFX2Split(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2); + if (mo) + { + mo->target = actor->target; + mo->args[0] = 0; // CW + mo->special1 = actor->angle; // Set angle + P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1); + } + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2); + if (mo) + { + mo->target = actor->target; + mo->args[0] = 1; // CCW + mo->special1 = actor->angle; // Set angle + P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1); + } + P_SetMobjStateNF(actor, S_NULL); +} + + +// Orbit FX2 about sorcerer +void A_SorcFX2Orbit(mobj_t *actor) +{ + angle_t angle; + fixed_t x,y,z; + mobj_t *parent = actor->target; + fixed_t dist = parent->info->radius; + + if ((parent->health <= 0) || // Sorcerer is dead + (!parent->args[0])) // Time expired + { + P_SetMobjStateNF(actor, actor->info->deathstate); + parent->args[0] = 0; + parent->flags2 &= ~MF2_REFLECTIVE; + parent->flags2 &= ~MF2_INVULNERABLE; + } + + if (actor->args[0] && (parent->args[0]-- <= 0)) // Time expired + { + P_SetMobjStateNF(actor, actor->info->deathstate); + parent->args[0] = 0; + parent->flags2 &= ~MF2_REFLECTIVE; + } + + // Move to new position based on angle + if (actor->args[0]) // Counter clock-wise + { + actor->special1 += ANGLE_1*10; + angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT; + x = parent->x + FixedMul(dist, finecosine[angle]); + y = parent->y + FixedMul(dist, finesine[angle]); + z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT; + z += FixedMul(15*FRACUNIT,finecosine[angle]); + // Spawn trailer + P_SpawnMobj(x,y,z, MT_SORCFX2_T1); + } + else // Clock wise + { + actor->special1 -= ANGLE_1*10; + angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT; + x = parent->x + FixedMul(dist, finecosine[angle]); + y = parent->y + FixedMul(dist, finesine[angle]); + z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT; + z += FixedMul(20*FRACUNIT,finesine[angle]); + // Spawn trailer + P_SpawnMobj(x,y,z, MT_SORCFX2_T1); + } + + actor->x = x; + actor->y = y; + actor->z = z; +} + + + +//============================================================================ +// Green spell - spawn bishops +//============================================================================ + +void A_SpawnBishop(mobj_t *actor) +{ + mobj_t *mo; + mo=P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOP); + if(mo) + { + if(!P_TestMobjLocation(mo)) + { + P_SetMobjState(mo, S_NULL); + } + } + P_SetMobjState(actor, S_NULL); +} + +/* +void A_SmokePuffEntry(mobj_t *actor) +{ + P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE); +} +*/ + +void A_SmokePuffExit(mobj_t *actor) +{ + P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKEEXIT); +} + +void A_SorcererBishopEntry(mobj_t *actor) +{ + P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX3_EXPLOSION); + S_StartSound(actor, actor->info->seesound); +} + + +//============================================================================ +// FX4 - rapid fire balls +//============================================================================ + +void A_SorcFX4Check(mobj_t *actor) +{ + if (actor->special2-- <= 0) + { + P_SetMobjStateNF(actor, actor->info->deathstate); + } +} + +//============================================================================ +// Ball death - spawn stuff +//============================================================================ + +void A_SorcBallPop(mobj_t *actor) +{ + S_StartSound(NULL, SFX_SORCERER_BALLPOP); + actor->flags &= ~MF_NOGRAVITY; + actor->flags2 |= MF2_LOGRAV; + actor->momx = ((P_Random()%10)-5) << FRACBITS; + actor->momy = ((P_Random()%10)-5) << FRACBITS; + actor->momz = (2+(P_Random()%3)) << FRACBITS; + actor->special2 = 4*FRACUNIT; // Initial bounce factor + actor->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit + actor->args[3] = 5; // Bounce time in seconds +} + + + +void A_BounceCheck(mobj_t *actor) +{ + if (actor->args[4]-- <= 0) + { + if (actor->args[3]-- <= 0) + { + P_SetMobjState(actor, actor->info->deathstate); + switch(actor->type) + { + case MT_SORCBALL1: + case MT_SORCBALL2: + case MT_SORCBALL3: + S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE); + break; + case MT_SORCFX1: + S_StartSound(NULL, SFX_SORCERER_HEADSCREAM); + break; + default: + break; + } + } + else + { + actor->args[4] = BOUNCE_TIME_UNIT; + } + } +} + + + + +//============================================================================ +// Class Bosses +//============================================================================ +#define CLASS_BOSS_STRAFE_RANGE 64*10*FRACUNIT + +void A_FastChase(mobj_t *actor) +{ + int delta; + fixed_t dist; + angle_t ang; + mobj_t *target; + + if(actor->reactiontime) + { + actor->reactiontime--; + } + + // Modify target threshold + if(actor->threshold) + { + actor->threshold--; + } + + if(gameskill == sk_nightmare) + { // Monsters move faster in nightmare mode + actor->tics -= actor->tics/2; + if(actor->tics < 3) + { + actor->tics = 3; + } + } + +// +// turn towards movement direction if not there yet +// + if(actor->movedir < 8) + { + actor->angle &= (7<<29); + delta = actor->angle-(actor->movedir << 29); + if(delta > 0) + { + actor->angle -= ANG90/2; + } + else if(delta < 0) + { + actor->angle += ANG90/2; + } + } + + if(!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { // look for a new target + if(P_LookForPlayers(actor, true)) + { // got a new target + return; + } + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + +// +// don't attack twice in a row +// + if(actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare) + P_NewChaseDir (actor); + return; + } + + // Strafe + if (actor->special2 > 0) + { + actor->special2--; + } + else + { + target = actor->target; + actor->special2 = 0; + actor->momx = actor->momy = 0; + dist=P_AproxDistance(actor->x - target->x, + actor->y - target->y); + if (dist < CLASS_BOSS_STRAFE_RANGE) + { + if (P_Random()<100) + { + ang = R_PointToAngle2(actor->x, actor->y, + target->x, target->y); + if (P_Random()<128) + ang += ANGLE_90; + else + ang -= ANGLE_90; + ang>>=ANGLETOFINESHIFT; + actor->momx = FixedMul(13*FRACUNIT, finecosine[ang]); + actor->momy = FixedMul(13*FRACUNIT, finesine[ang]); + actor->special2 = 3; // strafe time + } + } + } + +// +// check for missile attack +// + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare && actor->movecount) + goto nomissile; + if (!P_CheckMissileRange (actor)) + goto nomissile; + P_SetMobjState (actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } +nomissile: + +// +// possibly choose another target +// + if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) + { + if (P_LookForPlayers(actor,true)) + return; // got a new target + } + +// +// chase towards player +// + if (!actor->special2) + { + if (--actor->movecount<0 || !P_Move (actor)) + { + P_NewChaseDir (actor); + } + } +} + + +void A_FighterAttack(mobj_t *actor) +{ + extern void A_FSwordAttack2(mobj_t *actor); + + if(!actor->target) return; + A_FSwordAttack2(actor); +} + + +void A_ClericAttack(mobj_t *actor) +{ + extern void A_CHolyAttack3(mobj_t *actor); + + if(!actor->target) return; + A_CHolyAttack3(actor); +} + + + +void A_MageAttack(mobj_t *actor) +{ + extern void A_MStaffAttack2(mobj_t *actor); + + if(!actor->target) return; + A_MStaffAttack2(actor); +} + +void A_ClassBossHealth(mobj_t *actor) +{ + if (netgame && !deathmatch) // co-op only + { + if (!actor->special1) + { + actor->health *= 5; + actor->special1 = true; // has been initialized + } + } +} + + +//=========================================================================== +// +// A_CheckFloor - Checks if an object hit the floor +// +//=========================================================================== + +void A_CheckFloor(mobj_t *actor) +{ + if(actor->z <= actor->floorz) + { + actor->z = actor->floorz; + actor->flags2 &= ~MF2_LOGRAV; + P_SetMobjState(actor, actor->info->deathstate); + } +} + +//============================================================================ +// +// A_FreezeDeath +// +//============================================================================ + +void A_FreezeDeath(mobj_t *actor) +{ + actor->tics = 75+P_Random()+P_Random(); + actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD; + actor->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE; + actor->height <<= 2; + S_StartSound(actor, SFX_FREEZE_DEATH); + + if(actor->player) + { + actor->player->damagecount = 0; + actor->player->poisoncount = 0; + actor->player->bonuscount = 0; + if(actor->player == &players[consoleplayer]) + { + SB_PaletteFlash(false); + } + } + else if(actor->flags&MF_COUNTKILL && actor->special) + { // Initiate monster death actions + P_ExecuteLineSpecial(actor->special, actor->args, NULL, 0, actor); + } +} + +//============================================================================ +// +// A_IceSetTics +// +//============================================================================ + +void A_IceSetTics(mobj_t *actor) +{ + int floor; + + actor->tics = 70+(P_Random()&63); + floor = P_GetThingFloorType(actor); + if(floor == FLOOR_LAVA) + { + actor->tics >>= 2; + } + else if(floor == FLOOR_ICE) + { + actor->tics <<= 1; + } +} + +//============================================================================ +// +// A_IceCheckHeadDone +// +//============================================================================ + +void A_IceCheckHeadDone(mobj_t *actor) +{ + if(actor->special2 == 666) + { + P_SetMobjState(actor, S_ICECHUNK_HEAD2); + } +} + +//============================================================================ +// +// A_FreezeDeathChunks +// +//============================================================================ + +void A_FreezeDeathChunks(mobj_t *actor) +{ + int i; + mobj_t *mo; + + if(actor->momx || actor->momy || actor->momz) + { + actor->tics = 105; + return; + } + S_StartSound(actor, SFX_FREEZE_SHATTER); + + for(i = 12+(P_Random()&15); i >= 0; i--) + { + mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7), + actor->y+(((P_Random()-128)*actor->radius)>>7), + actor->z+(P_Random()*actor->height/255), MT_ICECHUNK); + P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3)); + if(mo) + { + mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2; + mo->momx = (P_Random()-P_Random())<<(FRACBITS-7); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-7); + A_IceSetTics(mo); // set a random tic wait + } + } + for(i = 12+(P_Random()&15); i >= 0; i--) + { + mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7), + actor->y+(((P_Random()-128)*actor->radius)>>7), + actor->z+(P_Random()*actor->height/255), MT_ICECHUNK); + P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3)); + if(mo) + { + mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2; + mo->momx = (P_Random()-P_Random())<<(FRACBITS-7); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-7); + A_IceSetTics(mo); // set a random tic wait + } + } + if(actor->player) + { // attach the player's view to a chunk of ice + mo = P_SpawnMobj(actor->x, actor->y, actor->z+VIEWHEIGHT, MT_ICECHUNK); + P_SetMobjState(mo, S_ICECHUNK_HEAD); + mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2; + mo->momx = (P_Random()-P_Random())<<(FRACBITS-7); + mo->momy = (P_Random()-P_Random())<<(FRACBITS-7); + mo->flags2 |= MF2_ICEDAMAGE; // used to force blue palette + mo->flags2 &= ~MF2_FLOORCLIP; + mo->player = actor->player; + actor->player = NULL; + mo->health = actor->health; + mo->angle = actor->angle; + mo->player->mo = mo; + mo->player->lookdir = 0; + } + P_RemoveMobjFromTIDList(actor); + P_SetMobjState(actor, S_FREETARGMOBJ); + actor->flags2 |= MF2_DONTDRAW; +} + +//=========================================================================== +// Korax Variables +// special1 last teleport destination +// special2 set if "below half" script not yet run +// +// Korax Scripts (reserved) +// 249 Tell scripts that we are below half health +// 250-254 Control scripts +// 255 Death script +// +// Korax TIDs (reserved) +// 245 Reserved for Korax himself +// 248 Initial teleport destination +// 249 Teleport destination +// 250-254 For use in respective control scripts +// 255 For use in death script (spawn spots) +//=========================================================================== +#define KORAX_SPIRIT_LIFETIME (5*(35/5)) // 5 seconds +#define KORAX_COMMAND_HEIGHT (120*FRACUNIT) +#define KORAX_COMMAND_OFFSET (27*FRACUNIT) + +void KoraxFire1(mobj_t *actor, int type); +void KoraxFire2(mobj_t *actor, int type); +void KoraxFire3(mobj_t *actor, int type); +void KoraxFire4(mobj_t *actor, int type); +void KoraxFire5(mobj_t *actor, int type); +void KoraxFire6(mobj_t *actor, int type); +void KSpiritInit(mobj_t *spirit, mobj_t *korax); + +#define KORAX_TID (245) +#define KORAX_FIRST_TELEPORT_TID (248) +#define KORAX_TELEPORT_TID (249) + +void A_KoraxChase(mobj_t *actor) +{ + mobj_t *spot; + int lastfound; + byte args[3]={0,0,0}; + + if ((!actor->special2) && + (actor->health <= (actor->info->spawnhealth/2))) + { + lastfound = 0; + spot = P_FindMobjFromTID(KORAX_FIRST_TELEPORT_TID, &lastfound); + if (spot) + { + P_Teleport(actor, spot->x, spot->y, spot->angle, true); + } + + P_StartACS(249, 0, args, actor, NULL, 0); + actor->special2 = 1; // Don't run again + + return; + } + + if (!actor->target) return; + if (P_Random()<30) + { + P_SetMobjState(actor, actor->info->missilestate); + } + else if (P_Random()<30) + { + S_StartSound(NULL, SFX_KORAX_ACTIVE); + } + + // Teleport away + if (actor->health < (actor->info->spawnhealth>>1)) + { + if (P_Random()<10) + { + lastfound = actor->special1; + spot = P_FindMobjFromTID(KORAX_TELEPORT_TID, &lastfound); + actor->special1 = lastfound; + if (spot) + { + P_Teleport(actor, spot->x, spot->y, spot->angle, true); + } + } + } +} + +void A_KoraxStep(mobj_t *actor) +{ + A_Chase(actor); +} + +void A_KoraxStep2(mobj_t *actor) +{ + S_StartSound(NULL, SFX_KORAX_STEP); + A_Chase(actor); +} + +void A_KoraxBonePop(mobj_t *actor) +{ + fixed_t x,y,z; + mobj_t *mo; + byte args[5]; + + args[0]=args[1]=args[2]=args[3]=args[4]=0; + x=actor->x, y=actor->y, z=actor->z; + + // Spawn 6 spirits equalangularly + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT1, ANGLE_60*0, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT2, ANGLE_60*1, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT3, ANGLE_60*2, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT4, ANGLE_60*3, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT5, ANGLE_60*4, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT6, ANGLE_60*5, 5*FRACUNIT); + if (mo) KSpiritInit(mo,actor); + + P_StartACS(255, 0, args, actor, NULL, 0); // Death script +} + +void KSpiritInit(mobj_t *spirit, mobj_t *korax) +{ + int i; + mobj_t *tail, *next; + + spirit->health = KORAX_SPIRIT_LIFETIME; + + spirit->special1 = (int)korax; // Swarm around korax + spirit->special2 = 32+(P_Random()&7); // Float bob index + spirit->args[0] = 10; // initial turn value + spirit->args[1] = 0; // initial look angle + + // Spawn a tail for spirit + tail = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL); + tail->special2 = (int)spirit; // parent + for(i = 1; i < 3; i++) + { + next = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL); + P_SetMobjState(next, next->info->spawnstate+1); + tail->special1 = (int)next; + tail = next; + } + tail->special1 = 0; // last tail bit +} + +void A_KoraxDecide(mobj_t *actor) +{ + if (P_Random()<220) + { + P_SetMobjState(actor, S_KORAX_MISSILE1); + } + else + { + P_SetMobjState(actor, S_KORAX_COMMAND1); + } +} + +void A_KoraxMissile(mobj_t *actor) +{ + int type = P_Random()%6; + int sound; + + S_StartSound(actor, SFX_KORAX_ATTACK); + + switch(type) + { + case 0: + type = MT_WRAITHFX1; + sound = SFX_WRAITH_MISSILE_FIRE; + break; + case 1: + type = MT_DEMONFX1; + sound = SFX_DEMON_MISSILE_FIRE; + break; + case 2: + type = MT_DEMON2FX1; + sound = SFX_DEMON_MISSILE_FIRE; + break; + case 3: + type = MT_FIREDEMON_FX6; + sound = SFX_FIRED_ATTACK; + break; + case 4: + type = MT_CENTAUR_FX; + sound = SFX_CENTAURLEADER_ATTACK; + break; + case 5: + type = MT_SERPENTFX; + sound = SFX_CENTAURLEADER_ATTACK; + break; + } + + // Fire all 6 missiles at once + S_StartSound(NULL, sound); + KoraxFire1(actor, type); + KoraxFire2(actor, type); + KoraxFire3(actor, type); + KoraxFire4(actor, type); + KoraxFire5(actor, type); + KoraxFire6(actor, type); +} + + +// Call action code scripts (250-254) +void A_KoraxCommand(mobj_t *actor) +{ + byte args[5]; + fixed_t x,y,z; + angle_t ang; + int numcommands; + + S_StartSound(actor, SFX_KORAX_COMMAND); + + // Shoot stream of lightning to ceiling + ang = (actor->angle - ANGLE_90) >> ANGLETOFINESHIFT; + x=actor->x + FixedMul(KORAX_COMMAND_OFFSET,finecosine[ang]); + y=actor->y + FixedMul(KORAX_COMMAND_OFFSET,finesine[ang]); + z=actor->z + KORAX_COMMAND_HEIGHT; + P_SpawnMobj(x,y,z, MT_KORAX_BOLT); + + args[0]=args[1]=args[2]=args[3]=args[4]=0; + + if (actor->health <= (actor->info->spawnhealth >> 1)) + { + numcommands = 5; + } + else + { + numcommands = 4; + } + + switch(P_Random() % numcommands) + { + case 0: + P_StartACS(250, 0, args, actor, NULL, 0); + break; + case 1: + P_StartACS(251, 0, args, actor, NULL, 0); + break; + case 2: + P_StartACS(252, 0, args, actor, NULL, 0); + break; + case 3: + P_StartACS(253, 0, args, actor, NULL, 0); + break; + case 4: + P_StartACS(254, 0, args, actor, NULL, 0); + break; + } +} + + +#define KORAX_DELTAANGLE (85*ANGLE_1) +#define KORAX_ARM_EXTENSION_SHORT (40*FRACUNIT) +#define KORAX_ARM_EXTENSION_LONG (55*FRACUNIT) + +#define KORAX_ARM1_HEIGHT (108*FRACUNIT) +#define KORAX_ARM2_HEIGHT (82*FRACUNIT) +#define KORAX_ARM3_HEIGHT (54*FRACUNIT) +#define KORAX_ARM4_HEIGHT (104*FRACUNIT) +#define KORAX_ARM5_HEIGHT (86*FRACUNIT) +#define KORAX_ARM6_HEIGHT (53*FRACUNIT) + + +// Arm projectiles +// arm positions numbered: +// 1 top left +// 2 middle left +// 3 lower left +// 4 top right +// 5 middle right +// 6 lower right + + +// Arm 1 projectile +void KoraxFire1(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM1_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + + +// Arm 2 projectile +void KoraxFire2(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM2_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + +// Arm 3 projectile +void KoraxFire3(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM3_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + +// Arm 4 projectile +void KoraxFire4(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM4_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + +// Arm 5 projectile +void KoraxFire5(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM5_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + +// Arm 6 projectile +void KoraxFire6(mobj_t *actor, int type) +{ + mobj_t *mo; + angle_t ang; + fixed_t x,y,z; + + ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; + x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); + y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); + z = actor->z - actor->floorclip + KORAX_ARM6_HEIGHT; + mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type); +} + + +void A_KSpiritWeave(mobj_t *actor) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + + weaveXY = actor->special2>>16; + weaveZ = actor->special2&0xFFFF; + angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + newX = actor->x-FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY = actor->y-FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + weaveXY = (weaveXY+(P_Random()%5))&63; + newX += FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY += FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + P_TryMove(actor, newX, newY); + actor->z -= FloatBobOffsets[weaveZ]<<1; + weaveZ = (weaveZ+(P_Random()%5))&63; + actor->z += FloatBobOffsets[weaveZ]<<1; + actor->special2 = weaveZ+(weaveXY<<16); +} + +void A_KSpiritSeeker(mobj_t *actor, angle_t thresh, angle_t turnMax) +{ + int dir; + int dist; + angle_t delta; + angle_t angle; + mobj_t *target; + fixed_t newZ; + fixed_t deltaZ; + + target = (mobj_t *)actor->special1; + if(target == NULL) + { + return; + } + dir = P_FaceMobj(actor, target, &delta); + if(delta > thresh) + { + delta >>= 1; + if(delta > turnMax) + { + delta = turnMax; + } + } + if(dir) + { // Turn clockwise + actor->angle += delta; + } + else + { // Turn counter clockwise + actor->angle -= delta; + } + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[angle]); + actor->momy = FixedMul(actor->info->speed, finesine[angle]); + + if(!(leveltime&15) + || actor->z > target->z+(target->info->height) + || actor->z+actor->height < target->z) + { + newZ = target->z+((P_Random()*target->info->height)>>8); + deltaZ = newZ-actor->z; + if(abs(deltaZ) > 15*FRACUNIT) + { + if(deltaZ > 0) + { + deltaZ = 15*FRACUNIT; + } + else + { + deltaZ = -15*FRACUNIT; + } + } + dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); + dist = dist/actor->info->speed; + if(dist < 1) + { + dist = 1; + } + actor->momz = deltaZ/dist; + } + return; +} + + +void A_KSpiritRoam(mobj_t *actor) +{ + if (actor->health-- <= 0) + { + S_StartSound(actor, SFX_SPIRIT_DIE); + P_SetMobjState(actor, S_KSPIRIT_DEATH1); + } + else + { + if (actor->special1) + { + A_KSpiritSeeker(actor, actor->args[0]*ANGLE_1, + actor->args[0]*ANGLE_1*2); + } + A_KSpiritWeave(actor); + if (P_Random()<50) + { + S_StartSound(NULL, SFX_SPIRIT_ACTIVE); + } + } +} + +void A_KBolt(mobj_t *actor) +{ + // Countdown lifetime + if (actor->special1-- <= 0) + { + P_SetMobjState(actor, S_NULL); + } +} + + +#define KORAX_BOLT_HEIGHT 48*FRACUNIT +#define KORAX_BOLT_LIFETIME 3 + +void A_KBoltRaise(mobj_t *actor) +{ + mobj_t *mo; + fixed_t z; + + // Spawn a child upward + z = actor->z + KORAX_BOLT_HEIGHT; + + if ((z + KORAX_BOLT_HEIGHT) < actor->ceilingz) + { + mo = P_SpawnMobj(actor->x, actor->y, z, MT_KORAX_BOLT); + if (mo) + { + mo->special1 = KORAX_BOLT_LIFETIME; + } + } + else + { + // Maybe cap it off here + } +} + diff --git a/src/hexen/p_floor.c b/src/hexen/p_floor.c new file mode 100644 index 00000000..41b1575a --- /dev/null +++ b/src/hexen/p_floor.c @@ -0,0 +1,931 @@ + +//************************************************************************** +//** +//** p_floor.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_floor.c,v $ +//** $Revision: 1.23 $ +//** $Date: 95/10/06 16:53:19 $ +//** $Author: paul $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +extern fixed_t FloatBobOffsets[64]; + +//================================================================== +//================================================================== +// +// FLOORS +// +//================================================================== +//================================================================== + +//================================================================== +// +// Move a plane (floor or ceiling) and check for crushing +// +//================================================================== +result_e T_MovePlane(sector_t *sector,fixed_t speed, + fixed_t dest, int crush,int floorOrCeiling,int direction) +{ + boolean flag; + fixed_t lastpos; + + switch(floorOrCeiling) + { + case 0: // FLOOR + switch(direction) + { + case -1: // DOWN + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + sector->floorheight =lastpos; + P_ChangeSector(sector,crush); + //return RES_CRUSHED; + } + return RES_PASTDEST; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + return RES_CRUSHED; + } + } + break; + + case 1: // UP + if (sector->floorheight + speed > dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + //return RES_CRUSHED; + } + return RES_PASTDEST; + } + else // COULD GET CRUSHED + { + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + //if (crush == true) + //{ + // return RES_CRUSHED; + //} + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + return RES_CRUSHED; + } + } + break; + } + break; + + case 1: // CEILING + switch(direction) + { + case -1: // DOWN + if (sector->ceilingheight - speed < dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector,crush); + //return RES_CRUSHED; + } + return RES_PASTDEST; + } + else // COULD GET CRUSHED + { + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + //if (crush == true) + //{ + // return RES_CRUSHED; + //} + sector->ceilingheight = lastpos; + P_ChangeSector(sector,crush); + return RES_CRUSHED; + } + } + break; + + case 1: // UP + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_ChangeSector(sector,crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector,crush); + //return RES_CRUSHED; + } + return RES_PASTDEST; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_ChangeSector(sector,crush); + #if 0 + if (flag == true) + { + sector->ceilingheight = lastpos; + P_ChangeSector(sector,crush); + return RES_CRUSHED; + } + #endif + } + break; + } + break; + + } + return RES_OK; +} + +//================================================================== +// +// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) +// +//================================================================== +void T_MoveFloor(floormove_t *floor) +{ + result_e res; + + if(floor->resetDelayCount) + { + floor->resetDelayCount--; + if(!floor->resetDelayCount) + { + floor->floordestheight = floor->resetHeight; + floor->direction = -floor->direction; + floor->resetDelay = 0; + floor->delayCount = 0; + floor->delayTotal = 0; + } + } + if(floor->delayCount) + { + floor->delayCount--; + if(!floor->delayCount && floor->textureChange) + { + floor->sector->floorpic += floor->textureChange; + } + return; + } + + res = T_MovePlane(floor->sector,floor->speed, + floor->floordestheight,floor->crush,0,floor->direction); + + if(floor->type == FLEV_RAISEBUILDSTEP) + { + if((floor->direction == 1 && floor->sector->floorheight >= + floor->stairsDelayHeight) || (floor->direction == -1 && + floor->sector->floorheight <= floor->stairsDelayHeight)) + { + floor->delayCount = floor->delayTotal; + floor->stairsDelayHeight += floor->stairsDelayHeightDelta; + } + } + if (res == RES_PASTDEST) + { + SN_StopSequence((mobj_t *)&floor->sector->soundorg); + if(floor->delayTotal) + { + floor->delayTotal = 0; + } + if(floor->resetDelay) + { +// floor->resetDelayCount = floor->resetDelay; +// floor->resetDelay = 0; + return; + } + floor->sector->specialdata = NULL; + /* + if (floor->direction == 1) + switch(floor->type) + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + else if (floor->direction == -1) + switch(floor->type) + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + */ + if(floor->textureChange) + { + floor->sector->floorpic -= floor->textureChange; + } + P_TagFinished(floor->sector->tag); + P_RemoveThinker(&floor->thinker); + } +} + +//================================================================== +// +// HANDLE FLOOR TYPES +// +//================================================================== +int EV_DoFloor(line_t *line, byte *args, floor_e floortype) +{ + int secnum; + int rtn; + sector_t *sec; + floormove_t *floor=NULL; + + secnum = -1; + rtn = 0; + while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + // + // new floor thinker + // + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->specialdata = floor; + floor->thinker.function = T_MoveFloor; + floor->type = floortype; + floor->crush = 0; + floor->speed = args[1]*(FRACUNIT/8); + if(floortype == FLEV_LOWERTIMES8INSTANT || + floortype == FLEV_RAISETIMES8INSTANT) + { + floor->speed = 2000<direction = -1; + floor->sector = sec; + floor->floordestheight = + P_FindHighestFloorSurrounding(sec); + break; + case FLEV_LOWERFLOORTOLOWEST: + floor->direction = -1; + floor->sector = sec; + floor->floordestheight = + P_FindLowestFloorSurrounding(sec); + break; + case FLEV_LOWERFLOORBYVALUE: + floor->direction = -1; + floor->sector = sec; + floor->floordestheight = floor->sector->floorheight- + args[2]*FRACUNIT; + break; + case FLEV_LOWERTIMES8INSTANT: + case FLEV_LOWERBYVALUETIMES8: + floor->direction = -1; + floor->sector = sec; + floor->floordestheight = floor->sector->floorheight- + args[2]*FRACUNIT*8; + break; + case FLEV_RAISEFLOORCRUSH: + floor->crush = args[2]; // arg[2] = crushing value + floor->direction = 1; + floor->sector = sec; + floor->floordestheight = sec->ceilingheight-8*FRACUNIT; + break; + case FLEV_RAISEFLOOR: + floor->direction = 1; + floor->sector = sec; + floor->floordestheight = + P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + break; + case FLEV_RAISEFLOORTONEAREST: + floor->direction = 1; + floor->sector = sec; + floor->floordestheight = + P_FindNextHighestFloor(sec,sec->floorheight); + break; + case FLEV_RAISEFLOORBYVALUE: + floor->direction = 1; + floor->sector = sec; + floor->floordestheight = floor->sector->floorheight+ + args[2]*FRACUNIT; + break; + case FLEV_RAISETIMES8INSTANT: + case FLEV_RAISEBYVALUETIMES8: + floor->direction = 1; + floor->sector = sec; + floor->floordestheight = floor->sector->floorheight+ + args[2]*FRACUNIT*8; + break; + case FLEV_MOVETOVALUETIMES8: + floor->sector = sec; + floor->floordestheight = args[2]*FRACUNIT*8; + if(args[3]) + { + floor->floordestheight = -floor->floordestheight; + } + if(floor->floordestheight > floor->sector->floorheight) + { + floor->direction = 1; + } + else if(floor->floordestheight < floor->sector->floorheight) + { + floor->direction = -1; + } + else + { // already at lowest position + rtn = 0; + } + break; + default: + rtn = 0; + break; + } + } + if(rtn) + { + SN_StartSequence((mobj_t *)&floor->sector->soundorg, + SEQ_PLATFORM+floor->sector->seqType); + } + return rtn; +} + +//============================================================================ +// +// EV_DoFloorAndCeiling +// +//============================================================================ + +int EV_DoFloorAndCeiling(line_t *line, byte *args, boolean raise) +{ + boolean floor, ceiling; + int secnum; + sector_t *sec; + + if(raise) + { + floor = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE); + secnum = -1; + while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + sec->specialdata = NULL; + } + ceiling = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE); + } + else + { + floor = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE); + secnum = -1; + while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + sec->specialdata = NULL; + } + ceiling = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE); + } + return (floor|ceiling); +} + +// ===== Build Stairs Private Data ===== + +#define STAIR_SECTOR_TYPE 26 +#define STAIR_QUEUE_SIZE 32 + +struct +{ + sector_t *sector; + int type; + int height; +} StairQueue[STAIR_QUEUE_SIZE]; + +static int QueueHead; +static int QueueTail; + +static int StepDelta; +static int Direction; +static int Speed; +static int Texture; +static int StartDelay; +static int StartDelayDelta; +static int TextureChange; +static int StartHeight; + +//========================================================================== +// +// QueueStairSector +// +//========================================================================== + +static void QueueStairSector(sector_t *sec, int type, int height) +{ + if((QueueTail+1)%STAIR_QUEUE_SIZE == QueueHead) + { + I_Error("BuildStairs: Too many branches located.\n"); + } + StairQueue[QueueTail].sector = sec; + StairQueue[QueueTail].type = type; + StairQueue[QueueTail].height = height; + + QueueTail = (QueueTail+1)%STAIR_QUEUE_SIZE; +} + +//========================================================================== +// +// DequeueStairSector +// +//========================================================================== + +static sector_t *DequeueStairSector(int *type, int *height) +{ + sector_t *sec; + + if(QueueHead == QueueTail) + { // queue is empty + return NULL; + } + *type = StairQueue[QueueHead].type; + *height = StairQueue[QueueHead].height; + sec = StairQueue[QueueHead].sector; + QueueHead = (QueueHead+1)%STAIR_QUEUE_SIZE; + + return sec; +} + +//========================================================================== +// +// ProcessStairSector +// +//========================================================================== + +static void ProcessStairSector(sector_t *sec, int type, int height, + stairs_e stairsType, int delay, int resetDelay) +{ + int i; + sector_t *tsec; + floormove_t *floor; + + // + // new floor thinker + // + height += StepDelta; + floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function = T_MoveFloor; + floor->type = FLEV_RAISEBUILDSTEP; + floor->direction = Direction; + floor->sector = sec; + floor->floordestheight = height; + switch(stairsType) + { + case STAIRS_NORMAL: + floor->speed = Speed; + if(delay) + { + floor->delayTotal = delay; + floor->stairsDelayHeight = sec->floorheight+StepDelta; + floor->stairsDelayHeightDelta = StepDelta; + } + floor->resetDelay = resetDelay; + floor->resetDelayCount = resetDelay; + floor->resetHeight = sec->floorheight; + break; + case STAIRS_SYNC: + floor->speed = FixedMul(Speed, FixedDiv(height-StartHeight, + StepDelta)); + floor->resetDelay = delay; //arg4 + floor->resetDelayCount = delay; + floor->resetHeight = sec->floorheight; + break; +/* + case STAIRS_PHASED: + floor->floordestheight = sec->floorheight+StepDelta; + floor->speed = Speed; + floor->delayCount = StartDelay; + StartDelay += StartDelayDelta; + floor->textureChange = TextureChange; + floor->resetDelayCount = StartDelay; + break; +*/ + default: + break; + } + SN_StartSequence((mobj_t *)&sec->soundorg, SEQ_PLATFORM+sec->seqType); + // + // Find next sector to raise + // Find nearby sector with sector special equal to type + // + for (i = 0; i < sec->linecount; i++) + { + if(!((sec->lines[i])->flags&ML_TWOSIDED)) + { + continue; + } + tsec = (sec->lines[i])->frontsector; + if(tsec->special == type+STAIR_SECTOR_TYPE && !tsec->specialdata + && tsec->floorpic == Texture && tsec->validcount != validcount) + { + QueueStairSector(tsec, type^1, height); + tsec->validcount = validcount; + //tsec->special = 0; + } + tsec = (sec->lines[i])->backsector; + if(tsec->special == type+STAIR_SECTOR_TYPE && !tsec->specialdata + && tsec->floorpic == Texture && tsec->validcount != validcount) + { + QueueStairSector(tsec, type^1, height); + tsec->validcount = validcount; + //tsec->special = 0; + } + } +} + +//================================================================== +// +// BUILD A STAIRCASE! +// +// Direction is either positive or negative, denoting build stairs +// up or down. +//================================================================== + +int EV_BuildStairs(line_t *line, byte *args, int direction, + stairs_e stairsType) +{ + int secnum; + int height; + int delay; + int resetDelay; + sector_t *sec; + sector_t *qSec; + int type; + + // Set global stairs variables + TextureChange = 0; + Direction = direction; + StepDelta = Direction*(args[2]*FRACUNIT); + Speed = args[1]*(FRACUNIT/8); + resetDelay = args[4]; + delay = args[3]; + if(stairsType == STAIRS_PHASED) + { + StartDelayDelta = args[3]; + StartDelay = StartDelayDelta; + resetDelay = StartDelayDelta; + delay = 0; + TextureChange = args[4]; + } + + secnum = -1; + + validcount++; + while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + + Texture = sec->floorpic; + StartHeight = sec->floorheight; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec->specialdata) + continue; + + QueueStairSector(sec, 0, sec->floorheight); + sec->special = 0; + } + while((qSec = DequeueStairSector(&type, &height)) != NULL) + { + ProcessStairSector(qSec, type, height, stairsType, delay, resetDelay); + } + return(1); +} + +//========================================================================= +// +// T_BuildPillar +// +//========================================================================= + +void T_BuildPillar(pillar_t *pillar) +{ + result_e res1; + result_e res2; + + // First, raise the floor + res1 = T_MovePlane(pillar->sector, pillar->floorSpeed, pillar->floordest, + pillar->crush, 0, pillar->direction); // floorOrCeiling, direction + // Then, lower the ceiling + res2 = T_MovePlane(pillar->sector, pillar->ceilingSpeed, + pillar->ceilingdest, pillar->crush, 1, -pillar->direction); + if (res1 == RES_PASTDEST && res2 == RES_PASTDEST) + { + pillar->sector->specialdata = NULL; + SN_StopSequence((mobj_t *)&pillar->sector->soundorg); + P_TagFinished(pillar->sector->tag); + P_RemoveThinker(&pillar->thinker); + } +} + +//========================================================================= +// +// EV_BuildPillar +// +//========================================================================= + +int EV_BuildPillar(line_t *line, byte *args, boolean crush) +{ + int secnum; + sector_t *sec; + pillar_t *pillar; + int newHeight; + int rtn; + + rtn = 0; + secnum = -1; + while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + if(sec->specialdata) + continue; // already moving + if(sec->floorheight == sec->ceilingheight) + { // pillar is already closed + continue; + } + rtn = 1; + if(!args[2]) + { + newHeight = sec->floorheight+ + ((sec->ceilingheight-sec->floorheight)/2); + } + else + { + newHeight = sec->floorheight+(args[2]<specialdata = pillar; + P_AddThinker(&pillar->thinker); + pillar->thinker.function = T_BuildPillar; + pillar->sector = sec; + if(!args[2]) + { + pillar->ceilingSpeed = pillar->floorSpeed = args[1]*(FRACUNIT/8); + } + else if(newHeight-sec->floorheight > sec->ceilingheight-newHeight) + { + pillar->floorSpeed = args[1]*(FRACUNIT/8); + pillar->ceilingSpeed = FixedMul(sec->ceilingheight-newHeight, + FixedDiv(pillar->floorSpeed, newHeight-sec->floorheight)); + } + else + { + pillar->ceilingSpeed = args[1]*(FRACUNIT/8); + pillar->floorSpeed = FixedMul(newHeight-sec->floorheight, + FixedDiv(pillar->ceilingSpeed, sec->ceilingheight-newHeight)); + } + pillar->floordest = newHeight; + pillar->ceilingdest = newHeight; + pillar->direction = 1; + pillar->crush = crush*args[3]; + SN_StartSequence((mobj_t *)&pillar->sector->soundorg, + SEQ_PLATFORM+pillar->sector->seqType); + } + return rtn; +} + +//========================================================================= +// +// EV_OpenPillar +// +//========================================================================= + +int EV_OpenPillar(line_t *line, byte *args) +{ + int secnum; + sector_t *sec; + pillar_t *pillar; + int rtn; + + rtn = 0; + secnum = -1; + while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + if(sec->specialdata) + continue; // already moving + if(sec->floorheight != sec->ceilingheight) + { // pillar isn't closed + continue; + } + rtn = 1; + pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0); + sec->specialdata = pillar; + P_AddThinker(&pillar->thinker); + pillar->thinker.function = T_BuildPillar; + pillar->sector = sec; + if(!args[2]) + { + pillar->floordest = P_FindLowestFloorSurrounding(sec); + } + else + { + pillar->floordest = sec->floorheight-(args[2]<ceilingdest = P_FindHighestCeilingSurrounding(sec); + } + else + { + pillar->ceilingdest = sec->ceilingheight+(args[3]<floorheight-pillar->floordest >= pillar->ceilingdest- + sec->ceilingheight) + { + pillar->floorSpeed = args[1]*(FRACUNIT/8); + pillar->ceilingSpeed = FixedMul(sec->ceilingheight- + pillar->ceilingdest, FixedDiv(pillar->floorSpeed, + pillar->floordest-sec->floorheight)); + } + else + { + pillar->ceilingSpeed = args[1]*(FRACUNIT/8); + pillar->floorSpeed = FixedMul(pillar->floordest-sec->floorheight, + FixedDiv(pillar->ceilingSpeed, sec->ceilingheight- + pillar->ceilingdest)); + } + pillar->direction = -1; // open the pillar + SN_StartSequence((mobj_t *)&pillar->sector->soundorg, + SEQ_PLATFORM+pillar->sector->seqType); + } + return rtn; +} + +//========================================================================= +// +// EV_FloorCrushStop +// +//========================================================================= + +int EV_FloorCrushStop(line_t *line, byte *args) +{ + thinker_t *think; + floormove_t *floor; + boolean rtn; + + rtn = 0; + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != T_MoveFloor) + { + continue; + } + floor = (floormove_t *)think; + if(floor->type != FLEV_RAISEFLOORCRUSH) + { + continue; + } + // Completely remove the crushing floor + SN_StopSequence((mobj_t *)&floor->sector->soundorg); + floor->sector->specialdata = NULL; + P_TagFinished(floor->sector->tag); + P_RemoveThinker(&floor->thinker); + rtn = 1; + } + return rtn; +} + +//========================================================================== +// +// T_FloorWaggle +// +//========================================================================== + +#define WGLSTATE_EXPAND 1 +#define WGLSTATE_STABLE 2 +#define WGLSTATE_REDUCE 3 + +void T_FloorWaggle(floorWaggle_t *waggle) +{ + switch(waggle->state) + { + case WGLSTATE_EXPAND: + if((waggle->scale += waggle->scaleDelta) + >= waggle->targetScale) + { + waggle->scale = waggle->targetScale; + waggle->state = WGLSTATE_STABLE; + } + break; + case WGLSTATE_REDUCE: + if((waggle->scale -= waggle->scaleDelta) <= 0) + { // Remove + waggle->sector->floorheight = waggle->originalHeight; + P_ChangeSector(waggle->sector, true); + waggle->sector->specialdata = NULL; + P_TagFinished(waggle->sector->tag); + P_RemoveThinker(&waggle->thinker); + return; + } + break; + case WGLSTATE_STABLE: + if(waggle->ticker != -1) + { + if(!--waggle->ticker) + { + waggle->state = WGLSTATE_REDUCE; + } + } + break; + } + waggle->accumulator += waggle->accDelta; + waggle->sector->floorheight = waggle->originalHeight + +FixedMul(FloatBobOffsets[(waggle->accumulator>>FRACBITS)&63], + waggle->scale); + P_ChangeSector(waggle->sector, true); +} + +//========================================================================== +// +// EV_StartFloorWaggle +// +//========================================================================== + +boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset, + int timer) +{ + int sectorIndex; + sector_t *sector; + floorWaggle_t *waggle; + boolean retCode; + + retCode = false; + sectorIndex = -1; + while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) + { + sector = §ors[sectorIndex]; + if(sector->specialdata) + { // Already busy with another thinker + continue; + } + retCode = true; + waggle = Z_Malloc(sizeof(*waggle), PU_LEVSPEC, 0); + sector->specialdata = waggle; + waggle->thinker.function = T_FloorWaggle; + waggle->sector = sector; + waggle->originalHeight = sector->floorheight; + waggle->accumulator = offset*FRACUNIT; + waggle->accDelta = speed<<10; + waggle->scale = 0; + waggle->targetScale = height<<10; + waggle->scaleDelta = waggle->targetScale + /(35+((3*35)*height)/255); + waggle->ticker = timer ? timer*35 : -1; + waggle->state = WGLSTATE_EXPAND; + P_AddThinker(&waggle->thinker); + } + return retCode; +} diff --git a/src/hexen/p_inter.c b/src/hexen/p_inter.c new file mode 100644 index 00000000..b9e904fa --- /dev/null +++ b/src/hexen/p_inter.c @@ -0,0 +1,2252 @@ + +//************************************************************************** +//** +//** p_inter.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_inter.c,v $ +//** $Revision: 1.145 $ +//** $Date: 96/01/16 10:35:33 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +#define BONUSADD 6 + +int ArmorIncrement[NUMCLASSES][NUMARMOR] = +{ + { 25*FRACUNIT, 20*FRACUNIT, 15*FRACUNIT, 5*FRACUNIT }, + { 10*FRACUNIT, 25*FRACUNIT, 5*FRACUNIT, 20*FRACUNIT }, + { 5*FRACUNIT, 15*FRACUNIT, 10*FRACUNIT, 25*FRACUNIT }, + { 0, 0, 0, 0 } +}; + +int AutoArmorSave[NUMCLASSES] = { 15*FRACUNIT, 10*FRACUNIT, 5*FRACUNIT, 0 }; + +char *TextKeyMessages[] = +{ + TXT_KEY_STEEL, + TXT_KEY_CAVE, + TXT_KEY_AXE, + TXT_KEY_FIRE, + TXT_KEY_EMERALD, + TXT_KEY_DUNGEON, + TXT_KEY_SILVER, + TXT_KEY_RUSTED, + TXT_KEY_HORN, + TXT_KEY_SWAMP, + TXT_KEY_CASTLE +}; + +static void SetDormantArtifact(mobj_t *arti); +static void TryPickupArtifact(player_t *player, artitype_t artifactType, + mobj_t *artifact); +static void TryPickupWeapon(player_t *player, pclass_t weaponClass, + weapontype_t weaponType, mobj_t *weapon, char *message); +static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass, + int pieceValue, mobj_t *pieceMobj); + +#ifdef __NeXT__ +extern void strupr(char *s); +#endif + +//-------------------------------------------------------------------------- +// +// PROC P_SetMessage +// +//-------------------------------------------------------------------------- + +void P_SetMessage(player_t *player, char *message, boolean ultmsg) +{ + extern boolean messageson; + + if((player->ultimateMessage || !messageson) && !ultmsg) + { + return; + } + if(strlen(message) > 79) + { + memcpy(player->message, message, 80); + player->message[80] = 0; + } + else + { + strcpy(player->message, message); + } + strupr(player->message); + player->messageTics = MESSAGETICS; + player->yellowMessage = false; + if(ultmsg) + { + player->ultimateMessage = true; + } + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//========================================================================== +// +// P_SetYellowMessage +// +//========================================================================== + +void P_SetYellowMessage(player_t *player, char *message, boolean ultmsg) +{ + extern boolean messageson; + + if((player->ultimateMessage || !messageson) && !ultmsg) + { + return; + } + if(strlen(message) > 79) + { + memcpy(player->message, message, 80); + player->message[80] = 0; + } + else + { + strcpy(player->message, message); + } + player->messageTics = 5*MESSAGETICS; // Bold messages last longer + player->yellowMessage = true; + if(ultmsg) + { + player->ultimateMessage = true; + } + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//========================================================================== +// +// P_ClearMessage +// +//========================================================================== + +void P_ClearMessage(player_t *player) +{ + player->messageTics = 0; + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_HideSpecialThing +// +//---------------------------------------------------------------------------- + +void P_HideSpecialThing(mobj_t *thing) +{ + thing->flags &= ~MF_SPECIAL; + thing->flags2 |= MF2_DONTDRAW; + P_SetMobjState(thing, S_HIDESPECIAL1); +} + +//-------------------------------------------------------------------------- +// +// FUNC P_GiveMana +// +// Returns true if the player accepted the mana, false if it was +// refused (player has MAX_MANA). +// +//-------------------------------------------------------------------------- + +boolean P_GiveMana(player_t *player, manatype_t mana, int count) +{ + int prevMana; + //weapontype_t changeWeapon; + + if(mana == MANA_NONE || mana == MANA_BOTH) + { + return(false); + } + if(mana < 0 || mana > NUMMANA) + { + I_Error("P_GiveMana: bad type %i", mana); + } + if(player->mana[mana] == MAX_MANA) + { + return(false); + } + if(gameskill == sk_baby || gameskill == sk_nightmare) + { // extra mana in baby mode and nightmare mode + count += count>>1; + } + prevMana = player->mana[mana]; + + player->mana[mana] += count; + if(player->mana[mana] > MAX_MANA) + { + player->mana[mana] = MAX_MANA; + } + if(player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND + && mana == MANA_1 && prevMana <= 0) + { + P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); + } + return(true); +} + +//========================================================================== +// +// TryPickupWeapon +// +//========================================================================== + +static void TryPickupWeapon(player_t *player, pclass_t weaponClass, + weapontype_t weaponType, mobj_t *weapon, char *message) +{ + boolean remove; + boolean gaveMana; + boolean gaveWeapon; + + remove = true; + if(player->class != weaponClass) + { // Wrong class, but try to pick up for mana + if(netgame && !deathmatch) + { // Can't pick up weapons for other classes in coop netplay + return; + } + if(weaponType == WP_SECOND) + { + if(!P_GiveMana(player, MANA_1, 25)) + { + return; + } + } + else + { + if(!P_GiveMana(player, MANA_2, 25)) + { + return; + } + } + } + else if(netgame && !deathmatch) + { // Cooperative net-game + if(player->weaponowned[weaponType]) + { + return; + } + player->weaponowned[weaponType] = true; + if(weaponType == WP_SECOND) + { + P_GiveMana(player, MANA_1, 25); + } + else + { + P_GiveMana(player, MANA_2, 25); + } + player->pendingweapon = weaponType; + remove = false; + } + else + { // Deathmatch or single player game + if(weaponType == WP_SECOND) + { + gaveMana = P_GiveMana(player, MANA_1, 25); + } + else + { + gaveMana = P_GiveMana(player, MANA_2, 25); + } + if(player->weaponowned[weaponType]) + { + gaveWeapon = false; + } + else + { + gaveWeapon = true; + player->weaponowned[weaponType] = true; + if(weaponType > player->readyweapon) + { // Only switch to more powerful weapons + player->pendingweapon = weaponType; + } + } + if(!(gaveWeapon || gaveMana)) + { // Player didn't need the weapon or any mana + return; + } + } + + P_SetMessage(player, message, false); + if(weapon->special) + { + P_ExecuteLineSpecial(weapon->special, weapon->args, + NULL, 0, player->mo); + weapon->special = 0; + } + + if(remove) + { + if(deathmatch && !(weapon->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(weapon); + } + else + { + P_RemoveMobj(weapon); + } + } + + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + SB_PaletteFlash(false); + } +} + +//-------------------------------------------------------------------------- +// +// FUNC P_GiveWeapon +// +// Returns true if the weapon or its mana was accepted. +// +//-------------------------------------------------------------------------- + +/* +boolean P_GiveWeapon(player_t *player, pclass_t class, weapontype_t weapon) +{ + boolean gaveMana; + boolean gaveWeapon; + + if(player->class != class) + { // player cannot use this weapon, take it anyway, and get mana + if(netgame && !deathmatch) + { // Can't pick up weapons for other classes in coop netplay + return false; + } + if(weapon == WP_SECOND) + { + return P_GiveMana(player, MANA_1, 25); + } + else + { + return P_GiveMana(player, MANA_2, 25); + } + } + if(netgame && !deathmatch) + { // Cooperative net-game + if(player->weaponowned[weapon]) + { + return(false); + } + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + if(weapon == WP_SECOND) + { + P_GiveMana(player, MANA_1, 25); + } + else + { + P_GiveMana(player, MANA_2, 25); + } + player->pendingweapon = weapon; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + } + return(false); + } + if(weapon == WP_SECOND) + { + gaveMana = P_GiveMana(player, MANA_1, 25); + } + else + { + gaveMana = P_GiveMana(player, MANA_2, 25); + } + if(player->weaponowned[weapon]) + { + gaveWeapon = false; + } + else + { + gaveWeapon = true; + player->weaponowned[weapon] = true; + if(weapon > player->readyweapon) + { // Only switch to more powerful weapons + player->pendingweapon = weapon; + } + } + return(gaveWeapon || gaveMana); +} +*/ + +//=========================================================================== +// +// P_GiveWeaponPiece +// +//=========================================================================== + +/* +boolean P_GiveWeaponPiece(player_t *player, pclass_t class, int piece) +{ + P_GiveMana(player, MANA_1, 20); + P_GiveMana(player, MANA_2, 20); + if(player->class != class) + { + return true; + } + else if(player->pieces&piece) + { // player already has that weapon piece + return true; + } + player->pieces |= piece; + if(player->pieces == 7) + { // player has built the fourth weapon! + P_GiveWeapon(player, class, WP_FOURTH); + S_StartSound(player->mo, SFX_WEAPON_BUILD); + } + return true; +} +*/ + +//========================================================================== +// +// TryPickupWeaponPiece +// +//========================================================================== + +static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass, + int pieceValue, mobj_t *pieceMobj) +{ + boolean remove; + boolean checkAssembled; + boolean gaveWeapon; + int gaveMana; + static char *fourthWeaponText[] = + { + TXT_WEAPON_F4, + TXT_WEAPON_C4, + TXT_WEAPON_M4 + }; + static char *weaponPieceText[] = + { + TXT_QUIETUS_PIECE, + TXT_WRAITHVERGE_PIECE, + TXT_BLOODSCOURGE_PIECE + }; + static int pieceValueTrans[] = + { + 0, // 0: never + WPIECE1|WPIECE2|WPIECE3, // WPIECE1 (1) + WPIECE2|WPIECE3, // WPIECE2 (2) + 0, // 3: never + WPIECE3 // WPIECE3 (4) + }; + + remove = true; + checkAssembled = true; + gaveWeapon = false; + if(player->class != matchClass) + { // Wrong class, but try to pick up for mana + if(netgame && !deathmatch) + { // Can't pick up wrong-class weapons in coop netplay + return; + } + checkAssembled = false; + gaveMana = P_GiveMana(player, MANA_1, 20)+ + P_GiveMana(player, MANA_2, 20); + if(!gaveMana) + { // Didn't need the mana, so don't pick it up + return; + } + } + else if(netgame && !deathmatch) + { // Cooperative net-game + if(player->pieces&pieceValue) + { // Already has the piece + return; + } + pieceValue = pieceValueTrans[pieceValue]; + P_GiveMana(player, MANA_1, 20); + P_GiveMana(player, MANA_2, 20); + remove = false; + } + else + { // Deathmatch or single player game + gaveMana = P_GiveMana(player, MANA_1, 20)+ + P_GiveMana(player, MANA_2, 20); + if(player->pieces&pieceValue) + { // Already has the piece, check if mana needed + if(!gaveMana) + { // Didn't need the mana, so don't pick it up + return; + } + checkAssembled = false; + } + } + + // Pick up the weapon piece + if(pieceMobj->special) + { + P_ExecuteLineSpecial(pieceMobj->special, pieceMobj->args, + NULL, 0, player->mo); + pieceMobj->special = 0; + } + if(remove) + { + if(deathmatch && !(pieceMobj->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(pieceMobj); + } + else + { + P_RemoveMobj(pieceMobj); + } + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + SB_PaletteFlash(false); + } + + // Check if fourth weapon assembled + if(checkAssembled) + { + player->pieces |= pieceValue; + if(player->pieces == (WPIECE1|WPIECE2|WPIECE3)) + { + gaveWeapon = true; + player->weaponowned[WP_FOURTH] = true; + player->pendingweapon = WP_FOURTH; + } + } + + if(gaveWeapon) + { + P_SetMessage(player, fourthWeaponText[matchClass], false); + // Play the build-sound full volume for all players + S_StartSound(NULL, SFX_WEAPON_BUILD); + } + else + { + P_SetMessage(player, weaponPieceText[matchClass], false); + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveBody +// +// Returns false if the body isn't needed at all. +// +//--------------------------------------------------------------------------- + +boolean P_GiveBody(player_t *player, int num) +{ + int max; + + max = MAXHEALTH; + if(player->morphTics) + { + max = MAXMORPHHEALTH; + } + if(player->health >= max) + { + return(false); + } + player->health += num; + if(player->health > max) + { + player->health = max; + } + player->mo->health = player->health; + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveArmor +// +// Returns false if the armor is worse than the current armor. +// +//--------------------------------------------------------------------------- + +boolean P_GiveArmor(player_t *player, armortype_t armortype, int amount) +{ + int hits; + int totalArmor; + + extern int ArmorMax[NUMCLASSES]; + + if(amount == -1) + { + hits = ArmorIncrement[player->class][armortype]; + if(player->armorpoints[armortype] >= hits) + { + return false; + } + else + { + player->armorpoints[armortype] = hits; + } + } + else + { + hits = amount*5*FRACUNIT; + totalArmor = player->armorpoints[ARMOR_ARMOR] + +player->armorpoints[ARMOR_SHIELD] + +player->armorpoints[ARMOR_HELMET] + +player->armorpoints[ARMOR_AMULET] + +AutoArmorSave[player->class]; + if(totalArmor < ArmorMax[player->class]*5*FRACUNIT) + { + player->armorpoints[armortype] += hits; + } + else + { + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------- +// +// PROC P_GiveKey +// +//--------------------------------------------------------------------------- + +int P_GiveKey(player_t *player, keytype_t key) +{ + if(player->keys&(1<bonuscount += BONUSADD; + player->keys |= 1<powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = INVULNTICS; + player->mo->flags2 |= MF2_INVULNERABLE; + if(player->class == PCLASS_MAGE) + { + player->mo->flags2 |= MF2_REFLECTIVE; + } + return(true); + } + if(power == pw_flight) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = FLIGHTTICS; + player->mo->flags2 |= MF2_FLY; + player->mo->flags |= MF_NOGRAVITY; + if(player->mo->z <= player->mo->floorz) + { + player->flyheight = 10; // thrust the player in the air a bit + } + return(true); + } + if(power == pw_infrared) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = INFRATICS; + return(true); + } + if(power == pw_speed) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = SPEEDTICS; + return(true); + } + if(power == pw_minotaur) + { + // Doesn't matter if already have power, renew ticker + player->powers[power] = MAULATORTICS; + return(true); + } +/* + if(power == pw_ironfeet) + { + player->powers[power] = IRONTICS; + return(true); + } + if(power == pw_strength) + { + P_GiveBody(player, 100); + player->powers[power] = 1; + return(true); + } +*/ + if(player->powers[power]) + { + return(false); // already got it + } + player->powers[power] = 1; + return(true); +} + +//========================================================================== +// +// TryPickupArtifact +// +//========================================================================== + +static void TryPickupArtifact(player_t *player, artitype_t artifactType, + mobj_t *artifact) +{ + static char *artifactMessages[NUMARTIFACTS] = + { + NULL, + TXT_ARTIINVULNERABILITY, + TXT_ARTIHEALTH, + TXT_ARTISUPERHEALTH, + TXT_ARTIHEALINGRADIUS, + TXT_ARTISUMMON, + TXT_ARTITORCH, + TXT_ARTIEGG, + TXT_ARTIFLY, + TXT_ARTIBLASTRADIUS, + TXT_ARTIPOISONBAG, + TXT_ARTITELEPORTOTHER, + TXT_ARTISPEED, + TXT_ARTIBOOSTMANA, + TXT_ARTIBOOSTARMOR, + TXT_ARTITELEPORT, + TXT_ARTIPUZZSKULL, + TXT_ARTIPUZZGEMBIG, + TXT_ARTIPUZZGEMRED, + TXT_ARTIPUZZGEMGREEN1, + TXT_ARTIPUZZGEMGREEN2, + TXT_ARTIPUZZGEMBLUE1, + TXT_ARTIPUZZGEMBLUE2, + TXT_ARTIPUZZBOOK1, + TXT_ARTIPUZZBOOK2, + TXT_ARTIPUZZSKULL2, + TXT_ARTIPUZZFWEAPON, + TXT_ARTIPUZZCWEAPON, + TXT_ARTIPUZZMWEAPON, + TXT_ARTIPUZZGEAR, // All gear pickups use the same text + TXT_ARTIPUZZGEAR, + TXT_ARTIPUZZGEAR, + TXT_ARTIPUZZGEAR + }; + + if(P_GiveArtifact(player, artifactType, artifact)) + { + if(artifact->special) + { + P_ExecuteLineSpecial(artifact->special, artifact->args, + NULL, 0, NULL); + artifact->special = 0; + } + player->bonuscount += BONUSADD; + if(artifactType < arti_firstpuzzitem) + { + SetDormantArtifact(artifact); + S_StartSound(artifact, SFX_PICKUP_ARTIFACT); + P_SetMessage(player, artifactMessages[artifactType], false); + } + else + { // Puzzle item + S_StartSound(NULL, SFX_PICKUP_ITEM); + P_SetMessage(player, artifactMessages[artifactType], true); + if(!netgame || deathmatch) + { // Remove puzzle items if not cooperative netplay + P_RemoveMobj(artifact); + } + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveArtifact +// +// Returns true if artifact accepted. +// +//--------------------------------------------------------------------------- + +boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo) +{ + int i; + int j; + boolean slidePointer; + + slidePointer = false; + i = 0; + while(player->inventory[i].type != arti && i < player->inventorySlotNum) + { + i++; + } + if(i == player->inventorySlotNum) + { + if(arti < arti_firstpuzzitem) + { + i = 0; + while(player->inventory[i].type < arti_firstpuzzitem + && i < player->inventorySlotNum) + { + i++; + } + if(i != player->inventorySlotNum) + { + for(j = player->inventorySlotNum; j > i; j--) + { + player->inventory[j].count = player->inventory[j-1].count; + player->inventory[j].type = player->inventory[j-1].type; + slidePointer = true; + } + } + } + player->inventory[i].count = 1; + player->inventory[i].type = arti; + player->inventorySlotNum++; + } + else + { + if(arti >= arti_firstpuzzitem && netgame && !deathmatch) + { // Can't carry more than 1 puzzle item in coop netplay + return false; + } + if(player->inventory[i].count >= 25) + { // Player already has 25 of this item + return false; + } + player->inventory[i].count++; + } + if(!player->artifactCount) + { + player->readyArtifact = arti; + } + else if(player == &players[consoleplayer] && slidePointer + && i <= inv_ptr) + { + inv_ptr++; + curpos++; + if(curpos > 6) + { + curpos = 6; + } + } + player->artifactCount++; + return(true); +} + +//========================================================================== +// +// SetDormantArtifact +// +// Removes the MF_SPECIAL flag and initiates the artifact pickup +// animation. +// +//========================================================================== + +static void SetDormantArtifact(mobj_t *arti) +{ + arti->flags &= ~MF_SPECIAL; + if(deathmatch && !(arti->flags2 & MF2_DROPPED)) + { + if(arti->type == MT_ARTIINVULNERABILITY) + { + P_SetMobjState(arti, S_DORMANTARTI3_1); + } + else if(arti->type == MT_SUMMONMAULATOR + || arti->type == MT_ARTIFLY) + { + P_SetMobjState(arti, S_DORMANTARTI2_1); + } + else + { + P_SetMobjState(arti, S_DORMANTARTI1_1); + } + } + else + { // Don't respawn + P_SetMobjState(arti, S_DEADARTI1); + } +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreArtifact +// +//--------------------------------------------------------------------------- + +void A_RestoreArtifact(mobj_t *arti) +{ + arti->flags |= MF_SPECIAL; + P_SetMobjState(arti, arti->info->spawnstate); + S_StartSound(arti, SFX_RESPAWN); +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreSpecialThing1 +// +// Make a special thing visible again. +// +//--------------------------------------------------------------------------- + +void A_RestoreSpecialThing1(mobj_t *thing) +{ + thing->flags2 &= ~MF2_DONTDRAW; + S_StartSound(thing, SFX_RESPAWN); +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreSpecialThing2 +// +//--------------------------------------------------------------------------- + +void A_RestoreSpecialThing2(mobj_t *thing) +{ + thing->flags |= MF_SPECIAL; + P_SetMobjState(thing, thing->info->spawnstate); +} + +//--------------------------------------------------------------------------- +// +// PROC P_TouchSpecialThing +// +//--------------------------------------------------------------------------- + +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + fixed_t delta; + int sound; + boolean respawn; + + delta = special->z-toucher->z; + if(delta > toucher->height || delta < -32*FRACUNIT) + { // Out of reach + return; + } + if(toucher->health <= 0) + { // Toucher is dead + return; + } + sound = SFX_PICKUP_ITEM; + player = toucher->player; + respawn = true; + switch(special->sprite) + { + // Items + case SPR_PTN1: // Item_HealingPotion + if(!P_GiveBody(player, 10)) + { + return; + } + P_SetMessage(player, TXT_ITEMHEALTH, false); + break; + case SPR_ARM1: + if(!P_GiveArmor(player, ARMOR_ARMOR, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR1, false); + break; + case SPR_ARM2: + if(!P_GiveArmor(player, ARMOR_SHIELD, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR2, false); + break; + case SPR_ARM3: + if(!P_GiveArmor(player, ARMOR_HELMET, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR3, false); + break; + case SPR_ARM4: + if(!P_GiveArmor(player, ARMOR_AMULET, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR4, false); + break; + + // Keys + case SPR_KEY1: + case SPR_KEY2: + case SPR_KEY3: + case SPR_KEY4: + case SPR_KEY5: + case SPR_KEY6: + case SPR_KEY7: + case SPR_KEY8: + case SPR_KEY9: + case SPR_KEYA: + case SPR_KEYB: + if(!P_GiveKey(player, special->sprite-SPR_KEY1)) + { + return; + } + P_SetMessage(player, TextKeyMessages[special->sprite-SPR_KEY1], + true); + sound = SFX_PICKUP_KEY; + + // Check and process the special now in case the key doesn't + // get removed for coop netplay + if(special->special) + { + P_ExecuteLineSpecial(special->special, special->args, + NULL, 0, toucher); + special->special = 0; + } + + if(!netgame) + { // Only remove keys in single player game + break; + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, sound); + SB_PaletteFlash(false); + } + return; + + // Artifacts + case SPR_PTN2: + TryPickupArtifact(player, arti_health, special); + return; + case SPR_SOAR: + TryPickupArtifact(player, arti_fly, special); + return; + case SPR_INVU: + TryPickupArtifact(player, arti_invulnerability, special); + return; + case SPR_SUMN: + TryPickupArtifact(player, arti_summon, special); + return; + case SPR_PORK: + TryPickupArtifact(player, arti_egg, special); + return; + case SPR_SPHL: + TryPickupArtifact(player, arti_superhealth, special); + return; + case SPR_HRAD: + TryPickupArtifact(player, arti_healingradius, special); + return; + case SPR_TRCH: + TryPickupArtifact(player, arti_torch, special); + return; + case SPR_ATLP: + TryPickupArtifact(player, arti_teleport, special); + return; + case SPR_TELO: + TryPickupArtifact(player, arti_teleportother, special); + return; + case SPR_PSBG: + TryPickupArtifact(player, arti_poisonbag, special); + return; + case SPR_SPED: + TryPickupArtifact(player, arti_speed, special); + return; + case SPR_BMAN: + TryPickupArtifact(player, arti_boostmana, special); + return; + case SPR_BRAC: + TryPickupArtifact(player, arti_boostarmor, special); + return; + case SPR_BLST: + TryPickupArtifact(player, arti_blastradius, special); + return; + + // Puzzle artifacts + case SPR_ASKU: + TryPickupArtifact(player, arti_puzzskull, special); + return; + case SPR_ABGM: + TryPickupArtifact(player, arti_puzzgembig, special); + return; + case SPR_AGMR: + TryPickupArtifact(player, arti_puzzgemred, special); + return; + case SPR_AGMG: + TryPickupArtifact(player, arti_puzzgemgreen1, special); + return; + case SPR_AGG2: + TryPickupArtifact(player, arti_puzzgemgreen2, special); + return; + case SPR_AGMB: + TryPickupArtifact(player, arti_puzzgemblue1, special); + return; + case SPR_AGB2: + TryPickupArtifact(player, arti_puzzgemblue2, special); + return; + case SPR_ABK1: + TryPickupArtifact(player, arti_puzzbook1, special); + return; + case SPR_ABK2: + TryPickupArtifact(player, arti_puzzbook2, special); + return; + case SPR_ASK2: + TryPickupArtifact(player, arti_puzzskull2, special); + return; + case SPR_AFWP: + TryPickupArtifact(player, arti_puzzfweapon, special); + return; + case SPR_ACWP: + TryPickupArtifact(player, arti_puzzcweapon, special); + return; + case SPR_AMWP: + TryPickupArtifact(player, arti_puzzmweapon, special); + return; + case SPR_AGER: + TryPickupArtifact(player, arti_puzzgear1, special); + return; + case SPR_AGR2: + TryPickupArtifact(player, arti_puzzgear2, special); + return; + case SPR_AGR3: + TryPickupArtifact(player, arti_puzzgear3, special); + return; + case SPR_AGR4: + TryPickupArtifact(player, arti_puzzgear4, special); + return; + + // Mana + case SPR_MAN1: + if(!P_GiveMana(player, MANA_1, 15)) + { + return; + } + P_SetMessage(player, TXT_MANA_1, false); + break; + case SPR_MAN2: + if(!P_GiveMana(player, MANA_2, 15)) + { + return; + } + P_SetMessage(player, TXT_MANA_2, false); + break; + case SPR_MAN3: // Double Mana Dodecahedron + if(!P_GiveMana(player, MANA_1, 20)) + { + if(!P_GiveMana(player, MANA_2, 20)) + { + return; + } + } + else + { + P_GiveMana(player, MANA_2, 20); + } + P_SetMessage(player, TXT_MANA_BOTH, false); + break; + + // 2nd and 3rd Mage Weapons + case SPR_WMCS: // Frost Shards + TryPickupWeapon(player, PCLASS_MAGE, WP_SECOND, + special, TXT_WEAPON_M2); + return; + case SPR_WMLG: // Arc of Death + TryPickupWeapon(player, PCLASS_MAGE, WP_THIRD, + special, TXT_WEAPON_M3); + return; + + // 2nd and 3rd Fighter Weapons + case SPR_WFAX: // Timon's Axe + TryPickupWeapon(player, PCLASS_FIGHTER, WP_SECOND, + special, TXT_WEAPON_F2); + return; + case SPR_WFHM: // Hammer of Retribution + TryPickupWeapon(player, PCLASS_FIGHTER, WP_THIRD, + special, TXT_WEAPON_F3); + return; + + // 2nd and 3rd Cleric Weapons + case SPR_WCSS: // Serpent Staff + TryPickupWeapon(player, PCLASS_CLERIC, WP_SECOND, + special, TXT_WEAPON_C2); + return; + case SPR_WCFM: // Firestorm + TryPickupWeapon(player, PCLASS_CLERIC, WP_THIRD, + special, TXT_WEAPON_C3); + return; + + // Fourth Weapon Pieces + case SPR_WFR1: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE1, + special); + return; + case SPR_WFR2: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE2, + special); + return; + case SPR_WFR3: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE3, + special); + return; + case SPR_WCH1: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE1, + special); + return; + case SPR_WCH2: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE2, + special); + return; + case SPR_WCH3: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE3, + special); + return; + case SPR_WMS1: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE1, + special); + return; + case SPR_WMS2: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE2, + special); + return; + case SPR_WMS3: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE3, + special); + return; + + default: + I_Error("P_SpecialThing: Unknown gettable thing"); + } + if(special->special) + { + P_ExecuteLineSpecial(special->special, special->args, NULL, + 0, toucher); + special->special = 0; + } + if(deathmatch && respawn && !(special->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(special); + } + else + { + P_RemoveMobj(special); + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, sound); + SB_PaletteFlash(false); + } +} + +// Search thinker list for minotaur +mobj_t *ActiveMinotaur(player_t *master) +{ + mobj_t *mo; + player_t *plr; + thinker_t *think; + unsigned int *starttime; + + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) continue; + mo = (mobj_t *)think; + if(mo->type != MT_MINOTAUR) continue; + if(mo->health <= 0) continue; + if(!(mo->flags&MF_COUNTKILL)) continue; // for morphed minotaurs + if(mo->flags&MF_CORPSE) continue; + starttime = (unsigned int *)mo->args; + if ((leveltime - *starttime) >= MAULATORTICS) continue; + plr = ((mobj_t *)mo->special1)->player; + if(plr == master) return(mo); + } + return(NULL); +} + + +//--------------------------------------------------------------------------- +// +// PROC P_KillMobj +// +//--------------------------------------------------------------------------- + +void P_KillMobj(mobj_t *source, mobj_t *target) +{ + int dummy; + mobj_t *master; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_NOGRAVITY); + target->flags |= MF_CORPSE|MF_DROPOFF; + target->flags2 &= ~MF2_PASSMOBJ; + target->height >>= 2; + if((target->flags&MF_COUNTKILL || target->type == MT_ZBELL) + && target->special) + { // Initiate monster death actions + if(target->type == MT_SORCBOSS) + { + dummy = 0; + P_StartACS(target->special, 0, (byte *)&dummy, target, + NULL, 0); + } + else + { + P_ExecuteLineSpecial(target->special, target->args, + NULL, 0, target); + } + } + if(source && source->player) + { // Check for frag changes + if(target->player) + { + if(target == source) + { // Self-frag + target->player->frags[target->player-players]--; + if(cmdfrag && netgame + && source->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(source->player); + } + } + else + { + source->player->frags[target->player-players]++; + if(cmdfrag && netgame + && source->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(source->player); + } + } + } + } + if(target->player) + { // Player death + if(!source) + { // Self-frag + target->player->frags[target->player-players]--; + if(cmdfrag && netgame + && target->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(target->player); + } + } + target->flags &= ~MF_SOLID; + target->flags2 &= ~MF2_FLY; + target->player->powers[pw_flight] = 0; + target->player->playerstate = PST_DEAD; + P_DropWeapon(target->player); + if(target->flags2&MF2_FIREDAMAGE) + { // Player flame death + switch(target->player->class) + { + case PCLASS_FIGHTER: + S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); + P_SetMobjState(target, S_PLAY_F_FDTH1); + return; + case PCLASS_CLERIC: + S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); + P_SetMobjState(target, S_PLAY_C_FDTH1); + return; + case PCLASS_MAGE: + S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); + P_SetMobjState(target, S_PLAY_M_FDTH1); + return; + default: + break; + } + } + if(target->flags2&MF2_ICEDAMAGE) + { // Player ice death + target->flags &= ~(7<flags |= MF_ICECORPSE; + switch(target->player->class) + { + case PCLASS_FIGHTER: + P_SetMobjState(target, S_FPLAY_ICE); + return; + case PCLASS_CLERIC: + P_SetMobjState(target, S_CPLAY_ICE); + return; + case PCLASS_MAGE: + P_SetMobjState(target, S_MPLAY_ICE); + return; + case PCLASS_PIG: + P_SetMobjState(target, S_PIG_ICE); + return; + default: + break; + } + } + } + if(target->flags2&MF2_FIREDAMAGE) + { + if(target->type == MT_FIGHTER_BOSS + || target->type == MT_CLERIC_BOSS + || target->type == MT_MAGE_BOSS) + { + switch(target->type) + { + case MT_FIGHTER_BOSS: + S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); + P_SetMobjState(target, S_PLAY_F_FDTH1); + return; + case MT_CLERIC_BOSS: + S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); + P_SetMobjState(target, S_PLAY_C_FDTH1); + return; + case MT_MAGE_BOSS: + S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); + P_SetMobjState(target, S_PLAY_M_FDTH1); + return; + default: + break; + } + } + else if(target->type == MT_TREEDESTRUCTIBLE) + { + P_SetMobjState(target, S_ZTREEDES_X1); + target->height = 24*FRACUNIT; + S_StartSound(target, SFX_TREE_EXPLODE); + return; + } + } + if(target->flags2&MF2_ICEDAMAGE) + { + target->flags |= MF_ICECORPSE; + switch(target->type) + { + case MT_BISHOP: + P_SetMobjState(target, S_BISHOP_ICE); + return; + case MT_CENTAUR: + case MT_CENTAURLEADER: + P_SetMobjState(target, S_CENTAUR_ICE); + return; + case MT_DEMON: + case MT_DEMON2: + P_SetMobjState(target, S_DEMON_ICE); + return; + case MT_SERPENT: + case MT_SERPENTLEADER: + P_SetMobjState(target, S_SERPENT_ICE); + return; + case MT_WRAITH: + case MT_WRAITHB: + P_SetMobjState(target, S_WRAITH_ICE); + return; + case MT_ETTIN: + P_SetMobjState(target, S_ETTIN_ICE1); + return; + case MT_FIREDEMON: + P_SetMobjState(target, S_FIRED_ICE1); + return; + case MT_FIGHTER_BOSS: + P_SetMobjState(target, S_FIGHTER_ICE); + return; + case MT_CLERIC_BOSS: + P_SetMobjState(target, S_CLERIC_ICE); + return; + case MT_MAGE_BOSS: + P_SetMobjState(target, S_MAGE_ICE); + return; + case MT_PIG: + P_SetMobjState(target, S_PIG_ICE); + return; + default: + target->flags &= ~MF_ICECORPSE; + break; + } + } + + if(target->type == MT_MINOTAUR) + { + master = (mobj_t *)target->special1; + if(master->health > 0) + { + if (!ActiveMinotaur(master->player)) + { + master->player->powers[pw_minotaur] = 0; + } + } + } + else if(target->type == MT_TREEDESTRUCTIBLE) + { + target->height = 24*FRACUNIT; + } + if(target->health < -(target->info->spawnhealth>>1) + && target->info->xdeathstate) + { // Extreme death + P_SetMobjState(target, target->info->xdeathstate); + } + else + { // Normal death + if ((target->type==MT_FIREDEMON) && + (target->z <= target->floorz + 2*FRACUNIT) && + (target->info->xdeathstate)) + { + // This is to fix the imps' staying in fall state + P_SetMobjState(target, target->info->xdeathstate); + } + else + { + P_SetMobjState(target, target->info->deathstate); + } + } + target->tics -= P_Random()&3; +// I_StartSound(&actor->r, actor->info->deathsound); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_MinotaurSlam +// +//--------------------------------------------------------------------------- + +void P_MinotaurSlam(mobj_t *source, mobj_t *target) +{ + angle_t angle; + fixed_t thrust; + + angle = R_PointToAngle2(source->x, source->y, target->x, target->y); + angle >>= ANGLETOFINESHIFT; + thrust = 16*FRACUNIT+(P_Random()<<10); + target->momx += FixedMul(thrust, finecosine[angle]); + target->momy += FixedMul(thrust, finesine[angle]); + P_DamageMobj(target, NULL, source, HITDICE(4)); + if(target->player) + { + target->reactiontime = 14+(P_Random()&7); + } + source->args[0] = 0; // Stop charging +} + + +//--------------------------------------------------------------------------- +// +// FUNC P_MorphPlayer +// +// Returns true if the player gets turned into a pig +// +//--------------------------------------------------------------------------- + +boolean P_MorphPlayer(player_t *player) +{ + mobj_t *pmo; + mobj_t *fog; + mobj_t *beastMo; + fixed_t x; + fixed_t y; + fixed_t z; + angle_t angle; + int oldFlags2; + + if(player->powers[pw_invulnerability]) + { // Immune when invulnerable + return(false); + } + if(player->morphTics) + { // Player is already a beast + return false; + } + pmo = player->mo; + x = pmo->x; + y = pmo->y; + z = pmo->z; + angle = pmo->angle; + oldFlags2 = pmo->flags2; + P_SetMobjState(pmo, S_FREETARGMOBJ); + fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + beastMo = P_SpawnMobj(x, y, z, MT_PIGPLAYER); + beastMo->special1 = player->readyweapon; + beastMo->angle = angle; + beastMo->player = player; + player->health = beastMo->health = MAXMORPHHEALTH; + player->mo = beastMo; + memset(&player->armorpoints[0], 0, NUMARMOR*sizeof(int)); + player->class = PCLASS_PIG; + if(oldFlags2&MF2_FLY) + { + beastMo->flags2 |= MF2_FLY; + } + player->morphTics = MORPHTICS; + P_ActivateMorphWeapon(player); + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_MorphMonster +// +//--------------------------------------------------------------------------- + +boolean P_MorphMonster(mobj_t *actor) +{ + mobj_t *master, *monster, *fog; + mobjtype_t moType; + fixed_t x; + fixed_t y; + fixed_t z; + mobj_t oldMonster; + + if(actor->player) return(false); + if(!(actor->flags&MF_COUNTKILL)) return false; + if(actor->flags2&MF2_BOSS) return false; + moType = actor->type; + switch(moType) + { + case MT_PIG: + return(false); + case MT_FIGHTER_BOSS: + case MT_CLERIC_BOSS: + case MT_MAGE_BOSS: + return(false); + default: + break; + } + + oldMonster = *actor; + x = oldMonster.x; + y = oldMonster.y; + z = oldMonster.z; + P_RemoveMobjFromTIDList(actor); + P_SetMobjState(actor, S_FREETARGMOBJ); + fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + monster = P_SpawnMobj(x, y, z, MT_PIG); + monster->special2 = moType; + monster->special1 = MORPHTICS+P_Random(); + monster->flags |= (oldMonster.flags&MF_SHADOW); + monster->target = oldMonster.target; + monster->angle = oldMonster.angle; + monster->tid = oldMonster.tid; + monster->special = oldMonster.special; + P_InsertMobjIntoTIDList(monster, oldMonster.tid); + memcpy(monster->args, oldMonster.args, 5); + + // check for turning off minotaur power for active icon + if (moType==MT_MINOTAUR) + { + master = (mobj_t *)oldMonster.special1; + if(master->health > 0) + { + if (!ActiveMinotaur(master->player)) + { + master->player->powers[pw_minotaur] = 0; + } + } + } + return(true); +} + +//--------------------------------------------------------------------------- +// +// PROC P_AutoUseHealth +// +//--------------------------------------------------------------------------- + +void P_AutoUseHealth(player_t *player, int saveHealth) +{ + int i; + int count; + int normalCount; + int normalSlot=0; + int superCount; + int superSlot=0; + + normalCount = superCount = 0; + for(i = 0; i < player->inventorySlotNum; i++) + { + if(player->inventory[i].type == arti_health) + { + normalSlot = i; + normalCount = player->inventory[i].count; + } + else if(player->inventory[i].type == arti_superhealth) + { + superSlot = i; + superCount = player->inventory[i].count; + } + } + if((gameskill == sk_baby) && (normalCount*25 >= saveHealth)) + { // Use quartz flasks + count = (saveHealth+24)/25; + for(i = 0; i < count; i++) + { + player->health += 25; + P_PlayerRemoveArtifact(player, normalSlot); + } + } + else if(superCount*100 >= saveHealth) + { // Use mystic urns + count = (saveHealth+99)/100; + for(i = 0; i < count; i++) + { + player->health += 100; + P_PlayerRemoveArtifact(player, superSlot); + } + } + else if((gameskill == sk_baby) + && (superCount*100+normalCount*25 >= saveHealth)) + { // Use mystic urns and quartz flasks + count = (saveHealth+24)/25; + saveHealth -= count*25; + for(i = 0; i < count; i++) + { + player->health += 25; + P_PlayerRemoveArtifact(player, normalSlot); + } + count = (saveHealth+99)/100; + for(i = 0; i < count; i++) + { + player->health += 100; + P_PlayerRemoveArtifact(player, normalSlot); + } + } + player->mo->health = player->health; +} + +/* +================= += += P_DamageMobj += += Damages both enemies and players += inflictor is the thing that caused the damage += creature or missile, can be NULL (slime, etc) += source is the thing to target after taking damage += creature or NULL += Source and inflictor are the same for melee attacks += source can be null for barrel explosions and other environmental stuff +================== +*/ + +void P_DamageMobj +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + int damage +) +{ + unsigned ang; + int saved; + fixed_t savedPercent; + player_t *player; + mobj_t *master; + fixed_t thrust; + int temp; + int i; + + if(!(target->flags&MF_SHOOTABLE)) + { + // Shouldn't happen + return; + } + if(target->health <= 0) + { + if (inflictor && inflictor->flags2&MF2_ICEDAMAGE) + { + return; + } + else if (target->flags&MF_ICECORPSE) // frozen + { + target->tics = 1; + target->momx = target->momy = 0; + } + return; + } + if ((target->flags2&MF2_INVULNERABLE) && damage < 10000) + { // mobj is invulnerable + if(target->player) return; // for player, no exceptions + if(inflictor) + { + switch(inflictor->type) + { + // These inflictors aren't foiled by invulnerability + case MT_HOLY_FX: + case MT_POISONCLOUD: + case MT_FIREBOMB: + break; + default: + return; + } + } + else + { + return; + } + } + if(target->player) + { + if(damage < 1000 && ((target->player->cheats&CF_GODMODE) + || target->player->powers[pw_invulnerability])) + { + return; + } + } + if(target->flags&MF_SKULLFLY) + { + target->momx = target->momy = target->momz = 0; + } + if(target->flags2&MF2_DORMANT) + { + // Invulnerable, and won't wake up + return; + } + player = target->player; + if(player && gameskill == sk_baby) + { + // Take half damage in trainer mode + damage >>= 1; + } + // Special damage types + if(inflictor) + { + switch(inflictor->type) + { + case MT_EGGFX: + if(player) + { + P_MorphPlayer(player); + } + else + { + P_MorphMonster(target); + } + return; // Always return + case MT_TELOTHER_FX1: + case MT_TELOTHER_FX2: + case MT_TELOTHER_FX3: + case MT_TELOTHER_FX4: + case MT_TELOTHER_FX5: + if ((target->flags&MF_COUNTKILL) && + (target->type != MT_SERPENT) && + (target->type != MT_SERPENTLEADER) && + (!(target->flags2 & MF2_BOSS))) + { + P_TeleportOther(target); + } + return; + case MT_MINOTAUR: + if(inflictor->flags&MF_SKULLFLY) + { // Slam only when in charge mode + P_MinotaurSlam(inflictor, target); + return; + } + break; + case MT_BISH_FX: + // Bishops are just too nasty + damage >>= 1; + break; + case MT_SHARDFX1: + switch(inflictor->special2) + { + case 3: + damage <<= 3; + break; + case 2: + damage <<= 2; + break; + case 1: + damage <<= 1; + break; + default: + break; + } + break; + case MT_CSTAFF_MISSILE: + // Cleric Serpent Staff does poison damage + if(target->player) + { + P_PoisonPlayer(target->player, source, 20); + damage >>= 1; + } + break; + case MT_ICEGUY_FX2: + damage >>= 1; + break; + case MT_POISONDART: + if(target->player) + { + P_PoisonPlayer(target->player, source, 20); + damage >>= 1; + } + break; + case MT_POISONCLOUD: + if(target->player) + { + if(target->player->poisoncount < 4) + { + P_PoisonDamage(target->player, source, + 15+(P_Random()&15), false); // Don't play painsound + P_PoisonPlayer(target->player, source, 50); + S_StartSound(target, SFX_PLAYER_POISONCOUGH); + } + return; + } + else if(!(target->flags&MF_COUNTKILL)) + { // only damage monsters/players with the poison cloud + return; + } + break; + case MT_FSWORD_MISSILE: + if(target->player) + { + damage -= damage>>2; + } + break; + default: + break; + } + } + // Push the target unless source is using the gauntlets + if(inflictor && (!source || !source->player) + && !(inflictor->flags2&MF2_NODMGTHRUST)) + { + ang = R_PointToAngle2(inflictor->x, inflictor->y, + target->x, target->y); + //thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + thrust = damage*(FRACUNIT>>3)*150/target->info->mass; + // make fall forwards sometimes + if((damage < 40) && (damage > target->health) + && (target->z-inflictor->z > 64*FRACUNIT) && (P_Random()&1)) + { + ang += ANG180; + thrust *= 4; + } + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul(thrust, finecosine[ang]); + target->momy += FixedMul(thrust, finesine[ang]); + } + + // + // player specific + // + if(player) + { + savedPercent = AutoArmorSave[player->class] + +player->armorpoints[ARMOR_ARMOR]+player->armorpoints[ARMOR_SHIELD] + +player->armorpoints[ARMOR_HELMET] + +player->armorpoints[ARMOR_AMULET]; + if(savedPercent) + { // armor absorbed some damage + if(savedPercent > 100*FRACUNIT) + { + savedPercent = 100*FRACUNIT; + } + for(i = 0; i < NUMARMOR; i++) + { + if(player->armorpoints[i]) + { + player->armorpoints[i] -= + FixedDiv(FixedMul(damage<class][i]), 300*FRACUNIT); + if(player->armorpoints[i] < 2*FRACUNIT) + { + player->armorpoints[i] = 0; + } + } + } + saved = FixedDiv(FixedMul(damage< savedPercent*2) + { + saved = savedPercent*2; + } + damage -= saved>>FRACBITS; + } + if(damage >= player->health + && ((gameskill == sk_baby) || deathmatch) + && !player->morphTics) + { // Try to use some inventory health + P_AutoUseHealth(player, damage-player->health+1); + } + player->health -= damage; // mirror mobj health here for Dave + if(player->health < 0) + { + player->health = 0; + } + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + if(player->damagecount > 100) + { + player->damagecount = 100; // teleport stomp does 10k points... + } + temp = damage < 100 ? damage : 100; + if(player == &players[consoleplayer]) + { + I_Tactile(40, 10, 40+temp*2); + SB_PaletteFlash(false); + } + } + + // + // do the damage + // + target->health -= damage; + if(target->health <= 0) + { // Death + if(inflictor) + { // check for special fire damage or ice damage deaths + if(inflictor->flags2&MF2_FIREDAMAGE) + { + if(player && !player->morphTics) + { // Check for flame death + if(target->health > -50 && damage > 25) + { + target->flags2 |= MF2_FIREDAMAGE; + } + } + else + { + target->flags2 |= MF2_FIREDAMAGE; + } + } + else if(inflictor->flags2&MF2_ICEDAMAGE) + { + target->flags2 |= MF2_ICEDAMAGE; + } + } + if(source && (source->type == MT_MINOTAUR)) + { // Minotaur's kills go to his master + master = (mobj_t *)(source->special1); + // Make sure still alive and not a pointer to fighter head + if (master->player && (master->player->mo == master)) + { + source = master; + } + } + if(source && (source->player) && + (source->player->readyweapon == WP_FOURTH)) + { + // Always extreme death from fourth weapon + target->health = -5000; + } + P_KillMobj(source, target); + return; + } + if((P_Random() < target->info->painchance) + && !(target->flags&MF_SKULLFLY)) + { + if(inflictor && (inflictor->type >= MT_LIGHTNING_FLOOR + && inflictor->type <= MT_LIGHTNING_ZAP)) + { + if(P_Random() < 96) + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + } + else + { // "electrocute" the target + target->frame |= FF_FULLBRIGHT; + if(target->flags&MF_COUNTKILL && P_Random() < 128 + && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) + { + if ((target->type == MT_CENTAUR) || + (target->type == MT_CENTAURLEADER) || + (target->type == MT_ETTIN)) + { + S_StartSound(target, SFX_PUPPYBEAT); + } + } + } + } + else + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + if(inflictor && inflictor->type == MT_POISONCLOUD) + { + if(target->flags&MF_COUNTKILL && P_Random() < 128 + && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) + { + if ((target->type == MT_CENTAUR) || + (target->type == MT_CENTAURLEADER) || + (target->type == MT_ETTIN)) + { + S_StartSound(target, SFX_PUPPYBEAT); + } + } + } + } + } + target->reactiontime = 0; // we're awake now... + if(!target->threshold && source && !(source->flags2&MF2_BOSS) + && !(target->type == MT_BISHOP) && !(target->type == MT_MINOTAUR)) + { + // Target actor is not intent on another actor, + // so make him chase after source + if((target->type == MT_CENTAUR && source->type == MT_CENTAURLEADER) + || (target->type == MT_CENTAURLEADER + && source->type == MT_CENTAUR)) + { + return; + } + target->target = source; + target->threshold = BASETHRESHOLD; + if(target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + { + P_SetMobjState(target, target->info->seestate); + } + } +} + +//========================================================================== +// +// P_FallingDamage +// +//========================================================================== + +void P_FallingDamage(player_t *player) +{ + int damage; + int mom; + int dist; + + mom = abs(player->mo->momz); + dist = FixedMul(mom, 16*FRACUNIT/23); + + if(mom >= 63*FRACUNIT) + { // automatic death + P_DamageMobj(player->mo, NULL, NULL, 10000); + return; + } + damage = ((FixedMul(dist, dist)/10)>>FRACBITS)-24; + if(player->mo->momz > -39*FRACUNIT && damage > player->mo->health + && player->mo->health != 1) + { // No-death threshold + damage = player->mo->health-1; + } + S_StartSound(player->mo, SFX_PLAYER_LAND); + P_DamageMobj(player->mo, NULL, NULL, damage); +} + +//========================================================================== +// +// P_PoisonPlayer - Sets up all data concerning poisoning +// +//========================================================================== + +void P_PoisonPlayer(player_t *player, mobj_t *poisoner, int poison) +{ + if((player->cheats&CF_GODMODE) || player->powers[pw_invulnerability]) + { + return; + } + player->poisoncount += poison; + player->poisoner = poisoner; + if(player->poisoncount > 100) + { + player->poisoncount = 100; + } +} + +//========================================================================== +// +// P_PoisonDamage - Similar to P_DamageMobj +// +//========================================================================== + +void P_PoisonDamage(player_t *player, mobj_t *source, int damage, + boolean playPainSound) +{ + mobj_t *target; + mobj_t *inflictor; + + target = player->mo; + inflictor = source; + if(target->health <= 0) + { + return; + } + if(target->flags2&MF2_INVULNERABLE && damage < 10000) + { // mobj is invulnerable + return; + } + if(player && gameskill == sk_baby) + { + // Take half damage in trainer mode + damage >>= 1; + } + if(damage < 1000 && ((player->cheats&CF_GODMODE) + || player->powers[pw_invulnerability])) + { + return; + } + if(damage >= player->health + && ((gameskill == sk_baby) || deathmatch) + && !player->morphTics) + { // Try to use some inventory health + P_AutoUseHealth(player, damage-player->health+1); + } + player->health -= damage; // mirror mobj health here for Dave + if(player->health < 0) + { + player->health = 0; + } + player->attacker = source; + + // + // do the damage + // + target->health -= damage; + if(target->health <= 0) + { // Death + target->special1 = damage; + if(player && inflictor && !player->morphTics) + { // Check for flame death + if((inflictor->flags2&MF2_FIREDAMAGE) + && (target->health > -50) && (damage > 25)) + { + target->flags2 |= MF2_FIREDAMAGE; + } + if(inflictor->flags2&MF2_ICEDAMAGE) + { + target->flags2 |= MF2_ICEDAMAGE; + } + } + P_KillMobj(source, target); + return; + } + if(!(leveltime&63) && playPainSound) + { + P_SetMobjState(target, target->info->painstate); + } +/* + if((P_Random() < target->info->painchance) + && !(target->flags&MF_SKULLFLY)) + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + } +*/ +} diff --git a/src/hexen/p_lights.c b/src/hexen/p_lights.c new file mode 100644 index 00000000..7a0b4c13 --- /dev/null +++ b/src/hexen/p_lights.c @@ -0,0 +1,351 @@ + +//************************************************************************** +//** +//** p_lights.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_lights.c,v $ +//** $Revision: 1.2 $ +//** $Date: 95/07/11 10:23:36 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" + +//============================================================================ +// +// T_Light +// +//============================================================================ + +void T_Light(light_t *light) +{ + if(light->count) + { + light->count--; + return; + } + switch(light->type) + { + case LITE_FADE: + light->sector->lightlevel = ((light->sector->lightlevel<value2)>>FRACBITS; + if(light->tics2 == 1) + { + if(light->sector->lightlevel >= light->value1) + { + light->sector->lightlevel = light->value1; + P_RemoveThinker(&light->thinker); + } + } + else if(light->sector->lightlevel <= light->value1) + { + light->sector->lightlevel = light->value1; + P_RemoveThinker(&light->thinker); + } + break; + case LITE_GLOW: + light->sector->lightlevel = ((light->sector->lightlevel<tics1)>>FRACBITS; + if(light->tics2 == 1) + { + if(light->sector->lightlevel >= light->value1) + { + light->sector->lightlevel = light->value1; + light->tics1 = -light->tics1; + light->tics2 = -1; // reverse direction + } + } + else if(light->sector->lightlevel <= light->value2) + { + light->sector->lightlevel = light->value2; + light->tics1 = -light->tics1; + light->tics2 = 1; // reverse direction + } + break; + case LITE_FLICKER: + if(light->sector->lightlevel == light->value1) + { + light->sector->lightlevel = light->value2; + light->count = (P_Random()&7)+1; + } + else + { + light->sector->lightlevel = light->value1; + light->count = (P_Random()&31)+1; + } + break; + case LITE_STROBE: + if(light->sector->lightlevel == light->value1) + { + light->sector->lightlevel = light->value2; + light->count = light->tics2; + } + else + { + light->sector->lightlevel = light->value1; + light->count = light->tics1; + } + break; + default: + break; + } +} + +//============================================================================ +// +// EV_SpawnLight +// +//============================================================================ + +boolean EV_SpawnLight(line_t *line, byte *arg, lighttype_t type) +{ + light_t *light; + sector_t *sec; + int secNum; + int arg1, arg2, arg3, arg4; + boolean think; + boolean rtn; + + arg1 = arg[1] > 255 ? 255 : arg[1]; + arg1 = arg1 < 0 ? 0 : arg1; + arg2 = arg[2] > 255 ? 255 : arg[2]; + arg2 = arg2 < 0 ? 0 : arg2; + arg3 = arg[3] > 255 ? 255 : arg[3]; + arg3 = arg3 < 0 ? 0 : arg3; + arg4 = arg[4] > 255 ? 255 : arg[4]; + arg4 = arg4 < 0 ? 0 : arg4; + + secNum = -1; + rtn = false; + think = false; + while((secNum = P_FindSectorFromTag(arg[0], secNum)) >= 0) + { + think = false; + sec = §ors[secNum]; + + light = (light_t *)Z_Malloc(sizeof(light_t), PU_LEVSPEC, 0); + light->type = type; + light->sector = sec; + light->count = 0; + rtn = true; + switch(type) + { + case LITE_RAISEBYVALUE: + sec->lightlevel += arg1; + if(sec->lightlevel > 255) + { + sec->lightlevel = 255; + } + break; + case LITE_LOWERBYVALUE: + sec->lightlevel -= arg1; + if(sec->lightlevel < 0) + { + sec->lightlevel = 0; + } + break; + case LITE_CHANGETOVALUE: + sec->lightlevel = arg1; + if(sec->lightlevel < 0) + { + sec->lightlevel = 0; + } + else if(sec->lightlevel > 255) + { + sec->lightlevel = 255; + } + break; + case LITE_FADE: + think = true; + light->value1 = arg1; // destination lightlevel + light->value2 = FixedDiv((arg1-sec->lightlevel)<lightlevel <= arg1) + { + light->tics2 = 1; // get brighter + } + else + { + light->tics2 = -1; + } + break; + case LITE_GLOW: + think = true; + light->value1 = arg1; // upper lightlevel + light->value2 = arg2; // lower lightlevel + light->tics1 = FixedDiv((arg1-sec->lightlevel)<lightlevel <= arg1) + { + light->tics2 = 1; // get brighter + } + else + { + light->tics2 = -1; + } + break; + case LITE_FLICKER: + think = true; + light->value1 = arg1; // upper lightlevel + light->value2 = arg2; // lower lightlevel + sec->lightlevel = light->value1; + light->count = (P_Random()&64)+1; + break; + case LITE_STROBE: + think = true; + light->value1 = arg1; // upper lightlevel + light->value2 = arg2; // lower lightlevel + light->tics1 = arg3; // upper tics + light->tics2 = arg4; // lower tics + light->count = arg3; + sec->lightlevel = light->value1; + break; + default: + rtn = false; + break; + } + if(think) + { + P_AddThinker(&light->thinker); + light->thinker.function = T_Light; + } + else + { + Z_Free(light); + } + } + return rtn; +} + +//============================================================================ +// +// T_Phase +// +//============================================================================ + +int PhaseTable[64] = +{ + 128, 112, 96, 80, 64, 48, 32, 32, + 16, 16, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 16, 16, + 32, 32, 48, 64, 80, 96, 112, 128 +}; + +void T_Phase(phase_t *phase) +{ + phase->index = (phase->index+1)&63; + phase->sector->lightlevel = phase->base+PhaseTable[phase->index]; +} + +//========================================================================== +// +// P_SpawnPhasedLight +// +//========================================================================== + +void P_SpawnPhasedLight(sector_t *sector, int base, int index) +{ + phase_t *phase; + + phase = Z_Malloc(sizeof(*phase), PU_LEVSPEC, 0); + P_AddThinker(&phase->thinker); + phase->sector = sector; + if(index == -1) + { // sector->lightlevel as the index + phase->index = sector->lightlevel&63; + } + else + { + phase->index = index&63; + } + phase->base = base&255; + sector->lightlevel = phase->base+PhaseTable[phase->index]; + phase->thinker.function = T_Phase; + + sector->special = 0; +} + +//========================================================================== +// +// P_SpawnLightSequence +// +//========================================================================== + +void P_SpawnLightSequence(sector_t *sector, int indexStep) +{ + sector_t *sec; + sector_t *nextSec; + sector_t *tempSec; + int seqSpecial; + int i; + int count; + fixed_t index; + fixed_t indexDelta; + int base; + + seqSpecial = LIGHT_SEQUENCE; // look for Light_Sequence, first + sec = sector; + count = 1; + do + { + nextSec = NULL; + sec->special = LIGHT_SEQUENCE_START; // make sure that the search doesn't back up. + for(i = 0; i < sec->linecount; i++) + { + tempSec = getNextSector(sec->lines[i], sec); + if(!tempSec) + { + continue; + } + if(tempSec->special == seqSpecial) + { + if(seqSpecial == LIGHT_SEQUENCE) + { + seqSpecial = LIGHT_SEQUENCE_ALT; + } + else + { + seqSpecial = LIGHT_SEQUENCE; + } + nextSec = tempSec; + count++; + } + } + sec = nextSec; + } while(sec); + + sec = sector; + count *= indexStep; + index = 0; + indexDelta = FixedDiv(64*FRACUNIT, count*FRACUNIT); + base = sector->lightlevel; + do + { + nextSec = NULL; + if(sec->lightlevel) + { + base = sec->lightlevel; + } + P_SpawnPhasedLight(sec, base, index>>FRACBITS); + index += indexDelta; + for(i = 0; i < sec->linecount; i++) + { + tempSec = getNextSector(sec->lines[i], sec); + if(!tempSec) + { + continue; + } + if(tempSec->special == LIGHT_SEQUENCE_START) + { + nextSec = tempSec; + } + } + sec = nextSec; + } while(sec); +} \ No newline at end of file diff --git a/src/hexen/p_local.h b/src/hexen/p_local.h new file mode 100644 index 00000000..daf425e9 --- /dev/null +++ b/src/hexen/p_local.h @@ -0,0 +1,372 @@ + +//************************************************************************** +//** +//** p_local.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_local.h,v $ +//** $Revision: 1.64 $ +//** $Date: 95/10/13 03:00:16 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + +#ifndef __R_LOCAL__ +#include "r_local.h" +#endif + +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define STARTPOISONPALS 13 +#define STARTICEPAL 21 +#define STARTHOLYPAL 22 +#define STARTSCOURGEPAL 25 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +#define NUMPOISONPALS 8 + +#define TOCENTER -8 +#define FLOATSPEED (FRACUNIT*4) + +#define MAXHEALTH 100 +#define MAXMORPHHEALTH 30 +#define VIEWHEIGHT (48*FRACUNIT) + +// mapblocks are used to check movement against lines and things +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + +// player radius for movement checking +#define PLAYERRADIUS 16*FRACUNIT + +// MAXRADIUS is for precalculated sector block boxes +// the spider demon is larger, but we don't have any moving sectors +// nearby +#define MAXRADIUS 32*FRACUNIT + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) + +typedef enum +{ + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + +#define BASETHRESHOLD 100 // follow a player exlusively for 3 seconds + +// ***** P_TICK ***** + +extern thinker_t thinkercap; // both the head and tail of the thinker list +extern int TimerGame; // tic countdown for deathmatch + +void P_InitThinkers(void); +void P_AddThinker(thinker_t *thinker); +void P_RemoveThinker(thinker_t *thinker); + +// ***** P_PSPR ***** + +#define USE_MANA1 1 +#define USE_MANA2 1 + +void P_SetPsprite(player_t *player, int position, statenum_t stnum); +void P_SetPspriteNF(player_t *player, int position, statenum_t stnum); +void P_SetupPsprites(player_t *curplayer); +void P_MovePsprites(player_t *curplayer); +void P_DropWeapon(player_t *player); +void P_ActivateMorphWeapon(player_t *player); +void P_PostMorphWeapon(player_t *player, weapontype_t weapon); + +// ***** P_USER ***** + +extern int PStateNormal[NUMCLASSES]; +extern int PStateRun[NUMCLASSES]; +extern int PStateAttack[NUMCLASSES]; +extern int PStateAttackEnd[NUMCLASSES]; + +void P_PlayerThink(player_t *player); +void P_Thrust(player_t *player, angle_t angle, fixed_t move); +void P_PlayerRemoveArtifact(player_t *player, int slot); +void P_PlayerUseArtifact(player_t *player, artitype_t arti); +boolean P_UseArtifact(player_t *player, artitype_t arti); +int P_GetPlayerNum(player_t *player); +void P_TeleportOther(mobj_t *victim); +void ResetBlasted(mobj_t *mo); + +// ***** P_MOBJ ***** + +// Any floor type >= FLOOR_LIQUID will floorclip sprites +enum +{ + FLOOR_SOLID, + FLOOR_ICE, + FLOOR_LIQUID, + FLOOR_WATER, + FLOOR_LAVA, + FLOOR_SLUDGE +}; + +#define ONFLOORZ MININT +#define ONCEILINGZ MAXINT +#define FLOATRANDZ (MAXINT-1) +#define FROMCEILINGZ128 (MAXINT-2) + +extern mobjtype_t PuffType; +extern mobj_t *MissileMobj; + +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t *th); +boolean P_SetMobjState(mobj_t *mobj, statenum_t state); +boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state); +void P_ThrustMobj(mobj_t *mo, angle_t angle, fixed_t move); +int P_FaceMobj(mobj_t *source, mobj_t *target, angle_t *delta); +boolean P_SeekerMissile(mobj_t *actor, angle_t thresh, angle_t turnMax); +void P_MobjThinker(mobj_t *mobj); +void P_BlasterMobjThinker(mobj_t *mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage); +void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t *originator); +void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t *originator); +void P_RipperBlood(mobj_t *mo); +int P_GetThingFloorType(mobj_t *thing); +int P_HitFloor(mobj_t *thing); +boolean P_CheckMissileSpawn(mobj_t *missile); +mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type); +mobj_t *P_SpawnMissileXYZ(fixed_t x, fixed_t y, fixed_t z, + mobj_t *source, mobj_t *dest, mobjtype_t type); +mobj_t *P_SpawnMissileAngle(mobj_t *source, mobjtype_t type, + angle_t angle, fixed_t momz); +mobj_t *P_SpawnMissileAngleSpeed(mobj_t *source, mobjtype_t type, + angle_t angle, fixed_t momz, fixed_t speed); +mobj_t *P_SpawnPlayerMissile(mobj_t *source, mobjtype_t type); +mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle); +mobj_t *P_SPMAngleXYZ(mobj_t *source, fixed_t x, fixed_t y, + fixed_t z, mobjtype_t type, angle_t angle); +void P_CreateTIDList(void); +void P_RemoveMobjFromTIDList(mobj_t *mobj); +void P_InsertMobjIntoTIDList(mobj_t *mobj, int tid); +mobj_t *P_FindMobjFromTID(int tid, int *searchPosition); +mobj_t *P_SpawnKoraxMissile(fixed_t x, fixed_t y, fixed_t z, + mobj_t *source, mobj_t *dest, mobjtype_t type); + +// ***** P_ENEMY ***** + +void P_NoiseAlert (mobj_t *target, mobj_t *emmiter); +int P_Massacre(void); +boolean A_RaiseMobj(mobj_t *actor); +boolean A_SinkMobj(mobj_t *actor); +void A_NoBlocking(mobj_t *actor); +boolean P_LookForMonsters(mobj_t *actor); +void P_InitCreatureCorpseQueue(boolean corpseScan); +void A_DeQueueCorpse(mobj_t *actor); + + +// ***** P_MAPUTL ***** + +typedef struct +{ + fixed_t x, y, dx, dy; +} divline_t; + +typedef struct +{ + fixed_t frac; // along trace line + boolean isaline; + union { + mobj_t *thing; + line_t *line; + } d; +} intercept_t; + +#define MAXINTERCEPTS 128 +extern intercept_t intercepts[MAXINTERCEPTS], *intercept_p; +typedef boolean (*traverser_t) (intercept_t *in); + + +fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); +int P_PointOnLineSide (fixed_t x, fixed_t y, line_t *line); +int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t *line); +void P_MakeDivline (line_t *li, divline_t *dl); +fixed_t P_InterceptVector (divline_t *v2, divline_t *v1); +int P_BoxOnLineSide (fixed_t *tmbox, line_t *ld); + +extern fixed_t opentop, openbottom, openrange; +extern fixed_t lowfloor; +void P_LineOpening (line_t *linedef); + +boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) ); +boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) ); + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +extern divline_t trace; +boolean P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean (*trav) (intercept_t *)); + +void P_UnsetThingPosition (mobj_t *thing); +void P_SetThingPosition (mobj_t *thing); +mobj_t *P_RoughMonsterSearch(mobj_t *mo, int distance); + +// ***** P_MAP ***** + +extern boolean floatok; // if true, move would be ok if +extern fixed_t tmfloorz, tmceilingz; // within tmfloorz - tmceilingz +extern int tmfloorpic; +extern mobj_t *BlockingMobj; + +extern line_t *ceilingline; +boolean P_TestMobjLocation(mobj_t *mobj); +boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); +mobj_t *P_CheckOnmobj(mobj_t *thing); +void P_FakeZMovement(mobj_t *mo); +boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y); +boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y); +void P_SlideMove(mobj_t *mo); +void P_BounceWall(mobj_t *mo); +boolean P_CheckSight(mobj_t *t1, mobj_t *t2); +void P_UseLines(player_t *player); +boolean P_UsePuzzleItem(player_t *player, int itemType); +void PIT_ThrustSpike(mobj_t *actor); + +boolean P_ChangeSector (sector_t *sector, int crunch); + +extern mobj_t *PuffSpawned; // true if a puff was spawned +extern mobj_t *linetarget; // who got hit (or NULL) +fixed_t P_AimLineAttack (mobj_t *t1, angle_t angle, fixed_t distance); + +void P_LineAttack (mobj_t *t1, angle_t angle, fixed_t distance, fixed_t slope, int damage); + +void P_RadiusAttack (mobj_t *spot, mobj_t *source, int damage, int distance, + boolean damageSource); + +// ***** P_SETUP ***** + +extern byte *rejectmatrix; // for fast sight rejection +extern short *blockmaplump; // offsets in blockmap are from here +extern short *blockmap; +extern int bmapwidth, bmapheight; // in mapblocks +extern fixed_t bmaporgx, bmaporgy; // origin of block map +extern mobj_t **blocklinks; // for thing chains + +// ***** P_INTER ***** + +extern int clipmana[NUMMANA]; + +void P_SetMessage(player_t *player, char *message, boolean ultmsg); +void P_SetYellowMessage(player_t *player, char *message, boolean ultmsg); +void P_ClearMessage(player_t *player); +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher); +void P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, + int damage); +void P_FallingDamage(player_t *player); +void P_PoisonPlayer(player_t *player, mobj_t *poisoner, int poison); +void P_PoisonDamage(player_t *player, mobj_t *source, int damage, + boolean playPainSound); +boolean P_GiveMana(player_t *player, manatype_t mana, int count); +boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo); +boolean P_GiveArmor(player_t *player, armortype_t armortype, int amount); +boolean P_GiveBody(player_t *player, int num); +boolean P_GivePower(player_t *player, powertype_t power); +boolean P_MorphPlayer(player_t *player); + +// ***** AM_MAP ***** + +boolean AM_Responder(event_t *ev); +void AM_Ticker(void); +void AM_Drawer(void); + +// ***** A_ACTION ***** +boolean A_LocalQuake(byte *args, mobj_t *victim); +void P_SpawnDirt(mobj_t *actor, fixed_t radius); +void A_BridgeRemove(mobj_t *actor); + +// ***** SB_BAR ***** + +extern int SB_state; +extern int ArtifactFlash; +void SB_PaletteFlash(boolean forceChange); + +// ===== PO_MAN ===== + +typedef enum +{ + PODOOR_NONE, + PODOOR_SLIDE, + PODOOR_SWING, +} podoortype_t; + +typedef struct +{ + thinker_t thinker; + int polyobj; + int speed; + unsigned int dist; + int angle; + fixed_t xSpeed; // for sliding walls + fixed_t ySpeed; +} polyevent_t; + +typedef struct +{ + thinker_t thinker; + int polyobj; + int speed; + int dist; + int totalDist; + int direction; + fixed_t xSpeed, ySpeed; + int tics; + int waitTics; + podoortype_t type; + boolean close; +} polydoor_t; + +enum +{ + PO_ANCHOR_TYPE = 3000, + PO_SPAWN_TYPE, + PO_SPAWNCRUSH_TYPE +}; + +#define PO_LINE_START 1 // polyobj line start special +#define PO_LINE_EXPLICIT 5 + +extern polyobj_t *polyobjs; // list of all poly-objects on the level +extern int po_NumPolyobjs; + +void T_PolyDoor(polydoor_t *pd); +void T_RotatePoly(polyevent_t *pe); +boolean EV_RotatePoly(line_t *line, byte *args, int direction, boolean + overRide); +void T_MovePoly(polyevent_t *pe); +boolean EV_MovePoly(line_t *line, byte *args, boolean timesEight, boolean + overRide); +boolean EV_OpenPolyDoor(line_t *line, byte *args, podoortype_t type); + +boolean PO_MovePolyobj(int num, int x, int y); +boolean PO_RotatePolyobj(int num, angle_t angle); +void PO_Init(int lump); +boolean PO_Busy(int polyobj); + +#include "p_spec.h" + +#endif // __P_LOCAL__ diff --git a/src/hexen/p_map.c b/src/hexen/p_map.c new file mode 100644 index 00000000..c1b053b6 --- /dev/null +++ b/src/hexen/p_map.c @@ -0,0 +1,2296 @@ + +//************************************************************************** +//** +//** p_map.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_map.c,v $ +//** $Revision: 1.107 $ +//** $Date: 95/10/13 04:26:47 $ +//** $Author: paul $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj); + +/* +=============================================================================== + +NOTES: + + +=============================================================================== +*/ + +/* +=============================================================================== + +mobj_t NOTES + +mobj_ts are used to tell the refresh where to draw an image, tell the world simulation when objects are contacted, and tell the sound driver how to position a sound. + +The refresh uses the next and prev links to follow lists of things in sectors as they are being drawn. The sprite, frame, and angle elements determine which patch_t is used to draw the sprite if it is visible. The sprite and frame values are allmost allways set from state_t structures. The statescr.exe utility generates the states.h and states.c files that contain the sprite/frame numbers from the statescr.txt source file. The xyz origin point represents a point at the bottom middle of the sprite (between the feet of a biped). This is the default origin position for patch_ts grabbed with lumpy.exe. A walking creature will have its z equal to the floor it is standing on. + +The sound code uses the x,y, and subsector fields to do stereo positioning of any sound effited by the mobj_t. + +The play simulation uses the blocklinks, x,y,z, radius, height to determine when mobj_ts are touching each other, touching lines in the map, or hit by trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has various bit flags used by the simulation. + + +Every mobj_t is linked into a single sector based on it's origin coordinates. +The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can be found with subsector->sector. The sector links are only used by the rendering code, the play simulation does not care about them at all. + +Any mobj_t that needs to be acted upon be something else in the play world (block movement, be shot, etc) will also need to be linked into the blockmap. If the thing has the MF_NOBLOCK flag set, it will not use the block links. It can still interact with other things, but only as the instigator (missiles will run into other things, but nothing can run into a missile). Each block in the grid is 128*128 units, and knows about every line_t that it contains a piece of, and every interactable mobj_t that has it's origin contained. + +A valid mobj_t is a mobj_t that has the proper subsector_t filled in for it's xy coordinates and is linked into the subsector's sector or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even if MF_NOSECTOR is set), and is linked into a blockmap block or has the MF_NOBLOCKMAP flag set. Links should only be modified by the P_[Un]SetThingPosition () functions. Do not change the MF_NO? flags while a thing is valid. + + +=============================================================================== +*/ + +fixed_t tmbbox[4]; +mobj_t *tmthing; +mobj_t *tsthing; +int tmflags; +fixed_t tmx, tmy; + +boolean floatok; // if true, move would be ok if + // within tmfloorz - tmceilingz + +fixed_t tmfloorz, tmceilingz, tmdropoffz; +int tmfloorpic; + +// keep track of the line that lowers the ceiling, so missiles don't explode +// against sky hack walls +line_t *ceilingline; + +// keep track of special lines as they are hit, but don't process them +// until the move is proven valid +#define MAXSPECIALCROSS 8 +line_t *spechit[MAXSPECIALCROSS]; +int numspechit; + +mobj_t *onmobj; // generic global onmobj...used for landing on pods/players +mobj_t *BlockingMobj; + +/* +=============================================================================== + + TELEPORT MOVE + +=============================================================================== +*/ + +/* +================== += += PIT_StompThing += +================== +*/ + +boolean PIT_StompThing (mobj_t *thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE) ) + return true; + + blockdist = thing->radius + tmthing->radius; + if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) + return true; // didn't hit it + + if (thing == tmthing) + return true; // don't clip against self + + if(!(tmthing->flags2&MF2_TELESTOMP)) + { // Not allowed to stomp things + return(false); + } + + P_DamageMobj (thing, tmthing, tmthing, 10000); + + return true; +} + + +/* +=================== += += P_TeleportMove += +=================== +*/ + +boolean P_TeleportMove (mobj_t *thing, fixed_t x, fixed_t y) +{ + int xl,xh,yl,yh,bx,by; + subsector_t *newsubsec; + +// +// kill anything occupying the position +// + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = NULL; + +// +// the base floor / ceiling is from the subsector that contains the +// point. Any contacted lines the step closer together will adjust them +// + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + tmfloorpic = newsubsec->sector->floorpic; + + validcount++; + numspechit = 0; + +// +// stomp on any things contacted +// + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) + return false; + +// +// the move is ok, so link the thing into its new position +// + P_UnsetThingPosition (thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + return true; +} + + +boolean PIT_ThrustStompThing (mobj_t *thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE) ) + return true; + + blockdist = thing->radius + tsthing->radius; + if ( abs(thing->x - tsthing->x) >= blockdist || + abs(thing->y - tsthing->y) >= blockdist || + (thing->z > tsthing->z+tsthing->height) ) + return true; // didn't hit it + + if (thing == tsthing) + return true; // don't clip against self + + P_DamageMobj (thing, tsthing, tsthing, 10001); + tsthing->args[1] = 1; // Mark thrust thing as bloody + + return true; +} + + + +void PIT_ThrustSpike(mobj_t *actor) +{ + int xl,xh,yl,yh,bx,by; + int x0,x2,y0,y2; + + tsthing = actor; + + x0 = actor->x - actor->info->radius; + x2 = actor->x + actor->info->radius; + y0 = actor->y - actor->info->radius; + y2 = actor->y + actor->info->radius; + + xl = (x0 - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (x2 - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (y0 - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (y2 - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + // stomp on any things contacted + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockThingsIterator(bx,by,PIT_ThrustStompThing); +} + + + +/* +=============================================================================== + + MOVEMENT ITERATOR FUNCTIONS + +=============================================================================== +*/ + +/* +================== += += PIT_CheckLine += += Adjusts tmfloorz and tmceilingz as lines are contacted +================== +*/ + +boolean PIT_CheckLine(line_t *ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + { + return(true); + } + if(P_BoxOnLineSide(tmbbox, ld) != -1) + { + return(true); + } + +// a line has been hit +/* += += The moving thing's destination position will cross the given line. += If this should not be allowed, return false. += If the line is special, keep track of it to process later if the move += is proven ok. NOTE: specials are NOT sorted by order, so two special lines += that are only 8 pixels apart could be crossed in either order. +*/ + + if(!ld->backsector) + { // One sided line + if (tmthing->flags2&MF2_BLASTED) + { + P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5); + } + CheckForPushSpecial(ld, 0, tmthing); + return(false); + } + if(!(tmthing->flags&MF_MISSILE)) + { + if(ld->flags&ML_BLOCKING) + { // Explicitly blocking everything + if (tmthing->flags2&MF2_BLASTED) + { + P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5); + } + CheckForPushSpecial(ld, 0, tmthing); + return(false); + } + if(!tmthing->player && ld->flags&ML_BLOCKMONSTERS) + { // Block monsters only + if (tmthing->flags2&MF2_BLASTED) + { + P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5); + } + return(false); + } + } + P_LineOpening(ld); // set openrange, opentop, openbottom + // adjust floor / ceiling heights + if(opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + } + if(openbottom > tmfloorz) + { + tmfloorz = openbottom; + } + if(lowfloor < tmdropoffz) + { + tmdropoffz = lowfloor; + } + if(ld->special) + { // Contacted a special line, add it to the list + spechit[numspechit] = ld; + numspechit++; + } + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC PIT_CheckThing +// +//--------------------------------------------------------------------------- + +boolean PIT_CheckThing(mobj_t *thing) +{ + fixed_t blockdist; + boolean solid; + int damage; + + if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE))) + { // Can't hit thing + return(true); + } + blockdist = thing->radius+tmthing->radius; + if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist) + { // Didn't hit thing + return(true); + } + if(thing == tmthing) + { // Don't clip against self + return(true); + } + BlockingMobj = thing; + if(tmthing->flags2&MF2_PASSMOBJ) + { // check if a mobj passed over/under another object + if(tmthing->type == MT_BISHOP && thing->type == MT_BISHOP) + { // don't let bishops fly over other bishops + return false; + } + if(tmthing->z >= thing->z+thing->height + && !(thing->flags&MF_SPECIAL)) + { + return(true); + } + else if(tmthing->z+tmthing->height < thing->z + && !(thing->flags&MF_SPECIAL)) + { // under thing + return(true); + } + } + // Check for skulls slamming into things + if(tmthing->flags&MF_SKULLFLY) + { + if(tmthing->type == MT_MINOTAUR) + { + // Slamming minotaurs shouldn't move non-creatures + if (!(thing->flags&MF_COUNTKILL)) + { + return(false); + } + } + else if(tmthing->type == MT_HOLY_FX) + { + if(thing->flags&MF_SHOOTABLE && thing != tmthing->target) + { + if(netgame && !deathmatch && thing->player) + { // don't attack other co-op players + return true; + } + if(thing->flags2&MF2_REFLECTIVE + && (thing->player || thing->flags2&MF2_BOSS)) + { + tmthing->special1 = (int)tmthing->target; + tmthing->target = thing; + return true; + } + if(thing->flags&MF_COUNTKILL || thing->player) + { + tmthing->special1 = (int)thing; + } + if(P_Random() < 96) + { + damage = 12; + if(thing->player || thing->flags2&MF2_BOSS) + { + damage = 3; + // ghost burns out faster when attacking players/bosses + tmthing->health -= 6; + } + P_DamageMobj(thing, tmthing, tmthing->target, damage); + if(P_Random() < 128) + { + P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z, + MT_HOLY_PUFF); + S_StartSound(tmthing, SFX_SPIRIT_ATTACK); + if(thing->flags&MF_COUNTKILL && P_Random() < 128 + && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT)) + { + if ((thing->type == MT_CENTAUR) || + (thing->type == MT_CENTAURLEADER) || + (thing->type == MT_ETTIN)) + { + S_StartSound(thing, SFX_PUPPYBEAT); + } + } + } + } + if(thing->health <= 0) + { + tmthing->special1 = 0; + } + } + return true; + } + damage = ((P_Random()%8)+1)*tmthing->damage; + P_DamageMobj(thing, tmthing, tmthing, damage); + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + P_SetMobjState(tmthing, tmthing->info->seestate); + return(false); + } + // Check for blasted thing running into another + if(tmthing->flags2&MF2_BLASTED && thing->flags&MF_SHOOTABLE) + { + if (!(thing->flags2&MF2_BOSS) && + (thing->flags&MF_COUNTKILL)) + { + thing->momx += tmthing->momx; + thing->momy += tmthing->momy; + if ((thing->momx + thing->momy) > 3*FRACUNIT) + { + damage = (tmthing->info->mass/100)+1; + P_DamageMobj(thing, tmthing, tmthing, damage); + damage = (thing->info->mass/100)+1; + P_DamageMobj(tmthing, thing, thing, damage>>2); + } + return(false); + } + } + // Check for missile + if(tmthing->flags&MF_MISSILE) + { + // Check for a non-shootable mobj + if(thing->flags2&MF2_NONSHOOTABLE) + { + return true; + } + // Check if it went over / under + if(tmthing->z > thing->z+thing->height) + { // Over thing + return(true); + } + if(tmthing->z+tmthing->height < thing->z) + { // Under thing + return(true); + } + if(tmthing->flags2&MF2_FLOORBOUNCE) + { + if(tmthing->target == thing || !(thing->flags&MF_SOLID)) + { + return true; + } + else + { + return false; + } + } + if(tmthing->type == MT_LIGHTNING_FLOOR + || tmthing->type == MT_LIGHTNING_CEILING) + { + if(thing->flags&MF_SHOOTABLE && thing != tmthing->target) + { + if(thing->info->mass != MAXINT) + { + thing->momx += tmthing->momx>>4; + thing->momy += tmthing->momy>>4; + } + if((!thing->player && !(thing->flags2&MF2_BOSS)) + || !(leveltime&1)) + { + if(thing->type == MT_CENTAUR + || thing->type == MT_CENTAURLEADER) + { // Lightning does more damage to centaurs + P_DamageMobj(thing, tmthing, tmthing->target, 9); + } + else + { + P_DamageMobj(thing, tmthing, tmthing->target, 3); + } + if(!(S_GetSoundPlayingInfo(tmthing, + SFX_MAGE_LIGHTNING_ZAP))) + { + S_StartSound(tmthing, SFX_MAGE_LIGHTNING_ZAP); + } + if(thing->flags&MF_COUNTKILL && P_Random() < 64 + && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT)) + { + if ((thing->type == MT_CENTAUR) || + (thing->type == MT_CENTAURLEADER) || + (thing->type == MT_ETTIN)) + { + S_StartSound(thing, SFX_PUPPYBEAT); + } + } + } + tmthing->health--; + if(tmthing->health <= 0 || thing->health <= 0) + { + return false; + } + if(tmthing->type == MT_LIGHTNING_FLOOR) + { + if(tmthing->special2 + && !((mobj_t *)tmthing->special2)->special1) + { + ((mobj_t *)tmthing->special2)->special1 = + (int)thing; + } + } + else if(!tmthing->special1) + { + tmthing->special1 = (int)thing; + } + } + return true; // lightning zaps through all sprites + } + else if(tmthing->type == MT_LIGHTNING_ZAP) + { + mobj_t *lmo; + + if(thing->flags&MF_SHOOTABLE && thing != tmthing->target) + { + lmo = (mobj_t *)tmthing->special2; + if(lmo) + { + if(lmo->type == MT_LIGHTNING_FLOOR) + { + if(lmo->special2 + && !((mobj_t *)lmo->special2)->special1) + { + ((mobj_t *)lmo->special2)->special1 = (int)thing; + } + } + else if(!lmo->special1) + { + lmo->special1 = (int)thing; + } + if(!(leveltime&3)) + { + lmo->health--; + } + } + } + } + else if(tmthing->type == MT_MSTAFF_FX2 && thing != tmthing->target) + { + if(!thing->player && !(thing->flags2&MF2_BOSS)) + { + switch(thing->type) + { + case MT_FIGHTER_BOSS: // these not flagged boss + case MT_CLERIC_BOSS: // so they can be blasted + case MT_MAGE_BOSS: + break; + default: + P_DamageMobj(thing, tmthing, tmthing->target, 10); + return true; + break; + } + } + } + if(tmthing->target && tmthing->target->type == thing->type) + { // Don't hit same species as originator + if(thing == tmthing->target) + { // Don't missile self + return(true); + } + if(!thing->player) + { // Hit same species as originator, explode, no damage + return(false); + } + } + if(!(thing->flags&MF_SHOOTABLE)) + { // Didn't do any damage + return!(thing->flags&MF_SOLID); + } + if(tmthing->flags2&MF2_RIP) + { + if (!(thing->flags&MF_NOBLOOD) && + !(thing->flags2&MF2_REFLECTIVE) && + !(thing->flags2&MF2_INVULNERABLE)) + { // Ok to spawn some blood + P_RipperBlood(tmthing); + } + //S_StartSound(tmthing, sfx_ripslop); + damage = ((P_Random()&3)+2)*tmthing->damage; + P_DamageMobj(thing, tmthing, tmthing->target, damage); + if(thing->flags2&MF2_PUSHABLE + && !(tmthing->flags2&MF2_CANNOTPUSH)) + { // Push thing + thing->momx += tmthing->momx>>2; + thing->momy += tmthing->momy>>2; + } + numspechit = 0; + return(true); + } + // Do damage + damage = ((P_Random()%8)+1)*tmthing->damage; + if(damage) + { + if (!(thing->flags&MF_NOBLOOD) && + !(thing->flags2&MF2_REFLECTIVE) && + !(thing->flags2&MF2_INVULNERABLE) && + !(tmthing->type == MT_TELOTHER_FX1) && + !(tmthing->type == MT_TELOTHER_FX2) && + !(tmthing->type == MT_TELOTHER_FX3) && + !(tmthing->type == MT_TELOTHER_FX4) && + !(tmthing->type == MT_TELOTHER_FX5) && + (P_Random() < 192)) + { + P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing); + } + P_DamageMobj(thing, tmthing, tmthing->target, damage); + } + return(false); + } + if(thing->flags2&MF2_PUSHABLE && !(tmthing->flags2&MF2_CANNOTPUSH)) + { // Push thing + thing->momx += tmthing->momx>>2; + thing->momy += tmthing->momy>>2; + } + // Check for special thing + if(thing->flags&MF_SPECIAL) + { + solid = thing->flags&MF_SOLID; + if(tmflags&MF_PICKUP) + { // Can be picked up by tmthing + P_TouchSpecialThing(thing, tmthing); // Can remove thing + } + return(!solid); + } + return(!(thing->flags&MF_SOLID)); +} + +//--------------------------------------------------------------------------- +// +// PIT_CheckOnmobjZ +// +//--------------------------------------------------------------------------- + +boolean PIT_CheckOnmobjZ(mobj_t *thing) +{ + fixed_t blockdist; + + if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE))) + { // Can't hit thing + return(true); + } + blockdist = thing->radius+tmthing->radius; + if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist) + { // Didn't hit thing + return(true); + } + if(thing == tmthing) + { // Don't clip against self + return(true); + } + if(tmthing->z > thing->z+thing->height) + { + return(true); + } + else if(tmthing->z+tmthing->height < thing->z) + { // under thing + return(true); + } + if(thing->flags&MF_SOLID) + { + onmobj = thing; + } + return(!(thing->flags&MF_SOLID)); +} + +/* +=============================================================================== + + MOVEMENT CLIPPING + +=============================================================================== +*/ + +//---------------------------------------------------------------------------- +// +// FUNC P_TestMobjLocation +// +// Returns true if the mobj is not blocked by anything at its current +// location, otherwise returns false. +// +//---------------------------------------------------------------------------- + +boolean P_TestMobjLocation(mobj_t *mobj) +{ + int flags; + + flags = mobj->flags; + mobj->flags &= ~MF_PICKUP; + if(P_CheckPosition(mobj, mobj->x, mobj->y)) + { // XY is ok, now check Z + mobj->flags = flags; + if((mobj->z < mobj->floorz) + || (mobj->z+mobj->height > mobj->ceilingz)) + { // Bad Z + return(false); + } + return(true); + } + mobj->flags = flags; + return(false); +} + +/* +================== += += P_CheckPosition += += This is purely informative, nothing is modified (except things picked up) + +in: +a mobj_t (can be valid or invalid) +a position to be checked (doesn't need to be related to the mobj_t->x,y) + +during: +special things are touched if MF_PICKUP +early out on solid lines? + +out: +newsubsec +floorz +ceilingz +tmdropoffz = the lowest point contacted (monsters won't move to a dropoff) +speciallines[] +numspeciallines +mobj_t *BlockingMobj = pointer to thing that blocked position (NULL if not +blocked, or blocked by a line). + +================== +*/ + +boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y) +{ + int xl,xh,yl,yh,bx,by; + subsector_t *newsubsec; + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = NULL; + +// +// the base floor / ceiling is from the subsector that contains the +// point. Any contacted lines the step closer together will adjust them +// + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + tmfloorpic = newsubsec->sector->floorpic; + + validcount++; + numspechit = 0; + + if(tmflags&MF_NOCLIP && !(tmflags&MF_SKULLFLY)) + { + return true; + } + +// +// check things first, possibly picking things up +// the bounding box is extended by MAXRADIUS because mobj_ts are grouped +// into mapblocks based on their origin point, and can overlap into adjacent +// blocks by up to MAXRADIUS units +// + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + BlockingMobj = NULL; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) + return false; +// +// check lines +// + if(tmflags&MF_NOCLIP) + { + return true; + } + + BlockingMobj = NULL; + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) + return false; + return true; +} + +//============================================================================= +// +// P_CheckOnmobj(mobj_t *thing) +// +// Checks if the new Z position is legal +//============================================================================= + +mobj_t *P_CheckOnmobj(mobj_t *thing) +{ + int xl,xh,yl,yh,bx,by; + subsector_t *newsubsec; + fixed_t x; + fixed_t y; + mobj_t oldmo; + + x = thing->x; + y = thing->y; + tmthing = thing; + tmflags = thing->flags; + oldmo = *thing; // save the old mobj before the fake zmovement + P_FakeZMovement(tmthing); + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = NULL; + +// +// the base floor / ceiling is from the subsector that contains the +// point. Any contacted lines the step closer together will adjust them +// + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + tmfloorpic = newsubsec->sector->floorpic; + + validcount++; + numspechit = 0; + + if ( tmflags & MF_NOCLIP ) + return NULL; + +// +// check things first, possibly picking things up +// the bounding box is extended by MAXRADIUS because mobj_ts are grouped +// into mapblocks based on their origin point, and can overlap into adjacent +// blocks by up to MAXRADIUS units +// + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_CheckOnmobjZ)) + { + *tmthing = oldmo; + return onmobj; + } + *tmthing = oldmo; + return NULL; +} + +//============================================================================= +// +// P_FakeZMovement +// +// Fake the zmovement so that we can check if a move is legal +//============================================================================= + +void P_FakeZMovement(mobj_t *mo) +{ + int dist; + int delta; +// +// adjust height +// + mo->z += mo->momz; + if(mo->flags&MF_FLOAT && mo->target) + { // float down towards target if too close + if(!(mo->flags&MF_SKULLFLY) && !(mo->flags&MF_INFLOAT)) + { + dist = P_AproxDistance(mo->x-mo->target->x, mo->y-mo->target->y); + delta =( mo->target->z+(mo->height>>1))-mo->z; + if (delta < 0 && dist < -(delta*3)) + mo->z -= FLOATSPEED; + else if (delta > 0 && dist < (delta*3)) + mo->z += FLOATSPEED; + } + } + if(mo->player && mo->flags2&MF2_FLY && !(mo->z <= mo->floorz) + && leveltime&2) + { + mo->z += finesine[(FINEANGLES/20*leveltime>>2)&FINEMASK]; + } + +// +// clip movement +// + if(mo->z <= mo->floorz) + { // Hit the floor + mo->z = mo->floorz; + if(mo->momz < 0) + { + mo->momz = 0; + } + if(mo->flags&MF_SKULLFLY) + { // The skull slammed into something + mo->momz = -mo->momz; + } + if(mo->info->crashstate && (mo->flags&MF_CORPSE)) + { + return; + } + } + else if(mo->flags2&MF2_LOGRAV) + { + if(mo->momz == 0) + mo->momz = -(GRAVITY>>3)*2; + else + mo->momz -= GRAVITY>>3; + } + else if (! (mo->flags & MF_NOGRAVITY) ) + { + if (mo->momz == 0) + mo->momz = -GRAVITY*2; + else + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { // hit the ceiling + if (mo->momz > 0) + mo->momz = 0; + mo->z = mo->ceilingz - mo->height; + if (mo->flags & MF_SKULLFLY) + { // the skull slammed into something + mo->momz = -mo->momz; + } + } +} + +//=========================================================================== +// +// CheckForPushSpecial +// +//=========================================================================== + +static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj) +{ + if (line->special) + { + if(mobj->flags2&MF2_PUSHWALL) + { + P_ActivateLine(line, mobj, side, SPAC_PUSH); + } + else if(mobj->flags2&MF2_IMPACT) + { + P_ActivateLine(line, mobj, side, SPAC_IMPACT); + } + } +} + +/* +=================== += += P_TryMove += += Attempt to move to a new position, crossing special lines unless MF_TELEPORT += is set += +=================== +*/ + +boolean P_TryMove (mobj_t *thing, fixed_t x, fixed_t y) +{ + fixed_t oldx, oldy; + int side, oldside; + line_t *ld; + + floatok = false; + if(!P_CheckPosition(thing, x, y)) + { // Solid wall or thing + if(!BlockingMobj || BlockingMobj->player + || !thing->player) + { + goto pushline; + } + else if (BlockingMobj->z+BlockingMobj->height-thing->z + > 24*FRACUNIT + || (BlockingMobj->subsector->sector->ceilingheight + -(BlockingMobj->z+BlockingMobj->height) < thing->height) + || (tmceilingz-(BlockingMobj->z+BlockingMobj->height) + < thing->height)) + { + goto pushline; + } + } + if(!(thing->flags&MF_NOCLIP)) + { + if(tmceilingz-tmfloorz < thing->height) + { // Doesn't fit + goto pushline; + } + floatok = true; + if(!(thing->flags&MF_TELEPORT) + && tmceilingz-thing->z < thing->height + && thing->type != MT_LIGHTNING_CEILING + && !(thing->flags2&MF2_FLY)) + { // mobj must lower itself to fit + goto pushline; + } + if(thing->flags2&MF2_FLY) + { + if(thing->z+thing->height > tmceilingz) + { + thing->momz = -8*FRACUNIT; + goto pushline; + } + else if(thing->z < tmfloorz && tmfloorz-tmdropoffz > 24*FRACUNIT) + { + thing->momz = 8*FRACUNIT; + goto pushline; + } + } + if(!(thing->flags&MF_TELEPORT) + // The Minotaur floor fire (MT_MNTRFX2) can step up any amount + && thing->type != MT_MNTRFX2 && thing->type != MT_LIGHTNING_FLOOR + && tmfloorz-thing->z > 24*FRACUNIT) + { + goto pushline; + } + if (!(thing->flags&(MF_DROPOFF|MF_FLOAT)) && + (tmfloorz-tmdropoffz > 24*FRACUNIT) && + !(thing->flags2&MF2_BLASTED)) + { // Can't move over a dropoff unless it's been blasted + return(false); + } + if(thing->flags2&MF2_CANTLEAVEFLOORPIC + && (tmfloorpic != thing->subsector->sector->floorpic + || tmfloorz-thing->z != 0)) + { // must stay within a sector of a certain floor type + return false; + } + } + +// +// the move is ok, so link the thing into its new position +// + P_UnsetThingPosition (thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->floorpic = tmfloorpic; + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + if(thing->flags2&MF2_FLOORCLIP) + { + if(thing->z == thing->subsector->sector->floorheight + && P_GetThingFloorType(thing) >= FLOOR_LIQUID) + { + thing->floorclip = 10*FRACUNIT; + } + else + { + thing->floorclip = 0; + } + } + +// +// if any special lines were hit, do the effect +// + if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) + { + while (numspechit > 0) + { + numspechit--; + // see if the line was crossed + ld = spechit[numspechit]; + side = P_PointOnLineSide (thing->x, thing->y, ld); + oldside = P_PointOnLineSide (oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + { + if(thing->player) + { + P_ActivateLine(ld, thing, oldside, SPAC_CROSS); + } + else if(thing->flags2&MF2_MCROSS) + { + P_ActivateLine(ld, thing, oldside, SPAC_MCROSS); + } + else if(thing->flags2&MF2_PCROSS) + { + P_ActivateLine(ld, thing, oldside, SPAC_PCROSS); + } + } + } + } + } + return true; + +pushline: + if(!(thing->flags&(MF_TELEPORT|MF_NOCLIP))) + { + int numSpecHitTemp; + + if (tmthing->flags2&MF2_BLASTED) + { + P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5); + } + numSpecHitTemp = numspechit; + while (numSpecHitTemp > 0) + { + numSpecHitTemp--; + // see if the line was crossed + ld = spechit[numSpecHitTemp]; + side = P_PointOnLineSide (thing->x, thing->y, ld); + CheckForPushSpecial(ld, side, thing); + } + } + return false; +} + +/* +================== += += P_ThingHeightClip += += Takes a valid thing and adjusts the thing->floorz, thing->ceilingz, += anf possibly thing->z += += This is called for all nearby monsters whenever a sector changes height += += If the thing doesn't fit, the z will be set to the lowest value and += false will be returned +================== +*/ + +boolean P_ThingHeightClip (mobj_t *thing) +{ + boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition (thing, thing->x, thing->y); + // what about stranding a monster partially off an edge? + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->floorpic = tmfloorpic; + + if (onfloor) + { // walking monsters rise and fall with the floor + if((thing->z-thing->floorz < 9*FRACUNIT) + || (thing->flags&MF_NOGRAVITY)) + { + thing->z = thing->floorz; + } + } + else + { // don't adjust a floating monster unless forced to + if (thing->z+thing->height > thing->ceilingz) + thing->z = thing->ceilingz - thing->height; + } + + if (thing->ceilingz - thing->floorz < thing->height) + return false; + + return true; +} + + +/* +============================================================================== + + SLIDE MOVE + +Allows the player to slide along any angled walls + +============================================================================== +*/ + +fixed_t bestslidefrac, secondslidefrac; +line_t *bestslideline, *secondslideline; +mobj_t *slidemo; + +fixed_t tmxmove, tmymove; + +/* +================== += += P_HitSlideLine += += Adjusts the xmove / ymove so that the next move will slide along the wall +================== +*/ + +void P_HitSlideLine (line_t *ld) +{ + int side; + angle_t lineangle, moveangle, deltaangle; + fixed_t movelen, newlen; + + + if (ld->slopetype == ST_HORIZONTAL) + { + tmymove = 0; + return; + } + if (ld->slopetype == ST_VERTICAL) + { + tmxmove = 0; + return; + } + + side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); + if (side == 1) + lineangle += ANG180; + moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); + deltaangle = moveangle-lineangle; + if (deltaangle > ANG180) + deltaangle += ANG180; +// I_Error ("SlideLine: ang>ANG180"); + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = P_AproxDistance (tmxmove, tmymove); + newlen = FixedMul (movelen, finecosine[deltaangle]); + tmxmove = FixedMul (newlen, finecosine[lineangle]); + tmymove = FixedMul (newlen, finesine[lineangle]); +} + +/* +============== += += PTR_SlideTraverse += +============== +*/ + +boolean PTR_SlideTraverse (intercept_t *in) +{ + line_t *li; + + if (!in->isaline) + I_Error ("PTR_SlideTraverse: not a line?"); + + li = in->d.line; + if ( ! (li->flags & ML_TWOSIDED) ) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + return true; // don't hit the back side + goto isblocking; + } + + P_LineOpening (li); // set openrange, opentop, openbottom + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24*FRACUNIT ) + goto isblocking; // too big a step up + + return true; // this line doesn't block movement + +// the line does block movement, see if it is closer than best so far +isblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop +} + + +/* +================== += += P_SlideMove += += The momx / momy move is bad, so try to slide along a wall += += Find the first line hit, move flush to it, and slide along it += += This is a kludgy mess. +================== +*/ + +void P_SlideMove (mobj_t *mo) +{ + fixed_t leadx, leady; + fixed_t trailx, traily; + fixed_t newx, newy; + int hitcount; + + slidemo = mo; + hitcount = 0; +retry: + if (++hitcount == 3) + goto stairstep; // don't loop forever + +// +// trace along the three leading corners +// + if (mo->momx > 0) + { + leadx = mo->x + mo->radius; + trailx = mo->x - mo->radius; + } + else + { + leadx = mo->x - mo->radius; + trailx = mo->x + mo->radius; + } + + if (mo->momy > 0) + { + leady = mo->y + mo->radius; + traily = mo->y - mo->radius; + } + else + { + leady = mo->y - mo->radius; + traily = mo->y + mo->radius; + } + + bestslidefrac = FRACUNIT+1; + + P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse ); + P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse ); + P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy, + PT_ADDLINES, PTR_SlideTraverse ); + +// +// move up to the wall +// + if(bestslidefrac == FRACUNIT+1) + { // the move must have hit the middle, so stairstep +stairstep: + if(!P_TryMove(mo, mo->x, mo->y+mo->momy)) + { + P_TryMove(mo, mo->x+mo->momx, mo->y); + } + return; + } + + bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit + if (bestslidefrac > 0) + { + newx = FixedMul (mo->momx, bestslidefrac); + newy = FixedMul (mo->momy, bestslidefrac); + if (!P_TryMove (mo, mo->x+newx, mo->y+newy)) + goto stairstep; + } + +// +// now continue along the wall +// + bestslidefrac = FRACUNIT-(bestslidefrac+0x800); // remainder + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + if (bestslidefrac <= 0) + return; + + tmxmove = FixedMul (mo->momx, bestslidefrac); + tmymove = FixedMul (mo->momy, bestslidefrac); + + P_HitSlideLine (bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove)) + { + goto retry; + } +} + +//============================================================================ +// +// PTR_BounceTraverse +// +//============================================================================ + +boolean PTR_BounceTraverse(intercept_t *in) +{ + line_t *li; + + if (!in->isaline) + I_Error ("PTR_BounceTraverse: not a line?"); + + li = in->d.line; + if (!(li->flags&ML_TWOSIDED)) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + return true; // don't hit the back side + goto bounceblocking; + } + + P_LineOpening (li); // set openrange, opentop, openbottom + if (openrange < slidemo->height) + goto bounceblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto bounceblocking; // mobj is too high + return true; // this line doesn't block movement + +// the line does block movement, see if it is closer than best so far +bounceblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + return false; // stop +} + +//============================================================================ +// +// P_BounceWall +// +//============================================================================ + +void P_BounceWall(mobj_t *mo) +{ + fixed_t leadx, leady; + int side; + angle_t lineangle, moveangle, deltaangle; + fixed_t movelen; + + slidemo = mo; + +// +// trace along the three leading corners +// + if(mo->momx > 0) + { + leadx = mo->x+mo->radius; + } + else + { + leadx = mo->x-mo->radius; + } + if(mo->momy > 0) + { + leady = mo->y+mo->radius; + } + else + { + leady = mo->y-mo->radius; + } + bestslidefrac = FRACUNIT+1; + P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_BounceTraverse); + + side = P_PointOnLineSide(mo->x, mo->y, bestslideline); + lineangle = R_PointToAngle2(0, 0, bestslideline->dx, + bestslideline->dy); + if(side == 1) + lineangle += ANG180; + moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy); + deltaangle = (2*lineangle)-moveangle; +// if (deltaangle > ANG180) +// deltaangle += ANG180; +// I_Error ("SlideLine: ang>ANG180"); + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = P_AproxDistance(mo->momx, mo->momy); + movelen = FixedMul(movelen, 0.75*FRACUNIT); // friction + if (movelen < FRACUNIT) movelen = 2*FRACUNIT; + mo->momx = FixedMul(movelen, finecosine[deltaangle]); + mo->momy = FixedMul(movelen, finesine[deltaangle]); +} + + +/* +============================================================================== + + P_LineAttack + +============================================================================== +*/ + + +mobj_t *PuffSpawned; +mobj_t *linetarget; // who got hit (or NULL) +mobj_t *shootthing; +fixed_t shootz; // height if not aiming up or down + // ???: use slope for monsters? +int la_damage; +fixed_t attackrange; + +fixed_t aimslope; + +extern fixed_t topslope, bottomslope; // slopes to top and bottom of target + +/* +=============================================================================== += += PTR_AimTraverse += += Sets linetaget and aimslope when a target is aimed at +=============================================================================== +*/ + +boolean PTR_AimTraverse (intercept_t *in) +{ + line_t *li; + mobj_t *th; + fixed_t slope, thingtopslope, thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + if ( !(li->flags & ML_TWOSIDED) ) + return false; // stop +// +// crosses a two sided line +// a two sided line will restrict the possible target ranges + P_LineOpening (li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul (attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - shootz , dist); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - shootz , dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + +// +// shoot a thing +// + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + if(!(th->flags&MF_SHOOTABLE)) + { // corpse or something + return true; + } + if(th->player && netgame && !deathmatch) + { // don't aim at fellow co-op players + return true; + } + +// check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + if (thingtopslope < bottomslope) + return true; // shot over the thing + thingbottomslope = FixedDiv (th->z - shootz, dist); + if (thingbottomslope > topslope) + return true; // shot under the thing + +// +// this thing can be hit! +// + if (thingtopslope > topslope) + thingtopslope = topslope; + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope+thingbottomslope)/2; + linetarget = th; + + return false; // don't go any farther +} + + +/* +============================================================================== += += PTR_ShootTraverse += +============================================================================== +*/ + +boolean PTR_ShootTraverse (intercept_t *in) +{ + fixed_t x,y,z; + fixed_t frac; + line_t *li; + mobj_t *th; + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope, thingbottomslope; + + extern mobj_t LavaInflictor; + + if (in->isaline) + { + li = in->d.line; + if (li->special) + { + P_ActivateLine(li, shootthing, 0, SPAC_IMPACT); +// P_ShootSpecialLine (shootthing, li); + } + if ( !(li->flags & ML_TWOSIDED) ) + goto hitline; + +// +// crosses a two sided line +// + P_LineOpening (li); + + dist = FixedMul (attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - shootz , dist); + if (slope > aimslope) + goto hitline; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - shootz , dist); + if (slope < aimslope) + goto hitline; + } + + return true; // shot continues +// +// hit line +// +hitline: + // position a bit closer + frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + if (z > li->frontsector->ceilingheight) + return false; // don't shoot the sky! + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + return false; // it's a sky hack wall + } + + P_SpawnPuff (x,y,z); + return false; // don't go any farther + } + +// +// shoot a thing +// + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + +// +// check for physical attacks on a ghost +// +/* FIX: Impliment Heretic 2 weapons here + if(th->flags&MF_SHADOW && shootthing->player->readyweapon == wp_staff) + { + return(true); + } +*/ + +// check angles to see if the thing can be aimed at + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + if (thingtopslope < aimslope) + return true; // shot over the thing + thingbottomslope = FixedDiv (th->z - shootz, dist); + if (thingbottomslope > aimslope) + return true; // shot under the thing + +// +// hit thing +// + // position a bit closer + frac = in->frac - FixedDiv (10*FRACUNIT,attackrange); + x = trace.x + FixedMul(trace.dx, frac); + y = trace.y + FixedMul(trace.dy, frac); + z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); + P_SpawnPuff(x, y, z); + if(la_damage) + { + if (!(in->d.thing->flags&MF_NOBLOOD) && + !(in->d.thing->flags2&MF2_INVULNERABLE)) + { + if(PuffType == MT_AXEPUFF || PuffType == MT_AXEPUFF_GLOW) + { + P_BloodSplatter2(x, y, z, in->d.thing); + } + if(P_Random() < 192) + { + P_BloodSplatter(x, y, z, in->d.thing); + } + } + if(PuffType == MT_FLAMEPUFF2) + { // Cleric FlameStrike does fire damage + P_DamageMobj(th, &LavaInflictor, shootthing, la_damage); + } + else + { + P_DamageMobj(th, shootthing, shootthing, la_damage); + } + } + return(false); // don't go any farther +} + +/* +================= += += P_AimLineAttack += +================= +*/ + +fixed_t P_AimLineAttack (mobj_t *t1, angle_t angle, fixed_t distance) +{ + fixed_t x2, y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + topslope = 100*FRACUNIT/160; // can't shoot outside view angles + bottomslope = -100*FRACUNIT/160; + attackrange = distance; + linetarget = NULL; + + P_PathTraverse ( t1->x, t1->y, x2, y2 + , PT_ADDLINES|PT_ADDTHINGS, PTR_AimTraverse ); + + if (linetarget) + return aimslope; + return 0; +} + + + +/* +================= += += P_LineAttack += += if damage == 0, it is just a test trace that will leave linetarget set += +================= +*/ + +void P_LineAttack (mobj_t *t1, angle_t angle, fixed_t distance, fixed_t slope, int damage) +{ + fixed_t x2, y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + shootz -= t1->floorclip; + attackrange = distance; + aimslope = slope; + + if(P_PathTraverse(t1->x, t1->y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, + PTR_ShootTraverse)) + { + switch(PuffType) + { + case MT_PUNCHPUFF: + S_StartSound(t1, SFX_FIGHTER_PUNCH_MISS); + break; + case MT_HAMMERPUFF: + case MT_AXEPUFF: + case MT_AXEPUFF_GLOW: + S_StartSound(t1, SFX_FIGHTER_HAMMER_MISS); + break; + case MT_FLAMEPUFF: + P_SpawnPuff(x2, y2, shootz+FixedMul(slope, distance)); + break; + default: + break; + } + } +} + +/* +============================================================================== + + USE LINES + +============================================================================== +*/ + +mobj_t *usething; + +boolean PTR_UseTraverse (intercept_t *in) +{ + int sound; + fixed_t pheight; + + if (!in->d.line->special) + { + P_LineOpening (in->d.line); + if (openrange <= 0) + { + if(usething->player) + { + switch(usething->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PLAYER_FIGHTER_FAILED_USE; + break; + case PCLASS_CLERIC: + sound = SFX_PLAYER_CLERIC_FAILED_USE; + break; + case PCLASS_MAGE: + sound = SFX_PLAYER_MAGE_FAILED_USE; + break; + case PCLASS_PIG: + sound = SFX_PIG_ACTIVE1; + break; + default: + sound = SFX_NONE; + break; + } + S_StartSound(usething, sound); + } + return false; // can't use through a wall + } + if(usething->player) + { + pheight = usething->z + (usething->height/2); + if ((opentop < pheight) || (openbottom > pheight)) + { + switch(usething->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PLAYER_FIGHTER_FAILED_USE; + break; + case PCLASS_CLERIC: + sound = SFX_PLAYER_CLERIC_FAILED_USE; + break; + case PCLASS_MAGE: + sound = SFX_PLAYER_MAGE_FAILED_USE; + break; + case PCLASS_PIG: + sound = SFX_PIG_ACTIVE1; + break; + default: + sound = SFX_NONE; + break; + } + S_StartSound(usething, sound); + } + } + return true ; // not a special line, but keep checking + } + + if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) + return false; // don't use back sides + +// P_UseSpecialLine (usething, in->d.line); + P_ActivateLine(in->d.line, usething, 0, SPAC_USE); + + return false; // can't use for than one special line in a row +} + + +/* +================ += += P_UseLines += += Looks for special lines in front of the player to activate +================ +*/ + +void P_UseLines (player_t *player) +{ + int angle; + fixed_t x1, y1, x2, y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; + y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; + + P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); +} + +//========================================================================== +// +// PTR_PuzzleItemTraverse +// +//========================================================================== + +#define USE_PUZZLE_ITEM_SPECIAL 129 + +static mobj_t *PuzzleItemUser; +static int PuzzleItemType; +static boolean PuzzleActivated; + +boolean PTR_PuzzleItemTraverse(intercept_t *in) +{ + mobj_t *mobj; + int sound; + + if(in->isaline) + { // Check line + if(in->d.line->special != USE_PUZZLE_ITEM_SPECIAL) + { + P_LineOpening(in->d.line); + if(openrange <= 0) + { + sound = SFX_NONE; + if(PuzzleItemUser->player) + { + switch(PuzzleItemUser->player->class) + { + case PCLASS_FIGHTER: + sound = SFX_PUZZLE_FAIL_FIGHTER; + break; + case PCLASS_CLERIC: + sound = SFX_PUZZLE_FAIL_CLERIC; + break; + case PCLASS_MAGE: + sound = SFX_PUZZLE_FAIL_MAGE; + break; + default: + sound = SFX_NONE; + break; + } + } + S_StartSound(PuzzleItemUser, sound); + return false; // can't use through a wall + } + return true; // Continue searching + } + if(P_PointOnLineSide(PuzzleItemUser->x, PuzzleItemUser->y, + in->d.line) == 1) + { // Don't use back sides + return false; + } + if(PuzzleItemType != in->d.line->arg1) + { // Item type doesn't match + return false; + } + P_StartACS(in->d.line->arg2, 0, &in->d.line->arg3, + PuzzleItemUser, in->d.line, 0); + in->d.line->special = 0; + PuzzleActivated = true; + return false; // Stop searching + } + // Check thing + mobj = in->d.thing; + if(mobj->special != USE_PUZZLE_ITEM_SPECIAL) + { // Wrong special + return true; + } + if(PuzzleItemType != mobj->args[0]) + { // Item type doesn't match + return true; + } + P_StartACS(mobj->args[1], 0, &mobj->args[2], PuzzleItemUser, NULL, 0); + mobj->special = 0; + PuzzleActivated = true; + return false; // Stop searching +} + +//========================================================================== +// +// P_UsePuzzleItem +// +// Returns true if the puzzle item was used on a line or a thing. +// +//========================================================================== + +boolean P_UsePuzzleItem(player_t *player, int itemType) +{ + int angle; + fixed_t x1, y1, x2, y2; + + PuzzleItemType = itemType; + PuzzleItemUser = player->mo; + PuzzleActivated = false; + angle = player->mo->angle>>ANGLETOFINESHIFT; + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1+(USERANGE>>FRACBITS)*finecosine[angle]; + y2 = y1+(USERANGE>>FRACBITS)*finesine[angle]; + P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS, + PTR_PuzzleItemTraverse); + return PuzzleActivated; +} + +/* +============================================================================== + + RADIUS ATTACK + +============================================================================== +*/ + +mobj_t *bombsource; +mobj_t *bombspot; +int bombdamage; +int bombdistance; +boolean DamageSource; + +/* +================= += += PIT_RadiusAttack += += Source is the creature that casued the explosion at spot +================= +*/ + +boolean PIT_RadiusAttack (mobj_t *thing) +{ + fixed_t dx, dy, dist; + int damage; + + if(!(thing->flags&MF_SHOOTABLE)) + { + return true; + } +// if(thing->flags2&MF2_BOSS) +// { // Bosses take no damage from PIT_RadiusAttack +// return(true); +// } + if(!DamageSource && thing == bombsource) + { // don't damage the source of the explosion + return true; + } + if(abs((thing->z-bombspot->z)>>FRACBITS) > 2*bombdistance) + { // too high/low + return true; + } + dx = abs(thing->x-bombspot->x); + dy = abs(thing->y-bombspot->y); + dist = dx > dy ? dx : dy; + dist = (dist-thing->radius)>>FRACBITS; + if(dist < 0) + { + dist = 0; + } + if(dist >= bombdistance) + { // Out of range + return true; + } + if(P_CheckSight(thing, bombspot)) + { // OK to damage, target is in direct path + damage = (bombdamage*(bombdistance-dist)/bombdistance)+1; + if(thing->player) + { + damage >>= 2; + } + P_DamageMobj(thing, bombspot, bombsource, damage); + } + return(true); +} + +/* +================= += += P_RadiusAttack += += Source is the creature that caused the explosion at spot +================= +*/ + +void P_RadiusAttack (mobj_t *spot, mobj_t *source, int damage, int distance, + boolean damageSource) +{ + int x,y, xl, xh, yl, yh; + fixed_t dist; + + dist = (distance+MAXRADIUS)<y+dist-bmaporgy)>>MAPBLOCKSHIFT; + yl = (spot->y-dist-bmaporgy)>>MAPBLOCKSHIFT; + xh = (spot->x+dist-bmaporgx)>>MAPBLOCKSHIFT; + xl = (spot->x-dist-bmaporgx)>>MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + bombdistance = distance; + DamageSource = damageSource; + for (y = yl; y <= yh; y++) + { + for (x = xl; x <= xh; x++) + { + P_BlockThingsIterator(x, y, PIT_RadiusAttack); + } + } +} + +/* +============================================================================== + + SECTOR HEIGHT CHANGING + += After modifying a sectors floor or ceiling height, call this += routine to adjust the positions of all things that touch the += sector. += += If anything doesn't fit anymore, true will be returned. += If crunch is true, they will take damage as they are being crushed += If Crunch is false, you should set the sector height back the way it += was and call P_ChangeSector again to undo the changes +============================================================================== +*/ + +int crushchange; +boolean nofit; + +/* +=============== += += PIT_ChangeSector += +=============== +*/ + +boolean PIT_ChangeSector (mobj_t *thing) +{ + mobj_t *mo; + + if (P_ThingHeightClip (thing)) + return true; // keep checking + + // crunch bodies to giblets + if ((thing->flags&MF_CORPSE) && (thing->health <= 0)) + { + if (thing->flags&MF_NOBLOOD) + { + P_RemoveMobj (thing); + } + else + { + if (thing->state != &states[S_GIBS1]) + { + P_SetMobjState (thing, S_GIBS1); + thing->height = 0; + thing->radius = 0; + S_StartSound(thing, SFX_PLAYER_FALLING_SPLAT); + } + } + return true; // keep checking + } + + // crunch dropped items + if (thing->flags2&MF2_DROPPED) + { + P_RemoveMobj (thing); + return true; // keep checking + } + + if (! (thing->flags & MF_SHOOTABLE) ) + return true; // assume it is bloody gibs or something + + nofit = true; + if (crushchange && !(leveltime&3)) + { + P_DamageMobj(thing, NULL, NULL, crushchange); + // spray blood in a random direction + if ((!(thing->flags&MF_NOBLOOD)) && + (!(thing->flags2&MF2_INVULNERABLE))) + { + mo = P_SpawnMobj (thing->x, thing->y, thing->z + thing->height/2, + MT_BLOOD); + mo->momx = (P_Random() - P_Random ())<<12; + mo->momy = (P_Random() - P_Random ())<<12; + } + } + + return true; // keep checking (crush other things) +} + +/* +=============== += += P_ChangeSector += +=============== +*/ + +boolean P_ChangeSector (sector_t *sector, int crunch) +{ + int x,y; + + nofit = false; + crushchange = crunch; + +// recheck heights for all things near the moving sector + + for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) + for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) + P_BlockThingsIterator (x, y, PIT_ChangeSector); + + + return nofit; +} + diff --git a/src/hexen/p_maputl.c b/src/hexen/p_maputl.c new file mode 100644 index 00000000..7bd59fd0 --- /dev/null +++ b/src/hexen/p_maputl.c @@ -0,0 +1,1056 @@ + +//************************************************************************** +//** +//** p_maputl.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_maputl.c,v $ +//** $Revision: 1.11 $ +//** $Date: 95/10/04 02:39:28 $ +//** $Author: paul $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" + +static mobj_t *RoughBlockCheck(mobj_t *mo, int index); + +/* +=================== += += P_AproxDistance += += Gives an estimation of distance (not exact) += +=================== +*/ + +fixed_t P_AproxDistance (fixed_t dx, fixed_t dy) +{ + dx = abs(dx); + dy = abs(dy); + if (dx < dy) + return dx+dy-(dx>>1); + return dx+dy-(dy>>1); +} + + +/* +================== += += P_PointOnLineSide += += Returns 0 or 1 +================== +*/ + +int P_PointOnLineSide (fixed_t x, fixed_t y, line_t *line) +{ + fixed_t dx,dy; + fixed_t left, right; + + if (!line->dx) + { + if (x <= line->v1->x) + return line->dy > 0; + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->v1->y) + return line->dx < 0; + return line->dx > 0; + } + + dx = (x - line->v1->x); + dy = (y - line->v1->y); + + left = FixedMul ( line->dy>>FRACBITS , dx ); + right = FixedMul ( dy , line->dx>>FRACBITS ); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +/* +================= += += P_BoxOnLineSide += += Considers the line to be infinite += Returns side 0 or 1, -1 if box crosses the line +================= +*/ + +int P_BoxOnLineSide (fixed_t *tmbox, line_t *ld) +{ + int p1, p2; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = tmbox[BOXTOP] > ld->v1->y; + p2 = tmbox[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + case ST_VERTICAL: + p1 = tmbox[BOXRIGHT] < ld->v1->x; + p2 = tmbox[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + case ST_POSITIVE: + p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + case ST_NEGATIVE: + p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) + return p1; + return -1; +} + +/* +================== += += P_PointOnDivlineSide += += Returns 0 or 1 +================== +*/ + +int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t *line) +{ + fixed_t dx,dy; + fixed_t left, right; + + if (!line->dx) + { + if (x <= line->x) + return line->dy > 0; + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->y) + return line->dx < 0; + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + +// try to quickly decide by looking at sign bits + if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) + { + if ( (line->dy ^ dx) & 0x80000000 ) + return 1; // (left is negative) + return 0; + } + + left = FixedMul ( line->dy>>8, dx>>8 ); + right = FixedMul ( dy>>8 , line->dx>>8 ); + + if (right < left) + return 0; // front side + return 1; // back side +} + + + +/* +============== += += P_MakeDivline += +============== +*/ + +void P_MakeDivline (line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + + +/* +=============== += += P_InterceptVector += += Returns the fractional intercept point along the first divline += += This is only called by the addthings and addlines traversers +=============== +*/ + +fixed_t P_InterceptVector (divline_t *v2, divline_t *v1) +{ +#if 1 + fixed_t frac, num, den; + + den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); + if (den == 0) + return 0; +// I_Error ("P_InterceptVector: parallel"); + num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) + + FixedMul ( (v2->y - v1->y)>>8 , v1->dx); + frac = FixedDiv (num , den); + + return frac; +#else +float frac, num, den, v1x,v1y,v1dx,v1dy,v2x,v2y,v2dx,v2dy; + + v1x = (float)v1->x/FRACUNIT; + v1y = (float)v1->y/FRACUNIT; + v1dx = (float)v1->dx/FRACUNIT; + v1dy = (float)v1->dy/FRACUNIT; + v2x = (float)v2->x/FRACUNIT; + v2y = (float)v2->y/FRACUNIT; + v2dx = (float)v2->dx/FRACUNIT; + v2dy = (float)v2->dy/FRACUNIT; + + den = v1dy*v2dx - v1dx*v2dy; + if (den == 0) + return 0; // parallel + num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + frac = num / den; + + return frac*FRACUNIT; +#endif +} + +/* +================== += += P_LineOpening += += Sets opentop and openbottom to the window through a two sided line += OPTIMIZE: keep this precalculated +================== +*/ + +fixed_t opentop, openbottom, openrange; +fixed_t lowfloor; + +void P_LineOpening (line_t *linedef) +{ + sector_t *front, *back; + + if (linedef->sidenum[1] == -1) + { // single sided line + openrange = 0; + return; + } + + front = linedef->frontsector; + back = linedef->backsector; + + if (front->ceilingheight < back->ceilingheight) + opentop = front->ceilingheight; + else + opentop = back->ceilingheight; + if (front->floorheight > back->floorheight) + { + openbottom = front->floorheight; + lowfloor = back->floorheight; + tmfloorpic = front->floorpic; + } + else + { + openbottom = back->floorheight; + lowfloor = front->floorheight; + tmfloorpic = back->floorpic; + } + + openrange = opentop - openbottom; +} + +/* +=============================================================================== + + THING POSITION SETTING + +=============================================================================== +*/ + +/* +=================== += += P_UnsetThingPosition += += Unlinks a thing from block map and sectors += +=================== +*/ + +void P_UnsetThingPosition (mobj_t *thing) +{ + int blockx, blocky; + + if ( ! (thing->flags & MF_NOSECTOR) ) + { // inert things don't need to be in blockmap +// unlink from subsector + if (thing->snext) + thing->snext->sprev = thing->sprev; + if (thing->sprev) + thing->sprev->snext = thing->snext; + else + thing->subsector->sector->thinglist = thing->snext; + } + + if ( ! (thing->flags & MF_NOBLOCKMAP) ) + { // inert things don't need to be in blockmap +// unlink from block map + if (thing->bnext) + thing->bnext->bprev = thing->bprev; + if (thing->bprev) + thing->bprev->bnext = thing->bnext; + else + { + blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; + if (blockx>=0 && blockx < bmapwidth + && blocky>=0 && blocky bnext; + } + } +} + + +/* +=================== += += P_SetThingPosition += += Links a thing into both a block and a subsector based on it's x y += Sets thing->subsector properly += +=================== +*/ + +void P_SetThingPosition (mobj_t *thing) +{ + subsector_t *ss; + sector_t *sec; + int blockx, blocky; + mobj_t **link; + +// +// link into subsector +// + ss = R_PointInSubsector (thing->x,thing->y); + thing->subsector = ss; + if ( ! (thing->flags & MF_NOSECTOR) ) + { // invisible things don't go into the sector links + sec = ss->sector; + + thing->sprev = NULL; + thing->snext = sec->thinglist; + if (sec->thinglist) + sec->thinglist->sprev = thing; + sec->thinglist = thing; + } + +// +// link into blockmap +// + if ( ! (thing->flags & MF_NOBLOCKMAP) ) + { // inert things don't need to be in blockmap + blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; + if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky bprev = NULL; + thing->bnext = *link; + if (*link) + (*link)->bprev = thing; + *link = thing; + } + else + { // thing is off the map + thing->bnext = thing->bprev = NULL; + } + } +} + + + +/* +=============================================================================== + + BLOCK MAP ITERATORS + +For each line/thing in the given mapblock, call the passed function. +If the function returns false, exit with false without checking anything else. + +=============================================================================== +*/ + +/* +================== += += P_BlockLinesIterator += += The validcount flags are used to avoid checking lines += that are marked in multiple mapblocks, so increment validcount before += the first call to P_BlockLinesIterator, then make one or more calls to it +=================== +*/ + +boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) ) +{ + int offset; + short *list; + line_t *ld; + + int i; + polyblock_t *polyLink; + seg_t **tempSeg; + extern polyblock_t **PolyBlockMap; + + if (x < 0 || y<0 || x>=bmapwidth || y>=bmapheight) + return true; + offset = y*bmapwidth+x; + + polyLink = PolyBlockMap[offset]; + while(polyLink) + { + if(polyLink->polyobj) + { + if(polyLink->polyobj->validcount != validcount) + { + polyLink->polyobj->validcount = validcount; + tempSeg = polyLink->polyobj->segs; + for(i = 0; i < polyLink->polyobj->numsegs; i++, tempSeg++) + { + if((*tempSeg)->linedef->validcount == validcount) + { + continue; + } + (*tempSeg)->linedef->validcount = validcount; + if(!func((*tempSeg)->linedef)) + { + return false; + } + } + } + } + polyLink = polyLink->next; + } + + offset = *(blockmap+offset); + + for ( list = blockmaplump+offset ; *list != -1 ; list++) + { + ld = &lines[*list]; + if (ld->validcount == validcount) + continue; // line has already been checked + ld->validcount = validcount; + + if ( !func(ld) ) + return false; + } + + return true; // everything was checked +} + + +/* +================== += += P_BlockThingsIterator += +================== +*/ + +boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) ) +{ + mobj_t *mobj; + + if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) + return true; + + for (mobj = blocklinks[y*bmapwidth+x] ; mobj ; mobj = mobj->bnext) + if (!func( mobj ) ) + return false; + + return true; +} + +/* +=============================================================================== + + INTERCEPT ROUTINES + +=============================================================================== +*/ + +intercept_t intercepts[MAXINTERCEPTS], *intercept_p; + +divline_t trace; +boolean earlyout; +int ptflags; + +/* +================== += += PIT_AddLineIntercepts += += Looks for lines in the given block that intercept the given trace += to add to the intercepts list += A line is crossed if its endpoints are on opposite sides of the trace += Returns true if earlyout and a solid line hit +================== +*/ + +boolean PIT_AddLineIntercepts (line_t *ld) +{ + int s1, s2; + fixed_t frac; + divline_t dl; + +// avoid precision problems with two routines + if ( trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 + || trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) + { + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide (trace.x, trace.y, ld); + s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); + } + if (s1 == s2) + return true; // line isn't crossed + +// +// hit the line +// + P_MakeDivline (ld, &dl); + frac = P_InterceptVector (&trace, &dl); + if (frac < 0) + return true; // behind source + +// try to early out the check + if (earlyout && frac < FRACUNIT && !ld->backsector) + return false; // stop checking + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercept_p++; + + return true; // continue +} + + + +/* +================== += += PIT_AddThingIntercepts += +================== +*/ + +boolean PIT_AddThingIntercepts (mobj_t *thing) +{ + fixed_t x1,y1, x2,y2; + int s1, s2; + boolean tracepositive; + divline_t dl; + fixed_t frac; + + tracepositive = (trace.dx ^ trace.dy)>0; + + // check a corner to corner crossection for hit + + if (tracepositive) + { + x1 = thing->x - thing->radius; + y1 = thing->y + thing->radius; + + x2 = thing->x + thing->radius; + y2 = thing->y - thing->radius; + } + else + { + x1 = thing->x - thing->radius; + y1 = thing->y - thing->radius; + + x2 = thing->x + thing->radius; + y2 = thing->y + thing->radius; + } + s1 = P_PointOnDivlineSide (x1, y1, &trace); + s2 = P_PointOnDivlineSide (x2, y2, &trace); + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2-x1; + dl.dy = y2-y1; + frac = P_InterceptVector (&trace, &dl); + if (frac < 0) + return true; // behind source + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercept_p++; + + return true; // keep going +} + + +/* +==================== += += P_TraverseIntercepts += += Returns true if the traverser function returns true for all lines +==================== +*/ + +boolean P_TraverseIntercepts ( traverser_t func, fixed_t maxfrac ) +{ + int count; + fixed_t dist; + intercept_t *scan, *in; + + count = intercept_p - intercepts; + in = 0; // shut up compiler warning + + while (count--) + { + dist = MAXINT; + for (scan = intercepts ; scanfrac < dist) + { + dist = scan->frac; + in = scan; + } + + if (dist > maxfrac) + return true; // checked everything in range +#if 0 + { // don't check these yet, ther may be others inserted + in = scan = intercepts; + for ( scan = intercepts ; scanfrac > maxfrac) + *in++ = *scan; + intercept_p = in; + return false; + } +#endif + + if ( !func (in) ) + return false; // don't bother going farther + in->frac = MAXINT; + } + + return true; // everything was traversed +} + + + +/* +================== += += P_PathTraverse += += Traces a line from x1,y1 to x2,y2, calling the traverser function for each += Returns true if the traverser function returns true for all lines +================== +*/ + +boolean P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean (*trav) (intercept_t *)) +{ + fixed_t xt1,yt1,xt2,yt2; + fixed_t xstep,ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy, mapxstep, mapystep; + int count; + + earlyout = flags & PT_EARLYOUT; + + validcount++; + intercept_p = intercepts; + + if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) + x1 += FRACUNIT; // don't side exactly on a line + if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) + y1 += FRACUNIT; // don't side exactly on a line + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1>>MAPBLOCKSHIFT; + yt1 = y1>>MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2>>MAPBLOCKSHIFT; + yt2 = y2>>MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep); + + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); + + +// +// step through map blocks +// Count is present to prevent a round off error from skipping the break + mapx = xt1; + mapy = yt1; + + for (count = 0 ; count < 64 ; count++) + { + if (flags & PT_ADDLINES) + { + if (!P_BlockLinesIterator (mapx, mapy,PIT_AddLineIntercepts)) + return false; // early out + } + if (flags & PT_ADDTHINGS) + { + if (!P_BlockThingsIterator (mapx, mapy,PIT_AddThingIntercepts)) + return false; // early out + } + + if (mapx == xt2 && mapy == yt2) + break; + + if ( (yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else if ( (xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + + } + + +// +// go through the sorted list +// + return P_TraverseIntercepts ( trav, FRACUNIT ); +} + +//=========================================================================== +// +// P_RoughMonsterSearch +// +// Searches though the surrounding mapblocks for monsters/players +// distance is in MAPBLOCKUNITS +//=========================================================================== + +mobj_t *P_RoughMonsterSearch(mobj_t *mo, int distance) +{ + int blockX; + int blockY; + int startX, startY; + int blockIndex; + int firstStop; + int secondStop; + int thirdStop; + int finalStop; + int count; + mobj_t *target; + + startX = (mo->x-bmaporgx)>>MAPBLOCKSHIFT; + startY = (mo->y-bmaporgy)>>MAPBLOCKSHIFT; + + if(startX >= 0 && startX < bmapwidth && startY >= 0 && startY < bmapheight) + { + if(target = RoughBlockCheck(mo, startY*bmapwidth+startX)) + { // found a target right away + return target; + } + } + for(count = 1; count <= distance; count++) + { + blockX = startX-count; + blockY = startY-count; + + if(blockY < 0) + { + blockY = 0; + } + else if(blockY >= bmapheight) + { + blockY = bmapheight-1; + } + if(blockX < 0) + { + blockX = 0; + } + else if(blockX >= bmapwidth) + { + blockX = bmapwidth-1; + } + blockIndex = blockY*bmapwidth+blockX; + firstStop = startX+count; + if(firstStop < 0) + { + continue; + } + if(firstStop >= bmapwidth) + { + firstStop = bmapwidth-1; + } + secondStop = startY+count; + if(secondStop < 0) + { + continue; + } + if(secondStop >= bmapheight) + { + secondStop = bmapheight-1; + } + thirdStop = secondStop*bmapwidth+blockX; + secondStop = secondStop*bmapwidth+firstStop; + firstStop += blockY*bmapwidth; + finalStop = blockIndex; + + // Trace the first block section (along the top) + for(; blockIndex <= firstStop; blockIndex++) + { + if(target = RoughBlockCheck(mo, blockIndex)) + { + return target; + } + } + // Trace the second block section (right edge) + for(blockIndex--; blockIndex <= secondStop; blockIndex += bmapwidth) + { + if(target = RoughBlockCheck(mo, blockIndex)) + { + return target; + } + } + // Trace the third block section (bottom edge) + for(blockIndex -= bmapwidth; blockIndex >= thirdStop; blockIndex--) + { + if(target = RoughBlockCheck(mo, blockIndex)) + { + return target; + } + } + // Trace the final block section (left edge) + for(blockIndex++; blockIndex > finalStop; blockIndex -= bmapwidth) + { + if(target = RoughBlockCheck(mo, blockIndex)) + { + return target; + } + } + } + return NULL; +} + +//=========================================================================== +// +// RoughBlockCheck +// +//=========================================================================== + +static mobj_t *RoughBlockCheck(mobj_t *mo, int index) +{ + mobj_t *link; + mobj_t *master; + angle_t angle; + + link = blocklinks[index]; + while(link) + { + if (mo->player) // Minotaur looking around player + { + if ((link->flags&MF_COUNTKILL) || + (link->player && (link != mo))) + { + if (!(link->flags&MF_SHOOTABLE)) + { + link = link->bnext; + continue; + } + if (link->flags2&MF2_DORMANT) + { + link = link->bnext; + continue; + } + if ((link->type == MT_MINOTAUR) && + (((mobj_t *)link->special1) == mo)) + { + link = link->bnext; + continue; + } + if(netgame && !deathmatch && link->player) + { + link = link->bnext; + continue; + } + if(P_CheckSight(mo, link)) + { + return link; + } + } + link = link->bnext; + } + else if (mo->type == MT_MINOTAUR) // looking around minotaur + { + master = (mobj_t *)mo->special1; + if ((link->flags&MF_COUNTKILL) || + (link->player && (link != master))) + { + if (!(link->flags&MF_SHOOTABLE)) + { + link = link->bnext; + continue; + } + if (link->flags2&MF2_DORMANT) + { + link = link->bnext; + continue; + } + if ((link->type == MT_MINOTAUR) && + (link->special1 == mo->special1)) + { + link = link->bnext; + continue; + } + if(netgame && !deathmatch && link->player) + { + link = link->bnext; + continue; + } + if(P_CheckSight(mo, link)) + { + return link; + } + } + link = link->bnext; + } + else if (mo->type == MT_MSTAFF_FX2) // bloodscourge + { + if ((link->flags&MF_COUNTKILL || + (link->player && link != mo->target)) + && !(link->flags2&MF2_DORMANT)) + { + if (!(link->flags&MF_SHOOTABLE)) + { + link = link->bnext; + continue; + } + if(netgame && !deathmatch && link->player) + { + link = link->bnext; + continue; + } + else if(P_CheckSight(mo, link)) + { + master = mo->target; + angle = R_PointToAngle2(master->x, master->y, + link->x, link->y) - master->angle; + angle >>= 24; + if (angle>226 || angle<30) + { + return link; + } + } + } + link = link->bnext; + } + else // spirits + { + if ((link->flags&MF_COUNTKILL || + (link->player && link != mo->target)) + && !(link->flags2&MF2_DORMANT)) + { + if (!(link->flags&MF_SHOOTABLE)) + { + link = link->bnext; + continue; + } + if(netgame && !deathmatch && link->player) + { + link = link->bnext; + continue; + } + if (link == mo->target) + { + link = link->bnext; + continue; + } + else if(P_CheckSight(mo, link)) + { + return link; + } + } + link = link->bnext; + } + } + return NULL; +} diff --git a/src/hexen/p_mobj.c b/src/hexen/p_mobj.c new file mode 100644 index 00000000..1f9b5830 --- /dev/null +++ b/src/hexen/p_mobj.c @@ -0,0 +1,2441 @@ + +//************************************************************************** +//** +//** p_mobj.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_mobj.c,v $ +//** $Revision: 1.133 $ +//** $Date: 96/01/12 12:31:43 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "sounds.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_TID_COUNT 200 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +void G_PlayerReborn(int player); +void P_MarkAsLeaving(mobj_t *corpse); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void P_SpawnMapThing(mapthing_t *mthing); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void PlayerLandedOnThing(mobj_t *mo, mobj_t *onmobj); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern mobj_t LavaInflictor; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +mobjtype_t PuffType; +mobj_t *MissileMobj; + +fixed_t FloatBobOffsets[64] = +{ + 0, 51389, 102283, 152192, + 200636, 247147, 291278, 332604, + 370727, 405280, 435929, 462380, + 484378, 501712, 514213, 521763, + 524287, 521763, 514213, 501712, + 484378, 462380, 435929, 405280, + 370727, 332604, 291278, 247147, + 200636, 152192, 102283, 51389, + -1, -51390, -102284, -152193, + -200637, -247148, -291279, -332605, + -370728, -405281, -435930, -462381, + -484380, -501713, -514215, -521764, + -524288, -521764, -514214, -501713, + -484379, -462381, -435930, -405280, + -370728, -332605, -291279, -247148, + -200637, -152193, -102284, -51389 +}; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int TIDList[MAX_TID_COUNT+1]; // +1 for termination marker +static mobj_t *TIDMobj[MAX_TID_COUNT]; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_SetMobjState +// +// Returns true if the mobj is still present. +// +//========================================================================== + +boolean P_SetMobjState(mobj_t *mobj, statenum_t state) +{ + state_t *st; + + if(state == S_NULL) + { // Remove mobj + mobj->state = S_NULL; + P_RemoveMobj(mobj); + return(false); + } + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + if(st->action) + { // Call action function + st->action(mobj); + } + return(true); +} + +//========================================================================== +// +// P_SetMobjStateNF +// +// Same as P_SetMobjState, but does not call the state function. +// +//========================================================================== + +boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state) +{ + state_t *st; + + if(state == S_NULL) + { // Remove mobj + mobj->state = S_NULL; + P_RemoveMobj(mobj); + return(false); + } + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + return(true); +} + +//---------------------------------------------------------------------------- +// +// PROC P_ExplodeMissile +// +//---------------------------------------------------------------------------- + +void P_ExplodeMissile(mobj_t *mo) +{ + mo->momx = mo->momy = mo->momz = 0; + P_SetMobjState(mo, mobjinfo[mo->type].deathstate); + //mo->tics -= P_Random()&3; + mo->flags &= ~MF_MISSILE; + + switch(mo->type) + { + case MT_SORCBALL1: + case MT_SORCBALL2: + case MT_SORCBALL3: + S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE); + break; + case MT_SORCFX1: + S_StartSound(NULL, SFX_SORCERER_HEADSCREAM); + break; + default: + if(mo->info->deathsound) + { + S_StartSound(mo, mo->info->deathsound); + } + break; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_FloorBounceMissile +// +//---------------------------------------------------------------------------- + +void P_FloorBounceMissile(mobj_t *mo) +{ + if(P_HitFloor(mo) >= FLOOR_LIQUID) + { + switch(mo->type) + { + case MT_SORCFX1: + case MT_SORCBALL1: + case MT_SORCBALL2: + case MT_SORCBALL3: + break; + default: + P_RemoveMobj(mo); + return; + } + } + switch(mo->type) + { + case MT_SORCFX1: + mo->momz = -mo->momz; // no energy absorbed + break; + case MT_SGSHARD1: + case MT_SGSHARD2: + case MT_SGSHARD3: + case MT_SGSHARD4: + case MT_SGSHARD5: + case MT_SGSHARD6: + case MT_SGSHARD7: + case MT_SGSHARD8: + case MT_SGSHARD9: + case MT_SGSHARD0: + mo->momz = FixedMul(mo->momz, -0.3*FRACUNIT); + if(abs(mo->momz) < (FRACUNIT/2)) + { + P_SetMobjState(mo, S_NULL); + return; + } + break; + default: + mo->momz = FixedMul(mo->momz, -0.7*FRACUNIT); + break; + } + mo->momx = 2*mo->momx/3; + mo->momy = 2*mo->momy/3; + if(mo->info->seesound) + { + switch(mo->type) + { + case MT_SORCBALL1: + case MT_SORCBALL2: + case MT_SORCBALL3: + if (!mo->args[0]) S_StartSound(mo, mo->info->seesound); + break; + default: + S_StartSound(mo, mo->info->seesound); + break; + } + S_StartSound(mo, mo->info->seesound); + } +// P_SetMobjState(mo, mobjinfo[mo->type].deathstate); +} + +//---------------------------------------------------------------------------- +// +// PROC P_ThrustMobj +// +//---------------------------------------------------------------------------- + +void P_ThrustMobj(mobj_t *mo, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + mo->momx += FixedMul(move, finecosine[angle]); + mo->momy += FixedMul(move, finesine[angle]); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_FaceMobj +// +// Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs +// to turn counter clockwise. 'delta' is set to the amount 'source' +// needs to turn. +// +//---------------------------------------------------------------------------- + +int P_FaceMobj(mobj_t *source, mobj_t *target, angle_t *delta) +{ + angle_t diff; + angle_t angle1; + angle_t angle2; + + angle1 = source->angle; + angle2 = R_PointToAngle2(source->x, source->y, target->x, target->y); + if(angle2 > angle1) + { + diff = angle2-angle1; + if(diff > ANGLE_180) + { + *delta = ANGLE_MAX-diff; + return(0); + } + else + { + *delta = diff; + return(1); + } + } + else + { + diff = angle1-angle2; + if(diff > ANGLE_180) + { + *delta = ANGLE_MAX-diff; + return(1); + } + else + { + *delta = diff; + return(0); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// The missile special1 field must be mobj_t *target. Returns true if +// target was tracked, false if not. +// +//---------------------------------------------------------------------------- + +boolean P_SeekerMissile(mobj_t *actor, angle_t thresh, angle_t turnMax) +{ + int dir; + int dist; + angle_t delta; + angle_t angle; + mobj_t *target; + + target = (mobj_t *)actor->special1; + if(target == NULL) + { + return(false); + } + if(!(target->flags&MF_SHOOTABLE)) + { // Target died + actor->special1 = 0; + return(false); + } + dir = P_FaceMobj(actor, target, &delta); + if(delta > thresh) + { + delta >>= 1; + if(delta > turnMax) + { + delta = turnMax; + } + } + if(dir) + { // Turn clockwise + actor->angle += delta; + } + else + { // Turn counter clockwise + actor->angle -= delta; + } + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[angle]); + actor->momy = FixedMul(actor->info->speed, finesine[angle]); + if(actor->z+actor->height < target->z + || target->z+target->height < actor->z) + { // Need to seek vertically + dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); + dist = dist/actor->info->speed; + if(dist < 1) + { + dist = 1; + } + actor->momz = (target->z+(target->height>>1) + -(actor->z+(actor->height>>1)))/dist; + } + return(true); +} + +//---------------------------------------------------------------------------- +// +// PROC P_XYMovement +// +//---------------------------------------------------------------------------- + +#define STOPSPEED 0x1000 +#define FRICTION_NORMAL 0xe800 +#define FRICTION_LOW 0xf900 +#define FRICTION_FLY 0xeb00 + +void P_XYMovement(mobj_t *mo) +{ + fixed_t ptryx, ptryy; + player_t *player; + fixed_t xmove, ymove; + int special; + angle_t angle; + static int windTab[3] = {2048*5, 2048*10, 2048*25}; + + if(!mo->momx && !mo->momy) + { + if(mo->flags&MF_SKULLFLY) + { // A flying mobj slammed into something + mo->flags &= ~MF_SKULLFLY; + mo->momx = mo->momy = mo->momz = 0; + P_SetMobjState(mo, mo->info->seestate); + } + return; + } + special = mo->subsector->sector->special; + if(mo->flags2&MF2_WINDTHRUST) + { + switch(special) + { + case 40: case 41: case 42: // Wind_East + P_ThrustMobj(mo, 0, windTab[special-40]); + break; + case 43: case 44: case 45: // Wind_North + P_ThrustMobj(mo, ANG90, windTab[special-43]); + break; + case 46: case 47: case 48: // Wind_South + P_ThrustMobj(mo, ANG270, windTab[special-46]); + break; + case 49: case 50: case 51: // Wind_West + P_ThrustMobj(mo, ANG180, windTab[special-49]); + break; + } + } + player = mo->player; + if(mo->momx > MAXMOVE) + { + mo->momx = MAXMOVE; + } + else if(mo->momx < -MAXMOVE) + { + mo->momx = -MAXMOVE; + } + if(mo->momy > MAXMOVE) + { + mo->momy = MAXMOVE; + } + else if(mo->momy < -MAXMOVE) + { + mo->momy = -MAXMOVE; + } + xmove = mo->momx; + ymove = mo->momy; + do + { + if(xmove > MAXMOVE/2 || ymove > MAXMOVE/2) + { + ptryx = mo->x+xmove/2; + ptryy = mo->y+ymove/2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + if(!P_TryMove(mo, ptryx, ptryy)) + { // Blocked move + if(mo->flags2&MF2_SLIDE) + { // Try to slide along it + if(BlockingMobj == NULL) + { // Slide against wall + P_SlideMove(mo); + } + else + { // Slide against mobj + //if(P_TryMove(mo, mo->x, mo->y+mo->momy)) + if(P_TryMove(mo, mo->x, ptryy)) + { + mo->momx = 0; + } + //else if(P_TryMove(mo, mo->x+mo->momx, mo->y)) + else if(P_TryMove(mo, ptryx, mo->y)) + { + mo->momy = 0; + } + else + { + mo->momx = mo->momy = 0; + } + } + } + else if(mo->flags&MF_MISSILE) + { + if(mo->flags2&MF2_FLOORBOUNCE) + { + if(BlockingMobj) + { + if ((BlockingMobj->flags2&MF2_REFLECTIVE) || + ((!BlockingMobj->player) && + (!(BlockingMobj->flags&MF_COUNTKILL)))) + { + fixed_t speed; + + angle = R_PointToAngle2(BlockingMobj->x, + BlockingMobj->y, mo->x, mo->y) + +ANGLE_1*((P_Random()%16)-8); + speed = P_AproxDistance(mo->momx, mo->momy); + speed = FixedMul(speed, 0.75*FRACUNIT); + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->momx = FixedMul(speed, finecosine[angle]); + mo->momy = FixedMul(speed, finesine[angle]); + if(mo->info->seesound) + { + S_StartSound(mo, mo->info->seesound); + } + return; + } + else + { // Struck a player/creature + P_ExplodeMissile(mo); + } + } + else + { // Struck a wall + P_BounceWall(mo); + switch(mo->type) + { + case MT_SORCBALL1: + case MT_SORCBALL2: + case MT_SORCBALL3: + case MT_SORCFX1: + break; + default: + if(mo->info->seesound) + { + S_StartSound(mo, mo->info->seesound); + } + break; + } + return; + } + } + if(BlockingMobj && + (BlockingMobj->flags2 & MF2_REFLECTIVE)) + { + angle = R_PointToAngle2(BlockingMobj->x, + BlockingMobj->y, + mo->x, mo->y); + + // Change angle for delflection/reflection + switch(BlockingMobj->type) + { + case MT_CENTAUR: + case MT_CENTAURLEADER: + if ( abs(angle-BlockingMobj->angle)>>24 > 45) + goto explode; + if (mo->type == MT_HOLY_FX) + goto explode; + // Drop through to sorcerer full reflection + case MT_SORCBOSS: + // Deflection + if (P_Random()<128) + angle += ANGLE_45; + else + angle -= ANGLE_45; + break; + default: + // Reflection + angle += ANGLE_1 * ((P_Random()%16)-8); + break; + } + + // Reflect the missile along angle + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed>>1, finecosine[angle]); + mo->momy = FixedMul(mo->info->speed>>1, finesine[angle]); +// mo->momz = -mo->momz; + if (mo->flags2 & MF2_SEEKERMISSILE) + { + mo->special1 = (int)(mo->target); + } + mo->target = BlockingMobj; + return; + } +explode: + // Explode a missile + if(ceilingline && ceilingline->backsector + && ceilingline->backsector->ceilingpic == skyflatnum) + { // Hack to prevent missiles exploding against the sky + if(mo->type == MT_BLOODYSKULL) + { + mo->momx = mo->momy = 0; + mo->momz = -FRACUNIT; + } + else if(mo->type == MT_HOLY_FX) + { + P_ExplodeMissile(mo); + } + else + { + P_RemoveMobj(mo); + } + return; + } + P_ExplodeMissile(mo); + } + //else if(mo->info->crashstate) + //{ + // mo->momx = mo->momy = 0; + // P_SetMobjState(mo, mo->info->crashstate); + // return; + //} + else + { + mo->momx = mo->momy = 0; + } + } + } while(xmove || ymove); + + // Friction + + if(player && player->cheats&CF_NOMOMENTUM) + { // Debug option for no sliding at all + mo->momx = mo->momy = 0; + return; + } + if(mo->flags&(MF_MISSILE|MF_SKULLFLY)) + { // No friction for missiles + return; + } + if(mo->z > mo->floorz && !(mo->flags2&MF2_FLY) && !(mo->flags2&MF2_ONMOBJ)) + { // No friction when falling + if (mo->type != MT_BLASTEFFECT) + return; + } + if(mo->flags&MF_CORPSE) + { // Don't stop sliding if halfway off a step with some momentum + if(mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 + || mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) + { + if(mo->floorz != mo->subsector->sector->floorheight) + { + return; + } + } + } + if(mo->momx > -STOPSPEED && mo->momx < STOPSPEED + && mo->momy > -STOPSPEED && mo->momy < STOPSPEED + && (!player || (player->cmd.forwardmove == 0 + && player->cmd.sidemove == 0))) + { // If in a walking frame, stop moving + if(player) + { + if((unsigned)((player->mo->state-states) + -PStateRun[player->class]) < 4) + { + P_SetMobjState(player->mo, PStateNormal[player->class]); + } + } + mo->momx = 0; + mo->momy = 0; + } + else + { + if(mo->flags2&MF2_FLY && !(mo->z <= mo->floorz) + &&!(mo->flags2&MF2_ONMOBJ)) + { + mo->momx = FixedMul(mo->momx, FRICTION_FLY); + mo->momy = FixedMul(mo->momy, FRICTION_FLY); + } + else if(P_GetThingFloorType(mo) == FLOOR_ICE) + { + mo->momx = FixedMul(mo->momx, FRICTION_LOW); + mo->momy = FixedMul(mo->momy, FRICTION_LOW); + } + else + { + mo->momx = FixedMul(mo->momx, FRICTION_NORMAL); + mo->momy = FixedMul(mo->momy, FRICTION_NORMAL); + } + } +} + + +// Move this to p_inter *** +void P_MonsterFallingDamage(mobj_t *mo) +{ + int damage; + int mom; + + mom = abs(mo->momz); + if(mom > 35*FRACUNIT) + { // automatic death + damage=10000; + } + else + { + damage = ((mom - (23*FRACUNIT) )*6)>>FRACBITS; + } + damage=10000; // always kill 'em + P_DamageMobj(mo, NULL, NULL, damage); +} + + + +/* +=============== += += P_ZMovement += +=============== +*/ + +void P_ZMovement(mobj_t *mo) +{ + int dist; + int delta; +// +// check for smooth step up +// + if (mo->player && mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz-mo->z; + mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; + } +// +// adjust height +// + mo->z += mo->momz; + if(mo->flags&MF_FLOAT && mo->target) + { // float down towards target if too close + if(!(mo->flags&MF_SKULLFLY) && !(mo->flags&MF_INFLOAT)) + { + dist = P_AproxDistance(mo->x-mo->target->x, mo->y-mo->target->y); + delta =( mo->target->z+(mo->height>>1))-mo->z; + if (delta < 0 && dist < -(delta*3)) + mo->z -= FLOATSPEED; + else if (delta > 0 && dist < (delta*3)) + mo->z += FLOATSPEED; + } + } + if(mo->player && mo->flags2&MF2_FLY && !(mo->z <= mo->floorz) + && leveltime&2) + { + mo->z += finesine[(FINEANGLES/20*leveltime>>2)&FINEMASK]; + } + +// +// clip movement +// + if(mo->z <= mo->floorz) + { // Hit the floor + if(mo->flags&MF_MISSILE) + { + mo->z = mo->floorz; + if(mo->flags2&MF2_FLOORBOUNCE) + { + P_FloorBounceMissile(mo); + return; + } + else if(mo->type == MT_HOLY_FX) + { // The spirit struck the ground + mo->momz = 0; + P_HitFloor(mo); + return; + } + else if(mo->type == MT_MNTRFX2 || mo->type == MT_LIGHTNING_FLOOR) + { // Minotaur floor fire can go up steps + return; + } + else + { + P_HitFloor(mo); + P_ExplodeMissile(mo); + return; + } + } + if(mo->flags&MF_COUNTKILL) // Blasted mobj falling + { + if(mo->momz < -(23*FRACUNIT)) + { + P_MonsterFallingDamage(mo); + } + } + if(mo->z-mo->momz > mo->floorz) + { // Spawn splashes, etc. + P_HitFloor(mo); + } + mo->z = mo->floorz; + if(mo->momz < 0) + { + if(mo->flags2&MF2_ICEDAMAGE && mo->momz < -GRAVITY*8) + { + mo->tics = 1; + mo->momx = 0; + mo->momy = 0; + mo->momz = 0; + return; + } + if(mo->player) + { + mo->player->jumpTics = 7;// delay any jumping for a short time + if(mo->momz < -GRAVITY*8 && !(mo->flags2&MF2_FLY)) + { // squat down + mo->player->deltaviewheight = mo->momz>>3; + if(mo->momz < -23*FRACUNIT) + { + P_FallingDamage(mo->player); + P_NoiseAlert(mo, mo); + } + else if(mo->momz < -GRAVITY*12 && !mo->player->morphTics) + { + S_StartSound(mo, SFX_PLAYER_LAND); + switch(mo->player->class) + { + case PCLASS_FIGHTER: + S_StartSound(mo, SFX_PLAYER_FIGHTER_GRUNT); + break; + case PCLASS_CLERIC: + S_StartSound(mo, SFX_PLAYER_CLERIC_GRUNT); + break; + case PCLASS_MAGE: + S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT); + break; + default: + break; + } + } + else if ((P_GetThingFloorType(mo) < FLOOR_LIQUID) && + (!mo->player->morphTics)) + { + S_StartSound(mo, SFX_PLAYER_LAND); + } +#ifdef __WATCOMC__ + if(!useexterndriver) + { + mo->player->centering = true; + } +#else + mo->player->centering = true; +#endif + } + } + else if(mo->type >= MT_POTTERY1 + && mo->type <= MT_POTTERY3) + { + P_DamageMobj(mo, NULL, NULL, 25); + } + else if(mo->flags&MF_COUNTKILL) + { + if(mo->momz < -23*FRACUNIT) + { + // Doesn't get here + } + } + mo->momz = 0; + } + if(mo->flags&MF_SKULLFLY) + { // The skull slammed into something + mo->momz = -mo->momz; + } + if(mo->info->crashstate && + (mo->flags&MF_CORPSE) && + !(mo->flags2&MF2_ICEDAMAGE)) + { + P_SetMobjState(mo, mo->info->crashstate); + return; + } + } + else if(mo->flags2&MF2_LOGRAV) + { + if(mo->momz == 0) + mo->momz = -(GRAVITY>>3)*2; + else + mo->momz -= GRAVITY>>3; + } + else if (! (mo->flags & MF_NOGRAVITY) ) + { + if (mo->momz == 0) + mo->momz = -GRAVITY*2; + else + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { // hit the ceiling + if (mo->momz > 0) + mo->momz = 0; + mo->z = mo->ceilingz - mo->height; + if(mo->flags2&MF2_FLOORBOUNCE) + { + // Maybe reverse momentum here for ceiling bounce + // Currently won't happen + + if(mo->info->seesound) + { + S_StartSound(mo, mo->info->seesound); + } + return; + } + if (mo->flags & MF_SKULLFLY) + { // the skull slammed into something + mo->momz = -mo->momz; + } + if(mo->flags&MF_MISSILE) + { + if(mo->type == MT_LIGHTNING_CEILING) + { + return; + } + if(mo->subsector->sector->ceilingpic == skyflatnum) + { + if(mo->type == MT_BLOODYSKULL) + { + mo->momx = mo->momy = 0; + mo->momz = -FRACUNIT; + } + else if(mo->type == MT_HOLY_FX) + { + P_ExplodeMissile(mo); + } + else + { + P_RemoveMobj(mo); + } + return; + } + P_ExplodeMissile(mo); + return; + } + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_BlasterMobjThinker +// +// +//---------------------------------------------------------------------------- + +void P_BlasterMobjThinker(mobj_t *mobj) +{ + int i; + fixed_t xfrac; + fixed_t yfrac; + fixed_t zfrac; + fixed_t z; + boolean changexy; + mobj_t *mo; + + // Handle movement + if(mobj->momx || mobj->momy || + (mobj->z != mobj->floorz) || mobj->momz) + { + xfrac = mobj->momx>>3; + yfrac = mobj->momy>>3; + zfrac = mobj->momz>>3; + changexy = xfrac || yfrac; + for(i = 0; i < 8; i++) + { + if(changexy) + { + if(!P_TryMove(mobj, mobj->x+xfrac, mobj->y+yfrac)) + { // Blocked move + P_ExplodeMissile(mobj); + return; + } + } + mobj->z += zfrac; + if(mobj->z <= mobj->floorz) + { // Hit the floor + mobj->z = mobj->floorz; + P_HitFloor(mobj); + P_ExplodeMissile(mobj); + return; + } + if(mobj->z+mobj->height > mobj->ceilingz) + { // Hit the ceiling + mobj->z = mobj->ceilingz-mobj->height; + P_ExplodeMissile(mobj); + return; + } + if(changexy) + { + if(mobj->type == MT_MWAND_MISSILE && (P_Random() < 128)) + { + z = mobj->z-8*FRACUNIT; + if(z < mobj->floorz) + { + z = mobj->floorz; + } + P_SpawnMobj(mobj->x, mobj->y, z, MT_MWANDSMOKE); + } + else if(!--mobj->special1) + { + mobj->special1 = 4; + z = mobj->z-12*FRACUNIT; + if(z < mobj->floorz) + { + z = mobj->floorz; + } + mo = P_SpawnMobj(mobj->x, mobj->y, z, MT_CFLAMEFLOOR); + if(mo) + { + mo->angle = mobj->angle; + } + } + } + } + } + // Advance the state + if(mobj->tics != -1) + { + mobj->tics--; + while(!mobj->tics) + { + if(!P_SetMobjState(mobj, mobj->state->nextstate)) + { // mobj was removed + return; + } + } + } +} + +//=========================================================================== +// +// PlayerLandedOnThing +// +//=========================================================================== + +static void PlayerLandedOnThing(mobj_t *mo, mobj_t *onmobj) +{ + mo->player->deltaviewheight = mo->momz>>3; + if(mo->momz < -23*FRACUNIT) + { + P_FallingDamage(mo->player); + P_NoiseAlert(mo, mo); + } + else if(mo->momz < -GRAVITY*12 + && !mo->player->morphTics) + { + S_StartSound(mo, SFX_PLAYER_LAND); + switch(mo->player->class) + { + case PCLASS_FIGHTER: + S_StartSound(mo, SFX_PLAYER_FIGHTER_GRUNT); + break; + case PCLASS_CLERIC: + S_StartSound(mo, SFX_PLAYER_CLERIC_GRUNT); + break; + case PCLASS_MAGE: + S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT); + break; + default: + break; + } + } + else if(!mo->player->morphTics) + { + S_StartSound(mo, SFX_PLAYER_LAND); + } +#ifdef __WATCOMC__ + if(!useexterndriver) + { + mo->player->centering = true; + } +#else + mo->player->centering = true; +#endif +} + +//---------------------------------------------------------------------------- +// +// PROC P_MobjThinker +// +//---------------------------------------------------------------------------- + +void P_MobjThinker(mobj_t *mobj) +{ + mobj_t *onmo; +/* + // Reset to not blasted when momentums are gone + if((mobj->flags2&MF2_BLASTED) && (!(mobj->momx)) && (!(mobj->momy))) + ResetBlasted(mobj); +*/ + // Handle X and Y momentums + BlockingMobj = NULL; + if(mobj->momx || mobj->momy || (mobj->flags&MF_SKULLFLY)) + { + P_XYMovement(mobj); + if(mobj->thinker.function == (think_t)-1) + { // mobj was removed + return; + } + } + else if(mobj->flags2&MF2_BLASTED) + { // Reset to not blasted when momentums are gone + ResetBlasted(mobj); + } + if(mobj->flags2&MF2_FLOATBOB) + { // Floating item bobbing motion (special1 is height) + mobj->z = mobj->floorz + + mobj->special1 + + FloatBobOffsets[(mobj->health++)&63]; + } + else if((mobj->z != mobj->floorz) || mobj->momz || BlockingMobj) + { // Handle Z momentum and gravity + if(mobj->flags2&MF2_PASSMOBJ) + { + if(!(onmo = P_CheckOnmobj(mobj))) + { + P_ZMovement(mobj); + if(mobj->player && mobj->flags&MF2_ONMOBJ) + { + mobj->flags2 &= ~MF2_ONMOBJ; + } + } + else + { + if(mobj->player) + { + if(mobj->momz < -GRAVITY*8 && !(mobj->flags2&MF2_FLY)) + { + PlayerLandedOnThing(mobj, onmo); + } + if(onmo->z+onmo->height-mobj->z <= 24*FRACUNIT) + { + mobj->player->viewheight -= onmo->z+onmo->height + -mobj->z; + mobj->player->deltaviewheight = + (VIEWHEIGHT-mobj->player->viewheight)>>3; + mobj->z = onmo->z+onmo->height; + mobj->flags2 |= MF2_ONMOBJ; + mobj->momz = 0; + } + else + { // hit the bottom of the blocking mobj + mobj->momz = 0; + } + } +/* Landing on another player, and mimicking his movements + if(mobj->player && onmo->player) + { + mobj->momx = onmo->momx; + mobj->momy = onmo->momy; + if(onmo->z < onmo->floorz) + { + mobj->z += onmo->floorz-onmo->z; + if(onmo->player) + { + onmo->player->viewheight -= onmo->floorz-onmo->z; + onmo->player->deltaviewheight = (VIEWHEIGHT- + onmo->player->viewheight)>>3; + } + onmo->z = onmo->floorz; + } + } +*/ + } + } + else + { + P_ZMovement(mobj); + } + if(mobj->thinker.function == (think_t)-1) + { // mobj was removed + return; + } + } + + // Cycle through states, calling action functions at transitions + if(mobj->tics != -1) + { + mobj->tics--; + // you can cycle through multiple states in a tic + while(!mobj->tics) + { + if(!P_SetMobjState(mobj, mobj->state->nextstate)) + { // mobj was removed + return; + } + } + } +} + +//========================================================================== +// +// P_SpawnMobj +// +//========================================================================== + +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) +{ + mobj_t *mobj; + state_t *st; + mobjinfo_t *info; + fixed_t space; + + mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, NULL); + memset(mobj, 0, sizeof(*mobj)); + info = &mobjinfo[type]; + mobj->type = type; + mobj->info = info; + mobj->x = x; + mobj->y = y; + mobj->radius = info->radius; + mobj->height = info->height; + mobj->flags = info->flags; + mobj->flags2 = info->flags2; + mobj->damage = info->damage; + mobj->health = info->spawnhealth; + if(gameskill != sk_nightmare) + { + mobj->reactiontime = info->reactiontime; + } + mobj->lastlook = P_Random()%MAXPLAYERS; + + // Set the state, but do not use P_SetMobjState, because action + // routines can't be called yet. If the spawnstate has an action + // routine, it will not be called. + st = &states[info->spawnstate]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // Set subsector and/or block links. + P_SetThingPosition(mobj); + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + if(z == ONFLOORZ) + { + mobj->z = mobj->floorz; + } + else if(z == ONCEILINGZ) + { + mobj->z = mobj->ceilingz-mobj->info->height; + } + else if(z == FLOATRANDZ) + { + space = ((mobj->ceilingz)-(mobj->info->height))-mobj->floorz; + if(space > 48*FRACUNIT) + { + space -= 40*FRACUNIT; + mobj->z = ((space*P_Random())>>8)+mobj->floorz+40*FRACUNIT; + } + else + { + mobj->z = mobj->floorz; + } + } + else if (mobj->flags2&MF2_FLOATBOB) + { + mobj->z = mobj->floorz + z; // artifact z passed in as height + } + else + { + mobj->z = z; + } + if(mobj->flags2&MF2_FLOORCLIP && P_GetThingFloorType(mobj) >= FLOOR_LIQUID + && mobj->z == mobj->subsector->sector->floorheight) + { + mobj->floorclip = 10*FRACUNIT; + } + else + { + mobj->floorclip = 0; + } + + mobj->thinker.function = P_MobjThinker; + P_AddThinker(&mobj->thinker); + return(mobj); +} + +//========================================================================== +// +// P_RemoveMobj +// +//========================================================================== + +void P_RemoveMobj(mobj_t *mobj) +{ + // Remove from creature queue + if(mobj->flags&MF_COUNTKILL && + mobj->flags&MF_CORPSE) + { + A_DeQueueCorpse(mobj); + } + + if(mobj->tid) + { // Remove from TID list + P_RemoveMobjFromTIDList(mobj); + } + + // Unlink from sector and block lists + P_UnsetThingPosition(mobj); + + // Stop any playing sound + S_StopSound(mobj); + + // Free block + P_RemoveThinker((thinker_t *)mobj); +} + +//========================================================================== +// +// P_SpawnPlayer +// +// Called when a player is spawned on the level. Most of the player +// structure stays unchanged between levels. +// +//========================================================================== + +void P_SpawnPlayer(mapthing_t *mthing) +{ + player_t *p; + fixed_t x, y, z; + mobj_t *mobj; + + if(!playeringame[mthing->type-1]) + { // Not playing + return; + } + p = &players[mthing->type-1]; + if(p->playerstate == PST_REBORN) + { + G_PlayerReborn(mthing->type-1); + } + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + if(randomclass && deathmatch) + { + p->class = P_Random()%3; + if(p->class == PlayerClass[mthing->type-1]) + { + p->class = (p->class+1)%3; + } + PlayerClass[mthing->type-1] = p->class; + SB_SetClassData(); + } + else + { + p->class = PlayerClass[mthing->type-1]; + } + switch(p->class) + { + case PCLASS_FIGHTER: + mobj = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER); + break; + case PCLASS_CLERIC: + mobj = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC); + break; + case PCLASS_MAGE: + mobj = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE); + break; + default: + I_Error("P_SpawnPlayer: Unknown class type"); + break; + } + + // Set translation table data + if(p->class == PCLASS_FIGHTER && (mthing->type == 1 || mthing->type == 3)) + { + // The first type should be blue, and the third should be the + // Fighter's original gold color + if(mthing->type == 1) + { + mobj->flags |= 2<type > 1) + { // Set color translation bits for player sprites + mobj->flags |= (mthing->type-1)<angle = ANG45 * (mthing->angle/45); + mobj->player = p; + mobj->health = p->health; + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + P_ClearMessage(p); + p->damagecount = 0; + p->bonuscount = 0; + p->poisoncount = 0; + p->morphTics = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + P_SetupPsprites(p); + if(deathmatch) + { // Give all keys in death match mode + p->keys = 2047; + } +} + +//========================================================================== +// +// P_SpawnMapThing +// +// The fields of the mapthing should already be in host byte order. +// +//========================================================================== + +void P_SpawnMapThing(mapthing_t *mthing) +{ + int i; + unsigned int spawnMask; + mobj_t *mobj; + fixed_t x, y, z; + static unsigned int classFlags[] = + { + MTF_FIGHTER, + MTF_CLERIC, + MTF_MAGE + }; + + // Count deathmatch start positions + if(mthing->type == 11) + { + if(deathmatch_p < &deathmatchstarts[MAXDEATHMATCHSTARTS]) + { + memcpy(deathmatch_p, mthing, sizeof(*mthing)); + deathmatch_p++; + } + return; + } + if(mthing->type == PO_ANCHOR_TYPE) + { // Polyobj Anchor Pt. + return; + } + else if(mthing->type == PO_SPAWN_TYPE + || mthing->type == PO_SPAWNCRUSH_TYPE) + { // Polyobj Anchor Pt. + po_NumPolyobjs++; + return; + } + + // Check for player starts 1 to 4 + if(mthing->type <= 4) + { + playerstarts[mthing->arg1][mthing->type-1] = *mthing; + if(!deathmatch && !mthing->arg1) + { + P_SpawnPlayer(mthing); + } + return; + } + // Check for player starts 5 to 8 + if(mthing->type >= 9100 && mthing->type <= 9103) + { + mthing->type = 5+mthing->type-9100; // Translate to 5 - 8 + playerstarts[mthing->arg1][mthing->type-1] = *mthing; + if(!deathmatch && !mthing->arg1) + { + P_SpawnPlayer(mthing); + } + return; + } + + if(mthing->type >= 1400 && mthing->type < 1410) + { + R_PointInSubsector(mthing->x<y<sector->seqType = mthing->type-1400; + return; + } + + // Check current game type with spawn flags + if(netgame == false) + { + spawnMask = MTF_GSINGLE; + } + else if(deathmatch) + { + spawnMask = MTF_GDEATHMATCH; + } + else + { + spawnMask = MTF_GCOOP; + } + if(!(mthing->options&spawnMask)) + { + return; + } + + // Check current skill with spawn flags + if(gameskill == sk_baby || gameskill == sk_easy) + { + spawnMask = MTF_EASY; + } + else if(gameskill == sk_hard || gameskill == sk_nightmare) + { + spawnMask = MTF_HARD; + } + else + { + spawnMask = MTF_NORMAL; + } + if(!(mthing->options&spawnMask)) + { + return; + } + + // Check current character classes with spawn flags + if(netgame == false) + { // Single player + if((mthing->options&classFlags[PlayerClass[0]]) == 0) + { // Not for current class + return; + } + } + else if(deathmatch == false) + { // Cooperative + spawnMask = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + spawnMask |= classFlags[PlayerClass[i]]; + } + } + if((mthing->options&spawnMask) == 0) + { + return; + } + } + + // Find which type to spawn + for(i = 0; i < NUMMOBJTYPES; i++) + { + if(mthing->type == mobjinfo[i].doomednum) + { + break; + } + } + + if(i == NUMMOBJTYPES) + { // Can't find thing type + I_Error("P_SpawnMapThing: Unknown type %i at (%i, %i)", + mthing->type, mthing->x, mthing->y); + } + + // Don't spawn keys and players in deathmatch + if(deathmatch && mobjinfo[i].flags&MF_NOTDMATCH) + { + return; + } + + // Don't spawn monsters if -nomonsters + if(nomonsters && (mobjinfo[i].flags&MF_COUNTKILL)) + { + return; + } + + x = mthing->x<y<height<z += mthing->height<z -= mthing->height<tid = mthing->tid; + mobj->special = mthing->special; + mobj->args[0] = mthing->arg1; + mobj->args[1] = mthing->arg2; + mobj->args[2] = mthing->arg3; + mobj->args[3] = mthing->arg4; + mobj->args[4] = mthing->arg5; + if(mobj->flags2&MF2_FLOATBOB) + { // Seed random starting index for bobbing motion + mobj->health = P_Random(); + mobj->special1 = mthing->height<tics > 0) + { + mobj->tics = 1+(P_Random()%mobj->tics); + } +// if(mobj->flags&MF_COUNTITEM) +// { +// totalitems++; +// } + if (mobj->flags&MF_COUNTKILL) + { + // Quantize angle to 45 degree increments + mobj->angle = ANG45*(mthing->angle/45); + } + else + { + // Scale angle correctly (source is 0..359) + mobj->angle = ((mthing->angle<<8)/360)<<24; + } + if(mthing->options&MTF_AMBUSH) + { + mobj->flags |= MF_AMBUSH; + } + if(mthing->options&MTF_DORMANT) + { + mobj->flags2 |= MF2_DORMANT; + if(mobj->type == MT_ICEGUY) + { + P_SetMobjState(mobj, S_ICEGUY_DORMANT); + } + mobj->tics = -1; + } +} + +//========================================================================== +// +// P_CreateTIDList +// +//========================================================================== + +void P_CreateTIDList(void) +{ + int i; + mobj_t *mobj; + thinker_t *t; + + i = 0; + for(t = thinkercap.next; t != &thinkercap; t = t->next) + { // Search all current thinkers + if(t->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mobj = (mobj_t *)t; + if(mobj->tid != 0) + { // Add to list + if(i == MAX_TID_COUNT) + { + I_Error("P_CreateTIDList: MAX_TID_COUNT (%d) exceeded.", + MAX_TID_COUNT); + } + TIDList[i] = mobj->tid; + TIDMobj[i++] = mobj; + } + } + // Add termination marker + TIDList[i] = 0; +} + +//========================================================================== +// +// P_InsertMobjIntoTIDList +// +//========================================================================== + +void P_InsertMobjIntoTIDList(mobj_t *mobj, int tid) +{ + int i; + int index; + + index = -1; + for(i = 0; TIDList[i] != 0; i++) + { + if(TIDList[i] == -1) + { // Found empty slot + index = i; + break; + } + } + if(index == -1) + { // Append required + if(i == MAX_TID_COUNT) + { + I_Error("P_InsertMobjIntoTIDList: MAX_TID_COUNT (%d)" + "exceeded.", MAX_TID_COUNT); + } + index = i; + TIDList[index+1] = 0; + } + mobj->tid = tid; + TIDList[index] = tid; + TIDMobj[index] = mobj; +} + +//========================================================================== +// +// P_RemoveMobjFromTIDList +// +//========================================================================== + +void P_RemoveMobjFromTIDList(mobj_t *mobj) +{ + int i; + + for(i = 0; TIDList[i] != 0; i++) + { + if(TIDMobj[i] == mobj) + { + TIDList[i] = -1; + TIDMobj[i] = NULL; + mobj->tid = 0; + return; + } + } + mobj->tid = 0; +} + +//========================================================================== +// +// P_FindMobjFromTID +// +//========================================================================== + +mobj_t *P_FindMobjFromTID(int tid, int *searchPosition) +{ + int i; + + for(i = *searchPosition+1; TIDList[i] != 0; i++) + { + if(TIDList[i] == tid) + { + *searchPosition = i; + return TIDMobj[i]; + } + } + *searchPosition = -1; + return NULL; +} + +/* +=============================================================================== + + GAME SPAWN FUNCTIONS + +=============================================================================== +*/ + +//--------------------------------------------------------------------------- +// +// PROC P_SpawnPuff +// +//--------------------------------------------------------------------------- + +extern fixed_t attackrange; + +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z) +{ + mobj_t *puff; + + z += ((P_Random()-P_Random())<<10); + puff = P_SpawnMobj(x, y, z, PuffType); + if(linetarget && puff->info->seesound) + { // Hit thing sound + S_StartSound(puff, puff->info->seesound); + } + else if(puff->info->attacksound) + { + S_StartSound(puff, puff->info->attacksound); + } + switch(PuffType) + { + case MT_PUNCHPUFF: + puff->momz = FRACUNIT; + break; + case MT_HAMMERPUFF: + puff->momz = .8*FRACUNIT; + break; + default: + break; + } + PuffSpawned = puff; +} + +/* +================ += += P_SpawnBlood += +================ +*/ + +/* +void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage) +{ + mobj_t *th; + + z += ((P_Random()-P_Random())<<10); + th = P_SpawnMobj (x,y,z, MT_BLOOD); + th->momz = FRACUNIT*2; + th->tics -= P_Random()&3; + + if (damage <= 12 && damage >= 9) + P_SetMobjState (th,S_BLOOD2); + else if (damage < 9) + P_SetMobjState (th,S_BLOOD3); +} +*/ + +//--------------------------------------------------------------------------- +// +// PROC P_BloodSplatter +// +//--------------------------------------------------------------------------- + +void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t *originator) +{ + mobj_t *mo; + + mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER); + mo->target = originator; + mo->momx = (P_Random()-P_Random())<<10; + mo->momy = (P_Random()-P_Random())<<10; + mo->momz = 3*FRACUNIT; +} + +//=========================================================================== +// +// P_BloodSplatter2 +// +//=========================================================================== + +void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t *originator) +{ + mobj_t *mo; + + mo = P_SpawnMobj(x+((P_Random()-128)<<11), y+((P_Random()-128)<<11), z, + MT_AXEBLOOD); + mo->target = originator; +} + +//--------------------------------------------------------------------------- +// +// PROC P_RipperBlood +// +//--------------------------------------------------------------------------- + +void P_RipperBlood(mobj_t *mo) +{ + mobj_t *th; + fixed_t x, y, z; + + x = mo->x+((P_Random()-P_Random())<<12); + y = mo->y+((P_Random()-P_Random())<<12); + z = mo->z+((P_Random()-P_Random())<<12); + th = P_SpawnMobj(x, y, z, MT_BLOOD); +// th->flags |= MF_NOGRAVITY; + th->momx = mo->momx>>1; + th->momy = mo->momy>>1; + th->tics += P_Random()&3; +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GetThingFloorType +// +//--------------------------------------------------------------------------- + +int P_GetThingFloorType(mobj_t *thing) +{ + if(thing->floorpic) + { + return(TerrainTypes[thing->floorpic]); + } + else + { + return(TerrainTypes[thing->subsector->sector->floorpic]); + } +/* + if(thing->subsector->sector->floorpic + == W_GetNumForName("FLTWAWA1")-firstflat) + { + return(FLOOR_WATER); + } + else + { + return(FLOOR_SOLID); + } +*/ +} + +//--------------------------------------------------------------------------- +// +// FUNC P_HitFloor +// +//--------------------------------------------------------------------------- +#define SMALLSPLASHCLIP 12<floorz != thing->subsector->sector->floorheight) + { // don't splash if landing on the edge above water/lava/etc.... + return(FLOOR_SOLID); + } + + // Things that don't splash go here + switch(thing->type) + { + case MT_LEAF1: + case MT_LEAF2: +// case MT_BLOOD: // I set these to low mass -- pm +// case MT_BLOODSPLATTER: + case MT_SPLASH: + case MT_SLUDGECHUNK: + return(FLOOR_SOLID); + default: + break; + } + + // Small splash for small masses + if (thing->info->mass < 10) smallsplash = true; + + switch(P_GetThingFloorType(thing)) + { + case FLOOR_WATER: + if (smallsplash) + { + mo=P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE); + if (mo) mo->floorclip += SMALLSPLASHCLIP; + S_StartSound(mo, SFX_AMBIENT10); // small drip + } + else + { + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH); + mo->target = thing; + mo->momx = (P_Random()-P_Random())<<8; + mo->momy = (P_Random()-P_Random())<<8; + mo->momz = 2*FRACUNIT+(P_Random()<<8); + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE); + if (thing->player) P_NoiseAlert(thing, thing); + S_StartSound(mo, SFX_WATER_SPLASH); + } + return(FLOOR_WATER); + case FLOOR_LAVA: + if (smallsplash) + { + mo=P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH); + if (mo) mo->floorclip += SMALLSPLASHCLIP; + } + else + { + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASMOKE); + mo->momz = FRACUNIT+(P_Random()<<7); + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH); + if (thing->player) P_NoiseAlert(thing, thing); + } + S_StartSound(mo, SFX_LAVA_SIZZLE); + if(thing->player && leveltime&31) + { + P_DamageMobj(thing, &LavaInflictor, NULL, 5); + } + return(FLOOR_LAVA); + case FLOOR_SLUDGE: + if (smallsplash) + { + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, + MT_SLUDGESPLASH); + if (mo) mo->floorclip += SMALLSPLASHCLIP; + } + else + { + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK); + mo->target = thing; + mo->momx = (P_Random()-P_Random())<<8; + mo->momy = (P_Random()-P_Random())<<8; + mo->momz = FRACUNIT+(P_Random()<<8); + mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, + MT_SLUDGESPLASH); + if (thing->player) P_NoiseAlert(thing, thing); + } + S_StartSound(mo, SFX_SLUDGE_GLOOP); + return(FLOOR_SLUDGE); + } + return(FLOOR_SOLID); +} + + +//--------------------------------------------------------------------------- +// +// FUNC P_CheckMissileSpawn +// +// Returns true if the missile is at a valid spawn point, otherwise +// explodes it and returns false. +// +//--------------------------------------------------------------------------- + +boolean P_CheckMissileSpawn(mobj_t *missile) +{ + //missile->tics -= P_Random()&3; + + // move a little forward so an angle can be computed if it + // immediately explodes + missile->x += (missile->momx>>1); + missile->y += (missile->momy>>1); + missile->z += (missile->momz>>1); + if(!P_TryMove(missile, missile->x, missile->y)) + { + P_ExplodeMissile(missile); + return(false); + } + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_SpawnMissile +// +// Returns NULL if the missile exploded immediately, otherwise returns +// a mobj_t pointer to the missile. +// +//--------------------------------------------------------------------------- + +mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type) +{ + fixed_t z; + mobj_t *th; + angle_t an; + int dist; + + switch(type) + { + case MT_MNTRFX1: // Minotaur swing attack missile + z = source->z+40*FRACUNIT; + break; + case MT_MNTRFX2: // Minotaur floor fire missile + z = ONFLOORZ+source->floorclip; + break; + case MT_CENTAUR_FX: + z = source->z+45*FRACUNIT; + break; + case MT_ICEGUY_FX: + z = source->z+40*FRACUNIT; + break; + case MT_HOLY_MISSILE: + z = source->z+40*FRACUNIT; + break; + default: + z = source->z+32*FRACUNIT; + break; + } + z -= source->floorclip; + th = P_SpawnMobj(source->x, source->y, z, type); + if(th->info->seesound) + { + S_StartSound(th, th->info->seesound); + } + th->target = source; // Originator + an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); + if(dest->flags&MF_SHADOW) + { // Invisible target + an += (P_Random()-P_Random())<<21; + } + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul(th->info->speed, finecosine[an]); + th->momy = FixedMul(th->info->speed, finesine[an]); + dist = P_AproxDistance(dest->x - source->x, dest->y - source->y); + dist = dist/th->info->speed; + if(dist < 1) + { + dist = 1; + } + th->momz = (dest->z-source->z)/dist; + return(P_CheckMissileSpawn(th) ? th : NULL); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_SpawnMissileXYZ +// +// Returns NULL if the missile exploded immediately, otherwise returns +// a mobj_t pointer to the missile. +// +//--------------------------------------------------------------------------- + +mobj_t *P_SpawnMissileXYZ(fixed_t x, fixed_t y, fixed_t z, + mobj_t *source, mobj_t *dest, mobjtype_t type) +{ + mobj_t *th; + angle_t an; + int dist; + + z -= source->floorclip; + th = P_SpawnMobj(x, y, z, type); + if(th->info->seesound) + { + S_StartSound(th, th->info->seesound); + } + th->target = source; // Originator + an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); + if(dest->flags&MF_SHADOW) + { // Invisible target + an += (P_Random()-P_Random())<<21; + } + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul(th->info->speed, finecosine[an]); + th->momy = FixedMul(th->info->speed, finesine[an]); + dist = P_AproxDistance(dest->x - source->x, dest->y - source->y); + dist = dist/th->info->speed; + if(dist < 1) + { + dist = 1; + } + th->momz = (dest->z-source->z)/dist; + return(P_CheckMissileSpawn(th) ? th : NULL); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_SpawnMissileAngle +// +// Returns NULL if the missile exploded immediately, otherwise returns +// a mobj_t pointer to the missile. +// +//--------------------------------------------------------------------------- + +mobj_t *P_SpawnMissileAngle(mobj_t *source, mobjtype_t type, + angle_t angle, fixed_t momz) +{ + fixed_t z; + mobj_t *mo; + + switch(type) + { + case MT_MNTRFX1: // Minotaur swing attack missile + z = source->z+40*FRACUNIT; + break; + case MT_MNTRFX2: // Minotaur floor fire missile + z = ONFLOORZ+source->floorclip; + break; + case MT_ICEGUY_FX2: // Secondary Projectiles of the Ice Guy + z = source->z+3*FRACUNIT; + break; + case MT_MSTAFF_FX2: + z = source->z+40*FRACUNIT; + break; + default: + z = source->z+32*FRACUNIT; + break; + } + z -= source->floorclip; + mo = P_SpawnMobj(source->x, source->y, z, type); + if(mo->info->seesound) + { + S_StartSound(mo, mo->info->seesound); + } + mo->target = source; // Originator + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[angle]); + mo->momy = FixedMul(mo->info->speed, finesine[angle]); + mo->momz = momz; + return(P_CheckMissileSpawn(mo) ? mo : NULL); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_SpawnMissileAngleSpeed +// +// Returns NULL if the missile exploded immediately, otherwise returns +// a mobj_t pointer to the missile. +// +//--------------------------------------------------------------------------- + +mobj_t *P_SpawnMissileAngleSpeed(mobj_t *source, mobjtype_t type, + angle_t angle, fixed_t momz, fixed_t speed) +{ + fixed_t z; + mobj_t *mo; + + z = source->z; + z -= source->floorclip; + mo = P_SpawnMobj(source->x, source->y, z, type); + if(mo->info->seesound) + { + //S_StartSound(mo, mo->info->seesound); + } + mo->target = source; // Originator + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->momx = FixedMul(speed, finecosine[angle]); + mo->momy = FixedMul(speed, finesine[angle]); + mo->momz = momz; + return(P_CheckMissileSpawn(mo) ? mo : NULL); +} + + + +/* +================ += += P_SpawnPlayerMissile += += Tries to aim at a nearby monster +================ +*/ + +mobj_t *P_SpawnPlayerMissile(mobj_t *source, mobjtype_t type) +{ + angle_t an; + fixed_t x, y, z, slope; + + // Try to find a target + an = source->angle; + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT); + if(!linetarget) + { + an += 1<<26; + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT); + if(!linetarget) + { + an -= 2<<26; + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT); + } + if(!linetarget) + { + an = source->angle; + slope = ((source->player->lookdir)<x; + y = source->y; + if(type == MT_LIGHTNING_FLOOR) + { + z = ONFLOORZ; + slope = 0; + } + else if(type == MT_LIGHTNING_CEILING) + { + z = ONCEILINGZ; + slope = 0; + } + else + { + z = source->z + 4*8*FRACUNIT+((source->player->lookdir)<floorclip; + } + MissileMobj = P_SpawnMobj(x, y, z, type); + if(MissileMobj->info->seesound) + { + //S_StartSound(MissileMobj, MissileMobj->info->seesound); + } + MissileMobj->target = source; + MissileMobj->angle = an; + MissileMobj->momx = FixedMul(MissileMobj->info->speed, + finecosine[an>>ANGLETOFINESHIFT]); + MissileMobj->momy = FixedMul(MissileMobj->info->speed, + finesine[an>>ANGLETOFINESHIFT]); + MissileMobj->momz = FixedMul(MissileMobj->info->speed, slope); + if(MissileMobj->type == MT_MWAND_MISSILE + || MissileMobj->type == MT_CFLAME_MISSILE) + { // Ultra-fast ripper spawning missile + MissileMobj->x += (MissileMobj->momx>>3); + MissileMobj->y += (MissileMobj->momy>>3); + MissileMobj->z += (MissileMobj->momz>>3); + } + else + { // Normal missile + MissileMobj->x += (MissileMobj->momx>>1); + MissileMobj->y += (MissileMobj->momy>>1); + MissileMobj->z += (MissileMobj->momz>>1); + } + if(!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y)) + { // Exploded immediately + P_ExplodeMissile(MissileMobj); + return(NULL); + } + return(MissileMobj); +} + + +//---------------------------------------------------------------------------- +// +// P_SpawnPlayerMinotaur - +// +// Special missile that has larger blocking than player +//---------------------------------------------------------------------------- + +/* +mobj_t *P_SpawnPlayerMinotaur(mobj_t *source, mobjtype_t type) +{ + angle_t an; + fixed_t x, y, z; + fixed_t dist=0 *FRACUNIT; + + an = source->angle; + x = source->x + FixedMul(dist, finecosine[an>>ANGLETOFINESHIFT]); + y = source->y + FixedMul(dist, finesine[an>>ANGLETOFINESHIFT]); + z = source->z + 4*8*FRACUNIT+((source->player->lookdir)<floorclip; + MissileMobj = P_SpawnMobj(x, y, z, type); + if(MissileMobj->info->seesound) + { + //S_StartSound(MissileMobj, MissileMobj->info->seesound); + } + MissileMobj->target = source; + MissileMobj->angle = an; + MissileMobj->momx = FixedMul(MissileMobj->info->speed, + finecosine[an>>ANGLETOFINESHIFT]); + MissileMobj->momy = FixedMul(MissileMobj->info->speed, + finesine[an>>ANGLETOFINESHIFT]); + MissileMobj->momz = 0; + +// MissileMobj->x += (MissileMobj->momx>>3); +// MissileMobj->y += (MissileMobj->momy>>3); +// MissileMobj->z += (MissileMobj->momz>>3); + + if(!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y)) + { // Wouln't fit + + return(NULL); + } + return(MissileMobj); +} +*/ + +//--------------------------------------------------------------------------- +// +// PROC P_SPMAngle +// +//--------------------------------------------------------------------------- + +mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle) +{ + mobj_t *th; + angle_t an; + fixed_t x, y, z, slope; + +// +// see which target is to be aimed at +// + an = angle; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + if (!linetarget) + { + an += 1<<26; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + if (!linetarget) + { + an -= 2<<26; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + } + if (!linetarget) + { + an = angle; + slope = ((source->player->lookdir)<x; + y = source->y; + z = source->z + 4*8*FRACUNIT+((source->player->lookdir)<floorclip; + th = P_SpawnMobj(x, y, z, type); +// if(th->info->seesound) +// { +// S_StartSound(th, th->info->seesound); +// } + th->target = source; + th->angle = an; + th->momx = FixedMul(th->info->speed, finecosine[an>>ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed, finesine[an>>ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed, slope); + return(P_CheckMissileSpawn(th) ? th : NULL); +} + +//=========================================================================== +// +// P_SPMAngleXYZ +// +//=========================================================================== + +mobj_t *P_SPMAngleXYZ(mobj_t *source, fixed_t x, fixed_t y, + fixed_t z, mobjtype_t type, angle_t angle) +{ + mobj_t *th; + angle_t an; + fixed_t slope; + +// +// see which target is to be aimed at +// + an = angle; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + if (!linetarget) + { + an += 1<<26; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + if (!linetarget) + { + an -= 2<<26; + slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); + } + if (!linetarget) + { + an = angle; + slope = ((source->player->lookdir)<player->lookdir)<floorclip; + th = P_SpawnMobj(x, y, z, type); +// if(th->info->seesound) +// { +// S_StartSound(th, th->info->seesound); +// } + th->target = source; + th->angle = an; + th->momx = FixedMul(th->info->speed, finecosine[an>>ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed, finesine[an>>ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed, slope); + return(P_CheckMissileSpawn(th) ? th : NULL); +} + +mobj_t *P_SpawnKoraxMissile(fixed_t x, fixed_t y, fixed_t z, + mobj_t *source, mobj_t *dest, mobjtype_t type) +{ + mobj_t *th; + angle_t an; + int dist; + + z -= source->floorclip; + th = P_SpawnMobj(x, y, z, type); + if(th->info->seesound) + { + S_StartSound(th, th->info->seesound); + } + th->target = source; // Originator + an = R_PointToAngle2(x, y, dest->x, dest->y); + if(dest->flags&MF_SHADOW) + { // Invisible target + an += (P_Random()-P_Random())<<21; + } + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul(th->info->speed, finecosine[an]); + th->momy = FixedMul(th->info->speed, finesine[an]); + dist = P_AproxDistance(dest->x - x, dest->y - y); + dist = dist/th->info->speed; + if(dist < 1) + { + dist = 1; + } + th->momz = (dest->z-z+(30*FRACUNIT))/dist; + return(P_CheckMissileSpawn(th) ? th : NULL); +} diff --git a/src/hexen/p_plats.c b/src/hexen/p_plats.c new file mode 100644 index 00000000..df990b14 --- /dev/null +++ b/src/hexen/p_plats.c @@ -0,0 +1,266 @@ + +//************************************************************************** +//** +//** p_plats.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_plats.c,v $ +//** $Revision: 1.11 $ +//** $Date: 95/09/11 22:06:30 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +plat_t *activeplats[MAXPLATS]; + +//================================================================== +// +// Move a plat up and down +// +//================================================================== +void T_PlatRaise(plat_t *plat) +{ + result_e res; + + switch(plat->status) + { + case PLAT_UP: + res = T_MovePlane(plat->sector, plat->speed, + plat->high, plat->crush, 0, 1); + if (res == RES_CRUSHED && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = PLAT_DOWN; + SN_StartSequence((mobj_t *)&plat->sector->soundorg, + SEQ_PLATFORM+plat->sector->seqType); + } + else + if (res == RES_PASTDEST) + { + plat->count = plat->wait; + plat->status = PLAT_WAITING; + SN_StopSequence((mobj_t *)&plat->sector->soundorg); + switch(plat->type) + { + case PLAT_DOWNWAITUPSTAY: + case PLAT_DOWNBYVALUEWAITUPSTAY: + P_RemoveActivePlat(plat); + break; + default: + break; + } + } + break; + case PLAT_DOWN: + res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); + if (res == RES_PASTDEST) + { + plat->count = plat->wait; + plat->status = PLAT_WAITING; + switch(plat->type) + { + case PLAT_UPWAITDOWNSTAY: + case PLAT_UPBYVALUEWAITDOWNSTAY: + P_RemoveActivePlat(plat); + break; + default: + break; + } + SN_StopSequence((mobj_t *)&plat->sector->soundorg); + } + break; + case PLAT_WAITING: + if (!--plat->count) + { + if (plat->sector->floorheight == plat->low) + plat->status = PLAT_UP; + else + plat->status = PLAT_DOWN; + SN_StartSequence((mobj_t *)&plat->sector->soundorg, + SEQ_PLATFORM+plat->sector->seqType); + } +// case PLAT_IN_STASIS: +// break; + } +} + +//================================================================== +// +// Do Platforms +// "amount" is only used for SOME platforms. +// +//================================================================== +int EV_DoPlat(line_t *line, byte *args, plattype_e type, int amount) +{ + plat_t *plat; + int secnum; + int rtn; + sector_t *sec; + + secnum = -1; + rtn = 0; + +/* + // + // Activate all plats that are in_stasis + // + switch(type) + { + case PLAT_PERPETUALRAISE: + P_ActivateInStasis(args[0]); + break; + default: + break; + } +*/ + + while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) + continue; + + // + // Find lowest & highest floors around sector + // + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->specialdata = plat; + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = args[0]; + plat->speed = args[1]*(FRACUNIT/8); + switch(type) + { + case PLAT_DOWNWAITUPSTAY: + plat->low = P_FindLowestFloorSurrounding(sec)+8*FRACUNIT; + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = sec->floorheight; + plat->wait = args[2]; + plat->status = PLAT_DOWN; + break; + case PLAT_DOWNBYVALUEWAITUPSTAY: + plat->low = sec->floorheight-args[3]*8*FRACUNIT; + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = sec->floorheight; + plat->wait = args[2]; + plat->status = PLAT_DOWN; + break; + case PLAT_UPWAITDOWNSTAY: + plat->high = P_FindHighestFloorSurrounding(sec); + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->low = sec->floorheight; + plat->wait = args[2]; + plat->status = PLAT_UP; + break; + case PLAT_UPBYVALUEWAITDOWNSTAY: + plat->high = sec->floorheight+args[3]*8*FRACUNIT; + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->low = sec->floorheight; + plat->wait = args[2]; + plat->status = PLAT_UP; + break; + case PLAT_PERPETUALRAISE: + plat->low = P_FindLowestFloorSurrounding(sec)+8*FRACUNIT; + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = P_FindHighestFloorSurrounding(sec); + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->wait = args[2]; + plat->status = P_Random()&1; + break; + } + P_AddActivePlat(plat); + SN_StartSequence((mobj_t *)&sec->soundorg, SEQ_PLATFORM+sec->seqType); + } + return rtn; +} + +#if 0 +void P_ActivateInStasis(int tag) +{ + int i; + + for (i = 0;i < MAXPLATS;i++) + if (activeplats[i] && + (activeplats[i])->tag == tag && + (activeplats[i])->status == PLAT_IN_STASIS) + { + (activeplats[i])->status = (activeplats[i])->oldstatus; + (activeplats[i])->thinker.function = T_PlatRaise; + } +} +#endif + +void EV_StopPlat(line_t *line, byte *args) +{ + int i; + + for(i = 0; i < MAXPLATS; i++) + { + if((activeplats[i])->tag = args[0]) + { + (activeplats[i])->sector->specialdata = NULL; + P_TagFinished((activeplats[i])->sector->tag); + P_RemoveThinker(&(activeplats[i])->thinker); + activeplats[i] = NULL; + + return; + } + } + +/* + int j; + + for (j = 0;j < MAXPLATS;j++) + { + if (activeplats[j] && ((activeplats[j])->status != PLAT_IN_STASIS) && + ((activeplats[j])->tag == args[0])) + { + (activeplats[j])->oldstatus = (activeplats[j])->status; + (activeplats[j])->status = PLAT_IN_STASIS; + (activeplats[j])->thinker.function = NULL; + SN_StopSequence((mobj_t *)&(activeplats[j])->sector->soundorg); + } + } +*/ +} + +void P_AddActivePlat(plat_t *plat) +{ + int i; + for (i = 0;i < MAXPLATS;i++) + if (activeplats[i] == NULL) + { + activeplats[i] = plat; + return; + } + I_Error ("P_AddActivePlat: no more plats!"); +} + +void P_RemoveActivePlat(plat_t *plat) +{ + int i; + for (i = 0;i < MAXPLATS;i++) + if (plat == activeplats[i]) + { + (activeplats[i])->sector->specialdata = NULL; + P_TagFinished(plat->sector->tag); + P_RemoveThinker(&(activeplats[i])->thinker); + activeplats[i] = NULL; + return; + } + I_Error ("P_RemoveActivePlat: can't find plat!"); +} diff --git a/src/hexen/p_pspr.c b/src/hexen/p_pspr.c new file mode 100644 index 00000000..9dbd5de8 --- /dev/null +++ b/src/hexen/p_pspr.c @@ -0,0 +1,2456 @@ + +//************************************************************************** +//** +//** p_pspr.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_pspr.c,v $ +//** $Revision: 1.105 $ +//** $Date: 96/01/06 03:23:35 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define LOWERSPEED FRACUNIT*6 +#define RAISESPEED FRACUNIT*6 +#define WEAPONBOTTOM 128*FRACUNIT +#define WEAPONTOP 32*FRACUNIT + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +extern void P_ExplodeMissile(mobj_t *mo); +extern void A_UnHideThing(mobj_t *actor); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern fixed_t FloatBobOffsets[64]; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +fixed_t bulletslope; + +weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES] = +{ + { // First Weapons + { // Fighter First Weapon - Punch + MANA_NONE, // mana + S_PUNCHUP, // upstate + S_PUNCHDOWN, // downstate + S_PUNCHREADY, // readystate + S_PUNCHATK1_1, // atkstate + S_PUNCHATK1_1, // holdatkstate + S_NULL // flashstate + }, + { // Cleric First Weapon - Mace + MANA_NONE, // mana + S_CMACEUP, // upstate + S_CMACEDOWN, // downstate + S_CMACEREADY, // readystate + S_CMACEATK_1, // atkstate + S_CMACEATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Mage First Weapon - Wand + MANA_NONE, + S_MWANDUP, + S_MWANDDOWN, + S_MWANDREADY, + S_MWANDATK_1, + S_MWANDATK_1, + S_NULL + }, + { // Pig - Snout + MANA_NONE, // mana + S_SNOUTUP, // upstate + S_SNOUTDOWN, // downstate + S_SNOUTREADY, // readystate + S_SNOUTATK1, // atkstate + S_SNOUTATK1, // holdatkstate + S_NULL // flashstate + } + }, + { // Second Weapons + { // Fighter - Axe + MANA_NONE, // mana + S_FAXEUP, // upstate + S_FAXEDOWN, // downstate + S_FAXEREADY, // readystate + S_FAXEATK_1, // atkstate + S_FAXEATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Cleric - Serpent Staff + MANA_1, // mana + S_CSTAFFUP, // upstate + S_CSTAFFDOWN, // downstate + S_CSTAFFREADY, // readystate + S_CSTAFFATK_1, // atkstate + S_CSTAFFATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Mage - Cone of shards + MANA_1, // mana + S_CONEUP, // upstate + S_CONEDOWN, // downstate + S_CONEREADY, // readystate + S_CONEATK1_1, // atkstate + S_CONEATK1_3, // holdatkstate + S_NULL // flashstate + }, + { // Pig - Snout + MANA_NONE, // mana + S_SNOUTUP, // upstate + S_SNOUTDOWN, // downstate + S_SNOUTREADY, // readystate + S_SNOUTATK1, // atkstate + S_SNOUTATK1, // holdatkstate + S_NULL // flashstate + } + }, + { // Third Weapons + { // Fighter - Hammer + MANA_NONE, // mana + S_FHAMMERUP, // upstate + S_FHAMMERDOWN, // downstate + S_FHAMMERREADY, // readystate + S_FHAMMERATK_1, // atkstate + S_FHAMMERATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Cleric - Flame Strike + MANA_2, // mana + S_CFLAMEUP, // upstate + S_CFLAMEDOWN, // downstate + S_CFLAMEREADY1, // readystate + S_CFLAMEATK_1, // atkstate + S_CFLAMEATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Mage - Lightning + MANA_2, // mana + S_MLIGHTNINGUP, // upstate + S_MLIGHTNINGDOWN, // downstate + S_MLIGHTNINGREADY, // readystate + S_MLIGHTNINGATK_1, // atkstate + S_MLIGHTNINGATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Pig - Snout + MANA_NONE, // mana + S_SNOUTUP, // upstate + S_SNOUTDOWN, // downstate + S_SNOUTREADY, // readystate + S_SNOUTATK1, // atkstate + S_SNOUTATK1, // holdatkstate + S_NULL // flashstate + } + }, + { // Fourth Weapons + { // Fighter - Rune Sword + MANA_BOTH, // mana + S_FSWORDUP, // upstate + S_FSWORDDOWN, // downstate + S_FSWORDREADY, // readystate + S_FSWORDATK_1, // atkstate + S_FSWORDATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Cleric - Holy Symbol + MANA_BOTH, // mana + S_CHOLYUP, // upstate + S_CHOLYDOWN, // downstate + S_CHOLYREADY, // readystate + S_CHOLYATK_1, // atkstate + S_CHOLYATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Mage - Staff + MANA_BOTH, // mana + S_MSTAFFUP, // upstate + S_MSTAFFDOWN, // downstate + S_MSTAFFREADY, // readystate + S_MSTAFFATK_1, // atkstate + S_MSTAFFATK_1, // holdatkstate + S_NULL // flashstate + }, + { // Pig - Snout + MANA_NONE, // mana + S_SNOUTUP, // upstate + S_SNOUTDOWN, // downstate + S_SNOUTREADY, // readystate + S_SNOUTATK1, // atkstate + S_SNOUTATK1, // holdatkstate + S_NULL // flashstate + } + } +}; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int WeaponManaUse[NUMCLASSES][NUMWEAPONS] = +{ + { 0, 2, 3, 14 }, + { 0, 1, 4, 18 }, + { 0, 3, 5, 15 }, + { 0, 0, 0, 0 } +}; + +// CODE -------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// PROC P_SetPsprite +// +//--------------------------------------------------------------------------- + +void P_SetPsprite(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp; + state_t *state; + + psp = &player->psprites[position]; + do + { + if(!stnum) + { // Object removed itself. + psp->state = NULL; + break; + } + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + if(state->misc1) + { // Set coordinates. + psp->sx = state->misc1<misc2) + { + psp->sy = state->misc2<action) + { // Call action routine. + state->action(player, psp); + if(!psp->state) + { + break; + } + } + stnum = psp->state->nextstate; + } while(!psp->tics); // An initial state of 0 could cycle through. +} + +//--------------------------------------------------------------------------- +// +// PROC P_SetPspriteNF +// +// Identical to P_SetPsprite, without calling the action function +//--------------------------------------------------------------------------- + +void P_SetPspriteNF(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp; + state_t *state; + + psp = &player->psprites[position]; + do + { + if(!stnum) + { // Object removed itself. + psp->state = NULL; + break; + } + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + if(state->misc1) + { // Set coordinates. + psp->sx = state->misc1<misc2) + { + psp->sy = state->misc2<state->nextstate; + } while(!psp->tics); // An initial state of 0 could cycle through. +} + +/* +================= += += P_CalcSwing += +================= +*/ + +/* +fixed_t swingx, swingy; +void P_CalcSwing (player_t *player) +{ + fixed_t swing; + int angle; + +// OPTIMIZE: tablify this + + swing = player->bob; + + angle = (FINEANGLES/70*leveltime)&FINEMASK; + swingx = FixedMul ( swing, finesine[angle]); + + angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK; + swingy = -FixedMul ( swingx, finesine[angle]); +} +*/ + +//--------------------------------------------------------------------------- +// +// PROC P_ActivateMorphWeapon +// +//--------------------------------------------------------------------------- + +void P_ActivateMorphWeapon(player_t *player) +{ + player->pendingweapon = WP_NOCHANGE; + player->psprites[ps_weapon].sy = WEAPONTOP; + player->readyweapon = WP_FIRST; // Snout is the first weapon + P_SetPsprite(player, ps_weapon, S_SNOUTREADY); +} + + +//--------------------------------------------------------------------------- +// +// PROC P_PostMorphWeapon +// +//--------------------------------------------------------------------------- + +void P_PostMorphWeapon(player_t *player, weapontype_t weapon) +{ + player->pendingweapon = WP_NOCHANGE; + player->readyweapon = weapon; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + P_SetPsprite(player, ps_weapon, WeaponInfo[weapon][player->class].upstate); +} + +//--------------------------------------------------------------------------- +// +// PROC P_BringUpWeapon +// +// Starts bringing the pending weapon up from the bottom of the screen. +// +//--------------------------------------------------------------------------- + +void P_BringUpWeapon(player_t *player) +{ + statenum_t new; + + if(player->pendingweapon == WP_NOCHANGE) + { + player->pendingweapon = player->readyweapon; + } + if(player->class == PCLASS_FIGHTER && player->pendingweapon == WP_SECOND + && player->mana[MANA_1]) + { + new = S_FAXEUP_G; + } + else + { + new = WeaponInfo[player->pendingweapon][player->class].upstate; + } + player->pendingweapon = WP_NOCHANGE; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + P_SetPsprite(player, ps_weapon, new); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_CheckMana +// +// Returns true if there is enough mana to shoot. If not, selects the +// next weapon to use. +// +//--------------------------------------------------------------------------- + +boolean P_CheckMana(player_t *player) +{ + manatype_t mana; + int count; + + mana = WeaponInfo[player->readyweapon][player->class].mana; + count = WeaponManaUse[player->class][player->readyweapon]; + if(mana == MANA_BOTH) + { + if(player->mana[MANA_1] >= count && player->mana[MANA_2] >= count) + { + return true; + } + } + else if(mana == MANA_NONE || player->mana[mana] >= count) + { + return(true); + } + // out of mana, pick a weapon to change to + do + { + if(player->weaponowned[WP_THIRD] + && player->mana[MANA_2] >= WeaponManaUse[player->class][WP_THIRD]) + { + player->pendingweapon = WP_THIRD; + } + else if(player->weaponowned[WP_SECOND] + && player->mana[MANA_1] >= WeaponManaUse[player->class][WP_SECOND]) + { + player->pendingweapon = WP_SECOND; + } + else if(player->weaponowned[WP_FOURTH] + && player->mana[MANA_1] >= WeaponManaUse[player->class][WP_FOURTH] + && player->mana[MANA_2] >= WeaponManaUse[player->class][WP_FOURTH]) + { + player->pendingweapon = WP_FOURTH; + } + else + { + player->pendingweapon = WP_FIRST; + } + } while(player->pendingweapon == WP_NOCHANGE); + P_SetPsprite(player, ps_weapon, + WeaponInfo[player->readyweapon][player->class].downstate); + return(false); +} + +//--------------------------------------------------------------------------- +// +// PROC P_FireWeapon +// +//--------------------------------------------------------------------------- + +void P_FireWeapon(player_t *player) +{ + statenum_t attackState; + + if(!P_CheckMana(player)) + { + return; + } + P_SetMobjState(player->mo, PStateAttack[player->class]); // S_PLAY_ATK1); + if(player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND + && player->mana[MANA_1] > 0) + { // Glowing axe + attackState = S_FAXEATK_G1; + } + else + { + attackState = player->refire ? + WeaponInfo[player->readyweapon][player->class].holdatkstate + : WeaponInfo[player->readyweapon][player->class].atkstate; + } + P_SetPsprite(player, ps_weapon, attackState); + P_NoiseAlert(player->mo, player->mo); +} + +//--------------------------------------------------------------------------- +// +// PROC P_DropWeapon +// +// The player died, so put the weapon away. +// +//--------------------------------------------------------------------------- + +void P_DropWeapon(player_t *player) +{ + P_SetPsprite(player, ps_weapon, + WeaponInfo[player->readyweapon][player->class].downstate); +} + +//--------------------------------------------------------------------------- +// +// PROC A_WeaponReady +// +// The player can fire the weapon or change to another weapon at this time. +// +//--------------------------------------------------------------------------- + +void A_WeaponReady(player_t *player, pspdef_t *psp) +{ + int angle; + + // Change player from attack state + if(player->mo->state >= &states[PStateAttack[player->class]] + && player->mo->state <= &states[PStateAttackEnd[player->class]]) + { + P_SetMobjState(player->mo, PStateNormal[player->class]); + } + // Put the weapon away if the player has a pending weapon or has + // died. + if(player->pendingweapon != WP_NOCHANGE || !player->health) + { + P_SetPsprite(player, ps_weapon, + WeaponInfo[player->readyweapon][player->class].downstate); + return; + } + + // Check for fire. + if(player->cmd.buttons&BT_ATTACK) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + else + { + player->attackdown = false; + } + + if(!player->morphTics) + { + // Bob the weapon based on movement speed. + angle = (128*leveltime)&FINEMASK; + psp->sx = FRACUNIT+FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES/2-1; + psp->sy = WEAPONTOP+FixedMul(player->bob, finesine[angle]); + } +} + +//--------------------------------------------------------------------------- +// +// PROC A_ReFire +// +// The player can re fire the weapon without lowering it entirely. +// +//--------------------------------------------------------------------------- + +void A_ReFire(player_t *player, pspdef_t *psp) +{ + if((player->cmd.buttons&BT_ATTACK) + && player->pendingweapon == WP_NOCHANGE && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckMana(player); + } +} + +//--------------------------------------------------------------------------- +// +// PROC A_Lower +// +//--------------------------------------------------------------------------- + +void A_Lower(player_t *player, pspdef_t *psp) +{ + if(player->morphTics) + { + psp->sy = WEAPONBOTTOM; + } + else + { + psp->sy += LOWERSPEED; + } + if(psp->sy < WEAPONBOTTOM) + { // Not lowered all the way yet + return; + } + if(player->playerstate == PST_DEAD) + { // Player is dead, so don't bring up a pending weapon + psp->sy = WEAPONBOTTOM; + return; + } + if(!player->health) + { // Player is dead, so keep the weapon off screen + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + player->readyweapon = player->pendingweapon; + P_BringUpWeapon(player); +} + +//--------------------------------------------------------------------------- +// +// PROC A_Raise +// +//--------------------------------------------------------------------------- + +void A_Raise(player_t *player, pspdef_t *psp) +{ + psp->sy -= RAISESPEED; + if(psp->sy > WEAPONTOP) + { // Not raised all the way yet + return; + } + psp->sy = WEAPONTOP; + if(player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND + && player->mana[MANA_1]) + { + P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); + } + else + { + P_SetPsprite(player, ps_weapon, + WeaponInfo[player->readyweapon][player->class].readystate); + } +} + +/* +=============== += += P_BulletSlope += += Sets a slope so a near miss is at aproximately the height of the += intended target += +=============== +*/ + +/* +void P_BulletSlope (mobj_t *mo) +{ + angle_t an; + +// +// see which target is to be aimed at +// + an = mo->angle; + bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); + if (!linetarget) + { + an += 1<<26; + bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); + if (!linetarget) + { + an -= 2<<26; + bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); + } + if (!linetarget) + { + an += 1<<26; + bulletslope = (mo->player->lookdir<x, pmo->y, linetarget->x, linetarget->y); + difference = (int)angle-(int)pmo->angle; + if(abs(difference) > MAX_ANGLE_ADJUST) + { + pmo->angle += difference > 0 ? MAX_ANGLE_ADJUST : -MAX_ANGLE_ADJUST; + } + else + { + pmo->angle = angle; + } +} + +//============================================================================ +// +// A_SnoutAttack +// +//============================================================================ + +void A_SnoutAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + + damage = 3+(P_Random()&3); + angle = player->mo->angle; + slope = P_AimLineAttack(player->mo, angle, MELEERANGE); + PuffType = MT_SNOUTPUFF; + PuffSpawned = NULL; + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + S_StartSound(player->mo, SFX_PIG_ACTIVE1+(P_Random()&1)); + if(linetarget) + { + AdjustPlayerAngle(player->mo); +// player->mo->angle = R_PointToAngle2(player->mo->x, +// player->mo->y, linetarget->x, linetarget->y); + if(PuffSpawned) + { // Bit something + S_StartSound(player->mo, SFX_PIG_ATTACK); + } + } +} + +//============================================================================ +// +// A_FHammerAttack +// +//============================================================================ + +#define HAMMER_RANGE (MELEERANGE+MELEERANGE/2) + +void A_FHammerAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + mobj_t *pmo=player->mo; + int damage; + fixed_t power; + int slope; + int i; + + damage = 60+(P_Random()&63); + power = 10*FRACUNIT; + PuffType = MT_HAMMERPUFF; + for(i = 0; i < 16; i++) + { + angle = pmo->angle+i*(ANG45/32); + slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); + AdjustPlayerAngle(pmo); + if (linetarget->flags&MF_COUNTKILL || linetarget->player) + { + P_ThrustMobj(linetarget, angle, power); + } + pmo->special1 = false; // Don't throw a hammer + goto hammerdone; + } + angle = pmo->angle-i*(ANG45/32); + slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); + AdjustPlayerAngle(pmo); + if (linetarget->flags&MF_COUNTKILL || linetarget->player) + { + P_ThrustMobj(linetarget, angle, power); + } + pmo->special1 = false; // Don't throw a hammer + goto hammerdone; + } + } + // didn't find any targets in meleerange, so set to throw out a hammer + PuffSpawned = NULL; + angle = pmo->angle; + slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); + P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); + if(PuffSpawned) + { + pmo->special1 = false; + } + else + { + pmo->special1 = true; + } +hammerdone: + if(player->mana[MANA_2] < + WeaponManaUse[player->class][player->readyweapon]) + { // Don't spawn a hammer if the player doesn't have enough mana + pmo->special1 = false; + } + return; +} + +//============================================================================ +// +// A_FHammerThrow +// +//============================================================================ + +void A_FHammerThrow(player_t *player, pspdef_t *psp) +{ + mobj_t *mo; + + if(!player->mo->special1) + { + return; + } + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + mo = P_SpawnPlayerMissile(player->mo, MT_HAMMER_MISSILE); + if(mo) + { + mo->special1 = 0; + } +} + +//============================================================================ +// +// A_FSwordAttack +// +//============================================================================ + +void A_FSwordAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *pmo; + + player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + pmo = player->mo; + P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z-10*FRACUNIT, MT_FSWORD_MISSILE, + pmo->angle+ANG45/4); + P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z-5*FRACUNIT, MT_FSWORD_MISSILE, + pmo->angle+ANG45/8); + P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z, MT_FSWORD_MISSILE, pmo->angle); + P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z+5*FRACUNIT, MT_FSWORD_MISSILE, + pmo->angle-ANG45/8); + P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z+10*FRACUNIT, MT_FSWORD_MISSILE, + pmo->angle-ANG45/4); + S_StartSound(pmo, SFX_FIGHTER_SWORD_FIRE); +} + +//============================================================================ +// +// A_FSwordAttack2 +// +//============================================================================ + +void A_FSwordAttack2(mobj_t *actor) +{ + angle_t angle = actor->angle; + + P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle+ANG45/4, 0); + P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle+ANG45/8, 0); + P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle, 0); + P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle-ANG45/8, 0); + P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle-ANG45/4, 0); + S_StartSound(actor, SFX_FIGHTER_SWORD_FIRE); +} + +//============================================================================ +// +// A_FSwordFlames +// +//============================================================================ + +void A_FSwordFlames(mobj_t *actor) +{ + int i; + + for(i = 1+(P_Random()&3); i; i--) + { + P_SpawnMobj(actor->x+((P_Random()-128)<<12), actor->y + +((P_Random()-128)<<12), actor->z+((P_Random()-128)<<11), + MT_FSWORD_FLAME); + } +} + +//============================================================================ +// +// A_MWandAttack +// +//============================================================================ + +void A_MWandAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *mo; + + mo = P_SpawnPlayerMissile(player->mo, MT_MWAND_MISSILE); + if(mo) + { + mo->thinker.function = P_BlasterMobjThinker; + } + S_StartSound(player->mo, SFX_MAGE_WAND_FIRE); +} + +// ===== Mage Lightning Weapon ===== + +//============================================================================ +// +// A_LightningReady +// +//============================================================================ + +void A_LightningReady(player_t *player, pspdef_t *psp) +{ + A_WeaponReady(player, psp); + if(P_Random() < 160) + { + S_StartSound(player->mo, SFX_MAGE_LIGHTNING_READY); + } +} + +//============================================================================ +// +// A_LightningClip +// +//============================================================================ + +#define ZAGSPEED FRACUNIT + +void A_LightningClip(mobj_t *actor) +{ + mobj_t *cMo; + mobj_t *target; + int zigZag; + + if(actor->type == MT_LIGHTNING_FLOOR) + { + actor->z = actor->floorz; + target = (mobj_t *)((mobj_t *)actor->special2)->special1; + } + else if(actor->type == MT_LIGHTNING_CEILING) + { + actor->z = actor->ceilingz-actor->height; + target = (mobj_t *)actor->special1; + } + if(actor->type == MT_LIGHTNING_FLOOR) + { // floor lightning zig-zags, and forces the ceiling lightning to mimic + cMo = (mobj_t *)actor->special2; + zigZag = P_Random(); + if((zigZag > 128 && actor->special1 < 2) || actor->special1 < -2) + { + P_ThrustMobj(actor, actor->angle+ANG90, ZAGSPEED); + if(cMo) + { + P_ThrustMobj(cMo, actor->angle+ANG90, ZAGSPEED); + } + actor->special1++; + } + else + { + P_ThrustMobj(actor, actor->angle-ANG90, ZAGSPEED); + if(cMo) + { + P_ThrustMobj(cMo, cMo->angle-ANG90, ZAGSPEED); + } + actor->special1--; + } + } + if(target) + { + if(target->health <= 0) + { + P_ExplodeMissile(actor); + } + else + { + actor->angle = R_PointToAngle2(actor->x, actor->y, target->x, + target->y); + actor->momx = 0; + actor->momy = 0; + P_ThrustMobj(actor, actor->angle, actor->info->speed>>1); + } + } +} + +//============================================================================ +// +// A_LightningZap +// +//============================================================================ + +void A_LightningZap(mobj_t *actor) +{ + mobj_t *mo; + fixed_t deltaZ; + + A_LightningClip(actor); + + actor->health -= 8; + if(actor->health <= 0) + { + P_SetMobjState(actor, actor->info->deathstate); + return; + } + if(actor->type == MT_LIGHTNING_FLOOR) + { + deltaZ = 10*FRACUNIT; + } + else + { + deltaZ = -10*FRACUNIT; + } + mo = P_SpawnMobj(actor->x+((P_Random()-128)*actor->radius/256), + actor->y+((P_Random()-128)*actor->radius/256), + actor->z+deltaZ, MT_LIGHTNING_ZAP); + if(mo) + { + mo->special2 = (int)actor; + mo->momx = actor->momx; + mo->momy = actor->momy; + mo->target = actor->target; + if(actor->type == MT_LIGHTNING_FLOOR) + { + mo->momz = 20*FRACUNIT; + } + else + { + mo->momz = -20*FRACUNIT; + } + } +/* + mo = P_SpawnMobj(actor->x+((P_Random()-128)*actor->radius/256), + actor->y+((P_Random()-128)*actor->radius/256), + actor->z+deltaZ, MT_LIGHTNING_ZAP); + if(mo) + { + mo->special2 = (int)actor; + mo->momx = actor->momx; + mo->momy = actor->momy; + mo->target = actor->target; + if(actor->type == MT_LIGHTNING_FLOOR) + { + mo->momz = 16*FRACUNIT; + } + else + { + mo->momz = -16*FRACUNIT; + } + } +*/ + if(actor->type == MT_LIGHTNING_FLOOR && P_Random() < 160) + { + S_StartSound(actor, SFX_MAGE_LIGHTNING_CONTINUOUS); + } +} + +//============================================================================ +// +// A_MLightningAttack2 +// +//============================================================================ + +void A_MLightningAttack2(mobj_t *actor) +{ + mobj_t *fmo, *cmo; + + fmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_FLOOR); + cmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_CEILING); + if(fmo) + { + fmo->special1 = 0; + fmo->special2 = (int)cmo; + A_LightningZap(fmo); + } + if(cmo) + { + cmo->special1 = 0; // mobj that it will track + cmo->special2 = (int)fmo; + A_LightningZap(cmo); + } + S_StartSound(actor, SFX_MAGE_LIGHTNING_FIRE); +} + +//============================================================================ +// +// A_MLightningAttack +// +//============================================================================ + +void A_MLightningAttack(player_t *player, pspdef_t *psp) +{ + A_MLightningAttack2(player->mo); + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; +} + +//============================================================================ +// +// A_ZapMimic +// +//============================================================================ + +void A_ZapMimic(mobj_t *actor) +{ + mobj_t *mo; + + mo = (mobj_t *)actor->special2; + if(mo) + { + if(mo->state >= &states[mo->info->deathstate] + || mo->state == &states[S_FREETARGMOBJ]) + { + P_ExplodeMissile(actor); + } + else + { + actor->momx = mo->momx; + actor->momy = mo->momy; + } + } +} + +//============================================================================ +// +// A_LastZap +// +//============================================================================ + +void A_LastZap(mobj_t *actor) +{ + mobj_t *mo; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_LIGHTNING_ZAP); + if(mo) + { + P_SetMobjState(mo, S_LIGHTNING_ZAP_X1); + mo->momz = 40*FRACUNIT; + } +} + +//============================================================================ +// +// A_LightningRemove +// +//============================================================================ + +void A_LightningRemove(mobj_t *actor) +{ + mobj_t *mo; + + mo = (mobj_t *)actor->special2; + if(mo) + { + mo->special2 = 0; + P_ExplodeMissile(mo); + } +} + + +//============================================================================ +// +// MStaffSpawn +// +//============================================================================ +void MStaffSpawn(mobj_t *pmo, angle_t angle) +{ + mobj_t *mo; + + mo = P_SPMAngle(pmo, MT_MSTAFF_FX2, angle); + if (mo) + { + mo->target = pmo; + mo->special1 = (int)P_RoughMonsterSearch(mo, 10); + } +} + +//============================================================================ +// +// A_MStaffAttack +// +//============================================================================ + +void A_MStaffAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + mobj_t *pmo; + + player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + pmo = player->mo; + angle = pmo->angle; + + MStaffSpawn(pmo, angle); + MStaffSpawn(pmo, angle-ANGLE_1*5); + MStaffSpawn(pmo, angle+ANGLE_1*5); + S_StartSound(player->mo, SFX_MAGE_STAFF_FIRE); + if(player == &players[consoleplayer]) + { + player->damagecount = 0; + player->bonuscount = 0; + I_SetPalette((byte *)W_CacheLumpNum(W_GetNumForName("playpal"), + PU_CACHE)+STARTSCOURGEPAL*768); + } +} + +//============================================================================ +// +// A_MStaffPalette +// +//============================================================================ + +void A_MStaffPalette(player_t *player, pspdef_t *psp) +{ + int pal; + + if(player == &players[consoleplayer]) + { + pal = STARTSCOURGEPAL+psp->state-(&states[S_MSTAFFATK_2]); + if(pal == STARTSCOURGEPAL+3) + { // reset back to original playpal + pal = 0; + } + I_SetPalette((byte *)W_CacheLumpNum(W_GetNumForName("playpal"), + PU_CACHE)+pal*768); + } +} + +//============================================================================ +// +// A_MStaffWeave +// +//============================================================================ + +void A_MStaffWeave(mobj_t *actor) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + + weaveXY = actor->special2>>16; + weaveZ = actor->special2&0xFFFF; + angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + newX = actor->x-FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY = actor->y-FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + weaveXY = (weaveXY+6)&63; + newX += FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY += FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + P_TryMove(actor, newX, newY); + actor->z -= FloatBobOffsets[weaveZ]<<1; + weaveZ = (weaveZ+3)&63; + actor->z += FloatBobOffsets[weaveZ]<<1; + if(actor->z <= actor->floorz) + { + actor->z = actor->floorz+FRACUNIT; + } + actor->special2 = weaveZ+(weaveXY<<16); +} + + +//============================================================================ +// +// A_MStaffTrack +// +//============================================================================ + +void A_MStaffTrack(mobj_t *actor) +{ + if ((actor->special1 == 0) && (P_Random()<50)) + { + actor->special1 = (int)P_RoughMonsterSearch(actor, 10); + } + P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*10); +} + + +//============================================================================ +// +// MStaffSpawn2 - for use by mage class boss +// +//============================================================================ + +void MStaffSpawn2(mobj_t *actor, angle_t angle) +{ + mobj_t *mo; + + mo = P_SpawnMissileAngle(actor, MT_MSTAFF_FX2, angle, 0); + if (mo) + { + mo->target = actor; + mo->special1 = (int)P_RoughMonsterSearch(mo, 10); + } +} + +//============================================================================ +// +// A_MStaffAttack2 - for use by mage class boss +// +//============================================================================ + +void A_MStaffAttack2(mobj_t *actor) +{ + angle_t angle; + angle = actor->angle; + MStaffSpawn2(actor, angle); + MStaffSpawn2(actor, angle-ANGLE_1*5); + MStaffSpawn2(actor, angle+ANGLE_1*5); + S_StartSound(actor, SFX_MAGE_STAFF_FIRE); +} + +//============================================================================ +// +// A_FPunchAttack +// +//============================================================================ + +void A_FPunchAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + mobj_t *pmo = player->mo; + fixed_t power; + int i; + + damage = 40+(P_Random()&15); + power = 2*FRACUNIT; + PuffType = MT_PUNCHPUFF; + for(i = 0; i < 16; i++) + { + angle = pmo->angle+i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); + if(linetarget) + { + player->mo->special1++; + if(pmo->special1 == 3) + { + damage <<= 1; + power = 6*FRACUNIT; + PuffType = MT_HAMMERPUFF; + } + P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); + if (linetarget->flags&MF_COUNTKILL || linetarget->player) + { + P_ThrustMobj(linetarget, angle, power); + } + AdjustPlayerAngle(pmo); + goto punchdone; + } + angle = pmo->angle-i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); + if(linetarget) + { + pmo->special1++; + if(pmo->special1 == 3) + { + damage <<= 1; + power = 6*FRACUNIT; + PuffType = MT_HAMMERPUFF; + } + P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); + if (linetarget->flags&MF_COUNTKILL || linetarget->player) + { + P_ThrustMobj(linetarget, angle, power); + } + AdjustPlayerAngle(pmo); + goto punchdone; + } + } + // didn't find any creatures, so try to strike any walls + pmo->special1 = 0; + + angle = pmo->angle; + slope = P_AimLineAttack(pmo, angle, MELEERANGE); + P_LineAttack(pmo, angle, MELEERANGE, slope, damage); + +punchdone: + if(pmo->special1 == 3) + { + pmo->special1 = 0; + P_SetPsprite(player, ps_weapon, S_PUNCHATK2_1); + S_StartSound(pmo, SFX_FIGHTER_GRUNT); + } + return; +} + +//============================================================================ +// +// A_FAxeAttack +// +//============================================================================ + +#define AXERANGE 2.25*MELEERANGE + +void A_FAxeAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + mobj_t *pmo=player->mo; + fixed_t power; + int damage; + int slope; + int i; + int useMana; + + damage = 40+(P_Random()&15)+(P_Random()&7); + power = 0; + if(player->mana[MANA_1] > 0) + { + damage <<= 1; + power = 6*FRACUNIT; + PuffType = MT_AXEPUFF_GLOW; + useMana = 1; + } + else + { + PuffType = MT_AXEPUFF; + useMana = 0; + } + for(i = 0; i < 16; i++) + { + angle = pmo->angle+i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, AXERANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, AXERANGE, slope, damage); + if (linetarget->flags&MF_COUNTKILL || linetarget->player) + { + P_ThrustMobj(linetarget, angle, power); + } + AdjustPlayerAngle(pmo); + useMana++; + goto axedone; + } + angle = pmo->angle-i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, AXERANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, AXERANGE, slope, damage); + if (linetarget->flags&MF_COUNTKILL) + { + P_ThrustMobj(linetarget, angle, power); + } + AdjustPlayerAngle(pmo); + useMana++; + goto axedone; + } + } + // didn't find any creatures, so try to strike any walls + pmo->special1 = 0; + + angle = pmo->angle; + slope = P_AimLineAttack(pmo, angle, MELEERANGE); + P_LineAttack(pmo, angle, MELEERANGE, slope, damage); + +axedone: + if(useMana == 2) + { + player->mana[MANA_1] -= + WeaponManaUse[player->class][player->readyweapon]; + if(player->mana[MANA_1] <= 0) + { + P_SetPsprite(player, ps_weapon, S_FAXEATK_5); + } + } + return; +} + +//=========================================================================== +// +// A_CMaceAttack +// +//=========================================================================== + +void A_CMaceAttack(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + int i; + + damage = 25+(P_Random()&15); + PuffType = MT_HAMMERPUFF; + for(i = 0; i < 16; i++) + { + angle = player->mo->angle+i*(ANG45/16); + slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE); + if(linetarget) + { + P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, + damage); + AdjustPlayerAngle(player->mo); +// player->mo->angle = R_PointToAngle2(player->mo->x, +// player->mo->y, linetarget->x, linetarget->y); + goto macedone; + } + angle = player->mo->angle-i*(ANG45/16); + slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE); + if(linetarget) + { + P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, + damage); + AdjustPlayerAngle(player->mo); +// player->mo->angle = R_PointToAngle2(player->mo->x, +// player->mo->y, linetarget->x, linetarget->y); + goto macedone; + } + } + // didn't find any creatures, so try to strike any walls + player->mo->special1 = 0; + + angle = player->mo->angle; + slope = P_AimLineAttack(player->mo, angle, MELEERANGE); + P_LineAttack(player->mo, angle, MELEERANGE, slope, + damage); +macedone: + return; +} + +//============================================================================ +// +// A_CStaffCheck +// +//============================================================================ + +void A_CStaffCheck(player_t *player, pspdef_t *psp) +{ + mobj_t *pmo; + int damage; + int newLife; + angle_t angle; + int slope; + int i; + + pmo = player->mo; + damage = 20+(P_Random()&15); + PuffType = MT_CSTAFFPUFF; + for(i = 0; i < 3; i++) + { + angle = pmo->angle+i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, 1.5*MELEERANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage); + pmo->angle = R_PointToAngle2(pmo->x, pmo->y, + linetarget->x, linetarget->y); + if((linetarget->player || linetarget->flags&MF_COUNTKILL) + && (!(linetarget->flags2&(MF2_DORMANT+MF2_INVULNERABLE)))) + { + newLife = player->health+(damage>>3); + newLife = newLife > 100 ? 100 : newLife; + pmo->health = player->health = newLife; + P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); + } + player->mana[MANA_1] -= + WeaponManaUse[player->class][player->readyweapon]; + break; + } + angle = pmo->angle-i*(ANG45/16); + slope = P_AimLineAttack(player->mo, angle, 1.5*MELEERANGE); + if(linetarget) + { + P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage); + pmo->angle = R_PointToAngle2(pmo->x, pmo->y, + linetarget->x, linetarget->y); + if(linetarget->player || linetarget->flags&MF_COUNTKILL) + { + newLife = player->health+(damage>>4); + newLife = newLife > 100 ? 100 : newLife; + pmo->health = player->health = newLife; + P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); + } + player->mana[MANA_1] -= + WeaponManaUse[player->class][player->readyweapon]; + break; + } + } +} + +//============================================================================ +// +// A_CStaffAttack +// +//============================================================================ + +void A_CStaffAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *mo; + mobj_t *pmo; + + player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; + pmo = player->mo; + mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle-(ANG45/15)); + if(mo) + { + mo->special2 = 32; + } + mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle+(ANG45/15)); + if(mo) + { + mo->special2 = 0; + } + S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE); +} + +//============================================================================ +// +// A_CStaffMissileSlither +// +//============================================================================ + +void A_CStaffMissileSlither(mobj_t *actor) +{ + fixed_t newX, newY; + int weaveXY; + int angle; + + weaveXY = actor->special2; + angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + newX = actor->x-FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]); + newY = actor->y-FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]); + weaveXY = (weaveXY+3)&63; + newX += FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]); + newY += FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]); + P_TryMove(actor, newX, newY); + actor->special2 = weaveXY; +} + +//============================================================================ +// +// A_CStaffInitBlink +// +//============================================================================ + +void A_CStaffInitBlink(player_t *player, pspdef_t *psp) +{ + player->mo->special1 = (P_Random()>>1)+20; +} + +//============================================================================ +// +// A_CStaffCheckBlink +// +//============================================================================ + +void A_CStaffCheckBlink(player_t *player, pspdef_t *psp) +{ + if(!--player->mo->special1) + { + P_SetPsprite(player, ps_weapon, S_CSTAFFBLINK1); + player->mo->special1 = (P_Random()+50)>>2; + } +} + +//============================================================================ +// +// A_CFlameAttack +// +//============================================================================ + +#define FLAMESPEED (0.45*FRACUNIT) +#define CFLAMERANGE (12*64*FRACUNIT) + +void A_CFlameAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *mo; + + mo = P_SpawnPlayerMissile(player->mo, MT_CFLAME_MISSILE); + if(mo) + { + mo->thinker.function = P_BlasterMobjThinker; + mo->special1 = 2; + } + + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); +} + +//============================================================================ +// +// A_CFlamePuff +// +//============================================================================ + +void A_CFlamePuff(mobj_t *actor) +{ + A_UnHideThing(actor); + actor->momx = 0; + actor->momy = 0; + actor->momz = 0; + S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); +} + +//============================================================================ +// +// A_CFlameMissile +// +//============================================================================ + +void A_CFlameMissile(mobj_t *actor) +{ + int i; + int an, an90; + fixed_t dist; + mobj_t *mo; + + A_UnHideThing(actor); + S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); + if(BlockingMobj && BlockingMobj->flags&MF_SHOOTABLE) + { // Hit something, so spawn the flame circle around the thing + dist = BlockingMobj->radius+18*FRACUNIT; + for(i = 0; i < 4; i++) + { + an = (i*ANG45)>>ANGLETOFINESHIFT; + an90 = (i*ANG45+ANG90)>>ANGLETOFINESHIFT; + mo = P_SpawnMobj(BlockingMobj->x+FixedMul(dist, finecosine[an]), + BlockingMobj->y+FixedMul(dist, finesine[an]), + BlockingMobj->z+5*FRACUNIT, MT_CIRCLEFLAME); + if(mo) + { + mo->angle = an<target = actor->target; + mo->momx = mo->special1 = FixedMul(FLAMESPEED, finecosine[an]); + mo->momy = mo->special2 = FixedMul(FLAMESPEED, finesine[an]); + mo->tics -= P_Random()&3; + } + mo = P_SpawnMobj(BlockingMobj->x-FixedMul(dist, finecosine[an]), + BlockingMobj->y-FixedMul(dist, finesine[an]), + BlockingMobj->z+5*FRACUNIT, MT_CIRCLEFLAME); + if(mo) + { + mo->angle = ANG180+(an<target = actor->target; + mo->momx = mo->special1 = FixedMul(-FLAMESPEED, + finecosine[an]); + mo->momy = mo->special2 = FixedMul(-FLAMESPEED, finesine[an]); + mo->tics -= P_Random()&3; + } + } + P_SetMobjState(actor, S_FLAMEPUFF2_1); + } +} + +/* +void A_CFlameAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *pmo; + angle_t angle; + int damage; + int i; + int an, an90; + fixed_t dist; + mobj_t *mo; + + pmo = player->mo; + P_BulletSlope(pmo); + damage = 25+HITDICE(3); + angle = pmo->angle; + if(player->refire) + { + angle += (P_Random()-P_Random())<<17; + } + P_AimLineAttack(pmo, angle, CFLAMERANGE); // Correctly set linetarget + if(!linetarget) + { + angle += ANGLE_1*2; + P_AimLineAttack(pmo, angle, CFLAMERANGE); + if(!linetarget) + { + angle -= ANGLE_1*4; + P_AimLineAttack(pmo, angle, CFLAMERANGE); + if(!linetarget) + { + angle += ANGLE_1*2; + } + } + } + if(linetarget) + { + PuffType = MT_FLAMEPUFF2; + } + else + { + PuffType = MT_FLAMEPUFF; + } + P_LineAttack(pmo, angle, CFLAMERANGE, bulletslope, damage); + if(linetarget) + { // Hit something, so spawn the flame circle around the thing + dist = linetarget->radius+18*FRACUNIT; + for(i = 0; i < 4; i++) + { + an = (i*ANG45)>>ANGLETOFINESHIFT; + an90 = (i*ANG45+ANG90)>>ANGLETOFINESHIFT; + mo = P_SpawnMobj(linetarget->x+FixedMul(dist, finecosine[an]), + linetarget->y+FixedMul(dist, finesine[an]), + linetarget->z+5*FRACUNIT, MT_CIRCLEFLAME); + if(mo) + { + mo->angle = an<target = pmo; + mo->momx = mo->special1 = FixedMul(FLAMESPEED, finecosine[an]); + mo->momy = mo->special2 = FixedMul(FLAMESPEED, finesine[an]); + mo->tics -= P_Random()&3; + } + mo = P_SpawnMobj(linetarget->x-FixedMul(dist, finecosine[an]), + linetarget->y-FixedMul(dist, finesine[an]), + linetarget->z+5*FRACUNIT, MT_CIRCLEFLAME); + if(mo) + { + mo->angle = ANG180+(an<target = pmo; + mo->momx = mo->special1 = FixedMul(-FLAMESPEED, + finecosine[an]); + mo->momy = mo->special2 = FixedMul(-FLAMESPEED, finesine[an]); + mo->tics -= P_Random()&3; + } + } + } +// Create a line of flames from the player to the flame puff + CFlameCreateFlames(player->mo); + + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); +} +*/ + +//============================================================================ +// +// A_CFlameRotate +// +//============================================================================ + +#define FLAMEROTSPEED 2*FRACUNIT + +void A_CFlameRotate(mobj_t *actor) +{ + int an; + + an = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + actor->momx = actor->special1+FixedMul(FLAMEROTSPEED, finecosine[an]); + actor->momy = actor->special2+FixedMul(FLAMEROTSPEED, finesine[an]); + actor->angle += ANG90/15; +} + + +//============================================================================ +// +// A_CHolyAttack3 +// +// Spawns the spirits +//============================================================================ + +void A_CHolyAttack3(mobj_t *actor) +{ + P_SpawnMissile(actor, actor->target, MT_HOLY_MISSILE); + S_StartSound(actor, SFX_CHOLY_FIRE); +} + + +//============================================================================ +// +// A_CHolyAttack2 +// +// Spawns the spirits +//============================================================================ + +void A_CHolyAttack2(mobj_t *actor) +{ + int j; + int i; + mobj_t *mo; + mobj_t *tail, *next; + + for(j = 0; j < 4; j++) + { + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_FX); + if(!mo) + { + continue; + } + switch(j) + { // float bob index + case 0: + mo->special2 = P_Random()&7; // upper-left + break; + case 1: + mo->special2 = 32+(P_Random()&7); // upper-right + break; + case 2: + mo->special2 = (32+(P_Random()&7))<<16; // lower-left + break; + case 3: + mo->special2 = ((32+(P_Random()&7))<<16)+32+(P_Random()&7); + break; + } + mo->z = actor->z; + mo->angle = actor->angle+(ANGLE_45+ANGLE_45/2)-ANGLE_45*j; + P_ThrustMobj(mo, mo->angle, mo->info->speed); + mo->target = actor->target; + mo->args[0] = 10; // initial turn value + mo->args[1] = 0; // initial look angle + if(deathmatch) + { // Ghosts last slightly less longer in DeathMatch + mo->health = 85; + } + if(linetarget) + { + mo->special1 = (int)linetarget; + mo->flags |= MF_NOCLIP|MF_SKULLFLY; + mo->flags &= ~MF_MISSILE; + } + tail = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); + tail->special2 = (int)mo; // parent + for(i = 1; i < 3; i++) + { + next = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); + P_SetMobjState(next, next->info->spawnstate+1); + tail->special1 = (int)next; + tail = next; + } + tail->special1 = 0; // last tail bit + } +} + +//============================================================================ +// +// A_CHolyAttack +// +//============================================================================ + +void A_CHolyAttack(player_t *player, pspdef_t *psp) +{ + mobj_t *mo; + + player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; + player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; + mo = P_SpawnPlayerMissile(player->mo, MT_HOLY_MISSILE); + if(player == &players[consoleplayer]) + { + player->damagecount = 0; + player->bonuscount = 0; + I_SetPalette((byte *)W_CacheLumpNum(W_GetNumForName("playpal"), + PU_CACHE)+STARTHOLYPAL*768); + } + S_StartSound(player->mo, SFX_CHOLY_FIRE); +} + +//============================================================================ +// +// A_CHolyPalette +// +//============================================================================ + +void A_CHolyPalette(player_t *player, pspdef_t *psp) +{ + int pal; + + if(player == &players[consoleplayer]) + { + pal = STARTHOLYPAL+psp->state-(&states[S_CHOLYATK_6]); + if(pal == STARTHOLYPAL+3) + { // reset back to original playpal + pal = 0; + } + I_SetPalette((byte *)W_CacheLumpNum(W_GetNumForName("playpal"), + PU_CACHE)+pal*768); + } +} + +//============================================================================ +// +// CHolyFindTarget +// +//============================================================================ + +static void CHolyFindTarget(mobj_t *actor) +{ + mobj_t *target; + + if(target = P_RoughMonsterSearch(actor, 6)) + { + actor->special1 = (int)target; + actor->flags |= MF_NOCLIP|MF_SKULLFLY; + actor->flags &= ~MF_MISSILE; + } +} + +//============================================================================ +// +// CHolySeekerMissile +// +// Similar to P_SeekerMissile, but seeks to a random Z on the target +//============================================================================ + +static void CHolySeekerMissile(mobj_t *actor, angle_t thresh, angle_t turnMax) +{ + int dir; + int dist; + angle_t delta; + angle_t angle; + mobj_t *target; + fixed_t newZ; + fixed_t deltaZ; + + target = (mobj_t *)actor->special1; + if(target == NULL) + { + return; + } + if(!(target->flags&MF_SHOOTABLE) + || (!(target->flags&MF_COUNTKILL) && !target->player)) + { // Target died/target isn't a player or creature + actor->special1 = 0; + actor->flags &= ~(MF_NOCLIP|MF_SKULLFLY); + actor->flags |= MF_MISSILE; + CHolyFindTarget(actor); + return; + } + dir = P_FaceMobj(actor, target, &delta); + if(delta > thresh) + { + delta >>= 1; + if(delta > turnMax) + { + delta = turnMax; + } + } + if(dir) + { // Turn clockwise + actor->angle += delta; + } + else + { // Turn counter clockwise + actor->angle -= delta; + } + angle = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[angle]); + actor->momy = FixedMul(actor->info->speed, finesine[angle]); + if(!(leveltime&15) + || actor->z > target->z+(target->height) + || actor->z+actor->height < target->z) + { + newZ = target->z+((P_Random()*target->height)>>8); + deltaZ = newZ-actor->z; + if(abs(deltaZ) > 15*FRACUNIT) + { + if(deltaZ > 0) + { + deltaZ = 15*FRACUNIT; + } + else + { + deltaZ = -15*FRACUNIT; + } + } + dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); + dist = dist/actor->info->speed; + if(dist < 1) + { + dist = 1; + } + actor->momz = deltaZ/dist; + } + return; +} + +//============================================================================ +// +// A_CHolyWeave +// +//============================================================================ + +static void CHolyWeave(mobj_t *actor) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + + weaveXY = actor->special2>>16; + weaveZ = actor->special2&0xFFFF; + angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT; + newX = actor->x-FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY = actor->y-FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + weaveXY = (weaveXY+(P_Random()%5))&63; + newX += FixedMul(finecosine[angle], + FloatBobOffsets[weaveXY]<<2); + newY += FixedMul(finesine[angle], + FloatBobOffsets[weaveXY]<<2); + P_TryMove(actor, newX, newY); + actor->z -= FloatBobOffsets[weaveZ]<<1; + weaveZ = (weaveZ+(P_Random()%5))&63; + actor->z += FloatBobOffsets[weaveZ]<<1; + actor->special2 = weaveZ+(weaveXY<<16); +} + +//============================================================================ +// +// A_CHolySeek +// +//============================================================================ + +void A_CHolySeek(mobj_t *actor) +{ + actor->health--; + if(actor->health <= 0) + { + actor->momx >>= 2; + actor->momy >>= 2; + actor->momz = 0; + P_SetMobjState(actor, actor->info->deathstate); + actor->tics -= P_Random()&3; + return; + } + if(actor->special1) + { + CHolySeekerMissile(actor, actor->args[0]*ANGLE_1, + actor->args[0]*ANGLE_1*2); + if(!((leveltime+7)&15)) + { + actor->args[0] = 5+(P_Random()/20); + } + } + CHolyWeave(actor); +} + +//============================================================================ +// +// CHolyTailFollow +// +//============================================================================ + +static void CHolyTailFollow(mobj_t *actor, fixed_t dist) +{ + mobj_t *child; + int an; + fixed_t oldDistance, newDistance; + + child = (mobj_t *)actor->special1; + if(child) + { + an = R_PointToAngle2(actor->x, actor->y, child->x, + child->y)>>ANGLETOFINESHIFT; + oldDistance = P_AproxDistance(child->x-actor->x, child->y-actor->y); + if(P_TryMove(child, actor->x+FixedMul(dist, finecosine[an]), + actor->y+FixedMul(dist, finesine[an]))) + { + newDistance = P_AproxDistance(child->x-actor->x, + child->y-actor->y)-FRACUNIT; + if(oldDistance < FRACUNIT) + { + if(child->z < actor->z) + { + child->z = actor->z-dist; + } + else + { + child->z = actor->z+dist; + } + } + else + { + child->z = actor->z+FixedMul(FixedDiv(newDistance, + oldDistance), child->z-actor->z); + } + } + CHolyTailFollow(child, dist-FRACUNIT); + } +} + +//============================================================================ +// +// CHolyTailRemove +// +//============================================================================ + +static void CHolyTailRemove(mobj_t *actor) +{ + mobj_t *child; + + child = (mobj_t *)actor->special1; + if(child) + { + CHolyTailRemove(child); + } + P_RemoveMobj(actor); +} + +//============================================================================ +// +// A_CHolyTail +// +//============================================================================ + +void A_CHolyTail(mobj_t *actor) +{ + mobj_t *parent; + + parent = (mobj_t *)actor->special2; + + if(parent) + { + if(parent->state >= &states[parent->info->deathstate]) + { // Ghost removed, so remove all tail parts + CHolyTailRemove(actor); + return; + } + else if(P_TryMove(actor, parent->x-FixedMul(14*FRACUNIT, + finecosine[parent->angle>>ANGLETOFINESHIFT]), + parent->y-FixedMul(14*FRACUNIT, + finesine[parent->angle>>ANGLETOFINESHIFT]))) + { + actor->z = parent->z-5*FRACUNIT; + } + CHolyTailFollow(actor, 10*FRACUNIT); + } +} +//============================================================================ +// +// A_CHolyCheckScream +// +//============================================================================ + +void A_CHolyCheckScream(mobj_t *actor) +{ + A_CHolySeek(actor); + if(P_Random() < 20) + { + S_StartSound(actor, SFX_SPIRIT_ACTIVE); + } + if(!actor->special1) + { + CHolyFindTarget(actor); + } +} + +//============================================================================ +// +// A_CHolySpawnPuff +// +//============================================================================ + +void A_CHolySpawnPuff(mobj_t *actor) +{ + P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_MISSILE_PUFF); +} + +//---------------------------------------------------------------------------- +// +// PROC A_FireConePL1 +// +//---------------------------------------------------------------------------- + +#define SHARDSPAWN_LEFT 1 +#define SHARDSPAWN_RIGHT 2 +#define SHARDSPAWN_UP 4 +#define SHARDSPAWN_DOWN 8 + +void A_FireConePL1(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + int i; + mobj_t *pmo,*mo; + int conedone=false; + + pmo = player->mo; + player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; + S_StartSound(pmo, SFX_MAGE_SHARDS_FIRE); + + damage = 90+(P_Random()&15); + for(i = 0; i < 16; i++) + { + angle = pmo->angle+i*(ANG45/16); + slope = P_AimLineAttack(pmo, angle, MELEERANGE); + if(linetarget) + { + pmo->flags2 |= MF2_ICEDAMAGE; + P_DamageMobj(linetarget, pmo, pmo, damage); + pmo->flags2 &= ~MF2_ICEDAMAGE; + conedone = true; + break; + } + } + + // didn't find any creatures, so fire projectiles + if (!conedone) + { + mo = P_SpawnPlayerMissile(pmo, MT_SHARDFX1); + if (mo) + { + mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP + |SHARDSPAWN_RIGHT; + mo->special2 = 3; // Set sperm count (levels of reproductivity) + mo->target = pmo; + mo->args[0] = 3; // Mark Initial shard as super damage + } + } +} + +void A_ShedShard(mobj_t *actor) +{ + mobj_t *mo; + int spawndir = actor->special1; + int spermcount = actor->special2; + + if (spermcount <= 0) return; // No sperm left + actor->special2 = 0; + spermcount--; + + // every so many calls, spawn a new missile in it's set directions + if (spawndir & SHARDSPAWN_LEFT) + { + mo=P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle+(ANG45/9), + 0, (20+2*spermcount)<special1 = SHARDSPAWN_LEFT; + mo->special2 = spermcount; + mo->momz = actor->momz; + mo->target = actor->target; + mo->args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_RIGHT) + { + mo=P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle-(ANG45/9), + 0, (20+2*spermcount)<special1 = SHARDSPAWN_RIGHT; + mo->special2 = spermcount; + mo->momz = actor->momz; + mo->target = actor->target; + mo->args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_UP) + { + mo=P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, + 0, (15+2*spermcount)<momz = actor->momz; + mo->z += 8*FRACUNIT; + if (spermcount & 1) // Every other reproduction + mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; + else + mo->special1 = SHARDSPAWN_UP; + mo->special2 = spermcount; + mo->target = actor->target; + mo->args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_DOWN) + { + mo=P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, + 0, (15+2*spermcount)<momz = actor->momz; + mo->z -= 4*FRACUNIT; + if (spermcount & 1) // Every other reproduction + mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; + else + mo->special1 = SHARDSPAWN_DOWN; + mo->special2 = spermcount; + mo->target = actor->target; + mo->args[0] = (spermcount==3)?2:0; + } + } +} + +//---------------------------------------------------------------------------- +// +// PROC A_HideInCeiling +// +//---------------------------------------------------------------------------- + +/* +void A_HideInCeiling(mobj_t *actor) +{ + actor->z = actor->ceilingz+4*FRACUNIT; +} +*/ + +//---------------------------------------------------------------------------- +// +// PROC A_FloatPuff +// +//---------------------------------------------------------------------------- + +/* +void A_FloatPuff(mobj_t *puff) +{ + puff->momz += 1.8*FRACUNIT; +} +*/ + +void A_Light0(player_t *player, pspdef_t *psp) +{ + player->extralight = 0; +} + +/* +void A_Light1(player_t *player, pspdef_t *psp) +{ + player->extralight = 1; +} +*/ + +/* +void A_Light2(player_t *player, pspdef_t *psp) +{ + player->extralight = 2; +} +*/ + +//------------------------------------------------------------------------ +// +// PROC P_SetupPsprites +// +// Called at start of level for each player +// +//------------------------------------------------------------------------ + +void P_SetupPsprites(player_t *player) +{ + int i; + + // Remove all psprites + for(i = 0; i < NUMPSPRITES; i++) + { + player->psprites[i].state = NULL; + } + // Spawn the ready weapon + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + +//------------------------------------------------------------------------ +// +// PROC P_MovePsprites +// +// Called every tic by player thinking routine +// +//------------------------------------------------------------------------ + +void P_MovePsprites(player_t *player) +{ + int i; + pspdef_t *psp; + state_t *state; + + psp = &player->psprites[0]; + for(i = 0; i < NUMPSPRITES; i++, psp++) + { + if((state = psp->state) != 0) // a null state means not active + { + // drop tic count and possibly change state + if(psp->tics != -1) // a -1 tic count never changes + { + psp->tics--; + if(!psp->tics) + { + P_SetPsprite(player, i, psp->state->nextstate); + } + } + } + } + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/src/hexen/p_setup.c b/src/hexen/p_setup.c new file mode 100644 index 00000000..03f5b7ed --- /dev/null +++ b/src/hexen/p_setup.c @@ -0,0 +1,1218 @@ + +//************************************************************************** +//** +//** p_setup.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_setup.c,v $ +//** $Revision: 1.39 $ +//** $Date: 96/03/12 12:04:11 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define MAPINFO_SCRIPT_NAME "MAPINFO" +#define MCMD_SKY1 1 +#define MCMD_SKY2 2 +#define MCMD_LIGHTNING 3 +#define MCMD_FADETABLE 4 +#define MCMD_DOUBLESKY 5 +#define MCMD_CLUSTER 6 +#define MCMD_WARPTRANS 7 +#define MCMD_NEXT 8 +#define MCMD_CDTRACK 9 +#define MCMD_CD_STARTTRACK 10 +#define MCMD_CD_END1TRACK 11 +#define MCMD_CD_END2TRACK 12 +#define MCMD_CD_END3TRACK 13 +#define MCMD_CD_INTERTRACK 14 +#define MCMD_CD_TITLETRACK 15 + +#define UNKNOWN_MAP_NAME "DEVELOPMENT MAP" +#define DEFAULT_SKY_NAME "SKY1" +#define DEFAULT_SONG_LUMP "DEFSONG" +#define DEFAULT_FADE_TABLE "COLORMAP" + +// TYPES ------------------------------------------------------------------- + +typedef struct mapInfo_s mapInfo_t; +struct mapInfo_s +{ + short cluster; + short warpTrans; + short nextMap; + short cdTrack; + char name[32]; + short sky1Texture; + short sky2Texture; + fixed_t sky1ScrollDelta; + fixed_t sky2ScrollDelta; + boolean doubleSky; + boolean lightning; + int fadetable; + char songLump[10]; +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +void P_SpawnMapThing(mapthing_t *mthing); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static int QualifyMap(int map); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int MapCount; +mapthing_t deathmatchstarts[MAXDEATHMATCHSTARTS], *deathmatch_p; +mapthing_t playerstarts[MAX_PLAYER_STARTS][MAXPLAYERS]; +int numvertexes; +vertex_t *vertexes; +int numsegs; +seg_t *segs; +int numsectors; +sector_t *sectors; +int numsubsectors; +subsector_t *subsectors; +int numnodes; +node_t *nodes; +int numlines; +line_t *lines; +int numsides; +side_t *sides; +short *blockmaplump; // offsets in blockmap are from here +short *blockmap; +int bmapwidth, bmapheight; // in mapblocks +fixed_t bmaporgx, bmaporgy; // origin of block map +mobj_t **blocklinks; // for thing chains +byte *rejectmatrix; // for fast sight rejection + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static mapInfo_t MapInfo[99]; +static char *MapCmdNames[] = +{ + "SKY1", + "SKY2", + "DOUBLESKY", + "LIGHTNING", + "FADETABLE", + "CLUSTER", + "WARPTRANS", + "NEXT", + "CDTRACK", + "CD_START_TRACK", + "CD_END1_TRACK", + "CD_END2_TRACK", + "CD_END3_TRACK", + "CD_INTERMISSION_TRACK", + "CD_TITLE_TRACK", + NULL +}; +static int MapCmdIDs[] = +{ + MCMD_SKY1, + MCMD_SKY2, + MCMD_DOUBLESKY, + MCMD_LIGHTNING, + MCMD_FADETABLE, + MCMD_CLUSTER, + MCMD_WARPTRANS, + MCMD_NEXT, + MCMD_CDTRACK, + MCMD_CD_STARTTRACK, + MCMD_CD_END1TRACK, + MCMD_CD_END2TRACK, + MCMD_CD_END3TRACK, + MCMD_CD_INTERTRACK, + MCMD_CD_TITLETRACK +}; + +static int cd_NonLevelTracks[6]; // Non-level specific song cd track numbers + +// CODE -------------------------------------------------------------------- + +/* +================= += += P_LoadVertexes += +================= +*/ + +void P_LoadVertexes (int lump) +{ + byte *data; + int i; + mapvertex_t *ml; + vertex_t *li; + + numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t); + vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); + data = W_CacheLumpNum (lump,PU_STATIC); + + ml = (mapvertex_t *)data; + li = vertexes; + for (i=0 ; ix = SHORT(ml->x)<y = SHORT(ml->y)<v1 = &vertexes[SHORT(ml->v1)]; + li->v2 = &vertexes[SHORT(ml->v2)]; + + li->angle = (SHORT(ml->angle))<<16; + li->offset = (SHORT(ml->offset))<<16; + linedef = SHORT(ml->linedef); + ldef = &lines[linedef]; + li->linedef = ldef; + side = SHORT(ml->side); + li->sidedef = &sides[ldef->sidenum[side]]; + li->frontsector = sides[ldef->sidenum[side]].sector; + if (ldef-> flags & ML_TWOSIDED) + li->backsector = sides[ldef->sidenum[side^1]].sector; + else + li->backsector = 0; + } + + Z_Free (data); +} + + +/* +================= += += P_LoadSubsectors += +================= +*/ + +void P_LoadSubsectors (int lump) +{ + byte *data; + int i; + mapsubsector_t *ms; + subsector_t *ss; + + numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); + subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0); + data = W_CacheLumpNum (lump,PU_STATIC); + + ms = (mapsubsector_t *)data; + memset (subsectors,0, numsubsectors*sizeof(subsector_t)); + ss = subsectors; + for (i=0 ; inumlines = SHORT(ms->numsegs); + ss->firstline = SHORT(ms->firstseg); + } + + Z_Free (data); +} + + +/* +================= += += P_LoadSectors += +================= +*/ + +void P_LoadSectors (int lump) +{ + byte *data; + int i; + mapsector_t *ms; + sector_t *ss; + + numsectors = W_LumpLength (lump) / sizeof(mapsector_t); + sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0); + memset (sectors, 0, numsectors*sizeof(sector_t)); + data = W_CacheLumpNum (lump,PU_STATIC); + + ms = (mapsector_t *)data; + ss = sectors; + + // Make sure primary lumps are used for flat searching + W_UsePrimary(); + + for(i = 0; i < numsectors; i++, ss++, ms++) + { + ss->floorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = NULL; + ss->seqType = SEQTYPE_STONE; // default seqType + } + if(DevMaps) + { + W_UseAuxiliary(); + } + Z_Free(data); +} + + +/* +================= += += P_LoadNodes += +================= +*/ + +void P_LoadNodes (int lump) +{ + byte *data; + int i,j,k; + mapnode_t *mn; + node_t *no; + + numnodes = W_LumpLength (lump) / sizeof(mapnode_t); + nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); + data = W_CacheLumpNum (lump,PU_STATIC); + + mn = (mapnode_t *)data; + no = nodes; + for (i=0 ; ix = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = SHORT(mn->children[j]); + for (k=0 ; k<4 ; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k])<tid = SHORT(mt->tid); + mt->x = SHORT(mt->x); + mt->y = SHORT(mt->y); + mt->height = SHORT(mt->height); + mt->angle = SHORT(mt->angle); + mt->type = SHORT(mt->type); + mt->options = SHORT(mt->options); + P_SpawnMapThing(mt); + } + P_CreateTIDList(); + P_InitCreatureCorpseQueue(false); // false = do NOT scan for corpses + Z_Free(data); + + if(!deathmatch) + { // Don't need to check deathmatch spots + return; + } + playerCount = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + playerCount += playeringame[i]; + } + deathSpotsCount = deathmatch_p-deathmatchstarts; + if(deathSpotsCount < playerCount) + { + I_Error("P_LoadThings: Player count (%d) exceeds deathmatch " + "spots (%d)", playerCount, deathSpotsCount); + } +} + +/* +================= += += P_LoadLineDefs += +================= +*/ + +void P_LoadLineDefs(int lump) +{ + byte *data; + int i; + maplinedef_t *mld; + line_t *ld; + vertex_t *v1, *v2; + + numlines = W_LumpLength(lump)/sizeof(maplinedef_t); + lines = Z_Malloc(numlines*sizeof(line_t), PU_LEVEL, 0); + memset(lines, 0, numlines*sizeof(line_t)); + data = W_CacheLumpNum(lump, PU_STATIC); + + mld = (maplinedef_t *)data; + ld = lines; + for(i = 0; i < numlines; i++, mld++, ld++) + { + ld->flags = SHORT(mld->flags); + + // Old line special info ... + //ld->special = SHORT(mld->special); + //ld->tag = SHORT(mld->tag); + + // New line special info ... + ld->special = mld->special; + ld->arg1 = mld->arg1; + ld->arg2 = mld->arg2; + ld->arg3 = mld->arg3; + ld->arg4 = mld->arg4; + ld->arg5 = mld->arg5; + + v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; + v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + if (!ld->dx) + ld->slopetype = ST_VERTICAL; + else if (!ld->dy) + ld->slopetype = ST_HORIZONTAL; + else + { + if (FixedDiv (ld->dy , ld->dx) > 0) + ld->slopetype = ST_POSITIVE; + else + ld->slopetype = ST_NEGATIVE; + } + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + if (ld->sidenum[0] != -1) + ld->frontsector = sides[ld->sidenum[0]].sector; + else + ld->frontsector = 0; + if (ld->sidenum[1] != -1) + ld->backsector = sides[ld->sidenum[1]].sector; + else + ld->backsector = 0; + } + + Z_Free (data); +} + + +/* +================= += += P_LoadSideDefs += +================= +*/ + +void P_LoadSideDefs (int lump) +{ + byte *data; + int i; + mapsidedef_t *msd; + side_t *sd; + + numsides = W_LumpLength (lump) / sizeof(mapsidedef_t); + sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0); + memset (sides, 0, numsides*sizeof(side_t)); + data = W_CacheLumpNum (lump,PU_STATIC); + + msd = (mapsidedef_t *)data; + sd = sides; + + // Make sure primary lumps are used for texture searching + W_UsePrimary(); + + for(i = 0; i < numsides; i++, msd++, sd++) + { + sd->textureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<toptexture = R_TextureNumForName(msd->toptexture); + sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); + sd->midtexture = R_TextureNumForName(msd->midtexture); + sd->sector = §ors[SHORT(msd->sector)]; + } + if(DevMaps) + { + W_UseAuxiliary(); + } + Z_Free(data); +} + +/* +================= += += P_LoadBlockMap += +================= +*/ + +void P_LoadBlockMap (int lump) +{ + int i, count; + + blockmaplump = W_CacheLumpNum (lump,PU_LEVEL); + blockmap = blockmaplump+4; + count = W_LumpLength (lump)/2; + for (i=0 ; ifirstline]; + ss->sector = seg->sidedef->sector; + } + +// count number of lines in each sector + li = lines; + total = 0; + for (i=0 ; ifrontsector->linecount++; + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + +// build line tables for each sector + linebuffer = Z_Malloc (total*4, PU_LEVEL, 0); + sector = sectors; + for (i=0 ; ilines = linebuffer; + li = lines; + for (j=0 ; jfrontsector == sector || li->backsector == sector) + { + *linebuffer++ = li; + M_AddToBox (bbox, li->v1->x, li->v1->y); + M_AddToBox (bbox, li->v2->x, li->v2->y); + } + } + if (linebuffer - sector->lines != sector->linecount) + I_Error ("P_GroupLines: miscounted"); + + // set the degenmobj_t to the middle of the bounding box + sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; + sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; + + // adjust bounding box to map blocks + block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight-1 : block; + sector->blockbox[BOXTOP]=block; + + block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM]=block; + + block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth-1 : block; + sector->blockbox[BOXRIGHT]=block; + + block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT]=block; + } + +} + +//============================================================================= + + +/* +================= += += P_SetupLevel += +================= +*/ + +#ifdef __WATCOMC__ + extern boolean i_CDMusic; +#endif + +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + int parm; + char lumpname[9]; + char auxName[128]; + int lumpnum; + mobj_t *mobj; + + for(i = 0; i < MAXPLAYERS; i++) + { + players[i].killcount = players[i].secretcount + = players[i].itemcount = 0; + } + players[consoleplayer].viewz = 1; // will be set by player think + +#ifdef __WATCOMC__ + if(i_CDMusic == false) + { + S_StartSongName("chess", true); // Waiting-for-level-load song + } +#endif + + Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); + + P_InitThinkers(); + leveltime = 0; + + if(DevMaps) + { + sprintf(auxName, "%sMAP%02d.WAD", DevMapsDir, map); + W_OpenAuxiliary(auxName); + } + sprintf(lumpname, "MAP%02d", map); + lumpnum = W_GetNumForName(lumpname); + // + // Begin processing map lumps + // Note: most of this ordering is important + // + P_LoadBlockMap(lumpnum+ML_BLOCKMAP); + P_LoadVertexes(lumpnum+ML_VERTEXES); + P_LoadSectors(lumpnum+ML_SECTORS); + P_LoadSideDefs(lumpnum+ML_SIDEDEFS); + P_LoadLineDefs(lumpnum+ML_LINEDEFS); + P_LoadSubsectors(lumpnum+ML_SSECTORS); + P_LoadNodes(lumpnum+ML_NODES); + P_LoadSegs(lumpnum+ML_SEGS); + rejectmatrix = W_CacheLumpNum(lumpnum+ML_REJECT, PU_LEVEL); + P_GroupLines(); + bodyqueslot = 0; + po_NumPolyobjs = 0; + deathmatch_p = deathmatchstarts; + P_LoadThings(lumpnum+ML_THINGS); + PO_Init(lumpnum+ML_THINGS); // Initialize the polyobjs + P_LoadACScripts(lumpnum+ML_BEHAVIOR); // ACS object code + // + // End of map lump processing + // + if(DevMaps) + { + // Close the auxiliary file, but don't free its loaded lumps. + // The next call to W_OpenAuxiliary() will do a full shutdown + // of the current auxiliary WAD (free lumps and info lists). + W_CloseAuxiliaryFile(); + W_UsePrimary(); + } + + // If deathmatch, randomly spawn the active players + TimerGame = 0; + if(deathmatch) + { + for (i=0 ; icluster = 0; + info->warpTrans = 0; + info->nextMap = 1; // Always go to map 1 if not specified + info->cdTrack = 1; + info->sky1Texture = R_TextureNumForName(DEFAULT_SKY_NAME); + info->sky2Texture = info->sky1Texture; + info->sky1ScrollDelta = 0; + info->sky2ScrollDelta = 0; + info->doubleSky = false; + info->lightning = false; + info->fadetable = W_GetNumForName(DEFAULT_FADE_TABLE); + strcpy(info->name, UNKNOWN_MAP_NAME); + +// strcpy(info->songLump, DEFAULT_SONG_LUMP); + SC_Open(MAPINFO_SCRIPT_NAME); + while(SC_GetString()) + { + if(SC_Compare("MAP") == false) + { + SC_ScriptError(NULL); + } + SC_MustGetNumber(); + if(sc_Number < 1 || sc_Number > 99) + { // + SC_ScriptError(NULL); + } + map = sc_Number; + + info = &MapInfo[map]; + + // Save song lump name + strcpy(songMulch, info->songLump); + + // Copy defaults to current map definition + memcpy(info, &MapInfo[0], sizeof(*info)); + + // Restore song lump name + strcpy(info->songLump, songMulch); + + // The warp translation defaults to the map number + info->warpTrans = map; + + // Map name must follow the number + SC_MustGetString(); + strcpy(info->name, sc_String); + + // Process optional tokens + while(SC_GetString()) + { + if(SC_Compare("MAP")) + { // Start next map definition + SC_UnGet(); + break; + } + mcmdValue = MapCmdIDs[SC_MustMatchString(MapCmdNames)]; + switch(mcmdValue) + { + case MCMD_CLUSTER: + SC_MustGetNumber(); + info->cluster = sc_Number; + break; + case MCMD_WARPTRANS: + SC_MustGetNumber(); + info->warpTrans = sc_Number; + break; + case MCMD_NEXT: + SC_MustGetNumber(); + info->nextMap = sc_Number; + break; + case MCMD_CDTRACK: + SC_MustGetNumber(); + info->cdTrack = sc_Number; + break; + case MCMD_SKY1: + SC_MustGetString(); + info->sky1Texture = R_TextureNumForName(sc_String); + SC_MustGetNumber(); + info->sky1ScrollDelta = sc_Number<<8; + break; + case MCMD_SKY2: + SC_MustGetString(); + info->sky2Texture = R_TextureNumForName(sc_String); + SC_MustGetNumber(); + info->sky2ScrollDelta = sc_Number<<8; + break; + case MCMD_DOUBLESKY: + info->doubleSky = true; + break; + case MCMD_LIGHTNING: + info->lightning = true; + break; + case MCMD_FADETABLE: + SC_MustGetString(); + info->fadetable = W_GetNumForName(sc_String); + break; + case MCMD_CD_STARTTRACK: + case MCMD_CD_END1TRACK: + case MCMD_CD_END2TRACK: + case MCMD_CD_END3TRACK: + case MCMD_CD_INTERTRACK: + case MCMD_CD_TITLETRACK: + SC_MustGetNumber(); + cd_NonLevelTracks[mcmdValue-MCMD_CD_STARTTRACK] = + sc_Number; + break; + } + } + mapMax = map > mapMax ? map : mapMax; + } + SC_Close(); + MapCount = mapMax; +} + +//========================================================================== +// +// P_GetMapCluster +// +//========================================================================== + +int P_GetMapCluster(int map) +{ + return MapInfo[QualifyMap(map)].cluster; +} + +//========================================================================== +// +// P_GetMapCDTrack +// +//========================================================================== + +int P_GetMapCDTrack(int map) +{ + return MapInfo[QualifyMap(map)].cdTrack; +} + +//========================================================================== +// +// P_GetMapWarpTrans +// +//========================================================================== + +int P_GetMapWarpTrans(int map) +{ + return MapInfo[QualifyMap(map)].warpTrans; +} + +//========================================================================== +// +// P_GetMapNextMap +// +//========================================================================== + +int P_GetMapNextMap(int map) +{ + return MapInfo[QualifyMap(map)].nextMap; +} + +//========================================================================== +// +// P_TranslateMap +// +// Returns the actual map number given a warp map number. +// +//========================================================================== + +int P_TranslateMap(int map) +{ + int i; + + for(i = 1; i < 99; i++) // Make this a macro + { + if(MapInfo[i].warpTrans == map) + { + return i; + } + } + // Not found + return -1; +} + +//========================================================================== +// +// P_GetMapSky1Texture +// +//========================================================================== + +int P_GetMapSky1Texture(int map) +{ + return MapInfo[QualifyMap(map)].sky1Texture; +} + +//========================================================================== +// +// P_GetMapSky2Texture +// +//========================================================================== + +int P_GetMapSky2Texture(int map) +{ + return MapInfo[QualifyMap(map)].sky2Texture; +} + +//========================================================================== +// +// P_GetMapName +// +//========================================================================== + +char *P_GetMapName(int map) +{ + return MapInfo[QualifyMap(map)].name; +} + +//========================================================================== +// +// P_GetMapSky1ScrollDelta +// +//========================================================================== + +fixed_t P_GetMapSky1ScrollDelta(int map) +{ + return MapInfo[QualifyMap(map)].sky1ScrollDelta; +} + +//========================================================================== +// +// P_GetMapSky2ScrollDelta +// +//========================================================================== + +fixed_t P_GetMapSky2ScrollDelta(int map) +{ + return MapInfo[QualifyMap(map)].sky2ScrollDelta; +} + +//========================================================================== +// +// P_GetMapDoubleSky +// +//========================================================================== + +boolean P_GetMapDoubleSky(int map) +{ + return MapInfo[QualifyMap(map)].doubleSky; +} + +//========================================================================== +// +// P_GetMapLightning +// +//========================================================================== + +boolean P_GetMapLightning(int map) +{ + return MapInfo[QualifyMap(map)].lightning; +} + +//========================================================================== +// +// P_GetMapFadeTable +// +//========================================================================== + +boolean P_GetMapFadeTable(int map) +{ + return MapInfo[QualifyMap(map)].fadetable; +} + +//========================================================================== +// +// P_GetMapSongLump +// +//========================================================================== + +char *P_GetMapSongLump(int map) +{ + if(!strcasecmp(MapInfo[QualifyMap(map)].songLump, DEFAULT_SONG_LUMP)) + { + return NULL; + } + else + { + return MapInfo[QualifyMap(map)].songLump; + } +} + +//========================================================================== +// +// P_PutMapSongLump +// +//========================================================================== + +void P_PutMapSongLump(int map, char *lumpName) +{ + if(map < 1 || map > MapCount) + { + return; + } + strcpy(MapInfo[map].songLump, lumpName); +} + +//========================================================================== +// +// P_GetCDStartTrack +// +//========================================================================== + +int P_GetCDStartTrack(void) +{ + return cd_NonLevelTracks[MCMD_CD_STARTTRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// P_GetCDEnd1Track +// +//========================================================================== + +int P_GetCDEnd1Track(void) +{ + return cd_NonLevelTracks[MCMD_CD_END1TRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// P_GetCDEnd2Track +// +//========================================================================== + +int P_GetCDEnd2Track(void) +{ + return cd_NonLevelTracks[MCMD_CD_END2TRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// P_GetCDEnd3Track +// +//========================================================================== + +int P_GetCDEnd3Track(void) +{ + return cd_NonLevelTracks[MCMD_CD_END3TRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// P_GetCDIntermissionTrack +// +//========================================================================== + +int P_GetCDIntermissionTrack(void) +{ + return cd_NonLevelTracks[MCMD_CD_INTERTRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// P_GetCDTitleTrack +// +//========================================================================== + +int P_GetCDTitleTrack(void) +{ + return cd_NonLevelTracks[MCMD_CD_TITLETRACK-MCMD_CD_STARTTRACK]; +} + +//========================================================================== +// +// QualifyMap +// +//========================================================================== + +static int QualifyMap(int map) +{ + return (map < 1 || map > MapCount) ? 0 : map; +} + +//========================================================================== +// +// P_Init +// +//========================================================================== + +void P_Init(void) +{ + InitMapInfo(); + P_InitSwitchList(); + P_InitFTAnims(); // Init flat and texture animations + P_InitTerrainTypes(); + P_InitLava(); + R_InitSprites(sprnames); +} + + +// Special early initializer needed to start sound before R_Init() +void InitMapMusicInfo(void) +{ + int i; + + for (i=0; i<99; i++) + { + strcpy(MapInfo[i].songLump, DEFAULT_SONG_LUMP); + } + MapCount = 98; +} + +/* +void My_Debug(void) +{ + int i; + + printf("My debug stuff ----------------------\n"); + printf("gamemap=%d\n",gamemap); + for (i=0; i<10; i++) + { + printf("i=%d songlump=%s\n",i,MapInfo[i].songLump); + } +} +*/ diff --git a/src/hexen/p_sight.c b/src/hexen/p_sight.c new file mode 100644 index 00000000..cfc779ea --- /dev/null +++ b/src/hexen/p_sight.c @@ -0,0 +1,395 @@ + +//************************************************************************** +//** +//** p_sight.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_sight.c,v $ +//** $Revision: 1.1 $ +//** $Date: 95/05/11 00:22:50 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" + +/* +============================================================================== + + P_CheckSight + +This uses specialized forms of the maputils routines for optimized performance + +============================================================================== +*/ + +fixed_t sightzstart; // eye z of looker +fixed_t topslope, bottomslope; // slopes to top and bottom of target + +int sightcounts[3]; + +/* +============== += += PTR_SightTraverse += +============== +*/ + +boolean PTR_SightTraverse (intercept_t *in) +{ + line_t *li; + fixed_t slope; + + li = in->d.line; + +// +// crosses a two sided line +// + P_LineOpening (li); + + if (openbottom >= opentop) // quick test for totally closed doors + return false; // stop + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - sightzstart , in->frac); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - sightzstart , in->frac); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // keep going +} + + + +/* +================== += += P_SightBlockLinesIterator += +=================== +*/ + +boolean P_SightBlockLinesIterator (int x, int y ) +{ + int offset; + short *list; + line_t *ld; + int s1, s2; + divline_t dl; + + polyblock_t *polyLink; + seg_t **segList; + int i; + extern polyblock_t **PolyBlockMap; + + offset = y*bmapwidth+x; + + polyLink = PolyBlockMap[offset]; + while(polyLink) + { + if(polyLink->polyobj) + { // only check non-empty links + if(polyLink->polyobj->validcount != validcount) + { + segList = polyLink->polyobj->segs; + for(i = 0; i < polyLink->polyobj->numsegs; i++, segList++) + { + ld = (*segList)->linedef; + if(ld->validcount == validcount) + { + continue; + } + ld->validcount = validcount; + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + if (s1 == s2) + continue; // line isn't crossed + P_MakeDivline (ld, &dl); + s1 = P_PointOnDivlineSide (trace.x, trace.y, &dl); + s2 = P_PointOnDivlineSide (trace.x+trace.dx, trace.y+trace.dy, &dl); + if (s1 == s2) + continue; // line isn't crossed + + // try to early out the check + if (!ld->backsector) + return false; // stop checking + + // store the line for later intersection testing + intercept_p->d.line = ld; + intercept_p++; + } + polyLink->polyobj->validcount = validcount; + } + } + polyLink = polyLink->next; + } + + offset = *(blockmap+offset); + + for ( list = blockmaplump+offset ; *list != -1 ; list++) + { + ld = &lines[*list]; + if (ld->validcount == validcount) + continue; // line has already been checked + ld->validcount = validcount; + + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + if (s1 == s2) + continue; // line isn't crossed + P_MakeDivline (ld, &dl); + s1 = P_PointOnDivlineSide (trace.x, trace.y, &dl); + s2 = P_PointOnDivlineSide (trace.x+trace.dx, trace.y+trace.dy, &dl); + if (s1 == s2) + continue; // line isn't crossed + + // try to early out the check + if (!ld->backsector) + return false; // stop checking + + // store the line for later intersection testing + intercept_p->d.line = ld; + intercept_p++; + + } + + return true; // everything was checked +} + +/* +==================== += += P_SightTraverseIntercepts += += Returns true if the traverser function returns true for all lines +==================== +*/ + +boolean P_SightTraverseIntercepts ( void ) +{ + int count; + fixed_t dist; + intercept_t *scan, *in; + divline_t dl; + + count = intercept_p - intercepts; +// +// calculate intercept distance +// + for (scan = intercepts ; scand.line, &dl); + scan->frac = P_InterceptVector (&trace, &dl); + } + +// +// go through in order +// + in = 0; // shut up compiler warning + + while (count--) + { + dist = MAXINT; + for (scan = intercepts ; scanfrac < dist) + { + dist = scan->frac; + in = scan; + } + + if ( !PTR_SightTraverse (in) ) + return false; // don't bother going farther + in->frac = MAXINT; + } + + return true; // everything was traversed +} + + + +/* +================== += += P_SightPathTraverse += += Traces a line from x1,y1 to x2,y2, calling the traverser function for each += Returns true if the traverser function returns true for all lines +================== +*/ + +boolean P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + fixed_t xt1,yt1,xt2,yt2; + fixed_t xstep,ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy, mapxstep, mapystep; + int count; + + validcount++; + intercept_p = intercepts; + + if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) + x1 += FRACUNIT; // don't side exactly on a line + if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) + y1 += FRACUNIT; // don't side exactly on a line + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1>>MAPBLOCKSHIFT; + yt1 = y1>>MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2>>MAPBLOCKSHIFT; + yt2 = y2>>MAPBLOCKSHIFT; + +// points should never be out of bounds, but check once instead of +// each block + if (xt1<0 || yt1<0 || xt1>=bmapwidth || yt1>=bmapheight + || xt2<0 || yt2<0 || xt2>=bmapwidth || yt2>=bmapheight) + return false; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep); + + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); + + +// +// step through map blocks +// Count is present to prevent a round off error from skipping the break + mapx = xt1; + mapy = yt1; + + + for (count = 0 ; count < 64 ; count++) + { + if (!P_SightBlockLinesIterator (mapx, mapy)) + { +sightcounts[1]++; + return false; // early out + } + + if (mapx == xt2 && mapy == yt2) + break; + + if ( (yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else if ( (xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + + } + + +// +// couldn't early out, so go through the sorted list +// +sightcounts[2]++; + + return P_SightTraverseIntercepts ( ); +} + + + +/* +===================== += += P_CheckSight += += Returns true if a straight line between t1 and t2 is unobstructed += look from eyes of t1 to any part of t2 += +===================== +*/ + +boolean P_CheckSight (mobj_t *t1, mobj_t *t2) +{ + int s1, s2; + int pnum, bytenum, bitnum; + +// +// check for trivial rejection +// + s1 = (t1->subsector->sector - sectors); + s2 = (t2->subsector->sector - sectors); + pnum = s1*numsectors + s2; + bytenum = pnum>>3; + bitnum = 1 << (pnum&7); + + if (rejectmatrix[bytenum]&bitnum) + { +sightcounts[0]++; + return false; // can't possibly be connected + } + +// +// check precisely +// + sightzstart = t1->z + t1->height - (t1->height>>2); + topslope = (t2->z+t2->height) - sightzstart; + bottomslope = (t2->z) - sightzstart; + + return P_SightPathTraverse ( t1->x, t1->y, t2->x, t2->y ); +} + + + diff --git a/src/hexen/p_spec.c b/src/hexen/p_spec.c new file mode 100644 index 00000000..b351061a --- /dev/null +++ b/src/hexen/p_spec.c @@ -0,0 +1,1152 @@ + +//************************************************************************** +//** +//** p_spec.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_spec.c,v $ +//** $Revision: 1.67 $ +//** $Date: 96/01/06 18:37:33 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_TAGGED_LINES 64 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static boolean CheckedLockedDoor(mobj_t *mo, byte lock); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int *TerrainTypes; +struct +{ + char *name; + int type; +} TerrainTypeDefs[] = +{ + { "X_005", FLOOR_WATER }, + { "X_001", FLOOR_LAVA }, + { "X_009", FLOOR_SLUDGE }, + { "F_033", FLOOR_ICE }, + { "END", -1 } +}; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static struct +{ + line_t *line; + int lineTag; +} TaggedLines[MAX_TAGGED_LINES]; +static int TaggedLineCount; + +mobj_t LavaInflictor; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_InitLava +// +//========================================================================== + +void P_InitLava(void) +{ + memset(&LavaInflictor, 0, sizeof(mobj_t)); + LavaInflictor.type = MT_CIRCLEFLAME; + LavaInflictor.flags2 = MF2_FIREDAMAGE|MF2_NODMGTHRUST; +} + +//========================================================================== +// +// P_InitTerrainTypes +// +//========================================================================== + +void P_InitTerrainTypes(void) +{ + int i; + int lump; + int size; + + size = (numflats+1)*sizeof(int); + TerrainTypes = Z_Malloc(size, PU_STATIC, 0); + memset(TerrainTypes, 0, size); + for(i = 0; TerrainTypeDefs[i].type != -1; i++) + { + lump = W_CheckNumForName(TerrainTypeDefs[i].name); + if(lump != -1) + { + TerrainTypes[lump-firstflat] = TerrainTypeDefs[i].type; + } + } +} + +//========================================================================== +// +// getSide +// +// Will return a side_t* given the number of the current sector, the +// line number, and the side (0/1) that you want. +// +//========================================================================== + +/* +side_t *getSide(int currentSector, int line, int side) +{ + return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; +} +*/ + +//========================================================================== +// +// getSector +// +// Will return a sector_t* given the number of the current sector, the +// line number, and the side (0/1) that you want. +// +//========================================================================== + +/* +sector_t *getSector(int currentSector, int line, int side) +{ + return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; +} +*/ + +//========================================================================== +// +// twoSided +// +// Given the sector number and the line number, will tell you whether +// the line is two-sided or not. +// +//========================================================================== + +/* +int twoSided(int sector, int line) +{ + return (sectors[sector].lines[line])->flags & ML_TWOSIDED; +} +*/ + +//================================================================== +// +// Return sector_t * of sector next to current. NULL if not two-sided line +// +//================================================================== +sector_t *getNextSector(line_t *line,sector_t *sec) +{ + if (!(line->flags & ML_TWOSIDED)) + return NULL; + + if (line->frontsector == sec) + return line->backsector; + + return line->frontsector; +} + +//================================================================== +// +// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +//================================================================== +fixed_t P_FindLowestFloorSurrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t floor = sec->floorheight; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + if (!other) + continue; + if (other->floorheight < floor) + floor = other->floorheight; + } + return floor; +} + +//================================================================== +// +// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS +// +//================================================================== +fixed_t P_FindHighestFloorSurrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t floor = -500*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + if (!other) + continue; + if (other->floorheight > floor) + floor = other->floorheight; + } + return floor; +} + +//================================================================== +// +// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS +// +//================================================================== +fixed_t P_FindNextHighestFloor(sector_t *sec,int currentheight) +{ + int i; + int h; + int min; + line_t *check; + sector_t *other; + fixed_t height = currentheight; + fixed_t heightlist[20]; // 20 adjoining sectors max! + + for (i =0,h = 0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + if (!other) + continue; + if (other->floorheight > height) + heightlist[h++] = other->floorheight; + } + + // + // Find lowest height in list + // + min = heightlist[0]; + for (i = 1;i < h;i++) + if (heightlist[i] < min) + min = heightlist[i]; + + return min; +} + +//================================================================== +// +// FIND LOWEST CEILING IN THE SURROUNDING SECTORS +// +//================================================================== +fixed_t P_FindLowestCeilingSurrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t height = MAXINT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + if (!other) + continue; + if (other->ceilingheight < height) + height = other->ceilingheight; + } + return height; +} + +//================================================================== +// +// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS +// +//================================================================== +fixed_t P_FindHighestCeilingSurrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t height = 0; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + if (!other) + continue; + if (other->ceilingheight > height) + height = other->ceilingheight; + } + return height; +} + +//================================================================== +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// +//================================================================== + +/* +int P_FindSectorFromLineTag(line_t *line,int start) +{ + int i; + + for (i=start+1;iarg1) + return i; + return -1; +} +*/ + +//========================================================================= +// +// P_FindSectorFromTag +// +//========================================================================= + +int P_FindSectorFromTag(int tag, int start) +{ + int i; + + for(i = start+1; i < numsectors; i++) + { + if(sectors[i].tag == tag) + { + return i; + } + } + return -1; +} + +//================================================================== +// +// Find minimum light from an adjacent sector +// +//================================================================== + +/* +int P_FindMinSurroundingLight(sector_t *sector,int max) +{ + int i; + int min; + line_t *line; + sector_t *check; + + min = max; + for (i=0 ; i < sector->linecount ; i++) + { + line = sector->lines[i]; + check = getNextSector(line,sector); + if (!check) + continue; + if (check->lightlevel < min) + min = check->lightlevel; + } + return min; +} +*/ + +//========================================================================= +// +// EV_SectorSoundChange +// +//========================================================================= + +boolean EV_SectorSoundChange(byte *args) +{ + int secNum; + boolean rtn; + + if(!args[0]) + { + return false; + } + secNum = -1; + rtn = false; + while((secNum = P_FindSectorFromTag(args[0], secNum)) >= 0) + { + sectors[secNum].seqType = args[1]; + rtn = true; + } + return rtn; +} + +//============================================================================ +// +// CheckedLockedDoor +// +//============================================================================ + +static boolean CheckedLockedDoor(mobj_t *mo, byte lock) +{ + extern char *TextKeyMessages[11]; + char LockedBuffer[80]; + + if(!mo->player) + { + return false; + } + if(!lock) + { + return true; + } + if(!(mo->player->keys&(1<<(lock-1)))) + { + sprintf(LockedBuffer, "YOU NEED THE %s\n", + TextKeyMessages[lock-1]); + P_SetMessage(mo->player, LockedBuffer, true); + S_StartSound(mo, SFX_DOOR_LOCKED); + return false; + } + return true; +} + + +//========================================================================== +// +// EV_LineSearchForPuzzleItem +// +//========================================================================== + +boolean EV_LineSearchForPuzzleItem(line_t *line, byte *args, mobj_t *mo) +{ + player_t *player; + int i; + artitype_t type,arti; + + if (!mo) return false; + player = mo->player; + if (!player) return false; + + // Search player's inventory for puzzle items + for (i=0; iartifactCount; i++) + { + arti = player->inventory[i].type; + type = arti - arti_firstpuzzitem; + if (type < 0) continue; + if (type == line->arg1) + { + // A puzzle item was found for the line + if (P_UseArtifact(player, arti)) + { + // A puzzle item was found for the line + P_PlayerRemoveArtifact(player, i); + if(player == &players[consoleplayer]) + { + if(arti < arti_firstpuzzitem) + { + S_StartSound(NULL, SFX_ARTIFACT_USE); + } + else + { + S_StartSound(NULL, SFX_PUZZLE_SUCCESS); + } + ArtifactFlash = 4; + } + return true; + } + } + } + return false; +} + + + +/* +============================================================================== + + EVENTS + +Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers + +============================================================================== +*/ +//============================================================================ +// +// P_ExecuteLineSpecial +// +//============================================================================ + +boolean P_ExecuteLineSpecial(int special, byte *args, line_t *line, int side, + mobj_t *mo) +{ + boolean buttonSuccess; + + buttonSuccess = false; + switch(special) + { + case 1: // Poly Start Line + break; + case 2: // Poly Rotate Left + buttonSuccess = EV_RotatePoly(line, args, 1, false); + break; + case 3: // Poly Rotate Right + buttonSuccess = EV_RotatePoly(line, args, -1, false); + break; + case 4: // Poly Move + buttonSuccess = EV_MovePoly(line, args, false, false); + break; + case 5: // Poly Explicit Line: Only used in initialization + break; + case 6: // Poly Move Times 8 + buttonSuccess = EV_MovePoly(line, args, true, false); + break; + case 7: // Poly Door Swing + buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SWING); + break; + case 8: // Poly Door Slide + buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SLIDE); + break; + case 10: // Door Close + buttonSuccess = EV_DoDoor(line, args, DREV_CLOSE); + break; + case 11: // Door Open + if(!args[0]) + { + buttonSuccess = EV_VerticalDoor(line, mo); + } + else + { + buttonSuccess = EV_DoDoor(line, args, DREV_OPEN); + } + break; + case 12: // Door Raise + if(!args[0]) + { + buttonSuccess = EV_VerticalDoor(line, mo); + } + else + { + buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL); + } + break; + case 13: // Door Locked_Raise + if(CheckedLockedDoor(mo, args[3])) + { + if(!args[0]) + { + buttonSuccess = EV_VerticalDoor(line, mo); + } + else + { + buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL); + } + } + break; + case 20: // Floor Lower by Value + buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE); + break; + case 21: // Floor Lower to Lowest + buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORTOLOWEST); + break; + case 22: // Floor Lower to Nearest + buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOOR); + break; + case 23: // Floor Raise by Value + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE); + break; + case 24: // Floor Raise to Highest + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOOR); + break; + case 25: // Floor Raise to Nearest + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORTONEAREST); + break; + case 26: // Stairs Build Down Normal + buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_NORMAL); + break; + case 27: // Build Stairs Up Normal + buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_NORMAL); + break; + case 28: // Floor Raise and Crush + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORCRUSH); + break; + case 29: // Build Pillar (no crushing) + buttonSuccess = EV_BuildPillar(line, args, false); + break; + case 30: // Open Pillar + buttonSuccess = EV_OpenPillar(line, args); + break; + case 31: // Stairs Build Down Sync + buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_SYNC); + break; + case 32: // Build Stairs Up Sync + buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_SYNC); + break; + case 35: // Raise Floor by Value Times 8 + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEBYVALUETIMES8); + break; + case 36: // Lower Floor by Value Times 8 + buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERBYVALUETIMES8); + break; + case 40: // Ceiling Lower by Value + buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE); + break; + case 41: // Ceiling Raise by Value + buttonSuccess = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE); + break; + case 42: // Ceiling Crush and Raise + buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHANDRAISE); + break; + case 43: // Ceiling Lower and Crush + buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERANDCRUSH); + break; + case 44: // Ceiling Crush Stop + buttonSuccess = EV_CeilingCrushStop(line, args); + break; + case 45: // Ceiling Crush Raise and Stay + buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHRAISEANDSTAY); + break; + case 46: // Floor Crush Stop + buttonSuccess = EV_FloorCrushStop(line, args); + break; + case 60: // Plat Perpetual Raise + buttonSuccess = EV_DoPlat(line, args, PLAT_PERPETUALRAISE, 0); + break; + case 61: // Plat Stop + EV_StopPlat(line, args); + break; + case 62: // Plat Down-Wait-Up-Stay + buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNWAITUPSTAY, 0); + break; + case 63: // Plat Down-by-Value*8-Wait-Up-Stay + buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNBYVALUEWAITUPSTAY, + 0); + break; + case 64: // Plat Up-Wait-Down-Stay + buttonSuccess = EV_DoPlat(line, args, PLAT_UPWAITDOWNSTAY, 0); + break; + case 65: // Plat Up-by-Value*8-Wait-Down-Stay + buttonSuccess = EV_DoPlat(line, args, PLAT_UPBYVALUEWAITDOWNSTAY, + 0); + break; + case 66: // Floor Lower Instant * 8 + buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERTIMES8INSTANT); + break; + case 67: // Floor Raise Instant * 8 + buttonSuccess = EV_DoFloor(line, args, FLEV_RAISETIMES8INSTANT); + break; + case 68: // Floor Move to Value * 8 + buttonSuccess = EV_DoFloor(line, args, FLEV_MOVETOVALUETIMES8); + break; + case 69: // Ceiling Move to Value * 8 + buttonSuccess = EV_DoCeiling(line, args, CLEV_MOVETOVALUETIMES8); + break; + case 70: // Teleport + if(side == 0) + { // Only teleport when crossing the front side of a line + buttonSuccess = EV_Teleport(args[0], mo, true); + } + break; + case 71: // Teleport, no fog + if(side == 0) + { // Only teleport when crossing the front side of a line + buttonSuccess = EV_Teleport(args[0], mo, false); + } + break; + case 72: // Thrust Mobj + if(!side) // Only thrust on side 0 + { + P_ThrustMobj(mo, args[0]*(ANGLE_90/64), args[1]<player && mo->player->playerstate + == PST_DEAD)) // Players must be alive to teleport + { + G_Completed(args[0], args[1]); + buttonSuccess = true; + } + } + break; + case 75: // Teleport_EndGame + if(side == 0) + { // Only teleport when crossing the front side of a line + if(!(mo && mo->player && mo->player->playerstate + == PST_DEAD)) // Players must be alive to teleport + { + buttonSuccess = true; + if(deathmatch) + { // Winning in deathmatch just goes back to map 1 + G_Completed(1, 0); + } + else + { // Passing -1, -1 to G_Completed() starts the Finale + G_Completed(-1, -1); + } + } + } + break; + case 80: // ACS_Execute + buttonSuccess = + P_StartACS(args[0], args[1], &args[2], mo, line, side); + break; + case 81: // ACS_Suspend + buttonSuccess = P_SuspendACS(args[0], args[1]); + break; + case 82: // ACS_Terminate + buttonSuccess = P_TerminateACS(args[0], args[1]); + break; + case 83: // ACS_LockedExecute + buttonSuccess = P_StartLockedACS(line, args, mo, side); + break; + case 90: // Poly Rotate Left Override + buttonSuccess = EV_RotatePoly(line, args, 1, true); + break; + case 91: // Poly Rotate Right Override + buttonSuccess = EV_RotatePoly(line, args, -1, true); + break; + case 92: // Poly Move Override + buttonSuccess = EV_MovePoly(line, args, false, true); + break; + case 93: // Poly Move Times 8 Override + buttonSuccess = EV_MovePoly(line, args, true, true); + break; + case 94: // Build Pillar Crush + buttonSuccess = EV_BuildPillar(line, args, true); + break; + case 95: // Lower Floor and Ceiling + buttonSuccess = EV_DoFloorAndCeiling(line, args, false); + break; + case 96: // Raise Floor and Ceiling + buttonSuccess = EV_DoFloorAndCeiling(line, args, true); + break; + case 109: // Force Lightning + buttonSuccess = true; + P_ForceLightning(); + break; + case 110: // Light Raise by Value + buttonSuccess = EV_SpawnLight(line, args, LITE_RAISEBYVALUE); + break; + case 111: // Light Lower by Value + buttonSuccess = EV_SpawnLight(line, args, LITE_LOWERBYVALUE); + break; + case 112: // Light Change to Value + buttonSuccess = EV_SpawnLight(line, args, LITE_CHANGETOVALUE); + break; + case 113: // Light Fade + buttonSuccess = EV_SpawnLight(line, args, LITE_FADE); + break; + case 114: // Light Glow + buttonSuccess = EV_SpawnLight(line, args, LITE_GLOW); + break; + case 115: // Light Flicker + buttonSuccess = EV_SpawnLight(line, args, LITE_FLICKER); + break; + case 116: // Light Strobe + buttonSuccess = EV_SpawnLight(line, args, LITE_STROBE); + break; + case 120: // Quake Tremor + buttonSuccess = A_LocalQuake(args, mo); + break; + case 129: // UsePuzzleItem + buttonSuccess = EV_LineSearchForPuzzleItem(line, args, mo); + break; + case 130: // Thing_Activate + buttonSuccess = EV_ThingActivate(args[0]); + break; + case 131: // Thing_Deactivate + buttonSuccess = EV_ThingDeactivate(args[0]); + break; + case 132: // Thing_Remove + buttonSuccess = EV_ThingRemove(args[0]); + break; + case 133: // Thing_Destroy + buttonSuccess = EV_ThingDestroy(args[0]); + break; + case 134: // Thing_Projectile + buttonSuccess = EV_ThingProjectile(args, 0); + break; + case 135: // Thing_Spawn + buttonSuccess = EV_ThingSpawn(args, 1); + break; + case 136: // Thing_ProjectileGravity + buttonSuccess = EV_ThingProjectile(args, 1); + break; + case 137: // Thing_SpawnNoFog + buttonSuccess = EV_ThingSpawn(args, 0); + break; + case 138: // Floor_Waggle + buttonSuccess = EV_StartFloorWaggle(args[0], args[1], + args[2], args[3], args[4]); + break; + case 140: // Sector_SoundChange + buttonSuccess = EV_SectorSoundChange(args); + break; + + // Line specials only processed during level initialization + // 100: Scroll_Texture_Left + // 101: Scroll_Texture_Right + // 102: Scroll_Texture_Up + // 103: Scroll_Texture_Down + // 121: Line_SetIdentification + + // Inert Line specials + default: + break; + } + return buttonSuccess; +} + +//============================================================================ +// +// P_ActivateLine +// +//============================================================================ + +boolean P_ActivateLine(line_t *line, mobj_t *mo, int side, int activationType) +{ + int lineActivation; + boolean repeat; + boolean buttonSuccess; + + lineActivation = GET_SPAC(line->flags); + if(lineActivation != activationType) + { + return false; + } + if(!mo->player && !(mo->flags&MF_MISSILE)) + { + if(lineActivation != SPAC_MCROSS) + { // currently, monsters can only activate the MCROSS activation type + return false; + } + if(line->flags & ML_SECRET) + return false; // never open secret doors + } + repeat = line->flags&ML_REPEAT_SPECIAL; + buttonSuccess = false; + + buttonSuccess = P_ExecuteLineSpecial(line->special, &line->arg1, line, + side, mo); + if(!repeat && buttonSuccess) + { // clear the special on non-retriggerable lines + line->special = 0; + } + if((lineActivation == SPAC_USE || lineActivation == SPAC_IMPACT) + && buttonSuccess) + { + P_ChangeSwitchTexture(line, repeat); + } + return true; +} + +//---------------------------------------------------------------------------- +// +// PROC P_PlayerInSpecialSector +// +// Called every tic frame that the player origin is in a special sector. +// +//---------------------------------------------------------------------------- + +void P_PlayerInSpecialSector(player_t *player) +{ + sector_t *sector; + static int pushTab[3] = + { + 2048*5, + 2048*10, + 2048*25 + }; + + sector = player->mo->subsector->sector; + if(player->mo->z != sector->floorheight) + { // Player is not touching the floor + return; + } + switch(sector->special) + { + case 9: // SecretArea + player->secretcount++; + sector->special = 0; + break; + + case 201: case 202: case 203: // Scroll_North_xxx + P_Thrust(player, ANG90, pushTab[sector->special-201]); + break; + case 204: case 205: case 206: // Scroll_East_xxx + P_Thrust(player, 0, pushTab[sector->special-204]); + break; + case 207: case 208: case 209: // Scroll_South_xxx + P_Thrust(player, ANG270, pushTab[sector->special-207]); + break; + case 210: case 211: case 212: // Scroll_West_xxx + P_Thrust(player, ANG180, pushTab[sector->special-210]); + break; + case 213: case 214: case 215: // Scroll_NorthWest_xxx + P_Thrust(player, ANG90+ANG45, pushTab[sector->special-213]); + break; + case 216: case 217: case 218: // Scroll_NorthEast_xxx + P_Thrust(player, ANG45, pushTab[sector->special-216]); + break; + case 219: case 220: case 221: // Scroll_SouthEast_xxx + P_Thrust(player, ANG270+ANG45, pushTab[sector->special-219]); + break; + case 222: case 223: case 224: // Scroll_SouthWest_xxx + P_Thrust(player, ANG180+ANG45, pushTab[sector->special-222]); + break; + + case 40: case 41: case 42: case 43: case 44: case 45: + case 46: case 47: case 48: case 49: case 50: case 51: + // Wind specials are handled in (P_mobj):P_XYMovement + break; + + case 26: // Stairs_Special1 + case 27: // Stairs_Special2 + // Used in (P_floor):ProcessStairSector + break; + + case 198: // Lightning Special + case 199: // Lightning Flash special + case 200: // Sky2 + // Used in (R_plane):R_Drawplanes + break; + default: + I_Error("P_PlayerInSpecialSector: " + "unknown special %i", sector->special); + } +} + +//============================================================================ +// +// P_PlayerOnSpecialFlat +// +//============================================================================ + +void P_PlayerOnSpecialFlat(player_t *player, int floorType) +{ + if(player->mo->z != player->mo->floorz) + { // Player is not touching the floor + return; + } + switch(floorType) + { + case FLOOR_LAVA: + if(!(leveltime&31)) + { + P_DamageMobj(player->mo, &LavaInflictor, NULL, 10); + S_StartSound(player->mo, SFX_LAVA_SIZZLE); + } + break; + default: + break; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_UpdateSpecials +// +//---------------------------------------------------------------------------- + +void P_UpdateSpecials(void) +{ + int i; + + // Handle buttons + for(i = 0; i < MAXBUTTONS; i++) + { + if(buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if(!buttonlist[i].btimer) + { + switch(buttonlist[i].where) + { + case SWTCH_TOP: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + case SWTCH_MIDDLE: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + case SWTCH_BOTTOM: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + //S_StartSound((mobj_t *)&buttonlist[i].soundorg, sfx_switch); + memset(&buttonlist[i], 0, sizeof(button_t)); + } + } + } +} + +/* +============================================================================== + + SPECIAL SPAWNING + +============================================================================== +*/ +/* +================================================================================ += P_SpawnSpecials += += After the map has been loaded, scan for specials that += spawn thinkers += +=============================================================================== +*/ + +short numlinespecials; +line_t *linespeciallist[MAXLINEANIMS]; + +void P_SpawnSpecials (void) +{ + sector_t *sector; + int i; + + // + // Init special SECTORs + // + sector = sectors; + for (i=0 ; ispecial) + continue; + switch (sector->special) + { + case 1: // Phased light + // Hardcoded base, use sector->lightlevel as the index + P_SpawnPhasedLight(sector, 80, -1); + break; + case 2: // Phased light sequence start + P_SpawnLightSequence(sector, 1); + break; + // Specials 3 & 4 are used by the phased light sequences + + /* + case 1: // FLICKERING LIGHTS + P_SpawnLightFlash (sector); + break; + case 2: // STROBE FAST + P_SpawnStrobeFlash(sector,FASTDARK,0); + break; + case 3: // STROBE SLOW + P_SpawnStrobeFlash(sector,SLOWDARK,0); + break; + case 4: // STROBE FAST/DEATH SLIME + P_SpawnStrobeFlash(sector,FASTDARK,0); + sector->special = 4; + break; + case 8: // GLOWING LIGHT + P_SpawnGlowingLight(sector); + break; + case 9: // SECRET SECTOR + totalsecret++; + break; + case 10: // DOOR CLOSE IN 30 SECONDS + P_SpawnDoorCloseIn30 (sector); + break; + case 12: // SYNC STROBE SLOW + P_SpawnStrobeFlash (sector, SLOWDARK, 1); + break; + case 13: // SYNC STROBE FAST + P_SpawnStrobeFlash (sector, FASTDARK, 1); + break; + case 14: // DOOR RAISE IN 5 MINUTES + P_SpawnDoorRaiseIn5Mins (sector, i); + break; + */ + } + } + + + // + // Init line EFFECTs + // + numlinespecials = 0; + TaggedLineCount = 0; + for(i = 0; i < numlines; i++) + { + switch(lines[i].special) + { + case 100: // Scroll_Texture_Left + case 101: // Scroll_Texture_Right + case 102: // Scroll_Texture_Up + case 103: // Scroll_Texture_Down + linespeciallist[numlinespecials] = &lines[i]; + numlinespecials++; + break; + case 121: // Line_SetIdentification + if(lines[i].arg1) + { + if(TaggedLineCount == MAX_TAGGED_LINES) + { + I_Error("P_SpawnSpecials: MAX_TAGGED_LINES " + "(%d) exceeded.", MAX_TAGGED_LINES); + } + TaggedLines[TaggedLineCount].line = &lines[i]; + TaggedLines[TaggedLineCount++].lineTag + = lines[i].arg1; + } + lines[i].special = 0; + break; + } + } + + // + // Init other misc stuff + // + for (i = 0;i < MAXCEILINGS;i++) + activeceilings[i] = NULL; + for (i = 0;i < MAXPLATS;i++) + activeplats[i] = NULL; + for (i = 0;i < MAXBUTTONS;i++) + memset(&buttonlist[i],0,sizeof(button_t)); + + // Initialize flat and texture animations + P_InitFTAnims(); +} + +//========================================================================== +// +// P_FindLine +// +//========================================================================== + +line_t *P_FindLine(int lineTag, int *searchPosition) +{ + int i; + + for(i = *searchPosition+1; i < TaggedLineCount; i++) + { + if(TaggedLines[i].lineTag == lineTag) + { + *searchPosition = i; + return TaggedLines[i].line; + } + } + *searchPosition = -1; + return NULL; +} diff --git a/src/hexen/p_spec.h b/src/hexen/p_spec.h new file mode 100644 index 00000000..094f4065 --- /dev/null +++ b/src/hexen/p_spec.h @@ -0,0 +1,563 @@ + +//************************************************************************** +//** +//** p_spec.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_spec.h,v $ +//** $Revision: 1.38 $ +//** $Date: 96/01/06 18:37:35 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +extern int *TerrainTypes; + +// +// scrolling line specials +// + +#define MAXLINEANIMS 64 +extern short numlinespecials; +extern line_t *linespeciallist[MAXLINEANIMS]; + +// Define values for map objects +#define MO_TELEPORTMAN 14 + +// at game start +void P_InitTerrainTypes(void); +void P_InitLava(void); + +// at map load +void P_SpawnSpecials(void); + +// every tic +void P_UpdateSpecials(void); + +// when needed +boolean P_ExecuteLineSpecial(int special, byte *args, line_t *line, int side, + mobj_t *mo); +boolean P_ActivateLine(line_t *ld, mobj_t *mo, int side, int activationType); +//boolean P_UseSpecialLine ( mobj_t *thing, line_t *line); +//void P_ShootSpecialLine ( mobj_t *thing, line_t *line); +//void P_CrossSpecialLine (int linenum, int side, mobj_t *thing); + +void P_PlayerInSpecialSector(player_t *player); +void P_PlayerOnSpecialFlat(player_t *player, int floorType); + +//int twoSided(int sector,int line); +//sector_t *getSector(int currentSector,int line,int side); +//side_t *getSide(int currentSector,int line, int side); +fixed_t P_FindLowestFloorSurrounding(sector_t *sec); +fixed_t P_FindHighestFloorSurrounding(sector_t *sec); +fixed_t P_FindNextHighestFloor(sector_t *sec,int currentheight); +fixed_t P_FindLowestCeilingSurrounding(sector_t *sec); +fixed_t P_FindHighestCeilingSurrounding(sector_t *sec); +//int P_FindSectorFromLineTag(line_t *line,int start); +int P_FindSectorFromTag(int tag, int start); +//int P_FindMinSurroundingLight(sector_t *sector,int max); +sector_t *getNextSector(line_t *line,sector_t *sec); +line_t *P_FindLine(int lineTag, int *searchPosition); + +// +// SPECIAL +// +//int EV_DoDonut(line_t *line); + +//------------------------------- +// P_anim.c +//------------------------------- + +void P_AnimateSurfaces(void); +void P_InitFTAnims(void); +void P_InitLightning(void); +void P_ForceLightning(void); + +/* +=============================================================================== + + P_LIGHTS + +=============================================================================== +*/ + +typedef enum +{ + LITE_RAISEBYVALUE, + LITE_LOWERBYVALUE, + LITE_CHANGETOVALUE, + LITE_FADE, + LITE_GLOW, + LITE_FLICKER, + LITE_STROBE +} lighttype_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + lighttype_t type; + int value1; + int value2; + int tics1; + int tics2; + int count; +} light_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int index; + int base; +} phase_t; + +#define LIGHT_SEQUENCE_START 2 +#define LIGHT_SEQUENCE 3 +#define LIGHT_SEQUENCE_ALT 4 + +void T_Phase(phase_t *phase); +void T_Light(light_t *light); +void P_SpawnPhasedLight(sector_t *sector, int base, int index); +void P_SpawnLightSequence(sector_t *sector, int indexStep); +boolean EV_SpawnLight(line_t *line, byte *arg, lighttype_t type); + +#if 0 +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; +} lightflash_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; +} strobe_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int minlight; + int maxlight; + int direction; +} glow_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int index; + int base; +} phase_t; + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +#define LIGHT_SEQUENCE_START 2 +#define LIGHT_SEQUENCE 3 +#define LIGHT_SEQUENCE_ALT 4 + +void T_LightFlash (lightflash_t *flash); +void P_SpawnLightFlash (sector_t *sector); +void T_StrobeFlash (strobe_t *flash); +void P_SpawnStrobeFlash (sector_t *sector, int fastOrSlow, int inSync); +void EV_StartLightStrobing(line_t *line); +void EV_TurnTagLightsOff(line_t *line); +void EV_LightTurnOn(line_t *line, int bright); +void T_Glow(glow_t *g); +void P_SpawnGlowingLight(sector_t *sector); +void T_Phase(phase_t *phase); +void P_SpawnPhasedLight(sector_t *sector, int base, int index); +void P_SpawnLightSequence(sector_t *sector, int indexStep); +#endif + +/* +=============================================================================== + + P_SWITCH + +=============================================================================== +*/ +typedef struct +{ + char name1[9]; + char name2[9]; + int soundID; +} switchlist_t; + +typedef enum +{ + SWTCH_TOP, + SWTCH_MIDDLE, + SWTCH_BOTTOM +} bwhere_e; + +typedef struct +{ + line_t *line; + bwhere_e where; + int btexture; + int btimer; + mobj_t *soundorg; +} button_t; + +#define MAXSWITCHES 50 // max # of wall switches in a level +#define MAXBUTTONS 16 // 4 players, 4 buttons each at once, max. +#define BUTTONTIME 35 // 1 second + +extern button_t buttonlist[MAXBUTTONS]; + +void P_ChangeSwitchTexture(line_t *line, int useAgain); +void P_InitSwitchList(void); + +/* +=============================================================================== + + P_PLATS + +=============================================================================== +*/ + +typedef enum +{ + PLAT_UP, + PLAT_DOWN, + PLAT_WAITING, +// PLAT_IN_STASIS +} plat_e; + +typedef enum +{ + PLAT_PERPETUALRAISE, + PLAT_DOWNWAITUPSTAY, + PLAT_DOWNBYVALUEWAITUPSTAY, + PLAT_UPWAITDOWNSTAY, + PLAT_UPBYVALUEWAITDOWNSTAY, + //PLAT_RAISEANDCHANGE, + //PLAT_RAISETONEARESTANDCHANGE +} plattype_e; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + int crush; + int tag; + plattype_e type; +} plat_t; + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT +#define MAXPLATS 30 + +extern plat_t *activeplats[MAXPLATS]; + +void T_PlatRaise(plat_t *plat); +int EV_DoPlat(line_t *line, byte *args, plattype_e type, int amount); +void P_AddActivePlat(plat_t *plat); +void P_RemoveActivePlat(plat_t *plat); +void EV_StopPlat(line_t *line, byte *args); + +/* +=============================================================================== + + P_DOORS + +=============================================================================== +*/ +typedef enum +{ + DREV_NORMAL, + DREV_CLOSE30THENOPEN, + DREV_CLOSE, + DREV_OPEN, + DREV_RAISEIN5MINS, +} vldoor_e; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + vldoor_e type; + fixed_t topheight; + fixed_t speed; + int direction; // 1 = up, 0 = waiting at top, -1 = down + int topwait; // tics to wait at the top (keep in case a door going down is reset) + int topcountdown; // when it reaches 0, start going down +} vldoor_t; + +#define VDOORSPEED FRACUNIT*2 +#define VDOORWAIT 150 + +boolean EV_VerticalDoor(line_t *line, mobj_t *thing); +int EV_DoDoor(line_t *line, byte *args, vldoor_e type); +void T_VerticalDoor(vldoor_t *door); +//void P_SpawnDoorCloseIn30(sector_t *sec); +//void P_SpawnDoorRaiseIn5Mins(sector_t *sec, int secnum); + +/* +=============================================================================== + + P_CEILNG + +=============================================================================== +*/ +typedef enum +{ + CLEV_LOWERTOFLOOR, + CLEV_RAISETOHIGHEST, + CLEV_LOWERANDCRUSH, + CLEV_CRUSHANDRAISE, + CLEV_LOWERBYVALUE, + CLEV_RAISEBYVALUE, + CLEV_CRUSHRAISEANDSTAY, + CLEV_MOVETOVALUETIMES8 +} ceiling_e; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + ceiling_e type; + fixed_t bottomheight, topheight; + fixed_t speed; + int crush; + int direction; // 1 = up, 0 = waiting, -1 = down + int tag; // ID + int olddirection; +} ceiling_t; + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 +#define MAXCEILINGS 30 + +extern ceiling_t *activeceilings[MAXCEILINGS]; + +int EV_DoCeiling(line_t *line, byte *args, ceiling_e type); +void T_MoveCeiling(ceiling_t *ceiling); +void P_AddActiveCeiling(ceiling_t *c); +void P_RemoveActiveCeiling(ceiling_t *c); +int EV_CeilingCrushStop(line_t *line, byte *args); + +/* +=============================================================================== + + P_FLOOR + +=============================================================================== +*/ +typedef enum +{ + FLEV_LOWERFLOOR, // lower floor to highest surrounding floor + FLEV_LOWERFLOORTOLOWEST, // lower floor to lowest surrounding floor + FLEV_LOWERFLOORBYVALUE, + FLEV_RAISEFLOOR, // raise floor to lowest surrounding CEILING + FLEV_RAISEFLOORTONEAREST, // raise floor to next highest surrounding floor + FLEV_RAISEFLOORBYVALUE, + FLEV_RAISEFLOORCRUSH, + FLEV_RAISEBUILDSTEP, // One step of a staircase + FLEV_RAISEBYVALUETIMES8, + FLEV_LOWERBYVALUETIMES8, + FLEV_LOWERTIMES8INSTANT, + FLEV_RAISETIMES8INSTANT, + FLEV_MOVETOVALUETIMES8 +} floor_e; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + floor_e type; + int crush; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; + int delayCount; + int delayTotal; + fixed_t stairsDelayHeight; + fixed_t stairsDelayHeightDelta; + fixed_t resetHeight; + short resetDelay; + short resetDelayCount; + byte textureChange; +} floormove_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int ceilingSpeed; + int floorSpeed; + int floordest; + int ceilingdest; + int direction; + int crush; +} pillar_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + fixed_t originalHeight; + fixed_t accumulator; + fixed_t accDelta; + fixed_t targetScale; + fixed_t scale; + fixed_t scaleDelta; + int ticker; + int state; +} floorWaggle_t; + +#define FLOORSPEED FRACUNIT + +typedef enum +{ + RES_OK, + RES_CRUSHED, + RES_PASTDEST +} result_e; + +typedef enum +{ + STAIRS_NORMAL, + STAIRS_SYNC, + STAIRS_PHASED +} stairs_e; + +result_e T_MovePlane(sector_t *sector, fixed_t speed, + fixed_t dest, int crush, int floorOrCeiling, int direction); + +int EV_BuildStairs(line_t *line, byte *args, int direction, stairs_e type); +int EV_DoFloor(line_t *line, byte *args, floor_e floortype); +void T_MoveFloor(floormove_t *floor); +void T_BuildPillar(pillar_t *pillar); +void T_FloorWaggle(floorWaggle_t *waggle); +int EV_BuildPillar(line_t *line, byte *args, boolean crush); +int EV_OpenPillar(line_t *line, byte *args); +int EV_DoFloorAndCeiling(line_t *line, byte *args, boolean raise); +int EV_FloorCrushStop(line_t *line, byte *args); +boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset, + int timer); + +//-------------------------------------------------------------------------- +// +// p_telept +// +//-------------------------------------------------------------------------- + +boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, angle_t angle, + boolean useFog); +boolean EV_Teleport(int tid, mobj_t *thing, boolean fog); + +//-------------------------------------------------------------------------- +// +// p_acs +// +//-------------------------------------------------------------------------- + +#define MAX_ACS_SCRIPT_VARS 10 +#define MAX_ACS_MAP_VARS 32 +#define MAX_ACS_WORLD_VARS 64 +#define ACS_STACK_DEPTH 32 +#define MAX_ACS_STORE 20 + +typedef enum +{ + ASTE_INACTIVE, + ASTE_RUNNING, + ASTE_SUSPENDED, + ASTE_WAITINGFORTAG, + ASTE_WAITINGFORPOLY, + ASTE_WAITINGFORSCRIPT, + ASTE_TERMINATING +} aste_t; + +typedef struct acs_s acs_t; +typedef struct acsInfo_s acsInfo_t; + +struct acsInfo_s +{ + int number; + int *address; + int argCount; + aste_t state; + int waitValue; +}; + +struct acs_s +{ + thinker_t thinker; + mobj_t *activator; + line_t *line; + int side; + int number; + int infoIndex; + int delayCount; + int stack[ACS_STACK_DEPTH]; + int stackPtr; + int vars[MAX_ACS_SCRIPT_VARS]; + int *ip; +}; + +typedef struct +{ + int map; // Target map + int script; // Script number on target map + byte args[4]; // Padded to 4 for alignment +} acsstore_t; + +void P_LoadACScripts(int lump); +boolean P_StartACS(int number, int map, byte *args, mobj_t *activator, + line_t *line, int side); +boolean P_StartLockedACS(line_t *line, byte *args, mobj_t *mo, int side); +boolean P_TerminateACS(int number, int map); +boolean P_SuspendACS(int number, int map); +void T_InterpretACS(acs_t *script); +void P_TagFinished(int tag); +void P_PolyobjFinished(int po); +void P_ACSInitNewGame(void); +void P_CheckACSStore(void); + +extern int ACScriptCount; +extern byte *ActionCodeBase; +extern acsInfo_t *ACSInfo; +extern int MapVars[MAX_ACS_MAP_VARS]; +extern int WorldVars[MAX_ACS_WORLD_VARS]; +extern acsstore_t ACSStore[MAX_ACS_STORE+1]; // +1 for termination marker + +//-------------------------------------------------------------------------- +// +// p_things +// +//-------------------------------------------------------------------------- + +extern mobjtype_t TranslateThingType[]; + +boolean EV_ThingProjectile(byte *args, boolean gravity); +boolean EV_ThingSpawn(byte *args, boolean fog); +boolean EV_ThingActivate(int tid); +boolean EV_ThingDeactivate(int tid); +boolean EV_ThingRemove(int tid); +boolean EV_ThingDestroy(int tid); diff --git a/src/hexen/p_switch.c b/src/hexen/p_switch.c new file mode 100644 index 00000000..fe5aecd0 --- /dev/null +++ b/src/hexen/p_switch.c @@ -0,0 +1,146 @@ + +//************************************************************************** +//** +//** p_switch.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_switch.c,v $ +//** $Revision: 1.8 $ +//** $Date: 95/09/05 13:58:59 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +//================================================================== +// +// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE +// +//================================================================== +switchlist_t alphSwitchList[] = +{ + { "SW_1_UP", "SW_1_DN", SFX_SWITCH1 }, + { "SW_2_UP", "SW_2_DN", SFX_SWITCH1 }, + { "VALVE1", "VALVE2", SFX_VALVE_TURN }, + { "SW51_OFF", "SW51_ON", SFX_SWITCH2 }, + { "SW52_OFF", "SW52_ON", SFX_SWITCH2 }, + { "SW53_UP", "SW53_DN", SFX_ROPE_PULL }, + { "PUZZLE5", "PUZZLE9", SFX_SWITCH1 }, + { "PUZZLE6", "PUZZLE10", SFX_SWITCH1 }, + { "PUZZLE7", "PUZZLE11", SFX_SWITCH1 }, + { "PUZZLE8", "PUZZLE12", SFX_SWITCH1 }, + {"\0", "\0", 0} +}; + +int switchlist[MAXSWITCHES * 2]; +int numswitches; +button_t buttonlist[MAXBUTTONS]; + +/* +=============== += += P_InitSwitchList += += Only called at game initialization += +=============== +*/ + +void P_InitSwitchList(void) +{ + int i; + int index; + + for (index = 0, i = 0; i < MAXSWITCHES; i++) + { + if(!alphSwitchList[i].soundID) + { + numswitches = index/2; + switchlist[index] = -1; + break; + } + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name1); + switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name2); + } +} + +//================================================================== +// +// Start a button counting down till it turns off. +// +//================================================================== +void P_StartButton(line_t *line,bwhere_e w,int texture,int time) +{ + int i; + + for (i = 0;i < MAXBUTTONS;i++) + { + if (!buttonlist[i].btimer) + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + buttonlist[i].soundorg = (mobj_t *)&line->frontsector->soundorg; + return; + } + } + I_Error("P_StartButton: no button slots left!"); +} + +//================================================================== +// +// Function that changes wall texture. +// Tell it if switch is ok to use again (1=yes, it's a button). +// +//================================================================== +void P_ChangeSwitchTexture(line_t *line, int useAgain) +{ + int texTop; + int texMid; + int texBot; + int i; + + texTop = sides[line->sidenum[0]].toptexture; + texMid = sides[line->sidenum[0]].midtexture; + texBot = sides[line->sidenum[0]].bottomtexture; + + for (i = 0; i < numswitches*2; i++) + { + if (switchlist[i] == texTop) + { + S_StartSound((mobj_t *)&line->frontsector->soundorg, + alphSwitchList[i/2].soundID); + sides[line->sidenum[0]].toptexture = switchlist[i^1]; + if(useAgain) + { + P_StartButton(line, SWTCH_TOP, switchlist[i], BUTTONTIME); + } + return; + } + else if (switchlist[i] == texMid) + { + S_StartSound((mobj_t *)&line->frontsector->soundorg, + alphSwitchList[i/2].soundID); + sides[line->sidenum[0]].midtexture = switchlist[i^1]; + if(useAgain) + { + P_StartButton(line, SWTCH_MIDDLE, switchlist[i], BUTTONTIME); + } + return; + } + else if (switchlist[i] == texBot) + { + S_StartSound((mobj_t *)&line->frontsector->soundorg, + alphSwitchList[i/2].soundID); + sides[line->sidenum[0]].bottomtexture = switchlist[i^1]; + if(useAgain) + { + P_StartButton(line, SWTCH_BOTTOM, switchlist[i], BUTTONTIME); + } + return; + } + } +} diff --git a/src/hexen/p_telept.c b/src/hexen/p_telept.c new file mode 100644 index 00000000..0e0e200f --- /dev/null +++ b/src/hexen/p_telept.c @@ -0,0 +1,177 @@ + +//************************************************************************** +//** +//** p_telept.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_telept.c,v $ +//** $Revision: 1.13 $ +//** $Date: 95/10/08 04:23:24 $ +//** $Author: paul $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_Teleport +// +//========================================================================== + +boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, angle_t angle, + boolean useFog) +{ + fixed_t oldx; + fixed_t oldy; + fixed_t oldz; + fixed_t aboveFloor; + fixed_t fogDelta; + player_t *player; + unsigned an; + mobj_t *fog; + + oldx = thing->x; + oldy = thing->y; + oldz = thing->z; + aboveFloor = thing->z-thing->floorz; + if(!P_TeleportMove(thing, x, y)) + { + return false; + } + if(thing->player) + { + player = thing->player; + if(player->powers[pw_flight] && aboveFloor) + { + thing->z = thing->floorz+aboveFloor; + if(thing->z+thing->height > thing->ceilingz) + { + thing->z = thing->ceilingz-thing->height; + } + player->viewz = thing->z+player->viewheight; + } + else + { + thing->z = thing->floorz; + player->viewz = thing->z+player->viewheight; + if(useFog) + { + player->lookdir = 0; + } + } + } + else if(thing->flags&MF_MISSILE) + { + thing->z = thing->floorz+aboveFloor; + if(thing->z+thing->height > thing->ceilingz) + { + thing->z = thing->ceilingz-thing->height; + } + } + else + { + thing->z = thing->floorz; + } + // Spawn teleport fog at source and destination + if(useFog) + { + fogDelta = thing->flags&MF_MISSILE ? 0 : TELEFOGHEIGHT; + fog = P_SpawnMobj(oldx, oldy, oldz+fogDelta, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + an = angle>>ANGLETOFINESHIFT; + fog = P_SpawnMobj(x+20*finecosine[an], + y+20*finesine[an], thing->z+fogDelta, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + if(thing->player && !thing->player->powers[pw_speed]) + { // Freeze player for about .5 sec + thing->reactiontime = 18; + } + thing->angle = angle; + } + if(thing->flags2&MF2_FLOORCLIP) + { + if(thing->z == thing->subsector->sector->floorheight + && P_GetThingFloorType(thing) > FLOOR_SOLID) + { + thing->floorclip = 10*FRACUNIT; + } + else + { + thing->floorclip = 0; + } + } + if(thing->flags&MF_MISSILE) + { + angle >>= ANGLETOFINESHIFT; + thing->momx = FixedMul(thing->info->speed, finecosine[angle]); + thing->momy = FixedMul(thing->info->speed, finesine[angle]); + } + else if(useFog) // no fog doesn't alter the player's momentums + { + thing->momx = thing->momy = thing->momz = 0; + } + return true; +} + +//========================================================================== +// +// EV_Teleport +// +//========================================================================== + +boolean EV_Teleport(int tid, mobj_t *thing, boolean fog) +{ + int i; + int count; + mobj_t *mo; + int searcher; + + if(!thing) + { // Teleport function called with an invalid mobj + return false; + } + if(thing->flags2&MF2_NOTELEPORT) + { + return false; + } + count = 0; + searcher = -1; + while(P_FindMobjFromTID(tid, &searcher) != NULL) + { + count++; + } + if(count == 0) + { + return false; + } + count = 1+(P_Random()%count); + searcher = -1; + for(i = 0; i < count; i++) + { + mo = P_FindMobjFromTID(tid, &searcher); + } + if (!mo) I_Error("Can't find teleport mapspot\n"); + return P_Teleport(thing, mo->x, mo->y, mo->angle, fog); +} diff --git a/src/hexen/p_things.c b/src/hexen/p_things.c new file mode 100644 index 00000000..55f37418 --- /dev/null +++ b/src/hexen/p_things.c @@ -0,0 +1,533 @@ + +//************************************************************************** +//** +//** p_things.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_things.c,v $ +//** $Revision: 1.36 $ +//** $Date: 96/02/08 15:16:13 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static boolean ActivateThing(mobj_t *mobj); +static boolean DeactivateThing(mobj_t *mobj); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +mobjtype_t TranslateThingType[] = +{ + MT_MAPSPOT, // T_NONE + MT_CENTAUR, // T_CENTAUR + MT_CENTAURLEADER, // T_CENTAURLEADER + MT_DEMON, // T_DEMON + MT_ETTIN, // T_ETTIN + MT_FIREDEMON, // T_FIREGARGOYLE + MT_SERPENT, // T_WATERLURKER + MT_SERPENTLEADER, // T_WATERLURKERLEADER + MT_WRAITH, // T_WRAITH + MT_WRAITHB, // T_WRAITHBURIED + MT_FIREBALL1, // T_FIREBALL1 + MT_MANA1, // T_MANA1 + MT_MANA2, // T_MANA2 + MT_SPEEDBOOTS, // T_ITEMBOOTS + MT_ARTIEGG, // T_ITEMEGG + MT_ARTIFLY, // T_ITEMFLIGHT + MT_SUMMONMAULATOR, // T_ITEMSUMMON + MT_TELEPORTOTHER, // T_ITEMTPORTOTHER + MT_ARTITELEPORT, // T_ITEMTELEPORT + MT_BISHOP, // T_BISHOP + MT_ICEGUY, // T_ICEGOLEM + MT_BRIDGE, // T_BRIDGE + MT_BOOSTARMOR, // T_DRAGONSKINBRACERS + MT_HEALINGBOTTLE, // T_ITEMHEALTHPOTION + MT_HEALTHFLASK, // T_ITEMHEALTHFLASK + MT_ARTISUPERHEAL, // T_ITEMHEALTHFULL + MT_BOOSTMANA, // T_ITEMBOOSTMANA + MT_FW_AXE, // T_FIGHTERAXE + MT_FW_HAMMER, // T_FIGHTERHAMMER + MT_FW_SWORD1, // T_FIGHTERSWORD1 + MT_FW_SWORD2, // T_FIGHTERSWORD2 + MT_FW_SWORD3, // T_FIGHTERSWORD3 + MT_CW_SERPSTAFF, // T_CLERICSTAFF + MT_CW_HOLY1, // T_CLERICHOLY1 + MT_CW_HOLY2, // T_CLERICHOLY2 + MT_CW_HOLY3, // T_CLERICHOLY3 + MT_MW_CONE, // T_MAGESHARDS + MT_MW_STAFF1, // T_MAGESTAFF1 + MT_MW_STAFF2, // T_MAGESTAFF2 + MT_MW_STAFF3, // T_MAGESTAFF3 + MT_EGGFX, // T_MORPHBLAST + MT_ROCK1, // T_ROCK1 + MT_ROCK2, // T_ROCK2 + MT_ROCK3, // T_ROCK3 + MT_DIRT1, // T_DIRT1 + MT_DIRT2, // T_DIRT2 + MT_DIRT3, // T_DIRT3 + MT_DIRT4, // T_DIRT4 + MT_DIRT5, // T_DIRT5 + MT_DIRT6, // T_DIRT6 + MT_ARROW, // T_ARROW + MT_DART, // T_DART + MT_POISONDART, // T_POISONDART + MT_RIPPERBALL, // T_RIPPERBALL + MT_SGSHARD1, // T_STAINEDGLASS1 + MT_SGSHARD2, // T_STAINEDGLASS2 + MT_SGSHARD3, // T_STAINEDGLASS3 + MT_SGSHARD4, // T_STAINEDGLASS4 + MT_SGSHARD5, // T_STAINEDGLASS5 + MT_SGSHARD6, // T_STAINEDGLASS6 + MT_SGSHARD7, // T_STAINEDGLASS7 + MT_SGSHARD8, // T_STAINEDGLASS8 + MT_SGSHARD9, // T_STAINEDGLASS9 + MT_SGSHARD0, // T_STAINEDGLASS0 + MT_PROJECTILE_BLADE, // T_BLADE + MT_ICESHARD, // T_ICESHARD + MT_FLAME_SMALL, // T_FLAME_SMALL + MT_FLAME_LARGE, // T_FLAME_LARGE + MT_ARMOR_1, // T_MESHARMOR + MT_ARMOR_2, // T_FALCONSHIELD + MT_ARMOR_3, // T_PLATINUMHELM + MT_ARMOR_4, // T_AMULETOFWARDING + MT_ARTIPOISONBAG, // T_ITEMFLECHETTE + MT_ARTITORCH, // T_ITEMTORCH + MT_BLASTRADIUS, // T_ITEMREPULSION + MT_MANA3, // T_MANA3 + MT_ARTIPUZZSKULL, // T_PUZZSKULL + MT_ARTIPUZZGEMBIG, // T_PUZZGEMBIG + MT_ARTIPUZZGEMRED, // T_PUZZGEMRED + MT_ARTIPUZZGEMGREEN1, // T_PUZZGEMGREEN1 + MT_ARTIPUZZGEMGREEN2, // T_PUZZGEMGREEN2 + MT_ARTIPUZZGEMBLUE1, // T_PUZZGEMBLUE1 + MT_ARTIPUZZGEMBLUE2, // T_PUZZGEMBLUE2 + MT_ARTIPUZZBOOK1, // T_PUZZBOOK1 + MT_ARTIPUZZBOOK2, // T_PUZZBOOK2 + MT_KEY1, // T_METALKEY + MT_KEY2, // T_SMALLMETALKEY + MT_KEY3, // T_AXEKEY + MT_KEY4, // T_FIREKEY + MT_KEY5, // T_GREENKEY + MT_KEY6, // T_MACEKEY + MT_KEY7, // T_SILVERKEY + MT_KEY8, // T_RUSTYKEY + MT_KEY9, // T_HORNKEY + MT_KEYA, // T_SERPENTKEY + MT_WATER_DRIP, // T_WATERDRIP + MT_FLAME_SMALL_TEMP, // T_TEMPSMALLFLAME + MT_FLAME_SMALL, // T_PERMSMALLFLAME + MT_FLAME_LARGE_TEMP, // T_TEMPLARGEFLAME + MT_FLAME_LARGE, // T_PERMLARGEFLAME + MT_DEMON_MASH, // T_DEMON_MASH + MT_DEMON2_MASH, // T_DEMON2_MASH + MT_ETTIN_MASH, // T_ETTIN_MASH + MT_CENTAUR_MASH, // T_CENTAUR_MASH + MT_THRUSTFLOOR_UP, // T_THRUSTSPIKEUP + MT_THRUSTFLOOR_DOWN, // T_THRUSTSPIKEDOWN + MT_WRAITHFX4, // T_FLESH_DRIP1 + MT_WRAITHFX5, // T_FLESH_DRIP2 + MT_WRAITHFX2 // T_SPARK_DRIP +}; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// EV_ThingProjectile +// +//========================================================================== + +boolean EV_ThingProjectile(byte *args, boolean gravity) +{ + int tid; + angle_t angle; + int fineAngle; + fixed_t speed; + fixed_t vspeed; + mobjtype_t moType; + mobj_t *mobj; + mobj_t *newMobj; + int searcher; + boolean success; + + success = false; + searcher = -1; + tid = args[0]; + moType = TranslateThingType[args[1]]; + if(nomonsters && (mobjinfo[moType].flags&MF_COUNTKILL)) + { // Don't spawn monsters if -nomonsters + return false; + } + angle = (int)args[2]<<24; + fineAngle = angle>>ANGLETOFINESHIFT; + speed = (int)args[3]<<13; + vspeed = (int)args[4]<<13; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + newMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, moType); + if(newMobj->info->seesound) + { + S_StartSound(newMobj, newMobj->info->seesound); + } + newMobj->target = mobj; // Originator + newMobj->angle = angle; + newMobj->momx = FixedMul(speed, finecosine[fineAngle]); + newMobj->momy = FixedMul(speed, finesine[fineAngle]); + newMobj->momz = vspeed; + newMobj->flags2 |= MF2_DROPPED; // Don't respawn + if(gravity == true) + { + newMobj->flags &= ~MF_NOGRAVITY; + newMobj->flags2 |= MF2_LOGRAV; + } + if(P_CheckMissileSpawn(newMobj) == true) + { + success = true; + } + } + return success; +} + +//========================================================================== +// +// EV_ThingSpawn +// +//========================================================================== + +boolean EV_ThingSpawn(byte *args, boolean fog) +{ + int tid; + angle_t angle; + mobj_t *mobj; + mobj_t *newMobj; + mobj_t *fogMobj; + mobjtype_t moType; + int searcher; + boolean success; + fixed_t z; + + success = false; + searcher = -1; + tid = args[0]; + moType = TranslateThingType[args[1]]; + if(nomonsters && (mobjinfo[moType].flags&MF_COUNTKILL)) + { // Don't spawn monsters if -nomonsters + return false; + } + angle = (int)args[2]<<24; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if (mobjinfo[moType].flags2&MF2_FLOATBOB) + { + z = mobj->z - mobj->floorz; + } + else + { + z = mobj->z; + } + newMobj = P_SpawnMobj(mobj->x, mobj->y, z, moType); + if(P_TestMobjLocation(newMobj) == false) + { // Didn't fit + P_RemoveMobj(newMobj); + } + else + { + newMobj->angle = angle; + if(fog == true) + { + fogMobj = P_SpawnMobj(mobj->x, mobj->y, + mobj->z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fogMobj, SFX_TELEPORT); + } + newMobj->flags2 |= MF2_DROPPED; // Don't respawn + if (newMobj->flags2&MF2_FLOATBOB) + { + newMobj->special1 = newMobj->z - newMobj->floorz; + } + success = true; + } + } + return success; +} + +//========================================================================== +// +// EV_ThingActivate +// +//========================================================================== + +boolean EV_ThingActivate(int tid) +{ + mobj_t *mobj; + int searcher; + boolean success; + + success = false; + searcher = -1; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if(ActivateThing(mobj) == true) + { + success = true; + } + } + return success; +} + +//========================================================================== +// +// EV_ThingDeactivate +// +//========================================================================== + +boolean EV_ThingDeactivate(int tid) +{ + mobj_t *mobj; + int searcher; + boolean success; + + success = false; + searcher = -1; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if(DeactivateThing(mobj) == true) + { + success = true; + } + } + return success; +} + +//========================================================================== +// +// EV_ThingRemove +// +//========================================================================== + +boolean EV_ThingRemove(int tid) +{ + mobj_t *mobj; + int searcher; + boolean success; + + success = false; + searcher = -1; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if (mobj->type == MT_BRIDGE) + { + A_BridgeRemove(mobj); + return true; + } + P_RemoveMobj(mobj); + success = true; + } + return success; +} + +//========================================================================== +// +// EV_ThingDestroy +// +//========================================================================== + +boolean EV_ThingDestroy(int tid) +{ + mobj_t *mobj; + int searcher; + boolean success; + + success = false; + searcher = -1; + while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) + { + if(mobj->flags&MF_SHOOTABLE) + { + P_DamageMobj(mobj, NULL, NULL, 10000); + success = true; + } + } + return success; +} + +//========================================================================== +// +// EV_ThingMove +// +// arg[0] = tid +// arg[1] = speed +// arg[2] = angle (255 = use mobj angle) +// arg[3] = distance (pixels>>2) +// +//========================================================================== + +/* +boolean EV_ThingMove(byte *args) +{ + return false; +} +*/ + +//========================================================================== +// +// ActivateThing +// +//========================================================================== + +static boolean ActivateThing(mobj_t *mobj) +{ + if(mobj->flags&MF_COUNTKILL) + { // Monster + if(mobj->flags2&MF2_DORMANT) + { + mobj->flags2 &= ~MF2_DORMANT; + mobj->tics = 1; + return true; + } + return false; + } + switch(mobj->type) + { + case MT_ZTWINEDTORCH: + case MT_ZTWINEDTORCH_UNLIT: + P_SetMobjState(mobj, S_ZTWINEDTORCH_1); + S_StartSound(mobj, SFX_IGNITE); + break; + case MT_ZWALLTORCH: + case MT_ZWALLTORCH_UNLIT: + P_SetMobjState(mobj, S_ZWALLTORCH1); + S_StartSound(mobj, SFX_IGNITE); + break; + case MT_ZGEMPEDESTAL: + P_SetMobjState(mobj, S_ZGEMPEDESTAL2); + break; + case MT_ZWINGEDSTATUENOSKULL: + P_SetMobjState(mobj, S_ZWINGEDSTATUENOSKULL2); + break; + case MT_THRUSTFLOOR_UP: + case MT_THRUSTFLOOR_DOWN: + if (mobj->args[0]==0) + { + S_StartSound(mobj, SFX_THRUSTSPIKE_LOWER); + mobj->flags2 &= ~MF2_DONTDRAW; + if (mobj->args[1]) + P_SetMobjState(mobj, S_BTHRUSTRAISE1); + else + P_SetMobjState(mobj, S_THRUSTRAISE1); + } + break; + case MT_ZFIREBULL: + case MT_ZFIREBULL_UNLIT: + P_SetMobjState(mobj, S_ZFIREBULL_BIRTH); + S_StartSound(mobj, SFX_IGNITE); + break; + case MT_ZBELL: + if(mobj->health > 0) + { + P_DamageMobj(mobj, NULL, NULL, 10); // 'ring' the bell + } + break; + case MT_ZCAULDRON: + case MT_ZCAULDRON_UNLIT: + P_SetMobjState(mobj, S_ZCAULDRON1); + S_StartSound(mobj, SFX_IGNITE); + break; + case MT_FLAME_SMALL: + S_StartSound(mobj, SFX_IGNITE); + P_SetMobjState(mobj, S_FLAME_SMALL1); + break; + case MT_FLAME_LARGE: + S_StartSound(mobj, SFX_IGNITE); + P_SetMobjState(mobj, S_FLAME_LARGE1); + break; + case MT_BAT_SPAWNER: + P_SetMobjState(mobj, S_SPAWNBATS1); + break; + default: + return false; + break; + } + return true; +} + +//========================================================================== +// +// DeactivateThing +// +//========================================================================== + +static boolean DeactivateThing(mobj_t *mobj) +{ + if(mobj->flags&MF_COUNTKILL) + { // Monster + if(!(mobj->flags2&MF2_DORMANT)) + { + mobj->flags2 |= MF2_DORMANT; + mobj->tics = -1; + return true; + } + return false; + } + switch(mobj->type) + { + case MT_ZTWINEDTORCH: + case MT_ZTWINEDTORCH_UNLIT: + P_SetMobjState(mobj, S_ZTWINEDTORCH_UNLIT); + break; + case MT_ZWALLTORCH: + case MT_ZWALLTORCH_UNLIT: + P_SetMobjState(mobj, S_ZWALLTORCH_U); + break; + case MT_THRUSTFLOOR_UP: + case MT_THRUSTFLOOR_DOWN: + if (mobj->args[0]==1) + { + S_StartSound(mobj, SFX_THRUSTSPIKE_RAISE); + if (mobj->args[1]) + P_SetMobjState(mobj, S_BTHRUSTLOWER); + else + P_SetMobjState(mobj, S_THRUSTLOWER); + } + break; + case MT_ZFIREBULL: + case MT_ZFIREBULL_UNLIT: + P_SetMobjState(mobj, S_ZFIREBULL_DEATH); + break; + case MT_ZCAULDRON: + case MT_ZCAULDRON_UNLIT: + P_SetMobjState(mobj, S_ZCAULDRON_U); + break; + case MT_FLAME_SMALL: + P_SetMobjState(mobj, S_FLAME_SDORM1); + break; + case MT_FLAME_LARGE: + P_SetMobjState(mobj, S_FLAME_LDORM1); + break; + case MT_BAT_SPAWNER: + P_SetMobjState(mobj, S_SPAWNBATS_OFF); + break; + default: + return false; + break; + } + return true; +} diff --git a/src/hexen/p_tick.c b/src/hexen/p_tick.c new file mode 100644 index 00000000..a4bdb2cf --- /dev/null +++ b/src/hexen/p_tick.c @@ -0,0 +1,142 @@ + +//************************************************************************** +//** +//** p_tick.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_tick.c,v $ +//** $Revision: 1.5 $ +//** $Date: 95/10/08 21:53:00 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void RunThinkers(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int leveltime; +int TimerGame; +thinker_t thinkercap; // The head and tail of the thinker list + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// P_Ticker +// +//========================================================================== + +void P_Ticker(void) +{ + int i; + + if(paused) + { + return; + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + P_PlayerThink(&players[i]); + } + } + if(TimerGame) + { + if(!--TimerGame) + { + G_Completed(P_TranslateMap(P_GetMapNextMap(gamemap)), 0); + } + } + RunThinkers(); + P_UpdateSpecials(); + P_AnimateSurfaces(); + leveltime++; +} + +//========================================================================== +// +// RunThinkers +// +//========================================================================== + +static void RunThinkers(void) +{ + thinker_t *currentthinker; + + currentthinker = thinkercap.next; + while(currentthinker != &thinkercap) + { + if(currentthinker->function == (think_t)-1) + { // Time to remove it + currentthinker->next->prev = currentthinker->prev; + currentthinker->prev->next = currentthinker->next; + Z_Free(currentthinker); + } + else if(currentthinker->function) + { + currentthinker->function(currentthinker); + } + currentthinker = currentthinker->next; + } +} + +//========================================================================== +// +// P_InitThinkers +// +//========================================================================== + +void P_InitThinkers(void) +{ + thinkercap.prev = thinkercap.next = &thinkercap; +} + +//========================================================================== +// +// P_AddThinker +// +// Adds a new thinker at the end of the list. +// +//========================================================================== + +void P_AddThinker(thinker_t *thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; +} + +//========================================================================== +// +// P_RemoveThinker +// +// Deallocation is lazy -- it will not actually be freed until its +// thinking turn comes up. +// +//========================================================================== + +void P_RemoveThinker(thinker_t *thinker) +{ + thinker->function = (think_t)-1; +} diff --git a/src/hexen/p_user.c b/src/hexen/p_user.c new file mode 100644 index 00000000..1b19c204 --- /dev/null +++ b/src/hexen/p_user.c @@ -0,0 +1,1652 @@ + +//************************************************************************** +//** +//** p_user.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_user.c,v $ +//** $Revision: 1.123 $ +//** $Date: 96/01/05 14:21:01 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +void P_PlayerNextArtifact(player_t *player); + +// Macros + +#define MAXBOB 0x100000 // 16 pixels of bob + +// Data + +boolean onground; +int newtorch; // used in the torch flicker effect. +int newtorchdelta; + +int PStateNormal[NUMCLASSES] = +{ + S_FPLAY, + S_CPLAY, + S_MPLAY, + S_PIGPLAY +}; + +int PStateRun[NUMCLASSES] = +{ + S_FPLAY_RUN1, + S_CPLAY_RUN1, + S_MPLAY_RUN1, + S_PIGPLAY_RUN1 +}; + +int PStateAttack[NUMCLASSES] = +{ + S_FPLAY_ATK1, + S_CPLAY_ATK1, + S_MPLAY_ATK1, + S_PIGPLAY_ATK1 +}; + +int PStateAttackEnd[NUMCLASSES] = +{ + S_FPLAY_ATK2, + S_CPLAY_ATK3, + S_MPLAY_ATK2, + S_PIGPLAY_ATK1 +}; + +int ArmorMax[NUMCLASSES] = { 20, 18, 16, 1 }; +/* +================== += += P_Thrust += += moves the given origin along a given angle += +================== +*/ + +void P_Thrust(player_t *player, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + if(player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz)) + { + player->mo->momx += FixedMul(move, finecosine[angle]); + player->mo->momy += FixedMul(move, finesine[angle]); + } + else if(P_GetThingFloorType(player->mo) == FLOOR_ICE) // Friction_Low + { + player->mo->momx += FixedMul(move>>1, finecosine[angle]); + player->mo->momy += FixedMul(move>>1, finesine[angle]); + } + else + { + player->mo->momx += FixedMul(move, finecosine[angle]); + player->mo->momy += FixedMul(move, finesine[angle]); + } +} + + +/* +================== += += P_CalcHeight += += Calculate the walking / running height adjustment += +================== +*/ + +void P_CalcHeight (player_t *player) +{ + int angle; + fixed_t bob; + +// +// regular movement bobbing (needs to be calculated for gun swing even +// if not on ground) +// OPTIMIZE: tablify angle + + player->bob = FixedMul (player->mo->momx, player->mo->momx)+ + FixedMul (player->mo->momy,player->mo->momy); + player->bob >>= 2; + if (player->bob>MAXBOB) + player->bob = MAXBOB; + if(player->mo->flags2&MF2_FLY && !onground) + { + player->bob = FRACUNIT/2; + } + + if ((player->cheats & CF_NOMOMENTUM)) + { + player->viewz = player->mo->z + VIEWHEIGHT; + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; + player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES/20*leveltime)&FINEMASK; + bob = FixedMul ( player->bob/2, finesine[angle]); + +// +// move viewheight +// + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + if (player->viewheight < VIEWHEIGHT/2) + { + player->viewheight = VIEWHEIGHT/2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT/4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + + if(player->morphTics) + { + player->viewz = player->mo->z+player->viewheight-(20*FRACUNIT); + } + else + { + player->viewz = player->mo->z+player->viewheight+bob; + } + if(player->mo->floorclip && player->playerstate != PST_DEAD + && player->mo->z <= player->mo->floorz) + { + player->viewz -= player->mo->floorclip; + } + if(player->viewz > player->mo->ceilingz-4*FRACUNIT) + { + player->viewz = player->mo->ceilingz-4*FRACUNIT; + } + if(player->viewz < player->mo->floorz+4*FRACUNIT) + { + player->viewz = player->mo->floorz+4*FRACUNIT; + } +} + +/* +================= += += P_MovePlayer += +================= +*/ + +void P_MovePlayer(player_t *player) +{ + int look; + int fly; + ticcmd_t *cmd; + + cmd = &player->cmd; + player->mo->angle += (cmd->angleturn<<16); + + onground = (player->mo->z <= player->mo->floorz + || (player->mo->flags2&MF2_ONMOBJ)); + + if(cmd->forwardmove) + { + if(onground || player->mo->flags2&MF2_FLY) + { + P_Thrust(player, player->mo->angle, cmd->forwardmove*2048); + } + else + { + P_Thrust(player, player->mo->angle, FRACUNIT>>8); + } + } + if(cmd->sidemove) + { + if(onground || player->mo->flags2&MF2_FLY) + { + P_Thrust(player, player->mo->angle-ANG90, cmd->sidemove*2048); + } + else + { + P_Thrust(player, player->mo->angle, FRACUNIT>>8); + } + } + if(cmd->forwardmove || cmd->sidemove) + { + if(player->mo->state == &states[PStateNormal[player->class]]) + { + P_SetMobjState(player->mo, PStateRun[player->class]); + } + } + + look = cmd->lookfly&15; + if(look > 7) + { + look -= 16; + } + if(look) + { + if(look == TOCENTER) + { + player->centering = true; + } + else + { + player->lookdir += 5*look; + if(player->lookdir > 90 || player->lookdir < -110) + { + player->lookdir -= 5*look; + } + } + } + if(player->centering) + { + if(player->lookdir > 0) + { + player->lookdir -= 8; + } + else if(player->lookdir < 0) + { + player->lookdir += 8; + } + if(abs(player->lookdir) < 8) + { + player->lookdir = 0; + player->centering = false; + } + } + fly = cmd->lookfly>>4; + if(fly > 7) + { + fly -= 16; + } + if(fly && player->powers[pw_flight]) + { + if(fly != TOCENTER) + { + player->flyheight = fly*2; + if(!(player->mo->flags2&MF2_FLY)) + { + player->mo->flags2 |= MF2_FLY; + player->mo->flags |= MF_NOGRAVITY; + if(player->mo->momz <= -39*FRACUNIT) + { // stop falling scream + S_StopSound(player->mo); + } + } + } + else + { + player->mo->flags2 &= ~MF2_FLY; + player->mo->flags &= ~MF_NOGRAVITY; + } + } + else if(fly > 0) + { + P_PlayerUseArtifact(player, arti_fly); + } + if(player->mo->flags2&MF2_FLY) + { + player->mo->momz = player->flyheight*FRACUNIT; + if(player->flyheight) + { + player->flyheight /= 2; + } + } +} + +//========================================================================== +// +// P_DeathThink +// +//========================================================================== + +void P_DeathThink(player_t *player) +{ + int dir; + angle_t delta; + int lookDelta; + extern int inv_ptr; + extern int curpos; + + P_MovePsprites(player); + + onground = (player->mo->z <= player->mo->floorz); + if(player->mo->type == MT_BLOODYSKULL || player->mo->type == MT_ICECHUNK) + { // Flying bloody skull or flying ice chunk + player->viewheight = 6*FRACUNIT; + player->deltaviewheight = 0; + //player->damagecount = 20; + if(onground) + { + if(player->lookdir < 60) + { + lookDelta = (60-player->lookdir)/8; + if(lookDelta < 1 && (leveltime&1)) + { + lookDelta = 1; + } + else if(lookDelta > 6) + { + lookDelta = 6; + } + player->lookdir += lookDelta; + } + } + } + else if(!(player->mo->flags2&MF2_ICEDAMAGE)) + { // Fall to ground (if not frozen) + player->deltaviewheight = 0; + if(player->viewheight > 6*FRACUNIT) + { + player->viewheight -= FRACUNIT; + } + if(player->viewheight < 6*FRACUNIT) + { + player->viewheight = 6*FRACUNIT; + } + if(player->lookdir > 0) + { + player->lookdir -= 6; + } + else if(player->lookdir < 0) + { + player->lookdir += 6; + } + if(abs(player->lookdir) < 6) + { + player->lookdir = 0; + } + } + P_CalcHeight(player); + + if(player->attacker && player->attacker != player->mo) + { // Watch killer + dir = P_FaceMobj(player->mo, player->attacker, &delta); + if(delta < ANGLE_1*10) + { // Looking at killer, so fade damage and poison counters + if(player->damagecount) + { + player->damagecount--; + } + if(player->poisoncount) + { + player->poisoncount--; + } + } + delta = delta/8; + if(delta > ANGLE_1*5) + { + delta = ANGLE_1*5; + } + if(dir) + { // Turn clockwise + player->mo->angle += delta; + } + else + { // Turn counter clockwise + player->mo->angle -= delta; + } + } + else if(player->damagecount || player->poisoncount) + { + if(player->damagecount) + { + player->damagecount--; + } + else + { + player->poisoncount--; + } + } + + if(player->cmd.buttons&BT_USE) + { + if(player == &players[consoleplayer]) + { + I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE)); + inv_ptr = 0; + curpos = 0; + newtorch = 0; + newtorchdelta = 0; + } + player->playerstate = PST_REBORN; + player->mo->special1 = player->class; + if(player->mo->special1 > 2) + { + player->mo->special1 = 0; + } + // Let the mobj know the player has entered the reborn state. Some + // mobjs need to know when it's ok to remove themselves. + player->mo->special2 = 666; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_MorphPlayerThink +// +//---------------------------------------------------------------------------- + +void P_MorphPlayerThink(player_t *player) +{ + mobj_t *pmo; + + if(player->morphTics&15) + { + return; + } + pmo = player->mo; + if(!(pmo->momx+pmo->momy) && P_Random() < 64) + { // Snout sniff + P_SetPspriteNF(player, ps_weapon, S_SNOUTATK2); + S_StartSound(pmo, SFX_PIG_ACTIVE1); // snort + return; + } + if(P_Random() < 48) + { + if(P_Random() < 128) + { + S_StartSound(pmo, SFX_PIG_ACTIVE1); + } + else + { + S_StartSound(pmo, SFX_PIG_ACTIVE2); + } + } +} + +//---------------------------------------------------------------------------- +// +// FUNC P_GetPlayerNum +// +//---------------------------------------------------------------------------- + +int P_GetPlayerNum(player_t *player) +{ + int i; + + for(i = 0; i < MAXPLAYERS; i++) + { + if(player == &players[i]) + { + return(i); + } + } + return(0); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_UndoPlayerMorph +// +//---------------------------------------------------------------------------- + +boolean P_UndoPlayerMorph(player_t *player) +{ + mobj_t *fog; + mobj_t *mo; + mobj_t *pmo; + fixed_t x; + fixed_t y; + fixed_t z; + angle_t angle; + int playerNum; + weapontype_t weapon; + int oldFlags; + int oldFlags2; + int oldBeast; + + pmo = player->mo; + x = pmo->x; + y = pmo->y; + z = pmo->z; + angle = pmo->angle; + weapon = pmo->special1; + oldFlags = pmo->flags; + oldFlags2 = pmo->flags2; + oldBeast = pmo->type; + P_SetMobjState(pmo, S_FREETARGMOBJ); + playerNum = P_GetPlayerNum(player); + switch(PlayerClass[playerNum]) + { + case PCLASS_FIGHTER: + mo = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER); + break; + case PCLASS_CLERIC: + mo = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC); + break; + case PCLASS_MAGE: + mo = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE); + break; + default: + I_Error("P_UndoPlayerMorph: Unknown player class %d\n", + player->class); + } + if(P_TestMobjLocation(mo) == false) + { // Didn't fit + P_RemoveMobj(mo); + mo = P_SpawnMobj(x, y, z, oldBeast); + mo->angle = angle; + mo->health = player->health; + mo->special1 = weapon; + mo->player = player; + mo->flags = oldFlags; + mo->flags2 = oldFlags2; + player->mo = mo; + player->morphTics = 2*35; + return(false); + } + if(player->class == PCLASS_FIGHTER) + { + // The first type should be blue, and the third should be the + // Fighter's original gold color + if(playerNum == 0) + { + mo->flags |= 2<flags |= playerNum<flags |= playerNum<angle = angle; + mo->player = player; + mo->reactiontime = 18; + if(oldFlags2&MF2_FLY) + { + mo->flags2 |= MF2_FLY; + mo->flags |= MF_NOGRAVITY; + } + player->morphTics = 0; + player->health = mo->health = MAXHEALTH; + player->mo = mo; + player->class = PlayerClass[playerNum]; + angle >>= ANGLETOFINESHIFT; + fog = P_SpawnMobj(x+20*finecosine[angle], + y+20*finesine[angle], z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + P_PostMorphWeapon(player, weapon); + return(true); +} + + +//---------------------------------------------------------------------------- +// +// PROC P_PlayerThink +// +//---------------------------------------------------------------------------- + +void P_PlayerThink(player_t *player) +{ + ticcmd_t *cmd; + weapontype_t newweapon; + int floorType; + mobj_t *pmo; + + // No-clip cheat + if(player->cheats&CF_NOCLIP) + { + player->mo->flags |= MF_NOCLIP; + } + else + { + player->mo->flags &= ~MF_NOCLIP; + } + cmd = &player->cmd; + if(player->mo->flags&MF_JUSTATTACKED) + { // Gauntlets attack auto forward motion + cmd->angleturn = 0; + cmd->forwardmove = 0xc800/512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } +// messageTics is above the rest of the counters so that messages will +// go away, even in death. + player->messageTics--; // Can go negative + if(!player->messageTics || player->messageTics == -1) + { // Refresh the screen when a message goes away + player->ultimateMessage = false; // clear out any chat messages. + player->yellowMessage = false; + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } + } + player->worldTimer++; + if(player->playerstate == PST_DEAD) + { + P_DeathThink(player); + return; + } + if(player->jumpTics) + { + player->jumpTics--; + } + if(player->morphTics) + { + P_MorphPlayerThink(player); + } + // Handle movement + if(player->mo->reactiontime) + { // Player is frozen + player->mo->reactiontime--; + } + else + { + P_MovePlayer(player); + pmo = player->mo; + if(player->powers[pw_speed] && !(leveltime&1) + && P_AproxDistance(pmo->momx, pmo->momy) > 12*FRACUNIT) + { + mobj_t *speedMo; + int playerNum; + + speedMo = P_SpawnMobj(pmo->x, pmo->y, pmo->z, MT_PLAYER_SPEED); + if(speedMo) + { + speedMo->angle = pmo->angle; + playerNum = P_GetPlayerNum(player); + if(player->class == PCLASS_FIGHTER) + { + // The first type should be blue, and the + // third should be the Fighter's original gold color + if(playerNum == 0) + { + speedMo->flags |= 2<flags |= playerNum<flags |= playerNum<target = pmo; + speedMo->special1 = player->class; + if(speedMo->special1 > 2) + { + speedMo->special1 = 0; + } + speedMo->sprite = pmo->sprite; + speedMo->floorclip = pmo->floorclip; + if(player == &players[consoleplayer]) + { + speedMo->flags2 |= MF2_DONTDRAW; + } + } + } + } + P_CalcHeight(player); + if(player->mo->subsector->sector->special) + { + P_PlayerInSpecialSector(player); + } + if((floorType = P_GetThingFloorType(player->mo)) != FLOOR_SOLID) + { + P_PlayerOnSpecialFlat(player, floorType); + } + switch(player->class) + { + case PCLASS_FIGHTER: + if(player->mo->momz <= -35*FRACUNIT + && player->mo->momz >= -40*FRACUNIT && !player->morphTics + && !S_GetSoundPlayingInfo(player->mo, + SFX_PLAYER_FIGHTER_FALLING_SCREAM)) + { + S_StartSound(player->mo, + SFX_PLAYER_FIGHTER_FALLING_SCREAM); + } + break; + case PCLASS_CLERIC: + if(player->mo->momz <= -35*FRACUNIT + && player->mo->momz >= -40*FRACUNIT && !player->morphTics + && !S_GetSoundPlayingInfo(player->mo, + SFX_PLAYER_CLERIC_FALLING_SCREAM)) + { + S_StartSound(player->mo, + SFX_PLAYER_CLERIC_FALLING_SCREAM); + } + break; + case PCLASS_MAGE: + if(player->mo->momz <= -35*FRACUNIT + && player->mo->momz >= -40*FRACUNIT && !player->morphTics + && !S_GetSoundPlayingInfo(player->mo, + SFX_PLAYER_MAGE_FALLING_SCREAM)) + { + S_StartSound(player->mo, + SFX_PLAYER_MAGE_FALLING_SCREAM); + } + break; + default: + break; + } + if(cmd->arti) + { // Use an artifact + if((cmd->arti&AFLAG_JUMP) && onground && !player->jumpTics) + { + if(player->morphTics) + { + player->mo->momz = 6*FRACUNIT; + } + else + { + player->mo->momz = 9*FRACUNIT; + } + player->mo->flags2 &= ~MF2_ONMOBJ; + player->jumpTics = 18; + } + else if(cmd->arti&AFLAG_SUICIDE) + { + P_DamageMobj(player->mo, NULL, NULL, 10000); + } + if(cmd->arti == NUMARTIFACTS) + { // use one of each artifact (except puzzle artifacts) + int i; + + for(i = 1; i < arti_firstpuzzitem; i++) + { + P_PlayerUseArtifact(player, i); + } + } + else + { + P_PlayerUseArtifact(player, cmd->arti&AFLAG_MASK); + } + } + // Check for weapon change + if(cmd->buttons&BT_SPECIAL) + { // A special event has no other buttons + cmd->buttons = 0; + } + if(cmd->buttons&BT_CHANGE && !player->morphTics) + { + // The actual changing of the weapon is done when the weapon + // psprite can do it (A_WeaponReady), so it doesn't happen in + // the middle of an attack. + newweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT; + if(player->weaponowned[newweapon] + && newweapon != player->readyweapon) + { + player->pendingweapon = newweapon; + } + } + // Check for use + if(cmd->buttons&BT_USE) + { + if(!player->usedown) + { + P_UseLines(player); + player->usedown = true; + } + } + else + { + player->usedown = false; + } + // Morph counter + if(player->morphTics) + { + if(!--player->morphTics) + { // Attempt to undo the pig + P_UndoPlayerMorph(player); + } + } + // Cycle psprites + P_MovePsprites(player); + // Other Counters + if(player->powers[pw_invulnerability]) + { + if(player->class == PCLASS_CLERIC) + { + if(!(leveltime&7) && player->mo->flags&MF_SHADOW + && !(player->mo->flags2&MF2_DONTDRAW)) + { + player->mo->flags &= ~MF_SHADOW; + if(!(player->mo->flags&MF_ALTSHADOW)) + { + player->mo->flags2 |= MF2_DONTDRAW|MF2_NONSHOOTABLE; + } + } + if(!(leveltime&31)) + { + if(player->mo->flags2&MF2_DONTDRAW) + { + if(!(player->mo->flags&MF_SHADOW)) + { + player->mo->flags |= MF_SHADOW|MF_ALTSHADOW; + } + else + { + player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE); + } + } + else + { + player->mo->flags |= MF_SHADOW; + player->mo->flags &= ~MF_ALTSHADOW; + } + } + } + if(!(--player->powers[pw_invulnerability])) + { + player->mo->flags2 &= ~(MF2_INVULNERABLE|MF2_REFLECTIVE); + if(player->class == PCLASS_CLERIC) + { + player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE); + player->mo->flags &= ~(MF_SHADOW|MF_ALTSHADOW); + } + } + } + if(player->powers[pw_minotaur]) + { + player->powers[pw_minotaur]--; + } + if(player->powers[pw_infrared]) + { + player->powers[pw_infrared]--; + } + if(player->powers[pw_flight] && netgame) + { + if(!--player->powers[pw_flight]) + { + if(player->mo->z != player->mo->floorz) + { +#ifdef __WATCOMC__ + if(!useexterndriver) + { + player->centering = true; + } +#else + player->centering = true; +#endif + } + player->mo->flags2 &= ~MF2_FLY; + player->mo->flags &= ~MF_NOGRAVITY; + BorderTopRefresh = true; //make sure the sprite's cleared out + } + } + if(player->powers[pw_speed]) + { + player->powers[pw_speed]--; + } + if(player->damagecount) + { + player->damagecount--; + } + if(player->bonuscount) + { + player->bonuscount--; + } + if(player->poisoncount && !(leveltime&15)) + { + player->poisoncount -= 5; + if(player->poisoncount < 0) + { + player->poisoncount = 0; + } + P_PoisonDamage(player, player->poisoner, 1, true); + } + // Colormaps +// if(player->powers[pw_invulnerability]) +// { +// if(player->powers[pw_invulnerability] > BLINKTHRESHOLD +// || (player->powers[pw_invulnerability]&8)) +// { +// player->fixedcolormap = INVERSECOLORMAP; +// } +// else +// { +// player->fixedcolormap = 0; +// } +// } +// else + if(player->powers[pw_infrared]) + { + if (player->powers[pw_infrared] <= BLINKTHRESHOLD) + { + if(player->powers[pw_infrared]&8) + { + player->fixedcolormap = 0; + } + else + { + player->fixedcolormap = 1; + } + } + else if(!(leveltime&16) && player == &players[consoleplayer]) + { + if(newtorch) + { + if(player->fixedcolormap+newtorchdelta > 7 + || player->fixedcolormap+newtorchdelta < 1 + || newtorch == player->fixedcolormap) + { + newtorch = 0; + } + else + { + player->fixedcolormap += newtorchdelta; + } + } + else + { + newtorch = (M_Random()&7)+1; + newtorchdelta = (newtorch == player->fixedcolormap) ? + 0 : ((newtorch > player->fixedcolormap) ? 1 : -1); + } + } + } + else + { + player->fixedcolormap = 0; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_ArtiTele +// +//---------------------------------------------------------------------------- + +void P_ArtiTele(player_t *player) +{ + int i; + int selections; + fixed_t destX; + fixed_t destY; + angle_t destAngle; + + if(deathmatch) + { + selections = deathmatch_p-deathmatchstarts; + i = P_Random()%selections; + destX = deathmatchstarts[i].x<mo, destX, destY, destAngle, true); + if(player->morphTics) + { // Teleporting away will undo any morph effects (pig) + P_UndoPlayerMorph(player); + } + //S_StartSound(NULL, sfx_wpnup); // Full volume laugh +} + + +//---------------------------------------------------------------------------- +// +// PROC P_ArtiTeleportOther +// +//---------------------------------------------------------------------------- + +void P_ArtiTeleportOther(player_t *player) +{ + mobj_t *mo; + + mo=P_SpawnPlayerMissile(player->mo, MT_TELOTHER_FX1); + if (mo) + { + mo->target = player->mo; + } +} + + +void P_TeleportToPlayerStarts(mobj_t *victim) +{ + int i,selections=0; + fixed_t destX,destY; + angle_t destAngle; + + for (i=0;iplayer) + { + if (deathmatch) + P_TeleportToDeathmatchStarts(victim); + else + P_TeleportToPlayerStarts(victim); + } + else + { + // If death action, run it upon teleport + if (victim->flags&MF_COUNTKILL && victim->special) + { + P_RemoveMobjFromTIDList(victim); + P_ExecuteLineSpecial(victim->special, victim->args, + NULL, 0, victim); + victim->special = 0; + } + + // Send all monsters to deathmatch spots + P_TeleportToDeathmatchStarts(victim); + } +} + + + +#define BLAST_RADIUS_DIST 255*FRACUNIT +#define BLAST_SPEED 20*FRACUNIT +#define BLAST_FULLSTRENGTH 255 + +void ResetBlasted(mobj_t *mo) +{ + mo->flags2 &= ~MF2_BLASTED; + if (!(mo->flags&MF_ICECORPSE)) + { + mo->flags2 &= ~MF2_SLIDE; + } +} + +void P_BlastMobj(mobj_t *source, mobj_t *victim, fixed_t strength) +{ + angle_t angle,ang; + mobj_t *mo; + fixed_t x,y,z; + + angle = R_PointToAngle2(source->x, source->y, victim->x, victim->y); + angle >>= ANGLETOFINESHIFT; + if (strength < BLAST_FULLSTRENGTH) + { + victim->momx = FixedMul(strength, finecosine[angle]); + victim->momy = FixedMul(strength, finesine[angle]); + if (victim->player) + { + // Players handled automatically + } + else + { + victim->flags2 |= MF2_SLIDE; + victim->flags2 |= MF2_BLASTED; + } + } + else // full strength blast from artifact + { + if (victim->flags&MF_MISSILE) + { + switch(victim->type) + { + case MT_SORCBALL1: // don't blast sorcerer balls + case MT_SORCBALL2: + case MT_SORCBALL3: + return; + break; + case MT_MSTAFF_FX2: // Reflect to originator + victim->special1 = (int)victim->target; + victim->target = source; + break; + default: + break; + } + } + if (victim->type == MT_HOLY_FX) + { + if ((mobj_t *)(victim->special1) == source) + { + victim->special1 = (int)victim->target; + victim->target = source; + } + } + victim->momx = FixedMul(BLAST_SPEED, finecosine[angle]); + victim->momy = FixedMul(BLAST_SPEED, finesine[angle]); + + // Spawn blast puff + ang = R_PointToAngle2(victim->x, victim->y, source->x, source->y); + ang >>= ANGLETOFINESHIFT; + x = victim->x + FixedMul(victim->radius+FRACUNIT, finecosine[ang]); + y = victim->y + FixedMul(victim->radius+FRACUNIT, finesine[ang]); + z = victim->z - victim->floorclip + (victim->height>>1); + mo=P_SpawnMobj(x, y, z, MT_BLASTEFFECT); + if (mo) + { + mo->momx = victim->momx; + mo->momy = victim->momy; + } + + if (victim->flags&MF_MISSILE) + { + victim->momz = 8*FRACUNIT; + mo->momz = victim->momz; + } + else + { + victim->momz = (1000/victim->info->mass)<player) + { + // Players handled automatically + } + else + { + victim->flags2 |= MF2_SLIDE; + victim->flags2 |= MF2_BLASTED; + } + } +} + + +// Blast all mobj things away +void P_BlastRadius(player_t *player) +{ + mobj_t *mo; + mobj_t *pmo=player->mo; + thinker_t *think; + fixed_t dist; + + S_StartSound(pmo, SFX_ARTIFACT_BLAST); + P_NoiseAlert(player->mo, player->mo); + + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mo = (mobj_t *)think; + if((mo == pmo) || (mo->flags2&MF2_BOSS)) + { // Not a valid monster + continue; + } + if ((mo->type == MT_POISONCLOUD) || // poison cloud + (mo->type == MT_HOLY_FX) || // holy fx + (mo->flags&MF_ICECORPSE)) // frozen corpse + { + // Let these special cases go + } + else if ((mo->flags&MF_COUNTKILL) && + (mo->health <= 0)) + { + continue; + } + else if (!(mo->flags&MF_COUNTKILL) && + !(mo->player) && + !(mo->flags&MF_MISSILE)) + { // Must be monster, player, or missile + continue; + } + if (mo->flags2&MF2_DORMANT) + { + continue; // no dormant creatures + } + if ((mo->type == MT_WRAITHB) && (mo->flags2&MF2_DONTDRAW)) + { + continue; // no underground wraiths + } + if ((mo->type == MT_SPLASHBASE) || + (mo->type == MT_SPLASH)) + { + continue; + } + if(mo->type == MT_SERPENT || mo->type == MT_SERPENTLEADER) + { + continue; + } + dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); + if(dist > BLAST_RADIUS_DIST) + { // Out of range + continue; + } + P_BlastMobj(pmo, mo, BLAST_FULLSTRENGTH); + } +} + + +#define HEAL_RADIUS_DIST 255*FRACUNIT + +// Do class specific effect for everyone in radius +boolean P_HealRadius(player_t *player) +{ + mobj_t *mo; + mobj_t *pmo=player->mo; + thinker_t *think; + fixed_t dist; + int effective=false; + int amount; + + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + mo = (mobj_t *)think; + + if (!mo->player) continue; + if (mo->health <= 0) continue; + dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); + if(dist > HEAL_RADIUS_DIST) + { // Out of range + continue; + } + + switch(player->class) + { + case PCLASS_FIGHTER: // Radius armor boost + if ((P_GiveArmor(mo->player, ARMOR_ARMOR, 1)) || + (P_GiveArmor(mo->player, ARMOR_SHIELD, 1)) || + (P_GiveArmor(mo->player, ARMOR_HELMET, 1)) || + (P_GiveArmor(mo->player, ARMOR_AMULET, 1))) + { + effective=true; + S_StartSound(mo, SFX_MYSTICINCANT); + } + break; + case PCLASS_CLERIC: // Radius heal + amount = 50 + (P_Random()%50); + if (P_GiveBody(mo->player, amount)) + { + effective=true; + S_StartSound(mo, SFX_MYSTICINCANT); + } + break; + case PCLASS_MAGE: // Radius mana boost + amount = 50 + (P_Random()%50); + if ((P_GiveMana(mo->player, MANA_1, amount)) || + (P_GiveMana(mo->player, MANA_2, amount))) + { + effective=true; + S_StartSound(mo, SFX_MYSTICINCANT); + } + break; + case PCLASS_PIG: + default: + break; + } + } + return(effective); +} + + +//---------------------------------------------------------------------------- +// +// PROC P_PlayerNextArtifact +// +//---------------------------------------------------------------------------- + +void P_PlayerNextArtifact(player_t *player) +{ + extern int inv_ptr; + extern int curpos; + + if(player == &players[consoleplayer]) + { + inv_ptr--; + if(inv_ptr < 6) + { + curpos--; + if(curpos < 0) + { + curpos = 0; + } + } + if(inv_ptr < 0) + { + inv_ptr = player->inventorySlotNum-1; + if(inv_ptr < 6) + { + curpos = inv_ptr; + } + else + { + curpos = 6; + } + } + player->readyArtifact = + player->inventory[inv_ptr].type; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_PlayerRemoveArtifact +// +//---------------------------------------------------------------------------- + +void P_PlayerRemoveArtifact(player_t *player, int slot) +{ + int i; + extern int inv_ptr; + extern int curpos; + + player->artifactCount--; + if(!(--player->inventory[slot].count)) + { // Used last of a type - compact the artifact list + player->readyArtifact = arti_none; + player->inventory[slot].type = arti_none; + for(i = slot+1; i < player->inventorySlotNum; i++) + { + player->inventory[i-1] = player->inventory[i]; + } + player->inventorySlotNum--; + if(player == &players[consoleplayer]) + { // Set position markers and get next readyArtifact + inv_ptr--; + if(inv_ptr < 6) + { + curpos--; + if(curpos < 0) + { + curpos = 0; + } + } + if(inv_ptr >= player->inventorySlotNum) + { + inv_ptr = player->inventorySlotNum-1; + } + if(inv_ptr < 0) + { + inv_ptr = 0; + } + player->readyArtifact = + player->inventory[inv_ptr].type; + } + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_PlayerUseArtifact +// +//---------------------------------------------------------------------------- + +void P_PlayerUseArtifact(player_t *player, artitype_t arti) +{ + int i; + + for(i = 0; i < player->inventorySlotNum; i++) + { + if(player->inventory[i].type == arti) + { // Found match - try to use + if(P_UseArtifact(player, arti)) + { // Artifact was used - remove it from inventory + P_PlayerRemoveArtifact(player, i); + if(player == &players[consoleplayer]) + { + if(arti < arti_firstpuzzitem) + { + S_StartSound(NULL, SFX_ARTIFACT_USE); + } + else + { + S_StartSound(NULL, SFX_PUZZLE_SUCCESS); + } + ArtifactFlash = 4; + } + } + else if(arti < arti_firstpuzzitem) + { // Unable to use artifact, advance pointer + P_PlayerNextArtifact(player); + } + break; + } + } +} + +//========================================================================== +// +// P_UseArtifact +// +// Returns true if the artifact was used. +// +//========================================================================== + +boolean P_UseArtifact(player_t *player, artitype_t arti) +{ + mobj_t *mo; + angle_t angle; + int i; + int count; + + switch(arti) + { + case arti_invulnerability: + if(!P_GivePower(player, pw_invulnerability)) + { + return(false); + } + break; + case arti_health: + if(!P_GiveBody(player, 25)) + { + return(false); + } + break; + case arti_superhealth: + if(!P_GiveBody(player, 100)) + { + return(false); + } + break; + case arti_healingradius: + if (!P_HealRadius(player)) + { + return(false); + } + break; + case arti_torch: + if(!P_GivePower(player, pw_infrared)) + { + return(false); + } + break; + case arti_egg: + mo = player->mo; + P_SpawnPlayerMissile(mo, MT_EGGFX); + P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/6)); + P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/6)); + P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/3)); + P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/3)); + break; + case arti_fly: + if(!P_GivePower(player, pw_flight)) + { + return(false); + } + if(player->mo->momz <= -35*FRACUNIT) + { // stop falling scream + S_StopSound(player->mo); + } + break; + case arti_summon: + mo = P_SpawnPlayerMissile(player->mo, MT_SUMMON_FX); + if (mo) + { + mo->target = player->mo; + mo->special1 = (int)(player->mo); + mo->momz = 5*FRACUNIT; + } + break; + case arti_teleport: + P_ArtiTele(player); + break; + case arti_teleportother: + P_ArtiTeleportOther(player); + break; + case arti_poisonbag: + angle = player->mo->angle>>ANGLETOFINESHIFT; + if(player->class == PCLASS_CLERIC) + { + mo = P_SpawnMobj(player->mo->x+16*finecosine[angle], + player->mo->y+24*finesine[angle], player->mo->z- + player->mo->floorclip+8*FRACUNIT, MT_POISONBAG); + if(mo) + { + mo->target = player->mo; + } + } + else if(player->class == PCLASS_MAGE) + { + mo = P_SpawnMobj(player->mo->x+16*finecosine[angle], + player->mo->y+24*finesine[angle], player->mo->z- + player->mo->floorclip+8*FRACUNIT, MT_FIREBOMB); + if(mo) + { + mo->target = player->mo; + } + } + else // PCLASS_FIGHTER, obviously (also pig, not so obviously) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, + player->mo->z-player->mo->floorclip+35*FRACUNIT, + MT_THROWINGBOMB); + if(mo) + { + mo->angle = player->mo->angle+(((P_Random()&7)-4)<<24); + mo->momz = 4*FRACUNIT+((player->lookdir)<<(FRACBITS-4)); + mo->z += player->lookdir<<(FRACBITS-4); + P_ThrustMobj(mo, mo->angle, mo->info->speed); + mo->momx += player->mo->momx>>1; + mo->momy += player->mo->momy>>1; + mo->target = player->mo; + mo->tics -= P_Random()&3; + P_CheckMissileSpawn(mo); + } + } + break; + case arti_speed: + if(!P_GivePower(player, pw_speed)) + { + return(false); + } + break; + case arti_boostmana: + if(!P_GiveMana(player, MANA_1, MAX_MANA)) + { + if(!P_GiveMana(player, MANA_2, MAX_MANA)) + { + return false; + } + + } + else + { + P_GiveMana(player, MANA_2, MAX_MANA); + } + break; + case arti_boostarmor: + count = 0; + + for(i = 0; i < NUMARMOR; i++) + { + count += P_GiveArmor(player, i, 1); // 1 point per armor type + } + if(!count) + { + return false; + } + break; + case arti_blastradius: + P_BlastRadius(player); + break; + + case arti_puzzskull: + case arti_puzzgembig: + case arti_puzzgemred: + case arti_puzzgemgreen1: + case arti_puzzgemgreen2: + case arti_puzzgemblue1: + case arti_puzzgemblue2: + case arti_puzzbook1: + case arti_puzzbook2: + case arti_puzzskull2: + case arti_puzzfweapon: + case arti_puzzcweapon: + case arti_puzzmweapon: + case arti_puzzgear1: + case arti_puzzgear2: + case arti_puzzgear3: + case arti_puzzgear4: + if(P_UsePuzzleItem(player, arti-arti_firstpuzzitem)) + { + return true; + } + else + { + P_SetYellowMessage(player, TXT_USEPUZZLEFAILED, false); + return false; + } + break; + default: + return false; + } + return true; +} + +//============================================================================ +// +// A_SpeedFade +// +//============================================================================ + +void A_SpeedFade(mobj_t *actor) +{ + actor->flags |= MF_SHADOW; + actor->flags &= ~MF_ALTSHADOW; + actor->sprite = actor->target->sprite; +} diff --git a/src/hexen/po_man.c b/src/hexen/po_man.c new file mode 100644 index 00000000..6e0883ff --- /dev/null +++ b/src/hexen/po_man.c @@ -0,0 +1,1482 @@ + +//************************************************************************** +//** +//** PO_MAN.C : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: po_man.c,v $ +//** $Revision: 1.22 $ +//** $Date: 95/09/28 18:20:56 $ +//** $Author: cjr $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" +#include "r_local.h" + +// MACROS ------------------------------------------------------------------ + +#define PO_MAXPOLYSEGS 64 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +boolean PO_MovePolyobj(int num, int x, int y); +boolean PO_RotatePolyobj(int num, angle_t angle); +void PO_Init(int lump); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static polyobj_t *GetPolyobj(int polyNum); +static int GetPolyobjMirror(int poly); +static void ThrustMobj(mobj_t *mobj, seg_t *seg, polyobj_t *po); +static void UpdateSegBBox(seg_t *seg); +static void RotatePt(int an, fixed_t *x, fixed_t *y, fixed_t startSpotX, + fixed_t startSpotY); +static void UnLinkPolyobj(polyobj_t *po); +static void LinkPolyobj(polyobj_t *po); +static boolean CheckMobjBlocking(seg_t *seg, polyobj_t *po); +static void InitBlockMap(void); +static void IterFindPolySegs(int x, int y, seg_t **segList); +static void SpawnPolyobj(int index, int tag, boolean crush); +static void TranslateToStartSpot(int tag, int originX, int originY); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern seg_t *segs; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +polyblock_t **PolyBlockMap; +polyobj_t *polyobjs; // list of all poly-objects on the level +int po_NumPolyobjs; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int PolySegCount; +static fixed_t PolyStartX; +static fixed_t PolyStartY; + +// CODE -------------------------------------------------------------------- + +// ===== Polyobj Event Code ===== + +//========================================================================== +// +// T_RotatePoly +// +//========================================================================== + +void T_RotatePoly(polyevent_t *pe) +{ + int absSpeed; + polyobj_t *poly; + + if(PO_RotatePolyobj(pe->polyobj, pe->speed)) + { + absSpeed = abs(pe->speed); + + if(pe->dist == -1) + { // perpetual polyobj + return; + } + pe->dist -= absSpeed; + if(pe->dist <= 0) + { + poly = GetPolyobj(pe->polyobj); + if(poly->specialdata == pe) + { + poly->specialdata = NULL; + } + SN_StopSequence((mobj_t *)&poly->startSpot); + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pe->thinker); + } + if(pe->dist < absSpeed) + { + pe->speed = pe->dist*(pe->speed < 0 ? -1 : 1); + } + } +} + +//========================================================================== +// +// EV_RotatePoly +// +//========================================================================== + +boolean EV_RotatePoly(line_t *line, byte *args, int direction, boolean + overRide) +{ + int mirror; + int polyNum; + polyevent_t *pe; + polyobj_t *poly; + + polyNum = args[0]; + if(poly = GetPolyobj(polyNum)) + { + if(poly->specialdata && !overRide) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_RotatePoly; + pe->polyobj = polyNum; + if(args[2]) + { + if(args[2] == 255) + { + pe->dist = -1; + } + else + { + pe->dist = args[2]*(ANGLE_90/64); // Angle + } + } + else + { + pe->dist = ANGLE_MAX-1; + } + pe->speed = (args[1]*direction*(ANGLE_90/64))>>3; + poly->specialdata = pe; + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + + while(mirror = GetPolyobjMirror(polyNum)) + { + poly = GetPolyobj(mirror); + if(poly && poly->specialdata && !overRide) + { // mirroring poly is already in motion + break; + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_RotatePoly; + poly->specialdata = pe; + pe->polyobj = mirror; + if(args[2]) + { + if(args[2] == 255) + { + pe->dist = -1; + } + else + { + pe->dist = args[2]*(ANGLE_90/64); // Angle + } + } + else + { + pe->dist = ANGLE_MAX-1; + } + if(poly = GetPolyobj(polyNum)) + { + poly->specialdata = pe; + } + else + { + I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); + } + direction = -direction; + pe->speed = (args[1]*direction*(ANGLE_90/64))>>3; + polyNum = mirror; + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + return true; +} + +//========================================================================== +// +// T_MovePoly +// +//========================================================================== + +void T_MovePoly(polyevent_t *pe) +{ + int absSpeed; + polyobj_t *poly; + + if(PO_MovePolyobj(pe->polyobj, pe->xSpeed, pe->ySpeed)) + { + absSpeed = abs(pe->speed); + pe->dist -= absSpeed; + if(pe->dist <= 0) + { + poly = GetPolyobj(pe->polyobj); + if(poly->specialdata == pe) + { + poly->specialdata = NULL; + } + SN_StopSequence((mobj_t *)&poly->startSpot); + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pe->thinker); + } + if(pe->dist < absSpeed) + { + pe->speed = pe->dist*(pe->speed < 0 ? -1 : 1); + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + } + } +} + +//========================================================================== +// +// EV_MovePoly +// +//========================================================================== + +boolean EV_MovePoly(line_t *line, byte *args, boolean timesEight, boolean + overRide) +{ + int mirror; + int polyNum; + polyevent_t *pe; + polyobj_t *poly; + angle_t an; + + polyNum = args[0]; + if(poly = GetPolyobj(polyNum)) + { + if(poly->specialdata && !overRide) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_MovePoly: Invalid polyobj num: %d\n", polyNum); + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_MovePoly; + pe->polyobj = polyNum; + if(timesEight) + { + pe->dist = args[3]*8*FRACUNIT; + } + else + { + pe->dist = args[3]*FRACUNIT; // Distance + } + pe->speed = args[1]*(FRACUNIT/8); + poly->specialdata = pe; + + an = args[2]*(ANGLE_90/64); + + pe->angle = an>>ANGLETOFINESHIFT; + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + + while(mirror = GetPolyobjMirror(polyNum)) + { + poly = GetPolyobj(mirror); + if(poly && poly->specialdata && !overRide) + { // mirroring poly is already in motion + break; + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_MovePoly; + pe->polyobj = mirror; + poly->specialdata = pe; + if(timesEight) + { + pe->dist = args[3]*8*FRACUNIT; + } + else + { + pe->dist = args[3]*FRACUNIT; // Distance + } + pe->speed = args[1]*(FRACUNIT/8); + an = an+ANGLE_180; // reverse the angle + pe->angle = an>>ANGLETOFINESHIFT; + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + polyNum = mirror; + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + return true; +} + +//========================================================================== +// +// T_PolyDoor +// +//========================================================================== + +void T_PolyDoor(polydoor_t *pd) +{ + int absSpeed; + polyobj_t *poly; + + if(pd->tics) + { + if(!--pd->tics) + { + poly = GetPolyobj(pd->polyobj); + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + return; + } + switch(pd->type) + { + case PODOOR_SLIDE: + if(PO_MovePolyobj(pd->polyobj, pd->xSpeed, pd->ySpeed)) + { + absSpeed = abs(pd->speed); + pd->dist -= absSpeed; + if(pd->dist <= 0) + { + poly = GetPolyobj(pd->polyobj); + SN_StopSequence((mobj_t *)&poly->startSpot); + if(!pd->close) + { + pd->dist = pd->totalDist; + pd->close = true; + pd->tics = pd->waitTics; + pd->direction = (ANGLE_MAX>>ANGLETOFINESHIFT)- + pd->direction; + pd->xSpeed = -pd->xSpeed; + pd->ySpeed = -pd->ySpeed; + } + else + { + if(poly->specialdata == pd) + { + poly->specialdata = NULL; + } + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pd->thinker); + } + } + } + else + { + poly = GetPolyobj(pd->polyobj); + if(poly->crush || !pd->close) + { // continue moving if the poly is a crusher, or is opening + return; + } + else + { // open back up + pd->dist = pd->totalDist-pd->dist; + pd->direction = (ANGLE_MAX>>ANGLETOFINESHIFT)- + pd->direction; + pd->xSpeed = -pd->xSpeed; + pd->ySpeed = -pd->ySpeed; + pd->close = false; + SN_StartSequence((mobj_t *)&poly->startSpot, + SEQ_DOOR_STONE+poly->seqType); + } + } + break; + case PODOOR_SWING: + if(PO_RotatePolyobj(pd->polyobj, pd->speed)) + { + absSpeed = abs(pd->speed); + if(pd->dist == -1) + { // perpetual polyobj + return; + } + pd->dist -= absSpeed; + if(pd->dist <= 0) + { + poly = GetPolyobj(pd->polyobj); + SN_StopSequence((mobj_t *)&poly->startSpot); + if(!pd->close) + { + pd->dist = pd->totalDist; + pd->close = true; + pd->tics = pd->waitTics; + pd->speed = -pd->speed; + } + else + { + if(poly->specialdata == pd) + { + poly->specialdata = NULL; + } + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pd->thinker); + } + } + } + else + { + poly = GetPolyobj(pd->polyobj); + if(poly->crush || !pd->close) + { // continue moving if the poly is a crusher, or is opening + return; + } + else + { // open back up and rewait + pd->dist = pd->totalDist-pd->dist; + pd->speed = -pd->speed; + pd->close = false; + SN_StartSequence((mobj_t *)&poly->startSpot, + SEQ_DOOR_STONE+poly->seqType); + } + } + break; + default: + break; + } +} + +//========================================================================== +// +// EV_OpenPolyDoor +// +//========================================================================== + +boolean EV_OpenPolyDoor(line_t *line, byte *args, podoortype_t type) +{ + int mirror; + int polyNum; + polydoor_t *pd; + polyobj_t *poly; + angle_t an; + + polyNum = args[0]; + if(poly = GetPolyobj(polyNum)) + { + if(poly->specialdata) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_OpenPolyDoor: Invalid polyobj num: %d\n", polyNum); + } + pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); + memset(pd, 0, sizeof(polydoor_t)); + P_AddThinker(&pd->thinker); + pd->thinker.function = T_PolyDoor; + pd->type = type; + pd->polyobj = polyNum; + if(type == PODOOR_SLIDE) + { + pd->waitTics = args[4]; + pd->speed = args[1]*(FRACUNIT/8); + pd->totalDist = args[3]*FRACUNIT; // Distance + pd->dist = pd->totalDist; + an = args[2]*(ANGLE_90/64); + pd->direction = an>>ANGLETOFINESHIFT; + pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); + pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + else if(type == PODOOR_SWING) + { + pd->waitTics = args[3]; + pd->direction = 1; // ADD: PODOOR_SWINGL, PODOOR_SWINGR + pd->speed = (args[1]*pd->direction*(ANGLE_90/64))>>3; + pd->totalDist = args[2]*(ANGLE_90/64); + pd->dist = pd->totalDist; + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + + poly->specialdata = pd; + + while(mirror = GetPolyobjMirror(polyNum)) + { + poly = GetPolyobj(mirror); + if(poly && poly->specialdata) + { // mirroring poly is already in motion + break; + } + pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); + memset(pd, 0, sizeof(polydoor_t)); + P_AddThinker(&pd->thinker); + pd->thinker.function = T_PolyDoor; + pd->polyobj = mirror; + pd->type = type; + poly->specialdata = pd; + if(type == PODOOR_SLIDE) + { + pd->waitTics = args[4]; + pd->speed = args[1]*(FRACUNIT/8); + pd->totalDist = args[3]*FRACUNIT; // Distance + pd->dist = pd->totalDist; + an = an+ANGLE_180; // reverse the angle + pd->direction = an>>ANGLETOFINESHIFT; + pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); + pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + else if(type == PODOOR_SWING) + { + pd->waitTics = args[3]; + pd->direction = -1; // ADD: same as above + pd->speed = (args[1]*pd->direction*(ANGLE_90/64))>>3; + pd->totalDist = args[2]*(ANGLE_90/64); + pd->dist = pd->totalDist; + SN_StartSequence((mobj_t *)&poly->startSpot, SEQ_DOOR_STONE+ + poly->seqType); + } + polyNum = mirror; + } + return true; +} + +// ===== Higher Level Poly Interface code ===== + +//========================================================================== +// +// GetPolyobj +// +//========================================================================== + +static polyobj_t *GetPolyobj(int polyNum) +{ + int i; + + for(i = 0; i < po_NumPolyobjs; i++) + { + if(polyobjs[i].tag == polyNum) + { + return &polyobjs[i]; + } + } + return NULL; +} + +//========================================================================== +// +// GetPolyobjMirror +// +//========================================================================== + +static int GetPolyobjMirror(int poly) +{ + int i; + + for(i = 0; i < po_NumPolyobjs; i++) + { + if(polyobjs[i].tag == poly) + { + return((*polyobjs[i].segs)->linedef->arg2); + } + } + return 0; +} + +//========================================================================== +// +// ThrustMobj +// +//========================================================================== + +static void ThrustMobj(mobj_t *mobj, seg_t *seg, polyobj_t *po) +{ + int thrustAngle; + int thrustX; + int thrustY; + polyevent_t *pe; + + int force; + + if(!(mobj->flags&MF_SHOOTABLE) && !mobj->player) + { + return; + } + thrustAngle = (seg->angle-ANGLE_90)>>ANGLETOFINESHIFT; + + pe = po->specialdata; + if(pe) + { + if(pe->thinker.function == T_RotatePoly) + { + force = pe->speed>>8; + } + else + { + force = pe->speed>>3; + } + if(force < FRACUNIT) + { + force = FRACUNIT; + } + else if(force > 4*FRACUNIT) + { + force = 4*FRACUNIT; + } + } + else + { + force = FRACUNIT; + } + + thrustX = FixedMul(force, finecosine[thrustAngle]); + thrustY = FixedMul(force, finesine[thrustAngle]); + mobj->momx += thrustX; + mobj->momy += thrustY; + if(po->crush) + { + if(!P_CheckPosition(mobj, mobj->x+thrustX, mobj->y+thrustY)) + { + P_DamageMobj(mobj, NULL, NULL, 3); + } + } +} + +//========================================================================== +// +// UpdateSegBBox +// +//========================================================================== + +static void UpdateSegBBox(seg_t *seg) +{ + line_t *line; + + line = seg->linedef; + + if(seg->v1->x < seg->v2->x) + { + line->bbox[BOXLEFT] = seg->v1->x; + line->bbox[BOXRIGHT] = seg->v2->x; + } + else + { + line->bbox[BOXLEFT] = seg->v2->x; + line->bbox[BOXRIGHT] = seg->v1->x; + } + if(seg->v1->y < seg->v2->y) + { + line->bbox[BOXBOTTOM] = seg->v1->y; + line->bbox[BOXTOP] = seg->v2->y; + } + else + { + line->bbox[BOXBOTTOM] = seg->v2->y; + line->bbox[BOXTOP] = seg->v1->y; + } + + // Update the line's slopetype + line->dx = line->v2->x - line->v1->x; + line->dy = line->v2->y - line->v1->y; + if(!line->dx) + { + line->slopetype = ST_VERTICAL; + } + else if(!line->dy) + { + line->slopetype = ST_HORIZONTAL; + } + else + { + if(FixedDiv(line->dy, line->dx) > 0) + { + line->slopetype = ST_POSITIVE; + } + else + { + line->slopetype = ST_NEGATIVE; + } + } +} + +//========================================================================== +// +// PO_MovePolyobj +// +//========================================================================== + +boolean PO_MovePolyobj(int num, int x, int y) +{ + int count; + seg_t **segList; + seg_t **veryTempSeg; + polyobj_t *po; + vertex_t *prevPts; + boolean blocked; + + if(!(po = GetPolyobj(num))) + { + I_Error("PO_MovePolyobj: Invalid polyobj number: %d\n", num); + } + + UnLinkPolyobj(po); + + segList = po->segs; + prevPts = po->prevPts; + blocked = false; + + validcount++; + for(count = po->numsegs; count; count--, segList++, prevPts++) + { + if((*segList)->linedef->validcount != validcount) + { + (*segList)->linedef->bbox[BOXTOP] += y; + (*segList)->linedef->bbox[BOXBOTTOM] += y; + (*segList)->linedef->bbox[BOXLEFT] += x; + (*segList)->linedef->bbox[BOXRIGHT] += x; + (*segList)->linedef->validcount = validcount; + } + for(veryTempSeg = po->segs; veryTempSeg != segList; + veryTempSeg++) + { + if((*veryTempSeg)->v1 == (*segList)->v1) + { + break; + } + } + if(veryTempSeg == segList) + { + (*segList)->v1->x += x; + (*segList)->v1->y += y; + } + (*prevPts).x += x; // previous points are unique for each seg + (*prevPts).y += y; + } + segList = po->segs; + for(count = po->numsegs; count; count--, segList++) + { + if(CheckMobjBlocking(*segList, po)) + { + blocked = true; + } + } + if(blocked) + { + count = po->numsegs; + segList = po->segs; + prevPts = po->prevPts; + validcount++; + while(count--) + { + if((*segList)->linedef->validcount != validcount) + { + (*segList)->linedef->bbox[BOXTOP] -= y; + (*segList)->linedef->bbox[BOXBOTTOM] -= y; + (*segList)->linedef->bbox[BOXLEFT] -= x; + (*segList)->linedef->bbox[BOXRIGHT] -= x; + (*segList)->linedef->validcount = validcount; + } + for(veryTempSeg = po->segs; veryTempSeg != segList; + veryTempSeg++) + { + if((*veryTempSeg)->v1 == (*segList)->v1) + { + break; + } + } + if(veryTempSeg == segList) + { + (*segList)->v1->x -= x; + (*segList)->v1->y -= y; + } + (*prevPts).x -= x; + (*prevPts).y -= y; + segList++; + prevPts++; + } + LinkPolyobj(po); + return false; + } + po->startSpot.x += x; + po->startSpot.y += y; + LinkPolyobj(po); + return true; +} + +//========================================================================== +// +// RotatePt +// +//========================================================================== + +static void RotatePt(int an, fixed_t *x, fixed_t *y, fixed_t startSpotX, fixed_t startSpotY) +{ + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = *x; + try = *y; + + gxt = FixedMul(trx, finecosine[an]); + gyt = FixedMul(try, finesine[an]); + *x = (gxt-gyt)+startSpotX; + + gxt = FixedMul(trx, finesine[an]); + gyt = FixedMul(try, finecosine[an]); + *y = (gyt+gxt)+startSpotY; +} + +//========================================================================== +// +// PO_RotatePolyobj +// +//========================================================================== + +boolean PO_RotatePolyobj(int num, angle_t angle) +{ + int count; + seg_t **segList; + vertex_t *originalPts; + vertex_t *prevPts; + int an; + polyobj_t *po; + boolean blocked; + + if(!(po = GetPolyobj(num))) + { + I_Error("PO_RotatePolyobj: Invalid polyobj number: %d\n", num); + } + an = (po->angle+angle)>>ANGLETOFINESHIFT; + + UnLinkPolyobj(po); + + segList = po->segs; + originalPts = po->originalPts; + prevPts = po->prevPts; + + for(count = po->numsegs; count; count--, segList++, originalPts++, + prevPts++) + { + prevPts->x = (*segList)->v1->x; + prevPts->y = (*segList)->v1->y; + (*segList)->v1->x = originalPts->x; + (*segList)->v1->y = originalPts->y; + RotatePt(an, &(*segList)->v1->x, &(*segList)->v1->y, po->startSpot.x, + po->startSpot.y); + } + segList = po->segs; + blocked = false; + validcount++; + for(count = po->numsegs; count; count--, segList++) + { + if(CheckMobjBlocking(*segList, po)) + { + blocked = true; + } + if((*segList)->linedef->validcount != validcount) + { + UpdateSegBBox(*segList); + (*segList)->linedef->validcount = validcount; + } + (*segList)->angle += angle; + } + if(blocked) + { + segList = po->segs; + prevPts = po->prevPts; + for(count = po->numsegs; count; count--, segList++, prevPts++) + { + (*segList)->v1->x = prevPts->x; + (*segList)->v1->y = prevPts->y; + } + segList = po->segs; + validcount++; + for(count = po->numsegs; count; count--, segList++, prevPts++) + { + if((*segList)->linedef->validcount != validcount) + { + UpdateSegBBox(*segList); + (*segList)->linedef->validcount = validcount; + } + (*segList)->angle -= angle; + } + LinkPolyobj(po); + return false; + } + po->angle += angle; + LinkPolyobj(po); + return true; +} + +//========================================================================== +// +// UnLinkPolyobj +// +//========================================================================== + +static void UnLinkPolyobj(polyobj_t *po) +{ + polyblock_t *link; + int i, j; + int index; + + // remove the polyobj from each blockmap section + for(j = po->bbox[BOXBOTTOM]; j <= po->bbox[BOXTOP]; j++) + { + index = j*bmapwidth; + for(i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) + { + if(i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight) + { + link = PolyBlockMap[index+i]; + while(link != NULL && link->polyobj != po) + { + link = link->next; + } + if(link == NULL) + { // polyobj not located in the link cell + continue; + } + link->polyobj = NULL; + } + } + } +} + +//========================================================================== +// +// LinkPolyobj +// +//========================================================================== + +static void LinkPolyobj(polyobj_t *po) +{ + int leftX, rightX; + int topY, bottomY; + seg_t **tempSeg; + polyblock_t **link; + polyblock_t *tempLink; + int i, j; + + // calculate the polyobj bbox + tempSeg = po->segs; + rightX = leftX = (*tempSeg)->v1->x; + topY = bottomY = (*tempSeg)->v1->y; + + for(i = 0; i < po->numsegs; i++, tempSeg++) + { + if((*tempSeg)->v1->x > rightX) + { + rightX = (*tempSeg)->v1->x; + } + if((*tempSeg)->v1->x < leftX) + { + leftX = (*tempSeg)->v1->x; + } + if((*tempSeg)->v1->y > topY) + { + topY = (*tempSeg)->v1->y; + } + if((*tempSeg)->v1->y < bottomY) + { + bottomY = (*tempSeg)->v1->y; + } + } + po->bbox[BOXRIGHT] = (rightX-bmaporgx)>>MAPBLOCKSHIFT; + po->bbox[BOXLEFT] = (leftX-bmaporgx)>>MAPBLOCKSHIFT; + po->bbox[BOXTOP] = (topY-bmaporgy)>>MAPBLOCKSHIFT; + po->bbox[BOXBOTTOM] = (bottomY-bmaporgy)>>MAPBLOCKSHIFT; + // add the polyobj to each blockmap section + for(j = po->bbox[BOXBOTTOM]*bmapwidth; j <= po->bbox[BOXTOP]*bmapwidth; + j += bmapwidth) + { + for(i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) + { + if(i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight*bmapwidth) + { + link = &PolyBlockMap[j+i]; + if(!(*link)) + { // Create a new link at the current block cell + *link = Z_Malloc(sizeof(polyblock_t), PU_LEVEL, 0); + (*link)->next = NULL; + (*link)->prev = NULL; + (*link)->polyobj = po; + continue; + } + else + { + tempLink = *link; + while(tempLink->next != NULL && tempLink->polyobj != NULL) + { + tempLink = tempLink->next; + } + } + if(tempLink->polyobj == NULL) + { + tempLink->polyobj = po; + continue; + } + else + { + tempLink->next = Z_Malloc(sizeof(polyblock_t), + PU_LEVEL, 0); + tempLink->next->next = NULL; + tempLink->next->prev = tempLink; + tempLink->next->polyobj = po; + } + } + // else, don't link the polyobj, since it's off the map + } + } +} + +//========================================================================== +// +// CheckMobjBlocking +// +//========================================================================== + +static boolean CheckMobjBlocking(seg_t *seg, polyobj_t *po) +{ + mobj_t *mobj; + int i, j; + int left, right, top, bottom; + int tmbbox[4]; + line_t *ld; + boolean blocked; + + ld = seg->linedef; + + top = (ld->bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; + bottom = (ld->bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; + left = (ld->bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; + right = (ld->bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; + + blocked = false; + + bottom = bottom < 0 ? 0 : bottom; + bottom = bottom >= bmapheight ? bmapheight-1 : bottom; + top = top < 0 ? 0 : top; + top = top >= bmapheight ? bmapheight-1 : top; + left = left < 0 ? 0 : left; + left = left >= bmapwidth ? bmapwidth-1 : left; + right = right < 0 ? 0 : right; + right = right >= bmapwidth ? bmapwidth-1 : right; + + for(j = bottom*bmapwidth; j <= top*bmapwidth; j += bmapwidth) + { + for(i = left; i <= right; i++) + { + for(mobj = blocklinks[j+i]; mobj; mobj = mobj->bnext) + { + if(mobj->flags&MF_SOLID || mobj->player) + { + tmbbox[BOXTOP] = mobj->y+mobj->radius; + tmbbox[BOXBOTTOM] = mobj->y-mobj->radius; + tmbbox[BOXLEFT] = mobj->x-mobj->radius; + tmbbox[BOXRIGHT] = mobj->x+mobj->radius; + + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + { + continue; + } + if(P_BoxOnLineSide(tmbbox, ld) != -1) + { + continue; + } + ThrustMobj(mobj, seg, po); + blocked = true; + } + } + } + } + return blocked; +} + +//========================================================================== +// +// InitBlockMap +// +//========================================================================== + +static void InitBlockMap(void) +{ + int i; + + int j; + seg_t **segList; + int area; + int leftX, rightX; + int topY, bottomY; + + PolyBlockMap = Z_Malloc(bmapwidth*bmapheight*sizeof(polyblock_t *), + PU_LEVEL, 0); + memset(PolyBlockMap, 0, bmapwidth*bmapheight*sizeof(polyblock_t *)); + + for(i = 0; i < po_NumPolyobjs; i++) + { + LinkPolyobj(&polyobjs[i]); + + // calculate a rough area + // right now, working like shit...gotta fix this... + segList = polyobjs[i].segs; + leftX = rightX = (*segList)->v1->x; + topY = bottomY = (*segList)->v1->y; + for(j = 0; j < polyobjs[i].numsegs; j++, segList++) + { + if((*segList)->v1->x < leftX) + { + leftX = (*segList)->v1->x; + } + if((*segList)->v1->x > rightX) + { + rightX = (*segList)->v1->x; + } + if((*segList)->v1->y < bottomY) + { + bottomY = (*segList)->v1->y; + } + if((*segList)->v1->y > topY) + { + topY = (*segList)->v1->y; + } + } + area = ((rightX>>FRACBITS)-(leftX>>FRACBITS))* + ((topY>>FRACBITS)-(bottomY>>FRACBITS)); + +// fprintf(stdaux, "Area of Polyobj[%d]: %d\n", polyobjs[i].tag, area); +// fprintf(stdaux, "\t[%d]\n[%d]\t\t[%d]\n\t[%d]\n", topY>>FRACBITS, +// leftX>>FRACBITS, +// rightX>>FRACBITS, bottomY>>FRACBITS); + } +} + +//========================================================================== +// +// IterFindPolySegs +// +// Passing NULL for segList will cause IterFindPolySegs to +// count the number of segs in the polyobj +//========================================================================== + +static void IterFindPolySegs(int x, int y, seg_t **segList) +{ + int i; + + if(x == PolyStartX && y == PolyStartY) + { + return; + } + for(i = 0; i < numsegs; i++) + { + if(segs[i].v1->x == x && segs[i].v1->y == y) + { + if(!segList) + { + PolySegCount++; + } + else + { + *segList++ = &segs[i]; + } + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, segList); + return; + } + } + I_Error("IterFindPolySegs: Non-closed Polyobj located.\n"); +} + + +//========================================================================== +// +// SpawnPolyobj +// +//========================================================================== + +static void SpawnPolyobj(int index, int tag, boolean crush) +{ + int i; + int j; + int psIndex; + int psIndexOld; + seg_t *polySegList[PO_MAXPOLYSEGS]; + + for(i = 0; i < numsegs; i++) + { + if(segs[i].linedef->special == PO_LINE_START && + segs[i].linedef->arg1 == tag) + { + if(polyobjs[index].segs) + { + I_Error("SpawnPolyobj: Polyobj %d already spawned.\n", tag); + } + segs[i].linedef->special = 0; + segs[i].linedef->arg1 = 0; + PolySegCount = 1; + PolyStartX = segs[i].v1->x; + PolyStartY = segs[i].v1->y; + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, NULL); + + polyobjs[index].numsegs = PolySegCount; + polyobjs[index].segs = Z_Malloc(PolySegCount*sizeof(seg_t *), + PU_LEVEL, 0); + *(polyobjs[index].segs) = &segs[i]; // insert the first seg + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, + polyobjs[index].segs+1); + polyobjs[index].crush = crush; + polyobjs[index].tag = tag; + polyobjs[index].seqType = segs[i].linedef->arg3; + if(polyobjs[index].seqType < 0 + || polyobjs[index].seqType >= SEQTYPE_NUMSEQ) + { + polyobjs[index].seqType = 0; + } + break; + } + } + if(!polyobjs[index].segs) + { // didn't find a polyobj through PO_LINE_START + psIndex = 0; + polyobjs[index].numsegs = 0; + for(j = 1; j < PO_MAXPOLYSEGS; j++) + { + psIndexOld = psIndex; + for (i = 0; i < numsegs; i++) + { + if(segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag) + { + if(!segs[i].linedef->arg2) + { + I_Error("SpawnPolyobj: Explicit line missing order number (probably %d) in poly %d.\n", + j+1, tag); + } + if(segs[i].linedef->arg2 == j) + { + polySegList[psIndex] = &segs[i]; + polyobjs[index].numsegs++; + psIndex++; + if(psIndex > PO_MAXPOLYSEGS) + { + I_Error("SpawnPolyobj: psIndex > PO_MAXPOLYSEGS\n"); + } + } + } + } + // Clear out any specials for these segs...we cannot clear them out + // in the above loop, since we aren't guaranteed one seg per + // linedef. + for(i = 0; i < numsegs; i++) + { + if(segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag && segs[i].linedef->arg2 == j) + { + segs[i].linedef->special = 0; + segs[i].linedef->arg1 = 0; + } + } + if(psIndex == psIndexOld) + { // Check if an explicit line order has been skipped + // A line has been skipped if there are any more explicit + // lines with the current tag value + for(i = 0; i < numsegs; i++) + { + if(segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag) + { + I_Error("SpawnPolyobj: Missing explicit line %d for poly %d\n", + j, tag); + } + } + } + } + if(polyobjs[index].numsegs) + { + PolySegCount = polyobjs[index].numsegs; // PolySegCount used globally + polyobjs[index].crush = crush; + polyobjs[index].tag = tag; + polyobjs[index].segs = Z_Malloc(polyobjs[index].numsegs + *sizeof(seg_t *), PU_LEVEL, 0); + for(i = 0; i < polyobjs[index].numsegs; i++) + { + polyobjs[index].segs[i] = polySegList[i]; + } + polyobjs[index].seqType = (*polyobjs[index].segs)->linedef->arg4; + } + // Next, change the polyobjs first line to point to a mirror + // if it exists + (*polyobjs[index].segs)->linedef->arg2 = + (*polyobjs[index].segs)->linedef->arg3; + } +} + +//========================================================================== +// +// TranslateToStartSpot +// +//========================================================================== + +static void TranslateToStartSpot(int tag, int originX, int originY) +{ + seg_t **tempSeg; + seg_t **veryTempSeg; + vertex_t *tempPt; + subsector_t *sub; + polyobj_t *po; + int deltaX; + int deltaY; + vertex_t avg; // used to find a polyobj's center, and hence subsector + int i; + + po = NULL; + for(i = 0; i < po_NumPolyobjs; i++) + { + if(polyobjs[i].tag == tag) + { + po = &polyobjs[i]; + break; + } + } + if(!po) + { // didn't match the tag with a polyobj tag + I_Error("TranslateToStartSpot: Unable to match polyobj tag: %d\n", + tag); + } + if(po->segs == NULL) + { + I_Error("TranslateToStartSpot: Anchor point located without a StartSpot point: %d\n", tag); + } + po->originalPts = Z_Malloc(po->numsegs*sizeof(vertex_t), PU_LEVEL, 0); + po->prevPts = Z_Malloc(po->numsegs*sizeof(vertex_t), PU_LEVEL, 0); + deltaX = originX-po->startSpot.x; + deltaY = originY-po->startSpot.y; + + tempSeg = po->segs; + tempPt = po->originalPts; + avg.x = 0; + avg.y = 0; + + validcount++; + for(i = 0; i < po->numsegs; i++, tempSeg++, tempPt++) + { + if((*tempSeg)->linedef->validcount != validcount) + { + (*tempSeg)->linedef->bbox[BOXTOP] -= deltaY; + (*tempSeg)->linedef->bbox[BOXBOTTOM] -= deltaY; + (*tempSeg)->linedef->bbox[BOXLEFT] -= deltaX; + (*tempSeg)->linedef->bbox[BOXRIGHT] -= deltaX; + (*tempSeg)->linedef->validcount = validcount; + } + for(veryTempSeg = po->segs; veryTempSeg != tempSeg; veryTempSeg++) + { + if((*veryTempSeg)->v1 == (*tempSeg)->v1) + { + break; + } + } + if(veryTempSeg == tempSeg) + { // the point hasn't been translated, yet + (*tempSeg)->v1->x -= deltaX; + (*tempSeg)->v1->y -= deltaY; + } + avg.x += (*tempSeg)->v1->x>>FRACBITS; + avg.y += (*tempSeg)->v1->y>>FRACBITS; + // the original Pts are based off the startSpot Pt, and are + // unique to each seg, not each linedef + tempPt->x = (*tempSeg)->v1->x-po->startSpot.x; + tempPt->y = (*tempSeg)->v1->y-po->startSpot.y; + } + avg.x /= po->numsegs; + avg.y /= po->numsegs; + sub = R_PointInSubsector(avg.x<poly != NULL) + { + I_Error("PO_TranslateToStartSpot: Multiple polyobjs in a single subsector.\n"); + } + sub->poly = po; +} + +//========================================================================== +// +// PO_Init +// +//========================================================================== + +void PO_Init(int lump) +{ + byte *data; + int i; + mapthing_t *mt; + int numthings; + int polyIndex; + + polyobjs = Z_Malloc(po_NumPolyobjs*sizeof(polyobj_t), PU_LEVEL, 0); + memset(polyobjs, 0, po_NumPolyobjs*sizeof(polyobj_t)); + + data = W_CacheLumpNum(lump, PU_STATIC); + numthings = W_LumpLength(lump)/sizeof(mapthing_t); + mt = (mapthing_t *)data; + polyIndex = 0; // index polyobj number + // Find the startSpot points, and spawn each polyobj + for (i = 0; i < numthings; i++, mt++) + { + mt->x = SHORT(mt->x); + mt->y = SHORT(mt->y); + mt->angle = SHORT(mt->angle); + mt->type = SHORT(mt->type); + + // 3001 = no crush, 3002 = crushing + if(mt->type == PO_SPAWN_TYPE || mt->type == PO_SPAWNCRUSH_TYPE) + { // Polyobj StartSpot Pt. + polyobjs[polyIndex].startSpot.x = mt->x<y<angle, (mt->type == PO_SPAWNCRUSH_TYPE)); + polyIndex++; + } + } + mt = (mapthing_t *)data; + for (i = 0; i < numthings; i++, mt++) + { + mt->x = SHORT(mt->x); + mt->y = SHORT(mt->y); + mt->angle = SHORT(mt->angle); + mt->type = SHORT(mt->type); + if(mt->type == PO_ANCHOR_TYPE) + { // Polyobj Anchor Pt. + TranslateToStartSpot(mt->angle, mt->x<y<specialdata) + { + return false; + } + else + { + return true; + } +} diff --git a/src/hexen/r_bsp.c b/src/hexen/r_bsp.c new file mode 100644 index 00000000..728bb2d7 --- /dev/null +++ b/src/hexen/r_bsp.c @@ -0,0 +1,504 @@ + +//************************************************************************** +//** +//** r_bsp.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_bsp.c,v $ +//** $Revision: 1.2 $ +//** $Date: 95/07/13 15:17:10 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" +#include "r_local.h" + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector, *backsector; + +drawseg_t drawsegs[MAXDRAWSEGS], *ds_p; + +void R_StoreWallRange (int start, int stop); + +/* +==================== += += R_ClearDrawSegs += +==================== +*/ + +void R_ClearDrawSegs (void) +{ + ds_p = drawsegs; +} + +//============================================================================= + + +/* +=============================================================================== += += ClipWallSegment += += Clips the given range of columns and includes it in the new clip list +=============================================================================== +*/ + +typedef struct +{ + int first, last; +} cliprange_t; + +#define MAXSEGS 32 + +cliprange_t solidsegs[MAXSEGS], *newend; // newend is one past the last valid seg + + +void R_ClipSolidWallSegment (int first, int last) +{ + cliprange_t *next, *start; + +// find the first range that touches the range (adjacent pixels are touching) + start = solidsegs; + while (start->last < first-1) + start++; + + if (first < start->first) + { + if (last < start->first-1) + { // post is entirely visible (above start), so insert a new clippost + R_StoreWallRange (first, last); + next = newend; + newend++; + while (next != start) + { + *next = *(next-1); + next--; + } + next->first = first; + next->last = last; + return; + } + + // there is a fragment above *start + R_StoreWallRange (first, start->first - 1); + start->first = first; // adjust the clip size + } + + if (last <= start->last) + return; // bottom contained in start + + next = start; + while (last >= (next+1)->first-1) + { + // there is a fragment between two posts + R_StoreWallRange (next->last + 1, (next+1)->first - 1); + next++; + if (last <= next->last) + { // bottom is contained in next + start->last = next->last; // adjust the clip size + goto crunch; + } + } + + // there is a fragment after *next + R_StoreWallRange (next->last + 1, last); + start->last = last; // adjust the clip size + + +// remove start+1 to next from the clip list, +// because start now covers their area +crunch: + if (next == start) + return; // post just extended past the bottom of one post + + while (next++ != newend) // remove a post + *++start = *next; + newend = start+1; +} + +/* +=============================================================================== += += R_ClipPassWallSegment += += Clips the given range of columns, but does not includes it in the clip list +=============================================================================== +*/ + +void R_ClipPassWallSegment (int first, int last) +{ + cliprange_t *start; + +// find the first range that touches the range (adjacent pixels are touching) + start = solidsegs; + while (start->last < first-1) + start++; + + if (first < start->first) + { + if (last < start->first-1) + { // post is entirely visible (above start) + R_StoreWallRange (first, last); + return; + } + + // there is a fragment above *start + R_StoreWallRange (first, start->first - 1); + } + + if (last <= start->last) + return; // bottom contained in start + + while (last >= (start+1)->first-1) + { + // there is a fragment between two posts + R_StoreWallRange (start->last + 1, (start+1)->first - 1); + start++; + if (last <= start->last) + return; + } + + // there is a fragment after *next + R_StoreWallRange (start->last + 1, last); +} + + + +/* +==================== += += R_ClearClipSegs += +==================== +*/ + +void R_ClearClipSegs (void) +{ + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = viewwidth; + solidsegs[1].last = 0x7fffffff; + newend = solidsegs+2; +} + + +//============================================================================= + +/* +====================== += += R_AddLine += += Clips the given segment and adds any visible pieces to the line list += +====================== +*/ + +void R_AddLine (seg_t *line) +{ + int x1, x2; + angle_t angle1, angle2, span, tspan; + +#ifdef __NeXT__ + RD_DrawLineCheck (line); +#endif + curline = line; + +// OPTIMIZE: quickly reject orthogonal back sides + + angle1 = R_PointToAngle (line->v1->x, line->v1->y); + angle2 = R_PointToAngle (line->v2->x, line->v2->y); + +// +// clip to view edges +// OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW) + span = angle1 - angle2; + if (span >= ANG180) + return; // back side + + rw_angle1 = angle1; // global angle needed by segcalc + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + if (tspan >= span) + return; // totally off the left edge + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + if (tspan >= span) + return; // totally off the left edge + angle2 = -clipangle; + } + +// +// the seg is in the view range, but not necessarily visible +// + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + if (x1 == x2) + return; // does not cross a pixel + + backsector = line->backsector; + + if (!backsector) + goto clipsolid; // single sided line + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + goto clipsolid; // closed door + + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight) + goto clippass; // window + +// reject empty lines used for triggers and special events + if (backsector->ceilingpic == frontsector->ceilingpic + && backsector->floorpic == frontsector->floorpic + && backsector->lightlevel == frontsector->lightlevel + && backsector->special == frontsector->special + && curline->sidedef->midtexture == 0) + return; + +clippass: + R_ClipPassWallSegment (x1, x2-1); + return; + +clipsolid: + R_ClipSolidWallSegment (x1, x2-1); +} + +//============================================================================ + + +/* +=============================================================================== += += R_CheckBBox += += Returns true if some part of the bbox might be visible += +=============================================================================== +*/ + +int checkcoord[12][4] = { +{3,0, 2,1}, +{3,0, 2,0}, +{3,1, 2,0}, +{0}, +{2,0, 2,1}, +{0,0,0,0}, +{3,1, 3,0}, +{0}, +{2,0, 3,1}, +{2,1, 3,1}, +{2,1, 3,0} }; + + +boolean R_CheckBBox (fixed_t *bspcoord) +{ + int boxx, boxy, boxpos; + fixed_t x1, y1, x2, y2; + angle_t angle1, angle2, span, tspan; + cliprange_t *start; + int sx1, sx2; + +#ifdef __NeXT__ + RD_DrawBBox (bspcoord); +#endif + +// find the corners of the box that define the edges from current viewpoint + if (viewx <= bspcoord[BOXLEFT]) + boxx = 0; + else if (viewx < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (viewy >= bspcoord[BOXTOP]) + boxy = 0; + else if (viewy > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy<<2)+boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + +#ifdef __NeXT__ +// RD_DisplayLine (x1, y1, x2, y2, 0.1); +#endif + +// +// check clip list for an open space +// + angle1 = R_PointToAngle (x1, y1) - viewangle; + angle2 = R_PointToAngle (x2, y2) - viewangle; + + span = angle1 - angle2; + if (span >= ANG180) + return true; // sitting on a line + tspan = angle1 + clipangle; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + if (tspan >= span) + return false; // totally off the left edge + angle1 = clipangle; + } + tspan = clipangle - angle2; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + if (tspan >= span) + return false; // totally off the left edge + angle2 = -clipangle; + } + + +// find the first clippost that touches the source post (adjacent pixels are touching) + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + sx1 = viewangletox[angle1]; + sx2 = viewangletox[angle2]; + if (sx1 == sx2) + return false; // does not cross a pixel + sx2--; + + start = solidsegs; + while (start->last < sx2) + start++; + if (sx1 >= start->first && sx2 <= start->last) + return false; // the clippost contains the new span + + return true; +} + + +/* +================ += += R_Subsector += += Draw one or more segments +================ +*/ + +void R_Subsector (int num) +{ + int count; + seg_t *line; + subsector_t *sub; + int polyCount; + seg_t **polySeg; + +#ifdef RANGECHECK + if (num>=numsubsectors) + I_Error ("R_Subsector: ss %i with numss = %i",num, numsubsectors); +#endif + + sscount++; + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + if(frontsector->floorheight < viewz) + { + floorplane = R_FindPlane(frontsector->floorheight, + frontsector->floorpic, frontsector->lightlevel, + frontsector->special); + } + else + { + floorplane = NULL; + } + + if(frontsector->ceilingheight > viewz + || frontsector->ceilingpic == skyflatnum) + { + ceilingplane = R_FindPlane(frontsector->ceilingheight, + frontsector->ceilingpic, frontsector->lightlevel, 0); + } + else + { + ceilingplane = NULL; + } + + R_AddSprites(frontsector); + if(sub->poly) + { // Render the polyobj in the subsector first + polyCount = sub->poly->numsegs; + polySeg = sub->poly->segs; + while(polyCount--) + { + R_AddLine(*polySeg++); + } + } + while (count--) + { + R_AddLine (line); + line++; + } +} + + +/* +=============================================================================== += += RenderBSPNode += +=============================================================================== +*/ + +void R_RenderBSPNode (int bspnum) +{ + node_t *bsp; + int side; + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + R_Subsector (0); + else + R_Subsector (bspnum&(~NF_SUBSECTOR)); + return; + } + + bsp = &nodes[bspnum]; + +#ifdef __NeXT__ + RD_DrawNodeLine (bsp); +#endif + +// +// decide which side the view point is on +// + side = R_PointOnSide (viewx, viewy, bsp); + + R_RenderBSPNode (bsp->children[side]); // recursively divide front space + + if (R_CheckBBox (bsp->bbox[side^1])) // possibly divide back space + R_RenderBSPNode (bsp->children[side^1]); +} + + diff --git a/src/hexen/r_data.c b/src/hexen/r_data.c new file mode 100644 index 00000000..68727d9f --- /dev/null +++ b/src/hexen/r_data.c @@ -0,0 +1,676 @@ + +//************************************************************************** +//** +//** r_data.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_data.c,v $ +//** $Revision: 1.8 $ +//** $Date: 96/03/12 12:04:04 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "r_local.h" +#include "p_local.h" + +typedef struct +{ + int originx; // block origin (allways UL), which has allready + int originy; // accounted for the patch's internal origin + int patch; +} texpatch_t; + +// a maptexturedef_t describes a rectangular texture, which is composed of one +// or more mappatch_t structures that arrange graphic patches +typedef struct +{ + char name[8]; // for switch changing, etc + short width; + short height; + short patchcount; + texpatch_t patches[1]; // [patchcount] drawn back to front + // into the cached texture +} texture_t; + + + +int firstflat, lastflat, numflats; +int firstpatch, lastpatch, numpatches; +int firstspritelump, lastspritelump, numspritelumps; + +int numtextures; +texture_t **textures; +int *texturewidthmask; +fixed_t *textureheight; // needed for texture pegging +int *texturecompositesize; +short **texturecolumnlump; +unsigned short **texturecolumnofs; +byte **texturecomposite; + +int *flattranslation; // for global animation +int *texturetranslation; // for global animation + +fixed_t *spritewidth; // needed for pre rendering +fixed_t *spriteoffset; +fixed_t *spritetopoffset; + +lighttable_t *colormaps; + + +/* +============================================================================== + + MAPTEXTURE_T CACHING + +when a texture is first needed, it counts the number of composite columns +required in the texture and allocates space for a column directory and any +new columns. The directory will simply point inside other patches if there +is only one patch in a given column, but any columns with multiple patches +will have new column_ts generated. + +============================================================================== +*/ + +/* +=================== += += R_DrawColumnInCache += += Clip and draw a column from a patch into a cached post += +=================== +*/ + +void R_DrawColumnInCache (column_t *patch, byte *cache, int originy, int cacheheight) +{ + int count, position; + byte *source, *dest; + + dest = (byte *)cache + 3; + + while (patch->topdelta != 0xff) + { + source = (byte *)patch + 3; + count = patch->length; + position = originy + patch->topdelta; + if (position < 0) + { + count += position; + position = 0; + } + if (position + count > cacheheight) + count = cacheheight - position; + if (count > 0) + memcpy (cache + position, source, count); + + patch = (column_t *)( (byte *)patch + patch->length + 4); + } +} + + +/* +=================== += += R_GenerateComposite += +=================== +*/ + +void R_GenerateComposite (int texnum) +{ + byte *block; + texture_t *texture; + texpatch_t *patch; + patch_t *realpatch; + int x, x1, x2; + int i; + column_t *patchcol; + short *collump; + unsigned short *colofs; + + texture = textures[texnum]; + block = Z_Malloc (texturecompositesize[texnum], PU_STATIC, + &texturecomposite[texnum]); + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + +// +// composite the columns together +// + patch = texture->patches; + + for (i=0 , patch = texture->patches; ipatchcount ; i++, patch++) + { + realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1<0) + x = 0; + else + x = x1; + if (x2 > texture->width) + x2 = texture->width; + + for ( ; x= 0) + continue; // column does not have multiple patches + patchcol = (column_t *)((byte *)realpatch + + LONG(realpatch->columnofs[x-x1])); + R_DrawColumnInCache (patchcol, block + colofs[x], patch->originy, + texture->height); + } + + } + +// now that the texture has been built, it is purgable + Z_ChangeTag (block, PU_CACHE); +} + + +/* +=================== += += R_GenerateLookup += +=================== +*/ + +void R_GenerateLookup (int texnum) +{ + texture_t *texture; + byte *patchcount; // [texture->width] + texpatch_t *patch; + patch_t *realpatch; + int x, x1, x2; + int i; + short *collump; + unsigned short *colofs; + + texture = textures[texnum]; + + texturecomposite[texnum] = 0; // composited not created yet + texturecompositesize[texnum] = 0; + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + +// +// count the number of columns that are covered by more than one patch +// fill in the lump / offset, so columns with only a single patch are +// all done +// + patchcount = (byte *)alloca (texture->width); + memset (patchcount, 0, texture->width); + patch = texture->patches; + + for (i=0 , patch = texture->patches; ipatchcount ; i++, patch++) + { + realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + if (x1 < 0) + x = 0; + else + x = x1; + if (x2 > texture->width) + x2 = texture->width; + for ( ; xpatch; + colofs[x] = LONG(realpatch->columnofs[x-x1])+3; + } + } + + for (x=0 ; xwidth ; x++) + { + if (!patchcount[x]) + { + ST_Message ("R_GenerateLookup: column without a patch (%s)\n", texture->name); + return; + } +// I_Error ("R_GenerateLookup: column without a patch"); + if (patchcount[x] > 1) + { + collump[x] = -1; // use the cached block + colofs[x] = texturecompositesize[texnum]; + if (texturecompositesize[texnum] > 0x10000-texture->height) + I_Error ("R_GenerateLookup: texture %i is >64k",texnum); + texturecompositesize[texnum] += texture->height; + } + } +} + + +/* +================ += += R_GetColumn += +================ +*/ + +byte *R_GetColumn (int tex, int col) +{ + int lump, ofs; + + col &= texturewidthmask[tex]; + lump = texturecolumnlump[tex][col]; + ofs = texturecolumnofs[tex][col]; + if (lump > 0) + return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs; + if (!texturecomposite[tex]) + R_GenerateComposite (tex); + return texturecomposite[tex] + ofs; +} + + +/* +================== += += R_InitTextures += += Initializes the texture list with the textures from the world map += +================== +*/ + +void R_InitTextures (void) +{ + maptexture_t *mtexture; + texture_t *texture; + mappatch_t *mpatch; + texpatch_t *patch; + int i,j; + int *maptex, *maptex2, *maptex1; + char name[9], *names, *name_p; + int *patchlookup; + int totalwidth; + int nummappatches; + int offset, maxoff, maxoff2; + int numtextures1, numtextures2; + int *directory; + +// +// load the patch names from pnames.lmp +// + name[8] = 0; + names = W_CacheLumpName ("PNAMES", PU_STATIC); + nummappatches = LONG ( *((int *)names) ); + name_p = names+4; + patchlookup = alloca (nummappatches*sizeof(*patchlookup)); + for (i=0 ; i maxoff) + I_Error ("R_InitTextures: bad texture directory"); + mtexture = (maptexture_t *) ( (byte *)maptex + offset); + texture = textures[i] = Z_Malloc (sizeof(texture_t) + + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), PU_STATIC, + 0); + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + memcpy (texture->name, mtexture->name, sizeof(texture->name)); + mpatch = &mtexture->patches[0]; + patch = &texture->patches[0]; + for (j=0 ; jpatchcount ; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + I_Error ( + "R_InitTextures: Missing patch in texture %s",texture->name); + } + texturecolumnlump[i] = Z_Malloc (texture->width*2, PU_STATIC,0); + texturecolumnofs[i] = Z_Malloc (texture->width*2, PU_STATIC,0); + j = 1; + while (j*2 <= texture->width) + j<<=1; + texturewidthmask[i] = j-1; + textureheight[i] = texture->height<width; + } + + Z_Free (maptex1); + if (maptex2) + Z_Free (maptex2); + +// +// precalculate whatever possible +// + for (i=0 ; iwidth)<leftoffset)<topoffset)<name, name, 8) ) + return i; + + return -1; +} + + +/* +================ += += R_TextureNumForName += +================ +*/ + +int R_TextureNumForName (char *name) +{ + int i; + //char namet[9]; + + i = R_CheckTextureNumForName (name); + if (i==-1) + I_Error ("R_TextureNumForName: %s not found",name); + + return i; +} + + +/* +================= += += R_PrecacheLevel += += Preloads all relevent graphics for the level +================= +*/ + +int flatmemory, texturememory, spritememory; + +void R_PrecacheLevel (void) +{ + char *flatpresent; + char *texturepresent; + char *spritepresent; + int i,j,k, lump; + texture_t *texture; + thinker_t *th; + spriteframe_t *sf; + + if (demoplayback) + return; + +// +// precache flats +// + flatpresent = alloca(numflats); + memset (flatpresent,0,numflats); + for (i=0 ; ipatchcount ; j++) + { + lump = texture->patches[j].patch; + texturememory += lumpinfo[lump].size; + W_CacheLumpNum(lump , PU_CACHE); + } + } + +// +// precache sprites +// + spritepresent = alloca(numsprites); + memset (spritepresent,0, numsprites); + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + { + if (th->function == P_MobjThinker) + spritepresent[((mobj_t *)th)->sprite] = 1; + } + + spritememory = 0; + for (i=0 ; ilump[k]; + spritememory += lumpinfo[lump].size; + W_CacheLumpNum(lump , PU_CACHE); + } + } + } +} diff --git a/src/hexen/r_draw.c b/src/hexen/r_draw.c new file mode 100644 index 00000000..955bbc1b --- /dev/null +++ b/src/hexen/r_draw.c @@ -0,0 +1,576 @@ + +//************************************************************************** +//** +//** r_draw.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_draw.c,v $ +//** $Revision: 1.11 $ +//** $Date: 96/01/06 18:37:37 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "r_local.h" + +/* + +All drawing to the view buffer is accomplished in this file. The other refresh +files only know about ccordinates, not the architecture of the frame buffer. + +*/ + +byte *viewimage; +int viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy; +byte *ylookup[MAXHEIGHT]; +int columnofs[MAXWIDTH]; +//byte translations[3][256]; // color tables for different players +byte *tinttable; // used for translucent sprites + +/* +================== += += R_DrawColumn += += Source is the top of the column to scale += +================== +*/ + +lighttable_t *dc_colormap; +int dc_x; +int dc_yl; +int dc_yh; +fixed_t dc_iscale; +fixed_t dc_texturemid; +byte *dc_source; // first pixel in a column (possibly virtual) + +int dccount; // just for profiling + +#ifndef __WATCOMC__ +#ifndef __i386 +#ifndef __m68k +void R_DrawColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +} +#endif // __m68k +#endif // __i386 +#endif + +void R_DrawColumnLow (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +// dccount++; +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +} + +/* +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) +int fuzzoffset[FUZZTABLE] = { +FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF +}; +int fuzzpos = 0; +*/ + +#ifndef __WATCOMC__ +void R_DrawFuzzColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + if (!dc_yl) + dc_yl = 1; + if (dc_yh == viewheight-1) + dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + +// OLD FUZZY INVISO SPRITE STUFF +/* do + { + *dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; + if (++fuzzpos == FUZZTABLE) + fuzzpos = 0; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +*/ + + do + { + *dest = tinttable[*dest+ + (dc_colormap[dc_source[(frac>>FRACBITS)&127]]<<8)]; + dest += SCREENWIDTH; + frac += fracstep; + } while(count--); +} +#endif + +//============================================================================ +// +// R_DrawAltFuzzColumn +// +//============================================================================ + +void R_DrawAltFuzzColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + if (!dc_yl) + dc_yl = 1; + if (dc_yh == viewheight-1) + dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = tinttable[((*dest)<<8) + +dc_colormap[dc_source[(frac>>FRACBITS)&127]]]; + dest += SCREENWIDTH; + frac += fracstep; + } while(count--); +} + +/* +======================== += += R_DrawTranslatedColumn += +======================== +*/ + +byte *dc_translation; +byte *translationtables; + +void R_DrawTranslatedColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +} + +//============================================================================ +// +// R_DrawTranslatedFuzzColumn +// +//============================================================================ + +void R_DrawTranslatedFuzzColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = tinttable[((*dest)<<8) + +dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]]; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +} + +//============================================================================ +// +// R_DrawTranslatedAltFuzzColumn +// +//============================================================================ + +/* +void R_DrawTranslatedAltFuzzColumn (void) +{ + int count; + byte *dest; + fixed_t frac, fracstep; + + count = dc_yh - dc_yl; + if (count < 0) + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl-centery)*fracstep; + + do + { + *dest = tinttable[*dest + +(dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8)]; + dest += SCREENWIDTH; + frac += fracstep; + } while (count--); +} +*/ + +//-------------------------------------------------------------------------- +// +// PROC R_InitTranslationTables +// +//-------------------------------------------------------------------------- + +void R_InitTranslationTables (void) +{ + int i; + byte *transLump; + + // Load tint table + tinttable = W_CacheLumpName("TINTTAB", PU_STATIC); + + // Allocate translation tables + translationtables = Z_Malloc(256*3*(MAXPLAYERS-1)+255, + PU_STATIC, 0); + translationtables = (byte *)(((int)translationtables+255)&~255); + + for(i = 0; i < 3*(MAXPLAYERS-1); i++) + { + transLump = W_CacheLumpNum(W_GetNumForName("trantbl0")+i, PU_STATIC); + memcpy(translationtables+i*256, transLump, 256); + Z_Free(transLump); + } +} + +/* +================ += += R_DrawSpan += +================ +*/ + +int ds_y; +int ds_x1; +int ds_x2; +lighttable_t *ds_colormap; +fixed_t ds_xfrac; +fixed_t ds_yfrac; +fixed_t ds_xstep; +fixed_t ds_ystep; +byte *ds_source; // start of a 64*64 tile image + +int dscount; // just for profiling + +#ifndef __WATCOMC__ +#ifndef __i386 +#ifndef __m68k +void R_DrawSpan (void) +{ + fixed_t xfrac, yfrac; + byte *dest; + int count, spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + I_Error ("R_DrawSpan: %i to %i at %i",ds_x1,ds_x2,ds_y); +// dscount++; +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1; + do + { + spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63); + *dest++ = ds_colormap[ds_source[spot]]; + xfrac += ds_xstep; + yfrac += ds_ystep; + } while (count--); +} +#endif +#endif +#endif + +void R_DrawSpanLow (void) +{ + fixed_t xfrac, yfrac; + byte *dest; + int count, spot; + +#ifdef RANGECHECK + if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH + || (unsigned)ds_y>SCREENHEIGHT) + I_Error ("R_DrawSpan: %i to %i at %i",ds_x1,ds_x2,ds_y); +// dscount++; +#endif + + xfrac = ds_xfrac; + yfrac = ds_yfrac; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1; + do + { + spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63); + *dest++ = ds_colormap[ds_source[spot]]; + xfrac += ds_xstep; + yfrac += ds_ystep; + } while (count--); +} + + + +/* +================ += += R_InitBuffer += +================= +*/ + +void R_InitBuffer (int width, int height) +{ + int i; + + viewwindowx = (SCREENWIDTH-width) >> 1; + for (i=0 ; i> 1; + for (i=0 ; i8 bit display + +#define MAXVISPLANES 160 +#define MAXOPENINGS SCREENWIDTH*64 + +typedef struct +{ + fixed_t height; + int picnum; + int lightlevel; + int special; + int minx, maxx; + byte pad1; // leave pads for [minx-1]/[maxx+1] + byte top[SCREENWIDTH]; + byte pad2; + byte pad3; + byte bottom[SCREENWIDTH]; + byte pad4; +} visplane_t; + +typedef struct drawseg_s +{ + seg_t *curline; + int x1, x2; + fixed_t scale1, scale2, scalestep; + int silhouette; // 0=none, 1=bottom, 2=top, 3=both + fixed_t bsilheight; // don't clip sprites above this + fixed_t tsilheight; // don't clip sprites below this +// pointers to lists for sprite clipping + short *sprtopclip; // adjusted so [x1] is first value + short *sprbottomclip; // adjusted so [x1] is first value + short *maskedtexturecol; // adjusted so [x1] is first value +} drawseg_t; + +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + +// A vissprite_t is a thing that will be drawn during a refresh +typedef struct vissprite_s +{ + struct vissprite_s *prev, *next; + int x1, x2; + fixed_t gx, gy; // for line side calculation + fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t startfrac; // horizontal position of x1 + fixed_t scale; + fixed_t xiscale; // negative if flipped + fixed_t texturemid; + int patch; + lighttable_t *colormap; + int mobjflags; // for color translation and shadow draw + boolean psprite; // true if psprite + int class; // player class (used in translation) + fixed_t floorclip; +} vissprite_t; + + +extern visplane_t *floorplane, *ceilingplane; + +// Sprites are patches with a special naming convention so they can be +// recognized by R_InitSprites. The sprite and frame specified by a +// thing_t is range checked at run time. +// a sprite is a patch_t that is assumed to represent a three dimensional +// object and may have multiple rotations pre drawn. Horizontal flipping +// is used to save space. Some sprites will only have one picture used +// for all views. + +typedef struct +{ + boolean rotate; // if false use 0 for any position + short lump[8]; // lump to use for view angles 0-7 + byte flip[8]; // flip (1 = flip) to use for view angles 0-7 +} spriteframe_t; + +typedef struct +{ + int numframes; + spriteframe_t *spriteframes; +} spritedef_t; + +extern spritedef_t *sprites; +extern int numsprites; + +//============================================================================= + +extern int numvertexes; +extern vertex_t *vertexes; + +extern int numsegs; +extern seg_t *segs; + +extern int numsectors; +extern sector_t *sectors; + +extern int numsubsectors; +extern subsector_t *subsectors; + +extern int numnodes; +extern node_t *nodes; + +extern int numlines; +extern line_t *lines; + +extern int numsides; +extern side_t *sides; + + + +extern fixed_t viewx, viewy, viewz; +extern angle_t viewangle; +extern player_t *viewplayer; + + +extern angle_t clipangle; + +extern int viewangletox[FINEANGLES/2]; +extern angle_t xtoviewangle[SCREENWIDTH+1]; +extern fixed_t finetangent[FINEANGLES/2]; + +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// +// R_main.c +// +extern int viewwidth, viewheight, viewwindowx, viewwindowy; +extern int centerx, centery; +extern int flyheight; +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t projection; + +extern int validcount; + +extern int sscount, linecount, loopcount; +extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +extern int extralight; +extern lighttable_t *fixedcolormap; + +extern fixed_t viewcos, viewsin; + +extern int detailshift; // 0 = high, 1 = low + +extern void (*colfunc) (void); +extern void (*basecolfunc) (void); +extern void (*fuzzcolfunc) (void); +extern void (*spanfunc) (void); + +int R_PointOnSide (fixed_t x, fixed_t y, node_t *node); +int R_PointOnSegSide (fixed_t x, fixed_t y, seg_t *line); +angle_t R_PointToAngle (fixed_t x, fixed_t y); +angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); +fixed_t R_PointToDist (fixed_t x, fixed_t y); +fixed_t R_ScaleFromGlobalAngle (angle_t visangle); +subsector_t *R_PointInSubsector (fixed_t x, fixed_t y); +//void R_AddPointToBox (int x, int y, fixed_t *box); + + +// +// R_bsp.c +// +extern seg_t *curline; +extern side_t *sidedef; +extern line_t *linedef; +extern sector_t *frontsector, *backsector; + +extern int rw_x; +extern int rw_stopx; + +extern boolean segtextured; +extern boolean markfloor; // false if the back side is the same plane +extern boolean markceiling; +extern boolean skymap; + +extern drawseg_t drawsegs[MAXDRAWSEGS], *ds_p; + +extern lighttable_t **hscalelight, **vscalelight, **dscalelight; + +typedef void (*drawfunc_t) (int start, int stop); +void R_ClearClipSegs (void); + +void R_ClearDrawSegs (void); +void R_InitSkyMap (void); +void R_RenderBSPNode (int bspnum); + +// +// R_segs.c +// +extern int rw_angle1; // angle to line origin +extern int TransTextureStart; +extern int TransTextureEnd; + +void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2); + +// +// R_plane.c +// +typedef void (*planefunction_t) (int top, int bottom); +extern planefunction_t floorfunc, ceilingfunc; + +extern int skyflatnum; + +extern short openings[MAXOPENINGS], *lastopening; + +extern short floorclip[SCREENWIDTH]; +extern short ceilingclip[SCREENWIDTH]; + +extern fixed_t yslope[SCREENHEIGHT]; +extern fixed_t distscale[SCREENWIDTH]; + +void R_InitPlanes (void); +void R_ClearPlanes (void); +void R_MapPlane (int y, int x1, int x2); +void R_MakeSpans (int x, int t1, int b1, int t2, int b2); +void R_DrawPlanes (void); + +visplane_t *R_FindPlane (fixed_t height, int picnum, int lightlevel, + int special); +visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop); + + +// +// R_debug.m +// +extern int drawbsp; + +void RD_OpenMapWindow (void); +void RD_ClearMapWindow (void); +void RD_DisplayLine (int x1, int y1, int x2, int y2, float gray); +void RD_DrawNodeLine (node_t *node); +void RD_DrawLineCheck (seg_t *line); +void RD_DrawLine (seg_t *line); +void RD_DrawBBox (fixed_t *bbox); + + +// +// R_data.c +// +extern fixed_t *textureheight; // needed for texture pegging +extern fixed_t *spritewidth; // needed for pre rendering (fracs) +extern fixed_t *spriteoffset; +extern fixed_t *spritetopoffset; +extern lighttable_t *colormaps; +extern int viewwidth, scaledviewwidth, viewheight; +extern int firstflat; +extern int numflats; + +extern int *flattranslation; // for global animation +extern int *texturetranslation; // for global animation + +extern int firstspritelump, lastspritelump, numspritelumps; +extern boolean LevelUseFullBright; + +byte *R_GetColumn (int tex, int col); +void R_InitData (void); +void R_PrecacheLevel (void); + + +// +// R_things.c +// +#define MAXVISSPRITES 192 + +extern vissprite_t vissprites[MAXVISSPRITES], *vissprite_p; +extern vissprite_t vsprsortedhead; + +// constant arrays used for psprite clipping and initializing clipping +extern short negonearray[SCREENWIDTH]; +extern short screenheightarray[SCREENWIDTH]; + +// vars for R_DrawMaskedColumn +extern short *mfloorclip; +extern short *mceilingclip; +extern fixed_t spryscale; +extern fixed_t sprtopscreen; +extern fixed_t sprbotscreen; + +extern fixed_t pspritescale, pspriteiscale; + + +void R_DrawMaskedColumn (column_t *column, signed int baseclip); + + +void R_SortVisSprites (void); + +void R_AddSprites (sector_t *sec); +void R_AddPSprites (void); +void R_DrawSprites (void); +void R_InitSprites (char **namelist); +void R_ClearSprites (void); +void R_DrawMasked (void); +void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); + +//============================================================================= +// +// R_draw.c +// +//============================================================================= + +extern lighttable_t *dc_colormap; +extern int dc_x; +extern int dc_yl; +extern int dc_yh; +extern fixed_t dc_iscale; +extern fixed_t dc_texturemid; +extern byte *dc_source; // first pixel in a column + +void R_DrawColumn (void); +void R_DrawColumnLow (void); +void R_DrawFuzzColumn (void); +void R_DrawFuzzColumnLow (void); +void R_DrawTranslatedColumn (void); +void R_DrawTranslatedFuzzColumn (void); +void R_DrawTranslatedColumnLow (void); +void R_DrawAltFuzzColumn(void); +//void R_DrawTranslatedAltFuzzColumn(void); + +extern int ds_y; +extern int ds_x1; +extern int ds_x2; +extern lighttable_t *ds_colormap; +extern fixed_t ds_xfrac; +extern fixed_t ds_yfrac; +extern fixed_t ds_xstep; +extern fixed_t ds_ystep; +extern byte *ds_source; // start of a 64*64 tile image + +extern byte *translationtables; +extern byte *dc_translation; + +void R_DrawSpan (void); +void R_DrawSpanLow (void); + +void R_InitBuffer (int width, int height); +void R_InitTranslationTables (void); + +#endif // __R_LOCAL__ + diff --git a/src/hexen/r_main.c b/src/hexen/r_main.c new file mode 100644 index 00000000..0f308748 --- /dev/null +++ b/src/hexen/r_main.c @@ -0,0 +1,860 @@ + +//************************************************************************** +//** +//** r_main.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_main.c,v $ +//** $Revision: 1.16 $ +//** $Date: 96/01/06 18:37:41 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include +#include "h2def.h" +#include "r_local.h" + +int viewangleoffset; + +#ifdef __WATCOMC__ +int newViewAngleOff; +#endif + +int validcount = 1; // increment every time a check is made + +lighttable_t *fixedcolormap; +extern lighttable_t **walllights; + +int centerx, centery; +fixed_t centerxfrac, centeryfrac; +fixed_t projection; + +int framecount; // just for profiling purposes + +int sscount, linecount, loopcount; + +fixed_t viewx, viewy, viewz; +angle_t viewangle; +fixed_t viewcos, viewsin; +player_t *viewplayer; + +int detailshift; // 0 = high, 1 = low + +// +// precalculated math tables +// +angle_t clipangle; + +// The viewangletox[viewangle + FINEANGLES/4] lookup maps the visible view +// angles to screen X coordinates, flattening the arc to a flat projection +// plane. There will be many angles mapped to the same X. +int viewangletox[FINEANGLES/2]; + +// The xtoviewangleangle[] table maps a screen pixel to the lowest viewangle +// that maps back to x ranges from clipangle to -clipangle +angle_t xtoviewangle[SCREENWIDTH+1]; + +// the finetangentgent[angle+FINEANGLES/4] table holds the fixed_t tangent +// values for view angles, ranging from MININT to 0 to MAXINT. +// fixed_t finetangent[FINEANGLES/2]; + +// fixed_t finesine[5*FINEANGLES/4]; +fixed_t *finecosine = &finesine[FINEANGLES/4]; + + +lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +int extralight; // bumped light from gun blasts + +void (*colfunc) (void); +void (*basecolfunc) (void); +void (*fuzzcolfunc) (void); +void (*transcolfunc) (void); +void (*spanfunc) (void); + +/* +=================== += += R_AddPointToBox += +=================== +*/ + +/* +void R_AddPointToBox (int x, int y, fixed_t *box) +{ + if (x< box[BOXLEFT]) + box[BOXLEFT] = x; + if (x> box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y< box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + if (y> box[BOXTOP]) + box[BOXTOP] = y; +} +*/ + + +/* +=============================================================================== += += R_PointOnSide += += Returns side 0 (front) or 1 (back) +=============================================================================== +*/ + +int R_PointOnSide (fixed_t x, fixed_t y, node_t *node) +{ + fixed_t dx,dy; + fixed_t left, right; + + if (!node->dx) + { + if (x <= node->x) + return node->dy > 0; + return node->dy < 0; + } + if (!node->dy) + { + if (y <= node->y) + return node->dx < 0; + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + +// try to quickly decide by looking at sign bits + if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 ) + { + if ( (node->dy ^ dx) & 0x80000000 ) + return 1; // (left is negative) + return 0; + } + + left = FixedMul ( node->dy>>FRACBITS , dx ); + right = FixedMul ( dy , node->dx>>FRACBITS ); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +int R_PointOnSegSide (fixed_t x, fixed_t y, seg_t *line) +{ + fixed_t lx, ly; + fixed_t ldx, ldy; + fixed_t dx,dy; + fixed_t left, right; + + lx = line->v1->x; + ly = line->v1->y; + + ldx = line->v2->x - lx; + ldy = line->v2->y - ly; + + if (!ldx) + { + if (x <= lx) + return ldy > 0; + return ldy < 0; + } + if (!ldy) + { + if (y <= ly) + return ldx < 0; + return ldx > 0; + } + + dx = (x - lx); + dy = (y - ly); + +// try to quickly decide by looking at sign bits + if ( (ldy ^ ldx ^ dx ^ dy)&0x80000000 ) + { + if ( (ldy ^ dx) & 0x80000000 ) + return 1; // (left is negative) + return 0; + } + + left = FixedMul ( ldy>>FRACBITS , dx ); + right = FixedMul ( dy , ldx>>FRACBITS ); + + if (right < left) + return 0; // front side + return 1; // back side +} + + +/* +=============================================================================== += += R_PointToAngle += +=============================================================================== +*/ + +// to get a global angle from cartesian coordinates, the coordinates are +// flipped until they are in the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a tangent (slope) value +// which is looked up in the tantoangle[] table. The +1 size is to handle +// the case when x==y without additional checking. +#define SLOPERANGE 2048 +#define SLOPEBITS 11 +#define DBITS (FRACBITS-SLOPEBITS) + + +extern int tantoangle[SLOPERANGE+1]; // get from tables.c + +// int tantoangle[SLOPERANGE+1]; + +int SlopeDiv (unsigned num, unsigned den) +{ + unsigned ans; + if (den < 512) + return SLOPERANGE; + ans = (num<<3)/(den>>8); + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + +angle_t R_PointToAngle (fixed_t x, fixed_t y) +{ + x -= viewx; + y -= viewy; + if ( (!x) && (!y) ) + return 0; + if (x>= 0) + { // x >=0 + if (y>= 0) + { // y>= 0 + if (x>y) + return tantoangle[ SlopeDiv(y,x)]; // octant 0 + else + return ANG90-1-tantoangle[ SlopeDiv(x,y)]; // octant 1 + } + else + { // y<0 + y = -y; + if (x>y) + return -tantoangle[SlopeDiv(y,x)]; // octant 8 + else + return ANG270+tantoangle[ SlopeDiv(x,y)]; // octant 7 + } + } + else + { // x<0 + x = -x; + if (y>= 0) + { // y>= 0 + if (x>y) + return ANG180-1-tantoangle[ SlopeDiv(y,x)]; // octant 3 + else + return ANG90+ tantoangle[ SlopeDiv(x,y)]; // octant 2 + } + else + { // y<0 + y = -y; + if (x>y) + return ANG180+tantoangle[ SlopeDiv(y,x)]; // octant 4 + else + return ANG270-1-tantoangle[ SlopeDiv(x,y)]; // octant 5 + } + } + + return 0; +} + + +angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + viewx = x1; + viewy = y1; + return R_PointToAngle (x2, y2); +} + + +fixed_t R_PointToDist (fixed_t x, fixed_t y) +{ + int angle; + fixed_t dx, dy, temp; + fixed_t dist; + + dx = abs(x - viewx); + dy = abs(y - viewy); + + if (dy>dx) + { + temp = dx; + dx = dy; + dy = temp; + } + + angle = (tantoangle[ FixedDiv(dy,dx)>>DBITS ]+ANG90) >> ANGLETOFINESHIFT; + + dist = FixedDiv (dx, finesine[angle] ); // use as cosine + + return dist; +} + + + +/* +================= += += R_InitPointToAngle += +================= +*/ + +void R_InitPointToAngle (void) +{ +// now getting from tables.c +#if 0 + int i; + long t; + float f; +// +// slope (tangent) to angle lookup +// + for (i=0 ; i<=SLOPERANGE ; i++) + { + f = atan( (float)i/SLOPERANGE )/(3.141592657*2); + t = 0xffffffff*f; + tantoangle[i] = t; + } +#endif +} + +//============================================================================= + +/* +================ += += R_ScaleFromGlobalAngle += += Returns the texture mapping scale for the current line at the given angle += rw_distance must be calculated first +================ +*/ + +fixed_t R_ScaleFromGlobalAngle (angle_t visangle) +{ + fixed_t scale; + int anglea, angleb; + int sinea, sineb; + fixed_t num,den; + +#if 0 +{ + fixed_t dist,z; + fixed_t sinv, cosv; + + sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; + dist = FixedDiv (rw_distance, sinv); + cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; + z = abs(FixedMul (dist, cosv)); + scale = FixedDiv(projection, z); + return scale; +} +#endif + + anglea = ANG90 + (visangle-viewangle); + angleb = ANG90 + (visangle-rw_normalangle); +// bothe sines are allways positive + sinea = finesine[anglea>>ANGLETOFINESHIFT]; + sineb = finesine[angleb>>ANGLETOFINESHIFT]; + num = FixedMul(projection,sineb)< num>>16) + { + scale = FixedDiv (num, den); + if (scale > 64*FRACUNIT) + scale = 64*FRACUNIT; + else if (scale < 256) + scale = 256; + } + else + scale = 64*FRACUNIT; + + return scale; +} + + + +/* +================= += += R_InitTables += +================= +*/ + +void R_InitTables (void) +{ +// now getting from tables.c +#if 0 + int i; + float a, fv; + int t; + +// +// viewangle tangent table +// + for (i=0 ; i FRACUNIT*2) + t = -1; + else if (finetangent[i] < -FRACUNIT*2) + t = viewwidth+1; + else + { + t = FixedMul (finetangent[i], focallength); + t = (centerxfrac - t+FRACUNIT-1)>>FRACBITS; + if (t < -1) + t = -1; + else if (t>viewwidth+1) + t = viewwidth+1; + } + viewangletox[i] = t; + } + +// +// scan viewangletox[] to generate xtoviewangleangle[] +// +// xtoviewangle will give the smallest view angle that maps to x + for (x=0;x<=viewwidth;x++) + { + i = 0; + while (viewangletox[i]>x) + i++; + xtoviewangle[x] = (i<>= LIGHTSCALESHIFT; + level = startmap - scale/DISTMAP; + if (level < 0) + level = 0; + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + zlight[i][j] = colormaps + level*256; + } + } +} + + +/* +============== += += R_SetViewSize += += Don't really change anything here, because i might be in the middle of += a refresh. The change will take effect next refresh. += +============== +*/ + +boolean setsizeneeded; +int setblocks, setdetail; + +void R_SetViewSize (int blocks, int detail) +{ + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; +} + +/* +============== += += R_ExecuteSetViewSize += +============== +*/ + +void R_ExecuteSetViewSize (void) +{ + fixed_t cosadj, dy; + int i,j, level, startmap; + + setsizeneeded = false; + + if (setblocks == 11) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT; + } + else + { + scaledviewwidth = setblocks*32; + viewheight = (setblocks*161/10); + } + + detailshift = setdetail; + viewwidth = scaledviewwidth>>detailshift; + + centery = viewheight/2; + centerx = viewwidth/2; + centerxfrac = centerx<>ANGLETOFINESHIFT]); + distscale[i] = FixedDiv (FRACUNIT,cosadj); + } + +// +// Calculate the light levels to use for each level / scale combination +// + for (i=0 ; i< LIGHTLEVELS ; i++) + { + startmap = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; + for (j=0 ; j= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + scalelight[i][j] = colormaps + level*256; + } + } + +// +// draw the border +// + R_DrawViewBorder (); // erase old menu stuff +} + + +/* +============== += += R_Init += +============== +*/ + +int detailLevel; +int screenblocks; + +void R_Init(void) +{ + R_InitData(); + R_InitPointToAngle(); + R_InitTables(); + // viewwidth / viewheight / detailLevel are set by the defaults + R_SetViewSize(screenblocks, detailLevel); + R_InitPlanes(); + R_InitLightTables(); + R_InitSkyMap(); + R_InitTranslationTables(); + framecount = 0; +} + +/* +============== += += R_PointInSubsector += +============== +*/ + +subsector_t *R_PointInSubsector (fixed_t x, fixed_t y) +{ + node_t *node; + int side, nodenum; + + if (!numnodes) // single subsector is a special case + return subsectors; + + nodenum = numnodes-1; + + while (! (nodenum & NF_SUBSECTOR) ) + { + node = &nodes[nodenum]; + side = R_PointOnSide (x, y, node); + nodenum = node->children[side]; + } + + return &subsectors[nodenum & ~NF_SUBSECTOR]; + +} + +//---------------------------------------------------------------------------- +// +// PROC R_SetupFrame +// +//---------------------------------------------------------------------------- + +void R_SetupFrame(player_t *player) +{ + int i; + int tableAngle; + int tempCentery; + int intensity; + + //drawbsp = 1; + viewplayer = player; +#ifdef __WATCOMC__ + if(newViewAngleOff) + { + viewangleoffset = newViewAngleOff<mo->angle+viewangleoffset; + tableAngle = viewangle>>ANGLETOFINESHIFT; + viewx = player->mo->x; + viewy = player->mo->y; + + if(localQuakeHappening[displayplayer] && !paused) + { + intensity = localQuakeHappening[displayplayer]; + viewx += ((M_Random() % (intensity<<2)) + -(intensity<<1))<extralight; + viewz = player->viewz; + + tempCentery = viewheight/2+(player->lookdir)*screenblocks/10; + if(centery != tempCentery) + { + centery = tempCentery; + centeryfrac = centery<fixedcolormap) + { + fixedcolormap = colormaps+player->fixedcolormap + *256*sizeof(lighttable_t); + walllights = scalelightfixed; + for(i = 0; i < MAXLIGHTSCALE; i++) + { + scalelightfixed[i] = fixedcolormap; + } + } + else + { + fixedcolormap = 0; + } + framecount++; + validcount++; + if(BorderNeedRefresh) + { + if(setblocks < 10) + { + R_DrawViewBorder(); + } + BorderNeedRefresh = false; + BorderTopRefresh = false; + UpdateState |= I_FULLSCRN; + } + if(BorderTopRefresh) + { + if(setblocks < 10) + { + R_DrawTopBorder(); + } + BorderTopRefresh = false; + UpdateState |= I_MESSAGES; + } + +#ifdef __NeXT__ + RD_ClearMapWindow (); +#endif +#ifdef __WATCOMC__ + destview = destscreen+(viewwindowx>>2)+viewwindowy*80; +#endif + +#if 0 +{ +static int frame; +memset (screen, frame, SCREENWIDTH*SCREENHEIGHT); +frame++; +} +#endif +} + +/* +============== += += R_RenderView += +============== +*/ + +void R_RenderPlayerView (player_t *player) +{ + R_SetupFrame (player); + R_ClearClipSegs (); + R_ClearDrawSegs (); + R_ClearPlanes (); + R_ClearSprites (); + NetUpdate (); // check for new console commands + + // Make displayed player invisible locally + if (localQuakeHappening[displayplayer] && gamestate == GS_LEVEL) + { + players[displayplayer].mo->flags2 |= MF2_DONTDRAW; + R_RenderBSPNode (numnodes-1); // head node is the last node output + players[displayplayer].mo->flags2 &= ~MF2_DONTDRAW; + } + else + { + R_RenderBSPNode (numnodes-1); // head node is the last node output + } + + NetUpdate (); // check for new console commands + R_DrawPlanes (); + NetUpdate (); // check for new console commands + R_DrawMasked (); + NetUpdate (); // check for new console commands +} diff --git a/src/hexen/r_plane.c b/src/hexen/r_plane.c new file mode 100644 index 00000000..ece108dc --- /dev/null +++ b/src/hexen/r_plane.c @@ -0,0 +1,559 @@ + +//************************************************************************** +//** +//** r_plane.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_plane.c,v $ +//** $Revision: 1.5 $ +//** $Date: 95/07/13 15:17:12 $ +//** $Author: cjr $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "r_local.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern fixed_t Sky1ScrollDelta; +extern fixed_t Sky2ScrollDelta; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int Sky1Texture; +int Sky2Texture; +fixed_t Sky1ColumnOffset; +fixed_t Sky2ColumnOffset; +int skyflatnum; +int skytexturemid; +fixed_t skyiscale; +boolean DoubleSky; +planefunction_t floorfunc, ceilingfunc; + +// Opening +visplane_t visplanes[MAXVISPLANES], *lastvisplane; +visplane_t *floorplane, *ceilingplane; +short openings[MAXOPENINGS], *lastopening; + +// Clip values are the solid pixel bounding the range. +// floorclip start out SCREENHEIGHT +// ceilingclip starts out -1 +short floorclip[SCREENWIDTH]; +short ceilingclip[SCREENWIDTH]; + +// spanstart holds the start of a plane span, initialized to 0 +int spanstart[SCREENHEIGHT]; +int spanstop[SCREENHEIGHT]; + +// Texture mapping +lighttable_t **planezlight; +fixed_t planeheight; +fixed_t yslope[SCREENHEIGHT]; +fixed_t distscale[SCREENWIDTH]; +fixed_t basexscale, baseyscale; +fixed_t cachedheight[SCREENHEIGHT]; +fixed_t cacheddistance[SCREENHEIGHT]; +fixed_t cachedxstep[SCREENHEIGHT]; +fixed_t cachedystep[SCREENHEIGHT]; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// R_InitSky +// +// Called at level load. +// +//========================================================================== + +void R_InitSky(int map) +{ + Sky1Texture = P_GetMapSky1Texture(map); + Sky2Texture = P_GetMapSky2Texture(map); + Sky1ScrollDelta = P_GetMapSky1ScrollDelta(map); + Sky2ScrollDelta = P_GetMapSky2ScrollDelta(map); + Sky1ColumnOffset = 0; + Sky2ColumnOffset = 0; + DoubleSky = P_GetMapDoubleSky(map); +} + +//========================================================================== +// +// R_InitSkyMap +// +// Called whenever the view size changes. +// +//========================================================================== + +void R_InitSkyMap(void) +{ + skyflatnum = R_FlatNumForName("F_SKY"); + skytexturemid = 200*FRACUNIT; + skyiscale = FRACUNIT; +} + +//========================================================================== +// +// R_InitPlanes +// +// Called at game startup. +// +//========================================================================== + +void R_InitPlanes(void) +{ +} + +//========================================================================== +// +// R_MapPlane +// +// Globals used: planeheight, ds_source, basexscale, baseyscale, +// viewx, viewy. +// +//========================================================================== + +void R_MapPlane(int y, int x1, int x2) +{ + angle_t angle; + fixed_t distance, length; + unsigned index; + +#ifdef RANGECHECK + if(x2 < x1 || x1 < 0 || x2 >= viewwidth || (unsigned)y > viewheight) + { + I_Error("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + if(planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); + ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale); + ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale); + } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + length = FixedMul(distance, distscale[x1]); + angle = (viewangle+xtoviewangle[x1])>>ANGLETOFINESHIFT; + ds_xfrac = viewx+FixedMul(finecosine[angle], length); + ds_yfrac = -viewy-FixedMul(finesine[angle], length); + + if(fixedcolormap) + { + ds_colormap = fixedcolormap; + } + else + { + index = distance >> LIGHTZSHIFT; + if(index >= MAXLIGHTZ ) + { + index = MAXLIGHTZ-1; + } + ds_colormap = planezlight[index]; + } + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + + spanfunc(); // High or low detail +} + +//========================================================================== +// +// R_ClearPlanes +// +// Called at the beginning of each frame. +// +//========================================================================== + +void R_ClearPlanes(void) +{ + int i; + angle_t angle; + + // Opening / clipping determination + for(i = 0; i < viewwidth; i++) + { + floorclip[i] = viewheight; + ceilingclip[i] = -1; + } + + lastvisplane = visplanes; + lastopening = openings; + + // Texture calculation + memset(cachedheight, 0, sizeof(cachedheight)); + angle = (viewangle-ANG90)>>ANGLETOFINESHIFT; // left to right mapping + // Scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv(finecosine[angle], centerxfrac); + baseyscale = -FixedDiv(finesine[angle], centerxfrac); +} + +//========================================================================== +// +// R_FindPlane +// +//========================================================================== + +visplane_t *R_FindPlane(fixed_t height, int picnum, + int lightlevel, int special) +{ + visplane_t *check; + + if(special < 150) + { // Don't let low specials affect search + special = 0; + } + + if(picnum == skyflatnum) + { // All skies map together + height = 0; + lightlevel = 0; + } + + for(check = visplanes; check < lastvisplane; check++) + { + if(height == check->height + && picnum == check->picnum + && lightlevel == check->lightlevel + && special == check->special) + break; + } + + if(check < lastvisplane) + { + return(check); + } + + if(lastvisplane-visplanes == MAXVISPLANES) + { + I_Error("R_FindPlane: no more visplanes"); + } + + lastvisplane++; + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->special = special; + check->minx = SCREENWIDTH; + check->maxx = -1; + memset(check->top, 0xff, sizeof(check->top)); + return(check); +} + +//========================================================================== +// +// R_CheckPlane +// +//========================================================================== + +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop) +{ + int intrl, intrh; + int unionl, unionh; + int x; + + if(start < pl->minx) + { + intrl = pl->minx; + unionl = start; + } + else + { + unionl = pl->minx; + intrl = start; + } + if(stop > pl->maxx) + { + intrh = pl->maxx; + unionh = stop; + } + else + { + unionh = pl->maxx; + intrh = stop; + } + + for(x = intrl; x <= intrh; x++) + { + if(pl->top[x] != 0xff) + { + break; + } + } + + if(x > intrh) + { + pl->minx = unionl; + pl->maxx = unionh; + return pl; // use the same visplane + } + + // Make a new visplane + lastvisplane->height = pl->height; + lastvisplane->picnum = pl->picnum; + lastvisplane->lightlevel = pl->lightlevel; + lastvisplane->special = pl->special; + pl = lastvisplane++; + pl->minx = start; + pl->maxx = stop; + memset(pl->top, 0xff, sizeof(pl->top)); + + return pl; +} + +//========================================================================== +// +// R_MakeSpans +// +//========================================================================== + +void R_MakeSpans(int x, int t1, int b1, int t2, int b2) +{ + while(t1 < t2 && t1 <= b1) + { + R_MapPlane(t1, spanstart[t1], x-1); + t1++; + } + while(b1 > b2 && b1 >= t1) + { + R_MapPlane(b1, spanstart[b1], x-1); + b1--; + } + while(t2 < t1 && t2 <= b2) + { + spanstart[t2] = x; + t2++; + } + while(b2 > b1 && b2 >= t2) + { + spanstart[b2] = x; + b2--; + } +} + +//========================================================================== +// +// R_DrawPlanes +// +//========================================================================== + +#define SKYTEXTUREMIDSHIFTED 200 + +void R_DrawPlanes(void) +{ + visplane_t *pl; + int light; + int x, stop; + int angle; + byte *tempSource; + byte *source; + byte *source2; + byte *dest; + int count; + int offset; + int skyTexture; + int offset2; + int skyTexture2; + int scrollOffset; + + extern byte *ylookup[MAXHEIGHT]; + extern int columnofs[MAXWIDTH]; + +#ifdef RANGECHECK + if(ds_p-drawsegs > MAXDRAWSEGS) + { + I_Error("R_DrawPlanes: drawsegs overflow (%i)", ds_p-drawsegs); + } + if(lastvisplane-visplanes > MAXVISPLANES) + { + I_Error("R_DrawPlanes: visplane overflow (%i)", + lastvisplane-visplanes); + } + if(lastopening-openings > MAXOPENINGS) + { + I_Error("R_DrawPlanes: opening overflow (%i)", + lastopening-openings); + } +#endif + + for(pl = visplanes; pl < lastvisplane; pl++) + { + if(pl->minx > pl->maxx) + { + continue; + } + if(pl->picnum == skyflatnum) + { // Sky flat + if(DoubleSky) + { // Render 2 layers, sky 1 in front + offset = Sky1ColumnOffset>>16; + skyTexture = texturetranslation[Sky1Texture]; + offset2 = Sky2ColumnOffset>>16; + skyTexture2 = texturetranslation[Sky2Texture]; + for(x = pl->minx; x <= pl->maxx; x++) + { + dc_yl = pl->top[x]; + dc_yh = pl->bottom[x]; + if(dc_yl <= dc_yh) + { + count = dc_yh-dc_yl; + if(count < 0) + { + return; + } + angle = (viewangle+xtoviewangle[x]) + >>ANGLETOSKYSHIFT; + source = R_GetColumn(skyTexture, angle+offset) + +SKYTEXTUREMIDSHIFTED+(dc_yl-centery); + source2 = R_GetColumn(skyTexture2, angle+offset2) + +SKYTEXTUREMIDSHIFTED+(dc_yl-centery); + dest = ylookup[dc_yl]+columnofs[x]; + do + { + if(*source) + { + *dest = *source++; + source2++; + } + else + { + *dest = *source2++; + source++; + } + dest += SCREENWIDTH; + } while(count--); + } + } + continue; // Next visplane + } + else + { // Render single layer + if(pl->special == 200) + { // Use sky 2 + offset = Sky2ColumnOffset>>16; + skyTexture = texturetranslation[Sky2Texture]; + } + else + { // Use sky 1 + offset = Sky1ColumnOffset>>16; + skyTexture = texturetranslation[Sky1Texture]; + } + for(x = pl->minx; x <= pl->maxx; x++) + { + dc_yl = pl->top[x]; + dc_yh = pl->bottom[x]; + if(dc_yl <= dc_yh) + { + count = dc_yh-dc_yl; + if(count < 0) + { + return; + } + angle = (viewangle+xtoviewangle[x]) + >>ANGLETOSKYSHIFT; + source = R_GetColumn(skyTexture, angle+offset) + +SKYTEXTUREMIDSHIFTED+(dc_yl-centery); + dest = ylookup[dc_yl]+columnofs[x]; + do + { + *dest = *source++; + dest += SCREENWIDTH; + } while(count--); + } + } + continue; // Next visplane + } + } + // Regular flat + tempSource = W_CacheLumpNum(firstflat+ + flattranslation[pl->picnum], PU_STATIC); + scrollOffset = leveltime>>1&63; + switch(pl->special) + { // Handle scrolling flats + case 201: case 202: case 203: // Scroll_North_xxx + ds_source = tempSource+((scrollOffset + <<(pl->special-201)&63)<<6); + break; + case 204: case 205: case 206: // Scroll_East_xxx + ds_source = tempSource+((63-scrollOffset) + <<(pl->special-204)&63); + break; + case 207: case 208: case 209: // Scroll_South_xxx + ds_source = tempSource+(((63-scrollOffset) + <<(pl->special-207)&63)<<6); + break; + case 210: case 211: case 212: // Scroll_West_xxx + ds_source = tempSource+(scrollOffset + <<(pl->special-210)&63); + break; + case 213: case 214: case 215: // Scroll_NorthWest_xxx + ds_source = tempSource+(scrollOffset + <<(pl->special-213)&63)+((scrollOffset + <<(pl->special-213)&63)<<6); + break; + case 216: case 217: case 218: // Scroll_NorthEast_xxx + ds_source = tempSource+((63-scrollOffset) + <<(pl->special-216)&63)+((scrollOffset + <<(pl->special-216)&63)<<6); + break; + case 219: case 220: case 221: // Scroll_SouthEast_xxx + ds_source = tempSource+((63-scrollOffset) + <<(pl->special-219)&63)+(((63-scrollOffset) + <<(pl->special-219)&63)<<6); + break; + case 222: case 223: case 224: // Scroll_SouthWest_xxx + ds_source = tempSource+(scrollOffset + <<(pl->special-222)&63)+(((63-scrollOffset) + <<(pl->special-222)&63)<<6); + break; + default: + ds_source = tempSource; + break; + } + planeheight = abs(pl->height-viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT)+extralight; + if(light >= LIGHTLEVELS) + { + light = LIGHTLEVELS-1; + } + if(light < 0) + { + light = 0; + } + planezlight = zlight[light]; + + pl->top[pl->maxx+1] = 0xff; + pl->top[pl->minx-1] = 0xff; + + stop = pl->maxx+1; + for(x = pl->minx; x <= stop; x++) + { + R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], + pl->top[x], pl->bottom[x]); + } + Z_ChangeTag(tempSource, PU_CACHE); + } +} diff --git a/src/hexen/r_segs.c b/src/hexen/r_segs.c new file mode 100644 index 00000000..a5fad3f8 --- /dev/null +++ b/src/hexen/r_segs.c @@ -0,0 +1,651 @@ + +//************************************************************************** +//** +//** r_segs.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_segs.c,v $ +//** $Revision: 1.4 $ +//** $Date: 95/08/28 17:02:44 $ +//** $Author: cjr $ +//** +//** This version has the tall-sector-crossing-precision-bug fixed. +//** +//************************************************************************** + +#include "h2def.h" +#include "r_local.h" + +// OPTIMIZE: closed two sided lines as single sided + +boolean segtextured; // true if any of the segs textures might be vis +boolean markfloor; // false if the back side is the same plane +boolean markceiling; +boolean maskedtexture; +int toptexture, bottomtexture, midtexture; + + +angle_t rw_normalangle; +int rw_angle1; // angle to line origin + +// +// wall +// +int rw_x; +int rw_stopx; +angle_t rw_centerangle; +fixed_t rw_offset; +fixed_t rw_distance; +fixed_t rw_scale; +fixed_t rw_scalestep; +fixed_t rw_midtexturemid; +fixed_t rw_toptexturemid; +fixed_t rw_bottomtexturemid; + +int worldtop, worldbottom, worldhigh, worldlow; + +fixed_t pixhigh, pixlow; +fixed_t pixhighstep, pixlowstep; +fixed_t topfrac, topstep; +fixed_t bottomfrac, bottomstep; + + +lighttable_t **walllights; + +short *maskedtexturecol; + +/* +================ += += R_RenderMaskedSegRange += +================ +*/ + +void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) +{ + unsigned index; + column_t *col; + int lightnum; + int texnum; + +// +// calculate light table +// use different light tables for horizontal / vertical / diagonal +// OPTIMIZE: get rid of LIGHTSEGSHIFT globally + curline = ds->curline; + frontsector = curline->frontsector; + backsector = curline->backsector; + texnum = texturetranslation[curline->sidedef->midtexture]; + + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; + //if (curline->v1->y == curline->v2->y) + // lightnum--; + //else if (curline->v1->x == curline->v2->x) + // lightnum++; + //if (lightnum < 0) + // walllights = scalelight[0]; + if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS-1]; + else + walllights = scalelight[lightnum]; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + +// +// find positioning +// + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dc_texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; + } + else + { + dc_texturemid =frontsector->ceilingheightceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dc_texturemid = dc_texturemid - viewz; + } + dc_texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) + dc_colormap = fixedcolormap; +// +// draw the columns +// + for (dc_x = x1 ; dc_x <= x2 ; dc_x++) + { + // calculate lighting + if (maskedtexturecol[dc_x] != MAXSHORT) + { + if (!fixedcolormap) + { + index = spryscale>>LIGHTSCALESHIFT; + if (index >= MAXLIGHTSCALE ) + index = MAXLIGHTSCALE-1; + dc_colormap = walllights[index]; + } + + sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + dc_iscale = 0xffffffffu / (unsigned)spryscale; + + // + // draw the texture + // + col = (column_t *)( + (byte *)R_GetColumn(texnum,maskedtexturecol[dc_x]) -3); + + R_DrawMaskedColumn (col, -1); + maskedtexturecol[dc_x] = MAXSHORT; + } + spryscale += rw_scalestep; + } + +} + +/* +================ += += R_RenderSegLoop += += Draws zero, one, or two textures (and possibly a masked texture) for walls += Can draw or mark the starting pixel of floor and ceiling textures += += CALLED: CORE LOOPING ROUTINE +================ +*/ + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1<>HEIGHTBITS; + if (yl < ceilingclip[rw_x]+1) + yl = ceilingclip[rw_x]+1; // no space above wall + if (markceiling) + { + top = ceilingclip[rw_x]+1; + bottom = yl-1; + if (bottom >= floorclip[rw_x]) + bottom = floorclip[rw_x]-1; + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + } + + yh = bottomfrac>>HEIGHTBITS; + if (yh >= floorclip[rw_x]) + yh = floorclip[rw_x]-1; + if (markfloor) + { + top = yh+1; + bottom = floorclip[rw_x]-1; + if (top <= ceilingclip[rw_x]) + top = ceilingclip[rw_x]+1; + if (top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + } + +// +// texturecolumn and lighting are independent of wall tiers +// + if (segtextured) + { + // calculate texture offset + angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; + texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); + texturecolumn >>= FRACBITS; + // calculate lighting + index = rw_scale>>LIGHTSCALESHIFT; + if (index >= MAXLIGHTSCALE ) + index = MAXLIGHTSCALE-1; + dc_colormap = walllights[index]; + dc_x = rw_x; + dc_iscale = 0xffffffffu / (unsigned)rw_scale; + } + +// +// draw the wall tiers +// + if (midtexture) + { // single sided line + dc_yl = yl; + dc_yh = yh; + dc_texturemid = rw_midtexturemid; + dc_source = R_GetColumn(midtexture,texturecolumn); + colfunc (); + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { // two sided line + if (toptexture) + { // top wall + mid = pixhigh>>HEIGHTBITS; + pixhigh += pixhighstep; + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x]-1; + if (mid >= yl) + { + dc_yl = yl; + dc_yh = mid; + dc_texturemid = rw_toptexturemid; + dc_source = R_GetColumn(toptexture,texturecolumn); + colfunc (); + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl-1; + } + else + { // no top wall + if (markceiling) + ceilingclip[rw_x] = yl-1; + } + + if (bottomtexture) + { // bottom wall + mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; + pixlow += pixlowstep; + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x]+1; // no space above wall + if (mid <= yh) + { + dc_yl = mid; + dc_yh = yh; + dc_texturemid = rw_bottomtexturemid; + dc_source = R_GetColumn(bottomtexture, + texturecolumn); + colfunc (); + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh+1; + } + else + { // no bottom wall + if (markfloor) + floorclip[rw_x] = yh+1; + } + + if (maskedtexture) + { // save texturecol for backdrawing of masked mid texture + maskedtexturecol[rw_x] = texturecolumn; + } + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } + +} + + + +/* +===================== += += R_StoreWallRange += += A wall segment will be drawn between start and stop pixels (inclusive) += +====================== +*/ + +void R_StoreWallRange (int start, int stop) +{ + fixed_t hyp; + fixed_t sineval; + angle_t distangle, offsetangle; + fixed_t vtop; + int lightnum; + + if (ds_p == &drawsegs[MAXDRAWSEGS]) + return; // don't overflow and crash + +#ifdef RANGECHECK + if (start >=viewwidth || start > stop) + I_Error ("Bad R_RenderWallRange: %i to %i", start , stop); +#endif +#ifdef __NeXT__ + RD_DrawLine (curline); +#endif + + sidedef = curline->sidedef; + linedef = curline->linedef; + +// mark the segment as visible for auto map + linedef->flags |= ML_MAPPED; + +// +// calculate rw_distance for scale calculation +// + rw_normalangle = curline->angle + ANG90; + offsetangle = abs(rw_normalangle-rw_angle1); + if (offsetangle > ANG90) + offsetangle = ANG90; + distangle = ANG90 - offsetangle; + hyp = R_PointToDist (curline->v1->x, curline->v1->y); + sineval = finesine[distangle>>ANGLETOFINESHIFT]; + rw_distance = FixedMul (hyp, sineval); + + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop+1; + +// +// calculate scale at both ends and step +// + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]); + if (stop > start ) + { + ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = + (ds_p->scale2 - rw_scale) / (stop-start); + } + else + { + // + // try to fix the stretched line bug + // +#if 0 + if (rw_distance < FRACUNIT/2) + { + fixed_t trx,try; + fixed_t gxt,gyt; + + trx = curline->v1->x - viewx; + try = curline->v1->y - viewy; + + gxt = FixedMul(trx,viewcos); + gyt = -FixedMul(try,viewsin); + ds_p->scale1 = FixedDiv(projection, gxt-gyt); + } +#endif + ds_p->scale2 = ds_p->scale1; + } + + +// +// calculate texture boundaries and decide if floor / ceiling marks +// are needed +// + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = NULL; + + if (!backsector) + { +// +// single sided line +// + midtexture = texturetranslation[sidedef->midtexture]; + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + if (linedef->flags & ML_DONTPEGBOTTOM) + { + vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + rw_midtexturemid = vtop - viewz; // bottom of texture at bottom + } + else + rw_midtexturemid = worldtop; // top of texture at top + rw_midtexturemid += sidedef->rowoffset; + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = MAXINT; + ds_p->tsilheight = MININT; + } + else + { +// +// two sided line +// + ds_p->sprtopclip = ds_p->sprbottomclip = NULL; + ds_p->silhouette = 0; + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = MAXINT; +// ds_p->sprbottomclip = negonearray; + } + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = MININT; +// ds_p->sprtopclip = screenheightarray; + } + + if (backsector->ceilingheight <= frontsector->floorheight) + { + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = MAXINT; + ds_p->silhouette |= SIL_BOTTOM; + } + if (backsector->floorheight >= frontsector->ceilingheight) + { + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = MININT; + ds_p->silhouette |= SIL_TOP; + } + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + // hack to allow height changes in outdoor areas + if (frontsector->ceilingpic == skyflatnum + && backsector->ceilingpic == skyflatnum) + worldtop = worldhigh; + + if (worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel + || backsector->special != frontsector->special) + markfloor = true; + else + markfloor = false; // same plane on both sides + + if (worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel) + markceiling = true; + else + markceiling = false; // same plane on both sides + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + markceiling = markfloor = true; // closed door + + if (worldhigh < worldtop) + { // top texture + toptexture = texturetranslation[sidedef->toptexture]; + if (linedef->flags & ML_DONTPEGTOP) + rw_toptexturemid = worldtop; // top of texture at top + else + { + vtop = backsector->ceilingheight + + textureheight[sidedef->toptexture]; + rw_toptexturemid = vtop - viewz; // bottom of texture + } + } + if (worldlow > worldbottom) + { // bottom texture + bottomtexture = texturetranslation[sidedef->bottomtexture]; + if (linedef->flags & ML_DONTPEGBOTTOM ) + { // bottom of texture at bottom + rw_bottomtexturemid = worldtop;// top of texture at top + } + else // top of texture at top + rw_bottomtexturemid = worldlow; + } + rw_toptexturemid += sidedef->rowoffset; + rw_bottomtexturemid += sidedef->rowoffset; + + // + // allocate space for masked texture tables + // + if (sidedef->midtexture) + { // masked midtexture + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + +// +// calculate rw_offset (only needed for textured lines) +// + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + offsetangle = rw_normalangle-rw_angle1; + if (offsetangle > ANG180) + offsetangle = -offsetangle; + if (offsetangle > ANG90) + offsetangle = ANG90; + sineval = finesine[offsetangle >>ANGLETOFINESHIFT]; + rw_offset = FixedMul (hyp, sineval); + if (rw_normalangle-rw_angle1 < ANG180) + rw_offset = -rw_offset; + rw_offset += sidedef->textureoffset + curline->offset; + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + // + // calculate light table + // use different light tables for horizontal / vertical / diagonal + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + if (!fixedcolormap) + { + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; + //if (curline->v1->y == curline->v2->y) + // lightnum--; + //else if (curline->v1->x == curline->v2->x) + // lightnum++; + //if (lightnum < 0) + // walllights = scalelight[0]; + if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS-1]; + else + walllights = scalelight[lightnum]; + } + } + + +// +// if a floor / ceiling plane is on the wrong side of the view plane +// it is definately invisible and doesn't need to be marked +// + if (frontsector->floorheight >= viewz) + markfloor = false; // above view plane + if (frontsector->ceilingheight <= viewz + && frontsector->ceilingpic != skyflatnum) + markceiling = false; // below view plane + +// +// calculate incremental stepping values for texture edges +// + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul (rw_scalestep, worldtop); + topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale); + + bottomstep = -FixedMul (rw_scalestep,worldbottom); + bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale); + pixhighstep = -FixedMul (rw_scalestep,worldhigh); + } + if (worldlow > worldbottom) + { + pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale); + pixlowstep = -FixedMul (rw_scalestep,worldlow); + } + } + +// +// render it +// + if (markceiling) + ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); + if (markfloor) + floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); + + R_RenderSegLoop (); + +// +// save sprite clipping info +// + if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) + { + memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip) + { + memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + if (maskedtexture && !(ds_p->silhouette&SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = MININT; + } + if (maskedtexture && !(ds_p->silhouette&SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = MAXINT; + } + ds_p++; +} + diff --git a/src/hexen/r_things.c b/src/hexen/r_things.c new file mode 100644 index 00000000..4f503442 --- /dev/null +++ b/src/hexen/r_things.c @@ -0,0 +1,1033 @@ + +//************************************************************************** +//** +//** r_things.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: r_things.c,v $ +//** $Revision: 1.26 $ +//** $Date: 96/01/06 18:37:42 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include +#include +#include "h2def.h" +#include "r_local.h" + +void R_DrawColumn (void); +void R_DrawFuzzColumn (void); +void R_DrawAltFuzzColumn(void); +//void R_DrawTranslatedAltFuzzColumn(void); + +typedef struct +{ + int x1, x2; + + int column; + int topclip; + int bottomclip; +} maskdraw_t; + +/* + +Sprite rotation 0 is facing the viewer, rotation 1 is one angle turn CLOCKWISE around the axis. +This is not the same as the angle, which increases counter clockwise +(protractor). There was a lot of stuff grabbed wrong, so I changed it... + +*/ + + +fixed_t pspritescale, pspriteiscale; + +lighttable_t **spritelights; + +// constant arrays used for psprite clipping and initializing clipping +short negonearray[SCREENWIDTH]; +short screenheightarray[SCREENWIDTH]; + +boolean LevelUseFullBright; +/* +=============================================================================== + + INITIALIZATION FUNCTIONS + +=============================================================================== +*/ + +// variables used to look up and range check thing_t sprites patches +spritedef_t *sprites; +int numsprites; + +spriteframe_t sprtemp[30]; +int maxframe; +char *spritename; + + + +/* +================= += += R_InstallSpriteLump += += Local function for R_InitSprites +================= +*/ + +void R_InstallSpriteLump (int lump, unsigned frame, unsigned rotation, boolean flipped) +{ + int r; + + if (frame >= 30 || rotation > 8) + I_Error ("R_InstallSpriteLump: Bad frame characters in lump %i", lump); + + if ((int)frame > maxframe) + maxframe = frame; + + if (rotation == 0) + { +// the lump should be used for all rotations + if (sprtemp[frame].rotate == false) + I_Error ("R_InitSprites: Sprite %s frame %c has multip rot=0 lump" + , spritename, 'A'+frame); + if (sprtemp[frame].rotate == true) + I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump" + , spritename, 'A'+frame); + + sprtemp[frame].rotate = false; + for (r=0 ; r<8 ; r++) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte)flipped; + } + return; + } + +// the lump is only used for one rotation + if (sprtemp[frame].rotate == false) + I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump" + , spritename, 'A'+frame); + + sprtemp[frame].rotate = true; + + rotation--; // make 0 based + if (sprtemp[frame].lump[rotation] != -1) + I_Error ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it" + ,spritename, 'A'+frame, '1'+rotation); + + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte)flipped; +} + +/* +================= += += R_InitSpriteDefs += += Pass a null terminated list of sprite names (4 chars exactly) to be used += Builds the sprite rotation matrixes to account for horizontally flipped += sprites. Will report an error if the lumps are inconsistant += Only called at startup += += Sprite lump names are 4 characters for the actor, a letter for the frame, += and a number for the rotation, A sprite that is flippable will have an += additional letter/number appended. The rotation character can be 0 to += signify no rotations +================= +*/ + +void R_InitSpriteDefs (char **namelist) +{ + char **check; + int i, l, intname, frame, rotation; + int start, end; + +// count the number of sprite names + check = namelist; + while (*check != NULL) + check++; + numsprites = check-namelist; + + if (!numsprites) + return; + + sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); + + start = firstspritelump-1; + end = lastspritelump+1; + +// scan all the lump names for each of the names, noting the highest +// frame letter +// Just compare 4 characters as ints + for (i=0 ; itopdelta != 0xff ; ) + { +// calculate unclipped screen coordinates for post + topscreen = sprtopscreen + spryscale*column->topdelta; + bottomscreen = topscreen + spryscale*column->length; + dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; + dc_yh = (bottomscreen-1)>>FRACBITS; + + if (dc_yh >= mfloorclip[dc_x]) + dc_yh = mfloorclip[dc_x]-1; + if (dc_yl <= mceilingclip[dc_x]) + dc_yl = mceilingclip[dc_x]+1; + + if(dc_yh >= baseclip && baseclip != -1) + dc_yh = baseclip; + + if (dc_yl <= dc_yh) + { + dc_source = (byte *)column + 3; + dc_texturemid = basetexturemid - (column->topdelta<topdelta; + colfunc (); // either R_DrawColumn or R_DrawFuzzColumn + } + column = (column_t *)( (byte *)column + column->length + 4); + } + + dc_texturemid = basetexturemid; +} + + +/* +================ += += R_DrawVisSprite += += mfloorclip and mceilingclip should also be set +================ +*/ + +void R_DrawVisSprite (vissprite_t *vis, int x1, int x2) +{ + column_t *column; + int texturecolumn; + fixed_t frac; + patch_t *patch; + fixed_t baseclip; + + + patch = W_CacheLumpNum(vis->patch+firstspritelump, PU_CACHE); + + dc_colormap = vis->colormap; + +// if(!dc_colormap) +// colfunc = fuzzcolfunc; // NULL colormap = shadow draw + + if(vis->mobjflags&(MF_SHADOW|MF_ALTSHADOW)) + { + if(vis->mobjflags&MF_TRANSLATION) + { + colfunc = R_DrawTranslatedFuzzColumn; + dc_translation = translationtables-256 + +vis->class*((MAXPLAYERS-1)*256)+ + ((vis->mobjflags&MF_TRANSLATION)>>(MF_TRANSSHIFT-8)); + } + else if(vis->mobjflags&MF_SHADOW) + { // Draw using shadow column function + colfunc = fuzzcolfunc; + } + else + { + colfunc = R_DrawAltFuzzColumn; + } + } + else if(vis->mobjflags&MF_TRANSLATION) + { + // Draw using translated column function + colfunc = R_DrawTranslatedColumn; + dc_translation = translationtables-256 + +vis->class*((MAXPLAYERS-1)*256)+ + ((vis->mobjflags&MF_TRANSLATION)>>(MF_TRANSSHIFT-8)); + } + + dc_iscale = abs(vis->xiscale)>>detailshift; + dc_texturemid = vis->texturemid; + frac = vis->startfrac; + spryscale = vis->scale; + + sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale); + + // check to see if vissprite is a weapon + if(vis->psprite) + { + dc_texturemid += FixedMul(((centery-viewheight/2)<xiscale); + sprtopscreen += (viewheight/2-centery)<floorclip && !vis->psprite) + { + sprbotscreen = sprtopscreen+FixedMul(patch->height<floorclip, + spryscale))>>FRACBITS; + } + else + { + baseclip = -1; + } + + for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale) + { + texturecolumn = frac>>FRACBITS; +#ifdef RANGECHECK + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + I_Error ("R_DrawSpriteRange: bad texturecolumn"); +#endif + column = (column_t *) ((byte *)patch + + LONG(patch->columnofs[texturecolumn])); + R_DrawMaskedColumn (column, baseclip); + } + + colfunc = basecolfunc; +} + + + +/* +=================== += += R_ProjectSprite += += Generates a vissprite for a thing if it might be visible += +=================== +*/ + +void R_ProjectSprite (mobj_t *thing) +{ + fixed_t trx,try; + fixed_t gxt,gyt; + fixed_t tx,tz; + fixed_t xscale; + int x1, x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + unsigned rot; + boolean flip; + int index; + vissprite_t *vis; + angle_t ang; + fixed_t iscale; + + if(thing->flags2&MF2_DONTDRAW) + { // Never make a vissprite when MF2_DONTDRAW is flagged. + return; + } + +// +// transform the origin point +// + trx = thing->x - viewx; + try = thing->y - viewy; + + gxt = FixedMul(trx,viewcos); + gyt = -FixedMul(try,viewsin); + tz = gxt-gyt; + + if (tz < MINZ) + return; // thing is behind view plane + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(trx,viewsin); + gyt = FixedMul(try,viewcos); + tx = -(gyt+gxt); + + if (abs(tx)>(tz<<2)) + return; // too far off the side + +// +// decide which patch to use for sprite reletive to player +// +#ifdef RANGECHECK + if ((unsigned)thing->sprite >= numsprites) + I_Error ("R_ProjectSprite: invalid sprite number %i ",thing->sprite); +#endif + sprdef = &sprites[thing->sprite]; +#ifdef RANGECHECK + if ( (thing->frame&FF_FRAMEMASK) >= sprdef->numframes ) + I_Error ("R_ProjectSprite: invalid sprite frame %i : %i " + ,thing->sprite, thing->frame); +#endif + sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { // choose a different rotation based on player view + ang = R_PointToAngle (thing->x, thing->y); + rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29; + lump = sprframe->lump[rot]; + flip = (boolean)sprframe->flip[rot]; + } + else + { // use single rotation for all views + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + } + +// +// calculate edges of the shape +// + tx -= spriteoffset[lump]; + x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS; + if (x1 > viewwidth) + return; // off the right side + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1; + if (x2 < 0) + return; // off the left side + + +// +// store information in a vissprite +// + vis = R_NewVisSprite (); + vis->mobjflags = thing->flags; + vis->psprite = false; + vis->scale = xscale<gx = thing->x; + vis->gy = thing->y; + vis->gz = thing->z; + vis->gzt = thing->z + spritetopoffset[lump]; + if(thing->flags&MF_TRANSLATION) + { + if(thing->player) + { + vis->class = thing->player->class; + } + else + { + vis->class = thing->special1; + } + if(vis->class > 2) + { + vis->class = 0; + } + } + // foot clipping + vis->floorclip = thing->floorclip; + vis->texturemid = vis->gzt-viewz-vis->floorclip; + + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + iscale = FixedDiv (FRACUNIT, xscale); + if (flip) + { + vis->startfrac = spritewidth[lump]-1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + vis->patch = lump; +// +// get light level +// + +// if (thing->flags & MF_SHADOW) +// vis->colormap = NULL; // shadow draw +// else ... + + if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed map + else if(LevelUseFullBright && thing->frame&FF_FULLBRIGHT) + vis->colormap = colormaps; // full bright + else + { // diminished light + index = xscale>>(LIGHTSCALESHIFT-detailshift); + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE-1; + vis->colormap = spritelights[index]; + } +} + + + + +/* +======================== += += R_AddSprites += +======================== +*/ + +void R_AddSprites (sector_t *sec) +{ + mobj_t *thing; + int lightnum; + + if (sec->validcount == validcount) + return; // already added + + sec->validcount = validcount; + + lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+extralight; + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS-1]; + else + spritelights = scalelight[lightnum]; + + + for (thing = sec->thinglist ; thing ; thing = thing->snext) + R_ProjectSprite (thing); +} + + +/* +======================== += += R_DrawPSprite += +======================== +*/ + +// Y-adjustment values for full screen (4 weapons) +int PSpriteSY[NUMCLASSES][NUMWEAPONS] = +{ + { 0, -12*FRACUNIT, -10*FRACUNIT, 10*FRACUNIT }, // Fighter + { -8*FRACUNIT, 10*FRACUNIT, 10*FRACUNIT, 0 }, // Cleric + { 9*FRACUNIT, 20*FRACUNIT, 20*FRACUNIT, 20*FRACUNIT }, // Mage + { 10*FRACUNIT, 10*FRACUNIT, 10*FRACUNIT, 10*FRACUNIT } // Pig +}; + +void R_DrawPSprite (pspdef_t *psp) +{ + fixed_t tx; + int x1, x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis, avis; + + int tempangle; + +// +// decide which patch to use +// +#ifdef RANGECHECK + if ( (unsigned)psp->state->sprite >= numsprites) + I_Error ("R_ProjectSprite: invalid sprite number %i " + , psp->state->sprite); +#endif + sprdef = &sprites[psp->state->sprite]; +#ifdef RANGECHECK + if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: invalid sprite frame %i : %i " + , psp->state->sprite, psp->state->frame); +#endif + sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ]; + + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + +// +// calculate edges of the shape +// + tx = psp->sx-160*FRACUNIT; + + tx -= spriteoffset[lump]; + if(viewangleoffset) + { + tempangle = ((centerxfrac/1024)*(viewangleoffset>>ANGLETOFINESHIFT)); + } + else + { + tempangle = 0; + } + x1 = (centerxfrac + FixedMul (tx,pspritescale)+tempangle ) >>FRACBITS; + if (x1 > viewwidth) + return; // off the right side + tx += spritewidth[lump]; + x2 = ((centerxfrac + FixedMul (tx, pspritescale)+tempangle ) >>FRACBITS) - 1; + if (x2 < 0) + return; // off the left side + +// +// store information in a vissprite +// + vis = &avis; + vis->mobjflags = 0; + vis->class = 0; + vis->psprite = true; + vis->texturemid = (BASEYCENTER<sy-spritetopoffset[lump]); + if(viewheight == SCREENHEIGHT) + { + vis->texturemid -= PSpriteSY[viewplayer->class] + [players[consoleplayer].readyweapon]; + } + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + vis->scale = pspritescale<xiscale = -pspriteiscale; + vis->startfrac = spritewidth[lump]-1; + } + else + { + vis->xiscale = pspriteiscale; + vis->startfrac = 0; + } + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + vis->patch = lump; + + if(viewplayer->powers[pw_invulnerability] && viewplayer->class + == PCLASS_CLERIC) + { + vis->colormap = spritelights[MAXLIGHTSCALE-1]; + if(viewplayer->powers[pw_invulnerability] > 4*32) + { + if(viewplayer->mo->flags2&MF2_DONTDRAW) + { // don't draw the psprite + vis->mobjflags |= MF_SHADOW; + } + else if(viewplayer->mo->flags&MF_SHADOW) + { + vis->mobjflags |= MF_ALTSHADOW; + } + } + else if(viewplayer->powers[pw_invulnerability]&8) + { + vis->mobjflags |= MF_SHADOW; + } + } + else if(fixedcolormap) + { + // Fixed color + vis->colormap = fixedcolormap; + } + else if(psp->state->frame & FF_FULLBRIGHT) + { + // Full bright + vis->colormap = colormaps; + } + else + { + // local light + vis->colormap = spritelights[MAXLIGHTSCALE-1]; + } + R_DrawVisSprite(vis, vis->x1, vis->x2); +} + +/* +======================== += += R_DrawPlayerSprites += +======================== +*/ + +void R_DrawPlayerSprites (void) +{ + int i, lightnum; + pspdef_t *psp; + +// +// get light level +// + lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + +extralight; + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS-1]; + else + spritelights = scalelight[lightnum]; +// +// clip to screen bounds +// + mfloorclip = screenheightarray; + mceilingclip = negonearray; + +// +// add all active psprites +// + for (i=0, psp=viewplayer->psprites ; istate) + R_DrawPSprite (psp); + +} + + +/* +======================== += += R_SortVisSprites += +======================== +*/ + +vissprite_t vsprsortedhead; + +void R_SortVisSprites (void) +{ + int i, count; + vissprite_t *ds, *best; + vissprite_t unsorted; + fixed_t bestscale; + + count = vissprite_p - vissprites; + + unsorted.next = unsorted.prev = &unsorted; + if (!count) + return; + + for (ds=vissprites ; dsnext = ds+1; + ds->prev = ds-1; + } + vissprites[0].prev = &unsorted; + unsorted.next = &vissprites[0]; + (vissprite_p-1)->next = &unsorted; + unsorted.prev = vissprite_p-1; + +// +// pull the vissprites out by scale +// + best = 0; // shut up the compiler warning + vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; + for (i=0 ; inext) + { + if (ds->scale < bestscale) + { + bestscale = ds->scale; + best = ds; + } + } + best->next->prev = best->prev; + best->prev->next = best->next; + best->next = &vsprsortedhead; + best->prev = vsprsortedhead.prev; + vsprsortedhead.prev->next = best; + vsprsortedhead.prev = best; + } +} + + + +/* +======================== += += R_DrawSprite += +======================== +*/ + +void R_DrawSprite (vissprite_t *spr) +{ + drawseg_t *ds; + short clipbot[SCREENWIDTH], cliptop[SCREENWIDTH]; + int x, r1, r2; + fixed_t scale, lowscale; + int silhouette; + + for (x = spr->x1 ; x<=spr->x2 ; x++) + clipbot[x] = cliptop[x] = -2; + +// +// scan drawsegs from end to start for obscuring segs +// the first drawseg that has a greater scale is the clip seg +// + for (ds=ds_p-1 ; ds >= drawsegs ; ds--) + { + // + // determine if the drawseg obscures the sprite + // + if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + (!ds->silhouette && !ds->maskedtexturecol) ) + continue; // doesn't cover sprite + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale || ( lowscale < spr->scale + && !R_PointOnSegSide (spr->gx, spr->gy, ds->curline) ) ) + { + if (ds->maskedtexturecol) // masked mid texture + R_RenderMaskedSegRange (ds, r1, r2); + continue; // seg is behind sprite + } + +// +// clip this piece of the sprite +// + silhouette = ds->silhouette; + if (spr->gz >= ds->bsilheight) + silhouette &= ~SIL_BOTTOM; + if (spr->gzt <= ds->tsilheight) + silhouette &= ~SIL_TOP; + + if (silhouette == 1) + { // bottom sil + for (x=r1 ; x<=r2 ; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + } + else if (silhouette == 2) + { // top sil + for (x=r1 ; x<=r2 ; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + else if (silhouette == 3) + { // both + for (x=r1 ; x<=r2 ; x++) + { + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + } + + } + +// +// all clipping has been performed, so draw the sprite +// + +// check for unclipped columns + for (x = spr->x1 ; x<=spr->x2 ; x++) + { + if (clipbot[x] == -2) + clipbot[x] = viewheight; + if (cliptop[x] == -2) + cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr, spr->x1, spr->x2); +} + + +/* +======================== += += R_DrawMasked += +======================== +*/ + +void R_DrawMasked (void) +{ + vissprite_t *spr; + drawseg_t *ds; + + R_SortVisSprites (); + + if (vissprite_p > vissprites) + { + // draw all vissprites back to front + + for (spr = vsprsortedhead.next ; spr != &vsprsortedhead + ; spr=spr->next) + R_DrawSprite (spr); + } + +// +// render any remaining masked mid textures +// + for (ds=ds_p-1 ; ds >= drawsegs ; ds--) + if (ds->maskedtexturecol) + R_RenderMaskedSegRange (ds, ds->x1, ds->x2); + +// +// draw the psprites on top of everything +// +// Added for the sideviewing with an external device + if (viewangleoffset <= 1024<= + -1024<>1)+ \ + (((a)&4)<<5)+ \ + (((a)&8)<<2)+ \ + (((a)&16)>>3)+ \ + (((a)&32)<<1)+ \ + (((a)&64)>>3)+ \ + (((a)&128)>>3)) + +// TYPES ------------------------------------------------------------------- + +typedef struct Cheat_s +{ + void (*func)(player_t *player, struct Cheat_s *cheat); + byte *sequence; + byte *pos; + int args[2]; + int currentArg; +} Cheat_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void SB_PaletteFlash(boolean forceChange); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void DrawSoundInfo(void); +static void DrINumber(signed int val, int x, int y); +static void DrRedINumber(signed int val, int x, int y); +static void DrBNumber(signed int val, int x, int y); +static void DrawCommonBar(void); +static void DrawMainBar(void); +static void DrawInventoryBar(void); +static void DrawKeyBar(void); +static void DrawWeaponPieces(void); +static void DrawFullScreenStuff(void); +static void DrawAnimatedIcons(void); +static boolean HandleCheats(byte key); +static boolean CheatAddKey(Cheat_t *cheat, byte key, boolean *eat); +static void CheatGodFunc(player_t *player, Cheat_t *cheat); +static void CheatNoClipFunc(player_t *player, Cheat_t *cheat); +static void CheatWeaponsFunc(player_t *player, Cheat_t *cheat); +static void CheatHealthFunc(player_t *player, Cheat_t *cheat); +static void CheatKeysFunc(player_t *player, Cheat_t *cheat); +static void CheatSoundFunc(player_t *player, Cheat_t *cheat); +static void CheatTickerFunc(player_t *player, Cheat_t *cheat); +static void CheatArtifactAllFunc(player_t *player, Cheat_t *cheat); +static void CheatPuzzleFunc(player_t *player, Cheat_t *cheat); +static void CheatWarpFunc(player_t *player, Cheat_t *cheat); +static void CheatPigFunc(player_t *player, Cheat_t *cheat); +static void CheatMassacreFunc(player_t *player, Cheat_t *cheat); +static void CheatIDKFAFunc(player_t *player, Cheat_t *cheat); +static void CheatQuickenFunc1(player_t *player, Cheat_t *cheat); +static void CheatQuickenFunc2(player_t *player, Cheat_t *cheat); +static void CheatQuickenFunc3(player_t *player, Cheat_t *cheat); +static void CheatClassFunc1(player_t *player, Cheat_t *cheat); +static void CheatClassFunc2(player_t *player, Cheat_t *cheat); +static void CheatInitFunc(player_t *player, Cheat_t *cheat); +static void CheatInitFunc(player_t *player, Cheat_t *cheat); +static void CheatVersionFunc(player_t *player, Cheat_t *cheat); +static void CheatDebugFunc(player_t *player, Cheat_t *cheat); +static void CheatScriptFunc1(player_t *player, Cheat_t *cheat); +static void CheatScriptFunc2(player_t *player, Cheat_t *cheat); +static void CheatScriptFunc3(player_t *player, Cheat_t *cheat); +static void CheatRevealFunc(player_t *player, Cheat_t *cheat); +static void CheatTrackFunc1(player_t *player, Cheat_t *cheat); +static void CheatTrackFunc2(player_t *player, Cheat_t *cheat); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern byte *screen; +extern int ArmorIncrement[NUMCLASSES][NUMARMOR]; +extern int AutoArmorSave[NUMCLASSES]; + +#ifdef __WATCOMC__ +extern boolean i_CDMusic; +extern int i_CDMusicLength; +extern int i_CDTrack; +extern int i_CDCurrentTrack; +extern int oldTic; +#endif + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +boolean DebugSound; // Debug flag for displaying sound info +boolean inventory; +int curpos; +int inv_ptr; +int ArtifactFlash; + +#ifndef __WATCOMC__ +boolean i_CDMusic; // in Watcom, defined in i_ibm +#endif + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static byte CheatLookup[256]; +static int HealthMarker; +//static int ChainWiggle; +static player_t *CPlayer; +static int SpinFlylump; +static int SpinMinotaurLump; +static int SpinSpeedLump; +static int SpinDefenseLump; + +static int FontBNumBase; +static int PlayPalette; + +static patch_t *PatchH2BAR; +static patch_t *PatchH2TOP; +static patch_t *PatchLFEDGE; +static patch_t *PatchRTEDGE; +static patch_t *PatchARMCLEAR; +static patch_t *PatchARTICLEAR; +static patch_t *PatchMANACLEAR; +static patch_t *PatchKILLS; +static patch_t *PatchMANAVIAL1; +static patch_t *PatchMANAVIAL2; +static patch_t *PatchMANAVIALDIM1; +static patch_t *PatchMANAVIALDIM2; +static patch_t *PatchMANADIM1; +static patch_t *PatchMANADIM2; +static patch_t *PatchMANABRIGHT1; +static patch_t *PatchMANABRIGHT2; +static patch_t *PatchCHAIN; +static patch_t *PatchSTATBAR; +static patch_t *PatchKEYBAR; +static patch_t *PatchLIFEGEM; +static patch_t *PatchSELECTBOX; +static patch_t *PatchINumbers[10]; +static patch_t *PatchNEGATIVE; +static patch_t *PatchSmNumbers[10]; +static patch_t *PatchINVBAR; +static patch_t *PatchWEAPONSLOT; +static patch_t *PatchWEAPONFULL; +static patch_t *PatchPIECE1; +static patch_t *PatchPIECE2; +static patch_t *PatchPIECE3; +static patch_t *PatchINVLFGEM1; +static patch_t *PatchINVLFGEM2; +static patch_t *PatchINVRTGEM1; +static patch_t *PatchINVRTGEM2; + +// Toggle god mode +static byte CheatGodSeq[] = +{ + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('n'), + 0xff +}; + +// Toggle no clipping mode +static byte CheatNoClipSeq[] = +{ + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('p'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + 0xff +}; + +// Get all weapons and mana +static byte CheatWeaponsSeq[] = +{ + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('a'), + 0xff +}; + +// Get full health +static byte CheatHealthSeq[] = +{ + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('l'), + CHEAT_ENCRYPT('u'), + CHEAT_ENCRYPT('b'), + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('d'), + 0xff +}; + +// Get all keys +static byte CheatKeysSeq[] = +{ + CHEAT_ENCRYPT('l'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('h'), + 0xff, 0 +}; + +// Toggle sound debug info +static byte CheatSoundSeq[] = +{ + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('e'), + 0xff +}; + +// Toggle ticker +static byte CheatTickerSeq[] = +{ + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + 0xff, 0 +}; + +// Get all artifacts +static byte CheatArtifactAllSeq[] = +{ + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('d'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('a'), + 0xff, 0 +}; + +// Get all puzzle pieces +static byte CheatPuzzleSeq[] = +{ + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('h'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('l'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('k'), + 0xff, 0 +}; + +// Warp to new level +static byte CheatWarpSeq[] = +{ + CHEAT_ENCRYPT('v'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('t'), + 0, 0, 0xff, 0 +}; + +// Become a pig +static byte CheatPigSeq[] = +{ + CHEAT_ENCRYPT('d'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('l'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('v'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('e'), + 0xff, 0 +}; + +// Kill all monsters +static byte CheatMassacreSeq[] = +{ + CHEAT_ENCRYPT('b'), + CHEAT_ENCRYPT('u'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('h'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + 0xff, 0 +}; + +static byte CheatIDKFASeq[] = +{ + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('n'), + 0xff, 0 +}; + +static byte CheatQuickenSeq1[] = +{ + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + 0xff, 0 +}; + +static byte CheatQuickenSeq2[] = +{ + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + 0xff, 0 +}; + +static byte CheatQuickenSeq3[] = +{ + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('k'), + 0xff, 0 +}; + +// New class +static byte CheatClass1Seq[] = +{ + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('h'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('d'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('w'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + 0xff, 0 +}; + +static byte CheatClass2Seq[] = +{ + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('h'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('d'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('w'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('t'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + 0, 0xff, 0 +}; + +static byte CheatInitSeq[] = +{ + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('i'), + CHEAT_ENCRYPT('t'), + 0xff, 0 +}; + +static byte CheatVersionSeq[] = +{ + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('j'), + CHEAT_ENCRYPT('o'), + CHEAT_ENCRYPT('n'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('s'), + 0xff, 0 +}; + +static byte CheatDebugSeq[] = +{ + CHEAT_ENCRYPT('w'), + CHEAT_ENCRYPT('h'), + CHEAT_ENCRYPT('e'), + CHEAT_ENCRYPT('r'), + CHEAT_ENCRYPT('e'), + 0xff, 0 +}; + +static byte CheatScriptSeq1[] = +{ + CHEAT_ENCRYPT('p'), + CHEAT_ENCRYPT('u'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('e'), + 0xff, 0 +}; + +static byte CheatScriptSeq2[] = +{ + CHEAT_ENCRYPT('p'), + CHEAT_ENCRYPT('u'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('e'), + 0, 0xff, 0 +}; + +static byte CheatScriptSeq3[] = +{ + CHEAT_ENCRYPT('p'), + CHEAT_ENCRYPT('u'), + CHEAT_ENCRYPT('k'), + CHEAT_ENCRYPT('e'), + 0, 0, 0xff, +}; + +static byte CheatRevealSeq[] = +{ + CHEAT_ENCRYPT('m'), + CHEAT_ENCRYPT('a'), + CHEAT_ENCRYPT('p'), + CHEAT_ENCRYPT('s'), + CHEAT_ENCRYPT('c'), + CHEAT_ENCRYPT('o'), + 0xff, 0 +}; + +static byte CheatTrackSeq1[] = +{ + CHEAT_ENCRYPT('`'), + 0xff, 0 +}; + +static byte CheatTrackSeq2[] = +{ + CHEAT_ENCRYPT('`'), + 0, 0, 0xff, 0 +}; + +static Cheat_t Cheats[] = +{ + { CheatTrackFunc1, CheatTrackSeq1, NULL, 0, 0, 0 }, + { CheatTrackFunc2, CheatTrackSeq2, NULL, 0, 0, 0 }, + { CheatGodFunc, CheatGodSeq, NULL, 0, 0, 0 }, + { CheatNoClipFunc, CheatNoClipSeq, NULL, 0, 0, 0 }, + { CheatWeaponsFunc, CheatWeaponsSeq, NULL, 0, 0, 0 }, + { CheatHealthFunc, CheatHealthSeq, NULL, 0, 0, 0 }, + { CheatKeysFunc, CheatKeysSeq, NULL, 0, 0, 0 }, + { CheatSoundFunc, CheatSoundSeq, NULL, 0, 0, 0 }, + { CheatTickerFunc, CheatTickerSeq, NULL, 0, 0, 0 }, + { CheatArtifactAllFunc, CheatArtifactAllSeq, NULL, 0, 0, 0 }, + { CheatPuzzleFunc, CheatPuzzleSeq, NULL, 0, 0, 0 }, + { CheatWarpFunc, CheatWarpSeq, NULL, 0, 0, 0 }, + { CheatPigFunc, CheatPigSeq, NULL, 0, 0, 0 }, + { CheatMassacreFunc, CheatMassacreSeq, NULL, 0, 0, 0 }, + { CheatIDKFAFunc, CheatIDKFASeq, NULL, 0, 0, 0 }, + { CheatQuickenFunc1, CheatQuickenSeq1, NULL, 0, 0, 0 }, + { CheatQuickenFunc2, CheatQuickenSeq2, NULL, 0, 0, 0 }, + { CheatQuickenFunc3, CheatQuickenSeq3, NULL, 0, 0, 0 }, + { CheatClassFunc1, CheatClass1Seq, NULL, 0, 0, 0 }, + { CheatClassFunc2, CheatClass2Seq, NULL, 0, 0, 0 }, + { CheatInitFunc, CheatInitSeq, NULL, 0, 0, 0 }, + { CheatVersionFunc, CheatVersionSeq, NULL, 0, 0, 0 }, + { CheatDebugFunc, CheatDebugSeq, NULL, 0, 0, 0 }, + { CheatScriptFunc1, CheatScriptSeq1, NULL, 0, 0, 0 }, + { CheatScriptFunc2, CheatScriptSeq2, NULL, 0, 0, 0 }, + { CheatScriptFunc3, CheatScriptSeq3, NULL, 0, 0, 0 }, + { CheatRevealFunc, CheatRevealSeq, NULL, 0, 0, 0 }, + { NULL, NULL, NULL, 0, 0, 0 } // Terminator +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SB_Init +// +//========================================================================== + +void SB_Init(void) +{ + int i; + int startLump; + + PatchH2BAR = W_CacheLumpName("H2BAR", PU_STATIC); + PatchH2TOP = W_CacheLumpName("H2TOP", PU_STATIC); + PatchINVBAR = W_CacheLumpName("INVBAR", PU_STATIC); + PatchLFEDGE = W_CacheLumpName("LFEDGE", PU_STATIC); + PatchRTEDGE = W_CacheLumpName("RTEDGE", PU_STATIC); + PatchSTATBAR = W_CacheLumpName("STATBAR", PU_STATIC); + PatchKEYBAR = W_CacheLumpName("KEYBAR", PU_STATIC); + PatchSELECTBOX = W_CacheLumpName("SELECTBOX", PU_STATIC); + PatchARTICLEAR = W_CacheLumpName("ARTICLS", PU_STATIC); + PatchARMCLEAR = W_CacheLumpName("ARMCLS", PU_STATIC); + PatchMANACLEAR = W_CacheLumpName("MANACLS", PU_STATIC); + PatchMANAVIAL1 = W_CacheLumpName("MANAVL1", PU_STATIC); + PatchMANAVIAL2 = W_CacheLumpName("MANAVL2", PU_STATIC); + PatchMANAVIALDIM1 = W_CacheLumpName("MANAVL1D", PU_STATIC); + PatchMANAVIALDIM2 = W_CacheLumpName("MANAVL2D", PU_STATIC); + PatchMANADIM1 = W_CacheLumpName("MANADIM1", PU_STATIC); + PatchMANADIM2 = W_CacheLumpName("MANADIM2", PU_STATIC); + PatchMANABRIGHT1 = W_CacheLumpName("MANABRT1", PU_STATIC); + PatchMANABRIGHT2 = W_CacheLumpName("MANABRT2", PU_STATIC); + PatchINVLFGEM1 = W_CacheLumpName("invgeml1", PU_STATIC); + PatchINVLFGEM2 = W_CacheLumpName("invgeml2", PU_STATIC); + PatchINVRTGEM1 = W_CacheLumpName("invgemr1", PU_STATIC); + PatchINVRTGEM2 = W_CacheLumpName("invgemr2", PU_STATIC); + +// PatchCHAINBACK = W_CacheLumpName("CHAINBACK", PU_STATIC); + startLump = W_GetNumForName("IN0"); + for(i = 0; i < 10; i++) + { + PatchINumbers[i] = W_CacheLumpNum(startLump+i, PU_STATIC); + } + PatchNEGATIVE = W_CacheLumpName("NEGNUM", PU_STATIC); + FontBNumBase = W_GetNumForName("FONTB16"); + startLump = W_GetNumForName("SMALLIN0"); + for(i = 0; i < 10; i++) + { + PatchSmNumbers[i] = W_CacheLumpNum(startLump+i, PU_STATIC); + } + PlayPalette = W_GetNumForName("PLAYPAL"); + SpinFlylump = W_GetNumForName("SPFLY0"); + SpinMinotaurLump = W_GetNumForName("SPMINO0"); + SpinSpeedLump = W_GetNumForName("SPBOOT0"); + SpinDefenseLump = W_GetNumForName("SPSHLD0"); + + for(i = 0; i < 256; i++) + { + CheatLookup[i] = CHEAT_ENCRYPT(i); + } + + if(deathmatch) + { + PatchKILLS = W_CacheLumpName("KILLS", PU_STATIC); + } + SB_SetClassData(); +} + +//========================================================================== +// +// SB_SetClassData +// +//========================================================================== + +void SB_SetClassData(void) +{ + int class; + + class = PlayerClass[consoleplayer]; // original player class (not pig) + PatchWEAPONSLOT = W_CacheLumpNum(W_GetNumForName("wpslot0") + +class, PU_STATIC); + PatchWEAPONFULL = W_CacheLumpNum(W_GetNumForName("wpfull0") + +class, PU_STATIC); + PatchPIECE1 = W_CacheLumpNum(W_GetNumForName("wpiecef1") + +class, PU_STATIC); + PatchPIECE2 = W_CacheLumpNum(W_GetNumForName("wpiecef2") + +class, PU_STATIC); + PatchPIECE3 = W_CacheLumpNum(W_GetNumForName("wpiecef3") + +class, PU_STATIC); + PatchCHAIN = W_CacheLumpNum(W_GetNumForName("chain") + +class, PU_STATIC); + if(!netgame) + { // single player game uses red life gem (the second gem) + PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("lifegem") + +MAXPLAYERS*class+1, PU_STATIC); + } + else + { + PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("lifegem") + +MAXPLAYERS*class+consoleplayer, PU_STATIC); + } + SB_state = -1; + UpdateState |= I_FULLSCRN; +} + +//========================================================================== +// +// SB_Ticker +// +//========================================================================== + +void SB_Ticker(void) +{ + int delta; + int curHealth; + + curHealth = players[consoleplayer].mo->health; + if(curHealth < 0) + { + curHealth = 0; + } + if(curHealth < HealthMarker) + { + delta = (HealthMarker-curHealth)>>2; + if(delta < 1) + { + delta = 1; + } + else if(delta > 6) + { + delta = 6; + } + HealthMarker -= delta; + } + else if(curHealth > HealthMarker) + { + delta = (curHealth-HealthMarker)>>2; + if(delta < 1) + { + delta = 1; + } + else if(delta > 6) + { + delta = 6; + } + HealthMarker += delta; + } +} + +//========================================================================== +// +// DrINumber +// +// Draws a three digit number. +// +//========================================================================== + +static void DrINumber(signed int val, int x, int y) +{ + patch_t *patch; + int oldval; + + oldval = val; + if(val < 0) + { + val = -val; + if(val > 99) + { + val = 99; + } + if(val > 9) + { + patch = PatchINumbers[val/10]; + V_DrawPatch(x+8, y, patch); + V_DrawPatch(x, y, PatchNEGATIVE); + } + else + { + V_DrawPatch(x+8, y, PatchNEGATIVE); + } + val = val%10; + patch = PatchINumbers[val]; + V_DrawPatch(x+16, y, patch); + return; + } + if(val > 99) + { + patch = PatchINumbers[val/100]; + V_DrawPatch(x, y, patch); + } + val = val%100; + if(val > 9 || oldval > 99) + { + patch = PatchINumbers[val/10]; + V_DrawPatch(x+8, y, patch); + } + val = val%10; + patch = PatchINumbers[val]; + V_DrawPatch(x+16, y, patch); +} + +//========================================================================== +// +// DrRedINumber +// +// Draws a three digit number using the red font +// +//========================================================================== + +static void DrRedINumber(signed int val, int x, int y) +{ + patch_t *patch; + int oldval; + + oldval = val; + if(val < 0) + { + val = 0; + } + if(val > 99) + { + patch = W_CacheLumpNum(W_GetNumForName("inred0")+val/100, PU_CACHE); + V_DrawPatch(x, y, patch); + } + val = val%100; + if(val > 9 || oldval > 99) + { + patch = W_CacheLumpNum(W_GetNumForName("inred0")+val/10, PU_CACHE); + V_DrawPatch(x+8, y, patch); + } + val = val%10; + patch = W_CacheLumpNum(W_GetNumForName("inred0")+val, PU_CACHE); + V_DrawPatch(x+16, y, patch); +} + +//========================================================================== +// +// DrBNumber +// +// Draws a three digit number using FontB +// +//========================================================================== + +static void DrBNumber(signed int val, int x, int y) +{ + patch_t *patch; + int xpos; + int oldval; + + oldval = val; + xpos = x; + if(val < 0) + { + val = 0; + } + if(val > 99) + { + patch = W_CacheLumpNum(FontBNumBase+val/100, PU_CACHE); + V_DrawShadowedPatch(xpos+6-patch->width/2, y, patch); + } + val = val%100; + xpos += 12; + if(val > 9 || oldval > 99) + { + patch = W_CacheLumpNum(FontBNumBase+val/10, PU_CACHE); + V_DrawShadowedPatch(xpos+6-patch->width/2, y, patch); + } + val = val%10; + xpos += 12; + patch = W_CacheLumpNum(FontBNumBase+val, PU_CACHE); + V_DrawShadowedPatch(xpos+6-patch->width/2, y, patch); +} + +//========================================================================== +// +// DrSmallNumber +// +// Draws a small two digit number. +// +//========================================================================== + +static void DrSmallNumber(int val, int x, int y) +{ + patch_t *patch; + + if(val <= 0) + { + return; + } + if(val > 999) + { + val %= 1000; + } + if(val > 99) + { + patch = PatchSmNumbers[val/100]; + V_DrawPatch(x, y, patch); + patch = PatchSmNumbers[(val%100)/10]; + V_DrawPatch(x+4, y, patch); + } + else if(val > 9) + { + patch = PatchSmNumbers[val/10]; + V_DrawPatch(x+4, y, patch); + } + val %= 10; + patch = PatchSmNumbers[val]; + V_DrawPatch(x+8, y, patch); +} + +/* +//========================================================================== +// +// ShadeLine +// +//========================================================================== + +static void ShadeLine(int x, int y, int height, int shade) +{ + byte *dest; + byte *shades; + + shades = colormaps+9*256+shade*2*256; + dest = screen+y*SCREENWIDTH+x; + while(height--) + { + *(dest) = *(shades+*dest); + dest += SCREENWIDTH; + } +} + +//========================================================================== +// +// ShadeChain +// +//========================================================================== + +static void ShadeChain(void) +{ + int i; + + for(i = 0; i < 16; i++) + { + ShadeLine(277+i, 190, 10, i/2); + ShadeLine(19+i, 190, 10, 7-(i/2)); + } +} +*/ + +//========================================================================== +// +// DrawSoundInfo +// +// Displays sound debugging information. +// +//========================================================================== + +static void DrawSoundInfo(void) +{ + int i; + SoundInfo_t s; + ChanInfo_t *c; + char text[32]; + int x; + int y; + int xPos[7] = {1, 75, 112, 156, 200, 230, 260}; + + if(leveltime&16) + { + MN_DrTextA("*** SOUND DEBUG INFO ***", xPos[0], 20); + } + S_GetChannelInfo(&s); + if(s.channelCount == 0) + { + return; + } + x = 0; + MN_DrTextA("NAME", xPos[x++], 30); + MN_DrTextA("MO.T", xPos[x++], 30); + MN_DrTextA("MO.X", xPos[x++], 30); + MN_DrTextA("MO.Y", xPos[x++], 30); + MN_DrTextA("ID", xPos[x++], 30); + MN_DrTextA("PRI", xPos[x++], 30); + MN_DrTextA("DIST", xPos[x++], 30); + for(i = 0; i < s.channelCount; i++) + { + c = &s.chan[i]; + x = 0; + y = 40+i*10; + if(c->mo == NULL) + { // Channel is unused + MN_DrTextA("------", xPos[0], y); + continue; + } + sprintf(text, "%s", c->name); + M_ForceUppercase(text); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->mo->type); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->mo->x>>FRACBITS); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->mo->y>>FRACBITS); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->id); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->priority); + MN_DrTextA(text, xPos[x++], y); + sprintf(text, "%d", c->distance); + MN_DrTextA(text, xPos[x++], y); + } + UpdateState |= I_FULLSCRN; + BorderNeedRefresh = true; +} + +//========================================================================== +// +// SB_Drawer +// +//========================================================================== + +char patcharti[][10] = +{ + { "ARTIBOX" }, // none + { "ARTIINVU" }, // invulnerability + { "ARTIPTN2" }, // health + { "ARTISPHL" }, // superhealth + { "ARTIHRAD" }, // healing radius + { "ARTISUMN" }, // summon maulator + { "ARTITRCH" }, // torch + { "ARTIPORK" }, // egg + { "ARTISOAR" }, // fly + { "ARTIBLST" }, // blast radius + { "ARTIPSBG" }, // poison bag + { "ARTITELO" }, // teleport other + { "ARTISPED" }, // speed + { "ARTIBMAN" }, // boost mana + { "ARTIBRAC" }, // boost armor + { "ARTIATLP" }, // teleport + { "ARTISKLL" }, // arti_puzzskull + { "ARTIBGEM" }, // arti_puzzgembig + { "ARTIGEMR" }, // arti_puzzgemred + { "ARTIGEMG" }, // arti_puzzgemgreen1 + { "ARTIGMG2" }, // arti_puzzgemgreen2 + { "ARTIGEMB" }, // arti_puzzgemblue1 + { "ARTIGMB2" }, // arti_puzzgemblue2 + { "ARTIBOK1" }, // arti_puzzbook1 + { "ARTIBOK2" }, // arti_puzzbook2 + { "ARTISKL2" }, // arti_puzzskull2 + { "ARTIFWEP" }, // arti_puzzfweapon + { "ARTICWEP" }, // arti_puzzcweapon + { "ARTIMWEP" }, // arti_puzzmweapon + { "ARTIGEAR" }, // arti_puzzgear1 + { "ARTIGER2" }, // arti_puzzgear2 + { "ARTIGER3" }, // arti_puzzgear3 + { "ARTIGER4" }, // arti_puzzgear4 +}; + +int SB_state = -1; +static int oldarti = 0; +static int oldartiCount = 0; +static int oldfrags = -9999; +static int oldmana1 = -1; +static int oldmana2 = -1; +static int oldarmor = -1; +static int oldhealth = -1; +static int oldlife = -1; +static int oldpieces = -1; +static int oldweapon = -1; +static int oldkeys = -1; + +extern boolean automapactive; + +void SB_Drawer(void) +{ + // Sound info debug stuff + if(DebugSound == true) + { + DrawSoundInfo(); + } + CPlayer = &players[consoleplayer]; + if(viewheight == SCREENHEIGHT && !automapactive) + { + DrawFullScreenStuff(); + SB_state = -1; + } + else + { + if(SB_state == -1) + { + V_DrawPatch(0, 134, PatchH2BAR); + oldhealth = -1; + } + DrawCommonBar(); + if(!inventory) + { + if(SB_state != 0) + { + // Main interface + if(!automapactive) + { + V_DrawPatch(38, 162, PatchSTATBAR); + } + else + { + V_DrawPatch(38, 162, PatchKEYBAR); + } + oldarti = 0; + oldmana1 = -1; + oldmana2 = -1; + oldarmor = -1; + oldpieces = -1; + oldfrags = -9999; //can't use -1, 'cuz of negative frags + oldlife = -1; + oldweapon = -1; + oldkeys = -1; + } + if(!automapactive) + { + DrawMainBar(); + } + else + { + DrawKeyBar(); + } + SB_state = 0; + } + else + { + DrawInventoryBar(); + SB_state = 1; + } + } + SB_PaletteFlash(false); + DrawAnimatedIcons(); +} + +//========================================================================== +// +// DrawAnimatedIcons +// +//========================================================================== + +static void DrawAnimatedIcons(void) +{ + int frame; + static boolean hitCenterFrame; + + // Wings of wrath + if(CPlayer->powers[pw_flight]) + { + if(CPlayer->powers[pw_flight] > BLINKTHRESHOLD + || !(CPlayer->powers[pw_flight]&16)) + { + frame = (leveltime/3)&15; + if(CPlayer->mo->flags2&MF2_FLY) + { + if(hitCenterFrame && (frame != 15 && frame != 0)) + { + V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump+15, + PU_CACHE)); + } + else + { + V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump+frame, + PU_CACHE)); + hitCenterFrame = false; + } + } + else + { + if(!hitCenterFrame && (frame != 15 && frame != 0)) + { + V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump+frame, + PU_CACHE)); + hitCenterFrame = false; + } + else + { + V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump+15, + PU_CACHE)); + hitCenterFrame = true; + } + } + } + BorderTopRefresh = true; + UpdateState |= I_MESSAGES; + } + + // Speed Boots + if(CPlayer->powers[pw_speed]) + { + if(CPlayer->powers[pw_speed] > BLINKTHRESHOLD + || !(CPlayer->powers[pw_speed]&16)) + { + frame = (leveltime/3)&15; + V_DrawPatch(60, 19, W_CacheLumpNum(SpinSpeedLump+frame, + PU_CACHE)); + } + BorderTopRefresh = true; + UpdateState |= I_MESSAGES; + } + + // Defensive power + if(CPlayer->powers[pw_invulnerability]) + { + if(CPlayer->powers[pw_invulnerability] > BLINKTHRESHOLD + || !(CPlayer->powers[pw_invulnerability]&16)) + { + frame = (leveltime/3)&15; + V_DrawPatch(260, 19, W_CacheLumpNum(SpinDefenseLump+frame, + PU_CACHE)); + } + BorderTopRefresh = true; + UpdateState |= I_MESSAGES; + } + + // Minotaur Active + if(CPlayer->powers[pw_minotaur]) + { + if(CPlayer->powers[pw_minotaur] > BLINKTHRESHOLD + || !(CPlayer->powers[pw_minotaur]&16)) + { + frame = (leveltime/3)&15; + V_DrawPatch(300, 19, W_CacheLumpNum(SpinMinotaurLump+frame, + PU_CACHE)); + } + BorderTopRefresh = true; + UpdateState |= I_MESSAGES; + } +} + +//========================================================================== +// +// SB_PaletteFlash +// +// Sets the new palette based upon the current values of +// consoleplayer->damagecount and consoleplayer->bonuscount. +// +//========================================================================== + +void SB_PaletteFlash(boolean forceChange) +{ + static int sb_palette = 0; + int palette; + byte *pal; + + if(forceChange) + { + sb_palette = -1; + } + if(gamestate == GS_LEVEL) + { + CPlayer = &players[consoleplayer]; + if(CPlayer->poisoncount) + { + palette = 0; + palette = (CPlayer->poisoncount+7)>>3; + if(palette >= NUMPOISONPALS) + { + palette = NUMPOISONPALS-1; + } + palette += STARTPOISONPALS; + } + else if(CPlayer->damagecount) + { + palette = (CPlayer->damagecount+7)>>3; + if(palette >= NUMREDPALS) + { + palette = NUMREDPALS-1; + } + palette += STARTREDPALS; + } + else if(CPlayer->bonuscount) + { + palette = (CPlayer->bonuscount+7)>>3; + if(palette >= NUMBONUSPALS) + { + palette = NUMBONUSPALS-1; + } + palette += STARTBONUSPALS; + } + else if(CPlayer->mo->flags2&MF2_ICEDAMAGE) + { // Frozen player + palette = STARTICEPAL; + } + else + { + palette = 0; + } + } + else + { + palette = 0; + } + if(palette != sb_palette) + { + sb_palette = palette; + pal = (byte *)W_CacheLumpNum(PlayPalette, PU_CACHE)+palette*768; + I_SetPalette(pal); + } +} + +//========================================================================== +// +// DrawCommonBar +// +//========================================================================== + +void DrawCommonBar(void) +{ + int healthPos; + + V_DrawPatch(0, 134, PatchH2TOP); + + if(oldhealth != HealthMarker) + { + oldhealth = HealthMarker; + healthPos = HealthMarker; + if(healthPos < 0) + { + healthPos = 0; + } + if(healthPos > 100) + { + healthPos = 100; + } + V_DrawPatch(28+(((healthPos*196)/100)%9), 193, PatchCHAIN); + V_DrawPatch(7+((healthPos*11)/5), 193, PatchLIFEGEM); + V_DrawPatch(0, 193, PatchLFEDGE); + V_DrawPatch(277, 193, PatchRTEDGE); +// ShadeChain(); + UpdateState |= I_STATBAR; + } +} + +//========================================================================== +// +// DrawMainBar +// +//========================================================================== + +void DrawMainBar(void) +{ + int i; + int temp; + patch_t *manaPatch1, *manaPatch2; + patch_t *manaVialPatch1, *manaVialPatch2; + + manaPatch1 = NULL; + manaPatch2 = NULL; + manaVialPatch1 = NULL; + manaVialPatch2 = NULL; + + // Ready artifact + if(ArtifactFlash) + { + V_DrawPatch(144, 160, PatchARTICLEAR); + V_DrawPatch(148, 164, W_CacheLumpNum(W_GetNumForName("useartia") + + ArtifactFlash - 1, PU_CACHE)); + ArtifactFlash--; + oldarti = -1; // so that the correct artifact fills in after the flash + UpdateState |= I_STATBAR; + } + else if(oldarti != CPlayer->readyArtifact + || oldartiCount != CPlayer->inventory[inv_ptr].count) + { + V_DrawPatch(144, 160, PatchARTICLEAR); + if(CPlayer->readyArtifact > 0) + { + V_DrawPatch(143, 163, + W_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE)); + if(CPlayer->inventory[inv_ptr].count > 1) + { + DrSmallNumber(CPlayer->inventory[inv_ptr].count, 162, 184); + } + } + oldarti = CPlayer->readyArtifact; + oldartiCount = CPlayer->inventory[inv_ptr].count; + UpdateState |= I_STATBAR; + } + + // Frags + if(deathmatch) + { + temp = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + temp += CPlayer->frags[i]; + } + if(temp != oldfrags) + { + V_DrawPatch(38, 162, PatchKILLS); + DrINumber(temp, 40, 176); + oldfrags = temp; + UpdateState |= I_STATBAR; + } + } + else + { + temp = HealthMarker; + if(temp < 0) + { + temp = 0; + } + else if(temp > 100) + { + temp = 100; + } + if(oldlife != temp) + { + oldlife = temp; + V_DrawPatch(41, 178, PatchARMCLEAR); + if(temp >= 25) + { + DrINumber(temp, 40, 176); + } + else + { + DrRedINumber(temp, 40, 176); + } + UpdateState |= I_STATBAR; + } + } + // Mana + temp = CPlayer->mana[0]; + if(oldmana1 != temp) + { + V_DrawPatch(77, 178, PatchMANACLEAR); + DrSmallNumber(temp, 79, 181); + manaVialPatch1 = (patch_t *)1; // force a vial update + if(temp == 0) + { // Draw Dim Mana icon + manaPatch1 = PatchMANADIM1; + } + else if(oldmana1 == 0) + { + manaPatch1 = PatchMANABRIGHT1; + } + oldmana1 = temp; + UpdateState |= I_STATBAR; + } + temp = CPlayer->mana[1]; + if(oldmana2 != temp) + { + V_DrawPatch(109, 178, PatchMANACLEAR); + DrSmallNumber(temp, 111, 181); + manaVialPatch1 = (patch_t *)1; // force a vial update + if(temp == 0) + { // Draw Dim Mana icon + manaPatch2 = PatchMANADIM2; + } + else if(oldmana2 == 0) + { + manaPatch2 = PatchMANABRIGHT2; + } + oldmana2 = temp; + UpdateState |= I_STATBAR; + } + if(oldweapon != CPlayer->readyweapon || manaPatch1 || manaPatch2 + || manaVialPatch1) + { // Update mana graphics based upon mana count/weapon type + if(CPlayer->readyweapon == WP_FIRST) + { + manaPatch1 = PatchMANADIM1; + manaPatch2 = PatchMANADIM2; + manaVialPatch1 = PatchMANAVIALDIM1; + manaVialPatch2 = PatchMANAVIALDIM2; + } + else if(CPlayer->readyweapon == WP_SECOND) + { + if(!manaPatch1) + { + manaPatch1 = PatchMANABRIGHT1; + } + manaVialPatch1 = PatchMANAVIAL1; + manaPatch2 = PatchMANADIM2; + manaVialPatch2 = PatchMANAVIALDIM2; + } + else if(CPlayer->readyweapon == WP_THIRD) + { + manaPatch1 = PatchMANADIM1; + manaVialPatch1 = PatchMANAVIALDIM1; + if(!manaPatch2) + { + manaPatch2 = PatchMANABRIGHT2; + } + manaVialPatch2 = PatchMANAVIAL2; + } + else + { + manaVialPatch1 = PatchMANAVIAL1; + manaVialPatch2 = PatchMANAVIAL2; + if(!manaPatch1) + { + manaPatch1 = PatchMANABRIGHT1; + } + if(!manaPatch2) + { + manaPatch2 = PatchMANABRIGHT2; + } + } + V_DrawPatch(77, 164, manaPatch1); + V_DrawPatch(110, 164, manaPatch2); + V_DrawPatch(94, 164, manaVialPatch1); + for(i = 165; i < 187-(22*CPlayer->mana[0])/MAX_MANA; i++) + { + screen[i*SCREENWIDTH+95] = 0; + screen[i*SCREENWIDTH+96] = 0; + screen[i*SCREENWIDTH+97] = 0; + } + V_DrawPatch(102, 164, manaVialPatch2); + for(i = 165; i < 187-(22*CPlayer->mana[1])/MAX_MANA; i++) + { + screen[i*SCREENWIDTH+103] = 0; + screen[i*SCREENWIDTH+104] = 0; + screen[i*SCREENWIDTH+105] = 0; + } + oldweapon = CPlayer->readyweapon; + UpdateState |= I_STATBAR; + } + // Armor + temp = AutoArmorSave[CPlayer->class] + +CPlayer->armorpoints[ARMOR_ARMOR]+CPlayer->armorpoints[ARMOR_SHIELD] + +CPlayer->armorpoints[ARMOR_HELMET]+CPlayer->armorpoints[ARMOR_AMULET]; + if(oldarmor != temp) + { + oldarmor = temp; + V_DrawPatch(255, 178, PatchARMCLEAR); + DrINumber(FixedDiv(temp, 5*FRACUNIT)>>FRACBITS, 250, 176); + UpdateState |= I_STATBAR; + } + // Weapon Pieces + if(oldpieces != CPlayer->pieces) + { + DrawWeaponPieces(); + oldpieces = CPlayer->pieces; + UpdateState |= I_STATBAR; + } +} + +//========================================================================== +// +// DrawInventoryBar +// +//========================================================================== + +void DrawInventoryBar(void) +{ + int i; + int x; + + x = inv_ptr-curpos; + UpdateState |= I_STATBAR; + V_DrawPatch(38, 162, PatchINVBAR); + for(i = 0; i < 7; i++) + { + //V_DrawPatch(50+i*31, 160, W_CacheLumpName("ARTIBOX", PU_CACHE)); + if(CPlayer->inventorySlotNum > x+i + && CPlayer->inventory[x+i].type != arti_none) + { + V_DrawPatch(50+i*31, 163, W_CacheLumpName( + patcharti[CPlayer->inventory[x+i].type], PU_CACHE)); + if(CPlayer->inventory[x+i].count > 1) + { + DrSmallNumber(CPlayer->inventory[x+i].count, 68+i*31, 185); + } + } + } + V_DrawPatch(50+curpos*31, 163, PatchSELECTBOX); + if(x != 0) + { + V_DrawPatch(42, 163, !(leveltime&4) ? PatchINVLFGEM1 : + PatchINVLFGEM2); + } + if(CPlayer->inventorySlotNum-x > 7) + { + V_DrawPatch(269, 163, !(leveltime&4) ? PatchINVRTGEM1 : + PatchINVRTGEM2); + } +} + +//========================================================================== +// +// DrawKeyBar +// +//========================================================================== + +void DrawKeyBar(void) +{ + int i; + int xPosition; + int temp; + + if(oldkeys != CPlayer->keys) + { + xPosition = 46; + for(i = 0; i < NUMKEYS && xPosition <= 126; i++) + { + if(CPlayer->keys&(1<keys; + UpdateState |= I_STATBAR; + } + temp = AutoArmorSave[CPlayer->class] + +CPlayer->armorpoints[ARMOR_ARMOR]+CPlayer->armorpoints[ARMOR_SHIELD] + +CPlayer->armorpoints[ARMOR_HELMET]+CPlayer->armorpoints[ARMOR_AMULET]; + if(oldarmor != temp) + { + for(i = 0; i < NUMARMOR; i++) + { + if(!CPlayer->armorpoints[i]) + { + continue; + } + if(CPlayer->armorpoints[i] <= + (ArmorIncrement[CPlayer->class][i]>>2)) + { + V_DrawFuzzPatch(150+31*i, 164, + W_CacheLumpNum(W_GetNumForName("armslot1")+i, PU_CACHE)); + } + else if(CPlayer->armorpoints[i] <= + (ArmorIncrement[CPlayer->class][i]>>1)) + { + V_DrawAltFuzzPatch(150+31*i, 164, + W_CacheLumpNum(W_GetNumForName("armslot1")+i, PU_CACHE)); + } + else + { + V_DrawPatch(150+31*i, 164, + W_CacheLumpNum(W_GetNumForName("armslot1")+i, PU_CACHE)); + } + } + oldarmor = temp; + UpdateState |= I_STATBAR; + } +} + +//========================================================================== +// +// DrawWeaponPieces +// +//========================================================================== + +static int PieceX[NUMCLASSES][3] = +{ + { 190, 225, 234 }, + { 190, 212, 225 }, + { 190, 205, 224 }, + { 0, 0, 0 } // Pig is never used +}; + +static void DrawWeaponPieces(void) +{ + if(CPlayer->pieces == 7) + { + V_DrawPatch(190, 162, PatchWEAPONFULL); + return; + } + V_DrawPatch(190, 162, PatchWEAPONSLOT); + if(CPlayer->pieces&WPIECE1) + { + V_DrawPatch(PieceX[PlayerClass[consoleplayer]][0], 162, PatchPIECE1); + } + if(CPlayer->pieces&WPIECE2) + { + V_DrawPatch(PieceX[PlayerClass[consoleplayer]][1], 162, PatchPIECE2); + } + if(CPlayer->pieces&WPIECE3) + { + V_DrawPatch(PieceX[PlayerClass[consoleplayer]][2], 162, PatchPIECE3); + } +} + +//========================================================================== +// +// DrawFullScreenStuff +// +//========================================================================== + +void DrawFullScreenStuff(void) +{ + int i; + int x; + int temp; + + UpdateState |= I_FULLSCRN; + if(CPlayer->mo->health > 0) + { + DrBNumber(CPlayer->mo->health, 5, 180); + } + else + { + DrBNumber(0, 5, 180); + } + if(deathmatch) + { + temp = 0; + for(i=0; ifrags[i]; + } + } + DrINumber(temp, 45, 185); + } + if(!inventory) + { + if(CPlayer->readyArtifact > 0) + { + V_DrawFuzzPatch(286, 170, W_CacheLumpName("ARTIBOX", + PU_CACHE)); + V_DrawPatch(284, 169, + W_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE)); + if(CPlayer->inventory[inv_ptr].count > 1) + { + DrSmallNumber(CPlayer->inventory[inv_ptr].count, 302, 192); + } + } + } + else + { + x = inv_ptr-curpos; + for(i = 0; i < 7; i++) + { + V_DrawFuzzPatch(50+i*31, 168, W_CacheLumpName("ARTIBOX", + PU_CACHE)); + if(CPlayer->inventorySlotNum > x+i + && CPlayer->inventory[x+i].type != arti_none) + { + V_DrawPatch(49+i*31, 167, W_CacheLumpName( + patcharti[CPlayer->inventory[x+i].type], PU_CACHE)); + if(CPlayer->inventory[x+i].count > 1) + { + DrSmallNumber(CPlayer->inventory[x+i].count, 66+i*31, + 188); + } + } + } + V_DrawPatch(50+curpos*31, 167, PatchSELECTBOX); + if(x != 0) + { + V_DrawPatch(40, 167, !(leveltime&4) ? PatchINVLFGEM1 : + PatchINVLFGEM2); + } + if(CPlayer->inventorySlotNum-x > 7) + { + V_DrawPatch(268, 167, !(leveltime&4) ? + PatchINVRTGEM1 : PatchINVRTGEM2); + } + } +} + + +//========================================================================== +// +// Draw_TeleportIcon +// +//========================================================================== +void Draw_TeleportIcon(void) +{ + patch_t *patch; + patch = W_CacheLumpNum(W_GetNumForName("teleicon"), PU_CACHE); + V_DrawPatch(100, 68, patch); + UpdateState |= I_FULLSCRN; + I_Update(); + UpdateState |= I_FULLSCRN; +} + +//========================================================================== +// +// Draw_SaveIcon +// +//========================================================================== +void Draw_SaveIcon(void) +{ + patch_t *patch; + patch = W_CacheLumpNum(W_GetNumForName("saveicon"), PU_CACHE); + V_DrawPatch(100, 68, patch); + UpdateState |= I_FULLSCRN; + I_Update(); + UpdateState |= I_FULLSCRN; +} + +//========================================================================== +// +// Draw_LoadIcon +// +//========================================================================== +void Draw_LoadIcon(void) +{ + patch_t *patch; + patch = W_CacheLumpNum(W_GetNumForName("loadicon"), PU_CACHE); + V_DrawPatch(100, 68, patch); + UpdateState |= I_FULLSCRN; + I_Update(); + UpdateState |= I_FULLSCRN; +} + + + +//========================================================================== +// +// SB_Responder +// +//========================================================================== + +boolean SB_Responder(event_t *event) +{ + if(event->type == ev_keydown) + { + if(HandleCheats(event->data1)) + { // Need to eat the key + return(true); + } + } + return(false); +} + +//========================================================================== +// +// HandleCheats +// +// Returns true if the caller should eat the key. +// +//========================================================================== + +static boolean HandleCheats(byte key) +{ + int i; + boolean eat; + + if(gameskill == sk_nightmare) + { // Can't cheat in nightmare mode + return(false); + } + else if(netgame) + { // change CD track is the only cheat available in deathmatch + eat = false; + if(i_CDMusic) + { + if(CheatAddKey(&Cheats[0], key, &eat)) + { + Cheats[0].func(&players[consoleplayer], &Cheats[0]); + S_StartSound(NULL, SFX_PLATFORM_STOP); + } + if(CheatAddKey(&Cheats[1], key, &eat)) + { + Cheats[1].func(&players[consoleplayer], &Cheats[1]); + S_StartSound(NULL, SFX_PLATFORM_STOP); + } + } + return eat; + } + if(players[consoleplayer].health <= 0) + { // Dead players can't cheat + return(false); + } + eat = false; + for(i = 0; Cheats[i].func != NULL; i++) + { + if(CheatAddKey(&Cheats[i], key, &eat)) + { + Cheats[i].func(&players[consoleplayer], &Cheats[i]); + S_StartSound(NULL, SFX_PLATFORM_STOP); + } + } + return(eat); +} + +//========================================================================== +// +// CheatAddkey +// +// Returns true if the added key completed the cheat, false otherwise. +// +//========================================================================== + +static boolean CheatAddKey(Cheat_t *cheat, byte key, boolean *eat) +{ + if(!cheat->pos) + { + cheat->pos = cheat->sequence; + cheat->currentArg = 0; + } + if(*cheat->pos == 0) + { + *eat = true; + cheat->args[cheat->currentArg++] = key; + cheat->pos++; + } + else if(CheatLookup[key] == *cheat->pos) + { + cheat->pos++; + } + else + { + cheat->pos = cheat->sequence; + cheat->currentArg = 0; + } + if(*cheat->pos == 0xff) + { + cheat->pos = cheat->sequence; + cheat->currentArg = 0; + return(true); + } + return(false); +} + +//========================================================================== +// +// CHEAT FUNCTIONS +// +//========================================================================== + +static void CheatGodFunc(player_t *player, Cheat_t *cheat) +{ + player->cheats ^= CF_GODMODE; + if(player->cheats&CF_GODMODE) + { + P_SetMessage(player, TXT_CHEATGODON, true); + } + else + { + P_SetMessage(player, TXT_CHEATGODOFF, true); + } + SB_state = -1; +} + +static void CheatNoClipFunc(player_t *player, Cheat_t *cheat) +{ + player->cheats ^= CF_NOCLIP; + if(player->cheats&CF_NOCLIP) + { + P_SetMessage(player, TXT_CHEATNOCLIPON, true); + } + else + { + P_SetMessage(player, TXT_CHEATNOCLIPOFF, true); + } +} + +static void CheatWeaponsFunc(player_t *player, Cheat_t *cheat) +{ + int i; + //extern boolean *WeaponInShareware; + + for(i = 0; i < NUMARMOR; i++) + { + player->armorpoints[i] = ArmorIncrement[player->class][i]; + } + for(i = 0; i < NUMWEAPONS; i++) + { + player->weaponowned[i] = true; + } + for(i = 0; i < NUMMANA; i++) + { + player->mana[i] = MAX_MANA; + } + P_SetMessage(player, TXT_CHEATWEAPONS, true); +} + +static void CheatHealthFunc(player_t *player, Cheat_t *cheat) +{ + if(player->morphTics) + { + player->health = player->mo->health = MAXMORPHHEALTH; + } + else + { + player->health = player->mo->health = MAXHEALTH; + } + P_SetMessage(player, TXT_CHEATHEALTH, true); +} + +static void CheatKeysFunc(player_t *player, Cheat_t *cheat) +{ + player->keys = 2047; + P_SetMessage(player, TXT_CHEATKEYS, true); +} + +static void CheatSoundFunc(player_t *player, Cheat_t *cheat) +{ + DebugSound = !DebugSound; + if(DebugSound) + { + P_SetMessage(player, TXT_CHEATSOUNDON, true); + } + else + { + P_SetMessage(player, TXT_CHEATSOUNDOFF, true); + } +} + +static void CheatTickerFunc(player_t *player, Cheat_t *cheat) +{ + extern int DisplayTicker; + + DisplayTicker = !DisplayTicker; + if(DisplayTicker) + { + P_SetMessage(player, TXT_CHEATTICKERON, true); + } + else + { + P_SetMessage(player, TXT_CHEATTICKEROFF, true); + } +} + +static void CheatArtifactAllFunc(player_t *player, Cheat_t *cheat) +{ + int i; + int j; + + for(i = arti_none+1; i < arti_firstpuzzitem; i++) + { + for(j = 0; j < 25; j++) + { + P_GiveArtifact(player, i, NULL); + } + } + P_SetMessage(player, TXT_CHEATARTIFACTS3, true); +} + +static void CheatPuzzleFunc(player_t *player, Cheat_t *cheat) +{ + int i; + + for(i = arti_firstpuzzitem; i < NUMARTIFACTS; i++) + { + P_GiveArtifact(player, i, NULL); + } + P_SetMessage(player, TXT_CHEATARTIFACTS3, true); +} + +static void CheatInitFunc(player_t *player, Cheat_t *cheat) +{ + G_DeferedInitNew(gameskill, gameepisode, gamemap); + P_SetMessage(player, TXT_CHEATWARP, true); +} + +static void CheatWarpFunc(player_t *player, Cheat_t *cheat) +{ + int tens; + int ones; + int map; + char mapName[9]; + char auxName[128]; + FILE *fp; + + tens = cheat->args[0]-'0'; + ones = cheat->args[1]-'0'; + if(tens < 0 || tens > 9 || ones < 0 || ones > 9) + { // Bad map + P_SetMessage(player, TXT_CHEATBADINPUT, true); + return; + } + map = P_TranslateMap((cheat->args[0]-'0')*10+cheat->args[1]-'0'); + if(map == -1) + { // Not found + P_SetMessage(player, TXT_CHEATNOMAP, true); + return; + } + if(map == gamemap) + { // Don't try to teleport to current map + P_SetMessage(player, TXT_CHEATBADINPUT, true); + return; + } + if(DevMaps) + { // Search map development directory + sprintf(auxName, "%sMAP%02d.WAD", DevMapsDir, map); + fp = fopen(auxName, "rb"); + if(fp) + { + fclose(fp); + } + else + { // Can't find + P_SetMessage(player, TXT_CHEATNOMAP, true); + return; + } + } + else + { // Search primary lumps + sprintf(mapName, "MAP%02d", map); + if(W_CheckNumForName(mapName) == -1) + { // Can't find + P_SetMessage(player, TXT_CHEATNOMAP, true); + return; + } + } + P_SetMessage(player, TXT_CHEATWARP, true); + G_TeleportNewMap(map, 0); +} + +static void CheatPigFunc(player_t *player, Cheat_t *cheat) +{ + extern boolean P_UndoPlayerMorph(player_t *player); + + if(player->morphTics) + { + P_UndoPlayerMorph(player); + } + else + { + P_MorphPlayer(player); + } + P_SetMessage(player, "SQUEAL!!", true); +} + +static void CheatMassacreFunc(player_t *player, Cheat_t *cheat) +{ + int count; + char buffer[80]; + + count = P_Massacre(); + sprintf(buffer, "%d MONSTERS KILLED\n", count); + P_SetMessage(player, buffer, true); +} + +static void CheatIDKFAFunc(player_t *player, Cheat_t *cheat) +{ + int i; + if(player->morphTics) + { + return; + } + for(i = 1; i < 8; i++) + { + player->weaponowned[i] = false; + } + player->pendingweapon = WP_FIRST; + P_SetMessage(player, TXT_CHEATIDKFA, true); +} + +static void CheatQuickenFunc1(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, "TRYING TO CHEAT? THAT'S ONE....", true); +} + +static void CheatQuickenFunc2(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, "THAT'S TWO....", true); +} + +static void CheatQuickenFunc3(player_t *player, Cheat_t *cheat) +{ + P_DamageMobj(player->mo, NULL, player->mo, 10000); + P_SetMessage(player, "THAT'S THREE! TIME TO DIE.", true); +} + +static void CheatClassFunc1(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, "ENTER NEW PLAYER CLASS (0 - 2)", true); +} + +static void CheatClassFunc2(player_t *player, Cheat_t *cheat) +{ + int i; + int class; + + if(player->morphTics) + { // don't change class if the player is morphed + return; + } + class = cheat->args[0]-'0'; + if(class > 2 || class < 0) + { + P_SetMessage(player, "INVALID PLAYER CLASS", true); + return; + } + player->class = class; + for(i = 0; i < NUMARMOR; i++) + { + player->armorpoints[i] = 0; + } + PlayerClass[consoleplayer] = class; + P_PostMorphWeapon(player, WP_FIRST); + SB_SetClassData(); + SB_state = -1; + UpdateState |= I_FULLSCRN; +} + +static void CheatVersionFunc(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, VERSIONTEXT, true); +} + +static void CheatDebugFunc(player_t *player, Cheat_t *cheat) +{ + char textBuffer[50]; + sprintf(textBuffer, "MAP %d (%d) X:%5d Y:%5d Z:%5d", + P_GetMapWarpTrans(gamemap), + gamemap, + player->mo->x >> FRACBITS, + player->mo->y >> FRACBITS, + player->mo->z >> FRACBITS); + P_SetMessage(player, textBuffer, true); +} + +static void CheatScriptFunc1(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, "RUN WHICH SCRIPT(01-99)?", true); +} + +static void CheatScriptFunc2(player_t *player, Cheat_t *cheat) +{ + P_SetMessage(player, "RUN WHICH SCRIPT(01-99)?", true); +} + +static void CheatScriptFunc3(player_t *player, Cheat_t *cheat) +{ + int script; + byte args[3]; + int tens, ones; + char textBuffer[40]; + + tens = cheat->args[0]-'0'; + ones = cheat->args[1]-'0'; + script = tens*10 + ones; + if (script < 1) return; + if (script > 99) return; + args[0]=args[1]=args[2]=0; + + if(P_StartACS(script, 0, args, player->mo, NULL, 0)) + { + sprintf(textBuffer, "RUNNING SCRIPT %.2d", script); + P_SetMessage(player, textBuffer, true); + } +} + +extern int cheating; + +static void CheatRevealFunc(player_t *player, Cheat_t *cheat) +{ + cheating = (cheating+1) % 3; +} + +//=========================================================================== +// +// CheatTrackFunc1 +// +//=========================================================================== + +static void CheatTrackFunc1(player_t *player, Cheat_t *cheat) +{ +#ifdef __WATCOMC__ + char buffer[80]; + + if(!i_CDMusic) + { + return; + } + if(I_CDMusInit() == -1) + { + P_SetMessage(player, "ERROR INITIALIZING CD", true); + } + sprintf(buffer, "ENTER DESIRED CD TRACK (%.2d - %.2d):\n", + I_CDMusFirstTrack(), I_CDMusLastTrack()); + P_SetMessage(player, buffer, true); +#endif +} + +//=========================================================================== +// +// CheatTrackFunc2 +// +//=========================================================================== + +static void CheatTrackFunc2(player_t *player, Cheat_t *cheat) +{ +#ifdef __WATCOMC__ + char buffer[80]; + int track; + + if(!i_CDMusic) + { + return; + } + track = (cheat->args[0]-'0')*10+(cheat->args[1]-'0'); + if(track < I_CDMusFirstTrack() || track > I_CDMusLastTrack()) + { + P_SetMessage(player, "INVALID TRACK NUMBER\n", true); + return; + } + if(track == i_CDCurrentTrack) + { + return; + } + if(I_CDMusPlay(track)) + { + sprintf(buffer, "ERROR WHILE TRYING TO PLAY CD TRACK: %.2d\n", track); + P_SetMessage(player, buffer, true); + } + else + { // No error encountered while attempting to play the track + sprintf(buffer, "PLAYING TRACK: %.2d\n", track); + P_SetMessage(player, buffer, true); + i_CDMusicLength = 35*I_CDMusTrackLength(track); + oldTic = gametic; + i_CDTrack = track; + i_CDCurrentTrack = track; + } +#endif +} diff --git a/src/hexen/sc_man.c b/src/hexen/sc_man.c new file mode 100644 index 00000000..394679cc --- /dev/null +++ b/src/hexen/sc_man.c @@ -0,0 +1,485 @@ + +//************************************************************************** +//** +//** sc_man.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sc_man.c,v $ +//** $Revision: 1.3 $ +//** $Date: 96/01/06 03:23:43 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "h2def.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_STRING_SIZE 64 +#define ASCII_COMMENT (';') +#define ASCII_QUOTE (34) +#define LUMP_SCRIPT 1 +#define FILE_ZONE_SCRIPT 2 +#define FILE_CLIB_SCRIPT 3 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void CheckOpen(void); +static void OpenScript(char *name, int type); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +char *sc_String; +int sc_Number; +int sc_Line; +boolean sc_End; +boolean sc_Crossed; +boolean sc_FileScripts = false; +char *sc_ScriptsDir = ""; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static char ScriptName[16]; +static char *ScriptBuffer; +static char *ScriptPtr; +static char *ScriptEndPtr; +static char StringBuffer[MAX_STRING_SIZE]; +static boolean ScriptOpen = false; +static boolean ScriptFreeCLib; // true = de-allocate using free() +static int ScriptSize; +static boolean AlreadyGot = false; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SC_Open +// +//========================================================================== + +void SC_Open(char *name) +{ + char fileName[128]; + + if(sc_FileScripts == true) + { + sprintf(fileName, "%s%s.txt", sc_ScriptsDir, name); + SC_OpenFile(fileName); + } + else + { + SC_OpenLump(name); + } +} + +//========================================================================== +// +// SC_OpenLump +// +// Loads a script (from the WAD files) and prepares it for parsing. +// +//========================================================================== + +void SC_OpenLump(char *name) +{ + OpenScript(name, LUMP_SCRIPT); +} + +//========================================================================== +// +// SC_OpenFile +// +// Loads a script (from a file) and prepares it for parsing. Uses the +// zone memory allocator for memory allocation and de-allocation. +// +//========================================================================== + +void SC_OpenFile(char *name) +{ + OpenScript(name, FILE_ZONE_SCRIPT); +} + +//========================================================================== +// +// SC_OpenFileCLib +// +// Loads a script (from a file) and prepares it for parsing. Uses C +// library function calls for memory allocation and de-allocation. +// +//========================================================================== + +void SC_OpenFileCLib(char *name) +{ + OpenScript(name, FILE_CLIB_SCRIPT); +} + +//========================================================================== +// +// OpenScript +// +//========================================================================== + +static void OpenScript(char *name, int type) +{ + SC_Close(); + if(type == LUMP_SCRIPT) + { // Lump script + ScriptBuffer = (char *)W_CacheLumpName(name, PU_STATIC); + ScriptSize = W_LumpLength(W_GetNumForName(name)); + strcpy(ScriptName, name); + ScriptFreeCLib = false; // De-allocate using Z_Free() + } + else if(type == FILE_ZONE_SCRIPT) + { // File script - zone + ScriptSize = M_ReadFile(name, (byte **)&ScriptBuffer); + M_ExtractFileBase(name, ScriptName); + ScriptFreeCLib = false; // De-allocate using Z_Free() + } + else + { // File script - clib + ScriptSize = M_ReadFileCLib(name, (byte **)&ScriptBuffer); + M_ExtractFileBase(name, ScriptName); + ScriptFreeCLib = true; // De-allocate using free() + } + ScriptPtr = ScriptBuffer; + ScriptEndPtr = ScriptPtr+ScriptSize; + sc_Line = 1; + sc_End = false; + ScriptOpen = true; + sc_String = StringBuffer; + AlreadyGot = false; +} + +//========================================================================== +// +// SC_Close +// +//========================================================================== + +void SC_Close(void) +{ + if(ScriptOpen) + { + if(ScriptFreeCLib == true) + { + free(ScriptBuffer); + } + else + { + Z_Free(ScriptBuffer); + } + ScriptOpen = false; + } +} + +//========================================================================== +// +// SC_GetString +// +//========================================================================== + +boolean SC_GetString(void) +{ + char *text; + boolean foundToken; + + CheckOpen(); + if(AlreadyGot) + { + AlreadyGot = false; + return true; + } + foundToken = false; + sc_Crossed = false; + if(ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + while(foundToken == false) + { + while(*ScriptPtr <= 32) + { + if(ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + if(*ScriptPtr++ == '\n') + { + sc_Line++; + sc_Crossed = true; + } + } + if(ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + if(*ScriptPtr != ASCII_COMMENT) + { // Found a token + foundToken = true; + } + else + { // Skip comment + while(*ScriptPtr++ != '\n') + { + if(ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + } + sc_Line++; + sc_Crossed = true; + } + } + text = sc_String; + if(*ScriptPtr == ASCII_QUOTE) + { // Quoted string + ScriptPtr++; + while(*ScriptPtr != ASCII_QUOTE) + { + *text++ = *ScriptPtr++; + if(ScriptPtr == ScriptEndPtr + || text == &sc_String[MAX_STRING_SIZE-1]) + { + break; + } + } + ScriptPtr++; + } + else + { // Normal string + while((*ScriptPtr > 32) && (*ScriptPtr != ASCII_COMMENT)) + { + *text++ = *ScriptPtr++; + if(ScriptPtr == ScriptEndPtr + || text == &sc_String[MAX_STRING_SIZE-1]) + { + break; + } + } + } + *text = 0; + return true; +} + +//========================================================================== +// +// SC_MustGetString +// +//========================================================================== + +void SC_MustGetString(void) +{ + if(SC_GetString() == false) + { + SC_ScriptError("Missing string."); + } +} + +//========================================================================== +// +// SC_MustGetStringName +// +//========================================================================== + +void SC_MustGetStringName(char *name) +{ + SC_MustGetString(); + if(SC_Compare(name) == false) + { + SC_ScriptError(NULL); + } +} + +//========================================================================== +// +// SC_GetNumber +// +//========================================================================== + +boolean SC_GetNumber(void) +{ + char *stopper; + + CheckOpen(); + if(SC_GetString()) + { + sc_Number = strtol(sc_String, &stopper, 0); + if(*stopper != 0) + { + I_Error("SC_GetNumber: Bad numeric constant \"%s\".\n" + "Script %s, Line %d", sc_String, ScriptName, sc_Line); + } + return true; + } + else + { + return false; + } +} + +//========================================================================== +// +// SC_MustGetNumber +// +//========================================================================== + +void SC_MustGetNumber(void) +{ + if(SC_GetNumber() == false) + { + SC_ScriptError("Missing integer."); + } +} + +//========================================================================== +// +// SC_UnGet +// +// Assumes there is a valid string in sc_String. +// +//========================================================================== + +void SC_UnGet(void) +{ + AlreadyGot = true; +} + +//========================================================================== +// +// SC_Check +// +// Returns true if another token is on the current line. +// +//========================================================================== + +/* +boolean SC_Check(void) +{ + char *text; + + CheckOpen(); + text = ScriptPtr; + if(text >= ScriptEndPtr) + { + return false; + } + while(*text <= 32) + { + if(*text == '\n') + { + return false; + } + text++; + if(text == ScriptEndPtr) + { + return false; + } + } + if(*text == ASCII_COMMENT) + { + return false; + } + return true; +} +*/ + +//========================================================================== +// +// SC_MatchString +// +// Returns the index of the first match to sc_String from the passed +// array of strings, or -1 if not found. +// +//========================================================================== + +int SC_MatchString(char **strings) +{ + int i; + + for(i = 0; *strings != NULL; i++) + { + if(SC_Compare(*strings++)) + { + return i; + } + } + return -1; +} + +//========================================================================== +// +// SC_MustMatchString +// +//========================================================================== + +int SC_MustMatchString(char **strings) +{ + int i; + + i = SC_MatchString(strings); + if(i == -1) + { + SC_ScriptError(NULL); + } + return i; +} + +//========================================================================== +// +// SC_Compare +// +//========================================================================== + +boolean SC_Compare(char *text) +{ + if(strcasecmp(text, sc_String) == 0) + { + return true; + } + return false; +} + +//========================================================================== +// +// SC_ScriptError +// +//========================================================================== + +void SC_ScriptError(char *message) +{ + if(message == NULL) + { + message = "Bad syntax."; + } + I_Error("Script error, \"%s\" line %d: %s", ScriptName, + sc_Line, message); +} + +//========================================================================== +// +// CheckOpen +// +//========================================================================== + +static void CheckOpen(void) +{ + if(ScriptOpen == false) + { + I_Error("SC_ call before SC_Open()."); + } +} diff --git a/src/hexen/sn_sonix.c b/src/hexen/sn_sonix.c new file mode 100644 index 00000000..78ab6e0d --- /dev/null +++ b/src/hexen/sn_sonix.c @@ -0,0 +1,506 @@ + +//************************************************************************** +//** +//** sn_sonix.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sn_sonix.c,v $ +//** $Revision: 1.17 $ +//** $Date: 95/10/05 18:25:44 $ +//** $Author: paul $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "h2def.h" +#include "soundst.h" + +// MACROS ------------------------------------------------------------------ + +#define SS_MAX_SCRIPTS 64 +#define SS_TEMPBUFFER_SIZE 1024 +#define SS_SEQUENCE_NAME_LENGTH 32 + +#define SS_SCRIPT_NAME "SNDSEQ" +#define SS_STRING_PLAY "play" +#define SS_STRING_PLAYUNTILDONE "playuntildone" +#define SS_STRING_PLAYTIME "playtime" +#define SS_STRING_PLAYREPEAT "playrepeat" +#define SS_STRING_DELAY "delay" +#define SS_STRING_DELAYRAND "delayrand" +#define SS_STRING_VOLUME "volume" +#define SS_STRING_END "end" +#define SS_STRING_STOPSOUND "stopsound" + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + SS_CMD_NONE, + SS_CMD_PLAY, + SS_CMD_WAITUNTILDONE, // used by PLAYUNTILDONE + SS_CMD_PLAYTIME, + SS_CMD_PLAYREPEAT, + SS_CMD_DELAY, + SS_CMD_DELAYRAND, + SS_CMD_VOLUME, + SS_CMD_STOPSOUND, + SS_CMD_END +} sscmds_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void VerifySequencePtr(int *base, int *ptr); +static int GetSoundOffset(char *name); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern sfxinfo_t S_sfx[]; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static struct +{ + char name[SS_SEQUENCE_NAME_LENGTH]; + int scriptNum; + int stopSound; +} SequenceTranslate[SEQ_NUMSEQ] = +{ + { "Platform", 0, 0 }, + { "Platform", 0, 0 }, // a 'heavy' platform is just a platform + { "PlatformMetal", 0, 0 }, + { "Platform", 0, 0 }, // same with a 'creak' platform + { "Silence", 0, 0 }, + { "Lava", 0, 0 }, + { "Water", 0, 0 }, + { "Ice", 0, 0 }, + { "Earth", 0, 0 }, + { "PlatformMetal2", 0, 0 }, + { "DoorNormal", 0, 0 }, + { "DoorHeavy", 0, 0 }, + { "DoorMetal", 0, 0 }, + { "DoorCreak", 0, 0 }, + { "Silence", 0, 0 }, + { "Lava", 0, 0 }, + { "Water", 0, 0}, + { "Ice", 0, 0 }, + { "Earth", 0, 0}, + { "DoorMetal2", 0, 0 }, + { "Wind", 0, 0 } +}; + +static int *SequenceData[SS_MAX_SCRIPTS]; + +int ActiveSequences; +seqnode_t *SequenceListHead; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// VerifySequencePtr +// +// Verifies the integrity of the temporary ptr, and ensures that the ptr +// isn't exceeding the size of the temporary buffer +//========================================================================== + +static void VerifySequencePtr(int *base, int *ptr) +{ + if(ptr-base > SS_TEMPBUFFER_SIZE) + { + I_Error("VerifySequencePtr: tempPtr >= %d\n", SS_TEMPBUFFER_SIZE); + } +} + +//========================================================================== +// +// GetSoundOffset +// +//========================================================================== + +static int GetSoundOffset(char *name) +{ + int i; + + for(i = 0; i < NUMSFX; i++) + { + if(!strcasecmp(name, S_sfx[i].tagName)) + { + return i; + } + } + SC_ScriptError("GetSoundOffset: Unknown sound name\n"); + return 0; +} + +//========================================================================== +// +// SN_InitSequenceScript +// +//========================================================================== + +void SN_InitSequenceScript(void) +{ + int i, j; + int inSequence; + int *tempDataStart; + int *tempDataPtr; + + inSequence = -1; + ActiveSequences = 0; + for(i = 0; i < SS_MAX_SCRIPTS; i++) + { + SequenceData[i] = NULL; + } + SC_Open(SS_SCRIPT_NAME); + while(SC_GetString()) + { + if(*sc_String == ':') + { + if(inSequence != -1) + { + SC_ScriptError("SN_InitSequenceScript: Nested Script Error"); + } + tempDataStart = (int *)Z_Malloc(SS_TEMPBUFFER_SIZE, + PU_STATIC, NULL); + memset(tempDataStart, 0, SS_TEMPBUFFER_SIZE); + tempDataPtr = tempDataStart; + for(i = 0; i < SS_MAX_SCRIPTS; i++) + { + if(SequenceData[i] == NULL) + { + break; + } + } + if(i == SS_MAX_SCRIPTS) + { + I_Error("Number of SS Scripts >= SS_MAX_SCRIPTS"); + } + for(j = 0; j < SEQ_NUMSEQ; j++) + { + if(!strcasecmp(SequenceTranslate[j].name, sc_String+1)) + { + SequenceTranslate[j].scriptNum = i; + inSequence = j; + break; + } + } + continue; // parse the next command + } + if(inSequence == -1) + { + continue; + } + if(SC_Compare(SS_STRING_PLAYUNTILDONE)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + SC_MustGetString(); + *tempDataPtr++ = SS_CMD_PLAY; + *tempDataPtr++ = GetSoundOffset(sc_String); + *tempDataPtr++ = SS_CMD_WAITUNTILDONE; + } + else if(SC_Compare(SS_STRING_PLAY)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + SC_MustGetString(); + *tempDataPtr++ = SS_CMD_PLAY; + *tempDataPtr++ = GetSoundOffset(sc_String); + } + else if(SC_Compare(SS_STRING_PLAYTIME)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + SC_MustGetString(); + *tempDataPtr++ = SS_CMD_PLAY; + *tempDataPtr++ = GetSoundOffset(sc_String); + SC_MustGetNumber(); + *tempDataPtr++ = SS_CMD_DELAY; + *tempDataPtr++ = sc_Number; + } + else if(SC_Compare(SS_STRING_PLAYREPEAT)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + SC_MustGetString(); + *tempDataPtr++ = SS_CMD_PLAYREPEAT; + *tempDataPtr++ = GetSoundOffset(sc_String); + } + else if(SC_Compare(SS_STRING_DELAY)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + *tempDataPtr++ = SS_CMD_DELAY; + SC_MustGetNumber(); + *tempDataPtr++ = sc_Number; + } + else if(SC_Compare(SS_STRING_DELAYRAND)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + *tempDataPtr++ = SS_CMD_DELAYRAND; + SC_MustGetNumber(); + *tempDataPtr++ = sc_Number; + SC_MustGetNumber(); + *tempDataPtr++ = sc_Number; + } + else if(SC_Compare(SS_STRING_VOLUME)) + { + VerifySequencePtr(tempDataStart, tempDataPtr); + *tempDataPtr++ = SS_CMD_VOLUME; + SC_MustGetNumber(); + *tempDataPtr++ = sc_Number; + } + else if(SC_Compare(SS_STRING_END)) + { + int dataSize; + + *tempDataPtr++ = SS_CMD_END; + dataSize = (tempDataPtr-tempDataStart)*sizeof(int); + SequenceData[i] = (int *)Z_Malloc(dataSize, PU_STATIC, + NULL); + memcpy(SequenceData[i], tempDataStart, dataSize); + Z_Free(tempDataStart); + inSequence = -1; + } + else if(SC_Compare(SS_STRING_STOPSOUND)) + { + SC_MustGetString(); + SequenceTranslate[inSequence].stopSound = + GetSoundOffset(sc_String); + *tempDataPtr++ = SS_CMD_STOPSOUND; + } + else + { + SC_ScriptError("SN_InitSequenceScript: Unknown commmand.\n"); + } + } +} + +//========================================================================== +// +// SN_StartSequence +// +//========================================================================== + +void SN_StartSequence(mobj_t *mobj, int sequence) +{ + seqnode_t *node; + + SN_StopSequence(mobj); // Stop any previous sequence + node = (seqnode_t *)Z_Malloc(sizeof(seqnode_t), PU_STATIC, NULL); + node->sequencePtr = SequenceData[SequenceTranslate[sequence].scriptNum]; + node->sequence = sequence; + node->mobj = mobj; + node->delayTics = 0; + node->stopSound = SequenceTranslate[sequence].stopSound; + node->volume = 127; // Start at max volume + + if(!SequenceListHead) + { + SequenceListHead = node; + node->next = node->prev = NULL; + } + else + { + SequenceListHead->prev = node; + node->next = SequenceListHead; + node->prev = NULL; + SequenceListHead = node; + } + ActiveSequences++; + return; +} + +//========================================================================== +// +// SN_StartSequenceName +// +//========================================================================== + +void SN_StartSequenceName(mobj_t *mobj, char *name) +{ + int i; + + for(i = 0; i < SEQ_NUMSEQ; i++) + { + if(!strcmp(name, SequenceTranslate[i].name)) + { + SN_StartSequence(mobj, i); + return; + } + } +} + +//========================================================================== +// +// SN_StopSequence +// +//========================================================================== + +void SN_StopSequence(mobj_t *mobj) +{ + seqnode_t *node; + + for(node = SequenceListHead; node; node = node->next) + { + if(node->mobj == mobj) + { + S_StopSound(mobj); + if(node->stopSound) + { + S_StartSoundAtVolume(mobj, node->stopSound, node->volume); + } + if(SequenceListHead == node) + { + SequenceListHead = node->next; + } + if(node->prev) + { + node->prev->next = node->next; + } + if(node->next) + { + node->next->prev = node->prev; + } + Z_Free(node); + ActiveSequences--; + } + } +} + +//========================================================================== +// +// SN_UpdateActiveSequences +// +//========================================================================== + +void SN_UpdateActiveSequences(void) +{ + seqnode_t *node; + boolean sndPlaying; + + if(!ActiveSequences || paused) + { // No sequences currently playing/game is paused + return; + } + for(node = SequenceListHead; node; node = node->next) + { + if(node->delayTics) + { + node->delayTics--; + continue; + } + sndPlaying = S_GetSoundPlayingInfo(node->mobj, node->currentSoundID); + switch(*node->sequencePtr) + { + case SS_CMD_PLAY: + if(!sndPlaying) + { + node->currentSoundID = *(node->sequencePtr+1); + S_StartSoundAtVolume(node->mobj, node->currentSoundID, + node->volume); + } + node->sequencePtr += 2; + break; + case SS_CMD_WAITUNTILDONE: + if(!sndPlaying) + { + node->sequencePtr++; + node->currentSoundID = 0; + } + break; + case SS_CMD_PLAYREPEAT: + if(!sndPlaying) + { + node->currentSoundID = *(node->sequencePtr+1); + S_StartSoundAtVolume(node->mobj, node->currentSoundID, + node->volume); + } + break; + case SS_CMD_DELAY: + node->delayTics = *(node->sequencePtr+1); + node->sequencePtr += 2; + node->currentSoundID = 0; + break; + case SS_CMD_DELAYRAND: + node->delayTics = *(node->sequencePtr+1)+ + M_Random()%(*(node->sequencePtr+2)-*(node->sequencePtr+1)); + node->sequencePtr += 2; + node->currentSoundID = 0; + break; + case SS_CMD_VOLUME: + node->volume = (127*(*(node->sequencePtr+1)))/100; + node->sequencePtr += 2; + break; + case SS_CMD_STOPSOUND: + // Wait until something else stops the sequence + break; + case SS_CMD_END: + SN_StopSequence(node->mobj); + break; + default: + break; + } + } +} + +//========================================================================== +// +// SN_StopAllSequences +// +//========================================================================== + +void SN_StopAllSequences(void) +{ + seqnode_t *node; + + for(node = SequenceListHead; node; node = node->next) + { + node->stopSound = 0; // don't play any stop sounds + SN_StopSequence(node->mobj); + } +} + +//========================================================================== +// +// SN_GetSequenceOffset +// +//========================================================================== + +int SN_GetSequenceOffset(int sequence, int *sequencePtr) +{ + return (sequencePtr-SequenceData[SequenceTranslate[sequence].scriptNum]); +} + +//========================================================================== +// +// SN_ChangeNodeData +// +// nodeNum zero is the first node +//========================================================================== + +void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume, + int currentSoundID) +{ + int i; + seqnode_t *node; + + i = 0; + node = SequenceListHead; + while(node && i < nodeNum) + { + node = node->next; + i++; + } + if(!node) + { // reach the end of the list before finding the nodeNum-th node + return; + } + node->delayTics = delayTics; + node->volume = volume; + node->sequencePtr += seqOffset; + node->currentSoundID = currentSoundID; +} diff --git a/src/hexen/sounds.c b/src/hexen/sounds.c new file mode 100644 index 00000000..5f0e75bd --- /dev/null +++ b/src/hexen/sounds.c @@ -0,0 +1,306 @@ + +//************************************************************************** +//** +//** sounds.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sounds.c,v $ +//** $Revision: 1.83 $ +//** $Date: 96/01/06 18:37:44 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "sounds.h" + +// Music info + +/* +musicinfo_t S_music[] = +{ + { "MUS_E1M1", 0 }, // 1-1 + { "MUS_E1M2", 0 }, + { "MUS_E1M3", 0 }, + { "MUS_E1M4", 0 }, + { "MUS_E1M5", 0 }, + { "MUS_E1M6", 0 }, + { "MUS_E1M7", 0 }, + { "MUS_E1M8", 0 }, + { "MUS_E1M9", 0 }, + { "MUS_E2M1", 0 }, // 2-1 + { "MUS_E2M2", 0 }, + { "MUS_E2M3", 0 }, + { "MUS_E2M4", 0 }, + { "MUS_E1M4", 0 }, + { "MUS_E2M6", 0 }, + { "MUS_E2M7", 0 }, + { "MUS_E2M8", 0 }, + { "MUS_E2M9", 0 }, + { "MUS_E1M1", 0 }, // 3-1 + { "MUS_E3M2", 0 }, + { "MUS_E3M3", 0 }, + { "MUS_E1M6", 0 }, + { "MUS_E1M3", 0 }, + { "MUS_E1M2", 0 }, + { "MUS_E1M5", 0 }, + { "MUS_E1M9", 0 }, + { "MUS_E2M6", 0 }, + { "MUS_E1M6", 0 }, // 4-1 + { "MUS_TITL", 0 }, + { "MUS_INTR", 0 }, + { "MUS_CPTD", 0 } +}; +*/ + +// Sound info + +sfxinfo_t S_sfx[] = +{ + // tagname, lumpname, priority, usefulness, snd_ptr, lumpnum, numchannels, + // pitchshift + { "", "", 0, -1, NULL, 0, 0, 0 }, + { "PlayerFighterNormalDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterCrazyDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterExtreme1Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterExtreme2Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterExtreme3Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterBurnDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericNormalDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericCrazyDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericExtreme1Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericExtreme2Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericExtreme3Death", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericBurnDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerMageNormalDeath", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerMageCrazyDeath", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerMageExtreme1Death", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerMageExtreme2Death", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerMageExtreme3Death", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerMageBurnDeath", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerFighterPain", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericPain", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerMagePain", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerFighterGrunt", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericGrunt", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerMageGrunt", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerLand", "", 32, -1, NULL, 0, 2, 1 }, + { "PlayerPoisonCough", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterFallingScream", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerClericFallingScream", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerMageFallingScream", "", 256, -1, NULL, 0, 2, 0 }, + { "PlayerFallingSplat", "", 256, -1, NULL, 0, 2, 1 }, + { "PlayerFighterFailedUse", "", 256, -1, NULL, 0, 1, 1 }, + { "PlayerClericFailedUse", "", 256, -1, NULL, 0, 1, 1 }, + { "PlayerMageFailedUse", "", 256, -1, NULL, 0, 1, 0 }, + { "PlatformStart", "", 36, -1, NULL, 0, 2, 1 }, + { "PlatformStartMetal", "", 36, -1, NULL, 0, 2, 1 }, + { "PlatformStop", "", 40, -1, NULL, 0, 2, 1 }, + { "StoneMove", "", 32, -1, NULL, 0, 2, 1 }, + { "MetalMove", "", 32, -1, NULL, 0, 2, 1 }, + { "DoorOpen", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorLocked", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorOpenMetal", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorCloseMetal", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorCloseLight", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorCloseHeavy", "", 36, -1, NULL, 0, 2, 1 }, + { "DoorCreak", "", 36, -1, NULL, 0, 2, 1 }, + { "PickupWeapon", "", 36, -1, NULL, 0, 2, 0 }, + { "PickupArtifact", "", 36, -1, NULL, 0, 2, 1 }, + { "PickupKey", "", 36, -1, NULL, 0, 2, 1 }, + { "PickupItem", "", 36, -1, NULL, 0, 2, 1 }, + { "PickupPiece", "", 36, -1, NULL, 0, 2, 0 }, + { "WeaponBuild", "", 36, -1, NULL, 0, 2, 0 }, + { "UseArtifact", "", 36, -1, NULL, 0, 2, 1 }, + { "BlastRadius", "", 36, -1, NULL, 0, 2, 1 }, + { "Teleport", "", 256, -1, NULL, 0, 2, 1 }, + { "ThunderCrash", "", 30, -1, NULL, 0, 2, 1 }, + { "FighterPunchMiss", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterPunchHitThing", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterPunchHitWall", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterGrunt", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterAxeHitThing", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterHammerMiss", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterHammerHitThing", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterHammerHitWall", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterHammerContinuous", "", 32, -1, NULL, 0, 2, 1 }, + { "FighterHammerExplode", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterSwordFire", "", 80, -1, NULL, 0, 2, 1 }, + { "FighterSwordExplode", "", 80, -1, NULL, 0, 2, 1 }, + { "ClericCStaffFire", "", 80, -1, NULL, 0, 2, 1 }, + { "ClericCStaffExplode", "", 40, -1, NULL, 0, 2, 1 }, + { "ClericCStaffHitThing", "", 80, -1, NULL, 0, 2, 1 }, + { "ClericFlameFire", "", 80, -1, NULL, 0, 2, 1 }, + { "ClericFlameExplode", "", 80, -1, NULL, 0, 2, 1 }, + { "ClericFlameCircle", "", 80, -1, NULL, 0, 2, 1 }, + { "MageWandFire", "", 80, -1, NULL, 0, 2, 1 }, + { "MageLightningFire", "", 80, -1, NULL, 0, 2, 1 }, + { "MageLightningZap", "", 32, -1, NULL, 0, 2, 1 }, + { "MageLightningContinuous", "", 32, -1, NULL, 0, 2, 1 }, + { "MageLightningReady", "", 30, -1, NULL, 0, 2, 1 }, + { "MageShardsFire","", 80, -1, NULL, 0, 2, 1 }, + { "MageShardsExplode","", 36, -1, NULL, 0, 2, 1 }, + { "MageStaffFire","", 80, -1, NULL, 0, 2, 1 }, + { "MageStaffExplode","", 40, -1, NULL, 0, 2, 1 }, + { "Switch1", "", 32, -1, NULL, 0, 2, 1 }, + { "Switch2", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentSight", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentActive", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentPain", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentMeleeHit", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "SerpentBirth", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentFXContinuous", "", 32, -1, NULL, 0, 2, 1 }, + { "SerpentFXHit", "", 32, -1, NULL, 0, 2, 1 }, + { "PotteryExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "Drip", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurSight", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurActive", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurPain", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "CentaurLeaderAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "CentaurMissileExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "Wind", "", 1, -1, NULL, 0, 2, 1 }, + { "BishopSight", "", 32, -1, NULL, 0, 2, 1 }, + { "BishopActive", "", 32, -1, NULL, 0, 2, 1 }, + { "BishopPain", "", 32, -1, NULL, 0, 2, 1 }, + { "BishopAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "BishopDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "BishopMissileExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "BishopBlur", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonSight", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonActive", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonPain", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonMissileFire", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonMissileExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "DemonDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "WraithSight", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithActive", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithPain", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithMissileFire", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithMissileExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "WraithDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "PigActive1", "", 32, -1, NULL, 0, 2, 1 }, + { "PigActive2", "", 32, -1, NULL, 0, 2, 1 }, + { "PigPain", "", 32, -1, NULL, 0, 2, 1 }, + { "PigAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "PigDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "MaulatorSight", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorActive", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorPain", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorHamSwing", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorHamHit", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorMissileHit", "", 32, -1, NULL, 0, 2, 1 }, + { "MaulatorDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "FreezeDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "FreezeShatter", "", 40, -1, NULL, 0, 2, 1 }, + { "EttinSight", "", 32, -1, NULL, 0, 2, 1 }, + { "EttinActive", "", 32, -1, NULL, 0, 2, 1 }, + { "EttinPain", "", 32, -1, NULL, 0, 2, 1 }, + { "EttinAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "EttinDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "FireDemonSpawn", "", 32, -1, NULL, 0, 2, 1 }, + { "FireDemonActive", "", 32, -1, NULL, 0, 2, 1 }, + { "FireDemonPain", "", 32, -1, NULL, 0, 2, 1 }, + { "FireDemonAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "FireDemonMissileHit", "", 32, -1, NULL, 0, 2, 1 }, + { "FireDemonDeath", "", 40, -1, NULL, 0, 2, 1 }, + { "IceGuySight", "", 32, -1, NULL, 0, 2, 1 }, + { "IceGuyActive", "", 32, -1, NULL, 0, 2, 1 }, + { "IceGuyAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "IceGuyMissileExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "SorcererSight", "", 256, -1, NULL, 0, 2, 1 }, + { "SorcererActive", "", 256, -1, NULL, 0, 2, 1 }, + { "SorcererPain", "", 256, -1, NULL, 0, 2, 1 }, + { "SorcererSpellCast", "", 256, -1, NULL, 0, 2, 1 }, + { "SorcererBallWoosh", "", 256, -1, NULL, 0, 4, 1 }, + { "SorcererDeathScream", "", 256, -1, NULL, 0, 2, 1 }, + { "SorcererBishopSpawn", "", 80, -1, NULL, 0, 2, 1 }, + { "SorcererBallPop", "", 80, -1, NULL, 0, 2, 1 }, + { "SorcererBallBounce", "", 80, -1, NULL, 0, 3, 1 }, + { "SorcererBallExplode", "", 80, -1, NULL, 0, 3, 1 }, + { "SorcererBigBallExplode", "", 80, -1, NULL, 0, 3, 1 }, + { "SorcererHeadScream", "", 256, -1, NULL, 0, 2, 1 }, + { "DragonSight", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonActive", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonWingflap", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonAttack", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonPain", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonDeath", "", 64, -1, NULL, 0, 2, 1 }, + { "DragonFireballExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "KoraxSight", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxActive", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxPain", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxAttack", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxCommand", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxDeath", "", 256, -1, NULL, 0, 2, 1 }, + { "KoraxStep", "", 128, -1, NULL, 0, 2, 1 }, + { "ThrustSpikeRaise", "", 32, -1, NULL, 0, 2, 1 }, + { "ThrustSpikeLower", "", 32, -1, NULL, 0, 2, 1 }, + { "GlassShatter", "", 32, -1, NULL, 0, 2, 1 }, + { "FlechetteBounce", "", 32, -1, NULL, 0, 2, 1 }, + { "FlechetteExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "LavaMove", "", 36, -1, NULL, 0, 2, 1 }, + { "WaterMove", "", 36, -1, NULL, 0, 2, 1 }, + { "IceStartMove", "", 36, -1, NULL, 0, 2, 1 }, + { "EarthStartMove", "", 36, -1, NULL, 0, 2, 1 }, + { "WaterSplash", "", 32, -1, NULL, 0, 2, 1 }, + { "LavaSizzle", "", 32, -1, NULL, 0, 2, 1 }, + { "SludgeGloop", "", 32, -1, NULL, 0, 2, 1 }, + { "HolySymbolFire", "", 64, -1, NULL, 0, 2, 1 }, + { "SpiritActive", "", 32, -1, NULL, 0, 2, 1 }, + { "SpiritAttack", "", 32, -1, NULL, 0, 2, 1 }, + { "SpiritDie", "", 32, -1, NULL, 0, 2, 1 }, + { "ValveTurn", "", 36, -1, NULL, 0, 2, 1 }, + { "RopePull", "", 36, -1, NULL, 0, 2, 1 }, + { "FlyBuzz", "", 20, -1, NULL, 0, 2, 1 }, + { "Ignite", "", 32, -1, NULL, 0, 2, 1 }, + { "PuzzleSuccess", "", 256, -1, NULL, 0, 2, 1 }, + { "PuzzleFailFighter", "", 256, -1, NULL, 0, 2, 1 }, + { "PuzzleFailCleric", "", 256, -1, NULL, 0, 2, 1 }, + { "PuzzleFailMage", "", 256, -1, NULL, 0, 2, 1 }, + { "Earthquake", "", 32, -1, NULL, 0, 2, 1 }, + { "BellRing", "", 32, -1, NULL, 0, 2, 0 }, + { "TreeBreak", "", 32, -1, NULL, 0, 2, 1 }, + { "TreeExplode", "", 32, -1, NULL, 0, 2, 1 }, + { "SuitofArmorBreak", "", 32, -1, NULL, 0, 2, 1 }, + { "PoisonShroomPain", "", 20, -1, NULL, 0, 2, 1 }, + { "PoisonShroomDeath", "", 32, -1, NULL, 0, 2, 1 }, + { "Ambient1", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient2", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient3", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient4", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient5", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient6", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient7", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient8", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient9", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient10", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient11", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient12", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient13", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient14", "", 1, -1, NULL, 0, 1, 1 }, + { "Ambient15", "", 1, -1, NULL, 0, 1, 1 }, + { "StartupTick", "", 32, -1, NULL, 0, 2, 1 }, + { "SwitchOtherLevel", "", 32, -1, NULL, 0, 2, 1 }, + { "Respawn", "", 32, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceGreetings", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceReady", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceBlood", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceGame", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceBoard", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceWorship", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceMaybe", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceStrong", "", 512, -1, NULL, 0, 2, 1 }, + { "KoraxVoiceFace", "", 512, -1, NULL, 0, 2, 1 }, + { "BatScream", "", 32, -1, NULL, 0, 2, 1 }, + { "Chat", "", 512, -1, NULL, 0, 2, 1 }, + { "MenuMove", "", 32, -1, NULL, 0, 2, 1 }, + { "ClockTick", "", 32, -1, NULL, 0, 2, 1 }, + { "Fireball", "", 32, -1, NULL, 0, 2, 1 }, + { "PuppyBeat", "", 30, -1, NULL, 0, 2, 1 }, + { "MysticIncant", "", 32, -1, NULL, 0, 4, 1 } +}; diff --git a/src/hexen/sounds.h b/src/hexen/sounds.h new file mode 100644 index 00000000..00716383 --- /dev/null +++ b/src/hexen/sounds.h @@ -0,0 +1,311 @@ + +//************************************************************************** +//** +//** sounds.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sounds.h,v $ +//** $Revision: 1.74 $ +//** $Date: 95/10/10 21:51:20 $ +//** $Author: paul $ +//** +//************************************************************************** + +#ifndef __SOUNDSH__ +#define __SOUNDSH__ + +#include "soundst.h" + +#define MAX_SND_DIST 2025 +#define MAX_CHANNELS 16 + +// Music identifiers + +typedef enum +{ + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_e4m1, + mus_titl, + mus_intr, + mus_cptd, + NUMMUSIC +} musicenum_t; + +// Sound identifiers + +typedef enum +{ + SFX_NONE, + SFX_PLAYER_FIGHTER_NORMAL_DEATH, // class specific death screams + SFX_PLAYER_FIGHTER_CRAZY_DEATH, + SFX_PLAYER_FIGHTER_EXTREME1_DEATH, + SFX_PLAYER_FIGHTER_EXTREME2_DEATH, + SFX_PLAYER_FIGHTER_EXTREME3_DEATH, + SFX_PLAYER_FIGHTER_BURN_DEATH, + SFX_PLAYER_CLERIC_NORMAL_DEATH, + SFX_PLAYER_CLERIC_CRAZY_DEATH, + SFX_PLAYER_CLERIC_EXTREME1_DEATH, + SFX_PLAYER_CLERIC_EXTREME2_DEATH, + SFX_PLAYER_CLERIC_EXTREME3_DEATH, + SFX_PLAYER_CLERIC_BURN_DEATH, + SFX_PLAYER_MAGE_NORMAL_DEATH, + SFX_PLAYER_MAGE_CRAZY_DEATH, + SFX_PLAYER_MAGE_EXTREME1_DEATH, + SFX_PLAYER_MAGE_EXTREME2_DEATH, + SFX_PLAYER_MAGE_EXTREME3_DEATH, + SFX_PLAYER_MAGE_BURN_DEATH, + SFX_PLAYER_FIGHTER_PAIN, + SFX_PLAYER_CLERIC_PAIN, + SFX_PLAYER_MAGE_PAIN, + SFX_PLAYER_FIGHTER_GRUNT, + SFX_PLAYER_CLERIC_GRUNT, + SFX_PLAYER_MAGE_GRUNT, + SFX_PLAYER_LAND, + SFX_PLAYER_POISONCOUGH, + SFX_PLAYER_FIGHTER_FALLING_SCREAM, // class specific falling screams + SFX_PLAYER_CLERIC_FALLING_SCREAM, + SFX_PLAYER_MAGE_FALLING_SCREAM, + SFX_PLAYER_FALLING_SPLAT, + SFX_PLAYER_FIGHTER_FAILED_USE, + SFX_PLAYER_CLERIC_FAILED_USE, + SFX_PLAYER_MAGE_FAILED_USE, + SFX_PLATFORM_START, + SFX_PLATFORM_STARTMETAL, + SFX_PLATFORM_STOP, + SFX_STONE_MOVE, + SFX_METAL_MOVE, + SFX_DOOR_OPEN, + SFX_DOOR_LOCKED, + SFX_DOOR_METAL_OPEN, + SFX_DOOR_METAL_CLOSE, + SFX_DOOR_LIGHT_CLOSE, + SFX_DOOR_HEAVY_CLOSE, + SFX_DOOR_CREAK, + SFX_PICKUP_WEAPON, + SFX_PICKUP_ARTIFACT, + SFX_PICKUP_KEY, + SFX_PICKUP_ITEM, + SFX_PICKUP_PIECE, + SFX_WEAPON_BUILD, + SFX_ARTIFACT_USE, + SFX_ARTIFACT_BLAST, + SFX_TELEPORT, + SFX_THUNDER_CRASH, + SFX_FIGHTER_PUNCH_MISS, + SFX_FIGHTER_PUNCH_HITTHING, + SFX_FIGHTER_PUNCH_HITWALL, + SFX_FIGHTER_GRUNT, + SFX_FIGHTER_AXE_HITTHING, + SFX_FIGHTER_HAMMER_MISS, + SFX_FIGHTER_HAMMER_HITTHING, + SFX_FIGHTER_HAMMER_HITWALL, + SFX_FIGHTER_HAMMER_CONTINUOUS, + SFX_FIGHTER_HAMMER_EXPLODE, + SFX_FIGHTER_SWORD_FIRE, + SFX_FIGHTER_SWORD_EXPLODE, + SFX_CLERIC_CSTAFF_FIRE, + SFX_CLERIC_CSTAFF_EXPLODE, + SFX_CLERIC_CSTAFF_HITTHING, + SFX_CLERIC_FLAME_FIRE, + SFX_CLERIC_FLAME_EXPLODE, + SFX_CLERIC_FLAME_CIRCLE, + SFX_MAGE_WAND_FIRE, + SFX_MAGE_LIGHTNING_FIRE, + SFX_MAGE_LIGHTNING_ZAP, + SFX_MAGE_LIGHTNING_CONTINUOUS, + SFX_MAGE_LIGHTNING_READY, + SFX_MAGE_SHARDS_FIRE, + SFX_MAGE_SHARDS_EXPLODE, + SFX_MAGE_STAFF_FIRE, + SFX_MAGE_STAFF_EXPLODE, + SFX_SWITCH1, + SFX_SWITCH2, + SFX_SERPENT_SIGHT, + SFX_SERPENT_ACTIVE, + SFX_SERPENT_PAIN, + SFX_SERPENT_ATTACK, + SFX_SERPENT_MELEEHIT, + SFX_SERPENT_DEATH, + SFX_SERPENT_BIRTH, + SFX_SERPENTFX_CONTINUOUS, + SFX_SERPENTFX_HIT, + SFX_POTTERY_EXPLODE, + SFX_DRIP, + SFX_CENTAUR_SIGHT, + SFX_CENTAUR_ACTIVE, + SFX_CENTAUR_PAIN, + SFX_CENTAUR_ATTACK, + SFX_CENTAUR_DEATH, + SFX_CENTAURLEADER_ATTACK, + SFX_CENTAUR_MISSILE_EXPLODE, + SFX_WIND, + SFX_BISHOP_SIGHT, + SFX_BISHOP_ACTIVE, + SFX_BISHOP_PAIN, + SFX_BISHOP_ATTACK, + SFX_BISHOP_DEATH, + SFX_BISHOP_MISSILE_EXPLODE, + SFX_BISHOP_BLUR, + SFX_DEMON_SIGHT, + SFX_DEMON_ACTIVE, + SFX_DEMON_PAIN, + SFX_DEMON_ATTACK, + SFX_DEMON_MISSILE_FIRE, + SFX_DEMON_MISSILE_EXPLODE, + SFX_DEMON_DEATH, + SFX_WRAITH_SIGHT, + SFX_WRAITH_ACTIVE, + SFX_WRAITH_PAIN, + SFX_WRAITH_ATTACK, + SFX_WRAITH_MISSILE_FIRE, + SFX_WRAITH_MISSILE_EXPLODE, + SFX_WRAITH_DEATH, + SFX_PIG_ACTIVE1, + SFX_PIG_ACTIVE2, + SFX_PIG_PAIN, + SFX_PIG_ATTACK, + SFX_PIG_DEATH, + SFX_MAULATOR_SIGHT, + SFX_MAULATOR_ACTIVE, + SFX_MAULATOR_PAIN, + SFX_MAULATOR_HAMMER_SWING, + SFX_MAULATOR_HAMMER_HIT, + SFX_MAULATOR_MISSILE_HIT, + SFX_MAULATOR_DEATH, + SFX_FREEZE_DEATH, + SFX_FREEZE_SHATTER, + SFX_ETTIN_SIGHT, + SFX_ETTIN_ACTIVE, + SFX_ETTIN_PAIN, + SFX_ETTIN_ATTACK, + SFX_ETTIN_DEATH, + SFX_FIRED_SPAWN, + SFX_FIRED_ACTIVE, + SFX_FIRED_PAIN, + SFX_FIRED_ATTACK, + SFX_FIRED_MISSILE_HIT, + SFX_FIRED_DEATH, + SFX_ICEGUY_SIGHT, + SFX_ICEGUY_ACTIVE, + SFX_ICEGUY_ATTACK, + SFX_ICEGUY_FX_EXPLODE, + SFX_SORCERER_SIGHT, + SFX_SORCERER_ACTIVE, + SFX_SORCERER_PAIN, + SFX_SORCERER_SPELLCAST, + SFX_SORCERER_BALLWOOSH, + SFX_SORCERER_DEATHSCREAM, + SFX_SORCERER_BISHOPSPAWN, + SFX_SORCERER_BALLPOP, + SFX_SORCERER_BALLBOUNCE, + SFX_SORCERER_BALLEXPLODE, + SFX_SORCERER_BIGBALLEXPLODE, + SFX_SORCERER_HEADSCREAM, + SFX_DRAGON_SIGHT, + SFX_DRAGON_ACTIVE, + SFX_DRAGON_WINGFLAP, + SFX_DRAGON_ATTACK, + SFX_DRAGON_PAIN, + SFX_DRAGON_DEATH, + SFX_DRAGON_FIREBALL_EXPLODE, + SFX_KORAX_SIGHT, + SFX_KORAX_ACTIVE, + SFX_KORAX_PAIN, + SFX_KORAX_ATTACK, + SFX_KORAX_COMMAND, + SFX_KORAX_DEATH, + SFX_KORAX_STEP, + SFX_THRUSTSPIKE_RAISE, + SFX_THRUSTSPIKE_LOWER, + SFX_STAINEDGLASS_SHATTER, + SFX_FLECHETTE_BOUNCE, + SFX_FLECHETTE_EXPLODE, + SFX_LAVA_MOVE, + SFX_WATER_MOVE, + SFX_ICE_STARTMOVE, + SFX_EARTH_STARTMOVE, + SFX_WATER_SPLASH, + SFX_LAVA_SIZZLE, + SFX_SLUDGE_GLOOP, + SFX_CHOLY_FIRE, + SFX_SPIRIT_ACTIVE, + SFX_SPIRIT_ATTACK, + SFX_SPIRIT_DIE, + SFX_VALVE_TURN, + SFX_ROPE_PULL, + SFX_FLY_BUZZ, + SFX_IGNITE, + SFX_PUZZLE_SUCCESS, + SFX_PUZZLE_FAIL_FIGHTER, + SFX_PUZZLE_FAIL_CLERIC, + SFX_PUZZLE_FAIL_MAGE, + SFX_EARTHQUAKE, + SFX_BELLRING, + SFX_TREE_BREAK, + SFX_TREE_EXPLODE, + SFX_SUITOFARMOR_BREAK, + SFX_POISONSHROOM_PAIN, + SFX_POISONSHROOM_DEATH, + SFX_AMBIENT1, + SFX_AMBIENT2, + SFX_AMBIENT3, + SFX_AMBIENT4, + SFX_AMBIENT5, + SFX_AMBIENT6, + SFX_AMBIENT7, + SFX_AMBIENT8, + SFX_AMBIENT9, + SFX_AMBIENT10, + SFX_AMBIENT11, + SFX_AMBIENT12, + SFX_AMBIENT13, + SFX_AMBIENT14, + SFX_AMBIENT15, + SFX_STARTUP_TICK, + SFX_SWITCH_OTHERLEVEL, + SFX_RESPAWN, + SFX_KORAX_VOICE_1, + SFX_KORAX_VOICE_2, + SFX_KORAX_VOICE_3, + SFX_KORAX_VOICE_4, + SFX_KORAX_VOICE_5, + SFX_KORAX_VOICE_6, + SFX_KORAX_VOICE_7, + SFX_KORAX_VOICE_8, + SFX_KORAX_VOICE_9, + SFX_BAT_SCREAM, + SFX_CHAT, + SFX_MENU_MOVE, + SFX_CLOCK_TICK, + SFX_FIREBALL, + SFX_PUPPYBEAT, + SFX_MYSTICINCANT, + NUMSFX +} sfxenum_t; + +#endif diff --git a/src/hexen/soundst.h b/src/hexen/soundst.h new file mode 100644 index 00000000..963ba174 --- /dev/null +++ b/src/hexen/soundst.h @@ -0,0 +1,81 @@ + +//************************************************************************** +//** +//** soundst.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: soundst.h,v $ +//** $Revision: 1.13 $ +//** $Date: 95/10/12 18:01:27 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#ifndef __SOUNDSTH__ +#define __SOUNDSTH__ + +typedef struct +{ + char name[8]; + int p1; +} musicinfo_t; + +typedef struct sfxinfo_s +{ + char tagName[32]; + char lumpname[12]; // Only need 9 bytes, but padded out to be dword aligned + //struct sfxinfo_s *link; // Make alias for another sound + int priority; // Higher priority takes precendence + int usefulness; // Determines when a sound should be cached out + void *snd_ptr; + int lumpnum; + int numchannels; // total number of channels a sound type may occupy + boolean changePitch; +} sfxinfo_t; + +typedef struct +{ + mobj_t *mo; + int sound_id; + int handle; + int volume; + int pitch; + int priority; +} channel_t; + +typedef struct +{ + long id; + unsigned short priority; + char *name; + mobj_t *mo; + int distance; +} ChanInfo_t; + +typedef struct +{ + int channelCount; + int musicVolume; + int soundVolume; + ChanInfo_t chan[8]; +} SoundInfo_t; + +extern int snd_MaxVolume; +extern int snd_MusicVolume; + +void S_Start(void); +void S_StartSound(mobj_t *origin, int sound_id); +int S_GetSoundID(char *name); +void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume); +void S_StopSound(mobj_t *origin); +void S_StopAllSound(void); +void S_PauseSound(void); +void S_ResumeSound(void); +void S_UpdateSounds(mobj_t *listener); +void S_StartSong(int song, boolean loop); +void S_StartSongName(char *songLump, boolean loop); +void S_Init(void); +void S_GetChannelInfo(SoundInfo_t *s); +void S_SetMusicVolume(void); +boolean S_GetSoundPlayingInfo(mobj_t *mobj, int sound_id); + +#endif diff --git a/src/hexen/st_start.c b/src/hexen/st_start.c new file mode 100644 index 00000000..3c943d5c --- /dev/null +++ b/src/hexen/st_start.c @@ -0,0 +1,347 @@ + +//************************************************************************** +//** +//** st_start.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: st_start.c,v $ +//** $Revision: 1.21 $ +//** $Date: 95/12/21 15:03:51 $ +//** $Author: bgokey $ +//** +//************************************************************************** + + +// HEADER FILES ------------------------------------------------------------ +#ifdef __WATCOMC__ + #include + #include + #include +#else + #include + #include + #define O_BINARY 0 +#endif +#include "h2def.h" +#include +#include // Needed for next as well as dos +#include "st_start.h" + + +// MACROS ------------------------------------------------------------------ +#define ST_MAX_NOTCHES 32 +#define ST_NOTCH_WIDTH 16 +#define ST_NOTCH_HEIGHT 23 +#define ST_PROGRESS_X 64 // Start of notches x screen pos. +#define ST_PROGRESS_Y 441 // Start of notches y screen pos. + +#define ST_NETPROGRESS_X 288 +#define ST_NETPROGRESS_Y 32 +#define ST_NETNOTCH_WIDTH 8 +#define ST_NETNOTCH_HEIGHT 16 +#define ST_MAX_NETNOTCHES 8 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +extern void SetVideoModeHR(void); +extern void ClearScreenHR(void); +extern void SlamHR(char *buffer); +extern void SlamBlockHR(int x, int y, int w, int h, char *src); +extern void InitPaletteHR(void); +extern void SetPaletteHR(byte *palette); +extern void GetPaletteHR(byte *palette); +extern void FadeToPaletteHR(byte *palette); +extern void FadeToBlackHR(void); +extern void BlackPaletteHR(void); +extern void I_StartupReadKeys(void); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- +char *ST_LoadScreen(void); +void ST_UpdateNotches(int notchPosition); +void ST_UpdateNetNotches(int notchPosition); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ +char *bitmap = NULL; + +char notchTable[]= +{ + // plane 0 + 0x00, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x03, 0xC0, + 0x0F, 0x90, 0x1B, 0x68, 0x3D, 0xBC, 0x3F, 0xFC, 0x20, 0x08, 0x20, 0x08, + 0x2F, 0xD8, 0x37, 0xD8, 0x37, 0xF8, 0x1F, 0xF8, 0x1C, 0x50, + + // plane 1 + 0x00, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xA0, + 0x30, 0x6C, 0x24, 0x94, 0x42, 0x4A, 0x60, 0x0E, 0x60, 0x06, 0x7F, 0xF6, + 0x7F, 0xF6, 0x7F, 0xF6, 0x5E, 0xF6, 0x38, 0x16, 0x23, 0xAC, + + // plane 2 + 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x03, 0xE0, + 0x30, 0x6C, 0x24, 0x94, 0x52, 0x6A, 0x7F, 0xFE, 0x60, 0x0E, 0x60, 0x0E, + 0x6F, 0xD6, 0x77, 0xD6, 0x56, 0xF6, 0x38, 0x36, 0x23, 0xAC, + + // plane 3 + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0x80, 0x02, 0x40, + 0x0F, 0x90, 0x1B, 0x68, 0x3D, 0xB4, 0x1F, 0xF0, 0x1F, 0xF8, 0x1F, 0xF8, + 0x10, 0x28, 0x08, 0x28, 0x29, 0x08, 0x07, 0xE8, 0x1C, 0x50 +}; + + +// Red Network Progress notches +char netnotchTable[]= +{ + // plane 0 + 0x80, 0x50, 0xD0, 0xf0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xD0, 0xF0, 0xC0, + 0x70, 0x50, 0x80, 0x60, + + // plane 1 + 0x60, 0xE0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0xA0, + 0xA0, 0xE0, 0x60, 0x00, + + // plane 2 + 0x80, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x10, 0x10, 0x80, 0x60, + + // plane 3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +// CODE -------------------------------------------------------------------- + + + +//-------------------------------------------------------------------------- +// +// Startup Screen Functions +// +//-------------------------------------------------------------------------- + + +//========================================================================== +// +// ST_Init - Do the startup screen +// +//========================================================================== + +void ST_Init(void) +{ +#ifdef __WATCOMC__ + char *pal; + char *buffer; + + if (!debugmode) + { + // Set 640x480x16 mode + SetVideoModeHR(); + ClearScreenHR(); + InitPaletteHR(); + BlackPaletteHR(); + + // Load graphic + buffer = ST_LoadScreen(); + pal = buffer; + bitmap = buffer + 16*3; + + SlamHR(bitmap); + FadeToPaletteHR(pal); + Z_Free(buffer); + } +#endif +} + + +void ST_Done(void) +{ +#ifdef __WATCOMC__ + ClearScreenHR(); +#endif +} + + +//========================================================================== +// +// ST_UpdateNotches +// +//========================================================================== + +void ST_UpdateNotches(int notchPosition) +{ +#ifdef __WATCOMC__ + int x = ST_PROGRESS_X + notchPosition*ST_NOTCH_WIDTH; + int y = ST_PROGRESS_Y; + SlamBlockHR(x,y, ST_NOTCH_WIDTH,ST_NOTCH_HEIGHT, notchTable); +#endif +} + + +//========================================================================== +// +// ST_UpdateNetNotches - indicates network progress +// +//========================================================================== + +void ST_UpdateNetNotches(int notchPosition) +{ +#ifdef __WATCOMC__ + int x = ST_NETPROGRESS_X + notchPosition*ST_NETNOTCH_WIDTH; + int y = ST_NETPROGRESS_Y; + SlamBlockHR(x,y, ST_NETNOTCH_WIDTH, ST_NETNOTCH_HEIGHT, netnotchTable); +#endif +} + + +//========================================================================== +// +// ST_Progress - increments progress indicator +// +//========================================================================== + +void ST_Progress(void) +{ +#ifdef __WATCOMC__ + static int notchPosition=0; + + // Check for ESC press -- during startup all events eaten here + I_StartupReadKeys(); + + if (debugmode) + { + printf("."); + } + else + { + if(notchPosition= 80 ) + { + I_Error("Long debug message has overwritten memory"); + } + +#ifdef __WATCOMC__ + if (debugmode) + { + printf(buffer); + } +#else + printf(buffer); +#endif +} + +//========================================================================== +// +// ST_RealMessage - gives user message +// +//========================================================================== + +void ST_RealMessage(char *message, ...) +{ + va_list argptr; + char buffer[80]; + + va_start(argptr, message); + vsprintf(buffer, message, argptr); + va_end(argptr); + + if ( strlen(buffer) >= 80 ) + { + I_Error("Long debug message has overwritten memory\n"); + } + + printf(buffer); // Always print these messages +} + + + +//========================================================================== +// +// ST_LoadScreen - loads startup graphic +// +//========================================================================== + + +char *ST_LoadScreen(void) +{ + int length,lump; + char *buffer; + + lump = W_GetNumForName("STARTUP"); + length = W_LumpLength(lump); + buffer = (char *)Z_Malloc(length, PU_STATIC, NULL); + W_ReadLump(lump, buffer); + return(buffer); +} diff --git a/src/hexen/st_start.h b/src/hexen/st_start.h new file mode 100644 index 00000000..e3ef6686 --- /dev/null +++ b/src/hexen/st_start.h @@ -0,0 +1,28 @@ + +//************************************************************************** +//** +//** template.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: st_start.h,v $ +//** $Revision: 1.5 $ +//** $Date: 95/10/11 23:35:33 $ +//** $Author: paul $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- +extern void ST_Init(void); +extern void ST_Done(void); +extern void ST_Message(char *message, ...); +extern void ST_RealMessage(char *message, ...); +extern void ST_Progress(void); +extern void ST_NetProgress(void); +extern void ST_NetDone(void); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ diff --git a/src/hexen/sv_save.c b/src/hexen/sv_save.c new file mode 100644 index 00000000..9ee23acd --- /dev/null +++ b/src/hexen/sv_save.c @@ -0,0 +1,1756 @@ + +//************************************************************************** +//** +//** sv_save.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sv_save.c,v $ +//** $Revision: 1.36 $ +//** $Date: 95/10/17 00:10:02 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "p_local.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_TARGET_PLAYERS 512 +#define MOBJ_NULL -1 +#define MOBJ_XX_PLAYER -2 +#define GET_BYTE (*SavePtr.b++) +#define GET_WORD (*SavePtr.w++) +#define GET_LONG (*SavePtr.l++) +#define MAX_MAPS 99 +#define BASE_SLOT 6 +#define REBORN_SLOT 7 +#define REBORN_DESCRIPTION "TEMP GAME" +#define MAX_THINKER_SIZE 256 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + ASEG_GAME_HEADER = 101, + ASEG_MAP_HEADER, + ASEG_WORLD, + ASEG_POLYOBJS, + ASEG_MOBJS, + ASEG_THINKERS, + ASEG_SCRIPTS, + ASEG_PLAYERS, + ASEG_SOUNDS, + ASEG_MISC, + ASEG_END +} gameArchiveSegment_t; + +typedef enum +{ + TC_NULL, + TC_MOVE_CEILING, + TC_VERTICAL_DOOR, + TC_MOVE_FLOOR, + TC_PLAT_RAISE, + TC_INTERPRET_ACS, + TC_FLOOR_WAGGLE, + TC_LIGHT, + TC_PHASE, + TC_BUILD_PILLAR, + TC_ROTATE_POLY, + TC_MOVE_POLY, + TC_POLY_DOOR +} thinkClass_t; + +typedef struct +{ + thinkClass_t tClass; + think_t thinkerFunc; + void (*mangleFunc)(); + void (*restoreFunc)(); + size_t size; +} thinkInfo_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; +} ssthinker_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +void P_SpawnPlayer(mapthing_t *mthing); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void ArchiveWorld(void); +static void UnarchiveWorld(void); +static void ArchivePolyobjs(void); +static void UnarchivePolyobjs(void); +static void ArchiveMobjs(void); +static void UnarchiveMobjs(void); +static void ArchiveThinkers(void); +static void UnarchiveThinkers(void); +static void ArchiveScripts(void); +static void UnarchiveScripts(void); +static void ArchivePlayers(void); +static void UnarchivePlayers(void); +static void ArchiveSounds(void); +static void UnarchiveSounds(void); +static void ArchiveMisc(void); +static void UnarchiveMisc(void); +static void SetMobjArchiveNums(void); +static void RemoveAllThinkers(void); +static void MangleMobj(mobj_t *mobj); +static void RestoreMobj(mobj_t *mobj); +static int GetMobjNum(mobj_t *mobj); +static void SetMobjPtr(int *archiveNum); +static void MangleSSThinker(ssthinker_t *sst); +static void RestoreSSThinker(ssthinker_t *sst); +static void RestoreSSThinkerNoSD(ssthinker_t *sst); +static void MangleScript(acs_t *script); +static void RestoreScript(acs_t *script); +static void RestorePlatRaise(plat_t *plat); +static void RestoreMoveCeiling(ceiling_t *ceiling); +static void AssertSegment(gameArchiveSegment_t segType); +static void ClearSaveSlot(int slot); +static void CopySaveSlot(int sourceSlot, int destSlot); +static void CopyFile(char *sourceName, char *destName); +static boolean ExistingFile(char *name); +static void OpenStreamOut(char *fileName); +static void CloseStreamOut(void); +static void StreamOutBuffer(void *buffer, int size); +static void StreamOutByte(byte val); +static void StreamOutWord(unsigned short val); +static void StreamOutLong(unsigned int val); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern int ACScriptCount; +extern byte *ActionCodeBase; +extern acsInfo_t *ACSInfo; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +char *SavePath; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int MobjCount; +static mobj_t **MobjList; +static int **TargetPlayerAddrs; +static int TargetPlayerCount; +static byte *SaveBuffer; +static boolean SavingPlayers; +static union +{ + byte *b; + short *w; + int *l; +} SavePtr; +static FILE *SavingFP; + +// This list has been prioritized using frequency estimates +static thinkInfo_t ThinkerInfo[] = +{ + { + TC_MOVE_FLOOR, + T_MoveFloor, + MangleSSThinker, + RestoreSSThinker, + sizeof(floormove_t) + }, + { + TC_PLAT_RAISE, + T_PlatRaise, + MangleSSThinker, + RestorePlatRaise, + sizeof(plat_t) + }, + { + TC_MOVE_CEILING, + T_MoveCeiling, + MangleSSThinker, + RestoreMoveCeiling, + sizeof(ceiling_t) + }, + { + TC_LIGHT, + T_Light, + MangleSSThinker, + RestoreSSThinkerNoSD, + sizeof(light_t) + }, + { + TC_VERTICAL_DOOR, + T_VerticalDoor, + MangleSSThinker, + RestoreSSThinker, + sizeof(vldoor_t) + }, + { + TC_PHASE, + T_Phase, + MangleSSThinker, + RestoreSSThinkerNoSD, + sizeof(phase_t) + }, + { + TC_INTERPRET_ACS, + T_InterpretACS, + MangleScript, + RestoreScript, + sizeof(acs_t) + }, + { + TC_ROTATE_POLY, + T_RotatePoly, + NULL, + NULL, + sizeof(polyevent_t) + }, + { + TC_BUILD_PILLAR, + T_BuildPillar, + MangleSSThinker, + RestoreSSThinker, + sizeof(pillar_t) + }, + { + TC_MOVE_POLY, + T_MovePoly, + NULL, + NULL, + sizeof(polyevent_t) + }, + { + TC_POLY_DOOR, + T_PolyDoor, + NULL, + NULL, + sizeof(polydoor_t) + }, + { + TC_FLOOR_WAGGLE, + T_FloorWaggle, + MangleSSThinker, + RestoreSSThinker, + sizeof(floorWaggle_t) + }, + { // Terminator + TC_NULL, NULL, NULL, NULL, 0 + } +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SV_SaveGame +// +//========================================================================== + +void SV_SaveGame(int slot, char *description) +{ + char fileName[100]; + char versionText[HXS_VERSION_TEXT_LENGTH]; + + // Open the output file + sprintf(fileName, "%shex6.hxs", SavePath); + OpenStreamOut(fileName); + + // Write game save description + StreamOutBuffer(description, HXS_DESCRIPTION_LENGTH); + + // Write version info + memset(versionText, 0, HXS_VERSION_TEXT_LENGTH); + strcpy(versionText, HXS_VERSION_TEXT); + StreamOutBuffer(versionText, HXS_VERSION_TEXT_LENGTH); + + // Place a header marker + StreamOutLong(ASEG_GAME_HEADER); + + // Write current map and difficulty + StreamOutByte(gamemap); + StreamOutByte(gameskill); + + // Write global script info + StreamOutBuffer(WorldVars, sizeof(WorldVars)); + StreamOutBuffer(ACSStore, sizeof(ACSStore)); + + ArchivePlayers(); + + // Place a termination marker + StreamOutLong(ASEG_END); + + // Close the output file + CloseStreamOut(); + + // Save out the current map + SV_SaveMap(true); // true = save player info + + // Clear all save files at destination slot + ClearSaveSlot(slot); + + // Copy base slot to destination slot + CopySaveSlot(BASE_SLOT, slot); +} + +//========================================================================== +// +// SV_SaveMap +// +//========================================================================== + +void SV_SaveMap(boolean savePlayers) +{ + char fileName[100]; + + SavingPlayers = savePlayers; + + // Open the output file + sprintf(fileName, "%shex6%02d.hxs", SavePath, gamemap); + OpenStreamOut(fileName); + + // Place a header marker + StreamOutLong(ASEG_MAP_HEADER); + + // Write the level timer + StreamOutLong(leveltime); + + // Set the mobj archive numbers + SetMobjArchiveNums(); + + ArchiveWorld(); + ArchivePolyobjs(); + ArchiveMobjs(); + ArchiveThinkers(); + ArchiveScripts(); + ArchiveSounds(); + ArchiveMisc(); + + // Place a termination marker + StreamOutLong(ASEG_END); + + // Close the output file + CloseStreamOut(); +} + +//========================================================================== +// +// SV_LoadGame +// +//========================================================================== + +void SV_LoadGame(int slot) +{ + int i; + char fileName[100]; + player_t playerBackup[MAXPLAYERS]; + mobj_t *mobj; + + // Copy all needed save files to the base slot + if(slot != BASE_SLOT) + { + ClearSaveSlot(BASE_SLOT); + CopySaveSlot(slot, BASE_SLOT); + } + + // Create the name + sprintf(fileName, "%shex6.hxs", SavePath); + + // Load the file + M_ReadFile(fileName, &SaveBuffer); + + // Set the save pointer and skip the description field + SavePtr.b = SaveBuffer+HXS_DESCRIPTION_LENGTH; + + // Check the version text + if(strcmp(SavePtr.b, HXS_VERSION_TEXT)) + { // Bad version + return; + } + SavePtr.b += HXS_VERSION_TEXT_LENGTH; + + AssertSegment(ASEG_GAME_HEADER); + + gameepisode = 1; + gamemap = GET_BYTE; + gameskill = GET_BYTE; + + // Read global script info + memcpy(WorldVars, SavePtr.b, sizeof(WorldVars)); + SavePtr.b += sizeof(WorldVars); + memcpy(ACSStore, SavePtr.b, sizeof(ACSStore)); + SavePtr.b += sizeof(ACSStore); + + // Read the player structures + UnarchivePlayers(); + + AssertSegment(ASEG_END); + + Z_Free(SaveBuffer); + + // Save player structs + for(i = 0; i < MAXPLAYERS; i++) + { + playerBackup[i] = players[i]; + } + + // Load the current map + SV_LoadMap(); + + // Don't need the player mobj relocation info for load game + Z_Free(TargetPlayerAddrs); + + // Restore player structs + inv_ptr = 0; + curpos = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + mobj = players[i].mo; + players[i] = playerBackup[i]; + players[i].mo = mobj; + if(i == consoleplayer) + { + players[i].readyArtifact = players[i].inventory[inv_ptr].type; + } + } +} + +//========================================================================== +// +// SV_UpdateRebornSlot +// +// Copies the base slot to the reborn slot. +// +//========================================================================== + +void SV_UpdateRebornSlot(void) +{ + ClearSaveSlot(REBORN_SLOT); + CopySaveSlot(BASE_SLOT, REBORN_SLOT); +} + +//========================================================================== +// +// SV_ClearRebornSlot +// +//========================================================================== + +void SV_ClearRebornSlot(void) +{ + ClearSaveSlot(REBORN_SLOT); +} + +//========================================================================== +// +// SV_MapTeleport +// +//========================================================================== + +void SV_MapTeleport(int map, int position) +{ + int i; + int j; + char fileName[100]; + player_t playerBackup[MAXPLAYERS]; + mobj_t *targetPlayerMobj; + mobj_t *mobj; + int inventoryPtr; + int currentInvPos; + boolean rClass; + boolean playerWasReborn; + boolean oldWeaponowned[NUMWEAPONS]; + int oldKeys; + int oldPieces; + int bestWeapon; + + if(!deathmatch) + { + if(P_GetMapCluster(gamemap) == P_GetMapCluster(map)) + { // Same cluster - save map without saving player mobjs + SV_SaveMap(false); + } + else + { // Entering new cluster - clear base slot + ClearSaveSlot(BASE_SLOT); + } + } + + // Store player structs for later + rClass = randomclass; + randomclass = false; + for(i = 0; i < MAXPLAYERS; i++) + { + playerBackup[i] = players[i]; + } + + // Save some globals that get trashed during the load + inventoryPtr = inv_ptr; + currentInvPos = curpos; + + // Only SV_LoadMap() uses TargetPlayerAddrs, so it's NULLed here + // for the following check (player mobj redirection) + TargetPlayerAddrs = NULL; + + gamemap = map; + sprintf(fileName, "%shex6%02d.hxs", SavePath, gamemap); + if(!deathmatch && ExistingFile(fileName)) + { // Unarchive map + SV_LoadMap(); + } + else + { // New map + G_InitNew(gameskill, gameepisode, gamemap); + + // Destroy all freshly spawned players + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + P_RemoveMobj(players[i].mo); + } + } + } + + // Restore player structs + targetPlayerMobj = NULL; + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + players[i] = playerBackup[i]; + P_ClearMessage(&players[i]); + players[i].attacker = NULL; + players[i].poisoner = NULL; + + if(netgame) + { + if(players[i].playerstate == PST_DEAD) + { // In a network game, force all players to be alive + players[i].playerstate = PST_REBORN; + } + if(!deathmatch) + { // Cooperative net-play, retain keys and weapons + oldKeys = players[i].keys; + oldPieces = players[i].pieces; + for(j = 0; j < NUMWEAPONS; j++) + { + oldWeaponowned[j] = players[i].weaponowned[j]; + } + } + } + playerWasReborn = (players[i].playerstate == PST_REBORN); + if(deathmatch) + { + memset(players[i].frags, 0, sizeof(players[i].frags)); + mobj = P_SpawnMobj(playerstarts[0][i].x<<16, + playerstarts[0][i].y<<16, 0, MT_PLAYER_FIGHTER); + players[i].mo = mobj; + G_DeathMatchSpawnPlayer(i); + P_RemoveMobj(mobj); + } + else + { + P_SpawnPlayer(&playerstarts[position][i]); + } + + if(playerWasReborn && netgame && !deathmatch) + { // Restore keys and weapons when reborn in co-op + players[i].keys = oldKeys; + players[i].pieces = oldPieces; + for(bestWeapon = 0, j = 0; j < NUMWEAPONS; j++) + { + if(oldWeaponowned[j]) + { + bestWeapon = j; + players[i].weaponowned[j] = true; + } + } + players[i].mana[MANA_1] = 25; + players[i].mana[MANA_2] = 25; + if(bestWeapon) + { // Bring up the best weapon + players[i].pendingweapon = bestWeapon; + } + } + + if(targetPlayerMobj == NULL) + { // The poor sap + targetPlayerMobj = players[i].mo; + } + } + randomclass = rClass; + + // Redirect anything targeting a player mobj + if(TargetPlayerAddrs) + { + for(i = 0; i < TargetPlayerCount; i++) + { + *TargetPlayerAddrs[i] = (int)targetPlayerMobj; + } + Z_Free(TargetPlayerAddrs); + } + + // Destroy all things touching players + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + P_TeleportMove(players[i].mo, players[i].mo->x, + players[i].mo->y); + } + } + + // Restore trashed globals + inv_ptr = inventoryPtr; + curpos = currentInvPos; + + // Launch waiting scripts + if(!deathmatch) + { + P_CheckACSStore(); + } + + // For single play, save immediately into the reborn slot + if(!netgame) + { + SV_SaveGame(REBORN_SLOT, REBORN_DESCRIPTION); + } +} + +//========================================================================== +// +// SV_GetRebornSlot +// +//========================================================================== + +int SV_GetRebornSlot(void) +{ + return(REBORN_SLOT); +} + +//========================================================================== +// +// SV_RebornSlotAvailable +// +// Returns true if the reborn slot is available. +// +//========================================================================== + +boolean SV_RebornSlotAvailable(void) +{ + char fileName[100]; + + sprintf(fileName, "%shex%d.hxs", SavePath, REBORN_SLOT); + return ExistingFile(fileName); +} + +//========================================================================== +// +// SV_LoadMap +// +//========================================================================== + +void SV_LoadMap(void) +{ + char fileName[100]; + + // Load a base level + G_InitNew(gameskill, gameepisode, gamemap); + + // Remove all thinkers + RemoveAllThinkers(); + + // Create the name + sprintf(fileName, "%shex6%02d.hxs", SavePath, gamemap); + + // Load the file + M_ReadFile(fileName, &SaveBuffer); + SavePtr.b = SaveBuffer; + + AssertSegment(ASEG_MAP_HEADER); + + // Read the level timer + leveltime = GET_LONG; + + UnarchiveWorld(); + UnarchivePolyobjs(); + UnarchiveMobjs(); + UnarchiveThinkers(); + UnarchiveScripts(); + UnarchiveSounds(); + UnarchiveMisc(); + + AssertSegment(ASEG_END); + + // Free mobj list and save buffer + Z_Free(MobjList); + Z_Free(SaveBuffer); +} + +//========================================================================== +// +// SV_InitBaseSlot +// +//========================================================================== + +void SV_InitBaseSlot(void) +{ + ClearSaveSlot(BASE_SLOT); +} + +//========================================================================== +// +// ArchivePlayers +// +//========================================================================== + +static void ArchivePlayers(void) +{ + int i; + int j; + player_t tempPlayer; + + StreamOutLong(ASEG_PLAYERS); + for(i = 0; i < MAXPLAYERS; i++) + { + StreamOutByte(playeringame[i]); + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + StreamOutByte(PlayerClass[i]); + tempPlayer = players[i]; + for(j = 0; j < NUMPSPRITES; j++) + { + if(tempPlayer.psprites[j].state) + { + tempPlayer.psprites[j].state = + (state_t *)(tempPlayer.psprites[j].state-states); + } + } + StreamOutBuffer(&tempPlayer, sizeof(player_t)); + } +} + +//========================================================================== +// +// UnarchivePlayers +// +//========================================================================== + +static void UnarchivePlayers(void) +{ + int i, j; + + AssertSegment(ASEG_PLAYERS); + for(i = 0; i < MAXPLAYERS; i++) + { + playeringame[i] = GET_BYTE; + } + for(i = 0; i < MAXPLAYERS; i++) + { + if(!playeringame[i]) + { + continue; + } + PlayerClass[i] = GET_BYTE; + memcpy(&players[i], SavePtr.b, sizeof(player_t)); + SavePtr.b += sizeof(player_t); + players[i].mo = NULL; // Will be set when unarc thinker + P_ClearMessage(&players[i]); + players[i].attacker = NULL; + players[i].poisoner = NULL; + for(j = 0; j < NUMPSPRITES; j++) + { + if(players[i].psprites[j].state) + { + players[i].psprites[j].state = + &states[(int)players[i].psprites[j].state]; + } + } + } +} + +//========================================================================== +// +// ArchiveWorld +// +//========================================================================== + +static void ArchiveWorld(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + StreamOutLong(ASEG_WORLD); + for(i = 0, sec = sectors; i < numsectors; i++, sec++) + { + StreamOutWord(sec->floorheight>>FRACBITS); + StreamOutWord(sec->ceilingheight>>FRACBITS); + StreamOutWord(sec->floorpic); + StreamOutWord(sec->ceilingpic); + StreamOutWord(sec->lightlevel); + StreamOutWord(sec->special); + StreamOutWord(sec->tag); + StreamOutWord(sec->seqType); + } + for(i = 0, li = lines; i < numlines; i++, li++) + { + StreamOutWord(li->flags); + StreamOutByte(li->special); + StreamOutByte(li->arg1); + StreamOutByte(li->arg2); + StreamOutByte(li->arg3); + StreamOutByte(li->arg4); + StreamOutByte(li->arg5); + for(j = 0; j < 2; j++) + { + if(li->sidenum[j] == -1) + { + continue; + } + si = &sides[li->sidenum[j]]; + StreamOutWord(si->textureoffset>>FRACBITS); + StreamOutWord(si->rowoffset>>FRACBITS); + StreamOutWord(si->toptexture); + StreamOutWord(si->bottomtexture); + StreamOutWord(si->midtexture); + } + } +} + +//========================================================================== +// +// UnarchiveWorld +// +//========================================================================== + +static void UnarchiveWorld(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + AssertSegment(ASEG_WORLD); + for(i = 0, sec = sectors; i < numsectors; i++, sec++) + { + sec->floorheight = GET_WORD<ceilingheight = GET_WORD<floorpic = GET_WORD; + sec->ceilingpic = GET_WORD; + sec->lightlevel = GET_WORD; + sec->special = GET_WORD; + sec->tag = GET_WORD; + sec->seqType = GET_WORD; + sec->specialdata = 0; + sec->soundtarget = 0; + } + for(i = 0, li = lines; i < numlines; i++, li++) + { + li->flags = GET_WORD; + li->special = GET_BYTE; + li->arg1 = GET_BYTE; + li->arg2 = GET_BYTE; + li->arg3 = GET_BYTE; + li->arg4 = GET_BYTE; + li->arg5 = GET_BYTE; + for(j = 0; j < 2; j++) + { + if(li->sidenum[j] == -1) + { + continue; + } + si = &sides[li->sidenum[j]]; + si->textureoffset = GET_WORD<rowoffset = GET_WORD<toptexture = GET_WORD; + si->bottomtexture = GET_WORD; + si->midtexture = GET_WORD; + } + } +} + +//========================================================================== +// +// SetMobjArchiveNums +// +// Sets the archive numbers in all mobj structs. Also sets the MobjCount +// global. Ignores player mobjs if SavingPlayers is false. +// +//========================================================================== + +static void SetMobjArchiveNums(void) +{ + mobj_t *mobj; + thinker_t *thinker; + + MobjCount = 0; + for(thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + if(thinker->function == P_MobjThinker) + { + mobj = (mobj_t *)thinker; + if(mobj->player && !SavingPlayers) + { // Skipping player mobjs + continue; + } + mobj->archiveNum = MobjCount++; + } + } +} + +//========================================================================== +// +// ArchiveMobjs +// +//========================================================================== + +static void ArchiveMobjs(void) +{ + int count; + thinker_t *thinker; + mobj_t tempMobj; + + StreamOutLong(ASEG_MOBJS); + StreamOutLong(MobjCount); + count = 0; + for(thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + if(thinker->function != P_MobjThinker) + { // Not a mobj thinker + continue; + } + if(((mobj_t *)thinker)->player && !SavingPlayers) + { // Skipping player mobjs + continue; + } + count++; + memcpy(&tempMobj, thinker, sizeof(mobj_t)); + MangleMobj(&tempMobj); + StreamOutBuffer(&tempMobj, sizeof(mobj_t)); + } + if(count != MobjCount) + { + I_Error("ArchiveMobjs: bad mobj count"); + } +} + +//========================================================================== +// +// UnarchiveMobjs +// +//========================================================================== + +static void UnarchiveMobjs(void) +{ + int i; + mobj_t *mobj; + + AssertSegment(ASEG_MOBJS); + TargetPlayerAddrs = Z_Malloc(MAX_TARGET_PLAYERS*sizeof(int *), + PU_STATIC, NULL); + TargetPlayerCount = 0; + MobjCount = GET_LONG; + MobjList = Z_Malloc(MobjCount*sizeof(mobj_t *), PU_STATIC, NULL); + for(i = 0; i < MobjCount; i++) + { + MobjList[i] = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); + } + for(i = 0; i < MobjCount; i++) + { + mobj = MobjList[i]; + memcpy(mobj, SavePtr.b, sizeof(mobj_t)); + SavePtr.b += sizeof(mobj_t); + mobj->thinker.function = P_MobjThinker; + RestoreMobj(mobj); + P_AddThinker(&mobj->thinker); + } + P_CreateTIDList(); + P_InitCreatureCorpseQueue(true); // true = scan for corpses +} + +//========================================================================== +// +// MangleMobj +// +//========================================================================== + +static void MangleMobj(mobj_t *mobj) +{ + boolean corpse; + + corpse = mobj->flags&MF_CORPSE; + mobj->state = (state_t *)(mobj->state-states); + if(mobj->player) + { + mobj->player = (player_t *)((mobj->player-players)+1); + } + if(corpse) + { + mobj->target = (mobj_t *)MOBJ_NULL; + } + else + { + mobj->target = (mobj_t *)GetMobjNum(mobj->target); + } + switch(mobj->type) + { + // Just special1 + case MT_BISH_FX: + case MT_HOLY_FX: + case MT_DRAGON: + case MT_THRUSTFLOOR_UP: + case MT_THRUSTFLOOR_DOWN: + case MT_MINOTAUR: + case MT_SORCFX1: + case MT_MSTAFF_FX2: + if(corpse) + { + mobj->special1 = MOBJ_NULL; + } + else + { + mobj->special1 = GetMobjNum((mobj_t *)mobj->special1); + } + break; + + // Just special2 + case MT_LIGHTNING_FLOOR: + case MT_LIGHTNING_ZAP: + if(corpse) + { + mobj->special2 = MOBJ_NULL; + } + else + { + mobj->special2 = GetMobjNum((mobj_t *)mobj->special2); + } + break; + + // Both special1 and special2 + case MT_HOLY_TAIL: + case MT_LIGHTNING_CEILING: + if(corpse) + { + mobj->special1 = MOBJ_NULL; + mobj->special2 = MOBJ_NULL; + } + else + { + mobj->special1 = GetMobjNum((mobj_t *)mobj->special1); + mobj->special2 = GetMobjNum((mobj_t *)mobj->special2); + } + break; + + // Miscellaneous + case MT_KORAX: + mobj->special1 = 0; // Searching index + break; + + default: + break; + } +} + +//========================================================================== +// +// GetMobjNum +// +//========================================================================== + +static int GetMobjNum(mobj_t *mobj) +{ + if(mobj == NULL) + { + return MOBJ_NULL; + } + if(mobj->player && !SavingPlayers) + { + return MOBJ_XX_PLAYER; + } + return mobj->archiveNum; +} + +//========================================================================== +// +// RestoreMobj +// +//========================================================================== + +static void RestoreMobj(mobj_t *mobj) +{ + mobj->state = &states[(int)mobj->state]; + if(mobj->player) + { + mobj->player = &players[(int)mobj->player-1]; + mobj->player->mo = mobj; + } + P_SetThingPosition(mobj); + mobj->info = &mobjinfo[mobj->type]; + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + SetMobjPtr((int *)&mobj->target); + switch(mobj->type) + { + // Just special1 + case MT_BISH_FX: + case MT_HOLY_FX: + case MT_DRAGON: + case MT_THRUSTFLOOR_UP: + case MT_THRUSTFLOOR_DOWN: + case MT_MINOTAUR: + case MT_SORCFX1: + SetMobjPtr(&mobj->special1); + break; + + // Just special2 + case MT_LIGHTNING_FLOOR: + case MT_LIGHTNING_ZAP: + SetMobjPtr(&mobj->special2); + break; + + // Both special1 and special2 + case MT_HOLY_TAIL: + case MT_LIGHTNING_CEILING: + SetMobjPtr(&mobj->special1); + SetMobjPtr(&mobj->special2); + break; + + default: + break; + } +} + +//========================================================================== +// +// SetMobjPtr +// +//========================================================================== + +static void SetMobjPtr(int *archiveNum) +{ + if(*archiveNum == MOBJ_NULL) + { + *archiveNum = 0; + return; + } + if(*archiveNum == MOBJ_XX_PLAYER) + { + if(TargetPlayerCount == MAX_TARGET_PLAYERS) + { + I_Error("RestoreMobj: exceeded MAX_TARGET_PLAYERS"); + } + TargetPlayerAddrs[TargetPlayerCount++] = archiveNum; + *archiveNum = 0; + return; + } + *archiveNum = (int)MobjList[*archiveNum]; +} + +//========================================================================== +// +// ArchiveThinkers +// +//========================================================================== + +static void ArchiveThinkers(void) +{ + thinker_t *thinker; + thinkInfo_t *info; + byte buffer[MAX_THINKER_SIZE]; + + StreamOutLong(ASEG_THINKERS); + for(thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + for(info = ThinkerInfo; info->tClass != TC_NULL; info++) + { + if(thinker->function == info->thinkerFunc) + { + StreamOutByte(info->tClass); + memcpy(buffer, thinker, info->size); + if(info->mangleFunc) + { + info->mangleFunc(buffer); + } + StreamOutBuffer(buffer, info->size); + break; + } + } + } + // Add a termination marker + StreamOutByte(TC_NULL); +} + +//========================================================================== +// +// UnarchiveThinkers +// +//========================================================================== + +static void UnarchiveThinkers(void) +{ + int tClass; + thinker_t *thinker; + thinkInfo_t *info; + + AssertSegment(ASEG_THINKERS); + while((tClass = GET_BYTE) != TC_NULL) + { + for(info = ThinkerInfo; info->tClass != TC_NULL; info++) + { + if(tClass == info->tClass) + { + thinker = Z_Malloc(info->size, PU_LEVEL, NULL); + memcpy(thinker, SavePtr.b, info->size); + SavePtr.b += info->size; + thinker->function = info->thinkerFunc; + if(info->restoreFunc) + { + info->restoreFunc(thinker); + } + P_AddThinker(thinker); + break; + } + } + if(info->tClass == TC_NULL) + { + I_Error("UnarchiveThinkers: Unknown tClass %d in " + "savegame", tClass); + } + } +} + +//========================================================================== +// +// MangleSSThinker +// +//========================================================================== + +static void MangleSSThinker(ssthinker_t *sst) +{ + sst->sector = (sector_t *)(sst->sector-sectors); +} + +//========================================================================== +// +// RestoreSSThinker +// +//========================================================================== + +static void RestoreSSThinker(ssthinker_t *sst) +{ + sst->sector = §ors[(int)sst->sector]; + sst->sector->specialdata = sst->thinker.function; +} + +//========================================================================== +// +// RestoreSSThinkerNoSD +// +//========================================================================== + +static void RestoreSSThinkerNoSD(ssthinker_t *sst) +{ + sst->sector = §ors[(int)sst->sector]; +} + +//========================================================================== +// +// MangleScript +// +//========================================================================== + +static void MangleScript(acs_t *script) +{ + script->ip = (int *)((int)(script->ip)-(int)ActionCodeBase); + script->line = script->line ? + (line_t *)(script->line-lines) : (line_t *)-1; + script->activator = (mobj_t *)GetMobjNum(script->activator); +} + +//========================================================================== +// +// RestoreScript +// +//========================================================================== + +static void RestoreScript(acs_t *script) +{ + script->ip = (int *)(ActionCodeBase+(int)script->ip); + if((int)script->line == -1) + { + script->line = NULL; + } + else + { + script->line = &lines[(int)script->line]; + } + SetMobjPtr((int *)&script->activator); +} + +//========================================================================== +// +// RestorePlatRaise +// +//========================================================================== + +static void RestorePlatRaise(plat_t *plat) +{ + plat->sector = §ors[(int)plat->sector]; + plat->sector->specialdata = T_PlatRaise; + P_AddActivePlat(plat); +} + +//========================================================================== +// +// RestoreMoveCeiling +// +//========================================================================== + +static void RestoreMoveCeiling(ceiling_t *ceiling) +{ + ceiling->sector = §ors[(int)ceiling->sector]; + ceiling->sector->specialdata = T_MoveCeiling; + P_AddActiveCeiling(ceiling); +} + +//========================================================================== +// +// ArchiveScripts +// +//========================================================================== + +static void ArchiveScripts(void) +{ + int i; + + StreamOutLong(ASEG_SCRIPTS); + for(i = 0; i < ACScriptCount; i++) + { + StreamOutWord(ACSInfo[i].state); + StreamOutWord(ACSInfo[i].waitValue); + } + StreamOutBuffer(MapVars, sizeof(MapVars)); +} + +//========================================================================== +// +// UnarchiveScripts +// +//========================================================================== + +static void UnarchiveScripts(void) +{ + int i; + + AssertSegment(ASEG_SCRIPTS); + for(i = 0; i < ACScriptCount; i++) + { + ACSInfo[i].state = GET_WORD; + ACSInfo[i].waitValue = GET_WORD; + } + memcpy(MapVars, SavePtr.b, sizeof(MapVars)); + SavePtr.b += sizeof(MapVars); +} + +//========================================================================== +// +// ArchiveMisc +// +//========================================================================== + +static void ArchiveMisc(void) +{ + int ix; + + StreamOutLong(ASEG_MISC); + for (ix=0; ixnext; + if(thinker->function == P_MobjThinker) + { + P_RemoveMobj((mobj_t *)thinker); + } + else + { + Z_Free(thinker); + } + thinker = nextThinker; + } + P_InitThinkers(); +} + +//========================================================================== +// +// ArchiveSounds +// +//========================================================================== + +static void ArchiveSounds(void) +{ + seqnode_t *node; + sector_t *sec; + int difference; + int i; + + StreamOutLong(ASEG_SOUNDS); + + // Save the sound sequences + StreamOutLong(ActiveSequences); + for(node = SequenceListHead; node; node = node->next) + { + StreamOutLong(node->sequence); + StreamOutLong(node->delayTics); + StreamOutLong(node->volume); + StreamOutLong(SN_GetSequenceOffset(node->sequence, + node->sequencePtr)); + StreamOutLong(node->currentSoundID); + for(i = 0; i < po_NumPolyobjs; i++) + { + if(node->mobj == (mobj_t *)&polyobjs[i].startSpot) + { + break; + } + } + if(i == po_NumPolyobjs) + { // Sound is attached to a sector, not a polyobj + sec = R_PointInSubsector(node->mobj->x, node->mobj->y)->sector; + difference = (int)((byte *)sec + -(byte *)§ors[0])/sizeof(sector_t); + StreamOutLong(0); // 0 -- sector sound origin + } + else + { + StreamOutLong(1); // 1 -- polyobj sound origin + difference = i; + } + StreamOutLong(difference); + } +} + +//========================================================================== +// +// UnarchiveSounds +// +//========================================================================== + +static void UnarchiveSounds(void) +{ + int i; + int numSequences; + int sequence; + int delayTics; + int volume; + int seqOffset; + int soundID; + int polySnd; + int secNum; + mobj_t *sndMobj; + + AssertSegment(ASEG_SOUNDS); + + // Reload and restart all sound sequences + numSequences = GET_LONG; + i = 0; + while(i < numSequences) + { + sequence = GET_LONG; + delayTics = GET_LONG; + volume = GET_LONG; + seqOffset = GET_LONG; + + soundID = GET_LONG; + polySnd = GET_LONG; + secNum = GET_LONG; + if(!polySnd) + { + sndMobj = (mobj_t *)§ors[secNum].soundorg; + } + else + { + sndMobj = (mobj_t *)&polyobjs[secNum].startSpot; + } + SN_StartSequence(sndMobj, sequence); + SN_ChangeNodeData(i, seqOffset, delayTics, volume, soundID); + i++; + } +} + +//========================================================================== +// +// ArchivePolyobjs +// +//========================================================================== + +static void ArchivePolyobjs(void) +{ + int i; + + StreamOutLong(ASEG_POLYOBJS); + StreamOutLong(po_NumPolyobjs); + for(i = 0; i < po_NumPolyobjs; i++) + { + StreamOutLong(polyobjs[i].tag); + StreamOutLong(polyobjs[i].angle); + StreamOutLong(polyobjs[i].startSpot.x); + StreamOutLong(polyobjs[i].startSpot.y); + } +} + +//========================================================================== +// +// UnarchivePolyobjs +// +//========================================================================== + +static void UnarchivePolyobjs(void) +{ + int i; + fixed_t deltaX; + fixed_t deltaY; + + AssertSegment(ASEG_POLYOBJS); + if(GET_LONG != po_NumPolyobjs) + { + I_Error("UnarchivePolyobjs: Bad polyobj count"); + } + for(i = 0; i < po_NumPolyobjs; i++) + { + if(GET_LONG != polyobjs[i].tag) + { + I_Error("UnarchivePolyobjs: Invalid polyobj tag"); + } + PO_RotatePolyobj(polyobjs[i].tag, (angle_t)GET_LONG); + deltaX = GET_LONG-polyobjs[i].startSpot.x; + deltaY = GET_LONG-polyobjs[i].startSpot.y; + PO_MovePolyobj(polyobjs[i].tag, deltaX, deltaY); + } +} + +//========================================================================== +// +// AssertSegment +// +//========================================================================== + +static void AssertSegment(gameArchiveSegment_t segType) +{ + if(GET_LONG != segType) + { + I_Error("Corrupt save game: Segment [%d] failed alignment check", + segType); + } +} + +//========================================================================== +// +// ClearSaveSlot +// +// Deletes all save game files associated with a slot number. +// +//========================================================================== + +static void ClearSaveSlot(int slot) +{ + int i; + char fileName[100]; + + for(i = 0; i < MAX_MAPS; i++) + { + sprintf(fileName, "%shex%d%02d.hxs", SavePath, slot, i); + remove(fileName); + } + sprintf(fileName, "%shex%d.hxs", SavePath, slot); + remove(fileName); +} + +//========================================================================== +// +// CopySaveSlot +// +// Copies all the save game files from one slot to another. +// +//========================================================================== + +static void CopySaveSlot(int sourceSlot, int destSlot) +{ + int i; + char sourceName[100]; + char destName[100]; + + for(i = 0; i < MAX_MAPS; i++) + { + sprintf(sourceName, "%shex%d%02d.hxs", SavePath, sourceSlot, i); + if(ExistingFile(sourceName)) + { + sprintf(destName, "%shex%d%02d.hxs", SavePath, destSlot, i); + CopyFile(sourceName, destName); + } + } + sprintf(sourceName, "%shex%d.hxs", SavePath, sourceSlot); + if(ExistingFile(sourceName)) + { + sprintf(destName, "%shex%d.hxs", SavePath, destSlot); + CopyFile(sourceName, destName); + } +} + +//========================================================================== +// +// CopyFile +// +//========================================================================== + +static void CopyFile(char *sourceName, char *destName) +{ + int length; + byte *buffer; + + length = M_ReadFile(sourceName, &buffer); + M_WriteFile(destName, buffer, length); + Z_Free(buffer); +} + +//========================================================================== +// +// ExistingFile +// +//========================================================================== + +static boolean ExistingFile(char *name) +{ + FILE *fp; + + if((fp = fopen(name, "rb")) != NULL) + { + fclose(fp); + return true; + } + else + { + return false; + } +} + +//========================================================================== +// +// OpenStreamOut +// +//========================================================================== + +static void OpenStreamOut(char *fileName) +{ + SavingFP = fopen(fileName, "wb"); +} + +//========================================================================== +// +// CloseStreamOut +// +//========================================================================== + +static void CloseStreamOut(void) +{ + if(SavingFP) + { + fclose(SavingFP); + } +} + +//========================================================================== +// +// StreamOutBuffer +// +//========================================================================== + +static void StreamOutBuffer(void *buffer, int size) +{ + fwrite(buffer, size, 1, SavingFP); +} + +//========================================================================== +// +// StreamOutByte +// +//========================================================================== + +static void StreamOutByte(byte val) +{ + fwrite(&val, sizeof(byte), 1, SavingFP); +} + +//========================================================================== +// +// StreamOutWord +// +//========================================================================== + +static void StreamOutWord(unsigned short val) +{ + fwrite(&val, sizeof(unsigned short), 1, SavingFP); +} + +//========================================================================== +// +// StreamOutLong +// +//========================================================================== + +static void StreamOutLong(unsigned int val) +{ + fwrite(&val, sizeof(int), 1, SavingFP); +} diff --git a/src/hexen/tables.c b/src/hexen/tables.c new file mode 100644 index 00000000..7ee69fc5 --- /dev/null +++ b/src/hexen/tables.c @@ -0,0 +1,2074 @@ + +//************************************************************************** +//** +//** tables.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: tables.c,v $ +//** $Revision: 1.1 $ +//** $Date: 95/05/11 00:24:29 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" + +int finetangent[4096] = { +-170910304,-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683, +-10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368, +-5178251,-4882318,-4618375,-4381502,-4167737,-3973855,-3797206,-3635590, +-3487165,-3350381,-3223918,-3106651,-2997613,-2895966,-2800983,-2712030, +-2628549,-2550052,-2476104,-2406322,-2340362,-2277919,-2218719,-2162516, +-2109087,-2058233,-2009771,-1963536,-1919378,-1877161,-1836758,-1798063, +-1760956,-1725348,-1691149,-1658278,-1626658,-1596220,-1566898,-1538632, +-1511367,-1485049,-1459630,-1435065,-1411312,-1388330,-1366084,-1344537, +-1323658,-1303416,-1283783,-1264730,-1246234,-1228269,-1210813,-1193846, +-1177345,-1161294,-1145673,-1130465,-1115654,-1101225,-1087164,-1073455, +-1060087,-1047046,-1034322,-1021901,-1009774,-997931,-986361,-975054, +-964003,-953199,-942633,-932298,-922186,-912289,-902602,-893117, +-883829,-874730,-865817,-857081,-848520,-840127,-831898,-823827, +-815910,-808143,-800521,-793041,-785699,-778490,-771411,-764460, +-757631,-750922,-744331,-737853,-731486,-725227,-719074,-713023, +-707072,-701219,-695462,-689797,-684223,-678737,-673338,-668024, +-662792,-657640,-652568,-647572,-642651,-637803,-633028,-628323, +-623686,-619117,-614613,-610174,-605798,-601483,-597229,-593033, +-588896,-584815,-580789,-576818,-572901,-569035,-565221,-561456, +-557741,-554074,-550455,-546881,-543354,-539870,-536431,-533034, +-529680,-526366,-523094,-519861,-516667,-513512,-510394,-507313, +-504269,-501261,-498287,-495348,-492443,-489571,-486732,-483925, +-481150,-478406,-475692,-473009,-470355,-467730,-465133,-462565, +-460024,-457511,-455024,-452564,-450129,-447720,-445337,-442978, +-440643,-438332,-436045,-433781,-431540,-429321,-427125,-424951, +-422798,-420666,-418555,-416465,-414395,-412344,-410314,-408303, +-406311,-404338,-402384,-400448,-398530,-396630,-394747,-392882, +-391034,-389202,-387387,-385589,-383807,-382040,-380290,-378555, +-376835,-375130,-373440,-371765,-370105,-368459,-366826,-365208, +-363604,-362013,-360436,-358872,-357321,-355783,-354257,-352744, +-351244,-349756,-348280,-346816,-345364,-343924,-342495,-341078, +-339671,-338276,-336892,-335519,-334157,-332805,-331464,-330133, +-328812,-327502,-326201,-324910,-323629,-322358,-321097,-319844, +-318601,-317368,-316143,-314928,-313721,-312524,-311335,-310154, +-308983,-307819,-306664,-305517,-304379,-303248,-302126,-301011, +-299904,-298805,-297714,-296630,-295554,-294485,-293423,-292369, +-291322,-290282,-289249,-288223,-287204,-286192,-285186,-284188, +-283195,-282210,-281231,-280258,-279292,-278332,-277378,-276430, +-275489,-274553,-273624,-272700,-271782,-270871,-269965,-269064, +-268169,-267280,-266397,-265519,-264646,-263779,-262917,-262060, +-261209,-260363,-259522,-258686,-257855,-257029,-256208,-255392, +-254581,-253774,-252973,-252176,-251384,-250596,-249813,-249035, +-248261,-247492,-246727,-245966,-245210,-244458,-243711,-242967, +-242228,-241493,-240763,-240036,-239314,-238595,-237881,-237170, +-236463,-235761,-235062,-234367,-233676,-232988,-232304,-231624, +-230948,-230275,-229606,-228941,-228279,-227621,-226966,-226314, +-225666,-225022,-224381,-223743,-223108,-222477,-221849,-221225, +-220603,-219985,-219370,-218758,-218149,-217544,-216941,-216341, +-215745,-215151,-214561,-213973,-213389,-212807,-212228,-211652, +-211079,-210509,-209941,-209376,-208815,-208255,-207699,-207145, +-206594,-206045,-205500,-204956,-204416,-203878,-203342,-202809, +-202279,-201751,-201226,-200703,-200182,-199664,-199149,-198636, +-198125,-197616,-197110,-196606,-196105,-195606,-195109,-194614, +-194122,-193631,-193143,-192658,-192174,-191693,-191213,-190736, +-190261,-189789,-189318,-188849,-188382,-187918,-187455,-186995, +-186536,-186080,-185625,-185173,-184722,-184274,-183827,-183382, +-182939,-182498,-182059,-181622,-181186,-180753,-180321,-179891, +-179463,-179037,-178612,-178190,-177769,-177349,-176932,-176516, +-176102,-175690,-175279,-174870,-174463,-174057,-173653,-173251, +-172850,-172451,-172053,-171657,-171263,-170870,-170479,-170089, +-169701,-169315,-168930,-168546,-168164,-167784,-167405,-167027, +-166651,-166277,-165904,-165532,-165162,-164793,-164426,-164060, +-163695,-163332,-162970,-162610,-162251,-161893,-161537,-161182, +-160828,-160476,-160125,-159775,-159427,-159079,-158734,-158389, +-158046,-157704,-157363,-157024,-156686,-156349,-156013,-155678, +-155345,-155013,-154682,-154352,-154024,-153697,-153370,-153045, +-152722,-152399,-152077,-151757,-151438,-151120,-150803,-150487, +-150172,-149859,-149546,-149235,-148924,-148615,-148307,-148000, +-147693,-147388,-147084,-146782,-146480,-146179,-145879,-145580, +-145282,-144986,-144690,-144395,-144101,-143808,-143517,-143226, +-142936,-142647,-142359,-142072,-141786,-141501,-141217,-140934, +-140651,-140370,-140090,-139810,-139532,-139254,-138977,-138701, +-138426,-138152,-137879,-137607,-137335,-137065,-136795,-136526, +-136258,-135991,-135725,-135459,-135195,-134931,-134668,-134406, +-134145,-133884,-133625,-133366,-133108,-132851,-132594,-132339, +-132084,-131830,-131576,-131324,-131072,-130821,-130571,-130322, +-130073,-129825,-129578,-129332,-129086,-128841,-128597,-128353, +-128111,-127869,-127627,-127387,-127147,-126908,-126669,-126432, +-126195,-125959,-125723,-125488,-125254,-125020,-124787,-124555, +-124324,-124093,-123863,-123633,-123404,-123176,-122949,-122722, +-122496,-122270,-122045,-121821,-121597,-121374,-121152,-120930, +-120709,-120489,-120269,-120050,-119831,-119613,-119396,-119179, +-118963,-118747,-118532,-118318,-118104,-117891,-117678,-117466, +-117254,-117044,-116833,-116623,-116414,-116206,-115998,-115790, +-115583,-115377,-115171,-114966,-114761,-114557,-114354,-114151, +-113948,-113746,-113545,-113344,-113143,-112944,-112744,-112546, +-112347,-112150,-111952,-111756,-111560,-111364,-111169,-110974, +-110780,-110586,-110393,-110200,-110008,-109817,-109626,-109435, +-109245,-109055,-108866,-108677,-108489,-108301,-108114,-107927, +-107741,-107555,-107369,-107184,-107000,-106816,-106632,-106449, +-106266,-106084,-105902,-105721,-105540,-105360,-105180,-105000, +-104821,-104643,-104465,-104287,-104109,-103933,-103756,-103580, +-103404,-103229,-103054,-102880,-102706,-102533,-102360,-102187, +-102015,-101843,-101671,-101500,-101330,-101159,-100990,-100820, +-100651,-100482,-100314,-100146,-99979,-99812,-99645,-99479, +-99313,-99148,-98982,-98818,-98653,-98489,-98326,-98163, +-98000,-97837,-97675,-97513,-97352,-97191,-97030,-96870, +-96710,-96551,-96391,-96233,-96074,-95916,-95758,-95601, +-95444,-95287,-95131,-94975,-94819,-94664,-94509,-94354, +-94200,-94046,-93892,-93739,-93586,-93434,-93281,-93129, +-92978,-92826,-92675,-92525,-92375,-92225,-92075,-91926, +-91777,-91628,-91480,-91332,-91184,-91036,-90889,-90742, +-90596,-90450,-90304,-90158,-90013,-89868,-89724,-89579, +-89435,-89292,-89148,-89005,-88862,-88720,-88577,-88435, +-88294,-88152,-88011,-87871,-87730,-87590,-87450,-87310, +-87171,-87032,-86893,-86755,-86616,-86479,-86341,-86204, +-86066,-85930,-85793,-85657,-85521,-85385,-85250,-85114, +-84980,-84845,-84710,-84576,-84443,-84309,-84176,-84043, +-83910,-83777,-83645,-83513,-83381,-83250,-83118,-82987, +-82857,-82726,-82596,-82466,-82336,-82207,-82078,-81949, +-81820,-81691,-81563,-81435,-81307,-81180,-81053,-80925, +-80799,-80672,-80546,-80420,-80294,-80168,-80043,-79918, +-79793,-79668,-79544,-79420,-79296,-79172,-79048,-78925, +-78802,-78679,-78557,-78434,-78312,-78190,-78068,-77947, +-77826,-77705,-77584,-77463,-77343,-77223,-77103,-76983, +-76864,-76744,-76625,-76506,-76388,-76269,-76151,-76033, +-75915,-75797,-75680,-75563,-75446,-75329,-75213,-75096, +-74980,-74864,-74748,-74633,-74517,-74402,-74287,-74172, +-74058,-73944,-73829,-73715,-73602,-73488,-73375,-73262, +-73149,-73036,-72923,-72811,-72699,-72587,-72475,-72363, +-72252,-72140,-72029,-71918,-71808,-71697,-71587,-71477, +-71367,-71257,-71147,-71038,-70929,-70820,-70711,-70602, +-70494,-70385,-70277,-70169,-70061,-69954,-69846,-69739, +-69632,-69525,-69418,-69312,-69205,-69099,-68993,-68887, +-68781,-68676,-68570,-68465,-68360,-68255,-68151,-68046, +-67942,-67837,-67733,-67629,-67526,-67422,-67319,-67216, +-67113,-67010,-66907,-66804,-66702,-66600,-66498,-66396, +-66294,-66192,-66091,-65989,-65888,-65787,-65686,-65586, +-65485,-65385,-65285,-65185,-65085,-64985,-64885,-64786, +-64687,-64587,-64488,-64389,-64291,-64192,-64094,-63996, +-63897,-63799,-63702,-63604,-63506,-63409,-63312,-63215, +-63118,-63021,-62924,-62828,-62731,-62635,-62539,-62443, +-62347,-62251,-62156,-62060,-61965,-61870,-61775,-61680, +-61585,-61491,-61396,-61302,-61208,-61114,-61020,-60926, +-60833,-60739,-60646,-60552,-60459,-60366,-60273,-60181, +-60088,-59996,-59903,-59811,-59719,-59627,-59535,-59444, +-59352,-59261,-59169,-59078,-58987,-58896,-58805,-58715, +-58624,-58534,-58443,-58353,-58263,-58173,-58083,-57994, +-57904,-57815,-57725,-57636,-57547,-57458,-57369,-57281, +-57192,-57104,-57015,-56927,-56839,-56751,-56663,-56575, +-56487,-56400,-56312,-56225,-56138,-56051,-55964,-55877, +-55790,-55704,-55617,-55531,-55444,-55358,-55272,-55186, +-55100,-55015,-54929,-54843,-54758,-54673,-54587,-54502, +-54417,-54333,-54248,-54163,-54079,-53994,-53910,-53826, +-53741,-53657,-53574,-53490,-53406,-53322,-53239,-53156, +-53072,-52989,-52906,-52823,-52740,-52657,-52575,-52492, +-52410,-52327,-52245,-52163,-52081,-51999,-51917,-51835, +-51754,-51672,-51591,-51509,-51428,-51347,-51266,-51185, +-51104,-51023,-50942,-50862,-50781,-50701,-50621,-50540, +-50460,-50380,-50300,-50221,-50141,-50061,-49982,-49902, +-49823,-49744,-49664,-49585,-49506,-49427,-49349,-49270, +-49191,-49113,-49034,-48956,-48878,-48799,-48721,-48643, +-48565,-48488,-48410,-48332,-48255,-48177,-48100,-48022, +-47945,-47868,-47791,-47714,-47637,-47560,-47484,-47407, +-47331,-47254,-47178,-47102,-47025,-46949,-46873,-46797, +-46721,-46646,-46570,-46494,-46419,-46343,-46268,-46193, +-46118,-46042,-45967,-45892,-45818,-45743,-45668,-45593, +-45519,-45444,-45370,-45296,-45221,-45147,-45073,-44999, +-44925,-44851,-44778,-44704,-44630,-44557,-44483,-44410, +-44337,-44263,-44190,-44117,-44044,-43971,-43898,-43826, +-43753,-43680,-43608,-43535,-43463,-43390,-43318,-43246, +-43174,-43102,-43030,-42958,-42886,-42814,-42743,-42671, +-42600,-42528,-42457,-42385,-42314,-42243,-42172,-42101, +-42030,-41959,-41888,-41817,-41747,-41676,-41605,-41535, +-41465,-41394,-41324,-41254,-41184,-41113,-41043,-40973, +-40904,-40834,-40764,-40694,-40625,-40555,-40486,-40416, +-40347,-40278,-40208,-40139,-40070,-40001,-39932,-39863, +-39794,-39726,-39657,-39588,-39520,-39451,-39383,-39314, +-39246,-39178,-39110,-39042,-38973,-38905,-38837,-38770, +-38702,-38634,-38566,-38499,-38431,-38364,-38296,-38229, +-38161,-38094,-38027,-37960,-37893,-37826,-37759,-37692, +-37625,-37558,-37491,-37425,-37358,-37291,-37225,-37158, +-37092,-37026,-36959,-36893,-36827,-36761,-36695,-36629, +-36563,-36497,-36431,-36365,-36300,-36234,-36168,-36103, +-36037,-35972,-35907,-35841,-35776,-35711,-35646,-35580, +-35515,-35450,-35385,-35321,-35256,-35191,-35126,-35062, +-34997,-34932,-34868,-34803,-34739,-34675,-34610,-34546, +-34482,-34418,-34354,-34289,-34225,-34162,-34098,-34034, +-33970,-33906,-33843,-33779,-33715,-33652,-33588,-33525, +-33461,-33398,-33335,-33272,-33208,-33145,-33082,-33019, +-32956,-32893,-32830,-32767,-32705,-32642,-32579,-32516, +-32454,-32391,-32329,-32266,-32204,-32141,-32079,-32017, +-31955,-31892,-31830,-31768,-31706,-31644,-31582,-31520, +-31458,-31396,-31335,-31273,-31211,-31150,-31088,-31026, +-30965,-30904,-30842,-30781,-30719,-30658,-30597,-30536, +-30474,-30413,-30352,-30291,-30230,-30169,-30108,-30048, +-29987,-29926,-29865,-29805,-29744,-29683,-29623,-29562, +-29502,-29441,-29381,-29321,-29260,-29200,-29140,-29080, +-29020,-28959,-28899,-28839,-28779,-28719,-28660,-28600, +-28540,-28480,-28420,-28361,-28301,-28241,-28182,-28122, +-28063,-28003,-27944,-27884,-27825,-27766,-27707,-27647, +-27588,-27529,-27470,-27411,-27352,-27293,-27234,-27175, +-27116,-27057,-26998,-26940,-26881,-26822,-26763,-26705, +-26646,-26588,-26529,-26471,-26412,-26354,-26295,-26237, +-26179,-26120,-26062,-26004,-25946,-25888,-25830,-25772, +-25714,-25656,-25598,-25540,-25482,-25424,-25366,-25308, +-25251,-25193,-25135,-25078,-25020,-24962,-24905,-24847, +-24790,-24732,-24675,-24618,-24560,-24503,-24446,-24389, +-24331,-24274,-24217,-24160,-24103,-24046,-23989,-23932, +-23875,-23818,-23761,-23704,-23647,-23591,-23534,-23477, +-23420,-23364,-23307,-23250,-23194,-23137,-23081,-23024, +-22968,-22911,-22855,-22799,-22742,-22686,-22630,-22573, +-22517,-22461,-22405,-22349,-22293,-22237,-22181,-22125, +-22069,-22013,-21957,-21901,-21845,-21789,-21733,-21678, +-21622,-21566,-21510,-21455,-21399,-21343,-21288,-21232, +-21177,-21121,-21066,-21010,-20955,-20900,-20844,-20789, +-20734,-20678,-20623,-20568,-20513,-20457,-20402,-20347, +-20292,-20237,-20182,-20127,-20072,-20017,-19962,-19907, +-19852,-19797,-19742,-19688,-19633,-19578,-19523,-19469, +-19414,-19359,-19305,-19250,-19195,-19141,-19086,-19032, +-18977,-18923,-18868,-18814,-18760,-18705,-18651,-18597, +-18542,-18488,-18434,-18380,-18325,-18271,-18217,-18163, +-18109,-18055,-18001,-17946,-17892,-17838,-17784,-17731, +-17677,-17623,-17569,-17515,-17461,-17407,-17353,-17300, +-17246,-17192,-17138,-17085,-17031,-16977,-16924,-16870, +-16817,-16763,-16710,-16656,-16603,-16549,-16496,-16442, +-16389,-16335,-16282,-16229,-16175,-16122,-16069,-16015, +-15962,-15909,-15856,-15802,-15749,-15696,-15643,-15590, +-15537,-15484,-15431,-15378,-15325,-15272,-15219,-15166, +-15113,-15060,-15007,-14954,-14901,-14848,-14795,-14743, +-14690,-14637,-14584,-14531,-14479,-14426,-14373,-14321, +-14268,-14215,-14163,-14110,-14057,-14005,-13952,-13900, +-13847,-13795,-13742,-13690,-13637,-13585,-13533,-13480, +-13428,-13375,-13323,-13271,-13218,-13166,-13114,-13062, +-13009,-12957,-12905,-12853,-12800,-12748,-12696,-12644, +-12592,-12540,-12488,-12436,-12383,-12331,-12279,-12227, +-12175,-12123,-12071,-12019,-11967,-11916,-11864,-11812, +-11760,-11708,-11656,-11604,-11552,-11501,-11449,-11397, +-11345,-11293,-11242,-11190,-11138,-11086,-11035,-10983, +-10931,-10880,-10828,-10777,-10725,-10673,-10622,-10570, +-10519,-10467,-10415,-10364,-10312,-10261,-10209,-10158, +-10106,-10055,-10004,-9952,-9901,-9849,-9798,-9747, +-9695,-9644,-9592,-9541,-9490,-9438,-9387,-9336, +-9285,-9233,-9182,-9131,-9080,-9028,-8977,-8926, +-8875,-8824,-8772,-8721,-8670,-8619,-8568,-8517, +-8466,-8414,-8363,-8312,-8261,-8210,-8159,-8108, +-8057,-8006,-7955,-7904,-7853,-7802,-7751,-7700, +-7649,-7598,-7547,-7496,-7445,-7395,-7344,-7293, +-7242,-7191,-7140,-7089,-7038,-6988,-6937,-6886, +-6835,-6784,-6733,-6683,-6632,-6581,-6530,-6480, +-6429,-6378,-6327,-6277,-6226,-6175,-6124,-6074, +-6023,-5972,-5922,-5871,-5820,-5770,-5719,-5668, +-5618,-5567,-5517,-5466,-5415,-5365,-5314,-5264, +-5213,-5162,-5112,-5061,-5011,-4960,-4910,-4859, +-4808,-4758,-4707,-4657,-4606,-4556,-4505,-4455, +-4404,-4354,-4303,-4253,-4202,-4152,-4101,-4051, +-4001,-3950,-3900,-3849,-3799,-3748,-3698,-3648, +-3597,-3547,-3496,-3446,-3395,-3345,-3295,-3244, +-3194,-3144,-3093,-3043,-2992,-2942,-2892,-2841, +-2791,-2741,-2690,-2640,-2590,-2539,-2489,-2439, +-2388,-2338,-2288,-2237,-2187,-2137,-2086,-2036, +-1986,-1935,-1885,-1835,-1784,-1734,-1684,-1633, +-1583,-1533,-1483,-1432,-1382,-1332,-1281,-1231, +-1181,-1131,-1080,-1030,-980,-929,-879,-829, +-779,-728,-678,-628,-578,-527,-477,-427, +-376,-326,-276,-226,-175,-125,-75,-25, +25,75,125,175,226,276,326,376, +427,477,527,578,628,678,728,779, +829,879,929,980,1030,1080,1131,1181, +1231,1281,1332,1382,1432,1483,1533,1583, +1633,1684,1734,1784,1835,1885,1935,1986, +2036,2086,2137,2187,2237,2288,2338,2388, +2439,2489,2539,2590,2640,2690,2741,2791, +2841,2892,2942,2992,3043,3093,3144,3194, +3244,3295,3345,3395,3446,3496,3547,3597, +3648,3698,3748,3799,3849,3900,3950,4001, +4051,4101,4152,4202,4253,4303,4354,4404, +4455,4505,4556,4606,4657,4707,4758,4808, +4859,4910,4960,5011,5061,5112,5162,5213, +5264,5314,5365,5415,5466,5517,5567,5618, +5668,5719,5770,5820,5871,5922,5972,6023, +6074,6124,6175,6226,6277,6327,6378,6429, +6480,6530,6581,6632,6683,6733,6784,6835, +6886,6937,6988,7038,7089,7140,7191,7242, +7293,7344,7395,7445,7496,7547,7598,7649, +7700,7751,7802,7853,7904,7955,8006,8057, +8108,8159,8210,8261,8312,8363,8414,8466, +8517,8568,8619,8670,8721,8772,8824,8875, +8926,8977,9028,9080,9131,9182,9233,9285, +9336,9387,9438,9490,9541,9592,9644,9695, +9747,9798,9849,9901,9952,10004,10055,10106, +10158,10209,10261,10312,10364,10415,10467,10519, +10570,10622,10673,10725,10777,10828,10880,10931, +10983,11035,11086,11138,11190,11242,11293,11345, +11397,11449,11501,11552,11604,11656,11708,11760, +11812,11864,11916,11967,12019,12071,12123,12175, +12227,12279,12331,12383,12436,12488,12540,12592, +12644,12696,12748,12800,12853,12905,12957,13009, +13062,13114,13166,13218,13271,13323,13375,13428, +13480,13533,13585,13637,13690,13742,13795,13847, +13900,13952,14005,14057,14110,14163,14215,14268, +14321,14373,14426,14479,14531,14584,14637,14690, +14743,14795,14848,14901,14954,15007,15060,15113, +15166,15219,15272,15325,15378,15431,15484,15537, +15590,15643,15696,15749,15802,15856,15909,15962, +16015,16069,16122,16175,16229,16282,16335,16389, +16442,16496,16549,16603,16656,16710,16763,16817, +16870,16924,16977,17031,17085,17138,17192,17246, +17300,17353,17407,17461,17515,17569,17623,17677, +17731,17784,17838,17892,17946,18001,18055,18109, +18163,18217,18271,18325,18380,18434,18488,18542, +18597,18651,18705,18760,18814,18868,18923,18977, +19032,19086,19141,19195,19250,19305,19359,19414, +19469,19523,19578,19633,19688,19742,19797,19852, +19907,19962,20017,20072,20127,20182,20237,20292, +20347,20402,20457,20513,20568,20623,20678,20734, +20789,20844,20900,20955,21010,21066,21121,21177, +21232,21288,21343,21399,21455,21510,21566,21622, +21678,21733,21789,21845,21901,21957,22013,22069, +22125,22181,22237,22293,22349,22405,22461,22517, +22573,22630,22686,22742,22799,22855,22911,22968, +23024,23081,23137,23194,23250,23307,23364,23420, +23477,23534,23591,23647,23704,23761,23818,23875, +23932,23989,24046,24103,24160,24217,24274,24331, +24389,24446,24503,24560,24618,24675,24732,24790, +24847,24905,24962,25020,25078,25135,25193,25251, +25308,25366,25424,25482,25540,25598,25656,25714, +25772,25830,25888,25946,26004,26062,26120,26179, +26237,26295,26354,26412,26471,26529,26588,26646, +26705,26763,26822,26881,26940,26998,27057,27116, +27175,27234,27293,27352,27411,27470,27529,27588, +27647,27707,27766,27825,27884,27944,28003,28063, +28122,28182,28241,28301,28361,28420,28480,28540, +28600,28660,28719,28779,28839,28899,28959,29020, +29080,29140,29200,29260,29321,29381,29441,29502, +29562,29623,29683,29744,29805,29865,29926,29987, +30048,30108,30169,30230,30291,30352,30413,30474, +30536,30597,30658,30719,30781,30842,30904,30965, +31026,31088,31150,31211,31273,31335,31396,31458, +31520,31582,31644,31706,31768,31830,31892,31955, +32017,32079,32141,32204,32266,32329,32391,32454, +32516,32579,32642,32705,32767,32830,32893,32956, +33019,33082,33145,33208,33272,33335,33398,33461, +33525,33588,33652,33715,33779,33843,33906,33970, +34034,34098,34162,34225,34289,34354,34418,34482, +34546,34610,34675,34739,34803,34868,34932,34997, +35062,35126,35191,35256,35321,35385,35450,35515, +35580,35646,35711,35776,35841,35907,35972,36037, +36103,36168,36234,36300,36365,36431,36497,36563, +36629,36695,36761,36827,36893,36959,37026,37092, +37158,37225,37291,37358,37425,37491,37558,37625, +37692,37759,37826,37893,37960,38027,38094,38161, +38229,38296,38364,38431,38499,38566,38634,38702, +38770,38837,38905,38973,39042,39110,39178,39246, +39314,39383,39451,39520,39588,39657,39726,39794, +39863,39932,40001,40070,40139,40208,40278,40347, +40416,40486,40555,40625,40694,40764,40834,40904, +40973,41043,41113,41184,41254,41324,41394,41465, +41535,41605,41676,41747,41817,41888,41959,42030, +42101,42172,42243,42314,42385,42457,42528,42600, +42671,42743,42814,42886,42958,43030,43102,43174, +43246,43318,43390,43463,43535,43608,43680,43753, +43826,43898,43971,44044,44117,44190,44263,44337, +44410,44483,44557,44630,44704,44778,44851,44925, +44999,45073,45147,45221,45296,45370,45444,45519, +45593,45668,45743,45818,45892,45967,46042,46118, +46193,46268,46343,46419,46494,46570,46646,46721, +46797,46873,46949,47025,47102,47178,47254,47331, +47407,47484,47560,47637,47714,47791,47868,47945, +48022,48100,48177,48255,48332,48410,48488,48565, +48643,48721,48799,48878,48956,49034,49113,49191, +49270,49349,49427,49506,49585,49664,49744,49823, +49902,49982,50061,50141,50221,50300,50380,50460, +50540,50621,50701,50781,50862,50942,51023,51104, +51185,51266,51347,51428,51509,51591,51672,51754, +51835,51917,51999,52081,52163,52245,52327,52410, +52492,52575,52657,52740,52823,52906,52989,53072, +53156,53239,53322,53406,53490,53574,53657,53741, +53826,53910,53994,54079,54163,54248,54333,54417, +54502,54587,54673,54758,54843,54929,55015,55100, +55186,55272,55358,55444,55531,55617,55704,55790, +55877,55964,56051,56138,56225,56312,56400,56487, +56575,56663,56751,56839,56927,57015,57104,57192, +57281,57369,57458,57547,57636,57725,57815,57904, +57994,58083,58173,58263,58353,58443,58534,58624, +58715,58805,58896,58987,59078,59169,59261,59352, +59444,59535,59627,59719,59811,59903,59996,60088, +60181,60273,60366,60459,60552,60646,60739,60833, +60926,61020,61114,61208,61302,61396,61491,61585, +61680,61775,61870,61965,62060,62156,62251,62347, +62443,62539,62635,62731,62828,62924,63021,63118, +63215,63312,63409,63506,63604,63702,63799,63897, +63996,64094,64192,64291,64389,64488,64587,64687, +64786,64885,64985,65085,65185,65285,65385,65485, +65586,65686,65787,65888,65989,66091,66192,66294, +66396,66498,66600,66702,66804,66907,67010,67113, +67216,67319,67422,67526,67629,67733,67837,67942, +68046,68151,68255,68360,68465,68570,68676,68781, +68887,68993,69099,69205,69312,69418,69525,69632, +69739,69846,69954,70061,70169,70277,70385,70494, +70602,70711,70820,70929,71038,71147,71257,71367, +71477,71587,71697,71808,71918,72029,72140,72252, +72363,72475,72587,72699,72811,72923,73036,73149, +73262,73375,73488,73602,73715,73829,73944,74058, +74172,74287,74402,74517,74633,74748,74864,74980, +75096,75213,75329,75446,75563,75680,75797,75915, +76033,76151,76269,76388,76506,76625,76744,76864, +76983,77103,77223,77343,77463,77584,77705,77826, +77947,78068,78190,78312,78434,78557,78679,78802, +78925,79048,79172,79296,79420,79544,79668,79793, +79918,80043,80168,80294,80420,80546,80672,80799, +80925,81053,81180,81307,81435,81563,81691,81820, +81949,82078,82207,82336,82466,82596,82726,82857, +82987,83118,83250,83381,83513,83645,83777,83910, +84043,84176,84309,84443,84576,84710,84845,84980, +85114,85250,85385,85521,85657,85793,85930,86066, +86204,86341,86479,86616,86755,86893,87032,87171, +87310,87450,87590,87730,87871,88011,88152,88294, +88435,88577,88720,88862,89005,89148,89292,89435, +89579,89724,89868,90013,90158,90304,90450,90596, +90742,90889,91036,91184,91332,91480,91628,91777, +91926,92075,92225,92375,92525,92675,92826,92978, +93129,93281,93434,93586,93739,93892,94046,94200, +94354,94509,94664,94819,94975,95131,95287,95444, +95601,95758,95916,96074,96233,96391,96551,96710, +96870,97030,97191,97352,97513,97675,97837,98000, +98163,98326,98489,98653,98818,98982,99148,99313, +99479,99645,99812,99979,100146,100314,100482,100651, +100820,100990,101159,101330,101500,101671,101843,102015, +102187,102360,102533,102706,102880,103054,103229,103404, +103580,103756,103933,104109,104287,104465,104643,104821, +105000,105180,105360,105540,105721,105902,106084,106266, +106449,106632,106816,107000,107184,107369,107555,107741, +107927,108114,108301,108489,108677,108866,109055,109245, +109435,109626,109817,110008,110200,110393,110586,110780, +110974,111169,111364,111560,111756,111952,112150,112347, +112546,112744,112944,113143,113344,113545,113746,113948, +114151,114354,114557,114761,114966,115171,115377,115583, +115790,115998,116206,116414,116623,116833,117044,117254, +117466,117678,117891,118104,118318,118532,118747,118963, +119179,119396,119613,119831,120050,120269,120489,120709, +120930,121152,121374,121597,121821,122045,122270,122496, +122722,122949,123176,123404,123633,123863,124093,124324, +124555,124787,125020,125254,125488,125723,125959,126195, +126432,126669,126908,127147,127387,127627,127869,128111, +128353,128597,128841,129086,129332,129578,129825,130073, +130322,130571,130821,131072,131324,131576,131830,132084, +132339,132594,132851,133108,133366,133625,133884,134145, +134406,134668,134931,135195,135459,135725,135991,136258, +136526,136795,137065,137335,137607,137879,138152,138426, +138701,138977,139254,139532,139810,140090,140370,140651, +140934,141217,141501,141786,142072,142359,142647,142936, +143226,143517,143808,144101,144395,144690,144986,145282, +145580,145879,146179,146480,146782,147084,147388,147693, +148000,148307,148615,148924,149235,149546,149859,150172, +150487,150803,151120,151438,151757,152077,152399,152722, +153045,153370,153697,154024,154352,154682,155013,155345, +155678,156013,156349,156686,157024,157363,157704,158046, +158389,158734,159079,159427,159775,160125,160476,160828, +161182,161537,161893,162251,162610,162970,163332,163695, +164060,164426,164793,165162,165532,165904,166277,166651, +167027,167405,167784,168164,168546,168930,169315,169701, +170089,170479,170870,171263,171657,172053,172451,172850, +173251,173653,174057,174463,174870,175279,175690,176102, +176516,176932,177349,177769,178190,178612,179037,179463, +179891,180321,180753,181186,181622,182059,182498,182939, +183382,183827,184274,184722,185173,185625,186080,186536, +186995,187455,187918,188382,188849,189318,189789,190261, +190736,191213,191693,192174,192658,193143,193631,194122, +194614,195109,195606,196105,196606,197110,197616,198125, +198636,199149,199664,200182,200703,201226,201751,202279, +202809,203342,203878,204416,204956,205500,206045,206594, +207145,207699,208255,208815,209376,209941,210509,211079, +211652,212228,212807,213389,213973,214561,215151,215745, +216341,216941,217544,218149,218758,219370,219985,220603, +221225,221849,222477,223108,223743,224381,225022,225666, +226314,226966,227621,228279,228941,229606,230275,230948, +231624,232304,232988,233676,234367,235062,235761,236463, +237170,237881,238595,239314,240036,240763,241493,242228, +242967,243711,244458,245210,245966,246727,247492,248261, +249035,249813,250596,251384,252176,252973,253774,254581, +255392,256208,257029,257855,258686,259522,260363,261209, +262060,262917,263779,264646,265519,266397,267280,268169, +269064,269965,270871,271782,272700,273624,274553,275489, +276430,277378,278332,279292,280258,281231,282210,283195, +284188,285186,286192,287204,288223,289249,290282,291322, +292369,293423,294485,295554,296630,297714,298805,299904, +301011,302126,303248,304379,305517,306664,307819,308983, +310154,311335,312524,313721,314928,316143,317368,318601, +319844,321097,322358,323629,324910,326201,327502,328812, +330133,331464,332805,334157,335519,336892,338276,339671, +341078,342495,343924,345364,346816,348280,349756,351244, +352744,354257,355783,357321,358872,360436,362013,363604, +365208,366826,368459,370105,371765,373440,375130,376835, +378555,380290,382040,383807,385589,387387,389202,391034, +392882,394747,396630,398530,400448,402384,404338,406311, +408303,410314,412344,414395,416465,418555,420666,422798, +424951,427125,429321,431540,433781,436045,438332,440643, +442978,445337,447720,450129,452564,455024,457511,460024, +462565,465133,467730,470355,473009,475692,478406,481150, +483925,486732,489571,492443,495348,498287,501261,504269, +507313,510394,513512,516667,519861,523094,526366,529680, +533034,536431,539870,543354,546881,550455,554074,557741, +561456,565221,569035,572901,576818,580789,584815,588896, +593033,597229,601483,605798,610174,614613,619117,623686, +628323,633028,637803,642651,647572,652568,657640,662792, +668024,673338,678737,684223,689797,695462,701219,707072, +713023,719074,725227,731486,737853,744331,750922,757631, +764460,771411,778490,785699,793041,800521,808143,815910, +823827,831898,840127,848520,857081,865817,874730,883829, +893117,902602,912289,922186,932298,942633,953199,964003, +975054,986361,997931,1009774,1021901,1034322,1047046,1060087, +1073455,1087164,1101225,1115654,1130465,1145673,1161294,1177345, +1193846,1210813,1228269,1246234,1264730,1283783,1303416,1323658, +1344537,1366084,1388330,1411312,1435065,1459630,1485049,1511367, +1538632,1566898,1596220,1626658,1658278,1691149,1725348,1760956, +1798063,1836758,1877161,1919378,1963536,2009771,2058233,2109087, +2162516,2218719,2277919,2340362,2406322,2476104,2550052,2628549, +2712030,2800983,2895966,2997613,3106651,3223918,3350381,3487165, +3635590,3797206,3973855,4167737,4381502,4618375,4882318,5178251, +5512368,5892567,6329090,6835455,7429880,8137527,8994149,10052327, +11392683,13145455,15535599,18988036,24413316,34178904,56965752,170910304 + +}; + +int finesine[10240] = { +25,75,125,175,226,276,326,376, +427,477,527,578,628,678,728,779, +829,879,929,980,1030,1080,1130,1181, +1231,1281,1331,1382,1432,1482,1532,1583, +1633,1683,1733,1784,1834,1884,1934,1985, +2035,2085,2135,2186,2236,2286,2336,2387, +2437,2487,2537,2587,2638,2688,2738,2788, +2839,2889,2939,2989,3039,3090,3140,3190, +3240,3291,3341,3391,3441,3491,3541,3592, +3642,3692,3742,3792,3843,3893,3943,3993, +4043,4093,4144,4194,4244,4294,4344,4394, +4445,4495,4545,4595,4645,4695,4745,4796, +4846,4896,4946,4996,5046,5096,5146,5197, +5247,5297,5347,5397,5447,5497,5547,5597, +5647,5697,5748,5798,5848,5898,5948,5998, +6048,6098,6148,6198,6248,6298,6348,6398, +6448,6498,6548,6598,6648,6698,6748,6798, +6848,6898,6948,6998,7048,7098,7148,7198, +7248,7298,7348,7398,7448,7498,7548,7598, +7648,7697,7747,7797,7847,7897,7947,7997, +8047,8097,8147,8196,8246,8296,8346,8396, +8446,8496,8545,8595,8645,8695,8745,8794, +8844,8894,8944,8994,9043,9093,9143,9193, +9243,9292,9342,9392,9442,9491,9541,9591, +9640,9690,9740,9790,9839,9889,9939,9988, +10038,10088,10137,10187,10237,10286,10336,10386, +10435,10485,10534,10584,10634,10683,10733,10782, +10832,10882,10931,10981,11030,11080,11129,11179, +11228,11278,11327,11377,11426,11476,11525,11575, +11624,11674,11723,11773,11822,11872,11921,11970, +12020,12069,12119,12168,12218,12267,12316,12366, +12415,12464,12514,12563,12612,12662,12711,12760, +12810,12859,12908,12957,13007,13056,13105,13154, +13204,13253,13302,13351,13401,13450,13499,13548, +13597,13647,13696,13745,13794,13843,13892,13941, +13990,14040,14089,14138,14187,14236,14285,14334, +14383,14432,14481,14530,14579,14628,14677,14726, +14775,14824,14873,14922,14971,15020,15069,15118, +15167,15215,15264,15313,15362,15411,15460,15509, +15557,15606,15655,15704,15753,15802,15850,15899, +15948,15997,16045,16094,16143,16191,16240,16289, +16338,16386,16435,16484,16532,16581,16629,16678, +16727,16775,16824,16872,16921,16970,17018,17067, +17115,17164,17212,17261,17309,17358,17406,17455, +17503,17551,17600,17648,17697,17745,17793,17842, +17890,17939,17987,18035,18084,18132,18180,18228, +18277,18325,18373,18421,18470,18518,18566,18614, +18663,18711,18759,18807,18855,18903,18951,19000, +19048,19096,19144,19192,19240,19288,19336,19384, +19432,19480,19528,19576,19624,19672,19720,19768, +19816,19864,19912,19959,20007,20055,20103,20151, +20199,20246,20294,20342,20390,20438,20485,20533, +20581,20629,20676,20724,20772,20819,20867,20915, +20962,21010,21057,21105,21153,21200,21248,21295, +21343,21390,21438,21485,21533,21580,21628,21675, +21723,21770,21817,21865,21912,21960,22007,22054, +22102,22149,22196,22243,22291,22338,22385,22433, +22480,22527,22574,22621,22668,22716,22763,22810, +22857,22904,22951,22998,23045,23092,23139,23186, +23233,23280,23327,23374,23421,23468,23515,23562, +23609,23656,23703,23750,23796,23843,23890,23937, +23984,24030,24077,24124,24171,24217,24264,24311, +24357,24404,24451,24497,24544,24591,24637,24684, +24730,24777,24823,24870,24916,24963,25009,25056, +25102,25149,25195,25241,25288,25334,25381,25427, +25473,25520,25566,25612,25658,25705,25751,25797, +25843,25889,25936,25982,26028,26074,26120,26166, +26212,26258,26304,26350,26396,26442,26488,26534, +26580,26626,26672,26718,26764,26810,26856,26902, +26947,26993,27039,27085,27131,27176,27222,27268, +27313,27359,27405,27450,27496,27542,27587,27633, +27678,27724,27770,27815,27861,27906,27952,27997, +28042,28088,28133,28179,28224,28269,28315,28360, +28405,28451,28496,28541,28586,28632,28677,28722, +28767,28812,28858,28903,28948,28993,29038,29083, +29128,29173,29218,29263,29308,29353,29398,29443, +29488,29533,29577,29622,29667,29712,29757,29801, +29846,29891,29936,29980,30025,30070,30114,30159, +30204,30248,30293,30337,30382,30426,30471,30515, +30560,30604,30649,30693,30738,30782,30826,30871, +30915,30959,31004,31048,31092,31136,31181,31225, +31269,31313,31357,31402,31446,31490,31534,31578, +31622,31666,31710,31754,31798,31842,31886,31930, +31974,32017,32061,32105,32149,32193,32236,32280, +32324,32368,32411,32455,32499,32542,32586,32630, +32673,32717,32760,32804,32847,32891,32934,32978, +33021,33065,33108,33151,33195,33238,33281,33325, +33368,33411,33454,33498,33541,33584,33627,33670, +33713,33756,33799,33843,33886,33929,33972,34015, +34057,34100,34143,34186,34229,34272,34315,34358, +34400,34443,34486,34529,34571,34614,34657,34699, +34742,34785,34827,34870,34912,34955,34997,35040, +35082,35125,35167,35210,35252,35294,35337,35379, +35421,35464,35506,35548,35590,35633,35675,35717, +35759,35801,35843,35885,35927,35969,36011,36053, +36095,36137,36179,36221,36263,36305,36347,36388, +36430,36472,36514,36555,36597,36639,36681,36722, +36764,36805,36847,36889,36930,36972,37013,37055, +37096,37137,37179,37220,37262,37303,37344,37386, +37427,37468,37509,37551,37592,37633,37674,37715, +37756,37797,37838,37879,37920,37961,38002,38043, +38084,38125,38166,38207,38248,38288,38329,38370, +38411,38451,38492,38533,38573,38614,38655,38695, +38736,38776,38817,38857,38898,38938,38979,39019, +39059,39100,39140,39180,39221,39261,39301,39341, +39382,39422,39462,39502,39542,39582,39622,39662, +39702,39742,39782,39822,39862,39902,39942,39982, +40021,40061,40101,40141,40180,40220,40260,40300, +40339,40379,40418,40458,40497,40537,40576,40616, +40655,40695,40734,40773,40813,40852,40891,40931, +40970,41009,41048,41087,41127,41166,41205,41244, +41283,41322,41361,41400,41439,41478,41517,41556, +41595,41633,41672,41711,41750,41788,41827,41866, +41904,41943,41982,42020,42059,42097,42136,42174, +42213,42251,42290,42328,42366,42405,42443,42481, +42520,42558,42596,42634,42672,42711,42749,42787, +42825,42863,42901,42939,42977,43015,43053,43091, +43128,43166,43204,43242,43280,43317,43355,43393, +43430,43468,43506,43543,43581,43618,43656,43693, +43731,43768,43806,43843,43880,43918,43955,43992, +44029,44067,44104,44141,44178,44215,44252,44289, +44326,44363,44400,44437,44474,44511,44548,44585, +44622,44659,44695,44732,44769,44806,44842,44879, +44915,44952,44989,45025,45062,45098,45135,45171, +45207,45244,45280,45316,45353,45389,45425,45462, +45498,45534,45570,45606,45642,45678,45714,45750, +45786,45822,45858,45894,45930,45966,46002,46037, +46073,46109,46145,46180,46216,46252,46287,46323, +46358,46394,46429,46465,46500,46536,46571,46606, +46642,46677,46712,46747,46783,46818,46853,46888, +46923,46958,46993,47028,47063,47098,47133,47168, +47203,47238,47273,47308,47342,47377,47412,47446, +47481,47516,47550,47585,47619,47654,47688,47723, +47757,47792,47826,47860,47895,47929,47963,47998, +48032,48066,48100,48134,48168,48202,48237,48271, +48305,48338,48372,48406,48440,48474,48508,48542, +48575,48609,48643,48676,48710,48744,48777,48811, +48844,48878,48911,48945,48978,49012,49045,49078, +49112,49145,49178,49211,49244,49278,49311,49344, +49377,49410,49443,49476,49509,49542,49575,49608, +49640,49673,49706,49739,49771,49804,49837,49869, +49902,49935,49967,50000,50032,50065,50097,50129, +50162,50194,50226,50259,50291,50323,50355,50387, +50420,50452,50484,50516,50548,50580,50612,50644, +50675,50707,50739,50771,50803,50834,50866,50898, +50929,50961,50993,51024,51056,51087,51119,51150, +51182,51213,51244,51276,51307,51338,51369,51401, +51432,51463,51494,51525,51556,51587,51618,51649, +51680,51711,51742,51773,51803,51834,51865,51896, +51926,51957,51988,52018,52049,52079,52110,52140, +52171,52201,52231,52262,52292,52322,52353,52383, +52413,52443,52473,52503,52534,52564,52594,52624, +52653,52683,52713,52743,52773,52803,52832,52862, +52892,52922,52951,52981,53010,53040,53069,53099, +53128,53158,53187,53216,53246,53275,53304,53334, +53363,53392,53421,53450,53479,53508,53537,53566, +53595,53624,53653,53682,53711,53739,53768,53797, +53826,53854,53883,53911,53940,53969,53997,54026, +54054,54082,54111,54139,54167,54196,54224,54252, +54280,54308,54337,54365,54393,54421,54449,54477, +54505,54533,54560,54588,54616,54644,54672,54699, +54727,54755,54782,54810,54837,54865,54892,54920, +54947,54974,55002,55029,55056,55084,55111,55138, +55165,55192,55219,55246,55274,55300,55327,55354, +55381,55408,55435,55462,55489,55515,55542,55569, +55595,55622,55648,55675,55701,55728,55754,55781, +55807,55833,55860,55886,55912,55938,55965,55991, +56017,56043,56069,56095,56121,56147,56173,56199, +56225,56250,56276,56302,56328,56353,56379,56404, +56430,56456,56481,56507,56532,56557,56583,56608, +56633,56659,56684,56709,56734,56760,56785,56810, +56835,56860,56885,56910,56935,56959,56984,57009, +57034,57059,57083,57108,57133,57157,57182,57206, +57231,57255,57280,57304,57329,57353,57377,57402, +57426,57450,57474,57498,57522,57546,57570,57594, +57618,57642,57666,57690,57714,57738,57762,57785, +57809,57833,57856,57880,57903,57927,57950,57974, +57997,58021,58044,58067,58091,58114,58137,58160, +58183,58207,58230,58253,58276,58299,58322,58345, +58367,58390,58413,58436,58459,58481,58504,58527, +58549,58572,58594,58617,58639,58662,58684,58706, +58729,58751,58773,58795,58818,58840,58862,58884, +58906,58928,58950,58972,58994,59016,59038,59059, +59081,59103,59125,59146,59168,59190,59211,59233, +59254,59276,59297,59318,59340,59361,59382,59404, +59425,59446,59467,59488,59509,59530,59551,59572, +59593,59614,59635,59656,59677,59697,59718,59739, +59759,59780,59801,59821,59842,59862,59883,59903, +59923,59944,59964,59984,60004,60025,60045,60065, +60085,60105,60125,60145,60165,60185,60205,60225, +60244,60264,60284,60304,60323,60343,60363,60382, +60402,60421,60441,60460,60479,60499,60518,60537, +60556,60576,60595,60614,60633,60652,60671,60690, +60709,60728,60747,60766,60785,60803,60822,60841, +60859,60878,60897,60915,60934,60952,60971,60989, +61007,61026,61044,61062,61081,61099,61117,61135, +61153,61171,61189,61207,61225,61243,61261,61279, +61297,61314,61332,61350,61367,61385,61403,61420, +61438,61455,61473,61490,61507,61525,61542,61559, +61577,61594,61611,61628,61645,61662,61679,61696, +61713,61730,61747,61764,61780,61797,61814,61831, +61847,61864,61880,61897,61913,61930,61946,61963, +61979,61995,62012,62028,62044,62060,62076,62092, +62108,62125,62141,62156,62172,62188,62204,62220, +62236,62251,62267,62283,62298,62314,62329,62345, +62360,62376,62391,62407,62422,62437,62453,62468, +62483,62498,62513,62528,62543,62558,62573,62588, +62603,62618,62633,62648,62662,62677,62692,62706, +62721,62735,62750,62764,62779,62793,62808,62822, +62836,62850,62865,62879,62893,62907,62921,62935, +62949,62963,62977,62991,63005,63019,63032,63046, +63060,63074,63087,63101,63114,63128,63141,63155, +63168,63182,63195,63208,63221,63235,63248,63261, +63274,63287,63300,63313,63326,63339,63352,63365, +63378,63390,63403,63416,63429,63441,63454,63466, +63479,63491,63504,63516,63528,63541,63553,63565, +63578,63590,63602,63614,63626,63638,63650,63662, +63674,63686,63698,63709,63721,63733,63745,63756, +63768,63779,63791,63803,63814,63825,63837,63848, +63859,63871,63882,63893,63904,63915,63927,63938, +63949,63960,63971,63981,63992,64003,64014,64025, +64035,64046,64057,64067,64078,64088,64099,64109, +64120,64130,64140,64151,64161,64171,64181,64192, +64202,64212,64222,64232,64242,64252,64261,64271, +64281,64291,64301,64310,64320,64330,64339,64349, +64358,64368,64377,64387,64396,64405,64414,64424, +64433,64442,64451,64460,64469,64478,64487,64496, +64505,64514,64523,64532,64540,64549,64558,64566, +64575,64584,64592,64601,64609,64617,64626,64634, +64642,64651,64659,64667,64675,64683,64691,64699, +64707,64715,64723,64731,64739,64747,64754,64762, +64770,64777,64785,64793,64800,64808,64815,64822, +64830,64837,64844,64852,64859,64866,64873,64880, +64887,64895,64902,64908,64915,64922,64929,64936, +64943,64949,64956,64963,64969,64976,64982,64989, +64995,65002,65008,65015,65021,65027,65033,65040, +65046,65052,65058,65064,65070,65076,65082,65088, +65094,65099,65105,65111,65117,65122,65128,65133, +65139,65144,65150,65155,65161,65166,65171,65177, +65182,65187,65192,65197,65202,65207,65212,65217, +65222,65227,65232,65237,65242,65246,65251,65256, +65260,65265,65270,65274,65279,65283,65287,65292, +65296,65300,65305,65309,65313,65317,65321,65325, +65329,65333,65337,65341,65345,65349,65352,65356, +65360,65363,65367,65371,65374,65378,65381,65385, +65388,65391,65395,65398,65401,65404,65408,65411, +65414,65417,65420,65423,65426,65429,65431,65434, +65437,65440,65442,65445,65448,65450,65453,65455, +65458,65460,65463,65465,65467,65470,65472,65474, +65476,65478,65480,65482,65484,65486,65488,65490, +65492,65494,65496,65497,65499,65501,65502,65504, +65505,65507,65508,65510,65511,65513,65514,65515, +65516,65518,65519,65520,65521,65522,65523,65524, +65525,65526,65527,65527,65528,65529,65530,65530, +65531,65531,65532,65532,65533,65533,65534,65534, +65534,65535,65535,65535,65535,65535,65535,65535, +65535,65535,65535,65535,65535,65535,65535,65534, +65534,65534,65533,65533,65532,65532,65531,65531, +65530,65530,65529,65528,65527,65527,65526,65525, +65524,65523,65522,65521,65520,65519,65518,65516, +65515,65514,65513,65511,65510,65508,65507,65505, +65504,65502,65501,65499,65497,65496,65494,65492, +65490,65488,65486,65484,65482,65480,65478,65476, +65474,65472,65470,65467,65465,65463,65460,65458, +65455,65453,65450,65448,65445,65442,65440,65437, +65434,65431,65429,65426,65423,65420,65417,65414, +65411,65408,65404,65401,65398,65395,65391,65388, +65385,65381,65378,65374,65371,65367,65363,65360, +65356,65352,65349,65345,65341,65337,65333,65329, +65325,65321,65317,65313,65309,65305,65300,65296, +65292,65287,65283,65279,65274,65270,65265,65260, +65256,65251,65246,65242,65237,65232,65227,65222, +65217,65212,65207,65202,65197,65192,65187,65182, +65177,65171,65166,65161,65155,65150,65144,65139, +65133,65128,65122,65117,65111,65105,65099,65094, +65088,65082,65076,65070,65064,65058,65052,65046, +65040,65033,65027,65021,65015,65008,65002,64995, +64989,64982,64976,64969,64963,64956,64949,64943, +64936,64929,64922,64915,64908,64902,64895,64887, +64880,64873,64866,64859,64852,64844,64837,64830, +64822,64815,64808,64800,64793,64785,64777,64770, +64762,64754,64747,64739,64731,64723,64715,64707, +64699,64691,64683,64675,64667,64659,64651,64642, +64634,64626,64617,64609,64600,64592,64584,64575, +64566,64558,64549,64540,64532,64523,64514,64505, +64496,64487,64478,64469,64460,64451,64442,64433, +64424,64414,64405,64396,64387,64377,64368,64358, +64349,64339,64330,64320,64310,64301,64291,64281, +64271,64261,64252,64242,64232,64222,64212,64202, +64192,64181,64171,64161,64151,64140,64130,64120, +64109,64099,64088,64078,64067,64057,64046,64035, +64025,64014,64003,63992,63981,63971,63960,63949, +63938,63927,63915,63904,63893,63882,63871,63859, +63848,63837,63825,63814,63803,63791,63779,63768, +63756,63745,63733,63721,63709,63698,63686,63674, +63662,63650,63638,63626,63614,63602,63590,63578, +63565,63553,63541,63528,63516,63504,63491,63479, +63466,63454,63441,63429,63416,63403,63390,63378, +63365,63352,63339,63326,63313,63300,63287,63274, +63261,63248,63235,63221,63208,63195,63182,63168, +63155,63141,63128,63114,63101,63087,63074,63060, +63046,63032,63019,63005,62991,62977,62963,62949, +62935,62921,62907,62893,62879,62865,62850,62836, +62822,62808,62793,62779,62764,62750,62735,62721, +62706,62692,62677,62662,62648,62633,62618,62603, +62588,62573,62558,62543,62528,62513,62498,62483, +62468,62453,62437,62422,62407,62391,62376,62360, +62345,62329,62314,62298,62283,62267,62251,62236, +62220,62204,62188,62172,62156,62141,62125,62108, +62092,62076,62060,62044,62028,62012,61995,61979, +61963,61946,61930,61913,61897,61880,61864,61847, +61831,61814,61797,61780,61764,61747,61730,61713, +61696,61679,61662,61645,61628,61611,61594,61577, +61559,61542,61525,61507,61490,61473,61455,61438, +61420,61403,61385,61367,61350,61332,61314,61297, +61279,61261,61243,61225,61207,61189,61171,61153, +61135,61117,61099,61081,61062,61044,61026,61007, +60989,60971,60952,60934,60915,60897,60878,60859, +60841,60822,60803,60785,60766,60747,60728,60709, +60690,60671,60652,60633,60614,60595,60576,60556, +60537,60518,60499,60479,60460,60441,60421,60402, +60382,60363,60343,60323,60304,60284,60264,60244, +60225,60205,60185,60165,60145,60125,60105,60085, +60065,60045,60025,60004,59984,59964,59944,59923, +59903,59883,59862,59842,59821,59801,59780,59759, +59739,59718,59697,59677,59656,59635,59614,59593, +59572,59551,59530,59509,59488,59467,59446,59425, +59404,59382,59361,59340,59318,59297,59276,59254, +59233,59211,59190,59168,59146,59125,59103,59081, +59059,59038,59016,58994,58972,58950,58928,58906, +58884,58862,58840,58818,58795,58773,58751,58729, +58706,58684,58662,58639,58617,58594,58572,58549, +58527,58504,58481,58459,58436,58413,58390,58367, +58345,58322,58299,58276,58253,58230,58207,58183, +58160,58137,58114,58091,58067,58044,58021,57997, +57974,57950,57927,57903,57880,57856,57833,57809, +57785,57762,57738,57714,57690,57666,57642,57618, +57594,57570,57546,57522,57498,57474,57450,57426, +57402,57377,57353,57329,57304,57280,57255,57231, +57206,57182,57157,57133,57108,57083,57059,57034, +57009,56984,56959,56935,56910,56885,56860,56835, +56810,56785,56760,56734,56709,56684,56659,56633, +56608,56583,56557,56532,56507,56481,56456,56430, +56404,56379,56353,56328,56302,56276,56250,56225, +56199,56173,56147,56121,56095,56069,56043,56017, +55991,55965,55938,55912,55886,55860,55833,55807, +55781,55754,55728,55701,55675,55648,55622,55595, +55569,55542,55515,55489,55462,55435,55408,55381, +55354,55327,55300,55274,55246,55219,55192,55165, +55138,55111,55084,55056,55029,55002,54974,54947, +54920,54892,54865,54837,54810,54782,54755,54727, +54699,54672,54644,54616,54588,54560,54533,54505, +54477,54449,54421,54393,54365,54337,54308,54280, +54252,54224,54196,54167,54139,54111,54082,54054, +54026,53997,53969,53940,53911,53883,53854,53826, +53797,53768,53739,53711,53682,53653,53624,53595, +53566,53537,53508,53479,53450,53421,53392,53363, +53334,53304,53275,53246,53216,53187,53158,53128, +53099,53069,53040,53010,52981,52951,52922,52892, +52862,52832,52803,52773,52743,52713,52683,52653, +52624,52594,52564,52534,52503,52473,52443,52413, +52383,52353,52322,52292,52262,52231,52201,52171, +52140,52110,52079,52049,52018,51988,51957,51926, +51896,51865,51834,51803,51773,51742,51711,51680, +51649,51618,51587,51556,51525,51494,51463,51432, +51401,51369,51338,51307,51276,51244,51213,51182, +51150,51119,51087,51056,51024,50993,50961,50929, +50898,50866,50834,50803,50771,50739,50707,50675, +50644,50612,50580,50548,50516,50484,50452,50420, +50387,50355,50323,50291,50259,50226,50194,50162, +50129,50097,50065,50032,50000,49967,49935,49902, +49869,49837,49804,49771,49739,49706,49673,49640, +49608,49575,49542,49509,49476,49443,49410,49377, +49344,49311,49278,49244,49211,49178,49145,49112, +49078,49045,49012,48978,48945,48911,48878,48844, +48811,48777,48744,48710,48676,48643,48609,48575, +48542,48508,48474,48440,48406,48372,48338,48304, +48271,48237,48202,48168,48134,48100,48066,48032, +47998,47963,47929,47895,47860,47826,47792,47757, +47723,47688,47654,47619,47585,47550,47516,47481, +47446,47412,47377,47342,47308,47273,47238,47203, +47168,47133,47098,47063,47028,46993,46958,46923, +46888,46853,46818,46783,46747,46712,46677,46642, +46606,46571,46536,46500,46465,46429,46394,46358, +46323,46287,46252,46216,46180,46145,46109,46073, +46037,46002,45966,45930,45894,45858,45822,45786, +45750,45714,45678,45642,45606,45570,45534,45498, +45462,45425,45389,45353,45316,45280,45244,45207, +45171,45135,45098,45062,45025,44989,44952,44915, +44879,44842,44806,44769,44732,44695,44659,44622, +44585,44548,44511,44474,44437,44400,44363,44326, +44289,44252,44215,44178,44141,44104,44067,44029, +43992,43955,43918,43880,43843,43806,43768,43731, +43693,43656,43618,43581,43543,43506,43468,43430, +43393,43355,43317,43280,43242,43204,43166,43128, +43091,43053,43015,42977,42939,42901,42863,42825, +42787,42749,42711,42672,42634,42596,42558,42520, +42481,42443,42405,42366,42328,42290,42251,42213, +42174,42136,42097,42059,42020,41982,41943,41904, +41866,41827,41788,41750,41711,41672,41633,41595, +41556,41517,41478,41439,41400,41361,41322,41283, +41244,41205,41166,41127,41088,41048,41009,40970, +40931,40891,40852,40813,40773,40734,40695,40655, +40616,40576,40537,40497,40458,40418,40379,40339, +40300,40260,40220,40180,40141,40101,40061,40021, +39982,39942,39902,39862,39822,39782,39742,39702, +39662,39622,39582,39542,39502,39462,39422,39382, +39341,39301,39261,39221,39180,39140,39100,39059, +39019,38979,38938,38898,38857,38817,38776,38736, +38695,38655,38614,38573,38533,38492,38451,38411, +38370,38329,38288,38248,38207,38166,38125,38084, +38043,38002,37961,37920,37879,37838,37797,37756, +37715,37674,37633,37592,37551,37509,37468,37427, +37386,37344,37303,37262,37220,37179,37137,37096, +37055,37013,36972,36930,36889,36847,36805,36764, +36722,36681,36639,36597,36556,36514,36472,36430, +36388,36347,36305,36263,36221,36179,36137,36095, +36053,36011,35969,35927,35885,35843,35801,35759, +35717,35675,35633,35590,35548,35506,35464,35421, +35379,35337,35294,35252,35210,35167,35125,35082, +35040,34997,34955,34912,34870,34827,34785,34742, +34699,34657,34614,34571,34529,34486,34443,34400, +34358,34315,34272,34229,34186,34143,34100,34057, +34015,33972,33929,33886,33843,33799,33756,33713, +33670,33627,33584,33541,33498,33454,33411,33368, +33325,33281,33238,33195,33151,33108,33065,33021, +32978,32934,32891,32847,32804,32760,32717,32673, +32630,32586,32542,32499,32455,32411,32368,32324, +32280,32236,32193,32149,32105,32061,32017,31974, +31930,31886,31842,31798,31754,31710,31666,31622, +31578,31534,31490,31446,31402,31357,31313,31269, +31225,31181,31136,31092,31048,31004,30959,30915, +30871,30826,30782,30738,30693,30649,30604,30560, +30515,30471,30426,30382,30337,30293,30248,30204, +30159,30114,30070,30025,29980,29936,29891,29846, +29801,29757,29712,29667,29622,29577,29533,29488, +29443,29398,29353,29308,29263,29218,29173,29128, +29083,29038,28993,28948,28903,28858,28812,28767, +28722,28677,28632,28586,28541,28496,28451,28405, +28360,28315,28269,28224,28179,28133,28088,28042, +27997,27952,27906,27861,27815,27770,27724,27678, +27633,27587,27542,27496,27450,27405,27359,27313, +27268,27222,27176,27131,27085,27039,26993,26947, +26902,26856,26810,26764,26718,26672,26626,26580, +26534,26488,26442,26396,26350,26304,26258,26212, +26166,26120,26074,26028,25982,25936,25889,25843, +25797,25751,25705,25658,25612,25566,25520,25473, +25427,25381,25334,25288,25241,25195,25149,25102, +25056,25009,24963,24916,24870,24823,24777,24730, +24684,24637,24591,24544,24497,24451,24404,24357, +24311,24264,24217,24171,24124,24077,24030,23984, +23937,23890,23843,23796,23750,23703,23656,23609, +23562,23515,23468,23421,23374,23327,23280,23233, +23186,23139,23092,23045,22998,22951,22904,22857, +22810,22763,22716,22668,22621,22574,22527,22480, +22433,22385,22338,22291,22243,22196,22149,22102, +22054,22007,21960,21912,21865,21817,21770,21723, +21675,21628,21580,21533,21485,21438,21390,21343, +21295,21248,21200,21153,21105,21057,21010,20962, +20915,20867,20819,20772,20724,20676,20629,20581, +20533,20485,20438,20390,20342,20294,20246,20199, +20151,20103,20055,20007,19959,19912,19864,19816, +19768,19720,19672,19624,19576,19528,19480,19432, +19384,19336,19288,19240,19192,19144,19096,19048, +19000,18951,18903,18855,18807,18759,18711,18663, +18614,18566,18518,18470,18421,18373,18325,18277, +18228,18180,18132,18084,18035,17987,17939,17890, +17842,17793,17745,17697,17648,17600,17551,17503, +17455,17406,17358,17309,17261,17212,17164,17115, +17067,17018,16970,16921,16872,16824,16775,16727, +16678,16629,16581,16532,16484,16435,16386,16338, +16289,16240,16191,16143,16094,16045,15997,15948, +15899,15850,15802,15753,15704,15655,15606,15557, +15509,15460,15411,15362,15313,15264,15215,15167, +15118,15069,15020,14971,14922,14873,14824,14775, +14726,14677,14628,14579,14530,14481,14432,14383, +14334,14285,14236,14187,14138,14089,14040,13990, +13941,13892,13843,13794,13745,13696,13646,13597, +13548,13499,13450,13401,13351,13302,13253,13204, +13154,13105,13056,13007,12957,12908,12859,12810, +12760,12711,12662,12612,12563,12514,12464,12415, +12366,12316,12267,12218,12168,12119,12069,12020, +11970,11921,11872,11822,11773,11723,11674,11624, +11575,11525,11476,11426,11377,11327,11278,11228, +11179,11129,11080,11030,10981,10931,10882,10832, +10782,10733,10683,10634,10584,10534,10485,10435, +10386,10336,10286,10237,10187,10137,10088,10038, +9988,9939,9889,9839,9790,9740,9690,9640, +9591,9541,9491,9442,9392,9342,9292,9243, +9193,9143,9093,9043,8994,8944,8894,8844, +8794,8745,8695,8645,8595,8545,8496,8446, +8396,8346,8296,8246,8196,8147,8097,8047, +7997,7947,7897,7847,7797,7747,7697,7648, +7598,7548,7498,7448,7398,7348,7298,7248, +7198,7148,7098,7048,6998,6948,6898,6848, +6798,6748,6698,6648,6598,6548,6498,6448, +6398,6348,6298,6248,6198,6148,6098,6048, +5998,5948,5898,5848,5798,5748,5697,5647, +5597,5547,5497,5447,5397,5347,5297,5247, +5197,5146,5096,5046,4996,4946,4896,4846, +4796,4745,4695,4645,4595,4545,4495,4445, +4394,4344,4294,4244,4194,4144,4093,4043, +3993,3943,3893,3843,3792,3742,3692,3642, +3592,3541,3491,3441,3391,3341,3291,3240, +3190,3140,3090,3039,2989,2939,2889,2839, +2788,2738,2688,2638,2587,2537,2487,2437, +2387,2336,2286,2236,2186,2135,2085,2035, +1985,1934,1884,1834,1784,1733,1683,1633, +1583,1532,1482,1432,1382,1331,1281,1231, +1181,1130,1080,1030,980,929,879,829, +779,728,678,628,578,527,477,427, +376,326,276,226,175,125,75,25, +-25,-75,-125,-175,-226,-276,-326,-376, +-427,-477,-527,-578,-628,-678,-728,-779, +-829,-879,-929,-980,-1030,-1080,-1130,-1181, +-1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583, +-1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985, +-2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387, +-2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788, +-2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190, +-3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592, +-3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993, +-4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394, +-4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796, +-4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197, +-5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597, +-5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998, +-6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398, +-6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798, +-6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198, +-7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598, +-7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997, +-8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396, +-8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794, +-8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193, +-9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591, +-9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988, +-10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386, +-10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782, +-10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179, +-11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575, +-11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970, +-12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366, +-12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760, +-12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154, +-13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548, +-13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941, +-13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334, +-14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726, +-14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118, +-15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509, +-15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899, +-15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289, +-16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678, +-16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067, +-17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455, +-17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842, +-17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228, +-18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614, +-18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000, +-19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384, +-19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768, +-19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151, +-20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533, +-20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915, +-20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295, +-21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675, +-21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054, +-22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433, +-22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810, +-22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186, +-23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562, +-23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937, +-23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311, +-24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684, +-24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056, +-25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427, +-25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797, +-25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166, +-26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534, +-26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902, +-26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268, +-27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633, +-27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997, +-28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360, +-28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722, +-28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083, +-29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443, +-29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801, +-29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159, +-30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515, +-30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871, +-30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225, +-31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578, +-31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930, +-31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280, +-32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630, +-32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978, +-33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325, +-33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670, +-33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015, +-34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358, +-34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699, +-34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040, +-35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379, +-35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717, +-35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053, +-36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388, +-36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722, +-36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055, +-37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386, +-37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715, +-37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043, +-38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370, +-38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695, +-38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019, +-39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341, +-39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662, +-39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982, +-40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299, +-40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616, +-40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931, +-40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244, +-41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556, +-41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866, +-41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174, +-42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481, +-42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787, +-42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091, +-43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393, +-43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693, +-43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992, +-44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289, +-44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585, +-44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879, +-44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171, +-45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462, +-45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750, +-45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037, +-46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323, +-46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606, +-46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888, +-46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168, +-47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446, +-47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723, +-47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998, +-48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271, +-48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542, +-48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811, +-48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078, +-49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344, +-49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608, +-49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869, +-49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129, +-50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387, +-50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644, +-50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898, +-50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150, +-51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401, +-51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649, +-51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896, +-51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140, +-52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383, +-52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624, +-52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862, +-52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099, +-53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334, +-53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566, +-53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797, +-53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026, +-54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252, +-54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477, +-54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699, +-54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920, +-54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138, +-55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354, +-55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569, +-55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781, +-55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991, +-56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199, +-56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404, +-56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608, +-56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810, +-56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009, +-57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206, +-57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402, +-57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594, +-57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785, +-57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974, +-57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160, +-58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345, +-58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527, +-58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706, +-58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884, +-58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059, +-59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233, +-59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404, +-59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572, +-59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739, +-59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903, +-59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065, +-60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225, +-60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382, +-60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537, +-60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690, +-60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841, +-60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989, +-61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135, +-61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279, +-61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420, +-61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559, +-61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696, +-61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831, +-61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963, +-61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092, +-62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220, +-62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345, +-62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468, +-62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588, +-62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706, +-62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822, +-62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935, +-62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046, +-63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155, +-63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261, +-63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365, +-63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466, +-63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565, +-63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662, +-63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756, +-63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848, +-63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938, +-63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025, +-64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109, +-64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192, +-64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271, +-64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349, +-64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424, +-64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496, +-64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566, +-64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634, +-64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699, +-64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762, +-64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822, +-64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880, +-64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936, +-64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989, +-64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040, +-65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088, +-65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133, +-65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177, +-65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217, +-65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256, +-65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292, +-65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325, +-65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356, +-65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385, +-65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411, +-65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434, +-65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455, +-65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474, +-65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490, +-65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504, +-65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515, +-65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524, +-65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530, +-65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534, +-65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535, +-65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534, +-65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531, +-65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525, +-65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516, +-65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505, +-65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492, +-65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476, +-65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458, +-65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437, +-65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414, +-65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388, +-65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360, +-65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329, +-65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296, +-65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260, +-65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222, +-65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182, +-65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139, +-65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094, +-65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046, +-65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995, +-64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943, +-64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887, +-64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830, +-64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770, +-64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707, +-64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642, +-64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575, +-64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505, +-64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433, +-64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358, +-64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281, +-64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202, +-64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120, +-64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035, +-64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949, +-63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859, +-63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768, +-63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674, +-63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578, +-63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479, +-63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378, +-63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274, +-63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168, +-63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060, +-63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949, +-62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836, +-62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721, +-62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603, +-62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483, +-62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360, +-62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236, +-62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108, +-62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979, +-61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847, +-61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713, +-61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577, +-61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438, +-61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297, +-61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153, +-61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007, +-60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859, +-60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709, +-60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556, +-60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402, +-60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244, +-60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085, +-60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923, +-59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759, +-59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593, +-59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425, +-59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254, +-59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081, +-59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906, +-58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729, +-58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549, +-58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367, +-58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183, +-58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997, +-57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809, +-57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618, +-57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426, +-57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231, +-57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034, +-57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835, +-56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633, +-56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430, +-56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225, +-56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017, +-55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807, +-55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595, +-55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381, +-55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165, +-55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947, +-54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727, +-54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505, +-54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280, +-54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054, +-54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826, +-53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595, +-53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363, +-53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128, +-53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892, +-52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653, +-52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413, +-52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171, +-52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926, +-51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680, +-51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432, +-51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182, +-51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929, +-50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675, +-50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420, +-50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162, +-50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902, +-49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640, +-49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377, +-49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112, +-49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844, +-48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575, +-48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305, +-48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032, +-47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757, +-47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481, +-47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203, +-47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923, +-46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642, +-46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358, +-46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073, +-46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786, +-45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498, +-45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207, +-45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915, +-44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622, +-44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326, +-44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029, +-43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731, +-43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430, +-43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128, +-43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825, +-42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520, +-42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213, +-42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904, +-41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595, +-41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283, +-41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970, +-40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655, +-40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339, +-40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021, +-39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702, +-39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382, +-39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059, +-39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736, +-38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411, +-38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084, +-38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756, +-37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427, +-37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096, +-37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764, +-36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430, +-36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095, +-36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759, +-35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421, +-35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082, +-35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742, +-34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400, +-34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057, +-34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713, +-33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368, +-33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021, +-32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673, +-32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324, +-32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974, +-31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622, +-31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269, +-31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915, +-30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560, +-30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204, +-30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846, +-29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488, +-29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128, +-29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767, +-28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405, +-28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042, +-27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678, +-27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313, +-27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947, +-26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580, +-26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212, +-26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843, +-25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473, +-25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102, +-25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730, +-24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357, +-24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984, +-23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609, +-23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233, +-23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857, +-22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480, +-22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102, +-22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723, +-21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343, +-21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962, +-20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581, +-20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199, +-20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816, +-19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432, +-19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048, +-19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663, +-18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277, +-18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890, +-17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503, +-17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115, +-17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727, +-16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338, +-16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948, +-15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557, +-15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167, +-15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775, +-14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383, +-14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990, +-13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597, +-13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204, +-13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810, +-12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415, +-12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020, +-11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624, +-11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228, +-11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832, +-10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435, +-10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038, +-9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640, +-9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243, +-9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844, +-8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446, +-8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047, +-7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648, +-7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248, +-7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848, +-6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448, +-6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048, +-5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647, +-5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247, +-5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846, +-4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445, +-4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043, +-3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642, +-3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240, +-3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839, +-2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437, +-2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035, +-1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633, +-1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231, +-1181,-1130,-1080,-1030,-980,-929,-879,-829, +-779,-728,-678,-628,-578,-527,-477,-427, +-376,-326,-276,-226,-175,-125,-75,-25, +25,75,125,175,226,276,326,376, +427,477,527,578,628,678,728,779, +829,879,929,980,1030,1080,1130,1181, +1231,1281,1331,1382,1432,1482,1532,1583, +1633,1683,1733,1784,1834,1884,1934,1985, +2035,2085,2135,2186,2236,2286,2336,2387, +2437,2487,2537,2587,2638,2688,2738,2788, +2839,2889,2939,2989,3039,3090,3140,3190, +3240,3291,3341,3391,3441,3491,3542,3592, +3642,3692,3742,3792,3843,3893,3943,3993, +4043,4093,4144,4194,4244,4294,4344,4394, +4445,4495,4545,4595,4645,4695,4745,4796, +4846,4896,4946,4996,5046,5096,5146,5197, +5247,5297,5347,5397,5447,5497,5547,5597, +5647,5697,5747,5798,5848,5898,5948,5998, +6048,6098,6148,6198,6248,6298,6348,6398, +6448,6498,6548,6598,6648,6698,6748,6798, +6848,6898,6948,6998,7048,7098,7148,7198, +7248,7298,7348,7398,7448,7498,7548,7598, +7648,7697,7747,7797,7847,7897,7947,7997, +8047,8097,8147,8196,8246,8296,8346,8396, +8446,8496,8545,8595,8645,8695,8745,8794, +8844,8894,8944,8994,9043,9093,9143,9193, +9243,9292,9342,9392,9442,9491,9541,9591, +9640,9690,9740,9790,9839,9889,9939,9988, +10038,10088,10137,10187,10237,10286,10336,10386, +10435,10485,10534,10584,10634,10683,10733,10782, +10832,10882,10931,10981,11030,11080,11129,11179, +11228,11278,11327,11377,11426,11476,11525,11575, +11624,11674,11723,11773,11822,11872,11921,11970, +12020,12069,12119,12168,12218,12267,12316,12366, +12415,12464,12514,12563,12612,12662,12711,12760, +12810,12859,12908,12957,13007,13056,13105,13154, +13204,13253,13302,13351,13401,13450,13499,13548, +13597,13647,13696,13745,13794,13843,13892,13941, +13990,14040,14089,14138,14187,14236,14285,14334, +14383,14432,14481,14530,14579,14628,14677,14726, +14775,14824,14873,14922,14971,15020,15069,15118, +15167,15215,15264,15313,15362,15411,15460,15509, +15557,15606,15655,15704,15753,15802,15850,15899, +15948,15997,16045,16094,16143,16191,16240,16289, +16338,16386,16435,16484,16532,16581,16629,16678, +16727,16775,16824,16872,16921,16970,17018,17067, +17115,17164,17212,17261,17309,17358,17406,17455, +17503,17551,17600,17648,17697,17745,17793,17842, +17890,17939,17987,18035,18084,18132,18180,18228, +18277,18325,18373,18421,18470,18518,18566,18614, +18663,18711,18759,18807,18855,18903,18951,19000, +19048,19096,19144,19192,19240,19288,19336,19384, +19432,19480,19528,19576,19624,19672,19720,19768, +19816,19864,19912,19959,20007,20055,20103,20151, +20199,20246,20294,20342,20390,20438,20485,20533, +20581,20629,20676,20724,20772,20819,20867,20915, +20962,21010,21057,21105,21153,21200,21248,21295, +21343,21390,21438,21485,21533,21580,21628,21675, +21723,21770,21817,21865,21912,21960,22007,22054, +22102,22149,22196,22243,22291,22338,22385,22432, +22480,22527,22574,22621,22668,22716,22763,22810, +22857,22904,22951,22998,23045,23092,23139,23186, +23233,23280,23327,23374,23421,23468,23515,23562, +23609,23656,23703,23750,23796,23843,23890,23937, +23984,24030,24077,24124,24171,24217,24264,24311, +24357,24404,24451,24497,24544,24591,24637,24684, +24730,24777,24823,24870,24916,24963,25009,25056, +25102,25149,25195,25241,25288,25334,25381,25427, +25473,25520,25566,25612,25658,25705,25751,25797, +25843,25889,25936,25982,26028,26074,26120,26166, +26212,26258,26304,26350,26396,26442,26488,26534, +26580,26626,26672,26718,26764,26810,26856,26902, +26947,26993,27039,27085,27131,27176,27222,27268, +27313,27359,27405,27450,27496,27542,27587,27633, +27678,27724,27770,27815,27861,27906,27952,27997, +28042,28088,28133,28179,28224,28269,28315,28360, +28405,28451,28496,28541,28586,28632,28677,28722, +28767,28812,28858,28903,28948,28993,29038,29083, +29128,29173,29218,29263,29308,29353,29398,29443, +29488,29533,29577,29622,29667,29712,29757,29801, +29846,29891,29936,29980,30025,30070,30114,30159, +30204,30248,30293,30337,30382,30427,30471,30516, +30560,30604,30649,30693,30738,30782,30826,30871, +30915,30959,31004,31048,31092,31136,31181,31225, +31269,31313,31357,31402,31446,31490,31534,31578, +31622,31666,31710,31754,31798,31842,31886,31930, +31974,32017,32061,32105,32149,32193,32236,32280, +32324,32368,32411,32455,32499,32542,32586,32630, +32673,32717,32760,32804,32847,32891,32934,32978, +33021,33065,33108,33151,33195,33238,33281,33325, +33368,33411,33454,33498,33541,33584,33627,33670, +33713,33756,33799,33843,33886,33929,33972,34015, +34057,34100,34143,34186,34229,34272,34315,34358, +34400,34443,34486,34529,34571,34614,34657,34699, +34742,34785,34827,34870,34912,34955,34997,35040, +35082,35125,35167,35210,35252,35294,35337,35379, +35421,35464,35506,35548,35590,35633,35675,35717, +35759,35801,35843,35885,35927,35969,36011,36053, +36095,36137,36179,36221,36263,36305,36347,36388, +36430,36472,36514,36556,36597,36639,36681,36722, +36764,36805,36847,36889,36930,36972,37013,37055, +37096,37137,37179,37220,37262,37303,37344,37386, +37427,37468,37509,37551,37592,37633,37674,37715, +37756,37797,37838,37879,37920,37961,38002,38043, +38084,38125,38166,38207,38248,38288,38329,38370, +38411,38451,38492,38533,38573,38614,38655,38695, +38736,38776,38817,38857,38898,38938,38979,39019, +39059,39100,39140,39180,39221,39261,39301,39341, +39382,39422,39462,39502,39542,39582,39622,39662, +39702,39742,39782,39822,39862,39902,39942,39982, +40021,40061,40101,40141,40180,40220,40260,40299, +40339,40379,40418,40458,40497,40537,40576,40616, +40655,40695,40734,40773,40813,40852,40891,40931, +40970,41009,41048,41087,41127,41166,41205,41244, +41283,41322,41361,41400,41439,41478,41517,41556, +41595,41633,41672,41711,41750,41788,41827,41866, +41904,41943,41982,42020,42059,42097,42136,42174, +42213,42251,42290,42328,42366,42405,42443,42481, +42520,42558,42596,42634,42672,42711,42749,42787, +42825,42863,42901,42939,42977,43015,43053,43091, +43128,43166,43204,43242,43280,43317,43355,43393, +43430,43468,43506,43543,43581,43618,43656,43693, +43731,43768,43806,43843,43880,43918,43955,43992, +44029,44067,44104,44141,44178,44215,44252,44289, +44326,44363,44400,44437,44474,44511,44548,44585, +44622,44659,44695,44732,44769,44806,44842,44879, +44915,44952,44989,45025,45062,45098,45135,45171, +45207,45244,45280,45316,45353,45389,45425,45462, +45498,45534,45570,45606,45642,45678,45714,45750, +45786,45822,45858,45894,45930,45966,46002,46037, +46073,46109,46145,46180,46216,46252,46287,46323, +46358,46394,46429,46465,46500,46536,46571,46606, +46642,46677,46712,46747,46783,46818,46853,46888, +46923,46958,46993,47028,47063,47098,47133,47168, +47203,47238,47273,47308,47342,47377,47412,47446, +47481,47516,47550,47585,47619,47654,47688,47723, +47757,47792,47826,47861,47895,47929,47963,47998, +48032,48066,48100,48134,48168,48202,48237,48271, +48305,48338,48372,48406,48440,48474,48508,48542, +48575,48609,48643,48676,48710,48744,48777,48811, +48844,48878,48911,48945,48978,49012,49045,49078, +49112,49145,49178,49211,49244,49278,49311,49344, +49377,49410,49443,49476,49509,49542,49575,49608, +49640,49673,49706,49739,49771,49804,49837,49869, +49902,49935,49967,50000,50032,50064,50097,50129, +50162,50194,50226,50259,50291,50323,50355,50387, +50420,50452,50484,50516,50548,50580,50612,50644, +50675,50707,50739,50771,50803,50834,50866,50898, +50929,50961,50993,51024,51056,51087,51119,51150, +51182,51213,51244,51276,51307,51338,51369,51401, +51432,51463,51494,51525,51556,51587,51618,51649, +51680,51711,51742,51773,51803,51834,51865,51896, +51926,51957,51988,52018,52049,52079,52110,52140, +52171,52201,52231,52262,52292,52322,52353,52383, +52413,52443,52473,52503,52534,52564,52594,52624, +52653,52683,52713,52743,52773,52803,52832,52862, +52892,52922,52951,52981,53010,53040,53069,53099, +53128,53158,53187,53216,53246,53275,53304,53334, +53363,53392,53421,53450,53479,53508,53537,53566, +53595,53624,53653,53682,53711,53739,53768,53797, +53826,53854,53883,53912,53940,53969,53997,54026, +54054,54082,54111,54139,54167,54196,54224,54252, +54280,54309,54337,54365,54393,54421,54449,54477, +54505,54533,54560,54588,54616,54644,54672,54699, +54727,54755,54782,54810,54837,54865,54892,54920, +54947,54974,55002,55029,55056,55084,55111,55138, +55165,55192,55219,55246,55274,55300,55327,55354, +55381,55408,55435,55462,55489,55515,55542,55569, +55595,55622,55648,55675,55701,55728,55754,55781, +55807,55833,55860,55886,55912,55938,55965,55991, +56017,56043,56069,56095,56121,56147,56173,56199, +56225,56250,56276,56302,56328,56353,56379,56404, +56430,56456,56481,56507,56532,56557,56583,56608, +56633,56659,56684,56709,56734,56760,56785,56810, +56835,56860,56885,56910,56935,56959,56984,57009, +57034,57059,57083,57108,57133,57157,57182,57206, +57231,57255,57280,57304,57329,57353,57377,57402, +57426,57450,57474,57498,57522,57546,57570,57594, +57618,57642,57666,57690,57714,57738,57762,57785, +57809,57833,57856,57880,57903,57927,57950,57974, +57997,58021,58044,58067,58091,58114,58137,58160, +58183,58207,58230,58253,58276,58299,58322,58345, +58367,58390,58413,58436,58459,58481,58504,58527, +58549,58572,58594,58617,58639,58662,58684,58706, +58729,58751,58773,58795,58818,58840,58862,58884, +58906,58928,58950,58972,58994,59016,59038,59059, +59081,59103,59125,59146,59168,59190,59211,59233, +59254,59276,59297,59318,59340,59361,59382,59404, +59425,59446,59467,59488,59509,59530,59551,59572, +59593,59614,59635,59656,59677,59697,59718,59739, +59759,59780,59801,59821,59842,59862,59883,59903, +59923,59944,59964,59984,60004,60025,60045,60065, +60085,60105,60125,60145,60165,60185,60205,60225, +60244,60264,60284,60304,60323,60343,60363,60382, +60402,60421,60441,60460,60479,60499,60518,60537, +60556,60576,60595,60614,60633,60652,60671,60690, +60709,60728,60747,60766,60785,60803,60822,60841, +60859,60878,60897,60915,60934,60952,60971,60989, +61007,61026,61044,61062,61081,61099,61117,61135, +61153,61171,61189,61207,61225,61243,61261,61279, +61297,61314,61332,61350,61367,61385,61403,61420, +61438,61455,61473,61490,61507,61525,61542,61559, +61577,61594,61611,61628,61645,61662,61679,61696, +61713,61730,61747,61764,61780,61797,61814,61831, +61847,61864,61880,61897,61913,61930,61946,61963, +61979,61995,62012,62028,62044,62060,62076,62092, +62108,62125,62141,62156,62172,62188,62204,62220, +62236,62251,62267,62283,62298,62314,62329,62345, +62360,62376,62391,62407,62422,62437,62453,62468, +62483,62498,62513,62528,62543,62558,62573,62588, +62603,62618,62633,62648,62662,62677,62692,62706, +62721,62735,62750,62764,62779,62793,62808,62822, +62836,62850,62865,62879,62893,62907,62921,62935, +62949,62963,62977,62991,63005,63019,63032,63046, +63060,63074,63087,63101,63114,63128,63141,63155, +63168,63182,63195,63208,63221,63235,63248,63261, +63274,63287,63300,63313,63326,63339,63352,63365, +63378,63390,63403,63416,63429,63441,63454,63466, +63479,63491,63504,63516,63528,63541,63553,63565, +63578,63590,63602,63614,63626,63638,63650,63662, +63674,63686,63698,63709,63721,63733,63745,63756, +63768,63779,63791,63803,63814,63825,63837,63848, +63859,63871,63882,63893,63904,63915,63927,63938, +63949,63960,63971,63981,63992,64003,64014,64025, +64035,64046,64057,64067,64078,64088,64099,64109, +64120,64130,64140,64151,64161,64171,64181,64192, +64202,64212,64222,64232,64242,64252,64261,64271, +64281,64291,64301,64310,64320,64330,64339,64349, +64358,64368,64377,64387,64396,64405,64414,64424, +64433,64442,64451,64460,64469,64478,64487,64496, +64505,64514,64523,64532,64540,64549,64558,64566, +64575,64584,64592,64600,64609,64617,64626,64634, +64642,64651,64659,64667,64675,64683,64691,64699, +64707,64715,64723,64731,64739,64747,64754,64762, +64770,64777,64785,64793,64800,64808,64815,64822, +64830,64837,64844,64852,64859,64866,64873,64880, +64887,64895,64902,64908,64915,64922,64929,64936, +64943,64949,64956,64963,64969,64976,64982,64989, +64995,65002,65008,65015,65021,65027,65033,65040, +65046,65052,65058,65064,65070,65076,65082,65088, +65094,65099,65105,65111,65117,65122,65128,65133, +65139,65144,65150,65155,65161,65166,65171,65177, +65182,65187,65192,65197,65202,65207,65212,65217, +65222,65227,65232,65237,65242,65246,65251,65256, +65260,65265,65270,65274,65279,65283,65287,65292, +65296,65300,65305,65309,65313,65317,65321,65325, +65329,65333,65337,65341,65345,65349,65352,65356, +65360,65363,65367,65371,65374,65378,65381,65385, +65388,65391,65395,65398,65401,65404,65408,65411, +65414,65417,65420,65423,65426,65429,65431,65434, +65437,65440,65442,65445,65448,65450,65453,65455, +65458,65460,65463,65465,65467,65470,65472,65474, +65476,65478,65480,65482,65484,65486,65488,65490, +65492,65494,65496,65497,65499,65501,65502,65504, +65505,65507,65508,65510,65511,65513,65514,65515, +65516,65518,65519,65520,65521,65522,65523,65524, +65525,65526,65527,65527,65528,65529,65530,65530, +65531,65531,65532,65532,65533,65533,65534,65534, +65534,65535,65535,65535,65535,65535,65535,65535 + +}; + +int tantoangle[2049] = { +0,333772,667544,1001315,1335086,1668857,2002626,2336395, +2670163,3003929,3337694,3671457,4005219,4338979,4672736,5006492, +5340245,5673995,6007743,6341488,6675230,7008968,7342704,7676435, +8010164,8343888,8677609,9011325,9345037,9678744,10012447,10346145, +10679838,11013526,11347209,11680887,12014558,12348225,12681885,13015539, +13349187,13682829,14016464,14350092,14683714,15017328,15350936,15684536, +16018129,16351714,16685291,17018860,17352422,17685974,18019518,18353054, +18686582,19020100,19353610,19687110,20020600,20354080,20687552,21021014, +21354466,21687906,22021338,22354758,22688168,23021568,23354956,23688332, +24021698,24355052,24688396,25021726,25355046,25688352,26021648,26354930, +26688200,27021456,27354702,27687932,28021150,28354356,28687548,29020724, +29353888,29687038,30020174,30353296,30686404,31019496,31352574,31685636, +32018684,32351718,32684734,33017736,33350722,33683692,34016648,34349584, +34682508,35015412,35348300,35681172,36014028,36346868,36679688,37012492, +37345276,37678044,38010792,38343524,38676240,39008936,39341612,39674272, +40006912,40339532,40672132,41004716,41337276,41669820,42002344,42334848, +42667332,42999796,43332236,43664660,43997060,44329444,44661800,44994140, +45326456,45658752,45991028,46323280,46655512,46987720,47319908,47652072, +47984212,48316332,48648428,48980500,49312548,49644576,49976580,50308556, +50640512,50972444,51304352,51636236,51968096,52299928,52631740,52963524, +53295284,53627020,53958728,54290412,54622068,54953704,55285308,55616888, +55948444,56279972,56611472,56942948,57274396,57605816,57937212,58268576, +58599916,58931228,59262512,59593768,59924992,60256192,60587364,60918508, +61249620,61580704,61911760,62242788,62573788,62904756,63235692,63566604, +63897480,64228332,64559148,64889940,65220696,65551424,65882120,66212788, +66543420,66874024,67204600,67535136,67865648,68196120,68526568,68856984, +69187360,69517712,69848024,70178304,70508560,70838776,71168960,71499112, +71829224,72159312,72489360,72819376,73149360,73479304,73809216,74139096, +74468936,74798744,75128520,75458264,75787968,76117632,76447264,76776864, +77106424,77435952,77765440,78094888,78424304,78753688,79083032,79412336, +79741608,80070840,80400032,80729192,81058312,81387392,81716432,82045440, +82374408,82703336,83032224,83361080,83689896,84018664,84347400,84676096, +85004760,85333376,85661952,85990488,86318984,86647448,86975864,87304240, +87632576,87960872,88289128,88617344,88945520,89273648,89601736,89929792, +90257792,90585760,90913688,91241568,91569408,91897200,92224960,92552672, +92880336,93207968,93535552,93863088,94190584,94518040,94845448,95172816, +95500136,95827416,96154648,96481832,96808976,97136080,97463136,97790144, +98117112,98444032,98770904,99097736,99424520,99751256,100077944,100404592, +100731192,101057744,101384248,101710712,102037128,102363488,102689808,103016080, +103342312,103668488,103994616,104320696,104646736,104972720,105298656,105624552, +105950392,106276184,106601928,106927624,107253272,107578872,107904416,108229920, +108555368,108880768,109206120,109531416,109856664,110181872,110507016,110832120, +111157168,111482168,111807112,112132008,112456856,112781648,113106392,113431080, +113755720,114080312,114404848,114729328,115053760,115378136,115702464,116026744, +116350960,116675128,116999248,117323312,117647320,117971272,118295176,118619024, +118942816,119266560,119590248,119913880,120237456,120560984,120884456,121207864, +121531224,121854528,122177784,122500976,122824112,123147200,123470224,123793200, +124116120,124438976,124761784,125084528,125407224,125729856,126052432,126374960, +126697424,127019832,127342184,127664472,127986712,128308888,128631008,128953072, +129275080,129597024,129918912,130240744,130562520,130884232,131205888,131527480, +131849016,132170496,132491912,132813272,133134576,133455816,133776992,134098120, +134419184,134740176,135061120,135382000,135702816,136023584,136344272,136664912, +136985488,137306016,137626464,137946864,138267184,138587456,138907664,139227808, +139547904,139867920,140187888,140507776,140827616,141147392,141467104,141786752, +142106336,142425856,142745312,143064720,143384048,143703312,144022512,144341664, +144660736,144979744,145298704,145617584,145936400,146255168,146573856,146892480, +147211040,147529536,147847968,148166336,148484640,148802880,149121056,149439152, +149757200,150075168,150393072,150710912,151028688,151346400,151664048,151981616, +152299136,152616576,152933952,153251264,153568496,153885680,154202784,154519824, +154836784,155153696,155470528,155787296,156104000,156420624,156737200,157053696, +157370112,157686480,158002768,158318976,158635136,158951216,159267232,159583168, +159899040,160214848,160530592,160846256,161161840,161477376,161792832,162108208, +162423520,162738768,163053952,163369040,163684080,163999040,164313936,164628752, +164943504,165258176,165572784,165887312,166201776,166516160,166830480,167144736, +167458912,167773008,168087040,168400992,168714880,169028688,169342432,169656096, +169969696,170283216,170596672,170910032,171223344,171536576,171849728,172162800, +172475808,172788736,173101600,173414384,173727104,174039728,174352288,174664784, +174977200,175289536,175601792,175913984,176226096,176538144,176850096,177161984, +177473792,177785536,178097200,178408784,178720288,179031728,179343088,179654368, +179965568,180276704,180587744,180898720,181209616,181520448,181831184,182141856, +182452448,182762960,183073408,183383760,183694048,184004240,184314368,184624416, +184934400,185244288,185554096,185863840,186173504,186483072,186792576,187102000, +187411344,187720608,188029808,188338912,188647936,188956896,189265760,189574560, +189883264,190191904,190500448,190808928,191117312,191425632,191733872,192042016, +192350096,192658096,192966000,193273840,193581584,193889264,194196848,194504352, +194811792,195119136,195426400,195733584,196040688,196347712,196654656,196961520, +197268304,197574992,197881616,198188144,198494592,198800960,199107248,199413456, +199719584,200025616,200331584,200637456,200943248,201248960,201554576,201860128, +202165584,202470960,202776256,203081456,203386592,203691632,203996592,204301472, +204606256,204910976,205215600,205520144,205824592,206128960,206433248,206737456, +207041584,207345616,207649568,207953424,208257216,208560912,208864512,209168048, +209471488,209774832,210078112,210381296,210684384,210987408,211290336,211593184, +211895936,212198608,212501184,212803680,213106096,213408432,213710672,214012816, +214314880,214616864,214918768,215220576,215522288,215823920,216125472,216426928, +216728304,217029584,217330784,217631904,217932928,218233856,218534704,218835472, +219136144,219436720,219737216,220037632,220337952,220638192,220938336,221238384, +221538352,221838240,222138032,222437728,222737344,223036880,223336304,223635664, +223934912,224234096,224533168,224832160,225131072,225429872,225728608,226027232, +226325776,226624240,226922608,227220880,227519056,227817152,228115168,228413088, +228710912,229008640,229306288,229603840,229901312,230198688,230495968,230793152, +231090256,231387280,231684192,231981024,232277760,232574416,232870960,233167440, +233463808,233760096,234056288,234352384,234648384,234944304,235240128,235535872, +235831504,236127056,236422512,236717888,237013152,237308336,237603424,237898416, +238193328,238488144,238782864,239077488,239372016,239666464,239960816,240255072, +240549232,240843312,241137280,241431168,241724960,242018656,242312256,242605776, +242899200,243192512,243485744,243778896,244071936,244364880,244657744,244950496, +245243168,245535744,245828224,246120608,246412912,246705104,246997216,247289216, +247581136,247872960,248164688,248456320,248747856,249039296,249330640,249621904, +249913056,250204128,250495088,250785968,251076736,251367424,251658016,251948512, +252238912,252529200,252819408,253109520,253399536,253689456,253979280,254269008, +254558640,254848176,255137632,255426976,255716224,256005376,256294432,256583392, +256872256,257161024,257449696,257738272,258026752,258315136,258603424,258891600, +259179696,259467696,259755600,260043392,260331104,260618704,260906224,261193632, +261480960,261768176,262055296,262342320,262629248,262916080,263202816,263489456, +263776000,264062432,264348784,264635024,264921168,265207216,265493168,265779024, +266064784,266350448,266636000,266921472,267206832,267492096,267777264,268062336, +268347312,268632192,268916960,269201632,269486208,269770688,270055072,270339360, +270623552,270907616,271191616,271475488,271759296,272042976,272326560,272610048, +272893440,273176736,273459936,273743040,274026048,274308928,274591744,274874432, +275157024,275439520,275721920,276004224,276286432,276568512,276850528,277132416, +277414240,277695936,277977536,278259040,278540448,278821728,279102944,279384032, +279665056,279945952,280226752,280507456,280788064,281068544,281348960,281629248, +281909472,282189568,282469568,282749440,283029248,283308960,283588544,283868032, +284147424,284426720,284705920,284985024,285264000,285542912,285821696,286100384, +286378976,286657440,286935840,287214112,287492320,287770400,288048384,288326240, +288604032,288881696,289159264,289436768,289714112,289991392,290268576,290545632, +290822592,291099456,291376224,291652896,291929440,292205888,292482272,292758528, +293034656,293310720,293586656,293862496,294138240,294413888,294689440,294964864, +295240192,295515424,295790560,296065600,296340512,296615360,296890080,297164704, +297439200,297713632,297987936,298262144,298536256,298810240,299084160,299357952, +299631648,299905248,300178720,300452128,300725408,300998592,301271680,301544640, +301817536,302090304,302362976,302635520,302908000,303180352,303452608,303724768, +303996800,304268768,304540608,304812320,305083968,305355520,305626944,305898272, +306169472,306440608,306711616,306982528,307253344,307524064,307794656,308065152, +308335552,308605856,308876032,309146112,309416096,309685984,309955744,310225408, +310494976,310764448,311033824,311303072,311572224,311841280,312110208,312379040, +312647776,312916416,313184960,313453376,313721696,313989920,314258016,314526016, +314793920,315061728,315329408,315597024,315864512,316131872,316399168,316666336, +316933408,317200384,317467232,317733984,318000640,318267200,318533632,318799968, +319066208,319332352,319598368,319864288,320130112,320395808,320661408,320926912, +321192320,321457632,321722816,321987904,322252864,322517760,322782528,323047200, +323311744,323576192,323840544,324104800,324368928,324632992,324896928,325160736, +325424448,325688096,325951584,326215008,326478304,326741504,327004608,327267584, +327530464,327793248,328055904,328318496,328580960,328843296,329105568,329367712, +329629760,329891680,330153536,330415264,330676864,330938400,331199808,331461120, +331722304,331983392,332244384,332505280,332766048,333026752,333287296,333547776, +333808128,334068384,334328544,334588576,334848512,335108352,335368064,335627712, +335887200,336146624,336405920,336665120,336924224,337183200,337442112,337700864, +337959552,338218112,338476576,338734944,338993184,339251328,339509376,339767296, +340025120,340282848,340540480,340797984,341055392,341312704,341569888,341826976, +342083968,342340832,342597600,342854272,343110848,343367296,343623648,343879904, +344136032,344392064,344648000,344903808,345159520,345415136,345670656,345926048, +346181344,346436512,346691616,346946592,347201440,347456224,347710880,347965440, +348219872,348474208,348728448,348982592,349236608,349490528,349744320,349998048, +350251648,350505152,350758528,351011808,351264992,351518048,351771040,352023872, +352276640,352529280,352781824,353034272,353286592,353538816,353790944,354042944, +354294880,354546656,354798368,355049952,355301440,355552800,355804096,356055264, +356306304,356557280,356808128,357058848,357309504,357560032,357810464,358060768, +358311008,358561088,358811104,359060992,359310784,359560480,359810048,360059520, +360308896,360558144,360807296,361056352,361305312,361554144,361802880,362051488, +362300032,362548448,362796736,363044960,363293056,363541024,363788928,364036704, +364284384,364531936,364779392,365026752,365274016,365521152,365768192,366015136, +366261952,366508672,366755296,367001792,367248192,367494496,367740704,367986784, +368232768,368478656,368724416,368970080,369215648,369461088,369706432,369951680, +370196800,370441824,370686752,370931584,371176288,371420896,371665408,371909792, +372154080,372398272,372642336,372886304,373130176,373373952,373617600,373861152, +374104608,374347936,374591168,374834304,375077312,375320224,375563040,375805760, +376048352,376290848,376533248,376775520,377017696,377259776,377501728,377743584, +377985344,378227008,378468544,378709984,378951328,379192544,379433664,379674688, +379915584,380156416,380397088,380637696,380878176,381118560,381358848,381599040, +381839104,382079072,382318912,382558656,382798304,383037856,383277280,383516640, +383755840,383994976,384233984,384472896,384711712,384950400,385188992,385427488, +385665888,385904160,386142336,386380384,386618368,386856224,387093984,387331616, +387569152,387806592,388043936,388281152,388518272,388755296,388992224,389229024, +389465728,389702336,389938816,390175200,390411488,390647680,390883744,391119712, +391355584,391591328,391826976,392062528,392297984,392533312,392768544,393003680, +393238720,393473632,393708448,393943168,394177760,394412256,394646656,394880960, +395115136,395349216,395583200,395817088,396050848,396284512,396518080,396751520, +396984864,397218112,397451264,397684288,397917248,398150080,398382784,398615424, +398847936,399080320,399312640,399544832,399776928,400008928,400240832,400472608, +400704288,400935872,401167328,401398720,401629984,401861120,402092192,402323136, +402553984,402784736,403015360,403245888,403476320,403706656,403936896,404167008, +404397024,404626944,404856736,405086432,405316032,405545536,405774912,406004224, +406233408,406462464,406691456,406920320,407149088,407377760,407606336,407834784, +408063136,408291392,408519520,408747584,408975520,409203360,409431072,409658720, +409886240,410113664,410340992,410568192,410795296,411022304,411249216,411476032, +411702720,411929312,412155808,412382176,412608480,412834656,413060736,413286720, +413512576,413738336,413964000,414189568,414415040,414640384,414865632,415090784, +415315840,415540800,415765632,415990368,416215008,416439552,416663968,416888288, +417112512,417336640,417560672,417784576,418008384,418232096,418455712,418679200, +418902624,419125920,419349120,419572192,419795200,420018080,420240864,420463552, +420686144,420908608,421130976,421353280,421575424,421797504,422019488,422241344, +422463104,422684768,422906336,423127776,423349120,423570400,423791520,424012576, +424233536,424454368,424675104,424895744,425116288,425336736,425557056,425777280, +425997408,426217440,426437376,426657184,426876928,427096544,427316064,427535488, +427754784,427974016,428193120,428412128,428631040,428849856,429068544,429287168, +429505664,429724064,429942368,430160576,430378656,430596672,430814560,431032352, +431250048,431467616,431685120,431902496,432119808,432336992,432554080,432771040, +432987936,433204736,433421408,433637984,433854464,434070848,434287104,434503296, +434719360,434935360,435151232,435367008,435582656,435798240,436013696,436229088, +436444352,436659520,436874592,437089568,437304416,437519200,437733856,437948416, +438162880,438377248,438591520,438805696,439019744,439233728,439447584,439661344, +439875008,440088576,440302048,440515392,440728672,440941824,441154880,441367872, +441580736,441793472,442006144,442218720,442431168,442643552,442855808,443067968, +443280032,443492000,443703872,443915648,444127296,444338880,444550336,444761696, +444972992,445184160,445395232,445606176,445817056,446027840,446238496,446449088, +446659552,446869920,447080192,447290400,447500448,447710432,447920320,448130112, +448339776,448549376,448758848,448968224,449177536,449386720,449595808,449804800, +450013664,450222464,450431168,450639776,450848256,451056640,451264960,451473152, +451681248,451889248,452097152,452304960,452512672,452720288,452927808,453135232, +453342528,453549760,453756864,453963904,454170816,454377632,454584384,454791008, +454997536,455203968,455410304,455616544,455822688,456028704,456234656,456440512, +456646240,456851904,457057472,457262912,457468256,457673536,457878688,458083744, +458288736,458493600,458698368,458903040,459107616,459312096,459516480,459720768, +459924960,460129056,460333056,460536960,460740736,460944448,461148064,461351584, +461554976,461758304,461961536,462164640,462367680,462570592,462773440,462976160, +463178816,463381344,463583776,463786144,463988384,464190560,464392608,464594560, +464796448,464998208,465199872,465401472,465602944,465804320,466005600,466206816, +466407904,466608896,466809824,467010624,467211328,467411936,467612480,467812896, +468013216,468213440,468413600,468613632,468813568,469013440,469213184,469412832, +469612416,469811872,470011232,470210528,470409696,470608800,470807776,471006688, +471205472,471404192,471602784,471801312,471999712,472198048,472396288,472594400, +472792448,472990400,473188256,473385984,473583648,473781216,473978688,474176064, +474373344,474570528,474767616,474964608,475161504,475358336,475555040,475751648, +475948192,476144608,476340928,476537184,476733312,476929376,477125344,477321184, +477516960,477712640,477908224,478103712,478299104,478494400,478689600,478884704, +479079744,479274656,479469504,479664224,479858880,480053408,480247872,480442240, +480636512,480830656,481024736,481218752,481412640,481606432,481800128,481993760, +482187264,482380704,482574016,482767264,482960416,483153472,483346432,483539296, +483732064,483924768,484117344,484309856,484502240,484694560,484886784,485078912, +485270944,485462880,485654720,485846464,486038144,486229696,486421184,486612576, +486803840,486995040,487186176,487377184,487568096,487758912,487949664,488140320, +488330880,488521312,488711712,488901984,489092160,489282240,489472256,489662176, +489851968,490041696,490231328,490420896,490610336,490799712,490988960,491178144, +491367232,491556224,491745120,491933920,492122656,492311264,492499808,492688256, +492876608,493064864,493253056,493441120,493629120,493817024,494004832,494192544, +494380160,494567712,494755136,494942496,495129760,495316928,495504000,495691008, +495877888,496064704,496251424,496438048,496624608,496811040,496997408,497183680, +497369856,497555936,497741920,497927840,498113632,498299360,498484992,498670560, +498856000,499041376,499226656,499411840,499596928,499781920,499966848,500151680, +500336416,500521056,500705600,500890080,501074464,501258752,501442944,501627040, +501811072,501995008,502178848,502362592,502546240,502729824,502913312,503096704, +503280000,503463232,503646368,503829408,504012352,504195200,504377984,504560672, +504743264,504925760,505108192,505290496,505472736,505654912,505836960,506018944, +506200832,506382624,506564320,506745952,506927488,507108928,507290272,507471552, +507652736,507833824,508014816,508195744,508376576,508557312,508737952,508918528, +509099008,509279392,509459680,509639904,509820032,510000064,510180000,510359872, +510539648,510719328,510898944,511078432,511257856,511437216,511616448,511795616, +511974688,512153664,512332576,512511392,512690112,512868768,513047296,513225792, +513404160,513582432,513760640,513938784,514116800,514294752,514472608,514650368, +514828064,515005664,515183168,515360608,515537952,515715200,515892352,516069440, +516246432,516423328,516600160,516776896,516953536,517130112,517306592,517482976, +517659264,517835488,518011616,518187680,518363648,518539520,518715296,518891008, +519066624,519242144,519417600,519592960,519768256,519943424,520118528,520293568, +520468480,520643328,520818112,520992800,521167392,521341888,521516320,521690656, +521864896,522039072,522213152,522387168,522561056,522734912,522908640,523082304, +523255872,523429376,523602784,523776096,523949312,524122464,524295552,524468512, +524641440,524814240,524986976,525159616,525332192,525504640,525677056,525849344, +526021568,526193728,526365792,526537760,526709632,526881440,527053152,527224800, +527396352,527567840,527739200,527910528,528081728,528252864,528423936,528594880, +528765760,528936576,529107296,529277920,529448480,529618944,529789344,529959648, +530129856,530300000,530470048,530640000,530809888,530979712,531149440,531319072, +531488608,531658080,531827488,531996800,532166016,532335168,532504224,532673184, +532842080,533010912,533179616,533348288,533516832,533685312,533853728,534022048, +534190272,534358432,534526496,534694496,534862400,535030240,535197984,535365632, +535533216,535700704,535868128,536035456,536202720,536369888,536536992,536704000, +536870912 +}; + diff --git a/src/hexen/template.c b/src/hexen/template.c new file mode 100644 index 00000000..358517bc --- /dev/null +++ b/src/hexen/template.c @@ -0,0 +1,32 @@ + +//************************************************************************** +//** +//** TEMPLATE.C +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// +// +//========================================================================== diff --git a/src/hexen/textdefs.h b/src/hexen/textdefs.h new file mode 100644 index 00000000..e583cd24 --- /dev/null +++ b/src/hexen/textdefs.h @@ -0,0 +1,167 @@ + +//************************************************************************** +//** +//** textdefs.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: textdefs.h,v $ +//** $Revision: 1.34 $ +//** $Date: 96/01/05 23:33:22 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// MN_menu.c --------------------------------------------------------------- + +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define TXT_PAUSED "PAUSED" +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +#define QLPROMPT "do you want to quickload the game named"\ + "\n\n'%s'?\n\n"PRESSYN +#define NEWGAME "you can't start a new game\n"\ + "while in a network game.\n\n"PRESSKEY +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define NETEND "you can't end a netgame!\n\n"PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +#define DOSY "(press y to quit to dos.)" +#define TXT_GAMMA_LEVEL_OFF "GAMMA CORRECTION OFF" +#define TXT_GAMMA_LEVEL_1 "GAMMA CORRECTION LEVEL 1" +#define TXT_GAMMA_LEVEL_2 "GAMMA CORRECTION LEVEL 2" +#define TXT_GAMMA_LEVEL_3 "GAMMA CORRECTION LEVEL 3" +#define TXT_GAMMA_LEVEL_4 "GAMMA CORRECTION LEVEL 4" +#define EMPTYSTRING "empty slot" + +// P_inter.c --------------------------------------------------------------- + +// Mana + +#define TXT_MANA_1 "BLUE MANA" +#define TXT_MANA_2 "GREEN MANA" +#define TXT_MANA_BOTH "COMBINED MANA" + +// Keys + +#define TXT_KEY_STEEL "STEEL KEY" +#define TXT_KEY_CAVE "CAVE KEY" +#define TXT_KEY_AXE "AXE KEY" +#define TXT_KEY_FIRE "FIRE KEY" +#define TXT_KEY_EMERALD "EMERALD KEY" +#define TXT_KEY_DUNGEON "DUNGEON KEY" +#define TXT_KEY_SILVER "SILVER KEY" +#define TXT_KEY_RUSTED "RUSTED KEY" +#define TXT_KEY_HORN "HORN KEY" +#define TXT_KEY_SWAMP "SWAMP KEY" +#define TXT_KEY_CASTLE "CASTLE KEY" + +// Artifacts + +#define TXT_ARTIINVULNERABILITY "ICON OF THE DEFENDER" +#define TXT_ARTIHEALTH "QUARTZ FLASK" +#define TXT_ARTISUPERHEALTH "MYSTIC URN" +#define TXT_ARTISUMMON "DARK SERVANT" +#define TXT_ARTITORCH "TORCH" +#define TXT_ARTIEGG "PORKALATOR" +#define TXT_ARTIFLY "WINGS OF WRATH" +#define TXT_ARTITELEPORT "CHAOS DEVICE" +#define TXT_ARTIPOISONBAG "FLECHETTE" +#define TXT_ARTITELEPORTOTHER "BANISHMENT DEVICE" +#define TXT_ARTISPEED "BOOTS OF SPEED" +#define TXT_ARTIBOOSTMANA "KRATER OF MIGHT" +#define TXT_ARTIBOOSTARMOR "DRAGONSKIN BRACERS" +#define TXT_ARTIBLASTRADIUS "DISC OF REPULSION" +#define TXT_ARTIHEALINGRADIUS "MYSTIC AMBIT INCANT" + +// Puzzle artifacts + +#define TXT_ARTIPUZZSKULL "YORICK'S SKULL" +#define TXT_ARTIPUZZGEMBIG "HEART OF D'SPARIL" +#define TXT_ARTIPUZZGEMRED "RUBY PLANET" +#define TXT_ARTIPUZZGEMGREEN1 "EMERALD PLANET" +#define TXT_ARTIPUZZGEMGREEN2 "EMERALD PLANET" +#define TXT_ARTIPUZZGEMBLUE1 "SAPPHIRE PLANET" +#define TXT_ARTIPUZZGEMBLUE2 "SAPPHIRE PLANET" +#define TXT_ARTIPUZZBOOK1 "DAEMON CODEX" +#define TXT_ARTIPUZZBOOK2 "LIBER OSCURA" +#define TXT_ARTIPUZZSKULL2 "FLAME MASK" +#define TXT_ARTIPUZZFWEAPON "GLAIVE SEAL" +#define TXT_ARTIPUZZCWEAPON "HOLY RELIC" +#define TXT_ARTIPUZZMWEAPON "SIGIL OF THE MAGUS" +#define TXT_ARTIPUZZGEAR "CLOCK GEAR" +#define TXT_USEPUZZLEFAILED "YOU CANNOT USE THIS HERE" + +// Items + +#define TXT_ITEMHEALTH "CRYSTAL VIAL" +#define TXT_ITEMBAGOFHOLDING "BAG OF HOLDING" +#define TXT_ITEMSHIELD1 "SILVER SHIELD" +#define TXT_ITEMSHIELD2 "ENCHANTED SHIELD" +#define TXT_ITEMSUPERMAP "MAP SCROLL" +#define TXT_ARMOR1 "MESH ARMOR" +#define TXT_ARMOR2 "FALCON SHIELD" +#define TXT_ARMOR3 "PLATINUM HELMET" +#define TXT_ARMOR4 "AMULET OF WARDING" + +// Weapons + +#define TXT_WEAPON_F2 "TIMON'S AXE" +#define TXT_WEAPON_F3 "HAMMER OF RETRIBUTION" +#define TXT_WEAPON_F4 "QUIETUS ASSEMBLED" +#define TXT_WEAPON_C2 "SERPENT STAFF" +#define TXT_WEAPON_C3 "FIRESTORM" +#define TXT_WEAPON_C4 "WRAITHVERGE ASSEMBLED" +#define TXT_WEAPON_M2 "FROST SHARDS" +#define TXT_WEAPON_M3 "ARC OF DEATH" +#define TXT_WEAPON_M4 "BLOODSCOURGE ASSEMBLED" +#define TXT_QUIETUS_PIECE "SEGMENT OF QUIETUS" +#define TXT_WRAITHVERGE_PIECE "SEGMENT OF WRAITHVERGE" +#define TXT_BLOODSCOURGE_PIECE "SEGMENT OF BLOODSCOURGE" + +// SB_bar.c ---------------------------------------------------------------- + +#define TXT_CHEATGODON "GOD MODE ON" +#define TXT_CHEATGODOFF "GOD MODE OFF" +#define TXT_CHEATNOCLIPON "NO CLIPPING ON" +#define TXT_CHEATNOCLIPOFF "NO CLIPPING OFF" +#define TXT_CHEATWEAPONS "ALL WEAPONS" +#define TXT_CHEATHEALTH "FULL HEALTH" +#define TXT_CHEATKEYS "ALL KEYS" +#define TXT_CHEATSOUNDON "SOUND DEBUG ON" +#define TXT_CHEATSOUNDOFF "SOUND DEBUG OFF" +#define TXT_CHEATTICKERON "TICKER ON" +#define TXT_CHEATTICKEROFF "TICKER OFF" +#define TXT_CHEATARTIFACTS3 "ALL ARTIFACTS" +#define TXT_CHEATARTIFACTSFAIL "BAD INPUT" +#define TXT_CHEATWARP "LEVEL WARP" +#define TXT_CHEATSCREENSHOT "SCREENSHOT" +#define TXT_CHEATIDDQD "TRYING TO CHEAT, EH? NOW YOU DIE!" +#define TXT_CHEATIDKFA "CHEATER - YOU DON'T DESERVE WEAPONS" +#define TXT_CHEATBADINPUT "BAD INPUT" +#define TXT_CHEATNOMAP "CAN'T FIND MAP" + +// G_game.c ---------------------------------------------------------------- + +#define TXT_GAMESAVED "GAME SAVED" + +// M_misc.c ---------------------------------------------------------------- + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +// AM_map.c ---------------------------------------------------------------- + +#define AMSTR_FOLLOWON "FOLLOW MODE ON" +#define AMSTR_FOLLOWOFF "FOLLOW MODE OFF" diff --git a/src/hexen/v_video.c b/src/hexen/v_video.c new file mode 100644 index 00000000..e7306412 --- /dev/null +++ b/src/hexen/v_video.c @@ -0,0 +1,261 @@ + +//************************************************************************** +//** +//** v_video.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: v_video.c,v $ +//** $Revision: 1.2 $ +//** $Date: 95/09/11 14:58:16 $ +//** $Author: cjr $ +//** +//************************************************************************** + +#include "h2def.h" + +#define SC_INDEX 0x3c4 + +byte *screen; +int dirtybox[4]; +int usegamma; + +byte gammatable[5][256] = +{ +{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}, + +{2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,247,248,249,250,251,252,252,253,254,255}, + +{4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,255}, + +{8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,253,253,254,254,255}, + +{16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,254,254,255,255} +}; + +//--------------------------------------------------------------------------- +// +// PROC V_DrawPatch +// +// Draws a column based masked pic to the screen. +// +//--------------------------------------------------------------------------- + +void V_DrawPatch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + byte *desttop; + byte *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + if(x < 0 || x+SHORT(patch->width) > SCREENWIDTH || y < 0 + || y+SHORT(patch->height) > SCREENHEIGHT) + { + I_Error("Bad V_DrawPatch"); + } + col = 0; + desttop = screen+y*SCREENWIDTH+x; + w = SHORT(patch->width); + for(; col < w; x++, col++, desttop++) + { + column = (column_t *)((byte *)patch+LONG(patch->columnofs[col])); + // Step through the posts in a column + while(column->topdelta != 0xff) + { + source = (byte *)column+3; + dest = desttop+column->topdelta*SCREENWIDTH; + count = column->length; + while(count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + column = (column_t *)((byte *)column+column->length+4); + } + } +} + +/* +================== += += V_DrawFuzzPatch += += Masks a column based translucent masked pic to the screen. += +================== +*/ +extern byte *tinttable; + +void V_DrawFuzzPatch (int x, int y, patch_t *patch) +{ + int count,col; + column_t *column; + byte *desttop, *dest, *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x<0||x+SHORT(patch->width) >SCREENWIDTH || y<0 || y+SHORT(patch->height)>SCREENHEIGHT ) + I_Error ("Bad V_DrawPatch"); + + col = 0; + desttop = screen+y*SCREENWIDTH+x; + + w = SHORT(patch->width); + for ( ; colcolumnofs[col])); + +// step through the posts in a column + + while (column->topdelta != 0xff ) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta*SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = tinttable[*dest + ((*source++)<<8)]; + dest += SCREENWIDTH; + } + column = (column_t *)((byte *)column+column->length+4); + } + } +} + +/* +================== += += V_DrawAltFuzzPatch += += Masks a column based translucent masked pic to the screen. += +================== +*/ +extern byte *tinttable; + +void V_DrawAltFuzzPatch (int x, int y, patch_t *patch) +{ + int count,col; + column_t *column; + byte *desttop, *dest, *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x<0||x+SHORT(patch->width) >SCREENWIDTH || y<0 + || y+SHORT(patch->height)>SCREENHEIGHT ) + { + I_Error ("Bad V_DrawPatch"); + } + + col = 0; + desttop = screen+y*SCREENWIDTH+x; + + w = SHORT(patch->width); + for ( ; colcolumnofs[col])); + +// step through the posts in a column + + while (column->topdelta != 0xff ) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta*SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = tinttable[((*dest)<<8) + *source++]; + dest += SCREENWIDTH; + } + column = (column_t *)((byte *)column+column->length+4); + } + } +} + +/* +================== += += V_DrawShadowedPatch += += Masks a column based masked pic to the screen. += +================== +*/ + +void V_DrawShadowedPatch(int x, int y, patch_t *patch) +{ + int count,col; + column_t *column; + byte *desttop, *dest, *source; + byte *desttop2, *dest2; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x<0||x+SHORT(patch->width) >SCREENWIDTH || y<0 || y+SHORT(patch->height)>SCREENHEIGHT ) + I_Error ("Bad V_DrawPatch"); + + col = 0; + desttop = screen+y*SCREENWIDTH+x; + desttop2 = screen+(y+2)*SCREENWIDTH+x+2; + + w = SHORT(patch->width); + for ( ; colcolumnofs[col])); + +// step through the posts in a column + + while (column->topdelta != 0xff ) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta*SCREENWIDTH; + dest2 = desttop2 + column->topdelta*SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest2 = tinttable[((*dest2)<<8)]; + dest2 += SCREENWIDTH; + *dest = *source++; + dest += SCREENWIDTH; + + } + column = (column_t *)( (byte *)column + column->length ++ 4 ); + } + } +} + +//--------------------------------------------------------------------------- +// +// PROC V_DrawRawScreen +// +//--------------------------------------------------------------------------- + +void V_DrawRawScreen(byte *raw) +{ + memcpy(screen, raw, SCREENWIDTH*SCREENHEIGHT); +} + +//--------------------------------------------------------------------------- +// +// PROC V_Init +// +//--------------------------------------------------------------------------- + +void V_Init(void) +{ + // I_AllocLow will put screen in low dos memory on PCs. + screen = I_AllocLow(SCREENWIDTH*SCREENHEIGHT); +} diff --git a/src/hexen/vgaview.h b/src/hexen/vgaview.h new file mode 100644 index 00000000..b880833b --- /dev/null +++ b/src/hexen/vgaview.h @@ -0,0 +1,34 @@ + +//************************************************************************** +//** +//** VGAView.h : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: VGAView.h,v $ +//** $Revision: 1.1 $ +//** $Date: 95/05/11 00:19:48 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#import +#import "h2def.h" + +// a few globals +extern byte *bytebuffer; + + +@interface VGAView:View +{ + id game; + int nextpalette[256]; // color lookup table + int *nextimage; // palette expanded and scaled + unsigned scale; + NXWindowDepth depth; +} + +- updateView; +- (unsigned)scale; +- setPalette:(byte *)pal; +- setScale:(int)newscale; + +@end diff --git a/src/hexen/w_wad.c b/src/hexen/w_wad.c new file mode 100644 index 00000000..50b2ac92 --- /dev/null +++ b/src/hexen/w_wad.c @@ -0,0 +1,596 @@ + +//************************************************************************** +//** +//** w_wad.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: w_wad.c,v $ +//** $Revision: 1.6 $ +//** $Date: 95/10/06 20:56:47 $ +//** $Author: cjr $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#ifdef NeXT +#include +#include +#else +#include +#include +#include +#include +#endif +#include "h2def.h" + +// MACROS ------------------------------------------------------------------ + +#ifdef NeXT +// NeXT doesn't need a binary flag in open call +#define O_BINARY 0 +#define strcmpi strcasecmp +#endif + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + char identification[4]; + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +lumpinfo_t *lumpinfo; +int numlumps; +void **lumpcache; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static lumpinfo_t *PrimaryLumpInfo; +static int PrimaryNumLumps; +static void **PrimaryLumpCache; +static lumpinfo_t *AuxiliaryLumpInfo; +static int AuxiliaryNumLumps; +static void **AuxiliaryLumpCache; +static int AuxiliaryHandle = 0; +boolean AuxiliaryOpened = false; + +// CODE -------------------------------------------------------------------- + +#ifdef NeXT +//========================================================================== +// +// strupr +// +//========================================================================== + +void strupr(char *s) +{ + while(*s) + *s++ = toupper(*s); +} + +//========================================================================== +// +// filelength +// +//========================================================================== + +int filelength(int handle) +{ + struct stat fileinfo; + + if(fstat(handle, &fileinfo) == -1) + { + I_Error("Error fstating"); + } + return fileinfo.st_size; +} +#endif + +//========================================================================== +// +// W_AddFile +// +// Files with a .wad extension are wadlink files with multiple lumps, +// other files are single lumps with the base filename for the lump name. +// +//========================================================================== + +void W_AddFile(char *filename) +{ + wadinfo_t header; + lumpinfo_t *lump_p; + unsigned i; + int handle, length; + int startlump; + filelump_t *fileinfo, singleinfo; + filelump_t *freeFileInfo; + + if((handle = open(filename, O_RDONLY|O_BINARY)) == -1) + { // Didn't find file + return; + } + startlump = numlumps; + if(strcmpi(filename+strlen(filename)-3, "wad")) + { // Single lump file + fileinfo = &singleinfo; + freeFileInfo = NULL; + singleinfo.filepos = 0; + singleinfo.size = LONG(filelength(handle)); + M_ExtractFileBase(filename, singleinfo.name); + numlumps++; + } + else + { // WAD file + read(handle, &header, sizeof(header)); + if(strncmp(header.identification, "IWAD", 4)) + { + if(strncmp(header.identification, "PWAD", 4)) + { // Bad file id + I_Error("Wad file %s doesn't have IWAD or PWAD id\n", + filename); + } + } + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps*sizeof(filelump_t); +// fileinfo = alloca(length); + if(!(fileinfo = malloc(length))) + { + I_Error("W_AddFile: fileinfo malloc failed\n"); + } + freeFileInfo = fileinfo; + lseek(handle, header.infotableofs, SEEK_SET); + read(handle, fileinfo, length); + numlumps += header.numlumps; + } + + // Fill in lumpinfo + lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)); + if(!lumpinfo) + { + I_Error("Couldn't realloc lumpinfo"); + } + lump_p = &lumpinfo[startlump]; + for(i = startlump; i < numlumps; i++, lump_p++, fileinfo++) + { + lump_p->handle = handle; + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + strncpy(lump_p->name, fileinfo->name, 8); + } + if(freeFileInfo) + { + free(freeFileInfo); + } +} + +//========================================================================== +// +// W_InitMultipleFiles +// +// Pass a null terminated list of files to use. All files are optional, +// but at least one file must be found. Lump names can appear multiple +// times. The name searcher looks backwards, so a later file can +// override an earlier one. +// +//========================================================================== + +void W_InitMultipleFiles(char **filenames) +{ + int size; + + // Open all the files, load headers, and count lumps + numlumps = 0; + lumpinfo = malloc(1); // Will be realloced as lumps are added + + for(; *filenames; filenames++) + { + W_AddFile(*filenames); + } + if(!numlumps) + { + I_Error("W_InitMultipleFiles: no files found"); + } + + // Set up caching + size = numlumps*sizeof(*lumpcache); + lumpcache = malloc(size); + if(!lumpcache) + { + I_Error("Couldn't allocate lumpcache"); + } + memset(lumpcache, 0, size); + + PrimaryLumpInfo = lumpinfo; + PrimaryLumpCache = lumpcache; + PrimaryNumLumps = numlumps; +} + +//========================================================================== +// +// W_InitFile +// +// Initialize the primary from a single file. +// +//========================================================================== + +void W_InitFile(char *filename) +{ + char *names[2]; + + names[0] = filename; + names[1] = NULL; + W_InitMultipleFiles(names); +} + +//========================================================================== +// +// W_OpenAuxiliary +// +//========================================================================== + +void W_OpenAuxiliary(char *filename) +{ + int i; + int size; + wadinfo_t header; + int handle; + int length; + filelump_t *fileinfo; + filelump_t *sourceLump; + lumpinfo_t *destLump; + + if(AuxiliaryOpened) + { + W_CloseAuxiliary(); + } + if((handle = open(filename, O_RDONLY|O_BINARY)) == -1) + { + I_Error("W_OpenAuxiliary: %s not found.", filename); + return; + } + AuxiliaryHandle = handle; + read(handle, &header, sizeof(header)); + if(strncmp(header.identification, "IWAD", 4)) + { + if(strncmp(header.identification, "PWAD", 4)) + { // Bad file id + I_Error("Wad file %s doesn't have IWAD or PWAD id\n", + filename); + } + } + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps*sizeof(filelump_t); + fileinfo = Z_Malloc(length, PU_STATIC, 0); + lseek(handle, header.infotableofs, SEEK_SET); + read(handle, fileinfo, length); + numlumps = header.numlumps; + + // Init the auxiliary lumpinfo array + lumpinfo = Z_Malloc(numlumps*sizeof(lumpinfo_t), PU_STATIC, 0); + sourceLump = fileinfo; + destLump = lumpinfo; + for(i = 0; i < numlumps; i++, destLump++, sourceLump++) + { + destLump->handle = handle; + destLump->position = LONG(sourceLump->filepos); + destLump->size = LONG(sourceLump->size); + strncpy(destLump->name, sourceLump->name, 8); + } + Z_Free(fileinfo); + + // Allocate the auxiliary lumpcache array + size = numlumps*sizeof(*lumpcache); + lumpcache = Z_Malloc(size, PU_STATIC, 0); + memset(lumpcache, 0, size); + + AuxiliaryLumpInfo = lumpinfo; + AuxiliaryLumpCache = lumpcache; + AuxiliaryNumLumps = numlumps; + AuxiliaryOpened = true; +} + +//========================================================================== +// +// W_CloseAuxiliary +// +//========================================================================== + +void W_CloseAuxiliary(void) +{ + int i; + + if(AuxiliaryOpened) + { + W_UseAuxiliary(); + for(i = 0; i < numlumps; i++) + { + if(lumpcache[i]) + { + Z_Free(lumpcache[i]); + } + } + Z_Free(AuxiliaryLumpInfo); + Z_Free(AuxiliaryLumpCache); + W_CloseAuxiliaryFile(); + AuxiliaryOpened = false; + } + W_UsePrimary(); +} + +//========================================================================== +// +// W_CloseAuxiliaryFile +// +// WARNING: W_CloseAuxiliary() must be called before any further +// auxiliary lump processing. +// +//========================================================================== + +void W_CloseAuxiliaryFile(void) +{ + if(AuxiliaryHandle) + { + close(AuxiliaryHandle); + AuxiliaryHandle = 0; + } +} + +//========================================================================== +// +// W_UsePrimary +// +//========================================================================== + +void W_UsePrimary(void) +{ + lumpinfo = PrimaryLumpInfo; + numlumps = PrimaryNumLumps; + lumpcache = PrimaryLumpCache; +} + +//========================================================================== +// +// W_UseAuxiliary +// +//========================================================================== + +void W_UseAuxiliary(void) +{ + if(AuxiliaryOpened == false) + { + I_Error("W_UseAuxiliary: WAD not opened."); + } + lumpinfo = AuxiliaryLumpInfo; + numlumps = AuxiliaryNumLumps; + lumpcache = AuxiliaryLumpCache; +} + +//========================================================================== +// +// W_NumLumps +// +//========================================================================== + +int W_NumLumps(void) +{ + return numlumps; +} + +//========================================================================== +// +// W_CheckNumForName +// +// Returns -1 if name not found. +// +//========================================================================== + +int W_CheckNumForName(char *name) +{ + char name8[9]; + int v1, v2; + lumpinfo_t *lump_p; + + // Make the name into two integers for easy compares + strncpy(name8, name, 8); + name8[8] = 0; // in case the name was a full 8 chars + strupr(name8); // case insensitive + v1 = *(int *)name8; + v2 = *(int *)&name8[4]; + + // Scan backwards so patch lump files take precedence + lump_p = lumpinfo+numlumps; + while(lump_p-- != lumpinfo) + { + if(*(int *)lump_p->name == v1 && *(int *)&lump_p->name[4] == v2) + { + return lump_p-lumpinfo; + } + } + return -1; +} + +//========================================================================== +// +// W_GetNumForName +// +// Calls W_CheckNumForName, but bombs out if not found. +// +//========================================================================== + +int W_GetNumForName (char *name) +{ + int i; + + i = W_CheckNumForName(name); + if(i != -1) + { + return i; + } + I_Error("W_GetNumForName: %s not found!", name); + return -1; +} + +//========================================================================== +// +// W_LumpLength +// +// Returns the buffer size needed to load the given lump. +// +//========================================================================== + +int W_LumpLength(int lump) +{ + if(lump >= numlumps) + { + I_Error("W_LumpLength: %i >= numlumps", lump); + } + return lumpinfo[lump].size; +} + +//========================================================================== +// +// W_ReadLump +// +// Loads the lump into the given buffer, which must be >= W_LumpLength(). +// +//========================================================================== + +void W_ReadLump(int lump, void *dest) +{ + int c; + lumpinfo_t *l; + + if(lump >= numlumps) + { + I_Error("W_ReadLump: %i >= numlumps", lump); + } + l = lumpinfo+lump; + //I_BeginRead(); + lseek(l->handle, l->position, SEEK_SET); + c = read(l->handle, dest, l->size); + if(c < l->size) + { + I_Error("W_ReadLump: only read %i of %i on lump %i", + c, l->size, lump); + } + //I_EndRead(); +} + +//========================================================================== +// +// W_CacheLumpNum +// +//========================================================================== + +void *W_CacheLumpNum(int lump, int tag) +{ + byte *ptr; + + if((unsigned)lump >= numlumps) + { + I_Error("W_CacheLumpNum: %i >= numlumps", lump); + } + if(!lumpcache[lump]) + { // Need to read the lump in + ptr = Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]); + W_ReadLump(lump, lumpcache[lump]); + } + else + { + Z_ChangeTag(lumpcache[lump], tag); + } + return lumpcache[lump]; +} + +//========================================================================== +// +// W_CacheLumpName +// +//========================================================================== + +void *W_CacheLumpName(char *name, int tag) +{ + return W_CacheLumpNum(W_GetNumForName(name), tag); +} + +//========================================================================== +// +// W_Profile +// +//========================================================================== + +// Ripped out for Heretic +/* +int info[2500][10]; +int profilecount; + +void W_Profile (void) +{ + int i; + memblock_t *block; + void *ptr; + char ch; + FILE *f; + int j; + char name[9]; + + + for (i=0 ; itag < PU_PURGELEVEL) + ch = 'S'; + else + ch = 'P'; + } + info[i][profilecount] = ch; + } + profilecount++; + + f = fopen ("waddump.txt","w"); + name[8] = 0; + for (i=0 ; i>ML_SPAC_SHIFT) + +// Special activation types +#define SPAC_CROSS 0 // when player crosses line +#define SPAC_USE 1 // when player uses line +#define SPAC_MCROSS 2 // when monster crosses line +#define SPAC_IMPACT 3 // when projectile hits line +#define SPAC_PUSH 4 // when player/monster pushes line +#define SPAC_PCROSS 5 // when projectile crosses line + +typedef struct +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} mapsector_t; + +typedef struct +{ + short numsegs; + short firstseg; // segs are stored sequentially +} mapsubsector_t; + +typedef struct +{ + short v1; + short v2; + short angle; + short linedef; + short side; + short offset; +} mapseg_t; + +enum +{ // bbox coordinates + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; + +#define NF_SUBSECTOR 0x8000 +typedef struct +{ + short x,y,dx,dy; // partition line + short bbox[2][4]; // bounding box for each child + unsigned short children[2]; // if NF_SUBSECTOR its a subsector +} mapnode_t; + +typedef struct +{ + short tid; + short x; + short y; + short height; + short angle; + short type; + short options; + byte special; + byte arg1; + byte arg2; + byte arg3; + byte arg4; + byte arg5; +} mapthing_t; + +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 +#define MTF_AMBUSH 8 +#define MTF_DORMANT 16 +#define MTF_FIGHTER 32 +#define MTF_CLERIC 64 +#define MTF_MAGE 128 +#define MTF_GSINGLE 256 +#define MTF_GCOOP 512 +#define MTF_GDEATHMATCH 1024 + +//-------------------------------------------------------------------------- +// +// Texture definition +// +//-------------------------------------------------------------------------- + +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} mappatch_t; + +typedef struct +{ + char name[8]; + boolean masked; + short width; + short height; + void **columndirectory; // OBSOLETE + short patchcount; + mappatch_t patches[1]; +} maptexture_t; + +//-------------------------------------------------------------------------- +// +// Graphics +// +//-------------------------------------------------------------------------- + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; +// length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + +// a patch holds one or more columns +// patches are used for sprites and all masked pictures +typedef struct +{ + short width; // bounding box size + short height; + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used + // the [0] is &columnofs[width] +} patch_t; + +// a pic is an unmasked block of pixels +typedef struct +{ + byte width,height; + byte data; +} pic_t; + +#endif // __XDDEFS__ diff --git a/src/hexen/z_zone.c b/src/hexen/z_zone.c new file mode 100644 index 00000000..2d7c0b7c --- /dev/null +++ b/src/hexen/z_zone.c @@ -0,0 +1,396 @@ + +//************************************************************************** +//** +//** z_zone.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: z_zone.c,v $ +//** $Revision: 1.2 $ +//** $Date: 96/01/06 03:23:53 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include +#include "h2def.h" + +/* +============================================================================== + + ZONE MEMORY ALLOCATION + +There is never any space between memblocks, and there will never be two +contiguous free memblocks. + +The rover can be left pointing at a non-empty block + +It is of no value to free a cachable block, because it will get overwritten +automatically if needed + +============================================================================== +*/ + +#define ZONEID 0x1d4a11 + + +typedef struct +{ + int size; // total bytes malloced, including header + memblock_t blocklist; // start / end cap for linked list + memblock_t *rover; +} memzone_t; + +memzone_t *mainzone; + +/* +======================== += += Z_ClearZone += +======================== +*/ + +/* +void Z_ClearZone (memzone_t *zone) +{ + memblock_t *block; + +// set the entire zone to one free block + + zone->blocklist.next = zone->blocklist.prev = block = + (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); + zone->blocklist.user = (void *)zone; + zone->blocklist.tag = PU_STATIC; + zone->rover = block; + + block->prev = block->next = &zone->blocklist; + block->user = NULL; // free block + block->size = zone->size - sizeof(memzone_t); +} +*/ + + +/* +======================== += += Z_Init += +======================== +*/ + +void Z_Init (void) +{ + memblock_t *block; + int size; + + mainzone = (memzone_t *)I_ZoneBase (&size); + mainzone->size = size; + +// set the entire zone to one free block + + mainzone->blocklist.next = mainzone->blocklist.prev = block = + (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) ); + mainzone->blocklist.user = (void *)mainzone; + mainzone->blocklist.tag = PU_STATIC; + mainzone->rover = block; + + block->prev = block->next = &mainzone->blocklist; + block->user = NULL; // free block + block->size = mainzone->size - sizeof(memzone_t); +} + + +/* +======================== += += Z_Free += +======================== +*/ + +void Z_Free (void *ptr) +{ + memblock_t *block, *other; + + block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); + if (block->id != ZONEID) + I_Error ("Z_Free: freed a pointer without ZONEID"); + + if (block->user > (void **)0x100) // smaller values are not pointers + *block->user = 0; // clear the user's mark + block->user = NULL; // mark as free + block->tag = 0; + block->id = 0; + + other = block->prev; + if (!other->user) + { // merge with previous free block + other->size += block->size; + other->next = block->next; + other->next->prev = other; + if (block == mainzone->rover) + mainzone->rover = other; + block = other; + } + + other = block->next; + if (!other->user) + { // merge the next free block onto the end + block->size += other->size; + block->next = other->next; + block->next->prev = block; + if (other == mainzone->rover) + mainzone->rover = block; + } +} + + +/* +======================== += += Z_Malloc += += You can pass a NULL user if the tag is < PU_PURGELEVEL +======================== +*/ + +#define MINFRAGMENT 64 + +void *Z_Malloc (int size, int tag, void *user) +{ + int extra; + memblock_t *start, *rover, *new, *base; + +// +// scan through the block list looking for the first free block +// of sufficient size, throwing out any purgable blocks along the way +// + size += sizeof(memblock_t); // account for size of block header + + +// +// if there is a free block behind the rover, back up over them +// + base = mainzone->rover; + if (!base->prev->user) + base = base->prev; + + rover = base; + start = base->prev; + + do + { + if (rover == start) // scaned all the way around the list + I_Error ("Z_Malloc: failed on allocation of %i bytes",size); + if (rover->user) + { + if (rover->tag < PU_PURGELEVEL) + // hit a block that can't be purged, so move base past it + base = rover = rover->next; + else + { + // free the rover block (adding the size to base) + base = base->prev; // the rover can be the base block + Z_Free ((byte *)rover+sizeof(memblock_t)); + base = base->next; + rover = base->next; + } + } + else + rover = rover->next; + } while (base->user || base->size < size); + +// +// found a block big enough +// + extra = base->size - size; + if (extra > MINFRAGMENT) + { // there will be a free fragment after the allocated block + new = (memblock_t *) ((byte *)base + size ); + new->size = extra; + new->user = NULL; // free block + new->tag = 0; + new->prev = base; + new->next = base->next; + new->next->prev = new; + base->next = new; + base->size = size; + } + + if (user) + { + base->user = user; // mark as an in use block + *(void **)user = (void *) ((byte *)base + sizeof(memblock_t)); + } + else + { + if (tag >= PU_PURGELEVEL) + I_Error ("Z_Malloc: an owner is required for purgable blocks"); + base->user = (void *)2; // mark as in use, but unowned + } + base->tag = tag; + + mainzone->rover = base->next; // next allocation will start looking here + + base->id = ZONEID; + return (void *) ((byte *)base + sizeof(memblock_t)); +} + + +/* +======================== += += Z_FreeTags += +======================== +*/ + +void Z_FreeTags (int lowtag, int hightag) +{ + memblock_t *block, *next; + + for (block = mainzone->blocklist.next ; block != &mainzone->blocklist + ; block = next) + { + next = block->next; // get link before freeing + if (!block->user) + continue; // free block + if (block->tag >= lowtag && block->tag <= hightag) + Z_Free ( (byte *)block+sizeof(memblock_t)); + } +} + +/* +======================== += += Z_DumpHeap += +======================== +*/ + +/* +void Z_DumpHeap (int lowtag, int hightag) +{ + memblock_t *block; + + printf ("zone size: %i location: %p\n",mainzone->size,mainzone); + printf ("tag range: %i to %i\n",lowtag, hightag); + + for (block = mainzone->blocklist.next ; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + printf ("block:%p size:%7i user:%p tag:%3i\n", + block, block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + break; // all blocks have been hit + if ( (byte *)block + block->size != (byte *)block->next) + printf ("ERROR: block size does not touch the next block\n"); + if ( block->next->prev != block) + printf ("ERROR: next block doesn't have proper back link\n"); + if (!block->user && !block->next->user) + printf ("ERROR: two consecutive free blocks\n"); + } +} +*/ + +/* +======================== += += Z_FileDumpHeap += +======================== +*/ + +/* +void Z_FileDumpHeap (FILE *f) +{ + memblock_t *block; + + fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone); + + for (block = mainzone->blocklist.next ; ; block = block->next) + { + fprintf (f,"block:%p size:%7i user:%p tag:%3i\n", + block, block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + break; // all blocks have been hit + if ( (byte *)block + block->size != (byte *)block->next) + fprintf (f,"ERROR: block size does not touch the next block\n"); + if ( block->next->prev != block) + fprintf (f,"ERROR: next block doesn't have proper back link\n"); + if (!block->user && !block->next->user) + fprintf (f,"ERROR: two consecutive free blocks\n"); + } +} +*/ + +/* +======================== += += Z_CheckHeap += +======================== +*/ + +void Z_CheckHeap (void) +{ + memblock_t *block; + + for (block = mainzone->blocklist.next ; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + break; // all blocks have been hit + if ( (byte *)block + block->size != (byte *)block->next) + I_Error ("Z_CheckHeap: block size does not touch the next block\n"); + if ( block->next->prev != block) + I_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); + if (!block->user && !block->next->user) + I_Error ("Z_CheckHeap: two consecutive free blocks\n"); + } +} + + +/* +======================== += += Z_ChangeTag += +======================== +*/ + +void Z_ChangeTag2 (void *ptr, int tag) +{ + memblock_t *block; + + block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); + if (block->id != ZONEID) + I_Error ("Z_ChangeTag: freed a pointer without ZONEID"); + if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100) + I_Error ("Z_ChangeTag: an owner is required for purgable blocks"); + block->tag = tag; +} + + +/* +======================== += += Z_FreeMemory += +======================== +*/ + +/* +int Z_FreeMemory (void) +{ + memblock_t *block; + int free; + + free = 0; + for (block = mainzone->blocklist.next ; block != &mainzone->blocklist + ; block = block->next) + if (!block->user || block->tag >= PU_PURGELEVEL) + free += block->size; + return free; +} +*/ -- cgit v1.2.3