ref: aa5529bc6cbd5424c33f624d8a33fc482d18c489
dir: /src/heretic/p_inter.c/
// 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.
//
//-----------------------------------------------------------------------------
// P_inter.c
#include "doomdef.h"
#include "deh_str.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_random.h"
#include "p_local.h"
#include "s_sound.h"
#define BONUSADD 6
int WeaponValue[] = {
1, // staff
3, // goldwand
4, // crossbow
5, // blaster
6, // skullrod
7, // phoenixrod
8, // mace
2, // gauntlets
0 // beak
};
int maxammo[NUMAMMO] = {
100, // gold wand
50, // crossbow
200, // blaster
200, // skull rod
20, // phoenix rod
150 // mace
};
int GetWeaponAmmo[NUMWEAPONS] = {
0, // staff
25, // gold wand
10, // crossbow
30, // blaster
50, // skull rod
2, // phoenix rod
50, // mace
0, // gauntlets
0 // beak
};
static weapontype_t GetAmmoChange[] = {
wp_goldwand,
wp_crossbow,
wp_blaster,
wp_skullrod,
wp_phoenixrod,
wp_mace
};
/*
static boolean GetAmmoChangePL1[NUMWEAPONS][NUMAMMO] =
{
// staff
{wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
// gold wand
{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
// crossbow
{-1, -1, wp_blaster, wp_skullrod, -1, -1},
// blaster
{-1, -1, -1, -1, -1, -1},
// skull rod
{-1, -1, -1, -1, -1, -1},
// phoenix rod
{-1, -1, -1, -1, -1, -1},
// mace
{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
// gauntlets
{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}
};
*/
/*
static boolean GetAmmoChangePL2[NUMWEAPONS][NUMAMMO] =
{
// staff
{wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod,
wp_mace},
// gold wand
{-1, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace},
// crossbow
{-1, -1, wp_blaster, wp_skullrod, wp_phoenixrod, -1},
// blaster
{-1, -1, -1, wp_skullrod, wp_phoenixrod, -1},
// skull rod
{-1, -1, -1, -1, -1, -1},
// phoenix rod
{-1, -1, -1, -1, -1, -1},
// mace
{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
// gauntlets
{-1, -1, -1, wp_skullrod, wp_phoenixrod, wp_mace}
};
*/
//--------------------------------------------------------------------------
//
// PROC P_SetMessage
//
//--------------------------------------------------------------------------
boolean ultimatemsg;
void P_SetMessage(player_t * player, char *message, boolean ultmsg)
{
extern boolean messageson;
if ((ultimatemsg || !messageson) && !ultmsg)
{
return;
}
player->message = message;
player->messageTics = MESSAGETICS;
BorderTopRefresh = true;
if (ultmsg)
{
ultimatemsg = true;
}
}
//--------------------------------------------------------------------------
//
// FUNC P_GiveAmmo
//
// Returns true if the player accepted the ammo, false if it was
// refused (player has maxammo[ammo]).
//
//--------------------------------------------------------------------------
boolean P_GiveAmmo(player_t * player, ammotype_t ammo, int count)
{
int prevAmmo;
//weapontype_t changeWeapon;
if (ammo == am_noammo)
{
return (false);
}
if (ammo < 0 || ammo > NUMAMMO)
{
I_Error("P_GiveAmmo: bad type %i", ammo);
}
if (player->ammo[ammo] == player->maxammo[ammo])
{
return (false);
}
if (gameskill == sk_baby || gameskill == sk_nightmare)
{ // extra ammo in baby mode and nightmare mode
count += count >> 1;
}
prevAmmo = player->ammo[ammo];
player->ammo[ammo] += count;
if (player->ammo[ammo] > player->maxammo[ammo])
{
player->ammo[ammo] = player->maxammo[ammo];
}
if (prevAmmo)
{
// Don't attempt to change weapons if the player already had
// ammo of the type just given
return (true);
}
if (player->readyweapon == wp_staff
|| player->readyweapon == wp_gauntlets)
{
if (player->weaponowned[GetAmmoChange[ammo]])
{
player->pendingweapon = GetAmmoChange[ammo];
}
}
/*
if(player->powers[pw_weaponlevel2])
{
changeWeapon = GetAmmoChangePL2[player->readyweapon][ammo];
}
else
{
changeWeapon = GetAmmoChangePL1[player->readyweapon][ammo];
}
if(changeWeapon != -1)
{
if(player->weaponowned[changeWeapon])
{
player->pendingweapon = changeWeapon;
}
}
*/
return (true);
}
//--------------------------------------------------------------------------
//
// FUNC P_GiveWeapon
//
// Returns true if the weapon or its ammo was accepted.
//
//--------------------------------------------------------------------------
boolean P_GiveWeapon(player_t * player, weapontype_t weapon)
{
boolean gaveAmmo;
boolean gaveWeapon;
if (netgame && !deathmatch)
{ // Cooperative net-game
if (player->weaponowned[weapon])
{
return (false);
}
player->bonuscount += BONUSADD;
player->weaponowned[weapon] = true;
P_GiveAmmo(player, wpnlev1info[weapon].ammo, GetWeaponAmmo[weapon]);
player->pendingweapon = weapon;
if (player == &players[consoleplayer])
{
S_StartSound(NULL, sfx_wpnup);
}
return (false);
}
gaveAmmo = P_GiveAmmo(player, wpnlev1info[weapon].ammo,
GetWeaponAmmo[weapon]);
if (player->weaponowned[weapon])
{
gaveWeapon = false;
}
else
{
gaveWeapon = true;
player->weaponowned[weapon] = true;
if (WeaponValue[weapon] > WeaponValue[player->readyweapon])
{ // Only switch to more powerful weapons
player->pendingweapon = weapon;
}
}
return (gaveWeapon || gaveAmmo);
}
//---------------------------------------------------------------------------
//
// 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->chickenTics)
{
max = MAXCHICKENHEALTH;
}
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, int armortype)
{
int hits;
hits = armortype * 100;
if (player->armorpoints >= hits)
{
return (false);
}
player->armortype = armortype;
player->armorpoints = hits;
return (true);
}
//---------------------------------------------------------------------------
//
// PROC P_GiveKey
//
//---------------------------------------------------------------------------
void P_GiveKey(player_t * player, keytype_t key)
{
extern int playerkeys;
extern vertex_t KeyPoints[];
if (player->keys[key])
{
return;
}
if (player == &players[consoleplayer])
{
playerkeys |= 1 << key;
KeyPoints[key].x = 0;
KeyPoints[key].y = 0;
}
player->bonuscount = BONUSADD;
player->keys[key] = 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;
return (true);
}
if (power == pw_weaponlevel2)
{
if (player->powers[power] > BLINKTHRESHOLD)
{ // Already have it
return (false);
}
player->powers[power] = WPNLEV2TICS;
return (true);
}
if (power == pw_invisibility)
{
if (player->powers[power] > BLINKTHRESHOLD)
{ // Already have it
return (false);
}
player->powers[power] = INVISTICS;
player->mo->flags |= MF_SHADOW;
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_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);
}
//---------------------------------------------------------------------------
//
// FUNC P_GiveArtifact
//
// Returns true if artifact accepted.
//
//---------------------------------------------------------------------------
boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo)
{
int i;
i = 0;
while (player->inventory[i].type != arti && i < player->inventorySlotNum)
{
i++;
}
if (i == player->inventorySlotNum)
{
player->inventory[i].count = 1;
player->inventory[i].type = arti;
player->inventorySlotNum++;
}
else
{
if (player->inventory[i].count >= 16)
{ // Player already has 16 of this item
return (false);
}
player->inventory[i].count++;
}
if (player->artifactCount == 0)
{
player->readyArtifact = arti;
}
player->artifactCount++;
if (mo && (mo->flags & MF_COUNTITEM))
{
player->itemcount++;
}
return (true);
}
//---------------------------------------------------------------------------
//
// PROC P_SetDormantArtifact
//
// Removes the MF_SPECIAL flag, and initiates the artifact pickup
// animation.
//
//---------------------------------------------------------------------------
void P_SetDormantArtifact(mobj_t * arti)
{
arti->flags &= ~MF_SPECIAL;
if (deathmatch && (arti->type != MT_ARTIINVULNERABILITY)
&& (arti->type != MT_ARTIINVISIBILITY))
{
P_SetMobjState(arti, S_DORMANTARTI1);
}
else
{ // Don't respawn
P_SetMobjState(arti, S_DEADARTI1);
}
S_StartSound(arti, sfx_artiup);
}
//---------------------------------------------------------------------------
//
// 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 P_HideSpecialThing
//
//----------------------------------------------------------------------------
void P_HideSpecialThing(mobj_t * thing)
{
thing->flags &= ~MF_SPECIAL;
thing->flags2 |= MF2_DONTDRAW;
P_SetMobjState(thing, S_HIDESPECIAL1);
}
//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing1
//
// Make a special thing visible again.
//
//---------------------------------------------------------------------------
void A_RestoreSpecialThing1(mobj_t * thing)
{
if (thing->type == MT_WMACE)
{ // Do random mace placement
P_RepositionMace(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)
{
int i;
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_itemup;
player = toucher->player;
respawn = true;
switch (special->sprite)
{
// Items
case SPR_PTN1: // Item_HealingPotion
if (!P_GiveBody(player, 10))
{
return;
}
P_SetMessage(player, DEH_String(TXT_ITEMHEALTH), false);
break;
case SPR_SHLD: // Item_Shield1
if (!P_GiveArmor(player, 1))
{
return;
}
P_SetMessage(player, DEH_String(TXT_ITEMSHIELD1), false);
break;
case SPR_SHD2: // Item_Shield2
if (!P_GiveArmor(player, 2))
{
return;
}
P_SetMessage(player, DEH_String(TXT_ITEMSHIELD2), false);
break;
case SPR_BAGH: // Item_BagOfHolding
if (!player->backpack)
{
for (i = 0; i < NUMAMMO; i++)
{
player->maxammo[i] *= 2;
}
player->backpack = true;
}
P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY);
P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY);
P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
P_SetMessage(player, DEH_String(TXT_ITEMBAGOFHOLDING), false);
break;
case SPR_SPMP: // Item_SuperMap
if (!P_GivePower(player, pw_allmap))
{
return;
}
P_SetMessage(player, DEH_String(TXT_ITEMSUPERMAP), false);
break;
// Keys
case SPR_BKYY: // Key_Blue
if (!player->keys[key_blue])
{
P_SetMessage(player, DEH_String(TXT_GOTBLUEKEY), false);
}
P_GiveKey(player, key_blue);
sound = sfx_keyup;
if (!netgame)
{
break;
}
return;
case SPR_CKYY: // Key_Yellow
if (!player->keys[key_yellow])
{
P_SetMessage(player, DEH_String(TXT_GOTYELLOWKEY), false);
}
sound = sfx_keyup;
P_GiveKey(player, key_yellow);
if (!netgame)
{
break;
}
return;
case SPR_AKYY: // Key_Green
if (!player->keys[key_green])
{
P_SetMessage(player, DEH_String(TXT_GOTGREENKEY), false);
}
sound = sfx_keyup;
P_GiveKey(player, key_green);
if (!netgame)
{
break;
}
return;
// Artifacts
case SPR_PTN2: // Arti_HealingPotion
if (P_GiveArtifact(player, arti_health, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIHEALTH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_SOAR: // Arti_Fly
if (P_GiveArtifact(player, arti_fly, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIFLY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_INVU: // Arti_Invulnerability
if (P_GiveArtifact(player, arti_invulnerability, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIINVULNERABILITY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_PWBK: // Arti_TomeOfPower
if (P_GiveArtifact(player, arti_tomeofpower, special))
{
P_SetMessage(player, DEH_String(TXT_ARTITOMEOFPOWER), false);
P_SetDormantArtifact(special);
}
return;
case SPR_INVS: // Arti_Invisibility
if (P_GiveArtifact(player, arti_invisibility, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIINVISIBILITY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_EGGC: // Arti_Egg
if (P_GiveArtifact(player, arti_egg, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIEGG), false);
P_SetDormantArtifact(special);
}
return;
case SPR_SPHL: // Arti_SuperHealth
if (P_GiveArtifact(player, arti_superhealth, special))
{
P_SetMessage(player, DEH_String(TXT_ARTISUPERHEALTH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_TRCH: // Arti_Torch
if (P_GiveArtifact(player, arti_torch, special))
{
P_SetMessage(player, DEH_String(TXT_ARTITORCH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_FBMB: // Arti_FireBomb
if (P_GiveArtifact(player, arti_firebomb, special))
{
P_SetMessage(player, DEH_String(TXT_ARTIFIREBOMB), false);
P_SetDormantArtifact(special);
}
return;
case SPR_ATLP: // Arti_Teleport
if (P_GiveArtifact(player, arti_teleport, special))
{
P_SetMessage(player, DEH_String(TXT_ARTITELEPORT), false);
P_SetDormantArtifact(special);
}
return;
// Ammo
case SPR_AMG1: // Ammo_GoldWandWimpy
if (!P_GiveAmmo(player, am_goldwand, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND1), false);
break;
case SPR_AMG2: // Ammo_GoldWandHefty
if (!P_GiveAmmo(player, am_goldwand, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND2), false);
break;
case SPR_AMM1: // Ammo_MaceWimpy
if (!P_GiveAmmo(player, am_mace, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOMACE1), false);
break;
case SPR_AMM2: // Ammo_MaceHefty
if (!P_GiveAmmo(player, am_mace, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOMACE2), false);
break;
case SPR_AMC1: // Ammo_CrossbowWimpy
if (!P_GiveAmmo(player, am_crossbow, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW1), false);
break;
case SPR_AMC2: // Ammo_CrossbowHefty
if (!P_GiveAmmo(player, am_crossbow, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW2), false);
break;
case SPR_AMB1: // Ammo_BlasterWimpy
if (!P_GiveAmmo(player, am_blaster, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOBLASTER1), false);
break;
case SPR_AMB2: // Ammo_BlasterHefty
if (!P_GiveAmmo(player, am_blaster, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOBLASTER2), false);
break;
case SPR_AMS1: // Ammo_SkullRodWimpy
if (!P_GiveAmmo(player, am_skullrod, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD1), false);
break;
case SPR_AMS2: // Ammo_SkullRodHefty
if (!P_GiveAmmo(player, am_skullrod, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD2), false);
break;
case SPR_AMP1: // Ammo_PhoenixRodWimpy
if (!P_GiveAmmo(player, am_phoenixrod, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD1), false);
break;
case SPR_AMP2: // Ammo_PhoenixRodHefty
if (!P_GiveAmmo(player, am_phoenixrod, special->health))
{
return;
}
P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD2), false);
break;
// Weapons
case SPR_WMCE: // Weapon_Mace
if (!P_GiveWeapon(player, wp_mace))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNMACE), false);
sound = sfx_wpnup;
break;
case SPR_WBOW: // Weapon_Crossbow
if (!P_GiveWeapon(player, wp_crossbow))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNCROSSBOW), false);
sound = sfx_wpnup;
break;
case SPR_WBLS: // Weapon_Blaster
if (!P_GiveWeapon(player, wp_blaster))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNBLASTER), false);
sound = sfx_wpnup;
break;
case SPR_WSKL: // Weapon_SkullRod
if (!P_GiveWeapon(player, wp_skullrod))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNSKULLROD), false);
sound = sfx_wpnup;
break;
case SPR_WPHX: // Weapon_PhoenixRod
if (!P_GiveWeapon(player, wp_phoenixrod))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNPHOENIXROD), false);
sound = sfx_wpnup;
break;
case SPR_WGNT: // Weapon_Gauntlets
if (!P_GiveWeapon(player, wp_gauntlets))
{
return;
}
P_SetMessage(player, DEH_String(TXT_WPNGAUNTLETS), false);
sound = sfx_wpnup;
break;
default:
I_Error("P_SpecialThing: Unknown gettable thing");
}
if (special->flags & MF_COUNTITEM)
{
player->itemcount++;
}
if (deathmatch && respawn && !(special->flags & MF_DROPPED))
{
P_HideSpecialThing(special);
}
else
{
P_RemoveMobj(special);
}
player->bonuscount += BONUSADD;
if (player == &players[consoleplayer])
{
S_StartSound(NULL, sound);
SB_PaletteFlash();
}
}
//---------------------------------------------------------------------------
//
// PROC P_KillMobj
//
//---------------------------------------------------------------------------
void P_KillMobj(mobj_t * source, mobj_t * target)
{
target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_NOGRAVITY);
target->flags |= MF_CORPSE | MF_DROPOFF;
target->flags2 &= ~MF2_PASSMOBJ;
target->height >>= 2;
if (source && source->player)
{
if (target->flags & MF_COUNTKILL)
{ // Count for intermission
source->player->killcount++;
}
if (target->player)
{ // Frag stuff
if (target == source)
{ // Self-frag
target->player->frags[target->player - players]--;
}
else
{
source->player->frags[target->player - players]++;
if (source->player == &players[consoleplayer])
{
S_StartSound(NULL, sfx_gfrag);
}
if (source->player->chickenTics)
{ // Make a super chicken
P_GivePower(source->player, pw_weaponlevel2);
}
}
}
}
else if (!netgame && (target->flags & MF_COUNTKILL))
{ // Count all monster deaths
players[0].killcount++;
}
if (target->player)
{
if (!source)
{ // Self-frag
target->player->frags[target->player - players]--;
}
target->flags &= ~MF_SOLID;
target->flags2 &= ~MF2_FLY;
target->player->powers[pw_flight] = 0;
target->player->powers[pw_weaponlevel2] = 0;
target->player->playerstate = PST_DEAD;
P_DropWeapon(target->player);
if (target->flags2 & MF2_FIREDAMAGE)
{ // Player flame death
P_SetMobjState(target, S_PLAY_FDTH1);
//S_StartSound(target, sfx_hedat1); // Burn sound
return;
}
}
if (target->health < -(target->info->spawnhealth >> 1)
&& target->info->xdeathstate)
{ // Extreme death
P_SetMobjState(target, target->info->xdeathstate);
}
else
{ // Normal death
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, NULL, HITDICE(6));
if (target->player)
{
target->reactiontime = 14 + (P_Random() & 7);
}
}
//---------------------------------------------------------------------------
//
// FUNC P_TouchWhirlwind
//
//---------------------------------------------------------------------------
void P_TouchWhirlwind(mobj_t * target)
{
int randVal;
target->angle += (P_Random() - P_Random()) << 20;
target->momx += (P_Random() - P_Random()) << 10;
target->momy += (P_Random() - P_Random()) << 10;
if (leveltime & 16 && !(target->flags2 & MF2_BOSS))
{
randVal = P_Random();
if (randVal > 160)
{
randVal = 160;
}
target->momz += randVal << 10;
if (target->momz > 12 * FRACUNIT)
{
target->momz = 12 * FRACUNIT;
}
}
if (!(leveltime & 7))
{
P_DamageMobj(target, NULL, NULL, 3);
}
}
//---------------------------------------------------------------------------
//
// FUNC P_ChickenMorphPlayer
//
// Returns true if the player gets turned into a chicken.
//
//---------------------------------------------------------------------------
boolean P_ChickenMorphPlayer(player_t * player)
{
mobj_t *pmo;
mobj_t *fog;
mobj_t *chicken;
fixed_t x;
fixed_t y;
fixed_t z;
angle_t angle;
int oldFlags2;
if (player->chickenTics)
{
if ((player->chickenTics < CHICKENTICS - TICRATE)
&& !player->powers[pw_weaponlevel2])
{ // Make a super chicken
P_GivePower(player, pw_weaponlevel2);
}
return (false);
}
if (player->powers[pw_invulnerability])
{ // Immune when invulnerable
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_telept);
chicken = P_SpawnMobj(x, y, z, MT_CHICPLAYER);
chicken->special1 = player->readyweapon;
chicken->angle = angle;
chicken->player = player;
player->health = chicken->health = MAXCHICKENHEALTH;
player->mo = chicken;
player->armorpoints = player->armortype = 0;
player->powers[pw_invisibility] = 0;
player->powers[pw_weaponlevel2] = 0;
if (oldFlags2 & MF2_FLY)
{
chicken->flags2 |= MF2_FLY;
}
player->chickenTics = CHICKENTICS;
P_ActivateBeak(player);
return (true);
}
//---------------------------------------------------------------------------
//
// FUNC P_ChickenMorph
//
//---------------------------------------------------------------------------
boolean P_ChickenMorph(mobj_t * actor)
{
mobj_t *fog;
mobj_t *chicken;
mobj_t *target;
mobjtype_t moType;
fixed_t x;
fixed_t y;
fixed_t z;
angle_t angle;
int ghost;
if (actor->player)
{
return (false);
}
moType = actor->type;
switch (moType)
{
case MT_POD:
case MT_CHICKEN:
case MT_HEAD:
case MT_MINOTAUR:
case MT_SORCERER1:
case MT_SORCERER2:
return (false);
default:
break;
}
x = actor->x;
y = actor->y;
z = actor->z;
angle = actor->angle;
ghost = actor->flags & MF_SHADOW;
target = actor->target;
P_SetMobjState(actor, S_FREETARGMOBJ);
fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG);
S_StartSound(fog, sfx_telept);
chicken = P_SpawnMobj(x, y, z, MT_CHICKEN);
chicken->special2 = moType;
chicken->special1 = CHICKENTICS + P_Random();
chicken->flags |= ghost;
chicken->target = target;
chicken->angle = angle;
return (true);
}
//---------------------------------------------------------------------------
//
// FUNC P_AutoUseChaosDevice
//
//---------------------------------------------------------------------------
boolean P_AutoUseChaosDevice(player_t * player)
{
int i;
for (i = 0; i < player->inventorySlotNum; i++)
{
if (player->inventory[i].type == arti_teleport)
{
P_PlayerUseArtifact(player, arti_teleport);
player->health = player->mo->health = (player->health + 1) / 2;
return (true);
}
}
return (false);
}
//---------------------------------------------------------------------------
//
// PROC P_AutoUseHealth
//
//---------------------------------------------------------------------------
void P_AutoUseHealth(player_t * player, int saveHealth)
{
int i;
int count;
int normalCount;
int normalSlot;
int superCount;
int superSlot;
normalCount = 0;
superCount = 0;
normalSlot = 0;
superSlot = 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;
player_t *player;
fixed_t thrust;
int temp;
if (!(target->flags & MF_SHOOTABLE))
{
// Shouldn't happen
return;
}
if (target->health <= 0)
{
return;
}
if (target->flags & MF_SKULLFLY)
{
if (target->type == MT_MINOTAUR)
{ // Minotaur is invulnerable during charge attack
return;
}
target->momx = target->momy = target->momz = 0;
}
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_ChickenMorphPlayer(player);
}
else
{
P_ChickenMorph(target);
}
return; // Always return
case MT_WHIRLWIND:
P_TouchWhirlwind(target);
return;
case MT_MINOTAUR:
if (inflictor->flags & MF_SKULLFLY)
{ // Slam only when in charge mode
P_MinotaurSlam(inflictor, target);
return;
}
break;
case MT_MACEFX4: // Death ball
if ((target->flags2 & MF2_BOSS) || target->type == MT_HEAD)
{ // Don't allow cheap boss kills
break;
}
else if (target->player)
{ // Player specific checks
if (target->player->powers[pw_invulnerability])
{ // Can't hurt invulnerable players
break;
}
if (P_AutoUseChaosDevice(target->player))
{ // Player was saved using chaos device
return;
}
}
damage = 10000; // Something's gonna die
break;
case MT_PHOENIXFX2: // Flame thrower
if (target->player && P_Random() < 128)
{ // Freeze player for a bit
target->reactiontime += 4;
}
break;
case MT_RAINPLR1: // Rain missiles
case MT_RAINPLR2:
case MT_RAINPLR3:
case MT_RAINPLR4:
if (target->flags2 & MF2_BOSS)
{ // Decrease damage for bosses
damage = (P_Random() & 7) + 1;
}
break;
case MT_HORNRODFX2:
case MT_PHOENIXFX1:
if (target->type == MT_SORCERER2 && P_Random() < 96)
{ // D'Sparil teleports away
P_DSparilTeleport(target);
return;
}
break;
case MT_BLASTERFX1:
case MT_RIPPER:
if (target->type == MT_HEAD)
{ // Less damage to Ironlich bosses
damage = P_Random() & 1;
if (!damage)
{
return;
}
}
break;
default:
break;
}
}
// Push the target unless source is using the gauntlets
if (inflictor && (!source || !source->player
|| source->player->readyweapon != wp_gauntlets)
&& !(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;
if (source && source->player && (source == inflictor)
&& source->player->powers[pw_weaponlevel2]
&& source->player->readyweapon == wp_staff)
{
// Staff power level 2
target->momx += FixedMul(10 * FRACUNIT, finecosine[ang]);
target->momy += FixedMul(10 * FRACUNIT, finesine[ang]);
if (!(target->flags & MF_NOGRAVITY))
{
target->momz += 5 * FRACUNIT;
}
}
else
{
target->momx += FixedMul(thrust, finecosine[ang]);
target->momy += FixedMul(thrust, finesine[ang]);
}
}
//
// player specific
//
if (player)
{
// end of game hell hack
//if(target->subsector->sector->special == 11
// && damage >= target->health)
//{
// damage = target->health - 1;
//}
if (damage < 1000 && ((player->cheats & CF_GODMODE)
|| player->powers[pw_invulnerability]))
{
return;
}
if (player->armortype)
{
if (player->armortype == 1)
{
saved = damage >> 1;
}
else
{
saved = (damage >> 1) + (damage >> 2);
}
if (player->armorpoints <= saved)
{
// armor is used up
saved = player->armorpoints;
player->armortype = 0;
}
player->armorpoints -= saved;
damage -= saved;
}
if (damage >= player->health
&& ((gameskill == sk_baby) || deathmatch) && !player->chickenTics)
{ // 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();
}
}
//
// do the damage
//
target->health -= damage;
if (target->health <= 0)
{ // Death
target->special1 = damage;
if (target->type == MT_POD && source && source->type != MT_POD)
{ // Make sure players get frags for chain-reaction kills
target->target = source;
}
if (player && inflictor && !player->chickenTics)
{ // Check for flame death
if ((inflictor->flags2 & MF2_FIREDAMAGE)
|| ((inflictor->type == MT_PHOENIXFX1)
&& (target->health > -50) && (damage > 25)))
{
target->flags2 |= MF2_FIREDAMAGE;
}
}
P_KillMobj(source, target);
return;
}
if ((P_Random() < target->info->painchance)
&& !(target->flags & MF_SKULLFLY))
{
target->flags |= MF_JUSTHIT; // fight back!
P_SetMobjState(target, target->info->painstate);
}
target->reactiontime = 0; // we're awake now...
if (!target->threshold && source && !(source->flags2 & MF2_BOSS)
&& !(target->type == MT_SORCERER2 && source->type == MT_WIZARD))
{
// Target actor is not intent on another actor,
// so make him chase after source
target->target = source;
target->threshold = BASETHRESHOLD;
if (target->state == &states[target->info->spawnstate]
&& target->info->seestate != S_NULL)
{
P_SetMobjState(target, target->info->seestate);
}
}
}