|
1 | | -using Microsoft.Xna.Framework; |
| 1 | +using Microsoft.Xna.Framework; |
| 2 | +using Monocle; |
2 | 3 |
|
3 | 4 | namespace Celeste.Mod.Entities { |
| 5 | + [Tracked] |
4 | 6 | [CustomEntity("everest/dialogTrigger", "dialog/dialogtrigger", "cavern/dialogtrigger")] |
5 | 7 | public class DialogCutsceneTrigger : Trigger { |
6 | 8 |
|
7 | 9 | private string dialogEntry; |
8 | 10 | private bool triggered; |
9 | 11 | private EntityID id; |
10 | | - private bool onlyOnce; |
| 12 | + private bool noRespawnAfterUse; |
| 13 | + private bool triggerOnlyOnce; |
| 14 | + private bool ignoreIntroState; |
11 | 15 | private bool endLevel; |
12 | 16 | private int deathCount; |
13 | 17 |
|
14 | 18 | public DialogCutsceneTrigger(EntityData data, Vector2 offset, EntityID entId) |
15 | 19 | : base(data, offset) { |
16 | 20 | dialogEntry = data.Attr("dialogId"); |
17 | | - onlyOnce = data.Bool("onlyOnce", true); |
| 21 | + noRespawnAfterUse = data.Bool("onlyOnce", true); // don't rename the EntityData name for backwards compat |
| 22 | + triggerOnlyOnce = data.Bool("triggerOnlyOnce", true); |
| 23 | + ignoreIntroState = data.Bool("ignoreIntroState", false); |
18 | 24 | endLevel = data.Bool("endLevel", false); |
19 | 25 | deathCount = data.Int("deathCount", -1); |
20 | 26 | triggered = false; |
21 | 27 | id = entId; |
22 | 28 | } |
23 | 29 |
|
| 30 | + // do not remove OnEnter! doing so will break maps that rely on third-party triggers to start dialog cutscenes. |
| 31 | + // vanilla naturally calls OnEnter and OnStay in the same frame when entering the trigger, |
| 32 | + // which would mean that we don't need the OnEnter method. |
| 33 | + // however, sj's "in filtration" uses a Trigger Trigger (CrystallineHelper) to start a dialog cutscene; |
| 34 | + // it only calls OnEnter and not OnStay, so removing OnEnter will make the dialog not appear and cause a tas desync! |
24 | 35 | public override void OnEnter(Player player) { |
25 | | - if (triggered || (Scene as Level).Session.GetFlag("DoNotLoad" + id) || |
26 | | - (deathCount >= 0 && SceneAs<Level>().Session.DeathsInCurrentLevel != deathCount)) { |
| 36 | + TriggerCutscene(player); |
| 37 | + } |
| 38 | + |
| 39 | + public override void OnStay(Player player) { |
| 40 | + TriggerCutscene(player); |
| 41 | + } |
| 42 | + |
| 43 | + public override void OnLeave(Player player) { |
| 44 | + if (!triggerOnlyOnce) |
| 45 | + triggered = false; |
| 46 | + } |
27 | 47 |
|
| 48 | + private void TriggerCutscene(Player player) { |
| 49 | + if (Scene is not Level level) |
| 50 | + return; |
| 51 | + |
| 52 | + if (triggered) |
| 53 | + return; |
| 54 | + |
| 55 | + if (deathCount >= 0 && level.Session.DeathsInCurrentLevel != deathCount) |
| 56 | + return; |
| 57 | + |
| 58 | + if (ignoreIntroState && ((patch_Player) player).IsIntroState) |
| 59 | + return; |
| 60 | + |
| 61 | + // don't activate if some dialog is already in progress |
| 62 | + if (level.Tracker.GetEntity<Textbox>() is not null) |
28 | 63 | return; |
29 | | - } |
30 | 64 |
|
31 | 65 | triggered = true; |
32 | 66 |
|
33 | 67 | Scene.Add(new DialogCutscene(dialogEntry, player, endLevel)); |
34 | 68 |
|
35 | | - if (onlyOnce) |
36 | | - (Scene as Level).Session.SetFlag("DoNotLoad" + id, true); // Sets flag to not load |
| 69 | + if (noRespawnAfterUse) { |
| 70 | + // this flag is unused in vanilla and Everest, but mods may still make use of it, |
| 71 | + // so it remains here for backwards compatibility |
| 72 | + level.Session.SetFlag("DoNotLoad" + id, true); |
| 73 | + level.Session.DoNotLoad.Add(id); |
| 74 | + } |
37 | 75 | } |
38 | | - |
39 | 76 | } |
40 | 77 | } |
0 commit comments