- Implemented USS_EnhancedInputManager to manage input contexts with priority. - Added methods for pushing, popping, and querying input contexts. - Integrated input mode switching and key rebinding functionality. feat: Introduce Inventory System Component for item management - Created UBPC_InventorySystem to handle inventory operations such as adding, removing, and sorting items. - Implemented weight management and slot organization features. - Added event dispatchers for inventory changes. feat: Develop Item Data Asset for item definitions - Established UDA_ItemData as a base class for all items, encapsulating properties like type, weight, and stack limits. - Included conditional sub-data structures for equipment, consumables, and inspect data. feat: Create State Manager Component for player state management - Developed UBPC_StateManager to manage player action states and overlays. - Implemented gating logic for action requests and vital sign tracking. feat: Implement Save Manager for game state persistence - Introduced USS_SaveManager for handling save/load operations and slot management. - Utilized FArchive for efficient binary serialization. feat: Implement Damage Reception System for combat mechanics - Created UBPC_DamageReceptionSystem to process incoming damage and apply resistance calculations. - Added event dispatchers for damage reception and hit reactions.
137 lines
3.9 KiB
C++
137 lines
3.9 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
// UE5 Modular Game Framework — BPC_DamageReceptionSystem Implementation
|
|
|
|
#include "Weapons/BPC_DamageReceptionSystem.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogDamage, Log, All);
|
|
|
|
UBPC_DamageReceptionSystem::UBPC_DamageReceptionSystem()
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Damage Calculation — Hot Path
|
|
// ============================================================================
|
|
|
|
float UBPC_DamageReceptionSystem::ApplyDamage(float RawDamage, AActor* DamageCauser,
|
|
FGameplayTag DamageType, FVector HitLocation, FVector HitDirection)
|
|
{
|
|
if (RawDamage <= 0.0f || !DamageType.IsValid())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Step 1: Calculate damage multiplier from modifiers.
|
|
float Multiplier = GetDamageMultiplier(DamageType);
|
|
|
|
// Step 2: Apply resistance.
|
|
float Resistance = CalculateResistance(DamageType);
|
|
float ResistedAmount = RawDamage * Resistance;
|
|
|
|
// Step 3: Calculate final damage.
|
|
float FinalDamage = (RawDamage * Multiplier) - ResistedAmount;
|
|
FinalDamage = FMath::Max(FinalDamage, 0.0f); // No negative damage.
|
|
|
|
// Check for flat reduction modifiers.
|
|
for (const FDamageModifier& Mod : DamageModifiers)
|
|
{
|
|
if (Mod.DamageType == DamageType && Mod.bFlatReduction)
|
|
{
|
|
FinalDamage = FMath::Max(FinalDamage - Mod.FlatReduction, 1.0f); // Minimum 1 damage.
|
|
}
|
|
}
|
|
|
|
// Step 4: Apply shield absorption if available.
|
|
if (CachedShieldSystem)
|
|
{
|
|
// Shield absorbs damage before health.
|
|
// FinalDamage = CachedShieldSystem->AbsorbDamage(FinalDamage);
|
|
}
|
|
|
|
// Step 5: Route to health system.
|
|
if (CachedHealthSystem)
|
|
{
|
|
// CachedHealthSystem->ApplyHealthDamage(FinalDamage);
|
|
}
|
|
|
|
// Step 6: Evaluate hit reaction.
|
|
EvaluateHitReaction(FinalDamage, DamageCauser, HitDirection);
|
|
|
|
// Step 7: Broadcast.
|
|
OnDamageReceived.Broadcast(RawDamage, FinalDamage, DamageCauser, DamageType, HitLocation);
|
|
|
|
if (ResistedAmount > 0.0f)
|
|
{
|
|
OnDamageResisted.Broadcast(ResistedAmount, DamageType,
|
|
FString::Printf(TEXT("Resistance: %.1f%%"), Resistance * 100.0f));
|
|
}
|
|
|
|
UE_LOG(LogDamage, Verbose, TEXT("ApplyDamage — Raw: %.1f → Final: %.1f (Type: %s, Resist: %.1f%%)"),
|
|
RawDamage, FinalDamage, *DamageType.GetTagName().ToString(), Resistance * 100.0f);
|
|
|
|
return FinalDamage;
|
|
}
|
|
|
|
float UBPC_DamageReceptionSystem::CalculateResistance(FGameplayTag DamageType) const
|
|
{
|
|
if (!DamageType.IsValid())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Base resistance + equipment-specific bonuses.
|
|
float TotalResistance = BaseResistance;
|
|
|
|
if (EquipmentConfig)
|
|
{
|
|
// EquipmentConfig would provide per-damage-type resistance values.
|
|
// TotalResistance += EquipmentConfig->GetResistance(DamageType);
|
|
}
|
|
|
|
return FMath::Clamp(TotalResistance, 0.0f, 1.0f);
|
|
}
|
|
|
|
float UBPC_DamageReceptionSystem::GetDamageMultiplier(FGameplayTag DamageType) const
|
|
{
|
|
if (!DamageType.IsValid())
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
for (const FDamageModifier& Mod : DamageModifiers)
|
|
{
|
|
if (Mod.DamageType == DamageType && !Mod.bFlatReduction)
|
|
{
|
|
return Mod.Multiplier;
|
|
}
|
|
}
|
|
|
|
return 1.0f; // No modifier — normal damage.
|
|
}
|
|
|
|
// ============================================================================
|
|
// Hit Reaction
|
|
// ============================================================================
|
|
|
|
void UBPC_DamageReceptionSystem::EvaluateHitReaction(float FinalDamage, AActor* DamageCauser,
|
|
FVector HitDirection)
|
|
{
|
|
if (FinalDamage >= KnockdownThreshold)
|
|
{
|
|
UE_LOG(LogDamage, Log, TEXT("EvaluateHitReaction — Knockdown! (%.1f damage)"), FinalDamage);
|
|
OnKnockedDown.Broadcast(DamageCauser, FinalDamage);
|
|
}
|
|
else if (FinalDamage >= StaggerThreshold)
|
|
{
|
|
UE_LOG(LogDamage, Verbose, TEXT("EvaluateHitReaction — Stagger (%.1f damage)"), FinalDamage);
|
|
OnStaggered.Broadcast(DamageCauser, FinalDamage);
|
|
}
|
|
|
|
// Route to dedicated hit reaction system for animation selection.
|
|
if (CachedHitReactionSystem)
|
|
{
|
|
// CachedHitReactionSystem->PlayHitReaction(FinalDamage, HitDirection, DamageCauser);
|
|
}
|
|
}
|