mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
1137 lines
30 KiB
C++
1137 lines
30 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 1998-2006 by Randy Heit (ZDoom).
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// DESCRIPTION:
|
|
// [Blair] Define the ZDoom (Doom in Hexen) format doom map spec.
|
|
// Includes sector specials, linedef types, line crossing.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "odamex.h"
|
|
#include "p_local.h"
|
|
#include "p_lnspec.h"
|
|
#include "m_wdlstats.h"
|
|
#include "c_cvars.h"
|
|
#include "d_player.h"
|
|
#include "p_zdoomhexspec.h"
|
|
|
|
EXTERN_CVAR(sv_allowexit)
|
|
EXTERN_CVAR(sv_fragexitswitch)
|
|
EXTERN_CVAR(sv_forcewater)
|
|
|
|
bool P_CrossZDoomSpecialLine(line_t* line, int side, AActor* thing,
|
|
bool bossaction)
|
|
{
|
|
// Do not teleport on the wrong side
|
|
if (side)
|
|
{
|
|
switch (line->special)
|
|
{
|
|
case Teleport:
|
|
case Teleport_NoFog:
|
|
case Teleport_NewMap:
|
|
case Teleport_EndGame:
|
|
case Teleport_NoStop:
|
|
case Teleport_Line:
|
|
return false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (thing->player)
|
|
{
|
|
return P_ActivateZDoomLine(line, thing, side, ML_SPAC_CROSS);
|
|
}
|
|
else if (thing->flags2 & MF2_MCROSS)
|
|
{
|
|
return P_ActivateZDoomLine(line, thing, side, ML_SPAC_MCROSS);
|
|
}
|
|
else if (thing->flags2 & MF2_PCROSS)
|
|
{
|
|
return P_ActivateZDoomLine(line, thing, side, ML_SPAC_PCROSS);
|
|
}
|
|
else if (line->special == Teleport || line->special == Teleport_NoFog ||
|
|
line->special == Teleport_Line)
|
|
{ // [RH] Just a little hack for BOOM compatibility
|
|
return P_ActivateZDoomLine(line, thing, side, ML_SPAC_MCROSS);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool P_ActivateZDoomLine(line_t* line, AActor* mo, int side,
|
|
unsigned int activationType)
|
|
{
|
|
bool buttonSuccess;
|
|
|
|
// Err...
|
|
// Use the back sides of VERY SPECIAL lines...
|
|
if (side && (line->flags & (ML_SPAC_PUSH | ML_SPAC_USE)))
|
|
{
|
|
switch (line->special)
|
|
{
|
|
case Exit_Secret:
|
|
// Sliding door open&close
|
|
// UNUSED?
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!P_TestActivateZDoomLine(line, mo, side, activationType) ||
|
|
!P_CanActivateSpecials(mo, line))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
buttonSuccess = P_ExecuteZDoomLineSpecial(line->special, line->args, line, side, mo);
|
|
|
|
return buttonSuccess;
|
|
}
|
|
|
|
LineActivationType P_LineActivationTypeForSPACFlag(const unsigned int activationType)
|
|
{
|
|
if (activationType & (ML_SPAC_CROSS | ML_SPAC_MCROSS | ML_SPAC_PCROSS | ML_SPAC_CROSSTHROUGH))
|
|
return LineCross;
|
|
else if (activationType & ML_SPAC_IMPACT)
|
|
return LineShoot;
|
|
else if (activationType & ML_SPAC_PUSH)
|
|
return LinePush;
|
|
else if (activationType & (ML_SPAC_USE | ML_SPAC_USETHROUGH))
|
|
return LineUse;
|
|
|
|
return LineUse;
|
|
}
|
|
|
|
void P_CollectSecretZDoom(sector_t& sector, player_t& player)
|
|
{
|
|
P_CollectSecretCommon(sector, player);
|
|
}
|
|
|
|
bool P_TestActivateZDoomLine(line_t* line, AActor* mo, int side,
|
|
unsigned int activationType)
|
|
{
|
|
unsigned int lineActivation;
|
|
|
|
lineActivation = line->flags & ML_SPAC_MASK;
|
|
|
|
if (line->special == Teleport &&
|
|
(lineActivation & ML_SPAC_CROSS || lineActivation & ML_SPAC_CROSSTHROUGH) &&
|
|
activationType & ML_SPAC_PCROSS && mo && mo->flags & MF_MISSILE)
|
|
{ // Let missiles use regular player teleports
|
|
lineActivation |= ML_SPAC_PCROSS;
|
|
}
|
|
|
|
if (!(lineActivation & activationType))
|
|
{
|
|
if (!(activationType & ML_SPAC_MCROSS) || !(lineActivation & ML_SPAC_CROSS))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mo && !P_IsPlayerOrAvatar(*mo) && !(mo->flags & MF_MISSILE) &&
|
|
!(line->flags & ML_MONSTERSCANACTIVATE) &&
|
|
(!(activationType & ML_SPAC_MCROSS) || !(lineActivation & ML_SPAC_MCROSS)))
|
|
{
|
|
bool noway = true;
|
|
|
|
if ((activationType == ML_SPAC_USE || activationType == ML_SPAC_PUSH) &&
|
|
line->flags & ML_SECRET)
|
|
return false; // never open secret doors
|
|
|
|
switch (activationType)
|
|
{
|
|
case ML_SPAC_USE:
|
|
case ML_SPAC_PUSH:
|
|
switch (line->special)
|
|
{
|
|
case Door_Raise:
|
|
if (line->args[0] == 0 && line->args[1] < 64)
|
|
noway = false;
|
|
break;
|
|
case Teleport:
|
|
case Teleport_NoFog:
|
|
noway = false;
|
|
}
|
|
break;
|
|
|
|
case ML_SPAC_MCROSS:
|
|
if (!(lineActivation & ML_SPAC_MCROSS))
|
|
{
|
|
switch (line->special)
|
|
{
|
|
case Door_Raise:
|
|
if (line->args[1] >= 64)
|
|
break;
|
|
[[fallthrough]];
|
|
case Teleport:
|
|
case Teleport_NoFog:
|
|
case Teleport_Line:
|
|
case Plat_DownWaitUpStay:
|
|
case Plat_DownWaitUpStayLip:
|
|
noway = false;
|
|
}
|
|
}
|
|
else
|
|
noway = false;
|
|
break;
|
|
|
|
default:
|
|
noway = false;
|
|
}
|
|
return !noway;
|
|
}
|
|
|
|
if (activationType & ML_SPAC_MCROSS && !(lineActivation & ML_SPAC_MCROSS) &&
|
|
!(line->flags & ML_MONSTERSCANACTIVATE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// P_PlayerInZDoomSector
|
|
// Called every tic frame
|
|
// that the player origin is in a special sector
|
|
// Only runs in ZDoom Doom in Hexen format
|
|
//
|
|
void P_PlayerInZDoomSector(player_t& player)
|
|
{
|
|
// Spectators should not be affected by special sectors
|
|
if (player.spectator)
|
|
return;
|
|
|
|
// Sanity check - could have nulls just immediately after a disconnect.
|
|
if (not (player.mo && player.mo->subsector))
|
|
return;
|
|
|
|
// Falling, not all the way down yet?
|
|
if (player.mo->z != P_FloorHeight(player.mo) && !player.mo->waterlevel)
|
|
return;
|
|
|
|
sector_t& sectorRef = *player.mo->subsector->sector;
|
|
|
|
static constexpr int heretic_carry[5] = {2048 * 5, 2048 * 10, 2048 * 25, 2048 * 30,
|
|
2048 * 35};
|
|
|
|
static constexpr int hexen_carry[3] = {2048 * 5, 2048 * 10, 2048 * 25};
|
|
|
|
if (sectorRef.damageamount > 0)
|
|
{
|
|
// Some maps, like rjspace9f, abuse the fact that "end sector damage" will actually
|
|
// not damage the player beyond 1hp, but won't trigger the exit because they're not damaged.
|
|
const int oldhealth = player.health;
|
|
|
|
if (sectorRef.flags & SECF_ENDGODMODE)
|
|
{
|
|
player.cheats &= ~CF_GODMODE;
|
|
}
|
|
|
|
if (sectorRef.flags & SECF_DMGUNBLOCKABLE || !player.powers[pw_ironfeet] ||
|
|
(sectorRef.leakrate && P_Random(player.mo) < sectorRef.leakrate))
|
|
{
|
|
if (sectorRef.flags & SECF_HAZARD)
|
|
{
|
|
player.hazardcount += sectorRef.damageamount;
|
|
player.hazardinterval = sectorRef.damageinterval;
|
|
}
|
|
|
|
if (sectorRef.special == 0) // ZDoom Static Init Damage
|
|
{
|
|
if (sectorRef.damageamount < 20)
|
|
{
|
|
P_ApplySectorDamageNoRandom(player, sectorRef.damageamount,
|
|
MOD_UNKNOWN);
|
|
}
|
|
else if (sectorRef.damageamount < 50)
|
|
{
|
|
P_ApplySectorDamage(player, sectorRef.damageamount, 5, MOD_UNKNOWN);
|
|
}
|
|
else
|
|
{
|
|
P_ApplySectorDamageNoWait(player, sectorRef.damageamount,
|
|
MOD_UNKNOWN);
|
|
}
|
|
}
|
|
else if (level.time % sectorRef.damageinterval == 0)
|
|
{
|
|
P_DamageMobj(player.mo, NULL, NULL, sectorRef.damageamount);
|
|
}
|
|
|
|
if (sectorRef.flags & SECF_ENDLEVEL && player.health <= 10 && oldhealth != player.health)
|
|
{
|
|
if (serverside && sv_allowexit)
|
|
{
|
|
G_ExitLevel(0, 1);
|
|
}
|
|
}
|
|
|
|
if (sectorRef.flags & SECF_DMGTERRAINFX)
|
|
{
|
|
// MAP_FORMAT_TODO: damage special effects
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (sectorRef.special)
|
|
{
|
|
case dScroll_EastLavaDamage:
|
|
P_ThrustMobj(player.mo, 0, 2048 * 28);
|
|
break;
|
|
case Scroll_Strife_Current:
|
|
int anglespeed;
|
|
fixed_t carryspeed;
|
|
angle_t angle;
|
|
|
|
anglespeed = sectorRef.tag - 100;
|
|
carryspeed = (anglespeed % 10) * 4096;
|
|
angle = (anglespeed / 10) * ANG45;
|
|
P_ThrustMobj(player.mo, angle, carryspeed);
|
|
break;
|
|
case Scroll_Carry_East5:
|
|
case Scroll_Carry_East10:
|
|
case Scroll_Carry_East25:
|
|
case Scroll_Carry_East30:
|
|
case Scroll_Carry_East35:
|
|
P_ThrustMobj(player.mo, 0, heretic_carry[sectorRef.special - Scroll_Carry_East5]);
|
|
break;
|
|
case Scroll_Carry_North5:
|
|
case Scroll_Carry_North10:
|
|
case Scroll_Carry_North25:
|
|
case Scroll_Carry_North30:
|
|
case Scroll_Carry_North35:
|
|
P_ThrustMobj(player.mo, ANG90,
|
|
heretic_carry[sectorRef.special - Scroll_Carry_North5]);
|
|
break;
|
|
case Scroll_Carry_South5:
|
|
case Scroll_Carry_South10:
|
|
case Scroll_Carry_South25:
|
|
case Scroll_Carry_South30:
|
|
case Scroll_Carry_South35:
|
|
P_ThrustMobj(player.mo, ANG270,
|
|
heretic_carry[sectorRef.special - Scroll_Carry_South5]);
|
|
break;
|
|
case Scroll_Carry_West5:
|
|
case Scroll_Carry_West10:
|
|
case Scroll_Carry_West25:
|
|
case Scroll_Carry_West30:
|
|
case Scroll_Carry_West35:
|
|
P_ThrustMobj(player.mo, ANG180,
|
|
heretic_carry[sectorRef.special - Scroll_Carry_West5]);
|
|
break;
|
|
case Scroll_North_Slow:
|
|
case Scroll_North_Medium:
|
|
case Scroll_North_Fast:
|
|
P_ThrustMobj(player.mo, ANG90, hexen_carry[sectorRef.special - Scroll_North_Slow]);
|
|
break;
|
|
case Scroll_East_Slow:
|
|
case Scroll_East_Medium:
|
|
case Scroll_East_Fast:
|
|
P_ThrustMobj(player.mo, 0, hexen_carry[sectorRef.special - Scroll_East_Slow]);
|
|
break;
|
|
case Scroll_South_Slow:
|
|
case Scroll_South_Medium:
|
|
case Scroll_South_Fast:
|
|
P_ThrustMobj(player.mo, ANG270,
|
|
hexen_carry[sectorRef.special - Scroll_South_Slow]);
|
|
break;
|
|
case Scroll_West_Slow:
|
|
case Scroll_West_Medium:
|
|
case Scroll_West_Fast:
|
|
P_ThrustMobj(player.mo, ANG180, hexen_carry[sectorRef.special - Scroll_West_Slow]);
|
|
break;
|
|
case Scroll_NorthWest_Slow:
|
|
case Scroll_NorthWest_Medium:
|
|
case Scroll_NorthWest_Fast:
|
|
P_ThrustMobj(player.mo, ANG135,
|
|
hexen_carry[sectorRef.special - Scroll_NorthWest_Slow]);
|
|
break;
|
|
case Scroll_NorthEast_Slow:
|
|
case Scroll_NorthEast_Medium:
|
|
case Scroll_NorthEast_Fast:
|
|
P_ThrustMobj(player.mo, ANG45,
|
|
hexen_carry[sectorRef.special - Scroll_NorthEast_Slow]);
|
|
break;
|
|
case Scroll_SouthEast_Slow:
|
|
case Scroll_SouthEast_Medium:
|
|
case Scroll_SouthEast_Fast:
|
|
P_ThrustMobj(player.mo, ANG315,
|
|
hexen_carry[sectorRef.special - Scroll_SouthEast_Slow]);
|
|
break;
|
|
case Scroll_SouthWest_Slow:
|
|
case Scroll_SouthWest_Medium:
|
|
case Scroll_SouthWest_Fast:
|
|
P_ThrustMobj(player.mo, ANG225,
|
|
hexen_carry[sectorRef.special - Scroll_SouthWest_Slow]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (sectorRef.flags & SECF_SECRET)
|
|
{
|
|
P_CollectSecretZDoom(sectorRef, player);
|
|
}
|
|
}
|
|
|
|
bool P_ActorInZDoomSector(AActor* actor)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void P_SpawnZDoomGeneralizedSpecials(sector_t* sector)
|
|
{
|
|
int damage_bits = (sector->special & ZDOOM_DAMAGE_MASK) >> 8;
|
|
|
|
switch (damage_bits & 3)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
P_SetupSectorDamage(sector, 5, 32, 0, 0);
|
|
break;
|
|
case 2:
|
|
P_SetupSectorDamage(sector, 10, 32, 0, 0);
|
|
break;
|
|
case 3:
|
|
P_SetupSectorDamage(sector, 20, 32, 5, 0);
|
|
break;
|
|
}
|
|
|
|
if (sector->special & ZDOOM_SECRET_MASK)
|
|
P_AddSectorSecret(sector);
|
|
|
|
if (sector->special & ZDOOM_FRICTION_MASK)
|
|
sector->flags |= SECF_FRICTION;
|
|
|
|
if (sector->special & ZDOOM_PUSH_MASK)
|
|
sector->flags |= SECF_PUSH;
|
|
}
|
|
|
|
void P_SpawnZDoomLights(sector_t* sector)
|
|
{
|
|
switch (sector->special)
|
|
{
|
|
// [RH] Hexen-like phased lighting
|
|
case Light_Phased:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnPhasedLight(sector, 80, -1);
|
|
break;
|
|
case LightSequenceStart:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnLightSequence(sector);
|
|
break;
|
|
case dLight_Flicker:
|
|
// FLICKERING LIGHTS
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnLightFlash(sector);
|
|
break;
|
|
case dLight_StrobeFast:
|
|
// STROBE FAST
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, FASTDARK, false);
|
|
break;
|
|
case dLight_StrobeSlow:
|
|
// STROBE SLOW
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, SLOWDARK, false);
|
|
break;
|
|
case dLight_Strobe_Hurt:
|
|
// STROBE FAST/DEATH SLIME
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, FASTDARK, false);
|
|
sector->special |= dLight_Strobe_Hurt;
|
|
break;
|
|
case dLight_Glow:
|
|
// GLOWING LIGHT
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnGlowingLight(sector);
|
|
break;
|
|
case dLight_StrobeSlowSync:
|
|
// SYNC STROBE SLOW
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, SLOWDARK, true);
|
|
break;
|
|
case dLight_StrobeFastSync:
|
|
// SYNC STROBE FAST
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, FASTDARK, true);
|
|
break;
|
|
case dLight_FireFlicker:
|
|
// fire flickering
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnFireFlicker(sector);
|
|
break;
|
|
|
|
case dScroll_EastLavaDamage:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, FASTDARK, false);
|
|
sector->special |= dScroll_EastLavaDamage;
|
|
break;
|
|
case sLight_Strobe_Hurt:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SpawnStrobeFlash(sector, STROBEBRIGHT, FASTDARK, false);
|
|
sector->special |= sLight_Strobe_Hurt;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// P_SpawnZDoomSectorSpecial
|
|
// After the map has been loaded, scan for specials
|
|
// that spawn thinkers.
|
|
// Only runs for maps in ZDoom (Doom in Hexen) format.
|
|
//
|
|
|
|
void P_SpawnZDoomSectorSpecial(sector_t* sector)
|
|
{
|
|
if (!sector->special)
|
|
return;
|
|
|
|
P_SpawnZDoomGeneralizedSpecials(sector);
|
|
|
|
sector->special &= 0xff;
|
|
|
|
P_SpawnZDoomLights(sector);
|
|
|
|
switch (sector->special)
|
|
{
|
|
case dScroll_EastLavaDamage:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 5, 32, 0, SECF_DMGTERRAINFX | SECF_DMGUNBLOCKABLE);
|
|
new DScroller(DScroller::sc_carry, -4, 0, -1, sector - sectors, 0);
|
|
break;
|
|
case sLight_Strobe_Hurt:
|
|
case dDamage_Nukage:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 5, 32, 0, 0);
|
|
sector->special = 0;
|
|
break;
|
|
case dDamage_Hellslime:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 10, 32, 0, 0);
|
|
sector->special = 0;
|
|
break;
|
|
case dLight_Strobe_Hurt:
|
|
case dDamage_SuperHellslime:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 20, 32, 5, 0);
|
|
sector->special = 0;
|
|
break;
|
|
case dDamage_End:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 20, 32, 0,
|
|
SECF_ENDGODMODE | SECF_ENDLEVEL | SECF_DMGUNBLOCKABLE);
|
|
//sector->special = 0;
|
|
break;
|
|
case Damage_InstantDeath:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 10000, 1, 0, SECF_DMGUNBLOCKABLE);
|
|
sector->special = 0;
|
|
break;
|
|
case hDamage_Sludge:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 4, 32, 0, 0);
|
|
sector->special = 0;
|
|
break;
|
|
case dDamage_LavaWimpy:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 5, 32, 0, SECF_DMGTERRAINFX | SECF_DMGUNBLOCKABLE);
|
|
sector->special = 0;
|
|
break;
|
|
case dDamage_LavaHefty:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 8, 32, 0, SECF_DMGTERRAINFX | SECF_DMGUNBLOCKABLE);
|
|
sector->special = 0;
|
|
break;
|
|
case sDamage_Hellslime:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 2, 32, 0, SECF_HAZARD);
|
|
sector->special = 0;
|
|
break;
|
|
case sDamage_SuperHellslime:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, 4, 32, 0, SECF_HAZARD);
|
|
sector->special = 0;
|
|
break;
|
|
case Sector_Heal:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
P_SetupSectorDamage(sector, -1, 32, 0, 0);
|
|
sector->special = 0;
|
|
break;
|
|
case dSector_DoorCloseIn30:
|
|
if (!serverside)
|
|
break;
|
|
// DOOR CLOSE IN 30 SECONDS
|
|
P_SpawnDoorCloseIn30(sector);
|
|
break;
|
|
|
|
case dSector_DoorRaiseIn5Mins:
|
|
if (!serverside)
|
|
break;
|
|
// DOOR RAISE IN 5 MINUTES
|
|
P_SpawnDoorRaiseIn5Mins(sector);
|
|
break;
|
|
case dSector_LowFriction:
|
|
sector->friction = FRICTION_LOW;
|
|
sector->movefactor = 0x269;
|
|
sector->flags |= SECF_FRICTION;
|
|
break;
|
|
case Sector_Hidden:
|
|
sector->flags |= SECF_HIDDEN;
|
|
sector->special = 0;
|
|
break;
|
|
case Sky2:
|
|
sector->sky = PL_SKYFLAT;
|
|
break;
|
|
default:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
// [RH] Try for normal Hexen scroller
|
|
if (sector->special >= Scroll_North_Slow &&
|
|
sector->special <= Scroll_SouthWest_Fast)
|
|
{
|
|
static signed char 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;
|
|
int dx = hexenScrollies[i][0] * (FRACUNIT / 2);
|
|
int dy = hexenScrollies[i][1] * (FRACUNIT / 2);
|
|
|
|
new DScroller(DScroller::sc_floor, dx, dy, -1, sector - sectors, 0);
|
|
// Hexen scrolling floors cause the player to move
|
|
// faster than they scroll. I do this just for compatibility
|
|
// with Hexen and recommend using Killough's more-versatile
|
|
// scrollers instead.
|
|
dx = FixedMul(-dx, CARRYFACTOR * 2);
|
|
dy = FixedMul(dy, CARRYFACTOR * 2);
|
|
new DScroller(DScroller::sc_carry, dx, dy, -1, sector - sectors, 0);
|
|
}
|
|
else if (sector->special >= Scroll_Carry_East5 &&
|
|
sector->special <= Scroll_Carry_East35)
|
|
{ // Heretic scroll special
|
|
// Only east scrollers also scroll the texture
|
|
fixed_t dx =
|
|
FixedDiv((1 << (sector->special - Scroll_Carry_East5)) << FRACBITS, 2);
|
|
new DScroller(DScroller::sc_floor, dx, 0, -1, sector - sectors, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void P_SpawnZDoomExtra(int i)
|
|
{
|
|
switch (lines[i].special)
|
|
{
|
|
int s;
|
|
sector_t* sec;
|
|
|
|
// killough 3/7/98:
|
|
// support for drawn heights coming from different sector
|
|
case Transfer_Heights:
|
|
sec = sides[*lines[i].sidenum].sector;
|
|
DPrintFmt("Sector tagged {}: TransferHeights \n", sec->tag);
|
|
if (sv_forcewater)
|
|
{
|
|
sec->waterzone = 2;
|
|
}
|
|
if (lines[i].args[1] & 2)
|
|
{
|
|
sec->MoreFlags |= SECF_FAKEFLOORONLY;
|
|
}
|
|
if (lines[i].args[1] & 4)
|
|
{
|
|
sec->MoreFlags |= SECF_CLIPFAKEPLANES;
|
|
DPrintFmt("Sector tagged {}: CLIPFAKEPLANES \n", sec->tag);
|
|
}
|
|
if (lines[i].args[1] & 8)
|
|
{
|
|
sec->waterzone = 1;
|
|
DPrintFmt("Sector tagged {}: Sets waterzone=1 \n", sec->tag);
|
|
}
|
|
if (lines[i].args[1] & 16)
|
|
{
|
|
sec->MoreFlags |= SECF_IGNOREHEIGHTSEC;
|
|
DPrintFmt("Sector tagged {}: IGNOREHEIGHTSEC \n", sec->tag);
|
|
}
|
|
if (lines[i].args[1] & 32)
|
|
{
|
|
sec->MoreFlags |= SECF_NOFAKELIGHT;
|
|
DPrintFmt("Sector tagged {}: NOFAKELIGHTS \n", sec->tag);
|
|
}
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
{
|
|
sectors[s].heightsec = sec;
|
|
}
|
|
|
|
DPrintFmt("Sector tagged {}: MoreFlags: {} \n", sec->tag, sec->MoreFlags);
|
|
break;
|
|
|
|
// killough 3/16/98: Add support for setting
|
|
// floor lighting independently (e.g. lava)
|
|
case Transfer_FloorLight:
|
|
sec = sides[*lines[i].sidenum].sector;
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
sectors[s].floorlightsec = sec;
|
|
break;
|
|
|
|
// killough 4/11/98: Add support for setting
|
|
// ceiling lighting independently
|
|
case Transfer_CeilingLight:
|
|
sec = sides[*lines[i].sidenum].sector;
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
sectors[s].ceilinglightsec = sec;
|
|
break;
|
|
|
|
// [RH] ZDoom Static_Init settings
|
|
case Static_Init:
|
|
switch (lines[i].args[1])
|
|
{
|
|
case Init_Gravity: {
|
|
if (IgnoreSpecial)
|
|
break;
|
|
float grav =
|
|
((float)P_AproxDistance(lines[i].dx, lines[i].dy)) / (FRACUNIT * 100.0f);
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
sectors[s].gravity = grav;
|
|
}
|
|
break;
|
|
|
|
// case Init_Color:
|
|
// handled in P_LoadSideDefs2()
|
|
|
|
case Init_Damage: {
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
int damage = P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS;
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
{
|
|
sectors[s].damageamount = damage;
|
|
sectors[s].damageinterval = 32;
|
|
sectors[s].leakrate = 0;
|
|
sectors[s].mod = MOD_UNKNOWN;
|
|
}
|
|
}
|
|
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:
|
|
for (s = -1; (s = P_FindSectorFromTag(lines[i].args[0], s)) >= 0;)
|
|
sectors[s].sky = (i + 1) | PL_SKYFLAT;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Initialize the scrollers
|
|
void P_SpawnZDoomScroller(line_t* l, int i)
|
|
{
|
|
fixed_t dx = 0; // direction and speed of scrolling
|
|
fixed_t dy = 0;
|
|
int control = -1, accel = 0; // no control sector or acceleration
|
|
int special = l->special;
|
|
|
|
// killough 3/7/98: Types 245-249 are same as 250-254 except that the
|
|
// first side's sector's heights cause scrolling when they change, and
|
|
// this linedef controls the direction and speed of the scrolling. The
|
|
// most complicated linedef since donuts, but powerful :)
|
|
//
|
|
// killough 3/15/98: Add acceleration. Types 214-218 are the same but
|
|
// are accelerative.
|
|
|
|
if (special == 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 = sides[*l->sidenum].sector - sectors;
|
|
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->dx >> SCROLL_SHIFT;
|
|
dy = l->dy >> SCROLL_SHIFT;
|
|
}
|
|
else
|
|
{
|
|
// The speed and direction are parameters to the special.
|
|
dx = (l->args[3] - 128) * (FRACUNIT / 32);
|
|
dy = (l->args[4] - 128) * (FRACUNIT / 32);
|
|
}
|
|
}
|
|
|
|
switch (special)
|
|
{
|
|
int s;
|
|
|
|
case Scroll_Ceiling:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
new DScroller(DScroller::sc_ceiling, -dx, dy, control, s, accel);
|
|
break;
|
|
|
|
case Scroll_Floor:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
if (l->args[2] != 1)
|
|
// scroll the floor
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
new DScroller(DScroller::sc_floor, -dx, dy, control, s, accel);
|
|
|
|
if (l->args[2] > 0)
|
|
{
|
|
// carry objects on the floor
|
|
dx = FixedMul(dx, CARRYFACTOR);
|
|
dy = FixedMul(dy, CARRYFACTOR);
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
new DScroller(DScroller::sc_carry, dx, dy, control, s, accel);
|
|
}
|
|
break;
|
|
|
|
// killough 3/1/98: scroll wall according to linedef
|
|
// (same direction and speed as scrolling floors)
|
|
case Scroll_Texture_Model:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
for (s = -1; (s = P_FindLineFromID(l->args[0], s)) >= 0;)
|
|
if (s != i)
|
|
new DScroller(dx, dy, lines + s, control, accel);
|
|
break;
|
|
|
|
case Scroll_Texture_Offsets:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
if (l->args[0] == 0) // [Blair] Hack in generalized scrollers.
|
|
{
|
|
// killough 3/2/98: scroll according to sidedef offsets
|
|
s = lines[i].sidenum[0];
|
|
new DScroller(DScroller::sc_side, -sides[s].textureoffset, sides[s].rowoffset,
|
|
-1, s, accel);
|
|
}
|
|
else
|
|
{
|
|
if (l->id == 0)
|
|
PrintFmt(PRINT_HIGH, "Line {} is missing a tag!", i);
|
|
|
|
if (l->args[0] > 1024)
|
|
control = sides[*l->sidenum].sector - sectors;
|
|
|
|
if (l->args[0] == 1026)
|
|
accel = 1;
|
|
|
|
s = lines[i].sidenum[0];
|
|
dx = -sides[s].textureoffset / 8;
|
|
dy = sides[s].rowoffset / 8;
|
|
for (s = -1; (s = P_FindLineFromLineTag(l, s)) >= 0;)
|
|
if (s != i)
|
|
new DScroller(DScroller::sc_side, dx, dy, control,
|
|
lines[s].sidenum[0], accel);
|
|
}
|
|
break;
|
|
|
|
case Scroll_Texture_Left:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
new DScroller(DScroller::sc_side, l->args[0] * (FRACUNIT / 64), 0, -1,
|
|
lines[i].sidenum[0], accel);
|
|
break;
|
|
|
|
case Scroll_Texture_Right:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
new DScroller(DScroller::sc_side, l->args[0] * (-FRACUNIT / 64), 0, -1,
|
|
lines[i].sidenum[0], accel);
|
|
break;
|
|
|
|
case Scroll_Texture_Up:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
new DScroller(DScroller::sc_side, 0, l->args[0] * (FRACUNIT / 64), -1,
|
|
lines[i].sidenum[0], accel);
|
|
break;
|
|
|
|
case Scroll_Texture_Down:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
new DScroller(DScroller::sc_side, 0, l->args[0] * (-FRACUNIT / 64), -1,
|
|
lines[i].sidenum[0], accel);
|
|
break;
|
|
|
|
case Scroll_Texture_Both:
|
|
if (IgnoreSpecial)
|
|
break;
|
|
|
|
if (l->args[0] == 0)
|
|
{
|
|
dx = (l->args[1] - l->args[2]) * (FRACUNIT / 64);
|
|
dy = (l->args[4] - l->args[3]) * (FRACUNIT / 64);
|
|
new DScroller(DScroller::sc_side, dx, dy, -1, lines[i].sidenum[0], accel);
|
|
}
|
|
break;
|
|
default:
|
|
l->special = special;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void P_SpawnZDoomFriction(line_t* l)
|
|
{
|
|
if (l->special == Sector_SetFriction)
|
|
{
|
|
int value;
|
|
|
|
if (l->args[1])
|
|
value = l->args[1] <= 200 ? l->args[1] : 200;
|
|
else
|
|
value = P_AproxDistance(l->dx, l->dy) >> FRACBITS;
|
|
|
|
P_ApplySectorFriction(l->args[0], value, false);
|
|
|
|
l->special = 0;
|
|
}
|
|
}
|
|
|
|
void P_SpawnZDoomPusher(line_t* l)
|
|
{
|
|
int s;
|
|
|
|
switch (l->special)
|
|
{
|
|
case Sector_SetWind: // wind
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
new DPusher(DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2],
|
|
NULL, s);
|
|
break;
|
|
case Sector_SetCurrent: // current
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
new DPusher(DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2],
|
|
NULL, s);
|
|
break;
|
|
case PointPush_SetForce: // push/pull
|
|
if (l->args[0])
|
|
{ // [RH] Find thing by sector
|
|
for (s = -1; (s = P_FindSectorFromTag(l->args[0], s)) >= 0;)
|
|
{
|
|
AActor* thing = P_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)
|
|
new DPusher(DPusher::p_push, l->args[3] ? l : NULL, l->args[2], 0,
|
|
thing, s);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // [RH] Find thing by tid
|
|
AActor* thing = NULL;
|
|
|
|
while ((thing = AActor::FindByTID(thing, l->args[1])))
|
|
{
|
|
if (thing->type == MT_PUSH || thing->type == MT_PULL)
|
|
new DPusher(DPusher::p_push, l->args[3] ? l : NULL, l->args[2], 0,
|
|
thing, thing->subsector->sector - sectors);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void P_PostProcessZDoomSidedefSpecial(side_t* sd, mapsidedef_t* msd, sector_t* sec,
|
|
int i)
|
|
{
|
|
switch (sd->special)
|
|
{
|
|
case Transfer_Heights: // variable colormap via 242 linedef
|
|
// [RH] The colormap num we get here isn't really a colormap,
|
|
// but a packed ARGB word for blending, so we also allow
|
|
// the blend to be specified directly by the texture names
|
|
// instead of figuring something out from the colormap.
|
|
P_SetTransferHeightBlends(sd, msd);
|
|
break;
|
|
|
|
case Static_Init:
|
|
// [RH] Set sector color and fog
|
|
// upper "texture" is light color
|
|
// lower "texture" is fog color
|
|
{
|
|
unsigned int color = 0xffffff, fog = 0x000000;
|
|
|
|
SetTextureNoErr(&sd->bottomtexture, &fog, msd->bottomtexture);
|
|
SetTextureNoErr(&sd->toptexture, &color, msd->toptexture);
|
|
sd->midtexture = R_TextureNumForName(msd->midtexture);
|
|
|
|
if (fog != 0x000000 || color != 0xffffff)
|
|
{
|
|
dyncolormap_t* colormap =
|
|
GetSpecialLights(((argb_t)color).getr(), ((argb_t)color).getg(),
|
|
((argb_t)color).getb(), ((argb_t)fog).getr(),
|
|
((argb_t)fog).getg(), ((argb_t)fog).getb());
|
|
|
|
for (sector_t& sector : R_GetSectors())
|
|
{
|
|
if (sector.tag == sd->tag)
|
|
sector.colormap = colormap;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
case TranslucentLine: // killough 4/11/98: apply translucency to 2s
|
|
normal texture sd->midtexture = strncasecmp("TRANMAP", msd->midtexture, 8) ?
|
|
(sd->special = W_CheckNumForName(msd->midtexture)) < 0 ||
|
|
W_LumpLength(sd->special) != 65536 ?
|
|
sd->special=0, R_TextureNumForName(msd->midtexture) :
|
|
(sd->special++, 0) : (sd->special=0);
|
|
sd->toptexture = R_TextureNumForName(msd->toptexture);
|
|
sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
|
|
break;
|
|
*/
|
|
default: // normal cases
|
|
sd->midtexture = R_TextureNumForName(msd->midtexture);
|
|
sd->toptexture = R_TextureNumForName(msd->toptexture);
|
|
sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool P_ExecuteZDoomLineSpecial(int special, short* args, line_t* line, int side,
|
|
AActor* mo)
|
|
{
|
|
return LineSpecials[special](line, mo, args[0], args[1], args[2], args[3],
|
|
args[4]);
|
|
}
|
|
|
|
unsigned int P_TranslateZDoomLineFlags(const unsigned int flags)
|
|
{
|
|
unsigned int result = flags & 0x1ff;
|
|
|
|
static constexpr unsigned int spac_to_flags[8] = {ML_SPAC_CROSS,
|
|
ML_SPAC_USE,
|
|
ML_SPAC_MCROSS,
|
|
ML_SPAC_IMPACT,
|
|
ML_SPAC_PUSH,
|
|
ML_SPAC_PCROSS,
|
|
ML_SPAC_USE | ML_PASSUSE,
|
|
ML_SPAC_IMPACT | ML_SPAC_PCROSS};
|
|
|
|
// from zdoom-in-hexen to Odamex
|
|
|
|
result |= spac_to_flags[GET_HSPAC(flags)];
|
|
|
|
if (flags & HML_REPEATSPECIAL)
|
|
result |= ML_REPEATSPECIAL;
|
|
|
|
if (flags & ZML_BLOCKPLAYERS)
|
|
result |= ML_BLOCKPLAYERS;
|
|
|
|
if (flags & ZML_MONSTERSCANACTIVATE)
|
|
result |= ML_MONSTERSCANACTIVATE;
|
|
|
|
if (flags & ZML_BLOCKEVERYTHING)
|
|
result |= ML_BLOCKING | ML_BLOCKEVERYTHING;
|
|
|
|
return result;
|
|
}
|
|
|
|
void P_PostProcessZDoomLinedefSpecial(line_t* line)
|
|
{
|
|
switch (line->special)
|
|
{ // killough 4/11/98: handle special types
|
|
int j;
|
|
case TranslucentLine: // killough 4/11/98: translucent 2s textures
|
|
#if 0
|
|
lump = sides[*line->sidenum].special; // translucency from sidedef
|
|
if (!line->tag) // if tag==0,
|
|
line->tranlump = lump; // affect this linedef only
|
|
else
|
|
for (j=0;j<numlines;j++) // if tag!=0,
|
|
if (lines[j].tag == ld->tag) // affect all matching linedefs
|
|
lines[j].tranlump = lump;
|
|
#else
|
|
// [RH] Second arg controls how opaque it is.
|
|
if (!line->args[0])
|
|
line->lucency = (byte)line->args[1];
|
|
else
|
|
for (j = 0; j < numlines; j++)
|
|
if (lines[j].id == line->args[0])
|
|
lines[j].lucency = (byte)line->args[1];
|
|
#endif
|
|
line->special = 0;
|
|
break;
|
|
}
|
|
}
|