1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_tick.c 1512 2020-04-04 08:51:13Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2000 by DooM Legacy 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 //
20 // $Log: p_tick.c,v $
21 // Revision 1.6 2001/01/25 22:15:44 bpereira
22 // added heretic support
23 //
24 // Revision 1.5 2000/11/02 17:50:09 stroggonmeth
25 // Big 3Dfloors & FraggleScript commit!!
26 //
27 // Revision 1.4 2000/10/21 08:43:31 bpereira
28 // Revision 1.3 2000/10/08 13:30:01 bpereira
29 // Revision 1.2 2000/02/27 00:42:10 hurdler
30 // Revision 1.1.1.1 2000/02/22 20:32:32 hurdler
31 // Initial import into CVS (v1.29 pr3)
32 //
33 //
34 // DESCRIPTION:
35 // Archiving: SaveGame I/O.
36 // Thinker, Ticker.
37 //
38 //-----------------------------------------------------------------------------
39
40
41 #include "doomstat.h"
42 #include "p_tick.h"
43 #include "g_game.h"
44 #include "p_local.h"
45 #include "z_zone.h"
46 #include "t_script.h"
47
48
49
50 tic_t leveltime;
51
52 //
53 // THINKERS
54 // All thinkers should be allocated by Z_Malloc
55 // so they can be operated on uniformly.
56 // The actual structures will vary in size,
57 // but the first element must be thinker_t.
58 //
59
60
61
62 // Both the head and tail of the thinker list.
63 // Linked by next, prev.
64 thinker_t thinkercap;
65 // MBF, class-lists.
66 // Linked by cnext, cprev.
67 thinker_t thinkerclasscap[NUMTHCLASS];
68
69 #ifdef THINKER_INTERPOLATIONS
70 static byte newthinkerpresent = true;
71 #endif
72
73
74 //
75 // P_Init_Thinkers
76 //
P_Init_Thinkers(void)77 void P_Init_Thinkers (void)
78 {
79 int i;
80
81 // [WDJ] MBF, from MBF, PrBoom.
82 // Init all class-list.
83 for( i=0; i<NUMTHCLASS; i++ )
84 thinkerclasscap[i].cprev = thinkerclasscap[i].cnext = &thinkerclasscap[i];
85
86 thinkercap.prev = thinkercap.next = &thinkercap;
87 }
88
89
90
91
92 //
93 // P_AddThinker
94 // Adds a new thinker at the end of the list.
95 //
P_AddThinker(thinker_t * thinker)96 void P_AddThinker (thinker_t* thinker)
97 {
98 thinkercap.prev->next = thinker;
99 thinker->next = &thinkercap;
100 thinker->prev = thinkercap.prev;
101 thinkercap.prev = thinker;
102
103 // From MBF, PrBoom
104 #ifdef REFERENCE_COUNTING
105 thinker->references = 0; // killough 11/98: init reference counter to 0
106 #endif
107 // killough 8/29/98: init pointers, and then add to appropriate list
108 thinker->cnext = thinker->cprev = NULL; // init
109 P_UpdateClassThink(thinker, TH_unknown);
110
111 #ifdef THINKER_INTERPOLATIONS
112 newthinkerpresent = true;
113 #endif
114 }
115
116
117 //
118 // P_RemoveThinker
119 // Deallocation is lazy -- it will not actually be freed
120 // until its thinking turn comes up.
121 //
P_RemoveThinker(thinker_t * thinker)122 void P_RemoveThinker (thinker_t* thinker)
123 {
124 // Setup an action function that does removal.
125 thinker->function.acp1 = (actionf_p1) T_RemoveThinker;
126
127 // [WDJ] MBF, from MBF, PrBoom
128 // killough 8/29/98: remove immediately from class-list
129
130 // haleyjd 11/09/06: NO! Doing this here was always suspect to me, and
131 // sure enough: if a monster's removed at the wrong time, it gets put
132 // back into the list improperly and starts causing an infinite loop in
133 // the AI code. We'll follow PrBoom's lead and create a th_delete class
134 // for thinkers awaiting deferred removal.
135
136 // [WDJ] Being in a delete list does nothing to stop being found.
137 // Delete class links, and let acp1 block linking again.
138 P_UpdateClassThink( thinker, TH_delete );
139 }
140
141 // Thinker function that removes the thinker.
142 // In PrBoom, MBF, this is called P_RemoveThinkerDelayed, and is more
143 // complicated, using reference counts and modifying currentthinker.
144 // Our RunThinker handles removal better.
T_RemoveThinker(thinker_t * remthink)145 void T_RemoveThinker( thinker_t* remthink )
146 {
147 // [WDJ] MBF, from MBF, PrBoom
148 #ifdef REFERENCE_COUNTING
149 if( remthink->references ) return;
150 #endif
151
152 // Remove from current class-list, if in one.
153 P_UpdateClassThink( remthink, TH_none );
154
155 // Unlink and delete the thinker
156 remthink->next->prev = remthink->prev;
157 remthink->prev->next = remthink->next;
158 Z_Free (remthink); // mobj, etc.
159 }
160
161
162 // [WDJ] MBF, from MBF, PrBoom, EternityEngine.
163 // killough 8/29/98:
164 // [WDJ] Heavily rewritten to eliminate unused class-lists.
165 // Make readable.
166 //
167 // Maintain separate class-lists of friends and enemies, to permit more
168 // efficient searches.
169 //
170
P_UpdateClassThink(thinker_t * thinker,int tclass)171 void P_UpdateClassThink(thinker_t *thinker, int tclass )
172 {
173 register thinker_t * th;
174
175 if( tclass == TH_unknown )
176 {
177 // Find the class where the thinker belongs.
178 // Most common case first.
179 tclass = TH_misc;
180 if( thinker->function.acp1 == (actionf_p1)P_MobjThinker )
181 {
182 register mobj_t * mo = (mobj_t *) thinker;
183 if( mo->health > 0
184 && ( mo->flags & MF_COUNTKILL || mo->type == MT_SKULL) )
185 {
186 tclass = (mo->flags & MF_FRIEND)? TH_friends : TH_enemies;
187 }
188 }
189 }
190
191 // Remove from current class-list, if in one.
192 th = thinker->cnext;
193 if( th != NULL)
194 {
195 th->cprev = thinker->cprev;
196 th->cprev->cnext = th;
197 }
198
199 // Prevent linking dead mobj.
200 if( thinker->function.acp1 == (actionf_p1)T_RemoveThinker
201 || tclass >= NUMTHCLASS ) // TH_none, etc.
202 {
203 // Not in any class-list.
204 // Prevent unlinking again.
205 thinker->cprev = thinker->cnext = NULL;
206 return;
207 }
208
209 // Add to the appropriate class-list.
210 th = &thinkerclasscap[tclass];
211 thinker->cnext = th;
212 thinker->cprev = th->cprev;
213 th->cprev->cnext = thinker;
214 th->cprev = thinker;
215 return;
216
217 }
218
219 // Move to be first or last.
220 // first : 0=last, 1=first.
P_MoveClassThink(thinker_t * thinker,byte first)221 void P_MoveClassThink(thinker_t *thinker, byte first)
222 {
223 register thinker_t * th;
224
225 // Remove from current thread, if in one.
226 th = thinker->cnext;
227 if( th != NULL)
228 {
229 th->cprev = thinker->cprev;
230 thinker->cprev->cnext = th;
231 }
232
233 // prevent linking dead mobj
234 if( thinker->function.acp1 == (actionf_p1)T_RemoveThinker )
235 {
236 // Not in any class-list.
237 // Prevent unlinking again.
238 thinker->cprev = thinker->cnext = NULL;
239 return;
240 }
241
242 // Add to appropriate thread list.
243 register mobj_t * mo = (mobj_t *) thinker;
244 th = &thinkerclasscap[ (mo->flags & MF_FRIEND)? TH_friends : TH_enemies ];
245 if( first )
246 {
247 thinker->cprev = th;
248 thinker->cnext = th->cnext;
249 th->cnext->cprev = thinker;
250 th->cnext = thinker;
251 }
252 else
253 { // Last
254 thinker->cnext = th;
255 thinker->cprev = th->cprev;
256 th->cprev->cnext = thinker;
257 th->cprev = thinker;
258 }
259 }
260
261
262 // Move range cap to th, to be last in class-list.
263 // cap: is a class-list.
264 // thnext: becomes new first in class-list.
P_MoveClasslistRangeLast(thinker_t * cap,thinker_t * thnext)265 void P_MoveClasslistRangeLast( thinker_t * cap, thinker_t * thnext )
266 {
267 // cap is head of a class-list.
268 // Link first in class-list to end of class-list.
269 cap->cnext->cprev = cap->cprev;
270 cap->cprev->cnext = cap->cnext;
271 // Break list before th. Make thnext first in class-list.
272 register thinker_t * tp = thnext->cprev;
273 cap->cprev = tp;
274 tp->cnext = cap;
275 thnext->cprev = cap;
276 cap->cnext = thnext;
277 }
278
279
280 #ifdef REFERENCE_COUNTING
281 //
282 // P_SetReference
283 //
284 // This function is used to keep track of pointer references to mobj thinkers.
285 // In Doom, objects such as lost souls could sometimes be removed despite
286 // their still being referenced. In Boom, 'target' mobj fields were tested
287 // during each gametic, and any objects pointed to by them would be prevented
288 // from being removed. But this was incomplete, and was slow (every mobj was
289 // checked during every gametic). Now, we keep a count of the number of
290 // references, and delay removal until the count is 0.
291
292 // rm_mo: remove reference
293 // add_mo: add reference
P_SetReference(mobj_t * rm_mo,mobj_t * add_mo)294 void P_SetReference(mobj_t *rm_mo, mobj_t *add_mo)
295 {
296 if(rm_mo) // If there was a target already, decrease its refcount
297 rm_mo->thinker.references--;
298 if(targ) // Set new target and if non-NULL, increase its counter
299 add_mo->thinker.references++;
300 }
301 #endif
302
303
304 //
305 // P_RunThinkers
306 //
P_RunThinkers(void)307 void P_RunThinkers (void)
308 {
309 thinker_t* currentthinker;
310 thinker_t* next_thinker;
311
312 currentthinker = thinkercap.next;
313 while (currentthinker != &thinkercap)
314 {
315 next_thinker = currentthinker->next; // because of T_RemoveThinker
316 #ifdef THINKER_INTERPOLATIONS
317 if (newthinkerpresent)
318 R_ActivateThinkerInterpolations(currentthinker);
319 #endif
320 if (currentthinker->function.acp1)
321 {
322 currentthinker->function.acp1 (currentthinker);
323 }
324 currentthinker = next_thinker;
325 }
326 #ifdef THINKER_INTERPOLATIONS
327 newthinkerpresent = false;
328 #endif
329 }
330
331
332
333 //
334 // P_Ticker
335 //
336
P_Ticker(void)337 void P_Ticker (void)
338 {
339 int i;
340
341 // [WDJ] From PrBoom, adapted to game_comp_tic.
342 // Pause if in menu and at least one tic has been run.
343 // killough 9/29/98: note that this ties in with basetic,
344 // since G_Ticker does the pausing during recording or playback,
345 // and compensates by incrementing basetic (not incrementing game_comp_tic).
346 // All of this complicated mess is used to preserve demo sync.
347 // PrBoom and EternityEngine test (players[consoleplayer].viewz != 1)
348 // as test that one tic has run.
349 // Heretic only has paused.
350 if (paused
351 || (menuactive && !netgame && !demoplayback
352 && (players[consoleplayer].viewz != 1) ))
353 return;
354
355 #ifdef THINKER_INTERPOLATIONS
356 R_UpdateInterpolations();
357 #endif
358
359 // From PrBoom, EternityEngine, may affect demo sync.
360 // Not if this is an intermission screen.
361 if( gamestate == GS_LEVEL || gamestate == GS_DEDICATEDSERVER )
362 {
363 for (i=0 ; i<MAXPLAYERS ; i++)
364 {
365 if (playeringame[i])
366 P_PlayerThink (&players[i]);
367 }
368 }
369
370 P_RunThinkers ();
371 P_UpdateSpecials ();
372 if( cv_itemrespawn.EV ) P_RespawnSpecials ();
373
374 P_AmbientSound();
375
376 // for par times
377 leveltime++;
378
379 #ifdef FRAGGLESCRIPT
380 // SoM: Update FraggleScript...
381 T_DelayedScripts();
382 #endif
383 }
384