Skip to content

Commit ce86050

Browse files
authored
Merge pull request #123 from EverestAPI/madeline_silhouette_trigger
Madeline Silhouette Trigger
2 parents 0002b9d + 0fd1dbf commit ce86050

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

Ahorn/lang/en_gb.lang

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,6 @@ placements.entities.SpringCollab2020/MultiRoomStrawberry.tooltips.winged=The str
176176
placements.entities.SpringCollab2020/MultiRoomStrawberry.tooltips.checkpointID=Manually determine what checkpoint section strawberries are visually grouped up in, showing up on the start menu during gameplay and level select. Overrides Everest's automatic berry IDs. (Default= -1)
177177
placements.entities.SpringCollab2020/MultiRoomStrawberry.tooltips.order=Manually determine what order strawberries are visually placed in on the start menu during gameplay and level select. Overrides Everest's automatic berry IDs. (Default= -1)
178178
placements.entities.SpringCollab2020/MultiRoomStrawberry.tooltips.moon=Makes the strawberry render as a space berry.\nDoes not work with wings or nodes in the base game.
179+
180+
# Madeline Silhouette Trigger
181+
placements.triggers.SpringCollab2020/MadelineSilhouetteTrigger.tooltips.enable=If checked, the trigger will turn Madeline into a silhouette. If unchecked, it will turn Madeline back to normal.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module SpringCollab2020MadelineSilhouetteTrigger
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Trigger "SpringCollab2020/MadelineSilhouetteTrigger" MadelineSilhouetteTrigger(x::Integer, y::Integer,
6+
width::Integer=Maple.defaultTriggerWidth, height::Integer=Maple.defaultTriggerHeight, enable::Bool=true)
7+
8+
const placements = Ahorn.PlacementDict(
9+
"Madeline Silhouette (Spring Collab 2020)" => Ahorn.EntityPlacement(
10+
MadelineSilhouetteTrigger,
11+
"rectangle"
12+
)
13+
)
14+
15+
end

SpringCollab2020Module.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public override void Load() {
2828
FlagTouchSwitch.Load();
2929
DisableIcePhysicsTrigger.Load();
3030
MultiRoomStrawberrySeed.Load();
31+
MadelineSilhouetteTrigger.Load();
3132
}
3233

3334
public override void LoadContent(bool firstLoad) {
@@ -50,6 +51,7 @@ public override void Unload() {
5051
FlagTouchSwitch.Unload();
5152
DisableIcePhysicsTrigger.Unload();
5253
MultiRoomStrawberrySeed.Unload();
54+
MadelineSilhouetteTrigger.Unload();
5355
}
5456

5557
public override void PrepareMapDataProcessors(MapDataFixup context) {

SpringCollab2020Session.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ public class MultiRoomStrawberrySeedInfo {
1313
public bool IcePhysicsDisabled { get; set; } = false;
1414

1515
public List<MultiRoomStrawberrySeedInfo> CollectedMultiRoomStrawberrySeeds { get; set; } = new List<MultiRoomStrawberrySeedInfo>();
16+
17+
public bool MadelineIsSilhouette { get; set; } = false;
1618
}
1719
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using Celeste.Mod.Entities;
2+
using Microsoft.Xna.Framework;
3+
using Mono.Cecil.Cil;
4+
using Monocle;
5+
using MonoMod.Cil;
6+
using System;
7+
8+
namespace Celeste.Mod.SpringCollab2020.Triggers {
9+
[CustomEntity("SpringCollab2020/MadelineSilhouetteTrigger")]
10+
class MadelineSilhouetteTrigger : Trigger {
11+
public static void Load() {
12+
On.Celeste.Player.Added += onPlayerAdded;
13+
IL.Celeste.Player.Render += patchPlayerRender;
14+
On.Celeste.Player.ResetSprite += onPlayerResetSprite;
15+
}
16+
17+
public static void Unload() {
18+
On.Celeste.Player.Added -= onPlayerAdded;
19+
IL.Celeste.Player.Render -= patchPlayerRender;
20+
On.Celeste.Player.ResetSprite -= onPlayerResetSprite;
21+
}
22+
23+
private static void onPlayerAdded(On.Celeste.Player.orig_Added orig, Player self, Scene scene) {
24+
if (SpringCollab2020Module.Instance.Session.MadelineIsSilhouette) {
25+
refreshPlayerSpriteMode(self, true);
26+
}
27+
28+
orig(self, scene);
29+
}
30+
31+
private static void patchPlayerRender(ILContext il) {
32+
ILCursor cursor = new ILCursor(il);
33+
34+
// jump to the usage of the Red color
35+
if (cursor.TryGotoNext(instr => instr.MatchCall<Color>("get_Red"))) {
36+
Logger.Log("SpringCollab2020/MadelineSilhouetteTrigger", $"Patching silhouette hair color at {cursor.Index} in IL code for Player.Render()");
37+
38+
// when Madeline blinks red, make the hair blink red.
39+
cursor.Emit(OpCodes.Ldarg_0);
40+
cursor.EmitDelegate<Action<Player>>(player => player.Hair.Color = Color.Red);
41+
}
42+
43+
// jump to the usage of the White color
44+
if (cursor.TryGotoNext(instr => instr.MatchCall<Color>("get_White"))) {
45+
Logger.Log("SpringCollab2020/MadelineSilhouetteTrigger", $"Patching silhouette color at {cursor.Index} in IL code for Player.Render()");
46+
47+
// instead of calling Color.White, call getMadelineColor just below.
48+
cursor.Emit(OpCodes.Ldarg_0);
49+
cursor.Next.Operand = typeof(MadelineSilhouetteTrigger).GetMethod("GetMadelineColor");
50+
}
51+
}
52+
53+
public static Color GetMadelineColor(Player player) {
54+
if (SpringCollab2020Module.Instance.Session.MadelineIsSilhouette) {
55+
return player.Hair.Color;
56+
} else {
57+
return Color.White;
58+
}
59+
}
60+
61+
private static void refreshPlayerSpriteMode(Player player, bool enableSilhouette) {
62+
PlayerSpriteMode targetSpriteMode;
63+
if (enableSilhouette) {
64+
targetSpriteMode = PlayerSpriteMode.Playback;
65+
} else {
66+
targetSpriteMode = SaveData.Instance.Assists.PlayAsBadeline ? PlayerSpriteMode.MadelineAsBadeline : player.DefaultSpriteMode;
67+
}
68+
69+
if (player.Active) {
70+
player.ResetSpriteNextFrame(targetSpriteMode);
71+
} else {
72+
player.ResetSprite(targetSpriteMode);
73+
}
74+
}
75+
76+
private static void onPlayerResetSprite(On.Celeste.Player.orig_ResetSprite orig, Player self, PlayerSpriteMode mode) {
77+
// filter all calls to ResetSprite when MadelineIsSilhouette is enabled, only the ones with Playback will go through.
78+
// this prevents Madeline from turning back into normal when the Other Self variant is toggled.
79+
if (!SpringCollab2020Module.Instance.Session.MadelineIsSilhouette || mode == PlayerSpriteMode.Playback) {
80+
orig(self, mode);
81+
}
82+
}
83+
84+
85+
private bool enable;
86+
87+
public MadelineSilhouetteTrigger(EntityData data, Vector2 offset) : base(data, offset) {
88+
enable = data.Bool("enable", true);
89+
}
90+
91+
public override void OnEnter(Player player) {
92+
base.OnEnter(player);
93+
94+
bool oldValue = SpringCollab2020Module.Instance.Session.MadelineIsSilhouette;
95+
SpringCollab2020Module.Instance.Session.MadelineIsSilhouette = enable;
96+
97+
// if the value changed...
98+
if (oldValue != enable) {
99+
// switch modes right now. this uses the same way as turning the "Other Self" variant on.
100+
refreshPlayerSpriteMode(player, enable);
101+
}
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)