From 1fc443640a5a1b1686599495f6cbbd92f8ec7555 Mon Sep 17 00:00:00 2001 From: Morilli <35152647+Morilli@users.noreply.github.com> Date: Sat, 14 Sep 2024 09:54:33 +0200 Subject: [PATCH] Reorganize input display drawing I tested all combinations I can think of and this looks correct. Hopefully this can easily be verified with the given code comments now. --- .../DisplayManager/OSDManager.cs | 75 +++++++++---------- .../inputAdapters/InputManager.cs | 3 - 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs b/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs index d5273f431c..6ca3debc37 100644 --- a/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs +++ b/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs @@ -171,29 +171,30 @@ namespace BizHawk.Client.Common } } - public string InputStrMovie() + private string InputStrMovie() { var state = _movieSession.Movie?.GetInputState(_emulator.Frame - 1); return state is not null ? MakeStringFor(state) : ""; } - public string InputStrImmediate() + private string InputStrCurrent() => MakeStringFor(_inputManager.AutofireStickyXorAdapter); + // returns an input string for inputs pressed by the user that are not getting unpressed by the sticky adapters + private string InputStrUser() + => MakeStringFor(_inputManager.AutofireStickyXorAdapter.And(_inputManager.StickyXorAdapter.Source)); + private static string MakeStringFor(IController controller) { return Bk2InputDisplayGenerator.Generate(controller); } - public string MakeIntersectImmediatePrevious() + private string MakeIntersectImmediatePrevious() { - if (_movieSession.Movie.IsActive()) + if (_movieSession.Movie.IsRecording()) { - var m = _movieSession.Movie.IsPlayingOrRecording() - ? _movieSession.Movie.GetInputState(_emulator.Frame - 1) - : _movieSession.MovieController; - - return MakeStringFor(_inputManager.AutofireStickyXorAdapter.And(m)); + var movieInput = _movieSession.Movie.GetInputState(_emulator.Frame - 1); + return MakeStringFor(_inputManager.AutofireStickyXorAdapter.And(movieInput)); } return ""; @@ -237,42 +238,34 @@ namespace BizHawk.Client.Common } else // TODO: message config -- allow setting of "mixed", and "auto" { - var previousColor = Color.FromArgb(_config.LastInputColor); - var immediateColor = Color.FromArgb(_config.MessagesColor); - var autoColor = Color.Pink; - var changedColor = Color.PeachPuff; + var previousColor = _movieSession.Movie.IsRecording() ? Color.FromArgb(_config.LastInputColor) : Color.FromArgb(_config.MovieInput); + var currentColor = Color.FromArgb(_config.MessagesColor); + var stickyColor = Color.Pink; + var currentAndPreviousColor = Color.PeachPuff; - // now, we're going to render these repeatedly, with higher-priority things overriding + // now, we're going to render these repeatedly, with higher priority draws overwriting all lower priority draws + // in order of highest priority to lowest, we are effectively displaying (in different colors): + // 1. currently pressed input that was also pressed on the previous frame (movie active + recording mode only) + // 2. currently pressed input that is being pressed by sticky autohold or sticky autofire + // 3. currently pressed input by the user (non-sticky) + // 4. input that was pressed on the previous frame (movie active only) - // first display previous frame's input. - // note: that's only available in case we're working on a movie - var previousStr = InputStrMovie(); + var previousInput = InputStrMovie(); + var currentInput = InputStrCurrent(); + var userInput = InputStrUser(); + var currentAndPreviousInput = MakeIntersectImmediatePrevious(); - //we need some kind of string for calculating position when right-anchoring, of something like that - var point = GetCoordinates(g, _config.InputDisplay, previousStr); + // calculate origin for drawing all strings. Mainly relevant when right-anchoring + var point = GetCoordinates(g, _config.InputDisplay, currentInput); - bool atMovieEnd = _movieSession.Movie.IsFinished() && _movieSession.Movie.IsAtEnd(); - g.DrawString(previousStr, atMovieEnd ? Color.FromArgb(_config.MovieInput) : previousColor, point.X, point.Y); - - // next, draw the immediate input. - // that is, whatever is being held down interactively right this moment even if the game is paused - // this includes things held down due to autohold or autofire - // I know, this is all really confusing - var immediate = InputStrImmediate(); - g.DrawString(immediate, immediateColor, point.X, point.Y); - - // next draw anything that's pressed because it's sticky. - // this applies to autofire and autohold both. somehow. I don't understand it. - // basically we're tinting whatever is pressed because it's sticky specially - // in order to achieve this we want to avoid drawing anything pink that isn't actually held down right now - // so we make an AND adapter and combine it using immediate & sticky - // (adapter creation moved to InputManager) - var autoString = MakeStringFor(_inputManager.WeirdStickyControllerForInputDisplay); - g.DrawString(autoString, autoColor, point.X, point.Y); - - //recolor everything that's changed from the previous input - var immediateOverlay = MakeIntersectImmediatePrevious(); - g.DrawString(immediateOverlay, changedColor, point.X, point.Y); + // draw previous input first. Currently pressed input will overwrite this + g.DrawString(previousInput, previousColor, point.X, point.Y); + // draw all currently pressed input with the sticky color + g.DrawString(currentInput, stickyColor, point.X, point.Y); + // draw all currently pressed non-sticky input with the current color, overwriting previously drawn non-sticky input in the wrong color + g.DrawString(userInput, currentColor, point.X, point.Y); + // re-draw all currently pressed inputs that were also pressed on the previous frame in their own color + g.DrawString(currentAndPreviousInput, currentAndPreviousColor, point.X, point.Y); } } diff --git a/src/BizHawk.Client.Common/inputAdapters/InputManager.cs b/src/BizHawk.Client.Common/inputAdapters/InputManager.cs index 31549df327..c12e5c1522 100644 --- a/src/BizHawk.Client.Common/inputAdapters/InputManager.cs +++ b/src/BizHawk.Client.Common/inputAdapters/InputManager.cs @@ -32,8 +32,6 @@ namespace BizHawk.Client.Common /// public StickyXorAdapter StickyXorAdapter { get; } = new StickyXorAdapter(); - public IController WeirdStickyControllerForInputDisplay { get; private set; } - /// /// Used to AND to another controller, used for JoypadApi.Set /// @@ -79,7 +77,6 @@ namespace BizHawk.Client.Common // these are all reference types which don't change so this SHOULD be a no-op, but I'm not brave enough to move it to the ctor --yoshi StickyXorAdapter.Source = UdLRControllerAdapter; AutofireStickyXorAdapter = new() { Source = StickyXorAdapter }; - WeirdStickyControllerForInputDisplay = StickyXorAdapter.Source.Xor(AutofireStickyXorAdapter).And(AutofireStickyXorAdapter); session.MovieIn = AutofireStickyXorAdapter; session.StickySource = AutofireStickyXorAdapter;