vkdoom_m/src/dsectoreffect.cpp
Christoph Oelckers e7aa10b5c8 - changed thinker initialization to occur in a Construct function instead of the constructor itself.
This was done to ensure that this code only runs when the thinker itself is fully set up.
With a constructor there is no control about such things, if some common initialization needs to be done it has to be in the base constructor, but that makes the entire approach chosen here to ensure proper linking into the thinker chains impossible.
ZDoom originally did it that way, which resulted in a very inflexible system and required some awful hacks to let the serializer work with it - the corresponding bSerialOverride flag is now gone.

The only thinker class still having a constructor is DFraggleThinker, because it contains non-serializable data that needs to be initialized in a piece of code that always runs, regardless of whether the object is created explicitly or from a savegame.
2019-01-27 13:08:54 +01:00

411 lines
10 KiB
C++

//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2016 Christoph Oelckers
//
// 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 3 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, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// DESCRIPTION:
// Base class for effects on sectors.
// [RH] Created this class hierarchy.
//
//-----------------------------------------------------------------------------
#include "dsectoreffect.h"
#include "p_local.h"
#include "g_levellocals.h"
#include "p_3dmidtex.h"
#include "r_data/r_interpolate.h"
#include "serializer.h"
#include "doomstat.h"
#include "vm.h"
IMPLEMENT_CLASS(DSectorEffect, false, false)
void DSectorEffect::OnDestroy()
{
if (m_Sector)
{
if (m_Sector->floordata == this)
{
m_Sector->floordata = nullptr;
}
if (m_Sector->ceilingdata == this)
{
m_Sector->ceilingdata = nullptr;
}
if (m_Sector->lightingdata == this)
{
m_Sector->lightingdata = nullptr;
}
}
Super::OnDestroy();
}
void DSectorEffect::Construct(sector_t *sector)
{
m_Sector = sector;
}
void DSectorEffect::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("sector", m_Sector);
}
DEFINE_FIELD(DSectorEffect, m_Sector)
DEFINE_ACTION_FUNCTION(DSectorEffect, GetSector)
{
PARAM_SELF_PROLOGUE(DSectorEffect);
ACTION_RETURN_POINTER(self->GetSector());
}
IMPLEMENT_CLASS(DMover, true, true)
IMPLEMENT_POINTERS_START(DMover)
IMPLEMENT_POINTER(interpolation)
IMPLEMENT_POINTERS_END
void DMover::Construct (sector_t *sector)
{
Super::Construct(sector);
interpolation = nullptr;
}
void DMover::OnDestroy()
{
StopInterpolation();
Super::OnDestroy();
}
void DMover::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("interpolation", interpolation);
}
void DMover::StopInterpolation(bool force)
{
if (interpolation != nullptr)
{
interpolation->DelRef(force);
interpolation = nullptr;
}
}
IMPLEMENT_CLASS(DMovingFloor, true, false)
void DMovingFloor::Construct(sector_t *sector)
{
Super::Construct(sector);
sector->floordata = this;
interpolation = sector->SetInterpolation(sector_t::FloorMove, true);
}
IMPLEMENT_CLASS(DMovingCeiling, true, false)
void DMovingCeiling::Construct(sector_t *sector, bool interpolate)
{
Super::Construct(sector);
sector->ceilingdata = this;
if (interpolate) interpolation = sector->SetInterpolation(sector_t::CeilingMove, true);
}
bool sector_t::MoveAttached(int crush, double move, int floorOrCeiling, bool resetfailed, bool instant)
{
if (!P_Scroll3dMidtex(this, crush, move, !!floorOrCeiling, instant) && resetfailed)
{
P_Scroll3dMidtex(this, crush, -move, !!floorOrCeiling, instant);
return false;
}
if (!P_MoveLinkedSectors(this, crush, move, !!floorOrCeiling, instant) && resetfailed)
{
P_MoveLinkedSectors(this, crush, -move, !!floorOrCeiling, instant);
P_Scroll3dMidtex(this, crush, -move, !!floorOrCeiling, instant);
return false;
}
return true;
}
//
// Move a plane (floor or ceiling) and check for crushing
// [RH] Crush specifies the actual amount of crushing damage inflictable.
// (Use -1 to prevent it from trying to crush)
// dest is the desired d value for the plane
//
EMoveResult sector_t::MoveFloor(double speed, double dest, int crush, int direction, bool hexencrush, bool instant)
{
bool flag;
double lastpos;
double movedest;
double move;
//double destheight; //jff 02/04/98 used to keep floors/ceilings
// from moving thru each other
lastpos = floorplane.fD();
switch (direction)
{
case -1:
// DOWN
movedest = floorplane.GetChangedHeight(-speed);
if (movedest >= dest)
{
move = floorplane.HeightDiff(lastpos, dest);
if (!MoveAttached(crush, move, 0, true, instant)) return EMoveResult::crushed;
floorplane.setD(dest);
flag = P_ChangeSector(this, crush, move, 0, false, instant);
if (flag)
{
floorplane.setD(lastpos);
P_ChangeSector(this, crush, -move, 0, true, instant);
MoveAttached(crush, -move, 0, false, instant);
}
else
{
ChangePlaneTexZ(sector_t::floor, move);
AdjustFloorClip();
}
return EMoveResult::pastdest;
}
else
{
if (!MoveAttached(crush, -speed, 0, true, instant)) return EMoveResult::crushed;
floorplane.setD(movedest);
flag = P_ChangeSector(this, crush, -speed, 0, false, instant);
if (flag)
{
floorplane.setD(lastpos);
P_ChangeSector(this, crush, speed, 0, true, instant);
MoveAttached(crush, speed, 0, false, instant);
return EMoveResult::crushed;
}
else
{
ChangePlaneTexZ(sector_t::floor, floorplane.HeightDiff(lastpos));
AdjustFloorClip();
}
}
break;
case 1:
// UP
// jff 02/04/98 keep floor from moving thru ceilings
// [RH] not so easy with arbitrary planes
//destheight = (dest < ceilingheight) ? dest : ceilingheight;
if (!ceilingplane.isSlope() && !floorplane.isSlope() &&
!PortalIsLinked(sector_t::ceiling) &&
(!(i_compatflags2 & COMPATF2_FLOORMOVE) && -dest > ceilingplane.fD()))
{
dest = -ceilingplane.fD();
}
movedest = floorplane.GetChangedHeight(speed);
if (movedest <= dest)
{
move = floorplane.HeightDiff(lastpos, dest);
if (!MoveAttached(crush, move, 0, true, instant)) return EMoveResult::crushed;
floorplane.setD(dest);
flag = P_ChangeSector(this, crush, move, 0, false, instant);
if (flag)
{
floorplane.setD(lastpos);
P_ChangeSector(this, crush, -move, 0, true, instant);
MoveAttached(crush, -move, 0, false, instant);
}
else
{
ChangePlaneTexZ(sector_t::floor, move);
AdjustFloorClip();
}
return EMoveResult::pastdest;
}
else
{
if (!MoveAttached(crush, speed, 0, true, instant)) return EMoveResult::crushed;
floorplane.setD(movedest);
// COULD GET CRUSHED
flag = P_ChangeSector(this, crush, speed, 0, false, instant);
if (flag)
{
if (crush >= 0 && !hexencrush)
{
ChangePlaneTexZ(sector_t::floor, floorplane.HeightDiff(lastpos));
AdjustFloorClip();
return EMoveResult::crushed;
}
floorplane.setD(lastpos);
P_ChangeSector(this, crush, -speed, 0, true, instant);
MoveAttached(crush, -speed, 0, false, instant);
return EMoveResult::crushed;
}
ChangePlaneTexZ(sector_t::floor, floorplane.HeightDiff(lastpos));
AdjustFloorClip();
}
break;
}
return EMoveResult::ok;
}
DEFINE_ACTION_FUNCTION(_Sector, MoveFloor)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_FLOAT(speed);
PARAM_FLOAT(dest);
PARAM_INT(crush);
PARAM_INT(dir);
PARAM_BOOL(hex);
PARAM_BOOL(inst);
ACTION_RETURN_INT((int)self->MoveFloor(speed, dest, crush, dir, hex, inst));
}
EMoveResult sector_t::MoveCeiling(double speed, double dest, int crush, int direction, bool hexencrush)
{
bool flag;
double lastpos;
double movedest;
double move;
//double destheight; //jff 02/04/98 used to keep floors/ceilings
// from moving thru each other
lastpos = ceilingplane.fD();
switch (direction)
{
case -1:
// DOWN
// jff 02/04/98 keep ceiling from moving thru floors
// [RH] not so easy with arbitrary planes
//destheight = (dest > floorheight) ? dest : floorheight;
if (!ceilingplane.isSlope() && !floorplane.isSlope() &&
!PortalIsLinked(sector_t::floor) &&
(!(i_compatflags2 & COMPATF2_FLOORMOVE) && dest < -floorplane.fD()))
{
dest = -floorplane.fD();
}
movedest = ceilingplane.GetChangedHeight (-speed);
if (movedest <= dest)
{
move = ceilingplane.HeightDiff (lastpos, dest);
if (!MoveAttached(crush, move, 1, true)) return EMoveResult::crushed;
ceilingplane.setD(dest);
flag = P_ChangeSector (this, crush, move, 1, false);
if (flag)
{
ceilingplane.setD(lastpos);
P_ChangeSector (this, crush, -move, 1, true);
MoveAttached(crush, -move, 1, false);
}
else
{
ChangePlaneTexZ(sector_t::ceiling, move);
}
return EMoveResult::pastdest;
}
else
{
if (!MoveAttached(crush, -speed, 1, true)) return EMoveResult::crushed;
ceilingplane.setD(movedest);
// COULD GET CRUSHED
flag = P_ChangeSector (this, crush, -speed, 1, false);
if (flag)
{
if (crush >= 0 && !hexencrush)
{
ChangePlaneTexZ(sector_t::ceiling, ceilingplane.HeightDiff (lastpos));
return EMoveResult::crushed;
}
ceilingplane.setD(lastpos);
P_ChangeSector (this, crush, speed, 1, true);
MoveAttached(crush, speed, 1, false);
return EMoveResult::crushed;
}
ChangePlaneTexZ(sector_t::ceiling, ceilingplane.HeightDiff (lastpos));
}
break;
case 1:
// UP
movedest = ceilingplane.GetChangedHeight (speed);
if (movedest >= dest)
{
move = ceilingplane.HeightDiff (lastpos, dest);
if (!MoveAttached(crush, move, 1, true)) return EMoveResult::crushed;
ceilingplane.setD(dest);
flag = P_ChangeSector (this, crush, move, 1, false);
if (flag)
{
ceilingplane.setD(lastpos);
P_ChangeSector (this, crush, move, 1, true);
MoveAttached(crush, move, 1, false);
}
else
{
ChangePlaneTexZ(sector_t::ceiling, move);
}
return EMoveResult::pastdest;
}
else
{
if (!MoveAttached(crush, speed, 1, true)) return EMoveResult::crushed;
ceilingplane.setD(movedest);
flag = P_ChangeSector (this, crush, speed, 1, false);
if (flag)
{
ceilingplane.setD(lastpos);
P_ChangeSector (this, crush, -speed, 1, true);
MoveAttached(crush, -speed, 1, false);
return EMoveResult::crushed;
}
ChangePlaneTexZ(sector_t::ceiling, ceilingplane.HeightDiff (lastpos));
}
break;
}
return EMoveResult::ok;
}
DEFINE_ACTION_FUNCTION(_Sector, MoveCeiling)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_FLOAT(speed);
PARAM_FLOAT(dest);
PARAM_INT(crush);
PARAM_INT(dir);
PARAM_BOOL(hex);
ACTION_RETURN_INT((int)self->MoveCeiling(speed, dest, crush, dir, hex));
}