using System.Collections; using UnityEngine; using Yarn.Unity; namespace NightclubArcadia.Skills { /// /// Static Yarn adapter. No MonoBehaviour, no check math — translation only. /// /// Commands: /// <<check skillId band-or-int [situationalMod]>> /// <<skill_mod sourceKey skillId delta>> — set a state/situational mod /// <<clear_skill_mods sourceKey>> — clear one source key /// Function (kept as skill_rank; skeleton's "rank" would collide with future helpers): /// skill_rank("skillId") /// /// Modifier convention: source key "scene" is the situational bucket and must be /// cleared on scene exit; any other key (drunk, injured, …) is a state bucket. /// Both feed SkillStateModifiers — three modifier sources total (rank / check arg / /// state), not a fourth dictionary. /// public static class YarnSkillCommands { [YarnCommand("check")] public static IEnumerator Check(string skillId, string difficulty, int modifier = 0) { var runtime = SkillRuntime.Instance; if (runtime == null || runtime.Checks == null || runtime.DialogueRunner == null) { Debug.LogError("[YarnSkillCommands] <> called but SkillRuntime is not ready."); var fallbackRunner = runtime != null ? runtime.DialogueRunner : null; if (fallbackRunner == null) { fallbackRunner = Object.FindFirstObjectByType(); } WriteFailedResult(fallbackRunner, skillId, difficulty); yield break; } if (!DcBands.TryParse(difficulty, out var dc)) { Debug.LogError($"[YarnSkillCommands] <>: unknown difficulty; treating as Routine (11)."); dc = (int)DcBand.Routine; } if (runtime.Database == null || !runtime.Database.TryGet(skillId, out _)) { Debug.LogError($"[YarnSkillCommands] <>: no skill '{skillId}'."); } var result = runtime.Checks.Resolve(skillId, dc, modifier); WriteResult(runtime.DialogueRunner.VariableStorage, result); if (runtime.ResultView == null) { Debug.LogWarning("[YarnSkillCommands] SkillCheckResultView is not assigned; skipping roll banner."); yield break; } yield return runtime.StartCoroutine(runtime.ResultView.Show(result)); } [YarnCommand("skill_mod")] public static void SkillMod(string sourceKey, string skillId, int delta) { var runtime = SkillRuntime.Instance; if (runtime == null || runtime.States == null) { Debug.LogError("[YarnSkillCommands] <> called but SkillRuntime is not ready."); return; } if (string.IsNullOrEmpty(sourceKey) || string.IsNullOrEmpty(skillId)) { Debug.LogError("[YarnSkillCommands] <> requires sourceKey and skillId."); return; } runtime.States.Set(sourceKey, skillId, delta); } [YarnCommand("clear_skill_mods")] public static void ClearSkillMods(string sourceKey) { var runtime = SkillRuntime.Instance; if (runtime == null || runtime.States == null) { Debug.LogError("[YarnSkillCommands] <> called but SkillRuntime is not ready."); return; } if (string.IsNullOrEmpty(sourceKey)) { Debug.LogError("[YarnSkillCommands] <> requires a sourceKey."); return; } runtime.States.Clear(sourceKey); } [YarnFunction("skill_rank")] public static float SkillRank(string skillId) { var runtime = SkillRuntime.Instance; if (runtime == null || runtime.Skills == null) { return 0f; } return runtime.Skills.GetRank(skillId); } static void WriteResult(VariableStorageBehaviour storage, SkillCheckResult result) { if (storage == null) { return; } storage.SetValue("$check_result", result.Success); storage.SetValue("$check_roll", (float)result.Roll); storage.SetValue("$check_total", (float)result.Total); storage.SetValue("$check_dc", (float)result.DC); storage.SetValue("$check_modifier", (float)(result.RankMod + result.SituationalMod + result.StateMod)); storage.SetValue("$check_degree", DegreeToString(result.Degree)); storage.SetValue("$check_skill", result.SkillId ?? string.Empty); } static void WriteFailedResult(DialogueRunner runner, string skillId, string difficulty) { if (runner == null || runner.VariableStorage == null) { return; } DcBands.TryParse(difficulty, out var dc); if (dc == 0) { dc = (int)DcBand.Routine; } var storage = runner.VariableStorage; storage.SetValue("$check_result", false); storage.SetValue("$check_roll", 0f); storage.SetValue("$check_total", 0f); storage.SetValue("$check_dc", (float)dc); storage.SetValue("$check_modifier", 0f); storage.SetValue("$check_degree", "failure"); storage.SetValue("$check_skill", skillId ?? string.Empty); } static string DegreeToString(SkillCheckDegree degree) { return degree switch { SkillCheckDegree.CriticalFailure => "critical_failure", SkillCheckDegree.Failure => "failure", SkillCheckDegree.Success => "success", SkillCheckDegree.CriticalSuccess => "critical_success", _ => "failure", }; } } }