sin-2015/player.cpp
1999-04-22 00:00:00 +00:00

8197 lines
No EOL
179 KiB
C++

// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Class definition of the player.
//
#include "g_local.h"
#include "entity.h"
#include "player.h"
#include "worldspawn.h"
#include "weapon.h"
#include "trigger.h"
#include "scriptmaster.h"
#include "vehicle.h"
#include "path.h"
#include "navigate.h"
#include "misc.h"
#include "q_shared.h"
#include "console.h"
#include "earthquake.h"
#include "gravpath.h"
#include "armor.h"
#include "inventoryitem.h"
#include "gibs.h"
#include "spidermine.h"
#include "deadbody.h"
#include "actor.h"
#include <windows.h>
//###
#include "guidedmissile.h" // added for guidedmissile
#include "rope.h" // added for ropes
#include "hoverweap.h" // for hoverbike weapons
#include "jitter.h" // for view jitter
#include "checkpoints.h"
#include "flamethrower.h"
#include "powerups.h"
#include "flashlight.h"
#include "informergun.h"
//###
const Vector power_color( 0.0, 1.0, 0.0 );
const Vector acolor( 1.0, 1.0, 1.0 );
const Vector bcolor( 1.0, 0.0, 0.0 );
//###
//static const char *ammo_types[ NUM_AMMO_TYPES ] =
static const char *ammo_types[ (NUM_AMMO_TYPES + NUM_2015_AMMO_TYPES) ] =
{
"Bullet357", "ShotgunClip", "Bullet10mm", "Bullet50mm", "BulletPulse", "BulletSniper", "Rockets", "SpiderMines",
//### added new ammo types
"Missiles", "IlludiumModules", "ConcussionBattery", "FlameFuel"
};
//###
static const char *armor_types[ NUM_ARMOR_TYPES ] =
{
"RiotHelmet", "FlakJacket", "FlakPants"
};
Event EV_Player_GodCheat( "superfuzz", EV_CHEAT );
Event EV_Player_NoTargetCheat( "wallflower", EV_CHEAT );
Event EV_Player_NoClipCheat( "nocollision", EV_CHEAT );
Event EV_Player_GiveAllCheat( "wuss", EV_CHEAT );
Event EV_Player_EndLevel( "endlevel" );
Event EV_Player_DevGodCheat( "god", EV_CHEAT );
Event EV_Player_DevNoTargetCheat( "notarget", EV_CHEAT );
Event EV_Player_DevNoClipCheat( "noclip", EV_CHEAT );
Event EV_Player_PrevWeapon( "weapprev", EV_CONSOLE );
Event EV_Player_NextWeapon( "weapnext", EV_CONSOLE );
Event EV_Player_PrevItem( "invprev", EV_CONSOLE );
Event EV_Player_NextItem( "invnext", EV_CONSOLE );
Event EV_Player_UseInventoryItem( "invuse", EV_CONSOLE );
Event EV_Player_GiveCheat( "give", EV_CHEAT );
Event EV_Player_Take( "take" );
Event EV_Player_UseItem( "use", EV_CONSOLE );
Event EV_Player_Spectator( "spectator", EV_CONSOLE );
Event EV_Player_GameVersion( "gameversion", EV_CONSOLE );
Event EV_Player_Fov( "fov" );
Event EV_Player_SaveFov( "savefov", EV_CONSOLE );
Event EV_Player_RestoreFov( "restorefov", EV_CONSOLE );
Event EV_Player_ToggleViewMode( "toggleviewmode", EV_CONSOLE );
Event EV_Player_ToggleZoomMode( "togglezoommode", EV_CHEAT );
Event EV_Player_ZoomOut( "zoomout" );
Event EV_Player_Kill( "playerkill", EV_CONSOLE );
Event EV_Player_Dead( "dead" );
Event EV_Player_SpawnEntity( "spawn", EV_CHEAT );
Event EV_Player_SpawnActor( "actor", EV_CHEAT );
Event EV_Player_ShowInfo( "showinfo", EV_CONSOLE );
Event EV_Player_ReadyToFire( "readytofire" );
Event EV_Player_WaitingToFire( "waitingtofire" );
Event EV_Player_AttackDone( "attackdone" );
Event EV_Player_AddPathNode( "addnode", EV_CHEAT );
Event EV_Player_Respawn( "respawn" );
Event EV_Player_ClearFloatingInventory( "clear_flinv" );
Event EV_Player_TestThread( "testthread", EV_CHEAT );
Event EV_Player_PowerupTimer( "poweruptimer" );
Event EV_Player_UpdatePowerupTimer( "updatepoweruptime" );
Event EV_Player_DrawOverlay( "drawoverlay" );
Event EV_Player_HideOverlay( "hideoverlay" );
Event EV_Player_DrawStats( "drawstats" );
Event EV_Player_HideStats( "hidestats" );
Event EV_Player_SetFlashColor( "setflashcolor" );
Event EV_Player_ClearFlashColor( "clearflashcolor" );
Event EV_Player_Mutate( "mutate", EV_CHEAT );
Event EV_Player_Human( "human", EV_CHEAT );
Event EV_Player_Skin( "skin" );
Event EV_Player_WhatIs( "whatis", EV_CHEAT );
Event EV_Player_ActorInfo( "actorinfo", EV_CHEAT );
Event EV_Player_Taunt( "taunt", EV_CONSOLE );
Event EV_Player_KillEnt( "killent", EV_CONSOLE );
Event EV_Player_KillClass( "killclass", EV_CONSOLE );
Event EV_Player_RemoveEnt( "removeent", EV_CONSOLE );
Event EV_Player_RemoveClass( "removeclass", EV_CONSOLE );
//###
Event EV_Player_GiveBikeCheat("spawnbike", EV_CHEAT);
Event EV_Player_BikeSkin("bikeskin");
Event EV_Player_SetInformer("informer", EV_CONSOLE);
Event EV_Player_ToggleGoggles("goggleseffect", EV_CHEAT);
Event EV_Player_WeaponSwitch("weaponswitch");
Event EV_Player_WeaponOverride("weaponoverride");
Event EV_Player_SetAngleJitter("anglejitter", EV_CONSOLE);
Event EV_Player_SetOffsetJitter("offsetjitter", EV_CONSOLE);
Event EV_Player_WelcomeMessage("welcome", EV_CONSOLE);
//###
#define UPRIGHT_SPEED 320.0f
#define CROUCH_SPEED 110.0f
#define ACCELERATION 10.0f
#define TAUNT_TIME 1.0f
/*
==============================================================================
PLAYER
==============================================================================
*/
// deadflag values
cvar_t * s_debugmusic;
cvar_t * whereami;
CLASS_DECLARATION( Sentient, Player, "player" );
ResponseDef Player::Responses[] =
{
{ &EV_ClientMove, ( Response )Player::ClientThink },
{ &EV_ClientEndFrame, ( Response )Player::EndFrame },
{ &EV_Player_ShowInfo, ( Response )Player::ShowInfo },
{ &EV_Vehicle_Enter, ( Response )Player::EnterVehicle },
{ &EV_Vehicle_Exit, ( Response )Player::ExitVehicle },
{ &EV_Player_EndLevel, ( Response )Player::EndLevel },
{ &EV_Player_AddPathNode, ( Response )Player::AddPathNode },
{ &EV_Player_PrevWeapon, ( Response )Player::EventPreviousWeapon },
{ &EV_Player_NextWeapon, ( Response )Player::EventNextWeapon },
{ &EV_Player_PrevItem, ( Response )Player::EventPreviousItem },
{ &EV_Player_NextItem, ( Response )Player::EventNextItem },
{ &EV_Player_UseItem, ( Response )Player::EventUseItem },
{ &EV_Player_UseInventoryItem,( Response )Player::EventUseInventoryItem },
{ &EV_Player_GiveCheat, ( Response )Player::GiveCheat },
{ &EV_Player_GiveAllCheat, ( Response )Player::GiveAllCheat },
{ &EV_Player_Take, ( Response )Player::Take },
{ &EV_Player_GodCheat, ( Response )Player::GodCheat },
{ &EV_Player_DevGodCheat, ( Response )Player::GodCheat },
{ &EV_Player_Spectator, ( Response )Player::Spectator },
{ &EV_Player_NoTargetCheat, ( Response )Player::NoTargetCheat },
{ &EV_Player_DevNoTargetCheat,( Response )Player::NoTargetCheat },
{ &EV_Player_NoClipCheat, ( Response )Player::NoclipCheat },
{ &EV_Player_DevNoClipCheat, ( Response )Player::NoclipCheat },
{ &EV_Player_GameVersion, ( Response )Player::GameVersion },
{ &EV_Player_Fov, ( Response )Player::Fov },
{ &EV_Player_SaveFov, ( Response )Player::SaveFov },
{ &EV_Player_RestoreFov, ( Response )Player::RestoreFov },
{ &EV_Player_ToggleViewMode, ( Response )Player::ToggleViewMode },
{ &EV_Player_ToggleZoomMode, ( Response )Player::ToggleZoomMode },
{ &EV_Player_ZoomOut, ( Response )Player::ZoomOut },
{ &EV_EnterConsole, ( Response )Player::EnterConsole },
{ &EV_ExitConsole, ( Response )Player::ExitConsole },
{ &EV_KickFromConsole, ( Response )Player::KickConsole },
{ &EV_Player_Kill, ( Response )Player::Kill },
{ &EV_Player_Dead, ( Response )Player::Dead },
{ &EV_Player_SpawnEntity, ( Response )Player::SpawnEntity },
{ &EV_Player_SpawnActor, ( Response )Player::SpawnActor },
{ &EV_Player_Respawn, ( Response )Player::Respawn },
{ &EV_Pain, ( Response )Player::Pain },
{ &EV_Killed, ( Response )Player::Killed },
{ &EV_Gib, ( Response )Player::GibEvent },
{ &EV_GotKill, ( Response )Player::GotKill },
{ &EV_Player_ReadyToFire, ( Response )Player::ReadyToFire },
{ &EV_Player_WaitingToFire, ( Response )Player::WaitingToFire },
{ &EV_Player_AttackDone, ( Response )Player::DoneFiring },
{ &EV_Player_TestThread, ( Response )Player::TestThread },
{ &EV_Player_ClearFloatingInventory, (Response)Player::ClearFloatingInventory },
{ &EV_Player_PowerupTimer, ( Response )Player::SetPowerupTimer },
{ &EV_Player_UpdatePowerupTimer, ( Response )Player::UpdatePowerupTimer },
{ &EV_Player_WhatIs, ( Response )Player::WhatIs },
{ &EV_Player_ActorInfo, ( Response )Player::ActorInfo },
{ &EV_Player_Taunt, ( Response )Player::Taunt },
{ &EV_Player_KillEnt, ( Response )Player::KillEnt },
{ &EV_Player_RemoveEnt, ( Response )Player::RemoveEnt },
{ &EV_Player_KillClass, ( Response )Player::KillClass },
{ &EV_Player_RemoveClass, ( Response )Player::RemoveClass },
{ &EV_Player_DrawOverlay, ( Response )Player::DrawOverlay },
{ &EV_Player_HideOverlay, ( Response )Player::HideOverlay },
{ &EV_Player_DrawStats, ( Response )Player::DrawStats },
{ &EV_Player_HideStats, ( Response )Player::HideStats },
{ &EV_Player_SetFlashColor, ( Response )Player::SetFlashColor },
{ &EV_Player_ClearFlashColor, ( Response )Player::ClearFlashColor },
{ &EV_Player_Mutate, ( Response )Player::Mutate },
{ &EV_Player_Human, ( Response )Player::Human },
{ &EV_Player_Skin, ( Response )Player::SetSkin },
//###
{&EV_Player_GiveBikeCheat, ( Response )Player::GiveBikeCheat }, // cheat to spawn a hoverbike
{&EV_Player_BikeSkin, ( Response )Player::SetBikeSkin },
{&EV_Player_SetInformer, ( Response )Player::SetInformer },
{&EV_Player_ToggleGoggles, ( Response )Player::ToggleGoggles },
{&EV_Player_WeaponSwitch, ( Response )Player::WeaponSwitch },
{&EV_Player_WeaponOverride, ( Response )Player::WeaponOverride },
{&EV_Player_SetAngleJitter, (Response)Player::AngleJitterEvent},
{&EV_Player_SetOffsetJitter, (Response)Player::OffsetJitterEvent},
{&EV_Player_WelcomeMessage, (Response)Player::WelcomeMessage},
//###
{ NULL, NULL }
};
Player::Player()
{
path = NULL;
respawn_time = -1;
watchCamera = NULL;
thirdpersonCamera = NULL;
movieCamera = NULL;
spectatorCamera = NULL;
damage_blood = 0;
damage_alpha = 0;
action_level = 0;
drawoverlay = false;
defaultViewMode = FIRST_PERSON;
fov = atof( Info_ValueForKey( client->pers.userinfo, "fov" ) );
if ( fov < 1 )
{
fov = 90;
}
else if ( fov > 160 )
{
fov = 160;
}
savedfov = 0;
has_thought = false;
spectator_distance = 128;
//### get weapon switching value from client
autoweaponswitch = atof(Info_ValueForKey(client->pers.userinfo, "weaponswitch"));
//###
s_debugmusic = gi.cvar ("s_debugmusic", "0", 0);
whereami = gi.cvar ("whereami", "0", 0);
// Remove him from the world until we spawn him
unlink();
}
void Player::InitSkin
(
void
)
{
// If this is a new level, there will be no custom skin, so go back to the original blade skin.
if ( !LoadingSavegame && !deathmatch->value )
{
int playernum = edict-g_edicts-1;
strcpy( client->pers.skin, "blade_base" );
// combine name, skin and model into a configstring
gi.configstring( CS_PLAYERSKINS+playernum, va( "%s\\%s\\%s",
client->pers.netname,
client->pers.model,
client->pers.skin ) );
}
}
void Player::Init
(
void
)
{
InitClient();
InitPhysics();
InitPowerups();
InitWorldEffects();
InitMusic();
InitPath();
InitView();
InitState();
InitEdict();
InitModel();
InitWeapons();
InitSkin();
Init2015(); //### init 2015 stuff
// don't call RestoreEnt when respawning on a training level
if ( !LoadingServer && ( deathmatch->value || ( level.training && ( respawn_time != -1 ) ) ||
!PersistantData.RestoreEnt( this ) ) )
{
InitInventory();
InitHealth();
}
ChooseSpawnPoint();
if ( !LoadingSavegame && ( viewmode == THIRD_PERSON ) && !crosshair )
{
cvar_t * chair;
chair = gi.cvar("crosshair", "0", 0);
crosshair = new Entity;
crosshair->setModel( va( "sprites/crosshair%d.spr", (int)chair->value ) );
crosshair->edict->svflags |= SVF_ONLYPARENT;
crosshair->edict->owner = this->edict;
}
if ( !deathmatch->value )
{
gi.AddCommandString( "con_clearfade\n" );
}
// make sure we put the player back into the world
link();
}
void Player::WritePersistantData
(
SpawnArgGroup &group
)
{
str text;
char t[ 3 ];
int hi;
int lo;
int i;
// encode the damage states into a text string
t[ 2 ] = 0;
for( i = 0; i < MAX_MODEL_GROUPS; i++ )
{
hi = ( edict->s.groups[ i ] >> 4 ) & 0xf;
lo = edict->s.groups[ i ] & 0xf;
t[ 0 ] = ( char )( 'A' + hi );
t[ 1 ] = ( char )( 'A' + lo );
text += t;
}
G_SetSpawnArg( "savemodel", savemodel.c_str() );
G_SetSpawnArg( "saveskin", saveskin.c_str() );
G_SetSpawnArg( "damage_groups", text.c_str() );
G_SetFloatArg( "fov", fov );
G_SetIntArg( "defaultViewMode", defaultViewMode );
G_SetIntArg( "flags", flags & ( FL_GODMODE | FL_NOTARGET | FL_SP_MUTANT ) );
Sentient::WritePersistantData( group );
}
void Player::RestorePersistantData
(
SpawnArgGroup &group
)
{
#ifndef SIN_DEMO
SpiderMine *spidermine;
Detonator *detonator;
#endif
int i;
int len;
str text;
const char *ptr;
int hi;
int lo;
Sentient::RestorePersistantData( group );
group.RestoreArgs( 1 );
// clear the damage states
memset( edict->s.groups, 0, sizeof( edict->s.groups ) );
savemodel = G_GetStringArg( "savemodel" );
saveskin = G_GetStringArg( "saveskin" );
text = G_GetStringArg( "damage_groups" );
len = text.length() >> 1;
if ( len > MAX_MODEL_GROUPS )
{
len = MAX_MODEL_GROUPS;
}
// decode the damage states from text string
ptr = text.c_str();
for( i = 0; i < len; i++ )
{
hi = ( *ptr++ - 'A' ) & 0xf;
lo = ( *ptr++ - 'A' ) & 0xf;
edict->s.groups[ i ] = ( hi << 4 ) + lo;
}
flags |= G_GetIntArg( "flags" );
fov = G_GetFloatArg( "fov", 90 );
defaultViewMode = ( viewmode_t )G_GetIntArg( "defaultViewMode", FIRST_PERSON );
#ifndef SIN_DEMO
spidermine = ( SpiderMine * )FindItem( "SpiderMine" );
detonator = ( Detonator * )FindItem( "Detonator" );
if ( spidermine )
{
spidermine->SetDetonator( detonator );
}
if ( FindItem( "Silencer" ) )
{
flags |= FL_SILENCER;
}
if ( FindItem( "ScubaGear" ) )
{
flags |= FL_OXYGEN;
}
if ( flags & FL_SP_MUTANT )
{
setModel( "manumit_pl.def" );
SetAnim( "idle" );
}
#endif
if ( defaultViewMode == CAMERA_VIEW )
{
defaultViewMode = FIRST_PERSON;
}
SetViewMode( defaultViewMode );
// prevent the player from starting dead
if ( health < 1 )
{
health = 1;
}
}
void Player::InitEdict
(
void
)
{
// entity state stuff
setSolidType( SOLID_BBOX );
setMoveType( MOVETYPE_WALK );
setSize( Vector( -16, -16, 0 ), Vector( 16, 16, STAND_HEIGHT ) );
edict->clipmask = MASK_PLAYERSOLID;
edict->svflags &= ~SVF_DEADMONSTER;
edict->svflags &= ~SVF_HIDEOWNER;
edict->owner = NULL;
// clear entity state values
edict->s.effects = 0;
edict->s.frame = 0;
}
void Player::InitMusic
(
void
)
{
//
// reset the music
//
client->ps.current_music_mood = mood_normal;
client->ps.fallback_music_mood = mood_normal;
ChangeMusic( "normal", "normal", false );
music_forced = false;
}
void Player::InitClient
(
void
)
{
client_persistant_t saved;
client_respawn_t resp;
float save_fov;
save_fov = client->ps.fov;
// deathmatch wipes most client data every spawn
if ( deathmatch->value || level.training )
{
char userinfo[ MAX_INFO_STRING ];
resp = client->resp;
memcpy( userinfo, client->pers.userinfo, sizeof( userinfo ) );
G_InitClientPersistant( client );
G_ClientUserinfoChanged( edict, userinfo );
}
else
{
memset( &resp, 0, sizeof( resp ) );
}
// clear everything but the persistant data and fov
saved = client->pers;
memset( client, 0, sizeof( *client ) );
client->pers = saved;
client->resp = resp;
client->ps.fov = save_fov;
}
void Player::InitState
(
void
)
{
in_console = false;
trappedInQuantum = false;
floating_owner = NULL;
gibbed = false;
enemy = NULL;
lastEnemyTime = 0;
lastTauntTime = 0;
takedamage = DAMAGE_AIM;
deadflag = DEAD_NO;
flags &= ~FL_NO_KNOCKBACK;
flags |= ( FL_BLOOD | FL_DIE_GIBS );
if ( parentmode->value )
{
flags &= ~FL_BLOOD;
flags &= ~FL_DIE_GIBS;
}
}
void Player::InitHealth
(
void
)
{
// Don't do anything if we're loading a server game.
// This is either a loadgame or a restart
if ( LoadingServer )
{
return;
}
// reset the health values
health = 100;
max_health = 100;
last_damage_time = 0;
// clear the damage states
memset( edict->s.groups, 0, sizeof( edict->s.groups ) );
}
void Player::InitModel
(
void
)
{
str model;
// Model stuff
edict->s.renderfx |= RF_CUSTOMSKIN;
edict->s.skinnum = edict->s.number - 1;
// Make sure that the model is allowed
model = client->pers.model;
if ( !game.ValidPlayerModels.ObjectInList( model ) )
{
model = "pl_blade.def";
}
setModel( model.c_str() );
TempAnim( "idle", NULL );
showModel();
}
void Player::InitPhysics
(
void
)
{
// Physics stuff
onladder = false;
sentientFrozen = false;
oldvelocity = vec_zero;
velocity = vec_zero;
gravity = 1.0;
falling = false;
fall_time = 0;
fall_value = 0;
fallsurface = NULL;
mass = 200;
xyspeed = 0;
}
void Player::InitPowerups
(
void
)
{
// powerups
poweruptimer = 0;
poweruptype = 0;
flags &= ~( FL_SHIELDS | FL_ADRENALINE | FL_CLOAK | FL_MUTANT | FL_SILENCER | FL_OXYGEN );
edict->s.renderfx &= ~( RF_DLIGHT | RF_ENVMAPPED );
}
void Player::InitWorldEffects
(
void
)
{
// world effects
next_drown_time = 0;
air_finished = level.time + 20;
old_waterlevel = 0;
drown_damage = 0;
}
void Player::InitWeapons
(
void
)
{
// weapon stuff
firing = false;
firedown = false;
firedowntime = 0;
gunoffset = Vector( 0, 0, STAND_HEIGHT );
}
void Player::InitInventory
(
void
)
{
// Don't do anything if we're loading a server game.
// This is either a loadgame or a restart
if ( LoadingServer )
{
return;
}
// Give player the default weapon
if ( !FindItem( "Magnum" ) )
{
giveWeapon( "Magnum" );
}
if ( !FindItem( "Fists" ) )
{
giveWeapon( "Fists" );
}
#ifndef SIN_DEMO
if ( !FindItem( "SpiderMine" ) )
{
giveWeapon( "SpiderMine" );
}
#endif
}
void Player::InitView
(
void
)
{
// Camera stuff
zoom_mode = ZOOMED_OUT;
spectator = false;
currentCameraTarget = 0;
checked_dead_camera = false;
SetViewMode( defaultViewMode );
// view stuff
kick_angles = vec_zero;
kick_origin = vec_zero;
v_angle = vec_zero;
oldviewangles = vec_zero;
v_gunoffset = vec_zero;
v_gunangles = vec_zero;
viewheight = STAND_EYE_HEIGHT;
// blend stuff
v_dmg_time = 0;
damage_blend = vec_zero;
flash_color[0] = flash_color[1] = flash_color[2] = flash_color[3] = 0;
// hud
hidestats = false;
}
void Player::ChooseSpawnPoint
(
void
)
{
int startonbike; //###
// set up the player's spawn location
//### added spawning on bikes
// SelectSpawnPoint( origin, angles, &gravaxis );
SelectSpawnPoint( origin, angles, &gravaxis, &startonbike );
//###
setOrigin( origin + "0 0 17" );
worldorigin.copyTo( edict->s.old_origin );
edict->s.renderfx |= RF_FRAMELERP;
KillBox( this );
// set gravity axis
SetGravityAxis( gravaxis );
client->ps.pmove.gravity_axis = gravaxis;
// setup the orientation
setAngles( angles );
v_angle = worldangles;
// set the delta angle
client->ps.pmove.delta_angles[0] = ANGLE2SHORT( angles.x - client->resp.cmd_angles[0] );
client->ps.pmove.delta_angles[1] = ANGLE2SHORT( angles.y - client->resp.cmd_angles[1] );
client->ps.pmove.delta_angles[2] = ANGLE2SHORT( angles.z - client->resp.cmd_angles[2] );
if ( deathmatch->value || coop->value )
{
SpawnTeleportEffect( worldorigin, 124 );
}
//###
// put player on a hoverbike if supposed to
if(startonbike)
{
SpawnPlayerBike(1);
TempAnim( "ride", NULL );
}
//###
}
void Player::InitPath
(
void
)
{
// pathnode placement stuff
nearestNode = NULL;
if ( ai_createnodes->value )
{
nearestNode = new PathNode;
nearestNode->Setup( worldorigin );
}
goalNode = nearestNode;
lastNode = nearestNode;
lastVisible = worldorigin;
searchTime = level.time + 0.1;
}
Player::~Player()
{
//### make sure not on a hoverbike when disconnecting
if(hoverbike)
{
hoverbike->BikeGetOff();
}
//###
if ( client )
{
gi.bprintf( PRINT_HIGH, "%s disconnected\n", client->pers.netname );
}
// if he's in a vehicle, get him out
if(vehicle)
{
Event *event;
event = new Event(EV_Use);
event->AddEntity(this);
vehicle->ProcessEvent(event);
}
if ( watchCamera && ( ( watchCamera == thirdpersonCamera ) || ( watchCamera == spectatorCamera ) ) )
{
watchCamera->CancelPendingEvents();
delete watchCamera;
watchCamera = NULL;
thirdpersonCamera = NULL;
spectatorCamera = NULL;
}
if ( crosshair )
{
crosshair->CancelPendingEvents();
delete crosshair;
crosshair = NULL;
}
if ( path )
{
delete path;
path = NULL;
}
edict->s.modelindex = 0;
gi.configstring( CS_PLAYERSKINS + entnum - 1, "" );
}
void Player::EndLevel
(
Event *ev
)
{
if ( flags & FL_MUTANT )
{
Human( NULL );
}
InitPowerups();
if ( health > max_health )
{
health = max_health;
}
if ( health < 1 )
{
health = 1;
}
}
void Player::Respawn
(
Event *ev
)
{
if ( deathmatch->value || coop->value || level.training )
{
assert ( deadflag == DEAD_DEAD );
#ifdef SIN_ARCADE
if(deathmatch->value)
{
FreeInventory();
}
#else
FreeInventory();
#endif
if ( !gibbed && !level.training )
{
CopyToBodyQueue( edict );
}
respawn_time = level.time;
Init();
// hold in place briefly
client->ps.pmove.pm_time = 50;
client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
return;
}
// force the load game menu to come up
level.missionfailed = true;
level.missionfailedtime = level.time - FRAMETIME;
}
void Player::SetDeltaAngles
(
void
)
{
int i;
// Use v_angle since we may be in a camera
for( i = 0; i < 3; i++ )
{
client->ps.pmove.delta_angles[ i ] = ANGLE2SHORT( v_angle[ i ] );
}
}
void Player::Spectator
(
Event *ev
)
{
if ( deadflag )
return;
if ( spectator )
{
client->ps.stats[ STAT_LAYOUTS ] &= ~DRAW_SPECTATOR;
spectator = false;
takedamage = DAMAGE_YES;
setSolidType( SOLID_BBOX );
setMoveType( MOVETYPE_WALK );
defaultViewMode = FIRST_PERSON;
SetViewMode( FIRST_PERSON );
}
else
{
//
// if he is in a vehicle
// get him out
//
if ( vehicle )
{
Event * event;
event = new Event( EV_Use );
event->AddEntity( this );
vehicle->ProcessEvent( event );
}
if ( coop->value )
{
DropInventoryItems();
}
hideModel();
setSolidType( SOLID_NOT );
spectator = true;
takedamage = DAMAGE_NO;
FreeInventory();
defaultViewMode = FIRST_PERSON;
SetViewMode( FIRST_PERSON );
client->ps.stats[ STAT_LAYOUTS ] |= DRAW_SPECTATOR;
spectator_distance = 128;
}
}
void Player::Obituary
(
Entity *attacker,
Entity *inflictor,
str location,
int meansofdeath
)
{
char msg[ 1024 ];
const char *message1 = NULL;
const char *message2 = "";
int num;
//###
qboolean fullmessage = false;
qboolean mfd_game = false;
//###
if ( !deathmatch->value )
return;
if(deathmatch->value == DEATHMATCH_MFD || deathmatch->value == DEATHMATCH_MOB)
{
fullmessage = true;
mfd_game = true;
}
// Client killed themself
if ( attacker == this )
{
switch ( meansofdeath )
{
case MOD_FALLING:
message1 = "fell down";
break;
case MOD_ADRENALINE:
message1 = "overdosed on adrenaline";
break;
case MOD_ION_DESTRUCT:
message1 = "overloaded his gun";
break;
default:
message1 = "cracked under the pressure";
}
//###
if(fullmessage)
{
// MFD
if(mfd_game)
{
if(client->resp.informer)
{
if(message1)
gi.bprintf( PRINT_MEDIUM, "%s the informer %s.\n", client->pers.netname, message1 );
else
gi.bprintf( PRINT_MEDIUM, "%s the informer died.\n", client->pers.netname );
}
else
{
if(message1)
gi.bprintf( PRINT_MEDIUM, "the mobster %s %s.\n", client->pers.netname, message1 );
else
gi.bprintf( PRINT_MEDIUM, "the mobster %s died.\n", client->pers.netname );
}
}
else
{
if ( message1 )
{
gi.bprintf( PRINT_MEDIUM, "%s %s.\n", client->pers.netname, message1 );
}
else
{
gi.bprintf( PRINT_MEDIUM, "%s died.\n", client->pers.netname );
}
client->resp.score--;
}
}
else
{
if(dedicated->value)
{
if(message1)
{
gi.dprintf("%s %s.\n", client->pers.netname, message1);
}
else
{
gi.dprintf("%s died.\n", client->pers.netname);
}
}
Com_sprintf(msg, sizeof(msg), "dm %i -1 %i", edict-g_edicts-1, meansofdeath);
gi.WriteByte(svc_console_command);
gi.WriteString(msg);
gi.multicast(NULL, MULTICAST_ALL_R);
client->resp.score--;
}
//###
return;
}
// Killed by another client
if ( attacker && attacker->isClient() )
{
switch (meansofdeath)
{
case MOD_FISTS: // FISTS
{
num = G_Random(6);
switch (num)
{
case 0:
message1 = "was knocked out by";
break;
case 1:
message1 = "was pummelled by";
break;
case 2:
message1 = "was over come by";
message2 = "'s ninja technique";
break;
case 3:
message1 = "ate a knuckle sandwich courtesy of";
break;
case 4:
message1 = "takes";
message2 = "'s beating";
break;
case 5:
message1 = "liked";
message2 = "'s death touch";
break;
default:
message1 = "was killed by";
message2 = "'s fists";
break;
}
}
break;
case MOD_MUTANTHANDS:
message1 = "was shredded by";
break;
case MOD_MAGNUM: // Magnum
{
num = G_Random(5);
switch (num)
{
case 0:
message1 = "was capped in the ass by";
break;
case 1:
message1 = "was gunned down in broad daylight by";
break;
case 2:
message1 = "eats";
message2 = "'s magnum";
break;
case 3:
message1 = "was punked by";
break;
case 4:
message1 = "was jacked by";
break;
default:
message1 = "was killed by";
message2 = "'s magnum";
break;
}
break;
}
case MOD_SHOTGUN: // Shotgun
{
num = G_Random(5);
switch (num)
{
case 0:
message1 = "is a victim of";
message2 = "'s shotgun";
break;
case 1:
message1 = "was slaughtered by";
break;
case 2:
message1 = "drowns in a rain of";
message2 = "'s hot lead";
break;
case 3:
message1 = "finds out why";
message2 = " got an F in gun safety";
break;
case 4:
message1 = "was shotgunned by";
break;
default:
message1 = "was killed by";
message2 = "'s shotgun";
break;
}
break;
}
case MOD_ASSRIFLE:
{
num = G_Random(4);
switch (num)
{
case 0:
message1 = "was assaulted by";
break;
case 1:
message1 = "was cut in half by";
break;
case 2:
message1 = "forgot that";
message2 = " had a bigger gun";
break;
case 3:
message1 = "was machinegunned down by";
break;
default:
message1 = "was killed by";
message2 = "'s assault rifle";
break;
}
}
break;
case MOD_CHAINGUN:
message1 = "was mowed down by";
message2 = "'s chaingun";
break;
case MOD_GRENADE:
message1 = "was fragged by";
message2 = "'s grenade";
break;
case MOD_ROCKET:
{
num = G_Random(5);
switch (num)
{
case 0:
message1 = "was blown up by";
message2 = "'s direct rocket hit";
break;
case 1:
message1 = "bows down to";
message2 = "'s almighty rocket launcher";
break;
case 2:
message1 = "was incinerated by";
message2 = "'s rocket";
break;
case 3:
message1 = "was obliterated by";
message2 = "'s rocket";
break;
case 4:
message1 = "likes walking into";
message2 = "'s projectiles";
break;
default:
message1 = "was killed by";
message2 = "'s rocket launcher";
}
}
break;
case MOD_ROCKETSPLASH:
{
num = G_Random(5);
switch (num)
{
case 0:
message1 = "was caught in";
message2 = "'s explosion";
break;
case 1:
message1 = "was burned to a crisp in";
message2 = "'s blast";
break;
case 2:
message1 = "is pulling shrapnel out of his ass thanks to";
break;
case 3:
message1 = "couldn't dodge";
message2 = "'s blast damage";
break;
case 4:
message1 = "was drenched by";
message2 = "'s splash damage";
break;
default:
message1 = "was killed by";
message2 = "'s explosion";
}
break;
}
case MOD_SPIDERSPLASH:
message1 = "was bitten by";
message2 = "'s spidermine";
break;
case MOD_SPEARGUN:
message1 = "was stuck by";
message2 = "'s spear";
break;
case MOD_PULSE:
message1 = "was pulsed by";
break;
case MOD_PULSELASER:
message1 = "was burned to a crisp by";
message2 = "'s laser";
break;
case MOD_ION:
case MOD_ION_DESTRUCT:
message1 = "Was ionized by";
break;
case MOD_QUANTUM:
message1 = "Was particlized by";
break;
case MOD_SNIPER:
{
num = G_Random(5);
switch (num)
{
case 0:
message1 = "was hollowpointed by";
break;
case 1:
message1 = "was put to sleep by";
break;
case 2:
message1 = "was sniped by";
break;
case 3:
message1 = "never saw it coming from";
break;
case 4:
message1 = "was killed by a precision shot from";
break;
default:
message1 = "was killed by";
message2 = "'s sniper rifle";
}
break;
}
//### MoD's for 2015 stuff
case MOD_STINGERROCKET:
message1 = "can now fit into";
message2 = "'s small trophy box";
fullmessage = true;
break;
case MOD_STINGERSPLASH:
message1 = "has been defunked by";
fullmessage = true;
break;
case MOD_PLASMABOW:
{
fullmessage = true;
num = G_Random(2);
switch(num)
{
case 0:
message1 = "was plasmogrified by";
break;
default:
message1 = "sucked down one of";
message2 = "'s big balls";
break;
}
break;
}
case MOD_PLASMABOWSPLASH:
{
fullmessage = true;
num = G_Random(2);
switch(num)
{
case 0:
message1 = "shouldn't have crossed";
break;
default:
message1 = "wasn't quite paying attention to";
message2 = "'s big balls";
break;
}
break;
}
case MOD_CONCUSSION:
message1 = "was given a fatal concussion by";
fullmessage = true;
break;
case MOD_MISSILE:
message1 = "was hunted down by";
message2 = "'s masterfull hunting skillz";
fullmessage = true;
break;
case MOD_MISSILESPLASH:
message1 = "almost escaped";
message2 = "'s hunting skillz";
fullmessage = true;
break;
case MOD_NUKE:
message1 = "took";
message2 = "'s nuke right in the kisser";
fullmessage = true;
break;
case MOD_NUKEEXPLOSION:
{
fullmessage = true;
num = G_Random(2);
switch(num)
{
case 0:
message1 = "was nukified by";
break;
default:
message1 = "had an allergic reaction to";
message2 = "'s Illudium\n";
break;
}
break;
}
case MOD_FLAMETHROWER:
message1 = "has been fried by";
message2 = " the master chef";
fullmessage = true;
break;
case MOD_HOVERBIKE:
message1 = "was plowed over by";
message2 = " on a hoverbike";
fullmessage = true;
break;
case MOD_HB_ROCKET:
message1 = " said hi to";
message2 = "'s rocket";
fullmessage = true;
break;
case MOD_HB_ROCKETSPLASH:
message1 = "was violently rejected by";
message2 = "'s rocket";
fullmessage = true;
break;
case MOD_HB_GUN:
message1 = "was tenderized by";
fullmessage = true;
break;
case MOD_HB_MINE:
message1 = "skillfully ran over";
message2 = "'s hover mine";
fullmessage = true;
break;
//###
default:
message1 = "was killed by";
}
//###
if(fullmessage)
{
// MFD
if(mfd_game)
{
const char *message3 = "the mobster";
const char *message4 = "the mobster";
if(client->resp.informer)
{
message3 = "the informer";
if(attacker->client->resp.informer)
{
// this should actually be able to happen
message4 = "the informer";
attacker->client->resp.score--;
}
else
{
attacker->client->resp.score++;
}
}
else
{
if(attacker->client->resp.informer)
{
message4 = "the informer";
attacker->client->resp.score++;
}
else
{
attacker->client->resp.score--;
}
}
gi.bprintf( PRINT_MEDIUM, "%s %s %s %s %s%s\n", client->pers.netname,
message3,
message1,
attacker->client->pers.netname,
message4,
message2 );
}
else
{
gi.bprintf( PRINT_MEDIUM, "%s %s %s%s\n", client->pers.netname,
message1,
attacker->client->pers.netname,
message2 );
attacker->client->resp.score++;
}
}
else
{
if(dedicated->value)
{
gi.dprintf("%s %s %s%s\n", client->pers.netname,
message1,
attacker->client->pers.netname,
message2 );
}
Com_sprintf(msg, sizeof(msg), "dm %i %i %i", edict-g_edicts-1, attacker->edict-g_edicts-1, meansofdeath);
gi.WriteByte(svc_console_command);
gi.WriteString(msg);
gi.multicast(NULL, MULTICAST_ALL_R);
attacker->client->resp.score++;
}
//###
return;
}
// Killed by a non-client
switch (meansofdeath)
{
case MOD_FALLING:
{
// Credit the last person who damaged me with the kill
if ( enemy && enemy->isClient() && ( enemy != this ) && level.time < lastEnemyTime )
{
//###
// MFD
if(mfd_game)
{
if(client->resp.informer)
{
if(enemy->client->resp.informer)
{
// this shouldn't really happen
attacker = enemy;
message1 = "the informer was pushed over the edge by";
message2 = "the informer";
enemy->client->resp.score--;
}
else
{
attacker = enemy;
message1 = "the informer was pushed over the edge by";
message2 = "the mobster";
// Give the last mobster a frag
enemy->client->resp.score++;
}
}
else
{
if(enemy->client->resp.informer)
{
attacker = enemy;
message1 = "the mobster was pushed over the edge by";
message2 = "the informer";
// Give the informer a frag
enemy->client->resp.score++;
}
else
{
attacker = enemy;
message1 = "the mobster was pushed over the edge by";
message2 = "the mobster";
enemy->client->resp.score--;
}
}
}
else
{
attacker = enemy;
message1 = "was pushed over the edge by";
message2 = "";
// Give the last enemy a frag
enemy->client->resp.score++;
}
//###
}
else
{
//### MFD
if(mfd_game)
{
if(client->resp.informer)
{
message1 = " the informer fell down";
}
else
{
message1 = " the mobster fell down";
}
}
else
{
//###
message1 = "fell down";
} //###
message2 = "";
}
break;
}
}
// Killed indirectly by enemy
if (message1 && attacker->isClient() )
{
gi.bprintf( PRINT_MEDIUM, "%s %s %s%s\n", client->pers.netname,
message1,
attacker->client->pers.netname,
message2 );
}
// Killed by world somehow
else if (message1)
{
gi.bprintf( PRINT_MEDIUM, "%s %s\n", client->pers.netname,
message1);
//### MFD
if(!mfd_game)
client->resp.score--;
//###
}
else
{
gi.bprintf( PRINT_MEDIUM, "%s died.\n", client->pers.netname );
//### MFD
if(!mfd_game)
client->resp.score--;
//###
}
}
void Player::Dead
(
Event *ev
)
{
deadflag = DEAD_DEAD;
StopAnimating();
//
// drop anything that might be attached to us
//
if (numchildren && ( level.training != 2 ) )
{
Entity * child;
int i;
//
// detach all our children
//
for ( i=0; i < MAX_MODEL_CHILDREN; i++ )
{
if (children[i])
{
child = ( Entity * )G_GetEntity( children[ i ] );
child->ProcessEvent( EV_Detach );
}
}
}
if ( !deathmatch->value && !coop->value && !level.training )
{
// Fade to white
gi.WriteByte( svc_console_command );
gi.WriteString( "fo 0.8 1 1 1" );
gi.unicast( edict, false );
PostEvent( EV_Player_Respawn, 3.0f );
}
#ifdef SIN_ARCADE
if ( !ARCADE_ComWriteByte( 0x03 ) )
gi.error( "Could not send lost player life to communications port" );
#endif
}
void Player::Killed
(
Event *ev
)
{
Entity *attacker;
Entity *inflictor;
str location;
str prefix;
str aname;
int meansofdeath;
int i;
qboolean deathgib=false;
str dname;
Event *ev1;
//### make sure to release ropes when killed
if(rope_grabbed)
{
((RopePiece *)rope_grabbed.Ptr())->Release();
}
//###
attacker = ev->GetEntity( 1 );
inflictor = ev->GetEntity( 3 );
location = ev->GetString( 4 );
meansofdeath = ev->GetInteger( 5 );
//
// if he is in a vehicle
// get him out
//
if(vehicle)
{
Event * event;
event = new Event(EV_Use);
event->AddEntity(this);
vehicle->ProcessEvent(event);
}
//### also make sure to get off a hoverbike if we're on one
if(hoverbike)
{
hoverbike->GiveExtraFrag(attacker);
hoverbike->BikeGetOff();
}
//###
// Set the enemy to attacker if it is a client, otherwise
// some world object killed the player. We don't overwrite
// enemy with non-clients, so clients can get credit for
// kills they caused (i.e. shooting someone caused them to fall )
if ( attacker->isClient() )
{
enemy = attacker;
}
// get rid of the third person crosshair if we have one
if ( crosshair )
{
crosshair->CancelPendingEvents();
delete crosshair;
crosshair = NULL;
}
Obituary( attacker, inflictor, location, meansofdeath );
StopAnimating();
CancelPendingEvents();
//
// determine death animation
//
if ( DoGib( meansofdeath, inflictor ) )
{
deathgib = true;
}
prefix = AnimPrefixForPlayer();
if ( deathgib )
{
str location;
location = ev->GetString( 4 );
// Check for location first otherwise randomize
if ( location == "torso_upper" )
dname += str( "gibdeath_upper" );
else if ( location == "torso_lower" )
dname += str( "gibdeath_lower" );
else if ( strstr( location.c_str(), "leg" ) )
dname += str( "gibdeath_lower" );
else if ( strstr( location.c_str(), "arm" ) )
dname += str( "gibdeath_upper" );
else if ( strstr( location.c_str(), "head" ) )
dname += str( "gibdeath_upper" );
else if ( G_Random() > 0.5 )
dname += str( "gibdeath_upper" );
else
dname += str( "gibdeath_lower" );
}
if ( deathgib )
aname = prefix + dname;
else
aname = prefix + str("death_") + location;
i = gi.Anim_Random( edict->s.modelindex, aname.c_str() );
if ( i == -1 )
{
aname = prefix + str("death");
}
//### moved here to prevent a definate game crash
if ( flags & ( FL_MUTANT|FL_SP_MUTANT ) )
{
Human( NULL );
}
//###
deadflag = DEAD_DYING;
respawn_time = level.time + 1.0;
if ( currentWeapon )
{
if ( !level.training )
{
DropWeapon( currentWeapon );
}
else if ( level.training != 2 )
{
currentWeapon->CancelPendingEvents();
delete currentWeapon;
currentWeapon = NULL;
}
}
if ( coop->value )
{
DropInventoryItems();
}
edict->clipmask = MASK_DEADSOLID;
edict->svflags |= SVF_DEADMONSTER;
ProcessEvent( EV_Player_ZoomOut );
SetViewMode( THIRD_PERSON );
setMoveType( MOVETYPE_NONE );
edict->s.renderfx &= ~RF_DLIGHT;
edict->s.renderfx &= ~RF_ENVMAPPED;
angles.x = 0;
angles.z = 0;
setAngles( angles );
if ( deathmatch->value )
{
client->showinfo = true;
G_DeathmatchScoreboard( this );
}
if ( flags & ( FL_MUTANT|FL_SP_MUTANT ) )
{
//### moved to before the deadflag being set to
// prevent a definate game crash
// Human( NULL );
ev1 = new Event( EV_Gib );
ev1->AddInteger( 0 );
ProcessEvent( ev1 );
ProcessEvent( EV_Player_Dead );
}
else if ( strstr( aname.c_str(), "gibdeath" ) )
{
ev1 = new Event( EV_Gib );
ev1->AddInteger( 1 );
ProcessEvent( ev1 );
}
RandomAnimate( aname.c_str(), EV_Player_Dead );
animOverride = true;
//
// change music
//
ChangeMusic( "failure", "normal", true );
//### death from flames
if(inflictor->isSubclassOf(ThrowerFlame))
{
edict->s.renderfx |= RF_LIGHTOFFSET;
edict->s.lightofs = -127;
CancelEventsOfType(EV_Sentient_HurtFlame);
edict->s.effects &= ~EF_FLAMES;
edict->s.effects |= EF_DEATHFLAMES;
}
// remove the heat signature because he's dead
edict->s.effects &= ~EF_WARM;
//###
if ( !deathmatch->value && !level.training && !level.no_jc )
{
char name[ 128 ];
int num;
num = (int)G_Random( 4 ) + 1;
sprintf( name, "global/universal_script.scr::jc_talks_blade_dies%d", num );
ExecuteThread( name, true );
}
}
void Player::Pain
(
Event *ev
)
{
float damage,delta;
Entity *attacker;
int meansofdeath;
damage = ev->GetFloat( 1 );
attacker = ev->GetEntity( 2 );
meansofdeath = ev->GetInteger( 4 );
if ( viewmode == CAMERA_VIEW )
{
if ( level.cinematic )
{
return;
}
else
{
//###
if(movecapturer)
movecapturer->Deactivate();
//###
ExitConsole( NULL );
SetCamera( NULL );
}
}
// add to our damage time
v_dmg_time = level.time + DAMAGE_TIME;
if ( attacker->isClient() )
{
if ( !deadflag )
{
enemy = attacker;
lastEnemyTime = level.time + ENEMY_TIME;
}
}
// increase action level of game
action_level += damage;
// add to the damage inflicted on a player this frame
// the total will be turned into screen blends and view angle kicks
// at the end of the frame
damage_blood += damage;
// white flash signifying no damage was taken.
if ( ( damage <= 0 ) && ( meansofdeath != MOD_FISTS ) )
{
Event *ev1;
ProcessEvent( EV_Player_ClearFlashColor );
ev1 = new Event( EV_Player_SetFlashColor );
// Flash the player white
ev1->AddFloat( 1 );
ev1->AddFloat( 1 );
ev1->AddFloat( 1 );
ev1->AddFloat( 0.3 );
ProcessEvent( ev1 );
}
delta = level.time - last_damage_time;
// Don' play more than one pain sound or animation a second
if ( delta < 1.0 )
{
return;
}
last_damage_time = level.time;
// If damage > 5 then play a pain animation, otherwise play a pain sound
if ( !firing && !deadflag && !vehicle && damage > 5)
{
str prefix;
str aname;
int index;
//
// determine pain animation
//
//### different pain animation while on a hoverbike
if(hoverbike)
{
TempAnim("crouch_pain", NULL);
}
else
{
//###
prefix = AnimPrefixForPlayer();
aname = prefix + str("pain_") + str( ev->GetString( 3 ) );
index = gi.Anim_Random( edict->s.modelindex, aname.c_str() );
if ( index == -1 )
{
aname = prefix + str("pain");
}
TempAnim( aname.c_str(), NULL );
}//###
}
else
{
RandomSound( "snd_pain", 1 );
ProcessEvent( EV_PainSound );
}
}
void Player::DoUse
(
void
)
{
int i;
int num;
edict_t *touch[ MAX_EDICTS ];
edict_t *hit;
Event *ev;
Vector min;
Vector max;
Vector offset;
trace_t trace;
Vector start;
Vector end;
float t;
//### can't use things while piloting a missile
if(viewmode == MISSILE_VIEW)
{
return;
}
//### can't use things while on a bike
if(hoverbike)
{
return;
}
if ( spectator )
{
ChangeSpectator();
return;
}
// Pickup inventory if one exists
PickupFloatingInventory();
start = worldorigin + Vector( 0, 0, viewheight );
//### added support for precise use entities
end = start + Vector(orientation[ 0 ]) * 80.0f;
trace = G_Trace(start, vec_zero, vec_zero, end, this, MASK_SOLID, "Player::DoUse");
if(trace.ent) // hit something, so pass a precise use
{
ev = new Event(EV_PreciseUse);
ev->AddEntity(this);
trace.ent->entity->ProcessEvent(ev);
}
//###
end = start + Vector( orientation[ 0 ] ) * 64.0f;
trace = G_Trace( start, vec_zero, vec_zero, end, this, MASK_SOLID, "Player::DoUse" );
t = 64 * trace.fraction - maxs[ 0 ];
if ( t < 0 )
{
t = 0;
}
offset = Vector( orientation[ 0 ] ) * t;
min = start + offset + "-16 -16 -16";
max = start + offset + "16 16 16";
num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for( i = 0; i < num; i++ )
{
hit = touch[ i ];
if ( !hit->inuse )
{
continue;
}
assert( hit->entity );
ev = new Event( EV_Use );
ev->AddEntity( this );
hit->entity->ProcessEvent( ev );
}
num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for( i = 0; i < num; i++ )
{
hit = touch[ i ];
if ( !hit->inuse )
{
continue;
}
assert( hit->entity );
ev = new Event( EV_Use );
ev->AddEntity( this );
hit->entity->ProcessEvent( ev );
}
// Force a reload on a weapon
if ( currentWeapon )
{
currentWeapon->ForceReload();
}
}
static Entity *pm_passent;
// pmove doesn't need to know about passent and contentmask
extern "C" trace_t PM_trace
(
vec3_t start,
vec3_t mins,
vec3_t maxs,
vec3_t end
)
{
//### added in for the Hoverbike's client side prediction stuff
if(pm_passent->isSubclassOf(Player) && ((Player *)pm_passent)->GetHoverbike())
{
return G_Trace( start, mins, maxs, end, pm_passent->edict, MASK_SOLID, "PM_trace 1" );
}
// if ( ( pm_passent->flags & FL_GODMODE ) || ( pm_passent->health > 0 ) )
else if ( ( pm_passent->flags & FL_GODMODE ) || ( pm_passent->health > 0 ) )
//###
{
return G_Trace( start, mins, maxs, end, pm_passent->edict, MASK_PLAYERSOLID, "PM_trace 1" );
}
else
{
return G_Trace( start, mins, maxs, end, pm_passent->edict, MASK_DEADSOLID, "PM_trace 1" );
}
}
EXPORT_FROM_DLL void Player::SetViewMode
(
viewmode_t mode,
Entity * newCamera
)
{
Camera *c;
viewmode = mode;
if ( watchCamera && ( ( watchCamera == thirdpersonCamera ) || ( watchCamera == spectatorCamera ) ) )
{
watchCamera->CancelPendingEvents();
delete watchCamera;
watchCamera = NULL;
thirdpersonCamera = NULL;
spectatorCamera = NULL;
}
if ( crosshair )
{
crosshair->CancelPendingEvents();
delete crosshair;
crosshair = NULL;
}
switch( viewmode )
{
default :
case THIRD_PERSON :
{
Event * ev;
Vector pos;
c = new Camera;
ev = new Event( EV_Camera_MoveToPos );
pos = worldorigin;
pos.z = absmax.z;
ev->AddVector( pos );
c->ProcessEvent( ev );
ev = new Event( EV_Camera_TurnTo );
ev->AddVector( v_angle );
c->ProcessEvent( ev );
if ( vehicle )
c->FollowEntity( this, 256, MASK_OPAQUE );
//###
else if(hoverbike) // setup camera properly for a hoverbike
c->FollowEntity(this, 160, MASK_OPAQUE);
//###
else
c->FollowEntity( this, 128, MASK_PLAYERSOLID );
watchCamera = c;
thirdpersonCamera = c;
if ( !deadflag )
{
cvar_t * chair;
chair = gi.cvar("crosshair", "0", 0);
crosshair = new Entity;
crosshair->setModel( va( "sprites/crosshair%d.spr", (int)chair->value ) );
crosshair->edict->svflags |= SVF_ONLYPARENT;
crosshair->edict->owner = this->edict;
}
}
break;
case SPECTATOR :
{
Event * ev;
Vector pos;
edict_t *cl_ent;
cl_ent = g_edicts + 1 + currentCameraTarget;
if
(
!cl_ent->inuse ||
!cl_ent->entity ||
( !cl_ent->entity->client ) ||
( ( ( Player * )cl_ent->entity )->spectator )
)
{
ChangeSpectator();
if
(
!cl_ent->inuse ||
!cl_ent->entity ||
( !cl_ent->entity->client ) ||
( ( ( Player * )cl_ent->entity )->spectator )
)
{
SetViewMode( FIRST_PERSON );
return;
}
}
c = new Camera;
ev = new Event( EV_Camera_MoveToPos );
pos = cl_ent->entity->worldorigin;
pos.z = cl_ent->entity->absmax.z;
ev->AddVector( pos );
c->ProcessEvent( ev );
ev = new Event( EV_Camera_TurnTo );
ev->AddVector( cl_ent->entity->worldangles );
c->ProcessEvent( ev );
c->FollowEntity( cl_ent->entity, spectator_distance, MASK_PLAYERSOLID );
watchCamera = c;
spectatorCamera = c;
}
break;
case FIRST_PERSON :
watchCamera = this;
break;
case CAMERA_VIEW :
watchCamera = newCamera;
break;
//### added view mode for guided missile
case MISSILE_VIEW:
watchCamera = newCamera;
break;
//###
}
}
EXPORT_FROM_DLL void Player::CheckButtons
(
void
)
{
// Only process buttons if you're not dying
if ( ( deadflag != DEAD_NO ) || spectator )
{
if ( spectator && ( ( new_buttons & BUTTON_ATTACK ) && ( !firedown ) ) )
{
// we set defaultViewMode as if we toggled the camera
if ( defaultViewMode == FIRST_PERSON )
{
defaultViewMode = SPECTATOR;
}
ChangeSpectator();
}
else if
(
spectator && ( buttons & BUTTON_USE ) && ( viewmode == SPECTATOR )
)
{
spectator_distance += 2;
if ( spectator_distance > 384 )
spectator_distance = 0;
if ( spectatorCamera )
{
Camera * cam;
cam = ( Camera * )( ( Entity * )spectatorCamera );
cam->SetCurrentDistance( spectator_distance );
}
}
return;
}
if ( new_buttons & BUTTON_USE )
{
DoUse();
}
// Check for button release
if ( !( buttons & BUTTON_ATTACK ) )
{
if ( firedown )
{
Event *event;
event = new Event( EV_Sentient_ReleaseAttack );
event->AddFloat( level.time - firedowntime );
ProcessEvent( event );
firedown = false;
}
}
// fire weapon from final position if needed
if ( ( buttons & BUTTON_ATTACK ) && WeaponReady() )
{
//
// raise the action level
//
if ( currentWeapon )
action_level += currentWeapon->ActionLevelIncrement();
firedown = true;
firedowntime = level.time;
//### changed for hoverbike
// if ( !vehicle )
if(!vehicle && !hoverbike)
{
str name;
str prefix;
prefix = AnimPrefixForPlayer();
//
// append the prefix based on which weapon we are holding
//
prefix += str( AnimPrefixForWeapon() );
if ( ( xyspeed > 20 ) )
{
int num;
int numframes;
if ( ( waterlevel > 2 ) || ( xyspeed > 250 ) )
name = prefix + str("run_fire");
else
name = prefix + str("walk_fire");
num = gi.Anim_Random( edict->s.modelindex, name.c_str() );
if ( num != -1 )
{
numframes = gi.Anim_NumFrames( edict->s.modelindex, num );
if ( ( last_frame_in_anim + 1 ) == numframes )
edict->s.anim = num;
else
TempAnim( name.c_str(), NULL );
}
else
{
name = prefix + str("fire");
TempAnim( name.c_str(), NULL );
}
}
else
{
name = prefix + str("fire");
TempAnim( name.c_str(), NULL );
}
}
ProcessEvent( EV_Sentient_Attack );
if ( ai_createnodes->value )
{
goalNode = nearestNode;
if ( !goalNode )
{
if ( ai_debugpath->value )
{
gi.dprintf( "New path node\n" );
}
nearestNode = new PathNode;
nearestNode->Setup( worldorigin );
goalNode = nearestNode;
lastNode = nearestNode;
}
}
}
}
EXPORT_FROM_DLL void Player::TouchStuff
(
pmove_t *pm
)
{
edict_t *other;
Event *event;
int i;
int j;
if ( spectator )
{
return;
}
if ( getMoveType() != MOVETYPE_NOCLIP )
{
G_TouchTriggers( this );
}
// touch other objects
for( i = 0; i < pm->numtouch; i++ )
{
other = pm->touchents[ i ];
for( j = 0; j < i ; j++ )
{
if ( pm->touchents[ j ] == other )
break;
}
if ( j != i )
{
// duplicated
continue;
}
// Don't bother touching the world
if ( ( !other->entity ) || ( other->entity == world ) )
{
continue;
}
event = new Event( EV_Touch );
event->AddEntity( this );
other->entity->ProcessEvent( event );
event = new Event( EV_Touch );
event->AddEntity( other->entity );
ProcessEvent( event );
}
}
EXPORT_FROM_DLL void Player::EnterVehicle
(
Event *ev
)
{
Entity *ent;
ent = ev->GetEntity( 1 );
if ( ent && ent->isSubclassOf( Vehicle ) )
{
viewheight = STAND_EYE_HEIGHT;
levelVars.SetVariable( "player_in_vehicle", 1 );
velocity = vec_zero;
vehicle = ( Vehicle * )ent;
if ( vehicle->IsDrivable() )
setMoveType( MOVETYPE_VEHICLE );
else
setMoveType( MOVETYPE_NOCLIP );
if ( ev->NumArgs() > 1 )
vehicleanim = ev->GetString( 2 );
else
vehicleanim = "drive";
}
}
EXPORT_FROM_DLL void Player::ExitVehicle
(
Event *ev
)
{
levelVars.SetVariable( "player_in_vehicle", 0 );
setMoveType( MOVETYPE_WALK );
vehicle = NULL;
}
EXPORT_FROM_DLL void Player::GetMoveInfo
(
pmove_t *pm
)
{
if ( !deadflag )
{
v_angle[ 0 ] = pm->viewangles[ 0 ];
v_angle[ 1 ] = pm->viewangles[ 1 ];
v_angle[ 2 ] = pm->viewangles[ 2 ];
AnglesToMat( v_angle.vec3(), orientation );
}
client->ps.pmove = pm->s;
old_pmove = pm->s;
setOrigin( Vector( pm->s.origin[ 0 ], pm->s.origin[ 1 ], pm->s.origin[ 2 ] ) * 0.125 );
velocity = Vector( pm->s.velocity[ 0 ], pm->s.velocity[ 1 ], pm->s.velocity[ 2 ] ) * 0.125;
if (
( client->ps.pmove.pm_type == PM_FREEZE ) ||
( client->ps.pmove.pm_type == PM_INVEHICLE_ZOOM ) ||
( client->ps.pmove.pm_type == PM_INVEHICLE )
)
velocity = vec_zero;
setSize( pm->mins, pm->maxs );
gunoffset = Vector( 0, 0, pm->viewheight );
viewheight = pm->viewheight;
waterlevel = pm->waterlevel;
watertype = pm->watertype;
onladder = pm->onladder;
if ( pm->groundentity )
{
groundentity = pm->groundentity;
groundsurface = pm->groundsurface;
groundplane = pm->groundplane;
groundcontents = pm->groundcontents;
groundentity_linkcount = pm->groundentity->linkcount;
}
else
{
groundentity = NULL;
groundsurface = NULL;
groundcontents = 0;
}
if ( pm->groundsurface )
fallsurface = pm->groundsurface;
}
EXPORT_FROM_DLL void Player::SetMoveInfo
(
pmove_t *pm,
usercmd_t *ucmd
)
{
pm_passent = this;
// set up for pmove
memset( pm, 0, sizeof( pmove_t ) );
pm->s = client->ps.pmove;
pm->s.origin[ 0 ] = worldorigin.x * 8;
pm->s.origin[ 1 ] = worldorigin.y * 8;
pm->s.origin[ 2 ] = worldorigin.z * 8;
pm->s.velocity[ 0 ] = velocity.x * 8;
pm->s.velocity[ 1 ] = velocity.y * 8;
pm->s.velocity[ 2 ] = velocity.z * 8;
if ( !vehicle && memcmp( &old_pmove, &pm->s, sizeof( pm->s ) ) )
{
pm->snapinitial = true;
}
pm->cmd = *ucmd;
pm->trace = PM_trace; // adds default parms
pm->pointcontents = gi.pointcontents;
}
EXPORT_FROM_DLL pmtype_t Player::GetMovePlayerMoveType
(
void
)
{
if ( level.playerfrozen || sentientFrozen )
{
return PM_FREEZE;
}
//### added for movement capturing thinggy
else if(movecapturer)
{
return PM_MOVECAPTURED;
}
//###
else if ( ( viewmode == CAMERA_VIEW ) || ( viewmode == SPECTATOR ) )
{
return PM_LOCKVIEW;
}
else if ( vehicle )
{
if ( zoom_mode == ZOOMED_IN )
{
return PM_INVEHICLE_ZOOM;
}
else
{
return PM_INVEHICLE;
}
}
//### for hoverbike
else if(hoverbike)
{
return PM_ONBIKE;
}
//###
else if ( ( getMoveType() == MOVETYPE_NOCLIP ) || ( spectator ) )
{
return PM_SPECTATOR;
}
else if ( deadflag )
{
return PM_DEAD;
}
else if ( zoom_mode == ZOOMED_IN )
{
return PM_ZOOM;
}
else if ( ( viewmode == THIRD_PERSON ) && ( !gravaxis ) )
{
return PM_LOCKVIEW;
}
//###
// added for guided missile
else if(viewmode == MISSILE_VIEW)
{
return PM_ATTACHVIEW;
}
//###
else
{
return PM_NORMAL;
}
}
EXPORT_FROM_DLL void Player::ClientMove
(
usercmd_t *ucmd
)
{
pmove_t pm;
client->ps.pmove.pm_type = GetMovePlayerMoveType();
if (
( client->ps.pmove.pm_type == PM_LOCKVIEW ) ||
( client->ps.pmove.pm_type == PM_INVEHICLE ) ||
( client->ps.pmove.pm_type == PM_INVEHICLE_ZOOM ) ||
( client->ps.pmove.pm_type == PM_FREEZE ) ||
( client->ps.pmove.pm_type == PM_DEAD ) ||
//###
( client->ps.pmove.pm_type == PM_ATTACHVIEW) ||
(rope_grabbed) || // don't predict rope movement
(client->ps.pmove.pm_type == PM_MOVECAPTURED) // don't predict while in a movement capturer
//###
)
client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
else
client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
if ( flags & FL_MUTANT )
client->ps.pmove.pm_flags |= PMF_MUTANT;
else
client->ps.pmove.pm_flags &= ~PMF_MUTANT;
if ( flags & FL_ADRENALINE )
client->ps.pmove.pm_flags |= PMF_ADRENALINE;
else
client->ps.pmove.pm_flags &= ~PMF_ADRENALINE;
if ( level.airclamp )
client->ps.pmove.pm_flags &= ~PMF_NOAIRCLAMP;
else
client->ps.pmove.pm_flags |= PMF_NOAIRCLAMP;
edict->s.effects &= ~EF_NOFOOTSTEPS;
if ( !sv_footsteps->value )
{
edict->s.effects |= EF_NOFOOTSTEPS;
}
client->ps.pmove.gravity = sv_gravity->value * gravity;
SetMoveInfo( &pm, ucmd );
// perform a pmove
gi.Pmove( &pm );
//### added movement capturer condition
// if ( !deadflag && !level.playerfrozen )
if ( !deadflag && !level.playerfrozen && !movecapturer)
{
if ( waterlevel == 3 && falling)
{
Event * ev;
ev = new Event( EV_Sentient_AnimLoop );
ProcessEvent( ev );
falling = false;
}
if ( groundentity && !pm.groundentity && ( pm.cmd.upmove >= 10 ) && ( waterlevel <= 1 ) )
{
TempAnim( "jump", NULL );
falling = true;
}
if ( !groundentity && pm.groundentity && ( ( pm.s.velocity[ 2 ] < -1600 ) || falling ) )
{
falling = false;
if ( waterlevel <= 2 )
{
TempAnim( "land", NULL );
}
}
if ( ( !onladder ) && !pm.groundentity && ( velocity[ 2 ] < -200 ) && ( pm.s.velocity[ 2 ] > -2000 ) )
{
if ( !waterlevel )
{
TempAnim( "fall", NULL );
falling = true;
}
}
//###
if(!pm.groundentity && concussion_timer)
concussion_timer = level.time + 2;
//###
}
// save results of pmove
GetMoveInfo( &pm );
TouchStuff( &pm );
if ( !level.playerfrozen )
CheckButtons();
if ( whereami->value )
{
static Vector last;
if ( worldorigin != last )
{
last = worldorigin;
gi.dprintf("x %8.2f y%8.2f z %8.2f area %2d\n", worldorigin[0], worldorigin[1], worldorigin[2], edict->areanum );
}
}
}
/*
==============
ClientThink
This will be called once for each client frame, which will
usually be a couple times for each server frame.
==============
*/
EXPORT_FROM_DLL void Player::ClientThink
(
Event *ev
)
{
int i; //###
if ( !has_thought )
{
// let any threads waiting on us know they can go ahead
Director.PlayerSpawned();
has_thought = true;
//###
if(deathmatch->value)
{
PostEvent(EV_Player_WelcomeMessage, 1);
}
//###
}
//### added tracking of more buttons
// new_buttons = current_ucmd->buttons & ~buttons;
// buttons = current_ucmd->buttons;
i = current_ucmd->buttons;
if(current_ucmd->upmove > 0)
i |= BUTTON_JUMP;
new_buttons = i & ~buttons;
buttons = i;
// check for using a movement capturer
if(movecapturer)
{
movecapturer->CaptureMovement(current_ucmd);
}
// check rope stuff
if(rope_grabbed)
{
// check release
if(new_buttons & BUTTON_USE)
((RopePiece *)rope_grabbed.Ptr())->Release();
// check climb up
else if(buttons & BUTTON_JUMP)
((RopePiece *)rope_grabbed.Ptr())->ClimbUp();
// check climb down
else if(current_ucmd->upmove < 0)
((RopePiece *)rope_grabbed.Ptr())->ClimbDown();
}
//###
// set the current gravity axis
client->ps.pmove.gravity_axis = gravaxis;
// save light level the player is standing on for
// monster sighting AI
light_level = current_ucmd->lightlevel;
if ( level.intermissiontime )
{
client->ps.pmove.pm_type = PM_FREEZE;
// can exit intermission after 12 seconds
if ( level.time > level.intermissiontime + 12.0 && ( new_buttons & BUTTON_ANY ) )
{
level.exitintermission = true;
}
// Save cmd angles so that we can get delta angle movements next frame
client->resp.cmd_angles[ 0 ] = SHORT2ANGLE( current_ucmd->angles[ 0 ] );
client->resp.cmd_angles[ 1 ] = SHORT2ANGLE( current_ucmd->angles[ 1 ] );
client->resp.cmd_angles[ 2 ] = SHORT2ANGLE( current_ucmd->angles[ 2 ] );
return;
}
/*
if ( current_ucmd->debris )
{
float mult;
mult = SURFACE_DamageMultiplier( current_ucmd->debris << SURF_START_BIT );
mult *= 2;
if ( mult > 0 )
{
Damage( world, world, ( int )mult, worldorigin, vec_zero, vec_zero, 0, 0, MOD_DEBRIS, -1, -1, 1.0f );
}
}
*/
if (
( client->ps.pmove.pm_flags & PMF_TIME_TELEPORT ) &&
( savedfov ) &&
( client->ps.pmove.pm_time < 95 )
)
{
Event * ev;
ev = new Event( EV_Player_RestoreFov );
ProcessEvent( ev );
}
//### changed to allow support for the hoverbike
// if ( !vehicle || !vehicle->Drive( current_ucmd ) )
if(!(vehicle && vehicle->Drive(current_ucmd)) && !(hoverbike && hoverbike->Ride(current_ucmd)))
//###
{
short temporigin[ 3 ];
if ( watchCamera != this )
{
//
// we save off the origin so that camera's origins are not messed up
//
temporigin[ 0 ] = client->ps.pmove.origin[ 0 ];
temporigin[ 1 ] = client->ps.pmove.origin[ 1 ];
temporigin[ 2 ] = client->ps.pmove.origin[ 2 ];
ClientMove( current_ucmd );
client->ps.pmove.origin[ 0 ] = temporigin[ 0 ];
client->ps.pmove.origin[ 1 ] = temporigin[ 1 ];
client->ps.pmove.origin[ 2 ] = temporigin[ 2 ];
}
else
{
ClientMove( current_ucmd );
}
}
else
{
CheckWater();
if ( !level.playerfrozen )
CheckButtons();
}
// If we're dying, check for respawn
assert( !( deadflag && deadflag != DEAD_DEAD && !animating ) );
if ( ( deadflag == DEAD_DEAD && ( level.time > respawn_time ) ) ||
( deadflag && !animating ) )
{
if ( ( deathmatch->value ) && ( !checked_dead_camera ) && ( level.time > ( respawn_time + 10 ) ) )
{
int num;
checked_dead_camera = true;
// Find the end node
num = G_FindTarget( 0, "endnode1" );
if ( num && thirdpersonCamera )
{
Entity * path;
Event * event;
event = new Event( EV_Camera_NormalAngles );
thirdpersonCamera->ProcessEvent( event );
event = new Event( EV_Camera_Orbit);
path = G_GetEntity( num );
event->AddEntity( path );
thirdpersonCamera->ProcessEvent( event );
event = new Event( EV_Camera_JumpCut );
thirdpersonCamera->ProcessEvent( event );
}
}
// wait for any button just going down
if ( new_buttons || ( DM_FLAG( DF_FORCE_RESPAWN ) ) )
{
ProcessEvent( EV_Player_Respawn );
}
}
//
// check to see if we want to get out of a cinematic or camera
//
if (
( viewmode == CAMERA_VIEW ) &&
(!movecapturer) && //### added for movement capturer
(
( buttons & BUTTON_ATTACK ) ||
( buttons & BUTTON_USE ) ||
( abs( current_ucmd->forwardmove ) >= 200 ) ||
( abs( current_ucmd->sidemove ) >= 200 ) ||
( current_ucmd->upmove > 0 )
)
)
{
if ( level.cinematic )
{
if (
( buttons & BUTTON_ATTACK ) ||
( buttons & BUTTON_USE ) ||
( current_ucmd->upmove > 0 )
)
{
if ( world->skipthread.length() > 1 )
{
ExecuteThread( world->skipthread );
}
}
}
else
{
if ( !( buttons & BUTTON_USE ) &&
!( buttons & BUTTON_ATTACK ) &&
!( trappedInQuantum ) &&
!( in_console )
)
{
SetCamera( NULL );
}
}
}
//###
// make sure that goggles and flashlight are off if in a camera view
if(viewmode == CAMERA_VIEW)
{
Flashlight *flashlight;
// make sure flashlight is off
flashlight = (Flashlight *)FindItem("Flashlight");
if(flashlight)
{
// turn the flashlight off
if(flashlight->lighton > 0)
flashlight->Use(NULL);
}
// make sure goggles are off
if(nightvision)
{
Goggles *goggles;
goggles = (Goggles *)FindItem("Goggles");
if(goggles)
{
// turn the goggles off
goggles->goggleson = false;
nightvision = false;
}
}
}
//###
// Save cmd angles so that we can get delta angle movements next frame
client->resp.cmd_angles[ 0 ] = SHORT2ANGLE( current_ucmd->angles[ 0 ] );
client->resp.cmd_angles[ 1 ] = SHORT2ANGLE( current_ucmd->angles[ 1 ] );
client->resp.cmd_angles[ 2 ] = SHORT2ANGLE( current_ucmd->angles[ 2 ] );
}
void Player::EventUseItem
(
Event *ev
)
{
const char *name;
name = ev->GetString( 1 );
assert( name );
if ( deadflag )
return;
if ( flags & ( FL_MUTANT|FL_SP_MUTANT ) )
return;
if ( currentWeapon && !currentWeapon->ChangingWeapons() )
{
if ( !Q_stricmp( name, currentWeapon->getClassname() ) )
{
Event *event;
event = new Event(EV_Weapon_SecondaryUse);
event->AddEntity(this);
currentWeapon->ProcessEvent(event);
return;
}
}
ProcessEvent( EV_Player_ZoomOut );
//### added check for FistsOnly switch
// if ( checkInheritance( &Weapon::ClassInfo, name ) )
if(checkInheritance(&Weapon::ClassInfo, name) ||
!stricmp(name, "FistsOnly"))
//###
useWeapon( name );
}
void Player::EventUseInventoryItem
(
Event *ev
)
{
if ( deadflag )
return;
if ( !currentItem )
return;
if ( flags & ( FL_MUTANT|FL_SP_MUTANT ) )
return;
currentItem->ProcessEvent(EV_InventoryItem_Use);
}
Weapon *Player::useWeapon
(
const char *weaponname
)
{
if ( vehicle && vehicle->HasWeapon() )
return NULL;
//### added for hoverbike weapon switching
else if(hoverbike)
{
// these should allow weapon switching using
// the 1, 2, & 3 keys.
if(!stricmp(weaponname, "Fists"))
{
hoverbike->SelectWeapon(HWMODE_ROCKETS);
}
else if(!stricmp(weaponname, "Magnum"))
{
hoverbike->SelectWeapon(HWMODE_CHAINGUN);
}
else if(!stricmp(weaponname, "Shotgun"))
{
hoverbike->SelectWeapon(HWMODE_MINES);
}
// secondary weapon switch keys because they're
// similar to those actual weapons
else if(!stricmp(weaponname, "Rocketlauncher"))
{
hoverbike->SelectWeapon(HWMODE_ROCKETS);
}
else if(!stricmp(weaponname, "Chaingun"))
{
hoverbike->SelectWeapon(HWMODE_CHAINGUN);
}
else if(!stricmp(weaponname, "Spidermine"))
{
hoverbike->SelectWeapon(HWMODE_MINES);
}
return NULL;
}
//###
else
{
//### added special dual magnum switching thing
// return Sentient::useWeapon( weaponname );
if(!currentWeapon)
return Sentient::useWeapon(weaponname);
if(!stricmp(weaponname, "FistsOnly"))
{
if(Q_stricmp(currentWeapon->getClassname(), "Fists"))
return Sentient::useWeapon("Fists");
else
return currentWeapon;
}
// if switching to magnum while not using dual magnums,
// switch to dual magnums if has one
else if (
!stricmp(weaponname, "Magnum") &&
Q_stricmp(currentWeapon->getClassname(), "DualMagnum") &&
HasItem("DualMagnum") &&
((Weapon *)FindItem("DualMagnum"))->HasAmmo()
)
{
return Sentient::useWeapon("DualMagnum");
}
else if(weaponoverride)
{
if (
!stricmp(weaponname, "Fists") &&
Q_stricmp(currentWeapon->getClassname(), "ConcussionGun") &&
HasItem("ConcussionGun")
)
{
// the concussiongun overrides the fists
return Sentient::useWeapon("ConcussionGun");
}
else if (
!stricmp(weaponname, "Shotgun") &&
Q_stricmp(currentWeapon->getClassname(), "PlasmaBow") &&
HasItem("PlasmaBow") &&
((Weapon *)FindItem("PlasmaBow"))->HasAmmo()
)
{
// the plasma bow overrides the shotgun
return Sentient::useWeapon("PlasmaBow");
}
else if (
!stricmp(weaponname, "AssaultRifle") &&
Q_stricmp(currentWeapon->getClassname(), "Flamethrower") &&
HasItem("Flamethrower") &&
((Weapon *)FindItem("Flamethrower"))->HasAmmo()
)
{
// the flamethrower overrides the assaultrifle
return Sentient::useWeapon("Flamethrower");
}
else if (
!stricmp(weaponname, "QuantumDestabilizer") &&
Q_stricmp(currentWeapon->getClassname(), "StingerPack") &&
HasItem("StingerPack") &&
((Weapon *)FindItem("StingerPack"))->HasAmmo()
)
{
// the stinger pack overrides the quantum destabilizer
return Sentient::useWeapon("StingerPack");
}
else
{
return Sentient::useWeapon( weaponname );
}
}
else
{
if (
!stricmp(weaponname, "Fists") &&
!HasItem("Fists")
)
{
return Sentient::useWeapon("ConcussionGun");
}
else if (
!stricmp(weaponname, "Shotgun") &&
(
!HasItem("Shotgun") ||
(
HasItem("Shotgun") &&
!((Weapon *)FindItem("Shotgun"))->HasAmmo()
)
)
)
{
return Sentient::useWeapon("PlasmaBow");
}
else if (
!stricmp(weaponname, "AssaultRifle") &&
(
!HasItem("AssaultRifle") ||
(
HasItem("AssaultRifle") &&
!((Weapon *)FindItem("AssaultRifle"))->HasAmmo()
)
)
)
{
return Sentient::useWeapon("Flamethrower");
}
else if (
!stricmp(weaponname, "QuantumDestabilizer") &&
(
!HasItem("QuantumDestabilizer") ||
(
HasItem("QuantumDestabilizer") &&
!((Weapon *)FindItem("QuantumDestabilizer"))->HasAmmo()
)
)
)
{
return Sentient::useWeapon("StingerPack");
}
else if (
!stricmp(weaponname, "RocketLauncher") &&
(
!HasItem("RocketLauncher") ||
(
HasItem("RocketLauncher") &&
!((Weapon *)FindItem("RocketLauncher"))->HasAmmo()
)
)
)
{
return Sentient::useWeapon("MissileLauncher");
}
else
{
return Sentient::useWeapon( weaponname );
}
}
//###
}
}
void Player::Take
(
Event *ev
)
{
const char *name;
name = ev->GetString( 1 );
assert( name );
if ( deadflag )
{
return;
}
if ( !stricmp( name, "all" ) )
{
if ( currentWeapon )
currentWeapon->DetachFromOwner();
SetCurrentWeapon( NULL );
FreeInventoryOfType("Weapon");
giveWeapon( "Fists" );
giveWeapon( "SpiderMine" );
}
else if ( checkInheritance( &Weapon::ClassInfo, name ) )
{
takeWeapon( name );
}
else if ( checkInheritance( &Ammo::ClassInfo, name ) )
{
EventTakeAmmo( ev );
}
else if ( checkInheritance( &Item::ClassInfo, name ) )
{
EventTakeItem( ev );
}
else
{
gi.cprintf( edict, PRINT_HIGH, "Unknown take object : %s\n", name );
}
}
void Player::GiveCheat
(
Event *ev
)
{
const char *name;
name = ev->GetString( 1 );
assert( name );
if ( deadflag )
{
return;
}
//### added for all evil kind
if(!stricmp(name, "evil"))
{
giveWeapon("EvilStingerPack");
giveWeapon("EvilConcussionGun");
giveWeapon("EvilPlasmaBow");
giveWeapon("EvilIP36");
giveWeapon("EvilFlamethrower");
// give some other stuff too
giveItem( "RiotHelmet", 666);
giveItem( "FlakJacket", 666);
giveItem( "FlakPants", 666);
giveItem("ShotgunClip", 666);
giveItem("Bullet10mm", 666);
giveItem("Bullet50mm", 666);
giveItem("BulletPulse", 666);
giveItem("BulletSniper", 666);
giveItem("Rockets", 666);
giveItem("SpiderMines", 666);
giveItem("Missiles", 666);
giveItem("IlludiumModules", 666);
giveItem("ConcussionBattery", 666);
giveItem("FlameFuel", 666);
giveItem("Flashlight", 666);
giveItem("Goggles", 666);
}
else if ( checkInheritance( &Weapon::ClassInfo, name ) )
// if ( checkInheritance( &Weapon::ClassInfo, name ) )
//###
{
giveWeapon( name );
}
else if ( checkInheritance( &Ammo::ClassInfo, name ) )
{
EventGiveAmmo( ev );
}
else if ( checkInheritance( &InventoryItem::ClassInfo, name ) )
{
if (ev->NumArgs() != 2)
{
gi.cprintf( edict, PRINT_HIGH, "Usage: give <inventory item name> <amount>\n");
return;
}
EventGiveInventoryItem( ev );
}
else if ( checkInheritance( &Item::ClassInfo, name ) )
{
EventGiveItem( ev );
}
}
void Player::GiveAllCheat
(
Event *ev
)
{
if ( deadflag )
{
return;
}
#ifdef SIN_DEMO
giveWeapon( "Fists" );
giveWeapon( "RocketLauncher" );
giveWeapon( "Magnum" );
giveWeapon( "SniperRifle" );
giveWeapon( "AssaultRifle" );
giveWeapon( "Shotgun" );
#else
giveWeapon( "Fists" );
giveWeapon( "RocketLauncher" );
giveWeapon( "Magnum" );
giveWeapon( "SniperRifle" );
giveWeapon( "AssaultRifle" );
giveWeapon( "ChainGun" );
giveWeapon( "PulseRifle" );
giveWeapon( "Shotgun" );
giveWeapon( "SpearGun" );
giveWeapon( "SpiderMine" );
giveWeapon( "MutantHands" );
giveWeapon( "QuantumDestabilizer" );
//### added our weapons
if(developer->value)
giveWeapon("SpriteGun");
giveWeapon("DualMagnum");
giveWeapon("MissileLauncher");
giveWeapon("PlasmaBow");
giveWeapon("ConcussionGun");
giveWeapon("StingerPack");
giveWeapon("FlameThrower");
giveWeapon("IP36");
//###
#endif
giveItem( "RiotHelmet", 100);
giveItem( "FlakJacket", 100);
giveItem( "FlakPants", 100);
//### added some more stuff to give all
giveItem("ShotgunClip", 50);
giveItem("Bullet10mm", 400);
giveItem("Bullet50mm", 400);
giveItem("BulletPulse", 500);
giveItem("BulletSniper", 50);
giveItem("Rockets", 100);
giveItem("SpiderMines", 50);
giveItem("Missiles", 50);
giveItem("IlludiumModules", 4);
giveItem("ConcussionBattery", 100);
giveItem("FlameFuel", 100);
giveItem("Flashlight", 999);
giveItem("Goggles", 1);
//###
}
void Player::GodCheat
(
Event *ev
)
{
const char *msg;
if ( ev->NumArgs() > 0 )
{
if ( ev->GetInteger( 1 ) )
{
flags |= FL_GODMODE;
}
else
{
flags &= ~FL_GODMODE;
}
}
else
{
flags ^= FL_GODMODE;
}
if ( ev->GetSource() == EV_FROM_CONSOLE )
{
if ( !( flags & FL_GODMODE ) )
{
msg = "godmode OFF\n";
}
else
{
msg = "godmode ON\n";
}
gi.cprintf( edict, PRINT_HIGH, msg );
}
}
void Player::Kill
(
Event *ev
)
{
if ( ( level.time - respawn_time ) < 5 )
{
return;
}
flags &= ~FL_GODMODE;
health = 0;
Damage( this, this, 10, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_PROTECTION, MOD_SUICIDE, -1, -1, 1.0f );
}
void Player::NoTargetCheat
(
Event *ev
)
{
const char *msg;
flags ^= FL_NOTARGET;
if ( !( flags & FL_NOTARGET ) )
{
msg = "notarget OFF\n";
}
else
{
msg = "notarget ON\n";
}
gi.cprintf( edict, PRINT_HIGH, msg );
}
void Player::NoclipCheat
(
Event *ev
)
{
const char *msg;
if ( vehicle )
{
msg = "Must exit vehicle first\n";
}
//### added for hoverbike
else if(hoverbike)
{
if(getMoveType() == MOVETYPE_NOCLIP)
{
setMoveType(MOVETYPE_WALK);
msg = "noclip OFF\n";
}
else
{
hoverbike->BikeGetOff();
// turn on noclip if got off bike
if(!hoverbike)
{
setMoveType(MOVETYPE_NOCLIP);
msg = "noclip ON\n";
}
else
{
msg = "No room to get off hoverbike\n";
}
}
}
//###
else if ( getMoveType() == MOVETYPE_NOCLIP )
{
setMoveType( MOVETYPE_WALK );
msg = "noclip OFF\n";
}
else
{
setMoveType( MOVETYPE_NOCLIP );
msg = "noclip ON\n";
}
gi.cprintf( edict, PRINT_HIGH, msg );
}
void Player::SpawnEntity
(
Event *ev
)
{
Entity *ent;
const char *name;
ClassDef *cls;
str text;
Vector forward;
Vector right;
Vector up;
Vector delta;
Vector v;
int n;
int i;
if ( ev->NumArgs() < 1 )
{
ev->Error( "Usage: spawn entityname [keyname] [value]..." );
return;
}
name = ev->GetString( 1 );
if ( !name || !name[ 0 ] )
{
ev->Error( "Must specify an entity name" );
return;
}
// create a new entity
G_InitSpawnArguments();
if ( name )
{
cls = getClassForID( name );
if ( !cls )
{
cls = getClass( name );
}
if ( !cls )
{
str n;
n = name;
if ( !strstr( n.c_str(), ".def" ) )
{
n += ".def";
}
G_SetSpawnArg( "model", n.c_str() );
}
else
{
G_SetSpawnArg( "classname", name );
}
}
angles.AngleVectors( &forward, &right, &up );
v = worldorigin + ( forward + up ) * 80;
text = va( "%f %f %f", v[ 0 ], v[ 1 ], v[ 2 ] );
G_SetSpawnArg( "origin", text.c_str() );
delta = worldorigin - v;
v = delta.toAngles();
text = va( "%f", v[ 1 ] );
G_SetSpawnArg( "angle", text.c_str() );
if ( ev->NumArgs() > 2 )
{
n = ev->NumArgs();
for( i = 2; i <= n; i += 2 )
{
G_SetSpawnArg( ev->GetString( i ), ev->GetString( i + 1 ) );
}
}
cls = G_GetClassFromArgs();
if ( !cls )
{
ev->Error( "'%s' is not a valid entity name", name );
return;
}
ent = ( Entity * )cls->newInstance();
G_InitSpawnArguments();
}
void Player::SpawnActor
(
Event *ev
)
{
Entity *ent;
str name;
str text;
Vector forward;
Vector right;
Vector up;
Vector delta;
Vector v;
int n;
int i;
ClassDef *cls;
if ( ev->NumArgs() < 1 )
{
ev->Error( "Usage: actor [modelname] [keyname] [value]..." );
return;
}
name = ev->GetString( 1 );
if ( !name[ 0 ] )
{
ev->Error( "Must specify a model name" );
return;
}
if ( !strstr( name.c_str(), ".def" ) )
{
name += ".def";
}
// create a new entity
G_InitSpawnArguments();
G_SetSpawnArg( "model", name.c_str() );
angles.AngleVectors( &forward, &right, &up );
v = worldorigin + ( forward + up ) * 80;
text = va( "%f %f %f", v[ 0 ], v[ 1 ], v[ 2 ] );
G_SetSpawnArg( "origin", text.c_str() );
delta = worldorigin - v;
v = delta.toAngles();
text = va( "%f", v[ 1 ] );
G_SetSpawnArg( "angle", text.c_str() );
if ( ev->NumArgs() > 2 )
{
n = ev->NumArgs();
for( i = 2; i <= n; i += 2 )
{
G_SetSpawnArg( ev->GetString( i ), ev->GetString( i + 1 ) );
}
}
cls = G_GetClassFromArgs();
if ( !cls )
{
G_SetSpawnArg( "model", name.c_str() );
ent = new Actor;
}
else
{
ent = ( Entity * )cls->newInstance();
}
G_InitSpawnArguments();
}
void Player::EventPreviousWeapon
(
Event *ev
)
{
if ( deadflag )
{
return;
}
//### added for hoverbike weapon switching
if(hoverbike)
{
hoverbike->PreviousWeapon();
return;
}
//###
if ( vehicle && vehicle->HasWeapon() )
return;
if ( flags & (FL_MUTANT|FL_SP_MUTANT) )
return;
// Cycle backwards through weapons
if ( currentWeapon && currentWeapon->AttackDone() && !currentWeapon->ChangingWeapons() )
{
ProcessEvent( EV_Player_ZoomOut );
ChangeWeapon( PreviousWeapon( currentWeapon ) );
}
}
void Player::EventNextWeapon
(
Event *ev
)
{
if ( deadflag )
{
return;
}
//### added for hoverbike weapon switching
if(hoverbike)
{
hoverbike->NextWeapon();
return;
}
//###
if ( vehicle && vehicle->HasWeapon() )
return;
if ( flags & (FL_MUTANT|FL_SP_MUTANT) )
return;
// Cycle forwards through weapons
if ( currentWeapon && currentWeapon->AttackDone() && !currentWeapon->ChangingWeapons() )
{
ProcessEvent( EV_Player_ZoomOut );
ChangeWeapon( NextWeapon( currentWeapon ) );
}
}
void Player::EventNextItem
(
Event *ev
)
{
Item *nextItem;
if ( deadflag )
return;
nextItem = NextItem( currentItem );
if (nextItem)
currentItem = (InventoryItem *)nextItem;
}
void Player::EventPreviousItem
(
Event *ev
)
{
Item *prevItem;
if ( deadflag )
return;
prevItem = PrevItem( currentItem );
if (prevItem)
currentItem = (InventoryItem *)prevItem;
}
void Player::GameVersion
(
Event *ev
)
{
gi.cprintf( edict, PRINT_HIGH, "%s : %s\n", GAMEVERSION, __DATE__ );
}
void Player::Fov
(
Event *ev
)
{
if ( ev->NumArgs() < 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Fov = %d\n", fov );
return;
}
fov = ev->GetFloat( 1 );
if ( ( fov < 90 ) && DM_FLAG( DF_FIXED_FOV ) )
{
fov = 90;
return;
}
if ( fov < 1 )
fov = 90;
else if ( fov > 160 )
fov = 160;
}
void Player::SaveFov
(
Event *ev
)
{
savedfov = fov;
}
void Player::RestoreFov
(
Event *ev
)
{
if ( savedfov )
fov = savedfov;
savedfov = 0;
}
void Player::ToggleViewMode
(
Event *ev
)
{
if ( viewmode == CAMERA_VIEW || ( vehicle && !vehicle->IsDrivable() ) )
{
return;
}
//### can't toggle while in a movement capturer
if(movecapturer)
return;
//###
// Spectators switch between a camera view and noclip
if ( spectator )
{
spectator_distance = 128;
if ( defaultViewMode == FIRST_PERSON )
{
defaultViewMode = SPECTATOR;
SetViewMode( SPECTATOR );
}
else
{
defaultViewMode = FIRST_PERSON;
SetViewMode( FIRST_PERSON );
}
return;
}
if ( defaultViewMode == FIRST_PERSON )
{
defaultViewMode = THIRD_PERSON;
SetViewMode( THIRD_PERSON );
}
else
{
defaultViewMode = FIRST_PERSON;
SetViewMode( FIRST_PERSON );
}
}
/*
=========================================================================
Player::ToggleZoomMode - Plays a zoom sound, sets the layout to display a
crosshair and calls the FOV event for the player.
=========================================================================
*/
void Player::ToggleZoomMode
(
Event *ev
)
{
if ( zoom_mode == ZOOMED_IN )
{
zoom_mode = ZOOMED_OUT;
fov = atof( Info_ValueForKey( client->pers.userinfo, "fov" ) );
if ( fov < 1 )
{
fov = 90;
}
else if ( fov > 160 )
{
fov = 160;
}
RandomGlobalSound( "scope_zoomout", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_MovementSound );
}
else
{
RandomGlobalSound( "scope_zoomin", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_MovementSound );
zoom_mode = ZOOMED_IN;
fov = 20;
}
}
void Player::ZoomOut
(
Event *ev
)
{
if ( zoom_mode == ZOOMED_IN )
{
zoom_mode = ZOOMED_OUT;
fov = atof( Info_ValueForKey( client->pers.userinfo, "fov" ) );
if ( fov < 1 )
{
fov = 90;
}
else if ( fov > 160 )
{
fov = 160;
}
}
}
void Player::EnterConsole
(
Event *ev
)
{
velocity = vec_zero;
in_console = true;
con_name = ev->GetString(1);
// Create a console variable with the entnum of this player
// who used it.
Director.CreateConsoleUser(con_name.c_str(), entnum);
}
void Player::ExitConsole
(
Event *ev
)
{
ProcessEvent(EV_KickFromConsole);
in_console = false;
con_name = "";
SetCamera( NULL );
showModel();
hidestats = false;
}
void Player::KickConsole
(
Event *ev
)
{
char msg[ MAX_MSGLEN ];
in_console = false;
// Send a message to the client to kick us out of this console.
if ( !consoleManager.ConsoleExists( con_name.c_str() ) )
{
Event event = new Event( ev );
PostEvent(event,1.0f);
return;
}
sprintf( msg, "pku %s", con_name.c_str() );
gi.WriteByte (svc_console_command);
gi.WriteString (msg);
gi.unicast (edict, true);
}
/*
===============
CalcRoll
===============
*/
float Player::CalcRoll
(
void
)
{
float sign;
float side;
float value;
Vector r;
angles.AngleVectors( NULL, &r, NULL );
side = velocity * r;
sign = side < 0 ? -4 : 4;
side = fabs( side );
value = sv_rollangle->value;
if ( side < sv_rollspeed->value )
{
side = side * value / sv_rollspeed->value;
}
else
{
side = value;
}
return side * sign;
}
/*
===============
CalcViewOffset
Auto pitching on slopes?
fall from 128: 400 = 160000
fall from 256: 580 = 336400
fall from 384: 720 = 518400
fall from 512: 800 = 640000
fall from 640: 960 =
damage = deltavelocity*deltavelocity * 0.0001
===============
*/
void Player::CalcViewOffset
(
void
)
{
float bob;
float ratio;
float pitch;
float roll;
float delta;
Vector forward;
Vector right;
Vector ang;
// Initialize kick for this frame
v_kick = vec_zero;
//### hoverbike view stuff
if(hoverbike)
{
// add angles based on weapon kick
v_kick = kick_angles;
// add angles based on damage kick
ratio = ( v_dmg_time - level.time ) / DAMAGE_TIME;
if ( ratio < 0 )
{
ratio = 0;
v_dmg_pitch = 0;
v_dmg_roll = 0;
}
pitch = v_kick.pitch() + ratio * v_dmg_pitch;
roll = v_kick.roll() + ratio * v_dmg_roll;
// calc view roll from bike
delta = hoverbike->move_angles[ROLL]*0.6;
if(delta > 31.999)
delta = 31.999;
else if(delta < -31.999)
delta = -31.999;
roll += delta;
// add in view angle jitter
if(jitter_angle)
{
delta = (jitter_angle + jitter_angle*random())*0.5;
if(random() < 0.5)
delta *= -1;
pitch += delta;
if(pitch > 31.999)
pitch = 31.999;
else if(pitch < -31.999)
pitch = -31.999;
delta = (jitter_angle + jitter_angle*random())*0.5;
if(random() < 0.5)
delta *= -1;
roll += delta;
if(roll > 31.999)
roll = 31.999;
else if(roll < -31.999)
roll = -31.999;
delta = (jitter_angle + jitter_angle*random())*0.5;
if(random() < 0.5)
delta *= -1;
delta += v_kick.yaw();
if(delta > 31.999)
delta = 31.999;
else if(delta < -31.999)
delta = -31.999;
v_kick.setYaw(delta);
}
v_kick.setPitch(pitch);
v_kick.setRoll(roll);
// add kick offset
v_offset = kick_origin;
// add view height
v_offset[gravity_axis[gravaxis].z] += (HOVERBIKE_EYE_HEIGHT - 4)*gravity_axis[gravaxis].sign;
// add hoverbike's view bobbing
v_offset[gravity_axis[gravaxis].z] += hoverbike->bobfrac*3*gravity_axis[gravaxis].sign;
// add in view offset jitter
if(jitter_offset)
{
v_offset.x += jitter_offset* crandom();
v_offset.y += jitter_offset* crandom();
v_offset.z += jitter_offset* crandom();
}
// absolutely bound offsets
// so the view can never be outside the player box
if(v_offset.x < -16)
{
v_offset.x = -16;
}
else if(v_offset.x > 16)
{
v_offset.x = 16;
}
if(v_offset.y < -16)
{
v_offset.y = -16;
}
else if(v_offset.y > 16)
{
v_offset.y = 16;
}
if(v_offset.z < -10)
{
v_offset.z = -10;
}
else if(v_offset.z > HOVERBIKE_HEIGHT)
{
v_offset.z = HOVERBIKE_HEIGHT;
}
// clear weapon kicks
kick_origin = vec_zero;
kick_angles = vec_zero;
return;
}
//###
// don't add any kick when dead
if ( !deadflag )
{
// add angles based on weapon kick
v_kick = kick_angles;
// add angles based on damage kick
ratio = ( v_dmg_time - level.time ) / DAMAGE_TIME;
if ( ratio < 0 )
{
ratio = 0;
v_dmg_pitch = 0;
v_dmg_roll = 0;
}
pitch = v_kick.pitch() + ratio * v_dmg_pitch;
roll = v_kick.roll() + ratio * v_dmg_roll;
// add pitch based on fall kick
ratio = ( fall_time - level.time ) / FALL_TIME;
if ( ratio < 0 )
{
ratio = 0;
}
pitch += ratio * fall_value;
// calculate forward and right based upon yaw
ang[ 1 ] = v_angle[ 1 ];
ang.AngleVectors( &forward, &right, NULL );
//FIXME: need to handle alternate gravity axis
// add pitch based on forward velocity
delta = velocity * forward;
pitch += delta * run_pitch->value;
// add roll based on side velocity
delta = -( velocity * right );
roll += delta * run_pitch->value;
// add angles based on bob
delta = bobfracsin * bob_pitch->value * xyspeed;
if ( ( waterlevel <= 1 ) && ( client->ps.pmove.pm_flags & PMF_DUCKED ) )
{
delta *= 6; // crouching
}
pitch += delta;
delta = bobfracsin * bob_roll->value * xyspeed;
if ( ( waterlevel <= 1 ) && ( client->ps.pmove.pm_flags & PMF_DUCKED ) )
{
delta *= 6; // crouching
}
if ( bobcycle & 1 )
{
delta = -delta;
}
roll += delta;
//### add in view angle jitter
if(jitter_angle)
{
delta = (jitter_angle*crandom() + jitter_angle)*0.5;
pitch += delta;
if(pitch > 31.999)
pitch = 31.999;
else if(pitch < -31.999)
pitch = -31.999;
delta = (jitter_angle*crandom() + jitter_angle)*0.5;
roll += delta;
if(roll > 31.999)
roll = 31.999;
else if(roll < -31.999)
roll = -31.999;
delta = v_kick.yaw();
delta += (jitter_angle*crandom() + jitter_angle)*0.5;
if(delta > 31.999)
delta = 31.999;
else if(delta < -31.999)
delta = -31.999;
v_kick.setYaw(delta);
}
//###
v_kick.setPitch( pitch );
v_kick.setRoll( roll );
}
// add fall height
ratio = ( fall_time - level.time ) / FALL_TIME;
if ( ratio < 0 )
{
ratio = 0;
}
v_offset = Vector( 0, 0, viewheight - ratio * fall_value * 0.4 );
// add bob height
bob = bobfracsin * xyspeed * bob_up->value;
if ( bob > 6 )
{
bob = 6;
}
//gi.DebugGraph (bob *2, 255);
v_offset.z += bob;
// add kick offset
v_offset += kick_origin;
//### add in view offset jitter
if(jitter_offset)
{
v_offset.x += (jitter_offset*crandom() + jitter_offset)*0.5;
v_offset.y += (jitter_offset*crandom() + jitter_offset)*0.5;
v_offset.z += (jitter_offset*crandom() + jitter_offset)*0.5;
}
//###
// absolutely bound offsets
// so the view can never be outside the player box
if ( v_offset[ 0 ] < -14 )
{
v_offset[ 0 ] = -14;
}
else if ( v_offset[ 0 ] > 14 )
{
v_offset[ 0 ] = 14;
}
if ( v_offset[ 1 ] < -14 )
{
v_offset[ 1 ] = -14;
}
else if ( v_offset[ 1 ] > 14 )
{
v_offset[ 1 ] = 14;
}
if ( v_offset[ 2 ] < 0 )
{
v_offset[ 2 ] = 0;
}
else if ( v_offset[ 2 ] > STAND_HEIGHT - 2 )
{
v_offset[ 2 ] = STAND_HEIGHT - 2;
}
// clear weapon kicks
kick_origin = vec_zero;
kick_angles = vec_zero;
}
/*
==============
CalcGunOffset
==============
*/
void Player::CalcGunOffset
(
void
)
{
int i;
float delta;
//### for hoverbike viewmodel
if(hoverbike)
{
v_gunangles[PITCH] = 0;
v_gunangles[YAW] = 0;
v_gunangles[ROLL] = 0;
// get roll angle from bike
v_gunangles[ROLL] = hoverbike->move_angles[ROLL]*0.4;
// calc pitch angle from bike
delta = (hoverbike->move_angles[PITCH] - v_angle[PITCH])*gravity_axis[gravaxis].sign;
if(delta > 180)
delta -= 360;
if(delta < -180)
delta += 360;
delta *= 0.25;
if(delta > 30) // upper limit on pitching
delta = 30;
if(delta < -13) // lower limit on pitching
delta = -13;
v_gunangles[PITCH] = delta;
// gun angles from delta yaw
delta = oldviewangles[YAW] - v_angle[YAW];
if(delta > 180)
{
delta -= 360;
}
if(delta < -180)
{
delta += 360;
}
if(delta > 45)
{
delta = 45;
}
if(delta < -45)
{
delta = -45;
}
v_gunangles[ROLL] += 0.2 * delta;
v_gunangles[YAW] += 0.15 * delta;
// add in view angle jitter
if(jitter_angle)
{
for(i = 0; i < 3; i++)
{
delta = (jitter_angle*crandom() + jitter_angle)*0.25;
v_gunangles[i] += delta;
}
}
// clamp pitch
if(v_gunangles[PITCH] > 31.999)
v_gunangles[PITCH] = 31.999;
else if(v_gunangles[PITCH] < -15)
v_gunangles[PITCH] = -15;
// clamp yaw
if(v_gunangles[YAW] > 31.999)
v_gunangles[YAW] = 31.999;
else if(v_gunangles[YAW] < -31.999)
v_gunangles[YAW] = -31.999;
// clamp roll
if(v_gunangles[ROLL] > 31.999)
v_gunangles[ROLL] = 31.999;
else if(v_gunangles[ROLL] < -31.999)
v_gunangles[ROLL] = -31.999;
oldviewangles = v_angle;
// gun height
VectorClear(v_gunoffset);
// add in view offset jitter
if(jitter_offset)
{
for(i = 0; i < 3; i++)
v_gunoffset[i] += (jitter_offset*crandom() + jitter_offset)*0.25;
}
// put gun offset and angles into client playerstate
client->ps.gunoffset[0] = v_gunoffset[0];
client->ps.gunoffset[1] = v_gunoffset[1];
client->ps.gunoffset[2] = v_gunoffset[2];
client->ps.gunangles[0] = v_gunangles[0];
client->ps.gunangles[1] = v_gunangles[1];
client->ps.gunangles[2] = v_gunangles[2];
return;
}
//###
// gun angles from bobbing
v_gunangles[ ROLL ] = xyspeed * bobfracsin * 0.005;
v_gunangles[ YAW ] = xyspeed * bobfracsin * 0.01;
if ( bobcycle & 1 )
{
v_gunangles[ ROLL ] = -v_gunangles[ ROLL ];
v_gunangles[ YAW ] = -v_gunangles[ YAW ];
}
v_gunangles[ PITCH ] = xyspeed * bobfracsin * 0.005;
// gun angles from delta movement
for( i = 0; i < 3; i++ )
{
delta = oldviewangles[ i ] - v_angle[ i ];
if ( delta > 180 )
{
delta -= 360;
}
if ( delta < -180 )
{
delta += 360;
}
if ( delta > 45 )
{
delta = 45;
}
if ( delta < -45 )
{
delta = -45;
}
if ( i == YAW )
{
v_gunangles[ ROLL ] += 0.1 * delta;
}
v_gunangles[ i ] += 0.2 * delta;
}
//### add in view angle jitter
if(jitter_angle)
{
for(i = 0; i < 3; i++)
{
delta = (jitter_angle*crandom() + jitter_angle)*0.3;
v_gunangles[i] += delta;
if(v_gunangles[i] > 25)
v_gunangles[i] = 25;
else if(v_gunangles[i] < -25)
v_gunangles[i] = -25;
}
}
//###
oldviewangles = v_angle;
// gun height
VectorClear( v_gunoffset );
// gun_x / gun_y / gun_z are development tools
for( i = 0; i < 3; i++ )
{
v_gunoffset[ i ] += orientation[ 0 ][ i ] * gun_y->value;
v_gunoffset[ i ] += orientation[ 1 ][ i ] * gun_x->value;
v_gunoffset[ i ] += orientation[ 2 ][ i ] * -gun_z->value;
}
//### add in view offset jitter
if(jitter_offset)
{
for(i = 0; i < 3; i++)
v_gunoffset[i] += (jitter_offset*crandom() + jitter_offset)*0.25;
}
//###
//
// put gun offset and angles into client playerstate
//
client->ps.gunoffset[ 0 ] = v_gunoffset[0];
client->ps.gunoffset[ 1 ] = v_gunoffset[1];
client->ps.gunoffset[ 2 ] = v_gunoffset[2];
client->ps.gunangles[ 0 ] = v_gunangles[0];
client->ps.gunangles[ 1 ] = v_gunangles[1];
client->ps.gunangles[ 2 ] = v_gunangles[2];
if ( currentWeapon )
{
currentWeapon->SetGravityAxis( gravaxis );
}
}
void Player::GravityNodes
(
void
)
{
Vector grav,gravnorm,velnorm;
float dot;
qboolean force;
//
// Check for gravity pulling points
//
// no pull on ladders or during waterjumps
if ( onladder || ( client->ps.pmove.pm_flags & PMF_TIME_WATERJUMP ) )
{
return;
}
grav = gravPathManager.CalculateGravityPull( *this, worldorigin, &force );
// Check for unfightable gravity.
if ( force && grav != vec_zero )
{
velnorm = velocity;
velnorm.normalize();
gravnorm = grav;
gravnorm.normalize();
dot = gravnorm.x * velnorm.x + gravnorm.y * velnorm.y + gravnorm.z * velnorm.z;
// This prevents the player from trying to swim upstream
if ( dot < 0 )
{
float tempdot;
Vector tempvec;
tempdot = 0.2f - dot;
tempvec = velocity * tempdot;
velocity = velocity - tempvec;
}
}
#if 0
OutputDebugString(va("DOT : %f\n", dot));
OutputDebugString(va("GRAV: %f %f %f\n", grav.x, grav.y, grav.z));
OutputDebugString(va("VELN: %f %f %f\n", velnorm.x,velnorm.y,velnorm.z));
OutputDebugString(va("GRVN: %f %f %f\n", gravnorm.x,gravnorm.y,gravnorm.z));
#endif
velocity = velocity + grav;
}
/*
=============
CheckWater
=============
*/
void Player::CheckWater
(
void
)
{
Vector point;
int cont;
int sample1;
int sample2;
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
unlink();
if ( vehicle )
{
vehicle->unlink();
}
//
// get waterlevel, accounting for ducking
//
waterlevel = 0;
watertype = 0;
sample2 = viewheight - mins[ grav.z ];
sample1 = sample2 / 2;
point[ grav.x ] = worldorigin[ grav.x ];
point[ grav.y ] = worldorigin[ grav.y ];
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + grav.sign;
cont = gi.pointcontents( point.vec3() );
if ( cont & MASK_WATER )
{
watertype = cont;
waterlevel = 1;
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + sample1;
cont = gi.pointcontents( point.vec3() );
if ( cont & MASK_WATER )
{
waterlevel = 2;
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + sample2;
cont = gi.pointcontents( point.vec3() );
if ( cont & MASK_WATER )
{
waterlevel = 3;
}
}
}
link();
if ( vehicle )
{
vehicle->link();
}
}
/*
=============
WorldEffects
=============
*/
void Player::WorldEffects
(
void
)
{
//###
// check for new view angle jitter values
if(anglejitter_time > level.time)
{
if(anglejitter_magnitude >= jitter_angle)
{
// got a new jitter magnitude to go to
jitter_angle = anglejitter_magnitude;
jitter_angle_falloff = anglejitter_falloff;
// turn off the entity timmer
jitter_angle_enttime = 0;
}
else
{
// current jitter is higher,
// so fade down to the new value
jitter_angle -= jitter_angle_falloff*FRAMETIME;
if(jitter_angle < anglejitter_magnitude)
jitter_angle = anglejitter_magnitude;
}
}
else if(jitter_angle > 0)
{
// check if retaining an angle jitter from an entity
if(jitter_angle_enttime < level.time)
{
// need to fade out current jitter
jitter_angle -= jitter_angle_falloff*FRAMETIME;
if(jitter_angle < 0)
jitter_angle = 0;
}
}
else
{
jitter_angle = 0;
jitter_angle_enttime = 0;
}
// check for new view offset jitter values
if(offsetjitter_time > level.time)
{
if(offsetjitter_magnitude >= jitter_offset)
{
// got a new jitter magnitude to go to
jitter_offset = offsetjitter_magnitude;
jitter_offset_falloff = offsetjitter_falloff;
// turn off the entity timmer
jitter_offset_enttime = 0;
}
else
{
// current jitter is higher,
// so fade down to the new value
jitter_offset -= jitter_offset_falloff*FRAMETIME;
if(jitter_offset < offsetjitter_magnitude)
jitter_offset = offsetjitter_magnitude;
}
}
else if(jitter_offset > 0)
{
// check if retaining an angle jitter from an entity
if(jitter_offset_enttime < level.time)
{
// need to fade out current jitter
jitter_offset -= jitter_offset_falloff*FRAMETIME;
if(jitter_offset < 0)
jitter_offset = 0;
}
}
else
{
jitter_offset = 0;
jitter_offset_enttime = 0;
}
//###
if ( movetype == MOVETYPE_NOCLIP )
{
// don't need air
air_finished = level.time + 20;
return;
}
//
// Check for earthquakes
//
if ( groundentity && ( level.earthquake > level.time ) )
{
velocity += Vector(EARTHQUAKE_STRENGTH*G_CRandom(),EARTHQUAKE_STRENGTH*G_CRandom(),fabs(150*G_CRandom()));
}
//
// if just entered a water volume, play a sound
//
if ( !old_waterlevel && waterlevel )
{
if ( watertype & CONTENTS_LAVA )
{
RandomSound( "snd_burn", 1, CHAN_BODY, ATTN_NORM );
ProcessEvent( EV_MovementSound );
}
else if ( ( watertype & CONTENTS_WATER ) && waterlevel < 3)
{
RandomGlobalSound( "impact_playersplash", 1, CHAN_BODY, ATTN_NORM );
ProcessEvent( EV_MovementSound );
}
flags |= FL_INWATER;
}
//
// if just completely exited a water volume, play a sound
//
if ( old_waterlevel && !waterlevel )
{
RandomGlobalSound( "impact_playerleavewater", 1, CHAN_BODY, ATTN_NORM );
ProcessEvent( EV_MovementSound );
flags &= ~FL_INWATER;
}
//
// check for head just going under water
//
if ( old_waterlevel && old_waterlevel != 3 && waterlevel == 3 )
{
RandomGlobalSound( "impact_playersubmerge", 1, CHAN_BODY, ATTN_NORM );
ProcessEvent( EV_MovementSound );
}
//
// check for head just coming out of water
//
if ( old_waterlevel == 3 && waterlevel != 3 )
{
if ( air_finished < level.time )
{
// gasp for air
RandomSound( "snd_gasp", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_VoiceSound );
}
else if ( air_finished < level.time + 11 )
{
// just break surface
RandomSound( "snd_gasp", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_VoiceSound );
}
}
//
// check for lava
//
if ( watertype & CONTENTS_LAVA )
{
if ( next_drown_time < level.time )
{
next_drown_time = level.time + 0.2f;
//### added specifiable lava damage
// Damage( world, world, 10 * waterlevel, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f );
Damage( world, world, level.lavadamage * waterlevel, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f );
//###
}
}
//
// check for drowning
//
if (waterlevel == 3)
{
// if out of air, start drowning
if ( ( air_finished < level.time ) && !( flags & FL_OXYGEN ) && !( flags & FL_GODMODE ) )
{
// drown!
if ( next_drown_time < level.time && health > 0 )
{
next_drown_time = level.time + 1;
// take more damage the longer underwater
drown_damage += 2;
if ( drown_damage > 15 )
{
drown_damage = 15;
}
// play a gurp sound instead of a normal pain sound
if ( health <= drown_damage )
{
RandomSound( "snd_drown", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_PainSound );
}
else if ( rand() & 1 )
{
RandomSound( "snd_choke", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_PainSound );
}
else
{
RandomSound( "snd_choke", 1, CHAN_VOICE, ATTN_NORM );
ProcessEvent( EV_PainSound );
}
Damage( world, world, drown_damage, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_DROWN, -1, -1, 1.0f );
}
}
}
else
{
air_finished = level.time + 20;
drown_damage = 2;
}
GravityNodes();
old_waterlevel = waterlevel;
}
/*
=============
AddBlend
=============
*/
void Player::AddBlend
(
float r,
float g,
float b,
float a
)
{
float a2;
float a3;
if ( a <= 0 )
{
return;
}
// new total alpha
a2 = blend[ 3 ] + ( 1 - blend[ 3 ] ) * a;
// fraction of color from old
a3 = blend[ 3 ] / a2;
blend[ 0 ] = blend[ 0 ] * a3 + r * ( 1 - a3 );
blend[ 1 ] = blend[ 1 ] * a3 + g * ( 1 - a3 );
blend[ 2 ] = blend[ 2 ] * a3 + b * ( 1 - a3 );
blend[ 3 ] = a2;
}
/*
=============
CalcBlend
=============
*/
void Player::CalcBlend
(
void
)
{
int contents;
Vector vieworg;
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
blend[ 0 ] = blend[ 1 ] = blend[ 2 ] = blend[ 3 ] = 0;
// add for contents
vieworg[ grav.x ] = worldorigin[ grav.x ] + v_offset[ 0 ];
vieworg[ grav.y ] = worldorigin[ grav.y ] + v_offset[ 1 ] * grav.sign;
vieworg[ grav.z ] = worldorigin[ grav.z ] + v_offset[ 2 ] * grav.sign;
contents = gi.pointcontents( vieworg.vec3() );
if ( contents & CONTENTS_SOLID )
{
// Outside of world
AddBlend( 0.8, 0.5, 0.0, 0.2 );
}
else if ( contents & CONTENTS_LAVA )
{
AddBlend( level.lava_color[0], level.lava_color[1], level.lava_color[2], level.lava_alpha );
}
else if ( contents & CONTENTS_WATER )
{
AddBlend( level.water_color[0], level.water_color[1], level.water_color[2], level.water_alpha );
}
if ( contents & CONTENTS_LIGHTVOLUME )
{
AddBlend( level.lightvolume_color[0], level.lightvolume_color[1], level.lightvolume_color[2], level.lightvolume_alpha );
}
// Flashes
AddBlend( flash_color[0], flash_color[1], flash_color[2], flash_color[3] );
// add for zoom
if ( zoom_mode == ZOOMED_IN )
{
AddBlend( 0.4, 1, 0.1, 0.1 );
}
// add for damage
if ( damage_alpha > 0 )
{
AddBlend( damage_blend[ 0 ], damage_blend[ 1 ], damage_blend[ 2 ], damage_alpha );
}
if ( flags & FL_ADRENALINE )
{
AddBlend( 1.0f, 1.0f, 0.0f, 0.2f );
}
// drop the damage value
damage_alpha -= 0.06;
if ( damage_alpha < 0 )
{
damage_alpha = 0;
}
// Drop the flash
flash_color[3] -= 0.06;
if ( flash_color[3] < 0 )
{
flash_color[3] = 0;
}
//### added view blend for nuke explosions
if(nuke_alpha > 0)
{
// do fade in
if(nuke_alpha > 1)
{
if(nuke_alpha > 1.01)
AddBlend(nuke_blend[0], nuke_blend[1], nuke_blend[2], (nuke_alpha - 1));
//fade in the flash
nuke_alpha += 0.4;
// switch to fade out when we reach full opacity
if(nuke_alpha >= 2)
nuke_alpha = 1;
}
else // do fade out
{
AddBlend(nuke_blend[0], nuke_blend[1], nuke_blend[2], nuke_alpha);
// decay color to red
nuke_blend[1] -= 0.02;
nuke_blend[2] = nuke_blend[1];
// decay alpha
if(nuke_alpha > 0.6)
{
nuke_alpha -= 0.1;
}
else
{
nuke_alpha -= 0.035;
if(nuke_alpha < 0)
nuke_alpha = 0;
}
}
}
//###
}
//### function to init the nuke flash
void Player::StartNukeFlash(Vector fblend, float falpha)
{
float curra, newa;
if(nuke_alpha > 0)
{
// only apply new flash if it's brighter
if(nuke_alpha > 1)
curra = nuke_alpha - 1.0;
else
curra = nuke_alpha;
if(falpha > 1)
newa = falpha - 1.0;
else
newa = falpha;
if(newa < curra)
return;
}
nuke_blend = fblend;
nuke_alpha = falpha;
}
//###
/*
===============
P_DamageFeedback
Handles color blends and view kicks
===============
*/
void Player::DamageFeedback
(
void
)
{
float realcount;
float count;
// flash the backgrounds behind the status numbers
client->ps.stats[ STAT_FLASHES ] = 0;
if ( damage_blood )
{
client->ps.stats[ STAT_FLASHES ] |= 1;
}
// total points of damage shot at the player this frame
if ( !damage_blood )
{
// didn't take any damage
return;
}
count = damage_blood;
realcount = count;
if ( count < 10 )
{
// always make a visible effect
count = 10;
}
// the total alpha of the blend is always proportional to count
if ( damage_alpha < 0 )
{
damage_alpha = 0;
}
damage_alpha += count * 0.01;
if ( damage_alpha < 0.2 )
{
damage_alpha = 0.2;
}
if ( damage_alpha > 0.6 )
{
// don't go too saturated
damage_alpha = 0.6;
}
// the color of the blend will vary based on how much was absorbed
// by different armors
damage_blend = vec_zero;
if ( damage_blood )
{
damage_blend += ( damage_blood / realcount ) * bcolor;
}
//### add some view jitter from taking damage
count = realcount*0.2;
if(count < 2)
count = 2;
SetAngleJitter(count, count*2, 0.2);
count = realcount*0.2;
if(count < 1)
count = 1;
SetOffsetJitter(count, count*2, 0.2);
//###
//
// clear totals
//
damage_blood = 0;
}
EXPORT_FROM_DLL const char * Player::AnimPrefixForWeapon
(
void
)
{
if ( currentWeapon )
{
switch ( currentWeapon->GetType() )
{
case WEAPON_MELEE:
return "";
break;
case WEAPON_1HANDED:
return "1hand_";
break;
case WEAPON_2HANDED_HI:
return "hi2hand_";
break;
case WEAPON_2HANDED_LO:
return "lo2hand_";
break;
default:
return "";
break;
}
}
else
{
return "";
}
return "";
}
EXPORT_FROM_DLL void Player::ChooseAnim
(
void
)
{
str prefix;
str aname;
if ( deadflag )
{
return;
}
if ( vehicle )
{
falling = false;
animOverride = false;
SetAnim( vehicleanim.c_str() );
return;
}
//### added for hoverbike
if(hoverbike)
{
SetAnim("ride");
return;
}
//###
if ( onladder )
{
if ( falling )
{
Event * ev;
ev = new Event( EV_Sentient_AnimLoop );
ProcessEvent( ev );
falling = false;
}
if ( fabs( velocity[ 2 ] ) > 0 )
{
SetAnim( "climb" );
}
else if (!animOverride)
{
StopAnimating();
}
return;
}
if ( client->ps.pmove.pm_flags & PMF_DUCKED )
{
if ( !( old_pmove.pm_flags & PMF_DUCKED ) )
{
SetAnim( "crouch" );
return;
}
}
else if ( old_pmove.pm_flags & PMF_DUCKED )
{
SetAnim( "uncrouch" );
return;
}
//
// put the appropriate prefix on the animation
prefix = AnimPrefixForPlayer();
if ( currentWeapon )
{
if ( currentWeapon->WeaponRaising() )
{
aname = prefix + str( "readyweapon" );
SetAnim( aname.c_str() );
return;
}
else if ( currentWeapon->WeaponPuttingAway() )
{
aname = prefix + str( "putaway" );
SetAnim( aname.c_str() );
return;
}
else if ( currentWeapon->Reloading() )
{
aname = prefix + str( "reload" );
SetAnim( aname.c_str() );
return;
}
}
//
// append the prefix based on which weapon we are holding
//
prefix += str( AnimPrefixForWeapon() );
if ( waterlevel > 2 || (!groundentity && waterlevel ) )
{
if ( xyspeed > 20 )
{
aname = prefix + str( "run" );
}
else
{
aname = prefix + str( "idle" );
}
}
else
{
if ( xyspeed > 250 )
{
aname = prefix + str( "run" );
}
else if ( xyspeed > 20 )
{
if ( client->ps.pmove.pm_flags & PMF_DUCKED )
{
aname = prefix + str( "run" );
}
else
{
aname = prefix + str( "walk" );
}
}
else
{
aname = prefix + str( "idle" );
}
}
SetAnim( aname.c_str() );
}
void Player::SetCameraValues
(
Vector position,
Vector cameraoffset,
Vector ang,
Vector camerakick,
Vector vel,
float camerablend[ 4 ],
float camerafov
)
{
client->ps.viewangles[ 0 ] = ang[ 0 ];
client->ps.viewangles[ 1 ] = ang[ 1 ];
client->ps.viewangles[ 2 ] = ang[ 2 ];
client->ps.viewoffset[ 0 ] = cameraoffset[ 0 ];
client->ps.viewoffset[ 1 ] = cameraoffset[ 1 ];
client->ps.viewoffset[ 2 ] = cameraoffset[ 2 ];
client->ps.pmove.origin[ 0 ] = position[ 0 ] * 8;
client->ps.pmove.origin[ 1 ] = position[ 1 ] * 8;
client->ps.pmove.origin[ 2 ] = position[ 2 ] * 8;
client->ps.pmove.velocity[ 0 ] = vel[ 0 ] * 8.0;
client->ps.pmove.velocity[ 1 ] = vel[ 1 ] * 8.0;
client->ps.pmove.velocity[ 2 ] = vel[ 2 ] * 8.0;
client->ps.blend[ 0 ] = camerablend[ 0 ];
client->ps.blend[ 1 ] = camerablend[ 1 ];
client->ps.blend[ 2 ] = camerablend[ 2 ];
client->ps.blend[ 3 ] = camerablend[ 3 ];
client->ps.fov = camerafov;
client->ps.kick_angles[ 0 ] = camerakick[ 0 ];
client->ps.kick_angles[ 1 ] = camerakick[ 1 ];
client->ps.kick_angles[ 2 ] = camerakick[ 2 ];
}
void Player::SetCameraEntity
(
Entity *cameraEnt
)
{
assert( cameraEnt );
// In release, we should never be without a camera
if ( !cameraEnt )
{
cameraEnt = this;
}
// should we see the player's body?
if ( cameraEnt == this )
{
edict->s.renderfx |= RF_VIEWERMODEL;
if ( currentWeapon )
{
if ( !vehicle || vehicle->ShowWeapon() )
{
currentWeapon->edict->s.renderfx &= ~RF_DONTDRAW;
currentWeapon->edict->s.renderfx |= RF_VIEWERMODEL;
//###
if(hoverbike)
{
hoverbike->GuagesViewerOn();
}
//###
}
else
{
currentWeapon->edict->s.renderfx |= RF_DONTDRAW;
//###
if(hoverbike)
{
hoverbike->GuagesViewerOff();
}
//###
}
}
if ( vehicle && vehicle->IsDrivable() )
{
SetCameraValues( worldorigin, v_offset, v_angle, v_kick, velocity, blend, fov*1.25 );
}
//###
else if(hoverbike)
{
if(fov < 110)
SetCameraValues(worldorigin, v_offset, v_angle, v_kick, velocity, blend, 110);
else
SetCameraValues(worldorigin, v_offset, v_angle, v_kick, velocity, blend, fov);
}
//###
else
{
SetCameraValues( worldorigin, v_offset, v_angle, v_kick, velocity, blend, fov );
}
}
else
{
float noblend[ 4 ];
float camerafov;
Vector pos;
//###
static float c_jitter_angle = 0;
static float c_jitter_angle_falloff = 0;
static float c_jitter_offset = 0;
static float c_jitter_offset_falloff = 0;
Vector jitterang;
//###
/*
if ( vehicle && cameraEnt == thirdpersonCamera )
{
Event * event;
event = new Event( EV_Camera_SetDistance );
event->AddFloat( 256 );
cameraEnt->ProcessEvent( event );
}
*/
edict->s.renderfx &= ~RF_VIEWERMODEL;
if ( currentWeapon )
{
// Take the weapon out of the view
currentWeapon->edict->s.renderfx &= ~RF_VIEWERMODEL;
//###
if(hoverbike)
{
hoverbike->GuagesViewerOff();
}
//###
}
if ( in_console )
{
// Make the client act like a viewmodel so it doesn't block
// the camera to the console.
edict->s.renderfx |= RF_VIEWERMODEL;
}
// FIXME
// should add all these fields to entity or camera or whatever.
noblend[ 0 ] = 0;
noblend[ 1 ] = 0;
noblend[ 2 ] = 0;
noblend[ 3 ] = 0;
pos = vec_zero;
//### calc global jitter, but only in single player
jitterang = vec_zero;
if(!deathmatch->value)
{
if(anglejitter_time > level.time)
{
if(anglejitter_magnitude >= c_jitter_angle)
{
// got a new jitter magnitude to go to
c_jitter_angle = anglejitter_magnitude;
c_jitter_angle_falloff = anglejitter_falloff;
}
else
{
// current jitter is higher,
// so fade down to the new value
c_jitter_angle -= c_jitter_angle_falloff*FRAMETIME;
if(c_jitter_angle < anglejitter_magnitude)
c_jitter_angle = anglejitter_magnitude;
}
}
else if(c_jitter_angle > 0)
{
// need to fade out current jitter
c_jitter_angle -= c_jitter_angle_falloff*FRAMETIME;
if(c_jitter_angle < 0)
c_jitter_angle = 0;
}
else
{
c_jitter_angle = 0;
}
// check for new view offset jitter values
if(offsetjitter_time > level.time)
{
if(offsetjitter_magnitude >= c_jitter_offset)
{
// got a new jitter magnitude to go to
c_jitter_offset = offsetjitter_magnitude;
c_jitter_offset_falloff = offsetjitter_falloff;
}
else
{
// current jitter is higher,
// so fade down to the new value
c_jitter_offset -= c_jitter_offset_falloff*FRAMETIME;
if(c_jitter_offset < offsetjitter_magnitude)
c_jitter_offset = offsetjitter_magnitude;
}
}
else if(c_jitter_offset > 0)
{
// need to fade out current jitter
c_jitter_offset -= c_jitter_offset_falloff*FRAMETIME;
if(c_jitter_offset < 0)
c_jitter_offset = 0;
}
else
{
c_jitter_offset = 0;
}
if(c_jitter_angle)
{
float delta;
delta = (c_jitter_angle*crandom() + c_jitter_angle)*0.5;
jitterang[PITCH] += delta;
if(jitterang[PITCH] > 31.999)
jitterang[PITCH] = 31.999;
else if(jitterang[PITCH] < -31.999)
jitterang[PITCH] = -31.999;
delta = (c_jitter_angle*crandom() + c_jitter_angle)*0.5;
jitterang[YAW] += delta;
if(jitterang[YAW] > 31.999)
jitterang[YAW] = 31.999;
else if(jitterang[YAW] < -31.999)
jitterang[YAW] = -31.999;
delta = (c_jitter_angle*crandom() + c_jitter_angle)*0.5;
jitterang[ROLL] += delta;
if(jitterang[ROLL] > 31.999)
jitterang[ROLL] = 31.999;
else if(jitterang[ROLL] < -31.999)
jitterang[ROLL] = -31.999;
}
}
//###
camerafov = 90;
if ( cameraEnt->isSubclassOf( Mine ) )
{
Vector dir;
vec3_t mat[3];
camerafov = ( ( Projectile * )cameraEnt )->fov;
AnglesToMat( cameraEnt->worldangles.vec3(), mat );
dir = mat[0];
if ( cameraEnt->velocity == vec_zero )
{
pos = dir * 30;
}
else
{
pos = pos + Vector(0,0,1) * 30;
}
}
else if ( cameraEnt->isSubclassOf( Camera ) )
{
camerafov = ( ( Camera * )cameraEnt )->fov;
if ( cameraEnt->isSubclassOf( SecurityCamera ) )
cameraEnt->edict->s.renderfx |= RF_DONTDRAW;
}
else if ( trappedInQuantum )
{
Sentient *sent;
if ( cameraEnt->isSubclassOf( Sentient ) )
{
Vector dir;
sent = ( Sentient * )cameraEnt;
sent->GetMuzzlePositionAndDirection( &pos, &dir );
}
}
else if ( cameraEnt->isSubclassOf( Sentient ) )
{
pos = ( ( Sentient * )cameraEnt )->EyePosition() - cameraEnt->worldorigin;
}
//### added for guided missiles
else if(cameraEnt->isSubclassOf(MissileView))
{
Vector t[ 3 ];
Vector forward;
Vector right;
Vector up;
//set the missile's velocity and angle to the player view direction
v_angle.AngleVectors(&t[0], &t[1], &t[2]);
forward[gravity_axis[gravaxis].x] = t[0][0];
forward[gravity_axis[gravaxis].y] = t[0][1] * gravity_axis[gravaxis].sign;
forward[gravity_axis[gravaxis].z] = t[0][2] * gravity_axis[gravaxis].sign;
right[gravity_axis[gravaxis].x] = t[1][0];
right[gravity_axis[gravaxis].y] = t[1][1] * gravity_axis[gravaxis].sign;
right[gravity_axis[gravaxis].z] = t[1][2] * gravity_axis[gravaxis].sign;
up[gravity_axis[gravaxis].x] = t[2][0];
up[gravity_axis[gravaxis].y] = t[2][1] * gravity_axis[gravaxis].sign;
up[gravity_axis[gravaxis].z] = t[2][2] * gravity_axis[gravaxis].sign;
VectorsToEulerAngles(forward.vec3(), right.vec3(), up.vec3(), cameraEnt->angles.vec3());
cameraEnt->setAngles(cameraEnt->angles);
// correct velocity for gravityaxis
cameraEnt->velocity = forward*MISSILE_SPEED;
((MissileView *)cameraEnt)->SetupMissile();
camerafov = 110;
}
// add offset jitter
if(c_jitter_offset)
{
pos.x += (c_jitter_offset*crandom() + c_jitter_offset)*0.5;
pos.y += (c_jitter_offset*crandom() + c_jitter_offset)*0.5;
pos.z += (c_jitter_offset*crandom() + c_jitter_offset)*0.5;
}
SetCameraValues( cameraEnt->worldorigin, pos, cameraEnt->worldangles, jitterang,
cameraEnt->velocity, noblend, camerafov );
// SetCameraValues( cameraEnt->worldorigin, pos, cameraEnt->worldangles, vec_zero,
// cameraEnt->velocity, noblend, camerafov );
//###
//SetCameraValues( "0 0 64", pos, "0 270 0", vec_zero,
// cameraEnt->velocity, noblend, camerafov );
}
if ( flags & FL_SHIELDS )
{
edict->s.renderfx |= RF_ENVMAPPED;
}
else
edict->s.renderfx &= ~RF_ENVMAPPED;
if ( flags & FL_CLOAK )
{
setAlpha( 0 );
}
else
setAlpha( 1.0f );
if ( currentWeapon )
{
if ( flags & FL_SHIELDS )
currentWeapon->edict->s.renderfx |= RF_ENVMAPPED;
else
currentWeapon->edict->s.renderfx &= ~RF_ENVMAPPED;
if ( flags & FL_CLOAK )
{
currentWeapon->edict->s.alpha = 0.1f;
currentWeapon->edict->s.renderfx |= RF_TRANSLUCENT;
}
else
{
//### commented out this line because it interfiers
// with an alpha effected added for the plasma bow
// not really needed anyway
// currentWeapon->edict->s.alpha = 1.0f;
currentWeapon->edict->s.renderfx &= ~RF_TRANSLUCENT;
}
if ( cameraEnt != this )
{
if ( edict->s.renderfx & RF_DONTDRAW )
{
currentWeapon->edict->s.renderfx |= RF_DONTDRAW;
}
else
{
currentWeapon->edict->s.renderfx &= ~RF_DONTDRAW;
}
}
}
if ( ( viewmode == THIRD_PERSON ) && crosshair && currentWeapon )
{
Vector dir, src, end;
trace_t trace;
currentWeapon->GetMuzzlePosition( &src, &dir );
end = src + dir * 8192;
trace = G_FullTrace( src, vec_zero, vec_zero, end, 5, this, MASK_SHOT, "Player::SetCameraEntity" );
if ( trace.intersect.valid )
{
crosshair->edict->s.frame = 1;
}
else
{
crosshair->edict->s.frame = 0;
}
crosshair->setOrigin( trace.endpos - dir * 16 );
crosshair->edict->s.scale = ( crosshair->worldorigin - worldorigin ).length() / 256;
}
}
EXPORT_FROM_DLL void Player::CalcBob
(
void
)
{
float time;
Vector hvel;
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
//
// calculate speed and cycle to be used for
// all cyclic walking effects
//
hvel[ grav.x ] = velocity[ grav.x ];
hvel[ grav.y ] = velocity[ grav.y ];
hvel[ grav.z ] = 0;
xyspeed = hvel.length();
if ( xyspeed < 5 )
{
// start at beginning of cycle again
bobmove = 0;
bobtime = 0;
}
else if ( groundentity )
{
// so bobbing only cycles when on ground
if ( xyspeed > 210 )
{
bobmove = 0.25;
}
else if ( xyspeed > 100 )
{
bobmove = 0.125;
}
else
{
bobmove = 0.0625;
}
}
bobtime += bobmove;
time = bobtime;
if ( client->ps.pmove.pm_flags & PMF_DUCKED )
{
time *= 4;
}
bobcycle = ( int )time;
bobfracsin = fabs( sin( time * M_PI ) );
}
/*
=================
P_FallingDamage
=================
*/
void Player::FallingDamage
(
void
)
{
float delta;
float damage;
csurface_t *surf;
int z;
z = gravity_axis[ gravaxis ].z;
if ( deadflag )
{
return;
}
if ( flags & (FL_MUTANT|FL_SP_MUTANT|FL_ADRENALINE) )
{
return;
}
if ( getMoveType() == MOVETYPE_NOCLIP )
{
return;
}
if ( !fallsurface )
{
return;
}
surf = fallsurface;
fallsurface = NULL;
//### don't take falling damage from gaining velocity
delta = (oldvelocity[z] - velocity[z]) * gravity_axis[gravaxis].sign;
if(delta > 0)
return;
//###
delta = delta * delta * 0.0001;
// never take falling damage if completely underwater
switch( waterlevel )
{
case 1 :
delta *= 0.5;
break;
case 2 :
delta *= 0.25;
break;
case 3 :
return;
break;
}
if ( delta < 3 )
{
return;
}
fall_value = delta * 0.5;
if ( fall_value > 40 )
{
fall_value = 40;
}
fall_time = level.time + FALL_TIME;
if ( delta > 30 )
{
damage = ( delta - 30 ) / 2;
if ( surf )
{
// modify damage based on surface
switch ( surf->flags & MASK_SURF_TYPE )
{
// Can this happen?
case SURF_TYPE_WATER:
break;
// no damage
case SURF_TYPE_FABRIC:
damage *= 0;
break;
// low damage
case SURF_TYPE_FLESH:
case SURF_TYPE_VEGETATION:
damage *= 0.5;
break;
// medium damage
case SURF_TYPE_DIRT:
damage *= 0.6;
break;
case SURF_TYPE_WOOD:
damage *= 0.8;
break;
// full damage
case SURF_TYPE_GRAVEL:
case SURF_TYPE_GRILL:
case SURF_TYPE_METAL:
case SURF_TYPE_STONE:
case SURF_TYPE_CONCRETE:
case SURF_TYPE_DUCT:
default:
break;
}
}
if ( surf && surf->frequency != 0 )
{
damage = 0;
}
//### added check for being concussioned
if((damage >= 1) && (concussion_timer > level.time) && (concussioner))
{
Damage(concussioner, concussioner, ( int )damage, origin, vec_zero, vec_zero, 0, 0, MOD_FALLING, -1, -1, 1.0f );
}
// if ( ( damage >= 1 ) && ( !DM_FLAG( DF_NO_FALLING ) ) )
else if ( ( damage >= 1 ) && ( !DM_FLAG( DF_NO_FALLING ) ) )
//###
{
Damage( world, world, ( int )damage, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_FALLING, -1, -1, 1.0f );
}
}
if ( surf && surf->frequency != 0 )
{
fall_time = 0;
fall_value = 0;
}
}
EXPORT_FROM_DLL void Player::FinishMove
(
void
)
{
Vector t[ 3 ];
Vector forward;
Vector right;
Vector up;
Vector temp;
int movetype; //###
if ( ai_createnodes->value )
{
AddPathNodes();
}
//
// If the origin or velocity have changed since ClientThink(),
// update the pmove values. This will happen when the client
// is pushed by a bmodel or kicked by an explosion.
//
// If it wasn't updated here, the view position would lag a frame
// behind the body position when pushed -- "sinking into plats"
//
//###
movetype = GetMovePlayerMoveType();
if (
// ( GetMovePlayerMoveType() != PM_LOCKVIEW ) &&
// ( GetMovePlayerMoveType() != PM_FREEZE ) &&
( movetype != PM_LOCKVIEW ) &&
( movetype != PM_FREEZE ) &&
(movetype != PM_ATTACHVIEW) && // guided missile
(movetype != PM_MOVECAPTURED) // movement capturer
)
//###
{
client->ps.pmove.origin[ 0 ] = worldorigin.x * 8.0;
client->ps.pmove.origin[ 1 ] = worldorigin.y * 8.0;
client->ps.pmove.origin[ 2 ] = worldorigin.z * 8.0;
client->ps.pmove.velocity[ 0 ] = velocity.x * 8.0;
client->ps.pmove.velocity[ 1 ] = velocity.y * 8.0;
client->ps.pmove.velocity[ 2 ] = velocity.z * 8.0;
}
//### use hoverbike's angles if in one
if(hoverbike)
{
setAngles(hoverbike->angles);
AnglesToMat( v_angle.vec3(), orientation );
}
else
{
// ###
//
// set model angles from view angles so other things in
// the world can tell which direction you are looking
//
if ( waterlevel >= 3 )
{
angles[ PITCH ] = v_angle[ PITCH ];
}
else if ( v_angle[ PITCH ] > 180 )
{
angles[ PITCH ] = ( -360 + v_angle[ PITCH ] ) / 12;
}
else
{
angles[ PITCH ] = v_angle[ PITCH ] / 12;
}
angles[ YAW ] = v_angle[ YAW ];
angles[ ROLL ] = CalcRoll();
// Orient the angles to coincide with our special gravity vector
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
angles.AngleVectors( &t[0], &t[1], &t[2] );
forward[ grav.x ] = t[ 0 ][ 0 ];
forward[ grav.y ] = t[ 0 ][ 1 ] * grav.sign;
forward[ grav.z ] = t[ 0 ][ 2 ] * grav.sign;
right[ grav.x ] = t[ 1 ][ 0 ];
right[ grav.y ] = t[ 1 ][ 1 ] * grav.sign;
right[ grav.z ] = t[ 1 ][ 2 ] * grav.sign;
up[ grav.x ] = t[ 2 ][ 0 ];
up[ grav.y ] = t[ 2 ][ 1 ] * grav.sign;
up[ grav.z ] = t[ 2 ][ 2 ] * grav.sign;
VectorsToEulerAngles( forward.vec3(), right.vec3(), up.vec3(), angles.vec3() );
if ( !level.playerfrozen )
{
setAngles( angles );
AnglesToMat( v_angle.vec3(), orientation );
}
} //###
CalcBob();
// check if we're over the limit health-wise
if ( ( (int)health > (int)max_health ) && ( ( float )( ( int )level.time ) == level.time ) )
{
health -= 1;
}
// mutant mode
if ( ( flags & FL_SP_MUTANT ) && !( flags & FL_GODMODE ) && ( ( float )( ( int )level.time ) == level.time ) )
{
Damage( world, world, 1, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_MUTANT_DRAIN, -1, -1, 1.0f );
}
// Check for silencer
if ( !(flags & FL_SILENCER) && FindItem( "Silencer" ) )
{
flags |= FL_SILENCER;
}
// Check for O2
if ( !(flags & FL_OXYGEN) && FindItem( "ScubaGear" ) )
{
flags |= FL_OXYGEN;
}
// burn from lava, etc
WorldEffects();
//### never take falling or concussion damage while on a hoverbike
if(!hoverbike)
{
FallingDamage();
ConcussionDamage(); // for getting smacked by the concussion gun
}
// do an informergun check
if(deathmatch->value == DEATHMATCH_MFD)
{
if(client->resp.informer)
{
if(currentWeapon && !currentWeapon->isSubclassOf(InformerGun))
{
Item *gunitem;
Weapon *igun;
// hold our current weapon for switching back to later
lastWeapon = currentWeapon;
gunitem = FindItem("InformerGun");
if(gunitem)
{
igun = (Weapon *)gunitem;
ForceChangeWeapon(igun);
igun->NextAttack(0.7);
}
else
{
igun = giveWeapon("InformerGun");
ForceChangeWeapon(igun);
igun->NextAttack(0.7);
}
}
}
else
{
if(currentWeapon && currentWeapon->isSubclassOf(InformerGun))
{
// switch them back to their last weapon
newWeapon = lastWeapon;
takeWeapon("InformerGun");
currentWeapon->NextAttack(0.7);
}
}
}
//###
ChooseAnim();
// determine the view offsets
CalcViewOffset();
CalcGunOffset();
DamageFeedback();
CalcBlend();
oldvelocity = velocity;
}
EXPORT_FROM_DLL qboolean Player::CanMoveTo
(
Vector pos
)
{
Vector min;
trace_t trace;
Vector start;
Vector end;
Vector s;
s = Vector( 0, 0, 20 );
start = worldorigin + s;
end = pos + s;
trace = G_Trace( start, mins, maxs, end, this, MASK_SOLID, "Player::CanMoveTo" );
if ( trace.fraction == 1 )
{
return true;
}
return false;
}
EXPORT_FROM_DLL qboolean Player::ClearPathTo
(
Vector pos
)
{
Vector dir;
Vector midpos;
Vector end_trace;
Vector delta;
Vector start;
Vector min;
Vector max;
trace_t trace;
float dist;
float t;
min = Vector( -16, -16, 12 );
max = Vector( 16, 16, 40 );
start = worldorigin;
dir = pos - worldorigin;
delta = dir;
delta[ 2 ] = 0;
if ( delta.length() >= PATHMAP_CELLSIZE )
{
return false;
}
if ( !CanMoveTo( pos ) )
{
return false;
}
// only do a full test when we train a level
if ( !ai_createnodes->value )
{
midpos = start + dir * 0.5;
end_trace = midpos - Vector( 0, 0, 40 );
// check that the midpos is onground (down 28 units)
trace = G_Trace( midpos, min, max, end_trace, this, MASK_SOLID, "Player::ClearPathTo 1" );
if ( trace.fraction == 1 )
{
return false;
}
return true;
}
dist = dir.length();
if ( dist < 32 )
{
return true;
}
dir[ 2 ] = 0;
dist = dir.length();
dir.normalize();
min = Vector( -16, -16, 0 );
// check the entire move
midpos = start;
for( t = 0; t < dist; t += 8 )
{
midpos[ 0 ] = start[ 0 ] + t * dir[ 0 ];
midpos[ 1 ] = start[ 1 ] + t * dir[ 1 ];
midpos[ 2 ] += 18;
end_trace = midpos - Vector( 0, 0, 2048 );//140 );//128 );
trace = G_Trace( midpos, min, max, end_trace, this, MASK_SOLID, "Player::ClearPathTo 2" );
if ( ( trace.fraction == 1 ) || ( trace.startsolid ) )
{
return false;
}
midpos = trace.endpos;
}
// Check if we're close enough
delta = midpos - pos;
return ( delta.length() < 32 );
}
EXPORT_FROM_DLL void Player::AddPathNode
(
Event *ev
)
{
if ( ai_createnodes->value )
{
lastNode = new PathNode;
lastNode->Setup( worldorigin );
}
}
EXPORT_FROM_DLL void Player::AddPathNodes
(
void
)
{
StandardMovePath find;
if ( ai_createnodes->value && groundentity )
{
if ( !lastNode || !lastNode->CheckMove( worldorigin, mins, maxs ) ||
!ClearPathTo( lastNode->worldorigin ) )
{
gi.dprintf( "Can't see lastNode\n" );
lastNode = new PathNode;
lastNode->Setup( lastVisible );
// so that we don't just keep putting nodes in the same place
lastVisible = worldorigin;
}
nearestNode = PathManager.NearestNode( worldorigin );
if ( !nearestNode )
{
gi.dprintf( "Can't see NearestNode\n" );
lastNode = new PathNode;
lastNode->Setup( worldorigin );
lastVisible = worldorigin;
}
else if ( lastNode && ( !lastNode->CheckPath( nearestNode, mins, maxs )
|| !nearestNode->CheckPath( lastNode, mins, maxs ) ) )
{
gi.dprintf( "Nearest can't see last\n" );
lastNode = new PathNode;
lastNode->Setup( worldorigin );
lastVisible = worldorigin;
}
if ( !lastNode || ( lastNode->CheckMove( worldorigin, mins, maxs ) && ClearPathTo( lastNode->worldorigin ) ) )
{
lastVisible = worldorigin;
}
if ( nearestNode )
{
lastNode = nearestNode;
}
//gi.dprintf( "Num Nodes %d\n", PathManager.NumNodes() );
}
if ( searchTime <= level.time )
{
nearestNode = PathManager.NearestNode( worldorigin );
if ( path )
{
delete path;
path = NULL;
}
if ( ai_showpath->value )
{
if ( nearestNode )
{
if ( !goalNode )
{
goalNode = nearestNode;
}
find.heuristic.setSize( size );
path = find.FindPath( nearestNode, goalNode );
}
else if ( ai_debugpath->value )
{
gi.dprintf( "%d : No nearest node\n", level.framenum );
}
}
searchTime = level.time + 0.1;
if ( ai_showpath->value )
{
if ( path )
{
path->DrawPath( 0, 0.7, 0, 0.1 );
}
if ( nearestNode )
{
G_DebugLine( nearestNode->worldorigin + "0 0 16", worldorigin + "0 0 16", 0, 0, 0.7, 1 );
}
}
}
}
EXPORT_FROM_DLL void Player::UpdateStats
(
void
)
{
Armor *armor;
int i;
// Current Ammo
//### added for hoverbike
if(hoverbike)
{
// don't want either ammo number to show up
client->ps.stats[ STAT_AMMO ] = -1;
client->ps.stats[ STAT_CLIPAMMO ] = -1;
}
// if ( currentWeapon )
else if ( currentWeapon )
//###
{
client->ps.stats[ STAT_AMMO ] = currentWeapon->AmmoAvailable();
client->ps.stats[ STAT_CLIPAMMO ] = currentWeapon->ClipAmmo();
}
else
{
client->ps.stats[ STAT_AMMO ] = 0;
}
// All ammo types
//### these stat values are not being used for ammo numbers any more
#if 0
for( i = 0; i < NUM_AMMO_TYPES; i++ )
{
Ammo *ammo;
assert( ammo_types[ i ] );
ammo = ( Ammo * )FindItem( ammo_types[ i ] );
if ( ammo )
{
client->ps.stats[ STAT_AMMO_BASE + i ] = ammo->Amount();
}
else
{
client->ps.stats[ STAT_AMMO_BASE + i ] = 0;
}
}
#endif
//###
//
// Armor
//
client->ps.stats[ STAT_ARMOR ] = 0;
for( i = 0; i < NUM_ARMOR_TYPES; i++ )
{
assert( armor_types[ i ] );
armor = ( Armor * )FindItem( armor_types[ i ] );
if ( armor )
{
client->ps.stats[ STAT_ARMOR_BASE + i ] = armor->Amount();
client->ps.stats[ STAT_ARMOR ] += armor->Amount();
}
else
{
client->ps.stats[ STAT_ARMOR_BASE + i ] = 0;
}
}
// Average the armor into a single value
client->ps.stats[ STAT_ARMOR ] /= 3;
if ( currentWeapon )
{
client->ps.stats[ STAT_CURRENT_WEAPON ] = currentWeapon->GetIconIndex();
}
else
{
client->ps.stats[ STAT_CURRENT_WEAPON ] = 0;
}
//
// Weapon list
//
client->ps.stats[ STAT_WEAPONLIST ] = 0;
//
// Inventory
//
if ( currentItem && ( !( flags & ( FL_SP_MUTANT | FL_MUTANT ) ) ) )
{
InventoryItem *nextItem;
InventoryItem *prevItem;
nextItem = ( InventoryItem * )NextItem( currentItem );
prevItem = ( InventoryItem * )PrevItem( currentItem );
client->ps.stats[ STAT_SELECTED_ICON ] = currentItem->GetIconIndex();
client->ps.stats[ STAT_SELECTED_NAME ] = currentItem->GetItemIndex();
client->ps.stats[ STAT_SELECTED_AMOUNT ] = currentItem->Amount();
client->ps.stats[ STAT_SELECTED_MODELINDEX ] = currentItem->edict->s.modelindex;
if ( prevItem )
client->ps.stats[ STAT_PREVIOUS_ICON ] = prevItem->GetIconIndex();
else
client->ps.stats[ STAT_PREVIOUS_ICON ] = 0;
if ( nextItem )
client->ps.stats[ STAT_NEXT_ICON ] = nextItem->GetIconIndex();
else
client->ps.stats[ STAT_NEXT_ICON ] = 0;
}
else
{
client->ps.stats[ STAT_SELECTED_ICON ] = 0;
client->ps.stats[ STAT_PREVIOUS_ICON ] = 0;
client->ps.stats[ STAT_NEXT_ICON ] = 0;
client->ps.stats[ STAT_SELECTED_AMOUNT ] = 0;
}
//
// Health
//
if ( ( health < 1 ) && ( health > 0 ) )
client->ps.stats[ STAT_HEALTH ] = 1;
else
client->ps.stats[ STAT_HEALTH ] = health;
//
// Frags
//
client->ps.stats[ STAT_FRAGS ] = client->resp.score;
if ( spectator )
{
client->ps.stats[ STAT_LAYOUTS ] = DRAW_SPECTATOR;
}
else
{
if ( !hidestats )
client->ps.stats[ STAT_LAYOUTS ] = DRAW_STATS;
else
client->ps.stats[ STAT_LAYOUTS ] = 0;
}
//
// Overlays
//
if ( drawoverlay )
{
client->ps.stats[ STAT_LAYOUTS ] |= DRAW_OVERLAY;
}
//
// Show scores in dealthmatch during intermission or if the client wants to see them
//
if ( deathmatch->value && ( level.intermissiontime || client->showinfo ) )
{
client->ps.stats[ STAT_LAYOUTS ] |= DRAW_SCORES;
}
//
// Powerups timer
//
if ( poweruptimer > 0 )
{
client->ps.stats[ STAT_POWERUPTIMER ] = poweruptimer;
client->ps.stats[ STAT_POWERUPTYPE ] = poweruptype;
}
else
{
client->ps.stats[ STAT_POWERUPTIMER ] = 0;
client->ps.stats[ STAT_POWERUPTYPE ] = 0;
}
//### lap time stats
if(last_goal_time)
{
client->ps.stats[STAT_CURRENTLAP] = (int)((level.time - last_goal_time)*10);
}
else
{
client->ps.stats[STAT_CURRENTLAP] = 0;
}
if(client->resp.last_lap_time)
{
client->ps.stats[STAT_LASTLAP] = (int)(client->resp.last_lap_time*10);
}
else
{
client->ps.stats[STAT_LASTLAP] = 0;
}
if(level.cp_num)
{
client->ps.stats[STAT_CPCOUNT] = level.cp_num - cp_list.length();
}
else
{
client->ps.stats[STAT_CPCOUNT] = 0;
}
// set night vision stat value
if(nightvision)
{
client->ps.stats[STAT_NIGHTVISION] = 1;
}
else
{
client->ps.stats[STAT_NIGHTVISION] = 0;
}
//###
//
// Mission Computer
//
if ( client->showinfo && !deathmatch->value )
{
client->ps.stats[ STAT_LAYOUTS ] |= DRAW_MISSIONCPU;
}
//
// Clear out the exit sign
// if we are in the trigger_exit field, than this will constantly be set
// so this will clear it out on subsequent calls to UpdateStats
//
if ( client->ps.stats[ STAT_EXITSIGN ] > 0 )
{
client->ps.stats[ STAT_EXITSIGN ]--;
}
//
// if the scoreboard is up, update it
//
if ( client->showinfo && deathmatch->value && !( level.framenum & 31 ) )
{
G_DeathmatchScoreboardMessage( this, enemy );
gi.unicast( edict, false );
}
}
EXPORT_FROM_DLL void Player::UpdateMusic
(
void
)
{
if ( music_forced )
{
client->ps.current_music_mood = music_current_mood;
client->ps.fallback_music_mood = music_fallback_mood;
}
else if ( action_level > 30 )
{
music_current_mood = mood_normal;
music_fallback_mood = mood_normal;
client->ps.current_music_mood = mood_action;
client->ps.fallback_music_mood = mood_normal;
}
else if ( ( action_level < 15 ) && ( client->ps.current_music_mood == mood_action ) )
{
music_current_mood = mood_normal;
music_fallback_mood = mood_normal;
client->ps.current_music_mood = music_current_mood;
client->ps.fallback_music_mood = music_fallback_mood;
}
else if ( client->ps.current_music_mood != mood_action )
{
client->ps.current_music_mood = music_current_mood;
client->ps.fallback_music_mood = music_fallback_mood;
}
if (action_level > 0)
{
action_level -= 0.2f;
if (action_level > 80)
action_level = 80;
}
else
action_level = 0;
//
// set the music
// naturally decay the action level
//
if (s_debugmusic->value)
{
warning("DebugMusic","%s's action_level = %4.2f\n", client->pers.netname, action_level);
}
}
/*
=================
EndFrame
Called for each player at the end of the server frame
and right after spawning
=================
*/
EXPORT_FROM_DLL void Player::EndFrame
(
Event *ev
)
{
FinishMove();
UpdateStats();
UpdateMusic();
// set the crouch flag
if ( client->ps.pmove.pm_flags & PMF_DUCKED )
{
edict->s.effects |= EF_CROUCH;
}
else
{
edict->s.effects &= ~EF_CROUCH;
}
if ( movieCamera != CinematicCamera )
{
if ( movieCamera && movieCamera->isSubclassOf( SecurityCamera ) )
{
movieCamera->edict->s.renderfx &= ~RF_DONTDRAW;
}
movieCamera = CinematicCamera;
if ( movieCamera )
{
Entity * ent;
Camera * cam;
SetViewMode( CAMERA_VIEW, movieCamera );
if ( movieCamera->isSubclassOf( Camera ) )
{
ent = movieCamera;
cam = ( Camera * )ent;
if ( cam->Overlay().length() )
{
SendOverlay( this, cam->Overlay() );
drawoverlay = true;
hidestats = true;
}
}
}
else
{
drawoverlay = false;
hidestats = false;
SetViewMode( defaultViewMode );
}
}
if ( ( viewmode == THIRD_PERSON ) && ( zoom_mode == ZOOMED_IN ) )
{
SetCameraEntity( this );
if ( crosshair )
{
crosshair->hideModel();
}
}
else if ( ( viewmode == THIRD_PERSON ) && ( gravaxis ) )
{
SetCameraEntity( this );
if ( crosshair )
{
crosshair->hideModel();
}
}
else
{
if ( crosshair )
{
crosshair->showModel();
}
if ( !watchCamera )
{
gi.dprintf("No watchCamera value in Player::EndFrame, setting it to the player\n");
// fix it in release
watchCamera = this;
}
SetCameraEntity( watchCamera );
}
}
EXPORT_FROM_DLL void Player::ShowInfo
(
Event *ev
)
{
client->showinfo = !client->showinfo;
if ( deathmatch->value && client->showinfo )
{
G_DeathmatchScoreboard( this );
return;
}
}
void Player::ReadyToFire
(
Event *ev
)
{
int frame;
frame = ev->GetInteger( 1 );
if ( ( buttons & BUTTON_ATTACK ) && WeaponReady() )
{
NextFrame( frame );
}
}
void Player::WaitingToFire
(
Event *ev
)
{
int frame;
frame = ev->GetInteger( 1 );
if ( ( buttons & BUTTON_ATTACK ) && !WeaponReady() )
{
NextFrame( frame );
}
}
void Player::DoneFiring
(
Event *ev
)
{
firing = false;
}
void Player::AddItemToFloatingInventory
(
Item *item
)
{
floating_inventory.AddObject( item->entnum );
}
void Player::SendFloatingInventory
(
void
)
{
int i,n;
// Send over the floating inventory icons
n = floating_inventory.NumObjects();
assert( n < MAX_ITEMS );
gi.WriteByte( svc_inventory );
gi.WriteShort( n );
for( i=1; i<=n; i++)
{
Item *item;
item = ( Item * )G_GetEntity( floating_inventory.ObjectAt( i ) );
assert( item );
if ( item )
{
gi.WriteShort( item->GetIconIndex() );
gi.WriteShort( item->Amount() );
}
}
gi.unicast( this->edict, false );
}
void Player::ClearFloatingInventory
(
Event *ev
)
{
// Clear the items
floating_inventory.ClearObjectList();
// Clear the owner;
floating_owner = NULL;
// Clear the client's list
SendFloatingInventory();
}
void Player::SetFloatingOwner
(
Sentient *deceased_owner
)
{
floating_owner = deceased_owner;
}
Sentient *Player::GetFloatingOwner
(
void
)
{
return floating_owner;
}
void Player::PickupFloatingInventory
(
void
)
{
int i,n;
Event *event;
qboolean inventory_changed = false;
// Make sure there is an inventory to pickup
if (!floating_owner)
return;
n = floating_inventory.NumObjects();
if (!n)
return;
assert( n < MAX_ITEMS );
for( i=n; i>0; i--)
{
Item *item;
item = ( Item * )G_GetEntity( floating_inventory.ObjectAt( i ) );
assert( item );
// Check to see if we want to pick this up
if ( !item->Pickupable( this ) )
continue;
// Remove it from the dead body
item->Drop();
// Add item to player's inventory
event = new Event( EV_Item_Pickup );
event->AddEntity( this );
item->ProcessEvent( event );
item->ProcessEvent( EV_Trigger_StartThread );
// Remove it from the floating inventory
floating_inventory.RemoveObjectAt( i );
inventory_changed = true;
}
if ( inventory_changed )
{
// Update the client if the inventory changed
SendFloatingInventory();
}
else
{
// Make this guy useless in 60 seconds
Event *re;
re = new Event( "remove_useless" );
floating_owner->PostEvent( re, 60 );
RandomSound("snd_refusepickup");
}
if ( !floating_inventory.NumObjects() )
{
Event *re;
re = new Event( "remove_useless" );
floating_owner->ProcessEvent( re );
}
}
void Player::ChangeSpectator
(
void
)
{
int num;
edict_t *cl_ent;
currentCameraTarget = ( currentCameraTarget + 1 ) % game.maxclients;
cl_ent = g_edicts + 1 + currentCameraTarget;
num = 0;
while
(
!cl_ent->inuse ||
!cl_ent->entity ||
( !cl_ent->entity->client ) ||
( ( ( Player * )cl_ent->entity )->spectator )
)
{
if ( num == game.maxclients )
break;
currentCameraTarget = ( currentCameraTarget + 1 ) % game.maxclients;
cl_ent = g_edicts + 1 + currentCameraTarget;
num++;
}
if ( !cl_ent->inuse )
{
SetViewMode( FIRST_PERSON );
}
else
{
SetViewMode( SPECTATOR );
}
}
void Player::TestThread
(
Event *ev
)
{
const char *scriptfile;
const char *label = NULL;
ScriptThread * thread;
if ( ev->NumArgs() < 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Syntax: testthread scriptfile <label>.\n" );
return;
}
scriptfile = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
label = ev->GetString( 2 );
thread = Director.CreateThread( scriptfile, LEVEL_SCRIPT, label );
if ( thread )
{
// start right away
thread->Start( -1 );
}
}
void Player::GibEvent
(
Event *ev
)
{
qboolean hidemodel;
hidemodel = !ev->GetInteger( 1 );
if ( sv_gibs->value && !parentmode->value )
{
if ( hidemodel )
{
gibbed = true;
takedamage = DAMAGE_NO;
setSolidType( SOLID_NOT );
hideModel();
}
CreateGibs( this, health, 0.75f, 3 );
}
}
void Player::GotKill
(
Event *ev
)
{
Entity *victim;
Entity *inflictor;
str location;
float damage;
int meansofdeath;
qboolean gibbed;
if ( deathmatch->value )
{
return;
}
victim = ev->GetEntity( 1 );
damage = ev->GetInteger( 2 );
inflictor = ev->GetEntity( 3 );
location = ev->GetString( 4 );
meansofdeath = ev->GetInteger( 5 );
gibbed = ev->GetInteger( 6 );
if ( victim->isSubclassOf( Actor ) )
{
Actor * act;
act = ( Actor * )victim;
switch ( act->actortype )
{
case IS_ENEMY:
case IS_MONSTER:
if ( gibbed )
{
if ( G_Random( 100 ) < 15 )
{
RandomSound( "snd_gibfest", 1, CHAN_VOICE );
}
}
else if ( G_Random( 100 ) < 15 )
{
RandomSound( "snd_taunt", 1, CHAN_VOICE );
}
break;
case IS_FRIEND:
case IS_CIVILIAN:
if (
( !( flags & FL_MUTANT ) ) &&
( !deathmatch->value ) &&
( !level.no_jc )
)
{
char name[ 128 ];
int num;
num = (int)G_Random( 4 ) + 1;
sprintf( name, "global/universal_script.scr::blade_kills_innocent%d", num );
ExecuteThread( name, true );
}
break;
}
}
if ( victim->isClient() )
{
if ( gibbed )
{
if ( G_Random( 100 ) < 15 )
{
RandomSound( "snd_gibfest", 1, CHAN_VOICE );
}
}
else if (
( G_Random( 100 ) < 15 )
)
{
RandomSound( "snd_taunt", 1, CHAN_VOICE );
}
}
}
void Player::SetPowerupTimer
(
Event *ev
)
{
Event *event;
poweruptimer = ev->GetInteger( 1 );
poweruptype = ev->GetInteger( 2 );
event = new Event( EV_Player_UpdatePowerupTimer );
PostEvent ( event, 1 );
}
void Player::UpdatePowerupTimer
(
Event *ev
)
{
poweruptimer -= 1;
if (poweruptimer > 0)
PostEvent( ev, 1 );
else
poweruptype = 0;
}
viewmode_t Player::ViewMode
(
void
)
{
return viewmode;
}
Camera * Player::CurrentCamera
(
void
)
{
Entity * ent;
ent = CinematicCamera;
return ( Camera * )ent;
}
void Player::SetCamera
(
Entity *ent
)
{
CinematicCamera = ent;
}
void Player::WhatIs
(
Event *ev
)
{
int num;
Entity *ent;
if ( ev->NumArgs() != 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: whatis <entity number>\n");
return;
}
num = ev->GetInteger( 1 );
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
{
gi.cprintf( edict, PRINT_HIGH, "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
return;
}
ent = G_GetEntity( num );
if ( !ent )
{
gi.cprintf( edict, PRINT_HIGH, "Entity not in use.\n" );
}
else
{
const char * animname;
int master, own;
animname = NULL;
if ( gi.IsModel( ent->edict->s.modelindex ) )
animname = gi.Anim_NameForNum( ent->edict->s.modelindex, ent->edict->s.anim );
if ( !animname )
animname = "( N/A )";
if ( ent->bindmaster )
master = ent->bindmaster->entnum;
else
master = 0;
if ( ent->edict->owner )
own = ent->edict->owner - g_edicts;
else
own = 0;
gi.cprintf( edict, PRINT_HIGH,
"Entity # : %d\n"
"Class ID : %s\n"
"Classname : %s\n"
"Targetname : %s\n"
"Modelname : %s\n"
"Animname : %s\n"
"Origin : ( %f, %f, %f )\n"
"Bounds : Mins( %.2f, %.2f, %.2f ) Maxs( %.2f, %.2f, %.2f )\n"
"Velocity : ( %f, %f, %f )\n"
"SVFLAGS : %x\n"
"Movetype : %i\n"
"Solidtype : %i\n"
"Parent : %i\n"
"Health : %.1f\n"
"Max Health : %.1f\n"
"BindMaster : %i\n"
"Edict Owner: %i\n",
num,
ent->getClassID(),
ent->getClassname(),
ent->TargetName(),
ent->model.c_str(),
animname,
ent->worldorigin.x, ent->worldorigin.y, ent->worldorigin.z,
ent->mins.x, ent->mins.y, ent->mins.z, ent->maxs.x, ent->maxs.y, ent->maxs.z,
ent->velocity.x, ent->velocity.y, ent->velocity.z,
ent->edict->svflags,
ent->movetype,
ent->edict->solid,
ent->edict->s.parent,
ent->health,
ent->max_health,
master,
own
);
}
}
void Player::KillEnt
(
Event * ev
)
{
int num;
Entity *ent;
if ( ev->NumArgs() != 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: killent <entity number>\n");
return;
}
num = ev->GetInteger( 1 );
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
{
gi.cprintf( edict, PRINT_HIGH, "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
return;
}
ent = G_GetEntity( num );
ent->Damage( world, world, ent->max_health + 25, worldorigin, vec_zero, vec_zero, 0, 0, 0, -1, -1, 1 );
}
void Player::RemoveEnt
(
Event * ev
)
{
int num;
Entity *ent;
if ( ev->NumArgs() != 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: removeent <entity number>\n");
return;
}
num = ev->GetInteger( 1 );
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
{
gi.cprintf( edict, PRINT_HIGH, "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
return;
}
ent = G_GetEntity( num );
ent->PostEvent( Event( EV_Remove ), 0 );
}
void Player::KillClass
(
Event * ev
)
{
int except;
str classname;
edict_t * from;
Entity *ent;
if ( ev->NumArgs() < 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: killclass <classname> [except entity number]\n");
return;
}
classname = ev->GetString( 1 );
except = 0;
if ( ev->NumArgs() == 2 )
{
except = ev->GetInteger( 1 );
}
for ( from = this->edict + 1; from < &g_edicts[ globals.num_edicts ]; from++ )
{
if ( !from->inuse )
{
continue;
}
assert( from->entity );
ent = from->entity;
if ( ent->entnum == except )
{
continue;
}
if ( ent->inheritsFrom( classname.c_str() ) )
{
ent->Damage( world, world, ent->max_health + 25, worldorigin, vec_zero, vec_zero, 0, 0, 0, -1, -1, 1 );
}
}
}
void Player::RemoveClass
(
Event * ev
)
{
int except;
str classname;
edict_t * from;
Entity *ent;
if ( ev->NumArgs() < 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: removeclass <classname> [except entity number]\n");
return;
}
classname = ev->GetString( 1 );
except = 0;
if ( ev->NumArgs() == 2 )
{
except = ev->GetInteger( 1 );
}
for ( from = this->edict + 1; from < &g_edicts[ globals.num_edicts ]; from++ )
{
if ( !from->inuse )
{
continue;
}
assert( from->entity );
ent = from->entity;
if ( ent->entnum == except )
continue;
if ( ent->inheritsFrom( classname.c_str() ) )
{
ent->PostEvent( Event( EV_Remove ), 0 );
}
}
}
void Player::ActorInfo
(
Event *ev
)
{
int num;
Entity *ent;
if ( ev->NumArgs() != 1 )
{
gi.cprintf( edict, PRINT_HIGH, "Usage: actorinfo <entity number>\n");
return;
}
num = ev->GetInteger( 1 );
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
{
gi.cprintf( edict, PRINT_HIGH, "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
return;
}
ent = G_GetEntity( num );
if ( !ent || !ent->isSubclassOf( Actor ) )
{
gi.cprintf( edict, PRINT_HIGH, "Entity not an Actor.\n" );
}
else
{
( ( Actor * )ent )->ShowInfo();
}
}
void Player::Taunt
(
Event *ev
)
{
Event * event;
str taunt;
if ( level.time < lastTauntTime )
return;
taunt = str("snd_taunt") + str( ev->GetString( 1 ) );
event = new Event( EV_VoiceSound );
event->AddString( taunt );
event->AddFloat( 1 );
ProcessEvent( event );
lastTauntTime = level.time + TAUNT_TIME;
}
void Player::DrawOverlay
(
Event *ev
)
{
drawoverlay = true;
}
void Player::HideOverlay
(
Event *ev
)
{
drawoverlay = false;
}
void Player::DrawStats
(
Event *ev
)
{
hidestats = false;
}
void Player::HideStats
(
Event *ev
)
{
hidestats = true;
}
void Player::ChangeMusic
(
const char * current,
const char * fallback,
qboolean force
)
{
int current_mood_num;
int fallback_mood_num;
music_forced = force;
if ( str( current ) == str( "normal" ) )
{
music_forced = false;
}
if ( current )
{
current_mood_num = MusicMood_NameToNum( current );
if ( current_mood_num < 0 )
{
gi.dprintf( "current music mood %s not found", current );
}
else
{
music_current_mood = current_mood_num;
}
}
if ( fallback )
{
fallback_mood_num = MusicMood_NameToNum( fallback );
if ( fallback_mood_num < 0 )
{
gi.dprintf( "fallback music mood %s not found", fallback );
fallback = NULL;
}
else
{
music_fallback_mood = fallback_mood_num;
}
}
}
void Player::SetFlashColor
(
Event *ev
)
{
if ( flash_color[3] == 0 )
{
flash_color[0] = flash_color[1] = flash_color[2] = 0;
}
flash_color[0] += ev->GetFloat( 1 );
flash_color[1] += ev->GetFloat( 2 );
flash_color[2] += ev->GetFloat( 3 );
flash_color[3] += ev->GetFloat( 4 );
if ( flash_color[0] > 1 )
flash_color[0] = 1;
if ( flash_color[1] > 1 )
flash_color[1] = 1;
if ( flash_color[2] > 1 )
flash_color[2] = 1;
if ( flash_color[3] > 1 )
flash_color[3] = 1;
}
void Player::ClearFlashColor
(
Event *ev
)
{
flash_color[0] = 0;
flash_color[1] = 0;
flash_color[2] = 0;
flash_color[3] = 0;
}
void Player::GiveOxygen
(
float time
)
{
air_finished = level.time + time;
}
void Player::WeaponUse
(
Event *ev
)
{
Event *event;
if ( currentWeapon && !currentWeapon->ChangingWeapons() )
{
event = new Event( EV_Weapon_SecondaryUse );
event->AddEntity( this );
currentWeapon->ProcessEvent( event );
return;
}
}
void Player::Human
(
Event *ev
)
{
int playernum;
flags &= ~FL_SP_MUTANT;
flags &= ~FL_MUTANT;
setModel( savemodel.c_str() );
SetAnim( "idle" );
strcpy( client->pers.model, savemodel.c_str() );
strcpy( client->pers.skin, saveskin.c_str() );
playernum = edict-g_edicts-1;
// combine name, skin and model into a configstring
gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s\\%s",
client->pers.netname,
client->pers.model,
client->pers.skin));
takeWeapon( "MutantHands" );
}
void Player::Mutate
(
Event *ev
)
{
int playernum;
Weapon *mutanthands;
flags |= FL_SP_MUTANT;
setModel( "manumit_pl.def" );
SetAnim( "idle" );
savemodel = client->pers.model;
saveskin = client->pers.skin;
strcpy( client->pers.model, "manumit_pl.def" );
playernum = edict-g_edicts-1;
// combine name, skin and model into a configstring
gi.configstring (CS_PLAYERSKINS+playernum,
va("%s\\%s\\%s",
client->pers.netname,
client->pers.model,
client->pers.skin)
);
// Give the mutanthands weapon. - Force it.
mutanthands = giveWeapon( "MutantHands" );
ForceChangeWeapon( mutanthands );
}
void Player::SetSkin
(
Event *ev
)
{
int playernum;
playernum = edict-g_edicts-1;
strcpy( client->pers.skin, ev->GetString( 1 ) );
// combine name, skin and model into a configstring
gi.configstring( CS_PLAYERSKINS+playernum, va( "%s\\%s\\%s",
client->pers.netname,
client->pers.model,
client->pers.skin ) );
}
//### ===================================================================
// 2015 stuff added to the Player class
//
// 2015 player init stuff
void Player::Init2015(void)
{
Goggles *goggles;
Flashlight *flashlight;
// add warm flag to player
edict->s.effects |= EF_WARM;
// init nuke view flash
nuke_blend = vec_zero;
nuke_alpha = 0;
// init rope stuff
rope_grabbed = NULL;
ropesound = true;
// init light offset (for flamethrower death)
edict->s.renderfx &= ~RF_LIGHTOFFSET;
edict->s.lightofs = 0;
// make sure missile overlay is off
MissileOverlayOff();
// reset lap stuff
cp_list = "";
if(level.cp_anyorder) // any order goes
{
// players start on a new lap immediatly
last_cp_id = -1;
last_goal_time = level.time;
}
else // checkpoints must be hit in order
{
last_cp_id = 0;
last_goal_time = 0;
}
// just incase player finished last level on a bike
takeWeapon("HoverWeap");
// init view jitter values
jitter_angle = 0;
jitter_angle_falloff = 0;
jitter_angle_enttime = 0;
jitter_offset = 0;
jitter_offset_falloff = 0;
jitter_offset_enttime = 0;
// make sure flashlight is in proper state
if(DM_FLAG(DF_FLASHLIGHT))
{
// make sure player has a flashlight
flashlight = (Flashlight *)FindItem("Flashlight");
if(!flashlight)
{
flashlight = (Flashlight *)giveItem("Flashlight", 1);
}
if(DM_FLAG(DF_FLASHLIGHTON))
{
// turn the flashlight on
if(flashlight->lighton <= 0)
flashlight->Use(NULL);
}
}
else
{
flashlight = (Flashlight *)FindItem("Flashlight");
if(flashlight && (flashlight->lighton > 0))
{
// turn flashlight off
flashlight->Use(NULL);
}
}
// make sure goggles are in proper state
if(DM_FLAG(DF_GOGGLES))
{
// make sure the player has the goggles
goggles = (Goggles *)FindItem("Goggles");
if(!goggles)
goggles = (Goggles *)giveItem("Goggles", 1);
if(DM_FLAG(DF_GOGGLESON))
{
// turn the goggles on
goggles->goggleson = true;
nightvision = true;
}
}
else
{
goggles = (Goggles *)FindItem("Goggles");
if(goggles)
goggles->goggleson = false;
nightvision = false;
}
}
/*
=================
P_ConcussionDamage
=================
*/
void Player::ConcussionDamage(void)
{
float xyspeed, oldxyspeed, delta;
int damage;
if(deadflag)
{
return;
}
if(getMoveType() == MOVETYPE_NOCLIP)
{
return;
}
xyspeed = sqrt(velocity[0]*velocity[0] + velocity[1]*velocity[1]);
oldxyspeed = sqrt(oldvelocity[0]*oldvelocity[0] + oldvelocity[1]*oldvelocity[1]);
delta = xyspeed - oldxyspeed;
delta = delta * delta * 0.0001;
if(delta > 80)
{
//pain_debounce_time = level.time; // no normal pain sound
damage = (delta - 75) / 3;
if ( damage < 1 )
{
damage = 1;
}
//dir = Vector( 0, 0, 1 );
if(concussion_timer > level.time && concussioner)
{
Damage(concussioner, concussioner, damage, origin, vec_zero, vec_zero, 0, 0, MOD_FALLING, -1, -1, 1.0f);
}
else if(!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING))
{
Damage(world, world, damage, origin, vec_zero, vec_zero, 0, 0, MOD_FALLING, -1, -1, 1.0f);
}
}
}
// hoverbike spawn cheat
void Player::GiveBikeCheat (Event *ev)
{
SpawnPlayerBike(0);
}
void Player::SpawnPlayerBike(int forcespawn)
{
trace_t trace;
Vector tmpvec, forward;
Hoverbike *bike;
Event *e;
if(deadflag)
{
return;
}
// already got a hoverbike
if(hoverbike)
{
return;
}
// don't do space checks if forcing it to spawn
if(!forcespawn)
{
// make sure there's room to spawn a bike
tmpvec = Vector(0, angles[YAW], 0);
tmpvec.AngleVectors(&forward, NULL, NULL);
trace = G_Trace(origin, Vector(-20, -20, 0), Vector(20, 20, 64), origin, this, MASK_PLAYERSOLID, "Player::GiveBikeCheat");
if(trace.allsolid)
{
gi.cprintf(edict, PRINT_HIGH, "Not enough room to spawn a hoverbike here\n");
return;
}
tmpvec = origin + forward*40;
trace = G_Trace(tmpvec, Vector(-20, -20, 0), Vector(20, 20, 64), tmpvec, this, MASK_PLAYERSOLID, "Player::GiveBikeCheat");
if(trace.allsolid)
{
gi.cprintf(edict, PRINT_HIGH, "Not enough room to spawn a hoverbike here\n");
return;
}
tmpvec = origin - forward*40;
trace = G_Trace(tmpvec, Vector(-20, -20, 0), Vector(20, 20, 64), tmpvec, this, MASK_PLAYERSOLID, "Player::GiveBikeCheat");
if(trace.allsolid)
{
gi.cprintf(edict, PRINT_HIGH, "Not enough room to spawn a hoverbike here\n");
return;
}
}
// setup the bike to get on
bike = new Hoverbike;
bike->setAngles(angles);
tmpvec = origin + Vector(0, 0, 24);
bike->setOrigin(tmpvec);
bike->respawntimer = 1;
// put the player on the bike
e = new Event(EV_Use);
e->AddEntity(this);
bike->PostEvent(e, 0.1);
}
void Player::SetAngleJitter(float angle, float falloff, float time)
{
if(jitter_angle > angle)
return;
jitter_angle = angle;
jitter_angle_falloff = falloff;
jitter_angle_enttime = time;
}
void Player::SetOffsetJitter(float offset, float falloff, float time)
{
if(jitter_offset > offset)
return;
jitter_offset = offset;
jitter_offset_falloff = falloff;
jitter_offset_enttime = time;
}
void Player::AngleJitterEvent(Event *ev)
{
gi.dprintf("Player::AngleJitterEvent\n");
SetAngleJitter(ev->GetFloat(1), ev->GetFloat(2), ev->GetFloat(3));
}
void Player::OffsetJitterEvent(Event *ev)
{
gi.dprintf("Player::OffsetJitterEvent\n");
SetOffsetJitter(ev->GetFloat(1), ev->GetFloat(2), ev->GetFloat(3));
}
qboolean Player::CPTouched(int cp)
{
int i;
for(i = 0; i < cp_list.length(); i++)
{
if(cp_list[i] == cp)
return true;
}
return false;
}
qboolean Player::CPTouchedAll(void)
{
return (cp_list.length() == level.cp_num);
}
void Player::CPListAdd(int cp)
{
if(!CPTouched(cp))
{
int i;
cp_list += "_"; // lengthen the list by one
i = cp_list.length() - 1;
cp_list[i] = cp;
}
}
void Player::CPListClear(void)
{
cp_list = "";
}
void Player::SetBikeSkin (Event *ev)
{
int playernum;
playernum = edict-g_edicts-1;
strcpy(client->pers.bikeskin, ev->GetString(1));
// combine name, skin and model into a configstring
gi.configstring(CS_BIKESKINS+playernum, va("%s\\%s\\%s",
client->pers.bikemodel,
client->pers.bikeskin));
}
void Player::SetInformer(Event *ev)
{
int i;
edict_t *ent;
char userinfo[MAX_INFO_STRING];
if(deathmatch->value != DEATHMATCH_MFD && deathmatch->value != DEATHMATCH_MOB)
return;
if(client->resp.informer)
{
gi.cprintf(edict, PRINT_MEDIUM, "You're already the informer.\n");
return;
}
//undo the current informer
for(i = 1; i <= maxclients->value; i++)
{
if(!g_edicts[i].inuse)
{
continue;
}
ent = &g_edicts[i];
if(!ent->client->resp.informer)
continue;
// don't allow someone to take the informerness if it's locked
if((int)dmflags->value & DF_INFORMER_LOCK)
{
gi.cprintf(edict, PRINT_MEDIUM, "Someone is already the informer.\n");
return;
}
ent->client->resp.informer = false;
// correct the player's model and skin
memcpy(userinfo, ent->client->pers.userinfo, sizeof(userinfo));
G_ClientUserinfoChanged(ent, userinfo);
break;
}
// make this player into the informer
client->resp.informer = true;
// correct the player's model and skin
memcpy(userinfo, client->pers.userinfo, sizeof(userinfo));
G_ClientUserinfoChanged(edict, userinfo);
// broadcast a messages about who the new informer is
for(i = 1; i <= maxclients->value; i++)
{
if(!g_edicts[i].inuse)
{
continue;
}
ent = &g_edicts[i];
if(edict == ent)
gi.centerprintf(ent, "jcx yv -80 boxtext \"You are the Informer!\"");
else
gi.centerprintf(ent, "jcx yv -80 string \"%s is the Informer!\"", client->pers.netname);
}
}
void Player::SetMovementCapture(MoveCapture *capturer)
{
movecapturer = capturer;
}
void Player::ToggleGoggles(Event *ev)
{
int gogglestate;
Goggles *goggles;
gogglestate = ev->GetInteger(1);
if(gogglestate)
nightvision = true;
else
nightvision = false;
goggles = (Goggles *)FindItem("Goggles");
if(goggles)
{
if(gogglestate)
goggles->goggleson = true;
else
goggles->goggleson = false;
}
}
// turns on the guided missile view overlay
void Player::MissileOverlayOn(void)
{
SendOverlay(this, "missile_cam01");
drawoverlay = true;
hidestats = true;
}
// turns off the guided missile view overlay
void Player::MissileOverlayOff(void)
{
drawoverlay = false;
hidestats = false;
}
void Player::WeaponSwitch (Event *ev)
{
if(ev->NumArgs() < 1)
{
if(autoweaponswitch)
gi.cprintf( edict, PRINT_HIGH, "Weaponswitching is On\n");
else
gi.cprintf( edict, PRINT_HIGH, "Weaponswitching is Off\n");
return;
}
autoweaponswitch = ev->GetFloat(1);
}
void Player::WeaponOverride (Event *ev)
{
if(ev->NumArgs() < 1)
{
if(weaponoverride)
gi.cprintf( edict, PRINT_HIGH, "Weapon select override is On\n");
else
gi.cprintf( edict, PRINT_HIGH, "Weapon select override is Off\n");
return;
}
weaponoverride = ev->GetFloat(1);
}
#include <strstrea.h>
void Player::WelcomeMessage(Event *ev)
{
// initial welcome message for MFD & Lynch Mob
if(!ev->NumArgs() && ((deathmatch->value == DEATHMATCH_MFD) || (deathmatch->value == DEATHMATCH_MOB)))
{
Event *event;
if(deathmatch->value == DEATHMATCH_MFD)
{
if(client->resp.informer)
gi.centerprintf(edict, "jcx yv -80 boxtext \"Welcome to Marked for Death\nYou are the Informer!\"");
else
gi.centerprintf(edict, "jcx yv -80 string \"Welcome to Marked for Death\" jcx yv -90 string \"Mobster %s\"", client->pers.netname);
}
else
{
if(client->resp.informer)
gi.centerprintf(edict, "jcx yv -80 boxtext \"Welcome to Lynch Mob\nYou are the Informer!\"");
else
gi.centerprintf(edict, "jcx yv -80 string \"Welcome to Lynch Mob\" jcx yv -90 string \"Mobster %s\"", client->pers.netname);
}
// post event for displaying the server's welcome message
event = new Event(EV_Player_WelcomeMessage);
event->AddToken(" "); // just needed as a place holder
PostEvent(event, 2.5);
}
else
{
size_t length;
char *buffer;
str outstring;
qboolean formatted;
// first check for a welcome message with formatting
length = gi.LoadFile("welcome.mnu", (void **)&buffer, 0);
if(length == (size_t)(-1))
{
// couldn't find a formatted one, so look for a plain test one
length = gi.LoadFile("welcome.txt", (void **)&buffer, 0);
if(length == (size_t)(-1))
{
// still couldn't find it, so forget it.
return;
}
formatted = false;
}
else
{
// got a welcome message with formatting
formatted = true;
}
// put the data buffer into an easy to use class
istrstream Data(buffer, (int)length);
if(formatted)
{
gi.centerprintf(edict, buffer);
}
else
{
gi.centerprintf(edict, "jcx jcy string \"%s\"", buffer);
}
// free up the buffer data
gi.TagFree((void *)buffer);
// we want to itterate the message two times to give mor etime to read it
if(ev->NumArgs() < 2)
{
Event *event;
event = new Event(EV_Player_WelcomeMessage);
event->AddToken(" ");
event->AddToken(" ");
PostEvent(event, 1);
}
}
}