diff --git a/Content/Input/Minigame/Actions/IA_SubwaySurf_Down.uasset b/Content/Input/Minigame/Actions/IA_SubwaySurf_Down.uasset new file mode 100644 index 0000000..1fd7f59 Binary files /dev/null and b/Content/Input/Minigame/Actions/IA_SubwaySurf_Down.uasset differ diff --git a/Content/Input/Minigame/Actions/IA_SubwaySurf_Left.uasset b/Content/Input/Minigame/Actions/IA_SubwaySurf_Left.uasset new file mode 100644 index 0000000..88c042e Binary files /dev/null and b/Content/Input/Minigame/Actions/IA_SubwaySurf_Left.uasset differ diff --git a/Content/Input/Minigame/Actions/IA_SubwaySurf_Right.uasset b/Content/Input/Minigame/Actions/IA_SubwaySurf_Right.uasset new file mode 100644 index 0000000..5ebea38 Binary files /dev/null and b/Content/Input/Minigame/Actions/IA_SubwaySurf_Right.uasset differ diff --git a/Content/Input/Minigame/Actions/IA_SubwaySurf_Up.uasset b/Content/Input/Minigame/Actions/IA_SubwaySurf_Up.uasset new file mode 100644 index 0000000..2cbd7b0 Binary files /dev/null and b/Content/Input/Minigame/Actions/IA_SubwaySurf_Up.uasset differ diff --git a/Content/Input/Minigame/IMC_SubwaySurf.uasset b/Content/Input/Minigame/IMC_SubwaySurf.uasset new file mode 100644 index 0000000..a438b70 Binary files /dev/null and b/Content/Input/Minigame/IMC_SubwaySurf.uasset differ diff --git a/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_Manager.uasset b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_Manager.uasset new file mode 100644 index 0000000..467d2bb Binary files /dev/null and b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_Manager.uasset differ diff --git a/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleBig.uasset b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleBig.uasset new file mode 100644 index 0000000..b71d9b6 Binary files /dev/null and b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleBig.uasset differ diff --git a/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleHigh.uasset b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleHigh.uasset new file mode 100644 index 0000000..c05698f Binary files /dev/null and b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleHigh.uasset differ diff --git a/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleLow.uasset b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleLow.uasset new file mode 100644 index 0000000..b2af251 Binary files /dev/null and b/Content/Levels/Level_2/Blueprints/Minigames/SubwaySurf/BP_SubwaySurf_ObstacleLow.uasset differ diff --git a/Content/Levels/Test/L_Test.umap b/Content/Levels/Test/L_Test.umap index 9d2f8c6..88b8bd1 100644 Binary files a/Content/Levels/Test/L_Test.umap and b/Content/Levels/Test/L_Test.umap differ diff --git a/Source/Lost_Edge/Private/CommonFunctions.cpp b/Source/Lost_Edge/Private/CommonFunctions.cpp index 7199156..7309572 100644 --- a/Source/Lost_Edge/Private/CommonFunctions.cpp +++ b/Source/Lost_Edge/Private/CommonFunctions.cpp @@ -38,6 +38,8 @@ void UCommonFunctions::DestroyActorRecursively(AActor* actor) actor->Destroy(); } + + ALevelBase* UCommonFunctions::GetCurrentLevelScript(UObject* obj) { if(auto world = obj->GetWorld()) diff --git a/Source/Lost_Edge/Private/CommonFunctions.h b/Source/Lost_Edge/Private/CommonFunctions.h index 94d9a4e..bfb0136 100644 --- a/Source/Lost_Edge/Private/CommonFunctions.h +++ b/Source/Lost_Edge/Private/CommonFunctions.h @@ -18,9 +18,12 @@ public: UFUNCTION(BlueprintCallable, Category = Actor) static void DestroyActorRecursively(class AActor* actor); + + UFUNCTION(BlueprintCallable, Category = Level) static class ALevelBase* GetCurrentLevelScript(class UObject* obj); UFUNCTION(BlueprintCallable, Category = Player) static class APlayerBase* GetPlayer(class UObject* obj); + }; diff --git a/Source/Lost_Edge/Private/DialogueManager.cpp b/Source/Lost_Edge/Private/DialogueManager.cpp index ec089a0..b04d56e 100644 --- a/Source/Lost_Edge/Private/DialogueManager.cpp +++ b/Source/Lost_Edge/Private/DialogueManager.cpp @@ -220,6 +220,16 @@ void UDialogueManager::LockCallback(bool lock) _lockCallback = lock; } +void UDialogueManager::BeginDestroy() +{ + _timersLock.Lock(); + for(auto& timer : _timers) + GetWorld()->GetTimerManager().ClearTimer(timer); + _timersLock.Unlock(); + + UObject::BeginDestroy(); +} + void UDialogueManager::OnDialogueEnd() { _dialoguesLock.Lock(); diff --git a/Source/Lost_Edge/Private/DialogueManager.h b/Source/Lost_Edge/Private/DialogueManager.h index 4b60ce2..bf5b05a 100644 --- a/Source/Lost_Edge/Private/DialogueManager.h +++ b/Source/Lost_Edge/Private/DialogueManager.h @@ -84,6 +84,9 @@ public: UFUNCTION(BlueprintCallable) void LockCallback(bool lock); +protected: + virtual void BeginDestroy() override; + private: void PlayNextDialogue(); UFUNCTION() diff --git a/Source/Lost_Edge/Private/Minigame/Minigame.cpp b/Source/Lost_Edge/Private/Minigame/Minigame.cpp new file mode 100644 index 0000000..daee15f --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/Minigame.cpp @@ -0,0 +1,18 @@ +// Oleg Petruny proprietary. + + +#include "Minigame.h" + +AMinigame::AMinigame() +{ + //PrimaryActorTick.bCanEverTick = true; +} + +void AMinigame::End() +{} + +void AMinigame::Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) +{ + player = playerPawn; + callback = delegate; +} diff --git a/Source/Lost_Edge/Private/Minigame/Minigame.h b/Source/Lost_Edge/Private/Minigame/Minigame.h new file mode 100644 index 0000000..3ca004c --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/Minigame.h @@ -0,0 +1,45 @@ +// Oleg Petruny proprietary. + +#pragma once + +#include "GameFramework/Actor.h" + +#include "Minigame.generated.h" + +UENUM(BlueprintType) +enum class EMinigameResult : uint8 +{ + Loss, + Win, +}; + +DECLARE_DYNAMIC_DELEGATE_TwoParams(FMinigameEndCallback, EMinigameResult, result, int32, score); + +UCLASS(Blueprintable, BlueprintType, MinimalAPI, Abstract) +class AMinigame : public AActor +{ + GENERATED_BODY() + +public: + AMinigame(); + + UFUNCTION(BlueprintCallable) + virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate); + + UFUNCTION(BlueprintCallable) + virtual void End(); + + UFUNCTION(BlueprintPure) + inline class UInputMappingContext* GetInputMappings() { return input.LoadSynchronous(); } + +protected: + FMinigameEndCallback callback; + class APlayerBase* player; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 score = 0; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + TSoftObjectPtr input = nullptr; +}; + diff --git a/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp new file mode 100644 index 0000000..ec7b322 --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.cpp @@ -0,0 +1,186 @@ +// Oleg Petruny proprietary. + + +#include "SubwaySurfManager.h" + +#include "Components/SplineComponent.h" +#include "EnhancedInputComponent.h" +#include "InputMappingContext.h" + +#include "PlayerBase.h" +#include "SubwaySurfObstacle.h" + +ASubwaySurfManager::ASubwaySurfManager() +{ + input = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_SubwaySurf.IMC_SubwaySurf'") } }; +} + +void ASubwaySurfManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate) +{ + AMinigame::Start(playerPawn, delegate); + + player->LockPlayer(FPlayerLock::All()); + + for(auto& mapping : input.LoadSynchronous()->GetMappings()) + { + if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Up")))) + inputHandlers.Add(player->inputComponent->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()); + else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Left")))) + inputHandlers.Add(player->inputComponent->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()); + } + + playerPos.SetRelativeLocation(lines[0]->GetLocationAtTime(lines[0]->Duration * 0.2f, ESplineCoordinateSpace::Local)); + player->SetActorLocation(playerPos.GetComponentLocation()); + player->Controller->SetControlRotation(GetActorRotation()); + + PrimaryActorTick.bCanEverTick = true; +} + +void ASubwaySurfManager::End() +{ + PrimaryActorTick.bCanEverTick = false; + + for(int32 handler : inputHandlers) + player->inputComponent->RemoveActionBindingForHandle(handler); + + player->UnlockPlayer(FPlayerLock::All()); + + AMinigame::End(); +} + +void ASubwaySurfManager::Hit() +{ + if(score > 1000) + { + Finish(); + return; + } + + if(callback.IsBound()) + callback.Execute(EMinigameResult::Loss, score); + End(); +} + +void ASubwaySurfManager::Finish() +{ + if(callback.IsBound()) + callback.Execute(EMinigameResult::Win, score); + End(); +} + +void ASubwaySurfManager::PostInitializeComponents() +{ + AMinigame::PostInitializeComponents(); + + GetComponents(lines); +} + +void ASubwaySurfManager::Tick(float deltaTime) +{ + AMinigame::Tick(deltaTime); + + time += deltaTime; + + if(time >= spawnDelay / speed) + { + time = 0; // (time -= spawnDelay) is not good on lags + CreateObstacle(); + } + + for(int32 i = 0; i < obstacles.Num(); ++i) + { + if(obstacles[i].time > lines[obstacles[i].lineId]->Duration) + { + obstacles[i].actor->Destroy(); + obstacles.RemoveAt(i); + //CreateObstacle(); + speed += 0.1; + score += 100 * speed; + } + else + { + obstacles[i].time += deltaTime * speed; + obstacles[i].actor->SetActorLocation(lines[obstacles[i].lineId]->GetWorldLocationAtTime(obstacles[i].time)); + } + } + + player->SetActorLocation(FMath::VInterpTo(player->GetActorLocation(), playerPos.GetComponentLocation(), deltaTime, 1)); +} + +void ASubwaySurfManager::CreateObstacle(int32 index) +{ + auto obstacleClass = obstacleClasses.CreateIterator(); + for(int32 i = 0; i < FMath::RandRange(0, obstacleClasses.Num() - 1); ++i) // std::advance not works :( + ++obstacleClass; + + int32 lineId; + do + { + lineId = FMath::RandRange(0, lines.Num() - 1); + } while(lineId == lastSpawnedLine); + auto spline = lines[lineId]; + + FActorSpawnParameters spawnParams{}; + spawnParams.Owner = this; + + lastSpawnedLine = lineId; + auto obstacle = GetWorld()->SpawnActor(*obstacleClass, spline->GetComponentLocation(), GetActorRotation(), spawnParams); + obstacle->subwaySurf = this; + GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, FString::Printf(TEXT("v: %f, %f, %f"), obstacle->GetActorLocation().X, obstacle->GetActorLocation().Y, obstacle->GetActorLocation().Z)); + + if(index < 0) + { + obstacles.Add({ .actor = obstacle, .lineId = lineId, .time = 0 }); + } + else + { + obstacles[index].actor->Destroy(); + obstacles[index].actor = obstacle; + obstacles[index].lineId = lineId; + obstacles[index].time = 0; + } +} + +void ASubwaySurfManager::Up() +{ + if(playerUp) + return; + + playerUp = true; + + playerPos.AddLocalOffset(FVector::UpVector * 300); +} + +void ASubwaySurfManager::Down() +{ + if(!playerUp) + return; + + playerUp = false; + + playerPos.AddLocalOffset(FVector::DownVector * 300); +} + +void ASubwaySurfManager::Left() +{ + if(playerLine == 0) + return; + + --playerLine; + + playerPos.AddLocalOffset(FVector::LeftVector * 300); +} + +void ASubwaySurfManager::Right() +{ + if(playerLine == lines.Num() - 1) + return; + + ++playerLine; + + playerPos.AddLocalOffset(FVector::RightVector * 300); +} diff --git a/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.h b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.h new file mode 100644 index 0000000..05ce96b --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfManager.h @@ -0,0 +1,58 @@ +// Oleg Petruny proprietary. + +#pragma once + +#include "Minigame/Minigame.h" + +#include "SubwaySurfManager.generated.h" + +struct ObstacleData +{ + class ASubwaySurfObstacle* actor; + int32 lineId; + float time; +}; + +UCLASS(Blueprintable, BlueprintType, MinimalAPI) +class ASubwaySurfManager : public AMinigame +{ + GENERATED_BODY() + +public: + ASubwaySurfManager(); + + virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override; + virtual void End() override; + + void Hit(); + void Finish(); + +protected: + virtual void PostInitializeComponents() override; + virtual void Tick(float deltaTime) override; + + void CreateObstacle(int32 index = -1); + + void Up(); + void Down(); + void Left(); + void Right(); + + UPROPERTY(EditDefaultsOnly) + TSet> obstacleClasses; + + UPROPERTY(EditDefaultsOnly) + float speed = 2; + + UPROPERTY(EditDefaultsOnly) + float spawnDelay = 3; + float time = spawnDelay; + + TArray lines; + TArray obstacles; + int32 lastSpawnedLine = -1; + TArray inputHandlers; + class USceneComponent playerPos; + bool playerUp = false; + int32 playerLine = 0; +}; diff --git a/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.cpp b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.cpp new file mode 100644 index 0000000..5d0c284 --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.cpp @@ -0,0 +1,17 @@ +// Oleg Petruny proprietary. + + +#include "SubwaySurfObstacle.h" + +#include "SubwaySurfManager.h" + +void ASubwaySurfObstacle::Hit() +{ + if(!subwaySurf) + { + Destroy(); + return; + } + + subwaySurf->Hit(); +} diff --git a/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.h b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.h new file mode 100644 index 0000000..a9f5383 --- /dev/null +++ b/Source/Lost_Edge/Private/Minigame/SubwaySurf/SubwaySurfObstacle.h @@ -0,0 +1,21 @@ +// Oleg Petruny proprietary. + +#pragma once + +#include "GameFramework/Actor.h" + +#include "SubwaySurfObstacle.generated.h" + +UCLASS(Blueprintable, BlueprintType, MinimalAPI, Abstract) +class ASubwaySurfObstacle : public AActor +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable) + void Hit(); + + class ASubwaySurfManager* subwaySurf = nullptr; + +}; + diff --git a/Source/Lost_Edge/Private/PlayerBase.cpp b/Source/Lost_Edge/Private/PlayerBase.cpp index 6aa0db5..720f849 100644 --- a/Source/Lost_Edge/Private/PlayerBase.cpp +++ b/Source/Lost_Edge/Private/PlayerBase.cpp @@ -336,3 +336,10 @@ void APlayerBase::ShowJournal() if(auto WM = AMainGameModeBase::GetWidgetsManager()) WM->ShowJournal(); } + +FPlayerLock FPlayerLock::All() +{ + FPlayerLock lock; + std::memset(&lock, 1, sizeof(FPlayerLock)); + return std::move(lock); +} diff --git a/Source/Lost_Edge/Private/PlayerBase.h b/Source/Lost_Edge/Private/PlayerBase.h index 013dc00..4c93788 100644 --- a/Source/Lost_Edge/Private/PlayerBase.h +++ b/Source/Lost_Edge/Private/PlayerBase.h @@ -21,6 +21,8 @@ struct FPlayerLock uint8 interaction : 1; uint8 camera : 1; uint8 inventory : 1; + + static FPlayerLock All(); }; UCLASS(Blueprintable, BlueprintType)