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