Connect movement speed modifiers

This commit is contained in:
2026-05-15 14:22:00 -07:00
parent 775925c03d
commit 3a6b1da53b
8 changed files with 231 additions and 4 deletions
+112 -2
View File
@@ -67,6 +67,8 @@ void AAgrarianGameCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
ApplyMovementSpeed();
if (!HasAuthority() || !bWantsToSprint)
{
return;
@@ -80,7 +82,8 @@ void AAgrarianGameCharacter::Tick(float DeltaSeconds)
if (GetVelocity().SizeSquared2D() > KINDA_SMALL_NUMBER && SurvivalComponent)
{
SurvivalComponent->SpendStamina(SprintStaminaCostPerSecond * DeltaSeconds);
const float EffectiveEndurance = FMath::Max(0.25f, EnduranceMultiplier);
SurvivalComponent->SpendStamina((SprintStaminaCostPerSecond / EffectiveEndurance) * DeltaSeconds);
if (!CanSprint())
{
SetWantsToSprint(false);
@@ -92,6 +95,11 @@ void AAgrarianGameCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AAgrarianGameCharacter, bWantsToSprint);
DOREPLIFETIME(AAgrarianGameCharacter, AgeYears);
DOREPLIFETIME(AAgrarianGameCharacter, PhysicalConditionMultiplier);
DOREPLIFETIME(AAgrarianGameCharacter, StrengthMultiplier);
DOREPLIFETIME(AAgrarianGameCharacter, EnduranceMultiplier);
DOREPLIFETIME(AAgrarianGameCharacter, TerrainMovementMultiplier);
}
void AAgrarianGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
@@ -235,14 +243,116 @@ bool AAgrarianGameCharacter::IsSprinting() const
return bWantsToSprint && CanSprint();
}
float AAgrarianGameCharacter::GetCurrentCarryWeight() const
{
return InventoryComponent ? InventoryComponent->GetTotalWeight() : 0.0f;
}
float AAgrarianGameCharacter::GetCurrentMovementSpeedMultiplier() const
{
return CalculateMovementSpeedMultiplier();
}
void AAgrarianGameCharacter::SetTerrainMovementMultiplier(float NewTerrainMovementMultiplier)
{
TerrainMovementMultiplier = FMath::Clamp(NewTerrainMovementMultiplier, 0.25f, 1.25f);
ApplyMovementSpeed();
}
void AAgrarianGameCharacter::ApplyMovementSpeed()
{
if (UCharacterMovementComponent* MovementComponent = GetCharacterMovement())
{
MovementComponent->MaxWalkSpeed = IsSprinting() ? SprintSpeed : WalkSpeed;
const float BaseSpeed = IsSprinting() ? SprintSpeed : WalkSpeed;
MovementComponent->MaxWalkSpeed = FMath::Max(0.0f, BaseSpeed * CalculateMovementSpeedMultiplier());
}
}
float AAgrarianGameCharacter::CalculateMovementSpeedMultiplier() const
{
const float TraitMultiplier = FMath::Clamp(PhysicalConditionMultiplier, 0.25f, 1.25f)
* FMath::Clamp(EnduranceMultiplier, 0.25f, 2.0f);
return FMath::Clamp(
CalculateAgeMovementMultiplier()
* TraitMultiplier
* CalculateSurvivalMovementMultiplier()
* CalculateCarryWeightMovementMultiplier()
* FMath::Clamp(TerrainMovementMultiplier, 0.25f, 1.25f),
0.15f,
1.35f);
}
float AAgrarianGameCharacter::CalculateAgeMovementMultiplier() const
{
if (AgeYears < 13.0f)
{
return 0.65f;
}
if (AgeYears < 18.0f)
{
return FMath::GetMappedRangeValueClamped(FVector2D(13.0f, 18.0f), FVector2D(0.8f, 1.0f), AgeYears);
}
if (AgeYears <= 50.0f)
{
return 1.0f;
}
if (AgeYears <= 70.0f)
{
return FMath::GetMappedRangeValueClamped(FVector2D(50.0f, 70.0f), FVector2D(1.0f, 0.75f), AgeYears);
}
return 0.65f;
}
float AAgrarianGameCharacter::CalculateSurvivalMovementMultiplier() const
{
if (!SurvivalComponent || !SurvivalComponent->IsAlive())
{
return 0.0f;
}
const FAgrarianSurvivalSnapshot& Survival = SurvivalComponent->Survival;
const float HungerMultiplier = Survival.Hunger >= 50.0f
? 1.0f
: FMath::GetMappedRangeValueClamped(FVector2D(0.0f, 50.0f), FVector2D(0.85f, 1.0f), Survival.Hunger);
const float ThirstMultiplier = Survival.Thirst >= 50.0f
? 1.0f
: FMath::GetMappedRangeValueClamped(FVector2D(0.0f, 50.0f), FVector2D(0.75f, 1.0f), Survival.Thirst);
const float InjuryMultiplier = FMath::GetMappedRangeValueClamped(
FVector2D(0.0f, 100.0f),
FVector2D(1.0f, 0.5f),
Survival.InjurySeverity);
return HungerMultiplier * ThirstMultiplier * InjuryMultiplier;
}
float AAgrarianGameCharacter::CalculateCarryWeightMovementMultiplier() const
{
const float Strength = FMath::Clamp(StrengthMultiplier, 0.25f, 2.0f);
const float EffectiveComfortWeight = ComfortableCarryWeight * Strength;
const float EffectiveHeavyWeight = HeavyCarryWeight * Strength;
const float CurrentCarryWeight = GetCurrentCarryWeight();
if (CurrentCarryWeight <= EffectiveComfortWeight || EffectiveHeavyWeight <= EffectiveComfortWeight)
{
return 1.0f;
}
if (CurrentCarryWeight >= EffectiveHeavyWeight)
{
return 0.45f;
}
return FMath::GetMappedRangeValueClamped(
FVector2D(EffectiveComfortWeight, EffectiveHeavyWeight),
FVector2D(1.0f, 0.65f),
CurrentCarryWeight);
}
void AAgrarianGameCharacter::OnRep_SprintState()
{
ApplyMovementSpeed();
@@ -101,6 +101,34 @@ protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement", meta = (ClampMin = "0", ClampMax = "100"))
float MinSprintStamina = 5.0f;
/** Age hook used by movement until full lifecycle/aging systems own it. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
float AgeYears = 25.0f;
/** General physical condition scalar reserved for care, illness, sleep, and long-term state. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "1.25"))
float PhysicalConditionMultiplier = 1.0f;
/** Strength scalar that mainly offsets carried-weight penalties. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "2.0"))
float StrengthMultiplier = 1.0f;
/** Endurance scalar that improves stamina efficiency and movement resilience. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "2.0"))
float EnduranceMultiplier = 1.0f;
/** Comfort carry capacity before speed penalties, in item-weight units. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
float ComfortableCarryWeight = 25.0f;
/** Severe carry capacity where movement is heavily reduced, in item-weight units. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
float HeavyCarryWeight = 60.0f;
/** Terrain hook for later surface/volume systems. Values below one slow the character. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "1.25"))
float TerrainMovementMultiplier = 1.0f;
/** Third-person spring arm distance used when returning from first person. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Camera", meta = (ClampMin = "0"))
float ThirdPersonCameraDistance = 400.0f;
@@ -162,6 +190,13 @@ protected:
/** Applies current walk or sprint speed to character movement. */
void ApplyMovementSpeed();
/** Calculates the current multiplier from survival, carried weight, terrain, and placeholder traits. */
float CalculateMovementSpeedMultiplier() const;
float CalculateAgeMovementMultiplier() const;
float CalculateSurvivalMovementMultiplier() const;
float CalculateCarryWeightMovementMultiplier() const;
UFUNCTION()
void OnRep_SprintState();
@@ -195,6 +230,15 @@ public:
UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
bool IsSprinting() const;
UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
float GetCurrentCarryWeight() const;
UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
float GetCurrentMovementSpeedMultiplier() const;
UFUNCTION(BlueprintCallable, Category="Agrarian|Movement")
void SetTerrainMovementMultiplier(float NewTerrainMovementMultiplier);
/** Server-authoritative interaction entry point. */
UFUNCTION(Server, Reliable)
void ServerInteract(AActor* TargetActor);
@@ -33,6 +33,16 @@ int32 UAgrarianInventoryComponent::GetItemCount(FName ItemId) const
return Count;
}
float UAgrarianInventoryComponent::GetTotalWeight() const
{
float TotalWeight = 0.0f;
for (const FAgrarianItemStack& Stack : Items)
{
TotalWeight += FMath::Max(0.0f, Stack.UnitWeight) * FMath::Max(0, Stack.Quantity);
}
return TotalWeight;
}
bool UAgrarianInventoryComponent::AddItem(const FAgrarianItemStack& Stack)
{
if (!GetOwner() || !GetOwner()->HasAuthority() || !Stack.IsValidStack())
@@ -34,6 +34,9 @@ public:
UFUNCTION(BlueprintCallable, Category = "Agrarian|Inventory")
int32 GetItemCount(FName ItemId) const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Inventory")
float GetTotalWeight() const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Inventory")
bool AddItem(const FAgrarianItemStack& Stack);