From 0df2cb80cf03d7259746834220d209b306a8c503 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 4 Sep 2008 23:15:36 +0000 Subject: Add GPLed Heretic/Hexen source. Subversion-branch: /branches/raven-branch Subversion-revision: 1195 --- src/hexen/p_inter.c | 2252 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2252 insertions(+) create mode 100644 src/hexen/p_inter.c (limited to 'src/hexen/p_inter.c') diff --git a/src/hexen/p_inter.c b/src/hexen/p_inter.c new file mode 100644 index 00000000..b9e904fa --- /dev/null +++ b/src/hexen/p_inter.c @@ -0,0 +1,2252 @@ + +//************************************************************************** +//** +//** p_inter.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: p_inter.c,v $ +//** $Revision: 1.145 $ +//** $Date: 96/01/16 10:35:33 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +#include "h2def.h" +#include "p_local.h" +#include "soundst.h" + +#define BONUSADD 6 + +int ArmorIncrement[NUMCLASSES][NUMARMOR] = +{ + { 25*FRACUNIT, 20*FRACUNIT, 15*FRACUNIT, 5*FRACUNIT }, + { 10*FRACUNIT, 25*FRACUNIT, 5*FRACUNIT, 20*FRACUNIT }, + { 5*FRACUNIT, 15*FRACUNIT, 10*FRACUNIT, 25*FRACUNIT }, + { 0, 0, 0, 0 } +}; + +int AutoArmorSave[NUMCLASSES] = { 15*FRACUNIT, 10*FRACUNIT, 5*FRACUNIT, 0 }; + +char *TextKeyMessages[] = +{ + TXT_KEY_STEEL, + TXT_KEY_CAVE, + TXT_KEY_AXE, + TXT_KEY_FIRE, + TXT_KEY_EMERALD, + TXT_KEY_DUNGEON, + TXT_KEY_SILVER, + TXT_KEY_RUSTED, + TXT_KEY_HORN, + TXT_KEY_SWAMP, + TXT_KEY_CASTLE +}; + +static void SetDormantArtifact(mobj_t *arti); +static void TryPickupArtifact(player_t *player, artitype_t artifactType, + mobj_t *artifact); +static void TryPickupWeapon(player_t *player, pclass_t weaponClass, + weapontype_t weaponType, mobj_t *weapon, char *message); +static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass, + int pieceValue, mobj_t *pieceMobj); + +#ifdef __NeXT__ +extern void strupr(char *s); +#endif + +//-------------------------------------------------------------------------- +// +// PROC P_SetMessage +// +//-------------------------------------------------------------------------- + +void P_SetMessage(player_t *player, char *message, boolean ultmsg) +{ + extern boolean messageson; + + if((player->ultimateMessage || !messageson) && !ultmsg) + { + return; + } + if(strlen(message) > 79) + { + memcpy(player->message, message, 80); + player->message[80] = 0; + } + else + { + strcpy(player->message, message); + } + strupr(player->message); + player->messageTics = MESSAGETICS; + player->yellowMessage = false; + if(ultmsg) + { + player->ultimateMessage = true; + } + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//========================================================================== +// +// P_SetYellowMessage +// +//========================================================================== + +void P_SetYellowMessage(player_t *player, char *message, boolean ultmsg) +{ + extern boolean messageson; + + if((player->ultimateMessage || !messageson) && !ultmsg) + { + return; + } + if(strlen(message) > 79) + { + memcpy(player->message, message, 80); + player->message[80] = 0; + } + else + { + strcpy(player->message, message); + } + player->messageTics = 5*MESSAGETICS; // Bold messages last longer + player->yellowMessage = true; + if(ultmsg) + { + player->ultimateMessage = true; + } + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//========================================================================== +// +// P_ClearMessage +// +//========================================================================== + +void P_ClearMessage(player_t *player) +{ + player->messageTics = 0; + if(player == &players[consoleplayer]) + { + BorderTopRefresh = true; + } +} + +//---------------------------------------------------------------------------- +// +// PROC P_HideSpecialThing +// +//---------------------------------------------------------------------------- + +void P_HideSpecialThing(mobj_t *thing) +{ + thing->flags &= ~MF_SPECIAL; + thing->flags2 |= MF2_DONTDRAW; + P_SetMobjState(thing, S_HIDESPECIAL1); +} + +//-------------------------------------------------------------------------- +// +// FUNC P_GiveMana +// +// Returns true if the player accepted the mana, false if it was +// refused (player has MAX_MANA). +// +//-------------------------------------------------------------------------- + +boolean P_GiveMana(player_t *player, manatype_t mana, int count) +{ + int prevMana; + //weapontype_t changeWeapon; + + if(mana == MANA_NONE || mana == MANA_BOTH) + { + return(false); + } + if(mana < 0 || mana > NUMMANA) + { + I_Error("P_GiveMana: bad type %i", mana); + } + if(player->mana[mana] == MAX_MANA) + { + return(false); + } + if(gameskill == sk_baby || gameskill == sk_nightmare) + { // extra mana in baby mode and nightmare mode + count += count>>1; + } + prevMana = player->mana[mana]; + + player->mana[mana] += count; + if(player->mana[mana] > MAX_MANA) + { + player->mana[mana] = MAX_MANA; + } + if(player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND + && mana == MANA_1 && prevMana <= 0) + { + P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); + } + return(true); +} + +//========================================================================== +// +// TryPickupWeapon +// +//========================================================================== + +static void TryPickupWeapon(player_t *player, pclass_t weaponClass, + weapontype_t weaponType, mobj_t *weapon, char *message) +{ + boolean remove; + boolean gaveMana; + boolean gaveWeapon; + + remove = true; + if(player->class != weaponClass) + { // Wrong class, but try to pick up for mana + if(netgame && !deathmatch) + { // Can't pick up weapons for other classes in coop netplay + return; + } + if(weaponType == WP_SECOND) + { + if(!P_GiveMana(player, MANA_1, 25)) + { + return; + } + } + else + { + if(!P_GiveMana(player, MANA_2, 25)) + { + return; + } + } + } + else if(netgame && !deathmatch) + { // Cooperative net-game + if(player->weaponowned[weaponType]) + { + return; + } + player->weaponowned[weaponType] = true; + if(weaponType == WP_SECOND) + { + P_GiveMana(player, MANA_1, 25); + } + else + { + P_GiveMana(player, MANA_2, 25); + } + player->pendingweapon = weaponType; + remove = false; + } + else + { // Deathmatch or single player game + if(weaponType == WP_SECOND) + { + gaveMana = P_GiveMana(player, MANA_1, 25); + } + else + { + gaveMana = P_GiveMana(player, MANA_2, 25); + } + if(player->weaponowned[weaponType]) + { + gaveWeapon = false; + } + else + { + gaveWeapon = true; + player->weaponowned[weaponType] = true; + if(weaponType > player->readyweapon) + { // Only switch to more powerful weapons + player->pendingweapon = weaponType; + } + } + if(!(gaveWeapon || gaveMana)) + { // Player didn't need the weapon or any mana + return; + } + } + + P_SetMessage(player, message, false); + if(weapon->special) + { + P_ExecuteLineSpecial(weapon->special, weapon->args, + NULL, 0, player->mo); + weapon->special = 0; + } + + if(remove) + { + if(deathmatch && !(weapon->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(weapon); + } + else + { + P_RemoveMobj(weapon); + } + } + + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + SB_PaletteFlash(false); + } +} + +//-------------------------------------------------------------------------- +// +// FUNC P_GiveWeapon +// +// Returns true if the weapon or its mana was accepted. +// +//-------------------------------------------------------------------------- + +/* +boolean P_GiveWeapon(player_t *player, pclass_t class, weapontype_t weapon) +{ + boolean gaveMana; + boolean gaveWeapon; + + if(player->class != class) + { // player cannot use this weapon, take it anyway, and get mana + if(netgame && !deathmatch) + { // Can't pick up weapons for other classes in coop netplay + return false; + } + if(weapon == WP_SECOND) + { + return P_GiveMana(player, MANA_1, 25); + } + else + { + return P_GiveMana(player, MANA_2, 25); + } + } + if(netgame && !deathmatch) + { // Cooperative net-game + if(player->weaponowned[weapon]) + { + return(false); + } + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + if(weapon == WP_SECOND) + { + P_GiveMana(player, MANA_1, 25); + } + else + { + P_GiveMana(player, MANA_2, 25); + } + player->pendingweapon = weapon; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + } + return(false); + } + if(weapon == WP_SECOND) + { + gaveMana = P_GiveMana(player, MANA_1, 25); + } + else + { + gaveMana = P_GiveMana(player, MANA_2, 25); + } + if(player->weaponowned[weapon]) + { + gaveWeapon = false; + } + else + { + gaveWeapon = true; + player->weaponowned[weapon] = true; + if(weapon > player->readyweapon) + { // Only switch to more powerful weapons + player->pendingweapon = weapon; + } + } + return(gaveWeapon || gaveMana); +} +*/ + +//=========================================================================== +// +// P_GiveWeaponPiece +// +//=========================================================================== + +/* +boolean P_GiveWeaponPiece(player_t *player, pclass_t class, int piece) +{ + P_GiveMana(player, MANA_1, 20); + P_GiveMana(player, MANA_2, 20); + if(player->class != class) + { + return true; + } + else if(player->pieces&piece) + { // player already has that weapon piece + return true; + } + player->pieces |= piece; + if(player->pieces == 7) + { // player has built the fourth weapon! + P_GiveWeapon(player, class, WP_FOURTH); + S_StartSound(player->mo, SFX_WEAPON_BUILD); + } + return true; +} +*/ + +//========================================================================== +// +// TryPickupWeaponPiece +// +//========================================================================== + +static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass, + int pieceValue, mobj_t *pieceMobj) +{ + boolean remove; + boolean checkAssembled; + boolean gaveWeapon; + int gaveMana; + static char *fourthWeaponText[] = + { + TXT_WEAPON_F4, + TXT_WEAPON_C4, + TXT_WEAPON_M4 + }; + static char *weaponPieceText[] = + { + TXT_QUIETUS_PIECE, + TXT_WRAITHVERGE_PIECE, + TXT_BLOODSCOURGE_PIECE + }; + static int pieceValueTrans[] = + { + 0, // 0: never + WPIECE1|WPIECE2|WPIECE3, // WPIECE1 (1) + WPIECE2|WPIECE3, // WPIECE2 (2) + 0, // 3: never + WPIECE3 // WPIECE3 (4) + }; + + remove = true; + checkAssembled = true; + gaveWeapon = false; + if(player->class != matchClass) + { // Wrong class, but try to pick up for mana + if(netgame && !deathmatch) + { // Can't pick up wrong-class weapons in coop netplay + return; + } + checkAssembled = false; + gaveMana = P_GiveMana(player, MANA_1, 20)+ + P_GiveMana(player, MANA_2, 20); + if(!gaveMana) + { // Didn't need the mana, so don't pick it up + return; + } + } + else if(netgame && !deathmatch) + { // Cooperative net-game + if(player->pieces&pieceValue) + { // Already has the piece + return; + } + pieceValue = pieceValueTrans[pieceValue]; + P_GiveMana(player, MANA_1, 20); + P_GiveMana(player, MANA_2, 20); + remove = false; + } + else + { // Deathmatch or single player game + gaveMana = P_GiveMana(player, MANA_1, 20)+ + P_GiveMana(player, MANA_2, 20); + if(player->pieces&pieceValue) + { // Already has the piece, check if mana needed + if(!gaveMana) + { // Didn't need the mana, so don't pick it up + return; + } + checkAssembled = false; + } + } + + // Pick up the weapon piece + if(pieceMobj->special) + { + P_ExecuteLineSpecial(pieceMobj->special, pieceMobj->args, + NULL, 0, player->mo); + pieceMobj->special = 0; + } + if(remove) + { + if(deathmatch && !(pieceMobj->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(pieceMobj); + } + else + { + P_RemoveMobj(pieceMobj); + } + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + SB_PaletteFlash(false); + } + + // Check if fourth weapon assembled + if(checkAssembled) + { + player->pieces |= pieceValue; + if(player->pieces == (WPIECE1|WPIECE2|WPIECE3)) + { + gaveWeapon = true; + player->weaponowned[WP_FOURTH] = true; + player->pendingweapon = WP_FOURTH; + } + } + + if(gaveWeapon) + { + P_SetMessage(player, fourthWeaponText[matchClass], false); + // Play the build-sound full volume for all players + S_StartSound(NULL, SFX_WEAPON_BUILD); + } + else + { + P_SetMessage(player, weaponPieceText[matchClass], false); + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, SFX_PICKUP_WEAPON); + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveBody +// +// Returns false if the body isn't needed at all. +// +//--------------------------------------------------------------------------- + +boolean P_GiveBody(player_t *player, int num) +{ + int max; + + max = MAXHEALTH; + if(player->morphTics) + { + max = MAXMORPHHEALTH; + } + if(player->health >= max) + { + return(false); + } + player->health += num; + if(player->health > max) + { + player->health = max; + } + player->mo->health = player->health; + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveArmor +// +// Returns false if the armor is worse than the current armor. +// +//--------------------------------------------------------------------------- + +boolean P_GiveArmor(player_t *player, armortype_t armortype, int amount) +{ + int hits; + int totalArmor; + + extern int ArmorMax[NUMCLASSES]; + + if(amount == -1) + { + hits = ArmorIncrement[player->class][armortype]; + if(player->armorpoints[armortype] >= hits) + { + return false; + } + else + { + player->armorpoints[armortype] = hits; + } + } + else + { + hits = amount*5*FRACUNIT; + totalArmor = player->armorpoints[ARMOR_ARMOR] + +player->armorpoints[ARMOR_SHIELD] + +player->armorpoints[ARMOR_HELMET] + +player->armorpoints[ARMOR_AMULET] + +AutoArmorSave[player->class]; + if(totalArmor < ArmorMax[player->class]*5*FRACUNIT) + { + player->armorpoints[armortype] += hits; + } + else + { + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------- +// +// PROC P_GiveKey +// +//--------------------------------------------------------------------------- + +int P_GiveKey(player_t *player, keytype_t key) +{ + if(player->keys&(1<bonuscount += BONUSADD; + player->keys |= 1<powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = INVULNTICS; + player->mo->flags2 |= MF2_INVULNERABLE; + if(player->class == PCLASS_MAGE) + { + player->mo->flags2 |= MF2_REFLECTIVE; + } + return(true); + } + if(power == pw_flight) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = FLIGHTTICS; + player->mo->flags2 |= MF2_FLY; + player->mo->flags |= MF_NOGRAVITY; + if(player->mo->z <= player->mo->floorz) + { + player->flyheight = 10; // thrust the player in the air a bit + } + return(true); + } + if(power == pw_infrared) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = INFRATICS; + return(true); + } + if(power == pw_speed) + { + if(player->powers[power] > BLINKTHRESHOLD) + { // Already have it + return(false); + } + player->powers[power] = SPEEDTICS; + return(true); + } + if(power == pw_minotaur) + { + // Doesn't matter if already have power, renew ticker + player->powers[power] = MAULATORTICS; + 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); + } +*/ + if(player->powers[power]) + { + return(false); // already got it + } + player->powers[power] = 1; + return(true); +} + +//========================================================================== +// +// TryPickupArtifact +// +//========================================================================== + +static void TryPickupArtifact(player_t *player, artitype_t artifactType, + mobj_t *artifact) +{ + static char *artifactMessages[NUMARTIFACTS] = + { + NULL, + TXT_ARTIINVULNERABILITY, + TXT_ARTIHEALTH, + TXT_ARTISUPERHEALTH, + TXT_ARTIHEALINGRADIUS, + TXT_ARTISUMMON, + TXT_ARTITORCH, + TXT_ARTIEGG, + TXT_ARTIFLY, + TXT_ARTIBLASTRADIUS, + TXT_ARTIPOISONBAG, + TXT_ARTITELEPORTOTHER, + TXT_ARTISPEED, + TXT_ARTIBOOSTMANA, + TXT_ARTIBOOSTARMOR, + TXT_ARTITELEPORT, + TXT_ARTIPUZZSKULL, + TXT_ARTIPUZZGEMBIG, + TXT_ARTIPUZZGEMRED, + TXT_ARTIPUZZGEMGREEN1, + TXT_ARTIPUZZGEMGREEN2, + TXT_ARTIPUZZGEMBLUE1, + TXT_ARTIPUZZGEMBLUE2, + TXT_ARTIPUZZBOOK1, + TXT_ARTIPUZZBOOK2, + TXT_ARTIPUZZSKULL2, + TXT_ARTIPUZZFWEAPON, + TXT_ARTIPUZZCWEAPON, + TXT_ARTIPUZZMWEAPON, + TXT_ARTIPUZZGEAR, // All gear pickups use the same text + TXT_ARTIPUZZGEAR, + TXT_ARTIPUZZGEAR, + TXT_ARTIPUZZGEAR + }; + + if(P_GiveArtifact(player, artifactType, artifact)) + { + if(artifact->special) + { + P_ExecuteLineSpecial(artifact->special, artifact->args, + NULL, 0, NULL); + artifact->special = 0; + } + player->bonuscount += BONUSADD; + if(artifactType < arti_firstpuzzitem) + { + SetDormantArtifact(artifact); + S_StartSound(artifact, SFX_PICKUP_ARTIFACT); + P_SetMessage(player, artifactMessages[artifactType], false); + } + else + { // Puzzle item + S_StartSound(NULL, SFX_PICKUP_ITEM); + P_SetMessage(player, artifactMessages[artifactType], true); + if(!netgame || deathmatch) + { // Remove puzzle items if not cooperative netplay + P_RemoveMobj(artifact); + } + } + } +} + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveArtifact +// +// Returns true if artifact accepted. +// +//--------------------------------------------------------------------------- + +boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo) +{ + int i; + int j; + boolean slidePointer; + + slidePointer = false; + i = 0; + while(player->inventory[i].type != arti && i < player->inventorySlotNum) + { + i++; + } + if(i == player->inventorySlotNum) + { + if(arti < arti_firstpuzzitem) + { + i = 0; + while(player->inventory[i].type < arti_firstpuzzitem + && i < player->inventorySlotNum) + { + i++; + } + if(i != player->inventorySlotNum) + { + for(j = player->inventorySlotNum; j > i; j--) + { + player->inventory[j].count = player->inventory[j-1].count; + player->inventory[j].type = player->inventory[j-1].type; + slidePointer = true; + } + } + } + player->inventory[i].count = 1; + player->inventory[i].type = arti; + player->inventorySlotNum++; + } + else + { + if(arti >= arti_firstpuzzitem && netgame && !deathmatch) + { // Can't carry more than 1 puzzle item in coop netplay + return false; + } + if(player->inventory[i].count >= 25) + { // Player already has 25 of this item + return false; + } + player->inventory[i].count++; + } + if(!player->artifactCount) + { + player->readyArtifact = arti; + } + else if(player == &players[consoleplayer] && slidePointer + && i <= inv_ptr) + { + inv_ptr++; + curpos++; + if(curpos > 6) + { + curpos = 6; + } + } + player->artifactCount++; + return(true); +} + +//========================================================================== +// +// SetDormantArtifact +// +// Removes the MF_SPECIAL flag and initiates the artifact pickup +// animation. +// +//========================================================================== + +static void SetDormantArtifact(mobj_t *arti) +{ + arti->flags &= ~MF_SPECIAL; + if(deathmatch && !(arti->flags2 & MF2_DROPPED)) + { + if(arti->type == MT_ARTIINVULNERABILITY) + { + P_SetMobjState(arti, S_DORMANTARTI3_1); + } + else if(arti->type == MT_SUMMONMAULATOR + || arti->type == MT_ARTIFLY) + { + P_SetMobjState(arti, S_DORMANTARTI2_1); + } + else + { + P_SetMobjState(arti, S_DORMANTARTI1_1); + } + } + else + { // Don't respawn + P_SetMobjState(arti, S_DEADARTI1); + } +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreArtifact +// +//--------------------------------------------------------------------------- + +void A_RestoreArtifact(mobj_t *arti) +{ + arti->flags |= MF_SPECIAL; + P_SetMobjState(arti, arti->info->spawnstate); + S_StartSound(arti, SFX_RESPAWN); +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreSpecialThing1 +// +// Make a special thing visible again. +// +//--------------------------------------------------------------------------- + +void A_RestoreSpecialThing1(mobj_t *thing) +{ + thing->flags2 &= ~MF2_DONTDRAW; + S_StartSound(thing, SFX_RESPAWN); +} + +//--------------------------------------------------------------------------- +// +// PROC A_RestoreSpecialThing2 +// +//--------------------------------------------------------------------------- + +void A_RestoreSpecialThing2(mobj_t *thing) +{ + thing->flags |= MF_SPECIAL; + P_SetMobjState(thing, thing->info->spawnstate); +} + +//--------------------------------------------------------------------------- +// +// PROC P_TouchSpecialThing +// +//--------------------------------------------------------------------------- + +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + fixed_t delta; + int sound; + boolean respawn; + + delta = special->z-toucher->z; + if(delta > toucher->height || delta < -32*FRACUNIT) + { // Out of reach + return; + } + if(toucher->health <= 0) + { // Toucher is dead + return; + } + sound = SFX_PICKUP_ITEM; + player = toucher->player; + respawn = true; + switch(special->sprite) + { + // Items + case SPR_PTN1: // Item_HealingPotion + if(!P_GiveBody(player, 10)) + { + return; + } + P_SetMessage(player, TXT_ITEMHEALTH, false); + break; + case SPR_ARM1: + if(!P_GiveArmor(player, ARMOR_ARMOR, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR1, false); + break; + case SPR_ARM2: + if(!P_GiveArmor(player, ARMOR_SHIELD, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR2, false); + break; + case SPR_ARM3: + if(!P_GiveArmor(player, ARMOR_HELMET, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR3, false); + break; + case SPR_ARM4: + if(!P_GiveArmor(player, ARMOR_AMULET, -1)) + { + return; + } + P_SetMessage(player, TXT_ARMOR4, false); + break; + + // Keys + case SPR_KEY1: + case SPR_KEY2: + case SPR_KEY3: + case SPR_KEY4: + case SPR_KEY5: + case SPR_KEY6: + case SPR_KEY7: + case SPR_KEY8: + case SPR_KEY9: + case SPR_KEYA: + case SPR_KEYB: + if(!P_GiveKey(player, special->sprite-SPR_KEY1)) + { + return; + } + P_SetMessage(player, TextKeyMessages[special->sprite-SPR_KEY1], + true); + sound = SFX_PICKUP_KEY; + + // Check and process the special now in case the key doesn't + // get removed for coop netplay + if(special->special) + { + P_ExecuteLineSpecial(special->special, special->args, + NULL, 0, toucher); + special->special = 0; + } + + if(!netgame) + { // Only remove keys in single player game + break; + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, sound); + SB_PaletteFlash(false); + } + return; + + // Artifacts + case SPR_PTN2: + TryPickupArtifact(player, arti_health, special); + return; + case SPR_SOAR: + TryPickupArtifact(player, arti_fly, special); + return; + case SPR_INVU: + TryPickupArtifact(player, arti_invulnerability, special); + return; + case SPR_SUMN: + TryPickupArtifact(player, arti_summon, special); + return; + case SPR_PORK: + TryPickupArtifact(player, arti_egg, special); + return; + case SPR_SPHL: + TryPickupArtifact(player, arti_superhealth, special); + return; + case SPR_HRAD: + TryPickupArtifact(player, arti_healingradius, special); + return; + case SPR_TRCH: + TryPickupArtifact(player, arti_torch, special); + return; + case SPR_ATLP: + TryPickupArtifact(player, arti_teleport, special); + return; + case SPR_TELO: + TryPickupArtifact(player, arti_teleportother, special); + return; + case SPR_PSBG: + TryPickupArtifact(player, arti_poisonbag, special); + return; + case SPR_SPED: + TryPickupArtifact(player, arti_speed, special); + return; + case SPR_BMAN: + TryPickupArtifact(player, arti_boostmana, special); + return; + case SPR_BRAC: + TryPickupArtifact(player, arti_boostarmor, special); + return; + case SPR_BLST: + TryPickupArtifact(player, arti_blastradius, special); + return; + + // Puzzle artifacts + case SPR_ASKU: + TryPickupArtifact(player, arti_puzzskull, special); + return; + case SPR_ABGM: + TryPickupArtifact(player, arti_puzzgembig, special); + return; + case SPR_AGMR: + TryPickupArtifact(player, arti_puzzgemred, special); + return; + case SPR_AGMG: + TryPickupArtifact(player, arti_puzzgemgreen1, special); + return; + case SPR_AGG2: + TryPickupArtifact(player, arti_puzzgemgreen2, special); + return; + case SPR_AGMB: + TryPickupArtifact(player, arti_puzzgemblue1, special); + return; + case SPR_AGB2: + TryPickupArtifact(player, arti_puzzgemblue2, special); + return; + case SPR_ABK1: + TryPickupArtifact(player, arti_puzzbook1, special); + return; + case SPR_ABK2: + TryPickupArtifact(player, arti_puzzbook2, special); + return; + case SPR_ASK2: + TryPickupArtifact(player, arti_puzzskull2, special); + return; + case SPR_AFWP: + TryPickupArtifact(player, arti_puzzfweapon, special); + return; + case SPR_ACWP: + TryPickupArtifact(player, arti_puzzcweapon, special); + return; + case SPR_AMWP: + TryPickupArtifact(player, arti_puzzmweapon, special); + return; + case SPR_AGER: + TryPickupArtifact(player, arti_puzzgear1, special); + return; + case SPR_AGR2: + TryPickupArtifact(player, arti_puzzgear2, special); + return; + case SPR_AGR3: + TryPickupArtifact(player, arti_puzzgear3, special); + return; + case SPR_AGR4: + TryPickupArtifact(player, arti_puzzgear4, special); + return; + + // Mana + case SPR_MAN1: + if(!P_GiveMana(player, MANA_1, 15)) + { + return; + } + P_SetMessage(player, TXT_MANA_1, false); + break; + case SPR_MAN2: + if(!P_GiveMana(player, MANA_2, 15)) + { + return; + } + P_SetMessage(player, TXT_MANA_2, false); + break; + case SPR_MAN3: // Double Mana Dodecahedron + if(!P_GiveMana(player, MANA_1, 20)) + { + if(!P_GiveMana(player, MANA_2, 20)) + { + return; + } + } + else + { + P_GiveMana(player, MANA_2, 20); + } + P_SetMessage(player, TXT_MANA_BOTH, false); + break; + + // 2nd and 3rd Mage Weapons + case SPR_WMCS: // Frost Shards + TryPickupWeapon(player, PCLASS_MAGE, WP_SECOND, + special, TXT_WEAPON_M2); + return; + case SPR_WMLG: // Arc of Death + TryPickupWeapon(player, PCLASS_MAGE, WP_THIRD, + special, TXT_WEAPON_M3); + return; + + // 2nd and 3rd Fighter Weapons + case SPR_WFAX: // Timon's Axe + TryPickupWeapon(player, PCLASS_FIGHTER, WP_SECOND, + special, TXT_WEAPON_F2); + return; + case SPR_WFHM: // Hammer of Retribution + TryPickupWeapon(player, PCLASS_FIGHTER, WP_THIRD, + special, TXT_WEAPON_F3); + return; + + // 2nd and 3rd Cleric Weapons + case SPR_WCSS: // Serpent Staff + TryPickupWeapon(player, PCLASS_CLERIC, WP_SECOND, + special, TXT_WEAPON_C2); + return; + case SPR_WCFM: // Firestorm + TryPickupWeapon(player, PCLASS_CLERIC, WP_THIRD, + special, TXT_WEAPON_C3); + return; + + // Fourth Weapon Pieces + case SPR_WFR1: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE1, + special); + return; + case SPR_WFR2: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE2, + special); + return; + case SPR_WFR3: + TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE3, + special); + return; + case SPR_WCH1: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE1, + special); + return; + case SPR_WCH2: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE2, + special); + return; + case SPR_WCH3: + TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE3, + special); + return; + case SPR_WMS1: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE1, + special); + return; + case SPR_WMS2: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE2, + special); + return; + case SPR_WMS3: + TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE3, + special); + return; + + default: + I_Error("P_SpecialThing: Unknown gettable thing"); + } + if(special->special) + { + P_ExecuteLineSpecial(special->special, special->args, NULL, + 0, toucher); + special->special = 0; + } + if(deathmatch && respawn && !(special->flags2&MF2_DROPPED)) + { + P_HideSpecialThing(special); + } + else + { + P_RemoveMobj(special); + } + player->bonuscount += BONUSADD; + if(player == &players[consoleplayer]) + { + S_StartSound(NULL, sound); + SB_PaletteFlash(false); + } +} + +// Search thinker list for minotaur +mobj_t *ActiveMinotaur(player_t *master) +{ + mobj_t *mo; + player_t *plr; + thinker_t *think; + unsigned int *starttime; + + for(think = thinkercap.next; think != &thinkercap; think = think->next) + { + if(think->function != P_MobjThinker) continue; + mo = (mobj_t *)think; + if(mo->type != MT_MINOTAUR) continue; + if(mo->health <= 0) continue; + if(!(mo->flags&MF_COUNTKILL)) continue; // for morphed minotaurs + if(mo->flags&MF_CORPSE) continue; + starttime = (unsigned int *)mo->args; + if ((leveltime - *starttime) >= MAULATORTICS) continue; + plr = ((mobj_t *)mo->special1)->player; + if(plr == master) return(mo); + } + return(NULL); +} + + +//--------------------------------------------------------------------------- +// +// PROC P_KillMobj +// +//--------------------------------------------------------------------------- + +void P_KillMobj(mobj_t *source, mobj_t *target) +{ + int dummy; + mobj_t *master; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_NOGRAVITY); + target->flags |= MF_CORPSE|MF_DROPOFF; + target->flags2 &= ~MF2_PASSMOBJ; + target->height >>= 2; + if((target->flags&MF_COUNTKILL || target->type == MT_ZBELL) + && target->special) + { // Initiate monster death actions + if(target->type == MT_SORCBOSS) + { + dummy = 0; + P_StartACS(target->special, 0, (byte *)&dummy, target, + NULL, 0); + } + else + { + P_ExecuteLineSpecial(target->special, target->args, + NULL, 0, target); + } + } + if(source && source->player) + { // Check for frag changes + if(target->player) + { + if(target == source) + { // Self-frag + target->player->frags[target->player-players]--; + if(cmdfrag && netgame + && source->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(source->player); + } + } + else + { + source->player->frags[target->player-players]++; + if(cmdfrag && netgame + && source->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(source->player); + } + } + } + } + if(target->player) + { // Player death + if(!source) + { // Self-frag + target->player->frags[target->player-players]--; + if(cmdfrag && netgame + && target->player == &players[consoleplayer]) + { // Send out a frag count packet + NET_SendFrags(target->player); + } + } + target->flags &= ~MF_SOLID; + target->flags2 &= ~MF2_FLY; + target->player->powers[pw_flight] = 0; + target->player->playerstate = PST_DEAD; + P_DropWeapon(target->player); + if(target->flags2&MF2_FIREDAMAGE) + { // Player flame death + switch(target->player->class) + { + case PCLASS_FIGHTER: + S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); + P_SetMobjState(target, S_PLAY_F_FDTH1); + return; + case PCLASS_CLERIC: + S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); + P_SetMobjState(target, S_PLAY_C_FDTH1); + return; + case PCLASS_MAGE: + S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); + P_SetMobjState(target, S_PLAY_M_FDTH1); + return; + default: + break; + } + } + if(target->flags2&MF2_ICEDAMAGE) + { // Player ice death + target->flags &= ~(7<flags |= MF_ICECORPSE; + switch(target->player->class) + { + case PCLASS_FIGHTER: + P_SetMobjState(target, S_FPLAY_ICE); + return; + case PCLASS_CLERIC: + P_SetMobjState(target, S_CPLAY_ICE); + return; + case PCLASS_MAGE: + P_SetMobjState(target, S_MPLAY_ICE); + return; + case PCLASS_PIG: + P_SetMobjState(target, S_PIG_ICE); + return; + default: + break; + } + } + } + if(target->flags2&MF2_FIREDAMAGE) + { + if(target->type == MT_FIGHTER_BOSS + || target->type == MT_CLERIC_BOSS + || target->type == MT_MAGE_BOSS) + { + switch(target->type) + { + case MT_FIGHTER_BOSS: + S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); + P_SetMobjState(target, S_PLAY_F_FDTH1); + return; + case MT_CLERIC_BOSS: + S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); + P_SetMobjState(target, S_PLAY_C_FDTH1); + return; + case MT_MAGE_BOSS: + S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); + P_SetMobjState(target, S_PLAY_M_FDTH1); + return; + default: + break; + } + } + else if(target->type == MT_TREEDESTRUCTIBLE) + { + P_SetMobjState(target, S_ZTREEDES_X1); + target->height = 24*FRACUNIT; + S_StartSound(target, SFX_TREE_EXPLODE); + return; + } + } + if(target->flags2&MF2_ICEDAMAGE) + { + target->flags |= MF_ICECORPSE; + switch(target->type) + { + case MT_BISHOP: + P_SetMobjState(target, S_BISHOP_ICE); + return; + case MT_CENTAUR: + case MT_CENTAURLEADER: + P_SetMobjState(target, S_CENTAUR_ICE); + return; + case MT_DEMON: + case MT_DEMON2: + P_SetMobjState(target, S_DEMON_ICE); + return; + case MT_SERPENT: + case MT_SERPENTLEADER: + P_SetMobjState(target, S_SERPENT_ICE); + return; + case MT_WRAITH: + case MT_WRAITHB: + P_SetMobjState(target, S_WRAITH_ICE); + return; + case MT_ETTIN: + P_SetMobjState(target, S_ETTIN_ICE1); + return; + case MT_FIREDEMON: + P_SetMobjState(target, S_FIRED_ICE1); + return; + case MT_FIGHTER_BOSS: + P_SetMobjState(target, S_FIGHTER_ICE); + return; + case MT_CLERIC_BOSS: + P_SetMobjState(target, S_CLERIC_ICE); + return; + case MT_MAGE_BOSS: + P_SetMobjState(target, S_MAGE_ICE); + return; + case MT_PIG: + P_SetMobjState(target, S_PIG_ICE); + return; + default: + target->flags &= ~MF_ICECORPSE; + break; + } + } + + if(target->type == MT_MINOTAUR) + { + master = (mobj_t *)target->special1; + if(master->health > 0) + { + if (!ActiveMinotaur(master->player)) + { + master->player->powers[pw_minotaur] = 0; + } + } + } + else if(target->type == MT_TREEDESTRUCTIBLE) + { + target->height = 24*FRACUNIT; + } + if(target->health < -(target->info->spawnhealth>>1) + && target->info->xdeathstate) + { // Extreme death + P_SetMobjState(target, target->info->xdeathstate); + } + else + { // Normal death + if ((target->type==MT_FIREDEMON) && + (target->z <= target->floorz + 2*FRACUNIT) && + (target->info->xdeathstate)) + { + // This is to fix the imps' staying in fall state + P_SetMobjState(target, target->info->xdeathstate); + } + else + { + P_SetMobjState(target, target->info->deathstate); + } + } + target->tics -= P_Random()&3; +// I_StartSound(&actor->r, actor->info->deathsound); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_MinotaurSlam +// +//--------------------------------------------------------------------------- + +void P_MinotaurSlam(mobj_t *source, mobj_t *target) +{ + angle_t angle; + fixed_t thrust; + + angle = R_PointToAngle2(source->x, source->y, target->x, target->y); + angle >>= ANGLETOFINESHIFT; + thrust = 16*FRACUNIT+(P_Random()<<10); + target->momx += FixedMul(thrust, finecosine[angle]); + target->momy += FixedMul(thrust, finesine[angle]); + P_DamageMobj(target, NULL, source, HITDICE(4)); + if(target->player) + { + target->reactiontime = 14+(P_Random()&7); + } + source->args[0] = 0; // Stop charging +} + + +//--------------------------------------------------------------------------- +// +// FUNC P_MorphPlayer +// +// Returns true if the player gets turned into a pig +// +//--------------------------------------------------------------------------- + +boolean P_MorphPlayer(player_t *player) +{ + mobj_t *pmo; + mobj_t *fog; + mobj_t *beastMo; + fixed_t x; + fixed_t y; + fixed_t z; + angle_t angle; + int oldFlags2; + + if(player->powers[pw_invulnerability]) + { // Immune when invulnerable + return(false); + } + if(player->morphTics) + { // Player is already a beast + return false; + } + pmo = player->mo; + x = pmo->x; + y = pmo->y; + z = pmo->z; + angle = pmo->angle; + oldFlags2 = pmo->flags2; + P_SetMobjState(pmo, S_FREETARGMOBJ); + fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + beastMo = P_SpawnMobj(x, y, z, MT_PIGPLAYER); + beastMo->special1 = player->readyweapon; + beastMo->angle = angle; + beastMo->player = player; + player->health = beastMo->health = MAXMORPHHEALTH; + player->mo = beastMo; + memset(&player->armorpoints[0], 0, NUMARMOR*sizeof(int)); + player->class = PCLASS_PIG; + if(oldFlags2&MF2_FLY) + { + beastMo->flags2 |= MF2_FLY; + } + player->morphTics = MORPHTICS; + P_ActivateMorphWeapon(player); + return(true); +} + +//--------------------------------------------------------------------------- +// +// FUNC P_MorphMonster +// +//--------------------------------------------------------------------------- + +boolean P_MorphMonster(mobj_t *actor) +{ + mobj_t *master, *monster, *fog; + mobjtype_t moType; + fixed_t x; + fixed_t y; + fixed_t z; + mobj_t oldMonster; + + if(actor->player) return(false); + if(!(actor->flags&MF_COUNTKILL)) return false; + if(actor->flags2&MF2_BOSS) return false; + moType = actor->type; + switch(moType) + { + case MT_PIG: + return(false); + case MT_FIGHTER_BOSS: + case MT_CLERIC_BOSS: + case MT_MAGE_BOSS: + return(false); + default: + break; + } + + oldMonster = *actor; + x = oldMonster.x; + y = oldMonster.y; + z = oldMonster.z; + P_RemoveMobjFromTIDList(actor); + P_SetMobjState(actor, S_FREETARGMOBJ); + fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG); + S_StartSound(fog, SFX_TELEPORT); + monster = P_SpawnMobj(x, y, z, MT_PIG); + monster->special2 = moType; + monster->special1 = MORPHTICS+P_Random(); + monster->flags |= (oldMonster.flags&MF_SHADOW); + monster->target = oldMonster.target; + monster->angle = oldMonster.angle; + monster->tid = oldMonster.tid; + monster->special = oldMonster.special; + P_InsertMobjIntoTIDList(monster, oldMonster.tid); + memcpy(monster->args, oldMonster.args, 5); + + // check for turning off minotaur power for active icon + if (moType==MT_MINOTAUR) + { + master = (mobj_t *)oldMonster.special1; + if(master->health > 0) + { + if (!ActiveMinotaur(master->player)) + { + master->player->powers[pw_minotaur] = 0; + } + } + } + return(true); +} + +//--------------------------------------------------------------------------- +// +// PROC P_AutoUseHealth +// +//--------------------------------------------------------------------------- + +void P_AutoUseHealth(player_t *player, int saveHealth) +{ + int i; + int count; + int normalCount; + int normalSlot=0; + int superCount; + int superSlot=0; + + normalCount = superCount = 0; + for(i = 0; i < player->inventorySlotNum; i++) + { + if(player->inventory[i].type == arti_health) + { + normalSlot = i; + normalCount = player->inventory[i].count; + } + else if(player->inventory[i].type == arti_superhealth) + { + superSlot = i; + superCount = player->inventory[i].count; + } + } + if((gameskill == sk_baby) && (normalCount*25 >= saveHealth)) + { // Use quartz flasks + count = (saveHealth+24)/25; + for(i = 0; i < count; i++) + { + player->health += 25; + P_PlayerRemoveArtifact(player, normalSlot); + } + } + else if(superCount*100 >= saveHealth) + { // Use mystic urns + count = (saveHealth+99)/100; + for(i = 0; i < count; i++) + { + player->health += 100; + P_PlayerRemoveArtifact(player, superSlot); + } + } + else if((gameskill == sk_baby) + && (superCount*100+normalCount*25 >= saveHealth)) + { // Use mystic urns and quartz flasks + count = (saveHealth+24)/25; + saveHealth -= count*25; + for(i = 0; i < count; i++) + { + player->health += 25; + P_PlayerRemoveArtifact(player, normalSlot); + } + count = (saveHealth+99)/100; + for(i = 0; i < count; i++) + { + player->health += 100; + P_PlayerRemoveArtifact(player, normalSlot); + } + } + player->mo->health = player->health; +} + +/* +================= += += 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 barrel explosions and other environmental stuff +================== +*/ + +void P_DamageMobj +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + int damage +) +{ + unsigned ang; + int saved; + fixed_t savedPercent; + player_t *player; + mobj_t *master; + fixed_t thrust; + int temp; + int i; + + if(!(target->flags&MF_SHOOTABLE)) + { + // Shouldn't happen + return; + } + if(target->health <= 0) + { + if (inflictor && inflictor->flags2&MF2_ICEDAMAGE) + { + return; + } + else if (target->flags&MF_ICECORPSE) // frozen + { + target->tics = 1; + target->momx = target->momy = 0; + } + return; + } + if ((target->flags2&MF2_INVULNERABLE) && damage < 10000) + { // mobj is invulnerable + if(target->player) return; // for player, no exceptions + if(inflictor) + { + switch(inflictor->type) + { + // These inflictors aren't foiled by invulnerability + case MT_HOLY_FX: + case MT_POISONCLOUD: + case MT_FIREBOMB: + break; + default: + return; + } + } + else + { + return; + } + } + if(target->player) + { + if(damage < 1000 && ((target->player->cheats&CF_GODMODE) + || target->player->powers[pw_invulnerability])) + { + return; + } + } + if(target->flags&MF_SKULLFLY) + { + target->momx = target->momy = target->momz = 0; + } + if(target->flags2&MF2_DORMANT) + { + // Invulnerable, and won't wake up + return; + } + player = target->player; + if(player && gameskill == sk_baby) + { + // Take half damage in trainer mode + damage >>= 1; + } + // Special damage types + if(inflictor) + { + switch(inflictor->type) + { + case MT_EGGFX: + if(player) + { + P_MorphPlayer(player); + } + else + { + P_MorphMonster(target); + } + return; // Always return + case MT_TELOTHER_FX1: + case MT_TELOTHER_FX2: + case MT_TELOTHER_FX3: + case MT_TELOTHER_FX4: + case MT_TELOTHER_FX5: + if ((target->flags&MF_COUNTKILL) && + (target->type != MT_SERPENT) && + (target->type != MT_SERPENTLEADER) && + (!(target->flags2 & MF2_BOSS))) + { + P_TeleportOther(target); + } + return; + case MT_MINOTAUR: + if(inflictor->flags&MF_SKULLFLY) + { // Slam only when in charge mode + P_MinotaurSlam(inflictor, target); + return; + } + break; + case MT_BISH_FX: + // Bishops are just too nasty + damage >>= 1; + break; + case MT_SHARDFX1: + switch(inflictor->special2) + { + case 3: + damage <<= 3; + break; + case 2: + damage <<= 2; + break; + case 1: + damage <<= 1; + break; + default: + break; + } + break; + case MT_CSTAFF_MISSILE: + // Cleric Serpent Staff does poison damage + if(target->player) + { + P_PoisonPlayer(target->player, source, 20); + damage >>= 1; + } + break; + case MT_ICEGUY_FX2: + damage >>= 1; + break; + case MT_POISONDART: + if(target->player) + { + P_PoisonPlayer(target->player, source, 20); + damage >>= 1; + } + break; + case MT_POISONCLOUD: + if(target->player) + { + if(target->player->poisoncount < 4) + { + P_PoisonDamage(target->player, source, + 15+(P_Random()&15), false); // Don't play painsound + P_PoisonPlayer(target->player, source, 50); + S_StartSound(target, SFX_PLAYER_POISONCOUGH); + } + return; + } + else if(!(target->flags&MF_COUNTKILL)) + { // only damage monsters/players with the poison cloud + return; + } + break; + case MT_FSWORD_MISSILE: + if(target->player) + { + damage -= damage>>2; + } + break; + default: + break; + } + } + // Push the target unless source is using the gauntlets + if(inflictor && (!source || !source->player) + && !(inflictor->flags2&MF2_NODMGTHRUST)) + { + ang = R_PointToAngle2(inflictor->x, inflictor->y, + target->x, target->y); + //thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + thrust = damage*(FRACUNIT>>3)*150/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) + { + savedPercent = AutoArmorSave[player->class] + +player->armorpoints[ARMOR_ARMOR]+player->armorpoints[ARMOR_SHIELD] + +player->armorpoints[ARMOR_HELMET] + +player->armorpoints[ARMOR_AMULET]; + if(savedPercent) + { // armor absorbed some damage + if(savedPercent > 100*FRACUNIT) + { + savedPercent = 100*FRACUNIT; + } + for(i = 0; i < NUMARMOR; i++) + { + if(player->armorpoints[i]) + { + player->armorpoints[i] -= + FixedDiv(FixedMul(damage<class][i]), 300*FRACUNIT); + if(player->armorpoints[i] < 2*FRACUNIT) + { + player->armorpoints[i] = 0; + } + } + } + saved = FixedDiv(FixedMul(damage< savedPercent*2) + { + saved = savedPercent*2; + } + damage -= saved>>FRACBITS; + } + if(damage >= player->health + && ((gameskill == sk_baby) || deathmatch) + && !player->morphTics) + { // Try to use some inventory health + P_AutoUseHealth(player, damage-player->health+1); + } + player->health -= damage; // mirror mobj health here for Dave + if(player->health < 0) + { + player->health = 0; + } + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + 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); + SB_PaletteFlash(false); + } + } + + // + // do the damage + // + target->health -= damage; + if(target->health <= 0) + { // Death + if(inflictor) + { // check for special fire damage or ice damage deaths + if(inflictor->flags2&MF2_FIREDAMAGE) + { + if(player && !player->morphTics) + { // Check for flame death + if(target->health > -50 && damage > 25) + { + target->flags2 |= MF2_FIREDAMAGE; + } + } + else + { + target->flags2 |= MF2_FIREDAMAGE; + } + } + else if(inflictor->flags2&MF2_ICEDAMAGE) + { + target->flags2 |= MF2_ICEDAMAGE; + } + } + if(source && (source->type == MT_MINOTAUR)) + { // Minotaur's kills go to his master + master = (mobj_t *)(source->special1); + // Make sure still alive and not a pointer to fighter head + if (master->player && (master->player->mo == master)) + { + source = master; + } + } + if(source && (source->player) && + (source->player->readyweapon == WP_FOURTH)) + { + // Always extreme death from fourth weapon + target->health = -5000; + } + P_KillMobj(source, target); + return; + } + if((P_Random() < target->info->painchance) + && !(target->flags&MF_SKULLFLY)) + { + if(inflictor && (inflictor->type >= MT_LIGHTNING_FLOOR + && inflictor->type <= MT_LIGHTNING_ZAP)) + { + if(P_Random() < 96) + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + } + else + { // "electrocute" the target + target->frame |= FF_FULLBRIGHT; + if(target->flags&MF_COUNTKILL && P_Random() < 128 + && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) + { + if ((target->type == MT_CENTAUR) || + (target->type == MT_CENTAURLEADER) || + (target->type == MT_ETTIN)) + { + S_StartSound(target, SFX_PUPPYBEAT); + } + } + } + } + else + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + if(inflictor && inflictor->type == MT_POISONCLOUD) + { + if(target->flags&MF_COUNTKILL && P_Random() < 128 + && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) + { + if ((target->type == MT_CENTAUR) || + (target->type == MT_CENTAURLEADER) || + (target->type == MT_ETTIN)) + { + S_StartSound(target, SFX_PUPPYBEAT); + } + } + } + } + } + target->reactiontime = 0; // we're awake now... + if(!target->threshold && source && !(source->flags2&MF2_BOSS) + && !(target->type == MT_BISHOP) && !(target->type == MT_MINOTAUR)) + { + // Target actor is not intent on another actor, + // so make him chase after source + if((target->type == MT_CENTAUR && source->type == MT_CENTAURLEADER) + || (target->type == MT_CENTAURLEADER + && source->type == MT_CENTAUR)) + { + return; + } + target->target = source; + target->threshold = BASETHRESHOLD; + if(target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + { + P_SetMobjState(target, target->info->seestate); + } + } +} + +//========================================================================== +// +// P_FallingDamage +// +//========================================================================== + +void P_FallingDamage(player_t *player) +{ + int damage; + int mom; + int dist; + + mom = abs(player->mo->momz); + dist = FixedMul(mom, 16*FRACUNIT/23); + + if(mom >= 63*FRACUNIT) + { // automatic death + P_DamageMobj(player->mo, NULL, NULL, 10000); + return; + } + damage = ((FixedMul(dist, dist)/10)>>FRACBITS)-24; + if(player->mo->momz > -39*FRACUNIT && damage > player->mo->health + && player->mo->health != 1) + { // No-death threshold + damage = player->mo->health-1; + } + S_StartSound(player->mo, SFX_PLAYER_LAND); + P_DamageMobj(player->mo, NULL, NULL, damage); +} + +//========================================================================== +// +// P_PoisonPlayer - Sets up all data concerning poisoning +// +//========================================================================== + +void P_PoisonPlayer(player_t *player, mobj_t *poisoner, int poison) +{ + if((player->cheats&CF_GODMODE) || player->powers[pw_invulnerability]) + { + return; + } + player->poisoncount += poison; + player->poisoner = poisoner; + if(player->poisoncount > 100) + { + player->poisoncount = 100; + } +} + +//========================================================================== +// +// P_PoisonDamage - Similar to P_DamageMobj +// +//========================================================================== + +void P_PoisonDamage(player_t *player, mobj_t *source, int damage, + boolean playPainSound) +{ + mobj_t *target; + mobj_t *inflictor; + + target = player->mo; + inflictor = source; + if(target->health <= 0) + { + return; + } + if(target->flags2&MF2_INVULNERABLE && damage < 10000) + { // mobj is invulnerable + return; + } + if(player && gameskill == sk_baby) + { + // Take half damage in trainer mode + damage >>= 1; + } + if(damage < 1000 && ((player->cheats&CF_GODMODE) + || player->powers[pw_invulnerability])) + { + return; + } + if(damage >= player->health + && ((gameskill == sk_baby) || deathmatch) + && !player->morphTics) + { // Try to use some inventory health + P_AutoUseHealth(player, damage-player->health+1); + } + player->health -= damage; // mirror mobj health here for Dave + if(player->health < 0) + { + player->health = 0; + } + player->attacker = source; + + // + // do the damage + // + target->health -= damage; + if(target->health <= 0) + { // Death + target->special1 = damage; + if(player && inflictor && !player->morphTics) + { // Check for flame death + if((inflictor->flags2&MF2_FIREDAMAGE) + && (target->health > -50) && (damage > 25)) + { + target->flags2 |= MF2_FIREDAMAGE; + } + if(inflictor->flags2&MF2_ICEDAMAGE) + { + target->flags2 |= MF2_ICEDAMAGE; + } + } + P_KillMobj(source, target); + return; + } + if(!(leveltime&63) && playPainSound) + { + P_SetMobjState(target, target->info->painstate); + } +/* + if((P_Random() < target->info->painchance) + && !(target->flags&MF_SKULLFLY)) + { + target->flags |= MF_JUSTHIT; // fight back! + P_SetMobjState(target, target->info->painstate); + } +*/ +} -- cgit v1.2.3