From 4d55c28b60202df08d17202a6f04c95238a46968 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Jan 2019 20:27:34 +0100 Subject: [PATCH] - moved a large batch of code from p_spec.cpp and a few other files into the maploader folder. --- src/CMakeLists.txt | 1 + src/maploader/specials.cpp | 1309 ++++++++++++++++++++++++++++++++++++ src/p_3dfloors.cpp | 152 +---- src/p_3dfloors.h | 2 + src/p_lights.cpp | 186 +---- src/p_pusher.cpp | 141 +--- src/p_scroll.cpp | 265 +------- src/p_spec.cpp | 676 +------------------ src/p_spec_thinkers.h | 253 +++++++ 9 files changed, 1579 insertions(+), 1406 deletions(-) create mode 100644 src/maploader/specials.cpp create mode 100644 src/p_spec_thinkers.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 73d64cf7e..6e71eaa73 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1074,6 +1074,7 @@ set (PCH_SOURCES hwrenderer/utility/hw_shaderpatcher.cpp hwrenderer/utility/hw_vrmodes.cpp maploader/edata.cpp + maploader/specials.cpp maploader/maploader.cpp maploader/slopes.cpp maploader/glnodes.cpp diff --git a/src/maploader/specials.cpp b/src/maploader/specials.cpp new file mode 100644 index 000000000..38ace4b68 --- /dev/null +++ b/src/maploader/specials.cpp @@ -0,0 +1,1309 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1994-1996 Raven Software +// Copyright 1998-1998 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Implements special effects: +// Texture animation, height or lighting changes +// according to adjacent sectors, respective +// utility functions, etc. +// Line Tag handling. Line and Sector triggers. +// Implements donut linedef triggers +// Initializes and implements BOOM linedef triggers for +// Scrollers/Conveyors +// Friction +// Wind/Current +// +//----------------------------------------------------------------------------- + +/* For code that originates from ZDoom the following applies: +** +**--------------------------------------------------------------------------- +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "templates.h" +#include "doomdef.h" +#include "doomstat.h" +#include "d_event.h" +#include "g_level.h" +#include "gstrings.h" +#include "events.h" + +#include "m_random.h" + +#include "p_local.h" +#include "p_spec.h" +#include "p_blockmap.h" +#include "p_lnspec.h" +#include "p_terrain.h" +#include "p_acs.h" +#include "p_3dmidtex.h" + +#include "g_game.h" + +#include "a_sharedglobal.h" +#include "a_keys.h" +#include "c_dispatch.h" +#include "r_sky.h" +#include "d_player.h" +#include "g_levellocals.h" +#include "actorinlines.h" +#include "vm.h" +#include "p_setup.h" + +#include "c_console.h" +#include "p_spec_thinkers.h" +#include "maploader/maploader.h" + +EXTERN_CVAR(Bool, forcewater) + +//----------------------------------------------------------------------------- +// +// Portals +// +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// Lower stacks go in the bottom sector. +// +//----------------------------------------------------------------------------- + +void MapLoader::SetupFloorPortal (AActor *point) +{ + auto it = Level->GetActorIterator(NAME_LowerStackLookOnly, point->tid); + sector_t *Sector = point->Sector; + auto skyv = it.Next(); + if (skyv != nullptr) + { + skyv->target = point; + if (Sector->GetAlpha(sector_t::floor) == 1.) + Sector->SetAlpha(sector_t::floor, clamp(point->args[0], 0, 255) / 255.); + + Sector->Portals[sector_t::floor] = P_GetStackPortal(skyv, sector_t::floor); + } +} + +//----------------------------------------------------------------------------- +// +// Upper stacks go in the top sector. +// +//----------------------------------------------------------------------------- + +void MapLoader::SetupCeilingPortal (AActor *point) +{ + auto it = Level->GetActorIterator(NAME_UpperStackLookOnly, point->tid); + sector_t *Sector = point->Sector; + auto skyv = it.Next(); + if (skyv != nullptr) + { + skyv->target = point; + if (Sector->GetAlpha(sector_t::ceiling) == 1.) + Sector->SetAlpha(sector_t::ceiling, clamp(point->args[0], 0, 255) / 255.); + + Sector->Portals[sector_t::ceiling] = P_GetStackPortal(skyv, sector_t::ceiling); + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void MapLoader::SetupPortals() +{ + auto it = Level->GetThinkerIterator(NAME_StackPoint); + AActor *pt; + TArray points; + + while ((pt = it.Next())) + { + FName nm = pt->GetClass()->TypeName; + if (nm == NAME_UpperStackLookOnly) + { + SetupFloorPortal(pt); + } + else if (nm == NAME_LowerStackLookOnly) + { + SetupCeilingPortal(pt); + } + pt->special1 = 0; + points.Push(pt); + } + // the semantics here are incredibly lax so the final setup can only be done once all portals have been created, + // because later stackpoints will happily overwrite info in older ones, if there are multiple links. + for (auto &s : Level->sectorPortals) + { + if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox) + { + for (auto &ss : Level->sectorPortals) + { + if (ss.mType == PORTS_STACKEDSECTORTHING && ss.mSkybox == s.mSkybox->target) + { + s.mPartner = unsigned((&ss) - &Level->sectorPortals[0]); + } + } + } + } + // Now we can finally set the displacement and delete the stackpoint reference. + for (auto &s : Level->sectorPortals) + { + if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox) + { + s.mDisplacement = s.mSkybox->Pos() - s.mSkybox->target->Pos(); + s.mSkybox = nullptr; + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void MapLoader::SetPortal(sector_t *sector, int plane, unsigned pnum, double alpha) +{ + // plane: 0=floor, 1=ceiling, 2=both + if (plane > 0) + { + if (sector->GetPortalType(sector_t::ceiling) == PORTS_SKYVIEWPOINT) + { + sector->Portals[sector_t::ceiling] = pnum; + if (sector->GetAlpha(sector_t::ceiling) == 1.) + sector->SetAlpha(sector_t::ceiling, alpha); + + if (Level->sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY) + sector->SetTexture(sector_t::ceiling, skyflatnum); + } + } + if (plane == 2 || plane == 0) + { + if (sector->GetPortalType(sector_t::floor) == PORTS_SKYVIEWPOINT) + { + sector->Portals[sector_t::floor] = pnum; + } + if (sector->GetAlpha(sector_t::floor) == 1.) + sector->SetAlpha(sector_t::floor, alpha); + + if (Level->sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY) + sector->SetTexture(sector_t::floor, skyflatnum); + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void MapLoader::CopyPortal(int sectortag, int plane, unsigned pnum, double alpha, bool tolines) +{ + int s; + auto itr = Level->GetSectorTagIterator(sectortag); + while ((s = itr.Next()) >= 0) + { + SetPortal(&Level->sectors[s], plane, pnum, alpha); + } + + for (auto &line : Level->lines) + { + // Check if this portal needs to be copied to other sectors + // This must be done here to ensure that it gets done only after the portal is set up + if (line.special == Sector_SetPortal && + line.args[1] == 1 && + (line.args[2] == plane || line.args[2] == 3) && + line.args[3] == sectortag) + { + if (line.args[0] == 0) + { + SetPortal(line.frontsector, plane, pnum, alpha); + } + else + { + auto itr = Level->GetSectorTagIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + { + SetPortal(&Level->sectors[s], plane, pnum, alpha); + } + } + } + if (tolines && line.special == Sector_SetPortal && + line.args[1] == 5 && + line.args[3] == sectortag) + { + if (line.args[0] == 0) + { + line.portaltransferred = pnum; + } + else + { + auto itr = Level->GetLineIdIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + { + Level->lines[s].portaltransferred = pnum; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void MapLoader::SpawnPortal(line_t *line, int sectortag, int plane, int bytealpha, int linked) +{ + if (plane < 0 || plane > 2 || (linked && plane == 2)) return; + for (auto &oline : Level->lines) + { + // We must look for the reference line with a linear search unless we want to waste the line ID for it + // which is not a good idea. + if (oline.special == Sector_SetPortal && + oline.args[0] == sectortag && + oline.args[1] == linked && + oline.args[2] == plane && + oline.args[3] == 1) + { + // beware of overflows. + DVector2 pos1 = line->v1->fPos() + line->Delta() / 2; + DVector2 pos2 = oline.v1->fPos() + oline.Delta() / 2; + unsigned pnum = P_GetPortal(linked ? PORTS_LINKEDPORTAL : PORTS_PORTAL, plane, line->frontsector, oline.frontsector, pos2 - pos1); + CopyPortal(sectortag, plane, pnum, bytealpha / 255., false); + return; + } + } +} + +//----------------------------------------------------------------------------- +// +// This searches the viewpoint's sector +// for a skybox line special, gets its tag and transfers the skybox to all tagged sectors. +// +//----------------------------------------------------------------------------- + +void MapLoader::SpawnSkybox(AActor *origin) +{ + sector_t *Sector = origin->Sector; + if (Sector == NULL) + { + Printf("Sector not initialized for SkyCamCompat\n"); + origin->Sector = Sector = P_PointInSector(origin->Pos()); + } + if (Sector) + { + for(auto refline : Sector->Lines) + { + if (refline->special == Sector_SetPortal && refline->args[1] == 2) + { + // We found the setup linedef for this skybox, so let's use it for our init. + unsigned pnum = P_GetSkyboxPortal(origin); + CopyPortal(refline->args[0], refline->args[2], pnum, 0, true); + return; + } + } + } +} + +//----------------------------------------------------------------------------- +// +// P_SetSectorDamage +// +// Sets damage properties for one sector. Allows combination of original specials with explicit use of the damage properties +// +//----------------------------------------------------------------------------- + +static void SetupSectorDamage(sector_t *sector, int damage, int interval, int leakchance, FName type, int flags) +{ + // Only set if damage is not yet initialized. This ensures that UDMF takes precedence over sector specials. + if (sector->damageamount == 0) + { + sector->damageamount = damage; + sector->damageinterval = MAX(1, interval); + sector->leakydamage = leakchance; + sector->damagetype = type; + sector->Flags = (sector->Flags & ~SECF_DAMAGEFLAGS) | (flags & SECF_DAMAGEFLAGS); + } +} + +//----------------------------------------------------------------------------- +// +// P_InitSectorSpecial +// +// Sets up everything derived from 'sector->special' for one sector +// ('fromload' is necessary to allow conversion upon savegame load.) +// +//----------------------------------------------------------------------------- + +void MapLoader::InitSectorSpecial(sector_t *sector, int special) +{ + // [RH] All secret sectors are marked with a BOOM-ish bitfield + if (sector->special & SECRET_MASK) + { + sector->Flags |= SECF_SECRET | SECF_WASSECRET; + Level->total_secrets++; + } + if (sector->special & FRICTION_MASK) + { + sector->Flags |= SECF_FRICTION; + } + if (sector->special & PUSH_MASK) + { + sector->Flags |= SECF_PUSH; + } + if ((sector->special & DAMAGE_MASK) == 0x100) + { + SetupSectorDamage(sector, 5, 32, 0, NAME_Fire, 0); + } + else if ((sector->special & DAMAGE_MASK) == 0x200) + { + SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); + } + else if ((sector->special & DAMAGE_MASK) == 0x300) + { + SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + } + sector->special &= 0xff; + + // [RH] Normal DOOM special or BOOM specialized? + bool keepspecial = false; + SpawnLights(sector); + switch (sector->special) + { + case dLight_Strobe_Hurt: + SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + break; + + case dDamage_Hellslime: + SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); + break; + + case dDamage_Nukage: + SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); + break; + + case dSector_DoorCloseIn30: + Create(sector, DDoor::doorWaitClose, 2, 0, 0, 30 * TICRATE); + break; + + case dDamage_End: + SetupSectorDamage(sector, 20, 32, 256, NAME_None, SECF_ENDGODMODE|SECF_ENDLEVEL); + break; + + case dSector_DoorRaiseIn5Mins: + Create (sector, DDoor::doorWaitRaise, 2, TICRATE*30/7, 0, 5*60*TICRATE); + break; + + case dFriction_Low: + sector->friction = FRICTION_LOW; + sector->movefactor = 0x269/65536.; + sector->Flags |= SECF_FRICTION; + break; + + case dDamage_SuperHellslime: + SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + break; + + case dDamage_LavaWimpy: + SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); + break; + + case dDamage_LavaHefty: + SetupSectorDamage(sector, 8, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); + break; + + case dScroll_EastLavaDamage: + SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); + CreateScroller(EScroll::sc_floor, -4., 0, sector, 0); + keepspecial = true; + break; + + case hDamage_Sludge: + SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, 0); + break; + + case sLight_Strobe_Hurt: + SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); + break; + + case sDamage_Hellslime: + SetupSectorDamage(sector, 2, 32, 0, NAME_Slime, SECF_HAZARD); + break; + + case Damage_InstantDeath: + // Strife's instant death sector + SetupSectorDamage(sector, TELEFRAG_DAMAGE, 1, 256, NAME_InstantDeath, 0); + break; + + case sDamage_SuperHellslime: + SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, SECF_HAZARD); + break; + + case Sector_Hidden: + sector->MoreFlags |= SECMF_HIDDEN; + break; + + case Sector_Heal: + // CoD's healing sector + SetupSectorDamage(sector, -1, 32, 0, NAME_None, 0); + break; + + case Sky2: + sector->sky = PL_SKYFLAT; + break; + + default: + if (sector->special >= Scroll_North_Slow && sector->special <= Scroll_SouthWest_Fast) + { // Hexen scroll special + static const int8_t hexenScrollies[24][2] = + { + { 0, 1 }, { 0, 2 }, { 0, 4 }, + { -1, 0 }, { -2, 0 }, { -4, 0 }, + { 0, -1 }, { 0, -2 }, { 0, -4 }, + { 1, 0 }, { 2, 0 }, { 4, 0 }, + { 1, 1 }, { 2, 2 }, { 4, 4 }, + { -1, 1 }, { -2, 2 }, { -4, 4 }, + { -1, -1 }, { -2, -2 }, { -4, -4 }, + { 1, -1 }, { 2, -2 }, { 4, -4 } + }; + + + int i = sector->special - Scroll_North_Slow; + double dx = hexenScrollies[i][0] / 2.; + double dy = hexenScrollies[i][1] / 2.; + CreateScroller(EScroll::sc_floor, dx, dy, sector, 0); + } + else if (sector->special >= Carry_East5 && sector->special <= Carry_East35) + { + // Heretic scroll special + // Only east scrollers also scroll the texture + CreateScroller(EScroll::sc_floor, -0.5 * (1 << ((sector->special & 0xff) - Carry_East5)), 0, sector, 0); + } + keepspecial = true; + break; + } + if (!keepspecial) sector->special = 0; +} + +//----------------------------------------------------------------------------- +// +// P_SpawnSpecials +// +// After the map has been loaded, scan for specials that spawn thinkers +// +//----------------------------------------------------------------------------- + +void MapLoader::SpawnSpecials () +{ + SetupPortals(); + + for (auto &sec : Level->sectors) + { + if (sec.special == 0) + continue; + + InitSectorSpecial(&sec, sec.special); + } + + ProcessEDSectors(); + + // Init other misc stuff + + SpawnScrollers(); // killough 3/7/98: Add generalized scrollers + SpawnFriction(); // phares 3/12/98: New friction model using linedefs + SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + + auto it2 = Level->GetThinkerIterator(NAME_SkyCamCompat); + AActor *pt2; + while ((pt2 = it2.Next())) + { + SpawnSkybox(pt2); + } + + for (auto &line : Level->lines) + { + switch (line.special) + { + int s; + sector_t *sec; + + // killough 3/7/98: + // support for drawn heights coming from different sector + case Transfer_Heights: + { + sec = line.frontsector; + + if (line.args[1] & 2) + { + sec->MoreFlags |= SECMF_FAKEFLOORONLY; + } + if (line.args[1] & 4) + { + sec->MoreFlags |= SECMF_CLIPFAKEPLANES; + } + if (line.args[1] & 8) + { + sec->MoreFlags |= SECMF_UNDERWATER; + } + else if (forcewater) + { + sec->MoreFlags |= SECMF_FORCEDUNDERWATER; + } + if (line.args[1] & 16) + { + sec->MoreFlags |= SECMF_IGNOREHEIGHTSEC; + } + else Level->HasHeightSecs = true; + if (line.args[1] & 32) + { + sec->MoreFlags |= SECMF_NOFAKELIGHT; + } + auto itr = Level->GetSectorTagIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + { + Level->sectors[s].heightsec = sec; + sec->e->FakeFloor.Sectors.Push(&Level->sectors[s]); + Level->sectors[s].MoreFlags |= (sec->MoreFlags & SECMF_IGNOREHEIGHTSEC); // copy this to the destination sector for easier checking. + Level->sectors[s].AdjustFloorClip(); + } + break; + } + + // killough 3/16/98: Add support for setting + // floor lighting independently (e.g. lava) + case Transfer_FloorLight: + Create (line.frontsector, line.args[0], true); + break; + + // killough 4/11/98: Add support for setting + // ceiling lighting independently + case Transfer_CeilingLight: + Create (line.frontsector, line.args[0], false); + break; + + // [Graf Zahl] Add support for setting lighting + // per wall independently + case Transfer_WallLight: + Create (line.frontsector, line.args[0], line.args[1]); + break; + + case Sector_Attach3dMidtex: + P_Attach3dMidtexLinesToSector(line.frontsector, line.args[0], line.args[1], !!line.args[2]); + break; + + case Sector_SetLink: + if (line.args[0] == 0) + { + P_AddSectorLinks(line.frontsector, line.args[1], line.args[2], line.args[3]); + } + break; + + case Sector_SetPortal: + // arg 0 = sector tag + // arg 1 = type + // - 0: normal (handled here) + // - 1: copy (handled by the portal they copy) + // - 2: EE-style skybox (handled by the camera object) + // - 3: EE-style flat portal (GZDoom HW renderer only for now) + // - 4: EE-style horizon portal (GZDoom HW renderer only for now) + // - 5: copy portal to line (GZDoom HW renderer only for now) + // - 6: linked portal + // other values reserved for later use + // arg 2 = 0:floor, 1:ceiling, 2:both + // arg 3 = 0: anchor, 1: reference line + // arg 4 = for the anchor only: alpha + if ((line.args[1] == 0 || line.args[1] == 6) && line.args[3] == 0) + { + SpawnPortal(&line, line.args[0], line.args[2], line.args[4], line.args[1]); + } + else if (line.args[1] == 3 || line.args[1] == 4) + { + unsigned pnum = P_GetPortal(line.args[1] == 3 ? PORTS_PLANE : PORTS_HORIZON, line.args[2], line.frontsector, NULL, { 0,0 }); + CopyPortal(line.args[0], line.args[2], pnum, 0, true); + } + break; + + case Line_SetPortal: + P_SpawnLinePortal(&line); + break; + + // [RH] ZDoom Static_Init settings + case Static_Init: + switch (line.args[1]) + { + case Init_Gravity: + { + double grav = line.Delta().Length() / 100.; + auto itr = Level->GetSectorTagIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + Level->sectors[s].gravity = grav; + } + break; + + //case Init_Color: + // handled in P_LoadSideDefs2() + + case Init_Damage: + { + int damage = int(line.Delta().Length()); + auto itr = Level->GetSectorTagIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + { + sector_t *sec = &Level->sectors[s]; + sec->damageamount = damage; + sec->damagetype = NAME_None; + if (sec->damageamount < 20) + { + sec->leakydamage = 0; + sec->damageinterval = 32; + } + else if (sec->damageamount < 50) + { + sec->leakydamage = 5; + sec->damageinterval = 32; + } + else + { + sec->leakydamage = 256; + sec->damageinterval = 1; + } + } + } + break; + + case Init_SectorLink: + if (line.args[3] == 0) + P_AddSectorLinksByID(line.frontsector, line.args[0], line.args[2]); + break; + + // killough 10/98: + // + // Support for sky textures being transferred from sidedefs. + // Allows scrolling and other effects (but if scrolling is + // used, then the same sector tag needs to be used for the + // sky sector, the sky-transfer linedef, and the scroll-effect + // linedef). Still requires user to use F_SKY1 for the floor + // or ceiling texture, to distinguish floor and ceiling sky. + + case Init_TransferSky: + { + auto itr = Level->GetSectorTagIterator(line.args[0]); + while ((s = itr.Next()) >= 0) + Level->sectors[s].sky = (line.Index() + 1) | PL_SKYFLAT; + break; + } + } + break; + } + } + // [RH] Start running any open scripts on this map + Level->Behaviors.StartTypedScripts (SCRIPT_Open, NULL, false); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void MapLoader::SpawnFriction() +{ + line_t *l = &Level->lines[0]; + + for (unsigned i = 0 ; i < Level->lines.Size() ; i++,l++) + { + if (l->special == Sector_SetFriction) + { + int length; + + if (l->args[1]) + { // [RH] Allow setting friction amount from parameter + length = l->args[1] <= 200 ? l->args[1] : 200; + } + else + { + length = int(l->Delta().Length()); + } + + P_SetSectorFriction (Level, l->args[0], length, false); + l->special = 0; + } + } +} + +//========================================================================== +// +// Creates all 3D floors defined by one linedef +// +//========================================================================== + +int MapLoader::Set3DFloor(line_t * line, int param, int param2, int alpha) +{ + int s; + int flags; + int tag = line->args[0]; + sector_t * sec = line->frontsector, *ss; + + auto itr = Level->GetSectorTagIterator(tag); + while ((s = itr.Next()) >= 0) + { + ss = &Level->sectors[s]; + + if (param == 0) + { + flags = FF_EXISTS | FF_RENDERALL | FF_SOLID | FF_INVERTSECTOR; + alpha = 255; + for (auto l: sec->Lines) + { + if (l->special == Sector_SetContents && l->frontsector == sec) + { + alpha = clamp(l->args[1], 0, 100); + if (l->args[2] & 1) flags &= ~FF_SOLID; + if (l->args[2] & 2) flags |= FF_SEETHROUGH; + if (l->args[2] & 4) flags |= FF_SHOOTTHROUGH; + if (l->args[2] & 8) flags |= FF_ADDITIVETRANS; + if (alpha != 100) flags |= FF_TRANSLUCENT;//|FF_BOTHPLANES|FF_ALLSIDES; + if (l->args[0]) + { + // Yes, Vavoom's 3D-floor definitions suck! + // The content list changed in r1783 of Vavoom to be unified + // among all its supported games, so it has now ten different + // values instead of just five. + static uint32_t vavoomcolors[] = { VC_EMPTY, + VC_WATER, VC_LAVA, VC_NUKAGE, VC_SLIME, VC_HELLSLIME, + VC_BLOOD, VC_SLUDGE, VC_HAZARD, VC_BOOMWATER }; + flags |= FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_FLOOD; + + l->frontsector->Colormap.FadeColor = vavoomcolors[l->args[0]] & VC_COLORMASK; + l->frontsector->Colormap.FogDensity = 0; + } + alpha = (alpha * 255) / 100; + break; + } + } + } + else if (param == 4) + { + flags = FF_EXISTS | FF_RENDERPLANES | FF_INVERTPLANES | FF_NOSHADE | FF_FIX; + if (param2 & 1) flags |= FF_SEETHROUGH; // marker for allowing missing texture checks + alpha = 255; + } + else + { + static const int defflags[] = { 0, + FF_SOLID, + FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_SHOOTTHROUGH | FF_SEETHROUGH, + FF_SHOOTTHROUGH | FF_SEETHROUGH, + }; + + flags = defflags[param & 3] | FF_EXISTS | FF_RENDERALL; + + if (param & 4) flags |= FF_ALLSIDES | FF_BOTHPLANES; + if (param & 16) flags ^= FF_SEETHROUGH; + if (param & 32) flags ^= FF_SHOOTTHROUGH; + + if (param2 & 1) flags |= FF_NOSHADE; + if (param2 & 2) flags |= FF_DOUBLESHADOW; + if (param2 & 4) flags |= FF_FOG; + if (param2 & 8) flags |= FF_THINFLOOR; + if (param2 & 16) flags |= FF_UPPERTEXTURE; + if (param2 & 32) flags |= FF_LOWERTEXTURE; + if (param2 & 64) flags |= FF_ADDITIVETRANS | FF_TRANSLUCENT; + // if flooding is used the floor must be non-solid and is automatically made shootthrough and seethrough + if ((param2 & 128) && !(flags & FF_SOLID)) flags |= FF_FLOOD | FF_SEETHROUGH | FF_SHOOTTHROUGH; + if (param2 & 512) flags |= FF_FADEWALLS; + if (param2&1024) flags |= FF_RESET; + FTextureID tex = line->sidedef[0]->GetTexture(side_t::top); + if (!tex.Exists() && alpha < 255) + { + alpha = -tex.GetIndex(); + } + alpha = clamp(alpha, 0, 255); + if (alpha == 0) flags &= ~(FF_RENDERALL | FF_BOTHPLANES | FF_ALLSIDES); + else if (alpha != 255) flags |= FF_TRANSLUCENT; + + } + P_Add3DFloor(ss, sec, line, flags, alpha); + } + // To be 100% safe this should be done even if the alpha by texture value isn't used. + if (!line->sidedef[0]->GetTexture(side_t::top).isValid()) + line->sidedef[0]->SetTexture(side_t::top, FNullTextureID()); + return 1; +} + +//========================================================================== +// +// Spawns 3D floors +// +//========================================================================== + +void MapLoader::Spawn3DFloors () +{ + static int flagvals[] = {512, 2+512, 512+1024}; + + for (auto &line : Level->lines) + { + switch(line.special) + { + case ExtraFloor_LightOnly: + if (line.args[1] < 0 || line.args[1] > 2) line.args[1] = 0; + if (line.args[0] != 0) + Set3DFloor(&line, 3, flagvals[line.args[1]], 0); + break; + + case Sector_Set3DFloor: + // The flag high-byte/line id is only needed in Hexen format. + // UDMF can set both of these parameters without any restriction of the usable values. + // In Doom format the translators can take full integers for the tag and the line ID always is the same as the tag. + if (Level->maptype == MAPTYPE_HEXEN) + { + if (line.args[1]&8) + { + Level->tagManager.AddLineID(line.Index(), line.args[4]); + } + else + { + line.args[0]+=256*line.args[4]; + line.args[4]=0; + } + } + Set3DFloor(&line, line.args[1]&~8, line.args[2], line.args[3]); + break; + + default: + continue; + } + line.special=0; + line.args[0] = line.args[1] = line.args[2] = line.args[3] = line.args[4] = 0; + } + + for (auto &sec : Level->sectors) + { + P_Recalculate3DFloors(&sec); + } +} + +//========================================================================== +// +// Spawns light effects +// +//========================================================================== + +void MapLoader::SpawnLights(sector_t *sector) +{ + const int STROBEBRIGHT = 5; + const int FASTDARK = 15; + const int SLOWDARK = TICRATE; + + switch (sector->special) + { + case Light_Phased: + Create(sector, 48, 63 - (sector->lightlevel & 63)); + break; + + // [RH] Hexen-like phased lighting + case LightSequenceStart: + Create(sector); + break; + + case dLight_Flicker: + Create(sector); + break; + + case dLight_StrobeFast: + Create(sector, STROBEBRIGHT, FASTDARK, false); + break; + + case dLight_StrobeSlow: + Create(sector, STROBEBRIGHT, SLOWDARK, false); + break; + + case dLight_Strobe_Hurt: + Create(sector, STROBEBRIGHT, FASTDARK, false); + break; + + case dLight_Glow: + Create(sector); + break; + + case dLight_StrobeSlowSync: + Create(sector, STROBEBRIGHT, SLOWDARK, true); + break; + + case dLight_StrobeFastSync: + Create(sector, STROBEBRIGHT, FASTDARK, true); + break; + + case dLight_FireFlicker: + Create(sector); + break; + + case dScroll_EastLavaDamage: + Create(sector, STROBEBRIGHT, FASTDARK, false); + break; + + case sLight_Strobe_Hurt: + Create(sector, STROBEBRIGHT, FASTDARK, false); + break; + + default: + break; + } +} + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +AActor *MapLoader::GetPushThing(int s) +{ + AActor* thing; + sector_t* sec; + + sec = &Level->sectors[s]; + thing = sec->thinglist; + + while (thing && + thing->GetClass()->TypeName != NAME_PointPusher && + thing->GetClass()->TypeName != NAME_PointPuller) + { + thing = thing->snext; + } + return thing; +} + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +void MapLoader::SpawnPushers() +{ + line_t *l = &Level->lines[0]; + int s; + + for (unsigned i = 0; i < Level->lines.Size(); i++, l++) + { + switch (l->special) + { + case Sector_SetWind: // wind + { + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + Create(DPusher::p_wind, l->args[3] ? l : nullptr, l->args[1], l->args[2], nullptr, s); + l->special = 0; + break; + } + + case Sector_SetCurrent: // current + { + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + Create(DPusher::p_current, l->args[3] ? l : nullptr, l->args[1], l->args[2], nullptr, s); + l->special = 0; + break; + } + + case PointPush_SetForce: // push/pull + if (l->args[0]) { // [RH] Find thing by sector + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + { + AActor *thing = GetPushThing(s); + if (thing) { // No MT_P* means no effect + // [RH] Allow narrowing it down by tid + if (!l->args[1] || l->args[1] == thing->tid) + Create(DPusher::p_push, l->args[3] ? l : NULL, l->args[2], + 0, thing, s); + } + } + } + else { // [RH] Find thing by tid + AActor *thing; + auto iterator = Level->GetActorIterator(l->args[1]); + + while ((thing = iterator.Next())) + { + if (thing->GetClass()->TypeName == NAME_PointPusher || + thing->GetClass()->TypeName == NAME_PointPuller) + { + Create(DPusher::p_push, l->args[3] ? l : NULL, l->args[2], 0, thing, thing->Sector->Index()); + } + } + } + l->special = 0; + break; + } + } +} + +//----------------------------------------------------------------------------- +// +// Initialize the scrollers +// +//----------------------------------------------------------------------------- + +void MapLoader::SpawnScrollers() +{ + auto SCROLLTYPE = [](int i) { return EScrollPos((i <= 0) || (i & ~7) ? 7 : i); }; + + line_t *l = &Level->lines[0]; + side_t *side; + TArray copyscrollers; + + for (auto &line : Level->lines) + { + if (line.special == Sector_CopyScroller) + { + // don't allow copying the scroller if the sector has the same tag as it would just duplicate it. + if (!Level->SectorHasTag(line.frontsector, line.args[0])) + { + copyscrollers.Push(line.Index()); + } + line.special = 0; + } + } + + for (unsigned i = 0; i < Level->lines.Size(); i++, l++) + { + double dx; // direction and speed of scrolling + double dy; + sector_t *control = nullptr; + int accel = 0; // no control sector or acceleration + int special = l->special; + + // Check for undefined parameters that are non-zero and output messages for them. + // We don't report for specials we don't understand. + FLineSpecial *spec = P_GetLineSpecialInfo(special); + if (spec != nullptr) + { + int max = spec->map_args; + for (unsigned arg = max; arg < countof(l->args); ++arg) + { + if (l->args[arg] != 0) + { + Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n", + i, special, spec->name, arg + 1, l->args[arg]); + } + } + } + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + // [RH] Assume that it's a scroller and zero the line's special. + l->special = 0; + + dx = dy = 0; // Shut up, GCC + + if (special == Scroll_Ceiling || + special == Scroll_Floor || + special == Scroll_Texture_Model) + { + if (l->args[1] & 3) + { + // if 1, then displacement + // if 2, then accelerative (also if 3) + control = l->sidedef[0]->sector; + if (l->args[1] & 2) + accel = 1; + } + if (special == Scroll_Texture_Model || l->args[1] & 4) + { + // The line housing the special controls the + // direction and speed of scrolling. + dx = l->Delta().X / 32.; + dy = l->Delta().Y / 32.; + } + else + { + // The speed and direction are parameters to the special. + dx = (l->args[3] - 128) / 32.; + dy = (l->args[4] - 128) / 32.; + } + } + + switch (special) + { + int s; + + case Scroll_Ceiling: + { + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + { + Create(EScroll::sc_ceiling, -dx, dy, control, &Level->sectors[s], nullptr, accel); + } + for (unsigned j = 0; j < copyscrollers.Size(); j++) + { + line_t *line = &Level->lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 1)) + { + Create(EScroll::sc_ceiling, -dx, dy, control, line->frontsector, nullptr, accel); + } + } + break; + } + + case Scroll_Floor: + if (l->args[2] != 1) + { // scroll the floor texture + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + { + Create(EScroll::sc_floor, -dx, dy, control, &Level->sectors[s], nullptr, accel); + } + for (unsigned j = 0; j < copyscrollers.Size(); j++) + { + line_t *line = &Level->lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 2)) + { + Create(EScroll::sc_floor, -dx, dy, control, line->frontsector, nullptr, accel); + } + } + } + + if (l->args[2] > 0) + { // carry objects on the floor + auto itr = Level->GetSectorTagIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + { + Create(EScroll::sc_carry, dx, dy, control, &Level->sectors[s], nullptr, accel); + } + for (unsigned j = 0; j < copyscrollers.Size(); j++) + { + line_t *line = &Level->lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 4)) + { + Create(EScroll::sc_carry, dx, dy, control, line->frontsector, nullptr, accel); + } + } + } + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case Scroll_Texture_Model: + { + auto itr = Level->GetLineIdIterator(l->args[0]); + while ((s = itr.Next()) >= 0) + { + if (s != (int)i) + Create(dx, dy, &Level->lines[s], control, accel); + } + break; + } + + case Scroll_Texture_Offsets: + // killough 3/2/98: scroll according to sidedef offsets + side = Level->lines[i].sidedef[0]; + Create(EScroll::sc_side, -side->GetTextureXOffset(side_t::mid), + side->GetTextureYOffset(side_t::mid), nullptr, nullptr, side, accel, SCROLLTYPE(l->args[0])); + break; + + case Scroll_Texture_Left: + l->special = special; // Restore the special, for compat_useblocking's benefit. + side = Level->lines[i].sidedef[0]; + Create(EScroll::sc_side, l->args[0] / 64., 0, nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Right: + l->special = special; + side = Level->lines[i].sidedef[0]; + Create(EScroll::sc_side, -l->args[0] / 64., 0, nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Up: + l->special = special; + side = Level->lines[i].sidedef[0]; + Create(EScroll::sc_side, 0, l->args[0] / 64., nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Down: + l->special = special; + side = Level->lines[i].sidedef[0]; + Create(EScroll::sc_side, 0, -l->args[0] / 64., nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Both: + side = Level->lines[i].sidedef[0]; + if (l->args[0] == 0) { + dx = (l->args[1] - l->args[2]) / 64.; + dy = (l->args[4] - l->args[3]) / 64.; + Create(EScroll::sc_side, dx, dy, nullptr, nullptr, side, accel); + } + break; + + default: + // [RH] It wasn't a scroller after all, so restore the special. + l->special = special; + break; + } + } +} + + +void MapLoader::CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos) +{ + Create(type, dx, dy, nullptr, affectee, nullptr, accel, scrollpos); +} diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index c4b2d4cb1..e71038fda 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -106,7 +106,7 @@ void F3DFloor::UpdateColormap(FColormap &map) // Add one 3D floor to the sector // //========================================================================== -static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int alpha) +void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int alpha) { F3DFloor* ffloor; unsigned i; @@ -778,156 +778,6 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li } } -//========================================================================== -// -// Creates all 3D floors defined by one linedef -// -//========================================================================== -int MapLoader::Set3DFloor(line_t * line, int param, int param2, int alpha) -{ - int s; - int flags; - int tag = line->args[0]; - sector_t * sec = line->frontsector, *ss; - - auto itr = Level->GetSectorTagIterator(tag); - while ((s = itr.Next()) >= 0) - { - ss = &Level->sectors[s]; - - if (param == 0) - { - flags = FF_EXISTS | FF_RENDERALL | FF_SOLID | FF_INVERTSECTOR; - alpha = 255; - for (auto l: sec->Lines) - { - if (l->special == Sector_SetContents && l->frontsector == sec) - { - alpha = clamp(l->args[1], 0, 100); - if (l->args[2] & 1) flags &= ~FF_SOLID; - if (l->args[2] & 2) flags |= FF_SEETHROUGH; - if (l->args[2] & 4) flags |= FF_SHOOTTHROUGH; - if (l->args[2] & 8) flags |= FF_ADDITIVETRANS; - if (alpha != 100) flags |= FF_TRANSLUCENT;//|FF_BOTHPLANES|FF_ALLSIDES; - if (l->args[0]) - { - // Yes, Vavoom's 3D-floor definitions suck! - // The content list changed in r1783 of Vavoom to be unified - // among all its supported games, so it has now ten different - // values instead of just five. - static uint32_t vavoomcolors[] = { VC_EMPTY, - VC_WATER, VC_LAVA, VC_NUKAGE, VC_SLIME, VC_HELLSLIME, - VC_BLOOD, VC_SLUDGE, VC_HAZARD, VC_BOOMWATER }; - flags |= FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_FLOOD; - - l->frontsector->Colormap.FadeColor = vavoomcolors[l->args[0]] & VC_COLORMASK; - l->frontsector->Colormap.FogDensity = 0; - } - alpha = (alpha * 255) / 100; - break; - } - } - } - else if (param == 4) - { - flags = FF_EXISTS | FF_RENDERPLANES | FF_INVERTPLANES | FF_NOSHADE | FF_FIX; - if (param2 & 1) flags |= FF_SEETHROUGH; // marker for allowing missing texture checks - alpha = 255; - } - else - { - static const int defflags[] = { 0, - FF_SOLID, - FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_SHOOTTHROUGH | FF_SEETHROUGH, - FF_SHOOTTHROUGH | FF_SEETHROUGH, - }; - - flags = defflags[param & 3] | FF_EXISTS | FF_RENDERALL; - - if (param & 4) flags |= FF_ALLSIDES | FF_BOTHPLANES; - if (param & 16) flags ^= FF_SEETHROUGH; - if (param & 32) flags ^= FF_SHOOTTHROUGH; - - if (param2 & 1) flags |= FF_NOSHADE; - if (param2 & 2) flags |= FF_DOUBLESHADOW; - if (param2 & 4) flags |= FF_FOG; - if (param2 & 8) flags |= FF_THINFLOOR; - if (param2 & 16) flags |= FF_UPPERTEXTURE; - if (param2 & 32) flags |= FF_LOWERTEXTURE; - if (param2 & 64) flags |= FF_ADDITIVETRANS | FF_TRANSLUCENT; - // if flooding is used the floor must be non-solid and is automatically made shootthrough and seethrough - if ((param2 & 128) && !(flags & FF_SOLID)) flags |= FF_FLOOD | FF_SEETHROUGH | FF_SHOOTTHROUGH; - if (param2 & 512) flags |= FF_FADEWALLS; - if (param2&1024) flags |= FF_RESET; - FTextureID tex = line->sidedef[0]->GetTexture(side_t::top); - if (!tex.Exists() && alpha < 255) - { - alpha = -tex.GetIndex(); - } - alpha = clamp(alpha, 0, 255); - if (alpha == 0) flags &= ~(FF_RENDERALL | FF_BOTHPLANES | FF_ALLSIDES); - else if (alpha != 255) flags |= FF_TRANSLUCENT; - - } - P_Add3DFloor(ss, sec, line, flags, alpha); - } - // To be 100% safe this should be done even if the alpha by texture value isn't used. - if (!line->sidedef[0]->GetTexture(side_t::top).isValid()) - line->sidedef[0]->SetTexture(side_t::top, FNullTextureID()); - return 1; -} - -//========================================================================== -// -// Spawns 3D floors -// -//========================================================================== -void MapLoader::Spawn3DFloors () -{ - static int flagvals[] = {512, 2+512, 512+1024}; - - for (auto &line : Level->lines) - { - switch(line.special) - { - case ExtraFloor_LightOnly: - if (line.args[1] < 0 || line.args[1] > 2) line.args[1] = 0; - if (line.args[0] != 0) - Set3DFloor(&line, 3, flagvals[line.args[1]], 0); - break; - - case Sector_Set3DFloor: - // The flag high-byte/line id is only needed in Hexen format. - // UDMF can set both of these parameters without any restriction of the usable values. - // In Doom format the translators can take full integers for the tag and the line ID always is the same as the tag. - if (Level->maptype == MAPTYPE_HEXEN) - { - if (line.args[1]&8) - { - Level->tagManager.AddLineID(line.Index(), line.args[4]); - } - else - { - line.args[0]+=256*line.args[4]; - line.args[4]=0; - } - } - Set3DFloor(&line, line.args[1]&~8, line.args[2], line.args[3]); - break; - - default: - continue; - } - line.special=0; - line.args[0] = line.args[1] = line.args[2] = line.args[3] = line.args[4] = 0; - } - - for (auto &sec : Level->sectors) - { - P_Recalculate3DFloors(&sec); - } -} - //========================================================================== // diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index d0f4f500a..5a8abb807 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -136,4 +136,6 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li secplane_t P_FindFloorPlane(sector_t * sector, const DVector3 &pos); int P_Find3DFloor(sector_t * sec, const DVector3 &pos, bool above, bool floor, double &cmpz); +void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int alpha); + #endif \ No newline at end of file diff --git a/src/p_lights.cpp b/src/p_lights.cpp index f36fde90c..43bd6beff 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -38,6 +38,7 @@ #include "p_maputl.h" #include "g_levellocals.h" #include "maploader/maploader.h" +#include "p_spec_thinkers.h" // State. #include "serializer.h" @@ -48,132 +49,6 @@ static FRandom pr_strobeflash ("StrobeFlash"); static FRandom pr_fireflicker ("FireFlicker"); -class DFireFlicker : public DLighting -{ - DECLARE_CLASS(DFireFlicker, DLighting) -public: - DFireFlicker(sector_t *sector); - DFireFlicker(sector_t *sector, int upper, int lower); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_Count; - int m_MaxLight; - int m_MinLight; -private: - DFireFlicker(); -}; - -class DFlicker : public DLighting -{ - DECLARE_CLASS(DFlicker, DLighting) -public: - DFlicker(sector_t *sector, int upper, int lower); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_Count; - int m_MaxLight; - int m_MinLight; -private: - DFlicker(); -}; - -class DLightFlash : public DLighting -{ - DECLARE_CLASS(DLightFlash, DLighting) -public: - DLightFlash(sector_t *sector); - DLightFlash(sector_t *sector, int min, int max); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_Count; - int m_MaxLight; - int m_MinLight; - int m_MaxTime; - int m_MinTime; -private: - DLightFlash(); -}; - -class DStrobe : public DLighting -{ - DECLARE_CLASS(DStrobe, DLighting) -public: - DStrobe(sector_t *sector, int utics, int ltics, bool inSync); - DStrobe(sector_t *sector, int upper, int lower, int utics, int ltics); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_Count; - int m_MinLight; - int m_MaxLight; - int m_DarkTime; - int m_BrightTime; -private: - DStrobe(); -}; - -class DGlow : public DLighting -{ - DECLARE_CLASS(DGlow, DLighting) -public: - DGlow(sector_t *sector); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_MinLight; - int m_MaxLight; - int m_Direction; -private: - DGlow(); -}; - -// [RH] Glow from Light_Glow and Light_Fade specials -class DGlow2 : public DLighting -{ - DECLARE_CLASS(DGlow2, DLighting) -public: - DGlow2(sector_t *sector, int start, int end, int tics, bool oneshot); - void Serialize(FSerializer &arc); - void Tick(); -protected: - int m_Start; - int m_End; - int m_MaxTics; - int m_Tics; - bool m_OneShot; -private: - DGlow2(); -}; - -// [RH] Phased light thinker -class DPhased : public DLighting -{ - DECLARE_CLASS(DPhased, DLighting) -public: - DPhased(sector_t *sector); - DPhased(sector_t *sector, int baselevel, int phase); - // These are for internal use only but the Create template needs access to them. - DPhased(); - DPhased(sector_t *sector, int baselevel); - - void Serialize(FSerializer &arc); - void Tick(); -protected: - uint8_t m_BaseLevel; - uint8_t m_Phase; -private: - int PhaseHelper(sector_t *sector, int index, int light, sector_t *prev); -}; - -#define GLOWSPEED 8 -#define STROBEBRIGHT 5 -#define FASTDARK 15 -#define SLOWDARK TICRATE - - //----------------------------------------------------------------------------- // // @@ -694,6 +569,7 @@ void DGlow::Serialize(FSerializer &arc) void DGlow::Tick () { + const int GLOWSPEED = 8; int newlight = m_Sector->lightlevel; switch (m_Direction) @@ -998,61 +874,3 @@ void EV_StopLightEffect (int tag) } -void MapLoader::SpawnLights(sector_t *sector) -{ - switch (sector->special) - { - case Light_Phased: - Create(sector, 48, 63 - (sector->lightlevel & 63)); - break; - - // [RH] Hexen-like phased lighting - case LightSequenceStart: - Create(sector); - break; - - case dLight_Flicker: - Create(sector); - break; - - case dLight_StrobeFast: - Create(sector, STROBEBRIGHT, FASTDARK, false); - break; - - case dLight_StrobeSlow: - Create(sector, STROBEBRIGHT, SLOWDARK, false); - break; - - case dLight_Strobe_Hurt: - Create(sector, STROBEBRIGHT, FASTDARK, false); - break; - - case dLight_Glow: - Create(sector); - break; - - case dLight_StrobeSlowSync: - Create(sector, STROBEBRIGHT, SLOWDARK, true); - break; - - case dLight_StrobeFastSync: - Create(sector, STROBEBRIGHT, FASTDARK, true); - break; - - case dLight_FireFlicker: - Create(sector); - break; - - case dScroll_EastLavaDamage: - Create(sector, STROBEBRIGHT, FASTDARK, false); - break; - - case sLight_Strobe_Hurt: - Create(sector, STROBEBRIGHT, FASTDARK, false); - break; - - default: - break; - } -} - diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp index bbe135bd1..b8d079dda 100644 --- a/src/p_pusher.cpp +++ b/src/p_pusher.cpp @@ -36,49 +36,11 @@ #include "d_player.h" #include "g_levellocals.h" #include "actorinlines.h" +#include "p_spec_thinkers.h" #include "maploader/maploader.h" CVAR(Bool, var_pushers, true, CVAR_SERVERINFO); -// phares 3/20/98: added new model of Pushers for push/pull effects - -class DPusher : public DThinker -{ - DECLARE_CLASS (DPusher, DThinker) - HAS_OBJECT_POINTERS -public: - enum EPusher - { - p_push, - p_pull, - p_wind, - p_current - }; - - DPusher (); - DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); - void Serialize(FSerializer &arc); - int CheckForSectorMatch (EPusher type, int tag); - void ChangeValues (int magnitude, int angle) - { - DAngle ang = angle * (360. / 256.); - m_PushVec = ang.ToVector(magnitude); - m_Magnitude = magnitude; - } - - void Tick (); - -protected: - EPusher m_Type; - TObjPtr m_Source;// Point source if point pusher - DVector2 m_PushVec; - double m_Magnitude; // Vector strength for point pusher - double m_Radius; // Effective radius for point pusher - int m_Affectee; // Number of affected sector - - friend bool PIT_PushThing (AActor *thing); -}; - IMPLEMENT_CLASS(DPusher, false, true) IMPLEMENT_POINTERS_START(DPusher) @@ -334,113 +296,28 @@ void DPusher::Tick () } } -///////////////////////////// -// -// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, -// NULL otherwise. -AActor *MapLoader::GetPushThing (int s) +void AdjustPusher(int tag, int magnitude, int angle, bool wind) { - AActor* thing; - sector_t* sec; + DPusher::EPusher type = wind ? DPusher::p_wind : DPusher::p_current; - sec = &Level->sectors[s]; - thing = sec->thinglist; - - while (thing && - thing->GetClass()->TypeName != NAME_PointPusher && - thing->GetClass()->TypeName != NAME_PointPuller) - { - thing = thing->snext; - } - return thing; -} - -///////////////////////////// -// -// Initialize the sectors where pushers are present -// - -void MapLoader::SpawnPushers () -{ - line_t *l = &Level->lines[0]; - int s; - - for (unsigned i = 0; i < Level->lines.Size(); i++, l++) - { - switch (l->special) - { - case Sector_SetWind: // wind - { - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - Create(DPusher::p_wind, l->args[3] ? l : nullptr, l->args[1], l->args[2], nullptr, s); - l->special = 0; - break; - } - - case Sector_SetCurrent: // current - { - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - Create(DPusher::p_current, l->args[3] ? l : nullptr, l->args[1], l->args[2], nullptr, s); - l->special = 0; - break; - } - - case PointPush_SetForce: // push/pull - if (l->args[0]) { // [RH] Find thing by sector - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - { - AActor *thing = GetPushThing (s); - if (thing) { // No MT_P* means no effect - // [RH] Allow narrowing it down by tid - if (!l->args[1] || l->args[1] == thing->tid) - Create (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], - 0, thing, s); - } - } - } else { // [RH] Find thing by tid - AActor *thing; - auto iterator = Level->GetActorIterator(l->args[1]); - - while ( (thing = iterator.Next ()) ) - { - if (thing->GetClass()->TypeName == NAME_PointPusher || - thing->GetClass()->TypeName == NAME_PointPuller) - { - Create (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], 0, thing, thing->Sector->Index()); - } - } - } - l->special = 0; - break; - } - } -} - -void AdjustPusher (int tag, int magnitude, int angle, bool wind) -{ - DPusher::EPusher type = wind? DPusher::p_wind : DPusher::p_current; - // Find pushers already attached to the sector, and change their parameters. TArray Collection; { TThinkerIterator iterator; FThinkerCollection collect; - while ( (collect.Obj = iterator.Next ()) ) + while ((collect.Obj = iterator.Next())) { - if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0) + if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch(type, tag)) >= 0) { - ((DPusher *)collect.Obj)->ChangeValues (magnitude, angle); - Collection.Push (collect); + ((DPusher *)collect.Obj)->ChangeValues(magnitude, angle); + Collection.Push(collect); } } } - size_t numcollected = Collection.Size (); + size_t numcollected = Collection.Size(); int secnum; // Now create pushers for any sectors that don't already have them. @@ -455,7 +332,7 @@ void AdjustPusher (int tag, int magnitude, int angle, bool wind) } if (i == numcollected) { - Create (type, nullptr, magnitude, angle, nullptr, secnum); + Create(type, nullptr, magnitude, angle, nullptr, secnum); } } } diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 5e24b09d2..649b8afe8 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -63,50 +63,7 @@ #include "r_data/r_interpolate.h" #include "g_levellocals.h" #include "maploader/maploader.h" - -//----------------------------------------------------------------------------- -// -// killough 3/7/98: Add generalized scroll effects -// -//----------------------------------------------------------------------------- - -class DScroller : public DThinker -{ - DECLARE_CLASS (DScroller, DThinker) - HAS_OBJECT_POINTERS -public: - - DScroller(EScroll type, double dx, double dy, sector_t *control, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos = EScrollPos::scw_all); - DScroller (double dx, double dy, const line_t *l, sector_t *control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); - void OnDestroy() override; - - void Serialize(FSerializer &arc); - void Tick (); - - bool AffectsWall (side_t * wall) const { return m_Side == wall; } - side_t *GetWall () const { return m_Side; } - sector_t *GetSector() const { return m_Sector; } - void SetRate (double dx, double dy) { m_dx = dx; m_dy = dy; } - bool IsType (EScroll type) const { return type == m_Type; } - EScrollPos GetScrollParts() const { return m_Parts; } - -protected: - EScroll m_Type; // Type of scroll effect - double m_dx, m_dy; // (dx,dy) scroll speeds - sector_t *m_Sector; // Affected sector - side_t *m_Side; // ... or side - sector_t *m_Controller; // Control sector (nullptr if none) used to control scrolling - double m_LastHeight; // Last known height of control sector - double m_vdx, m_vdy; // Accumulated velocity if accelerative - int m_Accel; // Whether it's accelerative - EScrollPos m_Parts; // Which parts of a sidedef are being scrolled? - TObjPtr m_Interpolations[3]; - -private: - DScroller () - { - } -}; +#include "p_spec_thinkers.h" IMPLEMENT_CLASS(DScroller, false, true) @@ -412,221 +369,6 @@ DScroller::DScroller (double dx, double dy, const line_t *l, sector_t * control, } } -// Amount (dx,dy) vector linedef is shifted right to get scroll amount -#define SCROLL_SHIFT 5 -#define SCROLLTYPE(i) EScrollPos(((i) <= 0) || ((i) & ~7) ? 7 : (i)) - -//----------------------------------------------------------------------------- -// -// Initialize the scrollers -// -//----------------------------------------------------------------------------- - -void MapLoader::SpawnScrollers() -{ - line_t *l = &Level->lines[0]; - side_t *side; - TArray copyscrollers; - - for (auto &line : Level->lines) - { - if (line.special == Sector_CopyScroller) - { - // don't allow copying the scroller if the sector has the same tag as it would just duplicate it. - if (!Level->SectorHasTag(line.frontsector, line.args[0])) - { - copyscrollers.Push(line.Index()); - } - line.special = 0; - } - } - - for (unsigned i = 0; i < Level->lines.Size(); i++, l++) - { - double dx; // direction and speed of scrolling - double dy; - sector_t *control = nullptr; - int accel = 0; // no control sector or acceleration - int special = l->special; - - // Check for undefined parameters that are non-zero and output messages for them. - // We don't report for specials we don't understand. - FLineSpecial *spec = P_GetLineSpecialInfo(special); - if (spec != nullptr) - { - int max = spec->map_args; - for (unsigned arg = max; arg < countof(l->args); ++arg) - { - if (l->args[arg] != 0) - { - Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n", - i, special, spec->name, arg+1, l->args[arg]); - } - } - } - - // killough 3/7/98: Types 245-249 are same as 250-254 except that the - // first side's sector's heights cause scrolling when they change, and - // this linedef controls the direction and speed of the scrolling. The - // most complicated linedef since donuts, but powerful :) - // - // killough 3/15/98: Add acceleration. Types 214-218 are the same but - // are accelerative. - - // [RH] Assume that it's a scroller and zero the line's special. - l->special = 0; - - dx = dy = 0; // Shut up, GCC - - if (special == Scroll_Ceiling || - special == Scroll_Floor || - special == Scroll_Texture_Model) - { - if (l->args[1] & 3) - { - // if 1, then displacement - // if 2, then accelerative (also if 3) - control = l->sidedef[0]->sector; - if (l->args[1] & 2) - accel = 1; - } - if (special == Scroll_Texture_Model || l->args[1] & 4) - { - // The line housing the special controls the - // direction and speed of scrolling. - dx = l->Delta().X / 32.; - dy = l->Delta().Y / 32.; - } - else - { - // The speed and direction are parameters to the special. - dx = (l->args[3] - 128) / 32.; - dy = (l->args[4] - 128) / 32.; - } - } - - switch (special) - { - int s; - - case Scroll_Ceiling: - { - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - { - Create(EScroll::sc_ceiling, -dx, dy, control, &Level->sectors[s], nullptr, accel); - } - for (unsigned j = 0; j < copyscrollers.Size(); j++) - { - line_t *line = &Level->lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 1)) - { - Create(EScroll::sc_ceiling, -dx, dy, control, line->frontsector, nullptr, accel); - } - } - break; - } - - case Scroll_Floor: - if (l->args[2] != 1) - { // scroll the floor texture - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - { - Create (EScroll::sc_floor, -dx, dy, control, &Level->sectors[s], nullptr, accel); - } - for(unsigned j = 0;j < copyscrollers.Size(); j++) - { - line_t *line = &Level->lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 2)) - { - Create(EScroll::sc_floor, -dx, dy, control, line->frontsector, nullptr, accel); - } - } - } - - if (l->args[2] > 0) - { // carry objects on the floor - auto itr = Level->GetSectorTagIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - { - Create (EScroll::sc_carry, dx, dy, control, &Level->sectors[s], nullptr, accel); - } - for(unsigned j = 0;j < copyscrollers.Size(); j++) - { - line_t *line = &Level->lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 4)) - { - Create (EScroll::sc_carry, dx, dy, control, line->frontsector, nullptr, accel); - } - } - } - break; - - // killough 3/1/98: scroll wall according to linedef - // (same direction and speed as scrolling floors) - case Scroll_Texture_Model: - { - auto itr = Level->GetLineIdIterator(l->args[0]); - while ((s = itr.Next()) >= 0) - { - if (s != (int)i) - Create(dx, dy, &Level->lines[s], control, accel); - } - break; - } - - case Scroll_Texture_Offsets: - // killough 3/2/98: scroll according to sidedef offsets - side = Level->lines[i].sidedef[0]; - Create (EScroll::sc_side, -side->GetTextureXOffset(side_t::mid), - side->GetTextureYOffset(side_t::mid), nullptr, nullptr, side, accel, SCROLLTYPE(l->args[0])); - break; - - case Scroll_Texture_Left: - l->special = special; // Restore the special, for compat_useblocking's benefit. - side = Level->lines[i].sidedef[0]; - Create (EScroll::sc_side, l->args[0] / 64., 0, nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Right: - l->special = special; - side = Level->lines[i].sidedef[0]; - Create (EScroll::sc_side, -l->args[0] / 64., 0, nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Up: - l->special = special; - side = Level->lines[i].sidedef[0]; - Create (EScroll::sc_side, 0, l->args[0] / 64., nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Down: - l->special = special; - side = Level->lines[i].sidedef[0]; - Create (EScroll::sc_side, 0, -l->args[0] / 64., nullptr, nullptr, side, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Both: - side = Level->lines[i].sidedef[0]; - if (l->args[0] == 0) { - dx = (l->args[1] - l->args[2]) / 64.; - dy = (l->args[4] - l->args[3]) / 64.; - Create (EScroll::sc_side, dx, dy, nullptr, nullptr, side, accel); - } - break; - - default: - // [RH] It wasn't a scroller after all, so restore the special. - l->special = special; - break; - } - } -} - //----------------------------------------------------------------------------- // // Modify a wall scroller @@ -733,8 +475,3 @@ void SetScroller (FLevelLocals *Level, int tag, EScroll type, double dx, double Create (type, dx, dy, nullptr, &Level->sectors[i], nullptr, 0); } } - -void MapLoader::CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos) -{ - Create(type, dx, dy, nullptr, affectee, nullptr, accel, scrollpos); -} diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 0611627c8..d84f36074 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -98,7 +98,7 @@ #include "p_setup.h" #include "c_console.h" -#include "maploader/maploader.h" +#include "p_spec_thinkers.h" static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); @@ -709,25 +709,6 @@ CUSTOM_CVAR (Bool, forcewater, false, CVAR_ARCHIVE|CVAR_SERVERINFO) } } -class DLightTransfer : public DThinker -{ - DECLARE_CLASS (DLightTransfer, DThinker) - - DLightTransfer() {} -public: - DLightTransfer (sector_t *srcSec, int target, bool copyFloor); - void Serialize(FSerializer &arc); - void Tick (); - -protected: - void DoTransfer (int level, int target, bool floor); - - sector_t *Source; - int TargetTag; - bool CopyFloor; - short LastLight; -}; - IMPLEMENT_CLASS(DLightTransfer, false, false) void DLightTransfer::Serialize(FSerializer &arc) @@ -795,31 +776,6 @@ void DLightTransfer::DoTransfer (int llevel, int target, bool floor) } -class DWallLightTransfer : public DThinker -{ - enum - { - WLF_SIDE1=1, - WLF_SIDE2=2, - WLF_NOFAKECONTRAST=4 - }; - - DECLARE_CLASS (DWallLightTransfer, DThinker) - DWallLightTransfer() {} -public: - DWallLightTransfer (sector_t *srcSec, int target, uint8_t flags); - void Serialize(FSerializer &arc); - void Tick (); - -protected: - void DoTransfer (short level, int target, uint8_t flags); - - sector_t *Source; - int TargetID; - short LastLight; - uint8_t Flags; -}; - IMPLEMENT_CLASS(DWallLightTransfer, false, false) void DWallLightTransfer::Serialize(FSerializer &arc) @@ -900,611 +856,6 @@ void DWallLightTransfer::DoTransfer (short lightlevel, int target, uint8_t flags } } -//----------------------------------------------------------------------------- -// -// Portals -// -//----------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// Upper stacks go in the top sector. Lower stacks go in the bottom sector. - -void MapLoader::SetupFloorPortal (AActor *point) -{ - auto it = Level->GetActorIterator(NAME_LowerStackLookOnly, point->tid); - sector_t *Sector = point->Sector; - auto skyv = it.Next(); - if (skyv != nullptr) - { - skyv->target = point; - if (Sector->GetAlpha(sector_t::floor) == 1.) - Sector->SetAlpha(sector_t::floor, clamp(point->args[0], 0, 255) / 255.); - - Sector->Portals[sector_t::floor] = P_GetStackPortal(skyv, sector_t::floor); - } -} - -void MapLoader::SetupCeilingPortal (AActor *point) -{ - auto it = Level->GetActorIterator(NAME_UpperStackLookOnly, point->tid); - sector_t *Sector = point->Sector; - auto skyv = it.Next(); - if (skyv != nullptr) - { - skyv->target = point; - if (Sector->GetAlpha(sector_t::ceiling) == 1.) - Sector->SetAlpha(sector_t::ceiling, clamp(point->args[0], 0, 255) / 255.); - - Sector->Portals[sector_t::ceiling] = P_GetStackPortal(skyv, sector_t::ceiling); - } -} - -void MapLoader::SetupPortals() -{ - auto it = Level->GetThinkerIterator(NAME_StackPoint); - AActor *pt; - TArray points; - - while ((pt = it.Next())) - { - FName nm = pt->GetClass()->TypeName; - if (nm == NAME_UpperStackLookOnly) - { - SetupFloorPortal(pt); - } - else if (nm == NAME_LowerStackLookOnly) - { - SetupCeilingPortal(pt); - } - pt->special1 = 0; - points.Push(pt); - } - // the semantics here are incredibly lax so the final setup can only be done once all portals have been created, - // because later stackpoints will happily overwrite info in older ones, if there are multiple links. - for (auto &s : Level->sectorPortals) - { - if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox) - { - for (auto &ss : Level->sectorPortals) - { - if (ss.mType == PORTS_STACKEDSECTORTHING && ss.mSkybox == s.mSkybox->target) - { - s.mPartner = unsigned((&ss) - &Level->sectorPortals[0]); - } - } - } - } - // Now we can finally set the displacement and delete the stackpoint reference. - for (auto &s : Level->sectorPortals) - { - if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox) - { - s.mDisplacement = s.mSkybox->Pos() - s.mSkybox->target->Pos(); - s.mSkybox = nullptr; - } - } -} - -void MapLoader::SetPortal(sector_t *sector, int plane, unsigned pnum, double alpha) -{ - // plane: 0=floor, 1=ceiling, 2=both - if (plane > 0) - { - if (sector->GetPortalType(sector_t::ceiling) == PORTS_SKYVIEWPOINT) - { - sector->Portals[sector_t::ceiling] = pnum; - if (sector->GetAlpha(sector_t::ceiling) == 1.) - sector->SetAlpha(sector_t::ceiling, alpha); - - if (Level->sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY) - sector->SetTexture(sector_t::ceiling, skyflatnum); - } - } - if (plane == 2 || plane == 0) - { - if (sector->GetPortalType(sector_t::floor) == PORTS_SKYVIEWPOINT) - { - sector->Portals[sector_t::floor] = pnum; - } - if (sector->GetAlpha(sector_t::floor) == 1.) - sector->SetAlpha(sector_t::floor, alpha); - - if (Level->sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY) - sector->SetTexture(sector_t::floor, skyflatnum); - } -} - -void MapLoader::CopyPortal(int sectortag, int plane, unsigned pnum, double alpha, bool tolines) -{ - int s; - auto itr = Level->GetSectorTagIterator(sectortag); - while ((s = itr.Next()) >= 0) - { - SetPortal(&Level->sectors[s], plane, pnum, alpha); - } - - for (auto &line : Level->lines) - { - // Check if this portal needs to be copied to other sectors - // This must be done here to ensure that it gets done only after the portal is set up - if (line.special == Sector_SetPortal && - line.args[1] == 1 && - (line.args[2] == plane || line.args[2] == 3) && - line.args[3] == sectortag) - { - if (line.args[0] == 0) - { - SetPortal(line.frontsector, plane, pnum, alpha); - } - else - { - auto itr = Level->GetSectorTagIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - { - SetPortal(&Level->sectors[s], plane, pnum, alpha); - } - } - } - if (tolines && line.special == Sector_SetPortal && - line.args[1] == 5 && - line.args[3] == sectortag) - { - if (line.args[0] == 0) - { - line.portaltransferred = pnum; - } - else - { - auto itr = Level->GetLineIdIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - { - Level->lines[s].portaltransferred = pnum; - } - } - } - } -} - - -void MapLoader::SpawnPortal(line_t *line, int sectortag, int plane, int bytealpha, int linked) -{ - if (plane < 0 || plane > 2 || (linked && plane == 2)) return; - for (auto &oline : Level->lines) - { - // We must look for the reference line with a linear search unless we want to waste the line ID for it - // which is not a good idea. - if (oline.special == Sector_SetPortal && - oline.args[0] == sectortag && - oline.args[1] == linked && - oline.args[2] == plane && - oline.args[3] == 1) - { - // beware of overflows. - DVector2 pos1 = line->v1->fPos() + line->Delta() / 2; - DVector2 pos2 = oline.v1->fPos() + oline.Delta() / 2; - unsigned pnum = P_GetPortal(linked ? PORTS_LINKEDPORTAL : PORTS_PORTAL, plane, line->frontsector, oline.frontsector, pos2 - pos1); - CopyPortal(sectortag, plane, pnum, bytealpha / 255., false); - return; - } - } -} - -// This searches the viewpoint's sector -// for a skybox line special, gets its tag and transfers the skybox to all tagged sectors. -void MapLoader::SpawnSkybox(AActor *origin) -{ - sector_t *Sector = origin->Sector; - if (Sector == NULL) - { - Printf("Sector not initialized for SkyCamCompat\n"); - origin->Sector = Sector = P_PointInSector(origin->Pos()); - } - if (Sector) - { - for(auto refline : Sector->Lines) - { - if (refline->special == Sector_SetPortal && refline->args[1] == 2) - { - // We found the setup linedef for this skybox, so let's use it for our init. - unsigned pnum = P_GetSkyboxPortal(origin); - CopyPortal(refline->args[0], refline->args[2], pnum, 0, true); - return; - } - } - } -} - - - -// -// P_SetSectorDamage -// -// Sets damage properties for one sector. Allows combination of original specials with explicit use of the damage properties -// - -static void SetupSectorDamage(sector_t *sector, int damage, int interval, int leakchance, FName type, int flags) -{ - // Only set if damage is not yet initialized. This ensures that UDMF takes precedence over sector specials. - if (sector->damageamount == 0) - { - sector->damageamount = damage; - sector->damageinterval = MAX(1, interval); - sector->leakydamage = leakchance; - sector->damagetype = type; - sector->Flags = (sector->Flags & ~SECF_DAMAGEFLAGS) | (flags & SECF_DAMAGEFLAGS); - } -} - -// -// P_InitSectorSpecial -// -// Sets up everything derived from 'sector->special' for one sector -// ('fromload' is necessary to allow conversion upon savegame load.) -// - -void MapLoader::InitSectorSpecial(sector_t *sector, int special) -{ - // [RH] All secret sectors are marked with a BOOM-ish bitfield - if (sector->special & SECRET_MASK) - { - sector->Flags |= SECF_SECRET | SECF_WASSECRET; - Level->total_secrets++; - } - if (sector->special & FRICTION_MASK) - { - sector->Flags |= SECF_FRICTION; - } - if (sector->special & PUSH_MASK) - { - sector->Flags |= SECF_PUSH; - } - if ((sector->special & DAMAGE_MASK) == 0x100) - { - SetupSectorDamage(sector, 5, 32, 0, NAME_Fire, 0); - } - else if ((sector->special & DAMAGE_MASK) == 0x200) - { - SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); - } - else if ((sector->special & DAMAGE_MASK) == 0x300) - { - SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); - } - sector->special &= 0xff; - - // [RH] Normal DOOM special or BOOM specialized? - bool keepspecial = false; - SpawnLights(sector); - switch (sector->special) - { - case dLight_Strobe_Hurt: - SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); - break; - - case dDamage_Hellslime: - SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); - break; - - case dDamage_Nukage: - SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); - break; - - case dSector_DoorCloseIn30: - Create(sector, DDoor::doorWaitClose, 2, 0, 0, 30 * TICRATE); - break; - - case dDamage_End: - SetupSectorDamage(sector, 20, 32, 256, NAME_None, SECF_ENDGODMODE|SECF_ENDLEVEL); - break; - - case dSector_DoorRaiseIn5Mins: - Create (sector, DDoor::doorWaitRaise, 2, TICRATE*30/7, 0, 5*60*TICRATE); - break; - - case dFriction_Low: - sector->friction = FRICTION_LOW; - sector->movefactor = 0x269/65536.; - sector->Flags |= SECF_FRICTION; - break; - - case dDamage_SuperHellslime: - SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); - break; - - case dDamage_LavaWimpy: - SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); - break; - - case dDamage_LavaHefty: - SetupSectorDamage(sector, 8, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); - break; - - case dScroll_EastLavaDamage: - SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); - CreateScroller(EScroll::sc_floor, -4., 0, sector, 0); - keepspecial = true; - break; - - case hDamage_Sludge: - SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, 0); - break; - - case sLight_Strobe_Hurt: - SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); - break; - - case sDamage_Hellslime: - SetupSectorDamage(sector, 2, 32, 0, NAME_Slime, SECF_HAZARD); - break; - - case Damage_InstantDeath: - // Strife's instant death sector - SetupSectorDamage(sector, TELEFRAG_DAMAGE, 1, 256, NAME_InstantDeath, 0); - break; - - case sDamage_SuperHellslime: - SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, SECF_HAZARD); - break; - - case Sector_Hidden: - sector->MoreFlags |= SECMF_HIDDEN; - break; - - case Sector_Heal: - // CoD's healing sector - SetupSectorDamage(sector, -1, 32, 0, NAME_None, 0); - break; - - case Sky2: - sector->sky = PL_SKYFLAT; - break; - - default: - if (sector->special >= Scroll_North_Slow && sector->special <= Scroll_SouthWest_Fast) - { // Hexen scroll special - static const int8_t hexenScrollies[24][2] = - { - { 0, 1 }, { 0, 2 }, { 0, 4 }, - { -1, 0 }, { -2, 0 }, { -4, 0 }, - { 0, -1 }, { 0, -2 }, { 0, -4 }, - { 1, 0 }, { 2, 0 }, { 4, 0 }, - { 1, 1 }, { 2, 2 }, { 4, 4 }, - { -1, 1 }, { -2, 2 }, { -4, 4 }, - { -1, -1 }, { -2, -2 }, { -4, -4 }, - { 1, -1 }, { 2, -2 }, { 4, -4 } - }; - - - int i = sector->special - Scroll_North_Slow; - double dx = hexenScrollies[i][0] / 2.; - double dy = hexenScrollies[i][1] / 2.; - CreateScroller(EScroll::sc_floor, dx, dy, sector, 0); - } - else if (sector->special >= Carry_East5 && sector->special <= Carry_East35) - { - // Heretic scroll special - // Only east scrollers also scroll the texture - CreateScroller(EScroll::sc_floor, -0.5 * (1 << ((sector->special & 0xff) - Carry_East5)), 0, sector, 0); - } - keepspecial = true; - break; - } - if (!keepspecial) sector->special = 0; -} - -// -// P_SpawnSpecials -// -// After the map has been loaded, scan for specials that spawn thinkers -// - -void MapLoader::SpawnSpecials () -{ - SetupPortals(); - - for (auto &sec : Level->sectors) - { - if (sec.special == 0) - continue; - - InitSectorSpecial(&sec, sec.special); - } - - ProcessEDSectors(); - - // Init other misc stuff - - SpawnScrollers(); // killough 3/7/98: Add generalized scrollers - SpawnFriction(); // phares 3/12/98: New friction model using linedefs - SpawnPushers(); // phares 3/20/98: New pusher model using linedefs - - auto it2 = Level->GetThinkerIterator(NAME_SkyCamCompat); - AActor *pt2; - while ((pt2 = it2.Next())) - { - SpawnSkybox(pt2); - } - - for (auto &line : Level->lines) - { - switch (line.special) - { - int s; - sector_t *sec; - - // killough 3/7/98: - // support for drawn heights coming from different sector - case Transfer_Heights: - { - sec = line.frontsector; - - if (line.args[1] & 2) - { - sec->MoreFlags |= SECMF_FAKEFLOORONLY; - } - if (line.args[1] & 4) - { - sec->MoreFlags |= SECMF_CLIPFAKEPLANES; - } - if (line.args[1] & 8) - { - sec->MoreFlags |= SECMF_UNDERWATER; - } - else if (forcewater) - { - sec->MoreFlags |= SECMF_FORCEDUNDERWATER; - } - if (line.args[1] & 16) - { - sec->MoreFlags |= SECMF_IGNOREHEIGHTSEC; - } - else Level->HasHeightSecs = true; - if (line.args[1] & 32) - { - sec->MoreFlags |= SECMF_NOFAKELIGHT; - } - auto itr = Level->GetSectorTagIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - { - Level->sectors[s].heightsec = sec; - sec->e->FakeFloor.Sectors.Push(&Level->sectors[s]); - Level->sectors[s].MoreFlags |= (sec->MoreFlags & SECMF_IGNOREHEIGHTSEC); // copy this to the destination sector for easier checking. - Level->sectors[s].AdjustFloorClip(); - } - break; - } - - // killough 3/16/98: Add support for setting - // floor lighting independently (e.g. lava) - case Transfer_FloorLight: - Create (line.frontsector, line.args[0], true); - break; - - // killough 4/11/98: Add support for setting - // ceiling lighting independently - case Transfer_CeilingLight: - Create (line.frontsector, line.args[0], false); - break; - - // [Graf Zahl] Add support for setting lighting - // per wall independently - case Transfer_WallLight: - Create (line.frontsector, line.args[0], line.args[1]); - break; - - case Sector_Attach3dMidtex: - P_Attach3dMidtexLinesToSector(line.frontsector, line.args[0], line.args[1], !!line.args[2]); - break; - - case Sector_SetLink: - if (line.args[0] == 0) - { - P_AddSectorLinks(line.frontsector, line.args[1], line.args[2], line.args[3]); - } - break; - - case Sector_SetPortal: - // arg 0 = sector tag - // arg 1 = type - // - 0: normal (handled here) - // - 1: copy (handled by the portal they copy) - // - 2: EE-style skybox (handled by the camera object) - // - 3: EE-style flat portal (GZDoom HW renderer only for now) - // - 4: EE-style horizon portal (GZDoom HW renderer only for now) - // - 5: copy portal to line (GZDoom HW renderer only for now) - // - 6: linked portal - // other values reserved for later use - // arg 2 = 0:floor, 1:ceiling, 2:both - // arg 3 = 0: anchor, 1: reference line - // arg 4 = for the anchor only: alpha - if ((line.args[1] == 0 || line.args[1] == 6) && line.args[3] == 0) - { - SpawnPortal(&line, line.args[0], line.args[2], line.args[4], line.args[1]); - } - else if (line.args[1] == 3 || line.args[1] == 4) - { - unsigned pnum = P_GetPortal(line.args[1] == 3 ? PORTS_PLANE : PORTS_HORIZON, line.args[2], line.frontsector, NULL, { 0,0 }); - CopyPortal(line.args[0], line.args[2], pnum, 0, true); - } - break; - - case Line_SetPortal: - P_SpawnLinePortal(&line); - break; - - // [RH] ZDoom Static_Init settings - case Static_Init: - switch (line.args[1]) - { - case Init_Gravity: - { - double grav = line.Delta().Length() / 100.; - auto itr = Level->GetSectorTagIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - Level->sectors[s].gravity = grav; - } - break; - - //case Init_Color: - // handled in P_LoadSideDefs2() - - case Init_Damage: - { - int damage = int(line.Delta().Length()); - auto itr = Level->GetSectorTagIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - { - sector_t *sec = &Level->sectors[s]; - sec->damageamount = damage; - sec->damagetype = NAME_None; - if (sec->damageamount < 20) - { - sec->leakydamage = 0; - sec->damageinterval = 32; - } - else if (sec->damageamount < 50) - { - sec->leakydamage = 5; - sec->damageinterval = 32; - } - else - { - sec->leakydamage = 256; - sec->damageinterval = 1; - } - } - } - break; - - case Init_SectorLink: - if (line.args[3] == 0) - P_AddSectorLinksByID(line.frontsector, line.args[0], line.args[2]); - break; - - // killough 10/98: - // - // Support for sky textures being transferred from sidedefs. - // Allows scrolling and other effects (but if scrolling is - // used, then the same sector tag needs to be used for the - // sky sector, the sky-transfer linedef, and the scroll-effect - // linedef). Still requires user to use F_SKY1 for the floor - // or ceiling texture, to distinguish floor and ceiling sky. - - case Init_TransferSky: - { - auto itr = Level->GetSectorTagIterator(line.args[0]); - while ((s = itr.Next()) >= 0) - Level->sectors[s].sky = (line.Index() + 1) | PL_SKYFLAT; - break; - } - } - break; - } - } - // [RH] Start running any open scripts on this map - Level->Behaviors.StartTypedScripts (SCRIPT_Open, NULL, false); -} - //////////////////////////////////////////////////////////////////////////// // // FRICTION EFFECTS @@ -1558,31 +909,6 @@ void MapLoader::SpawnSpecials () // // Initialize the sectors where friction is increased or decreased -void MapLoader::SpawnFriction() -{ - line_t *l = &Level->lines[0]; - - for (unsigned i = 0 ; i < Level->lines.Size() ; i++,l++) - { - if (l->special == Sector_SetFriction) - { - int length; - - if (l->args[1]) - { // [RH] Allow setting friction amount from parameter - length = l->args[1] <= 200 ? l->args[1] : 200; - } - else - { - length = int(l->Delta().Length()); - } - - P_SetSectorFriction (Level, l->args[0], length, false); - l->special = 0; - } - } -} - void P_SetSectorFriction (FLevelLocals *Level, int tag, int amount, bool alterFlag) { int s; diff --git a/src/p_spec_thinkers.h b/src/p_spec_thinkers.h new file mode 100644 index 000000000..dc1a5b3cc --- /dev/null +++ b/src/p_spec_thinkers.h @@ -0,0 +1,253 @@ +#pragma once + +#include "p_spec.h" +#include "r_defs.h" + +class DLightTransfer : public DThinker +{ + DECLARE_CLASS (DLightTransfer, DThinker) + + DLightTransfer() {} +public: + DLightTransfer (sector_t *srcSec, int target, bool copyFloor); + void Serialize(FSerializer &arc); + void Tick (); + +protected: + void DoTransfer (int level, int target, bool floor); + + sector_t *Source; + int TargetTag; + bool CopyFloor; + short LastLight; +}; + +class DWallLightTransfer : public DThinker +{ + enum + { + WLF_SIDE1=1, + WLF_SIDE2=2, + WLF_NOFAKECONTRAST=4 + }; + + DECLARE_CLASS (DWallLightTransfer, DThinker) + DWallLightTransfer() {} +public: + DWallLightTransfer (sector_t *srcSec, int target, uint8_t flags); + void Serialize(FSerializer &arc); + void Tick (); + +protected: + void DoTransfer (short level, int target, uint8_t flags); + + sector_t *Source; + int TargetID; + short LastLight; + uint8_t Flags; +}; + + +class DFireFlicker : public DLighting +{ + DECLARE_CLASS(DFireFlicker, DLighting) +public: + DFireFlicker(sector_t *sector); + DFireFlicker(sector_t *sector, int upper, int lower); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_Count; + int m_MaxLight; + int m_MinLight; +private: + DFireFlicker(); +}; + +class DFlicker : public DLighting +{ + DECLARE_CLASS(DFlicker, DLighting) +public: + DFlicker(sector_t *sector, int upper, int lower); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_Count; + int m_MaxLight; + int m_MinLight; +private: + DFlicker(); +}; + +class DLightFlash : public DLighting +{ + DECLARE_CLASS(DLightFlash, DLighting) +public: + DLightFlash(sector_t *sector); + DLightFlash(sector_t *sector, int min, int max); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_Count; + int m_MaxLight; + int m_MinLight; + int m_MaxTime; + int m_MinTime; +private: + DLightFlash(); +}; + +class DStrobe : public DLighting +{ + DECLARE_CLASS(DStrobe, DLighting) +public: + DStrobe(sector_t *sector, int utics, int ltics, bool inSync); + DStrobe(sector_t *sector, int upper, int lower, int utics, int ltics); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_Count; + int m_MinLight; + int m_MaxLight; + int m_DarkTime; + int m_BrightTime; +private: + DStrobe(); +}; + +class DGlow : public DLighting +{ + DECLARE_CLASS(DGlow, DLighting) +public: + DGlow(sector_t *sector); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_MinLight; + int m_MaxLight; + int m_Direction; +private: + DGlow(); +}; + +// [RH] Glow from Light_Glow and Light_Fade specials +class DGlow2 : public DLighting +{ + DECLARE_CLASS(DGlow2, DLighting) +public: + DGlow2(sector_t *sector, int start, int end, int tics, bool oneshot); + void Serialize(FSerializer &arc); + void Tick(); +protected: + int m_Start; + int m_End; + int m_MaxTics; + int m_Tics; + bool m_OneShot; +private: + DGlow2(); +}; + +// [RH] Phased light thinker +class DPhased : public DLighting +{ + DECLARE_CLASS(DPhased, DLighting) +public: + DPhased(sector_t *sector); + DPhased(sector_t *sector, int baselevel, int phase); + // These are for internal use only but the Create template needs access to them. + DPhased(); + DPhased(sector_t *sector, int baselevel); + + void Serialize(FSerializer &arc); + void Tick(); +protected: + uint8_t m_BaseLevel; + uint8_t m_Phase; +private: + int PhaseHelper(sector_t *sector, int index, int light, sector_t *prev); +}; + +// phares 3/20/98: added new model of Pushers for push/pull effects + +class DPusher : public DThinker +{ + DECLARE_CLASS (DPusher, DThinker) + HAS_OBJECT_POINTERS +public: + enum EPusher + { + p_push, + p_pull, + p_wind, + p_current + }; + + DPusher (); + DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); + void Serialize(FSerializer &arc); + int CheckForSectorMatch (EPusher type, int tag); + void ChangeValues (int magnitude, int angle) + { + DAngle ang = angle * (360. / 256.); + m_PushVec = ang.ToVector(magnitude); + m_Magnitude = magnitude; + } + + void Tick (); + +protected: + EPusher m_Type; + TObjPtr m_Source;// Point source if point pusher + DVector2 m_PushVec; + double m_Magnitude; // Vector strength for point pusher + double m_Radius; // Effective radius for point pusher + int m_Affectee; // Number of affected sector + + friend bool PIT_PushThing (AActor *thing); +}; + +//----------------------------------------------------------------------------- +// +// killough 3/7/98: Add generalized scroll effects +// +//----------------------------------------------------------------------------- + +class DScroller : public DThinker +{ + DECLARE_CLASS (DScroller, DThinker) + HAS_OBJECT_POINTERS +public: + + DScroller(EScroll type, double dx, double dy, sector_t *control, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + DScroller (double dx, double dy, const line_t *l, sector_t *control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + void OnDestroy() override; + + void Serialize(FSerializer &arc); + void Tick (); + + bool AffectsWall (side_t * wall) const { return m_Side == wall; } + side_t *GetWall () const { return m_Side; } + sector_t *GetSector() const { return m_Sector; } + void SetRate (double dx, double dy) { m_dx = dx; m_dy = dy; } + bool IsType (EScroll type) const { return type == m_Type; } + EScrollPos GetScrollParts() const { return m_Parts; } + +protected: + EScroll m_Type; // Type of scroll effect + double m_dx, m_dy; // (dx,dy) scroll speeds + sector_t *m_Sector; // Affected sector + side_t *m_Side; // ... or side + sector_t *m_Controller; // Control sector (nullptr if none) used to control scrolling + double m_LastHeight; // Last known height of control sector + double m_vdx, m_vdy; // Accumulated velocity if accelerative + int m_Accel; // Whether it's accelerative + EScrollPos m_Parts; // Which parts of a sidedef are being scrolled? + TObjPtr m_Interpolations[3]; + +private: + DScroller () + { + } +}; +