217 lines
5.0 KiB
C#
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);
|
|
}
|
|
}
|
|
} |