255 lines
5.7 KiB
C#
255 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
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 : Form, 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
|
|
{
|
|
get
|
|
{
|
|
return ControllerPanel.Controls
|
|
.OfType<VirtualPad>()
|
|
.ToList();
|
|
}
|
|
}
|
|
|
|
public bool Readonly
|
|
{
|
|
get
|
|
{
|
|
return _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<SchemaAttributes>()
|
|
.Any())
|
|
.FirstOrDefault(t => t.GetCustomAttributes(false)
|
|
.OfType<SchemaAttributes>()
|
|
.First().SystemId == Emulator.SystemId);
|
|
|
|
if (schemaType != null)
|
|
{
|
|
var padschemas = (Activator.CreateInstance(schemaType) as IVirtualPadSchema).GetPadSchemas();
|
|
if (VersionInfo.DeveloperBuild)
|
|
{
|
|
CheckPads(padschemas, Emulator.ControllerDefinition);
|
|
}
|
|
var pads = padschemas.Select(s => new VirtualPad(s));
|
|
|
|
if (pads.Any())
|
|
{
|
|
ControllerPanel.Controls.AddRange(pads.Reverse().ToArray());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ScrollToPadSchema(string padSchemaName)
|
|
{
|
|
foreach (var control in ControllerPanel.Controls)
|
|
{
|
|
VirtualPad vp = control as VirtualPad;
|
|
if (vp == null) continue;
|
|
if (vp.PadSchemaDisplayName == padSchemaName)
|
|
ControllerPanel.ScrollControlIntoView(vp);
|
|
}
|
|
}
|
|
|
|
private void CheckPads(IEnumerable<PadSchema> schemas, BizHawk.Emulation.Common.ControllerDefinition def)
|
|
{
|
|
HashSet<string> analogs = new HashSet<string>(def.FloatControls);
|
|
HashSet<string> bools = new HashSet<string>(def.BoolButtons);
|
|
|
|
foreach (var schema in schemas)
|
|
{
|
|
foreach (var button in schema.Buttons)
|
|
{
|
|
HashSet<string> searchset = null;
|
|
switch (button.Type)
|
|
{
|
|
case PadSchema.PadInputType.AnalogStick:
|
|
case PadSchema.PadInputType.FloatSingle:
|
|
case PadSchema.PadInputType.TargetedPair:
|
|
// analog
|
|
searchset = analogs;
|
|
break;
|
|
case PadSchema.PadInputType.Boolean:
|
|
searchset = bools;
|
|
break;
|
|
case PadSchema.PadInputType.DiscManager:
|
|
searchset = bools;
|
|
searchset.UnionWith(analogs);
|
|
break;
|
|
}
|
|
if (!searchset.Contains(button.Name))
|
|
{
|
|
MessageBox.Show(this,
|
|
string.Format("Schema warning: Schema entry '{0}':'{1}' will not correspond to any control in definition '{2}'", schema.DisplayName, button.Name, def.Name),
|
|
"Dev Warning");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#region IToolForm Implementation
|
|
|
|
public bool AskSaveChanges() { return true; }
|
|
public bool UpdateBefore { get { return false; } }
|
|
|
|
public void Restart()
|
|
{
|
|
if (!IsHandleCreated || IsDisposed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CreatePads();
|
|
}
|
|
|
|
public void UpdateValues()
|
|
{
|
|
if (!IsHandleCreated || IsDisposed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Pads.ForEach(p => p.SetPrevious(null)); // Not the cleanest way to clear this every frame
|
|
|
|
if (Global.MovieSession.Movie.IsPlaying && !Global.MovieSession.Movie.IsFinished)
|
|
{
|
|
Readonly = true;
|
|
Pads.ForEach(p => p.Set(Global.MovieSession.CurrentInput));
|
|
}
|
|
else
|
|
{
|
|
if (Global.MovieSession.Movie.IsRecording)
|
|
{
|
|
Pads.ForEach(p => p.SetPrevious(Global.MovieSession.PreviousFrame));
|
|
}
|
|
|
|
Readonly = false;
|
|
}
|
|
|
|
if (!Readonly && !StickyPads && !Control.MouseButtons.HasFlag(MouseButtons.Left))
|
|
{
|
|
Pads.ForEach(pad => pad.Clear());
|
|
}
|
|
|
|
Pads.ForEach(pad => pad.UpdateValues());
|
|
}
|
|
|
|
public void FastUpdate()
|
|
{
|
|
// TODO: SetPrevious logic should go here too or that will get out of whack
|
|
|
|
if (!Readonly && !StickyPads)
|
|
{
|
|
Pads.ForEach(pad => pad.Clear());
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#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
|
|
}
|
|
}
|