Targeted item actions (action/heal), item.add scenario action, inventory cell Decorate; mod cover

This commit is contained in:
cthangover committed 2026-10-22 17:09:39 +02:00
1 parent d560638501
commit 468e393550
9 files changed
+313 -1

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
namespace Cthangover.Mods.Items.Tests
{
using System.Collections.Generic;
using Cthangover.Core.Actions;
using Cthangover.Core.Items;
using Cthangover.Core.Mods;
/// <summary>Action context for scenario action tests: dialog variables and an inventory, no other services.</summary>
public class FakeActionContext : IActionContext
{
/// <summary>Dialog variables returned by <see cref="GetParam"/>.</summary>
public Dictionary<string, string> Params { get; } = new();
/// <summary>Warnings the action logged.</summary>
public List<string> Warnings { get; } = new();
public IQuestService Quests => null;
public ICharacterService Character => null;
public ILightingService Lighting => null;
public ISceneNodeService Scene => null;
public IModRegistry ModRegistry => null;
public IInventory Inventory { get; set; }
public IMapZoneService Map => null;
public string GetParam(string name) => Params.TryGetValue(name, out var value) ? value : null;
public void Log(string category, string message)
{
}
public void LogWarning(string category, string message) => Warnings.Add(message);
}
}
+75
View File
@@ -0,0 +1,75 @@
namespace Cthangover.Mods.Items.Tests
{
using Cthangover.Core.Characters;
using Cthangover.Core.Items;
using Cthangover.Core.Mods.Testing;
using Cthangover.Core.Settings;
using Cthangover.Core.Utils;
/// <summary>
/// <c>action/heal</c>: the amount comes from the item's <c>heal</c> property, health is
/// capped at its base value, and an item that does nothing is reported as not used.
/// </summary>
public class HealItemActionTests
{
private static Item Food(int heal)
{
var properties = new PropertyData();
properties.SetInt(HealItemAction.HealProperty, heal);
return new Item { ID = "test/food", Properties = properties };
}
private static CharacterInfoData Hero(int health, int max)
{
var attributes = new CharacterAttributes();
attributes.Health.Value = health;
attributes.Health.BaseValue = max;
return new CharacterInfoData { ID = "hero", Attributes = attributes };
}
[ModTest("heal/restores_the_amount")]
static void RestoresTheAmount(ModTestContext ctx)
{
var hero = Hero(10, 24);
ModAssert.True(new HealItemAction().UseOn(Food(5), hero), "wounded hero is healed");
ModAssert.Equal(15, hero.Attributes.Health.Value, "10 + 5");
ModAssert.Equal(24, hero.Attributes.Health.BaseValue, "max unchanged");
}
[ModTest("heal/capped_at_max")]
static void CappedAtMax(ModTestContext ctx)
{
var hero = Hero(22, 24);
ModAssert.True(new HealItemAction().UseOn(Food(20), hero), "partly wounded hero is healed");
ModAssert.Equal(24, hero.Attributes.Health.Value, "never above max");
}
[ModTest("heal/useless_heal_is_not_used")]
static void UselessHealIsNotUsed(ModTestContext ctx)
{
var action = new HealItemAction();
var full = Hero(24, 24);
ModAssert.False(action.UseOn(Food(5), full), "full health");
ModAssert.Equal(24, full.Attributes.Health.Value, "full stays full");
var fallen = Hero(0, 24);
ModAssert.False(action.UseOn(Food(5), fallen), "a fallen hero is not revived");
ModAssert.Equal(0, fallen.Attributes.Health.Value, "fallen stays fallen");
var wounded = Hero(10, 24);
ModAssert.False(action.UseOn(Food(0), wounded), "no heal amount");
ModAssert.False(action.UseOn(new Item { ID = "plain" }, wounded), "no properties");
ModAssert.False(action.UseOn(Food(5), null), "no target");
ModAssert.False(action.UseOn(Food(5), new CharacterInfoData { ID = "bare" }), "no attributes");
ModAssert.False(action.UseAction(Food(5)), "heal needs a target");
ModAssert.Equal(10, wounded.Attributes.Health.Value, "untouched");
}
[ModTest("heal/id")]
static void Id(ModTestContext ctx)
{
ModAssert.Equal("action/heal", new HealItemAction().ID, "item definitions reference this id");
}
}
}
+75
View File
@@ -0,0 +1,75 @@
namespace Cthangover.Mods.Items.Tests
{
using System.Linq;
using Cthangover.Core.Items;
using Cthangover.Core.Mods.Resolvers;
using Cthangover.Core.Mods.Testing;
/// <summary><c>action item.add</c> from a scenario: a count defaults to one, a bad one adds nothing.</summary>
public class ItemAddActionTests
{
private static FakeActionContext Context(string id, string count)
{
var ctx = new FakeActionContext { Inventory = new Inventory() };
if (id != null)
ctx.Params["id"] = id;
if (count != null)
ctx.Params["count"] = count;
return ctx;
}
[ModTest("item_add/adds_count")]
static void AddsCount(ModTestContext test)
{
var id = Entities.Collect<ItemInfo>().Keys.First();
var ctx = Context(id, "3");
var before = ctx.Inventory.CheckCount(id);
new ItemAddAction().Run(ctx);
ModAssert.Equal(before + 3, ctx.Inventory.CheckCount(id), "three added");
ModAssert.Equal(0, ctx.Warnings.Count, "no warnings");
}
[ModTest("item_add/count_defaults_to_one")]
static void CountDefaultsToOne(ModTestContext test)
{
var id = Entities.Collect<ItemInfo>().Keys.First();
var ctx = Context(id, null);
var before = ctx.Inventory.CheckCount(id);
new ItemAddAction().Run(ctx);
ModAssert.Equal(before + 1, ctx.Inventory.CheckCount(id), "one added");
}
[ModTest("item_add/bad_arguments_add_nothing")]
static void BadArgumentsAddNothing(ModTestContext test)
{
var id = Entities.Collect<ItemInfo>().Keys.First();
foreach (var count in new[] { "0", "-2", "many" })
{
var ctx = Context(id, count);
var before = ctx.Inventory.CheckCount(id);
new ItemAddAction().Run(ctx);
ModAssert.Equal(before, ctx.Inventory.CheckCount(id), "count=" + count);
ModAssert.Equal(1, ctx.Warnings.Count, "warned about count=" + count);
}
var noId = Context(null, "1");
new ItemAddAction().Run(noId);
ModAssert.Equal(1, noId.Warnings.Count, "warned about the missing id");
var noInventory = new FakeActionContext();
noInventory.Params["id"] = id;
new ItemAddAction().Run(noInventory);
ModAssert.Equal(1, noInventory.Warnings.Count, "warned about the missing inventory");
}
[ModTest("item_add/name")]
static void Name(ModTestContext test)
{
ModAssert.Equal("item.add", new ItemAddAction().Name, "DSL name");
}
}
}