Add shelter damage placeholder
This commit is contained in:
@@ -631,7 +631,10 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Add shelter protection volume.
|
||||
- [x] Add shelter persistence.
|
||||
- [x] Add shelter replication.
|
||||
- [ ] Add deconstruction or damage placeholder.
|
||||
- [x] Add deconstruction or damage placeholder. Primitive shelters now have
|
||||
replicated structure health, authority-only damage/repair/deconstruct hooks,
|
||||
TakeDamage integration, depletion destroy behavior, and save/load support for
|
||||
current/max structure health.
|
||||
|
||||
## 0.1.J Injury And Basic Survival Consequences
|
||||
|
||||
|
||||
@@ -95,6 +95,11 @@ Early runtime systems should remain small and explicit:
|
||||
- MVP primitive shelters use an open entrance and do not include an
|
||||
interactive door. Door actors, locks, ownership permissions, and modular
|
||||
openings are deferred to permanent structures.
|
||||
- `AAgrarianShelterActor` includes a version 0.1 structure damage placeholder:
|
||||
replicated current/max health, authority-only damage, repair, and
|
||||
deconstruction hooks, `TakeDamage` integration, depletion destruction, and
|
||||
persistent health state. This gives fire, weather, tools, and future
|
||||
ownership systems a safe structure-health contract to extend.
|
||||
- `UAgrarianBuildingPlacementComponent` owns the MVP placement preview. It
|
||||
traces from the player view, snaps to the configured grid, validates distance
|
||||
and collision, broadcasts Blueprint-readable preview state, and draws a
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
HEADER = ROOT / "Source" / "AgrarianGame" / "AgrarianShelterActor.h"
|
||||
SOURCE = ROOT / "Source" / "AgrarianGame" / "AgrarianShelterActor.cpp"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
TECHNICAL_DESIGN = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||
|
||||
|
||||
def compact(path: Path) -> str:
|
||||
return " ".join(path.read_text(encoding="utf-8").split())
|
||||
|
||||
|
||||
def require(path: Path, text: str) -> None:
|
||||
data = compact(path)
|
||||
if text not in data:
|
||||
raise SystemExit(f"FAIL: {path.relative_to(ROOT)} missing required text: {text}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
require(HEADER, "public AActor, public IAgrarianPersistentStateProvider")
|
||||
require(HEADER, "MaxStructureHealth")
|
||||
require(HEADER, "CurrentStructureHealth")
|
||||
require(HEADER, "ApplyStructureDamage")
|
||||
require(HEADER, "RepairStructure")
|
||||
require(HEADER, "Deconstruct")
|
||||
require(HEADER, "GetStructureHealthRatio")
|
||||
require(HEADER, "IsStructureDamaged")
|
||||
require(SOURCE, "DOREPLIFETIME(AAgrarianShelterActor, MaxStructureHealth)")
|
||||
require(SOURCE, "DOREPLIFETIME(AAgrarianShelterActor, CurrentStructureHealth)")
|
||||
require(SOURCE, "TakeDamage")
|
||||
require(SOURCE, "current_structure_health")
|
||||
require(SOURCE, "max_structure_health")
|
||||
require(SOURCE, "Destroy()")
|
||||
require(ROADMAP, "[x] Add deconstruction or damage placeholder.")
|
||||
require(TECHNICAL_DESIGN, "version 0.1 structure damage placeholder")
|
||||
print("PASS: shelter damage and deconstruction placeholder is present.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "AgrarianPersistentActorComponent.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
AAgrarianShelterActor::AAgrarianShelterActor()
|
||||
{
|
||||
@@ -20,3 +21,113 @@ AAgrarianShelterActor::AAgrarianShelterActor()
|
||||
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
||||
PersistentActorComponent->ActorTypeId = TEXT("primitive_shelter");
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
ClampStructureHealth();
|
||||
}
|
||||
|
||||
float AAgrarianShelterActor::TakeDamage(float DamageAmount, const FDamageEvent& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
|
||||
{
|
||||
const float PreviousHealth = CurrentStructureHealth;
|
||||
ApplyStructureDamage(DamageAmount, DamageCauser);
|
||||
return FMath::Max(0.0f, PreviousHealth - CurrentStructureHealth);
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AAgrarianShelterActor, MaxStructureHealth);
|
||||
DOREPLIFETIME(AAgrarianShelterActor, CurrentStructureHealth);
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const
|
||||
{
|
||||
if (!PersistentComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentComponent->NumberState.Add(TEXT("max_structure_health"), MaxStructureHealth);
|
||||
PersistentComponent->NumberState.Add(TEXT("current_structure_health"), CurrentStructureHealth);
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent)
|
||||
{
|
||||
if (!HasAuthority() || !PersistentComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (const float* SavedMaxHealth = PersistentComponent->NumberState.Find(TEXT("max_structure_health")))
|
||||
{
|
||||
MaxStructureHealth = FMath::Max(1.0f, *SavedMaxHealth);
|
||||
}
|
||||
|
||||
if (const float* SavedCurrentHealth = PersistentComponent->NumberState.Find(TEXT("current_structure_health")))
|
||||
{
|
||||
CurrentStructureHealth = *SavedCurrentHealth;
|
||||
}
|
||||
|
||||
ClampStructureHealth();
|
||||
}
|
||||
|
||||
bool AAgrarianShelterActor::ApplyStructureDamage(float DamageAmount, AActor* DamageCauser)
|
||||
{
|
||||
if (!HasAuthority() || DamageAmount <= 0.0f || CurrentStructureHealth <= 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth - DamageAmount, 0.0f, MaxStructureHealth);
|
||||
if (CurrentStructureHealth <= 0.0f && bDestroyWhenHealthDepleted)
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AAgrarianShelterActor::RepairStructure(float RepairAmount)
|
||||
{
|
||||
if (!HasAuthority() || RepairAmount <= 0.0f || CurrentStructureHealth >= MaxStructureHealth)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth + RepairAmount, 0.0f, MaxStructureHealth);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AAgrarianShelterActor::Deconstruct(AActor* RequestingActor)
|
||||
{
|
||||
if (!HasAuthority() || !bCanBeDeconstructed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
float AAgrarianShelterActor::GetStructureHealthRatio() const
|
||||
{
|
||||
return MaxStructureHealth > 0.0f ? FMath::Clamp(CurrentStructureHealth / MaxStructureHealth, 0.0f, 1.0f) : 0.0f;
|
||||
}
|
||||
|
||||
bool AAgrarianShelterActor::IsStructureDamaged() const
|
||||
{
|
||||
return CurrentStructureHealth < MaxStructureHealth;
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::OnRep_StructureHealth()
|
||||
{
|
||||
ClampStructureHealth();
|
||||
}
|
||||
|
||||
void AAgrarianShelterActor::ClampStructureHealth()
|
||||
{
|
||||
MaxStructureHealth = FMath::Max(1.0f, MaxStructureHealth);
|
||||
CurrentStructureHealth = FMath::Clamp(CurrentStructureHealth, 0.0f, MaxStructureHealth);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "AgrarianPersistentStateProvider.h"
|
||||
#include "AgrarianShelterActor.generated.h"
|
||||
|
||||
class UBoxComponent;
|
||||
@@ -11,13 +12,19 @@ class UAgrarianPersistentActorComponent;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class AAgrarianShelterActor : public AActor
|
||||
class AAgrarianShelterActor : public AActor, public IAgrarianPersistentStateProvider
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAgrarianShelterActor();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual float TakeDamage(float DamageAmount, const FDamageEvent& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
virtual void CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const override;
|
||||
virtual void ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) override;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Shelter")
|
||||
TObjectPtr<UStaticMeshComponent> Mesh;
|
||||
|
||||
@@ -29,4 +36,37 @@ public:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Shelter", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float WeatherProtection = 0.65f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Shelter|Damage", meta = (ClampMin = "1"))
|
||||
float MaxStructureHealth = 100.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_StructureHealth, Category = "Agrarian|Shelter|Damage", meta = (ClampMin = "0"))
|
||||
float CurrentStructureHealth = 100.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Shelter|Damage")
|
||||
bool bCanBeDeconstructed = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Shelter|Damage")
|
||||
bool bDestroyWhenHealthDepleted = true;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Shelter|Damage")
|
||||
bool ApplyStructureDamage(float DamageAmount, AActor* DamageCauser);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Shelter|Damage")
|
||||
bool RepairStructure(float RepairAmount);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Shelter|Damage")
|
||||
bool Deconstruct(AActor* RequestingActor);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Shelter|Damage")
|
||||
float GetStructureHealthRatio() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Shelter|Damage")
|
||||
bool IsStructureDamaged() const;
|
||||
|
||||
protected:
|
||||
UFUNCTION()
|
||||
void OnRep_StructureHealth();
|
||||
|
||||
void ClampStructureHealth();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user