1287 lines
38 KiB
C++
1287 lines
38 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "AgrarianGamePlayerController.h"
|
|
#include "AgrarianCampfire.h"
|
|
#include "AgrarianCraftingComponent.h"
|
|
#include "AgrarianDebugHUD.h"
|
|
#include "AgrarianDemoNoticeWidget.h"
|
|
#include "AgrarianGameCharacter.h"
|
|
#include "AgrarianInventoryComponent.h"
|
|
#include "AgrarianItemPickup.h"
|
|
#include "AgrarianMvpFrontendWidget.h"
|
|
#include "AgrarianPersistenceSubsystem.h"
|
|
#include "AgrarianShelterActor.h"
|
|
#include "AgrarianSurvivalComponent.h"
|
|
#include "Camera/CameraActor.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "Engine/LocalPlayer.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "InputMappingContext.h"
|
|
#include "Materials/MaterialInterface.h"
|
|
#include "Blueprint/UserWidget.h"
|
|
#include "TimerManager.h"
|
|
#include "AgrarianGame.h"
|
|
#include "Widgets/Input/SVirtualJoystick.h"
|
|
|
|
namespace
|
|
{
|
|
const FVector GroundZeroDeveloperTravelHomeLocation(-22000.0f, -3500.0f, 1148.0f);
|
|
const TCHAR* GroundZeroServerTravelMapPath = TEXT("/Game/Agrarian/Maps/L_GroundZeroTerrain_Test");
|
|
|
|
bool ResolveAgrarianServerTravelMap(const FName MapName, FString& OutTravelMap)
|
|
{
|
|
if (MapName == NAME_None
|
|
|| MapName == TEXT("GroundZero")
|
|
|| MapName == TEXT("L_GroundZeroTerrain_Test"))
|
|
{
|
|
OutTravelMap = GroundZeroServerTravelMapPath;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ApplyAgrarianItemUseEffect(const FName ItemId, const int32 Quantity, UAgrarianSurvivalComponent* SurvivalComponent, FString& OutEffectSummary)
|
|
{
|
|
if (!SurvivalComponent || Quantity <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ItemId == TEXT("food"))
|
|
{
|
|
SurvivalComponent->AddFood(15.0f * Quantity);
|
|
OutEffectSummary = FString::Printf(TEXT("restored %.0f hunger"), 15.0f * Quantity);
|
|
return true;
|
|
}
|
|
|
|
if (ItemId == TEXT("meat"))
|
|
{
|
|
SurvivalComponent->AddFood(22.0f * Quantity);
|
|
SurvivalComponent->AddSickness(3.0f * Quantity);
|
|
OutEffectSummary = FString::Printf(TEXT("restored %.0f hunger but added raw-meat sickness risk"), 22.0f * Quantity);
|
|
return true;
|
|
}
|
|
|
|
if (ItemId == TEXT("bandage"))
|
|
{
|
|
SurvivalComponent->ReduceInjury(18.0f * Quantity);
|
|
SurvivalComponent->ReduceBleeding(28.0f * Quantity);
|
|
SurvivalComponent->ReduceSprain(10.0f * Quantity);
|
|
SurvivalComponent->RestoreHealth(4.0f * Quantity);
|
|
OutEffectSummary = FString::Printf(TEXT("treated %.0f injury, %.0f bleeding, and %.0f sprain severity"), 18.0f * Quantity, 28.0f * Quantity, 10.0f * Quantity);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const TCHAR* GetMvpCharacterProxyMeshPath(const FName ProxyId)
|
|
{
|
|
return ProxyId == TEXT("female")
|
|
? TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn_Simple.SKM_Quinn_Simple")
|
|
: TEXT("/Game/Characters/Mannequins/Meshes/SKM_Manny_Simple.SKM_Manny_Simple");
|
|
}
|
|
|
|
const TCHAR* GetMvpCharacterProxyMaterialPath(const FName ProxyId)
|
|
{
|
|
return ProxyId == TEXT("female")
|
|
? TEXT("/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Female.M_AGR_CharacterProxy_Workwear_Female")
|
|
: TEXT("/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Male.M_AGR_CharacterProxy_Workwear_Male");
|
|
}
|
|
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
if (IsLocalPlayerController())
|
|
{
|
|
StartStartupPresentation();
|
|
}
|
|
|
|
// only spawn touch controls on local player controllers
|
|
if (ShouldUseTouchControls() && IsLocalPlayerController())
|
|
{
|
|
// spawn the mobile controls widget
|
|
MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
|
|
|
|
if (MobileControlsWidget)
|
|
{
|
|
// add the controls to the player screen
|
|
MobileControlsWidget->AddToPlayerScreen(0);
|
|
|
|
} else {
|
|
|
|
UE_LOG(LogAgrarianGame, Error, TEXT("Could not spawn mobile controls widget."));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AcknowledgePossession(APawn* P)
|
|
{
|
|
Super::AcknowledgePossession(P);
|
|
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ApplyMvpCharacterProxyToPawn();
|
|
if (bMvpFrontendPresentationActive)
|
|
{
|
|
SetMvpFrontendPresentationActive(true);
|
|
}
|
|
else
|
|
{
|
|
RestoreGameplayControlState();
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::SetupInputComponent()
|
|
{
|
|
Super::SetupInputComponent();
|
|
|
|
InputComponent->BindKey(EKeys::Enter, IE_Pressed, this, &AAgrarianGamePlayerController::HandleMvpConfirmInput);
|
|
InputComponent->BindKey(EKeys::SpaceBar, IE_Pressed, this, &AAgrarianGamePlayerController::HandleMvpConfirmInput);
|
|
InputComponent->BindKey(EKeys::BackSpace, IE_Pressed, this, &AAgrarianGamePlayerController::HandleMvpBackInput);
|
|
InputComponent->BindKey(EKeys::Escape, IE_Pressed, this, &AAgrarianGamePlayerController::HandleMvpEscapeInput);
|
|
|
|
// only add IMCs for local player controllers
|
|
if (IsLocalPlayerController())
|
|
{
|
|
ApplyDefaultInputMappingContexts();
|
|
}
|
|
}
|
|
|
|
bool AAgrarianGamePlayerController::ShouldUseTouchControls() const
|
|
{
|
|
// are we on a mobile platform? Should we force touch?
|
|
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ShowMvpFrontend()
|
|
{
|
|
if (!IsLocalPlayerController() || (MvpFrontendWidget && MvpFrontendWidget->IsInViewport()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!MvpFrontendWidgetClass)
|
|
{
|
|
MvpFrontendWidgetClass = UAgrarianMvpFrontendWidget::StaticClass();
|
|
}
|
|
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
MvpFrontendWidget = CreateWidget<UAgrarianMvpFrontendWidget>(this, MvpFrontendWidgetClass);
|
|
}
|
|
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection);
|
|
if (!MvpFrontendWidget->IsInViewport())
|
|
{
|
|
MvpFrontendWidget->AddToPlayerScreen(10);
|
|
}
|
|
SetMvpFrontendPresentationActive(true);
|
|
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(MvpFrontendWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
|
|
bShowMouseCursor = true;
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::StartStartupPresentation()
|
|
{
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetMvpFrontendPresentationActive(true);
|
|
|
|
if (!StartupPresentationWidget)
|
|
{
|
|
StartupPresentationWidget = CreateWidget<UAgrarianDemoNoticeWidget>(this, UAgrarianDemoNoticeWidget::StaticClass());
|
|
}
|
|
|
|
if (StartupPresentationWidget && !StartupPresentationWidget->IsInViewport())
|
|
{
|
|
StartupPresentationWidget->AddToPlayerScreen(100);
|
|
}
|
|
|
|
ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Splash, StartupSplashSeconds);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment Segment, float DurationSeconds)
|
|
{
|
|
StartupPresentationSegment = Segment;
|
|
if (StartupPresentationWidget)
|
|
{
|
|
StartupPresentationWidget->SetPresentationSegment(Segment);
|
|
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(StartupPresentationWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
|
|
}
|
|
else
|
|
{
|
|
SetInputMode(FInputModeUIOnly());
|
|
}
|
|
|
|
bShowMouseCursor = false;
|
|
GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle);
|
|
GetWorldTimerManager().SetTimer(
|
|
StartupPresentationTimerHandle,
|
|
this,
|
|
&AAgrarianGamePlayerController::AdvanceStartupPresentation,
|
|
FMath::Max(0.1f, DurationSeconds),
|
|
false);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AdvanceStartupPresentation()
|
|
{
|
|
if (StartupPresentationSegment == EAgrarianStartupPresentationSegment::Splash)
|
|
{
|
|
ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Story, StartupStorySeconds);
|
|
return;
|
|
}
|
|
|
|
if (StartupPresentationSegment == EAgrarianStartupPresentationSegment::Story)
|
|
{
|
|
ShowStartupPresentationSegment(EAgrarianStartupPresentationSegment::Credits, StartupCreditsSeconds);
|
|
return;
|
|
}
|
|
|
|
FinishStartupPresentation();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::FinishStartupPresentation()
|
|
{
|
|
GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle);
|
|
if (StartupPresentationWidget)
|
|
{
|
|
StartupPresentationWidget->RemoveFromParent();
|
|
StartupPresentationWidget = nullptr;
|
|
}
|
|
|
|
ShowMvpFrontend();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ShowMvpPauseMenu()
|
|
{
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!MvpFrontendWidgetClass)
|
|
{
|
|
MvpFrontendWidgetClass = UAgrarianMvpFrontendWidget::StaticClass();
|
|
}
|
|
|
|
if (!MvpFrontendWidget || !MvpFrontendWidget->IsInViewport())
|
|
{
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
MvpFrontendWidget = CreateWidget<UAgrarianMvpFrontendWidget>(this, MvpFrontendWidgetClass);
|
|
}
|
|
|
|
if (MvpFrontendWidget)
|
|
{
|
|
MvpFrontendWidget->AddToPlayerScreen(10);
|
|
}
|
|
}
|
|
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
|
|
SetMvpFrontendPresentationActive(true);
|
|
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(MvpFrontendWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
|
|
bShowMouseCursor = true;
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::HandleMvpConfirmInput()
|
|
{
|
|
if (MvpFrontendWidget && MvpFrontendWidget->IsInViewport())
|
|
{
|
|
MvpFrontendWidget->ConfirmActiveScreen();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::HandleMvpBackInput()
|
|
{
|
|
if (MvpFrontendWidget && MvpFrontendWidget->IsInViewport())
|
|
{
|
|
MvpFrontendWidget->BackFromActiveScreen();
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::HandleMvpEscapeInput()
|
|
{
|
|
if (MvpFrontendWidget && MvpFrontendWidget->IsInViewport())
|
|
{
|
|
MvpFrontendWidget->BackFromActiveScreen();
|
|
return;
|
|
}
|
|
|
|
ShowMvpPauseMenu();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::SetMvpFrontendPresentationActive(bool bNewActive)
|
|
{
|
|
const bool bWasActive = bMvpFrontendPresentationActive;
|
|
bMvpFrontendPresentationActive = bNewActive;
|
|
|
|
if (bNewActive)
|
|
{
|
|
if (!bWasActive)
|
|
{
|
|
SetIgnoreMoveInput(true);
|
|
SetIgnoreLookInput(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RestoreGameplayControlState();
|
|
}
|
|
|
|
APawn* ControlledPawn = GetPawn();
|
|
if (ControlledPawn)
|
|
{
|
|
ControlledPawn->SetActorHiddenInGame(bNewActive);
|
|
ControlledPawn->SetActorEnableCollision(!bNewActive);
|
|
|
|
if (ACharacter* ControlledCharacter = Cast<ACharacter>(ControlledPawn))
|
|
{
|
|
if (UCharacterMovementComponent* MovementComponent = ControlledCharacter->GetCharacterMovement())
|
|
{
|
|
if (bNewActive)
|
|
{
|
|
MovementComponent->DisableMovement();
|
|
}
|
|
else
|
|
{
|
|
MovementComponent->SetMovementMode(MOVE_Walking);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CacheAndApplyMvpHudSuppression(bNewActive);
|
|
|
|
if (bNewActive)
|
|
{
|
|
CreateOrUpdateMvpFrontendCamera();
|
|
if (MvpFrontendCameraActor)
|
|
{
|
|
SetViewTarget(MvpFrontendCameraActor);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ControlledPawn)
|
|
{
|
|
SetViewTarget(ControlledPawn);
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::RestoreGameplayControlState()
|
|
{
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ResetIgnoreMoveInput();
|
|
ResetIgnoreLookInput();
|
|
SetInputMode(FInputModeGameOnly());
|
|
bShowMouseCursor = false;
|
|
ApplyDefaultInputMappingContexts();
|
|
|
|
APawn* ControlledPawn = GetPawn();
|
|
if (!ControlledPawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ControlledPawn->SetActorHiddenInGame(false);
|
|
ControlledPawn->SetActorEnableCollision(true);
|
|
|
|
if (ACharacter* ControlledCharacter = Cast<ACharacter>(ControlledPawn))
|
|
{
|
|
if (UCharacterMovementComponent* MovementComponent = ControlledCharacter->GetCharacterMovement())
|
|
{
|
|
MovementComponent->SetMovementMode(MOVE_Walking);
|
|
}
|
|
}
|
|
|
|
SetViewTarget(ControlledPawn);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ApplyDefaultInputMappingContexts()
|
|
{
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ULocalPlayer* LocalPlayer = GetLocalPlayer();
|
|
if (!LocalPlayer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LocalPlayer);
|
|
if (!Subsystem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
|
|
{
|
|
if (CurrentContext)
|
|
{
|
|
Subsystem->RemoveMappingContext(CurrentContext);
|
|
Subsystem->AddMappingContext(CurrentContext, 0);
|
|
}
|
|
}
|
|
|
|
if (!ShouldUseTouchControls())
|
|
{
|
|
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
|
|
{
|
|
if (CurrentContext)
|
|
{
|
|
Subsystem->RemoveMappingContext(CurrentContext);
|
|
Subsystem->AddMappingContext(CurrentContext, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::CreateOrUpdateMvpFrontendCamera()
|
|
{
|
|
UWorld* World = GetWorld();
|
|
if (!World)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const APawn* ControlledPawn = GetPawn();
|
|
const FVector TargetLocation = ControlledPawn
|
|
? ControlledPawn->GetActorLocation() + FVector(0.0f, 0.0f, 95.0f)
|
|
: GroundZeroDeveloperTravelHomeLocation;
|
|
const FVector CameraLocation = TargetLocation + FVector(-620.0f, -520.0f, 260.0f);
|
|
const FRotator CameraRotation = (TargetLocation - CameraLocation).Rotation();
|
|
|
|
if (!MvpFrontendCameraActor)
|
|
{
|
|
FActorSpawnParameters SpawnParameters;
|
|
SpawnParameters.Owner = this;
|
|
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
MvpFrontendCameraActor = World->SpawnActor<ACameraActor>(ACameraActor::StaticClass(), CameraLocation, CameraRotation, SpawnParameters);
|
|
if (MvpFrontendCameraActor)
|
|
{
|
|
MvpFrontendCameraActor->SetActorHiddenInGame(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
MvpFrontendCameraActor->SetActorLocationAndRotation(CameraLocation, CameraRotation);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::CacheAndApplyMvpHudSuppression(bool bSuppress)
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bSuppress)
|
|
{
|
|
if (!bCachedMvpHudState)
|
|
{
|
|
bCachedShowDebugHUD = AgrarianHUD->bShowDebugHUD;
|
|
bCachedShowMvpHudFrame = AgrarianHUD->bShowMvpHudFrame;
|
|
bCachedShowCriticalStatsHUD = AgrarianHUD->bShowCriticalStatsHUD;
|
|
bCachedShowInventoryHUD = AgrarianHUD->bShowInventoryHUD;
|
|
bCachedShowCraftingHUD = AgrarianHUD->bShowCraftingHUD;
|
|
bCachedShowInteractionPrompt = AgrarianHUD->bShowInteractionPrompt;
|
|
bCachedShowDeathRespawnUI = AgrarianHUD->bShowDeathRespawnUI;
|
|
bCachedShowDebugDevMenu = AgrarianHUD->bShowDebugDevMenu;
|
|
bCachedMvpHudState = true;
|
|
}
|
|
|
|
AgrarianHUD->bShowDebugHUD = false;
|
|
AgrarianHUD->bShowMvpHudFrame = false;
|
|
AgrarianHUD->bShowCriticalStatsHUD = false;
|
|
AgrarianHUD->bShowInventoryHUD = false;
|
|
AgrarianHUD->bShowCraftingHUD = false;
|
|
AgrarianHUD->bShowInteractionPrompt = false;
|
|
AgrarianHUD->bShowDeathRespawnUI = false;
|
|
AgrarianHUD->bShowDebugDevMenu = false;
|
|
return;
|
|
}
|
|
|
|
if (bCachedMvpHudState)
|
|
{
|
|
AgrarianHUD->bShowDebugHUD = bCachedShowDebugHUD;
|
|
AgrarianHUD->bShowMvpHudFrame = bCachedShowMvpHudFrame;
|
|
AgrarianHUD->bShowCriticalStatsHUD = bCachedShowCriticalStatsHUD;
|
|
AgrarianHUD->bShowInventoryHUD = bCachedShowInventoryHUD;
|
|
AgrarianHUD->bShowCraftingHUD = bCachedShowCraftingHUD;
|
|
AgrarianHUD->bShowInteractionPrompt = bCachedShowInteractionPrompt;
|
|
AgrarianHUD->bShowDeathRespawnUI = bCachedShowDeathRespawnUI;
|
|
AgrarianHUD->bShowDebugDevMenu = bCachedShowDebugDevMenu;
|
|
bCachedMvpHudState = false;
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ApplyMvpCharacterProxyToPawn()
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
USkeletalMeshComponent* MeshComponent = AgrarianCharacter ? AgrarianCharacter->GetMesh() : nullptr;
|
|
if (!MeshComponent)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (USkeletalMesh* ProxyMesh = LoadObject<USkeletalMesh>(nullptr, GetMvpCharacterProxyMeshPath(SelectedMvpCharacterProxyId)))
|
|
{
|
|
MeshComponent->SetSkeletalMesh(ProxyMesh);
|
|
}
|
|
|
|
if (UMaterialInterface* ProxyMaterial = LoadObject<UMaterialInterface>(nullptr, GetMvpCharacterProxyMaterialPath(SelectedMvpCharacterProxyId)))
|
|
{
|
|
const int32 MaterialCount = FMath::Max(1, MeshComponent->GetNumMaterials());
|
|
for (int32 MaterialIndex = 0; MaterialIndex < MaterialCount; ++MaterialIndex)
|
|
{
|
|
MeshComponent->SetMaterial(MaterialIndex, ProxyMaterial);
|
|
}
|
|
}
|
|
|
|
// Blockout clothing and pack geometry was removed because it read as broken placeholder art in the investor build.
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianGrantItem(FName ItemId, int32 Quantity)
|
|
{
|
|
if (ItemId == NAME_None || Quantity <= 0)
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianGrantItem <ItemId> <Quantity>"));
|
|
return;
|
|
}
|
|
|
|
ServerAgrarianGrantItem(ItemId, Quantity);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSaveWorld()
|
|
{
|
|
ServerAgrarianSaveWorld();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianLoadWorld()
|
|
{
|
|
ServerAgrarianLoadWorld();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSurvival()
|
|
{
|
|
const AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
const UAgrarianSurvivalComponent* SurvivalComponent = AgrarianCharacter ? AgrarianCharacter->GetSurvivalComponent() : nullptr;
|
|
if (!SurvivalComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian survival component found."));
|
|
return;
|
|
}
|
|
|
|
const FAgrarianSurvivalSnapshot& Survival = SurvivalComponent->Survival;
|
|
ClientMessage(FString::Printf(
|
|
TEXT("%s | Health %.1f | Stamina %.1f | Exhaustion %.1f | Hunger %.1f | Thirst %.1f | Temp %.1fC | Injury %.1f | Bleeding %.1f | Sprain %.1f | Sickness %.1f | Death %s"),
|
|
Survival.bIsDead ? TEXT("DEAD") : TEXT("ALIVE"),
|
|
Survival.Health,
|
|
Survival.Stamina,
|
|
Survival.Exhaustion,
|
|
Survival.Hunger,
|
|
Survival.Thirst,
|
|
Survival.BodyTemperature,
|
|
Survival.InjurySeverity,
|
|
Survival.BleedingSeverity,
|
|
Survival.SprainSeverity,
|
|
Survival.SicknessSeverity,
|
|
*Survival.LastDeathReason.ToString()));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianHeal()
|
|
{
|
|
ServerAgrarianHeal();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianRespawn()
|
|
{
|
|
ServerAgrarianRespawn();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianDropItem(FName ItemId, int32 Quantity)
|
|
{
|
|
if (ItemId == NAME_None || Quantity <= 0)
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianDropItem <ItemId> <Quantity>"));
|
|
return;
|
|
}
|
|
|
|
ServerAgrarianDropItem(ItemId, Quantity);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSplitStack(int32 StackIndex, int32 SplitQuantity)
|
|
{
|
|
if (StackIndex < 0 || SplitQuantity <= 0)
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianSplitStack <StackIndex> <SplitQuantity>"));
|
|
return;
|
|
}
|
|
|
|
ServerAgrarianSplitStack(StackIndex, SplitQuantity);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianUseItem(FName ItemId, int32 Quantity)
|
|
{
|
|
if (ItemId == NAME_None || Quantity <= 0)
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianUseItem <ItemId> <Quantity>"));
|
|
return;
|
|
}
|
|
|
|
ServerAgrarianUseItem(ItemId, Quantity);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianCraft(FName RecipeId)
|
|
{
|
|
if (RecipeId == NAME_None)
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianCraft <RecipeId>"));
|
|
return;
|
|
}
|
|
|
|
ServerAgrarianCraft(RecipeId);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianCraftStatus()
|
|
{
|
|
const AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
const UAgrarianCraftingComponent* CraftingComponent = AgrarianCharacter ? AgrarianCharacter->GetCraftingComponent() : nullptr;
|
|
if (!CraftingComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian crafting component found."));
|
|
return;
|
|
}
|
|
|
|
TArray<FAgrarianRecipe> Recipes;
|
|
CraftingComponent->GetKnownRecipes(Recipes);
|
|
if (Recipes.IsEmpty())
|
|
{
|
|
ClientMessage(TEXT("No known Agrarian recipes."));
|
|
return;
|
|
}
|
|
|
|
ClientMessage(FString::Printf(TEXT("Known Agrarian recipes: %d"), Recipes.Num()));
|
|
for (const FAgrarianRecipe& Recipe : Recipes)
|
|
{
|
|
FText FailureReason;
|
|
const bool bCanCraft = CraftingComponent->CanCraft(Recipe.RecipeId, FailureReason);
|
|
const FString RecipeName = Recipe.DisplayName.IsEmpty() ? Recipe.RecipeId.ToString() : Recipe.DisplayName.ToString();
|
|
ClientMessage(FString::Printf(
|
|
TEXT("- %s (%s): %s"),
|
|
*RecipeName,
|
|
*Recipe.RecipeId.ToString(),
|
|
bCanCraft ? TEXT("ready") : *FailureReason.ToString()));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleInventoryUI()
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian HUD is active."));
|
|
return;
|
|
}
|
|
|
|
AgrarianHUD->bShowInventoryHUD = !AgrarianHUD->bShowInventoryHUD;
|
|
ClientMessage(AgrarianHUD->bShowInventoryHUD ? TEXT("MVP inventory UI shown.") : TEXT("MVP inventory UI hidden."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleCraftingUI()
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian HUD is active."));
|
|
return;
|
|
}
|
|
|
|
AgrarianHUD->bShowCraftingHUD = !AgrarianHUD->bShowCraftingHUD;
|
|
ClientMessage(AgrarianHUD->bShowCraftingHUD ? TEXT("MVP crafting UI shown.") : TEXT("MVP crafting UI hidden."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleInteractionPrompts()
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian HUD is active."));
|
|
return;
|
|
}
|
|
|
|
AgrarianHUD->bShowInteractionPrompt = !AgrarianHUD->bShowInteractionPrompt;
|
|
ClientMessage(AgrarianHUD->bShowInteractionPrompt ? TEXT("MVP interaction prompts shown.") : TEXT("MVP interaction prompts hidden."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleDeathRespawnUI()
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian HUD is active."));
|
|
return;
|
|
}
|
|
|
|
AgrarianHUD->bShowDeathRespawnUI = !AgrarianHUD->bShowDeathRespawnUI;
|
|
ClientMessage(AgrarianHUD->bShowDeathRespawnUI ? TEXT("MVP death/respawn UI shown.") : TEXT("MVP death/respawn UI hidden."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleDebugDevMenu()
|
|
{
|
|
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
|
if (!AgrarianHUD)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian HUD is active."));
|
|
return;
|
|
}
|
|
|
|
AgrarianHUD->bShowDebugDevMenu = !AgrarianHUD->bShowDebugDevMenu;
|
|
ClientMessage(AgrarianHUD->bShowDebugDevMenu ? TEXT("MVP debug/dev menu shown.") : TEXT("MVP debug/dev menu hidden."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSetUiScale(float NewUiScale)
|
|
{
|
|
const float ClampedScale = FMath::Clamp(NewUiScale, 0.75f, 1.5f);
|
|
if (MvpFrontendWidget)
|
|
{
|
|
MvpFrontendWidget->SetUiScale(ClampedScale);
|
|
}
|
|
|
|
if (AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>())
|
|
{
|
|
AgrarianHUD->TextScale = ClampedScale;
|
|
AgrarianHUD->CriticalStatsTextScale = ClampedScale;
|
|
AgrarianHUD->InventoryTextScale = ClampedScale;
|
|
AgrarianHUD->PromptTextScale = ClampedScale;
|
|
}
|
|
|
|
ClientMessage(FString::Printf(TEXT("MVP UI scale set to %.2f."), ClampedScale));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianToggleHighContrastUI()
|
|
{
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
ClientMessage(TEXT("No MVP frontend widget is active."));
|
|
return;
|
|
}
|
|
|
|
MvpFrontendWidget->SetHighContrastMode(!MvpFrontendWidget->bUseHighContrast);
|
|
ClientMessage(MvpFrontendWidget->bUseHighContrast ? TEXT("MVP high contrast UI enabled.") : TEXT("MVP high contrast UI disabled."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSelectCharacter(FName Archetype)
|
|
{
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
ClientMessage(TEXT("No MVP frontend widget is active."));
|
|
return;
|
|
}
|
|
|
|
if (Archetype == TEXT("male") || Archetype == TEXT("YoungAdultMale"))
|
|
{
|
|
SelectedMvpCharacterProxyId = TEXT("male");
|
|
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
|
ApplyMvpCharacterProxyToPawn();
|
|
ClientMessage(TEXT("Selected MVP young adult male character archetype."));
|
|
return;
|
|
}
|
|
|
|
if (Archetype == TEXT("female") || Archetype == TEXT("YoungAdultFemale"))
|
|
{
|
|
SelectedMvpCharacterProxyId = TEXT("female");
|
|
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
|
ApplyMvpCharacterProxyToPawn();
|
|
ClientMessage(TEXT("Selected MVP young adult female character archetype."));
|
|
return;
|
|
}
|
|
|
|
ClientMessage(TEXT("Usage: AgrarianSelectCharacter male|female"));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianCompleteFrontend()
|
|
{
|
|
ApplyMvpCharacterProxyToPawn();
|
|
SetMvpFrontendPresentationActive(false);
|
|
RestoreGameplayControlState();
|
|
|
|
if (const APawn* ControlledPawn = GetPawn())
|
|
{
|
|
SetControlRotation(FRotator(-8.0f, ControlledPawn->GetActorRotation().Yaw, 0.0f));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianRepairGameplayInput()
|
|
{
|
|
bMvpFrontendPresentationActive = false;
|
|
GetWorldTimerManager().ClearTimer(StartupPresentationTimerHandle);
|
|
if (StartupPresentationWidget)
|
|
{
|
|
StartupPresentationWidget->RemoveFromParent();
|
|
StartupPresentationWidget = nullptr;
|
|
}
|
|
RestoreGameplayControlState();
|
|
ClientMessage(TEXT("Agrarian gameplay input repaired."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianSkipStartupPresentation()
|
|
{
|
|
if (!StartupPresentationWidget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AdvanceStartupPresentation();
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianShowMvpScreen(FName ScreenName)
|
|
{
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
ClientMessage(TEXT("No MVP frontend widget is active."));
|
|
return;
|
|
}
|
|
|
|
if (ScreenName == TEXT("main") || ScreenName == TEXT("MainMenu"))
|
|
{
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
|
|
ClientMessage(TEXT("MVP frontend screen: main menu."));
|
|
return;
|
|
}
|
|
|
|
if (ScreenName == TEXT("character") || ScreenName == TEXT("CharacterSelection"))
|
|
{
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::CharacterSelection);
|
|
ClientMessage(TEXT("MVP frontend screen: character selection."));
|
|
return;
|
|
}
|
|
|
|
if (ScreenName == TEXT("join") || ScreenName == TEXT("JoinServer"))
|
|
{
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer);
|
|
ClientMessage(TEXT("MVP frontend screen: join server."));
|
|
return;
|
|
}
|
|
|
|
if (ScreenName == TEXT("loading") || ScreenName == TEXT("Loading"))
|
|
{
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::Loading);
|
|
ClientMessage(TEXT("MVP frontend screen: loading."));
|
|
return;
|
|
}
|
|
|
|
if (ScreenName == TEXT("saving") || ScreenName == TEXT("SavingAndQuit"))
|
|
{
|
|
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::SavingAndQuit);
|
|
ClientMessage(TEXT("MVP frontend screen: saving and quit."));
|
|
return;
|
|
}
|
|
|
|
ClientMessage(TEXT("Usage: AgrarianShowMvpScreen main|character|join|loading|saving"));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianInvestorSmokeTest(float CaptureDelaySeconds, float QuitDelaySeconds)
|
|
{
|
|
if (!IsLocalPlayerController())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const float ClampedCaptureDelaySeconds = FMath::Max(2.0f, CaptureDelaySeconds);
|
|
const float ClampedQuitDelaySeconds = FMath::Max(ClampedCaptureDelaySeconds + 4.0f, QuitDelaySeconds);
|
|
|
|
FTimerDelegate EnterWorldDelegate;
|
|
EnterWorldDelegate.BindWeakLambda(this, [this]()
|
|
{
|
|
if (!MvpFrontendWidget)
|
|
{
|
|
ShowMvpFrontend();
|
|
}
|
|
|
|
SelectedMvpCharacterProxyId = TEXT("female");
|
|
if (MvpFrontendWidget)
|
|
{
|
|
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
|
}
|
|
|
|
AgrarianCompleteFrontend();
|
|
if (AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>())
|
|
{
|
|
AgrarianHUD->bShowDebugHUD = false;
|
|
AgrarianHUD->bShowCriticalStatsHUD = false;
|
|
AgrarianHUD->bShowInventoryHUD = false;
|
|
AgrarianHUD->bShowCraftingHUD = false;
|
|
}
|
|
|
|
if (APawn* ControlledPawn = GetPawn())
|
|
{
|
|
const FRotator InvestorViewRotation(-4.0f, 42.0f, 0.0f);
|
|
ControlledPawn->SetActorRotation(FRotator(0.0f, InvestorViewRotation.Yaw, 0.0f));
|
|
SetControlRotation(InvestorViewRotation);
|
|
}
|
|
|
|
ClientMessage(TEXT("Investor smoke test entered Ground Zero as the female MVP character proxy."));
|
|
});
|
|
|
|
FTimerHandle EnterWorldTimerHandle;
|
|
GetWorldTimerManager().SetTimer(EnterWorldTimerHandle, EnterWorldDelegate, 0.75f, false);
|
|
|
|
FTimerDelegate ScreenshotDelegate;
|
|
ScreenshotDelegate.BindWeakLambda(this, [this]()
|
|
{
|
|
ClientMessage(TEXT("Investor smoke test capturing gameplay screenshot."));
|
|
ConsoleCommand(TEXT("HighResShot 1"));
|
|
});
|
|
|
|
FTimerHandle ScreenshotTimerHandle;
|
|
GetWorldTimerManager().SetTimer(ScreenshotTimerHandle, ScreenshotDelegate, ClampedCaptureDelaySeconds, false);
|
|
|
|
FTimerDelegate QuitDelegate;
|
|
QuitDelegate.BindWeakLambda(this, [this]()
|
|
{
|
|
ClientMessage(TEXT("Investor smoke test completed; quitting packaged client."));
|
|
ConsoleCommand(TEXT("quit"));
|
|
});
|
|
|
|
FTimerHandle QuitTimerHandle;
|
|
GetWorldTimerManager().SetTimer(QuitTimerHandle, QuitDelegate, ClampedQuitDelaySeconds, false);
|
|
|
|
ClientMessage(FString::Printf(
|
|
TEXT("Investor smoke test scheduled: screenshot in %.1fs, quit in %.1fs."),
|
|
ClampedCaptureDelaySeconds,
|
|
ClampedQuitDelaySeconds));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z)
|
|
{
|
|
ServerAgrarianTravel(FVector(X, Y, Z));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianTravelHome()
|
|
{
|
|
ServerAgrarianTravel(GroundZeroDeveloperTravelHomeLocation);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianServerTravel(FName MapName)
|
|
{
|
|
ServerAgrarianServerTravel(MapName);
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianGrantItem_Implementation(FName ItemId, int32 Quantity)
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter ? AgrarianCharacter->GetInventoryComponent() : nullptr;
|
|
if (!InventoryComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian inventory component found."));
|
|
return;
|
|
}
|
|
|
|
FAgrarianItemStack Stack;
|
|
Stack.ItemId = ItemId;
|
|
Stack.DisplayName = FText::FromName(ItemId);
|
|
Stack.Quantity = Quantity;
|
|
|
|
if (InventoryComponent->AddItem(Stack))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Granted %d x %s."), Quantity, *ItemId.ToString()));
|
|
}
|
|
else
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Failed to grant %d x %s."), Quantity, *ItemId.ToString()));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianSaveWorld_Implementation()
|
|
{
|
|
UAgrarianPersistenceSubsystem* Persistence = GetGameInstance() ? GetGameInstance()->GetSubsystem<UAgrarianPersistenceSubsystem>() : nullptr;
|
|
if (!Persistence)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian persistence subsystem found."));
|
|
return;
|
|
}
|
|
|
|
const bool bSaved = Persistence->SaveCurrentWorld();
|
|
ClientMessage(bSaved ? TEXT("Agrarian world saved.") : TEXT("Agrarian world save failed."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianLoadWorld_Implementation()
|
|
{
|
|
UAgrarianPersistenceSubsystem* Persistence = GetGameInstance() ? GetGameInstance()->GetSubsystem<UAgrarianPersistenceSubsystem>() : nullptr;
|
|
if (!Persistence)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian persistence subsystem found."));
|
|
return;
|
|
}
|
|
|
|
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
|
|
Persistence->RegisterWorldActorClass(TEXT("campfire"), AAgrarianCampfire::StaticClass());
|
|
int32 RestoredPlayerCount = 0;
|
|
int32 RestoredActorCount = 0;
|
|
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount);
|
|
ClientMessage(FString::Printf(
|
|
TEXT("%s Restored players: %d. Restored actors: %d."),
|
|
bLoaded ? TEXT("Agrarian world loaded.") : TEXT("Agrarian world load restored actors/players, but world state restore failed."),
|
|
RestoredPlayerCount,
|
|
RestoredActorCount));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianHeal_Implementation()
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianSurvivalComponent* SurvivalComponent = AgrarianCharacter ? AgrarianCharacter->GetSurvivalComponent() : nullptr;
|
|
if (!SurvivalComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian survival component found."));
|
|
return;
|
|
}
|
|
|
|
SurvivalComponent->Revive(100.0f);
|
|
SurvivalComponent->AddFood(100.0f);
|
|
SurvivalComponent->AddWater(100.0f);
|
|
SurvivalComponent->ReduceExhaustion(100.0f);
|
|
SurvivalComponent->ReduceSickness(100.0f);
|
|
SurvivalComponent->AddWarmth(37.0f - SurvivalComponent->Survival.BodyTemperature);
|
|
ClientMessage(TEXT("Agrarian survival restored."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianRespawn_Implementation()
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianSurvivalComponent* SurvivalComponent = AgrarianCharacter ? AgrarianCharacter->GetSurvivalComponent() : nullptr;
|
|
if (!AgrarianCharacter || !SurvivalComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian character/survival component found."));
|
|
return;
|
|
}
|
|
|
|
if (!SurvivalComponent->IsDead())
|
|
{
|
|
ClientMessage(TEXT("AgrarianRespawn is only available after death. Use AgrarianHeal for admin recovery."));
|
|
return;
|
|
}
|
|
|
|
if (UCharacterMovementComponent* MovementComponent = AgrarianCharacter->GetCharacterMovement())
|
|
{
|
|
MovementComponent->StopMovementImmediately();
|
|
}
|
|
|
|
AgrarianCharacter->SetActorLocation(GroundZeroDeveloperTravelHomeLocation, false, nullptr, ETeleportType::TeleportPhysics);
|
|
SurvivalComponent->Revive(60.0f);
|
|
SurvivalComponent->AddFood(35.0f);
|
|
SurvivalComponent->AddWater(35.0f);
|
|
SurvivalComponent->ReduceExhaustion(100.0f);
|
|
SurvivalComponent->ReduceInjury(100.0f);
|
|
SurvivalComponent->ReduceBleeding(100.0f);
|
|
SurvivalComponent->ReduceSprain(100.0f);
|
|
SurvivalComponent->ReduceSickness(100.0f);
|
|
SurvivalComponent->AddWarmth(37.0f - SurvivalComponent->Survival.BodyTemperature);
|
|
ClientMessage(TEXT("Agrarian MVP respawn complete: returned to Ground Zero with acute conditions stabilized."));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianDropItem_Implementation(FName ItemId, int32 Quantity)
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter ? AgrarianCharacter->GetInventoryComponent() : nullptr;
|
|
if (!AgrarianCharacter || !InventoryComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian inventory component found."));
|
|
return;
|
|
}
|
|
|
|
FAgrarianItemStack DroppedStack;
|
|
if (!InventoryComponent->ExtractItem(ItemId, Quantity, DroppedStack))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Could not drop %d x %s."), Quantity, *ItemId.ToString()));
|
|
return;
|
|
}
|
|
|
|
const FVector DropLocation = AgrarianCharacter->GetActorLocation()
|
|
+ AgrarianCharacter->GetActorForwardVector() * 150.0f
|
|
+ FVector(0.0f, 0.0f, 40.0f);
|
|
const FRotator DropRotation(0.0f, AgrarianCharacter->GetActorRotation().Yaw, 0.0f);
|
|
|
|
FActorSpawnParameters SpawnParameters;
|
|
SpawnParameters.Owner = AgrarianCharacter;
|
|
SpawnParameters.Instigator = AgrarianCharacter;
|
|
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
|
|
|
AAgrarianItemPickup* Pickup = GetWorld()
|
|
? GetWorld()->SpawnActor<AAgrarianItemPickup>(AAgrarianItemPickup::StaticClass(), DropLocation, DropRotation, SpawnParameters)
|
|
: nullptr;
|
|
if (!Pickup)
|
|
{
|
|
InventoryComponent->AddItem(DroppedStack);
|
|
ClientMessage(FString::Printf(TEXT("Failed to spawn dropped %s; item restored."), *ItemId.ToString()));
|
|
return;
|
|
}
|
|
|
|
Pickup->PickupStack = DroppedStack;
|
|
Pickup->Quantity = DroppedStack.Quantity;
|
|
ClientMessage(FString::Printf(TEXT("Dropped %d x %s."), DroppedStack.Quantity, *ItemId.ToString()));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianSplitStack_Implementation(int32 StackIndex, int32 SplitQuantity)
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter ? AgrarianCharacter->GetInventoryComponent() : nullptr;
|
|
if (!InventoryComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian inventory component found."));
|
|
return;
|
|
}
|
|
|
|
if (InventoryComponent->SplitStackByIndex(StackIndex, SplitQuantity))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Split %d items from stack %d."), SplitQuantity, StackIndex));
|
|
}
|
|
else
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Could not split %d items from stack %d."), SplitQuantity, StackIndex));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianUseItem_Implementation(FName ItemId, int32 Quantity)
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter ? AgrarianCharacter->GetInventoryComponent() : nullptr;
|
|
UAgrarianSurvivalComponent* SurvivalComponent = AgrarianCharacter ? AgrarianCharacter->GetSurvivalComponent() : nullptr;
|
|
if (!InventoryComponent || !SurvivalComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian inventory or survival component found."));
|
|
return;
|
|
}
|
|
|
|
FAgrarianItemStack UsedStack;
|
|
if (!InventoryComponent->ExtractItem(ItemId, Quantity, UsedStack))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Could not use %d x %s."), Quantity, *ItemId.ToString()));
|
|
return;
|
|
}
|
|
|
|
FString EffectSummary;
|
|
if (!ApplyAgrarianItemUseEffect(ItemId, UsedStack.Quantity, SurvivalComponent, EffectSummary))
|
|
{
|
|
InventoryComponent->AddItem(UsedStack);
|
|
ClientMessage(FString::Printf(TEXT("%s cannot be used yet; item restored."), *ItemId.ToString()));
|
|
return;
|
|
}
|
|
|
|
ClientMessage(FString::Printf(TEXT("Used %d x %s: %s."), UsedStack.Quantity, *ItemId.ToString(), *EffectSummary));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianCraft_Implementation(FName RecipeId)
|
|
{
|
|
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
|
UAgrarianCraftingComponent* CraftingComponent = AgrarianCharacter ? AgrarianCharacter->GetCraftingComponent() : nullptr;
|
|
if (!CraftingComponent)
|
|
{
|
|
ClientMessage(TEXT("No Agrarian crafting component found."));
|
|
return;
|
|
}
|
|
|
|
FText FailureReason;
|
|
if (!CraftingComponent->CanCraft(RecipeId, FailureReason))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Cannot craft %s: %s"), *RecipeId.ToString(), *FailureReason.ToString()));
|
|
return;
|
|
}
|
|
|
|
if (CraftingComponent->Craft(RecipeId))
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Crafted %s."), *RecipeId.ToString()));
|
|
}
|
|
else
|
|
{
|
|
ClientMessage(FString::Printf(TEXT("Failed to craft %s."), *RecipeId.ToString()));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianTravel_Implementation(FVector Destination)
|
|
{
|
|
APawn* ControlledPawn = GetPawn();
|
|
if (!ControlledPawn)
|
|
{
|
|
ClientMessage(TEXT("No controlled pawn found for developer travel."));
|
|
return;
|
|
}
|
|
|
|
if (!Destination.ContainsNaN())
|
|
{
|
|
ControlledPawn->TeleportTo(Destination, ControlledPawn->GetActorRotation(), false, true);
|
|
if (ACharacter* ControlledCharacter = Cast<ACharacter>(ControlledPawn))
|
|
{
|
|
if (UCharacterMovementComponent* Movement = ControlledCharacter->GetCharacterMovement())
|
|
{
|
|
Movement->StopMovementImmediately();
|
|
}
|
|
}
|
|
|
|
ClientMessage(FString::Printf(
|
|
TEXT("Developer travel complete: X %.1f Y %.1f Z %.1f"),
|
|
Destination.X,
|
|
Destination.Y,
|
|
Destination.Z));
|
|
}
|
|
else
|
|
{
|
|
ClientMessage(TEXT("Developer travel failed: invalid destination."));
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::ServerAgrarianServerTravel_Implementation(FName MapName)
|
|
{
|
|
UWorld* World = GetWorld();
|
|
if (!World || !HasAuthority())
|
|
{
|
|
ClientMessage(TEXT("Agrarian server travel failed: no authoritative world."));
|
|
return;
|
|
}
|
|
|
|
FString TravelMap;
|
|
if (!ResolveAgrarianServerTravelMap(MapName, TravelMap))
|
|
{
|
|
ClientMessage(TEXT("Usage: AgrarianServerTravel GroundZero"));
|
|
return;
|
|
}
|
|
|
|
const FString TravelURL = FString::Printf(TEXT("%s?listen"), *TravelMap);
|
|
ClientMessage(FString::Printf(TEXT("Agrarian server travel requested: %s"), *TravelURL));
|
|
World->ServerTravel(TravelURL, false, false);
|
|
}
|