From 48cbb73b0bb5dc74f0813bdfd2d5aa56d494eb03 Mon Sep 17 00:00:00 2001 From: iisaev Date: Wed, 21 Oct 2026 13:29:31 +0200 Subject: [PATCH] Line endings: LF everywhere .gitattributes forces LF (CRLF only for .bat/.cmd), core.autocrlf is off; files that differed only by CRLF are normalized. --- .gitattributes | 30 +++ .gitignore | 74 +++---- characters/actions/physics_attack.json | 32 +-- characters/actions/physics_defence.json | 30 +-- characters/actions/physics_stun.json | 32 +-- .../ActionCharacterActiveExtension.cs | 126 ++++++------ src/Characters/ActionCharacterInfo.cs | 112 +++++------ src/Characters/CharacterInfo.cs | 186 +++++++++--------- src/Characters/CharacterPassiveExtension.cs | 50 ++--- src/Factories/AvatarFactory.cs | 60 +++--- 10 files changed, 381 insertions(+), 351 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3030c1a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,30 @@ +# Line endings: LF everywhere, on every OS, whatever core.autocrlf says. +* text=auto eol=lf + +# cmd.exe misreads labels and goto in LF batch files, so they stay CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf + +# Binaries: never converted, never diffed as text. +*.png binary +*.jpg binary +*.jpeg binary +*.webp binary +*.svg text +*.ogg binary +*.wav binary +*.mp3 binary +*.ttf binary +*.otf binary +*.woff binary +*.woff2 binary +*.dll binary +*.so binary +*.dylib binary +*.pdb binary +*.exe binary +*.zip binary +*.pck binary +*.moc3 binary +*.res binary +*.scn binary diff --git a/.gitignore b/.gitignore index bb88804..c301ca8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,37 @@ -# Godot 4.x -.godot/ -.idea/ -sdk/ -bin/ -obj/ - -#ai -.omo/ -.opencode/ - -gen/ -obj/ -bin/ - -*.tres~ -*.tscn~ - -# GDScript LSP -.godot/ - -# Export -*.exe -*.dmg -*.apk -*.aab -*.pck -*.zip -*.log - -# OS -.DS_Store -Thumbs.db - -# Editor -*.import -*.godot.uid +# Godot 4.x +.godot/ +.idea/ +sdk/ +bin/ +obj/ + +#ai +.omo/ +.opencode/ + +gen/ +obj/ +bin/ + +*.tres~ +*.tscn~ + +# GDScript LSP +.godot/ + +# Export +*.exe +*.dmg +*.apk +*.aab +*.pck +*.zip +*.log + +# OS +.DS_Store +Thumbs.db + +# Editor +*.import +*.godot.uid diff --git a/characters/actions/physics_attack.json b/characters/actions/physics_attack.json index 536c98a..5476359 100644 --- a/characters/actions/physics_attack.json +++ b/characters/actions/physics_attack.json @@ -1,17 +1,17 @@ -{ - "Kind": "action", - "Items": [ - { - "Id": "physics/attack", - "Name": "characters/action/physics_attack/name", - "Description": "characters/action/physics_attack/description", - "Image": "characters/actions/physics_attack", - "Type": "ToEnemy", - "Values": [ - "RequiredPoint=1", - "Attack=1.0", - "AttackType=Physics" - ] - } - ] +{ + "Kind": "action", + "Items": [ + { + "Id": "physics/attack", + "Name": "characters/action/physics_attack/name", + "Description": "characters/action/physics_attack/description", + "Image": "characters/actions/physics_attack", + "Type": "ToEnemy", + "Values": [ + "RequiredPoint=1", + "Attack=1.0", + "AttackType=Physics" + ] + } + ] } diff --git a/characters/actions/physics_defence.json b/characters/actions/physics_defence.json index 52163ea..1c59f98 100644 --- a/characters/actions/physics_defence.json +++ b/characters/actions/physics_defence.json @@ -1,16 +1,16 @@ -{ - "Kind": "action", - "Items": [ - { - "Id": "physics/defence", - "Name": "characters/action/defence/name", - "Description": "characters/action/defence/description", - "Image": "characters/actions/physics_defence", - "Type": "ToAlias", - "Values": [ - "RequiredPoint=1", - "Defence=4" - ] - } - ] +{ + "Kind": "action", + "Items": [ + { + "Id": "physics/defence", + "Name": "characters/action/defence/name", + "Description": "characters/action/defence/description", + "Image": "characters/actions/physics_defence", + "Type": "ToAlias", + "Values": [ + "RequiredPoint=1", + "Defence=4" + ] + } + ] } diff --git a/characters/actions/physics_stun.json b/characters/actions/physics_stun.json index 0cc290f..a7fc388 100644 --- a/characters/actions/physics_stun.json +++ b/characters/actions/physics_stun.json @@ -1,17 +1,17 @@ -{ - "Kind": "action", - "Items": [ - { - "Id": "physics/stun", - "Name": "characters/action/stun/name", - "Description": "characters/action/stun/description", - "Image": "characters/actions/physics_stun", - "Type": "ToEnemy", - "Values": [ - "RequiredPoint=2", - "AttackType=Physics", - "Turn=3" - ] - } - ] +{ + "Kind": "action", + "Items": [ + { + "Id": "physics/stun", + "Name": "characters/action/stun/name", + "Description": "characters/action/stun/description", + "Image": "characters/actions/physics_stun", + "Type": "ToEnemy", + "Values": [ + "RequiredPoint=2", + "AttackType=Physics", + "Turn=3" + ] + } + ] } diff --git a/src/Characters/ActionCharacterActiveExtension.cs b/src/Characters/ActionCharacterActiveExtension.cs index 42a832e..d35e5e1 100644 --- a/src/Characters/ActionCharacterActiveExtension.cs +++ b/src/Characters/ActionCharacterActiveExtension.cs @@ -1,63 +1,63 @@ -namespace Cthangover.Core.Characters -{ - /// - /// Cooldown helpers for cards. - /// Active cards are one-shot abilities usable outside battle (from the - /// character panel). The last-use timestamp is stored in - /// under - /// 'LastUsed' - the cooldown duration - /// (seconds) under 'ActiveCooldown' - /// Absence of LastUsed means the card has never been activated. - /// - public static class ActionCharacterActiveExtension - { - /// - /// Returns true when the card is - /// and its cooldown has expired (or it was never used). - /// - public static bool IsReadyToUse(this ActionCharacter action, long currentTime) - { - if (action.Type != ActionCharacterType.Active) - return false; - - var lastUsed = action.Properties.GetLong("LastUsed", 0); - var cooldown = action.GetInt("ActiveCooldown", 0); - - if (lastUsed == 0) - return true; - if (cooldown <= 0) - return false; - - return currentTime - lastUsed >= cooldown; - } - - /// - /// Seconds remaining until the cooldown expires. Returns 0 when - /// ready, never used, or not an Active card. - /// - public static long GetRemainingCooldown(this ActionCharacter action, long currentTime) - { - if (action.Type != ActionCharacterType.Active) - return 0; - - var lastUsed = action.Properties.GetLong("LastUsed", 0); - var cooldown = action.GetInt("ActiveCooldown", 0); - - if (lastUsed == 0 || cooldown <= 0) - return 0; - - var elapsed = currentTime - lastUsed; - return elapsed >= cooldown ? 0 : cooldown - elapsed; - } - - /// - /// Records the activation timestamp. Has no effect on non-Active cards. - /// - public static void MarkUsed(this ActionCharacter action, long timestamp) - { - if (action.Type != ActionCharacterType.Active) - return; - action.Properties.SetLong("LastUsed", timestamp); - } - } -} +namespace Cthangover.Core.Characters +{ + /// + /// Cooldown helpers for cards. + /// Active cards are one-shot abilities usable outside battle (from the + /// character panel). The last-use timestamp is stored in + /// under + /// 'LastUsed' - the cooldown duration + /// (seconds) under 'ActiveCooldown' + /// Absence of LastUsed means the card has never been activated. + /// + public static class ActionCharacterActiveExtension + { + /// + /// Returns true when the card is + /// and its cooldown has expired (or it was never used). + /// + public static bool IsReadyToUse(this ActionCharacter action, long currentTime) + { + if (action.Type != ActionCharacterType.Active) + return false; + + var lastUsed = action.Properties.GetLong("LastUsed", 0); + var cooldown = action.GetInt("ActiveCooldown", 0); + + if (lastUsed == 0) + return true; + if (cooldown <= 0) + return false; + + return currentTime - lastUsed >= cooldown; + } + + /// + /// Seconds remaining until the cooldown expires. Returns 0 when + /// ready, never used, or not an Active card. + /// + public static long GetRemainingCooldown(this ActionCharacter action, long currentTime) + { + if (action.Type != ActionCharacterType.Active) + return 0; + + var lastUsed = action.Properties.GetLong("LastUsed", 0); + var cooldown = action.GetInt("ActiveCooldown", 0); + + if (lastUsed == 0 || cooldown <= 0) + return 0; + + var elapsed = currentTime - lastUsed; + return elapsed >= cooldown ? 0 : cooldown - elapsed; + } + + /// + /// Records the activation timestamp. Has no effect on non-Active cards. + /// + public static void MarkUsed(this ActionCharacter action, long timestamp) + { + if (action.Type != ActionCharacterType.Active) + return; + action.Properties.SetLong("LastUsed", timestamp); + } + } +} diff --git a/src/Characters/ActionCharacterInfo.cs b/src/Characters/ActionCharacterInfo.cs index a2a7e56..874e018 100644 --- a/src/Characters/ActionCharacterInfo.cs +++ b/src/Characters/ActionCharacterInfo.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; -using Cthangover.Core.Factories; - -namespace Cthangover.Core.Characters -{ - /// - /// JSON-serializable descriptor for an ActionCharacter. The Values list is a - /// flat string array carrying the PropertyData pairs — serialized as a list - /// rather than a dictionary to maintain JSON readability and avoid key - /// escaping issues. Type uses a string enum converter so the JSON reads - /// "ToSelf"/"ToAlias"/"ToEnemy" instead of integers. The Image field is a - /// resource path string, resolved to Texture2D by the factory at load time. - /// - [Serializable] - [EntityKind("action")] - public class ActionCharacterInfo : IIdentifiable - { - /// - /// Unique action identifier, serialized as "Id" in JSON. - /// - [JsonPropertyName("Id")] - public string ID { get; set; } - /// - /// Display name for the battle action menu. - /// - public string Name { get; set; } - /// - /// Description text for the battle action tooltip. - /// - public string Description { get; set; } - /// - /// Targeting category, serialized as string (e.g. "ToSelf", - /// "ToAlias", "ToEnemy") via - /// for human-readable JSON. - /// - [JsonConverter(typeof(JsonStringEnumConverter))] - public ActionCharacterType Type { get; set; } - /// - /// Resource path string for the action icon. Resolved to - /// Texture2D by at load - /// time. - /// - public string Image { get; set; } - /// - /// Flat list of alternating key-value pairs that populate the - /// bag at load time. - /// Index 0 = first key, index 1 = first value, index 2 = second - /// key, etc. Stored as a list rather than a dictionary to - /// simplify JSON authoring and avoid key-escaping issues. - /// - public List Values { get; set; } - } - -} +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Cthangover.Core.Factories; + +namespace Cthangover.Core.Characters +{ + /// + /// JSON-serializable descriptor for an ActionCharacter. The Values list is a + /// flat string array carrying the PropertyData pairs — serialized as a list + /// rather than a dictionary to maintain JSON readability and avoid key + /// escaping issues. Type uses a string enum converter so the JSON reads + /// "ToSelf"/"ToAlias"/"ToEnemy" instead of integers. The Image field is a + /// resource path string, resolved to Texture2D by the factory at load time. + /// + [Serializable] + [EntityKind("action")] + public class ActionCharacterInfo : IIdentifiable + { + /// + /// Unique action identifier, serialized as "Id" in JSON. + /// + [JsonPropertyName("Id")] + public string ID { get; set; } + /// + /// Display name for the battle action menu. + /// + public string Name { get; set; } + /// + /// Description text for the battle action tooltip. + /// + public string Description { get; set; } + /// + /// Targeting category, serialized as string (e.g. "ToSelf", + /// "ToAlias", "ToEnemy") via + /// for human-readable JSON. + /// + [JsonConverter(typeof(JsonStringEnumConverter))] + public ActionCharacterType Type { get; set; } + /// + /// Resource path string for the action icon. Resolved to + /// Texture2D by at load + /// time. + /// + public string Image { get; set; } + /// + /// Flat list of alternating key-value pairs that populate the + /// bag at load time. + /// Index 0 = first key, index 1 = first value, index 2 = second + /// key, etc. Stored as a list rather than a dictionary to + /// simplify JSON authoring and avoid key-escaping issues. + /// + public List Values { get; set; } + } + +} diff --git a/src/Characters/CharacterInfo.cs b/src/Characters/CharacterInfo.cs index 811cab8..f12b077 100644 --- a/src/Characters/CharacterInfo.cs +++ b/src/Characters/CharacterInfo.cs @@ -1,93 +1,93 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; -using Cthangover.Core.Factories; - -namespace Cthangover.Core.Characters -{ - /// - /// JSON-serializable character descriptor loaded from data files. - /// Only the four core battle stats (Health, Defence, Attack, Point) - /// are hardcoded — they map directly to fields - /// in . All other properties - /// (any mod-defined extras) are captured by - /// and flowed into at - /// factory time, so mods can add arbitrary stat keys without - /// changing the DTO schema. - /// - [Serializable] - [EntityKind("character")] - public class CharacterInfo : IIdentifiable - { - /// - /// Unique character ID, serialized as "Id" in JSON. Serves as the - /// factory lookup key and save-data identifier. - /// - [JsonPropertyName("Id")] - public string ID { get; set; } - /// - /// AI behaviour class name for this character in battle. Resolved - /// externally — null means no AI override. - /// - public string Behaviour { get; set; } - /// - /// Starting level for this character when recruited. - /// - public int Level { get; set; } - /// - /// Resource path string for the full-body portrait. Resolved to a - /// Texture2D by at load time. - /// - public string Image { get; set; } - /// - /// Display name, localized via TranslationServer when shown to the - /// player. - /// - public string Name { get; set; } - /// - /// Starting health points (used as .BaseValue). - /// - public int Health { get; set; } - /// - /// Starting defence value. - /// - public int Defence { get; set; } - /// - /// Starting action points (resource pool for abilities). - /// - public int Point { get; set; } - /// - /// Starting attack power. - /// - public int Attack { get; set; } - /// - /// Starting experience points. - /// - public int Exp { get; set; } - /// - /// Recruitment chance percentage (0–100) after defeating this - /// character in battle. - /// - public int RecruitmentChance { get; set; } - /// - /// Action ID strings, each referencing an - /// entry in the action data file. - /// Stored as a JSON array for clean mod-patching with - /// $add/$remove/$set suffix operations. - /// - public List Actions { get; set; } - /// - /// Loot drop table for this character when defeated as an enemy. - /// - public List Loot { get; set; } - /// - /// Captures any JSON keys not matched by the hardcoded properties - /// above — including former DTO fields like any mod-defined extras. - /// These are imported into - /// by . - /// - [JsonExtensionData] - public Dictionary ExtraProperties { get; set; } - } - -} +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Cthangover.Core.Factories; + +namespace Cthangover.Core.Characters +{ + /// + /// JSON-serializable character descriptor loaded from data files. + /// Only the four core battle stats (Health, Defence, Attack, Point) + /// are hardcoded — they map directly to fields + /// in . All other properties + /// (any mod-defined extras) are captured by + /// and flowed into at + /// factory time, so mods can add arbitrary stat keys without + /// changing the DTO schema. + /// + [Serializable] + [EntityKind("character")] + public class CharacterInfo : IIdentifiable + { + /// + /// Unique character ID, serialized as "Id" in JSON. Serves as the + /// factory lookup key and save-data identifier. + /// + [JsonPropertyName("Id")] + public string ID { get; set; } + /// + /// AI behaviour class name for this character in battle. Resolved + /// externally — null means no AI override. + /// + public string Behaviour { get; set; } + /// + /// Starting level for this character when recruited. + /// + public int Level { get; set; } + /// + /// Resource path string for the full-body portrait. Resolved to a + /// Texture2D by at load time. + /// + public string Image { get; set; } + /// + /// Display name, localized via TranslationServer when shown to the + /// player. + /// + public string Name { get; set; } + /// + /// Starting health points (used as .BaseValue). + /// + public int Health { get; set; } + /// + /// Starting defence value. + /// + public int Defence { get; set; } + /// + /// Starting action points (resource pool for abilities). + /// + public int Point { get; set; } + /// + /// Starting attack power. + /// + public int Attack { get; set; } + /// + /// Starting experience points. + /// + public int Exp { get; set; } + /// + /// Recruitment chance percentage (0–100) after defeating this + /// character in battle. + /// + public int RecruitmentChance { get; set; } + /// + /// Action ID strings, each referencing an + /// entry in the action data file. + /// Stored as a JSON array for clean mod-patching with + /// $add/$remove/$set suffix operations. + /// + public List Actions { get; set; } + /// + /// Loot drop table for this character when defeated as an enemy. + /// + public List Loot { get; set; } + /// + /// Captures any JSON keys not matched by the hardcoded properties + /// above — including former DTO fields like any mod-defined extras. + /// These are imported into + /// by . + /// + [JsonExtensionData] + public Dictionary ExtraProperties { get; set; } + } + +} diff --git a/src/Characters/CharacterPassiveExtension.cs b/src/Characters/CharacterPassiveExtension.cs index 6713294..955436d 100644 --- a/src/Characters/CharacterPassiveExtension.cs +++ b/src/Characters/CharacterPassiveExtension.cs @@ -1,25 +1,25 @@ -using System.Linq; - -namespace Cthangover.Core.Characters -{ - /// - /// Extension for querying passive effects on a . - /// Passive actions are entries with - /// whose - /// declare effect flags as boolean keys (e.g. "IgnoreArmor"). The key names - /// are chosen by the mod author — the core imposes no registry or constants. - /// - public static class CharacterPassiveExtension - { - /// - /// Returns true if the character has at least one Passive action - /// whose contains - /// with a true boolean value. - /// - public static bool HasPassiveEffect(this Character character, string key) - { - return character?.Actions?.Any(a => - a.Type == ActionCharacterType.Passive && a.GetBool(key)) ?? false; - } - } -} +using System.Linq; + +namespace Cthangover.Core.Characters +{ + /// + /// Extension for querying passive effects on a . + /// Passive actions are entries with + /// whose + /// declare effect flags as boolean keys (e.g. "IgnoreArmor"). The key names + /// are chosen by the mod author — the core imposes no registry or constants. + /// + public static class CharacterPassiveExtension + { + /// + /// Returns true if the character has at least one Passive action + /// whose contains + /// with a true boolean value. + /// + public static bool HasPassiveEffect(this Character character, string key) + { + return character?.Actions?.Any(a => + a.Type == ActionCharacterType.Passive && a.GetBool(key)) ?? false; + } + } +} diff --git a/src/Factories/AvatarFactory.cs b/src/Factories/AvatarFactory.cs index f434431..8cfb390 100644 --- a/src/Factories/AvatarFactory.cs +++ b/src/Factories/AvatarFactory.cs @@ -1,30 +1,30 @@ -using Cthangover.Core.Factories; -using Cthangover.Core.Utils; -using Godot; - -namespace Cthangover.Core.Factories.Impls -{ - /// - /// Thin Texture2DFactory for character portrait avatars. The - /// separate factory (as opposed to reusing UITextureFactory) - /// isolates avatar images into their own mod group with a dedicated - /// cache budget, so frequently-swapped dialog portraits don't evict - /// other UI textures under memory pressure. - /// - /// The factory lives in the characters engine mod; its singleton is also - /// published to the kernel through by - /// CharactersModInitializer, so core-side consumers resolve - /// avatars without referencing this assembly. - /// - public class AvatarFactory : Texture2DFactory, IAvatarProvider - { - internal AvatarFactory() : base("avatars", 64) { } - - /// Process-wide singleton for this mod assembly; the public API for avatar lookups. - public static AvatarFactory Instance { get; } = new AvatarFactory(); - - /// Mod asset group scanned by this factory — files live under mods/*/avatars. - public override string GroupName => "avatars"; - - } -} +using Cthangover.Core.Factories; +using Cthangover.Core.Utils; +using Godot; + +namespace Cthangover.Core.Factories.Impls +{ + /// + /// Thin Texture2DFactory for character portrait avatars. The + /// separate factory (as opposed to reusing UITextureFactory) + /// isolates avatar images into their own mod group with a dedicated + /// cache budget, so frequently-swapped dialog portraits don't evict + /// other UI textures under memory pressure. + /// + /// The factory lives in the characters engine mod; its singleton is also + /// published to the kernel through by + /// CharactersModInitializer, so core-side consumers resolve + /// avatars without referencing this assembly. + /// + public class AvatarFactory : Texture2DFactory, IAvatarProvider + { + internal AvatarFactory() : base("avatars", 64) { } + + /// Process-wide singleton for this mod assembly; the public API for avatar lookups. + public static AvatarFactory Instance { get; } = new AvatarFactory(); + + /// Mod asset group scanned by this factory — files live under mods/*/avatars. + public override string GroupName => "avatars"; + + } +}