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