// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. // // DESCRIPTION: // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // Line Tag handling. Line and Sector triggers. // //----------------------------------------------------------------------------- #include #include "doomdef.h" #include "doomstat.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "m_argv.h" #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "r_local.h" #include "p_local.h" #include "g_game.h" #include "s_sound.h" // State. #include "r_state.h" // Data. #include "sounds.h" // [STRIFE] #include "hu_stuff.h" #include "p_dialog.h" // // Animating textures and planes // There is another anim_t used in wi_stuff, unrelated. // typedef struct { boolean istexture; int picnum; int basepic; int numpics; int speed; } anim_t; // // source animation definition // typedef struct { int istexture; // if false, it is a flat char endname[9]; char startname[9]; int speed; } animdef_t; // haleyjd 08/30/10: [STRIFE] MAXANIMS raised from 32 to 40 #define MAXANIMS 40 // // P_InitPicAnims // // Floor/ceiling animation sequences, // defined by first and last frame, // i.e. the flat (64x64 tile) name to // be used. // The full animation sequence is given // using all the flats between the start // and end entry, in the order found in // the WAD file. // // haleyjd 08/29/10: [STRIFE] Changed animdefs. // animdef_t animdefs[] = { { false, "F_SCANR8", "F_SCANR5", 4}, { false, "F_WATR03", "F_WATR01", 8}, { false, "F_PWATR3", "F_PWATR1", 11}, { false, "F_SCANR4", "F_SCANR1", 4}, { true, "SCAN08", "SCAN05", 4}, { true, "SWTRMG03", "SWTRMG01", 4}, { true, "SCAN04", "SCAN01", 4}, { true, "COMP04", "COMP01", 4}, { true, "COMP08", "COMP05", 6}, { true, "COMP12", "COMP09", 11}, { true, "COMP16", "COMP13", 12}, { true, "COMP20", "COMP17", 12}, { true, "COMP24", "COMP21", 12}, { true, "COMP28", "COMP25", 12}, { true, "COMP32", "COMP29", 12}, { true, "COMP37", "COMP33", 12}, { true, "COMP41", "COMP38", 12}, { true, "COMP49", "COMP42", 10}, { true, "BRKGRY16", "BRKGRY13", 10}, { true, "BRNSCN04", "BRNSCN01", 10}, { true, "CONCRT12", "CONCRT09", 11}, { true, "CONCRT25", "CONCRT22", 11}, { true, "WALPMP02", "WALPMP01", 16}, { true, "WALTEK17", "WALTEK16", 8}, { true, "FORCE04", "FORCE01", 4}, { true, "FORCE08", "FORCE05", 4}, { true, "FAN02", "FAN01", 4}, { false, "F_VWATR3", "P_VWATR1", 4}, { false, "F_HWATR3", "F_HWATR1", 4}, { false, "F_TELE2", "F_TELE1", 4}, { false, "F_FAN2", "F_FAN1", 4}, { false, "F_CONVY2", "F_CONVY1", 4}, { false, "F_RDALN4", "F_RDALN1", 4}, { -1, "", "", 0}, }; anim_t anims[MAXANIMS]; anim_t* lastanim; // // Animating line specials // // haleyjd 08/29/10: [STRIFE] MAXLINEANIMS raised from 64 to 96 #define MAXLINEANIMS 96 extern short numlinespecials; extern line_t* linespeciallist[MAXLINEANIMS]; void P_InitPicAnims (void) { int i; // Init animation lastanim = anims; for (i=0 ; animdefs[i].istexture != -1 ; i++) { char *startname, *endname; startname = DEH_String(animdefs[i].startname); endname = DEH_String(animdefs[i].endname); if (animdefs[i].istexture) { // different episode ? if (R_CheckTextureNumForName(startname) == -1) continue; lastanim->picnum = R_TextureNumForName(endname); lastanim->basepic = R_TextureNumForName(startname); } else { if (W_CheckNumForName(startname) == -1) continue; lastanim->picnum = R_FlatNumForName(endname); lastanim->basepic = R_FlatNumForName(startname); } lastanim->istexture = animdefs[i].istexture; lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; if (lastanim->numpics < 2) I_Error ("P_InitPicAnims: bad cycle from %s to %s", startname, endname); lastanim->speed = animdefs[i].speed; lastanim++; } } // villsa [STRIFE] terrain type definitions typedef struct { char* flat; int type; int num; } terraintype_t; terraintype_t terraintypes[] = { { "F_WATR03", FLOOR_WATER, -1 }, { "F_WATR02", FLOOR_WATER, -1 }, { "F_WATR01", FLOOR_WATER, -1 }, { "F_VWATR3", FLOOR_WATER, -1 }, { "F_VWATR2", FLOOR_WATER, -1 }, { "P_VWATR1", FLOOR_WATER, -1 }, { "F_HWATR3", FLOOR_WATER, -1 }, { "F_HWATR2", FLOOR_WATER, -1 }, { "F_HWATR1", FLOOR_WATER, -1 }, { "F_PWATR3", FLOOR_SLIME, -1 }, { "F_PWATR2", FLOOR_SLIME, -1 }, { "F_PWATR1", FLOOR_SLIME, -1 }, { "END", FLOOR_END, -1 }, }; // // P_GetTerrainType // villsa [STRIFE] new function // terraintype_e P_GetTerrainType(mobj_t* mobj) { int i = 0; subsector_t* ss = mobj->subsector; if(mobj->z <= ss->sector->floorheight && terraintypes[0].type != FLOOR_END) { while(ss->sector->floorpic != terraintypes[i].num) { if(terraintypes[i+1].type == FLOOR_END) return FLOOR_SOLID; i++; } return terraintypes[i].type; } return FLOOR_SOLID; } // // P_InitTerrainTypes // villsa [STRIFE] new function // Initialize terrain types // void P_InitTerrainTypes(void) { int pic = 0; int i = 0; if(terraintypes[0].type != FLOOR_END) { while(terraintypes[i].type != FLOOR_END) { terraintypes[i].num = R_FlatNumForName(terraintypes[i].flat); i++; } } } // // UTILITIES // // // 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, // it 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; } // // getNextSector() // 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; } // // P_FindLowestFloorSurrounding() // 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; } // // P_FindHighestFloorSurrounding() // 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; } // // P_FindNextHighestFloor // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // Note: this should be doable w/o a fixed array. // Thanks to entryway for the Vanilla overflow emulation. // 20 adjoining sectors max! #define MAX_ADJOINING_SECTORS 20 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[MAX_ADJOINING_SECTORS + 2]; for (i=0, h=0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > height) { // Emulation of memory (stack) overflow if (h == MAX_ADJOINING_SECTORS + 1) { height = other->floorheight; } else if (h == MAX_ADJOINING_SECTORS + 2) { // Fatal overflow: game crashes at 22 textures I_Error("Sector with more than 22 adjoining sectors. " "Vanilla will crash here"); } heightlist[h++] = other->floorheight; } } // Find lowest height in list if (!h) { return currentheight; } min = heightlist[0]; // Range checking? 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 = INT_MAX; 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;itag) 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; } // // EVENTS // Events are operations triggered by using, crossing, // or shooting special lines, or by timed thinkers. // // [STRIFE] static char crosslinestr[90]; // // P_CrossSpecialLine - TRIGGER // Called every time a thing origin is about // to cross a line with a non 0 special. // void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ) { line_t* line; side_t* sidedef; // [STRIFE] int flag; // [STRIFE] int ok; line = &lines[linenum]; // haleyjd 09/21/10: corpses and missiles cannot activate any cross-over // line types, *except* 182 (which is for the sake of missiles). if((thing->flags & (MF_MISSILE|MF_CORPSE)) && line->special != 182) return; // Triggers that other things can activate if (!thing->player) { // Things that should NOT trigger specials... // villsa [STRIFE] unused // haleyjd: removed dead switch. Strife only excludes missiles and // corpses, which is handled above. ok = 0; // [STRIFE] Added several line types. Removed none. switch(line->special) { case 97: // TELEPORT RETRIGGER case 185: // haleyjd: STRIFE-TODO: Identify type case 195: // haleyjd: STRIFE-TODO: Identify type case 231: // haleyjd: STRIFE-TODO: Identify type case 125: // TELEPORT MONSTERONLY TRIGGER case 126: // TELEPORT MONSTERONLY RETRIGGER case 182: // haleyjd: [STRIFE] Break glass - it's a W1 type too! case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER case 39: // TELEPORT TRIGGER case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER case 4: // RAISE DOOR ok = 1; break; } if (!ok) return; } // Note: could use some const's here. switch (line->special) { // // TRIGGERS. // All from here to RETRIGGERS. // case 230: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 2: // Open Door - [STRIFE] Verified unmodified. EV_DoDoor(line,open); line->special = 0; break; case 227: // haleyjd 09/21/10: [STRIFE] W1 Close Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 3: // Close Door - [STRIFE] Verified unmodified. EV_DoDoor(line,close); line->special = 0; break; case 4: // Raise Door - [STRIFE] Verified unmodified. EV_DoDoor(line,normal); line->special = 0; break; case 5: // Raise Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor); line->special = 0; break; case 6: // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,fastCrushAndRaise); line->special = 0; break; case 8: // Build Stairs - [STRIFE] Verified unmodified. EV_BuildStairs(line,build8); line->special = 0; break; case 10: // PlatDownWaitUp - [STRIFE] Verified unmodified. EV_DoPlat(line,downWaitUpStay,0); line->special = 0; break; case 12: // Light Turn On - brightest near - [STRIFE] Verified unmodified. EV_LightTurnOn(line,0); line->special = 0; break; case 13: // Light Turn On 255 - [STRIFE] Verified unmodified. EV_LightTurnOn(line,255); line->special = 0; break; case 16: // Close Door 30 - [STRIFE] Verified unmodified. EV_DoDoor(line,close30ThenOpen); line->special = 0; break; case 17: // Start Light Strobing - [STRIFE] Verified unmodified. EV_StartLightStrobing(line); line->special = 0; break; case 19: // Lower Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerFloor); line->special = 0; break; case 22: // villsa [STRIFE] Verified unmodified. // Raise floor to nearest height and change texture EV_DoPlat(line,raiseToNearestAndChange,0); line->special = 0; break; case 25: // Ceiling Crush and Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,crushAndRaise); line->special = 0; break; case 30: // Raise floor to shortest texture height - [STRIFE] Verified unmodified. // on either side of lines. EV_DoFloor(line,raiseToTexture); line->special = 0; break; case 35: // Lights Very Dark - [STRIFE] Verified unmodified. EV_LightTurnOn(line,35); line->special = 0; break; case 36: // Lower Floor (TURBO) - [STRIFE] Verified unmodified. EV_DoFloor(line,turboLower); line->special = 0; break; case 37: // LowerAndChange - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerAndChange); line->special = 0; break; case 193: // haleyjd 09/21/10: [STRIFE] W1 Floor Lower to Lowest if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t // must have the questflag indicated in the line's y offset if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 38: // Lower Floor To Lowest - [STRIFE] Verified unmodified. EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 39: // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param) EV_Teleport( line, side, thing, TF_NORMAL ); line->special = 0; break; /*case 40: // RaiseCeilingLowerFloor EV_DoCeiling( line, raiseToHighest ); EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break;*/ case 44: // Ceiling Crush - [STRIFE] Verified unmodified. EV_DoCeiling( line, lowerAndCrush ); line->special = 0; break; case 52: // EXIT! - haleyjd 09/21/10: [STRIFE] Exit to level tag/100 G_ExitLevel (line->tag / 100); break; case 53: // Perpetual Platform Raise - [STRIFE] Verified unmodified. EV_DoPlat(line,perpetualRaise,0); line->special = 0; break; case 54: // Platform Stop - [STRIFE] Verified unmodified. EV_StopPlat(line); line->special = 0; break; case 56: // Raise Floor Crush - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorCrush); line->special = 0; break; case 57: // Ceiling Crush Stop - [STRIFE] Verified unmodified. EV_CeilingCrushStop(line); line->special = 0; break; case 58: // [STRIFE] raiseFloor24 was modified into raiseFloor64 // Raise Floor 64 EV_DoFloor(line,raiseFloor64); line->special = 0; break; case 59: // Raise Floor 24 And Change - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor24AndChange); line->special = 0; break; case 104: // Turn lights off in sector(tag) - [STRIFE] Verified unmodified. EV_TurnTagLightsOff(line); line->special = 0; break; case 108: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeRaise); line->special = 0; break; case 109: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeOpen); line->special = 0; break; case 100: // Build Stairs Turbo 16 - [STRIFE] Verified unmodified. EV_BuildStairs(line,turbo16); line->special = 0; break; case 197: // haleyjd 09/21/10: [STRIFE] Blazing Door Close if Has Sigil B if(thing->player->sigiltype <= 0) break; // fall-through: case 110: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeClose); line->special = 0; break; case 119: // Raise floor to nearest surr. floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorToNearest); line->special = 0; break; case 121: // villsa [STRIFE] Verified unmodified. // Blazing PlatDownWaitUpStay EV_DoPlat(line,blazeDWUS,0); line->special = 0; break; case 124: // haleyjd 09/21/10: [STRIFE] W1 Start Finale // Altered from G_SecretExitLevel. G_StartFinale(); break; case 125: // TELEPORT MonsterONLY - [STRIFE] Verified unmodified // (except for 0 flags parameter) if (!thing->player) { EV_Teleport( line, side, thing, TF_NORMAL ); line->special = 0; } break; case 130: // Raise Floor Turbo - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorTurbo); line->special = 0; break; case 141: // Silent Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,silentCrushAndRaise); line->special = 0; break; case 174: // villsa [STRIFE] Split Open EV_DoDoor(line, splitOpen); line->special = 0; break; case 183: // villsa [STRIFE] Split Raise Nearest EV_DoDoor(line, splitRaiseNearest); line->special = 0; break; case 178: // haleyjd 09/24/10: [STRIFE] W1 Build Stairs Down 16 EV_BuildStairs(line, buildDown16); line->special = 0; break; case 179: // haleyjd 09/25/10: [STRIFE] W1 Ceiling Lower to Floor EV_DoCeiling(line, lowerToFloor); line->special = 0; break; case 182: // haleyjd 09/21/10: [STRIFE] Break Glass // 182 is a unique linetype in that it is both a G1 and a W1 linetype, // but only missiles may activate it as a W1 type. if(thing->flags & MF_MISSILE) P_ChangeSwitchTexture(line, 1); // why 1? it will be cleared anyway. break; case 187: // haleyjd 09/21/10: [STRIFE] W1 Clear Force Fields if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t // must have the questflag indicated in the line's y offset if(!(thing->player->questflags & (1 << flag))) break; // Do it! EV_ClearForceFields(line); line->special = 0; break; case 188: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest 16 (Gate Mechanism // Destroyed) if(!(thing->player->questflags & QF_QUEST16)) break; EV_DoDoor(line, open); line->special = 0; break; case 196: // haleyjd 09/26/10: [STRIFE] W1 Floor Lower to Lowest if Sigil Type > 0 if(thing->player->sigiltype > 0) { EV_DoFloor(line, lowerFloorToLowest); line->special = 0; } break; case 200: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Sigil Owned if(!(thing->player->weaponowned[wp_sigil])) break; EV_DoDoor(line, open); line->special = 0; break; case 201: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (First Side Only) if(side == 1) break; // fall-through: case 202: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (Tag = VOC/LOG #) // must be consoleplayer if(thing->player != &players[consoleplayer]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; // load voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag); I_StartVoice(crosslinestr); // load objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message..."); line->special = 0; break; case 210: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Flamethrower???? // I don't think this is actually used anywhere o_O // must be player 1... if(thing->player != &players[0]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; // must have... the flamethrower?! if(!(thing->player->weaponowned[wp_flame])) break; // load voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag); I_StartVoice(crosslinestr); // load objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message from BlackBird..."); line->special = 0; break; case 212: // haleyjd 09/25/10: [STRIFE] W1 Floor Lower to Lowest if Have Flamethrower if(thing->player->weaponowned[wp_flame]) { EV_DoFloor(line, lowerFloorToLowest); line->special = 0; } break; case 215: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Quest (Tag/100, Tag%100) // must be player 1... if(thing->player != &players[0]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; if(line->tag != 0) { // test for questflag if(!(thing->player->questflags & (1 << (line->tag % 100 - 1)))) break; } // start voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag/100); I_StartVoice(crosslinestr); // give objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag/100); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message from BlackBird..."); line->special = 0; break; case 204: // haleyjd 09/21/10: [STRIFE] W1 Change Music (unused!) if(thing->player != &players[0]) break; S_ChangeMusic(line->tag, 1); line->special = 0; break; case 228: // haleyjd 09/21/10: [STRIFE] W1 Entity Voice? if(!(thing->player->questflags & QF_QUEST24)) // Not killed Macil??? break; // STRIFE-TODO: verify... if(!(thing->player->questflags & QF_QUEST28)) // ????? STRIFE-TODO I_StartVoice(DEH_String("voc128")); else I_StartVoice(DEH_String("voc130")); line->special = 0; break; // // RETRIGGERS. All from here till end. // case 72: // Ceiling Crush - [STRIFE] Verified unmodified. EV_DoCeiling( line, lowerAndCrush ); break; case 73: // Ceiling Crush and Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,crushAndRaise); break; case 74: // Ceiling Crush Stop - [STRIFE] Verified unmodified. EV_CeilingCrushStop(line); break; case 75: // Close Door - [STRIFE] Verified unmodified. EV_DoDoor(line,close); break; case 76: // Close Door 30 - [STRIFE] Verified unmodified. EV_DoDoor(line,close30ThenOpen); break; case 77: // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,fastCrushAndRaise); break; case 79: // Lights Very Dark - [STRIFE] Verified unmodified. EV_LightTurnOn(line,35); break; case 80: // Light Turn On - brightest near - [STRIFE] Verified unmodified. EV_LightTurnOn(line,0); break; case 81: // Light Turn On 255 - [STRIFE] Verified unmodified. EV_LightTurnOn(line,255); break; case 82: // Lower Floor To Lowest - [STRIFE] Verified unmodified. EV_DoFloor( line, lowerFloorToLowest ); break; case 83: // Lower Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerFloor); break; case 84: // LowerAndChange - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerAndChange); break; case 86: // Open Door - [STRIFE] Verified unmodified. EV_DoDoor(line,open); break; case 87: // Perpetual Platform Raise - [STRIFE] Verified unmodified. EV_DoPlat(line,perpetualRaise,0); break; case 88: // PlatDownWaitUp - [STRIFE] Verified unmodified. EV_DoPlat(line,downWaitUpStay,0); break; case 89: // Platform Stop - [STRIFE] Verified unmodified. EV_StopPlat(line); break; case 216: // haleyjd 09/21/10: [STRIFE] WR Raise Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t. if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 90: // Raise Door - [STRIFE] Verified unmodified. EV_DoDoor(line,normal); break; case 91: // Raise Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor); break; case 92: // [STRIFE] raiseFloor24 changed to raiseFloor64 // Raise Floor 64 EV_DoFloor(line,raiseFloor64); break; case 93: // Raise Floor 24 And Change - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor24AndChange); break; case 94: // Raise Floor Crush - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorCrush); break; case 95: // villsa [STRIFE] Verified unmodified. // Raise floor to nearest height // and change texture. EV_DoPlat(line,raiseToNearestAndChange,0); break; case 96: // Raise floor to shortest texture height - [STRIFE] Verified unmodified. // on either side of lines. EV_DoFloor(line,raiseToTexture); break; case 97: // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param) EV_Teleport( line, side, thing, TF_NORMAL ); break; case 98: // Lower Floor (TURBO) - [STRIFE] Verified unmodified. EV_DoFloor(line,turboLower); break; case 105: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeRaise); break; case 106: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeOpen); break; case 107: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,blazeClose); break; case 120: // villsa [STRIFE] Verified unmodified. // Blazing PlatDownWaitUpStay. EV_DoPlat(line,blazeDWUS,0); break; case 126: // TELEPORT MonsterONLY. - [STRIFE] Verified unmodified (except for 0 flags param) if (!thing->player) EV_Teleport( line, side, thing, TF_NORMAL ); break; case 128: // Raise To Nearest Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorToNearest); break; case 129: // Raise Floor Turbo - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorTurbo); break; case 186: // haleyjd [STRIFE] Exit Level to Spot, First Side Only if(side == 1) break; // fall-through: case 145: // haleyjd [STRIFE] Exit Level to Spot thing->momx = thing->momy = thing->momz = 0; { int map = line->tag / 100; int spot = line->tag % 100; if(thing->player->weaponowned[wp_sigil]) { if(map == 3) map = 30; else if(map == 7) map = 10; } DEH_snprintf(crosslinestr, sizeof(crosslinestr), "Entering%s", DEH_String(mapnames[map - 1]) + 8); thing->player->message = crosslinestr; if(netgame && deathmatch) { if(levelTimer && levelTimeCount != 0) { DEH_snprintf(crosslinestr, sizeof(crosslinestr), "%d min left", (levelTimeCount/TICRATE)/60); break; } // raise switch from floor EV_DoFloor(line, raiseFloor64); } else { // normal single-player exit // BUG: Here is the opening for a flaming player to cross past // the exit line and hit a deathmatch switch ;) It's not so much // that this is incorrect, as that they forgot to add such a // check to the other kind of exit lines too ;) if(thing->player->health <= 0) break; G_RiftExitLevel(map, spot, thing->angle); } } break; case 175: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if < 16 Above Floor if(thing->z < thing->floorz + 16 * FRACUNIT) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 198: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if No Guard Uniform if(P_PlayerHasItem(thing->player, MT_QUEST_GUARD_UNIFORM)) break; // fall-through: case 150: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 208: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Flamethrower // O_o - this is definitely unused. Was an entire flamethrower quest // cut out of the game before release? if(thing->player->weaponowned[wp_flame]) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 206: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Chalice // This *is* used, inside the Tavern in Tarnhill. Oddly there is also // one just randomly placed outside the entrance to the Power Station. if(P_PlayerHasItem(thing->player, MT_INV_CHALICE)) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 184: // villsa [STRIFE] plat up wait down stay if(EV_DoPlat(line, upWaitDownStay, 0)) P_ChangeSwitchTexture(line, 1); // In P_CrossSpecialLine? Copypasta error? break; case 185: // haleyjd 09/21/10: [STRIFE] Silent Teleport (used for Converter) EV_Teleport(line, side, thing, TF_FULLSILENCE); break; case 195: // haleyjd 09/21/10: [STRIFE] Silent Teleport and Change Zombie EV_Teleport(line, side, thing, TF_FULLSILENCE); P_SetMobjState(thing, S_AGRD_00); // 419 break; case 203: // haleyjd 09/21/10: [STRIFE] WR Change Music if(thing->player != &players[0]) break; S_ChangeMusic(line->tag, 1); break; case 231: // haleyjd 09/21/10: [STRIFE] WR Teleport (Silent at Source) EV_Teleport(line, side, thing, TF_SRCSILENCE); break; // haleyjd 09/21/10: Moved one-time-use lines up above with the others. } } // // P_ShootSpecialLine - IMPACT SPECIALS // Called when a thing shoots a special line. // void P_ShootSpecialLine ( mobj_t* thing, line_t* line ) { int ok; // Impacts that other things can activate. if (!thing->player) { ok = 0; switch(line->special) { case 46: // OPEN DOOR IMPACT case 182: // villsa [STRIFE] for windows ok = 1; break; } if (!ok) return; } switch(line->special) { case 24: // RAISE FLOOR - [STRIFE] Verified unmodified EV_DoFloor(line,raiseFloor); P_ChangeSwitchTexture(line,0); break; case 46: // OPEN DOOR - [STRIFE] Verified unmodified. EV_DoDoor(line,open); P_ChangeSwitchTexture(line,1); break; case 47: // villsa [STRIFE] Verified unmodified. // RAISE FLOOR NEAR AND CHANGE EV_DoPlat(line,raiseToNearestAndChange,0); P_ChangeSwitchTexture(line,0); break; case 180: // haleyjd 09/22/10: [STRIFE] G1 Raise Floor 512 & Change EV_DoFloor(line, raiseFloor512AndChange); P_ChangeSwitchTexture(line, 0); break; case 182: // villsa [STRIFE] G1 Break Glass // haleyjd: note that 182 is also a W1 type in P_CrossSpecialLine, but // can only be activated in that manner by an MF_MISSILE object. P_ChangeSwitchTexture(line, 0); break; } } // // P_PlayerInSpecialSector // Called every tic frame // that the player origin is in a special sector // // [STRIFE] Modified for new sector types and changes to old ones. // void P_PlayerInSpecialSector (player_t* player) { sector_t* sector; sector = player->mo->subsector->sector; // Falling, not all the way down yet? if (player->mo->z != sector->floorheight) return; // Has hitten ground. switch (sector->special) { case 5: // HELLSLIME DAMAGE // [STRIFE] +2 to nukagecount if(!player->powers[pw_ironfeet]) player->nukagecount += 2; break; case 16: // [STRIFE] +4 to nukagecount if(!player->powers[pw_ironfeet]) player->nukagecount += 4; break; case 4: case 7: // [STRIFE] Immediate 5 damage every 31 tics if(!player->powers[pw_ironfeet]) if(!(leveltime & 0x1f)) P_DamageMobj(player->mo, NULL, NULL, 5); break; case 9: // SECRET SECTOR //player->secretcount++; [STRIFE] Don't have a secret count. sector->special = 0; if(player - players == consoleplayer) S_StartSound(NULL, sfx_yeah); break; case 11: // EXIT SUPER DAMAGE! (for E1M8 finale) player->cheats &= ~CF_GODMODE; if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); if (player->health <= 10) G_ExitLevel(0); break; case 15: // haleyjd 08/30/10: [STRIFE] "Instant" Death sector P_DamageMobj(player->mo, NULL, NULL, 999); break; case 18: // haleyjd 08/30/10: [STRIFE] Water current { int tagval = sector->tag - 100; fixed_t force; angle_t angle; if(player->cheats & CF_NOCLIP) return; force = (tagval % 10) << 12; angle = (tagval / 10) << 29; P_Thrust(player, angle, force); } break; default: I_Error ("P_PlayerInSpecialSector: " "unknown special %i", sector->special); break; }; } // // P_UpdateSpecials // Animate planes, scroll walls, etc. // // [STRIFE] Modifications to support multiple scrolling line types. // boolean levelTimer; int levelTimeCount; void P_UpdateSpecials (void) { anim_t* anim; int pic; int i; line_t* line; // LEVEL TIMER if (levelTimer == true) { if(levelTimeCount) // [STRIFE] Does not allow to go negative levelTimeCount--; /* // [STRIFE] Not done here. Exit lines check this manually instead. if (!levelTimeCount) G_ExitLevel(0); */ } // ANIMATE FLATS AND TEXTURES GLOBALLY for (anim = anims ; anim < lastanim ; anim++) { for (i=anim->basepic ; ibasepic+anim->numpics ; i++) { pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); if (anim->istexture) texturetranslation[i] = pic; else flattranslation[i] = pic; } } // ANIMATE LINE SPECIALS for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch(line->special) { case 48: // EFFECT FIRSTCOL SCROLL + sides[line->sidenum[0]].textureoffset += FRACUNIT; break; case 142: // haleyjd 09/25/10 [STRIFE] Scroll Up Slow sides[line->sidenum[0]].rowoffset += FRACUNIT; break; case 143: // haleyjd 09/25/10 [STRIFE] Scroll Down Fast (3 Units/Tic) sides[line->sidenum[0]].rowoffset -= 3*FRACUNIT; break; case 149: // haleyjd 09/25/10 [STRIFE] Scroll Down Slow sides[line->sidenum[0]].rowoffset -= FRACUNIT; break; } } // DO BUTTONS for (i = 0; i < MAXBUTTONS; i++) if (buttonlist[i].btimer) { buttonlist[i].btimer--; if (!buttonlist[i].btimer) { switch(buttonlist[i].where) { case top: sides[buttonlist[i].line->sidenum[0]].toptexture = buttonlist[i].btexture; break; case middle: sides[buttonlist[i].line->sidenum[0]].midtexture = buttonlist[i].btexture; break; case bottom: sides[buttonlist[i].line->sidenum[0]].bottomtexture = buttonlist[i].btexture; break; } S_StartSound(&buttonlist[i].soundorg,sfx_swtchn); memset(&buttonlist[i],0,sizeof(button_t)); } } } // // Donut overrun emulation // // Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) // as usual :-) // #define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 #define DONUT_FLOORPIC_DEFAULT 0x16 static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic, line_t *line, sector_t *pillar_sector) { static int first = 1; static int tmp_s3_floorheight; static int tmp_s3_floorpic; extern int numflats; if (first) { int p; // This is the first time we have had an overrun. first = 0; // Default values tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; //! // @category compat // @arg // // Use the specified magic values when emulating behavior caused // by memory overruns from improperly constructed donuts. // In Vanilla Doom this can differ depending on the operating // system. The default (if this option is not specified) is to // emulate the behavior when running under Windows 98. p = M_CheckParm("-donut"); if (p > 0 && p < myargc - 2) { // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) M_StrToInt(myargv[p + 1], &tmp_s3_floorheight); M_StrToInt(myargv[p + 2], &tmp_s3_floorpic); if (tmp_s3_floorpic >= numflats) { fprintf(stderr, "DonutOverrun: The second parameter for \"-donut\" " "switch should be greater than 0 and less than number " "of flats (%d). Using default value (%d) instead. \n", numflats, DONUT_FLOORPIC_DEFAULT); tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; } } } /* fprintf(stderr, "Linedef: %d; Sector: %d; " "New floor height: %d; New floor pic: %d\n", line->iLineID, pillar_sector->iSectorID, tmp_s3_floorheight >> 16, tmp_s3_floorpic); */ *s3_floorheight = (fixed_t) tmp_s3_floorheight; *s3_floorpic = (short) tmp_s3_floorpic; } // // Special Stuff that can not be categorized // int EV_DoDonut(line_t* line) { sector_t* s1; sector_t* s2; sector_t* s3; int secnum; int rtn; int i; floormove_t* floor; fixed_t s3_floorheight; short s3_floorpic; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { s1 = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1->specialdata) continue; rtn = 1; s2 = getNextSector(s1->lines[0],s1); // Vanilla Doom does not check if the linedef is one sided. The // game does not crash, but reads invalid memory and causes the // sector floor to move "down" to some unknown height. // DOSbox prints a warning about an invalid memory access. // // I'm not sure exactly what invalid memory is being read. This // isn't something that should be done, anyway. // Just print a warning and return. if (s2 == NULL) { fprintf(stderr, "EV_DoDonut: linedef had no second sidedef! " "Unexpected behavior may occur in Vanilla Doom. \n"); break; } for (i = 0; i < s2->linecount; i++) { s3 = s2->lines[i]->backsector; if (s3 == s1) continue; if (s3 == NULL) { // e6y // s3 is NULL, so // s3->floorheight is an int at 0000:0000 // s3->floorpic is a short at 0000:0008 // Trying to emulate fprintf(stderr, "EV_DoDonut: WARNING: emulating buffer overrun due to " "NULL back sector. " "Unexpected behavior may occur in Vanilla Doom.\n"); DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1); } else { s3_floorheight = s3->floorheight; s3_floorpic = s3->floorpic; } // Spawn rising slime floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s2->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = donutRaise; floor->crush = false; floor->direction = 1; floor->sector = s2; floor->speed = FLOORSPEED / 2; floor->texture = s3_floorpic; floor->newspecial = 0; floor->floordestheight = s3_floorheight; // Spawn lowering donut-hole floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s1->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = lowerFloor; floor->crush = false; floor->direction = -1; floor->sector = s1; floor->speed = FLOORSPEED / 2; floor->floordestheight = s3_floorheight; break; } } return rtn; } // // SPECIAL SPAWNING // // // P_SpawnSpecials // After the map has been loaded, scan for specials // that spawn thinkers // short numlinespecials; line_t* linespeciallist[MAXLINEANIMS]; // Parses command line parameters. // // haleyjd 09/25/10: [STRIFE] Modifications for more scrolling line types and // for initialization of sliding door resources. // void P_SpawnSpecials (void) { sector_t* sector; int i; int episode; episode = 1; if (W_CheckNumForName(DEH_String("texture2")) >= 0) episode = 2; // See if -TIMER was specified. if (timelimit > 0) { levelTimer = true; levelTimeCount = timelimit * 60 * TICRATE; } else { levelTimer = false; } // Init special SECTORs - [STRIFE] Verified unmodified. sector = sectors; for (i=0 ; ispecial) continue; switch (sector->special) { 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; case 17: P_SpawnFireFlicker(sector); break; } } // Init line EFFECTs numlinespecials = 0; for (i = 0;i < numlines; i++) { switch(lines[i].special) { case 48: // EFFECT FIRSTCOL SCROLL+ case 142: // [STRIFE] TODO: verify scroll types case 143: case 149: linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; 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)); // villsa [STRIFE] P_InitSlidingDoorFrames(); }