1952 lines
53 KiB
C++
1952 lines
53 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/weapons.cpp $*
|
||
|
* *
|
||
|
* $Author:: Byon_g $*
|
||
|
* *
|
||
|
* $Modtime:: 3/27/02 4:43p $*
|
||
|
* *
|
||
|
* $Revision:: 270 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
|
||
|
#include "weapons.h"
|
||
|
#include "weaponmanager.h"
|
||
|
#include "debug.h"
|
||
|
#include "soldier.h"
|
||
|
#include "combat.h"
|
||
|
#include "gameobjmanager.h"
|
||
|
#include "line3d.h"
|
||
|
#include "physcoltest.h"
|
||
|
#include "pscene.h"
|
||
|
#include "rendobj.h"
|
||
|
#include "quat.h"
|
||
|
#include "combatsound.h"
|
||
|
#include "slnode.h"
|
||
|
#include "scripts.h"
|
||
|
#include "WWAudio.H"
|
||
|
#include "Sound3D.H"
|
||
|
#include "chunkio.h"
|
||
|
#include "assets.h"
|
||
|
#include "bullet.h"
|
||
|
#include "crandom.h"
|
||
|
#include "damage.h"
|
||
|
#include "ccamera.h"
|
||
|
#include "rbody.h"
|
||
|
#include "timeddecophys.h"
|
||
|
#include "wwphysids.h"
|
||
|
#include "part_emt.h"
|
||
|
#include "vehicle.h"
|
||
|
#include "c4.h"
|
||
|
#include "objlibrary.h"
|
||
|
#include "wwprofile.h"
|
||
|
#include "hudinfo.h"
|
||
|
#include "projectile.h"
|
||
|
#include "combatphysobserver.h"
|
||
|
#include "surfaceeffects.h"
|
||
|
#include "beacongameobj.h"
|
||
|
#include "weaponview.h"
|
||
|
#include "diaglog.h"
|
||
|
#include "playerdata.h"
|
||
|
#include "cheatmgr.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
#define RANDOM_VECTOR( spread ) Vector3( FreeRandom.Get_Float( -(spread), (spread) ), \
|
||
|
FreeRandom.Get_Float( -(spread), (spread) ), \
|
||
|
FreeRandom.Get_Float( -(spread), (spread) ) )
|
||
|
|
||
|
|
||
|
/**
|
||
|
** EjectCasingObserverClass
|
||
|
** This is a class which observes all eject casings and creates sound effects when they
|
||
|
** collide. Since the task of this observer is so simple, I am creating a single static
|
||
|
** instance of it and pluging it into all eject casings.
|
||
|
*/
|
||
|
class EjectCasingObserverClass : public CombatPhysObserverClass
|
||
|
{
|
||
|
public:
|
||
|
virtual CollisionReactionType Collision_Occurred(const CollisionEventClass & event)
|
||
|
{
|
||
|
/*
|
||
|
SurfaceEffectsManager::Apply_Effect(event.CollisionResult->SurfaceType,
|
||
|
SurfaceEffectsManager::HITTER_TYPE_EJECT_CASING,
|
||
|
Matrix3D(event.CollisionResult->ContactPoint),
|
||
|
event.OtherObj, NULL, false);
|
||
|
*/
|
||
|
return COLLISION_REACTION_DEFAULT;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
static EjectCasingObserverClass _TheEjectCasingObserver;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
WeaponClass::WeaponClass( const WeaponDefinitionClass *def ) :
|
||
|
Definition( NULL ),
|
||
|
PrimaryAmmoDefinition( NULL ),
|
||
|
SecondaryAmmoDefinition( NULL ),
|
||
|
Model( NULL ),
|
||
|
State( STATE_IDLE ),
|
||
|
StateTimer( 0.0f ),
|
||
|
UpdateModel( WEAPON_MODEL_UPDATE_IS_NEEDED ),
|
||
|
NextAnimState( WEAPON_ANIM_NOT_FIRING ),
|
||
|
CurrentAnimState( WEAPON_ANIM_NOT_FIRING ),
|
||
|
LastFrameIsPrimaryTriggered( false ),
|
||
|
LastFrameIsSecondaryTriggered( false ),
|
||
|
IsPrimaryTriggered( false ),
|
||
|
IsSecondaryTriggered( false ),
|
||
|
TotalRoundsFired( 0 ),
|
||
|
ClipRounds( 0 ),
|
||
|
InventoryRounds( 0 ),
|
||
|
BurstDelayTimer( 0 ),
|
||
|
BurstCount( 0 ),
|
||
|
BulletBumpTime( 0 ),
|
||
|
DidFire( false ),
|
||
|
ContinuousEmitters( NULL ),
|
||
|
ContinuousSound( NULL ),
|
||
|
C4DetonationMode( 1 ),
|
||
|
Target( 0, 0, 0 ),
|
||
|
FiringSound( NULL ),
|
||
|
FiringSoundDefID( 0 ),
|
||
|
WeaponExists( true ),
|
||
|
SafetySet( false ),
|
||
|
LockTriggers( false ),
|
||
|
EmptySoundTimer( 0 )
|
||
|
{
|
||
|
if ( def != NULL ) {
|
||
|
Init( def );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WeaponClass::~WeaponClass( void )
|
||
|
{
|
||
|
if (FiringSound != NULL) {
|
||
|
FiringSound->Remove_From_Scene ();
|
||
|
FiringSoundDefID = 0;
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE( Model );
|
||
|
REF_PTR_RELEASE( FiringSound );
|
||
|
|
||
|
Do_Continuous_Effects( false );
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Init( const WeaponDefinitionClass *weapon_def )
|
||
|
{
|
||
|
WWASSERT( Definition == NULL );
|
||
|
Definition = weapon_def;
|
||
|
WWASSERT( Definition != NULL );
|
||
|
|
||
|
// Setup the ammo defs
|
||
|
WWASSERT( Definition->PrimaryAmmoDefID != 0 );
|
||
|
PrimaryAmmoDefinition = WeaponManager::Find_Ammo_Definition( Definition->PrimaryAmmoDefID );
|
||
|
|
||
|
WWASSERT( Definition->SecondaryAmmoDefID != 0 );
|
||
|
SecondaryAmmoDefinition = WeaponManager::Find_Ammo_Definition( Definition->SecondaryAmmoDefID );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Weapon Save and Load
|
||
|
*/
|
||
|
|
||
|
enum {
|
||
|
CHUNKID_VARIABLES = 910991544,
|
||
|
XXX_CHUNKID_AMMO,
|
||
|
CHUNKID_OWNER_REF,
|
||
|
XXXCHUNKID_TARGET_REF,
|
||
|
XXXCHUNKID_DESIRED_TARGET_REF,
|
||
|
CHUNKID_TARGET_OBJECT,
|
||
|
|
||
|
MICROCHUNKID_IN_FLY_MODE = 1,
|
||
|
MICROCHUNKID_STATE,
|
||
|
MICROCHUNKID_STATE_TIMER,
|
||
|
MICROCHUNKID_IS_PRIMARY_TRIGGERED,
|
||
|
XXXMICROCHUNKID_IS_SNIPING,
|
||
|
MICROCHUNKID_ROUNDS_FIRED,
|
||
|
MICROCHUNKID_NEXT_ANIM_STATE,
|
||
|
MICROCHUNKID_CURR_ANIM_STATE,
|
||
|
MICROCHUNKID_UPDATE_MODEM,
|
||
|
XXXXMICROCHUNKID_LAST_MUZZLE,
|
||
|
XXXMICROCHUNKID_PENDING_TIMER,
|
||
|
XXXMICROCHUNKID_TARGETING_MUZZLE,
|
||
|
XXXMICROCHUNKID_TARGET_POSITION,
|
||
|
XXXMICROCHUNKID_WEAPON_PARAMS_NAME,
|
||
|
XXXMICROCHUNKID_MODEL_NAME,
|
||
|
MICROCHUNKID_MODEL_PTR,
|
||
|
XXXMICROCHUNKID_DYNAMIC_ERROR_ANGLE,
|
||
|
MICROCHUNKID_CLIP_ROUNDS,
|
||
|
MICROCHUNKID_INVENTORY_ROUNDS,
|
||
|
MICROCHUNKID_BURST_TIMER,
|
||
|
MICROCHUNKID_BURST_COUNT,
|
||
|
XXX_MICROCHUNKID_AMMO_PARAMS_NAME,
|
||
|
XXXMICROCHUNKID_AMMO_DEFINITION_NAME,
|
||
|
XXXMICROCHUNKID_DEFINITION_NAME,
|
||
|
XXXMICROCHUNKID_AMMO_DEFINITION_ID,
|
||
|
MICROCHUNKID_DEFINITION_ID,
|
||
|
MICROCHUNKID_IS_SECONDARY_TRIGGERED,
|
||
|
MICROCHUNKID_C4_DETONATION_MODE,
|
||
|
MICROCHUNKID_TARGET_LOCATION,
|
||
|
MICROCHUNKID_WEAPON_EXISTS,
|
||
|
MICROCHUNKID_SAFETY_SET,
|
||
|
XXXMICROCHUNKID_OWNER_ERROR_ANGLE,
|
||
|
};
|
||
|
|
||
|
bool WeaponClass::Save( ChunkSaveClass & csave )
|
||
|
{
|
||
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
||
|
|
||
|
if ( Model ) {
|
||
|
#if 0
|
||
|
csave.Begin_Micro_Chunk( MICROCHUNKID_MODEL_NAME );
|
||
|
const char * model_name = Model->Get_Name();
|
||
|
csave.Write( model_name, strlen( model_name ) + 1 );
|
||
|
csave.End_Micro_Chunk();
|
||
|
#endif
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MODEL_PTR, Model );
|
||
|
}
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_TIMER, StateTimer );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_PRIMARY_TRIGGERED, IsPrimaryTriggered );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_SECONDARY_TRIGGERED, IsSecondaryTriggered );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ROUNDS_FIRED, TotalRoundsFired );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_NEXT_ANIM_STATE, NextAnimState );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CURR_ANIM_STATE, CurrentAnimState );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_UPDATE_MODEM, UpdateModel );
|
||
|
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_CLIP_ROUNDS, ClipRounds, int );
|
||
|
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_INVENTORY_ROUNDS, InventoryRounds, int );
|
||
|
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_BURST_TIMER, BurstDelayTimer, float );
|
||
|
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_BURST_COUNT, BurstCount, int );
|
||
|
int def_id = Definition->Get_ID();
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEFINITION_ID, def_id );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_C4_DETONATION_MODE, C4DetonationMode );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TARGET_LOCATION, Target );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_EXISTS, WeaponExists );
|
||
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SAFETY_SET, SafetySet );
|
||
|
|
||
|
csave.End_Chunk();
|
||
|
|
||
|
if ( Owner != NULL ) {
|
||
|
csave.Begin_Chunk( CHUNKID_OWNER_REF );
|
||
|
Owner.Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
if ( TargetObject != NULL ) {
|
||
|
csave.Begin_Chunk( CHUNKID_TARGET_OBJECT );
|
||
|
TargetObject.Save( csave );
|
||
|
csave.End_Chunk();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Don't need to save DidFire
|
||
|
// Don't need to save ContinuousEmitters;
|
||
|
// Don't need to save ContinuousSound;
|
||
|
// Don't need to save FiringSound;
|
||
|
// Dont need to save BulletBumpTime
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool WeaponClass::Load( ChunkLoadClass &cload )
|
||
|
{
|
||
|
while (cload.Open_Chunk()) {
|
||
|
switch(cload.Cur_Chunk_ID()) {
|
||
|
|
||
|
case CHUNKID_VARIABLES:
|
||
|
{
|
||
|
int def_id = 0;
|
||
|
|
||
|
while (cload.Open_Micro_Chunk()) {
|
||
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_TIMER, StateTimer );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_PRIMARY_TRIGGERED, IsPrimaryTriggered );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_SECONDARY_TRIGGERED, IsSecondaryTriggered );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_ROUNDS_FIRED, TotalRoundsFired );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_NEXT_ANIM_STATE, NextAnimState );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_CURR_ANIM_STATE, CurrentAnimState );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_UPDATE_MODEM, UpdateModel );
|
||
|
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_CLIP_ROUNDS, ClipRounds, int );
|
||
|
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_INVENTORY_ROUNDS, InventoryRounds, int );
|
||
|
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_BURST_TIMER, BurstDelayTimer, float );
|
||
|
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_BURST_COUNT, BurstCount, int );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEFINITION_ID, def_id );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_C4_DETONATION_MODE, C4DetonationMode );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TARGET_LOCATION, Target );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_EXISTS, WeaponExists );
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_SAFETY_SET, SafetySet );
|
||
|
|
||
|
#if 0
|
||
|
case XXX_MICROCHUNKID_MODEL_NAME:
|
||
|
{
|
||
|
WWASSERT( Model == NULL );
|
||
|
char model_name[80];
|
||
|
cload.Read( model_name, cload.Cur_Micro_Chunk_Length() );
|
||
|
RenderObjClass * robj = WW3DAssetManager::Get_Instance()->Create_Render_Obj( model_name );
|
||
|
|
||
|
Set_Model( robj );
|
||
|
|
||
|
cload.Close_Micro_Chunk();
|
||
|
cload.Open_Micro_Chunk();
|
||
|
WWASSERT( cload.Cur_Micro_Chunk_ID() == MICROCHUNKID_MODEL_PTR );
|
||
|
void * old_ptr = NULL;
|
||
|
cload.Read( &old_ptr, sizeof( old_ptr ) );
|
||
|
SaveLoadSystemClass::Register_Pointer( old_ptr, robj );
|
||
|
REF_PTR_RELEASE( robj );
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_MODEL_PTR, Model );
|
||
|
|
||
|
default:
|
||
|
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
|
||
|
break;
|
||
|
}
|
||
|
cload.Close_Micro_Chunk();
|
||
|
}
|
||
|
|
||
|
Init( WeaponManager::Find_Weapon_Definition( def_id ) );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CHUNKID_OWNER_REF:
|
||
|
Owner.Load( cload );
|
||
|
break;
|
||
|
|
||
|
case CHUNKID_TARGET_OBJECT:
|
||
|
TargetObject.Load( cload );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
cload.Close_Chunk();
|
||
|
}
|
||
|
|
||
|
if (Model != NULL) {
|
||
|
REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&Model);
|
||
|
}
|
||
|
|
||
|
// Legacy
|
||
|
if ( ClipRounds == 0 && InventoryRounds < 0 ) {
|
||
|
ClipRounds = Definition->ClipSize;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void WeaponClass::Set_Owner( ArmedGameObj *owner )
|
||
|
{
|
||
|
Owner = owner;
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Set_Target_Object( PhysicalGameObj * obj )
|
||
|
{
|
||
|
TargetObject = obj;
|
||
|
}
|
||
|
|
||
|
PhysicalGameObj * WeaponClass::Get_Target_Object( PhysicalGameObj * obj )
|
||
|
{
|
||
|
return ( PhysicalGameObj * )TargetObject.Get_Ptr();
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Set_Model( RenderObjClass *model )
|
||
|
{
|
||
|
if ( Model != NULL ) {
|
||
|
Model->Release_Ref();
|
||
|
}
|
||
|
Model = model;
|
||
|
if ( Model != NULL ) {
|
||
|
Model->Add_Ref();
|
||
|
Init_Muzzle_Flash( Model );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Select( void )
|
||
|
{
|
||
|
// Debug_Say(( "Weapon Selected\n" ));
|
||
|
Set_State( STATE_START_SWITCH );
|
||
|
UpdateModel = WEAPON_MODEL_UPDATE_WILL_BE_NEEDED;
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Deselect( void )
|
||
|
{
|
||
|
Do_Continuous_Effects( false );
|
||
|
IsPrimaryTriggered = false;
|
||
|
IsSecondaryTriggered = false;
|
||
|
LastFrameIsPrimaryTriggered = false;
|
||
|
LastFrameIsSecondaryTriggered = false;
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Next_C4_Detonation_Mode( void )
|
||
|
{
|
||
|
#define LAST_C4_DETONATION_MODE 3
|
||
|
if ( ++C4DetonationMode > LAST_C4_DETONATION_MODE ) {
|
||
|
C4DetonationMode = 1;
|
||
|
}
|
||
|
Debug_Say(( "Weapon Next C4 Detonation Mode %d\n", C4DetonationMode ));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void WeaponClass::Set_Primary_Triggered( bool triggered )
|
||
|
{
|
||
|
if ( !LockTriggers ) {
|
||
|
IsPrimaryTriggered = triggered;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Set_Secondary_Triggered( bool triggered )
|
||
|
{
|
||
|
if ( !LockTriggers ) {
|
||
|
IsSecondaryTriggered = triggered;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** AMMO
|
||
|
*/
|
||
|
void WeaponClass::Add_Rounds( int num )
|
||
|
{
|
||
|
// If we have an empty clip ( first time ) load directly
|
||
|
if ( ClipRounds == 0 ) {
|
||
|
if ( num < 0 ) {
|
||
|
ClipRounds = Definition->ClipSize;
|
||
|
} else if ( InventoryRounds >= 0 ) {
|
||
|
int count = MIN( num, (int)Definition->ClipSize );
|
||
|
num -= count;
|
||
|
ClipRounds = count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( CheatMgrClass::Get_Instance () != NULL &&
|
||
|
CheatMgrClass::Get_Instance ()->Is_Cheat_Enabled( CheatMgrClass::CHEAT_INFINITE_AMMO ) && Owner == COMBAT_STAR )
|
||
|
{
|
||
|
InventoryRounds = Definition->MaxInventoryRounds ;
|
||
|
} else if ( num < 0 ) {
|
||
|
InventoryRounds = -1;
|
||
|
} else if ( InventoryRounds >= 0 ) {
|
||
|
InventoryRounds += num;
|
||
|
}
|
||
|
|
||
|
if ( (InventoryRounds != -1) && (InventoryRounds > (int)Definition->MaxInventoryRounds) ) {
|
||
|
InventoryRounds = Definition->MaxInventoryRounds ;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
int WeaponClass::Get_Total_Rounds( void )
|
||
|
{
|
||
|
if ( InventoryRounds < 0 ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if the infinite ammo cheat is enabled
|
||
|
//
|
||
|
if ( CheatMgrClass::Get_Instance () != NULL &&
|
||
|
CheatMgrClass::Get_Instance ()->Is_Cheat_Enabled( CheatMgrClass::CHEAT_INFINITE_AMMO ) && Owner == COMBAT_STAR )
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return ClipRounds + InventoryRounds;
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Set_Total_Rounds( int num )
|
||
|
{
|
||
|
if ( CheatMgrClass::Get_Instance () != NULL &&
|
||
|
CheatMgrClass::Get_Instance ()->Is_Cheat_Enabled( CheatMgrClass::CHEAT_INFINITE_AMMO ) && Owner == COMBAT_STAR )
|
||
|
{
|
||
|
InventoryRounds = Definition->MaxInventoryRounds;
|
||
|
} else if ( num == -1 ) {
|
||
|
InventoryRounds = -1;
|
||
|
} else {
|
||
|
// Assume what you have in inventory is correct
|
||
|
num -= (int)InventoryRounds;
|
||
|
// And the rest goes in the clip
|
||
|
ClipRounds = num;
|
||
|
|
||
|
// Fix a negative clip
|
||
|
if ( ClipRounds < 0 ) {
|
||
|
InventoryRounds += ClipRounds;
|
||
|
ClipRounds = 0;
|
||
|
}
|
||
|
|
||
|
// Fix overloaded clip
|
||
|
if ( ClipRounds > Definition->ClipSize ) {
|
||
|
InventoryRounds += ClipRounds - Definition->ClipSize;
|
||
|
ClipRounds -= ClipRounds - Definition->ClipSize;
|
||
|
}
|
||
|
|
||
|
// Fix Overloaded inventory
|
||
|
if ( InventoryRounds > (int)Definition->MaxInventoryRounds ) {
|
||
|
InventoryRounds = Definition->MaxInventoryRounds;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool WeaponClass::Is_Ammo_Maxed( void )
|
||
|
{
|
||
|
if (InventoryRounds == -1) return true;
|
||
|
|
||
|
// Special case for C4
|
||
|
if (((int)Definition->MaxInventoryRounds == 0) && (ClipRounds == 0)) return false;
|
||
|
|
||
|
return (InventoryRounds == (int)Definition->MaxInventoryRounds);
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Decrement_Rounds( int rounds )
|
||
|
{
|
||
|
if ( ClipRounds != -1 ) {
|
||
|
ClipRounds -= rounds;
|
||
|
if ( ClipRounds <= 0 ) {
|
||
|
ClipRounds = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Do_Reload( void )
|
||
|
{
|
||
|
int added = (int)Definition->ClipSize - (int)ClipRounds;
|
||
|
|
||
|
//
|
||
|
// Check to see if the infinite ammo cheat is enabled
|
||
|
//
|
||
|
bool apply_cheat = false;
|
||
|
if ( CheatMgrClass::Get_Instance () != NULL &&
|
||
|
CheatMgrClass::Get_Instance ()->Is_Cheat_Enabled( CheatMgrClass::CHEAT_INFINITE_AMMO ) && Owner == COMBAT_STAR )
|
||
|
{
|
||
|
apply_cheat = true;
|
||
|
}
|
||
|
|
||
|
if ( apply_cheat == false && InventoryRounds >= 0 ) {
|
||
|
|
||
|
if ( InventoryRounds < added ) {
|
||
|
added = InventoryRounds;
|
||
|
}
|
||
|
|
||
|
InventoryRounds -= added;
|
||
|
}
|
||
|
|
||
|
ClipRounds += added;
|
||
|
}
|
||
|
|
||
|
float WeaponClass::Get_Range( void )
|
||
|
{
|
||
|
return ( PrimaryAmmoDefinition != NULL ) ? PrimaryAmmoDefinition->Range : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Fire_C4
|
||
|
*/
|
||
|
void WeaponClass::Fire_C4( const AmmoDefinitionClass *ammo_def )
|
||
|
{
|
||
|
#define C4_OBJECT_NAME "Tossed C4"
|
||
|
|
||
|
// C4 special case
|
||
|
C4GameObj *c4 = (C4GameObj *)ObjectLibraryManager::Create_Object( C4_OBJECT_NAME );
|
||
|
if ( c4 ) {
|
||
|
c4->Init_C4( ammo_def, Get_Owner()->As_SoldierGameObj(), C4DetonationMode, Get_Muzzle() );
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Fire_Beacon
|
||
|
*/
|
||
|
bool WeaponClass::Fire_Beacon( const AmmoDefinitionClass *ammo_def )
|
||
|
{
|
||
|
bool retval = false;
|
||
|
|
||
|
// only for server
|
||
|
if ( CombatManager::I_Am_Server() ) {
|
||
|
//
|
||
|
// Create the beacon
|
||
|
//
|
||
|
BeaconGameObj *beacon = (BeaconGameObj *)ObjectLibraryManager::Create_Object( ammo_def->BeaconDefID );
|
||
|
if ( beacon != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Get the position of the owner
|
||
|
//
|
||
|
Vector3 pos;
|
||
|
Get_Owner()->Get_Position( &pos );
|
||
|
beacon->Init_Beacon( Definition, Get_Owner()->As_SoldierGameObj(), pos );
|
||
|
beacon->Start_Observers ();
|
||
|
|
||
|
//
|
||
|
// Begin arming the beacon
|
||
|
//
|
||
|
if ( beacon->Can_Place_Here ( pos ) ) {
|
||
|
beacon->Begin_Arming();
|
||
|
retval = true;
|
||
|
} else {
|
||
|
beacon->Set_Delete_Pending ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void WeaponClass::Do_Fire( bool primary )
|
||
|
{
|
||
|
WWPROFILE( "Do Fire" );
|
||
|
|
||
|
// Stats
|
||
|
if ( Get_Owner() &&
|
||
|
Get_Owner()->As_SoldierGameObj() &&
|
||
|
Get_Owner()->As_SoldierGameObj()->Get_Player_Data() ) {
|
||
|
Get_Owner()->As_SoldierGameObj()->Get_Player_Data()->Stats_Add_Shot_Fired();
|
||
|
|
||
|
Get_Owner()->As_SoldierGameObj()->Get_Player_Data()->Stats_Add_Weapon_Fired(
|
||
|
this->Get_Definition()->Get_ID() );
|
||
|
|
||
|
}
|
||
|
|
||
|
// Debug_Say(( "Fire at %f\n", (float)WW3D::Get_Sync_Time() ));
|
||
|
|
||
|
const AmmoDefinitionClass *ammo_def;
|
||
|
if ( primary ) {
|
||
|
ammo_def = PrimaryAmmoDefinition;
|
||
|
} else {
|
||
|
ammo_def = SecondaryAmmoDefinition;
|
||
|
}
|
||
|
|
||
|
// Debug_Say(( "Firing from %f %f %f\n", muzzle_pos.X, muzzle_pos.Y, muzzle_pos.Z ));
|
||
|
// Debug_Say(( "Firing at %f %f %f\n", target_pos.X, target_pos.Y, target_pos.Z ));
|
||
|
// Debug_Say(( " %d Clips %d in Clip\n", NumClips, ClipContents ));
|
||
|
|
||
|
//
|
||
|
// Handle each weapon type differently
|
||
|
//
|
||
|
if ( Get_Style() == WEAPON_HOLD_STYLE_BEACON ) {
|
||
|
|
||
|
//
|
||
|
// Try to fire the beacon
|
||
|
//
|
||
|
if ( Fire_Beacon( ammo_def ) ) {
|
||
|
|
||
|
//
|
||
|
// Decrement the rounds
|
||
|
//
|
||
|
// WWASSERT( BurstCount != 0 );
|
||
|
BurstCount--;
|
||
|
// WWASSERT( ClipRounds != 0 );
|
||
|
Decrement_Rounds( ammo_def->SprayBulletCost );
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Decrement the rounds
|
||
|
//
|
||
|
// WWASSERT( BurstCount != 0 );
|
||
|
BurstCount--;
|
||
|
// WWASSERT( ClipRounds != 0 );
|
||
|
Decrement_Rounds( ammo_def->SprayBulletCost );
|
||
|
|
||
|
if ( Get_Style() == WEAPON_HOLD_STYLE_C4 ) {
|
||
|
if ( CombatManager::I_Am_Server() ) { // Only on servers
|
||
|
Fire_C4( ammo_def );
|
||
|
}
|
||
|
} else {
|
||
|
Fire_Bullet( ammo_def, primary );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Fire_Bullet
|
||
|
*/
|
||
|
void WeaponClass::Fire_Bullet( const AmmoDefinitionClass *ammo_def, bool primary )
|
||
|
{
|
||
|
WWPROFILE( "Fire Bullet" );
|
||
|
|
||
|
if ( Get_Owner() == COMBAT_STAR ) {
|
||
|
Vector3 pos;
|
||
|
COMBAT_STAR->Get_Position( &pos );
|
||
|
DIAG_LOG(( "WFRD", "%1.2f; %1.2f; %1.2f; %s; %d; %d", pos.X, pos.Y, pos.Z, Get_Definition()->Get_Name(), !primary, Get_Total_Rounds() ));
|
||
|
}
|
||
|
|
||
|
int spray_count = ammo_def->SprayCount;
|
||
|
if ( spray_count < 1 ) {
|
||
|
spray_count = 1;
|
||
|
}
|
||
|
|
||
|
int muzzle_index = (Get_Total_Rounds_Fired() & 1) + ( primary ? 0 : 2 );
|
||
|
|
||
|
if ( !Get_Owner()->Muzzle_Exists( muzzle_index ) ) {
|
||
|
// if the second muzzle doesn't exist, use the first
|
||
|
if ( muzzle_index & 1 ) {
|
||
|
muzzle_index--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix3D muzzle = Get_Muzzle( muzzle_index );
|
||
|
|
||
|
// Special test for first person
|
||
|
if ( CombatManager::Is_First_Person() && Get_Owner() == COMBAT_STAR ) {
|
||
|
Vector3 fp_muzzle_pos = WeaponViewClass::Get_Muzzle_Pos();
|
||
|
|
||
|
// cast to see what we hit
|
||
|
Vector3 start = fp_muzzle_pos;
|
||
|
Vector3 end = Get_Owner()->Get_Targeting_Pos();
|
||
|
LineSegClass ray( start, end );
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
|
||
|
{ WWPROFILE( "Cast_Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
Unignore_Owner();
|
||
|
|
||
|
// If the hit point is near the targeting_pos, use the first person muzzle
|
||
|
if ( raytest.Result->Fraction >= 0.99F ) {
|
||
|
muzzle.Obj_Look_At( fp_muzzle_pos, Get_Owner()->Get_Targeting_Pos(), 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Vector3 bullet_target(0,0,0);
|
||
|
DamageableGameObj * target_obj = NULL;
|
||
|
|
||
|
if ( ammo_def->IsTracking ) {
|
||
|
// determine the target
|
||
|
Vector3 start = muzzle.Get_Translation();
|
||
|
|
||
|
// (gth) this modification makes bikes able to target anything even though their muzzle doesn't rotate...
|
||
|
Vector3 end = Get_Owner()->Get_Targeting_Pos(); //muzzle * Vector3( ammo_def->Range, 0, 0 );
|
||
|
|
||
|
LineSegClass ray( start, end );
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
|
||
|
{ WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
|
||
|
Unignore_Owner();
|
||
|
|
||
|
ray.Compute_Point( raytest.Result->Fraction, &end );
|
||
|
bullet_target = end;
|
||
|
|
||
|
if ( raytest.CollidedPhysObj != NULL && raytest.CollidedPhysObj->Get_Observer() != NULL ) {
|
||
|
target_obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_DamageableGameObj();
|
||
|
// No homing on buildings
|
||
|
if ( target_obj->As_BuildingGameObj() != NULL ) {
|
||
|
target_obj = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int count = 0; count < spray_count; count++ ) {
|
||
|
|
||
|
Vector3 velocity = muzzle.Get_X_Vector() * ammo_def->Velocity;
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
if ( ammo_def->SprayAngle ) {
|
||
|
// Use Spray Angle
|
||
|
Vector3 spray_adjust = RANDOM_VECTOR( 1.0 );
|
||
|
spray_adjust.Normalize();
|
||
|
spray_adjust = spray_adjust * ammo_def->Velocity;
|
||
|
spray_adjust *= ammo_def->SprayAngle / (float)(2*WWMATH_PI);
|
||
|
|
||
|
velocity += spray_adjust;
|
||
|
velocity.Normalize();
|
||
|
velocity = velocity * ammo_def->Velocity;
|
||
|
}
|
||
|
|
||
|
if ( DynamicErrorAngle ) {
|
||
|
velocity += RANDOM_VECTOR( DynamicErrorAngle / 2 );
|
||
|
velocity.Normalize();
|
||
|
velocity = velocity * ammo_def->Velocity;
|
||
|
}
|
||
|
#else
|
||
|
Matrix3D VelTM;
|
||
|
// Look at backwards with random roll
|
||
|
VelTM.Look_At( velocity, Vector3( 0,0,0 ), FreeRandom.Get_Float( DEG_TO_RADF( 360.0f ) ) );
|
||
|
#define SPRAY_CHEAT 0
|
||
|
#if SPRAY_CHEAT
|
||
|
float error_angle = 0;
|
||
|
#else
|
||
|
float error_angle = ammo_def->SprayAngle;
|
||
|
#endif
|
||
|
float rand_angle = FreeRandom.Get_Float() * error_angle;
|
||
|
VelTM.Rotate_Y( rand_angle );
|
||
|
velocity = VelTM.Get_Z_Vector();
|
||
|
velocity.Normalize();
|
||
|
velocity *= ammo_def->Velocity;
|
||
|
#endif
|
||
|
|
||
|
//Vector3 position = muzzle.Get_Translation();
|
||
|
Vector3 position;
|
||
|
Compute_Bullet_Start_Point(muzzle,&position);
|
||
|
|
||
|
BulletBumpTime += ammo_def->AliasedSpeed * TimeManager::Get_Frame_Seconds() * 1.3f;
|
||
|
BulletBumpTime = WWMath::Wrap( BulletBumpTime, 0, TimeManager::Get_Frame_Seconds() );
|
||
|
|
||
|
float bump_time = BulletBumpTime;
|
||
|
|
||
|
if ( (float)ammo_def->Velocity != 0 ) {
|
||
|
bump_time += 1 / (float)ammo_def->Velocity; // Skip the first meter
|
||
|
}
|
||
|
|
||
|
//Debug_Say(( "%f %f\n", BulletBumpTime,bump_time ));
|
||
|
|
||
|
if ( Target.Length2() != 0 || !ammo_def->IsTracking ) {
|
||
|
bullet_target = Target;
|
||
|
}
|
||
|
|
||
|
// Bullets from vehicles belong to the gunner
|
||
|
ArmedGameObj * bullet_owner = Get_Owner();
|
||
|
if ( bullet_owner != NULL && bullet_owner->As_SmartGameObj() ) {
|
||
|
VehicleGameObj * vehicle = bullet_owner->As_SmartGameObj()->As_VehicleGameObj();
|
||
|
if ( ( vehicle ) && ( vehicle->Get_Actual_Gunner() != NULL ) ) {
|
||
|
bullet_owner = vehicle->Get_Actual_Gunner();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create a bullet
|
||
|
BulletManager::Create_Bullet( ammo_def, position, velocity, bullet_owner, bump_time, bullet_target, target_obj );
|
||
|
}
|
||
|
|
||
|
if ( ammo_def->DisplayLaser ) {
|
||
|
TimedDecorationPhysClass * laser_obj = NEW_REF( TimedDecorationPhysClass, () );
|
||
|
if ( laser_obj ) {
|
||
|
|
||
|
// Find where the laser should stop
|
||
|
Vector3 start = muzzle.Get_Translation();
|
||
|
Vector3 end = muzzle * Vector3( ammo_def->Range, 0, 0 );
|
||
|
LineSegClass ray( start, end );
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
|
||
|
{ WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
|
||
|
Unignore_Owner();
|
||
|
|
||
|
ray.Compute_Point( raytest.Result->Fraction, &end );
|
||
|
|
||
|
// Create a laser
|
||
|
RenderObjClass * model = NEW_REF( Line3DClass, ( start, end, 0.1f, 0.8f,0,0, 0.8f ) );
|
||
|
laser_obj->Set_Model( model );
|
||
|
model->Release_Ref();
|
||
|
laser_obj->Set_Lifetime( 0.6f );
|
||
|
laser_obj->Update_Cull_Box(); // htis may be needed due to a bug in Line3DClass
|
||
|
COMBAT_SCENE->Add_Dynamic_Object( laser_obj );
|
||
|
laser_obj->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Initiate recoil effects on our parent object.
|
||
|
Get_Owner()->Start_Recoil(muzzle_index,Get_Recoil_Scale(),Get_Recoil_Time());
|
||
|
|
||
|
TotalRoundsFired++;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool WeaponClass::Is_Muzzle_Clear()
|
||
|
{
|
||
|
int primary = 1;
|
||
|
int muzzle_index = Get_Total_Rounds_Fired() & 1 + ( primary ? 0 : 2 );
|
||
|
Matrix3D muzzle = Get_Muzzle( muzzle_index );
|
||
|
|
||
|
Vector3 start_pt;
|
||
|
Vector3 end_pt;
|
||
|
muzzle.Get_Translation( &end_pt );
|
||
|
|
||
|
if ( Get_Owner() == NULL ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ((Get_Owner() == COMBAT_STAR) && CombatManager::Is_First_Person()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (Get_Owner()->As_SoldierGameObj() != NULL) {
|
||
|
|
||
|
start_pt = Get_Owner()->Get_Bullseye_Position();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// Estimate the distance we need to sweep back along the muzzle in order to be inside the vehicle.
|
||
|
Vector3 owner_pos;
|
||
|
Get_Owner()->Get_Position( &owner_pos );
|
||
|
owner_pos -= end_pt;
|
||
|
owner_pos.Z = 0.0f;
|
||
|
float dist = owner_pos.Quick_Length();
|
||
|
|
||
|
// Compute the starting point for a ray to test if the muzzle is inside anything.
|
||
|
Vector3 offset;
|
||
|
Matrix3D::Rotate_Vector(muzzle,Vector3(-dist,0.0f,0.0f),&offset);
|
||
|
start_pt = end_pt + offset;
|
||
|
|
||
|
}
|
||
|
|
||
|
// Cast the ray
|
||
|
LineSegClass ray( start_pt, end_pt );
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
raytest.CheckDynamicObjs = false;
|
||
|
|
||
|
{
|
||
|
WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
|
||
|
return raytest.Result->Fraction == 1.0f;
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Compute_Bullet_Start_Point(const Matrix3D & muzzle,Vector3 * set_start_point)
|
||
|
{
|
||
|
//
|
||
|
// The default behavior is to start the bullet at the position of the muzzle.
|
||
|
//
|
||
|
muzzle.Get_Translation(set_start_point);
|
||
|
|
||
|
//
|
||
|
// If this weapon is owned by a vehicle, try to ensure the bullet starts in front
|
||
|
// of any dynamic objects that may be between the muzzle point and the body of the vehicle
|
||
|
//
|
||
|
if (Get_Owner()->As_VehicleGameObj() != NULL) {
|
||
|
Vector3 ray_start;
|
||
|
Vector3 ray_end;
|
||
|
muzzle.Get_Translation( &ray_end );
|
||
|
|
||
|
// Estimate the distance we need to sweep back along the muzzle in order to be inside the vehicle.
|
||
|
Vector3 owner_pos;
|
||
|
Get_Owner()->Get_Position( &owner_pos );
|
||
|
owner_pos -= ray_end;
|
||
|
owner_pos.Z = 0.0f;
|
||
|
float dist = owner_pos.Quick_Length();
|
||
|
|
||
|
// Compute the starting point for a ray to test if the muzzle is inside anything.
|
||
|
Vector3 offset;
|
||
|
Matrix3D::Rotate_Vector(muzzle,Vector3(-dist,0.0f,0.0f),&offset);
|
||
|
ray_start = ray_end + offset;
|
||
|
|
||
|
// Cast the ray
|
||
|
LineSegClass ray( ray_start, ray_end );
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
raytest.CheckStaticObjs = false;
|
||
|
|
||
|
{
|
||
|
Ignore_Owner();
|
||
|
|
||
|
WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
|
||
|
Unignore_Owner();
|
||
|
}
|
||
|
|
||
|
if (res.Fraction < 1.0f) {
|
||
|
ray.Compute_Point(res.Fraction,set_start_point);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Do_Firing_Effects( void )
|
||
|
{
|
||
|
if ( TimeManager::Get_Frame_Seconds() == 0 ) {
|
||
|
return; // No sounds when time stops
|
||
|
}
|
||
|
|
||
|
WWPROFILE( "Firing Effects" );
|
||
|
|
||
|
Matrix3D muzzle = Get_Muzzle();
|
||
|
|
||
|
// Don't do these two if fired by the star in First Person Mode
|
||
|
if ( (Get_Owner() != COMBAT_STAR) || !CombatManager::Is_First_Person() ) {
|
||
|
|
||
|
#if 01
|
||
|
// create muzzle flash
|
||
|
if ( Definition->MuzzleFlashPhysDefID != 0 ) {
|
||
|
|
||
|
DefinitionClass * def = DefinitionMgrClass::Find_Definition( Definition->MuzzleFlashPhysDefID );
|
||
|
if ( def != NULL ) {
|
||
|
WWASSERT( ((PhysDefClass *)def)->Is_Type( "TimedDecorationPhysDef" ) );
|
||
|
TimedDecorationPhysClass * muzzle_flash = (TimedDecorationPhysClass *)def->Create();
|
||
|
if ( muzzle_flash ) {
|
||
|
RenderObjClass * model = muzzle_flash->Peek_Model();
|
||
|
if ( model != NULL ) {
|
||
|
// muzzle.Rotate_X( FreeRandom.Get_Float( DEG_TO_RAD( 360 ) ) );
|
||
|
muzzle_flash->Set_Transform( muzzle );
|
||
|
COMBAT_SCENE->Add_Dynamic_Object( muzzle_flash );
|
||
|
|
||
|
// If this is an emitter, start it
|
||
|
if ( model->Class_ID() == RenderObjClass::CLASSID_PARTICLEEMITTER ) {
|
||
|
ParticleEmitterClass * pe = (ParticleEmitterClass *)model;
|
||
|
pe->Start();
|
||
|
}
|
||
|
} else {
|
||
|
Debug_Say(( "Missing muzzle flash for %s\n", Definition->Get_Name() ));
|
||
|
}
|
||
|
|
||
|
muzzle_flash->Release_Ref();
|
||
|
}
|
||
|
} else {
|
||
|
//Debug_Say(( "Couldn't find muzzle flash def for %s\n", Definition->Get_Name() ));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// if this gun is fired by the STAR and they have an eject bone, eject a shell.
|
||
|
if ((Get_Owner() == COMBAT_STAR) && (Model != NULL)) {
|
||
|
WWPROFILE( "Eject" );
|
||
|
int eject_index = Model->Get_Bone_Index( "eject" );
|
||
|
if ( eject_index > 0 ) {
|
||
|
Make_Shell_Eject( Model->Get_Bone_Transform( eject_index ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make the Gunshot sound
|
||
|
|
||
|
//
|
||
|
// Determine which sound to play, the primary or secondary firing sound?
|
||
|
//
|
||
|
int sound_id = 0;
|
||
|
if ( IsPrimaryTriggered && PrimaryAmmoDefinition != NULL ) {
|
||
|
sound_id = PrimaryAmmoDefinition->FireSoundDefID;
|
||
|
} else if ( IsSecondaryTriggered && SecondaryAmmoDefinition != NULL ) {
|
||
|
sound_id = SecondaryAmmoDefinition->FireSoundDefID;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the old firing sound (if necessary)
|
||
|
//
|
||
|
bool release_curr_sound = false;
|
||
|
if ( sound_id != FiringSoundDefID && FiringSound != NULL ) {
|
||
|
release_curr_sound = true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For first-person we make the weapon sounds "2D", so check to see
|
||
|
// if we need to re-create the sound...
|
||
|
//
|
||
|
if ( FiringSound != NULL ) {
|
||
|
if ( Get_Owner() == COMBAT_STAR && CombatManager::Is_First_Person() ) {
|
||
|
|
||
|
//
|
||
|
// In first person, we need the sound to be "2D"
|
||
|
//
|
||
|
if ( FiringSound->Get_Class_ID () != CLASSID_2D ) {
|
||
|
release_curr_sound = true;
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// In third person, we need the sound to be "3D"
|
||
|
//
|
||
|
if ( FiringSound->Get_Class_ID () == CLASSID_2D ) {
|
||
|
release_curr_sound = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the currently playing sound as necessary
|
||
|
//
|
||
|
if ( release_curr_sound ) {
|
||
|
FiringSound->Remove_From_Scene ();
|
||
|
REF_PTR_RELEASE (FiringSound);
|
||
|
FiringSoundDefID = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Do we need to recreate the firing sound object, or can we just
|
||
|
// reuse the one we already have?
|
||
|
//
|
||
|
if ( FiringSound != NULL ) {
|
||
|
|
||
|
//
|
||
|
// Stop the current sound
|
||
|
//
|
||
|
//FiringSound->Set_Transform( muzzle );
|
||
|
//FiringSound->Stop();
|
||
|
|
||
|
if ( FiringSound->Get_Class_ID () == CLASSID_3D ) {
|
||
|
FiringSound->Stop();
|
||
|
} else {
|
||
|
FiringSound->Seek( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Force the sound to play
|
||
|
//
|
||
|
if ( Get_Owner() != COMBAT_STAR || CombatManager::Is_First_Person() == false ) {
|
||
|
FiringSound->Add_To_Scene( true );
|
||
|
FiringSound->Play();
|
||
|
} else {
|
||
|
FiringSound->Play();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Play the sound (or add it to the scene)
|
||
|
//
|
||
|
/*if ( Get_Owner() == COMBAT_STAR && CombatManager::Is_First_Person() ) {
|
||
|
FiringSound->Play();
|
||
|
} else {
|
||
|
FiringSound->Remove_From_Scene();
|
||
|
FiringSound->Add_To_Scene( true );
|
||
|
}*/
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Create a pseudo-3d sound effect for this firing sound and
|
||
|
// add it to the sound scene.
|
||
|
//
|
||
|
|
||
|
if ( sound_id != 0 ) {
|
||
|
|
||
|
//
|
||
|
// Determine whether to play the sound as 2D or 3D
|
||
|
//
|
||
|
int classid_hint = CLASSID_3D;
|
||
|
if ( Get_Owner() == COMBAT_STAR && CombatManager::Is_First_Person() ) {
|
||
|
classid_hint = CLASSID_2D;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the sound
|
||
|
//
|
||
|
RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference;
|
||
|
owner_ref->Set_Ptr( Get_Owner() );
|
||
|
FiringSound = WWAudioClass::Get_Instance()->Create_Sound( sound_id, owner_ref, 0, classid_hint );
|
||
|
REF_PTR_RELEASE( owner_ref );
|
||
|
}
|
||
|
|
||
|
if ( FiringSound != NULL ) {
|
||
|
|
||
|
FiringSoundDefID = sound_id;
|
||
|
FiringSound->Set_Transform( muzzle );
|
||
|
FiringSound->Attach_To_Object( Model );
|
||
|
FiringSound->Add_To_Scene( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// (gth) Apply an Impulse to tanks when they fires a bullet
|
||
|
// Byon, this can be done with any RigidBody derived physics object (all vehicles)
|
||
|
// I suggest making it a parameter of the weapon and having the strength be a
|
||
|
// multiple of the mass of the vehicle.
|
||
|
ArmedGameObj * owner = Get_Owner();
|
||
|
VehicleGameObj * vehicle_game_obj = owner->As_VehicleGameObj();
|
||
|
if (vehicle_game_obj != NULL) {
|
||
|
PhysClass * vehicle = vehicle_game_obj->Peek_Physical_Object();
|
||
|
if (vehicle->As_TrackedVehicleClass() != NULL) {
|
||
|
|
||
|
RigidBodyClass * rbody = (RigidBodyClass *)vehicle;
|
||
|
|
||
|
Vector3 impulse_pos;
|
||
|
muzzle.Get_Translation(&impulse_pos);
|
||
|
Vector3 impulse;
|
||
|
muzzle.Get_X_Vector(&impulse);
|
||
|
impulse *= -Definition->RecoilImpulse * rbody->Get_Mass();
|
||
|
|
||
|
rbody->Apply_Impulse(impulse,impulse_pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Play an anim on the human owner
|
||
|
if ( owner != NULL && owner->As_SoldierGameObj() ) {
|
||
|
if ( !Definition->HumanFiringAnimation.Is_Empty() ) {
|
||
|
owner->As_SoldierGameObj()->Set_Animation( Definition->HumanFiringAnimation, 0, 0 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Make_Shell_Eject( const Matrix3D & tm )
|
||
|
{
|
||
|
DefinitionClass * def = DefinitionMgrClass::Find_Definition( Definition->EjectPhysDefID );
|
||
|
|
||
|
if ((def != NULL) && ((PhysDefClass *)def)->Is_Type( "ProjectileDef" )) {
|
||
|
|
||
|
// Create the object
|
||
|
ProjectileClass * PhysicalObject = (ProjectileClass *)def->Create();
|
||
|
|
||
|
// Set its transform, collision group, observer, and initial velocity
|
||
|
PhysicalObject->Set_Transform( tm );
|
||
|
PhysicalObject->Set_Observer(&_TheEjectCasingObserver);
|
||
|
PhysicalObject->Set_Collision_Group( DEFAULT_COLLISION_GROUP );
|
||
|
PhysicalObject->Set_Velocity( tm.Rotate_Vector( Vector3( 2,0,0 ) ) );
|
||
|
|
||
|
// Add it to the scene
|
||
|
COMBAT_SCENE->Add_Dynamic_Object( PhysicalObject );
|
||
|
PhysicalObject->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void WeaponClass::Do_Continuous_Effects( bool enable )
|
||
|
{
|
||
|
if ( !enable ) {
|
||
|
|
||
|
for ( int i = 0; i < ContinuousEmitters.Length(); i++ ) {
|
||
|
if ( ContinuousEmitters[i] != NULL ) {
|
||
|
// Check if it is in the scene, it may ahve already been remove by completion
|
||
|
if ( ContinuousEmitters[i]->Peek_Scene() != NULL ) {
|
||
|
PhysicsSceneClass::Get_Instance()->Remove_Render_Object( ContinuousEmitters[i] );
|
||
|
}
|
||
|
ContinuousEmitters[i]->Stop();
|
||
|
ContinuousEmitters[i]->Release_Ref();
|
||
|
ContinuousEmitters[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ContinuousSound != NULL ) {
|
||
|
ContinuousSound->Stop();
|
||
|
ContinuousSound->Remove_From_Scene();
|
||
|
ContinuousSound->Release_Ref();
|
||
|
ContinuousSound = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( enable ) {
|
||
|
|
||
|
const AmmoDefinitionClass *ammo_def = PrimaryAmmoDefinition;
|
||
|
if (IsPrimaryTriggered == false && IsSecondaryTriggered) {
|
||
|
ammo_def = SecondaryAmmoDefinition;
|
||
|
}
|
||
|
|
||
|
int i;
|
||
|
|
||
|
if ( ContinuousEmitters.Length() == 0 ) {
|
||
|
|
||
|
int num_muzzles = 1;
|
||
|
if ( Get_Muzzle( 0 ) != Get_Muzzle( 1 ) ) {
|
||
|
num_muzzles = 2;
|
||
|
}
|
||
|
|
||
|
ContinuousEmitters.Resize( num_muzzles );
|
||
|
for ( int i = 0; i < ContinuousEmitters.Length(); i++ ) {
|
||
|
ContinuousEmitters[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < ContinuousEmitters.Length(); i++ ) {
|
||
|
if ( ContinuousEmitters[i] == NULL && !ammo_def->ContinuousEmitterName.Is_Empty() ) {
|
||
|
RenderObjClass * renderobj = Create_Render_Obj_From_Filename( ammo_def->ContinuousEmitterName );
|
||
|
if ( renderobj ) {
|
||
|
WWASSERT( renderobj->Class_ID() == RenderObjClass::CLASSID_PARTICLEEMITTER );
|
||
|
ContinuousEmitters[i] = (ParticleEmitterClass *)renderobj;
|
||
|
ContinuousEmitters[i]->Set_Velocity_Inheritance_Factor( 1 );
|
||
|
}
|
||
|
if ( ContinuousEmitters[i] ) {
|
||
|
SET_REF_OWNER( ContinuousEmitters[i] );
|
||
|
ContinuousEmitters[i]->Start();
|
||
|
PhysicsSceneClass::Get_Instance()->Add_Render_Object( ContinuousEmitters[i] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ContinuousSound == NULL && ammo_def->ContinuousSoundDefID != 0 ) {
|
||
|
ContinuousSound = WWAudioClass::Get_Instance()->Create_Continuous_Sound( ammo_def->ContinuousSoundDefID );
|
||
|
if ( ContinuousSound != NULL ) {
|
||
|
ContinuousSound->Add_To_Scene( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if first person star, use first muzzle
|
||
|
if ( (Get_Owner() == COMBAT_STAR) && CombatManager::Is_First_Person() ) {
|
||
|
|
||
|
Vector3 fp_muzzle_pos = WeaponViewClass::Get_Muzzle_Pos();
|
||
|
Matrix3D muzzle;
|
||
|
muzzle.Obj_Look_At( fp_muzzle_pos, Get_Owner()->Get_Targeting_Pos(), 0 );
|
||
|
|
||
|
for ( i = 0; i < ContinuousEmitters.Length(); i++ ) {
|
||
|
if ( ContinuousEmitters[i] != NULL ) {
|
||
|
ContinuousEmitters[i]->Set_Transform( muzzle );
|
||
|
}
|
||
|
}
|
||
|
if ( ContinuousSound != NULL ) {
|
||
|
ContinuousSound->Set_Transform( muzzle );
|
||
|
}
|
||
|
} else {
|
||
|
for ( i = 0; i < ContinuousEmitters.Length(); i++ ) {
|
||
|
if ( ContinuousEmitters[i] != NULL ) {
|
||
|
ContinuousEmitters[i]->Set_Transform( Get_Muzzle( i ) );
|
||
|
}
|
||
|
}
|
||
|
if ( ContinuousSound != NULL ) {
|
||
|
ContinuousSound->Set_Transform( Get_Muzzle() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#define WEAPON_RATE_CHEAT 0
|
||
|
|
||
|
/*
|
||
|
** Weapon State
|
||
|
*/
|
||
|
void WeaponClass::Set_State( WeaponStateType new_state )
|
||
|
{
|
||
|
#define SWITCH_TIME 1.0f
|
||
|
#define RELOAD_TIME Definition->ReloadTime
|
||
|
#define READY_TIME 25.0f
|
||
|
|
||
|
// Debug_Say(( "Weapon switching from state %d to %d\n", State, new_state ));
|
||
|
|
||
|
State = new_state;
|
||
|
|
||
|
if ( State == STATE_START_SWITCH ) StateTimer = SWITCH_TIME/2.0f;
|
||
|
if ( State == STATE_END_SWITCH ) StateTimer = SWITCH_TIME/2.0f;
|
||
|
if ( State == STATE_RELOAD ) StateTimer = (float) RELOAD_TIME;
|
||
|
if ( State == STATE_READY ) StateTimer = READY_TIME; // delay til IDLE
|
||
|
if ( State == STATE_FIRE_PRIMARY ) StateTimer = 1.0f / PrimaryAmmoDefinition->RateOfFire;
|
||
|
if ( State == STATE_FIRE_SECONDARY )StateTimer = 1.0f / SecondaryAmmoDefinition->RateOfFire;
|
||
|
if ( State == STATE_CHARGE ) StateTimer = PrimaryAmmoDefinition->ChargeTime; // Charge Time
|
||
|
|
||
|
#if WEAPON_RATE_CHEAT
|
||
|
if ( Get_Owner() == COMBAT_STAR ) {
|
||
|
StateTimer *= 0.2f;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Update_State( float pending_time )
|
||
|
{
|
||
|
LockTriggers = false; // Unlock triggers
|
||
|
|
||
|
WWPROFILE( "Update weapon state" );
|
||
|
while ( pending_time > 0.0f ) {
|
||
|
|
||
|
bool trigger_ok = true;
|
||
|
// Only fire beacon if state is upright
|
||
|
if ( Get_Style() == WEAPON_HOLD_STYLE_BEACON &&
|
||
|
Get_Owner() && Get_Owner()->As_SoldierGameObj() &&
|
||
|
!Get_Owner()->As_SoldierGameObj()->Is_Upright() ) {
|
||
|
trigger_ok = false;
|
||
|
}
|
||
|
|
||
|
// If we can fire, start charging, it will fire and go back to READY
|
||
|
if ( (State == STATE_READY) || (State == STATE_IDLE) ) {
|
||
|
if ( (IsPrimaryTriggered || IsSecondaryTriggered) && (BurstCount != 0) && CombatManager::Is_Gameplay_Permitted() && trigger_ok ) {
|
||
|
if ( SafetySet ) {
|
||
|
LockTriggers = true;
|
||
|
} else {
|
||
|
if ( Is_Loaded() ) {
|
||
|
Set_State( STATE_CHARGE );
|
||
|
} else {
|
||
|
// We are not loaded, play the empty sound
|
||
|
#if 0
|
||
|
AudibleSoundClass * sound = WWAudioClass::Get_Instance()->Create_Sound( "Pistol_Empty_Click" );
|
||
|
if ( sound ) {
|
||
|
sound->Play();
|
||
|
sound->Release_Ref();
|
||
|
}
|
||
|
#else
|
||
|
if ( Definition->EmptySoundDefID != 0 ) {
|
||
|
EmptySoundTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( EmptySoundTimer <= 0 ) {
|
||
|
EmptySoundTimer = 0.3f;
|
||
|
Matrix3D muzzle = Get_Muzzle();
|
||
|
|
||
|
RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference;
|
||
|
owner_ref->Set_Ptr( Get_Owner() );
|
||
|
|
||
|
AudibleSoundClass * sound = WWAudioClass::Get_Instance()->Create_Sound( Definition->EmptySoundDefID, owner_ref );
|
||
|
if ( sound != NULL ) {
|
||
|
sound->Set_Transform( muzzle );
|
||
|
sound->Attach_To_Object( Model );
|
||
|
sound->Add_To_Scene( true );
|
||
|
sound->Release_Ref();
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE( owner_ref );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Force a reload when empty
|
||
|
if ( Is_Reload_Needed() ) {
|
||
|
Force_Reload();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float useable_time = min( StateTimer, pending_time );
|
||
|
StateTimer -= useable_time;
|
||
|
pending_time -= useable_time;
|
||
|
|
||
|
if ( StateTimer <= 0 ) { // Time for new state
|
||
|
|
||
|
switch ( State ) {
|
||
|
|
||
|
case STATE_IDLE:
|
||
|
StateTimer = 0;
|
||
|
pending_time = 0;
|
||
|
break;
|
||
|
|
||
|
case STATE_READY:
|
||
|
Set_State( STATE_IDLE );
|
||
|
break;
|
||
|
|
||
|
case STATE_CHARGE:
|
||
|
if ( IsPrimaryTriggered || !IsSecondaryTriggered ) {
|
||
|
Set_State( STATE_FIRE_PRIMARY );
|
||
|
} else {
|
||
|
Set_State( STATE_FIRE_SECONDARY );
|
||
|
}
|
||
|
|
||
|
if ( IsPrimaryTriggered || IsSecondaryTriggered ) {
|
||
|
if ( Is_Muzzle_Clear() ) {
|
||
|
Do_Fire( IsPrimaryTriggered );
|
||
|
DidFire = true;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STATE_FIRE_PRIMARY:
|
||
|
case STATE_FIRE_SECONDARY:
|
||
|
Set_State( STATE_READY );
|
||
|
break;
|
||
|
|
||
|
case STATE_RELOAD:
|
||
|
Do_Reload();
|
||
|
Set_State( STATE_READY );
|
||
|
break;
|
||
|
|
||
|
case STATE_START_SWITCH:
|
||
|
if ( UpdateModel == WEAPON_MODEL_UPDATE_WILL_BE_NEEDED ) {
|
||
|
UpdateModel = WEAPON_MODEL_UPDATE_IS_NEEDED;
|
||
|
}
|
||
|
Set_State( STATE_END_SWITCH );
|
||
|
break;
|
||
|
|
||
|
case STATE_END_SWITCH:
|
||
|
Set_State( STATE_IDLE );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Force_Reload( void )
|
||
|
{
|
||
|
// Stop reloading on last bullet to fire faster
|
||
|
// if ( Is_Reload_OK() && State != STATE_RELOAD ) {
|
||
|
if ( Is_Reload_OK() && State <= STATE_READY ) {
|
||
|
Set_State( STATE_RELOAD );
|
||
|
|
||
|
if ( Definition->ReloadSoundDefID != 0 ) {
|
||
|
// Make the reload sound
|
||
|
Matrix3D muzzle = Get_Muzzle();
|
||
|
|
||
|
RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference;
|
||
|
owner_ref->Set_Ptr( Get_Owner() );
|
||
|
|
||
|
AudibleSoundClass * sound = WWAudioClass::Get_Instance()->Create_Sound( Definition->ReloadSoundDefID, owner_ref );
|
||
|
if ( sound != NULL ) {
|
||
|
sound->Set_Transform( muzzle );
|
||
|
sound->Attach_To_Object( Model );
|
||
|
sound->Add_To_Scene( true );
|
||
|
sound->Release_Ref();
|
||
|
}
|
||
|
|
||
|
REF_PTR_RELEASE( owner_ref );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
void WeaponClass::Update( void )
|
||
|
{
|
||
|
// Remove player weapons from vehicle drivers
|
||
|
if ( Get_Owner() &&
|
||
|
Get_Owner()->As_SoldierGameObj() &&
|
||
|
Get_Owner()->As_SoldierGameObj()->Is_In_Vehicle() ) {
|
||
|
IsPrimaryTriggered = false;
|
||
|
IsSecondaryTriggered = false;
|
||
|
}
|
||
|
|
||
|
if ( Get_Style() == WEAPON_HOLD_STYLE_C4 ||
|
||
|
Get_Style() == WEAPON_HOLD_STYLE_BEACON ||
|
||
|
Get_Can_Snipe() )
|
||
|
{
|
||
|
IsSecondaryTriggered = false;
|
||
|
}
|
||
|
|
||
|
WWASSERT( PrimaryAmmoDefinition != NULL );
|
||
|
|
||
|
// Update Burst logic
|
||
|
if ( (int)PrimaryAmmoDefinition->BurstMax == 0 ) { // if not using bursts
|
||
|
BurstCount = -1;
|
||
|
} else {
|
||
|
BurstDelayTimer -= TimeManager::Get_Frame_Seconds();
|
||
|
if ( (!IsPrimaryTriggered && !IsSecondaryTriggered) || ( BurstDelayTimer <= 0.0f ) ) {
|
||
|
BurstDelayTimer = PrimaryAmmoDefinition->BurstDelayTime * FreeRandom.Get_Float( 0.50f, 1.0f );
|
||
|
BurstCount = PrimaryAmmoDefinition->BurstMax;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( TimeManager::Get_Frame_Seconds() != 0 ) {
|
||
|
DidFire = false; // reset the did fire flag for this frame
|
||
|
}
|
||
|
|
||
|
Update_State( TimeManager::Get_Frame_Seconds() );
|
||
|
|
||
|
// If star in first person, force no muzzle flashes on 3rd person model
|
||
|
if ( (Get_Owner() == COMBAT_STAR) && CombatManager::Is_First_Person() ) {
|
||
|
Update_Muzzle_Flash( false, false );
|
||
|
} else {
|
||
|
Update_Muzzle_Flash( DidFire && (TotalRoundsFired & 1), DidFire && (~TotalRoundsFired & 1) );
|
||
|
}
|
||
|
|
||
|
if ( DidFire ) {
|
||
|
Do_Firing_Effects(); // Create muzzle flash and sounds
|
||
|
NextAnimState = (TotalRoundsFired & 1) ? WEAPON_ANIM_FIRING_0 : WEAPON_ANIM_FIRING_1;
|
||
|
} else {
|
||
|
NextAnimState = WEAPON_ANIM_NOT_FIRING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove any continuous effects if the trigger state has changed since last frame
|
||
|
//
|
||
|
if ( (IsPrimaryTriggered == false && LastFrameIsPrimaryTriggered == true) ||
|
||
|
(IsSecondaryTriggered == false && LastFrameIsSecondaryTriggered == true) )
|
||
|
{
|
||
|
Do_Continuous_Effects( false );
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Determine whether or not to trigger the continuous effects (emitters) for
|
||
|
// this weapon.
|
||
|
//
|
||
|
bool emitters_on = IsPrimaryTriggered || IsSecondaryTriggered;
|
||
|
if ( State == STATE_START_SWITCH || State == STATE_END_SWITCH ||
|
||
|
State == STATE_RELOAD || Get_Clip_Rounds() == 0 ) {
|
||
|
emitters_on = false;
|
||
|
}
|
||
|
Do_Continuous_Effects( emitters_on );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remember what our state last frame was...
|
||
|
//
|
||
|
LastFrameIsPrimaryTriggered = IsPrimaryTriggered;
|
||
|
LastFrameIsSecondaryTriggered = IsSecondaryTriggered;
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Simulate firing straight down the muzzle and see where in the world we hit
|
||
|
*/
|
||
|
bool WeaponClass::Cast_Weapon_Down_Muzzle( Vector3 & hit_pos )
|
||
|
{
|
||
|
int muzzle_index = Get_Total_Rounds_Fired() & 1;
|
||
|
Matrix3D muzzle = Get_Muzzle( muzzle_index );
|
||
|
|
||
|
//
|
||
|
// Determine the start and end positions of the ray
|
||
|
//
|
||
|
Vector3 start = muzzle.Get_Translation();
|
||
|
Vector3 end = muzzle * Vector3( Get_Range(), 0, 0 );
|
||
|
LineSegClass ray( start, end );
|
||
|
|
||
|
//
|
||
|
// Cast the ray into the world
|
||
|
//
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
{ WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
Unignore_Owner();
|
||
|
|
||
|
//
|
||
|
// Calculate where in the world this would hit
|
||
|
//
|
||
|
bool retval = false;
|
||
|
if ( res.StartBad == false ) {
|
||
|
hit_pos = start + (end * res.Fraction);
|
||
|
retval = true;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Simulate firing from muzzle to target, see where and if we hit
|
||
|
*/
|
||
|
PhysicalGameObj * WeaponClass::Cast_Weapon( const Vector3 & target )
|
||
|
{
|
||
|
int muzzle_index = Get_Total_Rounds_Fired() & 1;
|
||
|
Matrix3D muzzle = Get_Muzzle( muzzle_index );
|
||
|
|
||
|
Vector3 start = muzzle.Get_Translation();
|
||
|
// Vector3 end = muzzle * Vector3( Get_Range(), 0, 0 );
|
||
|
Vector3 end = target;
|
||
|
LineSegClass ray( start, end );
|
||
|
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
{ WWPROFILE( "Cast Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
Unignore_Owner();
|
||
|
|
||
|
// Debug_Say(( "Weapon Focus %f %f %f\n", bullet_hit_position->X, bullet_hit_position->Y, bullet_hit_position->Z ));
|
||
|
|
||
|
PhysicalGameObj * hit_obj = NULL;
|
||
|
if ( raytest.CollidedPhysObj ) {
|
||
|
if ( raytest.CollidedPhysObj->Get_Observer() != NULL ) {
|
||
|
hit_obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_PhysicalGameObj();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hit_obj;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Check to see if this weapon can reload
|
||
|
*/
|
||
|
bool
|
||
|
WeaponClass::Is_Reload_OK( void )
|
||
|
{
|
||
|
bool retval = (InventoryRounds != 0);
|
||
|
|
||
|
//
|
||
|
// Allow reloads if the infinite ammo cheat is enabled
|
||
|
//
|
||
|
if ( CheatMgrClass::Get_Instance () != NULL &&
|
||
|
CheatMgrClass::Get_Instance ()->Is_Cheat_Enabled( CheatMgrClass::CHEAT_INFINITE_AMMO ) && Owner == COMBAT_STAR )
|
||
|
{
|
||
|
retval = true;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
void WeaponClass::Stop_Firing_Sound( void )
|
||
|
{
|
||
|
if ( FiringSound == NULL ) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stop the sound
|
||
|
//
|
||
|
FiringSound->Stop ();
|
||
|
FiringSound->Remove_From_Scene ();
|
||
|
REF_PTR_RELEASE (FiringSound);
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
// This function calcuates the weapon information that must be displayed for the star's
|
||
|
// weapon in the hud reticle & sniper view. This should only be called for the weapon
|
||
|
// we want to show a reticle for.
|
||
|
|
||
|
// The goal is to find the HitPosition, and HitPositionHot
|
||
|
|
||
|
void WeaponClass::Display_Targeting( void )
|
||
|
{
|
||
|
WWPROFILE( "Cast_Star_Weapon" );
|
||
|
int muzzle_index = Get_Total_Rounds_Fired() & 1;
|
||
|
|
||
|
// vehicles that don't have a turret bone and do have homing weapons target from the camera
|
||
|
Matrix3D muzzle;
|
||
|
VehicleGameObj * vehicle = Get_Owner()->As_VehicleGameObj();;
|
||
|
|
||
|
if (vehicle && (vehicle->Has_Turret() == false) && (PrimaryAmmoDefinition->IsTracking)) {
|
||
|
muzzle = COMBAT_CAMERA->Get_Transform();
|
||
|
muzzle.Rotate_Z( DEG_TO_RADF(90.0) );
|
||
|
muzzle.Rotate_Y( DEG_TO_RADF(90.0) );
|
||
|
} else {
|
||
|
muzzle = Get_Muzzle( muzzle_index );
|
||
|
}
|
||
|
|
||
|
// Cast a ray to see what the weapon is pointing at
|
||
|
Vector3 start = muzzle.Get_Translation();
|
||
|
Vector3 end = muzzle * Vector3( Get_Range(), 0, 0 );
|
||
|
LineSegClass ray( start, end );
|
||
|
|
||
|
CastResultStruct res;
|
||
|
PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
|
||
|
|
||
|
Ignore_Owner();
|
||
|
|
||
|
{
|
||
|
WWPROFILE( "Cast_Ray" );
|
||
|
COMBAT_SCENE->Cast_Ray( raytest );
|
||
|
}
|
||
|
|
||
|
Unignore_Owner();
|
||
|
|
||
|
Vector3 pos;
|
||
|
ray.Compute_Point( raytest.Result->Fraction, &pos );
|
||
|
HUDInfo::Set_Weapon_Target_Position( pos );
|
||
|
|
||
|
// Debug_Say(( "Weapon Focus %f %f %f\n", bullet_hit_position->X, bullet_hit_position->Y, bullet_hit_position->Z ));
|
||
|
|
||
|
if ( raytest.CollidedPhysObj && raytest.CollidedPhysObj->Get_Observer() != NULL ) {
|
||
|
DamageableGameObj * obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_DamageableGameObj();
|
||
|
|
||
|
if ( obj->Is_Targetable() == false ) {
|
||
|
obj = NULL;
|
||
|
}
|
||
|
|
||
|
// if stealthed and enemy, it's not targetable
|
||
|
if ( obj && obj->As_SmartGameObj() && obj->As_SmartGameObj()->Is_Stealthed() &&
|
||
|
COMBAT_STAR && obj->Is_Enemy( COMBAT_STAR ) ) {
|
||
|
obj = NULL;
|
||
|
}
|
||
|
|
||
|
HUDInfo::Set_Weapon_Target_Object( obj );
|
||
|
|
||
|
// We have a problem here with a building here clearing the PT, so dont set
|
||
|
// buildings here, assume they will be set from the ccamera
|
||
|
// Mo, try pulling back the cast above
|
||
|
if ( obj == NULL || obj->As_BuildingGameObj() == NULL ) {
|
||
|
HUDInfo::Set_Info_Object( obj );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// Aquire
|
||
|
if ( AmmoDefinition->AquireTime != 0.0f ) {
|
||
|
SmartGameObj *target = weapon->Pointing_At_Target();
|
||
|
|
||
|
if ( target ) {
|
||
|
if ( AquireTarget != target ) { // locking onto someone new
|
||
|
AquireTarget = target;
|
||
|
AquireTimer = 0.0f;
|
||
|
}
|
||
|
AquireTimer += TimeManager::Get_Frame_Seconds(); // Bump the timer forward in seconds
|
||
|
if (AquireTimer >= AmmoDefinition->AquireTime) {
|
||
|
AquireTimer = AmmoDefinition->AquireTime;
|
||
|
}
|
||
|
} else {
|
||
|
AquireTimer -= TimeManager::Get_Frame_Seconds(); // Bump the timer backwards in seconds
|
||
|
if ( AquireTimer < 0.0f ) {
|
||
|
AquireTimer = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IsAquired = (target != NULL) &&
|
||
|
(AquireTimer >= AmmoDefinition->AquireTime);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
const Matrix3D & WeaponClass::Get_Muzzle( int index )
|
||
|
{
|
||
|
WWASSERT( Get_Owner() != NULL );
|
||
|
|
||
|
// Star's weapon in first person fires from the camera
|
||
|
if ( Get_Owner() == COMBAT_STAR && CombatManager::Is_First_Person() ) {
|
||
|
static Matrix3D _muzzle;
|
||
|
_muzzle.Obj_Look_At( COMBAT_CAMERA->Get_Transform().Get_Translation(), Get_Owner()->Get_Targeting_Pos(), 0 );
|
||
|
return _muzzle;
|
||
|
}
|
||
|
|
||
|
return Get_Owner()->Get_Muzzle( index );
|
||
|
}
|
||
|
|
||
|
|
||
|
void WeaponClass::Ignore_Owner( void )
|
||
|
{
|
||
|
if ( Get_Owner() != NULL ) {
|
||
|
if ( Get_Owner()->Peek_Physical_Object() != NULL ) {
|
||
|
Get_Owner()->Peek_Physical_Object()->Inc_Ignore_Counter();
|
||
|
}
|
||
|
|
||
|
// If Owner is a vehicle, inc all the occupants
|
||
|
if ( Get_Owner()->As_VehicleGameObj() != NULL ) {
|
||
|
Get_Owner()->As_VehicleGameObj()->Ignore_Occupants();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WeaponClass::Unignore_Owner( void )
|
||
|
{
|
||
|
if ( Get_Owner() != NULL ) {
|
||
|
if ( Get_Owner()->Peek_Physical_Object() != NULL ) {
|
||
|
Get_Owner()->Peek_Physical_Object()->Dec_Ignore_Counter();
|
||
|
}
|
||
|
|
||
|
if ( Get_Owner()->As_VehicleGameObj() != NULL ) {
|
||
|
Get_Owner()->As_VehicleGameObj()->Unignore_Occupants();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
*/
|
||
|
MuzzleFlashClass::MuzzleFlashClass( void ) :
|
||
|
MuzzleA0Bone( 0 ),
|
||
|
MuzzleA1Bone( 0 ),
|
||
|
Rotation( 0 ),
|
||
|
Model( 0 ),
|
||
|
LastFlashA0( true ),
|
||
|
LastFlashA1( true )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
MuzzleFlashClass::~MuzzleFlashClass( void )
|
||
|
{
|
||
|
REF_PTR_RELEASE( Model );
|
||
|
}
|
||
|
|
||
|
void MuzzleFlashClass::Init( RenderObjClass * model )
|
||
|
{
|
||
|
REF_PTR_SET( Model, model );
|
||
|
|
||
|
if ( model != NULL ) {
|
||
|
|
||
|
// Find the muzzle bone ( for rotation )
|
||
|
MuzzleA0Bone = model->Get_Bone_Index( "muzzlea0" );
|
||
|
MuzzleA1Bone = model->Get_Bone_Index( "muzzlea1" );
|
||
|
if ( MuzzleA0Bone > 0 ) {
|
||
|
// model->Capture_Bone( MuzzleA0Bone );
|
||
|
}
|
||
|
if ( MuzzleA1Bone > 0 ) {
|
||
|
// model->Capture_Bone( MuzzleA1Bone );
|
||
|
}
|
||
|
|
||
|
Update( false, false );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void MuzzleFlashClass::Update( bool flashA0, bool flashA1 )
|
||
|
{
|
||
|
if ( MuzzleA1Bone == 0 ) {
|
||
|
flashA0 = flashA0 | flashA1;
|
||
|
flashA1 = false;
|
||
|
}
|
||
|
|
||
|
if ( Model != NULL ) {
|
||
|
if (( MuzzleA0Bone > 0 ) && (LastFlashA0 != flashA0)) {
|
||
|
LastFlashA0 = flashA0;
|
||
|
#if 0
|
||
|
if ( flash ) {
|
||
|
Rotation += TimeManager::Get_Frame_Seconds() * 10;
|
||
|
Matrix3D tm(1);
|
||
|
tm.Rotate_X( Rotation );
|
||
|
Model->Control_Bone( MuzzleA0Bone, tm );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (int i=0; i<Model->Get_Num_Sub_Objects_On_Bone( MuzzleA0Bone ); i++) {
|
||
|
RenderObjClass * robj = Model->Get_Sub_Object_On_Bone(i, MuzzleA0Bone );
|
||
|
|
||
|
// hide all meshes named "muzzleflash"
|
||
|
if (strstr(robj->Get_Name(),"MUZZLEFLASH") || strstr(robj->Get_Name(),"MZ")) {
|
||
|
robj->Set_Hidden( !flashA0 );
|
||
|
}
|
||
|
|
||
|
robj->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (( MuzzleA1Bone > 0 ) && (LastFlashA1 != flashA1)) {
|
||
|
|
||
|
LastFlashA1 = flashA1;
|
||
|
#if 0
|
||
|
if ( flash ) {
|
||
|
Rotation += TimeManager::Get_Frame_Seconds() * 10;
|
||
|
Matrix3D tm(1);
|
||
|
tm.Rotate_X( Rotation );
|
||
|
Model->Control_Bone( MuzzleA1Bone, tm );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (int i=0; i<Model->Get_Num_Sub_Objects_On_Bone( MuzzleA1Bone ); i++) {
|
||
|
RenderObjClass * robj = Model->Get_Sub_Object_On_Bone(i, MuzzleA1Bone );
|
||
|
// hide all meshes named "muzzleflash"
|
||
|
if (strstr(robj->Get_Name(),"MUZZLEFLASH") || strstr(robj->Get_Name(),"MZ")) {
|
||
|
robj->Set_Hidden( !flashA1 );
|
||
|
}
|
||
|
robj->Release_Ref();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|