Initial commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class MaybeThisTimeTarget : TargetRules
|
||||
{
|
||||
public MaybeThisTimeTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Game;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V7;
|
||||
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
|
||||
|
||||
ExtraModuleNames.AddRange( new string[] { "MaybeThisTime" } );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using UnrealBuildTool.Rules;
|
||||
|
||||
public class MaybeThisTime : ModuleRules
|
||||
{
|
||||
public MaybeThisTime(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"GameplayTags",
|
||||
"GameplayAbilities",
|
||||
"GameplayTasks",
|
||||
"NavigationSystem",
|
||||
"AIModule",
|
||||
"Slate",
|
||||
"SlateCore"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
||||
// Uncomment if you are using online features
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
|
||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "MaybeThisTime.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, MaybeThisTime, "MaybeThisTime" );
|
||||
@@ -0,0 +1,6 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "AModuleActor.h"
|
||||
|
||||
// Sets default values
|
||||
AAModuleActor::AAModuleActor()
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AAModuleActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void AAModuleActor::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BaseAttributeSet.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "GameplayEffectExtension.h"
|
||||
|
||||
void UBaseAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
|
||||
{
|
||||
Super::PreAttributeChange(Attribute, NewValue);
|
||||
|
||||
if(Attribute == GetHealthAttribute())
|
||||
{
|
||||
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
|
||||
}
|
||||
if (Attribute == GetStaminaAttribute())
|
||||
{
|
||||
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxStamina());
|
||||
}
|
||||
if (Attribute == GetManaAttribute())
|
||||
{
|
||||
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxMana());
|
||||
}
|
||||
if (Attribute == GetSatietyAttribute())
|
||||
{
|
||||
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxSatiety());
|
||||
}
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
|
||||
{
|
||||
if (Attribute == GetHealthAttribute()) {
|
||||
|
||||
}
|
||||
if (Attribute == GetStaminaAttribute())
|
||||
{
|
||||
|
||||
}
|
||||
if (Attribute == GetManaAttribute())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
|
||||
{
|
||||
Super::PostGameplayEffectExecute(Data);
|
||||
|
||||
if (Data.EvaluatedData.Attribute == GetSatietyAttribute())
|
||||
{
|
||||
SetSatiety(FMath::Clamp(GetSatiety(), 0.f, GetMaxSatiety()));
|
||||
}
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, Health, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, HealthRegen, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, MaxStamina, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, Stamina, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, StaminaRegen, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, Mana, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, ManaRegen, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, Armor, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, ElementalArmor, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, CritChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, CritaDamage, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, MoveSpeed, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, SprintSpeedMultiplier, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, SneakSpeedMultiplier, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, SneakDmgMultiplier, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, WeakSpotDmgMultiplier, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, MaxSatiety, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UBaseAttributeSet, Satiety, COND_None, REPNOTIFY_Always);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, MaxHealth, OldMaxHealth);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, Health, OldHealth);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_HealthRegen(const FGameplayAttributeData& OldHealthRegen)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, HealthRegen, OldHealthRegen);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_MaxStamina(const FGameplayAttributeData& OldMaxStamina)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, MaxStamina, OldMaxStamina);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_Stamina(const FGameplayAttributeData& OldStamina)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, Stamina, OldStamina);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_StaminaRegen(const FGameplayAttributeData& OldStaminaRegen)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, StaminaRegen, OldStaminaRegen);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, MaxMana, OldMaxMana);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, Mana, OldMana);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_ManaRegen(const FGameplayAttributeData& OldManaRegen)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, ManaRegen, OldManaRegen);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_Armor(const FGameplayAttributeData& OldArmor)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, Armor, OldArmor);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_ElementalArmor(const FGameplayAttributeData& OldElementalArmor)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, ElementalArmor, OldElementalArmor);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_CritChance(const FGameplayAttributeData& OldCritChance)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, CritChance, OldCritChance);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_CritaDamage(const FGameplayAttributeData& OldCritaDamage)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, CritaDamage, OldCritaDamage);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_MoveSpeed(const FGameplayAttributeData& OldMoveSpeed)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, MoveSpeed, OldMoveSpeed);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_SprintSpeedMultiplier(const FGameplayAttributeData& OldSprintSpeedMultiplier)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, SprintSpeedMultiplier, OldSprintSpeedMultiplier);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_SneakSpeedMultiplier(const FGameplayAttributeData& OldSneakSpeedMultiplier)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, SneakSpeedMultiplier, OldSneakSpeedMultiplier);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_SneakDmgMultiplier(const FGameplayAttributeData& OldSneakDmgMultiplier)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, SneakDmgMultiplier, OldSneakDmgMultiplier);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_WeakSpotDmgMultiplier(const FGameplayAttributeData& OldWeakSpotDmgMultiplier)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, WeakSpotDmgMultiplier, OldWeakSpotDmgMultiplier);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_MaxSatiety(const FGameplayAttributeData& OldMaxSatiety)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, MaxSatiety, OldMaxSatiety);
|
||||
}
|
||||
|
||||
void UBaseAttributeSet::OnRep_Satiety(const FGameplayAttributeData& OldSatiety)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UBaseAttributeSet, Satiety, OldSatiety);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "BaseCharacter.h"
|
||||
#include "InteractionInterface.h"
|
||||
#include "ItemData.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
|
||||
// Sets default values
|
||||
ABaseCharacter::ABaseCharacter()
|
||||
{
|
||||
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
if (!IsValid(AbilitySystemComponent))
|
||||
{
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||
}
|
||||
|
||||
if(!IsValid(InventoryComponent))
|
||||
{
|
||||
InventoryComponent = CreateDefaultSubobject<UInventoryComponent>(TEXT("InventoryComponent"));
|
||||
InventoryComponent->SetIsReplicated(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ABaseCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (IsLocallyControlled()) {
|
||||
UAbilitySystemComponent* ASC = GetAbilitySystemComponent();
|
||||
|
||||
if (IsValid(ASC))
|
||||
{
|
||||
AttributeSet = ASC->GetSet<UBaseAttributeSet>();
|
||||
}
|
||||
}
|
||||
|
||||
//if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Hello World")); }
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void ABaseCharacter::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
InteractionLineTrace();
|
||||
|
||||
}
|
||||
|
||||
// Called to bind functionality to input
|
||||
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* ABaseCharacter::GetAbilitySystemComponent() const
|
||||
{
|
||||
return AbilitySystemComponent;
|
||||
}
|
||||
|
||||
void ABaseCharacter::InteractionLineTrace()
|
||||
{
|
||||
FVector Start;
|
||||
FRotator Direction;
|
||||
if (!Controller) return;
|
||||
Controller->GetPlayerViewPoint(Start, Direction);
|
||||
|
||||
FVector End = Start + (Direction.Vector() * InteractionDistance);
|
||||
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Params;
|
||||
Params.AddIgnoredActor(this);
|
||||
|
||||
bool bHit = GetWorld()->LineTraceSingleByChannel(
|
||||
Hit,
|
||||
Start,
|
||||
End,
|
||||
ECC_Visibility,
|
||||
Params
|
||||
);
|
||||
|
||||
AActor* NewLookedAtActor = bHit ? Hit.GetActor() : nullptr;
|
||||
|
||||
if (NewLookedAtActor == LookedAtActor) return;
|
||||
|
||||
if (LookedAtActor && LookedAtActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
IInteractionInterface::Execute_LookingAt(LookedAtActor, this, false);
|
||||
}
|
||||
|
||||
LookedAtActor = nullptr;
|
||||
|
||||
if (NewLookedAtActor && NewLookedAtActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
LookedAtActor = NewLookedAtActor;
|
||||
IInteractionInterface::Execute_LookingAt(LookedAtActor, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ABaseCharacter::InteractWithInteractable_Implementation(AActor* TargetActor)
|
||||
{
|
||||
if (!LookedAtActor || !LookedAtActor->Implements<UInteractionInterface>()) return;
|
||||
IInteractionInterface::Execute_Interact(LookedAtActor, this);
|
||||
}
|
||||
|
||||
void ABaseCharacter::TryPickup_Implementation(AItemActor* ItemActor)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
if (!ItemActor || !InventoryComponent) return;
|
||||
|
||||
FItemData ItemToAdd = ItemActor->GetItemData();
|
||||
|
||||
FItemData OutRemainingItem = FItemData();
|
||||
EAddItemResult Result = InventoryComponent->TryAddItem(ItemToAdd, OutRemainingItem);
|
||||
|
||||
switch (Result) {
|
||||
case EAddItemResult::FullyAdded:
|
||||
ItemActor->Destroy();
|
||||
return;
|
||||
case EAddItemResult::PartiallyAdded:
|
||||
ItemActor->SetItemObject(OutRemainingItem);
|
||||
return;
|
||||
default:
|
||||
if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Couldnt Pick up Item")); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BaseCommand.h"
|
||||
|
||||
void UBaseCommand::Interrupt_Implementation()
|
||||
{
|
||||
OnCommandFinished.Broadcast(false);
|
||||
}
|
||||
|
||||
void UBaseCommand::Execute_Implementation()
|
||||
{
|
||||
OnCommandFinished.Broadcast(false);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BaseCommands.h"
|
||||
#include "InteractionInterface.h"
|
||||
#include "BaseUnit.h"
|
||||
|
||||
// UMoveToCommand
|
||||
void UMoveToCommand::Execute_Implementation()
|
||||
{
|
||||
if (!Owner)
|
||||
{
|
||||
Complete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AIController = Cast<AAIController>(Owner->GetController());
|
||||
if (!AIController)
|
||||
{
|
||||
Complete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind to move completed delegate
|
||||
AIController->ReceiveMoveCompleted.AddDynamic(this, &UMoveToCommand::OnMoveCompleted);
|
||||
|
||||
// Request navigation move
|
||||
FAIMoveRequest MoveRequest;
|
||||
MoveRequest.SetGoalLocation(Destination);
|
||||
MoveRequest.SetAcceptanceRadius(AcceptanceRadius);
|
||||
MoveRequest.SetUsePathfinding(true);
|
||||
|
||||
AIController->MoveTo(MoveRequest);
|
||||
}
|
||||
|
||||
void UMoveToCommand::Interrupt_Implementation()
|
||||
{
|
||||
if (AIController)
|
||||
{
|
||||
AIController->ReceiveMoveCompleted.RemoveDynamic(
|
||||
this, &UMoveToCommand::OnMoveCompleted);
|
||||
AIController->StopMovement();
|
||||
}
|
||||
}
|
||||
|
||||
void UMoveToCommand::OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result)
|
||||
{
|
||||
if (AIController)
|
||||
{
|
||||
AIController->ReceiveMoveCompleted.RemoveDynamic(
|
||||
this, &UMoveToCommand::OnMoveCompleted);
|
||||
}
|
||||
|
||||
switch (Result)
|
||||
{
|
||||
case EPathFollowingResult::Success:
|
||||
Complete(true);
|
||||
break;
|
||||
default:
|
||||
Complete(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// UInteractCommand
|
||||
void UInteractCommand::Execute_Implementation()
|
||||
{
|
||||
if(!TargetActor || !TargetActor->Implements<UInteractionInterface>()) { Complete(false); return; }
|
||||
|
||||
// Check if already in range
|
||||
float DistToTarget = FVector::Dist(Owner->GetActorLocation(), TargetActor->GetActorLocation());
|
||||
if (DistToTarget <= InteractionRange)
|
||||
{
|
||||
DoInteract();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not in range, create move subcommand
|
||||
SubMoveCommand = NewObject<UMoveToCommand>();
|
||||
SubMoveCommand->Owner = Owner;
|
||||
SubMoveCommand->Destination = TargetActor->GetActorLocation();
|
||||
|
||||
// Bind to its completion, ... NOT going through the queue
|
||||
SubMoveCommand->OnCommandFinished.AddDynamic(this, &UInteractCommand::OnMoveFinished);
|
||||
SubMoveCommand->Execute();
|
||||
}
|
||||
|
||||
void UInteractCommand::Interrupt_Implementation()
|
||||
{
|
||||
if (SubMoveCommand)
|
||||
{
|
||||
SubMoveCommand->OnCommandFinished.RemoveDynamic(this, &UInteractCommand::OnMoveFinished);
|
||||
SubMoveCommand->Interrupt();
|
||||
SubMoveCommand = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UInteractCommand::DoInteract()
|
||||
{
|
||||
IInteractionInterface::Execute_Interact(TargetActor, Owner);
|
||||
Complete(true);
|
||||
}
|
||||
|
||||
void UInteractCommand::OnMoveFinished(bool bSuccess)
|
||||
{
|
||||
if (SubMoveCommand)
|
||||
{
|
||||
SubMoveCommand->OnCommandFinished.RemoveDynamic(this, &UInteractCommand::OnMoveFinished);
|
||||
SubMoveCommand = nullptr;
|
||||
}
|
||||
|
||||
if (!bSuccess || !TargetActor)
|
||||
{
|
||||
Complete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
float DistToTarget = FVector::Dist(Owner->GetActorLocation(), TargetActor->GetActorLocation());
|
||||
|
||||
UE_LOG(LogTemp, Error, TEXT("%f %d"), DistToTarget, InteractionRange);
|
||||
|
||||
if (DistToTarget > InteractionRange)
|
||||
{
|
||||
Complete(false);
|
||||
return;
|
||||
}
|
||||
DoInteract();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BaseUnit.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "TopDownGameState.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Commands/CommandComponent.h"
|
||||
|
||||
// Sets default values
|
||||
ABaseUnit::ABaseUnit()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||
|
||||
InventoryComponent = CreateDefaultSubobject<UInventoryComponent>(TEXT("InventoryComponent"));
|
||||
InventoryComponent->SetIsReplicated(true);
|
||||
|
||||
AttributeSet = CreateDefaultSubobject<UBaseAttributeSet>( TEXT("AttributeSet"));
|
||||
|
||||
CommandComponent = CreateDefaultSubobject<UCommandComponent>(TEXT("CommandComponent"));
|
||||
|
||||
GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_GameTraceChannel2, ECR_Block);
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ABaseUnit::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
UAbilitySystemComponent* ASC = GetAbilitySystemComponent();
|
||||
if (!ASC || !AttributeSet) return;
|
||||
|
||||
AttributeSet->SetSatiety(100.f);
|
||||
AttributeSet->SetMaxSatiety(100.f);
|
||||
|
||||
// Apply default resource consumption
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : PassiveUpkeepEffects)
|
||||
{
|
||||
if (!EffectClass) continue;
|
||||
FGameplayEffectContextHandle Context = ASC->MakeEffectContext();
|
||||
ASC->ApplyGameplayEffectToSelf(EffectClass->GetDefaultObject<UGameplayEffect>(), 1.f, Context);
|
||||
}
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* ABaseUnit::GetAbilitySystemComponent() const
|
||||
{
|
||||
return AbilitySystemComponent;
|
||||
}
|
||||
|
||||
void ABaseUnit::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(ABaseUnit, DisplayName);
|
||||
DOREPLIFETIME(ABaseUnit, PrimaryFaction);
|
||||
}
|
||||
|
||||
UInventoryComponent* ABaseUnit::GetInventoryComponent_Implementation() const
|
||||
{
|
||||
return InventoryComponent;
|
||||
}
|
||||
|
||||
EOutlinerStatus ABaseUnit::GetOutlinerStatus() const
|
||||
{
|
||||
return EOutlinerStatus::OK;
|
||||
}
|
||||
|
||||
void ABaseUnit::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
// Unregister from GameState when destroyed
|
||||
if (ATopDownGameState* GS = GetWorld()->GetGameState<ATopDownGameState>())
|
||||
{
|
||||
GS->UnregisterUnit(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
void ABaseUnit::InteractionLineTrace()
|
||||
{
|
||||
FVector Start;
|
||||
FRotator Direction;
|
||||
if (!Controller) return;
|
||||
Controller->GetPlayerViewPoint(Start, Direction);
|
||||
|
||||
FVector End = Start + (Direction.Vector() * InteractionDistance);
|
||||
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Params;
|
||||
Params.AddIgnoredActor(this);
|
||||
|
||||
bool bHit = GetWorld()->LineTraceSingleByChannel(
|
||||
Hit,
|
||||
Start,
|
||||
End,
|
||||
ECC_Visibility,
|
||||
Params
|
||||
);
|
||||
|
||||
AActor* NewLookedAtActor = bHit ? Hit.GetActor() : nullptr;
|
||||
|
||||
if (NewLookedAtActor == LookedAtActor) return;
|
||||
|
||||
if (LookedAtActor && LookedAtActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
IInteractionInterface::Execute_LookingAt(LookedAtActor, this, false);
|
||||
}
|
||||
|
||||
LookedAtActor = nullptr;
|
||||
|
||||
if (NewLookedAtActor && NewLookedAtActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
LookedAtActor = NewLookedAtActor;
|
||||
IInteractionInterface::Execute_LookingAt(LookedAtActor, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ABaseUnit::InteractWithInteractable_Implementation(AActor* TargetActor)
|
||||
{
|
||||
if (!LookedAtActor || !LookedAtActor->Implements<UInteractionInterface>()) return;
|
||||
IInteractionInterface::Execute_Interact(LookedAtActor, this);
|
||||
}
|
||||
|
||||
void ABaseUnit::TryPickup_Implementation(AItemActor* ItemActor)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
if (!ItemActor || !InventoryComponent) return;
|
||||
|
||||
FItemData ItemToAdd = ItemActor->GetItemData();
|
||||
|
||||
FItemData OutRemainingItem = FItemData();
|
||||
EAddItemResult Result = InventoryComponent->TryAddItem(ItemToAdd, OutRemainingItem);
|
||||
|
||||
switch (Result) {
|
||||
case EAddItemResult::FullyAdded:
|
||||
ItemActor->Destroy();
|
||||
return;
|
||||
case EAddItemResult::PartiallyAdded:
|
||||
ItemActor->SetItemObject(OutRemainingItem);
|
||||
return;
|
||||
default:
|
||||
if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Couldnt Pick up Item")); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BaseUnitAIController.h"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Commands/CommandComponent.h"
|
||||
#include "BaseCommand.h"
|
||||
#include "BaseUnit.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UCommandComponent::UCommandComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Called when the game starts
|
||||
void UCommandComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
OwnerUnit = Cast<ABaseUnit>(GetOwner());
|
||||
|
||||
}
|
||||
|
||||
|
||||
void UCommandComponent::GiveCommand(UBaseCommand* NewCommand, bool bQueue)
|
||||
{
|
||||
if (!NewCommand) return;
|
||||
if (!bQueue) ClearCommandQueue();
|
||||
|
||||
NewCommand->Owner = OwnerUnit;
|
||||
CommandQueue.Add(NewCommand);
|
||||
if (CommandQueue.Num() == 1) ExecuteCommand(CommandQueue[0]);
|
||||
|
||||
OnCommandQueueChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UCommandComponent::ExecuteNextCommand(bool bInterrupt)
|
||||
{
|
||||
if (CommandQueue.Num() == 0) return;
|
||||
|
||||
if (bInterrupt) CommandQueue[0]->Interrupt();
|
||||
|
||||
CommandQueue[0]->OnCommandFinished.RemoveDynamic(this, &UCommandComponent::CommandFinished);
|
||||
CommandQueue.RemoveAt(0);
|
||||
|
||||
if (CommandQueue.Num() == 0) return;
|
||||
ExecuteCommand(CommandQueue[0]);
|
||||
}
|
||||
|
||||
void UCommandComponent::InterruptCurrentCommand()
|
||||
{
|
||||
if (CommandQueue.Num() == 0) return;
|
||||
|
||||
CommandQueue[0]->OnCommandFinished.RemoveDynamic(this, &UCommandComponent::CommandFinished);
|
||||
CommandQueue[0]->Interrupt();
|
||||
CommandQueue.RemoveAt(0);
|
||||
OnCommandQueueChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UCommandComponent::ClearCommandQueue()
|
||||
{
|
||||
InterruptCurrentCommand();
|
||||
CommandQueue.Empty();
|
||||
OnCommandQueueChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UCommandComponent::RemoveCommandFromQueue(UBaseCommand* CommandToRemove)
|
||||
{
|
||||
if (!CommandToRemove) return;
|
||||
if (CommandQueue[0] == CommandToRemove)
|
||||
{
|
||||
ExecuteNextCommand(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandQueue.Remove(CommandToRemove);
|
||||
}
|
||||
|
||||
OnCommandQueueChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UCommandComponent::ExecuteCommand(UBaseCommand* Command)
|
||||
{
|
||||
Command->OnCommandFinished.AddDynamic(this, &UCommandComponent::CommandFinished);
|
||||
Command->Execute();
|
||||
}
|
||||
|
||||
void UCommandComponent::CommandFinished(bool bSuccess)
|
||||
{
|
||||
if (bSuccess)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ExecuteNextCommand(false);
|
||||
OnCommandQueueChanged.Broadcast();
|
||||
}
|
||||
|
||||
const TArray<UBaseCommand*> UCommandComponent::GetUnitCommandQueue()
|
||||
{
|
||||
return CommandQueue;
|
||||
}
|
||||
|
||||
bool UCommandComponent::IsExecutingCommad() const
|
||||
{
|
||||
return CommandQueue.Num() != 0;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// FactionManager.cpp
|
||||
#include "FactionManager.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
UFactionManager::UFactionManager()
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void UFactionManager::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!GetOwner()->HasAuthority()) return;
|
||||
InitializeDefaultFactions();
|
||||
}
|
||||
|
||||
void UFactionManager::GetLifetimeReplicatedProps(
|
||||
TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(UFactionManager, Factions);
|
||||
}
|
||||
|
||||
void UFactionManager::InitializeDefaultFactions()
|
||||
{
|
||||
// Wanderer - neutral to everyone by default
|
||||
FFactionData Wanderer;
|
||||
Wanderer.FactionID = FactionID::Wanderer;
|
||||
Wanderer.DisplayName = FText::FromString("Wanderer");
|
||||
Wanderer.FactionType = EFactionType::Wanderer;
|
||||
Wanderer.FactionColor = FLinearColor::Gray;
|
||||
RegisterFaction(Wanderer);
|
||||
|
||||
// Feral - hostile to everyone
|
||||
FFactionData Feral;
|
||||
Feral.FactionID = FactionID::Feral;
|
||||
Feral.DisplayName = FText::FromString("Feral");
|
||||
Feral.FactionType = EFactionType::Hostile;
|
||||
Feral.FactionColor = FLinearColor::Red;
|
||||
// Hostile to all registered factions
|
||||
Feral.RelationsTo.Add(FFactionRelation{ FactionID::Wanderer, -100.f });
|
||||
Feral.RelationsTo.Add(FFactionRelation{ FactionID::Wildlife, -100.f });
|
||||
RegisterFaction(Feral);
|
||||
|
||||
// Wildlife - neutral unless provoked (no default relations = 0 = neutral)
|
||||
FFactionData Wildlife;
|
||||
Wildlife.FactionID = FactionID::Wildlife;
|
||||
Wildlife.DisplayName = FText::FromString("Wildlife");
|
||||
Wildlife.FactionType = EFactionType::Wildlife;
|
||||
Wildlife.FactionColor = FLinearColor::Green;
|
||||
RegisterFaction(Wildlife);
|
||||
|
||||
// NPC Faction A - Survivors
|
||||
FFactionData Survivors;
|
||||
Survivors.FactionID = "Survivors";
|
||||
Survivors.DisplayName = FText::FromString("Survivors");
|
||||
Survivors.FactionType = EFactionType::Civilized;
|
||||
Survivors.FactionColor = FLinearColor::Green;
|
||||
Survivors.RelationsTo.Add(FFactionRelation{ FactionID::Feral, -100 });
|
||||
RegisterFaction(Survivors);
|
||||
|
||||
// NPC Faction B - Raiders
|
||||
FFactionData Raiders;
|
||||
Raiders.FactionID = "Raiders";
|
||||
Raiders.DisplayName = FText::FromString("Raiders");
|
||||
Raiders.FactionType = EFactionType::Bandit;
|
||||
Raiders.FactionColor = FLinearColor::Red;
|
||||
Raiders.RelationsTo.Add(FFactionRelation{ "Survivors", -75 });
|
||||
RegisterFaction(Raiders);
|
||||
}
|
||||
|
||||
void UFactionManager::RegisterFaction(FFactionData NewFaction)
|
||||
{
|
||||
if (NewFaction.FactionID.IsNone()) return;
|
||||
|
||||
// Check if already exists - update if so
|
||||
FFactionData* Existing = GetFaction(NewFaction.FactionID);
|
||||
if (Existing)
|
||||
{
|
||||
*Existing = NewFaction;
|
||||
}
|
||||
else
|
||||
{
|
||||
Factions.Add(NewFaction);
|
||||
}
|
||||
OnFactionChanged.Broadcast(NewFaction.FactionID);
|
||||
}
|
||||
|
||||
FFactionData* UFactionManager::GetFaction(FName InFactionID)
|
||||
{
|
||||
return Factions.FindByPredicate([InFactionID](const FFactionData& F)
|
||||
{
|
||||
return F.FactionID == InFactionID;
|
||||
});
|
||||
}
|
||||
|
||||
const FFactionData* UFactionManager::GetFaction(FName InFactionID) const
|
||||
{
|
||||
return Factions.FindByPredicate([InFactionID](const FFactionData& F)
|
||||
{
|
||||
return F.FactionID == InFactionID;
|
||||
});
|
||||
}
|
||||
|
||||
FFactionData UFactionManager::GetFactionCopy(FName FactionID) const
|
||||
{
|
||||
const FFactionData* Found = GetFaction(FactionID);
|
||||
return Found ? *Found : FFactionData();
|
||||
}
|
||||
|
||||
bool UFactionManager::FactionExists(FName FactionID) const
|
||||
{
|
||||
return GetFaction(FactionID) != nullptr;
|
||||
}
|
||||
|
||||
float UFactionManager::GetRelation(FName FactionA, FName FactionB) const
|
||||
{
|
||||
const FFactionData* FA = GetFaction(FactionA);
|
||||
if (!FA) return 0.f;
|
||||
return FA->GetRelationTo(FactionB);
|
||||
}
|
||||
|
||||
ERelationState UFactionManager::GetRelationState(FName FactionA, FName FactionB) const
|
||||
{
|
||||
const FFactionData* FA = GetFaction(FactionA);
|
||||
if (!FA) return ERelationState::Neutral;
|
||||
return FA->GetRelationStateTo(FactionB);
|
||||
}
|
||||
|
||||
void UFactionManager::ModifyRelation(FName FactionA, FName FactionB, float Delta)
|
||||
{
|
||||
FFactionData* FA = GetFaction(FactionA);
|
||||
if (!FA) return;
|
||||
|
||||
float& Relation = FA->FindOrAddRelationTo(FactionB);
|
||||
Relation = FMath::Clamp(Relation + Delta, -100.f, 100.f);
|
||||
|
||||
OnRelationChanged.Broadcast(FactionA, FactionB, Relation);
|
||||
}
|
||||
|
||||
bool UFactionManager::AreHostile(FName FactionA, FName FactionB) const
|
||||
{
|
||||
return GetRelationState(FactionA, FactionB) == ERelationState::Hostile || GetRelationState(FactionB, FactionA) == ERelationState::Hostile;
|
||||
}
|
||||
|
||||
bool UFactionManager::AreAllied(FName FactionA, FName FactionB) const
|
||||
{
|
||||
return GetRelationState(FactionA, FactionB) == ERelationState::Allied && GetRelationState(FactionB, FactionA) == ERelationState::Allied;
|
||||
}
|
||||
|
||||
void UFactionManager::EvaluateFactionNeeds()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UFactionManager::OnRep_Factions()
|
||||
{
|
||||
// Clients can react to faction data arriving
|
||||
// UI updates etc
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "InteractionInterface.h"
|
||||
@@ -0,0 +1,416 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "InventoryComponent.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "BaseCharacter.h"
|
||||
#include "MyGameplayTags.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UInventoryComponent::UInventoryComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
SetInventorySize(InventorySize);
|
||||
ItemList.Init(FItemData(), InventorySize);
|
||||
}
|
||||
|
||||
|
||||
// Called when the game starts
|
||||
void UInventoryComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
|
||||
}
|
||||
|
||||
float UInventoryComponent::AddResource(FItemData& ItemData)
|
||||
{
|
||||
float AddableAmount = FMath::FloorToInt(GetFreeCapacity() / ItemData.Definition->WeightPerUnit);
|
||||
float AmountToAdd = FMath::Clamp(ItemData.CurrentStackSize, 0, AddableAmount);
|
||||
|
||||
if (AmountToAdd == 0) return ItemData.CurrentStackSize;
|
||||
|
||||
float AmountToReturn = ItemData.CurrentStackSize - AmountToAdd;
|
||||
|
||||
for (FItemData& Resource : Resources) {
|
||||
if (Resource.Definition->ID != ItemData.Definition->ID) continue;
|
||||
|
||||
Resource.CurrentStackSize += AmountToAdd;
|
||||
CurrentWeight += AmountToAdd * ItemData.Definition->WeightPerUnit;
|
||||
OnInventoryChanged.Broadcast();
|
||||
|
||||
return AmountToReturn;
|
||||
}
|
||||
|
||||
ItemData.CurrentStackSize = AmountToAdd;
|
||||
|
||||
Resources.Add(ItemData);
|
||||
|
||||
CurrentWeight += AmountToAdd * ItemData.Definition->WeightPerUnit;
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
|
||||
return AmountToReturn;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::ConsumeResource(FName ResourceID, float Amount)
|
||||
{
|
||||
for (int32 i = 0; i < Resources.Num(); i++)
|
||||
{
|
||||
FItemData& Resource = Resources[i];
|
||||
|
||||
if (Resource.Definition->ID != ResourceID) continue;
|
||||
if (Resource.CurrentStackSize < Amount) return false;
|
||||
|
||||
Resource.CurrentStackSize -= Amount;
|
||||
CurrentWeight -= Amount * Resource.Definition->WeightPerUnit;
|
||||
|
||||
if (Resource.CurrentStackSize == 0) Resources.RemoveAt(i);
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float UInventoryComponent::GetResource(FName ResourceID) const
|
||||
{
|
||||
for (const FItemData& Resource : Resources) {
|
||||
if (Resource.Definition->ID == ResourceID) return Resource.CurrentStackSize;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void UInventoryComponent::OnRep_ItemList()
|
||||
{
|
||||
OnInventoryChanged.Broadcast();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void UInventoryComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(UInventoryComponent, ItemList);
|
||||
DOREPLIFETIME(UInventoryComponent, InventorySize);
|
||||
}
|
||||
|
||||
const FItemData& UInventoryComponent::GetItemAt(int32 SlotIndex) const
|
||||
{
|
||||
static const FItemData InvalidItem;
|
||||
if (!ItemList.IsValidIndex(SlotIndex)) return InvalidItem;
|
||||
|
||||
return ItemList[SlotIndex];
|
||||
}
|
||||
|
||||
const TArray<FItemData> UInventoryComponent::GetAllItems() const
|
||||
{
|
||||
return ItemList;
|
||||
}
|
||||
|
||||
EAddItemResult UInventoryComponent::TryAddItem(FItemData NewItem, FItemData& OutRemainingItem)
|
||||
{
|
||||
int FreeSlotIndex = 0;
|
||||
bool PartiallyAdded = false;
|
||||
|
||||
if (!NewItem.IsValid()) return EAddItemResult::Failed;
|
||||
|
||||
if (NewItem.HasTag(Item_Flag_Resource)) {
|
||||
EAddItemResult Result;
|
||||
float AmountLeft = AddResource(NewItem);
|
||||
|
||||
if (AmountLeft == 0) Result = EAddItemResult::FullyAdded;
|
||||
else if (AmountLeft != NewItem.CurrentStackSize) Result = EAddItemResult::PartiallyAdded;
|
||||
else Result = EAddItemResult::Failed;
|
||||
|
||||
OutRemainingItem = NewItem;
|
||||
OutRemainingItem.CurrentStackSize = AmountLeft;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Try add Item to existing Stack
|
||||
for (int SlotIndex = 0; SlotIndex < InventorySize; SlotIndex++) {
|
||||
switch (AddItemAt(NewItem, SlotIndex, OutRemainingItem, false, false))
|
||||
{
|
||||
case EAddItemResult::FullyAdded:
|
||||
return EAddItemResult::FullyAdded;
|
||||
case EAddItemResult::PartiallyAdded:
|
||||
NewItem = OutRemainingItem;
|
||||
PartiallyAdded = true;
|
||||
break;
|
||||
case EAddItemResult::Failed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get first free Slot and add the Item there
|
||||
if (GetFirstFreeSlot(FreeSlotIndex)) {
|
||||
EAddItemResult a = AddItemAt(NewItem, FreeSlotIndex, OutRemainingItem, false, true);
|
||||
return a;
|
||||
}
|
||||
|
||||
if (PartiallyAdded) return EAddItemResult::PartiallyAdded;
|
||||
return EAddItemResult::Failed;
|
||||
}
|
||||
|
||||
EAddItemResult UInventoryComponent::AddItemAt(FItemData NewItem, int32 SlotIndex, FItemData& OutRemainingItem, bool AllowSwitch, bool AllowEmptySlots)
|
||||
{
|
||||
OutRemainingItem = FItemData();
|
||||
|
||||
if (!ItemList.IsValidIndex(SlotIndex)) return EAddItemResult::Failed; // Check if the Index is valid
|
||||
if (!NewItem.IsValid() || !NewItem.Definition) return EAddItemResult::Failed; // Check if a valid Item was provided
|
||||
|
||||
FItemData& ItemSlot = ItemList[SlotIndex];
|
||||
|
||||
if (!AllowEmptySlots && !ItemSlot.IsValid()) return EAddItemResult::Failed; // If the Slot is empty and AllowEmptySlots is false the Item cannot be added
|
||||
|
||||
// Add the Item to an emtpy slot
|
||||
if (AllowEmptySlots && !ItemSlot.IsValid()) {
|
||||
ItemSlot = NewItem;
|
||||
OnInventoryChanged.Broadcast();
|
||||
return EAddItemResult::FullyAdded;
|
||||
}
|
||||
|
||||
// Add Item to existing Stack
|
||||
if (ItemSlot.Definition && ItemSlot.Definition->ID == NewItem.Definition->ID) {
|
||||
if (ItemSlot.HasTag(Item_Flag_Stackable)) {
|
||||
|
||||
if (ItemSlot.CurrentStackSize + NewItem.CurrentStackSize <= ItemSlot.Definition->MaxStackSize) {
|
||||
ItemSlot.CurrentStackSize += NewItem.CurrentStackSize;
|
||||
OnInventoryChanged.Broadcast();
|
||||
return EAddItemResult::FullyAdded;
|
||||
}
|
||||
|
||||
int32 TotalStackSize = ItemSlot.CurrentStackSize + NewItem.CurrentStackSize;
|
||||
ItemSlot.CurrentStackSize = ItemSlot.Definition->MaxStackSize;
|
||||
|
||||
OutRemainingItem = NewItem;
|
||||
OutRemainingItem.CurrentStackSize = TotalStackSize - ItemSlot.Definition->MaxStackSize;
|
||||
OnInventoryChanged.Broadcast();
|
||||
return EAddItemResult::PartiallyAdded;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch an Item in an Slot if its not a Quest Item
|
||||
if(AllowSwitch && !ItemSlot.HasTag(Item_Flag_Quest)){
|
||||
OutRemainingItem = ItemSlot;
|
||||
ItemSlot = NewItem;
|
||||
OnInventoryChanged.Broadcast();
|
||||
return EAddItemResult::Switched;
|
||||
}
|
||||
|
||||
return EAddItemResult::Failed;
|
||||
}
|
||||
|
||||
void UInventoryComponent::SetItemAt(FItemData NewItem, int32 SlotIndex)
|
||||
{
|
||||
if (!ItemList.IsValidIndex(SlotIndex)) return;
|
||||
|
||||
ItemList[SlotIndex] = NewItem;
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
const TArray<FItemData> UInventoryComponent::FindItemsByID(FName ItemID) const
|
||||
{
|
||||
TArray<FItemData> FoundItems;
|
||||
|
||||
for (FItemData Item : ItemList) {
|
||||
if (Item.Definition->ID != ItemID) continue;
|
||||
|
||||
FoundItems.Add(Item);
|
||||
|
||||
}
|
||||
|
||||
return FoundItems;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::GetFirstFreeSlot(int32& SlotIndex) const
|
||||
{
|
||||
int32 Index = 0;
|
||||
for (const FItemData Item : ItemList) {
|
||||
if (!Item.IsValid()) {
|
||||
SlotIndex = Index;
|
||||
return true;
|
||||
}
|
||||
|
||||
Index++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UInventoryComponent::SetInventorySize(int32 NewInventorySize)
|
||||
{
|
||||
InventorySize = NewInventorySize;
|
||||
ItemList.SetNum(InventorySize);
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
int32 UInventoryComponent::GetInventorySize() const
|
||||
{
|
||||
return InventorySize;
|
||||
}
|
||||
|
||||
void UInventoryComponent::DropItemData_Implementation(FItemData ItemToDrop)
|
||||
{
|
||||
if (!ItemToDrop.IsValid()) return;
|
||||
|
||||
FTransform LocationToDrop = GetDropTransform();
|
||||
if (!LocationToDrop.IsValid()) return; // Currently not nececarry, as the output is always Valid unless the DropTransfrom calculation is Valid surly at some point.
|
||||
|
||||
DropItem(ItemToDrop, LocationToDrop);
|
||||
}
|
||||
|
||||
void UInventoryComponent::DropItemAtSlot_Implementation(int32 SlotIndex)
|
||||
{
|
||||
if (!ItemList.IsValidIndex(SlotIndex)) return;
|
||||
|
||||
FItemData ItemToDrop = GetItemAt(SlotIndex);
|
||||
RemoveItemAt(SlotIndex);
|
||||
|
||||
DropItemData(ItemToDrop);
|
||||
}
|
||||
|
||||
FTransform UInventoryComponent::GetDropTransform() const
|
||||
{
|
||||
AActor* Owner = GetOwner();
|
||||
if (!Owner) return FTransform::Identity;
|
||||
|
||||
FVector SpawnLocation = Owner->GetActorLocation() + (Owner->GetActorForwardVector() * DropDistance);
|
||||
|
||||
return FTransform(Owner->GetActorRotation(), SpawnLocation);
|
||||
}
|
||||
|
||||
bool UInventoryComponent::ContainsItem(FName ItemID, int32 AmountToFind) const
|
||||
{
|
||||
|
||||
int32 AmountFound = 0;
|
||||
|
||||
for (FItemData Item : ItemList) {
|
||||
if (Item.IsValid() && Item.Definition && Item.Definition->ID == ItemID) {
|
||||
AmountFound += Item.CurrentStackSize;
|
||||
}
|
||||
|
||||
if (AmountFound >= AmountToFind) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::ContainsItems(const TMap<FName, int32>& RequiredItems) const
|
||||
{
|
||||
for (const auto& Pair : RequiredItems) {
|
||||
if (!ContainsItem(Pair.Key, Pair.Value)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::RemoveItem(FName ItemID, int32 Amount)
|
||||
{
|
||||
if (!ContainsItem(ItemID, Amount))return false;
|
||||
|
||||
for (FItemData& Item : ItemList) {
|
||||
if (!Item.IsValid() || Item.Definition->ID != ItemID) continue;
|
||||
|
||||
Item.CurrentStackSize -= Amount;
|
||||
if (Item.CurrentStackSize <= 0) {
|
||||
Amount = FMath::Abs(Item.CurrentStackSize);
|
||||
Item = FItemData();
|
||||
|
||||
if (Amount == 0) {
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::RemoveItems(const TMap<FName, int32>& RequiredItems)
|
||||
{
|
||||
if (!ContainsItems(RequiredItems)) return false;
|
||||
for (const auto& Pair : RequiredItems) {
|
||||
RemoveItem(Pair.Key, Pair.Value);
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
void UInventoryComponent::RemoveItemAt(int32 SlotIndex)
|
||||
{
|
||||
if (!ItemList.IsValidIndex(SlotIndex)) return;
|
||||
ItemList[SlotIndex] = FItemData();
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
int32 UInventoryComponent::GetItemAmount(FName ID) const
|
||||
{
|
||||
int32 Amount = 0;
|
||||
for (const FItemData& Item : ItemList) {
|
||||
if (Item.IsValid() && Item.Definition->ID == ID) Amount += Item.CurrentStackSize;
|
||||
}
|
||||
|
||||
return Amount;
|
||||
}
|
||||
|
||||
void UInventoryComponent::Sort(ESortType SortType)
|
||||
{
|
||||
if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ToDo: Implement Inventory Sorting")); }
|
||||
switch (SortType) {
|
||||
case ESortType::Alpabeticaly:
|
||||
break;
|
||||
case ESortType::Type:
|
||||
break;
|
||||
case ESortType::Weight:
|
||||
break;
|
||||
case ESortType::Amount:
|
||||
break;
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventoryComponent::SetMaxCapacity_Implementation(float NewMaxCapacity)
|
||||
{
|
||||
MaxCapacity = NewMaxCapacity;
|
||||
}
|
||||
|
||||
|
||||
void UInventoryComponent::DropItem_Implementation(FItemData ItemToDrop, const FTransform& SpawnTransform)
|
||||
{
|
||||
if (!ItemToDrop.IsValid() || !ItemToDrop.Definition) return;
|
||||
|
||||
AItemActor* DroppedActor = GetWorld()->SpawnActorDeferred<AItemActor>(
|
||||
ItemToDrop.Definition->ItemActorClass,
|
||||
SpawnTransform,
|
||||
GetOwner()
|
||||
);
|
||||
|
||||
if (!DroppedActor) return;
|
||||
|
||||
DroppedActor->SetItemObject(ItemToDrop);
|
||||
DroppedActor->FinishSpawning(SpawnTransform);
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "InventoryInterface.h"
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "ItemActor.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "BaseCharacter.h"
|
||||
#include "InventoryInterface.h"
|
||||
|
||||
// Sets default values
|
||||
AItemActor::AItemActor()
|
||||
{
|
||||
bAlwaysRelevant = true;
|
||||
bReplicates = true;
|
||||
SetReplicateMovement(true);
|
||||
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
|
||||
RootComponent = MeshComponent;
|
||||
|
||||
MeshComponent->SetCollisionResponseToChannel(ECC_GameTraceChannel2, ECR_Block);
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AItemActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!ItemData.IsValid()) return;
|
||||
|
||||
if (!ItemData.Definition || !ItemData.Definition->StaticMesh)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Fuck this Item"));
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
MeshComponent->SetStaticMesh(ItemData.Definition->StaticMesh);
|
||||
}
|
||||
|
||||
void AItemActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AItemActor, ItemData);
|
||||
}
|
||||
|
||||
void AItemActor::Interact_Implementation(AActor* Interactor)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
if (!Interactor || !Interactor->Implements<UInventoryInterface>()) return;
|
||||
|
||||
UInventoryComponent* Inventory = IInventoryInterface::Execute_GetInventoryComponent(Interactor);
|
||||
if (!Inventory) return;
|
||||
|
||||
FItemData OutRemainingItem;
|
||||
EAddItemResult Result = Inventory->TryAddItem(ItemData, OutRemainingItem);
|
||||
|
||||
switch (Result)
|
||||
{
|
||||
case EAddItemResult::FullyAdded:
|
||||
Destroy();
|
||||
return;
|
||||
case EAddItemResult::PartiallyAdded:
|
||||
SetItemObject(OutRemainingItem);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AItemActor::LookingAt_Implementation(AActor* Interactor, bool IsLooking)
|
||||
{
|
||||
if (!Interactor) return;
|
||||
if (!ItemData.IsValid() || !ItemData.Definition) return;
|
||||
|
||||
// ToDo
|
||||
}
|
||||
|
||||
void AItemActor::SetItemObject(FItemData NewItem)
|
||||
{
|
||||
ItemData = NewItem;
|
||||
}
|
||||
|
||||
void AItemActor::Destroyed()
|
||||
{
|
||||
Super::Destroyed();
|
||||
// ToDo(maybe): Add other cleanupstuff
|
||||
}
|
||||
|
||||
void AItemActor::OnRep_ItemData()
|
||||
{
|
||||
if (!ItemData.Definition ||
|
||||
!ItemData.Definition->StaticMesh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MeshComponent->SetStaticMesh(ItemData.Definition->StaticMesh);
|
||||
}
|
||||
|
||||
FItemData AItemActor::GetItemData() const
|
||||
{
|
||||
return ItemData;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "ItemData.h"
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "ItemDefinition.h"
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "ItemObject.h"
|
||||
#include "MyGameplayTags.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
UItemObject::UItemObject()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
UItemObject::~UItemObject()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void UItemObject::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(UItemObject, CurrentStackSize);
|
||||
DOREPLIFETIME(UItemObject, CurrentDurability);
|
||||
DOREPLIFETIME(UItemObject, Definition);
|
||||
}
|
||||
|
||||
bool UItemObject::HasTag(FGameplayTag Tag) const
|
||||
{
|
||||
return Definition->Tags.HasTag(Tag);;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32 UItemObject::AddToStack(int32 Value)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Stackable)) {
|
||||
CurrentStackSize = 1;
|
||||
return Value;
|
||||
}
|
||||
|
||||
int32 NewTotal = CurrentStackSize + Value;
|
||||
CurrentStackSize = FMath::Min(CurrentStackSize + Value, Definition->MaxStackSize);
|
||||
int32 Overflow = FMath::Max(NewTotal - Definition->MaxStackSize, 0);
|
||||
|
||||
CurrentStackSize = FMath::Min(NewTotal, Definition->MaxStackSize);
|
||||
|
||||
return Overflow;
|
||||
}
|
||||
|
||||
float UItemObject::AddToDurability(float Value)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Durable)) {
|
||||
CurrentDurability = 1;
|
||||
return Value;
|
||||
}
|
||||
|
||||
float NewTotal = CurrentDurability + Value;
|
||||
float Overflow = FMath::Max(NewTotal - Definition->MaxDurability, 0.f);
|
||||
|
||||
CurrentDurability = FMath::Min(NewTotal, Definition->MaxDurability);
|
||||
|
||||
return Overflow;
|
||||
}
|
||||
|
||||
int32 UItemObject::SetStack(int32 NewValue)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Stackable))
|
||||
{
|
||||
CurrentStackSize = 1;
|
||||
return NewValue;
|
||||
}
|
||||
|
||||
int32 ClampedValue = FMath::Clamp(NewValue, 0, Definition->MaxStackSize);
|
||||
int32 Overflow = NewValue - ClampedValue;
|
||||
|
||||
CurrentStackSize = ClampedValue;
|
||||
|
||||
return FMath::Max(Overflow, 0);
|
||||
}
|
||||
|
||||
float UItemObject::SetDurability(float NewValue)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Durable))
|
||||
{
|
||||
CurrentDurability = 1;
|
||||
return NewValue;
|
||||
}
|
||||
|
||||
float ClampedValue = FMath::Clamp(NewValue, 0.f, Definition->MaxDurability);
|
||||
float Overflow = NewValue - ClampedValue;
|
||||
|
||||
CurrentDurability = ClampedValue;
|
||||
|
||||
return FMath::Max(Overflow, 0);
|
||||
}
|
||||
|
||||
void UItemObject::SetItemDefinition(UItemDefinition* NewDefinition)
|
||||
{
|
||||
if (!NewDefinition) return;
|
||||
Definition = NewDefinition;
|
||||
}
|
||||
|
||||
TArray<TSubclassOf<UGameplayEffect>> UItemObject::GetGrantedEffects() const
|
||||
{
|
||||
return Definition->GrantedEffects;
|
||||
}
|
||||
|
||||
TArray<TSubclassOf<UGameplayAbility>> UItemObject::GetGrantedAbility() const
|
||||
{
|
||||
return Definition->GrantedAbility;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// ItemRegistry.cpp
|
||||
#include "ItemRegistry.h"
|
||||
|
||||
void UItemRegistry::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
ItemDataTable = TSoftObjectPtr<UDataTable>(FSoftObjectPath(TEXT("/Game/Inventory/ItemRegisterDataTable.ItemRegisterDataTable")));
|
||||
|
||||
LoadTable();
|
||||
}
|
||||
|
||||
void UItemRegistry::LoadTable()
|
||||
{
|
||||
if (ItemDataTable.IsNull())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("ItemRegistry: No DataTable assigned!"));
|
||||
return;
|
||||
}
|
||||
|
||||
LoadedTable = ItemDataTable.LoadSynchronous();
|
||||
if (!LoadedTable)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("ItemRegistry: Failed to load DataTable!"));
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("ItemRegistry: Loaded %d items"),LoadedTable->GetRowNames().Num());
|
||||
}
|
||||
|
||||
UItemDefinition* UItemRegistry::GetDefinition(FName ItemID) const
|
||||
{
|
||||
if (!LoadedTable || ItemID.IsNone()) return nullptr;
|
||||
|
||||
const FItemRegistryRow* Row = LoadedTable->FindRow<FItemRegistryRow>(ItemID, TEXT("ItemRegistry::GetDefinition"));
|
||||
|
||||
if (!Row || !Row->Definition) return nullptr;
|
||||
return Row->Definition;
|
||||
}
|
||||
|
||||
bool UItemRegistry::IsValidItem(FName ItemID) const
|
||||
{
|
||||
return GetDefinition(ItemID) != nullptr;
|
||||
}
|
||||
|
||||
TArray<FName> UItemRegistry::GetAllItemIDs() const
|
||||
{
|
||||
if (!LoadedTable) return {};
|
||||
return LoadedTable->GetRowNames();
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
|
||||
#include "ModuleActor.h"
|
||||
#include "ZoneActor.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "BaseUnit.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include <ItemData.h>
|
||||
#include "ItemRegistry.h"
|
||||
|
||||
AModuleActor::AModuleActor()
|
||||
{
|
||||
bReplicates = true;
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void AModuleActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
// Auto register with DefaultZone if set in editor
|
||||
if (DefaultZone)
|
||||
{
|
||||
ForceCompleteConstruction(DefaultZone);
|
||||
}
|
||||
}
|
||||
|
||||
void AModuleActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (OwningZone)
|
||||
{
|
||||
OwningZone->UnregisterModule(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AModuleActor::GetLifetimeReplicatedProps(
|
||||
TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AModuleActor, Definition);
|
||||
DOREPLIFETIME(AModuleActor, ModuleState);
|
||||
DOREPLIFETIME(AModuleActor, ConstructionProgress);
|
||||
DOREPLIFETIME(AModuleActor, OwningZone);
|
||||
DOREPLIFETIME(AModuleActor, AssignedWorkers);
|
||||
}
|
||||
|
||||
void AModuleActor::AddResourceToZone(UInventoryComponent* ZoneStorage, FName ResourceID, float Amount)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Adding %f of %s to zone storage"), Amount, *ResourceID.ToString());
|
||||
if (!ZoneStorage) return;
|
||||
|
||||
UItemRegistry* Registry = GetGameInstance()->GetSubsystem<UItemRegistry>();
|
||||
if (!Registry) return;
|
||||
|
||||
UItemDefinition* Def = Registry->GetDefinition(ResourceID);
|
||||
if (!Def)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("ModuleActor: Unknown resource ID: %s"), *ResourceID.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
FItemData NewItem;
|
||||
NewItem.Definition = Def;
|
||||
NewItem.CurrentStackSize = FMath::FloorToInt(Amount);
|
||||
NewItem.Definition->Tags.AddTag(Item_Flag_Resource);
|
||||
|
||||
FItemData OutRemaining;
|
||||
EAddItemResult Result = ZoneStorage->TryAddItem(NewItem, OutRemaining);
|
||||
|
||||
if (Result == EAddItemResult::PartiallyAdded)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Zone storage full, overflow: %d of %s"),
|
||||
OutRemaining.CurrentStackSize, *ResourceID.ToString());
|
||||
}
|
||||
else if (Result == EAddItemResult::Failed)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Zone storage completely full, could not add %s"),
|
||||
*ResourceID.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
bool AModuleActor::CanAffordConstruction(UInventoryComponent* PayingInventory) const
|
||||
{
|
||||
if (!Definition || !PayingInventory) return false;
|
||||
|
||||
for (const FModuleCost& Cost : Definition->ConstructionCost)
|
||||
{
|
||||
if (PayingInventory->GetResource(Cost.ResourceID) < Cost.Amount)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AModuleActor::StartConstruction(
|
||||
AZoneActor* Zone, UInventoryComponent* PayingInventory)
|
||||
{
|
||||
if (!HasAuthority()) return false;
|
||||
if (!Definition || !Zone) return false;
|
||||
if (!CanAffordConstruction(PayingInventory)) return false;
|
||||
|
||||
// Deduct construction cost
|
||||
for (const FModuleCost& Cost : Definition->ConstructionCost)
|
||||
{
|
||||
PayingInventory->ConsumeResource(Cost.ResourceID, Cost.Amount);
|
||||
}
|
||||
|
||||
OwningZone = Zone;
|
||||
Zone->RegisterModule(this);
|
||||
ModuleState = EModuleState::UnderConstruction;
|
||||
ConstructionProgress = 0.f;
|
||||
OnModuleStateChanged.Broadcast(ModuleState);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AModuleActor::TickProduction(
|
||||
float GameHoursDelta, UInventoryComponent* ZoneStorage)
|
||||
{
|
||||
if (!HasAuthority() || !Definition) return;
|
||||
switch (ModuleState)
|
||||
{
|
||||
case EModuleState::UnderConstruction:
|
||||
TickConstruction(GameHoursDelta);
|
||||
break;
|
||||
case EModuleState::Active:
|
||||
TickActiveProduction(GameHoursDelta, ZoneStorage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AModuleActor::TickConstruction(float GameHoursDelta)
|
||||
{
|
||||
if (Definition->ConstructionTimeHours <= 0.f)
|
||||
{
|
||||
ConstructionProgress = 1.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConstructionProgress += GameHoursDelta / Definition->ConstructionTimeHours;
|
||||
ConstructionProgress = FMath::Clamp(ConstructionProgress, 0.f, 1.f);
|
||||
}
|
||||
|
||||
if (ConstructionProgress >= 1.f)
|
||||
{
|
||||
ModuleState = EModuleState::Inactive;
|
||||
UpdateWorkingState(); // check if we should go Active immediately
|
||||
OnModuleStateChanged.Broadcast(ModuleState);
|
||||
}
|
||||
}
|
||||
|
||||
void AModuleActor::TickActiveProduction(float GameHoursDelta, UInventoryComponent* ZoneStorage)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Module %s ticking production"), *GetName());
|
||||
if (!ZoneStorage) return;
|
||||
|
||||
// Produce outputs
|
||||
for (const FModuleResourceRate& Rate : Definition->ProducedResources)
|
||||
{
|
||||
// Worker-required production scales with worker count
|
||||
float WorkerMultiplier = 1.f;
|
||||
if (Rate.bRequiresWorkers)
|
||||
{
|
||||
if (AssignedWorkers.Num() == 0) continue;
|
||||
WorkerMultiplier = FMath::Clamp( (float)AssignedWorkers.Num() / Definition->MaxWorkers, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float AmountToAdd = Rate.AmountPerHour * GameHoursDelta * WorkerMultiplier;
|
||||
AddResourceToZone(ZoneStorage, Rate.ResourceID, AmountToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
void AModuleActor::UpdateWorkingState()
|
||||
{
|
||||
if (!Definition) return;
|
||||
if (ModuleState == EModuleState::UnderConstruction) return;
|
||||
if (ModuleState == EModuleState::Destroyed) return;
|
||||
|
||||
bool bShouldBeActive = true;
|
||||
|
||||
// Check minimum workers
|
||||
if (Definition->MinWorkersToOperate > 0 &&
|
||||
AssignedWorkers.Num() < Definition->MinWorkersToOperate)
|
||||
{
|
||||
bShouldBeActive = false;
|
||||
}
|
||||
|
||||
EModuleState NewState = bShouldBeActive ?
|
||||
EModuleState::Active : EModuleState::Inactive;
|
||||
|
||||
if (NewState != ModuleState)
|
||||
{
|
||||
ModuleState = NewState;
|
||||
OnModuleStateChanged.Broadcast(ModuleState);
|
||||
}
|
||||
}
|
||||
|
||||
bool AModuleActor::AssignWorker(ABaseUnit* Unit)
|
||||
{
|
||||
if (!Unit) return false;
|
||||
if (!Definition) return false;
|
||||
if (AssignedWorkers.Num() >= Definition->MaxWorkers) return false;
|
||||
if (AssignedWorkers.Contains(Unit)) return false;
|
||||
|
||||
AssignedWorkers.Add(Unit);
|
||||
UpdateWorkingState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AModuleActor::RemoveWorker(ABaseUnit* Unit)
|
||||
{
|
||||
AssignedWorkers.Remove(Unit);
|
||||
UpdateWorkingState();
|
||||
}
|
||||
|
||||
bool AModuleActor::CanBePlacedAt(FVector Location, AZoneActor* Zone) const
|
||||
{
|
||||
if (!Definition || !Zone) return false;
|
||||
|
||||
// Must be inside zone
|
||||
if (!Zone->IsLocationInsideZone(Location)) return false;
|
||||
|
||||
// Check overlap with other modules
|
||||
for (AModuleActor* Other : Zone->Modules)
|
||||
{
|
||||
if (!Other || Other == this) continue;
|
||||
float Dist = FVector::Dist2D(Location, Other->GetActorLocation());
|
||||
float MinDist = Definition->PlacementRadius +
|
||||
(Other->Definition ? Other->Definition->PlacementRadius : 200.f);
|
||||
if (Dist < MinDist) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AModuleActor::OnRep_Definition()
|
||||
{
|
||||
OnDefinitionLoaded();
|
||||
}
|
||||
|
||||
void AModuleActor::OnRep_State()
|
||||
{
|
||||
OnStateChanged(ModuleState);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AModuleActor::OnDefinitionLoaded_Implementation()
|
||||
{
|
||||
// Blueprint handles visual setup
|
||||
}
|
||||
|
||||
void AModuleActor::OnStateChanged_Implementation(EModuleState NewState)
|
||||
{
|
||||
// Blueprint swaps ghost mesh -> real mesh when construction completes
|
||||
}
|
||||
|
||||
EOutlinerStatus AModuleActor::GetOutlinerStatus() const
|
||||
{
|
||||
switch (ModuleState)
|
||||
{
|
||||
case EModuleState::UnderConstruction: return EOutlinerStatus::Construction;
|
||||
case EModuleState::Active: return EOutlinerStatus::OK;
|
||||
case EModuleState::Inactive: return EOutlinerStatus::Inactive;
|
||||
case EModuleState::Damaged: return EOutlinerStatus::Critical;
|
||||
case EModuleState::Destroyed: return EOutlinerStatus::Critical;
|
||||
default: return EOutlinerStatus::Inactive;
|
||||
}
|
||||
}
|
||||
|
||||
// Testing
|
||||
// -----------------------------
|
||||
|
||||
void AModuleActor::ForceCompleteConstruction(AZoneActor* Zone)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
OwningZone = Zone;
|
||||
Zone->RegisterModule(this);
|
||||
ModuleState = EModuleState::Active;
|
||||
ConstructionProgress = 1.f;
|
||||
OnModuleStateChanged.Broadcast(ModuleState);
|
||||
UE_LOG(LogTemp, Warning, TEXT("Module %s force completed"), *GetName());
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ModuleDefinition.h"
|
||||
@@ -0,0 +1,28 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "MyGameplayTags.h"
|
||||
|
||||
|
||||
//6.März
|
||||
//10:15
|
||||
|
||||
// Flags
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Flag_Stackable, "Item.Flag.Stackable");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Flag_Quest, "Item.Flag.Quest");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Flag_Consumeable, "Item.Flag.Consumeable");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Flag_Durable, "Item.Flag.Durable");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Flag_Resource, "Item.Flag.Resource");
|
||||
|
||||
// Equipment Slots
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Belt, "Item.Type.Belt");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Hands, "Item.Type.Hands");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Head, "Item.Type.Head");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Legs, "Item.Type.Legs");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Torso, "Item.Type.Torso");
|
||||
|
||||
// Weapons
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Weapon_Ranged, "Item.Type.Weapon.Ranged");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Weapon_Melee, "Item.Type.Weapon.Melee");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Weapon_Melee_TwoHanded, "Item.Type.Weapon.Melee.TwoHanded");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Item_Type_Weapon_Melee_OneHanded, "Item.Type.Weapon.Melee.OneHanded");
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "OutlinerTypes.h"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "PlaceableItemActor.h"
|
||||
|
||||
void APlaceableItemActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
if (!DefaultDefinition) { Destroy(); return; }
|
||||
|
||||
ItemData = FItemData();
|
||||
ItemData.SetItemDefinition(DefaultDefinition);
|
||||
ItemData.SetDurability(DefaultDurability);
|
||||
ItemData.SetStack(DefaultStackSize);
|
||||
|
||||
if (!ItemData.Definition->StaticMesh) { Destroy(); return; }
|
||||
MeshComponent->SetStaticMesh(ItemData.Definition->StaticMesh);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "PlayerUnit.h"
|
||||
|
||||
EOutlinerStatus APlayerUnit::GetOutlinerStatus() const
|
||||
{
|
||||
return EOutlinerStatus::Player;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "ThirdPersonCharacter.h"
|
||||
|
||||
// Sets default values
|
||||
AThirdPersonCharacter::AThirdPersonCharacter()
|
||||
{
|
||||
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AThirdPersonCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void AThirdPersonCharacter::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
}
|
||||
|
||||
// Called to bind functionality to input
|
||||
void AThirdPersonCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "ThirdPersonController.h"
|
||||
#include "InteractionInterface.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "UnitManagerComponent.h"
|
||||
#include "PlayerUnit.h"
|
||||
#include "TopDownGameState.h"
|
||||
#include "ZoneActor.h"
|
||||
#include "ModuleActor.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
|
||||
AThirdPersonController::AThirdPersonController()
|
||||
{
|
||||
UnitManager = CreateDefaultSubobject<UUnitManagerComponent>(TEXT("UnitManager"));
|
||||
bShowMouseCursor = true;
|
||||
bEnableClickEvents = true;
|
||||
bEnableMouseOverEvents = true;
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void AThirdPersonController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AThirdPersonController, PlayerFactionID);
|
||||
}
|
||||
|
||||
void AThirdPersonController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void AThirdPersonController::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
HandleHover();
|
||||
}
|
||||
|
||||
bool AThirdPersonController::IsMouseOverUI() const
|
||||
{
|
||||
ULocalPlayer* LP = GetLocalPlayer();
|
||||
if (!LP || !LP->ViewportClient) return false;
|
||||
|
||||
FVector2D MousePos;
|
||||
if (!LP->ViewportClient->GetMousePosition(MousePos)) return false;
|
||||
|
||||
FWidgetPath WidgetPath = FSlateApplication::Get()
|
||||
.LocateWindowUnderMouse(
|
||||
FSlateApplication::Get().GetCursorPos(),
|
||||
FSlateApplication::Get().GetInteractiveTopLevelWindows(),
|
||||
false);
|
||||
|
||||
if (!WidgetPath.IsValid()) return false;
|
||||
|
||||
for (int32 i = WidgetPath.Widgets.Num() - 1; i >= 0; i--)
|
||||
{
|
||||
FString Type = WidgetPath.Widgets[i].Widget->GetTypeAsString();
|
||||
if (Type == TEXT("SViewport") ||
|
||||
Type == TEXT("SGameLayerManager"))
|
||||
{
|
||||
return false; // hit viewport before any UI
|
||||
}
|
||||
if (Type == TEXT("SObjectWidget")) // UMG widget
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<AZoneActor*> AThirdPersonController::GetOwnedZones() const
|
||||
{
|
||||
ATopDownGameState* GS = GetWorld()->GetGameState<ATopDownGameState>();
|
||||
if (!GS) return {};
|
||||
return GS->GetZonesForFaction(PlayerFactionID);
|
||||
}
|
||||
|
||||
|
||||
void AThirdPersonController::HandleHover()
|
||||
{
|
||||
FHitResult Hit;
|
||||
GetHitResultUnderCursor(ECC_GameTraceChannel2, false, Hit);
|
||||
|
||||
AActor* NewHovered = Hit.GetActor();
|
||||
|
||||
// Actor changed
|
||||
if (NewHovered != LastHoveredActor)
|
||||
{
|
||||
// Stop looking at old actor
|
||||
if (LastHoveredActor && LastHoveredActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
IInteractionInterface::Execute_LookingAt(LastHoveredActor, GetPawn(), false);
|
||||
}
|
||||
|
||||
// Start looking at new actor
|
||||
if (NewHovered && NewHovered->Implements<UInteractionInterface>())
|
||||
{
|
||||
IInteractionInterface::Execute_LookingAt(NewHovered, GetPawn(), true);
|
||||
}
|
||||
|
||||
LastHoveredActor = NewHovered;
|
||||
}
|
||||
|
||||
HoveredActor = NewHovered;
|
||||
}
|
||||
|
||||
void AThirdPersonController::HandleLeftClick()
|
||||
{
|
||||
if (IsMouseOverUI()) return;
|
||||
|
||||
FHitResult Hit;
|
||||
GetHitResultUnderCursor(ECC_GameTraceChannel2, false, Hit);
|
||||
|
||||
AActor* ClickedActor = Hit.GetActor();
|
||||
if (!ClickedActor) return;
|
||||
|
||||
// Try to select an Actor
|
||||
if (SelectClickedActor(ClickedActor)) return;
|
||||
}
|
||||
|
||||
void AThirdPersonController::HandleRightClick()
|
||||
{
|
||||
FHitResult Hit;
|
||||
GetHitResultUnderCursor(ECC_GameTraceChannel2, false, Hit);
|
||||
|
||||
AActor* ClickedActor = Hit.GetActor();
|
||||
if (!ClickedActor) return;
|
||||
|
||||
// Issue Commands
|
||||
if (ClickedActor && ClickedActor->Implements<UInteractionInterface>())
|
||||
{
|
||||
UnitManager->IssueInteractCommand(ClickedActor, bShiftHeld);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnitManager->IssueMoveCommand(Hit.Location, bShiftHeld);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AThirdPersonController::SelectClickedActor(AActor* ClickedActor)
|
||||
{
|
||||
if (ABaseUnit* ClickedUnit = Cast<ABaseUnit>(ClickedActor))
|
||||
{
|
||||
SelectUnit(ClickedUnit, bShiftHeld);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AZoneActor* ClickedZone = Cast<AZoneActor>(ClickedActor))
|
||||
{
|
||||
SelectZone(ClickedZone, bShiftHeld);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AModuleActor* ClickedModule = Cast<AModuleActor>(ClickedActor))
|
||||
{
|
||||
SelectZone(ClickedModule->OwningZone, bShiftHeld);
|
||||
return true;
|
||||
}
|
||||
|
||||
ClearUnitSelection();
|
||||
return false;
|
||||
}
|
||||
|
||||
void AThirdPersonController::SelectUnit(ABaseUnit* Unit, bool bAddToSelection)
|
||||
{
|
||||
if (!Unit) return;
|
||||
if (!bAddToSelection) ClearMySelection();
|
||||
if (!SelectedUnits.Contains(Unit))
|
||||
{
|
||||
SelectedUnits.Add(Unit);
|
||||
UnitManager->AddUnitToSquad(Unit);
|
||||
}
|
||||
OnSelectionChanged.Broadcast();
|
||||
}
|
||||
|
||||
void AThirdPersonController::ClearUnitSelection()
|
||||
{
|
||||
SelectedUnits.Empty();
|
||||
UnitManager->ClearSquad();
|
||||
OnSelectionChanged.Broadcast();
|
||||
}
|
||||
|
||||
void AThirdPersonController::BoxSelect(TArray<ABaseUnit*> UnitsInBox)
|
||||
{
|
||||
ClearUnitSelection();
|
||||
for (ABaseUnit* Unit : UnitsInBox)
|
||||
{
|
||||
if (IsValid(Unit)) SelectedUnits.Add(Unit);
|
||||
}
|
||||
OnSelectionChanged.Broadcast();
|
||||
}
|
||||
|
||||
void AThirdPersonController::SelectZone(AZoneActor* Zone, bool bAddToSelection)
|
||||
{
|
||||
if (!Zone) return;
|
||||
if (!bAddToSelection) ClearMySelection();
|
||||
if (!SelectedZones.Contains(Zone)) SelectedZones.Add(Zone);
|
||||
OnSelectionChanged.Broadcast();
|
||||
}
|
||||
|
||||
void AThirdPersonController::ClearZoneSelection()
|
||||
{
|
||||
SelectedZones.Empty();
|
||||
OnSelectionChanged.Broadcast();
|
||||
}
|
||||
|
||||
void AThirdPersonController::ClearMySelection()
|
||||
{
|
||||
ClearUnitSelection();
|
||||
ClearZoneSelection();
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "TopDownGameMode.h"
|
||||
#include "ThirdPersonController.h"
|
||||
#include "BaseUnit.h"
|
||||
#include "TopDownGameState.h"
|
||||
#include "FactionManager.h"
|
||||
#include "PlayerUnit.h"
|
||||
#include "UnitManagerComponent.h"
|
||||
|
||||
ABaseUnit* ATopDownGameMode::SpawnUnit(TSubclassOf<ABaseUnit> UnitClass, FTransform SpawnTransform, FName FactionID)
|
||||
{
|
||||
if (!UnitClass) return nullptr;
|
||||
|
||||
FActorSpawnParameters Params;
|
||||
Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
||||
|
||||
ABaseUnit* Unit = GetWorld()->SpawnActor<ABaseUnit>(UnitClass, SpawnTransform, Params);
|
||||
|
||||
if (!Unit) return nullptr;
|
||||
|
||||
if (!FactionID.IsNone()) Unit->PrimaryFaction = FactionID;
|
||||
|
||||
// Register with GameState
|
||||
ATopDownGameState* CurrentGameState = GetGameState<ATopDownGameState>();
|
||||
if (CurrentGameState) CurrentGameState->RegisterUnit(Unit);
|
||||
|
||||
return Unit;
|
||||
}
|
||||
|
||||
void ATopDownGameMode::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
void ATopDownGameMode::PostLogin(APlayerController* NewPlayer)
|
||||
{
|
||||
Super::PostLogin(NewPlayer);
|
||||
|
||||
ATopDownGameState* GS = GetGameState<ATopDownGameState>();
|
||||
AThirdPersonController* PC = Cast<AThirdPersonController>(NewPlayer);
|
||||
if (!GS || !GS->FactionManager || !PC) return;
|
||||
|
||||
// Create a unique faction for this player
|
||||
FFactionData PlayerFaction;
|
||||
PlayerFaction.FactionID = FName(*FString::Printf(TEXT("Player_%d"), GS->FactionManager->Factions.Num()));
|
||||
PlayerFaction.DisplayName = FText::FromString("Player Faction");
|
||||
PlayerFaction.FactionType = EFactionType::PlayerFaction;
|
||||
PlayerFaction.FactionColor = FLinearColor::Blue;
|
||||
PlayerFaction.RelationsTo.Add(FFactionRelation{ FactionID::Feral, -100 });
|
||||
GS->FactionManager->RegisterFaction(PlayerFaction);
|
||||
|
||||
// Store faction ID on the controller
|
||||
PC->PlayerFactionID = PlayerFaction.FactionID;
|
||||
|
||||
// Spawn PlayerUnit near the controller's pawn
|
||||
FVector SpawnLocation = PC->GetPawn() ? PC->GetPawn()->GetActorLocation() : FVector::ZeroVector;
|
||||
FTransform SpawnTransform(FRotator::ZeroRotator, SpawnLocation);
|
||||
|
||||
ABaseUnit* SpawnedUnit = SpawnUnit(DefaultPlayerUnitClass, SpawnTransform, PlayerFaction.FactionID);
|
||||
APlayerUnit* PlayerUnit = Cast<APlayerUnit>(SpawnedUnit);
|
||||
|
||||
if (PlayerUnit)
|
||||
{
|
||||
PC->PlayerUnit = PlayerUnit;
|
||||
PC->UnitManager->AddUnitToSquad(PlayerUnit);
|
||||
PlayerUnit->DisplayName = FText::FromString("Player");
|
||||
}
|
||||
|
||||
GS->OnWorldStateChanged.Broadcast();
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "TopDownGameState.h"
|
||||
#include "FactionManager.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "BaseUnit.h"
|
||||
#include "ZoneActor.h"
|
||||
#include "ThirdPersonController.h"
|
||||
|
||||
ATopDownGameState::ATopDownGameState()
|
||||
{
|
||||
FactionManager = CreateDefaultSubobject<UFactionManager>(TEXT("FactionManager"));
|
||||
}
|
||||
|
||||
void ATopDownGameState::BroadcastWorldStateChanged()
|
||||
{
|
||||
OnWorldStateChanged.Broadcast();
|
||||
if (HasAuthority())
|
||||
{
|
||||
WorldStateVersion++;
|
||||
}
|
||||
}
|
||||
|
||||
void ATopDownGameState::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
OnWorldStateChanged.Clear();
|
||||
}
|
||||
|
||||
void ATopDownGameState::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!HasAuthority()) return; // only server runs the timer
|
||||
|
||||
GetWorldTimerManager().SetTimer(
|
||||
GameTickTimerHandle,
|
||||
this,
|
||||
&ATopDownGameState::OnGameTick,
|
||||
RealSecondsPerGameHour,
|
||||
true // loop
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void ATopDownGameState::OnRep_GameTime()
|
||||
{
|
||||
// Clients update their UI when time replicates
|
||||
OnGameTimeChanged.Broadcast(GameHour, GameDay);
|
||||
}
|
||||
|
||||
void ATopDownGameState::OnRep_WorldStateVersion()
|
||||
{
|
||||
OnWorldStateChanged.Broadcast();
|
||||
}
|
||||
|
||||
void ATopDownGameState::TickAllUnits(float GameHoursDelta)
|
||||
{
|
||||
for (ABaseUnit* Unit : AllUnits)
|
||||
{
|
||||
if (!IsValid(Unit)) continue;
|
||||
TickUnitNeeds(Unit, GameHoursDelta);
|
||||
}
|
||||
}
|
||||
|
||||
void ATopDownGameState::TickUnitNeeds(ABaseUnit* Unit, float GameHoursDelta)
|
||||
{
|
||||
UAbilitySystemComponent* ASC = Unit->GetAbilitySystemComponent();
|
||||
if (!ASC) return;
|
||||
|
||||
const UBaseAttributeSet* AS = ASC->GetSet<UBaseAttributeSet>();
|
||||
if (!AS) return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
AZoneActor* ATopDownGameState::GetZoneAtLocation(FVector Location) const
|
||||
{
|
||||
for (AZoneActor* Zone : AllZones)
|
||||
{
|
||||
if (IsValid(Zone) && Zone->IsLocationInsideZone(Location))
|
||||
{
|
||||
return Zone;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FText ATopDownGameState::GetFormattedTime() const
|
||||
{
|
||||
int32 Hour = FMath::FloorToInt(GameHour);
|
||||
int32 Minute = FMath::FloorToInt((GameHour - Hour) * 60.f);
|
||||
return FText::FromString(FString::Printf(TEXT("%02d:%02d"), Hour, Minute));
|
||||
}
|
||||
|
||||
bool ATopDownGameState::IsNightTime() const
|
||||
{
|
||||
return GameHour < 6.f || GameHour >= 20.f;
|
||||
}
|
||||
|
||||
void ATopDownGameState::RegisterUnit(ABaseUnit* Unit)
|
||||
{
|
||||
if (!Unit || AllUnits.Contains(Unit)) return;
|
||||
AllUnits.Add(Unit);
|
||||
|
||||
OnWorldStateChanged.Broadcast();
|
||||
WorldStateVersion++;
|
||||
}
|
||||
|
||||
void ATopDownGameState::UnregisterUnit(ABaseUnit* Unit)
|
||||
{
|
||||
AllUnits.Remove(Unit);
|
||||
|
||||
OnWorldStateChanged.Broadcast();
|
||||
WorldStateVersion++;
|
||||
}
|
||||
|
||||
void ATopDownGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(ATopDownGameState, GameHour);
|
||||
DOREPLIFETIME(ATopDownGameState, GameDay);
|
||||
DOREPLIFETIME(ATopDownGameState, AllUnits);
|
||||
DOREPLIFETIME(ATopDownGameState, AllZones);
|
||||
DOREPLIFETIME(ATopDownGameState, WorldStateVersion);
|
||||
}
|
||||
|
||||
void ATopDownGameState::OnGameTick()
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("GameTick Hour %f"), GameHour);
|
||||
GameHour += 1.f;
|
||||
if (GameHour >= 24.f) { GameHour = 0.f; GameDay++; }
|
||||
OnGameTimeChanged.Broadcast(GameHour, GameDay);
|
||||
|
||||
// Tick zones first (they supply units)
|
||||
for (AZoneActor* Zone : AllZones)
|
||||
{
|
||||
if (IsValid(Zone)) Zone->TickZone(1.f);
|
||||
}
|
||||
|
||||
// Then tick unit needs (consumes from own inventory if zone didn't supply)
|
||||
TickAllUnits(1.f);
|
||||
|
||||
if (FactionManager) FactionManager->EvaluateFactionNeeds();
|
||||
}
|
||||
|
||||
void ATopDownGameState::RegisterZone(AZoneActor* Zone)
|
||||
{
|
||||
if (!Zone || AllZones.Contains(Zone)) return;
|
||||
AllZones.Add(Zone);
|
||||
|
||||
OnWorldStateChanged.Broadcast();
|
||||
WorldStateVersion++;
|
||||
}
|
||||
|
||||
void ATopDownGameState::UnregisterZone(AZoneActor* Zone)
|
||||
{
|
||||
AllZones.Remove(Zone);
|
||||
|
||||
OnWorldStateChanged.Broadcast();
|
||||
WorldStateVersion++;
|
||||
}
|
||||
|
||||
TArray<FFactionData> ATopDownGameState::GetAllFactions() const
|
||||
{
|
||||
if (!FactionManager) return {};
|
||||
return FactionManager->Factions;
|
||||
}
|
||||
|
||||
float ATopDownGameState::GetPlayerMoney() const
|
||||
{
|
||||
AThirdPersonController* PC = Cast<AThirdPersonController>(GetWorld()->GetFirstPlayerController());
|
||||
if (!PC || !FactionManager) return 0.f;
|
||||
|
||||
FFactionData* Faction = FactionManager->GetFaction(PC->PlayerFactionID);
|
||||
return Faction ? Faction->Money : 0.f;
|
||||
}
|
||||
|
||||
TArray<AZoneActor*> ATopDownGameState::GetZonesForFaction(FName FactionID) const
|
||||
{
|
||||
return AllZones.FilterByPredicate([FactionID](const AZoneActor* Zone)
|
||||
{
|
||||
return IsValid(Zone) && Zone->OwningFaction == FactionID;
|
||||
});
|
||||
}
|
||||
|
||||
TArray<ABaseUnit*> ATopDownGameState::GetUnitsForFaction(FName FactionID) const
|
||||
{
|
||||
return AllUnits.FilterByPredicate([FactionID](const ABaseUnit* Unit)
|
||||
{
|
||||
return IsValid(Unit) && Unit->PrimaryFaction == FactionID;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "UnitManagerComponent.h"
|
||||
#include "BaseUnit.h"
|
||||
#include "BaseCommands.h"
|
||||
#include "Commands/CommandComponent.h"
|
||||
|
||||
UUnitManagerComponent::UUnitManagerComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
ABaseUnit* UUnitManagerComponent::GetFreeUnit()
|
||||
{
|
||||
for (ABaseUnit* Unit : Squad)
|
||||
{
|
||||
if (!Unit->CommandComponent->IsExecutingCommad()) return Unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::IssueCommandToUnit(UBaseCommand* Command, ABaseUnit* Unit)
|
||||
{
|
||||
if (!Command || !Unit) return;
|
||||
|
||||
Unit->CommandComponent->GiveCommand(Command);
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::IssueCommandToUnits(UBaseCommand* Command, TArray<ABaseUnit*> Units)
|
||||
{
|
||||
if (!Command) return;
|
||||
for (ABaseUnit* Unit : Units)
|
||||
{
|
||||
if (!Unit) return;
|
||||
Unit->CommandComponent->GiveCommand(Command);
|
||||
}
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::IssueCommandToSquad(UBaseCommand* Command)
|
||||
{
|
||||
if (!Command) return;
|
||||
for (ABaseUnit* Unit : Squad)
|
||||
{
|
||||
if (!Unit) return;
|
||||
Unit->CommandComponent->GiveCommand(Command);
|
||||
}
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::IssueInteractCommand(AActor* TargetActor, bool bQueue)
|
||||
{
|
||||
ABaseUnit* Unit = GetFreeUnit();
|
||||
if (!Unit) return;
|
||||
|
||||
UInteractCommand* Command = NewObject<UInteractCommand>(Unit);
|
||||
Command->TargetActor = TargetActor;
|
||||
Unit->CommandComponent->GiveCommand(Command, bQueue);
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::IssueMoveCommand(FVector Location, bool bQueue)
|
||||
{
|
||||
for (ABaseUnit* Unit : Squad)
|
||||
{
|
||||
if (!Unit) return;
|
||||
UMoveToCommand* Command = NewObject<UMoveToCommand>(Unit);
|
||||
Command->Destination = Location;
|
||||
Unit->CommandComponent->GiveCommand(Command, bQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::AddUnitToSquad(ABaseUnit* Unit)
|
||||
{
|
||||
if (!Unit || Squad.Contains(Unit)) return;
|
||||
Squad.Add(Unit);
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::RemoveUnitFromSquad(ABaseUnit* Unit)
|
||||
{
|
||||
if (!Unit || !Squad.Contains(Unit)) return;
|
||||
Squad.Remove(Unit);
|
||||
}
|
||||
|
||||
void UUnitManagerComponent::ClearSquad()
|
||||
{
|
||||
Squad.Empty();
|
||||
}
|
||||
|
||||
TArray<ABaseUnit*> UUnitManagerComponent::GetSquad()
|
||||
{
|
||||
return Squad;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "ZoneActor.h"
|
||||
#include "Components/SphereComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "ModuleActor.h"
|
||||
#include "TopDownGameState.h"
|
||||
#include "FactionManager.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "BaseUnit.h"
|
||||
|
||||
AZoneActor::AZoneActor()
|
||||
{
|
||||
bReplicates = true;
|
||||
PrimaryActorTick.bCanEverTick = false; // GameState drives ticking
|
||||
|
||||
// Use sphere component as the zone volume - invisible at runtime
|
||||
ZoneVolume = CreateDefaultSubobject<USphereComponent>(TEXT("ZoneVolume"));
|
||||
ZoneVolume->SetSphereRadius(Radius);
|
||||
ZoneVolume->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
||||
ZoneVolume->SetCollisionResponseToAllChannels(ECR_Ignore);
|
||||
ZoneVolume->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
|
||||
ZoneVolume->SetVisibility(false);
|
||||
ZoneVolume->SetHiddenInGame(true);
|
||||
RootComponent = ZoneVolume;
|
||||
|
||||
ZoneStorage = CreateDefaultSubobject<UInventoryComponent>(TEXT("ZoneStorage"));
|
||||
ZoneStorage->SetIsReplicated(true);
|
||||
|
||||
// Zones get much larger storage than units
|
||||
ZoneStorage->SetMaxCapacity(10000.f);
|
||||
}
|
||||
|
||||
void AZoneActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
// Generate unique zone ID if not set
|
||||
if (ZoneID.IsNone())
|
||||
{
|
||||
ZoneID = FName(*FString::Printf(TEXT("Zone_%s"),
|
||||
*FGuid::NewGuid().ToString().Left(8)));
|
||||
}
|
||||
|
||||
// Debug draw zone boundary
|
||||
DrawDebugCylinder(
|
||||
GetWorld(),
|
||||
GetActorLocation() - FVector(0, 0, 100),
|
||||
GetActorLocation() + FVector(0, 0, 100),
|
||||
Radius,
|
||||
32,
|
||||
FColor::Green,
|
||||
true, // persistent
|
||||
-1.f,
|
||||
0,
|
||||
5.f
|
||||
);
|
||||
|
||||
// Register with GameState
|
||||
if (ATopDownGameState* GS = GetWorld()->GetGameState<ATopDownGameState>())
|
||||
{
|
||||
GS->RegisterZone(this);
|
||||
GS->BroadcastWorldStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AZoneActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (ATopDownGameState* GS = GetWorld()->GetGameState<ATopDownGameState>())
|
||||
{
|
||||
GS->UnregisterZone(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AZoneActor::GetLifetimeReplicatedProps(
|
||||
TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AZoneActor, ZoneID);
|
||||
DOREPLIFETIME(AZoneActor, ZoneName);
|
||||
DOREPLIFETIME(AZoneActor, OwningFaction);
|
||||
DOREPLIFETIME(AZoneActor, Radius);
|
||||
DOREPLIFETIME(AZoneActor, Modules);
|
||||
DOREPLIFETIME(AZoneActor, FactionInfluences);
|
||||
}
|
||||
|
||||
bool AZoneActor::IsLocationInsideZone(FVector Location) const
|
||||
{
|
||||
float DistXY = FVector2D::Distance(
|
||||
FVector2D(GetActorLocation()),
|
||||
FVector2D(Location));
|
||||
return DistXY <= Radius;
|
||||
}
|
||||
|
||||
void AZoneActor::SetRadius(float NewRadius)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
Radius = FMath::Clamp(NewRadius, 100.f, 50000.f);
|
||||
UpdateVolumeSize();
|
||||
}
|
||||
|
||||
void AZoneActor::UpdateVolumeSize()
|
||||
{
|
||||
if (USphereComponent* Sphere = Cast<USphereComponent>(ZoneVolume))
|
||||
{
|
||||
Sphere->SetSphereRadius(Radius);
|
||||
}
|
||||
}
|
||||
|
||||
void AZoneActor::OnRep_Radius()
|
||||
{
|
||||
UpdateVolumeSize();
|
||||
}
|
||||
|
||||
void AZoneActor::SetOwningFaction(FName NewFaction)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
OwningFaction = NewFaction;
|
||||
OnZoneChanged.Broadcast(ZoneID);
|
||||
}
|
||||
|
||||
void AZoneActor::ModifyInfluence(FName FactionID, float Delta)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
FFactionInfluence* Found = FactionInfluences.FindByPredicate(
|
||||
[FactionID](const FFactionInfluence& F)
|
||||
{
|
||||
return F.FactionID == FactionID;
|
||||
});
|
||||
|
||||
if (Found)
|
||||
{
|
||||
Found->Influence = FMath::Clamp(Found->Influence + Delta, 0.f, 100.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFactionInfluence NewInfluence;
|
||||
NewInfluence.FactionID = FactionID;
|
||||
NewInfluence.Influence = FMath::Clamp(Delta, 0.f, 100.f);
|
||||
FactionInfluences.Add(NewInfluence);
|
||||
}
|
||||
|
||||
// Check if ownership should change
|
||||
FName Dominant = GetDominantFaction();
|
||||
if (Dominant != OwningFaction && !Dominant.IsNone())
|
||||
{
|
||||
SetOwningFaction(Dominant);
|
||||
}
|
||||
|
||||
OnZoneChanged.Broadcast(ZoneID);
|
||||
}
|
||||
|
||||
FName AZoneActor::GetDominantFaction() const
|
||||
{
|
||||
FName Dominant;
|
||||
float Highest = 0.f;
|
||||
for (const FFactionInfluence& Influence : FactionInfluences)
|
||||
{
|
||||
if (Influence.Influence > Highest)
|
||||
{
|
||||
Highest = Influence.Influence;
|
||||
Dominant = Influence.FactionID;
|
||||
}
|
||||
}
|
||||
return Dominant;
|
||||
}
|
||||
|
||||
bool AZoneActor::CanFactionBuildHere(FName FactionID, float Reputation) const
|
||||
{
|
||||
// Owning faction can always build
|
||||
if (FactionID == OwningFaction) return true;
|
||||
|
||||
// No owner - anyone can build
|
||||
if (OwningFaction.IsNone()) return true;
|
||||
|
||||
// Other factions need reputation threshold
|
||||
// TODO: make threshold configurable per zone
|
||||
const float ReputationThreshold = 50.f;
|
||||
return Reputation >= ReputationThreshold;
|
||||
}
|
||||
|
||||
void AZoneActor::RegisterModule(AModuleActor* Module)
|
||||
{
|
||||
if (!Module || Modules.Contains(Module)) return;
|
||||
Modules.Add(Module);
|
||||
OnZoneChanged.Broadcast(ZoneID);
|
||||
}
|
||||
|
||||
void AZoneActor::UnregisterModule(AModuleActor* Module)
|
||||
{
|
||||
Modules.Remove(Module);
|
||||
OnZoneChanged.Broadcast(ZoneID);
|
||||
}
|
||||
|
||||
// Called by GameState each game hour
|
||||
void AZoneActor::TickZone(float GameHoursDelta)
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
TickModuleProduction(GameHoursDelta);
|
||||
TickZoneInfluence(GameHoursDelta);
|
||||
SupplyUnitsInZone(GameHoursDelta);
|
||||
}
|
||||
|
||||
void AZoneActor::TickModuleProduction(float GameHoursDelta)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Zone %s ticking %d modules"),
|
||||
*ZoneID.ToString(), Modules.Num());
|
||||
for (AModuleActor* Module : Modules)
|
||||
{
|
||||
if (!IsValid(Module)) continue;
|
||||
Module->TickProduction(GameHoursDelta, ZoneStorage);
|
||||
}
|
||||
}
|
||||
|
||||
void AZoneActor::TickZoneInfluence(float GameHoursDelta)
|
||||
{
|
||||
// Owning faction passively gains influence over time
|
||||
if (!OwningFaction.IsNone())
|
||||
{
|
||||
ModifyInfluence(OwningFaction, 1.f * GameHoursDelta);
|
||||
}
|
||||
|
||||
// All other factions slowly lose influence if they have no presence
|
||||
// TODO: factor in military units in zone
|
||||
}
|
||||
|
||||
void AZoneActor::SupplyUnitsInZone(float GameHoursDelta)
|
||||
{
|
||||
// Find all units inside this zone and supply their upkeep from zone storage
|
||||
// This replaces the GameState consuming from unit inventory directly
|
||||
if (!ZoneStorage) return;
|
||||
|
||||
TArray<AActor*> OverlappingActors;
|
||||
ZoneVolume->GetOverlappingActors(OverlappingActors, ABaseUnit::StaticClass());
|
||||
|
||||
for (AActor* Actor : OverlappingActors)
|
||||
{
|
||||
ABaseUnit* Unit = Cast<ABaseUnit>(Actor);
|
||||
if (!Unit) continue;
|
||||
// ToDo
|
||||
}
|
||||
}
|
||||
|
||||
float AZoneActor::GetPriceForFaction(FName ResourceID, FName FactionID, float Reputation) const
|
||||
{
|
||||
const FResourcePrice* Price = ResourcePrices.FindByPredicate(
|
||||
[ResourceID](const FResourcePrice& P)
|
||||
{
|
||||
return P.ResourceID == ResourceID;
|
||||
});
|
||||
|
||||
if (!Price) return 0.f; // free if no price set
|
||||
|
||||
// Map reputation -100 to 100 -> price multiplier 2.0 to 0.5
|
||||
float Multiplier = FMath::GetMappedRangeValueClamped(
|
||||
FVector2D(-100.f, 100.f),
|
||||
FVector2D(2.f, 0.5f),
|
||||
Reputation
|
||||
);
|
||||
|
||||
return Price->BasePrice * Multiplier;
|
||||
}
|
||||
|
||||
void AZoneActor::OnRep_ZoneData()
|
||||
{
|
||||
OnZoneChanged.Broadcast(ZoneID);
|
||||
}
|
||||
|
||||
bool AZoneActor::CanFactionUseResources(FName FactionID, float Reputation) const
|
||||
{
|
||||
// Owning faction always has access
|
||||
if (FactionID == OwningFaction) return true;
|
||||
|
||||
// No owner - open to all
|
||||
if (OwningFaction.IsNone()) return true;
|
||||
|
||||
// Others need minimum reputation
|
||||
// Hostile factions can't use resources
|
||||
return Reputation >= -25.f; // above hostile threshold
|
||||
}
|
||||
|
||||
UInventoryComponent* AZoneActor::GetInventoryComponent_Implementation() const
|
||||
{
|
||||
return ZoneStorage;
|
||||
}
|
||||
|
||||
EOutlinerStatus AZoneActor::GetOutlinerStatus() const
|
||||
{
|
||||
if (Modules.Num() == 0) return EOutlinerStatus::Warning;
|
||||
// Check if any modules are critical
|
||||
for (AModuleActor* Module : Modules)
|
||||
{
|
||||
if (IsValid(Module) && Module->ModuleState == EModuleState::Damaged)
|
||||
return EOutlinerStatus::Critical;
|
||||
}
|
||||
return EOutlinerStatus::OK;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "AModuleActor.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API AAModuleActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AAModuleActor();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,170 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AttributeSet.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "BaseAttributeSet.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API UBaseAttributeSet : public UAttributeSet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth) FGameplayAttributeData MaxHealth;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_HealthRegen) FGameplayAttributeData HealthRegen;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_MaxStamina) FGameplayAttributeData MaxStamina;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Stamina) FGameplayAttributeData Stamina;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_StaminaRegen) FGameplayAttributeData StaminaRegen;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_MaxMana) FGameplayAttributeData MaxMana;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Mana) FGameplayAttributeData Mana;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_ManaRegen) FGameplayAttributeData ManaRegen;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Armor) FGameplayAttributeData Armor;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_ElementalArmor) FGameplayAttributeData ElementalArmor;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_CritChance) FGameplayAttributeData CritChance;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_CritaDamage) FGameplayAttributeData CritaDamage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_MoveSpeed) FGameplayAttributeData MoveSpeed;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_SprintSpeedMultiplier) FGameplayAttributeData SprintSpeedMultiplier;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_SneakSpeedMultiplier) FGameplayAttributeData SneakSpeedMultiplier;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_SneakDmgMultiplier) FGameplayAttributeData SneakDmgMultiplier;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_WeakSpotDmgMultiplier) FGameplayAttributeData WeakSpotDmgMultiplier;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_MaxSatiety) FGameplayAttributeData MaxSatiety;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Satiety) FGameplayAttributeData Satiety;
|
||||
|
||||
public:
|
||||
|
||||
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
|
||||
virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override;
|
||||
virtual void PostGameplayEffectExecute( const FGameplayEffectModCallbackData& Data) override;
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
UFUNCTION() virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
|
||||
UFUNCTION() virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
|
||||
UFUNCTION() virtual void OnRep_HealthRegen(const FGameplayAttributeData& OldHealthRegen);
|
||||
UFUNCTION() virtual void OnRep_MaxStamina(const FGameplayAttributeData& OldMaxStamina);
|
||||
UFUNCTION() virtual void OnRep_Stamina(const FGameplayAttributeData& OldStamina);
|
||||
UFUNCTION() virtual void OnRep_StaminaRegen(const FGameplayAttributeData& OldStaminaRegen);
|
||||
UFUNCTION() virtual void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana);
|
||||
UFUNCTION() virtual void OnRep_Mana(const FGameplayAttributeData& OldMana);
|
||||
UFUNCTION() virtual void OnRep_ManaRegen(const FGameplayAttributeData& OldManaRegen);
|
||||
UFUNCTION() virtual void OnRep_Armor(const FGameplayAttributeData& OldArmor);
|
||||
UFUNCTION() virtual void OnRep_ElementalArmor(const FGameplayAttributeData& OldElementalArmor);
|
||||
UFUNCTION() virtual void OnRep_CritChance(const FGameplayAttributeData& OldCritChance);
|
||||
UFUNCTION() virtual void OnRep_CritaDamage(const FGameplayAttributeData& OldCritaDamage);
|
||||
UFUNCTION() virtual void OnRep_MoveSpeed(const FGameplayAttributeData& OldMoveSpeed);
|
||||
UFUNCTION() virtual void OnRep_SprintSpeedMultiplier(const FGameplayAttributeData& OldSprintSpeedMultiplier);
|
||||
UFUNCTION() virtual void OnRep_SneakSpeedMultiplier(const FGameplayAttributeData& OldSneakSpeedMultiplier);
|
||||
UFUNCTION() virtual void OnRep_SneakDmgMultiplier(const FGameplayAttributeData& OldSneakDmgMultiplier);
|
||||
UFUNCTION() virtual void OnRep_WeakSpotDmgMultiplier(const FGameplayAttributeData& OldWeakSpotDmgMultiplier);
|
||||
UFUNCTION() virtual void OnRep_MaxSatiety(const FGameplayAttributeData& OldMaxSatiety);
|
||||
UFUNCTION() virtual void OnRep_Satiety(const FGameplayAttributeData& OldSatiety);
|
||||
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, MaxHealth);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(MaxHealth);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(MaxHealth);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(MaxHealth);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, Health);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(Health);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(Health);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(Health);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, MaxStamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(MaxStamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(MaxStamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(MaxStamina);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, Stamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(Stamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(Stamina);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(Stamina);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, MaxMana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(MaxMana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(MaxMana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(MaxMana);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, Mana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(Mana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(Mana);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(Mana);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, Armor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(Armor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(Armor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(Armor);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, ElementalArmor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(ElementalArmor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(ElementalArmor);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(ElementalArmor);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, CritChance);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(CritChance);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(CritChance);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(CritChance);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, CritaDamage);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(CritaDamage);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(CritaDamage);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(CritaDamage);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, MoveSpeed);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(MoveSpeed);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(MoveSpeed);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(MoveSpeed);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, SprintSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(SprintSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(SprintSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(SprintSpeedMultiplier);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, SneakSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(SneakSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(SneakSpeedMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(SneakSpeedMultiplier);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, HealthRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(HealthRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(HealthRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(HealthRegen);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, StaminaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(StaminaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(StaminaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(StaminaRegen);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, ManaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(ManaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(ManaRegen);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(ManaRegen);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, SneakDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(SneakDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(SneakDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(SneakDmgMultiplier);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, WeakSpotDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(WeakSpotDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(WeakSpotDmgMultiplier);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(WeakSpotDmgMultiplier);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, MaxSatiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(MaxSatiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(MaxSatiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(MaxSatiety);
|
||||
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UBaseAttributeSet, Satiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(Satiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(Satiety);
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(Satiety);
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "BaseAttributeSet.h"
|
||||
#include "AbilitySystemInterface.h"
|
||||
#include "ItemActor.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "BaseCharacter.generated.h"
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API ABaseCharacter : public ACharacter, public IAbilitySystemInterface, public IInteractionInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
ABaseCharacter();
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
//~ Begin IAbilitySystemInterface
|
||||
/** Returns our Ability System Component. */
|
||||
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
|
||||
//~ End IAbilitySystemInterface
|
||||
|
||||
/** Ability System Component. Required to use Gameplay Attributes and Gameplay Abilities. */
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
|
||||
UAbilitySystemComponent* AbilitySystemComponent;
|
||||
|
||||
/** Sample Attribute Set. */
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly)
|
||||
const UBaseAttributeSet* AttributeSet;
|
||||
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
|
||||
UInventoryComponent* InventoryComponent;
|
||||
|
||||
|
||||
//UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable)
|
||||
void TryPickup(AItemActor* ItemActor);
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
AActor* LookedAtActor = nullptr;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void InteractionLineTrace();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable)
|
||||
void InteractWithInteractable(AActor* TargetActor);
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
int32 InteractionDistance = 500;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "BaseCommand.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Abstract, BlueprintType)
|
||||
class MAYBETHISTIME_API UBaseCommand : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent) void Execute();
|
||||
UFUNCTION(BlueprintNativeEvent) void Interrupt();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly) class ABaseUnit* Owner;
|
||||
|
||||
// Broadcasts true if succeeded, false if failed
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCommandFinished, bool, bSuccess);
|
||||
UPROPERTY(BlueprintAssignable) FOnCommandFinished OnCommandFinished;
|
||||
|
||||
protected:
|
||||
// Subclasses call this when done
|
||||
void Complete(bool bSuccess)
|
||||
{
|
||||
OnCommandFinished.Broadcast(bSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BaseCommand.h"
|
||||
#include "AIController.h"
|
||||
#include "Navigation/PathFollowingComponent.h"
|
||||
#include "BaseCommands.generated.h"
|
||||
|
||||
class AActor;
|
||||
|
||||
UCLASS()
|
||||
class UMoveToCommand : public UBaseCommand
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY() FVector Destination;
|
||||
UPROPERTY() float AcceptanceRadius = 50.f; // how close = "arrived"
|
||||
|
||||
virtual void Execute_Implementation() override;
|
||||
virtual void Interrupt_Implementation() override;
|
||||
|
||||
private:
|
||||
UPROPERTY() AAIController* AIController;
|
||||
|
||||
UFUNCTION() void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result);
|
||||
};
|
||||
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UInteractCommand : public UBaseCommand
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY() AActor* TargetActor;
|
||||
UPROPERTY() int32 InteractionRange = 150;
|
||||
|
||||
virtual void Execute_Implementation() override;
|
||||
virtual void Interrupt_Implementation() override;
|
||||
|
||||
|
||||
private:
|
||||
UPROPERTY() UMoveToCommand* SubMoveCommand;
|
||||
|
||||
UFUNCTION() void DoInteract();
|
||||
UFUNCTION() void OnMoveFinished(bool bSuccess);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "BaseAttributeSet.h"
|
||||
#include "AbilitySystemInterface.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "BaseCommand.h"
|
||||
#include "InventoryInterface.h"
|
||||
#include "OutlinerTypes.h"
|
||||
#include "InteractionInterface.h"
|
||||
#include "BaseUnit.generated.h"
|
||||
|
||||
class UCommandComponent;
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API ABaseUnit : public ACharacter, public IAbilitySystemInterface, public IInteractionInterface, public IInventoryInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
ABaseUnit();
|
||||
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities") UCommandComponent* CommandComponent;
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities") UAbilitySystemComponent* AbilitySystemComponent;
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities") UBaseAttributeSet* AttributeSet;
|
||||
UPROPERTY(EditDefaultsOnly) TArray<TSubclassOf<UGameplayEffect>> PassiveUpkeepEffects;
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite) FName PrimaryFaction;
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite) FText DisplayName;
|
||||
|
||||
// Inventory
|
||||
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities") UInventoryComponent* InventoryComponent;
|
||||
virtual UInventoryComponent* GetInventoryComponent_Implementation() const override;
|
||||
|
||||
// HUD
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) virtual EOutlinerStatus GetOutlinerStatus() const;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AIController.h"
|
||||
#include "BaseUnitAIController.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API ABaseUnitAIController : public AAIController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "CommandComponent.generated.h"
|
||||
|
||||
class UBaseCommand;
|
||||
class ABaseUnit;
|
||||
|
||||
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||
class MAYBETHISTIME_API UCommandComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UCommandComponent();
|
||||
|
||||
ABaseUnit* OwnerUnit;
|
||||
|
||||
UFUNCTION(BlueprintCallable) void GiveCommand(UBaseCommand* NewCommand, bool bQueue = false);
|
||||
UFUNCTION(BlueprintCallable) void ExecuteNextCommand(bool bInterrupt = true);
|
||||
UFUNCTION(BlueprintCallable) void InterruptCurrentCommand();
|
||||
UFUNCTION(BlueprintCallable) void ClearCommandQueue();
|
||||
UFUNCTION(BlueprintCallable) void RemoveCommandFromQueue(UBaseCommand* CommandToRemove);
|
||||
UFUNCTION(BlueprintCallable) void ExecuteCommand(UBaseCommand* Command);
|
||||
UFUNCTION(BlueprintCallable) void CommandFinished(bool bSuccess);
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) const TArray<UBaseCommand*> GetUnitCommandQueue();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool IsExecutingCommad() const;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCommandQueueChanged);
|
||||
UPROPERTY(BlueprintAssignable) FOnCommandQueueChanged OnCommandQueueChanged;
|
||||
|
||||
private:
|
||||
UPROPERTY() TArray<UBaseCommand*> CommandQueue;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
// FactionManager.h
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "FactionManager.generated.h"
|
||||
|
||||
// Relation thresholds
|
||||
#define FACTION_HOSTILE_THRESHOLD -25.f
|
||||
#define FACTION_NEUTRAL_THRESHOLD 25.f
|
||||
#define FACTION_FRIENDLY_THRESHOLD 50.f
|
||||
#define FACTION_ALLIED_THRESHOLD 75.f
|
||||
|
||||
// Hardcoded starting faction IDs
|
||||
namespace FactionID
|
||||
{
|
||||
const FName Wanderer = "Wanderer";
|
||||
const FName Feral = "Feral";
|
||||
const FName Wildlife = "Wildlife";
|
||||
// Add more as needed
|
||||
}
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EFactionType : uint8
|
||||
{
|
||||
PlayerFaction, // player controlled
|
||||
Civilized, // organized NPC human faction
|
||||
Hostile, // mindless, attacks everything
|
||||
Bandit, // attacks weak, avoids strong
|
||||
Wanderer, // no allegiance
|
||||
Wildlife // neutral unless provoked
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ERelationState : uint8
|
||||
{
|
||||
Allied,
|
||||
Friendly,
|
||||
Neutral,
|
||||
Hostile
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FConsumptionRate
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere) FName ItemID;
|
||||
UPROPERTY(EditAnywhere) float AmountPerHour = 1.f;
|
||||
UPROPERTY(EditAnywhere) bool bPassive = true;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFactionNeed
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere) FName NeedID;
|
||||
UPROPERTY(EditAnywhere) float Threshold = 100.f;
|
||||
UPROPERTY() float Urgency = 0.f;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFactionRelation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FName TargetFactionID;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float Relation = 0.f;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFactionData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) FName FactionID;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) FText DisplayName;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) FLinearColor FactionColor = FLinearColor::White;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) EFactionType FactionType = EFactionType::Wanderer;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) TArray<FFactionRelation> RelationsTo;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) TArray<FFactionNeed> Needs;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) float Money = 10000000.f;
|
||||
|
||||
UPROPERTY(EditAnywhere) bool bInfiniteMoney = true;
|
||||
|
||||
float GetRelationTo(FName OtherFaction) const
|
||||
{
|
||||
const FFactionRelation* Found = RelationsTo.FindByPredicate([OtherFaction](const FFactionRelation& R) { return R.TargetFactionID == OtherFaction; });
|
||||
return Found ? Found->Relation : 0.f;
|
||||
}
|
||||
|
||||
ERelationState GetRelationStateTo(FName OtherFaction) const
|
||||
{
|
||||
float Relation = GetRelationTo(OtherFaction);
|
||||
if (Relation >= FACTION_ALLIED_THRESHOLD) return ERelationState::Allied;
|
||||
if (Relation >= FACTION_FRIENDLY_THRESHOLD) return ERelationState::Friendly;
|
||||
if (Relation >= FACTION_NEUTRAL_THRESHOLD) return ERelationState::Neutral;
|
||||
return ERelationState::Hostile;
|
||||
}
|
||||
|
||||
float& FindOrAddRelationTo(FName Faction)
|
||||
{
|
||||
FFactionRelation* Found = RelationsTo.FindByPredicate([Faction](const FFactionRelation& R) { return R.TargetFactionID == Faction; });
|
||||
if (!Found)
|
||||
{
|
||||
RelationsTo.Add(FFactionRelation{ Faction , 0});
|
||||
return RelationsTo.Last().Relation;
|
||||
}
|
||||
|
||||
return Found->Relation;
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
|
||||
class MAYBETHISTIME_API UFactionManager : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFactionManager();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
|
||||
UPROPERTY(ReplicatedUsing = OnRep_Factions, BlueprintReadOnly) TArray<FFactionData> Factions; // All factions
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable) void RegisterFaction(FFactionData NewFaction); // Faction Registration
|
||||
|
||||
FFactionData* GetFaction(FName FactionID);
|
||||
const FFactionData* GetFaction(FName FactionID) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) FFactionData GetFactionCopy(FName FactionID) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool FactionExists(FName FactionID) const;
|
||||
|
||||
// --- Relations ---
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) float GetRelation(FName FactionA, FName FactionB) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) ERelationState GetRelationState(FName FactionA, FName FactionB) const;
|
||||
UFUNCTION(BlueprintCallable) void ModifyRelation(FName FactionA, FName FactionB, float Delta);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool AreHostile(FName FactionA, FName FactionB) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool AreAllied(FName FactionA, FName FactionB) const;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFactionChanged, FName, FactionID);
|
||||
UPROPERTY(BlueprintAssignable) FOnFactionChanged OnFactionChanged;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnRelationChanged, FName, FactionA, FName, FactionB, float, NewRelation);
|
||||
UPROPERTY(BlueprintAssignable) FOnRelationChanged OnRelationChanged;
|
||||
|
||||
void EvaluateFactionNeeds();
|
||||
|
||||
private:
|
||||
void InitializeDefaultFactions();
|
||||
|
||||
UFUNCTION() void OnRep_Factions();
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "InteractionInterface.generated.h"
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UInteractionInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
|
||||
class MAYBETHISTIME_API IInteractionInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void Interact(AActor* Interactor);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void LookingAt(AActor* Interactor, bool IsLooking);
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ItemObject.h"
|
||||
#include <Delegates/DelegateCombinations.h>
|
||||
|
||||
#include "ItemData.h"
|
||||
#include "InventoryComponent.generated.h"
|
||||
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EAddItemResult : uint8
|
||||
{
|
||||
FullyAdded,
|
||||
PartiallyAdded,
|
||||
Failed,
|
||||
Switched
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ESortType : uint8
|
||||
{
|
||||
Alpabeticaly,
|
||||
Weight,
|
||||
Amount,
|
||||
Type,
|
||||
None
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType, ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||
class MAYBETHISTIME_API UInventoryComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UInventoryComponent();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_ItemList) TArray<FItemData> ItemList;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated) int32 InventorySize = 32;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite) TMap<FName, FItemData> EquipedItemMap;
|
||||
|
||||
UPROPERTY(EditAnywhere) float MaxCapacity = 1000.f;
|
||||
UPROPERTY(Replicated) float CurrentWeight = 0.f;
|
||||
UPROPERTY(Replicated) TArray<FItemData> Resources;
|
||||
|
||||
const int32 DropDistance = 200;
|
||||
public:
|
||||
|
||||
// Add resource, returns overflow
|
||||
float AddResource(FItemData& ItemData);
|
||||
|
||||
// Remove resource, returns false if not enough
|
||||
bool ConsumeResource(FName ResourceID, float Amount);
|
||||
|
||||
float GetResource(FName ResourceID) const;
|
||||
float GetFreeCapacity() const { return MaxCapacity - CurrentWeight; }
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_ItemList();
|
||||
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
// Event dispatcher
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnInventoryChanged);
|
||||
UPROPERTY(BlueprintAssignable, Category = "Inventory") FOnInventoryChanged OnInventoryChanged;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) const FItemData& GetItemAt(int32 SlotIndex) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) const TArray<FItemData> GetAllItems() const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) const TArray<FItemData> FindItemsByID(FName ItemID) const;
|
||||
UFUNCTION(BlueprintCallable) EAddItemResult TryAddItem(FItemData NewItem, FItemData& OutRemainingItem);
|
||||
UFUNCTION(BlueprintCallable) EAddItemResult AddItemAt(FItemData NewItem, int32 SlotIndex, FItemData& OutRemainingItem, bool AllowSwitch = false, bool AllowEmptySlots = true);
|
||||
UFUNCTION(BlueprintCallable) void SetItemAt(FItemData NewItem, int32 SlotIndex);
|
||||
UFUNCTION(BlueprintCallable) void RemoveItemAt(int32 SlotIndex);
|
||||
UFUNCTION(BlueprintCallable) bool GetFirstFreeSlot(int32& SlotIndex) const;
|
||||
UFUNCTION(BlueprintCallable) void SetInventorySize(int32 NewInventorySize);
|
||||
UFUNCTION(BlueprintCallable) int32 GetInventorySize() const;
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable) void DropItem(FItemData ItemToDrop, const FTransform& SpawnTransform);
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable) void DropItemData(FItemData ItemToDrop);
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable) void DropItemAtSlot(int32 SlotIndex);
|
||||
FTransform GetDropTransform() const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool ContainsItem(FName ItemID, int32 Amount = 1) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool ContainsItems(const TMap<FName, int32>& RequiredItems) const;
|
||||
UFUNCTION(BlueprintCallable) bool RemoveItem(FName ItemID, int32 Amount = 1);
|
||||
UFUNCTION(BlueprintCallable) bool RemoveItems(const TMap<FName, int32>& RequiredItems);
|
||||
UFUNCTION(BlueprintCallable) int32 GetItemAmount(FName ID) const;
|
||||
UFUNCTION(BlueprintCallable) void Sort(ESortType SortType);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable) void SetMaxCapacity(float NewMaxCapacity);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "InventoryInterface.generated.h"
|
||||
|
||||
class UInventoryComponent;
|
||||
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UInventoryInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
|
||||
class MAYBETHISTIME_API IInventoryInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
UInventoryComponent* GetInventoryComponent() const;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "InteractionInterface.h"
|
||||
#include "ItemData.h"
|
||||
#include "ItemActor.generated.h"
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API AItemActor : public AActor, public IInteractionInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AItemActor();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
virtual void Interact_Implementation(AActor* Interactor) override;
|
||||
virtual void LookingAt_Implementation(AActor* Interactor, bool IsLooking) override;
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
virtual void Destroyed() override;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_ItemData, Category = "Item", meta = (ExposeOnSpawn = "true"))
|
||||
FItemData ItemData;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
UStaticMeshComponent* MeshComponent;
|
||||
|
||||
FItemData GetItemData() const;
|
||||
|
||||
void SetItemObject(FItemData NewItem);
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_ItemData();
|
||||
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "MyGameplayTags.h"
|
||||
#include "ItemDefinition.h"
|
||||
#include "ItemData.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FItemData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
TObjectPtr<UItemDefinition> Definition = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 CurrentStackSize = 1;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
float CurrentDurability = 1.0f;
|
||||
|
||||
bool IsValid() const { return Definition != nullptr; }
|
||||
|
||||
bool HasTag(FGameplayTag Tag) const
|
||||
{
|
||||
return Definition && Definition->Tags.HasTag(Tag);
|
||||
}
|
||||
|
||||
int32 AddToStack(int32 Value)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Stackable)) { CurrentStackSize = 1; return Value; }
|
||||
int32 NewTotal = CurrentStackSize + Value;
|
||||
CurrentStackSize = FMath::Min(NewTotal, Definition->MaxStackSize);
|
||||
return FMath::Max(NewTotal - Definition->MaxStackSize, 0);
|
||||
}
|
||||
|
||||
float AddToDurability(float Value)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Durable)) { CurrentDurability = 1; return Value; }
|
||||
float NewTotal = CurrentDurability + Value;
|
||||
CurrentDurability = FMath::Min(NewTotal, Definition->MaxDurability);
|
||||
return FMath::Max(NewTotal - Definition->MaxDurability, 0.f);
|
||||
}
|
||||
|
||||
int32 SetStack(int32 NewValue)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Stackable)) { CurrentStackSize = 1; return NewValue; }
|
||||
int32 Clamped = FMath::Clamp(NewValue, 0, Definition->MaxStackSize);
|
||||
CurrentStackSize = Clamped;
|
||||
return FMath::Max(NewValue - Clamped, 0);
|
||||
}
|
||||
|
||||
float SetDurability(float NewValue)
|
||||
{
|
||||
if (!HasTag(Item_Flag_Durable)) { CurrentDurability = 1.f; return NewValue; }
|
||||
float Clamped = FMath::Clamp(NewValue, 0.f, Definition->MaxDurability);
|
||||
CurrentDurability = Clamped;
|
||||
return FMath::Max(NewValue - Clamped, 0.f);
|
||||
}
|
||||
|
||||
void SetItemDefinition(UItemDefinition* NewDefinition)
|
||||
{
|
||||
if (!NewDefinition) return;
|
||||
Definition = NewDefinition;
|
||||
}
|
||||
|
||||
TArray<TSubclassOf<UGameplayEffect>> GetGrantedEffects() const
|
||||
{
|
||||
return Definition ? Definition->GrantedEffects : TArray<TSubclassOf<UGameplayEffect>>{};
|
||||
}
|
||||
|
||||
TArray<TSubclassOf<UGameplayAbility>> GetGrantedAbility() const
|
||||
{
|
||||
return Definition ? Definition->GrantedAbility : TArray<TSubclassOf<UGameplayAbility>>{};
|
||||
}
|
||||
|
||||
// Equality operator needed for replication delta serialization
|
||||
bool operator==(const FItemData& Other) const
|
||||
{
|
||||
return Definition == Other.Definition &&
|
||||
CurrentStackSize == Other.CurrentStackSize &&
|
||||
CurrentDurability == Other.CurrentDurability;
|
||||
}
|
||||
|
||||
bool operator!=(const FItemData& Other) const { return !(*this == Other); }
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GameplayEffect.h"
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
#include "ItemDefinition.generated.h"
|
||||
|
||||
|
||||
class AItemActor;
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FItemRegistryRow : public FTableRowBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) UItemDefinition* Definition = nullptr;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API UItemDefinition : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FName ID;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FText Name;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FText Description ;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FGameplayTagContainer Tags;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
int32 MaxStackSize = 1;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float MaxDurability = 1.0f;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float WeightPerUnit = 0.1f;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
UTexture2D* Icon;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TArray<TSubclassOf<UGameplayEffect>> GrantedEffects;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TArray<TSubclassOf<UGameplayAbility>> GrantedAbility;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TObjectPtr<UStaticMesh> StaticMesh;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TObjectPtr<USkeletalMesh> SkeletalMesh;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TSubclassOf<AItemActor> ItemActorClass;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
bool bIsBulkResource = true;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ItemDefinition.h"
|
||||
#include "ItemObject.generated.h"
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API UItemObject : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UItemObject();
|
||||
~UItemObject();
|
||||
|
||||
virtual bool IsSupportedForNetworking() const override { return true; }
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
UItemDefinition* Definition;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
int32 CurrentStackSize = 1;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
float CurrentDurability = 1.0f;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
bool HasTag(FGameplayTag Tag) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
int32 AddToStack(int32 Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
float AddToDurability(float Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
int32 SetStack(int32 NewValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
float SetDurability(float NewValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetItemDefinition(UItemDefinition* NewDefinition);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
TArray<TSubclassOf<UGameplayEffect>> GetGrantedEffects() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
TArray<TSubclassOf<UGameplayAbility>> GetGrantedAbility() const;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// ItemRegistry.h
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "ItemDefinition.h"
|
||||
#include "ItemRegistry.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API UItemRegistry : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
|
||||
// Set this in your GameInstance Blueprint or config
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TSoftObjectPtr<UDataTable> ItemDataTable;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UItemDefinition* GetDefinition(FName ItemID) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
bool IsValidItem(FName ItemID) const;
|
||||
|
||||
// Get all registered item IDs
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
TArray<FName> GetAllItemIDs() const;
|
||||
|
||||
private:
|
||||
// Cached loaded table
|
||||
UPROPERTY()
|
||||
UDataTable* LoadedTable = nullptr;
|
||||
|
||||
void LoadTable();
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "ModuleDefinition.h"
|
||||
#include "OutlinerTypes.h"
|
||||
#include "ModuleActor.generated.h"
|
||||
|
||||
class UInventoryComponent;
|
||||
class AZoneActor;
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EModuleState : uint8
|
||||
{
|
||||
UnderConstruction, // ghost/scaffold phase
|
||||
Active, // working normally
|
||||
Inactive, // built but not working (no workers, no power etc)
|
||||
Damaged, // needs repair
|
||||
Destroyed
|
||||
};
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnModuleStateChanged, EModuleState, NewState);
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API AModuleActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AModuleActor();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
// --- Definition ---
|
||||
UPROPERTY(ReplicatedUsing = OnRep_Definition, EditAnywhere, BlueprintReadOnly) UModuleDefinition* Definition;
|
||||
|
||||
// --- State ---
|
||||
UPROPERTY(ReplicatedUsing = OnRep_State, BlueprintReadOnly) EModuleState ModuleState = EModuleState::UnderConstruction;
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) float ConstructionProgress = 0.f; // 0-1
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) EOutlinerStatus GetOutlinerStatus() const;
|
||||
|
||||
// --- Zone ---
|
||||
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Zone") AZoneActor* OwningZone;
|
||||
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Zone") AZoneActor* DefaultZone;
|
||||
|
||||
void AddResourceToZone(UInventoryComponent* ZoneStorage, FName ResourceID, float Amount);
|
||||
// --- Workers ---
|
||||
// Units assigned specifically to this module
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) TArray<class ABaseUnit*> AssignedWorkers;
|
||||
|
||||
// --- Delegates ---
|
||||
UPROPERTY(BlueprintAssignable) FOnModuleStateChanged OnModuleStateChanged;
|
||||
|
||||
// --- Construction ---
|
||||
UFUNCTION(BlueprintCallable) bool CanAffordConstruction(UInventoryComponent* PayingInventory) const;
|
||||
UFUNCTION(BlueprintCallable) bool StartConstruction(AZoneActor* Zone, UInventoryComponent* PayingInventory);
|
||||
|
||||
// Called by ZoneActor each game hour
|
||||
void TickProduction(float GameHoursDelta, UInventoryComponent* ZoneStorage);
|
||||
|
||||
// --- Workers ---
|
||||
UFUNCTION(BlueprintCallable) bool AssignWorker(ABaseUnit* Unit);
|
||||
UFUNCTION(BlueprintCallable) void RemoveWorker(ABaseUnit* Unit);
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) int32 GetWorkerCount() const { return AssignedWorkers.Num(); }
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool IsWorking() const { return ModuleState == EModuleState::Active; }
|
||||
|
||||
// --- Placement ---
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool CanBePlacedAt(FVector Location, AZoneActor* Zone) const;
|
||||
|
||||
// Called on clients when definition replicates - sets up visuals
|
||||
UFUNCTION(BlueprintNativeEvent) void OnDefinitionLoaded();
|
||||
|
||||
// Called on clients when state changes - swap ghost/real mesh etc
|
||||
UFUNCTION(BlueprintNativeEvent) void OnStateChanged(EModuleState NewState);
|
||||
|
||||
protected:
|
||||
void UpdateWorkingState();
|
||||
void TickConstruction(float GameHoursDelta);
|
||||
void TickActiveProduction(float GameHoursDelta, UInventoryComponent* ZoneStorage);
|
||||
|
||||
UFUNCTION() void OnRep_Definition();
|
||||
UFUNCTION() void OnRep_State();
|
||||
|
||||
private:
|
||||
|
||||
// Testing only
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void ForceCompleteConstruction(AZoneActor* Zone);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "ModuleDefinition.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EZoneModuleType : uint8
|
||||
{
|
||||
Storage,
|
||||
Production,
|
||||
Medical,
|
||||
Defensive,
|
||||
Housing,
|
||||
Infrastructure
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FModuleResourceRate
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) FName ResourceID;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) float AmountPerHour = 1.f;
|
||||
// If true needs workers to function, false = runs automatically
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bRequiresWorkers = false;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FModuleCost
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) FName ResourceID;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) float Amount = 0.f;
|
||||
};
|
||||
|
||||
class AModuleActor;
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API UModuleDefinition : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
// --- Identity ---
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity") FName ModuleID;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity") FText DisplayName;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity") FText Description;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity") EZoneModuleType ZoneModuleType;
|
||||
|
||||
// --- Visuals ---
|
||||
// The Blueprint actor class that represents this module in the world
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visuals") TSubclassOf<AModuleActor> ModuleActorClass;
|
||||
|
||||
// Ghost/scaffold mesh shown during construction
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visuals") TSubclassOf<AModuleActor> ConstructionActorClass;
|
||||
|
||||
// --- Placement ---
|
||||
// Radius this module occupies (for overlap checks)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement") float PlacementRadius = 200.f;
|
||||
|
||||
// Minimum reputation required to build this module in a zone
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement") float RequiredReputation = 0.f;
|
||||
|
||||
// --- Construction ---
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Construction") float ConstructionTimeHours = 2.f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Construction") TArray<FModuleCost> ConstructionCost;
|
||||
|
||||
// --- Workers ---
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Workers") int32 MaxWorkers = 0; // 0 = no workers needed
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Workers") int32 MinWorkersToOperate = 0; // below this = not working
|
||||
|
||||
// --- Production / Consumption ---
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Economy") TArray<FModuleResourceRate> ProducedResources;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Economy") TArray<FModuleResourceRate> ConsumedResources;
|
||||
|
||||
// --- Storage (for storage modules) ---
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage") float AddedStorageCapacity = 0.f;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NativeGameplayTags.h"
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Flag_Stackable);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Flag_Quest);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Flag_Consumeable);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Flag_Durable);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Flag_Resource);
|
||||
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Belt);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Hands);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Head);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Legs);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Torso);
|
||||
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Weapon_Ranged);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Weapon_Melee);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Weapon_Melee_TwoHanded);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Item_Type_Weapon_Melee_OneHanded);
|
||||
|
||||
|
||||
/*
|
||||
|
||||
3 x 25 marker
|
||||
2 x 25 dude
|
||||
5 - 6 x 32 flameers
|
||||
5 x 32 jumpers
|
||||
5 x 38 terminators
|
||||
6 x 38 inner circle
|
||||
10 x 38 termintator with sword an mace
|
||||
1 x 48 captain in terminator armor
|
||||
1 x 48 azazel
|
||||
1 x 68 big boy
|
||||
1 x 98 rawr
|
||||
1 x 16cm x 11cm tonk
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,16 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOutlinerStatus : uint8
|
||||
{
|
||||
OK, // green - working normally
|
||||
Warning, // yellow - needs attention (low resources, no workers)
|
||||
Critical, // red - not working, under attack, starving
|
||||
Inactive, // grey - built but idle
|
||||
Construction, // blue - being built
|
||||
Player
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ItemActor.h"
|
||||
#include "PlaceableItemActor.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class MAYBETHISTIME_API APlaceableItemActor : public AItemActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<UItemDefinition> DefaultDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
int32 DefaultStackSize = 1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
float DefaultDurability = 1.f;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BaseUnit.h"
|
||||
#include "PlayerUnit.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API APlayerUnit : public ABaseUnit
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
EOutlinerStatus GetOutlinerStatus() const override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "ThirdPersonCharacter.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API AThirdPersonCharacter : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
AThirdPersonCharacter();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Controller.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "ThirdPersonController.generated.h"
|
||||
|
||||
class APlayerUnit;
|
||||
class UUnitManagerComponent;
|
||||
class AZoneActor;
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API AThirdPersonController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
AThirdPersonController();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UUnitManagerComponent* UnitManager;
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) FName PlayerFactionID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite) bool bShiftHeld;
|
||||
|
||||
// Units
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) APlayerUnit* PlayerUnit;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) APlayerUnit* GetPlayerUnit() const { return PlayerUnit; }
|
||||
|
||||
// Selection
|
||||
UPROPERTY(BlueprintReadOnly) TArray<ABaseUnit*> SelectedUnits;
|
||||
UFUNCTION(BlueprintCallable) void SelectUnit(ABaseUnit* Unit, bool bAddToSelection = false);
|
||||
UFUNCTION(BlueprintCallable) void ClearUnitSelection();
|
||||
UFUNCTION(BlueprintCallable) void BoxSelect(TArray<ABaseUnit*> UnitsInBox);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly) TArray<AZoneActor*> SelectedZones;
|
||||
UFUNCTION(BlueprintCallable) void SelectZone(AZoneActor* Zone, bool bAddToSelection = false);
|
||||
UFUNCTION(BlueprintCallable) void ClearZoneSelection();
|
||||
|
||||
UFUNCTION(BlueprintCallable) void ClearMySelection();
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSelectionChanged); // Delegate so UI knows when selection changes
|
||||
UPROPERTY(BlueprintAssignable) FOnSelectionChanged OnSelectionChanged;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) TArray<AZoneActor*> GetOwnedZones() const;
|
||||
UFUNCTION(BlueprintCallable) bool SelectClickedActor(AActor* ClickedActor);
|
||||
|
||||
// Currently hovered interactable
|
||||
UPROPERTY() AActor* HoveredActor;
|
||||
UFUNCTION(BlueprintCallable) void HandleHover();
|
||||
UFUNCTION(BlueprintCallable) void HandleLeftClick();
|
||||
UFUNCTION(BlueprintCallable) void HandleRightClick();
|
||||
|
||||
|
||||
// Replication
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
private:
|
||||
UPROPERTY() AActor* LastHoveredActor;
|
||||
bool IsMouseOverUI() const;
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameMode.h"
|
||||
#include "TopDownGameMode.generated.h"
|
||||
|
||||
class ABaseUnit;
|
||||
class APlayerUnit;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API ATopDownGameMode : public AGameMode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Units") TSubclassOf<APlayerUnit> DefaultPlayerUnitClass;
|
||||
|
||||
UFUNCTION(BlueprintCallable) ABaseUnit* SpawnUnit( TSubclassOf<ABaseUnit> UnitClass, FTransform SpawnTransform, FName FactionID = NAME_None);
|
||||
|
||||
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
void PostLogin(APlayerController* NewPlayer) override;
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameState.h"
|
||||
#include "TopDownGameState.generated.h"
|
||||
|
||||
class UFactionManager;
|
||||
class ABaseUnit;
|
||||
class AZoneActor;
|
||||
|
||||
|
||||
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API ATopDownGameState : public AGameState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
ATopDownGameState();
|
||||
public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UFactionManager* FactionManager;
|
||||
|
||||
// Current game time
|
||||
UPROPERTY(ReplicatedUsing = OnRep_GameTime) float GameHour = 8.f; // start at 8am
|
||||
UPROPERTY(ReplicatedUsing = OnRep_GameTime) int32 GameDay = 1;
|
||||
UPROPERTY(EditDefaultsOnly) float RealSecondsPerGameHour = 5.f;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) FText GetFormattedTime() const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) bool IsNightTime() const;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnGameTimeChanged, float, NewHour, int32, NewDay);
|
||||
UPROPERTY(BlueprintAssignable) FOnGameTimeChanged OnGameTimeChanged;
|
||||
|
||||
// Units
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) TArray<ABaseUnit*> AllUnits;
|
||||
|
||||
void RegisterUnit(ABaseUnit* Unit);
|
||||
void UnregisterUnit(ABaseUnit* Unit);
|
||||
|
||||
void TickAllUnits(float GameHoursDelta);
|
||||
void TickUnitNeeds(ABaseUnit* Unit, float GameHoursDelta);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) TArray<ABaseUnit*> GetUnitsForFaction(FName FactionID) const;
|
||||
|
||||
// Zones
|
||||
UPROPERTY(Replicated, BlueprintReadOnly) TArray<AZoneActor*> AllZones;
|
||||
|
||||
void RegisterZone(AZoneActor* Zone);
|
||||
void UnregisterZone(AZoneActor* Zone);
|
||||
|
||||
AZoneActor* GetZoneAtLocation(FVector Location) const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) TArray<AZoneActor*> GetZonesForFaction(FName FactionID) const;
|
||||
|
||||
// Factions
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) TArray<FFactionData> GetAllFactions() const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) float GetPlayerMoney() const;
|
||||
|
||||
// Delegate so outliner knows when to refresh
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWorldStateChanged);
|
||||
UPROPERTY(BlueprintAssignable, BlueprintCallable) FOnWorldStateChanged OnWorldStateChanged;
|
||||
UFUNCTION(BlueprintCallable) void BroadcastWorldStateChanged();
|
||||
|
||||
void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
private:
|
||||
FTimerHandle GameTickTimerHandle;
|
||||
|
||||
UFUNCTION() void OnGameTick(); // fires every RealSecondsPerGameHour
|
||||
UFUNCTION() void OnRep_GameTime();
|
||||
UFUNCTION() void OnRep_WorldStateVersion();
|
||||
|
||||
UPROPERTY(ReplicatedUsing = OnRep_WorldStateVersion) int32 WorldStateVersion = 0;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "UnitManagerComponent.generated.h"
|
||||
|
||||
|
||||
class ABaseUnit;
|
||||
class UBaseCommand;
|
||||
|
||||
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||
class MAYBETHISTIME_API UUnitManagerComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUnitManagerComponent();
|
||||
UPROPERTY(BlueprintReadOnly) TArray<ABaseUnit*> Squad;
|
||||
|
||||
// Issue commands to Units
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) ABaseUnit* GetFreeUnit();
|
||||
UFUNCTION(BlueprintCallable) void IssueCommandToUnit(UBaseCommand* Command, ABaseUnit* Unit);
|
||||
UFUNCTION(BlueprintCallable) void IssueCommandToUnits(UBaseCommand* Command, TArray<ABaseUnit*> Units);
|
||||
UFUNCTION(BlueprintCallable) void IssueCommandToSquad(UBaseCommand* Command);
|
||||
UFUNCTION(BlueprintCallable) void IssueInteractCommand(AActor* TargetActor, bool bQueue = false);
|
||||
UFUNCTION(BlueprintCallable) void IssueMoveCommand(FVector Location, bool bQueue = false);
|
||||
|
||||
// Change squad
|
||||
UFUNCTION(BlueprintCallable) void AddUnitToSquad(ABaseUnit* Unit);
|
||||
UFUNCTION(BlueprintCallable) void RemoveUnitFromSquad(ABaseUnit* Unit);
|
||||
UFUNCTION(BlueprintCallable) void ClearSquad();
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) TArray<ABaseUnit*> GetSquad();
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "InventoryInterface.h"
|
||||
#include "OutlinerTypes.h"
|
||||
#include "ZoneActor.generated.h"
|
||||
|
||||
class AModuleActor;
|
||||
class UInventoryComponent;
|
||||
class USphereComponent;
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFactionInfluence
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) FName FactionID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) float Influence = 0.f;
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EBuildPermission : uint8
|
||||
{
|
||||
Denied,
|
||||
Allowed,
|
||||
AllowedWithReputation
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FResourcePrice
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FName ResourceID;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float BasePrice = 1.f; // price per unit at neutral reputation
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class MAYBETHISTIME_API AZoneActor : public AActor, public IInventoryInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AZoneActor();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
UPROPERTY(Replicated, BlueprintReadOnly)
|
||||
float ZoneAccount = 10000.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
bool bInfiniteZoneMoney = true; // for now
|
||||
|
||||
// --- Identity ---
|
||||
UPROPERTY(ReplicatedUsing = OnRep_ZoneData, EditAnywhere, BlueprintReadOnly) FName ZoneID;
|
||||
UPROPERTY(ReplicatedUsing = OnRep_ZoneData, EditAnywhere, BlueprintReadOnly) FText ZoneName;
|
||||
UPROPERTY(ReplicatedUsing = OnRep_ZoneData, EditAnywhere, BlueprintReadOnly) FName OwningFaction;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) EOutlinerStatus GetOutlinerStatus() const;
|
||||
|
||||
// --- Geometry ---
|
||||
UPROPERTY(ReplicatedUsing = OnRep_Radius, EditAnywhere, BlueprintReadOnly) float Radius = 2000.f; // unreal units
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) USphereComponent* ZoneVolume;
|
||||
|
||||
|
||||
// --- Modules ---
|
||||
UPROPERTY(Replicated, BlueprintReadWrite) TArray<AModuleActor*> Modules;
|
||||
|
||||
// --- Storage ---
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UInventoryComponent* ZoneStorage;
|
||||
|
||||
// --- Influence ---
|
||||
UPROPERTY(ReplicatedUsing = OnRep_ZoneData, BlueprintReadOnly) TArray<FFactionInfluence> FactionInfluences;
|
||||
|
||||
// --- Delegates ---
|
||||
// Delegate for UI/AI to react to faction changes
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnZoneChanged, FName, ZoneID);
|
||||
UPROPERTY(BlueprintAssignable) FOnZoneChanged OnZoneChanged;
|
||||
|
||||
// --- Module management ---
|
||||
UFUNCTION(BlueprintCallable) bool CanFactionBuildHere(FName FactionID, float Reputation) const;
|
||||
UFUNCTION(BlueprintCallable) void RegisterModule(AModuleActor* Module);
|
||||
UFUNCTION(BlueprintCallable) void UnregisterModule(AModuleActor* Module);
|
||||
UFUNCTION(BlueprintCallable) bool IsLocationInsideZone(FVector Location) const;
|
||||
|
||||
// --- Radius ---
|
||||
UFUNCTION(BlueprintCallable) void SetRadius(float NewRadius);
|
||||
|
||||
// --- Ownership ---
|
||||
UFUNCTION(BlueprintCallable) void SetOwningFaction(FName NewFaction);
|
||||
UFUNCTION(BlueprintCallable) void ModifyInfluence(FName FactionID, float Delta);
|
||||
|
||||
FName GetDominantFaction() const;
|
||||
|
||||
// --- Tick (called by GameState) ---
|
||||
void TickZone(float GameHoursDelta);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly) TArray<FResourcePrice> ResourcePrices;
|
||||
|
||||
// Price scales with reputation - better standing = cheaper
|
||||
float GetPriceForFaction(FName ResourceID, FName FactionID, float Reputation) const;
|
||||
|
||||
bool CanFactionUseResources(FName FactionID, float Reputation) const;
|
||||
|
||||
virtual UInventoryComponent* GetInventoryComponent_Implementation() const override;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void OnRep_ZoneData();
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_Radius();
|
||||
|
||||
void UpdateVolumeSize();
|
||||
void TickModuleProduction(float GameHoursDelta);
|
||||
void TickZoneInfluence(float GameHoursDelta);
|
||||
void SupplyUnitsInZone(float GameHoursDelta);
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class MaybeThisTimeEditorTarget : TargetRules
|
||||
{
|
||||
public MaybeThisTimeEditorTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Editor;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V7;
|
||||
|
||||
ExtraModuleNames.AddRange( new string[] { "MaybeThisTime" } );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user