mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
282 lines
6 KiB
C++
282 lines
6 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 1998-2006 by Randy Heit (ZDoom).
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
|
//
|
|
// 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.
|
|
//
|
|
// DESCRIPTION:
|
|
// MapObj data. Map Objects or mobjs are actors, entities,
|
|
// thinker, take-your-pick... anything that moves, acts, or
|
|
// suffers state changes of more or less violent nature.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include "odamex.h"
|
|
|
|
|
|
#include "dthinker.h"
|
|
#include "z_zone.h"
|
|
#include "stats.h"
|
|
#include "p_local.h"
|
|
#include "g_musinfo.h"
|
|
|
|
IMPLEMENT_SERIAL (DThinker, DObject)
|
|
|
|
DThinker *DThinker::FirstThinker = NULL;
|
|
DThinker *DThinker::LastThinker = NULL;
|
|
|
|
std::vector<DThinker *> LingerDestroy;
|
|
|
|
void DThinker::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
|
|
// We do not serialize m_Next or m_Prev, because the DThinker
|
|
// constructor handles them for us.
|
|
}
|
|
|
|
void DThinker::SerializeAll (FArchive &arc, bool hubLoad)
|
|
{
|
|
DThinker *thinker;
|
|
if (arc.IsStoring ())
|
|
{
|
|
thinker = FirstThinker;
|
|
while (thinker)
|
|
{
|
|
if (!(arc.IsReset() && P_ThinkerIsPlayerType(thinker)))
|
|
{
|
|
arc << (byte)1;
|
|
arc << thinker;
|
|
}
|
|
thinker = thinker->m_Next;
|
|
}
|
|
arc << (byte)0;
|
|
}
|
|
else
|
|
{
|
|
if (hubLoad)
|
|
DestroyMostThinkers ();
|
|
else
|
|
DestroyAllThinkers ();
|
|
|
|
byte more;
|
|
arc >> more;
|
|
while (more)
|
|
{
|
|
DThinker *thinker;
|
|
arc >> thinker;
|
|
arc >> more;
|
|
}
|
|
|
|
// killough 3/26/98: Spawn icon landings:
|
|
P_SpawnBrainTargets ();
|
|
}
|
|
}
|
|
|
|
DThinker::DThinker ()
|
|
{
|
|
// Add a new thinker at the end of the list.
|
|
m_Prev = LastThinker;
|
|
m_Next = NULL;
|
|
if (LastThinker)
|
|
LastThinker->m_Next = this;
|
|
if (!FirstThinker)
|
|
FirstThinker = this;
|
|
LastThinker = this;
|
|
refCount = 0;
|
|
destroyed = false;
|
|
}
|
|
|
|
DThinker::~DThinker ()
|
|
{
|
|
}
|
|
|
|
// This method is necessary if you construct the Thinker in an unconventional way,
|
|
// like via a copy ctor. Otherwise, DThinker::Destroy() runs the risk of stomping
|
|
// all over the thinker list.
|
|
void DThinker::Orphan()
|
|
{
|
|
m_Next = NULL;
|
|
m_Prev = NULL;
|
|
refCount = 0;
|
|
}
|
|
|
|
void DThinker::Destroy ()
|
|
{
|
|
// denis - allow this function to be safely called multiple times
|
|
if(destroyed)
|
|
return;
|
|
|
|
if (FirstThinker == this)
|
|
FirstThinker = m_Next;
|
|
if (LastThinker == this)
|
|
LastThinker = m_Prev;
|
|
if (m_Next)
|
|
m_Next->m_Prev = m_Prev;
|
|
if (m_Prev)
|
|
m_Prev->m_Next = m_Next;
|
|
|
|
destroyed = true;
|
|
|
|
if(refCount)
|
|
{
|
|
LingerDestroy.push_back(this); // something is still finding this pointer useful
|
|
}
|
|
else
|
|
Super::Destroy ();
|
|
|
|
size_t l = LingerDestroy.size();
|
|
for(size_t i = 0; i < l; i++)
|
|
{
|
|
DThinker *obj = LingerDestroy[i];
|
|
if(!obj->refCount)
|
|
{
|
|
obj->ObjectFlags |= OF_Cleanup;
|
|
LingerDestroy.erase(LingerDestroy.begin() + i);
|
|
l--; i--;
|
|
delete obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DThinker::WasDestroyed ()
|
|
{
|
|
return destroyed;
|
|
}
|
|
|
|
// Destroy every thinker
|
|
void DThinker::DestroyAllThinkers ()
|
|
{
|
|
DThinker *currentthinker = FirstThinker;
|
|
while (currentthinker)
|
|
{
|
|
DThinker *next = currentthinker->m_Next;
|
|
currentthinker->Destroy ();
|
|
currentthinker = next;
|
|
}
|
|
DObject::EndFrame ();
|
|
|
|
for (DThinker *obj : LingerDestroy)
|
|
{
|
|
// if(!obj->refCount)
|
|
{
|
|
obj->ObjectFlags |= OF_Cleanup;
|
|
delete obj;
|
|
}
|
|
}
|
|
LingerDestroy.clear();
|
|
}
|
|
|
|
// Destroy all thinkers except for player-controlled actors
|
|
void DThinker::DestroyMostThinkers ()
|
|
{
|
|
DThinker *thinker = FirstThinker;
|
|
while (thinker)
|
|
{
|
|
DThinker *next = thinker->m_Next;
|
|
if (!thinker->IsKindOf (RUNTIME_CLASS (AActor)) ||
|
|
static_cast<AActor *>(thinker)->player == NULL ||
|
|
static_cast<AActor *>(thinker)->player->mo
|
|
!= static_cast<AActor *>(thinker))
|
|
{
|
|
thinker->Destroy ();
|
|
}
|
|
thinker = next;
|
|
}
|
|
DObject::EndFrame ();
|
|
}
|
|
|
|
//
|
|
// IndependentThinker
|
|
//
|
|
// Returns true if a DThinker object is ticked independently elsewhere.
|
|
// Returns false if it should be ticked in DThinker::RunThinkers
|
|
//
|
|
bool IndependentThinker(DThinker *thinker)
|
|
{
|
|
// Only have independent thinkers in client/server mode
|
|
if (!multiplayer || demoplayback)
|
|
return false;
|
|
|
|
if (thinker->IsKindOf (RUNTIME_CLASS (AActor)))
|
|
{
|
|
AActor *mobj = static_cast<AActor*>(thinker);
|
|
if (!mobj->player || mobj->player->spectator)
|
|
return false;
|
|
|
|
// Clientside prediction takes care of ticking
|
|
if (clientside)
|
|
return true;
|
|
|
|
// Server ticks players as it processes their ticcmds
|
|
if (serverside)
|
|
return true;
|
|
}
|
|
|
|
if (thinker->IsA(RUNTIME_CLASS (DPillar)) ||
|
|
thinker->IsA(RUNTIME_CLASS (DElevator)) ||
|
|
thinker->IsA(RUNTIME_CLASS (DFloor)) ||
|
|
thinker->IsA(RUNTIME_CLASS (DCeiling)) ||
|
|
thinker->IsA(RUNTIME_CLASS (DPlat)) ||
|
|
thinker->IsA(RUNTIME_CLASS (DDoor)))
|
|
{
|
|
// Client ticks movable sectors in prediction code
|
|
if (clientside)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void DThinker::RunThinkers ()
|
|
{
|
|
DThinker *currentthinker;
|
|
|
|
BEGIN_STAT (ThinkCycles);
|
|
currentthinker = FirstThinker;
|
|
while (currentthinker)
|
|
{
|
|
if (!IndependentThinker(currentthinker))
|
|
currentthinker->RunThink();
|
|
currentthinker = currentthinker->m_Next;
|
|
}
|
|
END_STAT (ThinkCycles);
|
|
P_CheckMusicChange();
|
|
}
|
|
|
|
void *DThinker::operator new (size_t size)
|
|
{
|
|
return Z_Malloc (size, PU_LEVSPEC, 0);
|
|
}
|
|
|
|
// Deallocation is lazy -- it will not actually be freed
|
|
// until its thinking turn comes up.
|
|
void DThinker::operator delete (void *mem)
|
|
{
|
|
Z_Free (mem);
|
|
}
|
|
|
|
bool P_ThinkerIsPlayerType(DThinker* thinker)
|
|
{
|
|
if (thinker == NULL)
|
|
return false;
|
|
|
|
return thinker->IsKindOf(RUNTIME_CLASS(AActor)) &&
|
|
static_cast<AActor*>(thinker)->type == MT_PLAYER;
|
|
}
|
|
|
|
VERSION_CONTROL (dthinker_cpp, "$Id$")
|