// Oleg Petruny proprietary. #include "SubwaySurfManager.h" #include "Camera/CameraComponent.h" #include "Components/SkeletalMeshComponent.h" #include "Components/SplineComponent.h" #include "EnhancedInputComponent.h" #include "InputMappingContext.h" #include "CustomGameInstanceBase.h" #include "MainGameModeBase.h" #include "PlayerBase.h" #include "SubwaySurfObstacle.h" #include "Widgets/WidgetsManager.h" ASubwaySurfManager::ASubwaySurfManager() : AMinigame() { input = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_SubwaySurf.IMC_SubwaySurf'") } }; auto root = CreateDefaultSubobject(TEXT("Scene")); playerPos = CreateDefaultSubobject(TEXT("PlayerPos")); playerPos->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform); camera = CreateDefaultSubobject(TEXT("Camera")); camera->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform); mannequin = CreateDefaultSubobject(TEXT("Mannequin")); mannequin->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform); } void ASubwaySurfManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate) { if(!playerPawn) return; AMinigame::Start(playerPawn, delegate); AMainGameModeBase::GetWidgetsManager()->HideInteractionHints(nullptr); player->LockPlayer(FPlayerLock::All()); player->SwitchToView(this); 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[1]->GetLocationAtTime(lines[1]->Duration * 0.01f, ESplineCoordinateSpace::Local)); mannequin->SetWorldLocationAndRotation(playerPos->GetComponentLocation(), playerPos->GetComponentRotation()); PrimaryActorTick.SetTickFunctionEnable(true); } void ASubwaySurfManager::End() { PrimaryActorTick.SetTickFunctionEnable(false); for(int32 handler : inputHandlers) player->inputComponent->RemoveActionBindingForHandle(handler); AMainGameModeBase::GetWidgetsManager()->ShowInteractionHints(nullptr); player->UnlockPlayer(FPlayerLock::All()); player->ReturnPlayerView(); AMinigame::End(); } void ASubwaySurfManager::Hit() { if(ended) return; ended = true; 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.15; score += 100 * speed; } else { obstacles[i].time += deltaTime * speed; obstacles[i].actor->SetActorLocation(lines[obstacles[i].lineId]->GetWorldLocationAtTime(obstacles[i].time)); } } FVector targetpos = playerPos->GetComponentLocation(); FVector actualpos = mannequin->GetComponentLocation(); FVector newpos{ FMath::FInterpTo(actualpos.X, targetpos.X, deltaTime, 20), FMath::FInterpTo(actualpos.Y, targetpos.Y, deltaTime, 20), FMath::FInterpTo(actualpos.Z, targetpos.Z, deltaTime, (FMath::Abs(actualpos.Z - targetpos.Z) < 20 ? 2.5f : 5.0f) * (speed / 2)) }; mannequin->SetWorldLocation(newpos, false, nullptr, ETeleportType::ResetPhysics); if(vertical != 1 && FMath::IsNearlyEqual(actualpos.Z, targetpos.Z, 1.0)) { if(vertical == 0) Up(); else Down(); } } 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; 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(vertical == 2) return; ++vertical; playerPos->AddLocalOffset(FVector::UpVector * 150); } void ASubwaySurfManager::Down() { if(vertical == 0) return; --vertical; playerPos->AddLocalOffset(FVector::DownVector * 150); } void ASubwaySurfManager::Left() { if(horizontal == 0) return; --horizontal; playerPos->AddLocalOffset(FVector::LeftVector * 200); } void ASubwaySurfManager::Right() { if(horizontal == 2) return; ++horizontal; playerPos->AddLocalOffset(FVector::RightVector * 200); }