diff options
Diffstat (limited to 'apps/plugins/doom/p_spec.c')
| -rw-r--r-- | apps/plugins/doom/p_spec.c | 3255 |
1 files changed, 3255 insertions, 0 deletions
diff --git a/apps/plugins/doom/p_spec.c b/apps/plugins/doom/p_spec.c new file mode 100644 index 0000000..2876b7f --- /dev/null +++ b/apps/plugins/doom/p_spec.c @@ -0,0 +1,3255 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * + * 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: + * -Loads and initializes texture and flat animation sequences + * -Implements utility functions for all linedef/sector special handlers + * -Dispatches walkover and gun line triggers + * -Initializes and implements special sector types + * -Implements donut linedef triggers + * -Initializes and implements BOOM linedef triggers for + * Scrollers/Conveyors + * Friction + * Wind/Current + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_setup.h" +#include "m_random.h" +#include "d_englsh.h" +#include "m_argv.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "g_game.h" +#include "p_inter.h" +#include "m_swap.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_bbox.h" // phares 3/20/98 +//#include "d_deh.h" +#include "r_plane.h" +#include "i_system.h" +#include "rockmacros.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 +// +// +#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC +#pragma pack(push) +#pragma pack(1) +#endif //_MSC_VER + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + signed char istexture; //jff 3/23/98 make char for comparison // cph - make signed + char endname[9]; // if false, it is a flat + char startname[9]; + int speed; +} PACKEDATTR animdef_t; //jff 3/23/98 pack to read from memory + +#define MAXANIMS 32 // no longer a strict limit -- killough + +static anim_t* lastanim; +static anim_t* anims; // new structure w/o limits -- killough +static size_t maxanims; + +// killough 3/7/98: Initialize generalized scrolling +static void P_SpawnScrollers(void); + +static void P_SpawnFriction(void); // phares 3/16/98 +static void P_SpawnPushers(void); // phares 3/20/98 + +extern int allow_pushers; +extern int variable_friction; // phares 3/20/98 + +// +// P_InitPicAnims +// +// Load the table of animation definitions, checking for existence of +// the start and end of each frame. If the start doesn't exist the sequence +// is skipped, if the last doesn't exist, BOOM exits. +// +// Wall/Flat animation sequences, defined by name of first and last frame, +// The full animation sequence is given using all lumps between the start +// and end entry, in the order found in the WAD file. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called ANIMATED rather than a static table in this module to +// allow wad designers to insert or modify animation sequences. +// +// Lump format is an array of byte packed animdef_t structures, terminated +// by a structure with istexture == -1. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// +void P_InitPicAnims (void) +{ + int i; + const animdef_t *animdefs; //jff 3/23/98 pointer to animation lump + int lump = W_GetNumForName("ANIMATED"); // cph - new wad lump handling + // Init animation + + //jff 3/23/98 read from predefined or wad lump instead of table + animdefs = (const animdef_t *)W_CacheLumpNum(lump); + + lastanim = anims; + for (i=0 ; animdefs[i].istexture != -1 ; i++) + { + // 1/11/98 killough -- removed limit by array-doubling + if (lastanim >= anims + maxanims) + { + size_t newmax = maxanims ? maxanims*2 : MAXANIMS; + anims = realloc(anims, newmax*sizeof(*anims)); // killough + lastanim = anims + maxanims; + maxanims = newmax; + } + + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName (animdefs[i].endname); + lastanim->basepic = R_TextureNumForName (animdefs[i].startname); + } + else + { + if ((W_CheckNumForName)(animdefs[i].startname, ns_flats) == -1) // killough 4/17/98 + continue; + + lastanim->picnum = R_FlatNumForName (animdefs[i].endname); + lastanim->basepic = R_FlatNumForName (animdefs[i].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", + animdefs[i].startname, + animdefs[i].endname); + + lastanim->speed = LONG(animdefs[i].speed); // killough 5/5/98: add LONG() + lastanim++; + } + W_UnlockLumpNum(lump); +} + +/////////////////////////////////////////////////////////////// +// +// Linedef and Sector Special Implementation Utility Functions +// +/////////////////////////////////////////////////////////////// + +// +// getSide() +// +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +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. +// +// Note: if side=1 is specified, it must exist or results undefined +// +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. +// +// modified to return actual two-sidedness rather than presence +// of 2S flag unless compatibility optioned +// +int twoSided +( int sector, + int line ) +{ + //jff 1/26/98 return what is actually needed, whether the line + //has two sidedefs, rather than whether the 2S flag is set + + return comp[comp_model] ? + (sectors[sector].lines[line])->flags & ML_TWOSIDED + : + (sectors[sector].lines[line])->sidenum[1] != -1; +} + + +// +// getNextSector() +// +// Return sector_t * of sector next to current across line. +// +// Note: returns NULL if not two-sided line, or both sides refer to sector +// +sector_t* getNextSector +( line_t* line, + sector_t* sec ) +{ + //jff 1/26/98 check unneeded since line->backsector already + //returns NULL if the line is not two sided, and does so from + //the actual two-sidedness of the line, rather than its 2S flag + + if (comp[comp_model]) + { + if (!(line->flags & ML_TWOSIDED)) + return NULL; + } + + if (line->frontsector == sec) { + if (comp[comp_model] || line->backsector!=sec) + return line->backsector; //jff 5/3/98 don't retn sec unless compatibility + else // fixes an intra-sector line breaking functions + return NULL; // like floor->highest floor + } + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// +// Returns the fixed point value of the lowest floor height +// in the sector passed or its 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() +// +// Passed a sector, returns the fixed point value of the largest +// floor height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// if compatibility then -500*FRACUNIT is the smallest return possible +// +fixed_t P_FindHighestFloorSurrounding(sector_t *sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500*FRACUNIT; + + //jff 1/26/98 Fix initial value for floor to not act differently + //in sections of wad that are below -500 units + if (!comp[comp_model]) /* jff 3/12/98 avoid ovf */ + floor = -32000*FRACUNIT; // in height calculations + + 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() +// +// Passed a sector and a floor height, returns the fixed point value +// of the smallest floor height in a surrounding sector larger than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// Rewritten by Lee Killough to avoid fixed array and to be faster +// +fixed_t P_FindNextHighestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < height && + other->floorheight > currentheight) + height = other->floorheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextLowestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the largest floor height in a surrounding sector smaller than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > height && + other->floorheight < currentheight) + height = other->floorheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextLowestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the largest ceiling height in a surrounding sector smaller than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > height && + other->ceilingheight < currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextHighestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the smallest ceiling height in a surrounding sector larger than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextHighestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < height && + other->ceilingheight > currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindLowestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the smallest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists 32000*FRACUINT is returned +// but if compatibility then INT_MAX is the return +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = INT_MAX; + + /* jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = 32000*FRACUNIT; + + 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; +} + + +// +// P_FindHighestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// but if compatibility then 0 is the smallest return possible +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + /* jff 1/26/98 Fix initial value for floor to not act differently + * in sections of wad that are below 0 units + * jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = -32000*FRACUNIT; + + 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; +} + + +// +// P_FindShortestTextureAround() +// +// Passed a sector number, returns the shortest lower texture on a +// linedef bounding the sector. +// +// Note: If no lower texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 02/03/98 Add routine to find shortest lower texture +// +fixed_t P_FindShortestTextureAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<<FRACBITS; //jff 3/13/98 prevent overflow in height calcs + + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + return minsize; +} + + +// +// P_FindShortestUpperAround() +// +// Passed a sector number, returns the shortest upper texture on a +// linedef bounding the sector. +// +// Note: If no upper texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 03/20/98 Add routine to find shortest upper texture +// +fixed_t P_FindShortestUpperAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<<FRACBITS; //jff 3/13/98 prevent overflow + // in height calcs + for (i = 0; i < sec->linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + side = getSide(secnum,i,1); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + } + } + return minsize; +} + + +// +// P_FindModelFloorSector() +// +// Passed a floor height and a sector number, return a pointer to a +// a sector with that floor height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model floor +// around a sector specified by sector number +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using floormove_t +// +sector_t *P_FindModelFloorSector(fixed_t floordestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecount<linecount? + sec->linecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->floorheight == floordestheight) + return sec; + } + } + return NULL; +} + + +// +// P_FindModelCeilingSector() +// +// Passed a ceiling height and a sector number, return a pointer to a +// a sector with that ceiling height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model ceiling +// around a sector specified by sector number +// used only from generalized ceiling types +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using ceiling_t +// +sector_t *P_FindModelCeilingSector(fixed_t ceildestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecount<linecount? + sec->linecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->ceilingheight == ceildestheight) + return sec; + } + } + return NULL; +} + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// + +// Find the next sector with the same tag as a linedef. +// Rewritten by Lee Killough to use chained hashing to improve speed + +int P_FindSectorFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? sectors[start].nexttag : + sectors[(unsigned) line->tag % (unsigned) numsectors].firsttag; + while (start >= 0 && sectors[start].tag != line->tag) + start = sectors[start].nexttag; + return start; +} + +// killough 4/16/98: Same thing, only for linedefs + +int P_FindLineFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? lines[start].nexttag : + lines[(unsigned) line->tag % (unsigned) numlines].firsttag; + while (start >= 0 && lines[start].tag != line->tag) + start = lines[start].nexttag; + return start; +} + +// Hash the sector tags across the sectors and linedefs. +static void P_InitTagLists(void) +{ + register int i; + + for (i=numsectors; --i>=0; ) // Initially make all slots empty. + sectors[i].firsttag = -1; + for (i=numsectors; --i>=0; ) // Proceed from last to first sector + { // so that lower sectors appear first + int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func + sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain + sectors[j].firsttag = i; + } + + // killough 4/17/98: same thing, only for linedefs + + for (i=numlines; --i>=0; ) // Initially make all slots empty. + lines[i].firsttag = -1; + for (i=numlines; --i>=0; ) // Proceed from last to first linedef + { // so that lower linedefs appear first + int j = (unsigned) lines[i].tag % (unsigned) numlines; // Hash func + lines[i].nexttag = lines[j].firsttag; // Prepend linedef to chain + lines[j].firsttag = i; + } +} + +// +// P_FindMinSurroundingLight() +// +// Passed a sector and a light level, returns the smallest light level +// in a surrounding sector less than that passed. If no smaller light +// level exists, the light level passed is returned. +// +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; +} + + +// +// P_CanUnlockGenDoor() +// +// Passed a generalized locked door linedef and a player, returns whether +// the player has the keys necessary to unlock that door. +// +// Note: The linedef passed MUST be a generalized locked door type +// or results are undefined. +// +// jff 02/05/98 routine added to test for unlockability of +// generalized locked doors +// +boolean P_CanUnlockGenDoor +( line_t* line, + player_t* player) +{ + // does this line special distinguish between skulls and keys? + int skulliscard = (line->special & LockedNKeys)>>LockedNKeysShift; + + // determine for each case of lock type if player's keys are adequate + switch((line->special & LockedKey)>>LockedKeyShift) + { + case AnyKey: + if + ( + !player->cards[it_redcard] && + !player->cards[it_redskull] && + !player->cards[it_bluecard] && + !player->cards[it_blueskull] && + !player->cards[it_yellowcard] && + !player->cards[it_yellowskull] + ) + { + player->message = PD_ANY; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RCard: + if + ( + !player->cards[it_redcard] && + (!skulliscard || !player->cards[it_redskull]) + ) + { + player->message = skulliscard? PD_REDK : PD_REDC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BCard: + if + ( + !player->cards[it_bluecard] && + (!skulliscard || !player->cards[it_blueskull]) + ) + { + player->message = skulliscard? PD_BLUEK : PD_BLUEC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YCard: + if + ( + !player->cards[it_yellowcard] && + (!skulliscard || !player->cards[it_yellowskull]) + ) + { + player->message = skulliscard? PD_YELLOWK : PD_YELLOWC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RSkull: + if + ( + !player->cards[it_redskull] && + (!skulliscard || !player->cards[it_redcard]) + ) + { + player->message = skulliscard? PD_REDK : PD_REDS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BSkull: + if + ( + !player->cards[it_blueskull] && + (!skulliscard || !player->cards[it_bluecard]) + ) + { + player->message = skulliscard? PD_BLUEK : PD_BLUES; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YSkull: + if + ( + !player->cards[it_yellowskull] && + (!skulliscard || !player->cards[it_yellowcard]) + ) + { + player->message = skulliscard? PD_YELLOWK : PD_YELLOWS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case AllKeys: + if + ( + !skulliscard && + ( + !player->cards[it_redcard] || + !player->cards[it_redskull] || + !player->cards[it_bluecard] || + !player->cards[it_blueskull] || + !player->cards[it_yellowcard] || + !player->cards[it_yellowskull] + ) + ) + { + player->message = PD_ALL6; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + if + ( + skulliscard && + ( + (!player->cards[it_redcard] && + !player->cards[it_redskull]) || + (!player->cards[it_bluecard] && + !player->cards[it_blueskull]) || + (!player->cards[it_yellowcard] && + !player->cards[it_yellowskull]) + ) + ) + { + player->message = PD_ALL3; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + } + return true; +} + + +// +// P_SectorActive() +// +// Passed a linedef special class (floor, ceiling, lighting) and a sector +// returns whether the sector is already busy with a linedef special of the +// same class. If old demo compatibility true, all linedef special classes +// are the same. +// +// jff 2/23/98 added to prevent old demos from +// succeeding in starting multiple specials on one sector +// +int P_SectorActive(special_e t,sector_t *sec) +{ + if (demo_compatibility) // return whether any thinker is active + return sec->floordata || sec->ceilingdata || sec->lightingdata; + else + switch (t) // return whether thinker of same type is active + { + case floor_special: + return (int)sec->floordata; + case ceiling_special: + return (int)sec->ceilingdata; + case lighting_special: + return (int)sec->lightingdata; + } + return 1; // don't know which special, must be active, shouldn't be here +} + + +// +// P_CheckTag() +// +// Passed a line, returns true if the tag is non-zero or the line special +// allows no tag without harm. If compatibility, all linedef specials are +// allowed to have zero tag. +// +// Note: Only line specials activated by walkover, pushing, or shooting are +// checked by this routine. +// +// jff 2/27/98 Added to check for zero tag allowed for regular special types +// +int P_CheckTag(line_t *line) +{ + /* tag not zero, allowed, or + * killough 11/98: compatibility option */ + if (comp[comp_zerotags] || line->tag) + return 1; + + switch(line->special) + { + case 1: // Manual door specials + case 26: + case 27: + case 28: + case 31: + case 32: + case 33: + case 34: + case 117: + case 118: + + case 139: // Lighting specials + case 170: + case 79: + case 35: + case 138: + case 171: + case 81: + case 13: + case 192: + case 169: + case 80: + case 12: + case 194: + case 173: + case 157: + case 104: + case 193: + case 172: + case 156: + case 17: + + case 195: // Thing teleporters + case 174: + case 97: + case 39: + case 126: + case 125: + case 210: + case 209: + case 208: + case 207: + + case 11: // Exits + case 52: + case 197: + case 51: + case 124: + case 198: + + case 48: // Scrolling walls + case 85: + return 1; // zero tag allowed + + default: + break; + } + return 0; // zero tag not allowed +} + + +// +// P_IsSecret() +// +// Passed a sector, returns if the sector secret type is still active, i.e. +// secret type is set and the secret has not yet been obtained. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean P_IsSecret(sector_t *sec) +{ + return (sec->special==9 || (sec->special&SECRET_MASK)); +} + + +// +// P_WasSecret() +// +// Passed a sector, returns if the sector secret type is was active, i.e. +// secret type was set and the secret has been obtained already. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean P_WasSecret(sector_t *sec) +{ + return (sec->oldspecial==9 || (sec->oldspecial&SECRET_MASK)); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Events +// +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// +///////////////////////////////////////////////////////////////////////// + +// +// P_CrossSpecialLine - Walkover Trigger Dispatcher +// +// Called every time a thing origin is about +// to cross a line with a non 0 special, whether a walkover type or not. +// +// jff 02/12/98 all W1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be active when the line is +// crossed. Change is qualified by demo_compatibility. +// +// CPhipps - take a line_t pointer instead of a line number, as in MBF +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) +{ + int ok; + + // Things that should never trigger lines + if (!thing->player) + { + // Things that should NOT trigger specials... + switch(thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + } + + //jff 02/04/98 add check here for generalized lindef types + if (!demo_compatibility) // generalized types not recognized if old demo + { + // pointer to line function is NULL by default, set non-null if + // line special is walkover generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //3/2/98 move outside the monster check + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==WalkOnce) || ((line->special&TriggerType)==WalkMany)) + { //jff 4/1/98 check for being a walk type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + + if (linefunc) // if it was a valid generalized type + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case WalkOnce: + if (linefunc(line)) + line->special = 0; // clear special if a walk once type + return; + case WalkMany: + linefunc(line); + return; + default: // if not a walk type, do nothing here + return; + } + } + + if (!thing->player) + { + ok = 0; + switch(line->special) + { + case 39: // teleport trigger + case 97: // teleport retrigger + case 125: // teleport monsteronly trigger + case 126: // teleport monsteronly retrigger + case 4: // raise door + case 10: // plat down-wait-up-stay trigger + case 88: // plat down-wait-up-stay retrigger + //jff 3/5/98 add ability of monsters etc. to use teleporters + case 208: //silent thing teleporters + case 207: + case 243: //silent line-line teleporter + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + case 262: //jff 4/14/98 add monster only + case 263: //jff 4/14/98 silent thing,line,line rev types + case 264: //jff 4/14/98 plus player/monster silent line + case 265: // reversed types + case 266: + case 267: + case 268: + case 269: + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + // Dispatch on the line special value to the line's action routine + // If a once only function, and successful, clear the line special + + switch (line->special) + { + // Regular walk once triggers + + case 2: + // Open Door + if (EV_DoDoor(line,p_open) || demo_compatibility) + line->special = 0; + break; + + case 3: + // Close Door + if (EV_DoDoor(line,p_close) || demo_compatibility) + line->special = 0; + break; + + case 4: + // Raise Door + if (EV_DoDoor(line,normal) || demo_compatibility) + line->special = 0; + break; + + case 5: + // Raise Floor + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + if (EV_DoCeiling(line,fastCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 8: + // Build Stairs + if (EV_BuildStairs(line,build8) || demo_compatibility) + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + if (EV_DoPlat(line,downWaitUpStay,0) || demo_compatibility) + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + if (EV_LightTurnOn(line,0) || demo_compatibility) + line->special = 0; + break; + + case 13: + // Light Turn On 255 + if (EV_LightTurnOn(line,255) || demo_compatibility) + line->special = 0; + break; + + case 16: + // Close Door 30 + if (EV_DoDoor(line,close30ThenOpen) || demo_compatibility) + line->special = 0; + break; + + case 17: + // Start Light Strobing + if (EV_StartLightStrobing(line) || demo_compatibility) + line->special = 0; + break; + + case 19: + // Lower Floor + if (EV_DoFloor(line,lowerFloor) || demo_compatibility) + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + if (EV_DoCeiling(line,crushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + if (EV_DoFloor(line,raiseToTexture) || demo_compatibility) + line->special = 0; + break; + + case 35: + // Lights Very Dark + if (EV_LightTurnOn(line,35) || demo_compatibility) + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + if (EV_DoFloor(line,turboLower) || demo_compatibility) + line->special = 0; + break; + + case 37: + // LowerAndChange + if (EV_DoFloor(line,lowerAndChange) || demo_compatibility) + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + if (EV_DoFloor(line, lowerFloorToLowest) || demo_compatibility) + line->special = 0; + break; + + case 39: + // TELEPORT! //jff 02/09/98 fix using up with wrong side crossing + if (EV_Teleport(line, side, thing) || demo_compatibility) + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + if (demo_compatibility) + { + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); //jff 02/12/98 doesn't work + line->special = 0; + } + else + if (EV_DoCeiling(line, raiseToHighest)) + line->special = 0; + break; + + case 44: + // Ceiling Crush + if (EV_DoCeiling(line, lowerAndCrush) || demo_compatibility) + line->special = 0; + break; + + case 52: + // EXIT! + // killough 10/98: prevent zombies from exiting levels + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_ExitLevel (); + break; + + case 53: + // Perpetual Platform Raise + if (EV_DoPlat(line,perpetualRaise,0) || demo_compatibility) + line->special = 0; + break; + + case 54: + // Platform Stop + if (EV_StopPlat(line) || demo_compatibility) + line->special = 0; + break; + + case 56: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush) || demo_compatibility) + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + if (EV_CeilingCrushStop(line) || demo_compatibility) + line->special = 0; + break; + + case 58: + // Raise Floor 24 + if (EV_DoFloor(line,raiseFloor24) || demo_compatibility) + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + if (EV_DoFloor(line,raiseFloor24AndChange) || demo_compatibility) + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16) || demo_compatibility) + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + if (EV_TurnTagLightsOff(line) || demo_compatibility) + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line,blazeRaise) || demo_compatibility) + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen) || demo_compatibility) + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose) || demo_compatibility) + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + if (EV_DoFloor(line,raiseFloorToNearest) || demo_compatibility) + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0) || demo_compatibility) + line->special = 0; + break; + + case 124: + // Secret EXIT + // killough 10/98: prevent zombies from exiting levels + // CPhipps - change for lxdoom's compatibility handling + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_SecretExitLevel (); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player && + (EV_Teleport(line, side, thing) || demo_compatibility)) + line->special = 0; + break; + + case 130: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo) || demo_compatibility) + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + if (EV_DoCeiling(line,silentCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + // Regular walk many retriggerable + + case 72: + // Ceiling Crush + EV_DoCeiling( line, lowerAndCrush ); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line,crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line,p_close); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line,close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line,fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line,35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line,0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line,255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 83: + // Lower Floor + EV_DoFloor(line,lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line,lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line,p_open); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line,perpetualRaise,0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line,downWaitUpStay,0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line,normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line,raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line,raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line,raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line,raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line,raiseToNearestAndChange,0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line,raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport( line, side, thing ); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line,turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor (line,blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor (line,blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor (line,blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line,blazeDWUS,0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport( line, side, thing ); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line,raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line,raiseFloorTurbo); + break; + + // Extended walk triggers + + // jff 1/29/98 added new linedef types to fill all functions out so that + // all have varieties SR, S1, WR, W1 + + // killough 1/31/98: "factor out" compatibility test, by + // adding inner switch qualified by compatibility flag. + // relax test to demo_compatibility + + // killough 2/16/98: Fix problems with W1 types being cleared too early + + default: + if (!demo_compatibility) + switch (line->special) + { + // Extended walk once triggers + + case 142: + // Raise Floor 512 + // 142 W1 EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + line->special = 0; + break; + + case 143: + // Raise Floor 24 and change + // 143 W1 EV_DoPlat(raiseAndChange,24) + if (EV_DoPlat(line,raiseAndChange,24)) + line->special = 0; + break; + + case 144: + // Raise Floor 32 and change + // 144 W1 EV_DoPlat(raiseAndChange,32) + if (EV_DoPlat(line,raiseAndChange,32)) + line->special = 0; + break; + + case 145: + // Lower Ceiling to Floor + // 145 W1 EV_DoCeiling(lowerToFloor) + if (EV_DoCeiling( line, lowerToFloor )) + line->special = 0; + break; + + case 146: + // Lower Pillar, Raise Donut + // 146 W1 EV_DoDonut() + if (EV_DoDonut(line)) + line->special = 0; + break; + + case 199: + // Lower ceiling to lowest surrounding ceiling + // 199 W1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + line->special = 0; + break; + + case 200: + // Lower ceiling to highest surrounding floor + // 200 W1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + line->special = 0; + break; + + case 207: + // killough 2/16/98: W1 silent teleporter (normal kind) + if (EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 3/16/98 renumber 215->153 + case 153: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trig) + // 153 W1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + line->special = 0; + break; + + case 239: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 239 W1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + line->special = 0; + break; + + case 219: + // Lower floor to next lower neighbor + // 219 W1 Lower Floor Next Lower Neighbor + if (EV_DoFloor(line,lowerFloorToNearest)) + line->special = 0; + break; + + case 227: + // Raise elevator next floor + // 227 W1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + line->special = 0; + break; + + case 231: + // Lower elevator next floor + // 231 W1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + line->special = 0; + break; + + case 235: + // Elevator to current floor + // 235 W1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + line->special = 0; + break; + + case 243: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: W1 silent teleporter (linedef-linedef kind) + if (EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 262: //jff 4/14/98 add silent line-line reversed + if (EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 264: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 266: //jff 4/14/98 add monster-only silent line-line + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 268: //jff 4/14/98 add monster-only silent + if (!thing->player && EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 1/29/98 end of added W1 linedef types + + // Extended walk many retriggerable + + //jff 1/29/98 added new linedef types to fill all functions + //out so that all have varieties SR, S1, WR, W1 + + case 147: + // Raise Floor 512 + // 147 WR EV_DoFloor(raiseFloor512) + EV_DoFloor(line,raiseFloor512); + break; + + case 148: + // Raise Floor 24 and Change + // 148 WR EV_DoPlat(raiseAndChange,24) + EV_DoPlat(line,raiseAndChange,24); + break; + + case 149: + // Raise Floor 32 and Change + // 149 WR EV_DoPlat(raiseAndChange,32) + EV_DoPlat(line,raiseAndChange,32); + break; + + case 150: + // Start slow silent crusher + // 150 WR EV_DoCeiling(silentCrushAndRaise) + EV_DoCeiling(line,silentCrushAndRaise); + break; + + case 151: + // RaiseCeilingLowerFloor + // 151 WR EV_DoCeiling(raiseToHighest), + // EV_DoFloor(lowerFloortoLowest) + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 152: + // Lower Ceiling to Floor + // 152 WR EV_DoCeiling(lowerToFloor) + EV_DoCeiling( line, lowerToFloor ); + break; + + //jff 3/16/98 renumber 153->256 + case 256: + // Build stairs, step 8 + // 256 WR EV_BuildStairs(build8) + EV_BuildStairs(line,build8); + break; + + //jff 3/16/98 renumber 154->257 + case 257: + // Build stairs, step 16 + // 257 WR EV_BuildStairs(turbo16) + EV_BuildStairs(line,turbo16); + break; + + case 155: + // Lower Pillar, Raise Donut + // 155 WR EV_DoDonut() + EV_DoDonut(line); + break; + + case 156: + // Start lights strobing + // 156 WR Lights EV_StartLightStrobing() + EV_StartLightStrobing(line); + break; + + case 157: + // Lights to dimmest near + // 157 WR Lights EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + break; + + case 201: + // Lower ceiling to lowest surrounding ceiling + // 201 WR EV_DoCeiling(lowerToLowest) + EV_DoCeiling(line,lowerToLowest); + break; + + case 202: + // Lower ceiling to highest surrounding floor + // 202 WR EV_DoCeiling(lowerToMaxFloor) + EV_DoCeiling(line,lowerToMaxFloor); + break; + + case 208: + // killough 2/16/98: WR silent teleporter (normal kind) + EV_SilentTeleport(line, side, thing); + break; + + case 212: //jff 3/14/98 create instant toggle floor type + // Toggle floor between C and F instantly + // 212 WR Instant Toggle Floor + EV_DoPlat(line,toggleUpDn,0); + break; + + //jff 3/16/98 renumber 216->154 + case 154: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trigger) + // 154 WR Change Texture/Type Only + EV_DoChange(line,trigChangeOnly); + break; + + case 240: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 240 WR Change Texture/Type Only + EV_DoChange(line,numChangeOnly); + break; + + case 220: + // Lower floor to next lower neighbor + // 220 WR Lower Floor Next Lower Neighbor + EV_DoFloor(line,lowerFloorToNearest); + break; + + case 228: + // Raise elevator next floor + // 228 WR Raise Elevator next floor + EV_DoElevator(line,elevateUp); + break; + + case 232: + // Lower elevator next floor + // 232 WR Lower Elevator next floor + EV_DoElevator(line,elevateDown); + break; + + case 236: + // Elevator to current floor + // 236 WR Elevator to current floor + EV_DoElevator(line,elevateCurrent); + break; + + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: WR silent teleporter (linedef-linedef kind) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 263: //jff 4/14/98 add silent line-line reversed + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 265: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 267: //jff 4/14/98 add monster-only silent line-line + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 269: //jff 4/14/98 add monster-only silent + if (!thing->player) + EV_SilentTeleport(line, side, thing); + break; + + //jff 1/29/98 end of added WR linedef types + } + break; + } +} + +// +// P_ShootSpecialLine - Gun trigger special dispatcher +// +// Called when a thing shoots a special line with bullet, shell, saw, or fist. +// +// jff 02/12/98 all G1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be in motion when the line is +// impacted. Change is qualified by demo_compatibility. +// +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ) +{ + //jff 02/04/98 add check here for generalized linedef + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is gun triggered generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //jff 3/2/98 all gun generalized types require tag + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==GunOnce) || ((line->special&TriggerType)==GunMany)) + { //jff 4/1/98 check for being a gun type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case GunOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return; + case GunMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return; + default: // if not a gun type, do nothing here + return; + } + } + + // Impacts that other things can activate. + if (!thing->player) + { + int ok = 0; + switch(line->special) + { + case 46: + // 46 GR Open door on impact weapon is monster activatable + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + switch(line->special) + { + case 24: + // 24 G1 raise floor to highest adjacent + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + case 46: + // 46 GR open door, stay open + EV_DoDoor(line,p_open); + P_ChangeSwitchTexture(line,1); + break; + + case 47: + // 47 G1 raise floor to nearest and change texture and type + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + //jff 1/30/98 added new gun linedefs here + // killough 1/31/98: added demo_compatibility check, added inner switch + + default: + if (!demo_compatibility) + switch (line->special) + { + case 197: + // Exit to next level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_ExitLevel(); + break; + + case 198: + // Exit to secret level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel(); + break; + //jff end addition of new gun linedefs + } + break; + } +} + + +// +// P_PlayerInSpecialSector() +// +// Called every tick frame +// that the player origin is in a special sector +// +// Changed to ignore sector types the engine does not recognize +// +void P_PlayerInSpecialSector (player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + // Sector specials don't apply in mid-air + if (player->mo->z != sector->floorheight) + return; + + // Has hit ground. + //jff add if to handle old vs generalized types + if (sector->special<32) // regular sector specials + { + switch (sector->special) + { + case 5: + // 5/10 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + + case 7: + // 2/5 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + + case 16: + // 10/20 unit damage per 31 ticks + case 4: + // 10/20 unit damage plus blinking light (light already spawned) + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5) ) // even with suit, take damage + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + + case 9: + // Tally player in secret sector, clear secret special + player->secretcount++; + sector->special = 0; + break; + + case 11: + // Exit on health < 11, take 10/20 damage per 31 ticks + if (comp[comp_god]) /* killough 2/21/98: add compatibility switch */ + player->cheats &= ~CF_GODMODE; // on godmode cheat clearing + // does not affect invulnerability + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + //jff 1/24/98 Don't exit as DOOM2 did, just ignore + break; + }; + } + else //jff 3/14/98 handle extended sector types for secrets and damage + { + switch ((sector->special&DAMAGE_MASK)>>DAMAGE_SHIFT) + { + case 0: // no damage + break; + case 1: // 2/5 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + case 2: // 5/10 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + case 3: // 10/20 damage per 31 ticks + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5)) // take damage even with suit + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + } + if (sector->special&SECRET_MASK) + { + player->secretcount++; + sector->special &= ~SECRET_MASK; + if (sector->special<32) // if all extended bits clear, + sector->special=0; // sector is not special anymore + } + + // phares 3/19/98: + // + // If FRICTION_MASK or PUSH_MASK is set, we don't care at this + // point, since the code to deal with those situations is + // handled by Thinkers. + + } +} + +// +// P_UpdateSpecials() +// +// Check level timer, frag counter, +// animate flats, scroll walls, +// change button textures +// +// Reads and modifies globals: +// levelTimer, levelTimeCount, +// levelFragLimit, levelFragLimitCount +// + +boolean levelTimer; +int levelTimeCount; +boolean levelFragLimit; // Ty 03/18/98 Added -frags support +int levelFragLimitCount; // Ty 03/18/98 Added -frags support + +void P_UpdateSpecials (void) +{ + anim_t* anim; + int pic; + int i; + + // Downcount level timer, exit level if elapsed + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // Check frag counters, if frag limit reached, exit level // Ty 03/18/98 + // Seems like the total frags should be kept in a simple + // array somewhere, but until they are... + if (levelFragLimit == true) // we used -frags so compare count + { + int k,m,fragcount,exitflag=false; + for (k=0;k<MAXPLAYERS;k++) + { + if (!playeringame[k]) continue; + fragcount = 0; + for (m=0;m<MAXPLAYERS;m++) + { + if (!playeringame[m]) continue; + fragcount += (m!=k)? players[k].frags[m] : -players[k].frags[m]; + } + if (fragcount >= levelFragLimitCount) exitflag = true; + if (exitflag == true) break; // skip out of the loop--we're done + } + if (exitflag == true) + G_ExitLevel(); + } + + // Animate flats and textures globally + for (anim = anims ; anim < lastanim ; anim++) + { + for (i=anim->basepic ; i<anim->basepic+anim->numpics ; i++) + { + pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // Check buttons (retriggerable switches) and change texture on timeout + 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((mobj_t *)&buttonlist[i].soundorg,sfx_swtchn); + memset(&buttonlist[i],0,sizeof(button_t)); + } + } +} + +////////////////////////////////////////////////////////////////////// +// +// Sector and Line special thinker spawning at level startup +// +////////////////////////////////////////////////////////////////////// + +// +// P_SpawnSpecials +// After the map has been loaded, +// scan for specials that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials (void) +{ + sector_t* sector; + int i; + int episode; + + episode = 1; + if (W_CheckNumForName("texture2") >= 0) + episode = 2; + + // See if -timer needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); // Austin Virtual Gaming 20 min timer on DM play + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * TICRATE; + } + + i = M_CheckParm("-timer"); // user defined timer on game play + if (i && deathmatch) + { + int time; + time = atoi(myargv[i+1]) * 60 * TICRATE; + levelTimer = true; + levelTimeCount = time; + } + + // See if -frags has been used + levelFragLimit = false; + i = M_CheckParm("-frags"); // Ty 03/18/98 Added -frags support + if (i && deathmatch) + { + int frags; + frags = atoi(myargv[i+1]); + if (frags <= 0) frags = 10; // default 10 if no count provided + levelFragLimit = true; + levelFragLimitCount = frags; + } + + + // Init special sectors. + sector = sectors; + for (i=0 ; i<numsectors ; i++, sector++) + { + if (!sector->special) + continue; + + if (sector->special&SECRET_MASK) //jff 3/15/98 count extended + totalsecret++; // secret sectors too + + switch (sector->special&31) + { + case 1: + // random off + 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 |= 3<<DAMAGE_SHIFT; //jff 3/14/98 put damage bits in + break; + + case 8: + // glowing light + P_SpawnGlowingLight(sector); + break; + case 9: + // secret sector + if (sector->special<32) //jff 3/14/98 bits don't count unless not + totalsecret++; // a generalized sector type + 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: + // fire flickering + P_SpawnFireFlicker(sector); + break; + } + } + + P_RemoveAllActiveCeilings(); // jff 2/22/98 use killough's scheme + + P_RemoveAllActivePlats(); // killough + + for (i = 0;i < MAXBUTTONS;i++) + memset(&buttonlist[i],0,sizeof(button_t)); + + // P_InitTagLists() must be called before P_FindSectorFromLineTag() + // or P_FindLineFromLineTag() can be called. + + P_InitTagLists(); // killough 1/30/98: Create xref tables for tags + + P_SpawnScrollers(); // killough 3/7/98: Add generalized scrollers + + P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs + + P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + + for (i=0; i<numlines; i++) + switch (lines[i].special) + { + int s, sec; + + // killough 3/7/98: + // support for drawn heights coming from different sector + case 242: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].heightsec = sec; + break; + + // killough 3/16/98: Add support for setting + // floor lighting independently (e.g. lava) + case 213: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].floorlightsec = sec; + break; + + // killough 4/11/98: Add support for setting + // ceiling lighting independently + case 261: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].ceilinglightsec = sec; + break; + + // killough 10/98: + // + // Support for sky textures being transferred from sidedefs. + // Allows scrolling and other effects (but if scrolling is + // used, then the same sector tag needs to be used for the + // sky sector, the sky-transfer linedef, and the scroll-effect + // linedef). Still requires user to use F_SKY1 for the floor + // or ceiling texture, to distinguish floor and ceiling sky. + + case 271: // Regular sky + case 272: // Same, only flipped + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].sky = i | PL_SKYFLAT; + break; + } +} + +// killough 2/28/98: +// +// This function, with the help of r_plane.c and r_bsp.c, supports generalized +// scrolling floors and walls, with optional mobj-carrying properties, e.g. +// conveyor belts, rivers, etc. A linedef with a special type affects all +// tagged sectors the same way, by creating scrolling and/or object-carrying +// properties. Multiple linedefs may be used on the same sector and are +// cumulative, although the special case of scrolling a floor and carrying +// things on it, requires only one linedef. The linedef's direction determines +// the scrolling direction, and the linedef's length determines the scrolling +// speed. This was designed so that an edge around the sector could be used to +// control the direction of the sector's scrolling, which is usually what is +// desired. +// +// Process the active scrollers. +// +// This is the main scrolling code +// killough 3/7/98 + +void T_Scroll(scroll_t *s) +{ + fixed_t dx = s->dx, dy = s->dy; + + if (s->control != -1) + { // compute scroll amounts based on a sector's height changes + fixed_t height = sectors[s->control].floorheight + + sectors[s->control].ceilingheight; + fixed_t delta = height - s->last_height; + s->last_height = height; + dx = FixedMul(dx, delta); + dy = FixedMul(dy, delta); + } + + // killough 3/14/98: Add acceleration + if (s->accel) + { + s->vdx = dx += s->vdx; + s->vdy = dy += s->vdy; + } + + if (!(dx | dy)) // no-op if both (x,y) offsets 0 + return; + + switch (s->type) + { + side_t *side; + sector_t *sec; + fixed_t height, waterheight; // killough 4/4/98: add waterheight + msecnode_t *node; + mobj_t *thing; + + case sc_side: // killough 3/7/98: Scroll wall texture + side = sides + s->affectee; + side->textureoffset += dx; + side->rowoffset += dy; + break; + + case sc_floor: // killough 3/7/98: Scroll floor texture + sec = sectors + s->affectee; + sec->floor_xoffs += dx; + sec->floor_yoffs += dy; + break; + + case sc_ceiling: // killough 3/7/98: Scroll ceiling texture + sec = sectors + s->affectee; + sec->ceiling_xoffs += dx; + sec->ceiling_yoffs += dy; + break; + + case sc_carry: + + // killough 3/7/98: Carry things on floor + // killough 3/20/98: use new sector list which reflects true members + // killough 3/27/98: fix carrier bug + // killough 4/4/98: Underwater, carry things even w/o gravity + + sec = sectors + s->affectee; + height = sec->floorheight; + waterheight = sec->heightsec != -1 && + sectors[sec->heightsec].floorheight > height ? + sectors[sec->heightsec].floorheight : INT_MIN; + + for (node = sec->touching_thinglist; node; node = node->m_snext) + if (!((thing = node->m_thing)->flags & MF_NOCLIP) && + (!(thing->flags & MF_NOGRAVITY || thing->z > height) || + thing->z < waterheight)) + { + // Move objects only if on floor or underwater, + // non-floating, and clipped. + thing->momx += dx; + thing->momy += dy; + } + break; + + case sc_carry_ceiling: // to be added later + break; + } +} + +// +// Add_Scroller() +// +// Add a generalized scroller to the thinker list. +// +// type: the enumerated type of scrolling: floor, ceiling, floor carrier, +// wall, floor carrier & scroller +// +// (dx,dy): the direction and speed of the scrolling or its acceleration +// +// control: the sector whose heights control this scroller's effect +// remotely, or -1 if no control sector +// +// affectee: the index of the affected object (sector or sidedef) +// +// accel: non-zero if this is an accelerative effect +// + +static void Add_Scroller(int type, fixed_t dx, fixed_t dy, + int control, int affectee, int accel) +{ + scroll_t *s = Z_Malloc(sizeof *s, PU_LEVSPEC, 0); + s->thinker.function = T_Scroll; + s->type = type; + s->dx = dx; + s->dy = dy; + s->accel = accel; + s->vdx = s->vdy = 0; + if ((s->control = control) != -1) + s->last_height = + sectors[control].floorheight + sectors[control].ceilingheight; + s->affectee = affectee; + P_AddThinker(&s->thinker); +} + +// Adds wall scroller. Scroll amount is rotated with respect to wall's +// linedef first, so that scrolling towards the wall in a perpendicular +// direction is translated into vertical motion, while scrolling along +// the wall in a parallel direction is translated into horizontal motion. +// +// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff +// +// killough 10/98: +// fix scrolling aliasing problems, caused by long linedefs causing overflowing + +static void Add_WallScroller(fixed_t dx, fixed_t dy, const line_t *l, + int control, int accel) +{ + fixed_t x = D_abs(l->dx), y = D_abs(l->dy), d; + if (y > x) + d = x, x = y, y = d; + d = FixedDiv(x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) + >> ANGLETOFINESHIFT]); + + // CPhipps - Import scroller calc overflow fix, compatibility optioned + if (compatibility_level >= lxdoom_1_compatibility) { + x = (fixed_t)(((int_64_t)dy * -(int_64_t)l->dy - (int_64_t)dx * (int_64_t)l->dx) / (int_64_t)d); // killough 10/98: + y = (fixed_t)(((int_64_t)dy * (int_64_t)l->dx - (int_64_t)dx * (int_64_t)l->dy) / (int_64_t)d); // Use long long arithmetic + } else { + x = -FixedDiv(FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); + y = -FixedDiv(FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); + } + Add_Scroller(sc_side, x, y, control, *l->sidenum, accel); +} + +// Amount (dx,dy) vector linedef is shifted right to get scroll amount +#define SCROLL_SHIFT 5 + +// Factor to scale scrolling effect into mobj-carrying properties = 3/32. +// (This is so scrolling floors and objects on them can move at same speed.) +#define CARRYFACTOR ((fixed_t)(FRACUNIT*.09375)) + +// Initialize the scrollers +static void P_SpawnScrollers(void) +{ + int i; + line_t *l = lines; + + for (i=0;i<numlines;i++,l++) + { + fixed_t dx = l->dx >> SCROLL_SHIFT; // direction and speed of scrolling + fixed_t dy = l->dy >> SCROLL_SHIFT; + int control = -1, accel = 0; // no control sector or acceleration + int special = l->special; + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + if (special >= 245 && special <= 249) // displacement scrollers + { + special += 250-245; + control = sides[*l->sidenum].sector - sectors; + } + else + if (special >= 214 && special <= 218) // accelerative scrollers + { + accel = 1; + special += 250-214; + control = sides[*l->sidenum].sector - sectors; + } + + switch (special) + { + register int s; + + case 250: // scroll effect ceiling + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_ceiling, -dx, dy, control, s, accel); + break; + + case 251: // scroll effect floor + case 253: // scroll and carry objects on floor + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_floor, -dx, dy, control, s, accel); + if (special != 253) + break; + + case 252: // carry objects on floor + dx = FixedMul(dx,CARRYFACTOR); + dy = FixedMul(dy,CARRYFACTOR); + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_carry, dx, dy, control, s, accel); + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case 254: + for (s=-1; (s = P_FindLineFromLineTag(l,s)) >= 0;) + if (s != i) + Add_WallScroller(dx, dy, lines+s, control, accel); + break; + + case 255: // killough 3/2/98: scroll according to sidedef offsets + s = lines[i].sidenum[0]; + Add_Scroller(sc_side, -sides[s].textureoffset, + sides[s].rowoffset, -1, s, accel); + break; + + case 48: // scroll first side + Add_Scroller(sc_side, FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + + case 85: // jff 1/30/98 2-way scroll + Add_Scroller(sc_side, -FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + } + } +} + +// killough 3/7/98 -- end generalized scroll effects + +//////////////////////////////////////////////////////////////////////////// +// +// FRICTION EFFECTS +// +// phares 3/12/98: Start of friction effects +// +// As the player moves, friction is applied by decreasing the x and y +// momentum values on each tic. By varying the percentage of decrease, +// we can simulate muddy or icy conditions. In mud, the player slows +// down faster. In ice, the player slows down more slowly. +// +// The amount of friction change is controlled by the length of a linedef +// with type 223. A length < 100 gives you mud. A length > 100 gives you ice. +// +// Also, each sector where these effects are to take place is given a +// new special type _______. Changing the type value at runtime allows +// these effects to be turned on or off. +// +// Sector boundaries present problems. The player should experience these +// friction changes only when his feet are touching the sector floor. At +// sector boundaries where floor height changes, the player can find +// himself still 'in' one sector, but with his feet at the floor level +// of the next sector (steps up or down). To handle this, Thinkers are used +// in icy/muddy sectors. These thinkers examine each object that is touching +// their sectors, looking for players whose feet are at the same level as +// their floors. Players satisfying this condition are given new friction +// values that are applied by the player movement code later. +// +// killough 8/28/98: +// +// Completely redid code, which did not need thinkers, and which put a heavy +// drag on CPU. Friction is now a property of sectors, NOT objects inside +// them. All objects, not just players, are affected by it, if they touch +// the sector's floor. Code simpler and faster, only calling on friction +// calculations when an object needs friction considered, instead of doing +// friction calculations on every sector during every tic. +// +// Although this -might- ruin Boom demo sync involving friction, it's the only +// way, short of code explosion, to fix the original design bug. Fixing the +// design bug in Boom's original friction code, while maintaining demo sync +// under every conceivable circumstance, would double or triple code size, and +// would require maintenance of buggy legacy code which is only useful for old +// demos. Doom demos, which are more important IMO, are not affected by this +// change. +// +///////////////////////////// +// +// Initialize the sectors where friction is increased or decreased + +static void P_SpawnFriction(void) +{ + int i; + line_t *l = lines; + + // killough 8/28/98: initialize all sectors to normal friction first + for (i = 0; i < numsectors; i++) + { + sectors[i].friction = ORIG_FRICTION; + sectors[i].movefactor = ORIG_FRICTION_FACTOR; + } + + for (i = 0 ; i < numlines ; i++,l++) + if (l->special == 223) + { + int length = P_AproxDistance(l->dx,l->dy)>>FRACBITS; + int friction = (0x1EB8*length)/0x80 + 0xD000; + int movefactor, s; + + // The following check might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + + if (friction > ORIG_FRICTION) // ice + movefactor = ((0x10092 - friction)*(0x70))/0x158; + else + movefactor = ((friction - 0xDB34)*(0xA))/0x80; + + if (mbf_features) + { // killough 8/28/98: prevent odd situations + if (friction > FRACUNIT) + friction = FRACUNIT; + if (friction < 0) + friction = 0; + if (movefactor < 32) + movefactor = 32; + } + + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + // killough 8/28/98: + // + // Instead of spawning thinkers, which are slow and expensive, + // modify the sector's own friction values. Friction should be + // a property of sectors, not objects which reside inside them. + // Original code scanned every object in every friction sector + // on every tic, adjusting its friction, putting unnecessary + // drag on CPU. New code adjusts friction of sector only once + // at level startup, and then uses this friction value. + + sectors[s].friction = friction; + sectors[s].movefactor = movefactor; + } + } +} + +// +// phares 3/12/98: End of friction effects +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// PUSH/PULL EFFECT +// +// phares 3/20/98: Start of push/pull effects +// +// This is where push/pull effects are applied to objects in the sectors. +// +// There are four kinds of push effects +// +// 1) Pushing Away +// +// Pushes you away from a point source defined by the location of an +// MT_PUSH Thing. The force decreases linearly with distance from the +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PUSH. The force is felt only if the point +// MT_PUSH can see the target object. +// +// 2) Pulling toward +// +// Same as Pushing Away except you're pulled toward an MT_PULL point +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PULL. The force is felt only if the point +// MT_PULL can see the target object. +// +// 3) Wind +// +// Pushes you in a constant direction. Full force above ground, half +// force on the ground, nothing if you're below it (water). +// +// 4) Current +// +// Pushes you in a constant direction. No force above ground, full +// force if on the ground or below it (water). +// +// The magnitude of the force is controlled by the length of a controlling +// linedef. The force vector for types 3 & 4 is determined by the angle +// of the linedef, and is constant. +// +// For each sector where these effects occur, the sector special type has +// to have the PUSH_MASK bit set. If this bit is turned off by a switch +// at run-time, the effect will not occur. The controlling sector for +// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. + + +#define PUSH_FACTOR 7 + +///////////////////////////// +// +// Add a push thinker to the thinker list + +static void Add_Pusher(int type, int x_mag, int y_mag, mobj_t* source, int affectee) +{ + pusher_t *p = Z_Malloc(sizeof *p, PU_LEVSPEC, 0); + + p->thinker.function = T_Pusher; + p->source = source; + p->type = type; + p->x_mag = x_mag>>FRACBITS; + p->y_mag = y_mag>>FRACBITS; + p->magnitude = P_AproxDistance(p->x_mag,p->y_mag); + if (source) // point source exist? + { + p->radius = (p->magnitude)<<(FRACBITS+1); // where force goes to zero + p->x = p->source->x; + p->y = p->source->y; + } + p->affectee = affectee; + P_AddThinker(&p->thinker); +} + +///////////////////////////// +// +// PIT_PushThing determines the angle and magnitude of the effect. +// The object's x and y momentum values are changed. +// +// tmpusher belongs to the point source (MT_PUSH/MT_PULL). +// +// killough 10/98: allow to affect things besides players + +pusher_t* tmpusher; // pusher structure for blockmap searches + +boolean PIT_PushThing(mobj_t* thing) +{ + /* killough 10/98: made more general */ + if (!mbf_features ? + thing->player && !(thing->flags & (MF_NOCLIP | MF_NOGRAVITY)) : + (sentient(thing) || thing->flags & MF_SHOOTABLE) && + !(thing->flags & MF_NOCLIP)) + { + angle_t pushangle; + fixed_t speed; + fixed_t sx = tmpusher->x; + fixed_t sy = tmpusher->y; + + speed = (tmpusher->magnitude - + ((P_AproxDistance(thing->x - sx,thing->y - sy) + >>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); + + // killough 10/98: make magnitude decrease with square + // of distance, making it more in line with real nature, + // so long as it's still in range with original formula. + // + // Removes angular distortion, and makes effort required + // to stay close to source, grow increasingly hard as you + // get closer, as expected. Still, it doesn't consider z :( + + if (speed > 0 && mbf_features) + { + int x = (thing->x-sx) >> FRACBITS; + int y = (thing->y-sy) >> FRACBITS; + speed = (int)(((uint_64_t) tmpusher->magnitude << 23) / (x*x+y*y+1)); + } + + // If speed <= 0, you're outside the effective radius. You also have + // to be able to see the push/pull source point. + + if (speed > 0 && P_CheckSight(thing,tmpusher->source)) + { + pushangle = R_PointToAngle2(thing->x,thing->y,sx,sy); + if (tmpusher->source->type == MT_PUSH) + pushangle += ANG180; // away + pushangle >>= ANGLETOFINESHIFT; + thing->momx += FixedMul(speed,finecosine[pushangle]); + thing->momy += FixedMul(speed,finesine[pushangle]); + } + } + return true; +} + +///////////////////////////// +// +// T_Pusher looks for all objects that are inside the radius of +// the effect. +// + +void T_Pusher(pusher_t *p) +{ + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + int xspeed,yspeed; + int xl,xh,yl,yh,bx,by; + int radius; + int ht = 0; + + if (!allow_pushers) + return; + + sec = sectors + p->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & PUSH_MASK)) + return; + + // For constant pushers (wind/current) there are 3 situations: + // + // 1) Affected Thing is above the floor. + // + // Apply the full force if wind, no force if current. + // + // 2) Affected Thing is on the ground. + // + // Apply half force if wind, full force if current. + // + // 3) Affected Thing is below the ground (underwater effect). + // + // Apply no force if wind, full force if current. + + if (p->type == p_push) + { + + // Seek out all pushable things within the force radius of this + // point pusher. Crosses sectors, so use blockmap. + + tmpusher = p; // MT_PUSH/MT_PULL point source + radius = p->radius; // where force goes to zero + tmbbox[BOXTOP] = p->y + radius; + tmbbox[BOXBOTTOM] = p->y - radius; + tmbbox[BOXRIGHT] = p->x + radius; + tmbbox[BOXLEFT] = p->x - radius; + + 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++) + P_BlockThingsIterator(bx,by,PIT_PushThing); + return; + } + + // constant pushers p_wind and p_current + + if (sec->heightsec != -1) // special water sector? + ht = sectors[sec->heightsec].floorheight; + node = sec->touching_thinglist; // things touching this sector + for ( ; node ; node = node->m_snext) + { + thing = node->m_thing; + if (!thing->player || (thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) + continue; + if (p->type == p_wind) + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > thing->floorz) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // on ground + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + else // special water sector + { + if (thing->z > ht) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else if (thing->player->viewz < ht) // underwater + xspeed = yspeed = 0; // no force + else // wading in water + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + } + } + else // p_current + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > sec->floorheight) // above ground + xspeed = yspeed = 0; // no force + else // on ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // special water sector + if (thing->z > ht) // above ground + xspeed = yspeed = 0; // no force + else // underwater + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + } + thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR); + thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR); + } +} + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +mobj_t* P_GetPushThing(int s) +{ + mobj_t* thing; + sector_t* sec; + + sec = sectors + s; + thing = sec->thinglist; + while (thing) + { + switch(thing->type) + { + case MT_PUSH: + case MT_PULL: + return thing; + default: + break; + } + thing = thing->snext; + } + return NULL; +} + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +static void P_SpawnPushers(void) +{ + int i; + line_t *l = lines; + register int s; + mobj_t* thing; + + for (i = 0 ; i < numlines ; i++,l++) + switch(l->special) + { + case 224: // wind + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_wind,l->dx,l->dy,NULL,s); + break; + case 225: // current + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_current,l->dx,l->dy,NULL,s); + break; + case 226: // push/pull + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + thing = P_GetPushThing(s); + if (thing) // No MT_P* means no effect + Add_Pusher(p_push,l->dx,l->dy,thing,s); + } + break; + } +} + +// +// phares 3/20/98: End of Pusher effects +// +//////////////////////////////////////////////////////////////////////////// |