1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: dthinker.cpp 4469 2014-01-03 23:38:29Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	MapObj data. Map Objects or mobjs are actors, entities,
21 //	thinker, take-your-pick... anything that moves, acts, or
22 //	suffers state changes of more or less violent nature.
23 //
24 //-----------------------------------------------------------------------------
25 
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include "doomstat.h"
31 #include "dthinker.h"
32 #include "z_zone.h"
33 #include "stats.h"
34 #include "p_local.h"
35 
36 IMPLEMENT_SERIAL (DThinker, DObject)
37 
38 DThinker *DThinker::FirstThinker = NULL;
39 DThinker *DThinker::LastThinker = NULL;
40 
41 std::vector<DThinker *> LingerDestroy;
42 
Serialize(FArchive & arc)43 void DThinker::Serialize (FArchive &arc)
44 {
45 	Super::Serialize (arc);
46 
47 	// We do not serialize m_Next or m_Prev, because the DThinker
48 	// constructor handles them for us.
49 }
50 
SerializeAll(FArchive & arc,bool hubLoad,bool noStorePlayers)51 void DThinker::SerializeAll (FArchive &arc, bool hubLoad, bool noStorePlayers)
52 {
53 	DThinker *thinker;
54 	if (arc.IsStoring () && noStorePlayers)
55 	{
56 		thinker = FirstThinker;
57 		while (thinker)
58 		{
59 			// Don't store player mobjs.
60 			if (!(thinker->IsKindOf(RUNTIME_CLASS(AActor)) &&
61 			    static_cast<AActor *>(thinker)->type == MT_PLAYER))
62 			{
63 				arc << (BYTE)1;
64 				arc << thinker;
65 			}
66 			thinker = thinker->m_Next;
67 		}
68 		arc << (BYTE)0;
69 	}
70 	else if (arc.IsStoring ())
71 	{
72 		thinker = FirstThinker;
73 		while (thinker)
74 		{
75 			arc << (BYTE)1;
76 			arc << thinker;
77 			thinker = thinker->m_Next;
78 		}
79 		arc << (BYTE)0;
80 	}
81 	else
82 	{
83 		if (hubLoad || noStorePlayers)
84 			DestroyMostThinkers ();
85 		else
86 			DestroyAllThinkers ();
87 
88 		BYTE more;
89 		arc >> more;
90 		while (more)
91 		{
92 			DThinker *thinker;
93 			arc >> thinker;
94 			arc >> more;
95 		}
96 
97 		// killough 3/26/98: Spawn icon landings:
98 		P_SpawnBrainTargets ();
99 	}
100 }
101 
DThinker()102 DThinker::DThinker ()
103 {
104 	// Add a new thinker at the end of the list.
105 	m_Prev = LastThinker;
106 	m_Next = NULL;
107 	if (LastThinker)
108 		LastThinker->m_Next = this;
109 	if (!FirstThinker)
110 		FirstThinker = this;
111 	LastThinker = this;
112 	refCount = 0;
113 	destroyed = false;
114 }
115 
~DThinker()116 DThinker::~DThinker ()
117 {
118 }
119 
Destroy()120 void DThinker::Destroy ()
121 {
122 	// denis - allow this function to be safely called multiple times
123 	if(destroyed)
124 		return;
125 
126 	if (FirstThinker == this)
127 		FirstThinker = m_Next;
128 	if (LastThinker == this)
129 		LastThinker = m_Prev;
130 	if (m_Next)
131 		m_Next->m_Prev = m_Prev;
132 	if (m_Prev)
133 		m_Prev->m_Next = m_Next;
134 
135 	destroyed = true;
136 
137 	if(refCount)
138 	{
139 		LingerDestroy.push_back(this); // something is still finding this pointer useful
140 	}
141 	else
142 		Super::Destroy ();
143 
144 	size_t l = LingerDestroy.size();
145 	for(size_t i = 0; i < l; i++)
146 	{
147 		DThinker *obj = LingerDestroy[i];
148 		if(!obj->refCount)
149 		{
150 			obj->ObjectFlags |= OF_Cleanup;
151 			LingerDestroy.erase(LingerDestroy.begin() + i);
152 			l--; i--;
153 			delete obj;
154 		}
155 	}
156 }
157 
WasDestroyed()158 bool DThinker::WasDestroyed ()
159 {
160 	return destroyed;
161 }
162 
163 // Destroy every thinker
DestroyAllThinkers()164 void DThinker::DestroyAllThinkers ()
165 {
166 	DThinker *currentthinker = FirstThinker;
167 	while (currentthinker)
168 	{
169 		DThinker *next = currentthinker->m_Next;
170 		currentthinker->Destroy ();
171 		currentthinker = next;
172 	}
173 	DObject::EndFrame ();
174 
175 	size_t l = LingerDestroy.size();
176 	for(size_t i = 0; i < l; i++)
177 	{
178 		DThinker *obj = LingerDestroy[i];
179 //		if(!obj->refCount)
180 		{
181 			obj->ObjectFlags |= OF_Cleanup;
182 			delete obj;
183 		}
184 	}
185 	LingerDestroy.clear();
186 }
187 
188 // Destroy all thinkers except for player-controlled actors
DestroyMostThinkers()189 void DThinker::DestroyMostThinkers ()
190 {
191 	DThinker *thinker = FirstThinker;
192 	while (thinker)
193 	{
194 		DThinker *next = thinker->m_Next;
195 		if (!thinker->IsKindOf (RUNTIME_CLASS (AActor)) ||
196 			static_cast<AActor *>(thinker)->player == NULL ||
197 			static_cast<AActor *>(thinker)->player->mo
198 			 != static_cast<AActor *>(thinker))
199 		{
200 			thinker->Destroy ();
201 		}
202 		thinker = next;
203 	}
204 	DObject::EndFrame ();
205 }
206 
EXTERN_CVAR(sv_speedhackfix)207 EXTERN_CVAR (sv_speedhackfix)
208 
209 //
210 // IndependentThinker
211 //
212 // Returns true if a DThinker object is ticked independently elsewhere.
213 // Returns false if it should be ticked in DThinker::RunThinkers
214 //
215 bool IndependentThinker(DThinker *thinker)
216 {
217 	// Only have independent thinkers in client/server mode
218 	if (!multiplayer || demoplayback)
219 		return false;
220 
221 	if (thinker->IsKindOf (RUNTIME_CLASS (AActor)))
222 	{
223 		AActor *mobj = static_cast<AActor*>(thinker);
224 		if (!mobj->player || mobj->player->spectator)
225 			return false;
226 
227 		// Clientside prediction takes care of ticking
228 		if (clientside)
229 			return true;
230 
231 		// Server ticks players as it processes their ticcmds
232 		if (serverside)
233 			return true;
234 	}
235 
236 	if (thinker->IsA(RUNTIME_CLASS (DPillar)) ||
237 		thinker->IsA(RUNTIME_CLASS (DElevator)) ||
238 		thinker->IsA(RUNTIME_CLASS (DFloor)) ||
239 		thinker->IsA(RUNTIME_CLASS (DCeiling)) ||
240 		thinker->IsA(RUNTIME_CLASS (DPlat)) ||
241 		thinker->IsA(RUNTIME_CLASS (DDoor)))
242 	{
243 		// Client ticks movable sectors in prediction code
244 		if (clientside)
245 			return true;
246 	}
247 
248 	return false;
249 }
250 
251 
RunThinkers()252 void DThinker::RunThinkers ()
253 {
254 	DThinker *currentthinker;
255 
256 	BEGIN_STAT (ThinkCycles);
257 	currentthinker = FirstThinker;
258 	while (currentthinker)
259 	{
260 		if (!IndependentThinker(currentthinker))
261 			currentthinker->RunThink();
262 		currentthinker = currentthinker->m_Next;
263 	}
264 	END_STAT (ThinkCycles);
265 }
266 
operator new(size_t size)267 void *DThinker::operator new (size_t size)
268 {
269 	return Z_Malloc (size, PU_LEVSPEC, 0);
270 }
271 
272 // Deallocation is lazy -- it will not actually be freed
273 // until its thinking turn comes up.
operator delete(void * mem)274 void DThinker::operator delete (void *mem)
275 {
276 	Z_Free (mem);
277 }
278 
279 VERSION_CONTROL (dthinker_cpp, "$Id: dthinker.cpp 4469 2014-01-03 23:38:29Z dr_sean $")
280 
281