1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  p_tick.c
12 /// \brief Archiving: SaveGame I/O, Thinker, Ticker
13 
14 #include "doomstat.h"
15 #include "g_game.h"
16 #include "p_local.h"
17 #include "z_zone.h"
18 #include "s_sound.h"
19 #include "st_stuff.h"
20 #include "p_polyobj.h"
21 #include "m_random.h"
22 #include "lua_script.h"
23 #include "lua_hook.h"
24 #include "m_perfstats.h"
25 #include "i_system.h" // I_GetPreciseTime
26 
27 // Object place
28 #include "m_cheat.h"
29 
30 tic_t leveltime;
31 
32 //
33 // THINKERS
34 // All thinkers should be allocated by Z_Calloc
35 // so they can be operated on uniformly.
36 // The actual structures will vary in size,
37 // but the first element must be thinker_t.
38 //
39 
40 // The entries will behave like both the head and tail of the lists.
41 thinker_t thlist[NUM_THINKERLISTS];
42 
Command_Numthinkers_f(void)43 void Command_Numthinkers_f(void)
44 {
45 	INT32 num;
46 	INT32 count = 0;
47 	actionf_p1 action;
48 	thinker_t *think;
49 	thinklistnum_t start = 0;
50 	thinklistnum_t end = NUM_THINKERLISTS - 1;
51 	thinklistnum_t i;
52 
53 	if (gamestate != GS_LEVEL)
54 	{
55 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
56 		return;
57 	}
58 
59 	if (COM_Argc() < 2)
60 	{
61 		CONS_Printf(M_GetText("numthinkers <#>: Count number of thinkers\n"));
62 		CONS_Printf(
63 			"\t1: P_MobjThinker\n"
64 			/*"\t2: P_RainThinker\n"
65 			"\t3: P_SnowThinker\n"*/
66 			"\t2: P_NullPrecipThinker\n"
67 			"\t3: T_Friction\n"
68 			"\t4: T_Pusher\n"
69 			"\t5: P_RemoveThinkerDelayed\n");
70 		return;
71 	}
72 
73 	num = atoi(COM_Argv(1));
74 
75 	switch (num)
76 	{
77 		case 1:
78 			start = end = THINK_MOBJ;
79 			action = (actionf_p1)P_MobjThinker;
80 			CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker");
81 			break;
82 		/*case 2:
83 			action = (actionf_p1)P_RainThinker;
84 			CONS_Printf(M_GetText("Number of %s: "), "P_RainThinker");
85 			break;
86 		case 3:
87 			action = (actionf_p1)P_SnowThinker;
88 			CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker");
89 			break;*/
90 		case 2:
91 			start = end = THINK_PRECIP;
92 			action = (actionf_p1)P_NullPrecipThinker;
93 			CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker");
94 			break;
95 		case 3:
96 			start = end = THINK_MAIN;
97 			action = (actionf_p1)T_Friction;
98 			CONS_Printf(M_GetText("Number of %s: "), "T_Friction");
99 			break;
100 		case 4:
101 			start = end = THINK_MAIN;
102 			action = (actionf_p1)T_Pusher;
103 			CONS_Printf(M_GetText("Number of %s: "), "T_Pusher");
104 			break;
105 		case 5:
106 			action = (actionf_p1)P_RemoveThinkerDelayed;
107 			CONS_Printf(M_GetText("Number of %s: "), "P_RemoveThinkerDelayed");
108 			break;
109 		default:
110 			CONS_Printf(M_GetText("That is not a valid number.\n"));
111 			return;
112 	}
113 
114 	for (i = start; i <= end; i++)
115 	{
116 		for (think = thlist[i].next; think != &thlist[i]; think = think->next)
117 		{
118 			if (think->function.acp1 != action)
119 				continue;
120 
121 			count++;
122 		}
123 	}
124 
125 	CONS_Printf("%d\n", count);
126 }
127 
Command_CountMobjs_f(void)128 void Command_CountMobjs_f(void)
129 {
130 	thinker_t *th;
131 	mobjtype_t i;
132 	INT32 count;
133 
134 	if (gamestate != GS_LEVEL)
135 	{
136 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
137 		return;
138 	}
139 
140 	if (COM_Argc() >= 2)
141 	{
142 		size_t j;
143 		for (j = 1; j < COM_Argc(); j++)
144 		{
145 			i = atoi(COM_Argv(j));
146 			if (i >= NUMMOBJTYPES)
147 			{
148 				CONS_Printf(M_GetText("Object number %d out of range (max %d).\n"), i, NUMMOBJTYPES-1);
149 				continue;
150 			}
151 
152 			count = 0;
153 
154 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
155 			{
156 				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
157 					continue;
158 
159 				if (((mobj_t *)th)->type == i)
160 					count++;
161 			}
162 
163 			CONS_Printf(M_GetText("There are %d objects of type %d currently in the level.\n"), count, i);
164 		}
165 		return;
166 	}
167 
168 	CONS_Printf(M_GetText("Count of active objects in level:\n"));
169 
170 	for (i = 0; i < NUMMOBJTYPES; i++)
171 	{
172 		count = 0;
173 
174 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
175 		{
176 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
177 				continue;
178 
179 			if (((mobj_t *)th)->type == i)
180 				count++;
181 		}
182 
183 		if (count > 0) // Don't bother displaying if there are none of this type!
184 			CONS_Printf(" * %d: %d\n", i, count);
185 	}
186 }
187 
188 //
189 // P_InitThinkers
190 //
P_InitThinkers(void)191 void P_InitThinkers(void)
192 {
193 	UINT8 i;
194 	for (i = 0; i < NUM_THINKERLISTS; i++)
195 		thlist[i].prev = thlist[i].next = &thlist[i];
196 }
197 
198 // Adds a new thinker at the end of the list.
P_AddThinker(const thinklistnum_t n,thinker_t * thinker)199 void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
200 {
201 #ifdef PARANOIA
202 	I_Assert(n < NUM_THINKERLISTS);
203 #endif
204 
205 	thlist[n].prev->next = thinker;
206 	thinker->next = &thlist[n];
207 	thinker->prev = thlist[n].prev;
208 	thlist[n].prev = thinker;
209 
210 	thinker->references = 0;    // killough 11/98: init reference counter to 0
211 }
212 
213 //
214 // killough 11/98:
215 //
216 // Make currentthinker external, so that P_RemoveThinkerDelayed
217 // can adjust currentthinker when thinkers self-remove.
218 
219 static thinker_t *currentthinker;
220 
221 //
222 // P_RemoveThinkerDelayed()
223 //
224 // Called automatically as part of the thinker loop in P_RunThinkers(),
225 // on nodes which are pending deletion.
226 //
227 // If this thinker has no more pointers referencing it indirectly,
228 // remove it, and set currentthinker to one node preceeding it, so
229 // that the next step in P_RunThinkers() will get its successor.
230 //
P_RemoveThinkerDelayed(thinker_t * thinker)231 void P_RemoveThinkerDelayed(thinker_t *thinker)
232 {
233 	thinker_t *next;
234 #ifdef PARANOIA
235 #define BEENAROUNDBIT (0x40000000) // has to be sufficiently high that it's unlikely to happen in regular gameplay. If you change this, pay attention to the bit pattern of INT32_MIN.
236 	if (thinker->references & ~BEENAROUNDBIT)
237 	{
238 		if (thinker->references & BEENAROUNDBIT) // Usually gets cleared up in one frame; what's going on here, then?
239 			CONS_Printf("Number of potentially faulty references: %d\n", (thinker->references & ~BEENAROUNDBIT));
240 		thinker->references |= BEENAROUNDBIT;
241 		return;
242 	}
243 #undef BEENAROUNDBIT
244 #else
245 	if (thinker->references)
246 		return;
247 #endif
248 
249 	/* Remove from main thinker list */
250 	next = thinker->next;
251 	/* Note that currentthinker is guaranteed to point to us,
252 	* and since we're freeing our memory, we had better change that. So
253 	* point it to thinker->prev, so the iterator will correctly move on to
254 	* thinker->prev->next = thinker->next */
255 	(next->prev = currentthinker = thinker->prev)->next = next;
256 
257 	Z_Free(thinker);
258 }
259 
260 //
261 // P_RemoveThinker
262 //
263 // Deallocation is lazy -- it will not actually be freed
264 // until its thinking turn comes up.
265 //
266 // killough 4/25/98:
267 //
268 // Instead of marking the function with -1 value cast to a function pointer,
269 // set the function to P_RemoveThinkerDelayed(), so that later, it will be
270 // removed automatically as part of the thinker process.
271 //
P_RemoveThinker(thinker_t * thinker)272 void P_RemoveThinker(thinker_t *thinker)
273 {
274 	LUA_InvalidateUserdata(thinker);
275 	thinker->function.acp1 = (actionf_p1)P_RemoveThinkerDelayed;
276 }
277 
278 /*
279  * P_SetTarget
280  *
281  * This function is used to keep track of pointer references to mobj thinkers.
282  * In Doom, objects such as lost souls could sometimes be removed despite
283  * their still being referenced. In Boom, 'target' mobj fields were tested
284  * during each gametic, and any objects pointed to by them would be prevented
285  * from being removed. But this was incomplete, and was slow (every mobj was
286  * checked during every gametic). Now, we keep a count of the number of
287  * references, and delay removal until the count is 0.
288  */
289 
P_SetTarget(mobj_t ** mop,mobj_t * targ)290 mobj_t *P_SetTarget(mobj_t **mop, mobj_t *targ)
291 {
292 	if (*mop)              // If there was a target already, decrease its refcount
293 		(*mop)->thinker.references--;
294 if ((*mop = targ) != NULL) // Set new target and if non-NULL, increase its counter
295 		targ->thinker.references++;
296 	return targ;
297 }
298 
299 //
300 // P_RunThinkers
301 //
302 // killough 4/25/98:
303 //
304 // Fix deallocator to stop using "next" pointer after node has been freed
305 // (a Doom bug).
306 //
307 // Process each thinker. For thinkers which are marked deleted, we must
308 // load the "next" pointer prior to freeing the node. In Doom, the "next"
309 // pointer was loaded AFTER the thinker was freed, which could have caused
310 // crashes.
311 //
312 // But if we are not deleting the thinker, we should reload the "next"
313 // pointer after calling the function, in case additional thinkers are
314 // added at the end of the list.
315 //
316 // killough 11/98:
317 //
318 // Rewritten to delete nodes implicitly, by making currentthinker
319 // external and using P_RemoveThinkerDelayed() implicitly.
320 //
P_RunThinkers(void)321 static inline void P_RunThinkers(void)
322 {
323 	size_t i;
324 	for (i = 0; i < NUM_THINKERLISTS; i++)
325 	{
326 		ps_thlist_times[i] = I_GetPreciseTime();
327 		for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next)
328 		{
329 #ifdef PARANOIA
330 			I_Assert(currentthinker->function.acp1 != NULL);
331 #endif
332 			currentthinker->function.acp1(currentthinker);
333 		}
334 		ps_thlist_times[i] = I_GetPreciseTime() - ps_thlist_times[i];
335 	}
336 
337 }
338 
339 //
340 // P_DoAutobalanceTeams()
341 //
342 // Determine if the teams are unbalanced, and if so, move a player to the other team.
343 //
P_DoAutobalanceTeams(void)344 static void P_DoAutobalanceTeams(void)
345 {
346 	changeteam_union NetPacket;
347 	UINT16 usvalue;
348 	INT32 i=0;
349 	INT32 red=0, blue=0;
350 	INT32 redarray[MAXPLAYERS], bluearray[MAXPLAYERS];
351 	INT32 redflagcarrier = 0, blueflagcarrier = 0;
352 	INT32 totalred = 0, totalblue = 0;
353 
354 	NetPacket.value.l = NetPacket.value.b = 0;
355 	memset(redarray, 0, sizeof(redarray));
356 	memset(bluearray, 0, sizeof(bluearray));
357 
358 	// Only do it if we have enough room in the net buffer to send it.
359 	// Otherwise, come back next time and try again.
360 	if (sizeof(usvalue) > GetFreeXCmdSize())
361 		return;
362 
363 	//We have to store the players in an array with the rest of their team.
364 	//We can then pick a random player to be forced to change teams.
365 	for (i = 0; i < MAXPLAYERS; i++)
366 	{
367 		if (playeringame[i] && players[i].ctfteam)
368 		{
369 			if (players[i].ctfteam == 1)
370 			{
371 				if (!players[i].gotflag)
372 				{
373 					redarray[red] = i; //store the player's node.
374 					red++;
375 				}
376 				else
377 					redflagcarrier++;
378 			}
379 			else
380 			{
381 				if (!players[i].gotflag)
382 				{
383 					bluearray[blue] = i; //store the player's node.
384 					blue++;
385 				}
386 				else
387 					blueflagcarrier++;
388 			}
389 		}
390 	}
391 
392 	totalred = red + redflagcarrier;
393 	totalblue = blue + blueflagcarrier;
394 
395 	if ((abs(totalred - totalblue) > max(1, (totalred + totalblue) / 8)))
396 	{
397 		if (totalred > totalblue)
398 		{
399 			i = M_RandomKey(red);
400 			NetPacket.packet.newteam = 2;
401 			NetPacket.packet.playernum = redarray[i];
402 			NetPacket.packet.verification = true;
403 			NetPacket.packet.autobalance = true;
404 
405 			usvalue  = SHORT(NetPacket.value.l|NetPacket.value.b);
406 			SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
407 		}
408 		else //if (totalblue > totalred)
409 		{
410 			i = M_RandomKey(blue);
411 			NetPacket.packet.newteam = 1;
412 			NetPacket.packet.playernum = bluearray[i];
413 			NetPacket.packet.verification = true;
414 			NetPacket.packet.autobalance = true;
415 
416 			usvalue  = SHORT(NetPacket.value.l|NetPacket.value.b);
417 			SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
418 		}
419 	}
420 }
421 
422 //
423 // P_DoTeamscrambling()
424 //
425 // If a team scramble has been started, scramble one person from the
426 // pre-made scramble array. Said array is created in TeamScramble_OnChange()
427 //
P_DoTeamscrambling(void)428 void P_DoTeamscrambling(void)
429 {
430 	changeteam_union NetPacket;
431 	UINT16 usvalue;
432 	NetPacket.value.l = NetPacket.value.b = 0;
433 
434 	// Only do it if we have enough room in the net buffer to send it.
435 	// Otherwise, come back next time and try again.
436 	if (sizeof(usvalue) > GetFreeXCmdSize())
437 		return;
438 
439 	if (scramblecount < scrambletotal)
440 	{
441 		if (players[scrambleplayers[scramblecount]].ctfteam != scrambleteams[scramblecount])
442 		{
443 			NetPacket.packet.newteam = scrambleteams[scramblecount];
444 			NetPacket.packet.playernum = scrambleplayers[scramblecount];
445 			NetPacket.packet.verification = true;
446 			NetPacket.packet.scrambled = true;
447 
448 			usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
449 			SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
450 		}
451 
452 		scramblecount++; //Increment, and get to the next player when we come back here next time.
453 	}
454 	else
455 		CV_SetValue(&cv_teamscramble, 0);
456 }
457 
P_DoSpecialStageStuff(void)458 static inline void P_DoSpecialStageStuff(void)
459 {
460 	boolean stillalive = false;
461 	INT32 i;
462 
463 	// Can't drown in a special stage
464 	for (i = 0; i < MAXPLAYERS; i++)
465 	{
466 		if (!playeringame[i] || players[i].spectator)
467 			continue;
468 
469 		players[i].powers[pw_underwater] = players[i].powers[pw_spacetime] = 0;
470 	}
471 
472 	//if (sstimer < 15*TICRATE+6 && sstimer > 7 && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
473 		//S_SpeedMusic(1.4f);
474 
475 	if (sstimer && !objectplacing)
476 	{
477 		UINT16 countspheres = 0;
478 		// Count up the rings of all the players and see if
479 		// they've collected the required amount.
480 		for (i = 0; i < MAXPLAYERS; i++)
481 			if (playeringame[i])
482 			{
483 				tic_t oldnightstime = players[i].nightstime;
484 				countspheres += players[i].spheres;
485 
486 				if (!oldnightstime)
487 					continue;
488 
489 				// If in water, deplete timer 6x as fast.
490 				if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER) && !(players[i].powers[pw_shield] & SH_PROTECTWATER))
491 					players[i].nightstime -= 5;
492 				if (--players[i].nightstime > 6)
493 				{
494 					if (P_IsLocalPlayer(&players[i]) && oldnightstime > 10*TICRATE && players[i].nightstime <= 10*TICRATE)
495 						S_ChangeMusicInternal("_drown", false);
496 					stillalive = true;
497 				}
498 				else if (!players[i].exiting)
499 				{
500 					players[i].exiting = (14*TICRATE)/5 + 1;
501 					players[i].pflags &= ~(PF_GLIDING|PF_BOUNCING);
502 					players[i].nightstime = 0;
503 					if (P_IsLocalPlayer(&players[i]))
504 						S_StartSound(NULL, sfx_s3k66);
505 				}
506 			}
507 
508 		if (stillalive)
509 		{
510 			if (countspheres >= ssspheres)
511 			{
512 				// Halt all the players
513 				for (i = 0; i < MAXPLAYERS; i++)
514 					if (playeringame[i] && !players[i].exiting)
515 					{
516 						players[i].mo->momx = players[i].mo->momy = 0;
517 						players[i].exiting = (14*TICRATE)/5 + 1;
518 					}
519 				sstimer = 0;
520 				P_GiveEmerald(true);
521 				P_RestoreMusic(&players[consoleplayer]);
522 			}
523 		}
524 		else
525 			sstimer = 0;
526 	}
527 }
528 
P_DoTagStuff(void)529 static inline void P_DoTagStuff(void)
530 {
531 	INT32 i;
532 
533 	// tell the netgame who the initial IT person is.
534 	if (leveltime == TICRATE)
535 	{
536 		for (i = 0; i < MAXPLAYERS; i++)
537 		{
538 			if (players[i].pflags & PF_TAGIT)
539 			{
540 				CONS_Printf(M_GetText("%s is now IT!\n"), player_names[i]); // Tell everyone who is it!
541 				break;
542 			}
543 		}
544 	}
545 
546 	//increment survivor scores
547 	if (leveltime % TICRATE == 0 && leveltime > (hidetime * TICRATE))
548 	{
549 		INT32 participants = 0;
550 
551 		for (i=0; i < MAXPLAYERS; i++)
552 		{
553 			if (playeringame[i] && !players[i].spectator)
554 				participants++;
555 		}
556 
557 		for (i=0; i < MAXPLAYERS; i++)
558 		{
559 			if (playeringame[i] && !players[i].spectator && players[i].playerstate == PST_LIVE
560 			&& !(players[i].pflags & (PF_TAGIT|PF_GAMETYPEOVER)))
561 				//points given is the number of participating players divided by two.
562 				P_AddPlayerScore(&players[i], participants/2);
563 		}
564 	}
565 }
566 
P_DoCTFStuff(void)567 static inline void P_DoCTFStuff(void)
568 {
569 	// Automatic team balance for CTF and team match
570 	if (leveltime % (TICRATE * 5) == 0) //only check once per five seconds for the sake of CPU conservation.
571 	{
572 		// Do not attempt to autobalance and scramble teams at the same time.
573 		// Only the server should execute this. No verified admins, please.
574 		if ((cv_autobalance.value && !cv_teamscramble.value) && cv_allowteamchange.value && server)
575 			P_DoAutobalanceTeams();
576 	}
577 
578 	// Team scramble code for team match and CTF.
579 	if ((leveltime % (TICRATE/7)) == 0)
580 	{
581 		// If we run out of time in the level, the beauty is that
582 		// the Y_Ticker() team scramble code will pick it up.
583 		if (cv_teamscramble.value && server)
584 			P_DoTeamscrambling();
585 	}
586 }
587 
588 //
589 // P_Ticker
590 //
P_Ticker(boolean run)591 void P_Ticker(boolean run)
592 {
593 	INT32 i;
594 
595 	// Increment jointime and quittime even if paused
596 	for (i = 0; i < MAXPLAYERS; i++)
597 		if (playeringame[i])
598 		{
599 			players[i].jointime++;
600 
601 			if (players[i].quittime)
602 			{
603 				players[i].quittime++;
604 
605 				if (players[i].quittime == 30 * TICRATE && G_TagGametype())
606 					P_CheckSurvivors();
607 
608 				if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE)
609 				&& !(players[i].quittime % TICRATE))
610 					SendKick(i, KICK_MSG_PLAYER_QUIT);
611 			}
612 		}
613 
614 	if (objectplacing)
615 	{
616 		if (OP_FreezeObjectplace())
617 		{
618 			P_MapStart();
619 			OP_ObjectplaceMovement(&players[0]);
620 			P_MoveChaseCamera(&players[0], &camera, false);
621 			P_MapEnd();
622 			S_SetStackAdjustmentStart();
623 			return;
624 		}
625 	}
626 
627 	// Check for pause or menu up in single player
628 	if (paused || P_AutoPause())
629 	{
630 		S_SetStackAdjustmentStart();
631 		return;
632 	}
633 
634 	if (!S_MusicPaused())
635 		S_AdjustMusicStackTics();
636 
637 	postimgtype = postimgtype2 = postimg_none;
638 
639 	P_MapStart();
640 
641 	if (run)
642 	{
643 		if (demorecording)
644 			G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0);
645 		if (demoplayback)
646 		{
647 			player_t* p = &players[consoleplayer];
648 			G_ReadDemoTiccmd(&p->cmd, 0);
649 			if (!cv_freedemocamera.value)
650 			{
651 				P_ForceLocalAngle(p, p->cmd.angleturn << 16);
652 				localaiming = p->aiming;
653 			}
654 		}
655 
656 		ps_lua_mobjhooks = 0;
657 		ps_checkposition_calls = 0;
658 
659 		LUAh_PreThinkFrame();
660 
661 		ps_playerthink_time = I_GetPreciseTime();
662 		for (i = 0; i < MAXPLAYERS; i++)
663 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
664 				P_PlayerThink(&players[i]);
665 		ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
666 	}
667 
668 	// Keep track of how long they've been playing!
669 	if (!demoplayback) // Don't increment if a demo is playing.
670 		totalplaytime++;
671 
672 	if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
673 		P_DoSpecialStageStuff();
674 
675 	if (runemeraldmanager)
676 		P_EmeraldManager(); // Power stone mode
677 
678 	if (run)
679 	{
680 		ps_thinkertime = I_GetPreciseTime();
681 		P_RunThinkers();
682 		ps_thinkertime = I_GetPreciseTime() - ps_thinkertime;
683 
684 		// Run any "after all the other thinkers" stuff
685 		for (i = 0; i < MAXPLAYERS; i++)
686 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
687 				P_PlayerAfterThink(&players[i]);
688 
689 		ps_lua_thinkframe_time = I_GetPreciseTime();
690 		LUAh_ThinkFrame();
691 		ps_lua_thinkframe_time = I_GetPreciseTime() - ps_lua_thinkframe_time;
692 	}
693 
694 	// Run shield positioning
695 	P_RunShields();
696 	P_RunOverlays();
697 
698 	P_UpdateSpecials();
699 	P_RespawnSpecials();
700 
701 	// Lightning, rain sounds, etc.
702 	P_PrecipitationEffects();
703 
704 	if (run)
705 		leveltime++;
706 	timeinmap++;
707 
708 	if (G_TagGametype())
709 		P_DoTagStuff();
710 
711 	if (G_GametypeHasTeams())
712 		P_DoCTFStuff();
713 
714 	if (run)
715 	{
716 		if (countdowntimer && G_PlatformGametype() && ((gametyperules & GTR_CAMPAIGN) || leveltime >= 4*TICRATE) && !stoppedclock && --countdowntimer <= 0)
717 		{
718 			countdowntimer = 0;
719 			countdowntimeup = true;
720 			for (i = 0; i < MAXPLAYERS; i++)
721 			{
722 				if (!playeringame[i] || players[i].spectator)
723 					continue;
724 
725 				if (!players[i].mo)
726 					continue;
727 
728 				if (multiplayer || netgame)
729 					players[i].exiting = 0;
730 				P_DamageMobj(players[i].mo, NULL, NULL, 1, DMG_INSTAKILL);
731 			}
732 		}
733 
734 		if (countdown > 1)
735 			countdown--;
736 
737 		if (countdown2)
738 			countdown2--;
739 
740 		if (quake.time)
741 		{
742 			fixed_t ir = quake.intensity>>1;
743 			/// \todo Calculate distance from epicenter if set and modulate the intensity accordingly based on radius.
744 			quake.x = M_RandomRange(-ir,ir);
745 			quake.y = M_RandomRange(-ir,ir);
746 			quake.z = M_RandomRange(-ir,ir);
747 			--quake.time;
748 		}
749 		else
750 			quake.x = quake.y = quake.z = 0;
751 
752 		if (metalplayback)
753 			G_ReadMetalTic(metalplayback);
754 		if (metalrecording)
755 			G_WriteMetalTic(players[consoleplayer].mo);
756 		if (demorecording)
757 			G_WriteGhostTic(players[consoleplayer].mo);
758 		if (demoplayback) // Use Ghost data for consistency checks.
759 			G_ConsGhostTic();
760 		if (modeattacking)
761 			G_GhostTicker();
762 
763 		LUAh_PostThinkFrame();
764 	}
765 
766 	P_MapEnd();
767 
768 //	Z_CheckMemCleanup();
769 }
770 
771 // Abbreviated ticker for pre-loading, calls thinkers and assorted things
P_PreTicker(INT32 frames)772 void P_PreTicker(INT32 frames)
773 {
774 	INT32 i,framecnt;
775 	ticcmd_t temptic;
776 
777 	postimgtype = postimgtype2 = postimg_none;
778 
779 	if (marathonmode & MA_INGAME)
780 		marathonmode |= MA_INIT;
781 
782 	for (framecnt = 0; framecnt < frames; ++framecnt)
783 	{
784 		P_MapStart();
785 
786 		LUAh_PreThinkFrame();
787 
788 		for (i = 0; i < MAXPLAYERS; i++)
789 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
790 			{
791 				// stupid fucking cmd hack
792 				// if it isn't for this, players can move in preticker time
793 				// (and disrupt demo recording and other things !!)
794 				memcpy(&temptic, &players[i].cmd, sizeof(ticcmd_t));
795 				memset(&players[i].cmd, 0, sizeof(ticcmd_t));
796 				// correct angle on spawn...
797 				players[i].angleturn += temptic.angleturn - players[i].oldrelangleturn;
798 				players[i].oldrelangleturn = temptic.angleturn;
799 				players[i].cmd.angleturn = players[i].angleturn;
800 
801 				P_PlayerThink(&players[i]);
802 
803 				memcpy(&players[i].cmd, &temptic, sizeof(ticcmd_t));
804 			}
805 
806 		P_RunThinkers();
807 
808 		// Run any "after all the other thinkers" stuff
809 		for (i = 0; i < MAXPLAYERS; i++)
810 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
811 				P_PlayerAfterThink(&players[i]);
812 
813 		LUAh_ThinkFrame();
814 
815 		// Run shield positioning
816 		P_RunShields();
817 		P_RunOverlays();
818 
819 		P_UpdateSpecials();
820 		P_RespawnSpecials();
821 
822 		LUAh_PostThinkFrame();
823 
824 		P_MapEnd();
825 	}
826 
827 	if (marathonmode & MA_INGAME)
828 		marathonmode &= ~MA_INIT;
829 }
830