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