1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 * Handling interactions (i.e., collisions).
30 *-----------------------------------------------------------------------------*/
39 #include "d_deh.h" // Ty 03/22/98 - externalized strings
47 #pragma implementation "p_inter.h"
53 // Ty 03/07/98 - add deh externals
54 // Maximums and such were hardcoded values. Need to externalize those for
55 // dehacked support (and future flexibility). Most var names came from the key
56 // strings used in dehacked.
58 int initial_health
= 100;
59 int initial_bullets
= 50;
60 int maxhealth
= 100; // was MAXHEALTH as a #define, used only in this module
62 int green_armor_class
= 1; // these are involved with armortype below
63 int blue_armor_class
= 2;
65 int soul_health
= 100;
66 int mega_health
= 200;
67 int god_health
= 100; // these are used in cheats (see st_stuff.c)
69 int idfa_armor_class
= 2;
70 // not actually used due to pairing of cheat_k and cheat_fa
71 int idkfa_armor
= 200;
72 int idkfa_armor_class
= 2;
74 int bfgcells
= 40; // used in p_pspr.c
75 // Ty 03/07/98 - end deh externals
77 // a weapon is found with two clip loads,
78 // a big item has five clip loads
79 int maxammo
[NUMAMMO
] = {200, 50, 300, 50};
80 int clipammo
[NUMAMMO
] = { 10, 4, 20, 1};
88 // Num is the number of clip loads,
89 // not the individual count (0= 1/2 clip).
90 // Returns false if the ammo can't be picked up at all
93 boolean
P_GiveAmmo(player_t
*player
, ammotype_t ammo
, int num
)
97 if (ammo
== am_noammo
)
101 if (ammo
< 0 || ammo
> NUMAMMO
)
102 I_Error ("P_GiveAmmo: bad type %i", ammo
);
105 if ( player
->ammo
[ammo
] == player
->maxammo
[ammo
] )
109 num
*= clipammo
[ammo
];
111 num
= clipammo
[ammo
]/2;
113 // give double ammo in trainer mode, you'll need in nightmare
114 if (gameskill
== sk_baby
|| gameskill
== sk_nightmare
)
117 oldammo
= player
->ammo
[ammo
];
118 player
->ammo
[ammo
] += num
;
120 if (player
->ammo
[ammo
] > player
->maxammo
[ammo
])
121 player
->ammo
[ammo
] = player
->maxammo
[ammo
];
123 // If non zero ammo, don't change up weapons, player was lower on purpose.
127 // We were down to zero, so select a new weapon.
128 // Preferences are not user selectable.
133 if (player
->readyweapon
== wp_fist
) {
134 if (player
->weaponowned
[wp_chaingun
])
135 player
->pendingweapon
= wp_chaingun
;
137 player
->pendingweapon
= wp_pistol
;
142 if (player
->readyweapon
== wp_fist
|| player
->readyweapon
== wp_pistol
)
143 if (player
->weaponowned
[wp_shotgun
])
144 player
->pendingweapon
= wp_shotgun
;
148 if (player
->readyweapon
== wp_fist
|| player
->readyweapon
== wp_pistol
)
149 if (player
->weaponowned
[wp_plasma
])
150 player
->pendingweapon
= wp_plasma
;
154 if (player
->readyweapon
== wp_fist
)
155 if (player
->weaponowned
[wp_missile
])
156 player
->pendingweapon
= wp_missile
;
165 // The weapon name may have a MF_DROPPED flag ored in.
168 boolean
P_GiveWeapon(player_t
*player
, weapontype_t weapon
, boolean dropped
)
173 if (netgame
&& deathmatch
!=2 && !dropped
)
175 // leave placed weapons forever on net games
176 if (player
->weaponowned
[weapon
])
179 player
->bonuscount
+= BONUSADD
;
180 player
->weaponowned
[weapon
] = true;
182 P_GiveAmmo(player
, weaponinfo
[weapon
].ammo
, deathmatch
? 5 : 2);
184 player
->pendingweapon
= weapon
;
185 /* cph 20028/10 - for old-school DM addicts, allow old behavior
186 * where only consoleplayer's pickup sounds are heard */
187 if (!comp
[comp_sound
] || player
== &players
[consoleplayer
])
188 S_StartSound (player
->mo
, sfx_wpnup
|PICKUP_SOUND
); // killough 4/25/98
192 if (weaponinfo
[weapon
].ammo
!= am_noammo
)
194 // give one clip with a dropped weapon,
195 // two clips with a found weapon
196 gaveammo
= P_GiveAmmo (player
, weaponinfo
[weapon
].ammo
, dropped
? 1 : 2);
201 if (player
->weaponowned
[weapon
])
206 player
->weaponowned
[weapon
] = true;
207 player
->pendingweapon
= weapon
;
209 return gaveweapon
|| gaveammo
;
214 // Returns false if the body isn't needed at all
217 boolean
P_GiveBody(player_t
*player
, int num
)
219 if (player
->health
>= maxhealth
)
220 return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth
221 player
->health
+= num
;
222 if (player
->health
> maxhealth
)
223 player
->health
= maxhealth
;
224 player
->mo
->health
= player
->health
;
230 // Returns false if the armor is worse
231 // than the current armor.
234 boolean
P_GiveArmor(player_t
*player
, int armortype
)
236 int hits
= armortype
*100;
237 if (player
->armorpoints
>= hits
)
238 return false; // don't pick up
239 player
->armortype
= armortype
;
240 player
->armorpoints
= hits
;
248 void P_GiveCard(player_t
*player
, card_t card
)
250 if (player
->cards
[card
])
252 player
->bonuscount
= BONUSADD
;
253 player
->cards
[card
] = 1;
259 // Rewritten by Lee Killough
262 boolean
P_GivePower(player_t
*player
, int power
)
264 static const int tics
[NUMPOWERS
] = {
265 INVULNTICS
, 1 /* strength */, INVISTICS
,
266 IRONTICS
, 1 /* allmap */, INFRATICS
,
271 case pw_invisibility
:
272 player
->mo
->flags
|= MF_SHADOW
;
275 if (player
->powers
[pw_allmap
])
279 P_GiveBody(player
,100);
283 // Unless player has infinite duration cheat, set duration (killough)
285 if (player
->powers
[power
] >= 0)
286 player
->powers
[power
] = tics
[power
];
291 // P_TouchSpecialThing
294 void P_TouchSpecialThing(mobj_t
*special
, mobj_t
*toucher
)
299 fixed_t delta
= special
->z
- toucher
->z
;
301 if (delta
> toucher
->height
|| delta
< -8*FRACUNIT
)
302 return; // out of reach
305 player
= toucher
->player
;
307 // Dead thing touching.
308 // Can happen with a sliding player corpse.
309 if (toucher
->health
<= 0)
312 // Identify by sprite.
313 switch (special
->sprite
)
317 if (!P_GiveArmor (player
, green_armor_class
))
319 player
->message
= s_GOTARMOR
; // Ty 03/22/98 - externalized
323 if (!P_GiveArmor (player
, blue_armor_class
))
325 player
->message
= s_GOTMEGA
; // Ty 03/22/98 - externalized
330 player
->health
++; // can go over 100%
331 if (player
->health
> (maxhealth
* 2))
332 player
->health
= (maxhealth
* 2);
333 player
->mo
->health
= player
->health
;
334 player
->message
= s_GOTHTHBONUS
; // Ty 03/22/98 - externalized
338 player
->armorpoints
++; // can go over 100%
339 if (player
->armorpoints
> max_armor
)
340 player
->armorpoints
= max_armor
;
341 if (!player
->armortype
)
342 player
->armortype
= green_armor_class
;
343 player
->message
= s_GOTARMBONUS
; // Ty 03/22/98 - externalized
347 player
->health
+= soul_health
;
348 if (player
->health
> max_soul
)
349 player
->health
= max_soul
;
350 player
->mo
->health
= player
->health
;
351 player
->message
= s_GOTSUPER
; // Ty 03/22/98 - externalized
356 if (gamemode
!= commercial
)
358 player
->health
= mega_health
;
359 player
->mo
->health
= player
->health
;
360 P_GiveArmor (player
,blue_armor_class
);
361 player
->message
= s_GOTMSPHERE
; // Ty 03/22/98 - externalized
366 // leave cards for everyone
368 if (!player
->cards
[it_bluecard
])
369 player
->message
= s_GOTBLUECARD
; // Ty 03/22/98 - externalized
370 P_GiveCard (player
, it_bluecard
);
376 if (!player
->cards
[it_yellowcard
])
377 player
->message
= s_GOTYELWCARD
; // Ty 03/22/98 - externalized
378 P_GiveCard (player
, it_yellowcard
);
384 if (!player
->cards
[it_redcard
])
385 player
->message
= s_GOTREDCARD
; // Ty 03/22/98 - externalized
386 P_GiveCard (player
, it_redcard
);
392 if (!player
->cards
[it_blueskull
])
393 player
->message
= s_GOTBLUESKUL
; // Ty 03/22/98 - externalized
394 P_GiveCard (player
, it_blueskull
);
400 if (!player
->cards
[it_yellowskull
])
401 player
->message
= s_GOTYELWSKUL
; // Ty 03/22/98 - externalized
402 P_GiveCard (player
, it_yellowskull
);
408 if (!player
->cards
[it_redskull
])
409 player
->message
= s_GOTREDSKULL
; // Ty 03/22/98 - externalized
410 P_GiveCard (player
, it_redskull
);
417 if (!P_GiveBody (player
, 10))
419 player
->message
= s_GOTSTIM
; // Ty 03/22/98 - externalized
423 if (!P_GiveBody (player
, 25))
426 if (player
->health
< 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug
427 player
->message
= s_GOTMEDINEED
; // Ty 03/22/98 - externalized
429 player
->message
= s_GOTMEDIKIT
; // Ty 03/22/98 - externalized
435 if (!P_GivePower (player
, pw_invulnerability
))
437 player
->message
= s_GOTINVUL
; // Ty 03/22/98 - externalized
442 if (!P_GivePower (player
, pw_strength
))
444 player
->message
= s_GOTBERSERK
; // Ty 03/22/98 - externalized
445 if (player
->readyweapon
!= wp_fist
)
446 player
->pendingweapon
= wp_fist
;
451 if (!P_GivePower (player
, pw_invisibility
))
453 player
->message
= s_GOTINVIS
; // Ty 03/22/98 - externalized
458 if (!P_GivePower (player
, pw_ironfeet
))
460 player
->message
= s_GOTSUIT
; // Ty 03/22/98 - externalized
465 if (!P_GivePower (player
, pw_allmap
))
467 player
->message
= s_GOTMAP
; // Ty 03/22/98 - externalized
472 if (!P_GivePower (player
, pw_infrared
))
474 player
->message
= s_GOTVISOR
; // Ty 03/22/98 - externalized
480 if (special
->flags
& MF_DROPPED
)
482 if (!P_GiveAmmo (player
,am_clip
,0))
487 if (!P_GiveAmmo (player
,am_clip
,1))
490 player
->message
= s_GOTCLIP
; // Ty 03/22/98 - externalized
494 if (!P_GiveAmmo (player
, am_clip
,5))
496 player
->message
= s_GOTCLIPBOX
; // Ty 03/22/98 - externalized
500 if (!P_GiveAmmo (player
, am_misl
,1))
502 player
->message
= s_GOTROCKET
; // Ty 03/22/98 - externalized
506 if (!P_GiveAmmo (player
, am_misl
,5))
508 player
->message
= s_GOTROCKBOX
; // Ty 03/22/98 - externalized
512 if (!P_GiveAmmo (player
, am_cell
,1))
514 player
->message
= s_GOTCELL
; // Ty 03/22/98 - externalized
518 if (!P_GiveAmmo (player
, am_cell
,5))
520 player
->message
= s_GOTCELLBOX
; // Ty 03/22/98 - externalized
524 if (!P_GiveAmmo (player
, am_shell
,1))
526 player
->message
= s_GOTSHELLS
; // Ty 03/22/98 - externalized
530 if (!P_GiveAmmo (player
, am_shell
,5))
532 player
->message
= s_GOTSHELLBOX
; // Ty 03/22/98 - externalized
536 if (!player
->backpack
)
538 for (i
=0 ; i
<NUMAMMO
; i
++)
539 player
->maxammo
[i
] *= 2;
540 player
->backpack
= true;
542 for (i
=0 ; i
<NUMAMMO
; i
++)
543 P_GiveAmmo (player
, i
, 1);
544 player
->message
= s_GOTBACKPACK
; // Ty 03/22/98 - externalized
549 if (!P_GiveWeapon (player
, wp_bfg
, false) )
551 player
->message
= s_GOTBFG9000
; // Ty 03/22/98 - externalized
556 if (!P_GiveWeapon (player
, wp_chaingun
, (special
->flags
&MF_DROPPED
)!=0) )
558 player
->message
= s_GOTCHAINGUN
; // Ty 03/22/98 - externalized
563 if (!P_GiveWeapon (player
, wp_chainsaw
, false) )
565 player
->message
= s_GOTCHAINSAW
; // Ty 03/22/98 - externalized
570 if (!P_GiveWeapon (player
, wp_missile
, false) )
572 player
->message
= s_GOTLAUNCHER
; // Ty 03/22/98 - externalized
577 if (!P_GiveWeapon (player
, wp_plasma
, false) )
579 player
->message
= s_GOTPLASMA
; // Ty 03/22/98 - externalized
584 if (!P_GiveWeapon (player
, wp_shotgun
, (special
->flags
&MF_DROPPED
)!=0 ) )
586 player
->message
= s_GOTSHOTGUN
; // Ty 03/22/98 - externalized
591 if (!P_GiveWeapon(player
, wp_supershotgun
, (special
->flags
&MF_DROPPED
)!=0))
593 player
->message
= s_GOTSHOTGUN2
; // Ty 03/22/98 - externalized
598 I_Error ("P_SpecialThing: Unknown gettable thing");
601 if (special
->flags
& MF_COUNTITEM
)
603 P_RemoveMobj (special
);
604 player
->bonuscount
+= BONUSADD
;
606 /* cph 20028/10 - for old-school DM addicts, allow old behavior
607 * where only consoleplayer's pickup sounds are heard */
608 if (!comp
[comp_sound
] || player
== &players
[consoleplayer
])
609 S_StartSound (player
->mo
, sound
| PICKUP_SOUND
); // killough 4/25/98
615 // killough 11/98: make static
616 static void P_KillMobj(mobj_t
*source
, mobj_t
*target
)
621 target
->flags
&= ~(MF_SHOOTABLE
|MF_FLOAT
|MF_SKULLFLY
);
623 if (target
->type
!= MT_SKULL
)
624 target
->flags
&= ~MF_NOGRAVITY
;
626 target
->flags
|= MF_CORPSE
|MF_DROPOFF
;
627 target
->height
>>= 2;
629 if (!((target
->flags
^ MF_COUNTKILL
) & (MF_FRIEND
| MF_COUNTKILL
)))
632 if (source
&& source
->player
)
634 // count for intermission
635 if (target
->flags
& MF_COUNTKILL
)
636 source
->player
->killcount
++;
638 source
->player
->frags
[target
->player
-players
]++;
641 if (target
->flags
& MF_COUNTKILL
) { /* Add to kills tally */
642 if ((compatibility_level
< lxdoom_1_compatibility
) || !netgame
) {
644 // count all monster deaths,
645 // even those caused by other monsters
646 players
[0].killcount
++;
649 // try and find a player to give the kill to, otherwise give the
650 // kill to a random player. this fixes the missing monsters bug
652 // CPhipps - not a bug as such, but certainly an inconsistency.
653 if (target
->lastenemy
&& target
->lastenemy
->health
> 0
654 && target
->lastenemy
->player
) // Fighting a player
655 target
->lastenemy
->player
->killcount
++;
657 // cph - randomely choose a player in the game to be credited
658 // and do it uniformly between the active players
659 unsigned int activeplayers
= 0, player
, i
;
661 for (player
= 0; player
<MAXPLAYERS
; player
++)
662 if (playeringame
[player
])
666 player
= P_Random(pr_friends
) % activeplayers
;
668 for (i
=0; i
<MAXPLAYERS
; i
++)
671 players
[i
].killcount
++;
679 // count environment kills against you
681 target
->player
->frags
[target
->player
-players
]++;
683 target
->flags
&= ~MF_SOLID
;
684 target
->player
->playerstate
= PST_DEAD
;
685 P_DropWeapon (target
->player
);
687 if (target
->player
== &players
[consoleplayer
] && (automapmode
& am_active
))
688 AM_Stop(); // don't die in auto map; switch view prior to dying
691 if (target
->health
< -target
->info
->spawnhealth
&& target
->info
->xdeathstate
)
692 P_SetMobjState (target
, target
->info
->xdeathstate
);
694 P_SetMobjState (target
, target
->info
->deathstate
);
696 target
->tics
-= P_Random(pr_killtics
)&3;
698 if (target
->tics
< 1)
702 // This determines the kind of object spawned
703 // during the death frame of a thing.
705 switch (target
->type
)
724 mo
= P_SpawnMobj (target
->x
,target
->y
,ONFLOORZ
, item
);
725 mo
->flags
|= MF_DROPPED
; // special versions of items
730 // Damages both enemies and players
731 // "inflictor" is the thing that caused the damage
732 // creature or missile, can be NULL (slime, etc)
733 // "source" is the thing to target after taking damage
735 // Source and inflictor are the same for melee attacks.
736 // Source can be NULL for slime, barrel explosions
737 // and other environmental stuff.
740 void P_DamageMobj(mobj_t
*target
,mobj_t
*inflictor
, mobj_t
*source
, int damage
)
743 boolean justhit
; /* killough 11/98 */
745 /* killough 8/31/98: allow bouncers to take damage */
746 if (!(target
->flags
& (MF_SHOOTABLE
| MF_BOUNCES
)))
747 return; // shouldn't happen...
749 if (target
->health
<= 0)
752 /* proff 11/22/98: Andy Baker's Stealth monsters */
753 if (target
->flags
& MF_STEALTH
)
754 P_BecomeVisible(target
);
756 if (target
->flags
& MF_SKULLFLY
)
757 target
->momx
= target
->momy
= target
->momz
= 0;
759 player
= target
->player
;
760 if (player
&& gameskill
== sk_baby
)
761 damage
>>= 1; // take half damage in trainer mode
763 // Some close combat weapons should not
764 // inflict thrust and push the victim out of reach,
765 // thus kick away unless using the chainsaw.
767 if (inflictor
&& !(target
->flags
& MF_NOCLIP
) &&
768 (!source
|| !source
->player
||
769 source
->player
->readyweapon
!= wp_chainsaw
))
771 unsigned ang
= R_PointToAngle2 (inflictor
->x
, inflictor
->y
,
772 target
->x
, target
->y
);
774 fixed_t thrust
= damage
*(FRACUNIT
>>3)*100/target
->info
->mass
;
776 // make fall forwards sometimes
777 if ( damage
< 40 && damage
> target
->health
778 && target
->z
- inflictor
->z
> 64*FRACUNIT
779 && P_Random(pr_damagemobj
) & 1)
785 ang
>>= ANGLETOFINESHIFT
;
786 target
->momx
+= FixedMul (thrust
, finecosine
[ang
]);
787 target
->momy
+= FixedMul (thrust
, finesine
[ang
]);
789 /* killough 11/98: thrust objects hanging off ledges */
790 if (target
->intflags
& MIF_FALLING
&& target
->gear
>= MAXGEAR
)
797 // end of game hell hack
798 if (target
->subsector
->sector
->special
== 11 && damage
>= target
->health
)
799 damage
= target
->health
- 1;
801 // Below certain threshold,
802 // ignore damage in GOD mode, or with INVUL power.
803 // killough 3/26/98: make god mode 100% god mode in non-compat mode
805 if ((damage
< 1000 || (!comp
[comp_god
] && (player
->cheats
&CF_GODMODE
))) &&
806 (player
->cheats
&CF_GODMODE
|| player
->powers
[pw_invulnerability
]))
809 if (player
->armortype
)
811 int saved
= player
->armortype
== 1 ? damage
/3 : damage
/2;
812 if (player
->armorpoints
<= saved
)
815 saved
= player
->armorpoints
;
816 player
->armortype
= 0;
818 player
->armorpoints
-= saved
;
822 player
->health
-= damage
; // mirror mobj health here for Dave
823 if (player
->health
< 0)
826 player
->attacker
= source
;
827 player
->damagecount
+= damage
; // add damage after armor / invuln
829 if (player
->damagecount
> 100)
830 player
->damagecount
= 100; // teleport stomp does 10k points...
834 target
->health
-= damage
;
835 if (target
->health
<= 0)
837 P_KillMobj (source
, target
);
841 // killough 9/7/98: keep track of targets so that friends can help friends
844 /* If target is a player, set player's target to source,
845 * so that a friend can tell who's hurting a player
848 P_SetTarget(&target
->target
, source
);
851 * If target's health is less than 50%, move it to the front of its list.
852 * This will slightly increase the chances that enemies will choose to
853 * "finish it off", but its main purpose is to alert friends of danger.
855 if (target
->health
*2 < target
->info
->spawnhealth
)
857 thinker_t
*cap
= &thinkerclasscap
[target
->flags
& MF_FRIEND
?
858 th_friends
: th_enemies
];
859 (target
->thinker
.cprev
->cnext
= target
->thinker
.cnext
)->cprev
=
860 target
->thinker
.cprev
;
861 (target
->thinker
.cnext
= cap
->cnext
)->cprev
= &target
->thinker
;
862 (target
->thinker
.cprev
= cap
)->cnext
= &target
->thinker
;
866 if ((justhit
= (P_Random (pr_painchance
) < target
->info
->painchance
&&
867 !(target
->flags
& MF_SKULLFLY
)))) //killough 11/98: see below
868 P_SetMobjState(target
, target
->info
->painstate
);
870 target
->reactiontime
= 0; // we're awake now...
872 /* killough 9/9/98: cleaned up, made more consistent: */
874 if (source
&& source
!= target
&& source
->type
!= MT_VILE
&&
875 (!target
->threshold
|| target
->type
== MT_VILE
) &&
876 ((source
->flags
^ target
->flags
) & MF_FRIEND
||
877 monster_infighting
||
880 /* if not intent on another player, chase after this one
882 * killough 2/15/98: remember last enemy, to prevent
883 * sleeping early; 2/21/98: Place priority on players
884 * killough 9/9/98: cleaned up, made more consistent:
887 if (!target
->lastenemy
|| target
->lastenemy
->health
<= 0 ||
889 !target
->lastenemy
->player
:
890 !((target
->flags
^ target
->lastenemy
->flags
) & MF_FRIEND
) &&
891 target
->target
!= source
)) // remember last enemy - killough
892 P_SetTarget(&target
->lastenemy
, target
->target
);
894 P_SetTarget(&target
->target
, source
); // killough 11/98
895 target
->threshold
= BASETHRESHOLD
;
896 if (target
->state
== &states
[target
->info
->spawnstate
]
897 && target
->info
->seestate
!= S_NULL
)
898 P_SetMobjState (target
, target
->info
->seestate
);
901 /* killough 11/98: Don't attack a friend, unless hit by that friend. */
902 if (justhit
&& (target
->target
== source
|| !target
->target
||
903 !(target
->flags
& target
->target
->flags
& MF_FRIEND
)))
904 target
->flags
|= MF_JUSTHIT
; // fight back!