BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs

217 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
/// <summary>
/// Represents a GB add on
/// </summary>
public interface IPort
{
byte Read(IController c);
ushort ReadAccX(IController c);
ushort ReadAccY(IController c);
ControllerDefinition Definition { get; }
void SyncState(Serializer ser);
int PortNum { get; }
}
[DisplayName("Gameboy Controller")]
public class StandardControls : IPort
{
public StandardControls(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "Gameboy Controller H",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList()
};
}
public int PortNum { get; }
public ControllerDefinition Definition { get; }
public byte Read(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0]))
{
result -= 4;
}
if (c.IsPressed(Definition.BoolButtons[1]))
{
result -= 8;
}
if (c.IsPressed(Definition.BoolButtons[2]))
{
result -= 2;
}
if (c.IsPressed(Definition.BoolButtons[3]))
{
result -= 1;
}
if (c.IsPressed(Definition.BoolButtons[4]))
{
result -= 128;
}
if (c.IsPressed(Definition.BoolButtons[5]))
{
result -= 64;
}
if (c.IsPressed(Definition.BoolButtons[6]))
{
result -= 32;
}
if (c.IsPressed(Definition.BoolButtons[7]))
{
result -= 16;
}
return result;
}
public ushort ReadAccX(IController c)
{
return 0;
}
public ushort ReadAccY(IController c)
{
return 0;
}
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
};
public void SyncState(Serializer ser)
{
//nothing
}
}
[DisplayName("Gameboy Controller + Tilt")]
public class StandardTilt : IPort
{
public StandardTilt(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "Gameboy Controller + Tilt",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList(),
FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" },
FloatRanges = { new[] { -45.0f, 0, 45.0f }, new[] { -45.0f, 0, 45.0f } }
};
}
public int PortNum { get; }
public float theta, phi, theta_prev, phi_prev;
public ControllerDefinition Definition { get; }
public byte Read(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0]))
{
result -= 4;
}
if (c.IsPressed(Definition.BoolButtons[1]))
{
result -= 8;
}
if (c.IsPressed(Definition.BoolButtons[2]))
{
result -= 2;
}
if (c.IsPressed(Definition.BoolButtons[3]))
{
result -= 1;
}
if (c.IsPressed(Definition.BoolButtons[4]))
{
result -= 128;
}
if (c.IsPressed(Definition.BoolButtons[5]))
{
result -= 64;
}
if (c.IsPressed(Definition.BoolButtons[6]))
{
result -= 32;
}
if (c.IsPressed(Definition.BoolButtons[7]))
{
result -= 16;
}
return result;
}
// acc x is the result of rotating around body y AFTER rotating around body x
// therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases
public ushort ReadAccX(IController c)
{
theta_prev = theta;
phi_prev = phi;
theta = (float)(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0);
phi = (float)(c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0);
float temp = (float)(Math.Cos(theta) * Math.Sin(phi));
// here we add in rates of change parameters.
// a typical rate of change for a fast rotation is guessed at 0.5 rad / frame
// since rotations about X have less of a moment arm compared to by, we take 1/5 of the effect as a baseline
float temp2 = (float)((phi - phi_prev) / 0.5 * 25);
return (ushort)(0x81D0 - Math.Floor(temp * 125) - temp2);
}
// acc y is just the sine of the angle
// we assume that ReadAccX is called first, which updates the the states
public ushort ReadAccY(IController c)
{
float temp = (float)Math.Sin(theta);
// here we add in rates of change parameters.
// a typical rate of change for a fast rotation is guessed at 0.5 rad / frame
// further it will be assumed that the resulting acceleration is roughly eqvuivalent to gravity
float temp2 = (float)((theta - theta_prev)/0.5 * 125);
return (ushort)(0x81D0 - Math.Floor(temp * 125) + temp2);
}
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
};
public void SyncState(Serializer ser)
{
// since we need rate of change of angle, need to savestate them
ser.Sync(nameof(theta), ref theta);
}
}
}