diff --git a/src/p_spec.c.orig b/src/p_spec.c.orig deleted file mode 100644 index 244f7166b..000000000 --- a/src/p_spec.c.orig +++ /dev/null @@ -1,9150 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2021 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file p_spec.c -/// \brief 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 "dehacked.h" -#include "doomdef.h" -#include "g_game.h" -#include "p_local.h" -#include "p_setup.h" // levelflats for flat animation -#include "r_data.h" -#include "r_textures.h" -#include "m_random.h" -#include "p_mobj.h" -#include "i_system.h" -#include "s_sound.h" -#include "w_wad.h" -#include "z_zone.h" -#include "r_main.h" //Two extra includes. -#include "r_sky.h" -#include "p_polyobj.h" -#include "p_slopes.h" -#include "hu_stuff.h" -#include "v_video.h" // V_AUTOFADEOUT|V_ALLOWLOWERCASE -#include "m_misc.h" -#include "m_cond.h" //unlock triggers -#include "lua_hook.h" // LUA_HookLinedefExecute -#include "f_finale.h" // control text prompt -#include "r_skins.h" // skins - -#ifdef HW3SOUND -#include "hardware/hw3sound.h" -#endif - -// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog -#include - -mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint -mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs -mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs - -// Amount (dx, dy) vector linedef is shifted right to get scroll amount -#define SCROLL_SHIFT 5 - -/** Animated texture descriptor - * This keeps track of an animated texture or an animated flat. - * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t - */ -typedef struct -{ - SINT8 istexture; ///< ::true for a texture, ::false for a flat - INT32 picnum; ///< The end flat number - INT32 basepic; ///< The start flat number - INT32 numpics; ///< Number of frames in the animation - tic_t speed; ///< Number of tics for which each frame is shown -} anim_t; - -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -/** Animated texture definition. - * Used for loading an ANIMDEFS lump from a wad. - * - * Animations are defined by the first and last frame (i.e., flat or texture). - * The animation sequence uses all flats between the start and end entry, in - * the order found in the wad. - * - * \sa anim_t - */ -typedef struct -{ - SINT8 istexture; ///< True for a texture, false for a flat. - char endname[9]; ///< Name of the last frame, null-terminated. - char startname[9]; ///< Name of the first frame, null-terminated. - INT32 speed ; ///< Number of tics for which each frame is shown. -} ATTRPACK animdef_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - -typedef struct -{ - UINT32 count; - thinker_t **thinkers; -} thinkerlist_t; - -static void P_SpawnScrollers(void); -static void P_SpawnFriction(void); -static void P_SpawnPushers(void); -static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, mobj_t *source, INT32 affectee, INT32 referrer, INT32 exclusive, INT32 slider); //SoM: 3/9/2000 -static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t offset, INT32 line, INT32 sourceline); -static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data, boolean finalize); -#define P_RemoveFakeFloorFader(l) P_ResetFakeFloorFader(l, NULL, false); -static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destvalue, INT16 speed, boolean ticbased, INT32 *timer, - boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap, - boolean docollision, boolean doghostfade, boolean exactalpha); -static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum, - INT16 destvalue, INT16 speed, boolean ticbased, boolean relative, - boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap, - boolean docollision, boolean doghostfade, boolean exactalpha); -static void P_ResetColormapFader(sector_t *sector); -static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, extracolormap_t *dest_exc, - boolean ticbased, INT32 duration); -static void P_AddBlockThinker(sector_t *sec, line_t *sourceline); -static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline); -//static void P_AddBridgeThinker(line_t *sourceline, sector_t *sec); -static void P_AddFakeFloorsByLine(size_t line, INT32 alpha, ffloortype_e ffloorflags, thinkerlist_t *secthinkers); -static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec); -static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32 referrer); -static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee, UINT8 reverse); - - -//SoM: 3/7/2000: New sturcture without limits. -static anim_t *lastanim; -static anim_t *anims = NULL; /// \todo free leak -static size_t maxanims; - -// Animating line specials - -// Init animated textures -// - now called at level loading P_SetupLevel() - -static animdef_t *animdefs = NULL; - -// Increase the size of animdefs to make room for a new animation definition -static void GrowAnimDefs(void) -{ - maxanims++; - animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL); -} - -// A prototype; here instead of p_spec.h, so they're "private" -void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum); -void P_ParseAnimationDefintion(SINT8 istexture); - -/** Sets up texture and flat animations. - * - * Converts an ::animdef_t array loaded from a lump into - * ::anim_t format. - * - * Issues an error if any animation cycles are invalid. - * - * \sa P_FindAnimatedFlat, P_SetupLevelFlatAnims - * \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs), JTE (had to rewrite it to handle multiple WADs _correctly_) - */ -void P_InitPicAnims(void) -{ - // Init animation - INT32 w; // WAD - size_t i; - - I_Assert(animdefs == NULL); - - maxanims = 0; - - for (w = numwadfiles-1; w >= 0; w--) - { - UINT16 animdefsLumpNum; - - // Find ANIMDEFS lump in the WAD - animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", w, 0); - - while (animdefsLumpNum != INT16_MAX) - { - P_ParseANIMDEFSLump(w, animdefsLumpNum); - animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", (UINT16)w, animdefsLumpNum + 1); - } - } - - // Define the last one - animdefs[maxanims].istexture = -1; - strncpy(animdefs[maxanims].endname, "", 9); - strncpy(animdefs[maxanims].startname, "", 9); - animdefs[maxanims].speed = 0; - - if (anims) - free(anims); - - anims = (anim_t *)malloc(sizeof (*anims)*(maxanims + 1)); - if (!anims) - I_Error("Not enough free memory for ANIMDEFS data"); - - lastanim = anims; - for (i = 0; animdefs[i].istexture != -1; i++) - { - if (animdefs[i].istexture) - { - 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)) == LUMPERROR) - continue; - - lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname); - lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname); - } - - lastanim->istexture = animdefs[i].istexture; - lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; - - if (lastanim->numpics < 2) - { - free(anims); - I_Error("P_InitPicAnims: bad cycle from %s to %s", - animdefs[i].startname, animdefs[i].endname); - } - - lastanim->speed = LONG(animdefs[i].speed); - lastanim++; - } - lastanim->istexture = -1; - R_ClearTextureNumCache(false); - - // Clear animdefs now that we're done with it. - // We'll only be using anims from now on. - Z_Free(animdefs); - animdefs = NULL; -} - -void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum) -{ - char *animdefsLump; - size_t animdefsLumpLength; - char *animdefsText; - char *animdefsToken; - char *p; - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - animdefsLump = (char *)W_CacheLumpNumPwad(wadNum,lumpnum,PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (animdefsLump == NULL) return; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - animdefsLumpLength = W_LumpLengthPwad(wadNum,lumpnum); - animdefsText = (char *)Z_Malloc((animdefsLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(animdefsText,animdefsLump,animdefsLumpLength); - // Make damn well sure the last character in our new memory location is \0. - animdefsText[animdefsLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(animdefsLump); - - // Now, let's start parsing this thing - p = animdefsText; - animdefsToken = M_GetToken(p); - while (animdefsToken != NULL) - { - if (stricmp(animdefsToken, "TEXTURE") == 0) - { - Z_Free(animdefsToken); - P_ParseAnimationDefintion(1); - } - else if (stricmp(animdefsToken, "FLAT") == 0) - { - Z_Free(animdefsToken); - P_ParseAnimationDefintion(0); - } - else if (stricmp(animdefsToken, "OSCILLATE") == 0) - { - // This probably came off the tail of an earlier definition. It's technically legal syntax, but we don't support it. - I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"OSCILLATE\" (the animation plays in reverse when it reaches the end) are not supported by SRB2"); - } - else - { - I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\" or \"FLAT\", got \"%s\"",animdefsToken); - } - // parse next line - while (*p != '\0' && *p != '\n') ++p; - if (*p == '\n') ++p; - animdefsToken = M_GetToken(p); - } - Z_Free(animdefsToken); - Z_Free((void *)animdefsText); -} - -void P_ParseAnimationDefintion(SINT8 istexture) -{ - char *animdefsToken; - size_t animdefsTokenLength; - char *endPos; - INT32 animSpeed; - size_t i; - - // Startname - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be"); - } - if (stricmp(animdefsToken, "OPTIONAL") == 0) - { - // This is meaningful to ZDoom - it tells the program NOT to bomb out - // if the textures can't be found - but it's useless in SRB2, so we'll - // just smile, nod, and carry on - Z_Free(animdefsToken); - animdefsToken = M_GetToken(NULL); - - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be"); - } - else if (stricmp(animdefsToken, "RANGE") == 0) - { - // Oh. Um. Apparently "OPTIONAL" is a texture name. Naughty. - // I should probably handle this more gracefully, but right now - // I can't be bothered; especially since ZDoom doesn't handle this - // condition at all. - I_Error("Error parsing ANIMDEFS lump: \"OPTIONAL\" is a keyword; you cannot use it as the startname of an animation"); - } - } - animdefsTokenLength = strlen(animdefsToken); - if (animdefsTokenLength>8) - { - I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); - } - - // Search for existing animdef - for (i = 0; i < maxanims; i++) - if (animdefs[i].istexture == istexture // Check if it's the same type! - && stricmp(animdefsToken, animdefs[i].startname) == 0) - { - //CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken); - - // If we weren't parsing in reverse order, we would `break` here and parse the new data into the existing slot we found. - // Instead, we're just going to skip parsing the rest of this line entirely. - Z_Free(animdefsToken); - return; - } - - // Not found - if (i == maxanims) - { - // Increase the size to make room for the new animation definition - GrowAnimDefs(); - strncpy(animdefs[i].startname, animdefsToken, 9); - } - - // animdefs[i].startname is now set to animdefsToken either way. - Z_Free(animdefsToken); - - // set texture type - animdefs[i].istexture = istexture; - - // "RANGE" - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"RANGE\" after \"%s\"'s startname should be", animdefs[i].startname); - } - if (stricmp(animdefsToken, "ALLOWDECALS") == 0) - { - // Another ZDoom keyword, ho-hum. Skip it, move on to the next token. - Z_Free(animdefsToken); - animdefsToken = M_GetToken(NULL); - } - if (stricmp(animdefsToken, "PIC") == 0) - { - // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. - I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"PIC\" (specific frames instead of a consecutive range) are not supported by SRB2"); - } - if (stricmp(animdefsToken, "RANGE") != 0) - { - I_Error("Error parsing ANIMDEFS lump: Expected \"RANGE\" after \"%s\"'s startname, got \"%s\"", animdefs[i].startname, animdefsToken); - } - Z_Free(animdefsToken); - - // Endname - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s end texture/flat name should be", animdefs[i].startname); - } - animdefsTokenLength = strlen(animdefsToken); - if (animdefsTokenLength>8) - { - I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); - } - strncpy(animdefs[i].endname, animdefsToken, 9); - Z_Free(animdefsToken); - - // "TICS" - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s \"TICS\" should be", animdefs[i].startname); - } - if (stricmp(animdefsToken, "RAND") == 0) - { - // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. - I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"RAND\" (random duration per frame) are not supported by SRB2"); - } - if (stricmp(animdefsToken, "TICS") != 0) - { - I_Error("Error parsing ANIMDEFS lump: Expected \"TICS\" in animation definition for \"%s\", got \"%s\"", animdefs[i].startname, animdefsToken); - } - Z_Free(animdefsToken); - - // Speed - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s animation speed should be", animdefs[i].startname); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - animSpeed = strtol(animdefsToken,&endPos,10); - if (endPos == animdefsToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || animSpeed < 0) // Number is not positive - { - I_Error("Error parsing ANIMDEFS lump: Expected a positive integer for \"%s\"'s animation speed, got \"%s\"", animdefs[i].startname, animdefsToken); - } - animdefs[i].speed = animSpeed; - Z_Free(animdefsToken); - -#ifdef WALLFLATS - // hehe... uhh..... - if (!istexture) - { - GrowAnimDefs(); - M_Memcpy(&animdefs[maxanims-1], &animdefs[i], sizeof(animdef_t)); - animdefs[maxanims-1].istexture = 1; - } -#endif -} - -/** Checks for flats in levelflats that are part of a flat animation sequence - * and sets them up for animation. - * - * \param animnum Index into ::anims to find flats for. - * \sa P_SetupLevelFlatAnims - */ -static inline void P_FindAnimatedFlat(INT32 animnum) -{ - size_t i; - lumpnum_t startflatnum, endflatnum; - levelflat_t *foundflats; - - foundflats = levelflats; - startflatnum = anims[animnum].basepic; - endflatnum = anims[animnum].picnum; - - // note: high word of lumpnum is the wad number - if ((startflatnum>>16) != (endflatnum>>16)) - I_Error("AnimatedFlat start %s not in same wad as end %s\n", - animdefs[animnum].startname, animdefs[animnum].endname); - - // - // now search through the levelflats if this anim flat sequence is used - // - for (i = 0; i < numlevelflats; i++, foundflats++) - { - // is that levelflat from the flat anim sequence ? - if ((anims[animnum].istexture) && (foundflats->type == LEVELFLAT_TEXTURE) - && ((UINT16)foundflats->u.texture.num >= startflatnum && (UINT16)foundflats->u.texture.num <= endflatnum)) - { - foundflats->u.texture.basenum = startflatnum; - foundflats->animseq = foundflats->u.texture.num - startflatnum; - foundflats->numpics = endflatnum - startflatnum + 1; - foundflats->speed = anims[animnum].speed; - - CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n", - atoi(sizeu1(i)), foundflats->name, foundflats->animseq, - foundflats->numpics,foundflats->speed); - } - else if ((!anims[animnum].istexture) && (foundflats->type == LEVELFLAT_FLAT) - && (foundflats->u.flat.lumpnum >= startflatnum && foundflats->u.flat.lumpnum <= endflatnum)) - { - foundflats->u.flat.baselumpnum = startflatnum; - foundflats->animseq = foundflats->u.flat.lumpnum - startflatnum; - foundflats->numpics = endflatnum - startflatnum + 1; - foundflats->speed = anims[animnum].speed; - - CONS_Debug(DBG_SETUP, "animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d\n", - atoi(sizeu1(i)), foundflats->name, foundflats->animseq, - foundflats->numpics,foundflats->speed); - } - } -} - -/** Sets up all flats used in a level. - * - * \sa P_InitPicAnims, P_FindAnimatedFlat - */ -void P_SetupLevelFlatAnims(void) -{ - INT32 i; - - // the original game flat anim sequences - for (i = 0; anims[i].istexture != -1; i++) - P_FindAnimatedFlat(i); -} - -// -// UTILITIES -// - -#if 0 -/** Gets a side from a sector line. - * - * \param currentSector Sector the line is in. - * \param line Index of the line within the sector. - * \param side 0 for front, 1 for back. - * \return Pointer to the side_t of the side you want. - * \sa getSector, twoSided, getNextSector - */ -static inline side_t *getSide(INT32 currentSector, INT32 line, INT32 side) -{ - return &sides[(sectors[currentSector].lines[line])->sidenum[side]]; -} - -/** Gets a sector from a sector line. - * - * \param currentSector Sector the line is in. - * \param line Index of the line within the sector. - * \param side 0 for front, 1 for back. - * \return Pointer to the ::sector_t of the sector on that side. - * \sa getSide, twoSided, getNextSector - */ -static inline sector_t *getSector(INT32 currentSector, INT32 line, INT32 side) -{ - return sides[(sectors[currentSector].lines[line])->sidenum[side]].sector; -} - -/** Determines whether a sector line is two-sided. - * Uses the Boom method, checking if the line's back side is set to -1, rather - * than looking for ::ML_TWOSIDED. - * - * \param sector The sector. - * \param line Line index within the sector. - * \return 1 if the sector is two-sided, 0 otherwise. - * \sa getSide, getSector, getNextSector - */ -static inline boolean twoSided(INT32 sector, INT32 line) -{ - return (sectors[sector].lines[line])->sidenum[1] != 0xffff; -} -#endif - -/** Finds sector next to current. - * - * \param line Pointer to the line to cross. - * \param sec Pointer to the current sector. - * \return Pointer to a ::sector_t of the adjacent sector, or NULL if the line - * is one-sided. - * \sa getSide, getSector, twoSided - * \author Steven McGranahan - */ -static sector_t *getNextSector(line_t *line, sector_t *sec) -{ - if (line->frontsector == sec) - { - if (line->backsector != sec) - return line->backsector; - else - return NULL; - } - return line->frontsector; -} - -/** Finds lowest floor in adjacent sectors. - * - * \param sec Sector to start in. - * \return Lowest floor height in an adjacent sector. - * \sa P_FindHighestFloorSurrounding, P_FindNextLowestFloor, - * P_FindLowestCeilingSurrounding - */ -fixed_t P_FindLowestFloorSurrounding(sector_t *sec) -{ - size_t i; - line_t *check; - sector_t *other; - fixed_t floorh; - - floorh = sec->floorheight; - - for (i = 0; i < sec->linecount; i++) - { - check = sec->lines[i]; - other = getNextSector(check,sec); - - if (!other) - continue; - - if (other->floorheight < floorh) - floorh = other->floorheight; - } - return floorh; -} - -/** Finds highest floor in adjacent sectors. - * - * \param sec Sector to start in. - * \return Highest floor height in an adjacent sector. - * \sa P_FindLowestFloorSurrounding, P_FindNextHighestFloor, - * P_FindHighestCeilingSurrounding - */ -fixed_t P_FindHighestFloorSurrounding(sector_t *sec) -{ - size_t i; - line_t *check; - sector_t *other; - fixed_t floorh = -500*FRACUNIT; - INT32 foundsector = 0; - - for (i = 0; i < sec->linecount; i++) - { - check = sec->lines[i]; - other = getNextSector(check, sec); - - if (!other) - continue; - - if (other->floorheight > floorh || !foundsector) - floorh = other->floorheight; - - if (!foundsector) - foundsector = 1; - } - return floorh; -} - -/** Finds next highest floor in adjacent sectors. - * - * \param sec Sector to start in. - * \param currentheight Height to start at. - * \return Next highest floor height in an adjacent sector, or currentheight - * if there are none higher. - * \sa P_FindHighestFloorSurrounding, P_FindNextLowestFloor, - * P_FindNextHighestCeiling - * \author Lee Killough - */ -fixed_t P_FindNextHighestFloor(sector_t *sec, fixed_t currentheight) -{ - sector_t *other; - size_t i; - fixed_t height; - - for (i = 0; i < sec->linecount; i++) - { - other = getNextSector(sec->lines[i],sec); - if (other && other->floorheight > currentheight) - { - height = other->floorheight; - while (++i < sec->linecount) - { - other = getNextSector(sec->lines[i], sec); - if (other && - other->floorheight < height && - other->floorheight > currentheight) - height = other->floorheight; - } - return height; - } - } - return currentheight; -} - -//////////////////////////////////////////////////// -// SoM: Start new Boom functions -//////////////////////////////////////////////////// - -/** Finds next lowest floor in adjacent sectors. - * - * \param sec Sector to start in. - * \param currentheight Height to start at. - * \return Next lowest floor height in an adjacent sector, or currentheight - * if there are none lower. - * \sa P_FindLowestFloorSurrounding, P_FindNextHighestFloor, - * P_FindNextLowestCeiling - * \author Lee Killough - */ -fixed_t P_FindNextLowestFloor(sector_t *sec, fixed_t currentheight) -{ - sector_t *other; - size_t i; - fixed_t height; - - for (i = 0; i < sec->linecount; i++) - { - other = getNextSector(sec->lines[i], sec); - if (other && other->floorheight < currentheight) - { - height = other->floorheight; - while (++i < sec->linecount) - { - other = getNextSector(sec->lines[i], sec); - if (other && other->floorheight > height - && other->floorheight < currentheight) - height = other->floorheight; - } - return height; - } - } - return currentheight; -} - -#if 0 -/** Finds next lowest ceiling in adjacent sectors. - * - * \param sec Sector to start in. - * \param currentheight Height to start at. - * \return Next lowest ceiling height in an adjacent sector, or currentheight - * if there are none lower. - * \sa P_FindLowestCeilingSurrounding, P_FindNextHighestCeiling, - * P_FindNextLowestFloor - * \author Lee Killough - */ -static fixed_t P_FindNextLowestCeiling(sector_t *sec, fixed_t currentheight) -{ - sector_t *other; - size_t i; - fixed_t height; - - for (i = 0; i < sec->linecount; i++) - { - other = getNextSector(sec->lines[i],sec); - if (other && other->ceilingheight < currentheight) - { - height = other->ceilingheight; - while (++i < sec->linecount) - { - other = getNextSector(sec->lines[i],sec); - if (other && other->ceilingheight > height - && other->ceilingheight < currentheight) - height = other->ceilingheight; - } - return height; - } - } - return currentheight; -} - -/** Finds next highest ceiling in adjacent sectors. - * - * \param sec Sector to start in. - * \param currentheight Height to start at. - * \return Next highest ceiling height in an adjacent sector, or currentheight - * if there are none higher. - * \sa P_FindHighestCeilingSurrounding, P_FindNextLowestCeiling, - * P_FindNextHighestFloor - * \author Lee Killough - */ -static fixed_t P_FindNextHighestCeiling(sector_t *sec, fixed_t currentheight) -{ - sector_t *other; - size_t i; - fixed_t height; - - for (i = 0; i < sec->linecount; i++) - { - other = getNextSector(sec->lines[i], sec); - if (other && other->ceilingheight > currentheight) - { - height = other->ceilingheight; - while (++i < sec->linecount) - { - other = getNextSector(sec->lines[i],sec); - if (other && other->ceilingheight < height - && other->ceilingheight > currentheight) - height = other->ceilingheight; - } - return height; - } - } - return currentheight; -} -#endif - -//////////////////////////// -// End New Boom functions -//////////////////////////// - -/** Finds lowest ceiling in adjacent sectors. - * - * \param sec Sector to start in. - * \return Lowest ceiling height in an adjacent sector. - * \sa P_FindHighestCeilingSurrounding, P_FindNextLowestCeiling, - * P_FindLowestFloorSurrounding - */ -fixed_t P_FindLowestCeilingSurrounding(sector_t *sec) -{ - size_t i; - line_t *check; - sector_t *other; - fixed_t height = 32000*FRACUNIT; //SoM: 3/7/2000: Remove ovf - INT32 foundsector = 0; - - for (i = 0; i < sec->linecount; i++) - { - check = sec->lines[i]; - other = getNextSector(check, sec); - - if (!other) - continue; - - if (other->ceilingheight < height || !foundsector) - height = other->ceilingheight; - - if (!foundsector) - foundsector = 1; - } - return height; -} - -/** Finds Highest ceiling in adjacent sectors. - * - * \param sec Sector to start in. - * \return Highest ceiling height in an adjacent sector. - * \sa P_FindLowestCeilingSurrounding, P_FindNextHighestCeiling, - * P_FindHighestFloorSurrounding - */ -fixed_t P_FindHighestCeilingSurrounding(sector_t *sec) -{ - size_t i; - line_t *check; - sector_t *other; - fixed_t height = 0; - INT32 foundsector = 0; - - for (i = 0; i < sec->linecount; i++) - { - check = sec->lines[i]; - other = getNextSector(check, sec); - - if (!other) - continue; - - if (other->ceilingheight > height || !foundsector) - height = other->ceilingheight; - - if (!foundsector) - foundsector = 1; - } - return height; -} - -#if 0 -//SoM: 3/7/2000: UTILS..... -// -// P_FindShortestTextureAround() -// -// Passed a sector number, returns the shortest lower texture on a -// linedef bounding the sector. -// -// -static fixed_t P_FindShortestTextureAround(INT32 secnum) -{ - fixed_t minsize = 32000<linecount; i++) - { - if (twoSided(secnum, i)) - { - side = getSide(secnum,i,0); - if (side->bottomtexture > 0) - if (textureheight[side->bottomtexture] < minsize) - minsize = textureheight[side->bottomtexture]; - side = getSide(secnum,i,1); - if (side->bottomtexture > 0) - if (textureheight[side->bottomtexture] < minsize) - minsize = textureheight[side->bottomtexture]; - } - } - return minsize; -} - -//SoM: 3/7/2000: Stuff.... (can you tell I'm getting tired? It's 12 : 30!) -// -// P_FindShortestUpperAround() -// -// Passed a sector number, returns the shortest upper texture on a -// linedef bounding the sector. -// -// -static fixed_t P_FindShortestUpperAround(INT32 secnum) -{ - fixed_t minsize = 32000<linecount; i++) - { - if (twoSided(secnum, i)) - { - side = getSide(secnum,i,0); - if (side->toptexture > 0) - if (textureheight[side->toptexture] < minsize) - minsize = textureheight[side->toptexture]; - side = getSide(secnum,i,1); - if (side->toptexture > 0) - if (textureheight[side->toptexture] < minsize) - minsize = textureheight[side->toptexture]; - } - } - return minsize; -} - -//SoM: 3/7/2000 -// -// 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 -// -// -static sector_t *P_FindModelFloorSector(fixed_t floordestheight, INT32 secnum) -{ - size_t i; - sector_t *sec = §ors[secnum]; - - for (i = 0; i < sec->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 -// -static sector_t *P_FindModelCeilingSector(fixed_t ceildestheight, INT32 secnum) -{ - size_t i; - sector_t *sec = §ors[secnum]; - - for (i = 0; i < sec->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; -} -#endif - -// Parses arguments for parameterized polyobject door types -static boolean PolyDoor(line_t *line) -{ - polydoordata_t pdd; - - pdd.polyObjNum = Tag_FGet(&line->tags); // polyobject id - - switch(line->special) - { - case 480: // Polyobj_DoorSlide - pdd.doorType = POLY_DOOR_SLIDE; - pdd.speed = sides[line->sidenum[0]].textureoffset / 8; - pdd.angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y); // angle of motion - pdd.distance = sides[line->sidenum[0]].rowoffset; - - if (line->sidenum[1] != 0xffff) - pdd.delay = sides[line->sidenum[1]].textureoffset >> FRACBITS; // delay in tics - else - pdd.delay = 0; - break; - case 481: // Polyobj_DoorSwing - pdd.doorType = POLY_DOOR_SWING; - pdd.speed = sides[line->sidenum[0]].textureoffset >> FRACBITS; // angular speed - pdd.distance = sides[line->sidenum[0]].rowoffset >> FRACBITS; // angular distance - - if (line->sidenum[1] != 0xffff) - pdd.delay = sides[line->sidenum[1]].textureoffset >> FRACBITS; // delay in tics - else - pdd.delay = 0; - break; - default: - return 0; // ??? - } - - return EV_DoPolyDoor(&pdd); -} - -// Parses arguments for parameterized polyobject move specials -static boolean PolyMove(line_t *line) -{ - polymovedata_t pmd; - - pmd.polyObjNum = Tag_FGet(&line->tags); - pmd.speed = sides[line->sidenum[0]].textureoffset / 8; - pmd.angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y); - pmd.distance = sides[line->sidenum[0]].rowoffset; - - pmd.overRide = (line->special == 483); // Polyobj_OR_Move - - return EV_DoPolyObjMove(&pmd); -} - -// Makes a polyobject invisible and intangible -// If NOCLIMB is ticked, the polyobject will still be tangible, just not visible. -static void PolyInvisible(line_t *line) -{ - INT32 polyObjNum = Tag_FGet(&line->tags); - polyobj_t *po; - - if (!(po = Polyobj_GetForNum(polyObjNum))) - { - CONS_Debug(DBG_POLYOBJ, "PolyInvisible: bad polyobj %d\n", polyObjNum); - return; - } - - // don't allow line actions to affect bad polyobjects - if (po->isBad) - return; - - if (!(line->flags & ML_NOCLIMB)) - po->flags &= ~POF_SOLID; - - po->flags |= POF_NOSPECIALS; - po->flags &= ~POF_RENDERALL; -} - -// Makes a polyobject visible and tangible -// If NOCLIMB is ticked, the polyobject will not be tangible, just visible. -static void PolyVisible(line_t *line) -{ - INT32 polyObjNum = Tag_FGet(&line->tags); - polyobj_t *po; - - if (!(po = Polyobj_GetForNum(polyObjNum))) - { - CONS_Debug(DBG_POLYOBJ, "PolyVisible: bad polyobj %d\n", polyObjNum); - return; - } - - // don't allow line actions to affect bad polyobjects - if (po->isBad) - return; - - if (!(line->flags & ML_NOCLIMB)) - po->flags |= POF_SOLID; - - po->flags &= ~POF_NOSPECIALS; - po->flags |= (po->spawnflags & POF_RENDERALL); -} - - -// Sets the translucency of a polyobject -// Frontsector floor / 100 = translevel -static void PolyTranslucency(line_t *line) -{ - INT32 polyObjNum = Tag_FGet(&line->tags); - polyobj_t *po; - INT32 value; - - if (!(po = Polyobj_GetForNum(polyObjNum))) - { - CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjWaypoint: bad polyobj %d\n", polyObjNum); - return; - } - - // don't allow line actions to affect bad polyobjects - if (po->isBad) - return; - - // If Front X Offset is specified, use that. Else, use floorheight. - value = (sides[line->sidenum[0]].textureoffset ? sides[line->sidenum[0]].textureoffset : line->frontsector->floorheight) >> FRACBITS; - - // If DONTPEGBOTTOM, specify raw translucency value. Else, take it out of 1000. - if (!(line->flags & ML_DONTPEGBOTTOM)) - value /= 100; - - if (line->flags & ML_EFFECT3) // relative calc - po->translucency += value; - else - po->translucency = value; - - po->translucency = max(min(po->translucency, NUMTRANSMAPS), 0); -} - -// Makes a polyobject translucency fade and applies tangibility -static boolean PolyFade(line_t *line) -{ - INT32 polyObjNum = Tag_FGet(&line->tags); - polyobj_t *po; - polyfadedata_t pfd; - INT32 value; - - if (!(po = Polyobj_GetForNum(polyObjNum))) - { - CONS_Debug(DBG_POLYOBJ, "PolyFade: bad polyobj %d\n", polyObjNum); - return 0; - } - - // don't allow line actions to affect bad polyobjects - if (po->isBad) - return 0; - - // Prevent continuous execs from interfering on an existing fade - if (!(line->flags & ML_EFFECT5) - && po->thinker - && po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade) - { - CONS_Debug(DBG_POLYOBJ, "Line type 492 Executor: Fade PolyObject thinker already exists\n"); - return 0; - } - - pfd.polyObjNum = polyObjNum; - - // If Front X Offset is specified, use that. Else, use floorheight. - value = (sides[line->sidenum[0]].textureoffset ? sides[line->sidenum[0]].textureoffset : line->frontsector->floorheight) >> FRACBITS; - - // If DONTPEGBOTTOM, specify raw translucency value. Else, take it out of 1000. - if (!(line->flags & ML_DONTPEGBOTTOM)) - value /= 100; - - if (line->flags & ML_EFFECT3) // relative calc - pfd.destvalue = po->translucency + value; - else - pfd.destvalue = value; - - pfd.destvalue = max(min(pfd.destvalue, NUMTRANSMAPS), 0); - - // already equal, nothing to do - if (po->translucency == pfd.destvalue) - return 1; - - pfd.docollision = !(line->flags & ML_BOUNCY); // do not handle collision flags - pfd.doghostfade = (line->flags & ML_EFFECT1); // do ghost fade (no collision flags during fade) - pfd.ticbased = (line->flags & ML_EFFECT4); // Speed = Tic Duration - - // allow Back Y Offset to be consistent with other fade specials - pfd.speed = (line->sidenum[1] != 0xFFFF && !sides[line->sidenum[0]].rowoffset) ? - abs(sides[line->sidenum[1]].rowoffset>>FRACBITS) - : abs(sides[line->sidenum[0]].rowoffset>>FRACBITS); - - - return EV_DoPolyObjFade(&pfd); -} - -// Parses arguments for parameterized polyobject waypoint movement -static boolean PolyWaypoint(line_t *line) -{ - polywaypointdata_t pwd; - - pwd.polyObjNum = Tag_FGet(&line->tags); - pwd.speed = sides[line->sidenum[0]].textureoffset / 8; - pwd.sequence = sides[line->sidenum[0]].rowoffset >> FRACBITS; // Sequence # - - // Behavior after reaching the last waypoint? - if (line->flags & ML_EFFECT3) - pwd.returnbehavior = PWR_WRAP; // Wrap back to first waypoint - else if (line->flags & ML_EFFECT2) - pwd.returnbehavior = PWR_COMEBACK; // Go through sequence in reverse - else - pwd.returnbehavior = PWR_STOP; // Stop - - // Flags - pwd.flags = 0; - if (line->flags & ML_EFFECT1) - pwd.flags |= PWF_REVERSE; - if (line->flags & ML_EFFECT4) - pwd.flags |= PWF_LOOP; - - return EV_DoPolyObjWaypoint(&pwd); -} - -// Parses arguments for parameterized polyobject rotate specials -static boolean PolyRotate(line_t *line) -{ - polyrotdata_t prd; - - prd.polyObjNum = Tag_FGet(&line->tags); - prd.speed = sides[line->sidenum[0]].textureoffset >> FRACBITS; // angular speed - prd.distance = sides[line->sidenum[0]].rowoffset >> FRACBITS; // angular distance - - // Polyobj_(OR_)RotateRight have dir == -1 - prd.direction = (line->special == 484 || line->special == 485) ? -1 : 1; - - // Polyobj_OR types have override set to true - prd.overRide = (line->special == 485 || line->special == 487); - - if (line->flags & ML_NOCLIMB) - prd.turnobjs = 0; - else if (line->flags & ML_EFFECT4) - prd.turnobjs = 2; - else - prd.turnobjs = 1; - - return EV_DoPolyObjRotate(&prd); -} - -// Parses arguments for polyobject flag waving special -static boolean PolyFlag(line_t *line) -{ - polyflagdata_t pfd; - - pfd.polyObjNum = Tag_FGet(&line->tags); - pfd.speed = P_AproxDistance(line->dx, line->dy) >> FRACBITS; - pfd.angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y) >> ANGLETOFINESHIFT; - pfd.momx = sides[line->sidenum[0]].textureoffset >> FRACBITS; - - return EV_DoPolyObjFlag(&pfd); -} - -// Parses arguments for parameterized polyobject move-by-sector-heights specials -static boolean PolyDisplace(line_t *line) -{ - polydisplacedata_t pdd; - - pdd.polyObjNum = Tag_FGet(&line->tags); - - pdd.controlSector = line->frontsector; - pdd.dx = line->dx>>8; - pdd.dy = line->dy>>8; - - return EV_DoPolyObjDisplace(&pdd); -} - - -// Parses arguments for parameterized polyobject rotate-by-sector-heights specials -static boolean PolyRotDisplace(line_t *line) -{ - polyrotdisplacedata_t pdd; - fixed_t anginter, distinter; - - pdd.polyObjNum = Tag_FGet(&line->tags); - pdd.controlSector = line->frontsector; - - // Rotate 'anginter' interval for each 'distinter' interval from the control sector. - // Use default values if not provided as fallback. - anginter = sides[line->sidenum[0]].rowoffset ? sides[line->sidenum[0]].rowoffset : 90*FRACUNIT; - distinter = sides[line->sidenum[0]].textureoffset ? sides[line->sidenum[0]].textureoffset : 128*FRACUNIT; - pdd.rotscale = FixedDiv(anginter, distinter); - - // Same behavior as other rotators when carrying things. - if (line->flags & ML_NOCLIMB) - pdd.turnobjs = 0; - else if (line->flags & ML_EFFECT4) - pdd.turnobjs = 2; - else - pdd.turnobjs = 1; - - return EV_DoPolyObjRotDisplace(&pdd); -} - - -// -// P_RunNightserizeExecutors -// -void P_RunNightserizeExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 323 || lines[i].special == 324) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -// -// P_RunDeNightserizeExecutors -// -void P_RunDeNightserizeExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 325 || lines[i].special == 326) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -// -// P_RunNightsLapExecutors -// -void P_RunNightsLapExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 327 || lines[i].special == 328) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -// -// P_RunNightsCapsuleTouchExecutors -// -void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if ((lines[i].special == 329 || lines[i].special == 330) - && ((entering && (lines[i].flags & ML_TFERLINE)) - || (!entering && !(lines[i].flags & ML_TFERLINE))) - && ((lines[i].flags & ML_DONTPEGTOP) - || (enoughspheres && !(lines[i].flags & ML_BOUNCY)) - || (!enoughspheres && (lines[i].flags & ML_BOUNCY)))) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -/** Finds minimum light from an adjacent sector. - * - * \param sector Sector to start in. - * \param max Maximum value to return. - * \return Minimum light value from an adjacent sector, or max if the minimum - * light value is greater than max. - */ -INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max) -{ - size_t i; - INT32 min = max; - line_t *line; - sector_t *check; - - 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; -} - -void T_ExecutorDelay(executor_t *e) -{ - if (--e->timer <= 0) - { - if (e->caller && P_MobjWasRemoved(e->caller)) // If the mobj died while we were delaying - P_SetTarget(&e->caller, NULL); // Call with no mobj! - P_ProcessLineSpecial(e->line, e->caller, e->sector); - P_SetTarget(&e->caller, NULL); // Let the mobj know it can be removed now. - P_RemoveThinker(&e->thinker); - } -} - -static void P_AddExecutorDelay(line_t *line, mobj_t *mobj, sector_t *sector) -{ - executor_t *e; - INT32 delay; - - if (udmf) - delay = line->executordelay; - else - { - if (!line->backsector) - I_Error("P_AddExecutorDelay: Line has no backsector!\n"); - - delay = (line->backsector->ceilingheight >> FRACBITS) + (line->backsector->floorheight >> FRACBITS); - } - - e = Z_Calloc(sizeof (*e), PU_LEVSPEC, NULL); - - e->thinker.function.acp1 = (actionf_p1)T_ExecutorDelay; - e->line = line; - e->sector = sector; - e->timer = delay; - P_SetTarget(&e->caller, mobj); // Use P_SetTarget to make sure the mobj doesn't get freed while we're delaying. - P_AddThinker(THINK_MAIN, &e->thinker); -} - -/** Used by P_RunTriggerLinedef to check a NiGHTS trigger linedef's conditions - * - * \param triggerline Trigger linedef to check conditions for; should NEVER be NULL. - * \param actor Object initiating the action; should not be NULL. - * \sa P_RunTriggerLinedef - */ -static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) -{ - INT16 specialtype = triggerline->special; - size_t i; - - UINT8 inputmare = max(0, min(255, sides[triggerline->sidenum[0]].textureoffset>>FRACBITS)); - UINT8 inputlap = max(0, min(255, sides[triggerline->sidenum[0]].rowoffset>>FRACBITS)); - - boolean ltemare = triggerline->flags & ML_NOCLIMB; - boolean gtemare = triggerline->flags & ML_BLOCKMONSTERS; - boolean ltelap = triggerline->flags & ML_EFFECT1; - boolean gtelap = triggerline->flags & ML_EFFECT2; - - boolean lapfrombonustime = triggerline->flags & ML_EFFECT3; - boolean perglobalinverse = triggerline->flags & ML_DONTPEGBOTTOM; - boolean perglobal = !(triggerline->flags & ML_EFFECT4) && !perglobalinverse; - - boolean donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares) - boolean fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights - boolean fromnights = triggerline->flags & ML_DONTPEGTOP; // nightserize: from nights // denightserize: >0 players are nights - - UINT8 currentmare = UINT8_MAX; - UINT8 currentlap = UINT8_MAX; - - // Do early returns for Nightserize - if (specialtype >= 323 && specialtype <= 324) - { - // run only when no mares are found - if (donomares && P_FindLowestMare() != UINT8_MAX) - return false; - - // run only if there is a mare present - if (!donomares && P_FindLowestMare() == UINT8_MAX) - return false; - - // run only if player is nightserizing from non-nights - if (fromnonights) - { - if (!actor->player) - return false; - else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE) - return false; - } - // run only if player is nightserizing from nights - else if (fromnights) - { - if (!actor->player) - return false; - else if (actor->player->powers[pw_carry] != CR_NIGHTSMODE) - return false; - } - } - - // Get current mare and lap (and check early return for DeNightserize) - if (perglobal || perglobalinverse - || (specialtype >= 325 && specialtype <= 326 && (fromnonights || fromnights))) - { - UINT8 playersarenights = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 lap; - if (!playeringame[i] || players[i].spectator) - continue; - - // denightserize: run only if all players are not nights - if (specialtype >= 325 && specialtype <= 326 && fromnonights - && players[i].powers[pw_carry] == CR_NIGHTSMODE) - return false; - - // count number of nights players for denightserize return - if (specialtype >= 325 && specialtype <= 326 && fromnights - && players[i].powers[pw_carry] == CR_NIGHTSMODE) - playersarenights++; - - lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; - - // get highest mare/lap of players - if (perglobal) - { - if (players[i].mare > currentmare || currentmare == UINT8_MAX) - { - currentmare = players[i].mare; - currentlap = UINT8_MAX; - } - if (players[i].mare == currentmare - && (lap > currentlap || currentlap == UINT8_MAX)) - currentlap = lap; - } - // get lowest mare/lap of players - else if (perglobalinverse) - { - if (players[i].mare < currentmare || currentmare == UINT8_MAX) - { - currentmare = players[i].mare; - currentlap = UINT8_MAX; - } - if (players[i].mare == currentmare - && (lap < currentlap || currentlap == UINT8_MAX)) - currentlap = lap; - } - } - - // denightserize: run only if >0 players are nights - if (specialtype >= 325 && specialtype <= 326 && fromnights - && playersarenights < 1) - return false; - } - // get current mare/lap from triggering player - else if (!perglobal && !perglobalinverse) - { - if (!actor->player) - return false; - currentmare = actor->player->mare; - currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap; - } - - if (lapfrombonustime && !currentlap) - return false; // special case: player->marebonuslap is 0 until passing through on bonus time. Don't trigger lines looking for inputlap 0. - - // Compare current mare/lap to input mare/lap based on rules - if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far - && ((ltemare && currentmare > inputmare) - || (gtemare && currentmare < inputmare) - || (!ltemare && !gtemare && currentmare != inputmare) - || (ltelap && currentlap > inputlap) - || (gtelap && currentlap < inputlap) - || (!ltelap && !gtelap && currentlap != inputlap)) - ) - return false; - - return true; -} - -/** Used by P_LinedefExecute to check a trigger linedef's conditions - * The linedef executor specials in the trigger linedef's sector are run if all conditions are met. - * Return false cancels P_LinedefExecute, this happens if a condition is not met. - * - * \param triggerline Trigger linedef to check conditions for; should NEVER be NULL. - * \param actor Object initiating the action; should not be NULL. - * \param caller Sector in which the action was started. May be NULL. - * \sa P_ProcessLineSpecial, P_LinedefExecute - */ -boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller) -{ - sector_t *ctlsector; - fixed_t dist = P_AproxDistance(triggerline->dx, triggerline->dy)>>FRACBITS; - size_t i, linecnt, sectori; - INT16 specialtype = triggerline->special; - - ///////////////////////////////////////////////// - // Distance-checking/sector trigger conditions // - ///////////////////////////////////////////////// - - // Linetypes 303 and 304 require a specific - // number, or minimum or maximum, of rings. - if (specialtype == 303 || specialtype == 304) - { - fixed_t rings = 0; - - // With the passuse flag, count all player's - // rings. - if (triggerline->flags & ML_EFFECT4) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo || ((maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings) <= 0) - continue; - - rings += (maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings; - } - } - else - { - if (!(actor && actor->player)) - return false; // no player to count rings from here, sorry - - rings = (maptol & TOL_NIGHTS) ? actor->player->spheres : actor->player->rings; - } - - if (triggerline->flags & ML_NOCLIMB) - { - if (rings > dist) - return false; - } - else if (triggerline->flags & ML_BLOCKMONSTERS) - { - if (rings < dist) - return false; - } - else - { - if (rings != dist) - return false; - } - } - else if (specialtype >= 314 && specialtype <= 315) - { - msecnode_t *node; - mobj_t *mo; - INT32 numpush = 0; - INT32 numneeded = dist; - - if (!caller) - return false; // we need a calling sector to find pushables in, silly! - - // Count the pushables in this sector - node = caller->touching_thinglist; // things touching this sector - while (node) - { - mo = node->m_thing; - if ((mo->flags & MF_PUSHABLE) || ((mo->info->flags & MF_PUSHABLE) && mo->fuse)) - numpush++; - node = node->m_thinglist_next; - } - - if (triggerline->flags & ML_NOCLIMB) // Need at least or more - { - if (numpush < numneeded) - return false; - } - else if (triggerline->flags & ML_EFFECT4) // Need less than - { - if (numpush >= numneeded) - return false; - } - else // Need exact - { - if (numpush != numneeded) - return false; - } - } - else if (caller) - { - if (GETSECSPECIAL(caller->special, 2) == 6) - { - if (!(ALL7EMERALDS(emeralds))) - return false; - } - else if (GETSECSPECIAL(caller->special, 2) == 7) - { - UINT8 mare; - - if (!(maptol & TOL_NIGHTS)) - return false; - - mare = P_FindLowestMare(); - - if (triggerline->flags & ML_NOCLIMB) - { - if (!(mare <= dist)) - return false; - } - else if (triggerline->flags & ML_BLOCKMONSTERS) - { - if (!(mare >= dist)) - return false; - } - else - { - if (!(mare == dist)) - return false; - } - } - // If we were not triggered by a sector type especially for the purpose, - // a Linedef Executor linedef trigger is not handling sector triggers properly, return. - - else if ((!GETSECSPECIAL(caller->special, 2) || GETSECSPECIAL(caller->special, 2) > 7) && (specialtype > 322)) - { - CONS_Alert(CONS_WARNING, - M_GetText("Linedef executor trigger isn't handling sector triggers properly!\nspecialtype = %d, if you are not a dev, report this warning instance\nalong with the wad that caused it!\n"), - specialtype); - return false; - } - } - - ////////////////////////////////////// - // Miscellaneous trigger conditions // - ////////////////////////////////////// - - switch (specialtype) - { - case 305: // continuous - case 306: // each time - case 307: // once - if (!(actor && actor->player && actor->player->charability == dist/10)) - return false; - break; - case 309: // continuous - case 310: // each time - // Only red team members can activate this. - if (!(actor && actor->player && actor->player->ctfteam == 1)) - return false; - break; - case 311: // continuous - case 312: // each time - // Only blue team members can activate this. - if (!(actor && actor->player && actor->player->ctfteam == 2)) - return false; - break; - case 317: // continuous - case 318: // once - { // Unlockable triggers required - INT32 trigid = (INT32)(sides[triggerline->sidenum[0]].textureoffset>>FRACBITS); - - if ((modifiedgame && !savemoddata) || (netgame || multiplayer)) - return false; - else if (trigid < 0 || trigid > 31) // limited by 32 bit variable - { - CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid); - return false; - } - else if (!(unlocktriggers & (1 << trigid))) - return false; - } - break; - case 319: // continuous - case 320: // once - { // An unlockable itself must be unlocked! - INT32 unlockid = (INT32)(sides[triggerline->sidenum[0]].textureoffset>>FRACBITS); - - if ((modifiedgame && !savemoddata) || (netgame || multiplayer)) - return false; - else if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count - { - CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid); - return false; - } - else if (!(unlockables[unlockid-1].unlocked)) - return false; - } - break; - case 321: // continuous - case 322: // each time - // decrement calls left before triggering - if (triggerline->callcount > 0) - { - if (--triggerline->callcount > 0) - return false; - } - break; - case 323: // nightserize - each time - case 324: // nightserize - once - case 325: // denightserize - each time - case 326: // denightserize - once - case 327: // nights lap - each time - case 328: // nights lap - once - case 329: // nights egg capsule touch - each time - case 330: // nights egg capsule touch - once - if (!P_CheckNightsTriggerLine(triggerline, actor)) - return false; - break; - case 331: // continuous - case 332: // each time - case 333: // once - if (!(actor && actor->player && ((stricmp(triggerline->text, skins[actor->player->skin].name) == 0) ^ ((triggerline->flags & ML_NOCLIMB) == ML_NOCLIMB)))) - return false; - break; - case 334: // object dye - continuous - case 335: // object dye - each time - case 336: // object dye - once - { - INT32 triggercolor = (INT32)sides[triggerline->sidenum[0]].toptexture; - UINT16 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); - boolean invert = (triggerline->flags & ML_NOCLIMB ? true : false); - - if (invert ^ (triggercolor != color)) - return false; - } - default: - break; - } - - ///////////////////////////////// - // Processing linedef specials // - ///////////////////////////////// - - ctlsector = triggerline->frontsector; - sectori = (size_t)(ctlsector - sectors); - linecnt = ctlsector->linecount; - - if (triggerline->flags & ML_EFFECT5) // disregard order for efficiency - { - for (i = 0; i < linecnt; i++) - if (ctlsector->lines[i]->special >= 400 - && ctlsector->lines[i]->special < 500) - { - if (ctlsector->lines[i]->executordelay) - P_AddExecutorDelay(ctlsector->lines[i], actor, caller); - else - P_ProcessLineSpecial(ctlsector->lines[i], actor, caller); - } - } - else // walk around the sector in a defined order - { - boolean backwards = false; - size_t j, masterlineindex = (size_t)-1; - - for (i = 0; i < linecnt; i++) - if (ctlsector->lines[i] == triggerline) - { - masterlineindex = i; - break; - } - -#ifdef PARANOIA - if (masterlineindex == (size_t)-1) - { - const size_t li = (size_t)(ctlsector->lines[i] - lines); - I_Error("Line %s isn't linked into its front sector", sizeu1(li)); - } -#endif - - // i == masterlineindex - for (;;) - { - if (backwards) // v2 to v1 - { - for (j = 0; j < linecnt; j++) - { - if (i == j) - continue; - if (ctlsector->lines[i]->v1 == ctlsector->lines[j]->v2) - { - i = j; - break; - } - if (ctlsector->lines[i]->v1 == ctlsector->lines[j]->v1) - { - i = j; - backwards = false; - break; - } - } - if (j == linecnt) - { - const size_t vertexei = (size_t)(ctlsector->lines[i]->v1 - vertexes); - CONS_Debug(DBG_GAMELOGIC, "Warning: Sector %s is not closed at vertex %s (%d, %d)\n", - sizeu1(sectori), sizeu2(vertexei), ctlsector->lines[i]->v1->x, ctlsector->lines[i]->v1->y); - return false; // abort - } - } - else // v1 to v2 - { - for (j = 0; j < linecnt; j++) - { - if (i == j) - continue; - if (ctlsector->lines[i]->v2 == ctlsector->lines[j]->v1) - { - i = j; - break; - } - if (ctlsector->lines[i]->v2 == ctlsector->lines[j]->v2) - { - i = j; - backwards = true; - break; - } - } - if (j == linecnt) - { - const size_t vertexei = (size_t)(ctlsector->lines[i]->v1 - vertexes); - CONS_Debug(DBG_GAMELOGIC, "Warning: Sector %s is not closed at vertex %s (%d, %d)\n", - sizeu1(sectori), sizeu2(vertexei), ctlsector->lines[i]->v2->x, ctlsector->lines[i]->v2->y); - return false; // abort - } - } - - if (i == masterlineindex) - break; - - if (ctlsector->lines[i]->special >= 400 - && ctlsector->lines[i]->special < 500) - { - if (ctlsector->lines[i]->executordelay) - P_AddExecutorDelay(ctlsector->lines[i], actor, caller); - else - P_ProcessLineSpecial(ctlsector->lines[i], actor, caller); - } - } - } - - // "Trigger on X calls" linedefs reset if noclimb is set - if ((specialtype == 321 || specialtype == 322) && triggerline->flags & ML_NOCLIMB) - triggerline->callcount = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; - else - // These special types work only once - if (specialtype == 302 // Once - || specialtype == 304 // Ring count - Once - || specialtype == 307 // Character ability - Once - || specialtype == 308 // Race only - Once - || specialtype == 313 // No More Enemies - Once - || specialtype == 315 // No of pushables - Once - || specialtype == 318 // Unlockable trigger - Once - || specialtype == 320 // Unlockable - Once - || specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time - || specialtype == 324 // Nightserize - Once - || specialtype == 326 // DeNightserize - Once - || specialtype == 328 // Nights lap - Once - || specialtype == 330 // Nights Bonus Time - Once - || specialtype == 333 // Skin - Once - || specialtype == 336 // Dye - Once - || specialtype == 399) // Level Load - triggerline->special = 0; // Clear it out - - return true; -} - -/** Runs a linedef executor. - * Can be called by: - * - a player moving into a special sector or FOF. - * - a pushable object moving into a special sector or FOF. - * - a ceiling or floor movement from a previous linedef executor finishing. - * - any object in a state with the A_LinedefExecute() action. - * - * \param tag Tag of the linedef executor to run. - * \param actor Object initiating the action; should not be NULL. - * \param caller Sector in which the action was started. May be NULL. - * \sa P_ProcessLineSpecial, P_RunTriggerLinedef - * \author Graue - */ -void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller) -{ - size_t masterline; - - CONS_Debug(DBG_GAMELOGIC, "P_LinedefExecute: Executing trigger linedefs of tag %d\n", tag); - - I_Assert(!actor || !P_MobjWasRemoved(actor)); // If actor is there, it must be valid. - - for (masterline = 0; masterline < numlines; masterline++) - { - if (Tag_FGet(&lines[masterline].tags) != tag) - continue; - - // "No More Enemies" and "Level Load" take care of themselves. - if (lines[masterline].special == 313 - || lines[masterline].special == 399 - // Each-time executors handle themselves, too - || lines[masterline].special == 301 // Each time - || lines[masterline].special == 306 // Character ability - Each time - || lines[masterline].special == 310 // CTF Red team - Each time - || lines[masterline].special == 312 // CTF Blue team - Each time - || lines[masterline].special == 322 // Trigger on X calls - Each Time - || lines[masterline].special == 332 // Skin - Each time - || lines[masterline].special == 335)// Dye - Each time - continue; - - if (lines[masterline].special < 300 - || lines[masterline].special > 399) - continue; - - if (!P_RunTriggerLinedef(&lines[masterline], actor, caller)) - return; // cancel P_LinedefExecute if function returns false - } -} - -// -// P_SwitchWeather -// -// Switches the weather! -// -void P_SwitchWeather(INT32 weathernum) -{ - boolean purge = false; - INT32 swap = 0; - - switch (weathernum) - { - case PRECIP_NONE: // None - if (curWeather == PRECIP_NONE) - return; // Nothing to do. - purge = true; - break; - case PRECIP_STORM: // Storm - case PRECIP_STORM_NOSTRIKES: // Storm w/ no lightning - case PRECIP_RAIN: // Rain - if (curWeather == PRECIP_SNOW || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_RAIN; - break; - case PRECIP_SNOW: // Snow - if (curWeather == PRECIP_SNOW) - return; // Nothing to do. - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_SNOW; // Need to delete the other precips. - break; - case PRECIP_STORM_NORAIN: // Storm w/o rain - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN - || curWeather == PRECIP_BLANK) - swap = PRECIP_STORM_NORAIN; - else if (curWeather == PRECIP_STORM_NORAIN) - return; - break; - case PRECIP_BLANK: - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_BLANK) - return; - break; - default: - CONS_Debug(DBG_GAMELOGIC, "P_SwitchWeather: Unknown weather type %d.\n", weathernum); - break; - } - - if (purge) - { - thinker_t *think; - precipmobj_t *precipmobj; - - for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next) - { - if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) - continue; // not a precipmobj thinker - - precipmobj = (precipmobj_t *)think; - - P_RemovePrecipMobj(precipmobj); - } - } - else if (swap && !((swap == PRECIP_BLANK && curWeather == PRECIP_STORM_NORAIN) || (swap == PRECIP_STORM_NORAIN && curWeather == PRECIP_BLANK))) // Rather than respawn all that crap, reuse it! - { - thinker_t *think; - precipmobj_t *precipmobj; - state_t *st; - - for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next) - { - if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) - continue; // not a precipmobj thinker - precipmobj = (precipmobj_t *)think; - - if (swap == PRECIP_RAIN) // Snow To Rain - { - precipmobj->flags = mobjinfo[MT_RAIN].flags; - st = &states[mobjinfo[MT_RAIN].spawnstate]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_RAIN].speed; - - precipmobj->precipflags &= ~PCF_INVISIBLE; - - precipmobj->precipflags |= PCF_RAIN; - //think->function.acp1 = (actionf_p1)P_RainThinker; - } - else if (swap == PRECIP_SNOW) // Rain To Snow - { - INT32 z; - - precipmobj->flags = mobjinfo[MT_SNOWFLAKE].flags; - z = M_RandomByte(); - - if (z < 64) - z = 2; - else if (z < 144) - z = 1; - else - z = 0; - - st = &states[mobjinfo[MT_SNOWFLAKE].spawnstate+z]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_SNOWFLAKE].speed; - - precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN); - - //think->function.acp1 = (actionf_p1)P_SnowThinker; - } - else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse. - { - //think->function.acp1 = (actionf_p1)P_NullPrecipThinker; - - precipmobj->precipflags |= PCF_INVISIBLE; - } - } - } - - switch (weathernum) - { - case PRECIP_SNOW: // snow - curWeather = PRECIP_SNOW; - - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_RAIN: // rain - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_RAIN; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM: // storm - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM_NOSTRIKES; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NORAIN: // storm w/o rain - curWeather = PRECIP_STORM_NORAIN; - - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_BLANK: - curWeather = PRECIP_BLANK; - - if (!swap) - P_SpawnPrecipitation(); - - break; - default: - curWeather = PRECIP_NONE; - break; - } -} - -/** Gets an object. - * - * \param type Object type to look for. - * \param s Sector number to look in. - * \return Pointer to the first ::type found in the sector. - * \sa P_GetPushThing - */ -static mobj_t *P_GetObjectTypeInSectorNum(mobjtype_t type, size_t s) -{ - sector_t *sec = sectors + s; - mobj_t *thing = sec->thinglist; - - while (thing) - { - if (thing->type == type) - return thing; - thing = thing->snext; - } - return NULL; -} - -/** Processes the line special triggered by an object. - * - * \param line Line with the special command on it. - * \param mo mobj that triggered the line. Must be valid and non-NULL. - * \param callsec sector in which action was initiated; this can be NULL. - * Because of the A_LinedefExecute() action, even if non-NULL, - * this sector might not have the same tag as the linedef executor, - * and it might not have the linedef executor sector type. - * \todo Handle mo being NULL gracefully. T_MoveFloor() and T_MoveCeiling() - * don't have an object to pass. - * \todo Split up into multiple functions. - * \sa P_LinedefExecute - * \author Graue - */ -static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) -{ - INT32 secnum = -1; - mobj_t *bot = NULL; - mtag_t tag = Tag_FGet(&line->tags); - - I_Assert(!mo || !P_MobjWasRemoved(mo)); // If mo is there, mo must be valid! - - if (mo && mo->player && botingame) - bot = players[secondarydisplayplayer].mo; - - // note: only commands with linedef types >= 400 && < 500 can be used - switch (line->special) - { - case 400: // Set tagged sector's floor height/pic - EV_DoFloor(line, instantMoveFloorByFrontSector); - break; - - case 401: // Set tagged sector's ceiling height/pic - EV_DoCeiling(line, instantMoveCeilingByFrontSector); - break; - - case 402: // Set tagged sector's light level - { - INT16 newlightlevel; - INT32 newfloorlightsec, newceilinglightsec; - - newlightlevel = line->frontsector->lightlevel; - newfloorlightsec = line->frontsector->floorlightsec; - newceilinglightsec = line->frontsector->ceilinglightsec; - - // act on all sectors with the same tag as the triggering linedef - TAG_ITER_SECTORS(tag, secnum) - { - if (sectors[secnum].lightingdata) - { - // Stop the lighting madness going on in this sector! - P_RemoveThinker(&((elevator_t *)sectors[secnum].lightingdata)->thinker); - sectors[secnum].lightingdata = NULL; - - // No, it's not an elevator_t, but any struct with a thinker_t named - // 'thinker' at the beginning will do here. (We don't know what it - // actually is: could be lightlevel_t, fireflicker_t, glow_t, etc.) - } - - sectors[secnum].lightlevel = newlightlevel; - sectors[secnum].floorlightsec = newfloorlightsec; - sectors[secnum].ceilinglightsec = newceilinglightsec; - } - } - break; - - case 403: // Move floor, linelen = speed, frontsector floor = dest height - EV_DoFloor(line, moveFloorByFrontSector); - break; - - case 404: // Move ceiling, linelen = speed, frontsector ceiling = dest height - EV_DoCeiling(line, moveCeilingByFrontSector); - break; - - case 405: // Move floor by front side texture offsets, offset x = speed, offset y = amount to raise/lower - EV_DoFloor(line, moveFloorByFrontTexture); - break; - - case 407: // Move ceiling by front side texture offsets, offset x = speed, offset y = amount to raise/lower - EV_DoCeiling(line, moveCeilingByFrontTexture); - break; - -/* case 405: // Lower floor by line, dx = speed, dy = amount to lower - EV_DoFloor(line, lowerFloorByLine); - break; - - case 406: // Raise floor by line, dx = speed, dy = amount to raise - EV_DoFloor(line, raiseFloorByLine); - break; - - case 407: // Lower ceiling by line, dx = speed, dy = amount to lower - EV_DoCeiling(line, lowerCeilingByLine); - break; - - case 408: // Raise ceiling by line, dx = speed, dy = amount to raise - EV_DoCeiling(line, raiseCeilingByLine); - break;*/ - - case 409: // Change tagged sectors' tag - // (formerly "Change calling sectors' tag", but behavior was changed) - { - TAG_ITER_SECTORS(tag, secnum) - Tag_SectorFSet(secnum,(INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS)); - break; - } - - case 410: // Change front sector's tag - Tag_SectorFSet((UINT32)(line->frontsector - sectors), (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS)); - break; - - case 411: // Stop floor/ceiling movement in tagged sector(s) - TAG_ITER_SECTORS(tag, secnum) - { - if (sectors[secnum].floordata) - { - if (sectors[secnum].floordata == sectors[secnum].ceilingdata) // elevator - { - P_RemoveThinker(&((elevator_t *)sectors[secnum].floordata)->thinker); - sectors[secnum].floordata = sectors[secnum].ceilingdata = NULL; - sectors[secnum].floorspeed = sectors[secnum].ceilspeed = 0; - } - else // floormove - { - P_RemoveThinker(&((floormove_t *)sectors[secnum].floordata)->thinker); - sectors[secnum].floordata = NULL; - sectors[secnum].floorspeed = 0; - } - } - - if (sectors[secnum].ceilingdata) // ceiling - { - P_RemoveThinker(&((ceiling_t *)sectors[secnum].ceilingdata)->thinker); - sectors[secnum].ceilingdata = NULL; - sectors[secnum].ceilspeed = 0; - } - } - break; - - case 412: // Teleport the player or thing - { - mobj_t *dest; - - if (!mo) // nothing to teleport - return; - - if (line->flags & ML_EFFECT3) // Relative silent teleport - { - fixed_t x, y, z; - - x = sides[line->sidenum[0]].textureoffset; - y = sides[line->sidenum[0]].rowoffset; - z = line->frontsector->ceilingheight; - - P_UnsetThingPosition(mo); - mo->x += x; - mo->y += y; - mo->z += z; - P_SetThingPosition(mo); - - if (mo->player) - { - if (bot) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3 - P_TeleportMove(bot, bot->x + x, bot->y + y, bot->z + z); - if (splitscreen && mo->player == &players[secondarydisplayplayer] && camera2.chase) - { - camera2.x += x; - camera2.y += y; - camera2.z += z; - camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); - } - else if (camera.chase && mo->player == &players[displayplayer]) - { - camera.x += x; - camera.y += y; - camera.z += z; - camera.subsector = R_PointInSubsector(camera.x, camera.y); - } - } - } - else - { - if ((secnum = Tag_Iterate_Sectors(tag, 0)) < 0) - return; - - dest = P_GetObjectTypeInSectorNum(MT_TELEPORTMAN, secnum); - if (!dest) - return; - - if (bot) - P_Teleport(bot, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, (line->flags & ML_BLOCKMONSTERS) == 0, (line->flags & ML_EFFECT4) == ML_EFFECT4); - if (line->flags & ML_BLOCKMONSTERS) - P_Teleport(mo, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, false, (line->flags & ML_EFFECT4) == ML_EFFECT4); - else - { - P_Teleport(mo, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, true, (line->flags & ML_EFFECT4) == ML_EFFECT4); - // Play the 'bowrwoosh!' sound - S_StartSound(dest, sfx_mixup); - } - } - } - break; - - case 413: // Change music - // console player only unless NOCLIMB is set - if ((line->flags & ML_NOCLIMB) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction) - { - boolean musicsame = (!sides[line->sidenum[0]].text[0] || !strnicmp(sides[line->sidenum[0]].text, S_MusicName(), 7)); - UINT16 tracknum = (UINT16)max(sides[line->sidenum[0]].bottomtexture, 0); - INT32 position = (INT32)max(sides[line->sidenum[0]].midtexture, 0); - UINT32 prefadems = (UINT32)max(sides[line->sidenum[0]].textureoffset >> FRACBITS, 0); - UINT32 postfadems = (UINT32)max(sides[line->sidenum[0]].rowoffset >> FRACBITS, 0); - UINT8 fadetarget = (UINT8)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].textureoffset >> FRACBITS : 0, 0); - INT16 fadesource = (INT16)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].rowoffset >> FRACBITS : -1, -1); - - // Seek offset from current song position - if (line->flags & ML_EFFECT1) - { - // adjust for loop point if subtracting - if (position < 0 && S_GetMusicLength() && - S_GetMusicPosition() > S_GetMusicLoopPoint() && - S_GetMusicPosition() + position < S_GetMusicLoopPoint()) - position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0); - else - position = max(S_GetMusicPosition() + position, 0); - } - - // Fade current music to target volume (if music won't be changed) - if ((line->flags & ML_EFFECT2) && fadetarget && musicsame) - { - // 0 fadesource means fade from current volume. - // meaning that we can't specify volume 0 as the source volume -- this starts at 1. - if (!fadesource) - fadesource = -1; - - if (!postfadems) - S_SetInternalMusicVolume(fadetarget); - else - S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); - - if (position) - S_SetMusicPosition(position); - } - // Change the music and apply position/fade operations - else - { - strncpy(mapmusname, sides[line->sidenum[0]].text, 7); - mapmusname[6] = 0; - - mapmusflags = tracknum & MUSIC_TRACKMASK; - if (!(line->flags & ML_BLOCKMONSTERS)) - mapmusflags |= MUSIC_RELOADRESET; - if (line->flags & ML_BOUNCY) - mapmusflags |= MUSIC_FORCERESET; - - mapmusposition = position; - - S_ChangeMusicEx(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4), position, - !(line->flags & ML_EFFECT2) ? prefadems : 0, - !(line->flags & ML_EFFECT2) ? postfadems : 0); - - if ((line->flags & ML_EFFECT2) && fadetarget) - { - if (!postfadems) - S_SetInternalMusicVolume(fadetarget); - else - S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); - } - } - - // Except, you can use the ML_BLOCKMONSTERS flag to change this behavior. - // if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn. - } - break; - - case 414: // Play SFX - { - INT32 sfxnum; - - sfxnum = sides[line->sidenum[0]].toptexture; - - if (sfxnum == sfx_None) - return; // Do nothing! - if (sfxnum < sfx_None || sfxnum >= NUMSFX) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 414 Executor: sfx number %d is invalid!\n", sfxnum); - return; - } - - if (tag != 0) // Do special stuff only if a non-zero linedef tag is set - { - // Play sounds from tagged sectors' origins. - if (line->flags & ML_EFFECT5) // Repeat Midtexture - { - // Additionally play the sound from tagged sectors' soundorgs - sector_t *sec; - - TAG_ITER_SECTORS(tag, secnum) - { - sec = §ors[secnum]; - S_StartSound(&sec->soundorg, sfxnum); - } - } - - // Play the sound without origin for anyone, as long as they're inside tagged areas. - else - { - UINT8 i = 0; - mobj_t* camobj = players[displayplayer].mo; - ffloor_t *rover; - boolean foundit = false; - - for (i = 0; i < 2; camobj = players[secondarydisplayplayer].mo, i++) - { - if (!camobj) - continue; - - if (foundit || Tag_Find(&camobj->subsector->sector->tags, tag)) - { - foundit = true; - break; - } - - // Only trigger if mobj is touching the tag - for(rover = camobj->subsector->sector->ffloors; rover; rover = rover->next) - { - if (!Tag_Find(&rover->master->frontsector->tags, tag)) - continue; - - if (camobj->z > P_GetSpecialTopZ(camobj, sectors + rover->secnum, camobj->subsector->sector)) - continue; - - if (camobj->z + camobj->height < P_GetSpecialBottomZ(camobj, sectors + rover->secnum, camobj->subsector->sector)) - continue; - - foundit = true; - break; - } - } - - if (foundit) - S_StartSound(NULL, sfxnum); - } - } - else - { - if (line->flags & ML_NOCLIMB) - { - // play the sound from nowhere, but only if display player triggered it - if (mo && mo->player && (mo->player == &players[displayplayer] || mo->player == &players[secondarydisplayplayer])) - S_StartSound(NULL, sfxnum); - } - else if (line->flags & ML_EFFECT4) - { - // play the sound from nowhere - S_StartSound(NULL, sfxnum); - } - else if (line->flags & ML_BLOCKMONSTERS) - { - // play the sound from calling sector's soundorg - if (callsec) - S_StartSound(&callsec->soundorg, sfxnum); - else if (mo) - S_StartSound(&mo->subsector->sector->soundorg, sfxnum); - } - else if (mo) - { - // play the sound from mobj that triggered it - S_StartSound(mo, sfxnum); - } - } - } - break; - - case 415: // Run a script - if (cv_runscripts.value) - { - INT32 scrnum; - lumpnum_t lumpnum; - char newname[9]; - - strcpy(newname, G_BuildMapName(gamemap)); - newname[0] = 'S'; - newname[1] = 'C'; - newname[2] = 'R'; - - scrnum = sides[line->sidenum[0]].textureoffset>>FRACBITS; - if (scrnum < 0 || scrnum > 999) - { - scrnum = 0; - newname[5] = newname[6] = newname[7] = '0'; - } - else - { - newname[5] = (char)('0' + (char)((scrnum/100))); - newname[6] = (char)('0' + (char)((scrnum%100)/10)); - newname[7] = (char)('0' + (char)(scrnum%10)); - } - newname[8] = '\0'; - - lumpnum = W_CheckNumForName(newname); - - if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0) - { - CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname); - } - else - COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); - } - break; - - case 416: // Spawn adjustable fire flicker - TAG_ITER_SECTORS(tag, secnum) - { - if (line->flags & ML_NOCLIMB && line->backsector) - { - // Use front sector for min light level, back sector for max. - // This is tricky because P_SpawnAdjustableFireFlicker expects - // the maxsector (second argument) to also be the target - // sector, so we have to do some light level twiddling. - fireflicker_t *flick; - INT16 reallightlevel = sectors[secnum].lightlevel; - sectors[secnum].lightlevel = line->backsector->lightlevel; - - flick = P_SpawnAdjustableFireFlicker(line->frontsector, §ors[secnum], - P_AproxDistance(line->dx, line->dy)>>FRACBITS); - - // Make sure the starting light level is in range. - if (reallightlevel < flick->minlight) - reallightlevel = (INT16)flick->minlight; - else if (reallightlevel > flick->maxlight) - reallightlevel = (INT16)flick->maxlight; - - sectors[secnum].lightlevel = reallightlevel; - } - else - { - // Use front sector for min, target sector for max, - // the same way linetype 61 does it. - P_SpawnAdjustableFireFlicker(line->frontsector, §ors[secnum], - P_AproxDistance(line->dx, line->dy)>>FRACBITS); - } - } - break; - - case 417: // Spawn adjustable glowing light - TAG_ITER_SECTORS(tag, secnum) - { - if (line->flags & ML_NOCLIMB && line->backsector) - { - // Use front sector for min light level, back sector for max. - // This is tricky because P_SpawnAdjustableGlowingLight expects - // the maxsector (second argument) to also be the target - // sector, so we have to do some light level twiddling. - glow_t *glow; - INT16 reallightlevel = sectors[secnum].lightlevel; - sectors[secnum].lightlevel = line->backsector->lightlevel; - - glow = P_SpawnAdjustableGlowingLight(line->frontsector, §ors[secnum], - P_AproxDistance(line->dx, line->dy)>>FRACBITS); - - // Make sure the starting light level is in range. - if (reallightlevel < glow->minlight) - reallightlevel = (INT16)glow->minlight; - else if (reallightlevel > glow->maxlight) - reallightlevel = (INT16)glow->maxlight; - - sectors[secnum].lightlevel = reallightlevel; - } - else - { - // Use front sector for min, target sector for max, - // the same way linetype 602 does it. - P_SpawnAdjustableGlowingLight(line->frontsector, §ors[secnum], - P_AproxDistance(line->dx, line->dy)>>FRACBITS); - } - } - break; - - case 418: // Spawn adjustable strobe flash (unsynchronized) - TAG_ITER_SECTORS(tag, secnum) - { - if (line->flags & ML_NOCLIMB && line->backsector) - { - // Use front sector for min light level, back sector for max. - // This is tricky because P_SpawnAdjustableGlowingLight expects - // the maxsector (second argument) to also be the target - // sector, so we have to do some light level twiddling. - strobe_t *flash; - INT16 reallightlevel = sectors[secnum].lightlevel; - sectors[secnum].lightlevel = line->backsector->lightlevel; - - flash = P_SpawnAdjustableStrobeFlash(line->frontsector, §ors[secnum], - abs(line->dx)>>FRACBITS, abs(line->dy)>>FRACBITS, false); - - // Make sure the starting light level is in range. - if (reallightlevel < flash->minlight) - reallightlevel = (INT16)flash->minlight; - else if (reallightlevel > flash->maxlight) - reallightlevel = (INT16)flash->maxlight; - - sectors[secnum].lightlevel = reallightlevel; - } - else - { - // Use front sector for min, target sector for max, - // the same way linetype 602 does it. - P_SpawnAdjustableStrobeFlash(line->frontsector, §ors[secnum], - abs(line->dx)>>FRACBITS, abs(line->dy)>>FRACBITS, false); - } - } - break; - - case 419: // Spawn adjustable strobe flash (synchronized) - TAG_ITER_SECTORS(tag, secnum) - { - if (line->flags & ML_NOCLIMB && line->backsector) - { - // Use front sector for min light level, back sector for max. - // This is tricky because P_SpawnAdjustableGlowingLight expects - // the maxsector (second argument) to also be the target - // sector, so we have to do some light level twiddling. - strobe_t *flash; - INT16 reallightlevel = sectors[secnum].lightlevel; - sectors[secnum].lightlevel = line->backsector->lightlevel; - - flash = P_SpawnAdjustableStrobeFlash(line->frontsector, §ors[secnum], - abs(line->dx)>>FRACBITS, abs(line->dy)>>FRACBITS, true); - - // Make sure the starting light level is in range. - if (reallightlevel < flash->minlight) - reallightlevel = (INT16)flash->minlight; - else if (reallightlevel > flash->maxlight) - reallightlevel = (INT16)flash->maxlight; - - sectors[secnum].lightlevel = reallightlevel; - } - else - { - // Use front sector for min, target sector for max, - // the same way linetype 602 does it. - P_SpawnAdjustableStrobeFlash(line->frontsector, §ors[secnum], - abs(line->dx)>>FRACBITS, abs(line->dy)>>FRACBITS, true); - } - } - break; - - case 420: // Fade light levels in tagged sectors to new value - P_FadeLight(tag, - (line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].textureoffset>>FRACBITS, 0) : line->frontsector->lightlevel, - // failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset - // to be consistent with other light and fade specials - (line->flags & ML_DONTPEGBOTTOM) ? - ((line->sidenum[1] != 0xFFFF && !(sides[line->sidenum[0]].rowoffset>>FRACBITS)) ? - max(min(sides[line->sidenum[1]].rowoffset>>FRACBITS, 255), 0) - : max(min(sides[line->sidenum[0]].rowoffset>>FRACBITS, 255), 0)) - : abs(P_AproxDistance(line->dx, line->dy))>>FRACBITS, - (line->flags & ML_EFFECT4), - (line->flags & ML_EFFECT5)); - break; - - case 421: // Stop lighting effect in tagged sectors - TAG_ITER_SECTORS(tag, secnum) - if (sectors[secnum].lightingdata) - { - P_RemoveThinker(&((elevator_t *)sectors[secnum].lightingdata)->thinker); - sectors[secnum].lightingdata = NULL; - } - break; - - case 422: // Cut away to another view - { - mobj_t *altview; - - if ((!mo || !mo->player) && !titlemapinaction) // only players have views, and title screens - return; - - if ((secnum = Tag_Iterate_Sectors(tag, 0)) < 0) - return; - - altview = P_GetObjectTypeInSectorNum(MT_ALTVIEWMAN, secnum); - if (!altview) - return; - - // If titlemap, set the camera ref for title's thinker - // This is not revoked until overwritten; awayviewtics is ignored - if (titlemapinaction) - titlemapcameraref = altview; - else - { - P_SetTarget(&mo->player->awayviewmobj, altview); - mo->player->awayviewtics = P_AproxDistance(line->dx, line->dy)>>FRACBITS; - } - - - if (line->flags & ML_NOCLIMB) // lets you specify a vertical angle - { - INT32 aim; - - aim = sides[line->sidenum[0]].textureoffset>>FRACBITS; - aim = (aim + 360) % 360; - aim *= (ANGLE_90>>8); - aim /= 90; - aim <<= 8; - if (titlemapinaction) - titlemapcameraref->cusval = (angle_t)aim; - else - mo->player->awayviewaiming = (angle_t)aim; - } - else - { - // straight ahead - if (!titlemapinaction) - mo->player->awayviewaiming = 0; - // don't do cusval cause that's annoying - } - } - break; - - case 423: // Change Sky - if ((mo && mo->player && P_IsLocalPlayer(mo->player)) || (line->flags & ML_NOCLIMB)) - P_SetupLevelSky(sides[line->sidenum[0]].textureoffset>>FRACBITS, (line->flags & ML_NOCLIMB)); - break; - - case 424: // Change Weather - if (line->flags & ML_NOCLIMB) - { - globalweather = (UINT8)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - P_SwitchWeather(globalweather); - } - else if (mo && mo->player && P_IsLocalPlayer(mo->player)) - P_SwitchWeather(sides[line->sidenum[0]].textureoffset>>FRACBITS); - break; - - case 425: // Calls P_SetMobjState on calling mobj - if (mo && !mo->player) - P_SetMobjState(mo, sides[line->sidenum[0]].toptexture); //P_AproxDistance(line->dx, line->dy)>>FRACBITS); - break; - - case 426: // Moves the mobj to its sector's soundorg and on the floor, and stops it - if (!mo) - return; - - if (line->flags & ML_NOCLIMB) - { - P_UnsetThingPosition(mo); - mo->x = mo->subsector->sector->soundorg.x; - mo->y = mo->subsector->sector->soundorg.y; - mo->z = mo->floorz; - P_SetThingPosition(mo); - } - - mo->momx = mo->momy = mo->momz = 1; - mo->pmomz = 0; - - if (mo->player) - { - mo->player->rmomx = mo->player->rmomy = 1; - mo->player->cmomx = mo->player->cmomy = 0; - P_ResetPlayer(mo->player); - P_SetPlayerMobjState(mo, S_PLAY_STND); - - // Reset bot too. - if (bot) { - if (line->flags & ML_NOCLIMB) - P_TeleportMove(bot, mo->x, mo->y, mo->z); - bot->momx = bot->momy = bot->momz = 1; - bot->pmomz = 0; - bot->player->rmomx = bot->player->rmomy = 1; - bot->player->cmomx = bot->player->cmomy = 0; - P_ResetPlayer(bot->player); - P_SetPlayerMobjState(bot, S_PLAY_STND); - } - } - break; - - case 427: // Awards points if the mobj is a player - if (mo && mo->player) - P_AddPlayerScore(mo->player, sides[line->sidenum[0]].textureoffset>>FRACBITS); - break; - - case 428: // Start floating platform movement - EV_DoElevator(line, elevateContinuous, true); - break; - - case 429: // Crush Ceiling Down Once - EV_DoCrush(line, crushCeilOnce); - break; - - case 430: // Crush Floor Up Once - EV_DoFloor(line, crushFloorOnce); - break; - - case 431: // Crush Floor & Ceiling to middle Once - EV_DoCrush(line, crushBothOnce); - break; - - case 432: // Enable 2D Mode (Disable if noclimb) - if (mo && mo->player) - { - if (line->flags & ML_NOCLIMB) - mo->flags2 &= ~MF2_TWOD; - else - mo->flags2 |= MF2_TWOD; - - // Copy effect to bot if necessary - // (Teleport them to you so they don't break it.) - if (bot && (bot->flags2 & MF2_TWOD) != (mo->flags2 & MF2_TWOD)) { - bot->flags2 = (bot->flags2 & ~MF2_TWOD) | (mo->flags2 & MF2_TWOD); - P_TeleportMove(bot, mo->x, mo->y, mo->z); - } - } - break; - - case 433: // Flip gravity (Flop gravity if noclimb) Works on pushables, too! - if (line->flags & ML_NOCLIMB) - mo->flags2 &= ~MF2_OBJECTFLIP; - else - mo->flags2 |= MF2_OBJECTFLIP; - if (bot) - bot->flags2 = (bot->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); - break; - - case 434: // Custom Power - if (mo && mo->player) - { - mobj_t *dummy = P_SpawnMobj(mo->x, mo->y, mo->z, MT_NULL); - - var1 = sides[line->sidenum[0]].toptexture; //(line->dx>>FRACBITS)-1; - - if (line->sidenum[1] != 0xffff && line->flags & ML_BLOCKMONSTERS) // read power from back sidedef - var2 = sides[line->sidenum[1]].toptexture; - else if (line->flags & ML_NOCLIMB) // 'Infinite' - var2 = UINT16_MAX; - else - var2 = sides[line->sidenum[0]].textureoffset>>FRACBITS; - - P_SetTarget(&dummy->target, mo); - A_CustomPower(dummy); - - if (bot) { - P_SetTarget(&dummy->target, bot); - A_CustomPower(dummy); - } - P_RemoveMobj(dummy); - } - break; - - case 435: // Change scroller direction - { - scroll_t *scroller; - thinker_t *th; - - for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next) - { - if (th->function.acp1 != (actionf_p1)T_Scroll) - continue; - - scroller = (scroll_t *)th; - if (!Tag_Find(§ors[scroller->affectee].tags, tag)) - continue; - - scroller->dx = FixedMul(line->dx>>SCROLL_SHIFT, CARRYFACTOR); - scroller->dy = FixedMul(line->dy>>SCROLL_SHIFT, CARRYFACTOR); - } - } - break; - - case 436: // Shatter block remotely - { - INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible in - ffloor_t *rover; // FOF that we are going to crumble - boolean foundrover = false; // for debug, "Can't find a FOF" message - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 436 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - EV_CrumbleChain(sec, rover); - } - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 436 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - } - break; - - case 437: // Disable Player Controls - if (mo && mo->player) - { - UINT16 fractime = (UINT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - if (fractime < 1) - fractime = 1; //instantly wears off upon leaving - if (line->flags & ML_NOCLIMB) - fractime |= 1<<15; //more crazy &ing, as if music stuff wasn't enough - mo->player->powers[pw_nocontrol] = fractime; - if (bot) - bot->player->powers[pw_nocontrol] = fractime; - } - break; - - case 438: // Set player scale - if (mo) - { - mo->destscale = FixedDiv(P_AproxDistance(line->dx, line->dy), 100<destscale < FRACUNIT/100) - mo->destscale = FRACUNIT/100; - if (mo->player && bot) - bot->destscale = mo->destscale; - } - break; - - case 439: // Set texture - { - size_t linenum; - side_t *set = &sides[line->sidenum[0]], *this; - boolean always = !(line->flags & ML_NOCLIMB); // If noclimb: Only change mid texture if mid texture already exists on tagged lines, etc. - - for (linenum = 0; linenum < numlines; linenum++) - { - if (lines[linenum].special == 439) - continue; // Don't override other set texture lines! - - if (!Tag_Find(&lines[linenum].tags, tag)) - continue; // Find tagged lines - - // Front side - this = &sides[lines[linenum].sidenum[0]]; - if (always || this->toptexture) this->toptexture = set->toptexture; - if (always || this->midtexture) this->midtexture = set->midtexture; - if (always || this->bottomtexture) this->bottomtexture = set->bottomtexture; - - if (lines[linenum].sidenum[1] == 0xffff) - continue; // One-sided stops here. - - // Back side - this = &sides[lines[linenum].sidenum[1]]; - if (always || this->toptexture) this->toptexture = set->toptexture; - if (always || this->midtexture) this->midtexture = set->midtexture; - if (always || this->bottomtexture) this->bottomtexture = set->bottomtexture; - } - } - break; - - case 440: // Play race countdown and start Metal Sonic - if (!metalrecording && !metalplayback) - G_DoPlayMetal(); - break; - - case 441: // Trigger unlockable - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer)) - { - INT32 trigid = (INT32)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - - if (trigid < 0 || trigid > 31) // limited by 32 bit variable - CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", line->sidenum[0], trigid); - else - { - unlocktriggers |= 1 << trigid; - - // Unlocked something? - if (M_UpdateUnlockablesAndExtraEmblems()) - { - S_StartSound(NULL, sfx_s3k68); - G_SaveGameData(); // only save if unlocked something - } - } - } - - // Execute one time only - line->special = 0; - break; - - case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors - { - const mobjtype_t type = (mobjtype_t)sides[line->sidenum[0]].toptexture; - statenum_t state = NUMSTATES; - sector_t *sec; - mobj_t *thing; - - if (line->sidenum[1] != 0xffff) - state = (statenum_t)sides[line->sidenum[1]].toptexture; - - TAG_ITER_SECTORS(tag, secnum) - { - boolean tryagain; - sec = sectors + secnum; - do { - tryagain = false; - for (thing = sec->thinglist; thing; thing = thing->snext) - if (thing->type == type) - { - if (state != NUMSTATES) - { - if (!P_SetMobjState(thing, state)) // set state to specific state - { // mobj was removed - tryagain = true; // snext is corrupt, we'll have to start over. - break; - } - } - else if (!P_SetMobjState(thing, thing->state->nextstate)) // set state to nextstate - { // mobj was removed - tryagain = true; // snext is corrupt, we'll have to start over. - break; - } - } - } while (tryagain); - } - break; - } - - case 443: // Calls a named Lua function - if (line->stringargs[0]) - LUA_HookLinedefExecute(line, mo, callsec); - else - CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str)\n", sizeu1(line-lines)); - break; - - case 444: // Earthquake camera - { - quake.intensity = sides[line->sidenum[0]].textureoffset; - quake.radius = sides[line->sidenum[0]].rowoffset; - quake.time = P_AproxDistance(line->dx, line->dy)>>FRACBITS; - - quake.epicenter = NULL; /// \todo - - // reasonable defaults. - if (!quake.intensity) - quake.intensity = 8<sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible (or not visible) in - ffloor_t *rover; // FOF to vanish/un-vanish - boolean foundrover = false; // for debug, "Can't find a FOF" message - ffloortype_e oldflags; // store FOF's old flags - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 445 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - oldflags = rover->flags; - - // Abracadabra! - if (line->flags & ML_NOCLIMB) - rover->flags |= FF_EXISTS; - else - rover->flags &= ~FF_EXISTS; - - // if flags changed, reset sector's light list - if (rover->flags != oldflags) - { - sec->moved = true; - P_RecalcPrecipInSector(sec); - } - } - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 445 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - } - break; - - case 446: // Make block fall remotely (acts like FF_CRUMBLE) - { - INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible in - ffloor_t *rover; // FOF that we are going to make fall down - boolean foundrover = false; // for debug, "Can't find a FOF" message - player_t *player = NULL; // player that caused FOF to fall - boolean respawn = true; // should the fallen FOF respawn? - - if (mo) // NULL check - player = mo->player; - - if (line->flags & ML_NOCLIMB) // don't respawn! - respawn = false; - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 446 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - if (line->flags & ML_BLOCKMONSTERS) // FOF flags determine respawn ability instead? - respawn = !(rover->flags & FF_NORETURN) ^ !!(line->flags & ML_NOCLIMB); // no climb inverts - - EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, respawn); - } - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 446 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - } - break; - - case 447: // Change colormap of tagged sectors! - // Basically this special applies a colormap to the tagged sectors, just like 606 (the colormap linedef) - // Except it is activated by linedef executor, not level load - // This could even override existing colormaps I believe - // -- Monster Iestyn 14/06/18 - { - extracolormap_t *source; - if (!udmf) - source = sides[line->sidenum[0]].colormap_data; - else - { - if (!line->args[1]) - source = line->frontsector->extra_colormap; - else - { - INT32 sourcesec = Tag_Iterate_Sectors(line->args[1], 0); - if (sourcesec == -1) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 447 Executor: Can't find sector with source colormap (tag %d)!\n", line->args[1]); - return; - } - source = sectors[sourcesec].extra_colormap; - } - } - TAG_ITER_SECTORS(line->args[0], secnum) - { - if (sectors[secnum].colormap_protected) - continue; - - P_ResetColormapFader(§ors[secnum]); - - if (line->args[2] & TMCF_RELATIVE) - { - extracolormap_t *target = (!udmf && (line->flags & ML_TFERLINE) && line->sidenum[1] != 0xFFFF) ? - sides[line->sidenum[1]].colormap_data : sectors[secnum].extra_colormap; // use back colormap instead of target sector - - extracolormap_t *exc = R_AddColormaps( - target, - source, - line->args[2] & TMCF_SUBLIGHTR, - line->args[2] & TMCF_SUBLIGHTG, - line->args[2] & TMCF_SUBLIGHTB, - line->args[2] & TMCF_SUBLIGHTA, - line->args[2] & TMCF_SUBFADER, - line->args[2] & TMCF_SUBFADEG, - line->args[2] & TMCF_SUBFADEB, - line->args[2] & TMCF_SUBFADEA, - line->args[2] & TMCF_SUBFADESTART, - line->args[2] & TMCF_SUBFADEEND, - line->args[2] & TMCF_IGNOREFLAGS, - false); - - if (!(sectors[secnum].extra_colormap = R_GetColormapFromList(exc))) - { - exc->colormap = R_CreateLightTable(exc); - R_AddColormapToList(exc); - sectors[secnum].extra_colormap = exc; - } - else - Z_Free(exc); - } - else - sectors[secnum].extra_colormap = source; - } - break; - } - case 448: // Change skybox viewpoint/centerpoint - if ((mo && mo->player && P_IsLocalPlayer(mo->player)) || (line->flags & ML_NOCLIMB)) - { - INT32 viewid = sides[line->sidenum[0]].textureoffset>>FRACBITS; - INT32 centerid = sides[line->sidenum[0]].rowoffset>>FRACBITS; - - if ((line->flags & (ML_EFFECT4|ML_BLOCKMONSTERS)) == ML_EFFECT4) // Solid Midtexture is on but Block Enemies is off? - { - CONS_Alert(CONS_WARNING, - M_GetText("Skybox switch linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"), - tag); - } - else - { - // set viewpoint mobj - if (!(line->flags & ML_EFFECT4)) // Solid Midtexture turns off viewpoint setting - { - if (viewid >= 0 && viewid < 16) - skyboxmo[0] = skyboxviewpnts[viewid]; - else - skyboxmo[0] = NULL; - } - - // set centerpoint mobj - if (line->flags & ML_BLOCKMONSTERS) // Block Enemies turns ON centerpoint setting - { - if (centerid >= 0 && centerid < 16) - skyboxmo[1] = skyboxcenterpnts[centerid]; - else - skyboxmo[1] = NULL; - } - } - - CONS_Debug(DBG_GAMELOGIC, "Line type 448 Executor: viewid = %d, centerid = %d, viewpoint? = %s, centerpoint? = %s\n", - viewid, - centerid, - ((line->flags & ML_EFFECT4) ? "no" : "yes"), - ((line->flags & ML_BLOCKMONSTERS) ? "yes" : "no")); - } - break; - - case 449: // Enable bosses with parameter - { - INT32 bossid = sides[line->sidenum[0]].textureoffset>>FRACBITS; - if (bossid & ~15) // if any bits other than first 16 are set - { - CONS_Alert(CONS_WARNING, - M_GetText("Boss enable linedef (tag %d) has an invalid texture x offset.\nConsider changing it or removing it entirely.\n"), - tag); - break; - } - if (line->flags & ML_NOCLIMB) - { - bossdisabled |= (1<sidenum[0]].textureoffset>>FRACBITS; - INT32 rvalue2 = sides[line->sidenum[0]].rowoffset>>FRACBITS; - INT32 result; - - if (rvalue1 <= rvalue2) - result = P_RandomRange(rvalue1, rvalue2); - else - result = P_RandomRange(rvalue2, rvalue1); - - P_LinedefExecute((INT16)result, mo, NULL); - break; - } - - case 452: // Set FOF alpha - { - INT16 destvalue = line->sidenum[1] != 0xffff ? - (INT16)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : (INT16)(P_AproxDistance(line->dx, line->dy)>>FRACBITS); - INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible in - ffloor_t *rover; // FOF that we are going to operate - boolean foundrover = false; // for debug, "Can't find a FOF" message - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - // for relative alpha calc - if (!(line->flags & ML_NOCLIMB) && // do translucent - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - P_RemoveFakeFloorFader(rover); - P_FadeFakeFloor(rover, - rover->alpha, - max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), - 0, // set alpha immediately - false, NULL, // tic-based logic - false, // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // handle FF_TRANSLUCENT - false, // do not handle lighting - false, // do not handle colormap - false, // do not handle collision - false, // do not do ghost fade (no collision during fade) - true); // use exact alpha values (for opengl) - } - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - break; - } - - case 453: // Fade FOF - { - INT16 destvalue = line->sidenum[1] != 0xffff ? - (INT16)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : (INT16)(line->dx>>FRACBITS); - INT16 speed = line->sidenum[1] != 0xffff ? - (INT16)(abs(sides[line->sidenum[1]].rowoffset>>FRACBITS)) : (INT16)(abs(line->dy)>>FRACBITS); - INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible in - ffloor_t *rover; // FOF that we are going to operate - boolean foundrover = false; // for debug, "Can't find a FOF" message - size_t j = 0; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - // Prevent continuous execs from interfering on an existing fade - if (!(line->flags & ML_EFFECT5) - && rover->fadingdata) - //&& ((fade_t*)rover->fadingdata)->timer > (ticbased ? 2 : speed*2)) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Fade FOF thinker already exists, timer: %d\n", ((fade_t*)rover->fadingdata)->timer); - continue; - } - - if (speed > 0) - P_AddFakeFloorFader(rover, secnum, j, - destvalue, - speed, - (line->flags & ML_EFFECT4), // tic-based logic - (line->flags & ML_EFFECT3), // Relative destvalue - !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT - !(line->flags & ML_EFFECT2), // do not handle lighting - !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) - !(line->flags & ML_BOUNCY), // do not handle collision - (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) - (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) - else - { - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - // for relative alpha calc - if (!(line->flags & ML_NOCLIMB) && // do translucent - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - P_RemoveFakeFloorFader(rover); - P_FadeFakeFloor(rover, - rover->alpha, - max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), - 0, // set alpha immediately - false, NULL, // tic-based logic - !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT - !(line->flags & ML_EFFECT2), // do not handle lighting - !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) - !(line->flags & ML_BOUNCY), // do not handle collision - (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) - (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) - } - } - j++; - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - break; - } - - case 454: // Stop fading FOF - { - INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); - sector_t *sec; // Sector that the FOF is visible in - ffloor_t *rover; // FOF that we are going to operate - boolean foundrover = false; // for debug, "Can't find a FOF" message - - TAG_ITER_SECTORS(sectag, secnum) - { - sec = sectors + secnum; - - if (!sec->ffloors) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 454 Executor: Target sector #%d has no FOFs.\n", secnum); - return; - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (Tag_Find(&rover->master->frontsector->tags, foftag)) - { - foundrover = true; - - P_ResetFakeFloorFader(rover, NULL, - !(line->flags & ML_BLOCKMONSTERS)); // do not finalize collision flags - } - } - - if (!foundrover) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 454 Executor: Can't find a FOF control sector with tag %d\n", foftag); - return; - } - } - break; - } - - case 455: // Fade colormap - { - extracolormap_t *dest; - if (!udmf) - dest = sides[line->sidenum[0]].colormap_data; - else - { - if (!line->args[1]) - dest = line->frontsector->extra_colormap; - else - { - INT32 destsec = Tag_Iterate_Sectors(line->args[1], 0); - if (destsec == -1) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 455 Executor: Can't find sector with destination colormap (tag %d)!\n", line->args[1]); - return; - } - dest = sectors[destsec].extra_colormap; - } - } - - TAG_ITER_SECTORS(line->args[0], secnum) - { - extracolormap_t *source_exc, *dest_exc, *exc; - - if (sectors[secnum].colormap_protected) - continue; - - // Don't interrupt ongoing fade - if (!(line->args[3] & TMCF_OVERRIDE) - && sectors[secnum].fadecolormapdata) - //&& ((fadecolormap_t*)sectors[secnum].fadecolormapdata)->timer > (ticbased ? 2 : speed*2)) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 455 Executor: Fade color thinker already exists, timer: %d\n", ((fadecolormap_t*)sectors[secnum].fadecolormapdata)->timer); - continue; - } - - if (!udmf && (line->flags & ML_TFERLINE)) // use back colormap instead of target sector - sectors[secnum].extra_colormap = (line->sidenum[1] != 0xFFFF) ? - sides[line->sidenum[1]].colormap_data : NULL; - - exc = sectors[secnum].extra_colormap; - - if (!(line->args[3] & TMCF_FROMBLACK) // Override fade from default rgba - && !R_CheckDefaultColormap(dest, true, false, false) - && R_CheckDefaultColormap(exc, true, false, false)) - { - exc = R_CopyColormap(exc, false); - exc->rgba = R_GetRgbaRGB(dest->rgba) + R_PutRgbaA(R_GetRgbaA(exc->rgba)); - //exc->fadergba = R_GetRgbaRGB(dest->rgba) + R_PutRgbaA(R_GetRgbaA(exc->fadergba)); - - if (!(source_exc = R_GetColormapFromList(exc))) - { - exc->colormap = R_CreateLightTable(exc); - R_AddColormapToList(exc); - source_exc = exc; - } - else - Z_Free(exc); - - sectors[secnum].extra_colormap = source_exc; - } - else - source_exc = exc ? exc : R_GetDefaultColormap(); - - if (line->args[3] & TMCF_RELATIVE) - { - exc = R_AddColormaps( - source_exc, - dest, - line->args[3] & TMCF_SUBLIGHTR, - line->args[3] & TMCF_SUBLIGHTG, - line->args[3] & TMCF_SUBLIGHTB, - line->args[3] & TMCF_SUBLIGHTA, - line->args[3] & TMCF_SUBFADER, - line->args[3] & TMCF_SUBFADEG, - line->args[3] & TMCF_SUBFADEB, - line->args[3] & TMCF_SUBFADEA, - line->args[3] & TMCF_SUBFADESTART, - line->args[3] & TMCF_SUBFADEEND, - line->args[3] & TMCF_IGNOREFLAGS, - false); - } - else - exc = R_CopyColormap(dest, false); - - if (!(dest_exc = R_GetColormapFromList(exc))) - { - exc->colormap = R_CreateLightTable(exc); - R_AddColormapToList(exc); - dest_exc = exc; - } - else - Z_Free(exc); - - Add_ColormapFader(§ors[secnum], source_exc, dest_exc, true, // tic-based timing - line->args[2]); - } - break; - } - case 456: // Stop fade colormap - TAG_ITER_SECTORS(line->args[0], secnum) - P_ResetColormapFader(§ors[secnum]); - break; - - case 457: // Track mobj angle to point - if (mo) - { - INT32 failureangle = FixedAngle((min(max(abs(sides[line->sidenum[0]].textureoffset>>FRACBITS), 0), 360))*FRACUNIT); - INT32 failuredelay = abs(sides[line->sidenum[0]].rowoffset>>FRACBITS); - INT32 failureexectag = line->sidenum[1] != 0xffff ? - (INT32)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : 0; - boolean persist = (line->flags & ML_EFFECT2); - mobj_t *anchormo; - - if ((secnum = Tag_Iterate_Sectors(tag, 0)) < 0) - return; - - anchormo = P_GetObjectTypeInSectorNum(MT_ANGLEMAN, secnum); - if (!anchormo) - return; - - mo->eflags |= MFE_TRACERANGLE; - P_SetTarget(&mo->tracer, anchormo); - mo->lastlook = persist; // don't disable behavior after first failure - mo->extravalue1 = failureangle; // angle to exceed for failure state - mo->extravalue2 = failureexectag; // exec tag for failure state (angle is not within range) - mo->cusval = mo->cvmem = failuredelay; // cusval = tics to allow failure before line trigger; cvmem = decrement timer - } - break; - - case 458: // Stop tracking mobj angle to point - if (mo && (mo->eflags & MFE_TRACERANGLE)) - { - mo->eflags &= ~MFE_TRACERANGLE; - P_SetTarget(&mo->tracer, NULL); - mo->lastlook = mo->cvmem = mo->cusval = mo->extravalue1 = mo->extravalue2 = 0; - } - break; - - case 459: // Control Text Prompt - // console player only unless NOCLIMB is set - if (mo && mo->player && P_IsLocalPlayer(mo->player) && (!bot || bot != mo)) - { - INT32 promptnum = max(0, (sides[line->sidenum[0]].textureoffset>>FRACBITS)-1); - INT32 pagenum = max(0, (sides[line->sidenum[0]].rowoffset>>FRACBITS)-1); - INT32 postexectag = abs((line->sidenum[1] != 0xFFFF) ? sides[line->sidenum[1]].textureoffset>>FRACBITS : tag); - - boolean closetextprompt = (line->flags & ML_BLOCKMONSTERS); - //boolean allplayers = (line->flags & ML_NOCLIMB); - boolean runpostexec = (line->flags & ML_EFFECT1); - boolean blockcontrols = !(line->flags & ML_EFFECT2); - boolean freezerealtime = !(line->flags & ML_EFFECT3); - //boolean freezethinkers = (line->flags & ML_EFFECT4); - boolean callbynamedtag = (line->flags & ML_TFERLINE); - - if (closetextprompt) - F_EndTextPrompt(false, false); - else - { - if (callbynamedtag && sides[line->sidenum[0]].text && sides[line->sidenum[0]].text[0]) - F_GetPromptPageByNamedTag(sides[line->sidenum[0]].text, &promptnum, &pagenum); - F_StartTextPrompt(promptnum, pagenum, mo, runpostexec ? postexectag : 0, blockcontrols, freezerealtime); - } - } - break; - - case 460: // Award rings - { - INT16 rings = (sides[line->sidenum[0]].textureoffset>>FRACBITS); - INT32 delay = (sides[line->sidenum[0]].rowoffset>>FRACBITS); - if (mo && mo->player) - { - if (delay <= 0 || !(leveltime % delay)) - P_GivePlayerRings(mo->player, rings); - } - } - break; - - case 461: // Spawns an object on the map based on texture offsets - { - const mobjtype_t type = (mobjtype_t)(sides[line->sidenum[0]].toptexture); - mobj_t *mobj; - - fixed_t x, y, z; - x = sides[line->sidenum[0]].textureoffset; - y = sides[line->sidenum[0]].rowoffset; - z = line->frontsector->floorheight; - - if (line->flags & ML_NOCLIMB) // If noclimb is set, spawn randomly within a range - { - if (line->sidenum[1] != 0xffff) // Make sure the linedef has a back side - { - x = P_RandomRange(sides[line->sidenum[0]].textureoffset>>FRACBITS, sides[line->sidenum[1]].textureoffset>>FRACBITS)<sidenum[0]].rowoffset>>FRACBITS, sides[line->sidenum[1]].rowoffset>>FRACBITS)<frontsector->floorheight>>FRACBITS, line->frontsector->ceilingheight>>FRACBITS)<special); - break; - } - } - - mobj = P_SpawnMobj(x, y, z, type); - if (mobj) - { - if (line->flags & ML_EFFECT1) - mobj->angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y); - CONS_Debug(DBG_GAMELOGIC, "Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d)\n", line->special, mobj->type, mobj->x>>FRACBITS, mobj->y>>FRACBITS, mobj->z>>FRACBITS); //TODO: Convert mobj->type to a string somehow. - } - else - CONS_Alert(CONS_ERROR,"Linedef Type %d - Spawn Object: Object did not spawn!\n", line->special); - } - break; - - case 462: // Stop clock (and end level in record attack) - if (G_PlatformGametype()) - { - stoppedclock = true; - CONS_Debug(DBG_GAMELOGIC, "Clock stopped!\n"); - if (modeattacking) - { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - P_DoPlayerExit(&players[i]); - } - } - } - break; - - case 463: // Dye object - { - INT32 color = sides[line->sidenum[0]].toptexture; - - if (mo) - { - if (color < 0 || color >= numskincolors) - return; - - var1 = 0; - var2 = color; - A_Dye(mo); - } - } - break; - - case 464: // Trigger Egg Capsule - { - thinker_t *th; - mobj_t *mo2; - - // Find the center of the Eggtrap and release all the pretty animals! - // The chimps are my friends.. heeheeheheehehee..... - LouisJM - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type != MT_EGGTRAP) - continue; - - if (!mo2->spawnpoint) - continue; - - if (mo2->spawnpoint->angle != tag) - continue; - - P_KillMobj(mo2, NULL, mo, 0); - } - - if (!(line->flags & ML_NOCLIMB)) - { - INT32 i; - - // Mark all players with the time to exit thingy! - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - P_DoPlayerExit(&players[i]); - } - } - } - break; - - case 465: // Set linedef executor delay - { - INT32 linenum; - - if (!udmf) - break; - - TAG_ITER_LINES(line->args[0], linenum) - { - if (line->args[2]) - lines[linenum].executordelay += line->args[1]; - else - lines[linenum].executordelay = line->args[1]; - } - } - break; - - case 466: // Set level failure state - { - if (line->flags & ML_NOCLIMB) - { - stagefailed = false; - CONS_Debug(DBG_GAMELOGIC, "Stage can be completed successfully!\n"); - } - else - { - stagefailed = true; - CONS_Debug(DBG_GAMELOGIC, "Stage will end in failure...\n"); - } - } - break; - - case 480: // Polyobj_DoorSlide - case 481: // Polyobj_DoorSwing - PolyDoor(line); - break; - case 482: // Polyobj_Move - case 483: // Polyobj_OR_Move - PolyMove(line); - break; - case 484: // Polyobj_RotateRight - case 485: // Polyobj_OR_RotateRight - case 486: // Polyobj_RotateLeft - case 487: // Polyobj_OR_RotateLeft - PolyRotate(line); - break; - case 488: // Polyobj_Waypoint - PolyWaypoint(line); - break; - case 489: - PolyInvisible(line); - break; - case 490: - PolyVisible(line); - break; - case 491: - PolyTranslucency(line); - break; - case 492: - PolyFade(line); - break; - - default: - break; - } -} - -// -// P_SetupSignExit -// -// Finds the exit sign in the current sector and -// sets its target to the player who passed the map. -// -void P_SetupSignExit(player_t *player) -{ - mobj_t *thing; - msecnode_t *node = player->mo->subsector->sector->touching_thinglist; // things touching this sector - thinker_t *think; - INT32 numfound = 0; - - for (; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - if (thing->type != MT_SIGN) - continue; - - if (!numfound - && !(player->mo->target && player->mo->target->type == MT_SIGN) - && !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value)) - P_SetTarget(&player->mo->target, thing); - - if (thing->state != &states[thing->info->spawnstate]) - continue; - - P_SetTarget(&thing->target, player->mo); - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_SetMobjState(thing, S_SIGNSPIN1); - if (thing->info->seesound) - S_StartSound(thing, thing->info->seesound); - - ++numfound; - } - - if (numfound) - return; - - // didn't find any signposts in the exit sector. - // spin all signposts in the level then. - for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) - { - if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - thing = (mobj_t *)think; - if (thing->type != MT_SIGN) - continue; - - if (!numfound - && !(player->mo->target && player->mo->target->type == MT_SIGN) - && !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value)) - P_SetTarget(&player->mo->target, thing); - - if (thing->state != &states[thing->info->spawnstate]) - continue; - - P_SetTarget(&thing->target, player->mo); - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_SetMobjState(thing, S_SIGNSPIN1); - if (thing->info->seesound) - S_StartSound(thing, thing->info->seesound); - - ++numfound; - } -} - -// -// P_IsFlagAtBase -// -// Checks to see if a flag is at its base. -// -boolean P_IsFlagAtBase(mobjtype_t flag) -{ - thinker_t *think; - mobj_t *mo; - INT32 specialnum = (flag == MT_REDFLAG) ? 3 : 4; - - for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) - { - if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)think; - - if (mo->type != flag) - continue; - - if (GETSECSPECIAL(mo->subsector->sector->special, 4) == specialnum) - return true; - else if (mo->subsector->sector->ffloors) // Check the 3D floors - { - ffloor_t *rover; - - for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS)) - continue; - - if (GETSECSPECIAL(rover->master->frontsector->special, 4) != specialnum) - continue; - - if (!(mo->z <= P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector) - && mo->z >= P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector))) - continue; - - return true; - } - } - } - return false; -} - -// -// P_PlayerTouchingSectorSpecial -// -// Replaces the old player->specialsector. -// This allows a player to touch more than -// one sector at a time, if necessary. -// -// Returns a pointer to the first sector of -// the particular type that it finds. -// Returns NULL if it doesn't find it. -// -sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number) -{ - msecnode_t *node; - ffloor_t *rover; - - if (!player->mo) - return NULL; - - // Check default case first - if (GETSECSPECIAL(player->mo->subsector->sector->special, section) == number) - return player->mo->subsector->sector; - - // Hmm.. maybe there's a FOF that has it... - for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next) - { - fixed_t topheight, bottomheight; - - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) - continue; - - if (!(rover->flags & FF_EXISTS)) - continue; - - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - - // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) - { - boolean floorallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z == topheight)); - boolean ceilingallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z + player->mo->height == bottomheight)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - continue; - } - else - { - // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) - continue; - } - - // This FOF has the special we're looking for! - return rover->master->frontsector; - } - - for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - if (GETSECSPECIAL(node->m_sector->special, section) == number) - { - // This sector has the special we're looking for, but - // are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector - || (node->m_sector->flags & SF_TRIGGERSPECIAL_TOUCH)) - return node->m_sector; - } - - // Hmm.. maybe there's a FOF that has it... - for (rover = node->m_sector->ffloors; rover; rover = rover->next) - { - fixed_t topheight, bottomheight; - - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) - continue; - - if (!(rover->flags & FF_EXISTS)) - continue; - - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - - // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) - { - boolean floorallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z == topheight)); - boolean ceilingallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z + player->mo->height == bottomheight)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - continue; - } - else - { - // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) - continue; - } - - // This FOF has the special we're looking for, but are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector - || (rover->master->frontsector->flags & SF_TRIGGERSPECIAL_TOUCH)) - return rover->master->frontsector; - } - } - - return NULL; -} - -// -// P_ThingIsOnThe3DFloor -// -// This checks whether the mobj is on/in the FOF we want it to be at -// Needed for the "All players" trigger sector specials only -// -static boolean P_ThingIsOnThe3DFloor(mobj_t *mo, sector_t *sector, sector_t *targetsec) -{ - ffloor_t *rover; - fixed_t top, bottom; - - if (!mo->player) // should NEVER happen - return false; - - if (!targetsec->ffloors) // also should NEVER happen - return false; - - for (rover = targetsec->ffloors; rover; rover = rover->next) - { - if (rover->master->frontsector != sector) - continue; - - // we're assuming the FOF existed when the first player touched it - //if (!(rover->flags & FF_EXISTS)) - // return false; - - top = P_GetSpecialTopZ(mo, sector, targetsec); - bottom = P_GetSpecialBottomZ(mo, sector, targetsec); - - // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) - { - boolean floorallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(mo->eflags & MFE_VERTICALFLIP)) && (mo->z == top)); - boolean ceilingallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (mo->eflags & MFE_VERTICALFLIP)) && (mo->z + mo->height == bottom)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - continue; - } - else - { - // Water and intangible FOFs - if (mo->z > top || (mo->z + mo->height) < bottom) - return false; - } - - return true; - } - - return false; -} - -// -// P_MobjReadyToTrigger -// -// Is player standing on the sector's "ground"? -// -static boolean P_MobjReadyToTrigger(mobj_t *mo, sector_t *sec) -{ - boolean floorallowed = ((sec->flags & SF_FLIPSPECIAL_FLOOR) && ((sec->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(mo->eflags & MFE_VERTICALFLIP)) && (mo->z == P_GetSpecialBottomZ(mo, sec, sec))); - boolean ceilingallowed = ((sec->flags & SF_FLIPSPECIAL_CEILING) && ((sec->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (mo->eflags & MFE_VERTICALFLIP)) && (mo->z + mo->height == P_GetSpecialTopZ(mo, sec, sec))); - // Thing must be on top of the floor to be affected... - return (floorallowed || ceilingallowed); -} - -/** Applies a sector special to a player. - * - * \param player Player in the sector. - * \param sector Sector with the special. - * \param roversector If !NULL, sector is actually an FOF; otherwise, sector - * is being physically contacted by the player. - * \todo Split up into multiple functions. - * \sa P_PlayerInSpecialSector, P_PlayerOnSpecial3DFloor - */ -void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector) -{ - INT32 i = 0; - INT32 section1, section2, section3, section4; - INT32 special; - mtag_t sectag = Tag_FGet(§or->tags); - - section1 = GETSECSPECIAL(sector->special, 1); - section2 = GETSECSPECIAL(sector->special, 2); - section3 = GETSECSPECIAL(sector->special, 3); - section4 = GETSECSPECIAL(sector->special, 4); - - // Ignore spectators - if (player->spectator) - return; - - // Ignore dead players. - // If this strange phenomenon could be potentially used in levels, - // TODO: modify this to accommodate for it. - if (player->playerstate != PST_LIVE) - return; - - // Conveyor stuff - if (section3 == 2 || section3 == 4) - player->onconveyor = section3; - - special = section1; - - // Process Section 1 - switch (special) - { - case 1: // Damage (Generic) - if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, 0); - break; - case 2: // Damage (Water) - if ((roversector || P_MobjReadyToTrigger(player->mo, sector)) && (player->powers[pw_underwater] || player->powers[pw_carry] == CR_NIGHTSMODE)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_WATER); - break; - case 3: // Damage (Fire) - if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_FIRE); - break; - case 4: // Damage (Electrical) - if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_ELECTRIC); - break; - case 5: // Spikes - if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPIKE); - break; - case 6: // Death Pit (Camera Mod) - case 7: // Death Pit (No Camera Mod) - if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - { - if (player->quittime) - G_MovePlayerToSpawnOrStarpost(player - players); - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); - } - break; - case 8: // Instant Kill - if (player->quittime) - G_MovePlayerToSpawnOrStarpost(player - players); - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); - break; - case 9: // Ring Drainer (Floor Touch) - case 10: // Ring Drainer (No Floor Touch) - if (leveltime % (TICRATE/2) == 0 && player->rings > 0) - { - player->rings--; - S_StartSound(player->mo, sfx_antiri); - } - break; - case 11: // Special Stage Damage - if (player->exiting || player->bot) // Don't do anything for bots or players who have just finished - break; - - if (!(player->powers[pw_shield] || player->spheres > 0)) // Don't do anything if no shield or spheres anyway - break; - - P_SpecialStageDamage(player, NULL, NULL); - break; - case 12: // Space Countdown - if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime]) - player->powers[pw_spacetime] = spacetimetics + 1; - break; - case 13: // Ramp Sector (Increase step-up/down) - case 14: // Non-Ramp Sector (Don't step-down) - case 15: // Unused - break; - } - - special = section2; - - // Process Section 2 - switch (special) - { - case 1: // Trigger Linedef Exec (Pushable Objects) - break; - case 2: // Linedef executor requires all players present+doesn't require touching floor - case 3: // Linedef executor requires all players present - /// \todo check continues for proper splitscreen support? - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (!players[i].mo) - continue; - if (players[i].spectator) - continue; - if (players[i].bot) - continue; - if (G_CoopGametype() && players[i].lives <= 0) - continue; - if (roversector) - { - if (sector->flags & SF_TRIGGERSPECIAL_TOUCH) - { - msecnode_t *node; - for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - if (P_ThingIsOnThe3DFloor(players[i].mo, sector, node->m_sector)) - break; - } - if (!node) - goto DoneSection2; - } - else if (players[i].mo->subsector && !P_ThingIsOnThe3DFloor(players[i].mo, sector, players[i].mo->subsector->sector)) // this function handles basically everything for us lmao - goto DoneSection2; - } - else - { - if (players[i].mo->subsector->sector == sector) - ; - else if (sector->flags & SF_TRIGGERSPECIAL_TOUCH) - { - msecnode_t *node; - for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - if (node->m_sector == sector) - break; - } - if (!node) - goto DoneSection2; - } - else - goto DoneSection2; - - if (special == 3 && !P_MobjReadyToTrigger(players[i].mo, sector)) - goto DoneSection2; - } - } - /* FALLTHRU */ - case 4: // Linedef executor that doesn't require touching floor - case 5: // Linedef executor - case 6: // Linedef executor (7 Emeralds) - case 7: // Linedef executor (NiGHTS Mare) - if (!player->bot) - P_LinedefExecute(sectag, player->mo, sector); - break; - case 8: // Tells pushable things to check FOFs - break; - case 9: // Egg trap capsule - { - thinker_t *th; - mobj_t *mo2; - line_t junk; - - if (player->bot || sector->ceilingdata || sector->floordata) - return; - - // Find the center of the Eggtrap and release all the pretty animals! - // The chimps are my friends.. heeheeheheehehee..... - LouisJM - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - mo2 = (mobj_t *)th; - if (mo2->type != MT_EGGTRAP) - continue; - P_KillMobj(mo2, NULL, player->mo, 0); - } - - // clear the special so you can't push the button twice. - sector->special = 0; - - // Initialize my junk - junk.tags.tags = NULL; - junk.tags.count = 0; - - // Move the button down - Tag_FSet(&junk.tags, LE_CAPSULE0); - EV_DoElevator(&junk, elevateDown, false); - - // Open the top FOF - Tag_FSet(&junk.tags, LE_CAPSULE1); - EV_DoFloor(&junk, raiseFloorToNearestFast); - // Open the bottom FOF - Tag_FSet(&junk.tags, LE_CAPSULE2); - EV_DoCeiling(&junk, lowerToLowestFast); - - // Mark all players with the time to exit thingy! - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - P_DoPlayerExit(&players[i]); - } - break; - } - case 10: // Special Stage Time/Rings - case 11: // Custom Gravity - break; - case 12: // Lua sector special - break; - } -DoneSection2: - - special = section3; - - // Process Section 3 - switch (special) - { - case 1: // Unused - case 2: // Wind/Current - case 3: // Unused - case 4: // Conveyor Belt - break; - - case 5: // Speed pad - if (player->powers[pw_flashing] != 0 && player->powers[pw_flashing] < TICRATE/2) - break; - - i = Tag_FindLineSpecial(4, sectag); - - if (i != -1) - { - angle_t lineangle; - fixed_t linespeed; - fixed_t sfxnum; - - lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); - linespeed = sides[lines[i].sidenum[0]].textureoffset; - - if (linespeed == 0) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Speed pad (tag %d) at zero speed.\n", sectag); - break; - } - - player->mo->angle = player->drawangle = lineangle; - - if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG) - P_SetPlayerAngle(player, player->mo->angle); - - if (!(lines[i].flags & ML_EFFECT4)) - { - P_UnsetThingPosition(player->mo); - if (roversector) // make FOF speed pads work - { - player->mo->x = roversector->soundorg.x; - player->mo->y = roversector->soundorg.y; - } - else - { - player->mo->x = sector->soundorg.x; - player->mo->y = sector->soundorg.y; - } - P_SetThingPosition(player->mo); - } - - P_InstaThrust(player->mo, player->mo->angle, linespeed); - - if (lines[i].flags & ML_EFFECT5) // Roll! - { - if (!(player->pflags & PF_SPINNING)) - player->pflags |= PF_SPINNING; - - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - } - - player->powers[pw_flashing] = TICRATE/3; - - sfxnum = sides[lines[i].sidenum[0]].toptexture; - - if (!sfxnum) - sfxnum = sfx_spdpad; - - S_StartSound(player->mo, sfxnum); - } - break; - - case 6: // Unused - case 7: // Unused - case 8: // Unused - case 9: // Unused - case 10: // Unused - case 11: // Unused - case 12: // Unused - case 13: // Unused - case 14: // Unused - case 15: // Unused - break; - } - - special = section4; - - // Process Section 4 - switch (special) - { - case 1: // Starpost Activator - { - mobj_t *post = P_GetObjectTypeInSectorNum(MT_STARPOST, sector - sectors); - - if (!post) - break; - - P_TouchStarPost(post, player, false); - break; - } - - case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return - if (player->bot || !(gametyperules & GTR_ALLOWEXIT)) - break; - if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && player->nightstime > 6) - { - player->nightstime = 6; // Just let P_Ticker take care of the rest. - return; - } - - // Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c) - { - INT32 lineindex; - - P_DoPlayerFinish(player); - - P_SetupSignExit(player); - // important: use sector->tag on next line instead of player->mo->subsector->tag - // this part is different from in P_PlayerThink, this is what was causing - // FOF custom exits not to work. - lineindex = Tag_FindLineSpecial(2, sectag); - - if (G_CoopGametype() && lineindex != -1) // Custom exit! - { - // Special goodies with the block monsters flag depending on emeralds collected - if ((lines[lineindex].flags & ML_BLOCKMONSTERS) && ALL7EMERALDS(emeralds)) - nextmapoverride = (INT16)(lines[lineindex].frontsector->ceilingheight>>FRACBITS); - else - nextmapoverride = (INT16)(lines[lineindex].frontsector->floorheight>>FRACBITS); - - if (lines[lineindex].flags & ML_NOCLIMB) - skipstats = 1; - } - } - break; - - case 3: // Red Team's Base - if ((gametyperules & GTR_TEAMFLAGS) && P_IsObjectOnGround(player->mo)) - { - if (player->ctfteam == 1 && (player->gotflag & GF_BLUEFLAG)) - { - mobj_t *mo; - - // Make sure the red team still has their own - // flag at their base so they can score. - if (!P_IsFlagAtBase(MT_REDFLAG)) - break; - - HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE); - HU_SetCEchoDuration(5); - HU_DoCEcho(va(M_GetText("\205%s\200\\CAPTURED THE \204BLUE FLAG\200.\\\\\\\\"), player_names[player-players])); - - if (splitscreen || players[consoleplayer].ctfteam == 1) - S_StartSound(NULL, sfx_flgcap); - else if (players[consoleplayer].ctfteam == 2) - S_StartSound(NULL, sfx_lose); - - mo = P_SpawnMobj(player->mo->x,player->mo->y,player->mo->z,MT_BLUEFLAG); - player->gotflag &= ~GF_BLUEFLAG; - mo->flags &= ~MF_SPECIAL; - mo->fuse = TICRATE; - mo->spawnpoint = bflagpoint; - mo->flags2 |= MF2_JUSTATTACKED; - redscore += 1; - P_AddPlayerScore(player, 250); - } - } - break; - - case 4: // Blue Team's Base - if ((gametyperules & GTR_TEAMFLAGS) && P_IsObjectOnGround(player->mo)) - { - if (player->ctfteam == 2 && (player->gotflag & GF_REDFLAG)) - { - mobj_t *mo; - - // Make sure the blue team still has their own - // flag at their base so they can score. - if (!P_IsFlagAtBase(MT_BLUEFLAG)) - break; - - HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE); - HU_SetCEchoDuration(5); - HU_DoCEcho(va(M_GetText("\204%s\200\\CAPTURED THE \205RED FLAG\200.\\\\\\\\"), player_names[player-players])); - - if (splitscreen || players[consoleplayer].ctfteam == 2) - S_StartSound(NULL, sfx_flgcap); - else if (players[consoleplayer].ctfteam == 1) - S_StartSound(NULL, sfx_lose); - - mo = P_SpawnMobj(player->mo->x,player->mo->y,player->mo->z,MT_REDFLAG); - player->gotflag &= ~GF_REDFLAG; - mo->flags &= ~MF_SPECIAL; - mo->fuse = TICRATE; - mo->spawnpoint = rflagpoint; - mo->flags2 |= MF2_JUSTATTACKED; - bluescore += 1; - P_AddPlayerScore(player, 250); - } - } - break; - - case 5: // Fan sector - player->mo->momz += mobjinfo[MT_FAN].mass/4; - - if (player->mo->momz > mobjinfo[MT_FAN].mass) - player->mo->momz = mobjinfo[MT_FAN].mass; - - P_ResetPlayer(player); - if (player->panim != PA_FALL) - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); - break; - - case 6: // Super Sonic transformer - if (player->mo->health > 0 && !player->bot && (player->charflags & SF_SUPER) && !player->powers[pw_super] && ALL7EMERALDS(emeralds)) - P_DoSuperTransformation(player, true); - break; - - case 7: // Make player spin - if (!(player->pflags & PF_SPINNING)) - { - player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartAttackSound(player->mo, sfx_spin); - - if (abs(player->rmomx) < FixedMul(5*FRACUNIT, player->mo->scale) - && abs(player->rmomy) < FixedMul(5*FRACUNIT, player->mo->scale)) - P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale)); - } - break; - - case 8: // Zoom Tube Start - { - INT32 sequence; - fixed_t speed; - INT32 lineindex; - mobj_t *waypoint = NULL; - angle_t an; - - if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ZOOMTUBE) - break; - - if (player->powers[pw_ignorelatch] & (1<<15)) - break; - - // Find line #3 tagged to this sector - lineindex = Tag_FindLineSpecial(3, sectag); - - if (lineindex == -1) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #3.\n", sector->special); - break; - } - - // Grab speed and sequence values - speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; - sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; - - if (speed == 0) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); - break; - } - - waypoint = P_GetFirstWaypoint(sequence); - - if (!waypoint) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: FIRST WAYPOINT IN SEQUENCE %d NOT FOUND.\n", sequence); - break; - } - else - { - CONS_Debug(DBG_GAMELOGIC, "Waypoint %d found in sequence %d - speed = %d\n", waypoint->health, sequence, speed); - } - - an = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->x, waypoint->y) - player->mo->angle; - - if (an > ANGLE_90 && an < ANGLE_270 && !(lines[lineindex].flags & ML_EFFECT4)) - break; // behind back - - P_SetTarget(&player->mo->tracer, waypoint); - player->powers[pw_carry] = CR_ZOOMTUBE; - player->speed = speed; - player->pflags |= PF_SPINNING; - player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY); - player->climbing = 0; - - if (player->mo->state-states != S_PLAY_ROLL) - { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_spin); - } - } - break; - - case 9: // Zoom Tube End - { - INT32 sequence; - fixed_t speed; - INT32 lineindex; - mobj_t *waypoint = NULL; - angle_t an; - - if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ZOOMTUBE) - break; - - if (player->powers[pw_ignorelatch] & (1<<15)) - break; - - // Find line #3 tagged to this sector - lineindex = Tag_FindLineSpecial(3, sectag); - - if (lineindex == -1) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #3.\n", sector->special); - break; - } - - // Grab speed and sequence values - speed = -abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; // Negative means reverse - sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; - - if (speed == 0) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); - break; - } - - waypoint = P_GetLastWaypoint(sequence); - - if (!waypoint) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: LAST WAYPOINT IN SEQUENCE %d NOT FOUND.\n", sequence); - break; - } - else - { - CONS_Debug(DBG_GAMELOGIC, "Waypoint %d found in sequence %d - speed = %d\n", waypoint->health, sequence, speed); - } - - an = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->x, waypoint->y) - player->mo->angle; - - if (an > ANGLE_90 && an < ANGLE_270 && !(lines[lineindex].flags & ML_EFFECT4)) - break; // behind back - - P_SetTarget(&player->mo->tracer, waypoint); - player->powers[pw_carry] = CR_ZOOMTUBE; - player->speed = speed; - player->pflags |= PF_SPINNING; - player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY); - player->climbing = 0; - - if (player->mo->state-states != S_PLAY_ROLL) - { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_spin); - } - } - break; - - case 10: // Finish Line - if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && !player->exiting) - { - if (player->starpostnum == numstarposts) // Must have touched all the starposts - { - player->laps++; - - if (player->powers[pw_carry] == CR_NIGHTSMODE) - player->drillmeter += 48*20; - - if (player->laps >= (UINT8)cv_numlaps.value) - CONS_Printf(M_GetText("%s has finished the race.\n"), player_names[player-players]); - else if (player->laps == (UINT8)cv_numlaps.value-1) - CONS_Printf(M_GetText("%s started the \205final lap\200!\n"), player_names[player-players]); - else - CONS_Printf(M_GetText("%s started lap %u\n"), player_names[player-players], (UINT32)player->laps+1); - - // Reset starposts (checkpoints) info - player->starpostscale = player->starpostangle = player->starposttime = player->starpostnum = 0; - player->starpostx = player->starposty = player->starpostz = 0; - P_ResetStarposts(); - - // Play the starpost sound for 'consistency' - S_StartSound(player->mo, sfx_strpst); - } - else if (player->starpostnum) - { - // blatant reuse of a variable that's normally unused in circuit - if (!player->tossdelay) - S_StartSound(player->mo, sfx_lose); - player->tossdelay = 3; - } - - if (player->laps >= (unsigned)cv_numlaps.value) - { - if (P_IsLocalPlayer(player)) - { - HU_SetCEchoFlags(0); - HU_SetCEchoDuration(5); - HU_DoCEcho("FINISHED!"); - } - - P_DoPlayerExit(player); - } - } - break; - - case 11: // Rope hang - { - INT32 sequence; - fixed_t speed; - INT32 lineindex; - mobj_t *waypointmid = NULL; - mobj_t *waypointhigh = NULL; - mobj_t *waypointlow = NULL; - mobj_t *closest = NULL; - vector3_t p, line[2], resulthigh, resultlow; - - if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ROPEHANG) - break; - - if (player->powers[pw_ignorelatch] & (1<<15)) - break; - - if (player->mo->momz > 0) - break; - - if (player->cmd.buttons & BT_SPIN) - break; - - if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate]) - break; - - if (player->exiting) - break; - - //initialize resulthigh and resultlow with 0 - memset(&resultlow, 0x00, sizeof(resultlow)); - memset(&resulthigh, 0x00, sizeof(resulthigh)); - - // Find line #11 tagged to this sector - lineindex = Tag_FindLineSpecial(11, sectag); - - if (lineindex == -1) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #11.\n", sector->special); - break; - } - - // Grab speed and sequence values - speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; - sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; - - if (speed == 0) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); - break; - } - - // Find the closest waypoint - // Find the preceding waypoint - // Find the proceeding waypoint - // Determine the closest spot on the line between the three waypoints - // Put player at that location. - - waypointmid = P_GetClosestWaypoint(sequence, player->mo); - - if (!waypointmid) - { - CONS_Debug(DBG_GAMELOGIC, "ERROR: WAYPOINT(S) IN SEQUENCE %d NOT FOUND.\n", sequence); - break; - } - - waypointlow = P_GetPreviousWaypoint(waypointmid, true); - waypointhigh = P_GetNextWaypoint(waypointmid, true); - - CONS_Debug(DBG_GAMELOGIC, "WaypointMid: %d; WaypointLow: %d; WaypointHigh: %d\n", - waypointmid->health, waypointlow ? waypointlow->health : -1, waypointhigh ? waypointhigh->health : -1); - - // Now we have three waypoints... the closest one we're near, and the one that comes before, and after. - // Next, we need to find the closest point on the line between each set, and determine which one we're - // closest to. - - p.x = player->mo->x; - p.y = player->mo->y; - p.z = player->mo->z; - - // Waypointmid and Waypointlow: - if (waypointlow) - { - line[0].x = waypointmid->x; - line[0].y = waypointmid->y; - line[0].z = waypointmid->z; - line[1].x = waypointlow->x; - line[1].y = waypointlow->y; - line[1].z = waypointlow->z; - - P_ClosestPointOnLine3D(&p, line, &resultlow); - } - - // Waypointmid and Waypointhigh: - if (waypointhigh) - { - line[0].x = waypointmid->x; - line[0].y = waypointmid->y; - line[0].z = waypointmid->z; - line[1].x = waypointhigh->x; - line[1].y = waypointhigh->y; - line[1].z = waypointhigh->z; - - P_ClosestPointOnLine3D(&p, line, &resulthigh); - } - - // 3D support now available. Disregard the previous notice here. -Red - - P_UnsetThingPosition(player->mo); - P_ResetPlayer(player); - player->mo->momx = player->mo->momy = player->mo->momz = 0; - - if (lines[lineindex].flags & ML_EFFECT1) // Don't wrap - { - mobj_t *highest = P_GetLastWaypoint(sequence); - highest->flags |= MF_SLIDEME; - } - - // Changing the conditions on these ifs to fix issues with snapping to the wrong spot -Red - if ((lines[lineindex].flags & ML_EFFECT1) && waypointmid->health == 0) - { - closest = waypointhigh; - player->mo->x = resulthigh.x; - player->mo->y = resulthigh.y; - player->mo->z = resulthigh.z - P_GetPlayerHeight(player); - } - else if ((lines[lineindex].flags & ML_EFFECT1) && waypointmid->health == numwaypoints[sequence] - 1) - { - closest = waypointmid; - player->mo->x = resultlow.x; - player->mo->y = resultlow.y; - player->mo->z = resultlow.z - P_GetPlayerHeight(player); - } - else - { - if (P_AproxDistance(P_AproxDistance(player->mo->x-resultlow.x, player->mo->y-resultlow.y), - player->mo->z-resultlow.z) < P_AproxDistance(P_AproxDistance(player->mo->x-resulthigh.x, - player->mo->y-resulthigh.y), player->mo->z-resulthigh.z)) - { - // Line between Mid and Low is closer - closest = waypointmid; - player->mo->x = resultlow.x; - player->mo->y = resultlow.y; - player->mo->z = resultlow.z - P_GetPlayerHeight(player); - } - else - { - // Line between Mid and High is closer - closest = waypointhigh; - player->mo->x = resulthigh.x; - player->mo->y = resulthigh.y; - player->mo->z = resulthigh.z - P_GetPlayerHeight(player); - } - } - - P_SetTarget(&player->mo->tracer, closest); - player->powers[pw_carry] = CR_ROPEHANG; - - // Option for static ropes. - if (lines[lineindex].flags & ML_NOCLIMB) - player->speed = 0; - else - player->speed = speed; - - S_StartSound(player->mo, sfx_s3k4a); - - player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY); - player->climbing = 0; - P_SetThingPosition(player->mo); - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); - } - break; - case 12: // Camera noclip - case 13: // Unused - case 14: // Unused - case 15: // Unused - break; - } -} - -/** Checks if an object is standing on or is inside a special 3D floor. - * If so, the sector is returned. - * - * \param mo Object to check. - * \return Pointer to the sector with a special type, or NULL if no special 3D - * floors are being contacted. - * \sa P_PlayerOnSpecial3DFloor - */ -sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo) -{ - sector_t *sector; - ffloor_t *rover; - fixed_t topheight, bottomheight; - - sector = mo->subsector->sector; - if (!sector->ffloors) - return NULL; - - for (rover = sector->ffloors; rover; rover = rover->next) - { - if (!rover->master->frontsector->special) - continue; - - if (!(rover->flags & FF_EXISTS)) - continue; - - topheight = P_GetSpecialTopZ(mo, sectors + rover->secnum, sector); - bottomheight = P_GetSpecialBottomZ(mo, sectors + rover->secnum, sector); - - // Check the 3D floor's type... - if (((rover->flags & FF_BLOCKPLAYER) && mo->player) - || ((rover->flags & FF_BLOCKOTHERS) && !mo->player)) - { - boolean floorallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(mo->eflags & MFE_VERTICALFLIP)) && (mo->z == topheight)); - boolean ceilingallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (mo->eflags & MFE_VERTICALFLIP)) && (mo->z + mo->height == bottomheight)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - continue; - } - else - { - // Water and intangible FOFs - if (mo->z > topheight || (mo->z + mo->height) < bottomheight) - continue; - } - - return rover->master->frontsector; - } - - return NULL; -} - -#define TELEPORTED (player->mo->subsector->sector != originalsector) - -/** Checks if a player is standing on or is inside a 3D floor (e.g. water) and - * applies any specials. - * - * \param player Player to check. - * \sa P_ThingOnSpecial3DFloor, P_PlayerInSpecialSector - */ -static void P_PlayerOnSpecial3DFloor(player_t *player, sector_t *sector) -{ - sector_t *originalsector = player->mo->subsector->sector; - ffloor_t *rover; - fixed_t topheight, bottomheight; - - for (rover = sector->ffloors; rover; rover = rover->next) - { - if (!rover->master->frontsector->special) - continue; - - if (!(rover->flags & FF_EXISTS)) - continue; - - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, sector); - - // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) - { - boolean floorallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z == topheight)); - boolean ceilingallowed = ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && ((rover->master->frontsector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z + player->mo->height == bottomheight)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - continue; - } - else - { - // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) - continue; - } - - // This FOF has the special we're looking for, but are we allowed to touch it? - if (sector == player->mo->subsector->sector - || (rover->master->frontsector->flags & SF_TRIGGERSPECIAL_TOUCH)) - { - P_ProcessSpecialSector(player, rover->master->frontsector, sector); - if TELEPORTED return; - } - } - - // Allow sector specials to be applied to polyobjects! - if (player->mo->subsector->polyList) - { - polyobj_t *po = player->mo->subsector->polyList; - sector_t *polysec; - boolean touching = false; - boolean inside = false; - - while (po) - { - if (po->flags & POF_NOSPECIALS) - { - po = (polyobj_t *)(po->link.next); - continue; - } - - polysec = po->lines[0]->backsector; - - if ((polysec->flags & SF_TRIGGERSPECIAL_TOUCH)) - touching = P_MobjTouchingPolyobj(po, player->mo); - else - touching = false; - - inside = P_MobjInsidePolyobj(po, player->mo); - - if (!(inside || touching)) - { - po = (polyobj_t *)(po->link.next); - continue; - } - - // We're inside it! Yess... - if (!polysec->special) - { - po = (polyobj_t *)(po->link.next); - continue; - } - - if (!(po->flags & POF_TESTHEIGHT)) // Don't do height checking - ; - else if (po->flags & POF_SOLID) - { - boolean floorallowed = ((polysec->flags & SF_FLIPSPECIAL_FLOOR) && ((polysec->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z == polysec->ceilingheight)); - boolean ceilingallowed = ((polysec->flags & SF_FLIPSPECIAL_CEILING) && ((polysec->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z + player->mo->height == polysec->floorheight)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - { - po = (polyobj_t *)(po->link.next); - continue; - } - } - else - { - // Water and DEATH FOG!!! heh - if (player->mo->z > polysec->ceilingheight || (player->mo->z + player->mo->height) < polysec->floorheight) - { - po = (polyobj_t *)(po->link.next); - continue; - } - } - - P_ProcessSpecialSector(player, polysec, sector); - if TELEPORTED return; - - po = (polyobj_t *)(po->link.next); - } - } -} - -#define VDOORSPEED (FRACUNIT*2) - -// -// P_RunSpecialSectorCheck -// -// Helper function to P_PlayerInSpecialSector -// -static void P_RunSpecialSectorCheck(player_t *player, sector_t *sector) -{ - boolean nofloorneeded = false; - fixed_t f_affectpoint, c_affectpoint; - - if (!sector->special) // nothing special, exit - return; - - if (GETSECSPECIAL(sector->special, 2) == 9) // Egg trap capsule -- should only be for 3dFloors! - return; - - // The list of specials that activate without floor touch - // Check Section 1 - switch(GETSECSPECIAL(sector->special, 1)) - { - case 2: // Damage (water) - case 8: // Instant kill - case 10: // Ring drainer that doesn't require floor touch - case 12: // Space countdown - nofloorneeded = true; - break; - } - - // Check Section 2 - switch(GETSECSPECIAL(sector->special, 2)) - { - case 2: // Linedef executor (All players needed) - case 4: // Linedef executor - case 6: // Linedef executor (7 Emeralds) - case 7: // Linedef executor (NiGHTS Mare) - nofloorneeded = true; - break; - } - - // Check Section 3 -/* switch(GETSECSPECIAL(sector->special, 3)) - { - - }*/ - - // Check Section 4 - switch(GETSECSPECIAL(sector->special, 4)) - { - case 2: // Level Exit / GOAL Sector / Flag Return - if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap)) - { - // Special stage GOAL sector - // requires touching floor. - break; - } - /* FALLTHRU */ - - case 1: // Starpost activator - case 5: // Fan sector - case 6: // Super Sonic Transform - case 8: // Zoom Tube Start - case 9: // Zoom Tube End - case 10: // Finish line - nofloorneeded = true; - break; - } - - if (nofloorneeded) - { - P_ProcessSpecialSector(player, sector, NULL); - return; - } - - f_affectpoint = P_GetSpecialBottomZ(player->mo, sector, sector); - c_affectpoint = P_GetSpecialTopZ(player->mo, sector, sector); - - { - boolean floorallowed = ((sector->flags & SF_FLIPSPECIAL_FLOOR) && ((sector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || !(player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z == f_affectpoint)); - boolean ceilingallowed = ((sector->flags & SF_FLIPSPECIAL_CEILING) && ((sector->flags & SF_TRIGGERSPECIAL_HEADBUMP) || (player->mo->eflags & MFE_VERTICALFLIP)) && (player->mo->z + player->mo->height == c_affectpoint)); - // Thing must be on top of the floor to be affected... - if (!(floorallowed || ceilingallowed)) - return; - } - - P_ProcessSpecialSector(player, sector, NULL); -} - -/** Checks if the player is in a special sector or FOF and apply any specials. - * - * \param player Player to check. - * \sa P_PlayerOnSpecial3DFloor, P_ProcessSpecialSector - */ -void P_PlayerInSpecialSector(player_t *player) -{ - sector_t *originalsector; - sector_t *loopsector; - msecnode_t *node; - - if (!player->mo) - return; - - originalsector = player->mo->subsector->sector; - - P_PlayerOnSpecial3DFloor(player, originalsector); // Handle FOFs first. - if TELEPORTED return; - - P_RunSpecialSectorCheck(player, originalsector); - if TELEPORTED return; - - // Iterate through touching_sectorlist for SF_TRIGGERSPECIAL_TOUCH - for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - loopsector = node->m_sector; - - if (loopsector == originalsector) // Don't duplicate - continue; - - // Check 3D floors... - P_PlayerOnSpecial3DFloor(player, loopsector); - if TELEPORTED return; - - if (!(loopsector->flags & SF_TRIGGERSPECIAL_TOUCH)) - continue; - - P_RunSpecialSectorCheck(player, loopsector); - if TELEPORTED return; - } -} - -#undef TELEPORTED - -/** Animate planes, scroll walls, etc. and keeps track of level timelimit and exits if time is up. - * - * \sa P_CheckTimeLimit, P_CheckPointLimit - */ -void P_UpdateSpecials(void) -{ - anim_t *anim; - INT32 i; - INT32 pic; - size_t j; - - levelflat_t *foundflats; // for flat animation - - // LEVEL TIMER - P_CheckTimeLimit(); - - // POINT LIMIT - P_CheckPointLimit(); - - // ANIMATE TEXTURES - for (anim = anims; anim < lastanim; anim++) - { - for (i = 0; i < anim->numpics; i++) - { - pic = anim->basepic + ((leveltime/anim->speed + i) % anim->numpics); - if (anim->istexture) - texturetranslation[anim->basepic+i] = pic; - } - } - - // ANIMATE FLATS - /// \todo do not check the non-animate flat.. link the animated ones? - /// \note its faster than the original anywaysince it animates only - /// flats used in the level, and there's usually very few of them - foundflats = levelflats; - for (j = 0; j < numlevelflats; j++, foundflats++) - { - if (foundflats->speed) // it is an animated flat - { - // update the levelflat texture number - if ((foundflats->type == LEVELFLAT_TEXTURE) && (foundflats->u.texture.basenum != -1)) - foundflats->u.texture.num = foundflats->u.texture.basenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); - // update the levelflat lump number - else if ((foundflats->type == LEVELFLAT_FLAT) && (foundflats->u.flat.baselumpnum != LUMPERROR)) - foundflats->u.flat.lumpnum = foundflats->u.flat.baselumpnum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics); - } - } -} - -// -// Floor over floors (FOFs), 3Dfloors, 3Dblocks, fake floors (ffloors), rovers, or whatever you want to call them -// - -/** Gets the ID number for a 3Dfloor in its target sector. - * - * \param fflr The 3Dfloor we want an ID for. - * \return ID of 3Dfloor in target sector. Note that the first FOF's ID is 0. UINT16_MAX is given if invalid. - * \sa P_GetFFloorByID - */ -UINT16 P_GetFFloorID(ffloor_t *fflr) -{ - ffloor_t *rover; - sector_t *sec; - UINT16 i = 0; - - if (!fflr) - return UINT16_MAX; - - sec = fflr->target; - - if (!sec->ffloors) - return UINT16_MAX; - for (rover = sec->ffloors; rover; rover = rover->next, i++) - if (rover == fflr) - return i; - return UINT16_MAX; -} - -/** Gets a 3Dfloor by control sector. - * - * \param sec Target sector. - * \param sec2 Control sector. - * \return Pointer to found 3Dfloor, or NULL. - * \sa P_GetFFloorByID - */ -static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2) -{ - ffloor_t *rover; - - if (!sec->ffloors) - return NULL; - for (rover = sec->ffloors; rover; rover = rover->next) - if (rover->secnum == (size_t)(sec2 - sectors)) - return rover; - return NULL; -} - -/** Gets a 3Dfloor by ID number. - * - * \param sec Target sector. - * \param id ID of 3Dfloor in target sector. Note that the first FOF's ID is 0. - * \return Pointer to found 3Dfloor, or NULL. - * \sa P_GetFFloorBySec, P_GetFFloorID - */ -ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id) -{ - ffloor_t *rover; - UINT16 i = 0; - - if (!sec->ffloors) - return NULL; - for (rover = sec->ffloors; rover; rover = rover->next) - if (i++ == id) - return rover; - return NULL; -} - -/** Adds a newly formed 3Dfloor structure to a sector's ffloors list. - * - * \param sec Target sector. - * \param fflr Newly formed 3Dfloor structure. - * \sa P_AddFakeFloor - */ -static inline void P_AddFFloorToList(sector_t *sec, ffloor_t *fflr) -{ - ffloor_t *rover; - - if (!sec->ffloors) - { - sec->ffloors = fflr; - fflr->next = 0; - fflr->prev = 0; - return; - } - - for (rover = sec->ffloors; rover->next; rover = rover->next); - - rover->next = fflr; - fflr->prev = rover; - fflr->next = 0; -} - -/** Adds a 3Dfloor. - * - * \param sec Target sector. - * \param sec2 Control sector. - * \param master Control linedef. - * \param alpha Alpha value (0-255). - * \param flags Options affecting this 3Dfloor. - * \param secthinkers List of relevant thinkers sorted by sector. May be NULL. - * \return Pointer to the new 3Dfloor. - * \sa P_AddFFloor, P_AddFakeFloorsByLine, P_SpawnSpecials - */ -static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, INT32 alpha, ffloortype_e flags, thinkerlist_t *secthinkers) -{ - ffloor_t *fflr; - thinker_t *th; - friction_t *f; - pusher_t *p; - size_t sec2num; - size_t i; - - if (sec == sec2) - return NULL; //Don't need a fake floor on a control sector. - if ((fflr = (P_GetFFloorBySec(sec, sec2)))) - return fflr; // If this ffloor already exists, return it - - if (sec2->ceilingheight < sec2->floorheight) - { - fixed_t tempceiling = sec2->ceilingheight; - //flip the sector around and print an error instead of crashing 12.1.08 -Inuyasha - CONS_Alert(CONS_ERROR, M_GetText("A FOF tagged %d has a top height below its bottom.\n"), master->args[0]); - sec2->ceilingheight = sec2->floorheight; - sec2->floorheight = tempceiling; - } - - if (sec2->numattached == 0) - { - sec2->attached = Z_Malloc(sizeof (*sec2->attached) * sec2->maxattached, PU_STATIC, NULL); - sec2->attachedsolid = Z_Malloc(sizeof (*sec2->attachedsolid) * sec2->maxattached, PU_STATIC, NULL); - sec2->attached[0] = sec - sectors; - sec2->numattached = 1; - sec2->attachedsolid[0] = (flags & FF_SOLID); - } - else - { - for (i = 0; i < sec2->numattached; i++) - if (sec2->attached[i] == (size_t)(sec - sectors)) - return NULL; - - if (sec2->numattached >= sec2->maxattached) - { - sec2->maxattached *= 2; - sec2->attached = Z_Realloc(sec2->attached, sizeof (*sec2->attached) * sec2->maxattached, PU_STATIC, NULL); - sec2->attachedsolid = Z_Realloc(sec2->attachedsolid, sizeof (*sec2->attachedsolid) * sec2->maxattached, PU_STATIC, NULL); - } - sec2->attached[sec2->numattached] = sec - sectors; - sec2->attachedsolid[sec2->numattached] = (flags & FF_SOLID); - sec2->numattached++; - } - - // Add the floor - fflr = Z_Calloc(sizeof (*fflr), PU_LEVEL, NULL); - fflr->secnum = sec2 - sectors; - fflr->target = sec; - fflr->bottomheight = &sec2->floorheight; - fflr->bottompic = &sec2->floorpic; - fflr->bottomxoffs = &sec2->floor_xoffs; - fflr->bottomyoffs = &sec2->floor_yoffs; - fflr->bottomangle = &sec2->floorpic_angle; - - // Add the ceiling - fflr->topheight = &sec2->ceilingheight; - fflr->toppic = &sec2->ceilingpic; - fflr->toplightlevel = &sec2->lightlevel; - fflr->topxoffs = &sec2->ceiling_xoffs; - fflr->topyoffs = &sec2->ceiling_yoffs; - fflr->topangle = &sec2->ceilingpic_angle; - - // Add slopes - fflr->t_slope = &sec2->c_slope; - fflr->b_slope = &sec2->f_slope; - // mark the target sector as having slopes, if the FOF has any of its own - // (this fixes FOF slopes glitching initially at level load in software mode) - if (sec2->hasslope) - sec->hasslope = true; - - fflr->spawnflags = fflr->flags = flags; - fflr->master = master; - fflr->norender = INFTICS; - fflr->fadingdata = NULL; - - // Scan the thinkers to check for special conditions applying to this FOF. - // If we have thinkers sorted by sector, just check the relevant ones; - // otherwise, check them all. Apologies for the ugly loop... - - sec2num = sec2 - sectors; - - // Just initialise both of these to placate the compiler. - i = 0; - th = thlist[THINK_MAIN].next; - - for(;;) - { - if(secthinkers) - { - if(i < secthinkers[sec2num].count) - th = secthinkers[sec2num].thinkers[i]; - else break; - } - else if (th == &thlist[THINK_MAIN]) - break; - - // Should this FOF have friction? - if(th->function.acp1 == (actionf_p1)T_Friction) - { - f = (friction_t *)th; - - if (f->affectee == (INT32)sec2num) - Add_Friction(f->friction, f->movefactor, (INT32)(sec-sectors), f->affectee); - } - // Should this FOF have wind/current/pusher? - else if(th->function.acp1 == (actionf_p1)T_Pusher) - { - p = (pusher_t *)th; - - if (p->affectee == (INT32)sec2num) - Add_Pusher(p->type, p->x_mag<y_mag<source, (INT32)(sec-sectors), p->affectee, p->exclusive, p->slider); - } - - if(secthinkers) i++; - else th = th->next; - } - - fflr->alpha = max(0, min(0xff, alpha)); - if (fflr->alpha < 0xff) - { - fflr->flags |= FF_TRANSLUCENT; - fflr->spawnflags = fflr->flags; - } - fflr->spawnalpha = fflr->alpha; // save for netgames - - if (flags & FF_QUICKSAND) - CheckForQuicksand = true; - - if (flags & FF_BUSTUP) - CheckForBustableBlocks = true; - - if ((flags & FF_MARIO)) - { - if (!(flags & FF_GOOWATER)) // Don't change the textures of a brick block, just a question block - P_AddBlockThinker(sec2, master); - CheckForMarioBlocks = true; - } - - if ((flags & FF_CRUMBLE)) - sec2->crumblestate = CRUMBLE_WAIT; - - if ((flags & FF_FLOATBOB)) - { - P_AddFloatThinker(sec2, Tag_FGet(&master->tags), master); - CheckForFloatBob = true; - } - - P_AddFFloorToList(sec, fflr); - - return fflr; -} - -// -// SPECIAL SPAWNING -// - -/** Adds a float thinker. - * Float thinkers cause solid 3Dfloors to float on water. - * - * \param sec Control sector. - * \param actionsector Target sector. - * \sa P_SpawnSpecials, T_FloatSector - * \author SSNTails - */ -static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline) -{ - floatthink_t *floater; - - // create and initialize new thinker - floater = Z_Calloc(sizeof (*floater), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &floater->thinker); - - floater->thinker.function.acp1 = (actionf_p1)T_FloatSector; - - floater->sector = sec; - floater->tag = (INT16)tag; - floater->sourceline = sourceline; -} - -/** - * Adds a plane displacement thinker. - * Whenever the "control" sector moves, - * the "affectee" sector's floor or ceiling plane moves too! - * - * \param speed Rate of movement relative to control sector - * \param control Control sector. - * \param affectee Target sector. - * \param reverse Reverse direction? - * \sa P_SpawnSpecials, T_PlaneDisplace - * \author Monster Iestyn - */ -static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee, UINT8 reverse) -{ - planedisplace_t *displace; - - // create and initialize new displacement thinker - displace = Z_Calloc(sizeof (*displace), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &displace->thinker); - - displace->thinker.function.acp1 = (actionf_p1)T_PlaneDisplace; - displace->affectee = affectee; - displace->control = control; - displace->last_height = sectors[control].floorheight; - displace->speed = speed; - displace->type = type; - displace->reverse = reverse; -} - -/** Adds a Mario block thinker, which changes the block's texture between blank - * and ? depending on whether it has contents. - * Needed in case objects respawn inside. - * - * \param sec Control sector. - * \param actionsector Target sector. - * \param sourceline Control linedef. - * \sa P_SpawnSpecials, T_MarioBlockChecker - * \author SSNTails - */ -static void P_AddBlockThinker(sector_t *sec, line_t *sourceline) -{ - mariocheck_t *block; - - // create and initialize new elevator thinker - block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &block->thinker); - - block->thinker.function.acp1 = (actionf_p1)T_MarioBlockChecker; - block->sourceline = sourceline; - - block->sector = sec; -} - -/** Adds a raise thinker. - * A raise thinker checks to see if the - * player is standing on its 3D Floor, - * and if so, raises the platform towards - * it's destination. Otherwise, it lowers - * to the lowest nearby height if not - * there already. - * - * \param sec Control sector. - * \sa P_SpawnSpecials, T_RaiseSector - * \author SSNTails - */ -static void P_AddRaiseThinker(sector_t *sec, INT16 tag, fixed_t speed, fixed_t ceilingtop, fixed_t ceilingbottom, boolean lower, boolean spindash) -{ - raise_t *raise; - - raise = Z_Calloc(sizeof (*raise), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &raise->thinker); - - raise->thinker.function.acp1 = (actionf_p1)T_RaiseSector; - - raise->tag = tag; - raise->sector = sec; - - raise->ceilingtop = ceilingtop; - raise->ceilingbottom = ceilingbottom; - - raise->basespeed = speed >> 2; - - if (lower) - raise->flags |= RF_REVERSE; - if (spindash) - raise->flags |= RF_SPINDASH; -} - -static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, boolean spindash, boolean dynamic) -{ - raise_t *airbob; - - airbob = Z_Calloc(sizeof (*airbob), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &airbob->thinker); - - airbob->thinker.function.acp1 = (actionf_p1)T_RaiseSector; - - airbob->tag = tag; - airbob->sector = sec; - - airbob->ceilingtop = sec->ceilingheight; - airbob->ceilingbottom = sec->ceilingheight - dist; - - airbob->basespeed = FRACUNIT; - - if (!raise) - airbob->flags |= RF_REVERSE; - if (spindash) - airbob->flags |= RF_SPINDASH; - if (dynamic) - airbob->flags |= RF_DYNAMIC; -} - -/** Adds a thwomp thinker. - * Even thwomps need to think! - * - * \param sec Control sector. - * \sa P_SpawnSpecials, T_ThwompSector - * \author SSNTails - */ -static inline void P_AddThwompThinker(sector_t *sec, line_t *sourceline, fixed_t crushspeed, fixed_t retractspeed, UINT16 sound) -{ - thwomp_t *thwomp; - - // You *probably* already have a thwomp in this sector. If you've combined it with something - // else that uses the floordata/ceilingdata, you must be weird. - if (sec->floordata || sec->ceilingdata) - return; - - // create and initialize new elevator thinker - thwomp = Z_Calloc(sizeof (*thwomp), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &thwomp->thinker); - - thwomp->thinker.function.acp1 = (actionf_p1)T_ThwompSector; - - // set up the fields according to the type of elevator action - thwomp->sourceline = sourceline; - thwomp->sector = sec; - thwomp->crushspeed = crushspeed; - thwomp->retractspeed = retractspeed; - thwomp->direction = 0; - thwomp->floorstartheight = sec->floorheight; - thwomp->ceilingstartheight = sec->ceilingheight; - thwomp->delay = 1; - thwomp->tag = Tag_FGet(&sourceline->tags); - thwomp->sound = sound; - - sec->floordata = thwomp; - sec->ceilingdata = thwomp; - // Start with 'resting' texture - sides[sourceline->sidenum[0]].midtexture = sides[sourceline->sidenum[0]].bottomtexture; -} - -/** Adds a thinker which checks if any MF_ENEMY objects with health are in the defined area. - * If not, a linedef executor is run once. - * - * \param sourceline Control linedef. - * \sa P_SpawnSpecials, T_NoEnemiesSector - * \author SSNTails - */ -static inline void P_AddNoEnemiesThinker(line_t *sourceline) -{ - noenemies_t *nobaddies; - - // create and initialize new thinker - nobaddies = Z_Calloc(sizeof (*nobaddies), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &nobaddies->thinker); - - nobaddies->thinker.function.acp1 = (actionf_p1)T_NoEnemiesSector; - - nobaddies->sourceline = sourceline; -} - -/** Adds a thinker for Each-Time linedef executors. A linedef executor is run - * only when a player enters the area and doesn't run again until they re-enter. - * - * \param sourceline Control linedef. - * \sa P_SpawnSpecials, T_EachTimeThinker - * \author SSNTails - */ -static void P_AddEachTimeThinker(line_t *sourceline) -{ - eachtime_t *eachtime; - - // create and initialize new thinker - eachtime = Z_Calloc(sizeof (*eachtime), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &eachtime->thinker); - - eachtime->thinker.function.acp1 = (actionf_p1)T_EachTimeThinker; - - eachtime->sourceline = sourceline; - eachtime->triggerOnExit = !!(sourceline->flags & ML_BOUNCY); -} - -/** Adds a camera scanner. - * - * \param sourcesec Control sector. - * \param actionsector Target sector. - * \param angle Angle of the source line. - * \sa P_SpawnSpecials, T_CameraScanner - * \author SSNTails - */ -static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsector, angle_t angle) -{ - elevator_t *elevator; // Why not? LOL - - CONS_Alert(CONS_WARNING, M_GetText("Detected a camera scanner effect (linedef type 5). This effect is deprecated and will be removed in the future!\n")); - - // create and initialize new elevator thinker - elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL); - P_AddThinker(THINK_MAIN, &elevator->thinker); - - elevator->thinker.function.acp1 = (actionf_p1)T_CameraScanner; - elevator->type = elevateBounce; - - // set up the fields according to the type of elevator action - elevator->sector = sourcesec; - elevator->actionsector = actionsector; - elevator->distance = FixedInt(AngleFixed(angle)); -} - -/** Flashes a laser block. - * - * \param flash Thinker structure for this laser. - * \sa P_AddLaserThinker - * \author SSNTails - */ -void T_LaserFlash(laserthink_t *flash) -{ - msecnode_t *node; - mobj_t *thing; - INT32 s; - ffloor_t *fflr; - sector_t *sector; - sector_t *sourcesec = flash->sourceline->frontsector; - fixed_t top, bottom; - - TAG_ITER_SECTORS(flash->tag, s) - { - sector = §ors[s]; - for (fflr = sector->ffloors; fflr; fflr = fflr->next) - { - if (fflr->master != flash->sourceline) - continue; - - if (!(fflr->flags & FF_EXISTS)) - break; - - if (leveltime & 2) - //fflr->flags |= FF_RENDERALL; - fflr->alpha = 0xB0; - else - //fflr->flags &= ~FF_RENDERALL; - fflr->alpha = 0x90; - - top = P_GetFFloorTopZAt (fflr, sector->soundorg.x, sector->soundorg.y); - bottom = P_GetFFloorBottomZAt(fflr, sector->soundorg.x, sector->soundorg.y); - sector->soundorg.z = (top + bottom)/2; - S_StartSound(§or->soundorg, sfx_laser); - - // Seek out objects to DESTROY! MUAHAHHAHAHAA!!!*cough* - for (node = sector->touching_thinglist; node && node->m_thing; node = node->m_thinglist_next) - { - thing = node->m_thing; - - if (flash->nobosses && thing->flags & MF_BOSS) - continue; // Don't hurt bosses - - // Don't endlessly kill egg guard shields (or anything else for that matter) - if (thing->health <= 0) - continue; - - top = P_GetSpecialTopZ(thing, sourcesec, sector); - bottom = P_GetSpecialBottomZ(thing, sourcesec, sector); - - if (thing->z >= top - || thing->z + thing->height <= bottom) - continue; - - if (thing->flags & MF_SHOOTABLE) - P_DamageMobj(thing, NULL, NULL, 1, 0); - else if (thing->type == MT_EGGSHIELD) - P_KillMobj(thing, NULL, NULL, 0); - } - - break; - } - } -} - -static inline void P_AddLaserThinker(INT16 tag, line_t *line, boolean nobosses) -{ - laserthink_t *flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL); - - P_AddThinker(THINK_MAIN, &flash->thinker); - - flash->thinker.function.acp1 = (actionf_p1)T_LaserFlash; - flash->tag = tag; - flash->sourceline = line; - flash->nobosses = nobosses; -} - -// -// P_RunLevelLoadExecutors -// -// After loading/spawning all other specials -// and items, execute these. -// -static void P_RunLevelLoadExecutors(void) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 399) - P_RunTriggerLinedef(&lines[i], NULL, NULL); - } -} - -/** Before things are loaded, initialises certain stuff in case they're needed - * by P_SpawnSlopes or P_LoadThings. This was split off from - * P_SpawnSpecials, in case you couldn't tell. - * - * \sa P_SpawnSpecials - * \author Monster Iestyn - */ -void P_InitSpecials(void) -{ - // Set the default gravity. Custom gravity overrides this setting. - gravity = mapheaderinfo[gamemap-1]->gravity; - - // Defaults in case levels don't have them set. - sstimer = mapheaderinfo[gamemap-1]->sstimer*TICRATE + 6; - ssspheres = mapheaderinfo[gamemap-1]->ssspheres; - - CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; - - // Set curWeather - switch (mapheaderinfo[gamemap-1]->weather) - { - case PRECIP_SNOW: // snow - case PRECIP_RAIN: // rain - case PRECIP_STORM: // storm - case PRECIP_STORM_NORAIN: // storm w/o rain - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - curWeather = mapheaderinfo[gamemap-1]->weather; - break; - default: // blank/none - curWeather = PRECIP_NONE; - break; - } - - // Set globalweather - globalweather = mapheaderinfo[gamemap-1]->weather; -} - -static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs) -{ - if (!(master->flags & ML_NETONLY)) // Modify floor flat alignment unless ML_NETONLY flag is set - { - sector->floorpic_angle = flatangle; - sector->floor_xoffs += xoffs; - sector->floor_yoffs += yoffs; - } - - if (!(master->flags & ML_NONET)) // Modify ceiling flat alignment unless ML_NONET flag is set - { - sector->ceilingpic_angle = flatangle; - sector->ceiling_xoffs += xoffs; - sector->ceiling_yoffs += yoffs; - } - -} - -static void P_MakeFOFBouncy(line_t *paramline, line_t *masterline) -{ - INT32 s; - TAG_ITER_DECLARECOUNTER(0); - - if (masterline->special < 100 || masterline->special >= 300) - return; - - TAG_ITER_SECTORS(0, masterline->args[0], s) - { - ffloor_t *rover; - - for (rover = sectors[s].ffloors; rover; rover = rover->next) - { - if (rover->master != masterline) - continue; - - rover->flags |= FF_BOUNCY; - rover->spawnflags |= FF_BOUNCY; - rover->bouncestrength = (paramline->args[1]<< FRACBITS)/100; - if (paramline->args[2]) - rover->specialflags |= FS_DAMPEN; - else - rover->specialflags &= ~FS_DAMPEN; - CheckForBouncySector = true; - break; - } - } - -} - -/** After the map has loaded, scans for specials that spawn 3Dfloors and - * thinkers. - * - * \todo Split up into multiple functions. - * \todo Get rid of all the magic numbers. - * \todo Potentially use 'fromnetsave' to stop any new thinkers from being created - * as they'll just be erased by UnArchiveThinkers. - * \sa P_SpawnPrecipitation, P_SpawnFriction, P_SpawnPushers, P_SpawnScrollers - */ -void P_SpawnSpecials(boolean fromnetsave) -{ - sector_t *sector; - size_t i; - INT32 j; - thinkerlist_t *secthinkers; - thinker_t *th; - TAG_ITER_DECLARECOUNTER(0); - // This used to be used, and *should* be used in the future, - // but currently isn't. - (void)fromnetsave; - - // yep, we do this here - "bossdisabled" is considered an apparatus of specials. - bossdisabled = 0; - stoppedclock = false; - - // Init special SECTORs. - sector = sectors; - for (i = 0; i < numsectors; i++, sector++) - { - if (!sector->special) - continue; - - // Process Section 1 - switch(GETSECSPECIAL(sector->special, 1)) - { - case 5: // Spikes - //Terrible hack to replace an even worse hack: - //Spike damage automatically sets SF_TRIGGERSPECIAL_TOUCH. - //Yes, this also affects other specials on the same sector. Sorry. - sector->flags |= SF_TRIGGERSPECIAL_TOUCH; - break; - } - - // Process Section 2 - switch(GETSECSPECIAL(sector->special, 2)) - { - case 10: // Time for special stage - sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish - ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage - break; - - case 11: // Custom global gravity! - gravity = sector->floorheight/1000; - break; - } - - // Process Section 3 -/* switch(GETSECSPECIAL(player->specialsector, 3)) - { - - }*/ - - // Process Section 4 - switch(GETSECSPECIAL(sector->special, 4)) - { - case 10: // Circuit finish line - if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) - circuitmap = true; - break; - } - } - - P_SpawnScrollers(); // Add generalized scrollers - P_SpawnFriction(); // Friction model using linedefs - P_SpawnPushers(); // Pusher model using linedefs - - // Look for thinkers that affect FOFs, and sort them by sector - - secthinkers = Z_Calloc(numsectors * sizeof(thinkerlist_t), PU_STATIC, NULL); - - // Firstly, find out how many there are in each sector - for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)T_Friction) - secthinkers[((friction_t *)th)->affectee].count++; - else if (th->function.acp1 == (actionf_p1)T_Pusher) - secthinkers[((pusher_t *)th)->affectee].count++; - } - - // Allocate each list, and then zero the count so we can use it to track - // the end of the list as we add the thinkers - for (i = 0; i < numsectors; i++) - if(secthinkers[i].count > 0) - { - secthinkers[i].thinkers = Z_Malloc(secthinkers[i].count * sizeof(thinker_t *), PU_STATIC, NULL); - secthinkers[i].count = 0; - } - - // Finally, populate the lists. - for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next) - { - size_t secnum = (size_t)-1; - - if (th->function.acp1 == (actionf_p1)T_Friction) - secnum = ((friction_t *)th)->affectee; - else if (th->function.acp1 == (actionf_p1)T_Pusher) - secnum = ((pusher_t *)th)->affectee; - - if (secnum != (size_t)-1) - secthinkers[secnum].thinkers[secthinkers[secnum].count++] = th; - } - - - // Init line EFFECTs - for (i = 0; i < numlines; i++) - { - mtag_t tag = Tag_FGet(&lines[i].tags); - - if (lines[i].special != 7) // This is a hack. I can at least hope nobody wants to prevent flat alignment in netgames... - { - // set line specials to 0 here too, same reason as above - if (netgame || multiplayer) - { - if (lines[i].flags & ML_NONET) - { - lines[i].special = 0; - continue; - } - } - else if (lines[i].flags & ML_NETONLY) - { - lines[i].special = 0; - continue; - } - } - - switch (lines[i].special) - { - INT32 s; - INT32 l; - size_t sec; - ffloortype_e ffloorflags; - - case 1: // Definable gravity per sector - sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(tag, s) - { - sectors[s].gravity = §ors[sec].floorheight; // This allows it to change in realtime! - - if (lines[i].flags & ML_NOCLIMB) - sectors[s].verticalflip = true; - else - sectors[s].verticalflip = false; - - CheckForReverseGravity = sectors[s].verticalflip; - } - break; - - case 2: // Custom exit - break; - - case 3: // Zoom Tube Parameters - break; - - case 4: // Speed pad (combines with sector special Section3:5 or Section3:6) - break; - - case 5: // Change camera info - sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(tag, s) - P_AddCameraScanner(§ors[sec], §ors[s], R_PointToAngle2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y)); - break; - - case 7: // Flat alignment - redone by toast - if ((lines[i].flags & (ML_NETONLY|ML_NONET)) != (ML_NETONLY|ML_NONET)) // If you can do something... - { - angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y)); - fixed_t xoffs; - fixed_t yoffs; - - if (lines[i].flags & ML_EFFECT6) // Set offset through x and y texture offsets if ML_EFFECT6 flag is set - { - xoffs = sides[lines[i].sidenum[0]].textureoffset; - yoffs = sides[lines[i].sidenum[0]].rowoffset; - } - else // Otherwise, set calculated offsets such that line's v1 is the apparent origin - { - xoffs = -lines[i].v1->x; - yoffs = lines[i].v1->y; - } - - //If no tag is given, apply to front sector - if (tag == 0) - P_ApplyFlatAlignment(lines + i, lines[i].frontsector, flatangle, xoffs, yoffs); - else - { - TAG_ITER_SECTORS(tag, s) - P_ApplyFlatAlignment(lines + i, sectors + s, flatangle, xoffs, yoffs); - } - } - else // Otherwise, print a helpful warning. Can I do no less? - CONS_Alert(CONS_WARNING, - M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"), - tag); - break; - - case 8: // Sector Parameters - TAG_ITER_SECTORS(tag, s) - { - if (lines[i].flags & ML_NOCLIMB) - { - sectors[s].flags &= ~SF_FLIPSPECIAL_FLOOR; - sectors[s].flags |= SF_FLIPSPECIAL_CEILING; - } - else if (lines[i].flags & ML_EFFECT4) - sectors[s].flags |= SF_FLIPSPECIAL_BOTH; - - if (lines[i].flags & ML_EFFECT3) - sectors[s].flags |= SF_TRIGGERSPECIAL_TOUCH; - if (lines[i].flags & ML_EFFECT2) - sectors[s].flags |= SF_TRIGGERSPECIAL_HEADBUMP; - - if (lines[i].flags & ML_EFFECT1) - sectors[s].flags |= SF_INVERTPRECIP; - - if (lines[i].frontsector && GETSECSPECIAL(lines[i].frontsector->special, 4) == 12) - sectors[s].camsec = sides[*lines[i].sidenum].sector-sectors; - } - break; - - case 9: // Chain Parameters - break; - - case 10: // Vertical culling plane for sprites and FOFs - TAG_ITER_SECTORS(tag, s) - sectors[s].cullheight = &lines[i]; // This allows it to change in realtime! - break; - - case 50: // Insta-Lower Sector - EV_DoFloor(&lines[i], instantLower); - break; - - case 51: // Instant raise for ceilings - EV_DoCeiling(&lines[i], instantRaise); - break; - - case 52: // Continuously Falling sector - EV_DoContinuousFall(lines[i].frontsector, lines[i].backsector, P_AproxDistance(lines[i].dx, lines[i].dy), (lines[i].flags & ML_NOCLIMB)); - break; - - case 53: // New super cool and awesome moving floor and ceiling type - case 54: // New super cool and awesome moving floor type - if (lines[i].backsector) - EV_DoFloor(&lines[i], bounceFloor); - if (lines[i].special == 54) - break; - /* FALLTHRU */ - - case 55: // New super cool and awesome moving ceiling type - if (lines[i].backsector) - EV_DoCeiling(&lines[i], bounceCeiling); - break; - - case 56: // New super cool and awesome moving floor and ceiling crush type - case 57: // New super cool and awesome moving floor crush type - if (lines[i].backsector) - EV_DoFloor(&lines[i], bounceFloorCrush); - - if (lines[i].special == 57) - break; //only move the floor - /* FALLTHRU */ - - case 58: // New super cool and awesome moving ceiling crush type - if (lines[i].backsector) - EV_DoCeiling(&lines[i], bounceCeilingCrush); - break; - - case 59: // Activate floating platform - EV_DoElevator(&lines[i], elevateContinuous, false); - break; - - case 60: // Floating platform with adjustable speed - EV_DoElevator(&lines[i], elevateContinuous, true); - break; - - case 61: // Crusher! - EV_DoCrush(&lines[i], crushAndRaise); - break; - - case 62: // Crusher (up and then down)! - EV_DoCrush(&lines[i], fastCrushAndRaise); - break; - - case 63: // support for drawn heights coming from different sector - sec = sides[*lines[i].sidenum].sector-sectors; - TAG_ITER_SECTORS(tag, s) - sectors[s].heightsec = (INT32)sec; - break; - - case 64: // Appearing/Disappearing FOF option - if (lines[i].flags & ML_BLOCKMONSTERS) { // Find FOFs by control sector tag - TAG_ITER_SECTORS(tag, s) - for (j = 0; (unsigned)j < sectors[s].linecount; j++) - if (sectors[s].lines[j]->special >= 100 && sectors[s].lines[j]->special < 300) - Add_MasterDisappearer(abs(lines[i].dx>>FRACBITS), abs(lines[i].dy>>FRACBITS), abs(sides[lines[i].sidenum[0]].sector->floorheight>>FRACBITS), (INT32)(sectors[s].lines[j]-lines), (INT32)i); - } else // Find FOFs by effect sector tag - { - TAG_ITER_LINES(tag, s) - { - if ((size_t)s == i) - continue; - if (Tag_Find(&sides[lines[s].sidenum[0]].sector->tags, Tag_FGet(&sides[lines[i].sidenum[0]].sector->tags))) - Add_MasterDisappearer(abs(lines[i].dx>>FRACBITS), abs(lines[i].dy>>FRACBITS), abs(sides[lines[i].sidenum[0]].sector->floorheight>>FRACBITS), s, (INT32)i); - } - } - break; - - case 66: // Displace floor by front sector - TAG_ITER_SECTORS(tag, s) - P_AddPlaneDisplaceThinker(pd_floor, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); - break; - case 67: // Displace ceiling by front sector - TAG_ITER_SECTORS(tag, s) - P_AddPlaneDisplaceThinker(pd_ceiling, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); - break; - case 68: // Displace both floor AND ceiling by front sector - TAG_ITER_SECTORS(tag, s) - P_AddPlaneDisplaceThinker(pd_both, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); - break; - - case 70: // Add raise thinker to FOF - if (udmf) - { - fixed_t destheight = lines[i].args[2] << FRACBITS; - fixed_t startheight, topheight, bottomheight; - - TAG_ITER_LINES(0, lines[i].args[0], l) - { - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - startheight = lines[l].frontsector->ceilingheight; - topheight = max(startheight, destheight); - bottomheight = min(startheight, destheight); - - P_AddRaiseThinker(lines[l].frontsector, lines[l].args[0], lines[i].args[1] << FRACBITS, topheight, bottomheight, (destheight < startheight), !!(lines[i].args[3])); - } - } - break; - - case 71: // Add air bob thinker to FOF - if (udmf) - { - TAG_ITER_LINES(0, lines[i].args[0], l) - { - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - P_AddAirbob(lines[l].frontsector, lines[l].args[0], lines[i].args[1] << FRACBITS, !!(lines[i].args[2] & TMFB_REVERSE), !!(lines[i].args[2] & TMFB_SPINDASH), !!(lines[i].args[2] & TMFB_DYNAMIC)); - } - } - break; - - case 72: // Add thwomp thinker to FOF - if (udmf) - { - UINT16 sound = (lines[i].stringargs[0]) ? get_number(lines[i].stringargs[0]) : sfx_thwomp; - - TAG_ITER_LINES(0, lines[i].args[0], l) - { - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - P_AddThwompThinker(lines[l].frontsector, &lines[l], lines[i].args[1] << FRACBITS, lines[i].args[2] << FRACBITS, sound); - } - } - break; - - case 73: // Add laser thinker to FOF - if (udmf) - { - TAG_ITER_LINES(0, lines[i].args[0], l) - { - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - P_AddLaserThinker(lines[l].args[0], lines + l, !!(lines[i].args[1])); - } - } - break; - - case 100: // FOF (solid) - ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL; - - //Appearance settings - if (lines[i].args[2] & TMFA_NOPLANES) - ffloorflags &= ~FF_RENDERPLANES; - if (lines[i].args[2] & TMFA_NOSIDES) - ffloorflags &= ~FF_RENDERSIDES; - if (lines[i].args[2] & TMFA_INSIDES) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_BOTHPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_ALLSIDES; - } - if (lines[i].args[2] & TMFA_ONLYINSIDES) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_INVERTPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_INVERTSIDES; - } - if (lines[i].args[2] & TMFA_NOSHADE) - ffloorflags |= FF_NOSHADE; - if (lines[i].args[2] & TMFA_SPLAT) - ffloorflags |= FF_SPLAT; - - //Tangibility settings - if (lines[i].args[3] & TMFT_INTANGIBLETOP) - ffloorflags |= FF_REVERSEPLATFORM; - if (lines[i].args[3] & TMFT_INTANGIBLEBOTTOM) - ffloorflags |= FF_PLATFORM; - if (lines[i].args[3] & TMFT_DONTBLOCKPLAYER) - ffloorflags &= ~FF_BLOCKPLAYER; - if (lines[i].args[3] & TMFT_DONTBLOCKOTHERS) - ffloorflags &= ~FF_BLOCKOTHERS; - - //Cutting options - if (ffloorflags & FF_RENDERALL) - { - //If translucent or player can enter it, cut inner walls - if ((lines[i].args[1] < 255) || (lines[i].args[3] & TMFT_VISIBLEFROMINSIDE)) - ffloorflags |= FF_CUTEXTRA|FF_EXTRA; - else - ffloorflags |= FF_CUTLEVEL; - } - - P_AddFakeFloorsByLine(i, lines[i].args[1], ffloorflags, secthinkers); - break; - - case 120: // FOF (water) - ffloorflags = FF_EXISTS|FF_RENDERPLANES|FF_SWIMMABLE|FF_BOTHPLANES|FF_CUTEXTRA|FF_EXTRA|FF_CUTSPRITES; - if (!(lines[i].args[2] & TMFW_NOSIDES)) - ffloorflags |= FF_RENDERSIDES|FF_ALLSIDES; - if (lines[i].args[2] & TMFW_DOUBLESHADOW) - ffloorflags |= FF_DOUBLESHADOW; - if (lines[i].args[2] & TMFW_COLORMAPONLY) - ffloorflags |= FF_COLORMAPONLY; - if (!(lines[i].args[2] & TMFW_NORIPPLE)) - ffloorflags |= FF_RIPPLE; - if (lines[i].args[2] & TMFW_GOOWATER) - ffloorflags |= FF_GOOWATER; - if (lines[i].args[2] & TMFW_SPLAT) - ffloorflags |= FF_SPLAT; - P_AddFakeFloorsByLine(i, lines[i].args[1], ffloorflags, secthinkers); - break; - - case 150: // FOF (Air bobbing) - P_AddFakeFloorsByLine(i, 0xff, FF_EXISTS|FF_SOLID|FF_RENDERALL, secthinkers); - P_AddAirbob(lines[i].frontsector, lines[i].args[0], lines[i].args[1] << FRACBITS, !!(lines[i].args[2] & TMFB_REVERSE), !!(lines[i].args[2] & TMFB_SPINDASH), !!(lines[i].args[2] & TMFB_DYNAMIC)); - break; - - case 160: // FOF (Water bobbing) - P_AddFakeFloorsByLine(i, 0xff, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_FLOATBOB, secthinkers); - break; - - case 170: // FOF (Crumbling) - ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CRUMBLE; - - //Tangibility settings - if (lines[i].args[2] & TMFT_INTANGIBLETOP) - ffloorflags |= FF_REVERSEPLATFORM; - if (lines[i].args[2] & TMFT_INTANGIBLEBOTTOM) - ffloorflags |= FF_PLATFORM; - if (lines[i].args[2] & TMFT_DONTBLOCKPLAYER) - ffloorflags &= ~FF_BLOCKPLAYER; - if (lines[i].args[2] & TMFT_DONTBLOCKOTHERS) - ffloorflags &= ~FF_BLOCKOTHERS; - - //Flags - if (lines[i].args[3] & TMFC_NOSHADE) - ffloorflags |= FF_NOSHADE; - if (lines[i].args[3] & TMFC_NORETURN) - ffloorflags |= FF_NORETURN; - if (lines[i].args[3] & TMFC_FLOATBOB) - ffloorflags |= FF_FLOATBOB; - if (lines[i].args[3] & TMFC_SPLAT) - ffloorflags |= FF_SPLAT; - - //If translucent or player can enter it, cut inner walls - if (lines[i].args[1] < 0xff || (lines[i].args[2] & TMFT_VISIBLEFROMINSIDE)) - ffloorflags |= FF_CUTEXTRA|FF_EXTRA; - else - ffloorflags |= FF_CUTLEVEL; - - //If player can enter it, render insides - if (lines[i].args[2] & TMFT_VISIBLEFROMINSIDE) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_BOTHPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_ALLSIDES; - } - - P_AddFakeFloorsByLine(i, lines[i].args[1], ffloorflags, secthinkers); - if (lines[i].args[3] & TMFC_AIRBOB) - P_AddAirbob(lines[i].frontsector, lines[i].args[0], 16*FRACUNIT, false, false, false); - break; - - case 190: // FOF (Rising) - { - fixed_t ceilingtop = P_FindHighestCeilingSurrounding(lines[i].frontsector); - fixed_t ceilingbottom = P_FindLowestCeilingSurrounding(lines[i].frontsector); - - ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL; - - //Appearance settings - if (lines[i].args[2] & TMFA_NOPLANES) - ffloorflags &= ~FF_RENDERPLANES; - if (lines[i].args[2] & TMFA_NOSIDES) - ffloorflags &= ~FF_RENDERSIDES; - if (lines[i].args[2] & TMFA_INSIDES) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_BOTHPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_ALLSIDES; - } - if (lines[i].args[2] & TMFA_ONLYINSIDES) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_INVERTPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_INVERTSIDES; - } - if (lines[i].args[2] & TMFA_NOSHADE) - ffloorflags |= FF_NOSHADE; - if (lines[i].args[2] & TMFA_SPLAT) - ffloorflags |= FF_SPLAT; - - //Tangibility settings - if (lines[i].args[3] & TMFT_INTANGIBLETOP) - ffloorflags |= FF_REVERSEPLATFORM; - if (lines[i].args[3] & TMFT_INTANGIBLEBOTTOM) - ffloorflags |= FF_PLATFORM; - if (lines[i].args[3] & TMFT_DONTBLOCKPLAYER) - ffloorflags &= ~FF_BLOCKPLAYER; - if (lines[i].args[3] & TMFT_DONTBLOCKOTHERS) - ffloorflags &= ~FF_BLOCKOTHERS; - - //Cutting options - if (ffloorflags & FF_RENDERALL) - { - //If translucent or player can enter it, cut inner walls - if ((lines[i].args[1] < 255) || (lines[i].args[3] & TMFT_VISIBLEFROMINSIDE)) - ffloorflags |= FF_CUTEXTRA|FF_EXTRA; - else - ffloorflags |= FF_CUTLEVEL; - } - - P_AddFakeFloorsByLine(i, (ffloorflags & FF_TRANSLUCENT) ? (lines[i].alpha * 0xff) >> FRACBITS : 0xff, ffloorflags, secthinkers); - P_AddRaiseThinker(lines[i].frontsector, lines[i].args[0], lines[i].args[4] << FRACBITS, ceilingtop, ceilingbottom, !!(lines[i].args[5] & TMFR_REVERSE), !!(lines[i].args[5] & TMFR_SPINDASH)); - break; - } - case 200: // Light block - ffloorflags = FF_EXISTS|FF_CUTSPRITES; - if (!lines[i].args[1]) - ffloorflags |= FF_DOUBLESHADOW; - P_AddFakeFloorsByLine(i, 0xff, ffloorflags, secthinkers); - break; - - case 202: // Fog - ffloorflags = FF_EXISTS|FF_RENDERALL|FF_FOG|FF_INVERTPLANES|FF_INVERTSIDES|FF_CUTEXTRA|FF_EXTRA|FF_DOUBLESHADOW|FF_CUTSPRITES; - sec = sides[*lines[i].sidenum].sector - sectors; - // SoM: Because it's fog, check for an extra colormap and set the fog flag... - if (sectors[sec].extra_colormap) - sectors[sec].extra_colormap->flags = CMF_FOG; - P_AddFakeFloorsByLine(i, 0xff, ffloorflags, secthinkers); - break; - - case 220: //Intangible - ffloorflags = FF_EXISTS|FF_RENDERALL|FF_CUTEXTRA|FF_EXTRA|FF_CUTSPRITES; - - //Appearance settings - if (lines[i].args[2] & TMFA_NOPLANES) - ffloorflags &= ~FF_RENDERPLANES; - if (lines[i].args[2] & TMFA_NOSIDES) - ffloorflags &= ~FF_RENDERSIDES; - if (!(lines[i].args[2] & TMFA_INSIDES)) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_BOTHPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_ALLSIDES; - } - if (lines[i].args[2] & TMFA_ONLYINSIDES) - { - if (ffloorflags & FF_RENDERPLANES) - ffloorflags |= FF_INVERTPLANES; - if (ffloorflags & FF_RENDERSIDES) - ffloorflags |= FF_INVERTSIDES; - } - if (lines[i].args[2] & TMFA_NOSHADE) - ffloorflags |= FF_NOSHADE; - if (lines[i].args[2] & TMFA_SPLAT) - ffloorflags |= FF_SPLAT; - - P_AddFakeFloorsByLine(i, (ffloorflags & FF_TRANSLUCENT) ? (lines[i].alpha * 0xff) >> FRACBITS : 0xff, ffloorflags, secthinkers); - break; - - case 223: // FOF (intangible, invisible) - for combining specials in a sector - P_AddFakeFloorsByLine(i, 0xff, FF_EXISTS|FF_NOSHADE, secthinkers); - break; - - case 250: // Mario Block - ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO; - if (lines[i].args[1] & TMFM_BRICK) - ffloorflags |= FF_GOOWATER; - if (lines[i].args[1] & TMFM_INVISIBLE) - ffloorflags &= ~(FF_SOLID|FF_RENDERALL|FF_CUTLEVEL); - - P_AddFakeFloorsByLine(i, 0xff, ffloorflags, secthinkers); - break; - - case 251: // A THWOMP! - { - UINT16 sound = (lines[i].stringargs[0]) ? get_number(lines[i].stringargs[0]) : sfx_thwomp; - P_AddThwompThinker(lines[i].frontsector, &lines[i], lines[i].args[1] << FRACBITS, lines[i].args[2] << FRACBITS, sound); - P_AddFakeFloorsByLine(i, 0xff, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers); - break; - } - - case 254: // Bustable block - { - UINT8 busttype = BT_REGULAR; - ffloorspecialflags_e bustflags = 0; - - ffloorflags = FF_EXISTS|FF_BLOCKOTHERS|FF_RENDERALL|FF_BUSTUP; - - //Bustable type - switch (lines[i].args[2]) - { - case TMFB_TOUCH: - busttype = BT_TOUCH; - break; - case TMFB_SPIN: - busttype = BT_SPINBUST; - break; - case TMFB_REGULAR: - busttype = BT_REGULAR; - break; - case TMFB_STRONG: - busttype = BT_STRONG; - break; - } - - //Flags - if (lines[i].args[3] & TMFB_PUSHABLES) - bustflags |= FS_PUSHABLES; - if (lines[i].args[3] & TMFB_EXECUTOR) - bustflags |= FS_EXECUTOR; - if (lines[i].args[3] & TMFB_ONLYBOTTOM) - bustflags |= FS_ONLYBOTTOM; - if (lines[i].args[3] & TMFB_SPLAT) - ffloorflags |= FF_SPLAT; - - if (busttype != BT_TOUCH || bustflags & FS_ONLYBOTTOM) - ffloorflags |= FF_BLOCKPLAYER; - - TAG_ITER_SECTORS(0, lines[i].args[0], s) - { - ffloor_t *fflr = P_AddFakeFloor(§ors[s], lines[i].frontsector, lines + i, lines[i].args[1], ffloorflags, secthinkers); - fflr->busttype = busttype; - fflr->specialflags = bustflags; - fflr->busttag = lines[i].args[4]; - } - break; - } - case 257: // Quicksand - ffloorflags = FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES; - if (!(lines[i].args[1])) - ffloorflags |= FF_RIPPLE; - - TAG_ITER_SECTORS(0, lines[i].args[0], s) - { - ffloor_t *fflr = P_AddFakeFloor(§ors[s], lines[i].frontsector, lines + i, 0xff, ffloorflags, secthinkers); - fflr->sinkspeed = abs(lines[i].args[2]) << (FRACBITS - 1); - fflr->friction = abs(lines[i].args[3]) << (FRACBITS - 6); - } - break; - - case 258: // Laser block - ffloorflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA|FF_TRANSLUCENT; - P_AddLaserThinker(lines[i].args[0], lines + i, !!(lines[i].args[2] & TMFL_NOBOSSES)); - if (lines[i].args[2] & TMFL_SPLAT) - ffloorflags |= FF_SPLAT; - P_AddFakeFloorsByLine(i, lines[i].args[1], ffloorflags, secthinkers); - break; - - case 259: // Custom FOF - TAG_ITER_SECTORS(0, lines[i].args[0], s) - { - ffloor_t *fflr = P_AddFakeFloor(§ors[s], lines[i].frontsector, lines + i, lines[i].args[1], lines[i].args[2], secthinkers); - if (!udmf) // Ugly backwards compatibility stuff - { - if (lines[i].args[2] & FF_QUICKSAND) - { - fflr->sinkspeed = abs(lines[i].dx) >> 1; - fflr->friction = abs(lines[i].dy) >> 6; - } - if (lines[i].args[2] & FF_BUSTUP) - { - switch (lines[i].args[3] % TMFB_ONLYBOTTOM) - { - case TMFB_TOUCH: - fflr->busttype = BT_TOUCH; - break; - case TMFB_SPIN: - fflr->busttype = BT_SPINBUST; - break; - case TMFB_REGULAR: - fflr->busttype = BT_REGULAR; - break; - case TMFB_STRONG: - fflr->busttype = BT_STRONG; - break; - } - - if (lines[i].args[3] & TMFB_ONLYBOTTOM) - fflr->specialflags |= FS_ONLYBOTTOM; - if (lines[i].flags & ML_EFFECT4) - fflr->specialflags |= FS_PUSHABLES; - if (lines[i].flags & ML_EFFECT5) - { - fflr->specialflags |= FS_EXECUTOR; - fflr->busttag = P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS; - } - } - } - } - break; - - case 260: // GZDoom-like 3D Floor. - { - UINT8 dtype = lines[i].args[1] & 3; - UINT8 dflags1 = lines[i].args[1] - dtype; - UINT8 dflags2 = lines[i].args[2]; - UINT8 dopacity = lines[i].args[3]; - boolean isfog = false; - - if (dtype == 0) - dtype = 1; - - ffloorflags = FF_EXISTS; - - if (dflags2 & 1) ffloorflags |= FF_NOSHADE; // Disable light effects (Means no shadowcast) - if (dflags2 & 2) ffloorflags |= FF_DOUBLESHADOW; // Restrict light inside (Means doubleshadow) - if (dflags2 & 4) isfog = true; // Fog effect (Explicitly render like a fog block) - - if (dflags1 & 4) ffloorflags |= FF_BOTHPLANES|FF_ALLSIDES; // Render-inside - if (dflags1 & 16) ffloorflags |= FF_INVERTSIDES|FF_INVERTPLANES; // Invert visibility rules - - // Fog block - if (isfog) - ffloorflags |= FF_RENDERALL|FF_CUTEXTRA|FF_CUTSPRITES|FF_BOTHPLANES|FF_EXTRA|FF_FOG|FF_INVERTPLANES|FF_ALLSIDES|FF_INVERTSIDES; - else - { - ffloorflags |= FF_RENDERALL; - - // Solid - if (dtype == 1) - ffloorflags |= FF_SOLID|FF_CUTLEVEL; - // Water - else if (dtype == 2) - ffloorflags |= FF_SWIMMABLE|FF_CUTEXTRA|FF_CUTSPRITES|FF_EXTRA|FF_RIPPLE; - // Intangible - else if (dtype == 3) - ffloorflags |= FF_CUTEXTRA|FF_CUTSPRITES|FF_EXTRA; - } - - // Non-opaque - if (dopacity < 255) - { - // Invisible - if (dopacity == 0) - { - // True invisible - if (ffloorflags & FF_NOSHADE) - ffloorflags &= ~(FF_RENDERALL|FF_CUTEXTRA|FF_CUTSPRITES|FF_EXTRA|FF_BOTHPLANES|FF_ALLSIDES|FF_CUTLEVEL); - // Shadow block - else - { - ffloorflags |= FF_CUTSPRITES; - ffloorflags &= ~(FF_RENDERALL|FF_CUTEXTRA|FF_EXTRA|FF_BOTHPLANES|FF_ALLSIDES|FF_CUTLEVEL); - } - } - else - { - ffloorflags |= FF_TRANSLUCENT|FF_CUTEXTRA|FF_EXTRA; - ffloorflags &= ~FF_CUTLEVEL; - } - } - - P_AddFakeFloorsByLine(i, dopacity, ffloorflags, secthinkers); - } - break; - - case 300: // Linedef executor (combines with sector special 974/975) and commands - case 302: - case 303: - case 304: - - // Charability linedef executors - case 305: - case 307: - break; - - case 308: // Race-only linedef executor. Triggers once. - if (!(gametyperules & GTR_RACE)) - lines[i].special = 0; - break; - - // Linedef executor triggers for CTF teams. - case 309: - case 311: - if (!(gametyperules & GTR_TEAMFLAGS)) - lines[i].special = 0; - break; - - // Each time executors - case 306: - case 301: - case 310: - case 312: - case 332: - case 335: - P_AddEachTimeThinker(&lines[i]); - break; - - // No More Enemies Linedef Exec - case 313: - P_AddNoEnemiesThinker(&lines[i]); - break; - - // Pushable linedef executors (count # of pushables) - case 314: - case 315: - break; - - // Unlock trigger executors - case 317: - case 318: - break; - case 319: - case 320: - break; - - // Trigger on X calls - case 321: - case 322: - if (lines[i].flags & ML_NOCLIMB && sides[lines[i].sidenum[0]].rowoffset > 0) // optional "starting" count - lines[i].callcount = sides[lines[i].sidenum[0]].rowoffset>>FRACBITS; - else - lines[i].callcount = sides[lines[i].sidenum[0]].textureoffset>>FRACBITS; - if (lines[i].special == 322) // Each time - P_AddEachTimeThinker(&lines[i]); - break; - - // NiGHTS trigger executors - case 323: - case 324: - case 325: - case 326: - case 327: - case 328: - case 329: - case 330: - break; - - // Skin trigger executors - case 331: - case 333: - break; - - // Object dye executors - case 334: - case 336: - break; - - case 399: // Linedef execute on map load - // This is handled in P_RunLevelLoadExecutors. - break; - - case 400: - case 401: - case 402: - case 403: - case 404: - case 405: - case 406: - case 407: - case 408: - case 409: - case 410: - case 411: - case 412: - case 413: - case 414: - case 415: - case 416: - case 417: - case 418: - case 419: - case 420: - case 421: - case 422: - case 423: - case 424: - case 425: - case 426: - case 427: - case 428: - case 429: - case 430: - case 431: - break; - - case 449: // Enable bosses with parameter - { - INT32 bossid = sides[*lines[i].sidenum].textureoffset>>FRACBITS; - if (bossid & ~15) // if any bits other than first 16 are set - { - CONS_Alert(CONS_WARNING, - M_GetText("Boss enable linedef (tag %d) has an invalid texture x offset.\nConsider changing it or removing it entirely.\n"), - tag); - break; - } - if (!(lines[i].flags & ML_NOCLIMB)) - { - bossdisabled |= (1<>FRACBITS); - break; - - case 603: // Adjustable flickering light - sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(tag, s) - P_SpawnAdjustableFireFlicker(§ors[sec], §ors[s], - P_AproxDistance(lines[i].dx, lines[i].dy)>>FRACBITS); - break; - - case 604: // Adjustable Blinking Light (unsynchronized) - sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(tag, s) - P_SpawnAdjustableStrobeFlash(§ors[sec], §ors[s], - abs(lines[i].dx)>>FRACBITS, abs(lines[i].dy)>>FRACBITS, false); - break; - - case 605: // Adjustable Blinking Light (synchronized) - sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(tag, s) - P_SpawnAdjustableStrobeFlash(§ors[sec], §ors[s], - abs(lines[i].dx)>>FRACBITS, abs(lines[i].dy)>>FRACBITS, true); - break; - - case 606: // HACK! Copy colormaps. Just plain colormaps. - TAG_ITER_SECTORS(lines[i].args[0], s) - { - extracolormap_t *exc; - - if (sectors[s].colormap_protected) - continue; - - if (!udmf) - exc = sides[lines[i].sidenum[0]].colormap_data; - else - { - if (!lines[i].args[1]) - exc = lines[i].frontsector->extra_colormap; - else - { - INT32 sourcesec = Tag_Iterate_Sectors(lines[i].args[1], 0); - if (sourcesec == -1) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 606: Can't find sector with source colormap (tag %d)!\n", lines[i].args[1]); - return; - } - exc = sectors[sourcesec].extra_colormap; - } - } - sectors[s].extra_colormap = sectors[s].spawn_extra_colormap = exc; - } - break; - - default: - break; - } - } - - // And another round, this time with all FOFs already created - for (i = 0; i < numlines; i++) - { - switch (lines[i].special) - { - INT32 s; - INT32 l; - - case 74: // Make FOF bustable - { - UINT8 busttype = BT_REGULAR; - ffloorspecialflags_e bustflags = 0; - - if (!udmf) - break; - - switch (lines[i].args[1]) - { - case TMFB_TOUCH: - busttype = BT_TOUCH; - break; - case TMFB_SPIN: - busttype = BT_SPINBUST; - break; - case TMFB_REGULAR: - busttype = BT_REGULAR; - break; - case TMFB_STRONG: - busttype = BT_STRONG; - break; - } - - if (lines[i].args[2] & TMFB_PUSHABLES) - bustflags |= FS_PUSHABLES; - if (lines[i].args[2] & TMFB_EXECUTOR) - bustflags |= FS_EXECUTOR; - if (lines[i].args[2] & TMFB_ONLYBOTTOM) - bustflags |= FS_ONLYBOTTOM; - - TAG_ITER_LINES(0, lines[i].args[0], l) - { - TAG_ITER_DECLARECOUNTER(1); - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - TAG_ITER_SECTORS(1, lines[l].args[0], s) - { - ffloor_t *rover; - - for (rover = sectors[s].ffloors; rover; rover = rover->next) - { - if (rover->master != lines + l) - continue; - - rover->flags |= FF_BUSTUP; - rover->spawnflags |= FF_BUSTUP; - rover->busttype = busttype; - rover->specialflags &= ~FS_BUSTMASK; - rover->specialflags |= bustflags; - rover->busttag = lines[i].args[3]; - CheckForBustableBlocks = true; - break; - } - } - } - break; - } - - case 75: // Make FOF quicksand - { - if (!udmf) - break; - TAG_ITER_LINES(0, lines[i].args[0], l) - { - TAG_ITER_DECLARECOUNTER(1); - if (lines[l].special < 100 || lines[l].special >= 300) - continue; - - TAG_ITER_SECTORS(1, lines[l].args[0], s) - { - ffloor_t *rover; - - for (rover = sectors[s].ffloors; rover; rover = rover->next) - { - if (rover->master != lines + l) - continue; - - rover->flags |= FF_QUICKSAND; - rover->spawnflags |= FF_QUICKSAND; - rover->sinkspeed = abs(lines[i].args[1]) << (FRACBITS - 1); - rover->friction = abs(lines[i].args[2]) << (FRACBITS - 6); - CheckForQuicksand = true; - break; - } - } - } - break; - } - - case 76: // Make FOF bouncy - { - if (udmf) - { - TAG_ITER_LINES(0, lines[i].args[0], l) - P_MakeFOFBouncy(lines + i, lines + l); - } - else - { - TAG_ITER_SECTORS(0, lines[i].args[0], s) - for (j = 0; (unsigned)j < sectors[s].linecount; j++) - P_MakeFOFBouncy(lines + i, sectors[s].lines[j]); - } - break; - } - } - } - - - - - - // Allocate each list - for (i = 0; i < numsectors; i++) - if(secthinkers[i].thinkers) - Z_Free(secthinkers[i].thinkers); - - Z_Free(secthinkers); - - // haleyjd 02/20/06: spawn polyobjects - Polyobj_InitLevel(); - - for (i = 0; i < numlines; i++) - { - switch (lines[i].special) - { - case 30: // Polyobj_Flag - PolyFlag(&lines[i]); - break; - - case 31: // Polyobj_Displace - PolyDisplace(&lines[i]); - break; - - case 32: // Polyobj_RotDisplace - PolyRotDisplace(&lines[i]); - break; - } - } - - P_RunLevelLoadExecutors(); -} - -/** Adds 3Dfloors as appropriate based on a common control linedef. - * - * \param line Control linedef to use. - * \param alpha Alpha value (0-255). - * \param ffloorflags 3Dfloor flags to use. - * \param secthkiners Lists of thinkers sorted by sector. May be NULL. - * \sa P_SpawnSpecials, P_AddFakeFloor - * \author Graue - */ -static void P_AddFakeFloorsByLine(size_t line, INT32 alpha, ffloortype_e ffloorflags, thinkerlist_t *secthinkers) -{ - INT32 s; - mtag_t tag = lines[line].args[0]; - size_t sec = sides[*lines[line].sidenum].sector-sectors; - -<<<<<<< HEAD - TAG_ITER_SECTORS(0, tag, s) - P_AddFakeFloor(§ors[s], §ors[sec], lines+line, alpha, ffloorflags, secthinkers); -======= - line_t* li = lines + line; - TAG_ITER_SECTORS(tag, s) - P_AddFakeFloor(§ors[s], §ors[sec], li, ffloorflags, secthinkers); ->>>>>>> next -} - -/* - SoM: 3/8/2000: General scrolling functions. - T_Scroll, - Add_Scroller, - Add_WallScroller, - P_SpawnScrollers -*/ - -// helper function for T_Scroll -static void P_DoScrollMove(mobj_t *thing, fixed_t dx, fixed_t dy, INT32 exclusive) -{ - fixed_t fuckaj = 0; // Nov 05 14:12:08 <+MonsterIestyn> I've heard of explicitly defined variables but this is ridiculous - if (thing->player) - { - if (!(dx | dy)) - { - thing->player->cmomx = 0; - thing->player->cmomy = 0; - } - else - { - thing->player->cmomx += dx; - thing->player->cmomy += dy; - thing->player->cmomx = FixedMul(thing->player->cmomx, 0xe800); - thing->player->cmomy = FixedMul(thing->player->cmomy, 0xe800); - } - } - - if (thing->player && (thing->player->pflags & PF_SPINNING) && (thing->player->rmomx || thing->player->rmomy) && !(thing->player->pflags & PF_STARTDASH)) - fuckaj = FixedDiv(549*ORIG_FRICTION,500*FRACUNIT); - else if (thing->friction != ORIG_FRICTION) - fuckaj = thing->friction; - - if (fuckaj) { - // refactor thrust for new friction - dx = FixedDiv(dx, CARRYFACTOR); - dy = FixedDiv(dy, CARRYFACTOR); - - dx = FixedMul(dx, FRACUNIT-fuckaj); - dy = FixedMul(dy, FRACUNIT-fuckaj); - } - - thing->momx += dx; - thing->momy += dy; - - if (exclusive) - thing->eflags |= MFE_PUSHED; -} - -/** Processes an active scroller. - * 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 an - * edge around a sector can be used to control the direction of the sector's - * scrolling, which is usually what is desired. - * - * \param s Thinker for the scroller to process. - * \todo Split up into multiple functions. - * \todo Use attached lists to make ::sc_carry_ceiling case faster and - * cleaner. - * \sa Add_Scroller, Add_WallScroller, P_SpawnScrollers - * \author Steven McGranahan - * \author Graue - */ -void T_Scroll(scroll_t *s) -{ - fixed_t dx = s->dx, dy = s->dy; - boolean is3dblock = false; - - 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); - } - - 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; - msecnode_t *node; - mobj_t *thing; - line_t *line; - size_t i; - INT32 sect; - ffloor_t *rover; - - case sc_side: // scroll wall texture - side = sides + s->affectee; - side->textureoffset += dx; - side->rowoffset += dy; - break; - - case sc_floor: // scroll floor texture - sec = sectors + s->affectee; - sec->floor_xoffs += dx; - sec->floor_yoffs += dy; - break; - - case sc_ceiling: // scroll ceiling texture - sec = sectors + s->affectee; - sec->ceiling_xoffs += dx; - sec->ceiling_yoffs += dy; - break; - - case sc_carry: - sec = sectors + s->affectee; - height = sec->floorheight; - - // sec is the control sector, find the real sector(s) to use - for (i = 0; i < sec->linecount; i++) - { - line = sec->lines[i]; - - if (line->special < 100 || line->special >= 300) - is3dblock = false; - else - is3dblock = true; - - if (!is3dblock) - continue; - - TAG_ITER_SECTORS(Tag_FGet(&line->tags), sect) - { - sector_t *psec; - psec = sectors + sect; - - // Find the FOF corresponding to the control linedef - for (rover = psec->ffloors; rover; rover = rover->next) - { - if (rover->master == sec->lines[i]) - break; - } - - if (!rover) // This should be impossible, but don't complain if it is the case somehow - continue; - - if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there - continue; - - for (node = psec->touching_thinglist; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - - if (thing->eflags & MFE_PUSHED) // Already pushed this tic by an exclusive pusher. - continue; - - height = P_GetSpecialBottomZ(thing, sec, psec); - - if (!(thing->flags & MF_NOCLIP)) // Thing must be clipped - if (!(thing->flags & MF_NOGRAVITY || thing->z+thing->height != height)) // Thing must a) be non-floating and have z+height == height - { - // Move objects only if on floor - // non-floating, and clipped. - P_DoScrollMove(thing, dx, dy, s->exclusive); - } - } // end of for loop through touching_thinglist - } // end of loop through sectors - } - - if (!is3dblock) - { - for (node = sec->touching_thinglist; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - - if (thing->eflags & MFE_PUSHED) - continue; - - height = P_GetSpecialBottomZ(thing, sec, sec); - - if (!(thing->flags & MF_NOCLIP) && - (!(thing->flags & MF_NOGRAVITY || thing->z > height))) - { - // Move objects only if on floor or underwater, - // non-floating, and clipped. - P_DoScrollMove(thing, dx, dy, s->exclusive); - } - } - } - break; - - case sc_carry_ceiling: // carry on ceiling (FOF scrolling) - sec = sectors + s->affectee; - height = sec->ceilingheight; - - // sec is the control sector, find the real sector(s) to use - for (i = 0; i < sec->linecount; i++) - { - line = sec->lines[i]; - if (line->special < 100 || line->special >= 300) - is3dblock = false; - else - is3dblock = true; - - if (!is3dblock) - continue; - TAG_ITER_SECTORS(Tag_FGet(&line->tags), sect) - { - sector_t *psec; - psec = sectors + sect; - - // Find the FOF corresponding to the control linedef - for (rover = psec->ffloors; rover; rover = rover->next) - { - if (rover->master == sec->lines[i]) - break; - } - - if (!rover) // This should be impossible, but don't complain if it is the case somehow - continue; - - if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there - continue; - - for (node = psec->touching_thinglist; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - - if (thing->eflags & MFE_PUSHED) - continue; - - height = P_GetSpecialTopZ(thing, sec, psec); - - if (!(thing->flags & MF_NOCLIP)) // Thing must be clipped - if (!(thing->flags & MF_NOGRAVITY || thing->z != height))// Thing must a) be non-floating and have z == height - { - // Move objects only if on floor or underwater, - // non-floating, and clipped. - P_DoScrollMove(thing, dx, dy, s->exclusive); - } - } // end of for loop through touching_thinglist - } // end of loop through sectors - } - - if (!is3dblock) - { - for (node = sec->touching_thinglist; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - - if (thing->eflags & MFE_PUSHED) - continue; - - height = P_GetSpecialTopZ(thing, sec, sec); - - if (!(thing->flags & MF_NOCLIP) && - (!(thing->flags & MF_NOGRAVITY || thing->z+thing->height < height))) - { - // Move objects only if on floor or underwater, - // non-floating, and clipped. - P_DoScrollMove(thing, dx, dy, s->exclusive); - } - } - } - break; // end of sc_carry_ceiling - } // end of switch -} - -/** Adds a generalized scroller to the thinker list. - * - * \param type The enumerated type of scrolling. - * \param dx x speed of scrolling or its acceleration. - * \param dy y speed of scrolling or its acceleration. - * \param control Sector whose heights control this scroller's effect - * remotely, or -1 if there is no control sector. - * \param affectee Index of the affected object, sector or sidedef. - * \param accel Nonzero for an accelerative effect. - * \sa Add_WallScroller, P_SpawnScrollers, T_Scroll - */ -static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT32 affectee, INT32 accel, INT32 exclusive) -{ - scroll_t *s = Z_Calloc(sizeof *s, PU_LEVSPEC, NULL); - s->thinker.function.acp1 = (actionf_p1)T_Scroll; - s->type = type; - s->dx = dx; - s->dy = dy; - s->accel = accel; - s->exclusive = exclusive; - s->vdx = s->vdy = 0; - if ((s->control = control) != -1) - s->last_height = sectors[control].floorheight + sectors[control].ceilingheight; - s->affectee = affectee; - P_AddThinker(THINK_MAIN, &s->thinker); -} - -/** Initializes the scrollers. - * - * \todo Get rid of all the magic numbers. - * \sa P_SpawnSpecials, Add_Scroller, Add_WallScroller - */ -static void P_SpawnScrollers(void) -{ - size_t i; - line_t *l = lines; - mtag_t tag; - - 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; - INT32 control = -1, accel = 0; // no control sector or acceleration - INT32 special = l->special; - - tag = Tag_FGet(&l->tags); - - // These types are same as the ones they get set to 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 :) - - if (special == 515 || special == 512 || special == 522 || special == 532 || special == 504) // displacement scrollers - { - special -= 2; - control = (INT32)(sides[*l->sidenum].sector - sectors); - } - else if (special == 514 || special == 511 || special == 521 || special == 531 || special == 503) // accelerative scrollers - { - special--; - accel = 1; - control = (INT32)(sides[*l->sidenum].sector - sectors); - } - else if (special == 535 || special == 525) // displacement scrollers - { - special -= 2; - control = (INT32)(sides[*l->sidenum].sector - sectors); - } - else if (special == 534 || special == 524) // accelerative scrollers - { - accel = 1; - special--; - control = (INT32)(sides[*l->sidenum].sector - sectors); - } - - switch (special) - { - register INT32 s; - - case 513: // scroll effect ceiling - case 533: // scroll and carry objects on ceiling - TAG_ITER_SECTORS(tag, s) - Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - if (special != 533) - break; - /* FALLTHRU */ - - case 523: // carry objects on ceiling - dx = FixedMul(dx, CARRYFACTOR); - dy = FixedMul(dy, CARRYFACTOR); - TAG_ITER_SECTORS(tag, s) - Add_Scroller(sc_carry_ceiling, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - break; - - case 510: // scroll effect floor - case 530: // scroll and carry objects on floor - TAG_ITER_SECTORS(tag, s) - Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - if (special != 530) - break; - /* FALLTHRU */ - - case 520: // carry objects on floor - dx = FixedMul(dx, CARRYFACTOR); - dy = FixedMul(dy, CARRYFACTOR); - TAG_ITER_SECTORS(tag, s) - Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - break; - - // scroll wall according to linedef - // (same direction and speed as scrolling floors) - case 502: - { - TAG_ITER_LINES(tag, s) - if (s != (INT32)i) - { - if (l->flags & ML_EFFECT2) // use texture offsets instead - { - dx = sides[l->sidenum[0]].textureoffset; - dy = sides[l->sidenum[0]].rowoffset; - } - if (l->flags & ML_EFFECT3) - { - if (lines[s].sidenum[1] != 0xffff) - Add_Scroller(sc_side, dx, dy, control, lines[s].sidenum[1], accel, 0); - } - else - Add_Scroller(sc_side, dx, dy, control, lines[s].sidenum[0], accel, 0); - } - break; - } - - case 505: - s = lines[i].sidenum[0]; - Add_Scroller(sc_side, -sides[s].textureoffset, sides[s].rowoffset, -1, s, accel, 0); - break; - - case 506: - s = lines[i].sidenum[1]; - - if (s != 0xffff) - Add_Scroller(sc_side, -sides[s].textureoffset, sides[s].rowoffset, -1, lines[i].sidenum[0], accel, 0); - else - CONS_Debug(DBG_GAMELOGIC, "Line special 506 (line #%s) missing back side!\n", sizeu1(i)); - break; - - case 507: - s = lines[i].sidenum[0]; - - if (lines[i].sidenum[1] != 0xffff) - Add_Scroller(sc_side, -sides[s].textureoffset, sides[s].rowoffset, -1, lines[i].sidenum[1], accel, 0); - else - CONS_Debug(DBG_GAMELOGIC, "Line special 507 (line #%s) missing back side!\n", sizeu1(i)); - break; - - case 508: - s = lines[i].sidenum[1]; - - if (s != 0xffff) - Add_Scroller(sc_side, -sides[s].textureoffset, sides[s].rowoffset, -1, s, accel, 0); - else - CONS_Debug(DBG_GAMELOGIC, "Line special 508 (line #%s) missing back side!\n", sizeu1(i)); - break; - - case 500: // scroll first side - Add_Scroller(sc_side, FRACUNIT, 0, -1, lines[i].sidenum[0], accel, 0); - break; - - case 501: // jff 1/30/98 2-way scroll - Add_Scroller(sc_side, -FRACUNIT, 0, -1, lines[i].sidenum[0], accel, 0); - break; - } - } -} - -/** Adds master appear/disappear thinker. - * - * \param appeartime tics to be existent - * \param disappeartime tics to be nonexistent - * \param sector pointer to control sector - */ -static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t offset, INT32 line, INT32 sourceline) -{ - disappear_t *d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL); - - d->thinker.function.acp1 = (actionf_p1)T_Disappear; - d->appeartime = appeartime; - d->disappeartime = disappeartime; - d->offset = offset; - d->affectee = line; - d->sourceline = sourceline; - d->exists = true; - d->timer = 1; - - P_AddThinker(THINK_MAIN, &d->thinker); -} - -/** Makes a FOF appear/disappear - * - * \param d Disappear thinker. - * \sa Add_MasterDisappearer - */ -void T_Disappear(disappear_t *d) -{ - if (d->offset && !d->exists) - { - d->offset--; - return; - } - - if (--d->timer <= 0) - { - ffloor_t *rover; - register INT32 s; - mtag_t afftag = Tag_FGet(&lines[d->affectee].tags); - - TAG_ITER_SECTORS(afftag, s) - { - for (rover = sectors[s].ffloors; rover; rover = rover->next) - { - if (rover->master != &lines[d->affectee]) - continue; - - if (d->exists) - rover->flags &= ~FF_EXISTS; - else - { - rover->flags |= FF_EXISTS; - - if (!(lines[d->sourceline].flags & ML_NOCLIMB)) - { - sectors[s].soundorg.z = P_GetFFloorTopZAt(rover, sectors[s].soundorg.x, sectors[s].soundorg.y); - S_StartSound(§ors[s].soundorg, sfx_appear); - } - } - } - sectors[s].moved = true; - P_RecalcPrecipInSector(§ors[s]); - } - - if (d->exists) - { - d->timer = d->disappeartime; - d->exists = false; - } - else - { - d->timer = d->appeartime; - d->exists = true; - } - } -} - -/** Removes fadingdata from FOF control sector - * - * \param line line to search for target faders - * \param data pointer to set new fadingdata to. Can be NULL to erase. - */ -static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data, boolean finalize) -{ - fade_t *fadingdata = (fade_t *)rover->fadingdata; - // find any existing thinkers and remove them, then replace with new data - if (fadingdata != data) - { - if (fadingdata) - { - if (finalize) - P_FadeFakeFloor(rover, - fadingdata->sourcevalue, - fadingdata->alpha >= fadingdata->destvalue ? - fadingdata->alpha - 1 : // trigger fade-out finish - fadingdata->alpha + 1, // trigger fade-in finish - 0, - fadingdata->ticbased, - &fadingdata->timer, - fadingdata->doexists, - fadingdata->dotranslucent, - fadingdata->dolighting, - fadingdata->docolormap, - fadingdata->docollision, - fadingdata->doghostfade, - fadingdata->exactalpha); - rover->alpha = fadingdata->alpha; - - if (fadingdata->dolighting) - P_RemoveLighting(§ors[rover->secnum]); - - if (fadingdata->docolormap) - P_ResetColormapFader(§ors[rover->secnum]); - - P_RemoveThinker(&fadingdata->thinker); - } - - rover->fadingdata = data; - } -} - -static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destvalue, INT16 speed, boolean ticbased, INT32 *timer, - boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap, - boolean docollision, boolean doghostfade, boolean exactalpha) -{ - boolean stillfading = false; - INT32 alpha; - fade_t *fadingdata = (fade_t *)rover->fadingdata; - (void)docolormap; // *shrug* maybe we can use this in the future. For now, let's be consistent with our other function params - - if (rover->master->special == 258) // Laser block - return false; - - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - if (dotranslucent && - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->flags & FF_FOG) && // do not include fog - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - if (fadingdata) - alpha = fadingdata->alpha; - else - alpha = rover->alpha; - - // routines specific to fade in and fade out - if (!ticbased && alpha == destvalue) - return stillfading; - else if (alpha > destvalue) // fade out - { - // finish fading out - if (speed < 1 || (!ticbased && alpha - speed <= destvalue + speed) || - (ticbased && (--(*timer) <= 0 || alpha <= destvalue))) - { - alpha = destvalue; - - if (docollision) - { - if (rover->spawnflags & FF_SOLID) - rover->flags &= ~FF_SOLID; - if (rover->spawnflags & FF_SWIMMABLE) - rover->flags &= ~FF_SWIMMABLE; - if (rover->spawnflags & FF_QUICKSAND) - rover->flags &= ~FF_QUICKSAND; - if (rover->spawnflags & FF_BUSTUP) - rover->flags &= ~FF_BUSTUP; - if (rover->spawnflags & FF_MARIO) - rover->flags &= ~FF_MARIO; - } - } - else // continue fading out - { - if (!ticbased) - alpha -= speed; - else - { - INT16 delta = abs(destvalue - sourcevalue); - fixed_t factor = min(FixedDiv(speed - (*timer), speed), 1*FRACUNIT); - alpha = max(min(alpha, sourcevalue - (INT16)FixedMul(delta, factor)), destvalue); - } - stillfading = true; - } - } - else // fade in - { - // finish fading in - if (speed < 1 || (!ticbased && alpha + speed >= destvalue - speed) || - (ticbased && (--(*timer) <= 0 || alpha >= destvalue))) - { - alpha = destvalue; - - if (docollision) - { - if (rover->spawnflags & FF_SOLID) - rover->flags |= FF_SOLID; - if (rover->spawnflags & FF_SWIMMABLE) - rover->flags |= FF_SWIMMABLE; - if (rover->spawnflags & FF_QUICKSAND) - rover->flags |= FF_QUICKSAND; - if (rover->spawnflags & FF_BUSTUP) - rover->flags |= FF_BUSTUP; - if (rover->spawnflags & FF_MARIO) - rover->flags |= FF_MARIO; - } - } - else // continue fading in - { - if (!ticbased) - alpha += speed; - else - { - INT16 delta = abs(destvalue - sourcevalue); - fixed_t factor = min(FixedDiv(speed - (*timer), speed), 1*FRACUNIT); - alpha = min(max(alpha, sourcevalue + (INT16)FixedMul(delta, factor)), destvalue); - } - stillfading = true; - } - } - - // routines common to both fade in and fade out - if (!stillfading) - { - if (doexists && !(rover->spawnflags & FF_BUSTUP)) - { - if (alpha <= 1) - rover->flags &= ~FF_EXISTS; - else - rover->flags |= FF_EXISTS; - - // Re-render lighting at end of fade - if (dolighting && !(rover->spawnflags & FF_NOSHADE) && !(rover->flags & FF_EXISTS)) - rover->target->moved = true; - } - - if (dotranslucent && !(rover->flags & FF_FOG)) - { - if (alpha >= 256) - { - if (!(rover->flags & FF_CUTSOLIDS) && - (rover->spawnflags & FF_CUTSOLIDS)) - { - rover->flags |= FF_CUTSOLIDS; - rover->target->moved = true; - } - - rover->flags &= ~FF_TRANSLUCENT; - } - else - { - rover->flags |= FF_TRANSLUCENT; - - if ((rover->flags & FF_CUTSOLIDS) && - (rover->spawnflags & FF_CUTSOLIDS)) - { - rover->flags &= ~FF_CUTSOLIDS; - rover->target->moved = true; - } - } - - if ((rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES)) - { - if (rover->alpha > 1) - rover->flags |= FF_RENDERALL; - else - rover->flags &= ~FF_RENDERALL; - } - } - } - else - { - if (doexists && !(rover->spawnflags & FF_BUSTUP)) - { - // Re-render lighting if we haven't yet set FF_EXISTS (beginning of fade) - if (dolighting && !(rover->spawnflags & FF_NOSHADE) && !(rover->flags & FF_EXISTS)) - rover->target->moved = true; - - rover->flags |= FF_EXISTS; - } - - if (dotranslucent && !(rover->flags & FF_FOG)) - { - rover->flags |= FF_TRANSLUCENT; - - if ((rover->flags & FF_CUTSOLIDS) && - (rover->spawnflags & FF_CUTSOLIDS)) - { - rover->flags &= ~FF_CUTSOLIDS; - rover->target->moved = true; - } - - if ((rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES)) - rover->flags |= FF_RENDERALL; - } - - if (docollision) - { - if (doghostfade) // remove collision flags during fade - { - if (rover->spawnflags & FF_SOLID) - rover->flags &= ~FF_SOLID; - if (rover->spawnflags & FF_SWIMMABLE) - rover->flags &= ~FF_SWIMMABLE; - if (rover->spawnflags & FF_QUICKSAND) - rover->flags &= ~FF_QUICKSAND; - if (rover->spawnflags & FF_BUSTUP) - rover->flags &= ~FF_BUSTUP; - if (rover->spawnflags & FF_MARIO) - rover->flags &= ~FF_MARIO; - } - else // keep collision during fade - { - if (rover->spawnflags & FF_SOLID) - rover->flags |= FF_SOLID; - if (rover->spawnflags & FF_SWIMMABLE) - rover->flags |= FF_SWIMMABLE; - if (rover->spawnflags & FF_QUICKSAND) - rover->flags |= FF_QUICKSAND; - if (rover->spawnflags & FF_BUSTUP) - rover->flags |= FF_BUSTUP; - if (rover->spawnflags & FF_MARIO) - rover->flags |= FF_MARIO; - } - } - } - - if (!(rover->flags & FF_FOG)) // don't set FOG alpha - { - if (!stillfading || exactalpha) - rover->alpha = alpha; - else // clamp fadingdata->alpha to software's alpha levels - { - if (alpha < 12) - rover->alpha = destvalue < 12 ? destvalue : 1; // Don't even draw it - else if (alpha < 38) - rover->alpha = destvalue >= 12 && destvalue < 38 ? destvalue : 25; - else if (alpha < 64) - rover->alpha = destvalue >=38 && destvalue < 64 ? destvalue : 51; - else if (alpha < 89) - rover->alpha = destvalue >= 64 && destvalue < 89 ? destvalue : 76; - else if (alpha < 115) - rover->alpha = destvalue >= 89 && destvalue < 115 ? destvalue : 102; - else if (alpha < 140) - rover->alpha = destvalue >= 115 && destvalue < 140 ? destvalue : 128; - else if (alpha < 166) - rover->alpha = destvalue >= 140 && destvalue < 166 ? destvalue : 154; - else if (alpha < 192) - rover->alpha = destvalue >= 166 && destvalue < 192 ? destvalue : 179; - else if (alpha < 217) - rover->alpha = destvalue >= 192 && destvalue < 217 ? destvalue : 204; - else if (alpha < 243) - rover->alpha = destvalue >= 217 && destvalue < 243 ? destvalue : 230; - else // Opaque - rover->alpha = destvalue >= 243 ? destvalue : 256; - } - } - - if (fadingdata) - fadingdata->alpha = alpha; - - return stillfading; -} - -/** Adds fake floor fader thinker. - * - * \param destvalue transparency value to fade to - * \param speed speed to fade by - * \param ticbased tic-based logic, speed = duration - * \param relative Destvalue is relative to rover->alpha - * \param doexists handle FF_EXISTS - * \param dotranslucent handle FF_TRANSLUCENT - * \param dolighting fade FOF light - * \param docollision handle interactive flags - * \param doghostfade no interactive flags during fading - * \param exactalpha use exact alpha values (opengl) - */ -static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum, - INT16 destvalue, INT16 speed, boolean ticbased, boolean relative, - boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap, - boolean docollision, boolean doghostfade, boolean exactalpha) -{ - fade_t *d; - - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - if (dotranslucent && - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - // already equal, nothing to do - if (rover->alpha == max(1, min(256, relative ? rover->alpha + destvalue : destvalue))) - return; - - d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL); - - d->thinker.function.acp1 = (actionf_p1)T_Fade; - d->rover = rover; - d->sectornum = (UINT32)sectornum; - d->ffloornum = (UINT32)ffloornum; - - d->alpha = d->sourcevalue = rover->alpha; - d->destvalue = max(1, min(256, relative ? rover->alpha + destvalue : destvalue)); // rover->alpha is 1-256 - - if (ticbased) - { - d->ticbased = true; - d->timer = d->speed = abs(speed); // use d->speed as total duration - } - else - { - d->ticbased = false; - d->speed = max(1, speed); // minimum speed 1/tic // if speed < 1, alpha is set immediately in thinker - d->timer = -1; - } - - d->doexists = doexists; - d->dotranslucent = dotranslucent; - d->dolighting = dolighting; - d->docolormap = docolormap; - d->docollision = docollision; - d->doghostfade = doghostfade; - d->exactalpha = exactalpha; - - // find any existing thinkers and remove them, then replace with new data - P_ResetFakeFloorFader(rover, d, false); - - // Set a separate thinker for shadow fading - if (dolighting && !(rover->flags & FF_NOSHADE)) - { - UINT16 lightdelta = abs(sectors[rover->secnum].spawn_lightlevel - rover->target->lightlevel); - fixed_t alphapercent = min(FixedDiv(d->destvalue, rover->spawnalpha), 1*FRACUNIT); // don't make darker than spawn_lightlevel - fixed_t adjustedlightdelta = FixedMul(lightdelta, alphapercent); - - if (rover->target->lightlevel >= sectors[rover->secnum].spawn_lightlevel) // fading out, get lighter - d->destlightlevel = rover->target->lightlevel - adjustedlightdelta; - else // fading in, get darker - d->destlightlevel = rover->target->lightlevel + adjustedlightdelta; - - P_FadeLightBySector(§ors[rover->secnum], - d->destlightlevel, - ticbased ? d->timer : - FixedFloor(FixedDiv(abs(d->destvalue - d->alpha), d->speed))/FRACUNIT, - true); - } - else - d->destlightlevel = -1; - - // Set a separate thinker for colormap fading - if (docolormap && !(rover->flags & FF_NOSHADE) && sectors[rover->secnum].spawn_extra_colormap && !sectors[rover->secnum].colormap_protected) - { - extracolormap_t *dest_exc, - *source_exc = sectors[rover->secnum].extra_colormap ? sectors[rover->secnum].extra_colormap : R_GetDefaultColormap(); - - INT32 colordelta = R_GetRgbaA(sectors[rover->secnum].spawn_extra_colormap->rgba); // alpha is from 0 - fixed_t alphapercent = min(FixedDiv(d->destvalue, rover->spawnalpha), 1*FRACUNIT); // don't make darker than spawn_lightlevel - fixed_t adjustedcolordelta = FixedMul(colordelta, alphapercent); - INT32 coloralpha; - - coloralpha = adjustedcolordelta; - - dest_exc = R_CopyColormap(sectors[rover->secnum].spawn_extra_colormap, false); - dest_exc->rgba = R_GetRgbaRGB(dest_exc->rgba) + R_PutRgbaA(coloralpha); - - if (!(d->dest_exc = R_GetColormapFromList(dest_exc))) - { - dest_exc->colormap = R_CreateLightTable(dest_exc); - R_AddColormapToList(dest_exc); - d->dest_exc = dest_exc; - } - else - Z_Free(dest_exc); - - // If fading from 0, set source_exc rgb same to dest_exc - if (!R_CheckDefaultColormap(d->dest_exc, true, false, false) - && R_CheckDefaultColormap(source_exc, true, false, false)) - { - extracolormap_t *exc = R_CopyColormap(source_exc, false); - exc->rgba = R_GetRgbaRGB(d->dest_exc->rgba) + R_PutRgbaA(R_GetRgbaA(source_exc->rgba)); - exc->fadergba = R_GetRgbaRGB(d->dest_exc->rgba) + R_PutRgbaA(R_GetRgbaA(source_exc->fadergba)); - - if (!(source_exc = R_GetColormapFromList(exc))) - { - exc->colormap = R_CreateLightTable(exc); - R_AddColormapToList(exc); - source_exc = exc; - } - else - Z_Free(exc); - } - - Add_ColormapFader(§ors[rover->secnum], source_exc, d->dest_exc, true, - ticbased ? d->timer : - FixedFloor(FixedDiv(abs(d->destvalue - d->alpha), d->speed))/FRACUNIT); - } - - P_AddThinker(THINK_MAIN, &d->thinker); -} - -/** Makes a FOF fade - * - * \param d Fade thinker. - * \sa P_AddFakeFloorFader - */ -void T_Fade(fade_t *d) -{ - if (d->rover && !P_FadeFakeFloor(d->rover, d->sourcevalue, d->destvalue, d->speed, d->ticbased, &d->timer, - d->doexists, d->dotranslucent, d->dolighting, d->docolormap, d->docollision, d->doghostfade, d->exactalpha)) - { - // Finalize lighting, copypasta from P_AddFakeFloorFader - if (d->dolighting && !(d->rover->flags & FF_NOSHADE) && d->destlightlevel > -1) - sectors[d->rover->secnum].lightlevel = d->destlightlevel; - - // Finalize colormap - if (d->docolormap && !(d->rover->flags & FF_NOSHADE) && sectors[d->rover->secnum].spawn_extra_colormap) - sectors[d->rover->secnum].extra_colormap = d->dest_exc; - - P_RemoveFakeFloorFader(d->rover); - } -} - -static void P_ResetColormapFader(sector_t *sector) -{ - if (sector->fadecolormapdata) - { - // The thinker is the first member in all the action structs, - // so just let the thinker get freed, and that will free the whole - // structure. - P_RemoveThinker(&((elevator_t *)sector->fadecolormapdata)->thinker); - sector->fadecolormapdata = NULL; - } -} - -static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, extracolormap_t *dest_exc, - boolean ticbased, INT32 duration) -{ - fadecolormap_t *d; - - P_ResetColormapFader(sector); - - // nothing to do, set immediately - if (!duration || R_CheckEqualColormaps(source_exc, dest_exc, true, true, true)) - { - sector->extra_colormap = dest_exc; - return; - } - - d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL); - d->thinker.function.acp1 = (actionf_p1)T_FadeColormap; - d->sector = sector; - d->source_exc = source_exc; - d->dest_exc = dest_exc; - - if (ticbased) - { - d->ticbased = true; - d->duration = d->timer = duration; - } - else - { - d->ticbased = false; - d->timer = 256; - d->duration = duration; // use as speed - } - - sector->fadecolormapdata = d; - P_AddThinker(THINK_MAIN, &d->thinker); -} - -void T_FadeColormap(fadecolormap_t *d) -{ - if ((d->ticbased && --d->timer <= 0) - || (!d->ticbased && (d->timer -= d->duration) <= 0)) // d->duration used as speed decrement - { - d->sector->extra_colormap = d->dest_exc; - P_ResetColormapFader(d->sector); - } - else - { - extracolormap_t *exc; - INT32 duration = d->ticbased ? d->duration : 256; - fixed_t factor = min(FixedDiv(duration - d->timer, duration), 1*FRACUNIT); - INT16 cr, cg, cb, ca, fadestart, fadeend, flags; - INT32 rgba, fadergba; - - // NULL failsafes (or intentionally set to signify default) - if (!d->sector->extra_colormap) - d->sector->extra_colormap = R_GetDefaultColormap(); - - if (!d->source_exc) - d->source_exc = R_GetDefaultColormap(); - - if (!d->dest_exc) - d->dest_exc = R_GetDefaultColormap(); - - // For each var (rgba + fadergba + params = 11 vars), we apply - // percentage fading: currentval = sourceval + (delta * percent of duration elapsed) - // delta is negative when fading out (destval is lower) - // max/min are used to ensure progressive calcs don't go backwards and to cap values to dest. - -#define APPLYFADE(dest, src, cur) (\ -(dest-src < 0) ? \ - max(\ - min(cur,\ - src + (INT16)FixedMul(dest-src, factor)),\ - dest)\ -: (dest-src > 0) ? \ - min(\ - max(cur,\ - src + (INT16)FixedMul(dest-src, factor)),\ - dest)\ -: \ - dest\ -) - - cr = APPLYFADE(R_GetRgbaR(d->dest_exc->rgba), R_GetRgbaR(d->source_exc->rgba), R_GetRgbaR(d->sector->extra_colormap->rgba)); - cg = APPLYFADE(R_GetRgbaG(d->dest_exc->rgba), R_GetRgbaG(d->source_exc->rgba), R_GetRgbaG(d->sector->extra_colormap->rgba)); - cb = APPLYFADE(R_GetRgbaB(d->dest_exc->rgba), R_GetRgbaB(d->source_exc->rgba), R_GetRgbaB(d->sector->extra_colormap->rgba)); - ca = APPLYFADE(R_GetRgbaA(d->dest_exc->rgba), R_GetRgbaA(d->source_exc->rgba), R_GetRgbaA(d->sector->extra_colormap->rgba)); - - rgba = R_PutRgbaRGBA(cr, cg, cb, ca); - - cr = APPLYFADE(R_GetRgbaR(d->dest_exc->fadergba), R_GetRgbaR(d->source_exc->fadergba), R_GetRgbaR(d->sector->extra_colormap->fadergba)); - cg = APPLYFADE(R_GetRgbaG(d->dest_exc->fadergba), R_GetRgbaG(d->source_exc->fadergba), R_GetRgbaG(d->sector->extra_colormap->fadergba)); - cb = APPLYFADE(R_GetRgbaB(d->dest_exc->fadergba), R_GetRgbaB(d->source_exc->fadergba), R_GetRgbaB(d->sector->extra_colormap->fadergba)); - ca = APPLYFADE(R_GetRgbaA(d->dest_exc->fadergba), R_GetRgbaA(d->source_exc->fadergba), R_GetRgbaA(d->sector->extra_colormap->fadergba)); - - fadergba = R_PutRgbaRGBA(cr, cg, cb, ca); - - fadestart = APPLYFADE(d->dest_exc->fadestart, d->source_exc->fadestart, d->sector->extra_colormap->fadestart); - fadeend = APPLYFADE(d->dest_exc->fadeend, d->source_exc->fadeend, d->sector->extra_colormap->fadeend); - flags = abs(factor) > FRACUNIT/2 ? d->dest_exc->flags : d->source_exc->flags; // set new flags halfway through fade - -#undef APPLYFADE - - ////////////////// - // setup new colormap - ////////////////// - - if (!(d->sector->extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags))) - { - exc = R_CreateDefaultColormap(false); - exc->fadestart = fadestart; - exc->fadeend = fadeend; - exc->flags = flags; - exc->rgba = rgba; - exc->fadergba = fadergba; - exc->colormap = R_CreateLightTable(exc); - R_AddColormapToList(exc); - d->sector->extra_colormap = exc; - } - } -} - -/* - SoM: 3/8/2000: Friction functions start. - Add_Friction, - T_Friction, - P_SpawnFriction -*/ - -/** Adds friction thinker. - * - * \param friction Friction value, 0xe800 is normal. - * \param affectee Target sector. - * \param roverfriction FOF or not - * \sa T_Friction, P_SpawnFriction - */ -static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32 referrer) -{ - friction_t *f = Z_Calloc(sizeof *f, PU_LEVSPEC, NULL); - - f->thinker.function.acp1 = (actionf_p1)T_Friction; - f->friction = friction; - f->movefactor = movefactor; - f->affectee = affectee; - - if (referrer != -1) - { - f->roverfriction = true; - f->referrer = referrer; - } - else - f->roverfriction = false; - - P_AddThinker(THINK_MAIN, &f->thinker); -} - -/** Applies friction to all things in a sector. - * - * \param f Friction thinker. - * \sa Add_Friction - */ -void T_Friction(friction_t *f) -{ - sector_t *sec, *referrer = NULL; - mobj_t *thing; - msecnode_t *node; - - sec = sectors + f->affectee; - - // Get FOF control sector - if (f->roverfriction) - referrer = sectors + f->referrer; - - // Assign the friction value to players on the floor, non-floating, - // and clipped. Normally the object's friction value is kept at - // ORIG_FRICTION and this thinker changes it for icy or muddy floors. - - // When the object is straddling sectors with the same - // floorheight that have different frictions, use the lowest - // friction value (muddy has precedence over icy). - - node = sec->touching_thinglist; // things touching this sector - while (node) - { - thing = node->m_thing; - // apparently, all I had to do was comment out part of the next line and - // friction works for all mobj's - // (or at least MF_PUSHABLEs, which is all I care about anyway) - if (!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && thing->z == thing->floorz) - { - if (f->roverfriction) - { - if (thing->floorz != P_GetSpecialTopZ(thing, referrer, sec)) - { - node = node->m_thinglist_next; - continue; - } - - if ((thing->friction == ORIG_FRICTION) // normal friction? - || (f->friction < thing->friction)) - { - thing->friction = f->friction; - if (thing->player) - thing->movefactor = f->movefactor; - } - } - else if (P_GetSpecialBottomZ(thing, sec, sec) == thing->floorz && (thing->friction == ORIG_FRICTION // normal friction? - || f->friction < thing->friction)) - { - thing->friction = f->friction; - if (thing->player) - thing->movefactor = f->movefactor; - } - } - node = node->m_thinglist_next; - } -} - -/** Spawns all friction effects. - * - * \sa P_SpawnSpecials, Add_Friction - */ -static void P_SpawnFriction(void) -{ - size_t i; - line_t *l = lines; - mtag_t tag; - register INT32 s; - fixed_t strength; // frontside texture offset controls magnitude - fixed_t friction; // friction value to be applied during movement - INT32 movefactor; // applied to each player move to simulate inertia - - for (i = 0; i < numlines; i++, l++) - if (l->special == 540) - { - tag = Tag_FGet(&l->tags); - strength = sides[l->sidenum[0]].textureoffset>>FRACBITS; - if (strength > 0) // sludge - strength = strength*2; // otherwise, the maximum sludginess value is +967... - - // The following 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'. - friction = ORIG_FRICTION - (0x1EB8*strength)/0x80; // ORIG_FRICTION is 0xE800 - - if (friction > FRACUNIT) - friction = FRACUNIT; - if (friction < 0) - friction = 0; - - movefactor = FixedDiv(ORIG_FRICTION, friction); - if (movefactor < FRACUNIT) - movefactor = 8*movefactor - 7*FRACUNIT; - else - movefactor = FRACUNIT; - - TAG_ITER_SECTORS(tag, s) - Add_Friction(friction, movefactor, s, -1); - } -} - -/* - SoM: 3/8/2000: Push/Pull/Wind/Current functions. - Add_Pusher, - PIT_PushThing, - T_Pusher, - P_GetPushThing, - P_SpawnPushers -*/ - -#define PUSH_FACTOR 7 - -/** Adds a pusher. - * - * \param type Type of push/pull effect. - * \param x_mag X magnitude. - * \param y_mag Y magnitude. - * \param source For a point pusher/puller, the source object. - * \param affectee Target sector. - * \param referrer What sector set it - * \sa T_Pusher, P_GetPushThing, P_SpawnPushers - */ -static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, mobj_t *source, INT32 affectee, INT32 referrer, INT32 exclusive, INT32 slider) -{ - pusher_t *p = Z_Calloc(sizeof *p, PU_LEVSPEC, NULL); - - p->thinker.function.acp1 = (actionf_p1)T_Pusher; - p->source = source; - p->type = type; - p->x_mag = x_mag>>FRACBITS; - p->y_mag = y_mag>>FRACBITS; - p->exclusive = exclusive; - p->slider = slider; - - if (referrer != -1) - { - p->roverpusher = true; - p->referrer = referrer; - } - else - p->roverpusher = false; - - // "The right triangle of the square of the length of the hypotenuse is equal to the sum of the squares of the lengths of the other two sides." - // "Bah! Stupid brains! Don't you know anything besides the Pythagorean Theorem?" - Earthworm Jim - if (type == p_downcurrent || type == p_upcurrent || type == p_upwind || type == p_downwind) - p->magnitude = P_AproxDistance(p->x_mag,p->y_mag)<<(FRACBITS-PUSH_FACTOR); - else - p->magnitude = P_AproxDistance(p->x_mag,p->y_mag); - if (source) // point source exist? - { - // where force goes to zero - if (type == p_push) - p->radius = AngleFixed(source->angle); - else - p->radius = (p->magnitude)<<(FRACBITS+1); - - p->x = p->source->x; - p->y = p->source->y; - p->z = p->source->z; - } - p->affectee = affectee; - P_AddThinker(THINK_MAIN, &p->thinker); -} - - -// PIT_PushThing determines the angle and magnitude of the effect. -// The object's x and y momentum values are changed. -static pusher_t *tmpusher; // pusher structure for blockmap searches - -/** Applies a point pusher/puller to a thing. - * - * \param thing Thing to be pushed. - * \return True if the thing was pushed. - * \todo Make a more robust P_BlockThingsIterator() so the hidden parameter - * ::tmpusher won't need to be used. - * \sa T_Pusher - */ -static inline boolean PIT_PushThing(mobj_t *thing) -{ - if (thing->eflags & MFE_PUSHED) - return false; - - if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG) - return false; - - if (!tmpusher->source) - return false; - - // Allow this to affect pushable objects at some point? - if (thing->player && (!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) || thing->player->powers[pw_carry] == CR_NIGHTSMODE)) - { - INT32 dist; - INT32 speed; - INT32 sx, sy, sz; - - sx = tmpusher->x; - sy = tmpusher->y; - sz = tmpusher->z; - - // don't fade wrt Z if health & 2 (mapthing has multi flag) - if (tmpusher->source->health & 2) - dist = P_AproxDistance(thing->x - sx,thing->y - sy); - else - { - // Make sure the Z is in range - if (thing->z < sz - tmpusher->radius || thing->z > sz + tmpusher->radius) - return false; - - dist = P_AproxDistance(P_AproxDistance(thing->x - sx, thing->y - sy), - thing->z - sz); - } - - speed = (tmpusher->magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS - PUSH_FACTOR - 1); - - // If speed <= 0, you're outside the effective radius. You also have - // to be able to see the push/pull source point. - - // Written with bits and pieces of P_HomingAttack - if ((speed > 0) && (P_CheckSight(thing, tmpusher->source))) - { - if (thing->player->powers[pw_carry] != CR_NIGHTSMODE) - { - // only push wrt Z if health & 1 (mapthing has ambush flag) - if (tmpusher->source->health & 1) - { - fixed_t tmpmomx, tmpmomy, tmpmomz; - - tmpmomx = FixedMul(FixedDiv(sx - thing->x, dist), speed); - tmpmomy = FixedMul(FixedDiv(sy - thing->y, dist), speed); - tmpmomz = FixedMul(FixedDiv(sz - thing->z, dist), speed); - if (tmpusher->source->type == MT_PUSH) // away! - { - tmpmomx *= -1; - tmpmomy *= -1; - tmpmomz *= -1; - } - - thing->momx += tmpmomx; - thing->momy += tmpmomy; - thing->momz += tmpmomz; - - if (thing->player) - { - thing->player->cmomx += tmpmomx; - thing->player->cmomy += tmpmomy; - thing->player->cmomx = FixedMul(thing->player->cmomx, 0xe800); - thing->player->cmomy = FixedMul(thing->player->cmomy, 0xe800); - } - } - else - { - angle_t pushangle; - - pushangle = R_PointToAngle2(thing->x, thing->y, sx, sy); - if (tmpusher->source->type == MT_PUSH) - pushangle += ANGLE_180; // away - pushangle >>= ANGLETOFINESHIFT; - thing->momx += FixedMul(speed, FINECOSINE(pushangle)); - thing->momy += FixedMul(speed, FINESINE(pushangle)); - - if (thing->player) - { - thing->player->cmomx += FixedMul(speed, FINECOSINE(pushangle)); - thing->player->cmomy += FixedMul(speed, FINESINE(pushangle)); - thing->player->cmomx = FixedMul(thing->player->cmomx, 0xe800); - thing->player->cmomy = FixedMul(thing->player->cmomy, 0xe800); - } - } - } - else - { - //NiGHTS-specific handling. - //By default, pushes and pulls only affect the Z-axis. - //By having the ambush flag, it affects the X-axis. - //By having the object special flag, it affects the Y-axis. - fixed_t tmpmomx, tmpmomy, tmpmomz; - - if (tmpusher->source->health & 1) - tmpmomx = FixedMul(FixedDiv(sx - thing->x, dist), speed); - else - tmpmomx = 0; - - if (tmpusher->source->health & 2) - tmpmomy = FixedMul(FixedDiv(sy - thing->y, dist), speed); - else - tmpmomy = 0; - - tmpmomz = FixedMul(FixedDiv(sz - thing->z, dist), speed); - - if (tmpusher->source->type == MT_PUSH) // away! - { - tmpmomx *= -1; - tmpmomy *= -1; - tmpmomz *= -1; - } - - thing->momx += tmpmomx; - thing->momy += tmpmomy; - thing->momz += tmpmomz; - - if (thing->player) - { - thing->player->cmomx += tmpmomx; - thing->player->cmomy += tmpmomy; - thing->player->cmomx = FixedMul(thing->player->cmomx, 0xe800); - thing->player->cmomy = FixedMul(thing->player->cmomy, 0xe800); - } - } - } - } - - if (tmpusher->exclusive) - thing->eflags |= MFE_PUSHED; - - return true; -} - -/** Applies a pusher to all affected objects. - * - * \param p Thinker for the pusher effect. - * \todo Split up into multiple functions. - * \sa Add_Pusher, PIT_PushThing - */ -void T_Pusher(pusher_t *p) -{ - sector_t *sec, *referrer = NULL; - mobj_t *thing; - msecnode_t *node; - INT32 xspeed = 0,yspeed = 0; - INT32 xl, xh, yl, yh, bx, by; - INT32 radius; - //INT32 ht = 0; - boolean inFOF; - boolean touching; - boolean moved; - - xspeed = yspeed = 0; - - 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 (p->roverpusher) - { - referrer = §ors[p->referrer]; - - if (GETSECSPECIAL(referrer->special, 3) != 2) - return; - } - else if (GETSECSPECIAL(sec->special, 3) != 2) - 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. - // - // Apply the effect to clipped players only for now. - // - // In Phase II, you can apply these effects to Things other than players. - - 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 = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; - yh = (unsigned)(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 - node = sec->touching_thinglist; // things touching this sector - for (; node; node = node->m_thinglist_next) - { - thing = node->m_thing; - if (thing->flags & (MF_NOGRAVITY | MF_NOCLIP) - && !(thing->type == MT_SMALLBUBBLE - || thing->type == MT_MEDIUMBUBBLE - || thing->type == MT_EXTRALARGEBUBBLE)) - continue; - - if (!((thing->flags & MF_PUSHABLE) || ((thing->info->flags & MF_PUSHABLE) && thing->fuse)) - && !(thing->type == MT_PLAYER - || thing->type == MT_SMALLBUBBLE - || thing->type == MT_MEDIUMBUBBLE - || thing->type == MT_EXTRALARGEBUBBLE - || thing->type == MT_LITTLETUMBLEWEED - || thing->type == MT_BIGTUMBLEWEED)) - continue; - - if (thing->eflags & MFE_PUSHED) - continue; - - if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG) - continue; - - if (thing->player && (thing->state == &states[thing->info->painstate]) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics)) - continue; - - inFOF = touching = moved = false; - - // Find the area that the 'thing' is in - if (p->roverpusher) - { - fixed_t top, bottom; - - top = P_GetSpecialTopZ(thing, referrer, sec); - bottom = P_GetSpecialBottomZ(thing, referrer, sec); - - if (thing->eflags & MFE_VERTICALFLIP) - { - if (bottom > thing->z + thing->height - || top < (thing->z + (thing->height >> 1))) - continue; - - if (thing->z < bottom) - touching = true; - - if (thing->z + (thing->height >> 1) > bottom) - inFOF = true; - - } - else - { - if (top < thing->z || bottom > (thing->z + (thing->height >> 1))) - continue; - if (thing->z + thing->height > top) - touching = true; - - if (thing->z + (thing->height >> 1) < top) - inFOF = true; - } - } - else // Treat the entire sector as one big FOF - { - if (thing->z == P_GetSpecialBottomZ(thing, sec, sec)) - touching = true; - else if (p->type != p_current) - inFOF = true; - } - - if (!touching && !inFOF) // Object is out of range of effect - continue; - - if (p->type == p_wind) - { - if (touching) // on ground - { - xspeed = (p->x_mag)>>1; // half force - yspeed = (p->y_mag)>>1; - moved = true; - } - else if (inFOF) - { - xspeed = (p->x_mag); // full force - yspeed = (p->y_mag); - moved = true; - } - } - else if (p->type == p_upwind) - { - if (touching) // on ground - { - thing->momz += (p->magnitude)>>1; - moved = true; - } - else if (inFOF) - { - thing->momz += p->magnitude; - moved = true; - } - } - else if (p->type == p_downwind) - { - if (touching) // on ground - { - thing->momz -= (p->magnitude)>>1; - moved = true; - } - else if (inFOF) - { - thing->momz -= p->magnitude; - moved = true; - } - } - else // p_current - { - if (!touching && !inFOF) // Not in water at all - xspeed = yspeed = 0; // no force - else // underwater / touching water - { - if (p->type == p_upcurrent) - thing->momz += p->magnitude; - else if (p->type == p_downcurrent) - thing->momz -= p->magnitude; - else - { - xspeed = p->x_mag; // full force - yspeed = p->y_mag; - } - moved = true; - } - } - - if (p->type != p_downcurrent && p->type != p_upcurrent - && p->type != p_upwind && p->type != p_downwind) - { - thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR); - thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR); - if (thing->player) - { - thing->player->cmomx += xspeed<<(FRACBITS-PUSH_FACTOR); - thing->player->cmomy += yspeed<<(FRACBITS-PUSH_FACTOR); - thing->player->cmomx = FixedMul(thing->player->cmomx, ORIG_FRICTION); - thing->player->cmomy = FixedMul(thing->player->cmomy, ORIG_FRICTION); - } - - // Tumbleweeds bounce a bit... - if (thing->type == MT_LITTLETUMBLEWEED || thing->type == MT_BIGTUMBLEWEED) - thing->momz += P_AproxDistance(xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR)) >> 2; - } - - if (moved) - { - if (p->slider && thing->player) - { - pflags_t jumped = (thing->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)); - P_ResetPlayer (thing->player); - - if (jumped) - thing->player->pflags |= jumped; - - thing->player->pflags |= PF_SLIDING; - thing->angle = R_PointToAngle2 (0, 0, xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR)); - - if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG) - { - angle_t angle = thing->player->angleturn << 16; - if (thing->angle - angle > ANGLE_180) - P_SetPlayerAngle(thing->player, angle - (angle - thing->angle) / 8); - else - P_SetPlayerAngle(thing->player, angle + (thing->angle - angle) / 8); - //P_SetPlayerAngle(thing->player, thing->angle); - } - } - - if (p->exclusive) - thing->eflags |= MFE_PUSHED; - } - } -} - - -/** Gets a push/pull object. - * - * \param s Sector number to look in. - * \return Pointer to the first ::MT_PUSH or ::MT_PULL object found in the - * sector. - * \sa P_GetTeleportDestThing, P_GetStarpostThing, P_GetAltViewThing - */ -mobj_t *P_GetPushThing(UINT32 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; -} - -/** Spawns pushers. - * - * \todo Remove magic numbers. - * \sa P_SpawnSpecials, Add_Pusher - */ -static void P_SpawnPushers(void) -{ - size_t i; - line_t *l = lines; - mtag_t tag; - register INT32 s; - mobj_t *thing; - - for (i = 0; i < numlines; i++, l++) - { - tag = Tag_FGet(&l->tags); - switch (l->special) - { - case 541: // wind - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_wind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - case 544: // current - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_current, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - case 547: // push/pull - TAG_ITER_SECTORS(tag, s) - { - thing = P_GetPushThing(s); - if (thing) // No MT_P* means no effect - Add_Pusher(p_push, l->dx, l->dy, thing, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - } - break; - case 545: // current up - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_upcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - case 546: // current down - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_downcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - case 542: // wind up - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_upwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - case 543: // wind down - TAG_ITER_SECTORS(tag, s) - Add_Pusher(p_downwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - break; - } - } -}