// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Replays; using osu.Game.Scoring; using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Rulesets.UI { public abstract class ReplayRecorder : ReplayRecorder, IKeyBindingHandler where T : struct { private readonly Score target; private readonly List pressedActions = new List(); private InputManager inputManager; public int RecordFrameRate = 60; [Resolved] private SpectatorClient spectatorClient { get; set; } [Resolved] private GameplayState gameplayState { get; set; } protected ReplayRecorder(Score target) { this.target = target; RelativeSizeAxes = Axes.Both; Depth = float.MinValue; } protected override void LoadComplete() { base.LoadComplete(); inputManager = GetContainingInputManager(); spectatorClient.BeginPlaying(gameplayState, target); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); spectatorClient?.EndPlaying(gameplayState); } protected override void Update() { base.Update(); recordFrame(false); } protected override bool OnMouseMove(MouseMoveEvent e) { recordFrame(false); return base.OnMouseMove(e); } public bool OnPressed(KeyBindingPressEvent e) { pressedActions.Add(e.Action); recordFrame(true); return false; } public void OnReleased(KeyBindingReleaseEvent e) { pressedActions.Remove(e.Action); recordFrame(true); } private void recordFrame(bool important) { var last = target.Replay.Frames.LastOrDefault(); if (!important && last != null && Time.Current - last.Time < (1000d / RecordFrameRate)) return; var position = ScreenSpaceToGamefield?.Invoke(inputManager.CurrentState.Mouse.Position) ?? inputManager.CurrentState.Mouse.Position; var frame = HandleFrame(position, pressedActions, last); if (frame != null) { target.Replay.Frames.Add(frame); spectatorClient?.HandleFrame(frame); } } protected abstract ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame); } public abstract class ReplayRecorder : Component { public Func ScreenSpaceToGamefield; } }