Files
BallisticsDocs/Source/EasyBallistics/Public/EBGun.h
T
2025-07-10 01:02:24 -07:00

505 lines
20 KiB
C++

// Copyright 2016 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/StaticMeshComponent.h"
#include "EBBarrel.h"
#include "EBMagazine.h"
#include "EBWeaponConfiguration.h"
#include "EBBulletProperties.h"
#include "EBGun.generated.h"
UENUM(BlueprintType)
enum class EGunState : uint8
{
GS_Ready UMETA(DisplayName = "Ready"),
GS_Firing UMETA(DisplayName = "Firing"),
GS_Reloading UMETA(DisplayName = "Reloading"),
GS_Jammed UMETA(DisplayName = "Jammed"),
GS_Empty UMETA(DisplayName = "Empty"),
GS_SafetyOn UMETA(DisplayName = "Safety On"),
GS_Malfunction UMETA(DisplayName = "Malfunction")
};
/**
* EasyBallistics Gun Actor - Complete weapon system with organized settings
*
* PROPERTY ORGANIZATION:
* 1. Configuration - Weapon config asset
* 2. Barrel & Ballistics - Essential shooting properties (START HERE)
* 3. Fire Control - Fire modes, rates, burst settings
* 4. Advanced Ballistics - Fine-tuning for realism
* 5. Gatling Settings - For minigun/gatling type weapons
* 6. Gun State - Read-only operational status
* 7. Firing State - Read-only runtime firing status
*
* BLUEPRINT NODE ORGANIZATION:
* All nodes are under "Ballistics" with logical subcategories:
*
* Ballistics|
* ├── Firing|
* │ ├── Trigger (PullTrigger, ReleaseTrigger)
* │ ├── Advanced (Shoot)
* │ ├── Gatling (GatlingSpool)
* │ └── Bullet Spawn (SpawnBullet)
* ├── Controls|
* │ ├── Safety (SetSafety)
* │ └── Action (CockHammer, ReleaseHammer)
* ├── Ammunition|
* │ ├── Magazine (LoadMagazine, EjectMagazine, InsertMagazine)
* │ └── Chamber (ChargingHandle, EjectChamberedRound)
* ├── Status|
* │ ├── Readiness (CanFire, IsLoaded)
* │ └── Ammunition (GetRoundsInMagazine, GetTotalRounds, GetChamberedBulletType)
* ├── Configuration|
* │ ├── Setup (ApplyWeaponConfiguration)
* │ ├── Fire Control (SetFireMode, GetFireMode, SwitchFireMode)
* │ └── Mesh (SetMeshType, GetActiveMeshComponent, GetMuzzleTransform, SetMuzzleSocketName)
* ├── Calculations|
* │ ├── Properties (GetBarrelLength, GetEffectiveMuzzleVelocity, GetBarrelAccuracy)
* │ └── Recoil (CalculateRecoilImpulse)
* ├── Prediction|
* │ ├── Trajectory (PredictHit)
* │ └── Targeting (CalculateAimDirection)
* ├── Events|
* │ ├── State (OnGunStateChanged)
* │ ├── Firing (OnGunFired)
* │ ├── Ammunition (OnMagazineEmpty, OnMagazineChanged)
* │ └── Safety (OnSafetyChanged)
* └── Legacy|
* └── Barrel (IsBarrelConnected)
*/
UCLASS(Blueprintable, BlueprintType)
class EASYBALLISTICS_API AEBGun : public AActor
{
GENERATED_BODY()
public:
AEBGun();
// Core Components - Gun Mesh (can be either skeletal or static)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USkeletalMeshComponent* SkeletalGunMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent* StaticGunMesh;
// Gun Type Configuration
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components", meta = (ToolTip = "Use skeletal mesh (true) or static mesh (false). Set this to false to use StaticGunMesh."))
bool bUseSkeletalMesh = true;
// Firing system integrated into gun (replacing barrel dependency)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gun Configuration", meta = (EditCondition = "!bUseSkeletalMesh", ToolTip = "Muzzle transform for static mesh mode"))
FTransform MuzzleTransform;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gun Configuration", meta = (EditCondition = "bUseSkeletalMesh", ToolTip = "Socket name for muzzle in skeletal mesh mode"))
FString MuzzleSocketName = TEXT("MuzzleSocket");
// Legacy barrel component for backward compatibility
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (ToolTip = "Legacy barrel component - firing logic now handled by gun"))
UEBBarrel* Barrel;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UEBMagazine* Magazine;
// ========================================
// MESH SETUP (Configure First)
// ========================================
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "0. Mesh Setup", meta = (ToolTip = "Use skeletal mesh (true) or static mesh (false)"))
bool bUseSkeletal = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "0. Mesh Setup", meta = (EditCondition = "bUseSkeletal", ToolTip = "Skeletal mesh for the gun"))
USkeletalMesh* SkeletalMeshAsset;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "0. Mesh Setup", meta = (EditCondition = "!bUseSkeletal", ToolTip = "Static mesh for the gun"))
UStaticMesh* StaticMeshAsset;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "0. Mesh Setup", meta = (EditCondition = "bUseSkeletal", ToolTip = "Socket name for muzzle in skeletal mesh mode"))
FString MuzzleSocket = TEXT("MuzzleSocket");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ballistics|Calculations|Recoil", meta = (ToolTip = "Can process next shot based on current state"))
bool bCanProcessNextShot = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "0. Mesh Setup", meta = (EditCondition = "!bUseSkeletal", ToolTip = "Muzzle transform for static mesh mode"))
FTransform MuzzleOffset = FTransform::Identity;
// ========================================
// CONFIGURATION
// ========================================
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "1. Configuration")
UEBWeaponConfiguration* WeaponConfig;
// ========================================
// BARREL & BALLISTICS (Most Common Settings)
// ========================================
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2. Barrel & Ballistics", meta = (ClampMin = "1.0", ClampMax = "50.0", ToolTip = "Barrel length in inches"))
float BarrelLength = 16.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2. Barrel & Ballistics", meta = (ClampMin = "10000.0", ClampMax = "200000.0", ToolTip = "Minimum muzzle velocity in cm/s"))
float MuzzleVelocityMin = 91440.0f; // ~3000 fps
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2. Barrel & Ballistics", meta = (ClampMin = "10000.0", ClampMax = "200000.0", ToolTip = "Maximum muzzle velocity in cm/s"))
float MuzzleVelocityMax = 91440.0f; // ~3000 fps
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2. Barrel & Ballistics", meta = (ClampMin = "0.0", ClampMax = "1.0", ToolTip = "Spread in radians - 0 = perfect accuracy"))
float Spread = 0.0f;
// ========================================
// FIRE CONTROL (Essential Settings)
// ========================================
UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "3. Fire Control")
EFireMode FireMode = EFireMode::FM_Auto;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3. Fire Control", meta = (ClampMin = "0.1", ClampMax = "50.0", ToolTip = "Fire rate in rounds per second"))
float FireRateMin = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3. Fire Control", meta = (ClampMin = "0.1", ClampMax = "50.0", ToolTip = "Fire rate in rounds per second"))
float FireRateMax = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3. Fire Control", meta = (ClampMin = "1", ClampMax = "20", ToolTip = "Number of rounds per burst"))
int32 BurstCount = 3;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3. Fire Control", meta = (ClampMin = "0.0", ClampMax = "10.0", ToolTip = "Cooldown between bursts in seconds"))
float BurstCooldown = 0.0f;
// ========================================
// ADVANCED BALLISTICS (Fine-tuning)
// ========================================
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "4. Advanced Ballistics", meta = (ClampMin = "1.0", ClampMax = "20.0", ToolTip = "Rifling twist rate (1:X)"))
float RiflingTwist = 7.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "4. Advanced Ballistics", meta = (ClampMin = "0.05", ClampMax = "1.0", ToolTip = "Bore radius in inches"))
float BoreRadius = 0.112f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "4. Advanced Ballistics", meta = (ClampMin = "0.0", ClampMax = "10.0", ToolTip = "Spread bias - higher = more accurate on average"))
float SpreadBias = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "4. Advanced Ballistics", meta = (ClampMin = "0.5", ClampMax = "2.0", ToolTip = "Minimum velocity multiplier"))
float MuzzleVelocityMultiplierMin = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "4. Advanced Ballistics", meta = (ClampMin = "0.5", ClampMax = "2.0", ToolTip = "Maximum velocity multiplier"))
float MuzzleVelocityMultiplierMax = 1.0f;
// ========================================
// GATLING/MINIGUN (Specialized Settings)
// ========================================
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "5. Gatling Settings", meta = (ToolTip = "Automatically spool up when trigger held"))
bool bGatlingAutoSpool = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "5. Gatling Settings", meta = (ClampMin = "0.1", ClampMax = "10.0", ToolTip = "Time to spool up in seconds"))
float GatlingSpoolUpTime = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "5. Gatling Settings", meta = (ClampMin = "0.1", ClampMax = "10.0", ToolTip = "Time to spool down in seconds"))
float GatlingSpoolDownTime = 1.0f;
UPROPERTY(BlueprintReadOnly, Category = "5. Gatling Settings", meta = (ToolTip = "Current gatling phase (0-1)"))
float GatlingPhase = 0.0f;
// ========================================
// GUN STATE (Read-Only Status)
// ========================================
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_GunState, Category = "6. Gun State", meta = (ToolTip = "Current operational state of the gun"))
EGunState CurrentGunState = EGunState::GS_Ready;
UPROPERTY(BlueprintReadOnly, Replicated, Category = "6. Gun State", meta = (ToolTip = "Is the safety engaged"))
bool bSafetyOn = false;
UPROPERTY(BlueprintReadOnly, Replicated, Category = "6. Gun State", meta = (ToolTip = "Is there a round chambered"))
bool bChamberedRound = false;
UPROPERTY(BlueprintReadOnly, Replicated, Category = "6. Gun State", meta = (ToolTip = "Is the hammer cocked"))
bool bHammerCocked = false;
// Chambered Round
UPROPERTY(BlueprintReadOnly, Replicated, Category = "6. Gun State", meta = (ToolTip = "Current bullet type in chamber"))
UEBBulletPropertiesAsset* ChamberedBulletType;
// ========================================
// FIRING STATE (Runtime Status)
// ========================================
UPROPERTY(BlueprintReadOnly, Replicated, Category = "7. Firing State", meta = (ToolTip = "Is currently shooting"))
bool bShooting = false;
UPROPERTY(BlueprintReadOnly, Replicated, Category = "7. Firing State", meta = (ToolTip = "Is gatling gun spooling"))
bool bSpooling = false;
UPROPERTY(BlueprintReadOnly, Category = "7. Firing State", meta = (ToolTip = "Current gatling rounds per second"))
float GatlingRPS = 0.0f;
UPROPERTY(BlueprintReadOnly, Category = "7. Firing State", meta = (ToolTip = "Current cooldown remaining"))
float Cooldown = 0.0f;
UPROPERTY(BlueprintReadOnly, Category = "7. Firing State", meta = (ToolTip = "Burst shots remaining"))
int32 BurstRemaining = 0;
// ========================================
// BLUEPRINT FUNCTIONS
// ========================================
// Primary Firing Interface
UFUNCTION(BlueprintCallable, Category = "Ballistics|Firing|Trigger")
void PullTrigger();
UFUNCTION(BlueprintCallable, Category = "Ballistics|Firing|Trigger")
void ReleaseTrigger();
// Gun Controls
UFUNCTION(BlueprintCallable, Category = "Ballistics|Controls|Safety")
void SetSafety(bool bNewSafetyState);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Controls|Action")
void CockHammer();
UFUNCTION(BlueprintCallable, Category = "Ballistics|Controls|Action")
void ReleaseHammer();
// Ammunition Management
UFUNCTION(BlueprintCallable, Category = "Ballistics|Ammunition|Magazine")
void LoadMagazine(TArray<UEBBulletPropertiesAsset*> BulletTypes);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Ammunition|Magazine")
void EjectMagazine();
UFUNCTION(BlueprintCallable, Category = "Ballistics|Ammunition|Magazine")
void InsertMagazine(UEBMagazine* NewMagazine);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Ammunition|Chamber")
void ChargingHandle();
UFUNCTION(BlueprintCallable, Category = "Ballistics|Ammunition|Chamber")
void EjectChamberedRound();
// State Queries
UFUNCTION(BlueprintPure, Category = "Ballistics|Status|Readiness")
bool CanFire() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Status|Readiness")
bool IsLoaded() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Status|Ammunition")
int32 GetRoundsInMagazine() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Status|Ammunition")
int32 GetTotalRounds() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Status|Ammunition")
UEBBulletPropertiesAsset* GetChamberedBulletType() const;
// Configuration
UFUNCTION(BlueprintCallable, Category = "Ballistics|Configuration|Setup")
void ApplyWeaponConfiguration(UEBWeaponConfiguration* NewConfig);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Configuration|Fire Control")
void SetFireMode(EFireMode NewFireMode);
UFUNCTION(BlueprintPure, Category = "Ballistics|Configuration|Fire Control")
EFireMode GetFireMode() const;
// Gun Mesh Management
UFUNCTION(BlueprintCallable, Category = "Ballistics|Configuration|Mesh")
void SetMeshType(bool bUseSkeletalMeshParam);
UFUNCTION(BlueprintPure, Category = "Ballistics|Configuration|Mesh")
UPrimitiveComponent* GetActiveMeshComponent() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Configuration|Mesh")
FTransform GetMuzzleTransform() const;
UFUNCTION(BlueprintCallable, Category = "Ballistics|Configuration|Mesh")
void SetMuzzleSocketName(const FString& NewSocketName);
// Ballistics Calculations
UFUNCTION(BlueprintPure, Category = "Ballistics|Calculations|Properties")
float GetBarrelLength() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Calculations|Properties")
float GetEffectiveMuzzleVelocity() const;
UFUNCTION(BlueprintPure, Category = "Ballistics|Calculations|Properties")
float GetBarrelAccuracy() const;
UFUNCTION(BlueprintCallable, Category = "Ballistics|Calculations|Recoil")
FVector CalculateRecoilImpulse() const;
// Legacy barrel support
UFUNCTION(BlueprintPure, Category = "Ballistics|Legacy|Barrel")
bool IsBarrelConnected() const;
// ========================================
// BLUEPRINT EVENTS
// ========================================
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGunStateChanged, EGunState, NewState);
UPROPERTY(BlueprintAssignable, Category = "Ballistics|Events|State")
FOnGunStateChanged OnGunStateChanged;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGunFired, UEBBulletPropertiesAsset*, BulletType);
UPROPERTY(BlueprintAssignable, Category = "Ballistics|Events|Firing")
FOnGunFired OnGunFired;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMagazineEmpty);
UPROPERTY(BlueprintAssignable, Category = "Ballistics|Events|Ammunition")
FOnMagazineEmpty OnMagazineEmpty;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMagazineChanged, UEBMagazine*, NewMagazine);
UPROPERTY(BlueprintAssignable, Category = "Ballistics|Events|Ammunition")
FOnMagazineChanged OnMagazineChanged;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSafetyChanged, bool, bNewSafetyState);
UPROPERTY(BlueprintAssignable, Category = "Ballistics|Events|Safety")
FOnSafetyChanged OnSafetyChanged;
// Networking
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
// Advanced Firing Functions
UFUNCTION(BlueprintCallable, Category = "Ballistics|Firing|Advanced")
void Shoot(bool bTrigger);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Firing|Gatling")
void GatlingSpool(bool bSpool);
UFUNCTION(BlueprintCallable, Category = "Ballistics|Configuration|Fire Control")
void SwitchFireMode(EFireMode NewFireMode);
// ========================================
// BALLISTICS PREDICTION & CALCULATIONS
// ========================================
UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "IgnoredActors"), Category = "Ballistics|Prediction|Trajectory")
void PredictHit(bool& bHit, FHitResult& TraceResult, FVector& HitLocation, float& HitTime,
AActor*& HitActor, TArray<FVector>& Trajectory,
TSubclassOf<class AEBBullet> BulletClass,
const TArray<AActor*>& IgnoredActors,
float MaxTime = 10.0f, float Step = 0.1f) const;
UFUNCTION(BlueprintCallable, Category = "Ballistics|Prediction|Targeting")
void CalculateAimDirection(TSubclassOf<class AEBBullet> BulletClass, FVector TargetLocation, FVector TargetVelocity,
FVector& AimDirection, FVector& PredictedTargetLocation, FVector& PredictedIntersectionLocation,
float& PredictedFlightTime, float& Error, float MaxTime = 10.0f, float Step = 0.1f, int32 NumIterations = 4) const;
protected:
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
// Replication
UFUNCTION()
void OnRep_GunState();
// Internal Functions
UFUNCTION(BlueprintImplementableEvent, Category = "Gun|Internal")
void OnGunStateChangedInternal(EGunState OldState, EGunState NewState);
UFUNCTION(BlueprintImplementableEvent, Category = "Gun|Internal")
void OnFireAnimationStart();
UFUNCTION(BlueprintImplementableEvent, Category = "Gun|Internal")
void OnReloadAnimationStart();
UFUNCTION(BlueprintImplementableEvent, Category = "Gun|Internal")
void OnJamAnimation();
void SetGunState(EGunState NewState);
void ProcessFiring();
void ProcessReloading();
void CycleAction();
bool FeedNextRound();
// Timers
FTimerHandle FireRateTimer;
FTimerHandle ReloadTimer;
FTimerHandle CycleTimer;
// Firing Control
bool bTriggerPressed = false;
int32 BurstShotsFired = 0;
// Random Stream for firing calculations
FRandomStream RandomStream;
private:
EGunState PreviousGunState;
// Server Functions
UFUNCTION(Server, Reliable)
void ServerPullTrigger();
UFUNCTION(Server, Reliable)
void ServerReleaseTrigger();
UFUNCTION(Server, Reliable)
void ServerSetSafety(bool bNewSafetyState);
UFUNCTION(Server, Reliable)
void ServerEjectMagazine();
UFUNCTION(Server, Reliable)
void ServerInsertMagazine(UEBMagazine* NewMagazine);
UFUNCTION(Server, Reliable)
void ServerChargingHandle();
UFUNCTION(Server, Reliable)
void ServerSetFireMode(EFireMode NewFireMode);
UFUNCTION(Server, Reliable, WithValidation)
void ServerShoot(bool bTrigger);
UFUNCTION(Server, Reliable, WithValidation)
void ServerGatlingSpool(bool bSpool);
UFUNCTION(Server, Reliable, WithValidation)
void ServerSwitchFireMode(EFireMode NewFireMode);
// Server RPC Implementation Declarations
void ServerPullTrigger_Implementation();
void ServerReleaseTrigger_Implementation();
void ServerSetSafety_Implementation(bool bNewSafetyState);
void ServerEjectMagazine_Implementation();
void ServerInsertMagazine_Implementation(UEBMagazine* NewMagazine);
void ServerChargingHandle_Implementation();
void ServerSetFireMode_Implementation(EFireMode NewFireMode);
void ServerShoot_Implementation(bool bTrigger);
bool ServerShoot_Validate(bool bTrigger);
void ServerGatlingSpool_Implementation(bool bSpool);
bool ServerGatlingSpool_Validate(bool bSpool);
void ServerSwitchFireMode_Implementation(EFireMode NewFireMode);
bool ServerSwitchFireMode_Validate(EFireMode NewFireMode);
// Multicast Functions
UFUNCTION(NetMulticast, Reliable)
void MulticastOnGunFired(UEBBulletPropertiesAsset* BulletType);
UFUNCTION(NetMulticast, Reliable)
void MulticastShotFired();
UFUNCTION(NetMulticast, Reliable)
void MulticastOnMagazineChanged(UEBMagazine* NewMagazine);
UFUNCTION(NetMulticast, Reliable)
void MulticastOnSafetyChanged(bool bNewSafetyState);
// Event Handlers
UFUNCTION()
void OnMagazineEmptyHandler(UEBMagazine* EmptyMagazine);
UFUNCTION()
void OnBulletFedHandler(UEBMagazine* SourceMagazine, UEBBulletPropertiesAsset* BulletType);
UFUNCTION()
void OnBarrelShotFiredHandler();
// Internal firing function
void FireShot();
};