// Copyright Pacificao. All Rights Reserved. #include "AgrarianShelterActor.h" #include "AgrarianPersistentActorComponent.h" #include "Components/BoxComponent.h" #include "Components/StaticMeshComponent.h" #include "Engine/StaticMesh.h" #include "Materials/MaterialInterface.h" #include "Net/UnrealNetwork.h" #include "UObject/ConstructorHelpers.h" namespace { void ConfigureShelterProxyComponent(UStaticMeshComponent* Component, UStaticMesh* MeshAsset, UMaterialInterface* MaterialAsset) { if (!Component) { return; } Component->SetCollisionEnabled(ECollisionEnabled::NoCollision); Component->SetGenerateOverlapEvents(false); if (MeshAsset) { Component->SetStaticMesh(MeshAsset); } if (MaterialAsset) { Component->SetMaterial(0, MaterialAsset); } } } AAgrarianShelterActor::AAgrarianShelterActor() { bReplicates = true; NetCullDistanceSquared = FMath::Square(6000.0f); Mesh = CreateDefaultSubobject(TEXT("Mesh")); RootComponent = Mesh; Mesh->SetCollisionProfileName(TEXT("BlockAll")); static ConstructorHelpers::FObjectFinder CubeMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cube.SM_AGR_Placeholder_Cube")); static ConstructorHelpers::FObjectFinder ChamferCubeMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_ChamferCube.SM_AGR_Placeholder_ChamferCube")); static ConstructorHelpers::FObjectFinder CylinderMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cylinder.SM_AGR_Placeholder_Cylinder")); static ConstructorHelpers::FObjectFinder WoodMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource.M_AGR_GZ_Wood_Resource")); static ConstructorHelpers::FObjectFinder FiberMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource.M_AGR_GZ_Fiber_Resource")); if (CubeMesh.Succeeded()) { Mesh->SetStaticMesh(CubeMesh.Object); Mesh->SetRelativeScale3D(FVector(3.2f, 2.6f, 0.08f)); } if (WoodMaterial.Succeeded()) { Mesh->SetMaterial(0, WoodMaterial.Object); } FloorProxy = CreateDefaultSubobject(TEXT("FloorProxy")); FloorProxy->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(FloorProxy, CubeMesh.Succeeded() ? CubeMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr); FloorProxy->SetRelativeLocation(FVector(0.0f, 0.0f, 10.0f)); FloorProxy->SetRelativeScale3D(FVector(3.0f, 2.4f, 0.08f)); BackWallProxy = CreateDefaultSubobject(TEXT("BackWallProxy")); BackWallProxy->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(BackWallProxy, CubeMesh.Succeeded() ? CubeMesh.Object : nullptr, FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr); BackWallProxy->SetRelativeLocation(FVector(-140.0f, 0.0f, 105.0f)); BackWallProxy->SetRelativeScale3D(FVector(0.14f, 2.2f, 1.5f)); LeftRoofProxy = CreateDefaultSubobject(TEXT("LeftRoofProxy")); LeftRoofProxy->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(LeftRoofProxy, ChamferCubeMesh.Succeeded() ? ChamferCubeMesh.Object : (CubeMesh.Succeeded() ? CubeMesh.Object : nullptr), FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr); LeftRoofProxy->SetRelativeLocation(FVector(8.0f, -82.0f, 168.0f)); LeftRoofProxy->SetRelativeRotation(FRotator(0.0f, 0.0f, -24.0f)); LeftRoofProxy->SetRelativeScale3D(FVector(3.1f, 0.16f, 1.45f)); RightRoofProxy = CreateDefaultSubobject(TEXT("RightRoofProxy")); RightRoofProxy->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(RightRoofProxy, ChamferCubeMesh.Succeeded() ? ChamferCubeMesh.Object : (CubeMesh.Succeeded() ? CubeMesh.Object : nullptr), FiberMaterial.Succeeded() ? FiberMaterial.Object : nullptr); RightRoofProxy->SetRelativeLocation(FVector(8.0f, 82.0f, 168.0f)); RightRoofProxy->SetRelativeRotation(FRotator(0.0f, 0.0f, 24.0f)); RightRoofProxy->SetRelativeScale3D(FVector(3.1f, 0.16f, 1.45f)); FrameProxyA = CreateDefaultSubobject(TEXT("FrameProxyA")); FrameProxyA->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(FrameProxyA, CylinderMesh.Succeeded() ? CylinderMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr); FrameProxyA->SetRelativeLocation(FVector(95.0f, -94.0f, 96.0f)); FrameProxyA->SetRelativeRotation(FRotator(0.0f, 0.0f, -12.0f)); FrameProxyA->SetRelativeScale3D(FVector(0.12f, 0.12f, 1.92f)); FrameProxyB = CreateDefaultSubobject(TEXT("FrameProxyB")); FrameProxyB->SetupAttachment(RootComponent); ConfigureShelterProxyComponent(FrameProxyB, CylinderMesh.Succeeded() ? CylinderMesh.Object : nullptr, WoodMaterial.Succeeded() ? WoodMaterial.Object : nullptr); FrameProxyB->SetRelativeLocation(FVector(95.0f, 94.0f, 96.0f)); FrameProxyB->SetRelativeRotation(FRotator(0.0f, 0.0f, 12.0f)); FrameProxyB->SetRelativeScale3D(FVector(0.12f, 0.12f, 1.92f)); ProtectionVolume = CreateDefaultSubobject(TEXT("ProtectionVolume")); ProtectionVolume->SetupAttachment(RootComponent); ProtectionVolume->SetBoxExtent(FVector(250.0f, 250.0f, 180.0f)); ProtectionVolume->SetCollisionProfileName(TEXT("OverlapAllDynamic")); PersistentActorComponent = CreateDefaultSubobject(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& 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); }