// Copyright Pacificao. All Rights Reserved. #include "AgrarianMapBoundaryVolume.h" #include "AgrarianGameCharacter.h" #include "Components/BoxComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "Kismet/GameplayStatics.h" AAgrarianMapBoundaryVolume::AAgrarianMapBoundaryVolume() { PrimaryActorTick.bCanEverTick = true; bReplicates = false; BoundaryVolume = CreateDefaultSubobject(TEXT("BoundaryVolume")); RootComponent = BoundaryVolume; BoundaryVolume->SetBoxExtent(FVector(50000.0f, 50000.0f, 25000.0f)); BoundaryVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision); BoundaryVolume->SetHiddenInGame(false); BoundaryVolume->ShapeColor = FColor::Yellow; } void AAgrarianMapBoundaryVolume::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); if (!HasAuthority() || !bClampPlayersAtBoundary) { return; } TArray Characters; UGameplayStatics::GetAllActorsOfClass(this, AAgrarianGameCharacter::StaticClass(), Characters); for (AActor* Actor : Characters) { EnforceBoundaryForCharacter(Cast(Actor)); } } bool AAgrarianMapBoundaryVolume::IsLocationOutsideBoundary(const FVector& WorldLocation) const { if (!BoundaryVolume) { return false; } const FVector LocalLocation = GetActorTransform().InverseTransformPosition(WorldLocation); const FVector Extent = BoundaryVolume->GetUnscaledBoxExtent(); return FMath::Abs(LocalLocation.X) > Extent.X || FMath::Abs(LocalLocation.Y) > Extent.Y || FMath::Abs(LocalLocation.Z) > Extent.Z; } bool AAgrarianMapBoundaryVolume::IsLocationInsideWarningZone(const FVector& WorldLocation) const { if (!BoundaryVolume) { return false; } const FVector LocalLocation = GetActorTransform().InverseTransformPosition(WorldLocation); const FVector Extent = BoundaryVolume->GetUnscaledBoxExtent(); if (FMath::Abs(LocalLocation.X) > Extent.X || FMath::Abs(LocalLocation.Y) > Extent.Y) { return true; } return FMath::Abs(LocalLocation.X) >= Extent.X - WarningDistanceCm || FMath::Abs(LocalLocation.Y) >= Extent.Y - WarningDistanceCm; } FVector AAgrarianMapBoundaryVolume::ClampLocationToBoundary(const FVector& WorldLocation) const { if (!BoundaryVolume) { return WorldLocation; } const FVector Extent = BoundaryVolume->GetUnscaledBoxExtent(); const float SafePadding = FMath::Clamp(BoundaryPaddingCm, 0.0f, FMath::Min(Extent.X, Extent.Y) * 0.5f); const FVector LocalLocation = GetActorTransform().InverseTransformPosition(WorldLocation); const FVector ClampedLocalLocation( FMath::Clamp(LocalLocation.X, -Extent.X + SafePadding, Extent.X - SafePadding), FMath::Clamp(LocalLocation.Y, -Extent.Y + SafePadding, Extent.Y - SafePadding), FMath::Clamp(LocalLocation.Z, -Extent.Z + SafePadding, Extent.Z - SafePadding)); return GetActorTransform().TransformPosition(ClampedLocalLocation); } void AAgrarianMapBoundaryVolume::EnforceBoundaryForCharacter(AAgrarianGameCharacter* Character) const { if (!Character || !IsLocationOutsideBoundary(Character->GetActorLocation())) { return; } const FVector ClampedLocation = ClampLocationToBoundary(Character->GetActorLocation()); Character->TeleportTo(ClampedLocation, Character->GetActorRotation(), false, true); if (UCharacterMovementComponent* Movement = Character->GetCharacterMovement()) { Movement->StopMovementImmediately(); } }