////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// dude.cpp
// Project: Postal
//
// This module implements the CDude class, which is the main, player-controlled
// character in the game.
//
// History:
//		01/12/97 MJR	Started.
//
//		01/15/97 BRH	Changed 3D to 2D render position to subtract the
//							Y rather than Add it.
//
//		01/23/97	JMI	Now calls CRealm::GetLayerFromAttrib() in Render() to
//							get on the proper layer.
//
//		01/31/97 BRH	Added the ability to throw Grenades.
//
//		01/29/97	JMI	Now Load() and Save() call the base class versions.
//
//		02/04/97	JMI	Changed LoadDib() call to Load() (which now supports
//							loading of DIBs).
//
//		02/07/97 BRH	Changed to using the single frame 3D wire frame
//							instead of the 2D sprite.
//
//		02/07/97	JMI	Added XRay flag to dude.
//
//		02/07/97	JMI	GetResources() never closed its RFile.  Fixed.
//
//		02/07/97	JMI	Now CScene has its own pipeline, so we don't need ours
//							anymore.  And we don't need to set any of that schtuff
//							up.  Removed all the pipeline initialization stuff.
//
//		02/10/97	JMI	Added the 3D animation for walking (or is it running?).
//
//		02/11/97	JMI	Added the 3D animation for throwing and an associated
//							rigid body transform for a grenade.
//
//		02/12/97	JMI	Forgot to set m_pGrenade->m_dRot again on release so the
//							grenade would have correct trajectory even if the dude
//							had rotated since the start of the throw.  Fixed.
//
//		02/12/97	JMI	Now animates backwards for running backwards.
//
//		02/13/97	JMI	Changing RForm3d to RSop.
//
//		02/13/97	JMI	Now the dude maps his textures to the palette on load.
//
//		02/14/97	JMI	Removed Remap().  This'll now be done by a utility before
//							we even load the textures.
//							Also, changed the direction and rotation the guy does.
//							Currently, his position is hosed but I need to check it
//							in to get it at home.
//
//		02/17/97	JMI	Now uses actual standing anim for standing state.
//							Changed resource filenames to not use two '.'s.
//							Now uses new m_pRealm->m_scene.TransformPtsToRealm() func
//							to get relative position of grenade on release.
//							For the mapping from 3D to 2D, now does not take radius
//							into account b/c the scene actually draws relative to the
//							origin.
//							Added UpdateRadius() function to get the current
//							m_sSphereRadius of the guy.  But this is incorrect b/c
//							we should not be using m_sSphereRadius, but instead some-
//							thing like m_sCylinderR and m_sCylinderH to determine
//							the xray position, EditRect(), and EditHotSpot().
//
//		02/17/97	JMI	Now changes the lighting if the lighting bit is set in the
//							checked attribute zone.
//
//		02/18/97 MJR	Tuned guy turning and running speeds, added use of "shift"
//							key to run faster, switched all animations over to newly
//							existant main-guy versions.
//
//		02/18/97	JMI	Added firing of bullets.
//							Also, removed #if 0, #elif 0, .... blocks.
//							Also, added dieing animation and die state.
//							Now goes into Die state when hit in response to several
//							messages.
//
//		02/19/97	JMI	Now passes more masks to FireDeluxe() and uses Character
//							bit.
//							Also, does not subtract 90 before blitting.
//
//		02/19/97	JMI	Slowed bullets to MAX_BULLETS_PER_SEC.
//
//		02/19/97	JMI	Enhanced SetState().  Not it checks via its current state
//							if it's okay to change to the new state, and if it's okay,
//							cleans up the old state, sets the new state, and returns
//							true, if successful.
//
//		02/19/97	JMI	Added shoot while still and while running.
//
//		02/19/97	JMI	Changed main_rungun.* to main_run.* for running anim b/c
//							main_rungun looked too much like main_runshoot.
//							Also, now cannot rotate at user request when dead.
//							Now has a running and standing shooting anim.
//							Encapsulated release of grenade into ReleaseGrenade() so
//							it can be reused.
//							Fixed a bunch of little things in SetState().
//
//		02/19/97	JMI	Brightness tuned slightly due to everything seeming to
//							change in brightness after tonight's assets fest.
//
//		02/19/97	JMI	Added damage state.
//
//		02/20/97 MJR	Added use of new input functions as part of enabling
//							multiplayer network mode.
//
//		02/20/97	JMI	Changed bit masks to FireDeluxe so CDudes can shoot any
//							other characters.
//
//		02/20/97	JMI	Now processes delete message.
//
//		02/20/97	JMI	Added blood and now sets m_sprite flags only once (in
//							Init()).
//
//		02/21/97	JMI	Had forgotten to release the 3d/main_shot.* resource.
//							Fixed.
//
//		02/21/97	JMI	ThrowRelease was allowing any state change when it was the
//							current state.
//							Also, now if the dude changes animations during a throw,
//							the grenade just drops instead of exploding immediately.
//
//		02/21/97	JMI	The grenade now rolls off randomly when dropped accidently.
//
//		02/23/97	JMI	Now any state can jump out of damage state.
//
//		02/24/97	JMI	Use 3D 'link'-like pt for bullet start position.
//
//		02/24/97	JMI	Better blood splat and added blood pool.
//
//		02/24/97	JMI	No longer sets the m_type member of the m_sprite b/c it
//							is set by m_sprite's constructor.
//
//		02/24/97	JMI	Now State_Damage cannot interrupt State_Damage which looks
//							a lot better.  It would look even better still, I think,
//							if the shot animation was a little shorter/faster.
//
//		02/24/97	JMI	Now tweaks angle slightly before calling FireDeluxe() so
//							the gun does not aim perfectly.
//
//		02/24/97	JMI	Added on-fire state.
//
//		02/24/97	JMI	Tweaked on-fire state.
//
//		02/24/97	JMI	Changed reference to CAnimThing's m_pthingSendMsg member to
//							m_u16IdSendMsg.
//
//		02/24/97	JMI	No longer bleeds due to fire.
//
//		02/25/97	JMI	Added new burning yell sound.
//
//		02/26/97	JMI	Now <Control> fires weapon and <Alt> throws grenade.
//
//		03/03/97	JMI	Added m_statePersistent.
//							Now sets m_statePersistent if State_Damage interrupts
//							State_Burning.
//
//		03/03/97	JMI	Now passes CSmash::Dead as a bit that FireDeluxe() will not
//							hit.
//							Now sets m_smash.m_bits to include CSmash::Dead when we
//							reach State_Dead and removes it again when be leave
//							State_Dead.
//
//		03/03/97	JMI	Added states for strafing and multiweapon logic.
//							Currently, loads run and shoot for strafe anims which
//							should do until the strafe equivalents are ready.
//
//		03/03/97	JMI	Changed m_pGrenade to m_pWeapon (a CWeapon* instead of
//							a CGrenade*) so more weapons can be handled generically.
//
//		03/03/97	JMI	Now CDude is based upon CCharacter.
//							Not much base class functionality utilized yet, though.
//
//		03/04/97	JMI	Now utilizes more of the base class functionality.
//							All CCharacter::Edit*() functions are used.
//							Now uses real strafe animations.
//							Moved stuff I was incorrectly doing in Render() into
//							Update() so CDude now uses CCharacter::Render().
//							Update() now uses functions in CCharacter (i.e.,
//							UpdateVelocities(), GetNewPosition(), and
//							MakeValidPosition() ) to process acceleration,
//							velocities, and positions.
//
//		03/04/97	JMI	Got rid of #if 0'd out code in Update().
//
//		03/04/97	JMI	Now utilizes message overrides from CCharacter.
//
//		03/04/97	JMI	Removed UpdateRadius().
//							Now calls CCharacter::UpdateFirePosition() to keep fire
//							position up to date.
//							Converted all rspMod360() calls to new calling convention.
//							Removed m_panimthing for fire (now uses base class fire).
//							Removed ProcessMessages() (now base class is fine).
//
//		03/05/97	JMI	Was checking for fire gone incorrectly.  Fixed.
//
//		03/05/97	JMI	Now fires weapons (except SemiAutomatic) through base class.
//							Eventually, might move that to base class too??
//
//		03/05/97	JMI	Removed ReleaseWeapon() now uses CCharacter::ShootWeapon().
//
//		03/05/97	JMI	m_pWeapon no longer referenced.
//							Uses new multishot animation for when he's been shot.
//
//		03/05/97	JMI	Removed ms_apt3dAttribCheck (these points are now stored
//							and checked in CCharacer).
//							Added Suicide.
//
//		03/06/97	JMI	Drag no longer gets set to zero when velocity reaches zero
//							by this class (now handled by base class).
//							Added override to OnSuicideMsg().
//							Added a little fudge to BrainSplat location.
//
//		03/06/97	JMI	No longer does hack to get ID from m_pWeapon since the base
//							class now stores the ID in m_u16IdWeapon (and does not
//							rely on m_pWeapon to reference the weapon).
//
//		03/06/97	JMI	Now sets his velocity to 0.0 when committing suicide.
//
//		03/06/97	JMI	Changed fudge factor delay for suicide brain splat to
//							1400 ms (was 700 ms).
//
//		03/06/97	JMI	User can no longer influence velocity when dude is being
//							shot.
//
//		03/13/97	JMI	Added EditModify() member function.
//							Also, added m_sNumFireBombs/Missiles/Napalms.
//							Load now takes a version number.
//
//		03/13/97	JMI	Added file format version 2 load stuff:
//							m_sNumFireBombs/Missiles/Napalms.
//							Now considers amount of particular weapon left before
//							firing it.
//
//		03/13/97	JMI	Added DrawStatus() member function.
//							Now can run out of bullets.
//							BUG:  Wierd glitchiness when firing empty gun and running
//							which is exaggerated by the distance between shots.  Longer
//							times between empty shots seem to cause more noticable
//							glitches.
//
//		03/13/97	JMI	Dude now takes additional burn damage while on fire.
//
//		03/14/97	JMI	Changed the fire angle sway to 20 (was 10).
//
//		03/14/97	JMI	Added very simple jump.
//
//		03/21/97	JMI	Added launching animation.
//
//		03/21/97	JMI	Now deletes launched weapon when launch is interrupted.
//							Also, added a minimum time to the got shot animation.
//
//		03/24/97	JMI	Now utilize new input rotation embedded in UINPUT value.
//
//		03/25/97	JMI	Now uses main_runnogun*.* for run animation.
//
//		03/26/97	JMI	Changed STATUS_FONT_SIZE from 30 to 24.
//
//		03/26/97	JMI	When I added the rotation value built into the UINPUT,
//							and then ignored the rotation value when dying, the net
//							effect was the guy would snap to rotation 0, when dying.
//							Fixed.
//							Also, when I switched to having the guy get damaged
//							more than just once by the same fire, it caused the dude
//							to react to fire even if we was previously dead.  Fixed.
//
//		03/27/97	JMI	Added many animations.
//
//		03/27/97	JMI	Timing for blown up animation was being done through the
//							rigid body transform for some unknown reason.  Fixed.
//
//		03/27/97	JMI	Can no longer move forward when ducking, exploding,
//							or getting up from explosion.
//
//		03/28/97	JMI	Now dude will enter the blown up animation, even if he is
//							dead (returning to the dead anim).
//							Also, now damage anim will loop.
//
//		03/31/97	JMI	Took out INPUT_* macro definitions since input.h now
//							defines them correctly.
//
//		04/01/97	JMI	UINPUT now stores the delta rotation in the first 10 bits
//							as 0..720 representing delta values between -360 and 360.
//
//		04/02/97	JMI	Changed all resource animation names to use the
//							CREATERESNAMES macro that some were already using.
//
//		04/02/97	JMI	OnExplosionMsg() now beefs up his vertical velocity due to
//							the explosion, if a successful state change occurs.
//
//		04/03/97	JMI	Added additional link point for shooting from nealing pos.
//
//		04/03/97	JMI	Changed font color to RGB.  The index is gotten on Init().
//
//		04/03/97	JMI	Hard coded ms_u8FontBackIndex to RSP_BLACK_INDEX.
//
//		04/03/97	JMI	Now that PrepareWeapon() returns the pointer to the new
//							weapon or NULL.  The return value should not be compared to
//							0 for success (now that indicates failure).
//
//		04/03/97	JMI	Now, if the rise state is entered before the ducking anim
//							was finished, he jumps into the rise anim at the inverse of
//							his relative position in the duck anim so he appears to
//							rise back starting at the position he was in during the
//							duck.  It looks suprisingly good thanks to the consistency
//							of the art.
//							Also, if shoot is requested while ducking, the dude auto-
//							magically switches to rising state first.  Firing cannot
//							override rising, so it is not entered until the rise is
//							complete.
//
//		04/07/97	JMI	Added jumps, landings, and fall components.
//
//		04/08/97	JMI	He was not responding to State_Delete.  Now he deletes self
//							at the end of Update().
//
//		04/14/97 BRH	Added CSmash::Item to the type of things that bullets could
//							hit, so he can shoot barrels.
//
//		04/14/97	JMI	Now DrawStatus() will only display <= 100% health.
//
//		04/18/97	JMI	Changed EXPLOSION_VERTICAL_VEL_MULTIPLIER from 1.5 to 1.0
//							which should basically make is vertical explosion velocity
//							the same as all the other characters.
//
//		04/21/97	JMI	Changed EXPLOSION_VERTICAL_VEL_MULTIPLIER from 1.0 to 1.3.
//
//		04/21/97	JMI	Incorporated Mike's new crawler thinger for smooth rubbin'
//							and slidin' up against schtuff.
//							The nubs can be heavily tuned.
//
//		04/21/97	JMI	Added override of WhileBlownUp().
//
//		04/22/97 BRH	In the burning state, he used to check to see when the
//							fire object was dead.  Now since the fire changes to smoke,
//							he was running around on smoke.  So I added a call to
//							IsBurning so that he knows when he can resume normal
//							control.
//
//		04/22/97	JMI	Moved the setup of the font stuff to startup when the
//							palette for the hood should already have been set.  This
//							way we get the correct colors for the DrawStatus().
//
//		04/22/97	JMI	Fixed my use of shadow even though currently that code does
//							not get used.
//							Also, changed STATUS_FONT_SIZE from 24 to 26 which is a
//							currently cached scale for the g_fontBig.
//							Also, disabled jump for current demo for GT.
//
//		04/22/97	JMI	Dude can no longer get up after dying.
//
//		04/23/97	JMI	Now just uses a test version of IsPathClear().
//
//		04/23/97	JMI	Now our bullets can hit Characters, Miscs, Mines,
//							Barrels, and Fire.
//
//		04/23/97	JMI	Disabled IsPathClear() test.
//
//		04/24/97	JMI	Added shotgun weapon components.
//
//		04/25/97	JMI	ShootWeapon() now decrements m_sNumShells.
//							Also, previously when the guy was reacting to being shot,
//							he couldn't die.
//
//		04/25/97	JMI	Added a state for executing dudes.
//
//		04/25/97	JMI	Can no longer move forward or back or turn while executing.
//
//		04/25/97	JMI	Added flame thrower weapon support.  Required fuel ammo
//							info and special case on release of flamage.
//
//		04/25/97	JMI	Removed input for flame thrower for temp even though it will
//							soon rock.
//
//		04/27/97	JMI	Changed link point for execute and re-enabled flame thrower.
//							Also, execute now checks amount of bullets.
//
//		04/28/97	JMI	Removed PrepareWeapon() override since the base class
//							version now handles bullet oriented weapons.
//							Also, moved FireBullets() to base class, CCharacter.
//
//		04/29/97	JMI	Changed CAnim3D::Get()'s over to new version that does not
//							require an array of char pointers.
//							Added m_animPickPut for picking up and putting things down.
//							Also, added mine weapon.  You cannot choose the type now,
//							though (it's the default which is proximity).
//
//		04/29/97	JMI	I was unnecessarily making the dude go into State_Rise
//							after the State_PutDown animation finished.  But, as it
//							turns out, the m_animPickPut already contains a rise
//							which makes things a bit easier.
//
//		04/29/97	JMI	Flame thrower would only work while not moving.  Fixed.
//
//		04/30/97	JMI	Changed Mine enum value to ProximityMine, TimedMine,
//							RemoteMine, and BouncingBettyMine.
//
//		04/30/97	JMI	Changed m_animPickPut to use 'hand' instead of 'guntip'.
//
//		05/01/97	JMI	Now his animation speed for his run is in ratio to
//							RUN_ANIM_VELOCITY and he enters the stand state when his
//							speed falls below MIN_RUN_VEL and there's no user
//							acceleration.
//							Also, fixed some transition bugs involving
//							strafing/standing/shooting to strafing/running/shooting and
//							everything in between.
//
//		05/02/97	JMI	Now plays sample g_smidExecute1 or g_smidExecute2, if
//							he actually hits someone during execute.
//
//		05/02/97	JMI	Uses new inputs where weapons are a value stored in 4 bits.
//
//		05/08/97	JMI	Now shows status change even if the weapon pressed is
//							already the current weapon.
//							Added the NoWeapon weapon.
//
//		05/09/97	JMI	Changed MAX_BULLETS_PER_SEC from 10 to 6.
//							Added flame thrower noises.
//
//		05/12/97 JRD	Added a call to SpewTriggers on dude move to alert Pylons
//
//		05/13/97	JMI	Changed INPUT_WALK (was INPUT_RUN) from functioning as a
//							'fast' key to a 'slow' key.
//							Also, I made the slow key affect the rate the dude turns
//							by dividing the input rotation delta by SLOW_TURN_DIVISOR.
//							Also, added ability to fire the heatseeker.
//
//		05/14/97	JMI	Dude can now pick up and cash in on power ups.
//							Also, added Message() which allows you to add messages
//							to the dude's status display.
//
//		05/22/97	JMI	Added CDudeAnim3D so we could use Get() but have it not
//							load textures (they are loaded separately so the dude
//							color can be changed by the user).
//
//		05/23/97	JMI	Update() was nearly a thousand lines and that was just
//							too damned much.  So I broke it up a bit.
//							Also, changed FireCurrentWeapon() to ArmWeapon() which
//							takes as a parameter the weapon to fire.
//
//		05/23/97	JMI	Changed all the CAnim3D::Get()s to take the new
//							pszEventName parameter.
//
//		05/23/97	JMI	Changed names of loaded textures to "main_color%02d" (was
//							"main_%d".
//
//		05/23/97	JMI	Now loads the "main_missile" event data and uses it to
//							show and release the missile.
//							Consequently, most of the time the missile seems to be in
//							the wrong spot and kills us almost immediately... .
//
//		05/25/97	JMI	Restored main_missile anim to NOT using events b/c those
//							events happend so much earlier than the old FUDGE method,
//							the dude kills himself with the missiles.
//
//		05/26/97	JMI	Now will only repeat shot state every
//							MIN_CANNOT_BE_SHOT_DURATION ms (will groan and get bloody
//							though).
//							Also, will only yell due to being shot or blown up every
//							MIN_BETWEEN_YELLS ms.
//
//		05/26/97	JMI	Added m_sOrigHitPoints so dude can show percentage based
//							on initial hitpoints.
//
//		05/27/97	JMI	Changed 'beers' to 'health'.
//
//		05/29/97	JMI	Made it so the dude can go from blownup state to blownup
//							state repeatedly (not sure why I limited it before).
//							Attempted to make it so the dude does not cry out in
//							pain while blowing up.
//							Added a Revive() function which is the best way to revive
//							a dude that has died.
//							Added a timer for changing weapons that use the same input.
//
//		05/29/97	JMI	Now the INPUT_JUMP doubles as a revive (when he is dead).
//
//		05/29/97	JMI	Fixed comparison on mine delay timer that caused mines to
//							be unselectable.
//
//		05/30/97	JMI	Added SprayCannon weapon.
//
//		05/30/97	JMI	Changed SHELLS_PER_SPRAY_CANNON_FIRE to 4 (was 2) and
//							disabled the flame thrower.
//
//		05/30/97	JMI	Fixed Spray Cannon so it won't fire if there's less than
//							SHELLS_PER_SPRAY_CANNON_FIRE shells.
//
//		06/02/97	JMI	Added climb state.
//
//		06/02/97	JMI	Removed climb state.
//
//		06/03/97	JMI	Changed EXPLOSION_VERTICAL_VEL_MULTIPLIER from 1.3 to 1.0.
//
//		06/03/97	JMI	Got rid of 2d brain splat anim and replaced with CChunk
//							pieces.
//							Also, changed weapon used to shotgun.
//
//		06/03/97	JMI	Changed event name from 'mainmissile' to 'mainevent' and
//							changed the suicide to use the event for when to fire the
//							gun (and splat is brains).  Also, now uses the 'head'
//							transform for the location of the source of the brain
//							splat.
//
//		06/05/97	JMI	Removed m_sHitPoints and m_sNum*.  Now uses CThing3d's
//							m_stockpile instead.
//							Changed EditModify() to bring up the user settings for
//							m_stockpile when the 'StockPile...' button is pressed.
//
//		06/05/97	JMI	Now rotates at a reduced rate when standing still.
//							Also, fixed bug with divisors when input for rotation
//							is nullified.
//
//		06/06/97	JMI	Now utilizes CPowerUps' new CStockPile and GetDescription().
//							Also, reduced status font size so we can see moreat 640x480
//							(from 26 to 15).
//
//		06/07/97	JMI	Now Revive() will try to use a CWarp.
//
//		06/08/97 BRH	Added targeting sprite to the dude and using it
//							when calling IlluminateTarget() to display which enemy
//							or item is being targeted.  Temporarily disabled the
//							targeting aid.  Still need to add a key to toggle
//							m_bTargetingHelpEnabled and the position of the target
//							sprite needs to be adjusted.
//
//		06/09/97 BRH	Adjusted the target position on the child and enabled
//							the targeting help.
//
//		06/09/97	JMI	Now Revive() empties the message queue b/c we don't want
//							him responding to things that happened in his old location
//							(i.e., his location before he warped in).
//							Removed m_inputLast and m_lLastWeaponChangeTime which are
//							no longer necessary now that we can use the auto-repeat
//							feature with the new event driven rspGetKeyStatusArray()
//							used by input.cpp.
//							Added idle animation components.
//
//		06/09/97 BRH	Hid the target when the option is disabled.  Set the
//							default state to disabled.
//
//		06/09/97	JMI	Disabled the SprayCannon.
//
//		06/09/97	JMI	Changed INPUT_WALK to INPUT_RUN.
//
//		06/10/97 BRH	Sends WeaponSelect and WeaponFire messages to CDemon
//							so that comments can be made about these actions.
//
//		06/09/97	JMI	Added weapon selection feedback specific to whether weapons
//							are loaded or empty.
//
//		06/10/97	JMI	Changed SLOW_TURN_DIVISOR from 1.5 to 1.25.
//
//		06/10/97	JMI	Got rid of the SLOW_TURN_DIVISOR and added a second rate
//							inside of input to vary the rate.
//
//		06/10/97	JMI	Now notifies demon of suicides.
//
//		06/11/97	JMI	Commented out code that starts idle animation.
//							Also, made suicide zero your hitpoints.
//
//		06/11/97	JMI	Revive() now drops a powerup before warping dude.
//
//		06/12/97	JMI	Dude now checks to make sure he has the proper weapon to
//							fire the requested ammo.
//							Added handy functions, SetWeapon(), NextWeapon(), and
//							PrevWeapon().
//
//		06/12/97	JMI	Added powerup responses to inputs 10 to 14.
//
//		06/12/97	JMI	Adjusted some of the cheat powerups that were silly, silly
//							amounts.
//
//		06/12/97	JMI	Made launch and throw anims use the events to show and
//							release weapons.
//							Now registers suicide death with Score module (but will
//							probably have to add m_pRealm as a parm once that accepts
//							it).
//							Now napalm launches from the missile launcher.
//							Also, re-enabled idle animation.
//
//		06/12/97	JMI	Added ms_apszAmmoNames to differentiate between weapons
//							and ammo strings.
//
//		06/12/97	JMI	Now DrawStatus() only draws once per StatusChange() call.
//
//		06/13/97	JMI	Now State_ThrowRelease, State_LaunchRelease, and
//							State_PutDown all use WhileHoldingWeapon() to unhide and
//							release the weapon at the appropriate event driven times.
//
//		06/13/97	JMI	Now correctly uses columns and word wrap with its m_print.
//
//		06/14/97	JMI	Now picks up powerups when you run over them.
//
//		06/15/97	JMI	Now minimizes total stockpile after adding in powerups.
//							Now initializes stockpiles since CStockPile no longer has
//							a constructor (I wanted it to be aggregatable).
//
//		06/16/97	JMI	Now plays a sound when his torso hits the ground when dying.
//
//		06/16/97	JMI	The target now alpha blits.
//
//		06/16/97	JMI	Added MessageChange() and m_lMsgUpdateDoneTime.
//
//		06/16/97	JMI	Plays audible feedback via powerup when powerups picked up.
//							Up, up, up, up.
//
//		06/16/97	JMI	Added parms to make ShootWeapon() comply with virtual
//							base class version.
//							Also, fixed bug where, when out of bullets, we went ahead
//							and fired anyways.
//
//		06/16/97	JMI	Now applies resets shot timer when a shot kills us.
//
//					MJR	Changed strafe speed.
//
//					JMI	Now registers death when he enters the State_Dead.
//
//		06/17/97	JMI	Changed the hit the ground noise event val to 2 (was 1).
//
//					JMI	Added PlayStep() which is commented out b/c of sound probs.
//							Added m_u8LastEvent and OnExecute().
//
//					JMI	Added NULL in call to PlaySample() corresponding to new
//							param.
//
//		06/17/97	JMI	Converted all occurrences of rand() to GetRand() and
//							srand() to SeedRand().
//
//		06/24/97	JMI	Now notifies the demon of suicide in SetState() (used to
//							be in OnSuicideMsg() which is not the only way a suicide
//							can be initiated).
//							Also, now SetWeapon() only sends a message to the demon
//							if the new weapon has ammo.
//
//		06/25/97	JMI	Added FindExecutee() which is called to find an executee
//							before entering the execute state.  If none is found, we
//							do not enter the state.  If we do enter the state, while
//							he is leading up to the shooting portion of the animation,
//							he turns toward the executee.  When he fires at the
//							executee, it does not use FireBullets() but instead merely
//							creates a muzzle flare and sound for feedback and sends a
//							message to the executee telling him he's been shot.
//
//		06/25/97	JMI	Now calls PrepareShadow() in Init() which loads and sets up
//							a shadow sprite.
//							Also, now prints 'you are dead' when you are out of
//							hitpoints.
//
//		06/25/97	JMI	Replaced m_lStatusUpdateDoneTime with
//							m_lNextStatusUpdateTime and removed m_bUpdateStatus.
//							Also, changed SHELLS_PER_SPRAY_CANNON_FIRE from 4 to 1.
//							ms_apszAmmoNames[] now contain the sprintf format
//							specifiers to provide greater to control to each type over
//							whether to display a number and/or where to display it.
//							The machine gun no longer uses up bullets.  So, once you
//							have bullets, you never run out.
//							Now hitting the rocket key while the rocket is selected
//							switches to heatseekers and vice-versa.
//							Now switches to the machine gun when you run out of
//							a particular weapon.
//
//		06/26/97	JMI	The 'You are dead' message now flashes.
//							Target now goes away when you are dead.
//							Also, changed 'You are dead' to something else to get
//							Steve inspired enough to think of a cool one.
//
//		06/26/97	JMI	Now handles throw weapons the same way launch weapons are
//							handled when leaving the animation early.  That is, they
//							no longer drop, but, instead, just get deleted via a
//							message.
//
//		06/26/97	JMI	Re-activated foot step sound.
//
//		06/27/97	JMI	If the dude cannot move (i.e., MakeValidPos() fails), he
//							resets his velocity, acceleration, and drag to zero.
//							The only possible problem that I know of with this is that,
//							if there's an external force that's causing him to push up
//							against terrain, even if he is fighting the force by run-
//							ning against it, he won't be able to accelerate until the
//							force dissipates.  Currently, though, all external forces
//							have drag.
//
//		06/28/97	JMI	Now scales the Dude's Z through the realm before calling
//							SpewTriggers().
//
//		06/29/97	JMI	Now uses the new name for the function that scales the Z
//							through the realm (MapZ3DtoY2D (was ScaleZ) ).
//
//		06/30/97 BRH	Cached sound effects during the load so they will be
//							ready for their first use.
//
//		07/01/97	JMI	Changed name of animation file for strafe from 'sidestep'
//							to 'strafe' and strafe and shoot from 'sideshoot' to
//							'strafe'.
//							Added new rigid bodies to CDudeAnim3D and moved its
//							function defs into dude.cpp.
//							Also, added m_idFlagItem.
//
//		07/01/97	JMI	Strafing was not playing anim backwards for left strafe
//							when shooting.
//							Also, a bug caused the strafe to not go backwards for
//							the regular (non-shooting) strafe if m_dRot was less than
//							90.
//
//		07/02/97 BRH	Changed the flamethrower to use CFirestreamID rather than
//							CFireballID.
//
//		07/03/97	JMI	Now uses SetGuiToNotify() to make a button able to end a
//							DoGui() session.
//
//		07/04/97 BRH	Made the Dude drop the flag if he gets blownup, burned or
//							he dies, also it tries to use the current animation's
//							rigid body transform for the release point but if it doesn't
//							have one it will just use the guy's position.
//
//		07/07/97	JMI	DrawStatus() now returns true if it drew to the provide
//							image.
//
//		07/08/97 BRH	Changed some of the animation names that were too long
//							for the delicate MacOS.	  Also changed hand transform
//							and backpack animation names.
//
//		07/09/97	JMI	Changed MAX_STEPUP_THRESHOLD to use CThing3d's macro enum
//							MaxStepUpThreshold.
//
//		07/09/97	JMI	Now uses m_pRealm->Make2dResPath() to get the fullpath
//							for 2D image components.
//
//		07/10/97	JMI	Now uses "lfhand" for main rigid body for pick put anim.
//
//		07/10/97	JMI	Changed names of loaded textures from "main_color%02d" to
//							"main_%d".  Also, now it's zero based instead of 1 based.
//
//		07/12/97	JMI	Now m_pRealm->m_bMultiplayer must be true in order to
//							Revive() a CDude.
//
//		07/13/97	JMI	Set ms_dAccUser and ms_dAccDrag to 1000.0 each so the Dude
//							will start and stop nearly immediately.
//							Attempt to adjust the RUN_ANIM_VELOCITY but got nowhere.
//							Somebody who's better at judging distances should do that.
//
//		07/14/97	JMI	Now applies damange to dude in OnBurnMsg() after doing all
//							other processing.  The problem was that, when he was being
//							damaged before other processing, he would die if the damage
//							was great enough and then the other processing would not
//							be done.
//							Also, only destroys his fire when exiting the burning state
//							to go to other than the die state.
//
//		07/14/97	JMI	Was subtracting from fuel for flamethrower in both
//							ArmWeapon() and ShootWeapon().
//
//		07/15/97	JMI	Moved CDude() and ~CDude() from dude.h to dude.cpp.
//							Also, ShootWeapon() no longer checks for out of fuel con-
//							dition as this is done in ArmWeapon().
//
//		07/15/97	JMI	Now dude will leave portions of powerups that he doesn't
//							need.
//
//		07/15/97	JMI	Added TakePowerup().
//
//		07/16/97	JMI	Made DropPowerUp() return the newly created powerup.
//							Added CreateCheat() which trims the amount the dude cannot
//							use from the stockpile and passes that to DropPowerUp().
//
//		07/16/97	JMI	Changed the order of the weapons.
//
//		07/17/97	JMI	Changed m_psnd to m_siLastPlayInstance.
//							Now uses new SampleMaster interface for volume and play
//							instance reference.
//
//		07/18/97	JMI	Now uses StopLoopingSample() to stop looping the flame-
//							thrower sound (instead of GetInstanceChannel() ).
//
//		07/18/97	JMI	Got rid of bogus immitation PlaySample functions.
//							Now there is one PlaySample() function.  Also, you now
//							MUST specify a category and you don't have to specify a
//							SoundInstance ptr to specify a volume.
//
//		07/18/97	JMI	Now updates font colors based on hood background's palette
//							rather than the RSPiX system palette.
//
//		07/19/97	JMI	In previous fix, forgot to change the increment value for
//							the palette red, green, & blue arrays.
//							Also, now we pass our GUI to the stockpile editor.
//							Also, now Revive() allows revival in single player mode if
//							the dude has health greater than 0.
//							Also, the 'give health' cheat can now be activated while
//							dead.
//
//		07/20/97 BRH	Added a few more sample caches for weapon selection and
//							powerup pickups.
//
//		07/20/97	JMI	Now, if there's nothing the dude needs out of a powerup,
//							he plays a feedback sample and throws the powerup anyways.
//
//		07/20/97	JMI	Spray cannon is now governed by MAX_SPRAYS_PER_SEC (10).
//
//		07/21/97	JMI	Now TakePowerUp() returns the powerup if it persisted.
//							CreateCheat() now immediately hands the created powerup
//							to TakePowerUp() so there's no chance we'll miss it.
//							Also, TakePowerUp() will use the max stockpile for
//							backpacks not only if the dude has a backpack but, also,
//							if the dude will have a backpack (i.e., if there's one in
//							the powerup).
//							Also, added ms_apszWeaponStatusFormats[].
//							Now, if you have ammo but no weapon, it reports that you
//							have ammo but no weapon.  Ingenious.
//
//		07/21/97	JMI	Added OnWeaponDestroyed() override of CCharacter version.
//							Also, Update() now calls CCharacter version.
//							Also, ShootWeapon() now decrements ammo supplies (not
//							ArmWeapon() ).
//							Also, added m_weaponShooting.
//							Removed SHELLS_PER_SPRAY_CANNON_FIRE.
//
//		07/22/97	JMI	Now can never run out of machine gun bullets again.
//
//		07/23/97	JMI	Changed MIN_CANNOT_BE_SHOT_DURATION to 3000 (was 1500) ms.
//
//		07/23/97	JMI	Added DefHasNapalmLauncher enum and added strings to in-
//							corporate new napalm launcher (used to be launched by the
//							missile launcher).
//
//		07/25/97	JMI	Removed PickUp input.
//							Added option to Revive() to allow him to revive in-place
//							(rather than warping in).
//							Added m_bDead and IsDead() which are true when the dude
//							is dead.
//							Now uses m_bDead in most cases that m_state == State_Dead
//							and m_smash.m_bits & CSmash::Dead were used.
//
//		07/25/97	JMI	Added StartAnim() which starts a CAnimThing.
//							Added visual feedback (bullet CChunks that fly off) when
//							wearing the kevlar layers.
//
//		07/27/97	JMI	Now, like the chunks, ricochets appear on the appropriate
//							part of the dude's torso (based on the angle of the bullet).
//							It might make sense to eventually put the height of the shot
//							in the shot message so we can place the splat at the
//							appropriate height giving more feedback as to what's/who's
//							shooting us.  It'd also be neat to adjust the damage based on
//							the vertical position.
//							Also, now StartAnim() does not call CRealm::Make2dResPath()
//							on passed in paths since CAnimThing does this
//							automatically.
//
//		07/28/97	JMI	Now checks m_bDead (instead of m_stockpile.m_sHitPoints) to
//							determine if dude is dead in DrawStatus().
//							Also, only bobbles powerups when alive.
//
//		07/28/97	JMI	Fixed resurection w/o powerup that would occur sometimes.
//							There are only two ways to leave the Die state, BlowUp and
//							Dead.  Sometimes he would get blown up while dying skipping
//							ever getting his m_bDead flag set.  Now the Die state sets
//							this flag as well since we know we want him to die if he
//							enters the die state.
//							Since, once his dead flag is set, he can accept the
//							resurection cheat, there's some misleading feedback in the
//							Die state if that cheat is entered.
//							To avoid this problem we could not set the flag and instead
//							set the persistent state to State_Dead so that when he's
//							done blowing up he'll go to the dead state, but setting the
//							flag seems much more likely to make sure he dies.
//
//		07/29/97	JMI	Added GetCurrentWeapon().
//
//		07/30/97	JMI	Added DeathWad components.
//							Now displays a random message from g_apszDeathMessages[]
//							when dead.
//
//		07/30/97	JMI	Now the death wad cheat enables the death wad option in the
//							editor.
//
//		08/01/97 BRH	CreateCheat() now sends a message to the CDemon to let
//							it know that a cheat code was entered so the Demon can
//							mock the player.  Also added it in the revive case.
//							Also took out loading of .hot files since they aren't used
//							and will soon be deleted from the directories.
//							Changed fire damage to be based on difficulty setting.
//
//		08/04/97	JMI	Now Damage() function will set m_bDead even if
//							SetState(State_Die) fails.  This way, he'll die when he
//							comes out of the explosion state (he cannot go to die from
//							explode (unless explode is done) b/c that would look really
//							wrong).
//
//		08/04/97	JMI	Added array of weapon anims, m_aanimWeapons[], and
//							a sprite for showing the current weapon, m_spriteWeapon.
//							Now changes weapons visually based on current weapon type.
//
//		08/04/97	JMI	Now uses Jeff's rspDegDelta() to better determine which
//							direction to turn when executing.
//
//		08/05/97	JMI	Changed reference of m_pRealm->m_bMultiplayer to
//							m_pRealm->m_flags.bMultiplayer.
//							Changed "rthand" to "gun" for right hand rigid body trans-
//							form.
//
//		08/05/97	JMI	Added an ID to Message().  The idea being that, when the
//							same message ID is specified w/i a certain amount of time,
//							the more recent message is ignored.
//
//		08/06/97	JMI	Now OnExecute(), TrackExecutee(), and FindExecutee() can
//							work with any CThing that defines GetX/Y/Z() (not just
//							CThing3d's).
//
//		08/07/97	JMI	Added DoubleBarrel components.
//
//		08/07/97	JMI	Forgot a break in switch in GetWeaponInfo().
//
//		08/07/97	JMI	Added m_animBackpack and m_spriteBackpack for his backpack
//							when he has it.
//
//		08/07/97	JMI	Removed commented out hot channel stuff from CDudeAnim3D.
//							Added release of backpack anim in FreeResources().
//
//		08/08/97	JMI	Now that CCharacter handles flamer noise, this class now
//							longer has to (and doesn't).
//
//		08/08/97	JMI	Added TossPowerUp().
//							Also, changed TakePowerup() to TakePowerUp().
//							Now, when he enters the dead state in multiplayer mode,
//							he drops his schtuff.
//
//		08/08/97	JMI	Majorly condensed code segments for detaching flag by
//							calling DetachChild() which does most of what we were doing
//							here.  Also, removed some conditions that were checking to
//							make sure m_panimCur and m_panimCur->m_ptransRigid were
//							valid but they were not very useful since if m_panimCur were
//							NULL we'd be fucked AND m_ptransRigid was not the
//							rigid body transforms being used for the flag anyways (we're
//							using m_ptransLeft).
//
//		08/09/97	JMI	Draws dead frame into background just before reviving.
//
//		08/10/97	JMI	Added StrafeLeft and StrafeRight inputs.
//							Also, changed Jump to Revive and INPUT_JUMP to INPUT_REVIVE.
//
//		08/10/97	JMI	Changed doubles dDistX and Z to shorts sDistX and Z.  The
//							compiler seemed to lock up when inlining was enabled and we
//							passed these two doubles to rspATan.  With a different
//							function it worked fine and with one of the vars as a short
//							it worked fine.  /shrug  Looking for a VC patch.
//
//		08/10/97	JMI	Added m_dLastCrawledToPosX, Z which are the last position
//							successfully crawled to by the crawler (except on the
//							first iteration where they are simply set to the dude's
//							starting point).  In TRACENASSERT mode, an ASSERT is
//							generated if the position gets modified by other than the
//							crawler.  In release mode, it sets him back to the last
//							successful crawled position.
//							Added a function to set m_dX, Y, & Z, SetPosition().
//
//		08/12/97 BRH	Added CSmash::Ducking bits to the smash when he is
//							ducking down.  When he is ducking, he won't get hit
//							by the missiles.
//
//		08/12/97	JMI	Removed unused anims: m_animJump, JumpForward, Land,
//							LandForward, Fall.
//
//		08/13/97	JMI	Temporarily shows particle effects regardless of user
//							setting in multiplayer mode.
//
//		08/14/97	JMI	Switched references to g_GameSettings.m_sDifficulty to
//							m_pRealm->m_flags.sDifficulty.
//
//		08/14/97 BRH	Added static collision bits to be passed as the default
//							bits for weapons that the dude shoots.  The default
//							arguments in the ShootWeapon are unreliable and may
//							get set to the bits of the base class default args.
//							Also changed call of WhileHoldingWeapon to include
//							these default collision bits.
//
//		08/15/97 BRH	Set the XrayAll flag when the Dude is dead, so if he
//							dies behind opaque, you will be able to see that he is
//							dead.
//
//		08/17/97	JMI	Changed to only find writhers as executees and will
//							definitely hit the executee.
//
//		08/17/97	JMI	Removed all occurrences of the SetXRayAll() call as this
//							would not be a good place to do that since this dude is
//							not necessarily the local dude.  Soon we will change Play
//							to do it since it knows that.
//
//		08/17/97	JMI	Got rid of m_szMessages and all message related functions
//							and variables from CDude since we are now using the toolbar
//							for dude status feedback to the user.  This includes:
//							MsgTypeInfo, m_lNextStatusUpdateTime, m_lMsgUpdateDoneTime,
//							m_print, m_bClearedStatus, m_szMessages[], m_sDeadMsgNum,
//							ms_amtfMessages[], ms_u8FontForeIndex, ms_u8FontBackIndex,
//							ms_u8FontShadowIndex, DrawStatus(), StatusChange(),
//							MessageChange(), Message(), UpdateFontColors(),
//							CPowerUp::ms_apszPowerUpTypeNames[],
//							CPowerUp::GetDescription(), and some strings and a string
//							array in localize.*.
//
//		08/18/97	JMI	Now applies no randomization to CChunks and instead allows
//							CChunk::Setup() to apply its own randomization based on
//							sway values passed to it.  Also, CChunk::Construct() will
//							now fail if particles are disabled so we don't need to
//							check for that anymore.
//
//		08/18/97	JMI	Moved StartAnim() from CDude to CThing3d so more things
//							could use it.
//
//		08/18/97	JMI	Added m_bInvincible which gets set via cheat.
//							Added many cheats and revamped originals.  There is one
//							left.
//
//		08/18/97	JMI	Added cheat code for sales people.
//
//		08/18/97	JMI	Changed Revive() to call DeadRender3D() (which used to be
//							known/called as just another Render() overload).
//
//		08/19/97	JMI	No longer shoots CSmash::Misc items.
//
//		08/24/97 BRH	Forwards the burn message to the child flag so that it
//							can react.  Before he was dropping the flag when blown up
//							or burned, but only telling the flag when it was blown up,
//							so if you got burned while holding the flag, the flag was
//							in the wrong state and never looked for anyone to pick it
//							up again.  So the flag would be stuck in place and you could
//							not grab it again.
//
//		08/24/97	JMI	Now instead of setting his brightness to zero in the dead
//							state, he clears it in Revive().  This way he stays charred
//							even when laying there dead or being blown up but cannot
//							come back to life charred.
//
//		08/24/97	JMI	Now INPUT_WEAPON_0 is no weapon and INPUT_WEAPON_10 is
//							mines.
//
//		08/28/97 BRH	Moved the caching of samples to Init() now that the
//							Load function is not being called now that the warps
//							create a dude.  Also added OnPutMeDowmMsg handler function
//							that the flag sends when it wants to be set down (on the
//							flagbase).
//
//		08/30/97	JMI	Removed m_idFlagItem (now uses the sprite list and, for
//							flag child sprites, updates them all).
//
//		09/01/97	JMI	Now ArmWeapon() sets m_weaponShooting to NoWeapon if it
//							fails to arm a weapon (either b/c of no ammo or the shoot
//							state is not attainable).
//
//		09/01/97	JMI	Previous changed was hosing the shotgun so I made it a
//							little less strict.
//
//		09/02/97	JMI	Now targets CSmash::Sentry in ms_u32CollideBitsInclude.
//
//		09/07/97 MJR	Fixed bug in DropAllFlags() that didn't read the message
//							properly.
//
//		09/08/97	JMI	Added Kevlar type for pieces of kevlar vest that
//							splatter off of dudes with vest.
//
//		11/21/97	JMI	Now varies whether CSmash::Good is a desirable target to
//							our weapons based on the realm's multiplayer cooperative
//							flag.
//							Removed version of ShootWeapon() that took 3 default
//							parameters and replaced it with a version that takes no
//							parameters so we could control the defaults from within the
//							CPP.
//							Also, now sets CWeapons' detection bits explicitly via
//							SetDetectionBits() to NOT seek other players in Cooperative
//							multiplayer mode.
//
//		12/08/97	JMI	Added an option for DropPowerUp() to only drop the
//							currently selected weapon.
//							Also, now sets m_sHitPoints to zero before calling
//							SetState(State_Dead) which was previously generating a
//							health powerup if m_sHitPoints was greater than zero which
//							was the case for suicide.
//
//		12/09/97	JMI	Now drops all weapons when reviving in single player mode.
//							In general, drops all weapons when reviving at location of
//							death and drops only the current weapon when reviving via
//							a warp (the warp gives you additional stuff).
//
////////////////////////////////////////////////////////////////////////////////
#define DUDE_CPP

#include "RSPiX.h"
#include <math.h>

#include "dude.h"
#include "grenade.h"
#include "game.h"
#include "SampleMaster.h"
#include "input.h"
#include "AnimThing.h"
#include "reality.h"
#include "fire.h"
#include "TriggerRegions.h"
#include "PowerUp.h"
#include "chunk.h"
#include "warp.h"
#include "score.h"
#include "CompileOptions.h" // For sales cheat.
#include "play.h"

bool demoCompat = false; //Set in Play.cpp

#if defined(ALLOW_TWINSTICK)
void GetDudeVelocity(double *d_Velocity, double *d_Angle);
extern bool GetDudeFireAngle(double *d_Angle);
#endif

#include "pvr.h"

extern PVR::cRenderMgr *g_pvrRenderer;
extern cMapleController *rspGetJoy(int16_t sJoy);

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

// Determines the number of elements in the passed array at compile time.
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))

// Tunable bullet parameters.
#define MAX_BULLETS_PER_SEC 6

#define MS_BETWEEN_BULLETS (1000 / MAX_BULLETS_PER_SEC)

#define MAX_SPRAYS_PER_SEC 10

#define MS_BETWEEN_SPRAYS (1000 / MAX_SPRAYS_PER_SEC)

#define MAX_FLAMES_PER_SEC 1000

#define MS_BETWEEN_FLAMETHROWS (1000 / MAX_FLAMES_PER_SEC)

#define MODIFY_GUI_FILE "res/editor/EditDude.gui"

// Random amount the fire angle can adjust.
#define FIRE_ANGLE_Y_SWAY 15
#define FIRE_ANGLE_Z_SWAY 10

// Maximum the guy can tweak your angle while you're on fire.
#define ON_FIRE_ROT_TWEAKAGE 22
#define ON_FIRE_VEL_TWEAKAGE ((short)ms_dMaxVelForeFast)

// Amount per damage pt that translate to velocity.
#define VEL_PER_DAMAGE 0.5

// Gets a random between -range / 2 and range / 2.
#define RAND_SWAY(sway) ((GetRand() % sway) - sway / 2)

// Velocity for strafing.
#define STRAFE_VEL 60.0

#define EXPLOSION_VERTICAL_VEL_MULTIPLIER 1.0

// Amount of time to suffer from shot.
#define MIN_SHOT_DURATION 500

// Amount of time before we can suffer another shot.
#define MIN_CANNOT_BE_SHOT_DURATION 3000

// Minimum duration between yells (for blown up and shot).
#define MIN_BETWEEN_YELLS (750 + (16 - g_GameSettings.m_sPainFrequency) * 125) // Current yell ('groan_male1.wav') is 640 ms.

// IDs for GUIs.
#define GUI_ID_STOCKPILE 3

// The color range that is the same on all hoods.
#define CONSTANT_COLOR_START_INDEX 95
#define NUM_CONSTANT_COLOR_INDICES (256 - CONSTANT_COLOR_START_INDEX)

// Size of font used for status updates.
#define STATUS_FONT_SIZE 15
// 0 for any of these colors is transparent.
#define STATUS_FONT_FORE_RED 128
#define STATUS_FONT_FORE_GREEN 0
#define STATUS_FONT_FORE_BLUE 0
#define STATUS_FONT_BACK_RED 0
#define STATUS_FONT_BACK_GREEN 0
#define STATUS_FONT_BACK_BLUE 0
#define STATUS_FONT_SHADOW_RED 127
#define STATUS_FONT_SHADOW_GREEN 127
#define STATUS_FONT_SHADOW_BLUE 127

#define STATUS_PRINT_X 0
#define STATUS_PRINT_Y 0

#define STATUS_UPDATE_DURATION 5000	 // In ms.
#define MESSAGE_UPDATE_DURATION 3000 // In ms.

#define SHADOW_DEPTH_X 1
#define SHADOW_DEPTH_Y 1

// Rate at which run animation was made to travel in pixels per second.
// This is used to compute a ratio to the current speed.  The resulting
// ratio is used to tune the animation rate dynamically.
#define RUN_ANIM_VELOCITY 85.0 // Pix/Sec.

// When below this speed, the dude will not be in his running anim.
#define MIN_RUN_VEL 40.0 // Pix/Sec.

// Maximum springiness for crawler.
#define MAX_CRAWLER_PUSH_X 1.5
#define MAX_CRAWLER_PUSH_Z 1.5

// Time between cycling weapons that are chosen using the same input.
#define WEAPON_CYCLE_TIME 250

// Ladder tolerance.
#define LADDER_DIR_TOLERANCE 20 // +/- this amount in degrees.

#define BRAIN_SPLAT_NUM_CHUNKS 200
#define BRAIN_SPLAT_SWAY 30

// Reduced rate for standing still rotation.
#define STILL_TURN_DIVISOR 2.0

// Handy macro to define char* array with one 'base' name.
#define CREATERESNAMES(var, base, rigid)          \
	static char *var[] =                          \
		{                                         \
			"3d/main_" #base ".sop",              \
			"3d/main_" #base ".mesh",             \
			"3d/main_" #base ".tex",              \
			"3d/main_" #base ".hot",              \
			"3d/main_" #base ".bounds",           \
			"3d/main_" #base ".floor",            \
			"3d/main_" #base "_" #rigid ".trans", \
			NULL, /* Safety */                    \
	};

//#define TARGETING_FILE "target.img"
#define TARGETING_FILE "target.bmp"

// IDLE_ANIM_TIMEOUT is NOT evaluated every frame.
#define IDLE_ANIM_TIMEOUT (5000 + (GetRand() % 5000))
#define IDLE_ANIM_LOOPS 1

// This is multiplied our current number of kevlar layers and the
// result is divided into the bullet damage to produce the damage
// the dude receives.
#define KEVLAR_PROTECTION_MULTIPLIER 3

// Alpha level used when blit'ing the target crosshairs.
#define TARGET_ALPHA_LEVEL 100

// Max distance to writher when executing.
#define EXECUTE_RADIUS 50

// Time between constant status updates in milliseconds.
#define STATUS_UPDATE_DELAY 500 // In ms.
// Max lag time between a known status change and the next update.
#define MAX_STATUS_LAG 250 // In ms.

// These may not be correct for now, but just to get them up and running:
// These are the names of the rigid body transforms that are loaded for
// every dude animation.
#define LEFT_HAND_RIGID_ANIM_NAME "lfhand"
#define RIGHT_HAND_RIGID_ANIM_NAME "gun"
#define BACK_RIGID_ANIM_NAME "backpack"

// This value indicates no strafe motion and must be a totally invalid
// strafe angle value.
#define INVALID_STRAFE_ANGLE 0x7fff

#define VEST_HIT_SWAY 10

#define VEST_HIT_RES_NAME "Ricochet.aan"

// This is the distance in realm units from the dude's center
// to the outside of his jacket.  This is used to adjust things
// like ricochets off of his kevlar vest around the outside of
// his torso.
#define TORSO_RADIUS 5

// The minimum square distance the dude wants to be back from
// his target point when executing.
#define MIN_SQR_DISTANCE_TO_EXECUTEE 250.0

// The backpack animation basename.
#define BACKPACK_RES_NAME "3d/backpack"

#define COLLISION_BITS_INCLUDE (ms_u32CollideBitsInclude)
#define COLLISION_BITS_DONTCARE (ms_u32CollideBitsDontcare | (m_pRealm->m_flags.bCoopMode ? 0 : CSmash::Good))
#define COLLISION_BITS_EXCLUDE (ms_u32CollideBitsExclude | (m_pRealm->m_flags.bCoopMode ? CSmash::Good : 0))

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

// These are default values -- actually values are set using the editor!
double CDude::ms_dAccUser = 1000.0; //150.0;			// Acceleration due to user
double CDude::ms_dAccDrag = 1000.0; //110.0;			// Acceleration due to drag

double CDude::ms_dMaxVelFore = 85.0;	  // Maximum forward velocity
double CDude::ms_dMaxVelBack = -60.0;	  // Maximum backward velocity
double CDude::ms_dMaxVelForeFast = 120.0; // Maximum forward velocity
double CDude::ms_dMaxVelBackFast = -90.0; // Maximum backward velocity

double CDude::ms_dDegPerSec = 240.0; // Degrees of rotation per second

double CDude::ms_dVertVelJump = 100.0; // Velocity of jump.

U32 CDude::ms_u32CollideBitsInclude = CSmash::Character | CSmash::Barrel | CSmash::SpecialBarrel | CSmash::Sentry;
U32 CDude::ms_u32CollideBitsDontcare = CSmash::Bad;
U32 CDude::ms_u32CollideBitsExclude = 0;

// Let this auto-init to 0
int16_t CDude::ms_sFileCount;

// Weapon details database.
CDude::WeaponDetails CDude::ms_awdWeapons[NumWeaponTypes] =
	{
		// NoWeapon.
		{
			"No Weapon",	// Weapon name.
			"bare hands",	// Ammo name.
			"   No Weapon", // Status format.
			NULL,			// Weapon resource name.
			1,				// Min ammo required.
		},

		// SemiAutomatic.
		{
			"Machine Gun",	  // Weapon name.
			"Bullets",		  // Ammo name.
			"   Machine Gun", // Status format.
			"3d/machinegun",  // Weapon resource name.
			1,				  // Min ammo required.
		},

		// ShotGun.
		{
			"Shotgun",				   // Weapon name.
			"Shells",				   // Ammo name.
			"   Shotgun -- %d Shells", // Status format.
			"3d/shotgun",			   // Weapon resource name.
			1,						   // Min ammo required.
		},

		// SprayCannon.
		{
			"Spray Cannon",					// Weapon name.
			"Shells",						// Ammo name.
			"   Spray Cannon -- %d Shells", // Status format.
			"3d/spraygun",					// Weapon resource name.
			1,								// Min ammo required.
		},

		// Grenade.
		{
			"Grenades",		  // Weapon name.
			"Grenades",		  // Ammo name.
			"   %d Grenades", // Status format.
			NULL,			  // Weapon resource name.
			1,				  // Min ammo required.
		},

		// Rocket.
		{
			"Missile Launcher",					  // Weapon name.
			"Missiles",							  // Ammo name.
			"   Missile Launcher -- %d Missiles", // Status format.
			"3d/launcher",						  // Weapon resource name.
			1,									  // Min ammo required.
		},

		// Heatseeker.
		{
			"Missile Launcher",						 // Weapon name.
			"Heatseekers",							 // Ammo name.
			"   Missile Launcher -- %d Heatseekers", // Status format.
			"3d/launcher",							 // Weapon resource name.
			1,										 // Min ammo required.
		},

		// FireBomb.
		{
			"Cocktails",	   // Weapon name.
			"Cocktails",	   // Ammo name.
			"   %d Cocktails", // Status format.
			NULL,			   // Weapon resource name.
			1,				   // Min ammo required.
		},

		// Napalm.
		{
			"Napalm Launcher",					  // Weapon name.
			"Napalm Canisters",					  // Ammo name.
			"   Napalm Launcher -- %d Canisters", // Status format.
			"3d/napalmer",						  // Weapon resource name.
			1,									  // Min ammo required.
		},

		// Fuel.
		{
			"Flame Thrower",				  // Weapon name.
			"Fuel Canisters",				  // Ammo name.
			"   Flamer -- %d Fuel Canisters", // Status format.
			"3d/flmthrower",				  // Weapon resource name.
			1,								  // Min ammo required.
		},

		// BouncingBettyMine.
		{
			"Bouncing Bettys",		 // Weapon name.
			"Bouncing Bettys",		 // Ammo name.
			"   %d Bouncing Bettys", // Status format.
			NULL,					 // Weapon resource name.
			1,						 // Min ammo required.
		},

		// ProximityMine.
		{
			"Proximity Mines",		 // Weapon name.
			"Proximity Mines",		 // Ammo name.
			"   %d Proximity Mines", // Status format.
			NULL,					 // Weapon resource name.
			1,						 // Min ammo required.
		},

		// TimedMine.
		{
			"Timed Mines",		 // Weapon name.
			"Timed Mines",		 // Ammo name.
			"   %d Timed Mines", // Status format.
			NULL,				 // Weapon resource name.
			1,					 // Min ammo required.
		},

		// RemoteMine.
		{
			"Remote Mines",		  // Weapon name.
			"Remote Mines",		  // Ammo name.
			"   %d Remote Mines", // Status format.
			NULL,				  // Weapon resource name.
			1,					  // Min ammo required.
		},



		// DeathWad.
		{
			"Wad Launcher",																  // Weapon name.
			"Wads",																		  // Ammo name.
			"   Wad -- %d missiles, %d napalm canisters, %d fuel canisters, %d grenades", // Status format.
			"3d/napalmer",																  // Weapon resource name.
			1,																			  // Min ammo required.
		},

		// DoubleBarrel.
		{
			"Double Barrel",				 // Weapon name.
			"Shells",						 // Ammo name.
			"   Double Barrel -- %d Shells", // Status format.
			"3d/shotgun",					 // Weapon resource name.
			2,								 // Min ammo required.
		},
};

// Dude's default stockpile.
CStockPile CDude::ms_stockpileDefault =
	{
		DefHitPoints, // m_sHitPoints

		DefNumGrenades,	   // m_sNumGrenades
		DefNumFireBombs,   // m_sNumFireBombs
		DefNumMissiles,	   // m_sNumMissiles
		DefNumNapalms,	   // m_sNumNapalms
		DefNumBullets,	   // m_sNumBullets
		DefNumShells,	   // m_sNumShells
		DefNumFuel,		   // m_sNumFuel
		DefNumMines,	   // m_sNumMines
		DefNumHeatseekers, // m_sNumHeatseekers

		DefHasMachineGun,		// m_sMachineGun
		DefHasLauncher,			// m_sMissileLauncher
		DefHasShotgun,			// m_sShotGun
		DefHasSprayCannon,		// m_sSprayCannon
		DefHasFlamer,			// m_sFlameThrower
		DefHasNapalmLauncher,	// m_sNapalmLauncher.
		DefHasDeathWadLauncher, // m_sDeathWadLauncher.
		DefHasDoubleBarrel,		// m_sDoubleBarrel.

		DefKevlarLayers, // m_sKevlarLayers

		DefHasBackpack, // m_sBackpack
};

// Nubs (a CCrawler tool).  These are the realtive points on the X/Z plane that
// define the hard and soft 'springs' that deflect the dude from terrain
// obstacles.
static CCrawler::Nub ms_anubs[] =
	{
		// Hard nubs
		{8, 0, 1, 180, 1.0},
		{6, -6, 1, 225, 1.0},
		{0, -8, 1, 270, 1.0},
		{-6, -6, 1, 315, 1.0},
		{-8, 0, 1, 0, 1.0},
		{-6, 6, 1, 45, 1.0},
		{0, 8, 1, 90, 1.0},
		{6, 6, 1, 135, 1.0},
		// Soft nubs
		{12, 0, 0, 180, 1.0},
		{9, -9, 0, 225, 1.0},
		{0, -12, 0, 270, 1.0},
		{-9, -9, 0, 315, 1.0},
		{-12, 0, 0, 0, 1.0},
		{-9, 9, 0, 45, 1.0},
		{0, 12, 0, 90, 1.0},
		{9, 9, 0, 135, 1.0}};

////////////////////////////////////////////////////////////////////////////////
// Get the various components of this animation from the resource names
// specified in the provided array of pointers to strings.
////////////////////////////////////////////////////////////////////////////////
// virtual								// Overridden here.
int16_t CDude::CDudeAnim3D::Get( // Returns 0 on success.
	char *pszBaseFileName,		 // In:  Base string for resource filenames.
	char *pszRigidName,			 // In:  String to add for rigid transform channel
								 // or NULL for none.
	char *pszEventName,			 // In:  String to add for event states channel
								 // or NULL for none.
	int16_t sLoopFlags)			 // In:  Looping flags to apply to all channels
								 // in this anim.
{
	int16_t sRes;
	char szResName[RSP_MAX_PATH];
	sprintf(szResName, "%s.sop", pszBaseFileName);
	sRes = rspGetResource(&g_resmgrGame, szResName, &m_psops);
	sprintf(szResName, "%s.mesh", pszBaseFileName);
	sRes |= rspGetResource(&g_resmgrGame, szResName, &m_pmeshes);
	sprintf(szResName, "%s.bounds", pszBaseFileName);
	sRes |= rspGetResource(&g_resmgrGame, szResName, &m_pbounds);
	if (pszRigidName != NULL)
	{
		sprintf(szResName, "%s_%s.trans", pszBaseFileName, pszRigidName);
		sRes |= rspGetResource(&g_resmgrGame, szResName, &m_ptransRigid);
	}

	if (pszEventName != NULL)
	{
		sprintf(szResName, "%s_%s.event", pszBaseFileName, pszEventName);
		sRes |= rspGetResource(&g_resmgrGame, szResName, &m_pevent);
	}

	// We always load these transforms.
	sprintf(szResName, "%s_" LEFT_HAND_RIGID_ANIM_NAME ".trans", pszBaseFileName);
	sRes |= rspGetResource(&g_resmgrGame, szResName, &m_ptransLeft);
	sprintf(szResName, "%s_" RIGHT_HAND_RIGID_ANIM_NAME ".trans", pszBaseFileName);
	sRes |= rspGetResource(&g_resmgrGame, szResName, &m_ptransRight);
	sprintf(szResName, "%s_" BACK_RIGID_ANIM_NAME ".trans", pszBaseFileName);
	sRes |= rspGetResource(&g_resmgrGame, szResName, &m_ptransBack);

	// If successful . . .
	if (sRes == 0)
	{
		m_psops->SetLooping(sLoopFlags);
		m_pmeshes->SetLooping(sLoopFlags);
		m_pbounds->SetLooping(sLoopFlags);
		if (m_ptransRigid != NULL)
			m_ptransRigid->SetLooping(sLoopFlags);
		if (m_pevent != NULL)
			m_pevent->SetLooping(sLoopFlags);

		m_ptransLeft->SetLooping(sLoopFlags);
		m_ptransRight->SetLooping(sLoopFlags);
		m_ptransBack->SetLooping(sLoopFlags);
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
// Release all resources.
////////////////////////////////////////////////////////////////////////////////
// virtual										// Overridden here.
void CDude::CDudeAnim3D::Release(bool purge) // Returns nothing.
{
	if (purge)
	{
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_psops);
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_pmeshes);
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_pbounds);
		if (m_ptransRigid != NULL)
			rspReleaseAndPurgeResource(&g_resmgrGame, &m_ptransRigid);
		if (m_pevent != NULL)
			rspReleaseAndPurgeResource(&g_resmgrGame, &m_pevent);

		rspReleaseAndPurgeResource(&g_resmgrGame, &m_ptransLeft);
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_ptransRight);
		rspReleaseAndPurgeResource(&g_resmgrGame, &m_ptransBack);
	}
	else
	{
		rspReleaseResource(&g_resmgrGame, &m_psops);
		rspReleaseResource(&g_resmgrGame, &m_pmeshes);
		rspReleaseResource(&g_resmgrGame, &m_pbounds);
		if (m_ptransRigid != NULL)
			rspReleaseResource(&g_resmgrGame, &m_ptransRigid);
		if (m_pevent != NULL)
			rspReleaseResource(&g_resmgrGame, &m_pevent);

		rspReleaseResource(&g_resmgrGame, &m_ptransLeft);
		rspReleaseResource(&g_resmgrGame, &m_ptransRight);
		rspReleaseResource(&g_resmgrGame, &m_ptransBack);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Constructor.
////////////////////////////////////////////////////////////////////////////////
CDude::CDude(CRealm *pRealm)
	: CCharacter(pRealm, CDudeID)
{
	m_statePersistent = State_Stand;

	// Set default stockpile.
	m_stockpile.Copy(&ms_stockpileDefault);

	m_weapontypeCur = SemiAutomatic;
	m_weaponShooting = NoWeapon;

	m_lLastShotTime = 0;
	m_lLastYellTime = 0;

	m_u16IdChild = CIdBank::IdNil;

	m_sTextureIndex = 0;

	m_u8LastEvent = 0;

	m_idVictim = CIdBank::IdNil;

	m_idReviver = CIdBank::IdNil;

	m_bDead = false;
	m_bDeathBySuicide = false;

	m_bInvincible = false;

	// Base the dude number of the number of dude's in the realm.  Note that
	// this number already includes this dude, so we subtract 1 from so the
	// assigned dude numbers will start at 0.
	ASSERT(m_pRealm->m_asClassNumThings[CDudeID] > 0);
	m_sDudeNum = m_pRealm->m_asClassNumThings[CDudeID] - 1;
	
	pTargetThing = NULL;
	pLockedTargetThing = NULL;
	m_bLockedAim = false;
	m_bFixedAiming = false;		
	m_pController = NULL;
	m_pCamera = NULL;
	m_bIsRevivee = false;	
	m_bOffscreenMovement = false;
}

////////////////////////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////////////////////////
CDude::~CDude()
{
	// Kill dude
	Kill();
}

////////////////////////////////////////////////////////////////////////////////
// Load object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::Load(		// Returns 0 if successfull, non-zero otherwise
	RFile *pFile,			// In:  File to load from
	bool bEditMode,			// In:  True for edit mode, false otherwise
	int16_t sFileCount,		// In:  File count (unique per file, never 0)
	uint32_t ulFileVersion) // In:  File version being loaded.
{
	int16_t sResult = 0;

	// In most cases, the base class Load() should be called.
	sResult = CCharacter::Load(pFile, bEditMode, sFileCount, ulFileVersion);
	if (sResult == 0)
	{
		// Load common data just once per file (not with each object)
		if (ms_sFileCount != sFileCount)
		{
			ms_sFileCount = sFileCount;

			switch (ulFileVersion)
			{
			default: // Newer versions where this format has not changed.
			case 1:
				// Load static data
				pFile->Read(&ms_dAccUser);
				pFile->Read(&ms_dAccDrag);
				pFile->Read(&ms_dMaxVelFore);
				pFile->Read(&ms_dMaxVelBack);
				pFile->Read(&ms_dMaxVelForeFast);
				pFile->Read(&ms_dMaxVelBackFast);
				pFile->Read(&ms_dDegPerSec);
				break;
			}
		}

		switch (ulFileVersion)
		{
		default: // Newer versions where this format has not changed.
		case 16:
			// Nothing to load here.
			break;
		case 15:
		case 14:
		case 13:
			pFile->Read(&m_stockpile.m_sNumHeatseekers);
			pFile->Read(&m_stockpile.m_sNumMines);
		case 12:
		case 11:
		case 10:
			pFile->Read(&m_stockpile.m_sNumFuel);
		case 9:
			pFile->Read(&m_stockpile.m_sNumShells);
		case 8:
		case 7:
		case 6:
		case 5:
		case 4:
		case 3:
		case 2:
			pFile->Read(&m_stockpile.m_sNumBullets);
			pFile->Read(&m_stockpile.m_sNumFireBombs);
			pFile->Read(&m_stockpile.m_sNumMissiles);
			pFile->Read(&m_stockpile.m_sNumNapalms);
		case 1:
		{
			pFile->Read(&m_stockpile.m_sNumGrenades);

			U32 u32Temp;
			pFile->Read(&u32Temp);
			m_state = (CCharacter::State)u32Temp;

			break;
		}
		}

		// Make sure there were no file errors or format errors . . .
		if (!pFile->Error() && sResult == 0)
		{
			// Init dude
			sResult = Init();
		}
		else
		{
			sResult = -1;
			TRACE("CDude::Load(): Error reading from file!\n");
		}
	}
	else
	{
		TRACE("CDude::Load(): CCharacter::Load() failed.\n");
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Startup object
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::Startup(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = CCharacter::Startup();

	// Init other stuff
	m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + MS_BETWEEN_BULLETS;

	// Set start position for crawler verification.
	m_dLastCrawledToPosX = m_dX;
	m_dLastCrawledToPosZ = m_dZ;

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Shutdown object
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = CCharacter::Shutdown();
	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Suspend object
////////////////////////////////////////////////////////////////////////////////
void CDude::Suspend(void)
{
	if (m_sSuspend == 0)
	{
		// Store current delta so we can restore it.
		int32_t lCurTime = m_pRealm->m_time.GetGameTime();
		m_lNextBulletTime = lCurTime - m_lNextBulletTime;
	}

	CCharacter::Suspend();
}

////////////////////////////////////////////////////////////////////////////////
// Resume object
////////////////////////////////////////////////////////////////////////////////
void CDude::Resume(void)
{
	CCharacter::Resume();

	if (m_sSuspend == 0)
	{
		int32_t lCurTime = m_pRealm->m_time.GetGameTime();
		m_lNextBulletTime = lCurTime - m_lNextBulletTime;
	}
}

void CDude::ProcessMessages(void)
{
	GameMessage	msg;	
	while (m_MessageQueue.DeQ(&msg) == true)
	{
		ProcessMessage(&msg);
		if (msg.msg_Revive.eType == typeRevive)
		{			
			Revive(false);
		}
	}	
}


////////////////////////////////////////////////////////////////////////////////
// Update object
////////////////////////////////////////////////////////////////////////////////
void CDude::Update(void)
{
	if (!m_sSuspend)
	{
		// Get new time
		int32_t lThisTime = m_pRealm->m_time.GetGameTime();

		// Advance the animation timer.
		int32_t lDifTime = lThisTime - m_lAnimPrevUpdateTime;
		m_lAnimTime += lDifTime;

		// Update prev time.
		m_lAnimPrevUpdateTime = lThisTime;

		// Process Input ////////////////////////////////////////////////////////

		double dMaxForeVel;
		double dMaxBackVel;
		int16_t sStrafeAngle = INVALID_STRAFE_ANGLE; // Angle of motion from strafing.	
						 			
		ProcessInput(
			&dMaxForeVel,
			&dMaxBackVel,
			&sStrafeAngle);

		// Process Forces ///////////////////////////////////////////////////////

		switch (m_state)
		{
		case State_BlownUp:
			break;
		default:
			ProcessForces(
				lThisTime,
				dMaxForeVel,
				dMaxBackVel,
				sStrafeAngle);
			break;
		}
		
		if (g_sActivePlayers > PLAYER_1)
		{			
			int16_t sMinX;
			int16_t sMinY;
			int16_t sMaxX;
			int16_t sMaxY;

			int16_t sX = int16_t(m_dX);
			int16_t sY = int16_t(m_dZ);
			
			m_pRealm->Map3Dto2D(m_dX, m_dY, m_dZ, &sX, &sY);

			sX -= m_pCamera->m_sScene2FilmX;
			sY -= m_pCamera->m_sScene2FilmY;

			if (g_GameSettings.m_cHudMode == 1)
			{
				sMinX = 0;
				sMaxX = 640;
				sMinY = 0;
				sMaxY = 480;
			}
			else
			{
				sMinX = 10;
				sMaxX = 630;
				sMinY = 60;
				sMaxY = 430;
			}
						
			if (sX > sMinX && sY > sMinY && sX < sMaxX && sY <sMaxY)
			{
				m_bOffscreenMovement = false;
				m_spritePrompt.m_pszText = NULL;
			}
			else
			{
				m_bOffscreenMovement = true;				
				
				if(!m_bDead && !m_pRealm->IsSuspended())
				{
					SetTextToCoatColour(&m_spritePrompt);
					m_spritePrompt.m_sInFlags &= ~CSprite::InHidden;
					m_spritePrompt.m_sInFlags |= CSprite::InClampToScreen;							

					m_spritePrompt.imageWidth = 1;
					m_spritePrompt.imageHeight = 0;
					m_spritePrompt.m_sLayer = 32;			
					m_spritePrompt.m_sAlphaLevel = 254;						
					m_spritePrompt.texture = nullptr;
					m_spritePrompt.m_sX2 = 0;
					m_spritePrompt.m_sY2 = 0;
				
					m_spritePrompt.m_pszText = m_szStats;
					m_spritePrompt.m_sFontSize = (g_GameSettings.m_cHudMode == 1) ? 19 : 15;
					m_spritePrompt.m_cTextLayerOffset = -10;
					m_spritePrompt.m_sTextOffsetY = 0;
					m_spritePrompt.m_sTextOffsetX = 0;
				}					
			}
			
		}

		// Specific transitions by state dependent on motion ////////////////////

		switch (m_state)
		{
		default: // Catch all.  Go to idle if NYI state.
		case State_Idle:
			break;
		case State_Dead:
		case State_Strafe:
		case State_StrafeAndShoot:
		case State_Stand:
		case State_Shooting:
			// If there's any movement . . .
			if (m_dAcc != 0.0)
			{
				// Move to running.
				switch (m_state)
				{
				case State_Stand:
				case State_Strafe:
					SetState(State_Run);
					break;
				case State_StrafeAndShoot:
				case State_Shooting:
					SetState(State_RunAndShoot);
					break;
				}
			}
			else
			{
				// If there's strafe motion . . .
				if (sStrafeAngle != INVALID_STRAFE_ANGLE)
				{
					switch (m_state)
					{
					case State_Stand:
						SetState(State_Strafe);
						break;
					case State_Shooting:
						SetState(State_StrafeAndShoot);
						break;
					case State_Strafe:
					case State_StrafeAndShoot:
						break;
					}
				}
				else
				{
					switch (m_state)
					{
					case State_Stand:
					case State_Shooting:
						break;
					case State_Strafe:
						SetState(State_Stand);
						break;
					case State_StrafeAndShoot:
						SetState(State_Shooting);
						break;
					}
				}
			}
			break;
		case State_Burning:
		{
			// Tweak out.
			m_dRot = rspMod360(m_dRot + RAND_SWAY(ON_FIRE_ROT_TWEAKAGE));
			m_dVel += RAND_SWAY(ON_FIRE_VEL_TWEAKAGE) + ON_FIRE_VEL_TWEAKAGE / 4; // **FUDGE**
		}

			// Intentional fall through.
		case State_Run:
		case State_RunAndShoot:
			// If there's little movement . . .
			if ((m_dVel < MIN_RUN_VEL && m_dVel > -MIN_RUN_VEL && m_dAcc == 0.0))
			{
				// If not strafing . . .
				if (sStrafeAngle == INVALID_STRAFE_ANGLE)
				{
					// Move to standing.
					switch (m_state)
					{
					case State_Run:
						SetState(State_Stand);
						break;
					case State_RunAndShoot:
						SetState(State_Shooting);
						break;
					case State_Burning:
						break;
					}
				}
				else // We are strafing in some way.
				{
					// Move to standing.
					switch (m_state)
					{
					case State_Run:
						SetState(State_Strafe);
						break;
					case State_RunAndShoot:
						SetState(State_StrafeAndShoot);
						break;
					case State_Burning:
						break;
					}
				}
			}		
			break;
		case State_Throw:
		case State_ThrowRelease:
		case State_ThrowDone:
		case State_ThrowFinish:
		case State_Launch:
		case State_LaunchRelease:
		case State_LaunchDone:
		case State_LaunchFinish:
			// Let's just say you can't move while throwing/launching.
			m_dVel = 0.0;
			m_dAcc = 0.0;
			m_dDrag = 0.0;
			break;
		}

		// Service message queue ////////////////////////////////////////////////
		ProcessMessages();

		// Switch on state.
		switch (m_state)
		{
		case State_Stand:
		{
#if 1 // No idle anim currently. \
	// If we've been idle for a while . . .
			if (m_lAnimTime > m_lNextIdleTime)
			{
				// Toggle anim.
				if (m_panimCur == &m_animStand)
				{
					// Switch to idle.
					m_panimCur = &m_animIdle;
					// Set next timeout.
					m_lNextIdleTime = m_animIdle.m_psops->TotalTime() * IDLE_ANIM_LOOPS;
				}
				else
				{
					// Switch to stand.
					m_panimCur = &m_animStand;
					// Set next timeout.
					m_lNextIdleTime = IDLE_ANIM_TIMEOUT;
				}

				// Reset anim timer.
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = lThisTime;
			}
#endif
			break;
		}
		case State_Throw:
		{
#if 1
			U8 u8Event = *((U8 *)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime)));
			// Check for release point in animation . . .
			if (u8Event > 0)
			{
				CWeapon *pweapon;

				if (m_pRealm->m_idbank.GetThingByID((CThing **)&pweapon, m_u16IdWeapon) == 0)
				{
					if (pweapon->m_eState == CWeapon::State_Hide)
					{
						// Unhide grenade.
						pweapon->m_eState = CWeapon::State_Idle;
					}
					else if (u8Event > 1)
					{
						
				if (pTargetThing)
				{				
					float dX = pTargetThing->GetX() - m_dX;
					float dZ = pTargetThing->GetZ() - m_dZ;					
					pweapon->SetRangeToTarget(int16_t(rspSqrt((dX * dX) + (dZ * dZ))));
				}
				else if (pLockedTargetThing)
				{
					float dX = pLockedTargetThing->GetX() - m_dX;
					float dZ = pLockedTargetThing->GetZ() - m_dZ;
					pweapon->SetRangeToTarget(int16_t(rspSqrt((dX * dX) + (dZ * dZ))));
				}

						// Release projectile.
						// Go to State_ThrowRelease which is purely transitional to State_ThrowFinish.
						SetState(State_ThrowRelease);
					}
				}
				else
				{
					if (u8Event > 1)
					{
						// Release projectile.
						// Go to State_ThrowRelease which is purely transitional to State_ThrowFinish.
						SetState(State_ThrowRelease);
					}
				}
			}
#else
			if (WhileHoldingWeapon(
					COLLISION_BITS_INCLUDE,
					COLLISION_BITS_DONTCARE,
					COLLISION_BITS_EXCLUDE) == true)
			{
				// Go to State_ThrowFinish.
				SetState(State_ThrowFinish);
			}
#endif
			break;
		}
		case State_ThrowRelease:
		{
			ShootWeapon();
			// Finish animation.
			SetState(State_ThrowFinish);
			break;
		}
		case State_ThrowDone:
		case State_LaunchDone:
			// Move on.
			SetState(State_Persistent);
			break;
		case State_LaunchRelease:
		{
			ShootWeapon();
			// Finish animation.
			SetState(State_LaunchFinish);
			break;
		}
		case State_ThrowFinish:
			// Check for end of throw animation.
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// This will let us know to be done with throwing on the next Update.
				SetState(State_ThrowDone);
			}
			break;
		case State_Run:
		{
			// Remove adjustment made above.
			m_lAnimTime -= lDifTime;
			// Tune the time.  Note that this is automathematically negative when
			// velocity is negative.
			int32_t lDifAnimTime = lDifTime * (m_dVel / RUN_ANIM_VELOCITY);

			m_lAnimTime += lDifAnimTime;

			// Play step noise if event has changed.
			PlayStep();

			break;
		}
		case State_RunAndShoot:
		{
			// Remove adjustment made above.
			m_lAnimTime -= lDifTime;
			// Tune the time.  Note that this is automathematically negative when
			// velocity is negative.
			int32_t lDifAnimTime = lDifTime * (m_dVel / RUN_ANIM_VELOCITY);

			m_lAnimTime += lDifAnimTime;

			// Play step noise if event has changed.
			PlayStep();

			if (lThisTime >= m_lNextBulletTime)
			{
				// Shoot.
				ShootWeapon();
			}

			break;
		}
		case State_Shooting:						
			// If out of bullets . . .
			if (m_stockpile.m_sNumBullets <= 0)
			{
				// Don't animate.
				m_lAnimTime -= lDifTime;
			}

			if (lThisTime >= m_lNextBulletTime)
			{
				// Shoot.
				ShootWeapon();
			}
			break;
		case State_StrafeAndShoot:

			// If going left of forwards . . .
			if (m_dRot - sStrafeAngle < 0)
			{
				// Subtract twice the time to make up for fact that it was already added once above
				m_lAnimTime -= lDifTime * 2;
			}

			// Play step noise if event has changed.
			PlayStep();

			if (lThisTime >= m_lNextBulletTime)
			{
				// Shoot.
				ShootWeapon();
			}
			break;
		case State_Strafe:
			// If going left of forwards . . .
			if (m_dRot - sStrafeAngle < 0)
			{
				// Subtract twice the time to make up for fact that it was already added once above
				m_lAnimTime -= lDifTime * 2;
			}

			// Play step noise if event has changed.
			PlayStep();

			break;
		case State_Shot:						
			// Check for end of damage animation . . .
			//				if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			// Instead check for minimum duration since last shot time . . .
			if (lThisTime > m_lLastShotTime + MIN_SHOT_DURATION)
			{
				// Stand.
				SetState(State_Persistent);
			}
			break;
		case State_BlownUp:
			// Check for end . . .
			if (WhileBlownUp() == false)
			{
				SetState(State_GetUp);
			}
			break;
		case State_Suicide:
			// Check for moment of firing weapon . . .
			if (*((U8 *)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime))) > 0 && m_bBrainSplatted == false) // ***FUDGE***
			{
				// Play shot fire.
				PlaySample(g_smidShotgun, SampleMaster::Weapon);
				// Play shot received.
				PlaySample(g_smidDyingYell, SampleMaster::Voices);
				// Start gore.

				StartBrainSplat();

				// Remember.
				m_bBrainSplatted = true;

				// Register the kill.
				ScoreRegisterKill(m_pRealm, GetInstanceID(), m_u16InstanceId);

				// Note that he's dead
				m_bDead = true;
			}

			// Intentional fall through to State_Die.
		case State_Die:			
			// Check for end of die animation . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Make sure we're dead for reals.  Do this before the SetState()
				// so we doesn't end up dropping any health.
				m_stockpile.m_sHitPoints = 0;

				// Go to dead.
				SetState(State_Dead);
												
				// If he is carying the flag item, then he should drop it.
				DropAllFlags(NULL);
			}
			else if (m_bGenericEvent1 == false)
			{
				U8 u8Event = *((U8 *)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime)));
				if (u8Event > 1) // ***FUDGE***
				{
					PlaySample(g_smidBodyImpact2, SampleMaster::Unspecified);
					m_bGenericEvent1 = true;
				}
			}
			break;
		case State_Burning:
		{
			// Play step noise if event has changed.
			PlayStep();

			CThing *pthingFire;
			// If the fire's gone . . .
			if (m_pRealm->m_idbank.GetThingByID(&pthingFire, m_u16IdFire) == 0)
			{
				if (!((CFire *)(pthingFire))->IsBurning())
					// Stand.
					SetState(State_Stand);
			}
			else
			{
				// Stand.
				SetState(State_Stand);
			}
			break;
		}
		case State_Launch:
			if (WhileHoldingWeapon(
					COLLISION_BITS_INCLUDE,
					COLLISION_BITS_DONTCARE,
					COLLISION_BITS_EXCLUDE) == true)
			{
				// Go to State_LaunchFinish.
				SetState(State_LaunchFinish);
			}

			break;
		case State_LaunchFinish:
			// Check for end of launch animation . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to State_LaunchDone which is purely transitional to State_Stand.
				SetState(State_LaunchDone);
			}
			break;
		case State_GetUp:
			// check for end of getup anim . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to last persistent state.  Usually stand.
				SetState(State_Persistent);
			}
			break;
		case State_Rise:
			// check for end of rise anim . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to last persistent state.  Usually stand.
				SetState(State_Persistent);
			}
			break;
		case State_Jump:
		case State_JumpForward:
			if (m_lAnimTime > 750 && m_bJumpVerticalTrigger == false)
			{
				// Go up.
				m_dExtVertVel = ms_dVertVelJump;
				m_bJumpVerticalTrigger = true;
			}

			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to fall state.
				SetState(State_Fall);
			}
			else
			{
				break;
			}

		// Intentional fall through to State_Fall.
		case State_Fall:
			// If we've reached the ground . . .
			if (m_bAboveTerrain == false)
			{
				// Go to land state.
				SetState((m_dVel == 0.0) ? State_Land : State_LandForward);
			}
			break;
		case State_Land:
		case State_LandForward:
			// Return to last persistent state.
			SetState(State_Persistent);
			break;
		case State_Execute:
			// check for end of execute anim . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to last persistent state.  Usually stand.
				SetState(State_Persistent);								
			}
			// If before point of firing . . .
			else if (m_lAnimTime < 1350) // ***FUDGE***.
			{
				// Track executee.  If we lost 'em . . .
				if (TrackFloorTarget(lDifTime / 1000.0) == false)
				{
					// Go to last persistent state.  Usually stand.
					SetState(State_Persistent);
				}
			}
			// If in point of firing . . .
			else if (m_lAnimTime < 1950) // ***FUDGE***.
			{
				if (lThisTime >= m_lNextBulletTime)
				{
					OnExecute();
				}
			}
			break;

		case State_Revive:				
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to last persistent state.  Usually stand.				
				SetState(State_Persistent);								
			}
			// If before point of reviving . . .
			else if (m_lAnimTime < 600) // ***FUDGE***.
			{
				// Track revivee.  If we lost 'em . . .
				if (TrackFloorTarget(lDifTime / 1000.0) == false)
				{
					// Go to last persistent state.  Usually stand.
					SetState(State_Persistent);
				}
			}
			// If in point of reviving . . .
			else if (m_lAnimTime < 900) // ***FUDGE***.
			{				
				if (m_idVictim != CIdBank::IdNil)
				{
					CThing *pDudeDown;
					m_pRealm->m_idbank.GetThingByID(&pDudeDown, m_idVictim);
					((CDude*)(pDudeDown))->m_idReviver = GetInstanceID();
				}
				OnRevive();
			}
			break;

		case State_PutDown:

			if (WhileHoldingWeapon(
					COLLISION_BITS_INCLUDE,
					COLLISION_BITS_DONTCARE,
					COLLISION_BITS_EXCLUDE) == true)
			{
				// Go to State_ObjectReleased.
				SetState(State_ObjectReleased);
			}
			break;
		case State_ObjectReleased:
			// Check for end of anim . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// Go to last persistent state.  Usually stand.
				SetState(State_Persistent);
			}

			break;
		case State_PickUp:
		{
			CPowerUp *ppowerup;
			// Check for end of anim . . .
			if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
			{
				// If we still have the picked up item . . .
				if (m_pRealm->m_idbank.GetThingByID((CThing **)&ppowerup, m_u16IdChild) == 0)
				{
					TakePowerUp(&ppowerup);					
					m_u16IdChild = CIdBank::IdNil;
				}

				// Go to last persistent state.  Usually stand.
				SetState(State_Persistent);
			}
			else if (m_lAnimTime > 500 && m_bGenericEvent1 == false)
			{
				// This should be QuickCheckClosest, when available . . .
				CSmash *psmash = NULL;					 // Safety.
				if (m_pRealm->m_smashatorium.QuickCheck( // Returns true if collision detected, false otherwise
						&m_smash,						 // In:  CSmash to check
						CSmash::PowerUp,				 // In:  Bits that must be 1 to collide with a given CSmash
						0,								 // In:  Bits that you don't care about
						0,								 // In:  Bits that must be 0 to collide with a given CSmash
						&psmash) == true)				 // Out: Thing being smashed into if any (unless 0)
				{
					ASSERT(psmash->m_pThing != NULL);

					// Make it blit as a child.
					ASSERT(psmash->m_pThing->GetClassID() == CPowerUpID);
					ppowerup = (CPowerUp *)psmash->m_pThing;
					if (ppowerup->Grab(&m_sprite) == 0)
					{
						// Store/Identify child.
						m_u16IdChild = ppowerup->GetInstanceID();
					}

					// Note we already did this.
					m_bGenericEvent1 = true;
				}
			}
			else if (m_pRealm->m_idbank.GetThingByID((CThing **)&ppowerup, m_u16IdChild) == 0)
			{
				// Position powerup.
				PositionChild(
					&(ppowerup->m_sprite),											 // In:  Child sprite to detach.
					(RTransform *)m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime), // In:  Transform specifying position.
					&(ppowerup->m_dX),												 // Out: New position of child.
					&(ppowerup->m_dY),												 // Out: New position of child.
					&(ppowerup->m_dZ));												 // Out: New position of child.
			}
			break;
		}
		}

		// Update sphere.
		m_smash.m_sphere.sphere.X = m_dX;
		// Fudge center of sphere as half way up the dude.
		// Doesn't work if dude's feet leave the origin.
		m_smash.m_sphere.sphere.Y = m_dY + m_sprite.m_sRadius;
		m_smash.m_sphere.sphere.Z = m_dZ;
		m_smash.m_sphere.sphere.lRadius = m_sprite.m_sRadius;

		// Update the smash.
		m_pRealm->m_smashatorium.Update(&m_smash);

		// Check for powerups.
		CSmash *psmash = NULL;					 // Safety.
		if (m_pRealm->m_smashatorium.QuickCheck( // Returns true if collision detected, false otherwise
				&m_smash,						 // In:  CSmash to check
				CSmash::PowerUp,				 // In:  Bits that must be 1 to collide with a given CSmash
				0,								 // In:  Bits that you don't care about
				0,								 // In:  Bits that must be 0 to collide with a given CSmash
				&psmash) == true)				 // Out: Thing being smashed into if any (unless 0)
		{
			ASSERT(psmash->m_pThing != NULL);
			ASSERT(psmash->m_pThing->GetClassID() == CPowerUpID);

			CPowerUp *ppowerup = (CPowerUp *)psmash->m_pThing;

			// If we're not dead or the powerup contains health . . .
			if (m_bDead == false || ppowerup->m_stockpile.m_sHitPoints > 0)
			{
				TakePowerUp(&ppowerup);
			}
		}

		// Save time for next time
		m_lPrevTime = lThisTime;

		// Call the targeting aid function
		ShowTarget();
		
		// todo
		if (m_state != CDude::State_Suicide && !m_pRealm->IsSuspended())
		{
			ShowPrompt();
		}
								
		// Call base class //////////////////////////////////////////////////////

		CCharacter::Update();

		// If requested to delete self . . .
		if (m_state == State_Delete)
		{
			// Good bye.
			delete this;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Attempts user motivated state transitions.
////////////////////////////////////////////////////////////////////////////////
void CDude::ProcessInput(	// Returns nothing.
	double *pdMaxForeVel,	// Out: Maximum forward velocity.
	double *pdMaxBackVel,	// Out: Maximum backward velocity.
	int16_t *psStrafeAngle) // Out: Strafe angle.
{
	UINPUT input = 0;		
	CInputSettings *pInputSettings = &g_InputSettings[m_pController->m_cControlSchemeId];
	int8_t cMoveStick = 0;
	int8_t cAimStick = 0;

	switch (m_pController->m_cControlSchemeId)
	{
		case CONTROL_SCHEME_STANDARD:
		case CONTROL_SCHEME_STANDARD_ALT:		
		cMoveStick = 0;
		cAimStick = 0;
		break;
		case CONTROL_SCHEME_WINGMAN:
		cMoveStick = 0;
		cAimStick = 3;
		break;
		case CONTROL_SCHEME_REVERSE:
		cMoveStick = 3;
		cAimStick = 0;
		break;
		case CONTROL_SCHEME_TWINSTICK:
		cMoveStick = 1;
		cAimStick = 2;
		break;
	}

	// Get user input
	input = GetInput(m_sDudeNum);

	bool bCanMove = true; // Flags for twinstick input so they don't override death, etc. states
	bool bCanFire = true;
	float fRotTmp = 0.f;

	switch (m_state)
	{

	case State_Dead:		
		// todo: remove cheats?
		// Allow only one input (life cheat) . . .
		if ((input & INPUT_WEAPONS_MASK) == INPUT_CHEAT_15)
		{
			input = INPUT_CHEAT_15;
		}
		else
		{
			// No other user control during these states.
			input = 0;
			bCanMove = false;
			bCanFire = false;
		}
		break;
	case State_Die:
	case State_Suicide:
		// No user control during these states.
		input = 0;
		bCanMove = false;
		bCanFire = false;
		break;
	case State_BlownUp:
	case State_Rise:
	case State_GetUp:
	case State_PutDown:
	case State_ObjectReleased:
	case State_PickUp:
		// No user control during these states other than to change weapon.
		input &= INPUT_WEAPONS_MASK;
		bCanMove = false;
		bCanFire = false;
		break;
	case State_Throw:
	case State_ThrowRelease:
	case State_ThrowDone:
	case State_ThrowFinish:
	case State_Launch:
	case State_LaunchRelease:
	case State_LaunchDone:
	case State_LaunchFinish:
		//case State_Shot:
		// No influence on velocity during these states.
		input &= ~(INPUT_FORWARD | INPUT_BACKWARD | INPUT_REVIVE | INPUT_DUCK | INPUT_MOVE_UP | INPUT_MOVE_DOWN | INPUT_MOVE_LEFT | INPUT_MOVE_RIGHT);
		bCanMove = false;
		bCanFire = false;
		break;
	case State_Execute:
	case State_Revive:
	case State_Duck:
		// No rotation or jumping.
		input &= ~(INPUT_LEFT | INPUT_RIGHT | INPUT_REVIVE | INPUT_ROT_MASK | INPUT_FORWARD | INPUT_BACKWARD | INPUT_MOVE_UP | INPUT_MOVE_DOWN | INPUT_MOVE_LEFT | INPUT_MOVE_RIGHT);
		bCanMove = false;
		bCanFire = false;
		break;
	}

	// Change weapon . . .
	WeaponType wtNew = m_weapontypeCur;
	switch (input & INPUT_WEAPONS_MASK)
	{
	case INPUT_WEAPON_1:
	case INPUT_WEAPON_2:
	case INPUT_WEAPON_3:
	case INPUT_WEAPON_4:
	case INPUT_WEAPON_5:

		if (!demoCompat)
		{
			if (((input & INPUT_WEAPONS_MASK) == INPUT_WEAPON_5) && (m_weapontypeCur == Rocket))
			{
				wtNew = Heatseeker;
				break;
			}
		}

	case INPUT_WEAPON_6:
	case INPUT_WEAPON_7:
	case INPUT_WEAPON_8:
	case INPUT_WEAPON_9:
		wtNew = (WeaponType)(NoWeapon + (((input & INPUT_WEAPONS_MASK) - INPUT_WEAPON_0) >> INPUT_WEAPONS_BIT));
		break;
	case INPUT_WEAPON_0:
		switch (m_weapontypeCur)
		{
		case NoWeapon:
			if (m_stockpile.m_sDeathWadLauncher)
			{
				wtNew = DeathWad;
				break;
			}
		case DeathWad:
			if (m_stockpile.m_sDoubleBarrel)
			{
				wtNew = DoubleBarrel;
				break;
			}
		case DoubleBarrel:
		default:
			wtNew = NoWeapon;
			break;
		}
		break;
	case INPUT_WEAPON_10:
		switch (m_weapontypeCur)
		{
		default:
			wtNew = ProximityMine;
			break;
		case ProximityMine:
			wtNew = TimedMine;
			break;
		case TimedMine:
			wtNew = BouncingBettyMine;
			break;
		}
		break;
	}
		
	if (!demoCompat)
	{	
		if (input & INPUT_WEAPON_NEXT)
		{
			input &= ~(INPUT_FIRE);
			NextWeapon();
		}

		if (input & INPUT_WEAPON_PREV)
		{
			input &= ~(INPUT_FIRE);
			PrevWeapon();
		}			
	}

	// If a weapon key was pressed . . .
	if (input & INPUT_WEAPONS_MASK)
	{
		// If weapon was available . . .
		if (SetWeapon(wtNew, true, false) == true)
		{
		}
	}

	// See if the guy fires his weapon . . .
	// Check for general weapon fire . . .
	if (input & INPUT_FIRE)
	{
		// If not ducking . . .
		if (m_state != State_Duck)
		{
			ArmWeapon();
		}
		else
		{
			// We should get up first.
			SetState(State_Rise);
		}

		// Ducking is not a user option when rising for or firing.
		input &= ~INPUT_DUCK;
	}
	else
	{
		// If currently shooting . . .
		switch (m_state)
		{
		case State_Shooting:
			SetState(State_Stand);
			break;
		case State_RunAndShoot:
			SetState(State_Run);
			break;
		case State_StrafeAndShoot:
			SetState(State_Strafe);
			break;
		}
	}

	int16_t sRotDelta = (int16_t)(input & INPUT_ROT_MASK);
	bool bAimFollowMove = true;

	if (!demoCompat)
	{
		// Lock-on to target
		if (input & INPUT_STRAFE)
		{			
			// todo: strafe anim?
			// todo: cleanup
			bool lockable = false;
			if (pTargetThing && !pLockedTargetThing)
			{			
				CThing3d *person = (CThing3d *)(pTargetThing);
				lockable = (person->m_state != State_Dead && person->m_state != State_Die);			
				if (lockable)
				{								
					pLockedTargetThing = pTargetThing;
					pTargetThing = NULL;				
				}
			}

			if (pLockedTargetThing)
			{	
			
				CThing3d *person = (CThing3d *)(pLockedTargetThing);
				lockable = (person->m_state != State_Dead && person->m_state != State_Die);	
				if (lockable)
				{
					m_bFixedAiming = false;
					bAimFollowMove = false;
					TrackTarget(pLockedTargetThing);
				}
				else
				{
					pLockedTargetThing = NULL;				
					pTargetThing = NULL;					
					m_bLockedAim = true;
				}
			}

			if (m_bLockedAim)
			{
				bAimFollowMove = false;
			}		
		}
		else
		{
			pLockedTargetThing = NULL;
		}
		
		// Lock dude in place for fixed aiming
		if (input & INPUT_REVIVE)
		{				
			m_bFixedAiming = true;
			bAimFollowMove = false;
			if (pLockedTargetThing)
			{			
				m_bFixedAiming = false;						
			}
			else if (input & INPUT_FORWARD && bCanMove && pLockedTargetThing == NULL)
			{												
				if (m_pController->GetStickAngle(&fRotTmp, cMoveStick))
				{
					m_dRotTS = fRotTmp;	
				}

				if (m_pController->GetStickAngle(&fRotTmp, cAimStick))
				{
					m_dRot = fRotTmp;	
				}

				input &= ~INPUT_FORWARD;
				m_dAcc = 0;									
			}
		}
		
		if (bAimFollowMove)
		{
			int16_t sRotDelta = (int16_t)(input & INPUT_ROT_MASK);

			if (sRotDelta != 0)
				sRotDelta -= 360;

			// Adjust by input delta.
			m_dRot += (double)sRotDelta;

			// If there was any rotation while standing . . .
			if (sRotDelta != 0 && m_state == State_Stand)
			{
				// Reset stand state (to make sure idle anim doesn't kick (or gets turned
				// off if we're already in it) ).
				SetState(State_Stand);
			}

			// Release target
			pLockedTargetThing = NULL;
			//pTargetThing = NULL;
			m_bFixedAiming = false;
			m_bLockedAim = false;
		}
	}	
	else
	{
		int16_t sRotDelta = (int16_t)(input & INPUT_ROT_MASK);

		if (sRotDelta != 0)
			sRotDelta -= 360;

		// Adjust by input delta.
		m_dRot += (double)sRotDelta;

		// If there was any rotation while standing . . .
		if (sRotDelta != 0 && m_state == State_Stand)
		{
			// Reset stand state (to make sure idle anim doesn't kick (or gets turned
			// off if we're already in it) ).
			SetState(State_Stand);
		}
	}
		
	bool bPointForward = false;

	if (!demoCompat && pLockedTargetThing == NULL && m_bLockedAim == false && bCanMove)
	{		
		bPointForward = m_pController->GetStickAngle(&fRotTmp, cAimStick);
		if (bPointForward)
		{
			m_dRot = fRotTmp;
		}
	}

	// Set acceleration based on user input (forward has precedance over reverse)		
	if (!demoCompat && input & INPUT_FORWARD)
	{						
		if (m_pController->GetStickAngle(&fRotTmp, cMoveStick))
		{
			m_dRotTS = fRotTmp;	
			
			if (!bPointForward && pLockedTargetThing == NULL && m_bLockedAim == false)
			{
				m_dRot = fRotTmp;
			}
		}
		
		if (bCanMove)
		{
			m_dDrag = 0;
			m_dAcc = ms_dAccUser;
		}
		m_bUseRotTS = true;
	}
	else if (input & INPUT_FORWARD)
	{
		m_dAcc = ms_dAccUser;
		m_dDrag = 0;
		m_bUseRotTS = false;
	}
	else if (input & INPUT_BACKWARD)
	{
		m_dAcc = -ms_dAccUser;
		m_dDrag = 0;
		m_bUseRotTS = false;
	}
	else if (!demoCompat && !((input & INPUT_FORWARD)))
	{
		m_dAcc = 0;		
		// If traveling forward . . .
		if (m_dVel > 0.0)
		{
			m_dDrag = -ms_dAccDrag;
		}
		else if (m_dVel < 0.0)
		{
			m_dDrag = ms_dAccDrag;
		}
	}

	// Limit to maximum velocity, which depends on whether "slow" key is being pressed
	if (input & INPUT_RUN)
	{
		*pdMaxForeVel = ms_dMaxVelForeFast;
		*pdMaxBackVel = ms_dMaxVelBackFast;
	}
	else
	{
		*pdMaxForeVel = ms_dMaxVelFore;
		*pdMaxBackVel = ms_dMaxVelBack;
	}

	// If duck specified . . .
	if (input & INPUT_DUCK)
	{
		SetState(State_Duck);
	}
	else
	{
		if (m_state == State_Duck)
		{
			SetState(State_Rise);
		}
	}
	
	// If suicide specified . . .			
	if (input & INPUT_SUICIDE && g_sActivePlayers == PLAYER_1 && m_pRealm->m_lThisTime > 500 && m_state != State_Burning && m_state != State_BlownUp && m_stockpile.m_sMachineGun)
	{	
		int8_t cSuicideButton;			
		switch (m_pController->m_cControlSchemeId)
		{
			case CONTROL_SCHEME_STANDARD:
			case CONTROL_SCHEME_STANDARD_ALT:		
				cSuicideButton = PromptGrowX;		
				break;
			case CONTROL_SCHEME_WINGMAN:				
			case CONTROL_SCHEME_REVERSE:
				cSuicideButton = PromptGrowDPadDown;
				break;
		}

		// todo: magic numbers			
		if (!m_lButtonPressedStart)
		{
			m_fPromptScale = 0.25f;
			m_lButtonPressedStart = m_pRealm->m_lThisTime;
		}

		if (m_lButtonPressedStart && m_pRealm->m_lThisTime - m_lButtonPressedStart >= 1000)
		{			
			m_spritePrompt.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt2.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt3.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt4.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt5.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt6.m_sInFlags |= CSprite::InHidden;
			m_spritePrompt7.m_sInFlags |= CSprite::InHidden;
			m_spriteUnderlay.m_sInFlags |= CSprite::InHidden;

			// If we have the appropriate weapon . . .
			if (m_stockpile.m_sMachineGun)
			{
				SetState(State_Suicide);
			}
			else
			{
				// Audible user feedback.
				PlaySample(g_smidGeneralBeep, SampleMaster::UserFeedBack);
			}
		}

		g_pvrRenderer->m_fGlobalBScale -= 0.02f;
		g_pvrRenderer->m_fGlobalGScale -= 0.02f;
		
		if (g_pvrRenderer->m_fGlobalBScale < 0)
		{		
			g_pvrRenderer->m_fGlobalBScale = 0.f;
			g_pvrRenderer->m_fGlobalGScale = 0.f;
		}
		
		PreparePrompt(cSuicideButton);		
	}
	else if (g_sActivePlayers == PLAYER_1)
	{		
		if (m_lButtonPressedStart)
		{
			m_spritePrompt.m_sInFlags |= CSprite::InHidden;			
			m_lButtonPressedStart = 0;
			m_fPromptScale = 1.f;
		}

		// todo: magic numbers
		if (g_pvrRenderer->m_fGlobalBScale < 1.f)
		{
			g_pvrRenderer->m_fGlobalBScale += 0.02f;
			g_pvrRenderer->m_fGlobalGScale += 0.02f;

			if (g_pvrRenderer->m_fGlobalBScale > 1.f)
			{		
				g_pvrRenderer->m_fGlobalBScale = 1.f;
				g_pvrRenderer->m_fGlobalGScale = 1.f;
			}
		}					
	}	

	// If execute/revive specified . . .
	if (input & INPUT_EXECUTE)
	{
		// If not already reviving . . .
		if (m_state != State_Revive && m_state != State_Execute)
		{						
			if (FindFloorTarget(1))
			{	
				SetState(State_Revive);				
			}						
		}

		// If not already executing . . .
		if (m_state != State_Execute && m_state != State_Revive)
		{
			// If we have the appropriate weapon . . .
			if (m_stockpile.m_sMachineGun)
			{
				// If there's someone close enough to execute . . .
				if (FindFloorTarget())
				{
					// Do it.
					SetState(State_Execute);
				}
			}
			else
			{
				// Audible user feedback.
				PlaySample(g_smidGeneralBeep, SampleMaster::UserFeedBack);
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Applies accelerations, velocities, reacts to terrain obstructions, etc.
////////////////////////////////////////////////////////////////////////////////
void CDude::ProcessForces( // Returns nothing.
	int32_t lCurTime,	   // In:  Current game time.
	double dMaxForeVel,	   // Out: Maximum forward velocity.
	double dMaxBackVel,	   // Out: Maximum backward velocity.
	int16_t sStrafeAngle)  // Out: Strafe angle.
{
	double dNewX, dNewY, dNewZ;

	// Calculate elapsed time in seconds.
	double dSeconds = (double)(lCurTime - m_lPrevTime) / 1000.0;

	// Update Velocities ////////////////////////////////////////////////////////
	UpdateVelocities(dSeconds, dMaxForeVel, dMaxBackVel);

	// Get New Position /////////////////////////////////////////////////////////
	if (!demoCompat && m_bUseRotTS)
		GetNewPositionAngle(&dNewX, &dNewY, &dNewZ, dSeconds, m_dRotTS);		
	else
		GetNewPosition(&dNewX, &dNewY, &dNewZ, dSeconds);

	// If strafing . . .
	if (sStrafeAngle != INVALID_STRAFE_ANGLE)
	{
		// Add strafe.
		sStrafeAngle = rspMod360(sStrafeAngle);
		double dStrafeDistance = (STRAFE_VEL * dSeconds);

		float cosp;
		float sinp;
		rspSinCos(float(sStrafeAngle), &sinp, &cosp);
		dNewX += cosp * dStrafeDistance;
		dNewZ -= sinp * dStrafeDistance;
	}

	// Validate New Position ////////////////////////////////////////////////////
	if (MakeValidPosition(&dNewX, &dNewY, &dNewZ, MaxStepUpThreshold) == true)
	{				
		// Update Values /////////////////////////////////////////////////////////

		m_dX = dNewX;
		m_dY = dNewY;
		m_dZ = dNewZ;

		// Map through view angle which is the angle of the trigger plane.
		// (it is created by the user in the editor parallel with the
		// screen).
		double dTriggerY;
		m_pRealm->MapZ3DtoY2D(m_dZ, &dTriggerY);
		// Spew triggers.
		SpewTriggers(m_pRealm, GetInstanceID(), m_dX, dTriggerY);

		UpdateFirePosition();
	}
	else
	{
		// Restore Values ////////////////////////////////////////////////////////		
		// Didn't actually move and, therefore, did not actually accelerate.
		// Restore velocities.
		//			m_dVel			-= m_dDeltaVel;
		//			m_dExtHorzVel	-= m_dExtHorzDeltaVel;
		m_dExtVertVel -= m_dExtVertDeltaVel;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Render object
////////////////////////////////////////////////////////////////////////////////
void CDude::Render(void)
{
	// Use user's chosen texture.
	m_panimCur->m_ptextures = m_aptextures[m_sTextureIndex];

	m_sprite.m_cFrameTokenLevel = -1;

	// Tint	the dude based on his remaining health.	
	if (m_state != State_Dead)
	{
		float fScale = rspFastDiv(float(m_stockpile.m_sHitPoints) * 100.f, float(m_sOrigHitPoints)) *  0.01f;
		if (fScale > 1.f)
		{
			fScale = 1.f;
		}
	
		m_sprite.m_fScaleG = fScale;
		m_sprite.m_fScaleB = fScale;
	}
			
	// Call base class.
	CCharacter::Render();

	if (!m_pRealm->IsSuspended())
	{	
		if (!(m_spritePrompt2.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt2.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt2.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt2.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt2.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt2);			
			}

			if (!(m_spritePrompt3.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt3.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt3.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt3.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt3.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt3);
			}

			if (!(m_spritePrompt4.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt4.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt4.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt4.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt4.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt4);
			}

			if (!(m_spritePrompt5.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt5.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt5.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt5.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt5.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt5);
			}

			if (!(m_spritePrompt6.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt6.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt6.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt6.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt6.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt6);
			}

			if (!(m_spritePrompt7.m_sInFlags & CSprite::InHidden))
			{
				m_spritePrompt7.m_sX2 += m_sprite.m_sX2;
				m_spritePrompt7.m_sY2 += m_sprite.m_sY2;
				m_spritePrompt7.m_sPriority = m_sprite.m_sPriority;
				m_spritePrompt7.m_sInFlags |= CSprite::InTransparentList;
				m_pRealm->m_scene.UpdateSprite(&m_spritePrompt7);
			}

		if (!(m_spriteUnderlay.m_sInFlags & CSprite::InHidden))
		{
			m_spriteUnderlay.m_sX2 += m_sprite.m_sX2;
			m_spriteUnderlay.m_sY2 += m_sprite.m_sY2;
			m_spriteUnderlay.m_sPriority = m_sprite.m_sPriority;
			m_spriteUnderlay.m_sInFlags |= CSprite::InTransparentList;
			m_pRealm->m_scene.UpdateSprite(&m_spriteUnderlay);
		}	
	}
	else
	{		
		PreparePrompt(PromptNone);
		m_pRealm->m_scene.UpdateSprite(&m_spritePrompt);
	}


	// Update children, if any . . .
	CFlag *pflag = GetNextFlag(NULL);
	while (pflag)
	{
		PositionChild(
			pflag->GetSprite(),
			((CDudeAnim3D *)m_panimCur)->m_ptransLeft->GetAtTime(m_lAnimTime), // In:  Transform specifying position.
			NULL,
			NULL,
			NULL);

		// Update flag's position so it can correctly collision detect.
		pflag->m_dX = m_dX;
		pflag->m_dY = m_dY;
		pflag->m_dZ = m_dZ;

		// For asthetics, rotate it a bit.
		pflag->m_dRot = rspMod360(pflag->m_dRot + RAND_SWAY(10));

		// Get next.
		pflag = GetNextFlag(pflag);
	}

	// Get anim . . .
	CAnim3D *panimWeapon = NULL; // Safety.
	switch (m_state)
	{
	case State_Suicide:
	case State_Execute:
		panimWeapon = &(m_aanimWeapons[SemiAutomatic]);
		break;
	default:
		panimWeapon = &(m_aanimWeapons[m_weapontypeCur]);
		break;
	}

	// If we have a visible weapon . . .
	if (panimWeapon->m_pmeshes)
	{
		// Show weapon sprite.
		m_spriteWeapon.m_sInFlags &= ~CSprite::InHidden;

		m_spriteWeapon.m_pmesh = panimWeapon->m_pmeshes->GetAtTime(m_lAnimTime);
		m_spriteWeapon.m_psop = panimWeapon->m_psops->GetAtTime(m_lAnimTime);
		m_spriteWeapon.m_ptex = panimWeapon->m_ptextures->GetAtTime(m_lAnimTime);
		m_spriteWeapon.m_psphere = panimWeapon->m_pbounds->GetAtTime(m_lAnimTime);
		m_spriteWeapon.m_ptrans = ((CDudeAnim3D *)m_panimCur)->m_ptransRight->GetAtTime(m_lAnimTime);
	}
	else
	{
		// Hide weapon sprite.
		m_spriteWeapon.m_sInFlags |= CSprite::InHidden;
	}

	// If we have a backpack . . .
	if (m_stockpile.m_sBackpack)
	{
		// Show backpack sprite.
		m_spriteBackpack.m_sInFlags &= ~CSprite::InHidden;

		ASSERT(m_animBackpack.m_pmeshes);
		m_spriteBackpack.m_pmesh = m_animBackpack.m_pmeshes->GetAtTime(m_lAnimTime);
		m_spriteBackpack.m_psop = m_animBackpack.m_psops->GetAtTime(m_lAnimTime);
		m_spriteBackpack.m_ptex = m_animBackpack.m_ptextures->GetAtTime(m_lAnimTime);
		m_spriteBackpack.m_psphere = m_animBackpack.m_pbounds->GetAtTime(m_lAnimTime);
		m_spriteBackpack.m_ptrans = ((CDudeAnim3D *)m_panimCur)->m_ptransBack->GetAtTime(m_lAnimTime);
	}
	else
	{
		// Hide bakcpack sprite.
		m_spriteBackpack.m_sInFlags |= CSprite::InHidden;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Init dude
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::Init(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	m_lPrevTime = m_pRealm->m_time.GetGameTime();
	m_lNextBulletTime = m_lPrevTime;

	// Get resources
	sResult = GetResources();

	// Prepare shadow (get resources and setup sprite).
	sResult |= PrepareShadow();

	// Setup stand state.
	SetState(State_Stand);

	m_smash.m_bits = CSmash::Good | CSmash::Character;
	m_smash.m_pThing = this;

	// No special flags.
	m_sprite.m_sInFlags = 0;

	// Targeting flag initially starts out as Hidden
	m_TargetSprite.m_sInFlags = CSprite::InHidden;
	// Set alpha blendage.
	m_TargetSprite.m_sAlphaLevel = TARGET_ALPHA_LEVEL;
	
	// This can be changed with the toggle, and should probably be
	// set initially by the preferences file.
	m_bTargetingHelpEnabled = false;

	// Setup weapon sprite.
	m_spriteWeapon.m_sInFlags = 0;
	m_spriteWeapon.m_pthing = this;
	m_sprite.AddChild(&m_spriteWeapon);

	// Setup backpack sprite.
	m_spriteBackpack.m_sInFlags = 0;
	m_spriteBackpack.m_pthing = this;
	m_sprite.AddChild(&m_spriteBackpack);

	// Setup our crawler.
	m_crawler.m_prealm = m_pRealm;
	m_crawler.m_sVertTolerance = MaxStepUpThreshold;
	m_crawler.Setup(
		sizeof(ms_anubs) / sizeof(ms_anubs[0]),
		ms_anubs,
		MAX_CRAWLER_PUSH_X,
		MAX_CRAWLER_PUSH_Z);

	// Store initial hit points.
	m_sOrigHitPoints = m_stockpile.m_sHitPoints;

	// Start with a weapon.
	m_weapontypeCur = NoWeapon;
	NextWeapon();

	//todo:
	// Cache gun sound effects since the gun has no preload
	CacheSample(g_smidRicochet1);
	CacheSample(g_smidRicochet2);
	CacheSample(g_smidBulletFire);	
	CacheSample(g_smidShotgun);
	CacheSample(g_smidSprayCannon);
	CacheSample(g_smidFlameThrower3);
	// Cache your own sound effects
	CacheSample(g_smidDyingYell);
	CacheSample(g_smidBodyImpact2);
	CacheSample(g_smidOutOfBullets);
	CacheSample(g_smidBulletIntoVest);
	CacheSample(g_smidBlownupYell);
	CacheSample(g_smidBurningMainGuy);
	CacheSample(g_smidEmptyWeapon);
	CacheSample(g_smidLoadedWeapon);
	CacheSample(g_smidStep);
	CacheSample(g_smidPickedUpWeapon);	
	
	CacheSample(g_smidExecution);
	
	// todo?	
	m_bFixedAiming = false;
	pTargetThing = NULL;
	m_sAddedToStockPile = 0;
	m_lWeaponBarStart = 0;

	m_lButtonPressedStart = 0;
	m_lWeaponBarStart = 0;
	m_fWeaponBarAlphaLevel = 254.f;

	m_spritePrompt2.m_sInFlags = CSprite::InHidden;
	m_spritePrompt2.m_pthing = this;
	m_spritePrompt2.m_sLayer = 31;

	m_spritePrompt3.m_sInFlags = CSprite::InHidden;
	m_spritePrompt3.m_pthing = this;
	m_spritePrompt3.m_sLayer = 31;

	m_spritePrompt4.m_sInFlags = CSprite::InHidden;									
	m_spritePrompt4.m_pthing = this;
	m_spritePrompt4.m_sLayer = 31;
						
	m_spritePrompt5.m_sInFlags = CSprite::InHidden;									
	m_spritePrompt5.m_pthing = this;
	m_spritePrompt5.m_sLayer = 31;

	m_spritePrompt6.m_sInFlags = CSprite::InHidden;									
	m_spritePrompt6.m_pthing = this;
	m_spritePrompt6.m_sLayer = 31;

	m_spritePrompt7.m_sInFlags = CSprite::InHidden;									
	m_spritePrompt7.m_pthing = this;
	m_spritePrompt7.m_sLayer = 31;

	m_spriteUnderlay.m_sInFlags = CSprite::InHidden;
	m_spriteUnderlay.m_pthing = this;
	m_spriteUnderlay.m_sLayer = 30;

	m_MinimalHudOffsetX = 0;
	m_MinimalHudOffsetY = 0;

	// todo: local coop
	m_pController = rspGetJoy(m_sDudeNum);	
		
	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Kill dude
////////////////////////////////////////////////////////////////////////////////
void CDude::Kill(void)
{
	// Remove the target sprite, if there.	
	m_pRealm->m_scene.RemoveSprite(&m_TargetSprite);
	/*m_pRealm->m_scene.RemoveSprite(&m_spritePrompt);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt2);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt3);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt4);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt5);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt6);
	m_pRealm->m_scene.RemoveSprite(&m_spritePrompt7);
	m_pRealm->m_scene.RemoveSprite(&m_spriteUnderlay);*/

	// Free resources
	FreeResources();
}

////////////////////////////////////////////////////////////////////////////////
// Get all required resources
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::GetResources(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	//											Anim base name					Rigid name		Event name		Loop flags
	//											===================			=============	===========		===========
	sResult = m_animRun.Get("3d/main_runnogun", NULL, "mainevent", RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animThrow.Get("3d/main_grenade", "maingrenade", "mainevent", 0);
	sResult |= m_animStand.Get("3d/main_bobbing", NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animDie.Get("3d/main_die", NULL, "mainevent", 0);
	sResult |= m_animShoot.Get("3d/main_shoot", "guntip", NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animRunShoot.Get("3d/main_runshoot", "guntip", "mainevent", RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animDamage.Get("3d/main_multi", NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animBurning.Get("3d/main_onfire", NULL, "mainevent", RChannel_LoopAtStart | RChannel_LoopAtEnd);
	//sResult |= m_animStrafe.Get("3d/main_strafe", NULL, "mainevent", RChannel_LoopAtStart | RChannel_LoopAtEnd);
	//sResult |= m_animStrafeShoot.Get("3d/main_strafe", "guntip", "mainevent", RChannel_LoopAtStart | RChannel_LoopAtEnd);
	sResult |= m_animSuicide.Get("3d/main_suicide", "bhead", "mainevent", 0);
	sResult |= m_animLaunch.Get("3d/main_missile", "mainmissile", "mainevent", 0);
	sResult |= m_animBlownUp.Get("3d/main_blownup", NULL, NULL, 0);
	sResult |= m_animGetUp.Get("3d/main_getup", NULL, NULL, 0);
	sResult |= m_animDuck.Get("3d/main_duck", NULL, NULL, 0);
	sResult |= m_animRise.Get("3d/main_rise", NULL, NULL, 0);
	sResult |= m_animExecute.Get("3d/main_execute", "guntip", NULL, 0);
	sResult |= m_animPickPut.Get("3d/main_pickput", "lfhand", "mainevent", 0);
	sResult |= m_animIdle.Get("3d/main_idle", NULL, NULL, 0);

	// Get the different textures this dude could have.
	int16_t i;
	char szResName[RSP_MAX_PATH];
	for (i = 0; i < MaxTextures && sResult == 0; i++)
	{
		sprintf(szResName, "3d/main_color%d.tex", i);
		sResult |= rspGetResource(&g_resmgrGame, szResName, &(m_aptextures[i]));
	}

	// Get the different weapons this dude could use.
	for (i = NoWeapon; i < NumWeaponTypes; i++)
	{
		if (ms_awdWeapons[i].pszWeaponResName)
		{
			sResult |= m_aanimWeapons[i].Get(ms_awdWeapons[i].pszWeaponResName, NULL, NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);
		}
	}

	// Get the backpack.
	sResult |= m_animBackpack.Get(BACKPACK_RES_NAME, NULL, NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);

	// Get the targeting sprite	
	
	if (g_sActivePlayers > PLAYER_1)
	{
		switch (m_sDudeNum)
		{
			case 0:
				m_TargetSprite.texture = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_TARGET_FIRST);
			break;
			case 1:				
			case 2:				
			case 3:
				m_TargetSprite.texture = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_TARGET_THIRD);
			break;
		}		
	}
	else
	{		
		m_TargetSprite.texture = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_TARGET_FIRST);
	}
	
	m_TargetSprite.imageWidth = 16;
	m_TargetSprite.imageHeight = 16;
	m_TargetSprite.startU = 0.0f;
	m_TargetSprite.startV = 0.0f;
	m_TargetSprite.endU = 0.25f;
	m_TargetSprite.endV = 1.f;

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Free all resources
////////////////////////////////////////////////////////////////////////////////
void CDude::FreeResources(void)
{
	bool purge = m_pRealm->m_flags.bPurgeResOnShutdown;
	purge = false;
	// Release resources for animations.
	m_animRun.Release(purge);
	m_animThrow.Release(purge);
	m_animStand.Release(purge);
	m_animDie.Release(purge);
	m_animShoot.Release(purge);
	m_animRunShoot.Release(purge);
	m_animDamage.Release(purge);
	m_animBurning.Release(purge);
	//m_animStrafe.Release(purge);
	//m_animStrafeShoot.Release(purge);
	m_animSuicide.Release(purge);
	m_animLaunch.Release(purge);
	m_animBlownUp.Release(purge);
	m_animGetUp.Release(purge);
	m_animDuck.Release(purge);
	m_animRise.Release(purge);
	m_animExecute.Release(purge);
	m_animPickPut.Release(purge);
	m_animIdle.Release(purge);

	// Release the different textures.
	int16_t i;
	for (i = 0; i < MaxTextures; i++)
	{
		if (purge)
		{
			rspReleaseAndPurgeResource(&g_resmgrGame, &(m_aptextures[i]));
		}
		else
		{
			rspReleaseResource(&g_resmgrGame, &(m_aptextures[i]));
		}
	}

	// Release the different weapons this dude could use.
	for (i = NoWeapon; i < NumWeaponTypes; i++)
	{
		if (m_aanimWeapons[i].m_pmeshes)
		{
			m_aanimWeapons[i].Release(purge);
		}
	}

	// Release the backpack.
	m_animBackpack.Release(purge);
}

////////////////////////////////////////////////////////////////////////////////
// Sets a new state based on supplied state enum.  Will set animation ptr
// to proper animation for state and reset animation timer.
////////////////////////////////////////////////////////////////////////////////
bool CDude::SetState( // Returns true if new state realized, false otherwise.
	State state)	  // New state.
{
	bool bRealizeNewState = true; // Assume we can enter state.
	State stateOld = m_state;	  // Remember current state.

	// If persistent state specified . . .
	if (state == State_Persistent)
	{
		// Use last persistent state.
		state = m_statePersistent;
	}

	// See if the state can be realized.
	switch (m_state)
	{
	case State_Idle:
		// Any new state is okay.
		break;
	case State_Stand:
		// Any new state is okay.
		break;
	case State_Run:
		// Any new state is okay.
		break;
	case State_ThrowDone:
		break;
	case State_Throw:
	case State_ThrowRelease:
	case State_ThrowFinish:
		switch (state)
		{
		case State_ThrowRelease:
		case State_ThrowFinish:
		case State_ThrowDone:
		case State_Die:
		case State_Shot:
		case State_BlownUp:
		case State_Burning:
			break;
		case State_Suicide:
			bRealizeNewState = true;
			break;
		default:
			bRealizeNewState = false;
			break;
		}
		break;
	case State_Launch:
	case State_LaunchRelease:
	case State_LaunchFinish:
		switch (state)
		{
		case State_Die:
		case State_Shot:
		case State_BlownUp:
		case State_Burning:
		case State_LaunchRelease:
		case State_LaunchFinish:
		case State_LaunchDone:
			break;
		case State_Suicide:
			bRealizeNewState = true;
			break;
		default:
			bRealizeNewState = false;
			break;
		}
		break;
	case State_LaunchDone:
		break;
	case State_Die:
		switch (state)
		{
		case State_Dead:
		case State_BlownUp:
			break;
		default:
			bRealizeNewState = false;
			break;
		}
		break;
	case State_Dead:
		switch (state)
		{
		case State_Die:
		case State_Shot:
		case State_Burning:
			bRealizeNewState = false;
			break;
		case State_BlownUp:
			break;
		default:
			break;
		}
		break;
	case State_Shooting:
		switch (state)
		{
		case State_Suicide:
			bRealizeNewState = true;
			break;	
		case State_Duck:
			bRealizeNewState = false;
			break;
		}
		break;
	case State_RunAndShoot:
		// Can go anywhere from here.
		break;
	case State_Shot:
		switch (state)
		{
		case State_Shot:
			bRealizeNewState = false;
			break;
		case State_BlownUp:
		case State_Die:
			break;
		case State_Suicide:
			bRealizeNewState = true;
			break;	
		default:
			// Check for minimum duration since last shot time . . .
			if (m_pRealm->m_time.GetGameTime() < m_lLastShotTime + MIN_SHOT_DURATION)
			{
				bRealizeNewState = false;
			}
			break;
		}
		break;
	case State_BlownUp:
		switch (state)
		{
		case State_BlownUp:
			break;
		case State_Suicide:
			bRealizeNewState = true;
			break;
		default:
			// Check for end of blown up state . . .
			if (m_lAnimTime < m_panimCur->m_psops->TotalTime())
			{
				bRealizeNewState = false;
			}
			break;
		}
		break;
	case State_Burning:
		switch (state)
		{
		case State_Shot:
		case State_BlownUp:
			// Ok, but come back to this state.
			m_statePersistent = m_state;
			break;
		case State_Die:
		case State_Stand:
			// Don't come back.
			m_statePersistent = State_Stand;
			break;
		case State_Suicide:
			bRealizeNewState = true;
			break;
		default:
			bRealizeNewState = false;
			break;
		}
		break;
	case State_Suicide:
		m_bDeathBySuicide = true;
		switch (state)
		{
		case State_Dead:
			break;
		default:
			// Cannot leave this state except to die.
			bRealizeNewState = false;
			break;
		}
		break;
	case State_GetUp:
		// Can only switch states due to violence or anim done.
		switch (state)
		{
		case State_Die:
		case State_Shot:
		case State_BlownUp:
		case State_Burning:
			break;
		case State_Suicide:
			bRealizeNewState = true;
		break;	
		default:
			// Check for end of get up state . . .
			if (m_lAnimTime < m_panimCur->m_psops->TotalTime())
			{
				bRealizeNewState = false;
			}
			break;
		}
		break;
	case State_Duck:
		// Can go to any other state.
		break;
	case State_Rise:
		// Can go to any other state.
		break;
	case State_Jump:
	case State_JumpForward:
		// Only landing and falling?...damage? blown up? Not sure yet.
		break;
	case State_Fall:
		break;
	case State_Land:
		break;
	case State_LandForward:
		break;
	case State_Execute:
		break;
	case State_Revive:
		break;
	case State_PutDown:
		break;
	case State_ObjectReleased:
		break;
	case State_PickUp:
		break;
	}

	// If new state realized  . . .
	if (bRealizeNewState == true)
	{
		// Clean up old state.
		switch (stateOld)
		{
		case State_Idle: // No cleaning necessary.
		case State_Stand:
		case State_Die:
		case State_Run:
		case State_Shot:
		case State_Suicide:
		case State_GetUp:		
			break;
		case State_Duck:
			// Clear the ducking bit
			m_smash.m_bits &= ~CSmash::Ducking;
			break;
		case State_Dead:
			break;
		case State_Burning:
			// If not coming back to this state . . .
			if (stateOld != m_statePersistent)
			{
				// If the new state is die . . .
				if (state == State_Die)
				{
					// Keep the fire.
				}
				else
				{
					// If there's a fire burning . . .
					CThing *pthingFire;
					if (m_pRealm->m_idbank.GetThingByID(&pthingFire, m_u16IdFire) == 0)
					{
						// Send it a delete message.
						GameMessage msg;
						msg.msg_ObjectDelete.eType = typeObjectDelete;
						msg.msg_ObjectDelete.sPriority = 0;
						SendThingMessage(&msg, pthingFire);
					}
				}
			}
			break;
		case State_PickUp:
		{
			// If there's a powerup that we haven't let go of . . .
			CPowerUp *ppowerup;
			if (m_pRealm->m_idbank.GetThingByID((CThing **)&ppowerup, m_u16IdChild) == 0)
			{
				ppowerup->Drop(m_dX, m_dY, m_dZ);
			}
			break;
		}
		case State_Throw:
		case State_ThrowDone:
		case State_ThrowRelease:
		case State_ThrowFinish:
		case State_PutDown:
		{
			// If there's a weapon that we haven't let go of . . .
			CWeapon *pweapon;
			if (m_pRealm->m_idbank.GetThingByID((CThing **)&pweapon, m_u16IdWeapon) == 0 && state != State_ThrowRelease)
			{
				// It should drop like a rock.
				pweapon->m_dHorizVel = (GetRand() % (int16_t)CGrenade::ms_dThrowHorizVel); // NOTE:   ****USING RAND()****
				pweapon->m_dRot = GetRand() % 360;
				ShootWeapon();
				// Delete it!
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}
			break;
		}
		case State_Launch:
		case State_LaunchDone:
		case State_LaunchRelease:
		case State_LaunchFinish:
		case State_Shooting:
		case State_RunAndShoot:
		case State_StrafeAndShoot:
		{
			// Abort weapon launch.
			// If there's a weapon that we haven't launched . . .
			CWeapon *pweapon;
			if (m_pRealm->m_idbank.GetThingByID((CThing **)&pweapon, m_u16IdWeapon) == 0 && state != State_LaunchRelease)
			{
				// Done with it.
				ShootWeapon();
				// Delete it!
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}

			break;
		}
		case State_BlownUp:
			// If dead . . .
			if (m_bDead == true)
			{
				// Simon says, "die."
				state = State_Dead;
			}
			break;
		case State_Revive:
		case State_Execute:
			m_idVictim = CIdBank::IdNil;
			break;
		}

		// Setup new state.
		m_state = state;
		switch (state)
		{
		case State_Idle:
			m_panimCur = NULL;
			// Make sure we're not in the render list.
			m_pRealm->m_scene.RemoveSprite(&m_sprite);
			break;
		case State_Stand:
			m_statePersistent = State_Stand;
			m_panimCur = &m_animStand;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			m_lNextIdleTime = IDLE_ANIM_TIMEOUT;
			break;
		case State_Run:
			if (stateOld != State_Run)
			{
				m_panimCur = &m_animRun;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();

				m_u8LastEvent = 0;
			}
			break;
		case State_Throw:
			m_panimCur = &m_animThrow;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			break;
		case State_ThrowDone:
			// This state is transitional only.
			break;
		case State_ThrowRelease:
			// This state marks the release of the thrown object.
			break;
		case State_ThrowFinish:
			// During this state the throw animation is played to finish.
			// The thrown item has been released already.
			break;
		case State_Die:
			m_panimCur = &m_animDie;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			// Ahhhhhhhhhh....
			PlaySample(g_smidDyingYell, SampleMaster::Voices);

			m_bGenericEvent1 = false;

			// Make sure he ends up dead.
			m_bDead = true;

			break;
		case State_Dead:
		{
			// If not already dead . . .
			if (m_bDead == false)
			{
				// Note deadness.
				m_bDead = true;
			}

			// Add in our Dead smash bit.
			m_smash.m_bits |= CSmash::Dead;

			// If in multiplayer . . .			
			if (g_sActivePlayers > PLAYER_1 && !m_bDeathBySuicide)
			{				
				// Drop powerup.
				// Create powerup . . .							
				m_stockpile.m_sMachineGun = 0;
				m_stockpile.m_sNumBullets = 0;
				CPowerUp *ppowerup = DropPowerUp(&m_stockpile, false);
				if (ppowerup)
				{					
					// Clear my stockpile.
					m_stockpile.Zero();										
					// Toss it, baby.
					TossPowerUp(ppowerup, 30);
				}
				m_stockpile.m_sMachineGun = 1;
				m_stockpile.m_sNumBullets = CStockPile::ms_stockpileMax.m_sNumBullets;;
			}

			break;
		}
		case State_Shooting:
			// If not already in this state . . .
			if (stateOld != State_Shooting)
			{
				m_panimCur = &m_animShoot;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			}
			break;
		case State_RunAndShoot:
			// If not already in this state . . .
			if (stateOld != State_RunAndShoot)
			{
				m_panimCur = &m_animRunShoot;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				m_u8LastEvent = 0;
			}
			break;
		case State_Shot:
			m_panimCur = &m_animDamage;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			break;
		case State_BlownUp:
			m_panimCur = &m_animBlownUp;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();

			m_bGenericEvent1 = false;
			break;
		case State_Burning:
			// If not already in this state . . .
			if (stateOld != State_Burning)
			{
				m_panimCur = &m_animBurning;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();

				m_u8LastEvent = 0;
			}
			break;
		case State_Suicide:
		{	
			m_panimCur = &m_animSuicide;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			m_bBrainSplatted = false;
			m_bGenericEvent1 = false;
			m_dVel = 0.0;

			// Let the demon know.
			GameMessage msg;
			msg.msg_Suicide.eType = typeSuicide;
			msg.msg_Suicide.sPriority = 0;
			CThing *pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
			if (pDemon)
				SendThingMessage(&msg, pDemon);

			break;
		}
		case State_Launch:
			m_panimCur = &m_animLaunch;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			break;
		case State_LaunchRelease:
			// This state marks the release of the launched object.
			break;
		case State_LaunchFinish:
			// During this state the launch animation is played to finish.
			// The launched item has been released already.
			break;
		case State_LaunchDone:
			// This state is transitional only.
			break;
		case State_GetUp:
			m_panimCur = &m_animGetUp;
			m_lAnimTime = 0;
			m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			break;
		case State_Duck:
			// Set the ducking bits so missiles won't hit him.
			m_smash.m_bits |= CSmash::Ducking;
			if (stateOld != State_Duck)
			{
				m_panimCur = &m_animDuck;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			}
			break;
		case State_Rise:
			// If not already in this state . . .
			if (stateOld != State_Rise)
			{
				// If we were just in the duck state . . .
				if (stateOld == State_Duck)
				{
					// We want to only rise based on how ducked we are.
					// If we duck at the same rate we rise, there's a relation
					// between the times of the animations.
					// If not past the end of the duck . . .
					if (m_lAnimTime < m_animDuck.m_psops->TotalTime())
					{
						ASSERT(m_animDuck.m_psops->TotalTime() != 0);

						// Get the ratio between the two animations total times.
						float fRatio = (float)m_animRise.m_psops->TotalTime() / (float)m_animDuck.m_psops->TotalTime();
						// Invert the time in the new animations time base.
						m_lAnimTime = m_animRise.m_psops->TotalTime() - m_lAnimTime * fRatio;
					}
					else
					{
						m_lAnimTime = 0;
					}
				}
				else
				{
					m_lAnimTime = 0;
				}

				m_panimCur = &m_animRise;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			}
			break;
		case State_Jump:
			if (stateOld != State_Jump)
			{
				//					m_panimCur	= &m_animJump;
				ASSERT(0); // No longer a valid state.
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				// Clear trigger.
				m_bJumpVerticalTrigger = false;
			}
			break;
		case State_JumpForward:
			if (stateOld != State_JumpForward)
			{
				//					m_panimCur	= &m_animJumpForward;
				ASSERT(0); // No longer a valid state.
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				// Clear trigger.
				m_bJumpVerticalTrigger = false;
			}
			break;
		case State_Execute:
			if (stateOld != State_Execute)
			{
				m_panimCur = &m_animExecute;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				m_bGenericEvent1 = false;
				PlaySample(g_smidExecution, SampleMaster::UserFeedBack);
			}
			break;
		case State_Revive:
			if (stateOld != State_Revive)
			{
				m_panimCur = &m_animPickPut;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				m_bGenericEvent1 = false;				
			}
			break;
		case State_PutDown:
			if (stateOld != State_PutDown)
			{
				m_panimCur = &m_animPickPut;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
			}
			break;
		case State_ObjectReleased:
			// Use current anim settings.
			break;
		case State_PickUp:
			if (stateOld != State_PickUp)
			{
				m_panimCur = &m_animPickPut;
				m_lAnimTime = 0;
				m_lAnimPrevUpdateTime = m_pRealm->m_time.GetGameTime();
				m_bGenericEvent1 = false;
			}
			break;
		}
	}

	return bRealizeNewState;
}

////////////////////////////////////////////////////////////////////////////////
// Gets info on specified weapon.
////////////////////////////////////////////////////////////////////////////////
void CDude::GetWeaponInfo(	// Returns nothing.
	WeaponType weapon,		// In:  Weapon type to query.
	ClassIDType *pidWeapon, // Out: CThing class ID of weapon.
	int16_t **ppsNum)		// Out: Ptr to the weapon's counter.
{
	static int16_t sSafetyNum = 0;
	int16_t *psNumLeft = &sSafetyNum;
	// Switch on weapon type . . .
	switch (weapon)
	{
	case NoWeapon:
		*pidWeapon = 0;
		*ppsNum = &sSafetyNum;
		break;
	case Grenade:
		*pidWeapon = CGrenadeID;
		*ppsNum = &m_stockpile.m_sNumGrenades;
		break;
	case FireBomb:
		*pidWeapon = CFirebombID;
		*ppsNum = &m_stockpile.m_sNumFireBombs;
		break;
	case Rocket:
		*pidWeapon = CRocketID;
		*ppsNum = &m_stockpile.m_sNumMissiles;
		break;
	case Napalm:
		*pidWeapon = CNapalmID;
		*ppsNum = &m_stockpile.m_sNumNapalms;
		break;
	case SemiAutomatic:
		*pidWeapon = CMachineGunID;
		*ppsNum = &m_stockpile.m_sNumBullets;
		break;
	case ShotGun:
		*pidWeapon = CShotGunID;
		*ppsNum = &m_stockpile.m_sNumShells;
		break;
	case FlameThrower:
		*pidWeapon = CFirestreamID; //CFireballID;
		*ppsNum = &m_stockpile.m_sNumFuel;
		break;
	case ProximityMine:
		*pidWeapon = CProximityMineID;
		*ppsNum = &m_stockpile.m_sNumMines;
		break;
	case TimedMine:
		*pidWeapon = CTimedMineID;
		*ppsNum = &m_stockpile.m_sNumMines;
		break;
	case RemoteMine:
		*pidWeapon = CRemoteControlMineID;
		*ppsNum = &m_stockpile.m_sNumMines;
		break;
	case BouncingBettyMine:
		*pidWeapon = CBouncingBettyMineID;
		*ppsNum = &m_stockpile.m_sNumMines;
		break;
	case Heatseeker:
		*pidWeapon = CHeatseekerID;
		*ppsNum = &m_stockpile.m_sNumHeatseekers;
		break;
	case SprayCannon:
		*pidWeapon = CAssaultWeaponID;
		*ppsNum = &m_stockpile.m_sNumShells;
		break;
	case DeathWad:
		*pidWeapon = CDeathWadID;
		*ppsNum = &m_stockpile.m_sNumMissiles;
		break;
	case DoubleBarrel:
		*pidWeapon = CDoubleBarrelID;
		*ppsNum = &m_stockpile.m_sNumShells;
		break;
	default:
		TRACE("GetWeaponInfo():  Query on invalid weapon (%d).\n", (int16_t)weapon);
		break;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Fire specified weapon type.
////////////////////////////////////////////////////////////////////////////////
void CDude::ArmWeapon(					   // Returns nothing.
	WeaponType weapon /*= CurrentWeapon*/) // In:  Weapon to fire.
{
	// If firing not in progress . . .
	if (m_u16IdWeapon == CIdBank::IdNil)
	{
		// If no specific weapon . . .
		if (weapon == CurrentWeapon)
		{
			weapon = m_weapontypeCur;
		}

		// Get current weapon and stockpile.
		int16_t *psNumLeft;
		m_stockpile.m_sNumBullets = CStockPile::ms_stockpileMax.m_sNumBullets;
		GetWeaponInfo(weapon, &m_eWeaponType, &psNumLeft);
			
		State stateShoot = State_Throw;
		switch (weapon)
		{
		case FlameThrower:
		case SemiAutomatic:
		case ShotGun:
		case SprayCannon:
			if ((m_dVel >= MIN_RUN_VEL || m_dVel <= -MIN_RUN_VEL || m_dAcc != 0.0))
			{
				stateShoot = State_RunAndShoot;
			}
			else
			{
				switch (m_state)
				{
				case State_Strafe:
				case State_StrafeAndShoot:
					stateShoot = State_StrafeAndShoot;
					break;
				case State_Execute:
					stateShoot = State_Execute;
					break;
				default:
					stateShoot = State_Shooting;
					break;
				}
			}
			break;
	//	case ShotGun:
		case DoubleBarrel:
			stateShoot = State_Launch;
			break;
		case Grenade:
		case FireBomb:
			stateShoot = State_Throw;
			break;
		case Napalm:
		case Rocket:
		case Heatseeker:
		case DeathWad:
			stateShoot = State_Launch;
			break;
		case ProximityMine:
		case TimedMine:
		case RemoteMine:
		case BouncingBettyMine:
			stateShoot = State_PutDown;
			break;
		}

		// If we have any of this weapon . . .
		if (*psNumLeft >= ms_awdWeapons[weapon].sMinAmmoRequired)
		{
			// Enter our weapon launch state.
			if (SetState(stateShoot) == true)
			{	
				// Remember the type of ammo we're shooting.
				m_weaponShooting = weapon;

				CWeapon *pweapon = PrepareWeapon();
				if (pweapon != NULL)
				{
					GameMessage msg;
					msg.msg_WeaponFire.eType = typeWeaponFire;
					msg.msg_WeaponFire.sPriority = 0;
					msg.msg_WeaponFire.sWeapon = (int16_t)weapon;
					CThing *pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
					if (pDemon)
						SendThingMessage(&msg, pDemon);
				}
			}
			else
			{
				// Note that we were unable to arm a weapon.
				//				m_weaponShooting	= NoWeapon;
			}
		}
		else
		{
			// Switch to the old faithful.
			SetWeapon(SemiAutomatic, true);			
			m_sAddedToStockPile = 0;
			m_lWeaponBarStart = m_pRealm->m_lThisTime;
			m_fWeaponBarAlphaLevel = 254.f;

			// Note that we were unable to arm a weapon.
			m_weaponShooting = NoWeapon;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Shoot current weapon.
// This should be done when the character releases the weapon it's
// shooting.
////////////////////////////////////////////////////////////////////////////////
CWeapon *CDude::ShootWeapon(void) // Returns the weapoin ptr or NULL.
{
	return ShootWeapon(
		COLLISION_BITS_INCLUDE,
		COLLISION_BITS_DONTCARE,
		COLLISION_BITS_EXCLUDE);
}

////////////////////////////////////////////////////////////////////////////////
// Shoot current weapon.
// This should be done when the character releases the weapon it's
// shooting.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
CWeapon *CDude::ShootWeapon( // Returns the weapon ptr or NULL.
	CSmash::Bits bitsInclude /*= ms_u32CollideBitsInclude*/,
	CSmash::Bits bitsDontcare /*= ms_u32CollideBitsDontcare*/,
	CSmash::Bits bitsExclude /*= ms_u32CollideBitsExclude*/)
{
	bool bShootWeapon = true; // Assume we should shoot the weapon.
	CWeapon *pweapon = NULL;  // Assume nothing.	
	m_stockpile.m_sNumBullets = CStockPile::ms_stockpileMax.m_sNumBullets;

	// If the weapon is in an invalid position . . .
	if (ValidateWeaponPosition() == false)
	{
		bShootWeapon = false;
	}

	if (m_weaponShooting != NoWeapon && bShootWeapon == true)
	{
		// Get the weapon info.
		ClassIDType idWeapon;
		int16_t *psNumLeft;
		GetWeaponInfo(m_weaponShooting, &idWeapon, &psNumLeft);		
		
		switch (m_eWeaponType)
		{
		case CFirestreamID: //CFireballID:
			if (m_u16IdWeapon != CIdBank::IdNil)
			{
			}
			else
			{
				bShootWeapon = false;
			}
			break;
		case CShotGunID:
			m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + (MS_BETWEEN_BULLETS*4);
			if (m_stockpile.m_sNumShells > 0)
			{
			}
			else
			{
				// Don't fire.
				bShootWeapon = false;
			}
			break;
		case CDoubleBarrelID:
			if (m_stockpile.m_sNumShells > 1)
			{
				// Subtract one now (another will be taken soon -- cheezy... I know).
				m_stockpile.m_sNumShells--;
			}
			else
			{
				// Don't fire.
				bShootWeapon = false;
			}
			break;
		case CAssaultWeaponID:
			if (m_stockpile.m_sNumShells > 0)
			{
				// Note time of next fire.
				m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + MS_BETWEEN_SPRAYS;
			}
			else
			{
				// Note time of next fire.
				m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + MS_BETWEEN_SPRAYS * 3;
				// Feedback.
				PlaySample(g_smidOutOfBullets, SampleMaster::Weapon);
				// Don't fire.
				bShootWeapon = false;
			}
			break;
		case CMachineGunID:
			// Cannot run out of bullets.
			// Note time of next fire.
			m_stockpile.m_sNumBullets = CStockPile::ms_stockpileMax.m_sNumBullets;
			m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + MS_BETWEEN_BULLETS;			
			break;
		}

		// If we actually want to shoot the weapon . . .
		if (bShootWeapon)
		{
			// Deduct ammo.
			if (m_eWeaponType != CMachineGunID)
			{
				*psNumLeft = *psNumLeft - 1;
			}
			
			pweapon = CCharacter::ShootWeapon(bitsInclude, bitsDontcare, bitsExclude);

			// If a weapon was returned . . .
			if (pweapon)
			{
				// Set the detection bits (not all weapons use these).  The only one
				// I know of currently is the heatseeker.
				pweapon->SetDetectionBits(
					CSmash::Character,
					0,
					m_pRealm->m_flags.bCoopMode ? CSmash::Good : 0);
			}
		}
	}

	return pweapon;
}

////////////////////////////////////////////////////////////////////////////////
// Receive damage.
////////////////////////////////////////////////////////////////////////////////
void CDude::Damage(		// Returns nothing.
	int16_t sHitPoints, // Hit points of damage to do.
	U16 u16ShooterId)	// In:  Thing responsible for damage.
{
	// Remember if already dead . . .
	bool bDead = m_bDead;
	
	if (m_bInvincible == false)
	{				
		if (g_cPlayerCount == 2)
		{			
			sHitPoints = int16_t(float(sHitPoints) * 1.5f);
		}
		
		if (g_cPlayerCount == 3)
		{			
			sHitPoints = int16_t(float(sHitPoints) * 2.f);
		}

		if (g_cPlayerCount == 4)
		{			
			sHitPoints = int16_t(float(sHitPoints) * 2.5f);
		}

		m_stockpile.m_sHitPoints -= sHitPoints;
		// If out of life . . .
		if (m_stockpile.m_sHitPoints <= 0 )
		{			
			m_stockpile.m_sHitPoints = 0;

			if (m_state != State_Die && m_state != State_Dead)
			{
				// Go to die.				
				SetState(State_Die);				

				// Even if we fail to enter the die state (like, if we're being
				// blown up).  Be dead.
				m_bDead = true;

				// If he wasn't already dead when he entered here, then register the kill.
				if (bDead == false)
				{
					ScoreRegisterKill(m_pRealm, GetInstanceID(), u16ShooterId);
					ScoreRegisterDeath(m_pRealm, GetInstanceID());
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Start the brain splat anim on its way.
////////////////////////////////////////////////////////////////////////////////
void CDude::StartBrainSplat(void) // Returns nothing.
{
	double dBrainX, dBrainY, dBrainZ;
	GetLinkPoint(										   // Returns nothing.
		m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime), // In:  Transform specifying point.
		&dBrainX,										   // Out: Point specified.
		&dBrainY,										   // Out: Point specified.
		&dBrainZ);										   // Out: Point specified.

	// Make absolute by adding dude's position to relative position.
	dBrainX += m_dX;
	dBrainY += m_dY;
	dBrainZ += m_dZ;

	// Create blood chunks.
	int16_t i;
	for (i = 0; i < BRAIN_SPLAT_NUM_CHUNKS; i++)
	{
		// Create blood particles . . .
		CChunk *pchunk = NULL; // Initialized for safety.
		// Note that this will fail if particles are disabled.
		if (CThing::Construct(CChunkID, m_pRealm, (CThing **)&pchunk) == 0)
		{
			pchunk->Setup(
				dBrainX,		  // Source position.
				dBrainY,		  // Source position.
				dBrainZ,		  // Source position.
				m_dRot - 180,	  // Angle of velocity.
				BRAIN_SPLAT_SWAY, // Angle sway.
				40,				  // Velocity (X/Z plane).
				80,				  // Velocity (X/Z plane) sway.
				50,				  // Velocity (Vertical).
				100,			  // Velocity (Vertical) sway.
				CChunk::Blood);	  // Type of chunk.
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Determines if supplied position is valid tweaking it if necessary.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
bool CDude::MakeValidPosition(		// Returns true, if new position was valid.
									// Returns false, if could not reach new position.
	double *pdNewX,					// In:  x position to validate.
									// Out: New x position.
	double *pdNewY,					// In:  y position to validate.
									// Out: New y position.
	double *pdNewZ,					// In:  z position to validate.
									// Out: New z position.
	int16_t sVertTolerance /*= 0*/) // Vertical tolerance.
{
	bool bValidatedPosition = false; // Assume failure.

	double dCrawlerNewX;
	double dCrawlerNewY;
	double dCrawlerNewZ;
	int16_t sTerrainH;

	// Make sure the position has not changed since our crawlage.
	ASSERT(m_dX == m_dLastCrawledToPosX);
	ASSERT(m_dZ == m_dLastCrawledToPosZ);

	// Restore invalid movements to last good crawl position.
	// The crawler cannot keep us out of things unless it is the only
	// thing responsible for the movement.
	if (m_dX != m_dLastCrawledToPosX)
		m_dX = m_dLastCrawledToPosX;

	if (m_dZ != m_dLastCrawledToPosZ)
		m_dZ = m_dLastCrawledToPosZ;

	// Ask crawler for valid position as close as possible to new position
	if (m_crawler.Move(	   // Returns 0 if successfull, non-zero otherwise
			m_dX,		   // In:  Position #1 xcoord
			m_dY,		   // In:  Position #1 ycoord
			m_dZ,		   // In:  Position #1 zcoord
			*pdNewX,	   // In:  Position #2 xcoord
			*pdNewY,	   // In:  Position #2 ycoord
			*pdNewZ,	   // In:  Position #2 zcoord
			&dCrawlerNewX, // Out: Final position xcoord
			&dCrawlerNewY, // Out: Final position ycoord
			&dCrawlerNewZ, // Out: Final position zcoord
			&sTerrainH)	   // Out: Terrain height at new location
		== 0)
	{
		// Success.
		bValidatedPosition = true;

		if (g_sActivePlayers > PLAYER_1 && !m_bOffscreenMovement)
		{			
			// Limit movement so that all players stay on screen.
			int16_t sX, sY;
			int16_t sMinX, sMinY, sMaxX, sMaxY;
			m_pRealm->Map3Dto2D(*pdNewX, *pdNewY, *pdNewZ, &sX, &sY);
			sX -= m_pCamera->m_sScene2FilmX;
			sY -= m_pCamera->m_sScene2FilmY;
			
			if (g_GameSettings.m_cHudMode == 1)
			{
				sMinX = 0;
				sMaxX = 640;
				sMinY = 0;
				sMaxY = 480;
			}
			else
			{
				sMinX = 10;
				sMaxX = 630;
				sMinY = 60;
				sMaxY = 430;
			}

			if (sX <= sMinX || sX >= sMaxX || sY <= sMinY || sY >= sMaxY)
			{	
				bValidatedPosition = false;													
			}
			
			if (bValidatedPosition)
			{
				*pdNewX = dCrawlerNewX;				
				*pdNewY = dCrawlerNewY;
				*pdNewZ = dCrawlerNewZ;								
				m_dLastCrawledToPosX = dCrawlerNewX;
				m_dLastCrawledToPosZ = dCrawlerNewZ;		
			}			
		}
		else
		{
			*pdNewX = dCrawlerNewX;
			*pdNewY = dCrawlerNewY;
			*pdNewZ = dCrawlerNewZ;

			// Stored crawled to position on X/Z plane.
			m_dLastCrawledToPosX = dCrawlerNewX;
			m_dLastCrawledToPosZ = dCrawlerNewZ;
		}
	
		// If we're gonna be at or below ground level . . .
		if (sTerrainH >= *pdNewY)
		{
			// Get outta there!
			*pdNewY = sTerrainH;
			// Update vertical delta.
			m_dExtVertDeltaVel += -m_dExtVertVel;
			// Reset vertical velocity.
			m_dExtVertVel = 0.0;

			m_bAboveTerrain = false;
		}
		else
		{
			m_bAboveTerrain = true;
		}
	}
	else
	{	
		// Failure.  Homer, I can't seem to move under my own power.
		m_dVel = 0.0;
		m_dAcc = 0.0;
		m_dDrag = 0.0;
	}

	return bValidatedPosition;
}

////////////////////////////////////////////////////////////////////////////////
// Message handlers.
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Handles a msg_Shot.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDude::OnShotMsg(		// Returns nothing.
	Shot_Message *pshotmsg) // In:  Message to handle.
{
	// Remember if already dead . . .
	bool bDead = m_bDead;

	// Give the msg to CThing3d before we alter it.
	CThing3d::OnShotMsg(pshotmsg);	

	int16_t sInitKevlarLayers = m_stockpile.m_sKevlarLayers;

	if (m_stockpile.m_sKevlarLayers > 0)
	{
		pshotmsg->sDamage /= m_stockpile.m_sKevlarLayers * KEVLAR_PROTECTION_MULTIPLIER;

		// Choclate melts vest.
		m_stockpile.m_sKevlarLayers = MAX((int16_t)(m_stockpile.m_sKevlarLayers - (GetRand() & 0x3)), (int16_t)0);
	}

	CCharacter::OnShotMsg(pshotmsg);

	m_pController->m_pJumpPack->RumbleOn(1);

	// If we're taking damage . . .
	if (pshotmsg->sDamage > 0)
	{
		Damage(pshotmsg->sDamage, pshotmsg->u16ShooterID);

		// Check for mininum duration since last shot time . . .
		if (m_pRealm->m_time.GetGameTime() > m_lLastShotTime + MIN_CANNOT_BE_SHOT_DURATION || m_state == State_Stand)
		{
			if (SetState(State_Shot) == true)
			{
				m_lLastShotTime = m_pRealm->m_time.GetGameTime();
			}
		}
	}

	// If we have protection . . .
	if (sInitKevlarLayers > 0)
	{
		// Audible and visual feedback.
		PlaySample(g_smidBulletIntoVest, SampleMaster::Weapon);
		double dHitY = m_dY + m_sprite.m_sRadius + RAND_SWAY(VEST_HIT_SWAY);
		// X/Z position depends on angle of shot (it is opposite).
		int16_t sDeflectionAngle = rspMod360(pshotmsg->sAngle + 180);
		float cosp;
		float sinp;
		rspSinCos(float(sDeflectionAngle), &sinp, &cosp);
		double dHitX = m_dX + cosp * TORSO_RADIUS;
		double dHitZ = m_dZ - sinp * TORSO_RADIUS;

		StartAnim(VEST_HIT_RES_NAME, dHitX, dHitY, dHitZ, false);

		// Create a kevlar peice.
		CChunk *pchunk = NULL; // Initialized for safety.
		// Note that this will fail if particles are disabled.
		if (CThing::Construct(CChunkID, m_pRealm, (CThing **)&pchunk) == 0)
		{
			pchunk->Setup(
				dHitX,			  // Source position.
				dHitY,			  // Source position.
				dHitZ,			  // Source position.
				sDeflectionAngle, // Angle of velocity.
				0,				  // Angle sway.
				40,				  // Velocity (X/Z plane).
				80,				  // Velocity (X/Z plane) sway.
				50,				  // Velocity (Vertical).
				100,			  // Velocity (Vertical) sway.
				CChunk::Kevlar);  // Type of chunk.
		}
	}

	// If still alive . . .
	if (m_bDead == false)
	{
		if (m_pRealm->m_time.GetGameTime() > m_lLastYellTime + MIN_BETWEEN_YELLS)
		{
			// If we took damage . . .
			if (pshotmsg->sDamage > 0)
			{
				PlaySample(g_smidBlownupYell, SampleMaster::Voices);

				m_lLastYellTime = m_pRealm->m_time.GetGameTime();
			}
		}
	}
	else
	{
		// If not previously dead or dying . . .
		if (bDead == false && m_state != State_Die)
		{
			// Ahhhhhhhhhh....
			PlaySample(g_smidDyingYell, SampleMaster::Voices);

			m_lLastYellTime = m_pRealm->m_time.GetGameTime();
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Handles an Explosion_Message.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDude::OnExplosionMsg(			  // Returns nothing.
	Explosion_Message *pexplosionmsg) // In:  Message to handle.
{
	// Remember if already dead . . .
	bool bDead = m_bDead;

	CCharacter::OnExplosionMsg(pexplosionmsg);

	if (SetState(State_BlownUp) == true)
	{
		m_pController->m_pJumpPack->RumbleOn(3);

		// Let's increase his vertical just a bit.
		m_dExtVertVel *= EXPLOSION_VERTICAL_VEL_MULTIPLIER;

		Damage(pexplosionmsg->sDamage, pexplosionmsg->u16ShooterID);

		// If he is carying a flag item, then he should drop it and
		// pass the explosion message on to the flags so they can react.
		DropAllFlags((GameMessage *)pexplosionmsg);

		// If still alive . . .
		if (m_bDead == false)
		{
			if (m_pRealm->m_time.GetGameTime() > m_lLastYellTime + MIN_BETWEEN_YELLS)
			{
				PlaySample(g_smidBlownupYell, SampleMaster::Voices);

				m_lLastYellTime = m_pRealm->m_time.GetGameTime();
			}
		}
		else
		{
			// If not previously dead . . .
			if (bDead == false)
			{
				// Ahhhhhhhhhh....
				PlaySample(g_smidDyingYell, SampleMaster::Voices);

				m_lLastYellTime = m_pRealm->m_time.GetGameTime();
			}
		}
	}

	m_lLastShotTime = m_pRealm->m_time.GetGameTime();
}

////////////////////////////////////////////////////////////////////////////////
// Handles a Burn_Message.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDude::OnBurnMsg(		// Returns nothing.
	Burn_Message *pburnmsg) // In:  Message to handle.
{	
	if (SetState(State_Burning) == true)
	{
		CCharacter::OnBurnMsg(pburnmsg);

		PlaySample(g_smidBurningMainGuy, SampleMaster::Voices);

		// If he is carying a flag item, then he should drop them.
		DropAllFlags((GameMessage *)pburnmsg);

		m_pController->m_pJumpPack->RumbleOn(2);
	}

	int16_t sDamage = MAX((int16_t)1, (int16_t)((double)pburnmsg->sDamage * (((double)m_pRealm->m_flags.sDifficulty) / 10.0)));
	Damage(sDamage, pburnmsg->u16ShooterID);
}

////////////////////////////////////////////////////////////////////////////////
// Handles a PutMeDown_Message
// (virtual)
////////////////////////////////////////////////////////////////////////////////

void CDude::OnPutMeDownMsg( // Returns nothing
	PutMeDown_Message *pputmedownmsg)
{
	// If he is carrying the flag item, then he should put it down
	if (pputmedownmsg->u16FlagInstanceID != CIdBank::IdNil)
	{
		// Detatch child and update its position
		CThing3d *pthing3d = DetachChild(
			&(pputmedownmsg->u16FlagInstanceID),
			((CDudeAnim3D *)m_panimCur)->m_ptransLeft->GetAtTime(m_lAnimTime));
		if (pthing3d)
		{
			pthing3d->m_dX = m_dX;
			pthing3d->m_dY = m_dY;
			pthing3d->m_dZ = m_dZ;
			pthing3d->m_state = State_Die;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Handles an ObjectDelete_Message.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDude::OnDeleteMsg(			  // Returns nothing.
	ObjectDelete_Message *pdeletemsg) // In:  Message to handle.
{
	CCharacter::OnDeleteMsg(pdeletemsg);

	SetState(State_Delete);
}

////////////////////////////////////////////////////////////////////////////////
// Handles a Suicide_Message.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDude::OnSuicideMsg(		  // Returns nothing.
	Suicide_Message *psuicidemsg) // In:  Message to handle.
{
	CCharacter::OnSuicideMsg(psuicidemsg);

	SetState(State_Suicide);
}

////////////////////////////////////////////////////////////////////////////////
// Implements basic functionality while being blown up and returns true
// until the state is completed.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
bool CDude::WhileBlownUp(void) // Returns true until state is complete.
{
	bool bStatePersists = true; // Assume not done.
	double dNewX, dNewY, dNewZ;

	// Get time from last call in seconds.
	int32_t lCurTime = m_pRealm->m_time.GetGameTime();
	double dSeconds = double(lCurTime - m_lPrevTime) / 1000.0;

	// Update Velocities ////////////////////////////////////////////////////////
	UpdateVelocities(dSeconds, ms_dMaxVelForeFast, ms_dMaxVelBackFast);

	// Get New Position /////////////////////////////////////////////////////////
	GetNewPosition(&dNewX, &dNewY, &dNewZ, dSeconds);
	
	// Validate New Position ////////////////////////////////////////////////////
	if (MakeValidPosition(&dNewX, &dNewY, &dNewZ, MaxStepUpThreshold) == true)
	{
		// Update Values /////////////////////////////////////////////////////////

		m_dX = dNewX;
		m_dY = dNewY;
		m_dZ = dNewZ;

		UpdateFirePosition();
	}
	else
	{
		// Restore Values ////////////////////////////////////////////////////////

		// Didn't actually move and, therefore, did not actually accelerate.
		// Restore velocities.
		//			m_dVel			-= m_dDeltaVel;
		//			m_dExtHorzVel	-= m_dExtHorzDeltaVel;
		m_dExtVertVel -= m_dExtVertDeltaVel;
	}

	// If it was above the ground last time and is now below the ground, it must have
	// hit the ground and the blown up state is complete
	if (m_bAboveTerrain == false)
	{

		// If not yet triggered . . .
		if (m_bGenericEvent1 == false)
		{
			PlaySample(g_smidBodyImpact2, SampleMaster::Unspecified);

			m_bGenericEvent1 = true;
		}

		// Make sure its done with current animation also
		if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
		{
			bStatePersists = false;
		}
	}

	return bStatePersists;
}

////////////////////////////////////////////////////////////////////////////////
// Execute the nearest writhing guy, if any.
////////////////////////////////////////////////////////////////////////////////
void CDude::OnExecute(void) // Returns nothing.
{
	// Update execution point via link point.
	double dMuzzleX, dMuzzleY, dMuzzleZ;
	GetLinkPoint(										   // Returns nothing.
		m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime), // In:  Transform specifying point.
		&dMuzzleX,										   // Out: Point speicfied.
		&dMuzzleY,										   // Out: Point speicfied.
		&dMuzzleZ);										   // Out: Point speicfied.

	// Get current weapon and stockpile.
	int16_t *psNumLeft;
	ClassIDType idWeapon;
	GetWeaponInfo(SemiAutomatic, &idWeapon, &psNumLeft);
	if (*psNumLeft > 0)
	{
		// Muzzle flare and sound feedback.
		m_bullets.Flare(
			m_dRot,
			m_dX + dMuzzleX,
			m_dY + dMuzzleY,
			m_dZ + dMuzzleZ,
			m_pRealm);

		// Shoot this thing.
		GameMessage msg;
		msg.msg_Shot.eType = typeShot;
		msg.msg_Shot.sPriority = 0;
		msg.msg_Shot.sAngle = m_dRot;
		msg.msg_Shot.u16ShooterID = m_u16InstanceId;
		msg.msg_Shot.sDamage = 10;

		// Send it the message.
		SendThingMessage(&msg, m_idVictim);

		// Health bonus
		m_stockpile.m_sHitPoints += 8;
		// todo??
		if (m_stockpile.m_sHitPoints > 1000)
		{
			m_stockpile.m_sHitPoints = 1000;
		}
		
		// Note time of next fire.
		m_lNextBulletTime = m_pRealm->m_time.GetGameTime() + MS_BETWEEN_BULLETS;		
	}
	else
	{
		// Feedback.
		PlaySample(g_smidOutOfBullets, SampleMaster::Weapon);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Revive the nearest downed dude, if any.
////////////////////////////////////////////////////////////////////////////////
void CDude::OnRevive(void) // Returns nothing.
{
	GameMessage msg;
	msg.msg_Revive.eType = typeRevive;
	msg.msg_Revive.sPriority = 0;
	// Send it the message.
	SendThingMessage(&msg, m_idVictim);	
}


////////////////////////////////////////////////////////////////////////////////
// Implements one-time functionality for when a weapon is destroyed while
// we were moving it (i.e., before we let go or ShootWeapon()'ed it).
// This can occur when a weapon, while traveling along our rigid body,
// enters terrain.
////////////////////////////////////////////////////////////////////////////////
// virtual (overridden here).
void CDude::OnWeaponDestroyed(void)
{
	// Feedback that we aborted.  Perhaps different by state?
	PlaySample(g_smidGeneralBeep, SampleMaster::UserFeedBack);
	// Finish.
	switch (m_state)
	{
	case State_Throw:
	case State_ThrowRelease:
	case State_ThrowFinish:
		SetState(State_ThrowDone);
		break;

	case State_Launch:
	case State_LaunchRelease:
	case State_LaunchFinish:
		SetState(State_LaunchDone);
		break;

	default:
		// Be done with our state.
		SetState(State_Persistent);
		break;
	}

	m_weaponShooting = NoWeapon;
}

////////////////////////////////////////////////////////////////////////////////
// Revive a dead dude.  This is a more graceful way than just setting
// his state.  This will restore stockpile and make him animate out of
// a warp point.
////////////////////////////////////////////////////////////////////////////////
void CDude::Revive(			 // Returns nothing.
	bool bWarpIn /*= true*/) // In:  true, to warp in, false to just get up.
{

	// Must be dead, lying there still in multiplayer mode (or we could be cheating) . . .
	if (m_bDead == true && (m_pRealm->m_flags.bMultiplayer == true || bWarpIn == false))
	{		
		// Drop that fire.
		m_u16IdFire = CIdBank::IdNil;

		// Let's not be responding to old news.
		m_MessageQueue.Empty();

		// Create powerup . . .
		// NOTE that if the dude is going to be warped in (that is, he is going to get
		// reloaded with stuff from the warp), he only drops his current weapon and all
		// his ammo.  If he is not going to be warped in (that is, he will NOT get
		// any stuff from a warp), he drops all his stuff so he can pick it back up.
		DropPowerUp(&m_stockpile, bWarpIn);
		// Clear my stockpile.
		m_stockpile.Zero();

		// Have a life.
		m_stockpile.m_sHitPoints = m_sOrigHitPoints >> 1;
		m_smash.m_bits &= ~CSmash::Dead;
		m_bDead = false;
		m_sBrightness = 0;

		CDude *pdude = this;
		if (bWarpIn == true)
		{

			// It would be best if this only occurred when we're in the dead state
			// and, hence, on the proper animation.
			// This should flag us to other situations.
			ASSERT(m_state == State_Dead);

			// First try to find a warp in point . . .
			if (CWarp::WarpInAnywhere(
					m_pRealm,
					&pdude,
					CWarp::CopyStockPile) == 0)
			{
				// Force to base weapon
				SetWeapon(SemiAutomatic, true);
				NextWeapon();

				// Run on screen or out of building or whatever.
				SetState(State_Run);
				m_dVel = ms_dMaxVelForeFast;
			}
			else
			{
				// Just get up, you rogue.
				SetState(State_GetUp);
			}
		}
		else
		{
			// Just get up, you rogue.
			SetState(State_GetUp);
		}

		if (m_idReviver != CIdBank::IdNil)
		{
			ScoreRegisterRevive(m_pRealm, m_idReviver);
			m_idReviver = CIdBank::IdNil;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// ShowTarget - If the targeting aid is turned on, then this will put the
//		          targeting sprite on the target.
////////////////////////////////////////////////////////////////////////////////
void CDude::ShowTarget()
{
	// Don't show the target if dead or in a challenge map with no weapons.
	if (m_bTargetingHelpEnabled && m_bDead == false)
	{
		float fRateX = 0.0f;
		float fRateZ = 0.0f;

		// sAngle must be between 0 and 359.
		int16_t sRotY = rspMod360((int16_t)m_dRot);
		int16_t sRangeXZ = 100;
		
		float cosp;
		float sinp;
		rspSinCos((float)sRotY, &sinp, &cosp);

		fRateX = cosp * sRangeXZ;
		fRateZ = -sinp * sRangeXZ;
		
		((CThing3d *)this)->m_sprite.AddChild(&m_TargetSprite);
				
		// Map from 3d to 2d coords
		Map3Dto2D(
			fRateX - m_sprite.m_sRadius / 2,
			m_sprite.m_sRadius * 2,
			fRateZ,
			&m_TargetSprite.m_sX2,
			&m_TargetSprite.m_sY2);

		m_TargetSprite.m_sAlphaLevel = TARGET_ALPHA_LEVEL;
		
		if (g_sActivePlayers > PLAYER_1 || m_sTextureIndex > 0)
		{
			m_TargetSprite.texture = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_TARGET_THIRD);		
			CoatColourToRGB(m_sTextureIndex, &m_TargetSprite.m_cR, &m_TargetSprite.m_cG, &m_TargetSprite.m_cB);	
			m_TargetSprite.m_sAlphaLevel = 156;
		}
		else
		{					
			m_TargetSprite.texture = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_TARGET_FIRST);		
			m_TargetSprite.m_cR = 255;
			m_TargetSprite.m_cG = 255;
			m_TargetSprite.m_cB = 255;
		}
					
		m_TargetSprite.m_sInFlags |= CSprite::InTransparentList;
		m_TargetSprite.m_sInFlags &= ~CSprite::InHidden;

		m_TargetSprite.startU = 0.0f;
		m_TargetSprite.startV = 0.0f;
		m_TargetSprite.endU = 1.f;
		m_TargetSprite.endV = 1.f;	
		m_TargetSprite.m_sLayer = 32;
				
		CThing *active = NULL;
		int16_t sAimRadius = 0;

		switch (m_pController->m_cControlSchemeId)
		{
			case CONTROL_SCHEME_STANDARD:
			case CONTROL_SCHEME_STANDARD_ALT:
				sAimRadius = 5;
				break;
			case CONTROL_SCHEME_WINGMAN:
			case CONTROL_SCHEME_TWINSTICK:
				sAimRadius = 10;
				break;
			case CONTROL_SCHEME_REVERSE:
				sAimRadius = 2;
				break;
		}
	
		if (pLockedTargetThing)
		{
			// Release the target if it's on fire or blown up.
			if (((CThing3d *)pLockedTargetThing)->m_state == State_BlownUp || ((CThing3d *)pLockedTargetThing)->m_state == State_Burning)
			{
				pLockedTargetThing = nullptr;
				m_bLockedAim = true;				
			}
			else
			{
				// Draw the locked-on-target cursor.
				m_TargetSprite.m_sInFlags |= CSprite::InHidden;
				CThing3d *pThing = ((CThing3d *)pLockedTargetThing);

				if (g_sActivePlayers > PLAYER_1 || m_sTextureIndex > 0)
				{					
					pThing->PreparePrompt(PromptTargetP2);
					CoatColourToRGB(m_sTextureIndex, &pThing->m_spritePrompt.m_cR, &pThing->m_spritePrompt.m_cG, &pThing->m_spritePrompt.m_cB);
				}
				else
				{
					pThing->PreparePrompt(PromptTarget);					
				}												
			}
		}
		else
		{			
			if (IlluminateTarget((short)m_dX, (short)m_dY, (short)m_dZ, (short)m_dRot, 200, sAimRadius, CSmash::Character | CSmash::Sentry, CSmash::Bad, CSmash::AlmostDead | CSmash::Dead | CSmash::Good, &active, &m_smash))
			{
				if (((CThing3d *)active)->m_state != State_BlownUp && ((CThing3d *)active)->m_state != State_Burning)
				{
					pTargetThing = active;
					if (m_pController->m_cControlSchemeId == CONTROL_SCHEME_STANDARD)
					{
						((CThing3d *)pTargetThing)->PreparePrompt(PromptPulseA);
					}
					else if (m_pController->m_cControlSchemeId == CONTROL_SCHEME_STANDARD_ALT)
					{
						((CThing3d *)active)->PreparePrompt(PromptPulseLT);	
					}
					else
					{
						CThing3d *pThing = ((CThing3d *)active);
						m_TargetSprite.m_sInFlags |= CSprite::InHidden;												
						if (g_sActivePlayers > PLAYER_1 || m_sTextureIndex > 0)
						{					
							pThing->PreparePrompt(PromptTargetP2);
							CoatColourToRGB(m_sTextureIndex, &pThing->m_spritePrompt.m_cR, &pThing->m_spritePrompt.m_cG, &pThing->m_spritePrompt.m_cB);
						}
						else
						{
							pThing->PreparePrompt(PromptTarget);					
						}
					}						
				}
			}
			else
			{
				pTargetThing = NULL;
			}
		}			
	}
	else
		m_TargetSprite.m_sInFlags |= CSprite::InHidden;
}

// todo
int16_t CDude::WeaponToMinimalHud(WeaponType sWeaponId, int8_t slot, int16_t *sX, int16_t *sY)
{		
	switch (sWeaponId)
	{
		case NoWeapon:
			return PromptNone;
		break;
		case SemiAutomatic:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -15;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptMachineGun;
		break;
		case ShotGun:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -15;
					*sY = 34;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 34;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 34;
				break;
			}
			return PromptShotgun;
		break;
		case SprayCannon:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -15;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptSprayCannon;
		break;
		case Grenade:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -5;
					*sY = 31;
				break;
				case 1: // Curr
					*sX = 5;
					*sY = 31;
				break;
				case 2: // Next
					*sX = 20;
					*sY = 31;
				break;
			}
			return PromptGrenades;
		break;
		case Rocket:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -20;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptRocket;
		break;
		case Heatseeker:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -20;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptHeatSeeker;
		break;
		case FireBomb:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -5;
					*sY = 28;
				break;
				case 1: // Curr
					*sX = 9;
					*sY = 28;
				break;
				case 2: // Next
					*sX = 20;
					*sY = 28;
				break;
			}
			return PromptMolotovs;
		break;
		case Napalm:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -20;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = 0;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptNapalmLauncher;
		break;
		case FlameThrower:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -20;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = -5;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptFlameThrower;
		break;
		case ProximityMine:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -5;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = 4;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 20;
					*sY = 30;
				break;
			}
			return PromptProximityMine;
		break;
		case TimedMine:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -5;
					*sY = 31;
				break;
				case 1: // Curr
					*sX = 4;
					*sY = 31;
				break;
				case 2: // Next
					*sX = 20;
					*sY = 31;
				break;
			}
			return PromptTimedMine;
		break;
		case BouncingBettyMine:			
			switch (slot)
			{
				case 0:	// Prev
					*sX = -5;
					*sY = 32;
				break;
				case 1: // Curr
					*sX = 4;
					*sY = 32;
				break;
				case 2: // Next
					*sX = 20;
					*sY = 32;
				break;
			}
			return PromptBouncingBetty;
		break;
		case DeathWad:
			switch (slot)
			{
				case 0:	// Prev
					*sX = -20;
					*sY = 30;
				break;
				case 1: // Curr
					*sX = 0;
					*sY = 30;
				break;
				case 2: // Next
					*sX = 10;
					*sY = 30;
				break;
			}
			return PromptDeath;
		break;
	}
}


void CDude::CoatColourToRGB(int16_t sColour, uint8_t *cR, uint8_t *cG, uint8_t *cB, uint8_t *cSR, uint8_t *cSG, uint8_t *cSB)
{
	switch (sColour)
	{
		case 0:	// Black
			*cR = 255;
			*cG = 255;
			*cB = 255;
			if (cSR != nullptr)
			{
				*cSR = 0;
				*cSG = 0;
				*cSB = 0;
			}			
		break;			
		case 1:	// Green
			*cR = 0;
			*cG = 255;
			*cB = 0;
			if (cSR != nullptr)
			{
			*cSR = 0;
			*cSG = 64;
			*cSB = 0;
			}
		break;
		case 2: // Blue
			*cR = 32;
			*cG = 128;
			*cB = 255;
			if (cSR != nullptr)
			{
			*cSR = 64;
			*cSG = 64;
			*cSB = 0;
			}
		break;
		case 3: // Grey
			*cR = 128;
			*cG = 128;
			*cB = 128;
			if (cSR != nullptr)
			{
			*cSR = 0;
			*cSG = 0;
			*cSB = 0;
			}
		break;
		case 4: // Brown
			*cR = 205;
			*cG = 127;
			*cB = 60;
			if (cSR != nullptr)
			{
			*cSR = 32;
			*cSG = 32;
			*cSB = 0;
			}
		break;
		case 5: // Red
			*cR = 255;
			*cG = 0;
			*cB = 0;
			if (cSR != nullptr)
			{
			*cSR = 0;
			*cSG = 0;
			*cSB = 0;
			}
		break;
		case 6: // Tan
			*cR = 255;
			*cG = 255;
			*cB = 0;
			if (cSR != nullptr)
			{
			*cSR = 128;
			*cSG = 0;
			*cSB = 0;
			}
		break;
		case 7:	// Purple
			*cR = 255;
			*cG = 0;
			*cB = 255;
			if (cSR != nullptr)
			{
			*cSR = 64;
			*cSG = 0;
			*cSB = 64;
			}
		break;	
	}
}


void CDude::SetTextToCoatColour(CSprite* pSprite)
{
	pSprite->m_bOverrideTextColour = true;
	CoatColourToRGB(m_sTextureIndex, 
		&pSprite->m_ucFontR,
		&pSprite->m_ucFontG,
		&pSprite->m_ucFontB,
		&pSprite->m_ucFontShadowR,
		&pSprite->m_ucFontShadowG,
		&pSprite->m_ucFontShadowB);
}


// todo?
void CDude::ShowPrompt()
{
	int8_t cReviveButton;		
	switch (m_pController->m_cControlSchemeId)
	{
		case CONTROL_SCHEME_STANDARD:
		case CONTROL_SCHEME_STANDARD_ALT:		
		cReviveButton = PromptPulseB;
		break;
		case CONTROL_SCHEME_WINGMAN:
		cReviveButton = PromptPulseLT;		
		break;
		case CONTROL_SCHEME_REVERSE:
		cReviveButton = PromptPulseRT;
		break;
		case CONTROL_SCHEME_TWINSTICK:	
		cReviveButton = PromptPulseTwinLt;		
		break;
	}
		
	if (m_bDead)
	{		
		if (g_sActivePlayers > PLAYER_1)
		{
			CDude *pDude = (CDude*)FindFloorTarget(2);
			if (g_bAllCoopPlayersDead)
			{
				PreparePrompt(cReviveButton);	
			}
			else if (!m_bIsRevivee && pDude)
			{
				switch (pDude->m_pController->m_cControlSchemeId)
				{
					case CONTROL_SCHEME_STANDARD:
					case CONTROL_SCHEME_STANDARD_ALT:		
						PreparePrompt(PromptPulseY);
					break;
					case CONTROL_SCHEME_WINGMAN:						
					case CONTROL_SCHEME_REVERSE:
						PreparePrompt(PromptPulseDPadUp);
					break;
					case CONTROL_SCHEME_TWINSTICK:
						PreparePrompt(PromptPulseD);
				break;
				}				
			}
			else if (!m_bIsRevivee) // Death icon - coop
			{									
				PreparePrompt(PromptDeathPulse);
			}
			else
			{
				m_bIsRevivee = false;
			}					
		}
		else
		{
			PreparePrompt(cReviveButton);
		}				
	}

	if ((g_sActivePlayers > PLAYER_1 || g_GameSettings.m_cHudMode == 1) && !m_bDead && !g_bLastLevelDemo)
	{						
		// Coop health-and armour icons.
		PreparePrompt(PromptHealth, &m_spritePrompt2);
		PreparePrompt(PromptKevlar, &m_spritePrompt3);	

		SetTextToCoatColour(&m_sprite);
		SetTextToCoatColour(&m_spritePrompt2);
		SetTextToCoatColour(&m_spritePrompt3);
		SetTextToCoatColour(&m_spritePrompt4);
		SetTextToCoatColour(&m_spritePrompt5);
		SetTextToCoatColour(&m_spritePrompt6);
		SetTextToCoatColour(&m_spritePrompt7);

		if (m_MinimalHudOffsetX == 0)
		{
			if (m_sprite.m_sRadius > 0)
			{
				m_MinimalHudOffsetX = -(int16_t(float(m_sprite.m_sRadius)*2.5f));
				m_MinimalHudOffsetY = int16_t(rspSlowDiv(float(m_sprite.m_sRadius), 2.2f));
			}

			if (g_sActivePlayers > PLAYER_1)
			{
				sprintf(m_szStats, "P%d", m_sDudeNum + 1);								
				m_sprite.m_pszText = m_szStats;			
				m_sprite.m_sFontSize = (g_GameSettings.m_cHudMode == 1) ? 19 : 15;
				m_sprite.m_cTextLayerOffset = -10;
				m_sprite.m_sTextOffsetY = -(int16_t(float(m_sprite.m_sRadius)*2.5f));
				m_sprite.m_sTextOffsetX = 0;									
			}
		}
		
		if (g_GameSettings.m_cHudMode == 1 && (m_MinimalHudOffsetX || m_MinimalHudOffsetY))
		{			
			m_spritePrompt2.m_sX2 = m_MinimalHudOffsetX;
			m_spritePrompt2.m_sY2 = m_MinimalHudOffsetY;

			m_spritePrompt2.imageWidth = 18;
			m_spritePrompt2.imageHeight = 15;
			m_spritePrompt3.m_sY2 = m_spritePrompt2.m_sY2;
			m_spritePrompt3.m_sX2 = m_spritePrompt2.m_sX2 + m_spritePrompt2.imageWidth + 35;
			m_spritePrompt3.imageWidth = 15;
			m_spritePrompt3.imageHeight = 17;

			sprintf(m_szHP, "%d", int16_t(rspSlowDiv(float(m_stockpile.m_sHitPoints) * 100.f, m_sOrigHitPoints)));
			sprintf(m_szAP, "%d", int16_t(rspSlowDiv(float(m_stockpile.m_sKevlarLayers), 5.0f)));			

			m_spritePrompt2.m_pszText = m_szHP;
			m_spritePrompt3.m_pszText = m_szAP;

			m_spritePrompt2.m_sFontSize = m_spritePrompt3.m_sFontSize = m_spritePrompt4.m_sFontSize = (g_GameSettings.m_cHudMode == 1) ? 19 : 15;
			m_spritePrompt2.m_cTextLayerOffset = m_spritePrompt3.m_cTextLayerOffset = m_spritePrompt4.m_cTextLayerOffset = -14;

			m_spritePrompt2.m_sTextOffsetX = 20;
			m_spritePrompt3.m_sTextOffsetX = 18;
			m_spritePrompt2.m_sTextOffsetY = m_spritePrompt3.m_sTextOffsetY = m_spritePrompt4.m_sTextOffsetY = -2;
			m_spritePrompt4.m_sTextOffsetX = 22;

			m_spritePrompt4.m_pszText = m_szAmmo;
			
			int16_t sCurrWeapon = int16_t(GetCurrentWeapon());
			int16_t sPrevWeapon = PrevWeapon(false);
			int16_t sNextWeapon = NextWeapon(false);
			int16_t sX = 0;
			int16_t sY = 0;

			switch (CDude::WeaponType(sCurrWeapon))
			{
				case CDude::SemiAutomatic:									
					PreparePrompt(PromptBullets, &m_spritePrompt4);				
					sprintf(m_szAmmo, "%d", m_stockpile.m_sNumBullets);				
					m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 2;
					m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;
					m_spritePrompt4.m_sTextOffsetY -= 2;
				break;
				case CDude::ShotGun:
				case CDude::DoubleBarrel:
						PreparePrompt(PromptShells, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumShells);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 3;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;				
						m_spritePrompt4.m_sTextOffsetY -= 3;								
					break;
				case CDude::SprayCannon:
						PreparePrompt(PromptSpray, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumShells);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 3;						
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;
						m_spritePrompt4.m_sTextOffsetY -= 3;												
				break;
				case CDude::Grenade:
						PreparePrompt(PromptGrenades, &m_spritePrompt4);				
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumGrenades);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;						
				break;
				case CDude::Rocket:
						PreparePrompt(PromptRockets, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumMissiles);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 5;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;			
						m_spritePrompt4.m_sTextOffsetY -= 5;						
				break;
				case CDude::Heatseeker:
						PreparePrompt(PromptHeatSeekers, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumHeatseekers);					
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 5;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;			
						m_spritePrompt4.m_sTextOffsetY -= 5;
				break;
				case CDude::FireBomb:
						PreparePrompt(PromptMolotovs, &m_spritePrompt4);				
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumFireBombs);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;				
				break;
				case CDude::Napalm:
						PreparePrompt(PromptNapalm, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumNapalms);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 5;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 26;
						m_spritePrompt4.m_sTextOffsetY -= 5;	
						m_spritePrompt4.m_sTextOffsetX += 4;
					break;
					case CDude::FlameThrower:				
						PreparePrompt(PromptFuel, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", int16_t(rspSlowDiv(float(m_stockpile.m_sNumFuel), 2.50f)));
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 3;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 34;
						m_spritePrompt4.m_sTextOffsetY -= 3;	
						m_spritePrompt4.m_sTextOffsetX -= 4;
					break;
					case CDude::ProximityMine:
						PreparePrompt(PromptProximityMine, &m_spritePrompt4);						
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumMines);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;
					break;
					case CDude::TimedMine:
						PreparePrompt(PromptTimedMine, &m_spritePrompt4);						
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumMines);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;
					break;
					case CDude::BouncingBettyMine:						
						PreparePrompt(PromptBouncingBetty, &m_spritePrompt4);
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumMines);
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;										
					break;
					case CDude::DeathWad:
						PreparePrompt(PromptDeath, &m_spritePrompt4);
						m_spritePrompt4.imageWidth = 12;
						m_spritePrompt4.imageHeight = 11;
						sprintf(m_szAmmo, "%d", m_stockpile.m_sNumMissiles);
						m_spritePrompt4.m_sTextOffsetY -= 3;
						m_spritePrompt4.m_sY2 = m_spritePrompt3.m_sY2 + 3;
						m_spritePrompt4.m_sX2 = m_spritePrompt3.m_sX2 + m_spritePrompt3.imageWidth + 30;										
					break;
				}	

				if (m_lWeaponBarStart)
				{																
					m_spritePrompt6.m_sAlphaLevel = int16_t(m_fWeaponBarAlphaLevel);

					if (m_sAddedToStockPile)
					{						
						PreparePrompt(WeaponToMinimalHud(WeaponType(m_sAddedToStockPile), 1, &sX, &sY), &m_spritePrompt6);
					}
					else
					{
						PreparePrompt(WeaponToMinimalHud(WeaponType(sCurrWeapon), 1, &sX, &sY), &m_spritePrompt6);
					}
					
					m_spritePrompt6.m_sX2 = m_spritePrompt3.m_sX2 + sX;
					m_spritePrompt6.m_sY2 = m_spritePrompt2.m_sY2 + sY;
					m_spritePrompt6.m_sAlphaLevel = int16_t(m_fWeaponBarAlphaLevel);
					
					if (m_spritePrompt6.m_sAlphaLevel < 0) 
						m_spritePrompt6.m_sAlphaLevel = 0;
					
					bool bFoundPrev = true;
					bool bFoundNext = true;

					if (sPrevWeapon != sCurrWeapon && !m_sAddedToStockPile)
					{				
						int16_t cPrompt = WeaponToMinimalHud(WeaponType(sPrevWeapon), 0, &sX, &sY);
						if (cPrompt)
						{
							PreparePrompt(cPrompt, &m_spritePrompt5);
							m_spritePrompt5.m_sX2 = m_spritePrompt2.m_sX2 + sX;
							m_spritePrompt5.m_sY2 = m_spritePrompt2.m_sY2 + sY;
							m_spritePrompt5.m_sAlphaLevel = int16_t(m_fWeaponBarAlphaLevel) - 128;
							if (m_spritePrompt5.m_sAlphaLevel < 0) 
								m_spritePrompt5.m_sAlphaLevel = 0;
						}
						else
						{
							bFoundPrev = false;
						}			
					}

					if (sNextWeapon != sCurrWeapon && !m_sAddedToStockPile)
					{				
						int16_t cPrompt = WeaponToMinimalHud(WeaponType(sNextWeapon), 2, &sX, &sY);
						if (cPrompt)
						{						
							PreparePrompt(cPrompt, &m_spritePrompt7);
							m_spritePrompt7.m_sX2 = m_spritePrompt4.m_sX2 + sX;
							m_spritePrompt7.m_sY2 = m_spritePrompt2.m_sY2 + sY;
							m_spritePrompt7.m_sAlphaLevel = int16_t(m_fWeaponBarAlphaLevel) - 128;
							if (m_spritePrompt7.m_sAlphaLevel < 0) 
								m_spritePrompt7.m_sAlphaLevel = 0;
						}
						else
						{
							bFoundNext = false;
						}
					}
										
					if (((bFoundNext || bFoundPrev) && sCurrWeapon != CDude::DoubleBarrel))
					{
						m_spriteUnderlay.m_sInFlags &= ~CSprite::InHidden;
						m_spriteUnderlay.m_sLayer = 31;
						m_spriteUnderlay.m_sInFlags |= CSprite::InHideOnRender;																																

						if (m_sAddedToStockPile)
						{
							m_spriteUnderlay.m_sX2 = m_spritePrompt6.m_sX2 - (m_spritePrompt6.imageWidth >> 1);
							m_spriteUnderlay.m_sY2 = m_spritePrompt2.m_sY2 + 25;
							m_spriteUnderlay.imageHeight = 30;
							m_spriteUnderlay.imageWidth = (m_spritePrompt6.imageWidth << 1);
							m_spriteUnderlay.m_cR = 254;
							m_spriteUnderlay.m_cG = 254;
							m_spriteUnderlay.m_cB = 51;
							m_spriteUnderlay.m_sAlphaLevel = m_fWeaponBarAlphaLevel - 160;		
						}
						else
						{
							m_spriteUnderlay.m_sX2 = m_spritePrompt2.m_sX2 - 35;
							m_spriteUnderlay.m_sY2 = m_spritePrompt2.m_sY2 + 25;			
							m_spriteUnderlay.imageHeight = 30;
							m_spriteUnderlay.imageWidth = 200;					
							m_spriteUnderlay.m_cR = 128;
							m_spriteUnderlay.m_cG = 128;
							m_spriteUnderlay.m_cB = 128;
							m_spriteUnderlay.m_sAlphaLevel = m_fWeaponBarAlphaLevel - 64;
						}
					}
					
					if (m_spriteUnderlay.m_sAlphaLevel < 0) 
					{
						m_spriteUnderlay.m_sAlphaLevel = 0;
					}						

					if (m_fWeaponBarAlphaLevel == 254.f && (m_pRealm->m_lThisTime - m_lWeaponBarStart) > 1000)
					{
						m_fWeaponBarAlphaLevel = 253.f;
					}

					if (m_fWeaponBarAlphaLevel < 254.f)
					{
						const float incrementPerMilliSecond = 0.2f;
						float increment = float(m_pRealm->m_lElapsedTime) * incrementPerMilliSecond;
						m_fWeaponBarAlphaLevel -= increment;
						if (m_fWeaponBarAlphaLevel <= 0.f)
						{			
							m_sAddedToStockPile = 0;
							m_lWeaponBarStart = 0;				
						}
					}													
				}									
			}	
			else
			{						
				sprintf(m_szHP, "%d", int16_t(rspSlowDiv(float(m_stockpile.m_sHitPoints) * 100.f, m_sOrigHitPoints)));
				sprintf(m_szAP, "%d", int16_t(rspSlowDiv(float(m_stockpile.m_sKevlarLayers), 5.0f)));			
		
				m_spritePrompt2.m_pszText = m_szHP;
				m_spritePrompt3.m_pszText = m_szAP;

				m_spritePrompt2.m_sFontSize = m_spritePrompt3.m_sFontSize = m_spritePrompt4.m_sFontSize = 15;
				m_spritePrompt2.m_cTextLayerOffset = m_spritePrompt3.m_cTextLayerOffset = m_spritePrompt4.m_cTextLayerOffset = -14;
			
				m_spritePrompt2.m_sX2 = 0;
				m_spritePrompt2.m_sY2 = -15;
				m_spritePrompt2.m_sTextOffsetY = 0;
				m_spritePrompt3.m_sTextOffsetY = 0;
				m_spritePrompt2.m_sTextOffsetX = 20;
				m_spritePrompt3.m_sTextOffsetX = 20;			
			}	
	}
}

////////////////////////////////////////////////////////////////////////////////
// Next weapon please.
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::NextWeapon(bool bSwitch)
{
	int16_t sNumTried = 0;
	int16_t sCurWeapon = m_weapontypeCur;

	while (sNumTried < NumWeaponTypes)
	{
		sNumTried++;
		sCurWeapon++;
		if (sCurWeapon == NumWeaponTypes)
		{
			sCurWeapon = NoWeapon + 1;
		}

		// Only allow the double-barrel on special maps
		if (sCurWeapon == DoubleBarrel && m_stockpile.m_sMachineGun)
		{			
			continue;
		}
		
		if (SetWeapon((WeaponType)sCurWeapon, bSwitch, false) == true)
		{
			break;
		}
	}

	if (sNumTried == NumWeaponTypes)
	{		
		sCurWeapon = 0;
		SetWeapon(NoWeapon, bSwitch, false);		
	}

	if (bSwitch)
	{		
		PlaySample(g_smidLoadedWeapon, SampleMaster::UserFeedBack);
		m_sAddedToStockPile = 0;
		m_lWeaponBarStart = m_pRealm->m_lThisTime;
		m_fWeaponBarAlphaLevel = 254.f;
	}
	
	return sCurWeapon;
}

////////////////////////////////////////////////////////////////////////////////
// Previous weapon please.
////////////////////////////////////////////////////////////////////////////////
int16_t CDude::PrevWeapon(bool bSwitch)
{
	int16_t sNumTried = 0;	
	int16_t sCurWeapon = m_weapontypeCur;

	while (sNumTried < NumWeaponTypes)
	{
		sNumTried++;
		sCurWeapon--;
		if (sCurWeapon == NoWeapon)
		{
			sCurWeapon = NumWeaponTypes - 1;
		}

		// Only allow the double-barrel on special maps
		if (sCurWeapon == DoubleBarrel && m_stockpile.m_sMachineGun)
		{			
			continue;
		}

		if (SetWeapon((WeaponType)sCurWeapon, bSwitch, false) == true)
		{			
			break;
		}
	}

	if (sNumTried == NumWeaponTypes)
	{
		SetWeapon(NoWeapon, bSwitch, false);	
	}
				
	if (bSwitch)
	{
		PlaySample(g_smidLoadedWeapon, SampleMaster::UserFeedBack);
		m_sAddedToStockPile = 0;
		m_lWeaponBarStart = m_pRealm->m_lThisTime;
		m_fWeaponBarAlphaLevel = 254.f;
	}
	
	return sCurWeapon;
}

////////////////////////////////////////////////////////////////////////////////
// Set the current weapon.
////////////////////////////////////////////////////////////////////////////////
bool CDude::SetWeapon(			  // Returns true if weapon could be set as current.
	WeaponType weapon,			  // In:  New weapon to attempt to make the current.
	bool bSetWeapon,
	bool bSetIfNoAmmo /*= true*/) // In:  true to set weapon (even if no ammo).
{
	bool bCanSetWeapon = true; // Assume we could set the weapon.

	switch (weapon)
	{
	case NoWeapon:
		break;
	case SemiAutomatic:
		if (m_stockpile.m_sMachineGun == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case Grenade:
		break;
	case FireBomb:
		break;
	case Rocket:
		if (m_stockpile.m_sMissileLauncher == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case Napalm:
		if (m_stockpile.m_sNapalmLauncher == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case ShotGun:
		if (m_stockpile.m_sShotGun == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case SprayCannon:
		if (m_stockpile.m_sSprayCannon == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case FlameThrower:
		if (m_stockpile.m_sFlameThrower == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case BouncingBettyMine:
	case ProximityMine:
	case TimedMine:
		//		case RemoteMine: //Fixed crash when changing weapons and firing at the same time
		break;
	case Heatseeker:
		if (m_stockpile.m_sMissileLauncher == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case DeathWad:
		if (m_stockpile.m_sDeathWadLauncher == 0)
		{
			bCanSetWeapon = false;
		}
		break;
	case DoubleBarrel:
		if (m_stockpile.m_sDoubleBarrel == 0)
		{
			bCanSetWeapon = false;
		}
		break;

	default:
		bCanSetWeapon = false;
		break;
	}

	// Get info on this weapon.
	ClassIDType idDummy;
	int16_t *psNum;
	GetWeaponInfo(weapon, &idDummy, &psNum);

	// If weapon was available . . .
	if (bCanSetWeapon == true)
	{
		// If we have no ammo for this one . . .
		if (*psNum <= 0)
		{
			// If set we cannot set when no ammo . . .
			if (bSetIfNoAmmo == false)
			{
				bCanSetWeapon = false;
			}
			else
			{
				PlaySample(g_smidEmptyWeapon, SampleMaster::UserFeedBack);
			}
		}
		else
		{
			if (bSetIfNoAmmo == true)
			{
				PlaySample(g_smidLoadedWeapon, SampleMaster::UserFeedBack);
			}
		}

		// If able to change weapons . . .
		if (bCanSetWeapon == true && bSetWeapon == true)
		{
			// Set new weapon.
			m_weapontypeCur = weapon;

			// If we have ammo . . .
			if (*psNum > 0)
			{
				GameMessage msg;
				msg.msg_WeaponSelect.eType = typeWeaponSelect;
				msg.msg_WeaponSelect.sPriority = 0;
				msg.msg_WeaponSelect.sWeapon = (int16_t)m_weapontypeCur;
				CThing *pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
				if (pDemon)
					SendThingMessage(&msg, pDemon);
			}
		}
	}
	else
	{
		if (bSetIfNoAmmo == true)
		{
			PlaySample(g_smidEmptyWeapon, SampleMaster::UserFeedBack);
		}
	}

	return bCanSetWeapon;
}

////////////////////////////////////////////////////////////////////////////////
// Drop a powerup with the settings described by the specified stockpile.
////////////////////////////////////////////////////////////////////////////////
CPowerUp *CDude::DropPowerUp( // Returns new powerup on success; NULL on failure.
	CStockPile *pstockpile,	  // In:  Settings for powerup.
	bool bCurWeaponOnly)	  // In:  true, if only the current weapon should be
							  // in the powerup; false, if all.
{
	CPowerUp *ppowerup = NULL;

	// If not empty . . .
	if (pstockpile->IsEmpty() == false)
	{
		// Create powerup . . .
		if (ConstructWithID(CPowerUpID, m_pRealm, (CThing **)&ppowerup) == 0)
		{
			// Put stockpile into powerup.
			ppowerup->m_stockpile.Copy(pstockpile);

			if (bCurWeaponOnly)
			{
				// Note whether we had the weapon.  Not sure how it would be possible
				// but just in case we can somehow have a weapon selected that we do
				// hot have.
				int16_t sHasWeapon = ppowerup->m_stockpile.GetWeapon(m_weapontypeCur);
				// Remove all but our current weapon.
				int16_t sIndex;
				for (sIndex = SemiAutomatic; sIndex <= DoubleBarrel; sIndex++)
				{
					// Zero.
					ppowerup->m_stockpile.GetWeapon(sIndex) = 0;
				}

				// Add back our currently selected weapon.
				ppowerup->m_stockpile.GetWeapon(m_weapontypeCur) = sHasWeapon;
			}

			// Place powerup at our feet.
			ppowerup->Setup(m_dX, m_dY, m_dZ);
		}
	}

	return ppowerup;
}

////////////////////////////////////////////////////////////////////////////////
// Play a step noise if the event is different from the last.
////////////////////////////////////////////////////////////////////////////////
void CDude::PlayStep(void) // Returns nothing.
{
#if 1
	// If there is an event channel . . .
	if (m_panimCur->m_pevent != NULL)
	{
		// If the current event is different from the last . . .
		U8 u8Event = *((U8 *)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime)));
		if (u8Event > 0 && u8Event != m_u8LastEvent)
		{
			PlaySample(g_smidStep, SampleMaster::Unspecified);

			m_u8LastEvent = u8Event;
		}
	}
#endif
}

////////////////////////////////////////////////////////////////////////////////
// Find someone to execute.
////////////////////////////////////////////////////////////////////////////////
CThing* CDude::FindFloorTarget(int8_t cType) // Returns true, if we found one; false, otherwise.
{
	bool bFoundOne = false; // Assume not found.

	CSmash *psmashee = NULL; // Safety.

	CSmash::Bits includeBits = 0;
	CSmash::Bits excludeBits = 0;

	switch (cType)
	{
		case 0: // Potential executee
		includeBits = CSmash::FinishHim;
		break;
		case 1: // Potential resurrectee
		includeBits = CSmash::Dead;
		excludeBits = CSmash::Bad;
		break;
		case 2: // Potential resurrector
		includeBits = CSmash::Good;
		excludeBits = CSmash::Dead;
		break;
	}

	// Find the closest person including writhers.
	if (m_pRealm->m_smashatorium.QuickCheckClosest(
			&m_smash,			// In:  CSmash to check				
			includeBits, 		// In:  Bits we can hit.
			0,					// In:  Bits that you don't care about
			excludeBits,		// In:  Bits that must be 0 to collide with a given CSmash
			&psmashee) == true)
	{
		// We can handle anyone that wants to reveal their location . . .
		// This can be done via the GetX/Y/Z() functions or their smash . . .
		if ((psmashee->m_pThing->GetX() != CThing::InvalidPosition && psmashee->m_pThing->GetY() != CThing::InvalidPosition && psmashee->m_pThing->GetZ() != CThing::InvalidPosition) || psmashee->m_pThing->GetSprite())
		{
			// Found one.
			bFoundOne = true;
			// Remember who.
			m_idVictim = psmashee->m_pThing->GetInstanceID();
		}
		return psmashee->m_pThing;
	}
	else
	{
		return nullptr;
	}	
}

////////////////////////////////////////////////////////////////////////////////
// Track executee.
////////////////////////////////////////////////////////////////////////////////
bool CDude::TrackFloorTarget( // Returns true to persist, false, if we lost the target.
	double dSeconds)	   // In:  Seconds since last iteration.
{
	bool bPersist = true; // Assume we'll persist.	
	CInputSettings *pInputSettings = &g_InputSettings[m_pController->m_cControlSchemeId];

	// Get pointer to target.
	CThing *pthing;
	if (m_pRealm->m_idbank.GetThingByID(&pthing, m_idVictim) == 0)
	{
		double dVictimX;
		double dVictimZ;
		// Try to use smash position first, then resort to thing position.
		CSmash *psmash = pthing->GetSmash();
		if (psmash != NULL)
		{
			// This is generally more accurate for an execution point.
			dVictimX = psmash->m_sphere.sphere.X;
			dVictimZ = psmash->m_sphere.sphere.Z;
		}
		else
		{
			// This'll do though.
			dVictimX = pthing->GetX();
			dVictimZ = pthing->GetZ();
		}

		int16_t sDistX = dVictimX - m_dX;
		int16_t sDistZ = m_dZ - dVictimZ;
		double dSqrDistanceXZ = ABS2(sDistX, sDistZ);

		// Determine angle to target.
		double dRot = rspATan(sDistZ, sDistX);
		// Determine which rotation direction to target is smaller.
		double dDelta = rspDegDelta(m_dRot, dRot);

		// If turning counter clockwise . . .
		if (dDelta > 0.0)
		{
			m_dRot += MIN(dDelta, pInputSettings->m_dStillFastDegreesPerSec * dSeconds);
		}
		else
		{
			m_dRot += MAX(dDelta, -pInputSettings->m_dStillFastDegreesPerSec * dSeconds);
		}

#if 1
		// If too close . . .
		if (dSqrDistanceXZ < MIN_SQR_DISTANCE_TO_EXECUTEE)
		{
			// Back up.
			m_dVel = dSqrDistanceXZ - MIN_SQR_DISTANCE_TO_EXECUTEE;
		}
#else
		// Another solution, if we don't like the above, is to take
		// the gun and use a transform we create (rather than the
		// rigid body one) and aim the gun directly at the target
		// point.
#endif
	}
	else
	{
		// Lost our target.  Continuing is futile.
		m_idVictim = CIdBank::IdNil;
		bPersist = false;
	}

	return bPersist;
}

////////////////////////////////////////////////////////////////////////////////
// Track target.
// todo???
////////////////////////////////////////////////////////////////////////////////
bool CDude::TrackTarget(
	CThing *target)
{
	bool bPersist = true; // Assume we'll persist.

	// Get pointer to target.
	CThing3d *pthing = (CThing3d *)target;

	if (pthing->m_state != CCharacter::State_Dead)
	{
		double dVictimX;
		double dVictimZ;
		// Try to use smash position first, then resort to thing position.
		CSmash *psmash = pthing->GetSmash();
		if (psmash != NULL)
		{
			// This is generally more accurate for an execution point.
			dVictimX = psmash->m_sphere.sphere.X;
			dVictimZ = psmash->m_sphere.sphere.Z;
		}
		else
		{
			// This'll do though.
			dVictimX = pthing->GetX();
			dVictimZ = pthing->GetZ();
		}

		int16_t sDistX = dVictimX - m_dX;
		int16_t sDistZ = m_dZ - dVictimZ;		

		// Determine angle to target.
		double dRot = rspATan(sDistZ, sDistX);
		// Determine which rotation direction to target is smaller.
		double dDelta = rspDegDelta(m_dRot, dRot);

		// If turning counter clockwise . . .
		if (dDelta > 0.0)
		{
			m_dRot += dDelta;
		}
		else
		{
			m_dRot += dDelta;
		}		
	}
	else
	{
		// Lost our target.  Continuing is futile.		
		bPersist = false;
	}

	return bPersist;
}

////////////////////////////////////////////////////////////////////////////////
// Take a powerup.
////////////////////////////////////////////////////////////////////////////////
void CDude::TakePowerUp(  // Returns nothing.
	CPowerUp **pppowerup) // In:  Power up to take from.
						  // Out: Ptr to powerup, if it persisted; NULL otherwise.
{
	CStockPile *pspMax;
	// Note if we do or will have the backpack.
	if (m_stockpile.m_sBackpack || (*pppowerup)->m_stockpile.m_sBackpack)
	{
		pspMax = &CStockPile::ms_stockpileBackPackMax;
	}
	else
	{
		pspMax = &CStockPile::ms_stockpileMax;
	}

	// Create a stockpile of the amount we can take from the powerup.
	CStockPile spTake;
	spTake.Copy(pspMax);
	spTake.Sub(&m_stockpile);
	// Intersect the amount we can take with the amount available.
	spTake.Intersect(&((*pppowerup)->m_stockpile));

	// If we got anything . . .
	if (spTake.IsEmpty() == false)
	{
		// Cash it in.
		m_stockpile.Add(&spTake);

		if (
			spTake.m_sShotGun ||
			spTake.m_sSprayCannon ||
			spTake.m_sMissileLauncher ||
			spTake.m_sNapalmLauncher ||
			spTake.m_sFlameThrower 
			)
		{
			if (spTake.m_sShotGun)			
				m_sAddedToStockPile = ShotGun;			
			if (spTake.m_sSprayCannon)			
				m_sAddedToStockPile = SprayCannon;			
			if (spTake.m_sMissileLauncher)			
				m_sAddedToStockPile = Rocket;
			if (spTake.m_sMissileLauncher)			
				m_sAddedToStockPile = Napalm;
			if (spTake.m_sFlameThrower)			
				m_sAddedToStockPile = FlameThrower;

			m_lWeaponBarStart = m_pRealm->m_lThisTime;
			m_fWeaponBarAlphaLevel = 254.f;		
		}

		// If in MP mode, empty the powerup so it goes away.  Otherwise, just remove what we took
		if (m_pRealm->m_flags.bMultiplayer)
			(*pppowerup)->m_stockpile.Zero();
		else
			(*pppowerup)->m_stockpile.Sub(&spTake);

		// Play feedback.
		(*pppowerup)->PickUpFeedback();

		// Store its ID so we can attempt to get it if it does persist.
		U16 idInstance = (*pppowerup)->GetInstanceID();

		// Let powerup decide if it should persist.
		(*pppowerup)->RepaginateNow();

		// If it persisted . . .
		if (m_pRealm->m_idbank.GetThingByID((CThing **)pppowerup, idInstance) == 0)
		{
			ASSERT((*pppowerup)->GetClassID() == CPowerUpID);
		}
		else
		{
			*pppowerup = NULL;
		}
	}
	else
	{
		// Temp feedback for needed nothing from the weapon.
		PlaySample(g_smidOutOfBullets, SampleMaster::UserFeedBack);

		// If in MP mode, eat the powerup
		if (m_pRealm->m_flags.bMultiplayer)
		{
			(*pppowerup)->m_stockpile.Zero();
			(*pppowerup)->RepaginateNow();
			*pppowerup = NULL;
		}
	}

	// If it still exists . . .
	if (*pppowerup)
	{
		// Store its ID so we can attempt to get it if it does persist.
		U16 idInstance = (*pppowerup)->GetInstanceID();

		// Toss it.
		TossPowerUp(*pppowerup, 130);

		// If it persisted . . .
		if (m_pRealm->m_idbank.GetThingByID((CThing **)pppowerup, idInstance) == 0)
		{
			ASSERT((*pppowerup)->GetClassID() == CPowerUpID);
		}
		else
		{
			*pppowerup = NULL;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Create a cheat powerup.
////////////////////////////////////////////////////////////////////////////////
CPowerUp *CDude::CreateCheat( // Returns new powerup on success; NULL on failure.
	CStockPile *pstockpile)	  // In:  Settings for powerup.
{
	// First deduct from the cheat powerup what we cannot use.
	CStockPile spUsable;
	spUsable.Copy(m_stockpile.m_sBackpack ? &CStockPile::ms_stockpileBackPackMax : &CStockPile::ms_stockpileMax);
	spUsable.Sub(&m_stockpile);

	// Intersect with the supplied.
	spUsable.Intersect(pstockpile);

	// Finally, drop it.

	// If it contains anything . . .
	CPowerUp *ppowerup;
	if (spUsable.IsEmpty() == false)
	{
		ppowerup = DropPowerUp(&spUsable, false);

		if (ppowerup)
		{
			// Take it right away.
			TakePowerUp(&ppowerup);
		}
	}
	else
	{
		ppowerup = NULL;
	}
	
	// Let the demon know.that they are cheating
	GameMessage msg;
	msg.msg_Cheater.eType = typeCheater;
	msg.msg_Cheater.sPriority = 0;
	CThing *pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
	if (pDemon)
		SendThingMessage(&msg, pDemon);

	return ppowerup;
}

////////////////////////////////////////////////////////////////////////////////
// Break a powerup open and toss it.
////////////////////////////////////////////////////////////////////////////////
void CDude::TossPowerUp( // Returns nothing.
	CPowerUp *ppowerup,	 // In:  Powerup to toss.
	int16_t sVelocity)	 // In:  Velocity of toss.
{
	// Blow it up.
	GameMessage msg;
	msg.msg_Explosion.eType = typeExplosion;
	msg.msg_Explosion.sPriority = 0;
	msg.msg_Explosion.sDamage = 0;
	msg.msg_Explosion.sX = m_dX;
	msg.msg_Explosion.sY = m_dY;
	msg.msg_Explosion.sZ = m_dZ;
	msg.msg_Explosion.sVelocity = sVelocity;
	msg.msg_Explosion.u16ShooterID = GetInstanceID();

	SendThingMessage(&msg, msg.msg_Explosion.sPriority, ppowerup);
}

////////////////////////////////////////////////////////////////////////////////
// Get the current weapon the dude has ready to use.
////////////////////////////////////////////////////////////////////////////////
CDude::WeaponType CDude::GetCurrentWeapon(void)
{
	return m_weapontypeCur;
}

////////////////////////////////////////////////////////////////////////////////
// Sets the dude's position.  It is very important that the dude is not
// moved by outside things, other than the warp.
////////////////////////////////////////////////////////////////////////////////
void CDude::SetPosition( // Returns nothing.
	double dX,			 // In:  New position for dude.
	double dY,			 // In:  New position for dude.
	double dZ)			 // In:  New position for dude.
{
	// Set position and update crawler position.
	m_dLastCrawledToPosX = m_dX = dX;
	m_dY = dY;
	m_dLastCrawledToPosZ = m_dZ = dZ;
}

////////////////////////////////////////////////////////////////////////////////
// Get the next child flag item after the specified flag item.
////////////////////////////////////////////////////////////////////////////////
CFlag *CDude::GetNextFlag( // Returns the next flag item after pflag.
	CFlag *pflag)		   // In:  The flag to get the follower of.
						   // NULL for first child flag.
{
	CFlag *pflagNext = NULL;

	CSprite *psprite = pflag ? pflag->m_sprite.m_psprNext : m_sprite.m_psprHeadChild;
	while (psprite && pflagNext == NULL)
	{
		// If this sprite names its owner . . .
		CThing *pthing = psprite->m_pthing;
		if (pthing)
		{
			// If it is a flag . . .
			if (pthing->GetClassID() == CFlagID)
			{
				pflagNext = (CFlag *)pthing;
			}
		}

		// Next Please.
		psprite = psprite->m_psprNext;
	}

	return pflagNext;
}

////////////////////////////////////////////////////////////////////////////////
// Drop all child flag items.
////////////////////////////////////////////////////////////////////////////////
void CDude::DropAllFlags( // Returns nothing.
	GameMessage *pmsg)	  // In:  Message to pass to flags.
{
	GameMessage msg;

	// If no message specified . . .
	if (pmsg == NULL)
	{
		msg.msg_Explosion.eType = typeExplosion;
		msg.msg_Explosion.sPriority = 0;
		msg.msg_Explosion.sDamage = 10;
		msg.msg_Explosion.sX = (int16_t)m_dX;
		msg.msg_Explosion.sY = (int16_t)m_dY;
		msg.msg_Explosion.sZ = (int16_t)m_dZ;
		msg.msg_Explosion.sVelocity = 30;
		msg.msg_Explosion.u16ShooterID = GetInstanceID();

		pmsg = &msg;
	}
	else
	{
		// If it is an explosion message . . .
		if (pmsg->msg_Generic.eType == typeExplosion)
		{
			// Copy it so we can tweak it.
			msg = *pmsg;
		}
	}

	// Loop through all child flags and send them a message and remove them.
	CFlag *pflag = GetNextFlag(NULL);
	CFlag *pflagNext;
	while (pflag)
	{
		// Get the next now b/c this won't be a sibling after we detach it.
		pflagNext = GetNextFlag(pflag);

		U16 u16IdFlag = pflag->GetInstanceID();

		// Detach the flag.
		DetachChild(
			&u16IdFlag,
			((CDudeAnim3D *)m_panimCur)->m_ptransLeft->GetAtTime(m_lAnimTime));

		// Move it to our position rather than the transformed position b/c we
		// don't know if that's a valid position but we do know that our position
		// is.
		pflag->m_dX = m_dX;
		pflag->m_dY = m_dY;
		pflag->m_dZ = m_dZ;

		// If it's an explosion . . .
		if (pmsg->msg_Generic.eType == typeExplosion)
		{
			// Tweak the message a little.
			pmsg->msg_Explosion.sX = (int16_t)m_dX + RAND_SWAY(30);
			pmsg->msg_Explosion.sZ = (int16_t)m_dZ + RAND_SWAY(30);
		}

		// Forward the specified message.
		SendThingMessage(
			pmsg,
			pflag);

		// Next please.
		pflag = pflagNext;
	}
}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
