From d2f710a6a19cbbe5477b612cbb8588b61e7df9a0 Mon Sep 17 00:00:00 2001 From: iisaev Date: Sat, 24 Oct 2026 10:01:08 +0200 Subject: [PATCH] Battle unit gauges extension point; battle action schemas Co-Authored-By: Claude Opus 5.5 --- locale/schema_en.properties | 9 +++++ locale/schema_es.properties | 9 +++++ locale/schema_ja.properties | 9 +++++ locale/schema_ko.properties | 9 +++++ locale/schema_ru.properties | 9 +++++ locale/schema_vi.properties | 9 +++++ locale/schema_zh.properties | 9 +++++ schemas/turn_battle_base.json | 53 ++++++++++++++++++++++++++ src/Gauges/BattleGauge.cs | 24 ++++++++++++ src/Gauges/BattleUnitGaugeConsumer.cs | 17 +++++++++ src/Gauges/BattleUnitGauges.cs | 54 +++++++++++++++++++++++++++ src/Gauges/IBattleUnitGauge.cs | 20 ++++++++++ 12 files changed, 231 insertions(+) create mode 100644 locale/schema_en.properties create mode 100644 locale/schema_es.properties create mode 100644 locale/schema_ja.properties create mode 100644 locale/schema_ko.properties create mode 100644 locale/schema_ru.properties create mode 100644 locale/schema_vi.properties create mode 100644 locale/schema_zh.properties create mode 100644 schemas/turn_battle_base.json create mode 100644 src/Gauges/BattleGauge.cs create mode 100644 src/Gauges/BattleUnitGaugeConsumer.cs create mode 100644 src/Gauges/BattleUnitGauges.cs create mode 100644 src/Gauges/IBattleUnitGauge.cs diff --git a/locale/schema_en.properties b/locale/schema_en.properties new file mode 100644 index 0000000..3a3ef59 --- /dev/null +++ b/locale/schema_en.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = Start a battle +battle/schema/builtin/battle_init_desc = A battle from a novel scene: enemies, the return scene, a quest +battle/schema/field/enemies = Enemies +battle/schema/field/scene = Scene +battle/schema/field/quest = Quest +battle/schema/field/tag = Tag +battle/schema/builtin/set_core = Battle core +battle/schema/builtin/set_core_desc = Which rules the next battle follows +battle/schema/field/core = Core diff --git a/locale/schema_es.properties b/locale/schema_es.properties new file mode 100644 index 0000000..a1e977d --- /dev/null +++ b/locale/schema_es.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = Empezar un combate +battle/schema/builtin/battle_init_desc = Un combate desde una escena de novela: enemigos, escena de regreso, misión +battle/schema/field/enemies = Enemigos +battle/schema/field/scene = Escena +battle/schema/field/quest = Misión +battle/schema/field/tag = Etiqueta +battle/schema/builtin/set_core = Núcleo de combate +battle/schema/builtin/set_core_desc = Qué reglas sigue el próximo combate +battle/schema/field/core = Núcleo diff --git a/locale/schema_ja.properties b/locale/schema_ja.properties new file mode 100644 index 0000000..e51772f --- /dev/null +++ b/locale/schema_ja.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = 戦闘を始める +battle/schema/builtin/battle_init_desc = ノベルのシーンからの戦闘:敵、戻り先のシーン、クエスト +battle/schema/field/enemies = 敵 +battle/schema/field/scene = シーン +battle/schema/field/quest = クエスト +battle/schema/field/tag = タグ +battle/schema/builtin/set_core = バトルコア +battle/schema/builtin/set_core_desc = 次の戦闘がどのルールに従うか +battle/schema/field/core = コア diff --git a/locale/schema_ko.properties b/locale/schema_ko.properties new file mode 100644 index 0000000..57a3e7f --- /dev/null +++ b/locale/schema_ko.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = 전투 시작 +battle/schema/builtin/battle_init_desc = 노벨 장면에서의 전투: 적, 돌아올 장면, 퀘스트 +battle/schema/field/enemies = 적 +battle/schema/field/scene = 장면 +battle/schema/field/quest = 퀘스트 +battle/schema/field/tag = 태그 +battle/schema/builtin/set_core = 전투 코어 +battle/schema/builtin/set_core_desc = 다음 전투가 따를 규칙 +battle/schema/field/core = 코어 diff --git a/locale/schema_ru.properties b/locale/schema_ru.properties new file mode 100644 index 0000000..879f720 --- /dev/null +++ b/locale/schema_ru.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = Начать бой +battle/schema/builtin/battle_init_desc = Бой из сцены новеллы: враги, сцена возврата, квест +battle/schema/field/enemies = Враги +battle/schema/field/scene = Сцена +battle/schema/field/quest = Квест +battle/schema/field/tag = Метка +battle/schema/builtin/set_core = Боевое ядро +battle/schema/builtin/set_core_desc = Каким правилам подчиняется следующий бой +battle/schema/field/core = Ядро diff --git a/locale/schema_vi.properties b/locale/schema_vi.properties new file mode 100644 index 0000000..0e95883 --- /dev/null +++ b/locale/schema_vi.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = Bắt đầu trận đấu +battle/schema/builtin/battle_init_desc = Trận đấu từ cảnh tiểu thuyết: kẻ địch, cảnh quay về, nhiệm vụ +battle/schema/field/enemies = Kẻ địch +battle/schema/field/scene = Cảnh +battle/schema/field/quest = Nhiệm vụ +battle/schema/field/tag = Thẻ +battle/schema/builtin/set_core = Lõi chiến đấu +battle/schema/builtin/set_core_desc = Trận tới theo luật nào +battle/schema/field/core = Lõi diff --git a/locale/schema_zh.properties b/locale/schema_zh.properties new file mode 100644 index 0000000..4ce1d22 --- /dev/null +++ b/locale/schema_zh.properties @@ -0,0 +1,9 @@ +battle/schema/builtin/battle_init = 开始战斗 +battle/schema/builtin/battle_init_desc = 来自小说场景的战斗:敌人、返回场景、任务 +battle/schema/field/enemies = 敌人 +battle/schema/field/scene = 场景 +battle/schema/field/quest = 任务 +battle/schema/field/tag = 标签 +battle/schema/builtin/set_core = 战斗核心 +battle/schema/builtin/set_core_desc = 下一场战斗遵循哪套规则 +battle/schema/field/core = 核心 diff --git a/schemas/turn_battle_base.json b/schemas/turn_battle_base.json new file mode 100644 index 0000000..9a4317d --- /dev/null +++ b/schemas/turn_battle_base.json @@ -0,0 +1,53 @@ +{ + "Actions": [ + { + "Id": "battle.init", + "Name": "battle/schema/builtin/battle_init", + "Description": "battle/schema/builtin/battle_init_desc", + "Category": "battle", + "Params": [ + { + "Key": "enemies", + "Type": "ref_list", + "Kind": "character", + "Required": true, + "Name": "battle/schema/field/enemies" + }, + { + "Key": "scene", + "Type": "scene", + "Name": "battle/schema/field/scene" + }, + { + "Key": "quest_id", + "Type": "ref", + "Kind": "quest", + "Name": "battle/schema/field/quest" + }, + { + "Key": "new_tag", + "Type": "text", + "Name": "battle/schema/field/tag" + } + ] + }, + { + "Id": "battle.set_core", + "Name": "battle/schema/builtin/set_core", + "Description": "battle/schema/builtin/set_core_desc", + "Category": "battle", + "Params": [ + { + "Key": "core", + "Type": "choice", + "Options": [ + "ff_battle", + "card_battle" + ], + "Required": true, + "Name": "battle/schema/field/core" + } + ] + } + ] +} diff --git a/src/Gauges/BattleGauge.cs b/src/Gauges/BattleGauge.cs new file mode 100644 index 0000000..5e2e7db --- /dev/null +++ b/src/Gauges/BattleGauge.cs @@ -0,0 +1,24 @@ +namespace Cthangover.Core.Battle +{ + /// + /// One extra bar of a battle unit next to its health (mana, rage, ammo…), as an + /// reads it at this moment. + /// + public class BattleGauge + { + /// Stable id (a stat id, for example) the cores key their rows by. + public string Id { get; set; } + + /// Short label, a locale key or plain text ("MP"). + public string Label { get; set; } + + /// Current value. + public int Current { get; set; } + + /// Maximum; a bar with Max <= 0 is not drawn. + public int Max { get; set; } + + /// Fill colour as HTML ("#4a8cff"); null = the theme's info colour. + public string Color { get; set; } + } +} diff --git a/src/Gauges/BattleUnitGaugeConsumer.cs b/src/Gauges/BattleUnitGaugeConsumer.cs new file mode 100644 index 0000000..a1352af --- /dev/null +++ b/src/Gauges/BattleUnitGaugeConsumer.cs @@ -0,0 +1,17 @@ +namespace Cthangover.Core.Battle +{ + using Cthangover.Core.Mods.Consumers; + + /// Registers every of a loaded mod assembly in . + public class BattleUnitGaugeConsumer : ModTypeConsumer + { + /// + public override string Id => "turn_battle_base.unit_gauges"; + + /// + protected override void Add(IBattleUnitGauge instance, string modId) => BattleUnitGauges.Add(instance); + + /// + protected override void Remove(IBattleUnitGauge instance, string modId) => BattleUnitGauges.Remove(instance); + } +} diff --git a/src/Gauges/BattleUnitGauges.cs b/src/Gauges/BattleUnitGauges.cs new file mode 100644 index 0000000..6180c1e --- /dev/null +++ b/src/Gauges/BattleUnitGauges.cs @@ -0,0 +1,54 @@ +namespace Cthangover.Core.Battle +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Cthangover.Core.Characters; + using Cthangover.Core.Utils; + + /// + /// The registered s and the bars they give for a unit. A + /// provider calls when a value moved (mana spent), so the + /// cores redraw that unit; cores also read the bars whenever they redraw it themselves. + /// + public static class BattleUnitGauges + { + private static readonly List Providers = new(); + + /// Raised with a unit id (null = every unit) when its bars changed. + public static event Action Changed; + + /// Registers a provider. + public static void Add(IBattleUnitGauge gauge) + { + if (gauge != null && !Providers.Contains(gauge)) + Providers.Add(gauge); + } + + /// Removes a provider (its mod is unloaded). + public static void Remove(IBattleUnitGauge gauge) => Providers.Remove(gauge); + + /// Every bar of with a positive maximum, providers in order; a failing provider is logged and skipped. + public static List Read(Character unit) + { + var result = new List(); + if (unit == null) + return result; + foreach (var provider in Providers.OrderBy(p => p.Order).ToList()) + { + try + { + result.AddRange((provider.Read(unit) ?? Enumerable.Empty()).Where(g => g != null && g.Max > 0)); + } + catch (Exception ex) + { + GameLogger.Log("BATTLE", $"gauge provider {provider.GetType().Name} failed for '{unit.ID}': {ex.Message}", LogLevel.Error); + } + } + return result; + } + + /// Tells the cores that the bars of changed. + public static void NotifyChanged(string unitId) => Changed?.Invoke(unitId); + } +} diff --git a/src/Gauges/IBattleUnitGauge.cs b/src/Gauges/IBattleUnitGauge.cs new file mode 100644 index 0000000..165c4e5 --- /dev/null +++ b/src/Gauges/IBattleUnitGauge.cs @@ -0,0 +1,20 @@ +namespace Cthangover.Core.Battle +{ + using System.Collections.Generic; + using Cthangover.Core.Characters; + + /// + /// Supplies extra bars of battle units (found in mod assemblies by + /// ): a mod that adds a resource — mana of + /// battle_formulas, a rage meter — shows it in every battle core that draws + /// , without knowing the cores. + /// + public interface IBattleUnitGauge + { + /// Order among the providers (lower first). + int Order { get; } + + /// The bars of now; empty when it has none. + IEnumerable Read(Character unit); + } +}