2654 lines
72 KiB
C++
2654 lines
72 KiB
C++
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program is free software: you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation, either version 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** Confidential - Westwood Studios ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Combat/vehicle.cpp $*
|
||
|
* *
|
||
|
* $Author:: Byon_g $*
|
||
|
* *
|
||
|
* $Modtime:: 3/29/02 4:08p $*
|
||
|
* *
|
||
|
* $Revision:: 266 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Includes
|
||
|
*/
|
||
|
#include "vehicle.h"
|
||
|
#include "animcontrol.h"
|
||
|
#include "debug.h"
|
||
|
#include "combat.h"
|
||
|
#include "pscene.h"
|
||
|
#include "assets.h"
|
||
|
#include "rendobj.h"
|
||
|
#include "soldier.h"
|
||
|
#include "weapons.h"
|
||
|
#include "damage.h"
|
||
|
#include "wwpacket.h"
|
||
|
#include "gameobjmanager.h"
|
||
|
#include <stdio.h>
|
||
|
#include "WWAudio.H"
|
||
|
#include "Sound3D.H"
|
||
|
#include "vehiclephys.h"
|
||
|
#include "motorcycle.h"
|
||
|
#include "trackedvehicle.h"
|
||
|
#include "vtolvehicle.h"
|
||
|
#include "wheelvehicle.h"
|
||
|
#include "wheel.h"
|
||
|
#include "phys3.h"
|
||
|
#include "humanphys.h"
|
||
|
#include "persistfactory.h"
|
||
|
#include "combatchunkid.h"
|
||
|
#include "simpledefinitionfactory.h"
|
||
|
#include "wwhack.h"
|
||
|
#include "explosion.h"
|
||
|
#include "surfaceeffects.h"
|
||
|
#include "bitpackids.h"
|
||
|
#include "wwprofile.h"
|
||
|
#include "playertype.h"
|
||
|
#include "playerdata.h"
|
||
|
#include "translatedb.h"
|
||
|
#include "apppackettypes.h"
|
||
|
#include "stealtheffect.h"
|
||
|
#include "gametype.h"
|
||
|
#include "globalsettings.h"
|
||
|
#include "basecontroller.h"
|
||
|
#include "string_ids.h"
|
||
|
#include "specialbuilds.h"
|
||
|
|
||
|
/*
|
||
|
** Local prototypes
|
||
|
*/
|
||
|
static void Set_Subobject_Visibility (RenderObjClass *model, int bone_index, bool show);
|
||
|
|
||
|
/*
|
||
|
** Defines
|
||
|
*/
|
||
|
#define NORMALIZE_RADIANS( v ) while ( v > (float)DEG_TO_RAD( 180.0f ) ) v -= (float)DEG_TO_RAD( 360.0f ); \
|
||
|
while ( v < (float)DEG_TO_RAD( -180.0f ) ) v += (float)DEG_TO_RAD( 360.0f );
|
||
|
|
||
|
//
|
||
|
// Class statics
|
||
|
//
|
||
|
bool VehicleGameObj::DefaultDriverIsGunner = false;
|
||
|
bool VehicleGameObj::CameraLockedToTurret = false;
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Target Steering Option.
|
||
|
** (gth) 07/11/2001 - making this default to true
|
||
|
** (gth) 08/21/2001 - new default is false, user can turn it on with a console command...
|
||
|
*/
|
||
|
bool _Use_Target_Steering = false;
|
||
|
|
||
|
bool VehicleGameObj::Toggle_Target_Steering( void )
|
||
|
{
|
||
|
return _Use_Target_Steering = !_Use_Target_Steering;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Set_Target_Steering( bool onoff )
|
||
|
{
|
||
|
_Use_Target_Steering = onoff;
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObj::Is_Target_Steering( void )
|
||
|
{
|
||
|
return _Use_Target_Steering;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** VehicleGameObjDef
|
||
|
*/
|
||
|
DECLARE_FORCE_LINK( Vehicle )
|
||
|
|
||
|
SimplePersistFactoryClass<VehicleGameObjDef, CHUNKID_GAME_OBJECT_DEF_VEHICLE> _VehicleGameObjDefPersistFactory;
|
||
|
|
||
|
DECLARE_DEFINITION_FACTORY(VehicleGameObjDef, CLASSID_GAME_OBJECT_DEF_VEHICLE, "Vehicle") _VehicleGameObjDefDefFactory;
|
||
|
|
||
|
VehicleGameObjDef::VehicleGameObjDef( void ) :
|
||
|
Type( VEHICLE_TYPE_CAR ),
|
||
|
TurnRadius( 10.0f ),
|
||
|
OccupantsVisible( true ),
|
||
|
EngineSoundMaxPitchFactor( 2.0F ),
|
||
|
SightDownMuzzle( false ),
|
||
|
Aim2D( true ),
|
||
|
SquishVelocity( 1.5f ),
|
||
|
VehicleNameID( 0 ),
|
||
|
NumSeats( 2 ),
|
||
|
GDIDamageReportID(0),
|
||
|
NodDamageReportID(0),
|
||
|
GDIDestroyReportID(0),
|
||
|
NodDestroyReportID(0)
|
||
|
{
|
||
|
// initialize all engine sound defs to zero
|
||
|
for (int i=0; i<MAX_ENGINE_SOUND_STATES; i++) {
|
||
|
EngineSound[i] = 0;
|
||
|
}
|
||
|
|
||
|
MODEL_DEF_PARAM( VehicleGameObjDef, PhysDefID, "DynamicPhysDef" );
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, TypeName );
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Fire0Anim );
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Fire1Anim );
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Profile );
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_FLOAT, TurnRadius );
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_FLOAT, SquishVelocity );
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, Aim2D );
|
||
|
|
||
|
#ifdef PARAM_EDITING_ON
|
||
|
EnumParameterClass *param;
|
||
|
param = new EnumParameterClass( (int*)&Type );
|
||
|
param->Set_Name ("Type");
|
||
|
param->Add_Value ( "Car", VEHICLE_TYPE_CAR );
|
||
|
param->Add_Value ( "Tank", VEHICLE_TYPE_TANK );
|
||
|
param->Add_Value ( "Bike", VEHICLE_TYPE_BIKE );
|
||
|
param->Add_Value ( "Flying", VEHICLE_TYPE_FLYING );
|
||
|
param->Add_Value ( "Turret", VEHICLE_TYPE_TURRET );
|
||
|
|
||
|
GENERIC_EDITABLE_PARAM(VehicleGameObjDef,param)
|
||
|
#endif
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, OccupantsVisible );
|
||
|
|
||
|
// engine sounds
|
||
|
FLOAT_EDITABLE_PARAM(VehicleGameObjDef, EngineSoundMaxPitchFactor, 1.0F, 10.0F);
|
||
|
NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_STARTING],"Engine Start Sound");
|
||
|
NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_RUNNING],"Engine Running Loop");
|
||
|
NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_STOPPING],"Engine Stop Sound");
|
||
|
// NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_OFF],"Engine Off Sound");
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, SightDownMuzzle );
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, VehicleNameID );
|
||
|
|
||
|
EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_INT, NumSeats );
|
||
|
|
||
|
EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDamageReportID);
|
||
|
EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDamageReportID);
|
||
|
EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDestroyReportID);
|
||
|
EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDestroyReportID);
|
||
|
}
|
||
|
|
||
|
VehicleGameObjDef::~VehicleGameObjDef( void )
|
||
|
{
|
||
|
Free_Transition_List();
|
||
|
}
|
||
|
|
||
|
uint32 VehicleGameObjDef::Get_Class_ID (void) const
|
||
|
{
|
||
|
return CLASSID_GAME_OBJECT_DEF_VEHICLE;
|
||
|
}
|
||
|
|
||
|
PersistClass * VehicleGameObjDef::Create( void ) const
|
||
|
{
|
||
|
VehicleGameObj * obj = new VehicleGameObj;
|
||
|
obj->Init( *this );
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
enum {
|
||
|
CHUNKID_DEF_PARENT = 930991656,
|
||
|
CHUNKID_DEF_VARIABLES,
|
||
|
CHUNKID_DEF_TRANSITION,
|
||
|
|
||
|
MICROCHUNKID_TYPE = 1,
|
||
|
MICROCHUNKID_TYPE_NAME,
|
||
|
MICROCHUNKID_FIRE0ANIM,
|
||
|
MICROCHUNKID_FIRE1ANIM,
|
||
|
MICROCHUNKID_PROFILE,
|
||
|
MICROCHUNKID_WEAPON_TURN_TRANS,
|
||
|
|
||
|
XXXMICROCHUNKID_SOUND,
|
||
|
MICROCHUNKID_EMITER_NAME,
|
||
|
MICROCHUNKID_EMITER_OFFSET,
|
||
|
MICROCHUNKID_EMITER2_NAME,
|
||
|
MICROCHUNKID_EMITER2_OFFSET,
|
||
|
XXX_MICROCHUNKID_MASS,
|
||
|
XXX_MICROCHUNKID_MAX_ENGINE_TORQUE,
|
||
|
XXX_MICROCHUNKID_STEERING_ANGLE,
|
||
|
XXX_MICROCHUNKID_SPRING_CONSTANT,
|
||
|
XXX_MICROCHUNKID_DAMPING_COEFFIENT,
|
||
|
XXX_MICROCHUNKID_SPRING_LENGTH,
|
||
|
MICROCHUNKID_PHYS_ID,
|
||
|
MICROCHUNKID_TURN_RADIUS,
|
||
|
MICROCHUNKID_OCCUPANTS_VISIBLE,
|
||
|
|
||
|
XXX_MICROCHUNKID_ENGINE_SOUND_RPM_SCALE_MIN,
|
||
|
XXX_MICROCHUNKID_ENGINE_SOUND_RPM_SCALE_MAX,
|
||
|
MICROCHUNKID_ENGINE_START_SOUND,
|
||
|
MICROCHUNKID_ENGINE_RUN_SOUND,
|
||
|
MICROCHUNKID_ENGINE_STOP_SOUND,
|
||
|
MICROCHUNKID_ENGINE_OFF_SOUND,
|
||
|
|
||
|
MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE,
|
||
|
MICROCHUNKID_DEF_AIM_2D,
|
||
|
|
||
|
MICROCHUNKID_DEF_SQUISH_VELOCITY,
|
||
|
MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR,
|
||
|
MICROCHUNKID_DEF_VEHICLE_NAME_ID,
|
||
|
MICROCHUNKID_DEF_NUM_SEATS,
|
||
|
MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID,
|
||
|
MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID,
|
||
|
MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID,
|
||
|
MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID,
|
||
|
};
|
||
|
|
||
|
bool VehicleGameObjDef::Save( ChunkSaveClass & csave )
|
||
|
{
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_PARENT );
|
||
|
SmartGameObjDef::Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TYPE, Type );
|
||
|
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_TYPE_NAME, TypeName );
|
||
|
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_FIRE0ANIM, Fire0Anim );
|
||
|
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_FIRE1ANIM, Fire1Anim );
|
||
|
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_PROFILE, Profile );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TURN_RADIUS, TurnRadius );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OCCUPANTS_VISIBLE, OccupantsVisible );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR, EngineSoundMaxPitchFactor );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_START_SOUND, EngineSound[ENGINE_SOUND_STATE_STARTING] );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_RUN_SOUND, EngineSound[ENGINE_SOUND_STATE_RUNNING] );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_STOP_SOUND, EngineSound[ENGINE_SOUND_STATE_STOPPING] );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_OFF_SOUND, EngineSound[ENGINE_SOUND_STATE_OFF] );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE, SightDownMuzzle );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_AIM_2D, Aim2D );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SQUISH_VELOCITY, SquishVelocity );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_VEHICLE_NAME_ID, VehicleNameID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NUM_SEATS, NumSeats );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
// Save each of the transition 'definitions' in our list.
|
||
|
for( int index = 0; index < Transitions.Count (); index ++ ) {
|
||
|
TransitionDataClass *transition = Transitions[index];
|
||
|
if( transition != NULL ) {
|
||
|
|
||
|
// Save this transition 'defintion' to its own chunk.
|
||
|
csave.Begin_Chunk( CHUNKID_DEF_TRANSITION );
|
||
|
transition->Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObjDef::Load( ChunkLoadClass &cload )
|
||
|
{
|
||
|
Free_Transition_List ();
|
||
|
|
||
|
while (cload.Open_Chunk()) {
|
||
|
switch(cload.Cur_Chunk_ID()) {
|
||
|
|
||
|
case CHUNKID_DEF_PARENT:
|
||
|
SmartGameObjDef::Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_DEF_TRANSITION:
|
||
|
{
|
||
|
TransitionDataClass *transition = new TransitionDataClass;
|
||
|
transition->Load( cload );
|
||
|
Transitions.Add( transition );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_DEF_VARIABLES:
|
||
|
while (cload.Open_Micro_Chunk()) {
|
||
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TYPE, Type );
|
||
|
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_TYPE_NAME, TypeName );
|
||
|
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_FIRE0ANIM, Fire0Anim );
|
||
|
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_FIRE1ANIM, Fire1Anim );
|
||
|
READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_PROFILE, Profile );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_PHYS_ID, PhysDefID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TURN_RADIUS, TurnRadius );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_OCCUPANTS_VISIBLE, OccupantsVisible );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR, EngineSoundMaxPitchFactor);
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_START_SOUND, EngineSound[ENGINE_SOUND_STATE_STARTING] );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_RUN_SOUND, EngineSound[ENGINE_SOUND_STATE_RUNNING] );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_STOP_SOUND, EngineSound[ENGINE_SOUND_STATE_STOPPING] );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_OFF_SOUND, EngineSound[ENGINE_SOUND_STATE_OFF] );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE, SightDownMuzzle );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_AIM_2D, Aim2D );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SQUISH_VELOCITY, SquishVelocity );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_VEHICLE_NAME_ID, VehicleNameID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NUM_SEATS, NumSeats );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized VehicleDef Variable chunkID\n" ));
|
||
|
break;
|
||
|
}
|
||
|
cload.Close_Micro_Chunk();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized VehicleDef chunkID\n" ));
|
||
|
break;
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void VehicleGameObjDef::Free_Transition_List( void )
|
||
|
{
|
||
|
// Delete each of the transition 'definitions' in our list.
|
||
|
for( int index = 0; index < Transitions.Count (); index ++ ) {
|
||
|
TransitionDataClass *transition = Transitions[index];
|
||
|
if( transition != NULL ) {
|
||
|
delete transition;
|
||
|
}
|
||
|
}
|
||
|
Transitions.Delete_All();
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
const PersistFactoryClass & VehicleGameObjDef::Get_Factory (void) const
|
||
|
{
|
||
|
return _VehicleGameObjDefPersistFactory;
|
||
|
}
|
||
|
|
||
|
|
||
|
int VehicleGameObjDef::Get_Damage_Report(int team) const
|
||
|
{
|
||
|
if (PLAYERTYPE_GDI == team) {
|
||
|
return GDIDamageReportID;
|
||
|
} else if (PLAYERTYPE_NOD == team) {
|
||
|
return NodDamageReportID;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int VehicleGameObjDef::Get_Destroy_Report(int team) const
|
||
|
{
|
||
|
if (PLAYERTYPE_GDI == team) {
|
||
|
return GDIDestroyReportID;
|
||
|
} else if (PLAYERTYPE_NOD == team) {
|
||
|
return NodDestroyReportID;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** VehicleGameObj
|
||
|
*/
|
||
|
SimplePersistFactoryClass<VehicleGameObj, CHUNKID_GAME_OBJECT_VEHICLE> _VehicleGameObjPersistFactory;
|
||
|
|
||
|
const PersistFactoryClass & VehicleGameObj::Get_Factory (void) const
|
||
|
{
|
||
|
return _VehicleGameObjPersistFactory;
|
||
|
}
|
||
|
|
||
|
|
||
|
VehicleGameObj::VehicleGameObj() :
|
||
|
TurretBone( 0 ),
|
||
|
BarrelBone( 0 ),
|
||
|
TurretTurn( 0 ),
|
||
|
BarrelTilt( 0 ),
|
||
|
BarrelOffset( 0 ),
|
||
|
Sound( NULL ),
|
||
|
EngineSoundState( ENGINE_SOUND_STATE_OFF ),
|
||
|
CachedEngineSound( NULL ),
|
||
|
WheelSurfaceSound(NULL),
|
||
|
TransitionsEnabled( true ),
|
||
|
OccupiedSeats( 0 ),
|
||
|
HasEnterTransitions( false ),
|
||
|
HasExitTransitions( false ),
|
||
|
VehicleDelivered(false),
|
||
|
LockTimer( 0 )
|
||
|
{
|
||
|
DriverIsGunner = DefaultDriverIsGunner;
|
||
|
Set_App_Packet_Type(APPPACKETTYPE_VEHICLE);
|
||
|
}
|
||
|
|
||
|
VehicleGameObj::~VehicleGameObj()
|
||
|
{
|
||
|
if ( Peek_Physical_Object() != NULL && COMBAT_SCENE != NULL) {
|
||
|
|
||
|
// Make sure the exiters don't hit me
|
||
|
Peek_Physical_Object()->Set_Collision_Group( UNCOLLIDEABLE_GROUP );
|
||
|
|
||
|
// Eject all the occupants around the vehicle so they don't intersect
|
||
|
Vector3 vehicle_pos;
|
||
|
Peek_Physical_Object()->Get_Position( &vehicle_pos );
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] != NULL ) {
|
||
|
SeatOccupants[i]->Exit_Destroyed_Vehicle( i, vehicle_pos );
|
||
|
}
|
||
|
SeatOccupants[i] = NULL;
|
||
|
}
|
||
|
} else {
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] != NULL ) {
|
||
|
SeatOccupants[i]->Exit_Vehicle();
|
||
|
}
|
||
|
SeatOccupants[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Destroy_Transitions();
|
||
|
|
||
|
if (Sound) {
|
||
|
Sound->Remove_From_Scene ();
|
||
|
Sound->Release_Ref ();
|
||
|
Sound = NULL;
|
||
|
}
|
||
|
|
||
|
if (CachedEngineSound) {
|
||
|
CachedEngineSound->Remove_From_Scene();
|
||
|
REF_PTR_RELEASE(CachedEngineSound);
|
||
|
}
|
||
|
|
||
|
Release_Turret_Bones();
|
||
|
|
||
|
Shutdown_Wheel_Effects();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void VehicleGameObj::Init( void )
|
||
|
{
|
||
|
DriverIsGunner = DefaultDriverIsGunner;
|
||
|
// Vehicles are too complex to re-init
|
||
|
// Init( Get_Definition() );
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Init( const VehicleGameObjDef & definition )
|
||
|
{
|
||
|
DriverIsGunner = DefaultDriverIsGunner;
|
||
|
SmartGameObj::Init( definition );
|
||
|
|
||
|
SeatOccupants.Resize(definition.NumSeats);
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
SeatOccupants[i] = NULL;
|
||
|
}
|
||
|
|
||
|
Aquire_Turret_Bones();
|
||
|
Init_Wheel_Effects();
|
||
|
Create_And_Destroy_Transitions();
|
||
|
Update_Damage_Meshes();
|
||
|
|
||
|
//
|
||
|
// Refine the app packet type if this is a turret
|
||
|
//
|
||
|
if (Get_Definition().Type == VEHICLE_TYPE_TURRET)
|
||
|
{
|
||
|
Set_App_Packet_Type(APPPACKETTYPE_TURRET);
|
||
|
}
|
||
|
|
||
|
//WWDEBUG_SAY((">> VehicleGameObj::Init, Type = %d\n", Get_Definition().Type));
|
||
|
}
|
||
|
|
||
|
const VehicleGameObjDef & VehicleGameObj::Get_Definition( void ) const
|
||
|
{
|
||
|
return (const VehicleGameObjDef &)BaseGameObj::Get_Definition();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** VehicleGameObj Save and Load
|
||
|
*/
|
||
|
enum {
|
||
|
CHUNKID_PARENT = 923991632,
|
||
|
CHUNKID_VARIABLES,
|
||
|
XXXCHUNKID_ANIM_CONTROL,
|
||
|
XXXCHUNKID_TRANSITION_LIST_OLD,
|
||
|
CHUNKID_SEAT_LIST,
|
||
|
|
||
|
MICROCHUNKID_TURRET_BONE = 1,
|
||
|
MICROCHUNKID_BARREL_BONE,
|
||
|
XXXMICROCHUNKID_PHYSICAL_OBJECT,
|
||
|
MICROCHUNKID_TRANSITION_LIST_ENTRY_OLD,
|
||
|
MICROCHUNKID_TURRET_TURN,
|
||
|
MICROCHUNKID_BARREL_TILT,
|
||
|
MICROCHUNKID_TRANSITIONS_ENABLED,
|
||
|
MICROCHUNKID_OCCUPIED_SEATS,
|
||
|
MICROCHUNKID_NUM_SEATS,
|
||
|
};
|
||
|
|
||
|
bool VehicleGameObj::Save( ChunkSaveClass & csave )
|
||
|
{
|
||
|
// Destroy the transitions, because we don't save them, and don't
|
||
|
// want to have the vehicle have a un-matched reference to them.
|
||
|
// Re-create at the end of save
|
||
|
Destroy_Transitions();
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_PARENT );
|
||
|
SmartGameObj::Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TURRET_TURN, TurretTurn );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BARREL_TILT, BarrelTilt );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TRANSITIONS_ENABLED, TransitionsEnabled );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OCCUPIED_SEATS, OccupiedSeats );
|
||
|
int num_seats = SeatOccupants.Length();
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_NUM_SEATS, num_seats );
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
if ( num_seats != 0 ) {
|
||
|
csave.Begin_Chunk( CHUNKID_SEAT_LIST );
|
||
|
csave.Write( &SeatOccupants[0], num_seats * sizeof( SeatOccupants[0] ) );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
Create_And_Destroy_Transitions();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObj::Load( ChunkLoadClass &cload )
|
||
|
{
|
||
|
int num_seats = 0;
|
||
|
|
||
|
while (cload.Open_Chunk()) {
|
||
|
switch(cload.Cur_Chunk_ID()) {
|
||
|
|
||
|
case CHUNKID_PARENT:
|
||
|
SmartGameObj::Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_VARIABLES:
|
||
|
{
|
||
|
while (cload.Open_Micro_Chunk()) {
|
||
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TURRET_TURN, TurretTurn );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_BARREL_TILT, BarrelTilt );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TRANSITIONS_ENABLED, TransitionsEnabled );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_OCCUPIED_SEATS, OccupiedSeats );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_NUM_SEATS, num_seats );
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized Vehicle Variable chunkID\n" ));
|
||
|
break;
|
||
|
}
|
||
|
cload.Close_Micro_Chunk();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CHUNKID_SEAT_LIST:
|
||
|
{
|
||
|
if ( (num_seats == 0) || (num_seats != Get_Definition().NumSeats)) {
|
||
|
SeatOccupants.Resize(Get_Definition().NumSeats);
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
SeatOccupants[i] = NULL;
|
||
|
}
|
||
|
break; // May be legacy
|
||
|
}
|
||
|
|
||
|
SeatOccupants.Resize( num_seats );
|
||
|
cload.Read( &SeatOccupants[0], num_seats * sizeof( SeatOccupants[0] ) );
|
||
|
for ( int i = 0; i < num_seats; i++ ) {
|
||
|
if ( SeatOccupants[i] != NULL ) {
|
||
|
REQUEST_POINTER_REMAP( (void **)&SeatOccupants[i] );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
Debug_Say(( "Unrecognized Vehicle chunkID\n" ));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
}
|
||
|
|
||
|
SaveLoadSystemClass::Register_Post_Load_Callback(this);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::On_Post_Load( void )
|
||
|
{
|
||
|
DriverIsGunner = DefaultDriverIsGunner;
|
||
|
|
||
|
Init_Wheel_Effects();
|
||
|
Aquire_Turret_Bones();
|
||
|
SmartGameObj::On_Post_Load();
|
||
|
Create_And_Destroy_Transitions();
|
||
|
Update_Damage_Meshes();
|
||
|
|
||
|
// For some reason??? some vehicles come in with an anim control, but no model in the anim control.
|
||
|
if ( Get_Anim_Control() != NULL && Get_Anim_Control()->Peek_Model() == NULL ) {
|
||
|
Get_Anim_Control()->Set_Model( Peek_Model() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Refine the app packet type if this is a turret
|
||
|
//
|
||
|
if (Get_Definition().Type == VEHICLE_TYPE_TURRET)
|
||
|
{
|
||
|
Set_App_Packet_Type(APPPACKETTYPE_TURRET);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
void VehicleGameObj::Create_Transitions( void )
|
||
|
{
|
||
|
Destroy_Transitions();
|
||
|
|
||
|
WWASSERT( TransitionInstances.Count() == 0 );
|
||
|
|
||
|
const TRANSITION_DATA_LIST & trans_data_list = Get_Definition().Get_Transition_List();
|
||
|
|
||
|
if ( trans_data_list.Count() != 0 ) { // new style
|
||
|
|
||
|
if ( Seats[0] == NULL ) {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER_0 );
|
||
|
} else {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT_0 );
|
||
|
}
|
||
|
|
||
|
if ( Seats[1] == NULL ) {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER_1 );
|
||
|
} else {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT_1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void VehicleGameObj::Destroy_Transitions( void )
|
||
|
{
|
||
|
HasEnterTransitions = false;
|
||
|
HasExitTransitions = false;
|
||
|
while ( TransitionInstances.Count() ) {
|
||
|
TransitionManager::Destroy( TransitionInstances[0] );
|
||
|
TransitionInstances.Delete( 0 );
|
||
|
}
|
||
|
|
||
|
// They get put in a list, so flush the list.
|
||
|
TransitionManager::Destroy_Pending();
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Create_And_Destroy_Transitions( void )
|
||
|
{
|
||
|
// Manage the enter transitions
|
||
|
bool should_have_enter_transitions = (OccupiedSeats < Get_Definition().NumSeats) && TransitionsEnabled;
|
||
|
if ( should_have_enter_transitions != HasEnterTransitions ) {
|
||
|
if ( should_have_enter_transitions ) {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER );
|
||
|
HasEnterTransitions = true;
|
||
|
} else {
|
||
|
Remove_Transitions( TransitionDataClass::VEHICLE_ENTER );
|
||
|
HasEnterTransitions = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Manage the exit transitions
|
||
|
bool should_have_exit_transitions = (OccupiedSeats > 0) && TransitionsEnabled;
|
||
|
if ( should_have_exit_transitions != HasExitTransitions ) {
|
||
|
if ( should_have_exit_transitions ) {
|
||
|
Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT );
|
||
|
HasExitTransitions = true;
|
||
|
} else {
|
||
|
Remove_Transitions( TransitionDataClass::VEHICLE_EXIT );
|
||
|
HasExitTransitions = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void VehicleGameObj::Aquire_Turret_Bones( void )
|
||
|
{
|
||
|
if ( TurretBone == 0 ) {
|
||
|
TurretBone = Peek_Model()->Get_Bone_Index( "turret" );
|
||
|
if ( TurretBone != 0 ) {
|
||
|
Peek_Model()->Capture_Bone( TurretBone );
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( TurretBone ) ) {
|
||
|
Debug_Say(( "VehicleGameObj::Aquire_Bones() : Turret Bone not captured for model %s\n",
|
||
|
Peek_Model()->Get_Name()));
|
||
|
TurretBone = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BarrelOffset = 0;
|
||
|
|
||
|
if ( BarrelBone == 0 ) {
|
||
|
BarrelBone = Peek_Model()->Get_Bone_Index( "barrel" );
|
||
|
if ( BarrelBone != 0 ) {
|
||
|
Peek_Model()->Capture_Bone( BarrelBone );
|
||
|
if ( !Peek_Model()->Is_Bone_Captured( BarrelBone ) ) {
|
||
|
BarrelBone = 0;
|
||
|
Debug_Say(( "VehicleGameObj::Aquire_Bones() : Barrel Bone not captured for model %s\n",
|
||
|
Peek_Model()->Get_Name()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// find the barrel in turret space
|
||
|
if ( TurretBone != 0 ) {
|
||
|
Matrix3D turret_base = Peek_Model()->Get_Bone_Transform( TurretBone );
|
||
|
Vector3 barrel_pos = Peek_Model()->Get_Bone_Transform( BarrelBone ).Get_Translation();
|
||
|
Vector3 turret_space_barrel;
|
||
|
Matrix3D::Inverse_Transform_Vector( turret_base, barrel_pos, &turret_space_barrel );
|
||
|
BarrelOffset = turret_space_barrel.Y;
|
||
|
if ( WWMath::Fabs( BarrelOffset ) < 0.1f ) {
|
||
|
BarrelOffset = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Release_Turret_Bones( void )
|
||
|
{
|
||
|
if ( Peek_Model() ) {
|
||
|
if ( TurretBone != 0 ) {
|
||
|
if ( Peek_Model()->Is_Bone_Captured( TurretBone ) ) {
|
||
|
Peek_Model()->Release_Bone( TurretBone );
|
||
|
TurretBone = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( BarrelBone != 0 ) {
|
||
|
if ( Peek_Model()->Is_Bone_Captured( BarrelBone ) ) {
|
||
|
Peek_Model()->Release_Bone( BarrelBone );
|
||
|
BarrelBone = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BarrelOffset = 0;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Export_Creation( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Export_Creation( packet );
|
||
|
|
||
|
//
|
||
|
// Send the lock status to the client
|
||
|
//
|
||
|
int lock_owner_id = 0;
|
||
|
if (LockOwner != NULL) {
|
||
|
lock_owner_id = LockOwner.Get_Ptr ()->Get_ID ();
|
||
|
}
|
||
|
packet.Add(lock_owner_id);
|
||
|
|
||
|
if (lock_owner_id != 0) {
|
||
|
packet.Add(LockTimer,BITPACK_VEHICLE_LOCK_TIMER);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void VehicleGameObj::Import_Creation( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Import_Creation( packet );
|
||
|
|
||
|
//
|
||
|
// Get the lock status from the server
|
||
|
//
|
||
|
int lock_owner_id;
|
||
|
packet.Get(lock_owner_id);
|
||
|
if (lock_owner_id != 0) {
|
||
|
LockOwner = GameObjManager::Find_PhysicalGameObj( lock_owner_id );
|
||
|
packet.Get(LockTimer,BITPACK_VEHICLE_LOCK_TIMER);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void VehicleGameObj::Export_Rare( BitStreamClass &packet )
|
||
|
{
|
||
|
SmartGameObj::Export_Rare( packet );
|
||
|
|
||
|
//
|
||
|
// Export the seat occupants
|
||
|
//
|
||
|
for (int i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
if (SeatOccupants[i] == NULL) {
|
||
|
const int NO_OCCUPANTS = -1;
|
||
|
packet.Add(NO_OCCUPANTS);
|
||
|
} else {
|
||
|
packet.Add(SeatOccupants[i]->Get_ID());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
packet.Add(VehicleDelivered);
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Import_Rare( BitStreamClass &packet )
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
SmartGameObj::Import_Rare( packet );
|
||
|
|
||
|
//
|
||
|
// Update the seat occupants
|
||
|
//
|
||
|
for (int i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
int occupant = packet.Get(occupant);
|
||
|
if (occupant == -1) {
|
||
|
|
||
|
//
|
||
|
// Remove the occupant from the seat
|
||
|
//
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
Remove_Occupant (SeatOccupants[i]);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
if ((SeatOccupants[i] == NULL) || (SeatOccupants[i]->Get_ID() != occupant)) {
|
||
|
SmartGameObj * obj = GameObjManager::Find_SmartGameObj(occupant);
|
||
|
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
Remove_Occupant (SeatOccupants[i]);
|
||
|
}
|
||
|
|
||
|
if (obj != NULL) {
|
||
|
|
||
|
//
|
||
|
// Add the occupant to the seat
|
||
|
//
|
||
|
if (SeatOccupants[i] == NULL) {
|
||
|
Add_Occupant (obj->As_SoldierGameObj(), i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool wasDelivered = VehicleDelivered;
|
||
|
|
||
|
packet.Get(VehicleDelivered);
|
||
|
|
||
|
if (!wasDelivered && VehicleDelivered) {
|
||
|
BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Player_Type());
|
||
|
|
||
|
if (base) {
|
||
|
base->On_Vehicle_Delivered(this);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void VehicleGameObj::Import_Frequent(BitStreamClass & packet)
|
||
|
{
|
||
|
/*
|
||
|
if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
|
||
|
WWDEBUG_SAY(("VEHICLE_TYPE_TURRET::Import_Frequent: %s, %d, %d\n",
|
||
|
Get_Definition().Get_Name(),
|
||
|
Get_Definition().NumSeats,
|
||
|
SeatOccupants.Length()));
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
int rounds = packet.Get(rounds);
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
Get_Weapon()->Set_Total_Rounds(rounds);
|
||
|
}
|
||
|
|
||
|
switch(Get_Definition().Type) {
|
||
|
|
||
|
case VEHICLE_TYPE_BIKE:
|
||
|
case VEHICLE_TYPE_CAR:
|
||
|
case VEHICLE_TYPE_TANK:
|
||
|
case VEHICLE_TYPE_FLYING:
|
||
|
{
|
||
|
VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
|
||
|
|
||
|
if (p_obj != NULL) {
|
||
|
|
||
|
|
||
|
Vector3 sc_position;
|
||
|
Quaternion q;
|
||
|
Vector3 vel;
|
||
|
Vector3 ang_vel;
|
||
|
bool is_engine_on;
|
||
|
|
||
|
packet.Get(is_engine_on);
|
||
|
packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
|
||
|
packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
|
||
|
packet.Get(q.X, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.Y, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.Z, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.W, BITPACK_VEHICLE_QUATERNION);
|
||
|
q.Normalize();
|
||
|
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
//
|
||
|
// Mix up the packet order to make demo/non-demo code more incompatible.
|
||
|
//
|
||
|
packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
|
||
|
#else
|
||
|
packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
|
||
|
#endif
|
||
|
|
||
|
packet.Get(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Get(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Get(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
|
||
|
if (COMBAT_STAR && (COMBAT_STAR->Get_Vehicle() == this)) {
|
||
|
p_obj->Network_Latency_State_Update( sc_position,
|
||
|
q,
|
||
|
vel,
|
||
|
ang_vel);
|
||
|
} else {
|
||
|
p_obj->Network_Interpolate_State_Update( sc_position,
|
||
|
q,
|
||
|
vel,
|
||
|
ang_vel,
|
||
|
0.1f );
|
||
|
}
|
||
|
p_obj->Enable_Engine(is_engine_on);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case VEHICLE_TYPE_TURRET:
|
||
|
//WWDEBUG_SAY(("turret read %d\n", packet.Get_Bit_Write_Position()));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WWASSERT( 0 );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
packet.Get( DriverIsGunner );
|
||
|
|
||
|
SmartGameObj::Import_Frequent(packet);
|
||
|
|
||
|
WWASSERT(packet.Is_Flushed());
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void VehicleGameObj::Export_Frequent(BitStreamClass & packet)
|
||
|
{
|
||
|
Apply_Control(); // Make sure we have our drivers controls
|
||
|
/*
|
||
|
if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
|
||
|
WWDEBUG_SAY(("VEHICLE_TYPE_TURRET::Export_Frequent: %s, %d, %d\n",
|
||
|
Get_Definition().Get_Name(),
|
||
|
Get_Definition().NumSeats,
|
||
|
SeatOccupants.Length()));
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// Vehicles never change their weapon type
|
||
|
//
|
||
|
int total_rounds = 0;
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
total_rounds = Get_Weapon()->Get_Total_Rounds();
|
||
|
}
|
||
|
packet.Add(total_rounds);
|
||
|
|
||
|
switch(Get_Definition().Type) {
|
||
|
|
||
|
case VEHICLE_TYPE_BIKE:
|
||
|
case VEHICLE_TYPE_TANK:
|
||
|
case VEHICLE_TYPE_CAR:
|
||
|
case VEHICLE_TYPE_FLYING:
|
||
|
{
|
||
|
VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
|
||
|
if (p_obj != NULL) {
|
||
|
|
||
|
Vector3 pos;
|
||
|
Quaternion q;
|
||
|
Vector3 vel;
|
||
|
Vector3 ang_vel;
|
||
|
bool is_engine_on;
|
||
|
|
||
|
p_obj->Get_Position(&pos);
|
||
|
p_obj->Get_Orientation(&q);
|
||
|
p_obj->Get_Velocity(&vel);
|
||
|
p_obj->Get_Angular_Velocity(&ang_vel);
|
||
|
is_engine_on = p_obj->Is_Engine_Enabled();
|
||
|
|
||
|
packet.Add(is_engine_on);
|
||
|
packet.Add(pos.X, BITPACK_WORLD_POSITION_X);
|
||
|
packet.Add(pos.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Add(pos.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
|
||
|
packet.Add(q.X, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Add(q.Y, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Add(q.Z, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Add(q.W, BITPACK_VEHICLE_QUATERNION);
|
||
|
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
//
|
||
|
// Mix up the packet order to make demo/non-demo code more incompatible.
|
||
|
//
|
||
|
packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY);
|
||
|
#else
|
||
|
packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
packet.Add(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Add(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Add(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case VEHICLE_TYPE_TURRET:
|
||
|
//WWDEBUG_SAY(("turret write %d\n", packet.Get_Bit_Write_Position()));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WWASSERT(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
packet.Add( DriverIsGunner );
|
||
|
|
||
|
SmartGameObj::Export_Frequent(packet);
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void VehicleGameObj::Import_State_Cs(BitStreamClass & packet)
|
||
|
{
|
||
|
SmartGameObj::Import_State_Cs(packet);
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Export_State_Cs(BitStreamClass & packet)
|
||
|
{
|
||
|
SmartGameObj::Export_State_Cs(packet);
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Get_Velocity(Vector3 & vel)
|
||
|
{
|
||
|
VehiclePhysClass * vp = Peek_Vehicle_Phys();
|
||
|
if (vp != NULL) {
|
||
|
vp->Get_Velocity(&vel);
|
||
|
} else {
|
||
|
vel.Set(0,0,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Set_Velocity(Vector3 & vel)
|
||
|
{
|
||
|
VehiclePhysClass * vehicle = Peek_Vehicle_Phys();
|
||
|
if (vehicle != NULL) {
|
||
|
vehicle->Set_Velocity(vel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Startup( void )
|
||
|
{
|
||
|
// Setup all Seats for entry
|
||
|
Create_And_Destroy_Transitions();
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Turret( float weapon_turn, float weapon_tilt )
|
||
|
{
|
||
|
if ( TurretBone != 0 ) {
|
||
|
|
||
|
Matrix3D facing(1);
|
||
|
facing.Rotate_Z( weapon_turn );
|
||
|
|
||
|
if ( BarrelBone == 0 ) { // if no barrel bone
|
||
|
facing.Rotate_Y( -weapon_tilt ); // neg rotate y tilts up
|
||
|
}
|
||
|
|
||
|
Peek_Model()->Control_Bone( TurretBone, facing );
|
||
|
}
|
||
|
|
||
|
if ( BarrelBone != 0 ) {
|
||
|
Matrix3D facing(1);
|
||
|
facing.Rotate_Y( -weapon_tilt ); // neg rotate y tilts up
|
||
|
Peek_Model()->Control_Bone( BarrelBone, facing );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Vehicle Weapons
|
||
|
*/
|
||
|
bool VehicleGameObj::Set_Targeting( const Vector3 & target_pos, bool do_tilt )
|
||
|
{
|
||
|
bool ready = true;
|
||
|
|
||
|
//Debug_Say(( "Weapon set targeting %f %f %f\n", target_pos.X, target_pos.Y, target_pos.Z ));
|
||
|
|
||
|
SmartGameObj::Set_Targeting( target_pos );
|
||
|
|
||
|
// Find the desired turret tilt and turn
|
||
|
float relative_turn = 0;
|
||
|
float relative_tilt = 0;
|
||
|
|
||
|
if ( TurretBone != 0 ) {
|
||
|
|
||
|
// find the target pos in turret space
|
||
|
Matrix3D turret_base = Peek_Model()->Get_Bone_Transform( TurretBone );
|
||
|
Vector3 turret_space_target;
|
||
|
Matrix3D::Inverse_Transform_Vector( turret_base, target_pos, &turret_space_target );
|
||
|
|
||
|
// Set the tilt and turn
|
||
|
relative_turn = WWMath::Atan2( turret_space_target.Y, turret_space_target.X );
|
||
|
|
||
|
if ( BarrelOffset ) {
|
||
|
turret_space_target.Z = 0;
|
||
|
float barrel_offset_angle = WWMath::Atan2( BarrelOffset, turret_space_target.Length() );
|
||
|
// Debug_Say(( "barrel angle %f %f\n", RAD_TO_DEG( barrel_offset_angle ), RAD_TO_DEG( relative_turn + barrel_offset_angle ) ));
|
||
|
relative_turn -= barrel_offset_angle;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( WWMath::Fabs( relative_turn ) < DEG_TO_RAD( 80 ) ) {
|
||
|
|
||
|
if ( BarrelBone != 0 ) {
|
||
|
// find the target pos in barrel space
|
||
|
Matrix3D barrel_base = Peek_Model()->Get_Bone_Transform( BarrelBone );
|
||
|
Vector3 barrel_space_target;
|
||
|
Matrix3D::Inverse_Transform_Vector( barrel_base, target_pos, &barrel_space_target );
|
||
|
|
||
|
float dist = barrel_space_target.Length();
|
||
|
if ( dist ) {
|
||
|
// Only tilt when the turn is within 80 deg
|
||
|
relative_tilt = WWMath::Fast_Asin( barrel_space_target.Z / dist );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move the tilt and turn towards the desired, following rates and limits
|
||
|
float max_move;
|
||
|
max_move = Get_Definition().WeaponTurnRate * TimeManager::Get_Frame_Seconds();
|
||
|
if ( Get_Definition().WeaponTurnRate < DEG_TO_RAD(1000) ) {
|
||
|
TurretTurn += WWMath::Clamp( relative_turn, -max_move, max_move );
|
||
|
if ( WWMath::Fabs( relative_turn ) > WWMath::Fabs( max_move ) ) {
|
||
|
ready = false;
|
||
|
}
|
||
|
} else {
|
||
|
TurretTurn += relative_turn;
|
||
|
}
|
||
|
TurretTurn = WWMath::Clamp( TurretTurn, Get_Definition().WeaponTurnMin, Get_Definition().WeaponTurnMax );
|
||
|
max_move = Get_Definition().WeaponTiltRate * TimeManager::Get_Frame_Seconds();
|
||
|
if ( do_tilt ) {
|
||
|
if ( Get_Definition().WeaponTiltRate < DEG_TO_RAD(1000) ) {
|
||
|
BarrelTilt += WWMath::Clamp( relative_tilt, -max_move, max_move );
|
||
|
if ( WWMath::Fabs( relative_tilt ) > WWMath::Fabs( max_move ) ) {
|
||
|
ready = false;
|
||
|
}
|
||
|
} else {
|
||
|
BarrelTilt += relative_tilt;
|
||
|
}
|
||
|
}
|
||
|
BarrelTilt = WWMath::Clamp( BarrelTilt, Get_Definition().WeaponTiltMin, Get_Definition().WeaponTiltMax );
|
||
|
|
||
|
// Apply the turn and tilt to the bones
|
||
|
Update_Turret( TurretTurn, BarrelTilt );
|
||
|
|
||
|
// if a fast turner and had to turn, do it again,just to make sure (trying to fix obelisk)
|
||
|
if ( Get_Definition().WeaponTurnRate > DEG_TO_RAD(1000) &&
|
||
|
( WWMath::Fabs(relative_turn) >= DEG_TO_RAD( 2 ) || WWMath::Fabs(relative_tilt) > DEG_TO_RAD( 2 ) ) ) {
|
||
|
static int calls = 0;
|
||
|
if ( calls < 3 ) {
|
||
|
calls++;
|
||
|
Set_Targeting( target_pos, do_tilt ); // recurse
|
||
|
calls--;
|
||
|
}
|
||
|
ready = true;
|
||
|
}
|
||
|
|
||
|
return ready;
|
||
|
}
|
||
|
|
||
|
|
||
|
int VehicleGameObj::Get_Weapon_Control_Owner(void)
|
||
|
{
|
||
|
SoldierGameObj * gunner = Get_Gunner();
|
||
|
if ( gunner && !Get_Driver_Is_Gunner() ) {
|
||
|
return gunner->Get_Control_Owner();
|
||
|
}
|
||
|
SoldierGameObj * driver = Get_Driver();
|
||
|
if ( driver ) {
|
||
|
return driver->Get_Control_Owner();
|
||
|
}
|
||
|
return Get_Control_Owner();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
void VehicleGameObj::Apply_Control( void )
|
||
|
{
|
||
|
SoldierGameObj * driver = Seats[DRIVER_SEAT];
|
||
|
SoldierGameObj * gunner = Seats[GUNNER_SEAT];
|
||
|
if ( gunner == NULL ) {
|
||
|
gunner = driver;
|
||
|
}
|
||
|
|
||
|
bool target_steering = false;
|
||
|
// If I have a driver, use his control
|
||
|
if ( driver && !driver->In_Transition() ) {
|
||
|
//Debug_Say(( "Vehicle copy driver's control\n" )); //copy his control???
|
||
|
|
||
|
Control = driver->Get_Control();
|
||
|
target_steering = true;
|
||
|
|
||
|
} else if ( !Get_Action()->Is_Acting() ) {
|
||
|
Control.Clear_Control(); // Bad for network????
|
||
|
}
|
||
|
|
||
|
// If i have a gunner, use him for the gun control
|
||
|
if ( gunner && (gunner != driver) && !gunner->In_Transition() ) {
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_PREV, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_PREV ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_USE, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_USE ) );
|
||
|
}
|
||
|
|
||
|
if ( target_steering ) {
|
||
|
// Lets make turns come from the targeting
|
||
|
Vector3 target_pos = Get_Targeting_Pos();
|
||
|
Vector3 obj_space_target;
|
||
|
Matrix3D::Inverse_Transform_Vector( Get_Transform(), target_pos, &obj_space_target );
|
||
|
float target_direction = WWMath::Atan2( obj_space_target.Y, obj_space_target.X );
|
||
|
float turn_amount = target_direction / DEG_TO_RAD( 60 );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, turn_amount );
|
||
|
}
|
||
|
|
||
|
// Clamp turning
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT,
|
||
|
WWMath::Clamp( Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT ), -1.0F, 1.0F ) );
|
||
|
|
||
|
SmartGameObj::Apply_Control();
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
void VehicleGameObj::Apply_Control( void )
|
||
|
{
|
||
|
SoldierGameObj * driver = Get_Driver();
|
||
|
SoldierGameObj * gunner = Get_Gunner();
|
||
|
|
||
|
if ( gunner == NULL || Get_Driver_Is_Gunner() ) {
|
||
|
gunner = driver;
|
||
|
}
|
||
|
|
||
|
// if ( gunner || driver ) {
|
||
|
if ( !CombatManager::Is_Gameplay_Permitted() ) {
|
||
|
Clear_Control();
|
||
|
Controller.Reset();
|
||
|
if ( Peek_Vehicle_Phys() != NULL) {
|
||
|
Peek_Vehicle_Phys()->Set_Velocity( Vector3(0,0,0) );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// }
|
||
|
|
||
|
bool target_steering = false;
|
||
|
// If I have a driver, use his control
|
||
|
|
||
|
if ( driver && !driver->In_Transition() && driver->Is_Human_Controlled () == true) {
|
||
|
|
||
|
/*
|
||
|
bool use_control = CombatManager::I_Am_Server() ||
|
||
|
(CombatManager::Get_Client_Interpolation_Model() !=
|
||
|
CLIENT_INTERPOLATION_PATHFIND) ||
|
||
|
(CombatManager::I_Am_Client() &&
|
||
|
driver->Get_Control_Owner() == CombatManager::Get_My_Id());
|
||
|
|
||
|
//Debug_Say(( "Vehicle copy driver's control\n" )); //copy his control???
|
||
|
|
||
|
if (use_control) {
|
||
|
Control = driver->Get_Control();
|
||
|
target_steering = true;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// This could be a little dodgy... does client do this?
|
||
|
//
|
||
|
Control = driver->Get_Control();
|
||
|
target_steering = true;
|
||
|
}
|
||
|
|
||
|
// If i have a gunner, use him for the gun control
|
||
|
if ( gunner && (gunner != driver) && !gunner->In_Transition() && !Get_Driver_Is_Gunner() ) {
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY ) );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD ) );
|
||
|
}
|
||
|
|
||
|
// Vehicles never change weapons
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT, false );
|
||
|
Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_PREV, false );
|
||
|
|
||
|
|
||
|
if ( target_steering && _Use_Target_Steering ) {
|
||
|
|
||
|
// Target Steering kicks in if the user is pressing the accelerator key.
|
||
|
if (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) >= 0.0f) {
|
||
|
|
||
|
// Lets make turns come from the targeting
|
||
|
Vector3 target_pos = Get_Targeting_Pos();
|
||
|
Vector3 obj_space_target;
|
||
|
Matrix3D::Inverse_Transform_Vector( Get_Transform(), target_pos, &obj_space_target );
|
||
|
float target_direction = WWMath::Atan2( obj_space_target.Y, obj_space_target.X );
|
||
|
|
||
|
// Offset the target direction if the user is turning while driving forward
|
||
|
const float TARGET_STRAFE_ANGLE = DEG_TO_RADF(50.0f);
|
||
|
target_direction += TARGET_STRAFE_ANGLE * Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT );
|
||
|
|
||
|
// Set the final steering angle
|
||
|
float turn_amount = target_direction / DEG_TO_RAD( 60 ) * Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD );
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, turn_amount );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// When over-riding the target steering with keyboard steering,
|
||
|
// scale the turn rate down if we're going forward.
|
||
|
if (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) > 0.0f) {
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, 0.5f * Control.Get_Analog(ControlClass::ANALOG_TURN_LEFT) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clamp turning
|
||
|
Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT,
|
||
|
WWMath::Clamp( Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT ), -1.0F, 1.0F ) );
|
||
|
|
||
|
SmartGameObj::Apply_Control();
|
||
|
}
|
||
|
|
||
|
static char * _profile_name = "Vehicle Think";
|
||
|
|
||
|
void VehicleGameObj::Think( void )
|
||
|
{
|
||
|
{ WWPROFILE( _profile_name );
|
||
|
|
||
|
Apply_Control(); // ????
|
||
|
|
||
|
Update_Transitions();
|
||
|
}
|
||
|
SmartGameObj::Think(); // Perform smart object thinking
|
||
|
{ WWPROFILE( _profile_name );
|
||
|
|
||
|
Update_Sound_Effects();
|
||
|
|
||
|
Update_Wheel_Effects();
|
||
|
|
||
|
// Update the lock status
|
||
|
if (LockTimer > 0.0f) {
|
||
|
LockTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
} else {
|
||
|
LockOwner = NULL;
|
||
|
}
|
||
|
|
||
|
// UnStealth if we don't have any occupants, and we aren't in single play
|
||
|
if (StealthEffect != NULL) {
|
||
|
if ((Get_Occupant_Count() == 0 ) && ( !IS_MISSION )) {
|
||
|
StealthEffect->Enable_Stealth(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static char * _post_profile_name = "Vehicle PostThink";
|
||
|
|
||
|
void VehicleGameObj::Post_Think( void )
|
||
|
{
|
||
|
{ WWPROFILE( _post_profile_name );
|
||
|
RenderObjClass * model = Peek_Model();
|
||
|
WWASSERT( model );
|
||
|
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] != NULL ) {
|
||
|
char bone_name[80];
|
||
|
sprintf( bone_name, "SEAT%d", i );
|
||
|
int seat_bone_index = model->Get_Bone_Index( bone_name );
|
||
|
if ( seat_bone_index != -1 ) {
|
||
|
Matrix3D seat = model->Get_Bone_Transform( seat_bone_index );
|
||
|
if ( !SeatOccupants[i]->In_Transition() ) {
|
||
|
SeatOccupants[i]->Set_Transform( seat );
|
||
|
}
|
||
|
} else {
|
||
|
Debug_Say(( "No Seat bone %s\n", bone_name ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
SmartGameObj::Post_Think();
|
||
|
|
||
|
{ WWPROFILE( _post_profile_name );
|
||
|
if ( Get_Weapon() && !Get_Definition().Fire0Anim.Is_Empty() ) {
|
||
|
Get_Weapon()->Reset_Anim_Update();
|
||
|
int state = Get_Weapon()->Get_Anim_State();
|
||
|
if ( state == WEAPON_ANIM_NOT_FIRING ) {
|
||
|
Set_Animation( NULL );
|
||
|
} else if ( state == WEAPON_ANIM_FIRING_0 ) {
|
||
|
Set_Animation( Get_Definition().Fire0Anim );
|
||
|
} else if ( state == WEAPON_ANIM_FIRING_1 ) {
|
||
|
Set_Animation( !Get_Definition().Fire1Anim.Is_Empty() ? Get_Definition().Fire1Anim : Get_Definition().Fire0Anim );
|
||
|
} else {
|
||
|
Debug_Say(( "Bad Weapon Anim State\n" ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
int VehicleGameObj::Get_Player_Type(void) const
|
||
|
{
|
||
|
// If they have a driver, they are his team.
|
||
|
for (int i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
return SeatOccupants[i]->Get_Player_Type();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0 // Do this at exit time
|
||
|
// in MP, empty vehicles are neutral
|
||
|
if ( !IS_MISSION ) {
|
||
|
return PLAYERTYPE_NEUTRAL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return SmartGameObj::Get_Player_Type();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void VehicleGameObj::Init_Wheel_Effects( void )
|
||
|
{
|
||
|
// Create an array of persistant surface emitters, one for each "real" wheel
|
||
|
WWASSERT( WheelSurfaceEmitters.Length() == 0 );
|
||
|
if ( (Peek_Vehicle_Phys() != NULL) &&
|
||
|
(Peek_Vehicle_Phys()->Get_VehiclePhysDef()->Is_Fake() == false) )
|
||
|
{
|
||
|
int real_wheel_count = Peek_Vehicle_Phys()->Get_Real_Wheel_Count();
|
||
|
WheelSurfaceEmitters.Resize(real_wheel_count);
|
||
|
for (int i=0; i<real_wheel_count; i++) {
|
||
|
WheelSurfaceEmitters[i] = SurfaceEffectsManager::Create_Persistant_Emitter();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create a single persistant surface effect sound for tire squealing
|
||
|
WWASSERT(WheelSurfaceSound == NULL);
|
||
|
WheelSurfaceSound = SurfaceEffectsManager::Create_Persistant_Sound();
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Shutdown_Wheel_Effects( void )
|
||
|
{
|
||
|
// Release the persistant surface emitters
|
||
|
for (int i=0; i<WheelSurfaceEmitters.Length(); i++) {
|
||
|
SurfaceEffectsManager::Destroy_Persistant_Emitter( WheelSurfaceEmitters[i] );
|
||
|
WheelSurfaceEmitters[i] = NULL;
|
||
|
}
|
||
|
WheelSurfaceEmitters.Clear();
|
||
|
|
||
|
// Release the persistant surface sound effect
|
||
|
SurfaceEffectsManager::Destroy_Persistant_Sound(WheelSurfaceSound);
|
||
|
WheelSurfaceSound = NULL;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Wheel_Effects( void )
|
||
|
{
|
||
|
if ( (Peek_Vehicle_Phys() == NULL) ||
|
||
|
(Peek_Vehicle_Phys()->Get_VehiclePhysDef()->Is_Fake()) )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int roll_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TIRE_ROLLING;
|
||
|
int slide_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TIRE_SLIDING;
|
||
|
|
||
|
if ( Peek_Vehicle_Phys()->As_TrackedVehicleClass() != NULL) {
|
||
|
roll_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TRACK_ROLLING;
|
||
|
slide_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TRACK_SLIDING;
|
||
|
}
|
||
|
|
||
|
Vector3 vel;
|
||
|
Peek_Vehicle_Phys()->Get_Velocity(&vel);
|
||
|
float vlen2 = vel.Length2();
|
||
|
|
||
|
// Update the emitters and sound
|
||
|
const float EMITTER_SLIDE_THRESHOLD = 1.0f;
|
||
|
const float EMITTER_ROLL_THRESHOLD2 = 2.0f * 2.0f;
|
||
|
const float EMITTER_CINEMATIC_ROLL_AVEL = DEG_TO_RADF(1.0f);
|
||
|
|
||
|
bool sound_done = false;
|
||
|
|
||
|
if (WheelSurfaceEmitters.Length() > 0) {
|
||
|
|
||
|
if ( Peek_Vehicle_Phys() != NULL) {
|
||
|
int emitter_index = 0;
|
||
|
int wheel_count = Peek_Vehicle_Phys()->Get_Wheel_Count();
|
||
|
for (int i=0; i<wheel_count; i++) {
|
||
|
|
||
|
SuspensionElementClass * wheel = Peek_Vehicle_Phys()->Peek_Wheel(i);
|
||
|
if (wheel->Get_Flag(SuspensionElementClass::FAKE) == false) {
|
||
|
|
||
|
// Look up the surface and hitter types
|
||
|
int surface_type = wheel->Get_Contact_Surface();
|
||
|
int hitter_type = SurfaceEffectsManager::HITTER_TYPE_NONE;
|
||
|
|
||
|
// If stealthed, no emitters.
|
||
|
if (Is_Stealthed()) {
|
||
|
surface_type = SURFACE_TYPE_DEFAULT;
|
||
|
}
|
||
|
|
||
|
if (wheel->Get_Flag( SuspensionElementClass::INCONTACT ) ) {
|
||
|
if (wheel->Get_Slip_Factor() > EMITTER_SLIDE_THRESHOLD) {
|
||
|
|
||
|
hitter_type = slide_hitter_type;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// normally, trigger roll emitters by the velocity of the vehicle
|
||
|
if (vlen2 > EMITTER_ROLL_THRESHOLD2) {
|
||
|
hitter_type = roll_hitter_type;
|
||
|
}
|
||
|
|
||
|
// when attached to a cinematic, we deduce whether the wheels are rolling
|
||
|
// by using the rotation delta feature of wheels
|
||
|
if (Is_Attached_To_An_Object()) {
|
||
|
float wheel_avel = WWMath::Fabs(wheel->Get_Rotation_Delta() / TimeManager::Get_Frame_Seconds());
|
||
|
if (wheel_avel > EMITTER_CINEMATIC_ROLL_AVEL) {
|
||
|
hitter_type = roll_hitter_type;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update the emitter and move to the next emitter
|
||
|
SurfaceEffectsManager::Update_Persistant_Emitter( WheelSurfaceEmitters[emitter_index],
|
||
|
surface_type,
|
||
|
hitter_type,
|
||
|
wheel->Get_Wheel_Transform());
|
||
|
|
||
|
emitter_index++;
|
||
|
|
||
|
|
||
|
// If this is the sound generating wheel, update the sound
|
||
|
// I'm just using the first engine wheel as the one that generates the tire sound effects.
|
||
|
if (!sound_done && wheel->Get_Flag(SuspensionElementClass::ENGINE)) {
|
||
|
SurfaceEffectsManager::Update_Persistant_Sound( WheelSurfaceSound,
|
||
|
surface_type,
|
||
|
hitter_type,
|
||
|
Get_Transform());
|
||
|
|
||
|
sound_done = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Sound_Effects(void)
|
||
|
{
|
||
|
if (Peek_Vehicle_Phys() == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (EngineSoundState)
|
||
|
{
|
||
|
|
||
|
case ENGINE_SOUND_STATE_STARTING:
|
||
|
{
|
||
|
// play our ENGINE_STARTING sound, if it is finished or not found, destroy and go to ENGINE_RUNNING
|
||
|
if ((CachedEngineSound == NULL) || ((CachedEngineSound != NULL) && (CachedEngineSound->Get_State()==AudibleSoundClass::STATE_STOPPED))) {
|
||
|
Change_Engine_Sound_State(ENGINE_SOUND_STATE_RUNNING);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENGINE_SOUND_STATE_RUNNING:
|
||
|
{
|
||
|
if (CachedEngineSound != NULL) {
|
||
|
Update_Engine_Sound_Pitch ();
|
||
|
}
|
||
|
if (Peek_Vehicle_Phys()->Is_Engine_Enabled() == false) {
|
||
|
Change_Engine_Sound_State(ENGINE_SOUND_STATE_STOPPING);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENGINE_SOUND_STATE_STOPPING:
|
||
|
{
|
||
|
// play the ENGINE_STOPPING sound, if it is finished or not found, destroy and go to ENGINE_OFF
|
||
|
if ((CachedEngineSound == NULL) || ((CachedEngineSound != NULL) && (CachedEngineSound->Get_State()==AudibleSoundClass::STATE_STOPPED))) {
|
||
|
Change_Engine_Sound_State(ENGINE_SOUND_STATE_OFF);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case ENGINE_SOUND_STATE_OFF:
|
||
|
{
|
||
|
// if the engine is on, go to ENGINE_STARTING
|
||
|
if (Peek_Vehicle_Phys()->Is_Engine_Enabled() == true) {
|
||
|
Change_Engine_Sound_State(ENGINE_SOUND_STATE_STARTING);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WWDEBUG_SAY(("Error: Invalid Engine Sound State %d! file: %s line: %d\n",EngineSoundState,__FILE__,__LINE__));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Change_Engine_Sound_State(int new_state)
|
||
|
{
|
||
|
if (CachedEngineSound != NULL) {
|
||
|
CachedEngineSound->Remove_From_Scene();
|
||
|
CachedEngineSound->Release_Ref();
|
||
|
CachedEngineSound = NULL;
|
||
|
}
|
||
|
|
||
|
EngineSoundState = new_state;
|
||
|
|
||
|
DefinitionClass * sound_def = DefinitionMgrClass::Find_Definition(Get_Definition().EngineSound[new_state]);
|
||
|
if (sound_def != NULL) {
|
||
|
CachedEngineSound = (AudibleSoundClass *)sound_def->Create();
|
||
|
}
|
||
|
|
||
|
if (CachedEngineSound != NULL) {
|
||
|
CachedEngineSound->Add_To_Scene();
|
||
|
CachedEngineSound->Attach_To_Object(Peek_Model());
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Engine_Sound_Pitch(void)
|
||
|
{
|
||
|
if (CachedEngineSound == NULL) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
float pitch_factor = 1.0F;
|
||
|
|
||
|
if (Peek_Vehicle_Phys() != NULL) {
|
||
|
|
||
|
MotorVehicleClass * motophys = Peek_Physical_Object()->As_MotorVehicleClass();
|
||
|
TrackedVehicleClass * trackphys = Peek_Physical_Object()->As_TrackedVehicleClass();
|
||
|
VTOLVehicleClass * vtolphys = Peek_Physical_Object()->As_VTOLVehicleClass();
|
||
|
|
||
|
float max_factor = Get_Definition().EngineSoundMaxPitchFactor;
|
||
|
|
||
|
if (motophys != NULL) {
|
||
|
|
||
|
// Wheeled vehicle engine pitch depends on their rpms
|
||
|
float norm_rpm = motophys->Get_Normalized_Engine_RPM();
|
||
|
if (norm_rpm < 0) {
|
||
|
norm_rpm = -norm_rpm;
|
||
|
}
|
||
|
norm_rpm = WWMath::Clamp(norm_rpm,0.0f,1.0f);
|
||
|
|
||
|
pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_rpm);
|
||
|
|
||
|
} else if (trackphys != NULL) {
|
||
|
|
||
|
// Tracked vehicle engine pitch depends on their overall speed
|
||
|
const float MAX_TRACKED_SPEED = 10.0f;
|
||
|
Vector3 vel;
|
||
|
trackphys->Get_Velocity(&vel);
|
||
|
|
||
|
float norm_speed = WWMath::Clamp(vel.Length() / MAX_TRACKED_SPEED,0.0f,1.0f);
|
||
|
|
||
|
pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_speed);
|
||
|
|
||
|
} else if (vtolphys != NULL) {
|
||
|
|
||
|
// VTOL vehicle engine pitch depends on absolute acceleration
|
||
|
const float MAX_VTOL_SPEED = 20.0f;
|
||
|
Vector3 vel;
|
||
|
vtolphys->Get_Velocity(&vel);
|
||
|
|
||
|
float norm_speed = WWMath::Clamp(vel.Length() / MAX_VTOL_SPEED,0.0f,1.0f);
|
||
|
|
||
|
pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_speed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CachedEngineSound->Set_Pitch_Factor (pitch_factor);
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
VehiclePhysClass *VehicleGameObj::Peek_Vehicle_Phys( void ) const
|
||
|
{
|
||
|
WWASSERT( Peek_Physical_Object() );
|
||
|
return Peek_Physical_Object()->As_VehiclePhysClass(); // NOTE: sometimes will return NULL!
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Occupants ( drivers, gunners, passengers )
|
||
|
*/
|
||
|
void VehicleGameObj::Add_Occupant( SoldierGameObj * occupant )
|
||
|
{
|
||
|
// Add the the lowest empty seat
|
||
|
for ( int i = 0; i < Get_Definition().NumSeats; i++ ) {
|
||
|
if ( SeatOccupants[i] == NULL ) {
|
||
|
Add_Occupant( occupant, i );
|
||
|
break;;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Add_Occupant( SoldierGameObj * occupant, int seat_id )
|
||
|
{
|
||
|
WWASSERT( occupant );
|
||
|
|
||
|
if ( SeatOccupants[seat_id] != NULL ) {
|
||
|
Debug_Say(( "Vehicle already has an occupant in that seat\n" ));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( Find_Seat( occupant ) != -1 ) {
|
||
|
Debug_Say(( "Soldier already in vehicle\n" ));
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( seat_id == 0 ) {
|
||
|
DriverIsGunner = DefaultDriverIsGunner;
|
||
|
}
|
||
|
|
||
|
Debug_Say(( "Soldier %p added to seat %d\n", occupant, seat_id ));
|
||
|
|
||
|
if ( seat_id > Get_Definition().NumSeats-1 ) {
|
||
|
Debug_Say(( "Adding to extra seat %d\n", seat_id ));
|
||
|
}
|
||
|
|
||
|
SeatOccupants[seat_id] = occupant;
|
||
|
OccupiedSeats++;
|
||
|
Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
|
||
|
|
||
|
const char * anim_name = NULL;
|
||
|
if ( Get_Definition().Type == VEHICLE_TYPE_BIKE ) {
|
||
|
anim_name = "S_A_HUMAN.H_A_V20A";
|
||
|
} else {
|
||
|
anim_name = "S_A_HUMAN.H_A_V10A";
|
||
|
}
|
||
|
occupant->Enter_Vehicle( this, anim_name );
|
||
|
|
||
|
// Debug_Say(( "Vehicle has a occupant in seat %d\n", seat_id ));
|
||
|
|
||
|
// possibily delete transitions
|
||
|
Create_And_Destroy_Transitions();
|
||
|
|
||
|
Enable_Engine(true);
|
||
|
|
||
|
if ( Get_Definition().OccupantsVisible == false ) {
|
||
|
occupant->Peek_Model()->Set_Hidden( true );
|
||
|
}
|
||
|
|
||
|
// Notify the observers that someone entered
|
||
|
const GameObjObserverList & observer_list = Get_Observers();
|
||
|
for( int index = 0; index < observer_list.Count(); index++ ) {
|
||
|
observer_list[ index ]->Custom( this, CUSTOM_EVENT_VEHICLE_ENTERED, seat_id, occupant );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reveal this vehicle to the player if he was the one to enter the vehicle
|
||
|
//
|
||
|
if ( occupant == COMBAT_STAR ) {
|
||
|
EncyclopediaMgrClass::Reveal_Object( this );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reset any action the vehicle was performing
|
||
|
//
|
||
|
if (Get_Action() != NULL) {
|
||
|
Get_Action()->Reset(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unlock the vehicle!
|
||
|
//
|
||
|
LockTimer = 0.0f;
|
||
|
LockOwner = NULL;
|
||
|
}
|
||
|
|
||
|
int VehicleGameObj::Find_Seat( SoldierGameObj * occupant )
|
||
|
{
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] == occupant ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Remove_Occupant( SoldierGameObj * occupant )
|
||
|
{
|
||
|
WWASSERT( occupant );
|
||
|
|
||
|
int seat_num = Find_Seat( occupant );
|
||
|
|
||
|
if ( seat_num == -1 ) {
|
||
|
Debug_Say(( "Object is not in this vehicle\n" ));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do this anyway, just to make sure it happens.
|
||
|
if ( Get_Definition().OccupantsVisible == false ) {
|
||
|
if ( SeatOccupants[seat_num] && SeatOccupants[seat_num]->Peek_Model() ) {
|
||
|
SeatOccupants[seat_num]->Peek_Model()->Set_Hidden( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Debug_Say(( "Soldier %p removed from seat %d\n", occupant, seat_num ));
|
||
|
|
||
|
SeatOccupants[seat_num]->Exit_Vehicle();
|
||
|
SeatOccupants[seat_num] = NULL;
|
||
|
OccupiedSeats--;
|
||
|
|
||
|
// in MP, empty vehicles are neutral
|
||
|
if ( !IS_MISSION && OccupiedSeats == 0 ) {
|
||
|
Set_Player_Type( PLAYERTYPE_NEUTRAL );
|
||
|
}
|
||
|
|
||
|
Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
|
||
|
|
||
|
// possibily re-enable transitions
|
||
|
Create_And_Destroy_Transitions();
|
||
|
|
||
|
if ( seat_num == 0 ) {
|
||
|
Enable_Engine(false);
|
||
|
}
|
||
|
|
||
|
// Notify the observers that someone left
|
||
|
const GameObjObserverList & observer_list = Get_Observers();
|
||
|
for( int index = 0; index < observer_list.Count(); index++ ) {
|
||
|
observer_list[ index ]->Custom( this, CUSTOM_EVENT_VEHICLE_EXITED, seat_num, occupant );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObj::Contains_Occupant( SoldierGameObj * occupant )
|
||
|
{
|
||
|
WWASSERT( occupant );
|
||
|
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] == occupant ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int VehicleGameObj::Get_Occupant_Count(void)
|
||
|
{
|
||
|
int count = 0;
|
||
|
for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
|
||
|
if ( SeatOccupants[i] != NULL ) {
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObj::Is_Entry_Permitted( SoldierGameObj * p_soldier )
|
||
|
{
|
||
|
if (!CombatManager::Is_Gameplay_Permitted()) {
|
||
|
//
|
||
|
// You can't enter a vehicle if there are no opponents, etc
|
||
|
//
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Do not permit a soldier to enter a vehicle if there is already
|
||
|
// another soldier of a different player type in the vehicle.
|
||
|
// Actually, only allow 2 Nods or 2 GDI's to share a vehicle...
|
||
|
//
|
||
|
|
||
|
WWASSERT(p_soldier != NULL);
|
||
|
|
||
|
bool is_permitted = true;
|
||
|
|
||
|
int player_type = p_soldier->Get_Player_Type();
|
||
|
|
||
|
int i;
|
||
|
for (i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
int seated_pt = SeatOccupants[i]->Get_Player_Type();
|
||
|
|
||
|
if (seated_pt != player_type ||
|
||
|
(seated_pt != PLAYERTYPE_NOD && seated_pt != PLAYERTYPE_GDI)) {
|
||
|
|
||
|
is_permitted = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If there are no entry transitions, entry is not possible
|
||
|
bool entry_exists = false;
|
||
|
for ( i = 0; i < TransitionInstances.Count(); i++ ) {
|
||
|
// if ( ( TransitionInstances[i]->Get_Type() == TransitionDataClass::LEGACY_VEHICLE_ENTER_0 ) ||
|
||
|
// ( TransitionInstances[i]->Get_Type() == TransitionDataClass::LEGACY_VEHICLE_ENTER_1 ) ) {
|
||
|
if ( TransitionInstances[i]->Get_Type() == TransitionDataClass::VEHICLE_ENTER ) {
|
||
|
entry_exists = true;
|
||
|
}
|
||
|
}
|
||
|
if ( !entry_exists ) {
|
||
|
is_permitted = false;
|
||
|
}
|
||
|
|
||
|
// If the vehicle is locked and you are not the lock owner, entry is not permitted
|
||
|
if (Is_Locked() && (p_soldier != LockOwner.Get_Ptr())) {
|
||
|
is_permitted = false;
|
||
|
}
|
||
|
|
||
|
return is_permitted;
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Remove_Transitions( TransitionDataClass::StyleType transition_type )
|
||
|
{
|
||
|
for ( int i = 0; i < TransitionInstances.Count(); i++ ) {
|
||
|
if ( TransitionInstances[i]->Get_Type() == transition_type ) {
|
||
|
TransitionManager::Destroy( TransitionInstances[i] );
|
||
|
TransitionInstances.Delete( i );
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Create_New_Transitions( TransitionDataClass::StyleType transition_type )
|
||
|
{
|
||
|
const TRANSITION_DATA_LIST & trans_data_list = Get_Definition().Get_Transition_List();
|
||
|
|
||
|
for ( int i = 0; i < trans_data_list.Count(); i++ ) {
|
||
|
if ( trans_data_list[i]->Get_Type() == transition_type ) {
|
||
|
// make new instance
|
||
|
TransitionInstanceClass * trans = new TransitionInstanceClass( *trans_data_list[i] );
|
||
|
// setup
|
||
|
trans->Set_Vehicle( this );
|
||
|
// add to our list
|
||
|
TransitionInstances.Add( trans );
|
||
|
// add to master list
|
||
|
TransitionManager::Add( trans );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Transitions( void )
|
||
|
{
|
||
|
Matrix3D tm = Get_Transform();
|
||
|
|
||
|
// Update all transitions
|
||
|
for ( int i = 0; i < TransitionInstances.Count(); i++ ) {
|
||
|
TransitionInstances[i]->Set_Parent_Transform( tm );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void VehicleGameObj::Passenger_Entering( void )
|
||
|
{
|
||
|
// Play the passenger entering animation
|
||
|
char string[80];
|
||
|
sprintf( string, "V_%sL1.M_%sCL", Get_Definition().TypeName, Get_Definition().TypeName );
|
||
|
Set_Animation( string );
|
||
|
Get_Anim_Control()->Set_Mode( ANIM_MODE_ONCE );
|
||
|
//Debug_Say(("VehicleGameObj::Passenger_Entering\n"));
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Passenger_Exiting( void )
|
||
|
{
|
||
|
// Play the passenger leaving animation
|
||
|
char string[80];
|
||
|
sprintf( string, "V_%sL1.M_%sOP", Get_Definition().TypeName, Get_Definition().TypeName );
|
||
|
Set_Animation( string );
|
||
|
Get_Anim_Control()->Set_Mode( ANIM_MODE_ONCE );
|
||
|
Control.Clear_Control();
|
||
|
}
|
||
|
|
||
|
//void VehicleGameObj::Get_Extended_Information(StringClass & description)
|
||
|
void VehicleGameObj::Get_Description(StringClass & description)
|
||
|
{
|
||
|
StringClass line;
|
||
|
|
||
|
line.Format("ID: %d\n", Get_ID());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("NAME: %s\n", Get_Definition().Get_Name());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("TEAM: %d\n", Get_Player_Type());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("CONTR: %d\n", Get_Control_Owner());
|
||
|
description += line;
|
||
|
|
||
|
Vector3 position;
|
||
|
Get_Position(&position);
|
||
|
line.Format("POS: %-5.2f, %-5.2f, %-5.2f\n", position.X, position.Y, position.Z);
|
||
|
description += line;
|
||
|
|
||
|
Vector3 target = Get_Targeting_Pos();
|
||
|
line.Format("TGT: %-5.2f, %-5.2f, %-5.2f\n", target.X, target.Y, target.Z);
|
||
|
description += line;
|
||
|
|
||
|
Vector3 velocity;
|
||
|
Get_Velocity(velocity);
|
||
|
line.Format("VEL: %-5.2f, %-5.2f, %-5.2f\n", velocity.X, velocity.Y, velocity.Z);
|
||
|
description += line;
|
||
|
|
||
|
line.Format("SPD: %-5.2f\n", velocity.Length());
|
||
|
description += line;
|
||
|
|
||
|
WeaponClass * p_weapon = Get_Weapon();
|
||
|
if (p_weapon != NULL) {
|
||
|
line.Format("WEAP: %s\n", p_weapon->Get_Name());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("RNDS: %d\n", p_weapon->Get_Total_Rounds());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
if (Get_Defense_Object() != NULL) {
|
||
|
line.Format("HLTH: %-5.2f\n", Get_Defense_Object()->Get_Health());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
if (Get_Driver() != NULL) {
|
||
|
line.Format("DRVR: %d\n", Get_Driver()->Get_ID());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
if (Get_Gunner() != NULL) {
|
||
|
line.Format("GUNR: %d\n", Get_Gunner()->Get_ID());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format("HIB: %d\n", Is_Hibernating());
|
||
|
description += line;
|
||
|
|
||
|
ControlClass & control = Get_Control();
|
||
|
line.Format("CTRL: %d, %d, %5.2f, %5.2f, %5.2f, %5.2f\n",
|
||
|
control.Get_One_Time_Boolean_Bits(),
|
||
|
control.Get_Continuous_Boolean_Bits(),
|
||
|
control.Get_Analog(ControlClass::ANALOG_MOVE_FORWARD),
|
||
|
control.Get_Analog(ControlClass::ANALOG_MOVE_LEFT),
|
||
|
control.Get_Analog(ControlClass::ANALOG_MOVE_UP),
|
||
|
control.Get_Analog(ControlClass::ANALOG_TURN_LEFT));
|
||
|
description += line;
|
||
|
|
||
|
if (Get_Action() != NULL) {
|
||
|
|
||
|
line.Format("ACTCT: %d\n", Get_Action()->Get_Act_Count());
|
||
|
description += line;
|
||
|
|
||
|
Vector3 move_loc = Get_Action()->Get_Parameters().MoveLocation;
|
||
|
line.Format("MVLOC: (%5.2f, %5.2f, %5.2f)\n", move_loc.X, move_loc.Y, move_loc.Z);
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
line.Format ("ATTCH: %d\n", Is_Attached_To_An_Object ());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("stlth: %d\n", Is_Stealth_Enabled());
|
||
|
description += line;
|
||
|
|
||
|
line.Format(" on: %d\n", Is_Stealthed());
|
||
|
description += line;
|
||
|
|
||
|
line.Format("ISC: %d\n", Get_Import_State_Count());
|
||
|
description += line;
|
||
|
}
|
||
|
|
||
|
bool VehicleGameObj::Is_Engine_Enabled (void) const
|
||
|
{
|
||
|
if (Peek_Vehicle_Phys() != NULL) {
|
||
|
return Peek_Vehicle_Phys()->Is_Engine_Enabled();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Enable_Engine (bool onoff)
|
||
|
{
|
||
|
if (Peek_Vehicle_Phys() != NULL) {
|
||
|
Peek_Vehicle_Phys()->Enable_Engine(onoff);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Set_Precision(void)
|
||
|
{
|
||
|
cEncoderList::Set_Precision(BITPACK_VEHICLE_VELOCITY,
|
||
|
-90.0f, 90.0f, 0.01f); // 200 mph = approx. 90 m/s
|
||
|
cEncoderList::Set_Precision(BITPACK_VEHICLE_ANGULAR_VELOCITY,
|
||
|
-20.0f, 20.0f, 0.01f); // to approx 6 PI
|
||
|
cEncoderList::Set_Precision(BITPACK_VEHICLE_QUATERNION,
|
||
|
-1.0f, 1.0f, 0.0005f); // -1 to 1
|
||
|
cEncoderList::Set_Precision(BITPACK_VEHICLE_LOCK_TIMER,
|
||
|
0.0f, 16.0f, 0.25f);
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin )
|
||
|
{
|
||
|
float starting_health = Get_Defense_Object()->Get_Health();
|
||
|
|
||
|
//
|
||
|
// Let the base class actually handle the call
|
||
|
//
|
||
|
SmartGameObj::Apply_Damage( damager, scale, alternate_skin );
|
||
|
|
||
|
// Stats
|
||
|
if ( starting_health > 0 && Get_Defense_Object()->Get_Health() <= 0 ) {
|
||
|
if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() != NULL ) {
|
||
|
if ( damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data() != NULL ) {
|
||
|
damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data()->Stats_Add_Vehicle_Destroyed();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now make sure the model is in a state to match its damage level
|
||
|
//
|
||
|
Update_Damage_Meshes();
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Update_Damage_Meshes( void )
|
||
|
{
|
||
|
//
|
||
|
// Calculate what percent health we have after this damage
|
||
|
//
|
||
|
DefenseObjectClass *defense_object = Get_Defense_Object();
|
||
|
if ( defense_object != NULL ) {
|
||
|
float ending_health = defense_object->Get_Health() / defense_object->Get_Health_Max();
|
||
|
|
||
|
//
|
||
|
// Make sure we have a render object to lookup bones on
|
||
|
//
|
||
|
RenderObjClass *model = Peek_Model ();
|
||
|
if (model != NULL) {
|
||
|
bool show_damage25 = false;
|
||
|
bool show_damage50 = false;
|
||
|
bool show_damage75 = false;
|
||
|
|
||
|
//
|
||
|
// Determine what the visibility state of these bones should be
|
||
|
//
|
||
|
if (ending_health < 0.25F) {
|
||
|
show_damage25 = true;
|
||
|
show_damage50 = true;
|
||
|
show_damage75 = true;
|
||
|
} else if (ending_health < 0.5F) {
|
||
|
show_damage25 = true;
|
||
|
show_damage50 = true;
|
||
|
} else if (ending_health < 0.75F) {
|
||
|
show_damage25 = true;
|
||
|
}
|
||
|
|
||
|
static const char * DAMAGE25_BONE_NAME = "DAMAGE25";
|
||
|
static const char * DAMAGE50_BONE_NAME = "DAMAGE50";
|
||
|
static const char * DAMAGE75_BONE_NAME = "DAMAGE75";
|
||
|
static int DAMAGE25_BONE_NAME_LEN = ::strlen( DAMAGE25_BONE_NAME );
|
||
|
static int DAMAGE50_BONE_NAME_LEN = ::strlen( DAMAGE50_BONE_NAME );
|
||
|
static int DAMAGE75_BONE_NAME_LEN = ::strlen( DAMAGE75_BONE_NAME );
|
||
|
|
||
|
//
|
||
|
// Loop over all the bones in the model, showing and hiding any that represent
|
||
|
// damage levels
|
||
|
//
|
||
|
int bone_count = model->Get_Num_Bones();
|
||
|
for ( int index = 0; index < bone_count; index ++) {
|
||
|
const char *bone_name = model->Get_Bone_Name( index );
|
||
|
|
||
|
if ( ::strnicmp( bone_name, DAMAGE25_BONE_NAME, DAMAGE25_BONE_NAME_LEN ) == 0 ) {
|
||
|
|
||
|
//
|
||
|
// Show/hide the subobjects associated with 25% damage
|
||
|
//
|
||
|
::Set_Subobject_Visibility( model, index, show_damage25 );
|
||
|
} else if ( ::strnicmp( bone_name, DAMAGE50_BONE_NAME, DAMAGE50_BONE_NAME_LEN ) == 0 ) {
|
||
|
|
||
|
//
|
||
|
// Show/hide the subobjects associated with 50% damage
|
||
|
//
|
||
|
::Set_Subobject_Visibility( model, index, show_damage50 );
|
||
|
} else if ( ::strnicmp( bone_name, DAMAGE75_BONE_NAME, DAMAGE75_BONE_NAME_LEN ) == 0 ) {
|
||
|
|
||
|
//
|
||
|
// Show/hide the subobjects associated with 75% damage
|
||
|
//
|
||
|
::Set_Subobject_Visibility( model, index, show_damage75 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Set_Subobject_Visibility (RenderObjClass *model, int bone_index, bool show)
|
||
|
{
|
||
|
WWASSERT( model != NULL );
|
||
|
|
||
|
//
|
||
|
// Loop over all the subobjects that are attached to this bone
|
||
|
//
|
||
|
int count = model->Get_Num_Sub_Objects_On_Bone( bone_index );
|
||
|
for (int index = 0; index < count; index ++) {
|
||
|
RenderObjClass *sub_obj = model->Get_Sub_Object_On_Bone(index, bone_index);
|
||
|
if (sub_obj != NULL) {
|
||
|
|
||
|
//
|
||
|
// Show or hide this subobject
|
||
|
//
|
||
|
sub_obj->Set_Hidden (!show);
|
||
|
sub_obj->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
Matrix3D VehicleGameObj::Get_Look_Transform(void)
|
||
|
{
|
||
|
if ( Get_Definition().SightDownMuzzle ) {
|
||
|
return Get_Muzzle();
|
||
|
}
|
||
|
return SmartGameObj::Get_Look_Transform();
|
||
|
}
|
||
|
|
||
|
const WCHAR * VehicleGameObj::Get_Vehicle_Name( void )
|
||
|
{
|
||
|
if ( Get_Definition().VehicleNameID != 0 ) {
|
||
|
return TranslateDBClass::Get_String( Get_Definition().VehicleNameID );
|
||
|
}
|
||
|
return TRANSLATE(IDS_HUD_VEHICLE_NAME);
|
||
|
}
|
||
|
|
||
|
// Vehicles should explode when expired / flipped
|
||
|
ExpirationReactionType VehicleGameObj::Object_Expired( PhysClass * observed_obj )
|
||
|
{
|
||
|
// Only on the server
|
||
|
if ( CombatManager::I_Am_Server() ) {
|
||
|
|
||
|
if ( Get_Definition().KilledExplosion != 0 && !Is_Delete_Pending() ) {
|
||
|
Vector3 pos;
|
||
|
Get_Position(&pos);
|
||
|
WWASSERT(pos.Is_Valid());// most likely candidate for explosion damage bug....?
|
||
|
ExplosionManager::Server_Explode( Get_Definition().KilledExplosion,pos,0 );
|
||
|
}
|
||
|
|
||
|
if ( !Is_Delete_Pending() ) {
|
||
|
// notify the observers of killed
|
||
|
const GameObjObserverList & observer_list = Get_Observers();
|
||
|
for( int index = 0; index < observer_list.Count(); index++ ) {
|
||
|
observer_list[ index ]->Killed( this, NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return SmartGameObj::Object_Expired( observed_obj );
|
||
|
|
||
|
} else {
|
||
|
return EXPIRATION_DENIED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
SoldierGameObj * VehicleGameObj::Get_Driver(void)
|
||
|
{
|
||
|
if ( SeatOccupants.Length() > DRIVER_SEAT ) {
|
||
|
return SeatOccupants[DRIVER_SEAT];
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SoldierGameObj * VehicleGameObj::Get_Gunner(void)
|
||
|
{
|
||
|
if ( SeatOccupants.Length() > GUNNER_SEAT ) {
|
||
|
return SeatOccupants[GUNNER_SEAT];
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SoldierGameObj * VehicleGameObj::Get_Actual_Gunner(void)
|
||
|
{
|
||
|
SoldierGameObj * gunner = Get_Gunner();
|
||
|
if ( gunner && !Get_Driver_Is_Gunner() ) {
|
||
|
return gunner;
|
||
|
}
|
||
|
return Get_Driver();
|
||
|
}
|
||
|
|
||
|
float VehicleGameObj::Get_Stealth_Fade_Distance(void) const
|
||
|
{
|
||
|
if (IS_MISSION) {
|
||
|
return GlobalSettingsDef::Get_Global_Settings()->Get_Stealth_Distance_Vehicle();
|
||
|
} else {
|
||
|
return GlobalSettingsDef::Get_Global_Settings()->Get_MP_Stealth_Distance_Vehicle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void VehicleGameObj::Set_Vehicle_Delivered(void)
|
||
|
{
|
||
|
VehicleDelivered = true;
|
||
|
Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
float VehicleGameObj::Get_Filter_Distance(void) const
|
||
|
{
|
||
|
if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
|
||
|
WeaponClass *weapon = ((VehicleGameObj*)this)->Get_Weapon();
|
||
|
float range = Get_Definition().SightRange;
|
||
|
if (weapon != NULL) {
|
||
|
range = min(weapon->Get_Range(), range);
|
||
|
}
|
||
|
return(range);
|
||
|
}
|
||
|
return(SmartGameObj::Get_Filter_Distance());
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void VehicleGameObj::Ignore_Occupants( void )
|
||
|
{
|
||
|
for (int i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
if ( SeatOccupants[i]->Peek_Physical_Object() != NULL ) {
|
||
|
SeatOccupants[i]->Peek_Physical_Object()->Inc_Ignore_Counter();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void VehicleGameObj::Unignore_Occupants( void )
|
||
|
{
|
||
|
for (int i = 0; i < SeatOccupants.Length(); i++) {
|
||
|
if (SeatOccupants[i] != NULL) {
|
||
|
if ( SeatOccupants[i]->Peek_Physical_Object() != NULL ) {
|
||
|
SeatOccupants[i]->Peek_Physical_Object()->Dec_Ignore_Counter();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Vector3 pos_error = current_position - sc_position;
|
||
|
|
||
|
if (pos_error.Length() > 2.0f && Is_Attached_To_An_Object () == false) {
|
||
|
Matrix3D tm(q, sc_position);
|
||
|
p_obj->Set_Transform(tm);
|
||
|
p_obj->Set_Velocity(vel);
|
||
|
p_obj->Set_Angular_Velocity(ang_vel);
|
||
|
} else if (Is_Attached_To_An_Object ()) {
|
||
|
Debug_Say(( "Skipping vehicle position update - attached to bone.\n" ));
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
// New temporary scheme : constantly correct to server position by linearly
|
||
|
// interpolating a fraction of the error
|
||
|
//
|
||
|
Matrix3D dest_tm(q, sc_position);
|
||
|
Matrix3D tm = ::Lerp(Get_Transform (), dest_tm, 0.1F);
|
||
|
p_obj->Set_Transform(tm);
|
||
|
p_obj->Set_Velocity(vel);
|
||
|
p_obj->Set_Angular_Velocity(ang_vel);
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
packet.Add_Vector3(pos);
|
||
|
packet.Add_Quaternion(q);
|
||
|
packet.Add_Vector3(vel);
|
||
|
packet.Add_Vector3(ang_vel);
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
void VehicleGameObj::Import_State_Sc(BitStreamClass & packet)
|
||
|
{
|
||
|
WWASSERT(CombatManager::I_Am_Only_Client());
|
||
|
|
||
|
if (Get_Weapon() != NULL) {
|
||
|
int rounds = packet.Get(rounds);
|
||
|
Get_Weapon()->Set_Total_Rounds(rounds);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < NUM_SEATS; i++) {
|
||
|
int occupant = packet.Get(occupant);
|
||
|
if (occupant == -1) {
|
||
|
Seats[i] = NULL;
|
||
|
} else {
|
||
|
if ((Seats[i] == NULL) || (Seats[i]->Get_ID() != occupant)) {
|
||
|
SmartGameObj * obj = GameObjManager::Find_SmartGameObj(occupant);
|
||
|
Seats[i] = NULL;
|
||
|
if (obj != NULL) {
|
||
|
Seats[i] = obj->As_SoldierGameObj();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(Get_Definition().Type) {
|
||
|
|
||
|
case VEHICLE_TYPE_BIKE:
|
||
|
case VEHICLE_TYPE_CAR:
|
||
|
case VEHICLE_TYPE_TANK:
|
||
|
{
|
||
|
VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
|
||
|
WWASSERT(p_obj != NULL);
|
||
|
|
||
|
Vector3 sc_position;
|
||
|
Quaternion q;
|
||
|
Vector3 vel;
|
||
|
Vector3 ang_vel;
|
||
|
|
||
|
//packet.Get_Vector3(pos);
|
||
|
//packet.Get_Quaternion(q);
|
||
|
//packet.Get_Vector3(vel);
|
||
|
//packet.Get_Vector3(ang_vel);
|
||
|
|
||
|
packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
|
||
|
packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
|
||
|
packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
|
||
|
|
||
|
packet.Get(q.X, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.Y, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.Z, BITPACK_VEHICLE_QUATERNION);
|
||
|
packet.Get(q.W, BITPACK_VEHICLE_QUATERNION);
|
||
|
|
||
|
packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
|
||
|
packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
|
||
|
|
||
|
packet.Get(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Get(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
packet.Get(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
|
||
|
|
||
|
switch (CombatManager::Get_Client_Interpolation_Model()) {
|
||
|
|
||
|
case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE:
|
||
|
case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE_WITH_BLENDING:
|
||
|
{
|
||
|
//Matrix3D tm(q, sc_position);
|
||
|
//p_obj->Set_Transform(tm);
|
||
|
//p_obj->Set_Velocity(vel);
|
||
|
//p_obj->Set_Angular_Velocity(ang_vel);
|
||
|
|
||
|
Vector3 current_position;
|
||
|
Get_Position(¤t_position);
|
||
|
Vector3 pos_error = current_position - sc_position;
|
||
|
if (pos_error.Length() > 2.0f) {
|
||
|
Matrix3D tm(q, sc_position);
|
||
|
p_obj->Set_Transform(tm);
|
||
|
p_obj->Set_Velocity(vel);
|
||
|
p_obj->Set_Angular_Velocity(ang_vel);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CLIENT_INTERPOLATION_PATHFIND: {
|
||
|
|
||
|
SoldierGameObj * p_driver = Seats[DRIVER_SEAT];
|
||
|
if (p_driver != NULL && p_driver->Get_Control_Owner() == CombatManager::Get_My_Id()) {
|
||
|
|
||
|
Matrix3D tm(q, sc_position);
|
||
|
p_obj->Set_Transform(tm);
|
||
|
p_obj->Set_Velocity(vel);
|
||
|
p_obj->Set_Angular_Velocity(ang_vel);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
ActionParamsStruct parameters;
|
||
|
parameters.Priority = 1;
|
||
|
parameters.MoveLocation = sc_position;
|
||
|
parameters.MoveArrivedDistance = 0.1f;
|
||
|
WWASSERT(Get_Action() != NULL);
|
||
|
Get_Action()->Goto(parameters);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
WWASSERT(0);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case VEHICLE_TYPE_FLYING:
|
||
|
break;
|
||
|
|
||
|
case VEHICLE_TYPE_TURRET:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WWASSERT( 0 );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
SmartGameObj::Import_State_Sc(packet);
|
||
|
|
||
|
WWASSERT(packet.Is_Flushed());
|
||
|
}
|
||
|
*/
|