summaryrefslogtreecommitdiff
path: root/src/hexen/p_inter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hexen/p_inter.c')
-rw-r--r--src/hexen/p_inter.c2250
1 files changed, 2250 insertions, 0 deletions
diff --git a/src/hexen/p_inter.c b/src/hexen/p_inter.c
new file mode 100644
index 00000000..514d92a6
--- /dev/null
+++ b/src/hexen/p_inter.c
@@ -0,0 +1,2250 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+
+
+#include "h2def.h"
+#include "m_random.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "s_sound.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);
+
+//--------------------------------------------------------------------------
+//
+// PROC P_SetMessage
+//
+//--------------------------------------------------------------------------
+
+void P_SetMessage(player_t * player, char *message, boolean ultmsg)
+{
+ if ((player->ultimateMessage || !messageson) && !ultmsg)
+ {
+ return;
+ }
+ if (strlen(message) > 79)
+ {
+ strncpy(player->message, message, 80);
+ player->message[79] = 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)
+{
+ if ((player->ultimateMessage || !messageson) && !ultmsg)
+ {
+ return;
+ }
+ if (strlen(message) > 79)
+ {
+ strncpy(player->message, message, 80);
+ player->message[79] = 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 << key))
+ {
+ return false;
+ }
+ player->bonuscount += BONUSADD;
+ player->keys |= 1 << key;
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GivePower
+//
+// Returns true if power accepted.
+//
+//---------------------------------------------------------------------------
+
+boolean P_GivePower(player_t * player, powertype_t power)
+{
+ if (power == pw_invulnerability)
+ {
+ if (player->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 << MF_TRANSSHIFT); //no translation
+ target->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 << FRACBITS,
+ ArmorIncrement[player->class][i]),
+ 300 * FRACUNIT);
+ if (player->armorpoints[i] < 2 * FRACUNIT)
+ {
+ player->armorpoints[i] = 0;
+ }
+ }
+ }
+ saved = FixedDiv(FixedMul(damage << FRACBITS, savedPercent),
+ 100 * FRACUNIT);
+ if (saved > 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);
+ }
+*/
+}