hexen/Hexen Source/P_USER.C
2008-09-04 00:00:00 +00:00

1652 lines
34 KiB
C

//**************************************************************************
//**
//** p_user.c : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: p_user.c,v $
//** $Revision: 1.123 $
//** $Date: 96/01/05 14:21:01 $
//** $Author: bgokey $
//**
//**************************************************************************
#include "h2def.h"
#include "p_local.h"
#include "soundst.h"
void P_PlayerNextArtifact(player_t *player);
// Macros
#define MAXBOB 0x100000 // 16 pixels of bob
// Data
boolean onground;
int newtorch; // used in the torch flicker effect.
int newtorchdelta;
int PStateNormal[NUMCLASSES] =
{
S_FPLAY,
S_CPLAY,
S_MPLAY,
S_PIGPLAY
};
int PStateRun[NUMCLASSES] =
{
S_FPLAY_RUN1,
S_CPLAY_RUN1,
S_MPLAY_RUN1,
S_PIGPLAY_RUN1
};
int PStateAttack[NUMCLASSES] =
{
S_FPLAY_ATK1,
S_CPLAY_ATK1,
S_MPLAY_ATK1,
S_PIGPLAY_ATK1
};
int PStateAttackEnd[NUMCLASSES] =
{
S_FPLAY_ATK2,
S_CPLAY_ATK3,
S_MPLAY_ATK2,
S_PIGPLAY_ATK1
};
int ArmorMax[NUMCLASSES] = { 20, 18, 16, 1 };
/*
==================
=
= P_Thrust
=
= moves the given origin along a given angle
=
==================
*/
void P_Thrust(player_t *player, angle_t angle, fixed_t move)
{
angle >>= ANGLETOFINESHIFT;
if(player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz))
{
player->mo->momx += FixedMul(move, finecosine[angle]);
player->mo->momy += FixedMul(move, finesine[angle]);
}
else if(P_GetThingFloorType(player->mo) == FLOOR_ICE) // Friction_Low
{
player->mo->momx += FixedMul(move>>1, finecosine[angle]);
player->mo->momy += FixedMul(move>>1, finesine[angle]);
}
else
{
player->mo->momx += FixedMul(move, finecosine[angle]);
player->mo->momy += FixedMul(move, finesine[angle]);
}
}
/*
==================
=
= P_CalcHeight
=
=
Calculate the walking / running height adjustment
=
==================
*/
void P_CalcHeight (player_t *player)
{
int angle;
fixed_t bob;
//
// regular movement bobbing (needs to be calculated for gun swing even
// if not on ground)
// OPTIMIZE: tablify angle
player->bob = FixedMul (player->mo->momx, player->mo->momx)+
FixedMul (player->mo->momy,player->mo->momy);
player->bob >>= 2;
if (player->bob>MAXBOB)
player->bob = MAXBOB;
if(player->mo->flags2&MF2_FLY && !onground)
{
player->bob = FRACUNIT/2;
}
if ((player->cheats & CF_NOMOMENTUM))
{
player->viewz = player->mo->z + VIEWHEIGHT;
if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
player->viewz = player->mo->ceilingz-4*FRACUNIT;
player->viewz = player->mo->z + player->viewheight;
return;
}
angle = (FINEANGLES/20*leveltime)&FINEMASK;
bob = FixedMul ( player->bob/2, finesine[angle]);
//
// move viewheight
//
if (player->playerstate == PST_LIVE)
{
player->viewheight += player->deltaviewheight;
if (player->viewheight > VIEWHEIGHT)
{
player->viewheight = VIEWHEIGHT;
player->deltaviewheight = 0;
}
if (player->viewheight < VIEWHEIGHT/2)
{
player->viewheight = VIEWHEIGHT/2;
if (player->deltaviewheight <= 0)
player->deltaviewheight = 1;
}
if (player->deltaviewheight)
{
player->deltaviewheight += FRACUNIT/4;
if (!player->deltaviewheight)
player->deltaviewheight = 1;
}
}
if(player->morphTics)
{
player->viewz = player->mo->z+player->viewheight-(20*FRACUNIT);
}
else
{
player->viewz = player->mo->z+player->viewheight+bob;
}
if(player->mo->floorclip && player->playerstate != PST_DEAD
&& player->mo->z <= player->mo->floorz)
{
player->viewz -= player->mo->floorclip;
}
if(player->viewz > player->mo->ceilingz-4*FRACUNIT)
{
player->viewz = player->mo->ceilingz-4*FRACUNIT;
}
if(player->viewz < player->mo->floorz+4*FRACUNIT)
{
player->viewz = player->mo->floorz+4*FRACUNIT;
}
}
/*
=================
=
= P_MovePlayer
=
=================
*/
void P_MovePlayer(player_t *player)
{
int look;
int fly;
ticcmd_t *cmd;
cmd = &player->cmd;
player->mo->angle += (cmd->angleturn<<16);
onground = (player->mo->z <= player->mo->floorz
|| (player->mo->flags2&MF2_ONMOBJ));
if(cmd->forwardmove)
{
if(onground || player->mo->flags2&MF2_FLY)
{
P_Thrust(player, player->mo->angle, cmd->forwardmove*2048);
}
else
{
P_Thrust(player, player->mo->angle, FRACUNIT>>8);
}
}
if(cmd->sidemove)
{
if(onground || player->mo->flags2&MF2_FLY)
{
P_Thrust(player, player->mo->angle-ANG90, cmd->sidemove*2048);
}
else
{
P_Thrust(player, player->mo->angle, FRACUNIT>>8);
}
}
if(cmd->forwardmove || cmd->sidemove)
{
if(player->mo->state == &states[PStateNormal[player->class]])
{
P_SetMobjState(player->mo, PStateRun[player->class]);
}
}
look = cmd->lookfly&15;
if(look > 7)
{
look -= 16;
}
if(look)
{
if(look == TOCENTER)
{
player->centering = true;
}
else
{
player->lookdir += 5*look;
if(player->lookdir > 90 || player->lookdir < -110)
{
player->lookdir -= 5*look;
}
}
}
if(player->centering)
{
if(player->lookdir > 0)
{
player->lookdir -= 8;
}
else if(player->lookdir < 0)
{
player->lookdir += 8;
}
if(abs(player->lookdir) < 8)
{
player->lookdir = 0;
player->centering = false;
}
}
fly = cmd->lookfly>>4;
if(fly > 7)
{
fly -= 16;
}
if(fly && player->powers[pw_flight])
{
if(fly != TOCENTER)
{
player->flyheight = fly*2;
if(!(player->mo->flags2&MF2_FLY))
{
player->mo->flags2 |= MF2_FLY;
player->mo->flags |= MF_NOGRAVITY;
if(player->mo->momz <= -39*FRACUNIT)
{ // stop falling scream
S_StopSound(player->mo);
}
}
}
else
{
player->mo->flags2 &= ~MF2_FLY;
player->mo->flags &= ~MF_NOGRAVITY;
}
}
else if(fly > 0)
{
P_PlayerUseArtifact(player, arti_fly);
}
if(player->mo->flags2&MF2_FLY)
{
player->mo->momz = player->flyheight*FRACUNIT;
if(player->flyheight)
{
player->flyheight /= 2;
}
}
}
//==========================================================================
//
// P_DeathThink
//
//==========================================================================
void P_DeathThink(player_t *player)
{
int dir;
angle_t delta;
int lookDelta;
extern int inv_ptr;
extern int curpos;
P_MovePsprites(player);
onground = (player->mo->z <= player->mo->floorz);
if(player->mo->type == MT_BLOODYSKULL || player->mo->type == MT_ICECHUNK)
{ // Flying bloody skull or flying ice chunk
player->viewheight = 6*FRACUNIT;
player->deltaviewheight = 0;
//player->damagecount = 20;
if(onground)
{
if(player->lookdir < 60)
{
lookDelta = (60-player->lookdir)/8;
if(lookDelta < 1 && (leveltime&1))
{
lookDelta = 1;
}
else if(lookDelta > 6)
{
lookDelta = 6;
}
player->lookdir += lookDelta;
}
}
}
else if(!(player->mo->flags2&MF2_ICEDAMAGE))
{ // Fall to ground (if not frozen)
player->deltaviewheight = 0;
if(player->viewheight > 6*FRACUNIT)
{
player->viewheight -= FRACUNIT;
}
if(player->viewheight < 6*FRACUNIT)
{
player->viewheight = 6*FRACUNIT;
}
if(player->lookdir > 0)
{
player->lookdir -= 6;
}
else if(player->lookdir < 0)
{
player->lookdir += 6;
}
if(abs(player->lookdir) < 6)
{
player->lookdir = 0;
}
}
P_CalcHeight(player);
if(player->attacker && player->attacker != player->mo)
{ // Watch killer
dir = P_FaceMobj(player->mo, player->attacker, &delta);
if(delta < ANGLE_1*10)
{ // Looking at killer, so fade damage and poison counters
if(player->damagecount)
{
player->damagecount--;
}
if(player->poisoncount)
{
player->poisoncount--;
}
}
delta = delta/8;
if(delta > ANGLE_1*5)
{
delta = ANGLE_1*5;
}
if(dir)
{ // Turn clockwise
player->mo->angle += delta;
}
else
{ // Turn counter clockwise
player->mo->angle -= delta;
}
}
else if(player->damagecount || player->poisoncount)
{
if(player->damagecount)
{
player->damagecount--;
}
else
{
player->poisoncount--;
}
}
if(player->cmd.buttons&BT_USE)
{
if(player == &players[consoleplayer])
{
I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE));
inv_ptr = 0;
curpos = 0;
newtorch = 0;
newtorchdelta = 0;
}
player->playerstate = PST_REBORN;
player->mo->special1 = player->class;
if(player->mo->special1 > 2)
{
player->mo->special1 = 0;
}
// Let the mobj know the player has entered the reborn state. Some
// mobjs need to know when it's ok to remove themselves.
player->mo->special2 = 666;
}
}
//----------------------------------------------------------------------------
//
// PROC P_MorphPlayerThink
//
//----------------------------------------------------------------------------
void P_MorphPlayerThink(player_t *player)
{
mobj_t *pmo;
if(player->morphTics&15)
{
return;
}
pmo = player->mo;
if(!(pmo->momx+pmo->momy) && P_Random() < 64)
{ // Snout sniff
P_SetPspriteNF(player, ps_weapon, S_SNOUTATK2);
S_StartSound(pmo, SFX_PIG_ACTIVE1); // snort
return;
}
if(P_Random() < 48)
{
if(P_Random() < 128)
{
S_StartSound(pmo, SFX_PIG_ACTIVE1);
}
else
{
S_StartSound(pmo, SFX_PIG_ACTIVE2);
}
}
}
//----------------------------------------------------------------------------
//
// FUNC P_GetPlayerNum
//
//----------------------------------------------------------------------------
int P_GetPlayerNum(player_t *player)
{
int i;
for(i = 0; i < MAXPLAYERS; i++)
{
if(player == &players[i])
{
return(i);
}
}
return(0);
}
//----------------------------------------------------------------------------
//
// FUNC P_UndoPlayerMorph
//
//----------------------------------------------------------------------------
boolean P_UndoPlayerMorph(player_t *player)
{
mobj_t *fog;
mobj_t *mo;
mobj_t *pmo;
fixed_t x;
fixed_t y;
fixed_t z;
angle_t angle;
int playerNum;
weapontype_t weapon;
int oldFlags;
int oldFlags2;
int oldBeast;
pmo = player->mo;
x = pmo->x;
y = pmo->y;
z = pmo->z;
angle = pmo->angle;
weapon = pmo->special1;
oldFlags = pmo->flags;
oldFlags2 = pmo->flags2;
oldBeast = pmo->type;
P_SetMobjState(pmo, S_FREETARGMOBJ);
playerNum = P_GetPlayerNum(player);
switch(PlayerClass[playerNum])
{
case PCLASS_FIGHTER:
mo = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER);
break;
case PCLASS_CLERIC:
mo = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC);
break;
case PCLASS_MAGE:
mo = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE);
break;
default:
I_Error("P_UndoPlayerMorph: Unknown player class %d\n",
player->class);
}
if(P_TestMobjLocation(mo) == false)
{ // Didn't fit
P_RemoveMobj(mo);
mo = P_SpawnMobj(x, y, z, oldBeast);
mo->angle = angle;
mo->health = player->health;
mo->special1 = weapon;
mo->player = player;
mo->flags = oldFlags;
mo->flags2 = oldFlags2;
player->mo = mo;
player->morphTics = 2*35;
return(false);
}
if(player->class == PCLASS_FIGHTER)
{
// The first type should be blue, and the third should be the
// Fighter's original gold color
if(playerNum == 0)
{
mo->flags |= 2<<MF_TRANSSHIFT;
}
else if(playerNum != 2)
{
mo->flags |= playerNum<<MF_TRANSSHIFT;
}
}
else if(playerNum)
{ // Set color translation bits for player sprites
mo->flags |= playerNum<<MF_TRANSSHIFT;
}
mo->angle = angle;
mo->player = player;
mo->reactiontime = 18;
if(oldFlags2&MF2_FLY)
{
mo->flags2 |= MF2_FLY;
mo->flags |= MF_NOGRAVITY;
}
player->morphTics = 0;
player->health = mo->health = MAXHEALTH;
player->mo = mo;
player->class = PlayerClass[playerNum];
angle >>= ANGLETOFINESHIFT;
fog = P_SpawnMobj(x+20*finecosine[angle],
y+20*finesine[angle], z+TELEFOGHEIGHT, MT_TFOG);
S_StartSound(fog, SFX_TELEPORT);
P_PostMorphWeapon(player, weapon);
return(true);
}
//----------------------------------------------------------------------------
//
// PROC P_PlayerThink
//
//----------------------------------------------------------------------------
void P_PlayerThink(player_t *player)
{
ticcmd_t *cmd;
weapontype_t newweapon;
int floorType;
mobj_t *pmo;
// No-clip cheat
if(player->cheats&CF_NOCLIP)
{
player->mo->flags |= MF_NOCLIP;
}
else
{
player->mo->flags &= ~MF_NOCLIP;
}
cmd = &player->cmd;
if(player->mo->flags&MF_JUSTATTACKED)
{ // Gauntlets attack auto forward motion
cmd->angleturn = 0;
cmd->forwardmove = 0xc800/512;
cmd->sidemove = 0;
player->mo->flags &= ~MF_JUSTATTACKED;
}
// messageTics is above the rest of the counters so that messages will
// go away, even in death.
player->messageTics--; // Can go negative
if(!player->messageTics || player->messageTics == -1)
{ // Refresh the screen when a message goes away
player->ultimateMessage = false; // clear out any chat messages.
player->yellowMessage = false;
if(player == &players[consoleplayer])
{
BorderTopRefresh = true;
}
}
player->worldTimer++;
if(player->playerstate == PST_DEAD)
{
P_DeathThink(player);
return;
}
if(player->jumpTics)
{
player->jumpTics--;
}
if(player->morphTics)
{
P_MorphPlayerThink(player);
}
// Handle movement
if(player->mo->reactiontime)
{ // Player is frozen
player->mo->reactiontime--;
}
else
{
P_MovePlayer(player);
pmo = player->mo;
if(player->powers[pw_speed] && !(leveltime&1)
&& P_AproxDistance(pmo->momx, pmo->momy) > 12*FRACUNIT)
{
mobj_t *speedMo;
int playerNum;
speedMo = P_SpawnMobj(pmo->x, pmo->y, pmo->z, MT_PLAYER_SPEED);
if(speedMo)
{
speedMo->angle = pmo->angle;
playerNum = P_GetPlayerNum(player);
if(player->class == PCLASS_FIGHTER)
{
// The first type should be blue, and the
// third should be the Fighter's original gold color
if(playerNum == 0)
{
speedMo->flags |= 2<<MF_TRANSSHIFT;
}
else if(playerNum != 2)
{
speedMo->flags |= playerNum<<MF_TRANSSHIFT;
}
}
else if(playerNum)
{ // Set color translation bits for player sprites
speedMo->flags |= playerNum<<MF_TRANSSHIFT;
}
speedMo->target = pmo;
speedMo->special1 = player->class;
if(speedMo->special1 > 2)
{
speedMo->special1 = 0;
}
speedMo->sprite = pmo->sprite;
speedMo->floorclip = pmo->floorclip;
if(player == &players[consoleplayer])
{
speedMo->flags2 |= MF2_DONTDRAW;
}
}
}
}
P_CalcHeight(player);
if(player->mo->subsector->sector->special)
{
P_PlayerInSpecialSector(player);
}
if((floorType = P_GetThingFloorType(player->mo)) != FLOOR_SOLID)
{
P_PlayerOnSpecialFlat(player, floorType);
}
switch(player->class)
{
case PCLASS_FIGHTER:
if(player->mo->momz <= -35*FRACUNIT
&& player->mo->momz >= -40*FRACUNIT && !player->morphTics
&& !S_GetSoundPlayingInfo(player->mo,
SFX_PLAYER_FIGHTER_FALLING_SCREAM))
{
S_StartSound(player->mo,
SFX_PLAYER_FIGHTER_FALLING_SCREAM);
}
break;
case PCLASS_CLERIC:
if(player->mo->momz <= -35*FRACUNIT
&& player->mo->momz >= -40*FRACUNIT && !player->morphTics
&& !S_GetSoundPlayingInfo(player->mo,
SFX_PLAYER_CLERIC_FALLING_SCREAM))
{
S_StartSound(player->mo,
SFX_PLAYER_CLERIC_FALLING_SCREAM);
}
break;
case PCLASS_MAGE:
if(player->mo->momz <= -35*FRACUNIT
&& player->mo->momz >= -40*FRACUNIT && !player->morphTics
&& !S_GetSoundPlayingInfo(player->mo,
SFX_PLAYER_MAGE_FALLING_SCREAM))
{
S_StartSound(player->mo,
SFX_PLAYER_MAGE_FALLING_SCREAM);
}
break;
default:
break;
}
if(cmd->arti)
{ // Use an artifact
if((cmd->arti&AFLAG_JUMP) && onground && !player->jumpTics)
{
if(player->morphTics)
{
player->mo->momz = 6*FRACUNIT;
}
else
{
player->mo->momz = 9*FRACUNIT;
}
player->mo->flags2 &= ~MF2_ONMOBJ;
player->jumpTics = 18;
}
else if(cmd->arti&AFLAG_SUICIDE)
{
P_DamageMobj(player->mo, NULL, NULL, 10000);
}
if(cmd->arti == NUMARTIFACTS)
{ // use one of each artifact (except puzzle artifacts)
int i;
for(i = 1; i < arti_firstpuzzitem; i++)
{
P_PlayerUseArtifact(player, i);
}
}
else
{
P_PlayerUseArtifact(player, cmd->arti&AFLAG_MASK);
}
}
// Check for weapon change
if(cmd->buttons&BT_SPECIAL)
{ // A special event has no other buttons
cmd->buttons = 0;
}
if(cmd->buttons&BT_CHANGE && !player->morphTics)
{
// The actual changing of the weapon is done when the weapon
// psprite can do it (A_WeaponReady), so it doesn't happen in
// the middle of an attack.
newweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT;
if(player->weaponowned[newweapon]
&& newweapon != player->readyweapon)
{
player->pendingweapon = newweapon;
}
}
// Check for use
if(cmd->buttons&BT_USE)
{
if(!player->usedown)
{
P_UseLines(player);
player->usedown = true;
}
}
else
{
player->usedown = false;
}
// Morph counter
if(player->morphTics)
{
if(!--player->morphTics)
{ // Attempt to undo the pig
P_UndoPlayerMorph(player);
}
}
// Cycle psprites
P_MovePsprites(player);
// Other Counters
if(player->powers[pw_invulnerability])
{
if(player->class == PCLASS_CLERIC)
{
if(!(leveltime&7) && player->mo->flags&MF_SHADOW
&& !(player->mo->flags2&MF2_DONTDRAW))
{
player->mo->flags &= ~MF_SHADOW;
if(!(player->mo->flags&MF_ALTSHADOW))
{
player->mo->flags2 |= MF2_DONTDRAW|MF2_NONSHOOTABLE;
}
}
if(!(leveltime&31))
{
if(player->mo->flags2&MF2_DONTDRAW)
{
if(!(player->mo->flags&MF_SHADOW))
{
player->mo->flags |= MF_SHADOW|MF_ALTSHADOW;
}
else
{
player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE);
}
}
else
{
player->mo->flags |= MF_SHADOW;
player->mo->flags &= ~MF_ALTSHADOW;
}
}
}
if(!(--player->powers[pw_invulnerability]))
{
player->mo->flags2 &= ~(MF2_INVULNERABLE|MF2_REFLECTIVE);
if(player->class == PCLASS_CLERIC)
{
player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE);
player->mo->flags &= ~(MF_SHADOW|MF_ALTSHADOW);
}
}
}
if(player->powers[pw_minotaur])
{
player->powers[pw_minotaur]--;
}
if(player->powers[pw_infrared])
{
player->powers[pw_infrared]--;
}
if(player->powers[pw_flight] && netgame)
{
if(!--player->powers[pw_flight])
{
if(player->mo->z != player->mo->floorz)
{
#ifdef __WATCOMC__
if(!useexterndriver)
{
player->centering = true;
}
#else
player->centering = true;
#endif
}
player->mo->flags2 &= ~MF2_FLY;
player->mo->flags &= ~MF_NOGRAVITY;
BorderTopRefresh = true; //make sure the sprite's cleared out
}
}
if(player->powers[pw_speed])
{
player->powers[pw_speed]--;
}
if(player->damagecount)
{
player->damagecount--;
}
if(player->bonuscount)
{
player->bonuscount--;
}
if(player->poisoncount && !(leveltime&15))
{
player->poisoncount -= 5;
if(player->poisoncount < 0)
{
player->poisoncount = 0;
}
P_PoisonDamage(player, player->poisoner, 1, true);
}
// Colormaps
// if(player->powers[pw_invulnerability])
// {
// if(player->powers[pw_invulnerability] > BLINKTHRESHOLD
// || (player->powers[pw_invulnerability]&8))
// {
// player->fixedcolormap = INVERSECOLORMAP;
// }
// else
// {
// player->fixedcolormap = 0;
// }
// }
// else
if(player->powers[pw_infrared])
{
if (player->powers[pw_infrared] <= BLINKTHRESHOLD)
{
if(player->powers[pw_infrared]&8)
{
player->fixedcolormap = 0;
}
else
{
player->fixedcolormap = 1;
}
}
else if(!(leveltime&16) && player == &players[consoleplayer])
{
if(newtorch)
{
if(player->fixedcolormap+newtorchdelta > 7
|| player->fixedcolormap+newtorchdelta < 1
|| newtorch == player->fixedcolormap)
{
newtorch = 0;
}
else
{
player->fixedcolormap += newtorchdelta;
}
}
else
{
newtorch = (M_Random()&7)+1;
newtorchdelta = (newtorch == player->fixedcolormap) ?
0 : ((newtorch > player->fixedcolormap) ? 1 : -1);
}
}
}
else
{
player->fixedcolormap = 0;
}
}
//----------------------------------------------------------------------------
//
// PROC P_ArtiTele
//
//----------------------------------------------------------------------------
void P_ArtiTele(player_t *player)
{
int i;
int selections;
fixed_t destX;
fixed_t destY;
angle_t destAngle;
if(deathmatch)
{
selections = deathmatch_p-deathmatchstarts;
i = P_Random()%selections;
destX = deathmatchstarts[i].x<<FRACBITS;
destY = deathmatchstarts[i].y<<FRACBITS;
destAngle = ANG45*(deathmatchstarts[i].angle/45);
}
else
{
destX = playerstarts[0][0].x<<FRACBITS;
destY = playerstarts[0][0].y<<FRACBITS;
destAngle = ANG45*(playerstarts[0][0].angle/45);
}
P_Teleport(player->mo, destX, destY, destAngle, true);
if(player->morphTics)
{ // Teleporting away will undo any morph effects (pig)
P_UndoPlayerMorph(player);
}
//S_StartSound(NULL, sfx_wpnup); // Full volume laugh
}
//----------------------------------------------------------------------------
//
// PROC P_ArtiTeleportOther
//
//----------------------------------------------------------------------------
void P_ArtiTeleportOther(player_t *player)
{
mobj_t *mo;
mo=P_SpawnPlayerMissile(player->mo, MT_TELOTHER_FX1);
if (mo)
{
mo->target = player->mo;
}
}
void P_TeleportToPlayerStarts(mobj_t *victim)
{
int i,selections=0;
fixed_t destX,destY;
angle_t destAngle;
for (i=0;i<MAXPLAYERS;i++)
{
if (!playeringame[i]) continue;
selections++;
}
i = P_Random()%selections;
destX = playerstarts[0][i].x<<FRACBITS;
destY = playerstarts[0][i].y<<FRACBITS;
destAngle = ANG45*(playerstarts[0][i].angle/45);
P_Teleport(victim, destX, destY, destAngle, true);
//S_StartSound(NULL, sfx_wpnup); // Full volume laugh
}
void P_TeleportToDeathmatchStarts(mobj_t *victim)
{
int i,selections;
fixed_t destX,destY;
angle_t destAngle;
selections = deathmatch_p-deathmatchstarts;
if (selections)
{
i = P_Random()%selections;
destX = deathmatchstarts[i].x<<FRACBITS;
destY = deathmatchstarts[i].y<<FRACBITS;
destAngle = ANG45*(deathmatchstarts[i].angle/45);
P_Teleport(victim, destX, destY, destAngle, true);
//S_StartSound(NULL, sfx_wpnup); // Full volume laugh
}
else
{
P_TeleportToPlayerStarts(victim);
}
}
//----------------------------------------------------------------------------
//
// PROC P_TeleportOther
//
//----------------------------------------------------------------------------
void P_TeleportOther(mobj_t *victim)
{
if (victim->player)
{
if (deathmatch)
P_TeleportToDeathmatchStarts(victim);
else
P_TeleportToPlayerStarts(victim);
}
else
{
// If death action, run it upon teleport
if (victim->flags&MF_COUNTKILL && victim->special)
{
P_RemoveMobjFromTIDList(victim);
P_ExecuteLineSpecial(victim->special, victim->args,
NULL, 0, victim);
victim->special = 0;
}
// Send all monsters to deathmatch spots
P_TeleportToDeathmatchStarts(victim);
}
}
#define BLAST_RADIUS_DIST 255*FRACUNIT
#define BLAST_SPEED 20*FRACUNIT
#define BLAST_FULLSTRENGTH 255
void ResetBlasted(mobj_t *mo)
{
mo->flags2 &= ~MF2_BLASTED;
if (!(mo->flags&MF_ICECORPSE))
{
mo->flags2 &= ~MF2_SLIDE;
}
}
void P_BlastMobj(mobj_t *source, mobj_t *victim, fixed_t strength)
{
angle_t angle,ang;
mobj_t *mo;
fixed_t x,y,z;
angle = R_PointToAngle2(source->x, source->y, victim->x, victim->y);
angle >>= ANGLETOFINESHIFT;
if (strength < BLAST_FULLSTRENGTH)
{
victim->momx = FixedMul(strength, finecosine[angle]);
victim->momy = FixedMul(strength, finesine[angle]);
if (victim->player)
{
// Players handled automatically
}
else
{
victim->flags2 |= MF2_SLIDE;
victim->flags2 |= MF2_BLASTED;
}
}
else // full strength blast from artifact
{
if (victim->flags&MF_MISSILE)
{
switch(victim->type)
{
case MT_SORCBALL1: // don't blast sorcerer balls
case MT_SORCBALL2:
case MT_SORCBALL3:
return;
break;
case MT_MSTAFF_FX2: // Reflect to originator
victim->special1 = (int)victim->target;
victim->target = source;
break;
default:
break;
}
}
if (victim->type == MT_HOLY_FX)
{
if ((mobj_t *)(victim->special1) == source)
{
victim->special1 = (int)victim->target;
victim->target = source;
}
}
victim->momx = FixedMul(BLAST_SPEED, finecosine[angle]);
victim->momy = FixedMul(BLAST_SPEED, finesine[angle]);
// Spawn blast puff
ang = R_PointToAngle2(victim->x, victim->y, source->x, source->y);
ang >>= ANGLETOFINESHIFT;
x = victim->x + FixedMul(victim->radius+FRACUNIT, finecosine[ang]);
y = victim->y + FixedMul(victim->radius+FRACUNIT, finesine[ang]);
z = victim->z - victim->floorclip + (victim->height>>1);
mo=P_SpawnMobj(x, y, z, MT_BLASTEFFECT);
if (mo)
{
mo->momx = victim->momx;
mo->momy = victim->momy;
}
if (victim->flags&MF_MISSILE)
{
victim->momz = 8*FRACUNIT;
mo->momz = victim->momz;
}
else
{
victim->momz = (1000/victim->info->mass)<<FRACBITS;
}
if (victim->player)
{
// Players handled automatically
}
else
{
victim->flags2 |= MF2_SLIDE;
victim->flags2 |= MF2_BLASTED;
}
}
}
// Blast all mobj things away
void P_BlastRadius(player_t *player)
{
mobj_t *mo;
mobj_t *pmo=player->mo;
thinker_t *think;
fixed_t dist;
S_StartSound(pmo, SFX_ARTIFACT_BLAST);
P_NoiseAlert(player->mo, player->mo);
for(think = thinkercap.next; think != &thinkercap; think = think->next)
{
if(think->function != P_MobjThinker)
{ // Not a mobj thinker
continue;
}
mo = (mobj_t *)think;
if((mo == pmo) || (mo->flags2&MF2_BOSS))
{ // Not a valid monster
continue;
}
if ((mo->type == MT_POISONCLOUD) || // poison cloud
(mo->type == MT_HOLY_FX) || // holy fx
(mo->flags&MF_ICECORPSE)) // frozen corpse
{
// Let these special cases go
}
else if ((mo->flags&MF_COUNTKILL) &&
(mo->health <= 0))
{
continue;
}
else if (!(mo->flags&MF_COUNTKILL) &&
!(mo->player) &&
!(mo->flags&MF_MISSILE))
{ // Must be monster, player, or missile
continue;
}
if (mo->flags2&MF2_DORMANT)
{
continue; // no dormant creatures
}
if ((mo->type == MT_WRAITHB) && (mo->flags2&MF2_DONTDRAW))
{
continue; // no underground wraiths
}
if ((mo->type == MT_SPLASHBASE) ||
(mo->type == MT_SPLASH))
{
continue;
}
if(mo->type == MT_SERPENT || mo->type == MT_SERPENTLEADER)
{
continue;
}
dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y);
if(dist > BLAST_RADIUS_DIST)
{ // Out of range
continue;
}
P_BlastMobj(pmo, mo, BLAST_FULLSTRENGTH);
}
}
#define HEAL_RADIUS_DIST 255*FRACUNIT
// Do class specific effect for everyone in radius
boolean P_HealRadius(player_t *player)
{
mobj_t *mo;
mobj_t *pmo=player->mo;
thinker_t *think;
fixed_t dist;
int effective=false;
int amount;
for(think = thinkercap.next; think != &thinkercap; think = think->next)
{
if(think->function != P_MobjThinker)
{ // Not a mobj thinker
continue;
}
mo = (mobj_t *)think;
if (!mo->player) continue;
if (mo->health <= 0) continue;
dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y);
if(dist > HEAL_RADIUS_DIST)
{ // Out of range
continue;
}
switch(player->class)
{
case PCLASS_FIGHTER: // Radius armor boost
if ((P_GiveArmor(mo->player, ARMOR_ARMOR, 1)) ||
(P_GiveArmor(mo->player, ARMOR_SHIELD, 1)) ||
(P_GiveArmor(mo->player, ARMOR_HELMET, 1)) ||
(P_GiveArmor(mo->player, ARMOR_AMULET, 1)))
{
effective=true;
S_StartSound(mo, SFX_MYSTICINCANT);
}
break;
case PCLASS_CLERIC: // Radius heal
amount = 50 + (P_Random()%50);
if (P_GiveBody(mo->player, amount))
{
effective=true;
S_StartSound(mo, SFX_MYSTICINCANT);
}
break;
case PCLASS_MAGE: // Radius mana boost
amount = 50 + (P_Random()%50);
if ((P_GiveMana(mo->player, MANA_1, amount)) ||
(P_GiveMana(mo->player, MANA_2, amount)))
{
effective=true;
S_StartSound(mo, SFX_MYSTICINCANT);
}
break;
case PCLASS_PIG:
default:
break;
}
}
return(effective);
}
//----------------------------------------------------------------------------
//
// PROC P_PlayerNextArtifact
//
//----------------------------------------------------------------------------
void P_PlayerNextArtifact(player_t *player)
{
extern int inv_ptr;
extern int curpos;
if(player == &players[consoleplayer])
{
inv_ptr--;
if(inv_ptr < 6)
{
curpos--;
if(curpos < 0)
{
curpos = 0;
}
}
if(inv_ptr < 0)
{
inv_ptr = player->inventorySlotNum-1;
if(inv_ptr < 6)
{
curpos = inv_ptr;
}
else
{
curpos = 6;
}
}
player->readyArtifact =
player->inventory[inv_ptr].type;
}
}
//----------------------------------------------------------------------------
//
// PROC P_PlayerRemoveArtifact
//
//----------------------------------------------------------------------------
void P_PlayerRemoveArtifact(player_t *player, int slot)
{
int i;
extern int inv_ptr;
extern int curpos;
player->artifactCount--;
if(!(--player->inventory[slot].count))
{ // Used last of a type - compact the artifact list
player->readyArtifact = arti_none;
player->inventory[slot].type = arti_none;
for(i = slot+1; i < player->inventorySlotNum; i++)
{
player->inventory[i-1] = player->inventory[i];
}
player->inventorySlotNum--;
if(player == &players[consoleplayer])
{ // Set position markers and get next readyArtifact
inv_ptr--;
if(inv_ptr < 6)
{
curpos--;
if(curpos < 0)
{
curpos = 0;
}
}
if(inv_ptr >= player->inventorySlotNum)
{
inv_ptr = player->inventorySlotNum-1;
}
if(inv_ptr < 0)
{
inv_ptr = 0;
}
player->readyArtifact =
player->inventory[inv_ptr].type;
}
}
}
//----------------------------------------------------------------------------
//
// PROC P_PlayerUseArtifact
//
//----------------------------------------------------------------------------
void P_PlayerUseArtifact(player_t *player, artitype_t arti)
{
int i;
for(i = 0; i < player->inventorySlotNum; i++)
{
if(player->inventory[i].type == arti)
{ // Found match - try to use
if(P_UseArtifact(player, arti))
{ // Artifact was used - remove it from inventory
P_PlayerRemoveArtifact(player, i);
if(player == &players[consoleplayer])
{
if(arti < arti_firstpuzzitem)
{
S_StartSound(NULL, SFX_ARTIFACT_USE);
}
else
{
S_StartSound(NULL, SFX_PUZZLE_SUCCESS);
}
ArtifactFlash = 4;
}
}
else if(arti < arti_firstpuzzitem)
{ // Unable to use artifact, advance pointer
P_PlayerNextArtifact(player);
}
break;
}
}
}
//==========================================================================
//
// P_UseArtifact
//
// Returns true if the artifact was used.
//
//==========================================================================
boolean P_UseArtifact(player_t *player, artitype_t arti)
{
mobj_t *mo;
angle_t angle;
int i;
int count;
switch(arti)
{
case arti_invulnerability:
if(!P_GivePower(player, pw_invulnerability))
{
return(false);
}
break;
case arti_health:
if(!P_GiveBody(player, 25))
{
return(false);
}
break;
case arti_superhealth:
if(!P_GiveBody(player, 100))
{
return(false);
}
break;
case arti_healingradius:
if (!P_HealRadius(player))
{
return(false);
}
break;
case arti_torch:
if(!P_GivePower(player, pw_infrared))
{
return(false);
}
break;
case arti_egg:
mo = player->mo;
P_SpawnPlayerMissile(mo, MT_EGGFX);
P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/6));
P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/6));
P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/3));
P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/3));
break;
case arti_fly:
if(!P_GivePower(player, pw_flight))
{
return(false);
}
if(player->mo->momz <= -35*FRACUNIT)
{ // stop falling scream
S_StopSound(player->mo);
}
break;
case arti_summon:
mo = P_SpawnPlayerMissile(player->mo, MT_SUMMON_FX);
if (mo)
{
mo->target = player->mo;
mo->special1 = (int)(player->mo);
mo->momz = 5*FRACUNIT;
}
break;
case arti_teleport:
P_ArtiTele(player);
break;
case arti_teleportother:
P_ArtiTeleportOther(player);
break;
case arti_poisonbag:
angle = player->mo->angle>>ANGLETOFINESHIFT;
if(player->class == PCLASS_CLERIC)
{
mo = P_SpawnMobj(player->mo->x+16*finecosine[angle],
player->mo->y+24*finesine[angle], player->mo->z-
player->mo->floorclip+8*FRACUNIT, MT_POISONBAG);
if(mo)
{
mo->target = player->mo;
}
}
else if(player->class == PCLASS_MAGE)
{
mo = P_SpawnMobj(player->mo->x+16*finecosine[angle],
player->mo->y+24*finesine[angle], player->mo->z-
player->mo->floorclip+8*FRACUNIT, MT_FIREBOMB);
if(mo)
{
mo->target = player->mo;
}
}
else // PCLASS_FIGHTER, obviously (also pig, not so obviously)
{
mo = P_SpawnMobj(player->mo->x, player->mo->y,
player->mo->z-player->mo->floorclip+35*FRACUNIT,
MT_THROWINGBOMB);
if(mo)
{
mo->angle = player->mo->angle+(((P_Random()&7)-4)<<24);
mo->momz = 4*FRACUNIT+((player->lookdir)<<(FRACBITS-4));
mo->z += player->lookdir<<(FRACBITS-4);
P_ThrustMobj(mo, mo->angle, mo->info->speed);
mo->momx += player->mo->momx>>1;
mo->momy += player->mo->momy>>1;
mo->target = player->mo;
mo->tics -= P_Random()&3;
P_CheckMissileSpawn(mo);
}
}
break;
case arti_speed:
if(!P_GivePower(player, pw_speed))
{
return(false);
}
break;
case arti_boostmana:
if(!P_GiveMana(player, MANA_1, MAX_MANA))
{
if(!P_GiveMana(player, MANA_2, MAX_MANA))
{
return false;
}
}
else
{
P_GiveMana(player, MANA_2, MAX_MANA);
}
break;
case arti_boostarmor:
count = 0;
for(i = 0; i < NUMARMOR; i++)
{
count += P_GiveArmor(player, i, 1); // 1 point per armor type
}
if(!count)
{
return false;
}
break;
case arti_blastradius:
P_BlastRadius(player);
break;
case arti_puzzskull:
case arti_puzzgembig:
case arti_puzzgemred:
case arti_puzzgemgreen1:
case arti_puzzgemgreen2:
case arti_puzzgemblue1:
case arti_puzzgemblue2:
case arti_puzzbook1:
case arti_puzzbook2:
case arti_puzzskull2:
case arti_puzzfweapon:
case arti_puzzcweapon:
case arti_puzzmweapon:
case arti_puzzgear1:
case arti_puzzgear2:
case arti_puzzgear3:
case arti_puzzgear4:
if(P_UsePuzzleItem(player, arti-arti_firstpuzzitem))
{
return true;
}
else
{
P_SetYellowMessage(player, TXT_USEPUZZLEFAILED, false);
return false;
}
break;
default:
return false;
}
return true;
}
//============================================================================
//
// A_SpeedFade
//
//============================================================================
void A_SpeedFade(mobj_t *actor)
{
actor->flags |= MF_SHADOW;
actor->flags &= ~MF_ALTSHADOW;
actor->sprite = actor->target->sprite;
}