BizHawk/src/BizHawk.Client.EmuHawk/tools/VirtualPads/VirtualpadsTool.cs

225 lines
5.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class VirtualpadTool : ToolFormBase, IToolFormAutoConfig
{
[RequiredService]
private IEmulator Emulator { get; set; }
[ConfigPersist]
public bool StickyPads { get; set; }
[ConfigPersist]
public bool ClearAlsoClearsAnalog { get; set; }
private bool _readOnly;
private List<VirtualPad> Pads =>
ControllerPanel.Controls
.OfType<VirtualPad>()
.ToList();
public bool Readonly
{
get => _readOnly;
set
{
_readOnly = value;
Pads.ForEach(p => p.ReadOnly = value);
}
}
public VirtualpadTool()
{
StickyPads = true;
InitializeComponent();
}
private void VirtualpadTool_Load(object sender, EventArgs e)
{
CreatePads();
}
public void ClearVirtualPadHolds()
{
if (ClearAlsoClearsAnalog)
{
Pads.ForEach(pad => pad.Clear());
}
else
{
Pads.ForEach(pad => pad.ClearBoolean());
}
}
public void BumpAnalogValue(int? x, int? y) // TODO: multi-player
{
Pads.ForEach(pad => pad.BumpAnalog(x, y));
}
private void CreatePads()
{
ControllerPanel.Controls.Clear();
var schemaType = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => typeof(IVirtualPadSchema)
.IsAssignableFrom(t) && t.GetCustomAttributes(false)
.OfType<SchemaAttribute>()
.Any())
.FirstOrDefault(t => t.GetCustomAttributes(false)
.OfType<SchemaAttribute>()
.First().SystemId == Emulator.SystemId);
if (schemaType == null) return;
var padSchemata = ((IVirtualPadSchema) Activator.CreateInstance(schemaType))
.GetPadSchemas(Emulator)
.ToList();
if (VersionInfo.DeveloperBuild)
{
var buttonControls = Emulator.ControllerDefinition.BoolButtons;
var axisControls = Emulator.ControllerDefinition.AxisControls;
foreach (var schema in padSchemata) foreach (var controlSchema in schema.Buttons)
{
Predicate<string> searchSetContains = controlSchema switch
{
ButtonSchema _ => buttonControls.Contains,
DiscManagerSchema _ => s => buttonControls.Contains(s) || axisControls.Contains(s),
_ => axisControls.Contains
};
if (!searchSetContains(controlSchema.Name))
{
MessageBox.Show(this,
$"Schema warning: Schema entry '{schema.DisplayName}':'{controlSchema.Name}' will not correspond to any control in definition '{Emulator.ControllerDefinition.Name}'",
"Dev Warning");
}
}
}
ControllerPanel.Controls.AddRange(padSchemata.Select(s => (Control) new VirtualPad(s)).Reverse().ToArray());
}
public void ScrollToPadSchema(string padSchemaName)
{
foreach (var control in ControllerPanel.Controls)
{
var vp = control as VirtualPad;
if (vp == null)
{
continue;
}
if (vp.PadSchemaDisplayName == padSchemaName)
{
ControllerPanel.ScrollControlIntoView(vp);
}
}
}
public void Restart()
{
if (!IsHandleCreated || IsDisposed)
{
return;
}
CreatePads();
}
protected override void UpdateAfter()
{
if (!IsHandleCreated || IsDisposed)
{
return;
}
Pads.ForEach(p => p.SetPrevious(null)); // Not the cleanest way to clear this every frame
if (MovieSession.Movie.Mode == MovieMode.Play)
{
Readonly = true;
if (MovieSession.CurrentInput != null)
{
Pads.ForEach(p => p.Set(MovieSession.CurrentInput));
}
}
else
{
if (MovieSession.Movie.IsRecording())
{
Pads.ForEach(p => p.SetPrevious(MovieSession.PreviousFrame));
}
Readonly = false;
}
if (!Readonly && !StickyPads && !MouseButtons.HasFlag(MouseButtons.Left))
{
Pads.ForEach(pad => pad.Clear());
}
Pads.ForEach(pad => pad.UpdateValues());
}
protected override void FastUpdateAfter()
{
// TODO: SetPrevious logic should go here too or that will get out of whack
if (!Readonly && !StickyPads)
{
Pads.ForEach(pad => pad.Clear());
}
}
#region Menu
private void PadsSubMenu_DropDownOpened(object sender, EventArgs e)
{
StickyMenuItem.Checked = StickyPads;
}
private void ClearAllMenuItem_Click(object sender, EventArgs e)
{
ClearVirtualPadHolds();
}
private void StickyMenuItem_Click(object sender, EventArgs e)
{
StickyPads ^= true;
}
private void PadBoxContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
StickyContextMenuItem.Checked = StickyPads;
}
private void ExitMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e)
{
ClearClearsAnalogInputMenuItem.Checked = ClearAlsoClearsAnalog;
}
private void ClearClearsAnalogInputMenuItem_Click(object sender, EventArgs e)
{
ClearAlsoClearsAnalog ^= true;
}
#endregion
}
}