From 52cc2c3cefcc7839e187636c2fe92f4b2ae5b58a Mon Sep 17 00:00:00 2001 From: Maddie <52103563+maddie480@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:10:13 +0200 Subject: [PATCH 1/2] Reapply "Merge pull request #999 from SnipUndercover/dialog-cutscene-changes" This reverts commit dcc400b1724b4762ca92b50e8d274f66ddeafa0c. --- .../Mod/Entities/DialogCutsceneTrigger.cs | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs b/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs index 584722d8b..adf1730bb 100644 --- a/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs +++ b/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs @@ -1,40 +1,77 @@ -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; +using Monocle; namespace Celeste.Mod.Entities { + [Tracked] [CustomEntity("everest/dialogTrigger", "dialog/dialogtrigger", "cavern/dialogtrigger")] public class DialogCutsceneTrigger : Trigger { private string dialogEntry; private bool triggered; private EntityID id; - private bool onlyOnce; + private bool noRespawnAfterUse; + private bool triggerOnlyOnce; + private bool ignoreIntroState; private bool endLevel; private int deathCount; public DialogCutsceneTrigger(EntityData data, Vector2 offset, EntityID entId) : base(data, offset) { dialogEntry = data.Attr("dialogId"); - onlyOnce = data.Bool("onlyOnce", true); + noRespawnAfterUse = data.Bool("onlyOnce", true); // don't rename the EntityData name for backwards compat + triggerOnlyOnce = data.Bool("triggerOnlyOnce", true); + ignoreIntroState = data.Bool("ignoreIntroState", false); endLevel = data.Bool("endLevel", false); deathCount = data.Int("deathCount", -1); triggered = false; id = entId; } + // do not remove OnEnter! doing so will break maps that rely on third-party triggers to start dialog cutscenes. + // vanilla naturally calls OnEnter and OnStay in the same frame when entering the trigger, + // which would mean that we don't need the OnEnter method. + // however, sj's "in filtration" uses a Trigger Trigger (CrystallineHelper) to start a dialog cutscene; + // it only calls OnEnter and not OnStay, so removing OnEnter will make the dialog not appear and cause a tas desync! public override void OnEnter(Player player) { - if (triggered || (Scene as Level).Session.GetFlag("DoNotLoad" + id) || - (deathCount >= 0 && SceneAs().Session.DeathsInCurrentLevel != deathCount)) { + TriggerCutscene(player); + } + + public override void OnStay(Player player) { + TriggerCutscene(player); + } + + public override void OnLeave(Player player) { + if (!triggerOnlyOnce) + triggered = false; + } + private void TriggerCutscene(Player player) { + if (Scene is not Level level) + return; + + if (triggered) + return; + + if (deathCount >= 0 && level.Session.DeathsInCurrentLevel != deathCount) + return; + + if (ignoreIntroState && ((patch_Player) player).IsIntroState) + return; + + // don't activate if some dialog is already in progress + if (level.Tracker.GetEntity() is not null) return; - } triggered = true; Scene.Add(new DialogCutscene(dialogEntry, player, endLevel)); - if (onlyOnce) - (Scene as Level).Session.SetFlag("DoNotLoad" + id, true); // Sets flag to not load + if (noRespawnAfterUse) { + // this flag is unused in vanilla and Everest, but mods may still make use of it, + // so it remains here for backwards compatibility + level.Session.SetFlag("DoNotLoad" + id, true); + level.Session.DoNotLoad.Add(id); + } } - } } From d0ce85a251a10c5910412d24f083577e6146342b Mon Sep 17 00:00:00 2001 From: Maddie <52103563+maddie480@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:44:46 +0200 Subject: [PATCH 2/2] Allow different cutscenes to play at the same time --- Celeste.Mod.mm/Mod/Entities/DialogCutscene.cs | 8 ++++++++ Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Celeste.Mod.mm/Mod/Entities/DialogCutscene.cs b/Celeste.Mod.mm/Mod/Entities/DialogCutscene.cs index 3f716d550..039ccbd1b 100644 --- a/Celeste.Mod.mm/Mod/Entities/DialogCutscene.cs +++ b/Celeste.Mod.mm/Mod/Entities/DialogCutscene.cs @@ -2,6 +2,7 @@ using System.Collections; namespace Celeste.Mod.Entities { + [Tracked] public class DialogCutscene : CutsceneEntity { private Player player; @@ -45,5 +46,12 @@ public override void OnEnd(Level level) { } } + public static bool IsInProgress(string dialogID) { + foreach (DialogCutscene dialogCutscene in Engine.Scene.Tracker.GetEntities()) { + if (dialogCutscene.dialogID == dialogID) return true; + } + return false; + } + } } diff --git a/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs b/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs index adf1730bb..37d801006 100644 --- a/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs +++ b/Celeste.Mod.mm/Mod/Entities/DialogCutsceneTrigger.cs @@ -58,8 +58,8 @@ private void TriggerCutscene(Player player) { if (ignoreIntroState && ((patch_Player) player).IsIntroState) return; - // don't activate if some dialog is already in progress - if (level.Tracker.GetEntity() is not null) + // don't activate if the same dialog is already in progress + if (DialogCutscene.IsInProgress(dialogEntry)) return; triggered = true;