2253 lines
50 KiB
C
2253 lines
50 KiB
C
|
|
||
|
//**************************************************************************
|
||
|
//**
|
||
|
//** 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<<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);
|
||
|
}
|
||
|
*/
|
||
|
}
|