Files
2025-07-10 01:02:24 -07:00

429 lines
14 KiB
C++

// Copyright 2016 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "Engine/EngineTypes.h"
#include "EBGun.h"
#include "EBWeaponData.h"
#include "EBBulletProperties.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "EBPlayerWeaponController.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponChanged, UEBWeaponData*, NewWeapon);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponFired, UEBWeaponData*, WeaponData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponReloaded, UEBWeaponData*, WeaponData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponEmpty, UEBWeaponData*, WeaponData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAmmoChanged, int32, CurrentAmmo, int32, ReserveAmmo);
/**
* Player Weapon Controller - AAA Game Industry Standard Scene Component
*
* This follows the pattern used in games like Call of Duty:
* - Single persistent weapon actor (never destroyed)
* - Weapon data assets for all variations
* - Hot-swap meshes, stats, and behavior
* - No actor spawning/despawning
* - Inventory is just array of weapon data
*
* Benefits:
* - Zero GC pressure from spawning/destroying
* - Instant weapon switching (just data swap)
* - Memory efficient
* - Network friendly
* - Scales to hundreds of weapons
* - Direct positioning control as scene component
* - Gun spawns directly on this component's transform
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup=(EasyBallistics), meta=(BlueprintSpawnableComponent, DisplayName = "EB Player Weapon Controller", ToolTip = "Player weapon controller scene component for managing weapon inventory and interactions with direct positioning"))
class EASYBALLISTICS_API UEBPlayerWeaponController : public USceneComponent
{
GENERATED_BODY()
public:
UEBPlayerWeaponController();
// ========================================
// WEAPON SYSTEM
// ========================================
/** Single persistent weapon actor */
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_WeaponActor, Category = "Weapon System")
AEBGun* WeaponActor;
/** Current weapon data */
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_CurrentWeaponData, Category = "Weapon System")
UEBWeaponData* CurrentWeaponData;
/** Player's weapon inventory (just data assets) */
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_WeaponInventory, Category = "Weapon System", meta = (AllowedClasses = "EBWeaponData", DisplayName = "Weapon Inventory", ToolTip = "Array of weapon data assets available to the player"))
TArray<UEBWeaponData*> WeaponInventory;
/** Current weapon index in inventory */
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_CurrentWeaponIndex, Category = "Weapon System")
int32 CurrentWeaponIndex = 0;
/** Default weapon to spawn with */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon System", meta = (AllowedClasses = "EBWeaponData", DisplayName = "Default Weapon", ToolTip = "The default weapon data asset to equip on spawn"))
UEBWeaponData* DefaultWeapon;
/** Auto-equip first weapon on begin play */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon System")
bool bAutoEquipOnBeginPlay = true;
// ========================================
// AMMO SYSTEM
// ========================================
/** Current ammo in magazine */
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Ammo System")
int32 CurrentAmmo = 0;
/** Reserve ammo */
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Ammo System")
int32 ReserveAmmo = 0;
/** Infinite ammo cheat */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ammo System")
bool bInfiniteAmmo = false;
// ========================================
// WEAPON SPAWN SETTINGS
// ========================================
/** Spawn transform offset relative to this component */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Spawn", meta = (DisplayName = "Weapon Spawn Offset", ToolTip = "Transform offset for spawning the weapon relative to this component"))
FTransform WeaponSpawnOffset = FTransform::Identity;
// ========================================
// NIAGARA VFX SYSTEM
// ========================================
/** Persistent Niagara components for weapon effects */
UPROPERTY(BlueprintReadOnly, Category = "Niagara VFX")
UNiagaraComponent* MuzzleFlashComponent;
UPROPERTY(BlueprintReadOnly, Category = "Niagara VFX")
UNiagaraComponent* MuzzleSmokeComponent;
UPROPERTY(BlueprintReadOnly, Category = "Niagara VFX")
UNiagaraComponent* BarrelHeatComponent;
UPROPERTY(BlueprintReadOnly, Category = "Niagara VFX")
UNiagaraComponent* AttachmentGlowComponent;
/** Current weapon heat level (0-1) */
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Niagara VFX")
float CurrentHeatLevel = 0.0f;
/** Enable advanced VFX features */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Niagara VFX")
bool bEnableAdvancedVFX = true;
// ========================================
// INPUT ACTIONS
// ========================================
/** Primary fire */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void Fire();
/** Stop firing */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void StopFire();
/** Reload weapon */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void Reload();
/** Switch to next weapon */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void NextWeapon();
/** Switch to previous weapon */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void PreviousWeapon();
/** Switch to specific weapon by index */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void SwitchToWeapon(int32 WeaponIndex);
/** Switch to specific weapon by data */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void SwitchToWeaponData(UEBWeaponData* WeaponData);
/** Toggle fire mode */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void ToggleFireMode();
/** Toggle safety */
UFUNCTION(BlueprintCallable, Category = "Weapon Input")
void ToggleSafety();
// ========================================
// WEAPON MANAGEMENT
// ========================================
/** Add weapon to inventory */
UFUNCTION(BlueprintCallable, Category = "Weapon Management")
void AddWeapon(UEBWeaponData* WeaponData);
/** Remove weapon from inventory */
UFUNCTION(BlueprintCallable, Category = "Weapon Management")
void RemoveWeapon(UEBWeaponData* WeaponData);
/** Clear all weapons */
UFUNCTION(BlueprintCallable, Category = "Weapon Management")
void ClearWeapons();
/** Set entire weapon inventory */
UFUNCTION(BlueprintCallable, Category = "Weapon Management")
void SetWeaponInventory(const TArray<UEBWeaponData*>& NewInventory);
/** Apply weapon data to actor */
UFUNCTION(BlueprintCallable, Category = "Weapon Management")
void ApplyWeaponData(UEBWeaponData* WeaponData);
// ========================================
// AMMO MANAGEMENT
// ========================================
/** Add ammo to reserve */
UFUNCTION(BlueprintCallable, Category = "Ammo Management")
void AddAmmo(int32 Amount);
/** Set current ammo */
UFUNCTION(BlueprintCallable, Category = "Ammo Management")
void SetCurrentAmmo(int32 Amount);
/** Set reserve ammo */
UFUNCTION(BlueprintCallable, Category = "Ammo Management")
void SetReserveAmmo(int32 Amount);
/** Refill all ammo */
UFUNCTION(BlueprintCallable, Category = "Ammo Management")
void RefillAmmo();
// ========================================
// STATUS QUERIES
// ========================================
/** Can weapon fire */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
bool CanFire() const;
/** Is weapon equipped */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
bool HasWeapon() const;
/** Get current weapon data */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
UEBWeaponData* GetCurrentWeaponData() const { return CurrentWeaponData; }
/** Get weapon count */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
int32 GetWeaponCount() const { return WeaponInventory.Num(); }
/** Get weapon at index */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
UEBWeaponData* GetWeaponAtIndex(int32 Index) const;
/** Find weapon index */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
int32 FindWeaponIndex(UEBWeaponData* WeaponData) const;
/** Has weapon in inventory */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
bool HasWeaponInInventory(UEBWeaponData* WeaponData) const;
/** Get current fire mode */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
EFireMode GetFireMode() const;
/** Is safety on */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
bool IsSafetyOn() const;
/** Get muzzle transform */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
FTransform GetMuzzleTransform() const;
/** Get current heat level */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
float GetHeatLevel() const { return CurrentHeatLevel; }
/** Is weapon overheated */
UFUNCTION(BlueprintPure, Category = "Weapon Status")
bool IsOverheated() const;
// ========================================
// DEBUG FUNCTIONS
// ========================================
/** Print comprehensive weapon debug information to log */
UFUNCTION(BlueprintCallable, Category = "Debug")
void PrintWeaponDebugInfo() const;
// ========================================
// NIAGARA VFX CONTROL
// ========================================
/** Trigger muzzle flash effect */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void TriggerMuzzleFlash();
/** Trigger shell ejection effect */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void TriggerShellEjection();
/** Spawn tracer effect */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void SpawnTracerEffect(const FVector& StartLocation, const FVector& EndLocation);
/** Spawn impact effect */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void SpawnImpactEffect(const FVector& ImpactLocation, const FVector& ImpactNormal, UPhysicalMaterial* SurfaceMaterial = nullptr);
/** Update weapon heat effects */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void UpdateHeatEffects();
/** Set Niagara parameter on all weapon effects */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void SetNiagaraFloatParameter(const FString& ParameterName, float Value);
/** Set Niagara vector parameter on all weapon effects */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void SetNiagaraVectorParameter(const FString& ParameterName, const FVector& Value);
/** Set Niagara color parameter on all weapon effects */
UFUNCTION(BlueprintCallable, Category = "Niagara VFX")
void SetNiagaraColorParameter(const FString& ParameterName, const FLinearColor& Value);
// ========================================
// EVENTS
// ========================================
/** Called when weapon changes */
UPROPERTY(BlueprintAssignable, Category = "Weapon Events")
FOnWeaponChanged OnWeaponChanged;
/** Called when weapon fires */
UPROPERTY(BlueprintAssignable, Category = "Weapon Events")
FOnWeaponFired OnWeaponFired;
/** Called when weapon reloads */
UPROPERTY(BlueprintAssignable, Category = "Weapon Events")
FOnWeaponReloaded OnWeaponReloaded;
/** Called when weapon is empty */
UPROPERTY(BlueprintAssignable, Category = "Weapon Events")
FOnWeaponEmpty OnWeaponEmpty;
/** Called when ammo changes */
UPROPERTY(BlueprintAssignable, Category = "Weapon Events")
FOnAmmoChanged OnAmmoChanged;
/** Called when WeaponActor is replicated */
UFUNCTION()
void OnRep_WeaponActor();
// ========================================
// OVERRIDES
// ========================================
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
protected:
// ========================================
// INTERNAL FUNCTIONS
// ========================================
/** Create weapon actor */
void CreateWeaponActor();
/** Apply weapon data to gun actor */
void InternalApplyWeaponData(UEBWeaponData* WeaponData);
/** Update ammo from weapon data */
void UpdateAmmoFromWeaponData();
/** Handle weapon fired */
UFUNCTION()
void HandleWeaponFired(UEBBulletPropertiesAsset* BulletType);
/** Handle magazine empty */
UFUNCTION()
void HandleMagazineEmpty();
/** Bind weapon events */
void BindWeaponEvents();
/** Unbind weapon events */
void UnbindWeaponEvents();
/** Check authority */
bool HasAuthority() const;
/** Create Niagara components */
void CreateNiagaraComponents();
/** Update Niagara components with weapon data */
void UpdateNiagaraComponents();
/** Apply Niagara parameters from weapon data */
void ApplyNiagaraParameters(UNiagaraComponent* Component, const FNiagaraEffectParams& Params);
/** Update heat level and effects */
void UpdateWeaponHeat(float DeltaTime);
private:
// ========================================
// SERVER RPCS
// ========================================
UFUNCTION(Server, Reliable)
void ServerSwitchToWeapon(int32 WeaponIndex);
UFUNCTION(Server, Reliable)
void ServerSwitchToWeaponData(UEBWeaponData* WeaponData);
UFUNCTION(Server, Reliable)
void ServerAddWeapon(UEBWeaponData* WeaponData);
UFUNCTION(Server, Reliable)
void ServerRemoveWeapon(UEBWeaponData* WeaponData);
UFUNCTION(Server, Reliable)
void ServerSetCurrentAmmo(int32 Amount);
UFUNCTION(Server, Reliable)
void ServerSetReserveAmmo(int32 Amount);
UFUNCTION(Server, Reliable)
void ServerToggleFireMode();
UFUNCTION(Server, Reliable)
void ServerToggleSafety();
// ========================================
// REPLICATION
// ========================================
UFUNCTION()
void OnRep_CurrentWeaponData();
UFUNCTION()
void OnRep_WeaponInventory();
UFUNCTION()
void OnRep_CurrentWeaponIndex();
};