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

1952 lines
53 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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();
}
}
}
}