qzdoom/src/p_plats.cpp
Christoph Oelckers 66d28a24b8 - disabled the scripted virtual function module after finding out that it only works if each single class that may serve as a parent for scripting is explicitly declared.
Needless to say, this is simply too volatile and would require constant active maintenance, not to mention a huge amount of work up front to get going.
It also hid a nasty problem with the Destroy method. Due to the way the garbage collector works, Destroy cannot be exposed to scripts as-is. It may be called from scripts but it may not be overridden from scripts because the garbage collector can call this function after all data needed for calling a scripted override has already been destroyed because if that data is also being collected there is no guarantee that proper order of destruction is observed. So for now Destroy is just a normal native method to scripted classes
2016-11-25 00:25:26 +01:00

434 lines
10 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Plats (i.e. elevator platforms) code, raising/lowering.
//
//-----------------------------------------------------------------------------
#include "i_system.h"
#include "m_random.h"
#include "doomdef.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "s_sndseq.h"
#include "doomstat.h"
#include "r_state.h"
#include "gi.h"
#include "serializer.h"
#include "p_spec.h"
static FRandom pr_doplat ("DoPlat");
IMPLEMENT_CLASS(DPlat, false, false)
DPlat::DPlat ()
{
}
void DPlat::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc.Enum("type", m_Type)
("speed", m_Speed)
("low", m_Low)
("high", m_High)
("wait", m_Wait)
("count", m_Count)
.Enum("status", m_Status)
.Enum("oldstatus", m_OldStatus)
("crush", m_Crush)
("tag", m_Tag);
}
void DPlat::PlayPlatSound (const char *sound)
{
if (m_Sector->Flags & SECF_SILENTMOVE) return;
if (m_Sector->seqType >= 0)
{
SN_StartSequence (m_Sector, CHAN_FLOOR, m_Sector->seqType, SEQ_PLATFORM, 0);
}
else if (m_Sector->SeqName != NAME_None)
{
SN_StartSequence (m_Sector, CHAN_FLOOR, m_Sector->SeqName, 0);
}
else
{
SN_StartSequence (m_Sector, CHAN_FLOOR, sound, 0);
}
}
//
// Move a plat up and down
//
void DPlat::Tick ()
{
EMoveResult res;
switch (m_Status)
{
case up:
res = m_Sector->MoveFloor (m_Speed, m_High, m_Crush, 1, false);
if (res == EMoveResult::crushed && (m_Crush == -1))
{
m_Count = m_Wait;
m_Status = down;
PlayPlatSound ("Platform");
}
else if (res == EMoveResult::pastdest)
{
SN_StopSequence (m_Sector, CHAN_FLOOR);
if (m_Type != platToggle)
{
m_Count = m_Wait;
m_Status = waiting;
switch (m_Type)
{
case platRaiseAndStayLockout:
// Instead of keeping the dead thinker like Heretic did let's
// better use a flag to avoid problems elsewhere. For example,
// keeping the thinker would make tagwait wait indefinitely.
m_Sector->planes[sector_t::floor].Flags |= PLANEF_BLOCKED;
case platRaiseAndStay:
case platDownByValue:
case platDownWaitUpStay:
case platDownWaitUpStayStone:
case platUpByValueStay:
case platDownToNearestFloor:
case platDownToLowestCeiling:
Destroy ();
break;
default:
break;
}
}
else
{
m_OldStatus = m_Status; //jff 3/14/98 after action wait
m_Status = in_stasis; //for reactivation of toggle
}
}
break;
case down:
res = m_Sector->MoveFloor (m_Speed, m_Low, -1, -1, false);
if (res == EMoveResult::pastdest)
{
SN_StopSequence (m_Sector, CHAN_FLOOR);
// if not an instant toggle, start waiting
if (m_Type != platToggle) //jff 3/14/98 toggle up down
{ // is silent, instant, no waiting
m_Count = m_Wait;
m_Status = waiting;
switch (m_Type)
{
case platUpWaitDownStay:
case platUpNearestWaitDownStay:
case platUpByValue:
Destroy ();
break;
default:
break;
}
}
else
{ // instant toggles go into stasis awaiting next activation
m_OldStatus = m_Status; //jff 3/14/98 after action wait
m_Status = in_stasis; //for reactivation of toggle
}
}
else if (res == EMoveResult::crushed && m_Crush < 0 && m_Type != platToggle)
{
m_Status = up;
m_Count = m_Wait;
PlayPlatSound ("Platform");
}
//jff 1/26/98 remove the plat if it bounced so it can be tried again
//only affects plats that raise and bounce
// remove the plat if it's a pure raise type
switch (m_Type)
{
case platUpByValueStay:
case platRaiseAndStay:
case platRaiseAndStayLockout:
Destroy ();
default:
break;
}
break;
case waiting:
if (m_Count > 0 && !--m_Count)
{
if (m_Sector->floorplane.fD() == m_Low)
m_Status = up;
else
m_Status = down;
if (m_Type == platToggle)
SN_StartSequence (m_Sector, CHAN_FLOOR, "Silence", 0);
else
PlayPlatSound ("Platform");
}
break;
case in_stasis:
break;
}
}
DPlat::DPlat (sector_t *sector)
: DMovingFloor (sector)
{
}
//
// Do Platforms
// [RH] Changed amount to height and added delay,
// lip, change, tag, and speed parameters.
//
bool EV_DoPlat (int tag, line_t *line, DPlat::EPlatType type, double height,
double speed, int delay, int lip, int change)
{
DPlat *plat;
int secnum;
sector_t *sec;
bool rtn = false;
bool manual = false;
double newheight = 0;
vertex_t *spot;
if (tag != 0)
{
// Activate all <type> plats that are in_stasis
switch (type)
{
case DPlat::platToggle:
rtn = true;
case DPlat::platPerpetualRaise:
P_ActivateInStasis (tag);
break;
default:
break;
}
}
// [RH] If tag is zero, use the sector on the back side
// of the activating line (if any).
FSectorTagIterator itr(tag, line);
while ((secnum = itr.Next()) >= 0)
{
sec = &sectors[secnum];
if (sec->PlaneMoving(sector_t::floor))
{
continue;
}
// Find lowest & highest floors around sector
rtn = true;
plat = new DPlat (sec);
plat->m_Type = type;
plat->m_Crush = -1;
plat->m_Tag = tag;
plat->m_Speed = speed;
plat->m_Wait = delay;
//jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then
//going down forever -- default lower to plat height when triggered
plat->m_Low = sec->floorplane.fD();
if (change)
{
if (line)
sec->SetTexture(sector_t::floor, line->sidedef[0]->sector->GetTexture(sector_t::floor));
if (change == 1) sec->ClearSpecial();
}
switch (type)
{
case DPlat::platRaiseAndStay:
case DPlat::platRaiseAndStayLockout:
newheight = sec->FindNextHighestFloor (&spot);
plat->m_High = sec->floorplane.PointToDist (spot, newheight);
plat->m_Low = sec->floorplane.fD();
plat->m_Status = DPlat::up;
plat->PlayPlatSound ("Floor");
sec->ClearSpecial();
break;
case DPlat::platUpByValue:
case DPlat::platUpByValueStay:
newheight = sec->floorplane.ZatPoint (sec->centerspot) + height;
plat->m_High = sec->floorplane.PointToDist (sec->centerspot, newheight);
plat->m_Low = sec->floorplane.fD();
plat->m_Status = DPlat::up;
plat->PlayPlatSound ("Floor");
break;
case DPlat::platDownByValue:
newheight = sec->floorplane.ZatPoint (sec->centerspot) - height;
plat->m_Low = sec->floorplane.PointToDist (sec->centerspot, newheight);
plat->m_High = sec->floorplane.fD();
plat->m_Status = DPlat::down;
plat->PlayPlatSound ("Floor");
break;
case DPlat::platDownWaitUpStay:
case DPlat::platDownWaitUpStayStone:
newheight = sec->FindLowestFloorSurrounding (&spot) + lip;
plat->m_Low = sec->floorplane.PointToDist (spot, newheight);
if (plat->m_Low < sec->floorplane.fD())
plat->m_Low = sec->floorplane.fD();
plat->m_High = sec->floorplane.fD();
plat->m_Status = DPlat::down;
plat->PlayPlatSound (type == DPlat::platDownWaitUpStay ? "Platform" : "Floor");
break;
case DPlat::platUpNearestWaitDownStay:
newheight = sec->FindNextHighestFloor (&spot);
// Intentional fall-through
case DPlat::platUpWaitDownStay:
if (type == DPlat::platUpWaitDownStay)
{
newheight = sec->FindHighestFloorSurrounding (&spot);
}
plat->m_High = sec->floorplane.PointToDist (spot, newheight);
plat->m_Low = sec->floorplane.fD();
if (plat->m_High > sec->floorplane.fD())
plat->m_High = sec->floorplane.fD();
plat->m_Status = DPlat::up;
plat->PlayPlatSound ("Platform");
break;
case DPlat::platPerpetualRaise:
newheight = sec->FindLowestFloorSurrounding (&spot) + lip;
plat->m_Low = sec->floorplane.PointToDist (spot, newheight);
if (plat->m_Low < sec->floorplane.fD())
plat->m_Low = sec->floorplane.fD();
newheight = sec->FindHighestFloorSurrounding (&spot);
plat->m_High = sec->floorplane.PointToDist (spot, newheight);
if (plat->m_High > sec->floorplane.fD())
plat->m_High = sec->floorplane.fD();
plat->m_Status = pr_doplat() & 1 ? DPlat::up : DPlat::down;
plat->PlayPlatSound ("Platform");
break;
case DPlat::platToggle: //jff 3/14/98 add new type to support instant toggle
plat->m_Crush = 10; //jff 3/14/98 crush anything in the way
// set up toggling between ceiling, floor inclusive
newheight = sec->FindLowestCeilingPoint (&spot);
plat->m_Low = sec->floorplane.PointToDist (spot, newheight);
plat->m_High = sec->floorplane.fD();
plat->m_Status = DPlat::down;
SN_StartSequence (sec, CHAN_FLOOR, "Silence", 0);
break;
case DPlat::platDownToNearestFloor:
newheight = sec->FindNextLowestFloor (&spot) + lip;
plat->m_Low = sec->floorplane.PointToDist (spot, newheight);
plat->m_Status = DPlat::down;
plat->m_High = sec->floorplane.fD();
plat->PlayPlatSound ("Platform");
break;
case DPlat::platDownToLowestCeiling:
newheight = sec->FindLowestCeilingSurrounding (&spot);
plat->m_Low = sec->floorplane.PointToDist (spot, newheight);
plat->m_High = sec->floorplane.fD();
if (plat->m_Low < sec->floorplane.fD())
plat->m_Low = sec->floorplane.fD();
plat->m_Status = DPlat::down;
plat->PlayPlatSound ("Platform");
break;
default:
break;
}
}
return rtn;
}
void DPlat::Reactivate ()
{
if (m_Type == platToggle) //jff 3/14/98 reactivate toggle type
m_Status = m_OldStatus == up ? down : up;
else
m_Status = m_OldStatus;
}
void P_ActivateInStasis (int tag)
{
DPlat *scan;
TThinkerIterator<DPlat> iterator;
while ( (scan = iterator.Next ()) )
{
if (scan->m_Tag == tag && scan->m_Status == DPlat::in_stasis)
scan->Reactivate ();
}
}
void DPlat::Stop ()
{
m_OldStatus = m_Status;
m_Status = in_stasis;
}
void EV_StopPlat (int tag, bool remove)
{
DPlat *scan;
TThinkerIterator<DPlat> iterator;
scan = iterator.Next();
while (scan != nullptr)
{
DPlat *next = iterator.Next();
if (scan->m_Status != DPlat::in_stasis && scan->m_Tag == tag)
{
if (!remove) scan->Stop();
else scan->Destroy();
}
scan = next;
}
}