1 // Emacs style mode select	 -*- C++ -*-
2 // Emacs style mode select	 -*- C++ -*-
3 //-----------------------------------------------------------------------------
4 //
5 // $Id:$
6 //
7 // Copyright (C) 1993-1996 by id Software, Inc.
8 //
9 // This source is available for distribution and/or modification
10 // only under the terms of the DOOM Source Code License as
11 // published by id Software. All rights reserved.
12 //
13 // The source is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
16 // for more details.
17 //
18 // $Log:$
19 //
20 // DESCRIPTION:
21 //		Moving object handling. Spawn functions.
22 //
23 //-----------------------------------------------------------------------------
24 
25 // HEADER FILES ------------------------------------------------------------
26 
27 #include "templates.h"
28 #include "i_system.h"
29 #include "m_random.h"
30 #include "doomdef.h"
31 #include "p_local.h"
32 #include "p_lnspec.h"
33 #include "p_effect.h"
34 #include "p_terrain.h"
35 #include "st_stuff.h"
36 #include "hu_stuff.h"
37 #include "s_sound.h"
38 #include "doomstat.h"
39 #include "v_video.h"
40 #include "c_cvars.h"
41 #include "c_dispatch.h"
42 #include "b_bot.h"	//Added by MC:
43 #include "stats.h"
44 #include "a_hexenglobal.h"
45 #include "a_sharedglobal.h"
46 #include "gi.h"
47 #include "sbar.h"
48 #include "p_acs.h"
49 #include "cmdlib.h"
50 #include "decallib.h"
51 #include "ravenshared.h"
52 #include "a_action.h"
53 #include "a_keys.h"
54 #include "p_conversation.h"
55 #include "thingdef/thingdef.h"
56 #include "g_game.h"
57 #include "teaminfo.h"
58 #include "r_data/r_translate.h"
59 #include "r_sky.h"
60 #include "g_level.h"
61 #include "d_event.h"
62 #include "colormatcher.h"
63 #include "v_palette.h"
64 #include "p_enemy.h"
65 #include "gstrings.h"
66 #include "farchive.h"
67 #include "r_data/colormaps.h"
68 #include "r_renderer.h"
69 
70 // MACROS ------------------------------------------------------------------
71 
72 #define WATER_SINK_FACTOR		3
73 #define WATER_SINK_SMALL_FACTOR	4
74 #define WATER_SINK_SPEED		(FRACUNIT/2)
75 #define WATER_JUMP_SPEED		(FRACUNIT*7/2)
76 
77 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
78 
79 void G_PlayerReborn (int player);
80 
81 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
82 
83 static void PlayerLandedOnThing (AActor *mo, AActor *onmobj);
84 
85 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
86 
87 extern int BotWTG;
88 EXTERN_CVAR (Int,  cl_rockettrails)
89 
90 // PRIVATE DATA DEFINITIONS ------------------------------------------------
91 
92 static FRandom pr_explodemissile ("ExplodeMissile");
93 FRandom pr_bounce ("Bounce");
94 static FRandom pr_reflect ("Reflect");
95 static FRandom pr_nightmarerespawn ("NightmareRespawn");
96 static FRandom pr_botspawnmobj ("BotSpawnActor");
97 static FRandom pr_spawnmapthing ("SpawnMapThing");
98 static FRandom pr_spawnpuff ("SpawnPuff");
99 static FRandom pr_spawnblood ("SpawnBlood");
100 static FRandom pr_splatter ("BloodSplatter");
101 static FRandom pr_takedamage ("TakeDamage");
102 static FRandom pr_splat ("FAxeSplatter");
103 static FRandom pr_ripperblood ("RipperBlood");
104 static FRandom pr_chunk ("Chunk");
105 static FRandom pr_checkmissilespawn ("CheckMissileSpawn");
106 static FRandom pr_spawnmissile ("SpawnMissile");
107 static FRandom pr_missiledamage ("MissileDamage");
108 static FRandom pr_multiclasschoice ("MultiClassChoice");
109 static FRandom pr_rockettrail("RocketTrail");
110 static FRandom pr_uniquetid("UniqueTID");
111 
112 // PUBLIC DATA DEFINITIONS -------------------------------------------------
113 
114 FRandom pr_spawnmobj ("SpawnActor");
115 
116 CUSTOM_CVAR (Float, sv_gravity, 800.f, CVAR_SERVERINFO|CVAR_NOSAVE)
117 {
118 	level.gravity = self;
119 }
120 
121 CVAR (Bool, cl_missiledecals, true, CVAR_ARCHIVE)
122 CVAR (Bool, addrocketexplosion, false, CVAR_ARCHIVE)
123 CVAR (Int, cl_pufftype, 0, CVAR_ARCHIVE);
124 CVAR (Int, cl_bloodtype, 0, CVAR_ARCHIVE);
125 
126 // CODE --------------------------------------------------------------------
127 
128 IMPLEMENT_POINTY_CLASS (AActor)
DECLARE_POINTER(target)129  DECLARE_POINTER (target)
130  DECLARE_POINTER (lastenemy)
131  DECLARE_POINTER (tracer)
132  DECLARE_POINTER (goal)
133  DECLARE_POINTER (LastLookActor)
134  DECLARE_POINTER (Inventory)
135  DECLARE_POINTER (LastHeard)
136  DECLARE_POINTER (master)
137  DECLARE_POINTER (Poisoner)
138 END_POINTERS
139 
140 AActor::~AActor ()
141 {
142 	// Please avoid calling the destructor directly (or through delete)!
143 	// Use Destroy() instead.
144 }
145 
Serialize(FArchive & arc)146 void AActor::Serialize (FArchive &arc)
147 {
148 	Super::Serialize (arc);
149 
150 	if (arc.IsStoring ())
151 	{
152 		arc.WriteSprite (sprite);
153 	}
154 	else
155 	{
156 		sprite = arc.ReadSprite ();
157 	}
158 
159 	arc << __pos.x
160 		<< __pos.y
161 		<< __pos.z
162 		<< angle
163 		<< frame
164 		<< scaleX
165 		<< scaleY
166 		<< RenderStyle
167 		<< renderflags
168 		<< picnum
169 		<< floorpic
170 		<< ceilingpic
171 		<< TIDtoHate
172 		<< LastLookPlayerNumber
173 		<< LastLookActor
174 		<< effects
175 		<< alpha
176 		<< fillcolor
177 		<< pitch
178 		<< roll
179 		<< Sector
180 		<< floorz
181 		<< ceilingz
182 		<< dropoffz
183 		<< floorsector
184 		<< ceilingsector
185 		<< radius
186 		<< height
187 		<< projectilepassheight
188 		<< velx
189 		<< vely
190 		<< velz
191 		<< tics
192 		<< state
193 		<< Damage;
194 	if (SaveVersion >= 4530)
195 	{
196 		P_SerializeTerrain(arc, floorterrain);
197 	}
198 	if (SaveVersion >= 3227)
199 	{
200 		arc << projectileKickback;
201 	}
202 	arc	<< flags
203 		<< flags2
204 		<< flags3
205 		<< flags4
206 		<< flags5
207 		<< flags6;
208 	if (SaveVersion >= 4504)
209 	{
210 		arc << flags7;
211 	}
212 	if (SaveVersion >= 4512)
213 	{
214 		arc << weaponspecial;
215 	}
216 	arc	<< special1
217 		<< special2
218 		<< health
219 		<< movedir
220 		<< visdir
221 		<< movecount
222 		<< strafecount
223 		<< target
224 		<< lastenemy
225 		<< LastHeard
226 		<< reactiontime
227 		<< threshold
228 		<< player
229 		<< SpawnPoint[0] << SpawnPoint[1] << SpawnPoint[2]
230 		<< SpawnAngle;
231 	if (SaveVersion >= 4506)
232 	{
233 		arc << StartHealth;
234 	}
235 	arc << skillrespawncount
236 		<< tracer
237 		<< floorclip
238 		<< tid
239 		<< special;
240 	if (P_IsACSSpecial(special))
241 	{
242 		P_SerializeACSScriptNumber(arc, args[0], false);
243 	}
244 	else
245 	{
246 		arc << args[0];
247 	}
248 	arc << args[1] << args[2] << args[3] << args[4];
249 	if (SaveVersion >= 3427)
250 	{
251 		arc << accuracy << stamina;
252 	}
253 	arc << goal
254 		<< waterlevel
255 		<< MinMissileChance
256 		<< SpawnFlags
257 		<< Inventory
258 		<< InventoryID;
259 	if (SaveVersion < 4513)
260 	{
261 		SDWORD id;
262 		arc << id;
263 	}
264 	arc << FloatBobPhase
265 		<< Translation
266 		<< SeeSound
267 		<< AttackSound
268 		<< PainSound
269 		<< DeathSound
270 		<< ActiveSound
271 		<< UseSound
272 		<< BounceSound
273 		<< WallBounceSound
274 		<< CrushPainSound
275 		<< Speed
276 		<< FloatSpeed
277 		<< Mass
278 		<< PainChance
279 		<< SpawnState
280 		<< SeeState
281 		<< MeleeState
282 		<< MissileState
283 		<< MaxDropOffHeight
284 		<< MaxStepHeight
285 		<< BounceFlags
286 		<< bouncefactor
287 		<< wallbouncefactor
288 		<< bouncecount
289 		<< maxtargetrange
290 		<< meleethreshold
291 		<< meleerange
292 		<< DamageType;
293 	if (SaveVersion >= 4501)
294 	{
295 		arc << DamageTypeReceived;
296 	}
297 	if (SaveVersion >= 3237)
298 	{
299 		arc
300 		<< PainType
301 		<< DeathType;
302 	}
303 	arc	<< gravity
304 		<< FastChaseStrafeCount
305 		<< master
306 		<< smokecounter
307 		<< BlockingMobj
308 		<< BlockingLine
309 		<< VisibleToTeam // [BB]
310 		<< pushfactor
311 		<< Species
312 		<< Score;
313 	if (SaveVersion >= 3113)
314 	{
315 		arc << DesignatedTeam;
316 	}
317 	arc << lastpush << lastbump
318 		<< PainThreshold
319 		<< DamageFactor;
320 	if (SaveVersion >= 4516)
321 	{
322 		arc << DamageMultiply;
323 	}
324 	else
325 	{
326 		DamageMultiply = FRACUNIT;
327 	}
328 	arc << WeaveIndexXY << WeaveIndexZ
329 		<< PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner
330 		<< PoisonDamage << PoisonDuration << PoisonPeriod;
331 	if (SaveVersion >= 3235)
332 	{
333 		arc << PoisonDamageType << PoisonDamageTypeReceived;
334 	}
335 	arc << ConversationRoot << Conversation;
336 	if (SaveVersion >= 4509)
337 	{
338 		arc << FriendPlayer;
339 	}
340 	if (SaveVersion >= 4517)
341 	{
342 		arc << TeleFogSourceType
343 			<< TeleFogDestType;
344 	}
345 	if (SaveVersion >= 4518)
346 	{
347 		arc << RipperLevel
348 			<< RipLevelMin
349 			<< RipLevelMax;
350 	}
351 
352 	{
353 		FString tagstr;
354 		if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag;
355 		arc << tagstr;
356 		if (arc.IsLoading())
357 		{
358 			if (tagstr.Len() == 0) Tag = NULL;
359 			else Tag = mStringPropertyData.Alloc(tagstr);
360 		}
361 	}
362 
363 	if (arc.IsLoading ())
364 	{
365 		touching_sectorlist = NULL;
366 		LinkToWorld (Sector);
367 		AddToHash ();
368 		SetShade (fillcolor);
369 		if (player)
370 		{
371 			if (playeringame[player - players] &&
372 				player->cls != NULL &&
373 				!(flags4 & MF4_NOSKIN) &&
374 				state->sprite == GetDefaultByType (player->cls)->SpawnState->sprite)
375 			{ // Give player back the skin
376 				sprite = skins[player->userinfo.GetSkin()].sprite;
377 			}
378 			if (Speed == 0)
379 			{
380 				Speed = GetDefault()->Speed;
381 			}
382 		}
383 		PrevX = X();
384 		PrevY = Y();
385 		PrevZ = Z();
386 		PrevAngle = angle;
387 		UpdateWaterLevel(Z(), false);
388 	}
389 }
390 
391 
AActor()392 AActor::AActor () throw()
393 {
394 }
395 
AActor(const AActor & other)396 AActor::AActor (const AActor &other) throw()
397 	: DThinker()
398 {
399 	memcpy (&snext, &other.snext, (BYTE *)&this[1] - (BYTE *)&snext);
400 }
401 
operator =(const AActor & other)402 AActor &AActor::operator= (const AActor &other)
403 {
404 	memcpy (&snext, &other.snext, (BYTE *)&this[1] - (BYTE *)&snext);
405 	return *this;
406 }
407 
408 //==========================================================================
409 //
410 // AActor::InStateSequence
411 //
412 // Checks whether the current state is in a contiguous sequence that
413 // starts with basestate
414 //
415 //==========================================================================
416 
InStateSequence(FState * newstate,FState * basestate)417 bool AActor::InStateSequence(FState * newstate, FState * basestate)
418 {
419 	if (basestate == NULL) return false;
420 
421 	FState * thisstate = basestate;
422 	do
423 	{
424 		if (newstate == thisstate) return true;
425 		basestate = thisstate;
426 		thisstate = thisstate->GetNextState();
427 	}
428 	while (thisstate == basestate+1);
429 	return false;
430 }
431 
432 //==========================================================================
433 //
434 // AActor::GetTics
435 //
436 // Get the actual duration of the next state
437 // We are using a state flag now to indicate a state that should be
438 // accelerated in Fast mode or slowed in Slow mode.
439 //
440 //==========================================================================
441 
GetTics(FState * newstate)442 int AActor::GetTics(FState * newstate)
443 {
444 	int tics = newstate->GetTics();
445 	if (isFast() && newstate->Fast)
446 	{
447 		return tics - (tics>>1);
448 	}
449 	else if (isSlow() && newstate->Slow)
450 	{
451 		return tics<<1;
452 	}
453 	return tics;
454 }
455 
456 //==========================================================================
457 //
458 // AActor::SetState
459 //
460 // Returns true if the mobj is still present.
461 //
462 //==========================================================================
463 
SetState(FState * newstate,bool nofunction)464 bool AActor::SetState (FState *newstate, bool nofunction)
465 {
466 	if (debugfile && player && (player->cheats & CF_PREDICTING))
467 		fprintf (debugfile, "for pl %td: SetState while predicting!\n", player-players);
468 	do
469 	{
470 		if (newstate == NULL)
471 		{
472 			state = NULL;
473 			Destroy ();
474 			return false;
475 		}
476 		int prevsprite, newsprite;
477 
478 		if (state != NULL)
479 		{
480 			prevsprite = state->sprite;
481 		}
482 		else
483 		{
484 			prevsprite = -1;
485 		}
486 		state = newstate;
487 		tics = GetTics(newstate);
488 		renderflags = (renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (newstate->GetFullbright());
489 		newsprite = newstate->sprite;
490 		if (newsprite != SPR_FIXED)
491 		{ // okay to change sprite and/or frame
492 			if (!newstate->GetSameFrame())
493 			{ // okay to change frame
494 				frame = newstate->GetFrame();
495 			}
496 			if (newsprite != SPR_NOCHANGE)
497 			{ // okay to change sprite
498 				if (!(flags4 & MF4_NOSKIN) && newsprite == SpawnState->sprite)
499 				{ // [RH] If the new sprite is the same as the original sprite, and
500 				// this actor is attached to a player, use the player's skin's
501 				// sprite. If a player is not attached, do not change the sprite
502 				// unless it is different from the previous state's sprite; a
503 				// player may have been attached, died, and respawned elsewhere,
504 				// and we do not want to lose the skin on the body. If it wasn't
505 				// for Dehacked, I would move sprite changing out of the states
506 				// altogether, since actors rarely change their sprites after
507 				// spawning.
508 					if (player != NULL && skins != NULL)
509 					{
510 						sprite = skins[player->userinfo.GetSkin()].sprite;
511 					}
512 					else if (newsprite != prevsprite)
513 					{
514 						sprite = newsprite;
515 					}
516 				}
517 				else
518 				{
519 					sprite = newsprite;
520 				}
521 			}
522 		}
523 
524 		if (!nofunction && newstate->CallAction(this, this))
525 		{
526 			// Check whether the called action function resulted in destroying the actor
527 			if (ObjectFlags & OF_EuthanizeMe)
528 				return false;
529 		}
530 		newstate = newstate->GetNextState();
531 	} while (tics == 0);
532 
533 	if (Renderer != NULL)
534 	{
535 		Renderer->StateChanged(this);
536 	}
537 	return true;
538 }
539 
540 //============================================================================
541 //
542 // AActor :: AddInventory
543 //
544 //============================================================================
545 
AddInventory(AInventory * item)546 void AActor::AddInventory (AInventory *item)
547 {
548 	// Check if it's already attached to an actor
549 	if (item->Owner != NULL)
550 	{
551 		// Is it attached to us?
552 		if (item->Owner == this)
553 			return;
554 
555 		// No, then remove it from the other actor first
556 		item->Owner->RemoveInventory (item);
557 	}
558 
559 	item->Owner = this;
560 	item->Inventory = Inventory;
561 	Inventory = item;
562 
563 	// Each item receives an unique ID when added to an actor's inventory.
564 	// This is used by the DEM_INVUSE command to identify the item. Simply
565 	// using the item's position in the list won't work, because ticcmds get
566 	// run sometime in the future, so by the time it runs, the inventory
567 	// might not be in the same state as it was when DEM_INVUSE was sent.
568 	Inventory->InventoryID = InventoryID++;
569 }
570 
571 //============================================================================
572 //
573 // AActor :: RemoveInventory
574 //
575 //============================================================================
576 
RemoveInventory(AInventory * item)577 void AActor::RemoveInventory(AInventory *item)
578 {
579 	AInventory *inv, **invp;
580 
581 	if (item != NULL && item->Owner != NULL)	// can happen if the owner was destroyed by some action from an item's use state.
582 	{
583 		invp = &item->Owner->Inventory;
584 		for (inv = *invp; inv != NULL; invp = &inv->Inventory, inv = *invp)
585 		{
586 			if (inv == item)
587 			{
588 				*invp = item->Inventory;
589 				item->DetachFromOwner();
590 				item->Owner = NULL;
591 				item->Inventory = NULL;
592 				break;
593 			}
594 		}
595 	}
596 }
597 
598 //============================================================================
599 //
600 // AActor :: TakeInventory
601 //
602 //============================================================================
603 
TakeInventory(const PClass * itemclass,int amount,bool fromdecorate,bool notakeinfinite)604 bool AActor::TakeInventory(const PClass *itemclass, int amount, bool fromdecorate, bool notakeinfinite)
605 {
606 	AInventory *item = FindInventory(itemclass);
607 
608 	if (item == NULL)
609 		return false;
610 
611 	if (!fromdecorate)
612 	{
613 		item->Amount -= amount;
614 		if (item->Amount <= 0)
615 		{
616 			item->DepleteOrDestroy();
617 		}
618 		// It won't be used in non-decorate context, so return false here
619 		return false;
620 	}
621 
622 	bool result = false;
623 	if (item->Amount > 0)
624 	{
625 		result = true;
626 	}
627 
628 	if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor)))
629 		return false;
630 
631 	// Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
632 	// and infinite ammo is on
633 	if (notakeinfinite &&
634 	((dmflags & DF_INFINITE_AMMO) || (player && player->cheats & CF_INFINITEAMMO)) &&
635 		item->IsKindOf(RUNTIME_CLASS(AAmmo)))
636 	{
637 		// Nothing to do here, except maybe res = false;? Would it make sense?
638 	}
639 	else if (!amount || amount>=item->Amount)
640 	{
641 		item->DepleteOrDestroy();
642 	}
643 	else item->Amount-=amount;
644 
645 	return result;
646 }
647 
648 
649 //============================================================================
650 //
651 // AActor :: DestroyAllInventory
652 //
653 //============================================================================
654 
DestroyAllInventory()655 void AActor::DestroyAllInventory ()
656 {
657 	while (Inventory != NULL)
658 	{
659 		AInventory *item = Inventory;
660 		item->Destroy ();
661 		assert (item != Inventory);
662 	}
663 }
664 
665 //============================================================================
666 //
667 // AActor :: FirstInv
668 //
669 // Returns the first item in this actor's inventory that has IF_INVBAR set.
670 //
671 //============================================================================
672 
FirstInv()673 AInventory *AActor::FirstInv ()
674 {
675 	if (Inventory == NULL)
676 	{
677 		return NULL;
678 	}
679 	if (Inventory->ItemFlags & IF_INVBAR)
680 	{
681 		return Inventory;
682 	}
683 	return Inventory->NextInv ();
684 }
685 
686 //============================================================================
687 //
688 // AActor :: UseInventory
689 //
690 // Attempts to use an item. If the use succeeds, one copy of the item is
691 // removed from the inventory. If all copies are removed, then the item is
692 // destroyed.
693 //
694 //============================================================================
695 
UseInventory(AInventory * item)696 bool AActor::UseInventory (AInventory *item)
697 {
698 	// No using items if you're dead.
699 	if (health <= 0)
700 	{
701 		return false;
702 	}
703 	// Don't use it if you don't actually have any of it.
704 	if (item->Amount <= 0 || (item->ObjectFlags & OF_EuthanizeMe))
705 	{
706 		return false;
707 	}
708 	if (!item->Use (false))
709 	{
710 		return false;
711 	}
712 
713 	if (dmflags2 & DF2_INFINITE_INVENTORY)
714 		return true;
715 
716 	if (--item->Amount <= 0)
717 	{
718 		item->DepleteOrDestroy ();
719 	}
720 	return true;
721 }
722 
723 //===========================================================================
724 //
725 // AActor :: DropInventory
726 //
727 // Removes a single copy of an item and throws it out in front of the actor.
728 //
729 //===========================================================================
730 
DropInventory(AInventory * item)731 AInventory *AActor::DropInventory (AInventory *item)
732 {
733 	angle_t an;
734 	AInventory *drop = item->CreateTossable ();
735 
736 	if (drop == NULL)
737 	{
738 		return NULL;
739 	}
740 	an = angle >> ANGLETOFINESHIFT;
741 	drop->SetOrigin(PosPlusZ(10*FRACUNIT), false);
742 	drop->angle = angle;
743 	drop->velx = velx + 5 * finecosine[an];
744 	drop->vely = vely + 5 * finesine[an];
745 	drop->velz = velz + FRACUNIT;
746 	drop->flags &= ~MF_NOGRAVITY;	// Don't float
747 	drop->ClearCounters();	// do not count for statistics again
748 	return drop;
749 }
750 
751 //============================================================================
752 //
753 // AActor :: FindInventory
754 //
755 //============================================================================
756 
FindInventory(const PClass * type,bool subclass)757 AInventory *AActor::FindInventory (const PClass *type, bool subclass)
758 {
759 	AInventory *item;
760 
761 	if (type == NULL) return NULL;
762 
763 	assert (type->ActorInfo != NULL);
764 	for (item = Inventory; item != NULL; item = item->Inventory)
765 	{
766 		if (!subclass)
767 		{
768 			if (item->GetClass() == type)
769 			{
770 				break;
771 			}
772 		}
773 		else
774 		{
775 			if (item->IsKindOf(type))
776 			{
777 				break;
778 			}
779 		}
780 	}
781 	return item;
782 }
783 
FindInventory(FName type)784 AInventory *AActor::FindInventory (FName type)
785 {
786 	return FindInventory(PClass::FindClass(type));
787 }
788 
789 //============================================================================
790 //
791 // AActor :: GiveInventoryType
792 //
793 //============================================================================
794 
GiveInventoryType(const PClass * type)795 AInventory *AActor::GiveInventoryType (const PClass *type)
796 {
797 	AInventory *item = NULL;
798 
799 	if (type != NULL)
800 	{
801 		item = static_cast<AInventory *>(Spawn (type, 0,0,0, NO_REPLACE));
802 		if (!item->CallTryPickup (this))
803 		{
804 			item->Destroy ();
805 			return NULL;
806 		}
807 	}
808 	return item;
809 }
810 
811 //============================================================================
812 //
813 // AActor :: GiveAmmo
814 //
815 // Returns true if the ammo was added, false if not.
816 //
817 //============================================================================
818 
GiveAmmo(const PClass * type,int amount)819 bool AActor::GiveAmmo (const PClass *type, int amount)
820 {
821 	if (type != NULL)
822 	{
823 		AInventory *item = static_cast<AInventory *>(Spawn (type, 0, 0, 0, NO_REPLACE));
824 		if (item)
825 		{
826 			item->Amount = amount;
827 			item->flags |= MF_DROPPED;
828 			if (!item->CallTryPickup (this))
829 			{
830 				item->Destroy ();
831 				return false;
832 			}
833 			return true;
834 		}
835 	}
836 	return false;
837 }
838 
839 //============================================================================
840 //
841 // AActor :: ClearInventory
842 //
843 // Clears the inventory of a single actor.
844 //
845 //============================================================================
846 
ClearInventory()847 void AActor::ClearInventory()
848 {
849 	// In case destroying an inventory item causes another to be destroyed
850 	// (e.g. Weapons destroy their sisters), keep track of the pointer to
851 	// the next inventory item rather than the next inventory item itself.
852 	// For example, if a weapon is immediately followed by its sister, the
853 	// next weapon we had tracked would be to the sister, so it is now
854 	// invalid and we won't be able to find the complete inventory by
855 	// following it.
856 	//
857 	// When we destroy an item, we leave invp alone, since the destruction
858 	// process will leave it pointing to the next item we want to check. If
859 	// we don't destroy an item, then we move invp to point to its Inventory
860 	// pointer.
861 	//
862 	// It should be safe to assume that an item being destroyed will only
863 	// destroy items further down in the chain, because if it was going to
864 	// destroy something we already processed, we've already destroyed it,
865 	// so it won't have anything to destroy.
866 
867 	AInventory **invp = &Inventory;
868 
869 	while (*invp != NULL)
870 	{
871 		AInventory *inv = *invp;
872 		if (!(inv->ItemFlags & IF_UNDROPPABLE))
873 		{
874 			// For the sake of undroppable weapons, never remove ammo once
875 			// it has been acquired; just set its amount to 0.
876 			if (inv->IsKindOf(RUNTIME_CLASS(AAmmo)))
877 			{
878 				AAmmo *ammo = static_cast<AAmmo*>(inv);
879 				ammo->Amount = 0;
880 				invp = &inv->Inventory;
881 			}
882 			else
883 			{
884 				inv->Destroy ();
885 			}
886 		}
887 		else if (inv->GetClass() == RUNTIME_CLASS(AHexenArmor))
888 		{
889 			AHexenArmor *harmor = static_cast<AHexenArmor *> (inv);
890 			harmor->Slots[3] = harmor->Slots[2] = harmor->Slots[1] = harmor->Slots[0] = 0;
891 			invp = &inv->Inventory;
892 		}
893 		else
894 		{
895 			invp = &inv->Inventory;
896 		}
897 	}
898 	if (player != NULL)
899 	{
900 		player->ReadyWeapon = NULL;
901 		player->PendingWeapon = WP_NOCHANGE;
902 		player->psprites[ps_weapon].state = NULL;
903 		player->psprites[ps_flash].state = NULL;
904 	}
905 }
906 
907 //============================================================================
908 //
909 // AActor :: CopyFriendliness
910 //
911 // Makes this actor hate (or like) the same things another actor does.
912 //
913 //============================================================================
914 
CopyFriendliness(AActor * other,bool changeTarget,bool resetHealth)915 void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth)
916 {
917 	level.total_monsters -= CountsAsKill();
918 	TIDtoHate = other->TIDtoHate;
919 	LastLookActor = other->LastLookActor;
920 	LastLookPlayerNumber = other->LastLookPlayerNumber;
921 	flags  = (flags & ~MF_FRIENDLY) | (other->flags & MF_FRIENDLY);
922 	flags3 = (flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (other->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS));
923 	flags4 = (flags4 & ~(MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED)) | (other->flags4 & (MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED));
924 	FriendPlayer = other->FriendPlayer;
925 	DesignatedTeam = other->DesignatedTeam;
926 	if (changeTarget && other->target != NULL && !(other->target->flags3 & MF3_NOTARGET) && !(other->target->flags7 & MF7_NEVERTARGET))
927 	{
928 		// LastHeard must be set as well so that A_Look can react to the new target if called
929 		LastHeard = target = other->target;
930 	}
931 	if (resetHealth) health = SpawnHealth();
932 	level.total_monsters += CountsAsKill();
933 }
934 
935 //============================================================================
936 //
937 // AActor :: ObtainInventory
938 //
939 // Removes the items from the other actor and puts them in this actor's
940 // inventory. The actor receiving the inventory must not have any items.
941 //
942 //============================================================================
943 
ObtainInventory(AActor * other)944 void AActor::ObtainInventory (AActor *other)
945 {
946 	assert (Inventory == NULL);
947 
948 	Inventory = other->Inventory;
949 	InventoryID = other->InventoryID;
950 	other->Inventory = NULL;
951 	other->InventoryID = 0;
952 
953 	if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn)) && this->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
954 	{
955 		APlayerPawn *you = static_cast<APlayerPawn *>(other);
956 		APlayerPawn *me = static_cast<APlayerPawn *>(this);
957 		me->InvFirst = you->InvFirst;
958 		me->InvSel = you->InvSel;
959 		you->InvFirst = NULL;
960 		you->InvSel = NULL;
961 	}
962 
963 	AInventory *item = Inventory;
964 	while (item != NULL)
965 	{
966 		item->Owner = this;
967 		item = item->Inventory;
968 	}
969 }
970 
971 //============================================================================
972 //
973 // AActor :: CheckLocalView
974 //
975 // Returns true if this actor is local for the player. Here, local means the
976 // player is either looking out this actor's eyes, or this actor is the player
977 // and the player is looking out the eyes of something non-"sentient."
978 //
979 //============================================================================
980 
CheckLocalView(int playernum) const981 bool AActor::CheckLocalView (int playernum) const
982 {
983 	if (players[playernum].camera == this)
984 	{
985 		return true;
986 	}
987 	if (players[playernum].mo != this || players[playernum].camera == NULL)
988 	{
989 		return false;
990 	}
991 	if (players[playernum].camera->player == NULL &&
992 		!(players[playernum].camera->flags3 & MF3_ISMONSTER))
993 	{
994 		return true;
995 	}
996 	return false;
997 }
998 
999 //============================================================================
1000 //
1001 // AActor :: IsVisibleToPlayer
1002 //
1003 // Returns true if this actor should be seen by the console player.
1004 //
1005 //============================================================================
1006 
IsVisibleToPlayer() const1007 bool AActor::IsVisibleToPlayer() const
1008 {
1009 	// [BB] Safety check. This should never be NULL. Nevertheless, we return true to leave the default ZDoom behavior unaltered.
1010 	if ( players[consoleplayer].camera == NULL )
1011 		return true;
1012 
1013 	if (VisibleToTeam != 0 && teamplay &&
1014 		(signed)(VisibleToTeam-1) != players[consoleplayer].userinfo.GetTeam())
1015 		return false;
1016 
1017 	const player_t* pPlayer = players[consoleplayer].camera->player;
1018 
1019 	if(pPlayer && pPlayer->mo && GetClass()->ActorInfo->VisibleToPlayerClass.Size() > 0)
1020 	{
1021 		bool visible = false;
1022 		for(unsigned int i = 0;i < GetClass()->ActorInfo->VisibleToPlayerClass.Size();++i)
1023 		{
1024 			const PClass *cls = GetClass()->ActorInfo->VisibleToPlayerClass[i];
1025 			if(cls && pPlayer->mo->GetClass()->IsDescendantOf(cls))
1026 			{
1027 				visible = true;
1028 				break;
1029 			}
1030 		}
1031 		if(!visible)
1032 			return false;
1033 	}
1034 
1035 	// [BB] Passed all checks.
1036 	return true;
1037 }
1038 
1039 //============================================================================
1040 //
1041 // AActor :: ConversationAnimation
1042 //
1043 // Plays a conversation-related animation:
1044 //	 0 = greeting
1045 //   1 = "yes"
1046 //   2 = "no"
1047 //
1048 //============================================================================
1049 
ConversationAnimation(int animnum)1050 void AActor::ConversationAnimation (int animnum)
1051 {
1052 	FState * state = NULL;
1053 	switch (animnum)
1054 	{
1055 	case 0:
1056 		state = FindState(NAME_Greetings);
1057 		break;
1058 	case 1:
1059 		state = FindState(NAME_Yes);
1060 		break;
1061 	case 2:
1062 		state = FindState(NAME_No);
1063 		break;
1064 	}
1065 	if (state != NULL) SetState(state);
1066 }
1067 
1068 //============================================================================
1069 //
1070 // AActor :: Touch
1071 //
1072 // Something just touched this actor. Normally used only for inventory items,
1073 // but some Strife monsters also use it.
1074 //
1075 //============================================================================
1076 
Touch(AActor * toucher)1077 void AActor::Touch (AActor *toucher)
1078 {
1079 }
1080 
1081 //============================================================================
1082 //
1083 // AActor :: Grind
1084 //
1085 // Handles the an actor being crushed by a door, crusher or polyobject.
1086 // Originally part of P_DoCrunch(), it has been made into its own actor
1087 // function so that it could be called from a polyobject without hassle.
1088 // Bool items is true if it should destroy() dropped items, false otherwise.
1089 //============================================================================
1090 
Grind(bool items)1091 bool AActor::Grind(bool items)
1092 {
1093 	// crunch bodies to giblets
1094 	if ((flags & MF_CORPSE) && !(flags3 & MF3_DONTGIB) && (health <= 0))
1095 	{
1096 		FState * state = FindState(NAME_Crush);
1097 
1098 		// In Heretic and Chex Quest we don't change the actor's sprite, just its size.
1099 		if (state == NULL && gameinfo.dontcrunchcorpses)
1100 		{
1101 			flags &= ~MF_SOLID;
1102 			flags3 |= MF3_DONTGIB;
1103 			height = radius = 0;
1104 			return false;
1105 		}
1106 
1107 		bool isgeneric = false;
1108 		// ZDoom behavior differs from standard as crushed corpses cannot be raised.
1109 		// The reason for the change was originally because of a problem with players,
1110 		// see rh_log entry for February 21, 1999. Don't know if it is still relevant.
1111 		if (state == NULL 									// Only use the default crushed state if:
1112 			&& !(flags & MF_NOBLOOD)						// 1. the monster bleeeds,
1113 			&& (i_compatflags & COMPATF_CORPSEGIBS)			// 2. the compat setting is on,
1114 			&& player == NULL)								// 3. and the thing isn't a player.
1115 		{
1116 			isgeneric = true;
1117 			state = FindState(NAME_GenericCrush);
1118 			if (state != NULL && (sprites[state->sprite].numframes <= 0))
1119 				state = NULL; // If one of these tests fails, do not use that state.
1120 		}
1121 		if (state != NULL && !(flags & MF_ICECORPSE))
1122 		{
1123 			if (this->flags4 & MF4_BOSSDEATH)
1124 			{
1125 				CALL_ACTION(A_BossDeath, this);
1126 			}
1127 			flags &= ~MF_SOLID;
1128 			flags3 |= MF3_DONTGIB;
1129 			height = radius = 0;
1130 			SetState (state);
1131 			if (isgeneric)	// Not a custom crush state, so colorize it appropriately.
1132 			{
1133 				S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
1134 				PalEntry bloodcolor = GetBloodColor();
1135 				if (bloodcolor!=0) Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
1136 			}
1137 			return false;
1138 		}
1139 		if (!(flags & MF_NOBLOOD))
1140 		{
1141 			if (this->flags4 & MF4_BOSSDEATH)
1142 			{
1143 				CALL_ACTION(A_BossDeath, this);
1144 			}
1145 
1146 			const PClass *i = PClass::FindClass("RealGibs");
1147 
1148 			if (i != NULL)
1149 			{
1150 				i = i->GetReplacement();
1151 
1152 				const AActor *defaults = GetDefaultByType (i);
1153 				if (defaults->SpawnState == NULL ||
1154 					sprites[defaults->SpawnState->sprite].numframes == 0)
1155 				{
1156 					i = NULL;
1157 				}
1158 			}
1159 			if (i == NULL)
1160 			{
1161 				// if there's no gib sprite don't crunch it.
1162 				flags &= ~MF_SOLID;
1163 				flags3 |= MF3_DONTGIB;
1164 				height = radius = 0;
1165 				return false;
1166 			}
1167 
1168 			AActor *gib = Spawn (i, Pos(), ALLOW_REPLACE);
1169 			if (gib != NULL)
1170 			{
1171 				gib->RenderStyle = RenderStyle;
1172 				gib->alpha = alpha;
1173 				gib->height = 0;
1174 				gib->radius = 0;
1175 
1176 				PalEntry bloodcolor = GetBloodColor();
1177 				if (bloodcolor != 0)
1178 					gib->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
1179 			}
1180 			S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
1181 		}
1182 		if (flags & MF_ICECORPSE)
1183 		{
1184 			tics = 1;
1185 			velx = vely = velz = 0;
1186 		}
1187 		else if (player)
1188 		{
1189 			flags |= MF_NOCLIP;
1190 			flags3 |= MF3_DONTGIB;
1191 			renderflags |= RF_INVISIBLE;
1192 		}
1193 		else
1194 		{
1195 			Destroy ();
1196 		}
1197 		return false;		// keep checking
1198 	}
1199 
1200 	// killough 11/98: kill touchy things immediately
1201 	if (flags6 & MF6_TOUCHY && (flags6 & MF6_ARMED || IsSentient()))
1202     {
1203 		flags6 &= ~MF6_ARMED; // Disarm
1204 		P_DamageMobj (this, NULL, NULL, health, NAME_Crush, DMG_FORCED);  // kill object
1205 		return true;   // keep checking
1206     }
1207 
1208 	if (!(flags & MF_SOLID) || (flags & MF_NOCLIP))
1209 	{
1210 		return false;
1211 	}
1212 
1213 	if (!(flags & MF_SHOOTABLE))
1214 	{
1215 		return false;		// assume it is bloody gibs or something
1216 	}
1217 	return true;
1218 }
1219 
1220 //============================================================================
1221 //
1222 // AActor :: Massacre
1223 //
1224 // Called by the massacre cheat to kill monsters. Returns true if the monster
1225 // was killed and false if it was already dead.
1226 //============================================================================
1227 
Massacre()1228 bool AActor::Massacre ()
1229 {
1230 	int prevhealth;
1231 
1232 	if (health > 0)
1233 	{
1234 		flags |= MF_SHOOTABLE;
1235 		flags2 &= ~(MF2_DORMANT|MF2_INVULNERABLE);
1236 		do
1237 		{
1238 			prevhealth = health;
1239 			P_DamageMobj (this, NULL, NULL, TELEFRAG_DAMAGE, NAME_Massacre);
1240 		}
1241 		while (health != prevhealth && health > 0);	//abort if the actor wasn't hurt.
1242 		return health <= 0;
1243 	}
1244 	return false;
1245 }
1246 
1247 //----------------------------------------------------------------------------
1248 //
1249 // PROC P_ExplodeMissile
1250 //
1251 //----------------------------------------------------------------------------
1252 
P_ExplodeMissile(AActor * mo,line_t * line,AActor * target)1253 void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target)
1254 {
1255 	if (mo->flags3 & MF3_EXPLOCOUNT)
1256 	{
1257 		if (++mo->special2 < mo->special1)
1258 		{
1259 			return;
1260 		}
1261 	}
1262 	mo->velx = mo->vely = mo->velz = 0;
1263 	mo->effects = 0;		// [RH]
1264 	mo->flags &= ~MF_SHOOTABLE;
1265 
1266 	FState *nextstate=NULL;
1267 
1268 	if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) )
1269 	{
1270 		if (mo->flags7 & MF7_HITTARGET)	mo->target = target;
1271 		if (mo->flags7 & MF7_HITMASTER)	mo->master = target;
1272 		if (mo->flags7 & MF7_HITTRACER)	mo->tracer = target;
1273 		if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash);
1274 		if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme);
1275 	}
1276 	if (nextstate == NULL) nextstate = mo->FindState(NAME_Death);
1277 
1278 	if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE))
1279 	{
1280 		// [RH] Don't explode missiles on horizon lines.
1281 		mo->Destroy ();
1282 		return;
1283 	}
1284 
1285 	if (line != NULL && cl_missiledecals)
1286 	{
1287 		fixedvec3 pos = mo->PosRelative(line);
1288 		int side = P_PointOnLineSidePrecise (pos.x, pos.y, line);
1289 		if (line->sidedef[side] == NULL)
1290 			side ^= 1;
1291 		if (line->sidedef[side] != NULL)
1292 		{
1293 			FDecalBase *base = mo->DecalGenerator;
1294 			if (base != NULL)
1295 			{
1296 				// Find the nearest point on the line, and stick a decal there
1297 				fixed_t x, y, z;
1298 				SQWORD num, den;
1299 
1300 				den = (SQWORD)line->dx*line->dx + (SQWORD)line->dy*line->dy;
1301 				if (den != 0)
1302 				{
1303 					SDWORD frac;
1304 
1305 					num = (SQWORD)(pos.x-line->v1->x)*line->dx+(SQWORD)(pos.y-line->v1->y)*line->dy;
1306 					if (num <= 0)
1307 					{
1308 						frac = 0;
1309 					}
1310 					else if (num >= den)
1311 					{
1312 						frac = 1<<30;
1313 					}
1314 					else
1315 					{
1316 						frac = (SDWORD)(num / (den>>30));
1317 					}
1318 
1319 					x = line->v1->x + MulScale30 (line->dx, frac);
1320 					y = line->v1->y + MulScale30 (line->dy, frac);
1321 					z = pos.z;
1322 
1323 					F3DFloor * ffloor=NULL;
1324 					if (line->sidedef[side^1] != NULL)
1325 					{
1326 						sector_t * backsector = line->sidedef[side^1]->sector;
1327 						extsector_t::xfloor &xf = backsector->e->XFloor;
1328 						// find a 3D-floor to stick to
1329 						for(unsigned int i=0;i<xf.ffloors.Size();i++)
1330 						{
1331 							F3DFloor * rover=xf.ffloors[i];
1332 
1333 							if ((rover->flags&(FF_EXISTS|FF_SOLID|FF_RENDERSIDES))==(FF_EXISTS|FF_SOLID|FF_RENDERSIDES))
1334 							{
1335 								if (z<=rover->top.plane->ZatPoint(x, y) && z>=rover->bottom.plane->ZatPoint( x, y))
1336 								{
1337 									ffloor=rover;
1338 									break;
1339 								}
1340 							}
1341 						}
1342 					}
1343 
1344 					DImpactDecal::StaticCreate (base->GetDecal (),
1345 						x, y, z, line->sidedef[side], ffloor);
1346 				}
1347 			}
1348 		}
1349 	}
1350 
1351 	// play the sound before changing the state, so that AActor::Destroy can call S_RelinkSounds on it and the death state can override it.
1352 	if (mo->DeathSound)
1353 	{
1354 		S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1,
1355 			(mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM);
1356 	}
1357 
1358 	mo->SetState (nextstate);
1359 	if (!(mo->ObjectFlags & OF_EuthanizeMe))
1360 	{
1361 		// The rest only applies if the missile actor still exists.
1362 		// [RH] Change render style of exploding rockets
1363 		if (mo->flags5 & MF5_DEHEXPLOSION)
1364 		{
1365 			if (deh.ExplosionStyle == 255)
1366 			{
1367 				if (addrocketexplosion)
1368 				{
1369 					mo->RenderStyle = STYLE_Add;
1370 					mo->alpha = FRACUNIT;
1371 				}
1372 				else
1373 				{
1374 					mo->RenderStyle = STYLE_Translucent;
1375 					mo->alpha = FRACUNIT*2/3;
1376 				}
1377 			}
1378 			else
1379 			{
1380 				mo->RenderStyle = ERenderStyle(deh.ExplosionStyle);
1381 				mo->alpha = deh.ExplosionAlpha;
1382 			}
1383 		}
1384 
1385 		if (mo->flags4 & MF4_RANDOMIZE)
1386 		{
1387 			mo->tics -= (pr_explodemissile() & 3) * TICRATE / 35;
1388 			if (mo->tics < 1)
1389 				mo->tics = 1;
1390 		}
1391 
1392 		mo->flags &= ~MF_MISSILE;
1393 
1394 	}
1395 }
1396 
1397 
PlayBounceSound(bool onfloor)1398 void AActor::PlayBounceSound(bool onfloor)
1399 {
1400 	if (!onfloor && (BounceFlags & BOUNCE_NoWallSound))
1401 	{
1402 		return;
1403 	}
1404 
1405 	if (!(BounceFlags & BOUNCE_Quiet))
1406 	{
1407 		if (BounceFlags & BOUNCE_UseSeeSound)
1408 		{
1409 			S_Sound (this, CHAN_VOICE, SeeSound, 1, ATTN_IDLE);
1410 		}
1411 		else if (onfloor || WallBounceSound <= 0)
1412 		{
1413 			S_Sound (this, CHAN_VOICE, BounceSound, 1, ATTN_IDLE);
1414 		}
1415 		else
1416 		{
1417 			S_Sound (this, CHAN_VOICE, WallBounceSound, 1, ATTN_IDLE);
1418 		}
1419 	}
1420 }
1421 
1422 //----------------------------------------------------------------------------
1423 //
1424 // PROC P_FloorBounceMissile
1425 //
1426 // Returns true if the missile was destroyed
1427 //----------------------------------------------------------------------------
1428 
FloorBounceMissile(secplane_t & plane)1429 bool AActor::FloorBounceMissile (secplane_t &plane)
1430 {
1431 	if (Z() <= floorz && P_HitFloor (this))
1432 	{
1433 		// Landed in some sort of liquid
1434 		if (BounceFlags & BOUNCE_ExplodeOnWater)
1435 		{
1436 			if (flags & MF_MISSILE)
1437 				P_ExplodeMissile(this, NULL, NULL);
1438 			else
1439 				Die(NULL, NULL);
1440 			return true;
1441 		}
1442 		if (!(BounceFlags & BOUNCE_CanBounceWater))
1443 		{
1444 			Destroy ();
1445 			return true;
1446 		}
1447 	}
1448 
1449 	if (plane.c < 0)
1450 	{ // on ceiling
1451 		if (!(BounceFlags & BOUNCE_Ceilings))
1452 			return true;
1453 	}
1454 	else
1455 	{ // on floor
1456 		if (!(BounceFlags & BOUNCE_Floors))
1457 			return true;
1458 	}
1459 
1460 	// The amount of bounces is limited
1461 	if (bouncecount>0 && --bouncecount==0)
1462 	{
1463 		if (flags & MF_MISSILE)
1464 			P_ExplodeMissile(this, NULL, NULL);
1465 		else
1466 			Die(NULL, NULL);
1467 		return true;
1468 	}
1469 
1470 	fixed_t dot = TMulScale16 (velx, plane.a, vely, plane.b, velz, plane.c);
1471 
1472 	if (BounceFlags & (BOUNCE_HereticType | BOUNCE_MBF))
1473 	{
1474 		velx -= MulScale15 (plane.a, dot);
1475 		vely -= MulScale15 (plane.b, dot);
1476 		velz -= MulScale15 (plane.c, dot);
1477 		angle = R_PointToAngle2 (0, 0, velx, vely);
1478 		if (!(BounceFlags & BOUNCE_MBF)) // Heretic projectiles die, MBF projectiles don't.
1479 		{
1480 			flags |= MF_INBOUNCE;
1481 			SetState (FindState(NAME_Death));
1482 			flags &= ~MF_INBOUNCE;
1483 			return false;
1484 		}
1485 		else velz = FixedMul(velz, bouncefactor);
1486 	}
1487 	else // Don't run through this for MBF-style bounces
1488 	{
1489 		// The reflected velocity keeps only about 70% of its original speed
1490 		velx = FixedMul (velx - MulScale15 (plane.a, dot), bouncefactor);
1491 		vely = FixedMul (vely - MulScale15 (plane.b, dot), bouncefactor);
1492 		velz = FixedMul (velz - MulScale15 (plane.c, dot), bouncefactor);
1493 		angle = R_PointToAngle2 (0, 0, velx, vely);
1494 	}
1495 
1496 	PlayBounceSound(true);
1497 
1498 	// Set bounce state
1499 	if (BounceFlags & BOUNCE_UseBounceState)
1500 	{
1501 		FName names[2];
1502 		FState *bouncestate;
1503 
1504 		names[0] = NAME_Bounce;
1505 		names[1] = plane.c < 0 ? NAME_Ceiling : NAME_Floor;
1506 		bouncestate = FindState(2, names);
1507 		if (bouncestate != NULL)
1508 		{
1509 			SetState(bouncestate);
1510 		}
1511 	}
1512 
1513 	if (BounceFlags & BOUNCE_MBF) // Bring it to rest below a certain speed
1514 	{
1515 		if (abs(velz) < (fixed_t)(Mass * GetGravity() / 64))
1516 			velz = 0;
1517 	}
1518 	else if (BounceFlags & (BOUNCE_AutoOff|BOUNCE_AutoOffFloorOnly))
1519 	{
1520 		if (plane.c > 0 || (BounceFlags & BOUNCE_AutoOff))
1521 		{
1522 			// AutoOff only works when bouncing off a floor, not a ceiling (or in compatibility mode.)
1523 			if (!(flags & MF_NOGRAVITY) && (velz < 3*FRACUNIT))
1524 				BounceFlags &= ~BOUNCE_TypeMask;
1525 		}
1526 	}
1527 	return false;
1528 }
1529 
1530 //----------------------------------------------------------------------------
1531 //
1532 // PROC P_ThrustMobj
1533 //
1534 //----------------------------------------------------------------------------
1535 
P_ThrustMobj(AActor * mo,angle_t angle,fixed_t move)1536 void P_ThrustMobj (AActor *mo, angle_t angle, fixed_t move)
1537 {
1538 	angle >>= ANGLETOFINESHIFT;
1539 	mo->velx += FixedMul (move, finecosine[angle]);
1540 	mo->vely += FixedMul (move, finesine[angle]);
1541 }
1542 
1543 //----------------------------------------------------------------------------
1544 //
1545 // FUNC P_FaceMobj
1546 //
1547 // Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs
1548 // to turn counter clockwise.  'delta' is set to the amount 'source'
1549 // needs to turn.
1550 //
1551 //----------------------------------------------------------------------------
1552 
P_FaceMobj(AActor * source,AActor * target,angle_t * delta)1553 int P_FaceMobj (AActor *source, AActor *target, angle_t *delta)
1554 {
1555 	angle_t diff;
1556 	angle_t angle1;
1557 	angle_t angle2;
1558 
1559 	angle1 = source->angle;
1560 	angle2 = source->AngleTo(target);
1561 	if (angle2 > angle1)
1562 	{
1563 		diff = angle2 - angle1;
1564 		if (diff > ANGLE_180)
1565 		{
1566 			*delta = ANGLE_MAX - diff;
1567 			return 0;
1568 		}
1569 		else
1570 		{
1571 			*delta = diff;
1572 			return 1;
1573 		}
1574 	}
1575 	else
1576 	{
1577 		diff = angle1 - angle2;
1578 		if (diff > ANGLE_180)
1579 		{
1580 			*delta = ANGLE_MAX - diff;
1581 			return 1;
1582 		}
1583 		else
1584 		{
1585 			*delta = diff;
1586 			return 0;
1587 		}
1588 	}
1589 }
1590 
1591 //----------------------------------------------------------------------------
1592 //
1593 // CanSeek
1594 //
1595 // Checks if a seeker missile can home in on its target
1596 //
1597 //----------------------------------------------------------------------------
1598 
CanSeek(AActor * target) const1599 bool AActor::CanSeek(AActor *target) const
1600 {
1601 	if (target->flags5 & MF5_CANTSEEK) return false;
1602 	if ((flags2 & MF2_DONTSEEKINVISIBLE) &&
1603 		((target->flags & MF_SHADOW) ||
1604 		 (target->renderflags & RF_INVISIBLE) ||
1605 		 !target->RenderStyle.IsVisible(target->alpha)
1606 		)
1607 	   ) return false;
1608 	return true;
1609 }
1610 
1611 //----------------------------------------------------------------------------
1612 //
1613 // FUNC P_SeekerMissile
1614 //
1615 // The missile's tracer field must be the target.  Returns true if
1616 // target was tracked, false if not.
1617 //
1618 //----------------------------------------------------------------------------
1619 
P_SeekerMissile(AActor * actor,angle_t thresh,angle_t turnMax,bool precise,bool usecurspeed)1620 bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax, bool precise, bool usecurspeed)
1621 {
1622 	int dir;
1623 	int dist;
1624 	angle_t delta;
1625 	angle_t angle;
1626 	AActor *target;
1627 	fixed_t speed;
1628 
1629 	speed = !usecurspeed ? actor->Speed : xs_CRoundToInt(TVector3<double>(actor->velx, actor->vely, actor->velz).Length());
1630 	target = actor->tracer;
1631 	if (target == NULL || !actor->CanSeek(target))
1632 	{
1633 		return false;
1634 	}
1635 	if (!(target->flags & MF_SHOOTABLE))
1636 	{ // Target died
1637 		actor->tracer = NULL;
1638 		return false;
1639 	}
1640 	if (speed == 0)
1641 	{ // Technically, we're not seeking since our speed is 0, but the target *is* seekable.
1642 		return true;
1643 	}
1644 	dir = P_FaceMobj (actor, target, &delta);
1645 	if (delta > thresh)
1646 	{
1647 		delta >>= 1;
1648 		if (delta > turnMax)
1649 		{
1650 			delta = turnMax;
1651 		}
1652 	}
1653 	if (dir)
1654 	{ // Turn clockwise
1655 		actor->angle += delta;
1656 	}
1657 	else
1658 	{ // Turn counter clockwise
1659 		actor->angle -= delta;
1660 	}
1661 	angle = actor->angle>>ANGLETOFINESHIFT;
1662 
1663 	if (!precise)
1664 	{
1665 		actor->velx = FixedMul (speed, finecosine[angle]);
1666 		actor->vely = FixedMul (speed, finesine[angle]);
1667 
1668 		if (!(actor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)))
1669 		{
1670 			if (actor->Top() < target->Z() ||
1671 				target->Top() < actor->Z())
1672 			{ // Need to seek vertically
1673 				dist = actor->AproxDistance (target) / speed;
1674 				if (dist < 1)
1675 				{
1676 					dist = 1;
1677 				}
1678 				actor->velz = ((target->Z() + target->height / 2) - (actor->Z() + actor->height / 2)) / dist;
1679 			}
1680 		}
1681 	}
1682 	else
1683 	{
1684 		angle_t pitch = 0;
1685 		if (!(actor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)))
1686 		{ // Need to seek vertically
1687 			fixedvec2 vec = actor->Vec2To(target);
1688 			double dist = MAX(1.0, TVector2<double>(vec.x, vec.y).Length());
1689 			// Aim at a player's eyes and at the middle of the actor for everything else.
1690 			fixed_t aimheight = target->height/2;
1691 			if (target->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
1692 			{
1693 				aimheight = static_cast<APlayerPawn *>(target)->ViewHeight;
1694 			}
1695 			pitch = R_PointToAngle2(0, actor->Z() + actor->height/2, xs_CRoundToInt(dist), target->Z() + aimheight);
1696 			pitch >>= ANGLETOFINESHIFT;
1697 		}
1698 
1699 		fixed_t xyscale = FixedMul(speed, finecosine[pitch]);
1700 		actor->velz = FixedMul(speed, finesine[pitch]);
1701 		actor->velx = FixedMul(xyscale, finecosine[angle]);
1702 		actor->vely = FixedMul(xyscale, finesine[angle]);
1703 	}
1704 
1705 	return true;
1706 }
1707 
1708 
1709 //
1710 // P_XYMovement
1711 //
1712 // Returns the actor's old floorz.
1713 //
1714 #define STOPSPEED			0x1000
1715 #define CARRYSTOPSPEED		(STOPSPEED*32/3)
1716 
P_XYMovement(AActor * mo,fixed_t scrollx,fixed_t scrolly)1717 fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
1718 {
1719 	static int pushtime = 0;
1720 	bool bForceSlide = scrollx || scrolly;
1721 	angle_t angle;
1722 	fixed_t ptryx, ptryy;
1723 	player_t *player;
1724 	fixed_t xmove, ymove;
1725 	const secplane_t * walkplane;
1726 	static const int windTab[3] = {2048*5, 2048*10, 2048*25};
1727 	int steps, step, totalsteps;
1728 	fixed_t startx, starty;
1729 	fixed_t oldfloorz = mo->floorz;
1730 	fixed_t oldz = mo->Z();
1731 
1732 	fixed_t maxmove = (mo->waterlevel < 1) || (mo->flags & MF_MISSILE) ||
1733 					  (mo->player && mo->player->crouchoffset<-10*FRACUNIT) ? MAXMOVE : MAXMOVE/4;
1734 
1735 	if (mo->flags2 & MF2_WINDTHRUST && mo->waterlevel < 2 && !(mo->flags & MF_NOCLIP))
1736 	{
1737 		int special = mo->Sector->special;
1738 		switch (special)
1739 		{
1740 			case 40: case 41: case 42: // Wind_East
1741 				P_ThrustMobj (mo, 0, windTab[special-40]);
1742 				break;
1743 			case 43: case 44: case 45: // Wind_North
1744 				P_ThrustMobj (mo, ANG90, windTab[special-43]);
1745 				break;
1746 			case 46: case 47: case 48: // Wind_South
1747 				P_ThrustMobj (mo, ANG270, windTab[special-46]);
1748 				break;
1749 			case 49: case 50: case 51: // Wind_West
1750 				P_ThrustMobj (mo, ANG180, windTab[special-49]);
1751 				break;
1752 		}
1753 	}
1754 
1755 	// [RH] No need to clamp these now. However, wall running needs it so
1756 	// that large thrusts can't propel an actor through a wall, because wall
1757 	// running depends on the player's original movement continuing even after
1758 	// it gets blocked.
1759 	if ((mo->player != NULL && (i_compatflags & COMPATF_WALLRUN)) || (mo->waterlevel >= 1) ||
1760 		(mo->player != NULL && mo->player->crouchfactor < FRACUNIT*3/4))
1761 	{
1762 		// preserve the direction instead of clamping x and y independently.
1763 		xmove = clamp (mo->velx, -maxmove, maxmove);
1764 		ymove = clamp (mo->vely, -maxmove, maxmove);
1765 
1766 		fixed_t xfac = FixedDiv(xmove, mo->velx);
1767 		fixed_t yfac = FixedDiv(ymove, mo->vely);
1768 		fixed_t fac = MIN(xfac, yfac);
1769 
1770 		xmove = mo->velx = FixedMul(mo->velx, fac);
1771 		ymove = mo->vely = FixedMul(mo->vely, fac);
1772 	}
1773 	else
1774 	{
1775 		xmove = mo->velx;
1776 		ymove = mo->vely;
1777 	}
1778 	// [RH] Carrying sectors didn't work with low speeds in BOOM. This is
1779 	// because BOOM relied on the speed being fast enough to accumulate
1780 	// despite friction. If the speed is too low, then its movement will get
1781 	// cancelled, and it won't accumulate to the desired speed.
1782 	mo->flags4 &= ~MF4_SCROLLMOVE;
1783 	if (abs(scrollx) > CARRYSTOPSPEED)
1784 	{
1785 		scrollx = FixedMul (scrollx, CARRYFACTOR);
1786 		mo->velx += scrollx;
1787 		mo->flags4 |= MF4_SCROLLMOVE;
1788 	}
1789 	if (abs(scrolly) > CARRYSTOPSPEED)
1790 	{
1791 		scrolly = FixedMul (scrolly, CARRYFACTOR);
1792 		mo->vely += scrolly;
1793 		mo->flags4 |= MF4_SCROLLMOVE;
1794 	}
1795 	xmove += scrollx;
1796 	ymove += scrolly;
1797 
1798 	if ((xmove | ymove) == 0)
1799 	{
1800 		if (mo->flags & MF_SKULLFLY)
1801 		{
1802 			// the skull slammed into something
1803 			mo->flags &= ~MF_SKULLFLY;
1804 			mo->velx = mo->vely = mo->velz = 0;
1805 			if (!(mo->flags2 & MF2_DORMANT))
1806 			{
1807 				if (mo->SeeState != NULL) mo->SetState (mo->SeeState);
1808 				else mo->SetIdle();
1809 			}
1810 			else
1811 			{
1812 				mo->SetIdle();
1813 				mo->tics = -1;
1814 			}
1815 		}
1816 		return oldfloorz;
1817 	}
1818 
1819 	player = mo->player;
1820 
1821 	// [RH] Adjust player movement on sloped floors
1822 	fixed_t startxmove = xmove;
1823 	fixed_t startymove = ymove;
1824 	walkplane = P_CheckSlopeWalk (mo, xmove, ymove);
1825 
1826 	// [RH] Take smaller steps when moving faster than the object's size permits.
1827 	// Moving as fast as the object's "diameter" is bad because it could skip
1828 	// some lines because the actor could land such that it is just touching the
1829 	// line. For Doom to detect that the line is there, it needs to actually cut
1830 	// through the actor.
1831 
1832 	{
1833 		maxmove = mo->radius - FRACUNIT;
1834 
1835 		if (maxmove <= 0)
1836 		{ // gibs can have radius 0, so don't divide by zero below!
1837 			maxmove = MAXMOVE;
1838 		}
1839 
1840 		const fixed_t xspeed = abs (xmove);
1841 		const fixed_t yspeed = abs (ymove);
1842 
1843 		steps = 1;
1844 
1845 		if (xspeed > yspeed)
1846 		{
1847 			if (xspeed > maxmove)
1848 			{
1849 				steps = 1 + xspeed / maxmove;
1850 			}
1851 		}
1852 		else
1853 		{
1854 			if (yspeed > maxmove)
1855 			{
1856 				steps = 1 + yspeed / maxmove;
1857 			}
1858 		}
1859 	}
1860 
1861 	// P_SlideMove needs to know the step size before P_CheckSlopeWalk
1862 	// because it also calls P_CheckSlopeWalk on its clipped steps.
1863 	fixed_t onestepx = startxmove / steps;
1864 	fixed_t onestepy = startymove / steps;
1865 
1866 	startx = mo->X();
1867 	starty = mo->Y();
1868 	step = 1;
1869 	totalsteps = steps;
1870 
1871 	// [RH] Instead of doing ripping damage each step, do it each tic.
1872 	// This makes it compatible with Heretic and Hexen, which only did
1873 	// one step for their missiles with ripping damage (excluding those
1874 	// that don't use P_XYMovement). It's also more intuitive since it
1875 	// makes the damage done dependant on the amount of time the projectile
1876 	// spends inside a target rather than on the projectile's size. The
1877 	// last actor ripped through is recorded so that if the projectile
1878 	// passes through more than one actor this tic, each one takes damage
1879 	// and not just the first one.
1880 	pushtime++;
1881 
1882 	FCheckPosition tm(!!(mo->flags2 & MF2_RIP));
1883 
1884 
1885 	do
1886 	{
1887 		if (i_compatflags & COMPATF_WALLRUN) pushtime++;
1888 		tm.PushTime = pushtime;
1889 
1890 		ptryx = startx + Scale (xmove, step, steps);
1891 		ptryy = starty + Scale (ymove, step, steps);
1892 
1893 /*		if (mo->player)
1894 		Printf ("%d,%d/%d: %d %d %d %d %d %d %d\n", level.time, step, steps, startxmove, Scale(xmove,step,steps), startymove, Scale(ymove,step,steps), mo->x, mo->y, mo->z);
1895 */
1896 		// [RH] If walking on a slope, stay on the slope
1897 		// killough 3/15/98: Allow objects to drop off
1898 		fixed_t startvelx = mo->velx, startvely = mo->vely;
1899 
1900 		if (!P_TryMove (mo, ptryx, ptryy, true, walkplane, tm))
1901 		{
1902 			// blocked move
1903 			AActor *BlockingMobj = mo->BlockingMobj;
1904 			line_t *BlockingLine = mo->BlockingLine;
1905 
1906 			if (!(mo->flags & MF_MISSILE) && (mo->BounceFlags & BOUNCE_MBF)
1907 				&& (BlockingMobj != NULL ? P_BounceActor(mo, BlockingMobj, false) : P_BounceWall(mo)))
1908 			{
1909 				// Do nothing, relevant actions already done in the condition.
1910 				// This allows to avoid setting velocities to 0 in the final else of this series.
1911 			}
1912 			else if ((mo->flags2 & (MF2_SLIDE|MF2_BLASTED) || bForceSlide) && !(mo->flags&MF_MISSILE))
1913 			{	// try to slide along it
1914 				if (BlockingMobj == NULL)
1915 				{ // slide against wall
1916 					if (BlockingLine != NULL &&
1917 						mo->player && mo->waterlevel && mo->waterlevel < 3 &&
1918 						(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove) &&
1919 						mo->BlockingLine->sidedef[1] != NULL)
1920 					{
1921 						mo->velz = WATER_JUMP_SPEED;
1922 					}
1923 					// If the blocked move executed any push specials that changed the
1924 					// actor's velocity, do not attempt to slide.
1925 					if (mo->velx == startvelx && mo->vely == startvely)
1926 					{
1927 						if (player && (i_compatflags & COMPATF_WALLRUN))
1928 						{
1929 						// [RH] Here is the key to wall running: The move is clipped using its full speed.
1930 						// If the move is done a second time (because it was too fast for one move), it
1931 						// is still clipped against the wall at its full speed, so you effectively
1932 						// execute two moves in one tic.
1933 							P_SlideMove (mo, mo->velx, mo->vely, 1);
1934 						}
1935 						else
1936 						{
1937 							P_SlideMove (mo, onestepx, onestepy, totalsteps);
1938 						}
1939 						if ((mo->velx | mo->vely) == 0)
1940 						{
1941 							steps = 0;
1942 						}
1943 						else
1944 						{
1945 							if (!player || !(i_compatflags & COMPATF_WALLRUN))
1946 							{
1947 								xmove = mo->velx;
1948 								ymove = mo->vely;
1949 								onestepx = xmove / steps;
1950 								onestepy = ymove / steps;
1951 								P_CheckSlopeWalk (mo, xmove, ymove);
1952 							}
1953 							startx = mo->X() - Scale (xmove, step, steps);
1954 							starty = mo->Y() - Scale (ymove, step, steps);
1955 						}
1956 					}
1957 					else
1958 					{
1959 						steps = 0;
1960 					}
1961 				}
1962 				else
1963 				{ // slide against another actor
1964 					fixed_t tx, ty;
1965 					tx = 0, ty = onestepy;
1966 					walkplane = P_CheckSlopeWalk (mo, tx, ty);
1967 					if (P_TryMove (mo, mo->X() + tx, mo->Y() + ty, true, walkplane, tm))
1968 					{
1969 						mo->velx = 0;
1970 					}
1971 					else
1972 					{
1973 						tx = onestepx, ty = 0;
1974 						walkplane = P_CheckSlopeWalk (mo, tx, ty);
1975 						if (P_TryMove (mo, mo->X() + tx, mo->Y() + ty, true, walkplane, tm))
1976 						{
1977 							mo->vely = 0;
1978 						}
1979 						else
1980 						{
1981 							mo->velx = mo->vely = 0;
1982 						}
1983 					}
1984 					if (player && player->mo == mo)
1985 					{
1986 						if (mo->velx == 0)
1987 							player->velx = 0;
1988 						if (mo->vely == 0)
1989 							player->vely = 0;
1990 					}
1991 					steps = 0;
1992 				}
1993 			}
1994 			else if (mo->flags & MF_MISSILE)
1995 			{
1996 				steps = 0;
1997 				if (BlockingMobj)
1998 				{
1999 					if (mo->BounceFlags & BOUNCE_Actors)
2000 					{
2001 						// Bounce test and code moved to P_BounceActor
2002 						if (!P_BounceActor(mo, BlockingMobj, false))
2003 						{	// Struck a player/creature
2004 							P_ExplodeMissile (mo, NULL, BlockingMobj);
2005 						}
2006 						return oldfloorz;
2007 					}
2008 				}
2009 				else
2010 				{
2011 					// Struck a wall
2012 					if (P_BounceWall (mo))
2013 					{
2014 						mo->PlayBounceSound(false);
2015 						return oldfloorz;
2016 					}
2017 				}
2018 				if (BlockingMobj && (BlockingMobj->flags2 & MF2_REFLECTIVE))
2019 				{
2020 					bool seeker = (mo->flags2 & MF2_SEEKERMISSILE) ? true : false;
2021 					// Don't change the angle if there's THRUREFLECT on the monster.
2022 					if (!(BlockingMobj->flags7 & MF7_THRUREFLECT))
2023 					{
2024 						angle = BlockingMobj->AngleTo(mo);
2025 						bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle));
2026 						// Change angle for deflection/reflection
2027 
2028 						if (!dontReflect)
2029 						{
2030 							bool tg = (mo->target != NULL);
2031 							bool blockingtg = (BlockingMobj->target != NULL);
2032 							if ((BlockingMobj->flags7 & MF7_AIMREFLECT) && (tg | blockingtg))
2033 							{
2034 								AActor *origin = tg ? mo->target : BlockingMobj->target;
2035 
2036 								float speed = (float)(mo->Speed);
2037 								//dest->x - source->x
2038 								fixedvec3 vect = mo->Vec3To(origin);
2039 								vect.z += origin->height / 2;
2040 								TVector3<double> velocity(vect.x, vect.y, vect.z);
2041 								velocity.Resize(speed);
2042 								mo->velx = (fixed_t)(velocity.X);
2043 								mo->vely = (fixed_t)(velocity.Y);
2044 								mo->velz = (fixed_t)(velocity.Z);
2045 							}
2046 							else
2047 							{
2048 								if ((BlockingMobj->flags7 & MF7_MIRRORREFLECT) && (tg | blockingtg))
2049 								{
2050 									mo->angle += ANGLE_180;
2051 									mo->velx = -mo->velx / 2;
2052 									mo->vely = -mo->vely / 2;
2053 									mo->velz = -mo->velz / 2;
2054 								}
2055 								else
2056 								{
2057 									mo->angle = angle;
2058 									angle >>= ANGLETOFINESHIFT;
2059 									mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]);
2060 									mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]);
2061 									mo->velz = -mo->velz / 2;
2062 								}
2063 							}
2064 						}
2065 						else
2066 						{
2067 							goto explode;
2068 						}
2069 					}
2070 					if (mo->flags2 & MF2_SEEKERMISSILE)
2071 					{
2072 						mo->tracer = mo->target;
2073 					}
2074 					mo->target = BlockingMobj;
2075 					return oldfloorz;
2076 				}
2077 explode:
2078 				// explode a missile
2079 				if (!(mo->flags3 & MF3_SKYEXPLODE))
2080 				{
2081 					if (tm.ceilingline &&
2082 						tm.ceilingline->backsector &&
2083 						tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
2084 						mo->Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(mo))
2085 					{
2086 						// Hack to prevent missiles exploding against the sky.
2087 						// Does not handle sky floors.
2088 						mo->Destroy ();
2089 						return oldfloorz;
2090 					}
2091 					// [RH] Don't explode on horizon lines.
2092 					if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon)
2093 					{
2094 						mo->Destroy ();
2095 						return oldfloorz;
2096 					}
2097 				}
2098 				P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj);
2099 				return oldfloorz;
2100 			}
2101 			else
2102 			{
2103 				mo->velx = mo->vely = 0;
2104 				steps = 0;
2105 			}
2106 		}
2107 		else
2108 		{
2109 			if (mo->X() != ptryx || mo->Y() != ptryy)
2110 			{
2111 				// If the new position does not match the desired position, the player
2112 				// must have gone through a teleporter, so stop moving right now if it
2113 				// was a regular teleporter. If it was a line-to-line or fogless teleporter,
2114 				// the move should continue, but startx and starty need to change.
2115 				if (mo->velx == 0 && mo->vely == 0)
2116 				{
2117 					step = steps;
2118 				}
2119 				else
2120 				{
2121 					startx = mo->X() - Scale (xmove, step, steps);
2122 					starty = mo->Y() - Scale (ymove, step, steps);
2123 				}
2124 			}
2125 		}
2126 	} while (++step <= steps);
2127 
2128 	// Friction
2129 
2130 	if (player && player->mo == mo && player->cheats & CF_NOVELOCITY)
2131 	{ // debug option for no sliding at all
2132 		mo->velx = mo->vely = 0;
2133 		player->velx = player->vely = 0;
2134 		return oldfloorz;
2135 	}
2136 
2137 	if (mo->flags & (MF_MISSILE | MF_SKULLFLY))
2138 	{ // no friction for missiles
2139 		return oldfloorz;
2140 	}
2141 
2142 	if (mo->Z() > mo->floorz && !(mo->flags2 & MF2_ONMOBJ) &&
2143 		!mo->IsNoClip2() &&
2144 		(!(mo->flags2 & MF2_FLY) || !(mo->flags & MF_NOGRAVITY)) &&
2145 		!mo->waterlevel)
2146 	{ // [RH] Friction when falling is available for larger aircontrols
2147 		if (player != NULL && level.airfriction != FRACUNIT)
2148 		{
2149 			mo->velx = FixedMul (mo->velx, level.airfriction);
2150 			mo->vely = FixedMul (mo->vely, level.airfriction);
2151 
2152 			if (player->mo == mo)		//  Not voodoo dolls
2153 			{
2154 				player->velx = FixedMul (player->velx, level.airfriction);
2155 				player->vely = FixedMul (player->vely, level.airfriction);
2156 			}
2157 		}
2158 		return oldfloorz;
2159 	}
2160 
2161 	// killough 8/11/98: add bouncers
2162 	// killough 9/15/98: add objects falling off ledges
2163 	// killough 11/98: only include bouncers hanging off ledges
2164 	if ((mo->flags & MF_CORPSE) || (mo->BounceFlags & BOUNCE_MBF && mo->Z() > mo->dropoffz) || (mo->flags6 & MF6_FALLING))
2165 	{ // Don't stop sliding if halfway off a step with some velocity
2166 		if (mo->velx > FRACUNIT/4 || mo->velx < -FRACUNIT/4 || mo->vely > FRACUNIT/4 || mo->vely < -FRACUNIT/4)
2167 		{
2168 			if (mo->floorz > mo->Sector->floorplane.ZatPoint(mo))
2169 			{
2170 				if (mo->dropoffz != mo->floorz) // 3DMidtex or other special cases that must be excluded
2171 				{
2172 					unsigned i;
2173 					for(i=0;i<mo->Sector->e->XFloor.ffloors.Size();i++)
2174 					{
2175 						// Sliding around on 3D floors looks extremely bad so
2176 						// if the floor comes from one in the current sector stop sliding the corpse!
2177 						F3DFloor * rover=mo->Sector->e->XFloor.ffloors[i];
2178 						if (!(rover->flags&FF_EXISTS)) continue;
2179 						if (rover->flags&FF_SOLID && rover->top.plane->ZatPoint(mo) == mo->floorz) break;
2180 					}
2181 					if (i==mo->Sector->e->XFloor.ffloors.Size())
2182 						return oldfloorz;
2183 				}
2184 			}
2185 		}
2186 	}
2187 
2188 	// killough 11/98:
2189 	// Stop voodoo dolls that have come to rest, despite any
2190 	// moving corresponding player:
2191 	if (mo->velx > -STOPSPEED && mo->velx < STOPSPEED
2192 		&& mo->vely > -STOPSPEED && mo->vely < STOPSPEED
2193 		&& (!player || (player->mo != mo)
2194 			|| !(player->cmd.ucmd.forwardmove | player->cmd.ucmd.sidemove)))
2195 	{
2196 		// if in a walking frame, stop moving
2197 		// killough 10/98:
2198 		// Don't affect main player when voodoo dolls stop:
2199 		if (player && player->mo == mo && !(player->cheats & CF_PREDICTING))
2200 		{
2201 			player->mo->PlayIdle ();
2202 		}
2203 
2204 		mo->velx = mo->vely = 0;
2205 		mo->flags4 &= ~MF4_SCROLLMOVE;
2206 
2207 		// killough 10/98: kill any bobbing velocity too (except in voodoo dolls)
2208 		if (player && player->mo == mo)
2209 			player->velx = player->vely = 0;
2210 	}
2211 	else
2212 	{
2213 		// phares 3/17/98
2214 		// Friction will have been adjusted by friction thinkers for icy
2215 		// or muddy floors. Otherwise it was never touched and
2216 		// remained set at ORIG_FRICTION
2217 		//
2218 		// killough 8/28/98: removed inefficient thinker algorithm,
2219 		// instead using touching_sectorlist in P_GetFriction() to
2220 		// determine friction (and thus only when it is needed).
2221 		//
2222 		// killough 10/98: changed to work with new bobbing method.
2223 		// Reducing player velocity is no longer needed to reduce
2224 		// bobbing, so ice works much better now.
2225 
2226 		fixed_t friction = P_GetFriction (mo, NULL);
2227 
2228 		mo->velx = FixedMul (mo->velx, friction);
2229 		mo->vely = FixedMul (mo->vely, friction);
2230 
2231 		// killough 10/98: Always decrease player bobbing by ORIG_FRICTION.
2232 		// This prevents problems with bobbing on ice, where it was not being
2233 		// reduced fast enough, leading to all sorts of kludges being developed.
2234 
2235 		if (player && player->mo == mo)		//  Not voodoo dolls
2236 		{
2237 			player->velx = FixedMul (player->velx, ORIG_FRICTION);
2238 			player->vely = FixedMul (player->vely, ORIG_FRICTION);
2239 		}
2240 	}
2241 	return oldfloorz;
2242 }
2243 
2244 // Move this to p_inter ***
P_MonsterFallingDamage(AActor * mo)2245 void P_MonsterFallingDamage (AActor *mo)
2246 {
2247 	int damage;
2248 	int vel;
2249 
2250 	if (!(level.flags2 & LEVEL2_MONSTERFALLINGDAMAGE))
2251 		return;
2252 	if (mo->floorsector->Flags & SECF_NOFALLINGDAMAGE)
2253 		return;
2254 
2255 	vel = abs(mo->velz);
2256 	if (vel > 35*FRACUNIT)
2257 	{ // automatic death
2258 		damage = TELEFRAG_DAMAGE;
2259 	}
2260 	else
2261 	{
2262 		damage = ((vel - (23*FRACUNIT))*6)>>FRACBITS;
2263 	}
2264 	damage = TELEFRAG_DAMAGE;	// always kill 'em
2265 	P_DamageMobj (mo, NULL, NULL, damage, NAME_Falling);
2266 }
2267 
2268 //
2269 // P_ZMovement
2270 //
2271 
P_ZMovement(AActor * mo,fixed_t oldfloorz)2272 void P_ZMovement (AActor *mo, fixed_t oldfloorz)
2273 {
2274 	fixed_t dist;
2275 	fixed_t delta;
2276 	fixed_t oldz = mo->Z();
2277 	fixed_t grav = mo->GetGravity();
2278 
2279 //
2280 // check for smooth step up
2281 //
2282 	if (mo->player && mo->player->mo == mo && mo->Z() < mo->floorz)
2283 	{
2284 		mo->player->viewheight -= mo->floorz - mo->Z();
2285 		mo->player->deltaviewheight = mo->player->GetDeltaViewHeight();
2286 	}
2287 
2288 	mo->AddZ(mo->velz);
2289 
2290 //
2291 // apply gravity
2292 //
2293 	if (mo->Z() > mo->floorz && !(mo->flags & MF_NOGRAVITY))
2294 	{
2295 		fixed_t startvelz = mo->velz;
2296 
2297 		if (mo->waterlevel == 0 || (mo->player &&
2298 			!(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove)))
2299 		{
2300 			// [RH] Double gravity only if running off a ledge. Coming down from
2301 			// an upward thrust (e.g. a jump) should not double it.
2302 			if (mo->velz == 0 && oldfloorz > mo->floorz && mo->Z() == oldfloorz)
2303 			{
2304 				mo->velz -= grav + grav;
2305 			}
2306 			else
2307 			{
2308 				mo->velz -= grav;
2309 			}
2310 		}
2311 		if (mo->player == NULL)
2312 		{
2313 			if (mo->waterlevel >= 1)
2314 			{
2315 				fixed_t sinkspeed;
2316 
2317 				if ((mo->flags & MF_SPECIAL) && !(mo->flags3 & MF3_ISMONSTER))
2318 				{ // Pickup items don't sink if placed and drop slowly if dropped
2319 					sinkspeed = (mo->flags & MF_DROPPED) ? -WATER_SINK_SPEED / 8 : 0;
2320 				}
2321 				else
2322 				{
2323 					sinkspeed = -WATER_SINK_SPEED;
2324 
2325 					// If it's not a player, scale sinkspeed by its mass, with
2326 					// 100 being equivalent to a player.
2327 					if (mo->player == NULL)
2328 					{
2329 						sinkspeed = Scale(sinkspeed, clamp(mo->Mass, 1, 4000), 100);
2330 					}
2331 				}
2332 				if (mo->velz < sinkspeed)
2333 				{ // Dropping too fast, so slow down toward sinkspeed.
2334 					mo->velz -= MAX(sinkspeed*2, -FRACUNIT*8);
2335 					if (mo->velz > sinkspeed)
2336 					{
2337 						mo->velz = sinkspeed;
2338 					}
2339 				}
2340 				else if (mo->velz > sinkspeed)
2341 				{ // Dropping too slow/going up, so trend toward sinkspeed.
2342 					mo->velz = startvelz + MAX(sinkspeed/3, -FRACUNIT*8);
2343 					if (mo->velz < sinkspeed)
2344 					{
2345 						mo->velz = sinkspeed;
2346 					}
2347 				}
2348 			}
2349 		}
2350 		else
2351 		{
2352 			if (mo->waterlevel > 1)
2353 			{
2354 				fixed_t sinkspeed = -WATER_SINK_SPEED;
2355 
2356 				if (mo->velz < sinkspeed)
2357 				{
2358 					mo->velz = (startvelz < sinkspeed) ? startvelz : sinkspeed;
2359 				}
2360 				else
2361 				{
2362 					mo->velz = startvelz + ((mo->velz - startvelz) >>
2363 						(mo->waterlevel == 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR));
2364 				}
2365 			}
2366 		}
2367 	}
2368 
2369 	// Hexen compatibility handling for floatbobbing. Ugh...
2370 	// Hexen yanked all items to the floor, except those being spawned at map start in the air.
2371 	// Those were kept at their original height.
2372 	// Do this only if the item was actually spawned by the map above ground to avoid problems.
2373 	if (mo->special1 > 0 && (mo->flags2 & MF2_FLOATBOB) && (ib_compatflags & BCOMPATF_FLOATBOB))
2374 	{
2375 		mo->SetZ(mo->floorz + mo->special1);
2376 	}
2377 
2378 
2379 //
2380 // adjust height
2381 //
2382 	if ((mo->flags & MF_FLOAT) && !(mo->flags2 & MF2_DORMANT) && mo->target)
2383 	{	// float down towards target if too close
2384 		if (!(mo->flags & (MF_SKULLFLY | MF_INFLOAT)))
2385 		{
2386 			dist = mo->AproxDistance (mo->target);
2387 			delta = (mo->target->Z() + (mo->height>>1)) - mo->Z();
2388 			if (delta < 0 && dist < -(delta*3))
2389 				mo->AddZ(-mo->FloatSpeed);
2390 			else if (delta > 0 && dist < (delta*3))
2391 				mo->AddZ(mo->FloatSpeed);
2392 		}
2393 	}
2394 	if (mo->player && (mo->flags & MF_NOGRAVITY) && (mo->Z() > mo->floorz))
2395 	{
2396 		if (!mo->IsNoClip2())
2397 		{
2398 			mo->AddZ(finesine[(FINEANGLES/80*level.maptime)&FINEMASK]/8);
2399 		}
2400 		mo->velz = FixedMul (mo->velz, FRICTION_FLY);
2401 	}
2402 	if (mo->waterlevel && !(mo->flags & MF_NOGRAVITY))
2403 	{
2404 		mo->velz = FixedMul (mo->velz, mo->Sector->friction);
2405 	}
2406 
2407 //
2408 // clip movement
2409 //
2410 	if (mo->Z() <= mo->floorz)
2411 	{	// Hit the floor
2412 		if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) &&
2413 			mo->Sector->SecActTarget != NULL &&
2414 			mo->Sector->floorplane.ZatPoint(mo) == mo->floorz)
2415 		{ // [RH] Let the sector do something to the actor
2416 			mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitFloor);
2417 		}
2418 		P_CheckFor3DFloorHit(mo);
2419 		// [RH] Need to recheck this because the sector action might have
2420 		// teleported the actor so it is no longer below the floor.
2421 		if (mo->Z() <= mo->floorz)
2422 		{
2423 			if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
2424 			{
2425 				mo->SetZ(mo->floorz);
2426 				if (mo->BounceFlags & BOUNCE_Floors)
2427 				{
2428 					mo->FloorBounceMissile (mo->floorsector->floorplane);
2429 					/* if (!(mo->flags6 & MF6_CANJUMP)) */ return;
2430 				}
2431 				else if (mo->flags3 & MF3_NOEXPLODEFLOOR)
2432 				{
2433 					P_HitFloor (mo);
2434 					mo->velz = 0;
2435 					return;
2436 				}
2437 				else if (mo->flags3 & MF3_FLOORHUGGER)
2438 				{ // Floor huggers can go up steps
2439 					return;
2440 				}
2441 				else
2442 				{
2443 					if (mo->floorpic == skyflatnum && !(mo->flags3 & MF3_SKYEXPLODE))
2444 					{
2445 						// [RH] Just remove the missile without exploding it
2446 						//		if this is a sky floor.
2447 						mo->Destroy ();
2448 						return;
2449 					}
2450 					P_HitFloor (mo);
2451 					P_ExplodeMissile (mo, NULL, NULL);
2452 					return;
2453 				}
2454 			}
2455 			else if (mo->BounceFlags & BOUNCE_MBF && mo->velz) // check for MBF-like bounce on non-missiles
2456 			{
2457 				mo->FloorBounceMissile(mo->floorsector->floorplane);
2458 			}
2459 			if (mo->flags3 & MF3_ISMONSTER)		// Blasted mobj falling
2460 			{
2461 				if (mo->velz < -(23*FRACUNIT))
2462 				{
2463 					P_MonsterFallingDamage (mo);
2464 				}
2465 			}
2466 			mo->SetZ(mo->floorz);
2467 			if (mo->velz < 0)
2468 			{
2469 				const fixed_t minvel = -8*FRACUNIT;	// landing speed from a jump with normal gravity
2470 
2471 				// Spawn splashes, etc.
2472 				P_HitFloor (mo);
2473 				if (mo->DamageType == NAME_Ice && mo->velz < minvel)
2474 				{
2475 					mo->tics = 1;
2476 					mo->velx = 0;
2477 					mo->vely = 0;
2478 					mo->velz = 0;
2479 					return;
2480 				}
2481 				// Let the actor do something special for hitting the floor
2482 				mo->HitFloor ();
2483 				if (mo->player)
2484 				{
2485 					if (mo->player->jumpTics < 0 || mo->velz < minvel)
2486 					{ // delay any jumping for a short while
2487 						mo->player->jumpTics = 7;
2488 					}
2489 					if (mo->velz < minvel && !(mo->flags & MF_NOGRAVITY))
2490 					{
2491 						// Squat down.
2492 						// Decrease viewheight for a moment after hitting the ground (hard),
2493 						// and utter appropriate sound.
2494 						PlayerLandedOnThing (mo, NULL);
2495 					}
2496 				}
2497 				mo->velz = 0;
2498 			}
2499 			if (mo->flags & MF_SKULLFLY)
2500 			{ // The skull slammed into something
2501 				mo->velz = -mo->velz;
2502 			}
2503 			mo->Crash();
2504 		}
2505 	}
2506 
2507 	if (mo->flags2 & MF2_FLOORCLIP)
2508 	{
2509 		mo->AdjustFloorClip ();
2510 	}
2511 
2512 	if (mo->Top() > mo->ceilingz)
2513 	{ // hit the ceiling
2514 		if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) &&
2515 			mo->Sector->SecActTarget != NULL &&
2516 			mo->Sector->ceilingplane.ZatPoint(mo) == mo->ceilingz)
2517 		{ // [RH] Let the sector do something to the actor
2518 			mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitCeiling);
2519 		}
2520 		P_CheckFor3DCeilingHit(mo);
2521 		// [RH] Need to recheck this because the sector action might have
2522 		// teleported the actor so it is no longer above the ceiling.
2523 		if (mo->Top() > mo->ceilingz)
2524 		{
2525 			mo->SetZ(mo->ceilingz - mo->height);
2526 			if (mo->BounceFlags & BOUNCE_Ceilings)
2527 			{	// ceiling bounce
2528 				mo->FloorBounceMissile (mo->ceilingsector->ceilingplane);
2529 				/*if (!(mo->flags6 & MF6_CANJUMP))*/ return;
2530 			}
2531 			if (mo->flags & MF_SKULLFLY)
2532 			{	// the skull slammed into something
2533 				mo->velz = -mo->velz;
2534 			}
2535 			if (mo->velz > 0)
2536 				mo->velz = 0;
2537 			if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
2538 			{
2539 				if (mo->flags3 & MF3_CEILINGHUGGER)
2540 				{
2541 					return;
2542 				}
2543 				if (mo->ceilingpic == skyflatnum &&  !(mo->flags3 & MF3_SKYEXPLODE))
2544 				{
2545 					mo->Destroy ();
2546 					return;
2547 				}
2548 				P_ExplodeMissile (mo, NULL, NULL);
2549 				return;
2550 			}
2551 		}
2552 	}
2553 	P_CheckFakeFloorTriggers (mo, oldz);
2554 }
2555 
P_CheckFakeFloorTriggers(AActor * mo,fixed_t oldz,bool oldz_has_viewheight)2556 void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheight)
2557 {
2558 	if (mo->player && (mo->player->cheats & CF_PREDICTING))
2559 	{
2560 		return;
2561 	}
2562 	sector_t *sec = mo->Sector;
2563 	assert (sec != NULL);
2564 	if (sec == NULL)
2565 	{
2566 		return;
2567 	}
2568 	if (sec->heightsec != NULL && sec->SecActTarget != NULL)
2569 	{
2570 		sector_t *hs = sec->heightsec;
2571 		fixed_t waterz = hs->floorplane.ZatPoint(mo);
2572 		fixed_t newz;
2573 		fixed_t viewheight;
2574 
2575 		if (mo->player != NULL)
2576 		{
2577 			viewheight = mo->player->viewheight;
2578 		}
2579 		else
2580 		{
2581 			viewheight = mo->height / 2;
2582 		}
2583 
2584 		if (oldz > waterz && mo->Z() <= waterz)
2585 		{ // Feet hit fake floor
2586 			sec->SecActTarget->TriggerAction (mo, SECSPAC_HitFakeFloor);
2587 		}
2588 
2589 		newz = mo->Z() + viewheight;
2590 		if (!oldz_has_viewheight)
2591 		{
2592 			oldz += viewheight;
2593 		}
2594 
2595 		if (oldz <= waterz && newz > waterz)
2596 		{ // View went above fake floor
2597 			sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesSurface);
2598 		}
2599 		else if (oldz > waterz && newz <= waterz)
2600 		{ // View went below fake floor
2601 			sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesDive);
2602 		}
2603 
2604 		if (!(hs->MoreFlags & SECF_FAKEFLOORONLY))
2605 		{
2606 			waterz = hs->ceilingplane.ZatPoint(mo);
2607 			if (oldz <= waterz && newz > waterz)
2608 			{ // View went above fake ceiling
2609 				sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesAboveC);
2610 			}
2611 			else if (oldz > waterz && newz <= waterz)
2612 			{ // View went below fake ceiling
2613 				sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesBelowC);
2614 			}
2615 		}
2616 	}
2617 }
2618 
2619 //===========================================================================
2620 //
2621 // PlayerLandedOnThing
2622 //
2623 //===========================================================================
2624 
PlayerLandedOnThing(AActor * mo,AActor * onmobj)2625 static void PlayerLandedOnThing (AActor *mo, AActor *onmobj)
2626 {
2627 	bool grunted;
2628 
2629 	if (!mo->player)
2630 		return;
2631 
2632 	if (mo->player->mo == mo)
2633 	{
2634 		mo->player->deltaviewheight = mo->velz >> 3;
2635 	}
2636 
2637 	if (mo->player->cheats & CF_PREDICTING)
2638 		return;
2639 
2640 	P_FallingDamage (mo);
2641 
2642 	// [RH] only make noise if alive
2643 	if (!mo->player->morphTics && mo->health > 0)
2644 	{
2645 		grunted = false;
2646 		// Why should this number vary by gravity?
2647 		if (mo->health > 0 && mo->velz < -mo->player->mo->GruntSpeed)
2648 		{
2649 			S_Sound (mo, CHAN_VOICE, "*grunt", 1, ATTN_NORM);
2650 			grunted = true;
2651 		}
2652 		if (onmobj != NULL || !Terrains[P_GetThingFloorType (mo)].IsLiquid)
2653 		{
2654 			if (!grunted || !S_AreSoundsEquivalent (mo, "*grunt", "*land"))
2655 			{
2656 				S_Sound (mo, CHAN_AUTO, "*land", 1, ATTN_NORM);
2657 			}
2658 		}
2659 	}
2660 //	mo->player->centering = true;
2661 }
2662 
2663 
2664 
2665 //
2666 // P_NightmareRespawn
2667 //
P_NightmareRespawn(AActor * mobj)2668 void P_NightmareRespawn (AActor *mobj)
2669 {
2670 	fixed_t x, y, z;
2671 	AActor *mo;
2672 	AActor *info = mobj->GetDefault();
2673 
2674 	mobj->skillrespawncount++;
2675 
2676 	// spawn the new monster (assume the spawn will be good)
2677 	if (info->flags & MF_SPAWNCEILING)
2678 		z = ONCEILINGZ;
2679 	else if (info->flags2 & MF2_SPAWNFLOAT)
2680 		z = FLOATRANDZ;
2681 	else
2682 		z = ONFLOORZ;
2683 
2684 	// spawn it
2685 	x = mobj->SpawnPoint[0];
2686 	y = mobj->SpawnPoint[1];
2687 	mo = AActor::StaticSpawn(RUNTIME_TYPE(mobj), x, y, z, NO_REPLACE, true);
2688 
2689 	if (z == ONFLOORZ)
2690 	{
2691 		mo->AddZ(mobj->SpawnPoint[2]);
2692 		if (mo->Z() < mo->floorz)
2693 		{ // Do not respawn monsters in the floor, even if that's where they
2694 		  // started. The initial P_ZMovement() call would have put them on
2695 		  // the floor right away, but we need them on the floor now so we
2696 		  // can use P_CheckPosition() properly.
2697 			mo->SetZ(mo->floorz);
2698 		}
2699 		if (mo->Top() > mo->ceilingz)
2700 		{
2701 			mo->SetZ(mo->ceilingz - mo->height);
2702 		}
2703 	}
2704 	else if (z == ONCEILINGZ)
2705 	{
2706 		mo->AddZ(-mobj->SpawnPoint[2]);
2707 	}
2708 
2709 	// If there are 3D floors, we need to find floor/ceiling again.
2710 	P_FindFloorCeiling(mo, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
2711 
2712 	if (z == ONFLOORZ)
2713 	{
2714 		if (mo->Z() < mo->floorz)
2715 		{ // Do not respawn monsters in the floor, even if that's where they
2716 		  // started. The initial P_ZMovement() call would have put them on
2717 		  // the floor right away, but we need them on the floor now so we
2718 		  // can use P_CheckPosition() properly.
2719 			mo->SetZ(mo->floorz);
2720 		}
2721 		if (mo->Top() > mo->ceilingz)
2722 		{ // Do the same for the ceiling.
2723 			mo->SetZ(mo->ceilingz - mo->height);
2724 		}
2725 	}
2726 
2727 	// something is occupying its position?
2728 	if (!P_CheckPosition(mo, mo->X(), mo->Y(), true))
2729 	{
2730 		//[GrafZahl] MF_COUNTKILL still needs to be checked here.
2731 		mo->ClearCounters();
2732 		mo->Destroy ();
2733 		return;		// no respawn
2734 	}
2735 
2736 	z = mo->Z();
2737 
2738 	// inherit attributes from deceased one
2739 	mo->SpawnPoint[0] = mobj->SpawnPoint[0];
2740 	mo->SpawnPoint[1] = mobj->SpawnPoint[1];
2741 	mo->SpawnPoint[2] = mobj->SpawnPoint[2];
2742 	mo->SpawnAngle = mobj->SpawnAngle;
2743 	mo->SpawnFlags = mobj->SpawnFlags & ~MTF_DORMANT;	// It wasn't dormant when it died, so it's not dormant now, either.
2744 	mo->angle = ANG45 * (mobj->SpawnAngle/45);
2745 
2746 	mo->HandleSpawnFlags ();
2747 	mo->reactiontime = 18;
2748 	mo->CopyFriendliness (mobj, false);
2749 	mo->Translation = mobj->Translation;
2750 
2751 	mo->skillrespawncount = mobj->skillrespawncount;
2752 
2753 	mo->PrevZ = z;		// Do not interpolate Z position if we changed it since spawning.
2754 
2755 	// spawn a teleport fog at old spot because of removal of the body?
2756 	P_SpawnTeleportFog(mobj, mobj->PosPlusZ(TELEFOGHEIGHT), true, true);
2757 
2758 	// spawn a teleport fog at the new spot
2759 	P_SpawnTeleportFog(mobj, x, y, z + TELEFOGHEIGHT, false, true);
2760 
2761 	// remove the old monster
2762 	mobj->Destroy ();
2763 }
2764 
2765 
2766 AActor *AActor::TIDHash[128];
2767 
2768 //
2769 // P_ClearTidHashes
2770 //
2771 // Clears the tid hashtable.
2772 //
2773 
ClearTIDHashes()2774 void AActor::ClearTIDHashes ()
2775 {
2776 	memset(TIDHash, 0, sizeof(TIDHash));
2777 }
2778 
2779 //
2780 // P_AddMobjToHash
2781 //
2782 // Inserts an mobj into the correct chain based on its tid.
2783 // If its tid is 0, this function does nothing.
2784 //
AddToHash()2785 void AActor::AddToHash ()
2786 {
2787 	if (tid == 0)
2788 	{
2789 		iprev = NULL;
2790 		inext = NULL;
2791 		return;
2792 	}
2793 	else
2794 	{
2795 		int hash = TIDHASH (tid);
2796 
2797 		inext = TIDHash[hash];
2798 		iprev = &TIDHash[hash];
2799 		TIDHash[hash] = this;
2800 		if (inext)
2801 		{
2802 			inext->iprev = &inext;
2803 		}
2804 	}
2805 }
2806 
2807 //
2808 // P_RemoveMobjFromHash
2809 //
2810 // Removes an mobj from its hash chain.
2811 //
RemoveFromHash()2812 void AActor::RemoveFromHash ()
2813 {
2814 	if (tid != 0 && iprev)
2815 	{
2816 		*iprev = inext;
2817 		if (inext)
2818 		{
2819 			inext->iprev = iprev;
2820 		}
2821 		iprev = NULL;
2822 		inext = NULL;
2823 	}
2824 	tid = 0;
2825 }
2826 
2827 //==========================================================================
2828 //
2829 // P_IsTIDUsed
2830 //
2831 // Returns true if there is at least one actor with the specified TID
2832 // (dead or alive).
2833 //
2834 //==========================================================================
2835 
P_IsTIDUsed(int tid)2836 bool P_IsTIDUsed(int tid)
2837 {
2838 	AActor *probe = AActor::TIDHash[tid & 127];
2839 	while (probe != NULL)
2840 	{
2841 		if (probe->tid == tid)
2842 		{
2843 			return true;
2844 		}
2845 		probe = probe->inext;
2846 	}
2847 	return false;
2848 }
2849 
2850 //==========================================================================
2851 //
2852 // P_FindUniqueTID
2853 //
2854 // Returns an unused TID. If start_tid is 0, then a random TID will be
2855 // chosen. Otherwise, it will perform a linear search starting from
2856 // start_tid. If limit is non-0, then it will not check more than <limit>
2857 // number of TIDs. Returns 0 if no suitable TID was found.
2858 //
2859 //==========================================================================
2860 
P_FindUniqueTID(int start_tid,int limit)2861 int P_FindUniqueTID(int start_tid, int limit)
2862 {
2863 	int tid;
2864 
2865 	if (start_tid != 0)
2866 	{ // Do a linear search.
2867 		if (start_tid > INT_MAX-limit+1)
2868 		{ // If 'limit+start_tid-1' overflows, clamp 'limit' to INT_MAX
2869 			limit = INT_MAX;
2870 		}
2871 		else
2872 		{
2873 			limit += start_tid-1;
2874 		}
2875 		for (tid = start_tid; tid <= limit; ++tid)
2876 		{
2877 			if (tid != 0 && !P_IsTIDUsed(tid))
2878 			{
2879 				return tid;
2880 			}
2881 		}
2882 		// Nothing free found.
2883 		return 0;
2884 	}
2885 	// Do a random search. To try and be a bit more performant, this
2886 	// actually does several linear searches. In the case of *very*
2887 	// dense TID usage, this could potentially perform worse than doing
2888 	// a complete linear scan starting at 1. However, you would need
2889 	// to use an absolutely ridiculous number of actors before this
2890 	// becomes a real concern.
2891 	if (limit == 0)
2892 	{
2893 		limit = INT_MAX;
2894 	}
2895 	for (int i = 0; i < limit; i += 5)
2896 	{
2897 		// Use a positive starting TID.
2898 		tid = pr_uniquetid.GenRand32() & INT_MAX;
2899 		tid = P_FindUniqueTID(tid == 0 ? 1 : tid, 5);
2900 		if (tid != 0)
2901 		{
2902 			return tid;
2903 		}
2904 	}
2905 	// Nothing free found.
2906 	return 0;
2907 }
2908 
CCMD(utid)2909 CCMD(utid)
2910 {
2911 	Printf("%d\n",
2912 		P_FindUniqueTID(argv.argc() > 1 ? atoi(argv[1]) : 0,
2913 		(argv.argc() > 2 && atoi(argv[2]) >= 0) ? atoi(argv[2]) : 0));
2914 }
2915 
2916 //==========================================================================
2917 //
2918 // AActor :: GetMissileDamage
2919 //
2920 // If the actor's damage amount is an expression, evaluate it and return
2921 // the result. Otherwise, return ((random() & mask) + add) * damage.
2922 //
2923 //==========================================================================
2924 
GetMissileDamage(int mask,int add)2925 int AActor::GetMissileDamage (int mask, int add)
2926 {
2927 	if ((Damage & 0xC0000000) == 0x40000000)
2928 	{
2929 		return EvalExpressionI (Damage & 0x3FFFFFFF, this);
2930 	}
2931 	if (Damage == 0)
2932 	{
2933 		return 0;
2934 	}
2935 	else if (mask == 0)
2936 	{
2937 		return add * Damage;
2938 	}
2939 	else
2940 	{
2941 		return ((pr_missiledamage() & mask) + add) * Damage;
2942 	}
2943 }
2944 
Howl()2945 void AActor::Howl ()
2946 {
2947 	int howl = GetClass()->Meta.GetMetaInt(AMETA_HowlSound);
2948 	if (!S_IsActorPlayingSomething(this, CHAN_BODY, howl))
2949 	{
2950 		S_Sound (this, CHAN_BODY, howl, 1, ATTN_NORM);
2951 	}
2952 }
2953 
HitFloor()2954 void AActor::HitFloor ()
2955 {
2956 }
2957 
Slam(AActor * thing)2958 bool AActor::Slam (AActor *thing)
2959 {
2960 	flags &= ~MF_SKULLFLY;
2961 	velx = vely = velz = 0;
2962 	if (health > 0)
2963 	{
2964 		if (!(flags2 & MF2_DORMANT))
2965 		{
2966 			int dam = GetMissileDamage (7, 1);
2967 			int newdam = P_DamageMobj (thing, this, this, dam, NAME_Melee);
2968 			P_TraceBleed (newdam > 0 ? newdam : dam, thing, this);
2969 			// The charging monster may have died by the target's actions here.
2970 			if (health > 0)
2971 			{
2972 				if (SeeState != NULL) SetState (SeeState);
2973 				else SetIdle();
2974 			}
2975 		}
2976 		else
2977 		{
2978 			SetIdle();
2979 			tics = -1;
2980 		}
2981 	}
2982 	return false;			// stop moving
2983 }
2984 
SpecialBlastHandling(AActor * source,fixed_t strength)2985 bool AActor::SpecialBlastHandling (AActor *source, fixed_t strength)
2986 {
2987 	return true;
2988 }
2989 
SpecialMissileHit(AActor * victim)2990 int AActor::SpecialMissileHit (AActor *victim)
2991 {
2992 	return -1;
2993 }
2994 
AdjustReflectionAngle(AActor * thing,angle_t & angle)2995 bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
2996 {
2997 	if (flags2 & MF2_DONTREFLECT) return true;
2998 	if (thing->flags7 & MF7_THRUREFLECT) return false;
2999 	// Change angle for reflection
3000 	if (thing->flags4&MF4_SHIELDREFLECT)
3001 	{
3002 		// Shield reflection (from the Centaur
3003 		if (absangle(angle - thing->angle)>>24 > 45)
3004 			return true;	// Let missile explode
3005 
3006 		if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit)))	// shouldn't this be handled by another flag???
3007 			return true;
3008 
3009 		if (pr_reflect () < 128)
3010 			angle += ANGLE_45;
3011 		else
3012 			angle -= ANGLE_45;
3013 
3014 	}
3015 	else if (thing->flags4&MF4_DEFLECT)
3016 	{
3017 		// deflect (like the Heresiarch)
3018 		if(pr_reflect() < 128)
3019 			angle += ANG45;
3020 		else
3021 			angle -= ANG45;
3022 	}
3023 	else
3024 	{
3025 		angle += ANGLE_1 * ((pr_reflect() % 16) - 8);
3026 	}
3027 	//Always check for AIMREFLECT, no matter what else is checked above.
3028 	if (thing->flags7 & MF7_AIMREFLECT)
3029 	{
3030 		if (this->target != NULL)
3031 		{
3032 			A_Face(this, this->target);
3033 		}
3034 		else if (thing->target != NULL)
3035 		{
3036 			A_Face(this, thing->target);
3037 		}
3038 	}
3039 
3040 	return false;
3041 }
3042 
PlayActiveSound()3043 void AActor::PlayActiveSound ()
3044 {
3045 	if (ActiveSound && !S_IsActorPlayingSomething (this, CHAN_VOICE, -1))
3046 	{
3047 		S_Sound (this, CHAN_VOICE, ActiveSound, 1,
3048 			(flags3 & MF3_FULLVOLACTIVE) ? ATTN_NONE : ATTN_IDLE);
3049 	}
3050 }
3051 
IsOkayToAttack(AActor * link)3052 bool AActor::IsOkayToAttack (AActor *link)
3053 {
3054 	if (!(player							// Original AActor::IsOkayToAttack was only for players
3055 	//	|| (flags  & MF_FRIENDLY)			// Maybe let friendly monsters use the function as well?
3056 		|| (flags5 & MF5_SUMMONEDMONSTER)	// AMinotaurFriend has its own version, generalized to other summoned monsters
3057 		|| (flags2 & MF2_SEEKERMISSILE)))	// AHolySpirit and AMageStaffFX2 as well, generalized to other seeker missiles
3058 	{	// Normal monsters and other actors always return false.
3059 		return false;
3060 	}
3061 	// Standard things to eliminate: an actor shouldn't attack itself,
3062 	// or a non-shootable, dormant, non-player-and-non-monster actor.
3063 	if (link == this)									return false;
3064 	if (!(link->player||(link->flags3 & MF3_ISMONSTER)))return false;
3065 	if (!(link->flags & MF_SHOOTABLE))					return false;
3066 	if (link->flags2 & MF2_DORMANT)						return false;
3067 
3068 	// An actor shouldn't attack friendly actors. The reference depends
3069 	// on the type of actor: for a player's actor, itself; for a projectile,
3070 	// its target; and for a summoned minion, its tracer.
3071 	AActor * Friend = NULL;
3072 	if (player)											Friend = this;
3073 	else if (flags5 & MF5_SUMMONEDMONSTER)				Friend = tracer;
3074 	else if (flags2 & MF2_SEEKERMISSILE)				Friend = target;
3075 	else if ((flags & MF_FRIENDLY) && FriendPlayer)		Friend = players[FriendPlayer-1].mo;
3076 
3077 	// Friend checks
3078 	if (link == Friend)									return false;
3079 	if (Friend == NULL)									return false;
3080 	if (Friend->IsFriend(link))							return false;
3081 	if ((link->flags5 & MF5_SUMMONEDMONSTER)			// No attack against minions on the same side
3082 		&& (link->tracer == Friend))					return false;
3083 	if (multiplayer && !deathmatch						// No attack against fellow players in coop
3084 		&& link->player && Friend->player)				return false;
3085 	if (((flags & link->flags) & MF_FRIENDLY)			// No friendly infighting amongst minions
3086 		&& IsFriend(link))								return false;
3087 
3088 	// Now that all the actor checks are made, the line of sight can be checked
3089 	if (P_CheckSight (this, link))
3090 	{
3091 		// AMageStaffFX2::IsOkayToAttack had an extra check here, generalized with a flag,
3092 		// to only allow the check to succeed if the enemy was in a ~84� FOV of the player
3093 		if (flags3 & MF3_SCREENSEEKER)
3094 		{
3095 			angle_t angle = Friend->AngleTo(link) - Friend->angle;
3096 			angle >>= 24;
3097 			if (angle>226 || angle<30)
3098 			{
3099 				return true;
3100 			}
3101 		}
3102 		// Other actors are not concerned by this check
3103 		else return true;
3104 	}
3105 	// The sight check was failed, or the angle wasn't right for a screenseeker
3106 	return false;
3107 }
3108 
SetShade(DWORD rgb)3109 void AActor::SetShade (DWORD rgb)
3110 {
3111 	PalEntry *entry = (PalEntry *)&rgb;
3112 	fillcolor = rgb | (ColorMatcher.Pick (entry->r, entry->g, entry->b) << 24);
3113 }
3114 
SetShade(int r,int g,int b)3115 void AActor::SetShade (int r, int g, int b)
3116 {
3117 	fillcolor = MAKEARGB(ColorMatcher.Pick (r, g, b), r, g, b);
3118 }
3119 
SetPitch(int p,bool interpolate,bool forceclamp)3120 void AActor::SetPitch(int p, bool interpolate, bool forceclamp)
3121 {
3122 	if (player != NULL || forceclamp)
3123 	{ // clamp the pitch we set
3124 		int min, max;
3125 
3126 		if (player != NULL)
3127 		{
3128 			min = player->MinPitch;
3129 			max = player->MaxPitch;
3130 		}
3131 		else
3132 		{
3133 			min = -ANGLE_90 + (1 << ANGLETOFINESHIFT);
3134 			max = ANGLE_90 - (1 << ANGLETOFINESHIFT);
3135 		}
3136 		p = clamp<int>(p, min, max);
3137 	}
3138 	if (p != pitch)
3139 	{
3140 		pitch = p;
3141 		if (player != NULL && interpolate)
3142 		{
3143 			player->cheats |= CF_INTERPVIEW;
3144 		}
3145 	}
3146 }
3147 
SetAngle(angle_t ang,bool interpolate)3148 void AActor::SetAngle(angle_t ang, bool interpolate)
3149 {
3150 	if (ang != angle)
3151 	{
3152 		angle = ang;
3153 		if (player != NULL && interpolate)
3154 		{
3155 			player->cheats |= CF_INTERPVIEW;
3156 		}
3157 	}
3158 }
3159 
SetRoll(angle_t r,bool interpolate)3160 void AActor::SetRoll(angle_t r, bool interpolate)
3161 {
3162 	if (r != roll)
3163 	{
3164 		roll = r;
3165 		if (player != NULL && interpolate)
3166 		{
3167 			player->cheats |= CF_INTERPVIEW;
3168 		}
3169 	}
3170 }
3171 
3172 //
3173 // P_MobjThinker
3174 //
Tick()3175 void AActor::Tick ()
3176 {
3177 	// [RH] Data for Heretic/Hexen scrolling sectors
3178 	static const BYTE HexenScrollDirs[8] = { 64, 0, 192, 128, 96, 32, 224, 160 };
3179 	static const BYTE HexenSpeedMuls[3] = { 5, 10, 25 };
3180 	static const SBYTE HexenScrollies[24][2] =
3181 	{
3182 		{  0,  1 }, {  0,  2 }, {  0,  4 },
3183 		{ -1,  0 }, { -2,  0 }, { -4,  0 },
3184 		{  0, -1 }, {  0, -2 }, {  0, -4 },
3185 		{  1,  0 }, {  2,  0 }, {  4,  0 },
3186 		{  1,  1 }, {  2,  2 }, {  4,  4 },
3187 		{ -1,  1 }, { -2,  2 }, { -4,  4 },
3188 		{ -1, -1 }, { -2, -2 }, { -4, -4 },
3189 		{  1, -1 }, {  2, -2 }, {  4, -4 }
3190 	};
3191 
3192 	static const BYTE HereticScrollDirs[4] = { 6, 9, 1, 4 };
3193 	static const BYTE HereticSpeedMuls[5] = { 5, 10, 25, 30, 35 };
3194 
3195 
3196 	AActor *onmo;
3197 	int i;
3198 
3199 	//assert (state != NULL);
3200 	if (state == NULL)
3201 	{
3202 		Printf("Actor of type %s at (%f,%f) left without a state\n", GetClass()->TypeName.GetChars(),
3203 			X()/65536., Y()/65536.);
3204 		Destroy();
3205 		return;
3206 	}
3207 
3208 	// This is necessary to properly interpolate movement outside this function
3209 	// like from an ActorMover
3210 	PrevX = X();
3211 	PrevY = Y();
3212 	PrevZ = Z();
3213 	PrevAngle = angle;
3214 
3215 	if (flags5 & MF5_NOINTERACTION)
3216 	{
3217 		// only do the minimally necessary things here to save time:
3218 		// Check the time freezer
3219 		// apply velocity
3220 		// ensure that the actor is not linked into the blockmap
3221 
3222 		if (!(flags5 & MF5_NOTIMEFREEZE))
3223 		{
3224 			//Added by MC: Freeze mode.
3225 			if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN)
3226 			{
3227 				// Boss cubes shouldn't be accelerated by timefreeze
3228 				if (flags6 & MF6_BOSSCUBE)
3229 				{
3230 					special2++;
3231 				}
3232 				return;
3233 			}
3234 		}
3235 
3236 		UnlinkFromWorld ();
3237 		flags |= MF_NOBLOCKMAP;
3238 		SetXYZ(Vec3Offset(velx, vely, velz));
3239 		SetMovement(velx, vely, velz);
3240 		LinkToWorld ();
3241 	}
3242 	else
3243 	{
3244 		AInventory * item = Inventory;
3245 
3246 		// Handle powerup effects here so that the order is controlled
3247 		// by the order in the inventory, not the order in the thinker table
3248 		while (item != NULL && item->Owner == this)
3249 		{
3250 			item->DoEffect();
3251 			item = item->Inventory;
3252 		}
3253 
3254 		if (flags & MF_UNMORPHED)
3255 		{
3256 			return;
3257 		}
3258 
3259 		if (!(flags5 & MF5_NOTIMEFREEZE))
3260 		{
3261 			// Boss cubes shouldn't be accelerated by timefreeze
3262 			if (flags6 & MF6_BOSSCUBE)
3263 			{
3264 				special2++;
3265 			}
3266 			//Added by MC: Freeze mode.
3267 			if (bglobal.freeze && !(player && player->Bot == NULL))
3268 			{
3269 				return;
3270 			}
3271 
3272 			// Apply freeze mode.
3273 			if ((level.flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
3274 			{
3275 				return;
3276 			}
3277 		}
3278 
3279 
3280 		if (effects & FX_ROCKET)
3281 		{
3282 			if (++smokecounter == 4)
3283 			{
3284 				// add some smoke behind the rocket
3285 				smokecounter = 0;
3286 				AActor *th = Spawn("RocketSmokeTrail", Vec3Offset(-velx, -vely, -velz), ALLOW_REPLACE);
3287 				if (th)
3288 				{
3289 					th->tics -= pr_rockettrail()&3;
3290 					if (th->tics < 1) th->tics = 1;
3291 					if (!(cl_rockettrails & 2)) th->renderflags |= RF_INVISIBLE;
3292 				}
3293 			}
3294 		}
3295 		else if (effects & FX_GRENADE)
3296 		{
3297 			if (++smokecounter == 8)
3298 			{
3299 				smokecounter = 0;
3300 				angle_t moveangle = R_PointToAngle2(0,0,velx,vely);
3301 				fixed_t xo = -FixedMul(finecosine[(moveangle) >> ANGLETOFINESHIFT], radius * 2) + (pr_rockettrail() << 10);
3302 				fixed_t yo = -FixedMul(finesine[(moveangle) >> ANGLETOFINESHIFT], radius * 2) + (pr_rockettrail() << 10);
3303 				AActor * th = Spawn("GrenadeSmokeTrail", Vec3Offset(xo, yo, - (height>>3) * (velz>>16) + (2*height)/3), ALLOW_REPLACE);
3304 				if (th)
3305 				{
3306 					th->tics -= pr_rockettrail()&3;
3307 					if (th->tics < 1) th->tics = 1;
3308 					if (!(cl_rockettrails & 2)) th->renderflags |= RF_INVISIBLE;
3309 				}
3310 			}
3311 		}
3312 
3313 		fixed_t oldz = Z();
3314 
3315 		// [RH] Give the pain elemental vertical friction
3316 		// This used to be in APainElemental::Tick but in order to use
3317 		// A_PainAttack with other monsters it has to be here
3318 		if (flags4 & MF4_VFRICTION)
3319 		{
3320 			if (health >0)
3321 			{
3322 				if (abs (velz) < FRACUNIT/4)
3323 				{
3324 					velz = 0;
3325 					flags4 &= ~MF4_VFRICTION;
3326 				}
3327 				else
3328 				{
3329 					velz = FixedMul (velz, 0xe800);
3330 				}
3331 			}
3332 		}
3333 
3334 		// [RH] Pulse in and out of visibility
3335 		if (effects & FX_VISIBILITYPULSE)
3336 		{
3337 			if (visdir > 0)
3338 			{
3339 				alpha += 0x800;
3340 				if (alpha >= OPAQUE)
3341 				{
3342 					alpha = OPAQUE;
3343 					visdir = -1;
3344 				}
3345 			}
3346 			else
3347 			{
3348 				alpha -= 0x800;
3349 				if (alpha <= TRANSLUC25)
3350 				{
3351 					alpha = TRANSLUC25;
3352 					visdir = 1;
3353 				}
3354 			}
3355 		}
3356 		else if (flags & MF_STEALTH)
3357 		{
3358 			// [RH] Fade a stealth monster in and out of visibility
3359 			RenderStyle.Flags &= ~STYLEF_Alpha1;
3360 			if (visdir > 0)
3361 			{
3362 				alpha += 2*FRACUNIT/TICRATE;
3363 				if (alpha > OPAQUE)
3364 				{
3365 					alpha = OPAQUE;
3366 					visdir = 0;
3367 				}
3368 			}
3369 			else if (visdir < 0)
3370 			{
3371 				alpha -= 3*FRACUNIT/TICRATE/2;
3372 				if (alpha < 0)
3373 				{
3374 					alpha = 0;
3375 					visdir = 0;
3376 				}
3377 			}
3378 		}
3379 
3380 		if (bglobal.botnum && !demoplayback &&
3381 			((flags & (MF_SPECIAL|MF_MISSILE)) || (flags3 & MF3_ISMONSTER)))
3382 		{
3383 			BotSupportCycles.Clock();
3384 			bglobal.m_Thinking = true;
3385 			for (i = 0; i < MAXPLAYERS; i++)
3386 			{
3387 				if (!playeringame[i] || players[i].Bot == NULL)
3388 					continue;
3389 
3390 				if (flags3 & MF3_ISMONSTER)
3391 				{
3392 					if (health > 0
3393 						&& !players[i].Bot->enemy
3394 						&& player ? !IsTeammate (players[i].mo) : true
3395 						&& AproxDistance (players[i].mo) < MAX_MONSTER_TARGET_DIST
3396 						&& P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING))
3397 					{ //Probably a monster, so go kill it.
3398 						players[i].Bot->enemy = this;
3399 					}
3400 				}
3401 				else if (flags & MF_SPECIAL)
3402 				{ //Item pickup time
3403 					//clock (BotWTG);
3404 					players[i].Bot->WhatToGet (this);
3405 					//unclock (BotWTG);
3406 					BotWTG++;
3407 				}
3408 				else if (flags & MF_MISSILE)
3409 				{
3410 					if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT))
3411 					{ //warn for incoming missiles.
3412 						if (target != players[i].mo && players[i].Bot->Check_LOS (this, ANGLE_90))
3413 							players[i].Bot->missile = this;
3414 					}
3415 				}
3416 			}
3417 			bglobal.m_Thinking = false;
3418 			BotSupportCycles.Unclock();
3419 		}
3420 
3421 		//End of MC
3422 
3423 		// [RH] Consider carrying sectors here
3424 		fixed_t cummx = 0, cummy = 0;
3425 		if ((level.Scrolls != NULL || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR))
3426 		{
3427 			fixed_t height, waterheight;	// killough 4/4/98: add waterheight
3428 			const msecnode_t *node;
3429 			int countx, county;
3430 
3431 			// killough 3/7/98: Carry things on floor
3432 			// killough 3/20/98: use new sector list which reflects true members
3433 			// killough 3/27/98: fix carrier bug
3434 			// killough 4/4/98: Underwater, carry things even w/o gravity
3435 
3436 			// Move objects only if on floor or underwater,
3437 			// non-floating, and clipped.
3438 
3439 			countx = county = 0;
3440 
3441 			for (node = touching_sectorlist; node; node = node->m_tnext)
3442 			{
3443 				const sector_t *sec = node->m_sector;
3444 				fixed_t scrollx, scrolly;
3445 
3446 				if (level.Scrolls != NULL)
3447 				{
3448 					const FSectorScrollValues *scroll = &level.Scrolls[sec - sectors];
3449 					scrollx = scroll->ScrollX;
3450 					scrolly = scroll->ScrollY;
3451 				}
3452 				else
3453 				{
3454 					scrollx = scrolly = 0;
3455 				}
3456 
3457 				if (player != NULL)
3458 				{
3459 					int scrolltype = sec->special;
3460 
3461 					if (scrolltype >= Scroll_North_Slow &&
3462 						scrolltype <= Scroll_SouthWest_Fast)
3463 					{ // Hexen scroll special
3464 						scrolltype -= Scroll_North_Slow;
3465 						if (i_compatflags&COMPATF_RAVENSCROLL)
3466 						{
3467 							angle_t fineangle = HexenScrollDirs[scrolltype / 3] * 32;
3468 							fixed_t carryspeed = DivScale32 (HexenSpeedMuls[scrolltype % 3], 32*CARRYFACTOR);
3469 							scrollx += FixedMul (carryspeed, finecosine[fineangle]);
3470 							scrolly += FixedMul (carryspeed, finesine[fineangle]);
3471 						}
3472 						else
3473 						{
3474 							// Use speeds that actually match the scrolling textures!
3475 							scrollx -= HexenScrollies[scrolltype][0] << (FRACBITS-1);
3476 							scrolly += HexenScrollies[scrolltype][1] << (FRACBITS-1);
3477 						}
3478 					}
3479 					else if (scrolltype >= Carry_East5 &&
3480 							 scrolltype <= Carry_West35)
3481 					{ // Heretic scroll special
3482 						scrolltype -= Carry_East5;
3483 						BYTE dir = HereticScrollDirs[scrolltype / 5];
3484 						fixed_t carryspeed = DivScale32 (HereticSpeedMuls[scrolltype % 5], 32*CARRYFACTOR);
3485 						if (scrolltype<=Carry_East35 && !(i_compatflags&COMPATF_RAVENSCROLL))
3486 						{
3487 							// Use speeds that actually match the scrolling textures!
3488 							carryspeed = (1 << ((scrolltype%5) + FRACBITS-1));
3489 						}
3490 						scrollx += carryspeed * ((dir & 3) - 1);
3491 						scrolly += carryspeed * (((dir & 12) >> 2) - 1);
3492 					}
3493 					else if (scrolltype == dScroll_EastLavaDamage)
3494 					{ // Special Heretic scroll special
3495 						if (i_compatflags&COMPATF_RAVENSCROLL)
3496 						{
3497 							scrollx += DivScale32 (28, 32*CARRYFACTOR);
3498 						}
3499 						else
3500 						{
3501 							// Use a speed that actually matches the scrolling texture!
3502 							scrollx += DivScale32 (12, 32*CARRYFACTOR);
3503 						}
3504 					}
3505 					else if (scrolltype == Scroll_StrifeCurrent)
3506 					{ // Strife scroll special
3507 						int anglespeed = tagManager.GetFirstSectorTag(sec) - 100;
3508 						fixed_t carryspeed = DivScale32 (anglespeed % 10, 16*CARRYFACTOR);
3509 						angle_t fineangle = (anglespeed / 10) << (32-3);
3510 						fineangle >>= ANGLETOFINESHIFT;
3511 						scrollx += FixedMul (carryspeed, finecosine[fineangle]);
3512 						scrolly += FixedMul (carryspeed, finesine[fineangle]);
3513 					}
3514 				}
3515 
3516 				if ((scrollx | scrolly) == 0)
3517 				{
3518 					continue;
3519 				}
3520 				sector_t *heightsec = sec->GetHeightSec();
3521 				if (flags & MF_NOGRAVITY && heightsec == NULL)
3522 				{
3523 					continue;
3524 				}
3525 				height = sec->floorplane.ZatPoint (this);
3526 				if (Z() > height)
3527 				{
3528 					if (heightsec == NULL)
3529 					{
3530 						continue;
3531 					}
3532 
3533 					waterheight = heightsec->floorplane.ZatPoint (this);
3534 					if (waterheight > height && Z() >= waterheight)
3535 					{
3536 						continue;
3537 					}
3538 				}
3539 
3540 				cummx += scrollx;
3541 				cummy += scrolly;
3542 				if (scrollx) countx++;
3543 				if (scrolly) county++;
3544 			}
3545 
3546 			// Some levels designed with Boom in mind actually want things to accelerate
3547 			// at neighboring scrolling sector boundaries. But it is only important for
3548 			// non-player objects.
3549 			if (player != NULL || !(i_compatflags & COMPATF_BOOMSCROLL))
3550 			{
3551 				if (countx > 1)
3552 				{
3553 					cummx /= countx;
3554 				}
3555 				if (county > 1)
3556 				{
3557 					cummy /= county;
3558 				}
3559 			}
3560 		}
3561 
3562 		// [RH] If standing on a steep slope, fall down it
3563 		if ((flags & MF_SOLID) && !(flags & (MF_NOCLIP|MF_NOGRAVITY)) &&
3564 			!(flags & MF_NOBLOCKMAP) &&
3565 			velz <= 0 &&
3566 			floorz == Z())
3567 		{
3568 			secplane_t floorplane;
3569 
3570 			// Check 3D floors as well
3571 			floorplane = P_FindFloorPlane(floorsector, X(), Y(), floorz);
3572 
3573 			if (floorplane.c < STEEPSLOPE &&
3574 				floorplane.ZatPoint (this) <= floorz)
3575 			{
3576 				const msecnode_t *node;
3577 				bool dopush = true;
3578 
3579 				if (floorplane.c > STEEPSLOPE*2/3)
3580 				{
3581 					for (node = touching_sectorlist; node; node = node->m_tnext)
3582 					{
3583 						const sector_t *sec = node->m_sector;
3584 						if (sec->floorplane.c >= STEEPSLOPE)
3585 						{
3586 							if (floorplane.ZatPoint (this) >= Z() - MaxStepHeight)
3587 							{
3588 								dopush = false;
3589 								break;
3590 							}
3591 						}
3592 					}
3593 				}
3594 				if (dopush)
3595 				{
3596 					velx += floorplane.a;
3597 					vely += floorplane.b;
3598 				}
3599 			}
3600 		}
3601 
3602 		// [RH] Missiles moving perfectly vertical need some X/Y movement, or they
3603 		// won't hurt anything. Don't do this if damage is 0! That way, you can
3604 		// still have missiles that go straight up and down through actors without
3605 		// damaging anything.
3606 		if ((flags & MF_MISSILE) && (velx|vely) == 0 && Damage != 0)
3607 		{
3608 			velx = 1;
3609 		}
3610 
3611 		// Handle X and Y velocities
3612 		BlockingMobj = NULL;
3613 		fixed_t oldfloorz = P_XYMovement (this, cummx, cummy);
3614 		if (ObjectFlags & OF_EuthanizeMe)
3615 		{ // actor was destroyed
3616 			return;
3617 		}
3618 		if ((velx | vely) == 0) // Actors at rest
3619 		{
3620 			if (flags2 & MF2_BLASTED)
3621 			{ // Reset to not blasted when velocities are gone
3622 				flags2 &= ~MF2_BLASTED;
3623 			}
3624 			if ((flags6 & MF6_TOUCHY) && !IsSentient())
3625 			{ // Arm a mine which has come to rest
3626 				flags6 |= MF6_ARMED;
3627 			}
3628 
3629 		}
3630 		if (velz || BlockingMobj || Z() != floorz)
3631 		{	// Handle Z velocity and gravity
3632 			if (((flags2 & MF2_PASSMOBJ) || (flags & MF_SPECIAL)) && !(i_compatflags & COMPATF_NO_PASSMOBJ))
3633 			{
3634 				if (!(onmo = P_CheckOnmobj (this)))
3635 				{
3636 					P_ZMovement (this, oldfloorz);
3637 					flags2 &= ~MF2_ONMOBJ;
3638 				}
3639 				else
3640 				{
3641 					if (player)
3642 					{
3643 						if (velz < (fixed_t)(level.gravity * Sector->gravity * -655.36f)
3644 							&& !(flags&MF_NOGRAVITY))
3645 						{
3646 							PlayerLandedOnThing (this, onmo);
3647 						}
3648 					}
3649 					if (onmo->Top() - Z() <= MaxStepHeight)
3650 					{
3651 						if (player && player->mo == this)
3652 						{
3653 							player->viewheight -= onmo->Top() - Z();
3654 							fixed_t deltaview = player->GetDeltaViewHeight();
3655 							if (deltaview > player->deltaviewheight)
3656 							{
3657 								player->deltaviewheight = deltaview;
3658 							}
3659 						}
3660 						SetZ(onmo->Top());
3661 					}
3662 					// Check for MF6_BUMPSPECIAL
3663 					// By default, only players can activate things by bumping into them
3664 					// We trigger specials as long as we are on top of it and not just when
3665 					// we land on it. This could be considered as gravity making us continually
3666 					// bump into it, but it also avoids having to worry about walking on to
3667 					// something without dropping and not triggering anything.
3668 					if ((onmo->flags6 & MF6_BUMPSPECIAL) && ((player != NULL)
3669 						|| ((onmo->activationtype & THINGSPEC_MonsterTrigger) && (flags3 & MF3_ISMONSTER))
3670 						|| ((onmo->activationtype & THINGSPEC_MissileTrigger) && (flags & MF_MISSILE))
3671 						) && (level.maptime > onmo->lastbump)) // Leave the bumper enough time to go away
3672 					{
3673 						if (player == NULL || !(player->cheats & CF_PREDICTING))
3674 						{
3675 							if (P_ActivateThingSpecial(onmo, this))
3676 								onmo->lastbump = level.maptime + TICRATE;
3677 						}
3678 					}
3679 					if (velz != 0 && (BounceFlags & BOUNCE_Actors))
3680 					{
3681 						P_BounceActor(this, onmo, true);
3682 					}
3683 					else
3684 					{
3685 						flags2 |= MF2_ONMOBJ;
3686 						velz = 0;
3687 						Crash();
3688 					}
3689 				}
3690 			}
3691 			else
3692 			{
3693 				P_ZMovement (this, oldfloorz);
3694 			}
3695 
3696 			if (ObjectFlags & OF_EuthanizeMe)
3697 				return;		// actor was destroyed
3698 		}
3699 		else if (Z() <= floorz)
3700 		{
3701 			Crash();
3702 		}
3703 
3704 		UpdateWaterLevel (oldz);
3705 
3706 		// [RH] Don't advance if predicting a player
3707 		if (player && (player->cheats & CF_PREDICTING))
3708 		{
3709 			return;
3710 		}
3711 
3712 		// Check for poison damage, but only once per PoisonPeriod tics (or once per second if none).
3713 		if (PoisonDurationReceived && (level.time % (PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE) == 0))
3714 		{
3715 			P_DamageMobj(this, NULL, Poisoner, PoisonDamageReceived, PoisonDamageTypeReceived ? PoisonDamageTypeReceived : (FName)NAME_Poison, 0);
3716 
3717 			--PoisonDurationReceived;
3718 
3719 			// Must clear damage when duration is done, otherwise it
3720 			// could be added to with ADDITIVEPOISONDAMAGE.
3721 			if (!PoisonDurationReceived) PoisonDamageReceived = 0;
3722 		}
3723 	}
3724 
3725 	assert (state != NULL);
3726 	if (state == NULL)
3727 	{
3728 		Destroy();
3729 		return;
3730 	}
3731 	if ((flags7 & MF7_HANDLENODELAY) && !(flags2 & MF2_DORMANT))
3732 	{
3733 		flags7 &= ~MF7_HANDLENODELAY;
3734 		if (state->GetNoDelay())
3735 		{
3736 			// For immediately spawned objects with the NoDelay flag set for their
3737 			// Spawn state, explicitly call the current state's function.
3738 			if (state->CallAction(this, this) && (ObjectFlags & OF_EuthanizeMe))
3739 				return;				// freed itself
3740 		}
3741 	}
3742 	// cycle through states, calling action functions at transitions
3743 	if (tics != -1)
3744 	{
3745 		// [RH] Use tics <= 0 instead of == 0 so that spawnstates
3746 		// of 0 tics work as expected.
3747 		if (--tics <= 0)
3748 		{
3749 			if (!SetState(state->GetNextState()))
3750 				return; 		// freed itself
3751 		}
3752 	}
3753 	else
3754 	{
3755 		int respawn_monsters = G_SkillProperty(SKILLP_Respawn);
3756 		// check for nightmare respawn
3757 		if (!(flags5 & MF5_ALWAYSRESPAWN))
3758 		{
3759 			if (!respawn_monsters || !(flags3 & MF3_ISMONSTER) || (flags2 & MF2_DORMANT) || (flags5 & MF5_NEVERRESPAWN))
3760 				return;
3761 
3762 			int limit = G_SkillProperty (SKILLP_RespawnLimit);
3763 			if (limit > 0 && skillrespawncount >= limit)
3764 				return;
3765 		}
3766 
3767 		movecount++;
3768 
3769 		if (movecount < respawn_monsters)
3770 			return;
3771 
3772 		if (level.time & 31)
3773 			return;
3774 
3775 		if (pr_nightmarerespawn() > 4)
3776 			return;
3777 
3778 		P_NightmareRespawn (this);
3779 	}
3780 }
3781 
3782 //==========================================================================
3783 //
3784 // AActor :: CheckSectorTransition
3785 //
3786 // Fire off some sector triggers if the actor has changed sectors.
3787 //
3788 //==========================================================================
3789 
CheckSectorTransition(sector_t * oldsec)3790 void AActor::CheckSectorTransition(sector_t *oldsec)
3791 {
3792 	if (oldsec != Sector)
3793 	{
3794 		if (oldsec->SecActTarget != NULL)
3795 		{
3796 			oldsec->SecActTarget->TriggerAction(this, SECSPAC_Exit);
3797 		}
3798 		if (Sector->SecActTarget != NULL)
3799 		{
3800 			int act = SECSPAC_Enter;
3801 			if (Z() <= Sector->floorplane.ZatPoint(this))
3802 			{
3803 				act |= SECSPAC_HitFloor;
3804 			}
3805 			if (Z() + height >= Sector->ceilingplane.ZatPoint(this))
3806 			{
3807 				act |= SECSPAC_HitCeiling;
3808 			}
3809 			if (Sector->heightsec != NULL && Z() == Sector->heightsec->floorplane.ZatPoint(this))
3810 			{
3811 				act |= SECSPAC_HitFakeFloor;
3812 			}
3813 			Sector->SecActTarget->TriggerAction(this, act);
3814 		}
3815 		if (Z() == floorz)
3816 		{
3817 			P_CheckFor3DFloorHit(this);
3818 		}
3819 		if (Top() == ceilingz)
3820 		{
3821 			P_CheckFor3DCeilingHit(this);
3822 		}
3823 	}
3824 }
3825 
3826 //==========================================================================
3827 //
3828 // AActor::UpdateWaterLevel
3829 //
3830 // Returns true if actor should splash
3831 //
3832 //==========================================================================
3833 
UpdateWaterLevel(fixed_t oldz,bool dosplash)3834 bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash)
3835 {
3836 	BYTE lastwaterlevel = waterlevel;
3837 	fixed_t fh = FIXED_MIN;
3838 	bool reset=false;
3839 
3840 	waterlevel = 0;
3841 
3842 	if (Sector == NULL)
3843 	{
3844 		return false;
3845 	}
3846 
3847 	if (Sector->MoreFlags & SECF_UNDERWATER)	// intentionally not SECF_UNDERWATERMASK
3848 	{
3849 		waterlevel = 3;
3850 	}
3851 	else
3852 	{
3853 		const sector_t *hsec = Sector->GetHeightSec();
3854 		if (hsec != NULL)
3855 		{
3856 			fh = hsec->floorplane.ZatPoint (this);
3857 			//if (hsec->MoreFlags & SECF_UNDERWATERMASK)	// also check Boom-style non-swimmable sectors
3858 			{
3859 				if (Z() < fh)
3860 				{
3861 					waterlevel = 1;
3862 					if (Z() + height/2 < fh)
3863 					{
3864 						waterlevel = 2;
3865 						if ((player && Z() + player->viewheight <= fh) ||
3866 							(Z() + height <= fh))
3867 						{
3868 							waterlevel = 3;
3869 						}
3870 					}
3871 				}
3872 				else if (!(hsec->MoreFlags & SECF_FAKEFLOORONLY) && (Top() > hsec->ceilingplane.ZatPoint (this)))
3873 				{
3874 					waterlevel = 3;
3875 				}
3876 				else
3877 				{
3878 					waterlevel = 0;
3879 				}
3880 			}
3881 			// even non-swimmable deep water must be checked here to do the splashes correctly
3882 			// But the water level must be reset when this function returns
3883 			if (!(hsec->MoreFlags&SECF_UNDERWATERMASK))
3884 			{
3885 				reset = true;
3886 			}
3887 		}
3888 		else
3889 		{
3890 			// Check 3D floors as well!
3891 			for(unsigned int i=0;i<Sector->e->XFloor.ffloors.Size();i++)
3892 			{
3893 				F3DFloor*  rover=Sector->e->XFloor.ffloors[i];
3894 
3895 				if (!(rover->flags & FF_EXISTS)) continue;
3896 				if(!(rover->flags & FF_SWIMMABLE) || rover->flags & FF_SOLID) continue;
3897 
3898 				fixed_t ff_bottom=rover->bottom.plane->ZatPoint(this);
3899 				fixed_t ff_top=rover->top.plane->ZatPoint(this);
3900 
3901 				if(ff_top <= Z() || ff_bottom > (Z() + (height >> 1))) continue;
3902 
3903 				fh=ff_top;
3904 				if (Z() < fh)
3905 				{
3906 					waterlevel = 1;
3907 					if (Z() + height/2 < fh)
3908 					{
3909 						waterlevel = 2;
3910 						if ((player && Z() + player->viewheight <= fh) ||
3911 							(Z() + height <= fh))
3912 						{
3913 							waterlevel = 3;
3914 						}
3915 					}
3916 				}
3917 
3918 				break;
3919 			}
3920 		}
3921 	}
3922 
3923 	// some additional checks to make deep sectors like Boom's splash without setting
3924 	// the water flags.
3925 	if (boomwaterlevel == 0 && waterlevel != 0 && dosplash)
3926 	{
3927 		P_HitWater(this, Sector, FIXED_MIN, FIXED_MIN, fh, true);
3928 	}
3929 	boomwaterlevel = waterlevel;
3930 	if (reset)
3931 	{
3932 		waterlevel = lastwaterlevel;
3933 	}
3934 	return false;	// we did the splash ourselves
3935 }
3936 
3937 
3938 //==========================================================================
3939 //
3940 // P_SpawnMobj
3941 //
3942 //==========================================================================
3943 
StaticSpawn(const PClass * type,fixed_t ix,fixed_t iy,fixed_t iz,replace_t allowreplacement,bool SpawningMapThing)3944 AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t iz, replace_t allowreplacement, bool SpawningMapThing)
3945 {
3946 	if (type == NULL)
3947 	{
3948 		I_Error ("Tried to spawn a class-less actor\n");
3949 	}
3950 
3951 	if (type->ActorInfo == NULL)
3952 	{
3953 		I_Error ("%s is not an actor\n", type->TypeName.GetChars());
3954 	}
3955 
3956 	if (allowreplacement)
3957 		type = type->GetReplacement();
3958 
3959 
3960 	AActor *actor;
3961 
3962 	actor = static_cast<AActor *>(const_cast<PClass *>(type)->CreateNew ());
3963 
3964 	// Set default dialogue
3965 	actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName);
3966 	if (actor->ConversationRoot != -1)
3967 	{
3968 		actor->Conversation = StrifeDialogues[actor->ConversationRoot];
3969 	}
3970 	else
3971 	{
3972 		actor->Conversation = NULL;
3973 	}
3974 
3975 	actor->PrevX = ix;
3976 	actor->PrevY = iy;
3977 	actor->PrevZ = iz;
3978 	actor->SetXYZ(ix, iy, iz);
3979 	actor->picnum.SetInvalid();
3980 	actor->health = actor->SpawnHealth();
3981 
3982 	// Actors with zero gravity need the NOGRAVITY flag set.
3983 	if (actor->gravity == 0) actor->flags |= MF_NOGRAVITY;
3984 
3985 	FRandom &rng = bglobal.m_Thinking ? pr_botspawnmobj : pr_spawnmobj;
3986 
3987 	if (actor->isFast() && actor->flags3 & MF3_ISMONSTER)
3988 		actor->reactiontime = 0;
3989 
3990 	if (actor->flags3 & MF3_ISMONSTER)
3991 	{
3992 		actor->LastLookPlayerNumber = rng() % MAXPLAYERS;
3993 		actor->TIDtoHate = 0;
3994 	}
3995 
3996 	// Set the state, but do not use SetState, because action
3997 	// routines can't be called yet.  If the spawnstate has an action
3998 	// routine, it will not be called.
3999 	FState *st = actor->SpawnState;
4000 	actor->state = st;
4001 	actor->tics = st->GetTics();
4002 
4003 	actor->sprite = st->sprite;
4004 	actor->frame = st->GetFrame();
4005 	actor->renderflags = (actor->renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (st->GetFullbright());
4006 	actor->touching_sectorlist = NULL;	// NULL head of sector list // phares 3/13/98
4007 	if (G_SkillProperty(SKILLP_FastMonsters))
4008 		actor->Speed = actor->GetClass()->Meta.GetMetaFixed(AMETA_FastSpeed, actor->Speed);
4009 	actor->DamageMultiply = FRACUNIT;
4010 
4011 
4012 	// set subsector and/or block links
4013 	actor->LinkToWorld (SpawningMapThing);
4014 
4015 	actor->dropoffz =			// killough 11/98: for tracking dropoffs
4016 	actor->floorz = actor->Sector->floorplane.ZatPoint (ix, iy);
4017 	actor->ceilingz = actor->Sector->ceilingplane.ZatPoint (ix, iy);
4018 
4019 	// The z-coordinate needs to be set once before calling P_FindFloorCeiling
4020 	// For FLOATRANDZ just use the floor here.
4021 	if (iz == ONFLOORZ || iz == FLOATRANDZ)
4022 	{
4023 		actor->SetZ(actor->floorz, false);
4024 	}
4025 	else if (iz == ONCEILINGZ)
4026 	{
4027 		actor->SetZ(actor->ceilingz - actor->height);
4028 	}
4029 
4030 	if (SpawningMapThing || !type->IsDescendantOf (RUNTIME_CLASS(APlayerPawn)))
4031 	{
4032 		// Check if there's something solid to stand on between the current position and the
4033 		// current sector's floor. For map spawns this must be delayed until after setting the
4034 		// z-coordinate.
4035 		if (!SpawningMapThing)
4036 		{
4037 			P_FindFloorCeiling(actor, FFCF_ONLYSPAWNPOS);
4038 		}
4039 		else
4040 		{
4041 			actor->floorsector = actor->Sector;
4042 			actor->floorpic = actor->floorsector->GetTexture(sector_t::floor);
4043 			actor->floorterrain = actor->floorsector->GetTerrain(sector_t::floor);
4044 			actor->ceilingsector = actor->Sector;
4045 			actor->ceilingpic = actor->ceilingsector->GetTexture(sector_t::ceiling);
4046 		}
4047 	}
4048 	else if (!(actor->flags5 & MF5_NOINTERACTION))
4049 	{
4050 		P_FindFloorCeiling (actor);
4051 	}
4052 	else
4053 	{
4054 		actor->floorpic = actor->Sector->GetTexture(sector_t::floor);
4055 		actor->floorterrain = actor->Sector->GetTerrain(sector_t::floor);
4056 		actor->floorsector = actor->Sector;
4057 		actor->ceilingpic = actor->Sector->GetTexture(sector_t::ceiling);
4058 		actor->ceilingsector = actor->Sector;
4059 	}
4060 
4061 	actor->SpawnPoint[0] = ix;
4062 	actor->SpawnPoint[1] = iy;
4063 
4064 	if (iz == ONFLOORZ)
4065 	{
4066 		actor->SetZ(actor->floorz);
4067 	}
4068 	else if (iz == ONCEILINGZ)
4069 	{
4070 		actor->SetZ(actor->ceilingz - actor->height);
4071 	}
4072 	else if (iz == FLOATRANDZ)
4073 	{
4074 		fixed_t space = actor->ceilingz - actor->height - actor->floorz;
4075 		if (space > 48*FRACUNIT)
4076 		{
4077 			space -= 40*FRACUNIT;
4078 			actor->SetZ(MulScale8 (space, rng()) + actor->floorz + 40*FRACUNIT);
4079 		}
4080 		else
4081 		{
4082 			actor->SetZ(actor->floorz);
4083 		}
4084 	}
4085 	else
4086 	{
4087 		actor->SpawnPoint[2] = (actor->Z() - actor->Sector->floorplane.ZatPoint(actor));
4088 	}
4089 
4090 	if (actor->FloatBobPhase == (BYTE)-1) actor->FloatBobPhase = rng();	// Don't make everything bob in sync (unless deliberately told to do)
4091 	if (actor->flags2 & MF2_FLOORCLIP)
4092 	{
4093 		actor->AdjustFloorClip ();
4094 	}
4095 	else
4096 	{
4097 		actor->floorclip = 0;
4098 	}
4099 	actor->UpdateWaterLevel (actor->Z(), false);
4100 	if (!SpawningMapThing)
4101 	{
4102 		actor->BeginPlay ();
4103 		if (actor->ObjectFlags & OF_EuthanizeMe)
4104 		{
4105 			return NULL;
4106 		}
4107 	}
4108 	if (level.flags & LEVEL_NOALLIES && !actor->player)
4109 	{
4110 		actor->flags &= ~MF_FRIENDLY;
4111 	}
4112 	// [RH] Count monsters whenever they are spawned.
4113 	if (actor->CountsAsKill())
4114 	{
4115 		level.total_monsters++;
4116 	}
4117 	// [RH] Same, for items
4118 	if (actor->flags & MF_COUNTITEM)
4119 	{
4120 		level.total_items++;
4121 	}
4122 	// And for secrets
4123 	if (actor->flags5 & MF5_COUNTSECRET)
4124 	{
4125 		level.total_secrets++;
4126 	}
4127 	return actor;
4128 }
4129 
Spawn(const char * type,fixed_t x,fixed_t y,fixed_t z,replace_t allowreplacement)4130 AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
4131 {
4132 	FName classname(type, true);
4133 	if (classname == NAME_None)
4134 	{
4135 		I_Error("Attempt to spawn actor of unknown type '%s'\n", type);
4136 	}
4137 	return Spawn(classname, x, y, z, allowreplacement);
4138 }
4139 
Spawn(FName classname,fixed_t x,fixed_t y,fixed_t z,replace_t allowreplacement)4140 AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
4141 {
4142 	const PClass *cls = PClass::FindClass(classname);
4143 	if (cls == NULL)
4144 	{
4145 		I_Error("Attempt to spawn actor of unknown type '%s'\n", classname.GetChars());
4146 	}
4147 	return AActor::StaticSpawn (cls, x, y, z, allowreplacement);
4148 }
4149 
LevelSpawned()4150 void AActor::LevelSpawned ()
4151 {
4152 	if (tics > 0 && !(flags4 & MF4_SYNCHRONIZED))
4153 	{
4154 		tics = 1 + (pr_spawnmapthing() % tics);
4155 	}
4156 	// [RH] Clear MF_DROPPED flag if the default version doesn't have it set.
4157 	// (AInventory::BeginPlay() makes all inventory items spawn with it set.)
4158 	if (!(GetDefault()->flags & MF_DROPPED))
4159 	{
4160 		flags &= ~MF_DROPPED;
4161 	}
4162 	HandleSpawnFlags ();
4163 }
4164 
HandleSpawnFlags()4165 void AActor::HandleSpawnFlags ()
4166 {
4167 	if (SpawnFlags & MTF_AMBUSH)
4168 	{
4169 		flags |= MF_AMBUSH;
4170 	}
4171 	if (SpawnFlags & MTF_DORMANT)
4172 	{
4173 		Deactivate (NULL);
4174 	}
4175 	if (SpawnFlags & MTF_STANDSTILL)
4176 	{
4177 		flags4 |= MF4_STANDSTILL;
4178 	}
4179 	if (SpawnFlags & MTF_FRIENDLY)
4180 	{
4181 		flags |= MF_FRIENDLY;
4182 		// Friendlies don't count as kills!
4183 		if (flags & MF_COUNTKILL)
4184 		{
4185 			flags &= ~MF_COUNTKILL;
4186 			level.total_monsters--;
4187 		}
4188 	}
4189 	if (SpawnFlags & MTF_SHADOW)
4190 	{
4191 		flags |= MF_SHADOW;
4192 		RenderStyle = STYLE_Translucent;
4193 		alpha = TRANSLUC25;
4194 	}
4195 	else if (SpawnFlags & MTF_ALTSHADOW)
4196 	{
4197 		RenderStyle = STYLE_None;
4198 	}
4199 	if (SpawnFlags & MTF_SECRET)
4200 	{
4201 		if (!(flags5 & MF5_COUNTSECRET))
4202 		{
4203 			//Printf("Secret %s in sector %i!\n", GetTag(), Sector->sectornum);
4204 			flags5 |= MF5_COUNTSECRET;
4205 			level.total_secrets++;
4206 		}
4207 	}
4208 }
4209 
BeginPlay()4210 void AActor::BeginPlay ()
4211 {
4212 	// If the actor is spawned with the dormant flag set, clear it, and use
4213 	// the normal deactivation logic to make it properly dormant.
4214 	if (flags2 & MF2_DORMANT)
4215 	{
4216 		flags2 &= ~MF2_DORMANT;
4217 		Deactivate (NULL);
4218 	}
4219 }
4220 
PostBeginPlay()4221 void AActor::PostBeginPlay ()
4222 {
4223 	if (Renderer != NULL)
4224 	{
4225 		Renderer->StateChanged(this);
4226 	}
4227 	PrevAngle = angle;
4228 	flags7 |= MF7_HANDLENODELAY;
4229 }
4230 
MarkPrecacheSounds() const4231 void AActor::MarkPrecacheSounds() const
4232 {
4233 	SeeSound.MarkUsed();
4234 	AttackSound.MarkUsed();
4235 	PainSound.MarkUsed();
4236 	DeathSound.MarkUsed();
4237 	ActiveSound.MarkUsed();
4238 	UseSound.MarkUsed();
4239 	BounceSound.MarkUsed();
4240 	WallBounceSound.MarkUsed();
4241 	CrushPainSound.MarkUsed();
4242 }
4243 
isFast()4244 bool AActor::isFast()
4245 {
4246 	if (flags5&MF5_ALWAYSFAST) return true;
4247 	if (flags5&MF5_NEVERFAST) return false;
4248 	return !!G_SkillProperty(SKILLP_FastMonsters);
4249 }
4250 
isSlow()4251 bool AActor::isSlow()
4252 {
4253 	return !!G_SkillProperty(SKILLP_SlowMonsters);
4254 }
4255 
Activate(AActor * activator)4256 void AActor::Activate (AActor *activator)
4257 {
4258 	if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE)))
4259 	{
4260 		if (flags2 & MF2_DORMANT)
4261 		{
4262 			flags2 &= ~MF2_DORMANT;
4263 			FState *state = FindState(NAME_Active);
4264 			if (state != NULL)
4265 			{
4266 				SetState(state);
4267 			}
4268 			else
4269 			{
4270 				tics = 1;
4271 			}
4272 		}
4273 	}
4274 }
4275 
Deactivate(AActor * activator)4276 void AActor::Deactivate (AActor *activator)
4277 {
4278 	if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE)))
4279 	{
4280 		if (!(flags2 & MF2_DORMANT))
4281 		{
4282 			flags2 |= MF2_DORMANT;
4283 			FState *state = FindState(NAME_Inactive);
4284 			if (state != NULL)
4285 			{
4286 				SetState(state);
4287 			}
4288 			else
4289 			{
4290 				tics = -1;
4291 			}
4292 		}
4293 	}
4294 }
4295 
4296 //
4297 // P_RemoveMobj
4298 //
4299 
Destroy()4300 void AActor::Destroy ()
4301 {
4302 	// [RH] Destroy any inventory this actor is carrying
4303 	DestroyAllInventory ();
4304 
4305 	// [RH] Unlink from tid chain
4306 	RemoveFromHash ();
4307 
4308 	// unlink from sector and block lists
4309 	UnlinkFromWorld ();
4310 	flags |= MF_NOSECTOR|MF_NOBLOCKMAP;
4311 
4312 	// Delete all nodes on the current sector_list			phares 3/16/98
4313 	P_DelSector_List();
4314 
4315 	// Transform any playing sound into positioned, non-actor sounds.
4316 	S_RelinkSound (this, NULL);
4317 
4318 	Super::Destroy ();
4319 }
4320 
4321 //===========================================================================
4322 //
4323 // AdjustFloorClip
4324 //
4325 //===========================================================================
4326 
AdjustFloorClip()4327 void AActor::AdjustFloorClip ()
4328 {
4329 	if (flags3 & MF3_SPECIALFLOORCLIP)
4330 	{
4331 		return;
4332 	}
4333 
4334 	fixed_t oldclip = floorclip;
4335 	fixed_t shallowestclip = FIXED_MAX;
4336 	const msecnode_t *m;
4337 
4338 	// possibly standing on a 3D-floor
4339 	if (Sector->e->XFloor.ffloors.Size() && Z()>Sector->floorplane.ZatPoint(this)) floorclip=0;
4340 
4341 	// [RH] clip based on shallowest floor player is standing on
4342 	// If the sector has a deep water effect, then let that effect
4343 	// do the floorclipping instead of the terrain type.
4344 	for (m = touching_sectorlist; m; m = m->m_tnext)
4345 	{
4346 		sector_t *hsec = m->m_sector->GetHeightSec();
4347 		if (hsec == NULL && m->m_sector->floorplane.ZatPoint (this) == Z())
4348 		{
4349 			fixed_t clip = Terrains[m->m_sector->GetTerrain(sector_t::floor)].FootClip;
4350 			if (clip < shallowestclip)
4351 			{
4352 				shallowestclip = clip;
4353 			}
4354 		}
4355 	}
4356 	if (shallowestclip == FIXED_MAX)
4357 	{
4358 		floorclip = 0;
4359 	}
4360 	else
4361 	{
4362 		floorclip = shallowestclip;
4363 	}
4364 	if (player && player->mo == this && oldclip != floorclip)
4365 	{
4366 		player->viewheight -= oldclip - floorclip;
4367 		player->deltaviewheight = player->GetDeltaViewHeight();
4368 	}
4369 }
4370 
4371 //
4372 // P_SpawnPlayer
4373 // Called when a player is spawned on the level.
4374 // Most of the player structure stays unchanged between levels.
4375 //
4376 EXTERN_CVAR (Bool, chasedemo)
4377 
4378 extern bool demonew;
4379 
P_SpawnPlayer(FPlayerStart * mthing,int playernum,int flags)4380 APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
4381 {
4382 	player_t *p;
4383 	APlayerPawn *mobj, *oldactor;
4384 	BYTE	  state;
4385 	fixed_t spawn_x, spawn_y, spawn_z;
4386 	angle_t spawn_angle;
4387 
4388 	// not playing?
4389 	if ((unsigned)playernum >= (unsigned)MAXPLAYERS || !playeringame[playernum])
4390 		return NULL;
4391 
4392 	// Old lerp data needs to go
4393 	if (playernum == consoleplayer)
4394 	{
4395 		P_PredictionLerpReset();
4396 	}
4397 
4398 	p = &players[playernum];
4399 
4400 	if (p->cls == NULL)
4401 	{
4402 		// [GRB] Pick a class from player class list
4403 		if (PlayerClasses.Size () > 1)
4404 		{
4405 			int type;
4406 
4407 			if (!deathmatch || !multiplayer)
4408 			{
4409 				type = SinglePlayerClass[playernum];
4410 			}
4411 			else
4412 			{
4413 				type = p->userinfo.GetPlayerClassNum();
4414 				if (type < 0)
4415 				{
4416 					type = pr_multiclasschoice() % PlayerClasses.Size ();
4417 				}
4418 			}
4419 			p->CurrentPlayerClass = type;
4420 		}
4421 		else
4422 		{
4423 			p->CurrentPlayerClass = 0;
4424 		}
4425 		p->cls = PlayerClasses[p->CurrentPlayerClass].Type;
4426 	}
4427 
4428 	if (( dmflags2 & DF2_SAME_SPAWN_SPOT ) &&
4429 		( p->playerstate == PST_REBORN ) &&
4430 		( deathmatch == false ) &&
4431 		( gameaction != ga_worlddone ) &&
4432 		( p->mo != NULL ) &&
4433 		( !(p->mo->Sector->Flags & SECF_NORESPAWN) ) &&
4434 		( NULL != p->attacker ) &&							// don't respawn on damaging floors
4435 		( p->mo->Sector->damageamount < TELEFRAG_DAMAGE ))	// this really should be a bit smarter...
4436 	{
4437 		spawn_x = p->mo->X();
4438 		spawn_y = p->mo->Y();
4439 		spawn_z = p->mo->Z();
4440 
4441 		spawn_angle = p->mo->angle;
4442 	}
4443 	else
4444 	{
4445 		spawn_x = mthing->x;
4446 		spawn_y = mthing->y;
4447 
4448 		// Allow full angular precision but avoid roundoff errors for multiples of 45 degrees.
4449 		if (mthing->angle % 45 != 0)
4450 		{
4451 			spawn_angle = mthing->angle * (ANG45 / 45);
4452 		}
4453 		else
4454 		{
4455 			spawn_angle = ANG45 * (mthing->angle / 45);
4456 		}
4457 		if (i_compatflags2 & COMPATF2_BADANGLES)
4458 		{
4459 			spawn_angle += 1 << ANGLETOFINESHIFT;
4460 		}
4461 
4462 		if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING)
4463 			spawn_z = ONCEILINGZ;
4464 		else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT)
4465 			spawn_z = FLOATRANDZ;
4466 		else
4467 			spawn_z = ONFLOORZ;
4468 	}
4469 
4470 	mobj = static_cast<APlayerPawn *>
4471 		(Spawn (p->cls, spawn_x, spawn_y, spawn_z, NO_REPLACE));
4472 
4473 	if (level.flags & LEVEL_USEPLAYERSTARTZ)
4474 	{
4475 		if (spawn_z == ONFLOORZ)
4476 			mobj->AddZ(mthing->z);
4477 		else if (spawn_z == ONCEILINGZ)
4478 			mobj->AddZ(-mthing->z);
4479 		P_FindFloorCeiling(mobj, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
4480 	}
4481 
4482 	mobj->FriendPlayer = playernum + 1;	// [RH] players are their own friends
4483 	oldactor = p->mo;
4484 	p->mo = mobj;
4485 	mobj->player = p;
4486 	state = p->playerstate;
4487 	if (state == PST_REBORN || state == PST_ENTER)
4488 	{
4489 		G_PlayerReborn (playernum);
4490 	}
4491 	else if (oldactor != NULL && oldactor->player == p && !(flags & SPF_TEMPPLAYER))
4492 	{
4493 		// Move the voodoo doll's inventory to the new player.
4494 		mobj->ObtainInventory (oldactor);
4495 		FBehavior::StaticStopMyScripts (oldactor);	// cancel all ENTER/RESPAWN scripts for the voodoo doll
4496 	}
4497 
4498 	// [GRB] Reset skin
4499 	p->userinfo.SkinNumChanged(R_FindSkin (skins[p->userinfo.GetSkin()].name, p->CurrentPlayerClass));
4500 
4501 	if (!(mobj->flags2 & MF2_DONTTRANSLATE))
4502 	{
4503 		// [RH] Be sure the player has the right translation
4504 		R_BuildPlayerTranslation (playernum);
4505 
4506 		// [RH] set color translations for player sprites
4507 		mobj->Translation = TRANSLATION(TRANSLATION_Players,playernum);
4508 	}
4509 
4510 	mobj->angle = spawn_angle;
4511 	mobj->pitch = mobj->roll = 0;
4512 	mobj->health = p->health;
4513 
4514 	// [RH] Set player sprite based on skin
4515 	if (!(mobj->flags4 & MF4_NOSKIN))
4516 	{
4517 		mobj->sprite = skins[p->userinfo.GetSkin()].sprite;
4518 	}
4519 
4520 	p->DesiredFOV = p->FOV = 90.f;
4521 	p->camera = p->mo;
4522 	p->playerstate = PST_LIVE;
4523 	p->refire = 0;
4524 	p->damagecount = 0;
4525 	p->bonuscount = 0;
4526 	p->morphTics = 0;
4527 	p->MorphedPlayerClass = 0;
4528 	p->MorphStyle = 0;
4529 	p->MorphExitFlash = NULL;
4530 	p->extralight = 0;
4531 	p->fixedcolormap = NOFIXEDCOLORMAP;
4532 	p->fixedlightlevel = -1;
4533 	p->viewheight = mobj->ViewHeight;
4534 	p->inconsistant = 0;
4535 	p->attacker = NULL;
4536 	p->spreecount = 0;
4537 	p->multicount = 0;
4538 	p->lastkilltime = 0;
4539 	p->BlendR = p->BlendG = p->BlendB = p->BlendA = 0.f;
4540 	p->mo->ResetAirSupply(false);
4541 	p->Uncrouch();
4542 	p->MinPitch = p->MaxPitch = 0;	// will be filled in by PostBeginPlay()/netcode
4543 	p->MUSINFOactor = NULL;
4544 	p->MUSINFOtics = -1;
4545 
4546 	p->velx = p->vely = 0;		// killough 10/98: initialize bobbing to 0.
4547 
4548 	for (int ii = 0; ii < MAXPLAYERS; ++ii)
4549 	{
4550 		if (playeringame[ii] && players[ii].camera == oldactor)
4551 		{
4552 			players[ii].camera = mobj;
4553 		}
4554 	}
4555 
4556 	// [RH] Allow chasecam for demo watching
4557 	if ((demoplayback || demonew) && chasedemo)
4558 		p->cheats = CF_CHASECAM;
4559 
4560 	// setup gun psprite
4561 	if (!(flags & SPF_TEMPPLAYER))
4562 	{ // This can also start a script so don't do it for the dummy player.
4563 		P_SetupPsprites (p, !!(flags & SPF_WEAPONFULLYUP));
4564 	}
4565 
4566 	if (deathmatch)
4567 	{ // Give all cards in death match mode.
4568 		p->mo->GiveDeathmatchInventory ();
4569 	}
4570 	else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) && state == PST_REBORN && oldactor != NULL)
4571 	{ // Special inventory handling for respawning in coop
4572 		p->mo->FilterCoopRespawnInventory (oldactor);
4573 	}
4574 	if (oldactor != NULL)
4575 	{ // Remove any inventory left from the old actor. Coop handles
4576 	  // it above, but the other modes don't.
4577 		oldactor->DestroyAllInventory();
4578 	}
4579 	// [BC] Handle temporary invulnerability when respawned
4580 	if ((state == PST_REBORN || state == PST_ENTER) &&
4581 		(dmflags2 & DF2_YES_RESPAWN_INVUL) &&
4582 		(multiplayer || alwaysapplydmflags))
4583 	{
4584 		APowerup *invul = static_cast<APowerup*>(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable)));
4585 		invul->EffectTics = 3*TICRATE;
4586 		invul->BlendColor = 0;			// don't mess with the view
4587 		invul->ItemFlags |= IF_UNDROPPABLE;	// Don't drop this
4588 		p->mo->effects |= FX_RESPAWNINVUL;	// [RH] special effect
4589 	}
4590 
4591 	if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum))
4592 	{
4593 		StatusBar->AttachToPlayer (p);
4594 	}
4595 
4596 	if (multiplayer)
4597 	{
4598 		unsigned an = mobj->angle >> ANGLETOFINESHIFT;
4599 		Spawn ("TeleportFog", mobj->Vec3Offset(20*finecosine[an], 20*finesine[an], TELEFOGHEIGHT), ALLOW_REPLACE);
4600 	}
4601 
4602 	// "Fix" for one of the starts on exec.wad MAP01: If you start inside the ceiling,
4603 	// drop down below it, even if that means sinking into the floor.
4604 	if (mobj->Top() > mobj->ceilingz)
4605 	{
4606 		mobj->SetZ(mobj->ceilingz - mobj->height, false);
4607 	}
4608 
4609 	// [BC] Do script stuff
4610 	if (!(flags & SPF_TEMPPLAYER))
4611 	{
4612 		if (state == PST_ENTER || (state == PST_LIVE && !savegamerestore))
4613 		{
4614 			FBehavior::StaticStartTypedScripts (SCRIPT_Enter, p->mo, true);
4615 		}
4616 		else if (state == PST_REBORN)
4617 		{
4618 			assert (oldactor != NULL);
4619 
4620 			// before relocating all pointers to the player all sound targets
4621 			// pointing to the old actor have to be NULLed. Otherwise all
4622 			// monsters who last targeted this player will wake up immediately
4623 			// after the player has respawned.
4624 			AActor *th;
4625 			TThinkerIterator<AActor> it;
4626 			while ((th = it.Next()))
4627 			{
4628 				if (th->LastHeard == oldactor) th->LastHeard = NULL;
4629 			}
4630 			for(int i = 0; i < numsectors; i++)
4631 			{
4632 				if (sectors[i].SoundTarget == oldactor) sectors[i].SoundTarget = NULL;
4633 			}
4634 
4635 			DObject::StaticPointerSubstitution (oldactor, p->mo);
4636 			// PointerSubstitution() will also affect the bodyque, so undo that now.
4637 			for (int ii=0; ii < BODYQUESIZE; ++ii)
4638 				if (bodyque[ii] == p->mo)
4639 					bodyque[ii] = oldactor;
4640 			FBehavior::StaticStartTypedScripts (SCRIPT_Respawn, p->mo, true);
4641 		}
4642 	}
4643 	return mobj;
4644 }
4645 
4646 
4647 //
4648 // P_SpawnMapThing
4649 // The fields of the mapthing should
4650 // already be in host byte order.
4651 //
4652 // [RH] position is used to weed out unwanted start spots
P_SpawnMapThing(FMapThing * mthing,int position)4653 AActor *P_SpawnMapThing (FMapThing *mthing, int position)
4654 {
4655 	const PClass *i;
4656 	int mask;
4657 	AActor *mobj;
4658 	fixed_t x, y, z;
4659 
4660 	if (mthing->EdNum == 0 || mthing->EdNum == -1)
4661 		return NULL;
4662 
4663 	// find which type to spawn
4664 	FDoomEdEntry *mentry = mthing->info;
4665 
4666 	if (mentry == NULL)
4667 	{
4668 		// [RH] Don't die if the map tries to spawn an unknown thing
4669 		Printf ("Unknown type %i at (%i, %i)\n",
4670 				 mthing->EdNum,
4671 				 mthing->x>>FRACBITS, mthing->y>>FRACBITS);
4672 		mentry = DoomEdMap.CheckKey(0);
4673 		if (mentry == NULL)	// we need a valid entry for the rest of this function so if we can't find a default, let's exit right away.
4674 		{
4675 			return NULL;
4676 		}
4677 	}
4678 	if (mentry->Type == NULL && mentry->Special <= 0)
4679 	{
4680 		// has been explicitly set to not spawning anything.
4681 		return NULL;
4682 	}
4683 
4684 	// copy args to mapthing so that we have them in one place for the rest of this function
4685 	if (mentry->ArgsDefined > 0)
4686 	{
4687 		if (mentry->Type!= NULL) mthing->special = mentry->Special;
4688 		memcpy(mthing->args, mentry->Args, sizeof(mthing->args[0]) * mentry->ArgsDefined);
4689 	}
4690 
4691 	int pnum = -1;
4692 	if (mentry->Type == NULL)
4693 	{
4694 
4695 		switch (mentry->Special)
4696 		{
4697 		case SMT_DeathmatchStart:
4698 		{
4699 			// count deathmatch start positions
4700 			FPlayerStart start(mthing, 0);
4701 			deathmatchstarts.Push(start);
4702 			return NULL;
4703 		}
4704 
4705 		case SMT_PolyAnchor:
4706 		case SMT_PolySpawn:
4707 		case SMT_PolySpawnCrush:
4708 		case SMT_PolySpawnHurt:
4709 		{
4710 			polyspawns_t *polyspawn = new polyspawns_t;
4711 			polyspawn->next = polyspawns;
4712 			polyspawn->x = mthing->x;
4713 			polyspawn->y = mthing->y;
4714 			polyspawn->angle = mthing->angle;
4715 			polyspawn->type = mentry->Special;
4716 			polyspawns = polyspawn;
4717 			if (mentry->Special != SMT_PolyAnchor)
4718 				po_NumPolyobjs++;
4719 			return NULL;
4720 		}
4721 
4722 		case SMT_Player1Start:
4723 		case SMT_Player2Start:
4724 		case SMT_Player3Start:
4725 		case SMT_Player4Start:
4726 		case SMT_Player5Start:
4727 		case SMT_Player6Start:
4728 		case SMT_Player7Start:
4729 		case SMT_Player8Start:
4730 			pnum = mentry->Special - SMT_Player1Start;
4731 			break;
4732 
4733 		// Sound sequence override will be handled later
4734 		default:
4735 			break;
4736 
4737 		}
4738 	}
4739 
4740 	if (pnum == -1 || (level.flags & LEVEL_FILTERSTARTS))
4741 	{
4742 		// check for appropriate game type
4743 		if (deathmatch)
4744 		{
4745 			mask = MTF_DEATHMATCH;
4746 		}
4747 		else if (multiplayer)
4748 		{
4749 			mask = MTF_COOPERATIVE;
4750 		}
4751 		else
4752 		{
4753 			mask = MTF_SINGLE;
4754 		}
4755 		if (!(mthing->flags & mask))
4756 		{
4757 			return NULL;
4758 		}
4759 
4760 		mask = G_SkillProperty(SKILLP_SpawnFilter);
4761 		if (!(mthing->SkillFilter & mask))
4762 		{
4763 			return NULL;
4764 		}
4765 
4766 		// Check class spawn masks. Now with player classes available
4767 		// this is enabled for all games.
4768 		if (!multiplayer)
4769 		{ // Single player
4770 			int spawnmask = players[consoleplayer].GetSpawnClass();
4771 			if (spawnmask != 0 && (mthing->ClassFilter & spawnmask) == 0)
4772 			{ // Not for current class
4773 				return NULL;
4774 			}
4775 		}
4776 		else if (!deathmatch)
4777 		{ // Cooperative
4778 			mask = 0;
4779 			for (int i = 0; i < MAXPLAYERS; i++)
4780 			{
4781 				if (playeringame[i])
4782 				{
4783 					int spawnmask = players[i].GetSpawnClass();
4784 					if (spawnmask != 0)
4785 						mask |= spawnmask;
4786 					else
4787 						mask = -1;
4788 				}
4789 			}
4790 			if (mask != -1 && (mthing->ClassFilter & mask) == 0)
4791 			{
4792 				return NULL;
4793 			}
4794 		}
4795 	}
4796 
4797 	if (pnum != -1)
4798 	{
4799 		// [RH] Only spawn spots that match position.
4800 		if (mthing->args[0] != position)
4801 			return NULL;
4802 
4803 		// save spots for respawning in network games
4804 		FPlayerStart start(mthing, pnum+1);
4805 		playerstarts[pnum] = start;
4806 		AllPlayerStarts.Push(start);
4807 		if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS))
4808 		{
4809 			return P_SpawnPlayer(&start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
4810 		}
4811 		return NULL;
4812 	}
4813 
4814 	// [RH] sound sequence overriders
4815 	if (mentry->Type == NULL && mentry->Special == SMT_SSeqOverride)
4816 	{
4817 		int type = mthing->args[0];
4818 		if (type == 255) type = -1;
4819 		if (type > 63)
4820 		{
4821 			Printf ("Sound sequence %d out of range\n", type);
4822 		}
4823 		else
4824 		{
4825 			P_PointInSector (mthing->x,	mthing->y)->seqType = type;
4826 		}
4827 		return NULL;
4828 	}
4829 
4830 	// [RH] If the thing's corresponding sprite has no frames, also map
4831 	//		it to the unknown thing.
4832 	// Handle decorate replacements explicitly here
4833 	// to check for missing frames in the replacement object.
4834 	i = mentry->Type->GetReplacement();
4835 
4836 	const AActor *defaults = GetDefaultByType (i);
4837 	if (defaults->SpawnState == NULL ||
4838 		sprites[defaults->SpawnState->sprite].numframes == 0)
4839 	{
4840 		// We don't load mods for shareware games so we'll just ignore
4841 		// missing actors. Heretic needs this since the shareware includes
4842 		// the retail weapons in Deathmatch.
4843 		if (gameinfo.flags & GI_SHAREWARE)
4844 			return NULL;
4845 
4846 		Printf ("%s at (%i, %i) has no frames\n",
4847 				i->TypeName.GetChars(), mthing->x>>FRACBITS, mthing->y>>FRACBITS);
4848 		i = PClass::FindClass("Unknown");
4849 	}
4850 
4851 	const AActor *info = GetDefaultByType (i);
4852 
4853 	// don't spawn keycards and players in deathmatch
4854 	if (deathmatch && info->flags & MF_NOTDMATCH)
4855 		return NULL;
4856 
4857 	// [RH] don't spawn extra weapons in coop if so desired
4858 	if (multiplayer && !deathmatch && (dmflags & DF_NO_COOP_WEAPON_SPAWN))
4859 	{
4860 		if (GetDefaultByType(i)->flags7 & MF7_WEAPONSPAWN)
4861 		{
4862 			if ((mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH)
4863 				return NULL;
4864 		}
4865 	}
4866 
4867 	// don't spawn any monsters if -nomonsters
4868 	if (((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER )
4869 	{
4870 		return NULL;
4871 	}
4872 
4873 	// [RH] Other things that shouldn't be spawned depending on dmflags
4874 	if (deathmatch || alwaysapplydmflags)
4875 	{
4876 		if (dmflags & DF_NO_HEALTH)
4877 		{
4878 			if (i->IsDescendantOf (RUNTIME_CLASS(AHealth)))
4879 				return NULL;
4880 			if (i->TypeName == NAME_Berserk)
4881 				return NULL;
4882 			if (i->TypeName == NAME_Megasphere)
4883 				return NULL;
4884 		}
4885 		if (dmflags & DF_NO_ITEMS)
4886 		{
4887 //			if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact)))
4888 //				return;
4889 		}
4890 		if (dmflags & DF_NO_ARMOR)
4891 		{
4892 			if (i->IsDescendantOf (RUNTIME_CLASS(AArmor)))
4893 				return NULL;
4894 			if (i->TypeName == NAME_Megasphere)
4895 				return NULL;
4896 		}
4897 	}
4898 
4899 	// spawn it
4900 	x = mthing->x;
4901 	y = mthing->y;
4902 
4903 	if (info->flags & MF_SPAWNCEILING)
4904 		z = ONCEILINGZ;
4905 	else if (info->flags2 & MF2_SPAWNFLOAT)
4906 		z = FLOATRANDZ;
4907 	else
4908 		z = ONFLOORZ;
4909 
4910 	mobj = AActor::StaticSpawn (i, x, y, z, NO_REPLACE, true);
4911 
4912 	if (z == ONFLOORZ)
4913 	{
4914 		mobj->AddZ(mthing->z);
4915 		if ((mobj->flags2 & MF2_FLOATBOB) && (ib_compatflags & BCOMPATF_FLOATBOB))
4916 		{
4917 			mobj->special1 = mthing->z;
4918 		}
4919 	}
4920 	else if (z == ONCEILINGZ)
4921 		mobj->AddZ(-mthing->z);
4922 
4923 	mobj->SpawnPoint[0] = mthing->x;
4924 	mobj->SpawnPoint[1] = mthing->y;
4925 	mobj->SpawnPoint[2] = mthing->z;
4926 	mobj->SpawnAngle = mthing->angle;
4927 	mobj->SpawnFlags = mthing->flags;
4928 	if (mthing->FloatbobPhase >= 0 && mthing->FloatbobPhase < 64) mobj->FloatBobPhase = mthing->FloatbobPhase;
4929 	if (mthing->gravity < 0) mobj->gravity = -mthing->gravity;
4930 	else if (mthing->gravity > 0) mobj->gravity = FixedMul(mobj->gravity, mthing->gravity);
4931 	else mobj->flags &= ~MF_NOGRAVITY;
4932 
4933 	// For Hexen floatbob 'compatibility' we do not really want to alter the floorz.
4934 	if (mobj->special1 == 0 || !(mobj->flags2 & MF2_FLOATBOB) || !(ib_compatflags & BCOMPATF_FLOATBOB))
4935 	{
4936 		P_FindFloorCeiling(mobj, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
4937 	}
4938 
4939 	// if the actor got args defined either in DECORATE or MAPINFO we must ignore the map's properties.
4940 	if (!(mobj->flags2 & MF2_ARGSDEFINED))
4941 	{
4942 		// [RH] Set the thing's special
4943 		mobj->special = mthing->special;
4944 		for(int j=0;j<5;j++) mobj->args[j]=mthing->args[j];
4945 	}
4946 
4947 	// [RH] Add ThingID to mobj and link it in with the others
4948 	mobj->tid = mthing->thingid;
4949 	mobj->AddToHash ();
4950 
4951 	mobj->PrevAngle = mobj->angle = (DWORD)((mthing->angle * CONST64(0x100000000)) / 360);
4952 
4953 	// Check if this actor's mapthing has a conversation defined
4954 	if (mthing->Conversation > 0)
4955 	{
4956 		// Make sure that this does not partially overwrite the default dialogue settings.
4957 		int root = GetConversation(mthing->Conversation);
4958 		if (root != -1)
4959 		{
4960 			mobj->ConversationRoot = root;
4961 			mobj->Conversation = StrifeDialogues[mobj->ConversationRoot];
4962 		}
4963 	}
4964 
4965 	// Set various UDMF options
4966 	if (mthing->alpha != -1)
4967 		mobj->alpha = mthing->alpha;
4968 	if (mthing->RenderStyle != STYLE_Count)
4969 		mobj->RenderStyle = (ERenderStyle)mthing->RenderStyle;
4970 	if (mthing->scaleX)
4971 		mobj->scaleX = FixedMul(mthing->scaleX, mobj->scaleX);
4972 	if (mthing->scaleY)
4973 		mobj->scaleY = FixedMul(mthing->scaleY, mobj->scaleY);
4974 	if (mthing->pitch)
4975 		mobj->pitch = ANGLE_1 * mthing->pitch;
4976 	if (mthing->roll)
4977 		mobj->roll = ANGLE_1 * mthing->roll;
4978 	if (mthing->score)
4979 		mobj->Score = mthing->score;
4980 	if (mthing->fillcolor)
4981 		mobj->fillcolor = mthing->fillcolor;
4982 
4983 	mobj->BeginPlay ();
4984 	if (!(mobj->ObjectFlags & OF_EuthanizeMe))
4985 	{
4986 		mobj->LevelSpawned ();
4987 	}
4988 
4989 	if (mthing->health > 0)
4990 		mobj->health *= mthing->health;
4991 	else
4992 		mobj->health = -mthing->health;
4993 	if (mthing->health == 0)
4994 		mobj->Die(NULL, NULL);
4995 	else if (mthing->health != 1)
4996 		mobj->StartHealth = mobj->health;
4997 
4998 	return mobj;
4999 }
5000 
5001 
5002 
5003 //
5004 // GAME SPAWN FUNCTIONS
5005 //
5006 
5007 
5008 //
5009 // P_SpawnPuff
5010 //
5011 
P_SpawnPuff(AActor * source,const PClass * pufftype,fixed_t x,fixed_t y,fixed_t z,angle_t dir,int updown,int flags,AActor * vict)5012 AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict)
5013 {
5014 	AActor *puff;
5015 
5016 	if (!(flags & PF_NORANDOMZ))
5017 		z += pr_spawnpuff.Random2 () << 10;
5018 
5019 	puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE);
5020 	if (puff == NULL) return NULL;
5021 
5022 	if ((puff->flags4 & MF4_RANDOMIZE) && puff->tics > 0)
5023 	{
5024 		puff->tics -= pr_spawnpuff() & 3;
5025 		if (puff->tics < 1)
5026 			puff->tics = 1;
5027 	}
5028 
5029 	//Moved puff creation and target/master/tracer setting to here.
5030 	if (puff && vict)
5031 	{
5032 		if (puff->flags7 & MF7_HITTARGET)	puff->target = vict;
5033 		if (puff->flags7 & MF7_HITMASTER)	puff->master = vict;
5034 		if (puff->flags7 & MF7_HITTRACER)	puff->tracer = vict;
5035 	}
5036 	// [BB] If the puff came from a player, set the target of the puff to this player.
5037 	if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER))
5038 		puff->target = source;
5039 
5040 
5041 	if (source != NULL) puff->angle = puff->AngleTo(source);
5042 
5043 	// If a puff has a crash state and an actor was not hit,
5044 	// it will enter the crash state. This is used by the StrifeSpark
5045 	// and BlasterPuff.
5046 	FState *crashstate;
5047 	if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL)
5048 	{
5049 		puff->SetState (crashstate);
5050 	}
5051 	else if ((flags & PF_HITTHINGBLEED) && (crashstate = puff->FindState(NAME_Death, NAME_Extreme, true)) != NULL)
5052 	{
5053 		puff->SetState (crashstate);
5054 	}
5055 	else if ((flags & PF_MELEERANGE) && puff->MeleeState != NULL)
5056 	{
5057 		// handle the hard coded state jump of Doom's bullet puff
5058 		// in a more flexible manner.
5059 		puff->SetState (puff->MeleeState);
5060 	}
5061 
5062 	if (!(flags & PF_TEMPORARY))
5063 	{
5064 		if (cl_pufftype && updown != 3 && (puff->flags4 & MF4_ALLOWPARTICLES))
5065 		{
5066 			P_DrawSplash2 (32, x, y, z, dir, updown, 1);
5067 			puff->renderflags |= RF_INVISIBLE;
5068 		}
5069 
5070 		if ((flags & PF_HITTHING) && puff->SeeSound)
5071 		{ // Hit thing sound
5072 			S_Sound (puff, CHAN_BODY, puff->SeeSound, 1, ATTN_NORM);
5073 		}
5074 		else if (puff->AttackSound)
5075 		{
5076 			S_Sound (puff, CHAN_BODY, puff->AttackSound, 1, ATTN_NORM);
5077 		}
5078 	}
5079 
5080 	return puff;
5081 }
5082 
5083 
5084 
5085 //---------------------------------------------------------------------------
5086 //
5087 // P_SpawnBlood
5088 //
5089 //---------------------------------------------------------------------------
5090 
P_SpawnBlood(fixed_t x,fixed_t y,fixed_t z,angle_t dir,int damage,AActor * originator)5091 void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator)
5092 {
5093 	AActor *th;
5094 	PalEntry bloodcolor = originator->GetBloodColor();
5095 	const PClass *bloodcls = originator->GetBloodType();
5096 
5097 	int bloodtype = cl_bloodtype;
5098 
5099 	if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
5100 		bloodtype = 0;
5101 
5102 	if (bloodcls != NULL)
5103 	{
5104 		z += pr_spawnblood.Random2 () << 10;
5105 		th = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
5106 		th->velz = FRACUNIT*2;
5107 		th->angle = dir;
5108 		// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
5109 		if (th->flags5 & MF5_PUFFGETSOWNER) th->target = originator;
5110 		if (gameinfo.gametype & GAME_DoomChex)
5111 		{
5112 			th->tics -= pr_spawnblood() & 3;
5113 
5114 			if (th->tics < 1)
5115 				th->tics = 1;
5116 		}
5117 		// colorize the blood
5118 		if (bloodcolor != 0 && !(th->flags2 & MF2_DONTTRANSLATE))
5119 		{
5120 			th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
5121 		}
5122 
5123 		// Moved out of the blood actor so that replacing blood is easier
5124 		if (gameinfo.gametype & GAME_DoomStrifeChex)
5125 		{
5126 			if (gameinfo.gametype == GAME_Strife)
5127 			{
5128 				if (damage > 13)
5129 				{
5130 					FState *state = th->FindState(NAME_Spray);
5131 					if (state != NULL)
5132 					{
5133 						th->SetState (state);
5134 						goto statedone;
5135 					}
5136 				}
5137 				else damage += 2;
5138 			}
5139 			int advance = 0;
5140 			if (damage <= 12 && damage >= 9)
5141 			{
5142 				advance = 1;
5143 			}
5144 			else if (damage < 9)
5145 			{
5146 				advance = 2;
5147 			}
5148 
5149 			PClass *cls = th->GetClass();
5150 
5151 			while (cls != RUNTIME_CLASS(AActor))
5152 			{
5153 				FActorInfo *ai = cls->ActorInfo;
5154 				int checked_advance = advance;
5155 				if (ai->OwnsState(th->SpawnState))
5156 				{
5157 					for (; checked_advance > 0; --checked_advance)
5158 					{
5159 						// [RH] Do not set to a state we do not own.
5160 						if (ai->OwnsState(th->SpawnState + checked_advance))
5161 						{
5162 							th->SetState(th->SpawnState + checked_advance);
5163 							goto statedone;
5164 						}
5165 					}
5166 				}
5167 				cls = cls->ParentClass;
5168 			}
5169 		}
5170 
5171 	statedone:
5172 		if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE;
5173 	}
5174 
5175 	if (bloodtype >= 1)
5176 		P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor);
5177 }
5178 
5179 //---------------------------------------------------------------------------
5180 //
5181 // PROC P_BloodSplatter
5182 //
5183 //---------------------------------------------------------------------------
5184 
P_BloodSplatter(fixed_t x,fixed_t y,fixed_t z,AActor * originator)5185 void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
5186 {
5187 	PalEntry bloodcolor = originator->GetBloodColor();
5188 	const PClass *bloodcls = originator->GetBloodType(1);
5189 
5190 	int bloodtype = cl_bloodtype;
5191 
5192 	if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
5193 		bloodtype = 0;
5194 
5195 	if (bloodcls != NULL)
5196 	{
5197 		AActor *mo;
5198 
5199 		mo = Spawn(bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
5200 		mo->target = originator;
5201 		mo->velx = pr_splatter.Random2 () << 10;
5202 		mo->vely = pr_splatter.Random2 () << 10;
5203 		mo->velz = 3*FRACUNIT;
5204 
5205 		// colorize the blood!
5206 		if (bloodcolor!=0 && !(mo->flags2 & MF2_DONTTRANSLATE))
5207 		{
5208 			mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
5209 		}
5210 
5211 		if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
5212 	}
5213 	if (bloodtype >= 1)
5214 	{
5215 		P_DrawSplash2 (40, x, y, z, R_PointToAngle2 (x, y, originator->X(), originator->Y()), 2, bloodcolor);
5216 	}
5217 }
5218 
5219 //===========================================================================
5220 //
5221 //  P_BloodSplatter2
5222 //
5223 //===========================================================================
5224 
P_BloodSplatter2(fixed_t x,fixed_t y,fixed_t z,AActor * originator)5225 void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
5226 {
5227 	PalEntry bloodcolor = originator->GetBloodColor();
5228 	const PClass *bloodcls = originator->GetBloodType(2);
5229 
5230 	int bloodtype = cl_bloodtype;
5231 
5232 	if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
5233 		bloodtype = 0;
5234 
5235 	if (bloodcls != NULL)
5236 	{
5237 		AActor *mo;
5238 
5239 		x += ((pr_splat()-128)<<11);
5240 		y += ((pr_splat()-128)<<11);
5241 
5242 		mo = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
5243 		mo->target = originator;
5244 
5245 		// colorize the blood!
5246 		if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE))
5247 		{
5248 			mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
5249 		}
5250 
5251 		if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
5252 	}
5253 	if (bloodtype >= 1)
5254 	{
5255 		P_DrawSplash2 (100, x, y, z, R_PointToAngle2 (0, 0, originator->X() - x, originator->Y() - y), 2, bloodcolor);
5256 	}
5257 }
5258 
5259 //---------------------------------------------------------------------------
5260 //
5261 // PROC P_RipperBlood
5262 //
5263 //---------------------------------------------------------------------------
5264 
P_RipperBlood(AActor * mo,AActor * bleeder)5265 void P_RipperBlood (AActor *mo, AActor *bleeder)
5266 {
5267 	PalEntry bloodcolor = bleeder->GetBloodColor();
5268 	const PClass *bloodcls = bleeder->GetBloodType();
5269 
5270 	fixed_t xo = (pr_ripperblood.Random2() << 12);
5271 	fixed_t yo = (pr_ripperblood.Random2() << 12);
5272 	fixed_t zo = (pr_ripperblood.Random2() << 12);
5273 	fixedvec3 pos = mo->Vec3Offset(xo, yo, zo);
5274 
5275 	int bloodtype = cl_bloodtype;
5276 
5277 	if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
5278 		bloodtype = 0;
5279 
5280 	if (bloodcls != NULL)
5281 	{
5282 		AActor *th;
5283 		th = Spawn (bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
5284 		// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
5285 		if (th->flags5 & MF5_PUFFGETSOWNER) th->target = bleeder;
5286 		if (gameinfo.gametype == GAME_Heretic)
5287 			th->flags |= MF_NOGRAVITY;
5288 		th->velx = mo->velx >> 1;
5289 		th->vely = mo->vely >> 1;
5290 		th->tics += pr_ripperblood () & 3;
5291 
5292 		// colorize the blood!
5293 		if (bloodcolor!=0 && !(th->flags2 & MF2_DONTTRANSLATE))
5294 		{
5295 			th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
5296 		}
5297 
5298 		if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE;
5299 	}
5300 	if (bloodtype >= 1)
5301 	{
5302 		P_DrawSplash2 (28, pos.x, pos.y, pos.z, 0, 0, bloodcolor);
5303 	}
5304 }
5305 
5306 //---------------------------------------------------------------------------
5307 //
5308 // FUNC P_GetThingFloorType
5309 //
5310 //---------------------------------------------------------------------------
5311 
P_GetThingFloorType(AActor * thing)5312 int P_GetThingFloorType (AActor *thing)
5313 {
5314 	if (thing->floorterrain >= 0)
5315 	{
5316 		return thing->floorterrain;
5317 	}
5318 	else
5319 	{
5320 		return thing->Sector->GetTerrain(sector_t::floor);
5321 	}
5322 }
5323 
5324 //---------------------------------------------------------------------------
5325 //
5326 // FUNC P_HitWater
5327 //
5328 // Returns true if hit liquid and splashed, false if not.
5329 //---------------------------------------------------------------------------
5330 
P_HitWater(AActor * thing,sector_t * sec,fixed_t x,fixed_t y,fixed_t z,bool checkabove,bool alert,bool force)5331 bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert, bool force)
5332 {
5333 	if (thing->flags3 & MF3_DONTSPLASH)
5334 		return false;
5335 
5336 	if (thing->player && (thing->player->cheats & CF_PREDICTING))
5337 		return false;
5338 
5339 	AActor *mo = NULL;
5340 	FSplashDef *splash;
5341 	int terrainnum;
5342 	sector_t *hsec = NULL;
5343 
5344 	if (x == FIXED_MIN) x = thing->X();
5345 	if (y == FIXED_MIN) y = thing->Y();
5346 	if (z == FIXED_MIN) z = thing->Z();
5347 	// don't splash above the object
5348 	if (checkabove)
5349 	{
5350 		fixed_t compare_z = thing->Z() + (thing->height >> 1);
5351 		// Missiles are typically small and fast, so they might
5352 		// end up submerged by the move that calls P_HitWater.
5353 		if (thing->flags & MF_MISSILE)
5354 			compare_z -= thing->velz;
5355 		if (z > compare_z)
5356 			return false;
5357 	}
5358 
5359 #if 0 // needs some rethinking before activation
5360 
5361 	// This avoids spawning splashes on invisible self referencing sectors.
5362 	// For network consistency do this only in single player though because
5363 	// it is not guaranteed that all players have GL nodes loaded.
5364 	if (!multiplayer && thing->subsector->sector != thing->subsector->render_sector)
5365 	{
5366 		fixed_t zs = thing->subsector->sector->floorplane.ZatPoint(x, y);
5367 		fixed_t zr = thing->subsector->render_sector->floorplane.ZatPoint(x, y);
5368 
5369 		if (zs > zr && thing->z >= zs) return false;
5370 	}
5371 #endif
5372 
5373 	// 'force' means, we want this sector's terrain, no matter what.
5374 	if (!force)
5375 	{
5376 		for (unsigned int i = 0; i<sec->e->XFloor.ffloors.Size(); i++)
5377 		{
5378 			F3DFloor * rover = sec->e->XFloor.ffloors[i];
5379 			if (!(rover->flags & FF_EXISTS)) continue;
5380 			fixed_t planez = rover->top.plane->ZatPoint(x, y);
5381 			if (z > planez - FRACUNIT / 2 && z < planez + FRACUNIT / 2)	// allow minor imprecisions
5382 			{
5383 				if (rover->flags & (FF_SOLID | FF_SWIMMABLE))
5384 				{
5385 					terrainnum = rover->model->GetTerrain(rover->top.isceiling);
5386 					goto foundone;
5387 				}
5388 			}
5389 			planez = rover->bottom.plane->ZatPoint(x, y);
5390 			if (planez < z && !(planez < thing->floorz)) return false;
5391 		}
5392 	}
5393 	hsec = sec->GetHeightSec();
5394 	if (force || hsec == NULL || !(hsec->MoreFlags & SECF_CLIPFAKEPLANES))
5395 	{
5396 		terrainnum = sec->GetTerrain(sector_t::floor);
5397 	}
5398 	else
5399 	{
5400 		terrainnum = hsec->GetTerrain(sector_t::floor);
5401 	}
5402 foundone:
5403 
5404 	int splashnum = Terrains[terrainnum].Splash;
5405 	bool smallsplash = false;
5406 	const secplane_t *plane;
5407 
5408 	if (splashnum == -1)
5409 		return Terrains[terrainnum].IsLiquid;
5410 
5411 	// don't splash when touching an underwater floor
5412 	if (thing->waterlevel>=1 && z<=thing->floorz) return Terrains[terrainnum].IsLiquid;
5413 
5414 	plane = hsec != NULL? &sec->heightsec->floorplane : &sec->floorplane;
5415 
5416 	// Don't splash for living things with small vertical velocities.
5417 	// There are levels where the constant splashing from the monsters gets extremely annoying
5418 	if (((thing->flags3&MF3_ISMONSTER || thing->player) && thing->velz >= -6*FRACUNIT) && !force)
5419 		return Terrains[terrainnum].IsLiquid;
5420 
5421 	splash = &Splashes[splashnum];
5422 
5423 	// Small splash for small masses
5424 	if (thing->Mass < 10)
5425 		smallsplash = true;
5426 
5427 	if (smallsplash && splash->SmallSplash)
5428 	{
5429 		mo = Spawn (splash->SmallSplash, x, y, z, ALLOW_REPLACE);
5430 		if (mo) mo->floorclip += splash->SmallSplashClip;
5431 	}
5432 	else
5433 	{
5434 		if (splash->SplashChunk)
5435 		{
5436 			mo = Spawn (splash->SplashChunk, x, y, z, ALLOW_REPLACE);
5437 			mo->target = thing;
5438 			if (splash->ChunkXVelShift != 255)
5439 			{
5440 				mo->velx = pr_chunk.Random2() << splash->ChunkXVelShift;
5441 			}
5442 			if (splash->ChunkYVelShift != 255)
5443 			{
5444 				mo->vely = pr_chunk.Random2() << splash->ChunkYVelShift;
5445 			}
5446 			mo->velz = splash->ChunkBaseZVel + (pr_chunk() << splash->ChunkZVelShift);
5447 		}
5448 		if (splash->SplashBase)
5449 		{
5450 			mo = Spawn (splash->SplashBase, x, y, z, ALLOW_REPLACE);
5451 		}
5452 		if (thing->player && !splash->NoAlert && alert)
5453 		{
5454 			P_NoiseAlert (thing, thing, true);
5455 		}
5456 	}
5457 	if (mo)
5458 	{
5459 		S_Sound (mo, CHAN_ITEM, smallsplash ?
5460 			splash->SmallSplashSound : splash->NormalSplashSound,
5461 			1, ATTN_IDLE);
5462 	}
5463 	else
5464 	{
5465 		S_Sound (x, y, z, CHAN_ITEM, smallsplash ?
5466 			splash->SmallSplashSound : splash->NormalSplashSound,
5467 			1, ATTN_IDLE);
5468 	}
5469 
5470 	// Don't let deep water eat missiles
5471 	return plane == &sec->floorplane ? Terrains[terrainnum].IsLiquid : false;
5472 }
5473 
5474 //---------------------------------------------------------------------------
5475 //
5476 // FUNC P_HitFloor
5477 //
5478 // Returns true if hit liquid and splashed, false if not.
5479 //---------------------------------------------------------------------------
5480 
P_HitFloor(AActor * thing)5481 bool P_HitFloor (AActor *thing)
5482 {
5483 	const msecnode_t *m;
5484 
5485 	// killough 11/98: touchy objects explode on impact
5486 	// Allow very short drops to be safe, so that a touchy can be summoned without exploding.
5487 	if (thing->flags6 & MF6_TOUCHY && ((thing->flags6 & MF6_ARMED) || thing->IsSentient()) && ((thing->velz) < (-5 * FRACUNIT)))
5488 	{
5489 		thing->flags6 &= ~MF6_ARMED; // Disarm
5490 		P_DamageMobj (thing, NULL, NULL, thing->health, NAME_Crush, DMG_FORCED);  // kill object
5491 		return false;
5492 	}
5493 
5494 	if (thing->flags3 & MF3_DONTSPLASH)
5495 		return false;
5496 
5497 	// don't splash if landing on the edge above water/lava/etc....
5498 	for (m = thing->touching_sectorlist; m; m = m->m_tnext)
5499 	{
5500 		if (thing->Z() == m->m_sector->floorplane.ZatPoint(thing))
5501 		{
5502 			break;
5503 		}
5504 
5505 		// Check 3D floors
5506 		for(unsigned int i=0;i<m->m_sector->e->XFloor.ffloors.Size();i++)
5507 		{
5508 			F3DFloor * rover = m->m_sector->e->XFloor.ffloors[i];
5509 			if (!(rover->flags & FF_EXISTS)) continue;
5510 			if (rover->flags & (FF_SOLID|FF_SWIMMABLE))
5511 			{
5512 				if (rover->top.plane->ZatPoint(thing) == thing->Z())
5513 				{
5514 					return P_HitWater (thing, m->m_sector);
5515 				}
5516 			}
5517 		}
5518 	}
5519 	if (m == NULL || m->m_sector->GetHeightSec() != NULL)
5520 	{
5521 		return false;
5522 	}
5523 
5524 	return P_HitWater (thing, m->m_sector);
5525 }
5526 
5527 //---------------------------------------------------------------------------
5528 //
5529 // P_CheckSplash
5530 //
5531 // Checks for splashes caused by explosions
5532 //
5533 //---------------------------------------------------------------------------
5534 
P_CheckSplash(AActor * self,fixed_t distance)5535 void P_CheckSplash(AActor *self, fixed_t distance)
5536 {
5537 	if (self->Z() <= self->floorz + (distance<<FRACBITS) && self->floorsector == self->Sector && self->Sector->GetHeightSec() == NULL)
5538 	{
5539 		// Explosion splashes never alert monsters. This is because A_Explode has
5540 		// a separate parameter for that so this would get in the way of proper
5541 		// behavior.
5542 		P_HitWater (self, self->Sector, self->X(), self->Y(), self->floorz, false, false);
5543 	}
5544 }
5545 
5546 //---------------------------------------------------------------------------
5547 //
5548 // FUNC P_CheckMissileSpawn
5549 //
5550 // Returns true if the missile is at a valid spawn point, otherwise
5551 // explodes it and returns false.
5552 //
5553 //---------------------------------------------------------------------------
5554 
P_CheckMissileSpawn(AActor * th,fixed_t maxdist)5555 bool P_CheckMissileSpawn (AActor* th, fixed_t maxdist)
5556 {
5557 	// [RH] Don't decrement tics if they are already less than 1
5558 	if ((th->flags4 & MF4_RANDOMIZE) && th->tics > 0)
5559 	{
5560 		th->tics -= pr_checkmissilespawn() & 3;
5561 		if (th->tics < 1)
5562 			th->tics = 1;
5563 	}
5564 
5565 	if (maxdist > 0)
5566 	{
5567 		// move a little forward so an angle can be computed if it immediately explodes
5568 		TVector3<double> advance(FIXED2DBL(th->velx), FIXED2DBL(th->vely), FIXED2DBL(th->velz));
5569 		double maxsquared = FIXED2DBL(maxdist);
5570 		maxsquared *= maxsquared;
5571 
5572 		// Keep halving the advance vector until we get something less than maxdist
5573 		// units away, since we still want to spawn the missile inside the shooter.
5574 		do
5575 		{
5576 			advance *= 0.5f;
5577 		}
5578 		while (TVector2<double>(advance).LengthSquared() >= maxsquared);
5579 		th->SetXYZ(
5580 			th->X() + FLOAT2FIXED(advance.X),
5581 			th->Y() + FLOAT2FIXED(advance.Y),
5582 			th->Z() + FLOAT2FIXED(advance.Z));
5583 	}
5584 
5585 	FCheckPosition tm(!!(th->flags2 & MF2_RIP));
5586 
5587 	// killough 8/12/98: for non-missile objects (e.g. grenades)
5588 	//
5589 	// [GZ] MBF excludes non-missile objects from the P_TryMove test
5590 	// and subsequent potential P_ExplodeMissile call. That is because
5591 	// in MBF, a projectile is not an actor with the MF_MISSILE flag
5592 	// but an actor with either or both the MF_MISSILE and MF_BOUNCES
5593 	// flags, and a grenade is identified by not having MF_MISSILE.
5594 	// Killough wanted grenades not to explode directly when spawned,
5595 	// therefore they can be fired safely even when humping a wall as
5596 	// they will then just drop on the floor at their shooter's feet.
5597 	//
5598 	// However, ZDoom does allow non-missiles to be shot as well, so
5599 	// Killough's check for non-missiles is inadequate here. So let's
5600 	// replace it by a check for non-missile and MBF bounce type.
5601 	// This should allow MBF behavior where relevant without altering
5602 	// established ZDoom behavior for crazy stuff like a cacodemon cannon.
5603 	bool MBFGrenade = (!(th->flags & MF_MISSILE) || (th->BounceFlags & BOUNCE_MBF));
5604 
5605 	// killough 3/15/98: no dropoff (really = don't care for missiles)
5606 	if (!(P_TryMove (th, th->X(), th->Y(), false, NULL, tm, true)))
5607 	{
5608 		// [RH] Don't explode ripping missiles that spawn inside something
5609 		if (th->BlockingMobj == NULL || !(th->flags2 & MF2_RIP) || (th->BlockingMobj->flags5 & MF5_DONTRIP))
5610 		{
5611 			// If this is a monster spawned by A_CustomMissile subtract it from the counter.
5612 			th->ClearCounters();
5613 			// [RH] Don't explode missiles that spawn on top of horizon lines
5614 			if (th->BlockingLine != NULL && th->BlockingLine->special == Line_Horizon)
5615 			{
5616 				th->Destroy ();
5617 			}
5618 			else if (MBFGrenade && th->BlockingLine != NULL)
5619 			{
5620 				P_BounceWall(th);
5621 			}
5622 			else
5623 			{
5624 				P_ExplodeMissile (th, NULL, th->BlockingMobj);
5625 			}
5626 			return false;
5627 		}
5628 	}
5629 	return true;
5630 }
5631 
5632 
5633 //---------------------------------------------------------------------------
5634 //
5635 // FUNC P_PlaySpawnSound
5636 //
5637 // Plays a missiles spawn sound. Location depends on the
5638 // MF_SPAWNSOUNDSOURCE flag.
5639 //
5640 //---------------------------------------------------------------------------
5641 
P_PlaySpawnSound(AActor * missile,AActor * spawner)5642 void P_PlaySpawnSound(AActor *missile, AActor *spawner)
5643 {
5644 	if (missile->SeeSound != 0)
5645 	{
5646 		if (!(missile->flags & MF_SPAWNSOUNDSOURCE))
5647 		{
5648 			S_Sound (missile, CHAN_VOICE, missile->SeeSound, 1, ATTN_NORM);
5649 		}
5650 		else if (spawner != NULL)
5651 		{
5652 			S_Sound (spawner, CHAN_WEAPON, missile->SeeSound, 1, ATTN_NORM);
5653 		}
5654 		else
5655 		{
5656 			// If there is no spawner use the spawn position.
5657 			// But not in a silenced sector.
5658 			if (!(missile->Sector->Flags & SECF_SILENT))
5659 				S_Sound (missile->X(), missile->Y(), missile->Z(), CHAN_WEAPON, missile->SeeSound, 1, ATTN_NORM);
5660 		}
5661 	}
5662 }
5663 
GetDefaultSpeed(const PClass * type)5664 static fixed_t GetDefaultSpeed(const PClass *type)
5665 {
5666 	if (type == NULL) return 0;
5667 	else if (G_SkillProperty(SKILLP_FastMonsters))
5668 		return type->Meta.GetMetaFixed(AMETA_FastSpeed, GetDefaultByType(type)->Speed);
5669 	else
5670 		return GetDefaultByType(type)->Speed;
5671 }
5672 
5673 //---------------------------------------------------------------------------
5674 //
5675 // FUNC P_SpawnMissile
5676 //
5677 // Returns NULL if the missile exploded immediately, otherwise returns
5678 // a mobj_t pointer to the missile.
5679 //
5680 //---------------------------------------------------------------------------
5681 
P_SpawnMissile(AActor * source,AActor * dest,const PClass * type,AActor * owner)5682 AActor *P_SpawnMissile (AActor *source, AActor *dest, const PClass *type, AActor *owner)
5683 {
5684 	if (source == NULL)
5685 	{
5686 		return NULL;
5687 	}
5688 	return P_SpawnMissileXYZ (source->X(), source->Y(), source->Z() + 32*FRACUNIT + source->GetBobOffset(),
5689 		source, dest, type, true, owner);
5690 }
5691 
P_SpawnMissileZ(AActor * source,fixed_t z,AActor * dest,const PClass * type)5692 AActor *P_SpawnMissileZ (AActor *source, fixed_t z, AActor *dest, const PClass *type)
5693 {
5694 	if (source == NULL)
5695 	{
5696 		return NULL;
5697 	}
5698 	return P_SpawnMissileXYZ (source->X(), source->Y(), z, source, dest, type);
5699 }
5700 
P_SpawnMissileXYZ(fixed_t x,fixed_t y,fixed_t z,AActor * source,AActor * dest,const PClass * type,bool checkspawn,AActor * owner)5701 AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z,
5702 	AActor *source, AActor *dest, const PClass *type, bool checkspawn, AActor *owner)
5703 {
5704 	if (source == NULL)
5705 	{
5706 		return NULL;
5707 	}
5708 
5709 	if (dest == NULL)
5710 	{
5711 		Printf ("P_SpawnMissilyXYZ: Tried to shoot %s from %s with no dest\n",
5712 			type->TypeName.GetChars(), source->GetClass()->TypeName.GetChars());
5713 		return NULL;
5714 	}
5715 
5716 	if (z != ONFLOORZ && z != ONCEILINGZ)
5717 	{
5718 		z -= source->floorclip;
5719 	}
5720 
5721 	AActor *th = Spawn (type, x, y, z, ALLOW_REPLACE);
5722 
5723 	P_PlaySpawnSound(th, source);
5724 
5725 	// record missile's originator
5726 	if (owner == NULL) owner = source;
5727 	th->target = owner;
5728 
5729 	float speed = (float)(th->Speed);
5730 
5731 	// [RH]
5732 	// Hexen calculates the missile velocity based on the source's location.
5733 	// Would it be more useful to base it on the actual position of the
5734 	// missile?
5735 	// Answer: No, because this way, you can set up sets of parallel missiles.
5736 
5737 	fixedvec3 fixvel = source->Vec3To(dest);
5738 	FVector3 velocity(fixvel.x, fixvel.y, fixvel.z);
5739 	// Floor and ceiling huggers should never have a vertical component to their velocity
5740 	if (th->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))
5741 	{
5742 		velocity.Z = 0;
5743 	}
5744 	// [RH] Adjust the trajectory if the missile will go over the target's head.
5745 	else if (z - source->Z() >= dest->height)
5746 	{
5747 		velocity.Z += (dest->height - z + source->Z());
5748 	}
5749 	velocity.Resize (speed);
5750 	th->velx = (fixed_t)(velocity.X);
5751 	th->vely = (fixed_t)(velocity.Y);
5752 	th->velz = (fixed_t)(velocity.Z);
5753 
5754 	// invisible target: rotate velocity vector in 2D
5755 	// [RC] Now monsters can aim at invisible player as if they were fully visible.
5756 	if (dest->flags & MF_SHADOW && !(source->flags6 & MF6_SEEINVISIBLE))
5757 	{
5758 		angle_t an = pr_spawnmissile.Random2 () << 20;
5759 		an >>= ANGLETOFINESHIFT;
5760 
5761 		fixed_t newx = DMulScale16 (th->velx, finecosine[an], -th->vely, finesine[an]);
5762 		fixed_t newy = DMulScale16 (th->velx, finesine[an], th->vely, finecosine[an]);
5763 		th->velx = newx;
5764 		th->vely = newy;
5765 	}
5766 
5767 	th->angle = R_PointToAngle2 (0, 0, th->velx, th->vely);
5768 
5769 	if (th->flags4 & MF4_SPECTRAL)
5770 	{
5771 		th->SetFriendPlayer(owner->player);
5772 	}
5773 
5774 	return (!checkspawn || P_CheckMissileSpawn (th, source->radius)) ? th : NULL;
5775 }
5776 
P_OldSpawnMissile(AActor * source,AActor * owner,AActor * dest,const PClass * type)5777 AActor * P_OldSpawnMissile(AActor * source, AActor * owner, AActor * dest, const PClass *type)
5778 {
5779 	if (source == NULL)
5780 	{
5781 		return NULL;
5782 	}
5783 	angle_t an;
5784 	fixed_t dist;
5785 	AActor *th = Spawn (type, source->PosPlusZ(4*8*FRACUNIT), ALLOW_REPLACE);
5786 
5787 	P_PlaySpawnSound(th, source);
5788 	th->target = owner;		// record missile's originator
5789 
5790 	th->angle = an = source->AngleTo(dest);
5791 	an >>= ANGLETOFINESHIFT;
5792 	th->velx = FixedMul (th->Speed, finecosine[an]);
5793 	th->vely = FixedMul (th->Speed, finesine[an]);
5794 
5795 	dist = source->AproxDistance (dest);
5796 	if (th->Speed) dist = dist / th->Speed;
5797 
5798 	if (dist < 1)
5799 		dist = 1;
5800 
5801 	th->velz = (dest->Z() - source->Z()) / dist;
5802 
5803 	if (th->flags4 & MF4_SPECTRAL)
5804 	{
5805 		th->SetFriendPlayer(owner->player);
5806 	}
5807 
5808 	P_CheckMissileSpawn(th, source->radius);
5809 	return th;
5810 }
5811 
5812 //---------------------------------------------------------------------------
5813 //
5814 // FUNC P_SpawnMissileAngle
5815 //
5816 // Returns NULL if the missile exploded immediately, otherwise returns
5817 // a mobj_t pointer to the missile.
5818 //
5819 //---------------------------------------------------------------------------
5820 
P_SpawnMissileAngle(AActor * source,const PClass * type,angle_t angle,fixed_t velz)5821 AActor *P_SpawnMissileAngle (AActor *source, const PClass *type,
5822 	angle_t angle, fixed_t velz)
5823 {
5824 	if (source == NULL)
5825 	{
5826 		return NULL;
5827 	}
5828 	return P_SpawnMissileAngleZSpeed (source, source->Z() + 32*FRACUNIT + source->GetBobOffset(),
5829 		type, angle, velz, GetDefaultSpeed (type));
5830 }
5831 
P_SpawnMissileAngleZ(AActor * source,fixed_t z,const PClass * type,angle_t angle,fixed_t velz)5832 AActor *P_SpawnMissileAngleZ (AActor *source, fixed_t z,
5833 	const PClass *type, angle_t angle, fixed_t velz)
5834 {
5835 	return P_SpawnMissileAngleZSpeed (source, z, type, angle, velz,
5836 		GetDefaultSpeed (type));
5837 }
5838 
P_SpawnMissileZAimed(AActor * source,fixed_t z,AActor * dest,const PClass * type)5839 AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PClass *type)
5840 {
5841 	if (source == NULL)
5842 	{
5843 		return NULL;
5844 	}
5845 	angle_t an;
5846 	fixed_t dist;
5847 	fixed_t speed;
5848 	fixed_t velz;
5849 
5850 	an = source->angle;
5851 
5852 	if (dest->flags & MF_SHADOW)
5853 	{
5854 		an += pr_spawnmissile.Random2() << 20;
5855 	}
5856 	dist = source->AproxDistance (dest);
5857 	speed = GetDefaultSpeed (type);
5858 	dist /= speed;
5859 	velz = dist != 0 ? (dest->Z() - source->Z())/dist : speed;
5860 	return P_SpawnMissileAngleZSpeed (source, z, type, an, velz, speed);
5861 }
5862 
5863 //---------------------------------------------------------------------------
5864 //
5865 // FUNC P_SpawnMissileAngleSpeed
5866 //
5867 // Returns NULL if the missile exploded immediately, otherwise returns
5868 // a mobj_t pointer to the missile.
5869 //
5870 //---------------------------------------------------------------------------
5871 
P_SpawnMissileAngleSpeed(AActor * source,const PClass * type,angle_t angle,fixed_t velz,fixed_t speed)5872 AActor *P_SpawnMissileAngleSpeed (AActor *source, const PClass *type,
5873 	angle_t angle, fixed_t velz, fixed_t speed)
5874 {
5875 	if (source == NULL)
5876 	{
5877 		return NULL;
5878 	}
5879 	return P_SpawnMissileAngleZSpeed (source, source->Z() + 32*FRACUNIT + source->GetBobOffset(),
5880 		type, angle, velz, speed);
5881 }
5882 
P_SpawnMissileAngleZSpeed(AActor * source,fixed_t z,const PClass * type,angle_t angle,fixed_t velz,fixed_t speed,AActor * owner,bool checkspawn)5883 AActor *P_SpawnMissileAngleZSpeed (AActor *source, fixed_t z,
5884 	const PClass *type, angle_t angle, fixed_t velz, fixed_t speed, AActor *owner, bool checkspawn)
5885 {
5886 	if (source == NULL)
5887 	{
5888 		return NULL;
5889 	}
5890 	AActor *mo;
5891 
5892 	if (z != ONFLOORZ && z != ONCEILINGZ)
5893 	{
5894 		z -= source->floorclip;
5895 	}
5896 
5897 	mo = Spawn (type, source->X(), source->Y(), z, ALLOW_REPLACE);
5898 
5899 	P_PlaySpawnSound(mo, source);
5900 	if (owner == NULL) owner = source;
5901 	mo->target = owner;
5902 	mo->angle = angle;
5903 	angle >>= ANGLETOFINESHIFT;
5904 	mo->velx = FixedMul (speed, finecosine[angle]);
5905 	mo->vely = FixedMul (speed, finesine[angle]);
5906 	mo->velz = velz;
5907 
5908 	if (mo->flags4 & MF4_SPECTRAL)
5909 	{
5910 		mo->SetFriendPlayer(owner->player);
5911 	}
5912 
5913 	return (!checkspawn || P_CheckMissileSpawn(mo, source->radius)) ? mo : NULL;
5914 }
5915 
5916 /*
5917 ================
5918 =
5919 = P_SpawnPlayerMissile
5920 =
5921 = Tries to aim at a nearby monster
5922 ================
5923 */
5924 
P_SpawnPlayerMissile(AActor * source,const PClass * type)5925 AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type)
5926 {
5927 	if (source == NULL)
5928 	{
5929 		return NULL;
5930 	}
5931 	return P_SpawnPlayerMissile (source, 0, 0, 0, type, source->angle);
5932 }
5933 
P_SpawnPlayerMissile(AActor * source,const PClass * type,angle_t angle)5934 AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle)
5935 {
5936 	return P_SpawnPlayerMissile (source, 0, 0, 0, type, angle);
5937 }
5938 
P_SpawnPlayerMissile(AActor * source,fixed_t x,fixed_t y,fixed_t z,const PClass * type,angle_t angle,AActor ** pLineTarget,AActor ** pMissileActor,bool nofreeaim,bool noautoaim)5939 AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z,
5940 							  const PClass *type, angle_t angle, AActor **pLineTarget, AActor **pMissileActor,
5941 							  bool nofreeaim, bool noautoaim)
5942 {
5943 	static const int angdiff[3] = { -(1<<26), 1<<26, 0 };
5944 	angle_t an = angle;
5945 	angle_t pitch;
5946 	AActor *linetarget;
5947 	AActor *defaultobject = GetDefaultByType(type);
5948 	int vrange = nofreeaim ? ANGLE_1*35 : 0;
5949 
5950 	if (source == NULL)
5951 	{
5952 		return NULL;
5953 	}
5954 	if (source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM) || noautoaim))
5955 	{
5956 		// Keep exactly the same angle and pitch as the player's own aim
5957 		an = angle;
5958 		pitch = source->pitch;
5959 		linetarget = NULL;
5960 	}
5961 	else // see which target is to be aimed at
5962 	{
5963 		// [XA] If MaxTargetRange is defined in the spawned projectile, use this as the
5964 		//      maximum range for the P_AimLineAttack call later; this allows MaxTargetRange
5965 		//      to function as a "maximum tracer-acquisition range" for seeker missiles.
5966 		fixed_t linetargetrange = defaultobject->maxtargetrange > 0 ? defaultobject->maxtargetrange*64 : 16*64*FRACUNIT;
5967 
5968 		int i = 2;
5969 		do
5970 		{
5971 			an = angle + angdiff[i];
5972 			pitch = P_AimLineAttack (source, an, linetargetrange, &linetarget, vrange);
5973 
5974 			if (source->player != NULL &&
5975 				!nofreeaim &&
5976 				level.IsFreelookAllowed() &&
5977 				source->player->userinfo.GetAimDist() <= ANGLE_1/2)
5978 			{
5979 				break;
5980 			}
5981 		} while (linetarget == NULL && --i >= 0);
5982 
5983 		if (linetarget == NULL)
5984 		{
5985 			an = angle;
5986 			if (nofreeaim || !level.IsFreelookAllowed())
5987 			{
5988 				pitch = 0;
5989 			}
5990 		}
5991 	}
5992 	if (pLineTarget) *pLineTarget = linetarget;
5993 
5994 	if (z != ONFLOORZ && z != ONCEILINGZ)
5995 	{
5996 		// Doom spawns missiles 4 units lower than hitscan attacks for players.
5997 		z += source->Z() + (source->height>>1) - source->floorclip;
5998 		if (source->player != NULL)	// Considering this is for player missiles, it better not be NULL.
5999 		{
6000 			z += FixedMul (source->player->mo->AttackZOffset - 4*FRACUNIT, source->player->crouchfactor);
6001 		}
6002 		else
6003 		{
6004 			z += 4*FRACUNIT;
6005 		}
6006 		// Do not fire beneath the floor.
6007 		if (z < source->floorz)
6008 		{
6009 			z = source->floorz;
6010 		}
6011 	}
6012 	fixedvec2 pos = source->Vec2Offset(x, y);
6013 	AActor *MissileActor = Spawn (type, pos.x, pos.y, z, ALLOW_REPLACE);
6014 	if (pMissileActor) *pMissileActor = MissileActor;
6015 	P_PlaySpawnSound(MissileActor, source);
6016 	MissileActor->target = source;
6017 	MissileActor->angle = an;
6018 
6019 	fixed_t vx, vy, vz, speed;
6020 
6021 	vx = FixedMul (finecosine[pitch>>ANGLETOFINESHIFT], finecosine[an>>ANGLETOFINESHIFT]);
6022 	vy = FixedMul (finecosine[pitch>>ANGLETOFINESHIFT], finesine[an>>ANGLETOFINESHIFT]);
6023 	vz = -finesine[pitch>>ANGLETOFINESHIFT];
6024 	speed = MissileActor->Speed;
6025 
6026 	FVector3 vec(vx, vy, vz);
6027 
6028 	if (MissileActor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))
6029 	{
6030 		vec.Z = 0;
6031 	}
6032 	vec.Resize(speed);
6033 	MissileActor->velx = (fixed_t)vec.X;
6034 	MissileActor->vely = (fixed_t)vec.Y;
6035 	MissileActor->velz = (fixed_t)vec.Z;
6036 
6037 	if (MissileActor->flags4 & MF4_SPECTRAL)
6038 	{
6039 		MissileActor->SetFriendPlayer(source->player);
6040 	}
6041 	if (P_CheckMissileSpawn (MissileActor, source->radius))
6042 	{
6043 		return MissileActor;
6044 	}
6045 	return NULL;
6046 }
6047 
GetTeam()6048 int AActor::GetTeam()
6049 {
6050 	if (player)
6051 	{
6052 		return player->userinfo.GetTeam();
6053 	}
6054 
6055 	int myTeam = DesignatedTeam;
6056 
6057 	// Check for monsters that belong to a player on the team but aren't part of the team themselves.
6058 	if (myTeam == TEAM_NONE && FriendPlayer != 0)
6059 	{
6060 		myTeam = players[FriendPlayer - 1].userinfo.GetTeam();
6061 	}
6062 	return myTeam;
6063 
6064 }
6065 
IsTeammate(AActor * other)6066 bool AActor::IsTeammate (AActor *other)
6067 {
6068 	if (!other)
6069 	{
6070 		return false;
6071 	}
6072 	else if (!deathmatch && player && other->player)
6073 	{
6074 		return true;
6075 	}
6076 	else if (teamplay)
6077 	{
6078 		int myTeam = GetTeam();
6079 		int otherTeam = other->GetTeam();
6080 
6081 		return (myTeam != TEAM_NONE && myTeam == otherTeam);
6082 	}
6083 	return false;
6084 }
6085 
6086 //==========================================================================
6087 //
6088 // AActor :: GetSpecies
6089 //
6090 // Species is defined as the lowest base class that is a monster
6091 // with no non-monster class in between. If the actor specifies an explicit
6092 // species (i.e. not 'None'), that is used. This is virtualized, so special
6093 // monsters can change this behavior if they like.
6094 //
6095 //==========================================================================
6096 
GetSpecies()6097 FName AActor::GetSpecies()
6098 {
6099 	if (Species != NAME_None)
6100 	{
6101 		return Species;
6102 	}
6103 
6104 	const PClass *thistype = GetClass();
6105 
6106 	if (GetDefaultByType(thistype)->flags3 & MF3_ISMONSTER)
6107 	{
6108 		while (thistype->ParentClass)
6109 		{
6110 			if (GetDefaultByType(thistype->ParentClass)->flags3 & MF3_ISMONSTER)
6111 				thistype = thistype->ParentClass;
6112 			else
6113 				break;
6114 		}
6115 	}
6116 	return Species = thistype->TypeName; // [GZ] Speeds up future calls.
6117 }
6118 
6119 //==========================================================================
6120 //
6121 // AActor :: IsFriend
6122 //
6123 // Checks if two monsters have to be considered friendly.
6124 //
6125 //==========================================================================
6126 
IsFriend(AActor * other)6127 bool AActor::IsFriend (AActor *other)
6128 {
6129 	if (flags & other->flags & MF_FRIENDLY)
6130 	{
6131 		if (deathmatch && teamplay)
6132 			return IsTeammate(other) ||
6133 				(FriendPlayer != 0 && other->FriendPlayer != 0 &&
6134 					players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
6135 
6136 		return !deathmatch ||
6137 			FriendPlayer == other->FriendPlayer ||
6138 			FriendPlayer == 0 ||
6139 			other->FriendPlayer == 0 ||
6140 			players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
6141 	}
6142 	return false;
6143 }
6144 
6145 //==========================================================================
6146 //
6147 // AActor :: IsHostile
6148 //
6149 // Checks if two monsters have to be considered hostile under any circumstances
6150 //
6151 //==========================================================================
6152 
IsHostile(AActor * other)6153 bool AActor::IsHostile (AActor *other)
6154 {
6155 	// Both monsters are non-friendlies so hostilities depend on infighting settings
6156 	if (!((flags | other->flags) & MF_FRIENDLY)) return false;
6157 
6158 	// Both monsters are friendly and belong to the same player if applicable.
6159 	if (flags & other->flags & MF_FRIENDLY)
6160 	{
6161 		if (deathmatch && teamplay)
6162 			return !IsTeammate(other) &&
6163 				!(FriendPlayer != 0 && other->FriendPlayer != 0 &&
6164 					players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
6165 
6166 		return deathmatch &&
6167 			FriendPlayer != other->FriendPlayer &&
6168 			FriendPlayer !=0 &&
6169 			other->FriendPlayer != 0 &&
6170 			!players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
6171 	}
6172 	return true;
6173 }
6174 
DoSpecialDamage(AActor * target,int damage,FName damagetype)6175 int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype)
6176 {
6177 	if (target->player && target->player->mo == target && damage < 1000 &&
6178 		(target->player->cheats & CF_GODMODE || target->player->cheats & CF_GODMODE2))
6179 	{
6180 		return -1;
6181 	}
6182 	else
6183 	{
6184 		if (target->player)
6185 		{
6186 			// Only do this for old style poison damage.
6187 			if (PoisonDamage > 0 && PoisonDuration == INT_MIN)
6188 			{
6189 				P_PoisonPlayer (target->player, this, this->target, PoisonDamage);
6190 				damage >>= 1;
6191 			}
6192 		}
6193 
6194 		return damage;
6195 	}
6196 }
6197 
TakeSpecialDamage(AActor * inflictor,AActor * source,int damage,FName damagetype)6198 int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype)
6199 {
6200 	FState *death;
6201 
6202 	// If the actor does not have a corresponding death state, then it does not take damage.
6203 	// Note that DeathState matches every kind of damagetype, so an actor has that, it can
6204 	// be hurt with any type of damage. Exception: Massacre damage always succeeds, because
6205 	// it needs to work.
6206 
6207 	// Always kill if there is a regular death state or no death states at all.
6208 	if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates() || damagetype == NAME_Massacre)
6209 	{
6210 		return damage;
6211 	}
6212 
6213 	if (inflictor && inflictor->DeathType != NAME_None)
6214 		damagetype = inflictor->DeathType;
6215 
6216 	if (damagetype == NAME_Ice)
6217 	{
6218 		death = FindState (NAME_Death, NAME_Ice, true);
6219 		if (death == NULL && !deh.NoAutofreeze && !(flags4 & MF4_NOICEDEATH) &&
6220 			(player || (flags3 & MF3_ISMONSTER)))
6221 		{
6222 			death = FindState(NAME_GenericFreezeDeath);
6223 		}
6224 	}
6225 	else
6226 	{
6227 		death = FindState (NAME_Death, damagetype);
6228 	}
6229 	return (death == NULL) ? -1 : damage;
6230 }
6231 
GibHealth()6232 int AActor::GibHealth()
6233 {
6234 	return -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth, FixedMul(SpawnHealth(), gameinfo.gibfactor)));
6235 }
6236 
Crash()6237 void AActor::Crash()
6238 {
6239 	// [RC] Weird that this forces the Crash state regardless of flag.
6240 	if(!(flags6 & MF6_DONTCORPSE))
6241 	{
6242 	if (((flags & MF_CORPSE) || (flags6 & MF6_KILLED)) &&
6243 		!(flags3 & MF3_CRASHED) &&
6244 		!(flags & MF_ICECORPSE))
6245 	{
6246 		FState *crashstate = NULL;
6247 
6248 		if (DamageType != NAME_None)
6249 		{
6250 			if (health < GibHealth())
6251 			{ // Extreme death
6252 				FName labels[] = { NAME_Crash, NAME_Extreme, DamageType };
6253 				crashstate = FindState (3, labels, true);
6254 			}
6255 			if (crashstate == NULL)
6256 			{ // Normal death
6257 				crashstate = FindState(NAME_Crash, DamageType, true);
6258 			}
6259 		}
6260 		if (crashstate == NULL)
6261 		{
6262 			if (health < GibHealth())
6263 			{ // Extreme death
6264 				crashstate = FindState (NAME_Crash, NAME_Extreme);
6265 			}
6266 			else
6267 			{ // Normal death
6268 				crashstate = FindState (NAME_Crash);
6269 			}
6270 		}
6271 		if (crashstate != NULL) SetState(crashstate);
6272 		// Set MF3_CRASHED regardless of the presence of a crash state
6273 		// so this code doesn't have to be executed repeatedly.
6274 		flags3 |= MF3_CRASHED;
6275 	}
6276 	}
6277 }
6278 
SetIdle(bool nofunction)6279 void AActor::SetIdle(bool nofunction)
6280 {
6281 	FState *idle = FindState (NAME_Idle);
6282 	if (idle == NULL) idle = SpawnState;
6283 	SetState(idle, nofunction);
6284 }
6285 
SpawnHealth()6286 int AActor::SpawnHealth()
6287 {
6288 	int defhealth = StartHealth ? StartHealth : GetDefault()->health;
6289 	if (!(flags3 & MF3_ISMONSTER) || defhealth == 0)
6290 	{
6291 		return defhealth;
6292 	}
6293 	else if (flags & MF_FRIENDLY)
6294 	{
6295 		int adj = FixedMul(defhealth, G_SkillProperty(SKILLP_FriendlyHealth));
6296 		return (adj <= 0) ? 1 : adj;
6297 	}
6298 	else
6299 	{
6300 		int adj = FixedMul(defhealth, G_SkillProperty(SKILLP_MonsterHealth));
6301 		return (adj <= 0) ? 1 : adj;
6302 	}
6303 }
6304 
GetRaiseState()6305 FState *AActor::GetRaiseState()
6306 {
6307 	if (!(flags & MF_CORPSE))
6308 	{
6309 		return NULL;	// not a monster
6310 	}
6311 
6312 	if (tics != -1 && // not lying still yet
6313 		!state->GetCanRaise()) // or not ready to be raised yet
6314 	{
6315 		return NULL;
6316 	}
6317 
6318 	if (IsKindOf(RUNTIME_CLASS(APlayerPawn)))
6319 	{
6320 		return NULL;	// do not resurrect players
6321 	}
6322 
6323 	return FindState(NAME_Raise);
6324 }
6325 
Revive()6326 void AActor::Revive()
6327 {
6328 	AActor *info = GetDefault();
6329 	flags = info->flags;
6330 	flags2 = info->flags2;
6331 	flags3 = info->flags3;
6332 	flags4 = info->flags4;
6333 	flags5 = info->flags5;
6334 	flags6 = info->flags6;
6335 	flags7 = info->flags7;
6336 	DamageType = info->DamageType;
6337 	health = SpawnHealth();
6338 	target = NULL;
6339 	lastenemy = NULL;
6340 
6341 	// [RH] If it's a monster, it gets to count as another kill
6342 	if (CountsAsKill())
6343 	{
6344 		level.total_monsters++;
6345 	}
6346 }
6347 
GetDropItems()6348 FDropItem *AActor::GetDropItems()
6349 {
6350 	unsigned int index = GetClass()->Meta.GetMetaInt (ACMETA_DropItems) - 1;
6351 
6352 	if (index < DropItemList.Size())
6353 	{
6354 		return DropItemList[index];
6355 	}
6356 	return NULL;
6357 }
6358 
GetGravity() const6359 fixed_t AActor::GetGravity() const
6360 {
6361 	if (flags & MF_NOGRAVITY) return 0;
6362 	return fixed_t(level.gravity * Sector->gravity * FIXED2FLOAT(gravity) * 81.92);
6363 }
6364 
6365 // killough 11/98:
6366 // Whether an object is "sentient" or not. Used for environmental influences.
6367 // (left precisely the same as MBF even though it doesn't make much sense.)
IsSentient() const6368 bool AActor::IsSentient() const
6369 {
6370 	return health > 0 && SeeState != NULL;
6371 }
6372 
6373 
6374 FSharedStringArena AActor::mStringPropertyData;
6375 
GetTag(const char * def) const6376 const char *AActor::GetTag(const char *def) const
6377 {
6378 	if (Tag != NULL)
6379 	{
6380 		const char *tag = Tag->GetChars();
6381 		if (tag[0] == '$')
6382 		{
6383 			return GStrings(tag + 1);
6384 		}
6385 		else
6386 		{
6387 			return tag;
6388 		}
6389 	}
6390 	else if (def)
6391 	{
6392 		return def;
6393 	}
6394 	else
6395 	{
6396 		return GetClass()->TypeName.GetChars();
6397 	}
6398 }
6399 
SetTag(const char * def)6400 void AActor::SetTag(const char *def)
6401 {
6402 	if (def == NULL || *def == 0)
6403 	{
6404 		Tag = NULL;
6405 	}
6406 	else
6407 	{
6408 		Tag = mStringPropertyData.Alloc(def);
6409 	}
6410 }
6411 
6412 
ClearCounters()6413 void AActor::ClearCounters()
6414 {
6415 	if (CountsAsKill() && health > 0)
6416 	{
6417 		level.total_monsters--;
6418 		flags &= ~MF_COUNTKILL;
6419 	}
6420 	// Same, for items
6421 	if (flags & MF_COUNTITEM)
6422 	{
6423 		level.total_items--;
6424 		flags &= ~MF_COUNTITEM;
6425 	}
6426 	// And finally for secrets
6427 	if (flags5 & MF5_COUNTSECRET)
6428 	{
6429 		level.total_secrets--;
6430 		flags5 &= ~MF5_COUNTSECRET;
6431 	}
6432 }
6433 
6434 
6435 //----------------------------------------------------------------------------
6436 //
6437 // DropItem handling
6438 //
6439 //----------------------------------------------------------------------------
6440 FDropItemPtrArray DropItemList;
6441 
FreeDropItemChain(FDropItem * chain)6442 void FreeDropItemChain(FDropItem *chain)
6443 {
6444 	while (chain != NULL)
6445 	{
6446 		FDropItem *next = chain->Next;
6447 		delete chain;
6448 		chain = next;
6449 	}
6450 }
6451 
Clear()6452 void FDropItemPtrArray::Clear()
6453 {
6454 	for (unsigned int i = 0; i < Size(); ++i)
6455 	{
6456 		FreeDropItemChain ((*this)[i]);
6457 	}
6458 	TArray<FDropItem *>::Clear();
6459 }
6460 
StoreDropItemChain(FDropItem * chain)6461 int StoreDropItemChain(FDropItem *chain)
6462 {
6463 	return DropItemList.Push (chain) + 1;
6464 }
6465 
PrintMiscActorInfo(AActor * query)6466 void PrintMiscActorInfo(AActor *query)
6467 {
6468 	if (query)
6469 	{
6470 		int flagi;
6471 		int querystyle = STYLE_Count;
6472 		for (int style = STYLE_None; style < STYLE_Count; ++style)
6473 		{ // Check for a legacy render style that matches.
6474 			if (LegacyRenderStyles[style] == query->RenderStyle)
6475 			{
6476 				querystyle = style;
6477 				break;
6478 			}
6479 		}
6480 		static const char * renderstyles[]= {"None", "Normal", "Fuzzy", "SoulTrans",
6481 			"OptFuzzy", "Stencil", "Translucent", "Add", "Shaded", "TranslucentStencil",
6482 			"Shadow", "Subtract", "AddStencil", "AddShaded"};
6483 
6484 		Printf("%s @ %p has the following flags:\n   flags: %x", query->GetTag(), query, query->flags.GetValue());
6485 		for (flagi = 0; flagi <= 31; flagi++)
6486 			if (query->flags & ActorFlags::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags));
6487 		Printf("\n   flags2: %x", query->flags2.GetValue());
6488 		for (flagi = 0; flagi <= 31; flagi++)
6489 			if (query->flags2 & ActorFlags2::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags2));
6490 		Printf("\n   flags3: %x", query->flags3.GetValue());
6491 		for (flagi = 0; flagi <= 31; flagi++)
6492 			if (query->flags3 & ActorFlags3::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags3));
6493 		Printf("\n   flags4: %x", query->flags4.GetValue());
6494 		for (flagi = 0; flagi <= 31; flagi++)
6495 			if (query->flags4 & ActorFlags4::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags4));
6496 		Printf("\n   flags5: %x", query->flags5.GetValue());
6497 		for (flagi = 0; flagi <= 31; flagi++)
6498 			if (query->flags5 & ActorFlags5::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags5));
6499 		Printf("\n   flags6: %x", query->flags6.GetValue());
6500 		for (flagi = 0; flagi <= 31; flagi++)
6501 			if (query->flags6 & ActorFlags6::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags6));
6502 		Printf("\n   flags7: %x", query->flags7.GetValue());
6503 		for (flagi = 0; flagi <= 31; flagi++)
6504 			if (query->flags7 & ActorFlags7::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags7));
6505 		Printf("\nBounce flags: %x\nBounce factors: f:%f, w:%f",
6506 			query->BounceFlags.GetValue(), FIXED2FLOAT(query->bouncefactor),
6507 			FIXED2FLOAT(query->wallbouncefactor));
6508 		/*for (flagi = 0; flagi < 31; flagi++)
6509 			if (query->BounceFlags & 1<<flagi) Printf(" %s", flagnamesb[flagi]);*/
6510 		Printf("\nRender style = %i:%s, alpha %f\nRender flags: %x",
6511 			querystyle, (querystyle < STYLE_Count ? renderstyles[querystyle] : "Unknown"),
6512 			FIXED2FLOAT(query->alpha), query->renderflags.GetValue());
6513 		/*for (flagi = 0; flagi < 31; flagi++)
6514 			if (query->renderflags & 1<<flagi) Printf(" %s", flagnamesr[flagi]);*/
6515 		Printf("\nSpecial+args: %s(%i, %i, %i, %i, %i)\nspecial1: %i, special2: %i.",
6516 			(query->special ? LineSpecialsInfo[query->special]->name : "None"),
6517 			query->args[0], query->args[1], query->args[2], query->args[3],
6518 			query->args[4],	query->special1, query->special2);
6519 		Printf("\nTID: %d", query->tid);
6520 		Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f.",
6521 			FIXED2FLOAT(query->X()), FIXED2FLOAT(query->Y()), FIXED2FLOAT(query->Z()),
6522 			FIXED2FLOAT(query->floorz), FIXED2FLOAT(query->ceilingz));
6523 		Printf("\nSpeed= %f, velocity= x:%f, y:%f, z:%f, combined:%f.\n",
6524 			FIXED2FLOAT(query->Speed), FIXED2FLOAT(query->velx), FIXED2FLOAT(query->vely), FIXED2FLOAT(query->velz),
6525 			sqrt(pow(FIXED2FLOAT(query->velx), 2) + pow(FIXED2FLOAT(query->vely), 2) + pow(FIXED2FLOAT(query->velz), 2)));
6526 	}
6527 }
6528