1 #include <assert.h>
2 
3 #include "info.h"
4 #include "m_random.h"
5 #include "p_local.h"
6 #include "s_sound.h"
7 #include "gi.h"
8 #include "p_lnspec.h"
9 #include "sbar.h"
10 #include "statnums.h"
11 #include "c_dispatch.h"
12 #include "gstrings.h"
13 #include "templates.h"
14 #include "a_strifeglobal.h"
15 #include "a_morph.h"
16 #include "a_specialspot.h"
17 #include "thingdef/thingdef.h"
18 #include "g_level.h"
19 #include "g_game.h"
20 #include "doomstat.h"
21 #include "farchive.h"
22 
23 static FRandom pr_restore ("RestorePos");
24 
IMPLEMENT_CLASS(AAmmo)25 IMPLEMENT_CLASS (AAmmo)
26 
27 //===========================================================================
28 //
29 // AAmmo :: Serialize
30 //
31 //===========================================================================
32 
33 void AAmmo::Serialize (FArchive &arc)
34 {
35 	Super::Serialize (arc);
36 	arc << BackpackAmount << BackpackMaxAmount;
37 }
38 
39 //===========================================================================
40 //
41 // AAmmo :: GetParentAmmo
42 //
43 // Returns the least-derived ammo type that this ammo is a descendant of.
44 // That is, if this ammo is an immediate subclass of Ammo, then this ammo's
45 // type is returned. If this ammo's superclass is not Ammo, then this
46 // function travels up the inheritance chain until it finds a type that is
47 // an immediate subclass of Ammo and returns that.
48 //
49 // The intent of this is that all unique ammo types will be immediate
50 // subclasses of Ammo. To make different pickups with different ammo amounts,
51 // you subclass the type of ammo you want a different amount for and edit
52 // that.
53 //
54 //===========================================================================
55 
GetParentAmmo() const56 const PClass *AAmmo::GetParentAmmo () const
57 {
58 	const PClass *type = GetClass ();
59 
60 	while (type->ParentClass != RUNTIME_CLASS(AAmmo) && type->ParentClass != NULL)
61 	{
62 		type = type->ParentClass;
63 	}
64 	return type;
65 }
66 
67 //===========================================================================
68 //
69 // AAmmo :: HandlePickup
70 //
71 //===========================================================================
EXTERN_CVAR(Bool,sv_unlimited_pickup)72 EXTERN_CVAR(Bool, sv_unlimited_pickup)
73 
74 bool AAmmo::HandlePickup (AInventory *item)
75 {
76 	if (GetClass() == item->GetClass() ||
77 		(item->IsKindOf (RUNTIME_CLASS(AAmmo)) && static_cast<AAmmo*>(item)->GetParentAmmo() == GetClass()))
78 	{
79 		if (Amount < MaxAmount || sv_unlimited_pickup)
80 		{
81 			int receiving = item->Amount;
82 
83 			if (!(item->ItemFlags & IF_IGNORESKILL))
84 			{ // extra ammo in baby mode and nightmare mode
85 				receiving = FixedMul(receiving, G_SkillProperty(SKILLP_AmmoFactor));
86 			}
87 			int oldamount = Amount;
88 
89 			if (Amount > 0 && Amount + receiving < 0)
90 			{
91 				Amount = 0x7fffffff;
92 			}
93 			else
94 			{
95 				Amount += receiving;
96 			}
97 			if (Amount > MaxAmount && !sv_unlimited_pickup)
98 			{
99 				Amount = MaxAmount;
100 			}
101 			item->ItemFlags |= IF_PICKUPGOOD;
102 
103 			// If the player previously had this ammo but ran out, possibly switch
104 			// to a weapon that uses it, but only if the player doesn't already
105 			// have a weapon pending.
106 
107 			assert (Owner != NULL);
108 
109 			if (oldamount == 0 && Owner != NULL && Owner->player != NULL)
110 			{
111 				barrier_cast<APlayerPawn *>(Owner)->CheckWeaponSwitch(GetClass());
112 			}
113 		}
114 		return true;
115 	}
116 	if (Inventory != NULL)
117 	{
118 		return Inventory->HandlePickup (item);
119 	}
120 	return false;
121 }
122 
123 //===========================================================================
124 //
125 // AAmmo :: CreateCopy
126 //
127 //===========================================================================
128 
CreateCopy(AActor * other)129 AInventory *AAmmo::CreateCopy (AActor *other)
130 {
131 	AInventory *copy;
132 	int amount = Amount;
133 
134 	// extra ammo in baby mode and nightmare mode
135 	if (!(ItemFlags&IF_IGNORESKILL))
136 	{
137 		amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor));
138 	}
139 
140 	if (GetClass()->ParentClass != RUNTIME_CLASS(AAmmo) && GetClass() != RUNTIME_CLASS(AAmmo))
141 	{
142 		const PClass *type = GetParentAmmo();
143 		assert (type->ActorInfo != NULL);
144 		if (!GoAway ())
145 		{
146 			Destroy ();
147 		}
148 
149 		copy = static_cast<AInventory *>(Spawn (type, 0, 0, 0, NO_REPLACE));
150 		copy->Amount = amount;
151 		copy->BecomeItem ();
152 	}
153 	else
154 	{
155 		copy = Super::CreateCopy (other);
156 		copy->Amount = amount;
157 	}
158 	if (copy->Amount > copy->MaxAmount)
159 	{ // Don't pick up more ammo than you're supposed to be able to carry.
160 		copy->Amount = copy->MaxAmount;
161 	}
162 	return copy;
163 }
164 
165 //===========================================================================
166 //
167 // AAmmo :: CreateTossable
168 //
169 //===========================================================================
170 
CreateTossable()171 AInventory *AAmmo::CreateTossable()
172 {
173 	AInventory *copy = Super::CreateTossable();
174 	if (copy != NULL)
175 	{ // Do not increase ammo by dropping it and picking it back up at
176 	  // certain skill levels.
177 		copy->ItemFlags |= IF_IGNORESKILL;
178 	}
179 	return copy;
180 }
181 
182 //---------------------------------------------------------------------------
183 //
184 // FUNC P_GiveBody
185 //
186 // Returns false if the body isn't needed at all.
187 //
188 //---------------------------------------------------------------------------
189 
P_GiveBody(AActor * actor,int num,int max)190 bool P_GiveBody (AActor *actor, int num, int max)
191 {
192 	if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD))
193 	{ // Do not heal dead things.
194 		return false;
195 	}
196 
197 	player_t *player = actor->player;
198 
199 	num = clamp(num, -65536, 65536);	// prevent overflows for bad values
200 	if (player != NULL)
201 	{
202 		// Max is 0 by default, preserving default behavior for P_GiveBody()
203 		// calls while supporting AHealth.
204 		if (max <= 0)
205 		{
206 			max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
207 			// [MH] First step in predictable generic morph effects
208  			if (player->morphTics)
209  			{
210 				if (player->MorphStyle & MORPH_FULLHEALTH)
211 				{
212 					if (!(player->MorphStyle & MORPH_ADDSTAMINA))
213 					{
214 						max -= player->mo->stamina;
215 					}
216 				}
217 				else // old health behaviour
218 				{
219 					max = MAXMORPHHEALTH;
220 					if (player->MorphStyle & MORPH_ADDSTAMINA)
221 					{
222 						max += player->mo->stamina;
223 					}
224 				}
225  			}
226 		}
227 		// [RH] For Strife: A negative body sets you up with a percentage
228 		// of your full health.
229 		if (num < 0)
230 		{
231 			num = max * -num / 100;
232 			if (player->health < num)
233 			{
234 				player->health = num;
235 				actor->health = num;
236 				return true;
237 			}
238 		}
239 		else if (num > 0)
240 		{
241 			if (player->health < max)
242 			{
243 				num = FixedMul(num, G_SkillProperty(SKILLP_HealthFactor));
244 				if (num < 1) num = 1;
245 				player->health += num;
246 				if (player->health > max)
247 				{
248 					player->health = max;
249 				}
250 				actor->health = player->health;
251 				return true;
252 			}
253 		}
254 	}
255 	else
256 	{
257 		// Parameter value for max is ignored on monsters, preserving original
258 		// behaviour on AHealth as well as on existing calls to P_GiveBody().
259 		max = actor->SpawnHealth();
260 		if (num < 0)
261 		{
262 			num = max * -num / 100;
263 			if (actor->health < num)
264 			{
265 				actor->health = num;
266 				return true;
267 			}
268 		}
269 		else if (actor->health < max)
270 		{
271 			actor->health += num;
272 			if (actor->health > max)
273 			{
274 				actor->health = max;
275 			}
276 			return true;
277 		}
278 	}
279 	return false;
280 }
281 
282 //---------------------------------------------------------------------------
283 //
284 // PROC A_RestoreSpecialThing1
285 //
286 // Make a special thing visible again.
287 //
288 //---------------------------------------------------------------------------
289 
DEFINE_ACTION_FUNCTION(AActor,A_RestoreSpecialThing1)290 DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing1)
291 {
292 	self->renderflags &= ~RF_INVISIBLE;
293 	if (static_cast<AInventory *>(self)->DoRespawn ())
294 	{
295 		S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
296 	}
297 }
298 
299 //---------------------------------------------------------------------------
300 //
301 // PROC A_RestoreSpecialThing2
302 //
303 //---------------------------------------------------------------------------
304 
DEFINE_ACTION_FUNCTION(AActor,A_RestoreSpecialThing2)305 DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing2)
306 {
307 	self->flags |= MF_SPECIAL;
308 	if (!(self->GetDefault()->flags & MF_NOGRAVITY))
309 	{
310 		self->flags &= ~MF_NOGRAVITY;
311 	}
312 	self->SetState (self->SpawnState);
313 }
314 
315 
316 //---------------------------------------------------------------------------
317 //
318 // PROC A_RestoreSpecialDoomThing
319 //
320 //---------------------------------------------------------------------------
321 
DEFINE_ACTION_FUNCTION(AActor,A_RestoreSpecialDoomThing)322 DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing)
323 {
324 	self->renderflags &= ~RF_INVISIBLE;
325 	self->flags |= MF_SPECIAL;
326 	if (!(self->GetDefault()->flags & MF_NOGRAVITY))
327 	{
328 		self->flags &= ~MF_NOGRAVITY;
329 	}
330 	if (static_cast<AInventory *>(self)->DoRespawn ())
331 	{
332 		self->SetState (self->SpawnState);
333 		S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
334 		Spawn ("ItemFog", self->Pos(), ALLOW_REPLACE);
335 	}
336 }
337 
338 //---------------------------------------------------------------------------
339 //
340 // PROP A_RestoreSpecialPosition
341 //
342 //---------------------------------------------------------------------------
343 
DEFINE_ACTION_FUNCTION(AActor,A_RestoreSpecialPosition)344 DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition)
345 {
346 	// Move item back to its original location
347 	fixed_t _x, _y;
348 	sector_t *sec;
349 
350 	_x = self->SpawnPoint[0];
351 	_y = self->SpawnPoint[1];
352 
353 	self->UnlinkFromWorld();
354 	self->SetXY(_x, _y);
355 	self->LinkToWorld(true);
356 	sec = self->Sector;
357 	self->dropoffz =
358 	self->floorz = sec->floorplane.ZatPoint(_x, _y);
359 	self->ceilingz = sec->ceilingplane.ZatPoint(_x, _y);
360 	self->SetZ(self->floorz);
361 	P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS);
362 
363 	if (self->flags & MF_SPAWNCEILING)
364 	{
365 		self->SetZ(self->ceilingz - self->height - self->SpawnPoint[2]);
366 	}
367 	else if (self->flags2 & MF2_SPAWNFLOAT)
368 	{
369 		fixed_t space = self->ceilingz - self->height - self->floorz;
370 		if (space > 48*FRACUNIT)
371 		{
372 			space -= 40*FRACUNIT;
373 			self->SetZ(((space * pr_restore())>>8) + self->floorz + 40*FRACUNIT);
374 		}
375 		else
376 		{
377 			self->SetZ(self->floorz);
378 		}
379 	}
380 	else
381 	{
382 		self->SetZ(self->SpawnPoint[2] + self->floorz);
383 	}
384 	// Redo floor/ceiling check, in case of 3D floors
385 	P_FindFloorCeiling(self, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
386 	if (self->Z() < self->floorz)
387 	{ // Do not reappear under the floor, even if that's where we were for the
388 	  // initial spawn.
389 		self->SetZ(self->floorz);
390 	}
391 	if ((self->flags & MF_SOLID) && (self->Top() > self->ceilingz))
392 	{ // Do the same for the ceiling.
393 		self->SetZ(self->ceilingz - self->height);
394 	}
395 	// Do not interpolate from the position the actor was at when it was
396 	// picked up, in case that is different from where it is now.
397 	self->PrevX = self->X();
398 	self->PrevY = self->Y();
399 	self->PrevZ = self->Z();
400 }
401 
402 int AInventory::StaticLastMessageTic;
403 const char *AInventory::StaticLastMessage;
404 
405 IMPLEMENT_POINTY_CLASS (AInventory)
DECLARE_POINTER(Owner)406  DECLARE_POINTER (Owner)
407 END_POINTERS
408 
409 //===========================================================================
410 //
411 // AInventory :: Tick
412 //
413 //===========================================================================
414 
415 void AInventory::Tick ()
416 {
417 	if (Owner == NULL)
418 	{
419 		// AActor::Tick is only handling interaction with the world
420 		// and we don't want that for owned inventory items.
421 		Super::Tick ();
422 	}
423 	else if (tics != -1)	// ... but at least we have to advance the states
424 	{
425 		tics--;
426 
427 		// you can cycle through multiple states in a tic
428 		// [RH] Use <= 0 instead of == 0 so that spawnstates
429 		// of 0 tics work as expected.
430 		if (tics <= 0)
431 		{
432 			assert (state != NULL);
433 			if (state == NULL)
434 			{
435 				Destroy();
436 				return;
437 			}
438 			if (!SetState (state->GetNextState()))
439 				return; 		// freed itself
440 		}
441 	}
442 	if (DropTime)
443 	{
444 		if (--DropTime == 0)
445 		{
446 			flags |= GetDefault()->flags & (MF_SPECIAL|MF_SOLID);
447 		}
448 	}
449 }
450 
451 //===========================================================================
452 //
453 // AInventory :: Serialize
454 //
455 //===========================================================================
456 
Serialize(FArchive & arc)457 void AInventory::Serialize (FArchive &arc)
458 {
459 	Super::Serialize (arc);
460 	arc << Owner << Amount << MaxAmount << RespawnTics << ItemFlags << Icon << PickupSound << SpawnPointClass;
461 }
462 
463 //===========================================================================
464 //
465 // AInventory :: MarkPrecacheSounds
466 //
467 //===========================================================================
468 
MarkPrecacheSounds() const469 void AInventory::MarkPrecacheSounds() const
470 {
471 	Super::MarkPrecacheSounds();
472 	PickupSound.MarkUsed();
473 }
474 
475 //===========================================================================
476 //
477 // AInventory :: SpecialDropAction
478 //
479 // Called by P_DropItem. Return true to prevent the standard drop tossing.
480 // A few Strife items that are meant to trigger actions rather than be
481 // picked up use this. Normal items shouldn't need it.
482 //
483 //===========================================================================
484 
SpecialDropAction(AActor * dropper)485 bool AInventory::SpecialDropAction (AActor *dropper)
486 {
487 	return false;
488 }
489 
490 //===========================================================================
491 //
492 // AInventory :: ShouldRespawn
493 //
494 // Returns true if the item should hide itself and reappear later when picked
495 // up.
496 //
497 //===========================================================================
498 
ShouldRespawn()499 bool AInventory::ShouldRespawn ()
500 {
501 	if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false;
502 	if (ItemFlags & IF_NEVERRESPAWN) return false;
503 	return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN));
504 }
505 
506 //===========================================================================
507 //
508 // AInventory :: BeginPlay
509 //
510 //===========================================================================
511 
BeginPlay()512 void AInventory::BeginPlay ()
513 {
514 	Super::BeginPlay ();
515 	ChangeStatNum (STAT_INVENTORY);
516 	flags |= MF_DROPPED;	// [RH] Items are dropped by default
517 }
518 
519 //===========================================================================
520 //
521 // AInventory :: Grind
522 //
523 //===========================================================================
524 
Grind(bool items)525 bool AInventory::Grind(bool items)
526 {
527 	// Does this grind request even care about items?
528 	if (!items)
529 	{
530 		return false;
531 	}
532 	// Dropped items are normally destroyed by crushers. Set the DONTGIB flag,
533 	// and they'll act like corpses with it set and be immune to crushers.
534 	if (flags & MF_DROPPED)
535 	{
536 		if (!(flags3 & MF3_DONTGIB))
537 		{
538 			Destroy();
539 		}
540 		return false;
541 	}
542 	// Non-dropped items call the super method for compatibility.
543 	return Super::Grind(items);
544 }
545 
546 //===========================================================================
547 //
548 // AInventory :: DoEffect
549 //
550 // Handles any effect an item might apply to its owner
551 // Normally only used by subclasses of APowerup
552 //
553 //===========================================================================
554 
DoEffect()555 void AInventory::DoEffect ()
556 {
557 }
558 
559 //===========================================================================
560 //
561 // AInventory :: Travelled
562 //
563 // Called when an item in somebody's inventory is carried over to another
564 // map, in case it needs to do special reinitialization.
565 //
566 //===========================================================================
567 
Travelled()568 void AInventory::Travelled ()
569 {
570 }
571 
572 //===========================================================================
573 //
574 // AInventory :: OwnerDied
575 //
576 // Items receive this message when their owners die.
577 //
578 //===========================================================================
579 
OwnerDied()580 void AInventory::OwnerDied ()
581 {
582 }
583 
584 //===========================================================================
585 //
586 // AInventory :: HandlePickup
587 //
588 // Returns true if the pickup was handled (or should not happen at all),
589 // false if not.
590 //
591 //===========================================================================
592 
HandlePickup(AInventory * item)593 bool AInventory::HandlePickup (AInventory *item)
594 {
595 	if (item->GetClass() == GetClass())
596 	{
597 		if (Amount < MaxAmount || (sv_unlimited_pickup && !item->ShouldStay()))
598 		{
599 			if (Amount > 0 && Amount + item->Amount < 0)
600 			{
601 				Amount = 0x7fffffff;
602 			}
603 			else
604 			{
605 				Amount += item->Amount;
606 			}
607 
608 			if (Amount > MaxAmount && !sv_unlimited_pickup)
609 			{
610 				Amount = MaxAmount;
611 			}
612 			item->ItemFlags |= IF_PICKUPGOOD;
613 		}
614 		return true;
615 	}
616 	if (Inventory != NULL)
617 	{
618 		return Inventory->HandlePickup (item);
619 	}
620 	return false;
621 }
622 
623 //===========================================================================
624 //
625 // AInventory :: GoAway
626 //
627 // Returns true if you must create a copy of this item to give to the player
628 // or false if you can use this one instead.
629 //
630 //===========================================================================
631 
GoAway()632 bool AInventory::GoAway ()
633 {
634 	// Dropped items never stick around
635 	if (flags & MF_DROPPED)
636 	{
637 		return false;
638 	}
639 
640 	if (!ShouldStay ())
641 	{
642 		Hide ();
643 		if (ShouldRespawn ())
644 		{
645 			return true;
646 		}
647 		return false;
648 	}
649 	return true;
650 }
651 
652 //===========================================================================
653 //
654 // AInventory :: GoAwayAndDie
655 //
656 // Like GoAway but used by items that don't insert themselves into the
657 // inventory. If they won't be respawning, then they can destroy themselves.
658 //
659 //===========================================================================
660 
GoAwayAndDie()661 void AInventory::GoAwayAndDie ()
662 {
663 	if (!GoAway ())
664 	{
665 		flags &= ~MF_SPECIAL;
666 		SetState (FindState("HoldAndDestroy"));
667 	}
668 }
669 
670 //===========================================================================
671 //
672 // AInventory :: CreateCopy
673 //
674 // Returns an actor suitable for placing in an inventory, either itself or
675 // a copy based on whether it needs to respawn or not. Returning NULL
676 // indicates the item should not be picked up.
677 //
678 //===========================================================================
679 
CreateCopy(AActor * other)680 AInventory *AInventory::CreateCopy (AActor *other)
681 {
682 	AInventory *copy;
683 
684 	Amount = MIN(Amount, MaxAmount);
685 	if (GoAway ())
686 	{
687 		copy = static_cast<AInventory *>(Spawn (GetClass(), 0, 0, 0, NO_REPLACE));
688 		copy->Amount = Amount;
689 		copy->MaxAmount = MaxAmount;
690 	}
691 	else
692 	{
693 		copy = this;
694 	}
695 	return copy;
696 }
697 
698 //===========================================================================
699 //
700 // AInventory::CreateTossable
701 //
702 // Creates a copy of the item suitable for dropping. If this actor embodies
703 // only one item, then it is tossed out itself. Otherwise, the count drops
704 // by one and a new item with an amount of 1 is spawned.
705 //
706 //===========================================================================
707 
CreateTossable()708 AInventory *AInventory::CreateTossable ()
709 {
710 	AInventory *copy;
711 
712 	// If this actor lacks a SpawnState, don't drop it. (e.g. A base weapon
713 	// like the fist can't be dropped because you'll never see it.)
714 	if (SpawnState == ::GetDefault<AActor>()->SpawnState ||
715 		SpawnState == NULL)
716 	{
717 		return NULL;
718 	}
719 	if ((ItemFlags & (IF_UNDROPPABLE|IF_UNTOSSABLE)) || Owner == NULL || Amount <= 0)
720 	{
721 		return NULL;
722 	}
723 	if (Amount == 1 && !(ItemFlags & IF_KEEPDEPLETED))
724 	{
725 		BecomePickup ();
726 		DropTime = 30;
727 		flags &= ~(MF_SPECIAL|MF_SOLID);
728 		return this;
729 	}
730 	copy = static_cast<AInventory *>(Spawn (GetClass(), Owner->Pos(), NO_REPLACE));
731 	if (copy != NULL)
732 	{
733 		copy->MaxAmount = MaxAmount;
734 		copy->Amount = 1;
735 		copy->DropTime = 30;
736 		copy->flags &= ~(MF_SPECIAL|MF_SOLID);
737 		Amount--;
738 	}
739 	return copy;
740 }
741 
742 //===========================================================================
743 //
744 // AInventory :: BecomeItem
745 //
746 // Lets this actor know that it's about to be placed in an inventory.
747 //
748 //===========================================================================
749 
BecomeItem()750 void AInventory::BecomeItem ()
751 {
752 	if (!(flags & (MF_NOBLOCKMAP|MF_NOSECTOR)))
753 	{
754 		UnlinkFromWorld ();
755 		if (sector_list)
756 		{
757 			P_DelSeclist (sector_list);
758 			sector_list = NULL;
759 		}
760 		flags |= MF_NOBLOCKMAP|MF_NOSECTOR;
761 		LinkToWorld ();
762 	}
763 	RemoveFromHash ();
764 	flags &= ~MF_SPECIAL;
765 	SetState (FindState("Held"));
766 }
767 
768 //===========================================================================
769 //
770 // AInventory :: BecomePickup
771 //
772 // Lets this actor know it should wait to be picked up.
773 //
774 //===========================================================================
775 
BecomePickup()776 void AInventory::BecomePickup ()
777 {
778 	if (Owner != NULL)
779 	{
780 		Owner->RemoveInventory (this);
781 	}
782 	if (flags & (MF_NOBLOCKMAP|MF_NOSECTOR))
783 	{
784 		UnlinkFromWorld ();
785 		flags &= ~(MF_NOBLOCKMAP|MF_NOSECTOR);
786 		LinkToWorld ();
787 		P_FindFloorCeiling (this);
788 	}
789 	flags = (GetDefault()->flags | MF_DROPPED) & ~MF_COUNTITEM;
790 	renderflags &= ~RF_INVISIBLE;
791 	SetState (SpawnState);
792 }
793 
794 //===========================================================================
795 //
796 // AInventory :: AbsorbDamage
797 //
798 // Allows inventory items (primarily armor) to reduce the amount of damage
799 // taken. Damage is the amount of damage that would be done without armor,
800 // and newdamage is the amount that should be done after the armor absorbs
801 // it.
802 //
803 //===========================================================================
804 
AbsorbDamage(int damage,FName damageType,int & newdamage)805 void AInventory::AbsorbDamage (int damage, FName damageType, int &newdamage)
806 {
807 	if (Inventory != NULL)
808 	{
809 		Inventory->AbsorbDamage (damage, damageType, newdamage);
810 	}
811 }
812 
813 //===========================================================================
814 //
815 // AInventory :: ModifyDamage
816 //
817 // Allows inventory items to manipulate the amount of damage
818 // inflicted. Damage is the amount of damage that would be done without manipulation,
819 // and newdamage is the amount that should be done after the item has changed
820 // it.
821 // 'active' means it is called by the inflictor, 'passive' by the target.
822 // It may seem that this is redundant and AbsorbDamage is the same. However,
823 // AbsorbDamage is called only for players and also depends on other settings
824 // which are undesirable for a protection artifact.
825 //
826 //===========================================================================
827 
ModifyDamage(int damage,FName damageType,int & newdamage,bool passive)828 void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, bool passive)
829 {
830 	if (Inventory != NULL)
831 	{
832 		Inventory->ModifyDamage (damage, damageType, newdamage, passive);
833 	}
834 }
835 
836 //===========================================================================
837 //
838 // AInventory :: GetSpeedFactor
839 //
840 //===========================================================================
841 
GetSpeedFactor()842 fixed_t AInventory::GetSpeedFactor ()
843 {
844 	if (Inventory != NULL)
845 	{
846 		return Inventory->GetSpeedFactor();
847 	}
848 	else
849 	{
850 		return FRACUNIT;
851 	}
852 }
853 
854 //===========================================================================
855 //
856 // AInventory :: GetNoTeleportFreeze
857 //
858 //===========================================================================
859 
GetNoTeleportFreeze()860 bool AInventory::GetNoTeleportFreeze ()
861 {
862 	// do not check the flag here because it's only active when used on PowerUps, not on PowerupGivers.
863 	if (Inventory != NULL)
864 	{
865 		return Inventory->GetNoTeleportFreeze();
866 	}
867 	else
868 	{
869 		return false;
870 	}
871 }
872 
873 //===========================================================================
874 //
875 // AInventory :: AlterWeaponSprite
876 //
877 // Allows inventory items to alter a player's weapon sprite just before it
878 // is drawn.
879 //
880 //===========================================================================
881 
AlterWeaponSprite(visstyle_t * vis)882 int AInventory::AlterWeaponSprite (visstyle_t *vis)
883 {
884 	if (Inventory != NULL)
885 	{
886 		return Inventory->AlterWeaponSprite (vis);
887 	}
888 	return 0;
889 }
890 
891 //===========================================================================
892 //
893 // AInventory :: Use
894 //
895 //===========================================================================
896 
Use(bool pickup)897 bool AInventory::Use (bool pickup)
898 {
899 	return false;
900 }
901 
902 //===========================================================================
903 //
904 // AInventory :: Hide
905 //
906 // Hides this actor until it's time to respawn again.
907 //
908 //===========================================================================
909 
Hide()910 void AInventory::Hide ()
911 {
912 	FState *HideSpecialState = NULL, *HideDoomishState = NULL;
913 
914  	flags = (flags & ~MF_SPECIAL) | MF_NOGRAVITY;
915 	renderflags |= RF_INVISIBLE;
916 
917 	if (gameinfo.gametype & GAME_Raven)
918 	{
919 		HideSpecialState = FindState("HideSpecial");
920 		if (HideSpecialState == NULL)
921 		{
922 			HideDoomishState = FindState("HideDoomish");
923 		}
924 	}
925 	else
926 	{
927 		HideDoomishState = FindState("HideDoomish");
928 		if (HideDoomishState == NULL)
929 		{
930 			HideSpecialState = FindState("HideSpecial");
931 		}
932 	}
933 
934 	assert(HideDoomishState != NULL || HideSpecialState != NULL);
935 
936 	if (HideSpecialState != NULL)
937 	{
938 		SetState (HideSpecialState);
939 		tics = 1400;
940 		if (PickupFlash != NULL) tics += 30;
941 	}
942 	else if (HideDoomishState != NULL)
943 	{
944 		SetState (HideDoomishState);
945 		tics = 1050;
946 	}
947 	if (RespawnTics != 0)
948 	{
949 		tics = RespawnTics;
950 	}
951 }
952 
953 
954 //===========================================================================
955 //
956 //
957 //===========================================================================
958 
PrintPickupMessage(const char * str)959 static void PrintPickupMessage (const char *str)
960 {
961 	if (str != NULL)
962 	{
963 		if (str[0]=='$')
964 		{
965 			str=GStrings(str+1);
966 		}
967 		if (str[0] != 0) Printf (PRINT_LOW, "%s\n", str);
968 	}
969 }
970 
971 //===========================================================================
972 //
973 // AInventory :: Touch
974 //
975 // Handles collisions from another actor, possible adding itself to the
976 // collider's inventory.
977 //
978 //===========================================================================
979 
Touch(AActor * toucher)980 void AInventory::Touch (AActor *toucher)
981 {
982 	player_t *player = toucher->player;
983 
984 	// If a voodoo doll touches something, pretend the real player touched it instead.
985 	if (player != NULL)
986 	{
987 		toucher = player->mo;
988 	}
989 
990 	bool localview = toucher->CheckLocalView(consoleplayer);
991 
992 	if (!CallTryPickup (toucher, &toucher)) return;
993 
994 	// This is the only situation when a pickup flash should ever play.
995 	if (PickupFlash != NULL && !ShouldStay())
996 	{
997 		Spawn(PickupFlash, Pos(), ALLOW_REPLACE);
998 	}
999 
1000 	if (!(ItemFlags & IF_QUIET))
1001 	{
1002 		const char * message = PickupMessage ();
1003 
1004 		if (message != NULL && *message != 0 && localview
1005 			&& (StaticLastMessageTic != gametic || StaticLastMessage != message))
1006 		{
1007 			StaticLastMessageTic = gametic;
1008 			StaticLastMessage = message;
1009 			PrintPickupMessage (message);
1010 			StatusBar->FlashCrosshair ();
1011 		}
1012 
1013 		// Special check so voodoo dolls picking up items cause the
1014 		// real player to make noise.
1015 		if (player != NULL)
1016 		{
1017 			PlayPickupSound (player->mo);
1018 			if (!(ItemFlags & IF_NOSCREENFLASH))
1019 			{
1020 				player->bonuscount = BONUSADD;
1021 			}
1022 		}
1023 		else
1024 		{
1025 			PlayPickupSound (toucher);
1026 		}
1027 	}
1028 
1029 	// [RH] Execute an attached special (if any)
1030 	DoPickupSpecial (toucher);
1031 
1032 	if (flags & MF_COUNTITEM)
1033 	{
1034 		if (player != NULL)
1035 		{
1036 			player->itemcount++;
1037 		}
1038 		level.found_items++;
1039 	}
1040 
1041 	if (flags5 & MF5_COUNTSECRET)
1042 	{
1043 		P_GiveSecret(player != NULL? (AActor*)player->mo : toucher, true, true, -1);
1044 	}
1045 
1046 	//Added by MC: Check if item taken was the roam destination of any bot
1047 	for (int i = 0; i < MAXPLAYERS; i++)
1048 	{
1049 		if (players[i].Bot != NULL && this == players[i].Bot->dest)
1050 			players[i].Bot->dest = NULL;
1051 	}
1052 }
1053 
1054 //===========================================================================
1055 //
1056 // AInventory :: DoPickupSpecial
1057 //
1058 // Executes this actor's special when it is picked up.
1059 //
1060 //===========================================================================
1061 
DoPickupSpecial(AActor * toucher)1062 void AInventory::DoPickupSpecial (AActor *toucher)
1063 {
1064 	if (special)
1065 	{
1066 		P_ExecuteSpecial(special, NULL, toucher, false,
1067 			args[0], args[1], args[2], args[3], args[4]);
1068 		special = 0;
1069 	}
1070 }
1071 
1072 //===========================================================================
1073 //
1074 // AInventory :: PickupMessage
1075 //
1076 // Returns the message to print when this actor is picked up.
1077 //
1078 //===========================================================================
1079 
PickupMessage()1080 const char *AInventory::PickupMessage ()
1081 {
1082 	return GetClass()->Meta.GetMetaString (AIMETA_PickupMessage);
1083 }
1084 
1085 //===========================================================================
1086 //
1087 // AInventory :: PlayPickupSound
1088 //
1089 //===========================================================================
1090 
PlayPickupSound(AActor * toucher)1091 void AInventory::PlayPickupSound (AActor *toucher)
1092 {
1093 	float atten;
1094 	int chan;
1095 
1096 	if (ItemFlags & IF_NOATTENPICKUPSOUND)
1097 	{
1098 		atten = ATTN_NONE;
1099 	}
1100 #if 0
1101 	else if ((ItemFlags & IF_FANCYPICKUPSOUND) &&
1102 		(toucher == NULL || toucher->CheckLocalView(consoeplayer)))
1103 	{
1104 		atten = ATTN_NONE;
1105 	}
1106 #endif
1107 	else
1108 	{
1109 		atten = ATTN_NORM;
1110 	}
1111 
1112 	if (toucher != NULL && toucher->CheckLocalView(consoleplayer))
1113 	{
1114 		chan = CHAN_PICKUP|CHAN_NOPAUSE;
1115 	}
1116 	else
1117 	{
1118 		chan = CHAN_PICKUP;
1119 	}
1120 	S_Sound (toucher, chan, PickupSound, 1, atten);
1121 }
1122 
1123 //===========================================================================
1124 //
1125 // AInventory :: ShouldStay
1126 //
1127 // Returns true if the item should not disappear, even temporarily.
1128 //
1129 //===========================================================================
1130 
ShouldStay()1131 bool AInventory::ShouldStay ()
1132 {
1133 	return false;
1134 }
1135 
1136 //===========================================================================
1137 //
1138 // AInventory :: Destroy
1139 //
1140 //===========================================================================
1141 
Destroy()1142 void AInventory::Destroy ()
1143 {
1144 	if (Owner != NULL)
1145 	{
1146 		Owner->RemoveInventory (this);
1147 	}
1148 	Inventory = NULL;
1149 	Super::Destroy ();
1150 
1151 	// Although contrived it can theoretically happen that these variables still got a pointer to this item
1152 	if (SendItemUse == this) SendItemUse = NULL;
1153 	if (SendItemDrop == this) SendItemDrop = NULL;
1154 }
1155 
1156 //===========================================================================
1157 //
1158 // AInventory :: DepleteOrDestroy
1159 //
1160 // If the item is depleted, just change its amount to 0, otherwise it's destroyed.
1161 //
1162 //===========================================================================
1163 
DepleteOrDestroy()1164 void AInventory::DepleteOrDestroy ()
1165 {
1166 	// If it's not ammo or an internal armor, destroy it.
1167 	// Ammo needs to stick around, even when it's zero for the benefit
1168 	// of the weapons that use it and to maintain the maximum ammo
1169 	// amounts a backpack might have given.
1170 	// Armor shouldn't be removed because they only work properly when
1171 	// they are the last items in the inventory.
1172 	if (ItemFlags & IF_KEEPDEPLETED)
1173 	{
1174 		Amount = 0;
1175 	}
1176 	else
1177 	{
1178 		Destroy();
1179 	}
1180 }
1181 
1182 //===========================================================================
1183 //
1184 // AInventory :: GetBlend
1185 //
1186 // Returns a color to blend to the player's view as long as they possess this
1187 // item.
1188 //
1189 //===========================================================================
1190 
GetBlend()1191 PalEntry AInventory::GetBlend ()
1192 {
1193 	return 0;
1194 }
1195 
1196 //===========================================================================
1197 //
1198 // AInventory :: PrevItem
1199 //
1200 // Returns the previous item.
1201 //
1202 //===========================================================================
1203 
PrevItem()1204 AInventory *AInventory::PrevItem ()
1205 {
1206 	AInventory *item = Owner->Inventory;
1207 
1208 	while (item != NULL && item->Inventory != this)
1209 	{
1210 		item = item->Inventory;
1211 	}
1212 	return item;
1213 }
1214 
1215 //===========================================================================
1216 //
1217 // AInventory :: PrevInv
1218 //
1219 // Returns the previous item with IF_INVBAR set.
1220 //
1221 //===========================================================================
1222 
PrevInv()1223 AInventory *AInventory::PrevInv ()
1224 {
1225 	AInventory *lastgood = NULL;
1226 	AInventory *item = Owner->Inventory;
1227 
1228 	while (item != NULL && item != this)
1229 	{
1230 		if (item->ItemFlags & IF_INVBAR)
1231 		{
1232 			lastgood = item;
1233 		}
1234 		item = item->Inventory;
1235 	}
1236 	return lastgood;
1237 }
1238 //===========================================================================
1239 //
1240 // AInventory :: NextInv
1241 //
1242 // Returns the next item with IF_INVBAR set.
1243 //
1244 //===========================================================================
1245 
NextInv()1246 AInventory *AInventory::NextInv ()
1247 {
1248 	AInventory *item = Inventory;
1249 
1250 	while (item != NULL && !(item->ItemFlags & IF_INVBAR))
1251 	{
1252 		item = item->Inventory;
1253 	}
1254 	return item;
1255 }
1256 
1257 //===========================================================================
1258 //
1259 // AInventory :: DrawPowerup
1260 //
1261 // Gives this item a chance to draw a special status indicator on the screen.
1262 // Returns false if it didn't draw anything.
1263 //
1264 //===========================================================================
1265 
DrawPowerup(int x,int y)1266 bool AInventory::DrawPowerup (int x, int y)
1267 {
1268 	return false;
1269 }
1270 
1271 /***************************************************************************/
1272 /* AArtifact implementation												   */
1273 /***************************************************************************/
1274 
IMPLEMENT_CLASS(APowerupGiver)1275 IMPLEMENT_CLASS (APowerupGiver)
1276 
1277 //===========================================================================
1278 //
1279 // AInventory :: DoRespawn
1280 //
1281 //===========================================================================
1282 
1283 bool AInventory::DoRespawn ()
1284 {
1285 	if (SpawnPointClass != NULL)
1286 	{
1287 		AActor *spot = NULL;
1288 		DSpotState *state = DSpotState::GetSpotState();
1289 
1290 		if (state != NULL) spot = state->GetRandomSpot(SpawnPointClass);
1291 		if (spot != NULL)
1292 		{
1293 			SetOrigin (spot->Pos(), false);
1294 			SetZ(floorz);
1295 		}
1296 	}
1297 	return true;
1298 }
1299 
1300 
1301 //===========================================================================
1302 //
1303 // AInventory :: GiveQuest
1304 //
1305 //===========================================================================
1306 
GiveQuest(AActor * toucher)1307 void AInventory::GiveQuest (AActor *toucher)
1308 {
1309 	int quest = GetClass()->Meta.GetMetaInt(AIMETA_GiveQuest);
1310 	if (quest>0 && quest<31)
1311 	{
1312 		toucher->GiveInventoryType (QuestItemClasses[quest-1]);
1313 	}
1314 }
1315 
1316 //===========================================================================
1317 //
1318 // AInventory :: TryPickup
1319 //
1320 //===========================================================================
1321 
TryPickup(AActor * & toucher)1322 bool AInventory::TryPickup (AActor *&toucher)
1323 {
1324 	AActor *newtoucher = toucher; // in case changed by the powerup
1325 
1326 	// If HandlePickup() returns true, it will set the IF_PICKUPGOOD flag
1327 	// to indicate that this item has been picked up. If the item cannot be
1328 	// picked up, then it leaves the flag cleared.
1329 
1330 	ItemFlags &= ~IF_PICKUPGOOD;
1331 	if (toucher->Inventory != NULL && toucher->Inventory->HandlePickup (this))
1332 	{
1333 		// Let something else the player is holding intercept the pickup.
1334 		if (!(ItemFlags & IF_PICKUPGOOD))
1335 		{
1336 			return false;
1337 		}
1338 		ItemFlags &= ~IF_PICKUPGOOD;
1339 		GoAwayAndDie ();
1340 	}
1341 	else if (MaxAmount == 0 && !IsKindOf(RUNTIME_CLASS(AAmmo)))
1342 	{
1343 		// Special case: If an item's MaxAmount is 0, you can still pick it
1344 		// up if it is autoactivate-able.
1345 		if (!(ItemFlags & IF_AUTOACTIVATE))
1346 		{
1347 			return false;
1348 		}
1349 		// The item is placed in the inventory just long enough to be used.
1350 		toucher->AddInventory (this);
1351 		bool usegood = Use (true);
1352 		toucher->RemoveInventory (this);
1353 
1354 		if (usegood)
1355 		{
1356 			GoAwayAndDie ();
1357 		}
1358 		else
1359 		{
1360 			return false;
1361 		}
1362 	}
1363 	else
1364 	{
1365 		// Add the item to the inventory. It is not already there, or HandlePickup
1366 		// would have already taken care of it.
1367 		AInventory *copy = CreateCopy (toucher);
1368 		if (copy == NULL)
1369 		{
1370 			return false;
1371 		}
1372 		// Some powerups cannot activate absolutely, for
1373 		// example, PowerMorph; fail the pickup if so.
1374 		if (copy->ItemFlags & IF_INITEFFECTFAILED)
1375 		{
1376 			if (copy != this) copy->Destroy();
1377 			else ItemFlags &= ~IF_INITEFFECTFAILED;
1378 			return false;
1379 		}
1380 		// Handle owner-changing powerups
1381 		if (copy->ItemFlags & IF_CREATECOPYMOVED)
1382 		{
1383 			newtoucher = copy->Owner;
1384 			copy->Owner = NULL;
1385 			copy->ItemFlags &= ~IF_CREATECOPYMOVED;
1386 		}
1387 		// Continue onwards with the rest
1388 		copy->AttachToOwner (newtoucher);
1389 		if (ItemFlags & IF_AUTOACTIVATE)
1390 		{
1391 			if (copy->Use (true))
1392 			{
1393 				if (--copy->Amount <= 0)
1394 				{
1395 					copy->flags &= ~MF_SPECIAL;
1396 					copy->SetState (copy->FindState("HoldAndDestroy"));
1397 				}
1398 			}
1399 		}
1400 	}
1401 	return true;
1402 }
1403 
1404 //===========================================================================
1405 //
1406 // AInventory :: TryPickupRestricted
1407 //
1408 //===========================================================================
1409 
TryPickupRestricted(AActor * & toucher)1410 bool AInventory::TryPickupRestricted (AActor *&toucher)
1411 {
1412 	return false;
1413 }
1414 
1415 //===========================================================================
1416 //
1417 // AInventory :: CallTryPickup
1418 //
1419 //===========================================================================
1420 
CallTryPickup(AActor * toucher,AActor ** toucher_return)1421 bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
1422 {
1423 	TObjPtr<AInventory> Invstack = Inventory; // A pointer of the inventories item stack.
1424 
1425 	// unmorphed versions of a currently morphed actor cannot pick up anything.
1426 	if (toucher->flags & MF_UNMORPHED) return false;
1427 
1428 	bool res;
1429 	if (CanPickup(toucher))
1430 		res = TryPickup(toucher);
1431 	else if (!(ItemFlags & IF_RESTRICTABSOLUTELY))
1432 		res = TryPickupRestricted(toucher);	// let an item decide for itself how it will handle this
1433 	else
1434 		return false;
1435 
1436 	// Morph items can change the toucher so we need an option to return this info.
1437 	if (toucher_return != NULL) *toucher_return = toucher;
1438 
1439 	if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !ShouldStay())
1440 	{
1441 		res = true;
1442 		GoAwayAndDie();
1443 	}
1444 
1445 	if (res)
1446 	{
1447 		GiveQuest(toucher);
1448 
1449 		// Transfer all inventory accross that the old object had, if requested.
1450 		if ((ItemFlags & IF_TRANSFER))
1451 		{
1452 			while (Invstack)
1453 			{
1454 				AInventory* titem = Invstack;
1455 				Invstack = titem->Inventory;
1456 				if (titem->Owner == this)
1457 				{
1458 					if (!titem->CallTryPickup(toucher)) // The object no longer can exist
1459 					{
1460 						titem->Destroy();
1461 					}
1462 				}
1463 			}
1464 		}
1465 	}
1466 	return res;
1467 }
1468 
1469 
1470 //===========================================================================
1471 //
1472 // AInventory :: CanPickup
1473 //
1474 //===========================================================================
1475 
CanPickup(AActor * toucher)1476 bool AInventory::CanPickup (AActor *toucher)
1477 {
1478 	if (!toucher)
1479 		return false;
1480 
1481 	FActorInfo *ai = GetClass()->ActorInfo;
1482 	// Is the item restricted to certain player classes?
1483 	if (ai->RestrictedToPlayerClass.Size() != 0)
1484 	{
1485 		for (unsigned i = 0; i < ai->RestrictedToPlayerClass.Size(); ++i)
1486 		{
1487 			if (toucher->IsKindOf(ai->RestrictedToPlayerClass[i]))
1488 				return true;
1489 		}
1490 		return false;
1491 	}
1492 	// Or is it forbidden to certain other classes?
1493 	else
1494 	{
1495 		for (unsigned i = 0; i < ai->ForbiddenToPlayerClass.Size(); ++i)
1496 		{
1497 			if (toucher->IsKindOf(ai->ForbiddenToPlayerClass[i]))
1498 				return false;
1499 		}
1500 	}
1501 	return true;
1502 }
1503 
1504 //===========================================================================
1505 //
1506 // CCMD printinv
1507 //
1508 // Prints the console player's current inventory.
1509 //
1510 //===========================================================================
1511 
CCMD(printinv)1512 CCMD (printinv)
1513 {
1514 	AInventory *item;
1515 	int pnum = consoleplayer;
1516 
1517 #ifdef _DEBUG
1518 	// Only allow peeking on other players' inventory in debug builds.
1519 	if (argv.argc() > 1)
1520 	{
1521 		pnum = atoi (argv[1]);
1522 		if (pnum < 0 || pnum >= MAXPLAYERS)
1523 		{
1524 			return;
1525 		}
1526 	}
1527 #endif
1528 	if (players[pnum].mo == NULL)
1529 	{
1530 		return;
1531 	}
1532 	for (item = players[pnum].mo->Inventory; item != NULL; item = item->Inventory)
1533 	{
1534 		Printf ("%s #%u (%d/%d)\n", item->GetClass()->TypeName.GetChars(),
1535 			item->InventoryID,
1536 			item->Amount, item->MaxAmount);
1537 	}
1538 }
1539 
1540 //===========================================================================
1541 //
1542 // AInventory :: AttachToOwner
1543 //
1544 //===========================================================================
1545 
AttachToOwner(AActor * other)1546 void AInventory::AttachToOwner (AActor *other)
1547 {
1548 	BecomeItem ();
1549 	other->AddInventory (this);
1550 }
1551 
1552 //===========================================================================
1553 //
1554 // AInventory :: DetachFromOwner
1555 //
1556 // Performs any special work needed when the item leaves an inventory,
1557 // either through destruction or becoming a pickup.
1558 //
1559 //===========================================================================
1560 
DetachFromOwner()1561 void AInventory::DetachFromOwner ()
1562 {
1563 }
1564 
IMPLEMENT_CLASS(ACustomInventory)1565 IMPLEMENT_CLASS (ACustomInventory)
1566 
1567 //===========================================================================
1568 //
1569 // ACustomInventory :: SpecialDropAction
1570 //
1571 //===========================================================================
1572 
1573 bool ACustomInventory::SpecialDropAction (AActor *dropper)
1574 {
1575 	return CallStateChain (dropper, FindState(NAME_Drop));
1576 }
1577 
1578 //===========================================================================
1579 //
1580 // ACustomInventory :: Use
1581 //
1582 //===========================================================================
1583 
Use(bool pickup)1584 bool ACustomInventory::Use (bool pickup)
1585 {
1586 	return CallStateChain (Owner, FindState(NAME_Use));
1587 }
1588 
1589 //===========================================================================
1590 //
1591 // ACustomInventory :: TryPickup
1592 //
1593 //===========================================================================
1594 
TryPickup(AActor * & toucher)1595 bool ACustomInventory::TryPickup (AActor *&toucher)
1596 {
1597 	FState *pickupstate = FindState(NAME_Pickup);
1598 	bool useok = CallStateChain (toucher, pickupstate);
1599 	if ((useok || pickupstate == NULL) && FindState(NAME_Use) != NULL)
1600 	{
1601 		useok = Super::TryPickup (toucher);
1602 	}
1603 	else if (useok)
1604 	{
1605 		GoAwayAndDie();
1606 	}
1607 	return useok;
1608 }
1609 
IMPLEMENT_CLASS(AHealth) const1610 IMPLEMENT_CLASS (AHealth)
1611 
1612 //===========================================================================
1613 //
1614 // AHealth :: PickupMessage
1615 //
1616 //===========================================================================
1617 const char *AHealth::PickupMessage ()
1618 {
1619 	int threshold = GetClass()->Meta.GetMetaInt(AIMETA_LowHealth, 0);
1620 
1621 	if (PrevHealth < threshold)
1622 	{
1623 		const char *message = GetClass()->Meta.GetMetaString (AIMETA_LowHealthMessage);
1624 
1625 		if (message != NULL)
1626 		{
1627 			return message;
1628 		}
1629 	}
1630 	return Super::PickupMessage();
1631 }
1632 
1633 //===========================================================================
1634 //
1635 // AHealth :: TryPickup
1636 //
1637 //===========================================================================
1638 
TryPickup(AActor * & other)1639 bool AHealth::TryPickup (AActor *&other)
1640 {
1641 	PrevHealth = other->player != NULL ? other->player->health : other->health;
1642 
1643 	// P_GiveBody adds one new feature, applied only if it is possible to pick up negative health:
1644 	// Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount.
1645 	if (P_GiveBody(other, Amount, MaxAmount))
1646 	{
1647 		GoAwayAndDie();
1648 		return true;
1649 	}
1650 	return false;
1651 }
1652 
IMPLEMENT_CLASS(AHealthPickup)1653 IMPLEMENT_CLASS (AHealthPickup)
1654 
1655 //===========================================================================
1656 //
1657 // AHealthPickup :: CreateCopy
1658 //
1659 //===========================================================================
1660 
1661 AInventory *AHealthPickup::CreateCopy (AActor *other)
1662 {
1663 	AInventory *copy = Super::CreateCopy (other);
1664 	copy->health = health;
1665 	return copy;
1666 }
1667 
1668 //===========================================================================
1669 //
1670 // AHealthPickup :: CreateTossable
1671 //
1672 //===========================================================================
1673 
CreateTossable()1674 AInventory *AHealthPickup::CreateTossable ()
1675 {
1676 	AInventory *copy = Super::CreateTossable ();
1677 	if (copy != NULL)
1678 	{
1679 		copy->health = health;
1680 	}
1681 	return copy;
1682 }
1683 
1684 //===========================================================================
1685 //
1686 // AHealthPickup :: HandlePickup
1687 //
1688 //===========================================================================
1689 
HandlePickup(AInventory * item)1690 bool AHealthPickup::HandlePickup (AInventory *item)
1691 {
1692 	// HealthPickups that are the same type but have different health amounts
1693 	// do not count as the same item.
1694 	if (item->health == health)
1695 	{
1696 		return Super::HandlePickup (item);
1697 	}
1698 	if (Inventory != NULL)
1699 	{
1700 		return Inventory->HandlePickup (item);
1701 	}
1702 	return false;
1703 }
1704 
1705 //===========================================================================
1706 //
1707 // AHealthPickup :: Use
1708 //
1709 //===========================================================================
1710 
Use(bool pickup)1711 bool AHealthPickup::Use (bool pickup)
1712 {
1713 	return P_GiveBody (Owner, health);
1714 }
1715 
1716 //===========================================================================
1717 //
1718 // AHealthPickup :: Serialize
1719 //
1720 //===========================================================================
1721 
Serialize(FArchive & arc)1722 void AHealthPickup::Serialize (FArchive &arc)
1723 {
1724 	Super::Serialize(arc);
1725 	arc << autousemode;
1726 }
1727 
1728 // Backpack -----------------------------------------------------------------
1729 
1730 //===========================================================================
1731 //
1732 // ABackpackItem :: Serialize
1733 //
1734 //===========================================================================
1735 
Serialize(FArchive & arc)1736 void ABackpackItem::Serialize (FArchive &arc)
1737 {
1738 	Super::Serialize (arc);
1739 	arc << bDepleted;
1740 }
1741 
1742 //===========================================================================
1743 //
1744 // ABackpackItem :: CreateCopy
1745 //
1746 // A backpack is being added to a player who doesn't yet have one. Give them
1747 // every kind of ammo, and increase their max amounts.
1748 //
1749 //===========================================================================
1750 
CreateCopy(AActor * other)1751 AInventory *ABackpackItem::CreateCopy (AActor *other)
1752 {
1753 	// Find every unique type of ammo. Give it to the player if
1754 	// he doesn't have it already, and double its maximum capacity.
1755 	for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
1756 	{
1757 		const PClass *type = PClass::m_Types[i];
1758 
1759 		if (type->ParentClass == RUNTIME_CLASS(AAmmo))
1760 		{
1761 			AAmmo *ammo = static_cast<AAmmo *>(other->FindInventory (type));
1762 			int amount = static_cast<AAmmo *>(GetDefaultByType(type))->BackpackAmount;
1763 			// extra ammo in baby mode and nightmare mode
1764 			if (!(ItemFlags&IF_IGNORESKILL))
1765 			{
1766 				amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor));
1767 			}
1768 			if (amount < 0) amount = 0;
1769 			if (ammo == NULL)
1770 			{ // The player did not have the ammo. Add it.
1771 				ammo = static_cast<AAmmo *>(Spawn (type, 0, 0, 0, NO_REPLACE));
1772 				ammo->Amount = bDepleted ? 0 : amount;
1773 				if (ammo->BackpackMaxAmount > ammo->MaxAmount)
1774 				{
1775 					ammo->MaxAmount = ammo->BackpackMaxAmount;
1776 				}
1777 				if (ammo->Amount > ammo->MaxAmount)
1778 				{
1779 					ammo->Amount = ammo->MaxAmount;
1780 				}
1781 				ammo->AttachToOwner (other);
1782 			}
1783 			else
1784 			{ // The player had the ammo. Give some more.
1785 				if (ammo->MaxAmount < ammo->BackpackMaxAmount)
1786 				{
1787 					ammo->MaxAmount = ammo->BackpackMaxAmount;
1788 				}
1789 				if (!bDepleted && ammo->Amount < ammo->MaxAmount)
1790 				{
1791 					ammo->Amount += amount;
1792 					if (ammo->Amount > ammo->MaxAmount)
1793 					{
1794 						ammo->Amount = ammo->MaxAmount;
1795 					}
1796 				}
1797 			}
1798 		}
1799 	}
1800 	return Super::CreateCopy (other);
1801 }
1802 
1803 //===========================================================================
1804 //
1805 // ABackpackItem :: HandlePickup
1806 //
1807 // When the player picks up another backpack, just give them more ammo.
1808 //
1809 //===========================================================================
1810 
HandlePickup(AInventory * item)1811 bool ABackpackItem::HandlePickup (AInventory *item)
1812 {
1813 	// Since you already have a backpack, that means you already have every
1814 	// kind of ammo in your inventory, so we don't need to look at the
1815 	// entire PClass list to discover what kinds of ammo exist, and we don't
1816 	// have to alter the MaxAmount either.
1817 	if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem)))
1818 	{
1819 		for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory)
1820 		{
1821 			if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo))
1822 			{
1823 				if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup)
1824 				{
1825 					int amount = static_cast<AAmmo*>(probe->GetDefault())->BackpackAmount;
1826 					// extra ammo in baby mode and nightmare mode
1827 					if (!(item->ItemFlags&IF_IGNORESKILL))
1828 					{
1829 						amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor));
1830 					}
1831 					probe->Amount += amount;
1832 					if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup)
1833 					{
1834 						probe->Amount = probe->MaxAmount;
1835 					}
1836 				}
1837 			}
1838 		}
1839 		// The pickup always succeeds, even if you didn't get anything
1840 		item->ItemFlags |= IF_PICKUPGOOD;
1841 		return true;
1842 	}
1843 	else if (Inventory != NULL)
1844 	{
1845 		return Inventory->HandlePickup (item);
1846 	}
1847 	else
1848 	{
1849 		return false;
1850 	}
1851 }
1852 
1853 //===========================================================================
1854 //
1855 // ABackpackItem :: CreateTossable
1856 //
1857 // The tossed backpack must not give out any more ammo, otherwise a player
1858 // could cheat by dropping their backpack and picking it up for more ammo.
1859 //
1860 //===========================================================================
1861 
CreateTossable()1862 AInventory *ABackpackItem::CreateTossable ()
1863 {
1864 	ABackpackItem *pack = static_cast<ABackpackItem *>(Super::CreateTossable());
1865 	if (pack != NULL)
1866 	{
1867 		pack->bDepleted = true;
1868 	}
1869 	return pack;
1870 }
1871 
1872 //===========================================================================
1873 //
1874 // ABackpackItem :: DetachFromOwner
1875 //
1876 //===========================================================================
1877 
DetachFromOwner()1878 void ABackpackItem::DetachFromOwner ()
1879 {
1880 	// When removing a backpack, drop the player's ammo maximums to normal
1881 	AInventory *item;
1882 
1883 	for (item = Owner->Inventory; item != NULL; item = item->Inventory)
1884 	{
1885 		if (item->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo) &&
1886 			item->MaxAmount == static_cast<AAmmo*>(item)->BackpackMaxAmount)
1887 		{
1888 			item->MaxAmount = static_cast<AInventory*>(item->GetDefault())->MaxAmount;
1889 			if (item->Amount > item->MaxAmount)
1890 			{
1891 				item->Amount = item->MaxAmount;
1892 			}
1893 		}
1894 	}
1895 }
1896 
1897 //===========================================================================
1898 //
1899 // ABackpack
1900 //
1901 //===========================================================================
1902 
1903 IMPLEMENT_CLASS(ABackpackItem)
1904 
IMPLEMENT_CLASS(AMapRevealer)1905 IMPLEMENT_CLASS (AMapRevealer)
1906 
1907 //===========================================================================
1908 //
1909 // AMapRevealer :: TryPickup
1910 //
1911 // The MapRevealer doesn't actually go in your inventory. Instead, it sets
1912 // a flag on the level.
1913 //
1914 //===========================================================================
1915 
1916 bool AMapRevealer::TryPickup (AActor *&toucher)
1917 {
1918 	level.flags2 |= LEVEL2_ALLMAP;
1919 	GoAwayAndDie ();
1920 	return true;
1921 }
1922 
1923 
1924 //===========================================================================
1925 //
1926 // AScoreItem
1927 //
1928 //===========================================================================
1929 
IMPLEMENT_CLASS(AScoreItem)1930 IMPLEMENT_CLASS(AScoreItem)
1931 
1932 //===========================================================================
1933 //
1934 // AScoreItem :: TryPickup
1935 //
1936 // Adds the value (Amount) of the item to the toucher's Score property.
1937 //
1938 //===========================================================================
1939 
1940 bool AScoreItem::TryPickup (AActor *&toucher)
1941 {
1942 	toucher->Score += Amount;
1943 	GoAwayAndDie();
1944 	return true;
1945 }
1946 
1947