| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456 |
- /*
- ** 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/soldierobserver.cpp $*
- * *
- * $Author:: Byon_g $*
- * *
- * $Modtime:: 12/17/01 3:06p $*
- * *
- * $Revision:: 112 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "soldierobserver.h"
- #include "soldier.h"
- #include "action.h"
- #include "debug.h"
- #include "crandom.h"
- #include "cover.h"
- #include "wwaudio.h"
- #include "persistfactory.h"
- #include "combatchunkid.h"
- #include "combat.h"
- #include "gameobjobserver.h"
- #include "gameobjmanager.h"
- #include "weapons.h"
- #include "conversationmgr.h"
- #include "pathfind.h"
- #include "timemgr.h"
- #include "weaponbag.h"
- #include "wwprofile.h"
- #include "playertype.h"
- #define THINK_RATE 1
- #define THINK_ID 100123
- #define INNATE_ACTION_ID 9000000
- /*
- **
- */
- const char * SoldierAIStateNames[SoldierObserverClass::NUM_SOLDIER_AI_STATES] =
- {
- "Relaxed Idle", //SOLDIER_AI_RELAXED_IDLE = 0,
- "Alert Idle", //SOLDIER_AI_ALERT_IDLE,
- "Footsteps Heard", //SOLDIER_AI_FOOTSTEPS_HEARD,
- "Bullet Heard", //SOLDIER_AI_BULLET_HEARD,
- "Gunshot Heard", //SOLDIER_AI_GUNSHOT_HEARD,
- "Enemy Seen" //SOLDIER_AI_ENEMY_SEEN,
- };
- int StatePriorities[ SoldierObserverClass::NUM_SOLDIER_AI_STATES ] =
- {
- 10, // SOLDIER_AI_RELAXED_IDLE = 0,
- 20, // SOLDIER_AI_ALERTED_IDLE,
- 50, // SOLDIER_AI_FOOTSTEPS_HEARD,
- 50, // SOLDIER_AI_BULLET_HEARD,
- 70, // SOLDIER_AI_GUNSHOT_HEARD,
- 90, // SOLDIER_AI_ENEMY_SEEN,
- };
-
- typedef struct {
- float StateDuration; // How long we should stay in this state
- int NextState; // State to enter when we complete this state
- } SoldierAIStateData;
- SoldierAIStateData StateData[SoldierObserverClass::NUM_SOLDIER_AI_STATES] = {
- {9999, SoldierObserverClass::SOLDIER_AI_RELAXED_IDLE},
- {180, SoldierObserverClass::SOLDIER_AI_RELAXED_IDLE},
- {6, SoldierObserverClass::SOLDIER_AI_CONDITIONAL_IDLE},
- {20, SoldierObserverClass::SOLDIER_AI_ALERT_IDLE},
- {60, SoldierObserverClass::SOLDIER_AI_ALERT_IDLE}, // {60, SoldierObserverClass::SOLDIER_AI_BULLET_HEARD},
- {60, SoldierObserverClass::SOLDIER_AI_BULLET_HEARD},
- };
- /*
- ** Speech
- */
- const char* _SpeakEnemySeen[] =
- {
- "ThereHeIs02",
- "KillHim02",
- "GetHim02",
- "ForKane02",
- };
- const int _SpeakEnemySeenCount = (sizeof(_SpeakEnemySeen) / sizeof(_SpeakEnemySeen[0])) ;
- const char* Pick_Speak_EnemySeen(void)
- {
- int index = FreeRandom.Get_Int(_SpeakEnemySeenCount);
- return _SpeakEnemySeen[index];
- }
- Vector3 Random_Vector(float size)
- {
- Vector3 vect( FreeRandom.Get_Float( -size, size ),
- FreeRandom.Get_Float( -size, size ), 0 );
- return vect;
- }
- /*
- **
- */
- SoldierObserverClass::SoldierObserverClass( void ) :
- State( SOLDIER_AI_RELAXED_IDLE ),
- StateTimer( 0 ),
- HomeRadius( 9999999 ),
- ActionTimer( 0 ),
- CoverPosition( NULL ),
- CoveredAttack( false ),
- LastEvent( SOLDIER_AI_RELAXED_IDLE ),
- ConversationTimer( WWMath::Random_Float( 1.0F, 10.0F ) ),
- IsAlerted( false ),
- Aggressiveness( 0.5f ),
- TakeCoverProbability( 0.5f ),
- IsStationary( false ),
- LastWeaponIndex( 0 )
- {
- }
- SoldierObserverClass::~SoldierObserverClass( void )
- {
- Release_Cover_Position();
- }
- /*
- **
- */
- void SoldierObserverClass::Release_Cover_Position( void )
- {
- if ( CoverPosition != NULL ) {
- CoverManager::Release_Cover( CoverPosition );
- CoverPosition = NULL;
- }
- }
- /*
- **
- */
- SimplePersistFactoryClass<SoldierObserverClass, CHUNKID_SOLDIER_OBSERVER> _SoldierObserverPersistFactory;
- const PersistFactoryClass & SoldierObserverClass::Get_Factory (void) const
- {
- return _SoldierObserverPersistFactory;
- }
- enum {
- CHUNKID_PARENT = 410001836,
- CHUNKID_VARIABLES,
- CHUNKID_ENEMY_OBJ_REF,
- MICROCHUNKID_STATE = 1,
- MICROCHUNKID_STATE_TIMER,
- MICROCHUNKID_HOME_LOCATION,
- MICROCHUNKID_ALERT_POSITION,
- MICROCHUNKID_ACTION_TIMER,
- XXXMICROCHUNKID_ANIMATING,
- XXXMICROCHUNKID_MOVING,
- MICROCHUNKID_COVER_POSITION,
- MICROCHUNKID_COVERED_ATTACK,
- MICROCHUNKID_HOME_RADIUS,
- XXXMICROCHUNKID_ESCORT_OFFSET,
- MICROCHUNKID_CONVERSATION_TIMER,
- MICROCHUNKID_IS_ALERTED,
- MICROCHUNKID_AGGRESSIVENESS,
- MICROCHUNKID_TAKE_COVER_PROBABILITY,
- MICROCHUNKID_IS_STATIONARY,
- MICROCHUNKID_LAST_WEAPON_INDEX,
- };
- bool SoldierObserverClass::Save (ChunkSaveClass &csave)
- {
- csave.Begin_Chunk( CHUNKID_PARENT );
- PersistentGameObjObserverClass::Save( csave );
- csave.End_Chunk();
- if ( CombatManager::Are_Observers_Active() ) {
- csave.Begin_Chunk( CHUNKID_VARIABLES );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_TIMER, StateTimer );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HOME_LOCATION, HomeLocation );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HOME_RADIUS, HomeRadius );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ALERT_POSITION, AlertPosition );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ACTION_TIMER, ActionTimer );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_COVER_POSITION, CoverPosition );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_COVERED_ATTACK, CoveredAttack );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CONVERSATION_TIMER, ConversationTimer );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_ALERTED, IsAlerted );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AGGRESSIVENESS, Aggressiveness );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TAKE_COVER_PROBABILITY, TakeCoverProbability );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_STATIONARY, IsStationary );
- WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LAST_WEAPON_INDEX, LastWeaponIndex );
- csave.End_Chunk();
- if ( EnemyObject != NULL ) {
- csave.Begin_Chunk( CHUNKID_ENEMY_OBJ_REF );
- EnemyObject.Save( csave );
- csave.End_Chunk();
- }
- }
- // Don't need to save SubStateString, or LastEvent
- return true;
- }
- bool SoldierObserverClass::Load (ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk()) {
- switch(cload.Cur_Chunk_ID()) {
- case CHUNKID_PARENT:
- PersistentGameObjObserverClass::Load( cload );
- break;
-
- case CHUNKID_VARIABLES:
- while (cload.Open_Micro_Chunk()) {
- switch(cload.Cur_Micro_Chunk_ID()) {
- READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_TIMER, StateTimer );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_HOME_LOCATION, HomeLocation );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_HOME_RADIUS, HomeRadius );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_ALERT_POSITION, AlertPosition );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_ACTION_TIMER, ActionTimer );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_COVER_POSITION, CoverPosition );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_COVERED_ATTACK, CoveredAttack );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_CONVERSATION_TIMER, ConversationTimer );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_ALERTED, IsAlerted );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_AGGRESSIVENESS, Aggressiveness );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_TAKE_COVER_PROBABILITY, TakeCoverProbability );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_STATIONARY, IsStationary );
- READ_MICRO_CHUNK( cload, MICROCHUNKID_LAST_WEAPON_INDEX, LastWeaponIndex );
- default:
- Debug_Say(("Unhandled Variable Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
- break;
- }
- cload.Close_Micro_Chunk();
- }
- break;
- case CHUNKID_ENEMY_OBJ_REF:
- EnemyObject.Load( cload );
- break;
- default:
- Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
- break;
- }
- cload.Close_Chunk();
- }
- if ( CoverPosition ) {
- REQUEST_POINTER_REMAP( (void **)&CoverPosition );
- }
- return true;
- }
- /*
- **
- */
- void SoldierObserverClass::Attach( GameObject * obj )
- {
- // Warning, Attach may not be called on loaded scripts
- }
- void SoldierObserverClass::Detach( GameObject * obj )
- {
- //
- // Clear the soldier's internal innate observer pointer
- //
- SmartGameObj *smart_game_obj = obj->As_SmartGameObj();
- if ( smart_game_obj != NULL ) {
- SoldierGameObj *soldier = smart_game_obj->As_SoldierGameObj();
- if ( soldier != NULL ) {
- if ( soldier->Get_Innate_Observer () == this ) {
- soldier->Clear_Innate_Observer();
- }
- }
- }
- GameObjObserverManager::Delete_Register( this );
- }
- void SoldierObserverClass::Created( GameObject* obj )
- {
- SmartGameObj* smart = obj->As_SmartGameObj();
- WWASSERT( smart != NULL );
- // Debug_Say(("Innate soldier [%d] created\n", obj->Get_ID()));
- smart->Get_Position( &HomeLocation );
- // Enable enemy sensory
- smart->Set_Enemy_Seen_Enabled(true);
- obj->Start_Observer_Timer( Get_ID(), THINK_RATE, THINK_ID );
- Aggressiveness = smart->As_SoldierGameObj()->Get_Definition().InnateAggressiveness;
- // Modify aggressiveness based on difficulty
- switch ( CombatManager::Get_Difficulty_Level() ) {
- case 0:
- if ( Aggressiveness > 0.25f ) {
- Aggressiveness -= 0.25f;
- }
- break;
- case 1:
- // no change
- break;
- case 2:
- Aggressiveness += 0.25f;
- if ( Aggressiveness > 1 ) {
- Aggressiveness = 1;
- }
- break;
- };
- TakeCoverProbability = smart->As_SoldierGameObj()->Get_Definition().InnateTakeCoverProbability;
- IsStationary = smart->As_SoldierGameObj()->Get_Definition().InnateIsStationary;
- }
- void SoldierObserverClass::Destroyed(GameObject* obj)
- {
- // Debug_Say(("Innate soldier [%d] destroyed\n", obj->Get_ID()));
- Release_Cover_Position();
- }
- void SoldierObserverClass::Timer_Expired(GameObject* obj, int timer_id)
- {
- SmartGameObj* smart = obj->As_SmartGameObj();
- WWASSERT( smart != NULL );
- SoldierGameObj* soldier = smart->As_SoldierGameObj();
- WWASSERT(soldier != NULL);
- if (timer_id == THINK_ID) {
- obj->Start_Observer_Timer( Get_ID(), THINK_RATE, THINK_ID );
- Think(soldier);
- }
- }
- void SoldierObserverClass::Killed(GameObject* obj, GameObject* killer)
- {
- // Debug_Say(("Innate soldier [%d] killed by [%d]\n", obj->Get_ID(), killer ? killer->Get_ID() : -1));
- }
- void SoldierObserverClass::Damaged(GameObject* obj, GameObject* damager, float amount)
- {
- // Debug_Say(("Innate soldier [%d] damaged by [%d]\n", obj->Get_ID(), damager ? damager->Get_ID() : -1));
- if ( obj->Is_Hibernating() ) {
- return;
- }
- SmartGameObj* smart = obj->As_SmartGameObj();
- WWASSERT( smart != NULL );
- SoldierGameObj* soldier = smart->As_SoldierGameObj();
- WWASSERT(soldier != NULL);
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_EVENT_BULLET_HEARD)) {
- //Debug_Say(( "Damaged\n" ));
- Vector3 pos;
- soldier->Get_Position( &pos );
- if ( damager != NULL ) {
- if ( FreeRandom.Get_Float() < 0.8 ) { // 80% chance of knowing the source
- damager->Get_Position( &pos );
- }
- }
- Set_State(soldier, SOLDIER_AI_BULLET_HEARD, pos);
- }
- // If I am attacking an object, and I am damaged by a closer object, switch targets
- if ( State == SOLDIER_AI_ENEMY_SEEN ) {
- GameObject * enemy = EnemyObject;
- if ( damager != NULL && enemy != damager ) {
- Vector3 my_pos;
- soldier->Get_Position( &my_pos );
- Vector3 pos;
- float target_range = 1000000;
- if ( enemy ) {
- enemy->Get_Position( &pos );
- pos -= my_pos;
- target_range = pos.Length();
- }
- damager->Get_Position( &pos );
- pos -= my_pos;
- if ( pos.Length() < target_range ) {
- PhysicalGameObj * pdamager = damager->As_PhysicalGameObj();
- if ( pdamager && pdamager->Is_Enemy( soldier ) ) {
- Debug_Say(( "Switching to nearer target\n" ));
- EnemyObject = damager;
- ActionTimer = 0; // Act on it now
- }
- }
- }
- }
- }
- void SoldierObserverClass::Sound_Heard(GameObject* obj, const CombatSound& sound)
- {
- if ( obj->Is_Hibernating() ) {
- return;
- }
- SmartGameObj* smart = obj->As_SmartGameObj();
- WWASSERT( smart != NULL );
- SoldierGameObj* soldier = smart->As_SoldierGameObj();
- WWASSERT(soldier != NULL);
- PhysicalGameObj * creator = NULL;
- if ( sound.Creator != NULL ) {
- creator = sound.Creator->As_PhysicalGameObj();
- }
- bool state_changed = false;
- switch (sound.Type) {
- case SOUND_TYPE_BULLET_HIT:
- {
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_EVENT_BULLET_HEARD)) {
- Vector3 pos;
- pos = sound.Position;
- if ( creator && FreeRandom.Get_Float() < 0.7 ) { // 70% chance of knowing the source
- creator->Get_Position( &pos );
- }
- state_changed = Set_State(soldier, SOLDIER_AI_BULLET_HEARD, pos);
- }
- }
- break;
- case SOUND_TYPE_GUNSHOT:
- // Don't hear friendly
- if ( (creator == NULL) || !soldier->Is_Teammate( creator ) ) {
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_EVENT_GUNSHOT_HEARD)) {
- state_changed = Set_State(soldier, SOLDIER_AI_GUNSHOT_HEARD, sound.Position);
- }
- }
- break;
- case SOUND_TYPE_FOOTSTEPS:
- if ( (creator == NULL) || !soldier->Is_Teammate( creator ) ) {
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_EVENT_FOOTSTEP_HEARD)) {
- //Debug_Say(( "Heard Footsteps\n" ));
- state_changed = Set_State(soldier, SOLDIER_AI_FOOTSTEPS_HEARD, sound.Position);
- }
- }
- break;
- default:
- // Debug_Say(("Innate soldier [%d] unrecognized sound!\n", obj->Get_ID()));
- break;
- }
- if ( state_changed ) {
- Notify_Neighbors_Sound( soldier, sound );
- }
- }
- void SoldierObserverClass::Enemy_Seen(GameObject* obj, GameObject* enemy)
- {
- if ( obj->Is_Hibernating() ) {
- return;
- }
- // WWASSERT( obj != enemy );
- // only see the enemy if it has health
- WWASSERT( enemy );
- PhysicalGameObj * p_enemy = enemy->As_PhysicalGameObj();
- if ( p_enemy == NULL || p_enemy->Get_Defense_Object()->Get_Health() <= 0 ) {
- // Debug_Say(( "I see dead people\n" ));
- return;
- }
- GameObject * curr_enemy = EnemyObject;
- if ( curr_enemy != NULL && curr_enemy != enemy ) {
- // take the closer enemy
- Vector3 old_pos;
- curr_enemy->Get_Position( &old_pos );
- Vector3 new_pos;
- enemy->Get_Position( &new_pos );
- Vector3 my_pos;
- obj->Get_Position( &my_pos );
- old_pos -= my_pos;
- new_pos -= my_pos;
- if ( new_pos.Length2() > old_pos.Length2() ) {
- // Debug_Say(( "New guy is too far away!\n" ));
- return;
- }
- // Debug_Say(( "Found New guy!!\n" ));
- }
- SmartGameObj* smart = obj->As_SmartGameObj();
- WWASSERT( smart != NULL );
- SoldierGameObj* soldier = smart->As_SoldierGameObj();
- WWASSERT(soldier != NULL);
- // Debug_Say(("Innate soldier [%d] seen enemy [%d]\n", obj->Get_ID(), enemy->Get_ID()));
- bool state_changed = false;
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_EVENT_ENEMY_SEEN)) {
- //Debug_Say(( "Seen Enemy\n" ));
- state_changed = Set_State(soldier, SOLDIER_AI_ENEMY_SEEN, Vector3(0,0,0), enemy);
- }
- if ( state_changed ) {
- Notify_Neighbors_Enemy( soldier, enemy );
- }
- }
- void SoldierObserverClass::Custom( GameObject * obj, int type, int param, GameObject * sender )
- {
- if ( type == CUSTOM_EVENT_ATTACK_ARRIVED ) {
- // Debug_Say(( "Attack Arrvied event %d\n", param ));
- }
- }
- void SoldierObserverClass::Action_Complete( GameObject * obj, int action_id, ActionCompleteReason complete_reason )
- {
- if ( action_id == INNATE_ACTION_ID ) {
- SubStateString += "\nComplete";
- // ActionTimer = MIN( ActionTimer, 10 ); // When he completes, only stay in the action state for 5 more seconds
- // if I had an enemy target, who was just killed...
- PhysicalGameObj* enemy = (PhysicalGameObj*)EnemyObject.Get_Ptr();
- if ( enemy && State == SOLDIER_AI_ENEMY_SEEN ) {
- if ( enemy->Get_Defense_Object()->Get_Health() <= 0 ) {
- // Debug_Say(( "Target dead, moving on\n" ));
- StateTimer = 100000; // Leave current state
- EnemyObject = NULL;
- }
- }
- if ( complete_reason == ACTION_COMPLETE_NORMAL ||
- complete_reason == ACTION_COMPLETE_PATH_BAD_START ||
- complete_reason == ACTION_COMPLETE_PATH_BAD_DEST )
- {
- if ( complete_reason == ACTION_COMPLETE_PATH_BAD_DEST ) {
- // Pick a new action within 1 to 2 seconds
- ActionTimer = MIN( ActionTimer, WWMath::Random_Float( 1.0F, 2.0F ) );
- } else {
- // Pick a new action within 2 to 3 seconds
- ActionTimer = MIN( ActionTimer, WWMath::Random_Float( 2.0F, 3.0F ) );
- }
- if ( State > SOLDIER_AI_ALERT_IDLE ) {
- StateTimer = 100000; // Leave current state
- }
- }
- }
- }
- /*
- **
- */
- void SoldierObserverClass::State_Changed( SoldierGameObj * soldier )
- {
- switch ( State ) {
- case SOLDIER_AI_RELAXED_IDLE:
- IsAlerted = false;
- break;
- case SOLDIER_AI_FOOTSTEPS_HEARD:
- // Leave it was it was
- break;
- case SOLDIER_AI_ALERT_IDLE:
- case SOLDIER_AI_BULLET_HEARD:
- case SOLDIER_AI_GUNSHOT_HEARD:
- case SOLDIER_AI_ENEMY_SEEN:
- IsAlerted = true;
- break;
- }
- // Remeber the last selected weapon
- if ( soldier->Get_Weapon_Bag()->Get_Index() != 0 ) {
- LastWeaponIndex = soldier->Get_Weapon_Bag()->Get_Index();
- }
- if ( IsAlerted ) {
- soldier->Get_Weapon_Bag()->Select_Index( LastWeaponIndex );
- } else {
- soldier->Get_Weapon_Bag()->Select_Index( 0 );
- }
- if ( State == SOLDIER_AI_ALERT_IDLE ) {
- soldier->Get_Human_State()->Drop_Weapon();
- }
- ActionTimer = 0;
- }
- /*
- **
- */
- bool SoldierObserverClass::Set_State( SoldierGameObj * soldier, int state, const Vector3& location, GameObject* enemy)
- {
- // WWASSERT( soldier != enemy );
- LastEvent = state;
- int old_state = State;
- // If this is a more extreme state, act surprised!
- if (StatePriorities[State] < StatePriorities[state]) {
- // If we have seen the enemy then make a comment.
- if (state == SOLDIER_AI_ENEMY_SEEN) {
- // Speak(obj, Pick_Speak_EnemySeen());
- } else if (state != SOLDIER_AI_FOOTSTEPS_HEARD) {
- // Duck
- // Action_Play_Animation(obj, "s_a_human.h_a_j21c");
- // Speak(obj, "Q_Huh02");
- }
- // Debug_Say(("Switching to State %d from %d\n", state, State));
- AlertPosition = location;
- if ( State != state ) {
- State = state;
- State_Changed( soldier );
- }
- // Debug_Say(( "New Innate State!\n" ));
- }
- // If this is not a less extreme state, reset state timer
- if (StatePriorities[State] <= StatePriorities[state]) {
- if ( State != state ) {
- State = state;
- State_Changed( soldier );
- }
- StateTimer = 0;
- if ( ( state == SOLDIER_AI_BULLET_HEARD ) ||
- ( state == SOLDIER_AI_GUNSHOT_HEARD ) ) {
- if ( ActionTimer > 1 ) {
- ActionTimer = 1;
- }
- }
- // ActionTimer = 0; no
- #if 01
- // Only take the new pos if closer
- Vector3 my_pos;
- soldier->Get_Position(&my_pos);
- Vector3 old_pos = AlertPosition - my_pos;
- Vector3 new_pos = location - my_pos;
- if ( old_pos.Length2() > new_pos.Length2() )
- #endif
- {
- AlertPosition = location;
- }
- if ( enemy != NULL ) {
- EnemyObject = enemy->As_PhysicalGameObj();
- } else {
- EnemyObject = NULL;
- }
- Think(soldier, (State != old_state) );
- }
- //
- // Reset the conversation timer if the state has changed
- //
- if ( old_state != State ) {
- if ( State == SOLDIER_AI_ENEMY_SEEN ) {
- ConversationTimer = WWMath::Random_Float( 5.0F, 30.0F );
- } else {
- ConversationTimer = WWMath::Random_Float( 20.0F, 60.0F );
- }
- }
- //
- // Decide which (if any) dialogue the soldier should say
- //
- if ( ( old_state == SOLDIER_AI_RELAXED_IDLE ) || ( old_state == SOLDIER_AI_ALERT_IDLE ) ) {
-
- if ( State == SOLDIER_AI_FOOTSTEPS_HEARD ||
- State == SOLDIER_AI_BULLET_HEARD ||
- State == SOLDIER_AI_GUNSHOT_HEARD)
- {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_IDLE_TO_SEARCH );
- } else if ( State == SOLDIER_AI_ENEMY_SEEN ) {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_IDLE_TO_COMBAT );
- }
- } else if ( old_state == SOLDIER_AI_ENEMY_SEEN ) {
-
- if ( State == SOLDIER_AI_FOOTSTEPS_HEARD ||
- State == SOLDIER_AI_BULLET_HEARD ||
- State == SOLDIER_AI_GUNSHOT_HEARD)
- {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_COMBAT_TO_SEARCH );
- } else if ( ( old_state == SOLDIER_AI_RELAXED_IDLE ) || ( old_state == SOLDIER_AI_ALERT_IDLE ) ) {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_COMBAT_TO_IDLE );
- }
- } else {
- if ( State == SOLDIER_AI_ENEMY_SEEN ) {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_SEARCH_TO_COMBAT );
- } else if ( ( old_state == SOLDIER_AI_RELAXED_IDLE ) || ( old_state == SOLDIER_AI_ALERT_IDLE ) ) {
- soldier->Say_Dialogue( DIALOG_STATE_FROM_SEARCH_TO_IDLE );
- }
- }
- return (old_state != State);
- }
- void SoldierObserverClass::Notify_Neighbors_Sound( SoldierGameObj * soldier, const CombatSound & sound )
- {
- // Notify nearby neighbors of my knowledge
- // for all physicalgameobjs
- Vector3 my_pos;
- soldier->Get_Position( &my_pos );
- SLNode<SmartGameObj> *objnode;
- for ( objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
- SmartGameObj *obj = objnode->Data()->As_SmartGameObj();
- if ( obj ) {
- if ( obj == soldier ) continue;
- if ( obj->Get_Player_Type() != soldier->Get_Player_Type() ) continue;
- Vector3 obj_pos;
- obj->Get_Position( &obj_pos );
- obj_pos -= my_pos;
- if ( obj_pos.Length() <= 5 ) {
- // Notify him of my info
- const GameObjObserverList & observer_list = obj->Get_Observers();
- for( int index = 0; index < observer_list.Count(); index++ ) {
- observer_list[ index ]->Sound_Heard( obj, sound );
- }
- }
- }
- }
- }
- void SoldierObserverClass::Notify_Neighbors_Enemy( SoldierGameObj * soldier, GameObject * enemy )
- {
- // Notify nearby neighbors of my knowledge
- // for all physicalgameobjs
- Vector3 my_pos;
- soldier->Get_Position( &my_pos );
- SLNode<SmartGameObj> *objnode;
- for ( objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
- SmartGameObj *obj = objnode->Data()->As_SmartGameObj();
- if ( obj ) {
- if ( obj == soldier ) continue;
- if ( obj->Get_Player_Type() != soldier->Get_Player_Type() ) continue;
- Vector3 obj_pos;
- obj->Get_Position( &obj_pos );
- obj_pos -= my_pos;
- if ( obj_pos.Length() <= 5 ) {
- // Notify him of my info
- const GameObjObserverList & observer_list = obj->Get_Observers();
- for( int index = 0; index < observer_list.Count(); index++ ) {
- observer_list[ index ]->Enemy_Seen( obj, enemy );
- }
- }
- }
- }
- }
- void SoldierObserverClass::Poked( GameObject * obj, GameObject * poker )
- {
- if ( obj != NULL && obj->As_PhysicalGameObj() != NULL ) {
- PhysicalGameObj *physical_obj = obj->As_PhysicalGameObj();
- if ( physical_obj->As_SoldierGameObj() != NULL ) {
- bool face_poker = false;
-
- //
- // Get the soldier to say one of its pre-canned remarks
- //
- SoldierGameObj *soldier = physical_obj->As_SoldierGameObj();
- float duration = 2.0F;
- switch ( State )
- {
- case SOLDIER_AI_RELAXED_IDLE:
- case SOLDIER_AI_ALERT_IDLE:
- soldier->Say_Dialogue( DIALOG_ON_POKE_IDLE );
- face_poker = true;
- break;
- case SOLDIER_AI_FOOTSTEPS_HEARD:
- case SOLDIER_AI_BULLET_HEARD:
- case SOLDIER_AI_GUNSHOT_HEARD:
- soldier->Say_Dialogue( DIALOG_ON_POKE_SEARCH );
- face_poker = true;
- break;
- case SOLDIER_AI_ENEMY_SEEN:
- soldier->Say_Dialogue( DIALOG_ON_POKE_COMBAT );
- break;
- }
- //
- // Increment the duration by a small random amount
- //
- duration += WWMath::Random_Float( 0.5F, 2.0F );
- PhysicalGameObj *physical_poker_obj = poker->As_PhysicalGameObj();
- if ( physical_poker_obj != NULL ) {
- //
- // Get the position of the player who poked us
- //
- Vector3 poker_pos = physical_poker_obj->Get_Bullseye_Position();
- //
- // Make the soldier turn his body to face the speaker
- //
- if ( face_poker ) {
- ActionParamsStruct parameters;
- parameters.Priority = 20;
- parameters.ObserverID = Get_ID();
- parameters.Set_Face_Location( poker_pos, duration );
- soldier->Get_Action()->Face_Location( parameters );
- }
-
- //
- // Turn the soldier's head to look at the speaker
- //
- soldier->Look_At( poker_pos, duration );
- }
- }
- }
- return ;
- }
- void SoldierObserverClass::Think( SoldierGameObj * soldier, bool is_new_state )
- {
- WWPROFILE( "SoldierObserver Think" );
- WWASSERT( soldier != NULL );
- //
- // We can't switch states if the soldier is "busy". Busy usually
- // means he's going up an elevator, climbing a ladder, or opening a door.
- //
- if ( soldier->Get_Action()->Is_Busy() ) {
- return ;
- }
- // Check how long we have been in this state, and if we should switch
- StateTimer += 1;
- if (StateTimer >= StateData[State].StateDuration) {
- // Debug_Say(("Soldier relaxing from state %d to %d\n", State, StateData[State].NextState));
-
- Action_Reset( soldier ); // reset the old state
- State = StateData[State].NextState;
- if ( State == SOLDIER_AI_CONDITIONAL_IDLE ) {
- State = ( IsAlerted ) ? SOLDIER_AI_ALERT_IDLE : SOLDIER_AI_RELAXED_IDLE;
- }
- State_Changed( soldier );
- StateTimer = 0;
- }
- if ( ( State != SOLDIER_AI_RELAXED_IDLE ) && ( State != SOLDIER_AI_ALERT_IDLE ) ) {
- soldier->Reset_Loiter_Delay();
- }
- // Check if time for another state action
- if (soldier->Is_Innate_Enabled(SOLDIER_INNATE_ACTIONS)) {
- ActionTimer -= 1;
- if (ActionTimer <= 0) {
- State_Act(soldier, is_new_state);
- }
- }
- //
- // Allow this unit to start a conversation regardless of the state he's in
- //
- if ( soldier->Is_Human_Controlled () == false && soldier->Is_In_Conversation () == false ) {
- //
- // Is it time to start a new conversation?
- //
- ConversationTimer -= 1.0F;
- if (ConversationTimer <= 0) {
-
- //
- // Don't start an innate conversation if there are already
- // conversations taking place
- //
- #ifdef ENABLE_INNATE_CONVERSATIONS
- if ( ConversationMgrClass::Get_Active_Conversation_Count () == 0 ) {
- ConversationMgrClass::Start_Conversation( soldier );
- }
- #endif //ENABLE_INNATE_CONVERSATIONS
- Reset_Conversation_Timer();
- }
- }
- }
- void SoldierObserverClass::Get_Information( StringClass & string )
- {
- StringClass temp;
- string += "-----------\n";
- temp.Format( "State: %s\n", SoldierAIStateNames[ State ] );
- string += temp;
- temp.Format( "Sub: %s\n", SubStateString );
- string += temp;
- if ( IsStationary ) {
- string += "Stationary\n";
- }
- if ( State == SOLDIER_AI_ENEMY_SEEN ) {
- temp.Format( "Attacking: %d\n", EnemyObject.Get_Ptr() ? EnemyObject.Get_Ptr()->Get_ID() : 0 );
- string += temp;
- }
- temp.Format( "Last: %s\n", SoldierAIStateNames[ LastEvent ] );
- string += temp;
- temp.Format( "Act Time: %f\n", ActionTimer );
- string += temp;
- if ( CoverPosition != NULL ) {
- temp.Format( "Has Cover Pos\n" );
- string += temp;
- }
- }
- void SoldierObserverClass::Reset_Conversation_Timer( void )
- {
- ConversationTimer = WWMath::Random_Float( 20.0F, 60.0F );
- }
- /***********************************************************************************************
- ** Innate AI State Actions
- ***********************************************************************************************/
- void SoldierObserverClass::State_Act( SoldierGameObj * soldier, bool is_new_state )
- {
- WWASSERT( soldier != NULL );
- switch (State) {
- case SOLDIER_AI_RELAXED_IDLE:
- State_Act_Idle( soldier );
- break;
- case SOLDIER_AI_FOOTSTEPS_HEARD:
- State_Act_Footsteps_Heard( soldier );
- break;
- case SOLDIER_AI_ALERT_IDLE:
- case SOLDIER_AI_BULLET_HEARD:
- State_Act_Bullet_Heard( soldier, is_new_state );
- break;
- case SOLDIER_AI_GUNSHOT_HEARD:
- State_Act_Gunshot_Heard( soldier );
- break;
- case SOLDIER_AI_ENEMY_SEEN:
- State_Act_Attack( soldier );
- break;
- }
- }
- #define IDLE_WALK_DISTANCE 6
- #define IDLE_WALK_SPEED 0.3f // was 0.2f
- #define ALERTED_RUN_DISTANCE 6
- #define ALERTED_RUN_SPEED 0.9f
- #define ALERTED_RUN_RANGE 2
- /*
- **
- */
- void SoldierObserverClass::State_Act_Idle( SoldierGameObj * soldier )
- {
- ActionTimer = 5; // Will probably be overridden
- Vector3 position;
- soldier->Get_Position(&position);
- Release_Cover_Position();
- if ( FreeRandom.Get_Float() < 0.5f ) { // 50% chance of facing a random direction
- SubStateString = "Facing Random";
- position += Random_Vector(1);
- if ( COMBAT_STAR ) {
- SubStateString = "Facing Star";
- COMBAT_STAR->Get_Position( &position );
- }
- Action_Face_Location( soldier, position, AI_STATE_IDLE );
- ActionTimer = FreeRandom.Get_Float(4, 6); //Delay 4 to 6 seconds
- } else if ( !IsStationary ) { // 50% chance of walking to a random point
- SubStateString = "Random Walk";
- PathfindClass *pathfind = PathfindClass::Get_Instance(); // Lookup a safe random position to walk to
- if ( pathfind->Find_Random_Spot( position, IDLE_WALK_DISTANCE, &position ) ) {
- Action_Goto_Location( soldier, position, AI_STATE_IDLE, IDLE_WALK_SPEED, 0.3f );
- }
- ActionTimer = FreeRandom.Get_Float(7, 10); //Delay 7 to 10 seconds
- }
- Look_Random( soldier, ActionTimer );
- }
- /*
- **
- */
- void SoldierObserverClass::State_Act_Footsteps_Heard( SoldierGameObj * soldier )
- {
- ActionTimer = FreeRandom.Get_Float(3, 4); //Delay 7 to 10 seconds
- Release_Cover_Position();
- bool walking = false;
- if ( FreeRandom.Get_Float() < Aggressiveness && !IsStationary ) {
- SubStateString = "Walk To Footsteps";
- Vector3 position;
- PathfindClass *pathfind = PathfindClass::Get_Instance();
- if ( pathfind->Find_Random_Spot( AlertPosition, 2, &position ) ) {
- Action_Goto_Location( soldier, position, AI_STATE_IDLE, IDLE_WALK_SPEED, 0.5f );
- walking = true;
- }
- }
- if ( !walking ) {
- SubStateString = "Face Footsteps";
- Action_Face_Location( soldier, AlertPosition, AI_STATE_SEARCH );
- }
- Look_Random( soldier, 0 ); // No random looking!
- }
- /*
- **
- */
- void SoldierObserverClass::State_Act_Bullet_Heard( SoldierGameObj * soldier, bool is_new_state )
- {
- ActionTimer = FreeRandom.Get_Float(5,10);
- ActionTimer = FreeRandom.Get_Float(2,4);
- soldier->Get_Human_State()->Raise_Weapon(); // Keep the gun up
- Vector3 current_position;
- soldier->Get_Position(¤t_position);
- Vector3 walk_position;
- #define RANGE 10 // Dive with probability tied to bullet distance; 100% at 10m, 0 percent at 20m
- float range = (current_position - AlertPosition).Length();
- float percent = WWMath::Clamp( (1 - ((range-RANGE)/RANGE)), 0, 1 );
- bool dive = is_new_state && (FreeRandom.Get_Float() < percent) && !IsStationary;
- if ( dive ) {
- SubStateString = "Dive Away"; // Dive away
- ActionTimer = FreeRandom.Get_Float(2,4);
- Vector3 other_dir = 2*current_position - AlertPosition;
- Action_Dive( soldier, other_dir );
- } else {
- bool done = false;
- if ( !IsStationary ) {
- done = Take_Cover( soldier );
- }
- if ( !done ) {
- if ( FreeRandom.Get_Float() < TakeCoverProbability || IsStationary ) {
- SubStateString = "Face Random Crouched"; // Face and crouch
- walk_position = current_position + Random_Vector(1);
- if ( COMBAT_STAR ) {
- SubStateString = "Face Star Crouched";
- COMBAT_STAR->Get_Position( &walk_position );
- }
- Action_Face_Location( soldier, walk_position, AI_STATE_IDLE, true );
- } else {
- SubStateString = "Run To";
- // Lookup a safe random position to run to
- PathfindClass *pathfind = PathfindClass::Get_Instance();
- if ( pathfind->Find_Random_Spot( AlertPosition, ALERTED_RUN_DISTANCE, &walk_position ) ) {
- Action_Goto_Location( soldier, walk_position, AI_STATE_SEARCH, ALERTED_RUN_SPEED, ALERTED_RUN_RANGE, true );
- }
- }
- }
- }
- Look_Random( soldier, ActionTimer );
- }
- /*
- **
- */
- void SoldierObserverClass::State_Act_Gunshot_Heard( SoldierGameObj * soldier )
- {
- soldier->Get_Human_State()->Raise_Weapon(); // Keep the gun up
- Look_Random( soldier, 0 );
- ActionTimer = FreeRandom.Get_Float( 5, 10 );
- if ( !IsStationary ) {
- bool done = Take_Cover( soldier, true, AlertPosition );
- if ( !done ) {
- SubStateString = "Run To Random";
- PathfindClass *pathfind = PathfindClass::Get_Instance();
-
- //
- // Lookup a safe random position to run to
- //
- Vector3 position = AlertPosition;
- if (pathfind->Find_Random_Spot( AlertPosition, ALERTED_RUN_DISTANCE, &position )) {
- Action_Goto_Location_Facing(soldier, position, AI_STATE_SEARCH, AlertPosition, ALERTED_RUN_SPEED, ALERTED_RUN_RANGE);
- } else {
-
- //
- // Wasn't possible to run to a random location near the alert position,
- // so simply move around your local area a little bit and look at the alert position
- //
- Vector3 curr_pos;
- soldier->Get_Position (&curr_pos);
- if (pathfind->Find_Random_Spot( curr_pos, IDLE_WALK_DISTANCE, &position )) {
- Action_Goto_Location_Facing( soldier, position, AI_STATE_SEARCH, AlertPosition, IDLE_WALK_SPEED, 0.5F );
- }
- //Release_Cover_Position();
- //SubStateString = "Face gunshot";
- //Action_Face_Location( soldier, AlertPosition, AI_STATE_SEARCH );
- }
- }
- } else {
- Release_Cover_Position();
- SubStateString = "Face gunshot";
- Action_Face_Location( soldier, AlertPosition, AI_STATE_SEARCH );
- }
- }
- /*
- **
- */
- void SoldierObserverClass::State_Act_Attack( SoldierGameObj * soldier )
- {
- WWASSERT( soldier != NULL );
- soldier->Get_Human_State()->Raise_Weapon(); // Keep the gun up
- ActionTimer = FreeRandom.Get_Float( 5, 8 );
- Look_Random( soldier, 0 );
- #define RUN_SPEED 0.9f
- #define WALK_SPEED 0.2f
- bool done = false;
- // OK, here's the attack plan....
- // Periodically (every 5 seconds?), pick a new attack action. 4 Attack options are:
- // If in a attack point, jump back to the cover
- // If in a cover spot, possibily jump out and attack (goto attack point)
- // If there is a new cover spot available, goto it
- // Pick a random point and goto it
- PhysicalGameObj* enemy = (PhysicalGameObj*)EnemyObject.Get_Ptr();
- // WWASSERT( soldier != enemy );
- if ( enemy == NULL ) {
- StateTimer = 100000; // Leave this state
- return;
- }
- Vector3 enemy_pos;
- enemy->Get_Position(&enemy_pos);
- float weapon_range = 60.0f;
- if ( soldier->Get_Weapon() ) {
- weapon_range = soldier->Get_Weapon()->Get_Range();
- }
- Vector3 current_position;
- soldier->Get_Position(¤t_position);
- if ( CoverPosition != NULL ) {
- if ( CoveredAttack == true ) {
- SubStateString = "Return to Cover";
- Vector3 cover_position = CoverPosition->Get_Transform().Get_Translation();
- Action_Attack_Object( soldier, enemy, weapon_range, CoverPosition->Get_Crouch(), cover_position, 0.5f );
- CoveredAttack = false;
- ActionTimer = 5; // for 3 seconds
- done = true;
- } else if ( FreeRandom.Get_Float() < Aggressiveness ) {
- if ( soldier->Get_Weapon() != NULL ) {
- /*
- ** Start a covered attack.
- ** Pick an attack point, and go there, attacking at the target
- */
- SubStateString = "Covered Attack";
- Vector3 attack_point = CoverPosition->Get_Attack_Position( enemy_pos );
- Action_Attack_Object( soldier, enemy, weapon_range, false, attack_point, 0.5f );
- CoveredAttack = true;
- ActionTimer = 5; // for 3 seconds
- done = true;
- }
- }
- }
- if ( CoverPosition != NULL ) {
- if ( !CoverManager::Is_Cover_Safe( CoverPosition, enemy_pos ) ) {
- Release_Cover_Position();
- CoveredAttack = false;
- done = false;
- }
- }
- float effective_range = 20;
- if ( soldier->Get_Weapon() ) {
- effective_range = soldier->Get_Weapon()->Get_Effective_Range();
- }
- if ( !done && ( FreeRandom.Get_Float() < TakeCoverProbability ) && !IsStationary ) { // 25% chance of not finding cover
- Release_Cover_Position(); // Give us the option of selecting our current spot
- CoverEntryClass * cover = CoverManager::Request_Cover(current_position, enemy_pos, effective_range);
- if (cover != NULL) { // Yes, take it
- SubStateString = "Take Cover";
- CoverPosition = cover;
- Vector3 cover_position = cover->Get_Transform().Get_Translation();
- Action_Attack_Object( soldier, enemy, weapon_range, CoverPosition->Get_Crouch(), cover_position, 0.5f );
- ActionTimer = 15;
- done = true;
- }
- }
- if ( FreeRandom.Get_Float() < Aggressiveness && !IsStationary ) { // Charge Attack
- Vector3 pos;
- enemy->Get_Position( &pos );
- SubStateString = "Charge Attack";
- ActionTimer = 10;
- bool kneel = ( FreeRandom.Get_Float() < (1-Aggressiveness) );
- Action_Attack_Object( soldier, enemy, weapon_range, kneel, pos, 5 );
- done = true;
- }
- if ( !done ) {
- Vector3 best_pos = current_position;
- float best_distance = 100000;
- for (int i = 0; i < 3; i++) {
- // Lookup a safe random position to run to
- Vector3 pos = current_position;
- PathfindClass *pathfind = PathfindClass::Get_Instance();
- if ( pathfind->Find_Random_Spot( current_position, effective_range*2, &pos ) ) {
- float distance = (pos - enemy_pos).Length();
- if (WWMath::Fabs(distance - effective_range) < best_distance) {
- best_distance = WWMath::Fabs(distance - effective_range);
- best_pos = pos;
- }
- }
- }
- if ( IsStationary ) {
- best_pos = current_position;
- }
- SubStateString = "Random Run Attack";
- ActionTimer = 10;
- bool kneel = ( FreeRandom.Get_Float() < (1-Aggressiveness) );
- Action_Attack_Object( soldier, enemy, weapon_range, kneel, best_pos, 0.5f );
- }
- }
- /***********************************************************************************************
- ** Innate AI Actions
- ***********************************************************************************************/
- void SoldierObserverClass::Action_Reset( SoldierGameObj * soldier )
- {
- WWASSERT( soldier != NULL );
- soldier->Get_Action()->Reset( StatePriorities[ State ] );
- }
- void SoldierObserverClass::Action_Face_Location( SoldierGameObj * soldier, const Vector3 & location, SoldierAIState ai_state, bool crouched )
- {
- WWASSERT( soldier != NULL );
- ActionParamsStruct parameters;
- parameters.Set_Basic( Get_ID(), StatePriorities[ State ], INNATE_ACTION_ID, ai_state );
- parameters.Set_Face_Location( location, 2 );
- parameters.MoveCrouched = crouched;
- soldier->Get_Action()->Face_Location( parameters );
- }
- void SoldierObserverClass::Action_Goto_Location( SoldierGameObj * soldier, const Vector3 & location, SoldierAIState ai_state, float speed, float distance, bool crouched )
- {
- WWASSERT( soldier != NULL );
- Vector3 move_position = location;
- Stay_Within_Home( soldier, &move_position, &distance );
- ActionParamsStruct parameters;
- parameters.Set_Basic( Get_ID(), StatePriorities[ State ], INNATE_ACTION_ID, ai_state );
- parameters.Set_Movement( move_position, speed, distance, crouched );
- soldier->Get_Action()->Goto( parameters );
- }
- void SoldierObserverClass::Action_Goto_Location_Facing( SoldierGameObj * soldier, const Vector3 & location, SoldierAIState ai_state, const Vector3 & facing_pos, float speed, float distance, bool crouched )
- {
- WWASSERT( soldier != NULL );
- Vector3 move_position = location;
- Stay_Within_Home( soldier, &move_position, &distance );
- ActionParamsStruct parameters;
- parameters.Set_Basic( Get_ID(), StatePriorities[ State ], INNATE_ACTION_ID, ai_state );
- parameters.Set_Movement( move_position, speed, distance, crouched );
- parameters.ForceFacing = true;
- parameters.FaceLocation = facing_pos;
- soldier->Get_Action()->Goto( parameters );
- }
- void SoldierObserverClass::Action_Attack_Object( SoldierGameObj * soldier, PhysicalGameObj * enemy, float range,
- bool kneel, const Vector3 & move_location, float arrived_distance )
- {
- WWASSERT( soldier != NULL );
- Vector3 move_position = move_location;
- Stay_Within_Home( soldier, &move_position, &arrived_distance );
- if ( IsStationary ) { // Don't move if stationary
- arrived_distance = 10000;
- }
- if ( enemy != NULL && soldier->Get_Weapon() != NULL ) {
- ActionParamsStruct parameters;
- parameters.Set_Basic( Get_ID(), StatePriorities[ State ], INNATE_ACTION_ID, AI_STATE_COMBAT );
- parameters.Set_Movement( move_position, RUN_SPEED, arrived_distance, kneel );
- parameters.Set_Attack( enemy, range, FreeRandom.Get_Float(2), true );
- parameters.AttackCrouched = kneel;
- parameters.AttackWanderAllowed = !IsStationary;
- // 50% chance of looking where you are running, for a bit
- if ( FreeRandom.Get_Float() < 0.5 ) {
- parameters.LookLocation = move_position + Vector3( 0,0,1 );
- parameters.LookDuration = FreeRandom.Get_Float( 0.3f, 1 );
- }
- soldier->Get_Action()->Attack( parameters );
- }
- }
- void SoldierObserverClass::Action_Dive( SoldierGameObj * soldier, const Vector3 & location )
- {
- WWASSERT( soldier != NULL );
- ActionParamsStruct parameters;
- parameters.Set_Basic( Get_ID(), StatePriorities[ State ], INNATE_ACTION_ID, AI_STATE_SEARCH );
- parameters.MoveLocation = location;
- soldier->Get_Action()->Dive( parameters );
- }
- void SoldierObserverClass::Stay_Within_Home( SoldierGameObj * soldier, Vector3 * location, float * distance )
- {
- // Whenever we move, if we are outside our home radius, override the move to move back inside
- Vector3 home_diff;
- soldier->Get_Position( &home_diff );
- home_diff -= HomeLocation;
- home_diff.Z = 0; // don't consider Z
- if ( home_diff.Length() > HomeRadius ) {
- *location = HomeLocation;
- *distance = HomeRadius;
- }
- }
- bool SoldierObserverClass::Take_Cover( SoldierGameObj * soldier, bool force_face, const Vector3 & face_pos )
- {
- if ( CoverPosition != NULL ) {
- if ( !CoverManager::Is_Cover_Safe( CoverPosition, AlertPosition ) ) {
- Release_Cover_Position();
- }
- }
- // Possibily take a cover spot
- if ( FreeRandom.Get_Float() < TakeCoverProbability ) {
- Release_Cover_Position(); // Give us the option of selecting our current spot
- float cover_range = 20;
- Vector3 current_position;
- soldier->Get_Position(¤t_position);
- CoverEntryClass * cover = CoverManager::Request_Cover(current_position, AlertPosition, cover_range); // Can we find a cover spot?
- if (cover != NULL) { // Yes, take it
- SubStateString = "Take Cover";
- CoverPosition = cover;
- Vector3 cover_position = cover->Get_Transform().Get_Translation();
- if ( force_face ) {
- Action_Goto_Location_Facing(soldier, cover_position, AI_STATE_SEARCH, face_pos, ALERTED_RUN_SPEED, 0.5, cover->Get_Crouch() );
- } else {
- Action_Goto_Location(soldier, cover_position, AI_STATE_SEARCH, ALERTED_RUN_SPEED, 0.5, cover->Get_Crouch() );
- }
- return true;
- }
- }
- return false;
- }
- void SoldierObserverClass::Look_Random( SoldierGameObj * soldier, float duration )
- {
- soldier->Look_Random( duration );
- }
|