minigames hints & cheat menu

This commit is contained in:
Oleg Petruny 2025-06-07 20:35:35 +02:00
parent 3523c179f0
commit aa6dad4e05
33 changed files with 191 additions and 117 deletions

BIN
Audio/Sounds/Signal_C.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,12 +13,12 @@ TMap<UClass*, FAgeOfWarUnitStats> AAgeOfWarManager::unitStats = {};
const FAgeOfWarUnitStats& AAgeOfWarManager::UnitGetStats(TSubclassOf<AAgeOfWarUnit> unitClass)
{
check(unitStats.Contains(*unitClass));
//if(!unitStats.Contains(*unitClass))
//{
// static FAgeOfWarUnitStats empty;
// return empty;
//}
//check(unitStats.Contains(*unitClass));
if(!unitStats.Contains(*unitClass))
{
static FAgeOfWarUnitStats empty;
return empty;
}
return unitStats[unitClass];
}
@ -31,16 +31,12 @@ AAgeOfWarManager::AAgeOfWarManager()
camera->SetupAttachment(root);
}
void AAgeOfWarManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
void AAgeOfWarManager::OnStart_Internal()
{
if(!playerPawn)
return;
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
InitUI();
FillUnitStats();
TArray<UChildActorComponent*> units;
GetComponents<UChildActorComponent>(units);
@ -54,7 +50,7 @@ void AAgeOfWarManager::Start(APlayerBase* playerPawn, FMinigameEndCallback deleg
}
}
void AAgeOfWarManager::End()
void AAgeOfWarManager::OnEnd_Internal()
{
for(auto& unit : spawnedUnits)
unit->Destroy();
@ -64,8 +60,6 @@ void AAgeOfWarManager::End()
player->ReturnPlayerView();
player->UnlockPlayer(FPlayerLock::All());
AMinigame::End();
}
void AAgeOfWarManager::UnitKill(AAgeOfWarUnit* unit)

View File

@ -16,14 +16,16 @@ public:
AAgeOfWarManager();
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
void UnitKill(class AAgeOfWarUnit* unit);
UFUNCTION(BlueprintImplementableEvent)
void OnUnitKill(class AAgeOfWarUnit* unit);
protected:
virtual void OnStart_Internal() override;
virtual void OnEnd_Internal() override;
UFUNCTION(BlueprintImplementableEvent)
void InitUI();
UFUNCTION(BlueprintImplementableEvent)
void FillUnitStats();
UFUNCTION(BlueprintCallable)

View File

@ -32,13 +32,8 @@ ACrossyRoadManager::ACrossyRoadManager()
mannequin->SetupAttachment(root);
}
void ACrossyRoadManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
void ACrossyRoadManager::OnStart_Internal()
{
if(!playerPawn)
return;
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
@ -65,7 +60,7 @@ void ACrossyRoadManager::Start(APlayerBase* playerPawn, FMinigameEndCallback del
PrimaryActorTick.SetTickFunctionEnable(true);
}
void ACrossyRoadManager::End()
void ACrossyRoadManager::OnEnd_Internal()
{
PrimaryActorTick.SetTickFunctionEnable(false);
@ -74,8 +69,6 @@ void ACrossyRoadManager::End()
player->UnlockPlayer(FPlayerLock::All());
player->ReturnPlayerView();
AMinigame::End();
}
void ACrossyRoadManager::Hit()

View File

@ -14,9 +14,6 @@ class ACrossyRoadManager : public AMinigame
public:
ACrossyRoadManager();
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
void Hit();
void Finish();
@ -29,6 +26,9 @@ protected:
float speed;
};
virtual void OnStart_Internal() override;
virtual void OnEnd_Internal() override;
virtual void PostInitializeComponents() override;
virtual void Tick(float deltaTime) override;

View File

@ -15,13 +15,8 @@ AFishingManager::AFishingManager()
context = asset.Object;
}
void AFishingManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
void AFishingManager::OnStart_Internal()
{
if(!playerPawn)
return;
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
auto input = ACustomPlayerController::GetInput();
@ -30,13 +25,11 @@ void AFishingManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delega
handler2 = input->BindAction(mapping.Action, ETriggerEvent::Completed, this, &AFishingManager::OnInputUnhold).GetHandle();
}
void AFishingManager::End()
void AFishingManager::OnEnd_Internal()
{
auto input = ACustomPlayerController::GetInput();
input->RemoveBindingByHandle(handler1);
input->RemoveBindingByHandle(handler2);
player->UnlockPlayer(FPlayerLock::All());
AMinigame::End();
}

View File

@ -35,9 +35,6 @@ class AFishingManager : public AMinigame
public:
AFishingManager();
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void CatchSucceed(int32 price);
@ -45,6 +42,9 @@ public:
void CatchFailed();
protected:
virtual void OnStart_Internal() override;
virtual void OnEnd_Internal() override;
UFUNCTION(BlueprintImplementableEvent)
void OnInputHold();
UFUNCTION(BlueprintImplementableEvent)

View File

@ -7,27 +7,52 @@
#include "CustomPlayerController.h"
#include "PlayerBase.h"
AMinigame* AMinigame::instance = nullptr;
AMinigame* AMinigame::GetActiveInstance()
{
return instance;
}
AMinigame::AMinigame()
{
PrimaryActorTick.bStartWithTickEnabled = false;
PrimaryActorTick.bCanEverTick = true;
}
void AMinigame::End()
{
if(callback.IsBound())
callback.Execute(result, score);
OnEnd();
}
void AMinigame::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
{
if(instance)
return;
instance = this;
player = playerPawn;
callback = delegate;
if(context.IsValid())
ACustomPlayerController::AppendInputContext(context);
OnStart_Internal();
OnStart();
}
void AMinigame::End()
{
if(instance != this)
return;
ended = true;
if(callback.IsBound())
callback.Execute(result, score);
OnEnd_Internal();
OnEnd();
instance = nullptr;
}
void AMinigame::Restart()
{
OnRestart_Internal();
OnRestart();
}

View File

@ -18,9 +18,10 @@ DECLARE_DYNAMIC_DELEGATE_TwoParams(FMinigameEndCallback, EMinigameResult, result
/**
* Interface for all minigame classes.
* Any minigame is started/ended/restarted by executing Start()/End()/Restart() on specific instance.
* Only one minigame can be active in the same time to avoid control collisions.
* Each call is after broadcasted into a blueprint event also.
* On start the caller can pass a OnEndCallback delegate to receive result and score on a minigame end.
* Each minigame can have its own contextInput
* Each minigame can have its own contextInput.
*/
UCLASS(Abstract, Blueprintable, BlueprintType, MinimalAPI)
class AMinigame : public AActor
@ -28,27 +29,36 @@ class AMinigame : public AActor
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (DisplayName = "GetActiveMinigame"), Category = "Minigame")
static AMinigame* GetActiveInstance();
AMinigame();
UFUNCTION(BlueprintCallable)
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate);
UFUNCTION(BlueprintImplementableEvent)
void OnStart();
void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate);
UFUNCTION(BlueprintCallable)
virtual void End();
UFUNCTION(BlueprintImplementableEvent)
void OnEnd();
void End();
UFUNCTION(BlueprintCallable)
virtual void Restart() {}
UFUNCTION(BlueprintImplementableEvent)
void OnRestart();
void Restart();
UFUNCTION(BlueprintPure)
TSoftObjectPtr<class UInputMappingContext> GetInputMappings() { return context; }
protected:
virtual void OnStart_Internal() {}
UFUNCTION(BlueprintImplementableEvent)
void OnStart();
virtual void OnEnd_Internal() {}
UFUNCTION(BlueprintImplementableEvent)
void OnEnd();
virtual void OnRestart_Internal() {}
UFUNCTION(BlueprintImplementableEvent)
void OnRestart();
FMinigameEndCallback callback;
class APlayerBase* player;
@ -63,5 +73,9 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSoftObjectPtr<class UInputMappingContext> context = nullptr;
private:
static AMinigame* instance; // active minigame
};

View File

@ -8,19 +8,12 @@
ARythmClickManager::ARythmClickManager()
{}
void ARythmClickManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
void ARythmClickManager::OnStart_Internal()
{
if(!playerPawn)
return;
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
}
void ARythmClickManager::End()
void ARythmClickManager::OnEnd_Internal()
{
player->UnlockPlayer(FPlayerLock::All());
AMinigame::End();
}

View File

@ -14,6 +14,7 @@ class ARythmClickManager : public AMinigame
public:
ARythmClickManager();
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
protected:
virtual void OnStart_Internal() override;
virtual void OnEnd_Internal() override;
};

View File

@ -32,13 +32,8 @@ ASubwaySurfManager::ASubwaySurfManager()
mannequin->SetupAttachment(root);
}
void ASubwaySurfManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
void ASubwaySurfManager::OnStart_Internal()
{
if(!playerPawn)
return;
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
@ -61,7 +56,7 @@ void ASubwaySurfManager::Start(APlayerBase* playerPawn, FMinigameEndCallback del
PrimaryActorTick.SetTickFunctionEnable(true);
}
void ASubwaySurfManager::End()
void ASubwaySurfManager::OnEnd_Internal()
{
PrimaryActorTick.SetTickFunctionEnable(false);
@ -70,8 +65,6 @@ void ASubwaySurfManager::End()
player->UnlockPlayer(FPlayerLock::All());
player->ReturnPlayerView();
AMinigame::End();
}
void ASubwaySurfManager::Hit()

View File

@ -21,13 +21,13 @@ class ASubwaySurfManager : public AMinigame
public:
ASubwaySurfManager();
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
void Hit();
void Finish();
protected:
virtual void OnStart_Internal() override;
virtual void OnEnd_Internal() override;
virtual void PostInitializeComponents() override;
virtual void Tick(float deltaTime) override;

View File

@ -151,10 +151,25 @@ void UWidgetsManager::UpdateWidgetsOwner()
void UWidgetsManager::AddOverlayWidget(UUserWidget* widget)
{
overlayWidgetsInstances.Add(widget);
widget->AddToViewport();
}
void UWidgetsManager::RemoveOverlayWidget(UUserWidget* widget)
{
overlayWidgetsInstances.Remove(widget);
widget->RemoveFromViewport();
}
void UWidgetsManager::ShowMainMenu(bool pause)
{
mainMenuWidget->Show(pause);
journalWidget->Hide();
HideJournal();
HideCheatMenu();
}
void UWidgetsManager::HideMainMenu()
@ -164,6 +179,35 @@ void UWidgetsManager::HideMainMenu()
void UWidgetsManager::SwitchCheatMenu()
{
if(cheatMenuWidget)
HideCheatMenu();
else
ShowCheatMenu();
}
void UWidgetsManager::ShowCheatMenu()
{
if(cheatMenuWidget || mainMenuWidget->Visibility == ESlateVisibility::Visible)
return;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
cheatMenuWidget = CreateWidget<UUserWidget>(PC, cheatMenuWidgetClass);
cheatMenuWidget->AddToViewport(100);
}
void UWidgetsManager::HideCheatMenu()
{
if(!cheatMenuWidget)
return;
cheatMenuWidget->RemoveFromParent();
cheatMenuWidget = nullptr;
}
void UWidgetsManager::ShowFpsCount()
{
if(fpsCountWidget)

View File

@ -27,9 +27,21 @@ public:
void HideWidgets();
void UpdateWidgetsOwner();
UFUNCTION(BlueprintCallable, Category = WidgetsManager)
void AddOverlayWidget(class UUserWidget* widget);
UFUNCTION(BlueprintCallable, Category = WidgetsManager)
void RemoveOverlayWidget(class UUserWidget* widget);
void ShowMainMenu(bool pause = true);
void HideMainMenu();
UFUNCTION(BlueprintCallable, Category = WidgetsManager)
void SwitchCheatMenu();
UFUNCTION(BlueprintCallable, Category = WidgetsManager)
void ShowCheatMenu();
UFUNCTION(BlueprintCallable, Category = WidgetsManager)
void HideCheatMenu();
void ShowFpsCount();
void HideFpsCount();
@ -69,12 +81,16 @@ protected:
UPROPERTY(EditDefaultsOnly)
TSet<TSubclassOf<class UUserWidget>> overlayWidgets; // hidden in pause, cutscene
TArray<class UUserWidget*> overlayWidgetsInstances;
TSet<class UUserWidget*> overlayWidgetsInstances;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UMainMenuWidget> mainMenuWidgetClass;
class UMainMenuWidget* mainMenuWidget = nullptr;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UUserWidget> cheatMenuWidgetClass;
class UUserWidget* cheatMenuWidget = nullptr;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UUserWidget> fpsCountWidgetClass;
class UUserWidget* fpsCountWidget = nullptr;