/* ** 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 . */ /*********************************************************************************************** *** 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 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 ; }