// 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 "EBWeaponData.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") UEBWeaponData* WeaponData; // ======================================== // 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 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 ApplyWeaponData(UEBWeaponData* NewData); 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& 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& Trajectory, TSubclassOf BulletClass, const TArray& IgnoredActors, float MaxTime = 10.0f, float Step = 0.1f) const; UFUNCTION(BlueprintCallable, Category = "Ballistics|Prediction|Targeting") void CalculateAimDirection(TSubclassOf 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(); };