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