implemented skill system according to designdocuments
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c70934ddbc404dfcb0952241bec17b6c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NightclubArcadia.Skills;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Skills.Tests
|
||||
{
|
||||
public class CommentaryPickerTests
|
||||
{
|
||||
sealed class FixedRollSource : IRollSource
|
||||
{
|
||||
readonly int _value;
|
||||
public FixedRollSource(int value) => _value = value;
|
||||
public int Roll(int sides) => _value;
|
||||
}
|
||||
|
||||
SkillDefinition Make(string id, int startingRank)
|
||||
{
|
||||
var def = ScriptableObject.CreateInstance<SkillDefinition>();
|
||||
def.EditorSet(id, id, SkillDomain.Instinct, string.Empty, startingRank, Color.white);
|
||||
return def;
|
||||
}
|
||||
|
||||
PlayerSkills SkillsFor(params (string id, int rank)[] ranks)
|
||||
{
|
||||
var db = ScriptableObject.CreateInstance<SkillDatabase>();
|
||||
var list = new List<SkillDefinition>();
|
||||
foreach (var (id, rank) in ranks)
|
||||
{
|
||||
list.Add(Make(id, rank));
|
||||
}
|
||||
|
||||
db.EditorSetSkills(list);
|
||||
return new PlayerSkills(db);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pick_EmptyCandidates_ReturnsNull()
|
||||
{
|
||||
var skills = SkillsFor(("paranoia", 2));
|
||||
var result = CommentarySystem.Pick(
|
||||
new List<SkillDefinition>(),
|
||||
skills,
|
||||
new List<string>(),
|
||||
3,
|
||||
new FixedRollSource(1));
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pick_HigherRankPreferred_GivenLowRoll()
|
||||
{
|
||||
// ranks 1 and 3 → weights 1 and 3 → total 4
|
||||
// roll 1 maps to pick index 0 of cumulative: only weight-1 skill
|
||||
// roll 2..4 maps to higher-ranked skill
|
||||
var low = Make("read_room", 1);
|
||||
var high = Make("paranoia", 3);
|
||||
var skills = SkillsFor(("read_room", 1), ("paranoia", 3));
|
||||
|
||||
var highPick = CommentarySystem.Pick(
|
||||
new List<SkillDefinition> { low, high },
|
||||
skills,
|
||||
new List<string>(),
|
||||
3,
|
||||
new FixedRollSource(2));
|
||||
|
||||
Assert.AreEqual("paranoia", highPick.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pick_RecentSpeaker_HasHalvedWeight()
|
||||
{
|
||||
// Without recency: weights 2 and 2, total 4
|
||||
// With "a" recent: weights 1 and 2, total 3 → roll 1 picks a, roll 2+ picks b
|
||||
var a = Make("a", 2);
|
||||
var b = Make("b", 2);
|
||||
var skills = SkillsFor(("a", 2), ("b", 2));
|
||||
|
||||
var pick = CommentarySystem.Pick(
|
||||
new List<SkillDefinition> { a, b },
|
||||
skills,
|
||||
new List<string> { "a" },
|
||||
3,
|
||||
new FixedRollSource(2));
|
||||
|
||||
Assert.AreEqual("b", pick.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ba3f2a4a2a44285acd610ab51884753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using NUnit.Framework;
|
||||
using NightclubArcadia.Skills;
|
||||
|
||||
namespace NightclubArcadia.Skills.Tests
|
||||
{
|
||||
public class DcBandTests
|
||||
{
|
||||
[TestCase("standard", 11)]
|
||||
[TestCase("STANDARD", 11)]
|
||||
[TestCase("13", 13)]
|
||||
[TestCase("-3", -3)]
|
||||
public void TryParse_AcceptsBandNamesAndIntegers(string input, int expected)
|
||||
{
|
||||
Assert.IsTrue(DcBands.TryParse(input, out var dc));
|
||||
Assert.AreEqual(expected, dc);
|
||||
}
|
||||
|
||||
[TestCase("banana")]
|
||||
[TestCase("")]
|
||||
[TestCase(null)]
|
||||
public void TryParse_RejectsGarbage(string input)
|
||||
{
|
||||
Assert.IsFalse(DcBands.TryParse(input, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9057738b1d8f402d907c1d9dab8d82a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "NightclubArcadia.Skills.Tests",
|
||||
"rootNamespace": "NightclubArcadia.Skills.Tests",
|
||||
"references": [
|
||||
"NightclubArcadia.Skills",
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09d3d01ec5554542b5dd23239ace7a70
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,137 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NightclubArcadia.Skills;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NightclubArcadia.Skills.Tests
|
||||
{
|
||||
public class SkillCheckSystemTests
|
||||
{
|
||||
sealed class FixedRollSource : IRollSource
|
||||
{
|
||||
readonly int _value;
|
||||
public FixedRollSource(int value) => _value = value;
|
||||
public int Roll(int sides) => _value;
|
||||
}
|
||||
|
||||
SkillDatabase CreateDatabase(params (string id, string display, int rank)[] skills)
|
||||
{
|
||||
var db = ScriptableObject.CreateInstance<SkillDatabase>();
|
||||
var list = new List<SkillDefinition>();
|
||||
foreach (var (id, display, rank) in skills)
|
||||
{
|
||||
var def = ScriptableObject.CreateInstance<SkillDefinition>();
|
||||
def.EditorSet(id, display, SkillDomain.Presence, string.Empty, rank, Color.white);
|
||||
list.Add(def);
|
||||
}
|
||||
|
||||
db.EditorSetSkills(list);
|
||||
return db;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_SumsModifiers_AndSucceedsWhenTotalMeetsDc()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 2));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(10));
|
||||
|
||||
var result = system.Resolve("persuasion", 12, 1);
|
||||
|
||||
Assert.AreEqual(13, result.Total);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(SkillCheckDegree.Success, result.Degree);
|
||||
Assert.AreEqual(2, result.RankMod);
|
||||
Assert.AreEqual(1, result.SituationalMod);
|
||||
Assert.AreEqual(0, result.StateMod);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_NaturalOne_IsCriticalFailureEvenIfModifiersWouldPass()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 10));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(1));
|
||||
|
||||
var result = system.Resolve("persuasion", 5, 5);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(SkillCheckDegree.CriticalFailure, result.Degree);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_NaturalTwenty_IsCriticalSuccessEvenIfBelowDc()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 0));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(20));
|
||||
|
||||
var result = system.Resolve("persuasion", 25, 0);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(SkillCheckDegree.CriticalSuccess, result.Degree);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_ExactDc_IsSuccess()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 1));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(10));
|
||||
|
||||
var result = system.Resolve("persuasion", 11, 0);
|
||||
|
||||
Assert.AreEqual(11, result.Total);
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_AppliesNegativeAndWildcardStateModifiers()
|
||||
{
|
||||
var db = CreateDatabase(("reflexes", "Reflexes", 1));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
state.Set("drunk", "reflexes", -2);
|
||||
state.Set("crowd", "*", -1);
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(10));
|
||||
|
||||
var result = system.Resolve("reflexes", 5, 0);
|
||||
|
||||
Assert.AreEqual(-3, result.StateMod);
|
||||
Assert.AreEqual(8, result.Total);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_UnknownSkill_DoesNotThrow_AndUsesZeroRank()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 3));
|
||||
var skills = new PlayerSkills(db);
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(10));
|
||||
|
||||
SkillCheckResult result = default;
|
||||
Assert.DoesNotThrow(() => result = system.Resolve("nosuchskill", 10, 0));
|
||||
Assert.AreEqual(0, result.RankMod);
|
||||
Assert.AreEqual("NOSUCHSKILL", result.SkillDisplayName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_DoesNotMutatePlayerSkills()
|
||||
{
|
||||
var db = CreateDatabase(("persuasion", "Persuasion", 2));
|
||||
var skills = new PlayerSkills(db);
|
||||
var before = skills.GetRank("persuasion");
|
||||
var state = new SkillStateModifiers();
|
||||
var system = new SkillCheckSystem(db, skills, state, new FixedRollSource(10));
|
||||
|
||||
system.Resolve("persuasion", 10, 0);
|
||||
|
||||
Assert.AreEqual(before, skills.GetRank("persuasion"));
|
||||
Assert.AreEqual(1, skills.AllRanks.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78734080b9b5473db4365b8203fc79b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user