// 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 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(thinker)->player == NULL || static_cast(thinker)->player->mo != static_cast(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(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(thinker)->type == MT_PLAYER; } VERSION_CONTROL (dthinker_cpp, "$Id$")