// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handling interactions (i.e., collisions). // // Data. #include "doomdef.h" #include "dstrings.h" #include "sounds.h" #include "deh_main.h" #include "deh_misc.h" #include "doomstat.h" #include "m_misc.h" #include "m_random.h" #include "i_system.h" #include "am_map.h" #include "p_local.h" #include "p_dialog.h" // villsa [STRIFE] #include "s_sound.h" #include "p_inter.h" #include "hu_stuff.h" // villsa [STRIFE] #include "z_zone.h" // villsa [STRIFE] // haleyjd [STRIFE] #include "w_wad.h" #include "p_pspr.h" #include "p_dialog.h" #include "f_finale.h" #define BONUSADD 6 // a weapon is found with two clip loads, // a big item has five clip loads // villsa [STRIFE] updated arrays int maxammo[NUMAMMO] = { 250, 50, 25, 400, 100, 30, 16 }; int clipammo[NUMAMMO] = { 10, 4, 2, 20, 4, 6, 4 }; // // GET STUFF // // // P_GiveAmmo // Num is the number of clip loads, // not the individual count (0= 1/2 clip). // Returns false if the ammo can't be picked up at all // // [STRIFE] Modified for Strife ammo types // boolean P_GiveAmmo(player_t* player, ammotype_t ammo, int num) { int oldammo; if(ammo == am_noammo) return false; if(ammo > NUMAMMO) I_Error ("P_GiveAmmo: bad type %i", ammo); if(player->ammo[ammo] == player->maxammo[ammo]) return false; if(num) num *= clipammo[ammo]; else num = clipammo[ammo]/2; if(gameskill == sk_baby || gameskill == sk_nightmare) { // give double ammo in trainer mode, // you'll need in nightmare num <<= 1; } oldammo = player->ammo[ammo]; player->ammo[ammo] += num; if(player->ammo[ammo] > player->maxammo[ammo]) player->ammo[ammo] = player->maxammo[ammo]; // If non zero ammo, // don't change up weapons, // player was lower on purpose. if(oldammo) return true; // We were down to zero, // so select a new weapon. // Preferences are not user selectable. // villsa [STRIFE] ammo update // where's the check for grenades? - haleyjd: verified no switch to grenades // haleyjd 10/03/10: don't change to electric bow when picking up poison // arrows. if(!player->readyweapon) { switch(ammo) { case am_bullets: if(player->weaponowned[wp_rifle]) player->pendingweapon = wp_rifle; break; case am_elecbolts: if(player->weaponowned[wp_elecbow]) player->pendingweapon = wp_elecbow; break; case am_cell: if(player->weaponowned[wp_mauler]) player->pendingweapon = wp_mauler; break; case am_missiles: if(player->weaponowned[wp_missile]) player->pendingweapon = wp_missile; break; default: break; } } return true; } // // P_GiveWeapon // The weapon name may have a MF_DROPPED flag ored in. // // villsa [STRIFE] some stuff has been changed/moved around // boolean P_GiveWeapon(player_t* player, weapontype_t weapon, boolean dropped) { boolean gaveammo; boolean gaveweapon; // villsa [STRIFE] new code for giving alternate version // of the weapon to player if(player->weaponowned[weapon]) gaveweapon = false; else { gaveweapon = true; player->weaponowned[weapon] = true; // Alternate "sister" weapons that you also get as a bonus: switch(weapon) { case wp_elecbow: player->weaponowned[wp_poisonbow] = true; break; case wp_hegrenade: player->weaponowned[wp_wpgrenade] = true; break; case wp_mauler: player->weaponowned[wp_torpedo] = true; break; default: break; } // check for the standard weapons only if(weapon > player->readyweapon && weapon <= wp_sigil) player->pendingweapon = weapon; } if(netgame && (deathmatch != 2) && !dropped) { // leave placed weapons forever on net games if(!gaveweapon) return false; player->bonuscount += BONUSADD; player->weaponowned[weapon] = true; if(deathmatch) P_GiveAmmo(player, weaponinfo[weapon].ammo, 5); else P_GiveAmmo(player, weaponinfo[weapon].ammo, 2); if(player == &players[consoleplayer]) S_StartSound (NULL, sfx_wpnup); return false; } if(weaponinfo[weapon].ammo != am_noammo) { // give one clip with a dropped weapon, // two clips with a found weapon if(dropped) gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); else gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); } else gaveammo = false; return(gaveweapon || gaveammo); } // // P_GiveBody // Returns false if the body isn't needed at all // // villsa [STRIFE] a lot of changes have been added for stamina // boolean P_GiveBody(player_t* player, int num) { int maxhealth; int healing; maxhealth = MAXHEALTH + player->stamina; if(num >= 0) // haleyjd 20100923: fixed to give proper amount of health { mobj_t *mo; // haleyjd 20110225: needed below... // any healing to do? if(player->health >= maxhealth) return false; // give, and cap to maxhealth player->health += num; if(player->health >= maxhealth) player->health = maxhealth; // Set mo->health for consistency. // haleyjd 20110225: Seems Strife can call this on a NULL player->mo // when giving items to players that are not in the game... mo = P_SubstNullMobj(player->mo); mo->health = player->health; } else { // [STRIFE] handle healing from the Front's medic // The amount the player's health will be set to scales up with stamina // increases. // Ex 1: On the wimpiest skill level, -100 is sent in. This restores // full health no matter what your stamina. // (100*100)/100 = 100 // (200*100)/100 = 200 // Ex 2: On the most stringent skill levels, -50 is sent in. This will // restore at most half of your health. // (100*50)/100 = 50 // (200*50)/100 = 100 healing = (-num * maxhealth) / MAXHEALTH; // This is also the "threshold" of healing. You need less health than // the amount that will be restored in order to get any benefit. // So on the easiest skill you will always be fully healed. // On the hardest skill you must have less than 50 health, and will // only recover to 50 (assuming base stamina stat) if(player->health >= healing) return false; // Set health. BUG: Oddly, mo->health is NOT set here... player->health = healing; } return true; } // // P_GiveArmor // Returns false if the armor is worse // than the current armor. // // [STRIFE] Modified for Strife armor items // boolean P_GiveArmor(player_t* player, int armortype) { int hits; // villsa [STRIFE] if(armortype < 0) { if(player->armorpoints) return false; armortype = -armortype; } hits = armortype * 100; if(player->armorpoints >= hits) return false; // don't pick up player->armortype = armortype; player->armorpoints = hits; return true; } // // P_GiveCard // // [STRIFE] Modified to use larger bonuscount // boolean P_GiveCard(player_t* player, card_t card) { if (player->cards[card]) return false; // villsa [STRIFE] multiply by 2 player->bonuscount = BONUSADD * 2; player->cards[card] = true; return true; } // // P_GivePower // // [STRIFE] Modifications for new powerups // boolean P_GivePower(player_t* player, powertype_t power) { // haleyjd 09/14/10: [STRIFE] moved to top, exception for Shadow Armor if(player->powers[power] && power != pw_invisibility) return false; // already got it // if giving pw_invisibility and player already has MVIS, no can do. if(power == pw_invisibility && (player->mo->flags & MF_MVIS)) return false; // villsa [STRIFE] if(power == pw_targeter) { player->powers[power] = TARGTICS; P_SetPsprite(player, ps_targcenter, S_TRGT_00); // 10 P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11 P_SetPsprite(player, ps_targright, S_TRGT_02); // 12 player->psprites[ps_targcenter].sx = (160*FRACUNIT); player->psprites[ps_targleft ].sy = (100*FRACUNIT); player->psprites[ps_targcenter].sy = (100*FRACUNIT); player->psprites[ps_targright ].sy = (100*FRACUNIT); return true; } if(power == pw_invisibility) { // if player already had this power... if(player->powers[power]) { // remove SHADOW, give MVIS. player->mo->flags &= ~MF_SHADOW; player->mo->flags |= MF_MVIS; } else // give SHADOW player->mo->flags |= MF_SHADOW; // set tics if giving shadow, or renew them if MVIS. player->powers[power] = INVISTICS; return true; } if(power == pw_ironfeet) { player->powers[power] = IRONTICS; return true; } if(power == pw_strength) { P_GiveBody(player, 100); player->powers[power] = 1; return true; } // villsa [STRIFE] if(power == pw_allmap) { // remember in mapstate if(gamemap < 40) player->mapstate[gamemap] = true; player->powers[power] = 1; return true; } // villsa [STRIFE] if(power == pw_communicator) { player->powers[power] = 1; return true; } // default behavior: player->powers[power] = 1; return true; } // villsa [STRIFE] static char pickupmsg[80]; // // P_TouchSpecialThing // // [STRIFE] Rewritten for Strife collectables. // void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher) { player_t* player; int i; fixed_t delta; int sound; delta = special->z - toucher->z; if(delta > toucher->height || delta < -8*FRACUNIT) return; // out of reach sound = sfx_itemup; player = toucher->player; // Dead thing touching. // Can happen with a sliding player corpse. if(toucher->health <= 0) return; // villsa [STRIFE] damage toucher if special is spectral // haleyjd 09/21/10: corrected to test for SPECTRE thingtypes specifically switch(special->type) { case MT_SPECTRE_A: case MT_SPECTRE_B: case MT_SPECTRE_C: case MT_SPECTRE_D: case MT_SPECTRE_E: case MT_ENTITY: case MT_SUBENTITY: P_DamageMobj(toucher, NULL, NULL, 5); return; default: break; } // villsa [STRIFE] pickupmsg[0] = 0; // Identify by sprite. // villsa [STRIFE] new items switch(special->sprite) { // bullets case SPR_BLIT: // haleyjd: fixed missing MF_DROPPED check if(!P_GiveAmmo(player, am_bullets, !(special->flags & MF_DROPPED))) return; break; // box of bullets case SPR_BBOX: if(!P_GiveAmmo(player, am_bullets, 5)) return; break; // missile case SPR_ROKT: if(!P_GiveAmmo(player, am_missiles, 1)) return; break; // box of missiles case SPR_MSSL: if(!P_GiveAmmo(player, am_missiles, 5)) return; break; // battery case SPR_BRY1: if(!P_GiveAmmo(player, am_cell, 1)) return; break; // cell pack case SPR_CPAC: if(!P_GiveAmmo(player, am_cell, 5)) return; break; // poison bolts case SPR_PQRL: if(!P_GiveAmmo(player, am_poisonbolts, 5)) return; break; // electric bolts case SPR_XQRL: if(!P_GiveAmmo(player, am_elecbolts, 5)) return; break; // he grenades case SPR_GRN1: if(!P_GiveAmmo(player, am_hegrenades, 1)) return; break; // wp grenades case SPR_GRN2: if(!P_GiveAmmo(player, am_wpgrenades, 1)) return; break; // rifle case SPR_RIFL: if(!P_GiveWeapon(player, wp_rifle, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // flame thrower case SPR_FLAM: if(!P_GiveWeapon(player, wp_flame, false)) return; // haleyjd: gives extra ammo. P_GiveAmmo(player, am_cell, 3); sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // missile launcher case SPR_MMSL: if(!P_GiveWeapon(player, wp_missile, false)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // grenade launcher case SPR_GRND: if(!P_GiveWeapon(player, wp_hegrenade, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // mauler case SPR_TRPD: if(!P_GiveWeapon(player, wp_mauler, false)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // electric bolt crossbow case SPR_CBOW: if(!P_GiveWeapon(player, wp_elecbow, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // haleyjd 09/21/10: missed case: THE SIGIL! case SPR_SIGL: if(!P_GiveWeapon(player, wp_sigil, (special->flags & MF_DROPPED) != 0)) { player->sigiltype = special->frame; return; } if(netgame) player->sigiltype = 4; player->pendingweapon = wp_sigil; player->st_update = true; if(deathmatch) return; sound = sfx_wpnup; break; // backpack case SPR_BKPK: if(!player->backpack) { for(i = 0; i < NUMAMMO; i++) player->maxammo[i] *= 2; player->backpack = true; } for(i = 0; i < NUMAMMO; i++) P_GiveAmmo(player, i, 1); break; // 1 Gold case SPR_COIN: P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // 10 Gold case SPR_CRED: for(i = 0; i < 10; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // 25 Gold case SPR_SACK: // haleyjd 09/21/10: missed code: if a SPR_SACK object has negative // health, it will give that much gold - STRIFE-TODO: verify if(special->health < 0) { for(i = special->health; i != 0; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); } else { for(i = 0; i < 25; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); } break; // 50 Gold case SPR_CHST: for(i = 0; i < 50; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // Leather Armor case SPR_ARM1: if(!P_GiveArmor(player, -2)) if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; break; // Metal Armor case SPR_ARM2: if(!P_GiveArmor(player, -1)) if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; break; // All-map powerup case SPR_PMAP: if(!P_GivePower(player, pw_allmap)) return; sound = sfx_yeah; break; // The Comm Unit - because you need Blackbird whining in your ear the // whole time and telling you how lost she is :P case SPR_COMM: if(!P_GivePower(player, pw_communicator)) return; sound = sfx_yeah; break; // haleyjd 09/21/10: missed case - Shadow Armor; though, I do not know why // this has a case distinct from generic inventory items... Maybe it started // out as an auto-use-if-possible item much like regular armor... case SPR_SHD1: if(!P_GiveInventoryItem(player, SPR_SHD1, special->type)) pickupmsg[0] = '!'; break; // villsa [STRIFE] check default items case SPR_TOKN: default: if(special->type >= MT_KEY_BASE && special->type <= MT_NEWKEY5) { // haleyjd 09/21/10: Strife player still picks up keys that // he has already found. (break, not return) if(!P_GiveCard(player, special->type - MT_KEY_BASE)) break; } else { if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; } break; } // villsa [STRIFE] set message if(!pickupmsg[0]) { if(special->info->name) { DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the %s.", DEH_String(special->info->name)); } else DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the item."); } // use the first character to indicate that the player is full on items else if(pickupmsg[0] == '!') { DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You cannot hold any more."); player->message = pickupmsg; return; } if(special->flags & MF_GIVEQUEST) { // [STRIFE]: Award quest flag based on the thing's speed. Quest 8 was // apparently at some point given by the Broken Power Coupling, which is // why they don't want to award it if you have Quest 6 (which is // acquired by destroying the Front's working power coupling). BUT, the // broken coupling object's speed is NOT 8... it is 512*FRACUNIT. For // strict portability beyond the x86, we need to AND the operand by 31. if(special->info->speed != 8 || !(player->questflags & QF_QUEST6)) player->questflags |= 1 << ((special->info->speed - 1) & 31); } // haleyjd 08/30/10: [STRIFE] No itemcount //if (special->flags & MF_COUNTITEM) // player->itemcount++; P_RemoveMobj(special); player->message = pickupmsg; player->bonuscount += BONUSADD; if(player == &players[consoleplayer]) S_StartSound(NULL, sound); } // villsa [STRIFE] static char plrkilledmsg[80]; // // KillMobj // // [STRIFE] Major modifications for drop types, no tic randomization, etc. // void P_KillMobj(mobj_t* source, mobj_t* target) { mobjtype_t item; mobj_t* mo; line_t junk; int i; // villsa [STRIFE] corpse and dropoff are removed, but why when these two flags // are set a few lines later? watcom nonsense perhaps? target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_BOUNCE|MF_CORPSE|MF_DROPOFF); // villsa [STRIFE] unused /* if (target->type != MT_SKULL) target->flags &= ~MF_NOGRAVITY; */ target->flags |= MF_CORPSE|MF_DROPOFF; target->height = FRACUNIT; // villsa [STRIFE] set to fracunit instead of >>= 2 if(source && source->player) { // count for intermission if(target->flags & MF_COUNTKILL) source->player->killcount++; if(target->player) { source->player->frags[target->player-players]++; // villsa [STRIFE] new messages when fragging players // haleyjd 20141024: corrected; uses player->allegiance, not mo->miscdata DEH_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s killed %s", player_names[source->player->allegiance], player_names[target->player->allegiance]); if(netgame) players[consoleplayer].message = plrkilledmsg; } } else if(!netgame && (target->flags & MF_COUNTKILL)) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; } if(target->player) { // count environment kills against you if(!source) target->player->frags[target->player-players]++; if(gamemap == 29 && !netgame) { // haleyjd 09/13/10: [STRIFE] Give player the bad ending. F_StartFinale(); return; } // villsa [STRIFE] spit out inventory items when killed if(netgame) { int amount = 0; mobj_t* loot; int r = 0; while(1) { if(target->player->inventory[0].amount <= 0) break; item = target->player->inventory[0].type; if(item == MT_MONY_1) { loot = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), MT_MONY_25); // [STRIFE] TODO - what the hell is it doing here? loot->health = target->player->inventory[0].amount; loot->health = -target->player->inventory[0].amount; amount = target->player->inventory[0].amount; } else { loot = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item); amount = 1; } P_RemoveInventoryItem(target->player, 0, amount); r = P_Random(); loot->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS; loot->momy += ((P_Random() & 7) + 1) << FRACBITS; loot->flags |= MF_DROPPED; } } target->flags &= ~MF_SOLID; target->player->playerstate = PST_DEAD; target->player->mo->momz = 5*FRACUNIT; // [STRIFE]: small hop! P_DropWeapon(target->player); if(target->player == &players[consoleplayer] && automapactive) { // don't die in auto map, // switch view prior to dying AM_Stop (); } } // villsa [STRIFE] some modifications to setting states if(target->state != &states[S_BURN_23]) { if(target->health == -6666) P_SetMobjState(target, S_DISR_00); // 373 else { if(target->health < -target->info->spawnhealth && target->info->xdeathstate) P_SetMobjState(target, target->info->xdeathstate); else P_SetMobjState(target, target->info->deathstate); } } // villsa [STRIFE] no death tics randomization // Drop stuff. // villsa [STRIFE] get item from dialog target item = P_DialogFind(target->type, target->miscdata)->dropitem; if(!item) { // villsa [STRIFE] drop default items switch(target->type) { case MT_ORACLE: item = MT_MEAT; break; case MT_PROGRAMMER: item = MT_SIGIL_A; break; case MT_PRIEST: item = MT_JUNK; break; case MT_BISHOP: item = MT_AMINIBOX; break; case MT_PGUARD: case MT_CRUSADER: item = MT_ACELL; break; case MT_RLEADER: item = MT_AAMMOBOX; break; case MT_GUARD1: case MT_REBEL1: case MT_SHADOWGUARD: item = MT_ACLIP; break; case MT_SPECTRE_B: item = MT_SIGIL_B; break; case MT_SPECTRE_C: item = MT_SIGIL_C; break; case MT_SPECTRE_D: item = MT_SIGIL_D; break; case MT_SPECTRE_E: item = MT_SIGIL_E; break; case MT_COUPLING: junk.tag = 225; EV_DoDoor(&junk, vld_close); junk.tag = 44; EV_DoFloor(&junk, lowerFloor); GiveVoiceObjective("VOC13", "LOG13", 0); item = MT_COUPLING_BROKEN; players[0].questflags |= (1 << (mobjinfo[MT_COUPLING].speed - 1)); break; default: return; } } // handle special case for scripted target's dropped item switch(item) { case MT_TOKEN_SHOPCLOSE: junk.tag = 222; EV_DoDoor(&junk, vld_close); P_NoiseAlert(players[0].mo, players[0].mo); M_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s", DEH_String("You're dead! You set off the alarm.")); if(!deathmatch) players[consoleplayer].message = plrkilledmsg; return; case MT_TOKEN_PRISON_PASS: junk.tag = 223; EV_DoDoor(&junk, vld_open); return; case MT_TOKEN_DOOR3: junk.tag = 224; EV_DoDoor(&junk, vld_open); return; case MT_SIGIL_A: case MT_SIGIL_B: case MT_SIGIL_C: case MT_SIGIL_D: case MT_SIGIL_E: for(i = 0; i < MAXPLAYERS; i++) { if(!P_GiveWeapon(&players[i], wp_sigil, false)) { if(players[i].sigiltype++ > 4) players[i].sigiltype = 4; } // haleyjd 20110225: fixed these two assignments which Watcom munged // up in the assembly by pre-incrementing the pointer into players[] players[i].st_update = true; players[i].pendingweapon = wp_sigil; } return; case MT_TOKEN_ALARM: P_NoiseAlert(players[0].mo, players[0].mo); M_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s", DEH_String("You Fool! You've set off the alarm")); if(!deathmatch) players[consoleplayer].message = plrkilledmsg; return; default: break; } // villsa [STRIFE] toss out item if(!deathmatch || !(mobjinfo[item].flags & MF_NOTDMATCH)) { int r; mo = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item); r = P_Random(); mo->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS; r = P_Random(); mo->momy += ((r & 7) - (P_Random() & 7)) << FRACBITS; mo->flags |= (MF_SPECIAL|MF_DROPPED); // special versions of items } } // // P_IsMobjBoss // // villsa [STRIFE] new function // static boolean P_IsMobjBoss(mobjtype_t type) { switch(type) { case MT_PROGRAMMER: case MT_BISHOP: case MT_RLEADER: case MT_ORACLE: case MT_PRIEST: return true; default: return false; } } // // P_DamageMobj // Damages both enemies and players // "inflictor" is the thing that caused the damage // creature or missile, can be NULL (slime, etc) // "source" is the thing to target after taking damage // creature or NULL // Source and inflictor are the same for melee attacks. // Source can be NULL for slime, barrel explosions // and other environmental stuff. // // [STRIFE] Extensive changes for spectrals, fire damage, disintegration, and // a plethora of mobjtype-specific hacks. // void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage) { angle_t ang; int saved; player_t* player; fixed_t thrust; int temp; if(!(target->flags & MF_SHOOTABLE) ) return; // shouldn't happen... if(target->health <= 0) return; player = target->player; // villsa [STRIFE] unused - skullfly check (removed) // villsa [STRIFE] handle spectral stuff // notes on projectile health: // -2 == enemy spectral projectile // -1 == player spectral projectile // haleyjd 20110203: refactored completely if(inflictor && (inflictor->flags & MF_SPECTRAL)) { // players aren't damaged by their own (or others???) sigils // STRIFE-TODO: verify in deathmatch if(target->type == MT_PLAYER && inflictor->health == -1) return; // enemies aren't damaged by enemy sigil attacks if((target->flags & MF_SPECTRAL) && inflictor->health == -2) return; // Macil2, Oracle, and Spectre C cannot be damaged by Sigil A switch(target->type) { case MT_RLEADER2: case MT_ORACLE: case MT_SPECTRE_C: // haleyjd: added source->player validity check for safety... if(source->player && source->player->sigiltype < 1) return; default: break; } } // villsa [STRIFE] new checks for various actors if(inflictor) { // Fire damage inflictors if(inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME) { temp = damage / 2; if(P_IsMobjBoss(target->type)) damage /= 2; else if(inflictor->type == MT_PFLAME) { damage /= 2; // robots take very little damage if(target->flags & MF_NOBLOOD) damage = temp / 2; } } else { switch(inflictor->type) { case MT_HOOKSHOT: // haleyjd 20110203: should use source, not inflictor ang = R_PointToAngle2( target->x, target->y, source->x, source->y) >> ANGLETOFINESHIFT; target->momx += FixedMul(finecosine[ang], (12750*FRACUNIT) / target->info->mass); target->momy += FixedMul(finesine[ang], (12750*FRACUNIT) / target->info->mass); target->reactiontime += 10; temp = P_AproxDistance(target->x - source->x, target->y - source->y); temp /= target->info->mass; if(temp < 1) temp = 1; target->momz = (source->z - target->z) / temp; break; case MT_POISARROW: // don't affect robots if(target->flags & MF_NOBLOOD) return; // instant kill damage = target->health + 10; break; default: // Spectral retaliation, though this may in fact be unreachable // since non-spectral inflictors are mostly filtered out. if(target->flags & MF_SPECTRAL && !(inflictor->flags & MF_SPECTRAL)) { P_SetMobjState(target, target->info->missilestate); return; // take no damage } break; } } } // villsa [STRIFE] special cases for shopkeepers and macil if((target->type >= MT_SHOPKEEPER_W && target->type <= MT_SHOPKEEPER_M) || target->type == MT_RLEADER) { if(source) target->target = source; P_SetMobjState(target, target->info->painstate); return; } // villsa [STRIFE] handle fieldguard damage if(target->type == MT_FIELDGUARD) { // degnin ores are only allowed to damage the fieldguard if(!inflictor || inflictor->type != MT_DEGNINORE) return; damage = target->health; } if(player && gameskill == sk_baby) damage >>= 1; // take half damage in trainer mode // Some close combat weapons should not // inflict thrust and push the victim out of reach, // thus kick away unless using the chainsaw. if (inflictor && !(target->flags & MF_NOCLIP) && (!source || !source->player || source->player->readyweapon != wp_flame)) { ang = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y); thrust = damage * (FRACUNIT>>3) * 100 / target->info->mass; // make fall forwards sometimes if(damage < 40 && damage > target->health && target->z - inflictor->z > 64*FRACUNIT && (P_Random() & 1)) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; target->momx += FixedMul (thrust, finecosine[ang]); target->momy += FixedMul (thrust, finesine[ang]); } // player specific if(player) { // end of game hell hack if (target->subsector->sector->special == 11 && damage >= target->health) { damage = target->health - 1; } // Below certain threshold, // ignore damage in GOD mode. // villsa [STRIFE] removed pw_invulnerability check if(damage < 1000 && (player->cheats & CF_GODMODE)) return; // villsa [STRIFE] flame attacks don't damage player if wearing envirosuit if(player->powers[pw_ironfeet] && inflictor) { if(inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME) { damage = 0; } } if(player->armortype) { if (player->armortype == 1) saved = damage/3; else saved = damage/2; if(player->armorpoints <= saved) { // armor is used up saved = player->armorpoints; player->armortype = 0; // villsa [STRIFE] P_UseInventoryItem(player, SPR_ARM1); P_UseInventoryItem(player, SPR_ARM2); } player->armorpoints -= saved; damage -= saved; } player->health -= damage; // mirror mobj health here for Dave // [STRIFE] haleyjd 20130302: bug fix - this is *not* capped here. //if(player->health < 0) // player->health = 0; player->attacker = source; player->damagecount += damage; // add damage after armor / invuln // haleyjd 20110203 [STRIFE]: target->target set here if(target != source) target->target = source; if(player->damagecount > 100) player->damagecount = 100; // teleport stomp does 10k points... temp = damage < 100 ? damage : 100; if(player == &players[consoleplayer]) I_Tactile (40,10,40+temp*2); } // do the damage target->health -= damage; // villsa [STRIFE] auto use medkits if(player && player->health < 50) { if(deathmatch || player->cheats & CF_AUTOHEALTH) { while(player->health < 50 && P_UseInventoryItem(player, SPR_MDKT)); while(player->health < 50 && P_UseInventoryItem(player, SPR_STMP)); } } if(target->health <= 0) { // villsa [STRIFE] grenades hurt... OUCH if(inflictor && inflictor->type == MT_HEGRENADE) target->health = -target->info->spawnhealth; else if(!(target->flags & MF_NOBLOOD)) { // villsa [STRIFE] disintegration death if(inflictor && (inflictor->type == MT_STRIFEPUFF3 || inflictor->type == MT_L_LASER || inflictor->type == MT_TORPEDO || inflictor->type == MT_TORPEDOSPREAD)) { S_StartSound(target, sfx_dsrptr); target->health = -6666; } } // villsa [STRIFE] flame death stuff if(!(target->flags & MF_NOBLOOD) && inflictor && (inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME)) { target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SHADOW|MF_MVIS); if(target->player) { target->player->cheats |= CF_ONFIRE; target->player->powers[pw_communicator] = false; target->player->readyweapon = 0; P_SetPsprite(target->player, ps_weapon, S_WAVE_00); // 02 P_SetPsprite(target->player, ps_flash, S_NULL); } P_SetMobjState(target, S_BURN_00); // 349 S_StartSound(target, sfx_burnme); return; } P_KillMobj(source, target); return; } // villsa [STRIFE] set crash state if(target->health <= 6 && target->info->crashstate) { P_SetMobjState(target, target->info->crashstate); return; } if(damage) { // villsa [STRIFE] removed unused skullfly flag if(P_Random() < target->info->painchance) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState (target, target->info->painstate); } } target->reactiontime = 0; // we're awake now... // villsa [STRIFE] new checks for thing types if (target->type != MT_PROGRAMMER && (!target->threshold || target->type == MT_ENTITY) && source && source != target && source->type != MT_ENTITY && ((source->flags & MF_ALLY) != (target->flags & MF_ALLY))) { // if not intent on another player, // chase after this one target->target = source; target->threshold = BASETHRESHOLD; if(target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) P_SetMobjState (target, target->info->seestate); } }