Code & git cleanup #6

Merged
oleg.petruny merged 15 commits from Documentation into master 2024-12-15 13:32:54 +00:00
108 changed files with 1188 additions and 948 deletions

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
ReleaseBuilds/** filter=lfs diff=lfs merge=lfs -text
Images/** filter=lfs diff=lfs merge=lfs -text
Fonts/** filter=lfs diff=lfs merge=lfs -text
Audio/** filter=lfs diff=lfs merge=lfs -text

1
Audio/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
./** filter=lfs diff=lfs merge=lfs -text

3
Documentation/README.md Normal file
View File

@ -0,0 +1,3 @@
# Documentation
The always actual documentation is on [google drive](https://drive.google.com/drive/folders/1o40kh_8BgrMI3BzPyfNT0ZLG_5mrIjEy?usp=sharing). \
There is only a dump/backup.

Binary file not shown.

1
Fonts/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
./** filter=lfs diff=lfs merge=lfs -text

1
Images/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
./** filter=lfs diff=lfs merge=lfs -text

1
Models/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.blend1

View File

@ -1,15 +1,31 @@
# Repo structure
This repository contains all sources and data. The current unreal project is located in UnrealProject/Lost_Edge/.
This repository contains all sources and data for project Lost_Edge excluding server and network deploy configs. \
Most of subdirectories represents individual modules and contains their own README.
# Documentation
Actualised documentation is on [google drive](https://drive.google.com/drive/folders/1o40kh_8BgrMI3BzPyfNT0ZLG_5mrIjEy?usp=sharing).
Lost_Edge/ \
├─ [Audio/](Audio) ---> Audio data (e.g. music, sounds, dialogues) \
├─ [Documentation/](Documentation) ---> Documentation (e.g. game and level design) \
├─ [Fonts/](Fonts) ---> Fonts used in project \
├─ [Images/](Images) ---> Image data (e.g. textures, postures, logo) \
├─ [Models/](Models) ---> Models data (e.g. characters, decorations, foliages) \
├─ [ReleaseBuilds/](ReleaseBuilds) \
│ ├─ Legacy/ ---> Legacy build created as high school project \
│ └─ vX.X/ ---> Current builds of Lost_Edge (university project) \
├─ UnrealProject/ \
│ ├─ [Lost_Edge/](UnrealProject/Lost_Edge) ---> Current unreal project of Lost_Edge \
│ └─ Lost_Edge_Legacy/ ---> Legacy unreal project created as high school project \
└─[VoiceGenerator/](VoiceGenerator) ---> AI voice generation module
# How to compile
It's recommended to work in Unreal Engine 5.4 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file "UnrealProject/Lost_Edge/Lost_Edge.uproject". Or by typing "%UE5PATH%\Engine\Build\BatchFiles\GenerateProjectFiles.bat" "%REPOPATH%\UnrealProject\Lost_Edge\Lost_Edge.uproject". \
After generating, "Lost_Edge.sln" can be opened and project can start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own "check()" function. This error can be used to determine that in the file "%UE5PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp" it is necessary to comment out the warning macro on lines 52-54 and rename the function itself on line 957 to, for example, "checkcv()".
After this modification, the build should run fine and it is possible to open the project in UnrealEngine.
# Building your own project copy
Building a copy of the game requires only [UnrealProject/LostEdge/](UnrealProject/Lost_Edge) directory. \
For that purposes you can download the directory as archive or do a sparse checkout via commands below.
- git clone --no-checkout https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git
- cd Lost_Edge/
- git sparse-checkout init
- git sparse-checkout set UnrealProject/Lost_Edge
- git checkout master
# Git lfs common issues
The download can be sometimes too long which make git lfs drop the connection with error "LFS: Put "https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git/info/lfs/objects/HASH": read tcp IP:PORT->pixelyfier.com:443: i/o timeout" \
The download can be sometimes too long which makes git lfs drop the connection with error \
`"LFS: Put "https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git/info/lfs/objects/HASH": read tcp IP:PORT->pixelyfier.com:443: i/o timeout"`
- To fix this set local/global repo config `git config lfs.activitytimeout 240`

1
ReleaseBuilds/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
./** filter=lfs diff=lfs merge=lfs -text

View File

@ -84,7 +84,7 @@ CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet'
+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/Lost_Edge")
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/Lost_Edge")
+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="Lost_EdgeGameModeBase")
GameUserSettingsClassName=/Script/Lost_Edge.CustomGameUserSettings
GameUserSettingsClassName=/Script/Lost_Edge.CustomGameSettings
LevelScriptActorClassName=/Script/Lost_Edge.LevelBase
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]

View File

@ -1,4 +1,4 @@
[/Script/Lost_Edge.CustomGameUserSettings]
[/Script/Lost_Edge.CustomGameSettings]
bUseMotionBlur=False
bShowFps=False
bMouseInverted=False

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
# How to compile
It's recommended to work in Unreal Engine 5.4 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file "UnrealProject/Lost_Edge/Lost_Edge.uproject". Or by typing "%UE5PATH%\Engine\Build\BatchFiles\GenerateProjectFiles.bat" "%REPOPATH%\UnrealProject\Lost_Edge\Lost_Edge.uproject". \
After generating, "Lost_Edge.sln" can be opened and project can start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own "check()" function. This error can be used to determine that in the file "%UE5PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp" it is necessary to comment out the warning macro on lines 52-54 and rename the function itself on line 957 to, for example, "checkcv()".
After this modification, the build should run fine and it is possible to open the project in UnrealEngine.

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "CameraModeBase.h"
#include "Camera/CameraComponent.h"
@ -10,12 +9,16 @@
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameUserSettings.h"
#include "CustomGameSettings.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
ACameraModeBase::ACameraModeBase()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_CameraMode.IMC_CameraMode'") };
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
PrimaryActorTick.bCanEverTick = true;
}
@ -25,29 +28,12 @@ void ACameraModeBase::BeginPlay()
auto world = GetWorld();
//GetMovementComponent()->speed
// GetCharacterMovement()->MaxWalkSpeed = moveSpeed;
auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();
if(auto gameSettings = UCustomGameSettings::Get())
{
if(auto camera = FindComponentByClass<UCameraComponent>())
{
camera->PostProcessSettings.MotionBlurAmount = gameSettings->bUseMotionBlur ? 1.0f : 0.0f;
}
if(auto PC = Cast<APlayerController>(GetController()))
{
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
{
inputSubsystem->ClearAllMappings();
for(auto& inputContext : GI->inputContexts)
{
inputSubsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
}
}
}
}
}
@ -66,14 +52,9 @@ void ACameraModeBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComp
void ACameraModeBase::SwitchToPlayerPawn()
{
if(auto gamemode_base = UGameplayStatics::GetGameMode(GetWorld()))
{
if(auto gamemode = Cast<AMainGameModeBase>(gamemode_base))
{
if(auto gamemode = AMainGameModeBase::Get())
gamemode->SwitchCameraMode();
}
}
}
void ACameraModeBase::ElevatePawn(float value)
{

View File

@ -2,11 +2,13 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/SpectatorPawn.h"
#include "CameraModeBase.generated.h"
/**
* Cheap copy of PlayerBase for level fly spectating purposes.
*/
UCLASS()
class ACameraModeBase : public ASpectatorPawn
{
@ -33,4 +35,8 @@ protected:
float moveSpeed = 200;
UPROPERTY(EditDefaultsOnly)
float runSpeedMultiplier = 4;
private:
TSoftObjectPtr<class UInputMappingContext> context;
};

View File

@ -7,7 +7,7 @@
#include "Kismet/GameplayStatics.h"
#include "UObject/Object.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
@ -53,25 +53,6 @@ TArray<int32> UCommonFunctions::GetRandomIntArray(int32 size, int32 min, int32 m
ALevelBase* UCommonFunctions::GetCurrentLevelScript(UObject* obj)
{
if(auto world = obj->GetWorld())
if(auto level = world->GetCurrentLevel())
if(auto script = level->GetLevelScriptActor())
return Cast<ALevelBase>(script);
return nullptr;
}
APlayerBase* UCommonFunctions::GetPlayer(UObject* obj)
{
if(auto pc = UGameplayStatics::GetPlayerController(obj->GetWorld(), 0))
if(auto pawn = pc->GetPawn())
return Cast<APlayerBase>(pawn);
return nullptr;
}
namespace SlowMotion
{
FTimerHandle timer;
@ -86,7 +67,7 @@ void UCommonFunctions::EnterSlowMotion(float duration)
SlowMotion::targetDilation = SlowMotion::slowDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI)
return;
@ -101,7 +82,7 @@ void UCommonFunctions::ExitSlowMotion(float duration)
SlowMotion::targetDilation = SlowMotion::normalDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI)
return;
@ -116,7 +97,7 @@ FWorldDilationChangedDelegate& UCommonFunctions::GetWorldDilationChangedDelegate
void UCommonFunctions::SlowMotionTick()
{
const UWorld* world = UCustomGameInstanceBase::GetGameInstance()->GetWorld();
const UWorld* world = UCustomGameInstance::Get()->GetWorld();
const float currentDilation = UGameplayStatics::GetGlobalTimeDilation(world);
float newDilation = FMath::FInterpTo(currentDilation, SlowMotion::targetDilation, 1, SlowMotion::interpolationSpeed);

View File

@ -2,34 +2,33 @@
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CommonFunctions.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWorldDilationChangedDelegate, float, newDilation);
/**
* Collection of common/universal/without own scope/specific functions.
*/
UCLASS()
class UCommonFunctions : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/** Returns true if the object is UE class template (used for copy/archetype/meta system but not on the level) */
UFUNCTION(BlueprintPure)
static bool IsNonGameObject(class UObject* object);
/** "Overload" of the built-in keys translator (to not build own engine copy) */
UFUNCTION(BlueprintPure)
static FText GetKeyDisplayName(struct FKey key);
/** Recursively destroy actor and all its childs (the default Destroy doesn't have consistent behavior) */
UFUNCTION(BlueprintCallable, Category = Actor)
static void DestroyActorRecursively(class AActor* actor);
UFUNCTION(BlueprintPure)
static TArray<int32> GetRandomIntArray(int32 size = 16, int32 min = 0, int32 max = 16);
UFUNCTION(BlueprintCallable, Category = Level)
static class ALevelBase* GetCurrentLevelScript(class UObject* obj);
UFUNCTION(BlueprintCallable, Category = Player)
static class APlayerBase* GetPlayer(class UObject* obj);
UFUNCTION(BlueprintCallable, Category = World)
@ -41,6 +40,10 @@ public:
static FWorldDilationChangedDelegate& GetWorldDilationChangedDelegate();
UFUNCTION(BlueprintPure)
static TArray<int32> GetRandomIntArray(int32 size = 16, int32 min = 0, int32 max = 16);
template<typename T>
static TArray<T> ArrayDiff(const TArray<T>& a, const TArray<T>& b);
template<typename T>

View File

@ -1,12 +1,12 @@
// Oleg Petruny proprietary.
#include "ContentLoader.h"
#include "CommonFunctions.h"
#include "IPlatformFilePak.h"
#include "Misc/FileHelper.h"
#include "CommonFunctions.h"
#include <regex>
#include <string>
#include <vector>

View File

@ -10,13 +10,16 @@
UENUM(BlueprintType)
enum class EContentDownloadMethod : uint8
{
ClearRandom = 0,
NonRepeatRandom,
RatingOftenRandom
ClearRandom = 0, //!< Always random from 0 to n
NonRepeatRandom, //!< Download the one that isn't downloaded in current runtime (Reset on full set)
RatingOftenRandom //!< NonRepeatRandom combined with asset rating [WIP]
};
DECLARE_DYNAMIC_DELEGATE_OneParam(FContentDownloadedCallback, FString, pakFilePath);
/*
* High language wrapper for Paks(Assets) download and loading.
*/
UCLASS(BlueprintType)
class UContentLoader : public UObject
{
@ -35,12 +38,18 @@ public:
protected:
virtual void BeginDestroy() override;
/** Sends http get request */
void HttpGet(const FString& url, FHttpRequestCompleteDelegate requestCompleteCallback);
/** Parses assets html/json index */
TArray<FString> ParseDirectoryIndex(const TArray<uint8>& content, const FString& contentType);
/** Selects item by desired method */
FString SelectContentByMethod(const TArray<FString>& content, const EContentDownloadMethod method);
/** Reads Pak content */
FString GetPakMountContent(const FString& pakFilePath, TArray<FString>* content = nullptr);
/** Returns mount path to desired content item */
UClass* GetPakClass(const FString& pakContentPath);
/** Cache of already downloaded content at runtime (used by EContentDownloadMethod::NonRepeatRandom)*/
TArray<FString> downloadedContent;
};

View File

@ -0,0 +1,74 @@
// Oleg Petruny proprietary.
#include "CustomGameInstance.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "ContentLoader.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
#include "SaveData.h"
UCustomGameInstance* UCustomGameInstance::instance = nullptr;
void UCustomGameInstance::Init()
{
UGameInstance::Init();
instance = this;
contentLoader = NewObject<UContentLoader>(this);
saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
}
UCustomGameInstance* UCustomGameInstance::Get()
{
return instance;
}
UContentLoader* UCustomGameInstance::GetContentLoader()
{
if(auto GI = Get())
return GI->contentLoader;
return nullptr;
}
void UCustomGameInstance::SaveGame(FName checkpointName)
{
auto levelScript = ALevelBase::Get();
if(!levelScript)
return;
auto player = APlayerBase::Get();
if(!player)
return;
saveData->level = GetWorld()->GetFName();
saveData->state = levelScript->GetState();
saveData->checkpoint = checkpointName;
if(player->leftPocketItem)
saveData->playerLeftPocketItem = player->leftPocketItem->GetFName();
else
saveData->playerLeftPocketItem = FName(TEXT(""));
if(player->rightPocketItem)
saveData->playerRightPocketItem = player->rightPocketItem->GetFName();
else
saveData->playerRightPocketItem = FName(TEXT(""));
UGameplayStatics::SaveGameToSlot(saveData, TEXT("Save"), 0);
}
void UCustomGameInstance::LoadGame()
{
saveData = Cast<USaveData>(UGameplayStatics::LoadGameFromSlot(TEXT("Save"), 0));
if(!saveData)
return;
UGameplayStatics::OpenLevel(this, saveData->level);
}
void UCustomGameInstance::ExitGame()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, true);
}

View File

@ -4,29 +4,29 @@
#include "Engine/GameInstance.h"
#include "CustomGameInstanceBase.generated.h"
#include "CustomGameInstance.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLevelBeginnedDelegate, FName, levelName);
/**
* Expands basic UE game instance.
* Manages saves, handles content loader and can shutdown the game.
*/
UCLASS()
class UCustomGameInstanceBase : public UGameInstance
class UCustomGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
/** Instantiates content loader, dummy save data and applies settings */
virtual void Init() override;
UFUNCTION(BlueprintPure)
static UCustomGameInstanceBase* GetGameInstance();
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Game Instance"))
static UCustomGameInstance* Get();
UFUNCTION(BlueprintPure)
static class UContentLoader* GetContentLoader();
UFUNCTION(BlueprintCallable, Category = Settings)
void ApplyMouseSettings();
void AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator);
UFUNCTION(BlueprintCallable, Category = Save)
void SaveGame(FName checkpointName);
UFUNCTION(BlueprintCallable, Category = Save)
@ -34,17 +34,11 @@ public:
UFUNCTION(BlueprintCallable)
void ExitGame();
static UCustomGameInstanceBase* instance;
static UCustomGameInstance* instance;
/** Public delegate called by ALevelBase instance on instantiation */
FLevelBeginnedDelegate OnLevelBeginned;
UPROPERTY(EditDefaultsOnly)
TSet<TSubclassOf<class UInteractableActivator>> interactionsActivators;
TSet<TSubclassOf<class UInteractableModificator>> interactionsModificators;
UPROPERTY(EditDefaultsOnly)
TSet<TSoftObjectPtr<class UInputMappingContext>> inputContexts;
UPROPERTY(VisibleAnywhere)
class USaveData* saveData = nullptr;

View File

@ -1,149 +0,0 @@
// Oleg Petruny proprietary.
#include "CustomGameInstanceBase.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "CommonFunctions.h"
#include "ContentLoader.h"
#include "CustomGameUserSettings.h"
#include "Interactable/Activators/InCameraInteractableActivator.h"
#include "Interactable/Activators/RaycastInteractableActivator.h"
#include "Interactable/Modificators/ActivateInteractableModificator.h"
#include "Interactable/Modificators/SawInteractableModificator.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
#include "SaveData.h"
UCustomGameInstanceBase* UCustomGameInstanceBase::instance = nullptr;
void UCustomGameInstanceBase::Init()
{
UGameInstance::Init();
instance = this;
contentLoader = NewObject<UContentLoader>(this);
interactionsActivators.Add(URaycastInteractableActivator::StaticClass());
interactionsActivators.Add(UInCameraInteractableActivator::StaticClass());
for(auto& modificator : interactionsModificators)
{
if(modificator.GetDefaultObject()->GetMappingContext())
{
inputContexts.Add(modificator.GetDefaultObject()->GetMappingContext());
}
}
ApplyMouseSettings();
saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
}
UCustomGameInstanceBase* UCustomGameInstanceBase::GetGameInstance()
{
return instance;
}
UContentLoader* UCustomGameInstanceBase::GetContentLoader()
{
if(auto GI = GetGameInstance())
return GI->contentLoader;
return nullptr;
}
void UCustomGameInstanceBase::ApplyMouseSettings()
{
if(auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings())
{
for(auto& context : inputContexts)
{
if(!context.LoadSynchronous())
continue;
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Key == EKeys::Mouse2D)
{
for(auto& modifier : mapping.Modifiers)
{
if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
{
negate_modifier->bY = !gameSettings->bMouseInverted;
}
if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
{
scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
}
}
}
}
UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
}
}
}
void UCustomGameInstanceBase::AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
bool alreadyPresent = false;
interactionsModificators.Add(modificator, &alreadyPresent);
if(!alreadyPresent)
{
if(auto IC = modificator.GetDefaultObject()->GetMappingContext())
{
inputContexts.Add(IC);
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
inputSubsystem->AddMappingContext(IC, 0);
}
}
}
}
}
void UCustomGameInstanceBase::SaveGame(FName checkpointName)
{
auto levelScript = UCommonFunctions::GetCurrentLevelScript(this);
if(!levelScript)
return;
auto player = UCommonFunctions::GetPlayer(this);
if(!player)
return;
saveData->level = GetWorld()->GetFName();
saveData->state = levelScript->GetState();
saveData->checkpoint = checkpointName;
if(player->leftPocketItem)
saveData->playerLeftPocketItem = player->leftPocketItem->GetFName();
else
saveData->playerLeftPocketItem = FName(TEXT(""));
if(player->rightPocketItem)
saveData->playerRightPocketItem = player->rightPocketItem->GetFName();
else
saveData->playerRightPocketItem = FName(TEXT(""));
UGameplayStatics::SaveGameToSlot(saveData, TEXT("Save"), 0);
}
void UCustomGameInstanceBase::LoadGame()
{
saveData = Cast<USaveData>(UGameplayStatics::LoadGameFromSlot(TEXT("Save"), 0));
if(!saveData)
return;
UGameplayStatics::OpenLevel(this, saveData->level);
}
void UCustomGameInstanceBase::ExitGame()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, true);
}

View File

@ -0,0 +1,28 @@
// Oleg Petruny proprietary.
#include "CustomGameSettings.h"
#include "Kismet/GameplayStatics.h"
UCustomGameSettings::UCustomGameSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{
bUseMotionBlur = false;
bShowFps = false;
bMouseInverted = false;
fMouseSensetivity = 1.0f;
}
UCustomGameSettings* UCustomGameSettings::Get()
{
return Cast<UCustomGameSettings>(UGameUserSettings::GetGameUserSettings());
}
void UCustomGameSettings::SetMouseSensetivity(float value)
{
fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f);
}
float UCustomGameSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}

View File

@ -0,0 +1,49 @@
// Oleg Petruny proprietary.
#pragma once
#include "GameFramework/GameUserSettings.h"
#include "CustomGameSettings.generated.h"
/**
* Manages custom game settings.
* Mouse sensetivity and inversion, motion blur usage, fps show.
*/
UCLASS(Config = Game, defaultconfig)
class UCustomGameSettings : public UGameUserSettings
{
GENERATED_UCLASS_BODY()
public:
// Is auto defined by UE but implementation is in cpp
//UCustomGameSettings(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintPure, Category = Settings, meta = (DisplayName = "Get Custom Game Settings"))
static UCustomGameSettings* Get();
/**
* Sets mouse sensetivity multiplier
* @param value [0.1 - 2.0]
*/
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
/** Returns mouse sensetivity multiplier in [0.1 - 2.0] */
UFUNCTION(BlueprintCallable, Category = Settings)
float GetMouseSensetivity() const;
UPROPERTY(Config, BlueprintReadWrite)
bool bUseMotionBlur;
UPROPERTY(Config, BlueprintReadWrite)
bool bShowFps;
UPROPERTY(Config, BlueprintReadWrite)
bool bMouseInverted;
protected:
UPROPERTY(Config)
float fMouseSensetivity;
};

View File

@ -1,29 +0,0 @@
// Oleg Petruny proprietary.
#include "CustomGameUserSettings.h"
#include "Kismet/GameplayStatics.h"
UCustomGameUserSettings::UCustomGameUserSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{
bUseMotionBlur = false;
bShowFps = false;
bMouseInverted = false;
fMouseSensetivity = 1.0f;
}
UCustomGameUserSettings* UCustomGameUserSettings::GetCustomGameUserSettings()
{
return Cast<UCustomGameUserSettings>(UGameUserSettings::GetGameUserSettings());
}
void UCustomGameUserSettings::SetMouseSensetivity(float value)
{
fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f);
}
float UCustomGameUserSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}

View File

@ -1,41 +0,0 @@
// Oleg Petruny proprietary.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "CustomGameUserSettings.generated.h"
UCLASS()
class UCustomGameUserSettings : public UGameUserSettings
{
GENERATED_UCLASS_BODY()
public:
UFUNCTION(BlueprintCallable, Category = Settings)
static UCustomGameUserSettings* GetCustomGameUserSettings();
// Sets mouse sensetivity multiplier
// @param value [0.1 - 2.0]
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
// Returns mouse sensetivity multiplier in [0.1 - 2.0]
UFUNCTION(BlueprintCallable, Category = Settings)
float GetMouseSensetivity() const;
UPROPERTY(Config, BlueprintReadWrite)
bool bUseMotionBlur;
UPROPERTY(Config, BlueprintReadWrite)
bool bShowFps;
UPROPERTY(Config, BlueprintReadWrite)
bool bMouseInverted;
protected:
UPROPERTY(Config)
float fMouseSensetivity;
};

View File

@ -0,0 +1,116 @@
// Oleg Petruny proprietary.
#include "CustomPlayerController.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "CustomGameInstance.h"
#include "CustomGameSettings.h"
ACustomPlayerController* ACustomPlayerController::instance = nullptr;
UEnhancedInputComponent* ACustomPlayerController::input = nullptr;
UEnhancedInputLocalPlayerSubsystem* ACustomPlayerController::subsystem = nullptr;
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contexts = {};
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contextsBeforeInit = {};
void ACustomPlayerController::AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context)
{
if(!context.IsValid())
return;
if(!UCustomGameInstance::Get()) //game settings not initialized yet
{
contextsBeforeInit.Add(context);
return;
}
ApplyMouseSettings(context);
contexts.Add(context);
if(subsystem)
subsystem->AddMappingContext(context.LoadSynchronous(), 0);
}
void ACustomPlayerController::BeginPlay()
{
Super::BeginPlay();
instance = this;
for(auto& context : contextsBeforeInit)
{
ApplyMouseSettings(context);
contexts.Add(context);
}
contextsBeforeInit.Empty();
subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
if(subsystem)
{
subsystem->ClearAllMappings();
for(auto& inputContext : contexts)
subsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
}
input = Cast<UEnhancedInputComponent>(InputComponent);
if(input)
{
((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Pressed, this, &ACustomPlayerController::OnAnyKeyPressed);
((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Released, this, &ACustomPlayerController::OnAnyKeyReleased);
}
}
void ACustomPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
instance = nullptr;
input = nullptr;
subsystem = nullptr;
Super::EndPlay(EndPlayReason);
}
void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context)
{
auto gameSettings = UCustomGameSettings::Get();
if(!gameSettings)
return;
if(!context.LoadSynchronous())
return;
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Key != EKeys::Mouse2D)
continue;
for(auto& modifier : mapping.Modifiers)
{
if(gameSettings->bMouseInverted)
{
if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
{
negate_modifier->bY = !negate_modifier->bY;
continue;
}
}
if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
}
}
UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
}
void ACustomPlayerController::OnAnyKeyPressed(FKey key)
{
if(onAnyKeyPressed.IsBound())
onAnyKeyPressed.Broadcast(key);
}
void ACustomPlayerController::OnAnyKeyReleased(FKey key)
{
if(onAnyKeyReleased.IsBound())
onAnyKeyReleased.Broadcast(key);
}

View File

@ -0,0 +1,52 @@
// Oleg Petruny proprietary.
#pragma once
#include "GameFramework/PlayerController.h"
#include "CustomPlayerController.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyPressedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyReleasedDelegate, FKey, key);
/**
* De-facto wrapper/interface of all usefull shortcuts and automatization around input system.
* Append new input context here for auto init on BeginPlay and apply user input settings.
* Also, has delegates onAnyKeyPressed/Released.
*/
UCLASS()
class ACustomPlayerController : public APlayerController
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Player Controller"))
static ACustomPlayerController* Get() { return instance; }
UFUNCTION(BlueprintPure)
static class UEnhancedInputComponent* GetInput() { return input; }
UFUNCTION(BlueprintPure)
static class UEnhancedInputLocalPlayerSubsystem* GetInputSubsystem() { return subsystem; }
UFUNCTION(BlueprintCallable)
static void AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context);
FPlayerAnyKeyPressedDelegate onAnyKeyPressed;
FPlayerAnyKeyReleasedDelegate onAnyKeyReleased;
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
static void ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context);
void OnAnyKeyPressed(FKey key);
void OnAnyKeyReleased(FKey key);
static ACustomPlayerController* instance;
static class UEnhancedInputLocalPlayerSubsystem* subsystem;
static class UEnhancedInputComponent* input;
static TSet<TSoftObjectPtr<class UInputMappingContext>> contexts;
/** Contexts added before game instance fully initialized cannot apply game settings because not fully initialized game */
static TSet<TSoftObjectPtr<class UInputMappingContext>> contextsBeforeInit;
};

View File

@ -9,7 +9,7 @@
#include "LevelSequence.h"
#include "LevelSequencePlayer.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/CutsceneSkipWidget.h"
@ -19,14 +19,8 @@
UCutsceneManager::UCutsceneManager()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->inputContexts.Add(_inputContext);
}
}
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
}
void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCallback endCallback)
@ -34,106 +28,82 @@ void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCal
if(!sequence)
return;
FScopeLock lock1(&_sequencesLock);
FScopeLock lock2(&_callbacksLock);
FScopeLock lock1(&sequencesLock);
FScopeLock lock2(&callbacksLock);
if(_endCallbacks.IsEmpty()) // most first sequence, so widgets and binds don't exist
{
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
_lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(_lastPlayer)
{
_lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
OnFirstCutsceneInit();
auto& mapping = _inputContext.LoadSynchronous()->GetMapping(0);
int32 handler1 = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Started, this, &UCutsceneManager::OnInputHold).GetHandle();
int32 handler2 = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Completed, this, &UCutsceneManager::OnInputUnhold).GetHandle();
_handlers = { handler1, handler2 };
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideWidgets();
static FSkipCutsceneDelegate skipCutsceneDelegate;
if(!skipCutsceneDelegate.IsBound())
skipCutsceneDelegate.BindDynamic(this, &UCutsceneManager::SkipSequence);
WM->EnableCutsceneWidget(skipCutsceneDelegate);
}
}
}
}
_nextSequences.Enqueue(sequence);
_endCallbacks.Enqueue(endCallback);
nextSequences.Enqueue(sequence);
endCallbacks.Enqueue(endCallback);
PlayNextSequence();
}
void UCutsceneManager::PlayNextSequence()
{
if(_sequencePlayer)
if(sequencePlayer)
{
if(_sequencePlayer->IsPlaying())
if(sequencePlayer->IsPlaying())
return;
else
_sequencePlayer->MarkAsGarbage();
sequencePlayer->MarkAsGarbage();
}
FScopeLock lock(&_sequencesLock);
FScopeLock lock(&sequencesLock);
ULevelSequence* sequence;
_nextSequences.Dequeue(sequence);
_sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence, FMovieSceneSequencePlaybackSettings{}, _sequencePlayerActor);
_sequencePlayer->OnStop.AddDynamic(this, &UCutsceneManager::OnSequenceEnd);
_sequencePlayer->Play();
nextSequences.Dequeue(sequence);
sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence, FMovieSceneSequencePlaybackSettings{}, sequencePlayerActor);
sequencePlayer->OnStop.AddDynamic(this, &UCutsceneManager::OnSequenceEnd);
sequencePlayer->Play();
}
void UCutsceneManager::SkipSequence()
{
if(!_sequencePlayer || !_sequencePlayer->IsPlaying())
if(!sequencePlayer || !sequencePlayer->IsPlaying())
return;
_lastlySkipped = true;
_sequencePlayer->GoToEndAndStop();
lastlySkipped = true;
sequencePlayer->GoToEndAndStop();
}
void UCutsceneManager::ClearQueue()
{
FScopeLock lock1(&_sequencesLock);
FScopeLock lock2(&_callbacksLock);
if(!_nextSequences.IsEmpty())
_nextSequences.Empty();
if(!_endCallbacks.IsEmpty())
_endCallbacks.Empty();
FScopeLock lock1(&sequencesLock);
FScopeLock lock2(&callbacksLock);
if(!nextSequences.IsEmpty())
nextSequences.Empty();
if(!endCallbacks.IsEmpty())
endCallbacks.Empty();
}
void UCutsceneManager::LockCallback(bool lock)
{
_lockCallback = lock;
lockCallback = lock;
}
void UCutsceneManager::OnSequenceEnd()
{
if(_lockCallback)
if(lockCallback)
return;
_sequencePlayer->MarkAsGarbage();
_sequencePlayer = nullptr;
sequencePlayer->MarkAsGarbage();
sequencePlayer = nullptr;
FScopeLock lock(&_callbacksLock);
FScopeLock lock(&callbacksLock);
FCutsceneEndCallback callback;
_endCallbacks.Dequeue(callback);
endCallbacks.Dequeue(callback);
if(callback.IsBound())
callback.Execute();
if(!_nextSequences.IsEmpty())
if(!nextSequences.IsEmpty())
{
PlayNextSequence();
if(_lastlySkipped)
if(lastlySkipped)
{
_lastlySkipped = false;
if(_holding)
lastlySkipped = false;
if(holding)
{
OnInputHold();
}
@ -147,36 +117,63 @@ void UCutsceneManager::OnSequenceEnd()
WM->ShowWidgets();
}
if(_lastPlayer)
if(lastPlayer)
{
_lastPlayer->UnlockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
lastPlayer->UnlockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
_lastPlayer->inputComponent->RemoveBindingByHandle(_handlers.Key);
_lastPlayer->inputComponent->RemoveBindingByHandle(_handlers.Value);
auto input = ACustomPlayerController::GetInput();
input->RemoveBindingByHandle(handlers.Key);
input->RemoveBindingByHandle(handlers.Value);
_lastPlayer = nullptr;
lastPlayer = nullptr;
}
_lastlySkipped = false;
_holding = false;
lastlySkipped = false;
holding = false;
}
void UCutsceneManager::OnFirstCutsceneInit() // most first sequence, so widgets and binds don't exist
{
if(!endCallbacks.IsEmpty())
return;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(lastPlayer)
{
lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
auto& mapping = context.LoadSynchronous()->GetMapping(0);
auto input = ACustomPlayerController::GetInput();
int32 handler1 = input->BindAction(mapping.Action, ETriggerEvent::Started, this, &UCutsceneManager::OnInputHold).GetHandle();
int32 handler2 = input->BindAction(mapping.Action, ETriggerEvent::Completed, this, &UCutsceneManager::OnInputUnhold).GetHandle();
handlers = { handler1, handler2 };
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideWidgets();
static FSkipCutsceneDelegate skipCutsceneDelegate;
if(!skipCutsceneDelegate.IsBound())
skipCutsceneDelegate.BindDynamic(this, &UCutsceneManager::SkipSequence);
WM->EnableCutsceneWidget(skipCutsceneDelegate);
}
}
}
}
void UCutsceneManager::OnInputHold()
{
_holding = true;
holding = true;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Hold);
}
}
void UCutsceneManager::OnInputUnhold()
{
_holding = false;
holding = false;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Unhold);
}
}

View File

@ -32,22 +32,23 @@ private:
void PlayNextSequence();
UFUNCTION()
void OnSequenceEnd();
void OnFirstCutsceneInit();
void OnInputHold();
void OnInputUnhold();
class ULevelSequencePlayer* _sequencePlayer = nullptr;
class ALevelSequenceActor* _sequencePlayerActor = nullptr;
TQueue<class ULevelSequence*> _nextSequences;
FCriticalSection _sequencesLock;
TQueue<FCutsceneEndCallback> _endCallbacks;
FCriticalSection _callbacksLock;
class ULevelSequencePlayer* sequencePlayer = nullptr;
class ALevelSequenceActor* sequencePlayerActor = nullptr;
TQueue<class ULevelSequence*> nextSequences;
FCriticalSection sequencesLock;
TQueue<FCutsceneEndCallback> endCallbacks;
FCriticalSection callbacksLock;
class APlayerBase* _lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> _inputContext;
TPair<int32, int32> _handlers;
class APlayerBase* lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> context;
TPair<int32, int32> handlers;
bool _lockCallback = false;
bool _lastlySkipped = false;
bool _holding = false;
bool lockCallback = false;
bool lastlySkipped = false;
bool holding = false;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "DialogueManager.h"
#include "Components/AudioComponent.h"
@ -11,7 +10,7 @@
#include "Kismet/KismetMathLibrary.h"
#include "Sound/SoundWave.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/DialogueSkipWidget.h"
@ -21,14 +20,8 @@
UDialogueManager::UDialogueManager()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Dialogue.IMC_Dialogue'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->inputContexts.Add(_inputContext);
}
}
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
}
void UDialogueManager::PlayDialogue(FDialogueEnqueProperties properties, FDialogueEndCallback endCallback)
@ -49,32 +42,28 @@ void UDialogueManager::PlayDialogue(FDialogueEnqueProperties properties, FDialog
FTimerHandle timer;
int32 timerId;
_timersLock.Lock();
timerId = _timers.Num();
_timers.Add(timer);
_timersLock.Unlock();
timersLock.Lock();
timerId = timers.Num();
timers.Add(timer);
timersLock.Unlock();
UGameplayStatics::PlaySound2D(this, row->wave.LoadSynchronous());
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->ShowDialogueWidget(*row);
}
auto func = [properties = properties,
timerId = timerId,
_timersLock = &_timersLock,
_timers = &_timers,
endCallback = endCallback]()
auto func = [properties,
timerId,
timersLock = &timersLock,
timers = &timers,
endCallback]()
{
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(properties.dialogue.LoadSynchronous()->FindRowUnchecked(properties.rowName));
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideDialogueWidget(*row);
}
_timersLock->Lock();
_timers->RemoveAt(timerId);
_timersLock->Unlock();
timersLock->Lock();
timers->RemoveAt(timerId);
timersLock->Unlock();
endCallback.Execute();
};
@ -88,18 +77,96 @@ void UDialogueManager::EnqueDialogue(FDialogueEnqueProperties properties, FDialo
if(!properties.dialogue.LoadSynchronous())
return;
FScopeLock lock1(&_dialoguesLock);
FScopeLock lock2(&_callbacksLock);
FScopeLock lock1(&dialoguesLock);
FScopeLock lock2(&callbacksLock);
if(_endCallbacks.IsEmpty()) // most first dialogue, so widgets and binds don't exist
OnFirstDialogueInit();
nextDialogues.Enqueue(properties);
endCallbacks.Enqueue(endCallback);
PlayNextDialogue();
}
void UDialogueManager::PlayNextDialogue()
{
dialoguesLock.Lock();
auto properties = nextDialogues.Peek();
TArray<FName> rows = properties->dialogue.LoadSynchronous()->GetRowNames();
if(rows.Num() == 0)
{
FDialogueEndCallback callback;
if(endCallbacks.Dequeue(callback))
callback.Execute();
nextDialogues.Pop();
return;
}
if(properties->playMode == EDialoguePlayMode::Random)
{
properties->rowName = rows[FMath::RandRange(0, rows.Num() - 1)];
}
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(properties->dialogue.LoadSynchronous()->FindRowUnchecked(properties->rowName));
if(!row)
{
FDialogueEndCallback callback;
endCallbacks.Dequeue(callback);
callback.ExecuteIfBound();
nextDialogues.Pop();
return;
}
if(properties->playMode == EDialoguePlayMode::Sequential
&& !properties->rowName.ToString().IsNumeric())
{
nextDialogues.Pop();
FDialogueEndCallback callback;
if(endCallbacks.Dequeue(callback))
callback.Execute();
return;
}
if(row->wave.LoadSynchronous())
leadDialogueAudio = UGameplayStatics::SpawnSound2D(this, row->wave.LoadSynchronous());
else
leadDialogueAudio = nullptr;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
WM->ShowDialogueWidget(*row);
FTimerHandle timer;
int32 timerId;
const float duration = row->wave ? row->wave->GetDuration() : row->duration;
GetWorld()->GetTimerManager().SetTimer(timer, [&]() { OnDialogueEnd(); }, duration, false);
timersLock.Lock();
timerId = timers.Num();
timers.Add(timer);
timersLock.Unlock();
leadDialogueTimerId = timerId;
leadDialogueProperties = properties;
dialoguesLock.Unlock();
}
void UDialogueManager::OnFirstDialogueInit() // most first dialogue, so widgets and binds don't exist
{
if(!endCallbacks.IsEmpty())
return;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
_lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(_lastPlayer)
lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(lastPlayer)
{
auto& mapping = _inputContext.LoadSynchronous()->GetMapping(0);
_inputHandler = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
auto& mapping = context.LoadSynchronous()->GetMapping(0);
inputHandler = ACustomPlayerController::GetInput()->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
@ -114,93 +181,16 @@ void UDialogueManager::EnqueDialogue(FDialogueEnqueProperties properties, FDialo
}
}
_nextDialogues.Enqueue(properties);
_endCallbacks.Enqueue(endCallback);
PlayNextDialogue();
}
void UDialogueManager::PlayNextDialogue()
{
_dialoguesLock.Lock();
auto properties = _nextDialogues.Peek();
TArray<FName> rows = properties->dialogue.LoadSynchronous()->GetRowNames();
if(rows.Num() == 0)
{
FDialogueEndCallback callback;
if(_endCallbacks.Dequeue(callback))
callback.Execute();
_nextDialogues.Pop();
return;
}
if(properties->playMode == EDialoguePlayMode::Random)
{
properties->rowName = rows[FMath::RandRange(0, rows.Num() - 1)];
}
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(properties->dialogue.LoadSynchronous()->FindRowUnchecked(properties->rowName));
if(!row)
{
FDialogueEndCallback callback;
_endCallbacks.Dequeue(callback);
callback.ExecuteIfBound();
_nextDialogues.Pop();
return;
}
if(properties->playMode == EDialoguePlayMode::Sequential)
{
if(!properties->rowName.ToString().IsNumeric())
{
_nextDialogues.Pop();
FDialogueEndCallback callback;
if(_endCallbacks.Dequeue(callback))
callback.Execute();
return;
}
}
if(row->wave.LoadSynchronous())
leadDialogueAudio = UGameplayStatics::SpawnSound2D(this, row->wave.LoadSynchronous());
else
leadDialogueAudio = nullptr;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->ShowDialogueWidget(*row);
}
FTimerHandle timer;
int32 timerId;
const float duration = row->wave ? row->wave->GetDuration() : row->duration;
GetWorld()->GetTimerManager().SetTimer(timer, [&]() { OnDialogueEnd(); }, duration, false);
_timersLock.Lock();
timerId = _timers.Num();
_timers.Add(timer);
_timersLock.Unlock();
leadDialogueTimerId = timerId;
leadDialogueProperties = properties;
_dialoguesLock.Unlock();
}
void UDialogueManager::SkipDialogue()
{
if(_timers.Num() == 0 || leadDialogueTimerId < 0)
if(timers.Num() == 0 || leadDialogueTimerId < 0)
return;
_timersLock.Lock();
GetWorld()->GetTimerManager().ClearTimer(_timers[leadDialogueTimerId]);
_timers.RemoveAt(leadDialogueTimerId);
timersLock.Lock();
GetWorld()->GetTimerManager().ClearTimer(timers[leadDialogueTimerId]);
timers.RemoveAt(leadDialogueTimerId);
leadDialogueTimerId = -1;
_timersLock.Unlock();
timersLock.Unlock();
if(leadDialogueAudio)
leadDialogueAudio->Stop();
@ -210,25 +200,25 @@ void UDialogueManager::SkipDialogue()
void UDialogueManager::ClearQueue()
{
if(!_nextDialogues.IsEmpty())
_nextDialogues.Empty();
if(!_endCallbacks.IsEmpty())
_endCallbacks.Empty();
if(!nextDialogues.IsEmpty())
nextDialogues.Empty();
if(!endCallbacks.IsEmpty())
endCallbacks.Empty();
}
void UDialogueManager::LockCallback(bool lock)
{
_lockCallback = lock;
lockCallback = lock;
}
void UDialogueManager::BeginDestroy()
{
if(auto world = GetWorld())
{
_timersLock.Lock();
for(auto& timer : _timers)
timersLock.Lock();
for(auto& timer : timers)
world->GetTimerManager().ClearTimer(timer);
_timersLock.Unlock();
timersLock.Unlock();
}
UObject::BeginDestroy();
@ -236,41 +226,35 @@ void UDialogueManager::BeginDestroy()
void UDialogueManager::OnDialogueEnd()
{
_dialoguesLock.Lock();
dialoguesLock.Lock();
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(leadDialogueProperties->dialogue.LoadSynchronous()->FindRowUnchecked(leadDialogueProperties->rowName));
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideDialogueWidget(*row);
}
if(leadDialogueProperties->playMode == EDialoguePlayMode::Sequential)
{
if(leadDialogueProperties->rowName.ToString().IsNumeric())
if(leadDialogueProperties->playMode == EDialoguePlayMode::Sequential
&& leadDialogueProperties->rowName.ToString().IsNumeric())
{
leadDialogueProperties->rowName = FName(FString::FromInt(FCString::Atoi(*(leadDialogueProperties->rowName.ToString())) + 1));
}
}
_dialoguesLock.Unlock();
dialoguesLock.Unlock();
if(!_endCallbacks.IsEmpty())
if(!endCallbacks.IsEmpty())
PlayNextDialogue();
_dialoguesLock.Lock();
if(_endCallbacks.IsEmpty() && _lastPlayer)
dialoguesLock.Lock();
if(endCallbacks.IsEmpty() && lastPlayer)
{
_lastPlayer->inputComponent->RemoveBindingByHandle(_inputHandler);
_lastPlayer = nullptr;
ACustomPlayerController::GetInput()->RemoveBindingByHandle(inputHandler);
lastPlayer = nullptr;
}
_dialoguesLock.Unlock();
dialoguesLock.Unlock();
}
void UDialogueManager::OnInputPress()
{
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateDialogueWidget(EInputAnimatedWidgetAnimation::Click);
}
}

View File

@ -68,7 +68,7 @@ class UDialogueManager : public UObject
public:
UDialogueManager();
// Ignores play mode and force pushing dialogue
/** Ignores play mode and force pushing dialogue */
UFUNCTION(BlueprintCallable)
void PlayDialogue(FDialogueEnqueProperties properties, FDialogueEndCallback endCallback);
@ -89,26 +89,28 @@ protected:
private:
void PlayNextDialogue();
void OnFirstDialogueInit();
UFUNCTION()
void OnDialogueEnd();
void OnInputPress();
TQueue<FDialogueEnqueProperties> _nextDialogues;
FCriticalSection _dialoguesLock;
TQueue<FDialogueEndCallback> _endCallbacks;
FCriticalSection _callbacksLock;
TQueue<FDialogueEnqueProperties> nextDialogues;
FCriticalSection dialoguesLock;
TQueue<FDialogueEndCallback> endCallbacks;
FCriticalSection callbacksLock;
TArray<FTimerHandle> _timers;
FCriticalSection _timersLock;
TArray<FTimerHandle> timers;
FCriticalSection timersLock;
int32 leadDialogueTimerId = -1;
FDialogueEnqueProperties* leadDialogueProperties;
class UAudioComponent* leadDialogueAudio;
class APlayerBase* _lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> _inputContext;
int32 _inputHandler;
class APlayerBase* lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> context;
int32 inputHandler;
bool _lockCallback = false;
bool lockCallback = false;
};

View File

@ -1,9 +1,9 @@
// Oleg Petruny proprietary.
#include "InCameraInteractableActivator.h"
#include "CommonFunctions.h"
#include "Interactable/Interactable.h"
#include "InteractableScreenCapturer.h"
UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInitializer& ObjectInitializer)
@ -20,10 +20,10 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
activatorType = EActivatorType::Saw;
scanDistance = 7000;
_capturer = CreateDefaultSubobject<UInteractableScreenCapturer>(TEXT("UInCameraInteractableActivator_UInteractableScreenCapturer"));
_capturer->interactableInScreenDelegate.BindUObject(this, &UInCameraInteractableActivator::NewSeenInteractable_Implementation);
_capturer->SetupAttachment(this);
_capturer->scanDistance = scanDistance;
capturer = CreateDefaultSubobject<UInteractableScreenCapturer>(TEXT("UInCameraInteractableActivator_UInteractableScreenCapturer"));
capturer->interactableInScreenDelegate.BindUObject(this, &UInCameraInteractableActivator::NewSeenInteractable);
capturer->SetupAttachment(this);
capturer->scanDistance = scanDistance;
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
@ -31,27 +31,28 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
void UInCameraInteractableActivator::OnRegister()
{
UInteractableActivator::OnRegister();
_capturer->RegisterComponent();
_capturer->Activate();
Super::OnRegister();
capturer->RegisterComponent();
capturer->Activate();
}
void UInCameraInteractableActivator::NewSeenInteractable_Implementation(AInteractable* interactable)
void UInCameraInteractableActivator::NewSeenInteractable(AInteractable* interactable)
{
_interactablesToActivate.Enqueue(interactable);
interactablesToActivate.Enqueue(interactable);
SetComponentTickEnabled(true);
}
void UInCameraInteractableActivator::Scan_Implementation()
{
SetComponentTickEnabled(false);
while(!_interactablesToActivate.IsEmpty())
while(!interactablesToActivate.IsEmpty())
{
AInteractable* interactable;
_interactablesToActivate.Dequeue(interactable);
interactablesToActivate.Dequeue(interactable);
if(interactableActivatedDelegate.IsBound())
{
interactableActivatedDelegate.Execute(interactable, activatorType);
OnNewSeenInteractable(interactable);
}
}
}

View File

@ -2,13 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableActivator.h"
#include "InCameraInteractableActivator.generated.h"
UCLASS(Blueprintable, BlueprintType)
/**
* Activates interactable only once if is in a camera view
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Activates interactable only once if is in a camera view"), MinimalAPI)
class UInCameraInteractableActivator : public UInteractableActivator
{
GENERATED_BODY()
@ -19,14 +20,30 @@ public:
protected:
virtual void OnRegister() override;
virtual bool AutoInstantiateInPlayer() override { return true; }
/**
* Scan is performed independently by _capturer member object.
* This implementation just activates interactables in the game thread.
*/
virtual void Scan_Implementation() override;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void NewSeenInteractable(class AInteractable* interactable);
virtual void NewSeenInteractable_Implementation(class AInteractable* interactable);
/**
* Thread safe enques interactable for activation in the next game tick
* @param interactable .. interactable to activate
*/
UFUNCTION(BlueprintCallable)
void NewSeenInteractable(class AInteractable* interactable);
/**
* Called after interactable activation
* @param interactable .. interactable activated
*/
UFUNCTION(BlueprintImplementableEvent)
void OnNewSeenInteractable(class AInteractable* interactable);
private:
UPROPERTY()
class UInteractableScreenCapturer* _capturer;
TQueue<class AInteractable*> _interactablesToActivate;
class UInteractableScreenCapturer* capturer;
TQueue<class AInteractable*> interactablesToActivate;
};

View File

@ -1,19 +1,23 @@
// Oleg Petruny proprietary.
#include "InteractableActivator.h"
#include "Engine/CollisionProfile.h"
#include "Interactable/Interactable.h"
#include "PlayerBase.h"
UInteractableActivator::UInteractableActivator(const FObjectInitializer& ObjectInitializer)
: USceneComponent(ObjectInitializer)
{
if(HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
if(GetClass() != UInteractableActivator::StaticClass())
{
return;
AInteractable::AppendActivatorClass(GetClass());
}
if(HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
return;
world = GetWorld();
player = Cast<APlayerBase>(GetOwner());

View File

@ -3,7 +3,6 @@
#pragma once
#include "Components/SceneComponent.h"
#include "CoreMinimal.h"
#include "Interactable/Interactable.h"
@ -12,15 +11,22 @@
DECLARE_DELEGATE_TwoParams(FInteractableActivated, AInteractable*, EActivatorType);
DECLARE_DELEGATE_TwoParams(FInteractableDeactivated, AInteractable*, EActivatorType);
UCLASS(Abstract, Blueprintable, BlueprintType)
/**
* Activates interactable based on type
*/
UCLASS(Abstract, Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (ShortTooltip = "Activates interactable based on type"), MinimalAPI)
class UInteractableActivator : public USceneComponent
{
GENERATED_BODY()
public:
/** Append itself to CustomGameInstance modificators registry */
UInteractableActivator(const FObjectInitializer& ObjectInitializer);
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
virtual bool AutoInstantiateInPlayer() { return false; }
/** Resets activator state forcing to rescan */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void Rescan();
virtual void Rescan_Implementation() {}
@ -29,6 +35,7 @@ public:
FInteractableActivated interactableDeactivatedDelegate;
protected:
/** Activator scan function in game thread tick */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void Scan();
virtual void Scan_Implementation() PURE_VIRTUAL(UInteractableActivator::Scan_Implementation, );

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "InteractableScreenCapturer.h"
#include "Engine/Texture.h"
@ -16,9 +15,12 @@
#include "Interactable/Interactable.h"
#include "InteractableScreenCapturerBitMapCS.h"
namespace
{
constexpr float tickInterval = 1.0f / 5;
constexpr float textureWidth = 1280 / 2;
constexpr float textureHeight = 720 / 2;
}
UInteractableScreenCapturer::UInteractableScreenCapturer(const FObjectInitializer& ObjectInitializer)
: USceneCaptureComponent2D(ObjectInitializer)
@ -85,7 +87,7 @@ void UInteractableScreenCapturer::TickComponent(float DeltaTime, enum ELevelTick
{
USceneCaptureComponent2D::TickComponent(DeltaTime, TickType, ThisTickFunction);
CaptureScene();
GetCameraView(DeltaTime, _view);
GetCameraView(DeltaTime, view);
Process();
}
break;
@ -104,7 +106,7 @@ void UInteractableScreenCapturer::Process()
[
capture = TextureTarget->GetResource()->TextureRHI,
world = GetWorld(),
view = _view,
view = view,
//output = _output->GetResource()->TextureRHI,
this
]
@ -262,6 +264,7 @@ void UInteractableScreenCapturer::Process()
AsyncTask(ENamedThreads::GameThread, [=, this]()
{
interactableInScreenDelegate.Execute(interactable);
OnInteractableInScreen(interactable);
});
}
}

View File

@ -9,7 +9,10 @@
DECLARE_DELEGATE_OneParam(FInteractableInScreen, class AInteractable*);
UCLASS(hidecategories = (Collision, Object, Physics, SceneComponent), ClassGroup = Rendering, editinlinenew, meta = (BlueprintSpawnableComponent), MinimalAPI)
/**
* Notifies only once about interactable is in a camera view
*/
UCLASS(BlueprintType, hidecategories = (Collision, Object, Physics, SceneComponent), ClassGroup = Rendering, editinlinenew, meta = (BlueprintSpawnableComponent, ShortTooltip = "Notifies only once about interactable is in a camera view"), MinimalAPI)
class UInteractableScreenCapturer : public USceneCaptureComponent2D
{
GENERATED_BODY()
@ -19,15 +22,18 @@ public:
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
FInteractableInScreen interactableInScreenDelegate;
UFUNCTION(BlueprintImplementableEvent)
void OnInteractableInScreen(class AInteractable* interactable);
UPROPERTY(EditAnywhere)
float scanDistance = 7000;
protected:
/** Enques render thread task to find obect on screen */
void Process();
private:
FMinimalViewInfo _view;
FMinimalViewInfo view; //!< Camera view cache
TSet<class AInteractable*> _sawInteractables;
//class UTextureRenderTarget2D* _output;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "RaycastInteractableActivator.h"
#include "DrawDebugHelpers.h"
@ -14,12 +13,12 @@ URaycastInteractableActivator::URaycastInteractableActivator(const FObjectInitia
void URaycastInteractableActivator::Rescan_Implementation()
{
_last = nullptr;
lastInteractable = nullptr;
}
void URaycastInteractableActivator::Scan_Implementation()
{
FHitResult result{};
FHitResult result;
FVector startLocation = GetComponentLocation();
FVector endLocation = startLocation + (GetComponentRotation().Vector() * scanDistance);
@ -32,17 +31,17 @@ void URaycastInteractableActivator::Scan_Implementation()
if(result.bBlockingHit)
{
if(_last != result.GetActor())
if(lastInteractable != result.GetActor())
{
if(_last)
if(lastInteractable)
{
interactableDeactivatedDelegate.Execute(_last, activatorType);
_last = nullptr;
interactableDeactivatedDelegate.Execute(lastInteractable, activatorType);
lastInteractable = nullptr;
}
_activated = true;
activated = true;
if(auto interactable = Cast<AInteractable>(result.GetActor()))
{
_last = interactable;
lastInteractable = interactable;
if(interactableActivatedDelegate.IsBound())
{
interactableActivatedDelegate.Execute(interactable, activatorType);
@ -56,14 +55,14 @@ void URaycastInteractableActivator::Scan_Implementation()
}
else
{
if(_activated)
if(activated)
{
if(interactableDeactivatedDelegate.IsBound())
{
interactableDeactivatedDelegate.Execute(_last, activatorType);
interactableDeactivatedDelegate.Execute(lastInteractable, activatorType);
}
_activated = false;
_last = nullptr;
activated = false;
lastInteractable = nullptr;
}
#ifdef INTERACTABLE_ACTIVATOR_DEBUG

View File

@ -2,12 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableActivator.h"
#include "RaycastInteractableActivator.generated.h"
UCLASS(Blueprintable, BlueprintType)
/**
* Activates interactable with a single raycast from a camera center
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Activates interactable with a single raycast from a camera center"), MinimalAPI)
class URaycastInteractableActivator : public UInteractableActivator
{
GENERATED_BODY()
@ -15,12 +17,14 @@ class URaycastInteractableActivator : public UInteractableActivator
public:
URaycastInteractableActivator(const FObjectInitializer& ObjectInitializer);
virtual bool AutoInstantiateInPlayer() override { return true; }
virtual void Rescan_Implementation() override;
protected:
virtual void Scan_Implementation() override;
private:
class AInteractable* _last = nullptr;
bool _activated = false;
class AInteractable* lastInteractable = nullptr;
bool activated = false;
};

View File

@ -1,15 +1,38 @@
// Oleg Petruny proprietary.
#include "Interactable.h"
#include "Kismet/GameplayStatics.h"
#include "Activators/InteractableActivator.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "Modificators/InteractableModificator.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
TSet<TSubclassOf<class UInteractableActivator>> AInteractable::interactionActivators = {};
TSet<TSubclassOf<class UInteractableModificator>> AInteractable::interactionModificators = {};
void AInteractable::AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator)
{
if(interactionActivators.Contains(activator))
return;
interactionActivators.Add(activator);
}
void AInteractable::AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
if(interactionModificators.Contains(modificator))
return;
interactionModificators.Add(modificator);
auto IC = modificator.GetDefaultObject()->GetMappingContext();
ACustomPlayerController::AppendInputContext(IC);
}
int32 AInteractable::GetActivatedFlags()
{
return activated;
@ -86,13 +109,13 @@ void AInteractable::EndPlay(const EEndPlayReason::Type EndPlayReason)
if(activated)
{
activationLockers.Empty();
_Deactivate(static_cast<EActivatorType>(activated));
Deactivate(static_cast<EActivatorType>(activated));
}
Super::EndPlay(EndPlayReason);
}
void AInteractable::_Activate(EActivatorType type)
void AInteractable::Activate(EActivatorType type)
{
#ifdef INTERACTABLE_DEBUG
GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Cyan, TEXT("Player activate: ") + this->GetName()
@ -113,7 +136,7 @@ void AInteractable::_Activate(EActivatorType type)
if(static_cast<uint8>(modificator.Value->GetActivatorTypes()) & static_cast<uint8>(type))
{
WM->ShowInteractionHints(modificator.Value);
modificator.Value->Bind(player->inputComponent);
modificator.Value->Bind(ACustomPlayerController::GetInput());
}
}
}
@ -124,10 +147,10 @@ void AInteractable::_Activate(EActivatorType type)
collision->SetCustomDepthStencilValue(132);
}
Activate(type);
OnActivate(type);
}
void AInteractable::_Deactivate(EActivatorType type)
void AInteractable::Deactivate(EActivatorType type)
{
#ifdef INTERACTABLE_DEBUG
GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Magenta, TEXT("Player deactivate: ") + this->GetName()
@ -159,5 +182,5 @@ void AInteractable::_Deactivate(EActivatorType type)
collision->SetCustomDepthStencilValue(0);
}
Deactivate(type);
OnDeactivate(type);
}

View File

@ -2,15 +2,18 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interactable.generated.h"
/** Turns on debug of interactable objects */
//#define INTERACTABLE_DEBUG
/** Turns on debug of interactable activator components */
//#define INTERACTABLE_ACTIVATOR_DEBUG
/** Turns on debug of interactable modificator components */
//#define INTERACTABLE_MODIFICATOR_DEBUG
/** Defines activation types */
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EActivatorType : uint8
{
@ -27,38 +30,61 @@ enum class EActivatorType : uint8
};
ENUM_CLASS_FLAGS(EActivatorType);
/**
* Object capable of reacting to activators and execute modificators.
* Sets all needed settings as collision layers on begin play.
*/
UCLASS(Blueprintable, BlueprintType, MinimalAPI)
class AInteractable : public AActor
{
GENERATED_BODY()
public:
static void AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator);
static void AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator);
/** Interactables shared cache */
static TSet<TSubclassOf<class UInteractableActivator>> interactionActivators;
static TSet<TSubclassOf<class UInteractableModificator>> interactionModificators;
public:
/** Returns flags mask of activated types */
int32 GetActivatedFlags();
void _Activate(EActivatorType type);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
/** Receives activate signal from activator of specific type and activates all modificators of that type */
UFUNCTION(BlueprintCallable)
void Activate(EActivatorType type);
virtual void Activate_Implementation(EActivatorType type) {}
void _Deactivate(EActivatorType type);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
/** Receives deactivate signal from activator of specific type and deactivates all modificators of that type */
UFUNCTION(BlueprintCallable)
void Deactivate(EActivatorType type);
virtual void Deactivate_Implementation(EActivatorType type) {}
/**
* All modificators that requires (de)activation lock for current interactable.
* Used manually by modificators to handle operations which can be continued after physical deactivation.
* Eg. MoveModificator movement while mouse buttons are down even if player don't activating interactable anymore.
*/
TSet<class UInteractableModificator*> activationLockers;
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintImplementableEvent)
void OnActivate(EActivatorType type);
UFUNCTION(BlueprintImplementableEvent)
void OnDeactivate(EActivatorType type);
/** Mask of active activator types */
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "EActivatorType"))
int32 activated = 0;
/** Map of modificators to activator types initialized on BeginPlay */
UPROPERTY()
TMap<EActivatorType, class UInteractableModificator*> modificators;
class APlayerBase* player = nullptr;
TArray<UPrimitiveComponent*> collisions;
class APlayerBase* player = nullptr;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "ActivateInteractableModificator.h"
#include "EnhancedInputComponent.h"

View File

@ -2,14 +2,16 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "ActivateInteractableModificator.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FActivateInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for type Use
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for type Use"), MinimalAPI)
class UActivateInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,14 +1,11 @@
// Oleg Petruny proprietary.
#include "EditInteractableModificator.h"
#include "Interactable/Interactable.h"
UEditInteractableModificator::UEditInteractableModificator(const FObjectInitializer& ObjectInitializer)
: UInteractableModificator(ObjectInitializer)
{
activatorTypes |= static_cast<uint8>(EActivatorType::Collide);
}
//UEditInteractableModificator::UEditInteractableModificator(const FObjectInitializer& ObjectInitializer)
// : UInteractableModificator(ObjectInitializer)
//{
// activatorTypes |= static_cast<uint8>(EActivatorType::Collide);
//}

View File

@ -2,17 +2,20 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "EditInteractableModificator.generated.h"
//#include "EditInteractableModificator.generated.h"
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
class UEditInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()
public:
UEditInteractableModificator(const FObjectInitializer& ObjectInitializer);
};
/**
* <WIP>
* Edits object topology on collision
*/
//UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Edits object topology on collision"), MinimalAPI)
//class UEditInteractableModificator : public UInteractableModificator
//{
// GENERATED_BODY()
//
//public:
// UEditInteractableModificator(const FObjectInitializer& ObjectInitializer);
//
//};

View File

@ -1,29 +1,20 @@
// Oleg Petruny proprietary.
#include "InteractableModificator.h"
#include "InputMappingContext.h"
#include "CustomGameInstanceBase.h"
#include "Interactable/Interactable.h"
#include "Widgets/InteractableHintWidget.h"
void UInteractableModificator::OnRegister()
{
UActorComponent::OnRegister();
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->AppendInteractableModificatorClass(this->GetClass());
}
}
AInteractable::AppendModificatorClass(GetClass());
}
const UInputMappingContext* UInteractableModificator::GetMappingContext() const
const TSoftObjectPtr<class UInputMappingContext> UInteractableModificator::GetMappingContext() const
{
return inputMapping.LoadSynchronous();
return inputMapping;
}
EActivatorType UInteractableModificator::GetActivatorTypes() const

View File

@ -3,29 +3,37 @@
#pragma once
#include "Components/ActorComponent.h"
#include "CoreMinimal.h"
#include "InteractableModificator.generated.h"
enum class EActivatorType : uint8;
UCLASS(Abstract, Blueprintable, BlueprintType)
/**
* Do something on activation by specified activator types
*/
UCLASS(Abstract, Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Do something on activation by specified activator types"), MinimalAPI)
class UInteractableModificator : public UActorComponent
{
GENERATED_BODY()
public:
void OnRegister() override;
/** Append itself to CustomGameInstance modificators registry */
virtual void OnRegister() override;
/** Returns input mappings assigned in constructor */
UFUNCTION(BlueprintCallable)
const class UInputMappingContext* GetMappingContext() const;
const TSoftObjectPtr<class UInputMappingContext> GetMappingContext() const;
/** Filters activation type in interractable */
UFUNCTION(BlueprintCallable)
EActivatorType GetActivatorTypes() const;
/** Called from interactable on activation (mostly used to bind input context to internal modificator functions) */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void Bind(class UEnhancedInputComponent* input);
virtual void Bind_Implementation(class UEnhancedInputComponent* input) {}
/** Called from interactable on deactivation (mostly used to unbind input context from internal functions) */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void Unbind();
virtual void Unbind_Implementation() {}

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "InventoryInteractableModificator.h"
#include "EnhancedInputComponent.h"

View File

@ -2,12 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "InventoryInteractableModificator.generated.h"
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Modificator of type Use for storing items in a APlayerBase
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Modificator of type Use for storing items in a APlayerBase"), MinimalAPI)
class UInventoryInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "MoveInteractableModificator.h"
#include "EnhancedInputComponent.h"
@ -90,7 +89,7 @@ void UMoveInteractableModificator::Unbind_Implementation()
bindindingHandlers.Empty();
SetComponentTickEnabled(false);
actor->_Deactivate(GetActivatorTypes());
actor->Deactivate(GetActivatorTypes());
OnMoveDeactivated.Broadcast();
}

View File

@ -2,7 +2,6 @@
#pragma once
#include "CoreMinimal.h"
#include "InputActionValue.h"
#include "InteractableModificator.h"
@ -13,7 +12,10 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDeactivateMoveInteractableModificatorActivat
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FHoldingMoveInteractableModificatorActivatedDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FRotatingMoveInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for Move type activator
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for Move type activator"), MinimalAPI)
class UMoveInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "SawInteractableModificator.h"
#include "Interactable/Interactable.h"

View File

@ -2,14 +2,16 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "SawInteractableModificator.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSawInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for Saw type activator
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for Saw type activator"), MinimalAPI)
class USawInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,12 +1,11 @@
// Oleg Petruny proprietary.
#include "Checkpoint.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
void ACheckpoint::SaveGame()
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
if(auto GI = UCustomGameInstance::Get())
GI->SaveGame(GetFName());
}

View File

@ -6,6 +6,7 @@
#include "Checkpoint.generated.h"
/** Simple actor for game save */
UCLASS(Blueprintable, BlueprintType, MinimalAPI)
class ACheckpoint : public AActor
{

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level1.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level2.h"
void ALevel2::BeginPlay()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level3.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level4.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level5.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "LevelBase.h"
#include "Engine/StaticMesh.h"
@ -10,8 +9,8 @@
#include "Kismet/GameplayStatics.h"
#include "LevelSequencePlayer.h"
#include "CommonFunctions.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
#include "CustomPlayerController.h"
#include "Interactable/Interactable.h"
#include "Levels/Checkpoint.h"
#include "MainGameModeBase.h"
@ -19,21 +18,17 @@
#include "PlayerBase.h"
#include "SaveData.h"
ALevelBase* ALevelBase::Get()
{
if(auto GM = AMainGameModeBase::Get())
return GM->leadLevel.Get();
return nullptr;
}
void ALevelBase::BeginPlay()
{
AMainGameModeBase::leadLevel = TStrongObjectPtr<ALevelBase>{ this };
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
for(TActorIterator<AMinigame> it(GetWorld()); it; ++it)
{
GI->inputContexts.Add(it->GetInputMappings());
}
}
}
ALevelScriptActor::BeginPlay();
BroadcastNewLevelBeginPlay();
@ -62,7 +57,7 @@ void ALevelBase::IterateToState(int32 to)
void ALevelBase::BroadcastNewLevelBeginPlay()
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
if(auto GI = UCustomGameInstance::Get())
GI->OnLevelBeginned.Broadcast(GetFName());
}
@ -83,13 +78,13 @@ void ALevelBase::StartLevelAnimations()
void ALevelBase::ApplySaveData()
{
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI || !GI->saveData || GI->saveData->level != GetWorld()->GetFName())
return;
IterateToState(GI->saveData->state);
auto player = UCommonFunctions::GetPlayer(this);
auto player = APlayerBase::Get();
if(!player)
return;

View File

@ -6,15 +6,28 @@
#include "LevelBase.generated.h"
/**
* Expands basic UE level script actor.
* Has int type level states and events.
* Brodcasts level instantiation by CustomGameInstance::OnLevelBeginned(FName).
* On BeginPlay applies last save data if valid,
* and instantiate all sequencers from array onBeginPlaySequences.
*/
UCLASS(BlueprintType)
class ALevelBase : public ALevelScriptActor
{
GENERATED_BODY()
public:
/** Returns lead level script */
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Lead Level Script"))
static ALevelBase* Get();
/** Iterates throught level states */
UFUNCTION(BlueprintCallable)
inline void CallNextState() { ++state; NextState(); }
/** Calls specific event id in level */
UFUNCTION(BlueprintImplementableEvent)
void CallEvent(int32 id);
@ -23,9 +36,11 @@ public:
protected:
virtual void BeginPlay() override;
/** Notifies level to process current state */
UFUNCTION(BlueprintImplementableEvent)
void NextState();
/** Shortcut for iterating from current to expected state */
UFUNCTION(BlueprintCallable)
void IterateToState(int32 to);
@ -33,12 +48,13 @@ protected:
void StartLevelAnimations();
void ApplySaveData();
/** List of level animations for instantiation on level BeginPlay */
UPROPERTY(EditDefaultsOnly)
TArray<TSoftObjectPtr<class ULevelSequence>> onBeginPlaySequences;
/** Cache of actors that hold instantiated animations on BeginPlay */
TArray<class ALevelSequenceActor*> onBeginPlaySequencesActors;
/** Current state of level */
UPROPERTY(BlueprintReadOnly)
int32 state = -1;
UPROPERTY(EditDefaultsOnly)
TArray<TSoftObjectPtr<class ULevelSequence>> stateSequences;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "MainGameModeBase.h"
#include "Engine/World.h"
@ -8,7 +7,6 @@
#include "Kismet/GameplayStatics.h"
#include "UObject/ScriptInterface.h"
#include "CustomGameInstanceBase.h"
#include "CutsceneManager.h"
#include "DialogueManager.h"
#include "Levels/LevelBase.h"
@ -16,66 +14,71 @@
#include "QuickTimeEvent.h"
#include "Widgets/WidgetsManager.h"
AMainGameModeBase* AMainGameModeBase::_instance = nullptr;
TStrongObjectPtr<UWidgetsManager> AMainGameModeBase::_widgetsManager = nullptr;
TStrongObjectPtr<UCutsceneManager> AMainGameModeBase::_cutsceneManager = nullptr;
TStrongObjectPtr<UQuickTimeEventManager> AMainGameModeBase::_quickTimeEventManager = nullptr;
TStrongObjectPtr<UDialogueManager> AMainGameModeBase::_dialogueManager = nullptr;
AMainGameModeBase* AMainGameModeBase::instance = nullptr;
TStrongObjectPtr<UWidgetsManager> AMainGameModeBase::widgetsManager = nullptr;
TStrongObjectPtr<UCutsceneManager> AMainGameModeBase::cutsceneManager = nullptr;
TStrongObjectPtr<UQuickTimeEventManager> AMainGameModeBase::quickTimeEventManager = nullptr;
TStrongObjectPtr<UDialogueManager> AMainGameModeBase::dialogueManager = nullptr;
TStrongObjectPtr<ALevelBase> AMainGameModeBase::leadLevel = nullptr;
void AMainGameModeBase::StartPlay()
{
_instance = this;
_widgetsManager = TStrongObjectPtr<UWidgetsManager>{ NewObject<UWidgetsManager>(this, widgetManagerClass) };
_cutsceneManager = TStrongObjectPtr<UCutsceneManager>{ NewObject<UCutsceneManager>(this) };
_quickTimeEventManager = TStrongObjectPtr<UQuickTimeEventManager>{ NewObject<UQuickTimeEventManager>(this) };
_dialogueManager = TStrongObjectPtr<UDialogueManager>{ NewObject<UDialogueManager>(this) };
instance = this;
widgetsManager = TStrongObjectPtr<UWidgetsManager>{ NewObject<UWidgetsManager>(this, widgetManagerClass) };
cutsceneManager = TStrongObjectPtr<UCutsceneManager>{ NewObject<UCutsceneManager>(this) };
quickTimeEventManager = TStrongObjectPtr<UQuickTimeEventManager>{ NewObject<UQuickTimeEventManager>(this) };
dialogueManager = TStrongObjectPtr<UDialogueManager>{ NewObject<UDialogueManager>(this) };
AGameModeBase::StartPlay();
_widgetsManager->Init();
widgetsManager->Init();
}
void AMainGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
_cutsceneManager->LockCallback(true);
//_cutsceneManager->ClearQueue();
cutsceneManager->LockCallback(true);
//cutsceneManager->ClearQueue(); // condition race segfault?
}
bool AMainGameModeBase::SetPause(APlayerController* PC, FCanUnpause CanUnpauseDelegate)
{
if(_widgetsManager)
if(widgetsManager)
{
if(IsPaused())
{
_widgetsManager->HideWidgets();
widgetsManager->HideWidgets();
}
else
{
_widgetsManager->ShowWidgets();
widgetsManager->ShowWidgets();
}
}
return AGameModeBase::SetPause(PC, CanUnpauseDelegate);
}
AMainGameModeBase* AMainGameModeBase::Get()
{
return instance;
}
UWidgetsManager* AMainGameModeBase::GetWidgetsManager()
{
return _widgetsManager.Get();
return widgetsManager.Get();
}
UCutsceneManager* AMainGameModeBase::GetCutsceneManager()
{
return _cutsceneManager.Get();
return cutsceneManager.Get();
}
UQuickTimeEventManager* AMainGameModeBase::GetQuickTimeEventManager()
{
return _quickTimeEventManager.Get();
return quickTimeEventManager.Get();
}
UDialogueManager* AMainGameModeBase::GetDialogueManager()
{
return _dialogueManager.Get();
return dialogueManager.Get();
}
void AMainGameModeBase::CallNextLevelState()
@ -92,39 +95,37 @@ void AMainGameModeBase::CallLevelEvent(int32 id)
void AMainGameModeBase::UpdateQuests(TArray<FText> items)
{
if(_instance && _instance->questsUpdateDelegate.IsBound())
_instance->questsUpdateDelegate.Broadcast(items);
if(instance && instance->questsUpdateDelegate.IsBound())
instance->questsUpdateDelegate.Broadcast(items);
}
void AMainGameModeBase::SwitchCameraMode()
{
static TWeakObjectPtr<APawn> _playerPawn = nullptr;
static TWeakObjectPtr<APawn> playerPawn = nullptr;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
if(auto pawn = PC->GetPawn())
{
if(!_playerPawn.IsValid())
if(!playerPawn.IsValid())
{
auto spawnLoc = pawn->GetActorLocation();
auto spawnRot = pawn->GetActorRotation();
if(auto cameraPawn = GetWorld()->SpawnActor<APawn>(SpectatorClass, spawnLoc, spawnRot))
{
_playerPawn = pawn;
playerPawn = pawn;
PC->Possess(cameraPawn);
}
}
else
{
PC->Possess(_playerPawn.Get());
PC->Possess(playerPawn.Get());
pawn->Destroy();
_playerPawn = nullptr;
playerPawn = nullptr;
}
}
}
if(_widgetsManager)
{
_widgetsManager->UpdateWidgetsOwner();
}
if(widgetsManager.IsValid())
widgetsManager->UpdateWidgetsOwner();
}

View File

@ -8,6 +8,11 @@
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FQuestsUpdateDelegate, TArray<FText>, quests);
/*
* Manager of most gameplay systems.
* Widgets, custcenes, QTE, dialogues, quests, camera mode, level.
* Please, use MainGameMode SetPause to pause the game.
*/
UCLASS()
class AMainGameModeBase : public AGameModeBase
{
@ -18,6 +23,8 @@ public:
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual bool SetPause(APlayerController* PC, FCanUnpause CanUnpauseDelegate = FCanUnpause()) override;
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Main Game Mode"))
static AMainGameModeBase* Get();
UFUNCTION(BlueprintPure)
static class UWidgetsManager* GetWidgetsManager();
UFUNCTION(BlueprintPure)
@ -46,9 +53,9 @@ protected:
TSubclassOf<class UWidgetsManager> widgetManagerClass;
private:
static AMainGameModeBase* _instance;
static TStrongObjectPtr<class UWidgetsManager> _widgetsManager;
static TStrongObjectPtr<class UCutsceneManager> _cutsceneManager;
static TStrongObjectPtr<class UQuickTimeEventManager> _quickTimeEventManager;
static TStrongObjectPtr<class UDialogueManager> _dialogueManager;
static AMainGameModeBase* instance;
static TStrongObjectPtr<class UWidgetsManager> widgetsManager;
static TStrongObjectPtr<class UCutsceneManager> cutsceneManager;
static TStrongObjectPtr<class UQuickTimeEventManager> quickTimeEventManager;
static TStrongObjectPtr<class UDialogueManager> dialogueManager;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "AgeOfWarManager.h"
#include "Camera/CameraComponent.h"

View File

@ -1,12 +1,11 @@
// Oleg Petruny proprietary.
#include "AgeOfWarUnit.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "AgeOfWarManager.h"
#include <Components/BoxComponent.h>
namespace
{

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "CrossyRoadManager.h"
#include "Camera/CameraComponent.h"
@ -10,7 +9,7 @@
#include "InputMappingContext.h"
#include "CrossyRoadObstacle.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
@ -19,7 +18,7 @@ ACrossyRoadManager::ACrossyRoadManager()
: AMinigame()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_Minigame_CrossyRoad.IMC_Minigame_CrossyRoad'") };
input = asset.Object;
context = asset.Object;
auto root = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
@ -43,16 +42,17 @@ void ACrossyRoadManager::Start(APlayerBase* playerPawn, FMinigameEndCallback del
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
for(auto& mapping : input.LoadSynchronous()->GetMappings())
auto input = ACustomPlayerController::GetInput();
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Up"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Up).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Up).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Down"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Down).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Down).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Left"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Left).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Left).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Right"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Right).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Right).GetHandle());
}
playerPos->SetWorldLocation(lines[0]->GetLocationAtTime(lines[0]->Duration * playerLineTime, ESplineCoordinateSpace::World));
@ -70,7 +70,7 @@ void ACrossyRoadManager::End()
PrimaryActorTick.SetTickFunctionEnable(false);
for(int32 handler : inputHandlers)
player->inputComponent->RemoveBindingByHandle(handler);
ACustomPlayerController::GetInput()->RemoveBindingByHandle(handler);
player->UnlockPlayer(FPlayerLock::All());
player->ReturnPlayerView();

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "CrossyRoadObstacle.h"
#include "CrossyRoadManager.h"

View File

@ -1,18 +1,18 @@
// Oleg Petruny proprietary.
#include "FishingManager.h"
#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
AFishingManager::AFishingManager()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_Minigame_Fishing.IMC_Minigame_Fishing'") };
input = asset.Object;
context = asset.Object;
}
void AFishingManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
@ -24,15 +24,17 @@ void AFishingManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delega
player->LockPlayer(FPlayerLock::All());
auto& mapping = input.LoadSynchronous()->GetMapping(0);
handler1 = player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Started, this, &AFishingManager::OnInputHold).GetHandle();
handler2 = player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Completed, this, &AFishingManager::OnInputUnhold).GetHandle();
auto input = ACustomPlayerController::GetInput();
auto& mapping = context.LoadSynchronous()->GetMapping(0);
handler1 = input->BindAction(mapping.Action, ETriggerEvent::Started, this, &AFishingManager::OnInputHold).GetHandle();
handler2 = input->BindAction(mapping.Action, ETriggerEvent::Completed, this, &AFishingManager::OnInputUnhold).GetHandle();
}
void AFishingManager::End()
{
player->inputComponent->RemoveBindingByHandle(handler1);
player->inputComponent->RemoveBindingByHandle(handler2);
auto input = ACustomPlayerController::GetInput();
input->RemoveBindingByHandle(handler1);
input->RemoveBindingByHandle(handler2);
player->UnlockPlayer(FPlayerLock::All());

View File

@ -1,11 +1,10 @@
// Oleg Petruny proprietary.
#include "Minigame.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "CustomPlayerController.h"
#include "PlayerBase.h"
AMinigame::AMinigame()
@ -22,23 +21,13 @@ void AMinigame::End()
OnEnd();
}
void AMinigame::Restart()
{}
UInputMappingContext* AMinigame::GetInputMappings()
{
return input.LoadSynchronous();
}
void AMinigame::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
{
player = playerPawn;
callback = delegate;
if(input)
if(auto PC = Cast<APlayerController>(playerPawn->GetController()))
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
inputSubsystem->AddMappingContext(input.LoadSynchronous(), 0);
if(context.IsValid())
ACustomPlayerController::AppendInputContext(context);
OnStart();
}

View File

@ -15,7 +15,14 @@ enum class EMinigameResult : uint8
DECLARE_DYNAMIC_DELEGATE_TwoParams(FMinigameEndCallback, EMinigameResult, result, int32, score);
UCLASS(Blueprintable, BlueprintType, MinimalAPI, Abstract)
/**
* Interface for all minigame classes.
* Any minigame is started/ended/restarted by executing Start()/End()/Restart() on specific instance.
* 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
*/
UCLASS(Abstract, Blueprintable, BlueprintType, MinimalAPI)
class AMinigame : public AActor
{
GENERATED_BODY()
@ -25,21 +32,21 @@ public:
UFUNCTION(BlueprintCallable)
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate);
UFUNCTION(BlueprintImplementableEvent)
void OnStart();
UFUNCTION(BlueprintCallable)
virtual void End();
UFUNCTION(BlueprintCallable)
virtual void Restart();
UFUNCTION(BlueprintImplementableEvent)
void OnEnd();
UFUNCTION(BlueprintCallable)
virtual void Restart() {}
UFUNCTION(BlueprintImplementableEvent)
void OnRestart();
UFUNCTION(BlueprintPure)
class UInputMappingContext* GetInputMappings();
TSoftObjectPtr<class UInputMappingContext> GetInputMappings() { return context; }
protected:
FMinigameEndCallback callback;
@ -55,6 +62,6 @@ protected:
bool ended = false;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSoftObjectPtr<class UInputMappingContext> input = nullptr;
TSoftObjectPtr<class UInputMappingContext> context = nullptr;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "RythmClickManager.h"
#include "MainGameModeBase.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "SubwaySurfManager.h"
#include "Camera/CameraComponent.h"
@ -9,7 +8,7 @@
#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "SubwaySurfObstacle.h"
@ -19,7 +18,7 @@ ASubwaySurfManager::ASubwaySurfManager()
: AMinigame()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_Minigame_SubwaySurf.IMC_Minigame_SubwaySurf'") };
input = asset.Object;
context = asset.Object;
auto root = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
@ -43,16 +42,17 @@ void ASubwaySurfManager::Start(APlayerBase* playerPawn, FMinigameEndCallback del
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
for(auto& mapping : input.LoadSynchronous()->GetMappings())
auto input = ACustomPlayerController::GetInput();
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Up"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Up).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Up).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Down"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Down).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Down).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Left"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Left).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Left).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Right"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Right).GetHandle());
inputHandlers.Add(input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ASubwaySurfManager::Right).GetHandle());
}
playerPos->SetRelativeLocation(lines[1]->GetLocationAtTime(lines[1]->Duration * 0.01f, ESplineCoordinateSpace::Local));
@ -66,7 +66,7 @@ void ASubwaySurfManager::End()
PrimaryActorTick.SetTickFunctionEnable(false);
for(int32 handler : inputHandlers)
player->inputComponent->RemoveBindingByHandle(handler);
ACustomPlayerController::GetInput()->RemoveBindingByHandle(handler);
player->UnlockPlayer(FPlayerLock::All());
player->ReturnPlayerView();

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "SubwaySurfObstacle.h"
#include "SubwaySurfManager.h"

View File

@ -1,19 +1,16 @@
// Oleg Petruny proprietary.
#include "PlayerBase.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameUserSettings.h"
#include "CustomGameSettings.h"
#include "CustomPlayerController.h"
#include "Interactable/Activators/InteractableActivator.h"
#include "Interactable/Interactable.h"
#include "Interactable/Modificators/InteractableModificator.h"
@ -28,6 +25,10 @@ namespace
APlayerBase::APlayerBase()
: ACharacter()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Player.IMC_Player'") };
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
PrimaryActorTick.bCanEverTick = true;
camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
@ -61,6 +62,13 @@ void APlayerBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponen
ACharacter::SetupPlayerInputComponent(PlayerInputComponent);
}
APlayerBase* APlayerBase::Get()
{
if(auto PC = ACustomPlayerController::Get())
return Cast<APlayerBase>(PC->GetPawn());
return nullptr;
}
void APlayerBase::BeginPlay()
{
ACharacter::BeginPlay();
@ -77,51 +85,15 @@ void APlayerBase::BeginPlay()
cameraManager->ViewPitchMax = maxPitch;
}
auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();
if(camera)
auto gameSettings = UCustomGameSettings::Get();
if(gameSettings && camera)
{
camera->PostProcessSettings.MotionBlurAmount = gameSettings->bUseMotionBlur ? 1.0f : 0.0f;
}
playerController = Cast<APlayerController>(GetController());
if(playerController)
{
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(playerController->GetLocalPlayer()))
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
{
inputSubsystem->ClearAllMappings();
for(auto& inputContext : GI->inputContexts)
{
inputSubsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
}
}
}
inputComponent = Cast<UEnhancedInputComponent>(playerController->InputComponent);
if(inputComponent)
{
((UInputComponent*)inputComponent)->BindAction(TEXT("AnyKey"), IE_Pressed, this, &APlayerBase::OnAnyKeyPressed);
((UInputComponent*)inputComponent)->BindAction(TEXT("AnyKey"), IE_Released, this, &APlayerBase::OnAnyKeyReleased);
}
}
LoadInteractablesActivators();
}
void APlayerBase::OnAnyKeyPressed(FKey key)
{
if(onAnyKeyPressedDelegate.IsBound())
onAnyKeyPressedDelegate.Broadcast(key);
}
void APlayerBase::OnAnyKeyReleased(FKey key)
{
if(onAnyKeyReleasedDelegate.IsBound())
onAnyKeyReleasedDelegate.Broadcast(key);
}
bool APlayerBase::IsMoving()
{
return bIsMoving;
@ -142,7 +114,7 @@ void APlayerBase::LockPlayer(FPlayerLock lock)
inventoryLocked += lock.inventory;
if(interactionLocked)
ResetIntractions();
ResetInteractions();
}
void APlayerBase::UnlockPlayer(FPlayerLock lock)
@ -155,7 +127,7 @@ void APlayerBase::UnlockPlayer(FPlayerLock lock)
inventoryLocked -= lock.inventory;
if(!interactionLocked)
ResetIntractions();
ResetInteractions();
}
void APlayerBase::FlyMode(bool on)
@ -163,7 +135,7 @@ void APlayerBase::FlyMode(bool on)
GetCharacterMovement()->GravityScale = on ? 0.0f : 1.0f;
}
void APlayerBase::ResetIntractions()
void APlayerBase::ResetInteractions()
{
InteractableDeactivated(lastInteractable, static_cast<EActivatorType>(0xFF));
for(auto activator : interactableActivators)
@ -256,15 +228,18 @@ void APlayerBase::UpdatePitch(float min, float max)
}
void APlayerBase::LoadInteractablesActivators()
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
{
TSet<UClass*> instancedActivators;
for(auto& act : GI->interactionsActivators)
for(auto& act : AInteractable::interactionActivators)
{
if(instancedActivators.Contains(act))
if(act.Get())
if(auto obj = Cast<UInteractableActivator>(act.Get()->GetDefaultObject()))
if(!obj->AutoInstantiateInPlayer())
continue;
instancedActivators.Add(act);
if(instancedActivators.Contains(act.Get()))
continue;
instancedActivators.Add(act.Get());
auto component = NewObject<UInteractableActivator>(this, act);
component->interactableActivatedDelegate.BindUObject(this, &APlayerBase::InteractableActivated);
@ -274,7 +249,6 @@ void APlayerBase::LoadInteractablesActivators()
interactableActivators.Add(component);
}
}
}
void APlayerBase::InteractableActivated(AInteractable* interactable, EActivatorType type)
{
@ -284,7 +258,7 @@ void APlayerBase::InteractableActivated(AInteractable* interactable, EActivatorT
if(interactionLocked || interactable->IsHidden())
return;
interactable->_Activate(type);
interactable->Activate(type);
if(interactable != lastInteractable)
lastInteractable = interactable;
@ -295,7 +269,7 @@ void APlayerBase::InteractableDeactivated(AInteractable* interactable, EActivato
if(!interactable)
return;
interactable->_Deactivate(type);
interactable->Deactivate(type);
if(interactable->GetActivatedFlags() == 0)
lastInteractable = nullptr;

View File

@ -8,10 +8,9 @@
enum class EActivatorType : uint8;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyPressedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyReleasedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPlayerMovedDelegate);
/**
* Bit collection of player locks.
*/
USTRUCT(BlueprintType)
struct FPlayerLock
{
@ -27,17 +26,27 @@ struct FPlayerLock
static FPlayerLock All();
};
/**
* Base class for player pawn.
* Synchronizes movement speed with fps, manages camera and inventory,
* prepares for work with interactables, and manages basic player input.
*/
UCLASS(Blueprintable, BlueprintType)
class APlayerBase : public ACharacter
{
GENERATED_BODY()
public:
/** setup input context, camera and inventory */
APlayerBase();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Player Base Pawn"))
static APlayerBase* Get();
UFUNCTION(BlueprintPure)
bool IsMoving();
UFUNCTION(BlueprintPure)
@ -48,38 +57,35 @@ public:
void LockPlayer(const FPlayerLock lock);
void UnlockPlayer(const FPlayerLock lock);
void FlyMode(bool on);
void ResetIntractions();
/** Force interactable activators reset */
void ResetInteractions();
/** Switch camera possesion to passed Actor */
UFUNCTION(BlueprintCallable, Category = Character)
void SwitchToView(class AActor* target);
/** Posess player camera */
UFUNCTION(BlueprintCallable, Category = Character)
void ReturnPlayerView();
/** Place item into first inventory */
UFUNCTION(BlueprintCallable, Category = Character)
void TakeItemToLeftHand(class AActor* actor);
/** Place item into second inventory */
UFUNCTION(BlueprintCallable, Category = Character)
void TakeItemToRightHand(class AActor* actor);
/** Check if Actor is in inventory */
UFUNCTION(BlueprintCallable, Category = Character)
bool IsInInventory(const class AActor* actor);
UPROPERTY(BlueprintAssignable)
FPlayerMovedDelegate OnPlayerMoved;
FVector moveVector;
FPlayerAnyKeyPressedDelegate onAnyKeyPressedDelegate;
FPlayerAnyKeyReleasedDelegate onAnyKeyReleasedDelegate;
class UEnhancedInputComponent* inputComponent = nullptr;
class AActor* leftPocketItem = nullptr;
class AActor* rightPocketItem = nullptr;
protected:
virtual void BeginPlay() override;
void OnAnyKeyPressed(FKey key);
void OnAnyKeyReleased(FKey key);
UFUNCTION(BlueprintCallable, Category = CameraMode)
void SwitchToCameraPawn();
@ -91,6 +97,7 @@ protected:
UFUNCTION(BlueprintCallable, Category = Character)
void SwitchRun(bool run);
/** Sets camera pitch range */
UFUNCTION(BlueprintCallable, Category = Character)
void UpdatePitch(float min, float max);
@ -153,4 +160,6 @@ private:
bool itemDropLocked = false;
FTimerHandle itemDropLockTimer;
TSoftObjectPtr<class UInputMappingContext> context;
};

View File

@ -1,29 +1,19 @@
// Oleg Petruny proprietary.
#include "QuestManager.h"
#include "Kismet/GameplayStatics.h"
#include "Levels/LevelBase.h"
#include "MainGameModeBase.h"
#include "Widgets/WidgetsManager.h"
void AQuestManager::BeginPlay()
{
if(auto gamemode_base = UGameplayStatics::GetGameMode(GetWorld()))
{
gamemode = Cast<AMainGameModeBase>(gamemode_base);
if(gamemode)
{
if(auto gamemode = AMainGameModeBase::Get())
gamemode->questsUpdateDelegate.AddDynamic(this, &AQuestManager::Update);
}
}
}
void AQuestManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if(gamemode)
if(auto gamemode = AMainGameModeBase::Get())
gamemode->questsUpdateDelegate.RemoveDynamic(this, &AQuestManager::Update);
}

View File

@ -6,6 +6,9 @@
#include "QuestManager.generated.h"
/**
* "Manages" quests. In fact takes string rows from a level and push them to Journal widget.
*/
UCLASS(Blueprintable, BlueprintType)
class AQuestManager : public AActor
{
@ -21,6 +24,4 @@ protected:
UFUNCTION()
void Update(TArray<FText> quests);
class AMainGameModeBase* gamemode = nullptr;
};

View File

@ -1,11 +1,11 @@
// Oleg Petruny proprietary.
#include "QuickTimeEvent.h"
#include "Kismet/GameplayStatics.h"
#include "CommonFunctions.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
@ -14,7 +14,7 @@ int32 Event::counter = 0;
Event::Event()
{
_id = counter++;
id = counter++;
}
Event::Event(EQuickTimeEventType type, FKey& key, FQuickTimeEventEndCallback& callback, bool sequence)
@ -28,14 +28,14 @@ Event::Event(EQuickTimeEventType type, FKey& key, FQuickTimeEventEndCallback& ca
inline int32 Event::GetId()
{
return _id;
return id;
}
void UQuickTimeEventManager::ShowQuickTimeEvent(FQuickTimeEventEnqueProperties properties)
{
UE_LOG(LogTemp, Log, TEXT("ShowQuickTimeEvent Start"));
OnFirstEventInit();
FScopeLock lock1(&_lock);
FScopeLock lock1(&lock);
CreateEvent(properties, false);
UE_LOG(LogTemp, Log, TEXT("ShowQuickTimeEvent End"));
}
@ -45,8 +45,8 @@ void UQuickTimeEventManager::EnqueQuickTimeEvent(FQuickTimeEventEnqueProperties
UE_LOG(LogTemp, Log, TEXT("EnqueQuickTimeEvent Start"));
OnFirstEventInit();
{
FScopeLock lock1(&_lock);
_nextEvents.Enqueue(properties);
FScopeLock lock1(&lock);
nextEvents.Enqueue(properties);
}
ShowNextEvent();
UE_LOG(LogTemp, Log, TEXT("EnqueQuickTimeEvent End"));
@ -55,9 +55,9 @@ void UQuickTimeEventManager::EnqueQuickTimeEvent(FQuickTimeEventEnqueProperties
void UQuickTimeEventManager::ShowNextEvent()
{
UE_LOG(LogTemp, Log, TEXT("ShowNextEvent Start"));
FScopeLock lock1(&_lock);
FScopeLock lock1(&lock);
FQuickTimeEventEnqueProperties properties;
_nextEvents.Dequeue(properties);
nextEvents.Dequeue(properties);
CreateEvent(properties, true);
UE_LOG(LogTemp, Log, TEXT("ShowNextEvent End"));
}
@ -65,9 +65,9 @@ void UQuickTimeEventManager::ShowNextEvent()
void UQuickTimeEventManager::OnEventEnd(int32 id, EQuickTimeEventResult result)
{
UE_LOG(LogTemp, Log, TEXT("OnEventEnd Start"));
FScopeLock lock1(&_lock);
FScopeLock lock1(&lock);
Event event;
if(!_events.RemoveAndCopyValue(id, event))
if(!events.RemoveAndCopyValue(id, event))
return;
GetWorld()->GetTimerManager().ClearTimer(event.timer);
@ -75,19 +75,19 @@ void UQuickTimeEventManager::OnEventEnd(int32 id, EQuickTimeEventResult result)
if(event.callback.IsBound())
event.callback.Execute(result);
if(event.sequence && !_nextEvents.IsEmpty())
if(event.sequence && !nextEvents.IsEmpty())
{
ShowNextEvent();
UE_LOG(LogTemp, Log, TEXT("OnEventEnd End"));
return;
}
if(_events.IsEmpty())
if(events.IsEmpty())
{
if(auto player = UCommonFunctions::GetPlayer(this))
if(auto PC = ACustomPlayerController::Get())
{
player->onAnyKeyPressedDelegate.RemoveDynamic(this, &UQuickTimeEventManager::OnInputPressed);
player->onAnyKeyReleasedDelegate.RemoveDynamic(this, &UQuickTimeEventManager::OnInputReleased);
PC->onAnyKeyPressed.RemoveDynamic(this, &UQuickTimeEventManager::OnInputPressed);
PC->onAnyKeyReleased.RemoveDynamic(this, &UQuickTimeEventManager::OnInputReleased);
}
UCommonFunctions::ExitSlowMotion();
}
@ -96,10 +96,11 @@ void UQuickTimeEventManager::OnEventEnd(int32 id, EQuickTimeEventResult result)
void UQuickTimeEventManager::OnInput(const FKey& key, bool released)
{
for(auto eventMapping : _events)
{
if(eventMapping.Value.key == key)
for(auto eventMapping : events)
{
if(eventMapping.Value.key != key)
continue;
EQuickTimeEventResult result = EQuickTimeEventResult::Success;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
@ -119,23 +120,22 @@ void UQuickTimeEventManager::OnInput(const FKey& key, bool released)
OnEventEnd(eventMapping.Key, result);
return;
}
}
if(released)
return;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
while(_events.Num() > 0)
while(events.Num() > 0)
{
WM->RemoveQuickTimeEvent(_events.begin()->Key);
OnEventEnd(_events.begin()->Key, EQuickTimeEventResult::FailedKey);
WM->RemoveQuickTimeEvent(events.begin()->Key);
OnEventEnd(events.begin()->Key, EQuickTimeEventResult::FailedKey);
}
}
else
{
while(_events.Num() > 0)
OnEventEnd(_events.begin()->Key, EQuickTimeEventResult::FailedKey);
while(events.Num() > 0)
OnEventEnd(events.begin()->Key, EQuickTimeEventResult::FailedKey);
}
}
@ -151,31 +151,31 @@ void UQuickTimeEventManager::OnInputReleased(FKey key)
void UQuickTimeEventManager::OnFirstEventInit()
{
if(_events.IsEmpty())
if(!events.IsEmpty())
return;
if(auto PC = ACustomPlayerController::Get())
{
if(auto player = UCommonFunctions::GetPlayer(this))
GetWorld()->GetTimerManager().SetTimerForNextTick([=, this]()
{
GetWorld()->GetTimerManager().SetTimerForNextTick([manager = this, player = player]()
{
player->onAnyKeyPressedDelegate.AddDynamic(manager, &UQuickTimeEventManager::OnInputPressed);
player->onAnyKeyReleasedDelegate.AddDynamic(manager, &UQuickTimeEventManager::OnInputReleased);
PC->onAnyKeyPressed.AddDynamic(this, &UQuickTimeEventManager::OnInputPressed);
PC->onAnyKeyReleased.AddDynamic(this, &UQuickTimeEventManager::OnInputReleased);
});
}
UCommonFunctions::EnterSlowMotion();
}
}
void UQuickTimeEventManager::CreateEvent(FQuickTimeEventEnqueProperties& properties, bool sequence)
{
Event event{ properties.type, properties.key, properties.callback, sequence };
GetWorld()->GetTimerManager().SetTimer(event.timer, [id = event.GetId(), manager = this]()
GetWorld()->GetTimerManager().SetTimer(event.timer, [id = event.GetId(), this]()
{
EQuickTimeEventResult result = EQuickTimeEventResult::FailedTime;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
result = WM->RemoveQuickTimeEvent(id);
manager->OnEventEnd(id, result);
this->OnEventEnd(id, result);
}, properties.duration, false);
_events.Add(event.GetId(), event);
events.Add(event.GetId(), event);
if(auto WM = AMainGameModeBase::GetWidgetsManager())
WM->ShowQuickTimeEvent(event.GetId(), properties);

View File

@ -60,7 +60,7 @@ public:
bool sequence;
private:
int32 _id;
int32 id;
static int32 counter;
};
@ -89,7 +89,7 @@ protected:
void OnFirstEventInit();
void CreateEvent(FQuickTimeEventEnqueProperties& properties, bool sequence);
TQueue<FQuickTimeEventEnqueProperties> _nextEvents;
TMap<int32, Event> _events;
FCriticalSection _lock;
TQueue<FQuickTimeEventEnqueProperties> nextEvents;
TMap<int32, Event> events;
FCriticalSection lock;
};

View File

@ -6,18 +6,24 @@
#include "SaveData.generated.h"
/**
* Custom game save object
*/
UCLASS(BlueprintType)
class USaveData : public USaveGame
{
GENERATED_BODY()
public:
/** Level to load */
UPROPERTY(VisibleAnywhere)
FName level;
/** State of the level */
UPROPERTY(VisibleAnywhere)
int32 state;
/** Checkpoint object name in level */
UPROPERTY(VisibleAnywhere)
FName checkpoint;

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "AutohideWidget.h"
#include "Animation/WidgetAnimation.h"

View File

@ -6,6 +6,9 @@
#include "AutohideWidget.generated.h"
/**
* Automatically hides itself after some period
*/
UCLASS()
class UAutohideWidget : public UResolutionResponsiveUserWidget
{

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "DialogueRowWidgetManager.h"
#include "Blueprint/WidgetTree.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "InteractableHintWidgetManager.h"
#include "Blueprint/WidgetTree.h"
@ -23,10 +22,10 @@ void UInteractableHintWidgetManager::Append(const UInteractableModificator* modi
return;
}
if(hintsMap.Contains(modificator) || !modificator->GetMappingContext())
if(hintsMap.Contains(modificator) || !modificator->GetMappingContext().IsValid())
return;
const auto& mappings = modificator->GetMappingContext()->GetMappings();
const auto& mappings = modificator->GetMappingContext().LoadSynchronous()->GetMappings();
for(int32 i = hints->GetChildrenCount() - count - mappings.Num(); i < 0; ++i)
{

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "JournalWidget.h"
#include "Blueprint/WidgetTree.h"

View File

@ -1,5 +1,4 @@
// Oleg Petruny proprietary.
#include "MainMenuButtonWidget.h"

View File

@ -1,11 +1,10 @@
// Oleg Petruny proprietary.
#include "MainMenuWidget.h"
#include "Animation/WidgetAnimation.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
#include "MainGameModeBase.h"
#include "MainMenuButtonWidget.h"
#include "Widgets/WidgetsManager.h"
@ -14,15 +13,13 @@ bool UMainMenuWidget::Initialize()
{
if(ButtonLoadLastSave)
{
if(auto GI = UCustomGameInstanceBase::GetGameInstance())
{
if(GI->saveData)
auto GI = UCustomGameInstance::Get();
if(GI && GI->saveData)
{
ButtonLoadLastSave->SetIsEnabled(true);
ButtonLoadLastSave->SetRenderOpacity(1.0f);
}
}
}
//FWidgetAnimationDynamicEvent closeFinished;
//closeFinished.BindDynamic(this, &UMainMenuWidget::Closed);

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "QuickTimeEventWidget.h"
#include "Animation/WidgetAnimation.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "QuickTimeEventWidgetManager.h"
#include "Animation/UMGSequencePlayer.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "ResolutionResponsiveUserWidget.h"
#include "Components/PanelSlot.h"

View File

@ -3,17 +3,18 @@
#pragma once
#include "Blueprint/UserWidget.h"
#include "CoreMinimal.h"
#include "ResolutionResponsiveUserWidget.generated.h"
/**
* Automatically scale itself for current resolution to keep UI sizes universal.
*/
UCLASS()
class UResolutionResponsiveUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
//UResolutionResponsiveUserWidget(const FObjectInitializer& ObjectInitializer);
virtual bool Initialize() override;
virtual void BeginDestroy() override;

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