1 //----------------------------------------------------------------------------
2 //  EDGE Moving Object Handling Code
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -MH- 1998/07/02  "shootupdown" --> "true3dgameplay"
27 //
28 // -ACB- 1998/07/30 Took an axe to the item respawn code: now uses a
29 //                  double-linked list to store to individual items;
30 //                  limit removed; P_MobjItemRespawn replaces P_RespawnSpecials
31 //                  as the procedure that handles respawning of items.
32 //
33 //                  P_NightmareRespawnOld -> P_TeleportRespawn
34 //                  P_NightmareRespawnNew -> P_ResurrectRespawn
35 //
36 // -ACB- 1998/07/31 Use new procedure to handle flying missiles that hammer
37 //                  into sky-hack walls & ceilings. Also don't explode the
38 //                  missile if it hits sky-hack ceiling or floor.
39 //
40 // -ACB- 1998/08/06 Implemented limitless mobjdef list, altered/removed all
41 //                  mobjdef[] references.
42 //
43 // -AJA- 1999/07/21: Replaced some non-critical P_Randoms with M_Random.
44 //
45 // -AJA- 1999/07/30: Removed redundant code from P_SpawnMobj (it was
46 //                   virtually identical to P_MobjCreateObject).
47 //
48 // -AJA- 1999/09/15: Removed P_SpawnMobj itself :-).
49 //
50 
51 #include "i_defs.h"
52 #include "i_defs_gl.h"  // we need r_shader.h
53 #include "p_mobj.h"
54 
55 #include "con_main.h"
56 #include "dm_defs.h"
57 #include "dm_state.h"
58 #include "g_game.h"
59 #include "f_interm.h"
60 #include "hu_stuff.h"
61 #include "m_argv.h"
62 #include "m_random.h"
63 #include "p_local.h"
64 #include "r_misc.h"
65 #include "r_shader.h"
66 #include "s_sound.h"
67 #include "z_zone.h"
68 
69 #include "epi/arrays.h"
70 
71 #include <list>
72 
73 #define LADDER_FRICTION  0.5f
74 
75 #define DEBUG_MOBJ  0
76 
77 #if 1  // DEBUGGING
P_DumpMobjs(void)78 void P_DumpMobjs(void)
79 {
80 	mobj_t *mo;
81 
82 	int index = 0;
83 
84 	L_WriteDebug("MOBJs:\n");
85 
86 	for (mo=mobjlisthead; mo; mo=mo->next, index++)
87 	{
88 		L_WriteDebug(" %4d: %p next:%p prev:%p [%s] at (%1.0f,%1.0f,%1.0f) states=%d > %d tics=%d\n",
89 			index,
90 			mo, mo->next, mo->prev,
91 			mo->info->name.c_str(),
92 			mo->x, mo->y, mo->z,
93 			mo->state ? mo->state - states : -1,
94 			mo->next_state ? mo->next_state - states : -1,
95 			mo->tics);
96 	}
97 
98 	L_WriteDebug("END OF MOBJs\n");
99 }
100 #endif
101 
102 
103 // List of all objects in map.
104 mobj_t *mobjlisthead;
105 
106 // Where objects go to die...
107 static std::list<mobj_t *> remove_queue;
108 
109 iteminque_t *itemquehead;
110 
111 
112 // convenience function
113 // -AJA- FIXME: duplicate code from p_map.c
PointOnLineSide(float x,float y,line_t * ld)114 static inline int PointOnLineSide(float x, float y, line_t *ld)
115 {
116 	divline_t div;
117 
118 	div.x = ld->v1->x;
119 	div.y = ld->v1->y;
120 	div.dx = ld->dx;
121 	div.dy = ld->dy;
122 
123 	return P_PointOnDivlineSide(x, y, &div);
124 }
125 
126 //
127 // EnterBounceStates
128 //
129 // -AJA- 1999/10/18: written.
130 //
EnterBounceStates(mobj_t * mo)131 static void EnterBounceStates(mobj_t * mo)
132 {
133 	if (! mo->info->bounce_state)
134 		return;
135 
136 	// ignore if disarmed
137 	if (mo->extendedflags & EF_JUSTBOUNCED)
138 		return;
139 
140 	// give deferred states a higher priority
141 	if (!mo->state || !mo->next_state ||
142 		(mo->next_state - states) != mo->state->nextstate)
143 	{
144 		return;
145 	}
146 
147 	mo->extendedflags |= EF_JUSTBOUNCED;
148 
149 	P_SetMobjState(mo, mo->info->bounce_state);
150 }
151 
152 //
153 // BounceOffWall
154 //
155 // -AJA- 1999/08/22: written.
156 //
BounceOffWall(mobj_t * mo,line_t * wall)157 static void BounceOffWall(mobj_t * mo, line_t * wall)
158 {
159 	angle_t angle;
160 	angle_t wall_angle;
161 	angle_t diff;
162 
163 	divline_t div;
164 	float dest_x, dest_y;
165 
166 	angle = R_PointToAngle(0, 0, mo->mom.x, mo->mom.y);
167 	wall_angle = R_PointToAngle(0, 0, wall->dx, wall->dy);
168 
169 	diff = wall_angle - angle;
170 
171 	if (diff > ANG90 && diff < ANG270)
172 		diff -= ANG180;
173 
174 	// -AJA- Prevent getting stuck at some walls...
175 
176 	dest_x = mo->x + M_Cos(angle) * (mo->speed + mo->info->radius) * 4.0f;
177 	dest_y = mo->y + M_Sin(angle) * (mo->speed + mo->info->radius) * 4.0f;
178 
179 	div.x = wall->v1->x;
180 	div.y = wall->v1->y;
181 	div.dx = wall->dx;
182 	div.dy = wall->dy;
183 
184 	if (P_PointOnDivlineSide(mo->x, mo->y, &div) ==
185 		P_PointOnDivlineSide(dest_x, dest_y, &div))
186 	{
187 		// Result is the same, thus we haven't crossed the line.  Choose a
188 		// random angle to bounce away.  And don't attenuate the speed (so
189 		// we can get far enough away).
190 
191 		angle = P_Random() << (ANGLEBITS - 8);
192 	}
193 	else
194 	{
195 		angle += diff << 1;
196 	}
197 
198 	// calculate new momentum
199 
200 	mo->speed *= mo->info->bounce_speed;
201 
202 	mo->mom.x = M_Cos(angle) * mo->speed;
203 	mo->mom.y = M_Sin(angle) * mo->speed;
204 	mo->angle = angle;
205 
206 	EnterBounceStates(mo);
207 }
208 
209 //
210 // BounceOffPlane
211 //
212 // -AJA- 1999/10/18: written.
213 //
BounceOffPlane(mobj_t * mo,float dir)214 static void BounceOffPlane(mobj_t * mo, float dir)
215 {
216 	// calculate new momentum
217 
218 	mo->speed *= mo->info->bounce_speed;
219 
220 	mo->mom.x = (float)(M_Cos(mo->angle) * mo->speed);
221 	mo->mom.y = (float)(M_Sin(mo->angle) * mo->speed);
222 	mo->mom.z = (float)(dir * mo->speed * mo->info->bounce_up);
223 
224 	EnterBounceStates(mo);
225 }
226 
227 //
228 // CorpseShouldSlide
229 //
230 // -AJA- 1999/09/25: written.
231 //
CorpseShouldSlide(mobj_t * mo)232 static bool CorpseShouldSlide(mobj_t * mo)
233 {
234 	float floor, ceil;
235 
236 	if (-0.25f < mo->mom.x && mo->mom.x < 0.25f &&
237 		-0.25f < mo->mom.y && mo->mom.y < 0.25f)
238 	{
239 		return false;
240 	}
241 
242 	P_ComputeThingGap(mo, mo->subsector->sector, mo->z, &floor, &ceil);
243 
244 	return (mo->floorz != floor);
245 }
246 
247 //
248 // TeleportRespawn
249 //
TeleportRespawn(mobj_t * mobj)250 static void TeleportRespawn(mobj_t * mobj)
251 {
252 	float x, y, z, oldradius, oldheight;
253 	const mobjtype_c *info = mobj->spawnpoint.info;
254 	mobj_t *new_mo;
255 	int oldflags;
256 
257 	if (!info)
258 		return;
259 
260 	x = mobj->spawnpoint.x;
261 	y = mobj->spawnpoint.y;
262 	z = mobj->spawnpoint.z;
263 
264 	// something is occupying it's position?
265 
266 	//
267 	// -ACB- 2004/02/01 Check if the object can respawn in this position with
268 	// its correct radius. Should this check fail restore the old values back
269 	//
270 	oldradius = mobj->radius;
271 	oldheight = mobj->height;
272 	oldflags = mobj->flags;
273 
274 	mobj->radius = mobj->spawnpoint.info->radius;
275 	mobj->height = mobj->spawnpoint.info->height;
276 
277 	if (info->flags & MF_SOLID)						// Should it be solid?
278 		mobj->flags |= MF_SOLID;
279 
280 	if (!P_CheckAbsPosition(mobj, x, y, z))
281 	{
282 		mobj->radius = oldradius;
283 		mobj->height = oldheight;
284 		mobj->flags = oldflags;
285 		return;
286 	}
287 
288 	// spawn a teleport fog at old spot
289 	// because of removal of the body...
290 
291 	// temp fix for teleport flash...
292 	if (info->respawneffect)
293 		P_MobjCreateObject(mobj->x, mobj->y, mobj->z, info->respawneffect);
294 
295 	// spawn a teleport fog at the new spot...
296 
297 	// temp fix for teleport flash...
298 	if (info->respawneffect)
299 		P_MobjCreateObject(x, y, z, info->respawneffect);
300 
301 	// spawn it, inheriting attributes from deceased one
302 	// -ACB- 1998/08/06 Create Object
303 	new_mo = P_MobjCreateObject(x, y, z, info);
304 
305 	new_mo->spawnpoint = mobj->spawnpoint;
306 	new_mo->angle = mobj->spawnpoint.angle;
307 	new_mo->vertangle = mobj->spawnpoint.vertangle;
308 	new_mo->tag = mobj->spawnpoint.tag;
309 
310 	if (mobj->spawnpoint.flags & MF_AMBUSH)
311 		new_mo->flags |= MF_AMBUSH;
312 
313 	new_mo->reactiontime = RESPAWN_DELAY;
314 
315 	// remove the old monster.
316 	P_RemoveMobj(mobj);
317 }
318 
319 //
320 // ResurrectRespawn
321 //
322 // -ACB- 1998/07/29 Prevented respawning of ghosts
323 //                  Make monster deaf, if originally deaf
324 //                  Given a reaction time, delays monster starting up immediately.
325 //                  Doesn't try to raise an object with no raisestate
326 //
ResurrectRespawn(mobj_t * mobj)327 static void ResurrectRespawn(mobj_t * mobj)
328 {
329 	float x, y, z, oldradius, oldheight;
330 	const mobjtype_c *info;
331 	int oldflags;
332 
333 	x = mobj->x;
334 	y = mobj->y;
335 	z = mobj->z;
336 
337 	info = mobj->info;
338 
339 	// cannot raise the unraisable
340 	if (!info->raise_state)
341 		return;
342 
343 	// don't respawn gibs
344 	if (mobj->extendedflags & EF_GIBBED)
345 		return;
346 
347 	//
348 	// -ACB- 2004/02/01 Check if the object can respawn in this position with
349 	// its correct radius. Should this check fail restore the old values back
350 	//
351 	oldradius = mobj->radius;
352 	oldheight = mobj->height;
353 	oldflags = mobj->flags;
354 
355 	mobj->radius = info->radius;
356 	mobj->height = info->height;
357 
358 	if (info->flags & MF_SOLID)					// Should it be solid?
359 		mobj->flags |= MF_SOLID;
360 
361 	if (!P_CheckAbsPosition(mobj, x, y, z))
362 	{
363 		mobj->radius = oldradius;
364 		mobj->height = oldheight;
365 		mobj->flags = oldflags;
366 		return;
367 	}
368 
369 	// Resurrect monster
370 	if (info->overkill_sound)
371 		S_StartFX(info->overkill_sound, P_MobjGetSfxCategory(mobj), mobj);
372 
373 	P_SetMobjState(mobj, info->raise_state);
374 
375 	SYS_ASSERT(! mobj->isRemoved());
376 
377 	mobj->flags = info->flags;
378 	mobj->extendedflags = info->extendedflags;
379 	mobj->hyperflags = info->hyperflags;
380 	mobj->health = info->spawnhealth;
381 
382 	mobj->visibility = PERCENT_2_FLOAT(info->translucency);
383 	mobj->movecount = 0;  // -ACB- 1998/08/03 Don't head off in any direction
384 
385 	mobj->SetSource(NULL);
386 	mobj->SetTarget(NULL);
387 
388 	mobj->tag = mobj->spawnpoint.tag;
389 
390 	if (mobj->spawnpoint.flags & MF_AMBUSH)
391 		mobj->flags |= MF_AMBUSH;
392 
393 	mobj->reactiontime = RESPAWN_DELAY;
394 	return;
395 }
396 
ClearStaleRefs()397 void mobj_t::ClearStaleRefs()
398 {
399 	if (target && target->isRemoved()) SetTarget(NULL);
400 	if (source && source->isRemoved()) SetSource(NULL);
401 	if (tracer && tracer->isRemoved()) SetTracer(NULL);
402 
403 	if (supportobj && supportobj->isRemoved()) SetSupportObj(NULL);
404 	if (above_mo   && above_mo->isRemoved())   SetAboveMo(NULL);
405 	if (below_mo   && below_mo->isRemoved())   SetBelowMo(NULL);
406 }
407 
408 //
409 // Finally destroy the map object.
410 //
DeleteMobj(mobj_t * mo)411 static void DeleteMobj(mobj_t * mo)
412 {
413 #if (DEBUG_MOBJ > 0)
414 	L_WriteDebug("tics=%05d  DELETE %p [%s]\n", leveltime, mo,
415 		mo->info ? mo->info->name.c_str() : "???");
416 #endif
417 
418 	// Sound might still be playing, so use remove the
419     // link between object and effect
420 
421     S_StopFX(mo);
422 
423 	if (mo->refcount != 0)
424 	{
425 		I_Error("INTERNAL ERROR: Reference count %d", mo->refcount);
426 		return;
427 	}
428 
429 	delete mo->dlight.shader;
430 
431 	Z_Free(mo);
432 }
433 
434 
435 // Use these methods to set mobj entries.
436 // NEVER EVER modify the entries directly.
437 
438 #define FUNCTION_BODY(field) \
439 { \
440 	if (field) field->refcount--; \
441 	field = ref; \
442 	if (field) field->refcount++; \
443 }
444 
SetTarget(mobj_t * ref)445 void mobj_t::SetTarget(mobj_t *ref)  FUNCTION_BODY(target)
446 void mobj_t::SetSource(mobj_t *ref)  FUNCTION_BODY(source)
447 void mobj_t::SetTracer(mobj_t *ref)  FUNCTION_BODY(tracer)
448 
449 void mobj_t::SetSupportObj(mobj_t *ref)  FUNCTION_BODY(supportobj)
450 void mobj_t::SetAboveMo(mobj_t *ref)     FUNCTION_BODY(above_mo)
451 void mobj_t::SetBelowMo(mobj_t *ref)     FUNCTION_BODY(below_mo)
452 
453 #undef FUNCTION_BODY
454 
455 //
456 // P_MobjSetRealSource
457 //
458 // -AJA- This is for missiles that spawn other missiles -- what we
459 //       really want to know is who spawned the original missile
460 //       (the "instigator" of all the mayhem :-).
461 //
462 void mobj_t::SetRealSource(mobj_t *ref)
463 {
464 	while (ref && ref->source && (ref->flags & MF_MISSILE))
465 		ref = ref->source;
466 
467 	SetSource(ref);
468 }
469 
470 //
471 // P_SetMobjState
472 //
473 // Returns true if the mobj is still present.
474 //
P_SetMobjState(mobj_t * mobj,statenum_t state)475 bool P_SetMobjState(mobj_t * mobj, statenum_t state)
476 {
477 	state_t *st;
478 
479 	// ignore removed objects
480 	if (mobj->isRemoved())
481 		return false;
482 
483 	if (state == S_NULL)
484 	{
485 		P_RemoveMobj(mobj);
486 		return false;
487 	}
488 
489 	st = &states[state];
490 
491 	// model interpolation stuff
492 	if ((st->flags & SFF_Model) && (mobj->state->flags & SFF_Model) &&
493 		(st->sprite == mobj->state->sprite) && st->tics > 1)
494 	{
495 		mobj->model_last_frame = mobj->state->frame;
496 	}
497 	else
498 		mobj->model_last_frame = -1;
499 
500 	mobj->state  = st;
501 	mobj->tics   = st->tics;
502 	mobj->next_state = (st->nextstate == S_NULL) ? NULL :
503 		(states + st->nextstate);
504 
505 	if (st->action)
506 		(* st->action)(mobj);
507 
508 	return true;
509 }
510 
P_SetMobjState2(mobj_t * mobj,statenum_t state)511 bool P_SetMobjState2(mobj_t * mobj, statenum_t state)
512 {
513 	// -AJA- 2010/07/10: mundo hack for DDF inheritance. When jumping
514 	//                   to an old state, check if a newer one exists.
515 
516 	if (mobj->isRemoved())
517 		return false;
518 
519 	if (state == S_NULL)
520 		return P_SetMobjState(mobj, state);
521 
522 	SYS_ASSERT(! mobj->info->state_grp.empty());
523 
524 	// state is old?
525 	if (state < mobj->info->state_grp.back().first)
526 	{
527 		state_t *st = &states[state];
528 
529 		if (st->label)
530 		{
531 			statenum_t new_state = P_MobjFindLabel(mobj, st->label);
532 
533 			if (new_state != S_NULL)
534 				state = new_state;
535 		}
536 	}
537 
538 	return P_SetMobjState(mobj, state);
539 }
540 
541 
542 //
543 // P_SetMobjStateDeferred
544 //
545 // Similiar to P_SetMobjState, but no actions are performed yet.
546 // The new state will entered when the P_MobjThinker code reaches it,
547 // which may happen in the current tick, or at worst the next tick.
548 //
549 // Prevents re-entrancy into code like P_CheckRelPosition which is
550 // inherently non re-entrant.
551 //
552 // -AJA- 1999/09/12: written.
553 //
P_SetMobjStateDeferred(mobj_t * mo,statenum_t stnum,int tic_skip)554 bool P_SetMobjStateDeferred(mobj_t * mo, statenum_t stnum, int tic_skip)
555 {
556 	// ignore removed objects
557 	if (mo->isRemoved() || !mo->next_state)
558 		return false;
559 
560 ///???	if (stnum == S_NULL)
561 ///???	{
562 ///???		P_RemoveMobj(mo);
563 ///???		return false;
564 ///???	}
565 
566 	mo->next_state = (stnum == S_NULL) ? NULL : (states + stnum);
567 
568 	mo->tics = 0;
569 	mo->tic_skip = tic_skip;
570 
571 	return true;
572 }
573 
574 //
575 // P_MobjFindLabel
576 //
577 // Look for the given label in the mobj's states.  Returns the state
578 // number if found, otherwise S_NULL.
579 //
P_MobjFindLabel(mobj_t * mobj,const char * label)580 statenum_t P_MobjFindLabel(mobj_t * mobj, const char *label)
581 {
582 	return DDF_StateFindLabel(mobj->info->state_grp, label, true /* quiet */);
583 }
584 
585 //
586 // P_SetMobjDirAndSpeed
587 //
P_SetMobjDirAndSpeed(mobj_t * mo,angle_t angle,float slope,float speed)588 void P_SetMobjDirAndSpeed(mobj_t * mo, angle_t angle, float slope, float speed)
589 {
590 	mo->angle = angle;
591 	mo->vertangle = M_ATan(slope);
592 
593 	mo->mom.z = M_Sin(mo->vertangle) * speed;
594 	speed    *= M_Cos(mo->vertangle);
595 
596 	mo->mom.x = M_Cos(angle) * speed;
597 	mo->mom.y = M_Sin(angle) * speed;
598 }
599 
600 //
601 // P_MobjExplodeMissile
602 //
603 // -AJA- 1999/09/12: Now uses P_SetMobjStateDeferred, since this
604 //       routine can be called by TryMove/PIT_CheckRelThing.
605 //
P_MobjExplodeMissile(mobj_t * mo)606 void P_MobjExplodeMissile(mobj_t * mo)
607 {
608 	mo->mom.x = mo->mom.y = mo->mom.z = 0;
609 
610 	mo->flags &= ~(MF_MISSILE | MF_TOUCHY);
611 	mo->extendedflags &= ~(EF_BOUNCE | EF_USABLE);
612 
613 	if (mo->info->deathsound)
614 		S_StartFX(mo->info->deathsound, SNCAT_Object, mo);
615 
616 	// mobjdef used -ACB- 1998/08/06
617 	P_SetMobjStateDeferred(mo, mo->info->death_state, P_Random() & 3);
618 }
619 
620 
AddRegionProperties(const mobj_t * mo,float bz,float tz,region_properties_t * new_p,float f_h,float c_h,const region_properties_t * p)621 static inline void AddRegionProperties(const mobj_t *mo,
622 									   float bz, float tz, region_properties_t *new_p,
623 									   float f_h, float c_h, const region_properties_t *p)
624 {
625 	int flags = p->special ? p->special->special_flags : SECSP_PushConstant;
626 
627 	float factor = 1.0f;
628 	float push_mul;
629 
630 	SYS_ASSERT(tz > bz);
631 
632 	if (tz > c_h)
633 		factor -= factor * (tz - c_h) / (tz-bz);
634 
635 	if (bz < f_h)
636 		factor -= factor * (f_h - bz) / (tz-bz);
637 
638 	if (factor <= 0)
639 		return;
640 
641 	new_p->gravity   += factor * p->gravity;
642 	new_p->viscosity += factor * p->viscosity;
643 	new_p->drag      += factor * p->drag;
644 
645 	// handle push sectors
646 
647 	if (! (flags & SECSP_WholeRegion) && bz > f_h + 1)
648 		return;
649 
650 	push_mul = 1.0f;
651 
652 	if (! (flags & SECSP_PushConstant))
653 	{
654 		SYS_ASSERT(mo->info->mass > 0);
655 		push_mul = 100.0f / mo->info->mass;
656 	}
657 
658 	if (flags & SECSP_Proportional)
659 		push_mul *= factor;
660 
661 	new_p->push.x += push_mul * p->push.x;
662 	new_p->push.y += push_mul * p->push.y;
663 	new_p->push.z += push_mul * p->push.z;
664 }
665 
666 //
667 // P_CalcFullProperties
668 //
669 // Calculates the properties (gravity etc..) acting on an object,
670 // especially when the object is in multiple extrafloors with
671 // different props.
672 //
673 // Only used for players for now (too expensive to be used by
674 // everything).
675 //
P_CalcFullProperties(const mobj_t * mo,region_properties_t * new_p)676 void P_CalcFullProperties(const mobj_t *mo, region_properties_t *new_p)
677 {
678 	sector_t *sector = mo->subsector->sector;
679 
680 	extrafloor_t *S, *L, *C;
681 	float floor_h;
682 
683 	float bz = mo->z;
684 	float tz = bz + mo->height;
685 
686 
687 	new_p->gravity = 0;
688 	new_p->viscosity = 0;
689 	new_p->drag = 0;
690 
691 	new_p->push.x = new_p->push.y = new_p->push.z = 0;
692 
693 	new_p->type = 0;  // these shouldn't be used
694 	new_p->special = NULL;
695 
696 	// Note: friction not averaged: comes from region foot is in
697 	new_p->friction = sector->p->friction;
698 
699 	floor_h = sector->f_h;
700 
701 	S = sector->bottom_ef;
702 	L = sector->bottom_liq;
703 
704 	while (S || L)
705 	{
706 		if (!L || (S && S->bottom_h < L->bottom_h))
707 		{
708 			C = S;  S = S->higher;
709 		}
710 		else
711 		{
712 			C = L;  L = L->higher;
713 		}
714 
715 		SYS_ASSERT(C);
716 
717 		// ignore "hidden" liquids
718 		if (C->bottom_h < floor_h || C->bottom_h > sector->c_h)
719 			continue;
720 
721 		if (bz < C->bottom_h)
722 			new_p->friction = C->p->friction;
723 
724 		AddRegionProperties(mo, bz, tz, new_p, floor_h, C->top_h, C->p);
725 
726 		floor_h = C->top_h;
727 	}
728 
729 	AddRegionProperties(mo, bz, tz, new_p, floor_h, sector->c_h, sector->p);
730 }
731 
732 //
733 // P_XYMovement
734 //
P_XYMovement(mobj_t * mo,const region_properties_t * props)735 static void P_XYMovement(mobj_t * mo, const region_properties_t *props)
736 {
737 	float orig_x = mo->x;
738 	float orig_y = mo->y;
739 
740 	float ptryx;
741 	float ptryy;
742 	float xstep;
743 	float ystep;
744 	float absx,absy;
745 	float maxstep;
746 
747 	if (fabs(mo->mom.x) > MAXMOVE)
748 	{
749 		float factor = MAXMOVE / fabs(mo->mom.x);
750 		mo->mom.x *= factor;
751 		mo->mom.y *= factor;
752 	}
753 
754 	if (fabs(mo->mom.y) > MAXMOVE)
755 	{
756 		float factor = MAXMOVE / fabs(mo->mom.y);
757 		mo->mom.x *= factor;
758 		mo->mom.y *= factor;
759 	}
760 
761 	float xmove = mo->mom.x;
762 	float ymove = mo->mom.y;
763 
764 	// -AJA- 1999/07/31: Ride that rawhide :->
765 	if (mo->above_mo && !(mo->above_mo->flags & MF_FLOAT) &&
766 		mo->above_mo->floorz < (mo->z + mo->height + 1))
767 	{
768 		mo->above_mo->mom.x += xmove * mo->info->ride_friction;
769 		mo->above_mo->mom.y += ymove * mo->info->ride_friction;
770 	}
771 
772 	// -AJA- 1999/10/09: Reworked viscosity.
773 	xmove *= 1.0f - props->viscosity;
774 	ymove *= 1.0f - props->viscosity;
775 
776 	// -ES- 1999/10/16 For fast mobjs, break down
777 	//  the move into steps of max half radius for collision purposes.
778 
779 	// Use half radius as max step, if not exceptionally small.
780 	if (mo->radius > STEPMOVE)
781 		maxstep = mo->radius / 2;
782 	else
783 		maxstep = STEPMOVE / 2;
784 
785 	// precalculate these two, they are used frequently
786 	absx = (float)fabs(xmove);
787 	absy = (float)fabs(ymove);
788 
789 	if (absx > maxstep || absy > maxstep)
790 	{
791 		// Do it in the most number of steps.
792 		if (absx > absy)
793 		{
794 			xstep = (xmove > 0) ? maxstep : -maxstep;
795 
796 			// almost orthogonal movements are rounded to orthogonal, to prevent
797 			// an infinite loop in some extreme cases.
798 			if (absy * 256 < absx)
799 				ystep = ymove = 0;
800 			else
801 				ystep = ymove * xstep / xmove;
802 		}
803 		else
804 		{
805 			ystep = (ymove > 0) ? maxstep : -maxstep;
806 
807 			if (absx * 256 < absy)
808 				xstep = xmove = 0;
809 			else
810 				xstep = xmove * ystep / ymove;
811 		}
812 	}
813 	else
814 	{
815 		// Step is less than half radius, so one iteration is enough.
816 		xstep = xmove;
817 		ystep = ymove;
818 	}
819 
820 	// Keep attempting moves until object has lost all momentum.
821 	do
822 	{
823 		// if movement is more than half that of the maximum, attempt the move
824 		// in two halves or move.
825 		if (fabs(xmove) > fabs(xstep))
826 		{
827 			ptryx = mo->x + xstep;
828 			xmove -= xstep;
829 		}
830 		else
831 		{
832 			ptryx = mo->x + xmove;
833 			xmove = 0;
834 		}
835 
836 		if (fabs(ymove) > fabs(ystep))
837 		{
838 			ptryy = mo->y + ystep;
839 			ymove -= ystep;
840 		}
841 		else
842 		{
843 			ptryy = mo->y + ymove;
844 			ymove = 0;
845 		}
846 
847 		int did_move = P_TryMove(mo, ptryx, ptryy);
848 
849 		// unable to complete desired move ?
850 		if (!did_move)
851 		{
852 			// check for missiles hitting shootable lines
853 			// NOTE: this is for solid lines.  The "pass over" case is
854 			// handled in P_TryMove().
855 
856 			if ((mo->flags & MF_MISSILE) &&
857 				(! mo->currentattack ||
858 				! (mo->currentattack->flags & AF_NoTriggerLines)))
859 			{
860 				//
861 				// -AJA- Seems this is called to handle this situation:
862 				// P_TryMove is called, but fails because missile would hit
863 				// solid line.  BUT missile did pass over some special lines.
864 				// These special lines were not activated in P_TryMove since it
865 				// failed.  Ugh !
866 				//
867 				if (spechit.GetSize() > 0)
868 				{
869 					epi::array_iterator_c it;
870 					line_t* ld;
871 
872 					for (it=spechit.GetTailIterator(); it.IsValid(); it--)
873 					{
874 						ld = ITERATOR_TO_TYPE(it, line_t*);
875 
876 						P_ShootSpecialLine(ld, PointOnLineSide(mo->x, mo->y, ld),
877 											mo->source);
878 					}
879 				}
880 
881 				if (blockline && blockline->special)
882 				{
883 					P_ShootSpecialLine(blockline,
884 						PointOnLineSide(mo->x, mo->y, blockline), mo->source);
885 				}
886 			}
887 
888 			// -AJA- 2008/01/20: Jumping out of Water
889 			if (blockline && blockline->backsector &&
890 				mo->player && mo->player->mo == mo &&
891 				mo->player->wet_feet && !mo->player->swimming &&
892 				mo->player->jumpwait == 0 &&
893 				mo->z > mo->floorz + 0.5f)
894 			{
895 				float ground_h;
896 
897 				int i = P_FindThingGap(blockline->gaps, blockline->gap_num,
898 				                       mo->z + mo->height, mo->z + 2 * mo->height);
899 				if (i >= 0)
900 				{
901 					ground_h = blockline->gaps[i].f;
902 				}
903 				else
904 				{
905 					ground_h = MAX(blockline->frontsector->f_h,
906 				                   blockline->backsector->f_h);
907 				}
908 
909 				// I_Debugf("ground_h: %1.0f  mo_Z: %1.0f\n", ground_h, mo->z);
910 
911 				if (mo->z < ground_h - 20.5f &&
912 				    mo->z > ground_h - mo->height * 1.4)
913 				{
914 					P_PlayerJump(mo->player, 60, 2 * TICRATE);
915 				}
916 			}
917 
918 			if (mo->info->flags & MF_SLIDE)
919 			{
920 				P_SlideMove(mo, ptryx, ptryy);
921 			}
922 			else if (mo->extendedflags & EF_BOUNCE)
923 			{
924 				// -KM- 1999/01/31 Bouncy objects (grenades)
925 				// -AJA- 1999/07/30: Moved up here.
926 
927 				if (! blockline)
928 				{
929 					if (mobj_hit_sky)
930 						P_MobjRemoveMissile(mo);
931 					else
932 						P_MobjExplodeMissile(mo);
933 
934 					return;
935 				}
936 
937 				BounceOffWall(mo, blockline);
938 				xmove = ymove = 0;
939 			}
940 			else if (mo->flags & MF_MISSILE)
941 			{
942 				if (mobj_hit_sky)
943 					P_MobjRemoveMissile(mo);  // New Procedure -ACB- 1998/07/30
944 				else
945 					P_MobjExplodeMissile(mo);
946 
947 				return;
948 			}
949 			else
950 			{
951 				xmove = ymove = 0;
952 				mo->mom.x = mo->mom.y = 0;
953 			}
954 		}
955 	}
956 	while (xmove || ymove);
957 
958 	if ((mo->extendedflags & EF_NOFRICTION) || (mo->flags & MF_SKULLFLY))
959 		return;
960 
961 	if (mo->flags & MF_CORPSE)
962 	{
963 		// do not stop sliding if halfway off a step with some momentum
964 		if (CorpseShouldSlide(mo))
965 			return;
966 	}
967 
968 	//
969 	// -MH- 1998/08/18 - make mid-air movement normal when using the jetpack
970 	//      When in mid-air there's no friction so you slide about
971 	//      uncontrollably. This is realistic but makes the game
972 	//      difficult to control to the extent that for normal people,
973 	//      it's not worth playing - a bit like having auto-aim
974 	//      permanently off (as most real people are not crack-shots!)
975 	//
976 	if ((mo->z > mo->floorz) && !(mo->on_ladder >= 0) &&
977 		!(mo->player && mo->player->powers[PW_Jetpack] > 0))
978 	{
979 		// apply drag when airborne
980 		mo->mom.x *= props->drag;
981 		mo->mom.y *= props->drag;
982 	}
983 	else
984 	{
985 		mo->mom.x *= props->friction;
986 		mo->mom.y *= props->friction;
987 	}
988 
989 	if (mo->player)
990 	{
991 		float x_diff = fabs(orig_x - mo->x);
992 		float y_diff = fabs(orig_y - mo->y);
993 
994 		float speed = APPROX_DIST2(x_diff, y_diff);
995 
996 		mo->player->actual_speed =
997 			(mo->player->actual_speed * 0.8 + speed * 0.2);
998 
999 // I_Debugf("Actual speed = %1.4f\n", mo->player->actual_speed);
1000 
1001 		if (fabs(mo->mom.x) < STOPSPEED && fabs(mo->mom.y) < STOPSPEED &&
1002 			mo->player->cmd.forwardmove == 0 &&
1003 			mo->player->cmd.sidemove == 0)
1004 		{
1005 			mo->mom.x = mo->mom.y = 0;
1006 		}
1007 	}
1008 }
1009 
1010 //
1011 // P_ZMovement
1012 //
P_ZMovement(mobj_t * mo,const region_properties_t * props)1013 static void P_ZMovement(mobj_t * mo, const region_properties_t *props)
1014 {
1015 	float dist;
1016 	float delta;
1017 	float zmove;
1018 
1019 	// -KM- 1998/11/25 Gravity is now not precalculated so that
1020 	//  menu changes affect instantly.
1021 	float gravity = props->gravity / 8.0f *
1022 		(float)level_flags.menu_grav / (float)MENU_GRAV_NORMAL;
1023 
1024 	// check for smooth step up
1025 	if (mo->player && mo->player->mo == mo && mo->z < mo->floorz)
1026 	{
1027 		mo->player->viewheight -= (mo->floorz - mo->z);
1028 		mo->player->viewz      -= (mo->floorz - mo->z);
1029 		mo->player->deltaviewheight = (mo->player->std_viewheight -
1030 			mo->player->viewheight) / 8.0f;
1031 	}
1032 
1033 	zmove = mo->mom.z * (1.0f - props->viscosity);
1034 
1035 	// adjust height
1036 	mo->z += zmove;
1037 
1038 	if (mo->flags & MF_FLOAT && mo->target)
1039 	{
1040 		// float down towards target if too close
1041 		if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
1042 		{
1043 			dist = P_ApproxDistance(mo->x - mo->target->x, mo->y - mo->target->y);
1044 			delta = mo->target->z + (mo->height / 2) - mo->z;
1045 
1046 			if (delta < 0 && dist < -(delta * 3))
1047 				mo->z -= mo->info->float_speed;
1048 			else if (delta > 0 && dist < (delta * 3))
1049 				mo->z += mo->info->float_speed;
1050 		}
1051 	}
1052 
1053 	//
1054 	//  HIT FLOOR ?
1055 	//
1056 
1057 	if (mo->z <= mo->floorz)
1058 	{
1059 		if (mo->flags & MF_SKULLFLY)
1060 			mo->mom.z = -mo->mom.z;
1061 
1062 		if (mo->mom.z < 0)
1063 		{
1064 			float hurt_momz = gravity * mo->info->maxfall;
1065 			bool fly_or_swim = mo->player && (mo->player->swimming ||
1066 				mo->player->powers[PW_Jetpack] > 0 || mo->on_ladder >= 0);
1067 
1068 			if (mo->player && gravity > 0 && -zmove > OOF_SPEED && ! fly_or_swim)
1069 			{
1070 				// Squat down. Decrease viewheight for a moment after hitting the
1071 				// ground (hard), and utter appropriate sound.
1072 				mo->player->deltaviewheight = zmove / 8.0f;
1073 				S_StartFX(mo->info->oof_sound, P_MobjGetSfxCategory(mo), mo);
1074 			}
1075 			// -KM- 1998/12/16 If bigger than max fall, take damage.
1076 			if (mo->info->maxfall > 0 && gravity > 0 && -mo->mom.z > hurt_momz &&
1077 				(! mo->player || ! fly_or_swim))
1078 			{
1079 				P_DamageMobj(mo, NULL, NULL, (-mo->mom.z - hurt_momz), NULL);
1080 			}
1081 
1082 			// -KM- 1999/01/31 Bouncy bouncy...
1083 			if (mo->extendedflags & EF_BOUNCE)
1084 			{
1085 				BounceOffPlane(mo, +1.0f);
1086 
1087 				// don't bounce forever on the floor
1088 				if (! (mo->flags & MF_NOGRAVITY) &&
1089 					fabs(mo->mom.z) < STOPSPEED + fabs(gravity))
1090 				{
1091 					mo->mom.x = mo->mom.y = mo->mom.z = 0;
1092 				}
1093 			}
1094 			else
1095 				mo->mom.z = 0;
1096 		}
1097 
1098 		mo->z = mo->floorz;
1099 
1100 		if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
1101 		{
1102 			// -AJA- 2003/10/09: handle missiles that hit a monster on
1103 			//       the head from a sharp downward angle (such a case
1104 			//       is missed by PIT_CheckRelThing).  FIXME: more kludge.
1105 
1106 			if (mo->below_mo && (int)mo->floorz ==
1107 				(int)(mo->below_mo->z + mo->below_mo->info->height) &&
1108 				(mo->below_mo->flags & MF_SHOOTABLE) &&
1109 				(mo->source != mo->below_mo))
1110 			{
1111 				if (P_MissileContact(mo, mo->below_mo) < 0 ||
1112 					(mo->extendedflags & EF_TUNNEL))
1113 					return;
1114 			}
1115 
1116 			// if the floor is sky, don't explode missile -ACB- 1998/07/31
1117 			if (IS_SKY(mo->subsector->sector->floor) &&
1118 				mo->subsector->sector->f_h >= mo->floorz)
1119 			{
1120 				P_MobjRemoveMissile(mo);
1121 			}
1122 			else
1123 			{
1124 				if (! (mo->extendedflags & EF_BOUNCE))
1125 					P_MobjExplodeMissile(mo);
1126 			}
1127 			return;
1128 		}
1129 	}
1130 	else if (gravity > 0.0f)
1131 	{
1132 		// thing is above the ground, therefore apply gravity
1133 
1134 		// -MH- 1998/08/18 - Disable gravity while player has jetpack
1135 		//                   (nearly forgot this one:-)
1136 
1137 		if (!(mo->flags & MF_NOGRAVITY) &&
1138 			!(mo->player && mo->player->powers[PW_Jetpack] > 0) &&
1139 			!(mo->on_ladder >= 0))
1140 		{
1141 			mo->mom.z -= gravity;
1142 		}
1143 	}
1144 
1145 	//
1146 	//  HIT CEILING ?
1147 	//
1148 
1149 	if (mo->z + mo->height > mo->ceilingz)
1150 	{
1151 		if (mo->flags & MF_SKULLFLY)
1152 			mo->mom.z = -mo->mom.z;  // the skull slammed into something
1153 
1154 		// hit the ceiling
1155 		if (mo->mom.z > 0)
1156 		{
1157 			float hurt_momz = gravity * mo->info->maxfall;
1158 			bool fly_or_swim = mo->player && (mo->player->swimming ||
1159 				mo->player->powers[PW_Jetpack] > 0 || mo->on_ladder >= 0);
1160 
1161 			if (mo->player && gravity < 0 && zmove > OOF_SPEED && ! fly_or_swim)
1162 			{
1163 				mo->player->deltaviewheight = zmove / 8.0f;
1164 				S_StartFX(mo->info->oof_sound, P_MobjGetSfxCategory(mo), mo);
1165 			}
1166 			if (mo->info->maxfall > 0 && gravity < 0 && mo->mom.z > hurt_momz &&
1167 				(! mo->player || ! fly_or_swim))
1168 			{
1169 				P_DamageMobj(mo, NULL, NULL, (mo->mom.z - hurt_momz), NULL);
1170 			}
1171 
1172 			// -KM- 1999/01/31 More bouncing.
1173 			if (mo->extendedflags & EF_BOUNCE)
1174 			{
1175 				BounceOffPlane(mo, -1.0f);
1176 
1177 				// don't bounce forever on the ceiling
1178 				if (! (mo->flags & MF_NOGRAVITY) &&
1179 					fabs(mo->mom.z) < STOPSPEED + fabs(gravity))
1180 				{
1181 					mo->mom.x = mo->mom.y = mo->mom.z = 0;
1182 				}
1183 			}
1184 			else
1185 				mo->mom.z = 0;
1186 		}
1187 
1188 		mo->z = mo->ceilingz - mo->height;
1189 
1190 		if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
1191 		{
1192 			if (mo->above_mo && (int)mo->ceilingz == (int)(mo->above_mo->z) &&
1193 				(mo->above_mo->flags & MF_SHOOTABLE) &&
1194 				(mo->source != mo->above_mo))
1195 			{
1196 				if (P_MissileContact(mo, mo->above_mo) < 0 ||
1197 					(mo->extendedflags & EF_TUNNEL))
1198 					return;
1199 			}
1200 
1201 			// if the ceiling is sky, don't explode missile -ACB- 1998/07/31
1202 			if (IS_SKY(mo->subsector->sector->ceil) &&
1203 				mo->subsector->sector->c_h <= mo->ceilingz)
1204 			{
1205 				P_MobjRemoveMissile(mo);
1206 			}
1207 			else
1208 			{
1209 				if (! (mo->extendedflags & EF_BOUNCE))
1210 					P_MobjExplodeMissile(mo);
1211 			}
1212 			return;
1213 		}
1214 	}
1215 	else if (gravity < 0.0f)
1216 	{
1217 		// thing is below ceiling, therefore apply any negative gravity
1218 
1219 		// -MH- 1998/08/18 - Disable gravity while player has jetpack
1220 		//                   (nearly forgot this one:-)
1221 
1222 		if (!(mo->flags & MF_NOGRAVITY) &&
1223 			!(mo->player && mo->player->powers[PW_Jetpack] > 0) &&
1224 			!(mo->on_ladder >= 0))
1225 		{
1226 			mo->mom.z += -gravity;
1227 		}
1228 	}
1229 
1230 	// update the object's vertical region
1231 	P_TryMove(mo, mo->x, mo->y);
1232 
1233 	// apply drag -- but not to frictionless things
1234 	if ((mo->extendedflags & EF_NOFRICTION) || (mo->flags & MF_SKULLFLY))
1235 		return;
1236 
1237 	// ladders have friction
1238 	if (mo->on_ladder >= 0)
1239 		mo->mom.z *= LADDER_FRICTION;
1240 	else if (mo->player && mo->player->powers[PW_Jetpack] > 0)
1241 		mo->mom.z *= props->friction;
1242 	else
1243 		mo->mom.z *= props->drag;
1244 
1245 	if (mo->player)
1246 	{
1247 		if (fabs(mo->mom.z) < STOPSPEED &&
1248 			mo->player->cmd.upwardmove == 0)
1249 		{
1250 			mo->mom.z = 0;
1251 		}
1252 	}
1253 }
1254 
1255 //
1256 // P_MobjThinker
1257 //
1258 #define MAX_THINK_LOOP  8
1259 
P_MobjThinker(mobj_t * mobj)1260 static void P_MobjThinker(mobj_t * mobj)
1261 {
1262 	const region_properties_t *props;
1263 	region_properties_t player_props;
1264 
1265 	SYS_ASSERT_MSG(mobj->next != (mobj_t *)-1,
1266 		("P_MobjThinker INTERNAL ERROR: mobj has been Z_Freed"));
1267 
1268 	SYS_ASSERT(mobj->state);
1269 	SYS_ASSERT(mobj->refcount >= 0);
1270 
1271 	mobj->ClearStaleRefs();
1272 
1273 	mobj->visibility = (15 * mobj->visibility + mobj->vis_target)  / 16;
1274 	mobj->dlight.r   = (15 * mobj->dlight.r + mobj->dlight.target) / 16;
1275 
1276 	// position interpolation
1277 	if (mobj->lerp_num > 1)
1278 	{
1279 		mobj->lerp_pos++;
1280 
1281 		if (mobj->lerp_pos >= mobj->lerp_num)
1282 		{
1283 			mobj->lerp_pos = mobj->lerp_num = 0;
1284 		}
1285 	}
1286 
1287 	// handle SKULLFLY attacks
1288 	if ((mobj->flags & MF_SKULLFLY) && mobj->mom.x == 0 && mobj->mom.y == 0)
1289 	{
1290 		// the skull slammed into something
1291 		mobj->flags &= ~MF_SKULLFLY;
1292 		mobj->mom.x = mobj->mom.y = mobj->mom.z = 0;
1293 
1294 		P_SetMobjState(mobj, mobj->info->idle_state);
1295 
1296 		if (mobj->isRemoved()) return;
1297 	}
1298 
1299 	// determine properties, & handle push sectors
1300 
1301 	SYS_ASSERT(mobj->props);
1302 
1303 	if (mobj->player)
1304 	{
1305 		P_CalcFullProperties(mobj, &player_props);
1306 
1307 		mobj->mom.x += player_props.push.x;
1308 		mobj->mom.y += player_props.push.y;
1309 		mobj->mom.z += player_props.push.z;
1310 
1311 		props = &player_props;
1312 	}
1313 	else
1314 	{
1315 		props = mobj->props;
1316 
1317 		if (props->push.x || props->push.y || props->push.z)
1318 		{
1319 			sector_flag_e flags = props->special ?
1320 				props->special->special_flags : SECSP_PushConstant;
1321 
1322 			if (!((mobj->flags & MF_NOGRAVITY) || (flags & SECSP_PushAll))  &&
1323 				(mobj->z <= mobj->floorz + 1.0f || (flags & SECSP_WholeRegion)))
1324 			{
1325 				float push_mul = 1.0f;
1326 
1327 				SYS_ASSERT(mobj->info->mass > 0);
1328 				if (! (flags & SECSP_PushConstant))
1329 					push_mul = 100.0f / mobj->info->mass;
1330 
1331 				mobj->mom.x += push_mul * props->push.x;
1332 				mobj->mom.y += push_mul * props->push.y;
1333 				mobj->mom.z += push_mul * props->push.z;
1334 			}
1335 		}
1336 	}
1337 
1338 	// momentum movement
1339 	if (mobj->mom.x != 0 || mobj->mom.y != 0 || mobj->player)
1340 	{
1341 		P_XYMovement(mobj, props);
1342 
1343 		if (mobj->isRemoved()) return;
1344 	}
1345 
1346 	if ((mobj->z != mobj->floorz) || mobj->mom.z != 0) //  || mobj->ride_em)
1347 	{
1348 		P_ZMovement(mobj, props);
1349 
1350 		if (mobj->isRemoved()) return;
1351 	}
1352 
1353 	if (mobj->fuse >= 0)
1354 	{
1355 		if (!--mobj->fuse)
1356 			P_MobjExplodeMissile(mobj);
1357 
1358 		if (mobj->isRemoved()) return;
1359 	}
1360 
1361 	if (mobj->tics < 0)
1362 	{
1363 		// check for nightmare respawn
1364 		if (!(mobj->extendedflags & EF_MONSTER))
1365 			return;
1366 
1367 		// replaced respawnmonsters & newnmrespawn with respawnsetting
1368 		// -ACB- 1998/07/30
1369 		if (!level_flags.respawn)
1370 			return;
1371 
1372 		mobj->movecount++;
1373 
1374 		//
1375 		// Uses movecount as a timer, when movecount hits 12*TICRATE the
1376 		// object will try to respawn. So after 12 seconds the object will
1377 		// try to respawn.
1378 		//
1379 		if (mobj->movecount < mobj->info->respawntime)
1380 			return;
1381 
1382 		// if the first 5 bits of leveltime are on, don't respawn now...ok?
1383 		if (leveltime & 31)
1384 			return;
1385 
1386 		// give a limited "random" chance that respawn don't respawn now
1387 		if (P_Random() > 32)
1388 			return;
1389 
1390 		// replaced respawnmonsters & newnmrespawn with respawnsetting
1391 		// -ACB- 1998/07/30
1392 		if (level_flags.res_respawn)
1393 			ResurrectRespawn(mobj);
1394 		else
1395 			TeleportRespawn(mobj);
1396 
1397 		return;
1398 	}
1399 
1400 	// Cycle through states, calling action functions at transitions.
1401 	// -AJA- 1999/09/12: reworked for deferred states.
1402 	// -AJA- 2000/10/17: reworked again.
1403 
1404 	for (int loop_count=0; loop_count < MAX_THINK_LOOP; loop_count++)
1405 	{
1406 		mobj->tics -= (1 + mobj->tic_skip);
1407 		mobj->tic_skip = 0;
1408 
1409 		if (mobj->tics >= 1)
1410 			break;
1411 
1412 		// You can cycle through multiple states in a tic.
1413 		// NOTE: returns false if object freed itself.
1414 
1415 		P_SetMobjState2(mobj, mobj->next_state ?
1416 			(mobj->next_state - states) : S_NULL);
1417 
1418 		if (mobj->isRemoved())
1419 			return;
1420 
1421 		if (mobj->tics != 0)
1422 			break;
1423 	}
1424 }
1425 
1426 //
1427 // P_RunMobjThinkers
1428 //
1429 // Cycle through all mobjs and let them think.
1430 //
P_RunMobjThinkers(void)1431 void P_RunMobjThinkers(void)
1432 {
1433 	mobj_t *mo;
1434 	mobj_t *next;
1435 
1436 	for (mo = mobjlisthead; mo; mo = next)
1437 	{
1438 		next = mo->next;
1439 
1440 		P_MobjThinker(mo);
1441 	}
1442 
1443 	P_RemoveQueuedMobjs(false);
1444 }
1445 
1446 
P_ClearAllStaleRefs(void)1447 void P_ClearAllStaleRefs(void)
1448 {
1449 	for (mobj_t * mo = mobjlisthead; mo; mo = mo->next)
1450 	{
1451 		mo->ClearStaleRefs();
1452 	}
1453 
1454 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
1455 	{
1456 		player_t *p = players[pnum];
1457 
1458 		if (p && p->attacker && p->attacker->isRemoved())
1459 			p->attacker = NULL;
1460 	}
1461 }
1462 
1463 
1464 //
1465 // P_RemoveQueuedMobjs
1466 //
1467 // Removes all the mobjs in the remove_queue list.
1468 //
1469 // -ES- 1999/10/24 Written.
1470 //
P_RemoveQueuedMobjs(bool force_all)1471 void P_RemoveQueuedMobjs(bool force_all)
1472 {
1473 	std::list<mobj_t *>::iterator M;
1474 
1475 	bool did_remove = false;
1476 
1477 	for (M = remove_queue.begin(); M != remove_queue.end(); M++)
1478 	{
1479 		mobj_t *mo = *M;
1480 
1481 		mo->fuse--;
1482 
1483 		if (!force_all && mo->fuse == 1 && mo->refcount != 0)
1484 			I_Warning("Bad ref count for %s = %d.\n",
1485 						mo->info->name.c_str(), mo->refcount);
1486 
1487 		if (force_all || (mo->fuse <= 0 && mo->refcount == 0))
1488 		{
1489 			DeleteMobj(mo);
1490 
1491 			// cannot mess with the list while traversing it,
1492 			// hence set the current entry to NULL and remove
1493 			// those nodes afterwards.
1494 			*M = NULL; did_remove = true;
1495 		}
1496 	}
1497 
1498 	if (did_remove)
1499 		remove_queue.remove(NULL);
1500 }
1501 
AddMobjToList(mobj_t * mo)1502 static void AddMobjToList(mobj_t *mo)
1503 {
1504 	mo->prev = NULL;
1505 	mo->next = mobjlisthead;
1506 
1507 	if (mo->next != NULL)
1508 	{
1509 		SYS_ASSERT(mo->next->prev == NULL);
1510 		mo->next->prev = mo;
1511 	}
1512 
1513 	mobjlisthead = mo;
1514 
1515 #if (DEBUG_MOBJ > 0)
1516 	L_WriteDebug("tics=%05d  ADD %p [%s]\n", leveltime, mo,
1517 		mo->info ? mo->info->name.c_str() : "???");
1518 #endif
1519 }
1520 
RemoveMobjFromList(mobj_t * mo)1521 static void RemoveMobjFromList(mobj_t *mo)
1522 {
1523 	if (mo->prev != NULL)
1524 	{
1525 		SYS_ASSERT(mo->prev->next == mo);
1526 		mo->prev->next = mo->next;
1527 	}
1528 	else // no previous, must be first item
1529 	{
1530 		SYS_ASSERT(mobjlisthead == mo);
1531 		mobjlisthead = mo->next;
1532 	}
1533 
1534 	if (mo->next != NULL)
1535 	{
1536 		SYS_ASSERT(mo->next->prev == mo);
1537 		mo->next->prev = mo->prev;
1538 	}
1539 
1540 	mo->next = (mobj_t *) -1;
1541 	mo->prev = (mobj_t *) -1;
1542 }
1543 
1544 //
1545 // P_RemoveMobj
1546 //
1547 // Removes the object from the play simulation: no longer thinks, if
1548 // the mobj is MF_SPECIAL: i.e. item can be picked up, it is added to
1549 // the item-respawn-que, so it gets respawned if needed; The respawning
1550 // only happens if itemrespawn is set or the deathmatch mode is
1551 // version 2.0: altdeath.
1552 //
P_RemoveMobj(mobj_t * mo)1553 void P_RemoveMobj(mobj_t *mo)
1554 {
1555 #if (DEBUG_MOBJ > 0)
1556 	L_WriteDebug("tics=%05d  REMOVE %p [%s]\n", leveltime, mo,
1557 		mo->info ? mo->info->name.c_str() : "???");
1558 #endif
1559 
1560 	if (mo->isRemoved())
1561 	{
1562 		L_WriteDebug("Warning: Object %p (%s) REMOVED TWICE\n",
1563 		     mo, mo->info ? mo->info->name.c_str() : "???");
1564 		return;
1565 	}
1566 
1567 	if ((mo->info->flags & MF_SPECIAL) &&
1568 	    ! (mo->flags & MF_MISSILE) &&
1569 		(deathmatch >= 2 || level_flags.itemrespawn) &&
1570 		!(mo->extendedflags & EF_NORESPAWN) &&
1571 		!(mo->flags & MF_DROPPED))
1572 	{
1573 		iteminque_t *newbie = Z_New(iteminque_t, 1);
1574 
1575 		newbie->spawnpoint = mo->spawnpoint;
1576 		newbie->time = mo->info->respawntime;
1577 
1578 		if (itemquehead == NULL)
1579 		{
1580 			newbie->next = newbie->prev = NULL;
1581 			itemquehead = newbie;
1582 		}
1583 		else
1584 		{
1585 			iteminque_t *tail;
1586 
1587 			for (tail = itemquehead; tail->next; tail = tail->next)
1588 			{ /* nothing */ }
1589 
1590 			newbie->next = NULL;
1591 			newbie->prev = tail;
1592 
1593 			tail->next = newbie;
1594 		}
1595 	}
1596 
1597 	// mark as REMOVED
1598 	mo->state = NULL;
1599 	mo->next_state = NULL;
1600 
1601 	// Clear all references to other mobjs
1602 	mo->SetTarget(NULL);
1603 	mo->SetSource(NULL);
1604 	mo->SetTracer(NULL);
1605 
1606 	mo->SetSupportObj(NULL);
1607 	mo->SetAboveMo(NULL);
1608 	mo->SetBelowMo(NULL);
1609 
1610 	// unlink from sector and block lists
1611 	P_UnsetThingFinally(mo);
1612 
1613 	// remove from global list
1614 	RemoveMobjFromList(mo);
1615 
1616 	// set timer to keep in the remove queue ("IN LIMBO")
1617 	mo->fuse = TICRATE * 3;
1618 
1619 	remove_queue.push_back(mo);
1620 }
1621 
P_RemoveAllMobjs(void)1622 void P_RemoveAllMobjs(void)
1623 {
1624 	while (mobjlisthead)
1625 	{
1626 		mobj_t *mo = mobjlisthead;
1627 
1628 		P_UnsetThingFinally(mo);
1629 
1630 		RemoveMobjFromList(mo);
1631 
1632 		mo->refcount = 0;
1633 		DeleteMobj(mo);
1634 	}
1635 }
1636 
1637 //
1638 // P_RemoveItemsInQue
1639 //
P_RemoveItemsInQue(void)1640 void P_RemoveItemsInQue(void)
1641 {
1642 	while (itemquehead)
1643 	{
1644 		iteminque_t *tmp = itemquehead;
1645 		itemquehead = itemquehead->next;
1646 		Z_Free(tmp);
1647 	}
1648 }
1649 
1650 //
1651 // GAME SPAWN FUNCTIONS
1652 //
1653 
1654 //
1655 // P_SpawnPuff
1656 //
P_SpawnPuff(float x,float y,float z,const mobjtype_c * puff,angle_t angle)1657 void P_SpawnPuff(float x, float y, float z, const mobjtype_c * puff, angle_t angle)
1658 {
1659 	mobj_t *th;
1660 
1661 	z += (float) P_RandomNegPos() / 80.0f;
1662 
1663 	// -ACB- 1998/08/06 Specials table for non-negotiables....
1664 	th = P_MobjCreateObject(x, y, z, puff);
1665 
1666 	// -AJA- 1999/07/14: DDF-itised.
1667 	th->mom.z = puff->float_speed;
1668 
1669 	// -AJA- 2011/03/14: set the angle
1670 	th->angle = angle;
1671 
1672 	th->tics -= P_Random() & 3;
1673 
1674 	if (th->tics < 1)
1675 		th->tics = 1;
1676 }
1677 
1678 //
1679 // P_SpawnBlood
1680 //
1681 // -KM- 1998/11/25 Made more violent. :-)
1682 // -KM- 1999/01/31 Different blood objects for different mobjs.
1683 //
P_SpawnBlood(float x,float y,float z,float damage,angle_t angle,const mobjtype_c * blood)1684 void P_SpawnBlood(float x, float y, float z, float damage,
1685 				  angle_t angle, const mobjtype_c * blood)
1686 {
1687 	int num;
1688 	mobj_t *th;
1689 
1690 	angle += ANG180;
1691 
1692 	num = (int) (!level_flags.more_blood ? 1.0f : (M_Random() % 7) +
1693 		(float)((MAX(damage / 4.0f, 7.0f))));
1694 
1695 	while (num--)
1696 	{
1697 		z += (float)(P_RandomNegPos() / 64.0f);
1698 
1699 		angle += (angle_t) (P_RandomNegPos() * (int)(ANG1 / 2));
1700 
1701 		th = P_MobjCreateObject(x, y, z, blood);
1702 
1703 		P_SetMobjDirAndSpeed(th, angle, ((float)num + 12.0f) / 6.0f,
1704 			(float)num / 4.0f);
1705 
1706 		th->tics -= P_Random() & 3;
1707 
1708 		if (th->tics < 1)
1709 			th->tics = 1;
1710 
1711 		if (damage <= 12 && th->state && th->next_state)
1712 			P_SetMobjState(th, th->next_state - states);
1713 
1714 		if (damage <= 8 && th->state && th->next_state)
1715 			P_SetMobjState(th, th->next_state - states);
1716 	}
1717 }
1718 
1719 //
1720 // P_MobjItemRespawn
1721 //
1722 // Replacement procedure for P_RespawnSpecials, uses a linked list to go through
1723 // the item-respawn-que. The time until respawn (in tics) is decremented every tic,
1724 // when the item-in-the-que has a time of zero is it respawned.
1725 //
1726 // -ACB- 1998/07/30 Procedure written.
1727 // -KM- 1999/01/31 Custom respawn fog.
1728 //
P_MobjItemRespawn(void)1729 void P_MobjItemRespawn(void)
1730 {
1731 	float x, y, z;
1732 	mobj_t *mo;
1733 	const mobjtype_c *objtype;
1734 
1735 	iteminque_t *cur, *next;
1736 
1737 	// only respawn items in deathmatch or if itemrespawn
1738 	if (! (deathmatch >= 2 || level_flags.itemrespawn))
1739 		return;
1740 
1741 	// No item-respawn-que exists, so nothing to process.
1742 	if (itemquehead == NULL)
1743 		return;
1744 
1745 	// lets start from the beginning....
1746 	for (cur = itemquehead; cur; cur = next)
1747 	{
1748 		next = cur->next;
1749 
1750 		cur->time--;
1751 
1752 		if (cur->time > 0)
1753 			continue;
1754 
1755 		// no time left, so respawn object
1756 
1757 		x = cur->spawnpoint.x;
1758 		y = cur->spawnpoint.y;
1759 		z = cur->spawnpoint.z;
1760 
1761 		objtype = cur->spawnpoint.info;
1762 
1763 		if (objtype == NULL)
1764 		{
1765 			I_Error("P_MobjItemRespawn: No such item type!");
1766 			return;  // shouldn't happen.
1767 		}
1768 
1769 		// spawn a teleport fog at the new spot
1770 		SYS_ASSERT(objtype->respawneffect);
1771 		P_MobjCreateObject(x, y, z, objtype->respawneffect);
1772 
1773 		// -ACB- 1998/08/06 Use MobjCreateObject
1774 		mo = P_MobjCreateObject(x, y, z, objtype);
1775 
1776 		mo->angle = cur->spawnpoint.angle;
1777 		mo->vertangle = cur->spawnpoint.vertangle;
1778 		mo->spawnpoint = cur->spawnpoint;
1779 
1780 		// Taking this item-in-que out of the que, remove
1781 		// any references by the previous and next items to
1782 		// the current one.....
1783 
1784 		if (cur->next)
1785 			cur->next->prev = cur->prev;
1786 
1787 		if (cur->prev)
1788 			cur->prev->next = next;
1789 		else
1790 			itemquehead = next;
1791 
1792 		Z_Free(cur);
1793 	}
1794 }
1795 
1796 //
1797 // P_MobjRemoveMissile
1798 //
1799 // This procedure only is used when a flying missile is removed because
1800 // it "hit" a wall or ceiling that in the simulation acts as a sky. The
1801 // only major differences with P_RemoveMobj are that now item respawn check
1802 // is not done (not needed) and any sound will continue playing despite
1803 // the fact the missile has been removed: This is only done due to the
1804 // fact that a missile in reality would continue flying through a sky and
1805 // you should still be able to hear it.
1806 //
1807 // -ACB- 1998/07/31 Procedure written.
1808 // -AJA- 1999/09/15: Functionality subsumed by DoRemoveMobj.
1809 // -ES- 1999/10/24 Removal Queue.
1810 //
P_MobjRemoveMissile(mobj_t * missile)1811 void P_MobjRemoveMissile(mobj_t * missile)
1812 {
1813 	P_RemoveMobj(missile);
1814 
1815 	missile->mom.x = missile->mom.y = missile->mom.z = 0;
1816 
1817 	missile->flags &= ~(MF_MISSILE | MF_TOUCHY);
1818 	missile->extendedflags &= ~(EF_BOUNCE);
1819 }
1820 
1821 //
1822 // P_MobjCreateObject
1823 //
1824 // Creates a Map Object (MOBJ) at the specified location, with the
1825 // specified type (given by DDF).  The special z values ONFLOORZ and
1826 // ONCEILINGZ are recognised and handled appropriately.
1827 //
1828 // -ACB- 1998/08/02 Procedure written.
1829 //
P_MobjCreateObject(float x,float y,float z,const mobjtype_c * info)1830 mobj_t *P_MobjCreateObject(float x, float y, float z, const mobjtype_c *info)
1831 {
1832 	mobj_t *mobj = Z_New(mobj_t, 1);
1833 
1834 	Z_Clear(mobj, mobj_t, 1);
1835 
1836 #if (DEBUG_MOBJ > 0)
1837 	L_WriteDebug("tics=%05d  CREATE %p [%s]  AT %1.0f,%1.0f,%1.0f\n",
1838 		leveltime, mobj, info->name.c_str(), x, y, z);
1839 #endif
1840 
1841 	mobj->info = info;
1842 	mobj->x = x;
1843 	mobj->y = y;
1844 	mobj->radius = info->radius;
1845 	mobj->height = info->height;
1846 	mobj->flags = info->flags;
1847 	mobj->health = info->spawnhealth;
1848 	mobj->speed = info->speed;
1849 	mobj->fuse = info->fuse;
1850 	mobj->side = info->side;
1851 	mobj->model_skin = info->model_skin;
1852 	mobj->model_last_frame = -1;
1853 
1854 	if (level_flags.fastparm)
1855 		mobj->speed *= info->fast;
1856 
1857 	// -ACB- 1998/06/25 new mobj Stuff (1998/07/11 - invisibility added)
1858 	mobj->extendedflags = info->extendedflags;
1859 	mobj->hyperflags = info->hyperflags;
1860 	mobj->vis_target = mobj->visibility = PERCENT_2_FLOAT(info->translucency);
1861 
1862 	mobj->currentattack = NULL;
1863 	mobj->on_ladder = -1;
1864 
1865 	if (gameskill != sk_nightmare)
1866 		mobj->reactiontime = info->reactiontime;
1867 
1868 	mobj->lastlook = P_Random() % MAXPLAYERS;
1869 
1870 	//
1871 	// Do not set the state with P_SetMobjState,
1872 	// because action routines can not be called yet
1873 	//
1874 	// if we have a spawnstate use that; else try the meanderstate
1875 	// -ACB- 1998/09/06
1876 	//
1877 	// -AJA- So that the first action gets executed, the `next_state'
1878 	//       is set to the first state and `tics' set to 0.
1879 	//
1880 	state_t *st;
1881 
1882 	if (info->spawn_state)
1883 		st = &states[info->spawn_state];
1884 	else if (info->meander_state)
1885 		st = &states[info->meander_state];
1886 	else
1887 		st = &states[info->idle_state];
1888 
1889 	mobj->state  = st;
1890 	mobj->tics   = 0;
1891 	mobj->next_state = st;
1892 
1893 	SYS_ASSERT(! mobj->isRemoved());
1894 
1895 	// enable usable items
1896 	if (mobj->extendedflags & EF_USABLE)
1897 		mobj->flags |= MF_TOUCHY;
1898 
1899 	// handle dynamic lights
1900 	{
1901 		const dlight_info_c *dinfo = &info->dlight[0];
1902 
1903 		if (dinfo->type != DLITE_None)
1904 		{
1905 			mobj->dlight.r = mobj->dlight.target = dinfo->radius;
1906 			mobj->dlight.color = dinfo->colour;
1907 
1908 			// leave 'shader' field as NULL : renderer will create it
1909 		}
1910 	}
1911 
1912 	// set subsector and/or block links
1913 	P_SetThingPosition(mobj);
1914 
1915 	// -AJA- 1999/07/30: Updated for extra floors.
1916 
1917 	sector_t *sec = mobj->subsector->sector;
1918 
1919 	mobj->z = P_ComputeThingGap(mobj, sec, z, &mobj->floorz, &mobj->ceilingz);
1920 
1921 	// Find the real players height (TELEPORT WEAPONS).
1922 	mobj->origheight = z;
1923 
1924 	// update totals for countable items.  Doing it here means that
1925 	// things spawned dynamically can be counted as well.  Whilst this
1926 	// has its dangers, at least it is consistent (more than can be said
1927 	// when RTS comes into play -- trying to second guess which
1928 	// spawnthings should not be counted just doesn't work).
1929 
1930 	if (mobj->flags & MF_COUNTKILL)
1931 		wi_stats.kills++;
1932 
1933 	if (mobj->flags & MF_COUNTITEM)
1934 		wi_stats.items++;
1935 
1936 	//
1937 	// -ACB- 1998/08/27 Mobj Linked-List Addition
1938 	//
1939 	// A useful way of cycling through the current things without
1940 	// having to deref everything using thinkers.
1941 	//
1942 	// -AJA- 1999/09/15: now adds to _head_ of list (for speed).
1943 	//
1944 	AddMobjToList(mobj);
1945 
1946 	return mobj;
1947 }
1948 
1949 //
1950 // P_MobjGetSfxCategory
1951 //
1952 // Returns the sound category for an object.
1953 //
P_MobjGetSfxCategory(const mobj_t * mo)1954 int P_MobjGetSfxCategory(const mobj_t *mo)
1955 {
1956     if (mo->player)
1957     {
1958         if (mo->player == players[displayplayer])
1959             return SNCAT_Player;
1960 
1961 		return SNCAT_Opponent;
1962     }
1963     else
1964     {
1965         if (mo->extendedflags & EF_MONSTER)
1966             return SNCAT_Monster;
1967 
1968 		return SNCAT_Object;
1969     }
1970 }
1971 
1972 
1973 //--- editor settings ---
1974 // vi:ts=4:sw=4:noexpandtab
1975