Skip to content

Commit

Permalink
amending this later
Browse files Browse the repository at this point in the history
  • Loading branch information
maddie480 committed Sep 13, 2024
1 parent d774b0a commit 53bcb47
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 25 deletions.
66 changes: 42 additions & 24 deletions Entities/FlagTouchSwitch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Celeste.Mod.MaxHelpingHand.Entities {
/// - inactiveColor / activeColor / finishColor: custom colors for the touch switch.
/// </summary>
[CustomEntity("MaxHelpingHand/FlagTouchSwitch")]
[Tracked]
[Tracked(inherited: true)]
public class FlagTouchSwitch : Entity {
private static FieldInfo seekerPushRadius = typeof(Seeker).GetField("pushRadius", BindingFlags.NonPublic | BindingFlags.Instance);
private static FieldInfo seekerPhysicsHitbox = typeof(Seeker).GetField("physicsHitbox", BindingFlags.NonPublic | BindingFlags.Instance);
Expand Down Expand Up @@ -90,7 +90,7 @@ private static void turnOnTouchSwitchesCollidingWith(Entity self) {

private MTexture border = GFX.Game["objects/touchswitch/container"];

private Sprite icon;
protected Sprite icon;

private int[] frames;
private bool persistent;
Expand Down Expand Up @@ -119,6 +119,8 @@ private static void turnOnTouchSwitchesCollidingWith(Entity self) {

private Level level => (Level) Scene;

protected virtual Vector2 IconPosition => Vector2.Zero;

public FlagTouchSwitch(EntityData data, Vector2 offset)
: base(data.Position + offset) {

Expand Down Expand Up @@ -152,13 +154,7 @@ public FlagTouchSwitch(EntityData data, Vector2 offset)
Color = finishColor
};

// set up collision
Collider = new Hitbox(16f, 16f, -8f, -8f);
if (data.Bool("playerCanActivate", defaultValue: true)) {
Add(new PlayerCollider(onPlayer, null, new Hitbox(30f, 30f, -15f, -15f)));
}
Add(new HoldableCollider(onHoldable, new Hitbox(20f, 20f, -10f, -10f)));
Add(new SeekerCollider(onSeeker, new Hitbox(24f, 24f, -12f, -12f)));
setUpCollision(data);

// set up the icon
string iconAttribute = data.Attr("icon", "vanilla");
Expand All @@ -175,16 +171,27 @@ public FlagTouchSwitch(EntityData data, Vector2 offset)
icon.Play("spin");
icon.Color = inactiveColor;
icon.CenterOrigin();
icon.Position = IconPosition;

Add(bloom = new BloomPoint(0f, 16f));
bloom.Alpha = 0f;
bloom.Position = IconPosition;

Add(wiggler = Wiggler.Create(0.5f, 4f, v => {
pulse = Vector2.One * (1f + v * 0.25f);
}));

Add(new VertexLight(Color.White, 0.8f, 16, 32));
Add(touchSfx = new SoundSource());
Add(new VertexLight(Color.White, 0.8f, 16, 32) { Position = IconPosition });
Add(touchSfx = new SoundSource { Position = IconPosition });
}

protected virtual void setUpCollision(EntityData data) {
Collider = new Hitbox(16f, 16f, -8f, -8f);
if (data.Bool("playerCanActivate", defaultValue: true)) {
Add(new PlayerCollider(onPlayer, null, new Hitbox(30f, 30f, -15f, -15f)));
}
Add(new HoldableCollider(onHoldable, new Hitbox(20f, 20f, -10f, -10f)));
Add(new SeekerCollider(onSeeker, new Hitbox(24f, 24f, -12f, -12f)));
}

public override void Added(Scene scene) {
Expand Down Expand Up @@ -224,15 +231,15 @@ public override void Awake(Scene scene) {
new DynData<TouchSwitch>(touchSwitch).Get<string>("flag") == flag).ToList();
}

private void onPlayer(Player player) {
protected void onPlayer(Player player) {
TurnOn();
}

private void onHoldable(Holdable h) {
protected void onHoldable(Holdable h) {
TurnOn();
}

private void onSeeker(Seeker seeker) {
protected void onSeeker(Seeker seeker) {
if (SceneAs<Level>().InsideCamera(Position, 10f)) {
TurnOn();
}
Expand All @@ -249,31 +256,38 @@ public void TurnOn() {
wiggler.Start();
for (int i = 0; i < 32; i++) {
float num = Calc.Random.NextFloat((float) Math.PI * 2f);
level.Particles.Emit(TouchSwitch.P_FireWhite, Position + Calc.AngleToVector(num, 6f), num);
level.Particles.Emit(TouchSwitch.P_FireWhite, Position + IconPosition + Calc.AngleToVector(num, 6f), num);
}
});
icon.Rate = 4f;

HandleCollectedFlagTouchSwitch(flag, inverted, persistent, level, id, allTouchSwitchesInRoom, allMovingFlagTouchSwitchesInRoom, () => doEffect(() => {
SoundEmitter.Play(completeSoundFromScene);
Add(new SoundSource(completeSoundFromSwitch));
Add(new SoundSource(completeSoundFromSwitch) { Position = IconPosition });
}));
}
}

// returns true if the entire group was completed.
internal static bool HandleCollectedFlagTouchSwitch(string flag, bool inverted, bool persistent, Level level, int id,
List<FlagTouchSwitch> allTouchSwitchesInRoom, List<TouchSwitch> allMovingFlagTouchSwitchesInRoom, Action onFinished) {
internal static bool HandleCollectedFlagTouchSwitch(
string flag,
bool inverted,
bool persistent,
Level level,
int id,
List<FlagTouchSwitch> allTouchSwitchesInRoom,
List<TouchSwitch> allMovingFlagTouchSwitchesInRoom,
Action onFinished) {

if (persistent) {
// this switch is persistent. save its activation in the session.
level.Session.SetFlag(flag + "_switch" + id, true);
}

if (MaxHelpingHandMapDataProcessor.FlagTouchSwitches[level.Session.Area.SID][(int) level.Session.Area.Mode][new KeyValuePair<string, bool>(flag, inverted)]
.All(touchSwitchID => touchSwitchID.Level == level.Session.Level || level.Session.GetFlag(flag + "_switch" + touchSwitchID.ID))
&& allTouchSwitchesInRoom.All(touchSwitch => touchSwitch.Activated || touchSwitch.isHidden())
&& allMovingFlagTouchSwitchesInRoom.All(touchSwitch => touchSwitch.Switch.Activated || MovingFlagTouchSwitch.IsHidden(touchSwitch))) {
.All(touchSwitchID => touchSwitchID.Level == level.Session.Level || level.Session.GetFlag(flag + "_switch" + touchSwitchID.ID))
&& allTouchSwitchesInRoom.All(touchSwitch => touchSwitch.Activated || touchSwitch.isHidden())
&& allMovingFlagTouchSwitchesInRoom.All(touchSwitch => touchSwitch.Switch.Activated || MovingFlagTouchSwitch.IsHidden(touchSwitch))) {

// all switches in the room are enabled or hidden, and all session flags for switches outside the room are enabled.
// so, the group is complete.
Expand Down Expand Up @@ -366,10 +380,10 @@ public override void Update() {
icon.Rate = 0.1f;
wiggler.Start();
icon.Play("idle");
level.Displacement.AddBurst(Position, 0.6f, 4f, 28f, 0.2f);
level.Displacement.AddBurst(Position + IconPosition, 0.6f, 4f, 28f, 0.2f);
}
} else if (Scene.OnInterval(0.03f) && smoke) {
Vector2 position = Position + new Vector2(0f, 1f) + Calc.AngleToVector(Calc.Random.NextAngle(), 5f);
Vector2 position = Position + IconPosition + new Vector2(0f, 1f) + Calc.AngleToVector(Calc.Random.NextAngle(), 5f);
level.ParticlesBG.Emit(P_RecoloredFire, position);
}

Expand All @@ -395,9 +409,13 @@ public override void Update() {
}

public override void Render() {
renderBorder();
base.Render();
}

protected virtual void renderBorder() {
border.DrawCentered(Position + new Vector2(0f, -1f), Color.Black);
border.DrawCentered(Position, icon.Color, pulse);
base.Render();
}

private void doEffect(Action effect) {
Expand Down
29 changes: 29 additions & 0 deletions Entities/FlagTouchSwitchWall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Celeste.Mod.Entities;
using Microsoft.Xna.Framework;
using Monocle;

namespace Celeste.Mod.MaxHelpingHand.Entities {
[CustomEntity("MaxHelpingHand/FlagTouchSwitchWall")]
public class FlagTouchSwitchWall : FlagTouchSwitch {
protected override Vector2 IconPosition => new Vector2(Width / 2, Height / 2);

public FlagTouchSwitchWall(EntityData data, Vector2 offset) : base(data, offset) {
}

protected override void setUpCollision(EntityData data) {
Collider = new Hitbox(data.Width, data.Height);

if (data.Bool("playerCanActivate", defaultValue: true)) {
Add(new PlayerCollider(onPlayer, null, new Hitbox(data.Width, data.Height)));
}

Add(new HoldableCollider(onHoldable, new Hitbox(data.Width, data.Height)));
Add(new SeekerCollider(onSeeker, new Hitbox(data.Width, data.Height)));
}

protected override void renderBorder() {
Draw.HollowRect(X - 1, Y - 1, Width + 2, Height + 2, new Color(icon.Color.R, icon.Color.G, icon.Color.B, (int) (0.7f * 255)));
Draw.Rect(X + 1, Y + 1, Width - 2, Height - 2, Color.Lerp(icon.Color, Calc.HexToColor("0a0a0a"), 0.5f) * 0.3f);
}
}
}
1 change: 0 additions & 1 deletion Loenn/entities/flagTouchSwitch.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
local drawableSprite = require("structs.drawable_sprite")
local utils = require("utils")

local touchSwitch = {}

Expand Down
77 changes: 77 additions & 0 deletions Loenn/entities/flagTouchSwitchWall.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
local drawableSprite = require("structs.drawable_sprite")
local drawableRectangle = require("structs.drawable_rectangle")

local touchSwitch = {}

touchSwitch.name = "MaxHelpingHand/FlagTouchSwitchWall"
touchSwitch.depth = 2000
touchSwitch.minimumSize = {8, 8}
touchSwitch.placements = {
{
name = "touch_switch",
data = {
width = 16,
height = 16,
flag = "flag_touch_switch",
icon = "vanilla",
animationLength = 6,
persistent = false,
inactiveColor = "5FCDE4",
activeColor = "FFFFFF",
finishColor = "F141DF",
smoke = true,
inverted = false,
allowDisable = false,
playerCanActivate = true,
hitSound = "event:/game/general/touchswitch_any",
completeSoundFromSwitch = "event:/game/general/touchswitch_last_cutoff",
completeSoundFromScene = "event:/game/general/touchswitch_last_oneshot",
hideIfFlag = ""
}
}
}

function touchSwitch.fieldOrder(entity)
local fieldOrder = {"x", "y", "width", "height", "inactiveColor", "activeColor", "finishColor", "hitSound", "completeSoundFromSwitch", "completeSoundFromScene"}

-- only include animationLength to fieldOrder if the field exists, otherwise it will appear as nil in the entity properties window
if entity.animationLength ~= nil then
table.insert(fieldOrder, "animationLength")
end

return fieldOrder
end

touchSwitch.fieldInformation = {
inactiveColor = {
fieldType = "color"
},
activeColor = {
fieldType = "color"
},
finishColor = {
fieldType = "color"
},
icon = {
options = { "vanilla", "tall", "triangle", "circle", "diamond", "double", "heart", "square", "wide", "winged", "cross", "drop", "hourglass", "split", "star", "triple" }
},
animationLength = {
fieldType = "integer"
}
}

function touchSwitch.sprite(room, entity)
local containerSprite = drawableRectangle.fromRectangle('bordered', entity.x, entity.y, entity.width, entity.height, {0.0, 0.0, 0.0, 0.3}, {1.0, 1.0, 1.0, 0.5})

local iconResource = "objects/touchswitch/icon00"
if entity.icon ~= "vanilla" then
iconResource = "objects/MaxHelpingHand/flagTouchSwitch/" .. entity.icon .."/icon00"
end

local iconSprite = drawableSprite.fromTexture(iconResource, entity)
iconSprite:setPosition(entity.x + entity.width / 2, entity.y + entity.height / 2)

return {containerSprite, iconSprite}
end

return touchSwitch
20 changes: 20 additions & 0 deletions Loenn/lang/en_gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,26 @@ entities.MaxHelpingHand/FlagTouchSwitch.attributes.name.completeSoundFromScene=C
entities.MaxHelpingHand/FlagTouchSwitch.attributes.description.hideIfFlag=If a session flag is specified here, turning it on will make the touch switch disappear, and will make other touch switches in the room ignore it for the group's completion.
entities.MaxHelpingHand/FlagTouchSwitch.attributes.description.animationLength=The length of the provided spinning animation, in frames.

# Flag Touch Switch Wall
entities.MaxHelpingHand/FlagTouchSwitchWall.placements.name.touch_switch=Flag Touch Switch Wall
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.flag=The session flag this touch switch sets. Give the same to multiple touch switches and switch gates to group them.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.icon=The name of the icon for the touch switch (relative to objects/MaxHelpingHand/flagTouchSwitch), or "vanilla" for the default one.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.persistent=If enabled, the touch switch will stay active when the player dies or changes rooms.\nThis touch switch will also set a flag when it is enabled: [flagName]_switch[entityID]
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.inactiveColor=The switch color when not triggered yet.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.activeColor=The switch color when triggered, but the group is not complete yet.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.finishColor=The switch color when the group is complete.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.smoke=Whether the touch switch emits smoke when the group is complete.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.inverted=Whether the touch switch should turn off the flag instead of turning it on. The option should match on all touch switches of the group.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.allowDisable=Whether the touch switch can be disabled once the group is complete, if the flag change is reverted. Does not work with non-persistent flag switch gates.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.playerCanActivate=Whether the player can activate the touch switch. If not, only throwables can activate it.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.hitSound=The sound played when the switch is hit by the player.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.completeSoundFromSwitch=The sound emitted by the latest switch collected when the group is complete.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.completeSoundFromScene=A global sound emitted when the group is complete. As opposed to "Complete Sound (Switch)", this sound isn't emitted by a particular switch.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.name.completeSoundFromSwitch=Complete Sound (Switch)
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.name.completeSoundFromScene=Complete Sound (Global)
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.hideIfFlag=If a session flag is specified here, turning it on will make the touch switch disappear, and will make other touch switches in the room ignore it for the group's completion.
entities.MaxHelpingHand/FlagTouchSwitchWall.attributes.description.animationLength=The length of the provided spinning animation, in frames.

# Floaty Space Block with Attached Sideways Jumpthru support
entities.MaxHelpingHand/FloatySpaceBlockWithAttachedSidewaysJumpthruSupport.placements.name.floaty_space_block=Floaty Space Block (supporting Sideways Jump Throughs)
entities.MaxHelpingHand/FloatySpaceBlockWithAttachedSidewaysJumpthruSupport.attributes.description.tiletype=Determines the visual appearance of the wall.
Expand Down
25 changes: 25 additions & 0 deletions Module/MaxHelpingHandMapDataProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,34 @@ public class MaxHelpingHandMapDataProcessor : EverestMapDataProcessor {
{
"entity:MaxHelpingHand/MovingFlagTouchSwitch", flagTouchSwitchHandler
},
{
"entity:MaxHelpingHand/FlagTouchSwitchWall", flagTouchSwitchHandler
},
{
"entity:ChroniaHelper/FlagTouchSwitch", flagTouchSwitch => {
// Take over Chronia Helper flag touch switches, after UnderDragon reached out to merge those back into Helping Hand
if (flagTouchSwitch.Attr("switch") == "touchSwitch") {
Logger.Log(LogLevel.Verbose, "MaxHelpingHand/MapDataProcessor", $"area {AreaKey.SID} / mode {AreaKey.Mode} / room {levelName} has a ChroniaHelper/FlagTouchSwitch, turning it into a MaxHelpingHand/FlagTouchSwitch");
flagTouchSwitch.Name = "MaxHelpingHand/FlagTouchSwitch";
flagTouchSwitch.SetAttr("x", flagTouchSwitch.AttrInt("x") + flagTouchSwitch.AttrInt("width") / 2);
flagTouchSwitch.SetAttr("y", flagTouchSwitch.AttrInt("y") + flagTouchSwitch.AttrInt("height") / 2);
} else {
Logger.Log(LogLevel.Verbose, "MaxHelpingHand/MapDataProcessor", $"area {AreaKey.SID} / mode {AreaKey.Mode} / room {levelName} has a ChroniaHelper/FlagTouchSwitch, turning it into a MaxHelpingHand/FlagTouchSwitchWall");
flagTouchSwitch.Name = "MaxHelpingHand/FlagTouchSwitchWall";
}
flagTouchSwitchHandler(flagTouchSwitch);
}
},
{
"entity:MaxHelpingHand/FlagSwitchGate", flagSwitchGateHandler
},
{
"entity:ChroniaHelper/FlagSwitchGate", flagSwitchGate => {
Logger.Log(LogLevel.Verbose, "MaxHelpingHand/MapDataProcessor", $"area {AreaKey.SID} / mode {AreaKey.Mode} / room {levelName} has a ChroniaHelper/FlagSwitchGate, turning it into a MaxHelpingHand/FlagSwitchGate");
flagSwitchGate.Name = "MaxHelpingHand/FlagSwitchGate";
flagSwitchGateHandler(flagSwitchGate);
}
},
{
"entity:CommunalHelper/MaxHelpingHand/DreamFlagSwitchGate", flagSwitchGateHandler
},
Expand Down

0 comments on commit 53bcb47

Please sign in to comment.