init mod
This commit is contained in:
commit
7a088b25a9
58 files changed
+6053
No files matched your search
@@ -0,0 +1,571 @@
|
||||
namespace Cthangover.Mods.ScenarioStudio
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Cthangover.Core.Bootstrap;
|
||||
using Cthangover.Core.Events;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Mods.Resolvers;
|
||||
using Cthangover.Core.Quests;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.Utils;
|
||||
using Cthangover.Mods.Dialog;
|
||||
using Cthangover.Mods.Dialog.Executable;
|
||||
using Godot;
|
||||
|
||||
/// <summary>
|
||||
/// The studio's connection to the running game, parked at <c>/root</c> for
|
||||
/// the whole session (the dock comes and goes, this stays). It installs
|
||||
/// the <see cref="StudioDebugger"/> into the dialog runtime, keeps the
|
||||
/// <see cref="StudioJournal"/> fed from the event bus, owns the edit
|
||||
/// <see cref="StudioLayer"/>, the per-scenario <see cref="StudioWorkingCopy"/>s
|
||||
/// and the rollback <see cref="StudioSnapshot"/> of the running scenario, and
|
||||
/// performs every action of the dock: apply edits, the restart modes, run a
|
||||
/// scene candidate, go to a scene, create scenes and scenarios, change
|
||||
/// state values, export the layer.
|
||||
/// <para/>
|
||||
/// <see cref="Version"/> grows on every change the dock may want to show;
|
||||
/// the dock polls it instead of subscribing, so a burst of dialog actions
|
||||
/// costs one redraw.
|
||||
/// </summary>
|
||||
public partial class StudioLive : Node
|
||||
{
|
||||
private const string ChainGroup = "main_event_chain";
|
||||
private static readonly Regex ModIdPattern = new("^[a-z][a-z0-9_]*$");
|
||||
|
||||
private readonly Dictionary<string, StudioWorkingCopy> _copies = new();
|
||||
private StudioSnapshot _snapshot;
|
||||
private StudioSnapshot _checkpoint;
|
||||
|
||||
public static StudioLive Instance { get; private set; }
|
||||
|
||||
public StudioDebugger Debugger { get; } = new();
|
||||
public StudioJournal Journal { get; } = new();
|
||||
public StudioLayer Layer { get; }
|
||||
|
||||
/// <summary>Grows on every change worth redrawing.</summary>
|
||||
public int Version { get; private set; }
|
||||
|
||||
public StudioLive(StudioLayer layer)
|
||||
{
|
||||
Name = "ScenarioStudioLive";
|
||||
Layer = layer;
|
||||
}
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
Instance = this;
|
||||
DialogRuntime.Debugger = Debugger;
|
||||
Debugger.ScenarioStarted += OnScenarioStarted;
|
||||
Debugger.Held += OnHeld;
|
||||
Debugger.Changed += Touch;
|
||||
ModEventBus.Subscribe<SceneEnteredEvent>(OnSceneEntered, priority: 50);
|
||||
ModEventBus.Subscribe<SceneComposedEvent>(OnSceneComposed);
|
||||
ModEventBus.Subscribe<DialogEndedEvent>(OnDialogEnded);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (DialogRuntime.Debugger == Debugger)
|
||||
DialogRuntime.Debugger = null;
|
||||
ModEventBus.Unsubscribe<SceneEnteredEvent>(OnSceneEntered);
|
||||
ModEventBus.Unsubscribe<SceneComposedEvent>(OnSceneComposed);
|
||||
ModEventBus.Unsubscribe<DialogEndedEvent>(OnDialogEnded);
|
||||
if (Instance == this)
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
// ── what is running ────────────────────────────────────────
|
||||
|
||||
public DialogBox Box => SceneContextNode.FindNode<DialogBox>("DialogBox");
|
||||
|
||||
public static string SceneName => SceneManager.GetCurrentSceneName();
|
||||
|
||||
/// <summary>The scenario in the dialog box, or the last one the debugger saw while it still exists.</summary>
|
||||
public ScenarioEvent CurrentEvent
|
||||
{
|
||||
get
|
||||
{
|
||||
var box = Box;
|
||||
if (box != null && box.IsRunning && box.Locker is ScenarioEvent running)
|
||||
return running;
|
||||
var last = Debugger.Event;
|
||||
return last != null && IsInstanceValid(last) ? last : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>True while <see cref="CurrentEvent"/> is the dialog in the box.</summary>
|
||||
public bool IsCurrentRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
var box = Box;
|
||||
return box != null && box.IsRunning && box.Locker == CurrentEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Scenario line of the action the running dialog stands on; 0 when nothing runs.</summary>
|
||||
public int CurrentLine()
|
||||
{
|
||||
if (!IsCurrentRunning)
|
||||
return 0;
|
||||
|
||||
var runtime = Box.Runtime;
|
||||
var queue = runtime.Dialog?.Queue;
|
||||
var index = runtime.Index;
|
||||
return queue != null && index >= 0 && index < queue.Count ? queue[index]?.SourceLine ?? 0 : 0;
|
||||
}
|
||||
|
||||
/// <summary>True when the running dialog has an action built from scenario line <paramref name="line"/>, so it can run next.</summary>
|
||||
public bool CanRunFrom(int line)
|
||||
{
|
||||
var queue = IsCurrentRunning ? Box.Runtime.Dialog?.Queue : null;
|
||||
return line > 0 && queue != null && StudioDebugger.IndexOf(queue, line) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>Makes scenario line <paramref name="line"/> the next one to run (<see cref="StudioDebugger.SetNext"/>) and logs it.</summary>
|
||||
public bool SetNextLine(int line)
|
||||
{
|
||||
if (!Debugger.SetNext(Box, line))
|
||||
return false;
|
||||
Journal.Add(StudioLogCategory.Stop, new StudioText("studio/log_text_next_line", line));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Current text of a scenario event's file (edits applied so far), or its built-in text.</summary>
|
||||
public static string SourceText(ScenarioEvent evt)
|
||||
{
|
||||
if (evt == null)
|
||||
return null;
|
||||
if (!string.IsNullOrEmpty(evt.SourceModId))
|
||||
return ScenarioDefs.ReadText(evt.SourceModId, evt.SourcePath) ?? evt.ScenarioText;
|
||||
return evt.ScenarioText;
|
||||
}
|
||||
|
||||
/// <summary>The mod (or layer) a scenario file belongs to and how the studio may touch it.</summary>
|
||||
public StudioModScope ScopeOf(string modId, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(modId))
|
||||
return new StudioModScope("inline", StudioScopeKind.ReadOnly);
|
||||
if (modId == StudioLayer.Id)
|
||||
return new StudioModScope(modId, StudioScopeKind.Layer);
|
||||
return string.IsNullOrEmpty(ModManager.Instance.GetFileSystemPath(modId, path))
|
||||
? new StudioModScope(modId, StudioScopeKind.Overlay)
|
||||
: new StudioModScope(modId, StudioScopeKind.Own);
|
||||
}
|
||||
|
||||
/// <summary>Where an edit of a file in <paramref name="fileScope"/> is written.</summary>
|
||||
public StudioModScope EditScopeFor(StudioModScope fileScope)
|
||||
{
|
||||
if (fileScope.Kind == StudioScopeKind.Own && !Layer.ForceLayer)
|
||||
return fileScope;
|
||||
return new StudioModScope(StudioLayer.Id, StudioScopeKind.Layer);
|
||||
}
|
||||
|
||||
// ── edits ──────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Pending edits of a scenario, or null.</summary>
|
||||
public StudioWorkingCopy Copy(string scenarioName) =>
|
||||
scenarioName != null && _copies.TryGetValue(scenarioName, out var copy) ? copy : null;
|
||||
|
||||
/// <summary>Pending edits of the current scenario, created from its file on first use; null without a scenario.</summary>
|
||||
public StudioWorkingCopy EnsureCopy()
|
||||
{
|
||||
var evt = CurrentEvent;
|
||||
if (evt == null)
|
||||
return null;
|
||||
if (!_copies.TryGetValue(evt.ID, out var copy))
|
||||
_copies[evt.ID] = copy = new StudioWorkingCopy(evt.ID, SourceText(evt));
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>Inserts lines before 0-based line <paramref name="index"/> of the current scenario.</summary>
|
||||
public void Insert(int index, params string[] lines)
|
||||
{
|
||||
EnsureCopy()?.Insert(index, lines);
|
||||
Touch();
|
||||
}
|
||||
|
||||
public void ReplaceLine(int index, string line)
|
||||
{
|
||||
EnsureCopy()?.Replace(index, line);
|
||||
Touch();
|
||||
}
|
||||
|
||||
public void ReplaceText(string text)
|
||||
{
|
||||
EnsureCopy()?.ReplaceAll(text);
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>Points the answer at 0-based line <paramref name="index"/> to a scene (a label block leading there is added).</summary>
|
||||
public void TargetScene(int index, string scene)
|
||||
{
|
||||
var copy = EnsureCopy();
|
||||
if (copy == null)
|
||||
return;
|
||||
var label = StudioScriptEdits.EnsureSceneExit(copy, scene);
|
||||
copy.Replace(index, StudioScriptEdits.WithTarget(copy.Line(index), label));
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>Points the answer at 0-based line <paramref name="index"/> to a label of the scenario.</summary>
|
||||
public void TargetLabel(int index, string label)
|
||||
{
|
||||
var copy = EnsureCopy();
|
||||
if (copy == null)
|
||||
return;
|
||||
copy.Replace(index, StudioScriptEdits.WithTarget(copy.Line(index), label));
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>Points the answer at 0-based line <paramref name="index"/> to the end of the scenario.</summary>
|
||||
public void TargetEnd(int index)
|
||||
{
|
||||
var copy = EnsureCopy();
|
||||
if (copy == null)
|
||||
return;
|
||||
var label = StudioScriptEdits.EnsureEnd(copy);
|
||||
copy.Replace(index, StudioScriptEdits.WithTarget(copy.Line(index), label));
|
||||
Touch();
|
||||
}
|
||||
|
||||
public void Discard()
|
||||
{
|
||||
var evt = CurrentEvent;
|
||||
if (evt != null && _copies.Remove(evt.ID))
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the pending edits of the current scenario — into its folder mod,
|
||||
/// or into the studio layer for zip mods, layer files and forced layer —
|
||||
/// and replays the scenario from its start with the game state rolled
|
||||
/// back. Returns the scope written to, or null when there was nothing.
|
||||
/// </summary>
|
||||
public StudioModScope Apply()
|
||||
{
|
||||
var evt = CurrentEvent;
|
||||
var copy = Copy(evt?.ID);
|
||||
if (copy == null || copy.PendingCount == 0)
|
||||
return null;
|
||||
|
||||
if (string.IsNullOrEmpty(evt.SourceModId))
|
||||
{
|
||||
evt.ScenarioText = copy.Text;
|
||||
_copies.Remove(evt.ID);
|
||||
Restart(StudioRestartMode.Scenario);
|
||||
return ScopeOf(null, null);
|
||||
}
|
||||
|
||||
var target = EditScopeFor(ScopeOf(evt.SourceModId, evt.SourcePath));
|
||||
var pending = copy.PendingCount;
|
||||
if (target.Kind == StudioScopeKind.Layer)
|
||||
Layer.Write(evt.SourcePath, copy.Text);
|
||||
else
|
||||
File.WriteAllText(ModManager.Instance.GetFileSystemPath(evt.SourceModId, evt.SourcePath), copy.Text);
|
||||
|
||||
_copies.Remove(evt.ID);
|
||||
StudioLayer.RefreshContent();
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_applied", evt.ID, pending, target.ModId));
|
||||
GameLogger.Log("UI", $"Scenario studio: applied {pending} change(s) of '{evt.ID}' to '{target.ModId}'");
|
||||
Restart(StudioRestartMode.Rollback);
|
||||
return target;
|
||||
}
|
||||
|
||||
// ── restarts and navigation ────────────────────────────────
|
||||
|
||||
public void Restart(StudioRestartMode mode)
|
||||
{
|
||||
var evt = CurrentEvent;
|
||||
if (evt == null)
|
||||
return;
|
||||
|
||||
var name = evt.ID;
|
||||
switch (mode)
|
||||
{
|
||||
case StudioRestartMode.Scene:
|
||||
GoToScene(SceneName);
|
||||
return;
|
||||
case StudioRestartMode.Rollback when _snapshot != null && _snapshot.ScenarioName == name:
|
||||
StopDialog();
|
||||
if (_snapshot.Restore())
|
||||
ForgetCompleted(name);
|
||||
RunScenario(name, null, evt);
|
||||
return;
|
||||
case StudioRestartMode.FromLabel:
|
||||
var label = LabelAbove(SourceText(evt), CurrentLine());
|
||||
StopDialog();
|
||||
ForgetCompleted(name);
|
||||
RunScenario(name, label, evt);
|
||||
return;
|
||||
default:
|
||||
StopDialog();
|
||||
ForgetCompleted(name);
|
||||
RunScenario(name, null, evt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Starts scenario <paramref name="name"/> of the current scene now, stopping the running dialog.</summary>
|
||||
public bool RunNow(string name)
|
||||
{
|
||||
StopDialog();
|
||||
ForgetCompleted(name);
|
||||
return RunScenario(name, null, null);
|
||||
}
|
||||
|
||||
/// <summary>Removes <paramref name="name"/> from the one-run completion history.</summary>
|
||||
public void ForgetCompleted(string name)
|
||||
{
|
||||
var runtime = GameData.Instance?.Runtime;
|
||||
if (runtime == null)
|
||||
return;
|
||||
runtime.CompletedScenarioIds.Remove(name);
|
||||
runtime.LoadedCompletedScenarioIds?.Remove(name);
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>Leaves the running dialog and enters <paramref name="scene"/> (a debugging teleport).</summary>
|
||||
public void GoToScene(string scene)
|
||||
{
|
||||
if (string.IsNullOrEmpty(scene))
|
||||
return;
|
||||
StopDialog();
|
||||
Debugger.Reset();
|
||||
GetNodeOrNull<SceneManager>("/root/SceneManager")?.SwitchScene(scene);
|
||||
Touch();
|
||||
}
|
||||
|
||||
private void StopDialog()
|
||||
{
|
||||
Debugger.Continue(null);
|
||||
Box?.Abort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a fresh event for scenario <paramref name="name"/> from the
|
||||
/// current definitions (edits included), puts it on the scene's chain in
|
||||
/// place of <paramref name="replaced"/> and starts it at once, so other
|
||||
/// queued scenarios do not run first. A scenario without a definition
|
||||
/// (test play, inline) is rebuilt from the replaced event's text.
|
||||
/// </summary>
|
||||
private bool RunScenario(string name, string startLabel, ScenarioEvent replaced)
|
||||
{
|
||||
var chain = GetTree().GetFirstNodeInGroup(ChainGroup) as ExecutableMainEventChain;
|
||||
var factory = AppServices.Current.SceneEvents.ScenarioEventFactory;
|
||||
if (chain == null || factory == null)
|
||||
{
|
||||
GameLogger.Log("UI", $"Scenario studio: cannot run '{name}' — no event chain or scenario factory", LogLevel.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
var def = FindDefinition(name);
|
||||
ScenarioEvent evt;
|
||||
if (def != null)
|
||||
{
|
||||
var text = ScenarioDefs.ReadText(def.ModId, def.FilePath);
|
||||
evt = factory.CreateFromDefinition(text, name, def.Condition, def.LightUseTime, def.IsOneRun, def.ModId, def.FilePath) as ScenarioEvent;
|
||||
}
|
||||
else if (replaced != null && IsInstanceValid(replaced))
|
||||
{
|
||||
evt = factory.CreateFromText(replaced.ScenarioText) as ScenarioEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
GameLogger.Log("UI", $"Scenario studio: scenario '{name}' not found", LogLevel.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (evt == null)
|
||||
return false;
|
||||
|
||||
evt.StartLabel = startLabel;
|
||||
if (replaced != null && IsInstanceValid(replaced) && replaced.GetParent() == chain)
|
||||
chain.RemoveEvent(replaced);
|
||||
chain.AddEvent(evt);
|
||||
|
||||
var started = evt.RunDialog();
|
||||
GameLogger.Log("UI", $"Scenario studio: run '{name}'" + (startLabel != null ? $" from :{startLabel}" : "") + (started ? "" : " — dialog box busy"));
|
||||
Touch();
|
||||
return started;
|
||||
}
|
||||
|
||||
public static ScenarioDefinition FindDefinition(string name)
|
||||
{
|
||||
var defs = ScenarioDefs.CollectDefinitions();
|
||||
if (defs == null)
|
||||
return null;
|
||||
foreach (var list in defs.Values)
|
||||
{
|
||||
foreach (var def in list)
|
||||
{
|
||||
if (def.Name == name)
|
||||
return def;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string LabelAbove(string text, int line)
|
||||
{
|
||||
if (text == null || line <= 0)
|
||||
return null;
|
||||
var lines = text.Replace("\r\n", "\n").Split('\n');
|
||||
for (var i = Math.Min(line, lines.Length) - 1; i >= 0; i--)
|
||||
{
|
||||
var trimmed = lines[i].Trim();
|
||||
if (trimmed.StartsWith(':'))
|
||||
return trimmed.Substring(1).Trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>True when a manual checkpoint was saved in this session.</summary>
|
||||
public bool HasCheckpoint => _checkpoint != null;
|
||||
|
||||
/// <summary>Remembers the whole game state and the scene now, to come back to with <see cref="RestoreCheckpoint"/>.</summary>
|
||||
public void SaveCheckpoint()
|
||||
{
|
||||
_checkpoint = StudioSnapshot.Capture(null, SceneName);
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_checkpoint_saved", SceneName));
|
||||
Touch();
|
||||
}
|
||||
|
||||
/// <summary>Brings back the saved checkpoint: game state, then its scene entered again.</summary>
|
||||
public bool RestoreCheckpoint()
|
||||
{
|
||||
if (_checkpoint == null)
|
||||
return false;
|
||||
|
||||
StopDialog();
|
||||
Debugger.Reset();
|
||||
if (!_checkpoint.Restore())
|
||||
return false;
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_checkpoint_restored", _checkpoint.SceneName));
|
||||
GoToScene(_checkpoint.SceneName);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── new content ────────────────────────────────────────────
|
||||
|
||||
/// <summary>Creates a scene in the layer (backgrounds of the current scene) with a first scenario; returns false on a bad name.</summary>
|
||||
public bool CreateScene(string name)
|
||||
{
|
||||
name = (name ?? string.Empty).Trim().ToLowerInvariant();
|
||||
if (!ModIdPattern.IsMatch(name))
|
||||
return false;
|
||||
|
||||
var current = SceneName;
|
||||
var defs = AppServices.Current.SceneDefs.CollectAll();
|
||||
List<string> backgrounds = null;
|
||||
if (current != null && defs != null && defs.TryGetValue(current, out var list) && list.Count > 0)
|
||||
backgrounds = list[0].DefaultBackground;
|
||||
|
||||
var scenario = Layer.CreateScene(name, backgrounds, StudioScriptEdits.NewLine());
|
||||
StudioLayer.RefreshContent();
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_scene_created", name, scenario));
|
||||
Touch();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new scenario for the current scene in the layer; returns its name.</summary>
|
||||
public string CreateScenario()
|
||||
{
|
||||
var scene = SceneName;
|
||||
if (string.IsNullOrEmpty(scene))
|
||||
return null;
|
||||
var name = Layer.CreateScenario(scene, StudioScriptEdits.NewLine());
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_scenario_created", name, scene));
|
||||
Touch();
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>Exports the layer as mod <paramref name="modId"/>; null on a bad id.</summary>
|
||||
public string Export(string modId)
|
||||
{
|
||||
modId = (modId ?? string.Empty).Trim().ToLowerInvariant();
|
||||
if (!ModIdPattern.IsMatch(modId))
|
||||
return null;
|
||||
var path = Layer.Export(modId);
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_exported", modId));
|
||||
Touch();
|
||||
return path;
|
||||
}
|
||||
|
||||
// ── state ──────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Writes a value edited in the "State" tab back into the game.</summary>
|
||||
public void EditState(StudioStateEntry entry, string value)
|
||||
{
|
||||
switch (entry.Editor)
|
||||
{
|
||||
case StudioStateEditor.DialogVariable:
|
||||
Box?.Runtime.SetVariable(entry.Id, value);
|
||||
break;
|
||||
case StudioStateEditor.Inventory:
|
||||
var inventory = GameData.Instance?.Runtime.Inventory;
|
||||
if (inventory == null || !int.TryParse(value, out var count) || count < 0)
|
||||
return;
|
||||
var delta = count - inventory.CheckCount(entry.Id);
|
||||
if (delta > 0)
|
||||
inventory.Add(entry.Id, delta);
|
||||
else if (delta < 0)
|
||||
inventory.Remove(entry.Id, -delta);
|
||||
break;
|
||||
case StudioStateEditor.Quest:
|
||||
QuestServiceRegistry.Provider?.SetStatus(entry.Id, value);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Journal.Add(StudioLogCategory.Edit, new StudioText("studio/log_text_state", entry.Id, value));
|
||||
Touch();
|
||||
}
|
||||
|
||||
// ── bus and debugger callbacks ─────────────────────────────
|
||||
|
||||
private void OnScenarioStarted(ScenarioEvent evt)
|
||||
{
|
||||
try
|
||||
{
|
||||
_snapshot = StudioSnapshot.Capture(evt.ID, SceneName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_snapshot = null;
|
||||
GameLogger.Log("UI", $"Scenario studio: snapshot of '{evt.ID}' failed: {ex.Message}", LogLevel.Warning);
|
||||
}
|
||||
Journal.Add(StudioLogCategory.Scenario, new StudioText("studio/log_text_started", evt.ID));
|
||||
}
|
||||
|
||||
private void OnHeld(int line, bool breakpoint)
|
||||
{
|
||||
Journal.Add(StudioLogCategory.Stop, new StudioText(breakpoint ? "studio/log_text_breakpoint" : "studio/log_text_paused", line));
|
||||
}
|
||||
|
||||
private void OnSceneEntered(SceneEnteredEvent e)
|
||||
{
|
||||
Journal.OnSceneEntered(e);
|
||||
Touch();
|
||||
}
|
||||
|
||||
private void OnSceneComposed(SceneComposedEvent e)
|
||||
{
|
||||
Journal.OnSceneComposed(e);
|
||||
Touch();
|
||||
}
|
||||
|
||||
private void OnDialogEnded(DialogEndedEvent e)
|
||||
{
|
||||
Journal.OnDialogEnded(e);
|
||||
Touch();
|
||||
}
|
||||
|
||||
private void Touch() => Version++;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user