/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/Combat/action.cpp $*
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 3/19/02 11:31a $*
* *
* $Revision:: 266 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "action.h"
#include "input.h"
#include "persistfactory.h"
#include "combatchunkid.h"
#include "path.h"
#include "pathsolve.h"
#include "pathmgr.h"
#include "vehicledriver.h"
#include "pilot.h"
#include "soldier.h"
#include "vehicle.h"
#include "weapons.h"
#include "movephys.h"
#include "pathfind.h"
#include "pathfindportal.h"
#include "debug.h"
#include "animcontrol.h"
#include "conversationmgr.h"
#include "activeconversation.h"
#include "orator.h"
#include "soldierobserver.h"
#include "physcon.h"
#include "phys3.h"
#include "humanphys.h"
#include "pscene.h"
#include "staticphys.h"
#include "elevator.h"
#include "doors.h"
#include "pathaction.h"
#include "crandom.h"
#include "playertype.h"
#include "wwprofile.h"
#include "combat.h"
#include "waypath.h"
#include "waypoint.h"
#include "gameobjmanager.h"
#include "colmathaabox.h"
#include "dinput.h"
int _ActionActCalls = 0;
int _ActionCodeChanges = 0;
/*
**
*/
static bool Display_Findpaths = false;
void Toggle_Display_Findpaths( void ) { Display_Findpaths = !Display_Findpaths; }
/*
** ActionCodeClass
*/
class ActionCodeClass : public PersistClass {
public:
typedef enum {
ACTION_DONE,
ACTION_IN_PROGRESS,
} ActResult;
ActionCodeClass( void ) : Action( NULL ) {}
virtual ~ActionCodeClass( void ) {}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001350,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
PersistClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
PersistClass::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();
}
return true;
}
virtual void Init( ActionClass * action )
{
WWASSERT( action );
WWASSERT( Action == NULL );
Action = action;
//
// Check to see if we need to change the soldier's AI state
//
if ( Action->Get_Parameters().AIState != NO_AI_STATE_CHANGE ) {
SmartGameObj * obj = Action->Get_Action_Obj();
if ( obj != NULL ) {
//
// Check to ensure this is really a soldier
//
SoldierGameObj * soldier = obj->As_SoldierGameObj();
if ( soldier != NULL ) {
soldier->Set_AI_State( Action->Get_Parameters().AIState );
}
}
}
}
virtual void Modify_Parameters( const SafeActionParamsStruct & parameters, bool modify_move, bool modify_attack ) {}
virtual void Set_Action( ActionClass * action ) { WWASSERT( action ); WWASSERT( Action == NULL ); Action = action; }
virtual void Shutdown( void ) { Action = NULL; }
virtual ActResult Act( void ) { return ACTION_IN_PROGRESS; }
virtual bool Is_Animating( void ) { return false; }
virtual bool Is_Busy( void ) { return false; }
virtual void Begin_Hibernation( void ) {}
virtual void End_Hibernation( void ) {}
protected:
ActionClass * Action;
};
/*
**
*/
SimpleDynVecClass ActionDeleteList;
void Register_Action_Code_Deletion( ActionCodeClass * action )
{
ActionDeleteList.Add( action );
}
void Delete_Action_Code( void )
{
for ( int i = 0; i < ActionDeleteList.Count(); i++ ) {
delete ActionDeleteList[i];
}
// Clear vector but don't allow shrink.
ActionDeleteList.Delete_All(false);
}
/*
** Follow Input Action Code Class
*/
class FollowInputActionCodeClass;
SimplePersistFactoryClass _FollowInputActionCodeClassFactory;
class FollowInputActionCodeClass : public ActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _FollowInputActionCodeClassFactory; }
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001349,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
ActionCodeClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
ActionCodeClass::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();
}
return true;
}
virtual ActResult Act( void )
{
float amount;
SmartGameObj * obj = Action->Get_Action_Obj();
// If we're in a vehicle, use the input function VEHICLE_TURN_LEFT...
if ((obj->As_SoldierGameObj() != NULL) && (obj->As_SoldierGameObj()->Get_Vehicle() != NULL)) {
amount = Input::Get_Amount( INPUT_FUNCTION_VEHICLE_TURN_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_VEHICLE_TURN_RIGHT );
} else {
amount = Input::Get_Amount( INPUT_FUNCTION_TURN_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_TURN_RIGHT );
}
obj->Set_Analog_Control( ControlClass::ANALOG_TURN_LEFT, amount );
float forward_amount = Input::Get_Amount( INPUT_FUNCTION_MOVE_FORWARD ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_BACKWARD );
float left_amount = Input::Get_Amount( INPUT_FUNCTION_MOVE_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_RIGHT );
float up_amount = Input::Get_Amount( INPUT_FUNCTION_MOVE_UP ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_DOWN );
if ( obj->As_SoldierGameObj() && obj->As_SoldierGameObj()->Get_Vehicle() ) {
VehicleGameObj * vehicle = obj->As_SoldierGameObj()->Get_Vehicle();
// Convert turn to straffe
if ( vehicle->Is_Aircraft() && (Input::Is_Button_Down (DIK_LCONTROL) || Input::Is_Button_Down (DIK_LMENU) ) ) {
left_amount = amount;
obj->Set_Analog_Control( ControlClass::ANALOG_TURN_LEFT, 0 );
}
}
Vector3 move( forward_amount, left_amount, up_amount );
float length = move.Length();
if ( length > 1 ) {
forward_amount /= length;
left_amount /= length;
}
#if 0
if ( Input::Get_State( INPUT_FUNCTION_WALK_MODE ) ) {
forward_amount /= 4;
left_amount /= 4;
}
#else
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WALK, Input::Get_State( INPUT_FUNCTION_WALK_MODE ) );
#endif
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_FORWARD, forward_amount );
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_LEFT, left_amount );
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_UP, up_amount );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_JUMP, Input::Get_State( INPUT_FUNCTION_JUMP ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_VEHICLE_TOGGLE_GUNNER, Input::Get_State( INPUT_FUNCTION_VEHICLE_TOGGLE_GUNNER ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_NEXT, Input::Get_State( INPUT_FUNCTION_NEXT_WEAPON ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_PREV, Input::Get_State( INPUT_FUNCTION_PREV_WEAPON ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY, Input::Get_State( INPUT_FUNCTION_FIRE_WEAPON_PRIMARY ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY, Input::Get_State( INPUT_FUNCTION_FIRE_WEAPON_SECONDARY ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_USE, Input::Get_State( INPUT_FUNCTION_USE_WEAPON ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_RELOAD, Input::Get_State( INPUT_FUNCTION_RELOAD_WEAPON ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_DIVE_FORWARD, Input::Get_State( INPUT_FUNCTION_DIVE_FORWARD ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_DIVE_BACKWARD, Input::Get_State( INPUT_FUNCTION_DIVE_BACKWARD ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_DIVE_LEFT, Input::Get_State( INPUT_FUNCTION_DIVE_LEFT ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_DIVE_RIGHT, Input::Get_State( INPUT_FUNCTION_DIVE_RIGHT ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH, Input::Get_State( INPUT_FUNCTION_CROUCH ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_ACTION, Input::Get_State( INPUT_FUNCTION_ACTION ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_DROP_FLAG, Input::Get_State( INPUT_FUNCTION_DROP_FLAG ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_NO_WEAPON, Input::Get_State( INPUT_FUNCTION_SELECT_NO_WEAPON ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_0, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_0 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_1, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_1 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_2, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_2 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_3, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_3 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_4, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_4 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_5, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_5 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_6, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_6 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_7, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_7 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_8, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_8 ) );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_SELECT_WEAPON_9, Input::Get_State( INPUT_FUNCTION_SELECT_WEAPON_9 ) );
return ACTION_IN_PROGRESS;
}
};
/*
** Stand Action Code Class
*/
class StandActionCodeClass;
SimplePersistFactoryClass _StandActionCodeClassFactory;
class StandActionCodeClass : public ActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _StandActionCodeClassFactory; }
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001348,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
ActionCodeClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
ActionCodeClass::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();
}
return true;
}
virtual ActResult Act( void )
{
return ACTION_IN_PROGRESS;
}
};
/*
** Play Animation Action Code Class
*/
class PlayAnimationActionCodeClass;
SimplePersistFactoryClass _PlayAnimationActionCodeClassFactory;
class PlayAnimationActionCodeClass : public ActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _PlayAnimationActionCodeClassFactory; }
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001347,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
ActionCodeClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
ActionCodeClass::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();
}
return true;
}
virtual bool Is_Animating( void ) { return true; }
virtual void Init( ActionClass * action )
{
ActionCodeClass::Init( action );
SmartGameObj * obj = Action->Get_Action_Obj();
WWASSERT( obj->Get_Anim_Control() != NULL );
obj->Set_Animation( action->Get_Parameters().SafeAnimationName, action->Get_Parameters().AnimationLooping );
}
virtual void Shutdown( void )
{
SmartGameObj * obj = Action->Get_Action_Obj();
WWASSERT( obj->Get_Anim_Control() != NULL );
// WWASSERT( obj->Get_Anim_Control()->Get_Animation_Name()[0] != 0 );
obj->Set_Animation( NULL, false );
ActionCodeClass::Shutdown();
}
virtual ActResult Act( void )
{
SmartGameObj * obj = Action->Get_Action_Obj();
WWASSERT( obj->Get_Anim_Control() != NULL );
if ( obj->Get_Anim_Control()->Get_Animation_Name()[0] == 0 ) {
Debug_Say(( "Not playing an anim when we should be playing %s\n", Action->Get_Parameters().SafeAnimationName ));
Action->Done( ACTION_COMPLETE_NORMAL );
return ACTION_DONE;
}
// WWASSERT( obj->Get_Anim_Control()->Get_Animation_Name()[0] != 0 );
if ( obj->Get_Anim_Control()->Is_Complete() ) {
Action->Done( ACTION_COMPLETE_NORMAL );
return ACTION_DONE;
}
return ACTION_IN_PROGRESS;
}
};
/*
** Base Action Code Class
**
** This action will move and/or shoot context Object to the context Locations
** From the outside, you can set the moveto location, and you can set the
** shootAt location. The last one you set sets the moving flag, which
** is ignored if object can move and shoot independently.
** Need to know:
Can object move and fire independantly( if turreted || moves laterally )
Is Object Turreted ( gun can face a different direction than object, just tanks )
Can object move laterally (tanks and cars cannot, humans can)
Can object turn without moving ( cars cannot )
*/
class BaseActionCodeClass : public ActionCodeClass {
public:
BaseActionCodeClass( void )
{
IsMoving = false;
IsAttacking = false;
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001345,
CHUNKID_VARIABLES,
CHUNKID_VEHICLE_DRIVER,
CHUNKID_PILOT,
MICROCHUNKID_IS_ATTACKING = 1,
MICROCHUNKID_IS_MOVING = 2,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
ActionCodeClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_ATTACKING, IsAttacking );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_MOVING, IsMoving );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VEHICLE_DRIVER );
VehicleDriver.Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_PILOT );
Pilot.Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
ActionCodeClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_ATTACKING, IsAttacking );
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_MOVING, IsMoving );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_VEHICLE_DRIVER:
VehicleDriver.Load( cload );
break;
case CHUNKID_PILOT:
Pilot.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();
}
return true;
}
// Return a pointer to the object that will be doing the movement
SmartGameObj *Peek_Transport_Object( SmartGameObj *game_obj )
{
SmartGameObj *transport = game_obj;
if ( game_obj->As_SoldierGameObj() != NULL &&
game_obj->As_SoldierGameObj()->Get_Profile_Vehicle() != NULL ) {
transport = game_obj->As_SoldierGameObj()->Get_Profile_Vehicle();
}
return transport;
}
void Clamped_Units( Vector3& value, float rate )
{
// convert to "units", and clamp
rate *= TimeManager::Get_Frame_Seconds();
if ( rate > 0.0001f ) {
value /= rate;
}
if ( (rate <= 0.0001f) || value.Length() > 1.0 ) {
value.Normalize();
}
}
// Move Human object to given relative position
void Human_Move_To_Relative( Vector3 & orig_rel_pos )
{
//Debug_Say(( "Mov To Rel %f %f %f\n", rel_pos.X, rel_pos.Y, rel_pos.Z ));
SmartGameObj * obj = Action->Get_Action_Obj();
float speed = Action->Get_Parameters().MoveSpeed;
Vector3 move = orig_rel_pos;
//
// Zero out the relative distance in the z axis (if necessary)
//
SoldierGameObj * soldier = obj->As_SoldierGameObj();
if ( soldier == NULL || soldier->Is_On_Ladder() == false) {
move.Z = 0;
}
Clamped_Units( move, obj->Get_Max_Speed() * speed );
if ( move.Length2() > 1 ) {
move.Normalize();
}
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_FORWARD, move[0] * speed );
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_LEFT, move[1] * speed );
// Special case for soldier moving
if ( soldier && soldier->Is_On_Ladder() ) {
obj->Set_Analog_Control( ControlClass::ANALOG_MOVE_FORWARD, (move[2] >= 0) ? 1 : -1 ); // Could we make this move up?
}
}
// Move object to given relative position, return if complete
bool Move_To_Absolute( const Vector3 & abs_pos, bool ignore_arrived_dist = false )
{
SmartGameObj * obj = Action->Get_Action_Obj();
// Should the vehicle AI code take over?
SmartGameObj *transport_obj = Peek_Transport_Object( obj );
VehicleGameObj *vehicle = transport_obj->As_VehicleGameObj ();
if ( vehicle != NULL ) {
bool retval = false;
if (vehicle->Is_Aircraft ()) {
Pilot.Set_Speed_Factor( Action->Get_Parameters().MoveSpeed );
if ( Action->Get_Parameters().AttackObject != NULL ) {
Vector3 target_pos;
Action->Get_Parameters().AttackObject->Get_Position( &target_pos );
Pilot.Set_Target( &target_pos );
}
Pilot.Set_Speed_Factor( Action->Get_Parameters().MoveSpeed );
Pilot.Set_Arrived_Dist( Action->Get_Parameters().MoveArrivedDistance );
retval = Pilot.Think();
} else {
VehicleDriver.Set_Next_Position( abs_pos );
VehicleDriver.Set_Speed_Factor( Action->Get_Parameters().MoveSpeed );
VehicleDriver.Set_Arrived_Dist( Action->Get_Parameters().MoveArrivedDistance );
VehicleDriver.Force_Backup( Action->Get_Parameters().MoveBackup );
retval = VehicleDriver.Drive();
}
IsMoving = true;
return retval;
}
// If we want to be facing a particular position, set targeting to it.
if ( Action->Get_Parameters().ForceFacing ) {
obj->Set_Targeting( Action->Get_Parameters().FaceLocation, false ); // Don't tilt
}
Vector3 curr_pos;
obj->Get_Position( &curr_pos );
curr_pos -= abs_pos;
// I'm having soldiers get stuck because they are directly below
// the goal point. Lets try ignoring Z
curr_pos.Z = 0;
float range = curr_pos.Length();
SoldierGameObj *soldier = obj->As_SoldierGameObj();
bool done_facing = true; // Assume we are done facing
//
// If we are not attacking, and not force facing, and are a soldier and
// are not on a ladder and not very close
//
if ( !IsAttacking && !Action->Get_Parameters().ForceFacing &&
!Action->Get_Parameters().IgnoreFacing && soldier &&
!soldier->Is_On_Ladder() && (range > 0.1f ) )
{
// Then try to face the point we are moving to
Vector3 face_pt = abs_pos + Vector3( 0,0, obj->Get_Bullseye_Offset_Z() );
done_facing = obj->Set_Targeting( face_pt, false );
}
//
// Determine how close we want to get to the end-point before
// we stop moving
//
float arrived_dist = Action->Get_Parameters().MoveArrivedDistance;
if ( ignore_arrived_dist ) {
arrived_dist = 0.05F;
}
//
// If we are close enough
//
if ( (Get_Distance_From_Goal() + range) <= arrived_dist) {
IsMoving = false;
return done_facing; // complete;
}
IsMoving = true;
// Find the relitive location
Vector3 rel_pos;
Matrix3D::Inverse_Transform_Vector( obj->Get_Transform(), abs_pos, &rel_pos );
// we done facing, or facing close enough, move
float goal_bearing = WWMath::Atan2( rel_pos.Y, rel_pos.X );
if ( done_facing || WWMath::Fabs(goal_bearing) < DEG_TO_RAD( 40 ) ) {
Human_Move_To_Relative( rel_pos );
}
return false;
}
virtual float Get_Distance_From_Goal (void)
{
SmartGameObj * obj = Action->Get_Action_Obj();
//
// Get the object's current position
//
Vector3 curr_pos;
obj->Get_Position( &curr_pos );
//
// Find the difference between the current position and
// the destination
//
curr_pos -= Action->Get_Parameters().MoveLocation;
curr_pos.Z = 0;
//
// Return the length
//
return curr_pos.Length ();
}
protected:
bool IsAttacking;
bool IsMoving;
VehicleDriverClass VehicleDriver;
PilotClass Pilot;
};
/*
** Goto Action Code Class
*/
class GotoActionCodeClass;
SimplePersistFactoryClass _GotoActionCodeClassFactory;
class GotoActionCodeClass : public BaseActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _GotoActionCodeClassFactory; }
GotoActionCodeClass( void ) :
UseMoveObject( false ),
FirstCall( true ),
PathSolver( NULL ),
Path( NULL )
{
}
~GotoActionCodeClass( void )
{
return ;
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001308,
CHUNKID_VARIABLES,
CHUNKID_PATH,
CHUNKID_PATH_SOLVER,
CHUNKID_PATH_ACTION,
MICROCHUNKID_FIRST_CALL = 1,
MICROCHUNKID_USE_MOVE_OBJECT = 2,
MICROCHUNKID_TARGET_PREV_POS = 3,
MICROCHUNKID_PATH_SOLVE_PTR = 4,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
BaseActionCodeClass::Save( csave );
csave.End_Chunk();
if ( Path != NULL ) {
csave.Begin_Chunk( CHUNKID_PATH );
Path->Save( csave );
csave.End_Chunk();
}
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FIRST_CALL, FirstCall );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_USE_MOVE_OBJECT, UseMoveObject );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TARGET_PREV_POS, TargetPrevPos );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PATH_SOLVE_PTR, PathSolver );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_PATH_ACTION );
PathAction.Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
BaseActionCodeClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_FIRST_CALL, FirstCall );
READ_MICRO_CHUNK( cload, MICROCHUNKID_USE_MOVE_OBJECT, UseMoveObject );
READ_MICRO_CHUNK( cload, MICROCHUNKID_TARGET_PREV_POS, TargetPrevPos );
case MICROCHUNKID_PATH_SOLVE_PTR:
LOAD_MICRO_CHUNK( cload, PathSolver );
if (PathSolver != NULL) {
REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&PathSolver );
}
break;
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_PATH:
{
Path = new PathClass;
Path->Load( cload );
}
break;
case CHUNKID_PATH_ACTION:
PathAction.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();
}
return true;
}
virtual void Begin_Hibernation( void )
{
BaseActionCodeClass::Begin_Hibernation();
FirstCall = true;
//
// Return the path we got from the manager
//
if ( PathSolver != NULL ) {
PathMgrClass::Return_Path_Object( PathSolver );
PathSolver = NULL;
}
return ;
}
virtual void Init( ActionClass * action )
{
BaseActionCodeClass::Init( action );
FirstCall = true;
SoldierGameObj *soldier_obj = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( soldier_obj && Action->Get_Parameters().LookDuration != 0 ) {
soldier_obj->Look_At( Action->Get_Parameters().LookLocation, Action->Get_Parameters().LookDuration );
}
UseMoveObject = (Action->Get_Parameters().MoveObjectRef.Get_Ptr() != NULL);
}
virtual void Shutdown( void )
{
//
// Return the path we got from the manager
//
if (PathSolver != NULL) {
PathMgrClass::Return_Path_Object (PathSolver);
PathSolver = NULL;
}
Release_Path();
BaseActionCodeClass::Shutdown();
return ;
}
virtual void Init( WaypathClass *waypath )
{
//
// Return the path we got from the manager
//
if (PathSolver != NULL) {
PathMgrClass::Return_Path_Object (PathSolver);
PathSolver = NULL;
}
Release_Path();
Prepare_Path ();
Path->Set_Traversal_Type( PathClass::LINEAR );
Path->Initialize( waypath, -1, -1 );
Initialize_Vehicle_AI();
return ;
}
virtual bool Is_Busy( void )
{
return ( PathAction.Get_State () != PathActionClass::STATE_FINISHED );
}
virtual ActResult Arrived()
{
if ( Action->Get_Parameters().MoveFollow ) {
return ACTION_IN_PROGRESS; // Do nothing special when we arrive
}
if ( Action->Get_Action_Obj() && Action->Get_Parameters().ShutdownEngineOnArrival ) {
SmartGameObj *transport_obj = Peek_Transport_Object( Action->Get_Action_Obj() );
if ( transport_obj && transport_obj->As_VehicleGameObj() ) {
transport_obj->As_VehicleGameObj()->Enable_Engine (false);
}
}
Action->Done( ACTION_COMPLETE_NORMAL );
return ACTION_DONE; // We are done
}
virtual ActResult Beeline( void )
{
//
// Make sure we have a valid path object to follow
//
if ( Path == NULL ) {
Prepare_Path();
Vector3 curr_pos;
Action->Get_Action_Obj()->Get_Position( &curr_pos );
Path->Initialize( curr_pos, Action->Get_Parameters().MoveLocation );
Initialize_Vehicle_AI();
}
ActResult result = ACTION_IN_PROGRESS;
if ( Path != NULL ) {
result = Traverse_Path ();
}
// Tell the unit to beeline w/o using pathfind
/*ActResult result = ACTION_IN_PROGRESS;
if ( Move_To_Absolute( Action->Get_Parameters().MoveLocation ) ) {
result = Arrived();
}*/
return result;
}
void Prepare_Path( void )
{
//
// Allocate a path object (if necessary)
//
if ( Path == NULL ) {
Path = new PathClass;
}
//
// Setup the path traverser
//
PathObjectClass path_obj;
Configure_Path_Object( path_obj );
//
// Initialize the path
//
Path->Set_Path_Object( path_obj );
return ;
}
void Initialize_Vehicle_AI( void )
{
//
// Get the transport object
//
SmartGameObj *transport_obj = Peek_Transport_Object( Action->Get_Action_Obj() );
//
// Initialize the different driving/flying AI's
//
VehicleDriver.Initialize( transport_obj, Path );
VehicleDriver.Force_Backup( Action->Get_Parameters().MoveBackup );
Pilot.Initialize( transport_obj );
Pilot.Set_Mode( PilotClass::MODE_FLY_TO_POINT );
Pilot.Set_Current_Path ( Path );
Pilot.Set_Arrived_Dist( Action->Get_Parameters().MoveArrivedDistance );
Pilot.Set_Is_Exact_Z_Important( bool(Action->Get_Parameters().WaypathID != 0) );
//
// Check to make sure we aren't trying to drive a "simple" vehicle around
//
if ( transport_obj != NULL && transport_obj->As_VehicleGameObj () != NULL &&
transport_obj->Peek_Physical_Object () != NULL &&
transport_obj->Peek_Physical_Object ()->As_MoveablePhysClass () == NULL )
{
//
// Warn the user that they are trying to drive a non-driveable object
//
Debug_Say (("Warning! Attempting to drive a non-moveable vehicle. ID = %d.\n", transport_obj->Get_ID ()));
}
return ;
}
void Initialize_Waypath( void )
{
Prepare_Path ();
//
// Lookup the waypath object
//
WaypathClass *waypath = PathfindClass::Get_Instance()->Find_Waypath( Action->Get_Parameters().WaypathID );
if ( waypath == NULL ) {
Debug_Say (("Warning! Waypath %d does not exist.\n", Action->Get_Parameters().WaypathID));
}
WWASSERT( waypath != NULL );
//
// Initialize the path
//
Path->Set_Traversal_Type( Action->Get_Parameters().WaypathSplined ? PathClass::SPLINE : PathClass::LINEAR );
Path->Initialize( waypath, Action->Get_Parameters().WaypointStartID, Action->Get_Parameters().WaypointEndID );
return ;
}
void Configure_Path_Object( PathObjectClass &path_obj )
{
//
// Determine which game object will actually be doing the moving along the path
//
SmartGameObj *transport_obj = Peek_Transport_Object( Action->Get_Action_Obj() );
WWASSERT( transport_obj != NULL );
//
// Determine the turn radius
//
float turn_radius = 0;
VehicleGameObj *vehicle_obj = transport_obj->As_VehicleGameObj();
if ( vehicle_obj != NULL ) {
turn_radius = vehicle_obj->Get_Turn_Radius();
}
//
// Dig out the physics object that will be moving along the path
//
MoveablePhysClass *phys_obj = transport_obj->Peek_Physical_Object()->As_MoveablePhysClass();
if (phys_obj != NULL) {
//
// Configure the path-traverser object
//
path_obj.Initialize( *phys_obj );
path_obj.Set_Max_Speed( transport_obj->Get_Max_Speed () );
path_obj.Set_Turn_Radius( turn_radius );
path_obj.Set_Flag( PathObjectClass::IS_VEHICLE, bool(vehicle_obj != NULL) );
}
//
// Pass the key information onto the path object
//
SoldierGameObj *soldier = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( soldier != NULL ) {
path_obj.Set_Key_Ring( soldier->Get_Key_Ring () );
}
return ;
}
void Initialize_Path_Solver( void )
{
//
// Get a new path object from the manager (if necessary)
//
if (PathSolver == NULL) {
PathSolver = PathMgrClass::Request_Path_Object();
}
//
// Get the destination position
//
Vector3 dest_pos = Action->Get_Parameters().MoveLocation;
TargetPrevPos = Action->Get_Parameters().MoveLocation;
//
// Setup the path traverser
//
PathObjectClass path_obj;
Configure_Path_Object( path_obj );
//
// Initialize the path itself
//
Vector3 cur_pos;
Peek_Transport_Object (Action->Get_Action_Obj())->Get_Position( &cur_pos );
PathSolver->Set_Path_Object( path_obj );
PathSolver->Reset( cur_pos, dest_pos, 2.0F );
return ;
}
void Initialize_Path( void )
{
Release_Path();
//
// Determine if we should use a waypath instead of trying to solve
// the path dynamically
//
if ( Action->Get_Parameters().WaypathID != 0 ) {
Initialize_Waypath();
Initialize_Vehicle_AI();
} else {
Initialize_Path_Solver();
}
return ;
}
void Release_Path( void )
{
Pilot.Reset();
VehicleDriver.Reset();
REF_PTR_RELEASE( Path );
// Reset the PathAction class too - otherwise it keeps the pointer we game it to Path. ST - 9/6/2001 10:36AM
PathAction.Reset();
return ;
}
bool
Clip_Point (Vector3 *point, const AABoxClass &box)
{
Vector3 temp_point = *point;
//
// Clip the temporary point
//
temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);
//
// Did the clip change the point?
//
bool retval = (point->X != temp_point.X);
retval |= (point->Y != temp_point.Y);
retval |= (point->Z != temp_point.Z);
//
// Pass the new point back to the caller
//
(*point) = temp_point;
return retval;
}
virtual ActResult Traverse_Path ( void )
{
ActResult act_result = ACTION_IN_PROGRESS;
//
// Let the pathfind system know which directions we can (and are) moving
//
Path->Set_Movement_Directions( PathClass::MOVE_X | PathClass::MOVE_Y );
//
// Is the soldier only moving in Z?
//
SmartGameObj *obj = Action->Get_Action_Obj();
SoldierGameObj *soldier = obj->As_SoldierGameObj();
if( soldier != NULL ) {
if ( soldier->Is_On_Ladder() ) {
Path->Set_Movement_Directions( PathClass::MOVE_Z );
}
}
//
// Get the object's current position
//
Vector3 curr_pos;
obj->Get_Position( &curr_pos );
//
// Determine where the object should go next
//
Vector3 next_pos;
if( Path->Evaluate_Next_Point( curr_pos, next_pos ) ) {
/*return act_result;
}*/
//
// Allow debug display of the path
//
Path->Display_Path( Display_Findpaths );
//
// What should we be doing?
//
switch( Path->Get_State() )
{
case PathClass::STATE_PATH_COMPLETE:
case PathClass::STATE_TRAVERSING_PATH:
//
// Don't allow any movement if something is in our way
//
if ( soldier == NULL || soldier->Is_Soldier_Blocked( curr_pos ) == false ) {
if ( Move_To_Absolute( next_pos ) ) {
act_result = Arrived();
}
}
break;
case PathClass::STATE_ACTION_REQUIRED:
act_result = Perform_Action();
break;
/*case PathClass::STATE_PATH_COMPLETE:
act_result = Arrived();
break; */
}
} else {
//
// Error, free the path object
//
Release_Path();
act_result = ACTION_DONE;
Action->Done( ACTION_COMPLETE_MOVE_NO_PROGRESS_MADE );
}
return act_result;
}
virtual ActResult Perform_Action ( void )
{
SmartGameObj *obj = Action->Get_Action_Obj();
//
// Determine what action we need to perform
//
PathClass::ACTION_ID action_id = Path->Get_Action_Type();
switch( action_id )
{
case PathClass::ACTION_LADDER:
{
//
// Initialize the action object that will be responsible for
// controlling this action
//
PathAction.Initialize( PathActionClass::TYPE_LADDER, Path, obj, NULL );
PathAction.Set_Ladder_Index( Path->Get_Action_Mechanism() );
}
break;
case PathClass::ACTION_MECHANISM:
{
//
// Lookup the physics object we will be interacting with
//
uint32 mechanism_id = Path->Get_Action_Mechanism();
StaticPhysClass *mechanism = PhysicsSceneClass::Get_Instance ()->Find_Static_Object( mechanism_id );
if (mechanism != NULL) {
PathActionClass::TYPE type = PathActionClass::TYPE_UNKNOWN;
//
// Determine what type of mechanism this is
//
if( mechanism->As_ElevatorPhysClass () != NULL ) {
type = PathActionClass::TYPE_ELEVATOR;
} else if( mechanism->As_DoorPhysClass () != NULL ) {
type = PathActionClass::TYPE_DOOR;
}
//
// Initialize the action object that will be responsible for
// controlling this action
//
PathAction.Initialize( type, Path, obj, mechanism );
}
}
break;
case PathClass::ACTION_LEAP:
case PathClass::ACTION_JUMP:
{
//
// Get the point where we will be jumping to
//
Vector3 dest_pos;
Path->Get_Action_Destination( dest_pos );
//
// Instruct the physics object to jump to this point
//
HumanPhysClass *human_phys_obj = obj->Peek_Physical_Object()->As_HumanPhysClass ();
if( human_phys_obj != NULL ) {
human_phys_obj->Jump_To_Point( dest_pos );
}
//
// Initialize the action object that will be responsible for
// controlling this action
//
PathAction.Initialize( PathActionClass::TYPE_JUMPING, Path, obj, NULL );
}
break;
}
return ACTION_IN_PROGRESS;
}
virtual ActResult Process_Action ( void )
{
//
// Continue to let the action take place
//
PathAction.Process ();
//
// Does this action require us to move somewhere?
//
if( PathAction.Get_State () == PathActionClass::STATE_MOVING ) {
Move_To_Absolute( PathAction.Get_Destination (), true );
}
return ACTION_IN_PROGRESS;
}
virtual ActResult Solve_Path ( void )
{
ActResult act_result = ACTION_IN_PROGRESS;
//
// Evaluate the path
//
PathSolveClass::STATE_DESC result = PathSolver->Get_State();
//
// Has the path finished solving?
//
bool is_finished = true;
switch( result ) {
case PathSolveClass::THINKING:
is_finished = false;
break;
case PathSolveClass::SOLVED_PATH:
//
// Allocate and initialize the path object
//
Prepare_Path();
Initialize_Vehicle_AI();
//
// Configure the path object with information we learned
// during the path solve
//
Path->Set_Traversal_Type( PathClass::SPLINE );
Path->Initialize( *PathSolver );
break;
case PathSolveClass::ERROR_INVALID_START_POS:
case PathSolveClass::ERROR_INVALID_DEST_POS:
case PathSolveClass::ERROR_NO_PATH:
break;
}
//
// If we are finished solving the path, then release our
// hold on the path solver.
//
if ( is_finished ) {
PathMgrClass::Return_Path_Object( PathSolver );
PathSolver = NULL;
}
return act_result;
}
virtual ActResult Pathfind( void )
{
ActResult act_result = ACTION_IN_PROGRESS;
if( PathAction.Get_State () != PathActionClass::STATE_FINISHED ) {
//
// Keep performing the action until we've completed it
//
act_result = Process_Action();
} else {
if( PathSolver != NULL ) {
//
// Try to find a path to the destination
//
act_result = Solve_Path();
}
if( PathSolver == NULL) {
if( Path != NULL ) {
//
// Move along the path we found
//
act_result = Traverse_Path();
} else if ( PathSolver == NULL ){
//
// Unable to solve path...
//
// If this level doesn't have any pathfind data, then allow the unit
// to beeline -- otherwise, kill the action...
//
if (PathfindClass::Get_Instance ()->Does_Pathfind_Data_Exist ()) {
//
// Kill the action
//
Action->Done( ACTION_COMPLETE_PATH_BAD_DEST );
act_result = ACTION_DONE;
} else {
act_result = Beeline ();
}
}
}
}
return act_result;
}
virtual float Get_Distance_From_Goal (void)
{
float dist_to_goal = 0;
//
// Get the distance remaining from the path object (if possible)
//
if ( Path != NULL ) {
dist_to_goal = Path->Get_Remaining_Path_Length();
} else {
dist_to_goal = BaseActionCodeClass::Get_Distance_From_Goal();
}
//
// Return the length
//
return dist_to_goal;
}
void Update_Move_Location( void )
{
ScriptableGameObj *target = Action->Get_Parameters().MoveObjectRef.Get_Ptr();
if ( target == NULL ) { // If the target is gone
// Debug_Say(( "No Movement Target\n" ));
// Move_Target_Gone();
// Action->Reset( Action->Get_Parameters().Priority ); // Stop current Command (this may get this deleted!)
// Do we need to notify observers?
// return ACTION_DONE;
return;
}
// Action->Get_Parameters().MoveLocation = target->Get_Transform() * Action->Get_Parameters().MoveObjectOffset;
target->Get_Position( &(Action->Get_Parameters().MoveLocation) );
if ( FirstCall == false ) {
SmartGameObj * obj = Action->Get_Action_Obj();
SoldierGameObj * soldier = NULL;
if ( target->As_PhysicalGameObj() != NULL ) {
soldier = target->As_PhysicalGameObj()->As_SoldierGameObj();
}
// Get the current positions of the action object and the target object
Vector3 curr_pos;
Vector3 target_pos;
target->Get_Position( &target_pos );
obj->Get_Position( &curr_pos );
// Determine how far the target has moved since we last
// did a pathfind.
float target_move_dist = (target_pos - TargetPrevPos).Length ();
float to_target_dist = (curr_pos - target_pos).Length ();
if ( to_target_dist > 0 &&
(soldier == NULL || (soldier->Is_Airborne () == false && PathAction.Get_State () == PathActionClass::STATE_FINISHED)))
{
// Get the ratio of the distance the target has moved relative
// to the distance we are from the target.
float ratio = target_move_dist / to_target_dist;
if ((target_move_dist > 2.0F && ratio > 0.1F)) {
Initialize_Path();
}
}
}
}
virtual ActResult Act()
{
WWPROFILE( "Goto Act" );
SoldierGameObj * soldier = Action->Get_Action_Obj()->As_SoldierGameObj();
ScriptableGameObj * look_obj = Action->Get_Parameters().LookObjectRef.Get_Ptr();
if ( soldier != NULL && look_obj != NULL ) {
Vector3 pos;
look_obj->Get_Position( &pos );
pos.Z += 1; // Offset bullseye
soldier->Update_Look_At( pos );
}
if ( UseMoveObject ) {
Update_Move_Location();
}
if ( FirstCall ) {
Initialize_Path();
FirstCall = false;
}
#if 0
// Make the soldier crouch (if necessary)
if ( soldier && (soldier->Is_Crouched() != Action->Get_Parameters().MoveCrouched) ) {
soldier->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH );
}
#else
// Make the soldier crouch (if necessary)
if ( soldier ) {
soldier->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH, Action->Get_Parameters().MoveCrouched );
}
#endif
ActResult result = ACTION_IN_PROGRESS;
// Determine whether we should use pathfinding or
// simply beeline to the destination
if ( Action->Get_Parameters().MovePathfind ) {
result = Pathfind();
} else {
result = Beeline();
}
return result;
}
private:
PathClass * Path;
PathSolveClass * PathSolver;
PathActionClass PathAction;
bool FirstCall;
bool UseMoveObject;
Vector3 TargetPrevPos;
};
/*
** EnterExit Action Code Class
*/
class EnterExitActionCodeClass;
SimplePersistFactoryClass _EnterExitActionCodeClassFactory;
class EnterExitActionCodeClass : public BaseActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _EnterExitActionCodeClassFactory; }
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001312,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
BaseActionCodeClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
BaseActionCodeClass::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();
}
return true;
}
virtual ActResult Act( void )
{
SoldierGameObj * obj = Action->Get_Action_Obj()->As_SoldierGameObj();
WWASSERT( obj );
obj->Set_Boolean_Control( ControlClass::BOOLEAN_ACTION );
Action->Done( ACTION_COMPLETE_NORMAL );
return ACTION_DONE;
}
};
/*
** Dive Action Code Class
*/
class DiveActionCodeClass;
SimplePersistFactoryClass _DiveActionCodeClassFactory;
class DiveActionCodeClass : public BaseActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _DiveActionCodeClassFactory; }
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001310,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
BaseActionCodeClass::Save( csave );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
BaseActionCodeClass::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();
}
return true;
}
virtual ActResult Act( void )
{
SoldierGameObj * obj = Action->Get_Action_Obj()->As_SoldierGameObj();
WWASSERT( obj );
Vector3 rel_pos;
Matrix3D::Inverse_Transform_Vector( obj->Get_Transform(), Action->Get_Parameters().MoveLocation, &rel_pos );
if ( WWMath::Fabs( rel_pos.X ) > WWMath::Fabs( rel_pos.Y ) ) {
obj->Set_Boolean_Control( rel_pos.X > 0 ? ControlClass::BOOLEAN_DIVE_FORWARD : ControlClass::BOOLEAN_DIVE_BACKWARD );
} else {
obj->Set_Boolean_Control( rel_pos.Y > 0 ? ControlClass::BOOLEAN_DIVE_LEFT : ControlClass::BOOLEAN_DIVE_RIGHT );
}
#if 0
if ( !obj->Is_Crouched() ) {
obj->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH );
}
#else
obj->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH );
#endif
Action->Done( ACTION_COMPLETE_NORMAL );
return ACTION_DONE;
}
};
/*
** Attack - Location
*/
class AttackActionCodeClass;
SimplePersistFactoryClass _AttackActionCodeClassFactory;
class AttackActionCodeClass : public GotoActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _AttackActionCodeClassFactory; }
AttackActionCodeClass( void ) :
UseAttackObject( false ),
WanderPos(0,0,0),
WanderTimer(0),
HasArrived( false ),
HoldPrimaryTriggerTimer( 0 )
{
ErrorScale = FreeRandom.Get_Float( 0.75f, 1.25f );
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001308,
CHUNKID_VARIABLES,
MICROCHUNKID_USE_ATTACK_OBJECT = 1,
MICROCHUNKID_IS_BLOCKED = 2,
MICROCHUNKID_CHECK_BLOCKED_TIMER = 3,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
GotoActionCodeClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_USE_ATTACK_OBJECT, UseAttackObject );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_BLOCKED, IsBlocked );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CHECK_BLOCKED_TIMER, CheckBlockedTimer );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
GotoActionCodeClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_USE_ATTACK_OBJECT, UseAttackObject );
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_BLOCKED, IsBlocked );
READ_MICRO_CHUNK( cload, MICROCHUNKID_CHECK_BLOCKED_TIMER, CheckBlockedTimer );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
virtual void Init( ActionClass * action )
{
GotoActionCodeClass::Init( action );
UseAttackObject = (Action->Get_Parameters().AttackObjectRef.Get_Ptr() != NULL);
IsBlocked = action->Get_Parameters().AttackCheckBlocked;
CheckBlockedTimer = 0;
WanderPos = Vector3(0,0,0);
}
virtual ActResult Arrived()
{
if ( !HasArrived ) {
// Debug_Say(( "Attack Arrived %d\n", HasArrived ));
HasArrived = true;
SoldierGameObj * obj = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( obj != NULL ) {
int observer_id = Action->Get_Parameters ().ObserverID;
if ( observer_id != 0 ) {
const GameObjObserverList & observer_list = obj->Get_Observers();
for( int index = 0; index < observer_list.Count(); index++ ) {
if ( observer_list[ index ]->Get_ID() == observer_id ) {
observer_list[ index ]->Custom( obj, CUSTOM_EVENT_ATTACK_ARRIVED, Action->Get_Parameters().ActionID, NULL );
}
}
}
}
}
return ACTION_IN_PROGRESS; // Do nothing special when we arrive
}
// Attack the given absolute position, return nothing
void Attack_Absolute( Vector3 & abs_pos )
{
WWPROFILE( "Attack Absolute" );
IsAttacking = true;
float range = Action->Get_Parameters().AttackRange;
SmartGameObj * obj = Action->Get_Action_Obj();
Vector3 my_pos;
obj->Get_Position( &my_pos );
// move the abs_pos around a little for AttackError
Vector3 attack_pos = abs_pos;
Matrix3D error;
error.Obj_Look_At( my_pos, abs_pos, 0 );
float dist = (my_pos - abs_pos).Length();
float move = TimeManager::Get_Total_Seconds() * ErrorScale;
float error_angle = WWMath::Fabs(Action->Get_Parameters().AttackError) +
WWMath::Fabs(obj->Get_Weapon_Error());
#if 0
// Modify error angle based on difficulty
switch ( CombatManager::Get_Difficulty_Level() ) {
case 0:
error_angle += 20;
break;
case 1:
// no change
break;
case 2:
error_angle -= 20;
break;
};
if ( error_angle < 0 ) {
error_angle = 0;
}
#endif
// If AttackErrorOverride, only use AttackError
if ( Action->Get_Parameters().AttackErrorOverride ) {
error_angle = WWMath::Fabs(Action->Get_Parameters().AttackError);
}
error.Rotate_Z( DEG_TO_RADF( WWMath::Sin( move * 1.7f ) * error_angle ) );
error.Rotate_Y( DEG_TO_RADF( WWMath::Cos( move ) * error_angle ) * 0.5f );
attack_pos = error * Vector3( dist,0,0 );
if ( obj->Set_Targeting( attack_pos ) == false && !Action->Get_Parameters().AttackForceFire ) {
// we are not facing it yet, don't fire
return;
}
WeaponClass * weapon = obj->Get_Weapon();
WWASSERT( weapon );
if ( Action->Get_Parameters().AttackCheckBlocked ) {
WWPROFILE( "Check Blocked" );
CheckBlockedTimer -= TimeManager::Get_Frame_Seconds();
if ( CheckBlockedTimer <= 0 ) {
CheckBlockedTimer = FreeRandom.Get_Float( 2, 3 ); // Re check every 2 - 3 sec
// cast a ray from muzzle to target
PhysicalGameObj * hit_obj = weapon->Cast_Weapon( abs_pos );
if ( hit_obj != NULL ) {
if ( UseAttackObject ) {
PhysicalGameObj * target = (PhysicalGameObj *)Action->Get_Parameters().AttackObjectRef.Get_Ptr();
IsBlocked = ( hit_obj != target );
if ( IsBlocked && target->As_SoldierGameObj() && target->As_SoldierGameObj()->Get_Vehicle() == hit_obj ) {
IsBlocked = false;
}
} else {
IsBlocked = !hit_obj->Is_Enemy( obj );
}
} else {
IsBlocked = true;
}
}
}
if ( IsBlocked && !Action->Get_Parameters().AttackForceFire ) {
return;
}
// Update Weapons Dynamic Error
if ( weapon ) {
if ( Action->Get_Parameters().AttackCheckBlocked != false ) {
range = MIN( range, weapon->Get_Range() ); // dont fire if not in range
}
}
my_pos -= abs_pos;
my_pos.Z = 0;
if ( my_pos.Length() < range || Action->Get_Parameters().AttackForceFire ) {
if ( Action->Get_Parameters().AttackPrimaryFire ) {
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY );
// Keep the trigger down (helps obelisk)
float rate = 0;
if ( obj->Get_Weapon() ) {
rate = obj->Get_Weapon()->Get_Primary_Fire_Rate();
}
if ( rate != 0 ) {
HoldPrimaryTriggerTimer = (1 / rate) * 0.75f;
}
} else {
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY );
}
}
}
virtual void Modify_Parameters( const SafeActionParamsStruct & parameters, bool modify_move, bool modify_attack )
{
//
// Copy the parameters that are safe to change in the middle of an action
//
if ( modify_attack ) {
Action->Get_Parameters().AttackLocation = parameters.AttackLocation;
Action->Get_Parameters().AttackObjectRef = parameters.AttackObjectRef;
Action->Get_Parameters().AttackCrouched = parameters.AttackCrouched;
Action->Get_Parameters().AttackRange = parameters.AttackRange;
Action->Get_Parameters().AttackCheckBlocked = parameters.AttackCheckBlocked;
Action->Get_Parameters().AttackError = parameters.AttackError;
Action->Get_Parameters().AttackErrorOverride = parameters.AttackErrorOverride;
Action->Get_Parameters().AttackPrimaryFire = parameters.AttackPrimaryFire;
Action->Get_Parameters().AttackActive = parameters.AttackActive;
Action->Get_Parameters().AttackWanderAllowed = parameters.AttackWanderAllowed;
Action->Get_Parameters().AttackFaceTarget = parameters.AttackFaceTarget;
Action->Get_Parameters().AttackForceFire = parameters.AttackForceFire;
}
if ( modify_move ) {
Action->Get_Parameters().MoveObject = parameters.MoveObject;
Action->Get_Parameters().MoveObjectOffset = parameters.MoveObjectOffset;
Action->Get_Parameters().MoveSpeed = parameters.MoveSpeed;
Action->Get_Parameters().MoveArrivedDistance = parameters.MoveArrivedDistance;
Action->Get_Parameters().MoveBackup = parameters.MoveBackup;
Action->Get_Parameters().MoveFollow = parameters.MoveFollow;
Action->Get_Parameters().MoveCrouched = parameters.MoveCrouched;
Action->Get_Parameters().ShutdownEngineOnArrival = parameters.ShutdownEngineOnArrival;
Action->Get_Parameters().MovePathfind = parameters.MovePathfind;
}
//
// Reset some of the internal flags
//
UseAttackObject = (parameters.AttackObjectRef.Get_Ptr() != NULL);
IsBlocked = false;
CheckBlockedTimer = 0;
return ;
}
virtual ActResult Act( void )
{
WWPROFILE( "Attack Act" );
//
// Do the movement (if necessary)
//
if ( Action->Get_Parameters().MoveArrivedDistance < DONT_MOVE_ARRIVED_DIST ) {
GotoActionCodeClass::Act();
}
ActResult result = ACTION_IN_PROGRESS;
//
// Only "do" the attack if the attack state is currently active
//
if ( Action != NULL && Action->Get_Parameters().AttackActive ) {
Vector3 set_target;
result = Process_Attack(&set_target);
if ( Action != NULL && Action->Get_Parameters().AttackFaceTarget) {
Pilot.Set_Target (&set_target);
}
} else {
Pilot.Set_Target (NULL);
}
return result;
}
ActResult Process_Attack( Vector3 * set_target )
{
SmartGameObj * obj = Action->Get_Action_Obj();
if ( HoldPrimaryTriggerTimer > 0 ) {
HoldPrimaryTriggerTimer -= TimeManager::Get_Frame_Seconds();
obj->Set_Boolean_Control( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY );
}
Vector3 target_pos = Action->Get_Parameters().AttackLocation;
WeaponClass * weapon = obj->Get_Weapon();
if ( weapon == NULL ) {
Action->Done( ACTION_COMPLETE_NORMAL ); // Stop current Command (this may get this deleted!)
return ACTION_DONE;
}
if ( UseAttackObject ) {
WWPROFILE( "Attack Object" );
// See if our target still exists
PhysicalGameObj * target = (PhysicalGameObj *)Action->Get_Parameters().AttackObjectRef.Get_Ptr();
if ( target ) {
if ( target->Get_Defense_Object()->Get_Health() <= 0 ) {
// Debug_Say(( "Attack target dead\n" ));
target = NULL;
}
}
if ( target == NULL ) { // If the target is gone or dead
static int count = 0;
if (count++ < 20) {
// Debug_Say(( "No Attack Target\n" ));
}
Action->Done( ACTION_COMPLETE_NORMAL ); // Stop current Command (this may get this deleted!)
return ACTION_DONE;
}
// Goto our target's location
target_pos = target->Get_Bullseye_Position();
if ( target->As_SoldierGameObj() != NULL && target->As_SoldierGameObj()->Peek_Human_Phys() != NULL ) {
Vector3 vel;
target->As_SoldierGameObj()->Peek_Human_Phys()->Get_Velocity( &vel );
vel *= TimeManager::Get_Frame_Seconds();
target_pos += vel;
}
}
// If not moving, use attack crouch
if ( !IsMoving ) {
SoldierGameObj * soldier = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( soldier != NULL ) {
#if 0
if ( soldier->Is_Crouched() != Action->Get_Parameters().AttackCrouched ) {
soldier->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH );
}
#else
soldier->Set_Boolean_Control( ControlClass::BOOLEAN_CROUCH, Action->Get_Parameters().AttackCrouched );
#endif
}
}
Attack_Absolute( target_pos );
#if 0
// Elie Wander!
// Always wander if attacking!
WanderTimer -= TimeManager::Get_Frame_Seconds();
SoldierGameObj * soldier = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( !IsMoving && WanderTimer <= 0 && soldier != NULL && Action->Get_Parameters().AttackWanderAllowed ) {
WWPROFILE( "Wander" );
Vector3 rel_pos;
Matrix3D::Inverse_Transform_Vector( obj->Get_Transform(), WanderPos, &rel_pos );
// Debug_Say(( "Wander Pos %f %f %f\n", WanderPos.X, WanderPos.Y, WanderPos.Z ));
// Debug_Say(( "Rel Pos %f %f %f\n", rel_pos.X, rel_pos.Y, rel_pos.Z ));
// If he is near the wander position, or very far from it, pick a new wander pos
float dist = rel_pos.Length();
if ( dist < 0.5 || dist > 8 ) {
WanderTimer = FreeRandom.Get_Float( 0.4f, 1.2f ); // random pause
// If near the move loc, wander away, else wander back
Vector3 dif = WanderPos - Action->Get_Parameters().MoveLocation;
WanderPos = Action->Get_Parameters().MoveLocation;
if ( dif.Length() < 2 ) {
#if 1
PathfindClass *pathfind = PathfindClass::Get_Instance(); // Lookup a safe random position to walk to
pathfind->Find_Random_Spot( Action->Get_Parameters().MoveLocation, 5, &WanderPos );
#else
float angle = FreeRandom.Get_Float( 0, DEG_TO_RADF( 360.0f ) );
Vector3 move( ::sinf(angle), ::cosf(angle), 0 );
// Debug_Say(( " Move to %f %f %f\n", move.X, move.Y, move.Z ));
WanderPos += move * FreeRandom.Get_Float( 3, 5 );
#endif
soldier->Look_At( WanderPos + Vector3( 0,0,1 ), FreeRandom.Get_Float( 0.2f, 0.6f ) );
}
}
Human_Move_To_Relative( rel_pos );
}
#endif
*set_target = target_pos;
return ACTION_IN_PROGRESS;
}
bool UseAttackObject;
bool IsBlocked;
float CheckBlockedTimer;
bool HasArrived;
float ErrorScale;
float HoldPrimaryTriggerTimer;
Vector3 WanderPos;
float WanderTimer;
};
/*
** Face - Location
*/
class FaceLocationActionCodeClass;
SimplePersistFactoryClass _FaceLocationActionCodeClassFactory;
class FaceLocationActionCodeClass : public BaseActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _FaceLocationActionCodeClassFactory; }
FaceLocationActionCodeClass( void ) :
EndTime( 0 )
{
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001305,
CHUNKID_VARIABLES,
MICROCHUNKID_END_TIME = 1,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
BaseActionCodeClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_END_TIME, EndTime );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
BaseActionCodeClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_END_TIME, EndTime );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
virtual void Init( ActionClass * action )
{
BaseActionCodeClass::Init( action );
SoldierGameObj *soldier_obj = Action->Get_Action_Obj()->As_SoldierGameObj();
if ( soldier_obj && Action->Get_Parameters().LookDuration != 0 ) {
soldier_obj->Look_At( Action->Get_Parameters().LookLocation, Action->Get_Parameters().LookDuration );
}
EndTime = TimeManager::Get_Seconds() +
MAX(action->Get_Parameters().FaceDuration, Action->Get_Parameters().LookDuration);
}
virtual ActResult Act( void )
{
BaseActionCodeClass::Act();
// Look at the moving object
SoldierGameObj * soldier = Action->Get_Action_Obj()->As_SoldierGameObj();
ScriptableGameObj * look_obj = Action->Get_Parameters().LookObjectRef.Get_Ptr();
if ( soldier != NULL && look_obj != NULL ) {
Vector3 pos;
look_obj->Get_Position( &pos );
pos.Z += 1; // Offset bullseye
soldier->Update_Look_At( pos );
}
if ( Action->Get_Parameters().FaceDuration != 0 ) {
//
// Make the object target (face for humans) the given location
//
SmartGameObj * obj = Action->Get_Action_Obj();
Vector3 face_location = Action->Get_Parameters().FaceLocation;
obj->Set_Targeting( face_location, false );
}
//
// Have we timed out yet?
//
ActResult result = ACTION_IN_PROGRESS;
if ( EndTime < TimeManager::Get_Seconds() ) {
//
// Stop this action. Note: The following call may cause the
// object to be deleted...
//
Action->Done( ACTION_COMPLETE_NORMAL );
result = ACTION_DONE;
}
return result;
}
float EndTime;
};
/*
** Have a conversation
*/
class ConversationActionCodeClass;
SimplePersistFactoryClass _ConversationActionCodeClassFactory;
class ConversationActionCodeClass : public GotoActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _ConversationActionCodeClassFactory; }
enum
{
STATE_WALKING_TO_START_POS = 0,
STATE_HAVING_CONVERSATION,
STATE_FINISHED
};
ConversationActionCodeClass( void ) :
Conversation( NULL ),
State( STATE_WALKING_TO_START_POS ),
OriginalPos( 0, 0, 0 )
{
}
~ConversationActionCodeClass( void )
{
return ;
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001258,
CHUNKID_VARIABLES,
MICROCHUNKID_STATE = 1,
MICROCHUNKID_ORIGINAL_POS = 2,
MICROCHUNKID_CONVERSATION_PTR = 3,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
GotoActionCodeClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ORIGINAL_POS, OriginalPos );
//
// Only save the conversation ptr if it exists in the manager
//
if (Conversation != NULL) {
ConversationClass *conv = ConversationMgrClass::Find_Conversation (Conversation->Get_ID ());
if (conv != NULL) {
//
// Save the conversation pointer
//
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CONVERSATION_PTR, Conversation );
REF_PTR_RELEASE( conv );
}
}
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
GotoActionCodeClass::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_ORIGINAL_POS, OriginalPos );
case MICROCHUNKID_CONVERSATION_PTR:
LOAD_MICRO_CHUNK( cload, Conversation );
//
// Fixup the pointer
//
if (Conversation != NULL) {
REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&Conversation );
}
break;
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
virtual void Begin_Hibernation( void )
{
//
// Stop this action. Note: The following call may cause the
// object to be deleted...
//
if ( Action != NULL ) {
Action->Done( ACTION_COMPLETE_NORMAL );
}
return ;
}
virtual void Init( ActionClass * action )
{
State = STATE_WALKING_TO_START_POS;
REF_PTR_RELEASE( Conversation );
OriginalPos.Set( 0, 0, 0 );
SmartGameObj *obj = action->Get_Action_Obj();
if ( obj != NULL ) {
SoldierGameObj *soldier = obj->As_SoldierGameObj ();
if( soldier != NULL ) {
//
// Should we join an already active conversation or should
// we start our own conversation?
//
if ( action->Get_Parameters().ActiveConversationID != 0 ) {
Conversation = ConversationMgrClass::Find_Active_Conversation( action->Get_Parameters().ActiveConversationID );
} else {
//
// Start a new conversation
//
Conversation = ConversationMgrClass::Start_Conversation( soldier, action->Get_Parameters().SafeConversationName );
}
//
// By default our new position is our current position
//
soldier->Get_Position( &OriginalPos );
if ( Conversation != NULL ) {
OratorClass *orator_info = Conversation->Get_Orator_Information( soldier );
//
// Determine if the soldier needs to move to a new location
//
if (orator_info->Get_Flag (OratorClass::FLAG_DONT_MOVE) == false) {
//
// Get the soldier's expected position
//
Vector3 new_pos = OriginalPos;
Conversation->Get_Orator_Location( soldier, &new_pos );
//
// Set our movement location
//
action->Get_Parameters().MoveLocation = new_pos;
action->Get_Parameters().MoveSpeed = 0.15F;
action->Get_Parameters().MoveArrivedDistance = 0.25F;
} else {
State = STATE_HAVING_CONVERSATION;
}
}
}
}
GotoActionCodeClass::Init( action );
return ;
}
virtual void Shutdown( void )
{
//
// Release our hold on the conversation (but don't terminate it)
//
if ( Conversation != NULL ) {
//Conversation->Stop_Conversation ();
REF_PTR_RELEASE( Conversation );
}
GotoActionCodeClass::Shutdown();
return ;
}
virtual ActResult Act( void )
{
SmartGameObj *obj = Action->Get_Action_Obj();
if ( obj != NULL ) {
SoldierGameObj *soldier = obj->As_SoldierGameObj ();
if( soldier != NULL ) {
if( State == STATE_WALKING_TO_START_POS ) {
GotoActionCodeClass::Act();
} else if ( State == STATE_HAVING_CONVERSATION ) {
//
// Check to see if the conversation has finished yet
//
if ( Conversation == NULL || Conversation->Is_Finished () ) {
State ++;
//
// Tell this soldier to return to his original position
//
ActionParamsStruct parameters;
parameters.Priority = Action->Get_Parameters().Priority;
parameters.ObserverID = Action->Get_Parameters().ObserverID;
parameters.MoveLocation = OriginalPos;
parameters.MoveSpeed = 0.15F;
parameters.MoveArrivedDistance = 1.0F;
soldier->Get_Action()->Goto (parameters);
}
}
}
}
//
// Have we timed out yet?
//
ActResult result = ACTION_IN_PROGRESS;
if ( Conversation == NULL || Conversation->Is_Finished () ) {
//
// Stop this action. Note: The following call may cause the
// object to be deleted...
//
if ( Action != NULL ) {
Action->Done( ACTION_COMPLETE_NORMAL );
}
result = ACTION_DONE;
}
return result;
}
virtual ActResult Arrived (void)
{
if ( Conversation != NULL && State == STATE_WALKING_TO_START_POS ) {
//
// Let the conversation object know this soldier has arrived
//
SmartGameObj *obj = Action->Get_Action_Obj();
if ( obj != NULL ) {
SoldierGameObj *soldier = obj->As_SoldierGameObj();
if( soldier != NULL ) {
OratorClass *orator_info = Conversation->Get_Orator_Information( soldier );
if ( orator_info != NULL ) {
orator_info->Set_Flag( OratorClass::FLAG_TEMP_DONT_FACE, false );
}
Conversation->Set_Orator_Arrived( soldier, true );
}
}
State ++;
}
return ACTION_IN_PROGRESS;
}
private:
ActiveConversationClass * Conversation;
int State;
Vector3 OriginalPos;
};
/*
** Dock vehicle (backup) into a bay
*/
class DockActionCodeClass;
SimplePersistFactoryClass _DockActionCodeClassFactory;
class DockActionCodeClass : public GotoActionCodeClass {
public:
virtual const PersistFactoryClass & Get_Factory( void ) const { return _DockActionCodeClassFactory; }
enum
{
STATE_DRIVING_TO_ENTRANCE = 0,
STATE_DRIVING_TO_SIDE,
STATE_BACKING_INTO_BAY
};
DockActionCodeClass( void ) :
State( STATE_DRIVING_TO_ENTRANCE )
{
}
~DockActionCodeClass( void )
{
return ;
}
/*
** Save / Load
*/
enum {
CHUNKID_PARENT = 1031001258,
CHUNKID_VARIABLES,
MICROCHUNKID_STATE = 1,
};
virtual bool Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
GotoActionCodeClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
csave.End_Chunk();
return true;
}
virtual bool Load( ChunkLoadClass & cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
GotoActionCodeClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
virtual void Init( ActionClass * action )
{
State = STATE_DRIVING_TO_ENTRANCE;
//
// Skip the "driving to side" state if we're a tracked vehicle which
// can turn in place.
//
SmartGameObj *obj = action->Get_Action_Obj();
VehicleGameObj *vehicle = obj->As_VehicleGameObj();
if ( vehicle != NULL && vehicle->Get_Turn_Radius() == 0 ) {
State = STATE_DRIVING_TO_SIDE;
}
//
// Setup the action structure so the vehicle will drive
// to the entrance of the docking bay
//
action->Get_Parameters().MoveLocation = action->Get_Parameters().DockEntrance;
action->Get_Parameters().MovePathfind = true;
action->Get_Parameters().MoveBackup = false;
OriginalMoveSpeed = action->Get_Parameters().MoveSpeed;
//
// Ensure that the arrived distance is valid
//
if (action->Get_Parameters().MoveArrivedDistance == DONT_MOVE_ARRIVED_DIST) {
action->Get_Parameters().MoveArrivedDistance = 1.0F;
}
GotoActionCodeClass::Init( action );
return ;
}
virtual ActResult Arrived (void)
{
SafeActionParamsStruct ¶ms = Action->Get_Parameters();
ActResult result = ACTION_IN_PROGRESS;
//
// Determine which action we just finished
//
if ( State == STATE_DRIVING_TO_ENTRANCE ) {
//
// Terminate the action, we have arrived successfully
//
SmartGameObj *obj = Action->Get_Action_Obj();
VehicleGameObj *vehicle = obj->As_VehicleGameObj();
//
// Get the vector which points the direction the vehicle is
// currently facing
//
const Matrix3D &tm = obj->Get_Transform();
Vector3 facing_vector;
tm.Get_X_Vector( &facing_vector );
//
// Build a vector which points the way into the docking bay
//
Vector3 entrance_vector = params.DockEntrance - params.DockLocation;
entrance_vector.Normalize();
//
// Lookup the vehicle's turn radius
//
float turn_radius = 2.0F;
if ( vehicle != NULL ) {
turn_radius = vehicle->Get_Turn_Radius();
}
if (turn_radius == 0) {
//
// Instruct the object to drive to the side of the docking bay
//
ActionClass *action = Action;
action->Get_Parameters().MoveLocation = params.DockEntrance;
action->Get_Parameters().MovePathfind = false;
action->Get_Parameters().MoveBackup = true;
action->Get_Parameters().MoveSpeed = OriginalMoveSpeed * 0.25F;
GotoActionCodeClass::Init( action );
} else {
WaypathClass *test_waypath = new WaypathClass;
Vector3 side_pos = params.DockLocation + (params.DockEntrance - params.DockLocation) * 0.8F;
//
// Determine which side we should drive to in order to be oriented
// correctly for backing up
//
float dot_product = Vector3::Dot_Product (facing_vector, entrance_vector);
if (dot_product > 0) {
Vector3 vector1 = entrance_vector;
Vector3 vector2 = Vector3 (0, 0, 1.0F);
Vector3 side_vector = Vector3::Cross_Product (vector1, vector2);
test_waypath->Add_Point (side_pos + (side_vector * (turn_radius * 0.5F)));
side_pos += (side_vector * (turn_radius * 1.4F));
test_waypath->Add_Point (side_pos);
} else {
Vector3 vector1 = entrance_vector;
Vector3 vector2 = Vector3 (0, 0, 1.0F);
Vector3 side_vector = Vector3::Cross_Product (vector1, vector2);
test_waypath->Add_Point (side_pos - (side_vector * (turn_radius * 0.5F)));
side_pos -= (side_vector * (turn_radius * 1.4F));
test_waypath->Add_Point (side_pos);
}
//
// Instruct the object to drive to the side of the docking bay
//
ActionClass *action = Action;
action->Get_Parameters().MoveLocation = side_pos;
action->Get_Parameters().MovePathfind = true;
action->Get_Parameters().MoveBackup = false;
action->Get_Parameters().MoveSpeed = OriginalMoveSpeed * 0.25F;
GotoActionCodeClass::Init( test_waypath );
//
// Free our temporary waypath
//
REF_PTR_RELEASE( test_waypath );
}
//
// Move onto the next state
//
State ++;
} else if ( State == STATE_DRIVING_TO_SIDE ) {
WaypathClass *test_waypath = new WaypathClass;
SmartGameObj *obj = Action->Get_Action_Obj();
VehicleGameObj *vehicle = obj->As_VehicleGameObj();
float turn_radius = 2.0F;
if ( vehicle != NULL ) {
turn_radius = vehicle->Get_Turn_Radius();
}
Vector3 entrance_vector = params.DockEntrance - params.DockLocation;
entrance_vector.Normalize();
Vector3 vector1 = entrance_vector;
Vector3 vector2 = Vector3 (0, 0, 1.0F);
Vector3 side_vector = Vector3::Cross_Product (vector1, vector2);
test_waypath->Add_Point (params.DockLocation + (params.DockEntrance - params.DockLocation) * 0.7F - (side_vector * turn_radius * 0.6F));
test_waypath->Add_Point (params.DockLocation + (params.DockEntrance - params.DockLocation) * 0.3F);
test_waypath->Add_Point (params.DockLocation);
//
// Instruct the object to back into the docking bay
//
ActionClass *action = Action;
action->Get_Parameters().MoveLocation = params.DockLocation;
action->Get_Parameters().MovePathfind = true;
action->Get_Parameters().MoveBackup = true;
action->Get_Parameters().MoveSpeed = OriginalMoveSpeed * 0.1F;
GotoActionCodeClass::Init( test_waypath );
//
// Move onto the next state
//
State ++;
//
// Free our temporary waypath
//
REF_PTR_RELEASE( test_waypath );
//
// Let the observer know that the vehicle will be
// backing into the bay
//
int observer_id = Action->Get_Parameters ().ObserverID;
if ( observer_id != 0 ) {
const GameObjObserverList & observer_list = obj->Get_Observers();
for( int index = 0; index < observer_list.Count(); index++ ) {
if ( observer_list[ index ]->Get_ID() == observer_id ) {
observer_list[ index ]->Custom( obj, CUSTOM_EVENT_DOCK_BACKING_IN, 0, NULL );
}
}
}
} else if ( State == STATE_BACKING_INTO_BAY ) {
//
// Terminate the action, we have arrived successfully
//
State ++;
Action->Done( ACTION_COMPLETE_NORMAL );
result = ACTION_DONE;
}
return result;
}
private:
int State;
float OriginalMoveSpeed;
};
/*
**
*/
SafeActionParamsStruct & SafeActionParamsStruct::operator = (const ActionParamsStruct & src)
{
Priority = src.Priority;
ActionID = src.ActionID;
ObserverID = src.ObserverID;
LookLocation = src.LookLocation;
LookObjectRef = src.LookObject;
LookDuration = src.LookDuration;
MoveLocation = src.MoveLocation;
MoveObjectRef = src.MoveObject;
MoveObjectOffset = src.MoveObjectOffset;
MoveSpeed = src.MoveSpeed;
MoveArrivedDistance = src.MoveArrivedDistance;
MoveBackup = src.MoveBackup;
MoveFollow = src.MoveFollow;
MoveCrouched = src.MoveCrouched;
ShutdownEngineOnArrival = src.ShutdownEngineOnArrival;
MovePathfind = src.MovePathfind;
AttackRange = src.AttackRange;
AttackError = src.AttackError;
AttackErrorOverride = src.AttackErrorOverride;
AttackPrimaryFire = src.AttackPrimaryFire;
AttackCrouched = src.AttackCrouched;
AttackObjectRef = src.AttackObject;
AttackLocation = src.AttackLocation;
AttackCheckBlocked = src.AttackCheckBlocked;
AttackActive = src.AttackActive;
AttackWanderAllowed = src.AttackWanderAllowed;
AttackFaceTarget = src.AttackFaceTarget;
AttackForceFire = src.AttackForceFire;
WaypathID = src.WaypathID;
WaypointStartID = src.WaypointStartID;
WaypointEndID = src.WaypointEndID;
WaypathSplined = src.WaypathSplined;
AnimationLooping = src.AnimationLooping;
ForceFacing = src.ForceFacing;
IgnoreFacing = src.IgnoreFacing;
FaceLocation = src.FaceLocation;
FaceDuration = src.FaceDuration;
SafeConversationName = src.ConversationName;
ActiveConversationID = src.ActiveConversationID;
SafeAnimationName = src.AnimationName;
AIState = src.AIState;
DockLocation = src.DockLocation;
DockEntrance = src.DockEntrance;
return *this;
}
enum {
CHUNKID_VARIABLES = 712001855,
CHUNKID_MOVE_OBJECT,
CHUNKID_ATTACK_OBJECT,
CHUNKID_LOOK_OBJECT,
MICROCHUNKID_PRIORITY = 1,
MICROCHUNKID_ACTION_ID,
MICROCHUNKID_OBSERVER_ID,
MICROCHUNKID_MOVE_LOCATION,
MICROCHUNKID_MOVE_OBJECT_OFFSET,
MICROCHUNKID_MOVE_SPEED,
MICROCHUNKID_MOVE_ARRIVED_DISTANCE,
MICROCHUNKID_MOVE_BACKUP,
MICROCHUNKID_MOVE_FOLLOW,
MICROCHUNKID_MOVE_CROUCHED,
MICROCHUNKID_MOVE_PATHFIND,
MICROCHUNKID_ATTACK_RANGE,
MICROCHUNKID_ATTACK_ERROR,
MICROCHUNKID_ATTACK_PRIMARY_FIRE,
MICROCHUNKID_ATTACK_CROUCHED,
MICROCHUNKID_ATTACK_LOCATION,
MICROCHUNKID_WAYPATH_ID,
MICROCHUNKID_WAYPOINT_START_ID,
MICROCHUNKID_WAYPOINT_END_ID,
MICROCHUNKID_WAYPATH_SPLINED,
MICROCHUNKID_ANIMATION_NAME,
MICROCHUNKID_ANIMATION_LOOPING,
MICROCHUNKID_FACE_LOCATION,
MICROCHUNKID_FACE_DURATION,
MICROCHUNKID_CONVERSATION_NAME,
MICROCHUNKID_ACTIVE_CONVERSATION_ID,
MICROCHUNKID_LOOK_LOCATION,
MICROCHUNKID_LOOK_DURATION,
MICROCHUNKID_DISABLE_HIBERNATION,
MICROCHUNKID_ATTACK_CHECK_BLOCKED,
MICROCHUNKID_ATTACK_ACTIVE,
MICROCHUNKID_ATTACK_WANDER_ALLOWED,
MICROCHUNKID_FORCE_FACING,
MICROCHUNKID_ATTACK_ERROR_OVERRIDE,
MICROCHUNKID_ATTACK_FACE_TARGET,
MICROCHUNKID_IGNORE_FACING,
MICROCHUNKID_ATTACK_FORCE_FIRE,
MICROCHUNKID_SHUTDOWN_ENGINE_ON_ARRIVAL,
};
bool SafeActionParamsStruct::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PRIORITY, Priority );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ACTION_ID, ActionID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OBSERVER_ID, ObserverID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_LOCATION, LookLocation );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_DURATION, LookDuration );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_LOCATION, MoveLocation );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_OBJECT_OFFSET, MoveObjectOffset );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_SPEED, MoveSpeed );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_ARRIVED_DISTANCE, MoveArrivedDistance );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_BACKUP, MoveBackup );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_FOLLOW, MoveFollow );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_CROUCHED, MoveCrouched );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SHUTDOWN_ENGINE_ON_ARRIVAL, ShutdownEngineOnArrival );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MOVE_PATHFIND, MovePathfind );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_RANGE, AttackRange );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_ERROR, AttackError );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_ERROR_OVERRIDE, AttackErrorOverride );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_PRIMARY_FIRE, AttackPrimaryFire );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_CROUCHED, AttackCrouched );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_LOCATION, AttackLocation );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_ACTIVE, AttackActive );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_CHECK_BLOCKED, AttackCheckBlocked );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_WANDER_ALLOWED, AttackWanderAllowed );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_FACE_TARGET, AttackFaceTarget );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ATTACK_FORCE_FIRE, AttackForceFire );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WAYPATH_ID, WaypathID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WAYPOINT_START_ID, WaypointStartID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WAYPOINT_END_ID, WaypointEndID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WAYPATH_SPLINED, WaypathSplined );
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_ANIMATION_NAME,SafeAnimationName );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ANIMATION_LOOPING, AnimationLooping );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FORCE_FACING, ForceFacing );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IGNORE_FACING, IgnoreFacing );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FACE_LOCATION, FaceLocation );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FACE_DURATION, FaceDuration );
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_CONVERSATION_NAME, SafeConversationName );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ACTIVE_CONVERSATION_ID, ActiveConversationID );
csave.End_Chunk();
if ( MoveObjectRef != NULL ) {
csave.Begin_Chunk( CHUNKID_MOVE_OBJECT );
MoveObjectRef.Save( csave );
csave.End_Chunk();
}
if ( AttackObjectRef != NULL ) {
csave.Begin_Chunk( CHUNKID_ATTACK_OBJECT );
AttackObjectRef.Save( csave );
csave.End_Chunk();
}
if ( LookObjectRef != NULL ) {
csave.Begin_Chunk( CHUNKID_LOOK_OBJECT );
LookObjectRef.Save( csave );
csave.End_Chunk();
}
return true;
}
bool SafeActionParamsStruct::Load( ChunkLoadClass &cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_PRIORITY, Priority );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ACTION_ID, ActionID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_OBSERVER_ID, ObserverID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_LOCATION, LookLocation );
READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_DURATION, LookDuration );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_LOCATION, MoveLocation );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_OBJECT_OFFSET, MoveObjectOffset );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_SPEED, MoveSpeed );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_ARRIVED_DISTANCE,MoveArrivedDistance );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_BACKUP, MoveBackup );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_FOLLOW, MoveFollow );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_CROUCHED, MoveCrouched );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SHUTDOWN_ENGINE_ON_ARRIVAL, ShutdownEngineOnArrival );
READ_MICRO_CHUNK( cload, MICROCHUNKID_MOVE_PATHFIND, MovePathfind );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_RANGE, AttackRange );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_ERROR, AttackError );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_ERROR_OVERRIDE,AttackErrorOverride );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_PRIMARY_FIRE, AttackPrimaryFire );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_CROUCHED, AttackCrouched );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_LOCATION, AttackLocation );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_ACTIVE, AttackActive );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_CHECK_BLOCKED, AttackCheckBlocked );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_WANDER_ALLOWED, AttackWanderAllowed );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_FACE_TARGET, AttackFaceTarget );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ATTACK_FORCE_FIRE, AttackForceFire );
READ_MICRO_CHUNK( cload, MICROCHUNKID_WAYPATH_ID, WaypathID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_WAYPOINT_START_ID, WaypointStartID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_WAYPOINT_END_ID, WaypointEndID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_WAYPATH_SPLINED, WaypathSplined );
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_ANIMATION_NAME,SafeAnimationName );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ANIMATION_LOOPING, AnimationLooping );
READ_MICRO_CHUNK( cload, MICROCHUNKID_FORCE_FACING, ForceFacing );
READ_MICRO_CHUNK( cload, MICROCHUNKID_IGNORE_FACING, IgnoreFacing );
READ_MICRO_CHUNK( cload, MICROCHUNKID_FACE_LOCATION, FaceLocation );
READ_MICRO_CHUNK( cload, MICROCHUNKID_FACE_DURATION, FaceDuration );
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_CONVERSATION_NAME, SafeConversationName );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ACTIVE_CONVERSATION_ID, ActiveConversationID );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_MOVE_OBJECT:
MoveObjectRef.Load( cload );
break;
case CHUNKID_ATTACK_OBJECT:
AttackObjectRef.Load( cload );
break;
case CHUNKID_LOOK_OBJECT:
LookObjectRef.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();
}
return true;
}
/*
**
*/
ActionClass::ActionClass( SmartGameObj *obj ) :
ActionCode( NULL ),
ActCount( 0 ),
IsPaused( false )
{
WWASSERT( obj != NULL );
ActionObj = obj;
}
ActionClass::~ActionClass( void )
{
Set_Action_Code( NULL );
Delete_Action_Code();
}
void ActionClass::Set_Action_Code( ActionCodeClass * code )
{
if ( ActionCode != NULL ) {
ActionCode->Shutdown();
Register_Action_Code_Deletion( ActionCode );
}
ActionCode = code;
if ( ActionCode ) {
_ActionCodeChanges++;
ActionCode->Init( this );
}
}
void ActionClass::Done( int reason )
{
Parameters.Priority = 0; // reset
if ( ActionCode != NULL ) {
Set_Action_Code( NULL );
Notify_Completed( Parameters.ObserverID, Parameters.ActionID, reason ); // Notify the observer
}
}
bool ActionClass::Is_Acting( void )
{
return ( ActionCode != NULL );
}
bool ActionClass::Is_Animating( void )
{
if ( ActionCode != NULL ) {
return ActionCode->Is_Animating();
}
return false;
}
bool ActionClass::Modify( const ActionParamsStruct & parameters, bool modify_move, bool modify_attack )
{
bool retval = false;
if ( ActionCode != NULL ) {
SafeActionParamsStruct safe_params;
safe_params = parameters;
ActionCode->Modify_Parameters( safe_params, modify_move, modify_attack );
retval = true;
}
return retval;
}
bool ActionClass::Reset( float priority )
{
if ( priority >= Parameters.Priority ) {
Done( ACTION_COMPLETE_LOW_PRIORITY );
return true;
}
return false;
}
void ActionClass::Notify_Completed( int observer_id, int action_id, int reason )
{
if ( observer_id != 0 ) {
// Find the observer and notify
SmartGameObj * obj = Get_Action_Obj();
const GameObjObserverList & observer_list = obj->Get_Observers();
for( int index = 0; index < observer_list.Count(); index++ ) {
if ( observer_list[ index ]->Get_ID() == observer_id ) {
observer_list[ index ]->Action_Complete( obj, action_id, (ActionCompleteReason)reason );
}
}
}
}
bool ActionClass::Request_Action( ActionCodeClass * action, const ActionParamsStruct & parameters )
{
if ( parameters.Priority >= Parameters.Priority ) {
bool old_action_completed = false;
int old_observer_id = 0;
int old_action_id = 0;
if ( ActionCode != NULL ) {
old_action_completed = true;
old_observer_id = Parameters.ObserverID;
old_action_id = Parameters.ActionID;
}
Parameters = parameters;
Set_Action_Code( action );
if ( old_action_completed ) {
Notify_Completed( old_observer_id, old_action_id, ACTION_COMPLETE_LOW_PRIORITY );
}
return true;
} else {
Notify_Completed( parameters.ObserverID, parameters.ActionID, ACTION_COMPLETE_LOW_PRIORITY );
if ( action != NULL ) {
delete action;
}
return false;
}
}
bool ActionClass::Follow_Input( const ActionParamsStruct & parameters )
{
return Request_Action( new FollowInputActionCodeClass(), parameters );
}
bool ActionClass::Stand( const ActionParamsStruct & parameters )
{
return Request_Action( new StandActionCodeClass(), parameters );
}
bool ActionClass::Play_Animation( const ActionParamsStruct & parameters )
{
#if 0 // This is difficult, because huamns play default anims
// Try to confirm we are not playing a non-action anim
if ( !Is_Animating() && ActionObj->Get_Anim_Control()->Get_Animation_Name()[0] != 0 ) {
Debug_Say(( "Can't Action_Play_Animation when playing a non-action anim\n" ));
WWASSERT( 0 );
}
#endif
return Request_Action( new PlayAnimationActionCodeClass(), parameters );
}
bool ActionClass::Goto( const ActionParamsStruct & parameters )
{
return Request_Action( new GotoActionCodeClass(), parameters );
}
bool ActionClass::Attack( const ActionParamsStruct & parameters )
{
return Request_Action( new AttackActionCodeClass(), parameters );
}
bool ActionClass::Face_Location( const ActionParamsStruct & parameters )
{
return Request_Action( new FaceLocationActionCodeClass(), parameters );
}
bool ActionClass::Have_Conversation( const ActionParamsStruct & parameters )
{
return Request_Action( new ConversationActionCodeClass(), parameters );
}
bool ActionClass::Dock_Vehicle( const ActionParamsStruct & parameters )
{
return Request_Action( new DockActionCodeClass(), parameters );
}
bool ActionClass::Enter_Exit( const ActionParamsStruct & parameters )
{
return Request_Action( new EnterExitActionCodeClass(), parameters );
}
bool ActionClass::Dive( const ActionParamsStruct & parameters )
{
return Request_Action( new DiveActionCodeClass(), parameters );
}
void ActionClass::Act( void )
{
if ( IsPaused == false ) {
ActCount++;
ActionObj->Clear_Control();
if ( ActionCode != NULL ) {
WWPROFILE( "Action Code" );
_ActionActCalls++;
ActionCode->Act();
}
Delete_Action_Code();
}
}
bool ActionClass::Is_Active( void )
{
return ( ActionCode != NULL );
}
bool ActionClass::Is_Busy( void )
{
bool retval = false;
if ( ActionCode != NULL ) {
retval = ActionCode->Is_Busy();
}
return retval;
}
/*
** ActionClass Save and Load
*/
enum {
CHUNKID_ACTION_CODE = 712001900,
CHUNKID_PARAMETERS,
};
bool ActionClass::Save( ChunkSaveClass & csave )
{
if ( ActionCode != NULL ) {
csave.Begin_Chunk( CHUNKID_ACTION_CODE );
csave.Begin_Chunk( ActionCode->Get_Factory().Chunk_ID() );
ActionCode->Get_Factory().Save( csave, ActionCode );
csave.End_Chunk();
csave.End_Chunk();
}
csave.Begin_Chunk( CHUNKID_PARAMETERS );
Parameters.Save( csave );
csave.End_Chunk();
// Don't need to save ActionObj because it is set on ActionClass Constructor
// Don't need to save ActCount because it is diagnostic
return true;
}
bool ActionClass::Load( ChunkLoadClass &cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_ACTION_CODE:
while (cload.Open_Chunk()) {
PersistFactoryClass * factory = SaveLoadSystemClass::Find_Persist_Factory( cload.Cur_Chunk_ID() );
if ( factory ) {
ActionCode = (ActionCodeClass *)factory->Load( cload );
WWASSERT( ActionCode != NULL );
ActionCode->Set_Action( this );
}
cload.Close_Chunk();
}
break;
case CHUNKID_PARAMETERS:
Parameters.Load( cload );
break;
default:
Debug_Say(( "Unrecognized Action chunkID\n" ));
break;
}
cload.Close_Chunk();
}
return true;
}
void ActionClass::Begin_Hibernation( void )
{
//
// Let the action code know we are entering hibernation
//
if ( ActionCode != NULL ) {
ActionCode->Begin_Hibernation();
}
return ;
}
void ActionClass::End_Hibernation( void )
{
//
// Let the action code know we are coming out of hibernation
//
if ( ActionCode != NULL ) {
ActionCode->End_Hibernation();
}
return ;
}