raze/source/games/duke/src/sectors.cpp
2023-12-05 22:39:30 +01:00

2000 lines
49 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
Copyright (C) 2000, 2003 - Matt Saettler (EDuke Enhancements)
Copyright (C) 2020 - Christoph Oelckers
This file is part of Enhanced Duke Nukem 3D version 1.5 - Atomic Edition
Duke Nukem 3D 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Original Source: 1996 - Todd Replogle
Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
EDuke enhancements integrated: 04/13/2003 - Matt Saettler
Note: EDuke source was in transition. Changes are in-progress in the
source as it is released.
*/
//-------------------------------------------------------------------------
#include <utility>
#include "ns.h"
#include "global.h"
#include "sounds.h"
#include "dukeactor.h"
#include "interpolate.h"
// PRIMITIVE
BEGIN_DUKE_NS
static int interptype[] = { Interp_Sect_Floorz, Interp_Sect_Ceilingz, Interp_Wall_X, Interp_Wall_Y };
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool ceilingspace(sectortype* sectp)
{
return (sectp && (sectp->ceilingstat & CSTAT_SECTOR_SKY) && sectp->ceilingpal == 0 && (tilesurface(sectp->ceilingtexture) == TSURF_OUTERSPACE));
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool floorspace(sectortype* sectp)
{
// Yes, ceilingpal in this check is correct...
return (sectp && (sectp->floorstat & CSTAT_SECTOR_SKY) && sectp->ceilingpal == 0 && (tilesurface(sectp->floortexture) == TSURF_OUTERSPACE));
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static bool haltsoundhack;
int callsound(sectortype* sn, DDukeActor* whatsprite, bool endstate)
{
if (!isRRRA() && haltsoundhack)
{
haltsoundhack = 0;
return -1;
}
DukeSectIterator it(sn);
while (auto act = it.Next())
{
if (issoundcontroller(act) && act->spr.lotag < 1000)
{
if (whatsprite == nullptr) whatsprite = act;
int snum = act->spr.lotag;
auto flags = S_GetUserFlags(snum);
// Reset if the desired actor isn't playing anything.
bool hival = S_IsSoundValid(act->spr.hitag);
if (act->counter == 1 && !hival && !endstate)
{
if (!S_CheckActorSoundPlaying(act->temp_actor, snum))
act->counter = 0;
}
if (act->counter == 0)
{
if ((flags & (SF_GLOBAL | SF_DTAG)) != SF_GLOBAL)
{
if (snum)
{
if (act->spr.hitag && snum != act->spr.hitag)
S_StopSound(act->spr.hitag, act->temp_actor);
S_PlayActorSound(snum, whatsprite);
act->temp_actor = whatsprite;
}
if ((act->sector()->lotag & 0xff) != ST_22_SPLITTING_DOOR)
act->counter = 1;
}
}
else if (act->spr.hitag < 1000)
{
// The original code performed these two actions in reverse order which in case of a looped sound being stopped
// being the same as the sound about to be started, the newly started sound would fall through some cracks in the sound system and be rejected.
// Here this case needs to be simulated.
bool stopped = false;
if ((flags & SF_LOOP) || (act->spr.hitag && act->spr.hitag != act->spr.lotag))
{
S_StopSound(act->spr.lotag, act->temp_actor);
if (act->spr.hitag == act->spr.lotag) stopped = true;
}
if (act->spr.hitag && !stopped) S_PlayActorSound(act->spr.hitag, whatsprite);
act->counter = 0;
act->temp_actor = whatsprite;
}
return act->spr.lotag;
}
}
return -1;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int check_activator_motion(int lotag)
{
DukeStatIterator it(STAT_ACTIVATOR);
while (auto act = it.Next())
{
if (act->spr.lotag == lotag)
{
for (int j = animates.Size() - 1; j >= 0; j--)
if (act->sector() == animates[j].sect)
return(1);
DukeStatIterator it1(STAT_EFFECTOR);
while (auto act2 = it1.Next())
{
if (act->sector() == act2->sector())
switch (act2->spr.lotag)
{
case SE_11_SWINGING_DOOR:
case SE_30_TWO_WAY_TRAIN:
if (act2->temp_data[4])
return(1);
break;
case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
if (isRRRA()) break;
[[fallthrough]];
case SE_20_STRETCH_BRIDGE:
case SE_31_FLOOR_RISE_FALL:
case SE_32_CEILING_RISE_FALL:
if (act2->counter)
return(1);
break;
}
}
}
}
return(0);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool isanunderoperator(int lotag)
{
switch (lotag & 0xff)
{
case ST_15_WARP_ELEVATOR:
case ST_16_PLATFORM_DOWN:
case ST_17_PLATFORM_UP:
case ST_18_ELEVATOR_DOWN:
case ST_19_ELEVATOR_UP:
case ST_26_SPLITTING_ST_DOOR:
return true;
case ST_22_SPLITTING_DOOR:
return !isRR();
}
return false;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool isanearoperator(int lotag)
{
switch (lotag & 0xff)
{
case ST_9_SLIDING_ST_DOOR:
case ST_15_WARP_ELEVATOR:
case ST_16_PLATFORM_DOWN:
case ST_17_PLATFORM_UP:
case ST_18_ELEVATOR_DOWN:
case ST_19_ELEVATOR_UP:
case ST_20_CEILING_DOOR:
case ST_21_FLOOR_DOOR:
case ST_22_SPLITTING_DOOR:
case ST_23_SWINGING_DOOR:
case ST_25_SLIDING_DOOR:
case ST_26_SPLITTING_ST_DOOR:
case ST_29_TEETH_DOOR:
return true;
case 41:
return isRR();
}
return false;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int findplayer(const DDukeActor* actor, double* d)
{
int j, closest_player;
const auto s = actor->spr.pos;
if (ud.multimode < 2)
{
if (d) *d = (getPlayer(myconnectindex)->GetActor()->getPrevPosWithOffsetZ() - s).plusZ(28).Sum();
return myconnectindex;
}
double closest = 0x7fffffff;
closest_player = 0;
for (j = connecthead; j >= 0; j = connectpoint2[j])
{
const auto jact = getPlayer(j)->GetActor();
double x = (jact->getPrevPosWithOffsetZ() - s).plusZ(28).Sum();
if (x < closest && jact->spr.extra > 0)
{
closest_player = j;
closest = x;
}
}
if (d) *d = closest;
return closest_player;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int findotherplayer(int p, double* d)
{
const auto ppos = getPlayer(p)->GetActor()->getPosWithOffsetZ();
int j, closest_player;
double closest = 0x7fffffff;
closest_player = p;
for (j = connecthead; j >= 0; j = connectpoint2[j])
{
const auto jact = getPlayer(j)->GetActor();
if (p != j && jact->spr.extra > 0)
{
double x = (jact->getPrevPosWithOffsetZ() - ppos).Sum();
if (x < closest)
{
closest_player = j;
closest = x;
}
}
}
*d = closest;
return closest_player;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
double getanimatevalue(int type, int index)
{
switch (type)
{
case anim_floorz:
return sector[index].floorz;
case anim_ceilingz:
return sector[index].ceilingz;
case anim_vertexx:
return wall[index].pos.X;
case anim_vertexy:
return wall[index].pos.Y;
default:
assert(false);
return 0;
}
}
double getanimatevalue(int i)
{
return getanimatevalue(animates[i].type, animates[i].target);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void setanimatevalue(int type, int index, double value)
{
static int scratch;
switch (type)
{
case anim_floorz:
sector[index].setfloorz(value);
break;
case anim_ceilingz:
sector[index].setceilingz(value);
break;
case anim_vertexx:
wall[index].pos.X = value;
wall[index].moved();
break;
case anim_vertexy:
wall[index].pos.Y = value;
wall[index].moved();
break;
default:
assert(false);
}
}
void setanimatevalue(int i, double value)
{
return setanimatevalue(animates[i].type, animates[i].target, value);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void doanimations(void)
{
for (int i = animates.Size() - 1; i >= 0; i--)
{
double a = getanimatevalue(i);
double const v = animates[i].vel * TICSPERFRAME;
auto dasectp = animates[i].sect;
int type = animates[i].type;
if (a == animates[i].goal)
{
StopInterpolation(animates[i].target, interptype[animates[i].type]);
animates[i] = animates.Last();
animates.Pop();
if (dasectp->lotag == ST_18_ELEVATOR_DOWN || dasectp->lotag == ST_19_ELEVATOR_UP)
if (type == anim_ceilingz)
continue;
if ((dasectp->lotag & 0xff) != ST_22_SPLITTING_DOOR)
callsound(dasectp, nullptr, true);
continue;
}
if (v > 0) { a = min(a + v, animates[i].goal); }
else { a = max(a + v, animates[i].goal); }
if (type == anim_floorz)
{
for (auto j = connecthead; j >= 0; j = connectpoint2[j])
{
const auto p = getPlayer(j);
const auto pact = p->GetActor();
if ((p->cursector == dasectp) && ((dasectp->floorz - pact->getOffsetZ()) < 64) && (pact->GetOwner() != nullptr))
{
pact->spr.pos.Z += v;
p->vel.Z = 0;
}
}
DukeSectIterator it(dasectp);
while (auto act = it.Next())
{
if (act->spr.statnum != STAT_EFFECTOR)
{
if (act->spr.statnum != STAT_PLAYER)
{
act->backupz();
act->spr.pos.Z += v;
}
act->floorz = dasectp->floorz + v;
}
}
}
setanimatevalue(i, a);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int getanimationindex(int animtype, sectortype* animtargetp)
{
int i, j;
j = -1;
int animtarget = sectindex(animtargetp);
for (i = animates.Size() - 1; i >= 0; i--)
if (animtype == animates[i].type && animtarget == animates[i].target)
{
j = i;
break;
}
return(j);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static int dosetanimation(sectortype* animsect, int animtype, int animtarget, double thegoal, double thevel)
{
int j = -1;
for (unsigned i = 0; i < animates.Size(); i++)
if (animtype == animates[i].type && animtarget == animates[i].target)
{
j = i;
break;
}
if (j == -1) j = animates.Reserve(1);
auto animval = getanimatevalue(animtype, animtarget);
animates[j].sect = animsect;
animates[j].type = animtype;
animates[j].target = animtarget;
animates[j].goal = thegoal;
if (thegoal >= animval)
animates[j].vel = thevel;
else
animates[j].vel = -thevel;
StartInterpolation(animates[j].target, interptype[animates[j].type]);
return(j);
}
int setanimation(sectortype* animsect, int animtype, walltype* animtarget, double thegoal, double thevel)
{
assert(animtype == anim_vertexx || animtype == anim_vertexy);
return dosetanimation(animsect, animtype, wallindex(animtarget), thegoal, thevel);
}
int setanimation(sectortype* animsect, int animtype, sectortype* animtarget, double thegoal, double thevel)
{
assert(animtype == anim_ceilingz || animtype == anim_floorz);
return dosetanimation(animsect, animtype, sectindex(animtarget), thegoal, thevel);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool activatewarpelevators(DDukeActor* actor, int d) //Parm = sectoreffectornum
{
auto sect = actor->sector();
// See if the sector exists
DukeStatIterator it(STAT_EFFECTOR);
DDukeActor *act2;
while ((act2 = it.Next()))
{
if (act2->spr.lotag == SE_17_WARP_ELEVATOR || (isRRRA() && act2->spr.lotag == SE_18_INCREMENTAL_SECTOR_RISE_FALL))
if (act2->spr.hitag == actor->spr.hitag)
if ((abs(sect->floorz - actor->temp_pos.X) > act2->spr.yint * maptoworld) ||
(act2->sector()->hitag == (sect->hitag - d)))
break;
}
if (act2 == nullptr)
{
d = 0;
return 1; // No find
}
else
{
if (d == 0)
S_PlayActorSound(ELEVATOR_OFF, actor);
else S_PlayActorSound(ELEVATOR_ON, actor);
}
it.Reset(STAT_EFFECTOR);
while ((act2 = it.Next()))
{
if (act2->spr.lotag == SE_17_WARP_ELEVATOR || (isRRRA() && act2->spr.lotag == SE_18_INCREMENTAL_SECTOR_RISE_FALL))
if (act2->spr.hitag == actor->spr.hitag)
{
act2->counter = d;
if (act2->spr.lotag == SE_17_WARP_ELEVATOR) act2->temp_data[1] = d; //Make all check warp (only SE17, in SE18 this is a coordinate)
}
}
return 0;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st09(sectortype* sptr, DDukeActor* actor)
{
double dax, day, dax2, day2, sp;
walltype* wallfind[2];
sp = (sptr->extra >> 4) / 16.;
//first find center point by averaging all points
dax = 0, day = 0;
for (auto& wal : sptr->walls)
{
dax += wal.pos.X;
day += wal.pos.Y;
}
dax /= sptr->walls.Size();
day /= sptr->walls.Size();
//find any points with either same x or same y coordinate
// as center (dax, day) - should be 2 points found.
wallfind[0] = nullptr;
wallfind[1] = nullptr;
for (auto& wal : sptr->walls)
// more precise checks won't work here.
if (abs(wal.pos.X - dax) <= (1 / 32.) || abs(wal.pos.Y - day) <= (1 / 32.))
{
if (wallfind[0] == nullptr)
wallfind[0] = &wal;
else wallfind[1] = &wal;
}
for (int j = 0; j < 2; j++)
{
auto wal = wallfind[j];
//find what direction door should open by averaging the
// 2 neighboring points of wallfind[0] & wallfind[1].
auto prevwall = wal - 1;
if (prevwall < sptr->walls.Data()) prevwall += sptr->walls.Size();
if ((wal->pos.X == dax) && (wal->pos.Y == day))
{
dax2 = ((prevwall->pos.X + wal->point2Wall()->pos.X) * 0.5) - wal->pos.X;
day2 = ((prevwall->pos.Y + wal->point2Wall()->pos.Y) * 0.5) - wal->pos.Y;
if (dax2 != 0)
{
dax2 = wal->point2Wall()->point2Wall()->pos.X;
dax2 -= wal->point2Wall()->pos.X;
setanimation(sptr, anim_vertexx, wal, wal->pos.X + dax2, sp);
setanimation(sptr, anim_vertexx, prevwall, prevwall->pos.X + dax2, sp);
setanimation(sptr, anim_vertexx, wal->point2Wall(), wal->point2Wall()->pos.X + dax2, sp);
callsound(sptr, actor);
}
else if (day2 != 0)
{
day2 = wal->point2Wall()->point2Wall()->pos.Y;
day2 -= wal->point2Wall()->pos.Y;
setanimation(sptr, anim_vertexy, wal, wal->pos.Y + day2, sp);
setanimation(sptr, anim_vertexy, prevwall, prevwall->pos.Y + day2, sp);
setanimation(sptr, anim_vertexy, wal->point2Wall(), wal->point2Wall()->pos.Y + day2, sp);
callsound(sptr, actor);
}
}
else
{
dax2 = ((prevwall->pos.X + wal->point2Wall()->pos.X) * 0.5) - wal->pos.X;
day2 = ((prevwall->pos.Y + wal->point2Wall()->pos.Y) * 0.5) - wal->pos.Y;
if (dax2 != 0)
{
setanimation(sptr, anim_vertexx, wal, dax, sp);
setanimation(sptr, anim_vertexx, prevwall, dax + dax2, sp);
setanimation(sptr, anim_vertexx, wal->point2Wall(), dax + dax2, sp);
callsound(sptr, actor);
}
else if (day2 != 0)
{
setanimation(sptr, anim_vertexy, wal, day, sp);
setanimation(sptr, anim_vertexy, prevwall, day + day2, sp);
setanimation(sptr, anim_vertexy, wal->point2Wall(), day + day2, sp);
callsound(sptr, actor);
}
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st15(sectortype* sptr, DDukeActor* actor)
{
if (!actor->isPlayer()) return;
DukeSectIterator it(sptr);
DDukeActor* a2;
while ((a2 = it.Next()))
{
if (iseffector(a2) && a2->spr.lotag == ST_17_PLATFORM_UP) break;
}
if (!a2) return;
if (actor->sector() == sptr)
{
if (activatewarpelevators(a2, -1))
activatewarpelevators(a2, 1);
else if (activatewarpelevators(a2, 1))
activatewarpelevators(a2, -1);
return;
}
else
{
if (sptr->floorz > a2->spr.pos.Z)
activatewarpelevators(a2, -1);
else
activatewarpelevators(a2, 1);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st16(sectortype* sptr, DDukeActor* actor)
{
int i = getanimationindex(anim_floorz, sptr);
sectortype* sectp;
if (i == -1)
{
sectp = nextsectorneighborzptr(sptr, sptr->floorz, Find_FloorDown);
if (sectp == nullptr)
{
sectp = nextsectorneighborzptr(sptr, sptr->floorz, Find_FloorUp);
if (sectp == nullptr) return;
}
setanimation(sptr, anim_floorz, sptr, sectp->floorz, sptr->extra / 256.);
callsound(sptr, actor);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st18(sectortype* sptr, DDukeActor* actor)
{
int i = getanimationindex(anim_floorz, sptr);
if (i == -1)
{
auto sectp = nextsectorneighborzptr(sptr, sptr->floorz, Find_FloorUp);
if (sectp == nullptr) sectp = nextsectorneighborzptr(sptr, sptr->floorz, Find_FloorDown);
if (sectp == nullptr) return;
double speed = sptr->extra / 256.;
setanimation(sptr, anim_floorz, sptr, sectp->floorz, speed);
setanimation(sptr, anim_ceilingz, sptr, sectp->floorz + sptr->ceilingz - sptr->floorz, speed);
callsound(sptr, actor);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st29(sectortype* sptr, DDukeActor* actor)
{
double j;
if (sptr->lotag & 0x8000)
j = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_FloorDown | Find_Safe)->floorz;
else
j = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_CeilingUp | Find_Safe)->ceilingz;
DukeStatIterator it(STAT_EFFECTOR);
while (auto act2 = it.Next())
{
if ((act2->spr.lotag == 22) &&
(act2->spr.hitag == sptr->hitag))
{
act2->sector()->extra = -act2->sector()->extra;
act2->counter = sectindex(sptr);
act2->temp_data[1] = 1;
}
}
sptr->lotag ^= 0x8000;
setanimation(sptr, anim_ceilingz, sptr, j, sptr->extra / 256.);
callsound(sptr, actor);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st20(sectortype* sptr, DDukeActor* actor)
{
double j = 0;
REDODOOR:
if (sptr->lotag & 0x8000)
{
DDukeActor* a2;
DukeSectIterator it(sptr);
while ((a2 = it.Next()))
{
if (a2->spr.statnum == STAT_EFFECTOR && a2->spr.lotag == SE_9_DOWN_OPEN_DOOR_LIGHTS)
{
j = a2->spr.pos.Z;
break;
}
}
if (a2 == nullptr)
j = sptr->floorz;
}
else
{
auto sectp = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_CeilingUp);
if (sectp) j = sectp->ceilingz;
else
{
sptr->lotag |= 32768;
goto REDODOOR;
}
}
sptr->lotag ^= 0x8000;
setanimation(sptr, anim_ceilingz, sptr, j, sptr->extra / 256.);
callsound(sptr, actor);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st21(sectortype* sptr, DDukeActor* actor)
{
int i = getanimationindex(anim_floorz, sptr);
double j;
if (i >= 0)
{
if (animates[i].goal == sptr->ceilingz)
animates[i].goal = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_FloorDown | Find_Safe)->floorz;
else animates[i].goal = sptr->ceilingz;
j = animates[i].goal;
}
else
{
if (sptr->ceilingz == sptr->floorz)
j = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_FloorDown | Find_Safe)->floorz;
else j = sptr->ceilingz;
sptr->lotag ^= 0x8000;
if (setanimation(sptr, anim_floorz, sptr, j, sptr->extra / 256.) >= 0)
callsound(sptr, actor);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st22(sectortype* sptr, DDukeActor* actor)
{
int j;
double z;
double speed = sptr->extra / 256.;
if ((sptr->lotag & 0x8000))
{
z = (sptr->ceilingz + sptr->floorz) * 0.5;
j = setanimation(sptr, anim_floorz, sptr, z, speed);
j = setanimation(sptr, anim_ceilingz, sptr, z, speed);
}
else
{
z = nextsectorneighborzptr(sptr, sptr->floorz, Find_FloorDown | Find_Safe)->floorz;
j = setanimation(sptr, anim_floorz, sptr, z, speed);
z = nextsectorneighborzptr(sptr, sptr->ceilingz, Find_CeilingUp | Find_Safe)->ceilingz;
j = setanimation(sptr, anim_ceilingz, sptr, z, speed);
}
sptr->lotag ^= 0x8000;
callsound(sptr, actor);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st23(sectortype* sptr, DDukeActor* actor)
{
int q = 0;
DukeStatIterator it(STAT_EFFECTOR);
DDukeActor* act2;
while ((act2 = it.Next()))
{
if (act2->spr.lotag == SE_11_SWINGING_DOOR && act2->sector() == sptr && !act2->temp_data[4])
{
break;
}
}
if (!act2) return;
int l = act2->sector()->lotag & 0x8000;
if (act2)
{
DukeStatIterator itr(STAT_EFFECTOR);
while (auto act3 = itr.Next())
{
if (l == (act3->sector()->lotag & 0x8000) && act3->spr.lotag == SE_11_SWINGING_DOOR && act2->spr.hitag == act3->spr.hitag && act3->temp_data[4])
{
return;
}
}
itr.Reset(STAT_EFFECTOR);
while (auto act3 = itr.Next())
{
if (l == (act3->sector()->lotag & 0x8000) && act3->spr.lotag == SE_11_SWINGING_DOOR && act2->spr.hitag == act3->spr.hitag)
{
if (act3->sector()->lotag & 0x8000) act3->sector()->lotag &= 0x7fff;
else act3->sector()->lotag |= 0x8000;
act3->temp_data[4] = 1;
act3->temp_data[3] = -act3->temp_data[3];
if (q == 0)
{
callsound(sptr, act3);
q = 1;
}
}
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st25(sectortype* sptr, DDukeActor* actor)
{
DukeStatIterator it(STAT_EFFECTOR);
DDukeActor* act2;
while ((act2 = it.Next()))
{
if (act2->spr.lotag == 15 && act2->sector() == sptr)
{
break;
}
}
if (act2 == nullptr)
return;
it.Reset(STAT_EFFECTOR);
while (auto act3 = it.Next())
{
if (act3->spr.hitag == act2->spr.hitag)
{
if (act3->spr.lotag == 15)
{
act3->sector()->lotag ^= 0x8000; // Toggle the open or close
act3->spr.Angles.Yaw += DAngle180;
if (act3->temp_data[4]) callsound(act3->sector(), act3);
callsound(act3->sector(), act3);
if (act3->sector()->lotag & 0x8000) act3->temp_data[4] = 1;
else act3->temp_data[4] = 2;
}
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st27(sectortype* sptr, DDukeActor* actor)
{
DukeStatIterator it(STAT_EFFECTOR);
while (auto act2 = it.Next())
{
if ((act2->spr.lotag & 0xff) == 20 && act2->sector() == sptr) //Bridge
{
sptr->lotag ^= 0x8000;
if (sptr->lotag & 0x8000) //OPENING
act2->counter = 1;
else act2->counter = 2;
callsound(sptr, actor);
break;
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void handle_st28(sectortype* sptr, DDukeActor* actor)
{
//activate the rest of them
int j = -1;
DukeSectIterator it(sptr);
while (auto a2 = it.Next())
{
if (a2->spr.statnum == 3 && (a2->spr.lotag & 0xff) == 21)
{
j = a2->spr.hitag;
break; //Found it
}
}
if (j == -1) return;
DukeStatIterator it1(STAT_EFFECTOR);
while (auto act3 = it.Next())
{
if ((act3->spr.lotag & 0xff) == 21 && !act3->counter &&
(act3->spr.hitag) == j)
act3->counter = 1;
}
callsound(sptr, actor);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void operatesectors(sectortype* sptr, DDukeActor *actor)
{
int j=0;
int i;
switch (sptr->lotag & (0x3fff))
{
case 41:
if (isRR()) operatejaildoors(sptr->hitag);
break;
case 7:
if (!isRR()) break;
for (auto& wal : sptr->walls)
{
setanimation(sptr, anim_vertexx, &wal, wal.pos.X + 64, 1 / 4.);
if (wal.twoSided()) setanimation(sptr, anim_vertexx, wal.nextWall(), wal.nextWall()->pos.X + 64, 1 / 4.);
}
break;
case ST_30_ROTATE_RISE_BRIDGE:
{
auto act = barrier_cast<DDukeActor*>(sptr->hitagactor);
if (!act) break;
if (act->tempval == 0 || act->tempval == 256) callsound(sptr, actor);
if (act->spr.extra == 1) act->spr.extra = 3;
else act->spr.extra = 1;
break;
}
case ST_31_TWO_WAY_TRAIN:
{
auto act = barrier_cast<DDukeActor*>(sptr->hitagactor);
if (!act) break;
if (act->temp_data[4] == 0)
act->temp_data[4] = 1;
callsound(sptr, actor);
break;
}
case ST_26_SPLITTING_ST_DOOR: //The split doors
i = getanimationindex(anim_ceilingz, sptr);
if (i == -1) //if the door has stopped
{
haltsoundhack = 1;
sptr->lotag &= 0xff00;
sptr->lotag |= ST_22_SPLITTING_DOOR;
operatesectors(sptr, actor);
sptr->lotag &= 0xff00;
sptr->lotag |= ST_9_SLIDING_ST_DOOR;
operatesectors(sptr, actor);
sptr->lotag &= 0xff00;
sptr->lotag |= ST_26_SPLITTING_ST_DOOR;
}
return;
case ST_9_SLIDING_ST_DOOR:
handle_st09(sptr, actor);
return;
case ST_15_WARP_ELEVATOR://Warping elevators
handle_st15(sptr, actor);
return;
case ST_16_PLATFORM_DOWN:
case ST_17_PLATFORM_UP:
handle_st16(sptr, actor);
return;
case ST_18_ELEVATOR_DOWN:
case ST_19_ELEVATOR_UP:
handle_st18(sptr, actor);
return;
case ST_29_TEETH_DOOR:
handle_st29(sptr, actor);
return;
case ST_20_CEILING_DOOR:
handle_st20(sptr, actor);
return;
case ST_21_FLOOR_DOOR:
handle_st21(sptr, actor);
return;
case ST_22_SPLITTING_DOOR:
handle_st22(sptr, actor);
return;
case ST_23_SWINGING_DOOR: //Swingdoor
handle_st23(sptr, actor);
return;
case ST_25_SLIDING_DOOR: //Subway type sliding doors
{
handle_st25(sptr, actor);
return;
}
case ST_27_STRETCH_BRIDGE: //Extended bridge
handle_st27(sptr, actor);
return;
case ST_28_DROP_FLOOR:
handle_st28(sptr, actor);
return;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void operateactivators(int low, DDukePlayer* plr)
{
int i, j, k;
Cycler * p;
walltype* wal;
for (i = cyclers.Size() - 1; i >= 0; i--)
{
p = &cyclers[i];
if (p->hitag == low)
{
auto sect = p->sector;
p->state = !p->state;
sect->floorshade = sect->ceilingshade = (int8_t)p->shade2;
wal = sect->walls.Data();
for (j = sect->walls.Size(); j > 0; j--, wal++)
wal->shade = (int8_t)p->shade2;
}
}
k = -1;
DukeStatIterator it(STAT_ACTIVATOR);
while (auto act = it.Next())
{
if (act->spr.lotag == low)
{
if (islockedactivator(act))
{
act->sector()->lotag ^= 16384;
if (plr)
{
if (act->sector()->lotag & 16384)
FTA(4, plr);
else FTA(8, plr);
}
}
else
{
switch (act->spr.hitag)
{
case 0:
break;
case 1:
if (act->sector()->floorz != act->sector()->ceilingz)
{
continue;
}
break;
case 2:
if (act->sector()->floorz == act->sector()->ceilingz)
{
continue;
}
break;
}
if (act->sector()->lotag < 3)
{
DukeSectIterator itr(act->sector());
while (auto a2 = itr.Next())
{
if (a2->spr.statnum == 3) switch (a2->spr.lotag)
{
case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
if (isRRRA()) break;
[[fallthrough]];
case SE_36_PROJ_SHOOTER:
case SE_31_FLOOR_RISE_FALL:
case SE_32_CEILING_RISE_FALL:
a2->counter = 1 - a2->counter;
callsound(act->sector(), a2);
break;
}
}
}
if (k == -1 && (act->sector()->lotag & 0xff) == SE_22_TEETH_DOOR)
k = callsound(act->sector(), act);
operatesectors(act->sector(), act);
}
}
}
operaterespawns(low);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void operatemasterswitches(int low)
{
DukeStatIterator it(STAT_STANDABLE);
while (auto act2 = it.Next())
{
if (ismasterswitch(act2) && act2->spr.lotag == low && act2->spr.yint == 0)
{
act2->spr.yint = 1;
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void operateforcefields(DDukeActor *effector, int low)
{
for (int p = numanimwalls-1; p >= 0; p--)
{
auto wal = animwall[p].wall;
if (low == wal->lotag || low == -1)
if (tileflags(wal->overtexture) & (TFLAG_FORCEFIELD | TFLAG_ANIMFORCEFIELD))
{
animwall[p].tag = 0;
if (wal->cstat)
{
wal->cstat = 0;
if (effector && iseffector(effector) && effector->spr.lotag == SE_30_TWO_WAY_TRAIN)
wal->lotag = 0;
}
else
wal->cstat = (CSTAT_WALL_BLOCK | CSTAT_WALL_ALIGN_BOTTOM | CSTAT_WALL_MASKED | CSTAT_WALL_BLOCK_HITSCAN);
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void checkhitwall(DDukeActor* spr, walltype* wal, const DVector3& pos)
{
if (wal->overtexture == mirrortex && (spr->flags2 & SFLAG2_BREAKMIRRORS))
{
lotsofglass(spr, wal, 70);
wal->cstat &= ~CSTAT_WALL_MASKED;
wal->setovertexture(TexMan.CheckForTexture("MIRRORBROKE", ETextureType::Any));
wal->portalflags = 0;
S_PlayActorSound(GLASS_HEAVYBREAK, spr);
return;
}
auto handler = [=](const BreakWallRec* data) ->bool
{
if (!data->handler)
{
S_PlayActorSound(data->breaksound, spr);
return true;
}
else
{
VMValue args[7] = { wal, data->brokentex.GetIndex(), data->breaksound.index(), spr, pos.X, pos.Y, pos.Z};
VMCall(data->handler, args, 7, nullptr, 0);
}
return false;
};
if (wal->twoSided() && wal->nextSector()->floorz > pos.Z && wal->nextSector()->floorz - wal->nextSector()->ceilingz)
{
auto data = breakWallMap.CheckKey(wal->overtexture.GetIndex());
if (data && (data->flags & 1) && (!(data->flags & 2) || wal->cstat & CSTAT_WALL_MASKED))
{
if (handler(data)) wal->setovertexture(data->brokentex);
}
}
auto data = breakWallMap.CheckKey(wal->walltexture.GetIndex());
if (data && !(data->flags & 1))
{
if (handler(data)) wal->setwalltexture(data->brokentex);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool checkhitceiling(sectortype* sectp)
{
auto data = breakCeilingMap.CheckKey(sectp->ceilingtexture.GetIndex());
if (data)
{
if (!data->handler)
{
sectp->setceilingtexture(data->brokentex);
S_PlayActorSound(data->breaksound, getPlayer(screenpeek)->GetActor()); // this is nonsense but what the original code did.
}
else
{
VMValue args[7] = { sectp, data->brokentex.GetIndex(), data->breaksound.index()};
VMCall(data->handler, args, 3, nullptr, 0);
}
if (data->flags & 1)
{
if (!sectp->hitag)
{
DukeSectIterator it(sectp);
while (auto act = it.Next())
{
if (iseffector(act) && act->spr.lotag == SE_12_LIGHT_SWITCH)
{
DukeStatIterator it1(STAT_EFFECTOR);
while (auto act2 = it1.Next())
{
if (act2->spr.hitag == act->spr.hitag)
act2->temp_data[3] = 1;
}
break;
}
}
}
int j = krand() & 1;
DukeStatIterator it(STAT_EFFECTOR);
while (auto act = it.Next())
{
if (act->spr.hitag == (sectp->hitag) && act->spr.lotag == SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT)
{
act->temp_data[2] = j;
act->temp_data[4] = 1;
}
}
}
if (data->flags & 2)
{
ceilingglass(getPlayer(myconnectindex)->GetActor(), sectp, 10);
}
return true;
}
return false;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void checkhitdefault(DDukeActor* targ, DDukeActor* proj)
{
if ((targ->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && targ->spr.hitag == 0 && targ->spr.lotag == 0 && targ->spr.statnum == STAT_DEFAULT)
return;
if (((proj->flags3 & SFLAG3_CANHURTSHOOTER) || proj->GetOwner() != targ) && targ->spr.statnum != STAT_PROJECTILE)
{
if (badguy(targ))
{
if (targ->spr.scale.X < targ->FloatVar(NAME_minhitscale))
return;
if (proj->flags4 & SFLAG4_DOUBLEHITDAMAGE) proj->spr.extra <<= 1;
if (!(targ->flags3 & SFLAG3_NOHITJIBS) && !(proj->flags3 & SFLAG3_NOHITJIBS))
{
auto spawned = spawn(proj, DukeJibs6Class);
if (spawned)
{
if (proj->spr.pal == 6)
spawned->spr.pal = 6;
spawned->spr.pos.Z += 4;
spawned->vel.X = 1;
spawned->spr.scale = DVector2(0.375, 0.375);
spawned->spr.Angles.Yaw = DAngle22_5 / 4 - randomAngle(22.5 / 2);
}
}
auto Owner = proj->GetOwner();
if (Owner && Owner->isPlayer() && !(targ->flags3 & SFLAG3_NOSHOTGUNBLOOD))
if (getPlayer(Owner->PlayerIndex())->curr_weapon == SHOTGUN_WEAPON)
{
shoot(targ, DukeBloodSplat3Class);
shoot(targ, DukeBloodSplat1Class);
shoot(targ, DukeBloodSplat2Class);
shoot(targ, DukeBloodSplat4Class);
}
if (!(targ->flags2 & SFLAG2_NODAMAGEPUSH))
{
if ((targ->spr.cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == 0)
targ->spr.Angles.Yaw = proj->spr.Angles.Yaw + DAngle180;
targ->vel.X = -proj->spr.extra * 0.25;
auto sp = targ->sector();
pushmove(targ->spr.pos, &sp, 8, 4, 4, CLIPMASK0);
if (sp != targ->sector() && sp != nullptr)
ChangeActorSect(targ, sp);
}
if (targ->spr.statnum == STAT_ZOMBIEACTOR)
{
ChangeActorStat(targ, STAT_ACTOR);
targ->timetosleep = SLEEPTIME;
}
if (!shrinkersizecheck(proj->GetClass(), targ)) return;
}
if (targ->spr.statnum != STAT_ZOMBIEACTOR)
{
if ((proj->flags2 & SFLAG2_FREEZEDAMAGE) && ((targ->isPlayer() && targ->spr.pal == 1) || (gs.freezerhurtowner == 0 && proj->GetOwner() == targ)))
return;
auto hitpic = static_cast<PClassActor*>(proj->GetClass());
auto Owner = proj->GetOwner();
if (Owner && Owner->isPlayer())
{
if (targ->isPlayer() && ud.coop != 0 && ud.ffire == 0)
return;
auto tOwner = targ->GetOwner();
if (hitpic == DukeFireballClass && tOwner && tOwner->GetClass() != DukeFireballClass) // hack alert! Even with damage types this special check needs to stay.
hitpic = DukeFlamethrowerFlameClass;
}
targ->attackertype = hitpic;
targ->hitextra += proj->spr.extra;
if (!(targ->flags4 & SFLAG4_NODAMAGETURN))
targ->hitang = proj->spr.Angles.Yaw;
targ->SetHitOwner(Owner);
}
if (targ->spr.statnum == STAT_PLAYER)
{
const auto p = getPlayer(targ->PlayerIndex());
const auto pact = p->GetActor();
if (p->newOwner != nullptr)
{
p->newOwner = nullptr;
pact->restoreloc();
updatesector(pact->getPosWithOffsetZ(), &p->cursector);
DukeStatIterator it(STAT_ACTOR);
while (auto itActor = it.Next())
{
if (itActor->flags2 & SFLAG2_CAMERA) itActor->spr.yint = 0;
}
}
if (!shrinkersizecheck(proj->GetClass(), targ))
return;
auto hitowner = targ->GetHitOwner();
if (!hitowner || !hitowner->isPlayer())
if (ud.player_skill >= 3)
proj->spr.extra += (proj->spr.extra >> 1);
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void allignwarpelevators(void)
{
DukeStatIterator it(STAT_EFFECTOR);
while (auto act = it.Next())
{
if (act->spr.lotag == SE_17_WARP_ELEVATOR && act->spr.shade > 16)
{
DukeStatIterator it1(STAT_EFFECTOR);
while (auto act2 = it1.Next())
{
if ((act2->spr.lotag) == SE_17_WARP_ELEVATOR && act != act2 && act->spr.hitag == act2->spr.hitag)
{
act2->sector()->setfloorz(act->sector()->floorz);
act2->sector()->setceilingz(act->sector()->ceilingz);
}
}
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void moveclouds(double interpfrac)
{
// The math here is very messy.. :(
int myclock = interpfrac < 0.5 ? PlayClock-2 : PlayClock;
if (myclock > cloudclock || myclock < (cloudclock - 7))
{
const auto pact = getPlayer(screenpeek)->GetActor();
cloudclock = myclock + 6;
// cloudx/y were an array, but all entries were always having the same value so a single pair is enough.
cloudx += (float)pact->spr.Angles.Yaw.Cos() * 0.5f;
cloudy += (float)pact->spr.Angles.Yaw.Sin() * 0.5f;
for (int i = 0; i < numclouds; i++)
{
// no clamping here!
clouds[i]->ceilingxpan_ = cloudx;
clouds[i]->ceilingypan_ = cloudy;
clouds[i]->exflags |= SECTOREX_CLOUDSCROLL;
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void resetswitch(int tag)
{
DukeStatIterator it2(STAT_DEFAULT);
while (auto act2 = it2.Next())
{
auto& ext = GetExtInfo(act2->spr.spritetexture());
if (ext.switchindex > 0 && ext.switchphase == 1 && act2->spr.hitag == tag)
{
auto& swdef = switches[ext.switchindex];
if (swdef.type == SwitchDef::Regular && swdef.flags & SwitchDef::resettable)
{
act2->spr.setspritetexture(swdef.states[0]);
}
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void tag10000specialswitch(DDukePlayer* const p, DDukeActor* act, const DVector3& v)
{
DDukeActor* switches[3];
int switchcount = 0, j;
S_PlaySound3D(SWITCH_ON, act, v);
DukeSpriteIterator itr;
while (auto actt = itr.Next())
{
int jht = actt->spr.hitag;
auto ext = GetExtInfo(actt->spr.spritetexture());
if (jht == 10000 && ext.switchphase == 0 && ::switches[ext.switchindex].type == SwitchDef::Multi)
{
if (switchcount < 3)
{
switches[switchcount] = actt;
switchcount++;
}
}
}
if (switchcount == 3)
{
// This once was a linear search over sprites[] so bring things back in order, just to be safe.
if (switches[0]->GetIndex() > switches[1]->GetIndex()) std::swap(switches[0], switches[1]);
if (switches[0]->GetIndex() > switches[2]->GetIndex()) std::swap(switches[0], switches[2]);
if (switches[1]->GetIndex() > switches[2]->GetIndex()) std::swap(switches[1], switches[2]);
S_PlaySound3D(78, act, v);
for (j = 0; j < switchcount; j++)
{
switches[j]->spr.hitag = 0;
switches[j]->spr.setspritetexture(::switches[GetExtInfo(switches[j]->spr.spritetexture()).switchindex].states[3]);
checkhitswitch(p, nullptr, switches[j]);
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void togglespriteswitches(DDukeActor* act, const TexExtInfo& ext, int lotag, int& correctdips, int& numdips)
{
auto& swdef = switches[ext.switchindex];
DukeStatIterator it(STAT_DEFAULT);
while (auto other = it.Next())
{
if (lotag != other->spr.lotag) continue;
auto& other_ext = GetExtInfo(other->spr.spritetexture());
auto& other_swdef = switches[other_ext.switchindex];
switch (other_swdef.type)
{
case SwitchDef::Combo:
if (other_ext.switchphase == 0)
{
if (act == other) other->spr.setspritetexture(other_swdef.states[1]);
else if (other->spr.hitag == 0) correctdips++;
numdips++;
}
else
{
if (act == other) other->spr.setspritetexture(other_swdef.states[0]);
else if (other->spr.hitag == 1) correctdips++;
numdips++;
}
break;
case SwitchDef::Multi:
other->spr.setspritetexture(other_swdef.states[(other_ext.switchphase + 1) & 3]);
break;
case SwitchDef::Access:
case SwitchDef::Regular:
if (other->spr.hitag != 999 || other_ext.switchphase != 1 || !(other_swdef.flags & SwitchDef::resettable))
{
other->spr.setspritetexture(other_swdef.states[1 - other_ext.switchphase]);
}
// one of RR's ugly hacks.
if (other->spr.hitag == 999 && other_ext.switchphase == 0 && (other_swdef.flags & SwitchDef::resettable))
{
DukeStatIterator it1(STAT_LUMBERMILL);
while (auto other2 = it1.Next())
{
CallOnUse(other2, nullptr);
}
}
break;
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void togglewallswitches(walltype* wwal, const TexExtInfo& ext, int lotag, int& correctdips, int& numdips)
{
for (auto& wal : wall)
{
if (lotag != wal.lotag) continue;
auto& other_ext = GetExtInfo(wal.walltexture);
auto& other_swdef = switches[other_ext.switchindex];
switch (other_swdef.type)
{
case SwitchDef::Combo:
if (other_ext.switchphase == 0)
{
if (&wal == wwal) wal.setwalltexture(other_swdef.states[1]);
else if (wal.hitag == 0) correctdips++;
numdips++;
}
else
{
if (&wal == wwal) wal.setwalltexture(other_swdef.states[0]);
else if (wal.hitag == 1) correctdips++;
numdips++;
}
break;
case SwitchDef::Multi:
wal.setwalltexture(other_swdef.states[(other_ext.switchphase + 1) & 3]);
break;
case SwitchDef::Access:
case SwitchDef::Regular:
wal.setwalltexture(other_swdef.states[1 - other_ext.switchphase]);
break;
}
}
}
//---------------------------------------------------------------------------
//
// how NOT to implement switches...
// (even after cleaning up the hard coded texture checks it's still a disaster)
//
//---------------------------------------------------------------------------
bool checkhitswitch(DDukePlayer* const p, walltype* wwal, DDukeActor* act)
{
const auto pact = p->GetActor();
uint8_t switchpal;
int lotag, hitag, correctdips, numdips;
DVector2 spos;
FTextureID texid;
int swresult = 0;
if (wwal == nullptr && act == nullptr) return 0;
correctdips = 1;
numdips = 0;
if (act)
{
lotag = act->spr.lotag;
if (lotag == 0) return 0;
hitag = act->spr.hitag;
spos = act->spr.pos.XY();
texid = act->spr.spritetexture();
switchpal = act->spr.pal;
// custom switches that maintain themselves can immediately abort.
swresult = CallTriggerSwitch(act, p);
if (swresult == 1) return true;
}
else
{
lotag = wwal->lotag;
if (lotag == 0) return 0;
hitag = wwal->hitag;
spos = wwal->pos;
texid = wwal->walltexture;
switchpal = wwal->pal;
}
auto& ext = GetExtInfo(texid);
auto& swdef = switches[ext.switchindex];
if (swresult == 0)
{
// check if the switch may be activated.
switch (swdef.type)
{
case SwitchDef::Combo:
break;
case SwitchDef::Access:
if (fi.checkaccessswitch(p, switchpal, act, wwal))
return 0;
[[fallthrough]];
case SwitchDef::Regular:
case SwitchDef::Multi:
if (check_activator_motion(lotag)) return 0;
break;
default:
if (isadoorwall(texid) == 0) return 0;
break;
}
togglespriteswitches(act, ext, lotag, correctdips, numdips);
togglewallswitches(wwal, ext, lotag, correctdips, numdips);
if (lotag == -1)
{
setnextmap(false);
return 1;
}
// Yet another crude RRRA hack that cannot be fully generalized.
if (hitag == 10001 && swdef.flags & SwitchDef::oneway && isRRRA())
{
act->spr.setspritetexture(swdef.states[1]);
if (p->SeaSick == 0)
p->SeaSick = 350;
operateactivators(668, p);
operatemasterswitches(668);
S_PlayActorSound(328, pact);
return 1;
}
}
DVector3 v(spos, pact->getOffsetZ());
if (swdef.type != SwitchDef::None || isadoorwall(texid))
{
if (swresult == 0)
{
if (swdef.type == SwitchDef::Combo)
{
FSoundID sound = swdef.soundid != NO_SOUND ? swdef.soundid : S_FindSoundByResID(SWITCH_ON);
if (act) S_PlaySound3D(sound, act, v);
else S_PlaySound3D(sound, pact, v);
if (numdips != correctdips) return 0;
S_PlaySound3D(END_OF_LEVEL_WARN, pact, v);
}
if (swdef.type == SwitchDef::Multi)
{
lotag += ext.switchphase;
if (hitag == 10000 && act && isRRRA()) // no idea if the game check is really needed for something this far off the beaten path...
{
tag10000specialswitch(p, act, v);
return 1;
}
}
}
DukeStatIterator itr(STAT_EFFECTOR);
while (auto other = itr.Next())
{
if (other->spr.hitag == lotag)
{
switch (other->spr.lotag)
{
case 46:
case SE_47_LIGHT_SWITCH:
case SE_48_LIGHT_SWITCH:
if (!isRRRA()) break;
[[fallthrough]];
case SE_12_LIGHT_SWITCH:
other->sector()->floorpal = 0;
other->counter++;
if (other->counter == 2)
other->counter++;
break;
case SE_24_CONVEYOR:
case SE_34:
case SE_25_PISTON:
other->temp_data[4] = !other->temp_data[4];
if (other->temp_data[4])
FTA(15, p);
else FTA(2, p);
break;
case SE_21_DROP_FLOOR:
FTA(2, getPlayer(screenpeek));
break;
}
}
}
operateactivators(lotag, p);
operateforcefields(pact, lotag);
operatemasterswitches(lotag);
if (swdef.type == SwitchDef::Combo) return 1;
if (hitag == 0 && isadoorwall(texid) == 0)
{
FSoundID sound = swdef.soundid != NO_SOUND ? swdef.soundid : S_FindSoundByResID(SWITCH_ON);
if (act) S_PlaySound3D(sound, act, v);
else S_PlaySound3D(sound, pact, v);
}
else if (hitag != 0)
{
auto flags = S_GetUserFlags(hitag);
if (act && (flags & SF_TALK) == 0)
S_PlaySound3D(hitag, act, v);
else
S_PlayActorSound(hitag, pact);
}
return 1;
}
return 0;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void animatewalls(void)
{
static FTextureID noise, ff1, ff2;
// all that was done here is to open the system up sufficiently to allow replacing the textures being used without having to use ART files.
// Custom animated textures are better done with newly written controller actors.
if (!noise.isValid()) noise = TexMan.CheckForTexture("SCREENBREAK6", ETextureType::Any);
if (!ff1.isValid()) ff1 = TexMan.CheckForTexture("W_FORCEFIELD", ETextureType::Any);
if (!ff2.isValid()) ff2 = TexMan.CheckForTexture("W_FORCEFIELD2", ETextureType::Any);
if (getPlayer(screenpeek)->sea_sick_stat == 1)
{
for (auto& wal : wall)
{
if (tileflags(wal.walltexture) & TFLAG_SEASICKWALL)
wal.addxpan(6);
}
}
int t;
for (int p = 0; p < numanimwalls; p++)
{
auto wal = animwall[p].wall;
auto texid = wal->walltexture;
if (!animwall[p].overpic)
{
if (tileflags(wal->walltexture) & TFLAG_ANIMSCREEN)
{
if ((krand() & 255) < 16)
{
wal->setwalltexture(noise);
}
}
else if (tileflags(wal->walltexture) & TFLAG_ANIMSCREENNOISE)
{
if (animwall[p].origtex.isValid())
wal->setwalltexture(animwall[p].origtex);
else
{
texid = texid + 1;
if (texid.GetIndex() > noise.GetIndex() + 3 || texid.GetIndex() < noise.GetIndex()) texid = noise;
wal->setwalltexture(texid);
}
}
}
else
{
if (tileflags(wal->overtexture) & TFLAG_ANIMFORCEFIELD && wal->cstat & CSTAT_WALL_MASKED)
{
t = animwall[p].tag;
if (wal->cstat & CSTAT_WALL_ANY_EXCEPT_BLOCK)
{
wal->addxpan(-t / 4096.f);
wal->addypan(-t / 4096.f);
if (wal->extra == 1)
{
wal->extra = 0;
animwall[p].tag = 0;
}
else
animwall[p].tag += 128;
if (animwall[p].tag < (128 << 4))
{
if (animwall[p].tag & 128)
wal->setovertexture(ff1);
else wal->setovertexture(ff2);
}
else
{
if ((krand() & 255) < 32)
animwall[p].tag = 128 << (krand() & 3);
else wal->setovertexture(ff2);
}
}
}
}
}
}
END_DUKE_NS