Add EventBus, SpeedRunManager, and GhostManager; implement ghost recording and playback features

This commit is contained in:
2025-09-13 03:30:15 +02:00
parent 46553a351a
commit dfc9201f62
24 changed files with 526 additions and 4 deletions

View File

@@ -0,0 +1,31 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
[GlobalClass]
public partial class GhostEventHandler : Node
{
private GhostManager _ghostManager;
public override void _Ready()
{
_ghostManager = GetNode<GhostManager>("/root/GhostManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
eventBus.LevelStarted += OnLevelStarted;
eventBus.LevelCompleted += OnLevelCompleted;
}
private void OnLevelStarted(int levelIndex, Node currentScene)
{
GD.Print($"GhostEventHandler: Level {levelIndex} started.");
_ghostManager.StartRecording(levelIndex);
_ghostManager.SpawnGhostPlayer(levelIndex, currentScene);
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
_ghostManager.StopRecording(true, completionTime);
}
}

View File

@@ -0,0 +1 @@
uid://dkqeys6lsf04v

View File

@@ -0,0 +1,23 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
[GlobalClass]
public partial class SpeedRunEventHandler : Node
{
private SpeedRunManager _speedRunManager;
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
eventBus.LevelCompleted += OnLevelCompleted;
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
_speedRunManager.Split();
}
}

View File

@@ -0,0 +1 @@
uid://bysxvcvt00t04

9
scripts/GhostFrame.cs Normal file
View File

@@ -0,0 +1,9 @@
using Godot;
namespace Mr.BrickAdventures.scripts;
public partial class GhostFrame : GodotObject
{
public double Timestamp { get; set; }
public Vector2 Position { get; set; }
}

View File

@@ -0,0 +1 @@
uid://d2vth3yveucti

48
scripts/GhostPlayer.cs Normal file
View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Godot;
namespace Mr.BrickAdventures.scripts;
[GlobalClass]
public partial class GhostPlayer : Node2D
{
private List<GhostFrame> _playbackData = [];
private double _startTime = 0;
private int _currentFrameIndex = 0;
private bool _isPlaying = false;
public void StartPlayback(List<GhostFrame> playbackData)
{
_playbackData = playbackData;
_startTime = Time.GetTicksMsec() / 1000.0;
_currentFrameIndex = 0;
_isPlaying = true;
SetProcess(true);
}
public override void _PhysicsProcess(double delta)
{
if (!_isPlaying || _playbackData.Count == 0) return;
var currentTime = (Time.GetTicksMsec() / 1000.0) - _startTime;
while (_currentFrameIndex + 1 < _playbackData.Count && _playbackData[_currentFrameIndex + 1].Timestamp <= currentTime)
{
_currentFrameIndex++;
}
if (_currentFrameIndex + 1 >= _playbackData.Count)
{
GlobalPosition = _playbackData[_currentFrameIndex].Position;
_isPlaying = false;
QueueFree();
return;
}
var frameA = _playbackData[_currentFrameIndex];
var frameB = _playbackData[_currentFrameIndex + 1];
var t = (currentTime - frameA.Timestamp) / (frameB.Timestamp - frameA.Timestamp);
GlobalPosition = frameA.Position.Lerp(frameB.Position, (float)t);
}
}

View File

@@ -0,0 +1 @@
uid://cr4bfcpo27e7t

26
scripts/UI/SpeedRunHud.cs Normal file
View File

@@ -0,0 +1,26 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
[GlobalClass]
public partial class SpeedRunHud : Control
{
[Export] private Label _timerLabel;
private SpeedRunManager _speedRunManager;
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
_speedRunManager.TimeUpdated += OnTimerUpdated;
Visible = _speedRunManager.IsVisible;
}
private void OnTimerUpdated(double totalTime, double levelTime)
{
_timerLabel.Text = SpeedRunManager.FormatTime(totalTime);
}
}

View File

@@ -0,0 +1 @@
uid://0jfdx0hufs55