using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Otter {
///
/// Component representing a group of Button and Axis classes. The controller updates all buttons
/// and axes manually. This is used by the Session class to manage player input.
///
/// Input recording should only be used in fixed framerate games! If used with variable framerate
/// the playback is not reliable.
///
public class Controller : Component {
int recordingTimer = 0;
int playingTimer = 0;
int playbackMax = 0;
Dictionary> recordedButtonData = new Dictionary>();
Dictionary> playbackButtonData = new Dictionary>();
List> recordedAxisData = new List>();
List> playbackAxisData = new List>();
///
/// The joystick id associated with this controller.
///
public List JoystickIds = new List();
///
/// Determines if the controller is enabled. If not, all buttons return false, and all axes return 0, 0.
///
public bool Enabled = true;
///
/// If the controller should record axes data.
///
public bool RecordAxes = true;
string recordedString = "";
Dictionary buttons = new Dictionary();
Dictionary axes = new Dictionary();
///
/// If the controller is currently recording input.
///
public bool Recording { get; private set; }
///
/// If the controller is currently playing input data.
///
public bool Playing { get; private set; }
///
/// The last recorded data as a compressed string.
///
public string LastRecordedString {
get {
if (recordedString != "") return recordedString;
string s = "";
//save button data
foreach (var rec in recordedButtonData) {
s += rec.Key.ToString() + ":";
foreach (var e in rec.Value) {
s += e.Key + ">" + e.Value + "|";
}
s = s.TrimEnd('|');
s += (char)16;
}
s = s.TrimEnd((char)16);
//save axis data
s += "%";
foreach (var axisdata in recordedAxisData) {
foreach (var axis in axisdata) {
s += axis.Key + ">";
s += axis.Value.X.ToString() + "," + axis.Value.Y.ToString() + ";";
}
s.TrimEnd(';');
s += "^";
}
s.TrimEnd('^');
recordedString = Util.CompressString(s);
return recordedString;
}
}
public Controller(params int[] joystickId) {
foreach (var i in joystickId) {
JoystickIds.Add(i);
}
}
public Button Button(Enum name) {
return Button(Util.EnumValueToString(name));
}
public Button Button(string name) {
return buttons[name];
}
public Axis Axis(Enum name) {
return Axis(Util.EnumValueToString(name));
}
public Axis Axis(string name) {
return axes[name];
}
public Controller AddButton(Enum name, Button b = null) {
AddButton(Util.EnumValueToString(name), b);
return this;
}
public Controller AddButton(params Enum[] names) {
foreach (var n in names) {
AddButton(n);
}
return this;
}
public Controller AddButton(params string[] names) {
foreach (var n in names) {
AddButton(n);
}
return this;
}
public Controller AddButton(string name, Button b = null) {
buttons.Add(name, b == null ? new Button() : b);
return this;
}
public Controller AddAxis(Enum name, Axis a = null) {
AddAxis(Util.EnumValueToString(name), a);
return this;
}
public Controller AddAxis(string name, Axis a = null) {
axes.Add(name, a == null ? new Axis() : a);
return this;
}
public Controller AddAxis(params Enum[] names) {
foreach (var n in names) {
AddAxis(n);
}
return this;
}
public Controller AddAxis(params string[] names) {
foreach (var n in names) {
AddAxis(n);
}
return this;
}
public void Clear() {
buttons.Clear();
axes.Clear();
}
public override void UpdateFirst() {
base.UpdateFirst();
foreach (var b in buttons) {
b.Value.Enabled = Enabled;
b.Value.UpdateFirst();
}
foreach (var a in axes) {
a.Value.Enabled = Enabled;
a.Value.UpdateFirst();
}
// The recording and playback code is pretty ugly, sorry :I
if (Recording) {
foreach (var b in buttons) {
if (b.Value.Pressed || b.Value.Released) {
if (!recordedButtonData.ContainsKey(recordingTimer)) {
recordedButtonData.Add(recordingTimer, new Dictionary());
}
}
if (b.Value.Pressed) {
recordedButtonData[recordingTimer].Add(b.Key, 1);
}
if (b.Value.Released) {
recordedButtonData[recordingTimer].Add(b.Key, 0);
}
}
if (RecordAxes) {
var id = 0;
foreach (var a in axes) {
var axis = a.Value;
if (axis.HasInput && (axis.X != axis.LastX || axis.Y != axis.LastY)) {
recordedAxisData[id].Add(recordingTimer, new Vector2(axis.X, axis.Y));
}
id++;
}
}
recordingTimer++;
}
if (Playing) {
if (playingTimer > playbackMax) {
Stop();
}
if (playbackButtonData.ContainsKey(playingTimer)) {
foreach (var act in playbackButtonData[playingTimer]) {
if (act.Value == 0) {
buttons[act.Key].ForceState(false);
//Util.Log("Time: " + playingTimer + " " + act.Key + " Released");
}
else {
buttons[act.Key].ForceState(true);
//Util.Log("Time: " + playingTimer + " " + act.Key + " Pressed");
}
}
}
var i = 0;
foreach (var a in axes) {
if (playbackAxisData[i].ContainsKey(playingTimer)) {
a.Value.ForceState(playbackAxisData[i][playingTimer].X, playbackAxisData[i][playingTimer].Y);
//Util.Log("Time: " + playingTimer + " X: " + playbackAxisData[i][playingTimer].X + " Y: " + playbackAxisData[i][playingTimer].Y);
}
i++;
}
playingTimer++;
}
}
///
/// Record the input to a string. Optionally save it out to a file when finished.
///
public void Record() {
Playing = false;
Recording = true;
recordingTimer = 0;
recordedString = "";
recordedButtonData.Clear();
recordedAxisData.Clear();
foreach (var a in axes) {
recordedAxisData.Add(new Dictionary());
}
}
///
/// Play back recorded input data.
///
/// The recorded data.
public void Playback(string source) {
PlaybackInternal(source);
}
///
/// Playbacks the recorded input data loaded from a file path.
///
/// The path to the file relative to Game.Filepath.
public void PlaybackFile(string path) {
path = Game.Instance.Filepath + path;
byte[] b;
using (FileStream f = new FileStream(path, FileMode.Open))
using (GZipStream gz = new GZipStream(f, CompressionMode.Decompress)) {
int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream()) {
int count = 0;
do {
count = gz.Read(buffer, 0, size);
if (count > 0) {
memory.Write(buffer, 0, count);
}
}
while (count > 0);
b = memory.ToArray();
}
}
//string str = Encoding.Default.GetString(b);
string dataString = Encoding.Default.GetString(b);
PlaybackInternal(dataString);
}
///
/// Save the last recorded input data to a file.
///
/// The path to save the data to.
public void SaveRecording(string path = "") {
// I realize that I'm compressing this data twice but whatever ;D
path = Game.Instance.Filepath + path;
string temp = Path.GetTempFileName();
File.WriteAllText(temp, LastRecordedString);
File.WriteAllText(path, LastRecordedString);
byte[] b;
using (FileStream f = new FileStream(temp, FileMode.Open)) {
b = new byte[f.Length];
f.Read(b, 0, (int)f.Length);
}
using (FileStream f2 = new FileStream(path, FileMode.Create))
using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false)) {
gz.Write(b, 0, b.Length);
}
}
///
/// Stop the recording or playback of the controller. This will also release input states.
///
public void Stop() {
if (Recording || Playing) {
foreach (var b in buttons) {
b.Value.ReleaseState();
}
foreach (var a in axes) {
a.Value.ReleaseState();
}
}
Recording = false;
Playing = false;
}
public void Disable() {
Enabled = false;
}
public void Enable() {
Enabled = true;
}
void PlaybackInternal(string source) {
Recording = false;
Playing = true;
playingTimer = 0;
playbackMax = 0;
playbackButtonData.Clear();
playbackAxisData.Clear();
foreach (var a in axes) {
playbackAxisData.Add(new Dictionary());
}
string s = Util.DecompressString(source);
var sb = s.Split('%');
if (sb[0] != "") {
var split = sb[0].Split((char)16);
foreach (var rec in split) {
var timedata = rec.Split(':');
var time = int.Parse(timedata[0]);
playbackMax = (int)Util.Max(time, playbackMax);
playbackButtonData.Add(time, new Dictionary());
var entries = timedata[1].Split('|');
foreach (var e in entries) {
var data = e.Split('>');
playbackButtonData[time].Add(data[0], int.Parse(data[1]));
}
}
}
var i = 0;
foreach (var axesdata in sb[1].Split('^')) {
foreach (var axis in axesdata.Split(';')) {
if (axis == "") continue;
var axisdata = axis.Split('>');
var time = int.Parse(axisdata[0]);
playbackMax = (int)Util.Max(time, playbackMax);
axisdata = axisdata[1].Split(',');
var x = axisdata[0];
var y = axisdata[1];
playbackAxisData[i].Add(time, new Vector2(float.Parse(x), float.Parse(y)));
}
i++;
}
foreach (var b in buttons) {
b.Value.ForceState(false);
}
foreach (var a in axes) {
a.Value.ForceState(0, 0);
}
}
}
}