Add key bindings for pause, message refresh.
[chocolate-doom.git] / src / p_inter.c
blobdda55c37a57c42705666148f1102cb08da4330b6
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
6 //
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
20 // 02111-1307, USA.
22 // DESCRIPTION:
23 // Handling interactions (i.e., collisions).
25 //-----------------------------------------------------------------------------
30 // Data.
31 #include "doomdef.h"
32 #include "dstrings.h"
33 #include "sounds.h"
35 #include "deh_main.h"
36 #include "deh_misc.h"
37 #include "doomstat.h"
39 #include "m_random.h"
40 #include "i_system.h"
42 #include "am_map.h"
44 #include "p_local.h"
46 #include "s_sound.h"
48 #include "p_inter.h"
51 #define BONUSADD 6
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};
63 // GET STUFF
67 // P_GiveAmmo
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
73 boolean
74 P_GiveAmmo
75 ( player_t* player,
76 ammotype_t ammo,
77 int num )
79 int oldammo;
81 if (ammo == am_noammo)
82 return false;
84 if (ammo > NUMAMMO)
85 I_Error ("P_GiveAmmo: bad type %i", ammo);
87 if ( player->ammo[ammo] == player->maxammo[ammo] )
88 return false;
90 if (num)
91 num *= clipammo[ammo];
92 else
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
100 num <<= 1;
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];
110 // If non zero ammo,
111 // don't change up weapons,
112 // player was lower on purpose.
113 if (oldammo)
114 return true;
116 // We were down to zero,
117 // so select a new weapon.
118 // Preferences are not user selectable.
119 switch (ammo)
121 case am_clip:
122 if (player->readyweapon == wp_fist)
124 if (player->weaponowned[wp_chaingun])
125 player->pendingweapon = wp_chaingun;
126 else
127 player->pendingweapon = wp_pistol;
129 break;
131 case am_shell:
132 if (player->readyweapon == wp_fist
133 || player->readyweapon == wp_pistol)
135 if (player->weaponowned[wp_shotgun])
136 player->pendingweapon = wp_shotgun;
138 break;
140 case am_cell:
141 if (player->readyweapon == wp_fist
142 || player->readyweapon == wp_pistol)
144 if (player->weaponowned[wp_plasma])
145 player->pendingweapon = wp_plasma;
147 break;
149 case am_misl:
150 if (player->readyweapon == wp_fist)
152 if (player->weaponowned[wp_missile])
153 player->pendingweapon = wp_missile;
155 default:
156 break;
159 return true;
164 // P_GiveWeapon
165 // The weapon name may have a MF_DROPPED flag ored in.
167 boolean
168 P_GiveWeapon
169 ( player_t* player,
170 weapontype_t weapon,
171 boolean dropped )
173 boolean gaveammo;
174 boolean gaveweapon;
176 if (netgame
177 && (deathmatch!=2)
178 && !dropped )
180 // leave placed weapons forever on net games
181 if (player->weaponowned[weapon])
182 return false;
184 player->bonuscount += BONUSADD;
185 player->weaponowned[weapon] = true;
187 if (deathmatch)
188 P_GiveAmmo (player, weaponinfo[weapon].ammo, 5);
189 else
190 P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
191 player->pendingweapon = weapon;
193 if (player == &players[consoleplayer])
194 S_StartSound (NULL, sfx_wpnup);
195 return false;
198 if (weaponinfo[weapon].ammo != am_noammo)
200 // give one clip with a dropped weapon,
201 // two clips with a found weapon
202 if (dropped)
203 gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
204 else
205 gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
207 else
208 gaveammo = false;
210 if (player->weaponowned[weapon])
211 gaveweapon = false;
212 else
214 gaveweapon = true;
215 player->weaponowned[weapon] = true;
216 player->pendingweapon = weapon;
219 return (gaveweapon || gaveammo);
225 // P_GiveBody
226 // Returns false if the body isn't needed at all
228 boolean
229 P_GiveBody
230 ( player_t* player,
231 int num )
233 if (player->health >= MAXHEALTH)
234 return false;
236 player->health += num;
237 if (player->health > MAXHEALTH)
238 player->health = MAXHEALTH;
239 player->mo->health = player->health;
241 return true;
247 // P_GiveArmor
248 // Returns false if the armor is worse
249 // than the current armor.
251 boolean
252 P_GiveArmor
253 ( player_t* player,
254 int armortype )
256 int hits;
258 hits = armortype*100;
259 if (player->armorpoints >= hits)
260 return false; // don't pick up
262 player->armortype = armortype;
263 player->armorpoints = hits;
265 return true;
271 // P_GiveCard
273 void
274 P_GiveCard
275 ( player_t* player,
276 card_t card )
278 if (player->cards[card])
279 return;
281 player->bonuscount = BONUSADD;
282 player->cards[card] = 1;
287 // P_GivePower
289 boolean
290 P_GivePower
291 ( player_t* player,
292 int /*powertype_t*/ power )
294 if (power == pw_invulnerability)
296 player->powers[power] = INVULNTICS;
297 return true;
300 if (power == pw_invisibility)
302 player->powers[power] = INVISTICS;
303 player->mo->flags |= MF_SHADOW;
304 return true;
307 if (power == pw_infrared)
309 player->powers[power] = INFRATICS;
310 return true;
313 if (power == pw_ironfeet)
315 player->powers[power] = IRONTICS;
316 return true;
319 if (power == pw_strength)
321 P_GiveBody (player, 100);
322 player->powers[power] = 1;
323 return true;
326 if (player->powers[power])
327 return false; // already got it
329 player->powers[power] = 1;
330 return true;
336 // P_TouchSpecialThing
338 void
339 P_TouchSpecialThing
340 ( mobj_t* special,
341 mobj_t* toucher )
343 player_t* player;
344 int i;
345 fixed_t delta;
346 int sound;
348 delta = special->z - toucher->z;
350 if (delta > toucher->height
351 || delta < -8*FRACUNIT)
353 // out of reach
354 return;
358 sound = sfx_itemup;
359 player = toucher->player;
361 // Dead thing touching.
362 // Can happen with a sliding player corpse.
363 if (toucher->health <= 0)
364 return;
366 // Identify by sprite.
367 switch (special->sprite)
369 // armor
370 case SPR_ARM1:
371 if (!P_GiveArmor (player, deh_green_armor_class))
372 return;
373 player->message = DEH_String(GOTARMOR);
374 break;
376 case SPR_ARM2:
377 if (!P_GiveArmor (player, deh_blue_armor_class))
378 return;
379 player->message = DEH_String(GOTMEGA);
380 break;
382 // bonus items
383 case SPR_BON1:
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);
389 break;
391 case SPR_BON2:
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);
400 break;
402 case SPR_SOUL:
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);
408 sound = sfx_getpow;
409 break;
411 case SPR_MEGA:
412 if (gamemode != commercial)
413 return;
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);
420 sound = sfx_getpow;
421 break;
423 // cards
424 // leave cards for everyone
425 case SPR_BKEY:
426 if (!player->cards[it_bluecard])
427 player->message = DEH_String(GOTBLUECARD);
428 P_GiveCard (player, it_bluecard);
429 if (!netgame)
430 break;
431 return;
433 case SPR_YKEY:
434 if (!player->cards[it_yellowcard])
435 player->message = DEH_String(GOTYELWCARD);
436 P_GiveCard (player, it_yellowcard);
437 if (!netgame)
438 break;
439 return;
441 case SPR_RKEY:
442 if (!player->cards[it_redcard])
443 player->message = DEH_String(GOTREDCARD);
444 P_GiveCard (player, it_redcard);
445 if (!netgame)
446 break;
447 return;
449 case SPR_BSKU:
450 if (!player->cards[it_blueskull])
451 player->message = DEH_String(GOTBLUESKUL);
452 P_GiveCard (player, it_blueskull);
453 if (!netgame)
454 break;
455 return;
457 case SPR_YSKU:
458 if (!player->cards[it_yellowskull])
459 player->message = DEH_String(GOTYELWSKUL);
460 P_GiveCard (player, it_yellowskull);
461 if (!netgame)
462 break;
463 return;
465 case SPR_RSKU:
466 if (!player->cards[it_redskull])
467 player->message = DEH_String(GOTREDSKULL);
468 P_GiveCard (player, it_redskull);
469 if (!netgame)
470 break;
471 return;
473 // medikits, heals
474 case SPR_STIM:
475 if (!P_GiveBody (player, 10))
476 return;
477 player->message = DEH_String(GOTSTIM);
478 break;
480 case SPR_MEDI:
481 if (!P_GiveBody (player, 25))
482 return;
484 if (player->health < 25)
485 player->message = DEH_String(GOTMEDINEED);
486 else
487 player->message = DEH_String(GOTMEDIKIT);
488 break;
491 // power ups
492 case SPR_PINV:
493 if (!P_GivePower (player, pw_invulnerability))
494 return;
495 player->message = DEH_String(GOTINVUL);
496 sound = sfx_getpow;
497 break;
499 case SPR_PSTR:
500 if (!P_GivePower (player, pw_strength))
501 return;
502 player->message = DEH_String(GOTBERSERK);
503 if (player->readyweapon != wp_fist)
504 player->pendingweapon = wp_fist;
505 sound = sfx_getpow;
506 break;
508 case SPR_PINS:
509 if (!P_GivePower (player, pw_invisibility))
510 return;
511 player->message = DEH_String(GOTINVIS);
512 sound = sfx_getpow;
513 break;
515 case SPR_SUIT:
516 if (!P_GivePower (player, pw_ironfeet))
517 return;
518 player->message = DEH_String(GOTSUIT);
519 sound = sfx_getpow;
520 break;
522 case SPR_PMAP:
523 if (!P_GivePower (player, pw_allmap))
524 return;
525 player->message = DEH_String(GOTMAP);
526 sound = sfx_getpow;
527 break;
529 case SPR_PVIS:
530 if (!P_GivePower (player, pw_infrared))
531 return;
532 player->message = DEH_String(GOTVISOR);
533 sound = sfx_getpow;
534 break;
536 // ammo
537 case SPR_CLIP:
538 if (special->flags & MF_DROPPED)
540 if (!P_GiveAmmo (player,am_clip,0))
541 return;
543 else
545 if (!P_GiveAmmo (player,am_clip,1))
546 return;
548 player->message = DEH_String(GOTCLIP);
549 break;
551 case SPR_AMMO:
552 if (!P_GiveAmmo (player, am_clip,5))
553 return;
554 player->message = DEH_String(GOTCLIPBOX);
555 break;
557 case SPR_ROCK:
558 if (!P_GiveAmmo (player, am_misl,1))
559 return;
560 player->message = DEH_String(GOTROCKET);
561 break;
563 case SPR_BROK:
564 if (!P_GiveAmmo (player, am_misl,5))
565 return;
566 player->message = DEH_String(GOTROCKBOX);
567 break;
569 case SPR_CELL:
570 if (!P_GiveAmmo (player, am_cell,1))
571 return;
572 player->message = DEH_String(GOTCELL);
573 break;
575 case SPR_CELP:
576 if (!P_GiveAmmo (player, am_cell,5))
577 return;
578 player->message = DEH_String(GOTCELLBOX);
579 break;
581 case SPR_SHEL:
582 if (!P_GiveAmmo (player, am_shell,1))
583 return;
584 player->message = DEH_String(GOTSHELLS);
585 break;
587 case SPR_SBOX:
588 if (!P_GiveAmmo (player, am_shell,5))
589 return;
590 player->message = DEH_String(GOTSHELLBOX);
591 break;
593 case SPR_BPAK:
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);
603 break;
605 // weapons
606 case SPR_BFUG:
607 if (!P_GiveWeapon (player, wp_bfg, false) )
608 return;
609 player->message = DEH_String(GOTBFG9000);
610 sound = sfx_wpnup;
611 break;
613 case SPR_MGUN:
614 if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) )
615 return;
616 player->message = DEH_String(GOTCHAINGUN);
617 sound = sfx_wpnup;
618 break;
620 case SPR_CSAW:
621 if (!P_GiveWeapon (player, wp_chainsaw, false) )
622 return;
623 player->message = DEH_String(GOTCHAINSAW);
624 sound = sfx_wpnup;
625 break;
627 case SPR_LAUN:
628 if (!P_GiveWeapon (player, wp_missile, false) )
629 return;
630 player->message = DEH_String(GOTLAUNCHER);
631 sound = sfx_wpnup;
632 break;
634 case SPR_PLAS:
635 if (!P_GiveWeapon (player, wp_plasma, false) )
636 return;
637 player->message = DEH_String(GOTPLASMA);
638 sound = sfx_wpnup;
639 break;
641 case SPR_SHOT:
642 if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) )
643 return;
644 player->message = DEH_String(GOTSHOTGUN);
645 sound = sfx_wpnup;
646 break;
648 case SPR_SGN2:
649 if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) )
650 return;
651 player->message = DEH_String(GOTSHOTGUN2);
652 sound = sfx_wpnup;
653 break;
655 default:
656 I_Error ("P_SpecialThing: Unknown gettable thing");
659 if (special->flags & MF_COUNTITEM)
660 player->itemcount++;
661 P_RemoveMobj (special);
662 player->bonuscount += BONUSADD;
663 if (player == &players[consoleplayer])
664 S_StartSound (NULL, sound);
669 // KillMobj
671 void
672 P_KillMobj
673 ( mobj_t* source,
674 mobj_t* target )
676 mobjtype_t item;
677 mobj_t* mo;
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++;
693 if (target->player)
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++;
703 if (target->player)
705 // count environment kills against you
706 if (!source)
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]
714 && automapactive)
716 // don't die in auto map,
717 // switch view prior to dying
718 AM_Stop ();
723 if (target->health < -target->info->spawnhealth
724 && target->info->xdeathstate)
726 P_SetMobjState (target, target->info->xdeathstate);
728 else
729 P_SetMobjState (target, target->info->deathstate);
730 target->tics -= P_Random()&3;
732 if (target->tics < 1)
733 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)
741 return;
744 // Drop stuff.
745 // This determines the kind of object spawned
746 // during the death frame of a thing.
747 switch (target->type)
749 case MT_WOLFSS:
750 case MT_POSSESSED:
751 item = MT_CLIP;
752 break;
754 case MT_SHOTGUY:
755 item = MT_SHOTGUN;
756 break;
758 case MT_CHAINGUY:
759 item = MT_CHAINGUN;
760 break;
762 default:
763 return;
766 mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
767 mo->flags |= MF_DROPPED; // special versions of items
774 // P_DamageMobj
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
779 // creature or NULL
780 // Source and inflictor are the same for melee attacks.
781 // Source can be NULL for slime, barrel explosions
782 // and other environmental stuff.
784 void
785 P_DamageMobj
786 ( mobj_t* target,
787 mobj_t* inflictor,
788 mobj_t* source,
789 int damage )
791 unsigned ang;
792 int saved;
793 player_t* player;
794 fixed_t thrust;
795 int temp;
797 if ( !(target->flags & MF_SHOOTABLE) )
798 return; // shouldn't happen...
800 if (target->health <= 0)
801 return;
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.
816 if (inflictor
817 && !(target->flags & MF_NOCLIP)
818 && (!source
819 || !source->player
820 || source->player->readyweapon != wp_chainsaw))
822 ang = R_PointToAngle2 ( inflictor->x,
823 inflictor->y,
824 target->x,
825 target->y);
827 thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
829 // make fall forwards sometimes
830 if ( damage < 40
831 && damage > target->health
832 && target->z - inflictor->z > 64*FRACUNIT
833 && (P_Random ()&1) )
835 ang += ANG180;
836 thrust *= 4;
839 ang >>= ANGLETOFINESHIFT;
840 target->momx += FixedMul (thrust, finecosine[ang]);
841 target->momy += FixedMul (thrust, finesine[ang]);
844 // player specific
845 if (player)
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.
857 if ( damage < 1000
858 && ( (player->cheats&CF_GODMODE)
859 || player->powers[pw_invulnerability] ) )
861 return;
864 if (player->armortype)
866 if (player->armortype == 1)
867 saved = damage/3;
868 else
869 saved = damage/2;
871 if (player->armorpoints <= saved)
873 // armor is used up
874 saved = player->armorpoints;
875 player->armortype = 0;
877 player->armorpoints -= saved;
878 damage -= saved;
880 player->health -= damage; // mirror mobj health here for Dave
881 if (player->health < 0)
882 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);
896 // do the damage
897 target->health -= damage;
898 if (target->health <= 0)
900 P_KillMobj (source, target);
901 return;
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);