Skip to content

Commit 6ff63dc

Browse files
committed
Revert "Remove input chunk from GhostFrame, input record & replay"
This reverts commit bec1b31b558465248c56aed539b840f31803d2e2.
1 parent 63150fe commit 6ff63dc

File tree

6 files changed

+400
-0
lines changed

6 files changed

+400
-0
lines changed

GhostMod/GhostData.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public static void ForAllGhosts(Session session, Func<int, GhostData, bool> cb)
6868

6969
public string SID;
7070
public AreaMode Mode;
71+
public string From;
7172
public string Level;
7273
public string Target;
7374

GhostMod/GhostFrame.cs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public void Read(BinaryReader reader) {
2222
case "data":
2323
ReadChunkData(reader);
2424
break;
25+
case "input":
26+
ReadChunkInput(reader);
27+
break;
2528
default:
2629
// Skip any unknown chunks.
2730
reader.BaseStream.Seek(length, SeekOrigin.Current);
@@ -33,6 +36,8 @@ public void Read(BinaryReader reader) {
3336
public void Write(BinaryWriter writer) {
3437
WriteChunkData(writer);
3538

39+
WriteChunkInput(writer);
40+
3641
writer.WriteNullTerminatedString("\r\n");
3742
}
3843

@@ -148,5 +153,204 @@ public void WriteChunkData(BinaryWriter writer) {
148153
WriteChunkEnd(writer, start);
149154
}
150155

156+
public bool HasInput;
157+
158+
public int MoveX;
159+
public int MoveY;
160+
161+
public Vector2 Aim;
162+
public Vector2 MountainAim;
163+
164+
public int Buttons;
165+
public bool ESC {
166+
get {
167+
return (Buttons & (int) ButtonMask.ESC) == (int) ButtonMask.ESC;
168+
}
169+
set {
170+
Buttons &= (int) ~ButtonMask.ESC;
171+
if (value)
172+
Buttons |= (int) ButtonMask.ESC;
173+
}
174+
}
175+
public bool Pause {
176+
get {
177+
return (Buttons & (int) ButtonMask.Pause) == (int) ButtonMask.Pause;
178+
}
179+
set {
180+
Buttons &= (int) ~ButtonMask.Pause;
181+
if (value)
182+
Buttons |= (int) ButtonMask.Pause;
183+
}
184+
}
185+
public bool MenuLeft {
186+
get {
187+
return (Buttons & (int) ButtonMask.MenuLeft) == (int) ButtonMask.MenuLeft;
188+
}
189+
set {
190+
Buttons &= (int) ~ButtonMask.MenuLeft;
191+
if (value)
192+
Buttons |= (int) ButtonMask.MenuLeft;
193+
}
194+
}
195+
public bool MenuRight {
196+
get {
197+
return (Buttons & (int) ButtonMask.MenuRight) == (int) ButtonMask.MenuRight;
198+
}
199+
set {
200+
Buttons &= (int) ~ButtonMask.MenuRight;
201+
if (value)
202+
Buttons |= (int) ButtonMask.MenuRight;
203+
}
204+
}
205+
public bool MenuUp {
206+
get {
207+
return (Buttons & (int) ButtonMask.MenuUp) == (int) ButtonMask.MenuUp;
208+
}
209+
set {
210+
Buttons &= (int) ~ButtonMask.MenuUp;
211+
if (value)
212+
Buttons |= (int) ButtonMask.MenuUp;
213+
}
214+
}
215+
public bool MenuDown {
216+
get {
217+
return (Buttons & (int) ButtonMask.MenuDown) == (int) ButtonMask.MenuDown;
218+
}
219+
set {
220+
Buttons &= (int) ~ButtonMask.MenuDown;
221+
if (value)
222+
Buttons |= (int) ButtonMask.MenuDown;
223+
}
224+
}
225+
public bool MenuConfirm {
226+
get {
227+
return (Buttons & (int) ButtonMask.MenuConfirm) == (int) ButtonMask.MenuConfirm;
228+
}
229+
set {
230+
Buttons &= (int) ~ButtonMask.MenuConfirm;
231+
if (value)
232+
Buttons |= (int) ButtonMask.MenuConfirm;
233+
}
234+
}
235+
public bool MenuCancel {
236+
get {
237+
return (Buttons & (int) ButtonMask.MenuCancel) == (int) ButtonMask.MenuCancel;
238+
}
239+
set {
240+
Buttons &= (int) ~ButtonMask.MenuCancel;
241+
if (value)
242+
Buttons |= (int) ButtonMask.MenuCancel;
243+
}
244+
}
245+
public bool MenuJournal {
246+
get {
247+
return (Buttons & (int) ButtonMask.MenuJournal) == (int) ButtonMask.MenuJournal;
248+
}
249+
set {
250+
Buttons &= (int) ~ButtonMask.MenuJournal;
251+
if (value)
252+
Buttons |= (int) ButtonMask.MenuJournal;
253+
}
254+
}
255+
public bool QuickRestart {
256+
get {
257+
return (Buttons & (int) ButtonMask.QuickRestart) == (int) ButtonMask.QuickRestart;
258+
}
259+
set {
260+
Buttons &= (int) ~ButtonMask.QuickRestart;
261+
if (value)
262+
Buttons |= (int) ButtonMask.QuickRestart;
263+
}
264+
}
265+
public bool Jump {
266+
get {
267+
return (Buttons & (int) ButtonMask.Jump) == (int) ButtonMask.Jump;
268+
}
269+
set {
270+
Buttons &= (int) ~ButtonMask.Jump;
271+
if (value)
272+
Buttons |= (int) ButtonMask.Jump;
273+
}
274+
}
275+
public bool Dash {
276+
get {
277+
return (Buttons & (int) ButtonMask.Dash) == (int) ButtonMask.Dash;
278+
}
279+
set {
280+
Buttons &= (int) ~ButtonMask.Dash;
281+
if (value)
282+
Buttons |= (int) ButtonMask.Dash;
283+
}
284+
}
285+
public bool Grab {
286+
get {
287+
return (Buttons & (int) ButtonMask.Grab) == (int) ButtonMask.Grab;
288+
}
289+
set {
290+
Buttons &= (int) ~ButtonMask.Grab;
291+
if (value)
292+
Buttons |= (int) ButtonMask.Grab;
293+
}
294+
}
295+
public bool Talk {
296+
get {
297+
return (Buttons & (int) ButtonMask.Talk) == (int) ButtonMask.Talk;
298+
}
299+
set {
300+
Buttons &= (int) ~ButtonMask.Talk;
301+
if (value)
302+
Buttons |= (int) ButtonMask.Talk;
303+
}
304+
}
305+
306+
public void ReadChunkInput(BinaryReader reader) {
307+
HasInput = true;
308+
309+
MoveX = reader.ReadInt32();
310+
MoveY = reader.ReadInt32();
311+
312+
Aim = new Vector2(reader.ReadSingle(), reader.ReadSingle());
313+
MountainAim = new Vector2(reader.ReadSingle(), reader.ReadSingle());
314+
315+
Buttons = reader.ReadInt32();
316+
}
317+
318+
public void WriteChunkInput(BinaryWriter writer) {
319+
if (!HasInput)
320+
return;
321+
long start = WriteChunkStart(writer, "input");
322+
323+
writer.Write(MoveX);
324+
writer.Write(MoveY);
325+
326+
writer.Write(Aim.X);
327+
writer.Write(Aim.Y);
328+
329+
writer.Write(MountainAim.X);
330+
writer.Write(MountainAim.Y);
331+
332+
writer.Write(Buttons);
333+
334+
WriteChunkEnd(writer, start);
335+
}
336+
337+
[Flags]
338+
public enum ButtonMask : int {
339+
ESC = 1 << 0,
340+
Pause = 1 << 1,
341+
MenuLeft = 1 << 2,
342+
MenuRight = 1 << 3,
343+
MenuUp = 1 << 4,
344+
MenuDown = 1 << 5,
345+
MenuConfirm = 1 << 6,
346+
MenuCancel = 1 << 7,
347+
MenuJournal = 1 << 8,
348+
QuickRestart = 1 << 9,
349+
Jump = 1 << 10,
350+
Dash = 1 << 11,
351+
Grab = 1 << 12,
352+
Talk = 1 << 13
353+
}
354+
151355
}
152356
}

GhostMod/GhostInputNodes.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using FMOD.Studio;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
using System;
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using YamlDotNet.Serialization;
11+
12+
namespace Celeste.Mod.Ghost {
13+
public static class GhostInputNodes {
14+
15+
public class MoveX : VirtualAxis.Node {
16+
public GhostInputReplayer Replayer;
17+
public MoveX(GhostInputReplayer replayer) {
18+
Replayer = replayer;
19+
}
20+
public override float Value => Replayer.Frame.MoveX;
21+
}
22+
23+
public class MoveY : VirtualAxis.Node {
24+
public GhostInputReplayer Replayer;
25+
public MoveY(GhostInputReplayer replayer) {
26+
Replayer = replayer;
27+
}
28+
public override float Value => Replayer.Frame.MoveY;
29+
}
30+
31+
public class Aim : VirtualJoystick.Node {
32+
public GhostInputReplayer Replayer;
33+
public Aim(GhostInputReplayer replayer) {
34+
Replayer = replayer;
35+
}
36+
public override Vector2 Value => Replayer.Frame.Aim;
37+
}
38+
39+
public class MountainAim : VirtualJoystick.Node {
40+
public GhostInputReplayer Replayer;
41+
public MountainAim(GhostInputReplayer replayer) {
42+
Replayer = replayer;
43+
}
44+
public override Vector2 Value => Replayer.Frame.MountainAim;
45+
}
46+
47+
public class Button : VirtualButton.Node {
48+
public GhostInputReplayer Replayer;
49+
public int Mask;
50+
public Button(GhostInputReplayer replayer, GhostFrame.ButtonMask mask) {
51+
Replayer = replayer;
52+
Mask = (int) mask;
53+
}
54+
public override bool Check => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == Mask;
55+
public override bool Pressed => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == Mask && (Replayer.PrevFrame.Buttons & Mask) == 0;
56+
public override bool Released => !MInput.Disabled && (Replayer.Frame.Buttons & Mask) == 0 && (Replayer.PrevFrame.Buttons & Mask) == Mask;
57+
}
58+
59+
}
60+
}

GhostMod/GhostInputReplayer.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using FMOD.Studio;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
using System;
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using YamlDotNet.Serialization;
11+
12+
namespace Celeste.Mod.Ghost {
13+
// We need this to work across scenes.
14+
public class GhostInputReplayer : GameComponent {
15+
16+
public GhostData Data;
17+
public int FrameIndex = 0;
18+
public GhostFrame Frame => Data == null ? default(GhostFrame) : Data[FrameIndex];
19+
public GhostFrame PrevFrame => Data == null ? default(GhostFrame) : Data[FrameIndex - 1];
20+
21+
public GhostInputReplayer(Game game, GhostData data)
22+
: base(game) {
23+
Data = data;
24+
25+
Everest.Events.Input.OnInitialize += HookInput;
26+
HookInput();
27+
}
28+
29+
public void HookInput() {
30+
Input.MoveX.Nodes.Add(new GhostInputNodes.MoveX(this));
31+
Input.MoveY.Nodes.Add(new GhostInputNodes.MoveY(this));
32+
33+
Input.Aim.Nodes.Add(new GhostInputNodes.Aim(this));
34+
Input.MountainAim.Nodes.Add(new GhostInputNodes.MountainAim(this));
35+
36+
Input.ESC.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.ESC));
37+
Input.Pause.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Pause));
38+
Input.MenuLeft.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuLeft));
39+
Input.MenuRight.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuRight));
40+
Input.MenuUp.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuUp));
41+
Input.MenuDown.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuDown));
42+
Input.MenuConfirm.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuConfirm));
43+
Input.MenuCancel.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuCancel));
44+
Input.MenuJournal.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.MenuJournal));
45+
Input.QuickRestart.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.QuickRestart));
46+
Input.Jump.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Jump));
47+
Input.Dash.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Dash));
48+
Input.Grab.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Grab));
49+
Input.Talk.Nodes.Add(new GhostInputNodes.Button(this, GhostFrame.ButtonMask.Talk));
50+
51+
Logger.Log("ghost", "GhostReplayer hooked input.");
52+
}
53+
54+
public override void Update(GameTime gameTime) {
55+
base.Update(gameTime);
56+
57+
do {
58+
FrameIndex++;
59+
} while (
60+
(!Frame.HasInput && FrameIndex < Data.Frames.Count) // Skip any frames not containing the input chunk.
61+
);
62+
63+
if (Data == null || FrameIndex >= Data.Frames.Count)
64+
Remove();
65+
}
66+
67+
public void Remove() {
68+
Everest.Events.Input.OnInitialize -= HookInput;
69+
Input.Initialize();
70+
Logger.Log("ghost", "GhostReplayer returned input.");
71+
Game.Components.Remove(this);
72+
}
73+
74+
}
75+
}

GhostMod/GhostMod.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,15 @@
6565
</ItemGroup>
6666
<ItemGroup>
6767
<Compile Include="GhostName.cs" />
68+
<Compile Include="GhostInputReplayer.cs" />
6869
<Compile Include="GhostRecorder.cs" />
6970
<Compile Include="GhostData.cs" />
7071
<Compile Include="Ghost.cs" />
7172
<Compile Include="GhostFrame.cs" />
7273
<Compile Include="GhostModuleSettings.cs" />
7374
<Compile Include="GhostModule.cs" />
7475
<Compile Include="Properties\AssemblyInfo.cs" />
76+
<Compile Include="GhostInputNodes.cs" />
7577
</ItemGroup>
7678
<ItemGroup>
7779
<ProjectReference Include="..\Everest\Celeste.Mod.mm\Celeste.Mod.mm.csproj">

0 commit comments

Comments
 (0)