1 //----------------------------------------------------------------------------
2 //  EDGE Play Simulation Action routines
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 // Notes:
27 //  All Procedures here are never called directly, except possibly
28 //  by another P_Act* Routine. Otherwise the procedure is called
29 //  by referencing an code pointer from the states[] table. The only
30 //  exception to these rules are P_ActMissileContact and
31 //  P_SlammedIntoObject that requiring "acting" on the part
32 //  of an obj.
33 //
34 // This file was created for all action code by DDF.
35 //
36 // -KM- 1998/09/27 Added sounds.ddf capability
37 // -KM- 1998/12/21 New smooth visibility.
38 // -AJA- 1999/07/21: Replaced some non-critical P_Randoms with M_Random.
39 // -AJA- 1999/08/08: Replaced some P_Random()-P_Random() stuff.
40 //
41 
42 #include "i_defs.h"
43 #include "p_action.h"
44 
45 #include "con_main.h"
46 #include "dm_defs.h"
47 #include "dm_state.h"
48 #include "g_game.h"
49 #include "m_misc.h"
50 #include "m_random.h"
51 #include "p_local.h"
52 #include "p_weapon.h"
53 #include "r_state.h"
54 #include "rad_trig.h"
55 #include "s_sound.h"
56 #include "w_wad.h"
57 #include "z_zone.h"
58 
59 
60 cvar_c g_aggression;
61 
62 
AttackSfxCat(const mobj_t * mo)63 static int AttackSfxCat(const mobj_t *mo)
64 {
65 	int category = P_MobjGetSfxCategory(mo);
66 
67 	if (category == SNCAT_Player)
68 		return SNCAT_Weapon;
69 
70 	return category;
71 }
72 
SfxFlags(const mobjtype_c * info)73 static int SfxFlags(const mobjtype_c *info)
74 {
75 	int flags = 0;
76 
77 	if (info->extendedflags & EF_ALWAYSLOUD)
78 		flags |= FX_Boss;
79 
80 	return flags;
81 }
82 
83 //-----------------------------------------
84 //--------------MISCELLANOUS---------------
85 //-----------------------------------------
86 
87 //
88 // P_ActActivateLineType
89 //
90 // Allows things to also activate linetypes, bringing them into the
91 // fold with radius triggers, which can also do it.  There's only two
92 // parameters needed: linetype number & tag number, which are stored
93 // in the state's `action_par' field as a pointer to two integers.
94 //
P_ActActivateLineType(mobj_t * mo)95 void P_ActActivateLineType(mobj_t * mo)
96 {
97 	int *values;
98 
99 	if (!mo->state || !mo->state->action_par)
100 		return;
101 
102 	values = (int *) mo->state->action_par;
103 
104 	// Note the `NULL' here: this prevents the activation from failing
105 	// because the object isn't a PLAYER, for example.
106 	P_RemoteActivation(NULL, values[0], values[1], 0, line_Any);
107 }
108 
109 //
110 // P_ActEnableRadTrig
111 // P_ActDisableRadTrig
112 //
113 // Allows things to enable or disable radius triggers (by tag number),
114 // like linetypes can do already.
115 //
P_ActEnableRadTrig(mobj_t * mo)116 void P_ActEnableRadTrig(mobj_t * mo)
117 {
118 	if (!mo->state || !mo->state->action_par)
119 		return;
120 
121 	int *value = (int *) mo->state->action_par;
122 
123 	RAD_EnableByTag(mo, value[0], false);
124 }
125 
P_ActDisableRadTrig(mobj_t * mo)126 void P_ActDisableRadTrig(mobj_t * mo)
127 {
128 	if (!mo->state || !mo->state->action_par)
129 		return;
130 
131 	int *value = (int *) mo->state->action_par;
132 
133 	RAD_EnableByTag(mo, value[0], true);
134 }
135 
136 //
137 // P_ActLookForTargets
138 //
139 // Looks for targets: used in the same way as enemy things look
140 // for players
141 //
142 // TODO: Write a decent procedure.
143 // -KM- 1999/01/31 Added sides. Still has to search every mobj on the
144 //  map to find a target.  There must be a better way...
145 // -AJA- 2004/04/28: Rewritten. Mobjs on same side are never targeted.
146 //
147 // NOTE: a better way might be: do a mini "BSP render", use a small 1D
148 //       occlusion buffer (e.g. 64 bits).
149 //
P_ActLookForTargets(mobj_t * we)150 bool P_ActLookForTargets(mobj_t *we)
151 {
152 	mobj_t *them;
153 
154 	// Optimisation: nobody to support when side is zero
155 	if (we->side == 0)
156 		return P_LookForPlayers(we, we->info->sight_angle);
157 
158 	for (them = mobjlisthead; them != NULL; them = them->next)
159 	{
160 		if (them == we)
161 			continue;
162 
163 		bool same_side = ((them->side & we->side) != 0);
164 
165 		// only target monsters or players (not barrels)
166 		if (! (them->extendedflags & EF_MONSTER) && ! them->player)
167 			continue;
168 
169 		if (! (them->flags & MF_SHOOTABLE))
170 			continue;
171 
172 		if (same_side && !we->supportobj && them->supportobj != we)
173 		{
174 			if (them->supportobj && P_CheckSight(we, them->supportobj))
175 				them = them->supportobj;
176 			else if (! P_CheckSight(we, them))
177 				continue;  // OK since same side
178 
179 			if (them)
180 			{
181 				we->SetSupportObj(them);
182 				if (we->info->meander_state)
183 					P_SetMobjStateDeferred(we, we->info->meander_state, 0);
184 				return true;
185 			}
186 		}
187 
188 		if (same_side)
189 			continue;
190 
191 		/// if (them == we->supportobj || we == them->supportobj ||
192 		///	(them->supportobj && them->supportobj == we->supportobj))
193 		///	continue;
194 
195 		if ((we->info == them->info) && ! (we->extendedflags & EF_DISLOYALTYPE))
196 			continue;
197 
198         /// -AJA- IDEALLY: use this to prioritize possible targets.
199 		/// if (! (them->target &&
200 		///	  (them->target == we || them->target == we->supportobj)))
201 		///	continue;
202 
203 		if (P_CheckSight(we, them))
204 		{
205 			we->SetTarget(them);
206 			if (we->info->chase_state)
207 				P_SetMobjStateDeferred(we, we->info->chase_state, 0);
208 			return true;
209 		}
210 	}
211 
212 	return false;
213 }
214 
215 //
216 // DecideMeleeAttack
217 //
218 // This is based on P_CheckMeleeRange, except that it relys upon
219 // info from the objects close combat attack, the original code
220 // used a set value for all objects which was MELEERANGE + 20,
221 // this code allows different melee ranges for different objects.
222 //
223 // -ACB- 1998/08/15
224 // -KM- 1998/11/25 Added attack parameter.
225 //
DecideMeleeAttack(mobj_t * object,const atkdef_c * attack)226 static bool DecideMeleeAttack(mobj_t * object, const atkdef_c * attack)
227 {
228 	mobj_t *target;
229 	float distance;
230 	float meleedist;
231 
232 	target = object->target;
233 
234 	if (!target)
235 		return false;
236 
237 	distance = P_ApproxDistance(target->x - object->x, target->y - object->y);
238 
239 	if (level_flags.true3dgameplay)
240 		distance = P_ApproxDistance(target->z - object->z, distance);
241 
242 	meleedist = attack ? attack->range : MELEERANGE;
243 	meleedist += target->radius - 20.0f;	// Check the thing's actual radius
244 
245 	if (distance >= meleedist)
246 		return false;
247 
248 	return P_CheckSight(object, target);
249 }
250 
251 //
252 // DecideRangeAttack
253 //
254 // This is based on P_CheckMissileRange, contrary the name it does more
255 // than check the missile range, it makes a decision of whether or not an
256 // attack should be made or not depending on the object with the option
257 // to attack. A return of false is mandatory if the object cannot see its
258 // target (makes sense, doesn't it?), after this the distance is calculated,
259 // it will eventually be check to see if it is greater than a number from
260 // the Random Number Generator; if so the procedure returns true. Essentially
261 // the closer the object is to its target, the more chance an attack will
262 // be made (another logical decision).
263 //
264 // -ACB- 1998/08/15
265 //
DecideRangeAttack(mobj_t * object)266 static bool DecideRangeAttack(mobj_t * object)
267 {
268 	percent_t chance;
269 	float distance;
270 	const atkdef_c *attack;
271 
272 	if (! object->target)
273 		return false;
274 
275 	if (object->info->rangeattack)
276 		attack = object->info->rangeattack;
277 	else
278 		return false;  // cannot evaluate range with no attack range
279 
280 	// Just been hit (and have felt pain), so in true tit-for-tat
281 	// style, the object - without regard to anything else - hits back.
282 	if (object->flags & MF_JUSTHIT)
283 	{
284 		if (! P_CheckSight(object, object->target))
285                 	return false;
286 
287 		object->flags &= ~MF_JUSTHIT;
288 		return true;
289 	}
290 
291 	// Bit slow on the up-take: the object hasn't had time to
292 	// react his target.
293 	if (object->reactiontime)
294 		return false;
295 
296 	// Get the distance, a basis for our decision making from now on
297 	distance = P_ApproxDistance(object->x - object->target->x,
298 								object->y - object->target->y);
299 
300 	// If no close-combat attack, increase the chance of a missile attack
301 	if (!object->info->melee_state)
302 		distance -= 192;
303 	else
304 		distance -= 64;
305 
306 	// Object is too far away to attack?
307 	if (attack->range && distance >= attack->range)
308 		return false;
309 
310 	// Object is too close to target
311 	if (attack->tooclose && attack->tooclose >= distance)
312 		return false;
313 
314 	// Object likes to fire? if so, double the chance of it happening
315 	if (object->extendedflags & EF_TRIGGERHAPPY)
316 		distance /= 2;
317 
318 	// The chance in the object is one given that the attack will happen, so
319 	// we inverse the result (since its one in 255) to get the chance that
320 	// the attack will not happen.
321 	chance = 1.0f - object->info->minatkchance;
322 	chance = MIN(distance / 255.0f, chance);
323 
324 	// now after modifing distance where applicable, we get the random number and
325 	// check if it is less than distance, if so no attack is made.
326 	if (P_RandomTest(chance))
327 		return false;
328 
329 	return P_CheckSight(object, object->target);
330 }
331 
332 //
333 // P_ActFaceTarget
334 //
335 // Look at the prey......
336 //
P_ActFaceTarget(mobj_t * object)337 void P_ActFaceTarget(mobj_t * object)
338 {
339 	mobj_t *target = object->target;
340 
341 	if (!target)
342 		return;
343 
344 	if (object->flags & MF_STEALTH)
345 		object->vis_target = VISIBLE;
346 
347 	object->flags &= ~MF_AMBUSH;
348 
349 	object->angle = R_PointToAngle(object->x, object->y, target->x, target->y);
350 
351 	float dist = R_PointToDist(object->x, object->y, target->x, target->y);
352 
353 	if (dist >= 0.1f)
354 	{
355 		float dz = MO_MIDZ(target) - MO_MIDZ(object);
356 
357 		object->vertangle = M_ATan(dz / dist);
358 	}
359 
360 	if (target->flags & MF_FUZZY)
361 	{
362 		object->angle += P_RandomNegPos() << (ANGLEBITS - 11);
363 		object->vertangle += M_ATan(P_RandomNegPos() / 1024.0f);
364 	}
365 
366 	if (target->visibility < VISIBLE)
367 	{
368 		float amount = (VISIBLE - target->visibility);
369 
370 		object->angle += (angle_t)(P_RandomNegPos() * (ANGLEBITS - 12) * amount);
371 		object->vertangle += M_ATan(P_RandomNegPos() * amount / 2048.0f);
372 	}
373 
374 	// don't look up/down too far...
375 	if (object->vertangle < ANG180 && object->vertangle > ANG45)
376 		object->vertangle = ANG45;
377 
378 	if (object->vertangle >= ANG180 && object->vertangle < ANG315)
379 		object->vertangle = ANG315;
380 }
381 
382 
P_ActMakeIntoCorpse(mobj_t * mo)383 void P_ActMakeIntoCorpse(mobj_t * mo)
384 {
385 	// Gives the effect of the object being a corpse....
386 
387 	if (mo->flags & MF_STEALTH)
388 		mo->vis_target = VISIBLE;  // dead and very visible
389 
390 	// object is on ground, it can be walked over
391 	mo->flags &= ~MF_SOLID;
392 
393 	mo->tag = 0;
394 }
395 
396 
P_BringCorpseToLife(mobj_t * corpse)397 void P_BringCorpseToLife(mobj_t * corpse)
398 {
399 	// Bring a corpse back to life (the opposite of the above routine).
400 	// Handles players too !
401 
402 	const mobjtype_c *info = corpse->info;
403 
404 	corpse->flags = info->flags;
405 	corpse->health = info->spawnhealth;
406 	corpse->radius = info->radius;
407 	corpse->height = info->height;
408 	corpse->extendedflags = info->extendedflags;
409 	corpse->hyperflags = info->hyperflags;
410 	corpse->vis_target = PERCENT_2_FLOAT(info->translucency);
411 	corpse->tag = corpse->spawnpoint.tag;
412 
413 	if (corpse->player)
414 	{
415 		corpse->player->playerstate = PST_LIVE;
416 		corpse->player->health = corpse->health;
417 		corpse->player->std_viewheight = corpse->height *
418 			PERCENT_2_FLOAT(info->viewheight);
419 	}
420 
421 	if (info->overkill_sound)
422 		S_StartFX(info->overkill_sound, P_MobjGetSfxCategory(corpse), corpse);
423 
424 	if (info->raise_state)
425 		P_SetMobjState(corpse, info->raise_state);
426 	else if (info->meander_state)
427 		P_SetMobjState(corpse, info->meander_state);
428 	else if (info->idle_state)
429 		P_SetMobjState(corpse, info->idle_state);
430 	else
431 		I_Error("Object %s has no RESURRECT states.\n", info->name.c_str());
432 }
433 
434 
P_ActResetSpreadCount(mobj_t * mo)435 void P_ActResetSpreadCount(mobj_t * mo)
436 {
437 	// Resets the spreader count for fixed-order spreaders, normally used
438 	// at the beginning of a set of missile states to ensure that an object
439 	// fires in the same object each time.
440 
441 	mo->spreadcount = 0;
442 }
443 
444 //-------------------------------------------------------------------
445 //-------------------VISIBILITY HANDLING ROUTINES--------------------
446 //-------------------------------------------------------------------
447 
P_ActTransSet(mobj_t * mo)448 void P_ActTransSet(mobj_t * mo)
449 {
450 	float value = VISIBLE;
451 
452 	const state_t *st = mo->state;
453 
454 	if (st && st->action_par)
455 	{
456 		value = ((percent_t *)st->action_par)[0];
457 		value = MAX(0.0f, MIN(1.0f, value));
458 	}
459 
460 	mo->visibility = mo->vis_target = value;
461 }
462 
P_ActTransFade(mobj_t * mo)463 void P_ActTransFade(mobj_t * mo)
464 {
465 	float value = INVISIBLE;
466 
467 	const state_t *st = mo->state;
468 
469 	if (st && st->action_par)
470 	{
471 		value = ((percent_t *)st->action_par)[0];
472 		value = MAX(0.0f, MIN(1.0f, value));
473 	}
474 
475 	mo->vis_target = value;
476 }
477 
P_ActTransLess(mobj_t * mo)478 void P_ActTransLess(mobj_t * mo)
479 {
480 	float value = 0.05f;
481 
482 	const state_t *st = mo->state;
483 
484 	if (st && st->action_par)
485 	{
486 		value = ((percent_t *)st->action_par)[0];
487 		value = MAX(0.0f, MIN(1.0f, value));
488 	}
489 
490 	mo->vis_target -= value;
491 
492 	if (mo->vis_target < INVISIBLE)
493 		mo->vis_target = INVISIBLE;
494 }
495 
P_ActTransMore(mobj_t * mo)496 void P_ActTransMore(mobj_t * mo)
497 {
498 	float value = 0.05f;
499 
500 	const state_t *st = mo->state;
501 
502 	if (st && st->action_par)
503 	{
504 		value = ((percent_t *)st->action_par)[0];
505 		value = MAX(0.0f, MIN(1.0f, value));
506 	}
507 
508 	mo->vis_target += value;
509 
510 	if (mo->vis_target > VISIBLE)
511 		mo->vis_target = VISIBLE;
512 }
513 
514 //
515 // P_ActTransAlternate
516 //
517 // Alters the translucency of an item, EF_LESSVIS is used
518 // internally to tell the object if it should be getting
519 // more visible or less visible; EF_LESSVIS is set when an
520 // object is to get less visible (because it has become
521 // to a level of lowest translucency) and the flag is unset
522 // if the object has become as highly translucent as possible.
523 //
P_ActTransAlternate(mobj_t * object)524 void P_ActTransAlternate(mobj_t * object)
525 {
526 	const state_t *st;
527 	float value = 0.05f;
528 
529 	st = object->state;
530 
531 	if (st && st->action_par)
532 	{
533 		value = ((percent_t *)st->action_par)[0];
534 		value = MAX(0.0f, MIN(1.0f, value));
535 	}
536 
537 	if (object->extendedflags & EF_LESSVIS)
538 	{
539 		object->vis_target -= value;
540 		if (object->vis_target <= INVISIBLE)
541 		{
542 			object->vis_target = INVISIBLE;
543 			object->extendedflags &= ~EF_LESSVIS;
544 		}
545 	}
546 	else
547 	{
548 		object->vis_target += value;
549 		if (object->vis_target >= VISIBLE)
550 		{
551 			object->vis_target = VISIBLE;
552 			object->extendedflags |= EF_LESSVIS;
553 		}
554 	}
555 }
556 
557 
P_ActDLightSet(mobj_t * mo)558 void P_ActDLightSet(mobj_t * mo)
559 {
560 	const state_t *st = mo->state;
561 
562 	if (st && st->action_par)
563 	{
564 		mo->dlight.r = MAX(0.0f, ((int *)st->action_par)[0]);
565 
566 		if (mo->info->hyperflags & HF_QUADRATIC_COMPAT)
567 			mo->dlight.r = DLIT_COMPAT_RAD(mo->dlight.r);
568 
569 		mo->dlight.target = mo->dlight.r;
570 	}
571 }
572 
573 
P_ActDLightFade(mobj_t * mo)574 void P_ActDLightFade(mobj_t * mo)
575 {
576 	const state_t *st = mo->state;
577 
578 	if (st && st->action_par)
579 	{
580 		mo->dlight.target = MAX(0.0f, ((int *)st->action_par)[0]);
581 
582 		if (mo->info->hyperflags & HF_QUADRATIC_COMPAT)
583 			mo->dlight.target = DLIT_COMPAT_RAD(mo->dlight.target);
584 	}
585 }
586 
587 
P_ActDLightRandom(mobj_t * mo)588 void P_ActDLightRandom(mobj_t * mo)
589 {
590 	const state_t *st = mo->state;
591 
592 	if (st && st->action_par)
593 	{
594 		int low  = ((int *)st->action_par)[0];
595 		int high = ((int *)st->action_par)[1];
596 
597 		// Note: using M_Random so that gameplay is unaffected
598 		float qty = low + (high - low) * M_Random() / 255.0f;
599 
600 		if (mo->info->hyperflags & HF_QUADRATIC_COMPAT)
601 			qty = DLIT_COMPAT_RAD(qty);
602 
603 		mo->dlight.r = MAX(0.0f, qty);
604 		mo->dlight.target = mo->dlight.r;
605 	}
606 }
607 
P_ActDLightColour(struct mobj_s * mo)608 void P_ActDLightColour(struct mobj_s *mo)
609 {
610 	const state_t *st = mo->state;
611 
612 	if (st && st->action_par)
613 	{
614 		mo->dlight.color = ((rgbcol_t*) st->action_par)[0];
615 	}
616 }
617 
P_ActSetSkin(mobj_t * mo)618 void P_ActSetSkin(mobj_t * mo)
619 {
620 	const state_t *st = mo->state;
621 
622 	if (st && st->action_par)
623 	{
624 		int skin = ((int *)st->action_par)[0];
625 
626 		if (skin < 0 || skin > 9)
627 			I_Error("Thing [%s]: Bad skin number %d in SET_SKIN action.\n",
628 					mo->info->name.c_str(), skin);
629 
630 		mo->model_skin = skin;
631 	}
632 }
633 
634 
635 
636 //-------------------------------------------------------------------
637 //------------------- MOVEMENT ROUTINES -----------------------------
638 //-------------------------------------------------------------------
639 
P_ActFaceDir(mobj_t * mo)640 void P_ActFaceDir(mobj_t * mo)
641 {
642 	const state_t *st = mo->state;
643 
644 	if (st && st->action_par)
645 		mo->angle = *(angle_t *)st->action_par;
646 	else
647 		mo->angle = 0;
648 }
649 
P_ActTurnDir(mobj_t * mo)650 void P_ActTurnDir(mobj_t * mo)
651 {
652 	const state_t *st = mo->state;
653 
654 	angle_t turn = ANG180;
655 
656 	if (st && st->action_par)
657 		turn = *(angle_t *)st->action_par;
658 
659 	mo->angle += turn;
660 }
661 
P_ActTurnRandom(mobj_t * mo)662 void P_ActTurnRandom(mobj_t * mo)
663 {
664 	const state_t *st = mo->state;
665 	int turn = 359;
666 
667 	if (st && st->action_par)
668 	{
669         turn = (int)ANG_2_FLOAT(*(angle_t *)st->action_par);
670 	}
671 
672 	turn = turn * P_Random() / 90;  // 10 bits of angle
673 
674 	if (turn < 0)
675 		mo->angle -= (angle_t)((-turn) << (ANGLEBITS - 10));
676 	else
677 		mo->angle += (angle_t)(turn << (ANGLEBITS - 10));
678 }
679 
P_ActMlookFace(mobj_t * mo)680 void P_ActMlookFace(mobj_t * mo)
681 {
682 	const state_t *st = mo->state;
683 
684 	if (st && st->action_par)
685 		mo->vertangle = M_ATan(*(float *)st->action_par);
686 	else
687 		mo->vertangle = 0;
688 }
689 
P_ActMlookTurn(mobj_t * mo)690 void P_ActMlookTurn(mobj_t * mo)
691 {
692 	const state_t *st = mo->state;
693 
694 	if (st && st->action_par)
695 		mo->vertangle = M_ATan(*(float *)st->action_par);
696 }
697 
P_ActMoveFwd(mobj_t * mo)698 void P_ActMoveFwd(mobj_t * mo)
699 {
700 	const state_t *st = mo->state;
701 
702 	if (st && st->action_par)
703 	{
704 		float amount = *(float *)st->action_par;
705 
706 		float dx = M_Cos(mo->angle);
707 		float dy = M_Sin(mo->angle);
708 
709 		mo->mom.x += dx * amount;
710 		mo->mom.y += dy * amount;
711 	}
712 }
713 
P_ActMoveRight(mobj_t * mo)714 void P_ActMoveRight(mobj_t * mo)
715 {
716 	const state_t *st = mo->state;
717 
718 	if (st && st->action_par)
719 	{
720 		float amount = *(float *)st->action_par;
721 
722 		float dx = M_Cos(mo->angle - ANG90);
723 		float dy = M_Sin(mo->angle - ANG90);
724 
725 		mo->mom.x += dx * amount;
726 		mo->mom.y += dy * amount;
727 	}
728 }
729 
P_ActMoveUp(mobj_t * mo)730 void P_ActMoveUp(mobj_t * mo)
731 {
732 	const state_t *st = mo->state;
733 
734 	if (st && st->action_par)
735 		mo->mom.z += *(float *)st->action_par;
736 }
737 
P_ActStopMoving(mobj_t * mo)738 void P_ActStopMoving(mobj_t * mo)
739 {
740 	mo->mom.x = mo->mom.y = mo->mom.z = 0;
741 }
742 
743 
744 //-------------------------------------------------------------------
745 //-------------------SOUND CAUSING ROUTINES--------------------------
746 //-------------------------------------------------------------------
747 
P_ActPlaySound(mobj_t * mo)748 void P_ActPlaySound(mobj_t * mo)
749 {
750 	// Generate an arbitrary sound.
751 
752 	sfx_t *sound = NULL;
753 
754 	if (mo->state && mo->state->action_par)
755 		sound = (sfx_t *) mo->state->action_par;
756 
757 	if (! sound)
758 	{
759 		M_WarnError("P_ActPlaySound: missing sound name in %s.\n",
760 					mo->info->name.c_str());
761 		return;
762 	}
763 
764 	S_StartFX(sound, P_MobjGetSfxCategory(mo), mo);
765 }
766 
767 
P_ActKillSound(mobj_t * mo)768 void P_ActKillSound(mobj_t * mo)
769 {
770 	// Kill any current sounds from this thing.
771 
772 	S_StopFX(mo);
773 }
774 
775 
P_ActMakeAmbientSound(mobj_t * mo)776 void P_ActMakeAmbientSound(mobj_t * mo)
777 {
778 	// Just a sound generating procedure that cause the sound ref
779 	// in seesound to be generated.
780 
781 	if (mo->info->seesound)
782 		S_StartFX(mo->info->seesound, P_MobjGetSfxCategory(mo), mo);
783 	else
784 		I_Debugf("%s has no ambient sound\n", mo->info->name.c_str());
785 }
786 
787 
P_ActMakeAmbientSoundRandom(mobj_t * mo)788 void P_ActMakeAmbientSoundRandom(mobj_t * mo)
789 {
790 	// Give a small "random" chance that this object will make its
791 	// ambient sound. Currently this is a set value of 50, however
792 	// the code that drives this, should allow for the user to set
793 	// the value, note for further DDF Development.
794 
795 	if (mo->info->seesound)
796 	{
797 		if (M_Random() < 50)
798 			S_StartFX(mo->info->seesound, P_MobjGetSfxCategory(mo), mo);
799 	}
800 	else
801 		I_Debugf("%s has no ambient sound\n", mo->info->name.c_str());
802 }
803 
804 
P_ActMakeActiveSound(mobj_t * mo)805 void P_ActMakeActiveSound(mobj_t * mo)
806 {
807 	// Just a sound generating procedure that cause the sound ref
808 	// in activesound to be generated.
809 	//
810 	// -KM- 1999/01/31
811 
812 	if (mo->info->activesound)
813 		S_StartFX(mo->info->activesound, P_MobjGetSfxCategory(mo), mo);
814 	else
815 		I_Debugf("%s has no ambient sound\n", mo->info->name.c_str());
816 }
817 
818 
P_ActMakeDyingSound(mobj_t * mo)819 void P_ActMakeDyingSound(mobj_t * mo)
820 {
821 	// This procedure is like everyother sound generating
822 	// procedure with the exception that if the object is
823 	// a boss (EF_ALWAYSLOUD extended flag) then the sound is
824 	// generated at full volume (source = NULL).
825 
826 	sfx_t *sound = mo->info->deathsound;
827 
828 	if (sound)
829 		S_StartFX(sound, P_MobjGetSfxCategory(mo), mo, SfxFlags(mo->info));
830 	else
831 		I_Debugf("%s has no death sound\n", mo->info->name.c_str());
832 }
833 
834 
P_ActMakePainSound(mobj_t * mo)835 void P_ActMakePainSound(mobj_t * mo)
836 {
837 	// Ow!! it hurts!
838 
839 	if (mo->info->painsound)
840 		S_StartFX(mo->info->painsound, P_MobjGetSfxCategory(mo),
841 				       mo, SfxFlags(mo->info));
842 	else
843 		I_Debugf("%s has no pain sound\n", mo->info->name.c_str());
844 }
845 
846 
P_ActMakeOverKillSound(mobj_t * mo)847 void P_ActMakeOverKillSound(mobj_t * mo)
848 {
849 	if (mo->info->overkill_sound)
850 		S_StartFX(mo->info->overkill_sound, P_MobjGetSfxCategory(mo),
851 					   mo, SfxFlags(mo->info));
852 	else
853 		I_Debugf("%s has no overkill sound\n", mo->info->name.c_str());
854 }
855 
856 
P_ActMakeCloseAttemptSound(mobj_t * mo)857 void P_ActMakeCloseAttemptSound(mobj_t * mo)
858 {
859 	// Attempting close combat sound
860 
861 	if (! mo->info->closecombat)
862 		I_Error("Object [%s] used CLOSEATTEMPTSND action, "
863 				"but has no CLOSE_ATTACK\n", mo->info->name.c_str());
864 
865 	sfx_t *sound = mo->info->closecombat->initsound;
866 
867 	if (sound)
868 		S_StartFX(sound, P_MobjGetSfxCategory(mo), mo);
869 	else
870 		I_Debugf("%s has no close combat attempt sound\n", mo->info->name.c_str());
871 }
872 
873 
P_ActMakeRangeAttemptSound(mobj_t * mo)874 void P_ActMakeRangeAttemptSound(mobj_t * mo)
875 {
876 	// Attempting range attack sound
877 
878 	if (! mo->info->rangeattack)
879 		I_Error("Object [%s] used RANGEATTEMPTSND action, "
880 				"but has no RANGE_ATTACK\n", mo->info->name.c_str());
881 
882 	sfx_t *sound = mo->info->rangeattack->initsound;
883 
884 	if (sound)
885 		S_StartFX(sound, P_MobjGetSfxCategory(mo), mo);
886 	else
887 		I_Debugf("%s has no range attack attempt sound\n", mo->info->name.c_str());
888 }
889 
890 
891 //-------------------------------------------------------------------
892 //-------------------EXPLOSION DAMAGE ROUTINES-----------------------
893 //-------------------------------------------------------------------
894 
895 //
896 // P_ActDamageExplosion
897 //
898 // Radius Attack damage set by info->damage. Used for the original Barrels
899 //
P_ActDamageExplosion(mobj_t * object)900 void P_ActDamageExplosion(mobj_t * object)
901 {
902 	float damage;
903 
904 	DAMAGE_COMPUTE(damage, &object->info->explode_damage);
905 
906 #ifdef DEVELOPERS
907 	if (!damage)
908 	{
909 		L_WriteDebug("%s caused no explosion damage\n", object->info->name.c_str());
910 		return;
911 	}
912 #endif
913 
914 	// -AJA- 2004/09/27: new EXPLODE_RADIUS command (overrides normal calc)
915 	float radius = object->info->explode_radius;
916 	if (radius == 0) radius = damage;
917 
918 	P_RadiusAttack(object, object->source, radius, damage,
919 				   &object->info->explode_damage, false);
920 }
921 
922 //
923 // P_ActThrust
924 //
925 // Thrust set by info->damage.
926 //
P_ActThrust(mobj_t * object)927 void P_ActThrust(mobj_t * object)
928 {
929 	float damage;
930 
931 	DAMAGE_COMPUTE(damage, &object->info->explode_damage);
932 
933 #ifdef DEVELOPERS
934 	if (!damage)
935 	{
936 		L_WriteDebug("%s caused no thrust\n", object->info->name.c_str());
937 		return;
938 	}
939 #endif
940 
941 	float radius = object->info->explode_radius;
942 	if (radius == 0) radius = damage;
943 
944 	P_RadiusAttack(object, object->source, radius, damage,
945 				   &object->info->explode_damage, true);
946 }
947 
948 //-------------------------------------------------------------------
949 //-------------------MISSILE HANDLING ROUTINES-----------------------
950 //-------------------------------------------------------------------
951 
952 //
953 // P_ActExplode
954 //
955 // The object blows up, like a missile.
956 //
957 // -AJA- 1999/08/21: Replaced P_ActExplodeMissile (which was identical
958 //       to p_mobj's P_ExplodeMissile) with this.
959 //
P_ActExplode(mobj_t * object)960 void P_ActExplode(mobj_t * object)
961 {
962 	P_MobjExplodeMissile(object);
963 }
964 
965 //
966 // P_ActCheckMissileSpawn
967 //
968 // This procedure handles a newly spawned missile, it moved
969 // by half the amount of momentum and then checked to see
970 // if the move is possible, if not the projectile is
971 // exploded. Also the number of initial tics on its
972 // current state is taken away from by a random number
973 // between 0 and 3, although the number of tics will never
974 // go below 1.
975 //
976 // -ACB- 1998/08/04
977 //
978 // -AJA- 1999/08/22: Fixed a bug that occasionally caused the game to
979 //       go into an infinite loop.  NOTE WELL: don't fiddle with the
980 //       object's x & y directly, use P_TryMove instead, or
981 //       P_ChangeThingPosition.
982 //
CheckMissileSpawn(mobj_t * projectile)983 static void CheckMissileSpawn(mobj_t * projectile)
984 {
985 	projectile->tics -= P_Random() & 3;
986 
987 	if (projectile->tics < 1)
988 		projectile->tics = 1;
989 
990 	projectile->z += projectile->mom.z / 2;
991 
992 	if (!P_TryMove(projectile,
993 				   projectile->x + projectile->mom.x / 2,
994 				   projectile->y + projectile->mom.y / 2))
995 	{
996 		P_MobjExplodeMissile(projectile);
997 	}
998 }
999 
1000 //
1001 // LaunchProjectile
1002 //
1003 // This procedure launches a project the direction of the target mobj.
1004 // * source - the source of the projectile
1005 // * target - the target of the projectile
1006 // * type   - the mobj type of the projectile
1007 //
1008 // For all sense and purposes it is possible for the target to be a dummy
1009 // mobj, just to act as a carrier for a set of target co-ordinates.
1010 //
1011 // Missiles can be spawned at different locations on and around
1012 // the mobj. Traditionally an mobj would fire a projectile
1013 // at a height of 32 from the centerpoint of that
1014 // mobj, this was true for all creatures from the Cyberdemon to
1015 // the Imp. The currentattack holds the height and x & y
1016 // offsets that dictates the spawning location of a projectile.
1017 //
1018 // Traditionally: Height   = 4*8
1019 //                x-offset = 0
1020 //                y-offset = 0
1021 //
1022 // The exception to this rule is the revenant, which did increase
1023 // its z value by 16 before firing: This was a hack
1024 // to launch a missile at a height of 48. The revenants
1025 // height was reduced to normal after firing, this new code
1026 // makes that an unnecesary procedure.
1027 //
1028 // projx, projy & projz are the projectiles spawn location
1029 //
1030 // NOTE: may return NULL.
1031 //
DoLaunchProjectile(mobj_t * source,float tx,float ty,float tz,mobj_t * target,const mobjtype_c * type)1032 static mobj_t *DoLaunchProjectile(mobj_t * source, float tx, float ty, float tz,
1033 								mobj_t * target, const mobjtype_c * type)
1034 {
1035 	const atkdef_c *attack = source->currentattack;
1036 
1037 	if (! attack)
1038 		return NULL;
1039 
1040 	// -AJA- projz now handles crouching
1041 	float projx = source->x;
1042 	float projy = source->y;
1043 	float projz = source->z + attack->height * source->height / source->info->height;
1044 
1045 	angle_t angle = source->angle;
1046 
1047 	projx += attack->xoffset * M_Cos(angle + ANG90);
1048 	projy += attack->xoffset * M_Sin(angle + ANG90);
1049 
1050 	float yoffset;
1051 
1052 	if (attack->yoffset)
1053 		yoffset = attack->yoffset;
1054 	else
1055 		yoffset = source->radius - 0.5f;
1056 
1057 	projx += yoffset * M_Cos(angle) * M_Cos(source->vertangle);
1058 	projy += yoffset * M_Sin(angle) * M_Cos(source->vertangle);
1059 	projz += yoffset * M_Sin(source->vertangle);
1060 
1061 	mobj_t *projectile = P_MobjCreateObject(projx, projy, projz, type);
1062 
1063 	// currentattack is held so that when a collision takes place
1064 	// with another object, we know whether or not the object hit
1065 	// can shake off the attack or is damaged by it.
1066 	//
1067 	projectile->currentattack = attack;
1068 	projectile->SetRealSource(source);
1069 
1070 	// check for blocking lines between source and projectile
1071 	if (P_MapCheckBlockingLine(source, projectile))
1072 	{
1073 		P_MobjExplodeMissile(projectile);
1074 		return NULL;
1075 	}
1076 
1077 	// launch sound
1078 	if (projectile->info && projectile->info->seesound)
1079 	{
1080 		int category = AttackSfxCat(source);
1081 		int flags = SfxFlags(projectile->info);
1082 
1083 		mobj_t *sfx_source = projectile;
1084 		if (category == SNCAT_Player || category == SNCAT_Weapon)
1085 			sfx_source = source;
1086 
1087 		S_StartFX(projectile->info->seesound, category, sfx_source, flags);
1088 	}
1089 
1090 	angle = R_PointToAngle(projx, projy, tx, ty);
1091 
1092 	// Now add the fact that the target may be difficult to spot and
1093 	// make the projectile's target the same as the sources. Only
1094 	// do these if the object is not a dummy object, otherwise just
1095 	// flag the missile not to trace: you cannot track a target that
1096 	// does not exist...
1097 
1098 	projectile->SetTarget(target);
1099 
1100 	if (! target)
1101 	{
1102 		tz += attack->height;
1103 	}
1104 	else
1105 	{
1106 		projectile->extendedflags |= EF_FIRSTCHECK;
1107 
1108 		if (! (attack->flags & AF_Player))
1109 		{
1110 			if (target->flags & MF_FUZZY)
1111 				angle += P_RandomNegPos() << (ANGLEBITS - 12);
1112 
1113 			if (target->visibility < VISIBLE)
1114 				angle += (angle_t)(P_RandomNegPos() * 64 * (VISIBLE - target->visibility));
1115 		}
1116 	}
1117 
1118 	// Calculate slope
1119 	float slope = P_ApproxSlope(tx - projx, ty - projy, tz - projz);
1120 
1121 	// -AJA- 1999/09/11: add in attack's angle & slope offsets.
1122 	angle -= attack->angle_offset;
1123 	slope += attack->slope_offset;
1124 
1125 	// is the attack not accurate?
1126 	if (!source->player || source->player->refire > 0)
1127 	{
1128 		if (attack->accuracy_angle > 0.0f)
1129 			angle += (attack->accuracy_angle >> 8) * P_RandomNegPos();
1130 
1131 		if (attack->accuracy_slope > 0.0f)
1132 			slope += attack->accuracy_slope * (P_RandomNegPos() / 255.0f);
1133 	}
1134 
1135 	P_SetMobjDirAndSpeed(projectile, angle, slope, projectile->speed);
1136 	CheckMissileSpawn(projectile);
1137 
1138 	return projectile;
1139 }
1140 
LaunchProjectile(mobj_t * source,mobj_t * target,const mobjtype_c * type)1141 static mobj_t *LaunchProjectile(mobj_t * source, mobj_t * target,
1142 								const mobjtype_c * type)
1143 {
1144 	float tx, ty, tz;
1145 
1146 	if (source->currentattack && (source->currentattack->flags & AF_NoTarget))
1147 		target = NULL;
1148 
1149 	P_TargetTheory(source, target, &tx, &ty, &tz);
1150 
1151 	return DoLaunchProjectile(source, tx, ty, tz, target, type);
1152 }
1153 
1154 //
1155 // LaunchSmartProjectile
1156 //
1157 // This procedure has the same effect as
1158 // LaunchProjectile, but it calculates a point where the target
1159 // and missile will intersect.  This comes from the fact that to shoot
1160 // something, you have to aim slightly ahead of it.  It will also put
1161 // an end to circle-strafing.  :-)
1162 //
1163 // -KM- 1998/10/29
1164 // -KM- 1998/12/16 Fixed it up.  Works quite well :-)
1165 //
LaunchSmartProjectile(mobj_t * source,mobj_t * target,const mobjtype_c * type)1166 static void LaunchSmartProjectile(mobj_t * source, mobj_t * target,
1167 								  const mobjtype_c * type)
1168 {
1169 	float t = -1;
1170 	float mx = 0, my = 0;
1171 
1172 	if (target)
1173 	{
1174 		mx = target->mom.x;
1175 		my = target->mom.y;
1176 
1177 		float dx = source->x - target->x;
1178 		float dy = source->y - target->y;
1179 
1180 		float s = type->speed;
1181 		if (level_flags.fastparm)
1182 			s *= type->fast;
1183 
1184 		float a = mx * mx + my * my - s * s;
1185 		float b = 2 * (dx * mx + dy * my);
1186 		float c = dx * dx + dy * dy;
1187 
1188 		float t1 = -1, t2 = -1;
1189 
1190 		// find solution to the quadratic equation
1191 		if (a && ((b * b - 4 * a * c) >= 0))
1192 		{
1193 			t1 = -b + (float)sqrt(b * b - 4.0f * a * c);
1194 			t1 /= 2.0f * a;
1195 
1196 			t2 = -b - (float)sqrt(b * b - 4.0f * a * c);
1197 			t2 /= 2.0f * a;
1198 		}
1199 
1200 		if (t1 < 0)
1201 			t = t2;
1202 		else if (t2 < 0)
1203 			t = t1;
1204 		else
1205 			t = (t1 < t2) ? t1 : t2;
1206 	}
1207 
1208 	if (t <= 0)
1209 	{
1210 		// -AJA- when no target, fall back to "dumb mode"
1211 		LaunchProjectile(source, target, type);
1212 	}
1213 	else
1214 	{
1215 		// -AJA- 2005/02/07: assumes target doesn't move up or down
1216 
1217 		float tx = target->x + mx * t;
1218 		float ty = target->y + my * t;
1219 		float tz = MO_MIDZ(target);
1220 
1221 		DoLaunchProjectile(source, tx, ty, tz, target, type);
1222 
1223 #if 0  // -AJA- this doesn't seem correct / consistent
1224 		if (projectile)
1225 			source->angle = projectile->angle;
1226 #endif
1227 	}
1228 }
1229 
Weakness_CheckHit(mobj_t * target,const atkdef_c * attack,float x,float y,float z)1230 static inline bool Weakness_CheckHit(mobj_t *target,
1231 		const atkdef_c *attack, float x, float y, float z)
1232 {
1233 	const weakness_info_c *weak = &target->info->weak;
1234 
1235 	if (weak->classes == BITSET_EMPTY)
1236 		return false;
1237 
1238 I_Debugf("Weakness_CheckHit: target=[%s] classes=0x%08x\n", target->info->name.c_str(), weak->classes);
1239 
1240 	if (BITSET_EMPTY != (attack->attack_class & ~weak->classes))
1241 		return false;
1242 
1243 	if (target->height < 1)
1244 		return false;
1245 
1246 	// compute vertical position.  Clamping it means that a missile
1247 	// which hits the target on the head (coming sharply down) will
1248 	// still register as a head-shot.
1249 	z = (z - target->z) / target->height;
1250 	z = CLAMP(0.01f, z, 0.99f);
1251 
1252 I_Debugf("HEIGHT CHECK: %1.2f < %1.2f < %1.2f\n",
1253 		  weak->height[0], z, weak->height[1]);
1254 
1255 	if (z < weak->height[0] || z > weak->height[1])
1256 		return false;
1257 
1258 	angle_t ang = R_PointToAngle(target->x, target->y, x, y);
1259 
1260 	ang -= target->angle;
1261 
1262 I_Debugf("ANGLE CHECK: %1.2f < %1.2f < %1.2f\n",
1263 		 ANG_2_FLOAT(weak->angle[0]), ANG_2_FLOAT(ang),
1264 		 ANG_2_FLOAT(weak->angle[1]));
1265 
1266 	if (weak->angle[0] <= weak->angle[1])
1267 	{
1268 		if (ang < weak->angle[0] || ang > weak->angle[1])
1269 			return false;
1270 	}
1271 	else
1272 	{
1273 		if (ang < weak->angle[0] && ang > weak->angle[1])
1274 			return false;
1275 	}
1276 
1277 	return true;
1278 }
1279 
1280 
1281 //
1282 // P_MissileContact
1283 //
1284 // Called by PIT_CheckRelThing when a missile comes into
1285 // contact with another object. Placed here with
1286 // the other missile code for cleaner code.
1287 //
1288 // Returns: -1 if missile should pass through.
1289 //           0 if hit but no damage was done.
1290 //          +1 if hit and damage was done.
1291 //
P_MissileContact(mobj_t * object,mobj_t * target)1292 int P_MissileContact(mobj_t * object, mobj_t * target)
1293 {
1294 	mobj_t *source = object->source;
1295 
1296 	if (source)
1297 	{
1298 		// check for ghosts (attack passes through)
1299 		if (object->currentattack && BITSET_EMPTY ==
1300 			(object->currentattack->attack_class & ~target->info->ghost))
1301 			return -1;
1302 
1303 		if ((target->side & source->side) != 0)
1304 		{
1305 			if (target->hyperflags & HF_SIDEGHOST)
1306 				return -1;
1307 
1308 			if (target->hyperflags & HF_SIDEIMMUNE)
1309 				return 0;
1310 		}
1311 
1312 		if (source->info == target->info)
1313 		{
1314 			if (!(target->extendedflags & EF_DISLOYALTYPE))
1315 				return 0;
1316 		}
1317 
1318 		if (object->currentattack != NULL &&
1319 			! (target->extendedflags & EF_OWNATTACKHURTS))
1320 		{
1321 			if (object->currentattack == target->info->rangeattack)
1322 				return 0;
1323 			if (object->currentattack == target->info->closecombat)
1324 				return 0;
1325 		}
1326 	}
1327 
1328 	const damage_c *damtype;
1329 
1330 	// transitional hack
1331 	if (object->currentattack)
1332 		damtype = &object->currentattack->damage;
1333 	else
1334 		damtype = &object->info->explode_damage;
1335 
1336 	float damage;
1337 	DAMAGE_COMPUTE(damage, damtype);
1338 
1339 	bool weak_spot = false;
1340 
1341 	// check for Weakness against the attack
1342 	if (Weakness_CheckHit(target, object->currentattack,
1343 				object->x, object->y, MO_MIDZ(object)))
1344 	{
1345 		damage *= target->info->weak.multiply;
1346 		weak_spot = true;
1347 	}
1348 
1349 	// check for immunity against the attack
1350 	if (object->hyperflags & HF_INVULNERABLE)
1351 		return 0;
1352 
1353 	if (!weak_spot && object->currentattack && BITSET_EMPTY ==
1354 		(object->currentattack->attack_class & ~target->info->immunity))
1355 	{
1356 		return 0;
1357 	}
1358 
1359 	// support for "tunnelling" missiles, which should only do damage at
1360 	// the first impact.
1361 	if (object->extendedflags & EF_TUNNEL)
1362 	{
1363 		// this hash is very basic, but should work OK
1364 		u32_t hash = (u32_t)(long)target;
1365 
1366 		if (object->tunnel_hash[0] == hash || object->tunnel_hash[1] == hash)
1367 			return -1;
1368 
1369 		object->tunnel_hash[0] = object->tunnel_hash[1];
1370 		object->tunnel_hash[1] = hash;
1371 	}
1372 
1373 	// Berserk handling
1374 	if (object->player && object->currentattack &&
1375 		object->player->powers[PW_Berserk] != 0.0f)
1376 	{
1377 		damage *= object->currentattack->berserk_mul;
1378 	}
1379 
1380 	if (!damage)
1381 	{
1382 #ifdef DEVELOPERS
1383 		L_WriteDebug("%s missile did zero damage.\n",
1384 					 object->info->name.c_str());
1385 #endif
1386 		return 0;
1387 	}
1388 
1389 	P_DamageMobj(target, object, object->source, damage, damtype, weak_spot);
1390 	return 1;
1391 }
1392 
1393 //
1394 // P_BulletContact
1395 //
1396 // Called by PTR_ShootTraverse when a bullet comes into contact with
1397 // another object.  Needed so that the "DISLOYAL" special will behave
1398 // in the same manner for bullets as for missiles.
1399 //
1400 // Note: also used for Close-Combat attacks.
1401 //
1402 // Returns: -1 if bullet should pass through.
1403 //           0 if hit but no damage was done.
1404 //          +1 if hit and damage was done.
1405 //
P_BulletContact(mobj_t * source,mobj_t * target,float damage,const damage_c * damtype,float x,float y,float z)1406 int P_BulletContact(mobj_t * source, mobj_t * target,
1407 					float damage, const damage_c *damtype,
1408 					float x, float y, float z)
1409 {
1410 	// check for ghosts (attack passes through)
1411 	if (source->currentattack && BITSET_EMPTY ==
1412 		(source->currentattack->attack_class & ~target->info->ghost))
1413 		return -1;
1414 
1415 	if ((target->side & source->side) != 0)
1416 	{
1417 		if (target->hyperflags & HF_SIDEGHOST)
1418 			return -1;
1419 
1420 		if (target->hyperflags & HF_SIDEIMMUNE)
1421 			return 0;
1422 	}
1423 
1424 	if (source->info == target->info)
1425 	{
1426 		if (! (target->extendedflags & EF_DISLOYALTYPE))
1427 			return 0;
1428 	}
1429 
1430 	if (source->currentattack != NULL &&
1431 		!(target->extendedflags & EF_OWNATTACKHURTS))
1432 	{
1433 		if (source->currentattack == target->info->rangeattack)
1434 			return 0;
1435 		if (source->currentattack == target->info->closecombat)
1436 			return 0;
1437 	}
1438 
1439 	// ignore damage in GOD mode, or with INVUL powerup
1440 	if (target->player)
1441 	{
1442 		if ((target->player->cheats & CF_GODMODE) ||
1443 			target->player->powers[PW_Invulnerable] > 0)
1444 		{
1445 			// emulate the thrust that P_DamageMobj() would have done
1446 			if (source && damage > 0 && !(target->flags & MF_NOCLIP))
1447 				P_ThrustMobj(target, source, damage);
1448 
1449 			return 0;
1450 		}
1451 	}
1452 
1453 	bool weak_spot = false;
1454 
1455 	// check for Weakness against the attack
1456 	if (Weakness_CheckHit(target, source->currentattack, x, y, z))
1457 	{
1458 		damage *= target->info->weak.multiply;
1459 		weak_spot = true;
1460 	}
1461 
1462 	// check for immunity against the attack
1463 	if (target->hyperflags & HF_INVULNERABLE)
1464 		return 0;
1465 
1466 	if (!weak_spot && source->currentattack && BITSET_EMPTY ==
1467 		(source->currentattack->attack_class & ~target->info->immunity))
1468 	{
1469 		return 0;
1470 	}
1471 
1472 	if (!damage)
1473 	{
1474 #ifdef DEVELOPERS
1475 		L_WriteDebug("%s's shoot/combat attack did zero damage.\n",
1476 					 source->info->name.c_str());
1477 #endif
1478 		return 0;
1479 	}
1480 
1481 	P_DamageMobj(target, source, source, damage, damtype, weak_spot);
1482 	return 1;
1483 }
1484 
1485 //
1486 // P_ActCreateSmokeTrail
1487 //
1488 // Just spawns smoke behind an mobj: the smoke is
1489 // risen by giving it z momentum, in order to
1490 // prevent the smoke appearing uniform (which obviously
1491 // does not happen), the number of tics that the smoke
1492 // mobj has is "randomly" reduced, although the number
1493 // of tics never gets to zero or below.
1494 //
1495 // -ACB- 1998/08/10 Written
1496 // -ACB- 1999/10/01 Check thing's current attack has a smoke projectile
1497 //
P_ActCreateSmokeTrail(mobj_t * projectile)1498 void P_ActCreateSmokeTrail(mobj_t * projectile)
1499 {
1500 	const atkdef_c *attack = projectile->currentattack;
1501 
1502 	if (attack == NULL)
1503 		return;
1504 
1505 	if (attack->puff == NULL)
1506 	{
1507 		M_WarnError("P_ActCreateSmokeTrail: attack %s has no PUFF object\n",
1508 					attack->name.c_str());
1509 		return;
1510 	}
1511 
1512 	// spawn a puff of smoke behind the rocket
1513 	mobj_t *smoke = P_MobjCreateObject(
1514 		projectile->x - projectile->mom.x / 2.0f,
1515 		projectile->y - projectile->mom.y / 2.0f,
1516 		projectile->z, attack->puff);
1517 
1518 	smoke->mom.z = smoke->info->float_speed;
1519 	smoke->tics -= M_Random() & 3;
1520 
1521 	if (smoke->tics < 1)
1522 		smoke->tics = 1;
1523 }
1524 
1525 //
1526 // P_ActHomingProjectile
1527 //
1528 // This projectile will alter its course to intercept its
1529 // target, if is possible for this procedure to be called
1530 // and nothing results because of a chance that the
1531 // projectile will not chase its target.
1532 //
1533 // As this code is based on the revenant tracer, it did use
1534 // a bit check on the current gametic - which was why every so
1535 // often a revenant fires a missile straight and not one that
1536 // homes in on its target: If the gametic has bits 1+2 on
1537 // (which boils down to 1 in every 4 tics), the trick in this
1538 // is that - in conjuntion with the tic count for the
1539 // tracing object's states - the tracing will always fail or
1540 // pass the check: if it passes first time, it will always
1541 // pass and vice versa. The problem with this was two fold:
1542 // demos will go out of sync if the starting gametic if different
1543 // from when the demo was recorded (which admittly is easily
1544 // fixable), the second is that for someone designing a new
1545 // tracing projectile it would be more than a bit confusing to
1546 // joe "dooming" public.
1547 //
1548 // The new system that affects the original gameplay slightly is
1549 // to get a random chance of the projectile not homing in on its
1550 // target and working this out first time round, the test result
1551 // is recorded (by clearing the 'target' field).
1552 //
1553 // -ACB- 1998/08/10
1554 //
P_ActHomingProjectile(mobj_t * projectile)1555 void P_ActHomingProjectile(mobj_t * projectile)
1556 {
1557 	const atkdef_c *attack = projectile->currentattack;
1558 
1559 	if (attack == NULL)
1560 		return;
1561 
1562 	if (attack->flags & AF_TraceSmoke)
1563 		P_ActCreateSmokeTrail(projectile);
1564 
1565 	if (projectile->extendedflags & EF_FIRSTCHECK)
1566 	{
1567 		projectile->extendedflags &= ~EF_FIRSTCHECK;
1568 
1569 		if (P_RandomTest(attack->notracechance))
1570 		{
1571 			projectile->SetTarget(NULL);
1572 			return;
1573 		}
1574 	}
1575 
1576 	mobj_t *destination = projectile->target;
1577 
1578 	if (!destination || destination->health <= 0)
1579 		return;
1580 
1581 	// change angle
1582 	angle_t exact = R_PointToAngle(projectile->x, projectile->y,
1583 								   destination->x, destination->y);
1584 
1585 	if (exact != projectile->angle)
1586 	{
1587 		if (exact - projectile->angle > ANG180)
1588 		{
1589 			projectile->angle -= attack->trace_angle;
1590 
1591 			if (exact - projectile->angle < ANG180)
1592 				projectile->angle = exact;
1593 		}
1594 		else
1595 		{
1596 			projectile->angle += attack->trace_angle;
1597 
1598 			if (exact - projectile->angle > ANG180)
1599 				projectile->angle = exact;
1600 		}
1601 	}
1602 
1603 	projectile->mom.x = projectile->speed * M_Cos(projectile->angle);
1604 	projectile->mom.y = projectile->speed * M_Sin(projectile->angle);
1605 
1606 	// change slope
1607 	float slope = P_ApproxSlope(destination->x - projectile->x,
1608 						  destination->y - projectile->y,
1609 						  MO_MIDZ(destination) - projectile->z);
1610 
1611 	slope *= projectile->speed;
1612 
1613 	if (slope < projectile->mom.z)
1614 		projectile->mom.z -= 0.125f;
1615 	else
1616 		projectile->mom.z += 0.125f;
1617 }
1618 
1619 //
1620 // P_ActHomeToSpot
1621 //
1622 // This projectile will alter its course to intercept its target,
1623 // or explode if it has reached it.  Used by the bossbrain cube.
1624 //
P_ActHomeToSpot(mobj_t * projectile)1625 void P_ActHomeToSpot(mobj_t * projectile)
1626 {
1627 	mobj_t *target = projectile->target;
1628 
1629 	if (!target)
1630 	{
1631 		P_MobjExplodeMissile(projectile);
1632 		return;
1633 	}
1634 
1635 	float dx = target->x - projectile->x;
1636 	float dy = target->y - projectile->y;
1637 	float dz = target->z - projectile->z;
1638 
1639 	float ck_radius = target->radius + projectile->radius + 2;
1640 	float ck_height = target->height + projectile->height + 2;
1641 
1642 	// reached target ?
1643 	if (fabs(dx) <= ck_radius && fabs(dy) <= ck_radius && fabs(dz) <= ck_height)
1644 	{
1645 		P_MobjExplodeMissile(projectile);
1646 		return;
1647 	}
1648 
1649 	// calculate new angles
1650 	angle_t angle = R_PointToAngle(0, 0, dx, dy);
1651 	float slope = P_ApproxSlope(dx, dy, dz);
1652 
1653 	P_SetMobjDirAndSpeed(projectile, angle, slope, projectile->speed);
1654 }
1655 
1656 //
1657 // LaunchOrderedSpread
1658 //
1659 // Due to the unique way of handling that the mancubus fires, it is necessary
1660 // to write a single procedure to handle the firing. In real terms it amounts
1661 // to a glorified hack; The table holds the angle modifier and the choice of
1662 // whether the firing object or the projectile is affected. This procedure
1663 // should NOT be used for players as it will alter the player's mobj, bypassing
1664 // the normal player controls; The only reason for its existance is to maintain
1665 // the original mancubus behaviour. Although it is possible to make this generic,
1666 // the benefits of doing so are minimal. Purist function....
1667 //
1668 // -ACB- 1998/08/15
1669 //
LaunchOrderedSpread(mobj_t * mo)1670 static void LaunchOrderedSpread(mobj_t * mo)
1671 {
1672 	// left side = angle modifier
1673 	// right side = object or projectile (true for object).
1674 	static int spreadorder[] =
1675 	{
1676 		(ANG90 / 8), true,
1677 		(ANG90 / 8), false,
1678 		-(ANG90 / 8), true,
1679 		-(ANG90 / 4), false,
1680 		-(ANG90 / 16), false,
1681 		(ANG90 / 16), false
1682 	};
1683 
1684 	const atkdef_c *attack = mo->currentattack;
1685 
1686 	if (attack == NULL)
1687 		return;
1688 
1689 	int count = mo->spreadcount;
1690 
1691 	if (count < 0 || count > 12)
1692 		count = mo->spreadcount = 0;
1693 
1694 	// object or projectile?
1695 	// true --> the object, false --> the projectile.
1696 	if (spreadorder[count + 1])
1697 	{
1698 		mo->angle += spreadorder[count];
1699 
1700 		LaunchProjectile(mo, mo->target, attack->atk_mobj);
1701 	}
1702 	else
1703 	{
1704 		mobj_t *projectile = LaunchProjectile(mo, mo->target,
1705 											  attack->atk_mobj);
1706 		if (projectile == NULL)
1707 			return;
1708 
1709 		projectile->angle += spreadorder[count];
1710 
1711 		projectile->mom.x = projectile->speed * M_Cos(projectile->angle);
1712 		projectile->mom.y = projectile->speed * M_Sin(projectile->angle);
1713 	}
1714 
1715 	mo->spreadcount += 2;
1716 }
1717 
1718 //
1719 // LaunchRandomSpread
1720 //
1721 // This is a the generic function that should be used for a spreader like
1722 // mancubus, although its random nature would certainly be a change to the
1723 // ordered method used now. The random number is bit shifted to the right
1724 // and then the ANG90 is divided by it, the first bit of the RN is checked
1725 // to detemine if the angle is change is negative or not (approx 50% chance).
1726 // The result is the modifier for the projectile's angle.
1727 //
1728 // -ACB- 1998/08/15
1729 //
LaunchRandomSpread(mobj_t * mo)1730 static void LaunchRandomSpread(mobj_t * mo)
1731 {
1732 	if (mo->currentattack == NULL)
1733 		return;
1734 
1735 	mobj_t *projectile = LaunchProjectile(mo, mo->target,
1736 								  mo->currentattack->atk_mobj);
1737 	if (projectile == NULL)
1738 		return;
1739 
1740 	int i = P_Random() & 127;
1741 
1742 	if (i >> 1)
1743 	{
1744 		angle_t spreadangle = (ANG90 / (i >> 1));
1745 
1746 		if (i & 1)
1747 			spreadangle -= spreadangle << 1;
1748 
1749 		projectile->angle += spreadangle;
1750 	}
1751 
1752 	projectile->mom.x = projectile->speed * M_Cos(projectile->angle);
1753 	projectile->mom.y = projectile->speed * M_Sin(projectile->angle);
1754 }
1755 
1756 //-------------------------------------------------------------------
1757 //-------------------LINEATTACK ATTACK ROUTINES-----------------------
1758 //-------------------------------------------------------------------
1759 
1760 // -KM- 1998/11/25 Added uncertainty to the z component of the line.
ShotAttack(mobj_t * mo)1761 static void ShotAttack(mobj_t * mo)
1762 {
1763 	const atkdef_c *attack = mo->currentattack;
1764 
1765 	if (! attack)
1766 		return;
1767 
1768 	float range = (attack->range > 0) ? attack->range : MISSILERANGE;
1769 
1770 	// -ACB- 1998/09/05 Remember to use the object angle, fool!
1771 	angle_t objangle = mo->angle;
1772 	float objslope;
1773 
1774 	if ((mo->player && !mo->target) || (attack->flags & AF_NoTarget))
1775 		objslope = M_Tan(mo->vertangle);
1776 	else
1777 		P_AimLineAttack(mo, objangle, range, &objslope);
1778 
1779 	if (attack->sound)
1780 		S_StartFX(attack->sound, AttackSfxCat(mo), mo);
1781 
1782 	// -AJA- 1999/09/10: apply the attack's angle offsets.
1783 	objangle -= attack->angle_offset;
1784 	objslope += attack->slope_offset;
1785 
1786 	for (int i = 0; i < attack->count; i++)
1787 	{
1788 		angle_t angle = objangle;
1789 		float slope = objslope;
1790 
1791 		// is the attack not accurate?
1792 		if (!mo->player || mo->player->refire > 0)
1793 		{
1794 			if (attack->accuracy_angle > 0)
1795 				angle += (attack->accuracy_angle >> 8) * P_RandomNegPos();
1796 
1797 			if (attack->accuracy_slope > 0)
1798 				slope += attack->accuracy_slope * (P_RandomNegPos() / 255.0f);
1799 		}
1800 
1801 		float damage;
1802 		DAMAGE_COMPUTE(damage, &attack->damage);
1803 
1804 		if (mo->player && mo->player->powers[PW_Berserk] != 0.0f)
1805 			damage *= attack->berserk_mul;
1806 
1807 		P_LineAttack(mo, angle, range, slope, damage,
1808 					 &attack->damage, attack->puff);
1809 	}
1810 }
1811 
1812 // -KM- 1998/11/25 BFG Spray attack.  Must be used from missiles.
1813 //   Will do a BFG spray on every monster in sight.
SprayAttack(mobj_t * mo)1814 static void SprayAttack(mobj_t * mo)
1815 {
1816 	const atkdef_c *attack = mo->currentattack;
1817 
1818 	if (! attack)
1819 		return;
1820 
1821 	float range = (attack->range > 0) ? attack->range : MISSILERANGE;
1822 
1823 	// offset angles from its attack angle
1824 	for (int i = 0; i < 40; i++)
1825 	{
1826 		angle_t an = mo->angle - ANG90 / 2 + (ANG90 / 40) * i;
1827 
1828 		// mo->source is the originator (player) of the missile
1829 		mobj_t *target = P_AimLineAttack(mo->source ? mo->source : mo, an, range, NULL);
1830 
1831 		if (! target)
1832 			continue;
1833 
1834 		mobj_t *ball = P_MobjCreateObject(target->x, target->y,
1835 							   target->z + target->height / 4,
1836 							   attack->atk_mobj);
1837 
1838 		ball->SetTarget(mo->target);
1839 
1840 		// check for immunity against the attack
1841 		if (target->hyperflags & HF_INVULNERABLE)
1842 			continue;
1843 
1844 		if (BITSET_EMPTY == (attack->attack_class & ~target->info->immunity))
1845 			continue;
1846 
1847 		float damage;
1848 		DAMAGE_COMPUTE(damage, &attack->damage);
1849 
1850 		if (mo->player && mo->player->powers[PW_Berserk] != 0.0f)
1851 			damage *= attack->berserk_mul;
1852 
1853 		if (damage)
1854 			P_DamageMobj(target, NULL, mo->source, damage, &attack->damage);
1855 	}
1856 }
1857 
DoMeleeAttack(mobj_t * mo)1858 static void DoMeleeAttack(mobj_t * mo)
1859 {
1860 	const atkdef_c *attack = mo->currentattack;
1861 
1862 	float range = (attack->range > 0) ? attack->range : MISSILERANGE;
1863 
1864 	float damage;
1865 	DAMAGE_COMPUTE(damage, &attack->damage);
1866 
1867 	// -KM- 1998/11/25 Berserk ability
1868 	// -ACB- 2004/02/04 Only zero is off
1869 	if (mo->player && mo->player->powers[PW_Berserk] != 0.0f)
1870 		damage *= attack->berserk_mul;
1871 
1872 	// -KM- 1998/12/21 Use Line attack so bullet puffs are spawned.
1873 
1874 	if (! DecideMeleeAttack(mo, attack))
1875 	{
1876 		P_LineAttack(mo, mo->angle, range, M_Tan(mo->vertangle),
1877 					 damage, &attack->damage, attack->puff);
1878 		return;
1879 	}
1880 
1881 	if (attack->sound)
1882 		S_StartFX(attack->sound, AttackSfxCat(mo), mo);
1883 
1884 	float slope;
1885 
1886 	P_AimLineAttack(mo, mo->angle, range, &slope);
1887 
1888 	P_LineAttack(mo, mo->angle, range, slope, damage,
1889 				 &attack->damage, attack->puff);
1890 }
1891 
1892 //-------------------------------------------------------------------
1893 //--------------------TRACKER HANDLING ROUTINES----------------------
1894 //-------------------------------------------------------------------
1895 
1896 //
1897 // A Tracker is an object that follows its target, by being on top of
1898 // it. This is the attack style used by an Arch-Vile. The first routines
1899 // handle the tracker itself, the last two are called by the source of
1900 // the tracker.
1901 //
1902 
1903 //
1904 // P_ActTrackerFollow
1905 //
1906 // Called by the tracker to follow its target.
1907 //
1908 // -ACB- 1998/08/22
1909 //
P_ActTrackerFollow(mobj_t * tracker)1910 void P_ActTrackerFollow(mobj_t * tracker)
1911 {
1912 	mobj_t *destination = tracker->target;
1913 
1914 	if (!destination || !tracker->source)
1915 		return;
1916 
1917 	// Can the parent of the tracker see the target?
1918 	if (!P_CheckSight(tracker->source, destination))
1919 		return;
1920 
1921 	angle_t angle = destination->angle;
1922 
1923 	P_ChangeThingPosition(tracker,
1924 						  destination->x + 24 * M_Cos(angle),
1925 						  destination->y + 24 * M_Sin(angle),
1926 						  destination->z);
1927 }
1928 
1929 //
1930 // P_ActTrackerActive
1931 //
1932 // Called by the tracker to make its active sound: also tracks
1933 //
1934 // -ACB- 1998/08/22
1935 //
P_ActTrackerActive(mobj_t * tracker)1936 void P_ActTrackerActive(mobj_t * tracker)
1937 {
1938 	if (tracker->info->activesound)
1939 		S_StartFX(tracker->info->activesound, P_MobjGetSfxCategory(tracker), tracker);
1940 
1941 	P_ActTrackerFollow(tracker);
1942 }
1943 
1944 //
1945 // P_ActTrackerStart
1946 //
1947 // Called by the tracker to make its launch (see) sound: also tracks
1948 //
1949 // -ACB- 1998/08/22
1950 //
P_ActTrackerStart(mobj_t * tracker)1951 void P_ActTrackerStart(mobj_t * tracker)
1952 {
1953 	if (tracker->info->seesound)
1954 		S_StartFX(tracker->info->seesound, P_MobjGetSfxCategory(tracker), tracker);
1955 
1956 	P_ActTrackerFollow(tracker);
1957 }
1958 
1959 //
1960 // LaunchTracker
1961 //
1962 // This procedure starts a tracking object off and links
1963 // the tracker and the monster together.
1964 //
1965 // -ACB- 1998/08/22
1966 //
LaunchTracker(mobj_t * object)1967 static void LaunchTracker(mobj_t * object)
1968 {
1969 	const atkdef_c *attack = object->currentattack;
1970 	mobj_t *target = object->target;
1971 
1972 	if (!attack || !target)
1973 		return;
1974 
1975 	mobj_t *tracker = P_MobjCreateObject(target->x, target->y, target->z,
1976 								 attack->atk_mobj);
1977 
1978 	// link the tracker to the object
1979 	object->SetTracer(tracker);
1980 
1981 	// tracker source is the object
1982 	tracker->SetRealSource(object);
1983 
1984 	// tracker's target is the object's target
1985 	tracker->SetTarget(target);
1986 
1987 	P_ActTrackerFollow(tracker);
1988 }
1989 
1990 //
1991 // P_ActEffectTracker
1992 //
1993 // Called by the object that launched the tracker to
1994 // cause damage to its target and a radius attack
1995 // (explosion) at the location of the tracker.
1996 //
1997 // -ACB- 1998/08/22
1998 //
P_ActEffectTracker(mobj_t * object)1999 void P_ActEffectTracker(mobj_t * object)
2000 {
2001 	mobj_t *tracker;
2002 	mobj_t *target;
2003 	const atkdef_c *attack;
2004 	angle_t angle;
2005 	float damage;
2006 
2007 	if (!object->target || !object->currentattack)
2008 		return;
2009 
2010 	attack = object->currentattack;
2011 	target = object->target;
2012 
2013 	if (attack->flags & AF_FaceTarget)
2014 		P_ActFaceTarget(object);
2015 
2016 	if (attack->flags & AF_NeedSight)
2017 	{
2018 		if (!P_CheckSight(object, target))
2019 			return;
2020 	}
2021 
2022 	if (attack->sound)
2023 		S_StartFX(attack->sound, P_MobjGetSfxCategory(object), object);
2024 
2025 	angle = object->angle;
2026 	tracker = object->tracer;
2027 
2028 	DAMAGE_COMPUTE(damage, &attack->damage);
2029 
2030 	if (damage)
2031 		P_DamageMobj(target, object, object, damage, &attack->damage);
2032 #ifdef DEVELOPERS
2033 	else
2034 		L_WriteDebug("%s + %s attack has zero damage\n",
2035 					 object->info->name.c_str(),
2036 					 tracker->info->name.c_str());
2037 #endif
2038 
2039 	// -ACB- 2000/03/11 Check for zero mass
2040 	if (target->info->mass)
2041 		target->mom.z = 1000 / target->info->mass;
2042 	else
2043 		target->mom.z = 2000;
2044 
2045 	if (!tracker)
2046 		return;
2047 
2048 	// move the tracker between the object and the object's target
2049 
2050 	P_ChangeThingPosition(tracker,
2051 						  target->x - 24 * M_Cos(angle),
2052 						  target->y - 24 * M_Sin(angle),
2053 						  target->z);
2054 
2055 #ifdef DEVELOPERS
2056 	if (!tracker->info->explode_damage.nominal)
2057 		L_WriteDebug("%s + %s explosion has zero damage\n",
2058 					 object->info->name.c_str(),
2059 					 tracker->info->name.c_str());
2060 #endif
2061 
2062 	DAMAGE_COMPUTE(damage, &tracker->info->explode_damage);
2063 
2064 	float radius = object->info->explode_radius;
2065 	if (radius == 0) radius = damage;
2066 
2067 	P_RadiusAttack(tracker, object, radius, damage,
2068 				   &tracker->info->explode_damage, false);
2069 }
2070 
2071 //-----------------------------------------------------------------
2072 //--------------------BOSS HANDLING PROCEDURES---------------------
2073 //-----------------------------------------------------------------
2074 
ShootToSpot(mobj_t * object)2075 static void ShootToSpot(mobj_t * object)
2076 {
2077 	// Note: using a static int here for better randomness.
2078 	// -AJA- FIXME: should be global to allow synchronisation in Netgames
2079 	static int current_spot = 0;
2080 
2081 	if (! object->currentattack)
2082 		return;
2083 
2084 	if (brain_spots.number < 0)
2085 	{
2086 		if (! object->info->spitspot)
2087 		{
2088 			M_WarnError("Thing [%s] used SHOOT_TO_SPOT attack, but has no "
2089 						"SPIT_SPOT\n", object->info->name.c_str());
2090 			return;
2091 		}
2092 
2093 		P_LookForShootSpots(object->info->spitspot);
2094 	}
2095 
2096 	if (brain_spots.number == 0)
2097 		return;
2098 
2099 	SYS_ASSERT(brain_spots.targets);
2100 	SYS_ASSERT(brain_spots.number > 0);
2101 
2102 	current_spot += P_Random();
2103 	current_spot %= brain_spots.number;
2104 
2105 	LaunchProjectile(object, brain_spots.targets[current_spot],
2106 					 object->currentattack->atk_mobj);
2107 }
2108 
2109 //-------------------------------------------------------------------
2110 //-------------------OBJECT-SPAWN-OBJECT HANDLING--------------------
2111 //-------------------------------------------------------------------
2112 
2113 //
2114 // P_ActObjectSpawning
2115 //
2116 // An Object spawns another object and is spawned in the state specificed
2117 // by attack->objinitstate. The procedure is based on the A_PainShootSkull
2118 // which is the routine for shooting skulls from a pain elemental. In
2119 // this the object being created is decided in the attack. This
2120 // procedure also used the new blocking line check to see if
2121 // the object is spawned across a blocking line, if so the procedure
2122 // terminates.
2123 //
2124 // -ACB- 1998/08/23
2125 //
ObjectSpawning(mobj_t * parent,angle_t angle)2126 static void ObjectSpawning(mobj_t * parent, angle_t angle)
2127 {
2128 	float slope;
2129 	const atkdef_c *attack;
2130 
2131 	attack = parent->currentattack;
2132 	if (! attack)
2133 		return;
2134 
2135 	const mobjtype_c *shoottype = attack->spawnedobj;
2136 
2137 	if (! shoottype)
2138 	{
2139 		I_Error("Object [%s] uses spawning attack [%s], but no object "
2140 				"specified.\n",
2141 				parent->info->name.c_str(),
2142 				attack->name.c_str());
2143 	}
2144 
2145 	if (attack->spawn_limit > 0)
2146 	{
2147 		int count = 0;
2148 		for (mobj_t *mo = mobjlisthead; mo; mo = mo->next)
2149 			if (mo->info == shoottype)
2150 				if (++count >= attack->spawn_limit)
2151 					return;
2152 	}
2153 
2154 	// -AJA- 1999/09/10: apply the angle offset of the attack.
2155 	angle -= attack->angle_offset;
2156 	slope = M_Tan(parent->vertangle) + attack->slope_offset;
2157 
2158 	float spawnx = parent->x;
2159 	float spawny = parent->y;
2160 	float spawnz = parent->z + attack->height;
2161 
2162 	if (attack->flags & AF_PrestepSpawn)
2163 	{
2164 		float prestep = 4.0f + 1.5f * parent->radius + shoottype->radius;
2165 
2166 		spawnx += prestep * M_Cos(angle);
2167 		spawny += prestep * M_Sin(angle);
2168 	}
2169 
2170 	mobj_t *child = P_MobjCreateObject(spawnx, spawny, spawnz, shoottype);
2171 
2172 	// Blocking line detected between object and spawnpoint?
2173 	if (P_MapCheckBlockingLine(parent, child))
2174 	{
2175 		// -KM- 1999/01/31 Explode objects over remove them.
2176 		// -AJA- 2000/02/01: Remove now the default.
2177 		if (attack->flags & AF_KillFailedSpawn)
2178 			P_KillMobj(parent, child, NULL);
2179 		else
2180 			P_RemoveMobj(child);
2181 
2182 		return;
2183 	}
2184 
2185 	if (attack->sound)
2186 		S_StartFX(attack->sound, AttackSfxCat(parent), parent);
2187 
2188 	// If the object cannot move from its position, remove it or kill it.
2189 	if (!P_TryMove(child, child->x, child->y))
2190 	{
2191 		if (attack->flags & AF_KillFailedSpawn)
2192 			P_KillMobj(parent, child, NULL);
2193 		else
2194 			P_RemoveMobj(child);
2195 
2196 		return;
2197 	}
2198 
2199 	if (! (attack->flags & AF_NoTarget))
2200 		child->SetTarget(parent->target);
2201 
2202 	child->SetSupportObj(parent);
2203 
2204 	child->side = parent->side;
2205 
2206 	// -AJA- 2004/09/27: keep ambush status of parent
2207 	child->flags |= (parent->flags & MF_AMBUSH);
2208 
2209 	// -AJA- 1999/09/25: Set the initial direction & momentum when
2210 	//       the ANGLED_SPAWN attack special is used.
2211 	if (attack->flags & AF_AngledSpawn)
2212 		P_SetMobjDirAndSpeed(child, angle, slope, attack->assault_speed);
2213 
2214 	P_SetMobjStateDeferred(child, attack->objinitstate, 0);
2215 }
2216 
2217 //
2218 // P_ActObjectTripleSpawn
2219 //
2220 // Spawns three objects at 90, 180 and 270 degrees. This is essentially
2221 // another purist function to support the death sequence of the Pain
2222 // elemental. However it could be used as in conjunction with radius
2223 // triggers to generate a nice teleport spawn invasion.
2224 //
2225 // -ACB- 1998/08/23 (I think....)
2226 //
2227 
ObjectTripleSpawn(mobj_t * object)2228 static void ObjectTripleSpawn(mobj_t * object)
2229 {
2230 	ObjectSpawning(object, object->angle + ANG90);
2231 	ObjectSpawning(object, object->angle + ANG180);
2232 	ObjectSpawning(object, object->angle + ANG270);
2233 }
2234 
2235 //-------------------------------------------------------------------
2236 //-------------------SKULLFLY HANDLING ROUTINES----------------------
2237 //-------------------------------------------------------------------
2238 
2239 //
2240 // SkullFlyAssault
2241 //
2242 // This is the attack procedure for objects that launch themselves
2243 // at their target like a missile.
2244 //
2245 // -ACB- 1998/08/16
2246 //
SkullFlyAssault(mobj_t * object)2247 static void SkullFlyAssault(mobj_t * object)
2248 {
2249 	if (!object->currentattack)
2250 		return;
2251 
2252 	if (!object->target && !object->player)
2253 	{
2254 		// -AJA- 2000/09/29: fix for the zombie lost soul bug
2255 		// -AJA- 2000/10/22: monsters only !  Don't stuff up gibs/missiles.
2256 		if (object->extendedflags & EF_MONSTER)
2257 			object->flags |= MF_SKULLFLY;
2258 		return;
2259 	}
2260 
2261 	float speed = object->currentattack->assault_speed;
2262 
2263 	// -KM- 1999/01/31 Fix skulls in nightmare mode
2264 	if (level_flags.fastparm)
2265 		speed *= object->info->fast;
2266 
2267 	sfx_t *sound = object->currentattack->initsound;
2268 
2269 	if (sound)
2270 		S_StartFX(sound, P_MobjGetSfxCategory(object), object);
2271 
2272 	object->flags |= MF_SKULLFLY;
2273 
2274 	// determine destination
2275 	float tx, ty, tz;
2276 
2277 	P_TargetTheory(object, object->target, &tx, &ty, &tz);
2278 
2279 	float slope = P_ApproxSlope(tx - object->x, ty - object->y, tz - object->z);
2280 
2281 	P_SetMobjDirAndSpeed(object, object->angle, slope, speed);
2282 }
2283 
2284 //
2285 // P_SlammedIntoObject
2286 //
2287 // Used when a flying object hammers into another object when on the
2288 // attack. Replaces the code in PIT_Checkthing.
2289 //
2290 // -ACB- 1998/07/29: Written
2291 //
2292 // -AJA- 1999/09/12: Now uses P_SetMobjStateDeferred, since this
2293 //                   routine can be called by TryMove/PIT_CheckRelThing.
2294 //
P_SlammedIntoObject(mobj_t * object,mobj_t * target)2295 void P_SlammedIntoObject(mobj_t * object, mobj_t * target)
2296 {
2297 	if (object->currentattack)
2298 	{
2299 		if (target != NULL)
2300 		{
2301 			// -KM- 1999/01/31 Only hurt shootable objects...
2302 			if (target->flags & MF_SHOOTABLE)
2303 			{
2304 				float damage;
2305 
2306 				DAMAGE_COMPUTE(damage, &object->currentattack->damage);
2307 
2308 				P_DamageMobj(target, object, object, damage,
2309 							 &object->currentattack->damage);
2310 			}
2311 		}
2312 
2313 		sfx_t *sound = object->currentattack->sound;
2314 		if (sound)
2315 			S_StartFX(sound, P_MobjGetSfxCategory(object), object);
2316 	}
2317 
2318 	object->flags &= ~MF_SKULLFLY;
2319 	object->mom.x = object->mom.y = object->mom.z = 0;
2320 
2321 	P_SetMobjStateDeferred(object, object->info->idle_state, 0);
2322 }
2323 
2324 
P_UseThing(mobj_t * user,mobj_t * thing,float open_bottom,float open_top)2325 bool P_UseThing(mobj_t * user, mobj_t * thing, float open_bottom,
2326 				float open_top)
2327 {
2328 	// Called when this thing is attempted to be used (i.e. by pressing
2329 	// the spacebar near it) by the player.  Returns true if successfully
2330 	// used, or false if other things should be checked.
2331 
2332 	// item is disarmed ?
2333 	if (!(thing->flags & MF_TOUCHY))
2334 		return false;
2335 
2336   // can be reached ?
2337 	open_top    = MIN(open_top, thing->z + thing->height);
2338 	open_bottom = MAX(open_bottom, thing->z);
2339 
2340 	if (user->z >= open_top ||
2341 		(user->z + user->height + USE_Z_RANGE < open_bottom))
2342 		return false;
2343 
2344   // OK, disarm and put into touch states
2345 	SYS_ASSERT(thing->info->touch_state > 0);
2346 
2347 	thing->flags &= ~MF_TOUCHY;
2348 	P_SetMobjStateDeferred(thing, thing->info->touch_state, 0);
2349 
2350 	return true;
2351 }
2352 
2353 
P_TouchyContact(mobj_t * touchy,mobj_t * victim)2354 void P_TouchyContact(mobj_t *touchy, mobj_t *victim)
2355 {
2356 	// Used whenever a thing comes into contact with a TOUCHY object.
2357 	//
2358 	// -AJA- 1999/09/12: Now uses P_SetMobjStateDeferred, since this
2359 	//       routine can be called by TryMove/PIT_CheckRelThing.
2360 
2361 	// dead thing touching. Can happen with a sliding player corpse.
2362 	if (victim->health <= 0)
2363 		return;
2364 
2365   // don't harm the grenadier...
2366 	if (touchy->source == victim)
2367 		return;
2368 
2369 	touchy->SetTarget(victim);
2370 	touchy->flags &= ~MF_TOUCHY;  // disarm
2371 
2372 	if (touchy->info->touch_state)
2373 		P_SetMobjStateDeferred(touchy, touchy->info->touch_state, 0);
2374 	else
2375 		P_MobjExplodeMissile(touchy);
2376 }
2377 
2378 
P_ActTouchyRearm(mobj_t * touchy)2379 void P_ActTouchyRearm(mobj_t * touchy)
2380 {
2381 	touchy->flags |= MF_TOUCHY;
2382 }
2383 
P_ActTouchyDisarm(mobj_t * touchy)2384 void P_ActTouchyDisarm(mobj_t * touchy)
2385 {
2386 	touchy->flags &= ~MF_TOUCHY;
2387 }
2388 
2389 
P_ActBounceRearm(mobj_t * mo)2390 void P_ActBounceRearm(mobj_t * mo)
2391 {
2392 	mo->extendedflags &= ~EF_JUSTBOUNCED;
2393 }
2394 
P_ActBounceDisarm(mobj_t * mo)2395 void P_ActBounceDisarm(mobj_t * mo)
2396 {
2397 	mo->extendedflags |= EF_JUSTBOUNCED;
2398 }
2399 
2400 
P_ActDropItem(mobj_t * mo)2401 void P_ActDropItem(mobj_t * mo)
2402 {
2403 	const mobjtype_c *info = mo->info->dropitem;
2404 
2405 	if (mo->state && mo->state->action_par)
2406 	{
2407 		mobj_strref_c *ref = (mobj_strref_c *) mo->state->action_par;
2408 
2409 		info = ref->GetRef();
2410 	}
2411 
2412 	if (! info)
2413 	{
2414 		M_WarnError("P_ActDropItem: %s specifies no item to drop.\n",
2415 					mo->info->name.c_str());
2416 		return;
2417 	}
2418 
2419 	// unlike normal drops, these ones are displaced randomly
2420 
2421 	float dx = P_RandomNegPos() * mo->info->radius / 255.0f;
2422 	float dy = P_RandomNegPos() * mo->info->radius / 255.0f;
2423 
2424 	mobj_t *item = P_MobjCreateObject(mo->x + dx, mo->y + dy, mo->floorz, info);
2425 	SYS_ASSERT(item);
2426 
2427 	item->flags |= MF_DROPPED;
2428 	item->flags &= ~MF_SOLID;
2429 
2430 	item->angle = mo->angle;
2431 
2432 	// allow respawning
2433 	item->spawnpoint.x = item->x;
2434 	item->spawnpoint.y = item->y;
2435 	item->spawnpoint.z = item->z;
2436 	item->spawnpoint.angle = item->angle;
2437 	item->spawnpoint.vertangle = item->vertangle;
2438 	item->spawnpoint.info  = info;
2439 	item->spawnpoint.flags = 0;
2440 }
2441 
P_ActSpawn(mobj_t * mo)2442 void P_ActSpawn(mobj_t * mo)
2443 {
2444 	if (!mo->state || !mo->state->action_par)
2445 		I_Error("SPAWN() action used without a object name!\n");
2446 
2447 	mobj_strref_c *ref = (mobj_strref_c *) mo->state->action_par;
2448 
2449 	const mobjtype_c *info = ref->GetRef();
2450 	SYS_ASSERT(info);
2451 
2452 	mobj_t *item = P_MobjCreateObject(mo->x, mo->y, mo->z, info);
2453 	SYS_ASSERT(item);
2454 
2455 	item->angle = mo->angle;
2456 	item->side  = mo->side;
2457 
2458 	item->SetSource(mo);
2459 }
2460 
2461 
P_ActPathCheck(mobj_t * mo)2462 void P_ActPathCheck(mobj_t * mo)
2463 {
2464 	// Checks if the creature is a path follower, and if so enters the
2465 	// meander states.
2466 
2467 	if (! mo->path_trigger || ! mo->info->meander_state)
2468 		return;
2469 
2470 	P_SetMobjStateDeferred(mo, mo->info->meander_state, 0);
2471 
2472 	mo->movedir = DI_SLOWTURN;
2473 	mo->movecount = 0;
2474 }
2475 
2476 
P_ActPathFollow(mobj_t * mo)2477 void P_ActPathFollow(mobj_t * mo)
2478 {
2479 	// For path-following creatures (spawned via RTS), makes the creature
2480 	// follow the path by trying to get to the next node.
2481 
2482 	if (!mo->path_trigger)
2483 		return;
2484 
2485 	if (RAD_CheckReachedTrigger(mo))
2486 	{
2487 		// reached the very last one ?
2488 		if (!mo->path_trigger)
2489 		{
2490 			mo->movedir = DI_NODIR;
2491 			return;
2492 		}
2493 
2494 		mo->movedir = DI_SLOWTURN;
2495 		return;
2496 	}
2497 
2498 	float dx = mo->path_trigger->x - mo->x;
2499 	float dy = mo->path_trigger->y - mo->y;
2500 
2501 	angle_t diff = R_PointToAngle(0, 0, dx, dy) - mo->angle;
2502 
2503 	// movedir value:
2504 	//   0 for slow turning.
2505 	//   1 for fast turning.
2506 	//   2 for walking.
2507 	//   3 for evasive maneouvres.
2508 
2509 	// if (mo->movedir < 2)
2510 
2511 	if (mo->movedir == DI_SLOWTURN || mo->movedir == DI_FASTTURN)
2512 	{
2513 		if (diff > ANG1*15 && diff < (ANG_MAX-ANG1*15))
2514 		{
2515 			angle_t step = ANG1*30;
2516 
2517 			if (diff < ANG180)
2518 				mo->angle += P_Random() * (step >> 8);
2519 			else
2520 				mo->angle -= P_Random() * (step >> 8);
2521 
2522 			return;
2523 		}
2524 
2525 		// we are now facing the next node
2526 		mo->angle += diff;
2527 		mo->movedir = DI_WALKING;
2528 
2529 		diff = 0;
2530 	}
2531 
2532 	if (mo->movedir == DI_WALKING)
2533 	{
2534 		if (diff < ANG1*30)
2535 			mo->angle += ANG1 * 2;
2536 		else if (diff > (ANG_MAX-ANG1*30))
2537 			mo->angle -= ANG1 * 2;
2538 		else
2539 			mo->movedir = DI_SLOWTURN;
2540 
2541 		if (! P_Move(mo, true))
2542 		{
2543 			mo->movedir = DI_EVASIVE;
2544 			mo->angle = P_Random() << (ANGLEBITS - 8);
2545 			mo->movecount = 1 + (P_Random() & 7);
2546 		}
2547 		return;
2548 	}
2549 
2550 	// make evasive maneouvres
2551 	mo->movecount--;
2552 
2553 	if (mo->movecount <= 0)
2554 	{
2555 		mo->movedir = DI_FASTTURN;
2556 		return;
2557 	}
2558 
2559 	P_Move(mo, true);
2560 }
2561 
2562 //-------------------------------------------------------------------
2563 //--------------------ATTACK HANDLING PROCEDURES---------------------
2564 //-------------------------------------------------------------------
2565 
2566 //
2567 // P_DoAttack
2568 //
2569 // When an object goes on the attack, it current attack is handled here;
2570 // the attack type is discerned and the assault is launched.
2571 //
2572 // -ACB- 1998/08/07
2573 //
P_DoAttack(mobj_t * object)2574 static void P_DoAttack(mobj_t * object)
2575 {
2576 	const atkdef_c *attack = object->currentattack;
2577 
2578 	SYS_ASSERT(attack);
2579 
2580 	switch (attack->attackstyle)
2581 	{
2582 		case ATK_CLOSECOMBAT:
2583 		{
2584 			DoMeleeAttack(object);
2585 			break;
2586 		}
2587 
2588 		case ATK_PROJECTILE:
2589 		{
2590 			LaunchProjectile(object, object->target, attack->atk_mobj);
2591 			break;
2592 		}
2593 
2594 		case ATK_SMARTPROJECTILE:
2595 		{
2596 			LaunchSmartProjectile(object, object->target, attack->atk_mobj);
2597 			break;
2598 		}
2599 
2600 		case ATK_RANDOMSPREAD:
2601 		{
2602 			LaunchRandomSpread(object);
2603 			break;
2604 		}
2605 
2606 		case ATK_SHOOTTOSPOT:
2607 		{
2608 			ShootToSpot(object);
2609 			break;
2610 		}
2611 
2612 		case ATK_SHOT:
2613 		{
2614 			ShotAttack(object);
2615 			break;
2616 		}
2617 
2618 		case ATK_SKULLFLY:
2619 		{
2620 			SkullFlyAssault(object);
2621 			break;
2622 		}
2623 
2624 		case ATK_SPAWNER:
2625 		{
2626 			ObjectSpawning(object, object->angle);
2627 			break;
2628 		}
2629 
2630 		case ATK_SPREADER:
2631 		{
2632 			LaunchOrderedSpread(object);
2633 			break;
2634 		}
2635 
2636 		case ATK_TRACKER:
2637 		{
2638 			LaunchTracker(object);
2639 			break;
2640 		}
2641 
2642 		case ATK_TRIPLESPAWNER:
2643 		{
2644 			ObjectTripleSpawn(object);
2645 			break;
2646 		}
2647 
2648 		// -KM- 1998/11/25 Added spray attack
2649 		case ATK_SPRAY:
2650 		{
2651 			SprayAttack(object);
2652 			break;
2653 		}
2654 
2655 		default:  // THIS SHOULD NOT HAPPEN
2656 		{
2657 			if (strict_errors)
2658 				I_Error("P_DoAttack: %s has an unknown attack type.\n",
2659 					object->info->name.c_str());
2660 			break;
2661 		}
2662 	}
2663 }
2664 
2665 //
2666 // P_ActComboAttack
2667 //
2668 // This is called at end of a set of states that can result in
2669 // either a closecombat or ranged attack. The procedure checks
2670 // to see if the target is within melee range and picks the
2671 // approiate attack.
2672 //
2673 // -ACB- 1998/08/07
2674 //
P_ActComboAttack(mobj_t * object)2675 void P_ActComboAttack(mobj_t * object)
2676 {
2677 	const atkdef_c *attack;
2678 
2679 	if (!object->target)
2680 		return;
2681 
2682 	if (DecideMeleeAttack(object, object->info->closecombat))
2683 		attack = object->info->closecombat;
2684 	else
2685 		attack = object->info->rangeattack;
2686 
2687 	if (attack)
2688 	{
2689 		if (attack->flags & AF_FaceTarget)
2690 			P_ActFaceTarget(object);
2691 
2692 		if (attack->flags & AF_NeedSight)
2693 		{
2694 			if (!P_CheckSight(object, object->target))
2695 				return;
2696 		}
2697 
2698 		object->currentattack = attack;
2699 		P_DoAttack(object);
2700 	}
2701 #ifdef DEVELOPERS
2702 	else
2703 	{
2704 		if (!object->info->closecombat)
2705 			M_WarnError("%s hasn't got a close combat attack\n", object->info->name.c_str());
2706 		else
2707 			M_WarnError("%s hasn't got a range attack\n", object->info->name.c_str());
2708 	}
2709 #endif
2710 
2711 }
2712 
2713 //
2714 // P_ActMeleeAttack
2715 //
2716 // Setup a close combat assault
2717 //
2718 // -ACB- 1998/08/07
2719 //
P_ActMeleeAttack(mobj_t * object)2720 void P_ActMeleeAttack(mobj_t * object)
2721 {
2722 	const atkdef_c *attack;
2723 
2724 	attack = object->info->closecombat;
2725 
2726 	// -AJA- 1999/08/10: Multiple attack support.
2727 	if (object->state && object->state->action_par)
2728 		attack = (const atkdef_c *) object->state->action_par;
2729 
2730 	if (!attack)
2731 	{
2732 		M_WarnError("P_ActMeleeAttack: %s has no close combat attack.\n",
2733 					object->info->name.c_str());
2734 		return;
2735 	}
2736 
2737 	if (attack->flags & AF_FaceTarget)
2738 		P_ActFaceTarget(object);
2739 
2740 	if (attack->flags & AF_NeedSight)
2741 	{
2742 		if (!object->target || !P_CheckSight(object, object->target))
2743 			return;
2744 	}
2745 
2746 	object->currentattack = attack;
2747 	P_DoAttack(object);
2748 }
2749 
2750 //
2751 // P_ActRangeAttack
2752 //
2753 // Setup an attack at range
2754 //
2755 // -ACB- 1998/08/07
2756 //
P_ActRangeAttack(mobj_t * object)2757 void P_ActRangeAttack(mobj_t * object)
2758 {
2759 	const atkdef_c *attack;
2760 
2761 	attack = object->info->rangeattack;
2762 
2763 	// -AJA- 1999/08/10: Multiple attack support.
2764 	if (object->state && object->state->action_par)
2765 		attack = (const atkdef_c *) object->state->action_par;
2766 
2767 	if (!attack)
2768 	{
2769 		M_WarnError("P_ActRangeAttack: %s hasn't got a range attack.\n",
2770 					object->info->name.c_str());
2771 		return;
2772 	}
2773 
2774 	if (attack->flags & AF_FaceTarget)
2775 		P_ActFaceTarget(object);
2776 
2777 	if (attack->flags & AF_NeedSight)
2778 	{
2779 		if (!object->target || !P_CheckSight(object, object->target))
2780 			return;
2781 	}
2782 
2783 	object->currentattack = attack;
2784 	P_DoAttack(object);
2785 }
2786 
2787 //
2788 // P_ActSpareAttack
2789 //
2790 // Setup an attack that is not defined as close or range. can be
2791 // used to act as a follow attack for close or range, if you want one to
2792 // add to the others.
2793 //
2794 // -ACB- 1998/08/24
2795 //
P_ActSpareAttack(mobj_t * object)2796 void P_ActSpareAttack(mobj_t *object)
2797 {
2798 	const atkdef_c *attack;
2799 
2800 	attack = object->info->spareattack;
2801 
2802 	// -AJA- 1999/08/10: Multiple attack support.
2803 	if (object->state && object->state->action_par)
2804 		attack = (const atkdef_c *) object->state->action_par;
2805 
2806 	if (attack)
2807 	{
2808 		if ((attack->flags & AF_FaceTarget) && object->target)
2809 			P_ActFaceTarget(object);
2810 
2811 		if ((attack->flags & AF_NeedSight) && object->target)
2812 		{
2813 			if (!P_CheckSight(object, object->target))
2814 				return;
2815 		}
2816 
2817 		object->currentattack = attack;
2818 		P_DoAttack(object);
2819 	}
2820 #ifdef DEVELOPERS
2821 	else
2822 	{
2823 		M_WarnError("P_ActSpareAttack: %s hasn't got a spare attack\n", object->info->name.c_str());
2824 		return;
2825 	}
2826 #endif
2827 
2828 }
2829 
2830 //
2831 // P_ActRefireCheck
2832 //
2833 // This procedure will be called inbetween firing on an object
2834 // that will fire repeatly (Chaingunner/Arachontron etc...), the
2835 // purpose of this is to see if the object should refire and
2836 // performs checks to that effect, first there is a check to see
2837 // if the object will keep firing regardless and the others
2838 // check if the the target exists, is alive and within view. The
2839 // only other code here is a stealth check: a object with stealth
2840 // capabilitys will lose the ability while firing.
2841 //
2842 // -ACB- 1998/08/10
2843 //
P_ActRefireCheck(mobj_t * object)2844 void P_ActRefireCheck(mobj_t * object)
2845 {
2846 	mobj_t *target;
2847 	const atkdef_c *attack;
2848 
2849 	attack = object->currentattack;
2850 
2851 	if (! attack)
2852 		return;
2853 
2854 	if (attack->flags & AF_FaceTarget)
2855 		P_ActFaceTarget(object);
2856 
2857 	// Random chance that object will keep firing regardless
2858 	if (P_RandomTest(attack->keepfirechance))
2859 		return;
2860 
2861 	target = object->target;
2862 
2863 	if (!target || (target->health <= 0) || !P_CheckSight(object, target))
2864 	{
2865 		if (object->info->chase_state)
2866 			P_SetMobjStateDeferred(object, object->info->chase_state, 0);
2867 	}
2868 	else if (object->flags & MF_STEALTH)
2869 	{
2870 		object->vis_target = VISIBLE;
2871 	}
2872 }
2873 
2874 //
2875 // P_ActReloadCheck
2876 //
2877 // Enter reload states if the monster has shot a certain number of
2878 // shots (given by RELOAD_SHOTS command).
2879 //
2880 // -AJA- 2004/11/15: added this.
2881 //
P_ActReloadCheck(mobj_t * object)2882 void P_ActReloadCheck(mobj_t * object)
2883 {
2884 	object->shot_count++;
2885 
2886 	if (object->shot_count >= object->info->reload_shots)
2887 	{
2888 		object->shot_count = 0;
2889 
2890 		if (object->info->reload_state)
2891 			P_SetMobjStateDeferred(object, object->info->reload_state, 0);
2892 	}
2893 }
2894 
P_ActReloadReset(mobj_t * object)2895 void P_ActReloadReset(mobj_t * object)
2896 {
2897 	object->shot_count = 0;
2898 }
2899 
2900 //---------------------------------------------
2901 //-----------LOOKING AND CHASING---------------
2902 //---------------------------------------------
2903 
2904 extern mobj_t ** bmap_things;
2905 
2906 //
2907 // CreateAggression
2908 //
2909 // Sets an object up to target a previously stored object.
2910 //
2911 // -ACB- 2000/06/20 Re-written and Simplified
2912 //
2913 // -AJA- 2009/07/05 Rewritten again, using the blockmap
2914 //
CreateAggression(mobj_t * mo)2915 static bool CreateAggression(mobj_t * mo)
2916 {
2917 	if (mo->target && mo->target->health > 0)
2918 		return false;
2919 
2920 	// pick a block in blockmap to check
2921 	int bdx = P_RandomNegPos() / 17;
2922 	int bdy = P_RandomNegPos() / 17;
2923 
2924 	int block_x = BLOCKMAP_GET_X(mo->x) + bdx;
2925 	int block_y = BLOCKMAP_GET_X(mo->y) + bdy;
2926 
2927 	block_x = abs(block_x + bmap_width)  % bmap_width;
2928 	block_y = abs(block_y + bmap_height) % bmap_height;
2929 
2930 //  I_Debugf("BLOCKMAP POS: %3d %3d  (size: %d %d)\n", block_x, block_y, bmap_width, bmap_height);
2931 
2932 	int bnum = block_y * bmap_width + block_x;
2933 
2934 	for (mobj_t *other = bmap_things[bnum]; other; other = other->bnext)
2935 	{
2936 		if (! (other->info->extendedflags & EF_MONSTER) || other->health <= 0)
2937 			continue;
2938 
2939 		if (other == mo)
2940 			continue;
2941 
2942 		if (other->info == mo->info)
2943 		{
2944 			if (! (other->info->extendedflags & EF_DISLOYALTYPE))
2945 				continue;
2946 
2947 			// Type the same and it can't hurt own kind - not good.
2948 			if (! (other->info->extendedflags & EF_OWNATTACKHURTS))
2949 				continue;
2950 		}
2951 
2952 		// don't attack a friend if we cannot hurt them.
2953 		// -AJA- I'm assuming that even friends will 'infight'.
2954 		if ((mo->info->side & other->info->side) != 0 &&
2955 			(other->info->hyperflags & (HF_SIDEIMMUNE | HF_ULTRALOYAL)))
2956 		{
2957 			continue;
2958 		}
2959 
2960 		// POTENTIAL TARGET
2961 
2962 		// fairly low chance of trying it, in case this block
2963 		// contains many monsters (spread the love)
2964 		if (P_Random() > 99)
2965 			continue;
2966 
2967 		// sight check is expensive, do it last
2968 		if (! P_CheckSight(mo, other))
2969 			continue;
2970 
2971 		// OK, you got me
2972 		mo->SetTarget(other);
2973 
2974 		I_Debugf("Created aggression : %s --> %s\n",
2975 				 mo->info->name.c_str(),
2976 				 other->info->name.c_str());
2977 
2978 		if (mo->info->seesound)
2979 			S_StartFX(mo->info->seesound, P_MobjGetSfxCategory(mo),
2980 						   mo, SfxFlags(mo->info));
2981 
2982 		if (mo->info->chase_state)
2983 			P_SetMobjStateDeferred(mo, mo->info->chase_state, 0);
2984 
2985 		return true;
2986 	}
2987 
2988 	return false;
2989 }
2990 
2991 
2992 //
2993 // P_ActStandardLook
2994 //
2995 // Standard Lookout procedure
2996 //
2997 // -ACB- 1998/08/22
2998 //
P_ActStandardLook(mobj_t * object)2999 void P_ActStandardLook(mobj_t * object)
3000 {
3001 	int targ_pnum;
3002 	mobj_t *targ = NULL;
3003 
3004 	object->threshold = 0;  // any shot will wake up
3005 
3006 	targ_pnum = object->subsector->sector->sound_player;
3007 
3008 	if (targ_pnum >= 0 && targ_pnum < MAXPLAYERS &&
3009 		players[targ_pnum])
3010 	{
3011 		targ = players[targ_pnum]->mo;
3012 	}
3013 
3014 	// -AJA- 2004/09/02: ignore the sound of a friend
3015 	// FIXME: maybe wake up and support that player ??
3016 	if (targ && (targ->side & object->side) != 0)
3017 		targ = NULL;
3018 
3019 	if (object->flags & MF_STEALTH)
3020 		object->vis_target = VISIBLE;
3021 
3022 	if (g_aggression.d)
3023 		if (CreateAggression(object) || CreateAggression(object))
3024 			return;
3025 
3026 	if (targ && (targ->flags & MF_SHOOTABLE))
3027 	{
3028 		object->SetTarget(targ);
3029 
3030 		if (object->flags & MF_AMBUSH)
3031 		{
3032 			if (!P_CheckSight(object, object->target) &&
3033 				!P_LookForPlayers(object, object->info->sight_angle))
3034 				return;
3035 		}
3036 	}
3037 	else
3038 	{
3039 		if (!P_LookForPlayers(object, object->info->sight_angle))
3040 			return;
3041 	}
3042 
3043 	if (object->info->seesound)
3044 	{
3045 		S_StartFX(object->info->seesound, P_MobjGetSfxCategory(object),
3046 					   object, SfxFlags(object->info));
3047 	}
3048 
3049 	// -AJA- this will remove objects which have no chase states.
3050         // For compatibility with original DOOM.
3051 	P_SetMobjStateDeferred(object, object->info->chase_state, 0);
3052 }
3053 
3054 //
3055 // P_ActPlayerSupportLook
3056 //
3057 // Player Support Lookout procedure
3058 //
3059 // -ACB- 1998/09/05
3060 //
P_ActPlayerSupportLook(mobj_t * object)3061 void P_ActPlayerSupportLook(mobj_t * object)
3062 {
3063 	object->threshold = 0;  // any shot will wake up
3064 
3065 	if (object->flags & MF_STEALTH)
3066 		object->vis_target = VISIBLE;
3067 
3068 	if (!object->supportobj)
3069 	{
3070 		if (! P_ActLookForTargets(object))
3071 			return;
3072 
3073 		// -AJA- 2004/09/02: join the player's side
3074 		if (object->side == 0)
3075 			object->side = object->target->side;
3076 
3077 		if (object->info->seesound)
3078 		{
3079 			S_StartFX(object->info->seesound, P_MobjGetSfxCategory(object),
3080 						   object, SfxFlags(object->info));
3081 		}
3082 	}
3083 
3084 	if (object->info->meander_state)
3085 		P_SetMobjStateDeferred(object, object->info->meander_state, 0);
3086 }
3087 
3088 //
3089 // P_ActStandardMeander
3090 //
P_ActStandardMeander(mobj_t * object)3091 void P_ActStandardMeander(mobj_t * object)
3092 {
3093 	int delta;
3094 
3095 	object->threshold = 0;  // any shot will wake up
3096 
3097 	// move within supporting distance of player
3098 	if (--object->movecount < 0 || !P_Move(object, false))
3099 		P_NewChaseDir(object);
3100 
3101 	// turn towards movement direction if not there yet
3102 	if (object->movedir < 8)
3103 	{
3104 		object->angle &= (7 << 29);
3105 		delta = object->angle - (object->movedir << 29);
3106 
3107 		if (delta > 0)
3108 			object->angle -= ANG45;
3109 		else if (delta < 0)
3110 			object->angle += ANG45;
3111 	}
3112 }
3113 
3114 //
3115 // P_ActPlayerSupportMeander
3116 //
P_ActPlayerSupportMeander(mobj_t * object)3117 void P_ActPlayerSupportMeander(mobj_t * object)
3118 {
3119 	int delta;
3120 
3121 	object->threshold = 0;  // any shot will wake up
3122 
3123 	// move within supporting distance of player
3124 	if (--object->movecount < 0 || !P_Move(object, false))
3125 		P_NewChaseDir(object);
3126 
3127 	// turn towards movement direction if not there yet
3128 	if (object->movedir < 8)
3129 	{
3130 		object->angle &= (7 << 29);
3131 		delta = object->angle - (object->movedir << 29);
3132 
3133 		if (delta > 0)
3134 			object->angle -= ANG45;
3135 		else if (delta < 0)
3136 			object->angle += ANG45;
3137 	}
3138 
3139 	//
3140 	// we have now meandered, now check for a support object, if we don't
3141 	// look for one and return; else look for targets to take out, if we
3142 	// find one, go for the chase.
3143 	//
3144 	/*  if (!object->supportobj)
3145 		{
3146 		P_ActPlayerSupportLook(object);
3147 		return;
3148 		} */
3149 
3150 	P_ActLookForTargets(object);
3151 }
3152 
3153 //
3154 // P_ActStandardChase
3155 //
3156 // Standard AI Chase Procedure
3157 //
3158 // -ACB- 1998/08/22 Procedure Written
3159 // -ACB- 1998/09/05 Added Support Object Check
3160 //
P_ActStandardChase(mobj_t * object)3161 void P_ActStandardChase(mobj_t * object)
3162 {
3163 	int delta;
3164 	sfx_t *sound;
3165 
3166 	if (object->reactiontime)
3167 		object->reactiontime--;
3168 
3169 	// object has a pain threshold, while this is true, reduce it. while
3170 	// the threshold is true, the object will remain intent on its target.
3171 	if (object->threshold)
3172 	{
3173 		if (!object->target || object->target->health <= 0)
3174 			object->threshold = 0;
3175 		else
3176 			object->threshold--;
3177 	}
3178 
3179 	// A Chasing Stealth Creature becomes less visible
3180 	if (object->flags & MF_STEALTH)
3181 		object->vis_target = INVISIBLE;
3182 
3183 	// turn towards movement direction if not there yet
3184 	if (object->movedir < 8)
3185 	{
3186 		object->angle &= (7 << 29);
3187 		delta = object->angle - (object->movedir << 29);
3188 
3189 		if (delta > 0)
3190 			object->angle -= ANG45;
3191 		else if (delta < 0)
3192 			object->angle += ANG45;
3193 	}
3194 
3195 	if (!object->target || !(object->target->flags & MF_SHOOTABLE))
3196 	{
3197 		if (P_ActLookForTargets(object))
3198 			return;
3199 
3200 		// -ACB- 1998/09/06 Target is not relevant: NULLify.
3201 		object->SetTarget(NULL);
3202 
3203 		P_SetMobjStateDeferred(object, object->info->idle_state, 0);
3204 		return;
3205 	}
3206 
3207 	// do not attack twice in a row
3208 	if (object->flags & MF_JUSTATTACKED)
3209 	{
3210 		object->flags &= ~MF_JUSTATTACKED;
3211 
3212 		// -KM- 1998/12/16 Nightmare mode set the fast parm.
3213 		if (!level_flags.fastparm)
3214 			P_NewChaseDir(object);
3215 
3216 		return;
3217 	}
3218 
3219 	sound = object->info->attacksound;
3220 
3221 	// check for melee attack
3222 	if (object->info->melee_state && DecideMeleeAttack(object, object->info->closecombat))
3223 	{
3224 		if (sound)
3225 			S_StartFX(sound, P_MobjGetSfxCategory(object), object);
3226 
3227 		if (object->info->melee_state)
3228 			P_SetMobjStateDeferred(object, object->info->melee_state, 0);
3229 		return;
3230 	}
3231 
3232 	// check for missile attack
3233 	if (object->info->missile_state)
3234 	{
3235 		// -KM- 1998/12/16 Nightmare set the fastparm.
3236 		if (!(!level_flags.fastparm && object->movecount))
3237 		{
3238 			if (DecideRangeAttack(object))
3239 			{
3240 				if (object->info->missile_state)
3241 					P_SetMobjStateDeferred(object, object->info->missile_state, 0);
3242 				object->flags |= MF_JUSTATTACKED;
3243 				return;
3244 			}
3245 		}
3246 	}
3247 
3248 	// possibly choose another target
3249 	// -ACB- 1998/09/05 Object->support->object check, go for new targets
3250 	if (!P_CheckSight(object, object->target) && !object->threshold)
3251 	{
3252 		if (P_ActLookForTargets(object))
3253 			return;
3254 	}
3255 
3256 	// chase towards player
3257 	if (--object->movecount < 0 || !P_Move(object, false))
3258 		P_NewChaseDir(object);
3259 
3260 	// make active sound
3261 	if (object->info->activesound && M_Random() < 3)
3262 		S_StartFX(object->info->activesound, P_MobjGetSfxCategory(object), object);
3263 }
3264 
3265 //
3266 // P_ActResurrectChase
3267 //
3268 // Before undertaking the standard chase procedure, the object
3269 // will check for a nearby corpse and raises one if it exists.
3270 //
3271 // -ACB- 1998/09/05 Support Check: Raised object supports raiser's supportobj
3272 //
P_ActResurrectChase(mobj_t * object)3273 void P_ActResurrectChase(mobj_t * object)
3274 {
3275 	mobj_t *corpse;
3276 
3277 	corpse = P_MapFindCorpse(object);
3278 
3279 	if (corpse)
3280 	{
3281 		object->angle = R_PointToAngle(object->x, object->y, corpse->x, corpse->y);
3282 		if (object->info->res_state)
3283 			P_SetMobjStateDeferred(object, object->info->res_state, 0);
3284 
3285 		// corpses without raise states should be skipped
3286 		SYS_ASSERT(corpse->info->raise_state);
3287 
3288 		P_BringCorpseToLife(corpse);
3289 
3290 		// -ACB- 1998/09/05 Support Check: Res creatures to support that object
3291 		if (object->supportobj)
3292 		{
3293 			corpse->SetSupportObj(object->supportobj);
3294 			corpse->SetTarget(object->target);
3295 		}
3296 		else
3297 		{
3298 			corpse->SetSupportObj(NULL);
3299 			corpse->SetTarget(NULL);
3300 		}
3301 
3302 		// -AJA- Resurrected creatures are on Archvile's side (like MBF)
3303 		corpse->side = object->side;
3304 		return;
3305 	}
3306 
3307 	P_ActStandardChase(object);
3308 }
3309 
3310 //
3311 // P_ActWalkSoundChase
3312 //
3313 // Make a sound and then chase...
3314 //
P_ActWalkSoundChase(mobj_t * object)3315 void P_ActWalkSoundChase(mobj_t * object)
3316 {
3317 	if (!object->info->walksound)
3318 	{
3319 		M_WarnError("WALKSOUND_CHASE: %s hasn't got a walksound.\n",
3320 					object->info->name.c_str());
3321 		return;
3322 	}
3323 
3324 	S_StartFX(object->info->walksound, P_MobjGetSfxCategory(object), object);
3325 	P_ActStandardChase(object);
3326 }
3327 
3328 
P_ActDie(mobj_t * mo)3329 void P_ActDie(mobj_t * mo)
3330 {
3331 	// Boom/MBF compatibility.
3332 
3333 	P_DamageMobj(mo, NULL, NULL, mo->health + 1, NULL);
3334 }
3335 
P_ActKeenDie(mobj_t * mo)3336 void P_ActKeenDie(mobj_t * mo)
3337 {
3338 	P_ActMakeIntoCorpse(mo);
3339 
3340 	// see if all other Keens are dead
3341 	for (mobj_t *cur = mobjlisthead; cur != NULL; cur = cur->next)
3342 	{
3343 		if (cur == mo)
3344 			continue;
3345 
3346 		if (cur->info != mo->info)
3347 			continue;
3348 
3349 		if (cur->health > 0)
3350 			return; // other Keen not dead
3351 	}
3352 
3353 	L_WriteDebug("P_ActKeenDie: ALL DEAD, activating...\n");
3354 
3355 	P_RemoteActivation(NULL, 2 /* door type */, 666 /* tag */, 0, line_Any);
3356 }
3357 
3358 
P_ActCheckMoving(mobj_t * mo)3359 void P_ActCheckMoving(mobj_t * mo)
3360 {
3361 	// -KM- 1999/01/31 Returns a player to spawnstate when not moving.
3362 
3363 	player_t *pl = mo->player;
3364 
3365 	if (pl)
3366 	{
3367 		if (pl->actual_speed < PLAYER_STOPSPEED)
3368 		{
3369 			P_SetMobjStateDeferred(mo, mo->info->idle_state, 0);
3370 
3371 			// we delay a little bit, in order to prevent a loop where
3372 			// CHECK_ACTIVITY jumps to SWIM states (for example) and
3373 			// then CHECK_MOVING jumps right back to IDLE states.
3374 			mo->tics = 2;
3375 		}
3376 		return;
3377 	}
3378 
3379 	if (fabs(mo->mom.x) < STOPSPEED && fabs(mo->mom.y) < STOPSPEED)
3380 	{
3381 		mo->mom.x = mo->mom.y = 0;
3382 		P_SetMobjStateDeferred(mo, mo->info->idle_state, 0);
3383 	}
3384 }
3385 
P_ActCheckActivity(mobj_t * mo)3386 void P_ActCheckActivity(mobj_t *mo)
3387 {
3388 	player_t *pl = mo->player;
3389 
3390 	if (! pl)
3391 		return;
3392 
3393 	if (pl->swimming)
3394 	{
3395 		// enter the SWIM states (if present)
3396 		statenum_t swim_st = P_MobjFindLabel(pl->mo, "SWIM");
3397 
3398 		if (swim_st == S_NULL)
3399 			swim_st = pl->mo->info->chase_state;
3400 
3401 		if (swim_st != S_NULL)
3402 			P_SetMobjStateDeferred(pl->mo, swim_st, 0);
3403 
3404 		return;
3405 	}
3406 
3407 	if (pl->powers[PW_Jetpack] > 0)
3408 	{
3409 		// enter the FLY states (if present)
3410 		statenum_t fly_st = P_MobjFindLabel(pl->mo, "FLY");
3411 
3412 		if (fly_st != S_NULL)
3413 			P_SetMobjStateDeferred(pl->mo, fly_st, 0);
3414 
3415 		return;
3416 	}
3417 
3418 	if (mo->on_ladder >= 0)
3419 	{
3420 		// enter the CLIMB states (if present)
3421 		statenum_t climb_st = P_MobjFindLabel(pl->mo, "CLIMB");
3422 
3423 		if (climb_st != S_NULL)
3424 			P_SetMobjStateDeferred(pl->mo, climb_st, 0);
3425 
3426 		return;
3427 	}
3428 
3429 	/* Otherwise: do nothing */
3430 }
3431 
3432 
P_ActCheckBlood(mobj_t * mo)3433 void P_ActCheckBlood(mobj_t * mo)
3434 {
3435 	// -KM- 1999/01/31 Part of the extra blood option, makes blood stick around...
3436 	// -AJA- 1999/10/02: ...but not indefinitely.
3437 
3438 	if (level_flags.more_blood && mo->tics >= 0)
3439 	{
3440 		int val = P_Random();
3441 
3442 		// exponential formula
3443 		mo->tics = ((val * val * val) >> 18) * TICRATE + TICRATE;
3444 	}
3445 }
3446 
3447 
P_ActJump(mobj_t * mo)3448 void P_ActJump(mobj_t * mo)
3449 {
3450 	// Jumps to the given label, possibly randomly.
3451 	//
3452 	// Note: nothing to do with monsters physically jumping.
3453 
3454 	if (!mo->state || !mo->state->action_par)
3455 	{
3456 		M_WarnError("JUMP action used in [%s] without a label !\n",
3457 					mo->info->name.c_str());
3458 		return;
3459 	}
3460 
3461 	act_jump_info_t *jump = (act_jump_info_t *) mo->state->action_par;
3462 
3463 	SYS_ASSERT(jump->chance >= 0);
3464 	SYS_ASSERT(jump->chance <= 1);
3465 
3466 	if (P_RandomTest(jump->chance))
3467 	{
3468 		mo->next_state = (mo->state->jumpstate == S_NULL) ?
3469 			NULL : (states + mo->state->jumpstate);
3470 	}
3471 }
3472 
3473 
P_ActSetInvuln(struct mobj_s * mo)3474 void P_ActSetInvuln(struct mobj_s *mo)
3475 {
3476 	mo->hyperflags |= HF_INVULNERABLE;
3477 }
3478 
P_ActClearInvuln(struct mobj_s * mo)3479 void P_ActClearInvuln(struct mobj_s *mo)
3480 {
3481 	mo->hyperflags &= ~HF_INVULNERABLE;
3482 }
3483 
3484 
P_ActBecome(struct mobj_s * mo)3485 void P_ActBecome(struct mobj_s *mo)
3486 {
3487 	if (!mo->state || !mo->state->action_par)
3488 	{
3489 		I_Error("BECOME action used in [%s] without arguments!\n",
3490 				mo->info->name.c_str());
3491 		return; /* NOT REACHED */
3492 	}
3493 
3494 	act_become_info_t *become = (act_become_info_t *) mo->state->action_par;
3495 
3496 	if (! become->info)
3497 	{
3498 		become->info = mobjtypes.Lookup(become->info_ref.c_str());
3499 		SYS_ASSERT(become->info);  // lookup should be OK (fatal error if not found)
3500 	}
3501 
3502 	// DO THE DEED !!
3503 
3504 	P_UnsetThingPosition(mo);
3505 	{
3506 		mo->info = become->info;
3507 
3508 		mo->radius = mo->info->radius;
3509 		mo->height = mo->info->height;
3510 		mo->speed  = mo->info->speed * (level_flags.fastparm ? mo->info->fast : 1);
3511 
3512 		// Note: health is not changed
3513 
3514 		mo->flags         = mo->info->flags;
3515 		mo->extendedflags = mo->info->extendedflags;
3516 		mo->hyperflags    = mo->info->hyperflags;
3517 
3518 		mo->vis_target    = PERCENT_2_FLOAT(mo->info->translucency);
3519 		mo->currentattack = NULL;
3520 		mo->model_skin    = mo->info->model_skin;
3521 		mo->model_last_frame = -1;
3522 
3523 		// handle dynamic lights
3524 		{
3525 			const dlight_info_c *dinfo = &mo->info->dlight[0];
3526 
3527 			if (dinfo->type != DLITE_None)
3528 			{
3529 				mo->dlight.target = dinfo->radius;
3530 				mo->dlight.color  = dinfo->colour;
3531 
3532 				// make renderer re-create shader info
3533 				if (mo->dlight.shader)
3534 				{
3535 					// FIXME: delete mo->dlight.shader;
3536 					mo->dlight.shader = NULL;
3537 				}
3538 			}
3539 		}
3540 	}
3541 	P_SetThingPosition(mo);
3542 
3543 	statenum_t state = P_MobjFindLabel(mo, become->start.label.c_str());
3544 	if (state == S_NULL)
3545 		I_Error("BECOME action: frame '%s' in [%s] not found!\n",
3546 				become->start.label.c_str(), mo->info->name.c_str());
3547 
3548 	state += become->start.offset;
3549 
3550 	P_SetMobjStateDeferred(mo, state, 0);
3551 }
3552 
3553 
3554 // -AJA- 1999/08/08: New attack flag FORCEAIM, which fixes chainsaw.
3555 //
P_PlayerAttack(mobj_t * p_obj,const atkdef_c * attack)3556 void P_PlayerAttack(mobj_t * p_obj, const atkdef_c * attack)
3557 {
3558 	SYS_ASSERT(attack);
3559 
3560 	p_obj->currentattack = attack;
3561 
3562 	float range = (attack->range > 0) ? attack->range : MISSILERANGE;
3563 
3564 	// see which target is to be aimed at
3565 	mobj_t *target = P_MapTargetAutoAim(p_obj, p_obj->angle, range,
3566 				 (attack->flags & AF_ForceAim) ? true : false);
3567 
3568 	p_obj->SetTarget(target);
3569 
3570 	if (attack->flags & AF_FaceTarget)
3571 		P_ActFaceTarget(p_obj);
3572 
3573 	P_DoAttack(p_obj);
3574 }
3575 
3576 
3577 //--- editor settings ---
3578 // vi:ts=4:sw=4:noexpandtab
3579