Battle unit gauges extension point; battle action schemas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cthangoverandClaude Opus 5.5 committed 2026-10-24 10:01:08 +02:00
1 parent 44462624cd
commit d2f710a6a1
12 files changed
+231

No files matched your search

+9
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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 = コア
+9
View File
@@ -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 = 코어
+9
View File
@@ -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 = Ядро
+9
View File
@@ -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
+9
View File
@@ -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 = 核心
+53
View File
@@ -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"
}
]
}
]
}
+24
View File
@@ -0,0 +1,24 @@
namespace Cthangover.Core.Battle
{
/// <summary>
/// One extra bar of a battle unit next to its health (mana, rage, ammo…), as an
/// <see cref="IBattleUnitGauge"/> reads it at this moment.
/// </summary>
public class BattleGauge
{
/// <summary>Stable id (a stat id, for example) the cores key their rows by.</summary>
public string Id { get; set; }
/// <summary>Short label, a locale key or plain text ("MP").</summary>
public string Label { get; set; }
/// <summary>Current value.</summary>
public int Current { get; set; }
/// <summary>Maximum; a bar with Max &lt;= 0 is not drawn.</summary>
public int Max { get; set; }
/// <summary>Fill colour as HTML ("#4a8cff"); null = the theme's info colour.</summary>
public string Color { get; set; }
}
}
+17
View File
@@ -0,0 +1,17 @@
namespace Cthangover.Core.Battle
{
using Cthangover.Core.Mods.Consumers;
/// <summary>Registers every <see cref="IBattleUnitGauge"/> of a loaded mod assembly in <see cref="BattleUnitGauges"/>.</summary>
public class BattleUnitGaugeConsumer : ModTypeConsumer<IBattleUnitGauge>
{
/// <inheritdoc />
public override string Id => "turn_battle_base.unit_gauges";
/// <inheritdoc />
protected override void Add(IBattleUnitGauge instance, string modId) => BattleUnitGauges.Add(instance);
/// <inheritdoc />
protected override void Remove(IBattleUnitGauge instance, string modId) => BattleUnitGauges.Remove(instance);
}
}
+54
View File
@@ -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;
/// <summary>
/// The registered <see cref="IBattleUnitGauge"/>s and the bars they give for a unit. A
/// provider calls <see cref="NotifyChanged"/> when a value moved (mana spent), so the
/// cores redraw that unit; cores also read the bars whenever they redraw it themselves.
/// </summary>
public static class BattleUnitGauges
{
private static readonly List<IBattleUnitGauge> Providers = new();
/// <summary>Raised with a unit id (null = every unit) when its bars changed.</summary>
public static event Action<string> Changed;
/// <summary>Registers a provider.</summary>
public static void Add(IBattleUnitGauge gauge)
{
if (gauge != null && !Providers.Contains(gauge))
Providers.Add(gauge);
}
/// <summary>Removes a provider (its mod is unloaded).</summary>
public static void Remove(IBattleUnitGauge gauge) => Providers.Remove(gauge);
/// <summary>Every bar of <paramref name="unit"/> with a positive maximum, providers in order; a failing provider is logged and skipped.</summary>
public static List<BattleGauge> Read(Character unit)
{
var result = new List<BattleGauge>();
if (unit == null)
return result;
foreach (var provider in Providers.OrderBy(p => p.Order).ToList())
{
try
{
result.AddRange((provider.Read(unit) ?? Enumerable.Empty<BattleGauge>()).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;
}
/// <summary>Tells the cores that the bars of <paramref name="unitId"/> changed.</summary>
public static void NotifyChanged(string unitId) => Changed?.Invoke(unitId);
}
}
+20
View File
@@ -0,0 +1,20 @@
namespace Cthangover.Core.Battle
{
using System.Collections.Generic;
using Cthangover.Core.Characters;
/// <summary>
/// Supplies extra bars of battle units (found in mod assemblies by
/// <see cref="BattleUnitGaugeConsumer"/>): a mod that adds a resource — mana of
/// <c>battle_formulas</c>, a rage meter — shows it in every battle core that draws
/// <see cref="BattleUnitGauges"/>, without knowing the cores.
/// </summary>
public interface IBattleUnitGauge
{
/// <summary>Order among the providers (lower first).</summary>
int Order { get; }
/// <summary>The bars of <paramref name="unit"/> now; empty when it has none.</summary>
IEnumerable<BattleGauge> Read(Character unit);
}
}