Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. // Emacs style mode select   -*- C++ -*-
  2. //-----------------------------------------------------------------------------
  3. //
  4. // $Id:$
  5. //
  6. // Copyright (C) 1993-1996 by id Software, Inc.
  7. //
  8. // This source is available for distribution and/or modification
  9. // only under the terms of the DOOM Source Code License as
  10. // published by id Software. All rights reserved.
  11. //
  12. // The source is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
  15. // for more details.
  16. //
  17. // $Log:$
  18. //
  19. // DESCRIPTION:
  20. //      Handling interactions (i.e., collisions).
  21. //
  22. //-----------------------------------------------------------------------------
  23.  
  24.  
  25. static const char
  26. rcsid[] = "$Id: p_inter.c,v 1.4 1997/02/03 22:45:11 b1 Exp $";
  27.  
  28.  
  29. // Data.
  30. #include "doomdef.h"
  31. #include "dstrings.h"
  32. #include "sounds.h"
  33.  
  34. #include "doomstat.h"
  35.  
  36. #include "m_random.h"
  37. #include "i_system.h"
  38.  
  39. #include "am_map.h"
  40.  
  41. #include "p_local.h"
  42.  
  43. #include "s_sound.h"
  44.  
  45. #ifdef __GNUG__
  46. #pragma implementation "p_inter.h"
  47. #endif
  48. #include "p_inter.h"
  49.  
  50.  
  51. #define BONUSADD        6
  52.  
  53.  
  54.  
  55.  
  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};
  60.  
  61.  
  62. //
  63. // GET STUFF
  64. //
  65.  
  66. //
  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
  71. //
  72.  
  73. boolean
  74. P_GiveAmmo
  75. ( player_t*     player,
  76.   ammotype_t    ammo,
  77.   int           num )
  78. {
  79.     int         oldammo;
  80.        
  81.     if (ammo == am_noammo)
  82.         return false;
  83.                
  84.     if (ammo < 0 || ammo > NUMAMMO)
  85.         I_Error ("P_GiveAmmo: bad type %i", ammo);
  86.                
  87.     if ( player->ammo[ammo] == player->maxammo[ammo]  )
  88.         return false;
  89.                
  90.     if (num)
  91.         num *= clipammo[ammo];
  92.     else
  93.         num = clipammo[ammo]/2;
  94.    
  95.     if (gameskill == sk_baby
  96.         || gameskill == sk_nightmare)
  97.     {
  98.         // give double ammo in trainer mode,
  99.         // you'll need in nightmare
  100.         num <<= 1;
  101.     }
  102.    
  103.                
  104.     oldammo = player->ammo[ammo];
  105.     player->ammo[ammo] += num;
  106.  
  107.     if (player->ammo[ammo] > player->maxammo[ammo])
  108.         player->ammo[ammo] = player->maxammo[ammo];
  109.  
  110.     // If non zero ammo,
  111.     // don't change up weapons,
  112.     // player was lower on purpose.
  113.     if (oldammo)
  114.         return true;   
  115.  
  116.     // We were down to zero,
  117.     // so select a new weapon.
  118.     // Preferences are not user selectable.
  119.     switch (ammo)
  120.     {
  121.       case am_clip:
  122.         if (player->readyweapon == wp_fist)
  123.         {
  124.             if (player->weaponowned[wp_chaingun])
  125.                 player->pendingweapon = wp_chaingun;
  126.             else
  127.                 player->pendingweapon = wp_pistol;
  128.         }
  129.         break;
  130.        
  131.       case am_shell:
  132.         if (player->readyweapon == wp_fist
  133.             || player->readyweapon == wp_pistol)
  134.         {
  135.             if (player->weaponowned[wp_shotgun])
  136.                 player->pendingweapon = wp_shotgun;
  137.         }
  138.         break;
  139.        
  140.       case am_cell:
  141.         if (player->readyweapon == wp_fist
  142.             || player->readyweapon == wp_pistol)
  143.         {
  144.             if (player->weaponowned[wp_plasma])
  145.                 player->pendingweapon = wp_plasma;
  146.         }
  147.         break;
  148.        
  149.       case am_misl:
  150.         if (player->readyweapon == wp_fist)
  151.         {
  152.             if (player->weaponowned[wp_missile])
  153.                 player->pendingweapon = wp_missile;
  154.         }
  155.       default:
  156.         break;
  157.     }
  158.        
  159.     return true;
  160. }
  161.  
  162.  
  163. //
  164. // P_GiveWeapon
  165. // The weapon name may have a MF_DROPPED flag ored in.
  166. //
  167. boolean
  168. P_GiveWeapon
  169. ( player_t*     player,
  170.   weapontype_t  weapon,
  171.   boolean       dropped )
  172. {
  173.     boolean     gaveammo;
  174.     boolean     gaveweapon;
  175.        
  176.     if (netgame
  177.         && (deathmatch!=2)
  178.          && !dropped )
  179.     {
  180.         // leave placed weapons forever on net games
  181.         if (player->weaponowned[weapon])
  182.             return false;
  183.  
  184.         player->bonuscount += BONUSADD;
  185.         player->weaponowned[weapon] = true;
  186.  
  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;
  192.  
  193.         if (player == &players[consoleplayer])
  194.             S_StartSound (NULL, sfx_wpnup);
  195.         return false;
  196.     }
  197.        
  198.     if (weaponinfo[weapon].ammo != am_noammo)
  199.     {
  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);
  206.     }
  207.     else
  208.         gaveammo = false;
  209.        
  210.     if (player->weaponowned[weapon])
  211.         gaveweapon = false;
  212.     else
  213.     {
  214.         gaveweapon = true;
  215.         player->weaponowned[weapon] = true;
  216.         player->pendingweapon = weapon;
  217.     }
  218.        
  219.     return (gaveweapon || gaveammo);
  220. }
  221.  
  222.  
  223.  
  224. //
  225. // P_GiveBody
  226. // Returns false if the body isn't needed at all
  227. //
  228. boolean
  229. P_GiveBody
  230. ( player_t*     player,
  231.   int           num )
  232. {
  233.     if (player->health >= MAXHEALTH)
  234.         return false;
  235.                
  236.     player->health += num;
  237.     if (player->health > MAXHEALTH)
  238.         player->health = MAXHEALTH;
  239.     player->mo->health = player->health;
  240.        
  241.     return true;
  242. }
  243.  
  244.  
  245.  
  246. //
  247. // P_GiveArmor
  248. // Returns false if the armor is worse
  249. // than the current armor.
  250. //
  251. boolean
  252. P_GiveArmor
  253. ( player_t*     player,
  254.   int           armortype )
  255. {
  256.     int         hits;
  257.        
  258.     hits = armortype*100;
  259.     if (player->armorpoints >= hits)
  260.         return false;   // don't pick up
  261.                
  262.     player->armortype = armortype;
  263.     player->armorpoints = hits;
  264.        
  265.     return true;
  266. }
  267.  
  268.  
  269.  
  270. //
  271. // P_GiveCard
  272. //
  273. void
  274. P_GiveCard
  275. ( player_t*     player,
  276.   card_t        card )
  277. {
  278.     if (player->cards[card])
  279.         return;
  280.    
  281.     player->bonuscount = BONUSADD;
  282.     player->cards[card] = 1;
  283. }
  284.  
  285.  
  286. //
  287. // P_GivePower
  288. //
  289. boolean
  290. P_GivePower
  291. ( player_t*     player,
  292.   int /*powertype_t*/   power )
  293. {
  294.     if (power == pw_invulnerability)
  295.     {
  296.         player->powers[power] = INVULNTICS;
  297.         return true;
  298.     }
  299.    
  300.     if (power == pw_invisibility)
  301.     {
  302.         player->powers[power] = INVISTICS;
  303.         player->mo->flags |= MF_SHADOW;
  304.         return true;
  305.     }
  306.    
  307.     if (power == pw_infrared)
  308.     {
  309.         player->powers[power] = INFRATICS;
  310.         return true;
  311.     }
  312.    
  313.     if (power == pw_ironfeet)
  314.     {
  315.         player->powers[power] = IRONTICS;
  316.         return true;
  317.     }
  318.    
  319.     if (power == pw_strength)
  320.     {
  321.         P_GiveBody (player, 100);
  322.         player->powers[power] = 1;
  323.         return true;
  324.     }
  325.        
  326.     if (player->powers[power])
  327.         return false;   // already got it
  328.                
  329.     player->powers[power] = 1;
  330.     return true;
  331. }
  332.  
  333.  
  334.  
  335. //
  336. // P_TouchSpecialThing
  337. //
  338. void
  339. P_TouchSpecialThing
  340. ( mobj_t*       special,
  341.   mobj_t*       toucher )
  342. {
  343.     player_t*   player;
  344.     int         i;
  345.     fixed_t     delta;
  346.     int         sound;
  347.                
  348.     delta = special->z - toucher->z;
  349.  
  350.     if (delta > toucher->height
  351.         || delta < -8*FRACUNIT)
  352.     {
  353.         // out of reach
  354.         return;
  355.     }
  356.    
  357.        
  358.     sound = sfx_itemup;
  359.     player = toucher->player;
  360.  
  361.     // Dead thing touching.
  362.     // Can happen with a sliding player corpse.
  363.     if (toucher->health <= 0)
  364.         return;
  365.  
  366.     // Identify by sprite.
  367.     switch (special->sprite)
  368.     {
  369.         // armor
  370.       case SPR_ARM1:
  371.         if (!P_GiveArmor (player, 1))
  372.             return;
  373.         player->message = GOTARMOR;
  374.         break;
  375.                
  376.       case SPR_ARM2:
  377.         if (!P_GiveArmor (player, 2))
  378.             return;
  379.         player->message = GOTMEGA;
  380.         break;
  381.        
  382.         // bonus items
  383.       case SPR_BON1:
  384.         player->health++;               // can go over 100%
  385.         if (player->health > 200)
  386.             player->health = 200;
  387.         player->mo->health = player->health;
  388.         player->message = GOTHTHBONUS;
  389.         break;
  390.        
  391.       case SPR_BON2:
  392.         player->armorpoints++;          // can go over 100%
  393.         if (player->armorpoints > 200)
  394.             player->armorpoints = 200;
  395.         if (!player->armortype)
  396.             player->armortype = 1;
  397.         player->message = GOTARMBONUS;
  398.         break;
  399.        
  400.       case SPR_SOUL:
  401.         player->health += 100;
  402.         if (player->health > 200)
  403.             player->health = 200;
  404.         player->mo->health = player->health;
  405.         player->message = GOTSUPER;
  406.         sound = sfx_getpow;
  407.         break;
  408.        
  409.       case SPR_MEGA:
  410.         if (gamemode != commercial)
  411.             return;
  412.         player->health = 200;
  413.         player->mo->health = player->health;
  414.         P_GiveArmor (player,2);
  415.         player->message = GOTMSPHERE;
  416.         sound = sfx_getpow;
  417.         break;
  418.        
  419.         // cards
  420.         // leave cards for everyone
  421.       case SPR_BKEY:
  422.         if (!player->cards[it_bluecard])
  423.             player->message = GOTBLUECARD;
  424.         P_GiveCard (player, it_bluecard);
  425.         if (!netgame)
  426.             break;
  427.         return;
  428.        
  429.       case SPR_YKEY:
  430.         if (!player->cards[it_yellowcard])
  431.             player->message = GOTYELWCARD;
  432.         P_GiveCard (player, it_yellowcard);
  433.         if (!netgame)
  434.             break;
  435.         return;
  436.        
  437.       case SPR_RKEY:
  438.         if (!player->cards[it_redcard])
  439.             player->message = GOTREDCARD;
  440.         P_GiveCard (player, it_redcard);
  441.         if (!netgame)
  442.             break;
  443.         return;
  444.        
  445.       case SPR_BSKU:
  446.         if (!player->cards[it_blueskull])
  447.             player->message = GOTBLUESKUL;
  448.         P_GiveCard (player, it_blueskull);
  449.         if (!netgame)
  450.             break;
  451.         return;
  452.        
  453.       case SPR_YSKU:
  454.         if (!player->cards[it_yellowskull])
  455.             player->message = GOTYELWSKUL;
  456.         P_GiveCard (player, it_yellowskull);
  457.         if (!netgame)
  458.             break;
  459.         return;
  460.        
  461.       case SPR_RSKU:
  462.         if (!player->cards[it_redskull])
  463.             player->message = GOTREDSKULL;
  464.         P_GiveCard (player, it_redskull);
  465.         if (!netgame)
  466.             break;
  467.         return;
  468.        
  469.         // medikits, heals
  470.       case SPR_STIM:
  471.         if (!P_GiveBody (player, 10))
  472.             return;
  473.         player->message = GOTSTIM;
  474.         break;
  475.        
  476.       case SPR_MEDI:
  477.         if (!P_GiveBody (player, 25))
  478.             return;
  479.  
  480.         if (player->health < 25)
  481.             player->message = GOTMEDINEED;
  482.         else
  483.             player->message = GOTMEDIKIT;
  484.         break;
  485.  
  486.        
  487.         // power ups
  488.       case SPR_PINV:
  489.         if (!P_GivePower (player, pw_invulnerability))
  490.             return;
  491.         player->message = GOTINVUL;
  492.         sound = sfx_getpow;
  493.         break;
  494.        
  495.       case SPR_PSTR:
  496.         if (!P_GivePower (player, pw_strength))
  497.             return;
  498.         player->message = GOTBERSERK;
  499.         if (player->readyweapon != wp_fist)
  500.             player->pendingweapon = wp_fist;
  501.         sound = sfx_getpow;
  502.         break;
  503.        
  504.       case SPR_PINS:
  505.         if (!P_GivePower (player, pw_invisibility))
  506.             return;
  507.         player->message = GOTINVIS;
  508.         sound = sfx_getpow;
  509.         break;
  510.        
  511.       case SPR_SUIT:
  512.         if (!P_GivePower (player, pw_ironfeet))
  513.             return;
  514.         player->message = GOTSUIT;
  515.         sound = sfx_getpow;
  516.         break;
  517.        
  518.       case SPR_PMAP:
  519.         if (!P_GivePower (player, pw_allmap))
  520.             return;
  521.         player->message = GOTMAP;
  522.         sound = sfx_getpow;
  523.         break;
  524.        
  525.       case SPR_PVIS:
  526.         if (!P_GivePower (player, pw_infrared))
  527.             return;
  528.         player->message = GOTVISOR;
  529.         sound = sfx_getpow;
  530.         break;
  531.        
  532.         // ammo
  533.       case SPR_CLIP:
  534.         if (special->flags & MF_DROPPED)
  535.         {
  536.             if (!P_GiveAmmo (player,am_clip,0))
  537.                 return;
  538.         }
  539.         else
  540.         {
  541.             if (!P_GiveAmmo (player,am_clip,1))
  542.                 return;
  543.         }
  544.         player->message = GOTCLIP;
  545.         break;
  546.        
  547.       case SPR_AMMO:
  548.         if (!P_GiveAmmo (player, am_clip,5))
  549.             return;
  550.         player->message = GOTCLIPBOX;
  551.         break;
  552.        
  553.       case SPR_ROCK:
  554.         if (!P_GiveAmmo (player, am_misl,1))
  555.             return;
  556.         player->message = GOTROCKET;
  557.         break;
  558.        
  559.       case SPR_BROK:
  560.         if (!P_GiveAmmo (player, am_misl,5))
  561.             return;
  562.         player->message = GOTROCKBOX;
  563.         break;
  564.        
  565.       case SPR_CELL:
  566.         if (!P_GiveAmmo (player, am_cell,1))
  567.             return;
  568.         player->message = GOTCELL;
  569.         break;
  570.        
  571.       case SPR_CELP:
  572.         if (!P_GiveAmmo (player, am_cell,5))
  573.             return;
  574.         player->message = GOTCELLBOX;
  575.         break;
  576.        
  577.       case SPR_SHEL:
  578.         if (!P_GiveAmmo (player, am_shell,1))
  579.             return;
  580.         player->message = GOTSHELLS;
  581.         break;
  582.        
  583.       case SPR_SBOX:
  584.         if (!P_GiveAmmo (player, am_shell,5))
  585.             return;
  586.         player->message = GOTSHELLBOX;
  587.         break;
  588.        
  589.       case SPR_BPAK:
  590.         if (!player->backpack)
  591.         {
  592.             for (i=0 ; i<NUMAMMO ; i++)
  593.                 player->maxammo[i] *= 2;
  594.             player->backpack = true;
  595.         }
  596.         for (i=0 ; i<NUMAMMO ; i++)
  597.             P_GiveAmmo (player, i, 1);
  598.         player->message = GOTBACKPACK;
  599.         break;
  600.        
  601.         // weapons
  602.       case SPR_BFUG:
  603.         if (!P_GiveWeapon (player, wp_bfg, false) )
  604.             return;
  605.         player->message = GOTBFG9000;
  606.         sound = sfx_wpnup;     
  607.         break;
  608.        
  609.       case SPR_MGUN:
  610.         if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) )
  611.             return;
  612.         player->message = GOTCHAINGUN;
  613.         sound = sfx_wpnup;     
  614.         break;
  615.        
  616.       case SPR_CSAW:
  617.         if (!P_GiveWeapon (player, wp_chainsaw, false) )
  618.             return;
  619.         player->message = GOTCHAINSAW;
  620.         sound = sfx_wpnup;     
  621.         break;
  622.        
  623.       case SPR_LAUN:
  624.         if (!P_GiveWeapon (player, wp_missile, false) )
  625.             return;
  626.         player->message = GOTLAUNCHER;
  627.         sound = sfx_wpnup;     
  628.         break;
  629.        
  630.       case SPR_PLAS:
  631.         if (!P_GiveWeapon (player, wp_plasma, false) )
  632.             return;
  633.         player->message = GOTPLASMA;
  634.         sound = sfx_wpnup;     
  635.         break;
  636.        
  637.       case SPR_SHOT:
  638.         if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) )
  639.             return;
  640.         player->message = GOTSHOTGUN;
  641.         sound = sfx_wpnup;     
  642.         break;
  643.                
  644.       case SPR_SGN2:
  645.         if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) )
  646.             return;
  647.         player->message = GOTSHOTGUN2;
  648.         sound = sfx_wpnup;     
  649.         break;
  650.                
  651.       default:
  652.         I_Error ("P_SpecialThing: Unknown gettable thing");
  653.     }
  654.        
  655.     if (special->flags & MF_COUNTITEM)
  656.         player->itemcount++;
  657.     P_RemoveMobj (special);
  658.     player->bonuscount += BONUSADD;
  659.     if (player == &players[consoleplayer])
  660.         S_StartSound (NULL, sound);
  661. }
  662.  
  663.  
  664. //
  665. // KillMobj
  666. //
  667. void
  668. P_KillMobj
  669. ( mobj_t*       source,
  670.   mobj_t*       target )
  671. {
  672.     mobjtype_t  item;
  673.     mobj_t*     mo;
  674.        
  675.     target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);
  676.  
  677.     if (target->type != MT_SKULL)
  678.         target->flags &= ~MF_NOGRAVITY;
  679.  
  680.     target->flags |= MF_CORPSE|MF_DROPOFF;
  681.     target->height >>= 2;
  682.  
  683.     if (source && source->player)
  684.     {
  685.         // count for intermission
  686.         if (target->flags & MF_COUNTKILL)
  687.             source->player->killcount++;       
  688.  
  689.         if (target->player)
  690.             source->player->frags[target->player-players]++;
  691.     }
  692.     else if (!netgame && (target->flags & MF_COUNTKILL) )
  693.     {
  694.         // count all monster deaths,
  695.         // even those caused by other monsters
  696.         players[0].killcount++;
  697.     }
  698.    
  699.     if (target->player)
  700.     {
  701.         // count environment kills against you
  702.         if (!source)   
  703.             target->player->frags[target->player-players]++;
  704.                        
  705.         target->flags &= ~MF_SOLID;
  706.         target->player->playerstate = PST_DEAD;
  707.         P_DropWeapon (target->player);
  708.  
  709.         if (target->player == &players[consoleplayer]
  710.             && automapactive)
  711.         {
  712.             // don't die in auto map,
  713.             // switch view prior to dying
  714.             AM_Stop ();
  715.         }
  716.        
  717.     }
  718.  
  719.     if (target->health < -target->info->spawnhealth
  720.         && target->info->xdeathstate)
  721.     {
  722.         P_SetMobjState (target, target->info->xdeathstate);
  723.     }
  724.     else
  725.         P_SetMobjState (target, target->info->deathstate);
  726.     target->tics -= P_Random()&3;
  727.  
  728.     if (target->tics < 1)
  729.         target->tics = 1;
  730.                
  731.     //  I_StartSound (&actor->r, actor->info->deathsound);
  732.  
  733.  
  734.     // Drop stuff.
  735.     // This determines the kind of object spawned
  736.     // during the death frame of a thing.
  737.     switch (target->type)
  738.     {
  739.       case MT_WOLFSS:
  740.       case MT_POSSESSED:
  741.         item = MT_CLIP;
  742.         break;
  743.        
  744.       case MT_SHOTGUY:
  745.         item = MT_SHOTGUN;
  746.         break;
  747.        
  748.       case MT_CHAINGUY:
  749.         item = MT_CHAINGUN;
  750.         break;
  751.        
  752.       default:
  753.         return;
  754.     }
  755.  
  756.     mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
  757.     mo->flags |= MF_DROPPED;    // special versions of items
  758. }
  759.  
  760.  
  761.  
  762.  
  763. //
  764. // P_DamageMobj
  765. // Damages both enemies and players
  766. // "inflictor" is the thing that caused the damage
  767. //  creature or missile, can be NULL (slime, etc)
  768. // "source" is the thing to target after taking damage
  769. //  creature or NULL
  770. // Source and inflictor are the same for melee attacks.
  771. // Source can be NULL for slime, barrel explosions
  772. // and other environmental stuff.
  773. //
  774. void
  775. P_DamageMobj
  776. ( mobj_t*       target,
  777.   mobj_t*       inflictor,
  778.   mobj_t*       source,
  779.   int           damage )
  780. {
  781.     unsigned    ang;
  782.     int         saved;
  783.     player_t*   player;
  784.     fixed_t     thrust;
  785.     int         temp;
  786.        
  787.     if ( !(target->flags & MF_SHOOTABLE) )
  788.         return; // shouldn't happen...
  789.                
  790.     if (target->health <= 0)
  791.         return;
  792.  
  793.     if ( target->flags & MF_SKULLFLY )
  794.     {
  795.         target->momx = target->momy = target->momz = 0;
  796.     }
  797.        
  798.     player = target->player;
  799.     if (player && gameskill == sk_baby)
  800.         damage >>= 1;   // take half damage in trainer mode
  801.                
  802.  
  803.     // Some close combat weapons should not
  804.     // inflict thrust and push the victim out of reach,
  805.     // thus kick away unless using the chainsaw.
  806.     if (inflictor
  807.         && !(target->flags & MF_NOCLIP)
  808.         && (!source
  809.             || !source->player
  810.             || source->player->readyweapon != wp_chainsaw))
  811.     {
  812.         ang = R_PointToAngle2 ( inflictor->x,
  813.                                 inflictor->y,
  814.                                 target->x,
  815.                                 target->y);
  816.                
  817.         thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
  818.  
  819.         // make fall forwards sometimes
  820.         if ( damage < 40
  821.              && damage > target->health
  822.              && target->z - inflictor->z > 64*FRACUNIT
  823.              && (P_Random ()&1) )
  824.         {
  825.             ang += ANG180;
  826.             thrust *= 4;
  827.         }
  828.                
  829.         ang >>= ANGLETOFINESHIFT;
  830.         target->momx += FixedMul (thrust, finecosine[ang]);
  831.         target->momy += FixedMul (thrust, finesine[ang]);
  832.     }
  833.    
  834.     // player specific
  835.     if (player)
  836.     {
  837.         // end of game hell hack
  838.         if (target->subsector->sector->special == 11
  839.             && damage >= target->health)
  840.         {
  841.             damage = target->health - 1;
  842.         }
  843.        
  844.  
  845.         // Below certain threshold,
  846.         // ignore damage in GOD mode, or with INVUL power.
  847.         if ( damage < 1000
  848.              && ( (player->cheats&CF_GODMODE)
  849.                   || player->powers[pw_invulnerability] ) )
  850.         {
  851.             return;
  852.         }
  853.        
  854.         if (player->armortype)
  855.         {
  856.             if (player->armortype == 1)
  857.                 saved = damage/3;
  858.             else
  859.                 saved = damage/2;
  860.            
  861.             if (player->armorpoints <= saved)
  862.             {
  863.                 // armor is used up
  864.                 saved = player->armorpoints;
  865.                 player->armortype = 0;
  866.             }
  867.             player->armorpoints -= saved;
  868.             damage -= saved;
  869.         }
  870.         player->health -= damage;       // mirror mobj health here for Dave
  871.         if (player->health < 0)
  872.             player->health = 0;
  873.        
  874.         player->attacker = source;
  875.         player->damagecount += damage;  // add damage after armor / invuln
  876.  
  877.         if (player->damagecount > 100)
  878.             player->damagecount = 100;  // teleport stomp does 10k points...
  879.        
  880.         temp = damage < 100 ? damage : 100;
  881.  
  882.         if (player == &players[consoleplayer])
  883.             I_Tactile (40,10,40+temp*2);
  884.     }
  885.    
  886.     // do the damage   
  887.     target->health -= damage;  
  888.     if (target->health <= 0)
  889.     {
  890.         P_KillMobj (source, target);
  891.         return;
  892.     }
  893.  
  894.     if ( (P_Random () < target->info->painchance)
  895.          && !(target->flags&MF_SKULLFLY) )
  896.     {
  897.         target->flags |= MF_JUSTHIT;    // fight back!
  898.        
  899.         P_SetMobjState (target, target->info->painstate);
  900.     }
  901.                        
  902.     target->reactiontime = 0;           // we're awake now...  
  903.  
  904.     if ( (!target->threshold || target->type == MT_VILE)
  905.          && source && source != target
  906.          && source->type != MT_VILE)
  907.     {
  908.         // if not intent on another player,
  909.         // chase after this one
  910.         target->target = source;
  911.         target->threshold = BASETHRESHOLD;
  912.         if (target->state == &states[target->info->spawnstate]
  913.             && target->info->seestate != S_NULL)
  914.             P_SetMobjState (target, target->info->seestate);
  915.     }
  916.                        
  917. }
  918.  
  919.