This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/pathaction.cpp

772 lines
18 KiB
C++
Raw Permalink Normal View History

/*
** 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/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Combat *
* *
* $Archive:: /Commando/Code/Combat/pathaction.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/16/01 1:51p $*
* *
* $Revision:: 18 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pathaction.h"
#include "smartgameobj.h"
#include "soldier.h"
#include "doors.h"
#include "humanphys.h"
#include "obbox.h"
#include "path.h"
#include "chunkio.h"
#include "saveload.h"
////////////////////////////////////////////////////////////////
// Save/Load constants
////////////////////////////////////////////////////////////////
enum
{
CHUNKID_GAMEOBJ_REF = 0x11020954,
CHUNKID_VARIABLES
};
enum
{
VARID_ELEVATOR_STATE = 0,
VARID_DOOR_STATE,
VARID_STATE,
VARID_TYPE,
VARID_PATH_PTR,
VARID_MECHANISM_PTR,
VARID_DESTINATION,
VARID_FACEPOS,
VARID_GAME_OBJ_PTR,
VARID_LADDER_STATE,
VARID_LADDER_INDEX
};
////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////
DynamicVectorClass<GameObjReference> PathActionClass::LadderList;
////////////////////////////////////////////////////////////////////////////////////////////
//
// PathActionClass
//
////////////////////////////////////////////////////////////////////////////////////////////
PathActionClass::PathActionClass (void) :
ElevatorState (ELEVATOR_STATE_NONE),
DoorState (DOOR_STATE_NONE),
LadderState (LADDER_STATE_NONE),
State (STATE_FINISHED),
Type (TYPE_UNKNOWN),
Mechanism (NULL),
GameObj (NULL),
Path (NULL),
LadderIndex (-1),
Destination (0, 0, 0),
FacePos (0, 0, 0),
Timer (0)
{
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// ~PathActionClass
//
////////////////////////////////////////////////////////////////////////////////////////////
PathActionClass::~PathActionClass (void)
{
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Initialize
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Initialize
(
TYPE type,
PathClass * path,
SmartGameObj * game_obj,
StaticPhysClass * mechanism
)
{
Type = type;
GameObj = game_obj;
Mechanism = mechanism;
Path = path;
//
// Reset the states
//
State = STATE_WAITING;
ElevatorState = ELEVATOR_STATE_WAITING;
DoorState = DOOR_STATE_GETTING_IN_POSITION;
LadderState = LADDER_STATE_WAITING;
Timer = 5;
assert(Path != NULL);
assert(Path->Get_Path_Vector_Length() >= Path->Get_Path_Vector_Count());
Path->Get_Action_Entrance (Destination);
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Process
//
////////////////////////////////////////////////////////////////////////////////////////////
bool
PathActionClass::Process (void)
{
switch (Type)
{
case TYPE_JUMPING:
Handle_Jump ();
break;
case TYPE_LADDER:
Handle_Ladder ();
break;
case TYPE_ELEVATOR:
Handle_Elevator ();
break;
case TYPE_DOOR:
Handle_Door ();
break;
default:
Set_Finished ();
break;
}
return (State == STATE_FINISHED);
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Handle_Ladder
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Handle_Ladder (void)
{
SoldierGameObj *soldier = GameObj->As_SoldierGameObj ();
WWASSERT (soldier != NULL);
switch (LadderState)
{
case LADDER_STATE_WAITING:
{
//
// Is there already someone on the ladder?
//
if (Get_Ladder_Occupant (LadderIndex) == NULL) {
Set_Ladder_Occupant (LadderIndex, GameObj);
//
// Get on the ladder
//
soldier->Set_Boolean_Control (ControlClass::BOOLEAN_ACTION);
LadderState = LADDER_STATE_GETTING_ON;
}
break;
}
case LADDER_STATE_GETTING_ON:
//
// Have we gotten on yet?
//
if (soldier->Is_On_Ladder ()) {
//
// Start climbing
//
State = STATE_MOVING;
LadderState = LADDER_STATE_CLIMBING;
assert(Path != NULL);
assert(Path->Get_Path_Vector_Length() > Path->Get_Path_Vector_Count());
Path->Get_Action_Destination (Destination);
Path->Set_Movement_Directions (PathClass::MOVE_Z);
} else {
soldier->Set_Boolean_Control (ControlClass::BOOLEAN_ACTION);
}
break;
case LADDER_STATE_CLIMBING:
//
// Have we finished climbing yet?
//
if (soldier->Is_On_Ladder () == false) {
LadderState = LADDER_STATE_NONE;
Set_Ladder_Occupant (LadderIndex, NULL);
Set_Finished ();
} else {
//
// Get the object's current position
//
Vector3 curr_pos;
GameObj->Get_Position (&curr_pos);
//
// Is the object within a small distance of the destination?
//
float dist = WWMath::Fabs(curr_pos.Z - Destination.Z);
if (dist < 0.25F) {
//
// Tell the object to get off the ladder (he may not be able to
// if someone is blocking his way)
//
soldier->Set_Boolean_Control (ControlClass::BOOLEAN_ACTION);
State = STATE_WAITING;
}
}
break;
}
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Handle_Jump
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Handle_Jump (void)
{
//
// Is this a human?
//
SoldierGameObj *soldier_obj = GameObj->As_SoldierGameObj ();
if (soldier_obj != NULL) {
HumanPhysClass *human_phys = soldier_obj->Peek_Physical_Object ()->As_HumanPhysClass ();
if (human_phys != NULL) {
//
// If we are still in the air, then turn to face
// our destination point.
//
if (soldier_obj->Is_Airborne ()) {
Vector3 dest(0,0,0);
assert(Path != NULL);
assert(Path->Get_Path_Vector_Length() > Path->Get_Path_Vector_Count());
Path->Get_Action_Destination (dest);
Vector3 face_pt = dest + Vector3 (0, 0, soldier_obj->Get_Bullseye_Offset_Z() );
soldier_obj->Set_Targeting (face_pt, false);
}
//
// Revert when we've finished landing
//
if ( human_phys->Has_Just_Jumped () == false &&
soldier_obj->Get_Human_State ()->Get_State () == HumanStateClass::UPRIGHT)
{
Set_Finished ();
}
}
} else {
Set_Finished ();
}
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Handle_Elevator
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Handle_Elevator (void)
{
ElevatorPhysClass *elevator = Mechanism->As_ElevatorPhysClass ();
WWASSERT (elevator != NULL);
switch (ElevatorState)
{
case ELEVATOR_STATE_WAITING:
//
// Switch states if the elevator has arrived
//
if (elevator->Can_Object_Enter (GameObj)) {
elevator->Set_Current_Rider (GameObj);
ElevatorState = ELEVATOR_STATE_ENTERING;
State = STATE_MOVING;
//
// Calculate where we want the unit to walk to in order
// to enter the elevator
//
if (elevator->Get_Floor () == 0) {
Get_Elevator_Zone_Pos (ZONE_LOWER_INSIDE, &Destination);
Get_Elevator_Zone_Pos (ZONE_UPPER_CALL, &FacePos);
} else {
Get_Elevator_Zone_Pos (ZONE_UPPER_INSIDE, &Destination);
Get_Elevator_Zone_Pos (ZONE_LOWER_CALL, &FacePos);
}
} else {
elevator->Request_Elevator (GameObj);
}
break;
case ELEVATOR_STATE_RIDING:
//
// Switch states if the elevator has arrived
//
if (elevator->Can_Object_Exit (GameObj)) {
ElevatorState = ELEVATOR_STATE_EXITING;
State = STATE_MOVING;
Timer = 5.0F;
assert(Path != NULL);
assert(Path->Get_Path_Vector_Length() > Path->Get_Path_Vector_Count());
Path->Get_Action_Destination (Destination);
} else {
//
// Make the unit face the exit
//
GameObj->Set_Targeting (FacePos, false);
}
break;
case ELEVATOR_STATE_ENTERING:
//
// Switch states if the elevator has started moving
//
if (elevator->Is_Moving ()) {
ElevatorState = ELEVATOR_STATE_RIDING;
State = STATE_WAITING;
} else if (Has_Arrived ()) {
//
// Make the unit face the exit
//
GameObj->Set_Targeting (FacePos, false);
State = STATE_WAITING;
//
// Make the elevator start moving
//
elevator->Request_Elevator (GameObj);
}
break;
case ELEVATOR_STATE_EXITING:
//
// Has the unit made it through the elevator door, or has time expired?
//
Timer -= TimeManager::Get_Frame_Seconds ();
if (Has_Arrived () || Timer <= 0) {
//
// Let the elevator know that its rider has left
//
if (elevator->Get_Current_Rider () == GameObj) {
elevator->Set_Current_Rider (NULL);
}
//
// Reset the state
//
ElevatorState = ELEVATOR_STATE_NONE;
Set_Finished ();
}
break;
}
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Handle_Door
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Handle_Door (void)
{
DoorPhysClass *door = Mechanism->As_DoorPhysClass ();
WWASSERT (door != NULL);
switch (DoorState)
{
case DOOR_STATE_GETTING_IN_POSITION:
{
//
// Has the unit made it into position, or has time expired?
//
Timer -= TimeManager::Get_Frame_Seconds ();
if (Has_Arrived () || Timer <= 0) {
DoorState = DOOR_STATE_WAITING;
State = STATE_WAITING;
} else {
State = STATE_MOVING;
}
break;
}
case DOOR_STATE_WAITING:
//
// Keep the door open for as long as we need it
//
door->Request_Door_Open ();
//
// If the door is opened, then move onto the next state
//
if (door->Is_Door_Open ()) {
DoorState = DOOR_STATE_ENTERING;
State = STATE_MOVING;
Timer = 5.0F;
assert(Path != NULL);
assert(Path->Get_Path_Vector_Length() >= Path->Get_Path_Vector_Count());
Path->Get_Action_Destination (Destination);
}
break;
case DOOR_STATE_ENTERING:
//
// Has the unit made it through the door, or has time expired?
//
Timer -= TimeManager::Get_Frame_Seconds ();
if (Has_Arrived () || Timer <= 0) {
DoorState = DOOR_STATE_NONE;
Set_Finished ();
}
break;
}
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Has_Arrived
//
////////////////////////////////////////////////////////////////////////////////////////////
bool
PathActionClass::Has_Arrived (void)
{
bool retval = false;
//
// Get the object's current position
//
Vector3 curr_pos;
GameObj->Get_Position (&curr_pos);
//
// Is the object within a small distance of the destination?
//
Vector3 delta = curr_pos - Destination;
delta.Z = 0;
float dist2 = delta.Length2 ();
if (dist2 < 0.0225F) {
retval = true;
}
return retval;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Get_Elevator_Zone_Pos
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Get_Elevator_Zone_Pos (ELEVATOR_ZONE zone_id, Vector3 *position)
{
WWASSERT (position != NULL);
//
// Make sure we have an elevator
//
ElevatorPhysClass *elevator = Mechanism->As_ElevatorPhysClass ();
WWASSERT (elevator != NULL);
//
// Get information about this type of elevator
//
const ElevatorPhysDefClass *definition = elevator->Get_ElevatorPhysDef ();
//
// Transform the requested zone into world-space
//
const Matrix3D &tm = elevator->Get_Transform ();
OBBoxClass zone_box;
OBBoxClass::Transform (tm, definition->Get_Zone (zone_id), &zone_box);
//
// Return the center of the zone to the caller
//
(*position) = zone_box.Center;
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Save
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_VARIABLES);
//
// Save each variable to its own microchunk
//
WRITE_MICRO_CHUNK (csave, VARID_ELEVATOR_STATE, ElevatorState);
WRITE_MICRO_CHUNK (csave, VARID_DOOR_STATE, DoorState);
WRITE_MICRO_CHUNK (csave, VARID_STATE, State);
WRITE_MICRO_CHUNK (csave, VARID_TYPE, Type);
WRITE_MICRO_CHUNK (csave, VARID_PATH_PTR, Path);
WRITE_MICRO_CHUNK (csave, VARID_MECHANISM_PTR, Mechanism);
WRITE_MICRO_CHUNK (csave, VARID_DESTINATION, Destination);
WRITE_MICRO_CHUNK (csave, VARID_FACEPOS, FacePos);
WRITE_MICRO_CHUNK (csave, VARID_GAME_OBJ_PTR, GameObj);
WRITE_MICRO_CHUNK (csave, VARID_LADDER_STATE, LadderState);
WRITE_MICRO_CHUNK (csave, VARID_LADDER_INDEX, LadderIndex);
csave.End_Chunk ();
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Load
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathActionClass::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
}
cload.Close_Chunk ();
}
return ;
}
///////////////////////////////////////////////////////////////////////
//
// Load_Variables
//
///////////////////////////////////////////////////////////////////////
void
PathActionClass::Load_Variables (ChunkLoadClass &cload)
{
//
// Loop through all the microchunks that define the variables
//
while (cload.Open_Micro_Chunk ()) {
switch (cload.Cur_Micro_Chunk_ID ()) {
READ_MICRO_CHUNK (cload, VARID_ELEVATOR_STATE, ElevatorState);
READ_MICRO_CHUNK (cload, VARID_DOOR_STATE, DoorState);
READ_MICRO_CHUNK (cload, VARID_STATE, State);
READ_MICRO_CHUNK (cload, VARID_TYPE, Type);
READ_MICRO_CHUNK (cload, VARID_PATH_PTR, Path);
READ_MICRO_CHUNK (cload, VARID_MECHANISM_PTR, Mechanism);
READ_MICRO_CHUNK (cload, VARID_DESTINATION, Destination);
READ_MICRO_CHUNK (cload, VARID_FACEPOS, FacePos);
READ_MICRO_CHUNK (cload, VARID_GAME_OBJ_PTR, GameObj);
READ_MICRO_CHUNK (cload, VARID_LADDER_STATE, LadderState);
READ_MICRO_CHUNK (cload, VARID_LADDER_INDEX, LadderIndex);
}
cload.Close_Micro_Chunk ();
}
//
// Request that the mechanism ptr gets remapped
//
if (GameObj != NULL) {
REQUEST_POINTER_REMAP ((void **)&GameObj);
}
//
// Request that the mechanism ptr gets remapped
//
if (Mechanism != NULL) {
REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&Mechanism);
}
//
// Request that the path ptr gets remapped
//
if (Path != NULL) {
REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&Path);
}
return ;
}
///////////////////////////////////////////////////////////////////////
//
// Set_Ladder_Occupant
//
///////////////////////////////////////////////////////////////////////
void
PathActionClass::Set_Ladder_Occupant (int ladder_index, ScriptableGameObj *object)
{
WWASSERT (ladder_index < 256);
if (ladder_index < 0) {
return ;
}
//
// Grow the ladder list until it contains enough entries
//
while (ladder_index >= LadderList.Count ()) {
LadderList.Add (NULL);
}
//
// Fill in this slot in the list
//
LadderList[ladder_index] = object;
return ;
}
///////////////////////////////////////////////////////////////////////
//
// Get_Ladder_Occupant
//
///////////////////////////////////////////////////////////////////////
ScriptableGameObj *
PathActionClass::Get_Ladder_Occupant (int ladder_index)
{
ScriptableGameObj *object = NULL;
//
// Return the object that's in this slot
//
if (ladder_index >= 0 && ladder_index < LadderList.Count ()) {
object = LadderList[ladder_index];
}
return object;
}
///////////////////////////////////////////////////////////////////////
//
// Reset
//
///////////////////////////////////////////////////////////////////////
void
PathActionClass::Reset (void)
{
if (GameObj != NULL) {
//
// Check to see if this unit is "reserving" any of the ladders...
//
for (int index = 0; index < LadderList.Count (); index ++) {
if (LadderList[index] == GameObj) {
//
// Clear the ladder reservation
//
LadderList[index] = NULL;
break;
}
}
}
GameObj = NULL;
Mechanism = NULL;
Path = NULL;
State = STATE_FINISHED;
Type = TYPE_UNKNOWN;
return ;
}
///////////////////////////////////////////////////////////////////////
//
// Set_Finished
//
///////////////////////////////////////////////////////////////////////
void
PathActionClass::Set_Finished (void)
{
State = STATE_FINISHED;
Reset ();
return ;
}