/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/humanstate.cpp $* * * * $Author:: Patrick $* * * * $Modtime:: 2/26/02 11:47a $* * * * $Revision:: 169 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* ** Includes */ #include "humanstate.h" #include "debug.h" #include "animcontrol.h" #include "humanphys.h" #include "weapons.h" #include "assets.h" #include "combat.h" #include "combat.h" #include "physcoltest.h" #include "pscene.h" #include #include "timemgr.h" #include "definitionclassids.h" #include "pathfind.h" #include "encoderlist.h" #include "bitpackids.h" #include "humanrecoil.h" #include "globalsettings.h" #include "combatchunkid.h" #include "wwprofile.h" #include "surfaceeffects.h" #include "crandom.h" #include "gametype.h" #include "soldier.h" #include "damage.h" #include "transitioneffect.h" #include "combatmaterialeffectmanager.h" /* ** Static instance of HumanRecoilClass for recoil calculations */ static HumanRecoilClass _TheRecoiler; /****************************************************************************************** ** ** HumanStateClass Implementation ** ******************************************************************************************/ HumanStateClass::HumanStateClass( void ) : State( UPRIGHT ), StateFlags( 0 ), StateTimer( 0 ), SubState( 0 ), StateLocked( false ), AnimControl( NULL ), WeaponHoldStyle(WEAPON_HOLD_STYLE_EMPTY_HANDS), HumanPhys( NULL ), TurnVelocity( 0 ), AimingTilt( 0 ), AimingTurn( 0 ), RecoilTimer( 0.0f ), RecoilScale( 1.0f ), LoiterDelay( 0 ), LoitersAllowed( true ), LegRotation( 0 ), WeaponHoldTimer( 0 ), NoAnimBlend( false ), HumanAnimOverride( NULL ), HumanLoiterCollection( NULL ), WeaponFired( false ) { Reset_Loiter_Delay(); } HumanStateClass::~HumanStateClass( void ) { if ( HumanPhys != NULL ) { HumanPhys->Release_Ref(); HumanPhys = NULL; } } void HumanStateClass::Init( HumanPhysClass * human_phys ) { WWASSERT( HumanPhys == NULL ); WWASSERT( human_phys != NULL ); HumanPhys = human_phys; HumanPhys->Add_Ref(); } void HumanStateClass::Reset( void ) { REF_PTR_RELEASE( HumanPhys ); // Clear the sniping flag if ( Get_State_Flag( SNIPING_FLAG ) ) { Toggle_State_Flag( SNIPING_FLAG ); } } void HumanStateClass::Set_Anim_Control( HumanAnimControlClass * anim_control ) { WWASSERT( AnimControl == NULL || AnimControl == anim_control ); WWASSERT( anim_control != NULL ); AnimControl = anim_control; AnimControl->Set_Model( HumanPhys->Peek_Model() ); } void HumanStateClass::Set_Human_Anim_Override( const char * name ) { HumanAnimOverride = (HumanAnimOverrideDef *)DefinitionMgrClass::Find_Typed_Definition( name, CLASSID_GLOBAL_SETTINGS_DEF_HUMAN_ANIM_OVERRIDE ); } void HumanStateClass::Set_Human_Anim_Override( int def_id ) { HumanAnimOverride = (HumanAnimOverrideDef *)DefinitionMgrClass::Find_Definition( def_id ); } void HumanStateClass::Set_Human_Loiter_Collection( int def_id ) { HumanLoiterCollection = (HumanLoiterGlobalSettingsDef *)DefinitionMgrClass::Find_Definition( def_id ); } /* ** CommandoHumanState Save and Load */ enum { CHUNKID_VARIABLES = 915991207, XXX_CHUNKID_ANIM_CONTROL, MICROCHUNKID_STATE = 1, MICROCHUNKID_SUB_STATE, MICROCHUNKID_STATE_LOCKED, MICROCHUNKID_WEAPON_HOLD_STYLE, XXXMICROCHUNKID_WEAPON_STATE, MICROCHUNKID_AIMING_TILT, MICROCHUNKID_AIMING_TURN, MICROCHUNKID_TURN_VELOCITY, MICROCHUNKID_PHYSOBJ, MICROCHUNKID_LOITER_DELAY, MICROCHUNKID_STATE_FLAGS, MICROCHUNKID_JUMP_TM, MICROCHUNKID_STATE_TIMER, MICROCHUNKID_LOITERS_ALLOWED, MICROCHUNKID_WEAPON_HOLD_TIMER, MICROCHUNKID_HUMAN_ANIM_OVERRIDE_DEF_ID, MICROCHUNKID_HUMAN_LOITER_COLLECTION_DEF_ID, }; bool HumanStateClass::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_FLAGS, StateFlags ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SUB_STATE, SubState ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_LOCKED, StateLocked ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_HOLD_STYLE, WeaponHoldStyle ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AIMING_TILT, AimingTilt ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AIMING_TURN, AimingTurn ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TURN_VELOCITY, TurnVelocity ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PHYSOBJ, HumanPhys ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOITER_DELAY, LoiterDelay ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOITERS_ALLOWED, LoitersAllowed ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_JUMP_TM, JumpTM ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_TIMER, StateTimer ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_HOLD_TIMER, WeaponHoldTimer ); if ( HumanAnimOverride != NULL ) { int id = HumanAnimOverride->Get_ID(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUMAN_ANIM_OVERRIDE_DEF_ID, id ); } if ( HumanLoiterCollection != NULL ) { int id = HumanLoiterCollection->Get_ID(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUMAN_LOITER_COLLECTION_DEF_ID, id ); } csave.End_Chunk(); // Don't need to save AnimControl, it gets set externally // We don't save recoil states return true; } bool HumanStateClass::Load( ChunkLoadClass &cload ) { int human_anim_override_def_id = 0; int human_loiter_collection_def_id = 0; WWASSERT( HumanPhys == NULL ); while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_VARIABLES: WWASSERT( HumanPhys == NULL ); while (cload.Open_Micro_Chunk()) { WWASSERT(SubState >= 0 && SubState <= HIGHEST_HUMAN_SUB_STATE); switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_FLAGS, StateFlags ); READ_MICRO_CHUNK( cload, MICROCHUNKID_SUB_STATE, SubState ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_LOCKED, StateLocked ); READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_HOLD_STYLE, WeaponHoldStyle ); READ_MICRO_CHUNK( cload, MICROCHUNKID_AIMING_TILT, AimingTilt ); READ_MICRO_CHUNK( cload, MICROCHUNKID_AIMING_TURN, AimingTurn ); READ_MICRO_CHUNK( cload, MICROCHUNKID_TURN_VELOCITY, TurnVelocity ); READ_MICRO_CHUNK( cload, MICROCHUNKID_PHYSOBJ, HumanPhys ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LOITER_DELAY, LoiterDelay ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LOITERS_ALLOWED, LoitersAllowed ); READ_MICRO_CHUNK( cload, MICROCHUNKID_JUMP_TM, JumpTM ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_TIMER, StateTimer ); READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_HOLD_TIMER, WeaponHoldTimer ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUMAN_ANIM_OVERRIDE_DEF_ID, human_anim_override_def_id ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUMAN_LOITER_COLLECTION_DEF_ID, human_loiter_collection_def_id ); default: Debug_Say(( "Unrecognized Human Variable chunkID\n" )); break; } cload.Close_Micro_Chunk(); } WWASSERT( HumanPhys != NULL ); if ( HumanPhys != NULL ) { REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&HumanPhys ); } break; default: Debug_Say(( "Unrecognized Human chunkID\n" )); break; } cload.Close_Chunk(); } if ( human_anim_override_def_id != 0 ) { Set_Human_Anim_Override( human_anim_override_def_id ); } if ( human_loiter_collection_def_id != 0 ) { Set_Human_Loiter_Collection( human_loiter_collection_def_id ); } return true; } /* ** */ void HumanStateClass::Update_Weapon( WeaponClass * weapon, bool new_weapon ) { WWPROFILE( "Human Weapon" ); Update_Recoil(weapon); int new_hold_style = WeaponHoldStyle; WeaponFired = false; if ( weapon ) { if ( weapon->Is_Reloading() ) { Raise_Weapon(); } weapon->Set_Safety( WeaponHoldStyle > WEAPON_HOLD_STYLE_EMPTY_HANDS ); // Only humans can cansider weapons firing in state if ( weapon->Get_Owner() && weapon->Get_Owner()->As_SoldierGameObj() && weapon->Get_Owner()->As_SoldierGameObj()->Is_Human_Controlled() ) { WeaponFired = weapon->Is_Firing(); } if ( weapon->Is_Triggered() || new_weapon || Get_State_Flag( CROUCHED_FLAG ) ) { if ( weapon->Is_Safety_Set() ) { // Don't blend anim when un-safetying NoAnimBlend = true; } Raise_Weapon(); // Reset the drop timer new_hold_style = weapon->Get_Style(); // Take the new style } if ( WeaponHoldTimer > 0 ) { if ( WeaponHoldStyle > WEAPON_HOLD_STYLE_EMPTY_HANDS ) { new_hold_style = weapon->Get_Style(); // Take the new style } } } else { new_hold_style = WEAPON_HOLD_STYLE_EMPTY_HANDS; } if ( new_hold_style == WeaponHoldStyle ) { return; } WeaponHoldStyle = new_hold_style; if ( !StateLocked ) { Update_Animation(); } } void HumanStateClass::Update_Aiming( float tilt, float turn ) { if ( (AimingTilt == tilt) && (AimingTurn == turn) ) { return; } AimingTilt = tilt; AimingTurn = turn; if ( !StateLocked ) { Update_Animation(); } } void HumanStateClass::Update_Recoil(WeaponClass * weapon) { // Programatic Recoil System. This code needs to run once per frame. if ((weapon != NULL) && (weapon->Is_Firing())) { // Set our recoil timer and capture all of the necessary bones // I'm copying all necessary data out of the weapon in case the weapon goes // away before our recoil is finished. RecoilTimer = weapon->Get_Recoil_Time(); RecoilScale = weapon->Get_Recoil_Scale(); if (RecoilTimer > 0.0f) { RecoilScale *= 1.0f / RecoilTimer; _TheRecoiler.Capture_Bones(HumanPhys->Peek_Model()); } } if (RecoilTimer > 0.0f) { // Apply the recoil effect. Matrix3D recoil_tm(1); recoil_tm.Rotate_Z( HumanPhys->Get_Facing() ); recoil_tm.Rotate_Y( -AimingTilt ); float recoil_scale = RecoilScale * RecoilTimer; _TheRecoiler.Apply_Recoil( recoil_tm,HumanPhys->Peek_Model(),recoil_scale); // Decrement the recoil timer and release the bones if it expires RecoilTimer -= TimeManager::Get_Frame_Seconds(); if (RecoilTimer <= 0.0f) { RecoilTimer = 0.0f; _TheRecoiler.Release_Bones(HumanPhys->Peek_Model()); } } } void HumanStateClass::Set_State( HumanStateType state, int sub_state ) { // Special case for death if (( State == DEATH ) || ( State == DESTROY )) { if ( state != DESTROY ) { return; } } if ( ( State != DEATH ) && ( state == DEATH ) ) { StateLocked = false; } if ( StateLocked ) { if ( state != DEATH ) { // Temp Test // Debug_Say(( "State is Locked. Can't change from %d to %d\n", State, state )); // return; } #pragma MESSAGE( "StateLocked Hack" ) if ( !IS_SOLOPLAY ) { // E3 HACK StateLocked = false; } } if ( State == state && SubState == sub_state ) { if (CombatManager::I_Am_Server()) { Debug_Say(( "Already in this state\n" )); } return; } // Debug_Say(( "%p Set State %d, %x from %d\n", this, state, sub_state, State )); State = state; SubState = sub_state; StateTimer = 0; if (( State == LADDER ) || ( State == IN_VEHICLE ) || ( State == TRANSITION ) || ( State == TRANSITION_COMPLETE ) || ( State == DEBUG_FLY ) ) { HumanPhys->Enable_User_Control( true ); } else { HumanPhys->Enable_User_Control( false ); } // Turn off shadows in vehicles #pragma message ("(gth) shadow review hacking") #if 0 if ( State == IN_VEHICLE ) { HumanPhys->Enable_Shadow_Generation( false ); } else { HumanPhys->Enable_Shadow_Generation( true ); } #endif if ( ( State == IN_VEHICLE ) || ( State == TRANSITION ) || ( State == TRANSITION_COMPLETE ) ) { HumanPhys->Set_Collision_Group( BULLET_ONLY_COLLISION_GROUP ); HumanPhys->Set_Immovable( true ); } else if ( ( State == DESTROY ) || ( State == DEATH ) ) { HumanPhys->Set_Collision_Group( TERRAIN_ONLY_COLLISION_GROUP ); HumanPhys->Set_Immovable( true ); } else { HumanPhys->Set_Collision_Group( SOLDIER_COLLISION_GROUP ); HumanPhys->Set_Immovable( false ); } Update_Animation(); } bool HumanStateClass::Is_State_Interruptable( void ) { return (State == UPRIGHT) || (State == WOUNDED) || (State == LAND) || (State == LOITER) || (State == ANIMATION); } #define ADD_CASE(exp) case exp: return #exp; break; const char * HumanStateClass::Get_State_Name( void ) { switch (State) { ADD_CASE(UPRIGHT); ADD_CASE(LAND); ADD_CASE(ANIMATION); ADD_CASE(WOUNDED); ADD_CASE(LOITER); ADD_CASE(AIRBORNE); ADD_CASE(DIVE); ADD_CASE(DEATH); ADD_CASE(LADDER); ADD_CASE(IN_VEHICLE); ADD_CASE(TRANSITION); ADD_CASE(TRANSITION_COMPLETE); ADD_CASE(DESTROY); ADD_CASE(DEBUG_FLY); ADD_CASE(ON_FIRE); ADD_CASE(ON_CHEM); ADD_CASE(ON_ELECTRIC); ADD_CASE(ON_CNC_FIRE); ADD_CASE(ON_CNC_CHEM); ADD_CASE(LOCKED_ANIMATION); default: WWASSERT(0); return ""; // to avoid compiler warning } } void HumanStateClass::Set_Sub_State( int sub_state ) { if ( Is_Sub_State_Adjustable() ) { if ( SubState != sub_state ) { WWASSERT(sub_state >= 0 && sub_state <= HIGHEST_HUMAN_SUB_STATE); SubState = sub_state; Update_Animation(); } } else { Debug_Say(( "Can't adjust state %s", Get_State_Name() )); } } bool HumanStateClass::Is_Sub_State_Adjustable( void ) { return (State == UPRIGHT) || (State == LADDER); } void HumanStateClass::Start_Transition_Animation( const char * anim_name, bool blend ) { if ( StateLocked ) { Debug_Say(( "State is Locked. Can't Start Transition Anim %s\n", anim_name )); return; } //Debug_Say(("Start_Transition_Animation %s\n", anim_name)); if (( Get_State() == DEATH ) || ( Get_State() == DESTROY ) ) { return; } Set_State( TRANSITION ); float blend_time = blend ? 0.2 : 0; AnimControl->Set_Animation( anim_name, blend_time ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); AnimControl->Update( 0 ); // update StateLocked = true; } void HumanStateClass::Start_Scripted_Animation( const char * anim_name, bool blend, bool looping ) { #if 0 if ( StateLocked ) { Debug_Say(( "State is Locked. Can't Start Transition Anim %s\n", anim_name )); return; } #endif //Debug_Say(("Start_Scripted_Animation %s\n", anim_name)); // We used to not start a scripted anim when wounded, but then the scripts couldn't set // up custom events well. So I am gonna try to remove the wounded check // 8/17/01 Byon // if (( Get_State() == DEATH ) || ( Get_State() == DESTROY ) || ( Get_State() == WOUNDED )) { if (( Get_State() == DEATH ) || ( Get_State() == DESTROY ) ) { return; } Set_State( ANIMATION ); float blend_time = blend ? 0.2 : 0; AnimControl->Set_Animation( anim_name, blend_time ); AnimControl->Set_Mode( looping ? ANIM_MODE_LOOP : ANIM_MODE_ONCE ); AnimControl->Update( 0 ); // update StateLocked = true; } void HumanStateClass::Stop_Scripted_Animation( void ) { if ( State != ANIMATION && State != LOCKED_ANIMATION ) { // Debug_Say(( "Not in a Scripted Animation to stop\n" )); // Better clear the locked state. This was keeping us in the place beacon mode StateLocked = false; return; } StateLocked = false; Set_State( UPRIGHT ); // AnimControl->Lock_Animation(); } void HumanStateClass::Force_Animation( const char * anim_name, bool blend ) { //Debug_Say(( "Forcing Animation to %s\n", anim_name )); float blend_time = blend ? 0.2 : 0; AnimControl->Set_Animation( anim_name, blend_time ); AnimControl->Update( 0 ); // update } /* ** */ typedef enum { LEG_STYLE_STAND, // A0 LEG_STYLE_RUN_FORWARD, // A1 LEG_STYLE_RUN_BACKWARD, // A2 LEG_STYLE_RUN_LEFT, // A3 LEG_STYLE_RUN_RIGHT, // A4 LEG_STYLE_TURN_LEFT, // A5 LEG_STYLE_TURN_RIGHT, // A6 LEG_STYLE_WALK_FORWARD, // B1 LEG_STYLE_WALK_BACKWARD, // B2 LEG_STYLE_WALK_LEFT, // B3 LEG_STYLE_WALK_RIGHT, // B4 LEG_STYLE_CROUCH, // C0 LEG_STYLE_CROUCH_MOVE_FORWARD, // C1 LEG_STYLE_CROUCH_MOVE_BACKWARD, // C2 LEG_STYLE_CROUCH_MOVE_LEFT, // C3 LEG_STYLE_CROUCH_MOVE_RIGHT, // C4 LEG_STYLE_CROUCH_TURN_LEFT, // C3 LEG_STYLE_CROUCH_TURN_RIGHT, // C4 LEG_STYLE_JUMP_UP, // D0 LEG_STYLE_JUMP_FORWARD, // D1 LEG_STYLE_JUMP_BACKWARD, // D2 LEG_STYLE_JUMP_LEFT, // D3 LEG_STYLE_JUMP_RIGHT, // D4 } HUMAN_ANIM_LEG_STYLE; static const char * LegAnimNames[] = { "A0", // LEG_STYLE_STAND, "A1", // LEG_STYLE_RUN_FORWARD, "A2", // LEG_STYLE_RUN_BACKWARD, "A3", // LEG_STYLE_RUN_LEFT, "A4", // LEG_STYLE_RUN_RIGHT, "A5", // LEG_STYLE_TURN_LEFT, "A6", // LEG_STYLE_TURN_RIGHT, "B1", // LEG_STYLE_WALK_FORWARD, "B2", // LEG_STYLE_WALK_BACKWARD, "B3", // LEG_STYLE_WALK_LEFT, "B4", // LEG_STYLE_WALK_RIGHT, "C0", // LEG_STYLE_CROUCH, "C1", // LEG_STYLE_CROUCH_MOVE_FORWARD, "C2", // LEG_STYLE_CROUCH_MOVE_BACKWARD, "C3", // LEG_STYLE_CROUCH_MOVE_LEFT, "C4", // LEG_STYLE_CROUCH_MOVE_RIGHT, "C5", // LEG_STYLE_CROUCH_TURN_LEFT, "C6", // LEG_STYLE_CROUCH_TURN_RIGHT, "J0", // LEG_STYLE_JUMP_UP, "J1", // LEG_STYLE_JUMP_FORWARD, "J2", // LEG_STYLE_JUMP_BACKWARD, "J3", // LEG_STYLE_JUMP_LEFT, "J4", // LEG_STYLE_JUMP_RIGHT, }; static const char * _weapon_style_names[ NUM_WEAPON_HOLD_STYLES ] = { "A0", //WEAPON_HOLD_STYLE_C4 = 0, "A0", //WEAPON_HOLD_STYLE_NOT_USED, "C2", //WEAPON_HOLD_STYLE_AT_SHOULDER, // 2 "D2", //WEAPON_HOLD_STYLE_AT_HIP, "E2", //WEAPON_HOLD_STYLE_LAUNCHER, "F2", //WEAPON_HOLD_STYLE_HANDGUN, "A0", //WEAPON_HOLD_STYLE_BEACON "A0", //WEAPON_HOLD_STYLE_EMPTY_HANDS, "B0", //WEAPON_HOLD_STYLE_AT_CHEST, "A0", //WEAPON_HOLD_STYLE_HANDS_DOWN, }; static const char * _dive_anims[ 4 * 2 ] = { // Forwrd Anims "S_A_HUMAN.H_A_SLD1_01", "S_A_HUMAN.H_A_SLD1_02", // Backward anims "S_A_HUMAN.H_A_SLD2_01", "S_A_HUMAN.H_A_SLD2_02", // Left anims "S_A_HUMAN.H_A_SLD3_01", "S_A_HUMAN.H_A_SLD3_02", // Right Anims "S_A_HUMAN.H_A_SLD4_01", "S_A_HUMAN.H_A_SLD4_02", }; // Weapons style, weapon action, recoil, blend, vehicle, mix/math, aiming tilt void HumanStateClass::Update_Animation( void ) { WWPROFILE( "Human Animation" ); // no updates for visceroids if ( AnimControl->Get_Skeleton() == 'V' ) { StateLocked = true; return; } if ( StateLocked ) { // Debug_Say(( "ERROR: updating animation when locked\n" )); return; // if you change your animn when locked, death state may clear a scripted anim } int hold_style = WeaponHoldStyle; // Setup animation for state, substate, weapon, tilt, etc. if ( (State == UPRIGHT) || (State == AIRBORNE) ) { // determine leg style int leg_style = LEG_STYLE_STAND; if ( State == AIRBORNE ) { leg_style = LEG_STYLE_JUMP_UP; if ( SubState & SUB_STATE_LEFT ) leg_style = LEG_STYLE_JUMP_LEFT; if ( SubState & SUB_STATE_RIGHT ) leg_style = LEG_STYLE_JUMP_RIGHT; if ( SubState & SUB_STATE_FORWARD ) leg_style = LEG_STYLE_JUMP_FORWARD; if ( SubState & SUB_STATE_BACKWARD ) leg_style = LEG_STYLE_JUMP_BACKWARD; } else { if ( SubState & SUB_STATE_TURN_LEFT ) leg_style = LEG_STYLE_TURN_LEFT; if ( SubState & SUB_STATE_TURN_RIGHT ) leg_style = LEG_STYLE_TURN_RIGHT; if ( SubState & SUB_STATE_LEFT ) leg_style = LEG_STYLE_RUN_LEFT; if ( SubState & SUB_STATE_RIGHT ) leg_style = LEG_STYLE_RUN_RIGHT; if ( SubState & SUB_STATE_FORWARD ) leg_style = LEG_STYLE_RUN_FORWARD; if ( SubState & SUB_STATE_BACKWARD ) leg_style = LEG_STYLE_RUN_BACKWARD; if ( Get_State_Flag( CROUCHED_FLAG ) ) { // Tend to hold at chest when crouched if ( ( hold_style == WEAPON_HOLD_STYLE_HANDS_DOWN ) || ( hold_style == WEAPON_HOLD_STYLE_C4 ) || ( hold_style == WEAPON_HOLD_STYLE_BEACON ) ) { hold_style = WEAPON_HOLD_STYLE_AT_CHEST; } leg_style += LEG_STYLE_CROUCH - LEG_STYLE_STAND; } else if ( SubState & SUB_STATE_SLOW ) { if ( ( leg_style >= LEG_STYLE_RUN_FORWARD ) && ( leg_style <= LEG_STYLE_RUN_RIGHT ) ) { leg_style += LEG_STYLE_WALK_FORWARD - LEG_STYLE_RUN_FORWARD; } } } const char * leg_anim_name = LegAnimNames[ leg_style ]; const char * torso_anim_name = _weapon_style_names[hold_style]; bool single_anim = true; float blend_time = 0.2f; if ( NoAnimBlend ) { blend_time = 0; NoAnimBlend = false; } if ( torso_anim_name[1] == '2' ) { // Lets try aiming StringClass anim1_name(0,true); StringClass anim2_name(0,true); StringClass anim3_name(0,true); anim1_name.Format( "S_A_HUMAN.H_A_%c1%s", 'A' + hold_style, leg_anim_name ); anim2_name.Format( "S_A_HUMAN.H_A_%c2%s", 'A' + hold_style, leg_anim_name ); anim3_name.Format( "S_A_HUMAN.H_A_%c3%s", 'A' + hold_style, leg_anim_name ); // See if we have the tilting data HAnimClass * anim = WW3DAssetManager::Get_Instance()->Get_HAnim( anim3_name ); if ( anim != NULL ) { anim->Release_Ref(); single_anim = false; float tilt_blend = WWMath::Clamp( (AimingTilt / DEG_TO_RADF( 65 )), -1, 1 ); float frame = AnimControl->Get_Frame(); // Maintain the frame number for moving if ( tilt_blend < 0 ) { AnimControl->Set_Animation( anim1_name, anim2_name, 1+tilt_blend, blend_time ); } else { AnimControl->Set_Animation( anim3_name, anim2_name, 1-tilt_blend, blend_time ); } AnimControl->Set_Mode( ANIM_MODE_LOOP, frame ); } } if ( single_anim ) { StringClass anim_name(0,true); anim_name.Format( "S_A_HUMAN.H_A_%s%s", torso_anim_name, leg_anim_name ); // Human Anim Override if ( HumanAnimOverride != NULL ) { if ( hold_style == WEAPON_HOLD_STYLE_EMPTY_HANDS ) { if ( leg_style == LEG_STYLE_RUN_FORWARD ) { anim_name = HumanAnimOverride->RunEmptyHands; } if ( leg_style == LEG_STYLE_WALK_FORWARD ) { anim_name = HumanAnimOverride->WalkEmptyHands; } } if ( hold_style == WEAPON_HOLD_STYLE_AT_CHEST ) { if ( leg_style == LEG_STYLE_RUN_FORWARD ) { anim_name = HumanAnimOverride->RunAtChest; } if ( leg_style == LEG_STYLE_WALK_FORWARD ) { anim_name = HumanAnimOverride->WalkAtChest; } } if ( hold_style == WEAPON_HOLD_STYLE_AT_HIP ) { if ( leg_style == LEG_STYLE_RUN_FORWARD ) { anim_name = HumanAnimOverride->RunAtHip; } if ( leg_style == LEG_STYLE_WALK_FORWARD ) { anim_name = HumanAnimOverride->WalkAtHip; } } } // Saftey check anim // Debug_Say(( "Anim name %s\n", (const char *)anim_name )); // float frame = AnimControl->Get_Frame(); // Maintain the frame number for moving AnimControl->Set_Animation( anim_name, blend_time ); // AnimControl->Set_Mode( ANIM_MODE_LOOP, frame ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } } else if ( State == DIVE ) { const char * anim_name = NULL; #if 0 if ( SubState & SUB_STATE_LEFT ) anim_name = "S_A_HUMAN.H_A_DIV3"; if ( SubState & SUB_STATE_RIGHT ) anim_name = "S_A_HUMAN.H_A_DIV4"; if ( SubState & SUB_STATE_FORWARD ) anim_name = "S_A_HUMAN.H_A_DIV1"; if ( SubState & SUB_STATE_BACKWARD ) anim_name = "S_A_HUMAN.H_A_DIV2"; #else int offset = FreeRandom.Get_Int( 2 ); if ( !IS_SOLOPLAY ) { offset = 0; } if ( SubState & SUB_STATE_FORWARD ) offset += 0; if ( SubState & SUB_STATE_BACKWARD ) offset += 2; if ( SubState & SUB_STATE_LEFT ) offset += 4; if ( SubState & SUB_STATE_RIGHT ) offset += 6; anim_name = _dive_anims[offset]; #endif AnimControl->Set_Animation( anim_name, 0.2f ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); StateLocked = true; } else if ( State == LAND ) { int dir = 0; if ( SubState & SUB_STATE_LEFT ) dir = 3; if ( SubState & SUB_STATE_RIGHT ) dir = 4; if ( SubState & SUB_STATE_FORWARD ) dir = 1; if ( SubState & SUB_STATE_BACKWARD ) dir = 2; StringClass anim_name(0,true); anim_name.Format( "S_A_HUMAN.H_A_A0L%d", dir ); AnimControl->Set_Animation( anim_name, 0.2f ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); } else if ( State == WOUNDED ) { AnimControl->Set_Animation( Get_Wound_Anim( SubState ), 0.2f ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); } else if ( State == DEATH ) { AnimControl->Set_Animation( Get_Death_Anim( SubState ), 0.2f ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); StateLocked = true; } else if ( State == LADDER ) { const char * anim_name = "S_A_HUMAN.H_A_412A"; if ( SubState & SUB_STATE_UP ) anim_name = "S_A_HUMAN.H_A_422A"; if ( SubState & SUB_STATE_DOWN ) anim_name = "S_A_HUMAN.H_A_432A"; AnimControl->Set_Animation( anim_name, 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == ANIMATION ) { } else if ( State == LOITER ) { } else if ( State == DESTROY ) { } else if ( State == TRANSITION ) { } else if ( State == TRANSITION_COMPLETE ) { } else if ( State == ON_FIRE ) { AnimControl->Set_Animation( "S_A_HUMAN.H_A_FLMA", 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == ON_CHEM ) { AnimControl->Set_Animation( "S_A_HUMAN.h_a_6x01", 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == ON_CNC_FIRE ) { AnimControl->Set_Animation( "S_A_HUMAN.H_A_FLMA", 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == ON_CNC_CHEM ) { AnimControl->Set_Animation( "S_A_HUMAN.h_a_6x01", 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == ON_ELECTRIC ) { AnimControl->Set_Animation( "S_A_HUMAN.h_a_6x05", 0.2f ); AnimControl->Set_Mode( ANIM_MODE_LOOP ); } else if ( State == DEBUG_FLY ) { } else { Debug_Say(( "Uncoded Human State %d\n", State )); AnimControl->Set_Animation( (const char *)NULL ); } } #define MOVING_THRESHHOLD 0.2 //#define WALKING_THRESHHOLD 4.5 #define WALKING_THRESHHOLD 3.21f void HumanStateClass::Reset_Loiter_Delay( void ) { LoiterDelay = FreeRandom.Get_Float( 6 ) - 3; } // Must deal with LAND, ANIMATIONS, DEATH, WOUNDS, TRANSITIONS, JUMP, DEATH FALLS, LADDER void HumanStateClass::Update_State( void ) { WWPROFILE( "Human State" ); StateTimer += TimeManager::Get_Frame_Seconds(); if ( AnimControl && AnimControl->Get_Skeleton() == 'V' ) { LoitersAllowed = false; } if ( State == UPRIGHT && LoitersAllowed ) { // Don't loiter when crouched or moving if ( SubState != 0 ) { Reset_Loiter_Delay(); } LoiterDelay += TimeManager::Get_Frame_Seconds(); HumanLoiterGlobalSettingsDef * loiter_def = HumanLoiterCollection; if ( loiter_def == NULL ) { loiter_def = HumanLoiterGlobalSettingsDef::Get_Default_Loiters(); } #if 0 // loiter based on holding a weapon if ( WeaponHoldStyle == WEAPON_HOLD_STYLE_EMPTY_HANDS ) { loiter_def = HumanLoiterGlobalSettingsDef::Get_Weaponless_Loiters(); } else { loiter_def = HumanLoiterGlobalSettingsDef::Get_Weapon_Loiters(); } #endif if ( loiter_def != NULL ) { if ( LoiterDelay > loiter_def->Get_Activation_Delay() ) { Set_State( LOITER, Get_Sub_State() ); StringClass anim(loiter_def->Pick_Animation(),true); // Debug_Say(( "Start loiter %s\n", anim )); if ( !anim.Is_Empty() ) { StringClass new_anim(true); ::Strip_Path_From_Filename( new_anim, anim ); // remove the .W3D if ( new_anim.Get_Length() >= 5 ) { new_anim.Erase( new_anim.Get_Length()-4, 4 ); } AnimControl->Set_Animation( new_anim, 0.2f ); AnimControl->Set_Mode( ANIM_MODE_ONCE ); AnimControl->Update( 0 ); // update } } } else { Debug_Say(( "Failed to find Loiter Def\n" )); } } else { Reset_Loiter_Delay(); } // Get out of locked states if ( AnimControl->Is_Complete() ) { // Time to unlock // if ( StateLocked && State != LOCKED_ANIMATION ) { if ( (StateLocked && State != LOCKED_ANIMATION) || (State == DEATH) ) { StateLocked = false; if ( State == DIVE ) { Set_State( UPRIGHT ); } else if ( State == ANIMATION ) { Set_State( UPRIGHT ); } else if ( State == DEATH ) { Set_State( DESTROY ); TransitionEffectClass * effect = CombatMaterialEffectManager::Get_Death_Effect(); this->HumanPhys->Add_Effect_To_Me(effect); REF_PTR_RELEASE(effect); } else if ( State == TRANSITION ) { Set_State( TRANSITION_COMPLETE ); } else if ( State != LOCKED_ANIMATION ) { Debug_Say(( "Unsupported locked state %s\n", Get_State_Name() )); } } else { // Debug_Say(( "Anim Complete %s\n", Get_State_Name() )); if ( State == LOITER ) { Set_State( UPRIGHT ); } else if ( State == LAND ) { Set_State( UPRIGHT, Get_Sub_State() ); } else if ( State == WOUNDED ) { Set_State( UPRIGHT ); } } } /* ** Handle Jump and landing */ if ( !HumanPhys->Is_In_Contact() ) { // If I am not in contact with the ground if ( State == UPRIGHT ) { // If I am currently UPRIGHT Begin_Jump(); // Begin a jump } } else if ( State == AIRBORNE ) { // If I am in contact, and in the , I just landed Complete_Jump(); // So complete the jump } if ( WeaponHoldTimer > 0 && !Get_State_Flag( CROUCHED_FLAG ) ) { Reset_Loiter_Delay(); WeaponHoldTimer -= TimeManager::Get_Frame_Seconds(); if ( WeaponHoldTimer <= 0 ) { if ( WeaponHoldStyle == WEAPON_HOLD_STYLE_HANDGUN || WeaponHoldStyle == WEAPON_HOLD_STYLE_C4 || WeaponHoldStyle == WEAPON_HOLD_STYLE_BEACON ) { WeaponHoldStyle = WEAPON_HOLD_STYLE_HANDS_DOWN; // Lower the weapon } else { WeaponHoldStyle = WEAPON_HOLD_STYLE_AT_CHEST; // Lower the weapon } Update_Animation(); } } } /* ** */ void HumanStateClass::Post_Think( void ) { // Update sub_state per movement // do it for upright, land, ladder, airborne, if ( Is_Sub_State_Adjustable() || Is_State_Interruptable() ) { // Update the SubState int new_sub_state = 0; // Get our current move vector Vector3 move_vector; HumanPhys->Get_Animation_Move( &move_vector ); if ( TimeManager::Get_Frame_Seconds() > 0 ) { move_vector /= TimeManager::Get_Frame_Seconds(); } move_vector = HumanPhys->Get_Transform().Inverse_Rotate_Vector( move_vector ); // When walking running diagonally, use forward/backward legs. // Unless you are crouched, then use straffe legs float direction_ratio = 0.75f; if ( Get_State_Flag( CROUCHED_FLAG ) ) { direction_ratio = 2; } // Convert to SubMode if ( WWMath::Fabs( move_vector[0] ) > direction_ratio * WWMath::Fabs( move_vector[1] ) ) { if ( move_vector[0] > MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_FORWARD; else if ( move_vector[0] < -MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_BACKWARD; } else { if ( move_vector[1] > MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_LEFT; else if ( move_vector[1] < -MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_RIGHT; } if ( new_sub_state == 0 ) { if ( move_vector[2] > MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_UP; else if ( move_vector[2] < -MOVING_THRESHHOLD ) new_sub_state |= SUB_STATE_DOWN; } if ( new_sub_state != 0 ) { if ( move_vector.Length() < WALKING_THRESHHOLD ) new_sub_state |= SUB_STATE_SLOW; //Debug_Say(( "%f %f\n", move_vector.Length(), WALKING_THRESHHOLD )); } #if 0 // No turn anims!!! // Get our current turn vector if ( TurnVelocity > 0 ) new_sub_state |= SUB_STATE_TURN_LEFT; else if ( TurnVelocity < 0 ) new_sub_state |= SUB_STATE_TURN_RIGHT; TurnVelocity = 0; #endif // Get him out of WOUNDED, LAND, LOITER states if moving or shooting if ( Is_State_Interruptable() && Get_State() != UPRIGHT ) { // if ( new_sub_state != 0 || WeaponState > WeaponClass::STATE_READY ) { if ( new_sub_state != 0 || WeaponFired ) { if ( Get_State() == LAND && Get_Sub_State() == new_sub_state ) { // don't interrupt lands for the same direction } else { // Debug_Say(( "Interrupt State %s\n", Get_State_Name() )); Set_State( UPRIGHT ); } } } if ( Is_Sub_State_Adjustable() ) { if ( new_sub_state != Get_Sub_State() ) { Set_Sub_State( (HumanSubStateType)new_sub_state ); } #if 0 // Disable all leg twisting // =================================================================== // LEG TWIST!!!!! // Don't leg twist for crouched if ( Get_State() == UPRIGHT && !Get_State_Flag( CROUCHED_FLAG ) ) { float legs_rotation = 0; // Compare the facing to the motion, set leg_racing to the difference if ( move_vector.Length() > 0 ) { float move_direction = WWMath::Atan2( -move_vector.Y, move_vector.X ); if ( new_sub_state & SUB_STATE_FORWARD ) { legs_rotation = -move_direction; } else if ( new_sub_state & SUB_STATE_BACKWARD ) { legs_rotation = -move_direction + DEG_TO_RAD( 180 ); } else if ( new_sub_state & SUB_STATE_LEFT ) { legs_rotation = -move_direction + DEG_TO_RAD( 270 ); } else if ( new_sub_state & SUB_STATE_RIGHT ) { legs_rotation = -move_direction + DEG_TO_RAD( 90 ); } legs_rotation = WWMath::Wrap( legs_rotation, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) ); // legs_rotation = WWMath::Clamp( legs_rotation, DEG_TO_RADF( -45 ), DEG_TO_RADF( 45 ) ); legs_rotation = WWMath::Clamp( legs_rotation, DEG_TO_RADF( -30 ), DEG_TO_RADF( 30 ) ); if ( WWMath::Fabs( legs_rotation ) < DEG_TO_RAD( 25 ) ) { legs_rotation = 0; } } // Move LegRotation toward leg_rotation float rot_diff = legs_rotation - LegRotation; float max_mov = DEG_TO_RAD( 180 ) * TimeManager::Get_Frame_Seconds(); // float max_mov = DEG_TO_RAD( 90 ) * TimeManager::Get_Frame_Seconds(); rot_diff = WWMath::Clamp( rot_diff, -max_mov, max_mov ); LegRotation += rot_diff; legs_rotation = LegRotation; // I'm making this staic for now, because all human // skeletons have the bone at the same index static int root_bone = -1; if ( root_bone == -1 ) { // Get root bone index root_bone = HumanPhys->Peek_Model()->Get_Bone_Index( "c spine" ); } static int torso_bone = -1; if ( torso_bone == -1 ) { // Get torso bone index torso_bone = HumanPhys->Peek_Model()->Get_Bone_Index( "c spine1" ); } if ( legs_rotation != 0 ) { WWASSERT( root_bone != -1 ); WWASSERT( torso_bone != -1 ); if ( !HumanPhys->Peek_Model()->Is_Bone_Captured( root_bone ) ) { HumanPhys->Peek_Model()->Capture_Bone( root_bone ); } if ( !HumanPhys->Peek_Model()->Is_Bone_Captured( torso_bone ) ) { HumanPhys->Peek_Model()->Capture_Bone( torso_bone ); } Matrix3D root_adjust(1); // adjust it root_adjust.Rotate_X( legs_rotation ); HumanPhys->Peek_Model()->Control_Bone( root_bone, root_adjust ); Matrix3D legs_adjust(1); // adjust it legs_adjust.Rotate_X( -legs_rotation ); HumanPhys->Peek_Model()->Control_Bone( torso_bone, legs_adjust ); } else { // no adjustment, release if ( HumanPhys->Peek_Model()->Is_Bone_Captured( root_bone ) ) { HumanPhys->Peek_Model()->Release_Bone( root_bone ); } if ( HumanPhys->Peek_Model()->Is_Bone_Captured( torso_bone ) ) { HumanPhys->Peek_Model()->Release_Bone( torso_bone ); } } } #endif } // Scale animation speed float ideal_speed = 0; if ( !(new_sub_state & SUB_STATE_SLOW) ) { if ( new_sub_state & SUB_STATE_FORWARD ) ideal_speed = 5.5f; if ( new_sub_state & SUB_STATE_BACKWARD ) ideal_speed = 4.5f; if ( new_sub_state & SUB_STATE_LEFT ) ideal_speed = 4.5f; if ( new_sub_state & SUB_STATE_RIGHT ) ideal_speed = 5.5f; } else { if ( new_sub_state & SUB_STATE_FORWARD ) ideal_speed = 1.6f; if ( new_sub_state & SUB_STATE_BACKWARD ) ideal_speed = 1.5f; if ( new_sub_state & SUB_STATE_LEFT ) ideal_speed = 1.5f; if ( new_sub_state & SUB_STATE_RIGHT ) ideal_speed = 1.6f; } if ( State == LADDER ) { if ( new_sub_state & SUB_STATE_UP ) ideal_speed = 0.15f; if ( new_sub_state & SUB_STATE_DOWN ) ideal_speed = 0.15f; } // Turning is at speed 1 bool turning = ( new_sub_state & (SUB_STATE_TURN_LEFT | SUB_STATE_TURN_RIGHT ) && !( new_sub_state & (SUB_STATE_FORWARD | SUB_STATE_BACKWARD ))); if ( !turning && ideal_speed != 0 ) { // Get Anim_Speed_Scale Vector3 vel; HumanPhys->Get_Animation_Move( &vel ); if ( TimeManager::Get_Frame_Seconds() > 0 ) { vel /= TimeManager::Get_Frame_Seconds(); } float speed = WWMath::Clamp( vel.Length() / ideal_speed, 0.33f, 3 ); AnimControl->Set_Anim_Speed_Scale( speed ); } else { AnimControl->Set_Anim_Speed_Scale( 1 ); } HumanPhys->Reset_Animation_Move(); } } /* ** */ bool HumanStateClass::Get_Leg_Mode( void ) { return AnimControl->Get_Progress() > 0.5f; } /* ** */ struct BoneToOuchType { const char * bone_name; int ouch_type; }; BoneToOuchType _BoneToOuchTypeList[] = { { "K_HEAD", HumanStateClass::HEAD_FROM_BEHIND }, { "K_NECK", HumanStateClass::HEAD_FROM_BEHIND }, { "K_CHEST", HumanStateClass::HEAD_FROM_BEHIND }, { "K_ABDOMEN", HumanStateClass::TORSO_FROM_BEHIND }, { "K_PELVIS", HumanStateClass::GROIN }, { "K_L THIGH", HumanStateClass::LEFT_LEG_FROM_BEHIND }, { "K_L CALF", HumanStateClass::LEFT_LEG_FROM_BEHIND }, { "K_L FOOT", HumanStateClass::LEFT_LEG_FROM_BEHIND }, { "K_L HAND", HumanStateClass::LEFT_ARM_FROM_BEHIND }, { "K_L FOREARM", HumanStateClass::LEFT_ARM_FROM_BEHIND }, { "K_L UPPERARM", HumanStateClass::LEFT_ARM_FROM_BEHIND }, { "K_R THIGH", HumanStateClass::RIGHT_LEG_FROM_BEHIND }, { "K_R CALF", HumanStateClass::RIGHT_LEG_FROM_BEHIND }, { "K_R FOOT", HumanStateClass::RIGHT_LEG_FROM_BEHIND }, { "K_R HAND", HumanStateClass::RIGHT_ARM_FROM_BEHIND }, { "K_R FOREARM", HumanStateClass::RIGHT_ARM_FROM_BEHIND }, { "K_R UPPERARM", HumanStateClass::RIGHT_ARM_FROM_BEHIND }, }; #define BONE_LIST_COUNT ( sizeof(_BoneToOuchTypeList) / sizeof(_BoneToOuchTypeList[0]) ) int HumanStateClass::Get_Ouch_Type( const Vector3 & direction, const char * collision_box_name ) { // Initialize ouch_type to a default value int ouch_type = TORSO_FROM_FRONT; const char * base_name = NULL; if ( collision_box_name != NULL ) { base_name = ::strchr( collision_box_name, '.' ); } else { return ouch_type; } if ( base_name != NULL ) { base_name++; for ( int i = 0; i < BONE_LIST_COUNT; i++ ) { if ( ::strcmp( _BoneToOuchTypeList[i].bone_name, base_name ) == 0 ) { ouch_type = _BoneToOuchTypeList[i].ouch_type; } } } if ( ouch_type != -1 ) { // Set direction Vector3 relative_direction = HumanPhys->Get_Transform().Inverse_Rotate_Vector( direction ); if ( ouch_type != HumanStateClass::GROIN ) { // but not for the groin shot if ( relative_direction.X < 0 ) { ouch_type += 1; } } } else { Debug_Say(( "Bad human collision box name %s\n", collision_box_name )); } return ouch_type; } /* ** */ const char * _WoundAnims[] = { "S_A_HUMAN.H_A_811A", // HEAD_FROM_BEHIND, "S_A_HUMAN.H_A_812A", // HEAD_FROM_FRONT, "S_A_HUMAN.H_A_821A", // TORSO_FROM_BEHIND, "S_A_HUMAN.H_A_822A", // TORSO_FROM_FRONT, "S_A_HUMAN.H_A_831A", // LEFT_ARM_FROM_BEHIND, "S_A_HUMAN.H_A_832A", // LEFT_ARM_FROM_FRONT, "S_A_HUMAN.H_A_841A", // RIGHT_ARM_FROM_BEHIND, "S_A_HUMAN.H_A_842A", // RIGHT_ARM_FROM_FRONT, "S_A_HUMAN.H_A_851A", // LEFT_LEG_FROM_BEHIND, "S_A_HUMAN.H_A_852A", // LEFT_LEG_FROM_FRONT, "S_A_HUMAN.H_A_861A", // RIGHT_LEG_FROM_BEHIND, "S_A_HUMAN.H_A_862A", // RIGHT_LEG_FROM_FRONT, "S_A_HUMAN.H_A_871A", // GROIN, }; const char * HumanStateClass::Get_Wound_Anim( int ouch_type ) { return _WoundAnims[ ouch_type ]; } /* ** */ const char * _DeathAnims[] = { "S_A_HUMAN.H_A_622A", // HEAD_FROM_BEHIND, "S_A_HUMAN.H_A_635A", // HEAD_FROM_FRONT, "S_A_HUMAN.H_A_622A", // TORSO_FROM_BEHIND, "S_A_HUMAN.H_A_632A", // TORSO_FROM_FRONT, "S_A_HUMAN.H_A_623A", // LEFT_ARM_FROM_BEHIND, "S_A_HUMAN.H_A_634A", // LEFT_ARM_FROM_FRONT, "S_A_HUMAN.H_A_624A", // RIGHT_ARM_FROM_BEHIND, "S_A_HUMAN.H_A_633A", // RIGHT_ARM_FROM_FRONT, "S_A_HUMAN.H_A_623A", // LEFT_LEG_FROM_BEHIND, "S_A_HUMAN.H_A_634A", // LEFT_LEG_FROM_FRONT, "S_A_HUMAN.H_A_624A", // RIGHT_LEG_FROM_BEHIND, "S_A_HUMAN.H_A_633A", // RIGHT_LEG_FROM_FRONT, "S_A_HUMAN.H_A_612A", // GROIN, "S_A_HUMAN.H_A_FLMB", // ON_FIRE, "S_A_HUMAN.H_A_FLMB", // ON_CHEM, "S_A_HUMAN.H_A_FLMB", // ON_ELECTRIC, "S_A_HUMAN.H_A_FLMB", // ON_CNC_FIRE, "S_A_HUMAN.H_A_FLMB", // ON_CNC_CHEM, }; const char * HumanStateClass::Get_Death_Anim( int ouch_type ) { WWASSERT( ouch_type <= OUCH_SUPER_FIRE ); return _DeathAnims[ ouch_type ]; } /* ** */ void HumanStateClass::Set_Precision(void) { cEncoderList::Set_Precision(BITPACK_HUMAN_STATE, 0, (int) HIGHEST_HUMAN_STATE); cEncoderList::Set_Precision(BITPACK_HUMAN_SUB_STATE, 0, (int) HIGHEST_HUMAN_SUB_STATE); } /* ** */ void HumanStateClass::Get_Information( StringClass & string ) { StringClass temp(0,true); temp.Format( "%s\n", Get_State_Name() ); string += temp; AnimControl->Get_Information( string ); } /* ** Begin_Jump is called when we first become airborne */ void HumanStateClass::Begin_Jump( void ) { // Store some information about the jump JumpTM = HumanPhys->Get_Transform(); // Set my state to Jump, with a direction of my current relative velocity Vector3 move_vector; HumanPhys->Get_Velocity( &move_vector ); move_vector = HumanPhys->Get_Transform().Inverse_Rotate_Vector( move_vector ); int sub_state = 0; if ( move_vector[0] > MOVING_THRESHHOLD ) sub_state |= SUB_STATE_FORWARD; else if ( move_vector[0] < -MOVING_THRESHHOLD ) sub_state |= SUB_STATE_BACKWARD; if ( move_vector[1] > MOVING_THRESHHOLD ) sub_state |= SUB_STATE_LEFT; else if ( move_vector[1] < -MOVING_THRESHHOLD ) sub_state |= SUB_STATE_RIGHT; Set_State( AIRBORNE, sub_state ); // Whenever a jump starts, play a jump surface effect Matrix3D tm = HumanPhys->Get_Transform(); int ground_type = HumanPhys->Get_Contact_Surface_Type(); SurfaceEffectsManager::Apply_Effect( ground_type, SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_JUMP, tm ); } /* ** Complete_Jump is called when we land */ void HumanStateClass::Complete_Jump( void ) { Vector3 fall = JumpTM.Get_Translation() - HumanPhys->Get_Transform().Get_Translation(); GlobalSettingsDef * settings = GlobalSettingsDef::Get_Global_Settings(); // Debug_Say(( "Fall Distance %f\n", fall.Z )); bool add_portal = true; // // Don't do this in the level editor // #ifndef PARAM_EDITING_ON if ( fall.Z > settings->Get_Falling_Damage_Min_Distance() ) { add_portal = false; float scale = (fall.Z - settings->Get_Falling_Damage_Min_Distance()) / (settings->Get_Falling_Damage_Max_Distance() - settings->Get_Falling_Damage_Min_Distance()); scale = WWMath::Clamp( scale, 0, 1 ); // (gth) don't take damage when falling onto an elevator (because we have // super-fast elevators...) if ( (HumanPhys->Peek_Ground_Object() != NULL) && (HumanPhys->Peek_Ground_Object()->As_ElevatorPhysClass() != NULL)) { Debug_Say(( "Fell onto an elevator, no damage!\n", scale )); scale = 0.0f; } Debug_Say(( "Fall Damage Scale %f\n", scale )); SoldierGameObj * owner = (SoldierGameObj *)HumanPhys->Get_Observer(); if ( owner != NULL ) { DefenseObjectClass * defense = owner->Get_Defense_Object(); float damage = defense->Get_Health_Max(); // AI's get notified // if ( !owner->Is_Human_Controlled() ) { const GameObjObserverList & observer_list = owner->Get_Observers(); for( int index = 0; index < observer_list.Count(); index++ ) { observer_list[ index ]->Custom( owner, CUSTOM_EVENT_FALLING_DAMAGE, damage*scale, NULL ); } // } // All get the damage OffenseObjectClass offense( damage, settings->Get_Falling_Damage_Warhead(), NULL ); owner->Apply_Damage_Extended( offense, scale, fall, NULL ); } } #endif // PARAM_EDITING_ON // Find the sector the human jumped from and the sector the human jumped to. /*Vector3 curr_pos = HumanPhys->Get_Position (); PathfindSectorClass *start_sector = PathfindClass::Get_Instance ()->Find_Sector (JumpTM.Get_Translation (), 2.0F); PathfindSectorClass *end_sector = PathfindClass::Get_Instance ()->Find_Sector (curr_pos, 2.0F); // Make a temporary pathfind connection between these sectors (if necessary) if (add_portal && start_sector != NULL && end_sector != NULL && start_sector != end_sector) { PathfindClass::Get_Instance ()->Add_Temporary_Portal (start_sector, end_sector, JumpTM.Get_Translation (), curr_pos); }*/ // Set me state to Land, with a direction of my current velocity Vector3 move_vector; HumanPhys->Get_Velocity( &move_vector ); move_vector = HumanPhys->Get_Transform().Inverse_Rotate_Vector( move_vector ); int sub_state = 0; if ( move_vector[0] > MOVING_THRESHHOLD ) sub_state |= SUB_STATE_FORWARD; else if ( move_vector[0] < -MOVING_THRESHHOLD ) sub_state |= SUB_STATE_BACKWARD; if ( move_vector[1] > MOVING_THRESHHOLD ) sub_state |= SUB_STATE_LEFT; else if ( move_vector[1] < -MOVING_THRESHHOLD ) sub_state |= SUB_STATE_RIGHT; Set_State( LAND, sub_state ); // Whenever a jump ends, play a land surface effect Matrix3D tm = HumanPhys->Get_Transform(); int ground_type = HumanPhys->Get_Contact_Surface_Type(); SurfaceEffectsManager::Apply_Effect( ground_type, SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_LAND, tm ); }