225 lines
5.1 KiB
C#
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
|
|
}
|
|
}
|