1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000,2002 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Thinker, Ticker.
31 *
32 *-----------------------------------------------------------------------------*/
33
34 #include "doomstat.h"
35 #include "p_user.h"
36 #include "p_spec.h"
37 #include "p_tick.h"
38 #include "p_map.h"
39 #include "r_fps.h"
40 #include "u_musinfo.h"
41
42 int leveltime;
43
44 static dbool newthinkerpresent;
45
46 //
47 // THINKERS
48 // All thinkers should be allocated by Z_Malloc
49 // so they can be operated on uniformly.
50 // The actual structures will vary in size,
51 // but the first element must be thinker_t.
52 //
53
54 // killough 8/29/98: we maintain several separate threads, each containing
55 // a special class of thinkers, to allow more efficient searches.
56 thinker_t thinkerclasscap[th_all+1];
57
58 /*
59 ===============
60 =
61 = P_InitThinkers
62 =
63 ===============
64 */
65
P_InitThinkers(void)66 void P_InitThinkers(void)
67 {
68 int i;
69
70 for (i=0; i<NUMTHCLASS; i++) // killough 8/29/98: initialize threaded lists
71 thinkerclasscap[i].cprev = thinkerclasscap[i].cnext = &thinkerclasscap[i];
72
73 thinkercap.prev = thinkercap.next = &thinkercap;
74 }
75
76 //
77 // killough 8/29/98:
78 //
79 // We maintain separate threads of friends and enemies, to permit more
80 // efficient searches.
81 //
82
P_UpdateThinker(thinker_t * thinker)83 void P_UpdateThinker(thinker_t *thinker)
84 {
85 thinker_t *th = NULL;
86 // find the class the thinker belongs to
87
88 int class =
89 thinker->function == P_RemoveThinkerDelayed ? th_delete :
90 thinker->function == P_MobjThinker &&
91 ((mobj_t *) thinker)->health > 0 &&
92 (((mobj_t *) thinker)->flags & MF_ISMONSTER) ?
93 ((mobj_t *) thinker)->flags & MF_FRIEND ?
94 th_friends : th_enemies : th_misc;
95
96 {
97 /* Remove from current thread, if in one */
98 if ((th = thinker->cnext)!= NULL)
99 (th->cprev = thinker->cprev)->cnext = th;
100 }
101
102 // Add to appropriate thread
103 th = &thinkerclasscap[class];
104 th->cprev->cnext = thinker;
105 thinker->cnext = th;
106 thinker->cprev = th->cprev;
107 th->cprev = thinker;
108 }
109
110 /*
111 ===============
112 =
113 = P_AddThinker
114 =
115 = Adds a new thinker at the end of the list
116 =
117 ===============
118 */
119
P_AddThinker(thinker_t * thinker)120 void P_AddThinker(thinker_t* thinker)
121 {
122 thinkercap.prev->next = thinker;
123 thinker->next = &thinkercap;
124 thinker->prev = thinkercap.prev;
125 thinkercap.prev = thinker;
126
127 thinker->references = 0; // killough 11/98: init reference counter to 0
128
129 // killough 8/29/98: set sentinel pointers, and then add to appropriate list
130 thinker->cnext = NULL;
131 thinker->cprev = NULL;
132 P_UpdateThinker(thinker);
133 newthinkerpresent = TRUE;
134 }
135
136 //
137 // killough 11/98:
138 //
139 // Make currentthinker external, so that P_RemoveThinkerDelayed
140 // can adjust currentthinker when thinkers self-remove.
141
142 static thinker_t *currentthinker;
143
144 //
145 // P_RemoveThinkerDelayed()
146 //
147 // Called automatically as part of the thinker loop in P_RunThinkers(),
148 // on nodes which are pending deletion.
149 //
150 // If this thinker has no more pointers referencing it indirectly,
151 // remove it, and set currentthinker to one node preceeding it, so
152 // that the next step in P_RunThinkers() will get its successor.
153 //
154
P_RemoveThinkerDelayed(thinker_t * thinker)155 void P_RemoveThinkerDelayed(thinker_t *thinker)
156 {
157 thinker_t *next = NULL;
158 thinker_t *th = NULL;
159 if (thinker->references)
160 return;
161
162 /* Remove from main thinker list */
163 next = thinker->next;
164 /* Note that currentthinker is guaranteed to point to us,
165 * and since we're freeing our memory, we had better change that. So
166 * point it to thinker->prev, so the iterator will correctly move on to
167 * thinker->prev->next = thinker->next */
168 (next->prev = currentthinker = thinker->prev)->next = next;
169
170 /* Remove from current thinker class list */
171 th = thinker->cnext;
172 (th->cprev = thinker->cprev)->cnext = th;
173 Z_Free(thinker);
174 }
175
176 //
177 // P_RemoveThinker
178 //
179 // Deallocation is lazy -- it will not actually be freed
180 // until its thinking turn comes up.
181 //
182 // killough 4/25/98:
183 //
184 // Instead of marking the function with -1 value cast to a function pointer,
185 // set the function to P_RemoveThinkerDelayed(), so that later, it will be
186 // removed automatically as part of the thinker process.
187 //
188
P_RemoveThinker(thinker_t * thinker)189 void P_RemoveThinker(thinker_t *thinker)
190 {
191 R_StopInterpolationIfNeeded(thinker);
192 thinker->function = P_RemoveThinkerDelayed;
193
194 P_UpdateThinker(thinker);
195 }
196
197 /* cph 2002/01/13 - iterator for thinker list
198 * WARNING: Do not modify thinkers between calls to this functin
199 */
P_NextThinker(thinker_t * th,th_class cl)200 thinker_t* P_NextThinker(thinker_t* th, th_class cl)
201 {
202 thinker_t* top = &thinkerclasscap[cl];
203 if (!th)
204 th = top;
205
206 if (cl == th_all)
207 th = th->next;
208 else
209 th = th->cnext;
210
211 if (th != top)
212 return th;
213 return NULL;
214 }
215
216 /*
217 * P_SetTarget
218 *
219 * This function is used to keep track of pointer references to mobj thinkers.
220 * In Doom, objects such as lost souls could sometimes be removed despite
221 * their still being referenced. In Boom, 'target' mobj fields were tested
222 * during each gametic, and any objects pointed to by them would be prevented
223 * from being removed. But this was incomplete, and was slow (every mobj was
224 * checked during every gametic). Now, we keep a count of the number of
225 * references, and delay removal until the count is 0.
226 */
227
P_SetTarget(mobj_t ** mop,mobj_t * targ)228 void P_SetTarget(mobj_t **mop, mobj_t *targ)
229 {
230 if (*mop) // If there was a target already, decrease its refcount
231 (*mop)->thinker.references--;
232 if ((*mop = targ)) // Set new target and if non-NULL, increase its counter
233 targ->thinker.references++;
234 }
235
236 // Process each thinker. For thinkers which are marked deleted, we must
237 // load the "next" pointer prior to freeing the node. In Doom, the "next"
238 // pointer was loaded AFTER the thinker was freed, which could have caused
239 // crashes.
240 //
241 // But if we are not deleting the thinker, we should reload the "next"
242 // pointer after calling the function, in case additional thinkers are
243 // added at the end of the list.
244
245 /*
246 ===============
247 =
248 = P_RunThinkers
249 =
250 ===============
251 */
252
P_RunThinkers(void)253 static void P_RunThinkers (void)
254 {
255 for (currentthinker = thinkercap.next;
256 currentthinker != &thinkercap;
257 currentthinker = currentthinker->next)
258 {
259 if (newthinkerpresent)
260 R_ActivateThinkerInterpolations(currentthinker);
261 if (currentthinker->function)
262 currentthinker->function(currentthinker);
263 }
264 newthinkerpresent = FALSE;
265
266 // Dedicated thinkers
267 P_MapMusicThinker();
268 }
269
270 /*
271 =================
272 =
273 = P_Ticker
274 =
275 =================
276 */
277
P_Ticker(void)278 void P_Ticker (void)
279 {
280 int i;
281
282 /* pause if in menu and at least one tic has been run
283 *
284 * killough 9/29/98: note that this ties in with basetic,
285 * since G_Ticker does the pausing during recording or
286 * playback, and compenates by incrementing basetic.
287 *
288 * All of this complicated mess is used to preserve demo sync.
289 */
290
291 if (paused || (menuactive && !demoplayback && !netgame &&
292 players[consoleplayer].viewz != 1))
293 return;
294
295 R_UpdateInterpolations ();
296
297 P_MapStart();
298 // not if this is an intermission screen
299 if(gamestate==GS_LEVEL)
300 for (i=0; i<MAXPLAYERS; i++)
301 if (playeringame[i])
302 P_PlayerThink(&players[i]);
303
304 P_RunThinkers();
305 P_UpdateSpecials();
306 P_RespawnSpecials();
307 P_MapEnd();
308 leveltime++; // for par times
309 }
310