Skip to content

Commit

Permalink
Revert "Remove input chunk from GhostFrame, input record & replay"
Browse files Browse the repository at this point in the history
This reverts commit bec1b31b558465248c56aed539b840f31803d2e2.
  • Loading branch information
0x0ade committed Mar 5, 2018
1 parent 63150fe commit 6ff63dc
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 0 deletions.
1 change: 1 addition & 0 deletions GhostMod/GhostData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static void ForAllGhosts(Session session, Func<int, GhostData, bool> cb)

public string SID;
public AreaMode Mode;
public string From;
public string Level;
public string Target;

Expand Down
204 changes: 204 additions & 0 deletions GhostMod/GhostFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public void Read(BinaryReader reader) {
case "data":
ReadChunkData(reader);
break;
case "input":
ReadChunkInput(reader);
break;
default:
// Skip any unknown chunks.
reader.BaseStream.Seek(length, SeekOrigin.Current);
Expand All @@ -33,6 +36,8 @@ public void Read(BinaryReader reader) {
public void Write(BinaryWriter writer) {
WriteChunkData(writer);

WriteChunkInput(writer);

writer.WriteNullTerminatedString("\r\n");
}

Expand Down Expand Up @@ -148,5 +153,204 @@ public void WriteChunkData(BinaryWriter writer) {
WriteChunkEnd(writer, start);
}

public bool HasInput;

public int MoveX;
public int MoveY;

public Vector2 Aim;
public Vector2 MountainAim;

public int Buttons;
public bool ESC {
get {
return (Buttons & (int) ButtonMask.ESC) == (int) ButtonMask.ESC;
}
set {
Buttons &= (int) ~ButtonMask.ESC;
if (value)
Buttons |= (int) ButtonMask.ESC;
}
}
public bool Pause {
get {
return (Buttons & (int) ButtonMask.Pause) == (int) ButtonMask.Pause;
}
set {
Buttons &= (int) ~ButtonMask.Pause;
if (value)
Buttons |= (int) ButtonMask.Pause;
}
}
public bool MenuLeft {
get {
return (Buttons & (int) ButtonMask.MenuLeft) == (int) ButtonMask.MenuLeft;
}
set {
Buttons &= (int) ~ButtonMask.MenuLeft;
if (value)
Buttons |= (int) ButtonMask.MenuLeft;
}
}
public bool MenuRight {
get {
return (Buttons & (int) ButtonMask.MenuRight) == (int) ButtonMask.MenuRight;
}
set {
Buttons &= (int) ~ButtonMask.MenuRight;
if (value)
Buttons |= (int) ButtonMask.MenuRight;
}
}
public bool MenuUp {
get {
return (Buttons & (int) ButtonMask.MenuUp) == (int) ButtonMask.MenuUp;
}
set {
Buttons &= (int) ~ButtonMask.MenuUp;
if (value)
Buttons |= (int) ButtonMask.MenuUp;
}
}
public bool MenuDown {
get {
return (Buttons & (int) ButtonMask.MenuDown) == (int) ButtonMask.MenuDown;
}
set {
Buttons &= (int) ~ButtonMask.MenuDown;
if (value)
Buttons |= (int) ButtonMask.MenuDown;
}
}
public bool MenuConfirm {
get {
return (Buttons & (int) ButtonMask.MenuConfirm) == (int) ButtonMask.MenuConfirm;
}
set {
Buttons &= (int) ~ButtonMask.MenuConfirm;
if (value)
Buttons |= (int) ButtonMask.MenuConfirm;
}
}
public bool MenuCancel {
get {
return (Buttons & (int) ButtonMask.MenuCancel) == (int) ButtonMask.MenuCancel;
}
set {
Buttons &= (int) ~ButtonMask.MenuCancel;
if (value)
Buttons |= (int) ButtonMask.MenuCancel;
}
}
public bool MenuJournal {
get {
return (Buttons & (int) ButtonMask.MenuJournal) == (int) ButtonMask.MenuJournal;
}
set {
Buttons &= (int) ~ButtonMask.MenuJournal;
if (value)
Buttons |= (int) ButtonMask.MenuJournal;
}
}
public bool QuickRestart {
get {
return (Buttons & (int) ButtonMask.QuickRestart) == (int) ButtonMask.QuickRestart;
}
set {
Buttons &= (int) ~ButtonMask.QuickRestart;
if (value)
Buttons |= (int) ButtonMask.QuickRestart;
}
}
public bool Jump {
get {
return (Buttons & (int) ButtonMask.Jump) == (int) ButtonMask.Jump;
}
set {
Buttons &= (int) ~ButtonMask.Jump;
if (value)
Buttons |= (int) ButtonMask.Jump;
}
}
public bool Dash {
get {
return (Buttons & (int) ButtonMask.Dash) == (int) ButtonMask.Dash;
}
set {
Buttons &= (int) ~ButtonMask.Dash;
if (value)
Buttons |= (int) ButtonMask.Dash;
}
}
public bool Grab {
get {
return (Buttons & (int) ButtonMask.Grab) == (int) ButtonMask.Grab;
}
set {
Buttons &= (int) ~ButtonMask.Grab;
if (value)
Buttons |= (int) ButtonMask.Grab;
}
}
public bool Talk {
get {
return (Buttons & (int) ButtonMask.Talk) == (int) ButtonMask.Talk;
}
set {
Buttons &= (int) ~ButtonMask.Talk;
if (value)
Buttons |= (int) ButtonMask.Talk;
}
}

public void ReadChunkInput(BinaryReader reader) {
HasInput = true;

MoveX = reader.ReadInt32();
MoveY = reader.ReadInt32();

Aim = new Vector2(reader.ReadSingle(), reader.ReadSingle());
MountainAim = new Vector2(reader.ReadSingle(), reader.ReadSingle());

Buttons = reader.ReadInt32();
}

public void WriteChunkInput(BinaryWriter writer) {
if (!HasInput)
return;
long start = WriteChunkStart(writer, "input");

writer.Write(MoveX);
writer.Write(MoveY);

writer.Write(Aim.X);
writer.Write(Aim.Y);

writer.Write(MountainAim.X);
writer.Write(MountainAim.Y);

writer.Write(Buttons);

WriteChunkEnd(writer, start);
}

[Flags]
public enum ButtonMask : int {
ESC = 1 << 0,
Pause = 1 << 1,
MenuLeft = 1 << 2,
MenuRight = 1 << 3,
MenuUp = 1 << 4,
MenuDown = 1 << 5,
MenuConfirm = 1 << 6,
MenuCancel = 1 << 7,
MenuJournal = 1 << 8,
QuickRestart = 1 << 9,
Jump = 1 << 10,
Dash = 1 << 11,
Grab = 1 << 12,
Talk = 1 << 13
}

}
}
60 changes: 60 additions & 0 deletions GhostMod/GhostInputNodes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using FMOD.Studio;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;

namespace Celeste.Mod.Ghost {
public static class GhostInputNodes {

public class MoveX : VirtualAxis.Node {
public GhostInputReplayer Replayer;
public MoveX(GhostInputReplayer replayer) {
Replayer = replayer;
}
public override float Value => Replayer.Frame.MoveX;
}

public class MoveY : VirtualAxis.Node {
public GhostInputReplayer Replayer;
public MoveY(GhostInputReplayer replayer) {
Replayer = replayer;
}
public override float Value => Replayer.Frame.MoveY;
}

public class Aim : VirtualJoystick.Node {
public GhostInputReplayer Replayer;
public Aim(GhostInputReplayer replayer) {
Replayer = replayer;
}
public override Vector2 Value => Replayer.Frame.Aim;
}

public class MountainAim : VirtualJoystick.Node {
public GhostInputReplayer Replayer;
public MountainAim(GhostInputReplayer replayer) {
Replayer = replayer;
}
public override Vector2 Value => Replayer.Frame.MountainAim;
}

public class Button : VirtualButton.Node {
public GhostInputReplayer Replayer;
public int Mask;
public Button(GhostInputReplayer replayer, GhostFrame.ButtonMask mask) {
Replayer = replayer;
Mask = (int) mask;
}
public override bool Check => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == Mask;
public override bool Pressed => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == Mask && (Replayer.PrevFrame.Buttons & Mask) == 0;
public override bool Released => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == 0 && (Replayer.PrevFrame.Buttons & Mask) == Mask;
}

}
}
75 changes: 75 additions & 0 deletions GhostMod/GhostInputReplayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using FMOD.Studio;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;

namespace Celeste.Mod.Ghost {
// We need this to work across scenes.
public class GhostInputReplayer : GameComponent {

public GhostData Data;
public int FrameIndex = 0;
public GhostFrame Frame => Data == null ? default(GhostFrame) : Data[FrameIndex];
public GhostFrame PrevFrame => Data == null ? default(GhostFrame) : Data[FrameIndex - 1];

public GhostInputReplayer(Game game, GhostData data)
: base(game) {
Data = data;

Everest.Events.Input.OnInitialize += HookInput;
HookInput();
}

public void HookInput() {
Input.MoveX.Nodes.Add(new GhostInputNodes.MoveX(this));
Input.MoveY.Nodes.Add(new GhostInputNodes.MoveY(this));

Input.Aim.Nodes.Add(new GhostInputNodes.Aim(this));
Input.MountainAim.Nodes.Add(new GhostInputNodes.MountainAim(this));

Input.ESC.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.ESC));
Input.Pause.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Pause));
Input.MenuLeft.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuLeft));
Input.MenuRight.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuRight));
Input.MenuUp.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuUp));
Input.MenuDown.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuDown));
Input.MenuConfirm.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuConfirm));
Input.MenuCancel.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuCancel));
Input.MenuJournal.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuJournal));
Input.QuickRestart.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.QuickRestart));
Input.Jump.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Jump));
Input.Dash.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Dash));
Input.Grab.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Grab));
Input.Talk.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Talk));

Logger.Log("ghost", "GhostReplayer hooked input.");
}

public override void Update(GameTime gameTime) {
base.Update(gameTime);

do {
FrameIndex++;
} while (
(!Frame.HasInput && FrameIndex < Data.Frames.Count) // Skip any frames not containing the input chunk.
);

if (Data == null || FrameIndex >= Data.Frames.Count)
Remove();
}

public void Remove() {
Everest.Events.Input.OnInitialize -= HookInput;
Input.Initialize();
Logger.Log("ghost", "GhostReplayer returned input.");
Game.Components.Remove(this);
}

}
}
2 changes: 2 additions & 0 deletions GhostMod/GhostMod.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@
</ItemGroup>
<ItemGroup>
<Compile Include="GhostName.cs" />
<Compile Include="GhostInputReplayer.cs" />
<Compile Include="GhostRecorder.cs" />
<Compile Include="GhostData.cs" />
<Compile Include="Ghost.cs" />
<Compile Include="GhostFrame.cs" />
<Compile Include="GhostModuleSettings.cs" />
<Compile Include="GhostModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GhostInputNodes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Everest\Celeste.Mod.mm\Celeste.Mod.mm.csproj">
Expand Down
Loading

0 comments on commit 6ff63dc

Please sign in to comment.