/*
** 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/repairbaygameobj.cpp $*
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 11/15/01 6:37p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "repairbaygameobj.h"
#include "basecontroller.h"
#include "wwhack.h"
#include "simpledefinitionfactory.h"
#include "persistfactory.h"
#include "definitionmgr.h"
#include "combatchunkid.h"
#include "debug.h"
#include "scriptzone.h"
#include "wwprofile.h"
#include "basecontroller.h"
#include "vehicle.h"
#include "soldier.h"
#include "playertype.h"
#include "playerdata.h"
#include "mesh.h"
#include "meshmdl.h"
#include "crandom.h"
#include "simplegameobj.h"
#include "objlibrary.h"
#include "phys.h"
#include "hanim.h"
#include "combat.h"
#include "bitpackids.h"
////////////////////////////////////////////////////////////////
// Hacks
////////////////////////////////////////////////////////////////
DECLARE_FORCE_LINK (RepairBay)
////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////
using namespace BuildingConstants;
////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////
const char *RepairBayGameObj::BoneNames[RepairBayGameObj::BONE_COUNT] =
{
"BONE02",
"BONE03",
"BONE04",
"BONE05",
"BONE06",
"BONE07",
};
////////////////////////////////////////////////////////////////
// Editable and persist factories
////////////////////////////////////////////////////////////////
SimplePersistFactoryClass _RepairBayGameObjDefPersistFactory;
SimplePersistFactoryClass _RepairBayGameObjPersistFactory;
DECLARE_DEFINITION_FACTORY (RepairBayGameObjDef, CLASSID_GAME_OBJECT_DEF_REPAIR_BAY, "Repair Bay") _RepairBayGameObjDefDefFactory;
////////////////////////////////////////////////////////////////
// Save/Load constants
////////////////////////////////////////////////////////////////
enum
{
CHUNKID_DEF_PARENT = 0x02200638,
CHUNKID_DEF_VARIABLES,
MICROCHUNKID_DEF_REPAIR_PER_SEC = 1,
MICROCHUNKID_DEF_REPARING_STATICANIM_DEFID
};
enum
{
CHUNKID_PARENT = 0x0219043,
CHUNKID_VARIABLES,
MICROCHUNKID_UNUSED = 1,
};
////////////////////////////////////////////////////////////////
//
// RepairBayGameObjDef
//
////////////////////////////////////////////////////////////////
RepairBayGameObjDef::RepairBayGameObjDef (void) :
RepairPerSec (0),
RepairingStaticAnimDefID (0)
{
//
// Editable support
//
EDITABLE_PARAM (RepairBayGameObjDef, ParameterClass::TYPE_FLOAT, RepairPerSec);
#ifdef PARAM_EDITING_ON
GenericDefParameterClass *param = new GenericDefParameterClass (&RepairingStaticAnimDefID);
param->Set_Class_ID (CLASSID_TILE);
param->Set_Name ("Repairing Static Anim Type");
GENERIC_EDITABLE_PARAM (RepairBayGameObjDef, param)
#endif //PARAM_EDITING_ON
return ;
}
////////////////////////////////////////////////////////////////
//
// ~RepairBayGameObjDef
//
////////////////////////////////////////////////////////////////
RepairBayGameObjDef::~RepairBayGameObjDef (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Class_ID
//
////////////////////////////////////////////////////////////////
uint32
RepairBayGameObjDef::Get_Class_ID (void) const
{
return CLASSID_GAME_OBJECT_DEF_REPAIR_BAY;
}
////////////////////////////////////////////////////////////////
//
// Create
//
////////////////////////////////////////////////////////////////
PersistClass *
RepairBayGameObjDef::Create (void) const
{
RepairBayGameObj *building = new RepairBayGameObj;
building->Init (*this);
return building;
}
////////////////////////////////////////////////////////////////
//
// Save
//
////////////////////////////////////////////////////////////////
bool
RepairBayGameObjDef::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_DEF_PARENT);
BuildingGameObjDef::Save (csave);
csave.End_Chunk ();
csave.Begin_Chunk (CHUNKID_DEF_VARIABLES);
WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_REPAIR_PER_SEC, RepairPerSec);
WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_REPARING_STATICANIM_DEFID, RepairingStaticAnimDefID);
csave.End_Chunk ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Load
//
////////////////////////////////////////////////////////////////
bool
RepairBayGameObjDef::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ())
{
switch (cload.Cur_Chunk_ID ())
{
case CHUNKID_DEF_PARENT:
BuildingGameObjDef::Load (cload);
break;
case CHUNKID_DEF_VARIABLES:
Load_Variables (cload);
break;
default:
Debug_Say (("Unrecognized RepairBay Def chunkID\n"));
break;
}
cload.Close_Chunk ();
}
return true;
}
////////////////////////////////////////////////////////////////
//
// Load_Variables
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObjDef::Load_Variables (ChunkLoadClass &cload)
{
while (cload.Open_Micro_Chunk ()) {
switch (cload.Cur_Micro_Chunk_ID ())
{
READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_REPAIR_PER_SEC, RepairPerSec);
READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_REPARING_STATICANIM_DEFID, RepairingStaticAnimDefID);
default:
Debug_Say (("Unrecognized RepairBay Def Variable chunkID\n"));
break;
}
cload.Close_Micro_Chunk();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Factory
//
////////////////////////////////////////////////////////////////
const PersistFactoryClass &
RepairBayGameObjDef::Get_Factory (void) const
{
return _RepairBayGameObjDefPersistFactory;
}
////////////////////////////////////////////////////////////////
//
// RepairBayGameObj
//
////////////////////////////////////////////////////////////////
RepairBayGameObj::RepairBayGameObj (void) :
RepairTimer (0),
RepairZone (Vector3 (0, 0, 0), Vector3 (0, 0, 0)),
RepairAnimationID (0),
IsReparing (false),
RepairMesh (NULL)
{
::memset (ArcObjects, 0, sizeof (ArcObjects));
for (int index = 0; index < ARC_OBJ_COUNT; index ++) {
ArcLifeRemaining[index] = 0;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// ~RepairBayGameObj
//
////////////////////////////////////////////////////////////////
RepairBayGameObj::~RepairBayGameObj (void)
{
for (int index = 0; index < ARC_OBJ_COUNT; index ++) {
ArcObjects[index] = NULL;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Factory
//
////////////////////////////////////////////////////////////////
const PersistFactoryClass &
RepairBayGameObj::Get_Factory (void) const
{
return _RepairBayGameObjPersistFactory;
}
////////////////////////////////////////////////////////////////
//
// Init
//
////////////////////////////////////////////////////////////////
void RepairBayGameObj::Init( void )
{
Init( Get_Definition() );
}
////////////////////////////////////////////////////////////////
//
// Init
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Init (const RepairBayGameObjDef &definition)
{
BuildingGameObj::Init (definition);
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Definition
//
////////////////////////////////////////////////////////////////
const RepairBayGameObjDef &
RepairBayGameObj::Get_Definition (void) const
{
return (const RepairBayGameObjDef &)BaseGameObj::Get_Definition ();
}
////////////////////////////////////////////////////////////////
//
// Save
//
////////////////////////////////////////////////////////////////
bool
RepairBayGameObj::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_PARENT);
BuildingGameObj::Save (csave);
csave.End_Chunk ();
csave.Begin_Chunk (CHUNKID_VARIABLES);
csave.End_Chunk ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Load
//
////////////////////////////////////////////////////////////////
bool
RepairBayGameObj::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_PARENT:
BuildingGameObj::Load (cload);
break;
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
default:
Debug_Say (("Unrecognized RepairBay chunkID\n"));
break;
}
cload.Close_Chunk();
}
return true;
}
////////////////////////////////////////////////////////////////
//
// Load_Variables
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Load_Variables (ChunkLoadClass &cload)
{
while (cload.Open_Micro_Chunk ()) {
/*switch (cload.Cur_Micro_Chunk_ID ())
{
default:
Debug_Say (("Unrecognized RepairBay Variable chunkID\n"));
break;
}*/
cload.Close_Micro_Chunk();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// CnC_Initialize
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::CnC_Initialize (BaseControllerClass *base)
{
BuildingGameObj::CnC_Initialize (base);
//
// Get the building's "position"
//
Vector3 pos;
Get_Position (&pos);
//
// Find the closest repair bay zone to the building
//
ScriptZoneGameObj *zone = NULL;
zone = ScriptZoneGameObj::Find_Closest_Zone (pos, ZoneConstants::TYPE_VEHICLE_REPAIR);
if (zone != NULL) {
RepairZone = zone->Get_Bounding_Box ();
//
// Get rid of the zone (if the game doesn't need it anymore)
//
if (zone->Get_Observers ().Count () == 0) {
zone->Set_Delete_Pending ();
}
}
//
// Try to find the mesh that contains the
//
RefMultiListIterator mesh_iterator (&Aggregates);
for (mesh_iterator.First (); !mesh_iterator.Is_Done (); mesh_iterator.Next ()) {
StaticPhysClass *phys_obj = mesh_iterator.Peek_Obj ();
if (phys_obj != NULL && phys_obj->Peek_Model () != NULL) {
if (::lstrcmpi (phys_obj->Peek_Model ()->Get_Name (), "rep^NOD_fx") == 0) {
RepairMesh = phys_obj;
break;
}
}
}
//
// Find the closest reparing static anim phys
//
float closest2 = 99999.0F;
RefPhysListIterator iterator = PhysicsSceneClass::Get_Instance()->Get_Static_Object_Iterator ();
for (iterator.First (); !iterator.Is_Done (); iterator.Next ()) {
StaticAnimPhysClass *anim_phys_obj = iterator.Peek_Obj ()->As_StaticAnimPhysClass ();
//
// Is this a repairing static anim phys?
//
if ( anim_phys_obj != NULL &&
anim_phys_obj->Get_Definition ()->Get_ID () == (uint32)Get_Definition ().RepairingStaticAnimDefID)
{
//
// Is this the closest one we've found so far?
//
Vector3 anim_pos;
anim_phys_obj->Get_Position (&anim_pos);
float dist2 = (anim_pos - pos).Length2 ();
if (dist2 < closest2) {
closest2 = dist2;
RepairAnimationID = anim_phys_obj->Get_ID ();
}
}
}
//
// Create the simple game object's that we'll use to display the repair-arc effect
//
for (int index = 0; index < ARC_OBJ_COUNT; index ++) {
ArcObjects[index] = (SimpleGameObj *)ObjectLibraryManager::Create_Object ("Arc Effect");
if ( ArcObjects[index]->Peek_Physical_Object () != NULL &&
ArcObjects[index]->Peek_Physical_Object ()->Peek_Model () != NULL)
{
ArcObjects[index]->Peek_Physical_Object ()->Peek_Model ()->Set_Hidden (true);
}
ArcLifeRemaining[index] = 0;
}
//
// Get information about the model
//
RenderObjClass *model = ArcObjects[0]->Peek_Physical_Object ()->Peek_Model ();
if (model != NULL) {
//
// Store the original transforms of each bone
//
for (int index = 0; index < BONE_COUNT; index ++) {
Bones[index] = model->Get_Bone_Transform (BoneNames[index]);
}
//
// Lookup the original transform of the end-bone
//
int end_index = model->Get_Bone_Index ("bone_end");
EndTM = model->Get_Bone_Transform (end_index);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Play_Repairing_Animation
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Play_Repairing_Animation (bool onoff)
{
//
// Lookup the static animation object we need to play
//
StaticPhysClass *static_phys_obj = PhysicsSceneClass::Get_Instance ()->Find_Static_Object (RepairAnimationID);
if (static_phys_obj != NULL) {
StaticAnimPhysClass *anim_phys_obj = static_phys_obj->As_StaticAnimPhysClass ();
if (anim_phys_obj != NULL) {
//
// Configure the animation
//
AnimCollisionManagerClass &anim_mgr = anim_phys_obj->Get_Animation_Manager ();
anim_mgr.Set_Animation_Mode (AnimCollisionManagerClass::ANIMATE_TARGET);
//
// Either play the animation forward or backward
//
if (onoff && anim_mgr.Get_Target_Frame () == 0) {
anim_mgr.Set_Target_Frame_End ();
} else if (onoff == false && anim_mgr.Get_Target_Frame () != 0) {
anim_mgr.Set_Target_Frame (0);
}
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Think
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Think (void)
{
WWPROFILE ("Repair Bay Think");
//
// Check to see if we should repair the vehicle a little more...
//
if (IsDestroyed == false) {
RepairTimer -= TimeManager::Get_Frame_Seconds ();
if (RepairTimer <= 0) {
RepairTimer = 0.5F;
//
// Repair any vehicle that is in our zone
//
bool is_repairing = Repair_Vehicle ();
//
// Play the repairing animation
//
if (is_repairing != IsReparing) {
IsReparing = is_repairing;
}
}
if (IsReparing) {
Update_Repairing_Animations ();
}
}
//
// Hide any arc-effects that have finished
//
for (int index = 0; index < ARC_OBJ_COUNT; index ++) {
if (ArcLifeRemaining[index] > 0) {
ArcLifeRemaining[index] -= TimeManager::Get_Frame_Seconds ();
if (ArcLifeRemaining[index] <= 0) {
PhysClass *phys_obj = ArcObjects[index]->Peek_Physical_Object ();
phys_obj->Peek_Model ()->Set_Hidden (true);
}
}
}
BuildingGameObj::Think ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Repair_Vehicle
//
////////////////////////////////////////////////////////////////
bool
RepairBayGameObj::Repair_Vehicle (void)
{
bool is_repairing = false;
VehicleList.Delete_All ();
//
// Collect the dynamic physics objects in the repair zone
//
NonRefPhysListClass objs_to_repair;
PhysicsSceneClass::Get_Instance ()->Collect_Objects (RepairZone, false, true, &objs_to_repair);
//
// Loop over all the objects
//
NonRefPhysListIterator it (&objs_to_repair);
for (it.First (); !it.Is_Done (); it.Next ()) {
PhysicalGameObj *gameobj = NULL;
//
// Get the game object from this physics object
//
if (it.Peek_Obj()->Get_Observer () != NULL) {
gameobj = ((CombatPhysObserverClass *)it.Peek_Obj()->Get_Observer())->As_PhysicalGameObj();
}
if (gameobj != NULL) {
//
// See if this is a vehicle game object
//
VehicleGameObj *vehicle = gameobj->As_VehicleGameObj ();
if (vehicle != NULL) {
//
// Check to ensure the driver is of the right team
//
SoldierGameObj *driver = vehicle->Get_Driver ();
if ( driver != NULL &&
driver->Get_Player_Type () == BaseController->Get_Player_Type() )
{
PlayerDataClass *player_data = driver->Get_Player_Data ();
float shield_max = vehicle->Get_Defense_Object ()->Get_Shield_Strength_Max ();
float health_max = vehicle->Get_Defense_Object ()->Get_Health_Max ();
float curr_health = vehicle->Get_Defense_Object ()->Get_Health ();
float curr_shield = vehicle->Get_Defense_Object ()->Get_Shield_Strength ();
//
// Avoid divide-by-zero
//
health_max = max (health_max, WWMATH_EPSILON);
shield_max = max (shield_max, WWMATH_EPSILON);
//
// Calculate how much health and shield to restore and how much it will cost
//
float vehicle_cost = 1000.0F;//vehicle->Get_Cost ();
float repair_rate = (Get_Definition ().RepairPerSec * 0.5F);
float total_damage = (health_max - curr_health) + (shield_max - curr_shield);
int damage_points = (int)min (repair_rate, total_damage);
float repair_cost_per_pt = vehicle_cost / (health_max + shield_max);
//int available_funds = 1000; //player_data->Get_Score ()
#pragma message("(TSS) available_funds = 1000 looks suspect...")
int available_funds = 1000; //player_data->Get_Money ()
int points_restored = int(available_funds / repair_cost_per_pt);
points_restored = max (points_restored, 0);
points_restored = min (points_restored, damage_points);
float total_cost = points_restored * repair_cost_per_pt;
int health_restored = min (int(health_max - curr_health), points_restored);
int shield_restored = min (int(shield_max - curr_shield), (points_restored - health_restored));
//
// Debit the player's account
//
//player_data->Purchase_Item (total_cost);
is_repairing = true;
//
// Repair the vehicle's health if the player has enough money
//
if (health_restored > 0) {
if (CombatManager::I_Am_Server ()) {
vehicle->Get_Defense_Object ()->Add_Health (health_restored);
}
is_repairing = true;
}
//
// Repair the vehicle's shield if the player has enough money
//
if (shield_restored > 0) {
if (CombatManager::I_Am_Server ()) {
vehicle->Get_Defense_Object ()->Add_Shield_Strength (shield_restored);
}
is_repairing = true;
}
//
// Add this vehicle to the list of vehicles that are being repaired
//
if (is_repairing) {
VehicleList.Add (vehicle);
}
}
}
}
}
return is_repairing;
}
////////////////////////////////////////////////////////////////
//
// Find_Random_Mesh
//
////////////////////////////////////////////////////////////////
MeshClass *
Find_Random_Mesh (RenderObjClass *model)
{
MeshClass *mesh = NULL;
//
// Is this model a mesh?
//
if (mesh == NULL && model->Class_ID () == RenderObjClass::CLASSID_MESH) {
mesh = reinterpret_cast (model);
}
//
// Pick a random start index in the list
//
int count = model->Get_Num_Sub_Objects ();
int start_index = FreeRandom.Get_Int ( count );
//
// Loop over all the objects
//
for (int index = 0; mesh == NULL && index < count; index ++) {
//
// Get a pointer to the current subobject
//
int real_index = (start_index + index) % count;
RenderObjClass *subobj = model->Get_Sub_Object (real_index);
if (subobj != NULL) {
//
// Try to find a mesh inside this object
//
mesh = Find_Random_Mesh (subobj);
REF_PTR_RELEASE (subobj);
}
}
return mesh;
}
////////////////////////////////////////////////////////////////
//
// Update_Repairing_Animations
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Update_Repairing_Animations (void)
{
//
// Loop over all the vehicles that are currently being repaired
//
for (int index = 0; index < VehicleList.Count (); index ++) {
ScriptableGameObj *game_obj = VehicleList[index];
if (game_obj != NULL) {
PhysicalGameObj *physical_game_obj = game_obj->As_PhysicalGameObj ();
RenderObjClass *model = physical_game_obj->Peek_Model ();
if (model != NULL) {
//
// Randomize the welding-arc's that this vehicle gets
//
if (FreeRandom.Get_Int (4) == 1) {
Emit_Welding_Arc (model);
}
}
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Emit_Welding_Arc
//
////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Emit_Welding_Arc (RenderObjClass *vehicle_model)
{
if (RepairMesh == NULL) {
return ;
}
//
// Pick a random "arc welder" to emit the animation from
//
Vector3 startpoint (0, 0, 0);
Get_Position (&startpoint);
//
// Pick a random bone to emit the animation from
//
RenderObjClass *aggregate_model = RepairMesh->Peek_Model ();
int count = 8;//aggregate_model->Get_Num_Bones ();
int bone_index = FreeRandom.Get_Int (count) + 1;
StringClass bone_name;
bone_name.Format ("REP^NODRIM_FX%d", bone_index);
startpoint = aggregate_model->Get_Bone_Transform (bone_name).Get_Translation ();
//
// Default the end of the vector to the vehicles start position
//
Vector3 endpoint = vehicle_model->Get_Transform ().Get_Translation ();
//
// Find a random mesh inside the vehicle's model
//
MeshClass *mesh = Find_Random_Mesh (vehicle_model);
if (mesh != NULL) {
MeshModelClass *mesh_model = mesh->Get_Model ();
if (mesh_model != NULL) {
//
// Get a random vertex inside the model
//
int vertex_count = mesh_model->Get_Vertex_Count ();
int random_vert_index = FreeRandom.Get_Int (vertex_count);
if (vertex_count > 0) {
endpoint = mesh->Get_Transform () * mesh_model->Get_Vertex_Array ()[random_vert_index];
}
REF_PTR_RELEASE (mesh_model);
}
}
//
// Try to find an available welding-arc
//
for (int index = 0; index < ARC_OBJ_COUNT; index ++) {
if ((ArcLifeRemaining[index] <= 0) && (FreeRandom.Get_Int (4) == 1)) {
//
// Get the model of the object we'll be displaying
//
RenderObjClass *model = ArcObjects[index]->Peek_Physical_Object ()->Peek_Model ();
//startpoint.Set (0, 0, 0);
//endpoint.Set (0, 0, 10);
//
// Make the object "look" at its endpoint
//
Matrix3D start_tm;
start_tm.Obj_Look_At (startpoint, endpoint, 0);
model->Set_Transform (start_tm);
//
// Now scale the bone's that control the length of the arc so it
// will fit perfectly between the start and endpoints.
//
for (int bone_index = 0; bone_index < BONE_COUNT; bone_index ++) {
float percent = WWMath::Fabs (Bones[bone_index].Get_Translation ().X / EndTM.Get_Translation ().X);
Vector3 new_pos = startpoint + (endpoint - startpoint) * percent;
//
// Calculate the world space position of the bone
//
Vector3 world_space_pos = start_tm * Bones[bone_index].Get_Translation ();
Vector3 world_space_offset = new_pos - world_space_pos;
Matrix3D bone_tm (world_space_offset);
//
// Control the bone
//
int bone_id = model->Get_Bone_Index (BoneNames[bone_index]);
model->Capture_Bone (bone_id);
model->Control_Bone (bone_id, bone_tm, true);
}
//
// Calculate the world space position of the endpoint
//
Vector3 world_space_end_pos = start_tm * EndTM.Get_Translation ();
Vector3 world_space_offset = endpoint - world_space_end_pos;
Matrix3D end_tm (world_space_offset);
//
// Control the ending bone
//
int end_index = model->Get_Bone_Index ("bone_end");
model->Capture_Bone (end_index);
model->Control_Bone (end_index, end_tm, true);
//
// Start the animation
//
ArcLifeRemaining[index] = 3.0F;
HAnimClass *anim = model->Peek_Animation ();
if (anim != NULL) {
model->Set_Animation (anim, 0, RenderObjClass::ANIM_MODE_ONCE);
ArcLifeRemaining[index] = anim->Get_Total_Time ();
}
//
// Show the model
//
model->Set_Hidden (false);
}
}
return ;
}
/////////////////////////////////////////////////////////////////////////////
//
// Export_Creation
//
/////////////////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Export_Creation (BitStreamClass &packet)
{
BuildingGameObj::Export_Creation (packet);
//
// Send the repair zone's position and size
//
packet.Add (RepairZone.Center.X, BITPACK_WORLD_POSITION_X);
packet.Add (RepairZone.Center.Y, BITPACK_WORLD_POSITION_Y);
packet.Add (RepairZone.Center.Z, BITPACK_WORLD_POSITION_Z);
packet.Add (RepairZone.Extent.X, BITPACK_WORLD_POSITION_X);
packet.Add (RepairZone.Extent.Y, BITPACK_WORLD_POSITION_Y);
packet.Add (RepairZone.Extent.Z, BITPACK_WORLD_POSITION_Z);
//
// Send the zone's facing (almost 100% of the time, this zone should
// simply be an AABox rotated about its Z-axis).
//
float facing = RepairZone.Basis.Get_Z_Rotation ();
packet.Add (facing);
return ;
}
/////////////////////////////////////////////////////////////////////////////
//
// Import_Creation
//
/////////////////////////////////////////////////////////////////////////////
void
RepairBayGameObj::Import_Creation (BitStreamClass &packet)
{
BuildingGameObj::Import_Creation (packet);
//
// Get the repair zone's position and size
//
packet.Get (RepairZone.Center.X, BITPACK_WORLD_POSITION_X);
packet.Get (RepairZone.Center.Y, BITPACK_WORLD_POSITION_Y);
packet.Get (RepairZone.Center.Z, BITPACK_WORLD_POSITION_Z);
packet.Get (RepairZone.Extent.X, BITPACK_WORLD_POSITION_X);
packet.Get (RepairZone.Extent.Y, BITPACK_WORLD_POSITION_Y);
packet.Get (RepairZone.Extent.Z, BITPACK_WORLD_POSITION_Z);
//
// Get the zone's facing (almost 100% of the time, this zone should
// simply be an AABox rotated about its Z-axis).
//
float facing = 0;
packet.Get (facing);
RepairZone.Basis.Rotate_Z (facing);
return ;
}