HAxis sos

This commit is contained in:
guus
2018-08-11 16:46:35 +02:00
commit 510654f8a1
480 changed files with 54126 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "CombatTextText.h"
void UCombatTextText::NativeConstruct()
{
Super::NativeConstruct();
lifeTime = 0;
}
void UCombatTextText::SetText_Implementation(const FString& text)
{
}
void UCombatTextText::SetColor_Implementation(const FLinearColor& color)
{
}

View File

@@ -0,0 +1,29 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "CombatTextText.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UCombatTextText : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct();
UFUNCTION(BlueprintNativeEvent, Category = "CombatText")
void SetText(const FString& text);
UFUNCTION(BlueprintNativeEvent, Category = "CombatText")
void SetColor(const FLinearColor& color);
float lifeTime;
FVector position;
};

View File

@@ -0,0 +1,106 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "CombatTextWidget.h"
#include "CombatTextText.h"
#include "WidgetLayoutLibrary.h"
#define TEXT_LIFETIME 1.0f
void UCombatTextWidget::NativeConstruct()
{
Super::NativeConstruct();
}
void UCombatTextWidget::NativeDestruct()
{
Super::NativeDestruct();
}
void UCombatTextWidget::NativeTick(const FGeometry& geometry, float deltaTime)
{
Super::NativeTick(geometry, deltaTime);
UWorld* const world = GetWorld();
if (!IsValid(world)) return;
APlayerController* const controller = world->GetFirstPlayerController();
if (!IsValid(controller)) return;
const FVector2D screenSize = FVector2D(world->GetGameViewport()->Viewport->GetSizeXY().X, world->GetGameViewport()->Viewport->GetSizeXY().Y);
const float viewportScale = UWidgetLayoutLibrary::GetViewportScale(this);
for (int32 i = 0; i < m_combatTexts.Num();)
{
// Check the lifetime
CombatText& text = m_combatTexts[i];
text.lifeTime += deltaTime;
if (text.lifeTime >= TEXT_LIFETIME)
{
text.textObject->RemoveFromParent();
m_combatTexts.RemoveAt(i);
continue;
}
// Set the color
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(text.textObject->Slot);
if (asSlot)
{
const float progress = text.lifeTime / TEXT_LIFETIME;
text.textObject->SetColor(FLinearColor(text.color.R, text.color.G, text.color.B, text.color.A * (1.0f - progress)));
// Update position
FVector2D position;
if (controller->ProjectWorldLocationToScreen(text.position + FVector(0, 0, progress * 200), position))
{
if (text.arc)
{
position -= FVector2D(progress * text.arcDir.X, FMath::Sin(progress * PI) * text.arcDir.Y) * 100;
asSlot->SetPosition(position / viewportScale);
}
else
asSlot->SetPosition(position / viewportScale);
}
}
i++;
}
}
void UCombatTextWidget::CreateCombatText(const FVector& position, const FString& text, const FLinearColor& color)
{
CombatText combatText(position, text, color, false);
combatText.textObject = m_CreateTextObject(text, color);
m_combatTexts.Add(combatText);
}
void UCombatTextWidget::CreateArcingCombatText(const FVector& position, const FString& text, const FLinearColor& color)
{
CombatText combatText(position, text, color, true);
combatText.arcDir.X = FMath::SRand() * 0.5f + 0.3f;
combatText.arcDir.Y = FMath::SRand() * 0.5f + 0.3f;
if (FMath::Rand() % 2 == 0)
combatText.arcDir.X = -combatText.arcDir.X;
combatText.textObject = m_CreateTextObject(text, color);
m_combatTexts.Add(combatText);
}
UCombatTextText* UCombatTextWidget::m_CreateTextObject(const FString& text, const FLinearColor& color)
{
UCombatTextText* newObject = CreateWidget<UCombatTextText>(GetWorld(), textWidget);
canvas->AddChild(newObject);
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(newObject->Slot);
asSlot->SetSize(FVector2D(1000, 500));
asSlot->SetAlignment(FVector2D(0.5f, 0.05f));
newObject->SetText(text);
newObject->SetColor(color);
return newObject;
}

View File

@@ -0,0 +1,54 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "CombatTextWidget.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UCombatTextWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct();
virtual void NativeDestruct();
virtual void NativeTick(const FGeometry& geometry, float deltaTime) override;
UPROPERTY(EditAnywhere, Category = "CombatText")
TSubclassOf<class UCombatTextText> textWidget;
UPROPERTY(BlueprintReadWrite, Category = "CombatText")
class UCanvasPanel* canvas;
UFUNCTION(BlueprintCallable, Category = "CombatText")
void CreateCombatText(const FVector& position, const FString& text, const FLinearColor& color);
UFUNCTION(BlueprintCallable, Category = "CombatText")
void CreateArcingCombatText(const FVector& position, const FString& text, const FLinearColor& color);
private:
struct CombatText
{
CombatText(const FVector& position, const FString& text, const FLinearColor& color, bool arc) : position(position), text(text), color(color), lifeTime(0), arc(arc) {}
FVector position;
FString text;
FLinearColor color;
float lifeTime;
bool arc;
FVector2D arcDir;
UCombatTextText* textObject;
};
TArray<CombatText> m_combatTexts;
UCombatTextText* m_CreateTextObject(const FString& text, const FLinearColor& color);
};

View File

@@ -0,0 +1,19 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "EventHUD.h"
void UEventHUD::NativeConstruct()
{
Super::NativeConstruct();
}
void UEventHUD::NativeDestruct()
{
Super::NativeDestruct();
}
void UEventHUD::AddEvent_Implementation( const FEventObject& event )
{
}

View File

@@ -0,0 +1,47 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "EventHUD.generated.h"
UENUM(BlueprintType)
enum class EEventType : uint8
{
ET_Kill UMETA(DisplayName = "Kill Event"),
ET_Capture UMETA(DisplayName = "Capture Event"),
ET_KOTH UMETA(DisplayName = "KOTH Event")
};
USTRUCT()
struct FEventObject
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
FText Source;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
FText Target;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Event Struct")
EEventType Type;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Event Struct" )
int32 SourceNum;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Event Struct" )
int32 TargetNum;
};
UCLASS()
class UNREALPROJECT_API UEventHUD : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UI")
void AddEvent( const FEventObject& event );
};

View File

@@ -0,0 +1,19 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "EventWidget.h"
void UEventWidget::NativeConstruct()
{
Super::NativeConstruct();
}
void UEventWidget::NativeDestruct()
{
Super::NativeDestruct();
}
void UEventWidget::SetVariables_Implementation(const FEventObject& event)
{
}

View File

@@ -0,0 +1,18 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "EventHUD.h"
#include "EventWidget.generated.h"
UCLASS()
class UNREALPROJECT_API UEventWidget : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UI")
void SetVariables( const FEventObject& event );
};

View File

@@ -0,0 +1,61 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "AbilityButton.h"
#include "NetworkCharacter.h"
#include "AbilityInfo.h"
#include "AbilityState.h"
void UAbilityButton::NativeConstruct()
{
m_globalEnable = true;
Super::NativeConstruct();
SetCooldown(1.0f);
SetToggled(false);
SetupButton(nullptr, false);
}
UAbilityInfo* UAbilityButton::GetAbility()
{
return m_abilityInfo;
}
void UAbilityButton::InitButton(class UAbilityInfo* info, int32 index, bool alt)
{
if (info)
{
SetCooldown(1.0f);
SetToggled(false);
SetupButton(info, false);
SetIndex(index, alt);
SetAbilityAvailable(true);
m_index = index;
m_abilityInfo = info;
}
}
bool UAbilityButton::IsAssigned() const
{
return m_abilityInfo != nullptr;
}
void UAbilityButton::UpdateState(class AAbilityState* state, ANetworkCharacter* character)
{
SetToggled(state->toggleState);
SetCooldown(state->cooldownRate);
SetAbilityAvailable(character->GetMana() >= state->info->mana);
}
void UAbilityButton::SetButtonEnabled(bool enabled)
{
m_enabled = enabled;
if (m_abilityInfo)
{
SetupButton(m_abilityInfo, m_enabled && m_globalEnable);
}
}
void UAbilityButton::SetGlobalEnable(bool enabled)
{
m_globalEnable = enabled;
SetupButton(m_abilityInfo, m_enabled && m_globalEnable);
}

View File

@@ -0,0 +1,42 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "AbilityButton.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UAbilityButton : public UUserWidget
{
GENERATED_BODY()
public:
void NativeConstruct() override;
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
void SetupButton(class UAbilityInfo* info, bool enabled);
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
void SetCooldown(float cooldown);
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
void SetToggled(bool toggled);
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
void SetIndex(int32 index, bool alt);
UFUNCTION(BlueprintImplementableEvent, Category = "Button")
void SetAbilityAvailable(bool available);
class UAbilityInfo* GetAbility();
void InitButton(class UAbilityInfo* info, int32 index, bool alt);
bool IsAssigned() const;
void UpdateState(class AAbilityState* state, class ANetworkCharacter* character);
void SetButtonEnabled(bool enabled);
void SetGlobalEnable(bool enabled);
private:
int32 m_index;
bool m_enabled;
bool m_globalEnable;
class UAbilityInfo* m_abilityInfo;
};

View File

@@ -0,0 +1,160 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "AbilityButton.h"
#include "ButtonBarSwitcher.h"
#include "BaseSkillObject.h"
#include "AbilityState.h"
#include "NetworkPlayer.h"
#include "IngameSkillTree.h"
#include "AbilityInfo.h"
void UButtonBarSwitcher::NativeConstruct()
{
Super::NativeConstruct();
m_globalEnable = true;
m_alternateSkillSet = false;
m_FindAndAddButtonBar("ButtonBarMain");
m_FindAndAddButtonBar("ButtonBarAlt");
}
bool UButtonBarSwitcher::AssignSkillButton(const FIngameSkillTreeSkill& skill, bool isAlt)
{
if (m_mappedButtons.Find(skill.selectedEffect))
return true;
int32 bar = isAlt ? 1 : 0;
if (bar >= m_buttonBars.Num())
return false;
UAbilityButton* targetButton;
if(!m_buttonBars[bar]->AssignSkillButton(skill, targetButton, isAlt))
{
GERROR("Too many skills, can't assign " + skill.selectedEffect->GetName() + " to bar " + bar);
return false;
}
m_mappedButtons.Add(skill.selectedEffect, targetButton);
return true;
}
void UButtonBarSwitcher::UpdateCooldowns(TArray<class AAbilityState*> abilityStates, ANetworkPlayer* player)
{
for (int32 i = 0; i < abilityStates.Num(); i++)
{
UAbilityButton** button = m_mappedButtons.Find(abilityStates[i]->info);
if (button)
{
(*button)->UpdateState(abilityStates[i], player);
}
}
}
UButtonBar* UButtonBarSwitcher::m_FindAndAddButtonBar(const FName& name)
{
UButtonBar* bar = Cast<UButtonBar>(WidgetTree->FindWidget(name));
if (bar)
{
m_buttonBars.Add(bar);
}
return bar;
}
void UButtonBarSwitcher::SetSkillButtonEnabled(const FIngameSkillTreeSkill& skill, bool enabled)
{
UAbilityButton** button = m_mappedButtons.Find(skill.selectedEffect);
if (button)
{
(*button)->SetButtonEnabled(enabled);
}
}
void UButtonBarSwitcher::SetGlobalEnable(bool enabled)
{
for(UButtonBar* bb : m_buttonBars)
{
bb->SetGlobalEnable(enabled);
}
}
class UAbilityInfo* UButtonBarSwitcher::GetAbilityMapping(int32 index)
{
if(m_buttonBars.Num() != 2)
{
GERROR("m_buttonBars.Num() != 2");
return nullptr;
}
UButtonBar* currentBar = m_buttonBars[m_alternateSkillSet ? 1 : 0];
if(index < 0 || index >= currentBar->buttons.Num())
return nullptr;
return currentBar->buttons[index]->GetAbility();
}
class UAbilityInfo* UButtonBarSwitcher::GetAbilityMapping(int32 index, bool isAlt)
{
if(m_buttonBars.Num() != 2)
{
GERROR("m_buttonBars.Num() != 2");
return nullptr;
}
UButtonBar* currentBar = m_buttonBars[isAlt ? 1 : 0];
if(index < 0 || index >= currentBar->buttons.Num())
return nullptr;
return currentBar->buttons[index]->GetAbility();
}
void UButtonBarSwitcher::TransitionToAlternateSkillSet_Implementation()
{
}
void UButtonBarSwitcher::TransitionToMainSkillSet_Implementation()
{
}
void UButtonBarSwitcher::ToggleSkillSet(bool useAlternateSkillSet)
{
if (m_alternateSkillSet != useAlternateSkillSet)
{
if(useAlternateSkillSet)
TransitionToAlternateSkillSet();
else
TransitionToMainSkillSet();
m_alternateSkillSet = useAlternateSkillSet;
}
}
void UButtonBar::NativeConstruct()
{
TArray<UWidget*> widgets;
WidgetTree->GetAllWidgets(widgets);
for (int32 i = 0; i < widgets.Num(); i++)
{
UAbilityButton* button = Cast<UAbilityButton>(widgets[i]);
if (button)
{
buttons.Add(button);
}
}
}
bool UButtonBar::AssignSkillButton(const FIngameSkillTreeSkill& skill, class UAbilityButton*& buttonOut, bool alt)
{
for (int32 i = 0; i < buttons.Num(); i++)
{
if (buttons[i]->IsAssigned())
continue;
buttonOut = buttons[i];
buttonOut->InitButton(skill.selectedEffect, i, alt);
return true;
}
return false;
}
void UButtonBar::SetGlobalEnable(bool enabled)
{
for(UAbilityButton* button : buttons)
{
if(button)
{
button->SetGlobalEnable(enabled);
}
}
}

View File

@@ -0,0 +1,58 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "ButtonBarSwitcher.generated.h"
UCLASS()
class UNREALPROJECT_API UButtonBar : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
bool AssignSkillButton(const struct FIngameSkillTreeSkill& skill, class UAbilityButton*& buttonOut, bool alt);
void SetGlobalEnable(bool enabled);
UPROPERTY()
TArray<class UAbilityButton*> buttons;
};
UCLASS()
class UNREALPROJECT_API UButtonBarSwitcher : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
bool AssignSkillButton(const struct FIngameSkillTreeSkill& skill, bool isAlt);
void UpdateCooldowns(TArray<class AAbilityState*> abilityStates, class ANetworkPlayer* player);
void SetSkillButtonEnabled(const struct FIngameSkillTreeSkill& skill, bool enabled);
// Set the global enabled button state
void SetGlobalEnable(bool enabled);
class UAbilityInfo* GetAbilityMapping(int32 index);
class UAbilityInfo* GetAbilityMapping(int32 index, bool isAlt);
UFUNCTION(BlueprintNativeEvent, Category="Animation")
void TransitionToAlternateSkillSet();
UFUNCTION(BlueprintNativeEvent, Category="Animation")
void TransitionToMainSkillSet();
void ToggleSkillSet(bool useAlternateSkillSet);
private:
UButtonBar* m_FindAndAddButtonBar(const FName& name);
bool m_globalEnable;
bool m_alternateSkillSet;
UPROPERTY()
TArray<UButtonBar*> m_buttonBars;
UPROPERTY()
TMap<class UAbilityInfo*, UAbilityButton*> m_mappedButtons;
};

View File

@@ -0,0 +1,126 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "HealthBar.h"
#include "StatBar.h"
// 1 = Crown
// 2 = Mini-Boss
// 3 = Dot
// 4 = Camp
// 5 = PlayerIcon
static UTexture2D* barIcons[6];
UHealthBar::UHealthBar(const FObjectInitializer& init) : Super(init)
{
barIcons[0] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconCrown").Object;
barIcons[1] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconMini").Object;
barIcons[2] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconDot").Object;
barIcons[3] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconCamp").Object;
barIcons[4] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/Minimap/T_IconPlayer").Object;
}
void UHealthBar::NativeConstruct()
{
Super::NativeConstruct();
m_currentIcon = (EMinimapIcon)-1;
m_HP = Cast<UStatBar>(WidgetTree->FindWidget("HP"));
m_mana = Cast<UStatBar>(WidgetTree->FindWidget("Mana"));
m_styleSwitcher = Cast<UWidgetSwitcher>(WidgetTree->FindWidget("StyleSwitcher"));
m_HP1 = Cast<UStatBar>(WidgetTree->FindWidget("HP1"));
m_icon = Cast<UImage>(WidgetTree->FindWidget("Icon"));
m_name = Cast<UTextBlock>(WidgetTree->FindWidget("Name"));
SetName("");
SetIcon(EMinimapIcon::None);
if(m_mana)
{
m_mana->SetBarColor(EBarColor::Blue);
}
m_style = 0;
m_friendly = false;
}
void UHealthBar::NativeDestruct()
{
Super::NativeDestruct();
}
void UHealthBar::SetIcon(EMinimapIcon _iconID)
{
if(!m_icon)
return;
if(_iconID == m_currentIcon)
return;
int32 iconID = FMath::Clamp((int32)_iconID, 0, 5);
if(iconID == 0)
m_icon->SetVisibility(ESlateVisibility::Collapsed);
else
{
m_icon->SetBrushFromTexture(barIcons[iconID - 1]);
m_icon->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
}
void UHealthBar::SetStyle(int32 style)
{
if(style == m_style)
return;
m_style = FMath::Clamp(style, 0, 1);
m_styleSwitcher->SetActiveWidgetIndex(m_style);
}
void UHealthBar::UpdateHealth(int32 curr, int32 max)
{
m_updateCount++;
bool showAnimation = m_updateCount > 8;
if(m_style == 0)
{
m_HP->SetStat(curr, max, showAnimation);
}
else
{
m_HP1->SetStat(curr, max, showAnimation);
}
}
void UHealthBar::UpdateMana(int32 curr, int32 max, float blocked)
{
if(m_style == 0)
{
m_mana->SetStat(curr, max, true);
}
}
void UHealthBar::UpdateAlignment(bool friendly)
{
m_friendly = friendly;
const FLinearColor colorFriendly = FLinearColor(0.18f, 0.8f, 0.23f);
const FLinearColor colorEnemy = FLinearColor(0.73f, 0.1f, 0.13f);
if(m_style == 0)
{
m_HP->SetBarColor(friendly ? EBarColor::Green : EBarColor::Red);
}
else
{
m_HP1->SetBarColor(friendly ? EBarColor::Green : EBarColor::Red);
}
}
void UHealthBar::SetName(FString name)
{
if(!m_name)
return;
if(name.IsEmpty())
{
m_name->SetVisibility(ESlateVisibility::Collapsed);
}
else
{
if(name.Len() > 16)
{
name = name.Left(13) + "...";
}
m_name->SetText(FText::FromString(name));
m_name->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
}

View File

@@ -0,0 +1,53 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "HealthBar.generated.h"
UENUM()
enum class EMinimapIcon : uint8
{
None = 0,
Crown,
MiniBoss,
Dot,
Camp,
Player
};
UCLASS()
class UNREALPROJECT_API UHealthBar : public UUserWidget
{
GENERATED_BODY()
public:
UHealthBar(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
// Icon ID's
// 0 = nothing
// 1 = Crown
// 2 = Mini-Boss
// 3 = Dot
// 4 = Camp
// 5 = PlayerIcon
void SetIcon(EMinimapIcon iconID);
void SetStyle(int32 style);
void UpdateHealth(int32 curr, int32 max);
void UpdateMana(int32 curr, int32 max, float blocked = 0.0f);
void UpdateAlignment(bool friendly);
void SetName(FString name);
private:
int32 m_style;
bool m_friendly;
class UWidgetSwitcher* m_styleSwitcher;
class UStatBar* m_HP;
class UStatBar* m_mana;
class UStatBar* m_HP1;
UTextBlock* m_name;
UImage* m_icon;
EMinimapIcon m_currentIcon;
int32 m_updateCount = 0;
};

View File

@@ -0,0 +1,347 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultPlayerController.h"
#include "IngameHUD.h"
#include "AbilityInfo.h"
#include "AbilityState.h"
#include "NetworkPlayer.h"
#include "HealthBar.h"
#include "CreatureSpawn.h"
#include "PlayerSlot.h"
#include "DefaultGameState.h"
#include "DefaultPlayerState.h"
#include "MiniMapWidget.h"
#include "ToolTipWidget.h"
#include "CombatText/CombatTextWidget.h"
#include "SpellInfoDisplay.h"
#include "ButtonBarSwitcher.h"
#include "BaseSkillObject.h"
#include "WidgetLayoutLibrary.h"
#include "NetworkGhost.h"
#include "TouhouBoss.h"
#include "MiniBossCreature.h"
#include "StatBar.h"
#include <algorithm>
static UClass* healthBarWidgetClass;
static UClass* playerSlotWidgetClass;
UIngameHUD::UIngameHUD(const FObjectInitializer& init)
:Super(init)
{
ConstructorHelpers::FClassFinder<UHealthBar> HealthBarWidgetCF(TEXT("/Game/Assets/GUI/WEEGEE_HealthBar"));
healthBarWidgetClass = HealthBarWidgetCF.Class;
miniMap = nullptr;
}
void UIngameHUD::NativeConstruct()
{
m_myTeam = 0;
m_character = nullptr;
Super::NativeConstruct();
m_spellInfoDisplay = Cast<USpellInfoDisplay>(WidgetTree->FindWidget("SpellInfoDisplay"));
miniMap = Cast<UMiniMapWidget>(WidgetTree->FindWidget("Minimap"));
toolTips = Cast<UToolTipWidget>(WidgetTree->FindWidget("ToolTipLayer"));
combatText = Cast<UCombatTextWidget>(WidgetTree->FindWidget("CombatTextLayer"));
buttonBarSwitcher = Cast<UButtonBarSwitcher>(WidgetTree->FindWidget("ButtonBarSwitcher"));
healthBarLayer = Cast<UCanvasPanel>(WidgetTree->FindWidget("HealthBarLayer"));
// Find stat display items
m_playerStatDisplay.hpBar = Cast<UStatBar>(WidgetTree->FindWidget("HP"));
m_playerStatDisplay.manaBar = Cast<UStatBar>(WidgetTree->FindWidget("Mana"));
m_playerStatDisplay.expBar = Cast<UExperienceBar>(WidgetTree->FindWidget("Exp"));
m_allyStatDisplay.hpBar = Cast<UStatBar>(WidgetTree->FindWidget("HP1"));
m_allyStatDisplay.manaBar = Cast<UStatBar>(WidgetTree->FindWidget("Mana1"));
m_allyStatDisplay.name = Cast<UTextBlock>(WidgetTree->FindWidget("Name1"));
m_InitStatDisplay(m_playerStatDisplay);
m_InitStatDisplay(m_allyStatDisplay);
check(healthBarLayer);
}
void UIngameHUD::NativeDestruct()
{
Super::NativeDestruct();
}
void UIngameHUD::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
auto p = m_healthBars.CreateIterator();
while (p)
{
m_UpdateHealthBar(p.Key(), p.Value());
++p;
}
ADefaultPlayerState* ps = Cast<ADefaultPlayerState>(GetOwningPlayer()->PlayerState);
if (IsValid(ps))
{
m_UpdateStatDisplay(ps, m_playerStatDisplay);
m_UpdateStatDisplay(ps->teamMate, m_allyStatDisplay);
}
//UWorld* world = GetWorld();
//check(world);
//ADefaultGameState* state = Cast<ADefaultGameState>(world->GetGameState());
}
void UIngameHUD::NativeOnAssignCharacter(class ANetworkPossessable* pawn /*= nullptr*/, TArray<FIngameSkillTreeSkill> skillsetPrototype /*= TArray<FIngameSkillTreeSkill>()*/)
{
m_character = pawn;
// Cast to ghost or player
ANetworkPlayer* player = Cast<ANetworkPlayer>(pawn);
ANetworkGhost* ghost = Cast<ANetworkGhost>(pawn);
if(ghost)
{
// Grey out buttons in ghost mode
buttonBarSwitcher->SetGlobalEnable(false);
}
else if(player)
{
// Enable buttons back in player mode
buttonBarSwitcher->SetGlobalEnable(true);
// Set skill prototypes
if(buttonBarSwitcher)
{
for(int32 i = 0; i < skillsetPrototype.Num(); i++)
{
// Don't assign buttons for passive skills
if(skillsetPrototype[i].selectedEffect->passive)
continue;
switch(skillsetPrototype[i].abilityType)
{
case 0:
buttonBarSwitcher->AssignSkillButton(skillsetPrototype[i], false);
break;
case 1:
buttonBarSwitcher->AssignSkillButton(skillsetPrototype[i], true);
break;
case 2:
if(skillsetPrototype[i].selectedEffect && !skillsetPrototype[i].selectedEffect->passive)
{
GERROR("Non-passive skill added to skill with shape type == passive, " +
skillsetPrototype[i].skillObject->GetName() + " <= " +
skillsetPrototype[i].selectedEffect->GetName());
}
}
}
}
m_myTeam = player->GetTeam();
}
OnAssignCharacter(pawn);
}
void UIngameHUD::OnLevelUp(TArray<FIngameSkillTreeSkill> updatedSkills)
{
if (m_spellInfoDisplay && GetPlayer())
m_spellInfoDisplay->OnLevelUp(GetPlayer(), updatedSkills);
if (buttonBarSwitcher)
{
for (int32 i = 0; i < updatedSkills.Num(); i++)
{
buttonBarSwitcher->SetSkillButtonEnabled(updatedSkills[i], true);
}
}
}
void UIngameHUD::UpdateCooldowns(TArray<class AAbilityState*> abilityStates, ANetworkPlayer* player)
{
if (buttonBarSwitcher)
buttonBarSwitcher->UpdateCooldowns(abilityStates, player);
}
void UIngameHUD::OnCharacterCreated(ANetworkCharacter* character)
{
if (m_healthBars.Find(character))
{
// When OnCharacterCreated is called twice
GWWARNING(L"OnCharacterCreated called more thatn once");
return;
}
UHealthBar* widget = CreateWidget<UHealthBar>(GetOwningPlayer(), healthBarWidgetClass);
check(widget);
healthBarLayer->AddChild(widget);
m_healthBars.Add(character, widget);
m_UpdateHealthBar(character, widget);
}
void UIngameHUD::OnCharacterDestroyed(ANetworkCharacter* character)
{
auto it = m_healthBars.Find(character);
if (it)
{
(*it)->RemoveFromViewport();
m_healthBars.Remove(character);
}
else
{
// When OnCharacterDestroyed is called twice
GWWARNING(L"OnCharacterDestroyed called more than once");
}
}
ANetworkPlayer* UIngameHUD::GetTeamMate() const
{
if (Cast<ANetworkPlayer>(m_character))
{
ANetworkPlayer* teamMate = Cast<ANetworkPlayer>(m_character)->GetTeamMate();
return teamMate;
}
return nullptr;
}
ANetworkPlayer* UIngameHUD::GetPlayer() const
{
return Cast<ANetworkPlayer>(m_character);
}
ANetworkGhost* UIngameHUD::GetGhost() const
{
return Cast<ANetworkGhost>(m_character);
}
class ADefaultPlayerState* UIngameHUD::GetPlayerState() const
{
if (!m_character)
return nullptr;
return Cast<ADefaultPlayerState>(m_character->PlayerState);
}
void UIngameHUD::m_UpdateHealthBar(class ANetworkCharacter* character, class UHealthBar* bar)
{
APlayerController* pc = GetOwningPlayer();
if (!IsValid(pc)) return;
FVector screenPos;
const float height = character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
const FVector position = character->GetActorLocation();
const FVector worldLocation = FVector(position.X, position.Y, position.Z + height);
// Check if is approximately in camera view
const FVector forward = pc->PlayerCameraManager->GetCameraRotation().RotateVector(FVector(1.0f, 0.0f, 0.0f));
const FVector dir = (worldLocation - pc->PlayerCameraManager->GetCameraLocation()).GetSafeNormal();
float dot = FVector::DotProduct(forward, dir);
if (dot < 0.01f)
{
bar->SetVisibility(ESlateVisibility::Hidden);
return;
}
bar->SetVisibility(ESlateVisibility::Visible);
pc->ProjectWorldLocationToScreenWithDistance(FVector(position.X, position.Y, position.Z + height), screenPos);
const float viewportScale = UWidgetLayoutLibrary::GetViewportScale(healthBarLayer);
uint32 otherTeam = character->GetTeam();
// Check witch bar style to use
bool useExtendedBarStyle = character->playerName.Len() > 0;
if(useExtendedBarStyle)
{
bar->SetStyle(0);
bar->UpdateMana(character->GetMana(), character->GetMaxMana(), character->GetBlockedMana());
bar->SetName(character->playerName);
}
else
{
bar->SetStyle(1);
bar->SetName("");
}
bar->UpdateAlignment(otherTeam == m_myTeam);
bar->UpdateHealth(character->GetHealth(), character->GetMaxHealth());
// Set the icon next to the bar
EMinimapIcon icon = EMinimapIcon::None;
if(character->IsA<ATouhouBoss>())
{
icon = EMinimapIcon::Crown;
}
else if(character->IsA<AMiniBossCreature>())
{
icon = EMinimapIcon::MiniBoss;
}
bar->SetIcon(icon);
// Set the on-screen position
UCanvasPanelSlot* asSlot = Cast<UCanvasPanelSlot>(bar->Slot);
if (IsValid(asSlot))
{
asSlot->SetAutoSize(true);
asSlot->SetAlignment(FVector2D(0.5f, 1));
const FVector2D setPos = FVector2D(screenPos.X, screenPos.Y) / viewportScale;
asSlot->SetPosition(FVector2D(FPlatformMath::RoundToFloat(setPos.X), FPlatformMath::RoundToFloat(setPos.Y)));
}
}
void UIngameHUD::m_UpdateStatDisplay(class ADefaultPlayerState* state, const FStatDisplay& sd)
{
ANetworkPlayer* player = state ? state->character : nullptr;
if(sd.hpBar)
{
if(!player)
sd.hpBar->SetStat(0.0f, true);
else
{
sd.hpBar->SetStat(player->GetHealth(), player->GetMaxHealth(), true);
}
}
if(sd.manaBar)
{
if(!player)
{
sd.manaBar->SetBlocked(0.0f);
sd.manaBar->SetStat(0, 1, true);
}
else
{
sd.manaBar->SetStat(player->GetMana(), player->GetMaxMana(), true);
sd.manaBar->SetBlocked((float)player->GetBlockedMana() / (float)player->GetMaxMana());
}
}
if(sd.expBar)
{
if(state)
{
if(state->GetLevel() == state->GetMaxLevel())
sd.expBar->Update(1.0f, state->GetMaxLevel(), true);
else
sd.expBar->Update((float)state->GetExperience() / (float)state->GetExperienceToLevel(), state->GetLevel(), false);
}
else
{
sd.expBar->Update(0.0f, 0, false);
}
}
if(sd.name)
{
if(state)
{
state->UpdatePersona();
sd.name->SetText(FText::FromString(state->nickname));
}
else
{
sd.name->SetText(FText());
}
}
if(sd.avatar && state)
{
sd.avatar->SetBrushFromTexture(state->avatar);
}
}
void UIngameHUD::m_InitStatDisplay(const FStatDisplay& sd)
{
if(sd.hpBar)
{
sd.hpBar->SetBarColor(EBarColor::GreenGradient);
}
if(sd.manaBar)
{
sd.manaBar->SetBarColor(EBarColor::BlueGradient);
}
}

View File

@@ -0,0 +1,93 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "IngameSkillTree.h"
#include <vector>
#include <map>
#include "IngameHUD.generated.h"
UCLASS()
class UExperienceBar : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent)
void Update(float rate, int32 level, bool max);
};
struct FStatDisplay
{
class UStatBar* hpBar;
class UStatBar* manaBar;
class UExperienceBar* expBar;
class UImage* avatar;
class UTextBlock* name;
};
UCLASS()
class UNREALPROJECT_API UIngameHUD : public UUserWidget
{
GENERATED_BODY()
public:
UIngameHUD(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
// Sets the player owned character
void NativeOnAssignCharacter(class ANetworkPossessable* pawn = nullptr, TArray<FIngameSkillTreeSkill> skillsetPrototype = TArray<FIngameSkillTreeSkill>());
UFUNCTION(BlueprintImplementableEvent, Category = "HUD")
void OnAssignCharacter(ANetworkPossessable* pawn);
// Called when the character levels up
void OnLevelUp(TArray<FIngameSkillTreeSkill> updatedSkills);
// Updates the displayed cooldowns in the UI with the provided ability states
void UpdateCooldowns(TArray<class AAbilityState*> abilityStates, class ANetworkPlayer* player);
// Called when a character spawns
// this makes it display a health-bar in the HUD
void OnCharacterCreated(class ANetworkCharacter* character);
// Called when a character despawns(destroyed, killed)
// this removes all the health-bars, etc. for this character
void OnCharacterDestroyed(class ANetworkCharacter* character);
UFUNCTION(BlueprintCallable, Category = "UI")
class ANetworkPlayer* GetTeamMate() const;
UFUNCTION(BlueprintCallable, Category = "UI")
class ANetworkPlayer* GetPlayer() const;
UFUNCTION(BlueprintCallable, Category = "UI")
class ANetworkGhost* GetGhost() const;
UFUNCTION(BlueprintCallable, Category = "UI")
class ADefaultPlayerState* GetPlayerState() const;
class UMiniMapWidget* miniMap;
class UToolTipWidget* toolTips;
class UCombatTextWidget* combatText;
class UButtonBarSwitcher* buttonBarSwitcher;
class UCanvasPanel* healthBarLayer;
private:
// This updates an on screen health bar for a given network character
void m_UpdateHealthBar(class ANetworkCharacter* character, class UHealthBar* bar);
void m_UpdateStatDisplay(class ADefaultPlayerState* player, const FStatDisplay& statDisplay);
void m_InitStatDisplay(const FStatDisplay& statDisplay);
FStatDisplay m_playerStatDisplay;
FStatDisplay m_allyStatDisplay;
// Maps every character in the game to a health bar widget on-screen
UPROPERTY()
TMap<class ANetworkCharacter*, class UHealthBar*> m_healthBars;
UPROPERTY()
class ANetworkPossessable* m_character;
UPROPERTY()
class USpellInfoDisplay* m_spellInfoDisplay;
uint32 m_myTeam;
};

View File

@@ -0,0 +1,13 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "KOTHHUD.h"
void UKOTHHUD::NativeConstruct()
{
Super::NativeConstruct();
}
void UKOTHHUD::Update(TArray<FKOTHTeamState> teamStates)
{
}

View File

@@ -0,0 +1,16 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "KOTHTeamState.h"
#include "KOTHHUD.generated.h"
UCLASS()
class UNREALPROJECT_API UKOTHHUD : public UUserWidget
{
GENERATED_BODY()
public:
void NativeConstruct() override;
void Update(TArray<FKOTHTeamState> teamStates);
};

View File

@@ -0,0 +1,14 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "KeyDisplay.h"
void UKeyDisplay::NativeConstruct()
{
Super::NativeConstruct();
}
void UKeyDisplay::NativeDestruct()
{
Super::NativeDestruct();
}

View File

@@ -0,0 +1,16 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "KeyDisplay.generated.h"
UCLASS()
class UNREALPROJECT_API UKeyDisplay : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
};

View File

@@ -0,0 +1,192 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "StatBar.h"
static UTexture2D* barTextures[6];
static FLinearColor barColors[4];
static UMaterial* textureBarMaterialClass;
UStatBar::UStatBar(const FObjectInitializer& init) : Super(init)
{
textureBarMaterialClass = ConstructorHelpers::FObjectFinder<UMaterial>(L"/Game/Assets/Art/UI/M_HealthBar2.M_HealthBar2").Object;
barColors[0] = FLinearColor::Green * 0.5f;
barColors[1] = FLinearColor::Red * 0.6f;
barColors[2] = FLinearColor::Blue * 0.8f;
barColors[3] = FLinearColor(1.0f, 0.0f, 1.0f, 1.0f);
barTextures[0] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarHealth").Object;
barTextures[1] = ConstructorHelpers::FObjectFinder<UTexture2D>(L"/Game/Assets/Art/UI/HealthBar/UI_MainStatusBarMana").Object;
m_lastValue = 1.0f;
m_currentValue = 1.0f;
damageAnimationDuration = 0.3f;
m_damageAnimation = 0.0f;
showText = false;
}
void UStatBar::NativeConstruct()
{
Super::NativeConstruct();
m_base = Cast<UImage>(WidgetTree->FindWidget("Base"));
m_blocked = Cast<UImage>(WidgetTree->FindWidget("Blocked"));
m_damage = Cast<UImage>(WidgetTree->FindWidget("Damage"));
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("BarText"));
if(m_damage)
{
m_damage->SetVisibility(ESlateVisibility::Hidden);
UMaterialInstanceDynamic* damageMat = m_damage->GetDynamicMaterial();
damageMat->SetScalarParameterValue("Brightness", 4.0f);
}
if(m_blocked)
m_blocked->SetVisibility(ESlateVisibility::Hidden);
}
#pragma optimize("", off)
void UStatBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(m_damageAnimation > 0.0f && m_damage)
{
float r = m_damageAnimation / damageAnimationDuration;
float damageSize = (m_currentValue - m_lastValue);
UMaterialInstanceDynamic* matDamage = m_damage->GetDynamicMaterial();
if(damageSize < 0.0f)
{
damageSize *= (1.0f - r);
matDamage->SetScalarParameterValue("Start", m_currentValue);
matDamage->SetScalarParameterValue("End", m_lastValue + damageSize);
}
else
{
damageSize *= (1.0f-r);
matDamage->SetScalarParameterValue("Start", m_lastValue + damageSize);
matDamage->SetScalarParameterValue("End", m_currentValue);
}
m_damageAnimation -= InDeltaTime;
if(m_damageAnimation <= 0.0f)
{
m_damageAnimation = 0.0f;
m_damage->SetVisibility(ESlateVisibility::Hidden);
}
}
}
#pragma optimize("", off)
void UStatBar::SetStat(float newStat, bool applyAnimation)
{
if(!m_base)
return;
if(newStat == m_currentValue)
return;
float delta = newStat - m_currentValue;
if(delta != 0 && m_damageAnimation > 0.0f)
{
// Append animation
float r = m_damageAnimation / damageAnimationDuration;
float damageSize = (m_currentValue - m_lastValue);
if(damageSize < 0.0f)
damageSize *= (1.0f - r);
else
damageSize *= (1.0f - r);
delta += damageSize;
m_lastValue = m_lastValue + damageSize;
}
else
{
m_lastValue = m_currentValue;
}
m_currentValue = newStat;
// Apply animation
if(delta != 0.0f && applyAnimation && m_damage)
{
const FLinearColor deltaColor = FLinearColor(1.0f, 0.0f, 0.0f, 1.0f);
m_damageAnimation = damageAnimationDuration;
UMaterialInstanceDynamic* matDamage = m_base->GetDynamicMaterial();
if(delta < 0.0f)
{
matDamage->SetScalarParameterValue("Start", newStat);
matDamage->SetScalarParameterValue("End", m_lastValue);
}
else
{
matDamage->SetScalarParameterValue("Start", m_lastValue);
matDamage->SetScalarParameterValue("End", newStat);
}
m_damage->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
UMaterialInstanceDynamic* matBase = m_base->GetDynamicMaterial();
matBase->SetScalarParameterValue("Start", 0.0f);
matBase->SetScalarParameterValue("End", newStat);
}
void UStatBar::SetStat(int32 newStat, int32 max, bool applyAnimation)
{
if(showText && m_text)
{
FText t = FText::FromString(FString::Printf(L"%d / %d", newStat, max));
m_text->SetText(t);
m_text->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
{
m_text->SetVisibility(ESlateVisibility::Hidden);
}
float r = (float)newStat / (float)max;
SetStat(r, applyAnimation);
}
void UStatBar::SetBlocked(float newBlocked)
{
if(!m_blocked)
return;
UMaterialInstanceDynamic* mat = m_blocked->GetDynamicMaterial();
if(newBlocked > 0.0f)
{
mat->SetScalarParameterValue("Start", 1.0f - newBlocked);
mat->SetScalarParameterValue("End", 1.0f);
m_blocked->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
{
m_blocked->SetVisibility(ESlateVisibility::Hidden);
}
}
void UStatBar::SetBarColor(EBarColor _colorID)
{
int32 colorID = (int32)_colorID;
if(!m_base)
return;
if((colorID & 0x80) != 0)
{
colorID = colorID & 0x7F;
colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barTextures) / sizeof(UTexture2D*)));
m_base->SetBrushFromMaterial(textureBarMaterialClass);
m_blocked->SetBrushFromMaterial(textureBarMaterialClass);
m_damage->SetBrushFromMaterial(textureBarMaterialClass);
UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
baseMat = m_damage->GetDynamicMaterial();
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
baseMat = m_blocked->GetDynamicMaterial();
baseMat->SetTextureParameterValue("Texture", barTextures[colorID]);
}
else
{
colorID = FMath::Clamp(colorID, 0, (int32)(sizeof(barColors) / sizeof(FLinearColor)));
UMaterialInstanceDynamic* baseMat = m_base->GetDynamicMaterial();
baseMat->SetVectorParameterValue("Color", barColors[colorID]);
}
}

View File

@@ -0,0 +1,49 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "StatBar.generated.h"
enum class EBarColor
{
Green = 0,
Red,
Blue,
Purple,
GreenGradient = 0x80,
BlueGradient
};
/**
*
*/
UCLASS()
class UNREALPROJECT_API UStatBar : public UUserWidget
{
GENERATED_BODY()
public:
UStatBar(const FObjectInitializer& init);
void NativeConstruct() override;
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
void SetStat(int32 newStat, int32 max, bool applyAnimation);
void SetStat(float newStat, bool applyAnimation);
void SetBlocked(float newBlocked);
void SetBarColor(EBarColor colorID);
UPROPERTY(EditDefaultsOnly, Category = "Stat Bar")
float damageAnimationDuration;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stat Bar")
bool showText;
private:
float m_damageAnimation;
float m_lastValue;
float m_currentValue;
UImage* m_base;
UImage* m_blocked;
UImage* m_damage;
UTextBlock* m_text;
};

View File

@@ -0,0 +1,49 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "Timer.h"
#include "Kismet/KismetStringLibrary.h"
void UTimer::NativeConstruct()
{
m_duration = 0.0f;
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
m_timerText = Cast<UTextBlock>(WidgetTree->FindWidget("Time"));
m_clock = Cast<UImage>(WidgetTree->FindWidget("Klokje"));
if(!m_text)
GWARNING("No \"Text\" text widget found in " + GetName());
if(!m_text)
GWARNING("No \"Time\" text widget found in " + GetName());
if(!m_clock)
GWARNING("No \"Klokje\" image widget found in " + GetName());
Super::NativeConstruct();
}
void UTimer::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
m_time = FMath::Clamp(m_time - InDeltaTime, 0.0f, m_duration);
if(m_duration == 0.0f)
OnSetTimer(0.0f);
else
OnSetTimer(m_time / m_duration);
if(m_timerText)
{
FString msg = UKismetStringLibrary::TimeSecondsToString(m_time);
m_timerText->SetText(FText::FromString(msg));
}
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UTimer::SetTimer(float duration, bool reset)
{
m_duration = duration;
if(reset)
m_time = duration;
}
void UTimer::SetText(const FString& text)
{
if(m_text)
m_text->SetText(FText::FromString(text));
}

View File

@@ -0,0 +1,44 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "Timer.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UTimer : public UUserWidget
{
GENERATED_BODY()
public:
// Calbacks handling timer changes
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
void OnSetTimer(float rate);
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
void OnHide(); // Play animation
UFUNCTION(BlueprintImplementableEvent, Category = "HUDTimer")
void OnShow(); // Play animation
void NativeConstruct();
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
void SetTimer(float duration, bool reset = true);
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
void SetText(const FString& text);
UFUNCTION(BlueprintCallable, Category = "HUDTimer")
float GetTime() const
{
return m_time;
}
private:
float m_time;
float m_duration;
UImage* m_clock;
UTextBlock* m_text;
UTextBlock* m_timerText;
};

View File

@@ -0,0 +1,285 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameInstance.h"
#include "MenuController.h"
#include "LobbySpawn.h"
#include "GameStateBase.h"
#include "PlayerStateBase.h"
#include "PlayerControllerBase.h"
#include "CharacterBase.h"
#include "LobbyCharacterSelect.h"
#include "MenuGameMode.h"
void ULobbyCharacterSelect::m_OnPlayerJoined(APlayerController* pc)
{
for(auto it = m_visualCharacters.CreateIterator(); it; ++it)
{
(it.Value()).character->SendInitialAppearance(pc);
}
}
void ULobbyCharacterSelect::NativeConstruct()
{
Super::NativeConstruct();
// Add GameMode callback
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
if(gm)
{
m_onPlayerJoinedDH = gm->onPlayerJoined.AddUObject(this, &ULobbyCharacterSelect::m_OnPlayerJoined);
}
UWorld* const world = GetWorld();
for (TActorIterator<ALobbySpawn> iter(world); iter; ++iter)
{
TArray<ALobbySpawn*>* find = m_spawnPoints.Find(iter->assignedTeam);
if (!find)
{
TArray<ALobbySpawn*> arr;
arr.Add(*iter);
m_spawnPoints.Add(iter->assignedTeam, arr);
}
else
find->Add(*iter);
}
m_requireUpdate = false;
m_updateInterval = 0;
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
gameState->OnPlayerStateChange.AddDynamic(this, &ULobbyCharacterSelect::UpdateVisualCharacters);
}
void ULobbyCharacterSelect::NativeDestruct()
{
Super::NativeDestruct();
// Remove GameMode Callback
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
if(gm)
{
gm->onPlayerJoined.Remove(m_onPlayerJoinedDH);
}
UWorld* const world = GetWorld();
check(world);
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
gameState->OnPlayerStateChange.RemoveAll(this);
}
void ULobbyCharacterSelect::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
// The updating is called once per frame
// This is to prevent double calls when two players leave in one cycle
// Ensure that only the server runs this
if (!GetOwningPlayer()->HasAuthority())
return;
// No need to update this frame
if (!m_requireUpdate)
return;
// Await the interval
m_updateInterval -= InDeltaTime;
if (m_updateInterval > 0)
return;
m_requireUpdate = false;
m_updateInterval = 0.3f;
UWorld* world = GetWorld();
check(world);
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
TArray<int32> teamSizes = gameState->GetTeamSizes();
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
// Check if players have left
TMap<APlayerStateBase*, VisualSlot> outdatedCharacters = m_visualCharacters;
TArray<APlayerStateBase*> players = gameState->GetPlayers();
for (int32 i = 0; i < players.Num(); i++)
{
VisualSlot* find = outdatedCharacters.Find(players[i]);
if (find)
outdatedCharacters.Remove(players[i]);
}
for (auto iter = outdatedCharacters.CreateIterator(); iter; ++iter)
{
if (IsValid(iter->Value.character))
iter->Value.character->Destroy();
m_visualCharacters.Remove(iter->Key);
}
// Check if players switched team
TMap<APlayerStateBase*, ACharacterBase*> newCharacters;
TMap<APlayerStateBase*, VisualSlot> newMap;
for (int32 i = 1; i < playersPerTeam.Num(); i++)
{
auto& set = playersPerTeam[i];
for (int32 j = 0; j < set.Num(); j++)
{
APlayerStateBase* playerState = set[j];
VisualSlot* find = m_visualCharacters.Find(playerState);
const int32 team = i - 1;
if (!find || find->team != team || find->slot != j)
{
// Fetch the spawn coordinates if the spawnpoint exists
FVector position;
FRotator rotation;
if (team < m_spawnPoints.Num() && j < m_spawnPoints[team].Num())
{
position = m_spawnPoints[team][j]->GetActorLocation();
rotation = m_spawnPoints[team][j]->GetActorRotation();
}
// Check if we need to create a new character, else we just repurpose the previous character (like when the player switched slot)
ACharacterBase* character;
if (!find || !IsValid(find->character))
{
FActorSpawnParameters params;
params.bNoFail = true;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
character = GetWorld()->SpawnActor<ACharacterBase>(characterBlueprint, FTransform::Identity, params);
if (!IsValid(character))
{
JERROR("Failed to spawn Actor");
return;
}
}
else
{
find->character->ClearEquipment();
character = find->character;
}
// Set and add
character->SetActorLocation(position);
character->SetActorRotation(rotation.Quaternion());
newMap.Add(playerState, VisualSlot(team, j, character));
newCharacters.Add(playerState, character);
}
else
newMap.Add(playerState, *find);
}
}
m_visualCharacters = newMap;
// Apply the customizations
TMap<APlayerControllerBase*, APlayerStateBase*> controllers = gameState->GetPlayersByController();
for (auto iter = controllers.CreateIterator(); iter; ++iter)
{
ACharacterBase** character = newCharacters.Find(iter->Value);
if (!character || !IsValid(*character)) continue;
(*character)->SetCustomizations(iter->Key->setupState.customizations);
(*character)->EquipSkillsFromSkillTreeState(iter->Key->setupState.skills);
// Set class specific equipment
if(characterClassProperties)
{
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(iter->Key->setupState.characterClass);
(*character)->EquipItems(properties.classItems);
}
}
}
void ULobbyCharacterSelect::Init()
{
UWorld* const world = GetWorld();
check(world);
UDefaultGameInstance* const instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
check(instance);
UCharacterSettings* const settings = instance->GetCharacterSettings();
check(settings);
// Fetch the characters from the save
check(settings->characterSaves.Num() > 0);
m_playerCharacterSaves = settings->GetValidCharacters();
m_selection = 0;
m_ApplyCharacter();
}
void ULobbyCharacterSelect::OnNextCharacter()
{
if (m_selection == m_playerCharacterSaves.Num() - 1)
return;
m_selection++;
m_ApplyCharacter();
}
void ULobbyCharacterSelect::OnPreviousCharacter()
{
if (m_selection == 0)
return;
m_selection--;
m_ApplyCharacter();
}
void ULobbyCharacterSelect::UpdateCharacter_Implementation(FCharacterSave save)
{
// Blueprint implementation
}
void ULobbyCharacterSelect::m_ApplyCharacter()
{
UpdateCharacter(m_playerCharacterSaves[m_selection]);
// Apply the selected character to the player controller
UWorld* const world = GetWorld();
check(world);
AMenuController* const controller = Cast<AMenuController>(world->GetFirstPlayerController());
check(controller);
FPlayerSetupState state;
state.skills = m_playerCharacterSaves[m_selection].skillTreeState;
state.customizations = m_playerCharacterSaves[m_selection].characterCustomization;
state.characterClass = m_playerCharacterSaves[m_selection].characterClass;
controller->SetSetupState(state);
}
void ULobbyCharacterSelect::UpdateVisualCharacters(APlayerControllerBase* playerController)
{
if (!IsValid(characterBlueprint))
{
JERROR("Character blueprint not assigned");
return;
}
if (!IsValid(playerController) && playerController != nullptr)
return;
// Ensure that only the server runs this
if (!GetOwningPlayer()->HasAuthority())
return;
if (playerController != nullptr)
{
// Just update the visuals on the specific player
APlayerStateBase* playerState = Cast<APlayerStateBase>(playerController->PlayerState);
if (IsValid(playerState))
{
VisualSlot* slot = m_visualCharacters.Find(playerState);
if (slot && IsValid(slot->character))
{
slot->character->SetCustomizations(playerController->setupState.customizations);
slot->character->ClearEquipment();
slot->character->EquipSkillsFromSkillTreeState(playerController->setupState.skills);
// Set class specific equipment
if(characterClassProperties)
{
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(playerController->setupState.characterClass);
slot->character->EquipItems(properties.classItems);
}
}
else // Failed to find the character, refresh all
m_requireUpdate = true;
}
}
else
m_requireUpdate = true;
}

View File

@@ -0,0 +1,65 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/SubMenu.h"
#include "CharacterSettings.h"
#include "LobbyCharacterSelect.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API ULobbyCharacterSelect : public USubMenu
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "Lobby")
void Init();
// On next/previous player character
UFUNCTION(BlueprintCallable, Category = "Lobby")
void OnNextCharacter();
UFUNCTION(BlueprintCallable, Category = "Lobby")
void OnPreviousCharacter();
// Called to set the name of the selected character
UFUNCTION(BlueprintNativeEvent, Category = "Lobby")
void UpdateCharacter(FCharacterSave save);
// Called to update all visual characters in the lobby (specify a controller if one player only requires a visual update, or nullptr if all characters need to be updated)
UFUNCTION(BlueprintCallable, Category = "Lobby")
void UpdateVisualCharacters(class APlayerControllerBase* playerController);
UPROPERTY(EditAnywhere, Category = "Lobby")
TSubclassOf<class ACharacterBase> characterBlueprint;
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Character Sub Menu")
class UCharacterClassPropertySet* characterClassProperties;
private:
void m_OnPlayerJoined(APlayerController* pc);
FDelegateHandle m_onPlayerJoinedDH;
TArray<FCharacterSave> m_playerCharacterSaves;
int32 m_selection;
TMap<int32, TArray<ALobbySpawn*>> m_spawnPoints;
struct VisualSlot
{
VisualSlot(int32 team, int32 slot, ACharacterBase* character) : team(team), slot(slot), character(character) {}
int32 team;
int32 slot;
ACharacterBase* character;
};
TMap<APlayerStateBase*, VisualSlot> m_visualCharacters;
void m_ApplyCharacter();
bool m_requireUpdate;
float m_updateInterval;
};

View File

@@ -0,0 +1,204 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "LobbyMenu.h"
#include "LobbySlot.h"
#include "DefaultPlayer.h"
#include "PlayerStateBase.h"
#include "MenuController.h"
#include "DefaultGameInstance.h"
#include "MenuGameMode.h"
#include "GameStateBase.h"
#include "SubMenu.h"
#include "SessionManager.h"
#include "Engine.h"
#include "ScreenOverlay.h"
#include "CharacterSettings.h"
ULobbyMenu::ULobbyMenu(const FObjectInitializer& init)
: Super(init)
{
}
void ULobbyMenu::NativeConstruct()
{
m_teamContainer = Cast<USubMenu>(WidgetTree->FindWidget("Container"));
Super::NativeConstruct();
}
void ULobbyMenu::NativeDestruct()
{
Super::NativeDestruct();
}
void ULobbyMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(!m_myState)
{
// Try to aquire my local player state, this is in Tick because player states are not guaranteed during BeginPlay
UWorld* const world = GetWorld();
check(world);
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
check(player);
m_myState = Cast<APlayerStateBase>(player->PlayerState);
}
UWorld* world = GetWorld();
check(world);
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
TArray<int32> teamSizes = gameState->GetTeamSizes();
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
// Update team slots with their respective player arrays
for (int32 i = 0; i < (int32)m_numTeams; i++)
{
if((i + 1) < playersPerTeam.Num())
{
m_lobbySlots[i]->UpdatePlayers(playersPerTeam[i+1]);
}
else
m_lobbySlots[i]->UpdatePlayers(TArray<APlayerStateBase*>());
if (i > teamSizes.Num())
{
m_lobbySlots[i]->SetState(false);
}
else
{
m_lobbySlots[i]->SetState(true);
}
}
}
void ULobbyMenu::Init()
{
// Generate the selectable builds
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
check(instance);
UCharacterSettings* settings = instance->GetCharacterSettings();
check(settings);
m_characters = settings->characterSaves;
// Remove existing lobby slots
for(ULobbySlot* s : m_lobbySlots)
{
s->RemoveFromParent();
}
m_lobbySlots.SetNum(0);
if(!m_teamContainer)
{
GWERROR("Container not set, can't initialize lobby menu");
return;
}
UPanelWidget* panel = Cast<UPanelWidget>(m_teamContainer->WidgetTree->RootWidget);
if (!panel)
{
GWERROR(L"No UPanelWidget found");
return;
}
// Create team slots in this menu for the amount of selected teams in the game setup screen
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
m_numTeams = gameState->GetMapTeamCount();
for (uint32 i = 0; i < m_numTeams; i++)
{
ULobbySlot* slot = CreateWidget<ULobbySlot>(GetWorld(), lobbySlotClass);
check(slot);
panel->AddChild(slot);
slot->SetTeamIndex(i+1);
m_lobbySlots.Add(slot);
}
m_teamContainer->RescanItems();
}
void ULobbyMenu::ToggleReadyState()
{
// Toggle my player state's ready state
if(!m_myState)
{
UWorld* const world = GetWorld();
check(world);
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
check(player);
m_myState = Cast<APlayerStateBase>(player->PlayerState);
}
if(m_myState)
m_myState->SetReadyState(!m_myState->GetReadyState());
}
void ULobbyMenu::StartGame()
{
UWorld* world = GetWorld();
check(world);
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
check(gameState);
TArray<APlayerStateBase*> players = gameState->GetPlayers<APlayerStateBase>();
// Check if we're the host
AMenuGameMode* gameMode = world->GetAuthGameMode<AMenuGameMode>();
if(gameMode)
{
// Check if everyone's ready
for (size_t i = 0; i < players.Num(); i++)
{
if (!players[i]->GetReadyState())
return;
}
// Tell the game to start
GPRINT("Starting game");
if(!gameMode->StartGame())
{
GERROR("Failed to start game");
TArray<FString> options;
options.Add("OK");
Cast<APlayerControllerBase>(GetOwningPlayer())->overlay->ShowMessageBox("Error", "Failed to start game", options);
}
}
}
void ULobbyMenu::GotoTeamSelector()
{
if (m_teamContainer)
{
OpenSubMenu(m_teamContainer);
}
}
void ULobbyMenu::BackToMenu()
{
// Close active session
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
inst->sessionManager->DestroySession();
inst->sessionManager->CloseNetConnections();
// Goto menu
AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
mc->SwitchToMenu();
}
bool ULobbyMenu::IsHost() const
{
if (!GetOwningPlayer())
return false;
return GetOwningPlayer()->Role == ROLE_Authority;
}
void ULobbyMenu::OnLobbyEnter()
{
// Register session callbacks
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
Init();
}
TArray<FCharacterSave> ULobbyMenu::GetCharacterSaves()
{
return m_characters;
}

View File

@@ -0,0 +1,63 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreenBase.h"
#include "CharacterSettings.h"
#include "LobbyMenu.generated.h"
UCLASS()
class UNREALPROJECT_API ULobbyMenu : public UMenuScreenBase
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
// Must be called from blueprint to setup this element
void Init();
UFUNCTION(BlueprintCallable, Category = "Ready")
void ToggleReadyState();
UFUNCTION(BlueprintCallable, Category = "Ready")
void StartGame();
UFUNCTION(BlueprintCallable, Category = "Ready")
void GotoTeamSelector();
UFUNCTION(BlueprintCallable, Category = "Game")
void BackToMenu();
UFUNCTION(BlueprintCallable, Category = "Game")
bool IsHost() const;
// Called when the local player enters the lobby menu & state
void OnLobbyEnter();
// When this menu needs to be hidden
UFUNCTION(BlueprintImplementableEvent)
void OnShow();
// When this menu needs to be shown
UFUNCTION(BlueprintImplementableEvent)
void OnHide();
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
TSubclassOf<class ULobbySlot> lobbySlotClass;
UFUNCTION(BlueprintCallable, Category = "Lobby")
TArray<FCharacterSave> GetCharacterSaves();
private:
ULobbyMenu(const FObjectInitializer& init);
UPROPERTY()
class APlayerStateBase* m_myState;
UPROPERTY()
TArray<class ULobbySlot*> m_lobbySlots;
UPROPERTY()
class USubMenu* m_teamContainer;
uint32 m_numTeams;
TArray<FCharacterSave> m_characters;
};

View File

@@ -0,0 +1,4 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "LobbyPlayerSlot.h"

View File

@@ -0,0 +1,16 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/PlayerSlot.h"
#include "PlayerSlot.h"
#include "LobbyPlayerSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API ULobbyPlayerSlot : public UPlayerSlot
{
GENERATED_BODY()
};

View File

@@ -0,0 +1,125 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "LobbySlot.h"
#include "LobbyPlayerSlot.h"
#include "PlayerStateBase.h"
#include "PlayerControllerBase.h"
#include "SubMenu.h"
#include "MenuScreenBase.h"
#include "GameStateBase.h"
ULobbySlot::ULobbySlot(const FObjectInitializer& init)
: Super(init)
{
}
void ULobbySlot::NativeConstruct()
{
Super::NativeConstruct();
m_open = false;
}
void ULobbySlot::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
this->bIsEnabled = m_open;
}
void ULobbySlot::SetTeamIndex(uint32 idx)
{
m_teamIndex = idx;
m_UpdateTeamTitle();
}
void ULobbySlot::m_UpdateTeamTitle()
{
// Set the title of this team slot
if (m_teamName)
{
std::wstring teamName = std::wstring() + L"Team " + m_teamIndex + L" (" + m_players.Num() + L"/2)";
SetButtonText(FString(teamName.c_str()));
}
}
void ULobbySlot::UpdatePlayers(TArray<APlayerStateBase*> players)
{
check(m_playerList);
check(m_playerSlots.Num() == 2);
UWorld* const world = GetWorld();
check(world);
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
check(player);
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
// Set all the player slots in this team to show their respective player / no player placeholder
m_players = players;
for (int32 i = 0; i < 2; i++)
{
if (i < m_players.Num())
{
APlayerStateBase* player = players[i];
m_playerSlots[i]->Init(player, player == myState);
}
else
{
m_playerSlots[i]->Init(nullptr);
}
}
m_UpdateTeamTitle();
}
bool ULobbySlot::OnSlotSelected()
{
UWorld* const world = GetWorld();
check(world);
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
check(player);
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
if (myState->GetReadyState())
return false;
if (gameState)
{
auto pbt = gameState->GetPlayersByTeam();
if (m_teamIndex < (uint32)pbt.Num() && pbt[m_teamIndex].Num() == 2)
{
return false;
}
}
// Try for the local player to join this team
if (myState) // Check the state cause it can be null
myState->RequestTeamEntry(m_teamIndex);
else
GWERROR(L"Player not set in " + GetName());
// Make sure this item is now selected
GetSubMenu()->SelectNewItem(this);
return true;
}
void ULobbySlot::Init(UTextBlock* teamName, UVerticalBox* playerList)
{
m_teamName = teamName;
m_playerList = playerList;
// Add fixed 2 player slots to this team
m_playerList->ClearChildren();
for (int32 i = 0; i < 2; i++)
{
ULobbyPlayerSlot* playerSlot = CreateWidget<ULobbyPlayerSlot>(GetWorld(), lobbyPlayerSlotClass);
check(playerSlot);
playerSlot->Init(nullptr);
m_playerSlots.Add(playerSlot);
m_playerList->AddChild(playerSlot);
}
}
void ULobbySlot::SetState(bool open)
{
m_open = open;
}

View File

@@ -0,0 +1,55 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "MenuButton.h"
#include <set>
#include "LobbySlot.generated.h"
UCLASS()
class UNREALPROJECT_API ULobbySlot : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
void SetTeamIndex(uint32 idx);
// Must be called from blueprint to setup this element
UFUNCTION(BlueprintCallable, Category = "Lobby")
void Init(UTextBlock* teamName, UVerticalBox* playerList);
void UpdatePlayers(TArray<class APlayerStateBase*> players);
void SetState(bool open);
UFUNCTION(BlueprintCallable, Category = "Lobby")
bool OnSlotSelected();
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
UFont* font;
UPROPERTY(EditDefaultsOnly, Category="Lobby")
TSubclassOf<class ULobbyPlayerSlot> lobbyPlayerSlotClass;
private:
ULobbySlot(const FObjectInitializer& init);
void m_UpdateTeamTitle();
UPROPERTY()
UTextBlock* m_teamName;
UPROPERTY()
UVerticalBox* m_playerList;
UPROPERTY()
TArray<class ULobbyPlayerSlot*> m_playerSlots;
UPROPERTY()
UButton* m_joinButton;
uint32 m_teamIndex;
UPROPERTY()
TArray<class APlayerStateBase*> m_players;
bool m_open;
};

View File

@@ -0,0 +1,4 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ButtonHintBar.h"

View File

@@ -0,0 +1,13 @@
// Project Lab - NHTV Igad
#pragma once
#include "UserWidget.h"
#include "ButtonHintBar.generated.h"
UCLASS()
class UNREALPROJECT_API UButtonHintBar : public UUserWidget
{
GENERATED_BODY()
public:
};

View File

@@ -0,0 +1,246 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "GameList.h"
#include "GameListItem.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
#include "ScreenOverlay.h"
#include "PlayerControllerBase.h"
static UClass* itemWidgetClass;
UGameList::UGameList(const FObjectInitializer& init)
: Super(init)
{
itemWidgetClass = ConstructorHelpers::FClassFinder<UGameListItem>(TEXT("/Game/Assets/GUI/Components/WEEGEE_GameListItem")).Class;
m_quickJoining = false;
}
void UGameList::NativeConstruct()
{
Super::NativeConstruct();
m_searchingSessions = false;
m_joining = false;
UWorld* world = this->GetWorld();
if (!world)
return;
m_gameInstance = Cast<UDefaultGameInstance>(world->GetGameInstance());
if (!m_gameInstance)
return;
onItemSelectionConfirmed.AddDynamic(this, &UGameList::OnListItemPressed);
}
void UGameList::NativeDestruct()
{
m_StopPingingGames();
Super::NativeDestruct();
}
void UGameList::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UGameList::OnStartSearching_Implementation()
{
}
void UGameList::OnEndSearching_Implementation(int32)
{
}
void UGameList::SetInitialSearchMode_Implementation(bool useLan)
{
}
void UGameList::OnQuickJoin()
{
OnSearchButton();
m_quickJoining = true;
}
void UGameList::JoinSession(const class FOnlineSessionSearchResult& result)
{
m_StopPingingGames();
if(!m_joining)
{
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
check(!m_joiningOverlay);
m_joiningOverlay = pc->overlay->ShowOverlay("Joining game");
m_joining = true;
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->onJoinSessionComplete.AddUObject(this, &UGameList::m_OnJoinSessionComplete);
inst->sessionManager->JoinSession(result);
}
}
void UGameList::SetSearchMode(bool useLan)
{
}
void UGameList::OnSearchButton()
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
if (!m_searchingSessions)
{
check(!m_searchingOverlay);
inst->sessionManager->onFindSessionsComplete.AddUObject(this, &UGameList::m_OnFindSessionsComplete);
inst->sessionManager->FindSessions();
m_searchingSessions = true;
container->ClearChildren();
m_StopPingingGames();
m_gameItems.SetNum(0);
RescanItems();
OnStartSearching();
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
m_searchingOverlay = pc->overlay->ShowOverlay("Searching for games");
}
}
void UGameList::OnListItemPressed(UMenuItemBase* item)
{
GWPRINT(L"Pressed item " + item);
UGameListItem* listItem = Cast<UGameListItem>(item);
if(listItem)
{
JoinSession(*listItem->result);
}
}
void UGameList::m_OnFindSessionsComplete(bool success)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
// Hide searching overlay
m_searchingOverlay->Close();
m_searchingOverlay = nullptr;
m_searchingSessions = false;
if(!success)
{
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
TArray<FString> options;
options.Add("OK");
pc->overlay->ShowMessageBox("Error", "Error occured while fining games", options);
return;
}
if(m_quickJoining)
{
// Find session with most players in, that is not full
auto& results = inst->sessionManager->sessionSearch->SearchResults;
if(results.Num() > 0)
{
FOnlineSessionSearchResult* optimal = &results[0];
int32 largest = 0;
for(int32 i = 1; i < results.Num(); i++)
{
const int32 available = results[i].Session.NumOpenPublicConnections;
if(available == 0) continue;
const int32 max = results[i].Session.SessionSettings.NumPublicConnections;
const int32 current = max - available;
if(current > largest)
{
optimal = &results[i];
largest = current;
}
}
JoinSession(*optimal);
}
m_quickJoining = false;
}
else
{
// Get session results
auto& results = inst->sessionManager->sessionSearch->SearchResults;
for(int32 i = 0; i < results.Num(); i++)
{
// Create a selectable slot in the game list
UGameListItem* childItem = CreateWidget<UGameListItem>(GetWorld(), itemWidgetClass);
check(container);
container->AddChild(childItem);
childItem->SetItem(this, results[i]);
m_gameItems.Add(childItem);
}
OnEndSearching(results.Num());
RescanItems();
// Give ping data for games
m_StartPingingGames();
}
m_searchingSessions = false;
// Remove callback
inst->sessionManager->onFindSessionsComplete.RemoveAll(this);
}
void UGameList::m_OnJoinSessionComplete(int32 result)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
m_joining = false;
// Remove callback
inst->sessionManager->onJoinSessionComplete.RemoveAll(this);
if(result != EOnJoinSessionCompleteResult::Success)
{
FString error = "Unknown";
switch(result)
{
case EOnJoinSessionCompleteResult::AlreadyInSession:
error = "Already in session";
break;
case EOnJoinSessionCompleteResult::CouldNotRetrieveAddress:
error = "Could not retrieve address";
break;
case EOnJoinSessionCompleteResult::SessionDoesNotExist:
error = "Session does not exist";
break;
case EOnJoinSessionCompleteResult::SessionIsFull:
error = "Session is full";
break;
}
// Close joining overlay
m_joiningOverlay->Close();
m_joiningOverlay = nullptr;
// Show error message
TArray<FString> options;
options.Add("OK");
pc->overlay->ShowMessageBox("Error", FString("Failed to join session:\n") + error, options);
}
else
{
// Travel to destination player lobby
pc->ClientTravel(inst->sessionManager->joinConnectionString, ETravelType::TRAVEL_Absolute, false);
//UWorld* world = GetWorld();
//world->SeamlessTravel(inst->sessionManager->joinConnectionString, true);
//world->ServerTravel()
//AMenuGameMode* mgm = Cast<AMenuGameMode>(world->GetAuthGameMode());
}
}
void UGameList::m_PingGames()
{
for(auto& g : m_gameItems)
{
g->Ping();
}
}
void UGameList::m_StartPingingGames()
{
GetGameInstance()->GetTimerManager().SetTimer(m_pinger, this, &UGameList::m_PingGames, 1.0f, true);
}
void UGameList::m_StopPingingGames()
{
GetGameInstance()->GetTimerManager().ClearTimer(m_pinger);
for(UGameListItem* i : m_gameItems)
{
i->Ping();
}
}

View File

@@ -0,0 +1,75 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "OnlineSessionInterface.h"
#include "GameList.generated.h"
using namespace std;
struct SessionItemData
{
FString name;
int32 index;
FOnlineSessionSearchResult* result;
};
UCLASS()
class UNREALPROJECT_API UGameList : public UPersistentSideView
{
GENERATED_BODY()
public:
UGameList(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void OnStartSearching();
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void OnEndSearching(int32 gamesFound);
UFUNCTION(BlueprintNativeEvent, Category = "UI")
void SetInitialSearchMode(bool useLan);
UFUNCTION(BlueprintCallable, Category = "UI")
void OnQuickJoin();
void JoinSession(const class FOnlineSessionSearchResult& result);
UFUNCTION(BlueprintCallable, Category = "UI")
void SetSearchMode(bool useLan);
UFUNCTION(BlueprintCallable, Category = "UI")
void OnSearchButton();
UFUNCTION()
void OnListItemPressed(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "UI")
int32 GetNumGames() const { return m_gameItems.Num(); }
UFUNCTION(BlueprintCallable, Category = "UI")
TArray<class UGameListItem*> GetGames() const { return m_gameItems; }
private:
UFUNCTION()
void m_OnFindSessionsComplete(bool success);
UFUNCTION()
void m_OnJoinSessionComplete(int32 result);
void m_PingGames();
void m_StartPingingGames();
void m_StopPingingGames();
FTimerHandle m_pinger;
TArray<class UGameListItem*> m_gameItems;
bool m_searchingSessions;
bool m_quickJoining;
bool m_joining;
UPROPERTY()
class UOverlayInfo* m_searchingOverlay;
UPROPERTY()
class UOverlayInfo* m_joiningOverlay;
UPROPERTY()
class UDefaultGameInstance* m_gameInstance;
};

View File

@@ -0,0 +1,53 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "GameListItem.h"
#include "GameList.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
UGameListItem::UGameListItem(const FObjectInitializer& init)
: Super(init)
{
valid = false;
}
void UGameListItem::NativeConstruct()
{
Super::NativeConstruct();
OnGameInfoUpdated();
}
void UGameListItem::SetItem(UGameList* parent, const FOnlineSessionSearchResult& _result)
{
// Set the sesion result to be displayed in this item
check(parent);
m_parent = parent;
result = &_result;
valid = result != nullptr;
if(valid)
{
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
FString nickname = inst->sessionManager->GetPlayerName(*result->Session.OwningUserId);
// Game Name = Online platform nickname of host
gameHost = nickname;
gamePing = result->PingInMs;
if(!result->Session.SessionSettings.Get(SETTING_MAPNAME, gameMapName))
{
GWWARNING(L"Game does not have a map name set");
}
gamePlayersMax = result->Session.SessionSettings.NumPublicConnections;
gamePlayers = gamePlayersMax - result->Session.NumOpenPublicConnections;
}
OnGameInfoUpdated();
}
void UGameListItem::Ping()
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
inst->sessionManager->PingResult(*result);
OnGameInfoUpdated();
}

View File

@@ -0,0 +1,41 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "GameListItem.generated.h"
UCLASS()
class UNREALPROJECT_API UGameListItem : public UMenuButton
{
GENERATED_BODY()
public:
UGameListItem(const FObjectInitializer& init);
virtual void NativeConstruct() override;
void SetItem(class UGameList* parent, const FOnlineSessionSearchResult& result);
UFUNCTION(BlueprintImplementableEvent, Category = "UI")
void OnGameInfoUpdated();
void Ping();
UPROPERTY(BlueprintReadOnly, Category = "Game")
bool valid;
UPROPERTY(BlueprintReadOnly, Category = "Game")
FString gameHost;
UPROPERTY(BlueprintReadOnly, Category = "Game")
FString gameMapName;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePlayers;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePlayersMax;
UPROPERTY(BlueprintReadOnly, Category = "Game")
int32 gamePing;
const class FOnlineSessionSearchResult* result;
private:
UPROPERTY()
class UGameList* m_parent;
};

View File

@@ -0,0 +1,103 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SelectButton.h"
#include "DefaultGameInstance.h"
#include "Prefs.h"
#include "GraphicsMenu.h"
void UGraphicsMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
m_Update();
}
void UGraphicsMenu::InitButtons(USelectButton* templateButton, TArray<USelectButton*> settingButtons)
{
m_templateButton = templateButton;
m_settingButtons = settingButtons;
if (!IsValid(m_templateButton) || m_settingButtons.Num() != 6)
{
JERROR("InitButton arguments invalid");
m_templateButton = nullptr;
return;
}
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(instance))
return;
UPrefs* const prefs = instance->GetPrefs();
int32 fetchPreset = instance->GetScalabilityQuality();
templateButton->selected = prefs->usingCustomGraphicsSettings ? 0 : fetchPreset + 1;
m_previousSelection = -5;
m_Update();
}
void UGraphicsMenu::Apply()
{
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(m_templateButton) || !IsValid(instance))
return;
const int32 current = m_templateButton->selected;
if (current > 0)
{
// Store the template setting
instance->SetScalabilityQuality(current - 1);
}
else
{
// Store the individual sub settings
instance->SetScalabilityQualityValues(
m_settingButtons[0]->selected,
m_settingButtons[1]->selected,
m_settingButtons[2]->selected,
m_settingButtons[3]->selected,
m_settingButtons[4]->selected,
m_settingButtons[5]->selected);
}
}
void UGraphicsMenu::m_Update()
{
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
if (!IsValid(m_templateButton) || !IsValid(instance))
return;
const int32 current = m_templateButton->selected;
if (m_previousSelection != current) // Only update when the value has changed
{
if (current == 0)
{
// Enable the sub setting buttons
for (int32 i = 0; i < m_settingButtons.Num(); i++)
m_settingButtons[i]->SetIsEnabled(true);
// Fetch the stored sub settings from prefs
TArray<int32> customSettings = instance->GetPrefs()->customGraphicsSettings;
if (customSettings.Num() <= m_settingButtons.Num())
{
for (int32 i = 0; i < customSettings.Num(); i++)
m_settingButtons[i]->selected = customSettings[i];
}
}
else
{
// Disable the subsetting buttons and set them to the template
for (int32 i = 0; i < m_settingButtons.Num(); i++)
{
m_settingButtons[i]->SetIsEnabled(false);
m_settingButtons[i]->selected = current - 1;
}
}
m_previousSelection = current;
}
}

View File

@@ -0,0 +1,32 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "GraphicsMenu.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UGraphicsMenu : public USideView
{
GENERATED_BODY()
public:
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "Graphics")
void InitButtons(class USelectButton* templateButton, TArray<class USelectButton*> settingButtons);
UFUNCTION(BlueprintCallable, Category = "Graphics")
void Apply();
private:
void m_Update();
USelectButton* m_templateButton;
TArray<USelectButton*> m_settingButtons;
int32 m_previousSelection;
};

View File

@@ -0,0 +1,161 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MapData.h"
#include "MapSelectionScreen.h"
#include "AssetRegistryModule.h"
#include "DefaultGameInstance.h"
#include "MapSlot.h"
#include "SessionManager.h"
#include "MenuController.h"
#include "ScreenOverlay.h"
#include "MenuGameMode.h"
static UClass* mapSlotWidgetClass;
UMapSelectionScreen::UMapSelectionScreen(const FObjectInitializer& init)
: Super(init)
{
mapSlotWidgetClass = ConstructorHelpers::FClassFinder<UMapSlot>(TEXT("/Game/Assets/GUI/Components/WEEGEE_MapListItem")).Class;
}
void UMapSelectionScreen::NativeConstruct()
{
Super::NativeConstruct();
if(container)
{
// Get all the available maps and create selection buttons for them
UWorld* world = GetWorld();
check(world);
auto& maps = Cast<UDefaultGameInstance>(world->GetGameInstance())->maps;
for(int32 i = 0; i < maps.Num(); i++)
{
UMapSlot* mapSlot = CreateWidget<UMapSlot>(GetWorld(), mapSlotWidgetClass);
check(mapSlot);
mapSlot->mapData = maps[i];
mapSlot->mapScreen = this;
mapSlot->OnMapSet();
m_mapSlots.Add(mapSlot);
container->AddChild(mapSlot);
}
onItemSelected.RemoveAll(this);
onItemSelected.AddDynamic(this, &UMapSelectionScreen::OnSelectionChanged);
RescanItems();
SelectMap(0);
}
}
void UMapSelectionScreen::OnSelectionChanged(UMenuItemBase* item)
{
UMapSlot* mapSlot = Cast<UMapSlot>(item);
if(mapSlot)
{
//SelectMap(mapSlot->index);
}
}
void UMapSelectionScreen::SelectMap(int32 index)
{
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
TArray<UMapData*>& maps = inst->maps;
if(index < 0 || index > maps.Num())
{
GWERROR(L"Selected map out of range");
return;
}
mapData = maps[index];
check(mapData);
// Set the asset path to the selected map in the session manager
AMenuGameMode* gameMode = Cast<AMenuGameMode>(world->GetAuthGameMode());
if(gameMode)
{
gameMode->SetMap(mapData->pathToAsset);
GWPRINT(L"Selected map: " + maps[index]->friendlyName);
}
else
{
GWARNING(L"Can't select map, no game mode!");
}
}
void UMapSelectionScreen::CreateGame()
{
SelectMap(GetSelectedItem());
if(!mapData)
{
GERROR("No map set");
return;
}
UWorld* world = GetWorld();
check(world);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
AMenuGameMode* gameMode = Cast<AMenuGameMode>(world->GetAuthGameMode());
if(!gameMode)
{
GERROR("Can't start game, no menu game mode set");
return;
}
APlayerControllerBase* pc = Cast<APlayerControllerBase>(GetOwningPlayer());
if(m_sessionCreateOverlay)
return; // Already creating session
m_sessionCreateOverlay = pc->overlay->ShowOverlay("Creating session");
// Set the map setting in the game mode
gameMode->AssignMapData(mapData);
// Make sure there is no connection already
inst->sessionManager->CloseNetConnections();
// Set host settings
FOnlineSessionSettings& sessionSettings = inst->sessionManager->sessionSettings;
sessionSettings.NumPublicConnections = mapData->maxTeamCount * 2; // Team count setting translated to max connected players (x2)
sessionSettings.Set(SETTING_MAPNAME, mapData->pathToAsset, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
// Create a new session
inst->sessionManager->onCreateSessionComplete.AddUObject(this, &UMapSelectionScreen::m_OnCreateSessionCompleted);
inst->sessionManager->CreateSession();
}
void UMapSelectionScreen::m_OnCreateSessionCompleted(bool success)
{
if(m_sessionCreateOverlay)
{
m_sessionCreateOverlay->Close();
m_sessionCreateOverlay = nullptr;
}
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
inst->sessionManager->onCreateSessionComplete.RemoveAll(this);
AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
if(success)
{
// Goto the lobby
mc->OnEnterLobby();
// Start the session, @note not sure if this is needed
//inst->sessionManager->StartSession();
}
else
{
TArray<FString> options;
options.Add("OK");
mc->overlay->ShowMessageBox("Error", "Failed to create game session", options);
}
}
UMapData::UMapData()
{
maxTeamCount = 3;
friendlyName = "Map Name";
description = "<Designers: Add description>";
}

View File

@@ -0,0 +1,40 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreen.h"
#include "MapSelectionScreen.generated.h"
UCLASS()
class UNREALPROJECT_API UMapSelectionScreen : public USideView
{
GENERATED_BODY()
public:
UMapSelectionScreen(const FObjectInitializer& init);
virtual void NativeConstruct() override;
UFUNCTION()
void OnSelectionChanged(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "Level")
void SelectMap(int32 mapIndex);
UFUNCTION(BlueprintCallable, Category = "Level")
void CreateGame();
// Currently Selected map data
UPROPERTY(BlueprintReadOnly, Category = "Level")
class UMapData* mapData;
UPROPERTY()
class UMapSlot* selectedMapSlot;
private:
UPROPERTY()
class UOverlayInfo* m_sessionCreateOverlay;
UFUNCTION()
void m_OnCreateSessionCompleted(bool success);
UPROPERTY()
TArray<class UMapSlot*> m_mapSlots;
};

View File

@@ -0,0 +1,14 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MapSlot.h"
#include "MapData.h"
void UMapSlot::NativeConstruct()
{
Super::NativeConstruct();
}
void UMapSlot::OnMapSet_Implementation()
{
}

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "MapSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMapSlot : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UFUNCTION(BlueprintNativeEvent, Category = "Slot")
void OnMapSet();
UPROPERTY(BlueprintReadOnly, Category = "Slot")
class UMapSelectionScreen* mapScreen;
UPROPERTY(BlueprintReadOnly, Category = "Slot")
class UMapData* mapData;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "MenuAction.Generated.h"
UENUM(BlueprintType)
enum class EMenuActionBinding : uint8
{
Confirm,
Back,
Opt1,
Opt2,
Start,
Up,
Down,
Left,
Right,
Repeat_Left,
Repeat_Right,
Trigger_Left,
Trigger_Right,
Shoulder_Left,
Shoulder_Right,
Options,
// These are actually just for button hint icons
// don't use these for actual actions as they wont get used
LeftStick,
RightStick,
DPad,
ShoulderButtons,
Triggers
};

View File

@@ -0,0 +1,147 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuButton.h"
#include "SubMenu.h"
FLinearColor uiIdleColor = FLinearColor(1.0f, 1.0f, 1.0f, 0.3f);
FLinearColor uiSelectedColor = FLinearColor(0.8f, 1.0f, 0.8f, 0.4f);
FLinearColor uiSelectedFocusedColor = FLinearColor(0.2f, 1.0f, 0.2f, 0.8f);
TSubclassOf<class UMenuButton> defaultButtonClass;
UMenuButton::UMenuButton(const FObjectInitializer& init)
: Super(init)
{
defaultButtonClass = ConstructorHelpers::FClassFinder<UMenuButton>(L"/Game/Assets/GUI/Components/WEEGEE_Button").Class;
}
void UMenuButton::NativeConstruct()
{
m_button = WidgetTree->FindWidget<UButton>("Button");
m_label = WidgetTree->FindWidget<UTextBlock>("Label");
m_textOverlay = WidgetTree->FindWidget<UOverlay>("TextOverlay");
if(!m_button)
{
GWERROR(L"Widget " + GetName() + L" does not contain a Button componenet");
}
else
{
m_button->OnClicked.AddDynamic(this, &UMenuButton::m_OnPressed);
}
NativeOnSelectionChanged(false, true);
Super::NativeConstruct();
if(m_textOverlay && m_label)
{
m_blurOverlay = NewObject<UOverlay>(GetWorld());
UOverlaySlot* slot = m_textOverlay->AddChildToOverlay(m_blurOverlay);
slot->SetHorizontalAlignment(EHorizontalAlignment::HAlign_Fill);
slot->SetVerticalAlignment(EVerticalAlignment::VAlign_Fill);
UOverlaySlot* labelSlot = Cast<UOverlaySlot>(m_label->Slot);
EHorizontalAlignment textAlignH = labelSlot ? labelSlot->HorizontalAlignment.GetValue() : HAlign_Fill;
EVerticalAlignment textAlignV = labelSlot ? labelSlot->VerticalAlignment.GetValue() : VAlign_Fill;
m_label->RemoveFromParent();
static int32 quality = 16;
static float distance = 2.0f;
for(int32 i = 0; i < quality; i++)
{
float r = (float)i / (float)(quality - 1);
FVector2D offset = FVector2D(cos(r*PI), sin(r*PI)) * distance;
// Create Glow effect
UTextBlock* blurText = NewObject<UTextBlock>(GetWorld());
UOverlaySlot* textSlot = m_blurOverlay->AddChildToOverlay(blurText);
textSlot->SetHorizontalAlignment(textAlignH);
textSlot->SetVerticalAlignment(textAlignV);
FSlateFontInfo font = m_label->Font;
blurText->SetFont(font);
blurText->SetColorAndOpacity(FSlateColor(FLinearColor(0.6f, 0.6f, 1.0f, 0.5f)));
blurText->SetRenderTranslation(offset);
blurText->SetText(m_label->GetText());
}
m_blurOverlay->SetVisibility(ESlateVisibility::Hidden);
labelSlot = m_textOverlay->AddChildToOverlay(m_label);
labelSlot->SetHorizontalAlignment(textAlignH);
labelSlot->SetVerticalAlignment(textAlignV);
}
}
void UMenuButton::FocusTick(float DeltaTime)
{
Super::FocusTick(DeltaTime);
if(m_button && m_button->IsHovered() && GetSubMenu()->GetSelectedItem() != index)
{
GetSubMenu()->SelectNewItemByIndex(index);
}
}
void UMenuButton::m_OnPressed()
{
if(HasFocus())
{
NativeOnPressed(false);
}
}
void UMenuButton::NativeOnMenuAction(EMenuActionBinding binding)
{
Super::NativeOnMenuAction(binding);
if(binding == EMenuActionBinding::Confirm)
NativeOnPressed(true);
}
void UMenuButton::NativeOnSelectionChanged(bool selected, bool controller)
{
Super::NativeOnSelectionChanged(selected, controller);
if(!m_button)
return;
UScrollBox* sb = Cast<UScrollBox>(Slot->Parent);
if(sb && selected)
{
sb->ScrollWidgetIntoView(this, true);
}
if(m_blurOverlay)
{
if(selected)
{
m_blurOverlay->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
{
m_blurOverlay->SetVisibility(ESlateVisibility::Hidden);
}
}
}
void UMenuButton::NativeOnPressed(bool controllerInput)
{
// Simulate OnSelectionConfirmed event if this button is pressed by a mouse
GetSubMenu()->NativeOnSelectionConfirmed(this);
onPressed.Broadcast();
}
void UMenuButton::SetButtonText(FString textStr)
{
FText text = FText::FromString(textStr);
if(m_label)
m_label->SetText(text);
if(m_blurOverlay)
{
for(int32 i = 0; i < m_blurOverlay->GetChildrenCount(); i++)
{
UTextBlock* tb = Cast<UTextBlock>(m_blurOverlay->GetChildAt(i));
if(tb)
{
tb->SetText(text);
}
}
}
}

View File

@@ -0,0 +1,48 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuItemBase.h"
#include "MenuButton.generated.h"
extern FLinearColor uiIdleColor;
extern FLinearColor uiSelectedColor;
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMenuButton : public UMenuItemBase
{
GENERATED_BODY()
public:
UMenuButton(const FObjectInitializer& init);
void NativeConstruct() override;
void FocusTick(float DeltaTime) override;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FButtonPressed);
UPROPERTY(BlueprintAssignable, Category = "MenuButton")
FButtonPressed onPressed;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
virtual void NativeOnSelectionChanged(bool selected, bool controller) override;
// Called when this button is pressed, and if it was a controller or a mouse
virtual void NativeOnPressed(bool controllerInput);
// Updates button text + glow
UFUNCTION(BlueprintCallable, Category="MenuButton")
void SetButtonText(FString text);
void SimulatePressed() { m_OnPressed(); }
protected:
UFUNCTION()
virtual void m_OnPressed();
UOverlay* m_textOverlay;
UOverlay* m_blurOverlay;
UPROPERTY()
UTextBlock* m_label;
UButton* m_button;
};

View File

@@ -0,0 +1,9 @@
#pragma once
UENUM(BlueprintType)
enum class MenuButtonID : uint8
{
OptionsGeneral,
OptionsGraphics,
Options,
};

View File

@@ -0,0 +1,92 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuItemBase.h"
#include "SubMenu.h"
#include "PlayerControllerBase.h"
#include "ScreenOverlay.h"
void UMenuItemBase::NativeConstruct()
{
m_selected = false;
blockInput = false;
m_subMenu = nullptr;
priority = 0;
Super::NativeConstruct();
}
void UMenuItemBase::NativeDestruct()
{
Super::NativeDestruct();
}
void UMenuItemBase::FocusTick(float DeltaTime)
{
}
UMsgBoxInfo* UMenuItemBase::ShowMessageBox(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString defaultOption)
{
UScreenOverlay* overlay = Cast<APlayerControllerBase>(GetOwningPlayer())->overlay;
if(!overlay)
return nullptr;
return overlay->ShowMessageBoxCallback(cb, caption, message, options, defaultOption);
}
void UMenuItemBase::NativeOnSelectionChanged(bool selected, bool controller)
{
if(selected != m_selected)
{
m_selected = selected;
onSelectionChanged.Broadcast(selected);
}
}
bool UMenuItemBase::IsSelected() const
{
return m_selected;
}
class USubMenu* UMenuItemBase::GetSubMenu() const
{
return m_subMenu;
}
bool UMenuItemBase::HasFocus() const
{
return GetSubMenu() && GetSubMenu()->HasFocus();
}
void UMenuItemBase::AddActionBinding(EMenuActionBinding binding, FMenuAction action)
{
m_actionBindings.Add(binding, action);
}
void UMenuItemBase::ClearActionBinding(EMenuActionBinding binding)
{
m_actionBindings.Remove(binding);
}
void UMenuItemBase::OnMenuAction_Implementation(EMenuActionBinding binding)
{
}
void UMenuItemBase::NativeOnMenuAction(EMenuActionBinding binding)
{
OnMenuAction(binding);
TArray<FMenuAction> actions;
m_actionBindings.MultiFind(binding, actions);
for(int32 i = 0; i < actions.Num(); i++)
{
actions[i].Execute();
}
}
FText UMenuItemBase::GetDescription_Implementation()
{
return FText();
}
FText UMenuItemBase::GetTitle_Implementation()
{
return FText();
}

View File

@@ -0,0 +1,83 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuAction.h"
#include "Blueprint/UserWidget.h"
#include "MenuItemBase.generated.h"
DECLARE_DYNAMIC_DELEGATE_OneParam(FOverlayItemClosed, int32, choice);
extern TSubclassOf<class UMenuButton> defaultButtonClass;
extern TSubclassOf<class UMenuSlider> defaultSliderClass;
/**
* Base class for a menu item with controller navigation/input support
*/
UCLASS()
class UNREALPROJECT_API UMenuItemBase : public UUserWidget
{
GENERATED_BODY()
public:
void NativeConstruct() override;
void NativeDestruct() override;
// Called when the item's menu has focus every frame
virtual void FocusTick(float DeltaTime);
UFUNCTION(BlueprintCallable, Category="MenuItem")
virtual void NativeOnSelectionChanged(bool selected, bool controller);
// Shows a message box and fires a delegate when the user chooses an option
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UMsgBoxInfo* ShowMessageBox(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString defaultOption);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSelectionChanged, bool, selected);
UPROPERTY(BlueprintAssignable, Category = "MenuItem")
FSelectionChanged onSelectionChanged;
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "MenuItem")
int32 priority;
UPROPERTY(BlueprintReadOnly)
int32 index;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
bool IsSelected() const;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
class USubMenu* GetSubMenu() const;
UFUNCTION(BlueprintCallable, Category = "MenuItem")
bool HasFocus() const;
DECLARE_DYNAMIC_DELEGATE(FMenuAction);
UFUNCTION(BlueprintCallable, Category = "MenuItem")
void AddActionBinding(EMenuActionBinding binding, FMenuAction action);
UFUNCTION(BlueprintCallable, Category = "MenuItem")
void ClearActionBinding(EMenuActionBinding binding);
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
void OnMenuAction(EMenuActionBinding binding);
virtual void NativeOnMenuAction(EMenuActionBinding binding);
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
FText GetDescription();
UFUNCTION(BlueprintNativeEvent, Category = "MenuItem")
FText GetTitle();
UPROPERTY(BlueprintReadOnly, Category="MenuItem")
bool blockInput;
private:
bool m_selected;
friend class APlayerControllerBase;
friend class USubMenu;
TMultiMap<EMenuActionBinding, FMenuAction> m_actionBindings;
protected:
UPROPERTY()
class USubMenu* m_subMenu;
};

View File

@@ -0,0 +1,337 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuScreen.h"
#include "ButtonHintBar.h"
#include "SubMenu.h"
#include "MenuButton.h"
void UMenuScreen::NativeConstruct()
{
Super::NativeConstruct();
m_sideViewActive = false;
m_buttonHintBar = Cast<UButtonHintBar>(WidgetTree->FindWidget("ButtonHintBar"));
m_menuContainer = Cast<UOverlay>(WidgetTree->FindWidget("MenuContainer"));
m_sideViewContainer = Cast<UOverlay>(WidgetTree->FindWidget("SideViewContainer"));
m_menuTitle = Cast<UTextBlock>(WidgetTree->FindWidget("MenuTitle"));
m_sideViewTitle = Cast<UTextBlock>(WidgetTree->FindWidget("SideViewTitle"));
}
UMenuPanel* UMenuScreen::OpenScreenMenu(TSubclassOf<UMenuPanel> cls)
{
UMenuPanel* menu = CreateWidget<UMenuPanel>(GetWorld(), cls);
if(!menu)
{
GERROR("Failed to create menu for screen " + GetName());
return nullptr;
}
m_menuContainer->AddChildToOverlay(menu);
OpenSubMenu(menu);
menu->FadeIn();
return menu;
}
USideView* UMenuScreen::OpenSideView(TSubclassOf<class USideView> cls)
{
USideView* view = CreateWidget<USideView>(GetWorld(), cls);
if(!view)
{
GERROR("Failed to create side view for screen " + GetName());
return nullptr;
}
UPersistentSideView* pview = Cast<UPersistentSideView>(view);
UOverlaySlot* slot = m_sideViewContainer->AddChildToOverlay(view);
slot->SetHorizontalAlignment(HAlign_Fill);
slot->SetVerticalAlignment(VAlign_Fill);
if(pview)
m_OpenSubMenu(view, false);
else
OpenSubMenu(view);
view->FadeIn();
// Switch side view active event
if(!m_sideViewActive)
{
SwitchSideViewVisible(true);
m_sideViewActive = true;
}
return view;
}
void UMenuScreen::CloseAllSideViews()
{
TArray<USideView*> viewsToRemove;
for(int32 i = 0; i < m_sideViewContainer->GetChildrenCount(); i++)
{
USideView* sv = Cast<USideView>(m_sideViewContainer->GetChildAt(i));
if(sv && !sv->IsA<UPersistentSideView>())
viewsToRemove.Add(sv);
}
for(USideView* sv : viewsToRemove)
{
CloseSubMenu(sv);
}
}
void UMenuScreen::OpenPersistentSideView(UPersistentSideView* sideView)
{
UMenuScreen::OpenSubMenu(sideView);
}
void UMenuScreen::ClosePersistentSideView(UPersistentSideView* sideView)
{
m_CloseSubMenu(sideView, true);
}
void UMenuScreen::OpenSubMenu(class USubMenu* submenu)
{
m_OpenSubMenu(submenu);
}
void UMenuScreen::CloseSubMenu(class USubMenu* submenu)
{
m_CloseSubMenu(submenu);
}
void UMenuScreen::m_OpenSubMenu(class USubMenu* submenu, bool transferFocus)
{
CloseAllSideViews();
if(transferFocus)
Super::OpenSubMenu(submenu);
UMenuScreenSubMenu* mssm = Cast<UMenuScreenSubMenu>(submenu);
if(!m_menuTitle || !mssm)
return;
// Update titles and sub-titles
UMenuPanel* panel = Cast<UMenuPanel>(submenu);
if(panel)
m_menuTitle->SetText(FText::FromString(mssm->menuName));
USideView* view = Cast<USideView>(submenu);
if(view)
{
m_sideViewTitle->SetText(FText::FromString(mssm->menuName));
m_currentSideViews.Add(view);
}
}
void UMenuScreen::m_CloseSubMenu(class USubMenu* submenu, bool persist)
{
UMenuScreenSubMenu* mssm = Cast<UMenuScreenSubMenu>(submenu);
if(mssm)
mssm->m_isBeingClosed = true;
UMenuPanel* panel = Cast<UMenuPanel>(submenu);
if(panel)
{
CloseAllSideViews();
panel->onAnimationEnded.RemoveAll(this);
panel->onAnimationEnded.AddUObject(this, &UMenuScreen::m_OnFadeOutComplete);
panel->FadeOut();
}
USideView* view = Cast<USideView>(submenu);
if(view && !persist)
{
view->onAnimationEnded.RemoveAll(this);
view->onAnimationEnded.AddUObject(this, &UMenuScreen::m_OnFadeOutComplete);
view->FadeOut();
// Switch side view active event
if(m_currentSideViews.Num() > 0 && m_sideViewActive)
{
SwitchSideViewVisible(false);
m_sideViewActive = false;
}
m_currentSideViews.Remove(view);
}
Super::CloseSubMenu(submenu);
// Update titles and sub-titles
if(panel)
{
if(m_subMenus.Num() > 0)
{
UMenuPanel* newMenu = Cast<UMenuPanel>(m_subMenus.Last());
if(newMenu)
m_menuTitle->SetText(FText::FromString(newMenu->menuName));
else
goto _set_no_panel_text;
}
else
{
_set_no_panel_text:
m_menuTitle->SetText(FText());
}
}
else if(view && !persist)
{
if(m_subMenus.Num() > 0)
{
USideView* newMenu = Cast<USideView>(m_subMenus.Last());
if(newMenu)
m_sideViewTitle->SetText(FText::FromString(newMenu->menuName));
else
goto _set_no_view_text;
}
else
{
_set_no_view_text:
m_sideViewTitle->SetText(FText());
}
}
if(mssm)
mssm->m_isBeingClosed = false;
}
void UMenuScreen::m_OnFadeOutComplete(UMenuScreenSubMenu* panel, int32 type)
{
if(type == 2) // Fade Out
{
panel->RemoveFromParent();
}
}
static float animationDuration = 0.35f;
void UMenuScreenSubMenu::NativeConstruct()
{
m_animationType = 0;
m_animationTime = 0.0f;
m_isBeingClosed = false;
Super::NativeConstruct();
}
UMenuScreenSubMenu::UMenuScreenSubMenu(const FObjectInitializer& init)
: Super(init)
{
menuName = "<Unnamed Menu!>";
}
void UMenuScreenSubMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
float r = FMath::Clamp(m_animationTime / animationDuration, 0.0f, 1.0f);
if(m_animationType == 1) // Fade In
{
SetRenderTranslation(FVector2D(0.0f - ((1.0f - r)*(1.0f - r)) * 100.0f, 0.0f));
FLinearColor color = FLinearColor(1.0f, 1.0f, 1.0f, r);
SetColorAndOpacity(color);
}
else if(m_animationType == 2) // Fade Out
{
SetRenderTranslation(FVector2D(0.0f + (r*r) * 100.0f, 0.0f));
FLinearColor color = FLinearColor(1.0f, 1.0f, 1.0f, (1.0f-r));
SetColorAndOpacity(color);
}
if(m_animationType != 0)
{
if(r >= 1.0f)
{
onAnimationEnded.Broadcast(this, m_animationType);
m_animationType = 0;
}
m_animationTime += InDeltaTime;
}
}
void UMenuScreenSubMenu::OnSuspend(USubMenu* newSubMenu, bool loseFocus)
{
// Don't fade out / lose focus when side view is opened
USideView* sv = Cast<USideView>(newSubMenu);
if(sv)
loseFocus = false;
else
FadeOut();
Super::OnSuspend(newSubMenu, loseFocus);
}
void UMenuScreenSubMenu::OnRestore(USubMenu* removedMenu)
{
// To prevent playing the fade in animation again when we receive focus from closing side views when we are actually closing this menu
if(!m_isBeingClosed && !Cast<USideView>(removedMenu))
FadeIn();
Super::OnRestore(removedMenu);
}
void UMenuScreenSubMenu::FadeIn()
{
m_animationTime = 0.0f;
m_animationType = 1;
}
void UMenuScreenSubMenu::FadeOut()
{
m_animationTime = 0.0f;
m_animationType = 2;
}
UMenuPanel* UMenuScreenSubMenu::OpenScreenMenu(TSubclassOf<class UMenuPanel> cls)
{
UMenuScreen* screen = Cast<UMenuScreen>(GetScreen());
if(screen)
{
return screen->OpenScreenMenu(cls);
}
return nullptr;
}
USideView* UMenuScreenSubMenu::OpenSideView(TSubclassOf<class USideView> cls)
{
UMenuScreen* screen = Cast<UMenuScreen>(GetScreen());
if(screen)
{
screen->CloseAllSideViews();
return screen->OpenSideView(cls);
}
return nullptr;
}
class UMenuButton* UMenuScreenSubMenu::AddButton(FString label)
{
if(!container)
return nullptr;
UMenuButton* button = CreateWidget<UMenuButton>(GetWorld(), defaultButtonClass);
if(!button)
return nullptr;
container->AddChild(button);
RescanItems();
button->SetButtonText(label);
return button;
}
void UMenuScreenSubMenu::RescanItems()
{
m_backButton = nullptr;
Super::RescanItems();
for(UMenuItemBase* item : m_items)
{
UMenuButton* button = Cast<UMenuButton>(item);
if(button)
{
if(button->GetName() == "Back")
{
m_backButton = button;
break;
}
}
}
}
void UMenuScreenSubMenu::NativeOnMenuAction(EMenuActionBinding binding)
{
Super::NativeOnMenuAction(binding);
if(binding == EMenuActionBinding::Back)
{
if(m_backButton)
{
m_backButton->SimulatePressed();
}
}
}

View File

@@ -0,0 +1,135 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuScreenBase.h"
#include "SubMenu.h"
#include "MenuScreen.generated.h"
UCLASS()
class UMenuScreenSubMenu : public USubMenu
{
GENERATED_BODY()
public:
UMenuScreenSubMenu(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void OnSuspend(USubMenu* newSubMenu, bool loseFocus);
virtual void OnRestore(USubMenu* removedMenu);
void FadeIn();
void FadeOut();
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
UMenuPanel* OpenScreenMenu(TSubclassOf<class UMenuPanel> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
USideView* OpenSideView(TSubclassOf<class USideView> cls);
class UMenuButton* AddButton(FString label);
virtual void RescanItems() override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
// Assigns a button to be the back button, this button will receive a pressed event when the player presses the circle button
void SetBackButton(UMenuButton* button) { m_backButton = button; }
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnAnimationEnded, UMenuScreenSubMenu*, int32)
FOnAnimationEnded onAnimationEnded;
UPROPERTY(EditDefaultsOnly, Category = "SubMenu")
FString menuName;
private:
UPROPERTY()
UMenuButton* m_backButton;
int32 m_animationType;
float m_animationTime;
bool m_isBeingClosed;
friend class UMenuScreen;
};
UCLASS()
class USideView : public UMenuScreenSubMenu
{
GENERATED_BODY()
public:
virtual ~USideView() = default;
};
UCLASS()
class UPersistentSideView : public USideView
{
GENERATED_BODY()
public:
virtual ~UPersistentSideView() = default;
};
UCLASS()
class UMenuPanel : public UMenuScreenSubMenu
{
GENERATED_BODY()
public:
};
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMenuScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
void NativeConstruct() override;
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
UMenuPanel* OpenScreenMenu(TSubclassOf<class UMenuPanel> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
USideView* OpenSideView(TSubclassOf<class USideView> cls);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void CloseAllSideViews();
UFUNCTION(BlueprintImplementableEvent, Category="Menu Screen")
void SwitchSideViewVisible(bool visible);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void OpenPersistentSideView(UPersistentSideView* sideView);
UFUNCTION(BlueprintCallable, Category = "Menu Screen")
void ClosePersistentSideView(UPersistentSideView* sideView);
// When this menu needs to be hidden
UFUNCTION(BlueprintImplementableEvent)
void OnShow();
// When this menu needs to be shown
UFUNCTION(BlueprintImplementableEvent)
void OnHide();
virtual void OpenSubMenu(class USubMenu* submenu) override;
virtual void CloseSubMenu(class USubMenu* submenu) override;
private:
void m_OpenSubMenu(class USubMenu* submenu, bool transferFocus = true);
void m_CloseSubMenu(class USubMenu* submenu, bool persist = false);
void m_OnFadeOutComplete(UMenuScreenSubMenu* panel, int32 type);
bool m_sideViewActive;
UButtonHintBar* m_buttonHintBar;
UOverlay* m_menuContainer;
UOverlay* m_sideViewContainer;
UTextBlock* m_menuTitle;
UTextBlock* m_sideViewTitle;
UPROPERTY()
TArray<USideView*> m_currentSideViews;
friend class UMenuScreenSubMenu;
};
UCLASS()
class UMainMenuScreen : public UMenuScreen
{
GENERATED_BODY()
public:
};

View File

@@ -0,0 +1,122 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuScreenBase.h"
#include "PlayerControllerBase.h"
#include "SubMenu.h"
#include "WidgetBlueprintLibrary.h"
UMenuScreenBase::UMenuScreenBase(const FObjectInitializer& init)
: Super(init)
{
registerMenuActions = false;
}
void UMenuScreenBase::NativeConstruct()
{
Super::NativeConstruct();
APlayerControllerBase* pcb = Cast<APlayerControllerBase>(GetWorld()->GetFirstPlayerController());
if(registerMenuActions && pcb)
{
pcb->AddMenuInputItem(this);
}
}
void UMenuScreenBase::NativeDestruct()
{
Super::NativeDestruct();
APlayerControllerBase* pcb = Cast<APlayerControllerBase>(GetWorld()->GetFirstPlayerController());
if(registerMenuActions && pcb)
{
pcb->RemoveMenuInputItem(this);
}
}
void UMenuScreenBase::OpenSubMenu(class USubMenu* submenu)
{
if(m_subMenus.Contains(submenu))
{
GWARNING("Submenu already active");
return;
}
if(!submenu)
{
GWWARNING(L"Invalid submenu passed to OpenSubMenu");
return;
}
submenu->OnEnter(this);
if(m_subMenus.Num() > 0)
{
m_subMenus.Last()->OnSuspend(submenu, true);
}
m_subMenus.Add(submenu);
UWidgetBlueprintLibrary::SetFocusToGameViewport();
}
void UMenuScreenBase::CloseActiveSubMenu()
{
if(m_subMenus.Num() > 0)
{
CloseSubMenu(m_subMenus.Last());
}
else
{
GWWARNING("No SubMenus to close");
return;
}
}
void UMenuScreenBase::CloseSubMenu(class USubMenu* submenu)
{
if(!m_subMenus.Contains(submenu))
return;
submenu->OnLeave();
// Restore underlying menus if that was the top-most menu
if(m_subMenus.Num() > 1)
{
bool wasLast = m_subMenus.Last() == submenu;
m_subMenus.Remove(submenu);
if(wasLast)
{
m_subMenus.Last()->OnRestore(submenu);
}
}
else
{
// Remove last menu
m_subMenus.Remove(submenu);
}
UWidgetBlueprintLibrary::SetFocusToGameViewport();
}
TArray<class USubMenu*> UMenuScreenBase::GetSubMenus()
{
return m_subMenus;
}
class USubMenu* UMenuScreenBase::GetActiveSubMenu()
{
if(m_subMenus.Num() == 0)
return nullptr;
return m_subMenus.Last();
}
void UMenuScreenBase::SetButtonHintBar(class UButtonHintBar* bar)
{
m_buttonHintBar = bar;
}
class UButtonHintBar* UMenuScreenBase::GetButtonHintBar() const
{
return m_buttonHintBar;
}
void UMenuScreenBase::NativeOnMenuAction(EMenuActionBinding action)
{
if(m_subMenus.Num() > 0)
{
m_subMenus.Last()->NativeOnMenuAction(action);
}
Super::NativeOnMenuAction(action);
}

View File

@@ -0,0 +1,52 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuItemBase.h"
#include "Blueprint/UserWidget.h"
#include "MenuScreenBase.generated.h"
/**
* Base class for a menu item with controller navigation/input support
*/
UCLASS()
class UNREALPROJECT_API UMenuScreenBase : public UMenuItemBase
{
GENERATED_BODY()
public:
UMenuScreenBase(const FObjectInitializer& init);
void NativeConstruct() override;
void NativeDestruct() override;
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
virtual void OpenSubMenu(class USubMenu* submenu);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
void CloseActiveSubMenu();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
virtual void CloseSubMenu(class USubMenu* submenu);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
TArray<class USubMenu*> GetSubMenus();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
class USubMenu* GetActiveSubMenu();
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
void SetButtonHintBar(class UButtonHintBar* bar);
UFUNCTION(BlueprintCallable, Category = "MenuScreen")
class UButtonHintBar* GetButtonHintBar() const;
void OnItemSelected();
virtual void NativeOnMenuAction(EMenuActionBinding action) override;
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "MenuScreen")
bool registerMenuActions;
protected:
UPROPERTY()
TArray<class USubMenu*> m_subMenus;
UPROPERTY()
class UButtonHintBar* m_buttonHintBar;
};

View File

@@ -0,0 +1,139 @@
#include "UnrealProject.h"
#include "MenuSlider.h"
#include "SizeBorder.h"
void UMenuSliderButton::NativeConstruct()
{
m_button = Cast<UButton>(WidgetTree->FindWidget("Button"));
m_dragBorder = Cast<UBorder>(WidgetTree->FindWidget("DragBorder"));
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
m_button->OnPressed.AddDynamic(this, &UMenuSliderButton::m_OnPressed);
m_panel = Cast<UCanvasPanel>(GetParent());
m_dragging = false;
Super::NativeConstruct();
SetSelected(false);
}
void UMenuSliderButton::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
UCanvasPanelSlot* canvasSlot = Cast<UCanvasPanelSlot>(Slot);
canvasSlot->SetAlignment(FVector2D(0.0f, 0.0f));
if(m_parent)
{
float totalPixelWidth = m_parent->m_sizeBorder->pixelSize.X;
float totalWidth = m_parent->m_sizeBorder->viewportSize.X;
float totalHeight = m_parent->m_sizeBorder->viewportSize.Y;
float buttonSize = totalWidth * 0.2f;
totalPixelWidth -= totalPixelWidth * 0.2f;
totalWidth -= buttonSize;
if(m_dragging)
{
float mouseX, mouseY;
GetOwningPlayer()->GetMousePosition(mouseX, mouseY);
float delta = (mouseX - m_dragStart.X) / (totalPixelWidth);
m_parent->value = FMath::Clamp(m_dragStartValue + delta, 0.0f, 1.0f);
if(!m_dragBorder->IsHovered() && !m_button->IsHovered())
{
m_EndDrag();
}
UpdateText();
}
canvasSlot->SetPosition(FVector2D(m_parent->value * totalWidth, 0.0f));
canvasSlot->SetSize(FVector2D(buttonSize, totalHeight));
}
Super::NativeTick(MyGeometry, InDeltaTime);
}
void UMenuSliderButton::UpdateText()
{
FString txt = FString::FromInt((int)(m_parent->value * 100)) + "%";
m_text->SetText(FText::FromString(txt));
}
void UMenuSliderButton::SetSelected(bool selected)
{
if(selected)
{
m_button->SetBackgroundColor(FLinearColor(0.3f, 0.3f, 1.0f, 0.8f));
}
else
{
m_button->SetBackgroundColor(FLinearColor(0.7f, 0.7f, 0.7f, 0.8f));
}
}
void UMenuSliderButton::m_OnPressed()
{
float mouseX, mouseY;
if(m_parent)
{
GetOwningPlayer()->GetMousePosition(mouseX, mouseY);
m_dragStart = FVector2D(mouseX, mouseY);
m_dragStartValue = m_parent->value;
m_dragging = true;
m_dragBorder->SetVisibility(ESlateVisibility::Visible);
}
}
void UMenuSliderButton::m_EndDrag()
{
m_dragging = false;
m_dragBorder->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
FReply UMenuSliderButton::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
m_EndDrag();
return FReply::Unhandled();
}
UMenuSlider::UMenuSlider(const FObjectInitializer& init)
: Super(init)
{
stepGranularity = 1.0f / 25;
value = 0.5f;
}
void UMenuSlider::NativeConstruct()
{
Super::NativeConstruct();
m_sizeBorder = Cast<USizeBorder>(WidgetTree->FindWidget("SizeBorder"));
m_sliderButton = Cast<UMenuSliderButton>(WidgetTree->FindWidget("SliderButton"));
m_sliderButton->m_parent = this;
}
void UMenuSlider::SetValue(float _value)
{
value = FMath::Clamp(_value, 0.0f, 1.0f);
m_sliderButton->UpdateText();
}
void UMenuSlider::NativeOnMenuAction(EMenuActionBinding binding)
{
switch(binding)
{
case EMenuActionBinding::Left:
case EMenuActionBinding::Repeat_Left:
SetValue(value - stepGranularity);
break;
case EMenuActionBinding::Right:
case EMenuActionBinding::Repeat_Right:
SetValue(value + stepGranularity);
break;
}
Super::NativeOnMenuAction(binding);
}
void UMenuSlider::NativeOnSelectionChanged(bool selected, bool controller)
{
Super::NativeOnSelectionChanged(selected, controller);
if(m_sliderButton)
m_sliderButton->SetSelected(selected);
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include "MenuButton.h"
#include "MenuSlider.generated.h"
UCLASS()
class UMenuSliderButton : public UMenuItemBase
{
GENERATED_BODY()
public:
void NativeConstruct() override ;
void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
void UpdateText();
void SetSelected(bool selected);
private:
UFUNCTION()
void m_OnPressed();
void m_EndDrag();
FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
friend class UMenuSlider;
class UMenuSlider* m_parent;
UBorder* m_dragBorder;
UButton* m_button;
UTextBlock* m_text;
UCanvasPanel* m_panel;
FVector2D m_dragStart;
float m_dragStartValue;
bool m_dragging;
};
UCLASS()
class UMenuSlider : public UMenuButton
{
GENERATED_BODY()
public:
UMenuSlider(const FObjectInitializer& init);
void NativeConstruct() override;
void NativeOnMenuAction(EMenuActionBinding binding) override;
void NativeOnSelectionChanged(bool selected, bool controller) override;
UFUNCTION(BlueprintCallable, Category="Slider")
void SetValue(float value);
UPROPERTY(BlueprintReadOnly, Category = "Slider")
float value;
UPROPERTY(EditAnywhere, Category = "Slider")
float stepGranularity;
private:
friend class UMenuSliderButton;
class USizeBorder* m_sizeBorder;
class UMenuSliderButton* m_sliderButton;
};

View File

@@ -0,0 +1,78 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "OverlayMessageBox.h"
#include "ScreenOverlay.h"
void UOverlayMessageBoxItem::NativeConstruct()
{
m_label = Cast<UTextBlock>(WidgetTree->FindWidget("Label"));
onPressed.AddDynamic(this, &UOverlayMessageBoxItem::m_OnPressed);
Super::NativeConstruct();
}
void UOverlayMessageBoxItem::SetText(const FString& text)
{
if(!m_label)
{
GERROR("UOverlayMessageBoxItem Label not set on " + GetName());
return;
}
m_label->SetText(FText::FromString(text));
}
void UOverlayMessageBoxItem::m_OnPressed()
{
//Cast<UOverlayMessageBox>(GetSubMenu())->returnCode = index;
//UMenuScreenBase* screen = GetSubMenu()->GetScreen();
//check(m_info);
m_info->Close(index);
//if(screen)
// screen->CloseActiveSubMenu();
//else
// GERROR("screen == nullptr, should not happen");
}
void UOverlayMessageBox::NativeConstruct()
{
Super::NativeConstruct();
m_buttonContainer = Cast<UHorizontalBox>(WidgetTree->FindWidget("ButtonContainer"));
m_caption = Cast<UTextBlock>(WidgetTree->FindWidget("Caption"));
m_message = Cast<UTextBlock>(WidgetTree->FindWidget("Message"));
}
void UOverlayMessageBox::SetMessageBoxInfo(class UMsgBoxInfo* info)
{
if(!buttonItemClass)
{
GERROR("Button item class not set on " + GetName());
return;
}
m_buttonContainer->ClearChildren();
m_info = info;
UOverlayMessageBoxItem* selectItem = 0;
for(FString option : info->options)
{
UOverlayMessageBoxItem* item = CreateWidget<UOverlayMessageBoxItem>(GetWorld(), buttonItemClass);
item->m_info = info;
UHorizontalBoxSlot* slot = Cast<UHorizontalBoxSlot>(m_buttonContainer->AddChild(item));
slot->SetHorizontalAlignment(HAlign_Fill);
slot->SetVerticalAlignment(VAlign_Fill);
slot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
item->SetButtonText(option);
if(option == info->defaultOption)
selectItem = item;
else if(!selectItem)
selectItem = item;
}
m_caption->SetText(FText::FromString(info->caption));
m_message->SetText(FText::FromString(info->message));
RescanItems();
SelectNewItem(selectItem);
}

View File

@@ -0,0 +1,46 @@
// Project Lab - NHTV Igad
#pragma once
#include "SubMenu.h"
#include "MenuButton.h"
#include "OverlayMessageBox.generated.h"
UCLASS()
class UOverlayMessageBoxItem : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void SetText(const FString& text);
private:
void m_OnPressed();
friend class UOverlayMessageBox;
UTextBlock* m_label;
class UMsgBoxInfo* m_info;
};
UCLASS()
class UOverlayMessageBox : public USubMenu
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void SetMessageBoxInfo(class UMsgBoxInfo* info);
UPROPERTY(BlueprintReadWrite, Category = "MsgBox")
int32 returnCode;
UPROPERTY(EditDefaultsOnly, Category = "MsgBox")
TSubclassOf<UOverlayMessageBoxItem> buttonItemClass;
private:
UPROPERTY()
UHorizontalBox* m_buttonContainer;
UPROPERTY()
UTextBlock* m_caption;
UPROPERTY()
UTextBlock* m_message;
class UMsgBoxInfo* m_info;
};

View File

@@ -0,0 +1,24 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "OverlayProgressBar.h"
#include "ScreenOverlay.h"
void UOverlayProgressBar::NativeConstruct()
{
m_text = Cast<UTextBlock>(WidgetTree->FindWidget("Text"));
if(!m_text)
{
GERROR("No \"Text\" element in overlay progress bar");
}
Super::NativeConstruct();
}
void UOverlayProgressBar::SetOverlayInfo(class UOverlayInfo* info)
{
if(m_text)
{
m_text->SetText(FText::FromString(info->message));
}
}

View File

@@ -0,0 +1,23 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/SubMenu.h"
#include "OverlayProgressBar.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UOverlayProgressBar : public USubMenu
{
GENERATED_BODY()
public:
virtual void NativeConstruct();
// Updates the text,etc. on the progress bar overlay
void SetOverlayInfo(class UOverlayInfo* info);
private:
UTextBlock* m_text;
};

View File

@@ -0,0 +1,78 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScoreBoardSlot.h"
#include "DefaultPlayerState.h"
#include "GameStateBase.h"
#include "TeamData.h"
#include "ScoreBoard.h"
UScoreBoard::UScoreBoard(const FObjectInitializer& init) : Super(init)
{
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(TEXT("/Game/Assets/TeamData")).Object;
}
void UScoreBoard::Init(UVerticalBox* container)
{
m_container = container;
// Precreate the widgets so that we can utilize them later
if (IsValid(scoreBoardSlot) && IsValid(m_container))
{
for (int32 i = 0; i < 8; i++)
{
UScoreBoardSlot* widget = CreateWidget<UScoreBoardSlot>(GetWorld(), scoreBoardSlot);
m_container->AddChild(widget);
m_slots.Add(widget);
widget->SetVisibility(ESlateVisibility::Hidden);
}
}
}
void UScoreBoard::UpdateScoreBoard(int32 sort)
{
UWorld* world = GetWorld();
if (!IsValid(world)) return;
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
if (!IsValid(gameState)) return;
TArray<APlayerStateBase*> players = gameState->GetPlayers();
// Fetch and update
TArray<ADefaultPlayerState*> playersSorted;
for (int32 i = 0; i < players.Num(); i++)
{
ADefaultPlayerState* const state = Cast<ADefaultPlayerState>(players[i]);
if (!IsValid(state)) continue;
state->UpdatePersona();
playersSorted.Add(state);
}
// Sort based on category
if(sort == 0)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.nickname < j.nickname; });
else if(sort == 1)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.kills > j.kills; });
else if (sort == 2)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return i.deaths > j.deaths; });
else if (sort == 3)
playersSorted.Sort([](const ADefaultPlayerState& i, const ADefaultPlayerState& j)->bool { return float(i.kills) / float(i.deaths) > float(j.kills) / float(j.deaths); });
// Activate the slots and set them to the person
for (int32 i = 0; i < m_slots.Num(); i++)
{
if (i >= playersSorted.Num())
{
m_slots[i]->SetVisibility(ESlateVisibility::Hidden);
continue;
}
else
m_slots[i]->SetVisibility(ESlateVisibility::Visible);
ADefaultPlayerState* const state = playersSorted[i];
m_slots[i]->SetVisibility(ESlateVisibility::Visible);
m_slots[i]->Update(state->avatar, FText::FromString(state->nickname), state->kills, state->deaths, teamData->GetTeamColor(state->GetTeam()));
}
}

View File

@@ -0,0 +1,34 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "ScoreBoard.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UScoreBoard : public UUserWidget
{
GENERATED_BODY()
public:
UScoreBoard(const FObjectInitializer& init);
UPROPERTY(EditAnywhere, Category = "Score Board")
TSubclassOf<class UScoreBoardSlot> scoreBoardSlot;
UFUNCTION(BlueprintCallable, Category = "Score Board")
void Init(class UVerticalBox* container);
UFUNCTION(BlueprintCallable, Category = "Score Board")
void UpdateScoreBoard(int32 sort);
UPROPERTY(BlueprintReadonly, Category = "SCore Board")
class UTeamData* teamData;
private:
class UVerticalBox* m_container;
TArray<class UScoreBoardSlot*> m_slots;
};

View File

@@ -0,0 +1,4 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScoreBoardSlot.h"

View File

@@ -0,0 +1,19 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "ScoreBoardSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UScoreBoardSlot : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent, Category = "Score Board Slot")
void Update(UTexture2D* image, const FText& name, int32 kills, int32 deaths, const FLinearColor& color);
};

View File

@@ -0,0 +1,195 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "ScreenOverlay.h"
#include "OverlayMessageBox.h"
#include "OverlayProgressBar.h"
UScreenOverlay::UScreenOverlay(const FObjectInitializer& init)
: Super(init)
{
}
void UScreenOverlay::NativeConstruct()
{
Super::NativeConstruct();
m_msgBox = Cast<UOverlayMessageBox>(WidgetTree->FindWidget("MessageBox"));
m_progressBar = Cast<UOverlayProgressBar>(WidgetTree->FindWidget("ProgressBar"));
m_background = Cast<UBorder>(WidgetTree->FindWidget("Background"));
check(m_msgBox);
check(m_progressBar);
check(m_background);
UpdateOverlays();
UpdateMsgBoxes();
UpdateVisiblity();
m_msgBox->onClosed.AddDynamic(this, &UScreenOverlay::OnMessageBoxClosed);
}
void UScreenOverlay::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
// Check all active screen overlays, remove the inactive ones
for(int32 i = 0; i < m_overlayItems.Num();)
{
if(m_overlayItems[i]->m_remove)
{
m_overlayItems.RemoveAt(i);
UpdateOverlays();
UpdateVisiblity();
}
else
{
i++;
}
}
// Check the currently shown message box, remove it if inactive
UMsgBoxInfo* msgBox = GetMessageBoxItem();
if(msgBox && msgBox->m_remove)
{
if(msgBox->onClosed.IsBound())
{
msgBox->onClosed.Execute(msgBox->m_closeResult);
}
m_messageBoxItems.Remove(msgBox);
UpdateMsgBoxes();
UpdateVisiblity();
// Show remaining message boxes
if(m_messageBoxItems.Num() > 0)
{
OpenSubMenu(m_msgBox);
}
}
}
UOverlayInfo* UScreenOverlay::ShowOverlay(FString message)
{
// Add a new overlay item to the list
UOverlayInfo* info = NewObject<UOverlayInfo>();
info->message = message;
m_overlayItems.Add(info);
// Update Widget blueprint
UpdateOverlays();
UpdateVisiblity();
return info;
}
UMsgBoxInfo* UScreenOverlay::ShowMessageBox(FString caption, FString message, TArray<FString> options, FString def /*= FString()*/)
{
// Add a new message box at the end of the queue
UMsgBoxInfo* info = NewObject<UMsgBoxInfo>();
info->message = message;
info->caption = caption;
info->options = options;
info->defaultOption = def;
m_messageBoxItems.Add(info);
// Update Widget blueprint
UpdateMsgBoxes();
UpdateVisiblity();
OpenSubMenu(m_msgBox);
return info;
}
UMsgBoxInfo* UScreenOverlay::ShowMessageBoxCallback(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString def /*= ""*/)
{
// Add a new message box at the end of the queue
UMsgBoxInfo* info = NewObject<UMsgBoxInfo>();
info->message = message;
info->caption = caption;
info->options = options;
info->onClosed = cb;
info->defaultOption = def;
m_messageBoxItems.Add(info);
// Update Widget blueprint
UpdateMsgBoxes();
UpdateVisiblity();
OpenSubMenu(m_msgBox);
return info;
}
void UScreenOverlay::OnMessageBoxClosed()
{
m_messageBoxItems.Last()->Close(m_msgBox->returnCode);
}
TArray<UOverlayInfo*>& UScreenOverlay::GetOverlayItems()
{
return m_overlayItems;
}
UMsgBoxInfo* UScreenOverlay::GetMessageBoxItem()
{
if(m_messageBoxItems.Num() == 0)
{
return nullptr;
}
return m_messageBoxItems[0];
}
void UScreenOverlay::UpdateOverlays()
{
if(m_overlayItems.Num() != 0)
{
m_progressBar->SetVisibility(ESlateVisibility::Visible);
m_progressBar->SetOverlayInfo(m_overlayItems.Last());
}
else
{
m_progressBar->SetVisibility(ESlateVisibility::Hidden);
}
}
void UScreenOverlay::UpdateMsgBoxes()
{
if(m_messageBoxItems.Num() != 0)
{
m_msgBox->SetMessageBoxInfo(GetMessageBoxItem());
m_msgBox->SetVisibility(ESlateVisibility::Visible);
}
else
{
m_msgBox->SetVisibility(ESlateVisibility::Hidden);
}
}
void UScreenOverlay::UpdateVisiblity()
{
if(m_messageBoxItems.Num() != 0 || m_overlayItems.Num() != 0)
{
m_background->SetVisibility(ESlateVisibility::Visible);
blockInput = true;
}
else
{
m_background->SetVisibility(ESlateVisibility::Hidden);
blockInput = false;
}
}
UOverlayQueueItem::UOverlayQueueItem(const FObjectInitializer& init)
: Super(init)
{
m_remove = false;
}
void UOverlayQueueItem::Close(int32 result)
{
m_remove = true;
m_closeResult = result;
}
UMsgBoxInfo::UMsgBoxInfo(const FObjectInitializer& init)
: Super(init)
{
message = "nothing";
options.Add("OK");
}
UOverlayInfo::UOverlayInfo(const FObjectInitializer& init)
: Super(init)
{
blockInput = true;
message = "nothing";
}

View File

@@ -0,0 +1,104 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreenBase.h"
#include "ScreenOverlay.generated.h"
UENUM(BlueprintType)
enum class EMessageBoxType : uint8
{
Ok,
OkCancel,
YesNo
};
UCLASS()
class UOverlayQueueItem : public UObject
{
GENERATED_BODY()
public:
UOverlayQueueItem(const FObjectInitializer& init);
// Close this overlay item
UFUNCTION(BlueprintCallable, Category = "Overlay")
void Close(int32 result = 0);
UPROPERTY()
FOverlayItemClosed onClosed;
private:
friend class UScreenOverlay;
bool m_remove;
int32 m_closeResult;
};
UCLASS()
class UMsgBoxInfo : public UOverlayQueueItem
{
GENERATED_BODY()
public:
UMsgBoxInfo(const FObjectInitializer& init);
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString caption;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString message;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
TArray<FString> options;
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString defaultOption;
};
UCLASS()
class UOverlayInfo : public UOverlayQueueItem
{
GENERATED_BODY()
public:
UOverlayInfo(const FObjectInitializer& init);
UPROPERTY(BlueprintReadOnly, Category = "MsgBox")
FString message;
UPROPERTY(BlueprintReadOnly, Category = "Overlay")
bool blockInput;
};
UCLASS()
class UNREALPROJECT_API UScreenOverlay : public UMenuScreenBase
{
GENERATED_BODY()
public:
UScreenOverlay(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "Overlay")
UOverlayInfo* ShowOverlay(FString message);
UMsgBoxInfo* ShowMessageBox(FString caption, FString message, TArray<FString> options, FString def = "");
UMsgBoxInfo* ShowMessageBoxCallback(FOverlayItemClosed cb, FString caption, FString message, TArray<FString> options, FString def);
//UFUNCTION(BlueprintCallable, Category = "Overlay")
//UMsgBoxInfo* ShowMessageBox(FOnOverlayItemClosedSingle cb, FString caption, FString message, TArray<FString> options, FString def = "");
UFUNCTION()
void OnMessageBoxClosed();
UFUNCTION(BlueprintCallable, Category = "Overlay")
TArray<UOverlayInfo*>& GetOverlayItems();
UFUNCTION(BlueprintCallable, Category = "Overlay")
UMsgBoxInfo* GetMessageBoxItem();
private:
void UpdateOverlays();
void UpdateMsgBoxes();
void UpdateVisiblity();
UPROPERTY()
class UOverlayMessageBox* m_msgBox;
UPROPERTY()
class UOverlayProgressBar* m_progressBar;
UPROPERTY()
class UBorder* m_background;
UPROPERTY()
TArray<UOverlayInfo*> m_overlayItems;
UPROPERTY()
TArray<UMsgBoxInfo*> m_messageBoxItems;
};

View File

@@ -0,0 +1,10 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SelectButton.h"
USelectButton::USelectButton(const FObjectInitializer& init) : UMenuButton(init)
{
selected = 0;
}

View File

@@ -0,0 +1,22 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "SelectButton.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USelectButton : public UMenuButton
{
GENERATED_BODY()
public:
USelectButton(const FObjectInitializer& init);
UPROPERTY(BlueprintReadWrite, Category = "SelectButton")
int32 selected;
};

View File

@@ -0,0 +1,130 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SkillSelector.h"
#include "SkillTreeObject.h"
#include "SkillObject.h"
#include "SkillWidget.h"
#include "SkillSelectorItem.h"
#include "SkillTreeWidget.h"
#include "BaseSkillObject.h"
#include "AbilityInfo.h"
TSubclassOf<class USkillSelector> defaultSkillSelector;
USkillSelector::USkillSelector(const FObjectInitializer& init)
: Super(init)
{
defaultSkillSelector = ConstructorHelpers::FClassFinder<USkillSelector>(L"/Game/Assets/GUI/Components/WEEGEE_SkillSelector").Class;
}
void USkillSelector::NativeConstruct()
{
Super::NativeConstruct();
m_selectedItem = 0;
TArray<UWidget*> swidgets;
m_scrollBox = WidgetTree->FindWidget<UScrollBox>("ScrollBox");
m_border = WidgetTree->FindWidget<UBorder>("Border");
m_sizeRoot = WidgetTree->FindWidget<USizeBox>("SizeRoot");
if(!m_sizeRoot)
{
GWERROR(L"USkillSelector doesn't have a SizeRoot set");
}
if(!m_scrollBox)
{
GWERROR(L"USkillSelector doesn't have a ScrollBox set");
}
if(!ItemWidgetTemplate)
{
GWERROR(L"USkillSelector doesn't have a Item Widget Template set");
return;
}
}
void USkillSelector::NativeOnMenuAction(EMenuActionBinding binding)
{
switch(binding)
{
case EMenuActionBinding::Left:
SetSelection(m_selectedItem - 1);
break;
case EMenuActionBinding::Right:
SetSelection(m_selectedItem + 1);
break;
default:
Super::NativeOnMenuAction(binding);
break;
}
}
void USkillSelector::SetSelection(int32 idx)
{
if(idx < 0 || idx >= m_scrollBox->GetChildrenCount())
return;
if(m_selectedItem < m_scrollBox->GetChildrenCount())
SetItemSelected(m_selectedItem, false);
SetItemSelected(idx, true);
m_selectedItem = idx;
if(m_sizeRoot && m_scrollBox)
{
m_scrollBox->ScrollWidgetIntoView(m_items[m_selectedItem], true);
}
}
void USkillSelector::SetItemSelected(int32 item, bool selected)
{
m_items[item]->NativeOnSelectionChanged(selected, false);
if(selected)
onSkillSelectionChanged.Broadcast(m_items[item]);
}
void USkillSelector::SetSkillTree(class USkillTreeWidget* skillTree)
{
if(!skillTree)
return;
this->skillTree = skillTree;
USkillTreeObject* treeObject = skillTree->skillTreeAsset;
if(!m_scrollBox)
return;
if(!ItemWidgetTemplate)
return;
m_scrollBox->ClearChildren();
m_items.SetNum(0);
uint32 itemIndex = 0;
for(int32 i = 0; i < treeObject->skills.Num(); i++)
{
UBaseSkillObject* skill = treeObject->skills[i]->GetDefaultObject<UBaseSkillObject>();
if(!skillShapeFilter.Contains(skill->skillShapeType))
continue;
USkillSelectorItem* widget = CreateWidget<USkillSelectorItem>(GetWorld(), ItemWidgetTemplate);
widget->baseSkillObject = skill;
widget->index = itemIndex++;
widget->parent = this;
widget->skillTree = skillTree;
m_scrollBox->AddChild(widget);
m_items.Add(widget);
}
SetSelection(FMath::Min(m_selectedItem, m_items.Num() - 1));
}
USkillWidget* USkillSelector::GetSelectedSkillWidget()
{
if(m_selectedItem >= m_items.Num())
return nullptr;
return m_items[m_selectedItem]->GetSkillWidget();
}
UBaseSkillObject* USkillSelector::GetSelectedSkillAsset()
{
if(m_selectedItem >= m_items.Num())
return nullptr;
return m_items[m_selectedItem]->baseSkillObject;
}

View File

@@ -0,0 +1,54 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuButton.h"
#include "BaseSkillObject.h"
#include "SkillSelector.generated.h"
extern TSubclassOf<class USkillSelector> defaultSkillSelector;
UCLASS()
class UNREALPROJECT_API USkillSelector : public UMenuButton
{
GENERATED_BODY()
public:
USkillSelector(const FObjectInitializer& init);
void NativeConstruct() override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
UFUNCTION(BlueprintCallable, Category="Skill Selector")
void SetSkillTree(class USkillTreeWidget* skillTree);
UPROPERTY(BlueprintReadOnly, Category = "UI")
class USkillTreeWidget* skillTree;
UPROPERTY(EditAnywhere, Category = "UI")
TArray<ESkillShapeType> skillShapeFilter;
int32 GetSelection() const { return m_selectedItem; }
void SetSelection(int32 idx);
void SetItemSelected(int32 item, bool selected);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSkillSelectionChanged, USkillSelectorItem*, item);
UPROPERTY(BlueprintAssignable)
FOnSkillSelectionChanged onSkillSelectionChanged;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
TSubclassOf<class USkillSelectorItem> ItemWidgetTemplate;
UFUNCTION(BlueprintCallable, Category = UI)
class USkillWidget* GetSelectedSkillWidget();
UFUNCTION(BlueprintCallable, Category = UI)
class UBaseSkillObject* GetSelectedSkillAsset();
private:
UPROPERTY()
TArray<class USkillSelectorItem*> m_items;
int32 m_selectedItem;
USizeBox* m_sizeRoot;
UScrollBox* m_scrollBox;
UBorder* m_border;
};

View File

@@ -0,0 +1,76 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SkillSelectorItem.h"
#include "SkillSelector.h"
#include "SkillWidget.h"
#include "SkillTreeWidget.h"
void USkillSelectorItem::NativeConstruct()
{
Super::NativeConstruct();
m_skillWidget = WidgetTree->FindWidget<USkillWidget>("InnerSkillWidget");
if(!m_skillWidget)
{
GWERROR(L"InnerSkillWidget not found on " + GetName());
return;
}
else
{
m_skillWidget->skillAsset = baseSkillObject;
m_skillWidget->parent = skillTree;
m_skillWidget->UpdateSkill();
}
if(m_button)
{
m_button->OnPressed.AddDynamic(this, &USkillSelectorItem::m_OnPressed1);
}
else
{
GWERROR(L"No button found on " + GetName());
}
}
void USkillSelectorItem::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
if(skillTree->IsUsingSkill(baseSkillObject))
{
this->SetIsEnabled(false);
}
else
{
this->SetIsEnabled(true);
}
}
void USkillSelectorItem::NativeOnSelectionChanged(bool selected, bool controller)
{
if(selected)
{
m_button->SetBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.5f));
}
else
{
m_button->SetBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.0f));
}
}
void USkillSelectorItem::m_OnPressed1()
{
if(!GetIsEnabled())
return;
if(parent)
{
USubMenu* menu = parent->GetSubMenu();
if(menu)
menu->SelectNewItem(parent);
//if(parent->GetSelection() == index)
skillTree->NewSkill(m_skillWidget);
parent->SetSelection(index);
//else
}
}

View File

@@ -0,0 +1,50 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuButton.h"
#include "SkillSelectorItem.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USkillSelectorItem : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void NativeOnSelectionChanged(bool selected, bool controller) override;
//virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
int32 index;
class USkillSelector* parent;
class UBaseSkillObject* baseSkillObject;
UPROPERTY(BlueprintReadOnly, Category = UI)
class USkillTreeWidget* skillTree;
UPROPERTY(EditDefaultsOnly, Category = UI)
TSubclassOf<class USkillWidget> skillWidget;
UFUNCTION(BlueprintCallable, Category = UI)
USkillSelector* GetSelector() const
{
return parent;
}
UFUNCTION(BlueprintCallable, Category = UI)
class USkillWidget* GetSkillWidget()
{
return m_skillWidget;
}
private:
UFUNCTION()
void m_OnPressed1();
class USkillWidget* m_skillWidget;
};

View File

@@ -0,0 +1,96 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SplashScreen.h"
#include "SplashScreenItem.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
USplashScreen::USplashScreen(const FObjectInitializer& init)
: Super(init)
{
}
void USplashScreen::NativeConstruct()
{
m_currentItem = -1;
Super::NativeConstruct();
}
void USplashScreen::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(m_currentItem == -1)
return;
if(m_currentItem >= m_activeScreens.Num())
{
OnEnd();
// Send done event
onDone.Broadcast();
m_currentItem = -1;
}
else
{
m_switcher->SetActiveWidget(m_activeScreens[m_currentItem]);
if(m_activeScreens[m_currentItem]->GetRate() >= 1.0f)
{
m_activeScreens[m_currentItem]->End();
m_currentItem++;
if(m_currentItem < m_activeScreens.Num())
{
m_activeScreens[m_currentItem]->Start();
}
}
}
}
void USplashScreen::NativeOnMenuAction(EMenuActionBinding binding)
{
if(binding == EMenuActionBinding::Start)
{
if(m_currentItem >= 0 && m_currentItem < m_activeScreens.Num())
{
m_activeScreens[m_currentItem]->Skip();
}
}
}
void USplashScreen::OnEnd_Implementation()
{
}
void USplashScreen::Init(UWidgetSwitcher* witcher)
{
m_switcher = witcher;
m_currentItem = 0;
for(int32 i = 0; i < splashItems.Num(); i++)
{
USplashScreenItem* item = CreateWidget<USplashScreenItem>(GetWorld(), splashItems[i]);
m_activeScreens.Add(item);
m_switcher->AddChild(item);
if(i == 0)
{
item->Start();
if((i + 1) == splashItems.Num())
item->fadeEnabled = false;
m_switcher->SetActiveWidget(item);
}
}
}
float USplashScreen::GetFade() const
{
if(m_currentItem == -1)
return 0.0f;
if(m_currentItem >= m_activeScreens.Num())
{
return 0.0f;
}
if((m_currentItem + 1) == m_activeScreens.Num())
{
return m_activeScreens[m_currentItem]->GetFadeOut();
}
return 1.0f;
}

View File

@@ -0,0 +1,45 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuScreenBase.h"
#include "SplashScreen.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USplashScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
USplashScreen(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
UFUNCTION(BlueprintNativeEvent, Category = "Splash")
void OnEnd();
UFUNCTION(BlueprintCallable, Category="Splash")
void Init(UWidgetSwitcher* witcher);
UFUNCTION(BlueprintCallable, Category="Splash")
float GetFade() const;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category="Splash")
TArray<TSubclassOf<class USplashScreenItem>> splashItems;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSplashScreenDone);
UPROPERTY(BlueprintAssignable, Category = "Splash")
FOnSplashScreenDone onDone;
public:
UPROPERTY()
TArray<class USplashScreenItem*> m_activeScreens;
int32 m_currentItem;
UWidgetSwitcher* m_switcher;
};

View File

@@ -0,0 +1,104 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SplashScreenItem.h"
#include "MediaTexture.h"
USplashScreenItem::USplashScreenItem(const FObjectInitializer& init)
: Super(init)
{
duration = 2.0f;
fadeDuration = 0.5f;
m_lifetime = 0.0f;
fadeEnabled = true;
}
void USplashScreenItem::NativeConstruct()
{
if(duration <= 0.0f)
{
GWWARNING(L"Splash screen duration is zero! " + GetName());
duration = 1.0f;
}
if(fadeDuration * 2.0f > duration)
{
GWWARNING(L"Splash screen fade time is greater than duration! " + GetName());
fadeDuration = duration * 0.25f;
}
m_started = false;
Super::NativeConstruct();
}
void USplashScreenItem::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
if(m_started)
m_lifetime += InDeltaTime;
Super::NativeTick(MyGeometry, InDeltaTime);
}
void USplashScreenItem::OnStarted_Implementation()
{
}
void USplashScreenItem::OnEnd_Implementation()
{
}
void USplashScreenItem::Skip()
{
if((m_lifetime + fadeDuration) < duration)
{
m_lifetime = duration - fadeDuration;
}
}
void USplashScreenItem::Start()
{
if(!m_started)
{
m_started = true;
m_lifetime = 0.0f;
OnStarted();
}
}
void USplashScreenItem::End()
{
if(m_started)
{
OnEnd();
m_started = false;
}
}
FVector2D USplashScreenItem::GetMediaSize(UMediaTexture* mediaTexture) const
{
if(!mediaTexture)
return FVector2D();
return FVector2D(mediaTexture->GetSurfaceWidth(), mediaTexture->GetSurfaceHeight());
}
float USplashScreenItem::GetFade() const
{
float base = GetFadeOut();
if(m_lifetime < fadeDuration)
{
base *= m_lifetime / fadeDuration;
}
return base;
}
float USplashScreenItem::GetFadeOut() const
{
if((duration - m_lifetime) < fadeDuration)
{
return FMath::Min((duration - m_lifetime) / fadeDuration, 1.0f);
}
else
{
return 1.0f;
}
}
float USplashScreenItem::GetRate() const
{
return FMath::Clamp(m_lifetime / duration, 0.0f, 1.0f);
}

View File

@@ -0,0 +1,52 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuItemBase.h"
#include "SplashScreenItem.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USplashScreenItem : public UMenuItemBase
{
GENERATED_BODY()
public:
USplashScreenItem(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintNativeEvent, Category = "SplashItem")
void OnStarted();
UFUNCTION(BlueprintNativeEvent, Category = "SplashItem")
void OnEnd();
UFUNCTION(BlueprintCallable, Category = "SplashItem")
void Skip();
void Start();
void End();
UFUNCTION(BlueprintCallable, Category="Media")
FVector2D GetMediaSize(class UMediaTexture* mediaTexture) const;
UFUNCTION(BlueprintCallable, Category="SplashItem")
float GetFade() const;
UFUNCTION(BlueprintCallable, Category = "SplashItem")
float GetFadeOut() const;
UFUNCTION(BlueprintCallable, Category="SplashItem")
float GetRate() const;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "SplashItem")
float fadeDuration;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="SplashItem")
float duration;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SplashItem")
bool fadeEnabled;
private:
bool m_started;
float m_lifetime;
};

View File

@@ -0,0 +1,10 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "StartPromptScreen.h"
void UStartPromptScreen::Close()
{
onClosed.Broadcast();
OnClose();
}

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuScreenBase.h"
#include "StartPromptScreen.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UStartPromptScreen : public UMenuScreenBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="Start Prompt")
void Close();
UFUNCTION(BlueprintImplementableEvent)
void OnClose();
DECLARE_MULTICAST_DELEGATE(FClosedEvent)
FClosedEvent onClosed;
};

View File

@@ -0,0 +1,319 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SubMenu.h"
#include "PlayerControllerBase.h"
#include "MenuButton.h"
#include "DefaultGameInstance.h"
#include "MenuScreenBase.h"
#include "EffectFunctionLibrary.h"
#include "ScreenOverlay.h"
USoundBase* confirmAudioClass;
USoundBase* backAudioClass;
USoundBase* selectionChangedAudioClass;
USoundBase* clickAudioClass;
USubMenu::USubMenu(const FObjectInitializer& init)
{
selectionChangedAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select2")).Object;
backAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select0")).Object;
confirmAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select1")).Object;
clickAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/Sound/T_SkillDrop")).Object;
}
void USubMenu::NativeConstruct()
{
m_focus = false;
m_layoutDirection = 0;
container = Cast<UPanelWidget>(WidgetTree->FindWidget("SubMenuContainer"));
if(!container)
{
container = Cast<UPanelWidget>(GetRootWidget());
if(!container)
{
GERROR("No \"Container\" found in widget " + GetName() + ", root widget was not a PanelWidget");
}
}
Super::NativeConstruct();
m_selectedItem = 0;
RescanItems();
}
void USubMenu::NativeDestruct()
{
Super::NativeDestruct();
}
void USubMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if(HasFocus())
{
for(UMenuItemBase* item : m_items)
{
item->FocusTick(InDeltaTime);
}
}
}
void USubMenu::m_ScanContainer(UWidget* widget, int32 depth /*= 0*/)
{
// Loop through panel widgets
UPanelWidget* panel = Cast<UPanelWidget>(widget);
if(panel)
{
for(int32 i = 0; i < panel->GetChildrenCount(); i++)
{
m_ScanContainer(panel->GetChildAt(i), depth+1);
}
return;
}
// Process Menu items
UMenuItemBase* item = Cast<UMenuItemBase>(widget);
if(item)
{
bool hidden = item->GetVisibility() == ESlateVisibility::Hidden || item->GetVisibility() == ESlateVisibility::Collapsed;
if(!hidden)
{
m_items.Add(item);
// Assign item to a submenu
item->m_subMenu = this;
}
return;
}
UPanelUserWidget* puw = Cast<UPanelUserWidget>(widget);
if(puw)
{
m_ScanContainer(puw->GetRootWidget());
return;
}
}
void USubMenu::RescanItems()
{
m_items.SetNum(0);
if(container)
{
if(Cast<UHorizontalBox>(container))
{
m_layoutDirection = 1;
}
else
{
m_layoutDirection = 0;
}
// Scan for buttons
m_ScanContainer(container);
int32 childCount = m_items.Num();
// Clamp selection
m_selectedItem = FMath::Clamp(m_selectedItem, 0, childCount - 1);
// Set active selection
for(int32 i = 0; i < childCount; i++)
{
UMenuItemBase* item = m_items[i];
item->index = i;
if(i == m_selectedItem)
{
item->NativeOnSelectionChanged(true, true);
}
else
{
item->NativeOnSelectionChanged(false, true);
}
}
// Reselect single items
if(childCount == 1)
{
m_items[0]->NativeOnSelectionChanged(false, true);
m_items[0]->NativeOnSelectionChanged(true, true);
}
if(childCount > 0)
{
// Selection event
onItemSelected.Broadcast(m_items[m_selectedItem]);
}
}
else
{
GWWARNING(L"SubMenu Container not set");
return;
}
}
void USubMenu::CloseSubMenu()
{
if(m_screen)
m_screen->CloseSubMenu(this);
}
void USubMenu::OnEnter(class UMenuScreenBase* screen)
{
check(screen);
m_screen = screen;
SetButtonHints();
onOpened.Broadcast();
m_SetFocus(true);
}
void USubMenu::OnLeave()
{
onClosed.Broadcast();
m_screen = nullptr;
m_SetFocus(false);
}
bool USubMenu::HasFocus() const
{
return m_focus;
}
void USubMenu::OnSuspend(USubMenu* newSubMenu, bool loseFocus)
{
if(loseFocus)
m_SetFocus(false);
onSuspend.Broadcast();
}
void USubMenu::OnRestore(USubMenu* removedMenu)
{
SetButtonHints();
m_SetFocus(true);
onRestore.Broadcast();
}
class UButtonHintBar* USubMenu::GetButtonHintBar() const
{
return GetScreen()->GetButtonHintBar();
}
class UCharacterSettings* USubMenu::GetCharacterSettings() const
{
return GetGameInstance()->GetCharacterSettings();
}
class UPrefs* USubMenu::GetPrefs() const
{
return GetGameInstance()->GetPrefs();
}
class UDefaultGameInstance* USubMenu::GetGameInstance() const
{
return Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
}
void USubMenu::NativeOnMenuAction(EMenuActionBinding binding)
{
if(m_items.Num() > 0)
m_items[m_selectedItem]->NativeOnMenuAction(binding);
switch(binding)
{
case EMenuActionBinding::Down:
if(m_items.Num() > 0 && m_layoutDirection == 0)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems()-1), true);
break;
case EMenuActionBinding::Up:
if(m_items.Num() > 0 && m_layoutDirection == 0)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems()-1), true);
break;
case EMenuActionBinding::Left:
if(m_items.Num() > 0 && m_layoutDirection == 1)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems() - 1), true);
break;
case EMenuActionBinding::Right:
if(m_items.Num() > 0 && m_layoutDirection == 1)
SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems() - 1), true);
break;
case EMenuActionBinding::Confirm:
break;
case EMenuActionBinding::Back:
onBack.Broadcast();
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), backAudioClass);
break;
}
Super::NativeOnMenuAction(binding);
}
void USubMenu::NativeOnSelectionConfirmed(UMenuItemBase* item)
{
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), clickAudioClass);
onItemSelectionConfirmed.Broadcast(m_items[m_selectedItem]);
}
UMenuItemBase* USubMenu::GetItem(int32 index) const
{
if(index >= m_items.Num() || index < 0)
{
GWWARNING(L"Item index out of range");
return nullptr;
}
return m_items[index];
}
int32 USubMenu::GetNumItems() const
{
return m_items.Num();
}
int32 USubMenu::GetSelectedItem() const
{
return m_selectedItem;
}
void USubMenu::SelectNewItemByIndex(int32 idx)
{
SelectNewItemByIndex(idx, false);
}
void USubMenu::SelectNewItemByIndex(int32 idx, bool controller)
{
if(idx >= m_items.Num() || idx < 0)
{
GWWARNING(L"Item index out of range");
return;
}
if(m_selectedItem != idx)
{
check(m_selectedItem < m_items.Num());
m_items[m_selectedItem]->NativeOnSelectionChanged(false, controller);
m_items[idx]->NativeOnSelectionChanged(true, controller);
m_selectedItem = idx;
onItemSelected.Broadcast(m_items[m_selectedItem]);
SetButtonHints();
UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), selectionChangedAudioClass);
}
}
void USubMenu::SelectNewItem(UMenuItemBase* item)
{
int32 idx = 0;
if (m_items.Find(item, idx))
{
SelectNewItemByIndex(idx);
}
}
int32 USubMenu::GetItemIndex(UMenuItemBase* item)
{
int32 idx = 0;
if (m_items.Find(item, idx))
return idx;
return -1;
}
UMenuScreenBase* USubMenu::GetScreen() const
{
return m_screen;
}
void USubMenu::m_SetFocus(bool focus)
{
m_focus = focus;
// Get Selected button
UMenuItemBase* item = GetItem(GetSelectedItem());
if (item)
{
item->NativeOnSelectionChanged(true, false);
}
}

View File

@@ -0,0 +1,127 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuItemBase.h"
#include "SubMenu.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMenuChange);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSubMenuItemSelected, UMenuItemBase*, item);
UCLASS()
class UNREALPROJECT_API UPanelUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
};
UCLASS()
class UNREALPROJECT_API USubMenu : public UMenuItemBase
{
GENERATED_BODY()
public:
USubMenu(const FObjectInitializer& init);
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
virtual void RescanItems();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void CloseSubMenu();
virtual void OnEnter(class UMenuScreenBase* screen);
virtual void OnLeave();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
bool HasFocus() const;
virtual void OnSuspend(USubMenu* newSubMenu, bool loseFocus);
virtual void OnRestore(USubMenu* removedMenu);
// Callback that gets called whenever the button hits should update when showing this menu
UFUNCTION(BlueprintImplementableEvent, Category = "SubMenu")
void SetButtonHints();
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UButtonHintBar* GetButtonHintBar() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UCharacterSettings* GetCharacterSettings() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UPrefs* GetPrefs() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
class UDefaultGameInstance* GetGameInstance() const;
virtual void NativeOnMenuAction(EMenuActionBinding binding) override;
virtual void NativeOnSelectionConfirmed(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category="SubMenu")
UMenuItemBase* GetItem(int32 itemIndex) const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetNumItems() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetSelectedItem() const;
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void SelectNewItemByIndex(int32 idx);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
void SelectNewItem(UMenuItemBase* item);
void SelectNewItemByIndex(int32 idx, bool controller);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
int32 GetItemIndex(UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "SubMenu")
UMenuScreenBase* GetScreen() const;
// Called when an item is selected
UPROPERTY(BlueprintAssignable, Category="SubMenu")
FSubMenuItemSelected onItemSelected;
// Called when a button is pressed
UPROPERTY(BlueprintAssignable, Category="SubMenu")
FSubMenuItemSelected onItemSelectionConfirmed;
// Called when the back button is pressed
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onBack;
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onClosed;
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onOpened;
// Called when another menu is opened on top of this menu
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onSuspend;
// Called when this menu is displayed again after a menu was closed on top of this
UPROPERTY(BlueprintAssignable, Category = "SubMenu")
FMenuChange onRestore;
// The panel widget that contains all the buttons selectable in this menu
UPROPERTY(BlueprintReadWrite, Category="SubMenu")
UPanelWidget* container;
protected:
void m_ScanContainer(UWidget* widget, int32 depth = 0);
// Set the allowance of mouse focus & presses of the buttons in this menu
void m_SetFocus(bool focus);
// The list of selectable items in the menu
UPROPERTY()
TArray<UMenuItemBase*> m_items;
private:
friend class UMenuItemBase;
int32 m_selectedItem;
// Horizontal or vertical input handling
int m_layoutDirection;
UPROPERTY()
class UMenuScreenBase* m_screen;
// True if this is the topmost screen and if the buttons in this screen are allowed to be pressed.
bool m_focus;
};

View File

@@ -0,0 +1,20 @@
#include "UnrealProject.h"
#include "MiniMap.h"
namespace MiniMap
{
void NodeBase::CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects)
{
if (objects.empty())
return;
for (auto iter = objects.begin(); iter != objects.end(); iter++)
{
MinimapHandle& obj = **iter;
if (FVector2D::DistSquared(obj.position, position) <= radius_sqr)
out_objects.Add(&obj);
}
}
}

View File

@@ -0,0 +1,325 @@
#pragma once
#include "UnrealProject.h"
#include <unordered_set>
using namespace std;
class ACharacterBase;
namespace MiniMap
{
struct Rect
{
Rect() = default;
Rect(float x, float y, float width, float height) : x(x), y(y), width(width), height(height) {}
Rect(const FVector2D& position, const FVector2D& size) : x(position.X), y(position.Y), width(size.X), height(size.Y) {}
float x;
float y;
float width;
float height;
inline FVector2D position() const
{
return FVector2D(x, y);
}
inline FVector2D size() const
{
return FVector2D(width, height);
}
inline bool contains(const FVector2D& pos) const
{
return pos.X >= x && pos.X <= (x + width) && pos.Y >= y && pos.Y <= (y + height);
}
};
inline Rect GetSubArea(const Rect& area, size_t idx)
{
const FVector2D half_size = area.size() / 2;
const FVector2D position = area.position();
switch (idx)
{
case 0: return Rect(position, half_size);
case 1: return Rect(position + FVector2D(half_size.X, 0), half_size);
case 2: return Rect(position + half_size, half_size);
case 3: return Rect(position + FVector2D(0, half_size.Y), half_size);
}
return area;
}
inline bool RectCircleOverlapSquared(const Rect& area, const FVector2D& position, float radius_sqr)
{
const FVector2D pos = area.position();
const float left = area.x;
const float bottom = area.y;
const float right = area.x + area.width;
const float top = area.y + area.height;
const float x = position.X > right ? right : position.X < left ? left : position.X;
const float y = position.Y > top ? top : position.Y < bottom ? bottom : position.Y;
return FVector2D::DistSquared(position, FVector2D(x, y)) <= radius_sqr;
}
class NonCopyable
{
public:
NonCopyable() = default;
NonCopyable(const NonCopyable& other) = delete;
NonCopyable& operator=(const NonCopyable& other) = delete;
};
class MinimapHandle;
class NodeBase : private NonCopyable
{
protected:
NodeBase(const Rect& area, NodeBase* parent) : area(area), parent(parent) {}
public:
virtual void AddObject(MinimapHandle& obj) = 0;
virtual void RemoveObject(MinimapHandle& obj) = 0;
virtual void ClearRecursive()
{
objects.clear();
}
virtual void ResizeRecursive(const Rect& area)
{
this->area = area;
}
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects);
unordered_set<MinimapHandle*> objects;
Rect area;
NodeBase* parent;
};
class MinimapHandle : private NonCopyable
{
public:
MinimapHandle() : character(nullptr), node(nullptr), root(nullptr) {}
MinimapHandle(const FVector2D& position) : character(nullptr), position(position), node(nullptr), root(nullptr) {}
~MinimapHandle()
{
}
FVector2D position;
class ::ACharacterBase* character;
NodeBase* node;
NodeBase* root;
};
template<size_t level> class TreeNode : public NodeBase
{
public:
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent), child0(GetSubArea(area, 0), this), child1(GetSubArea(area, 1), this), child2(GetSubArea(area, 2), this), child3(GetSubArea(area, 3), this) {}
typedef TreeNode<level - 1> child_type;
child_type child0;
child_type child1;
child_type child2;
child_type child3;
unordered_set<MinimapHandle*> child_objects;
virtual void AddObject(MinimapHandle& obj) override
{
child_type* children = &child0;
for (size_t i = 0; i < 4; i++)
{
if (children[i].area.contains(obj.position))
{
// Register to child
child_objects.emplace(&obj);
children[i].AddObject(obj);
return;
}
}
// Register to this node
objects.emplace(&obj);
obj.node = this;
}
virtual void RemoveObject(MinimapHandle& obj) override
{
// Check if present in children
auto find_child = child_objects.find(&obj);
if (find_child != child_objects.end())
{
child_type* children = &child0;
for (size_t i = 0; i < 4; i++)
children[i].RemoveObject(obj);
child_objects.erase(find_child);
}
else
{
// Remove from node
auto find = objects.find(&obj);
if (find != objects.end())
objects.erase(find);
}
}
virtual void ClearRecursive() override
{
child_type* children = &child0;
for (size_t i = 0; i < 4; i++)
children[i].ClearRecursive();
NodeBase::ClearRecursive();
child_objects.clear();
}
virtual void ResizeRecursive(const Rect& area) override
{
child_type* children = &child0;
for (size_t i = 0; i < 4; i++)
children[i].ResizeRecursive(GetSubArea(area, i));
NodeBase::ResizeRecursive(area);
}
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects) override
{
if (!child_objects.empty())
{
child_type* children = &child0;
for (size_t i = 0; i < 4; i++)
{
child_type& child = children[i];
if (RectCircleOverlapSquared(child.area, position, radius_sqr))
child.CircleOverlap(position, radius_sqr, out_objects);
}
}
NodeBase::CircleOverlap(position, radius_sqr, out_objects);
}
};
template<size_t level> class RootNode : public TreeNode<level>
{
public:
RootNode(const Rect& area, NodeBase* parent) : TreeNode<level>(area, parent)
{
}
virtual void AddObject(MinimapHandle& obj) override
{
TreeNode<level>::AddObject(obj);
all_objects.emplace(&obj);
}
virtual void RemoveObject(MinimapHandle& obj) override
{
TreeNode<level>::RemoveObject(obj);
auto find = all_objects.find(&obj);
if (find != all_objects.end())
all_objects.erase(find);
}
virtual void ClearRecursive() override
{
for (auto iter = all_objects.begin(); iter != all_objects.end(); iter++)
{
(*iter)->node = nullptr;
(*iter)->root = nullptr;
}
all_objects.clear();
TreeNode<level>::ClearRecursive();
}
void Reparent(MinimapHandle& obj)
{
TreeNode<level>::RemoveObject(obj);
TreeNode<level>::AddObject(obj);
}
unordered_set<MinimapHandle*> all_objects;
};
template<> class TreeNode<0> : public NodeBase
{
public:
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent)
{
}
void AddObject(MinimapHandle& obj) override
{
objects.emplace(&obj);
obj.node = this;
}
void RemoveObject(MinimapHandle& obj) override
{
auto find = objects.find(&obj);
if (find != objects.end())
objects.erase(find);
}
};
template<size_t depth> class QuadTree : private NonCopyable
{
public:
QuadTree() : m_root_node(Rect(0, 0, 1, 1), nullptr) {}
QuadTree(const Rect& area) : m_root_node(area, nullptr) {}
~QuadTree()
{
m_root_node.ClearRecursive();
}
void AddObject(MinimapHandle& obj)
{
if (obj.root == &m_root_node)
return;
if (obj.root != nullptr)
obj.root->RemoveObject(obj);
// Add object to root
m_root_node.AddObject(obj);
obj.root = &m_root_node;
}
void RemoveObject(MinimapHandle& obj)
{
if (obj.root == nullptr)
return;
if (obj.root != &m_root_node)
return;
// Remove from root
m_root_node.RemoveObject(obj);
obj.node = nullptr;
obj.root = nullptr;
}
void Update()
{
// Update positioning
for (auto iter = m_root_node.all_objects.begin(); iter != m_root_node.all_objects.end(); iter++)
{
MinimapHandle& obj = **iter;
if (!obj.node->area.contains(obj.position) && obj.node->parent)
m_root_node.Reparent(obj);
}
}
void Clear()
{
m_root_node->ClearRecursive();
}
void Resize(const Rect& area)
{
m_root_node.ResizeRecursive(area);
}
int32 Num() const
{
return int32(m_root_node.all_objects.size());
}
bool CircleOverlap(const FVector2D& position, float radius, TArray<MinimapHandle*>& out_objects)
{
out_objects.SetNum(0);
m_root_node.CircleOverlap(position, radius * radius, out_objects);
return out_objects.Num() > 0;
}
RootNode<depth> m_root_node;
};
}

View File

@@ -0,0 +1,10 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MiniMapIconWidget.h"
void UMiniMapIconWidget::SetObjectIcon_Implementation(UTexture2D* texture)
{
// No implementation
}

View File

@@ -0,0 +1,22 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "MiniMapIconWidget.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMiniMapIconWidget : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category = "Image")
UImage* image;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MiniMap")
void SetObjectIcon(class UTexture2D* texture);
};

View File

@@ -0,0 +1,15 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MiniMapVisionCircle.h"
UMiniMapVisionCircle::UMiniMapVisionCircle(const FObjectInitializer& init) : Super(init)
{
}
void UMiniMapVisionCircle::NativeConstruct()
{
Super::NativeConstruct();
}

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "MiniMapVisionCircle.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMiniMapVisionCircle : public UUserWidget
{
GENERATED_BODY()
public:
UMiniMapVisionCircle(const FObjectInitializer& init);
virtual void NativeConstruct() override;
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
void SetTexture(UTexture2D* texture);
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
void SetUV(const FVector2D& uv, const FVector2D& scale);
};

View File

@@ -0,0 +1,393 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MiniMapVolume.h"
#include "MiniMapIconWidget.h"
#include "MiniMapVisionCircle.h"
#include "NetworkPlayer.h"
#include "BossBase.h"
#include "NPCBase.h"
#include "KOTHBossSpawner.h"
#include "KOTHMinionSpawner.h"
#include "NetworkCharacter.h"
#include "DefaultGameState.h"
#include "DefaultPlayerState.h"
#include "DefaultPlayerController.h"
#include "MiniMapWidget.h"
#include "WidgetLayoutLibrary.h"
#if PLATFORM_SPECIFIC_WIN == 0
#include "HeatMapMetrics.h"
#endif
float AngleTowards(const FVector2D& from, const FVector2D& to)
{
const float angle_rad = FMath::Atan2(from.X - to.X, to.Y - from.Y) + PI;
return FMath::RadiansToDegrees(angle_rad);
}
UMiniMapWidget::UMiniMapWidget(const FObjectInitializer& init) : Super(init)
{
viewRadius = 2000;
}
void UMiniMapWidget::NativeConstruct()
{
Super::NativeConstruct();
// Find the minimap volume
ADefaultGameState* const gameState = Cast<ADefaultGameState>(GetWorld()->GetGameState());
if(IsValid(gameState))
{
for (TActorIterator<AMiniMapVolume> iter(GetWorld()); iter; ++iter)
{
AMiniMapVolume* const volume = *iter;
volume->SetActorRotation(FRotator(0, 0, 0));
const FVector pos = volume->GetActorLocation();
const FVector area = volume->area->GetScaledBoxExtent();
m_size = FMath::Max(area.X, area.Y);
m_min.X = -m_size + pos.X;
m_min.Y = -m_size + pos.Y;
m_max.X = m_size + pos.X;
m_max.Y = m_size + pos.Y;
m_size *= 2;
m_viewRadius = (viewRadius / (area.X * 2));
gameState->minimapQuadtree.Resize(MiniMap::Rect(FVector2D(m_min), FVector2D(m_size, m_size)));
return;
}
}
JERROR("Failed to locate Mini Map volume");
}
void UMiniMapWidget::NativeTick(const FGeometry& geometry, float deltaTime)
{
Super::NativeTick(geometry, deltaTime);
m_arrowAnim = FMath::Fmod(m_arrowAnim + deltaTime, 1.0f);
// Reset the state of all current widgets
{
// Set widgets hidden
for (int32 i = 0; i < m_miniMapIconWidgetPool.Num(); i++)
{
m_miniMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
// Reset rotations
FWidgetTransform trans = m_miniMapIconWidgetPool[i]->image->RenderTransform;
trans.Angle = 0;
m_miniMapIconWidgetPool[i]->image->SetRenderTransform(trans);
}
// Set existing arrows hidden
for (int32 i = 0; i < m_arrowMapIconWidgetPool.Num(); i++)
m_arrowMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
// Allocate the neccesairy vision circles
for (int32 i = 0; i < m_viewCircleWidgetPool.Num(); i++)
m_viewCircleWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
}
// Begin the rendering
UWorld* const world = GetWorld();
if (!IsValid(world)) return;
ADefaultGameState* const gameState = Cast<ADefaultGameState>(world->GetGameState());
if (!IsValid(gameState))
return;
// Get the local team
m_localTeam = -1;
AController* controller = world->GetGameInstance()->GetFirstLocalPlayerController();
ACharacterBase* localPlayer = nullptr;
if (IsValid(controller))
{
ADefaultPlayerController* const playerController = Cast<ADefaultPlayerController>(controller);
localPlayer = Cast<ACharacterBase>(playerController->GetPawn());
if (playerController->PlayerState)
m_localTeam = Cast<ADefaultPlayerState>(playerController->PlayerState)->GetTeam();
}
// Failed to fetch local team
if (m_localTeam == -1)
return;
gameState->minimapQuadtree.Update();
map<int32, unordered_set<ACharacterBase*>> drawActors;
unordered_set<ACharacterBase*>& friendlyPlayerSet = drawActors.emplace(m_localTeam, unordered_set<ACharacterBase*>()).first->second;
// Fetch the friendly viewpoints
for (TActorIterator<ACharacterBase> iter(world); iter; ++iter)
{
ACharacterBase* const character = *iter;
if (IsValid(character))
{
if (character->GetTeam() == m_localTeam)
friendlyPlayerSet.emplace(character);
}
}
// Fetch all the visible units around the friendly viewpoints
for (auto iter = friendlyPlayerSet.begin(); iter != friendlyPlayerSet.end(); iter++)
{
ACharacterBase* const character = (*iter);
FVector2D position = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
if (gameState->minimapQuadtree.CircleOverlap(position, character->visionRadius, m_handleBuffer))
{
for (int32 i = 0; i < m_handleBuffer.Num(); i++)
{
ACharacterBase* const visibleCharacter = m_handleBuffer[i]->character;
if(IsValid(visibleCharacter))
drawActors[visibleCharacter->GetTeam()].emplace(visibleCharacter);
}
}
}
// Fetch the minion camps
TArray<AKOTHSpawnerBase*> minionCamps;
AKOTHBossSpawner* bossCamp = nullptr;
FVector2D bossCampPos;
for (TActorIterator<AKOTHSpawnerBase> iter(world); iter; ++iter)
{
AKOTHSpawnerBase* camp = *iter;
if (camp->IsA<AKOTHBossSpawner>())
{
bossCamp = Cast<AKOTHBossSpawner>(camp);
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
bossCampPos = ConvertWorldToMinimap(charPos);
}
minionCamps.Add(camp);
}
// Draw the minion camps
m_widgetIndex = 0;
m_arrowIndex = 0;
m_circleIndex = 0;
for (int32 i = 0; i < minionCamps.Num(); i++)
{
UMiniMapIconWidget* const widget = m_AllocateWidget();
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
widget->SetVisibility(ESlateVisibility::Visible);
AKOTHSpawnerBase* const camp = minionCamps[i];
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
FVector2D relativePos = ConvertWorldToMinimap(charPos);
// Set sprite position
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
slot->SetSize(spriteSize);
SetObjectIcon(campIcon, widget);
FLinearColor color = enemyColor;
if (camp->team >= NPCTeam::Team1) // NPC Controlled
color = playerColor;
else if (int32(camp->team) == int32(m_localTeam)) // Friendly
color = allyColor;
else if (int32(camp->team) == 0) // Contested
color = neutralColor;
widget->image->Brush.TintColor = color;
// Draw arrows
if (bossCamp && int32(camp->team) > 0 && camp->team < NPCTeam::Team1 && camp != bossCamp)
{
const int32 arrowCount = int32(FVector2D::Distance(relativePos, bossCampPos) * 25);
if (arrowCount > 0)
{
const float step = 1.0f / float(arrowCount);
for (int32 i = 0; i < arrowCount; i++)
{
UMiniMapIconWidget* arrow = m_AllocateArrow();
arrow->SetVisibility(ESlateVisibility::Visible);
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(arrow->Slot);
// Lerp towards the target
color.A = arrowCount > 1 ? (i == 0 ? m_arrowAnim : i == arrowCount - 1 ? (1.f - m_arrowAnim) : 1) : 1;
const FVector2D setPos = FMath::Lerp(relativePos, bossCampPos, step * float(i) + step * m_arrowAnim);
FWidgetTransform trans = arrow->image->RenderTransform;
trans.Angle = AngleTowards(setPos, bossCampPos);
arrow->image->SetRenderTransform(trans);
arrow->image->Brush.TintColor = color;
const FVector2D spriteSize = geometry.GetLocalSize() * 0.04f;
slot->SetSize(spriteSize);
slot->SetPosition(geometry.GetLocalSize() * setPos - spriteSize * 0.5f);
}
}
}
}
// Generate draw order (Draw local team last)
TArray<int32> teamDrawOrder;
for (auto iter = drawActors.rbegin(); iter != drawActors.rend(); iter++)
{
if (iter->first == m_localTeam)
continue;
teamDrawOrder.Add(iter->first);
}
teamDrawOrder.Add(m_localTeam);
// Draw the actual characters to the minimap
int32 circleIndex = 0;
for (size_t i = 0; i < teamDrawOrder.Num(); i++)
{
const int32 team = teamDrawOrder[i];
auto& set = drawActors[team];
for (auto charIter = set.begin(); charIter != set.end(); charIter++)
{
ACharacterBase* const character = *charIter;
if (character == localPlayer)
continue;
UMiniMapIconWidget* const widget = m_AllocateWidget();
m_DrawCreature(geometry, widget, character);
}
}
// Draw the local player last
if (localPlayer)
m_DrawCreature(geometry, m_AllocateWidget(), localPlayer);
}
void UMiniMapWidget::m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character)
{
widget->SetVisibility(ESlateVisibility::Visible);
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
const int32 actorTeam = character->GetTeam();
const FVector2D charPos = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
FVector2D relativePos = ConvertWorldToMinimap(charPos);
// Rotation
const float drawPlayerAngle = character->GetActorRotation().Yaw;
// Set sprite position
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
slot->SetSize(spriteSize);
FWidgetTransform trans = widget->image->RenderTransform;
trans.Angle = 0;
// Set icon based on the object type
if (character->IsA<ABossBase>())
SetObjectIcon(bossIcon, widget);
else if (character->IsA<ANPCBase>())
SetObjectIcon(creatureIcon, widget);
else
{
trans.Angle = drawPlayerAngle;
SetObjectIcon(playerIcon, widget);
}
widget->image->SetRenderTransform(trans);
// Set the color
if (!(character->IsA<ANetworkPlayer>() && character->IsLocallyControlled()))
{
if (actorTeam != m_localTeam)
{
// Neutral or enemy?
if (actorTeam == 0)
widget->image->Brush.TintColor = neutralColor;
else
widget->image->Brush.TintColor = enemyColor;
}
else
widget->image->Brush.TintColor = allyColor;
}
else
widget->image->Brush.TintColor = playerColor;
// Update the view circle
if (actorTeam == m_localTeam)
{
UMiniMapVisionCircle* const circle = m_AllocateVisionCircle();
circle->SetVisibility(ESlateVisibility::Visible);
UCanvasPanelSlot* const circleSlot = Cast<UCanvasPanelSlot>(circle->Slot);
if (IsValid(circleSlot))
{
const float diameter = (character->visionRadius * 2) / m_size;
FVector2D size = geometry.GetLocalSize() * diameter;
size = FVector2D(FMath::RoundToFloat(size.X), FMath::RoundToFloat(size.Y));
FVector2D pos = geometry.GetLocalSize() * relativePos - size * 0.5f;
pos = FVector2D(FMath::RoundToFloat(pos.X), FMath::RoundToFloat(pos.Y));
circleSlot->SetPosition(pos);
circleSlot->SetSize(size);
circle->SetUV(pos / geometry.GetLocalSize(), size / geometry.GetLocalSize());
}
}
}
UMiniMapIconWidget* UMiniMapWidget::m_AllocateWidget()
{
UMiniMapIconWidget* widget = nullptr;
if (m_widgetIndex >= m_miniMapIconWidgetPool.Num())
{
widget = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
iconLayer->AddChild(widget);
m_miniMapIconWidgetPool.Add(widget);
}
else
widget = m_miniMapIconWidgetPool[m_widgetIndex];
m_widgetIndex++;
return widget;
}
UMiniMapIconWidget* UMiniMapWidget::m_AllocateArrow()
{
UMiniMapIconWidget* arrow = nullptr;
if (m_arrowIndex >= m_arrowMapIconWidgetPool.Num())
{
arrow = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
arrowLayer->AddChild(arrow);
m_arrowMapIconWidgetPool.Add(arrow);
SetObjectIcon(arrowIcon, arrow);
}
else
arrow = m_arrowMapIconWidgetPool[m_arrowIndex];
m_arrowIndex++;
return arrow;
}
UMiniMapVisionCircle* UMiniMapWidget::m_AllocateVisionCircle()
{
UMiniMapVisionCircle* circle = nullptr;
if (m_circleIndex >= m_viewCircleWidgetPool.Num())
{
circle = CreateWidget<UMiniMapVisionCircle>(GetWorld(), visionCircleWidget);
visionLayer->AddChild(circle);
m_viewCircleWidgetPool.Add(circle);
circle->SetTexture(backgroundTexture);
}
else
circle = m_viewCircleWidgetPool[m_circleIndex];
m_circleIndex++;
return circle;
}
FVector2D UMiniMapWidget::ConvertWorldToMinimap(const FVector2D& pos)
{
const FVector2D scale = FVector2D(m_max.X - m_min.X, m_max.Y - m_min.Y);
FVector2D relativePos = (pos - m_min) / scale;
FVector2D returnPos = FVector2D(relativePos.Y, relativePos.X);
returnPos.Y = 1.0f - returnPos.Y;
return returnPos;
}
void UMiniMapWidget::SetObjectIcon_Implementation(UTexture2D* texture, UMiniMapIconWidget* widget)
{
// No implementation
}
void UMiniMapWidget::SetMinimapInfo_Implementation(FVector2D player0, FVector2D player1, float radius)
{
// No implementation
}

View File

@@ -0,0 +1,89 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include <map>
#include "Minimap.h"
#include "MiniMapWidget.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UMiniMapWidget : public UUserWidget
{
GENERATED_BODY()
public:
UMiniMapWidget(const FObjectInitializer& init);
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
void SetObjectIcon(class UTexture2D* texture, class UMiniMapIconWidget* widget);
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& geometry, float deltaTime) override;
UPROPERTY(EditAnywhere, Category = "MiniMap")
float viewRadius;
UPROPERTY(EditAnywhere, Category = "Minimap")
FLinearColor playerColor;
UPROPERTY(EditAnywhere, Category = "Minimap")
FLinearColor allyColor;
UPROPERTY(EditAnywhere, Category = "Minimap")
FLinearColor enemyColor;
UPROPERTY(EditAnywhere, Category = "Minimap")
FLinearColor neutralColor;
UPROPERTY(EditAnywhere, Category = "MiniMap")
UTexture2D* playerIcon;
UPROPERTY(EditAnywhere, Category = "MiniMap")
UTexture2D* creatureIcon;
UPROPERTY(EditAnywhere, Category = "MiniMap")
UTexture2D* bossIcon;
UPROPERTY(EditAnywhere, Category = "MiniMap")
UTexture2D* campIcon;
UPROPERTY(EditAnywhere, Category = "MiniMap")
UTexture2D* arrowIcon;
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
void SetMinimapInfo(FVector2D player0, FVector2D player1, float radius);
FVector2D ConvertWorldToMinimap(const FVector2D& pos);
UPROPERTY(EditAnywhere, Category = "MiniMap")
TSubclassOf<class UMiniMapIconWidget> iconWidget;
UPROPERTY(EditAnywhere, Category = "MiniMap")
TSubclassOf<class UMiniMapVisionCircle> visionCircleWidget;
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
UCanvasPanel* iconLayer;
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
UCanvasPanel* arrowLayer;
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
UTexture2D* backgroundTexture;
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
UCanvasPanel* visionLayer;
private:
void m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character);
UMiniMapIconWidget* m_AllocateWidget();
UMiniMapIconWidget* m_AllocateArrow();
UMiniMapVisionCircle* m_AllocateVisionCircle();
TArray<class UMiniMapIconWidget*> m_miniMapIconWidgetPool;
TArray<class UMiniMapIconWidget*> m_arrowMapIconWidgetPool;
TArray<class UMiniMapVisionCircle*> m_viewCircleWidgetPool;
TArray<MiniMap::MinimapHandle*> m_handleBuffer;
FVector2D m_min;
FVector2D m_max;
float m_size;
float m_viewRadius;
float m_arrowAnim;
int32 m_localTeam;
int32 m_widgetIndex;
int32 m_arrowIndex;
int32 m_circleIndex;
};

View File

@@ -0,0 +1,58 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "PlayerSlot.h"
#include "DefaultGameInstance.h"
#include "PlayerStateBase.h"
UPlayerSlot::UPlayerSlot(const FObjectInitializer& init)
: Super(init)
{
}
void UPlayerSlot::NativeConstruct()
{
Super::NativeConstruct();
currentPlayerState = nullptr;
isLocal = false;
OnChanged();
}
void UPlayerSlot::Init(APlayerStateBase* playerState, bool isLocal)
{
if(!playerState && m_playerStateSet)
{
// Clear this slot
m_playerStateSet = false;
OnChanged();
return;
}
if (currentPlayerState == playerState)
return;
this->isLocal = isLocal;
if (playerState)
{
// Get the player info to display in this player slot
const TSharedPtr<const FUniqueNetId> netID = playerState->UniqueId.GetUniqueNetId();
if (netID.IsValid())
{
UWorld* const world = GetWorld();
check(world);
UDefaultGameInstance* gameInstance = Cast<UDefaultGameInstance>(world->GetGameInstance());
playerState->UpdatePersona();
}
else
{
// Invalid NetID somehow
GWWARNING(L"NetID is invalid");
return;
}
m_playerStateSet = true;
}
currentPlayerState = playerState;
OnChanged();
}
void UPlayerSlot::OnChanged_Implementation()
{
}

View File

@@ -0,0 +1,33 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "PlayerSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UPlayerSlot : public UUserWidget
{
GENERATED_BODY()
public:
UPlayerSlot(const FObjectInitializer& init);
virtual void NativeConstruct() override;
void Init(class APlayerStateBase* playerState, bool isLocal = false);
UFUNCTION(BlueprintNativeEvent, Category = "Player")
void OnChanged();
UPROPERTY(BlueprintReadOnly, Category = "Player")
class APlayerStateBase* currentPlayerState;
UPROPERTY(BlueprintReadOnly, Category = "Player")
bool isLocal;
private:
// Keep this boolean to detect currentPlayerState becoming invalid(nullptr)
bool m_playerStateSet;
};

View File

@@ -0,0 +1,16 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SizeBorder.h"
#include "SlateBlueprintLibrary.h"
void USizeBorder::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
{
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, MyGeometry.GetLocalSize(),
pixelSize, viewportSize);
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, FVector2D(0.0f, 0.0f),
pixelPosition, viewportPosition);
pixelSize -= pixelPosition;
viewportSize -= viewportPosition;
Super::NativeTick(MyGeometry, DeltaTime);
}

View File

@@ -0,0 +1,28 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "SizeBorder.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API USizeBorder : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "Size")
FVector2D pixelPosition;
UPROPERTY(BlueprintReadOnly, Category = "Size")
FVector2D pixelSize;
UPROPERTY(BlueprintReadOnly, Category = "Size")
FVector2D viewportPosition;
UPROPERTY(BlueprintReadOnly, Category = "Size")
FVector2D viewportSize;
void NativeTick(const FGeometry& MyGeometry, float DeltaTime) override;
};

View File

@@ -0,0 +1,130 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "EffectSelector.h"
#include "EffectSlot.h"
#include "BaseSkillObject.h"
#include "AbilityInfo.h"
#include "SkillWidget.h"
TSubclassOf<class UEffectSelector> defaultEffectSelectorClass;
UEffectSelector::UEffectSelector(const FObjectInitializer& init)
: Super(init)
{
defaultEffectSelectorClass = ConstructorHelpers::FClassFinder<UEffectSelector>(L"/Game/Assets/GUI/WEEGEE_SkillEffectSelector").Class;
}
void UEffectSelector::NativeConstruct()
{
m_container = Cast<UPanelWidget>(WidgetTree->FindWidget("EffectSlotContainer"));
if(!m_container)
{
GERROR("\"Container\" not found in effect selector widget " + GetName());
}
onItemSelected.AddDynamic(this, &UEffectSelector::m_OnItemSelected);
onItemSelectionConfirmed.AddDynamic(this, &UEffectSelector::m_OnItemSelectionConfirmed);
m_nameField = Cast<UTextBlock>(WidgetTree->FindWidget("NameField"));
m_descriptionField = Cast<UMultiLineEditableTextBox>(WidgetTree->FindWidget("DescField"));
Super::NativeConstruct();
}
void UEffectSelector::Init(class USkillWidget* widget)
{
this->skill = widget->skillAsset;
this->widget = widget;
// Remove old items
for(UEffectSlot* slot : m_effectSlots)
{
if(slot)
slot->RemoveFromParent();
}
if(!skill)
{
m_effectSlots.SetNum(0);
RescanItems();
return;
}
// Add effect slot buttons
for(UAbilityInfo* abilityEffect : skill->abilityEffects)
{
if(abilityEffect)
{
UEffectSlot* slot = CreateWidget<UEffectSlot>(GetWorld(), effectSlotClass);
m_container->AddChild(slot);
slot->Init(abilityEffect);
m_effectSlots.Add(slot);
}
else
{
GERROR("Didn't set ability on skill " + skill->GetName());
}
}
RescanItems();
}
void UEffectSelector::Init2(class TArray<UAbilityInfo*> abilities)
{
this->skill = nullptr;
this->widget = nullptr;
// Remove old items
for(UEffectSlot* slot : m_effectSlots)
{
if(slot)
slot->RemoveFromParent();
}
// Add effect slot buttons
for(UAbilityInfo* abilityEffect : abilities)
{
if(abilityEffect)
{
UEffectSlot* slot = CreateWidget<UEffectSlot>(GetWorld(), effectSlotClass);
m_container->AddChild(slot);
slot->Init(abilityEffect);
m_effectSlots.Add(slot);
}
else
{
GERROR("Didn't set ability on skill " + skill->GetName());
}
}
RescanItems();
}
void UEffectSelector::m_OnItemSelected(UMenuItemBase* item)
{
UEffectSlot* slot = Cast<UEffectSlot>(item);
if(slot && slot->ability)
{
if(m_nameField)
m_nameField->SetText(FText::FromString(slot->ability->name));
if(m_descriptionField)
m_descriptionField->SetText(FText::FromString(slot->ability->description));
}
}
void UEffectSelector::m_OnItemSelectionConfirmed(UMenuItemBase* item)
{
UEffectSlot* slot = Cast<UEffectSlot>(item);
if(slot && slot->ability)
{
if(widget)
{
widget->SetSelectedEffect(slot->index);
}
onEffectSelected.Broadcast(slot->index);
OnSelectionConfirmed(slot);
}
else
{
GERROR(L"Invalid ability selected");
}
}

View File

@@ -0,0 +1,52 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/SubMenu.h"
#include "EffectSelector.generated.h"
extern TSubclassOf<class UEffectSelector> defaultEffectSelectorClass;
UCLASS()
class UNREALPROJECT_API UEffectSelector : public USubMenu
{
GENERATED_BODY()
public:
UEffectSelector(const FObjectInitializer& init);
virtual void NativeConstruct();
UFUNCTION(BlueprintImplementableEvent)
void OnSelectionConfirmed(UEffectSlot* effect);
UFUNCTION(BlueprintCallable, Category="EffectSelector")
void Init(class USkillWidget* widget);
UFUNCTION(BlueprintCallable, Category = "EffectSelector")
void Init2(TArray<class UAbilityInfo*> abilities);
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UEffectSlot> effectSlotClass;
UPROPERTY(BlueprintReadOnly)
class UBaseSkillObject* skill;
UPROPERTY(BlueprintReadOnly)
class USkillWidget* widget;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEffectSelected, int32, selected);
UPROPERTY(BlueprintAssignable)
FOnEffectSelected onEffectSelected;
private:
UFUNCTION()
void m_OnItemSelected(UMenuItemBase* item);
UFUNCTION()
void m_OnItemSelectionConfirmed(UMenuItemBase* item);
UPROPERTY()
UTextBlock* m_nameField;
UPROPERTY()
UMultiLineEditableTextBox* m_descriptionField;
UPROPERTY()
UPanelWidget* m_container;
UPROPERTY()
TArray<class UEffectSlot*> m_effectSlots;
};

View File

@@ -0,0 +1,31 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "EffectSlot.h"
#include "AbilityInfo.h"
void UEffectSlot::NativeConstruct()
{
m_name = Cast<UTextBlock>(WidgetTree->FindWidget("NameField"));
m_image = Cast<UImage>(WidgetTree->FindWidget("Icon"));
Super::NativeConstruct();
}
void UEffectSlot::Init(class UAbilityInfo* ability)
{
this->ability = ability;
if(!ability)
return;
if(m_name)
{
m_name->SetText(FText::FromString(ability->name));
}
if(m_image)
{
m_image->SetBrushFromTexture(ability->icon);
}
OnInit(ability);
}

View File

@@ -0,0 +1,31 @@
// Project Lab - NHTV Igad
#pragma once
#include "GUI/Menu/MenuButton.h"
#include "EffectSlot.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UEffectSlot : public UMenuButton
{
GENERATED_BODY()
public:
virtual void NativeConstruct();
UFUNCTION(BlueprintCallable, Category = "EffectSelector")
void Init(class UAbilityInfo* ability);
UFUNCTION(BlueprintImplementableEvent, Category = "EffectSelector")
void OnInit(class UAbilityInfo* ability);
UPROPERTY(BlueprintReadOnly)
class UAbilityInfo* ability;
private:
UImage* m_image;
UTextBlock* m_name;
};

View File

@@ -0,0 +1,22 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "HexagonTile.h"
void UHexagonTile::NativeConstruct()
{
TArray<UWidget*> widgets;
WidgetTree->GetAllWidgets(widgets);
for (int32 i = 0; i < widgets.Num(); i++)
{
UImage* tmp = Cast<UImage>(widgets[i]);
if (tmp)
{
material = tmp->GetDynamicMaterial();
break;
}
}
}

View File

@@ -0,0 +1,30 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "HexagonTile.generated.h"
/**
*
*/
UCLASS()
class UNREALPROJECT_API UHexagonTile : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY( EditAnywhere, Category = "UI" )
int32 x;
UPROPERTY( EditAnywhere, Category = "UI" )
int32 y;
FMargin GetMargins()
{
return GetFullScreenOffset();
}
class UMaterialInstanceDynamic* material;
virtual void NativeConstruct() override;
};

View File

@@ -0,0 +1,155 @@
#pragma once
#include "SkillTreeState.Generated.h"
USTRUCT(BlueprintType)
struct FSkillTreeStateObject
{
GENERATED_BODY();
UPROPERTY()
FVector2D gridIndex;
UPROPERTY()
float rotation;
UPROPERTY()
TArray<FIntPoint> placedPoints;
UPROPERTY()
UClass* skillObject;
UPROPERTY()
int32 selectedEffect;
bool ContainsRoot(FIntPoint root)
{
for (int32 i = 0; i < placedPoints.Num(); i++)
{
if (placedPoints[i] == root) return true;
}
return false;
}
TArray<FSkillTreeStateObject*> ConnectedSkills( TMap<FIntPoint, FSkillTreeStateObject*>& skillsMap )
{
TArray<FSkillTreeStateObject*> result;
const FIntPoint offsets[2][6]{
{ FIntPoint(0, -1), FIntPoint(1, -1), FIntPoint(1, 0), FIntPoint(0, 1), FIntPoint(-1, 0), FIntPoint(-1, -1) },
{ FIntPoint(0, -1), FIntPoint(1, 0), FIntPoint(1, 1), FIntPoint(0, 1), FIntPoint(-1, 1), FIntPoint(-1, 0) } };
for (int32 i = 0; i < placedPoints.Num(); i++)
{
int32 k = placedPoints[i].X & 1;
for (int32 j = 0; j < 6; j++)
{
FIntPoint checkPoint = placedPoints[i] + offsets[k][j];
if (placedPoints.Contains(checkPoint)) continue;
if (skillsMap.Contains(checkPoint))
{
FSkillTreeStateObject* skill = *skillsMap.Find(checkPoint);
if (result.Contains(skill)) continue;
result.Push(skill);
}
}
}
return result;
}
};
USTRUCT(BlueprintType)
struct FSkillTreeState
{
GENERATED_BODY();
UPROPERTY(BlueprintReadOnly)
TArray<FSkillTreeStateObject> objects;
private:
void WalkTree(TMap<FSkillTreeStateObject*, TArray<FSkillTreeStateObject*>>* a_map, TArray<FSkillTreeStateObject*>* a_checked, TArray<FSkillTreeStateObject*> a_nodes)
{
for (int32 i = 0; i < a_nodes.Num(); i++)
{
if (a_checked->Contains(a_nodes[i])) continue;
a_checked->Push(a_nodes[i]);
TArray<FSkillTreeStateObject*>* connected = a_map->Find(a_nodes[i]);
if (!connected)
{
YERROR("Could not find the connected skills.");
continue;
}
WalkTree(a_map, a_checked, *connected);
}
}
public:
bool ValidateSkillTree()
{
bool rootResult = false;
bool connectResult = true;
if (objects.Num() == 0) return true;
TMap<FSkillTreeStateObject*, TArray<FSkillTreeStateObject*>> connectedSkillsMap;
TMap<FIntPoint, FSkillTreeStateObject*> skillsMap;
FSkillTreeStateObject* rootSkill = NULL;
for (int32 i = 0; i < objects.Num(); i++)
{
TArray<FIntPoint> points = objects[i].placedPoints;
for (int32 j = 0; j < points.Num(); j++)
{
skillsMap.Add(points[j], &objects[i]);
}
}
for (int32 i = 0; i < objects.Num(); i++)
{
TArray<FIntPoint> points = objects[i].placedPoints;
if (!rootResult)
{
bool rootTest = objects[i].ContainsRoot(FIntPoint(6,15));
if (rootTest)
{
rootResult = true;
rootSkill = &objects[i];
}
}
TArray<FSkillTreeStateObject*> connectedSkills = objects[i].ConnectedSkills(skillsMap);
connectedSkillsMap.Add(&objects[i], connectedSkills);
if (connectedSkills.Num() == 0)
{
connectResult = false;
break;
}
}
if (connectResult && rootResult)
{
if (!rootSkill)
{
YERROR("Root Skill is NULL!");
return false;
}
TArray<FSkillTreeStateObject*> checkedSkills;
checkedSkills.Push(rootSkill);
TArray<FSkillTreeStateObject*>* connected = connectedSkillsMap.Find(rootSkill);
if (!connected)
{
YPRINT("Root Skill is not attached to any skills!");
return false;
}
WalkTree(&connectedSkillsMap, &checkedSkills, *connected);
if (objects.Num() != checkedSkills.Num())
connectResult = false;
else
{
for (int32 i = 0; i < objects.Num(); i++)
{
if (!checkedSkills.Contains(&objects[i]))
{
connectResult = false;
}
}
}
}
bool isValid = false;
if (objects.Num() == 1 && rootResult)
isValid = true;
else
isValid = (rootResult && connectResult);
return isValid;
}
};

View File

@@ -0,0 +1,678 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "SkillTreeWidget.h"
#include "SkillTreeObject.h"
#include "BaseSkillObject.h"
#include "HexagonTile.h"
#include "ScrollBox.h"
#include "SkillWidget.h"
#include "CanvasPanel.h"
#include "PlayerControllerBase.h"
#include "DefaultGameInstance.h"
#include "CharacterSettings.h"
#include "SlateBlueprintLibrary.h"
#include "ToolTipWidget.h"
#include "AbilityInfo.h"
#include "SizeBorder.h"
#include "WidgetLayoutLibrary.h"
int32 roundToNearestOdd1(float a_in)
{
return 2 * FMath::FloorToInt((a_in / 2) + 0.5f) - 2;
}
int32 roundToNearestEven1(float a_in)
{
return 2 * FMath::FloorToInt(a_in / 2);
}
void USkillTreeWidget::NativeOnSkillTreeChanged(ESkillTreeChangeEvent event, UBaseSkillObject* relevantObject)
{
// Recalculate dominantDamageType
int32_t categoryCounters[] = { 0,0,0 };
int32_t largest = 0;
for(USkillWidget* skill : m_skillWidgets)
{
if(skill->selectedEffect >= 0 && skill->skillAsset)
{
if(skill->selectedEffect >= skill->skillAsset->abilityEffects.Num())
{
GERROR("CORRUPT SKILL TREE");
continue;
}
UAbilityInfo* info = skill->skillAsset->abilityEffects[skill->selectedEffect];
int32_t& myCounter = categoryCounters[(size_t)info->abilityCategory];
myCounter++;
if(myCounter >= largest)
{
largest = myCounter;
dominantAbilityCategory = info->abilityCategory;
}
}
}
onSkillTreeChanged.Broadcast(event, relevantObject);
}
USkillTreeWidget::USkillTreeWidget(const FObjectInitializer& init)
: Super( init )
{
m_interactive = true;
m_shown = false;
m_lastFocusedWidget = nullptr;
isValid = false;
placedSkillHexMap = new FHexMap();
for (int32 x = 0; x < 13; x++)
{
placedSkillHexMap->rawdata[x] = 0;
}
}
void USkillTreeWidget::NativeConstruct()
{
Super::NativeConstruct();
UpdateVisibility();
// Get the tooltip widget
m_toolTipWidget = Cast<UToolTipWidget>(WidgetTree->FindWidget("ToolTipSWidget"));
m_sizeBorder = Cast<USizeBorder>(WidgetTree->FindWidget("SizeBorder"));
if ( !skillTreeAsset ) return;
TArray<UWidget*> widgets;
WidgetTree->GetAllWidgets( widgets );
UScrollBox* scrollBox = NULL;
m_skillTreeCanvas = WidgetTree->FindWidget<UCanvasPanel>("Main_SkillTree");
//m_focusBorder = WidgetTree->FindWidget<UBorder>("FocusBorder");
m_inputCapture = WidgetTree->FindWidget<UBorder>("InputCapture");
m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
for ( int32 i = 0; i < widgets.Num(); i++ )
{
UHexagonTile* tmpHex = Cast<UHexagonTile>( widgets[i] );
UScrollBox* tmpScrollBox = Cast<UScrollBox>( widgets[i] );
//USkillWidget* tmpSkill = Cast<USkillWidget>( widgets[i] );
if ( tmpScrollBox ) scrollBox = tmpScrollBox;
//if ( tmpSkill ) m_selectedSkill = tmpSkill;
if ( !tmpHex ) continue;
tiles.Push( tmpHex );
bool test = skillTreeAsset->hexMap.Get( tmpHex->x, tmpHex->y );
if ( test ) tmpHex->SetVisibility( ESlateVisibility::Hidden );
}
if ( m_selectedSkill )
{
m_selectedSkill->parent = this;
m_selectedSkill->SetDragable( true );
}
else
{
YWARNING( "No Selected Skill found!" );
}
}
void USkillTreeWidget::NativeDestruct()
{
delete placedSkillHexMap;
Super::NativeDestruct();
}
void USkillTreeWidget::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
{
Super::NativeTick(MyGeometry, DeltaTime);
//if ((m_focusBorder && m_focusBorder->IsHovered()) || draggingWidget)
//{
// //m_inputCapture->SetVisibility(ESlateVisibility::Visible);
//}
//else
//{
// //m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
//}
if (m_toolTipWidget)
{
m_toolTipWidget->showTooltip = false;
FVector2D mousePos;
UWidgetLayoutLibrary::GetMousePositionScaledByDPI(GetOwningPlayer(), mousePos.X, mousePos.Y);
FIntPoint mouseGridPos = GetGridIndex(mousePos);
USkillWidget** sw = tileMap.Find(mouseGridPos);
USkillWidget* skill = sw ? *sw : nullptr;
if (skill)
{
// Reset hovered state
UAbilityInfo* info = skill->GetSelectedEffectAbility();
if (info)
{
m_toolTipWidget->SetTitle(info->name);
m_toolTipWidget->SetText(info->description);
m_toolTipWidget->position = skill->tooltipAreaPosition;
m_toolTipWidget->size = skill->tooltipAreaSize;
m_toolTipWidget->showTooltip = true;
}
skill->hovered = true;
}
if (m_lastFocusedWidget && m_lastFocusedWidget != skill)
{
m_lastFocusedWidget->hovered = false;
}
m_lastFocusedWidget = skill;
}
}
FReply USkillTreeWidget::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
if(!IsInteractive())
return FReply::Handled();
//GWPRINT(L"Skill tree up");
if(draggingWidget && !draggingWidget->IsUsingController())
{
draggingWidget->StopDragging();
return FReply::Handled();
}
return FReply::Handled();
}
FReply USkillTreeWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
if(!IsInteractive())
return FReply::Handled();
//GWPRINT(L"Skill tree down");
FKey button = InMouseEvent.GetEffectingButton();
if(button == EKeys::LeftMouseButton)
{
if(draggingWidget && !draggingWidget->IsUsingController())
{
draggingWidget->StopDragging();
return FReply::Handled();
}
else
{
if(m_lastFocusedWidget)
{
if(m_lastFocusedWidget->hovered)
m_lastFocusedWidget->StartDragging();
}
}
}
else if(button == EKeys::RightMouseButton)
{
if(!draggingWidget && m_lastFocusedWidget)
{
if(m_lastFocusedWidget->hovered)
{
m_lastFocusedWidget->OnRequireSkillEffect();
}
}
}
return FReply::Handled();
}
FReply USkillTreeWidget::NativeOnMouseWheel(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
//GWPRINT(L"USkillTreeWidget MouseWheel " + InMouseEvent.GetWheelDelta());
if (draggingWidget)
{
float add = 60.0f * FMath::FloorToFloat(InMouseEvent.GetWheelDelta());
draggingWidget->SetSkillRotation(add + draggingWidget->GetSkillRotation());
}
return FReply::Unhandled();
}
void USkillTreeWidget::SetIsInteractive(bool interactive)
{
m_interactive = interactive;
UpdateVisibility();
}
void USkillTreeWidget::UpdateVisibility()
{
if (m_shown)
{
SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
{
SetVisibility(ESlateVisibility::Hidden);
}
UImage* validImage = WidgetTree->FindWidget<UImage>("Validation_Image");
if (m_interactive)
{
validImage->SetVisibility(ESlateVisibility::Visible);
}
else
{
validImage->SetVisibility(ESlateVisibility::Hidden);
}
}
void WalkTree(TMap<USkillWidget*, TArray<USkillWidget*>>* a_map, TArray<USkillWidget*>* a_checked, TArray<USkillWidget*> a_nodes)
{
for (int32 i = 0; i < a_nodes.Num(); i++)
{
if (a_checked->Contains(a_nodes[i])) continue;
a_checked->Push(a_nodes[i]);
TArray<USkillWidget*>* connected = a_map->Find(a_nodes[i]);
if (!connected)
{
YERROR("Could not find the connected skills.");
continue;
}
WalkTree(a_map, a_checked, *connected);
}
}
bool USkillTreeWidget::ValidateSkillTree()
{
bool rootResult = false;
bool connectResult = true;
if (m_skillWidgets.Num() == 0) return true;
TMap<USkillWidget*, TArray<USkillWidget*>> connectedSkillsMap;
USkillWidget* rootSkill = NULL;
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
TArray<FIntPoint> points = m_skillWidgets[i]->GetPoints();
if (!rootResult)
{
bool rootTest = m_skillWidgets[i]->ContainsRoot(points);
if (rootTest)
{
rootResult = true;
rootSkill = m_skillWidgets[i];
}
}
TArray<USkillWidget*> connectedSkills = m_skillWidgets[i]->ConnectedSkills(points);
connectedSkillsMap.Add(m_skillWidgets[i], connectedSkills);
if ( connectedSkills.Num() == 0 )
{
connectResult = false;
break;
}
}
if (connectResult && rootResult)
{
if (!rootSkill)
{
YERROR("Root Skill is NULL!");
return false;
}
TArray<USkillWidget*> checkedSkills;
checkedSkills.Push(rootSkill);
TArray<USkillWidget*>* connected = connectedSkillsMap.Find(rootSkill);
if (!connected)
{
YPRINT("Root Skill is not attached to any skills!");
return false;
}
WalkTree(&connectedSkillsMap, &checkedSkills, *connected);
if (m_skillWidgets.Num() != checkedSkills.Num())
connectResult = false;
else
{
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
if (!checkedSkills.Contains(m_skillWidgets[i]))
{
connectResult = false;
}
}
}
}
if (m_skillWidgets.Num() == 1 && rootResult)
isValid = true;
else
isValid = (rootResult && connectResult);
if (isValid)
{
UpdateLevel(10.0f);
}
else
{
UpdateLevel(0.0f);
}
return isValid;
}
FSkillTreeState USkillTreeWidget::GetState()
{
FSkillTreeState state;
for(int32 i = 0; i < m_skillWidgets.Num(); i++)
{
FSkillTreeStateObject obj;
USkillWidget* skill = m_skillWidgets[i];
obj.gridIndex = skill->GetGridIndex() /*- FVector2D(1.5,4) * m_skillTreeCanvas->RenderTransform.Scale.X*/;
obj.placedPoints = skill->placedPoints;
obj.skillObject = skill->skillAsset->GetClass();
obj.rotation = skill->GetSkillRotation();
obj.selectedEffect = skill->selectedEffect;
state.objects.Add(obj);
//GWPRINT(L"Saving gridIndex " + obj.gridIndex);
//for(int32 i = 0; i < obj.placedPoints.Num(); i++)
//{
// GWPRINT(L"Saving Placed point " + i + L" " + obj.placedPoints[i].X + L", " + obj.placedPoints[i].Y);
//}
}
return state;
}
TArray<class UBaseSkillObject*> USkillTreeWidget::GetApearanceState()
{
return m_skillClasses;
}
void USkillTreeWidget::Clear()
{
// Remove all placed hexes
auto arrayCopy = m_skillWidgets;
for(int32 i = 0; i < arrayCopy.Num(); i++)
{
arrayCopy[i]->RemoveFromParent();
RemoveSkill(arrayCopy[i], arrayCopy[i]->placedPoints);
}
m_skillWidgets.SetNum(0);
m_skillClasses.SetNum(0);
tileMap.Reset();
// Clear Hex map
for(int32 x = 0; x < 13; x++)
{
placedSkillHexMap->rawdata[x] = 0;
}
isValid = true;
// Trigger Callbacks
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Cleared, nullptr);
}
void USkillTreeWidget::BuildFromState(const FSkillTreeState& state)
{
Clear();
for(int32 i = 0; i < state.objects.Num(); i++)
{
const FSkillTreeStateObject& obj = state.objects[i];
UBaseSkillObject* baseSkill = obj.skillObject->GetDefaultObject<UBaseSkillObject>();
if(!baseSkill)
continue;
//GWPRINT(L"gridIndex " + obj.gridIndex);
//for(int32 i = 0; i < obj.placedPoints.Num(); i++)
//{
// GWPRINT(L"Placed point " + i + L" " + obj.placedPoints[i].X + L", " + obj.placedPoints[i].Y);
//}
m_selectedSkill = CreateWidget<USkillWidget>(GetWorld(), WidgetTemplate);
m_selectedSkill->skillAsset = baseSkill;
m_selectedSkill->parent = this;
if(m_skillTreeCanvas)
m_selectedSkill->SetRenderScale(m_skillTreeCanvas->RenderTransform.Scale);
m_selectedSkill->SetPlaced();
if(m_skillTreeCanvas)
m_skillTreeCanvas->GetParent()->AddChild(m_selectedSkill);
UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(m_selectedSkill->Slot);
if(slot)
{
slot->SetAutoSize(true);
slot->SetAlignment(FVector2D(-0.5f, -0.5f));
}
m_selectedSkill->SetSelectedEffect(obj.selectedEffect);
m_selectedSkill->placedPoints = obj.placedPoints;
m_selectedSkill->SetSkillRotation(obj.rotation);
m_selectedSkill->PlaceOnGridIndex(obj.gridIndex);
AddSkill(m_selectedSkill, obj.placedPoints);
}
}
void USkillTreeWidget::RemoveSkill(UBaseSkillObject* skillObject)
{
int32 skillID = m_skillClasses.Find(skillObject);
if (skillID == INDEX_NONE) return;
CancelSkill(m_skillWidgets[skillID]);
m_skillWidgets[skillID]->Remove();
}
bool USkillTreeWidget::IsUsingSkill(UBaseSkillObject* skillObject) const
{
return m_skillClasses.Contains(skillObject);
}
USkillWidget* USkillTreeWidget::GetUsedSkill(class UBaseSkillObject* skillObject)
{
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
if (m_skillWidgets[i]->skillAsset == skillObject)
return m_skillWidgets[i];
}
return nullptr;
}
void USkillTreeWidget::Save(int32 saveSlot)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
UCharacterSettings* settings = inst->GetCharacterSettings();
if (saveSlot < 0 || saveSlot >= settings->characterSaves.Num())
{
JERROR("Invalid save slot");
return;
}
settings->characterSaves[saveSlot].skillTreeState = GetState();
inst->SaveSettings();
}
void USkillTreeWidget::Load(int32 saveSlot)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
UCharacterSettings* settings = inst->GetCharacterSettings();
if (saveSlot < 0 || saveSlot >= settings->characterSaves.Num())
{
JERROR("Invalid save slot");
return;
}
BuildFromState(settings->characterSaves[saveSlot].skillTreeState);
ValidateSkillTree();
}
FIntPoint USkillTreeWidget::GetGridIndex(FVector2D in) const
{
FVector2D result;
UCanvasPanelSlot* tmpSlot = UWidgetLayoutLibrary::SlotAsCanvasSlot(m_skillTreeCanvas);
float viewportScale = UWidgetLayoutLibrary::GetViewportScale(const_cast<USkillTreeWidget*>(this));
FVector2D topLeft;
float scale = 32.0f * (m_skillTreeCanvas->RenderTransform.Scale.X / 2);
float XScale = (scale * 1.73205f);
float YScale = scale;
if (tmpSlot)
{
FVector2D screenTopRight = FVector2D(GEngine->GameViewport->Viewport->GetSizeXY().X * tmpSlot->GetAnchors().Minimum.X, GEngine->GameViewport->Viewport->GetSizeXY().Y * tmpSlot->GetAnchors().Minimum.Y);
FVector2D offset = tmpSlot->GetPosition() * viewportScale;
FVector2D widgetTopLeft = ((tmpSlot->GetSize() * m_skillTreeCanvas->RenderTransform.Scale.X * viewportScale) / 2);
topLeft = screenTopRight + offset - widgetTopLeft;
topLeft /= viewportScale;
//topLeft.X -= 16.0f;
}
float XPreFloor = (in.X - topLeft.X) / XScale;
float YPreFloor = (in.Y - topLeft.Y) / YScale;
int32 XIndex = FMath::FloorToInt(XPreFloor);
int32 YIndex = FMath::FloorToInt(YPreFloor);
/*FVector2D magicOffset = FVector2D(1.5, 4) * m_skillTreeCanvas->RenderTransform.Scale.X;*/
bool odd = (XIndex % 2) == 1;
if (odd)
YIndex = roundToNearestOdd1(YIndex);
else
YIndex = roundToNearestEven1(YIndex);
return FIntPoint(XIndex, (YIndex) / 2.0f);
}
void USkillTreeWidget::StartDragging(USkillWidget* widget)
{
check(draggingWidget == nullptr);
draggingWidget = widget;
m_inputCapture->SetVisibility(ESlateVisibility::Visible);
}
void USkillTreeWidget::StopDragging(USkillWidget* widget)
{
if(draggingWidget != widget)
{
GERROR("Calling StopDragging twice");
}
draggingWidget = nullptr;
m_inputCapture->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
void USkillTreeWidget::UpdateTextInfo_Implementation()
{
}
void USkillTreeWidget::AddSkill(USkillWidget* a_skill, const TArray<FIntPoint>& a_points)
{
for ( int32 i = 0; i < a_points.Num(); i++ )
{
placedSkillHexMap->Set( a_points[i].X, a_points[i].Y, true );
tileMap.Add(a_points[i], a_skill);
}
UWorld* const world = GetWorld();
if ( !world )
{
YWARNING( "Couldn't get the World." );
return;
}
APlayerControllerBase* const controller = Cast<APlayerControllerBase>( GetOwningPlayer() );
if ( !controller )
{
YWARNING( "Couldn't get the Player." );
return;
}
controller->OnLearnSkill( a_skill->skillAsset );
m_skillWidgets.Add(a_skill);
m_skillClasses.Add(a_skill->skillAsset);
// Trigger Callbacks
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Added, a_skill->skillAsset);
ValidateSkillTree();
}
void USkillTreeWidget::RemoveSkill( USkillWidget* a_skill, const TArray<FIntPoint>& a_points )
{
m_skillClasses.Remove(a_skill->skillAsset);
m_skillWidgets.Remove(a_skill);
for ( int32 i = 0; i < a_points.Num(); i++ )
{
placedSkillHexMap->Set( a_points[i].X, a_points[i].Y, false );
tileMap.Remove(a_points[i]);
}
UWorld* const world = GetWorld();
if ( !world )
{
YWARNING( "Couldn't get the World." );
return;
}
APlayerControllerBase* const controller = Cast<APlayerControllerBase>( GetOwningPlayer() );
if ( !controller )
{
YWARNING( "Couldn't get the Player." );
return;
}
controller->OnUnlearnSkill( a_skill->skillAsset );
// Trigger Callbacks
NativeOnSkillTreeChanged(ESkillTreeChangeEvent::Removed, a_skill->skillAsset);
}
USkillWidget* USkillTreeWidget::NewSkill( USkillWidget* a_skill, bool controller )
{
return NewSkillFromAsset(a_skill->skillAsset, controller);
}
class USkillWidget* USkillTreeWidget::NewSkillFromAsset(UBaseSkillObject* skillAsset, bool controller /*= false*/)
{
if(draggingWidget)
return NULL;
//a_skill->Disable();
m_selectedSkill = CreateWidget<USkillWidget>(GetWorld(), WidgetTemplate);
m_selectedSkill->skillAsset = skillAsset;
m_selectedSkill->parent = this;
m_selectedSkill->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
m_selectedSkill->SetDragable(true, controller);
m_selectedSkill->SetRenderScale(m_skillTreeCanvas->RenderTransform.Scale);
m_skillTreeCanvas->GetParent()->AddChild(m_selectedSkill);
m_selectedSkill->SetUserFocus(GetOwningPlayer());
UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(m_selectedSkill->Slot);
if(slot)
{
slot->SetAutoSize(true);
slot->SetAlignment(FVector2D(-0.5f, -0.5f));
}
if(controller)
{
// Set initial position
m_selectedSkill->MoveSkillAbsolute(lastGridIndex);
}
return m_selectedSkill;
}
void USkillTreeWidget::CancelSkill(USkillWidget* a_skill)
{
a_skill->RemoveFromParent();
ValidateSkillTree();
}
void USkillTreeWidget::Show()
{
m_shown = true;
UpdateVisibility();
}
void USkillTreeWidget::Hide()
{
m_shown = false;
UpdateVisibility();
}
void USkillTreeWidget::UpdateLevel(float level)
{
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
m_skillWidgets[i]->UpdateLevel(level);
}
if (IsInteractable())
{
UpdateTextInfo();
}
}
void USkillTreeWidget::UpdateInfo(float skilltreeHeight, float skilltreeOffset)
{
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
m_skillWidgets[i]->UpdateInfo(skilltreeHeight, skilltreeOffset);
}
}
/*
void USkillTreeWidget::NativeTick(const FGeometry& MyGeometry, float DeltaTime)
{
FVector2D minPos;
FVector2D maxPos;
FVector2D viewportPos;
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, MyGeometry.GetLocalSize(), maxPos, viewportPos);
USlateBlueprintLibrary::LocalToViewport(this, MyGeometry, FVector2D(0,0), minPos, viewportPos);
FVector2D viewportSize = maxPos - minPos;
for (int32 i = 0; i < m_skillWidgets.Num(); i++)
{
m_skillWidgets[i]->UpdateInfo(viewportSize.Y, minPos.Y);
}
}
*/

Some files were not shown because too many files have changed in this diff Show More