using System; namespace NightclubArcadia.Skills { /// /// Pure check math. Exactly three modifier sources: rank, situational, state. /// No UnityEngine types. No side effects on PlayerSkills / VariableStorage / UI. /// public sealed class SkillCheckSystem { readonly SkillDatabase _database; readonly PlayerSkills _skills; readonly SkillStateModifiers _state; readonly IRollSource _rolls; public SkillCheckSystem( SkillDatabase database, PlayerSkills skills, SkillStateModifiers state, IRollSource rolls) { _database = database ?? throw new ArgumentNullException(nameof(database)); _skills = skills ?? throw new ArgumentNullException(nameof(skills)); _state = state ?? throw new ArgumentNullException(nameof(state)); _rolls = rolls ?? throw new ArgumentNullException(nameof(rolls)); } public SkillCheckResult Resolve(string skillId, int dc, int situationalMod) { skillId ??= string.Empty; string displayName; int rankMod; if (_database.TryGet(skillId, out var def) && def != null) { displayName = string.IsNullOrEmpty(def.DisplayName) ? def.Id.ToUpperInvariant() : def.DisplayName; rankMod = _skills.GetRank(skillId); } else { displayName = skillId.ToUpperInvariant(); rankMod = 0; } var roll = _rolls.Roll(20); var stateMod = _state.TotalFor(skillId); var total = roll + rankMod + situationalMod + stateMod; bool success; SkillCheckDegree degree; if (roll == 1) { success = false; degree = SkillCheckDegree.CriticalFailure; } else if (roll == 20) { success = true; degree = SkillCheckDegree.CriticalSuccess; } else { success = total >= dc; degree = success ? SkillCheckDegree.Success : SkillCheckDegree.Failure; } return new SkillCheckResult( skillId, displayName, roll, rankMod, situationalMod, stateMod, total, dc, success, degree); } } }