1 #include "info.h"
2 #include "a_pickups.h"
3 #include "d_player.h"
4 #include "p_local.h"
5 #include "c_dispatch.h"
6 #include "gi.h"
7 #include "s_sound.h"
8 #include "p_local.h"
9 #include "p_spec.h"
10 #include "p_lnspec.h"
11 #include "p_effect.h"
12 #include "a_artifacts.h"
13 #include "sbar.h"
14 #include "d_player.h"
15 #include "m_random.h"
16 #include "v_video.h"
17 #include "templates.h"
18 #include "a_morph.h"
19 #include "g_level.h"
20 #include "doomstat.h"
21 #include "v_palette.h"
22 #include "farchive.h"
23 #include "r_data/colormaps.h"
24
25 static FRandom pr_torch ("Torch");
26
27 /* Those are no longer needed, except maybe as reference?
28 * They're not used anywhere in the code anymore, except
29 * MAULATORTICS as redefined in a_minotaur.cpp...
30 #define INVULNTICS (30*TICRATE)
31 #define INVISTICS (60*TICRATE)
32 #define INFRATICS (120*TICRATE)
33 #define IRONTICS (60*TICRATE)
34 #define WPNLEV2TICS (40*TICRATE)
35 #define FLIGHTTICS (60*TICRATE)
36 #define SPEEDTICS (45*TICRATE)
37 #define MAULATORTICS (25*TICRATE)
38 #define TIMEFREEZE_TICS ( 12 * TICRATE )
39 */
40
IMPLEMENT_CLASS(APowerup)41 IMPLEMENT_CLASS (APowerup)
42
43 // Powerup-Giver -------------------------------------------------------------
44
45 //===========================================================================
46 //
47 // APowerupGiver :: Use
48 //
49 //===========================================================================
50
51 bool APowerupGiver::Use (bool pickup)
52 {
53 if (PowerupType == NULL) return true; // item is useless
54
55 APowerup *power = static_cast<APowerup *> (Spawn (PowerupType, 0, 0, 0, NO_REPLACE));
56
57 if (EffectTics != 0)
58 {
59 power->EffectTics = EffectTics;
60 }
61 if (BlendColor != 0)
62 {
63 if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor;
64 else power->BlendColor = 0;
65 }
66 if (Mode != NAME_None)
67 {
68 power->Mode = Mode;
69 }
70 if (Strength != 0)
71 {
72 power->Strength = Strength;
73 }
74
75 power->ItemFlags |= ItemFlags & (IF_ALWAYSPICKUP|IF_ADDITIVETIME|IF_NOTELEPORTFREEZE);
76 if (power->CallTryPickup (Owner))
77 {
78 return true;
79 }
80 power->GoAwayAndDie ();
81 return false;
82 }
83
84 //===========================================================================
85 //
86 // APowerupGiver :: Serialize
87 //
88 //===========================================================================
89
Serialize(FArchive & arc)90 void APowerupGiver::Serialize (FArchive &arc)
91 {
92 Super::Serialize (arc);
93 arc << PowerupType;
94 arc << EffectTics << BlendColor << Mode;
95 arc << Strength;
96 }
97
98 // Powerup -------------------------------------------------------------------
99
100 //===========================================================================
101 //
102 // APowerup :: Tick
103 //
104 //===========================================================================
105
Tick()106 void APowerup::Tick ()
107 {
108 // Powerups cannot exist outside an inventory
109 if (Owner == NULL)
110 {
111 Destroy ();
112 }
113 if (EffectTics > 0 && --EffectTics == 0)
114 {
115 Destroy ();
116 }
117 }
118
119 //===========================================================================
120 //
121 // APowerup :: Serialize
122 //
123 //===========================================================================
124
Serialize(FArchive & arc)125 void APowerup::Serialize (FArchive &arc)
126 {
127 Super::Serialize (arc);
128 arc << EffectTics << BlendColor << Mode;
129 arc << Strength;
130 }
131
132 //===========================================================================
133 //
134 // APowerup :: GetBlend
135 //
136 //===========================================================================
137
GetBlend()138 PalEntry APowerup::GetBlend ()
139 {
140 if (EffectTics <= BLINKTHRESHOLD && !(EffectTics & 8))
141 return 0;
142
143 if (IsSpecialColormap(BlendColor)) return 0;
144 return BlendColor;
145 }
146
147 //===========================================================================
148 //
149 // APowerup :: InitEffect
150 //
151 //===========================================================================
152
InitEffect()153 void APowerup::InitEffect ()
154 {
155 }
156
157 //===========================================================================
158 //
159 // APowerup :: DoEffect
160 //
161 //===========================================================================
162
DoEffect()163 void APowerup::DoEffect ()
164 {
165 if (Owner == NULL || Owner->player == NULL)
166 {
167 return;
168 }
169
170 if (EffectTics > 0)
171 {
172 int Colormap = GetSpecialColormap(BlendColor);
173
174 if (Colormap != NOFIXEDCOLORMAP)
175 {
176 if (EffectTics > BLINKTHRESHOLD || (EffectTics & 8))
177 {
178 Owner->player->fixedcolormap = Colormap;
179 }
180 else if (Owner->player->fixedcolormap == Colormap)
181 {
182 // only unset if the fixed colormap comes from this item
183 Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
184 }
185 }
186 }
187 }
188
189 //===========================================================================
190 //
191 // APowerup :: EndEffect
192 //
193 //===========================================================================
194
EndEffect()195 void APowerup::EndEffect ()
196 {
197 int colormap = GetSpecialColormap(BlendColor);
198
199 if (colormap != NOFIXEDCOLORMAP && Owner && Owner->player && Owner->player->fixedcolormap == colormap)
200 { // only unset if the fixed colormap comes from this item
201 Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
202 }
203 }
204
205 //===========================================================================
206 //
207 // APowerup :: Destroy
208 //
209 //===========================================================================
210
Destroy()211 void APowerup::Destroy ()
212 {
213 EndEffect ();
214 Super::Destroy ();
215 }
216
217 //===========================================================================
218 //
219 // APowerup :: DrawPowerup
220 //
221 //===========================================================================
222
DrawPowerup(int x,int y)223 bool APowerup::DrawPowerup (int x, int y)
224 {
225 if (!Icon.isValid())
226 {
227 return false;
228 }
229 if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16))
230 {
231 FTexture *pic = TexMan(Icon);
232 screen->DrawTexture (pic, x, y,
233 DTA_HUDRules, HUD_Normal,
234 // DTA_TopOffset, pic->GetHeight()/2,
235 // DTA_LeftOffset, pic->GetWidth()/2,
236 TAG_DONE);
237 }
238 return true;
239 }
240
241 //===========================================================================
242 //
243 // APowerup :: HandlePickup
244 //
245 //===========================================================================
246
HandlePickup(AInventory * item)247 bool APowerup::HandlePickup (AInventory *item)
248 {
249 if (item->GetClass() == GetClass())
250 {
251 APowerup *power = static_cast<APowerup*>(item);
252 if (power->EffectTics == 0)
253 {
254 power->ItemFlags |= IF_PICKUPGOOD;
255 return true;
256 }
257 // Color gets transferred if the new item has an effect.
258
259 // Increase the effect's duration.
260 if (power->ItemFlags & IF_ADDITIVETIME)
261 {
262 EffectTics += power->EffectTics;
263 BlendColor = power->BlendColor;
264 }
265 // If it's not blinking yet, you can't replenish the power unless the
266 // powerup is required to be picked up.
267 else if (EffectTics > BLINKTHRESHOLD && !(power->ItemFlags & IF_ALWAYSPICKUP))
268 {
269 return true;
270 }
271 // Reset the effect duration.
272 else if (power->EffectTics > EffectTics)
273 {
274 EffectTics = power->EffectTics;
275 BlendColor = power->BlendColor;
276 }
277 power->ItemFlags |= IF_PICKUPGOOD;
278 return true;
279 }
280 if (Inventory != NULL)
281 {
282 return Inventory->HandlePickup (item);
283 }
284 return false;
285 }
286
287 //===========================================================================
288 //
289 // APowerup :: CreateCopy
290 //
291 //===========================================================================
292
CreateCopy(AActor * other)293 AInventory *APowerup::CreateCopy (AActor *other)
294 {
295 // Get the effective effect time.
296 EffectTics = abs (EffectTics);
297 // Abuse the Owner field to tell the
298 // InitEffect method who started it;
299 // this should be cleared afterwards,
300 // as this powerup instance is not
301 // properly attached to anything yet.
302 Owner = other;
303 // Actually activate the powerup.
304 InitEffect ();
305 // Clear the Owner field, unless it was
306 // changed by the activation, for example,
307 // if this instance is a morph powerup;
308 // the flag tells the caller that the
309 // ownership has changed so that they
310 // can properly handle the situation.
311 if (!(ItemFlags & IF_CREATECOPYMOVED))
312 {
313 Owner = NULL;
314 }
315 // All done.
316 return this;
317 }
318
319 //===========================================================================
320 //
321 // APowerup :: CreateTossable
322 //
323 // Powerups are never droppable, even without IF_UNDROPPABLE set.
324 //
325 //===========================================================================
326
CreateTossable()327 AInventory *APowerup::CreateTossable ()
328 {
329 return NULL;
330 }
331
332 //===========================================================================
333 //
334 // APowerup :: OwnerDied
335 //
336 // Powerups don't last beyond death.
337 //
338 //===========================================================================
339
OwnerDied()340 void APowerup::OwnerDied ()
341 {
342 Destroy ();
343 }
344
345 //===========================================================================
346 //
347 // AInventory :: GetNoTeleportFreeze
348 //
349 //===========================================================================
350
GetNoTeleportFreeze()351 bool APowerup::GetNoTeleportFreeze ()
352 {
353 if (ItemFlags & IF_NOTELEPORTFREEZE) return true;
354 return Super::GetNoTeleportFreeze();
355 }
356
357 // Invulnerability Powerup ---------------------------------------------------
358
IMPLEMENT_CLASS(APowerInvulnerable)359 IMPLEMENT_CLASS (APowerInvulnerable)
360
361 //===========================================================================
362 //
363 // APowerInvulnerable :: InitEffect
364 //
365 //===========================================================================
366
367 void APowerInvulnerable::InitEffect ()
368 {
369 Super::InitEffect();
370 Owner->effects &= ~FX_RESPAWNINVUL;
371 Owner->flags2 |= MF2_INVULNERABLE;
372 if (Mode == NAME_None)
373 {
374 Mode = (ENamedName)RUNTIME_TYPE(Owner)->Meta.GetMetaInt(APMETA_InvulMode);
375 }
376 if (Mode == NAME_Reflective)
377 {
378 Owner->flags2 |= MF2_REFLECTIVE;
379 }
380 }
381
382 //===========================================================================
383 //
384 // APowerInvulnerable :: DoEffect
385 //
386 //===========================================================================
387
DoEffect()388 void APowerInvulnerable::DoEffect ()
389 {
390 Super::DoEffect ();
391
392 if (Owner == NULL)
393 {
394 return;
395 }
396
397 if (Mode == NAME_Ghost)
398 {
399 if (!(Owner->flags & MF_SHADOW))
400 {
401 // Don't mess with the translucency settings if an
402 // invisibility powerup is active.
403 Owner->RenderStyle = STYLE_Translucent;
404 if (!(level.time & 7) && Owner->alpha > 0 && Owner->alpha < OPAQUE)
405 {
406 if (Owner->alpha == HX_SHADOW)
407 {
408 Owner->alpha = HX_ALTSHADOW;
409 }
410 else
411 {
412 Owner->alpha = 0;
413 Owner->flags2 |= MF2_NONSHOOTABLE;
414 }
415 }
416 if (!(level.time & 31))
417 {
418 if (Owner->alpha == 0)
419 {
420 Owner->flags2 &= ~MF2_NONSHOOTABLE;
421 Owner->alpha = HX_ALTSHADOW;
422 }
423 else
424 {
425 Owner->alpha = HX_SHADOW;
426 }
427 }
428 }
429 else
430 {
431 Owner->flags2 &= ~MF2_NONSHOOTABLE;
432 }
433 }
434 }
435
436 //===========================================================================
437 //
438 // APowerInvulnerable :: EndEffect
439 //
440 //===========================================================================
441
EndEffect()442 void APowerInvulnerable::EndEffect ()
443 {
444 Super::EndEffect();
445
446 if (Owner == NULL)
447 {
448 return;
449 }
450
451 Owner->flags2 &= ~MF2_INVULNERABLE;
452 Owner->effects &= ~FX_RESPAWNINVUL;
453 if (Mode == NAME_Ghost)
454 {
455 Owner->flags2 &= ~MF2_NONSHOOTABLE;
456 if (!(Owner->flags & MF_SHADOW))
457 {
458 // Don't mess with the translucency settings if an
459 // invisibility powerup is active.
460 Owner->RenderStyle = STYLE_Normal;
461 Owner->alpha = OPAQUE;
462 }
463 }
464 else if (Mode == NAME_Reflective)
465 {
466 Owner->flags2 &= ~MF2_REFLECTIVE;
467 }
468
469 if (Owner->player != NULL)
470 {
471 Owner->player->fixedcolormap = NOFIXEDCOLORMAP;
472 }
473 }
474
475 //===========================================================================
476 //
477 // APowerInvulnerable :: AlterWeaponSprite
478 //
479 //===========================================================================
480
AlterWeaponSprite(visstyle_t * vis)481 int APowerInvulnerable::AlterWeaponSprite (visstyle_t *vis)
482 {
483 int changed = Inventory == NULL ? false : Inventory->AlterWeaponSprite(vis);
484 if (Owner != NULL)
485 {
486 if (Mode == NAME_Ghost && !(Owner->flags & MF_SHADOW))
487 {
488 fixed_t wp_alpha = MIN<fixed_t>(FRACUNIT/4 + Owner->alpha*3/4, FRACUNIT);
489 if (wp_alpha != FIXED_MAX) vis->alpha = wp_alpha;
490 }
491 }
492 return changed;
493 }
494
495 // Strength (aka Berserk) Powerup --------------------------------------------
496
IMPLEMENT_CLASS(APowerStrength)497 IMPLEMENT_CLASS (APowerStrength)
498
499 //===========================================================================
500 //
501 // APowerStrength :: HandlePickup
502 //
503 //===========================================================================
504
505 bool APowerStrength::HandlePickup (AInventory *item)
506 {
507 if (item->GetClass() == GetClass())
508 { // Setting EffectTics to 0 will force Powerup's HandlePickup()
509 // method to reset the tic count so you get the red flash again.
510 EffectTics = 0;
511 }
512 return Super::HandlePickup (item);
513 }
514
515 //===========================================================================
516 //
517 // APowerStrength :: InitEffect
518 //
519 //===========================================================================
520
InitEffect()521 void APowerStrength::InitEffect ()
522 {
523 Super::InitEffect();
524 }
525
526 //===========================================================================
527 //
528 // APowerStrength :: DoEffect
529 //
530 //===========================================================================
531
Tick()532 void APowerStrength::Tick ()
533 {
534 // Strength counts up to diminish the fade.
535 assert(EffectTics < (INT_MAX - 1)); // I can't see a game lasting nearly two years, but...
536 EffectTics += 2;
537 Super::Tick();
538 }
539
540 //===========================================================================
541 //
542 // APowerStrength :: GetBlend
543 //
544 //===========================================================================
545
GetBlend()546 PalEntry APowerStrength::GetBlend ()
547 {
548 // slowly fade the berserk out
549 int cnt = 12 - (EffectTics >> 6);
550
551 if (cnt > 0)
552 {
553 cnt = (cnt + 7) >> 3;
554 return PalEntry (BlendColor.a*cnt*255/9,
555 BlendColor.r, BlendColor.g, BlendColor.b);
556 }
557 return 0;
558 }
559
560 // Invisibility Powerup ------------------------------------------------------
561
IMPLEMENT_CLASS(APowerInvisibility)562 IMPLEMENT_CLASS (APowerInvisibility)
563
564 // Invisibility flag combos
565 #define INVISIBILITY_FLAGS1 (MF_SHADOW)
566 #define INVISIBILITY_FLAGS3 (MF3_GHOST)
567 #define INVISIBILITY_FLAGS5 (MF5_CANTSEEK)
568
569 //===========================================================================
570 //
571 // APowerInvisibility :: InitEffect
572 //
573 //===========================================================================
574
575 void APowerInvisibility::InitEffect ()
576 {
577 Super::InitEffect();
578 // This used to call CommonInit(), which used to contain all the code that's repeated every
579 // tic, plus the following code that needs to happen once and only once.
580 // The CommonInit() code has been moved to DoEffect(), so this now ends with a call to DoEffect(),
581 // and DoEffect() no longer needs to call InitEffect(). CommonInit() has been removed for being redundant.
582 if (Owner != NULL)
583 {
584 flags &= ~(Owner->flags & INVISIBILITY_FLAGS1);
585 Owner->flags |= flags & INVISIBILITY_FLAGS1;
586 flags3 &= ~(Owner->flags3 & INVISIBILITY_FLAGS3);
587 Owner->flags3 |= flags3 & INVISIBILITY_FLAGS3;
588 flags5 &= ~(Owner->flags5 & INVISIBILITY_FLAGS5);
589 Owner->flags5 |= flags5 & INVISIBILITY_FLAGS5;
590
591 DoEffect();
592 }
593 }
594
595 //===========================================================================
596 //
597 // APowerInvisibility :: DoEffect
598 //
599 //===========================================================================
DoEffect()600 void APowerInvisibility::DoEffect ()
601 {
602 Super::DoEffect();
603 // Due to potential interference with other PowerInvisibility items
604 // the effect has to be refreshed each tic.
605 fixed_t ts = (Strength/100) * (special1 + 1); if (ts > FRACUNIT) ts = FRACUNIT;
606 Owner->alpha = clamp<fixed_t>((OPAQUE - ts), 0, OPAQUE);
607 switch (Mode)
608 {
609 case (NAME_Fuzzy):
610 Owner->RenderStyle = STYLE_OptFuzzy;
611 break;
612 case (NAME_Opaque):
613 Owner->RenderStyle = STYLE_Normal;
614 break;
615 case (NAME_Additive):
616 Owner->RenderStyle = STYLE_Add;
617 break;
618 case (NAME_Stencil):
619 Owner->RenderStyle = STYLE_Stencil;
620 break;
621 case (NAME_AddStencil) :
622 Owner->RenderStyle = STYLE_AddStencil;
623 break;
624 case (NAME_TranslucentStencil) :
625 Owner->RenderStyle = STYLE_TranslucentStencil;
626 break;
627 case (NAME_None) :
628 case (NAME_Cumulative):
629 case (NAME_Translucent):
630 Owner->RenderStyle = STYLE_Translucent;
631 break;
632 default: // Something's wrong
633 Owner->RenderStyle = STYLE_Normal;
634 Owner->alpha = OPAQUE;
635 break;
636 }
637 }
638
639 //===========================================================================
640 //
641 // APowerInvisibility :: EndEffect
642 //
643 //===========================================================================
644
EndEffect()645 void APowerInvisibility::EndEffect ()
646 {
647 Super::EndEffect();
648 if (Owner != NULL)
649 {
650 Owner->flags &= ~(flags & INVISIBILITY_FLAGS1);
651 Owner->flags3 &= ~(flags3 & INVISIBILITY_FLAGS3);
652 Owner->flags5 &= ~(flags5 & INVISIBILITY_FLAGS5);
653
654 Owner->RenderStyle = STYLE_Normal;
655 Owner->alpha = OPAQUE;
656
657 // Check whether there are other invisibility items and refresh their effect.
658 // If this isn't done there will be one incorrectly drawn frame when this
659 // item expires.
660 AInventory *item = Owner->Inventory;
661 while (item != NULL)
662 {
663 if (item->IsKindOf(RUNTIME_CLASS(APowerInvisibility)) && item != this)
664 {
665 static_cast<APowerInvisibility*>(item)->DoEffect();
666 }
667 item = item->Inventory;
668 }
669 }
670 }
671
672 //===========================================================================
673 //
674 // APowerInvisibility :: AlterWeaponSprite
675 //
676 //===========================================================================
677
AlterWeaponSprite(visstyle_t * vis)678 int APowerInvisibility::AlterWeaponSprite (visstyle_t *vis)
679 {
680 int changed = Inventory == NULL ? false : Inventory->AlterWeaponSprite(vis);
681 // Blink if the powerup is wearing off
682 if (changed == 0 && EffectTics < 4*32 && !(EffectTics & 8))
683 {
684 vis->RenderStyle = STYLE_Normal;
685 vis->alpha = OPAQUE;
686 return 1;
687 }
688 else if (changed == 1)
689 {
690 // something else set the weapon sprite back to opaque but this item is still active.
691 fixed_t ts = (Strength/100) * (special1 + 1); if (ts > FRACUNIT) ts = FRACUNIT;
692 vis->alpha = clamp<fixed_t>((OPAQUE - ts), 0, OPAQUE);
693 switch (Mode)
694 {
695 case (NAME_Fuzzy):
696 vis->RenderStyle = STYLE_OptFuzzy;
697 break;
698 case (NAME_Opaque):
699 vis->RenderStyle = STYLE_Normal;
700 break;
701 case (NAME_Additive):
702 vis->RenderStyle = STYLE_Add;
703 break;
704 case (NAME_Stencil):
705 vis->RenderStyle = STYLE_Stencil;
706 break;
707 case (NAME_TranslucentStencil) :
708 vis->RenderStyle = STYLE_TranslucentStencil;
709 break;
710 case (NAME_AddStencil) :
711 vis->RenderStyle = STYLE_AddStencil;
712 break;
713 case (NAME_None) :
714 case (NAME_Cumulative):
715 case (NAME_Translucent):
716 default:
717 vis->RenderStyle = STYLE_Translucent;
718 break;
719 }
720 }
721 // Handling of Strife-like cumulative invisibility powerups, the weapon itself shouldn't become invisible
722 if ((vis->alpha < TRANSLUC25 && special1 > 0) || (vis->alpha == 0))
723 {
724 vis->alpha = clamp<fixed_t>((OPAQUE - (Strength/100)), 0, OPAQUE);
725 vis->colormap = SpecialColormaps[INVERSECOLORMAP].Colormap;
726 }
727 return -1; // This item is valid so another one shouldn't reset the translucency
728 }
729
730 //===========================================================================
731 //
732 // APowerInvisibility :: HandlePickup
733 //
734 // If the player already has the first stage of a cumulative powerup, getting
735 // it again increases the player's alpha. (But shouldn't this be in Use()?)
736 //
737 //===========================================================================
738
HandlePickup(AInventory * item)739 bool APowerInvisibility::HandlePickup (AInventory *item)
740 {
741 if (Mode == NAME_Cumulative && ((Strength * special1) < FRACUNIT) && item->GetClass() == GetClass())
742 {
743 APowerup *power = static_cast<APowerup *>(item);
744 if (power->EffectTics == 0)
745 {
746 power->ItemFlags |= IF_PICKUPGOOD;
747 return true;
748 }
749 // Only increase the EffectTics, not decrease it.
750 // Color also gets transferred only when the new item has an effect.
751 if (power->EffectTics > EffectTics)
752 {
753 EffectTics = power->EffectTics;
754 BlendColor = power->BlendColor;
755 }
756 special1++; // increases power
757 power->ItemFlags |= IF_PICKUPGOOD;
758 return true;
759 }
760 return Super::HandlePickup (item);
761 }
762
763 // Ironfeet Powerup ----------------------------------------------------------
764
IMPLEMENT_CLASS(APowerIronFeet)765 IMPLEMENT_CLASS (APowerIronFeet)
766
767 //===========================================================================
768 //
769 // APowerIronFeet :: AbsorbDamage
770 //
771 //===========================================================================
772
773 void APowerIronFeet::AbsorbDamage (int damage, FName damageType, int &newdamage)
774 {
775 if (damageType == NAME_Drowning)
776 {
777 newdamage = 0;
778 }
779 else if (Inventory != NULL)
780 {
781 Inventory->AbsorbDamage (damage, damageType, newdamage);
782 }
783 }
784
785 //===========================================================================
786 //
787 // APowerIronFeet :: DoEffect
788 //
789 //===========================================================================
790
DoEffect()791 void APowerIronFeet::DoEffect ()
792 {
793 if (Owner->player != NULL)
794 {
795 Owner->player->mo->ResetAirSupply ();
796 }
797 }
798
799
800 // Strife Environment Suit Powerup -------------------------------------------
801
IMPLEMENT_CLASS(APowerMask)802 IMPLEMENT_CLASS (APowerMask)
803
804 //===========================================================================
805 //
806 // APowerMask :: AbsorbDamage
807 //
808 //===========================================================================
809
810 void APowerMask::AbsorbDamage (int damage, FName damageType, int &newdamage)
811 {
812 if (damageType == NAME_Fire)
813 {
814 newdamage = 0;
815 }
816 else
817 {
818 Super::AbsorbDamage (damage, damageType, newdamage);
819 }
820 }
821
822 //===========================================================================
823 //
824 // APowerMask :: DoEffect
825 //
826 //===========================================================================
827
DoEffect()828 void APowerMask::DoEffect ()
829 {
830 Super::DoEffect ();
831 if (!(level.time & 0x3f))
832 {
833 S_Sound (Owner, CHAN_AUTO, "misc/mask", 1, ATTN_STATIC);
834 }
835 }
836
837 // Light-Amp Powerup ---------------------------------------------------------
838
IMPLEMENT_CLASS(APowerLightAmp)839 IMPLEMENT_CLASS (APowerLightAmp)
840
841 //===========================================================================
842 //
843 // APowerLightAmp :: DoEffect
844 //
845 //===========================================================================
846
847 void APowerLightAmp::DoEffect ()
848 {
849 Super::DoEffect ();
850
851 if (Owner->player != NULL && Owner->player->fixedcolormap < NUMCOLORMAPS)
852 {
853 if (EffectTics > BLINKTHRESHOLD || (EffectTics & 8))
854 {
855 Owner->player->fixedlightlevel = 1;
856 }
857 else
858 {
859 Owner->player->fixedlightlevel = -1;
860 }
861 }
862 }
863
864 //===========================================================================
865 //
866 // APowerLightAmp :: EndEffect
867 //
868 //===========================================================================
869
EndEffect()870 void APowerLightAmp::EndEffect ()
871 {
872 Super::EndEffect();
873 if (Owner != NULL && Owner->player != NULL && Owner->player->fixedcolormap < NUMCOLORMAPS)
874 {
875 Owner->player->fixedlightlevel = -1;
876 }
877 }
878
879 // Torch Powerup -------------------------------------------------------------
880
IMPLEMENT_CLASS(APowerTorch)881 IMPLEMENT_CLASS (APowerTorch)
882
883 //===========================================================================
884 //
885 // APowerTorch :: Serialize
886 //
887 //===========================================================================
888
889 void APowerTorch::Serialize (FArchive &arc)
890 {
891 Super::Serialize (arc);
892 arc << NewTorch << NewTorchDelta;
893 }
894
895 //===========================================================================
896 //
897 // APowerTorch :: DoEffect
898 //
899 //===========================================================================
900
DoEffect()901 void APowerTorch::DoEffect ()
902 {
903 if (Owner == NULL || Owner->player == NULL)
904 {
905 return;
906 }
907
908 if (EffectTics <= BLINKTHRESHOLD || Owner->player->fixedcolormap >= NUMCOLORMAPS)
909 {
910 Super::DoEffect ();
911 }
912 else
913 {
914 APowerup::DoEffect ();
915
916 if (!(level.time & 16) && Owner->player != NULL)
917 {
918 if (NewTorch != 0)
919 {
920 if (Owner->player->fixedlightlevel + NewTorchDelta > 7
921 || Owner->player->fixedlightlevel + NewTorchDelta < 0
922 || NewTorch == Owner->player->fixedlightlevel)
923 {
924 NewTorch = 0;
925 }
926 else
927 {
928 Owner->player->fixedlightlevel += NewTorchDelta;
929 }
930 }
931 else
932 {
933 NewTorch = (pr_torch() & 7) + 1;
934 NewTorchDelta = (NewTorch == Owner->player->fixedlightlevel) ?
935 0 : ((NewTorch > Owner->player->fixedlightlevel) ? 1 : -1);
936 }
937 }
938 }
939 }
940
941 // Flight (aka Wings of Wrath) powerup ---------------------------------------
942
IMPLEMENT_CLASS(APowerFlight)943 IMPLEMENT_CLASS (APowerFlight)
944
945 //===========================================================================
946 //
947 // APowerFlight :: Serialize
948 //
949 //===========================================================================
950
951 void APowerFlight::Serialize (FArchive &arc)
952 {
953 Super::Serialize (arc);
954 arc << HitCenterFrame;
955 }
956
957 //===========================================================================
958 //
959 // APowerFlight :: InitEffect
960 //
961 //===========================================================================
962
InitEffect()963 void APowerFlight::InitEffect ()
964 {
965 Super::InitEffect();
966 Owner->flags2 |= MF2_FLY;
967 Owner->flags |= MF_NOGRAVITY;
968 if (Owner->Z() <= Owner->floorz)
969 {
970 Owner->velz = 4*FRACUNIT; // thrust the player in the air a bit
971 }
972 if (Owner->velz <= -35*FRACUNIT)
973 { // stop falling scream
974 S_StopSound (Owner, CHAN_VOICE);
975 }
976 }
977
978 //===========================================================================
979 //
980 // APowerFlight :: DoEffect
981 //
982 //===========================================================================
983
Tick()984 void APowerFlight::Tick ()
985 {
986 // The Wings of Wrath only expire in multiplayer and non-hub games
987 if (!multiplayer && (level.flags2 & LEVEL2_INFINITE_FLIGHT))
988 {
989 assert(EffectTics < INT_MAX); // I can't see a game lasting nearly two years, but...
990 EffectTics++;
991 }
992
993 Super::Tick ();
994
995 // Owner->flags |= MF_NOGRAVITY;
996 // Owner->flags2 |= MF2_FLY;
997 }
998
999 //===========================================================================
1000 //
1001 // APowerFlight :: EndEffect
1002 //
1003 //===========================================================================
1004
EndEffect()1005 void APowerFlight::EndEffect ()
1006 {
1007 Super::EndEffect();
1008 if (Owner == NULL || Owner->player == NULL)
1009 {
1010 return;
1011 }
1012
1013 if (!(Owner->flags7 & MF7_FLYCHEAT))
1014 {
1015 if (Owner->Z() != Owner->floorz)
1016 {
1017 Owner->player->centering = true;
1018 }
1019 Owner->flags2 &= ~MF2_FLY;
1020 Owner->flags &= ~MF_NOGRAVITY;
1021 }
1022 // BorderTopRefresh = screen->GetPageCount (); //make sure the sprite's cleared out
1023 }
1024
1025 //===========================================================================
1026 //
1027 // APowerFlight :: DrawPowerup
1028 //
1029 //===========================================================================
1030
DrawPowerup(int x,int y)1031 bool APowerFlight::DrawPowerup (int x, int y)
1032 {
1033 // If this item got a valid icon use that instead of the default spinning wings.
1034 if (Icon.isValid())
1035 {
1036 return Super::DrawPowerup(x, y);
1037 }
1038
1039 if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16))
1040 {
1041 FTextureID picnum = TexMan.CheckForTexture ("SPFLY0", FTexture::TEX_MiscPatch);
1042 int frame = (level.time/3) & 15;
1043
1044 if (!picnum.isValid())
1045 {
1046 return false;
1047 }
1048 if (Owner->flags & MF_NOGRAVITY)
1049 {
1050 if (HitCenterFrame && (frame != 15 && frame != 0))
1051 {
1052 screen->DrawTexture (TexMan[picnum+15], x, y,
1053 DTA_HUDRules, HUD_Normal, TAG_DONE);
1054 }
1055 else
1056 {
1057 screen->DrawTexture (TexMan[picnum+frame], x, y,
1058 DTA_HUDRules, HUD_Normal, TAG_DONE);
1059 HitCenterFrame = false;
1060 }
1061 }
1062 else
1063 {
1064 if (!HitCenterFrame && (frame != 15 && frame != 0))
1065 {
1066 screen->DrawTexture (TexMan[picnum+frame], x, y,
1067 DTA_HUDRules, HUD_Normal, TAG_DONE);
1068 HitCenterFrame = false;
1069 }
1070 else
1071 {
1072 screen->DrawTexture (TexMan[picnum+15], x, y,
1073 DTA_HUDRules, HUD_Normal, TAG_DONE);
1074 HitCenterFrame = true;
1075 }
1076 }
1077 }
1078 return true;
1079 }
1080
1081 // Weapon Level 2 (aka Tome of Power) Powerup --------------------------------
1082
IMPLEMENT_CLASS(APowerWeaponLevel2)1083 IMPLEMENT_CLASS (APowerWeaponLevel2)
1084
1085 //===========================================================================
1086 //
1087 // APowerWeaponLevel2 :: InitEffect
1088 //
1089 //===========================================================================
1090
1091 void APowerWeaponLevel2::InitEffect ()
1092 {
1093 AWeapon *weapon, *sister;
1094
1095 Super::InitEffect();
1096
1097 if (Owner->player == NULL)
1098 return;
1099
1100 weapon = Owner->player->ReadyWeapon;
1101
1102 if (weapon == NULL)
1103 return;
1104
1105 sister = weapon->SisterWeapon;
1106
1107 if (sister == NULL)
1108 return;
1109
1110 if (!(sister->WeaponFlags & WIF_POWERED_UP))
1111 return;
1112
1113 assert (sister->SisterWeapon == weapon);
1114
1115 Owner->player->ReadyWeapon = sister;
1116
1117 if (weapon->GetReadyState() != sister->GetReadyState())
1118 {
1119 P_SetPsprite (Owner->player, ps_weapon, sister->GetReadyState());
1120 }
1121 }
1122
1123 //===========================================================================
1124 //
1125 // APowerWeaponLevel2 :: EndEffect
1126 //
1127 //===========================================================================
1128
EndEffect()1129 void APowerWeaponLevel2::EndEffect ()
1130 {
1131 player_t *player = Owner != NULL ? Owner->player : NULL;
1132
1133 Super::EndEffect();
1134 if (player != NULL)
1135 {
1136 if (player->ReadyWeapon != NULL &&
1137 player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP)
1138 {
1139 player->ReadyWeapon->EndPowerup ();
1140 }
1141 if (player->PendingWeapon != NULL && player->PendingWeapon != WP_NOCHANGE &&
1142 player->PendingWeapon->WeaponFlags & WIF_POWERED_UP &&
1143 player->PendingWeapon->SisterWeapon != NULL)
1144 {
1145 player->PendingWeapon = player->PendingWeapon->SisterWeapon;
1146 }
1147 }
1148 }
1149
1150 // Player Speed Trail (used by the Speed Powerup) ----------------------------
1151
1152 class APlayerSpeedTrail : public AActor
1153 {
1154 DECLARE_CLASS (APlayerSpeedTrail, AActor)
1155 public:
1156 void Tick ();
1157 };
1158
IMPLEMENT_CLASS(APlayerSpeedTrail)1159 IMPLEMENT_CLASS (APlayerSpeedTrail)
1160
1161 //===========================================================================
1162 //
1163 // APlayerSpeedTrail :: Tick
1164 //
1165 //===========================================================================
1166
1167 void APlayerSpeedTrail::Tick ()
1168 {
1169 const int fade = OPAQUE*6/10/8;
1170 if (alpha <= fade)
1171 {
1172 Destroy ();
1173 }
1174 else
1175 {
1176 alpha -= fade;
1177 }
1178 }
1179
1180 // Speed Powerup -------------------------------------------------------------
1181
IMPLEMENT_CLASS(APowerSpeed)1182 IMPLEMENT_CLASS (APowerSpeed)
1183
1184 //===========================================================================
1185 //
1186 // APowerSpeed :: Serialize
1187 //
1188 //===========================================================================
1189
1190 void APowerSpeed::Serialize(FArchive &arc)
1191 {
1192 Super::Serialize (arc);
1193 if (SaveVersion < 4146)
1194 {
1195 SpeedFlags = 0;
1196 }
1197 else
1198 {
1199 arc << SpeedFlags;
1200 }
1201 }
1202
1203 //===========================================================================
1204 //
1205 // APowerSpeed :: GetSpeedFactor
1206 //
1207 //===========================================================================
1208
GetSpeedFactor()1209 fixed_t APowerSpeed ::GetSpeedFactor ()
1210 {
1211 if (Inventory != NULL)
1212 return FixedMul(Speed, Inventory->GetSpeedFactor());
1213 else
1214 return Speed;
1215 }
1216
1217 //===========================================================================
1218 //
1219 // APowerSpeed :: DoEffect
1220 //
1221 //===========================================================================
1222
DoEffect()1223 void APowerSpeed::DoEffect ()
1224 {
1225 Super::DoEffect ();
1226
1227 if (Owner == NULL || Owner->player == NULL)
1228 return;
1229
1230 if (Owner->player->cheats & CF_PREDICTING)
1231 return;
1232
1233 if (SpeedFlags & PSF_NOTRAIL)
1234 return;
1235
1236 if (level.time & 1)
1237 return;
1238
1239 // Check if another speed item is present to avoid multiple drawing of the speed trail.
1240 // Only the last PowerSpeed without PSF_NOTRAIL set will actually draw the trail.
1241 for (AInventory *item = Inventory; item != NULL; item = item->Inventory)
1242 {
1243 if (item->IsKindOf(RUNTIME_CLASS(APowerSpeed)) &&
1244 !(static_cast<APowerSpeed *>(item)->SpeedFlags & PSF_NOTRAIL))
1245 {
1246 return;
1247 }
1248 }
1249
1250 if (P_AproxDistance (Owner->velx, Owner->vely) <= 12*FRACUNIT)
1251 return;
1252
1253 AActor *speedMo = Spawn<APlayerSpeedTrail> (Owner->Pos(), NO_REPLACE);
1254 if (speedMo)
1255 {
1256 speedMo->angle = Owner->angle;
1257 speedMo->Translation = Owner->Translation;
1258 speedMo->target = Owner;
1259 speedMo->sprite = Owner->sprite;
1260 speedMo->frame = Owner->frame;
1261 speedMo->floorclip = Owner->floorclip;
1262
1263 // [BC] Also get the scale from the owner.
1264 speedMo->scaleX = Owner->scaleX;
1265 speedMo->scaleY = Owner->scaleY;
1266
1267 if (Owner == players[consoleplayer].camera &&
1268 !(Owner->player->cheats & CF_CHASECAM))
1269 {
1270 speedMo->renderflags |= RF_INVISIBLE;
1271 }
1272 }
1273 }
1274
1275 // Minotaur (aka Dark Servant) powerup ---------------------------------------
1276
1277 IMPLEMENT_CLASS (APowerMinotaur)
1278
1279 // Targeter powerup ---------------------------------------------------------
1280
IMPLEMENT_CLASS(APowerTargeter)1281 IMPLEMENT_CLASS (APowerTargeter)
1282
1283 void APowerTargeter::Travelled ()
1284 {
1285 InitEffect ();
1286 }
1287
InitEffect()1288 void APowerTargeter::InitEffect ()
1289 {
1290 player_t *player;
1291
1292 Super::InitEffect();
1293
1294 if ((player = Owner->player) == NULL)
1295 return;
1296
1297 FState *state = FindState("Targeter");
1298
1299 if (state != NULL)
1300 {
1301 P_SetPsprite (player, ps_targetcenter, state + 0);
1302 P_SetPsprite (player, ps_targetleft, state + 1);
1303 P_SetPsprite (player, ps_targetright, state + 2);
1304 }
1305
1306 player->psprites[ps_targetcenter].sx = (160-3)*FRACUNIT;
1307 player->psprites[ps_targetcenter].sy =
1308 player->psprites[ps_targetleft].sy =
1309 player->psprites[ps_targetright].sy = (100-3)*FRACUNIT;
1310 PositionAccuracy ();
1311 }
1312
HandlePickup(AInventory * item)1313 bool APowerTargeter::HandlePickup(AInventory *item)
1314 {
1315 if (Super::HandlePickup(item))
1316 {
1317 InitEffect(); // reset the HUD sprites
1318 return true;
1319 }
1320 return false;
1321 }
1322
1323
1324
DoEffect()1325 void APowerTargeter::DoEffect ()
1326 {
1327 Super::DoEffect ();
1328
1329 if (Owner != NULL && Owner->player != NULL)
1330 {
1331 player_t *player = Owner->player;
1332
1333 PositionAccuracy ();
1334 if (EffectTics < 5*TICRATE)
1335 {
1336 FState *state = FindState("Targeter");
1337
1338 if (state != NULL)
1339 {
1340 if (EffectTics & 32)
1341 {
1342 P_SetPsprite (player, ps_targetright, NULL);
1343 P_SetPsprite (player, ps_targetleft, state+1);
1344 }
1345 else if (EffectTics & 16)
1346 {
1347 P_SetPsprite (player, ps_targetright, state+2);
1348 P_SetPsprite (player, ps_targetleft, NULL);
1349 }
1350 }
1351 }
1352 }
1353 }
1354
EndEffect()1355 void APowerTargeter::EndEffect ()
1356 {
1357 Super::EndEffect();
1358 if (Owner != NULL && Owner->player != NULL)
1359 {
1360 P_SetPsprite (Owner->player, ps_targetcenter, NULL);
1361 P_SetPsprite (Owner->player, ps_targetleft, NULL);
1362 P_SetPsprite (Owner->player, ps_targetright, NULL);
1363 }
1364 }
1365
PositionAccuracy()1366 void APowerTargeter::PositionAccuracy ()
1367 {
1368 player_t *player = Owner->player;
1369
1370 if (player != NULL)
1371 {
1372 player->psprites[ps_targetleft].sx = (160-3)*FRACUNIT - ((100 - player->mo->accuracy) << FRACBITS);
1373 player->psprites[ps_targetright].sx = (160-3)*FRACUNIT + ((100 - player->mo->accuracy) << FRACBITS);
1374 }
1375 }
1376
1377 // Frightener Powerup --------------------------------
1378
IMPLEMENT_CLASS(APowerFrightener)1379 IMPLEMENT_CLASS (APowerFrightener)
1380
1381 //===========================================================================
1382 //
1383 // APowerFrightener :: InitEffect
1384 //
1385 //===========================================================================
1386
1387 void APowerFrightener::InitEffect ()
1388 {
1389 Super::InitEffect();
1390
1391 if (Owner== NULL || Owner->player == NULL)
1392 return;
1393
1394 Owner->player->cheats |= CF_FRIGHTENING;
1395 }
1396
1397 //===========================================================================
1398 //
1399 // APowerFrightener :: EndEffect
1400 //
1401 //===========================================================================
1402
EndEffect()1403 void APowerFrightener::EndEffect ()
1404 {
1405 Super::EndEffect();
1406
1407 if (Owner== NULL || Owner->player == NULL)
1408 return;
1409
1410 Owner->player->cheats &= ~CF_FRIGHTENING;
1411 }
1412
1413 // Buddha Powerup --------------------------------
1414
IMPLEMENT_CLASS(APowerBuddha)1415 IMPLEMENT_CLASS (APowerBuddha)
1416
1417 //===========================================================================
1418 //
1419 // APowerBuddha :: InitEffect
1420 //
1421 //===========================================================================
1422
1423 void APowerBuddha::InitEffect ()
1424 {
1425 Super::InitEffect();
1426
1427 if (Owner== NULL || Owner->player == NULL)
1428 return;
1429
1430 Owner->player->cheats |= CF_BUDDHA;
1431 }
1432
1433 //===========================================================================
1434 //
1435 // APowerBuddha :: EndEffect
1436 //
1437 //===========================================================================
1438
EndEffect()1439 void APowerBuddha::EndEffect ()
1440 {
1441 Super::EndEffect();
1442
1443 if (Owner== NULL || Owner->player == NULL)
1444 return;
1445
1446 Owner->player->cheats &= ~CF_BUDDHA;
1447 }
1448
1449 // Scanner powerup ----------------------------------------------------------
1450
1451 IMPLEMENT_CLASS (APowerScanner)
1452
1453 // Time freezer powerup -----------------------------------------------------
1454
IMPLEMENT_CLASS(APowerTimeFreezer)1455 IMPLEMENT_CLASS( APowerTimeFreezer)
1456
1457 //===========================================================================
1458 //
1459 // APowerTimeFreezer :: InitEffect
1460 //
1461 //===========================================================================
1462
1463 void APowerTimeFreezer::InitEffect()
1464 {
1465 int freezemask;
1466
1467 Super::InitEffect();
1468
1469 if (Owner == NULL || Owner->player == NULL)
1470 return;
1471
1472 // When this powerup is in effect, pause the music.
1473 S_PauseSound(false, false);
1474
1475 // Give the player and his teammates the power to move when time is frozen.
1476 freezemask = 1 << (Owner->player - players);
1477 Owner->player->timefreezer |= freezemask;
1478 for (int i = 0; i < MAXPLAYERS; i++)
1479 {
1480 if (playeringame[i] &&
1481 players[i].mo != NULL &&
1482 players[i].mo->IsTeammate(Owner)
1483 )
1484 {
1485 players[i].timefreezer |= freezemask;
1486 }
1487 }
1488
1489 // [RH] The effect ends one tic after the counter hits zero, so make
1490 // sure we start at an odd count.
1491 EffectTics += !(EffectTics & 1);
1492 if ((EffectTics & 1) == 0)
1493 {
1494 EffectTics++;
1495 }
1496 // Make sure the effect starts and ends on an even tic.
1497 if ((level.time & 1) == 0)
1498 {
1499 level.flags2 |= LEVEL2_FROZEN;
1500 }
1501 else
1502 {
1503 // Compensate for skipped tic, but beware of overflow.
1504 if(EffectTics < INT_MAX)
1505 EffectTics++;
1506 }
1507 }
1508
1509 //===========================================================================
1510 //
1511 // APowerTimeFreezer :: DoEffect
1512 //
1513 //===========================================================================
1514
DoEffect()1515 void APowerTimeFreezer::DoEffect()
1516 {
1517 Super::DoEffect();
1518 // [RH] Do not change LEVEL_FROZEN on odd tics, or the Revenant's tracer
1519 // will get thrown off.
1520 // [ED850] Don't change it if the player is predicted either.
1521 if (level.time & 1 || (Owner != NULL && Owner->player != NULL && Owner->player->cheats & CF_PREDICTING))
1522 {
1523 return;
1524 }
1525 // [RH] The "blinking" can't check against EffectTics exactly or it will
1526 // never happen, because InitEffect ensures that EffectTics will always
1527 // be odd when level.time is even.
1528 if ( EffectTics > 4*32
1529 || (( EffectTics > 3*32 && EffectTics <= 4*32 ) && ((EffectTics + 1) & 15) != 0 )
1530 || (( EffectTics > 2*32 && EffectTics <= 3*32 ) && ((EffectTics + 1) & 7) != 0 )
1531 || (( EffectTics > 32 && EffectTics <= 2*32 ) && ((EffectTics + 1) & 3) != 0 )
1532 || (( EffectTics > 0 && EffectTics <= 1*32 ) && ((EffectTics + 1) & 1) != 0 ))
1533 level.flags2 |= LEVEL2_FROZEN;
1534 else
1535 level.flags2 &= ~LEVEL2_FROZEN;
1536 }
1537
1538 //===========================================================================
1539 //
1540 // APowerTimeFreezer :: EndEffect
1541 //
1542 //===========================================================================
1543
EndEffect()1544 void APowerTimeFreezer::EndEffect()
1545 {
1546 int i;
1547
1548 Super::EndEffect();
1549
1550 // If there is an owner, remove the timefreeze flag corresponding to
1551 // her from all players.
1552 if (Owner != NULL && Owner->player != NULL)
1553 {
1554 int freezemask = ~(1 << (Owner->player - players));
1555 for (i = 0; i < MAXPLAYERS; ++i)
1556 {
1557 players[i].timefreezer &= freezemask;
1558 }
1559 }
1560
1561 // Are there any players who still have timefreezer bits set?
1562 for (i = 0; i < MAXPLAYERS; ++i)
1563 {
1564 if (playeringame[i] && players[i].timefreezer != 0)
1565 {
1566 break;
1567 }
1568 }
1569
1570 if (i == MAXPLAYERS)
1571 {
1572 // No, so allow other actors to move about freely once again.
1573 level.flags2 &= ~LEVEL2_FROZEN;
1574
1575 // Also, turn the music back on.
1576 S_ResumeSound(false);
1577 }
1578 }
1579
1580 // Damage powerup ------------------------------------------------------
1581
IMPLEMENT_CLASS(APowerDamage)1582 IMPLEMENT_CLASS(APowerDamage)
1583
1584 //===========================================================================
1585 //
1586 // APowerDamage :: InitEffect
1587 //
1588 //===========================================================================
1589
1590 void APowerDamage::InitEffect( )
1591 {
1592 Super::InitEffect();
1593
1594 // Use sound channel 5 to avoid interference with other actions.
1595 if (Owner != NULL) S_Sound(Owner, 5, SeeSound, 1.0f, ATTN_NONE);
1596 }
1597
1598 //===========================================================================
1599 //
1600 // APowerDamage :: EndEffect
1601 //
1602 //===========================================================================
1603
EndEffect()1604 void APowerDamage::EndEffect( )
1605 {
1606 Super::EndEffect();
1607 // Use sound channel 5 to avoid interference with other actions.
1608 if (Owner != NULL) S_Sound(Owner, 5, DeathSound, 1.0f, ATTN_NONE);
1609 }
1610
1611 //===========================================================================
1612 //
1613 // APowerDamage :: ModifyDamage
1614 //
1615 //===========================================================================
1616
ModifyDamage(int damage,FName damageType,int & newdamage,bool passive)1617 void APowerDamage::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
1618 {
1619 static const fixed_t def = 4*FRACUNIT;
1620 if (!passive && damage > 0)
1621 {
1622 const fixed_t * pdf = NULL;
1623 DmgFactors * df = GetClass()->ActorInfo->DamageFactors;
1624 if (df != NULL && df->CountUsed() != 0)
1625 {
1626 pdf = df->CheckFactor(damageType);
1627 }
1628 else
1629 {
1630 pdf = &def;
1631 }
1632 if (pdf != NULL)
1633 {
1634 damage = newdamage = FixedMul(damage, *pdf);
1635 if (*pdf > 0 && damage == 0) damage = newdamage = 1; // don't allow zero damage as result of an underflow
1636 if (Owner != NULL && *pdf > FRACUNIT) S_Sound(Owner, 5, ActiveSound, 1.0f, ATTN_NONE);
1637 }
1638 }
1639 if (Inventory != NULL) Inventory->ModifyDamage(damage, damageType, newdamage, passive);
1640 }
1641
1642 // Quarter damage powerup ------------------------------------------------------
1643
IMPLEMENT_CLASS(APowerProtection)1644 IMPLEMENT_CLASS(APowerProtection)
1645
1646 #define PROTECTION_FLAGS3 (MF3_NORADIUSDMG | MF3_DONTMORPH | MF3_DONTSQUASH | MF3_DONTBLAST | MF3_NOTELEOTHER)
1647 #define PROTECTION_FLAGS5 (MF5_NOPAIN | MF5_DONTRIP)
1648
1649 //===========================================================================
1650 //
1651 // APowerProtection :: InitEffect
1652 //
1653 //===========================================================================
1654
1655 void APowerProtection::InitEffect( )
1656 {
1657 Super::InitEffect();
1658
1659 if (Owner != NULL)
1660 {
1661 S_Sound(Owner, CHAN_AUTO, SeeSound, 1.0f, ATTN_NONE);
1662
1663 // Transfer various protection flags if owner does not already have them.
1664 // If the owner already has the flag, clear it from the powerup.
1665 // If the powerup still has a flag set, add it to the owner.
1666 flags3 &= ~(Owner->flags3 & PROTECTION_FLAGS3);
1667 Owner->flags3 |= flags3 & PROTECTION_FLAGS3;
1668
1669 flags5 &= ~(Owner->flags5 & PROTECTION_FLAGS5);
1670 Owner->flags5 |= flags5 & PROTECTION_FLAGS5;
1671 }
1672 }
1673
1674 //===========================================================================
1675 //
1676 // APowerProtection :: EndEffect
1677 //
1678 //===========================================================================
1679
EndEffect()1680 void APowerProtection::EndEffect( )
1681 {
1682 Super::EndEffect();
1683 if (Owner != NULL)
1684 {
1685 S_Sound(Owner, CHAN_AUTO, DeathSound, 1.0f, ATTN_NONE);
1686 Owner->flags3 &= ~(flags3 & PROTECTION_FLAGS3);
1687 Owner->flags5 &= ~(flags5 & PROTECTION_FLAGS5);
1688 }
1689 }
1690
1691 //===========================================================================
1692 //
1693 // APowerProtection :: AbsorbDamage
1694 //
1695 //===========================================================================
1696
ModifyDamage(int damage,FName damageType,int & newdamage,bool passive)1697 void APowerProtection::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
1698 {
1699 static const fixed_t def = FRACUNIT/4;
1700 if (passive && damage > 0)
1701 {
1702 const fixed_t *pdf = NULL;
1703 DmgFactors *df = GetClass()->ActorInfo->DamageFactors;
1704 if (df != NULL && df->CountUsed() != 0)
1705 {
1706 pdf = df->CheckFactor(damageType);
1707 }
1708 else pdf = &def;
1709
1710 if (pdf != NULL)
1711 {
1712 damage = newdamage = FixedMul(damage, *pdf);
1713 if (Owner != NULL && *pdf < FRACUNIT) S_Sound(Owner, CHAN_AUTO, ActiveSound, 1.0f, ATTN_NONE);
1714 }
1715 }
1716 if (Inventory != NULL)
1717 {
1718 Inventory->ModifyDamage(damage, damageType, newdamage, passive);
1719 }
1720 }
1721
1722 // Drain rune -------------------------------------------------------
1723
IMPLEMENT_CLASS(APowerDrain)1724 IMPLEMENT_CLASS(APowerDrain)
1725
1726 //===========================================================================
1727 //
1728 // ARuneDrain :: InitEffect
1729 //
1730 //===========================================================================
1731
1732 void APowerDrain::InitEffect( )
1733 {
1734 Super::InitEffect();
1735
1736 if (Owner== NULL || Owner->player == NULL)
1737 return;
1738
1739 // Give the player the power to drain life from opponents when he damages them.
1740 Owner->player->cheats |= CF_DRAIN;
1741 }
1742
1743 //===========================================================================
1744 //
1745 // ARuneDrain :: EndEffect
1746 //
1747 //===========================================================================
1748
EndEffect()1749 void APowerDrain::EndEffect( )
1750 {
1751 Super::EndEffect();
1752
1753 // Nothing to do if there's no owner.
1754 if (Owner != NULL && Owner->player != NULL)
1755 {
1756 // Take away the drain power.
1757 Owner->player->cheats &= ~CF_DRAIN;
1758 }
1759 }
1760
1761
1762 // Regeneration rune -------------------------------------------------------
1763
IMPLEMENT_CLASS(APowerRegeneration)1764 IMPLEMENT_CLASS(APowerRegeneration)
1765
1766 //===========================================================================
1767 //
1768 // APowerRegeneration :: DoEffect
1769 //
1770 //===========================================================================
1771
1772 void APowerRegeneration::DoEffect()
1773 {
1774 Super::DoEffect();
1775 if (Owner != NULL && Owner->health > 0 && (level.time & 31) == 0)
1776 {
1777 if (P_GiveBody(Owner, Strength/FRACUNIT))
1778 {
1779 S_Sound(Owner, CHAN_ITEM, "*regenerate", 1, ATTN_NORM );
1780 }
1781 }
1782 }
1783
1784 // High jump rune -------------------------------------------------------
1785
IMPLEMENT_CLASS(APowerHighJump)1786 IMPLEMENT_CLASS(APowerHighJump)
1787
1788 //===========================================================================
1789 //
1790 // ARuneHighJump :: InitEffect
1791 //
1792 //===========================================================================
1793
1794 void APowerHighJump::InitEffect( )
1795 {
1796 Super::InitEffect();
1797
1798 if (Owner== NULL || Owner->player == NULL)
1799 return;
1800
1801 // Give the player the power to jump much higher.
1802 Owner->player->cheats |= CF_HIGHJUMP;
1803 }
1804
1805 //===========================================================================
1806 //
1807 // ARuneHighJump :: EndEffect
1808 //
1809 //===========================================================================
1810
EndEffect()1811 void APowerHighJump::EndEffect( )
1812 {
1813 Super::EndEffect();
1814 // Nothing to do if there's no owner.
1815 if (Owner != NULL && Owner->player != NULL)
1816 {
1817 // Take away the high jump power.
1818 Owner->player->cheats &= ~CF_HIGHJUMP;
1819 }
1820 }
1821
1822 // Double firing speed rune ---------------------------------------------
1823
IMPLEMENT_CLASS(APowerDoubleFiringSpeed)1824 IMPLEMENT_CLASS(APowerDoubleFiringSpeed)
1825
1826 //===========================================================================
1827 //
1828 // APowerDoubleFiringSpeed :: InitEffect
1829 //
1830 //===========================================================================
1831
1832 void APowerDoubleFiringSpeed::InitEffect( )
1833 {
1834 Super::InitEffect();
1835
1836 if (Owner== NULL || Owner->player == NULL)
1837 return;
1838
1839 // Give the player the power to shoot twice as fast.
1840 Owner->player->cheats |= CF_DOUBLEFIRINGSPEED;
1841 }
1842
1843 //===========================================================================
1844 //
1845 // APowerDoubleFiringSpeed :: EndEffect
1846 //
1847 //===========================================================================
1848
EndEffect()1849 void APowerDoubleFiringSpeed::EndEffect( )
1850 {
1851 Super::EndEffect();
1852 // Nothing to do if there's no owner.
1853 if (Owner != NULL && Owner->player != NULL)
1854 {
1855 // Take away the shooting twice as fast power.
1856 Owner->player->cheats &= ~CF_DOUBLEFIRINGSPEED;
1857 }
1858 }
1859
1860 // Morph powerup ------------------------------------------------------
1861
IMPLEMENT_CLASS(APowerMorph)1862 IMPLEMENT_CLASS(APowerMorph)
1863
1864 //===========================================================================
1865 //
1866 // APowerMorph :: Serialize
1867 //
1868 //===========================================================================
1869
1870 void APowerMorph::Serialize (FArchive &arc)
1871 {
1872 Super::Serialize (arc);
1873 arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash;
1874 arc << Player;
1875 }
1876
1877 //===========================================================================
1878 //
1879 // APowerMorph :: InitEffect
1880 //
1881 //===========================================================================
1882
InitEffect()1883 void APowerMorph::InitEffect( )
1884 {
1885 Super::InitEffect();
1886
1887 if (Owner != NULL && Owner->player != NULL && PlayerClass != NAME_None)
1888 {
1889 player_t *realplayer = Owner->player; // Remember the identity of the player
1890 const PClass *morph_flash = PClass::FindClass (MorphFlash);
1891 const PClass *unmorph_flash = PClass::FindClass (UnMorphFlash);
1892 const PClass *player_class = PClass::FindClass (PlayerClass);
1893 if (P_MorphPlayer(realplayer, realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
1894 {
1895 Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
1896 ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor)
1897 Player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
1898 }
1899 else // morph failed - give the caller an opportunity to fail the pickup completely
1900 {
1901 ItemFlags |= IF_INITEFFECTFAILED; // Let the caller know that the activation failed (can fail the pickup if appropriate)
1902 }
1903 }
1904 }
1905
1906 //===========================================================================
1907 //
1908 // APowerMorph :: EndEffect
1909 //
1910 //===========================================================================
1911
EndEffect()1912 void APowerMorph::EndEffect( )
1913 {
1914 Super::EndEffect();
1915
1916 // Abort if owner already destroyed
1917 if (Owner == NULL)
1918 {
1919 assert(Player == NULL);
1920 return;
1921 }
1922
1923 // Abort if owner already unmorphed
1924 if (Player == NULL)
1925 {
1926 return;
1927 }
1928
1929 // Abort if owner is dead; their Die() method will
1930 // take care of any required unmorphing on death.
1931 if (Player->health <= 0)
1932 {
1933 return;
1934 }
1935
1936 // Unmorph if possible
1937 if (!bNoCallUndoMorph)
1938 {
1939 int savedMorphTics = Player->morphTics;
1940 P_UndoPlayerMorph (Player, Player, 0, !!(Player->MorphStyle & MORPH_UNDOALWAYS));
1941
1942 // Abort if unmorph failed; in that case,
1943 // set the usual retry timer and return.
1944 if (Player != NULL && Player->morphTics)
1945 {
1946 // Transfer retry timeout
1947 // to the powerup's timer.
1948 EffectTics = Player->morphTics;
1949 // Reload negative morph tics;
1950 // use actual value; it may
1951 // be in use for animation.
1952 Player->morphTics = savedMorphTics;
1953 // Try again some time later
1954 return;
1955 }
1956 }
1957 // Unmorph suceeded
1958 Player = NULL;
1959 }
1960
1961 // Infinite Ammo Powerup -----------------------------------------------------
1962
IMPLEMENT_CLASS(APowerInfiniteAmmo)1963 IMPLEMENT_CLASS(APowerInfiniteAmmo)
1964
1965 //===========================================================================
1966 //
1967 // APowerInfiniteAmmo :: InitEffect
1968 //
1969 //===========================================================================
1970
1971 void APowerInfiniteAmmo::InitEffect( )
1972 {
1973 Super::InitEffect();
1974
1975 if (Owner== NULL || Owner->player == NULL)
1976 return;
1977
1978 // Give the player infinite ammo
1979 Owner->player->cheats |= CF_INFINITEAMMO;
1980 }
1981
1982 //===========================================================================
1983 //
1984 // APowerInfiniteAmmo :: EndEffect
1985 //
1986 //===========================================================================
1987
EndEffect()1988 void APowerInfiniteAmmo::EndEffect( )
1989 {
1990 Super::EndEffect();
1991
1992 // Nothing to do if there's no owner.
1993 if (Owner != NULL && Owner->player != NULL)
1994 {
1995 // Take away the limitless ammo
1996 Owner->player->cheats &= ~CF_INFINITEAMMO;
1997 }
1998 }
1999
2000