5309 lines
152 KiB
C++
5309 lines
152 KiB
C++
|
/*
|
||
|
** 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** Confidential - Westwood Studios ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Combat/soldier.cpp $*
|
||
|
* *
|
||
|
* $Author:: Byon_g $*
|
||
|
* *
|
||
|
* $Modtime:: 3/29/02 4:58p $*
|
||
|
* *
|
||
|
* $Revision:: 570 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "soldier.h"
|
||
|
#include "debug.h"
|
||
|
#include "pscene.h"
|
||
|
#include "combat.h"
|
||
|
#include "weapons.h"
|
||
|
#include "damage.h"
|
||
|
#include "humanphys.h"
|
||
|
#include "animcontrol.h"
|
||
|
#include "rendobj.h"
|
||
|
#include "wwpacket.h"
|
||
|
#include "assets.h"
|
||
|
#include "gameobjmanager.h"
|
||
|
#include "vehicle.h"
|
||
|
#include "c4.h"
|
||
|
#include "objlibrary.h"
|
||
|
#include "physicalgameobj.h"
|
||
|
#include "persistfactory.h"
|
||
|
#include "combatchunkid.h"
|
||
|
#include "definition.h"
|
||
|
#include "simpledefinitionfactory.h"
|
||
|
#include "wwhack.h"
|
||
|
#include "surfaceeffects.h"
|
||
|
#include "weaponmanager.h"
|
||
|
#include "weaponbag.h"
|
||
|
#include "physcoltest.h"
|
||
|
#include "htree.h"
|
||
|
#include "animobj.h"
|
||
|
#include "wwaudio.h"
|
||
|
#include "soldierobserver.h"
|
||
|
#include "playertype.h"
|
||
|
#include "crandom.h"
|
||
|
#include "ccamera.h"
|
||
|
#include "bitpackids.h"
|
||
|
#include "translatedb.h"
|
||
|
#include "translateobj.h"
|
||
|
#include "hlod.h"
|
||
|
#include "audiblesound.h"
|
||
|
#include "wwprofile.h"
|
||
|
#include "globalsettings.h"
|
||
|
#include "colors.h"
|
||
|
#include "input.h"
|
||
|
#include "gametype.h"
|
||
|
#include "messagewindow.h"
|
||
|
#include "conversationmgr.h"
|
||
|
#include "activeconversation.h"
|
||
|
#include "bones.h"
|
||
|
#include "dynamicspeechanim.h"
|
||
|
#include "hudinfo.h"
|
||
|
#include "powerup.h"
|
||
|
#include "diaglog.h"
|
||
|
#include "vistable.h"
|
||
|
#include "crandom.h"
|
||
|
#include "transitioneffect.h"
|
||
|
#include "combatmaterialeffectmanager.h"
|
||
|
#include "explosion.h"
|
||
|
#include "playerdata.h"
|
||
|
#include "encyclopediamgr.h"
|
||
|
#include "apppackettypes.h"
|
||
|
#include "hud.h"
|
||
|
#include "unitcoordinationzonemgr.h"
|
||
|
#include "specialbuilds.h"
|
||
|
#include "weaponview.h"
|
||
|
#include "ffactory.h"
|
||
|
#include "realcrc.h"
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
#define GUN_BONE_NAME "GUNBONE"
|
||
|
#define BACK_GUN_BONE_NAME "BACKGUNBONE"
|
||
|
|
||
|
#define POKE_ANGLE DEG_TO_RAD( 22 )
|
||
|
|
||
|
#define AMBUSH_DAMAGE_SCALE 1
|
||
|
|
||
|
const float EMOT_ICON_HEIGHT = 2.0F;
|
||
|
|
||
|
/*
|
||
|
** SoldierGameObjDef
|
||
|
*/
|
||
|
DECLARE_FORCE_LINK( Soldier )
|
||
|
|
||
|
SimplePersistFactoryClass<SoldierGameObjDef, CHUNKID_GAME_OBJECT_DEF_SOLDIER> _SoldierGameObjDefPersistFactory;
|
||
|
|
||
|
DECLARE_DEFINITION_FACTORY(SoldierGameObjDef, CLASSID_GAME_OBJECT_DEF_SOLDIER, "Soldier") _SoldierGameObjDefDefFactory;
|
||
|
|
||
|
SoldierGameObjDef::SoldierGameObjDef( void ) :
|
||
|
TurnRate( DEG_TO_RADF( 360.0f ) ),
|
||
|
JumpVelocity( 2 ),
|
||
|
SkeletonHeight( 0 ),
|
||
|
SkeletonWidth( 0 ),
|
||
|
UseInnateBehavior( true ),
|
||
|
InnateAggressiveness( 0.5f ),
|
||
|
InnateTakeCoverProbability( 0.5f ),
|
||
|
InnateIsStationary( false ),
|
||
|
HumanAnimOverrideDefID( 0 ),
|
||
|
HumanLoiterCollectionDefID( 0 ),
|
||
|
DeathSoundPresetID( 0 )
|
||
|
{
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_ANGLE, TurnRate );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, JumpVelocity );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, SkeletonHeight );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, SkeletonWidth );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_BOOL, UseInnateBehavior );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, InnateAggressiveness );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, InnateTakeCoverProbability );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_BOOL, InnateIsStationary );
|
||
|
EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FILENAME, FirstPersonHands );
|
||
|
GENERIC_DEFID_PARAM( SoldierGameObjDef, HumanAnimOverrideDefID, CLASSID_GLOBAL_SETTINGS_DEF_HUMAN_ANIM_OVERRIDE );
|
||
|
GENERIC_DEFID_PARAM( SoldierGameObjDef, HumanLoiterCollectionDefID, CLASSID_GLOBAL_SETTINGS_DEF_HUMAN_LOITER );
|
||
|
GENERIC_DEFID_PARAM( SoldierGameObjDef, DeathSoundPresetID, CLASSID_SOUND );
|
||
|
|
||
|
MODEL_DEF_PARAM( SoldierGameObjDef, PhysDefID, "HumanPhysDef" );
|
||
|
|
||
|
//
|
||
|
// We want soldiers to use innate conversations by default
|
||
|
//
|
||
|
AllowInnateConversations = true;
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
uint32 SoldierGameObjDef::Get_Class_ID (void) const
|
||
|
{
|
||
|
return CLASSID_GAME_OBJECT_DEF_SOLDIER;
|
||
|
}
|
||
|
|
||
|
PersistClass * SoldierGameObjDef::Create( void ) const
|
||
|
{
|
||
|
SoldierGameObj * obj = new SoldierGameObj;
|
||
|
obj->Init( *this );
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
enum {
|
||
|
CHUNKID_DEF_PARENT = 909991656,
|
||
|
CHUNKID_DEF_VARIABLES,
|
||
|
CHUNKID_DEF_DIALOG_ENTRY,
|
||
|
|
||
|
MICROCHUNKID_DEF_TURN_RATE = 1,
|
||
|
MICROCHUNKID_DEF_JUMP_VELOCITY,
|
||
|
MICROCHUNKID_DEF_SKELETON_HEIGHT,
|
||
|
MICROCHUNKID_DEF_SKELETON_WIDTH,
|
||
|
MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR,
|
||
|
MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS,
|
||
|
MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB,
|
||
|
XXXMICROCHUNKID_DEF_INNATE_ESCORT_ID,
|
||
|
XXXMICROCHUNKID_DEF_INNATE_ESCORT_RANGE,
|
||
|
MICROCHUNKID_DEF_FIRST_PERSON_HANDS,
|
||
|
XXXMICROCHUNKID_DEF_CORPSE_PERSIST_TIME,
|
||
|
MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS,
|
||
|
MICROCHUNKID_DEF_INNATE_IS_STATIONARY,
|
||
|
MICROCHUNKID_DEF_ORATOR_TYPE,
|
||
|
MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID,
|
||
|
MICROCHUNKID_DEF_DEATH_SOUND_PRESET,
|
||
|
MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID,
|
||
|
};
|
||
|
|
||
|
bool SoldierGameObjDef::Save( ChunkSaveClass & csave )
|
||
|
{
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_PARENT );
|
||
|
SmartGameObjDef::Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
//
|
||
|
// Save the dialog entries
|
||
|
//
|
||
|
for (int index = 0; index < DIALOG_MAX; index ++) {
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_DIALOG_ENTRY );
|
||
|
DialogList[index].Save (csave);
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TURN_RATE, TurnRate );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_JUMP_VELOCITY, JumpVelocity );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SKELETON_HEIGHT, SkeletonHeight );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SKELETON_WIDTH, SkeletonWidth );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR, UseInnateBehavior );
|
||
|
//WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS, UseInnateConversations );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS, InnateAggressiveness );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB, InnateTakeCoverProbability );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_IS_STATIONARY, InnateIsStationary );
|
||
|
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_FIRST_PERSON_HANDS, FirstPersonHands );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID, HumanAnimOverrideDefID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID, HumanLoiterCollectionDefID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEATH_SOUND_PRESET, DeathSoundPresetID );
|
||
|
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool SoldierGameObjDef::Load( ChunkLoadClass &cload )
|
||
|
{
|
||
|
int dialog_index = 0;
|
||
|
|
||
|
while (cload.Open_Chunk()) {
|
||
|
switch(cload.Cur_Chunk_ID()) {
|
||
|
|
||
|
case CHUNKID_DEF_PARENT:
|
||
|
SmartGameObjDef::Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_DEF_DIALOG_ENTRY:
|
||
|
if (dialog_index < DIALOG_MAX) {
|
||
|
DialogList[dialog_index++].Load (cload);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_DEF_VARIABLES:
|
||
|
while (cload.Open_Micro_Chunk()) {
|
||
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TURN_RATE, TurnRate );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_JUMP_VELOCITY, JumpVelocity );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SKELETON_HEIGHT, SkeletonHeight );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SKELETON_WIDTH, SkeletonWidth );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR, UseInnateBehavior );
|
||
|
//READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS, UseInnateConversations );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS, InnateAggressiveness );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB, InnateTakeCoverProbability );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_IS_STATIONARY, InnateIsStationary );
|
||
|
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_FIRST_PERSON_HANDS, FirstPersonHands );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ORATOR_TYPE, OratorType );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID, HumanAnimOverrideDefID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID, HumanLoiterCollectionDefID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DEATH_SOUND_PRESET, DeathSoundPresetID );
|
||
|
|
||
|
default:
|
||
|
//Debug_Say(( "Unrecognized SoldierDef Variable chunkID\n" ));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Micro_Chunk();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized SoldierDef chunkID\n" ));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const PersistFactoryClass & SoldierGameObjDef::Get_Factory (void) const
|
||
|
{
|
||
|
return _SoldierGameObjDefPersistFactory;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** SolderGameObj
|
||
|
*/
|
||
|
SimplePersistFactoryClass<SoldierGameObj, CHUNKID_GAME_OBJECT_SOLDIER> _SoldierGameObjPersistFactory;
|
||
|
|
||
|
const PersistFactoryClass & SoldierGameObj::Get_Factory (void) const
|
||
|
{
|
||
|
return _SoldierGameObjPersistFactory;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::DisplayDebugBoxForGhostCollision = false;
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
SoldierGameObj::SoldierGameObj() :
|
||
|
WeaponRenderModel( NULL ),
|
||
|
BackWeaponRenderModel( NULL ),
|
||
|
BackFlagRenderModel( NULL ),
|
||
|
WeaponAnimControl( NULL ),
|
||
|
TransitionCompletionData( NULL ),
|
||
|
Vehicle( NULL ),
|
||
|
LegFacing( 0 ),
|
||
|
SyncLegs( false ),
|
||
|
LastLegMode( 0 ),
|
||
|
HeadLookDuration( 0 ),
|
||
|
HeadRotation( 0,0,0 ),
|
||
|
HeadLookTarget( 0,0,0 ),
|
||
|
HeadLookAngle( 0,0,0 ),
|
||
|
HeadLookAngleTimer( 0 ),
|
||
|
InnateEnableBits( 0xFFFFFFFF ),
|
||
|
InnateObserver( NULL ),
|
||
|
// FlameTimer( 0 ),
|
||
|
SpecialDamageMode(ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE),
|
||
|
SpecialDamageTimer( 0 ),
|
||
|
GenerateIdleFacialAnimTimer( 0 ),
|
||
|
KeyRing( 0 ),
|
||
|
InFlyMode( false ),
|
||
|
IsVisible( true ),
|
||
|
CurrentSpeech( NULL ),
|
||
|
AIState( AI_STATE_IDLE ),
|
||
|
SpeechAnim( NULL ),
|
||
|
HeadModel( NULL ),
|
||
|
EmotIconModel( NULL ),
|
||
|
EmotIconTimer( 0 ),
|
||
|
LadderUpMask( false ),
|
||
|
LadderDownMask( false ),
|
||
|
SpecialDamageEffect( NULL ),
|
||
|
HealingEffect( NULL ),
|
||
|
ReloadingTilt(0),
|
||
|
WaterWake(NULL),
|
||
|
WeaponChanged( false )
|
||
|
{
|
||
|
// All Humans need a HuamnAnimControl
|
||
|
Set_Anim_Control( new HumanAnimControlClass );
|
||
|
Set_App_Packet_Type(APPPACKETTYPE_SOLDIER);
|
||
|
|
||
|
// create a water wake object
|
||
|
WaterWake = SurfaceEffectsManager::Create_Persistant_Emitter();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
SoldierGameObj::~SoldierGameObj()
|
||
|
{
|
||
|
if ( HealingEffect != NULL ) {
|
||
|
Peek_Human_Phys()->Remove_Effect_From_Me( HealingEffect );
|
||
|
REF_PTR_RELEASE( HealingEffect );
|
||
|
}
|
||
|
|
||
|
Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );
|
||
|
Set_Emot_Icon (NULL, 0);
|
||
|
|
||
|
REF_PTR_RELEASE( HeadModel );
|
||
|
REF_PTR_RELEASE( SpeechAnim );
|
||
|
REF_PTR_RELEASE( CurrentSpeech );
|
||
|
|
||
|
if (CombatManager::I_Am_Server()) {
|
||
|
CombatManager::On_Soldier_Death(this);
|
||
|
}
|
||
|
|
||
|
Set_Weapon_Model( NULL );
|
||
|
|
||
|
if ( BackWeaponRenderModel != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
|
||
|
BackWeaponRenderModel->Release_Ref();
|
||
|
BackWeaponRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( BackFlagRenderModel != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
|
||
|
BackFlagRenderModel->Release_Ref();
|
||
|
BackFlagRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( WeaponAnimControl != NULL ) {
|
||
|
delete WeaponAnimControl;
|
||
|
WeaponAnimControl = NULL;
|
||
|
}
|
||
|
|
||
|
if (Vehicle != NULL) {
|
||
|
// Remove myself from the vehicle !
|
||
|
Vehicle->Remove_Occupant(this);
|
||
|
}
|
||
|
|
||
|
COMBAT_SCENE->Remove_Object( Peek_Physical_Object() );
|
||
|
|
||
|
if ( Is_Human_Controlled() ) {
|
||
|
GameObjManager::Remove_Star( this );
|
||
|
}
|
||
|
|
||
|
Reset_RenderObjs();
|
||
|
|
||
|
if (WaterWake != NULL) {
|
||
|
SurfaceEffectsManager::Destroy_Persistant_Emitter( WaterWake );
|
||
|
WaterWake = NULL;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Init( void )
|
||
|
{
|
||
|
Re_Init( Get_Definition() );
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Init( const SoldierGameObjDef & definition )
|
||
|
{
|
||
|
SmartGameObj::Init( definition );
|
||
|
Copy_Settings( definition );
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Copy_Settings( const SoldierGameObjDef & definition )
|
||
|
{
|
||
|
HumanState.Init( Peek_Human_Phys() );
|
||
|
HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() ); // Must set the anim control after the phys object
|
||
|
//HumanState.Set_Human_Anim_Override( "HAO Test" );
|
||
|
if ( Get_Definition().HumanAnimOverrideDefID != 0 ) {
|
||
|
HumanState.Set_Human_Anim_Override( Get_Definition().HumanAnimOverrideDefID );
|
||
|
}
|
||
|
|
||
|
if ( Get_Definition().HumanLoiterCollectionDefID != 0 ) {
|
||
|
HumanState.Set_Human_Loiter_Collection( Get_Definition().HumanLoiterCollectionDefID );
|
||
|
}
|
||
|
|
||
|
Adjust_Skeleton( definition.SkeletonHeight, definition.SkeletonWidth );
|
||
|
|
||
|
// All characters force their heads and hands to use the same LOD level as their body.
|
||
|
RenderObjClass * model = Peek_Human_Phys()->Peek_Model();
|
||
|
if (model != NULL) {
|
||
|
model->Set_Sub_Objects_Match_LOD(true);
|
||
|
}
|
||
|
|
||
|
if ( InnateObserver == NULL &&
|
||
|
Get_Definition().UseInnateBehavior &&
|
||
|
Is_Controlled_By_Me () == false ) {
|
||
|
InnateObserver = new SoldierObserverClass;
|
||
|
Insert_Observer( InnateObserver );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the dialog entries from the definition
|
||
|
//
|
||
|
for( int index = 0; index < DIALOG_MAX; index ++ ) {
|
||
|
DialogList[index] = definition.DialogList[index];
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Put the soldier in its own collision group
|
||
|
//
|
||
|
Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );
|
||
|
|
||
|
Prepare_Speech_Framework ();
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Prepare_Speech_Framework( void )
|
||
|
{
|
||
|
//
|
||
|
// Make sure we have an allocated animation to use for speech
|
||
|
//
|
||
|
if ( SpeechAnim == NULL) {
|
||
|
HeadModel = Find_Head_Model();
|
||
|
|
||
|
//
|
||
|
// Dig out the skeleton name for the head model
|
||
|
//
|
||
|
if (HeadModel != NULL) {
|
||
|
const HTreeClass *htree = HeadModel->Get_HTree ();
|
||
|
if (htree != NULL) {
|
||
|
StringClass skeleton_name = htree->Get_Name ();
|
||
|
|
||
|
//
|
||
|
// Generate the animation
|
||
|
//
|
||
|
SpeechAnim = new DynamicSpeechAnimClass (skeleton_name);
|
||
|
GenerateIdleFacialAnimTimer = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Re_Init( const SoldierGameObjDef & definition )
|
||
|
{
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
HUDClass::Force_Weapon_Chart_Update();
|
||
|
WeaponViewClass::Reset();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove the object from the world (just to be safe)
|
||
|
//
|
||
|
COMBAT_SCENE->Remove_Object( Peek_Physical_Object() );
|
||
|
|
||
|
//
|
||
|
// Reset the weapon model
|
||
|
//
|
||
|
Set_Weapon_Model( NULL );
|
||
|
|
||
|
if ( BackWeaponRenderModel != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
|
||
|
BackWeaponRenderModel->Release_Ref();
|
||
|
BackWeaponRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( BackFlagRenderModel != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
|
||
|
BackFlagRenderModel->Release_Ref();
|
||
|
BackFlagRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( WeaponAnimControl != NULL ) {
|
||
|
delete WeaponAnimControl;
|
||
|
WeaponAnimControl = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Re-initialize the base class
|
||
|
//
|
||
|
SmartGameObj::Re_Init( definition );
|
||
|
|
||
|
//
|
||
|
// Free some of the data we will be re-initializing
|
||
|
//
|
||
|
REF_PTR_RELEASE( HeadModel );
|
||
|
REF_PTR_RELEASE( SpeechAnim );
|
||
|
REF_PTR_RELEASE( CurrentSpeech );
|
||
|
|
||
|
HumanState.Reset();
|
||
|
|
||
|
//
|
||
|
// Copy any internal settings from the definition
|
||
|
//
|
||
|
Copy_Settings( definition );
|
||
|
|
||
|
//
|
||
|
// "Dirty" the object for networking
|
||
|
//
|
||
|
Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
|
||
|
|
||
|
// When class changes, update for new weapon
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
HUDClass::Reset();
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
const SoldierGameObjDef & SoldierGameObj::Get_Definition( void ) const
|
||
|
{
|
||
|
return (const SoldierGameObjDef &)BaseGameObj::Get_Definition();
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Set_Control_Owner(int control_owner)
|
||
|
{
|
||
|
if ( Is_Human_Controlled() ) {
|
||
|
GameObjManager::Remove_Star( this );
|
||
|
}
|
||
|
SmartGameObj::Set_Control_Owner(control_owner);
|
||
|
if ( Is_Human_Controlled() ) {
|
||
|
GameObjManager::Add_Star( this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Soldier Save and Load
|
||
|
*/
|
||
|
|
||
|
enum {
|
||
|
CHUNKID_PARENT = 909991656,
|
||
|
CHUNKID_VARIABLES,
|
||
|
CHUNKID_WEAPON_ANIM,
|
||
|
CHUNKID_HUMAN_STATE,
|
||
|
XXXCHUNKID_C4_TIMER,
|
||
|
XXXCHUNKID_WEAPON_MODEL,
|
||
|
CHUNKID_TRANSITION_COMPLETION_DATA,
|
||
|
CHUNKID_DIALOG_ENTRY,
|
||
|
CHUNKID_RENDER_OBJS,
|
||
|
CHUNKID_SPECIAL_DAMAGE_DAMAGER,
|
||
|
|
||
|
MICROCHUNKID_DETONATE_C4 = 1,
|
||
|
MICROCHUNKID_LEG_FACING,
|
||
|
MICROCHUNKID_SYNC_LEGS,
|
||
|
MICROCHUNKID_ANIMATION_NAME,
|
||
|
MICROCHUNKID_VEHICLE,
|
||
|
XXX_MICROCHUNKID_TRANSITION,
|
||
|
XXX_MICROCHUNKID_FORCE_FACING,
|
||
|
MICROCHUNKID_INNATE_ENABLE_BITS,
|
||
|
MICROCHUNKID_INNATE_OBSERVER_PTR,
|
||
|
MICROCHUNKID_LAST_LEG_MODE,
|
||
|
MICROCHUNKID_HEAD_LOOK_DURATION,
|
||
|
MICROCHUNKID_HEAD_ROTATION,
|
||
|
MICROCHUNKID_LOOK_TARGET,
|
||
|
XXXMICROCHUNKID_FLAME_TIMER,
|
||
|
MICROCHUNKID_KEY_RING,
|
||
|
MICROCHUNKID_AI_STATE,
|
||
|
XXX_MICROCHUNKID_IN_CONVERSATION,
|
||
|
MICROCHUNKID_LOOK_ANGLE,
|
||
|
MICROCHUNKID_LOOK_ANGLE_TIMER,
|
||
|
XXX_MICROCHUNKID_ACTIVE_CONVERSATION,
|
||
|
MICROCHUNKID_WEAPON_MODEL,
|
||
|
MICROCHUNKID_SPECIAL_DAMAGE_MODE,
|
||
|
MICROCHUNKID_SPECIAL_DAMAGE_TIMER,
|
||
|
MICROCHUNKID_IS_USING_GHOST_COLLISION
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Save( ChunkSaveClass & csave )
|
||
|
{
|
||
|
csave.Begin_Chunk( CHUNKID_PARENT );
|
||
|
SmartGameObj::Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
//
|
||
|
// Save the dialog entries
|
||
|
//
|
||
|
for (int index = 0; index < DIALOG_MAX; index ++) {
|
||
|
csave.Begin_Chunk( CHUNKID_DIALOG_ENTRY );
|
||
|
DialogList[index].Save (csave);
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DETONATE_C4, DetonateC4 );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LEG_FACING, LegFacing );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SYNC_LEGS, SyncLegs );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_KEY_RING, KeyRing );
|
||
|
// WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FORCE_FACING, ForceFacing );
|
||
|
if ( Vehicle != NULL ) {
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_VEHICLE, Vehicle );
|
||
|
}
|
||
|
csave.Begin_Micro_Chunk( MICROCHUNKID_ANIMATION_NAME );
|
||
|
char anim_string[80];
|
||
|
strcpy( anim_string, AnimationName );
|
||
|
csave.Write( anim_string, strlen( anim_string ) + 1);
|
||
|
csave.End_Micro_Chunk();
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_INNATE_ENABLE_BITS, InnateEnableBits );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_INNATE_OBSERVER_PTR, InnateObserver );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LAST_LEG_MODE, LastLegMode );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HEAD_LOOK_DURATION, HeadLookDuration );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HEAD_ROTATION, HeadRotation );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_TARGET, HeadLookTarget );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_ANGLE, HeadLookAngle );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_ANGLE_TIMER, HeadLookAngleTimer );
|
||
|
// WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FLAME_TIMER, FlameTimer );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPECIAL_DAMAGE_MODE, SpecialDamageMode );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPECIAL_DAMAGE_TIMER, SpecialDamageTimer );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AI_STATE, AIState );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_MODEL, WeaponRenderModel );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_USING_GHOST_COLLISION, IsUsingGhostCollision );
|
||
|
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
if ( WeaponAnimControl ) {
|
||
|
csave.Begin_Chunk( CHUNKID_WEAPON_ANIM );
|
||
|
WeaponAnimControl->Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_HUMAN_STATE );
|
||
|
HumanState.Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
if ( TransitionCompletionData ) {
|
||
|
csave.Begin_Chunk( CHUNKID_TRANSITION_COMPLETION_DATA );
|
||
|
TransitionCompletionData->Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < RenderObjList.Count(); i++ ) {
|
||
|
csave.Begin_Chunk( CHUNKID_RENDER_OBJS );
|
||
|
csave.Begin_Chunk(RenderObjList[i]->Get_Factory().Chunk_ID());
|
||
|
RenderObjList[i]->Get_Factory().Save( csave, RenderObjList[i] );
|
||
|
csave.End_Chunk();
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
if ( SpecialDamageDamager.Get_Ptr() != NULL ) {
|
||
|
csave.Begin_Chunk( CHUNKID_SPECIAL_DAMAGE_DAMAGER );
|
||
|
SpecialDamageDamager.Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Load( ChunkLoadClass &cload )
|
||
|
{
|
||
|
char anim_string[80];
|
||
|
int dialog_index = 0;
|
||
|
|
||
|
WWASSERT( Vehicle == NULL );
|
||
|
|
||
|
while (cload.Open_Chunk()) {
|
||
|
switch(cload.Cur_Chunk_ID()) {
|
||
|
|
||
|
case CHUNKID_PARENT:
|
||
|
SmartGameObj::Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_DIALOG_ENTRY:
|
||
|
if (dialog_index < DIALOG_MAX) {
|
||
|
DialogList[dialog_index++].Load (cload);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_VARIABLES:
|
||
|
while (cload.Open_Micro_Chunk()) {
|
||
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DETONATE_C4, DetonateC4 );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_LEG_FACING, LegFacing );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_SYNC_LEGS, SyncLegs );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_VEHICLE, Vehicle );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_KEY_RING, KeyRing );
|
||
|
// READ_MICRO_CHUNK( cload, MICROCHUNKID_FORCE_FACING, ForceFacing );
|
||
|
|
||
|
case MICROCHUNKID_ANIMATION_NAME:
|
||
|
cload.Read( anim_string, cload.Cur_Micro_Chunk_Length() );
|
||
|
AnimationName = anim_string;
|
||
|
break;
|
||
|
|
||
|
case MICROCHUNKID_INNATE_OBSERVER_PTR:
|
||
|
cload.Read( &InnateObserver, sizeof (InnateObserver) );
|
||
|
if ( InnateObserver != NULL ) {
|
||
|
REQUEST_POINTER_REMAP( (void **)&InnateObserver );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_INNATE_ENABLE_BITS, InnateEnableBits );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_LAST_LEG_MODE, LastLegMode );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_HEAD_LOOK_DURATION, HeadLookDuration );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_HEAD_ROTATION, HeadRotation );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_TARGET, HeadLookTarget );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_ANGLE, HeadLookAngle );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_ANGLE_TIMER, HeadLookAngleTimer );
|
||
|
// READ_MICRO_CHUNK( cload, MICROCHUNKID_FLAME_TIMER, FlameTimer );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPECIAL_DAMAGE_MODE, SpecialDamageMode );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPECIAL_DAMAGE_TIMER, SpecialDamageTimer );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_AI_STATE, AIState );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_MODEL, WeaponRenderModel );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_USING_GHOST_COLLISION, IsUsingGhostCollision );
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized Soldier Variable chunkID\n" ));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Micro_Chunk();
|
||
|
}
|
||
|
if ( Vehicle != NULL ) {
|
||
|
REQUEST_POINTER_REMAP( (void **)&Vehicle );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_WEAPON_ANIM:
|
||
|
Set_Weapon_Animation( NULL ); // Get the anim control built
|
||
|
WeaponAnimControl->Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_HUMAN_STATE:
|
||
|
HumanState.Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_RENDER_OBJS:
|
||
|
cload.Open_Chunk();
|
||
|
PersistFactoryClass * factory;
|
||
|
factory = SaveLoadSystemClass::Find_Persist_Factory(cload.Cur_Chunk_ID());
|
||
|
WWASSERT(factory != NULL);
|
||
|
if (factory != NULL) {
|
||
|
RenderObjClass * robj = (RenderObjClass *)factory->Load(cload);
|
||
|
Add_RenderObj( robj );
|
||
|
robj->Release_Ref();
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_TRANSITION_COMPLETION_DATA:
|
||
|
WWASSERT( TransitionCompletionData == NULL );
|
||
|
TransitionCompletionData = new TransitionCompletionDataStruct();
|
||
|
TransitionCompletionData->Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_SPECIAL_DAMAGE_DAMAGER:
|
||
|
SpecialDamageDamager.Load( cload );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized SoldierGameObj chunkID\n" ));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
}
|
||
|
|
||
|
if ( WeaponRenderModel != NULL ) {
|
||
|
REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&WeaponRenderModel );
|
||
|
}
|
||
|
|
||
|
|
||
|
SaveLoadSystemClass::Register_Post_Load_Callback(this);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::On_Post_Load( void )
|
||
|
{
|
||
|
HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() ); // Must set the anim control after the phys object
|
||
|
|
||
|
SmartGameObj::On_Post_Load();
|
||
|
|
||
|
if ( Peek_Model() && (WeaponRenderModel != NULL) ) {
|
||
|
Peek_Model()->Add_Sub_Object_To_Bone( WeaponRenderModel, GUN_BONE_NAME );
|
||
|
}
|
||
|
|
||
|
Adjust_Skeleton( Get_Definition().SkeletonHeight, Get_Definition().SkeletonWidth );
|
||
|
|
||
|
// Fixup BackWeaponRenderModel
|
||
|
Update_Back_Gun();
|
||
|
|
||
|
// Hide if in vehicle
|
||
|
if ( Is_In_Vehicle() && Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Set_Hidden( true );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure we have an allocated animation to use for speech
|
||
|
//
|
||
|
Prepare_Speech_Framework ();
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CollisionReactionType SoldierGameObj::Collision_Occurred(const CollisionEventClass & event)
|
||
|
{
|
||
|
// Debug_Say(( "Soldier Collision %p %p\n", this, event.OtherObj ));
|
||
|
|
||
|
// Detect squishing...
|
||
|
if ( event.OtherObj != NULL && event.OtherObj->Get_Observer() != NULL ) {
|
||
|
PhysicalGameObj * obj = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_PhysicalGameObj();
|
||
|
if ( obj && obj->As_VehicleGameObj() ) {
|
||
|
// if ( !Is_Teammate( obj ) ) { // never squish teammates
|
||
|
if ( Is_Enemy( obj ) ) { // only squish enemies
|
||
|
Vector3 vel;
|
||
|
if ( event.OtherObj->As_MoveablePhysClass() != NULL ) {
|
||
|
event.OtherObj->As_MoveablePhysClass()->Get_Velocity( &vel );
|
||
|
|
||
|
Vector3 my_pos;
|
||
|
Vector3 vehicle_pos;
|
||
|
Get_Position(&my_pos);
|
||
|
obj->Get_Position(&vehicle_pos);
|
||
|
|
||
|
// only squish if velocity is high enough, and velocity is towards the soldier
|
||
|
if ( (vel.Length() > obj->As_VehicleGameObj()->Get_Squish_Velocity() ) &&
|
||
|
(Vector3::Dot_Product(vel,my_pos - vehicle_pos) > 0.0f) )
|
||
|
{
|
||
|
if ( HumanState.Get_State() != HumanStateClass::DEATH ) {
|
||
|
// We need to pick a better warhead to damage with
|
||
|
SmartGameObj * damager = obj->As_VehicleGameObj()->Get_Driver();
|
||
|
if ( damager == NULL ) {
|
||
|
damager = obj->As_VehicleGameObj();
|
||
|
}
|
||
|
OffenseObjectClass offense( 10000, 1, damager );
|
||
|
Apply_Damage_Extended( offense, 10000.0f, vel, NULL );
|
||
|
// Play the squish sound
|
||
|
|
||
|
// Stats
|
||
|
if (( obj->As_VehicleGameObj()->Get_Driver() != NULL ) &&
|
||
|
( obj->As_VehicleGameObj()->Get_Driver()->Get_Player_Data() != NULL ) ) {
|
||
|
obj->As_VehicleGameObj()->Get_Driver()->Get_Player_Data()->Stats_Add_Squish();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return COLLISION_REACTION_DEFAULT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Export_Creation( BitStreamClass &packet )
|
||
|
{
|
||
|
//TSS091001
|
||
|
//WWDEBUG_SAY((">>>>> SoldierGameObj::Export_Creation id %d\n", Get_ID()));
|
||
|
|
||
|
SmartGameObj::Export_Creation( packet );
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Import_Creation( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Import_Creation( packet );
|
||
|
|
||
|
//TSS091001
|
||
|
//WWDEBUG_SAY((">>>>> SoldierGameObj::Import_Creation id %d\n", Get_ID()));
|
||
|
|
||
|
/*
|
||
|
//
|
||
|
// Setup the speed settings for this soldier
|
||
|
//
|
||
|
float speed_factor = CombatManager::Get_Max_Speed_Pc( this ) / 100.0f;
|
||
|
Set_Max_Speed( speed_factor * Get_Max_Speed() );
|
||
|
*/
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Export_Rare( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Export_Rare( packet );
|
||
|
|
||
|
//
|
||
|
// Add our definition ID to the packet
|
||
|
//
|
||
|
uint32 definition_id = Get_Definition ().Get_ID ();
|
||
|
packet.Add( definition_id );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Import_Rare( BitStreamClass &packet )
|
||
|
{
|
||
|
Set_Weapon_Model (NULL);
|
||
|
// If we are changing to the same def, the above line made us lose our weapon model, and we won't get it back.
|
||
|
// The next line makes us get it back.
|
||
|
Get_Weapon_Bag()->Force_Changed();
|
||
|
|
||
|
SmartGameObj::Import_Rare( packet );
|
||
|
|
||
|
//
|
||
|
// Read the definition ID from the packet
|
||
|
//
|
||
|
uint32 definition_id = 0;
|
||
|
packet.Get( definition_id );
|
||
|
|
||
|
//
|
||
|
// Did our definition change?
|
||
|
//
|
||
|
if (definition_id != Get_Definition ().Get_ID ()) {
|
||
|
DefinitionClass *definition = DefinitionMgrClass::Find_Definition (definition_id);
|
||
|
|
||
|
//
|
||
|
// Did we find the right definition?
|
||
|
//
|
||
|
if (definition != NULL && definition->Get_Class_ID() == CLASSID_GAME_OBJECT_DEF_SOLDIER) {
|
||
|
SoldierGameObjDef *soldier_def = reinterpret_cast<SoldierGameObjDef *> (definition);
|
||
|
|
||
|
//
|
||
|
// Re-initialize ourselves
|
||
|
//
|
||
|
Re_Init (*soldier_def);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Export_Occasional( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Export_Occasional( packet );
|
||
|
|
||
|
//
|
||
|
// What weapon is being held?
|
||
|
//
|
||
|
#if 0 //(gth) moving this back to Frequent to fix the game, re-optimize later...
|
||
|
WeaponClass * p_weapon = Get_Weapon();
|
||
|
bool has_weapon = (p_weapon != NULL);
|
||
|
packet.Add(has_weapon);
|
||
|
if (has_weapon) {
|
||
|
packet.Add(p_weapon->Get_ID());
|
||
|
packet.Add(p_weapon->Get_Total_Rounds());
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
WWASSERT(WeaponBag != NULL);
|
||
|
WeaponBag->Export_Weapon_List(packet);
|
||
|
|
||
|
//packet.Add(CtfTeamFlag, BITPACK_CTF_TEAM_FLAG);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Import_Occasional( BitStreamClass &packet )
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
SmartGameObj::Import_Occasional( packet );
|
||
|
|
||
|
//
|
||
|
// Held weapon
|
||
|
//
|
||
|
#if 0 // (gth) moving back to "Frequent" to fix the game, re-optimize later?
|
||
|
bool has_weapon = packet.Get(has_weapon);
|
||
|
if (has_weapon) {
|
||
|
WWASSERT(!packet.Is_Flushed());
|
||
|
|
||
|
int weapon_id = packet.Get(weapon_id);
|
||
|
int rounds = packet.Get(rounds);
|
||
|
if ((Get_Weapon() == NULL) || (weapon_id != Get_Weapon()->Get_ID())) {
|
||
|
WeaponBag->Select_Weapon_ID(weapon_id);
|
||
|
}
|
||
|
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
// If this weapon is currently being fired, ignore the server rounds count packet
|
||
|
// This should help the jittery rounds count
|
||
|
if ( !Get_Weapon()->Is_Triggered() ) {
|
||
|
Get_Weapon()->Set_Total_Rounds( rounds );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
Get_Weapon_Bag()->Deselect();
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Weapon list
|
||
|
//
|
||
|
WWASSERT(WeaponBag != NULL);
|
||
|
WeaponBag->Import_Weapon_List(packet);
|
||
|
|
||
|
|
||
|
/*
|
||
|
//
|
||
|
// Flag pickup/drop
|
||
|
//
|
||
|
int ctf_team_flag = packet.Get(ctf_team_flag, BITPACK_CTF_TEAM_FLAG);
|
||
|
if (CtfTeamFlag != ctf_team_flag) {
|
||
|
CombatManager::Change_Flag_Status(this, ctf_team_flag);
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Export_Frequent(BitStreamClass & packet)
|
||
|
{
|
||
|
/**/
|
||
|
//TSS101601
|
||
|
bool in_vehicle = (Get_State() == HumanStateClass::IN_VEHICLE);
|
||
|
packet.Add(in_vehicle);
|
||
|
if (in_vehicle)
|
||
|
{
|
||
|
// Just do the control info
|
||
|
SmartGameObj::Export_Frequent(packet);
|
||
|
return;
|
||
|
}
|
||
|
/**/
|
||
|
|
||
|
//
|
||
|
// What weapon is being held?
|
||
|
// (gth) moved this back into frequent to fix the game, re-optimize later
|
||
|
//
|
||
|
WeaponClass * p_weapon = Get_Weapon();
|
||
|
bool has_weapon = (p_weapon != NULL);
|
||
|
packet.Add(has_weapon);
|
||
|
if (has_weapon) {
|
||
|
packet.Add(p_weapon->Get_ID());
|
||
|
packet.Add(p_weapon->Get_Total_Rounds());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Export Position and state
|
||
|
//
|
||
|
Vector3 position;
|
||
|
Get_Position(&position);
|
||
|
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
//
|
||
|
// Mix up the packet order to make demo/non-demo code more incompatible.
|
||
|
//
|
||
|
packet.Add(position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Add(position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
packet.Add(position.X, BITPACK_WORLD_POSITION_X);
|
||
|
#else
|
||
|
packet.Add(position.X, BITPACK_WORLD_POSITION_X);
|
||
|
packet.Add(position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Add(position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
packet.Add((int) HumanState.Get_State(), BITPACK_HUMAN_STATE);
|
||
|
packet.Add(HumanState.Get_Sub_State(), BITPACK_HUMAN_SUB_STATE);
|
||
|
|
||
|
|
||
|
if (HumanState.Get_State() == HumanStateClass::AIRBORNE) {
|
||
|
// velocity is only needed for jumping
|
||
|
Vector3 velocity;
|
||
|
Get_Velocity(velocity);
|
||
|
packet.Add(velocity.X);
|
||
|
packet.Add(velocity.Y);
|
||
|
packet.Add(velocity.Z);
|
||
|
}
|
||
|
|
||
|
if ((Get_State() == HumanStateClass::TRANSITION) ||
|
||
|
(Get_State() == HumanStateClass::ANIMATION) ||
|
||
|
(Get_State() == HumanStateClass::IN_VEHICLE)) {
|
||
|
packet.Add_Terminated_String(AnimationName);
|
||
|
// Debug_Say(( "In Transition %s\n", TransitionName ));
|
||
|
}
|
||
|
|
||
|
bool is_special_damage = SpecialDamageMode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE;
|
||
|
packet.Add(is_special_damage);
|
||
|
if ( is_special_damage ) {
|
||
|
packet.Add( SpecialDamageMode );
|
||
|
}
|
||
|
|
||
|
SmartGameObj::Export_Frequent(packet);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Import_Frequent( BitStreamClass & packet )
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
/**/
|
||
|
//TSS101601
|
||
|
bool in_vehicle = packet.Get(in_vehicle);
|
||
|
if (in_vehicle)
|
||
|
{
|
||
|
// Just get control info
|
||
|
SmartGameObj::Import_Frequent(packet);
|
||
|
return;
|
||
|
}
|
||
|
/**/
|
||
|
|
||
|
//
|
||
|
// What weapon is being held?
|
||
|
// (gth) moved this back into frequent to fix the game, re-optimize later
|
||
|
//
|
||
|
bool has_weapon = packet.Get(has_weapon);
|
||
|
if (has_weapon) {
|
||
|
WWASSERT(!packet.Is_Flushed());
|
||
|
|
||
|
int weapon_id = packet.Get(weapon_id);
|
||
|
int rounds = packet.Get(rounds);
|
||
|
if ((Get_Weapon() == NULL) || (weapon_id != Get_Weapon()->Get_ID())) {
|
||
|
WeaponBag->Select_Weapon_ID(weapon_id);
|
||
|
}
|
||
|
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
// If this weapon is currently being fired, ignore the server rounds count packet
|
||
|
// This should help the jittery rounds count
|
||
|
if ( !Get_Weapon()->Is_Triggered() ) {
|
||
|
Get_Weapon()->Set_Total_Rounds( rounds );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
Get_Weapon_Bag()->Deselect();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Position
|
||
|
//
|
||
|
Vector3 sc_position;
|
||
|
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
//
|
||
|
// Mix up the packet order to make demo/non-demo code more incompatible.
|
||
|
//
|
||
|
packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
|
||
|
#else
|
||
|
packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
|
||
|
packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Bump Z up to the top of the possible values due to packing
|
||
|
// we assume the max error is half of the resolution
|
||
|
float max_error = cEncoderList::Get_Encoder_Type_Entry( BITPACK_WORLD_POSITION_Z ).Get_Resolution() / 2.0f;
|
||
|
sc_position.Z += max_error;
|
||
|
|
||
|
Interpret_Sc_Position_Data(sc_position);
|
||
|
|
||
|
//
|
||
|
// State and substate
|
||
|
//
|
||
|
int h_state = packet.Get(h_state, BITPACK_HUMAN_STATE);
|
||
|
HumanStateClass::HumanStateType state =
|
||
|
(HumanStateClass::HumanStateType) h_state;
|
||
|
int sub_state = packet.Get(sub_state, BITPACK_HUMAN_SUB_STATE);
|
||
|
|
||
|
//
|
||
|
// Velocity (if airborne)
|
||
|
//
|
||
|
Vector3 velocity;
|
||
|
if (state == HumanStateClass::AIRBORNE) {
|
||
|
packet.Get(velocity.X);
|
||
|
packet.Get(velocity.Y);
|
||
|
packet.Get(velocity.Z);
|
||
|
}
|
||
|
|
||
|
if (HumanState.Is_Locked()) {
|
||
|
packet.Flush();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
if ( Get_State() == HumanStateClass::TRANSITION ) {
|
||
|
Debug_Say(( "Ignoring Transitioning!\n" ));
|
||
|
packet.Flush();
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
char trans_name[80] = "";
|
||
|
if ( ( state == HumanStateClass::TRANSITION ) ||
|
||
|
( state == HumanStateClass::ANIMATION ) ||
|
||
|
( state == HumanStateClass::IN_VEHICLE ) ) {
|
||
|
packet.Get_Terminated_String(trans_name, sizeof(trans_name));
|
||
|
}
|
||
|
|
||
|
Interpret_Sc_State_Data(state, sub_state, trans_name, velocity, sc_position);
|
||
|
|
||
|
bool is_special_damage;
|
||
|
packet.Get(is_special_damage);
|
||
|
int mode = ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE;
|
||
|
if ( is_special_damage ) {
|
||
|
packet.Get(mode);
|
||
|
}
|
||
|
if ( mode != SpecialDamageMode ) {
|
||
|
Set_Special_Damage_Mode( (ArmorWarheadManager::SpecialDamageType)mode );
|
||
|
}
|
||
|
|
||
|
if (Get_State() == HumanStateClass::DIVE) {
|
||
|
packet.Flush();
|
||
|
} else {
|
||
|
SmartGameObj::Import_Frequent(packet);
|
||
|
}
|
||
|
|
||
|
WWASSERT(packet.Is_Flushed());
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Import_State_Cs(BitStreamClass & packet)
|
||
|
{
|
||
|
bool is_sniping = packet.Get( is_sniping );
|
||
|
if ( is_sniping != Is_Sniping() ) {
|
||
|
// Take sniping state from client
|
||
|
// Debug_Say(( "Fixing Sniper State\n" ));
|
||
|
HumanState.Toggle_State_Flag( HumanStateClass::SNIPING_FLAG );
|
||
|
}
|
||
|
|
||
|
bool checking = packet.Get( checking );
|
||
|
if ( checking ) {
|
||
|
int check = packet.Get( check );
|
||
|
if ( check != Check() ) {
|
||
|
// React to cheating
|
||
|
if ( Get_Player_Data() ) {
|
||
|
Get_Player_Data()->Inc_Punish_Timer( TimeManager::Get_Frame_Seconds() ) ;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SmartGameObj::Import_State_Cs(packet);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Export_State_Cs(BitStreamClass & packet)
|
||
|
{
|
||
|
bool is_sniping = Is_Sniping();
|
||
|
packet.Add( is_sniping );
|
||
|
|
||
|
bool checking = Control.Get_Boolean( ControlClass::BOOLEAN_ACTION );
|
||
|
packet.Add( checking );
|
||
|
if ( checking ) {
|
||
|
int check = Check();
|
||
|
packet.Add( check );
|
||
|
}
|
||
|
|
||
|
SmartGameObj::Export_State_Cs(packet);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int SoldierGameObj::Check(void)
|
||
|
{
|
||
|
Matrix3D tm(1);
|
||
|
#define UNINITIALLIZED_CRC 0x78629140
|
||
|
static int crc = UNINITIALLIZED_CRC;
|
||
|
tm.Rotate_X( 1.4f );
|
||
|
if ( crc == UNINITIALLIZED_CRC ) {
|
||
|
char * filelist[] = {
|
||
|
"laif`wp-gga", //"objects.ddb",
|
||
|
"bqnlq-jmj", //"armor.ini",
|
||
|
"almfp-jmj", //"bones.ini",
|
||
|
"pvqeb`ffeef`wp-jmj", //"surfaceeffects.ini",
|
||
|
"`bnfqbp-jmj", //"cameras.ini",
|
||
|
"`\\mlg\\nd\\o3-t0g", //"c_nod_mg_l0.w3d",
|
||
|
"`\\mlg\\qh\\o3-t0g", //"c_nod_rk_l0.w3d",
|
||
|
"`\\mlg\\eo\\o3-t0g", //"c_nod_fl_l0.w3d",
|
||
|
"`\\mlg\\fm\\o3-t0g", //"c_nod_en_l0.w3d",
|
||
|
"`\\mlg\\ndl\\o3-t0g", //"c_nod_mgo_l0.w3d",
|
||
|
"`\\mlg\\qhl\\o3-t0g", //"c_nod_rko_l0.w3d",
|
||
|
"`\\mlg\\`kfnw\\o3-t0g", //"c_nod_chemt_l0.w3d",
|
||
|
"`\\mlg\\pmjsfq\\o3-t0g", //"c_nod_sniper_l0.w3d",
|
||
|
"`\\mlg\\qplog\\o3-t0g", //"c_nod_rsold_l0.w3d",
|
||
|
"`\\mlg\\pwowk\\o3-t0g", //"c_nod_stlth_l0.w3d",
|
||
|
"`\\mlg\\pbhv\\o3-t0g", //"c_nod_saku_l0.w3d",
|
||
|
"`\\mlg\\pbhv1\\o3-t0g", //"c_nod_saku2_l0.w3d",
|
||
|
"`\\mlg\\qbu\\o3-t0g", //"c_nod_rav_l0.w3d",
|
||
|
"`\\mlg\\nqbu\\o3-t0g", //"c_nod_mrav_l0.w3d",
|
||
|
"`\\mlg\\ngy\\o3-t0g", //"c_nod_mdz_l0.w3d",
|
||
|
"`\\mlg\\ngy1\\o3-t0g", //"c_nod_mdz2_l0.w3d",
|
||
|
"`\\mlg\\w`\\o3-t0g", //"c_nod_tc_l0.w3d",
|
||
|
"`\\mlg\\nvwbmw\\o3-t0g", //"c_nod_mutant_l0.w3d",
|
||
|
"`\\mlg\\npog\\o3-t0g", //"c_nod_msld_l0.w3d",
|
||
|
"`\\mlg\\pplog\\o3-t0g", //"c_nod_ssold_l0.w3d",
|
||
|
"`\\mlg\\sfwn\\o3-t0g", //"c_nod_petm_l0.w3d",
|
||
|
"`\\mlg\\hbmf\\o3-t0g", //"c_nod_kane_l0.w3d",
|
||
|
"`\\dgj\\nd\\o3-t0g", //"c_gdi_mg_l0.w3d",
|
||
|
"`\\dgj\\qh\\o3-t0g", //"c_gdi_rk_l0.w3d",
|
||
|
"`\\dgj\\dq\\o3-t0g", //"c_gdi_gr_l0.w3d",
|
||
|
"`\\dgj\\fm\\o3-t0g", //"c_gdi_en_l0.w3d",
|
||
|
"`\\dgj\\ndl\\o3-t0g", //"c_gdi_mgo_l0.w3d",
|
||
|
"`\\dgj\\qhl\\o3-t0g", //"c_gdi_rko_l0.w3d",
|
||
|
"`\\dgj\\pzg\\o3-t0g", //"c_gdi_syd_l0.w3d",
|
||
|
"`\\dgj\\gfbg\\o3-t0g", //"c_gdi_dead_l0.w3d",
|
||
|
"`\\dgj\\dvm\\o3-t0g", //"c_gdi_gun_l0.w3d",
|
||
|
"`\\dgj\\sw`k\\o3-t0g", //"c_gdi_ptch_l0.w3d",
|
||
|
"`\\kbul`\\o3-t0g", //"c_havoc_l0.w3d",
|
||
|
"`\\kbul`m\\o3-t0g", //"c_havocn_l0.w3d",
|
||
|
"`\\kbul`t\\o3-t0g", //"c_havocw_l0.w3d",
|
||
|
"`\\kbul`g\\o3-t0g", //"c_havocd_l0.w3d",
|
||
|
"`\\dgj\\pzg\\o3-t0g", //"c_gdi_syd_l0.w3d",
|
||
|
"`\\dgj\\pzg1\\o3-t0g", //"c_gdi_syd2_l0.w3d",
|
||
|
"`\\dgj\\nlaj\\o3-t0g", //"c_gdi_mobi_l0.w3d",
|
||
|
"`\\dgj\\klwt\\o3-t0g", //"c_gdi_hotw_l0.w3d",
|
||
|
"`\\dgj\\ow\\o3-t0g", //"c_gdi_lt_l0.w3d",
|
||
|
"`\\mlg\\sfwq\\o3-t0g", //"c_nod_petr_l0.w3d",
|
||
|
"`\\oldbm\\o3-t0g", //"c_logan_l0.w3d",
|
||
|
"`\\dgj\\ol`hf\\o3-t0g", //"c_gdi_locke_l0.w3d",
|
||
|
"u\\mlg\\avddz-t0g", //"v_nod_buggy.w3d",
|
||
|
"u\\mlg\\bs`\\n-t0g", //"v_nod_apc_m.w3d",
|
||
|
"u\\mlg\\bqwoqz-t0g", //"v_nod_artlry.w3d",
|
||
|
"u\\mlg\\eobnf-t0g", //"v_nod_flame.w3d",
|
||
|
"u\\mlg\\owbmh-t0g", //"v_nod_ltank.w3d",
|
||
|
"u\\mlg\\pwowk-t0g", //"v_nod_stlth.w3d",
|
||
|
"u\\mlg\\wqmpsw\\n-t0g", //"v_nod_trnspt_m.w3d",
|
||
|
"u\\mlg\\bsb`kf\\n-t0g", //"v_nod_apache_m.w3d",
|
||
|
"u\\`kbnfoflm-t0g", //"v_chameleon.w3d",
|
||
|
"u\\dgj\\kvnuff-t0g", //"v_gdi_humvee.w3d",
|
||
|
"u\\dgj\\bs`\\n-t0g", //"v_gdi_apc_m.w3d",
|
||
|
"u\\dgj\\nqop-t0g", //"v_gdi_mrls.w3d",
|
||
|
"u\\dgj\\nfgwmh-t0g", //"v_gdi_medtnk.w3d",
|
||
|
"u\\dgj\\nbnnwk-t0g", //"v_gdi_mammth.w3d",
|
||
|
"u\\sj`hvs32-t0g", //"v_pickup01.w3d",
|
||
|
"u\\pfgbm32-t0g", //"v_sedan01.w3d",
|
||
|
"u\\dgj\\lq`b\\n-t0g", //"v_gdi_orca_m.w3d",
|
||
|
"u\\dgj\\wqmpsw\\n-t0g", //"v_gdi_trnspt_m.w3d",
|
||
|
|
||
|
};
|
||
|
#define NUM_CRC_FILES (sizeof( filelist ) / sizeof( filelist[0] ) )
|
||
|
crc = 0;
|
||
|
tm.Rotate_Y( 2.8f );
|
||
|
for ( int i = 0; i < NUM_CRC_FILES; i++ ) {
|
||
|
StringClass name = filelist[i];
|
||
|
// Obfuscate name
|
||
|
char * n = &name[0];
|
||
|
while ( *n ) *n++ ^= 0x3;
|
||
|
tm.Rotate_Z( 3.6f );
|
||
|
// Debug_Say(( " \"%s\",\n", name ));
|
||
|
FileClass * file = _TheFileFactory->Get_File( name );
|
||
|
tm.Pre_Rotate_X( 0.3f );
|
||
|
if ( file && file->Is_Available() ) {
|
||
|
int size = file->Size();
|
||
|
tm.Pre_Rotate_Y( 0.4f );
|
||
|
file->Open();
|
||
|
tm.Pre_Rotate_Z( 0.5f );
|
||
|
while ( size > 0 ) {
|
||
|
unsigned char buffer[ 4096 ];
|
||
|
int amount = min( (int)size, (int)sizeof(buffer) );
|
||
|
tm.Translate_X( 3.1f );
|
||
|
amount = file->Read( buffer, amount );
|
||
|
tm.Translate_Y( 4.6f );
|
||
|
crc = CRC_Memory( buffer, amount, crc );
|
||
|
tm.Translate_Z( 8.2f );
|
||
|
size -= amount;
|
||
|
}
|
||
|
file->Close();
|
||
|
} else {
|
||
|
// Debug_Say(( "***%s not found\n", name ));
|
||
|
}
|
||
|
|
||
|
tm.Rotate_X( 1.4f );
|
||
|
}
|
||
|
}
|
||
|
return crc;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
bool _UseLatencyInterpret = true;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Interpret_Sc_Position_Data( const Vector3 & sc_position)
|
||
|
{
|
||
|
Vector3 position = sc_position;
|
||
|
|
||
|
// Only use the latency code for the star
|
||
|
if ( _UseLatencyInterpret && (this == COMBAT_STAR) ) {
|
||
|
Peek_Human_Phys()->Network_Latency_State_Update(sc_position,Vector3(0,0,0));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
if (Get_State() == HumanStateClass::TRANSITION) {
|
||
|
//
|
||
|
// Pop
|
||
|
//
|
||
|
// Set_Position(sc_position);
|
||
|
Peek_Human_Phys()->Network_State_Update(sc_position,Vector3(0,0,0));
|
||
|
} else {
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Vector3 pos_error = current_position - sc_position;
|
||
|
|
||
|
//TSS081501
|
||
|
if (Is_In_Elevator()) {
|
||
|
position.Z = current_position.Z;
|
||
|
}
|
||
|
|
||
|
// Set_Position(sc_position);
|
||
|
Peek_Human_Phys()->Network_State_Update(position,Vector3(0,0,0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Interpret_Sc_State_Data(
|
||
|
HumanStateClass::HumanStateType state, int sub_state,
|
||
|
LPCSTR trans_name, const Vector3 & velocity, const Vector3 & sc_position)
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
if ( ( Get_State() != state ) || ( Get_Sub_State() != sub_state ) ){
|
||
|
if ( ( Get_State() == HumanStateClass::TRANSITION ) && // If in TRANSITION, don't switch to TRANSITION_COMPLETE
|
||
|
( state == HumanStateClass::TRANSITION_COMPLETE ) ) {
|
||
|
// do nothing
|
||
|
} else if ( ( Get_State() == HumanStateClass::UPRIGHT ) && // If UPRIGHT, don't switch to LAND
|
||
|
( state == HumanStateClass::LAND ) ) {
|
||
|
// do nothing
|
||
|
} else {
|
||
|
if (Is_Controlled_By_Me() &&
|
||
|
((( Get_State() == HumanStateClass::UPRIGHT ) || ( Get_State() == HumanStateClass::AIRBORNE ) || ( Get_State() == HumanStateClass::LAND )) &&
|
||
|
(( state == HumanStateClass::UPRIGHT ) || ( state == HumanStateClass::AIRBORNE ) || ( state == HumanStateClass::LAND )) )) {
|
||
|
// do nothing
|
||
|
// } else if (Is_Controlled_By_Me() == false) {
|
||
|
} else {
|
||
|
// take the new state
|
||
|
if (state == HumanStateClass::TRANSITION) {
|
||
|
Start_Transition_Animation( trans_name, NULL );
|
||
|
AnimationName = trans_name;
|
||
|
} else if (state == HumanStateClass::ANIMATION) {
|
||
|
AnimationName = trans_name;
|
||
|
HumanState.Start_Scripted_Animation( AnimationName, true, false );
|
||
|
} else if (state == HumanStateClass::IN_VEHICLE) {
|
||
|
HumanState.Force_Animation( trans_name, false );
|
||
|
AnimationName = trans_name;
|
||
|
}
|
||
|
|
||
|
//TSS2001
|
||
|
// if (state != HumanStateClass::IN_VEHICLE) {
|
||
|
HumanState.Set_State( state, sub_state );
|
||
|
// }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (( state == HumanStateClass::AIRBORNE ) && ( this != COMBAT_STAR ) ) {
|
||
|
Peek_Human_Phys()->Set_In_Contact( false );
|
||
|
Peek_Human_Phys()->Set_Velocity( velocity );
|
||
|
Peek_Human_Phys()->Set_Position( sc_position );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int SoldierGameObj::Tally_Vis_Visible_Soldiers( void )
|
||
|
{
|
||
|
int retcode = -1;
|
||
|
|
||
|
Vector3 position;
|
||
|
Get_Position(&position);
|
||
|
position.Z += 1.5f;
|
||
|
|
||
|
WWASSERT(COMBAT_SCENE != NULL);
|
||
|
VisTableClass * pvs = COMBAT_SCENE->Get_Vis_Table(position);
|
||
|
|
||
|
if (pvs != NULL) {
|
||
|
|
||
|
retcode = 0;
|
||
|
|
||
|
for (
|
||
|
SLNode<SmartGameObj> * smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head();
|
||
|
smart_objnode != NULL;
|
||
|
smart_objnode = smart_objnode->Next()) {
|
||
|
|
||
|
SoldierGameObj * p_soldier = smart_objnode->Data()->As_SoldierGameObj();
|
||
|
if (p_soldier != NULL) {
|
||
|
|
||
|
PhysClass * phys_obj = p_soldier->Peek_Physical_Object();
|
||
|
if (phys_obj != NULL && pvs->Get_Bit(phys_obj->Get_Vis_Object_ID())) {
|
||
|
retcode++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retcode;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Is_In_Elevator( void )
|
||
|
{
|
||
|
HumanPhysClass * p_human_phys = Peek_Human_Phys();
|
||
|
|
||
|
return
|
||
|
p_human_phys != NULL &&
|
||
|
p_human_phys->Peek_Carrier_Object() != NULL &&
|
||
|
p_human_phys->Peek_Carrier_Object()->As_ElevatorPhysClass() != NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float SoldierGameObj::Get_Max_Speed( void )
|
||
|
{
|
||
|
return Peek_Human_Phys()->Get_Normalized_Speed();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Max_Speed( float speed )
|
||
|
{
|
||
|
Peek_Human_Phys()->Set_Normalized_Speed( speed );
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
float SoldierGameObj::Get_Turn_Rate( void )
|
||
|
{
|
||
|
return Get_Definition().TurnRate;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Generate_Control( void )
|
||
|
{
|
||
|
switch( Get_State() ) {
|
||
|
case HumanStateClass::DEATH:
|
||
|
// case HumanStateClass::WOUNDED:
|
||
|
// case HumanStateClass::DESTROY:
|
||
|
Clear_Control();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
SmartGameObj::Generate_Control();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Apply_Control( void )
|
||
|
{
|
||
|
// if gameplay not permitted, skip
|
||
|
if ( !CombatManager::Is_Gameplay_Permitted() ) {
|
||
|
Clear_Control();
|
||
|
Controller.Reset();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef WWDEBUG
|
||
|
// Change the control before applying
|
||
|
if ( IS_SOLOPLAY && Is_Human_Controlled() && Input::Get_State( INPUT_FUNCTION_DEBUG_RAPID_MOVE ) ) {
|
||
|
if ( InFlyMode ) {
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_UP,
|
||
|
Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );
|
||
|
} else {
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD,
|
||
|
Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) * 10 );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( Get_State() == HumanStateClass::IN_VEHICLE ) {
|
||
|
|
||
|
// Handle Vehicle Gunner Toggle
|
||
|
if ( Get_Vehicle() && Get_Vehicle()->Get_Driver() == this ) {
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_VEHICLE_TOGGLE_GUNNER ) ) {
|
||
|
Get_Vehicle()->Toggle_Driver_Is_Gunner();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( CombatManager::I_Am_Server() ) // Server authoritative
|
||
|
// if ( CombatManager::I_Am_Server() ||
|
||
|
// (Is_Controlled_By_Me() && TransitionCompletionData == NULL) )
|
||
|
{
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_ACTION ) ) {
|
||
|
TransitionManager::Check( this, true );
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( ( Get_State() == HumanStateClass::TRANSITION ) ||
|
||
|
( Get_State() == HumanStateClass::ANIMATION ) ||
|
||
|
( Get_State() == HumanStateClass::DESTROY ) ||
|
||
|
( Get_State() == HumanStateClass::DEATH ) ||
|
||
|
( Get_State() == HumanStateClass::ON_FIRE ) ||
|
||
|
( Get_State() == HumanStateClass::ON_CHEM ) ||
|
||
|
( Get_State() == HumanStateClass::ON_CNC_FIRE ) ||
|
||
|
( Get_State() == HumanStateClass::ON_CNC_CHEM ) ||
|
||
|
( Get_State() == HumanStateClass::ON_ELECTRIC ) ) {
|
||
|
|
||
|
// Force exit of the corpse mode
|
||
|
if ( Get_State() == HumanStateClass::DESTROY && Is_Human_Controlled() ) {
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) ) {
|
||
|
HumanState.Set_State_Timer( 1000 ); // Done!
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Debug_Say(( "No Control in transition\n" ));
|
||
|
Clear_Control();
|
||
|
// Controller.Reset();
|
||
|
// return;
|
||
|
}
|
||
|
|
||
|
if ( Get_State() == HumanStateClass::DIVE ) {
|
||
|
|
||
|
Clear_Control();
|
||
|
|
||
|
SmartGameObj::Apply_Control();
|
||
|
|
||
|
Vector3 forced_move(0,0,0);
|
||
|
int sub_state = HumanState.Get_Sub_State();
|
||
|
if ( sub_state & HumanStateClass::SUB_STATE_FORWARD ) forced_move = Vector3( 0.5f,0,0 );
|
||
|
if ( sub_state & HumanStateClass::SUB_STATE_BACKWARD ) forced_move = Vector3( -0.5f,0,0 );
|
||
|
if ( sub_state & HumanStateClass::SUB_STATE_LEFT ) forced_move = Vector3( 0,0.5f,0 );
|
||
|
if ( sub_state & HumanStateClass::SUB_STATE_RIGHT ) forced_move = Vector3( 0,-0.5f,0 );
|
||
|
|
||
|
Controller.Set_Move_Forward( forced_move.X );
|
||
|
Controller.Set_Move_Left( forced_move.Y );
|
||
|
Controller.Set_Move_Up( forced_move.Z );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#define CLIMB_SCALE 0.3f
|
||
|
|
||
|
// Skip all this if control is clear?!?!?!
|
||
|
if ( Get_State() == HumanStateClass::LADDER ) {
|
||
|
// Convert Forward motion to Up motion
|
||
|
|
||
|
float up_down = CLIMB_SCALE * Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD );
|
||
|
|
||
|
if ( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) == 0 ) {
|
||
|
LadderUpMask = false;
|
||
|
LadderDownMask = false;
|
||
|
}
|
||
|
|
||
|
if ( LadderUpMask ) {
|
||
|
up_down = MIN( up_down, 0 );
|
||
|
}
|
||
|
if ( LadderDownMask ) {
|
||
|
up_down = MAX( up_down, 0 );
|
||
|
}
|
||
|
|
||
|
// when on ladder, clear all velocity
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_UP, up_down );
|
||
|
|
||
|
|
||
|
// Can't clear this, or we wont be able to climb next net frame!!!
|
||
|
// Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_LEFT, 0 );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_JUMP, 0 );
|
||
|
} else {
|
||
|
LadderUpMask = false;
|
||
|
LadderDownMask = false;
|
||
|
|
||
|
// no move up
|
||
|
|
||
|
if ( !InFlyMode ) {
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_UP, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_CROUCH ) ) {
|
||
|
if ( HumanState.Is_Sub_State_Adjustable() ) {
|
||
|
// HumanState.Set_Sub_State( Get_Sub_State() ^ HumanStateClass::CROUCHED );
|
||
|
HumanState.Toggle_State_Flag( HumanStateClass::CROUCHED_FLAG );
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position( &pos );
|
||
|
if ( Is_Crouched() ) {
|
||
|
DIAG_LOG(( "COEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
} else {
|
||
|
DIAG_LOG(( "COEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
if ( HumanState.Is_Sub_State_Adjustable() ) {
|
||
|
|
||
|
bool new_state = Control.Get_Boolean( ControlClass::BOOLEAN_CROUCH );
|
||
|
bool old_state = HumanState.Get_State_Flag( HumanStateClass::CROUCHED_FLAG );
|
||
|
|
||
|
if ( new_state != old_state ) {
|
||
|
|
||
|
HumanState.Toggle_State_Flag( HumanStateClass::CROUCHED_FLAG );
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position( &pos );
|
||
|
if ( Is_Crouched() ) {
|
||
|
DIAG_LOG(( "COEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
} else {
|
||
|
DIAG_LOG(( "COEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
if ( CombatManager::I_Am_Server() &&
|
||
|
Control.Get_Boolean(ControlClass::BOOLEAN_DROP_FLAG) ) {
|
||
|
CombatManager::Soldier_Drops_Flag(this);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
DetonateC4 = false;
|
||
|
if ( Get_Weapon() && Control.Get_Boolean( ControlClass::BOOLEAN_WEAPON_USE ) ) {
|
||
|
if ( Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4 ) {
|
||
|
DetonateC4 = true;
|
||
|
}
|
||
|
|
||
|
if ( Get_Weapon()->Get_Can_Snipe() ) {
|
||
|
if ( HumanState.Is_Sub_State_Adjustable() ) {
|
||
|
HumanState.Toggle_State_Flag( HumanStateClass::SNIPING_FLAG );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
if ( Is_Sniping() ) {
|
||
|
|
||
|
// No motion for snipers (I don't know if this should be in Soldier.cpp)
|
||
|
// (But then, Sniper mode means nothing for plain soldiers, only for players)
|
||
|
// Don't clear the move forward, just don't allow the controller to have
|
||
|
// any move forward. This was not alowing sniper zoom
|
||
|
// Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_MOVE_LEFT, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_JUMP, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_CROUCH, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_LEFT, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_ACTION, 0 ); // stop ladder
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( Is_Sniping() || Is_On_Ladder() ) {
|
||
|
// No diving for snipers or on ladders
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_LEFT, 0 );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT, 0 );
|
||
|
}
|
||
|
|
||
|
bool dove = false;
|
||
|
|
||
|
// Can only dive if upright
|
||
|
if ( Get_State() == HumanStateClass::UPRIGHT ) {
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD ) ) {
|
||
|
HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_FORWARD );
|
||
|
dove = true;
|
||
|
}
|
||
|
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD ) ) {
|
||
|
HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_BACKWARD );
|
||
|
dove = true;
|
||
|
}
|
||
|
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_LEFT ) ) {
|
||
|
HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_LEFT );
|
||
|
dove = true;
|
||
|
}
|
||
|
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT ) ) {
|
||
|
HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_RIGHT );
|
||
|
dove = true;
|
||
|
}
|
||
|
|
||
|
if ( dove && this == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position( &pos );
|
||
|
DefenseObjectClass * defense = Get_Defense_Object();
|
||
|
const char * weapon_name = "";
|
||
|
int ammo = 0;
|
||
|
if ( Get_Weapon() != NULL ) {
|
||
|
weapon_name = Get_Weapon()->Get_Definition()->Get_Name();
|
||
|
ammo = Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
DIAG_LOG(( "ROUS", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), weapon_name, ammo ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle facing
|
||
|
float amount = Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT );
|
||
|
if ( COMBAT_STAR == this ) { // Star gets turning from the camera
|
||
|
amount = 0;
|
||
|
}
|
||
|
if ( amount != 0 ) {
|
||
|
float heading = Peek_Human_Phys()->Get_Heading();
|
||
|
heading += amount * Get_Turn_Rate() * TimeManager::Get_Frame_Seconds();
|
||
|
heading = WWMath::Wrap( heading, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
|
||
|
Peek_Human_Phys()->Set_Heading(heading);
|
||
|
Control.Set_Analog(ControlClass::ANALOG_TURN_LEFT,0.0f);
|
||
|
if ( !Is_Human_Controlled() ) { // human players don't use turn anims
|
||
|
HumanState.Set_Turn_Velocity( amount ); // play the leg turning anim
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Let parent class handle the rest
|
||
|
SmartGameObj::Apply_Control();
|
||
|
|
||
|
#if 01
|
||
|
// Have to Move_Up after after Apply control, but BOOLEAN_JUMP is cleared
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
|
||
|
if ( Get_State() != HumanStateClass::AIRBORNE ) {
|
||
|
Controller.Set_Move_Up( Get_Definition().JumpVelocity );
|
||
|
}
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position( &pos );
|
||
|
DefenseObjectClass * defense = Get_Defense_Object();
|
||
|
const char * weapon_name = "";
|
||
|
int ammo = 0;
|
||
|
if ( Get_Weapon() != NULL ) {
|
||
|
weapon_name = Get_Weapon()->Get_Definition()->Get_Name();
|
||
|
ammo = Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
DIAG_LOG(( "JUUS", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), weapon_name, ammo ));
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
static float jump_timer = 0;
|
||
|
|
||
|
// Have to Move_Up after after Apply control, but BOOLEAN_JUMP is cleared
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
|
||
|
if ( Get_State() != HumanStateClass::AIRBORNE ) {
|
||
|
HumanState.Set_State( HumanStateClass::AIRBORNE, HumanState.Get_Sub_State() );
|
||
|
jump_timer = 0.2f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( jump_timer > 0 ) {
|
||
|
jump_timer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( jump_timer <= 0 ) {
|
||
|
Controller.Set_Move_Up( Get_Definition().JumpVelocity );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#if 0
|
||
|
if ( Is_Sniping() ) {
|
||
|
// Dont allow move forward. It may have been requested by the
|
||
|
// Control, because we can't clear it if it must persist for
|
||
|
// network sniper zoom. So just clear it here.
|
||
|
Controller.Set_Move_Forward( 0 );
|
||
|
}
|
||
|
#else
|
||
|
if ( Is_Sniping() ) {
|
||
|
// Dont allow move forward. It may have been requested by the
|
||
|
// Control, because we can't clear it if it must persist for
|
||
|
// network sniper zoom. So just clear it here.
|
||
|
Controller.Set_Move_Forward( WWMath::Clamp( Controller.Get_Move_Forward(), -0.25f, 0.25f ) );
|
||
|
Controller.Set_Move_Left( WWMath::Clamp( Controller.Get_Move_Left(), -0.25f, 0.25f ) );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( Get_State() == HumanStateClass::LADDER ) {
|
||
|
// When on ladder, you can't move forward
|
||
|
// Same rules as above wrt the network persistence
|
||
|
Controller.Set_Move_Forward( 0 );
|
||
|
}
|
||
|
|
||
|
if ( ( Get_State() == HumanStateClass::UPRIGHT ) && Is_Crouched() ) {
|
||
|
float crouch_speed = GlobalSettingsDef::Get_Global_Settings ()->Get_Soldier_Crouch_Speed();
|
||
|
Controller.Set_Move_Forward( Controller.Get_Move_Forward() * crouch_speed );
|
||
|
Controller.Set_Move_Left( Controller.Get_Move_Left() * crouch_speed );
|
||
|
} else if ( Control.Get_Boolean( ControlClass::BOOLEAN_WALK ) ) {
|
||
|
float walk_speed = GlobalSettingsDef::Get_Global_Settings ()->Get_Soldier_Walk_Speed();
|
||
|
Controller.Set_Move_Forward( Controller.Get_Move_Forward() * walk_speed );
|
||
|
Controller.Set_Move_Left( Controller.Get_Move_Left() * walk_speed );
|
||
|
}
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
static bool was_walking = false;
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_WALK ) != was_walking ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position( &pos );
|
||
|
if ( !was_walking ) {
|
||
|
DIAG_LOG(( "WAEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
} else {
|
||
|
DIAG_LOG(( "WAEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
}
|
||
|
was_walking = Control.Get_Boolean( ControlClass::BOOLEAN_WALK );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( this == COMBAT_STAR && Control.Get_Boolean( ControlClass::BOOLEAN_ACTION ) ) {
|
||
|
Vector3 pos;
|
||
|
COMBAT_STAR->Get_Position( &pos );
|
||
|
DIAG_LOG(( "ACPR", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( CombatManager::I_Am_Server() ||
|
||
|
(Is_Controlled_By_Me() && TransitionCompletionData == NULL) )
|
||
|
{
|
||
|
bool action_triggered = Control.Get_Boolean( ControlClass::BOOLEAN_ACTION );
|
||
|
|
||
|
/// Transitions only trigger on server
|
||
|
bool transition_triggered = false;
|
||
|
if ( CombatManager::I_Am_Server() ) {
|
||
|
transition_triggered = TransitionManager::Check( this, action_triggered );
|
||
|
}
|
||
|
|
||
|
if ( !transition_triggered ) {
|
||
|
if ( action_triggered ) {
|
||
|
|
||
|
//
|
||
|
// Lookup the current target
|
||
|
//
|
||
|
// DamageableGameObj *damageable_target = HUDInfo::Get_Weapon_Target_Object();
|
||
|
DamageableGameObj *damageable_target = HUDInfo::Get_Info_Object();
|
||
|
if ( damageable_target != NULL ) {
|
||
|
PhysicalGameObj *physical_target = damageable_target->As_PhysicalGameObj();
|
||
|
if ( physical_target != NULL ) {
|
||
|
Vector3 target_pos;
|
||
|
physical_target->Get_Position( &target_pos );
|
||
|
Vector3 my_pos;
|
||
|
Get_Position( &my_pos );
|
||
|
|
||
|
//
|
||
|
// Check to see if the target is within the "poke" range
|
||
|
//
|
||
|
if ( (target_pos - my_pos).Length() <= 2 ) {
|
||
|
|
||
|
//
|
||
|
// Notify all the observers
|
||
|
//
|
||
|
if ( CombatManager::I_Am_Server () ) {
|
||
|
const GameObjObserverList & observer_list = physical_target->Get_Observers();
|
||
|
for( int index = 0; index < observer_list.Count(); index++ ) {
|
||
|
observer_list[ index ]->Poked( physical_target, this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Was the player the soldier who did the poking?
|
||
|
//
|
||
|
if ( COMBAT_STAR == this ) {
|
||
|
|
||
|
//
|
||
|
// Display this object in the encyclopedia
|
||
|
//
|
||
|
EncyclopediaMgrClass::Reveal_Object( damageable_target );
|
||
|
|
||
|
//
|
||
|
// Check for player terminal access
|
||
|
//
|
||
|
if ( physical_target->As_SimpleGameObj () != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Is this a player terminal?
|
||
|
//
|
||
|
PlayerTerminalClass::TYPE type = PlayerTerminalClass::TYPE_NONE;
|
||
|
type = (physical_target->As_SimpleGameObj())->Get_Definition().Get_Player_Terminal_Type();
|
||
|
if (type != PlayerTerminalClass::TYPE_NONE) {
|
||
|
|
||
|
//
|
||
|
// Show the player terminal
|
||
|
//
|
||
|
PlayerTerminalClass::Get_Instance()->Display_Terminal( this, type );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// We started a transition, clear the control
|
||
|
Controller.Reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Override the following controls
|
||
|
if ( IS_SOLOPLAY && Is_Human_Controlled() && Input::Get_State( INPUT_FUNCTION_DEBUG_RAPID_MOVE ) ) {
|
||
|
if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
|
||
|
Controller.Set_Move_Up( Get_Definition().JumpVelocity );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Handle_Legs( void )
|
||
|
{
|
||
|
#if 0
|
||
|
if ( Get_State() != HumanStateClass::UPRIGHT ) {
|
||
|
LegFacing = Get_Facing();
|
||
|
SyncLegs = false;
|
||
|
}
|
||
|
|
||
|
// If Moving, clear LegFacing
|
||
|
if ((Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f ) ||
|
||
|
(Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ) != 0.0f )) {
|
||
|
|
||
|
LegFacing = Get_Facing();
|
||
|
SyncLegs = false;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
#define CLAMP_DEGREES 60
|
||
|
#define SYNC_DEGREES 35
|
||
|
#define SYNC_RATE 75
|
||
|
#define FLIP_RATE 720
|
||
|
|
||
|
// find the leg difference;
|
||
|
LegFacing = WWMath::Wrap( LegFacing, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );
|
||
|
|
||
|
float legs_rotation = Get_Facing() - LegFacing;
|
||
|
|
||
|
legs_rotation = WWMath::Wrap( legs_rotation, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );
|
||
|
|
||
|
// Clamp to with 90
|
||
|
legs_rotation = WWMath::Clamp( legs_rotation, DEG_TO_RADF( -CLAMP_DEGREES ), DEG_TO_RADF( CLAMP_DEGREES ) );
|
||
|
LegFacing = Get_Facing() - legs_rotation;
|
||
|
LegFacing = WWMath::Wrap( LegFacing, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );
|
||
|
|
||
|
// if legs are more than 30 degrees off, start correcting
|
||
|
if ( WWMath::Fabs( legs_rotation ) > DEG_TO_RAD( SYNC_DEGREES ) ) {
|
||
|
SyncLegs = true;
|
||
|
}
|
||
|
|
||
|
SyncLegs = true;
|
||
|
|
||
|
// if syncing, start moving legs to match rotation
|
||
|
if ( SyncLegs ) {
|
||
|
float move = DEG_TO_RAD( SYNC_RATE ) * TimeManager::Get_Frame_Seconds() *
|
||
|
WWMath::Sign( legs_rotation );
|
||
|
if ( WWMath::Fabs( move ) >= WWMath::Fabs( legs_rotation ) ) {
|
||
|
move = legs_rotation; // Complete syncing
|
||
|
SyncLegs = false;
|
||
|
} else {
|
||
|
LegFacing += move;
|
||
|
if ( !Is_Human_Controlled() ) { // human players don't use turn anims
|
||
|
HumanState.Set_Turn_Velocity( move ); // Also, play the leg turning anim
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !SyncLegs ) {
|
||
|
HumanState.Set_Turn_Velocity( 0 );
|
||
|
}
|
||
|
|
||
|
// 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 = Peek_Model()->Get_Bone_Index( "root" );
|
||
|
}
|
||
|
|
||
|
static int torso_bone = -1;
|
||
|
if ( torso_bone == -1 ) { // Get torso bone index
|
||
|
torso_bone = Peek_Model()->Get_Bone_Index( "thorax" );
|
||
|
}
|
||
|
// Update the model
|
||
|
float legs_rotation = Get_Facing() - LegFacing;
|
||
|
if ( legs_rotation ) {
|
||
|
|
||
|
WWASSERT( root_bone != -1 );
|
||
|
WWASSERT( torso_bone != -1 );
|
||
|
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( root_bone ) ) {
|
||
|
Peek_Model()->Capture_Bone( root_bone );
|
||
|
}
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
|
||
|
Peek_Model()->Capture_Bone( torso_bone );
|
||
|
}
|
||
|
|
||
|
// LOOK INTO RELATIVE_CONTROL_BONE
|
||
|
Matrix3D root_adjust(1); // adjust it
|
||
|
root_adjust.Rotate_Z( -legs_rotation );
|
||
|
Peek_Model()->Control_Bone( root_bone, root_adjust );
|
||
|
|
||
|
Matrix3D legs_adjust(1); // adjust it
|
||
|
legs_adjust.Rotate_Z( legs_rotation );
|
||
|
Peek_Model()->Control_Bone( torso_bone, legs_adjust );
|
||
|
} else { // no adjustment, release
|
||
|
|
||
|
if ( Peek_Model()->Is_Bone_Captured( root_bone ) ) {
|
||
|
Peek_Model()->Release_Bone( root_bone );
|
||
|
}
|
||
|
if ( Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
|
||
|
Peek_Model()->Release_Bone( torso_bone );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
#if 0
|
||
|
float legs_rotation = 0;
|
||
|
|
||
|
// Compare the facing to the motion, set leg_racing to the difference
|
||
|
Vector3 move( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ), Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ), 0 );
|
||
|
if ( move.Length() > 0 ) {
|
||
|
float move_direction = ::WWMath::Atan2( -move.Y, move.X );
|
||
|
float diff = move_direction;
|
||
|
diff += 2*DEG_TO_RADF( 360 ) + DEG_TO_RADF( 45 );
|
||
|
diff -= WWMath::Floor( diff / DEG_TO_RADF( 90 ) ) * DEG_TO_RADF( 90 );
|
||
|
diff -= DEG_TO_RADF( 45 );
|
||
|
legs_rotation = diff;
|
||
|
Debug_Say(( "Move (%1.1f) %1.1f %1.1f %1.1f %1.1f\n", RAD_TO_DEG(diff), move.X, move.Y, move.Z, RAD_TO_DEG( move_direction ) ));
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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 = Peek_Model()->Get_Bone_Index( "c spine" );
|
||
|
}
|
||
|
|
||
|
static int torso_bone = -1;
|
||
|
if ( torso_bone == -1 ) { // Get torso bone index
|
||
|
torso_bone = Peek_Model()->Get_Bone_Index( "c spine1" );
|
||
|
}
|
||
|
|
||
|
if ( legs_rotation ) {
|
||
|
|
||
|
WWASSERT( root_bone != -1 );
|
||
|
WWASSERT( torso_bone != -1 );
|
||
|
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( root_bone ) ) {
|
||
|
Peek_Model()->Capture_Bone( root_bone );
|
||
|
}
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
|
||
|
Peek_Model()->Capture_Bone( torso_bone );
|
||
|
}
|
||
|
|
||
|
Matrix3D root_adjust(1); // adjust it
|
||
|
root_adjust.Rotate_X( legs_rotation );
|
||
|
Peek_Model()->Control_Bone( root_bone, root_adjust );
|
||
|
|
||
|
Matrix3D legs_adjust(1); // adjust it
|
||
|
legs_adjust.Rotate_X( -legs_rotation );
|
||
|
Peek_Model()->Control_Bone( torso_bone, legs_adjust );
|
||
|
} else { // no adjustment, release
|
||
|
|
||
|
if ( Peek_Model()->Is_Bone_Captured( root_bone ) ) {
|
||
|
Peek_Model()->Release_Bone( root_bone );
|
||
|
}
|
||
|
if ( Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
|
||
|
Peek_Model()->Release_Bone( torso_bone );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
bool do_steps = false;
|
||
|
if ( Is_On_Ladder() ) {
|
||
|
do_steps = (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f );
|
||
|
} else {
|
||
|
do_steps = ((Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f ) ||
|
||
|
(Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ) != 0.0f ));
|
||
|
}
|
||
|
|
||
|
// Footsteps
|
||
|
if (!Is_Sniping() && !InFlyMode && CombatManager::Is_Gameplay_Permitted() && do_steps && Is_Control_Enabled() ) {
|
||
|
|
||
|
bool leg_mode = HumanState.Get_Leg_Mode();
|
||
|
|
||
|
// Watch for the legs to reach certain percentage completions
|
||
|
|
||
|
if ( leg_mode != LastLegMode ) {
|
||
|
|
||
|
int my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_RUN;
|
||
|
if ( Is_Crouched() ) {
|
||
|
my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_CROUCHED;
|
||
|
} else if ( HumanState.Get_Sub_State() & HumanStateClass::SUB_STATE_SLOW ) {
|
||
|
my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_WALK;
|
||
|
}
|
||
|
|
||
|
int ground_type = Peek_Human_Phys()->Get_Contact_Surface_Type();
|
||
|
|
||
|
// Try effects on ladders again BMG
|
||
|
/**/if ( Is_On_Ladder() ) {
|
||
|
my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_LADDER;
|
||
|
ground_type = SURFACE_TYPE_LIGHT_METAL;
|
||
|
}/**/
|
||
|
|
||
|
Matrix3D tm;
|
||
|
tm = Get_Transform();
|
||
|
if ( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f ) {
|
||
|
tm.Rotate_Z( DEG_TO_RAD( -90 ) );
|
||
|
}
|
||
|
float offset = leg_mode ? -0.15f : 0.1f;
|
||
|
tm.Translate( Vector3( offset, 0.3f, 0 ) );
|
||
|
|
||
|
bool allow_emitters = !Is_Stealthed();
|
||
|
SurfaceEffectsManager::Apply_Effect( ground_type, my_type, tm, NULL, this, true, allow_emitters);
|
||
|
LastLegMode = leg_mode;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static float _shake_delay = 0;
|
||
|
static float _cry_delay = 0;
|
||
|
|
||
|
static char * _profile_name = "Soldier Think";
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Think( void )
|
||
|
{
|
||
|
{ WWPROFILE( _profile_name );
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
_shake_delay -= TimeManager::Get_Frame_Seconds();
|
||
|
_cry_delay -= TimeManager::Get_Frame_Seconds();
|
||
|
}
|
||
|
/*
|
||
|
ActionClass * p_action = Get_Action();
|
||
|
WWASSERT(p_action != NULL);
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// Simply check to see if this soldier has entered a coordination zone (which bound
|
||
|
// the entrances/exits of ladders and elevators). If so, then disable
|
||
|
// collision between them and all other soldiers.
|
||
|
//
|
||
|
Vector3 position;
|
||
|
{
|
||
|
WWPROFILE("Coordination Zone");
|
||
|
Get_Position( &position );
|
||
|
if ( UnitCoordinationZoneMgr::Is_Unit_In_Zone( position ) ) {
|
||
|
Enable_Ghost_Collision( true );
|
||
|
} else if ( Is_Safe_To_Disable_Ghost_Collision( position ) ) {
|
||
|
Enable_Ghost_Collision( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Display a debug box to show the ghosted collision as ncessary
|
||
|
//
|
||
|
if ( DisplayDebugBoxForGhostCollision &&
|
||
|
(Peek_Physical_Object ()->Get_Collision_Group() == SOLDIER_GHOST_COLLISION_GROUP))
|
||
|
{
|
||
|
WWPROFILE("Add_Debug_AABox");
|
||
|
AABoxClass soldier_box;
|
||
|
soldier_box.Center = position + Vector3( 0, 0, 1.0F );
|
||
|
soldier_box.Extent.Set( 0.3F, 0.3F, 1.0F );
|
||
|
PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox( soldier_box, Vector3( 1.0F, 0.0F, 0.25F ) );
|
||
|
}
|
||
|
|
||
|
// Stats
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
WWPROFILE("Stats");
|
||
|
Get_Player_Data()->Stats_Add_Game_Time( TimeManager::Get_Frame_Seconds() );
|
||
|
Get_Player_Data()->Stats_Set_Final_Health( Get_Defense_Object()->Get_Health() );
|
||
|
if ( Get_Vehicle() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Vehicle_Time( TimeManager::Get_Frame_Seconds() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the soldier's facing
|
||
|
//
|
||
|
{
|
||
|
WWPROFILE("Update_Locked_Facing");
|
||
|
Update_Locked_Facing();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
//
|
||
|
// In CTF a flag-bearer may not be permitted to use weapons
|
||
|
// This code will deselect the weapon if that is the case.
|
||
|
//
|
||
|
if (CombatManager::I_Am_Server() && Get_Weapon() != NULL &&
|
||
|
!CombatManager::Is_Firing_Enabled(this)) {
|
||
|
Get_Weapon_Bag()->Deselect();
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// Handle_Legs moved form Apply_Control because clients don't run it for server objects
|
||
|
{
|
||
|
WWPROFILE("Handle_Legs");
|
||
|
Handle_Legs();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if (CombatManager::I_Am_Server()) {
|
||
|
TransitionManager::Check( this );
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
{
|
||
|
WWPROFILE( "Embedded smart think in soldier" );
|
||
|
SmartGameObj::Think(); // Perform smart object thinking ( apply controls )
|
||
|
}
|
||
|
|
||
|
{
|
||
|
WWPROFILE( _profile_name );
|
||
|
|
||
|
if ( CombatManager::I_Am_Server() ) {
|
||
|
WWPROFILE("Handle C4");
|
||
|
// Handle C4 in a special way. When C4 is fired, the human plays
|
||
|
// a crouching animation, and starts a c4 placement timer.
|
||
|
if ( Get_Weapon() &&
|
||
|
(Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4) &&
|
||
|
(Get_Weapon()->Is_Firing()) &&
|
||
|
(Get_State() == HumanStateClass::UPRIGHT) ) {
|
||
|
|
||
|
AnimationName = "s_a_human.h_a_j12c";
|
||
|
HumanState.Start_Scripted_Animation( AnimationName, true, false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( Get_State() != HumanStateClass::IN_VEHICLE ) {
|
||
|
WWPROFILE("Vehicle");
|
||
|
// This may try to change controls, so can't be in Post_Think
|
||
|
HumanState.Update_Weapon( Get_Weapon(), WeaponBag->Is_Changed() );
|
||
|
|
||
|
if ( WeaponBag->Is_Changed() ) {
|
||
|
WeaponChanged = true;
|
||
|
WeaponBag->Reset_Changed();
|
||
|
}
|
||
|
|
||
|
// HumanState.Update_Aiming( Get_Weapon_Tilt(), Get_Weapon_Turn() );
|
||
|
HumanState.Update_State();
|
||
|
|
||
|
// Remove C4 from hand if empty
|
||
|
if ( Get_Weapon() && WeaponRenderModel ) {
|
||
|
if ( Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4 || Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_BEACON ) {
|
||
|
WeaponRenderModel->Set_Hidden( !Get_Weapon()->Is_Loaded() );
|
||
|
} else {
|
||
|
WeaponRenderModel->Set_Hidden( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// Debug_Say(( "No state update in vehicle\n" ));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Special Death
|
||
|
*/
|
||
|
// ArmorWarheadManager::SpecialDamageType SpecialDamageMode;
|
||
|
// float SpecialDamageTimer;
|
||
|
if ( SpecialDamageMode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE &&
|
||
|
SpecialDamageTimer > 0 ) {
|
||
|
WWPROFILE("Special Damage");
|
||
|
|
||
|
float previous_timer = SpecialDamageTimer;
|
||
|
|
||
|
SpecialDamageTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
|
||
|
// Once a second, make an explosion
|
||
|
if ( (int)SpecialDamageTimer != (int)previous_timer ) {
|
||
|
StringClass explosion_name = ArmorWarheadManager::Get_Special_Damage_Explosion( SpecialDamageMode );
|
||
|
if ( !explosion_name.Is_Empty() ) {
|
||
|
ExplosionDefinitionClass * def = (ExplosionDefinitionClass *)DefinitionMgrClass::Find_Typed_Definition( explosion_name, CLASSID_DEF_EXPLOSION );
|
||
|
if ( def != NULL) {
|
||
|
Vector3 pos = Get_Bullseye_Position();
|
||
|
ExplosionManager::Create_Explosion_At( def->Get_ID(), pos, NULL );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fix to allow taking fire damage in CNC
|
||
|
// if ( Get_Defense_Object()->Get_Health() > 0 && Allow_Special_Damage_State_Lock () ) {
|
||
|
if ( Get_Defense_Object()->Get_Health() > 0 &&
|
||
|
(Allow_Special_Damage_State_Lock () || Is_Human_Controlled() ) ) {
|
||
|
// do damage
|
||
|
WarheadType warhead = ArmorWarheadManager::Get_Special_Damage_Warhead( SpecialDamageMode );
|
||
|
float damage = ArmorWarheadManager::Get_Special_Damage_Scale( SpecialDamageMode ) *
|
||
|
TimeManager::Get_Frame_Seconds();
|
||
|
OffenseObjectClass off( damage, warhead, (ArmedGameObj*)SpecialDamageDamager.Get_Ptr() );
|
||
|
// Apply_Damage( off, 1 );
|
||
|
Apply_Damage_Extended( off, 1 );
|
||
|
}
|
||
|
|
||
|
if ( HumanState.Get_State() != HumanStateClass::DEATH ) {
|
||
|
|
||
|
// if dead, play death
|
||
|
if ( Get_Defense_Object()->Get_Health() <= 0 ) {
|
||
|
HumanState.Set_State( HumanStateClass::DEATH,
|
||
|
SpecialDamageMode - ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE + HumanStateClass::OUCH_FIRE );
|
||
|
}
|
||
|
|
||
|
if ( SpecialDamageTimer <= 0 ) { // Stop the flame
|
||
|
// HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Get_State() == HumanStateClass::DESTROY) {
|
||
|
if ( HumanState.Get_State_Timer() > CORPSE_PERSIST_TIME ) {
|
||
|
// If Human Controlled, drop a backpack
|
||
|
if ( Is_Human_Controlled() ) {
|
||
|
// PowerUpGameObj::Create_Backpack( this ); // No more backpacks
|
||
|
}
|
||
|
|
||
|
Set_Delete_Pending();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ClientOnlys doen't handle TRANSITION_COMPLETE
|
||
|
if ( !CombatManager::I_Am_Only_Client() || Is_Controlled_By_Me()) {
|
||
|
if ( Get_State() == HumanStateClass::TRANSITION_COMPLETE ) {
|
||
|
if ( TransitionCompletionData != NULL ) {
|
||
|
// Debug_Say(( "Ended Transition %p\n", Transition ));
|
||
|
// We just completed a transition, notify
|
||
|
TransitionInstanceClass::End( this, TransitionCompletionData );
|
||
|
TransitionCompletionData = NULL;
|
||
|
} else {
|
||
|
// Debug_Say(( "Ended Unknown Transition %p\n", Transition ));
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove the emot icon (if necessary)
|
||
|
//
|
||
|
{
|
||
|
WWPROFILE("EmotIcon");
|
||
|
EmotIconTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( EmotIconTimer <= 0 ) {
|
||
|
|
||
|
//
|
||
|
// Remove the emoticon
|
||
|
//
|
||
|
if ( EmotIconModel != NULL && EmotIconModel->Is_In_Scene() ) {
|
||
|
EmotIconModel->Remove();
|
||
|
REF_PTR_RELEASE( EmotIconModel );
|
||
|
}
|
||
|
|
||
|
} else if ( EmotIconModel != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Put the icon over the soldier's head
|
||
|
//
|
||
|
Matrix3D tm = Get_Transform();
|
||
|
tm.Set_Translation (tm.Get_Translation() + Vector3 (0, 0, EMOT_ICON_HEIGHT));
|
||
|
EmotIconModel->Set_Transform( tm );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Should we change the idle facial animation?
|
||
|
//
|
||
|
GenerateIdleFacialAnimTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( GenerateIdleFacialAnimTimer <= 0 ) {
|
||
|
WWPROFILE("FacialAnims");
|
||
|
|
||
|
//
|
||
|
// Change the facial animation
|
||
|
//
|
||
|
if ( HeadModel != NULL ) {
|
||
|
// Don't stop a playing script animation
|
||
|
if ( ( HeadModel->Peek_Animation() == NULL ) ||
|
||
|
(HeadModel->Peek_Animation() == SpeechAnim ) ) {
|
||
|
SpeechAnim->Generate_Idle_Animation( 20, 0.5F );
|
||
|
HeadModel->Set_Animation( SpeechAnim, 0, RenderObjClass::ANIM_MODE_LOOP );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reset the timer
|
||
|
//
|
||
|
GenerateIdleFacialAnimTimer = 100.0F;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if (!Is_Destroy()) {
|
||
|
CombatManager::Set_Soldier_Tint(this);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// Apply Surface damage
|
||
|
{
|
||
|
WWPROFILE("Apply_Damage");
|
||
|
if ( Peek_Human_Phys() ) {
|
||
|
SurfaceEffectsManager::Apply_Damage( Peek_Human_Phys()->Get_Contact_Surface_Type(), this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update the water wake
|
||
|
// - If our feet are not on "underwater dirt" we don't even check for water
|
||
|
// - Otherwise, do a ray cast to find the water surface, if it hits
|
||
|
{
|
||
|
WWPROFILE("Water");
|
||
|
bool in_water = false;
|
||
|
Vector3 p0 = Get_Transform().Get_Translation();
|
||
|
Vector3 p1 = Get_Bullseye_Position();
|
||
|
|
||
|
int hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_RUN;
|
||
|
if ( Is_Crouched() ) {
|
||
|
hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_CROUCHED;
|
||
|
} else if ( HumanState.Get_Sub_State() & HumanStateClass::SUB_STATE_SLOW ) {
|
||
|
hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_WALK;
|
||
|
}
|
||
|
|
||
|
Vector3 vel;
|
||
|
Peek_Human_Phys()->Get_Velocity(&vel);
|
||
|
|
||
|
if ((Peek_Human_Phys()->Get_Contact_Surface_Type() == SURFACE_TYPE_UNDERWATER_DIRT) &&
|
||
|
(vel.Length2() > 0.1f))
|
||
|
{
|
||
|
|
||
|
// Raycast to find water surface
|
||
|
CastResultStruct res;
|
||
|
LineSegClass ray(p0,p1);
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Peek_Human_Phys()->Inc_Ignore_Counter();
|
||
|
COMBAT_SCENE->Cast_Ray(raytest);
|
||
|
Peek_Human_Phys()->Dec_Ignore_Counter();
|
||
|
|
||
|
// if found update emitter
|
||
|
if ((res.Fraction < 1.0f) && (res.SurfaceType == SURFACE_TYPE_WATER_PERMEABLE)) {
|
||
|
|
||
|
Vector3 point;
|
||
|
ray.Compute_Point(res.Fraction,&point);
|
||
|
|
||
|
SurfaceEffectsManager::Update_Persistant_Emitter( WaterWake,
|
||
|
res.SurfaceType,
|
||
|
hit_type,
|
||
|
Matrix3D(point));
|
||
|
in_water = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!in_water) {
|
||
|
SurfaceEffectsManager::Update_Persistant_Emitter( WaterWake,
|
||
|
SURFACE_TYPE_DEFAULT,
|
||
|
hit_type,
|
||
|
Matrix3D(p0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Punish update
|
||
|
if ( Get_Player_Data() && Get_Player_Data()->Get_Punish_Timer() > 0 ) {
|
||
|
Get_Player_Data()->Inc_Punish_Timer( TimeManager::Get_Frame_Seconds() ) ;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Post_Think( void )
|
||
|
{
|
||
|
if ( Get_State() == HumanStateClass::IN_VEHICLE ) {
|
||
|
SmartGameObj::Post_Think();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
{ WWPROFILE( "Soldier PostThink" );
|
||
|
|
||
|
HumanState.Post_Think();
|
||
|
|
||
|
bool update_weapon = false;
|
||
|
|
||
|
if ( WeaponChanged ) {
|
||
|
WeaponChanged = false;
|
||
|
Update_Back_Gun();
|
||
|
update_weapon = true;
|
||
|
}
|
||
|
|
||
|
if ( ( Get_Weapon() != NULL ) && ( Get_Weapon()->Is_Model_Update_Needed() ) ) {
|
||
|
update_weapon = true;
|
||
|
Get_Weapon()->Reset_Model_Update(); // reset model update needed
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( update_weapon ) {
|
||
|
if ( Get_Weapon() != NULL ) {
|
||
|
Set_Weapon_Model( Get_Weapon()->Get_Model_Name() );
|
||
|
} else {
|
||
|
Set_Weapon_Model( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if ( Get_Weapon()->Is_Anim_Update_Needed() ) {
|
||
|
// Set_Weapon_Animation( Get_Weapon()->Get_Anim_Name() );
|
||
|
// Get_Weapon()->Reset_Anim_Update(); // reset model anim update needed
|
||
|
// }
|
||
|
|
||
|
if (WeaponAnimControl) {
|
||
|
WeaponAnimControl->Update( TimeManager::Get_Frame_Seconds() ); // update the animation control
|
||
|
}
|
||
|
|
||
|
|
||
|
Handle_Head_look();
|
||
|
|
||
|
if ( Get_Weapon() != NULL && Get_State() == HumanStateClass::ON_FIRE ) {
|
||
|
Get_Weapon()->Set_Primary_Triggered( false );
|
||
|
Get_Weapon()->Set_Secondary_Triggered( false );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
SmartGameObj::Post_Think();
|
||
|
|
||
|
Update_Healing_Effect();
|
||
|
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
const Matrix3D & SoldierGameObj::Get_Muzzle( int index )
|
||
|
{
|
||
|
static Matrix3D _muzzle(1);
|
||
|
if ( WeaponRenderModel != NULL ) {
|
||
|
Matrix3D true_muzzle = WeaponRenderModel->Get_Bone_Transform( "muzzlea0" );
|
||
|
Vector3 muzzle_pos = true_muzzle.Get_Translation();
|
||
|
_muzzle.Obj_Look_At( muzzle_pos, Get_Targeting_Pos(), 0 );
|
||
|
|
||
|
if ( !Is_Human_Controlled() ) {
|
||
|
// If the bullet is not close to going down the muzzle, force it to be
|
||
|
Vector3 to_target = _muzzle.Get_X_Vector();
|
||
|
Vector3 down_muzzle = true_muzzle.Get_X_Vector();
|
||
|
float cos = Vector3::Dot_Product( to_target, down_muzzle );
|
||
|
if ( cos < WWMath::Cos( DEG_TO_RADF( 20 ) ) ) {
|
||
|
_muzzle = true_muzzle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return _muzzle;
|
||
|
}
|
||
|
|
||
|
static int head_bone = -1;
|
||
|
static int neck_bone = -1;
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
Matrix3D SoldierGameObj::Get_Look_Transform(void)
|
||
|
{
|
||
|
if ( head_bone != -1 ) {
|
||
|
// Convert from CS head convention back to normal
|
||
|
Matrix3D tm = Peek_Model()->Get_Bone_Transform( head_bone );
|
||
|
tm.Rotate_Z( DEG_TO_RAD( 90 ) );
|
||
|
tm.Rotate_X( DEG_TO_RAD( 90 ) );
|
||
|
return tm;
|
||
|
}
|
||
|
return Get_Transform();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
#define HEAD_TURN_LIMIT DEG_TO_RADF( 70 )
|
||
|
#define HEAD_TILT_LIMIT DEG_TO_RADF( 20 )
|
||
|
|
||
|
void SoldierGameObj::Look_Random( float time )
|
||
|
{
|
||
|
if ( time == 0 && HeadLookDuration != 0 ) {
|
||
|
HeadLookDuration = 0.0001f; // maybe done next time...
|
||
|
} else {
|
||
|
HeadLookDuration = time;
|
||
|
}
|
||
|
HeadLookAngleTimer = 0;
|
||
|
HeadLookAngle = Vector3( 1,1,1 );
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
// Clamp_Angle
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
static inline float
|
||
|
Clamp_Angle (float angle, float min_angle, float max_angle)
|
||
|
{
|
||
|
//
|
||
|
// Make sure all the parameters are in the same range
|
||
|
//
|
||
|
angle = WWMath::Wrap (angle, 0, DEG_TO_RADF (360));
|
||
|
min_angle = WWMath::Wrap (min_angle, 0, DEG_TO_RADF (360));
|
||
|
max_angle = WWMath::Wrap (max_angle, 0, DEG_TO_RADF (360));
|
||
|
|
||
|
float result = angle;
|
||
|
|
||
|
if (min_angle <= max_angle) {
|
||
|
|
||
|
//
|
||
|
// Handle the 'typical' case where there is no 360-mark wrap-around
|
||
|
//
|
||
|
if (angle < min_angle) {
|
||
|
result = min_angle;
|
||
|
} else if (angle > max_angle) {
|
||
|
result = max_angle;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Handle the 360-mark wrap-around case
|
||
|
//
|
||
|
if (angle < min_angle && angle > max_angle) {
|
||
|
float min_delta = min_angle - angle;
|
||
|
float max_delta = angle - max_angle;
|
||
|
|
||
|
//
|
||
|
// Which edge is the angle closer to?
|
||
|
//
|
||
|
if (min_delta < max_delta) {
|
||
|
result = min_angle;
|
||
|
} else {
|
||
|
result = max_angle;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Handle_Head_look( void )
|
||
|
{
|
||
|
if ( Peek_Model ()->Get_HTree () == NULL ) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the head bone
|
||
|
//
|
||
|
if ( head_bone == -1 || neck_bone == -1) {
|
||
|
head_bone = Peek_Model()->Get_Bone_Index( "C HEAD" );
|
||
|
neck_bone = Peek_Model()->Get_Bone_Index( "C NECK" );
|
||
|
WWASSERT( head_bone != -1 );
|
||
|
WWASSERT( neck_bone != -1 );
|
||
|
}
|
||
|
|
||
|
if ( HeadLookDuration > 0 ) {
|
||
|
|
||
|
HeadLookDuration -= TimeManager::Get_Frame_Seconds();
|
||
|
|
||
|
// Should we be returning to look ahead?
|
||
|
bool returning = HeadLookDuration < 0;
|
||
|
|
||
|
//Matrix3D head(1);
|
||
|
|
||
|
Vector3 desired_head_rotation( 0,0,0 );
|
||
|
if ( !returning ) {
|
||
|
|
||
|
if ( HeadLookAngle.Length() > 0.001f ) {
|
||
|
HeadLookAngleTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( HeadLookAngleTimer < 0 ) {
|
||
|
HeadLookAngle = Vector3(
|
||
|
FreeRandom.Get_Float( -HEAD_TURN_LIMIT, HEAD_TURN_LIMIT ),
|
||
|
FreeRandom.Get_Float( -HEAD_TILT_LIMIT, HEAD_TILT_LIMIT ) ,0);
|
||
|
// Debug_Say(( "New Look Turn %f Tilt %f\n", RAD_TO_DEG(HeadLookAngle.X), RAD_TO_DEG(HeadLookAngle.Y) ));
|
||
|
HeadLookAngleTimer = FreeRandom.Get_Float( 2, 5 );
|
||
|
}
|
||
|
desired_head_rotation = HeadLookAngle;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Release the captured bone, this will have the effect of causing the Get_Bone_Transform ()
|
||
|
// methods to return the un-modified (due to being controlled) transform of the bone.
|
||
|
//
|
||
|
/*if ( Peek_Model()->Is_Bone_Captured( head_bone ) ) {
|
||
|
Peek_Model()->Release_Bone( head_bone );
|
||
|
}*/
|
||
|
|
||
|
///
|
||
|
// Get the transform that has been used to modify the head bone...
|
||
|
//
|
||
|
const HTreeClass *htree = Peek_Model()->Get_HTree();
|
||
|
WWASSERT( htree != NULL );
|
||
|
|
||
|
Matrix3D bone_control_tm( 1 );
|
||
|
htree->Get_Bone_Control( head_bone, bone_control_tm );
|
||
|
|
||
|
//
|
||
|
// Get the inverse of the head-bone transform
|
||
|
//
|
||
|
Matrix3D inv_bone_control_tm;
|
||
|
bone_control_tm.Get_Orthogonal_Inverse (inv_bone_control_tm);
|
||
|
|
||
|
//
|
||
|
// Get the head to world and neck to world transforms
|
||
|
//
|
||
|
Matrix3D cur_head = Peek_Model()->Get_Bone_Transform( head_bone );
|
||
|
Matrix3D cur_neck = Peek_Model()->Get_Bone_Transform( neck_bone );
|
||
|
|
||
|
//
|
||
|
// Strip off the control transform from last frame
|
||
|
//
|
||
|
cur_head = cur_head * inv_bone_control_tm;
|
||
|
|
||
|
//
|
||
|
// Get the world to neck transform
|
||
|
//
|
||
|
Matrix3D world_to_neck_tm;
|
||
|
cur_neck.Get_Orthogonal_Inverse (world_to_neck_tm);
|
||
|
|
||
|
//
|
||
|
// Build a head to neck transform
|
||
|
//
|
||
|
Matrix3D head_to_neck_tm = world_to_neck_tm * cur_head;
|
||
|
|
||
|
//
|
||
|
// Get the target relative to the head
|
||
|
//
|
||
|
Vector3 relative_head_target;
|
||
|
Matrix3D::Inverse_Transform_Vector( cur_head, HeadLookTarget, &relative_head_target );
|
||
|
|
||
|
//
|
||
|
// Determine the 'twist' and lookup/down angles.
|
||
|
// Note: Currently in the head bone coordinate system, the X axis is the same
|
||
|
// as the Z axis in object space, the Y axis is the same as the X axis in object space,
|
||
|
// and the Z axis is the same as the Y axis in object space.
|
||
|
//
|
||
|
desired_head_rotation.X = WWMath::Atan2( relative_head_target.Z, relative_head_target.Y );
|
||
|
desired_head_rotation.Z = -WWMath::Fast_Asin( relative_head_target.X / relative_head_target.Length() );
|
||
|
desired_head_rotation.Y = 0;
|
||
|
|
||
|
//
|
||
|
// Determine how far to allow the character to turn and tilt his/her head.
|
||
|
// These boundaries are based on the "absolute" amount the person can turn
|
||
|
// their head, this has to take into consideration the amount that the current
|
||
|
// animation is turning the head and the amount we need to turn to look at the target.
|
||
|
//
|
||
|
Vector3 temp_vec = head_to_neck_tm.Get_Y_Vector ();
|
||
|
float curr_rot_x = ::atan2 (temp_vec.Z, temp_vec.Y);
|
||
|
float curr_rot_z = ::atan2 (temp_vec.X, temp_vec.Y);
|
||
|
|
||
|
float min_twist = ((-HEAD_TURN_LIMIT) - curr_rot_x);
|
||
|
float max_twist = (HEAD_TURN_LIMIT - curr_rot_x);
|
||
|
|
||
|
float min_tilt = ((-HEAD_TILT_LIMIT) - curr_rot_z);
|
||
|
float max_tilt = (HEAD_TILT_LIMIT - curr_rot_z);
|
||
|
|
||
|
//
|
||
|
// Clamp the rotations
|
||
|
//
|
||
|
desired_head_rotation.X = WWMath::Clamp( desired_head_rotation.X, min_twist, max_twist);
|
||
|
desired_head_rotation.Z = WWMath::Clamp( desired_head_rotation.Z, min_tilt, max_tilt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define HEAD_TURN_RATE (DEG_TO_RAD( 360 )/2)
|
||
|
#define HEAD_TILT_RATE (DEG_TO_RAD( 180 )/2)
|
||
|
float max_turn = HEAD_TURN_RATE * TimeManager::Get_Frame_Seconds();
|
||
|
float max_tilt = HEAD_TILT_RATE * TimeManager::Get_Frame_Seconds();
|
||
|
HeadRotation.X += WWMath::Clamp( (desired_head_rotation.X - HeadRotation.X), -max_turn, max_turn );
|
||
|
HeadRotation.Z += WWMath::Clamp( (desired_head_rotation.Z - HeadRotation.Z), -max_tilt, max_tilt );
|
||
|
|
||
|
Matrix3D head(1);
|
||
|
head.Rotate_X( HeadRotation.X );
|
||
|
head.Rotate_Z( HeadRotation.Z );
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( head_bone ) ) {
|
||
|
Peek_Model()->Capture_Bone( head_bone );
|
||
|
}
|
||
|
WWASSERT( Peek_Model()->Is_Bone_Captured( head_bone ) );
|
||
|
if ( Peek_Model()->Is_Bone_Captured( head_bone ) ) {
|
||
|
Peek_Model()->Control_Bone( head_bone, head );
|
||
|
}
|
||
|
|
||
|
HeadRotation.Z = 0;
|
||
|
if ( returning && HeadRotation.Length() > 0.001f ) {
|
||
|
HeadLookDuration = 0.0001f; // maybe done next time...
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Blended_Animation( const char *animation_name, bool looping, float frame_offset, bool play_backwards )
|
||
|
{
|
||
|
if ( animation_name == NULL ) {
|
||
|
HumanState.Stop_Scripted_Animation();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Humans ignore the start_frame parameter for now
|
||
|
|
||
|
AnimationName = animation_name;
|
||
|
HumanState.Start_Scripted_Animation( animation_name, true, looping );
|
||
|
Get_Anim_Control()->Set_Mode( looping ? ANIM_MODE_LOOP : ANIM_MODE_ONCE, frame_offset );
|
||
|
|
||
|
if ( play_backwards ) {
|
||
|
HAnimClass *anim = Get_Anim_Control()->Peek_Animation();
|
||
|
if ( anim != NULL ) {
|
||
|
int frame_count = anim->Get_Num_Frames();
|
||
|
Get_Anim_Control()->Set_Mode( ANIM_MODE_TARGET, frame_count - 1 );
|
||
|
Get_Anim_Control()->Set_Target_Frame( 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Animation( const char *animation_name, bool looping, float start_frame )
|
||
|
{
|
||
|
if ( animation_name == NULL ) {
|
||
|
// Debug_Say(( "Stoping Scripted Human Animation\n" ));
|
||
|
HumanState.Stop_Scripted_Animation();
|
||
|
// FIX AnimControl->UnLock_Animation();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Humans ignore the start_frame parameter for now
|
||
|
|
||
|
// Debug_Say(( "Starting Scripted Human Animation %s\n", animation_name ));
|
||
|
AnimationName = animation_name;
|
||
|
HumanState.Start_Scripted_Animation( animation_name, false, looping );
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Start_Transition_Animation( const char * anim_name, TransitionCompletionDataStruct *data )
|
||
|
{
|
||
|
// Debug_Say(( "Starting Human Transition Animation %s\n", anim_name ));
|
||
|
//WWASSERT( TransitionCompletionData == NULL );
|
||
|
TransitionCompletionData = data;
|
||
|
AnimationName = anim_name;
|
||
|
HumanState.Start_Transition_Animation( anim_name, false );
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Weapon_Model( const char *model_name )
|
||
|
{
|
||
|
if ( WeaponRenderModel != NULL ) { // remove old gun model
|
||
|
if ( Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(WeaponRenderModel); // Clean the bone
|
||
|
}
|
||
|
WeaponRenderModel->Release_Ref();
|
||
|
WeaponRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( (model_name != NULL) && (*model_name != 0) ) {
|
||
|
//Debug_Say(( "Updating soldier Weapon model to %s!!!\n", model_name ));
|
||
|
|
||
|
StringClass stripped_name(true);
|
||
|
Get_Render_Obj_Name_From_Filename( stripped_name, model_name );
|
||
|
WeaponRenderModel = SoldierGameObj::Find_RenderObj( stripped_name );
|
||
|
if ( WeaponRenderModel == NULL ) {
|
||
|
WeaponRenderModel = Create_Render_Obj_From_Filename( model_name );
|
||
|
WWASSERT( WeaponRenderModel );
|
||
|
SET_REF_OWNER( WeaponRenderModel );
|
||
|
Add_RenderObj( WeaponRenderModel );
|
||
|
WeaponRenderModel->Release_Ref();
|
||
|
}
|
||
|
WeaponRenderModel->Add_Ref();
|
||
|
|
||
|
// Create and add new gun model
|
||
|
|
||
|
if ( Peek_Model() != NULL ) {
|
||
|
if ( WeaponRenderModel->Get_Container() != Peek_Model() ) {
|
||
|
Peek_Model()->Add_Sub_Object_To_Bone( WeaponRenderModel, GUN_BONE_NAME );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assert( Get_Weapon() );
|
||
|
Get_Weapon()->Set_Model( WeaponRenderModel ); // let the weapon know who's firing him
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Weapon_Animation( const char *anim_name )
|
||
|
{
|
||
|
if ( WeaponAnimControl == NULL ) {
|
||
|
WeaponAnimControl = new SimpleAnimControlClass;
|
||
|
if (WeaponRenderModel != NULL ) {
|
||
|
// Update Anim Control
|
||
|
WeaponAnimControl->Set_Model( WeaponRenderModel );
|
||
|
}
|
||
|
}
|
||
|
if ( WeaponAnimControl != NULL ) {
|
||
|
// Debug_Say(( "Updating soldier gun anim to \"%s\"!!!\n", anim_name ));
|
||
|
if ( ( anim_name != NULL ) && ( *anim_name != 0 ) ) {
|
||
|
WeaponAnimControl->Set_Animation( anim_name );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Set_Back_Weapon_Model( const char *model_name )
|
||
|
{
|
||
|
if ( BackWeaponRenderModel != NULL ) { // remove old gun model
|
||
|
if (Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
|
||
|
}
|
||
|
BackWeaponRenderModel->Release_Ref();
|
||
|
BackWeaponRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( (model_name != NULL) && (*model_name != 0) ) {
|
||
|
|
||
|
// Create and add back gun model
|
||
|
StringClass stripped_name(true);
|
||
|
Get_Render_Obj_Name_From_Filename( stripped_name, model_name );
|
||
|
BackWeaponRenderModel = SoldierGameObj::Find_RenderObj( stripped_name );
|
||
|
if ( BackWeaponRenderModel == NULL ) {
|
||
|
BackWeaponRenderModel = Create_Render_Obj_From_Filename( model_name );
|
||
|
// WWASSERT( BackWeaponRenderModel );
|
||
|
if ( BackWeaponRenderModel != NULL ) {
|
||
|
SET_REF_OWNER( BackWeaponRenderModel );
|
||
|
Add_RenderObj( BackWeaponRenderModel );
|
||
|
BackWeaponRenderModel->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( BackWeaponRenderModel != NULL ) {
|
||
|
BackWeaponRenderModel->Add_Ref();
|
||
|
|
||
|
if (Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Add_Sub_Object_To_Bone( BackWeaponRenderModel, BACK_GUN_BONE_NAME );
|
||
|
}
|
||
|
} else {
|
||
|
Debug_Say(( "Missing Back Model %s", model_name ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Set_Back_Flag_Model(const char *model_name,
|
||
|
const Vector3 & tint)
|
||
|
{
|
||
|
if ( BackFlagRenderModel != NULL ) { // remove old gun model
|
||
|
if (Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
|
||
|
}
|
||
|
BackFlagRenderModel->Release_Ref();
|
||
|
BackFlagRenderModel = NULL;
|
||
|
}
|
||
|
|
||
|
if ( (model_name != NULL) && (*model_name != 0) ) {
|
||
|
|
||
|
// Create and add back gun model
|
||
|
BackFlagRenderModel = Create_Render_Obj_From_Filename( model_name );
|
||
|
if ( BackFlagRenderModel != NULL ) {
|
||
|
SET_REF_OWNER( BackFlagRenderModel );
|
||
|
|
||
|
/*
|
||
|
extern void Tint(RenderObjClass *robj, const Vector3 & color);
|
||
|
Tint(BackFlagRenderModel, tint);
|
||
|
*/
|
||
|
|
||
|
if (Peek_Model() != NULL ) {
|
||
|
Peek_Model()->Add_Sub_Object_To_Bone( BackFlagRenderModel, BACK_GUN_BONE_NAME );
|
||
|
}
|
||
|
} else {
|
||
|
Debug_Say(( "Missing Back Model %s", model_name ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Update_Back_Gun( void )
|
||
|
{
|
||
|
//Debug_Say(("CommandoGameObj::Update_Back_Gun\n"));
|
||
|
|
||
|
WeaponClass *next_weapon = WeaponBag->Get_Next_Weapon();
|
||
|
if ( next_weapon && ( next_weapon != Get_Weapon() ) ) {
|
||
|
Set_Back_Weapon_Model( next_weapon->Get_Back_Model_Name() );
|
||
|
} else {
|
||
|
Set_Back_Weapon_Model( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
float SoldierGameObj::Get_Weapon_Height( void )
|
||
|
{
|
||
|
float height = 1.62f; // set to be at about eye level
|
||
|
if ( Is_Crouched() ) {
|
||
|
height -= 0.56f;
|
||
|
}
|
||
|
return height; // verticle offset, move to weapon
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
float SoldierGameObj::Get_Weapon_Length( void )
|
||
|
{
|
||
|
return 0.8f; // forward offset, move to weapon
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Internal_Set_Targeting( const Vector3 & target_pos, bool do_tilt )
|
||
|
{
|
||
|
WWPROFILE( "Soldier Set Targeting" );
|
||
|
|
||
|
if ( CombatManager::Is_Skeleton_Slider_Demo_Enabled() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SmartGameObj::Set_Targeting( target_pos );
|
||
|
|
||
|
if ( ( Get_State() == HumanStateClass::DEATH ) ||
|
||
|
( Get_State() == HumanStateClass::DESTROY ) ||
|
||
|
( Get_State() == HumanStateClass::TRANSITION ) ||
|
||
|
( Get_State() == HumanStateClass::LADDER ) ) {
|
||
|
// Debug_Say(( "Ignore Set Target in Transition \n" ));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( Get_State() == HumanStateClass::IN_VEHICLE ) {
|
||
|
if ( Vehicle != NULL ) {
|
||
|
if ( Vehicle->Get_Driver_Is_Gunner() ) {
|
||
|
if ( Vehicle->Get_Driver() == this ) {
|
||
|
Vehicle->Set_Targeting( target_pos );
|
||
|
}
|
||
|
} else {
|
||
|
if ( Vehicle->Get_Driver() == this && Vehicle->Get_Gunner() == NULL ) {
|
||
|
Vehicle->Set_Targeting( target_pos );
|
||
|
} else if ( Vehicle->Get_Gunner() == this ) {
|
||
|
Vehicle->Set_Targeting( target_pos );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Vector3 muzzle_pos;
|
||
|
Get_Position( &muzzle_pos );
|
||
|
muzzle_pos.Z += Get_Weapon_Height();
|
||
|
Vector3 rel_target_pos = target_pos - muzzle_pos;
|
||
|
|
||
|
// Set Tilt
|
||
|
float dist = rel_target_pos.Length();
|
||
|
float tilt = 0;
|
||
|
if ( dist && do_tilt ) {
|
||
|
tilt = WWMath::Fast_Asin( rel_target_pos.Z / dist );
|
||
|
}
|
||
|
|
||
|
// tilt = 0;
|
||
|
// Debug_Say(( "Tilt %f", tilt ));
|
||
|
|
||
|
bool is_complete = true;
|
||
|
|
||
|
// Set Facing
|
||
|
float cur_facing = Peek_Human_Phys()->Get_Heading();
|
||
|
float facing = WWMath::Atan2( rel_target_pos.Y, rel_target_pos.X );
|
||
|
float facing_dif = facing - cur_facing;
|
||
|
if ( WWMath::Fabs(facing_dif) > 0.001f ) {
|
||
|
WWPROFILE( "Facing" );
|
||
|
facing_dif = WWMath::Wrap( facing_dif, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
|
||
|
// Debug_Say(( "Must change %1.3f, from facing %1.3f to facing %1.3f\n", RAD_TO_DEG( facing_dif ), RAD_TO_DEG( cur_facing ), RAD_TO_DEG( facing ) ));
|
||
|
|
||
|
float change = facing_dif;
|
||
|
if ( !Is_Human_Controlled() ) { // human players don't use turn limits
|
||
|
float max_change = Get_Turn_Rate() * TimeManager::Get_Frame_Seconds();
|
||
|
|
||
|
// Turn more slowly when turning a small amount
|
||
|
if ( WWMath::Fabs( change ) < DEG_TO_RAD( 20 ) ) {
|
||
|
max_change *= 0.3f;
|
||
|
}
|
||
|
|
||
|
change = WWMath::Clamp( facing_dif, -max_change, max_change );
|
||
|
}
|
||
|
|
||
|
if ( !Is_Human_Controlled() ) { // human players don't use turn anims
|
||
|
HumanState.Set_Turn_Velocity( change ); // play the leg turning anim
|
||
|
}
|
||
|
|
||
|
is_complete = (change == facing_dif); // are we facing?
|
||
|
|
||
|
facing = cur_facing + change;
|
||
|
facing = WWMath::Wrap( facing, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
|
||
|
Peek_Human_Phys()->Set_Heading( facing );
|
||
|
}
|
||
|
|
||
|
if ( Is_Human_Controlled() && Get_State() != HumanStateClass::IN_VEHICLE ) {
|
||
|
|
||
|
#define TILT_DOWN_SPEED 4.0
|
||
|
float direction = -1;
|
||
|
if ( Get_Weapon() && Get_Weapon()->Is_Reloading() ) {
|
||
|
direction = 1;
|
||
|
}
|
||
|
|
||
|
ReloadingTilt += direction * TimeManager::Get_Frame_Seconds() * TILT_DOWN_SPEED;
|
||
|
ReloadingTilt = WWMath::Clamp( ReloadingTilt, 0, 1 );
|
||
|
|
||
|
if ( ReloadingTilt > 0 ) {
|
||
|
tilt = WWMath::Lerp( (float)tilt, DEG_TO_RADF( -90 ), ReloadingTilt );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HumanState.Update_Aiming( tilt, 0 ); // no turn
|
||
|
|
||
|
// facing = HumanState.Peek_Physical_Object()->Get_Heading();
|
||
|
// Debug_Say(( "Soldier Facing %f Tilt %f\n", RAD_TO_DEG( facing ), RAD_TO_DEG( WeaponTilt ) ));
|
||
|
// Debug_Say(( "rel_target_pos %f %f %f\n", rel_target_pos.X, rel_target_pos.Y, rel_target_pos.Z ));
|
||
|
|
||
|
return is_complete;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Set_Targeting( const Vector3 & target_pos, bool do_tilt )
|
||
|
{
|
||
|
bool retval = false;
|
||
|
|
||
|
//
|
||
|
// Don't do the targetting if we are locked on an object
|
||
|
//
|
||
|
if ( FacingObject == NULL ) {
|
||
|
retval = Internal_Set_Targeting( target_pos, do_tilt );
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
RenderObjClass * SoldierGameObj::Find_Head_Model( void )
|
||
|
{
|
||
|
RenderObjClass *head_model = NULL;
|
||
|
|
||
|
//
|
||
|
// Get a pointer to the HLod this object is using
|
||
|
//
|
||
|
RenderObjClass *model = Peek_Model ();
|
||
|
if ( model != NULL && model->Class_ID () == RenderObjClass::CLASSID_HLOD ) {
|
||
|
HLodClass *lod = reinterpret_cast<HLodClass *> ( model );
|
||
|
|
||
|
//
|
||
|
// Try to find the head-bone
|
||
|
//
|
||
|
int bone_index = lod->Get_Bone_Index ( "C HEAD" );
|
||
|
int obj_count = lod->Get_Num_Sub_Objects_On_Bone(bone_index);
|
||
|
|
||
|
//
|
||
|
// Try to find a model on the head bone that uses the human head skeleton
|
||
|
//
|
||
|
for (int index = 0; index < obj_count; index ++) {
|
||
|
RenderObjClass *render_obj = lod->Get_Sub_Object_On_Bone ( index, bone_index );
|
||
|
if (render_obj != NULL) {
|
||
|
const HTreeClass *htree = render_obj->Get_HTree();
|
||
|
if ( htree != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Is this really a head model?
|
||
|
//
|
||
|
if ( ::stricmp (htree->Get_Name(), "S_A_HEAD") == 0 ||
|
||
|
::stricmp (htree->Get_Name(), "S_B_HEAD") == 0)
|
||
|
{
|
||
|
head_model = render_obj;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
render_obj->Release_Ref ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return head_model;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Emot_Icon ( const char *model_name, float duration )
|
||
|
{
|
||
|
//
|
||
|
// Free the old emot icon
|
||
|
//
|
||
|
if ( EmotIconModel != NULL ) {
|
||
|
|
||
|
if ( EmotIconModel->Is_In_Scene() ) {
|
||
|
EmotIconModel->Remove ();
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE( EmotIconModel );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Try to load the new emot icon model
|
||
|
//
|
||
|
if ( model_name != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Create the new model
|
||
|
//
|
||
|
EmotIconModel = ::Create_Render_Obj_From_Filename( model_name );
|
||
|
if ( EmotIconModel != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Determine what the animation name for the model is
|
||
|
//
|
||
|
const char *name = EmotIconModel->Get_Name ();
|
||
|
StringClass anim_name;
|
||
|
anim_name.Format ("%s.%s", name, name);
|
||
|
|
||
|
//
|
||
|
// Animate the model
|
||
|
//
|
||
|
HAnimClass *anim = WW3DAssetManager::Get_Instance ()->Get_HAnim( anim_name );
|
||
|
if ( anim != NULL ) {
|
||
|
EmotIconModel->Set_Animation( anim, 0.0F, RenderObjClass::ANIM_MODE_LOOP );
|
||
|
REF_PTR_RELEASE( anim );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Put the icon a few meters above the soldier's head
|
||
|
//
|
||
|
Matrix3D tm = Get_Transform();
|
||
|
tm.Set_Translation (tm.Get_Translation() + Vector3 (0, 0, EMOT_ICON_HEIGHT));
|
||
|
EmotIconModel->Set_Transform( tm );
|
||
|
COMBAT_SCENE->Add_Render_Object( EmotIconModel );
|
||
|
EmotIconTimer = duration;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Say_Dialogue( int dialog_id )
|
||
|
{
|
||
|
WWASSERT( dialog_id >= 0 && dialog_id < DIALOG_MAX );
|
||
|
|
||
|
//
|
||
|
// Lookup the conversation we are starting
|
||
|
//
|
||
|
int conversation_id = DialogList[dialog_id].Get_Conversation();
|
||
|
if ( conversation_id > 0 ) {
|
||
|
|
||
|
//
|
||
|
// Start the conversation
|
||
|
//
|
||
|
ActiveConversationClass *conversation = ConversationMgrClass::Start_Conversation( this, conversation_id, true );
|
||
|
REF_PTR_RELEASE (conversation);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
float SoldierGameObj::Say_Dynamic_Dialogue
|
||
|
(
|
||
|
int text_id,
|
||
|
SoldierGameObj * speaker,
|
||
|
AudibleSoundClass ** sound_obj_to_return
|
||
|
)
|
||
|
{
|
||
|
float duration = 2.0F;
|
||
|
|
||
|
//
|
||
|
// Lookup the translation object from the strings database
|
||
|
//
|
||
|
TDBObjClass *translate_obj = TranslateDBClass::Find_Object( text_id );
|
||
|
if (translate_obj != NULL) {
|
||
|
|
||
|
const WCHAR *string = translate_obj->Get_String ();
|
||
|
int sound_def_id = (int)translate_obj->Get_Sound_ID ();
|
||
|
const char *anim_name = translate_obj->Get_Animation_Name ();
|
||
|
|
||
|
//
|
||
|
// If this soldier was still talking from a previos call, then
|
||
|
// kill the old sound
|
||
|
//
|
||
|
if ( speaker != NULL ) {
|
||
|
speaker->Stop_Current_Speech();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Play the sound effect
|
||
|
//
|
||
|
bool display_text = true;
|
||
|
if ( sound_def_id > 0 ) {
|
||
|
|
||
|
//
|
||
|
// Create the sound object
|
||
|
//
|
||
|
AudibleSoundClass *speech = WWAudioClass::Get_Instance ()->Create_Sound( sound_def_id );
|
||
|
if ( speech != NULL ) {
|
||
|
duration = (speech->Get_Duration() / 1000.0F);
|
||
|
|
||
|
//
|
||
|
// Return the sound object to the caller as necessary
|
||
|
//
|
||
|
if ( sound_obj_to_return != NULL ) {
|
||
|
(*sound_obj_to_return) = speech;
|
||
|
(*sound_obj_to_return)->Add_Ref();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Either play the sound at the speaker's location
|
||
|
// or just play it as a 2D sample.
|
||
|
//
|
||
|
if ( speaker != NULL ) {
|
||
|
speech->Set_Transform( speaker->Get_Transform() );
|
||
|
speech->Add_To_Scene();
|
||
|
display_text = (speech->Is_Sound_Culled() == false);
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Play the sound
|
||
|
//
|
||
|
speech->Play();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the speaker's internal sound object pointer
|
||
|
//
|
||
|
if ( speaker != NULL ) {
|
||
|
speaker->CurrentSpeech = speech;
|
||
|
} else {
|
||
|
REF_PTR_RELEASE (speech);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if this string is commented out.
|
||
|
//
|
||
|
if( string != NULL && string[0] == L'/' && string[1] == L'/' ) {
|
||
|
string += 2;
|
||
|
display_text = false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Display the text on the screen
|
||
|
//
|
||
|
if ( display_text && string != NULL && string[0] != 0 ) {
|
||
|
float message_duration = max( duration, 5.0F );
|
||
|
CombatManager::Get_Message_Window ()->Add_Message( string, Vector3 (1, 1, 1),
|
||
|
speaker, message_duration );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Play the facial animation (if one exists)
|
||
|
//
|
||
|
if ( speaker != NULL && speaker->HeadModel != NULL) {
|
||
|
HAnimClass *head_anim = NULL;
|
||
|
|
||
|
//
|
||
|
// Dig out the skeleton name for the head model
|
||
|
//
|
||
|
StringClass anim_root_name;
|
||
|
const HTreeClass *htree = speaker->HeadModel->Get_HTree ();
|
||
|
if (htree != NULL) {
|
||
|
anim_root_name = htree->Get_Name ();
|
||
|
anim_root_name += ".";
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine if we should use a pre-canned animation, or
|
||
|
// if we should build one dynamically
|
||
|
//
|
||
|
if ( anim_name == NULL || anim_name[0] == 0 ) {
|
||
|
|
||
|
//
|
||
|
// Build an animation dynamically
|
||
|
//
|
||
|
const char *english_string = translate_obj->Get_English_String();
|
||
|
if ( english_string != NULL && english_string[0] != 0 ) {
|
||
|
if (speaker->SpeechAnim->Generate_Animation( english_string, duration )) {
|
||
|
head_anim = speaker->SpeechAnim;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Build a string containing the fully qualified animation name
|
||
|
//
|
||
|
StringClass full_anim_name;
|
||
|
if ( ::strchr( anim_name, '.' ) == 0 ) {
|
||
|
full_anim_name = anim_root_name;
|
||
|
full_anim_name += anim_name;
|
||
|
} else {
|
||
|
full_anim_name = anim_name;
|
||
|
}
|
||
|
|
||
|
head_anim = WW3DAssetManager::Get_Instance()->Get_HAnim( full_anim_name );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Recreate the facial animation when the speech is complete
|
||
|
//
|
||
|
speaker->GenerateIdleFacialAnimTimer = duration + 1.0F;
|
||
|
|
||
|
//
|
||
|
// Play the animation on the head model (if one exists)
|
||
|
//
|
||
|
if ( speaker->HeadModel != NULL && head_anim != NULL ) {
|
||
|
speaker->HeadModel->Set_Animation( head_anim, 0, RenderObjClass::ANIM_MODE_ONCE );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return duration;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Stop_Current_Speech( void )
|
||
|
{
|
||
|
//
|
||
|
// Kill the current sound we are making (speech, scream, grunt, etc)
|
||
|
//
|
||
|
if ( CurrentSpeech != NULL ) {
|
||
|
GenerateIdleFacialAnimTimer = 0;
|
||
|
CurrentSpeech->Stop();
|
||
|
CurrentSpeech->Remove_From_Scene();
|
||
|
CurrentSpeech->Release_Ref();
|
||
|
CurrentSpeech = NULL;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin )
|
||
|
{
|
||
|
if ( !Is_In_Vehicle() ) {
|
||
|
SmartGameObj::Apply_Damage( damager, scale, alternate_skin );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Apply_Damage_Extended( const OffenseObjectClass & damager, float scale,
|
||
|
const Vector3 & direction, const char * collision_box_name )
|
||
|
{
|
||
|
// If negative damage, just apply
|
||
|
if ( Get_Defense_Object()->Is_Repair( damager ) ) {
|
||
|
// if ( damager.Get_Damage() * scale < 0 ) {
|
||
|
SmartGameObj::Apply_Damage_Extended( damager, scale, direction, collision_box_name );
|
||
|
|
||
|
// Add electric effect
|
||
|
if ( HealingEffect == NULL ) {
|
||
|
HealingEffect = CombatMaterialEffectManager::Get_Health_Effect();
|
||
|
if ( HealingEffect != NULL ) {
|
||
|
Peek_Human_Phys()->Add_Effect_To_Me( HealingEffect );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( HealingEffect != NULL ) {
|
||
|
HealingEffect->Set_Target_Parameter(0.495f); // go almost halfway
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do this after the repair effect
|
||
|
/* if ( !CombatManager::I_Am_Server() ) { Clients can apply damage
|
||
|
return;
|
||
|
}*/
|
||
|
|
||
|
if ( Is_In_Vehicle() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool ambushed = false;
|
||
|
// Ambush Kill
|
||
|
if ( ( Get_AI_State() == AI_STATE_IDLE ) || ( Get_AI_State() == AI_STATE_SECONDARY_IDLE ) ) {
|
||
|
// Only ambush if there is an owner to the damage, and the target is human controlled
|
||
|
if ( damager.Get_Owner() != NULL && !Is_Human_Controlled() ) {
|
||
|
scale *= AMBUSH_DAMAGE_SCALE;
|
||
|
ambushed = true;
|
||
|
// Debug_Say(( "Ambush Damage!!\n" ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool anim_ok = true;
|
||
|
|
||
|
Reset_Hibernating(); // Wake up when damaged
|
||
|
|
||
|
int ouch_type = Get_Ouch_Type( direction, collision_box_name );
|
||
|
|
||
|
// Stats
|
||
|
PlayerDataClass * damager_data = NULL;
|
||
|
if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() ) {
|
||
|
damager_data = damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data();
|
||
|
}
|
||
|
|
||
|
// Dont add stats for "non-directional" damage. E.g. standing in a tiberium
|
||
|
// field should not count as thousands of hits.
|
||
|
if (direction.Length2() > WWMATH_EPSILON) {
|
||
|
switch( ouch_type ) {
|
||
|
case HumanStateClass::HEAD_FROM_BEHIND:
|
||
|
case HumanStateClass::HEAD_FROM_FRONT:
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Head_Hit();
|
||
|
}
|
||
|
if ( damager_data != NULL ) {
|
||
|
damager_data->Stats_Add_Head_Shot();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HumanStateClass::TORSO_FROM_BEHIND:
|
||
|
case HumanStateClass::TORSO_FROM_FRONT:
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Torso_Hit();
|
||
|
}
|
||
|
if ( damager_data != NULL ) {
|
||
|
damager_data->Stats_Add_Torso_Shot();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HumanStateClass::LEFT_ARM_FROM_BEHIND:
|
||
|
case HumanStateClass::LEFT_ARM_FROM_FRONT:
|
||
|
case HumanStateClass::RIGHT_ARM_FROM_BEHIND:
|
||
|
case HumanStateClass::RIGHT_ARM_FROM_FRONT:
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Arm_Hit();
|
||
|
}
|
||
|
if ( damager_data != NULL ) {
|
||
|
damager_data->Stats_Add_Arm_Shot();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HumanStateClass::LEFT_LEG_FROM_BEHIND:
|
||
|
case HumanStateClass::LEFT_LEG_FROM_FRONT:
|
||
|
case HumanStateClass::RIGHT_LEG_FROM_BEHIND:
|
||
|
case HumanStateClass::RIGHT_LEG_FROM_FRONT:
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Leg_Hit();
|
||
|
}
|
||
|
if ( damager_data != NULL ) {
|
||
|
damager_data->Stats_Add_Leg_Shot();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HumanStateClass::GROIN:
|
||
|
if ( Get_Player_Data() != NULL ) {
|
||
|
Get_Player_Data()->Stats_Add_Crotch_Hit();
|
||
|
}
|
||
|
if ( damager_data != NULL ) {
|
||
|
damager_data->Stats_Add_Crotch_Shot();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Determine what (if any) dialogue to say
|
||
|
int dialogue_id = -1;
|
||
|
ArmedGameObj *damage_owner = damager.Get_Owner();
|
||
|
if( damage_owner != NULL ) {
|
||
|
|
||
|
// Were we injured by a friend or foe?
|
||
|
if ( Is_Enemy( damage_owner ) ) {
|
||
|
dialogue_id = DIALOG_ON_TAKE_DAMAGE_FROM_ENEMY;
|
||
|
} else {
|
||
|
dialogue_id = DIALOG_ON_TAKE_DAMAGE_FROM_FRIEND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float health_before = Get_Defense_Object()->Get_Health();
|
||
|
float armor_before = Get_Defense_Object()->Get_Shield_Strength();
|
||
|
|
||
|
if ( collision_box_name != NULL ) {
|
||
|
char * start = ::strchr( collision_box_name, '.' );
|
||
|
if ( start != NULL ) {
|
||
|
start++;
|
||
|
float bone_scale = BonesManager::Get_Bone_Damage_Scale( start );
|
||
|
scale *= bone_scale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update damage indicators
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
if ( Get_Defense_Object()->Would_Damage( damager ) && !damager.ForceServerDamage ) {
|
||
|
if ( direction.Length() == 0 ) {
|
||
|
for ( int i = 0; i < 8; i++ ) {
|
||
|
CombatManager::Show_Star_Damage_Direction( i );
|
||
|
}
|
||
|
// Set all
|
||
|
} else {
|
||
|
Vector3 relative_direction = Get_Transform().Inverse_Rotate_Vector( direction );
|
||
|
// Convert direction into 0 .. 7
|
||
|
float angle = ::WWMath::Atan2 ( relative_direction.Y, -relative_direction.X);
|
||
|
int dir = (int)(8.0f * angle / DEG_TO_RAD( 360 ) + 8.5f);
|
||
|
CombatManager::Show_Star_Damage_Direction( dir & 7 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
SmartGameObj::Apply_Damage_Extended( damager, scale, direction, collision_box_name );
|
||
|
float health = Get_Defense_Object()->Get_Health();
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
COMBAT_STAR->Get_Position( &pos );
|
||
|
int hitter_id = 0;
|
||
|
const char * weapon_name = "";
|
||
|
if ( damager.Get_Owner() ) {
|
||
|
hitter_id = damager.Get_Owner()->Get_ID();
|
||
|
if ( damager.Get_Owner()->Get_Weapon() ) {
|
||
|
weapon_name = damager.Get_Owner()->Get_Weapon()->Get_Definition()->Get_Name();
|
||
|
}
|
||
|
}
|
||
|
const char * body_part = ( collision_box_name != NULL ) ? collision_box_name : "";
|
||
|
float armor = Get_Defense_Object()->Get_Shield_Strength();
|
||
|
DIAG_LOG(( "DRCE", "%s; %d; %s; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", weapon_name, hitter_id, body_part, armor, health, pos.X, pos.Y, pos.Z ));
|
||
|
}
|
||
|
|
||
|
if (( damager.Get_Owner() == COMBAT_STAR ) && ( COMBAT_STAR != NULL )) {
|
||
|
Vector3 pos;
|
||
|
COMBAT_STAR->Get_Position( &pos );
|
||
|
const char * weapon_name = "";
|
||
|
int ammo = 0;
|
||
|
if ( COMBAT_STAR->Get_Weapon() ) {
|
||
|
weapon_name = COMBAT_STAR->Get_Weapon()->Get_Definition()->Get_Name();
|
||
|
ammo = COMBAT_STAR->Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
int hittee_id = Get_ID();
|
||
|
const char * body_part = ( collision_box_name != NULL ) ? collision_box_name : "";
|
||
|
float armor = Get_Defense_Object()->Get_Shield_Strength();
|
||
|
Vector3 victim_pos;
|
||
|
Get_Position( &victim_pos );
|
||
|
const char * team_name = Player_Type_Name( Get_Player_Type() );
|
||
|
DIAG_LOG(( "DEFE", "%1.2f; %1.2f; %1.2f; %s; %d; %d; %s; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s ", pos.X, pos.Y, pos.Z, weapon_name, ammo, hittee_id, body_part, armor, health, victim_pos.X, victim_pos.Y, victim_pos.Z, team_name ));
|
||
|
}
|
||
|
|
||
|
if ( ambushed ) {
|
||
|
int victim_id = Get_ID();
|
||
|
Vector3 victim_pos;
|
||
|
Get_Position( &victim_pos );
|
||
|
Vector3 shooter_pos;
|
||
|
damager.Get_Owner()->Get_Position( &shooter_pos );
|
||
|
float armor = Get_Defense_Object()->Get_Shield_Strength();
|
||
|
DIAG_LOG(( "AMGE", "%1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", victim_pos.X, victim_pos.Y, victim_pos.Z, victim_id, shooter_pos.X, shooter_pos.Y, shooter_pos.Z, armor, health ));
|
||
|
}
|
||
|
|
||
|
|
||
|
if (health == health_before && Get_Defense_Object()->Get_Shield_Strength() == armor_before) {
|
||
|
//
|
||
|
// No damage was applied.
|
||
|
// This is a hack.
|
||
|
//
|
||
|
|
||
|
// Handle zero health bug
|
||
|
if ( health > 0 ||
|
||
|
HumanState.Get_State() == HumanStateClass::DEATH ||
|
||
|
HumanState.Get_State() == HumanStateClass::DESTROY ) {
|
||
|
return;
|
||
|
} else {
|
||
|
Debug_Say(( "Solving Zero health Bug %f %d\n", health, HumanState.Get_State() ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Can't start special damage unless damage was done.
|
||
|
|
||
|
// Check for special damage
|
||
|
int warhead = damager.Get_Warhead();
|
||
|
ArmorWarheadManager::SpecialDamageType special_damage = ArmorWarheadManager::Get_Special_Damage_Type( warhead );
|
||
|
if ( special_damage != 0 ) {
|
||
|
float probability = ArmorWarheadManager::Get_Special_Damage_Probability( warhead );
|
||
|
if (health <= 0) {
|
||
|
probability = 1;
|
||
|
}
|
||
|
if ( FreeRandom.Get_Float() < probability ) {
|
||
|
// if skin is not impervious to the damage
|
||
|
int skin = Get_Defense_Object()->Get_Skin();
|
||
|
ArmorWarheadManager::SpecialDamageType special_damage = ArmorWarheadManager::Get_Special_Damage_Type( warhead );
|
||
|
if ( !ArmorWarheadManager::Is_Skin_Impervious( special_damage, skin ) ) {
|
||
|
Set_Special_Damage_Mode( special_damage, damager.Get_Owner() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
if ( _shake_delay < 0 ) {
|
||
|
_shake_delay = 0.6f; // don't shake/sound too fast
|
||
|
|
||
|
// And Shake the camera
|
||
|
Vector3 pos = COMBAT_CAMERA->Get_Transform().Get_Translation();
|
||
|
COMBAT_SCENE->Add_Camera_Shake( pos, 1, 0.5f, 0.05f );
|
||
|
}
|
||
|
|
||
|
anim_ok = false;
|
||
|
if ( _cry_delay < 0 ) {
|
||
|
_cry_delay = 2; // don't shake/sound too fast
|
||
|
anim_ok = true;
|
||
|
|
||
|
// Play ouch sound
|
||
|
AudibleSoundClass * sound = WWAudioClass::Get_Instance()->Create_Sound( "Take_Damage_Sound" );
|
||
|
if ( sound ) {
|
||
|
sound->Play();
|
||
|
sound->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( health <= 0 ) {
|
||
|
|
||
|
// Check for creating visceroids
|
||
|
if ( IS_MISSION ) {
|
||
|
int warhead = damager.Get_Warhead();
|
||
|
float visceroid_probability = ArmorWarheadManager::Get_Visceroid_Probability( warhead );
|
||
|
if ( !Is_Human_Controlled() && visceroid_probability != 0 ) {
|
||
|
if ( FreeRandom.Get_Float() < visceroid_probability ) {
|
||
|
|
||
|
#define VISCEROID_NAME "Visceroid"
|
||
|
// Not from other visceroids
|
||
|
if ( ::stricmp( Get_Definition().Get_Name(), VISCEROID_NAME ) != 0 ) {
|
||
|
// Create a visceroid
|
||
|
PhysicalGameObj * vis = ObjectLibraryManager::Create_Object( VISCEROID_NAME );
|
||
|
if ( vis != NULL ) {
|
||
|
vis->Set_Transform( Get_Transform() );
|
||
|
vis->Start_Observers ();
|
||
|
Set_Delete_Pending(); // Kill me
|
||
|
Debug_Say(( "Made Visceroid\n" ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Enable_Hibernation( false );
|
||
|
|
||
|
// Stats
|
||
|
SoldierGameObj * damager_owner = NULL;
|
||
|
if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() ) {
|
||
|
damager_owner = damager.Get_Owner()->As_SoldierGameObj();
|
||
|
}
|
||
|
if ( damager_owner && damager_owner->Get_Player_Data() ) {
|
||
|
|
||
|
if ( Is_Teammate( damager_owner ) ) {
|
||
|
damager_owner->Get_Player_Data()->Stats_Add_Ally_Killed();
|
||
|
}
|
||
|
|
||
|
if ( Is_Enemy( damager_owner ) ) {
|
||
|
damager_owner->Get_Player_Data()->Stats_Add_Enemy_Killed();
|
||
|
}
|
||
|
|
||
|
if ( damager_owner->Get_Vehicle() ) {
|
||
|
damager_owner->Get_Player_Data()->Stats_Add_Kill_From_Vehicle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( HumanState.Get_State() != HumanStateClass::DEATH ) {
|
||
|
|
||
|
if ( this == COMBAT_STAR ) {
|
||
|
CombatManager::Register_Star_Killer( damager.Get_Owner() );
|
||
|
}
|
||
|
|
||
|
// Play death animation
|
||
|
HumanState.Set_State( HumanStateClass::DEATH, ouch_type );
|
||
|
|
||
|
//
|
||
|
// Stop any sound we WERE making
|
||
|
//
|
||
|
Stop_Current_Speech ();
|
||
|
|
||
|
int death_sound_id = Get_Definition ().DeathSoundPresetID;
|
||
|
if (death_sound_id == 0) {
|
||
|
|
||
|
//
|
||
|
// Lookup the global death sound
|
||
|
//
|
||
|
GlobalSettingsDef *global_settings = GlobalSettingsDef::Get_Global_Settings ();
|
||
|
if (global_settings != NULL) {
|
||
|
death_sound_id = global_settings->Get_Death_Sound_ID();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reveal this object to the player's encyclopedia
|
||
|
//
|
||
|
if ( damager.Get_Owner () == COMBAT_STAR ) {
|
||
|
EncyclopediaMgrClass::Reveal_Object( this );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Play the death sound
|
||
|
//
|
||
|
if (death_sound_id != 0) {
|
||
|
Matrix3D tm = Get_Transform();
|
||
|
RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference(this);
|
||
|
WWAudioClass::Get_Instance()->Create_Instant_Sound( death_sound_id, tm, owner_ref );
|
||
|
REF_PTR_RELEASE(owner_ref);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Play death explosion, since we don't call Completely_Damaged
|
||
|
if ( Get_Definition().KilledExplosion != 0 ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position(&pos);
|
||
|
WWASSERT(pos.Is_Valid());// most likely candidate for explosion damage bug....?
|
||
|
ExplosionManager::Create_Explosion_At( Get_Definition().KilledExplosion, pos, damager.Get_Owner() ); // no one gets credit for this
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Queue the guy who killed us...
|
||
|
//
|
||
|
if ( damage_owner != NULL &&
|
||
|
damage_owner != this &&
|
||
|
damage_owner->As_SoldierGameObj () != NULL )
|
||
|
{
|
||
|
SoldierGameObj *our_killer = damage_owner->As_SoldierGameObj ();
|
||
|
|
||
|
//
|
||
|
// Were we killed by a friend or foe?
|
||
|
//
|
||
|
if ( Is_Enemy( damage_owner ) ) {
|
||
|
our_killer->Say_Dialogue( DIALOG_ON_KILLED_ENEMY );
|
||
|
} else {
|
||
|
our_killer->Say_Dialogue( DIALOG_ON_KILLED_FRIEND );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// Play wound animation
|
||
|
// If state is interruptable.....
|
||
|
if ( Get_State() == HumanStateClass::UPRIGHT && anim_ok ) {
|
||
|
HumanState.Set_State( HumanStateClass::WOUNDED, ouch_type );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( CurrentSpeech == NULL && dialogue_id >= 0 ) {
|
||
|
Say_Dialogue( dialogue_id );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Exit_Ladder( void )
|
||
|
{
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Enter_Ladder( bool top )
|
||
|
{
|
||
|
HumanState.Set_State( HumanStateClass::LADDER );
|
||
|
|
||
|
if ( Is_Human_Controlled() ) {
|
||
|
if ( top ) {
|
||
|
LadderUpMask = true;
|
||
|
} else {
|
||
|
LadderDownMask = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
HumanPhysClass *SoldierGameObj::Peek_Human_Phys( void ) const
|
||
|
{
|
||
|
WWASSERT( Peek_Physical_Object() );
|
||
|
WWASSERT( Peek_Physical_Object()->As_HumanPhysClass() );
|
||
|
return Peek_Physical_Object()->As_HumanPhysClass();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
/*
|
||
|
** Vehicles
|
||
|
*/
|
||
|
void SoldierGameObj::Enter_Vehicle( VehicleGameObj * vehicle, const char * anim_name )
|
||
|
{
|
||
|
// WWASSERT( Get_State() != HumanStateClass::IN_VEHICLE );
|
||
|
|
||
|
Vehicle = vehicle;
|
||
|
HumanState.Set_State( HumanStateClass::IN_VEHICLE );
|
||
|
|
||
|
AnimationName = anim_name;
|
||
|
HumanState.Force_Animation( anim_name, false );
|
||
|
|
||
|
if ( this == COMBAT_STAR && vehicle != NULL ) {
|
||
|
Vector3 pos;
|
||
|
Vehicle->Get_Position( &pos );
|
||
|
int ammo = 0;
|
||
|
if ( vehicle->Get_Weapon() ) {
|
||
|
ammo = vehicle->Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
DefenseObjectClass * defense = vehicle->Get_Defense_Object();
|
||
|
DIAG_LOG(( "ENVE", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), ammo ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Exit_Vehicle( void )
|
||
|
{
|
||
|
if ( this == COMBAT_STAR && Vehicle != NULL ) {
|
||
|
Vector3 pos;
|
||
|
Vehicle->Get_Position( &pos );
|
||
|
int ammo = 0;
|
||
|
if ( Vehicle->Get_Weapon() ) {
|
||
|
ammo = Vehicle->Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
DefenseObjectClass * defense = Vehicle->Get_Defense_Object();
|
||
|
DIAG_LOG(( "EXVE", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), ammo ));
|
||
|
}
|
||
|
|
||
|
Vehicle = NULL;
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Exit_Destroyed_Vehicle( int seat_num, const Vector3 & vehicle_pos )
|
||
|
{
|
||
|
Vehicle = NULL;
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
|
||
|
Vector3 extent = Peek_Human_Phys()->Get_Collision_Box().Extent;
|
||
|
float width = MAX( extent.X, extent.Y ) * 1.1f;
|
||
|
|
||
|
if ( Peek_Model() ) {
|
||
|
Peek_Model()->Set_Hidden( false );
|
||
|
}
|
||
|
|
||
|
Vector3 offsets[4] = {
|
||
|
Vector3( -1.0f, -1.0f, 0 ),
|
||
|
Vector3( -1.0f, 1.0f, 0 ),
|
||
|
Vector3( 1.0f, -1.0f, 0 ),
|
||
|
Vector3( 1.0f, 1.0f, 0 ),
|
||
|
};
|
||
|
|
||
|
Vector3 pos = vehicle_pos + offsets[ seat_num & 3 ] * width;
|
||
|
|
||
|
Matrix3D tm( pos );
|
||
|
if ( Peek_Human_Phys()->Can_Teleport( tm ) ) {
|
||
|
Peek_Human_Phys()->Set_Position( pos );
|
||
|
} else {
|
||
|
Vector3 new_pos = pos;
|
||
|
Peek_Human_Phys()->Find_Teleport_Location( pos, 4, &new_pos );
|
||
|
Peek_Human_Phys()->Set_Position( new_pos );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool SoldierGameObj::Is_Permitted_To_Enter_Vehicle(void)
|
||
|
{
|
||
|
if (!CombatManager::Is_Gameplay_Permitted()) {
|
||
|
//
|
||
|
// You can't enter a vehicle if there are no opponents, etc
|
||
|
//
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
//
|
||
|
// A soldier cannot enter a vehicle if he is carrying a flag
|
||
|
//
|
||
|
if (CtfTeamFlag == NO_FLAG) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Get_Velocity(Vector3 & vel)
|
||
|
{
|
||
|
Peek_Human_Phys()->Get_Velocity(&vel);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Velocity(Vector3 & vel)
|
||
|
{
|
||
|
Peek_Human_Phys()->Set_Velocity(vel);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Give_All_Weapons(void)
|
||
|
{
|
||
|
//WWASSERT(CombatManager::I_Am_Server());
|
||
|
// WWASSERT(PlayerType != PLAYERTYPE_NEUTRAL);
|
||
|
|
||
|
// For all weapon defs with the AGiveWeaponsWeapon checked, Give It!
|
||
|
for ( WeaponDefinitionClass *weapon_def = (WeaponDefinitionClass *)DefinitionMgrClass::Get_First( CLASSID_DEF_WEAPON );
|
||
|
weapon_def != NULL;
|
||
|
weapon_def = (WeaponDefinitionClass *)DefinitionMgrClass::Get_Next( weapon_def, CLASSID_DEF_WEAPON ) ) {
|
||
|
if ( weapon_def->AGiveWeaponsWeapon ) {
|
||
|
WeaponBag->Add_Weapon( weapon_def->Get_Name(), -1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
VehicleGameObj * SoldierGameObj::Get_Profile_Vehicle( void )
|
||
|
{
|
||
|
if (( Get_State() == HumanStateClass::IN_VEHICLE ) && Vehicle ) {
|
||
|
return Vehicle;
|
||
|
}
|
||
|
|
||
|
if (( Get_State() == HumanStateClass::TRANSITION ) &&
|
||
|
( TransitionCompletionData != NULL )) {
|
||
|
if ( TransitionCompletionData->Type == TransitionDataClass::VEHICLE_ENTER ) {
|
||
|
return (VehicleGameObj *)TransitionCompletionData->Vehicle.Get_Ptr();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
bool SoldierGameObj::Use_Ladder_View( void )
|
||
|
{
|
||
|
if ( Get_State() == HumanStateClass::LADDER ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (( Get_State() == HumanStateClass::TRANSITION ) &&
|
||
|
( TransitionCompletionData != NULL )) {
|
||
|
if (( TransitionCompletionData->Type == TransitionDataClass::LADDER_ENTER_TOP ) ||
|
||
|
( TransitionCompletionData->Type == TransitionDataClass::LADDER_ENTER_BOTTOM )) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Model(const char *model_name)
|
||
|
{
|
||
|
Peek_Physical_Object()->Set_Model_By_Name(model_name);
|
||
|
HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() ); // Must set the anim control after the phys object
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
|
||
|
Vector3 SoldierGameObj::Get_Bullseye_Position( void )
|
||
|
{
|
||
|
if ( Get_Vehicle() != NULL ) {
|
||
|
return Get_Vehicle()->Get_Bullseye_Position();
|
||
|
}
|
||
|
|
||
|
Vector3 pos;
|
||
|
Get_Position(&pos);
|
||
|
if ( Is_Crouched() ) {
|
||
|
pos.Z += 0.5f;
|
||
|
} else {
|
||
|
pos.Z += Get_Bullseye_Offset_Z();
|
||
|
}
|
||
|
// pos.Z += 1.2f;
|
||
|
return pos;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
|
||
|
bool SoldierGameObj::Can_See(SoldierGameObj * p_soldier)
|
||
|
{
|
||
|
WWASSERT(p_soldier != NULL);
|
||
|
|
||
|
Vector3 ray_start = Get_Bullseye_Position();
|
||
|
Vector3 ray_end = p_soldier->Get_Bullseye_Position(); // This may a big high
|
||
|
|
||
|
Vector3 path = ray_end - ray_start;
|
||
|
if (path.Length() > 1) {
|
||
|
Vector3 offset = path;
|
||
|
offset.Normalize();
|
||
|
ray_start += offset;
|
||
|
}
|
||
|
|
||
|
LineSegClass ray(ray_start, ray_end);
|
||
|
CastResultStruct result;
|
||
|
PhysRayCollisionTestClass raytest(ray, &result,
|
||
|
BULLET_COLLISION_GROUP, COLLISION_TYPE_PHYSICAL);
|
||
|
WWASSERT(COMBAT_SCENE != NULL);
|
||
|
{ WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray(raytest);
|
||
|
}
|
||
|
|
||
|
bool can_see = false;
|
||
|
|
||
|
if (raytest.CollidedPhysObj != NULL) {
|
||
|
SmartGameObj * p_blocker = NULL;
|
||
|
|
||
|
if ( raytest.CollidedPhysObj->Get_Observer() != NULL ) {
|
||
|
PhysicalGameObj * p_obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_PhysicalGameObj();
|
||
|
if ( p_obj != NULL ) {
|
||
|
p_blocker = p_obj->As_SmartGameObj();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (p_blocker != NULL && p_blocker->Get_Control_Owner() ==
|
||
|
p_soldier->Get_Control_Owner()) {
|
||
|
can_see = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return can_see;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Adjust_Skeleton( float height, float width )
|
||
|
{
|
||
|
// Debug_Say(( "Height %f, width %f\n", height, width ));
|
||
|
|
||
|
// Only adjust male skeletons
|
||
|
Animatable3DObjClass * robj = (Animatable3DObjClass *)Peek_Model();
|
||
|
if ( !robj || !robj->Get_HTree() || robj->Get_HTree()->Get_Name()[2] != 'A' ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
HTreeClass * tree_base = NULL;
|
||
|
HTreeClass * tree_tall = NULL;
|
||
|
HTreeClass * tree_wide = NULL;
|
||
|
|
||
|
if ( tree_base == NULL ) {
|
||
|
tree_base = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_human" );
|
||
|
tree_tall = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_tall" );
|
||
|
tree_wide = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_wide" );
|
||
|
}
|
||
|
|
||
|
if ( ( tree_base != NULL ) && ( tree_tall != NULL ) && ( tree_wide != NULL ) ) {
|
||
|
|
||
|
HTreeClass *tree = HTreeClass::Create_Interpolated( tree_base, tree_tall, tree_wide,
|
||
|
height, width );
|
||
|
if ( tree ) {
|
||
|
robj->Set_HTree( tree );
|
||
|
delete tree;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Ctf_Team_Flag(int team)
|
||
|
{
|
||
|
WWASSERT(
|
||
|
team == NO_FLAG ||
|
||
|
team == PLAYERTYPE_NOD ||
|
||
|
team == PLAYERTYPE_GDI);
|
||
|
|
||
|
CtfTeamFlag = team;
|
||
|
|
||
|
Set_Object_Dirty_Bit( NetworkObjectClass::BIT_OCCASIONAL, true );
|
||
|
|
||
|
if (team == NO_FLAG) {
|
||
|
Set_Back_Flag_Model(NULL);
|
||
|
} else {
|
||
|
//Set_Back_Flag_Model("O_Flag.w3d", Get_Team_Color());
|
||
|
Set_Back_Flag_Model("O_Flag.w3d", Get_Color_For_Team(team));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Set_Precision(void)
|
||
|
{
|
||
|
cEncoderList::Set_Precision(BITPACK_CTF_TEAM_FLAG, NO_FLAG, (int) PLAYERTYPE_GDI);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
SoldierObserverClass * SoldierGameObj::Get_Innate_Controller( void )
|
||
|
{
|
||
|
if ( Get_Definition().UseInnateBehavior ) {
|
||
|
const GameObjObserverList & observer_list = Get_Observers();
|
||
|
for( int index = 0; index < observer_list.Count(); index++ ) {
|
||
|
if ( !strcmp( observer_list[ index ]->Get_Name(), "Innate Soldier" ) ) {
|
||
|
return (SoldierObserverClass *)observer_list[ index ];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
const char * SoldierGameObj::Get_First_Person_Hands_Model_Name( void )
|
||
|
{
|
||
|
return Get_Definition().FirstPersonHands;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Get_Information( StringClass & string )
|
||
|
{
|
||
|
SmartGameObj::Get_Information( string );
|
||
|
|
||
|
HumanState.Get_Information( string );
|
||
|
|
||
|
SoldierObserverClass * innate = Get_Innate_Controller();
|
||
|
if ( innate ) {
|
||
|
innate->Get_Information( string );
|
||
|
|
||
|
StringClass disabled;
|
||
|
if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_BULLET_HEARD)) {
|
||
|
disabled += "B";
|
||
|
}
|
||
|
if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_GUNSHOT_HEARD)) {
|
||
|
disabled += "G";
|
||
|
}
|
||
|
if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_FOOTSTEP_HEARD)) {
|
||
|
disabled += "F";
|
||
|
}
|
||
|
if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_ENEMY_SEEN)) {
|
||
|
disabled += "S";
|
||
|
}
|
||
|
if (!Is_Innate_Enabled(SOLDIER_INNATE_ACTIONS)) {
|
||
|
disabled += "A";
|
||
|
}
|
||
|
|
||
|
if ( !disabled.Is_Empty() ) {
|
||
|
string += "Disabled:";
|
||
|
string += disabled;
|
||
|
string += "\n";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
//void SoldierGameObj::Get_Extended_Information(StringClass & description)
|
||
|
void SoldierGameObj::Get_Description(StringClass & description)
|
||
|
{
|
||
|
StringClass line;
|
||
|
|
||
|
line.Format("ID: %d\n", Get_ID());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("NAME: %s\n", Get_Definition().Get_Name());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("TEAM: %d\n", Get_Player_Type());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("CONTR: %d\n", Get_Control_Owner());
|
||
|
description += line;
|
||
|
|
||
|
Vector3 position;
|
||
|
Get_Position(&position);
|
||
|
line.Format("POS: %-5.2f, %-5.2f, %-5.2f\n", position.X, position.Y, position.Z);
|
||
|
description += line;
|
||
|
|
||
|
Vector3 target = Get_Targeting_Pos();
|
||
|
line.Format("TGT: %-5.2f, %-5.2f, %-5.2f\n", target.X, target.Y, target.Z);
|
||
|
description += line;
|
||
|
|
||
|
Vector3 velocity;
|
||
|
Get_Velocity(velocity);
|
||
|
line.Format("VEL: %-5.2f, %-5.2f, %-5.2f\n", velocity.X, velocity.Y, velocity.Z);
|
||
|
description += line;
|
||
|
|
||
|
WeaponClass * p_weapon = Get_Weapon();
|
||
|
if (p_weapon != NULL) {
|
||
|
line.Format("WEAP: %s\n", p_weapon->Get_Name());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("TRNDS: %d\n", p_weapon->Get_Total_Rounds());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format("HLTH: %-5.2f\n", DefenseObject.Get_Health());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("HMAX: %-5.2f\n", DefenseObject.Get_Health_Max());
|
||
|
description += line;
|
||
|
|
||
|
HumanPhysClass * p_human_phys = Peek_Human_Phys();
|
||
|
if (p_human_phys != NULL) {
|
||
|
line.Format("FACE: %-5.2f\n", p_human_phys->Get_Facing());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format("STATE: %s", Get_State_Name());
|
||
|
if (Is_Crouched()) {
|
||
|
line += " (Crouched)";
|
||
|
}
|
||
|
line += "\n";
|
||
|
description += line;
|
||
|
|
||
|
line.Format("ANIM: %s\n", AnimationName);
|
||
|
description += line;
|
||
|
|
||
|
if (Vehicle != NULL) {
|
||
|
line.Format("VEH: %d\n", Vehicle->Get_ID());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
const GameObjObserverList & observer_list = Get_Observers();
|
||
|
line.Format("#OBSV: %d\n", observer_list.Count());
|
||
|
description += line;
|
||
|
|
||
|
for (int index = 0; index < observer_list.Count(); index++) {
|
||
|
line.Format(" %s\n", observer_list[index]->Get_Name());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format("INNAT: %d\n", Is_Innate_Enabled());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("HIB: %d\n", Is_Hibernating());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("CTRL: %d, %d, %5.2f, %5.2f, %5.2f, %5.2f\n",
|
||
|
Control.Get_One_Time_Boolean_Bits(),
|
||
|
Control.Get_Continuous_Boolean_Bits(),
|
||
|
Control.Get_Analog(ControlClass::ANALOG_MOVE_FORWARD),
|
||
|
Control.Get_Analog(ControlClass::ANALOG_MOVE_LEFT),
|
||
|
Control.Get_Analog(ControlClass::ANALOG_MOVE_UP),
|
||
|
Control.Get_Analog(ControlClass::ANALOG_TURN_LEFT));
|
||
|
description += line;
|
||
|
|
||
|
ActionClass * p_action = Get_Action();
|
||
|
if (p_action != NULL)
|
||
|
{
|
||
|
line.Format("ACTCT: %d\n", p_action->Get_Act_Count());
|
||
|
description += line;
|
||
|
|
||
|
Vector3 move_loc = p_action->Get_Parameters().MoveLocation;
|
||
|
line.Format("MVLOC: (%5.2f, %5.2f, %5.2f)\n", move_loc.X, move_loc.Y, move_loc.Z);
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format("stlth: %d\n", Is_Stealth_Enabled());
|
||
|
description += line;
|
||
|
|
||
|
line.Format(" on: %d\n", Is_Stealthed());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("ISC: %d\n", Get_Import_State_Count());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
|
||
|
struct {
|
||
|
ArmorWarheadManager::SpecialDamageType Mode;
|
||
|
const char * EmitterName;
|
||
|
const char * BoneName;
|
||
|
} _SpecialDamageEmitters[] = {
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE, "AG_FLAME01", "C SPINE" },
|
||
|
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C SPINE" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C L UPPERARM" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C R UPPERARM" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C L CALF" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C R CALF" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE, "AG_FLAME01", "C NECK" },
|
||
|
// { ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE, "E_FLAME01", "C L UPPERARM" },
|
||
|
// { ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE, "E_FLAME01", "C R UPPERARM" },
|
||
|
{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM, "e_tib_dump", "C SPINE" },
|
||
|
// { ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM, "e_tib_dump", "C L UPPERARM" },
|
||
|
// { ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM, "e_tib_dump", "C R UPPERARM" },
|
||
|
};
|
||
|
#define NUM_SPECIAL_DAMAGE_EMITTERS (sizeof(_SpecialDamageEmitters)/sizeof(_SpecialDamageEmitters[0]))
|
||
|
|
||
|
void SoldierGameObj::Set_Special_Damage_Mode( ArmorWarheadManager::SpecialDamageType mode, ArmedGameObj * damager )
|
||
|
{
|
||
|
// Check for turning off
|
||
|
if ( mode == ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE ) {
|
||
|
|
||
|
// remove all subobjs from the bones used
|
||
|
for ( int emitter = 0; emitter < NUM_SPECIAL_DAMAGE_EMITTERS; emitter++ ) {
|
||
|
if ( _SpecialDamageEmitters[emitter].Mode == SpecialDamageMode ) {
|
||
|
int bone = Peek_Model()->Get_Bone_Index( _SpecialDamageEmitters[emitter].BoneName );
|
||
|
int sub_count = Peek_Model()->Get_Num_Sub_Objects_On_Bone( bone );
|
||
|
while ( --sub_count >= 0 ) {
|
||
|
RenderObjClass * sub = Peek_Model()->Get_Sub_Object_On_Bone( sub_count, bone );
|
||
|
if ( ::stricmp(sub->Get_Name(), _SpecialDamageEmitters[emitter].EmitterName ) == 0 ) {
|
||
|
Peek_Model()->Remove_Sub_Object( sub );
|
||
|
}
|
||
|
sub->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( SpecialDamageEffect != NULL ) {
|
||
|
SpecialDamageEffect->Set_Target_Parameter(0);
|
||
|
SpecialDamageEffect->Enable_Remove_On_Complete(true);
|
||
|
REF_PTR_RELEASE( SpecialDamageEffect );
|
||
|
}
|
||
|
|
||
|
if ( !Is_Human_Controlled() && IS_MISSION ) {
|
||
|
if ( HumanState.Get_State() >= HumanStateClass::ON_FIRE &&
|
||
|
HumanState.Get_State() <= HumanStateClass::ON_CNC_CHEM ) {
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SpecialDamageDamager = NULL;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if ( mode != SpecialDamageMode ) {
|
||
|
|
||
|
// Remove old mode
|
||
|
Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );
|
||
|
|
||
|
if ( Allow_Special_Damage_State_Lock() && IS_MISSION && mode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE ) {
|
||
|
HumanState.Set_State( (HumanStateClass::HumanStateType)( mode -
|
||
|
ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE + HumanStateClass::ON_FIRE ) );
|
||
|
}
|
||
|
|
||
|
// Add new emitters
|
||
|
for ( int emitter = 0; emitter < NUM_SPECIAL_DAMAGE_EMITTERS; emitter++ ) {
|
||
|
if ( _SpecialDamageEmitters[emitter].Mode == mode ) {
|
||
|
RenderObjClass * ro = WW3DAssetManager::Get_Instance()->Create_Render_Obj(
|
||
|
_SpecialDamageEmitters[emitter].EmitterName );
|
||
|
if ( ro != NULL ) {
|
||
|
Peek_Model()->Add_Sub_Object_To_Bone( ro, _SpecialDamageEmitters[emitter].BoneName );
|
||
|
ro->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add electric effect
|
||
|
if ( mode == ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_ELECTRIC ) {
|
||
|
WWASSERT( SpecialDamageEffect == NULL );
|
||
|
SpecialDamageEffect = CombatMaterialEffectManager::Get_Electrocution_Effect();
|
||
|
if ( SpecialDamageEffect != NULL ) {
|
||
|
SpecialDamageEffect->Set_Target_Parameter(0.49f); // go almost halfway
|
||
|
Peek_Human_Phys()->Add_Effect_To_Me( SpecialDamageEffect );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Save Damager
|
||
|
SpecialDamageDamager = damager;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
SpecialDamageMode = mode;
|
||
|
SpecialDamageTimer = ArmorWarheadManager::Get_Special_Damage_Duration( SpecialDamageMode );
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Perturb_Position(float max_perturb)
|
||
|
{
|
||
|
//
|
||
|
// Jiggle the soldier to a non-intersecting position
|
||
|
//
|
||
|
Vector3 initial_position;
|
||
|
Get_Position(&initial_position);
|
||
|
Vector3 new_position;
|
||
|
WWASSERT(Peek_Human_Phys() != NULL);
|
||
|
bool succeeded = Peek_Human_Phys()->Find_Teleport_Location(
|
||
|
initial_position, max_perturb, &new_position);
|
||
|
if (succeeded) {
|
||
|
Set_Position(new_position);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Toggle_Fly_Mode( void )
|
||
|
{
|
||
|
InFlyMode = !InFlyMode;
|
||
|
|
||
|
if ( InFlyMode ) {
|
||
|
HumanState.Set_State( HumanStateClass::DEBUG_FLY );
|
||
|
Peek_Physical_Object()->Set_Collision_Group( UNCOLLIDEABLE_GROUP );
|
||
|
} else {
|
||
|
HumanState.Set_State( HumanStateClass::UPRIGHT );
|
||
|
Peek_Physical_Object()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void SoldierGameObj::Set_AI_State( SoldierAIState state )
|
||
|
{
|
||
|
//
|
||
|
// Is the soldier changing states?
|
||
|
//
|
||
|
if ( AIState != state ) {
|
||
|
|
||
|
//
|
||
|
// Kill the conversation (if necessary)
|
||
|
//
|
||
|
if ( state >= AI_STATE_SEARCH && state > AIState &&
|
||
|
ActiveConversation != NULL &&
|
||
|
ActiveConversation->Is_Interruptable ())
|
||
|
{
|
||
|
ActiveConversation->Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
|
||
|
}
|
||
|
|
||
|
AIState = state;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Soldiers maintain all RenderObjs they have created for later use and saving
|
||
|
** This is used for weapon models
|
||
|
*/
|
||
|
void SoldierGameObj::Add_RenderObj( RenderObjClass * obj )
|
||
|
{
|
||
|
RenderObjList.Add( obj );
|
||
|
obj->Add_Ref();
|
||
|
}
|
||
|
|
||
|
RenderObjClass * SoldierGameObj::Find_RenderObj( const char * name )
|
||
|
{
|
||
|
for ( int i = 0; i < RenderObjList.Count(); i++ ) {
|
||
|
if ( !stricmp( RenderObjList[i]->Get_Name(), name ) ) {
|
||
|
return RenderObjList[i];
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Reset_RenderObjs( void )
|
||
|
{
|
||
|
while ( RenderObjList.Count() ) {
|
||
|
RenderObjList[0]->Release_Ref();
|
||
|
RenderObjList.Delete( 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void SoldierGameObj::Change_Flag_Status(int ctf_team_flag)
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
if (ctf_team_flag == NO_FLAG) {
|
||
|
//
|
||
|
// We dropped a flag
|
||
|
//
|
||
|
WWASSERT(WWAudioClass::Get_Instance() != NULL);
|
||
|
WWAudioClass::Get_Instance()->Create_Instant_Sound("Drop_Flag", Get_Transform());
|
||
|
|
||
|
float speed_factor = 100 / (float) CombatManager::Get_Max_Speed_Pc(this);
|
||
|
Set_Max_Speed(speed_factor * Get_Max_Speed());
|
||
|
|
||
|
Set_Ctf_Team_Flag(ctf_team_flag);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// We picked up a flag
|
||
|
//
|
||
|
WWASSERT(WWAudioClass::Get_Instance() != NULL);
|
||
|
WWAudioClass::Get_Instance()->Create_Instant_Sound("Pick_Up_Flag", Get_Transform());
|
||
|
|
||
|
Set_Ctf_Team_Flag(ctf_team_flag);
|
||
|
|
||
|
float speed_factor = CombatManager::Get_Max_Speed_Pc(this) / 100.0f;
|
||
|
Set_Max_Speed(speed_factor * Get_Max_Speed());
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
void SoldierGameObj::Interpret_Sc_Position_Data(Vector3 & sc_position)
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
switch (CombatManager::Get_Client_Interpolation_Model()) {
|
||
|
|
||
|
case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE:
|
||
|
//
|
||
|
// Simplest case - pop to location given by server
|
||
|
//
|
||
|
//Set_Position(sc_position);
|
||
|
|
||
|
if (Get_State() == HumanStateClass::TRANSITION) {
|
||
|
//
|
||
|
// Pop
|
||
|
//
|
||
|
Set_Position(sc_position);
|
||
|
if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
|
||
|
Debug_Say(("Popping own soldier at time %s\n", cMiscUtil::Get_Text_Time()));
|
||
|
}
|
||
|
} else {
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Vector3 pos_error = current_position - sc_position;
|
||
|
if (pos_error.Length() > 2.0f) {
|
||
|
Set_Position(sc_position);
|
||
|
if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
|
||
|
Debug_Say(("Popping own soldier at time %s\n", cMiscUtil::Get_Text_Time()));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE_WITH_BLENDING:
|
||
|
if (Get_State() == HumanStateClass::TRANSITION) {
|
||
|
//
|
||
|
// Pop
|
||
|
//
|
||
|
Set_Position(sc_position);
|
||
|
} else {
|
||
|
//
|
||
|
// Blend to position given by server
|
||
|
//
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Set_Position(0.50 * current_position + 0.50 * sc_position);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CLIENT_INTERPOLATION_PATHFIND: {
|
||
|
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Vector3 pos_error = current_position - sc_position;
|
||
|
|
||
|
if (pos_error.Length() > 0.02f) {
|
||
|
|
||
|
if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
|
||
|
if (pos_error.Length() > 2) {
|
||
|
//
|
||
|
// Client is authoritative over his own guy, unless error
|
||
|
// becomes significant. In this case, pop.
|
||
|
//
|
||
|
Set_Position(sc_position);
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
//if (pos_error.Length() > 20) {
|
||
|
// //
|
||
|
// // Pop
|
||
|
// //
|
||
|
// Set_Position(sc_position);
|
||
|
//} else {
|
||
|
|
||
|
//
|
||
|
// For NPC's the server could transmit the final destination as well as
|
||
|
// the proper location. Pop if appropriate to get as close to
|
||
|
// proper dest as possible, but pathfind instead to the final dest.
|
||
|
//
|
||
|
ActionParamsStruct parameters;
|
||
|
parameters.Priority = 1;
|
||
|
parameters.MoveLocation = sc_position;
|
||
|
parameters.MoveArrivedDistance = 0.1f;
|
||
|
WWASSERT(Get_Action() != NULL);
|
||
|
Get_Action()->Goto(parameters);
|
||
|
//}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
WWASSERT(0);
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
void SoldierGameObj::Think_Pathfind( void )
|
||
|
{
|
||
|
// CLIENT_PATHFINDING
|
||
|
|
||
|
//
|
||
|
// Pop pathfinding client-side soldiers at the earliest opportunity -
|
||
|
// as soon as we find them out of the camera fulcrum.
|
||
|
//
|
||
|
if (CombatManager::I_Am_Only_Client() &&
|
||
|
Get_Control_Owner() != CombatManager::Get_My_Id()) {
|
||
|
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
|
||
|
ActionClass * p_action = Get_Action();
|
||
|
WWASSERT(p_action != NULL);
|
||
|
|
||
|
const Vector3 height_offset(0, 0, 0.9f);
|
||
|
|
||
|
Vector3 from_position = current_position + height_offset;
|
||
|
Vector3 to_position = p_action->MovementLocation + height_offset;
|
||
|
|
||
|
//
|
||
|
// We can only pop if he is not presently in the camera fulcrum.
|
||
|
//
|
||
|
if (p_action->MovementAction != NULL &&
|
||
|
!CombatManager::Is_In_Camera_Frustrum(from_position)) {
|
||
|
|
||
|
if (!CombatManager::Is_In_Camera_Frustrum(to_position)) {
|
||
|
//
|
||
|
// If the correct position is also out of the fulcrum, pop
|
||
|
// all the way there.
|
||
|
//
|
||
|
Set_Position(p_action->MovementLocation);
|
||
|
p_action->Set_Movement(NULL);
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// We are crossing into the fulcrum. Pop as close to the edge
|
||
|
// of the fulcrum as possible and pathfind from there.
|
||
|
//
|
||
|
|
||
|
//Debug_Say(("FRAME\n"));
|
||
|
bool is_finished = false;
|
||
|
do {
|
||
|
|
||
|
Vector3 next_pos;
|
||
|
p_action->Movement_Act(next_pos);
|
||
|
|
||
|
//if (CombatManager::Is_In_Camera_Frustrum(next_pos + height_offset)) {
|
||
|
// is_finished = true;
|
||
|
//
|
||
|
// //
|
||
|
// // Logically it would be better if the next line could be omitted.
|
||
|
// // However this seems to screw things up.
|
||
|
// //
|
||
|
// Set_Position(next_pos);
|
||
|
//
|
||
|
// //Debug_Say(("Finished skipping."));
|
||
|
//} else {
|
||
|
// Set_Position(next_pos);
|
||
|
//
|
||
|
// //Debug_Say(("Skipping to (%5.2f, %5.2f, %5.2f)\n",
|
||
|
// // next_pos.X, next_pos.Y, next_pos.Z));
|
||
|
//}
|
||
|
|
||
|
Set_Position(next_pos);
|
||
|
|
||
|
Vector3 adjusted_next_pos = next_pos + height_offset;
|
||
|
if (CombatManager::Is_In_Camera_Frustrum(adjusted_next_pos)) {
|
||
|
is_finished = true;
|
||
|
}
|
||
|
|
||
|
} while ((p_action->MovementAction != NULL) && !is_finished);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SoldierGameObj::Think_Pathfind(void)
|
||
|
{
|
||
|
//
|
||
|
// Pop pathfinding client-side soldiers at the earliest opportunity -
|
||
|
// as soon as we find them out of the camera fulcrum.
|
||
|
//
|
||
|
|
||
|
if ((CombatManager::Get_Client_Interpolation_Model() == CLIENT_INTERPOLATION_PATHFIND) &&
|
||
|
CombatManager::I_Am_Only_Client() &&
|
||
|
Get_Control_Owner() != CombatManager::Get_My_Id()) {
|
||
|
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
|
||
|
ActionClass * p_action = Get_Action();
|
||
|
WWASSERT(p_action != NULL);
|
||
|
|
||
|
ActionParamsStruct parameters = p_action->Get_Parameters();
|
||
|
|
||
|
const Vector3 height_offset(0, 0, 0.9f);
|
||
|
|
||
|
Vector3 from_position = current_position + height_offset;
|
||
|
Vector3 to_position = parameters.MoveLocation + height_offset;
|
||
|
|
||
|
//
|
||
|
// We can only pop if he is not presently in the camera fulcrum.
|
||
|
//
|
||
|
if (!CombatManager::Is_In_Camera_Frustrum(from_position)) {
|
||
|
|
||
|
if (!CombatManager::Is_In_Camera_Frustrum(to_position)) {
|
||
|
//
|
||
|
// If the correct position is also out of the fulcrum, pop
|
||
|
// all the way there.
|
||
|
//
|
||
|
Set_Position(to_position);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Keep popping until we are almost in the fulcrum.
|
||
|
//
|
||
|
Vector3 current_pos;
|
||
|
Get_Position(¤t_pos);
|
||
|
Vector3 last_pos;
|
||
|
Vector3 test_pos;
|
||
|
do {
|
||
|
|
||
|
last_pos = current_pos;
|
||
|
p_action->Act();
|
||
|
Get_Position(¤t_pos);
|
||
|
test_pos = current_pos + height_offset;
|
||
|
|
||
|
} while ((current_pos != last_pos) &&
|
||
|
!CombatManager::Is_In_Camera_Frustrum(test_pos));
|
||
|
|
||
|
Set_Position(last_pos);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
void SoldierGameObj::Update_Healing_Effect( void )
|
||
|
{
|
||
|
if ( HealingEffect != NULL ) {
|
||
|
|
||
|
if (( HealingEffect->Get_Target_Parameter() >= 0.49f ) &&
|
||
|
( HealingEffect->Get_Parameter() >= 0.49f ) ) {
|
||
|
HealingEffect->Set_Target_Parameter( 0 );
|
||
|
}
|
||
|
|
||
|
if (( HealingEffect->Get_Target_Parameter() == 0 ) &&
|
||
|
( HealingEffect->Get_Parameter() == 0 ) ) {
|
||
|
Peek_Human_Phys()->Remove_Effect_From_Me( HealingEffect );
|
||
|
REF_PTR_RELEASE( HealingEffect );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Enable_Ghost_Collision( bool onoff )
|
||
|
{
|
||
|
bool is_using_ghost_collision = (Peek_Physical_Object ()->Get_Collision_Group() == SOLDIER_GHOST_COLLISION_GROUP);
|
||
|
if ( onoff == is_using_ghost_collision ) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Change the collision group as necessary
|
||
|
//
|
||
|
if ( onoff ) {
|
||
|
Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_GHOST_COLLISION_GROUP );
|
||
|
} else {
|
||
|
Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool SoldierGameObj::Is_Safe_To_Disable_Ghost_Collision( const Vector3 &curr_pos )
|
||
|
{
|
||
|
const Vector3 PERSONAL_SPACE_BOX_SIZE (1.5F, 1.5F, 1.0F);
|
||
|
const float HUMAN_HALF_HEIGHT = 1.0F;
|
||
|
bool retval = true;
|
||
|
|
||
|
//
|
||
|
// Build a box that represents the "personal" space around the soldier
|
||
|
//
|
||
|
Vector3 box_pos = curr_pos + Vector3 (0, 0, HUMAN_HALF_HEIGHT);
|
||
|
AABoxClass box (box_pos, PERSONAL_SPACE_BOX_SIZE);
|
||
|
|
||
|
//
|
||
|
// Collect all the dynamic objects in this box
|
||
|
//
|
||
|
NonRefPhysListClass obj_list;
|
||
|
PhysicsSceneClass::Get_Instance ()->Collect_Objects (box, false, true, &obj_list);
|
||
|
|
||
|
//
|
||
|
// Loop over all the collected objects
|
||
|
//
|
||
|
NonRefPhysListIterator it (&obj_list);
|
||
|
for (it.First(); !it.Is_Done(); it.Next()) {
|
||
|
PhysClass *phys_obj = it.Peek_Obj ();
|
||
|
PhysicalGameObj *game_obj = NULL;
|
||
|
|
||
|
if ( phys_obj->As_HumanPhysClass () && phys_obj->Get_Observer() != NULL ) {
|
||
|
game_obj = ((CombatPhysObserverClass *)phys_obj->Get_Observer())->As_PhysicalGameObj();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is this a living soldier?
|
||
|
//
|
||
|
if ( game_obj != NULL && game_obj != this &&
|
||
|
game_obj->As_SoldierGameObj() != NULL &&
|
||
|
game_obj->As_SoldierGameObj()->Is_Destroyed () == false)
|
||
|
{
|
||
|
Vector3 block_pos;
|
||
|
game_obj->Get_Position (&block_pos);
|
||
|
|
||
|
//
|
||
|
// Lets make sure the boxes really intersect
|
||
|
//
|
||
|
AABoxClass block_box (block_pos + Vector3 (0, 0, 1.0F), Vector3 (0.3F, 0.3F, 1.0F));
|
||
|
if (CollisionMath::Overlap_Test (box, block_box) != CollisionMath::OUTSIDE) {
|
||
|
|
||
|
//
|
||
|
// Is the boxes intersect, then it is NOT SAFE to disable ghost collision
|
||
|
// mode.
|
||
|
//
|
||
|
retval = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool SoldierGameObj::Is_Soldier_Blocked( const Vector3 &curr_pos )
|
||
|
{
|
||
|
//
|
||
|
// Only do this for soldiers who meet our criteria
|
||
|
//
|
||
|
if ( (Get_Action () != NULL && Get_Action ()->Is_Busy ()) ||
|
||
|
Is_Destroyed () || (IS_SOLOPLAY == false))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const Vector3 PERSONAL_SPACE_BOX_SIZE (1.5F, 1.5F, 1.0F);
|
||
|
const float HUMAN_HALF_HEIGHT = 1.0F;
|
||
|
|
||
|
//
|
||
|
// Build a box that represents the "personal" space around the soldier
|
||
|
//
|
||
|
Vector3 box_pos = curr_pos + Vector3 (0, 0, HUMAN_HALF_HEIGHT);
|
||
|
AABoxClass box (box_pos, PERSONAL_SPACE_BOX_SIZE);
|
||
|
|
||
|
//
|
||
|
// Collect all the dynamic objects in this box
|
||
|
//
|
||
|
NonRefPhysListClass obj_list;
|
||
|
PhysicsSceneClass::Get_Instance ()->Collect_Objects (box, false, true, &obj_list);
|
||
|
|
||
|
uint32 my_id = Get_ID ();
|
||
|
uint32 smallest_id = my_id;
|
||
|
|
||
|
//
|
||
|
// Loop over all the collected objects
|
||
|
//
|
||
|
NonRefPhysListIterator it (&obj_list);
|
||
|
for (it.First(); !it.Is_Done(); it.Next()) {
|
||
|
PhysClass *phys_obj = it.Peek_Obj ();
|
||
|
PhysicalGameObj *game_obj = NULL;
|
||
|
|
||
|
if ( phys_obj->As_HumanPhysClass () && phys_obj->Get_Observer() != NULL ) {
|
||
|
game_obj = ((CombatPhysObserverClass *)phys_obj->Get_Observer())->As_PhysicalGameObj();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is this a living soldier?
|
||
|
//
|
||
|
if ( game_obj != NULL && game_obj != this &&
|
||
|
game_obj->As_SoldierGameObj() != NULL &&
|
||
|
game_obj->As_SoldierGameObj()->Is_Destroyed () == false)
|
||
|
{
|
||
|
Vector3 block_pos;
|
||
|
game_obj->Get_Position (&block_pos);
|
||
|
|
||
|
//
|
||
|
// Lets make sure the boxes really intersect
|
||
|
//
|
||
|
AABoxClass block_box (block_pos + Vector3 (0, 0, 1.0F), Vector3 (0.3F, 0.3F, 1.0F));
|
||
|
if (CollisionMath::Overlap_Test (box, block_box) != CollisionMath::OUTSIDE) {
|
||
|
|
||
|
if (game_obj->As_SmartGameObj ()->Is_Human_Controlled () == false) {
|
||
|
|
||
|
//
|
||
|
// Is this soldier moving?
|
||
|
//
|
||
|
ActionClass *curr_action = game_obj->As_SmartGameObj ()->Get_Action ();
|
||
|
Vector3 dest_pos = curr_action->Get_Parameters ().MoveLocation;
|
||
|
float distance = (block_pos - dest_pos).Length ();
|
||
|
if (distance > curr_action->Get_Parameters ().MoveArrivedDistance) {
|
||
|
|
||
|
//
|
||
|
// Is this the smallest ID so far?
|
||
|
//
|
||
|
uint32 curr_id = game_obj->Get_ID ();
|
||
|
if (curr_id < smallest_id) {
|
||
|
smallest_id = curr_id;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Push the other guy out of the way (a little)
|
||
|
//
|
||
|
/*if (Is_Human_Controlled () || curr_id > my_id) {
|
||
|
Vector3 delta_vector = block_pos - curr_pos;
|
||
|
delta_vector.Normalize ();
|
||
|
SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
|
||
|
if (soldier->Peek_Human_Phys () != NULL) {
|
||
|
soldier->Peek_Human_Phys ()->Collide (delta_vector * TimeManager::Get_Frame_Seconds ());
|
||
|
}
|
||
|
}*/
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (smallest_id != my_id);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool SoldierGameObj::Is_Targetable( void ) const
|
||
|
{
|
||
|
return (!(((SoldierGameObj*)this)->Is_In_Vehicle())) &&
|
||
|
SmartGameObj::Is_Targetable();
|
||
|
}
|
||
|
|
||
|
|
||
|
float SoldierGameObj::Get_Stealth_Fade_Distance(void) const
|
||
|
{
|
||
|
if (IS_MISSION) {
|
||
|
return GlobalSettingsDef::Get_Global_Settings()->Get_Stealth_Distance_Human();
|
||
|
} else {
|
||
|
return GlobalSettingsDef::Get_Global_Settings()->Get_MP_Stealth_Distance_Human();
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Lock_Facing( PhysicalGameObj * game_obj, bool turn_body )
|
||
|
{
|
||
|
FacingObject = game_obj;
|
||
|
FacingAllowBodyTurn = turn_body;
|
||
|
|
||
|
//
|
||
|
// Stop facing if there's nothing to look at
|
||
|
//
|
||
|
if ( game_obj == NULL ) {
|
||
|
Cancel_Look_At();
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SoldierGameObj::Update_Locked_Facing( void )
|
||
|
{
|
||
|
if ( FacingObject != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Get the position of the object we're "looking" at.
|
||
|
//
|
||
|
Vector3 pos;
|
||
|
FacingObject.Get_Ptr ()->Get_Position( &pos );
|
||
|
|
||
|
//
|
||
|
// If the object is a soldier, then look at his head
|
||
|
//
|
||
|
if ( FacingObject.Get_Ptr ()->As_PhysicalGameObj ()->As_SoldierGameObj () != NULL ) {
|
||
|
const float SOLDIER_HEIGHT = 1.7F;
|
||
|
pos.Z += SOLDIER_HEIGHT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Look at the object
|
||
|
//
|
||
|
Look_At( pos, 100.0F );
|
||
|
|
||
|
//
|
||
|
// If we can turn to face the object, do so...
|
||
|
//
|
||
|
if ( FacingAllowBodyTurn ) {
|
||
|
Internal_Set_Targeting( pos, false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
if (CombatManager::I_Am_Server()) {
|
||
|
CombatManager::Soldier_Dies(this);
|
||
|
}
|
||
|
*/
|
||
|
|