406 lines
13 KiB
C++
406 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "AgrarianGamePlayerController.h"
|
|
#include "AgrarianGameCharacter.h"
|
|
#include "AgrarianInventoryComponent.h"
|
|
#include "AgrarianItemPickup.h"
|
|
#include "AgrarianPersistenceSubsystem.h"
|
|
#include "AgrarianShelterActor.h"
|
|
#include "AgrarianSurvivalComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "Engine/LocalPlayer.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "InputMappingContext.h"
|
|
#include "Blueprint/UserWidget.h"
|
|
#include "AgrarianGame.h"
|
|
#include "Widgets/Input/SVirtualJoystick.h"
|
|
|
|
namespace
|
|
{
|
|
const FVector GroundZeroDeveloperTravelHomeLocation(-22000.0f, -3500.0f, 1148.0f);
|
|
|
|
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->RestoreHealth(4.0f * Quantity);
|
|
OutEffectSummary = FString::Printf(TEXT("treated %.0f injury severity"), 18.0f * Quantity);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// 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::SetupInputComponent()
|
|
{
|
|
Super::SetupInputComponent();
|
|
|
|
// only add IMCs for local player controllers
|
|
if (IsLocalPlayerController())
|
|
{
|
|
// Add Input Mapping Contexts
|
|
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
|
|
{
|
|
for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
|
|
{
|
|
Subsystem->AddMappingContext(CurrentContext, 0);
|
|
}
|
|
|
|
// only add these IMCs if we're not using mobile touch input
|
|
if (!ShouldUseTouchControls())
|
|
{
|
|
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
|
|
{
|
|
Subsystem->AddMappingContext(CurrentContext, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AAgrarianGamePlayerController::ShouldUseTouchControls() const
|
|
{
|
|
// are we on a mobile platform? Should we force touch?
|
|
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
|
|
}
|
|
|
|
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("Health %.1f | Stamina %.1f | Exhaustion %.1f | Hunger %.1f | Thirst %.1f | Temp %.1fC | Injury %.1f | Sickness %.1f"),
|
|
Survival.Health,
|
|
Survival.Stamina,
|
|
Survival.Exhaustion,
|
|
Survival.Hunger,
|
|
Survival.Thirst,
|
|
Survival.BodyTemperature,
|
|
Survival.InjurySeverity,
|
|
Survival.SicknessSeverity));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianHeal()
|
|
{
|
|
ServerAgrarianHeal();
|
|
}
|
|
|
|
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::AgrarianTravel(float X, float Y, float Z)
|
|
{
|
|
ServerAgrarianTravel(FVector(X, Y, Z));
|
|
}
|
|
|
|
void AAgrarianGamePlayerController::AgrarianTravelHome()
|
|
{
|
|
ServerAgrarianTravel(GroundZeroDeveloperTravelHomeLocation);
|
|
}
|
|
|
|
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());
|
|
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->RestoreHealth(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::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::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."));
|
|
}
|
|
}
|