upd: sync local changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
iisaevandClaude Opus 5.5 committed 2026-10-16 16:18:24 +00:00
1 parent 46afb4b533
commit 13da8f87a2
18 files changed
+61 -18

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
mod_turn_battle_base_name = Turn-based Battle Engine
mod_turn_battle_base_desc = Turn-based battle platform: battle ruleset contracts (IBattleCore), the core registry, action executors, status effects, the battle scene, victory/defeat screens and EXP/loot reports.
+2
View File
@@ -0,0 +1,2 @@
mod_turn_battle_base_name = Движок пошаговых боёв
mod_turn_battle_base_desc = Платформа пошаговых боёв: контракты боевых ядер (IBattleCore), реестр ядер, исполнители действий, статус-эффекты, боевая сцена, экраны победы/поражения и отчёты опыта и добычи.
+2 -2
View File
@@ -1,4 +1,4 @@
{
{
"id": "turn_battle_base",
"name": "Turn-based battle engine",
"version": "2.0.0",
@@ -8,5 +8,5 @@
"sources": [
"src/**/*.cs"
],
"depends": [ "visual_novel_core>=1", "interface>=1" ]
"depends": [ "characters_base>=1", "visual_novel_core>=1", "interface>=1", "items_base>=1", "quests_base>=1", "time_base>=1" ]
}
+1 -1
View File
@@ -4,7 +4,7 @@
[ext_resource type="Script" path="src/BattleSceneContext.cs" id="BattleSceneContextScript"]
[ext_resource type="Script" path="src/BattleSpeedControl.cs" id="BattleSpeedControlScript"]
[ext_resource type="PackedScene" uid="uid://bjhtml8ujba7p" path="res://scenes/battle/deadground.tscn" id="DeadgroundScene"]
[ext_resource type="Script" uid="uid://icsu2hv2c68r" path="res://Core/UI/Lights/UiLightController.cs" id="UiLightControllerScript"]
[ext_resource type="Script" uid="uid://icsu2hv2c68r" path="res://mods/time_base/src/Lights/UiLightController.cs" id="UiLightControllerScript"]
[ext_resource type="PackedScene" uid="uid://b3yq032kqhtn4" path="res://scenes/battle/winground.tscn" id="WingroundScene"]
[node name="BattleScene" type="Control" unique_id=760066045]
+3
View File
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using Cthangover.Core.UI;
using Godot;
using Cthangover.Mods.Interface.Base;
namespace Cthangover.Core.Battle.Actions
{
@@ -37,12 +38,14 @@ namespace Cthangover.Core.Battle.Actions
/// </summary>
public event StopMachineDelegate OnStopMachine;
/// <summary>Stops the machine and fires <see cref="OnStopMachine"/> when the node leaves the tree, so subscribers never wait on a dead node.</summary>
protected override void OnExitTree()
{
base.OnExitTree();
StopMachine();
}
/// <summary>Drives the queued actions once per frame; fires <see cref="OnStopMachine"/> when the queue drains.</summary>
protected override void OnProcess(double delta)
{
if (Actions.Count == 0 && CurrentAction == null)
+9
View File
@@ -1,11 +1,20 @@
namespace Cthangover.Core.Events
{
/// <summary>
/// Published when the battle concludes (victory or defeat), after the
/// victory/defeat screen resolves. Mods can use it for post-battle cleanup
/// or story state. Note: the group defeat with loot and recruitment payload
/// is the separate <see cref="CharactersDefeatedEvent"/>, which fires from
/// the victory screen.
/// </summary>
[LifecycleEvent]
[EventKey("battle_ended")]
public class BattleEndedEvent : ModEvent
{
/// <summary>Outcome reported by the battle core (e.g. <c>"victory"</c>, <c>"defeat"</c>).</summary>
public string Result { get; init; }
/// <summary>Number of turns the battle lasted.</summary>
public int Turns { get; init; }
}
}
+6 -1
View File
@@ -5,12 +5,14 @@ using Cthangover.Core.Factories.Impls;
using Cthangover.Core.Mods;
using Cthangover.Core.Settings;
using Cthangover.Core.UI;
using Cthangover.Core.UI.Lights;
using Cthangover.Mods.Time;
using Cthangover.Core.Characters;
using Cthangover.Core.Mods.Resolvers;
using Cthangover.Core.Utils;
using Godot;
using Cthangover.Mods.Interface.Base;
using Cthangover.Mods.Interface.Base.Lists;
namespace Cthangover.Core.Battle
{
/// <summary>
@@ -120,6 +122,7 @@ namespace Cthangover.Core.Battle
});
}
/// <summary>Claims the scene singleton and registers as the <see cref="ActiveCharacterRegistry"/> provider.</summary>
protected override void OnEnterTree()
{
if (Instance != null && GodotObject.IsInstanceValid(Instance))
@@ -133,6 +136,7 @@ namespace Cthangover.Core.Battle
ActiveCharacterRegistry.Provider = this;
}
/// <summary>Releases the singleton slot and the active-character provider so a later scene can claim them.</summary>
protected override void OnExitTree()
{
base.OnExitTree();
@@ -154,6 +158,7 @@ namespace Cthangover.Core.Battle
/// </summary>
public IBattleCore ActiveBattleCore => activeBattleCore;
/// <summary>Caches the battle backdrop node, resolves the active <see cref="IBattleCore"/> and starts the battle.</summary>
protected override void OnReady()
{
battleBg = GetNodeOrNull<TextureRect>("BattleBg");
+1 -1
View File
@@ -4,7 +4,7 @@ using Cthangover.Core.Factories.Impls;
using Cthangover.Core.Quests;
using Cthangover.Core.Scenes;
using Cthangover.Core.Settings;
using Cthangover.Core.UI.Lights;
using Cthangover.Mods.Time;
using Cthangover.Core.Utils;
namespace Cthangover.Core.Battle
+5 -5
View File
@@ -8,7 +8,7 @@ namespace Cthangover.TurnBattle
{
/// <summary>
/// In-battle speed toggle bar (x1/x4/x8). Directly sets
/// Engine.TimeScale to globally accelerate the entire game loop —
/// Godot.Engine.TimeScale to globally accelerate the entire game loop —
/// not just animations. Persists the choice to GameData.Settings
/// so it survives scene reloads. <see cref="ResetToNormal"/> restores 1x
/// without saving and is the part the core scene consumes through
@@ -71,25 +71,25 @@ namespace Cthangover.TurnBattle
speed = 1;
_activeSpeed = speed;
Engine.TimeScale = speed;
Godot.Engine.TimeScale = speed;
UpdateHighlight();
}
/// <summary>
/// Resets <see cref="Engine.TimeScale"/> to 1.0x without
/// Resets <see cref="Godot.Engine.TimeScale"/> to 1.0x without
/// persisting to settings. Used when battle ends so the lobby
/// and menus run at normal speed regardless of the player's
/// in-battle preference.
/// </summary>
public void ResetToNormal()
{
Engine.TimeScale = 1.0f;
Godot.Engine.TimeScale = 1.0f;
}
private void OnSpeedSelected(int speed)
{
_activeSpeed = speed;
Engine.TimeScale = speed;
Godot.Engine.TimeScale = speed;
var settings = GameData.Instance.Settings;
settings.BattleSpeed = speed;
+9
View File
@@ -1,10 +1,19 @@
namespace Cthangover.Core.Events
{
/// <summary>
/// Published when a battle encounter is initialised (from
/// <see cref="Cthangover.Core.Battle.BattleService.Init"/>, before the battle
/// scene loads). Lets mods observe or prepare for the fight — e.g. pre-buff
/// party members or record the encounter for analytics. The
/// <c>[LifecycleEvent]</c> marker keeps it out of condition-cached flows.
/// </summary>
[LifecycleEvent]
[EventKey("battle_started")]
public class BattleStartedEvent : ModEvent
{
/// <summary>Logical name of the battle scene route being entered (e.g. <c>"Battle"</c>).</summary>
public string BattleType { get; init; }
/// <summary>Template ids of the enemies participating in the encounter.</summary>
public string[] Enemies { get; init; }
}
}
+3
View File
@@ -8,8 +8,11 @@ namespace Cthangover.Core.Events
[EventKey("character_died")]
public class CharacterDiedEvent : ModEvent
{
/// <summary>Template id of the character that died.</summary>
public string CharacterId { get; init; }
/// <summary>Display name at time of death (resolved locale string).</summary>
public string CharacterName { get; init; }
/// <summary>Free-form id of the reporting battle system.</summary>
public string Source { get; init; }
}
}
+2 -2
View File
@@ -4,7 +4,7 @@ using Cthangover.Core.Mods;
using Cthangover.Core.Mods.Resolvers;
using Cthangover.Core.Scenes;
using Cthangover.Core.Settings;
using Cthangover.Core.UI.Base.Lists;
using Cthangover.Mods.Interface.Base.Lists;
using Cthangover.Core.UI.Theming;
using Cthangover.Core.Utils;
using Godot;
@@ -41,7 +41,7 @@ namespace Cthangover.TurnBattle
GameLogger.Log("BATTLE", $"Deadground.Show() ENTER: Visible={Visible}, GodotVisible={base.Visible}", LogLevel.Debug);
var audioService = GetNode<AudioService>("/root/AudioService");
var audioService = GetNodeOrNull<Node>("/root/AudioService") as IAudioService;
audioService?.PlaySound("battle/deadground", SoundType.UI);
var bg = GetNodeOrNull<TextureRect>("TransitionBg");
+4
View File
@@ -19,6 +19,7 @@ namespace Cthangover.Core.Battle
{
private readonly GodotObject _instance;
/// <summary>Registry key reported by the script's <c>get_winground_listener_id()</c>; deduplicates on rescan.</summary>
public string Id { get; }
/// <summary>
@@ -31,6 +32,7 @@ namespace Cthangover.Core.Battle
_instance = instance;
}
/// <summary>Calls the script's <c>on_winground_before_exp(ctx)</c> if implemented; no-op otherwise.</summary>
public void OnBeforeExpAward(WingroundContext ctx)
{
if (!_instance.HasMethod("on_winground_before_exp"))
@@ -41,6 +43,7 @@ namespace Cthangover.Core.Battle
BattleGDScriptBridge.SyncWingroundBack(gdCtx, ctx);
}
/// <summary>Calls the script's <c>on_winground_after_loot(ctx)</c> if implemented; no-op otherwise.</summary>
public void OnAfterLootGenerated(WingroundContext ctx)
{
if (!_instance.HasMethod("on_winground_after_loot"))
@@ -51,6 +54,7 @@ namespace Cthangover.Core.Battle
BattleGDScriptBridge.SyncWingroundBack(gdCtx, ctx);
}
/// <summary>Calls the script's <c>on_winground_before_show(ctx)</c> if implemented; no-op otherwise.</summary>
public void OnBeforeShow(WingroundContext ctx)
{
if (!_instance.HasMethod("on_winground_before_show"))
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Cthangover.Core.Battle
public interface IBattleSpeedControl
{
/// <summary>
/// Restores <c>Engine.TimeScale</c> to 1x without saving the preference.
/// Restores <c>Godot.Engine.TimeScale</c> to 1x without saving the preference.
/// Called when the battle ends so menus and the lobby run at normal speed
/// regardless of the player's in-battle speed choice.
/// </summary>
+1 -1
View File
@@ -34,7 +34,7 @@ namespace Cthangover.TurnBattle
MouseFilter = MouseFilterEnum.Ignore;
_label = new Label();
_label.SetAnchorsPreset(LayoutPreset.Center);
_label.SetAnchorsAndOffsetsPreset(LayoutPreset.Center);
_label.HorizontalAlignment = HorizontalAlignment.Center;
_label.VerticalAlignment = VerticalAlignment.Center;
_label.AddThemeFontSizeOverride("font_size", 28);
+6 -1
View File
@@ -21,6 +21,7 @@ namespace Cthangover.Core.Factories.Impls
#region Singleton
private static readonly Lazy<StatusEffectActionFactory> instance = new(() => new StatusEffectActionFactory());
/// <summary>Lazy singleton; construction performs the AppDomain scan.</summary>
public static StatusEffectActionFactory Instance => instance.Value;
public StatusEffectActionFactory()
@@ -28,12 +29,16 @@ namespace Cthangover.Core.Factories.Impls
actions = new Dictionary<string, IStatusActions>();
foreach (var action in Reflections.FindAndCreate<IStatusActions>())
{
actions.Add(action.ID, action);
// First-wins: the AppDomain scan covers every loaded assembly and mod
// assemblies are never unloaded, so a hot-reload can discover the
// same implementation twice — keep the first instance (ItemActionFactory precedent).
if (!string.IsNullOrEmpty(action.ID) && !actions.ContainsKey(action.ID)) actions[action.ID] = action;
}
}
#endregion
/// <summary>Resolves the behaviour set registered under the effect's <c>Actions</c> id; unknown ids are logged and return <c>null</c>.</summary>
public IStatusActions Get(string id)
{
if (actions.TryGetValue(id, out var result))
+1
View File
@@ -15,6 +15,7 @@ namespace Cthangover.Core.Factories.Impls
public class StatusEffectInfoFactory : FileFactory<StatusEffectInfo>
{
private static readonly Lazy<StatusEffectInfoFactory> lazy = new(() => new StatusEffectInfoFactory());
/// <summary>Lazy singleton facade over the AppServices-owned factory.</summary>
public static StatusEffectInfoFactory Instance => lazy.Value;
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Cthangover.Core.Audio;
@@ -9,7 +9,7 @@ using Cthangover.Core.Items;
using Cthangover.Core.Mods;
using Cthangover.Core.Scenes;
using Cthangover.Core.Settings;
using Cthangover.Core.UI.Base.Lists;
using Cthangover.Mods.Interface.Base.Lists;
using Cthangover.Core.UI.Theming;
using Cthangover.Core.Characters;
using Cthangover.Core.Mods.Resolvers;
@@ -71,7 +71,7 @@ namespace Cthangover.TurnBattle
GameLogger.Log("BATTLE", $"Winground.Show() ENTER: Visible={Visible}, GodotVisible={base.Visible}, txtInfo={(txtInfo != null ? "found" : "NULL")}", LogLevel.Debug);
var audioService = GetNode<AudioService>("/root/AudioService");
var audioService = GetNodeOrNull<Node>("/root/AudioService") as IAudioService;
audioService?.PlaySound("battle/winground", SoundType.UI);
var bg = GetNodeOrNull<TextureRect>("TransitionBg");