ref: 32d566f7a2171eacb9b6713a9d1f98e3a1db6be6
dir: /src/strife/p_user.c/
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
// DESCRIPTION:
// Player related stuff.
// Bobbing POV/weapon, movement.
// Pending weapon.
//
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include "doomdef.h"
#include "d_event.h"
#include "p_local.h"
#include "sounds.h" // villsa [STRIFE]
#include "p_dialog.h" // villsa [STRIFE]
#include "d_main.h" // villsa [STRIFE]
#include "doomstat.h"
#include "deh_str.h" // haleyjd [STRIFE]
#include "z_zone.h"
#include "w_wad.h"
#include "p_pspr.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_inter.h"
// Index of the special effects (INVUL inverse) map.
#define LOOKPITCHAMOUNT 6 // villsa [STRIFE]
#define CENTERVIEWAMOUNT (LOOKPITCHAMOUNT + 2) // villsa [STRIFE]
#define LOOKUPMAX 90 // villsa [STRIFE]
#define LOOKDOWNMAX -110 // villsa [STRIFE]
boolean P_UseInventoryItem(player_t* player, int item); // fraggle [STRIFE]
void P_DropInventoryItem(player_t* player, int sprite); // villsa [STRIFE]
boolean P_ItemBehavior(player_t* player, int item); // villsa [STRIFE]
static char useinventorymsg[44]; // villsa [STRIFE]
//
// Movement.
//
// 16 pixels of bob
#define MAXBOB 0x100000
boolean onground;
//
// P_Thrust
// Moves the given origin along a given angle.
//
// [STRIFE] Verified unmodified
//
void
P_Thrust
( player_t* player,
angle_t angle,
fixed_t move )
{
angle >>= ANGLETOFINESHIFT;
player->mo->momx += FixedMul(move,finecosine[angle]);
player->mo->momy += FixedMul(move,finesine[angle]);
}
//
// P_CalcHeight
// Calculate the walking / running height adjustment
//
// [STRIFE] Some odd adjustments, and terrain view 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
// Note: a LUT allows for effects
// like a ramp with low health.
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;
// haleyjd 20110205 [STRIFE]: No CF_NOMOMENTUM check, and Rogue also removed
// the dead code inside.
if (!onground)
{
/*
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;
}
}
player->viewz = player->mo->z + player->viewheight + bob;
// villsa [STRIFE] account for terrain lowering the view
if(player->mo->flags & MF_FEETCLIPPED)
player->viewz -= 13*FRACUNIT;
if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
player->viewz = player->mo->ceilingz-4*FRACUNIT;
// haleyjd [STRIFE]: added a floorz clip here
if (player->viewz < player->mo->floorz)
player->viewz = player->mo->floorz;
}
//
// P_MovePlayer
//
// [STRIFE] Adjustments to allow air control, jumping, and up/down look.
//
void P_MovePlayer (player_t* player)
{
ticcmd_t* cmd;
cmd = &player->cmd;
player->mo->angle += (cmd->angleturn<<16);
// Do not let the player control movement
// if not onground.
onground = (player->mo->z <= player->mo->floorz);
// villsa [STRIFE] allows player to climb over things by jumping
// haleyjd 20110205: air control thrust should be 256, not cmd->forwardmove
if(!onground)
{
if(cmd->forwardmove)
P_Thrust (player, player->mo->angle, 256);
}
else
{
// villsa [STRIFE] jump button
if (cmd->buttons2 & BT2_JUMP)
{
if(!player->deltaviewheight)
player->mo->momz += 8*FRACUNIT;
}
// haleyjd 20110205 [STRIFE] Either Rogue or Watcom removed the
// redundant "onground" checks from these if's.
if (cmd->forwardmove)
P_Thrust (player, player->mo->angle, cmd->forwardmove*2048);
if (cmd->sidemove)
P_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048);
}
// villsa [STRIFE] player walking state set
if ( (cmd->forwardmove || cmd->sidemove)
&& player->mo->state == &states[S_PLAY_00] )
{
P_SetMobjState (player->mo, S_PLAY_01);
}
// villsa [STRIFE] centerview button
if (cmd->buttons2 & BT2_CENTERVIEW)
player->centerview = 1;
// villsa [STRIFE] adjust player's pitch when centerviewing
if (player->centerview)
{
if (player->pitch <= 0)
{
if (player->pitch < 0)
player->pitch = player->pitch + CENTERVIEWAMOUNT;
}
else
{
player->pitch = player->pitch - CENTERVIEWAMOUNT;
}
if (abs(player->pitch) < CENTERVIEWAMOUNT)
{
player->pitch = 0;
player->centerview = 0;
}
}
// villsa [STRIFE] look up action
if (cmd->buttons2 & BT2_LOOKUP)
{
player->pitch += LOOKPITCHAMOUNT;
if ((player->pitch + LOOKPITCHAMOUNT) > LOOKUPMAX ||
(player->pitch + LOOKPITCHAMOUNT) < LOOKDOWNMAX)
player->pitch -= LOOKPITCHAMOUNT;
}
else
{
// villsa [STRIFE] look down action
if (cmd->buttons2 & BT2_LOOKDOWN)
{
player->pitch -= LOOKPITCHAMOUNT;
if ((player->pitch - LOOKPITCHAMOUNT) > LOOKUPMAX ||
(player->pitch - LOOKPITCHAMOUNT) < LOOKDOWNMAX)
player->pitch += LOOKPITCHAMOUNT;
}
}
}
//
// P_DeathThink
// Fall on your face when dying.
// Decrease POV height to floor height.
//
// [STRIFE] Modifications for up/down look.
//
#define ANG5 (ANG90/18)
void P_DeathThink(player_t* player)
{
angle_t angle;
angle_t delta;
P_MovePsprites(player);
// fall to the ground
if (player->viewheight > 6*FRACUNIT)
player->viewheight -= FRACUNIT;
if (player->viewheight < 6*FRACUNIT)
player->viewheight = 6*FRACUNIT;
player->deltaviewheight = 0;
onground = (player->mo->z <= player->mo->floorz);
P_CalcHeight(player);
if(player->attacker && player->attacker != player->mo)
{
angle = R_PointToAngle2 (player->mo->x,
player->mo->y,
player->attacker->x,
player->attacker->y);
delta = angle - player->mo->angle;
if (delta < ANG5 || delta > (unsigned)-ANG5)
{
// Looking at killer,
// so fade damage flash down.
player->mo->angle = angle;
if (player->damagecount)
player->damagecount--;
}
else if (delta < ANG180)
player->mo->angle += ANG5;
else
player->mo->angle -= ANG5;
}
else if (player->damagecount)
player->damagecount--;
// villsa [STRIFE]
if(player->pitch <= 90)
player->pitch = player->pitch + 3;
if(player->cmd.buttons & BT_USE)
player->playerstate = PST_REBORN;
}
//
// P_PlayerThink
//
// [STRIFE] Massive changes/additions:
// * NOCLIP hack removed
// * P_DeathThink moved up
// * Inventory use/dropping
// * Strife weapons logic
// * Dialog
// * Strife powerups and nukage behavior
// * Fire Death/Sigil Shock
//
void P_PlayerThink (player_t* player)
{
ticcmd_t* cmd;
weapontype_t newweapon;
// villsa [STRIFE] unused code (see ST_Responder)
/*
// fixme: do this in the cheat code
if (player->cheats & CF_NOCLIP)
player->mo->flags |= MF_NOCLIP;
else
player->mo->flags &= ~MF_NOCLIP;
*/
// haleyjd 20110205 [STRIFE]: P_DeathThink moved up
if (player->playerstate == PST_DEAD)
{
P_DeathThink (player);
return;
}
// chain saw run forward
cmd = &player->cmd;
if (player->mo->flags & MF_JUSTATTACKED)
{
cmd->angleturn = 0;
cmd->forwardmove = 0xc800/512;
cmd->sidemove = 0;
player->mo->flags &= ~MF_JUSTATTACKED;
}
// Move around.
// Reactiontime is used to prevent movement
// for a bit after a teleport.
if (player->mo->reactiontime)
player->mo->reactiontime--;
else
P_MovePlayer (player);
P_CalcHeight (player);
if (player->mo->subsector->sector->special)
P_PlayerInSpecialSector (player);
// villsa [STRIFE] handle inventory input
if(cmd->buttons2 & (BT2_HEALTH|BT2_INVUSE|BT2_INVDROP))
{
if(!player->inventorydown)
{
if(cmd->buttons2 & BT2_HEALTH)
P_UseInventoryItem(player, SPR_FULL);
else if(cmd->buttons2 & BT2_INVUSE)
P_UseInventoryItem(player, cmd->inventory);
else if(cmd->buttons2 & BT2_INVDROP)
{
P_DropInventoryItem(player, cmd->inventory);
// haleyjd 20110205: removed incorrect "else" here
// villsa [STRIFE]
if(workparm)
{
int cheat = player->cheats ^ 1;
player->cheats ^= CF_NOCLIP;
if(cheat & CF_NOCLIP)
{
player->message = DEH_String("No Clipping Mode ON");
player->mo->flags |= MF_NOCLIP;
}
else
{
player->mo->flags &= ~MF_NOCLIP;
player->message = DEH_String("No Clipping Mode OFF");
}
}
}
}
player->inventorydown = true;
}
else
player->inventorydown = false;
// Check for weapon change.
// A special event has no other buttons.
if(cmd->buttons & BT_SPECIAL)
cmd->buttons = 0;
if(cmd->buttons & BT_CHANGE)
{
// The actual changing of the weapon is done
// when the weapon psprite can do it
// (read: not in the middle of an attack).
newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT;
// villsa [STRIFE] select poison bow
if(newweapon == wp_elecbow)
{
if(player->weaponowned[wp_poisonbow] && player->readyweapon == wp_elecbow)
{
if(player->ammo[weaponinfo[wp_poisonbow].ammo])
newweapon = wp_poisonbow;
}
}
// villsa [STRIFE] select wp grenade launcher
if(newweapon == wp_hegrenade)
{
if(player->weaponowned[wp_wpgrenade] && player->readyweapon == wp_hegrenade)
{
if(player->ammo[weaponinfo[wp_wpgrenade].ammo])
newweapon = wp_wpgrenade;
}
}
// villsa [STRIFE] select torpedo
if(newweapon == wp_mauler)
{
if(player->weaponowned[wp_torpedo] && player->readyweapon == wp_mauler)
{
if(player->ammo[weaponinfo[am_cell].ammo] >= 30)
newweapon = wp_torpedo;
}
}
if(player->weaponowned[newweapon] && newweapon != player->readyweapon)
{
// villsa [STRIFE] check weapon if in demo mode or not
if(weaponinfo[newweapon].availabledemo || !isdemoversion)
{
if(player->ammo[weaponinfo[newweapon].ammo])
player->pendingweapon = newweapon;
else
{
// decide between electric bow or poison arrow
if(newweapon == wp_elecbow &&
player->ammo[am_poisonbolts] &&
player->readyweapon != wp_poisonbow)
{
player->pendingweapon = wp_poisonbow;
}
// decide between hp grenade launcher or wp grenade launcher
else if(newweapon == wp_hegrenade &&
player->ammo[am_wpgrenades] &&
player->readyweapon != wp_wpgrenade)
{
player->pendingweapon = wp_wpgrenade;
}
// villsa [STRIFE] - no check for mauler/torpedo??
}
}
}
}
// check for use
if(cmd->buttons & BT_USE)
{
if(!player->usedown)
{
P_DialogStart(player); // villsa [STRIFE]
P_UseLines (player);
player->usedown = true;
}
}
else
player->usedown = false;
// cycle psprites
P_MovePsprites (player);
// Counters, time dependend power ups.
// Strength counts up to diminish fade.
if (player->powers[pw_strength])
player->powers[pw_strength]++;
// villsa [STRIFE] targeter powerup
if(player->powers[pw_targeter])
{
player->powers[pw_targeter]--;
if(player->powers[pw_targeter] == 1)
{
P_SetPsprite(player, ps_targcenter, S_NULL);
P_SetPsprite(player, ps_targleft, S_NULL);
P_SetPsprite(player, ps_targright, S_NULL);
}
else if(player->powers[pw_targeter] - 1 < 5*TICRATE)
{
if(player->powers[pw_targeter] & 32)
{
P_SetPsprite(player, ps_targright, S_NULL);
P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11
}
else if(player->powers[pw_targeter] & 16) // haleyjd 20110205: missing else
{
P_SetPsprite(player, ps_targright, S_TRGT_02); // 12
P_SetPsprite(player, ps_targleft, S_NULL);
}
}
}
if(player->powers[pw_invisibility])
{
// villsa [STRIFE] remove mvis flag as well
if(!--player->powers[pw_invisibility])
player->mo->flags &= ~(MF_SHADOW|MF_MVIS);
}
if(player->powers[pw_ironfeet])
{
player->powers[pw_ironfeet]--;
// villsa [STRIFE] gasmask sound
if(!(leveltime & 0x3f))
S_StartSound(player->mo, sfx_mask);
}
if(player->powers[pw_allmap] > 1)
player->powers[pw_allmap]--;
// haleyjd 08/30/10: [STRIFE]
// Nukage count keeps track of exposure to hazardous conditions over time.
// After accumulating 16 total seconds or more of exposure, you will take
// 5 damage roughly once per second until the count drops back under 560
// tics.
if(player->nukagecount)
{
player->nukagecount--;
if(!(leveltime & 0x1f) && player->nukagecount > 16*TICRATE)
P_DamageMobj(player->mo, NULL, NULL, 5);
}
if(player->damagecount)
player->damagecount--;
if(player->bonuscount)
player->bonuscount--;
// villsa [STRIFE] checks for extralight
if(player->extralight >= 0)
{
if(player->cheats & CF_ONFIRE)
player->fixedcolormap = 1;
else
player->fixedcolormap = 0;
}
else // Sigil shock:
player->fixedcolormap = INVERSECOLORMAP;
}
//
// P_RemoveInventoryItem
// villsa [STRIFE] new function
//
char* P_RemoveInventoryItem(player_t *player, int slot, int amount)
{
mobjtype_t type;
player->inventory[slot].amount -= amount;
player->st_update = true;
type = player->inventory[slot].type;
if(!player->inventory[slot].amount)
{
// shift everything above it down
// see P_TakeDialogItem for notes on possible bugs
int j;
for(j = slot + 1; j <= player->numinventory; j++)
{
inventory_t *item1 = &(player->inventory[j - 1]);
inventory_t *item2 = &(player->inventory[j]);
*item1 = *item2;
}
player->inventory[player->numinventory].type = NUMMOBJTYPES;
player->inventory[player->numinventory].sprite = -1;
player->numinventory--;
// update cursor position
if(player->inventorycursor >= player->numinventory)
{
if(player->inventorycursor)
player->inventorycursor--;
}
}
return mobjinfo[type].name;
}
//
// P_DropInventoryItem
// villsa [STRIFE] new function
//
void P_DropInventoryItem(player_t* player, int sprite)
{
int invslot;
inventory_t *item;
mobjtype_t type;
int amount;
invslot = 0;
amount = 1;
while(invslot < player->numinventory && sprite != player->inventory[invslot].sprite)
invslot++;
item = &(player->inventory[invslot]);
type = item->type;
if(item->amount)
{
angle_t angle;
fixed_t dist;
mobj_t* mo;
mobj_t* mobjitem;
fixed_t x;
fixed_t y;
fixed_t z;
int r;
if(item->type == MT_MONY_1)
{
if(item->amount >= 50)
{
type = MT_MONY_50;
amount = 50;
}
else if(item->amount >= 25)
{
type = MT_MONY_25;
amount = 25;
}
else if(item->amount >= 10)
{
type = MT_MONY_10;
amount = 10;
}
}
if(type >= NUMMOBJTYPES)
return;
angle = player->mo->angle;
r = P_Random();
angle = (angle + ((r - P_Random()) << 18)) >> ANGLETOFINESHIFT;
if(angle < 7618 && angle >= 6718)
angle = 7618;
else if(angle < 5570 && angle >= 4670)
angle = 5570;
else if(angle < 3522 && angle >= 2622)
angle = 3522;
else if(angle < 1474 && angle >= 574)
angle = 1474;
mo = player->mo;
dist = mobjinfo[type].radius + mo->info->radius + (4*FRACUNIT);
x = mo->x + FixedMul(finecosine[angle], dist);
y = mo->y + FixedMul(finesine[angle], dist);
z = mo->z + (10*FRACUNIT);
mobjitem = P_SpawnMobj(x, y, z, type);
mobjitem->flags |= (MF_SPECIAL|MF_DROPPED);
if(P_CheckPosition(mobjitem, x, y))
{
mobjitem->angle = (angle << ANGLETOFINESHIFT);
mobjitem->momx = FixedMul(finecosine[angle], (5*FRACUNIT)) + mo->momx;
mobjitem->momy = FixedMul(finesine[angle], (5*FRACUNIT)) + mo->momy;
mobjitem->momz = FRACUNIT;
P_RemoveInventoryItem(player, invslot, amount);
}
else
P_RemoveMobj(mobjitem);
}
}
//
// P_TossDegninOre
// villsa [STRIFE] new function
//
boolean P_TossDegninOre(player_t* player)
{
angle_t angle;
mobj_t* mo;
mobj_t* ore;
fixed_t x;
fixed_t y;
fixed_t z;
fixed_t dist;
angle = player->mo->angle >> ANGLETOFINESHIFT;
if(angle < 7618 && angle >= 6718)
angle = 7618;
else if(angle < 5570 && angle >= 4670)
angle = 5570;
else if(angle < 3522 && angle >= 2622)
angle = 3522;
else if(angle < 1474 && angle >= 574)
angle = 1474;
mo = player->mo;
dist = mobjinfo[MT_DEGNINORE].radius + mo->info->radius + (4*FRACUNIT);
x = mo->x + FixedMul(finecosine[angle], dist);
y = mo->y + FixedMul(finesine[angle], dist);
z = mo->z + (10*FRACUNIT);
ore = P_SpawnMobj(x, y, z, MT_DEGNINORE);
if(P_CheckPosition(ore, x, y))
{
ore->target = mo;
ore->angle = (angle << ANGLETOFINESHIFT);
ore->momx = FixedMul(finecosine[angle], (5*FRACUNIT));
ore->momy = FixedMul(finesine[angle], (5*FRACUNIT));
ore->momz = FRACUNIT;
return true;
}
else
P_RemoveMobj(ore);
return false;
}
//
// P_SpawnTeleportBeacon
// villsa [STRIFE] new function
//
boolean P_SpawnTeleportBeacon(player_t* player)
{
angle_t angle;
int r;
mobj_t* mo;
mobj_t* beacon;
fixed_t x;
fixed_t y;
fixed_t z;
fixed_t dist;
angle = player->mo->angle;
r = P_Random();
angle = (angle + ((r - P_Random()) << 18)) >> ANGLETOFINESHIFT;
if(angle < 7618 && angle >= 6718)
angle = 7618;
else if(angle < 5570 && angle >= 4670)
angle = 5570;
else if(angle < 3522 && angle >= 2622)
angle = 3522;
else if(angle < 1474 && angle >= 574)
angle = 1474;
mo = player->mo;
dist = mobjinfo[MT_BEACON].radius + mo->info->radius + (4*FRACUNIT);
x = mo->x + FixedMul(finecosine[angle], dist);
y = mo->y + FixedMul(finesine[angle], dist);
z = mo->z + (10*FRACUNIT);
beacon = P_SpawnMobj(x, y, z, MT_BEACON);
if(P_CheckPosition(beacon, x, y))
{
beacon->target = mo;
beacon->miscdata = mo->miscdata;
beacon->angle = (angle << ANGLETOFINESHIFT);
beacon->momx = FixedMul(finecosine[angle], (5*FRACUNIT));
beacon->momy = FixedMul(finesine[angle], (5*FRACUNIT));
beacon->momz = FRACUNIT;
P_SetMobjState(beacon, beacon->info->seestate);
return true;
}
else
P_RemoveMobj(beacon);
return false;
}
//
// P_UseInventoryItem
// villsa [STRIFE] new function
//
boolean P_UseInventoryItem(player_t* player, int item)
{
int i;
char* name;
if(player->cheats & CF_ONFIRE)
return false;
for(i = 0; i < player->numinventory; i++)
{
if(item != player->inventory[i].sprite)
continue;
if(!P_ItemBehavior(player, item))
return false;
name = P_RemoveInventoryItem(player, i, 1);
if(name == NULL)
name = "Item";
sprintf(useinventorymsg, "You used the %s.", name);
player->message = useinventorymsg;
if(player == &players[consoleplayer])
S_StartSound(NULL, sfx_itemup);
return true;
}
return false;
}
//
// P_ItemBehavior
// villsa [STRIFE] new function
//
boolean P_ItemBehavior(player_t* player, int item)
{
switch(item)
{
case SPR_ARM1: // 136
return P_GiveArmor(player, 2);
case SPR_ARM2: // 137
return P_GiveArmor(player, 1);
case SPR_SHD1: // 186
return P_GivePower(player, pw_invisibility);
case SPR_MASK: // 187
return P_GivePower(player, pw_ironfeet);
case SPR_PMUP: // 191
if(!player->powers[pw_allmap])
{
player->message = "The scanner won't work without a map!";
return false;
}
player->powers[pw_allmap] = PMUPTICS;
return true; // haleyjd 20110228: repaired
case SPR_STMP: // 180
return P_GiveBody(player, 10);
case SPR_MDKT: // 181
return P_GiveBody(player, 25);
case SPR_FULL: // 130
return P_GiveBody(player, 200);
case SPR_BEAC: // 135
return P_SpawnTeleportBeacon(player);
case SPR_TARG: // 108
return P_GivePower(player, pw_targeter);
}
return false;
}