From d39844fdfb1e371315c17ed620d7c321d7b78958 Mon Sep 17 00:00:00 2001
From: Oleg Petruny <oleg.petruny@gmail.com>
Date: Fri, 13 Dec 2024 00:20:57 +0100
Subject: [PATCH] CustomGameInstance, CustomPlayerController

---
 .../Blueprints/BP_CustomGameInstance.uasset   |   4 +-
 .../Lost_Edge/Private/CameraModeBase.cpp      |  20 +--
 .../Lost_Edge/Private/CommonFunctions.cpp     |   8 +-
 .../Lost_Edge/Private/CustomGameInstance.cpp  |  74 +++++++++
 ...ameInstanceBase.h => CustomGameInstance.h} |  26 ++-
 .../Private/CustomGameInstanceBase.cpp        | 148 ------------------
 .../Private/CustomPlayerController.cpp        |  90 +++++++++++
 .../Private/CustomPlayerController.h          |  49 ++++++
 .../Lost_Edge/Private/CutsceneManager.cpp     |  17 +-
 .../Lost_Edge/Private/DialogueManager.cpp     |  11 +-
 .../InCameraInteractableActivator.cpp         |   2 +-
 .../InCameraInteractableActivator.h           |   2 +
 .../Activators/InteractableActivator.cpp      |   8 +
 .../Activators/InteractableActivator.h        |   5 +
 .../Activators/RaycastInteractableActivator.h |   2 +
 .../Private/Interactable/Interactable.cpp     |  23 ++-
 .../Private/Interactable/Interactable.h       |   9 ++
 .../Modificators/InteractableModificator.cpp  |  13 +-
 .../Modificators/InteractableModificator.h    |   4 +-
 .../Lost_Edge/Private/Levels/Checkpoint.cpp   |   4 +-
 .../Lost_Edge/Private/Levels/LevelBase.cpp    |  16 +-
 .../Lost_Edge/Private/MainGameModeBase.cpp    |   2 +-
 .../Minigame/CrossyRoad/CrossyRoadManager.cpp |  17 +-
 .../Minigame/Fishing/FishingManager.cpp       |  15 +-
 .../Lost_Edge/Private/Minigame/Minigame.cpp   |   6 +-
 .../Lost_Edge/Private/Minigame/Minigame.h     |   2 +-
 .../Minigame/SubwaySurf/SubwaySurfManager.cpp |  17 +-
 .../Source/Lost_Edge/Private/PlayerBase.cpp   |  45 +-----
 .../Source/Lost_Edge/Private/PlayerBase.h     |  10 --
 .../Lost_Edge/Private/QuickTimeEvent.cpp      |  19 +--
 .../Widgets/InteractableHintWidgetManager.cpp |   4 +-
 .../Widgets/MainMenu/MainMenuWidget.cpp       |   4 +-
 .../Private/Widgets/WidgetsManager.cpp        |   2 +-
 33 files changed, 354 insertions(+), 324 deletions(-)
 create mode 100644 UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.cpp
 rename UnrealProject/Lost_Edge/Source/Lost_Edge/Private/{CustomGameInstanceBase.h => CustomGameInstance.h} (52%)
 delete mode 100644 UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.cpp
 create mode 100644 UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.cpp
 create mode 100644 UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.h

diff --git a/UnrealProject/Lost_Edge/Content/Blueprints/BP_CustomGameInstance.uasset b/UnrealProject/Lost_Edge/Content/Blueprints/BP_CustomGameInstance.uasset
index 67c869c..da2e0e6 100644
--- a/UnrealProject/Lost_Edge/Content/Blueprints/BP_CustomGameInstance.uasset
+++ b/UnrealProject/Lost_Edge/Content/Blueprints/BP_CustomGameInstance.uasset
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d9156bfa1fa7a0a42325c5cd1aad5f085d9da75143e46ce9919b54d49270bda2
-size 6180
+oid sha256:617aa7adcc34b939014b4d3e4e3442dacde726db2198f9943f13a9606a5dd12d
+size 6156
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CameraModeBase.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CameraModeBase.cpp
index 7410b09..29db8eb 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CameraModeBase.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CameraModeBase.cpp
@@ -10,8 +10,8 @@
 #include "InputMappingContext.h"
 #include "Kismet/GameplayStatics.h"
 
-#include "CustomGameInstanceBase.h"
 #include "CustomGameUserSettings.h"
+#include "CustomPlayerController.h"
 #include "MainGameModeBase.h"
 
 ACameraModeBase::ACameraModeBase()
@@ -25,30 +25,12 @@ void ACameraModeBase::BeginPlay()
 
     auto world = GetWorld();
 
-    //GetMovementComponent()->speed
-    //    GetCharacterMovement()->MaxWalkSpeed = moveSpeed;
-
     auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();
 
     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 = UCustomGameInstanceBase::GetGameInstance())
-            {
-                inputSubsystem->ClearAllMappings();
-                for(auto& inputContext : GI->inputContexts)
-                {
-                    inputSubsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
-                }
-            }
-        }
-    }
 }
 
 void ACameraModeBase::Tick(float DeltaTime)
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CommonFunctions.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CommonFunctions.cpp
index 3a5e4fa..e6dfbcc 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CommonFunctions.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CommonFunctions.cpp
@@ -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"
 
@@ -86,7 +86,7 @@ void UCommonFunctions::EnterSlowMotion(float duration)
 
     SlowMotion::targetDilation = SlowMotion::slowDilation;
 
-    auto GI = UCustomGameInstanceBase::GetGameInstance();
+    auto GI = UCustomGameInstance::GetGameInstance();
     if(!GI)
         return;
 
@@ -101,7 +101,7 @@ void UCommonFunctions::ExitSlowMotion(float duration)
 
     SlowMotion::targetDilation = SlowMotion::normalDilation;
 
-    auto GI = UCustomGameInstanceBase::GetGameInstance();
+    auto GI = UCustomGameInstance::GetGameInstance();
     if(!GI)
         return;
 
@@ -116,7 +116,7 @@ FWorldDilationChangedDelegate& UCommonFunctions::GetWorldDilationChangedDelegate
 
 void UCommonFunctions::SlowMotionTick()
 {
-    const UWorld* world = UCustomGameInstanceBase::GetGameInstance()->GetWorld();
+    const UWorld* world = UCustomGameInstance::GetGameInstance()->GetWorld();
     const float currentDilation = UGameplayStatics::GetGlobalTimeDilation(world);
     float newDilation = FMath::FInterpTo(currentDilation, SlowMotion::targetDilation, 1, SlowMotion::interpolationSpeed);
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.cpp
new file mode 100644
index 0000000..8e24701
--- /dev/null
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.cpp
@@ -0,0 +1,74 @@
+// Oleg Petruny proprietary.
+
+#include "CustomGameInstance.h"
+
+#include "Kismet/GameplayStatics.h"
+#include "Kismet/KismetSystemLibrary.h"
+
+#include "CommonFunctions.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::GetGameInstance()
+{
+    return instance;
+}
+
+UContentLoader* UCustomGameInstance::GetContentLoader()
+{
+    if(auto GI = GetGameInstance())
+        return GI->contentLoader;
+
+    return nullptr;
+}
+
+void UCustomGameInstance::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 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);
+}
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.h
similarity index 52%
rename from UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.h
rename to UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.h
index 6169ab4..ef0015b 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstance.h
@@ -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();
+    static UCustomGameInstance* GetGameInstance();
 
     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;
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.cpp
deleted file mode 100644
index b4da336..0000000
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomGameInstanceBase.cpp
+++ /dev/null
@@ -1,148 +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);
-}
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.cpp
new file mode 100644
index 0000000..dcf8e09
--- /dev/null
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.cpp
@@ -0,0 +1,90 @@
+// Oleg Petruny proprietary.
+
+#include "CustomPlayerController.h"
+
+#include "EnhancedInputComponent.h"
+#include "EnhancedInputLibrary.h"
+#include "EnhancedInputSubsystems.h"
+#include "InputMappingContext.h"
+
+#include "CustomGameUserSettings.h"
+
+ACustomPlayerController* ACustomPlayerController::instance = nullptr;
+UEnhancedInputLocalPlayerSubsystem* ACustomPlayerController::subsystem = nullptr;
+UEnhancedInputComponent* ACustomPlayerController::input = nullptr;
+TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contexts = {};
+
+void ACustomPlayerController::AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context)
+{
+    if(!context.IsValid())
+        return;
+
+    ApplyMouseSettings(context);
+    contexts.Add(context);
+    if(subsystem)
+        subsystem->AddMappingContext(context.LoadSynchronous(), 0);
+}
+
+void ACustomPlayerController::BeginPlay()
+{
+    instance = this;
+
+    subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(instance->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::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context)
+{
+    auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();
+    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);
+}
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.h
new file mode 100644
index 0000000..8e243e2
--- /dev/null
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CustomPlayerController.h
@@ -0,0 +1,49 @@
+// 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 = "GetCustomPlayerController"))
+    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;
+
+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;
+};
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CutsceneManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CutsceneManager.cpp
index ab9817b..7671c50 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CutsceneManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/CutsceneManager.cpp
@@ -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"
@@ -20,10 +20,7 @@ UCutsceneManager::UCutsceneManager()
 {
     static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") };
     _inputContext = asset.Object;
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
-    {
-        GI->inputContexts.Add(_inputContext);
-    }
+    ACustomPlayerController::AppendInputContext(_inputContext);
 }
 
 void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCallback endCallback)
@@ -44,8 +41,9 @@ void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCal
                 _lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
 
                 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();
+                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())
@@ -148,8 +146,9 @@ void UCutsceneManager::OnSequenceEnd()
     {
         _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;
     }
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/DialogueManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/DialogueManager.cpp
index a9c3a44..7fa331f 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/DialogueManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/DialogueManager.cpp
@@ -11,7 +11,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"
@@ -22,10 +22,7 @@ UDialogueManager::UDialogueManager()
 {
     static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Dialogue.IMC_Dialogue'") };
     _inputContext = asset.Object;
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
-    {
-        GI->inputContexts.Add(_inputContext);
-    }
+    ACustomPlayerController::AppendInputContext(_inputContext);
 }
 
 void UDialogueManager::PlayDialogue(FDialogueEnqueProperties properties, FDialogueEndCallback endCallback)
@@ -96,7 +93,7 @@ void UDialogueManager::EnqueDialogue(FDialogueEnqueProperties properties, FDialo
             if(_lastPlayer)
             {
                 auto& mapping = _inputContext.LoadSynchronous()->GetMapping(0);
-                _inputHandler = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
+                _inputHandler = ACustomPlayerController::GetInput()->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
 
                 if(auto WM = AMainGameModeBase::GetWidgetsManager())
                 {
@@ -258,7 +255,7 @@ void UDialogueManager::OnDialogueEnd()
     _dialoguesLock.Lock();
     if(_endCallbacks.IsEmpty() && _lastPlayer)
     {
-        _lastPlayer->inputComponent->RemoveBindingByHandle(_inputHandler);
+        ACustomPlayerController::GetInput()->RemoveBindingByHandle(_inputHandler);
         _lastPlayer = nullptr;
     }
     _dialoguesLock.Unlock();
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.cpp
index bbb0df2..06b696c 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.cpp
@@ -30,7 +30,7 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
 
 void UInCameraInteractableActivator::OnRegister()
 {
-    UInteractableActivator::OnRegister();
+    Super::OnRegister();
     capturer->RegisterComponent();
     capturer->Activate();
 }
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.h
index 1869e2d..4bc59ba 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InCameraInteractableActivator.h
@@ -20,6 +20,8 @@ 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.
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.cpp
index 08f08a6..339db5d 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.cpp
@@ -4,8 +4,16 @@
 
 #include "Engine/CollisionProfile.h"
 
+#include "Interactable/Interactable.h"
 #include "PlayerBase.h"
 
+void UInteractableActivator::OnRegister()
+{
+    Super::OnRegister();
+
+    AInteractable::AppendInteractableActivatorClass(GetClass());
+}
+
 UInteractableActivator::UInteractableActivator(const FObjectInitializer& ObjectInitializer)
     : USceneComponent(ObjectInitializer)
 {
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.h
index a56d4b3..21ed926 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/InteractableActivator.h
@@ -20,9 +20,14 @@ class UInteractableActivator : public USceneComponent
     GENERATED_BODY()
 
 public:
+    /** Append itself to CustomGameInstance modificators registry */
+    virtual void OnRegister() override;
+
     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();
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/RaycastInteractableActivator.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/RaycastInteractableActivator.h
index 5414678..7eb128b 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/RaycastInteractableActivator.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Activators/RaycastInteractableActivator.h
@@ -17,6 +17,8 @@ class URaycastInteractableActivator : public UInteractableActivator
 public:
     URaycastInteractableActivator(const FObjectInitializer& ObjectInitializer);
 
+    virtual bool AutoInstantiateInPlayer() override { return true; }
+
     virtual void Rescan_Implementation() override;
 
 protected:
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.cpp
index 7109f63..864a66b 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.cpp
@@ -4,11 +4,32 @@
 
 #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::AppendInteractableActivatorClass(TSubclassOf<class UInteractableActivator> activator)
+{
+    interactionActivators.Add(activator);
+}
+
+void AInteractable::AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
+{
+    bool alreadyPresent = false;
+    interactionModificators.Add(modificator, &alreadyPresent);
+    if(alreadyPresent)
+        return;
+
+    auto IC = modificator.GetDefaultObject()->GetMappingContext();
+    ACustomPlayerController::AppendInputContext(IC);
+}
+
 int32 AInteractable::GetActivatedFlags()
 {
     return activated;
@@ -112,7 +133,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());
             }
         }
     }
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.h
index 66676c8..9138a7c 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Interactable.h
@@ -40,6 +40,15 @@ class AInteractable : public AActor
     GENERATED_BODY()
 
 public:
+    static void AppendInteractableActivatorClass(TSubclassOf<class UInteractableActivator> activator);
+    static void AppendInteractableModificatorClass(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();
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.cpp
index 43d477a..2670353 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.cpp
@@ -2,24 +2,19 @@
 
 #include "InteractableModificator.h"
 
-#include "InputMappingContext.h"
-
-#include "CustomGameInstanceBase.h"
+#include "Interactable/Interactable.h"
 #include "Widgets/InteractableHintWidget.h"
 
 void UInteractableModificator::OnRegister()
 {
     UActorComponent::OnRegister();
 
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
-    {
-        GI->AppendInteractableModificatorClass(this->GetClass());
-    }
+    AInteractable::AppendInteractableModificatorClass(GetClass());
 }
 
-const UInputMappingContext* UInteractableModificator::GetMappingContext() const
+const TSoftObjectPtr<class UInputMappingContext> UInteractableModificator::GetMappingContext() const
 {
-    return inputMapping.LoadSynchronous();
+    return inputMapping;
 }
 
 EActivatorType UInteractableModificator::GetActivatorTypes() const
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.h
index 770b382..a765b4c 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Interactable/Modificators/InteractableModificator.h
@@ -18,11 +18,11 @@ class UInteractableModificator : public UActorComponent
 
 public:
     /** Append itself to CustomGameInstance modificators registry */
-    void OnRegister() override;
+    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)
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/Checkpoint.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/Checkpoint.cpp
index b9a2beb..81789e3 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/Checkpoint.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/Checkpoint.cpp
@@ -2,10 +2,10 @@
 
 #include "Checkpoint.h"
 
-#include "CustomGameInstanceBase.h"
+#include "CustomGameInstance.h"
 
 void ACheckpoint::SaveGame()
 {
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
+    if(auto GI = UCustomGameInstance::GetGameInstance())
         GI->SaveGame(GetFName());
 }
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/LevelBase.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/LevelBase.cpp
index 6cf735f..15c7cd7 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/LevelBase.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Levels/LevelBase.cpp
@@ -10,7 +10,8 @@
 #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"
@@ -22,13 +23,8 @@ void ALevelBase::BeginPlay()
 {
     AMainGameModeBase::leadLevel = TStrongObjectPtr<ALevelBase>{ this };
 
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
-    {
-        for(TActorIterator<AMinigame> it(GetWorld()); it; ++it)
-        {
-            GI->inputContexts.Add(it->GetInputMappings());
-        }
-    }
+    for(TActorIterator<AMinigame> it(GetWorld()); it; ++it)
+        ACustomPlayerController::AppendInputContext(it->GetInputMappings());
 
     ALevelScriptActor::BeginPlay();
 
@@ -58,7 +54,7 @@ void ALevelBase::IterateToState(int32 to)
 
 void ALevelBase::BroadcastNewLevelBeginPlay()
 {
-    if(auto GI = UCustomGameInstanceBase::GetGameInstance())
+    if(auto GI = UCustomGameInstance::GetGameInstance())
         GI->OnLevelBeginned.Broadcast(GetFName());
 }
 
@@ -79,7 +75,7 @@ void ALevelBase::StartLevelAnimations()
 
 void ALevelBase::ApplySaveData()
 {
-    auto GI = UCustomGameInstanceBase::GetGameInstance();
+    auto GI = UCustomGameInstance::GetGameInstance();
     if(!GI || !GI->saveData || GI->saveData->level != GetWorld()->GetFName())
         return;
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/MainGameModeBase.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/MainGameModeBase.cpp
index 5517836..081f64a 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/MainGameModeBase.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/MainGameModeBase.cpp
@@ -8,7 +8,7 @@
 #include "Kismet/GameplayStatics.h"
 #include "UObject/ScriptInterface.h"
 
-#include "CustomGameInstanceBase.h"
+#include "CustomGameInstance.h"
 #include "CutsceneManager.h"
 #include "DialogueManager.h"
 #include "Levels/LevelBase.h"
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/CrossyRoad/CrossyRoadManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/CrossyRoad/CrossyRoadManager.cpp
index c7794e6..b89df9b 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/CrossyRoad/CrossyRoadManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/CrossyRoad/CrossyRoadManager.cpp
@@ -10,7 +10,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 +19,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 +43,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 +71,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();
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Fishing/FishingManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Fishing/FishingManager.cpp
index b5f5a69..ac52d4a 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Fishing/FishingManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Fishing/FishingManager.cpp
@@ -6,13 +6,14 @@
 #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 +25,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());
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.cpp
index eb49fc5..e8aee09 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.cpp
@@ -27,7 +27,7 @@ void AMinigame::Restart()
 
 UInputMappingContext* AMinigame::GetInputMappings()
 {
-    return input.LoadSynchronous();
+    return context.LoadSynchronous();
 }
 
 void AMinigame::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
@@ -35,10 +35,10 @@ void AMinigame::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
     player = playerPawn;
     callback = delegate;
 
-    if(input)
+    if(context)
         if(auto PC = Cast<APlayerController>(playerPawn->GetController()))
             if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
-                inputSubsystem->AddMappingContext(input.LoadSynchronous(), 0);
+                inputSubsystem->AddMappingContext(context.LoadSynchronous(), 0);
 
     OnStart();
 }
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.h
index e103c50..5c940c8 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/Minigame.h
@@ -55,6 +55,6 @@ protected:
     bool ended = false;
 
     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
-    TSoftObjectPtr<class UInputMappingContext> input = nullptr;
+    TSoftObjectPtr<class UInputMappingContext> context = nullptr;
 };
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp
index 9b0746a..f89c289 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp
@@ -9,7 +9,7 @@
 #include "EnhancedInputComponent.h"
 #include "InputMappingContext.h"
 
-#include "CustomGameInstanceBase.h"
+#include "CustomPlayerController.h"
 #include "MainGameModeBase.h"
 #include "PlayerBase.h"
 #include "SubwaySurfObstacle.h"
@@ -19,7 +19,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 +43,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 +67,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();
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.cpp
index 375ad49..df11dbc 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.cpp
@@ -4,16 +4,14 @@
 #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 "CustomPlayerController.h"
 #include "Interactable/Activators/InteractableActivator.h"
 #include "Interactable/Interactable.h"
 #include "Interactable/Modificators/InteractableModificator.h"
@@ -84,44 +82,9 @@ void APlayerBase::BeginPlay()
         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 = UCustomGameInstanceBase::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;
@@ -257,12 +220,8 @@ void APlayerBase::UpdatePitch(float min, float max)
 
 void APlayerBase::LoadInteractablesActivators()
 {
-    auto GI = UCustomGameInstanceBase::GetGameInstance();
-    if(!GI)
-        return;
-
     TSet<UClass*> instancedActivators;
-    for(auto& act : GI->interactionsActivators)
+    for(auto& act : AInteractable::interactionActivators)
     {
         if(instancedActivators.Contains(act))
             continue;
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.h b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.h
index 5fd59dd..9e68195 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.h
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/PlayerBase.h
@@ -8,8 +8,6 @@
 
 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);
 
 USTRUCT(BlueprintType)
@@ -66,20 +64,12 @@ public:
     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();
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/QuickTimeEvent.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/QuickTimeEvent.cpp
index 333f9d3..b0d6b45 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/QuickTimeEvent.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/QuickTimeEvent.cpp
@@ -6,6 +6,7 @@
 #include "Kismet/GameplayStatics.h"
 
 #include "CommonFunctions.h"
+#include "CustomPlayerController.h"
 #include "MainGameModeBase.h"
 #include "PlayerBase.h"
 #include "Widgets/WidgetsManager.h"
@@ -84,10 +85,10 @@ void UQuickTimeEventManager::OnEventEnd(int32 id, EQuickTimeEventResult result)
 
     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();
     }
@@ -153,12 +154,12 @@ void UQuickTimeEventManager::OnFirstEventInit()
 {
     if(_events.IsEmpty())
     {
-        if(auto player = UCommonFunctions::GetPlayer(this))
+        if(auto PC = ACustomPlayerController::Get())
         {
-            GetWorld()->GetTimerManager().SetTimerForNextTick([manager = this, player = player]()
+            GetWorld()->GetTimerManager().SetTimerForNextTick([=, this]()
                 {
-                    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();
@@ -168,12 +169,12 @@ void UQuickTimeEventManager::OnFirstEventInit()
 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);
 
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/InteractableHintWidgetManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/InteractableHintWidgetManager.cpp
index 0e3a7ce..2a468ca 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/InteractableHintWidgetManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/InteractableHintWidgetManager.cpp
@@ -23,10 +23,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)
     {
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/MainMenu/MainMenuWidget.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/MainMenu/MainMenuWidget.cpp
index 47e1a66..70e211a 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/MainMenu/MainMenuWidget.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/MainMenu/MainMenuWidget.cpp
@@ -5,7 +5,7 @@
 
 #include "Animation/WidgetAnimation.h"
 
-#include "CustomGameInstanceBase.h"
+#include "CustomGameInstance.h"
 #include "MainGameModeBase.h"
 #include "MainMenuButtonWidget.h"
 #include "Widgets/WidgetsManager.h"
@@ -14,7 +14,7 @@ bool UMainMenuWidget::Initialize()
 {
     if(ButtonLoadLastSave)
     {
-        auto GI = UCustomGameInstanceBase::GetGameInstance();
+        auto GI = UCustomGameInstance::GetGameInstance();
         if(GI && GI->saveData)
         {
             ButtonLoadLastSave->SetIsEnabled(true);
diff --git a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/WidgetsManager.cpp b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/WidgetsManager.cpp
index 9921317..3c5b6e9 100644
--- a/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/WidgetsManager.cpp
+++ b/UnrealProject/Lost_Edge/Source/Lost_Edge/Private/Widgets/WidgetsManager.cpp
@@ -10,7 +10,7 @@
 #include "Kismet/GameplayStatics.h"
 #include "UObject/ScriptInterface.h"
 
-#include "CustomGameInstanceBase.h"
+#include "CustomGameInstance.h"
 #include "Interactable/Interactable.h"
 #include "Interactable/Modificators/InteractableModificator.h"
 #include "Interactable/Modificators/InventoryInteractableModificator.h"