vkdoom_m/src/p_lights.cpp
Christoph Oelckers cd180d29c7 - block direct use of 'new' for DObjects.
This is to ensure that the Class pointer can be set right on creation. ZDoom had always depended on handling this lazily which poses some problems for the VM.
So now there is a variadic Create<classtype> function taking care of that, but to ensure that it gets used, direct access to the new operator has been blocked.

This also neccessitated making DArgs a regular object because they get created before the type system is up. Since the few uses of DArgs are easily controllable this wasn't a big issue.

- did a bit of optimization on the bots' decision making whether to pick up a health item or not.
2017-04-14 13:31:58 +02:00

1055 lines
23 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Handle Sector base lighting effects.
//
//-----------------------------------------------------------------------------
#include "templates.h"
#include "m_random.h"
#include "doomdef.h"
#include "p_local.h"
#include "p_spec.h"
#include "p_lnspec.h"
#include "doomstat.h"
#include "p_maputl.h"
#include "g_levellocals.h"
// State.
#include "r_state.h"
#include "statnums.h"
#include "serializer.h"
static FRandom pr_flicker ("Flicker");
static FRandom pr_lightflash ("LightFlash");
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
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DLighting, false, false)
DLighting::DLighting ()
{
}
DLighting::DLighting (sector_t *sector)
: DSectorEffect (sector)
{
ChangeStatNum (STAT_LIGHT);
}
//-----------------------------------------------------------------------------
//
// FIRELIGHT FLICKER
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DFireFlicker, false, false)
DFireFlicker::DFireFlicker ()
{
}
void DFireFlicker::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("count", m_Count)
("maxlight", m_MaxLight)
("minlight", m_MinLight);
}
//-----------------------------------------------------------------------------
//
// T_FireFlicker
//
//-----------------------------------------------------------------------------
void DFireFlicker::Tick ()
{
int amount;
if (--m_Count == 0)
{
amount = (pr_fireflicker() & 3) << 4;
// [RH] Shouldn't this be (m_MaxLight - amount < m_MinLight)?
if (m_Sector->lightlevel - amount < m_MinLight)
m_Sector->SetLightLevel(m_MinLight);
else
m_Sector->SetLightLevel(m_MaxLight - amount);
m_Count = 4;
}
}
//-----------------------------------------------------------------------------
//
// P_SpawnFireFlicker
//
//-----------------------------------------------------------------------------
DFireFlicker::DFireFlicker (sector_t *sector)
: DLighting (sector)
{
m_MaxLight = sector->lightlevel;
m_MinLight = sector_t::ClampLight(sector->FindMinSurroundingLight(sector->lightlevel) + 16);
m_Count = 4;
}
DFireFlicker::DFireFlicker (sector_t *sector, int upper, int lower)
: DLighting (sector)
{
m_MaxLight = sector_t::ClampLight(upper);
m_MinLight = sector_t::ClampLight(lower);
m_Count = 4;
}
//-----------------------------------------------------------------------------
//
// [RH] flickering light like Hexen's
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DFlicker, false, false)
DFlicker::DFlicker ()
{
}
void DFlicker::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("count", m_Count)
("maxlight", m_MaxLight)
("minlight", m_MinLight);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void DFlicker::Tick ()
{
if (m_Count)
{
m_Count--;
}
else if (m_Sector->lightlevel == m_MaxLight)
{
m_Sector->SetLightLevel(m_MinLight);
m_Count = (pr_flicker()&7)+1;
}
else
{
m_Sector->SetLightLevel(m_MaxLight);
m_Count = (pr_flicker()&31)+1;
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
DFlicker::DFlicker (sector_t *sector, int upper, int lower)
: DLighting (sector)
{
m_MaxLight = sector_t::ClampLight(upper);
m_MinLight = sector_t::ClampLight(lower);
sector->lightlevel = m_MaxLight;
m_Count = (pr_flicker()&64)+1;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void EV_StartLightFlickering (int tag, int upper, int lower)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
Create<DFlicker> (&level.sectors[secnum], upper, lower);
}
}
//-----------------------------------------------------------------------------
//
// BROKEN LIGHT FLASHING
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DLightFlash, false, false)
DLightFlash::DLightFlash ()
{
}
void DLightFlash::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("count", m_Count)
("maxlight", m_MaxLight)
("minlight", m_MinLight)
("maxtime", m_MaxTime)
("mintime", m_MinTime);
}
//-----------------------------------------------------------------------------
//
// T_LightFlash
// Do flashing lights.
//
//-----------------------------------------------------------------------------
void DLightFlash::Tick ()
{
if (--m_Count == 0)
{
if (m_Sector->lightlevel == m_MaxLight)
{
m_Sector->SetLightLevel(m_MinLight);
m_Count = (pr_lightflash() & m_MinTime) + 1;
}
else
{
m_Sector->SetLightLevel(m_MaxLight);
m_Count = (pr_lightflash() & m_MaxTime) + 1;
}
}
}
//-----------------------------------------------------------------------------
//
// P_SpawnLightFlash
//
//-----------------------------------------------------------------------------
DLightFlash::DLightFlash (sector_t *sector)
: DLighting (sector)
{
// Find light levels like Doom.
m_MaxLight = sector->lightlevel;
m_MinLight = sector->FindMinSurroundingLight (sector->lightlevel);
m_MaxTime = 64;
m_MinTime = 7;
m_Count = (pr_lightflash() & m_MaxTime) + 1;
}
DLightFlash::DLightFlash (sector_t *sector, int min, int max)
: DLighting (sector)
{
// Use specified light levels.
m_MaxLight = sector_t::ClampLight(max);
m_MinLight = sector_t::ClampLight(min);
m_MaxTime = 64;
m_MinTime = 7;
m_Count = (pr_lightflash() & m_MaxTime) + 1;
}
//-----------------------------------------------------------------------------
//
// STROBE LIGHT FLASHING
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DStrobe, false, false)
DStrobe::DStrobe ()
{
}
void DStrobe::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("count", m_Count)
("maxlight", m_MaxLight)
("minlight", m_MinLight)
("darktime", m_DarkTime)
("brighttime", m_BrightTime);
}
//-----------------------------------------------------------------------------
//
// T_StrobeFlash
//
//-----------------------------------------------------------------------------
void DStrobe::Tick ()
{
if (--m_Count == 0)
{
if (m_Sector->lightlevel == m_MinLight)
{
m_Sector->SetLightLevel(m_MaxLight);
m_Count = m_BrightTime;
}
else
{
m_Sector->SetLightLevel(m_MinLight);
m_Count = m_DarkTime;
}
}
}
//-----------------------------------------------------------------------------
//
// Hexen-style constructor
//
//-----------------------------------------------------------------------------
DStrobe::DStrobe (sector_t *sector, int upper, int lower, int utics, int ltics)
: DLighting (sector)
{
m_DarkTime = ltics;
m_BrightTime = utics;
m_MaxLight = sector_t::ClampLight(upper);
m_MinLight = sector_t::ClampLight(lower);
m_Count = 1; // Hexen-style is always in sync
}
//-----------------------------------------------------------------------------
//
// Doom-style constructor
//
//-----------------------------------------------------------------------------
DStrobe::DStrobe (sector_t *sector, int utics, int ltics, bool inSync)
: DLighting (sector)
{
m_DarkTime = ltics;
m_BrightTime = utics;
m_MaxLight = sector->lightlevel;
m_MinLight = sector->FindMinSurroundingLight (sector->lightlevel);
if (m_MinLight == m_MaxLight)
m_MinLight = 0;
m_Count = inSync ? 1 : (pr_strobeflash() & 7) + 1;
}
//-----------------------------------------------------------------------------
//
// Start strobing lights (usually from a trigger)
// [RH] Made it more configurable.
//
//-----------------------------------------------------------------------------
void EV_StartLightStrobing (int tag, int upper, int lower, int utics, int ltics)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sec = &level.sectors[secnum];
if (sec->lightingdata)
continue;
Create<DStrobe> (sec, upper, lower, utics, ltics);
}
}
void EV_StartLightStrobing (int tag, int utics, int ltics)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sec = &level.sectors[secnum];
if (sec->lightingdata)
continue;
Create<DStrobe> (sec, utics, ltics, false);
}
}
//-----------------------------------------------------------------------------
//
// TURN LINE'S TAG LIGHTS OFF
// [RH] Takes a tag instead of a line
//
//-----------------------------------------------------------------------------
void EV_TurnTagLightsOff (int tag)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sector = &level.sectors[secnum];
int min = sector->lightlevel;
for (auto ln : sector->Lines)
{
sector_t *tsec = getNextSector (ln, sector);
if (!tsec)
continue;
if (tsec->lightlevel < min)
min = tsec->lightlevel;
}
sector->SetLightLevel(min);
}
}
//-----------------------------------------------------------------------------
//
// TURN LINE'S TAG LIGHTS ON
// [RH] Takes a tag instead of a line
//
//-----------------------------------------------------------------------------
void EV_LightTurnOn (int tag, int bright)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sector = &level.sectors[secnum];
int tbright = bright; //jff 5/17/98 search for maximum PER sector
// bright = -1 means to search ([RH] Not 0)
// for highest light level
// surrounding sector
if (bright < 0)
{
for (auto ln : sector->Lines)
{
sector_t *temp = getNextSector(ln, sector);
if (!temp)
continue;
if (temp->lightlevel > tbright)
tbright = temp->lightlevel;
}
}
sector->SetLightLevel(tbright);
//jff 5/17/98 unless compatibility optioned
//then maximum near ANY tagged sector
if (i_compatflags & COMPATF_LIGHT)
{
bright = tbright;
}
}
}
//-----------------------------------------------------------------------------
//
// killough 10/98
//
// EV_LightTurnOnPartway
//
// Turn sectors tagged to line lights on to specified or max neighbor level
//
// Passed the tag of sector(s) to light and a light level fraction between 0 and 1.
// Sets the light to min on 0, max on 1, and interpolates in-between.
// Used for doors with gradual lighting effects.
//
//-----------------------------------------------------------------------------
void EV_LightTurnOnPartway (int tag, double frac)
{
frac = clamp(frac, 0., 1.);
// Search all sectors for ones with same tag as activating line
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *temp, *sector = &level.sectors[secnum];
int bright = 0, min = sector->lightlevel;
for (auto ln : sector->Lines)
{
if ((temp = getNextSector (ln, sector)) != nullptr)
{
if (temp->lightlevel > bright)
{
bright = temp->lightlevel;
}
if (temp->lightlevel < min)
{
min = temp->lightlevel;
}
}
}
sector->SetLightLevel(int(frac * bright + (1 - frac) * min));
}
}
//-----------------------------------------------------------------------------
//
// [RH] New function to adjust tagged sectors' light levels
// by a relative amount. Light levels are clipped to
// be within range for sector_t::lightlevel.
//
//-----------------------------------------------------------------------------
void EV_LightChange (int tag, int value)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
level.sectors[secnum].SetLightLevel(level.sectors[secnum].lightlevel + value);
}
}
//-----------------------------------------------------------------------------
//
// Spawn glowing light
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DGlow, false, false)
DGlow::DGlow ()
{
}
void DGlow::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("direction", m_Direction)
("maxlight", m_MaxLight)
("minlight", m_MinLight);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void DGlow::Tick ()
{
int newlight = m_Sector->lightlevel;
switch (m_Direction)
{
case -1:
// DOWN
newlight -= GLOWSPEED;
if (newlight <= m_MinLight)
{
newlight += GLOWSPEED;
m_Direction = 1;
}
break;
case 1:
// UP
newlight += GLOWSPEED;
if (newlight >= m_MaxLight)
{
newlight -= GLOWSPEED;
m_Direction = -1;
}
break;
}
m_Sector->SetLightLevel(newlight);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
DGlow::DGlow (sector_t *sector)
: DLighting (sector)
{
m_MinLight = sector->FindMinSurroundingLight (sector->lightlevel);
m_MaxLight = sector->lightlevel;
m_Direction = -1;
}
//-----------------------------------------------------------------------------
//
// [RH] More glowing light, this time appropriate for Hexen-ish uses.
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DGlow2, false, false)
DGlow2::DGlow2 ()
{
}
void DGlow2::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("end", m_End)
("maxtics", m_MaxTics)
("oneshot", m_OneShot)
("start", m_Start)
("tics", m_Tics);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void DGlow2::Tick ()
{
if (m_Tics++ >= m_MaxTics)
{
if (m_OneShot)
{
m_Sector->SetLightLevel(m_End);
Destroy ();
return;
}
else
{
int temp = m_Start;
m_Start = m_End;
m_End = temp;
m_Tics -= m_MaxTics;
}
}
m_Sector->SetLightLevel(((m_End - m_Start) * m_Tics) / m_MaxTics + m_Start);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
DGlow2::DGlow2 (sector_t *sector, int start, int end, int tics, bool oneshot)
: DLighting (sector)
{
m_Start = sector_t::ClampLight(start);
m_End = sector_t::ClampLight(end);
m_MaxTics = tics;
m_Tics = -1;
m_OneShot = oneshot;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void EV_StartLightGlowing (int tag, int upper, int lower, int tics)
{
int secnum;
// If tics is non-positive, then we can't really do anything.
if (tics <= 0)
{
return;
}
if (upper < lower)
{
int temp = upper;
upper = lower;
lower = temp;
}
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sec = &level.sectors[secnum];
if (sec->lightingdata)
continue;
Create<DGlow2> (sec, upper, lower, tics, false);
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void EV_StartLightFading (int tag, int value, int tics)
{
int secnum;
FSectorTagIterator it(tag);
while ((secnum = it.Next()) >= 0)
{
sector_t *sec = &level.sectors[secnum];
if (sec->lightingdata)
continue;
if (tics <= 0)
{
sec->SetLightLevel(value);
}
else
{
// No need to fade if lightlevel is already at desired value.
if (sec->lightlevel == value)
continue;
Create<DGlow2> (sec, sec->lightlevel, value, tics, true);
}
}
}
//-----------------------------------------------------------------------------
//
// [RH] Phased lighting ala Hexen, but implemented without the help of the Hexen source
// The effect is a little different, but close enough, I feel.
//
//-----------------------------------------------------------------------------
IMPLEMENT_CLASS(DPhased, false, false)
DPhased::DPhased ()
{
}
void DPhased::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("baselevel", m_BaseLevel)
("phase", m_Phase);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void DPhased::Tick ()
{
const int steps = 12;
if (m_Phase < steps)
m_Sector->SetLightLevel( ((255 - m_BaseLevel) * m_Phase) / steps + m_BaseLevel);
else if (m_Phase < 2*steps)
m_Sector->SetLightLevel( ((255 - m_BaseLevel) * (2*steps - m_Phase - 1) / steps
+ m_BaseLevel));
else
m_Sector->SetLightLevel(m_BaseLevel);
if (m_Phase == 0)
m_Phase = 63;
else
m_Phase--;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
int DPhased::PhaseHelper (sector_t *sector, int index, int light, sector_t *prev)
{
if (!sector || sector->validcount == validcount)
{
return index;
}
else
{
DPhased *l;
int baselevel = sector->lightlevel ? sector->lightlevel : light;
sector->validcount = validcount;
if (index == 0)
{
l = this;
m_BaseLevel = baselevel;
}
else
l = Create<DPhased> (sector, baselevel);
int numsteps = PhaseHelper (sector->NextSpecialSector (
sector->special == LightSequenceSpecial1 ?
LightSequenceSpecial2 : LightSequenceSpecial1, prev),
index + 1, l->m_BaseLevel, sector);
l->m_Phase = ((numsteps - index - 1) * 64) / numsteps;
sector->special = 0;
return numsteps;
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
DPhased::DPhased (sector_t *sector, int baselevel)
: DLighting (sector)
{
m_BaseLevel = baselevel;
}
DPhased::DPhased (sector_t *sector)
: DLighting (sector)
{
validcount++;
PhaseHelper (sector, 0, 0, NULL);
}
DPhased::DPhased (sector_t *sector, int baselevel, int phase)
: DLighting (sector)
{
m_BaseLevel = baselevel;
m_Phase = phase;
}
//============================================================================
//
// EV_StopLightEffect
//
// Stops a lighting effect that is currently running in a sector.
//
//============================================================================
void EV_StopLightEffect (int tag)
{
TThinkerIterator<DLighting> iterator;
DLighting *effect;
while ((effect = iterator.Next()) != NULL)
{
if (tagManager.SectorHasTag(effect->GetSector(), tag))
{
effect->Destroy();
}
}
}
void P_SpawnLights(sector_t *sector)
{
switch (sector->special)
{
case Light_Phased:
Create<DPhased>(sector, 48, 63 - (sector->lightlevel & 63));
break;
// [RH] Hexen-like phased lighting
case LightSequenceStart:
Create<DPhased>(sector);
break;
case dLight_Flicker:
Create<DLightFlash>(sector);
break;
case dLight_StrobeFast:
Create<DStrobe>(sector, STROBEBRIGHT, FASTDARK, false);
break;
case dLight_StrobeSlow:
Create<DStrobe>(sector, STROBEBRIGHT, SLOWDARK, false);
break;
case dLight_Strobe_Hurt:
Create<DStrobe>(sector, STROBEBRIGHT, FASTDARK, false);
break;
case dLight_Glow:
Create<DGlow>(sector);
break;
case dLight_StrobeSlowSync:
Create<DStrobe>(sector, STROBEBRIGHT, SLOWDARK, true);
break;
case dLight_StrobeFastSync:
Create<DStrobe>(sector, STROBEBRIGHT, FASTDARK, true);
break;
case dLight_FireFlicker:
Create<DFireFlicker>(sector);
break;
case dScroll_EastLavaDamage:
Create<DStrobe>(sector, STROBEBRIGHT, FASTDARK, false);
break;
case sLight_Strobe_Hurt:
Create<DStrobe>(sector, STROBEBRIGHT, FASTDARK, false);
break;
default:
break;
}
}