2014-03-15 16:59:03 +00:00
|
|
|
// SONIC ROBO BLAST 2
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
2020-02-19 22:08:45 +00:00
|
|
|
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
2014-03-15 16:59:03 +00:00
|
|
|
//
|
|
|
|
// This program is free software distributed under the
|
|
|
|
// terms of the GNU General Public License, version 2.
|
|
|
|
// See the 'LICENSE' file for more details.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// \file p_lights.c
|
|
|
|
/// \brief Sector lighting effects
|
|
|
|
/// Fire flicker, light flash, strobe flash, lightning flash, glow, and fade.
|
|
|
|
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "r_state.h"
|
|
|
|
#include "z_zone.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "d_netcmd.h"
|
|
|
|
|
|
|
|
/** Removes any active lighting effects in a sector.
|
|
|
|
*
|
|
|
|
* \param sector The sector to remove effects from.
|
|
|
|
*/
|
2018-09-09 03:44:29 +00:00
|
|
|
void P_RemoveLighting(sector_t *sector)
|
2014-03-15 16:59:03 +00:00
|
|
|
{
|
|
|
|
if (sector->lightingdata)
|
|
|
|
{
|
|
|
|
// The thinker is the first member in all the lighting action structs,
|
|
|
|
// so just let the thinker get freed, and that will free the whole
|
|
|
|
// structure.
|
|
|
|
P_RemoveThinker(&((elevator_t *)sector->lightingdata)->thinker);
|
|
|
|
sector->lightingdata = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
// FIRELIGHT FLICKER
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
/** Thinker function for fire flicker.
|
|
|
|
*
|
|
|
|
* \param flick Action structure for this effect.
|
|
|
|
* \sa P_SpawnAdjustableFireFlicker
|
|
|
|
*/
|
|
|
|
void T_FireFlicker(fireflicker_t *flick)
|
|
|
|
{
|
|
|
|
INT16 amount;
|
|
|
|
|
|
|
|
if (--flick->count)
|
|
|
|
return;
|
|
|
|
|
2016-03-27 14:33:15 +00:00
|
|
|
amount = (INT16)((UINT8)(P_RandomByte() & 3) * 16);
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
if (flick->sector->lightlevel - amount < flick->minlight)
|
|
|
|
flick->sector->lightlevel = (INT16)flick->minlight;
|
|
|
|
else
|
|
|
|
flick->sector->lightlevel = (INT16)((INT16)flick->maxlight - amount);
|
|
|
|
|
|
|
|
flick->count = flick->resetcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Spawns an adjustable fire flicker effect in a sector.
|
|
|
|
*
|
|
|
|
* \param minsector Sector whose light level is used as the darkest.
|
|
|
|
* \param maxsector Sector whose light level is used as the brightest,
|
|
|
|
* and also the target sector for the effect.
|
|
|
|
* \param length Four times the number of tics between flickers.
|
|
|
|
* \sa T_FireFlicker
|
|
|
|
*/
|
|
|
|
fireflicker_t *P_SpawnAdjustableFireFlicker(sector_t *minsector, sector_t *maxsector, INT32 length)
|
|
|
|
{
|
|
|
|
fireflicker_t *flick;
|
|
|
|
|
|
|
|
P_RemoveLighting(maxsector); // out with the old, in with the new
|
|
|
|
flick = Z_Calloc(sizeof (*flick), PU_LEVSPEC, NULL);
|
|
|
|
|
2019-04-20 20:37:19 +00:00
|
|
|
P_AddThinker(THINK_MAIN, &flick->thinker);
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker;
|
|
|
|
flick->sector = maxsector;
|
|
|
|
flick->maxlight = maxsector->lightlevel;
|
|
|
|
flick->minlight = minsector->lightlevel;
|
|
|
|
if (flick->minlight > flick->maxlight)
|
|
|
|
{
|
|
|
|
// You mixed them up, you dummy.
|
|
|
|
INT32 oops = flick->minlight;
|
|
|
|
flick->minlight = flick->maxlight;
|
|
|
|
flick->maxlight = oops;
|
|
|
|
}
|
|
|
|
flick->count = flick->resetcount = length/4;
|
|
|
|
maxsector->lightingdata = flick;
|
|
|
|
|
|
|
|
// input bounds checking and stuff
|
|
|
|
if (!flick->resetcount)
|
|
|
|
flick->resetcount = 1;
|
|
|
|
if (flick->minlight == flick->maxlight)
|
|
|
|
{
|
|
|
|
if (flick->minlight > 0)
|
|
|
|
flick->minlight--;
|
|
|
|
if (flick->maxlight < 255)
|
|
|
|
flick->maxlight++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flick;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// LIGHTNING FLASH EFFECT
|
|
|
|
//
|
|
|
|
|
|
|
|
/** Thinker function for a lightning flash storm effect.
|
|
|
|
*
|
|
|
|
* \param flash The effect being considered.
|
|
|
|
* \sa P_SpawnLightningFlash
|
|
|
|
*/
|
|
|
|
void T_LightningFlash(lightflash_t *flash)
|
|
|
|
{
|
|
|
|
flash->sector->lightlevel -= 4;
|
|
|
|
|
|
|
|
if (flash->sector->lightlevel <= flash->minlight)
|
|
|
|
{
|
|
|
|
flash->sector->lightlevel = (INT16)flash->minlight;
|
|
|
|
P_RemoveLighting(flash->sector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Spawns a one-time lightning flash.
|
|
|
|
*
|
|
|
|
* \param sector Sector to light up.
|
|
|
|
* \sa T_LightningFlash
|
|
|
|
*/
|
|
|
|
void P_SpawnLightningFlash(sector_t *sector)
|
|
|
|
{
|
|
|
|
INT32 minlight;
|
|
|
|
lightflash_t *flash;
|
|
|
|
|
|
|
|
minlight = sector->lightlevel;
|
|
|
|
|
|
|
|
if (sector->lightingdata)
|
|
|
|
{
|
|
|
|
if (((lightflash_t *)sector->lightingdata)->thinker.function.acp1
|
|
|
|
== (actionf_p1)T_LightningFlash)
|
|
|
|
{
|
|
|
|
// lightning was already flashing in this sector
|
|
|
|
// save the original light level value
|
|
|
|
minlight = ((lightflash_t *)sector->lightingdata)->minlight;
|
|
|
|
}
|
|
|
|
|
|
|
|
P_RemoveThinker(&((elevator_t *)sector->lightingdata)->thinker);
|
|
|
|
}
|
|
|
|
|
|
|
|
sector->lightingdata = NULL;
|
|
|
|
|
|
|
|
flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL);
|
|
|
|
|
2019-04-20 20:37:19 +00:00
|
|
|
P_AddThinker(THINK_MAIN, &flash->thinker);
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
flash->thinker.function.acp1 = (actionf_p1)T_LightningFlash;
|
|
|
|
flash->sector = sector;
|
|
|
|
flash->maxlight = 255;
|
|
|
|
flash->minlight = minlight;
|
|
|
|
sector->lightlevel = (INT16)flash->maxlight;
|
|
|
|
|
|
|
|
sector->lightingdata = flash;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// STROBE LIGHT FLASHING
|
|
|
|
//
|
|
|
|
|
|
|
|
/** Thinker function for strobe light flashing.
|
|
|
|
*
|
|
|
|
* \param flash The effect under consideration.
|
|
|
|
* \sa P_SpawnAdjustableStrobeFlash
|
|
|
|
*/
|
|
|
|
void T_StrobeFlash(strobe_t *flash)
|
|
|
|
{
|
|
|
|
if (--flash->count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (flash->sector->lightlevel == flash->minlight)
|
|
|
|
{
|
|
|
|
flash->sector->lightlevel = (INT16)flash->maxlight;
|
|
|
|
flash->count = flash->brighttime;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
flash->sector->lightlevel = (INT16)flash->minlight;
|
|
|
|
flash->count = flash->darktime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Spawns an adjustable strobe light effect in a sector.
|
|
|
|
*
|
|
|
|
* \param minsector Sector whose light level is used as the darkest.
|
|
|
|
* \param maxsector Sector whose light level is used as the brightest,
|
|
|
|
* and also the target sector for the effect.
|
|
|
|
* \param darktime Time in tics for the light to be dark.
|
|
|
|
* \param brighttime Time in tics for the light to be bright.
|
|
|
|
* \param inSync If true, the effect will be kept in sync
|
|
|
|
* with other effects of this type, provided
|
|
|
|
* they use the same time values, and
|
|
|
|
* provided this function is called at level
|
|
|
|
* load. Otherwise, the starting state of
|
|
|
|
* the strobe flash is random.
|
|
|
|
* \sa T_StrobeFlash
|
|
|
|
*/
|
|
|
|
strobe_t *P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector, INT32 darktime, INT32 brighttime, boolean inSync)
|
|
|
|
{
|
|
|
|
strobe_t *flash;
|
|
|
|
|
|
|
|
P_RemoveLighting(maxsector); // out with the old, in with the new
|
|
|
|
flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL);
|
|
|
|
|
2019-04-20 20:37:19 +00:00
|
|
|
P_AddThinker(THINK_MAIN, &flash->thinker);
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
flash->sector = maxsector;
|
|
|
|
flash->darktime = darktime;
|
|
|
|
flash->brighttime = brighttime;
|
|
|
|
flash->thinker.function.acp1 = (actionf_p1)T_StrobeFlash;
|
|
|
|
flash->maxlight = maxsector->lightlevel;
|
|
|
|
flash->minlight = minsector->lightlevel;
|
|
|
|
|
|
|
|
if (flash->minlight > flash->maxlight)
|
|
|
|
{
|
|
|
|
// You mixed them up, you dummy.
|
|
|
|
INT32 oops = flash->minlight;
|
|
|
|
flash->minlight = flash->maxlight;
|
|
|
|
flash->maxlight = oops;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flash->minlight == flash->maxlight)
|
|
|
|
flash->minlight = 0;
|
|
|
|
|
|
|
|
if (!inSync)
|
2016-03-27 14:33:15 +00:00
|
|
|
flash->count = (P_RandomByte() & 7) + 1;
|
2014-03-15 16:59:03 +00:00
|
|
|
else
|
|
|
|
flash->count = 1;
|
|
|
|
|
|
|
|
maxsector->lightingdata = flash;
|
|
|
|
return flash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Thinker function for glowing light.
|
|
|
|
*
|
|
|
|
* \param g Action structure for this effect.
|
|
|
|
* \sa P_SpawnAdjustableGlowingLight
|
|
|
|
*/
|
|
|
|
void T_Glow(glow_t *g)
|
|
|
|
{
|
|
|
|
switch (g->direction)
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
// DOWN
|
|
|
|
g->sector->lightlevel = (INT16)(g->sector->lightlevel - (INT16)g->speed);
|
|
|
|
if (g->sector->lightlevel <= g->minlight)
|
|
|
|
{
|
|
|
|
g->sector->lightlevel = (INT16)(g->sector->lightlevel + (INT16)g->speed);
|
|
|
|
g->direction = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// UP
|
|
|
|
g->sector->lightlevel = (INT16)(g->sector->lightlevel + (INT16)g->speed);
|
|
|
|
if (g->sector->lightlevel >= g->maxlight)
|
|
|
|
{
|
|
|
|
g->sector->lightlevel = (INT16)(g->sector->lightlevel - (INT16)g->speed);
|
|
|
|
g->direction = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Spawns an adjustable glowing light effect in a sector.
|
|
|
|
*
|
|
|
|
* \param minsector Sector whose light level is used as the darkest.
|
|
|
|
* \param maxsector Sector whose light level is used as the brightest,
|
|
|
|
* and also the target sector for the effect.
|
|
|
|
* \param length The speed of the effect.
|
|
|
|
* \sa T_Glow
|
|
|
|
*/
|
|
|
|
glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length)
|
|
|
|
{
|
|
|
|
glow_t *g;
|
|
|
|
|
|
|
|
P_RemoveLighting(maxsector); // out with the old, in with the new
|
|
|
|
g = Z_Calloc(sizeof (*g), PU_LEVSPEC, NULL);
|
|
|
|
|
2019-04-20 20:37:19 +00:00
|
|
|
P_AddThinker(THINK_MAIN, &g->thinker);
|
2014-03-15 16:59:03 +00:00
|
|
|
|
|
|
|
g->sector = maxsector;
|
|
|
|
g->minlight = minsector->lightlevel;
|
|
|
|
g->maxlight = maxsector->lightlevel;
|
|
|
|
if (g->minlight > g->maxlight)
|
|
|
|
{
|
|
|
|
// You mixed them up, you dummy.
|
|
|
|
INT32 oops = g->minlight;
|
|
|
|
g->minlight = g->maxlight;
|
|
|
|
g->maxlight = oops;
|
|
|
|
}
|
|
|
|
g->thinker.function.acp1 = (actionf_p1)T_Glow;
|
|
|
|
g->direction = 1;
|
|
|
|
g->speed = length/4;
|
|
|
|
if (g->speed > (g->maxlight - g->minlight)/2) // don't make it ridiculous speed
|
|
|
|
g->speed = (g->maxlight - g->minlight)/2;
|
|
|
|
|
|
|
|
while (g->speed < 1)
|
|
|
|
{
|
|
|
|
if (g->minlight > 0)
|
|
|
|
g->minlight--;
|
|
|
|
if (g->maxlight < 255)
|
|
|
|
g->maxlight++;
|
|
|
|
|
|
|
|
g->speed = (g->maxlight - g->minlight)/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxsector->lightingdata = g;
|
|
|
|
|
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
/** Fades all the lights in specified sector to a new
|
2014-03-15 16:59:03 +00:00
|
|
|
* value.
|
|
|
|
*
|
2018-09-09 02:14:49 +00:00
|
|
|
* \param sector Target sector
|
2014-03-15 16:59:03 +00:00
|
|
|
* \param destvalue The final light value in these sectors.
|
2018-09-15 23:00:50 +00:00
|
|
|
* \param speed If tic-based: total duration of effect.
|
|
|
|
* If speed-based: Speed of the fade; the change to the ligh
|
2014-03-15 16:59:03 +00:00
|
|
|
* level in each sector per tic.
|
2018-09-09 02:10:51 +00:00
|
|
|
* \param ticbased Use a specific duration for the fade, defined by speed
|
2014-03-15 16:59:03 +00:00
|
|
|
* \sa T_LightFade
|
|
|
|
*/
|
2018-09-09 17:43:00 +00:00
|
|
|
void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased)
|
2014-03-15 16:59:03 +00:00
|
|
|
{
|
|
|
|
lightlevel_t *ll;
|
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
P_RemoveLighting(sector); // remove the old lighting effect first
|
2018-09-09 02:10:51 +00:00
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
|
|
|
|
{
|
|
|
|
sector->lightlevel = destvalue;
|
|
|
|
return;
|
|
|
|
}
|
2018-09-09 02:10:51 +00:00
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
|
|
|
|
ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
|
|
|
|
sector->lightingdata = ll; // set it to the lightlevel_t
|
2014-03-15 16:59:03 +00:00
|
|
|
|
2019-04-20 20:37:19 +00:00
|
|
|
P_AddThinker(THINK_MAIN, &ll->thinker); // add thinker
|
2014-03-15 16:59:03 +00:00
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
ll->sector = sector;
|
2018-09-11 14:05:25 +00:00
|
|
|
ll->sourcelevel = sector->lightlevel;
|
2018-09-09 02:14:49 +00:00
|
|
|
ll->destlevel = destvalue;
|
2018-09-09 02:10:51 +00:00
|
|
|
|
2018-09-15 10:21:25 +00:00
|
|
|
ll->fixedcurlevel = sector->lightlevel<<FRACBITS;
|
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
if (ticbased)
|
|
|
|
{
|
2018-09-15 10:21:25 +00:00
|
|
|
// Speed means duration.
|
|
|
|
ll->timer = abs(speed);
|
|
|
|
ll->fixedpertic = FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS);
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|
2018-09-09 02:14:49 +00:00
|
|
|
else
|
|
|
|
{
|
2018-09-15 10:21:25 +00:00
|
|
|
// Speed means increment per tic (literally speed).
|
|
|
|
ll->timer = FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS)>>FRACBITS;
|
|
|
|
ll->fixedpertic = speed<<FRACBITS;
|
2018-09-09 02:14:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-18 10:27:30 +00:00
|
|
|
void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean force)
|
2018-09-09 02:14:49 +00:00
|
|
|
{
|
|
|
|
INT32 i;
|
2020-11-12 12:48:14 +00:00
|
|
|
TAG_ITER_DECLARECOUNTER(0);
|
2020-04-14 21:33:56 +00:00
|
|
|
|
2018-09-09 02:14:49 +00:00
|
|
|
// search all sectors for ones with tag
|
2020-11-12 12:48:14 +00:00
|
|
|
TAG_ITER_SECTORS(0, tag, i)
|
2018-09-18 10:27:30 +00:00
|
|
|
{
|
|
|
|
if (!force && ticbased // always let speed fader execute
|
|
|
|
&& sectors[i].lightingdata
|
|
|
|
&& ((lightlevel_t*)sectors[i].lightingdata)->thinker.function.acp1 == (actionf_p1)T_LightFade)
|
|
|
|
// && ((lightlevel_t*)sectors[i].lightingdata)->timer > 2)
|
|
|
|
{
|
|
|
|
CONS_Debug(DBG_GAMELOGIC, "Line type 420 Executor: Fade light thinker already exists, timer: %d\n", ((lightlevel_t*)sectors[i].lightingdata)->timer);
|
|
|
|
continue;
|
|
|
|
}
|
2018-09-09 17:43:00 +00:00
|
|
|
P_FadeLightBySector(§ors[i], destvalue, speed, ticbased);
|
2018-09-18 10:27:30 +00:00
|
|
|
}
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Fades the light level in a sector to a new value.
|
|
|
|
*
|
|
|
|
* \param ll The fade effect under consideration.
|
|
|
|
* \sa P_FadeLight
|
|
|
|
*/
|
|
|
|
void T_LightFade(lightlevel_t *ll)
|
|
|
|
{
|
2018-09-15 10:21:25 +00:00
|
|
|
if (--ll->timer <= 0)
|
2018-09-09 02:10:51 +00:00
|
|
|
{
|
2018-09-15 23:00:50 +00:00
|
|
|
ll->sector->lightlevel = ll->destlevel; // set to dest lightlevel
|
|
|
|
P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
|
2018-09-09 17:43:00 +00:00
|
|
|
return;
|
2018-09-09 02:10:51 +00:00
|
|
|
}
|
2018-09-09 17:43:00 +00:00
|
|
|
|
2018-09-15 10:21:25 +00:00
|
|
|
ll->fixedcurlevel = ll->fixedcurlevel + ll->fixedpertic;
|
|
|
|
ll->sector->lightlevel = (ll->fixedcurlevel)>>FRACBITS;
|
2014-03-15 16:59:03 +00:00
|
|
|
}
|