//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/player.cpp                       $
// $Revision:: 422                                                            $
//   $Author:: Jimdose                                                        $
//     $Date:: 2/16/99 8:38p                                                  $
//
// 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.
//
// $Log:: /Quake 2 Engine/Sin/code/game/player.cpp                            $
// 
// 422   2/16/99 8:38p Jimdose
// moved sin arcade comm stuff to client
// 
// 421   1/29/99 6:14p Jimdose
// added 2015 changes
// 
// 420   1/27/99 5:15p Jimdose
// Fixed no weapon bug in arcade mode deathmatch
// 
// 419   12/18/98 11:03p Jimdose
// removed include of qcommon.h
// 
// 418   12/16/98 5:42p Jimdose
// fixed a bunch of respawn issues for Sin Arcade
// 
// 417   12/16/98 12:41a Jimdose
// Killed no longer deletes player's weapon in training mode 2 (arcade)
// 
// 416   12/14/98 8:17p Aldie
// Took out #if 0
// 
// 415   12/14/98 8:07p Aldie
// Temporarily remove arcade stuff
// 
// 414   12/14/98 6:53p Aldie
// Added some stuff for arcade when death occurs
// 
// 413   12/14/98 6:03p Jimdose
// made GodCheat allow a value to set it on of off
// 
// 412   11/18/98 3:47a Aldie
// Fix for mutants and inventory items showing up in inventory
// 
// 411   11/18/98 3:02a Jimdose
// made mutant mode stay across levels
// 
// 410   11/17/98 6:54p Aldie
// Don't do spawn particles for training
// 
// 409   11/17/98 3:48a Jimdose
// gravpaths no longer pull you during waterjumps
// 
// 408   11/16/98 9:05p Markd
// don't do flash blend if hit by MOD_FISTS
// 
// 407   11/16/98 8:29p Jimdose
// added CheckWater
// 
// 406   11/16/98 7:45p Markd
// made take, only take a certain amount
// 
// 405   11/16/98 4:37a Markd
// don't set angles if player is frozen
// 
// 404   11/16/98 12:42a Aldie
// spidermine camera
// 
// 403   11/15/98 8:43p Markd
// Fixed third person crosshair issues and gravity axis and zooming
// 
// 402   11/15/98 3:55a Markd
// fixed weapon visible in vehicles
// 
// 401   11/14/98 7:57p Markd
// always reload when pressing use
// 
// 400   11/14/98 2:52a Markd
// put in RF_DONTDRAW support on weapons
// 
// 399   11/14/98 2:54a Aldie
// Added InitSkin to preserve custom skins in single player
// 
// 398   11/13/98 11:02p Jimdose
// made fov persistant across levels and after zooming in and out
// 
// 397   11/13/98 10:42p Aldie
// Free inventory of type when a take item is issued by the script
// 
// 396   11/13/98 7:13p Jimdose
// health is now set to 1 if it's less than 1 when changing or entering levels
// 
// 395   11/13/98 2:37a Aldie
// Fixed pain event to play sound and flash within bounds
// 
// 394   11/11/98 11:12p Aldie
// Added custom gibbing
// 
// 393   11/10/98 3:57p Jimdose
// fixed bug where player enters vehicle crouched and the view hieght is too
// low
// 
// 392   11/09/98 3:31a Markd
// PM_trace should check for godmode
// 
// 391   11/09/98 1:43a Markd
// put in falling protection for landing on ladder
// 
// 390   11/09/98 12:55a Jimdose
// added sv_footsteps cvar so that server admins can turn footsteps off
// completely
// 
// 389   11/08/98 10:48p Jimdose
// moved earthquake to level struct
// 
// 388   11/08/98 8:29p Aldie
// Fix for some console bugs when moving while using them
// 
// 387   11/07/98 10:46p Markd
// Fixed music stuff
// 
// 386   11/07/98 10:15p Markd
// put in forcemusic support
// 
// 385   11/07/98 10:00p Jimdose
// Made spawn and actor commands work with model names
// 
// 384   11/07/98 8:01p Markd
// Added in damage_since_pain, fixed some camera stuff
// 
// 383   11/07/98 5:29p Aldie
// Drop inventory in coop
// 
// 382   11/07/98 5:27p Aldie
// Fixed coop death bug
// 
// 381   11/07/98 4:39p Markd
// fixed music stuff
// 
// 380   11/07/98 3:34p Markd
// reset music when coming back from action
// 
// 379   11/06/98 10:09p Aldie
// Moved has_thought to constructor
// 
// 378   11/06/98 9:38p Aldie
// Moved waitforplayer stuff to the think function
// 
// 377   11/06/98 5:17p Jimdose
// added missionfailed and missionfailedtime to level vars
// when a mission has failed or the player is dead in single player, the game
// code immediately shows the loadmenu, preventing them from letting the game
// continue running if they exit the menu
// 
// 376   11/05/98 2:06a Markd
// changed the way music works a bit, so that action would override anything
// else
// 
// 375   10/27/98 9:46p Aldie
// Changed training cvar to level.training
// 
// 374   10/27/98 5:10p Aldie
// Fixed take "all" for fists and spidermine
// 
// 373   10/26/98 10:32p Aldie
// If onladder, don't get affected by gravitypaths
// 
// 372   10/26/98 9:53p Markd
// Moved GotKill around, added gibfest sound
// 
// 371   10/26/98 8:12p Aldie
// Fixed some weapon bugs with detaching from owner when removing
// 
// 370   10/26/98 5:37p Markd
// expanded whatis functionality
// 
// 369   10/26/98 4:30p Aldie
// Changed airtime default to 20
// 
// 368   10/26/98 2:17p Aldie
// Added AirClamp
// 
// 367   10/26/98 2:18a Aldie
// Added FL_OXYGEN
// 
// 366   10/26/98 12:28a Markd
// put in no jc support
// 
// 365   10/25/98 11:55p Jimdose
// moved playerfrozen from game to level
// 
// 364   10/25/98 10:15p Aldie
// Added training cvar
// 
// 363   10/25/98 1:51a Aldie
// Bulletproof the floating inventory code
// 
// 362   10/24/98 6:39p Markd
// Added in more dialog based on Blade killing things
// 
// 361   10/24/98 6:21p Aldie
// Fixed spidermine error...
// 
// 360   10/24/98 5:44p Markd
// Added killent, removent, killclass removeclass and added parameters to
// whatis
// 
// 359   10/24/98 3:17p Aldie
// Take out gibs for parent mode
// 
// 358   10/24/98 3:14p Aldie
// Addec check for silencer, removed blood checks
// 
// 357   10/24/98 2:17p Markd
// Put in JC saying blade was dead
// 
// 356   10/24/98 4:05a Markd
// fixed zoomed in in a vehicle
// 
// 355   10/24/98 12:42a Markd
// changed origins to worldorigins where appropriate
// 
// 354   10/22/98 9:28p Aldie
// Added support for deathgib animations
// 
// 353   10/22/98 5:19p Aldie
// Fixed crash of using Powerups
// 
// 352   10/22/98 5:04p Markd
// Blade will no longer taunt deaths of civilians or inanimate objects
// 
// 351   10/22/98 4:21p Aldie
// Fixed spidermine and fov changing
// 
// 350   10/22/98 2:39a Jimdose
// player no longer does crouching bob when in water
// 
// 349   10/21/98 10:58p Jimdose
// Made TouchStuff not touch the world
// 
// 348   10/21/98 5:28p Aldie
// Added a setskin command
// 
// 347   10/21/98 1:45a Aldie
// Fixed silencer clearing out, updated gib test function
// 
// 346   10/20/98 10:29p Jimdose
// Restore crosshair when respawning in 3rd person
// 
// 345   10/20/98 7:42p Markd
// fixed weapons in vehicles
// 
// 344   10/20/98 5:22p Aldie
// Added die_gibs flag
// 
// 343   10/19/98 9:53p Jimdose
// Added blend for lightvolume
// changed slime variables to lightvolume
// 
// 342   10/19/98 5:29p Aldie
// Increased air time to 20
// 
// 341   10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// 
// 340   10/18/98 10:55p Aldie
// Added check for useless body removal on bodies that you don't need anything
// from
// 
// 339   10/18/98 9:01p Markd
// Added ShowWeapon support for vehicles and zooming in on vehicles
// 
// 338   10/18/98 1:31a Aldie
// Added some more stuff to whatis command
// 
// 337   10/17/98 12:23a Jimdose
// don't load persistant data during deathmatch
// 
// 336   10/16/98 2:27a Jimdose
// Added Endlevel
// saved godmode and notarget on level change
// made cameras work cross level and in savegames
// 
// 335   10/16/98 2:18a Aldie
// Took out mutant movement when in single player mutant mode
// 
// 334   10/14/98 10:58p Jimdose
// player now archives fov, viewmode, and skin damage
// 
// 333   10/14/98 6:29p Markd
// Put in STAT_EXITSIGN
// 
// 332   10/14/98 5:34p Aldie
// Mutant stuff
// 
// 331   10/14/98 1:19a Jimdose
// Got cross-level persistant info working
// 
// 330   10/12/98 8:54p Aldie
// Process player killed events
// 
// 329   10/12/98 8:44p Jimdose
// Rewrote init function
// started adding persistant functions
// 
// 328   10/12/98 12:59p Markd
// Fixed vehicle entering which messed up intro
// 
// 327   10/11/98 9:03p Aldie
// Updated Killed to clear mutant status
// 
// 326   10/11/98 7:40p Aldie
// Mutate and restore commands for Richard
// 
// 325   10/11/98 5:46p Aldie
// Obituaries
// 
// 324   10/11/98 5:33p Aldie
// Fix a couple init bugs
// 
// 323   10/11/98 12:02a Aldie
// Adrenaline effects
// 
// 322   10/10/98 9:58p Aldie
// Fixed mutant mode bug and changed alpha for PODD.
// 
// 321   10/10/98 9:14p Aldie
// Death message and fix camera in spidermines
// 
// 320   10/10/98 5:58p Aldie
// More quantumdestab fixes
// 
// 319   10/09/98 8:59p Aldie
// Moved oxygen to player
// 
// 318   10/09/98 4:54p Markd
// Replaced misc script code with ExecuteThread
// 
// 317   10/09/98 2:06a Aldie
// Updated DMFLAGS
// 
// 316   10/08/98 7:24p Markd
// changed vehicle movetype from noclip to vehicle
// 
// 315   10/07/98 11:57p Jimdose
// Added SetDeltaAngles
// Moved old_pmove out of client
// Moved PlayerFrozen to game
// Init isn't called until client enters game
// Got rid of MonsterGoal
// 
// 314   10/03/98 1:09p Aldie
// Fix a bug with flashcolor
// 
// 313   10/02/98 7:19p Aldie
// Added flash color to do blinding flashes
// 
// 312   9/30/98 11:22p Markd
// Changed taunts from volume 2 to volume 1
// 
// 311   9/30/98 11:11p Markd
// Limited fov in deathmatch
// 
// 310   9/30/98 5:39p Aldie
// Added showinfo command
// 
// 309   9/30/98 3:42p Markd
// put in bulletproofing for start thread functions
// 
// 308   9/30/98 1:25p Aldie
// Gib on ion destruct
// 
// 307   9/29/98 5:34p Aldie
// Moved gravity nodes to it's own function to fix goddam compiler bug
// 
// 306   9/29/98 2:59p Markd
// tried reformatting gravpath stuff
// 
// 305   9/29/98 2:12p Aldie
// Trying to fix grav release bug
// 
// 304   9/28/98 5:51p Markd
// 
// 303   9/28/98 4:34p Markd
// added "player_in_vehicle" variable for scripting
// 
// 302   9/28/98 4:07p Aldie
// Added oxygen powerup
// 
// 301   9/27/98 6:28p Aldie
// Added water, slime, and lava colors to worldspawn
// 
// 300   9/26/98 4:45p Aldie
// Added mutant mode
// 
// 299   9/24/98 1:48a Markd
// Made taunts double volume
// 
// 298   9/23/98 10:08p Aldie
// Gib on a ION_DESTRUCT
// 
// 297   9/22/98 8:10p Aldie
// Initialize some variables causing head bobbing
// 
// 296   9/22/98 5:19p Markd
// Put in new consolidated gib function
// 
// 295   9/22/98 3:27p Markd
// Took out old variable
// 
// 294   9/22/98 3:21p Markd
// put in parentmode lockout for blood and gibs
// 
// 293   9/22/98 2:42p Aldie
// Fixed health ticking below 100 when over 100 in prethink
// 
// 292   9/22/98 12:14p Aldie
// Added sentientFrozen and exit consoles when you take pain.
// 
// 291   9/21/98 1:34a Aldie
// Only do obits in deathmatch
// 
// 290   9/20/98 5:11p Aldie
// Fixed a crash with the "give" command
// 
// 289   9/19/98 4:47p Markd
// fixed music stuff and added actionincrement to weapons
// 
// 288   9/18/98 10:58p Jimdose
// Added spawnactor and actorinfo
// 
// 287   9/17/98 8:02p Jimdose
// Removed blending for underwater
// 
// 286   9/17/98 1:48p Markd
// Fixed swimmin animations
// 
// 285   9/16/98 8:58p Aldie
// Added ability to do a hold down weapon charge
// 
// 284   9/16/98 3:13p Aldie
// Initialize poweruptype 
// 
// 283   9/14/98 4:15p Markd
// zero out velocity when entering vehicles
// 
// 282   9/12/98 12:13a Jimdose
// NearestNode was being called even when ai_createnodes was not set
// 
// 281   9/11/98 5:03p Aldie
// Initialize draw overlay
// 
// 280   9/11/98 2:49p Aldie
// Fixed death message causing crash and added release firing.
// 
// 279   9/10/98 8:53p Markd
// put in proper falling and landing animation thresholds.
// 
// 278   9/10/98 12:45p Markd
// Added whereami support
// 
// 277   9/09/98 5:06p Markd
// Added savefov and restorefov, also added fov change when teleporting
// 
// 276   9/09/98 3:03p Markd
// fixed deathmatch camera
// 
// 275   9/07/98 6:21p Markd
// cleared out velocity when in vehicle.  Also don't use death cam when in
// single player
// 
// 274   9/07/98 5:28p Markd
// Fixed player kickangles while jumping, fixed prediciton while in the camera
// 
// 273   9/05/98 6:20p Markd
// Fixed player bouncing with kick angles
// 
// 272   9/05/98 6:03p Markd
// fixed next weapon and previous weapon in helicopter
// 
// 271   9/05/98 12:11p Aldie
// Fixed up falling damage flags and init'ed poweruptimer/poweruptype
// 
// 270   9/03/98 4:55p Jimdose
// Tweaked kick code
// 
// 269   9/02/98 7:47p Aldie
// Added ValidPlayerModels list
// 
// 268   9/02/98 12:54p Markd
// put in proper teleportation stuff
// 
// 267   9/02/98 11:57a Markd
// Put in walk_fire
// 
// 266   9/02/98 11:40a Markd
// really freeze the player when frozen
// 
// 265   9/01/98 7:46p Aldie
// Added itemname to inventory stuff
// 
// 264   9/01/98 12:10p Markd
// fixed run_firing while crouching
// 
// 263   8/31/98 7:45p Aldie
// Updated surface data structure and removed surfinfo field
// 
// 262   8/31/98 7:16p Markd
// Fixed player animation naming convention
// 
// 261   8/31/98 5:42p Aldie
// Changed powerups timer, added cloak
// 
// 260   8/30/98 7:29p Markd
// Put in auto-dead camera where after 10 seconds, player will follow the
// endnode1 path
// 
// 259   8/29/98 9:45p Jimdose
// Added call info to G_Trace
// 
// 258   8/28/98 8:06p Markd
// made crosshair change when targetting
// 
// 257   8/28/98 7:54p Markd
// Put in taunttime govenor on taunting
// 
// 256   8/28/98 4:13p Markd
// Made taunt into a VoiceSound so that AI would interact
// 
// 255   8/27/98 9:03p Jimdose
// Made grav a reference
// 
// 254   8/27/98 7:17p Aldie
// Don't draw the weapon when in a console.
// 
// 253   8/27/98 6:23p Jimdose
// Fixed bug with player walking in place
// 
// 252   8/27/98 4:50p Markd
// Fixed falling damage and detach things when dead
// 
// 251   8/27/98 2:30p Aldie
// Added adrenaline and more gravity stuff
// 
// 250   8/26/98 8:47p Aldie
// Removed a printf
// 
// 249   8/26/98 8:46p Aldie
// Put in velocity limiter for the gravity path nodes
// 
// 248   8/26/98 6:19p Aldie
// Fix for spidermine cameras
// 
// 247   8/25/98 8:02p Jimdose
// Now get the gravaxis from the starting location
// 
// 246   8/25/98 7:54p Markd
// Added third-person crosshair back into the game
// 
// 245   8/25/98 6:18p Aldie
// New spidermine and detonator support
// 
// 244   8/25/98 5:50p Markd
// Added THIRDPERSON support for client side crosshair
// 
// 243   8/25/98 4:11p Markd
// Added taunt support
// 
// 242   8/24/98 6:53p Jimdose
// Fixed falling damage on alternate gravity axis
// Fixed bug with inverted gravity axis
// 
// 241   8/24/98 5:35p Aldie
// Added projectile camera functionality
// 
// 240   8/24/98 3:49p Jimdose
// Fixed bug with inverted gravity axis
// Made fabric cause no falling damage
// 
// 239   8/24/98 11:32a Markd
// Added Start method to threads, repladed all ProcessEvent(
// EV_ScriptThread_execute) with thread->Start( -1 )
// 
// 238   8/22/98 9:57p Markd
// Only clear fade if not a cinematic
// 
// 237   8/22/98 9:36p Jimdose
// Added support for alternate gravity axis
// 
// 236   8/22/98 1:22a Markd
// Added Director.PlayerSpawned call, also made skipthread make sure there is
// at least one character
// 
// 235   8/21/98 7:38p Markd
// Fixed climbing code
// 
// 234   8/21/98 6:36p Markd
// made it so climbing works when climbing down as well
// 
// 233   8/21/98 3:47p Markd
// Only exit camera if strafing or movement are above a threshold
// 
// 232   8/21/98 1:05a Jimdose
// Got the movement and the model angles working for the alt-gravity stuff
// 
// 231   8/19/98 7:45p Jimdose
// Fixed viewhieght during noclip
// 
// 230   8/19/98 4:48p Aldie
// Fixed a bug in falling damage
// 
// 229   8/18/98 11:08p Markd
// Added new Alias System
// 
// 228   8/18/98 10:02p Jimdose
// Changed ClearPathTo to CheckMove
// 
// 227   8/17/98 8:59p Jimdose
// Added WhatIs
// 
// 226   8/17/98 7:45p Markd
// Added lava and slime 
// 
// 225   8/17/98 6:19p Markd
// Changed SetCamera to a Player method
// Got out of cinematic if in Pain
// 
// 224   8/17/98 4:34p Markd
// Put in camera exiting and no-falling-damage
// 
// 223   8/17/98 3:28p Jimdose
// Started adding code for varying gravity vectors
// 
// 222   8/17/98 2:56p Aldie
// Added weapon use command
// 
// 221   8/15/98 3:00p Markd
// Fixed camera/console issues with weapons
// 
// 220   8/14/98 8:14p Aldie
// Added generic overlay system
// 
// 219   8/13/98 8:11p Markd
// Made sure invulnerability gets reset in Init function
// 
// 218   8/13/98 8:11p Aldie
// Don't do pain animation if damage is below threshold
// 
// 217   8/13/98 1:53p Aldie
// Fixed initialization of pm
// 
// 216   8/12/98 6:03p Aldie
// Added a shield timer
// 
// 215   8/07/98 6:46p Aldie
// If you're dead, don't do prediction
// 
// 214   8/07/98 6:00p Aldie
// Credit frags to the correct player when falling damage kills someone.
// 
// 213   8/06/98 10:29p Markd
// Scaled down debris damage
// 
// 212   8/06/98 7:05p Markd
// Added crouch flag to effects
// 
// 211   8/03/98 7:36p Markd
// Put in debris damage
// 
// 210   8/03/98 1:15p Aldie
// Added QuantumD to give all
// 
// 209   8/02/98 9:00p Markd
// Merged code 3.17
// 
// 208   7/31/98 8:09p Jimdose
// Script commands now include flags to indicate cheats and console commands
// 
// 207   7/31/98 4:20p Jimdose
// Added GiveHealthCheat
// 
// 206   7/30/98 9:28p Jimdose
// Allows the Give cheat if Cheats are enabled
// 
// 205   7/29/98 2:32p Aldie
// Changed health to a float
// 
// 204   7/26/98 2:14p Aldie
// Force respawns in single player
// 
// 203   7/26/98 1:56p Aldie
// Respawn automatically
// 
// 202   7/26/98 12:34p Jimdose
// Changed cheat codes
// 
// 201   7/26/98 9:34a Markd
// Took out ToggleViewMode while in a vehicle
// 
// 200   7/26/98 4:15a Aldie
// Exit console = show stats
// 
// 199   7/26/98 1:18a Aldie
// Camera based consoles 
// 
// 198   7/26/98 12:50a Markd
// only do a reload on use, if not using something else
// 
// 197   7/26/98 12:45a Aldie
// Try to fix player death stuck anims
// 
// 196   7/25/98 7:13p Markd
// forgot to if_demo the spider mines
// 
// 195   7/25/98 7:04p Markd
// Eliminated unused weapons for demo
// 
// 194   7/25/98 5:42p Markd
// Fixed player runfire bug
// 
// 193   7/25/98 5:23p Markd
// Added GotKill to player
// 
// 192   7/25/98 4:36p Aldie
// Fade stuff
// 
// 191   7/24/98 10:04p Aldie
// Gibs and fade tweaks
// 
// 190   7/24/98 7:33p Aldie
// Changed method for hiding hud
// 
// 189   7/24/98 6:22p Jimdose
// Falling damage is now based on surface type (grass, concrete, wood, etc.)
// 
// 188   7/24/98 3:48p Aldie
// Fixed splashing sounds and hardcoded models, etc...
// 
// 187   7/23/98 9:56p Aldie
// Fixed obits and added hud command.
// 
// 186   7/23/98 7:12p Aldie
// Fun with gibs
// 
// 185   7/23/98 6:17p Aldie
// Updated damage system and fixed some damage related bugs.  Also put tracers
// back to the way they were, and added gib event to funcscriptmodels
// 
// 184   7/23/98 1:50p Aldie
// give armor in give all
// 
// 183   7/23/98 12:29p Markd
// Put in climbing support
// 
// 182   7/22/98 10:21p Markd
// Added support for multi-handed weapons and firing
// 
// 181   7/22/98 7:03p Aldie
// More MOD messages and remove actors when we clean them out.
// 
// 180   7/22/98 5:28p Markd
// FIxed third person camera stuff
// 
// 179   7/21/98 9:04p Markd
// Added UpdateMusic and stuff
// 
// 178   7/21/98 1:10p Aldie
// Added meansofdeath to obituaries
// 
// 177   7/20/98 6:54p Aldie
// Moved cancellation of fades to init function
// 
// 176   7/20/98 5:40p Aldie
// Fixed selected item stats.
// 
// 175   7/20/98 12:09p Markd
// Added vehicle anim support
// 
// 174   7/19/98 10:18p Aldie
// You should not be able to override the death animation.
// 
// 173   7/19/98 7:38p Markd
// Made sure to get rid of ENVMAPPED and DLIGHT in Init function
// 
// 172   7/19/98 5:40p Markd
// Fixed shields, added takeItem support
// 
// 171   7/19/98 3:42p Markd
// Fixed fade out to all players
// 
// 170   7/18/98 7:36p Aldie
// Added support for fades
// 
// 169   7/18/98 4:02p Markd
// Added vehicle check for snapintial
// 
// 168   7/18/98 3:53p Markd
// Put in vehicle code that prevents the player from going into other
// animations while in vehicle
// 
// 167   7/17/98 4:01p Markd
// Added UseWeapon method and weapons for vehicle
// 
// 166   7/17/98 11:31a Aldie
// Added clipammo and some reloading stuff when the player switches weaps
// 
// 165   7/15/98 11:23p Markd
// Added FL_SHIELDS support
// 
// 164   7/15/98 9:58p Markd
// Clear out environment mapping when respawning
// 
// 163   7/15/98 12:00a Markd
// Added location based pain animations
// 
// 162   7/14/98 9:54p Markd
// Fixed camera stuff
// 
// 161   7/14/98 5:37a Jimdose
// Removed definition of player height (already in q_shared.h)
// 
// 160   7/14/98 5:35a Jimdose
// Removed unused variable "bob"
// now initializing v_dmg_time.  Should fix bug where player's view is screwy
// when starting a new map
// 
// 159   7/13/98 5:00p Aldie
// Added dead player bodies with gibbing
// 
// 158   7/12/98 9:48p Markd
// Fixed weird lerping problem when pausing and unpausing the game and viewing
// from a cinematic camera or third person.  Turned out that
// client->ps.pmove.origin was being overwritten by incoming client movement
// commands.  So when you have an external camera, the origin is saved and
// restored around pmove movement.
// 
// 157   7/12/98 6:34p Markd
// Fixed camera stuff so that player automatically warps to camera properly
// 
// 156   7/11/98 8:58p Markd
// Added testthread command
// 
// 155   7/11/98 8:22p Jimdose
// Players are no longer non-solid when the respawn
// 
// 154   7/10/98 11:18p Jimdose
// Removed third-person crosshair
// 
// 153   7/10/98 11:11p Jimdose
// Players only swim when the water is deep enough
// no longer switch weapons after dropping them when killed
// 
// 152   7/09/98 11:39p Aldie
// Don't draw scores when in DM
// 
// 151   7/09/98 10:39p Aldie
// Moved bodyparts to game
// 
// 150   7/08/98 3:11p Aldie
// First try at spectator mode
// 
// 149   7/07/98 8:06p Aldie
// Spectator and dead stuff
// 
// 148   7/07/98 6:08p Aldie
// Gibs, drop weaps, water swim
// 
// 147   7/03/98 12:02p Aldie
// Made player init on the model specified in client data
// 
// 146   7/01/98 7:02p Aldie
// Mission computer and removed some crosshair stuff for zoom
// 
// 145   7/01/98 4:38p Aldie
// Moved some stats around
// 
// 144   6/28/98 3:43p Markd
// removed precaching from player
// 
// 143   6/27/98 4:25p Aldie
// Made a STAT_WEAPONLIST
// 
// 142   6/25/98 8:47p Markd
// Added keyed items for Triggers, Rewrote Item class, rewrote every pickup
// method
// 
// 141   6/25/98 7:31p Aldie
// Changed the armor stats
// 
// 140   6/24/98 1:37p Aldie
// Implementation of inventory system and picking stuff up
// 
// 139   6/22/98 2:04p Jimdose
// Added vehicles back in
// Organized the control code a bit more.  May want to make physics into a
// class
// 
// 138   6/20/98 6:21p Aldie
// Added modelindex for inventory stat
// 
// 137   6/20/98 2:13p Aldie
// Inventory system stats
// 
// 136   6/19/98 6:36p Aldie
// Put some more inventory managment back in
// 
// 135   6/18/98 9:25p Aldie
// Added inventory commands
// 
// 134   6/18/98 3:57p Aldie
// Changed the zooming alpha belnd color
// 
// 133   6/17/98 5:41p Aldie
// Returned scale of player back to normal
// 
// 132   6/17/98 3:03p Markd
// Changed NumArgs back to previous behavior
// 
// 131   6/17/98 2:47p Markd
// Inverted roll on player
// 
// 130   6/17/98 10:54a Aldie
// Removed the scaling of player's pitch
// 
// 129   6/15/98 10:48p Jimdose
// Added AddPathNode
// 
// 128   6/13/98 8:20p Jimdose
// Removed optimize path stuff
// 
// 127   6/10/98 7:53p Markd
// Made NumArgs behave correctly like argc
// 
// 126   6/10/98 2:10p Aldie
// Updated damage function.
// 
// 125   6/08/98 7:12p Markd
// put in assertions for ammo_types and armor_types so that the game would not
// crash
// 
// 124   6/08/98 7:15p Aldie
// Added SpiderMines
// 
// 123   6/08/98 11:34a Aldie
// Updated "give all" command
// 
// 122   6/05/98 6:25p Aldie
// Added armor stats to player
// 
// 121   6/04/98 4:35p Markd
// changed action_level decay
// 
// 120   6/03/98 4:38p Markd
// Added gunoffset and gunangle, changed the way weapons work with the camera,
// removed weapon parameter from SetCamerablablah
// 
// 119   5/28/98 8:01p Markd
// Clamped action_level at 80 
// 
// 118   5/28/98 7:17p Markd
// Tweaked music and action_level
// 
// 117   5/28/98 6:01p Markd
// moved action_level stuff around
// 
// 116   5/27/98 8:36p Markd
// Put in swimming, got rid of some "Give All" weapons
// 
// 115   5/27/98 7:04a Markd
// put in primitive action level
// 
// 114   5/27/98 6:32a Markd
// cleared out damageskin state in respawn
// 
// 113   5/27/98 5:21a Markd
// switched order of giveWeapon's
// 
// 112   5/26/98 7:55p Jimdose
// added scripted cameras
// 
// 111   5/26/98 4:45p Aldie
// Added take (object) from player
// 
// 110   5/25/98 1:39p Aldie
// Added create console user
// 
// 109   5/26/98 4:37a Jimdose
// Third person camera works again
// Added tracking crosshair
// 
// 108   5/26/98 1:54a Markd
// Fixed up a bunch of player sounds
// 
// 107   5/25/98 6:47p Jimdose
// Made animateframe, prethink and posthink into functions built into the base
// entity class
// 
// 106   5/24/98 8:46p Jimdose
// Made a lot of functions more str-friendly.
// Got rid of a lot of char * based strings
// Cleaned up get spawn arg functions and sound functions
// sound functions now use consistant syntax
// 
// 105   5/24/98 4:48p Jimdose
// Made char *'s const
// 
// 104   5/24/98 1:04a Jimdose
// Added sound events for ai
// 
// 103   5/22/98 12:22p Aldie
// Removed unused variable from player struct
// 
// 102   5/22/98 12:17p Aldie
// Added gravity paths and updated earthquake effect
// 
// 101   5/20/98 10:22p Aldie
// Added earthquake effects
// 
// 100   5/18/98 8:14p Jimdose
// Renamed Navigator back to PathManager
// 
// 99    5/16/98 5:00p Markd
// Added RF_XFLIP stuff
// 
// 98    5/13/98 4:49p Jimdose
// Now use SafePtrs for keeping track of nodes
// 
// 97    5/11/98 3:19p Markd
// Changed zoom-in and zoom-out sounds
// 
// 96    5/11/98 2:19p Markd
// Fixed randomsound stuff
// 
// 95    5/11/98 11:25a Markd
// Added new weapons
// 
// 94    5/09/98 7:44p Markd
// Added gunmodel, gunframe and gunindex for other side gun animations
// 
// 93    5/08/98 7:01p Markd
// Took out RF_SHELL flags
// 
// 92    5/07/98 11:23p Markd
// Added playerframe, playeranim and playerindex to playerstate
// 
// 91    5/07/98 10:46p Aldie
// Fixed players exiting consoles.
// 
// 90    5/07/98 10:43p Jimdose
// working on cameras and archiving
// 
// 89    5/05/98 2:40p Jimdose
// Added support for new camera system.
// Rewrote a lot of stuff to allow a clean separation between the camera
// control variables in client and the Player entity.  This allows a much
// easier interface for creating new camera views
// 
// 88    5/03/98 4:46p Jimdose
// Added camera view
// changed Vector class.  Aliased vectors via pointsTo no longer supported.
// 
// 87    5/02/98 4:24p Jimdose
// Added FL_BLOOD to player
// 
// 86    5/01/98 6:15p Jimdose
// Made Respawn an event so that Trigger_Hurt wouldn't rekill a player after
// respawn
// 
// 85    5/01/98 5:55p Aldie
// Removed debugging printf's
// 
// 84    4/29/98 5:38p Jimdose
// Added death messages
// Sniper rifle zooms out before changing weapon
// fov no longer a cheat and is persistant across respawns
// 
// 83    4/27/98 6:09p Jimdose
// Changed alpha on debug lines
// 
// 82    4/27/98 4:12p Jimdose
// Working on path placement
// 
// 81    4/21/98 2:25p Aldie
// Added enterconsole and exit console events.
// 
// 80    4/20/98 5:44p Jimdose
// working on ai
// 
// 79    4/20/98 2:45p Jimdose
// working on ai
// 
// 78    4/18/98 6:10p Aldie
// Updated ammo stats.
// 
// 77    4/18/98 4:24p Aldie
// Added ammo bar to stats.
// 
// 76    4/18/98 4:06p Jimdose
// Working on ai
// 
// 75    4/18/98 3:17p Markd
// Changed view weapon naming convention
// 
// 74    4/18/98 3:02p Jimdose
// Added ai_createnodes and ai_showpath	
// working on ai
// 
// 73    4/16/98 6:22p Jimdose
// 
// 72    4/16/98 5:35p Aldie
// Change fov of sniperrifle
// 
// 71    4/16/98 3:22p Jimdose
// 
// 70    4/16/98 2:09p Jimdose
// Added prevframe support for guns
// No longer set SOLID_NOT in destructor (world may have been deleted, so the
// gi.link in setSolidType would crash)
// 
// 69    4/09/98 8:44p Jimdose
// Added view blend effects
// added drowing
// 
// 68    4/09/98 7:44p Aldie
// Added blending for sniper zoom.
// 
// 67    4/08/98 5:32p Jimdose
// Fixed bug in SpawnEntity
// Added key/value pairs to spawn command
// 
// 66    4/08/98 4:46p Jimdose
// Made weapon appear in third person view
// Secondary Use isn't sent until the weapon is ready
// Zoom mode overrides third person view.
// 
// 65    4/07/98 11:55p Jimdose
// Changed beams to specify color
// 
// 64    4/07/98 8:05p Jimdose
// Made use much more sensitive.  Opens too many things at once, but, oh well.
// 
// 63    4/07/98 6:41p Jimdose
// Fixed bugs in weapon code
// Added checks for being dead in various functions (changing weapons)
// working on making use more responsive
// 
// 62    4/07/98 3:48p Aldie
// Added zooming crosshair
// 
// 61    4/06/98 8:00p Jimdose
// Init resets your gravity to 1.0
// 
// 60    4/06/98 7:10p Aldie
// Added zooming for SniperRifle
// 
// 59    4/05/98 2:57a Jimdose
// added respawn_time
// made firing anim work temporarily
// 
// 58    4/04/98 6:07p Jimdose
// Added events for syncing firing to animation
// 
// 57    4/02/98 4:56p Jimdose
// Added pain, obituary and showscores
// Fixed bug in Init where persistant data was always being cleared
// ammo stats are now kept up to date
// Added falling and landing animations
// 
// 56    3/31/98 1:40p Jimdose
// Removed footstep stuff (it's in entity now)
// jump sound is now played by the model
// 
// 55    3/31/98 12:47p Jimdose
// Fixed the gunoffset
// 
// 54    3/30/98 11:39p Markd
// Added modelIndex stuff
// 
// 53    3/30/98 9:55p Jimdose
// Changed location of .def files
// 
// 52    3/30/98 2:41p Jimdose
// Worked on weapons
// Fixed footstep.  There weren't any 'break's in the switch statement
// 
// 51    3/29/98 9:42p Jimdose
// Moved animation stuff to sentient
// Changed killed to an event
// 
// 50    3/29/98 5:57p Jimdose
// Added SpawnEntity command
// 
// 49    3/28/98 8:55p Jimdose
// Added pulse rifle
// 
// 48    3/28/98 6:51p Jimdose
// Made the angles in player starts work
// 
// 47    3/28/98 4:35p Jimdose
// Added kill command
// Added deathmatch starts
// You can no longer move your view when you're dead
// 
// 46    3/27/98 10:00p Jimdose
// Changed all worldangle references to use setAngles
// 
// 45    3/27/98 6:36p Jimdose
// Precached weapon models
// Added assault rifle
// 
// 44    3/27/98 5:40p Jimdose
// Added third person view mode
// respawn now calls FreeInventory so that weapons don't carry over when you
// die
// 
// 43    3/26/98 8:25p Jimdose
// Added deathmatch
// Precache sounds
// Added animation control functions
// 
// 42    3/23/98 1:31p Jimdose
// Revamped event and command system
// 
// 41    3/18/98 7:22p Jimdose
// Added new-style weapons
// Fixed bug where orientation and v_angle wasn't being set
// 
// 40    3/04/98 5:13p Aldie
// Added support for damage surfaces.
// 
// 39    3/03/98 6:56p Aldie
// Removed a printf.
// 
// 38    3/03/98 6:51p Aldie
// Added some more footstep sounds.
// 
// 37    3/03/98 6:02p Aldie
// First pass at footsteps.
// 
// 36    3/02/98 8:49p Jimdose
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
// that you could have a NULL classid.
// 
// 35    3/02/98 5:41p Jimdose
// Added commands for testing paths
// Changed the trace that use does.
// 
// 34    2/21/98 1:21p Jimdose
// Removed A LOT of unused varables and code.  Anything from here on out should
// be code written specifically for Sin and not converted Quake code.  Quake is
// not a blueprint.  It's only a reference.
// 
// 33    2/19/98 2:35p Jimdose
// Added weapons back in
// 
// 32    2/17/98 8:34p Jimdose
// Enabled the use key
// 
// 31    2/06/98 5:44p Jimdose
// replaced use of think and touch functions with events
// move client to Entity
// 
// 30    2/03/98 10:59a Jimdose
// Updated to work with Quake 2 engine
// Moved initialization to constructor and removed Init function
// This will probably be rewritten to be simpler.  This first rewrite sucks.
// 
// 28    12/15/97 2:52p Markd
// Fixed default weapons
// 
// 27    12/15/97 12:35p Aldie
// 
// 26    12/14/97 8:51p Aldie
// Updated impulse 9 to give all weapons.
// 
// 25    12/14/97 8:16p Aldie
// Modified give all weapons list.
// 
// 24    12/14/97 5:33p Markd
// Made default weapon Shotgun instead of DoubleBarrel
// 
// 23    12/13/97 6:53p Aldie
// Added shotgun to give all weapons list
// 
// 22    12/06/97 1:31p Aldie
// Fixed give weapon handler so that "give all" is case insensitive.
// 
// 21    12/06/97 12:34p Aldie
// Added Tracer Gun to give all weapons list.
// 
// 20    12/05/97 4:08p Aldie
// Added event to player for giving weapons.
// 
// 19    11/18/97 5:29p Markd
// Added magnum and spear gun to impulse 9, checked for NULL currentWeapon and
// updated weapon frames in PlayerPostThink
// 
// 18    11/12/97 5:12p Jimdose
// Changed enter and exit vehicle messages to be defined in Player.h
// 
// 17    11/12/97 2:10p Jimdose
// Simplified the interface to PathSearch
// 
// 16    11/10/97 8:30p Jimdose
// Flags were being subtracted rather than masked off in Jump.
// 
// 15    11/07/97 6:49p Jimdose
// Added code for testing path.cpp.  Enable by defining TESTPATH
// 
// 14    11/01/97 2:19p Jimdose
// looking
// 
// 13    11/01/97 2:12p Jimdose
// jump in vehicles
// 
// 12    10/31/97 9:02p Jimdose
// Added FL_PRETHINK and FL_POSTTHINK
// 
// 11    10/30/97 11:48p Jimdose
// Worked on vehicle code
// 
// 10    10/29/97 12:54p Markd
// made viewthing multiple spawnable
// 
// 9     10/29/97 11:51a Markd
// Made viewthing always face you when spawned.
// 
// 8     10/28/97 6:54p Markd
// Added SPAWN_VIEWTHING message.  Also added "return true" to other handled
// messages
// 
// 7     10/27/97 3:29p Jimdose
// Removed dependency on quakedef.h
// 
// 6     10/22/97 12:24p Jimdose
// Disabled warning when player recieves a blocked event.
// 
// 5     10/09/97 4:26p Jimdose
// Worked on vehicle code
// 
// 4     10/08/97 9:30p Jimdose
// Refined Vehicle movement.
// 
// 3     10/08/97 6:03p Jimdose
// Began vehicle support.
// 
// 2     9/26/97 6:47p Jimdose
// Added standard Ritual headers
//
// 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>

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 ] = 
	{
	"Bullet357", "ShotgunClip", "Bullet10mm", "Bullet50mm", "BulletPulse", "BulletSniper", "Rockets", "SpiderMines"
	};

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 );

#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 },

      { 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;

	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();

   // 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;

   // 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;
   }

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
   )

   {
   // set up the player's spawn location
   SelectSpawnPoint( origin, angles, &gravaxis );
	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 );
      }
   }

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()
	{
	if ( client )
		{
		gi.bprintf( PRINT_HIGH, "%s disconnected\n", client->pers.netname );
		}

	if ( watchCamera && ( ( watchCamera == thirdpersonCamera ) || ( watchCamera == spectatorCamera ) ) )
		{
		delete watchCamera;
		watchCamera = NULL;
      thirdpersonCamera = NULL;
      spectatorCamera = NULL;
		}

   if ( crosshair )
		{
		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
   )

   {
   hideModel();
   setSolidType( SOLID_NOT );
   spectator = true;
   FreeInventory();

   // Send over a message that lets the player know they are in spectator mode.

   gi.WriteByte (svc_layout);
   gi.WriteString ("jcx jt hstring 0 0 1 \"SPECTATOR MODE\"");
	gi.unicast (edict, true);

	client->ps.stats[ STAT_LAYOUTS ] |= DRAW_SPECTATOR;
   }

void Player::Obituary
	(
	Entity *attacker,
   Entity *inflictor,
   str location,
   int meansofdeath
	)

	{
   const char *message1 = NULL;
   const char *message2 = "";
   int num;
   
   if ( !deathmatch->value )
      return;

   // 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 ( 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--;
		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;
            }
         default:
            message1 = "was killed by";
         }           
         gi.bprintf( PRINT_MEDIUM, "%s %s %s%s\n", client->pers.netname, 
                                            message1,
                                            attacker->client->pers.netname,
                                            message2 );
		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 )
            {
            attacker = enemy;
            message1 = "was pushed over the edge by";
            message2 = "";
            
            // Give the last enemy a frag
            enemy->client->resp.score++;
            }
         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);
      client->resp.score--;
      }
   else
      {
      gi.bprintf( PRINT_MEDIUM, "%s died.\n", client->pers.netname );
	   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
   gi.WriteByte( svc_stufftext );
   gi.WriteString( "playerdead" );
   gi.unicast( edict, true );
#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;

	attacker       = ev->GetEntity( 1 );
	inflictor      = ev->GetEntity( 3 );
   location       = ev->GetString( 4 );
	meansofdeath   = ev->GetInteger( 5 );

   // 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 )
		{
      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 to before the deadflag being set 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 )
         {
         delete currentWeapon;
         }
      }

   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 ) )
      {
      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 );

   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
         {
         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 && damage > 5)
		{
      str prefix;
      str aname;
      int index;

      //
      // determine pain animation
      //
      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;


   // Pickup inventory if one exists
   PickupFloatingInventory();

	start = worldorigin + Vector( 0, 0, viewheight );
	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
	)

	{
	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 ) ) )
		{
		delete watchCamera;
		watchCamera = NULL;
      thirdpersonCamera = NULL;
      spectatorCamera = NULL;
		}

   if ( crosshair )
		{
		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
			   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;
			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, 128, MASK_PLAYERSOLID );
			watchCamera = c;
         spectatorCamera = c;
         }
			break;

		case FIRST_PERSON :
			watchCamera = this;
			break;

		case CAMERA_VIEW :
			watchCamera = newCamera;
			break;
		}
	}

EXPORT_FROM_DLL void Player::CheckButtons
	(
	void
	)

	{	
	// Only process buttons if you're not dying
	if ( deadflag != DEAD_NO )
		{
		return;
		}

	if ( new_buttons & BUTTON_USE )
		{
		DoUse();
		}

   if ( ( buttons & BUTTON_ATTACK ) && spectator )
      ChangeSpectator();

   // 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;

      if ( !vehicle )
         {
         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 ( 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;
		}
	else if ( viewmode == CAMERA_VIEW )
		{
		return PM_LOCKVIEW;
		}
	else if ( vehicle )
		{
   	if ( zoom_mode == ZOOMED_IN )
         {
         return PM_INVEHICLE_ZOOM;
         }
      else
         {
         return PM_INVEHICLE;
         }
		}
	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;
		}
   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_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 );

	if ( !deadflag && !level.playerfrozen )
		{
      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;
            }
			}
		}

	// 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
	)
	
	{
   if ( !has_thought )
      {
      // let any threads waiting on us know they can go ahead
      Director.PlayerSpawned();
      has_thought = true;
      }

	new_buttons	= current_ucmd->buttons & ~buttons;
	buttons		= current_ucmd->buttons;

	// 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 five seconds
		if ( level.time > level.intermissiontime + 5.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 );
      }

	if ( !vehicle || !vehicle->Drive( 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 ) &&
         (
            ( 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 );
            }
         }
      }

	// 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 );

   if ( checkInheritance( &Weapon::ClassInfo, name ) )
   	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;
   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;
		}

   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" );
#endif
   giveItem( "RiotHelmet", 100);
   giveItem( "FlakJacket", 100);
   giveItem( "FlakPants", 100);
	}

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";
		}
	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;
		}

   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;
		}

   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;
      }

   // Spectators don't need 3rd person
   if ( spectator )
      {
      defaultViewMode = SPECTATOR;
		SetViewMode( SPECTATOR );
      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;

	// 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;
	
		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;

	// 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;

	// 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;
		}

	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;
		}
   //
   // 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
	)

	{
	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;
   		Damage( world, world, 10 * 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;
		}
	}

/*
===============
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;
		}

	//
	// 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 )
		{
		SetAnim( vehicleanim.c_str() );
		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;
				}
			else
				{
         	currentWeapon->edict->s.renderfx |= RF_DONTDRAW;
				}
			}
      if ( vehicle && vehicle->IsDrivable() )
         {
	      SetCameraValues( worldorigin, v_offset, v_angle, v_kick, velocity, blend, fov*1.25 );
         }
      else
         {
	      SetCameraValues( worldorigin, v_offset, v_angle, v_kick, velocity, blend, fov );
         }
		}
	else
		{
		float noblend[ 4 ];
		float camerafov;
      Vector pos;

/*
      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 ( 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;
      
		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;
			}

		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
         {
		   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;
	delta = velocity[ z ] - oldvelocity[ z ];

	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;
         }

		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;

   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"
	//
   if ( 
         ( GetMovePlayerMoveType() != PM_LOCKVIEW )  &&
         ( GetMovePlayerMoveType() != PM_FREEZE )
      )
      {
	   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;
      }

	//
	// 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();
	FallingDamage();

	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
   if ( currentWeapon )
		{
		client->ps.stats[ STAT_AMMO ] = currentWeapon->AmmoAvailable();
      client->ps.stats[ STAT_CLIPAMMO ] = currentWeapon->ClipAmmo();
		}
	else
		{
		client->ps.stats[ STAT_AMMO ] = 0;
		}


   // not used, so don't set to save net bandwidth
#if 0
   // All ammo types
   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;
      }

   //
   // 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();
         }
      assert( watchCamera );
      if ( !watchCamera )
         {
         // 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
   )

   {
   edict_t		*cl_ent;

   currentCameraTarget = ( currentCameraTarget + 1 ) % game.maxclients;
   cl_ent = g_edicts + 1 + currentCameraTarget;

   while ( !cl_ent->inuse )
      {
      currentCameraTarget = ( currentCameraTarget + 1 ) % game.maxclients;
      cl_ent = g_edicts + 1 + currentCameraTarget;
      }
   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 ) );

   }
