// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. // // 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. */ 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); 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; 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 sectors with a particular tag to a new * value. * * \param tag Tag to look for sectors by. * \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. * \todo Calculate speed better so that it is possible to specify * the time for completion of the fade, and all lights fade * in this time regardless of initial values. * \sa T_LightFade */ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed) { INT32 i; lightlevel_t *ll; sector_t *sector; // search all sectors for ones with tag for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;) { sector = §ors[i]; P_RemoveLighting(sector); // remove the old lighting effect first 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 P_AddThinker(&ll->thinker); // add thinker ll->sector = sector; ll->destlevel = destvalue; ll->speed = speed; } } /** 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) { if (ll->sector->lightlevel < ll->destlevel) { // increase the lightlevel if (ll->sector->lightlevel + ll->speed >= ll->destlevel) { // stop changing light level ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker } else ll->sector->lightlevel = (INT16)(ll->sector->lightlevel + (INT16)ll->speed); // move lightlevel } else { // decrease lightlevel if (ll->sector->lightlevel - ll->speed <= ll->destlevel) { // stop changing light level ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker } else ll->sector->lightlevel = (INT16)(ll->sector->lightlevel - (INT16)ll->speed); // move lightlevel } }