1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 // Handling interactions (i.e., collisions).
25 //-----------------------------------------------------------------------------
56 // a weapon is found with two clip loads,
57 // a big item has five clip loads
58 int maxammo
[NUMAMMO
] = {200, 50, 300, 50};
59 int clipammo
[NUMAMMO
] = {10, 4, 20, 1};
68 // Num is the number of clip loads,
69 // not the individual count (0= 1/2 clip).
70 // Returns false if the ammo can't be picked up at all
81 if (ammo
== am_noammo
)
85 I_Error ("P_GiveAmmo: bad type %i", ammo
);
87 if ( player
->ammo
[ammo
] == player
->maxammo
[ammo
] )
91 num
*= clipammo
[ammo
];
93 num
= clipammo
[ammo
]/2;
95 if (gameskill
== sk_baby
96 || gameskill
== sk_nightmare
)
98 // give double ammo in trainer mode,
99 // you'll need in nightmare
104 oldammo
= player
->ammo
[ammo
];
105 player
->ammo
[ammo
] += num
;
107 if (player
->ammo
[ammo
] > player
->maxammo
[ammo
])
108 player
->ammo
[ammo
] = player
->maxammo
[ammo
];
111 // don't change up weapons,
112 // player was lower on purpose.
116 // We were down to zero,
117 // so select a new weapon.
118 // Preferences are not user selectable.
122 if (player
->readyweapon
== wp_fist
)
124 if (player
->weaponowned
[wp_chaingun
])
125 player
->pendingweapon
= wp_chaingun
;
127 player
->pendingweapon
= wp_pistol
;
132 if (player
->readyweapon
== wp_fist
133 || player
->readyweapon
== wp_pistol
)
135 if (player
->weaponowned
[wp_shotgun
])
136 player
->pendingweapon
= wp_shotgun
;
141 if (player
->readyweapon
== wp_fist
142 || player
->readyweapon
== wp_pistol
)
144 if (player
->weaponowned
[wp_plasma
])
145 player
->pendingweapon
= wp_plasma
;
150 if (player
->readyweapon
== wp_fist
)
152 if (player
->weaponowned
[wp_missile
])
153 player
->pendingweapon
= wp_missile
;
165 // The weapon name may have a MF_DROPPED flag ored in.
180 // leave placed weapons forever on net games
181 if (player
->weaponowned
[weapon
])
184 player
->bonuscount
+= BONUSADD
;
185 player
->weaponowned
[weapon
] = true;
188 P_GiveAmmo (player
, weaponinfo
[weapon
].ammo
, 5);
190 P_GiveAmmo (player
, weaponinfo
[weapon
].ammo
, 2);
191 player
->pendingweapon
= weapon
;
193 if (player
== &players
[consoleplayer
])
194 S_StartSound (NULL
, sfx_wpnup
);
198 if (weaponinfo
[weapon
].ammo
!= am_noammo
)
200 // give one clip with a dropped weapon,
201 // two clips with a found weapon
203 gaveammo
= P_GiveAmmo (player
, weaponinfo
[weapon
].ammo
, 1);
205 gaveammo
= P_GiveAmmo (player
, weaponinfo
[weapon
].ammo
, 2);
210 if (player
->weaponowned
[weapon
])
215 player
->weaponowned
[weapon
] = true;
216 player
->pendingweapon
= weapon
;
219 return (gaveweapon
|| gaveammo
);
226 // Returns false if the body isn't needed at all
233 if (player
->health
>= MAXHEALTH
)
236 player
->health
+= num
;
237 if (player
->health
> MAXHEALTH
)
238 player
->health
= MAXHEALTH
;
239 player
->mo
->health
= player
->health
;
248 // Returns false if the armor is worse
249 // than the current armor.
258 hits
= armortype
*100;
259 if (player
->armorpoints
>= hits
)
260 return false; // don't pick up
262 player
->armortype
= armortype
;
263 player
->armorpoints
= hits
;
278 if (player
->cards
[card
])
281 player
->bonuscount
= BONUSADD
;
282 player
->cards
[card
] = 1;
292 int /*powertype_t*/ power
)
294 if (power
== pw_invulnerability
)
296 player
->powers
[power
] = INVULNTICS
;
300 if (power
== pw_invisibility
)
302 player
->powers
[power
] = INVISTICS
;
303 player
->mo
->flags
|= MF_SHADOW
;
307 if (power
== pw_infrared
)
309 player
->powers
[power
] = INFRATICS
;
313 if (power
== pw_ironfeet
)
315 player
->powers
[power
] = IRONTICS
;
319 if (power
== pw_strength
)
321 P_GiveBody (player
, 100);
322 player
->powers
[power
] = 1;
326 if (player
->powers
[power
])
327 return false; // already got it
329 player
->powers
[power
] = 1;
336 // P_TouchSpecialThing
348 delta
= special
->z
- toucher
->z
;
350 if (delta
> toucher
->height
351 || delta
< -8*FRACUNIT
)
359 player
= toucher
->player
;
361 // Dead thing touching.
362 // Can happen with a sliding player corpse.
363 if (toucher
->health
<= 0)
366 // Identify by sprite.
367 switch (special
->sprite
)
371 if (!P_GiveArmor (player
, deh_green_armor_class
))
373 player
->message
= DEH_String(GOTARMOR
);
377 if (!P_GiveArmor (player
, deh_blue_armor_class
))
379 player
->message
= DEH_String(GOTMEGA
);
384 player
->health
++; // can go over 100%
385 if (player
->health
> deh_max_health
)
386 player
->health
= deh_max_health
;
387 player
->mo
->health
= player
->health
;
388 player
->message
= DEH_String(GOTHTHBONUS
);
392 player
->armorpoints
++; // can go over 100%
393 if (player
->armorpoints
> deh_max_armor
)
394 player
->armorpoints
= deh_max_armor
;
395 // deh_green_armor_class only applies to the green armor shirt;
396 // for the armor helmets, armortype 1 is always used.
397 if (!player
->armortype
)
398 player
->armortype
= 1;
399 player
->message
= DEH_String(GOTARMBONUS
);
403 player
->health
+= deh_soulsphere_health
;
404 if (player
->health
> deh_max_soulsphere
)
405 player
->health
= deh_max_soulsphere
;
406 player
->mo
->health
= player
->health
;
407 player
->message
= DEH_String(GOTSUPER
);
412 if (gamemode
!= commercial
)
414 player
->health
= deh_megasphere_health
;
415 player
->mo
->health
= player
->health
;
416 // We always give armor type 2 for the megasphere; dehacked only
417 // affects the MegaArmor.
418 P_GiveArmor (player
, 2);
419 player
->message
= DEH_String(GOTMSPHERE
);
424 // leave cards for everyone
426 if (!player
->cards
[it_bluecard
])
427 player
->message
= DEH_String(GOTBLUECARD
);
428 P_GiveCard (player
, it_bluecard
);
434 if (!player
->cards
[it_yellowcard
])
435 player
->message
= DEH_String(GOTYELWCARD
);
436 P_GiveCard (player
, it_yellowcard
);
442 if (!player
->cards
[it_redcard
])
443 player
->message
= DEH_String(GOTREDCARD
);
444 P_GiveCard (player
, it_redcard
);
450 if (!player
->cards
[it_blueskull
])
451 player
->message
= DEH_String(GOTBLUESKUL
);
452 P_GiveCard (player
, it_blueskull
);
458 if (!player
->cards
[it_yellowskull
])
459 player
->message
= DEH_String(GOTYELWSKUL
);
460 P_GiveCard (player
, it_yellowskull
);
466 if (!player
->cards
[it_redskull
])
467 player
->message
= DEH_String(GOTREDSKULL
);
468 P_GiveCard (player
, it_redskull
);
475 if (!P_GiveBody (player
, 10))
477 player
->message
= DEH_String(GOTSTIM
);
481 if (!P_GiveBody (player
, 25))
484 if (player
->health
< 25)
485 player
->message
= DEH_String(GOTMEDINEED
);
487 player
->message
= DEH_String(GOTMEDIKIT
);
493 if (!P_GivePower (player
, pw_invulnerability
))
495 player
->message
= DEH_String(GOTINVUL
);
500 if (!P_GivePower (player
, pw_strength
))
502 player
->message
= DEH_String(GOTBERSERK
);
503 if (player
->readyweapon
!= wp_fist
)
504 player
->pendingweapon
= wp_fist
;
509 if (!P_GivePower (player
, pw_invisibility
))
511 player
->message
= DEH_String(GOTINVIS
);
516 if (!P_GivePower (player
, pw_ironfeet
))
518 player
->message
= DEH_String(GOTSUIT
);
523 if (!P_GivePower (player
, pw_allmap
))
525 player
->message
= DEH_String(GOTMAP
);
530 if (!P_GivePower (player
, pw_infrared
))
532 player
->message
= DEH_String(GOTVISOR
);
538 if (special
->flags
& MF_DROPPED
)
540 if (!P_GiveAmmo (player
,am_clip
,0))
545 if (!P_GiveAmmo (player
,am_clip
,1))
548 player
->message
= DEH_String(GOTCLIP
);
552 if (!P_GiveAmmo (player
, am_clip
,5))
554 player
->message
= DEH_String(GOTCLIPBOX
);
558 if (!P_GiveAmmo (player
, am_misl
,1))
560 player
->message
= DEH_String(GOTROCKET
);
564 if (!P_GiveAmmo (player
, am_misl
,5))
566 player
->message
= DEH_String(GOTROCKBOX
);
570 if (!P_GiveAmmo (player
, am_cell
,1))
572 player
->message
= DEH_String(GOTCELL
);
576 if (!P_GiveAmmo (player
, am_cell
,5))
578 player
->message
= DEH_String(GOTCELLBOX
);
582 if (!P_GiveAmmo (player
, am_shell
,1))
584 player
->message
= DEH_String(GOTSHELLS
);
588 if (!P_GiveAmmo (player
, am_shell
,5))
590 player
->message
= DEH_String(GOTSHELLBOX
);
594 if (!player
->backpack
)
596 for (i
=0 ; i
<NUMAMMO
; i
++)
597 player
->maxammo
[i
] *= 2;
598 player
->backpack
= true;
600 for (i
=0 ; i
<NUMAMMO
; i
++)
601 P_GiveAmmo (player
, i
, 1);
602 player
->message
= DEH_String(GOTBACKPACK
);
607 if (!P_GiveWeapon (player
, wp_bfg
, false) )
609 player
->message
= DEH_String(GOTBFG9000
);
614 if (!P_GiveWeapon (player
, wp_chaingun
, special
->flags
&MF_DROPPED
) )
616 player
->message
= DEH_String(GOTCHAINGUN
);
621 if (!P_GiveWeapon (player
, wp_chainsaw
, false) )
623 player
->message
= DEH_String(GOTCHAINSAW
);
628 if (!P_GiveWeapon (player
, wp_missile
, false) )
630 player
->message
= DEH_String(GOTLAUNCHER
);
635 if (!P_GiveWeapon (player
, wp_plasma
, false) )
637 player
->message
= DEH_String(GOTPLASMA
);
642 if (!P_GiveWeapon (player
, wp_shotgun
, special
->flags
&MF_DROPPED
) )
644 player
->message
= DEH_String(GOTSHOTGUN
);
649 if (!P_GiveWeapon (player
, wp_supershotgun
, special
->flags
&MF_DROPPED
) )
651 player
->message
= DEH_String(GOTSHOTGUN2
);
656 I_Error ("P_SpecialThing: Unknown gettable thing");
659 if (special
->flags
& MF_COUNTITEM
)
661 P_RemoveMobj (special
);
662 player
->bonuscount
+= BONUSADD
;
663 if (player
== &players
[consoleplayer
])
664 S_StartSound (NULL
, sound
);
679 target
->flags
&= ~(MF_SHOOTABLE
|MF_FLOAT
|MF_SKULLFLY
);
681 if (target
->type
!= MT_SKULL
)
682 target
->flags
&= ~MF_NOGRAVITY
;
684 target
->flags
|= MF_CORPSE
|MF_DROPOFF
;
685 target
->height
>>= 2;
687 if (source
&& source
->player
)
689 // count for intermission
690 if (target
->flags
& MF_COUNTKILL
)
691 source
->player
->killcount
++;
694 source
->player
->frags
[target
->player
-players
]++;
696 else if (!netgame
&& (target
->flags
& MF_COUNTKILL
) )
698 // count all monster deaths,
699 // even those caused by other monsters
700 players
[0].killcount
++;
705 // count environment kills against you
707 target
->player
->frags
[target
->player
-players
]++;
709 target
->flags
&= ~MF_SOLID
;
710 target
->player
->playerstate
= PST_DEAD
;
711 P_DropWeapon (target
->player
);
713 if (target
->player
== &players
[consoleplayer
]
716 // don't die in auto map,
717 // switch view prior to dying
723 if (target
->health
< -target
->info
->spawnhealth
724 && target
->info
->xdeathstate
)
726 P_SetMobjState (target
, target
->info
->xdeathstate
);
729 P_SetMobjState (target
, target
->info
->deathstate
);
730 target
->tics
-= P_Random()&3;
732 if (target
->tics
< 1)
735 // I_StartSound (&actor->r, actor->info->deathsound);
737 // In Chex Quest, monsters don't drop items.
739 if (gameversion
== exe_chex
)
745 // This determines the kind of object spawned
746 // during the death frame of a thing.
747 switch (target
->type
)
766 mo
= P_SpawnMobj (target
->x
,target
->y
,ONFLOORZ
, item
);
767 mo
->flags
|= MF_DROPPED
; // special versions of items
775 // Damages both enemies and players
776 // "inflictor" is the thing that caused the damage
777 // creature or missile, can be NULL (slime, etc)
778 // "source" is the thing to target after taking damage
780 // Source and inflictor are the same for melee attacks.
781 // Source can be NULL for slime, barrel explosions
782 // and other environmental stuff.
797 if ( !(target
->flags
& MF_SHOOTABLE
) )
798 return; // shouldn't happen...
800 if (target
->health
<= 0)
803 if ( target
->flags
& MF_SKULLFLY
)
805 target
->momx
= target
->momy
= target
->momz
= 0;
808 player
= target
->player
;
809 if (player
&& gameskill
== sk_baby
)
810 damage
>>= 1; // take half damage in trainer mode
813 // Some close combat weapons should not
814 // inflict thrust and push the victim out of reach,
815 // thus kick away unless using the chainsaw.
817 && !(target
->flags
& MF_NOCLIP
)
820 || source
->player
->readyweapon
!= wp_chainsaw
))
822 ang
= R_PointToAngle2 ( inflictor
->x
,
827 thrust
= damage
*(FRACUNIT
>>3)*100/target
->info
->mass
;
829 // make fall forwards sometimes
831 && damage
> target
->health
832 && target
->z
- inflictor
->z
> 64*FRACUNIT
839 ang
>>= ANGLETOFINESHIFT
;
840 target
->momx
+= FixedMul (thrust
, finecosine
[ang
]);
841 target
->momy
+= FixedMul (thrust
, finesine
[ang
]);
847 // end of game hell hack
848 if (target
->subsector
->sector
->special
== 11
849 && damage
>= target
->health
)
851 damage
= target
->health
- 1;
855 // Below certain threshold,
856 // ignore damage in GOD mode, or with INVUL power.
858 && ( (player
->cheats
&CF_GODMODE
)
859 || player
->powers
[pw_invulnerability
] ) )
864 if (player
->armortype
)
866 if (player
->armortype
== 1)
871 if (player
->armorpoints
<= saved
)
874 saved
= player
->armorpoints
;
875 player
->armortype
= 0;
877 player
->armorpoints
-= saved
;
880 player
->health
-= damage
; // mirror mobj health here for Dave
881 if (player
->health
< 0)
884 player
->attacker
= source
;
885 player
->damagecount
+= damage
; // add damage after armor / invuln
887 if (player
->damagecount
> 100)
888 player
->damagecount
= 100; // teleport stomp does 10k points...
890 temp
= damage
< 100 ? damage
: 100;
892 if (player
== &players
[consoleplayer
])
893 I_Tactile (40,10,40+temp
*2);
897 target
->health
-= damage
;
898 if (target
->health
<= 0)
900 P_KillMobj (source
, target
);
904 if ( (P_Random () < target
->info
->painchance
)
905 && !(target
->flags
&MF_SKULLFLY
) )
907 target
->flags
|= MF_JUSTHIT
; // fight back!
909 P_SetMobjState (target
, target
->info
->painstate
);
912 target
->reactiontime
= 0; // we're awake now...
914 if ( (!target
->threshold
|| target
->type
== MT_VILE
)
915 && source
&& source
!= target
916 && source
->type
!= MT_VILE
)
918 // if not intent on another player,
919 // chase after this one
920 target
->target
= source
;
921 target
->threshold
= BASETHRESHOLD
;
922 if (target
->state
== &states
[target
->info
->spawnstate
]
923 && target
->info
->seestate
!= S_NULL
)
924 P_SetMobjState (target
, target
->info
->seestate
);