diff --git a/doomsday/plugins/hexen/hexen.pro b/doomsday/plugins/hexen/hexen.pro index dce20e531a..1d40511247 100644 --- a/doomsday/plugins/hexen/hexen.pro +++ b/doomsday/plugins/hexen/hexen.pro @@ -36,6 +36,7 @@ HEADERS += \ include/intermission.h \ include/info.h \ include/jhexen.h \ + include/lightninganimator.h \ include/m_cheat.h \ include/m_random.h \ include/p_enemy.h \ @@ -77,6 +78,7 @@ SOURCES += \ src/hconsole.cpp \ src/hrefresh.cpp \ src/intermission.cpp \ + src/lightninganimator.cpp \ src/m_cheat.cpp \ src/m_random.c \ src/p_enemy.c \ diff --git a/doomsday/plugins/hexen/include/lightninganimator.h b/doomsday/plugins/hexen/include/lightninganimator.h new file mode 100644 index 0000000000..029e01849a --- /dev/null +++ b/doomsday/plugins/hexen/include/lightninganimator.h @@ -0,0 +1,62 @@ +/** @file lightninganimator.h Animator for map-wide lightning effects. + * + * @authors Copyright © 2004-2014 Jaakko Keränen + * @authors Copyright © 2005-2014 Daniel Swanson + * @authors Copyright © 1999 Activision + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LIBHEXEN_PLAY_LIGHTNINGANIMATOR_H +#define LIBHEXEN_PLAY_LIGHTNINGANIMATOR_H + +#include "jhexen.h" + +/** + * Animator for the map-wide lightning effects. + */ +class LightningAnimator +{ +public: + LightningAnimator(); + + /** + * Determines whether lightning is enabled for the current map. + */ + bool enabled() const; + + /** + * Manually trigger a lightning flash which will begin animating on the next game tic. + * Can be used to trigger a flash at specific times. + */ + void triggerFlash(); + + /** + * Animate lightning for the current map. To be called once per GAMETIC. + */ + void advanceTime(); + + /** + * Initialize the lightning animator for the current map. + * + * @return @c true, if lightning is enabled for the current map, for caller convenience. + */ + bool initForMap(); + +private: + DENG2_PRIVATE(d) +}; + +#endif // LIBHEXEN_PLAY_LIGHTNINGANIMATOR_H diff --git a/doomsday/plugins/hexen/src/lightninganimator.cpp b/doomsday/plugins/hexen/src/lightninganimator.cpp new file mode 100644 index 0000000000..9f1f4867b5 --- /dev/null +++ b/doomsday/plugins/hexen/src/lightninganimator.cpp @@ -0,0 +1,241 @@ +/** @file lightninganimator.cpp Animator for map-wide lightning effects. + * + * @authors Copyright © 2003-2014 Jaakko Keränen + * @authors Copyright © 2005-2014 Daniel Swanson + * @authors Copyright © 1999 Activision + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "jhexen.h" +#include "lightninganimator.h" + +#include "dmu_lib.h" +#include "gamesession.h" + +using namespace de; + +#define LIGHTNING_SPECIAL 198 +#define LIGHTNING_SPECIAL2 199 + +static bool isLightningSector(Sector *sec) +{ + xsector_t *xsec = P_ToXSector(sec); + + if(xsec->special == LIGHTNING_SPECIAL || xsec->special == LIGHTNING_SPECIAL2) + return true; + + if(P_GetIntp(P_GetPtrp(sec, DMU_CEILING_MATERIAL), + DMU_FLAGS) & MATF_SKYMASK) + return true; + + if(P_GetIntp(P_GetPtrp(sec, DMU_FLOOR_MATERIAL), + DMU_FLAGS) & MATF_SKYMASK) + return true; + + return false; +} + +DENG2_PIMPL_NOREF(LightningAnimator) +{ + int flash = 0; + int nextFlash = 0; + float *sectorLightLevels = 0; ///< Ambient light levels for each sector (if enabled). +}; + +LightningAnimator::LightningAnimator() : d(new Instance) +{} + +bool LightningAnimator::enabled() const +{ + return d->sectorLightLevels != 0; +} + +void LightningAnimator::triggerFlash() +{ + if(!enabled()) return; + d->nextFlash = 0; +} + +void LightningAnimator::advanceTime() +{ + if(!enabled()) return; + + // Is it time for a lightning state change? + if(!(!d->nextFlash || d->flash)) + { + d->nextFlash--; + return; + } + + if(d->flash) + { + d->flash--; + float *tempLight = d->sectorLightLevels; + + if(d->flash) + { + for(int i = 0; i < numsectors; ++i) + { + Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); + if(!isLightningSector(sec)) continue; + + float lightLevel = P_GetFloat(DMU_SECTOR, i, DMU_LIGHT_LEVEL); + if(*tempLight < lightLevel - (4.f / 255)) + { + P_SetFloat(DMU_SECTOR, i, DMU_LIGHT_LEVEL, lightLevel - (1.f / 255) * 4); + } + tempLight++; + } + } + else + { + // Remove the alternate lightning flash special. + for(int i = 0; i < numsectors; ++i) + { + Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); + if(!isLightningSector(sec)) continue; + + P_SetFloatp(sec, DMU_LIGHT_LEVEL, *tempLight); + tempLight++; + } + + if(!IS_DEDICATED) + { + R_SkyParams(1, DD_DISABLE, nullptr); + R_SkyParams(0, DD_ENABLE, nullptr); + } + } + + return; + } + + d->flash = (P_Random() & 7) + 8; + + float const flashLight = (float) (200 + (P_Random() & 31)) / 255.0f; + float *tempLight = d->sectorLightLevels; + bool foundSec = false; + for(int i = 0; i < numsectors; ++i) + { + Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); + if(!isLightningSector(sec)) continue; + + xsector_t *xsec = P_ToXSector(sec); + float newLevel = P_GetFloatp(sec, DMU_LIGHT_LEVEL); + + *tempLight = newLevel; + + if(xsec->special == LIGHTNING_SPECIAL) + { + newLevel += .25f; + if(newLevel > flashLight) + newLevel = flashLight; + } + else if(xsec->special == LIGHTNING_SPECIAL2) + { + newLevel += .125f; + if(newLevel > flashLight) + newLevel = flashLight; + } + else + { + newLevel = flashLight; + } + + if(newLevel < *tempLight) + newLevel = *tempLight; + + P_SetFloatp(sec, DMU_LIGHT_LEVEL, newLevel); + tempLight++; + foundSec = true; + } + + if(foundSec) + { + mobj_t *plrmo = ::players[DISPLAYPLAYER].plr->mo; + mobj_t *clapSource = 0; + + if(!IS_DEDICATED) + { + // Set the alternate (lightning) sky. + R_SkyParams(0, DD_DISABLE, nullptr); + R_SkyParams(1, DD_ENABLE, nullptr); + } + + // If 3D sounds are active, position the clap somewhere above the player. + if(::cfg.snd3D && plrmo && !IS_NETGAME) + { + coord_t clapOrigin[] = { + plrmo->origin[VX] + (16 * (M_Random() - 127) << FRACBITS), + plrmo->origin[VY] + (16 * (M_Random() - 127) << FRACBITS), + plrmo->origin[VZ] + (4000 << FRACBITS) + }; + clapSource = P_SpawnMobj(MT_CAMERA, clapOrigin, 0, 0); + if(clapSource) + { + clapSource->tics = 5 * TICSPERSEC; // Five seconds will do. + } + } + + // Make it loud! + S_StartSound(SFX_THUNDER_CRASH | DDSF_NO_ATTENUATION, clapSource); + } + + // Calculate the next lighting flash. + if(!d->nextFlash) + { + if(P_Random() < 50) // Immediate, quick flash. + { + d->nextFlash = (P_Random() & 15) + 16; + } + else if(P_Random() < 128 && !(mapTime & 32)) + { + d->nextFlash = ((P_Random() & 7) + 2) * TICSPERSEC; + } + else + { + d->nextFlash = ((P_Random() & 15) + 5) * TICSPERSEC; + } + } +} + +bool LightningAnimator::initForMap() +{ + d->flash = 0; + d->nextFlash = 0; + Z_Free(d->sectorLightLevels); d->sectorLightLevels = 0; + + Record const *mapInfo = COMMON_GAMESESSION->mapInfo(); + if(mapInfo && !mapInfo->getb("lightning")) + { + int numLightningSectors = 0; + for(int i = 0; i < numsectors; ++i) + { + if(isLightningSector((Sector *)P_ToPtr(DMU_SECTOR, i))) + { + numLightningSectors++; + } + } + if(numLightningSectors > 0) + { + d->sectorLightLevels = (float *)Z_Malloc(numLightningSectors * sizeof(*d->sectorLightLevels), PU_MAP, nullptr); + + // Don't flash immediately on entering the map. + d->nextFlash = ((P_Random() & 15) + 5) * TICSPERSEC; + } + } + + return enabled(); +} diff --git a/doomsday/plugins/hexen/src/p_spec.cpp b/doomsday/plugins/hexen/src/p_spec.cpp index f2960e62b1..0d0829ca71 100644 --- a/doomsday/plugins/hexen/src/p_spec.cpp +++ b/doomsday/plugins/hexen/src/p_spec.cpp @@ -27,6 +27,7 @@ #include "dmu_lib.h" #include "g_common.h" #include "gamesession.h" +#include "lightninganimator.h" #include "p_inventory.h" #include "player.h" #include "p_map.h" @@ -44,234 +45,6 @@ using namespace de; using namespace common; -#define LIGHTNING_SPECIAL 198 -#define LIGHTNING_SPECIAL2 199 - -/** - * Animator for the map-wide lightning effects. - */ -class LightningAnimator -{ - int flash = 0; - int nextFlash = 0; - float *sectorLightLevels = 0; ///< Ambient light levels for each sector (if enabled). - -public: - /** - * Determines whether lighting is enabled for the current map. - */ - bool enabled() const - { - return sectorLightLevels != 0; - } - - /** - * Manually trigger a lightning flash which will begin animating on the next game tic. - * Can be used to trigger a flash at specific times. - */ - void triggerFlash() - { - if(!enabled()) return; - nextFlash = 0; - } - - /** - * Animate lighting for the current map. To be called once per GAMETIC. - */ - void advanceTime() - { - if(!enabled()) return; - - // Is it time for a lightning state change? - if((!nextFlash || flash)) - { - nextFlash--; - return; - } - - if(flash) - { - flash--; - float *tempLight = sectorLightLevels; - - if(flash) - { - for(int i = 0; i < numsectors; ++i) - { - Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); - if(!isLightningSector(sec)) continue; - - float lightLevel = P_GetFloat(DMU_SECTOR, i, DMU_LIGHT_LEVEL); - if(*tempLight < lightLevel - (4.f / 255)) - { - P_SetFloat(DMU_SECTOR, i, DMU_LIGHT_LEVEL, lightLevel - (1.f / 255) * 4); - } - tempLight++; - } - } - else - { - // Remove the alternate lightning flash special. - for(int i = 0; i < numsectors; ++i) - { - Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); - if(!isLightningSector(sec)) continue; - - P_SetFloatp(sec, DMU_LIGHT_LEVEL, *tempLight); - tempLight++; - } - - if(!IS_DEDICATED) - { - R_SkyParams(1, DD_DISABLE, NULL); - R_SkyParams(0, DD_ENABLE, NULL); - } - } - - return; - } - - flash = (P_Random() & 7) + 8; - - float const flashLight = (float) (200 + (P_Random() & 31)) / 255.0f; - float *tempLight = sectorLightLevels; - bool foundSec = false; - for(int i = 0; i < numsectors; ++i) - { - Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); - if(!isLightningSector(sec)) continue; - - xsector_t *xsec = P_ToXSector(sec); - float newLevel = P_GetFloatp(sec, DMU_LIGHT_LEVEL); - - *tempLight = newLevel; - - if(xsec->special == LIGHTNING_SPECIAL) - { - newLevel += .25f; - if(newLevel > flashLight) - newLevel = flashLight; - } - else if(xsec->special == LIGHTNING_SPECIAL2) - { - newLevel += .125f; - if(newLevel > flashLight) - newLevel = flashLight; - } - else - { - newLevel = flashLight; - } - - if(newLevel < *tempLight) - newLevel = *tempLight; - - P_SetFloatp(sec, DMU_LIGHT_LEVEL, newLevel); - tempLight++; - foundSec = true; - } - - if(foundSec) - { - mobj_t *plrmo = ::players[DISPLAYPLAYER].plr->mo; - mobj_t *crashOrigin = 0; - - if(!IS_DEDICATED) - { - // Set the alternate (lightning) sky. - R_SkyParams(0, DD_DISABLE, NULL); - R_SkyParams(1, DD_ENABLE, NULL); - } - - // If 3D sounds are active, position the clap somewhere above the player. - if(::cfg.snd3D && plrmo && !IS_NETGAME) - { - if((crashOrigin = - P_SpawnMobjXYZ(MT_CAMERA, - plrmo->origin[VX] + (16 * (M_Random() - 127) << FRACBITS), - plrmo->origin[VY] + (16 * (M_Random() - 127) << FRACBITS), - plrmo->origin[VZ] + (4000 << FRACBITS), - 0, 0)) != NULL) - { - crashOrigin->tics = 5 * TICSPERSEC; // Five seconds will do. - } - } - - // Make it loud! - S_StartSound(SFX_THUNDER_CRASH | DDSF_NO_ATTENUATION, crashOrigin); - } - - // Calculate the next lighting flash. - if(!nextFlash) - { - if(P_Random() < 50) // Immediate, quick flash. - { - nextFlash = (P_Random() & 15) + 16; - } - else if(P_Random() < 128 && !(mapTime & 32)) - { - nextFlash = ((P_Random() & 7) + 2) * TICSPERSEC; - } - else - { - nextFlash = ((P_Random() & 15) + 5) * TICSPERSEC; - } - } - } - - /** - * Initialize the lightning animator for the current map. - * - * @return @c true, if lightning is enabled for the current map, for caller convenience. - */ - bool initForMap() - { - flash = 0; - nextFlash = 0; - Z_Free(sectorLightLevels); sectorLightLevels = 0; - - Record const *mapInfo = COMMON_GAMESESSION->mapInfo(); - if(mapInfo && !mapInfo->getb("lightning")) - { - int numLightningSectors = 0; - for(int i = 0; i < numsectors; ++i) - { - if(isLightningSector((Sector *)P_ToPtr(DMU_SECTOR, i))) - { - numLightningSectors++; - } - } - if(numLightningSectors > 0) - { - sectorLightLevels = (float *)Z_Malloc(numLightningSectors * sizeof(float), PU_MAP, nullptr); - - // Don't flash immediately on entering the map. - nextFlash = ((P_Random() & 15) + 5) * TICSPERSEC; - } - } - - return enabled(); - } - -private: - static bool isLightningSector(Sector *sec) - { - xsector_t *xsec = P_ToXSector(sec); - - if(xsec->special == LIGHTNING_SPECIAL || xsec->special == LIGHTNING_SPECIAL2) - return true; - - if(P_GetIntp(P_GetPtrp(sec, DMU_CEILING_MATERIAL), - DMU_FLAGS) & MATF_SKYMASK) - return true; - - if(P_GetIntp(P_GetPtrp(sec, DMU_FLOOR_MATERIAL), - DMU_FLAGS) & MATF_SKYMASK) - return true; - - return false; - } -}; LightningAnimator lightningAnimator; ThinkerT lavaInflictor;