SRB2/src/p_lights.c

455 lines
12 KiB
C
Raw Normal View History

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.
// Copyright (C) 1999-2016 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 "doomstat.h" // gametic
2014-03-15 16:59:03 +00:00
#include "p_local.h"
#include "r_main.h" // LIGHTSEGSHIFT
2014-03-15 16:59:03 +00:00
#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.
*/
static void P_RemoveLighting(sector_t *sector)
{
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;
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);
P_AddThinker(&flick->thinker);
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);
P_AddThinker(&flash->thinker);
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);
P_AddThinker(&flash->thinker);
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)
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);
P_AddThinker(&g->thinker);
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;
}
/** Fades all the lights in specified sector to a new
2014-03-15 16:59:03 +00:00
* value.
*
* \param sector Target sector
2014-03-15 16:59:03 +00:00
* \param destvalue The final light value in these sectors.
* \param speed Speed of the fade; the change to the ligh
* level in each sector per tic.
* \param ticbased Use a specific duration for the fade, defined by speed
* \param exactlightlevel Do not snap to software values (for OpenGL)
2014-03-15 16:59:03 +00:00
* \sa T_LightFade
*/
void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
2014-03-15 16:59:03 +00:00
{
lightlevel_t *ll;
P_RemoveLighting(sector); // remove the old lighting effect first
if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
{
sector->lightlevel = destvalue;
return;
}
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
P_AddThinker(&ll->thinker); // add thinker
2014-03-15 16:59:03 +00:00
ll->sector = sector;
ll->destlevel = destvalue;
ll->exactlightlevel = exactlightlevel;
ll->lightlevel = sector->lightlevel;
if (ticbased)
{
ll->duration = abs(speed);
ll->speed = FixedFloor(FixedDiv(destvalue - sector->lightlevel, ll->duration))/FRACUNIT;
if (!ll->speed)
ll->speed = (destvalue < sector->lightlevel) ? -1 : 1;
ll->interval = max(FixedFloor(FixedDiv(ll->duration, abs(destvalue - sector->lightlevel)))/FRACUNIT, 1);
ll->firsttic = gametic;
2014-03-15 16:59:03 +00:00
}
else
{
ll->duration = -1;
ll->speed = abs(speed);
}
}
void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean exactlightlevel)
{
INT32 i;
// search all sectors for ones with tag
for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased, exactlightlevel);
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)
{
boolean stillfading = false;
INT16 lightlevel = ll->lightlevel;
if (ll->duration >= 0) // tic-based
{
stillfading = !(gametic - ll->firsttic >= ll->duration);
if (gametic - ll->firsttic >= ll->duration)
{
lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
}
else if (!((gametic - ll->firsttic) % ll->interval))
{
if (ll->speed < 0)
lightlevel = max(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
else
lightlevel = min(lightlevel + (INT16)ll->speed, (INT16)ll->destlevel);
}
}
else // x/tic speed-based
2014-03-15 16:59:03 +00:00
{
if (lightlevel < ll->destlevel)
2014-03-15 16:59:03 +00:00
{
// increase the lightlevel
if (lightlevel + ll->speed >= ll->destlevel)
{
// stop changing light level
lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
2014-03-15 16:59:03 +00:00
P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
}
else
{
stillfading = true;
lightlevel = (INT16)(lightlevel + (INT16)ll->speed); // move lightlevel
}
2014-03-15 16:59:03 +00:00
}
else
{
// decrease lightlevel
if (lightlevel - ll->speed <= ll->destlevel)
{
// stop changing light level
lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
2014-03-15 16:59:03 +00:00
P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
}
else
{
stillfading = true;
lightlevel = (INT16)(lightlevel - (INT16)ll->speed); // move lightlevel
}
2014-03-15 16:59:03 +00:00
}
}
// Snap light level to software values
if (!stillfading || ll->exactlightlevel)
ll->sector->lightlevel = lightlevel;
else
ll->sector->lightlevel = (lightlevel >> LIGHTSEGSHIFT) << LIGHTSEGSHIFT;
ll->lightlevel = lightlevel;
2014-03-15 16:59:03 +00:00
}