Character factories follow mod reloads
This commit is contained in:
1 parent
734169db3d
commit
3afa3a6a80
11 files changed
+365
-363
No files matched your search
+37
-37
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,63 +1,63 @@
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooldown helpers for <see cref="ActionCharacterType.Active"/> cards.
|
||||
/// Active cards are one-shot abilities usable outside battle (from the
|
||||
/// character panel). The last-use timestamp is stored in
|
||||
/// <see cref="ActionCharacter.Properties"/> under
|
||||
/// 'LastUsed' - the cooldown duration
|
||||
/// (seconds) under 'ActiveCooldown'
|
||||
/// Absence of <c>LastUsed</c> means the card has never been activated.
|
||||
/// </summary>
|
||||
public static class ActionCharacterActiveExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> when the card is <see cref="ActionCharacterType.Active"/>
|
||||
/// and its cooldown has expired (or it was never used).
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seconds remaining until the cooldown expires. Returns <c>0</c> when
|
||||
/// ready, never used, or not an Active card.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records the activation timestamp. Has no effect on non-Active cards.
|
||||
/// </summary>
|
||||
public static void MarkUsed(this ActionCharacter action, long timestamp)
|
||||
{
|
||||
if (action.Type != ActionCharacterType.Active)
|
||||
return;
|
||||
action.Properties.SetLong("LastUsed", timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooldown helpers for <see cref="ActionCharacterType.Active"/> cards.
|
||||
/// Active cards are one-shot abilities usable outside battle (from the
|
||||
/// character panel). The last-use timestamp is stored in
|
||||
/// <see cref="ActionCharacter.Properties"/> under
|
||||
/// 'LastUsed' - the cooldown duration
|
||||
/// (seconds) under 'ActiveCooldown'
|
||||
/// Absence of <c>LastUsed</c> means the card has never been activated.
|
||||
/// </summary>
|
||||
public static class ActionCharacterActiveExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> when the card is <see cref="ActionCharacterType.Active"/>
|
||||
/// and its cooldown has expired (or it was never used).
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seconds remaining until the cooldown expires. Returns <c>0</c> when
|
||||
/// ready, never used, or not an Active card.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records the activation timestamp. Has no effect on non-Active cards.
|
||||
/// </summary>
|
||||
public static void MarkUsed(this ActionCharacter action, long timestamp)
|
||||
{
|
||||
if (action.Type != ActionCharacterType.Active)
|
||||
return;
|
||||
action.Properties.SetLong("LastUsed", timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Cthangover.Core.Factories;
|
||||
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[EntityKind("action")]
|
||||
public class ActionCharacterInfo : IIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique action identifier, serialized as "Id" in JSON.
|
||||
/// </summary>
|
||||
[JsonPropertyName("Id")]
|
||||
public string ID { get; set; }
|
||||
/// <summary>
|
||||
/// Display name for the battle action menu.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Description text for the battle action tooltip.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// Targeting category, serialized as string (e.g. "ToSelf",
|
||||
/// "ToAlias", "ToEnemy") via <see cref="JsonStringEnumConverter"/>
|
||||
/// for human-readable JSON.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ActionCharacterType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Resource path string for the action icon. Resolved to
|
||||
/// <c>Texture2D</c> by <see cref="CharacterFactory"/> at load
|
||||
/// time.
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Flat list of alternating key-value pairs that populate the
|
||||
/// <see cref="ActionCharacter.Properties"/> 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.
|
||||
/// </summary>
|
||||
public List<string> Values { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Cthangover.Core.Factories;
|
||||
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[EntityKind("action")]
|
||||
public class ActionCharacterInfo : IIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique action identifier, serialized as "Id" in JSON.
|
||||
/// </summary>
|
||||
[JsonPropertyName("Id")]
|
||||
public string ID { get; set; }
|
||||
/// <summary>
|
||||
/// Display name for the battle action menu.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Description text for the battle action tooltip.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// Targeting category, serialized as string (e.g. "ToSelf",
|
||||
/// "ToAlias", "ToEnemy") via <see cref="JsonStringEnumConverter"/>
|
||||
/// for human-readable JSON.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ActionCharacterType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Resource path string for the action icon. Resolved to
|
||||
/// <c>Texture2D</c> by <see cref="CharacterFactory"/> at load
|
||||
/// time.
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Flat list of alternating key-value pairs that populate the
|
||||
/// <see cref="ActionCharacter.Properties"/> 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.
|
||||
/// </summary>
|
||||
public List<string> Values { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Cthangover.Core.Factories;
|
||||
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON-serializable character descriptor loaded from data files.
|
||||
/// Only the four core battle stats (Health, Defence, Attack, Point)
|
||||
/// are hardcoded — they map directly to <see cref="Attribute"/> fields
|
||||
/// in <see cref="CharacterAttributes"/>. All other properties
|
||||
/// (any mod-defined extras) are captured by <see cref="ExtraProperties"/>
|
||||
/// and flowed into <see cref="CharacterAttributes.Properties"/> at
|
||||
/// factory time, so mods can add arbitrary stat keys without
|
||||
/// changing the DTO schema.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[EntityKind("character")]
|
||||
public class CharacterInfo : IIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique character ID, serialized as "Id" in JSON. Serves as the
|
||||
/// factory lookup key and save-data identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("Id")]
|
||||
public string ID { get; set; }
|
||||
/// <summary>
|
||||
/// AI behaviour class name for this character in battle. Resolved
|
||||
/// externally — null means no AI override.
|
||||
/// </summary>
|
||||
public string Behaviour { get; set; }
|
||||
/// <summary>
|
||||
/// Starting level for this character when recruited.
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
/// <summary>
|
||||
/// Resource path string for the full-body portrait. Resolved to a
|
||||
/// <c>Texture2D</c> by <see cref="CharacterFactory"/> at load time.
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Display name, localized via TranslationServer when shown to the
|
||||
/// player.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Starting health points (used as <see cref="Attribute"/>.BaseValue).
|
||||
/// </summary>
|
||||
public int Health { get; set; }
|
||||
/// <summary>
|
||||
/// Starting defence value.
|
||||
/// </summary>
|
||||
public int Defence { get; set; }
|
||||
/// <summary>
|
||||
/// Starting action points (resource pool for abilities).
|
||||
/// </summary>
|
||||
public int Point { get; set; }
|
||||
/// <summary>
|
||||
/// Starting attack power.
|
||||
/// </summary>
|
||||
public int Attack { get; set; }
|
||||
/// <summary>
|
||||
/// Starting experience points.
|
||||
/// </summary>
|
||||
public int Exp { get; set; }
|
||||
/// <summary>
|
||||
/// Recruitment chance percentage (0–100) after defeating this
|
||||
/// character in battle.
|
||||
/// </summary>
|
||||
public int RecruitmentChance { get; set; }
|
||||
/// <summary>
|
||||
/// Action ID strings, each referencing an
|
||||
/// <see cref="ActionCharacterInfo"/> entry in the action data file.
|
||||
/// Stored as a JSON array for clean mod-patching with
|
||||
/// <c>$add</c>/<c>$remove</c>/<c>$set</c> suffix operations.
|
||||
/// </summary>
|
||||
public List<string> Actions { get; set; }
|
||||
/// <summary>
|
||||
/// Loot drop table for this character when defeated as an enemy.
|
||||
/// </summary>
|
||||
public List<LootEntry> Loot { get; set; }
|
||||
/// <summary>
|
||||
/// Captures any JSON keys not matched by the hardcoded properties
|
||||
/// above — including former DTO fields like any mod-defined extras.
|
||||
/// These are imported into <see cref="CharacterAttributes.Properties"/>
|
||||
/// by <see cref="CharacterFactory.ImportExtraProperty"/>.
|
||||
/// </summary>
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtraProperties { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Cthangover.Core.Factories;
|
||||
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON-serializable character descriptor loaded from data files.
|
||||
/// Only the four core battle stats (Health, Defence, Attack, Point)
|
||||
/// are hardcoded — they map directly to <see cref="Attribute"/> fields
|
||||
/// in <see cref="CharacterAttributes"/>. All other properties
|
||||
/// (any mod-defined extras) are captured by <see cref="ExtraProperties"/>
|
||||
/// and flowed into <see cref="CharacterAttributes.Properties"/> at
|
||||
/// factory time, so mods can add arbitrary stat keys without
|
||||
/// changing the DTO schema.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[EntityKind("character")]
|
||||
public class CharacterInfo : IIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique character ID, serialized as "Id" in JSON. Serves as the
|
||||
/// factory lookup key and save-data identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("Id")]
|
||||
public string ID { get; set; }
|
||||
/// <summary>
|
||||
/// AI behaviour class name for this character in battle. Resolved
|
||||
/// externally — null means no AI override.
|
||||
/// </summary>
|
||||
public string Behaviour { get; set; }
|
||||
/// <summary>
|
||||
/// Starting level for this character when recruited.
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
/// <summary>
|
||||
/// Resource path string for the full-body portrait. Resolved to a
|
||||
/// <c>Texture2D</c> by <see cref="CharacterFactory"/> at load time.
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Display name, localized via TranslationServer when shown to the
|
||||
/// player.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Starting health points (used as <see cref="Attribute"/>.BaseValue).
|
||||
/// </summary>
|
||||
public int Health { get; set; }
|
||||
/// <summary>
|
||||
/// Starting defence value.
|
||||
/// </summary>
|
||||
public int Defence { get; set; }
|
||||
/// <summary>
|
||||
/// Starting action points (resource pool for abilities).
|
||||
/// </summary>
|
||||
public int Point { get; set; }
|
||||
/// <summary>
|
||||
/// Starting attack power.
|
||||
/// </summary>
|
||||
public int Attack { get; set; }
|
||||
/// <summary>
|
||||
/// Starting experience points.
|
||||
/// </summary>
|
||||
public int Exp { get; set; }
|
||||
/// <summary>
|
||||
/// Recruitment chance percentage (0–100) after defeating this
|
||||
/// character in battle.
|
||||
/// </summary>
|
||||
public int RecruitmentChance { get; set; }
|
||||
/// <summary>
|
||||
/// Action ID strings, each referencing an
|
||||
/// <see cref="ActionCharacterInfo"/> entry in the action data file.
|
||||
/// Stored as a JSON array for clean mod-patching with
|
||||
/// <c>$add</c>/<c>$remove</c>/<c>$set</c> suffix operations.
|
||||
/// </summary>
|
||||
public List<string> Actions { get; set; }
|
||||
/// <summary>
|
||||
/// Loot drop table for this character when defeated as an enemy.
|
||||
/// </summary>
|
||||
public List<LootEntry> Loot { get; set; }
|
||||
/// <summary>
|
||||
/// Captures any JSON keys not matched by the hardcoded properties
|
||||
/// above — including former DTO fields like any mod-defined extras.
|
||||
/// These are imported into <see cref="CharacterAttributes.Properties"/>
|
||||
/// by <see cref="CharacterFactory.ImportExtraProperty"/>.
|
||||
/// </summary>
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtraProperties { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Cthangover.Core.Characters
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension for querying passive effects on a <see cref="Character"/>.
|
||||
/// Passive actions are <see cref="ActionCharacter"/> entries with
|
||||
/// <see cref="ActionCharacterType.Passive"/> whose <see cref="ActionCharacter.Properties"/>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static class CharacterPassiveExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if the character has at least one Passive action
|
||||
/// whose <see cref="ActionCharacter.Properties"/> contains <paramref name="key"/>
|
||||
/// with a <c>true</c> boolean value.
|
||||
/// </summary>
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension for querying passive effects on a <see cref="Character"/>.
|
||||
/// Passive actions are <see cref="ActionCharacter"/> entries with
|
||||
/// <see cref="ActionCharacterType.Passive"/> whose <see cref="ActionCharacter.Properties"/>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static class CharacterPassiveExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if the character has at least one Passive action
|
||||
/// whose <see cref="ActionCharacter.Properties"/> contains <paramref name="key"/>
|
||||
/// with a <c>true</c> boolean value.
|
||||
/// </summary>
|
||||
public static bool HasPassiveEffect(this Character character, string key)
|
||||
{
|
||||
return character?.Actions?.Any(a =>
|
||||
a.Type == ActionCharacterType.Passive && a.GetBool(key)) ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,12 +56,13 @@ namespace Cthangover.Core.Factories.Impls
|
||||
/// </summary>
|
||||
private Dictionary<string, ActionCharacterInfo> EnsureInfos()
|
||||
{
|
||||
if (_allInfos != null)
|
||||
return _allInfos;
|
||||
|
||||
// The resolver's dictionary identity is the data snapshot (see CharacterFactory.EnsureInfos).
|
||||
var infos = Entities.Collect<ActionCharacterInfo>();
|
||||
if (ModManager.Instance.IsInitialized)
|
||||
if (!ReferenceEquals(infos, _allInfos))
|
||||
{
|
||||
_allInfos = infos;
|
||||
_cache.Clear();
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
using Cthangover.Core.Factories;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Core.Factories.Impls
|
||||
{
|
||||
/// <summary>
|
||||
/// Thin <c>Texture2DFactory</c> for character portrait avatars. The
|
||||
/// separate factory (as opposed to reusing <c>UITextureFactory</c>)
|
||||
/// 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.
|
||||
/// <para/>
|
||||
/// The factory lives in the characters engine mod; its singleton is also
|
||||
/// published to the kernel through <see cref="AvatarProviderRegistry"/> by
|
||||
/// <c>CharactersModInitializer</c>, so core-side consumers resolve
|
||||
/// avatars without referencing this assembly.
|
||||
/// </summary>
|
||||
public class AvatarFactory : Texture2DFactory, IAvatarProvider
|
||||
{
|
||||
internal AvatarFactory() : base("avatars", 64) { }
|
||||
|
||||
/// <summary>Process-wide singleton for this mod assembly; the public API for avatar lookups.</summary>
|
||||
public static AvatarFactory Instance { get; } = new AvatarFactory();
|
||||
|
||||
/// <summary>Mod asset group scanned by this factory — files live under <c>mods/*/</c><b>avatars</b>.</summary>
|
||||
public override string GroupName => "avatars";
|
||||
|
||||
}
|
||||
}
|
||||
using Cthangover.Core.Factories;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Core.Factories.Impls
|
||||
{
|
||||
/// <summary>
|
||||
/// Thin <c>Texture2DFactory</c> for character portrait avatars. The
|
||||
/// separate factory (as opposed to reusing <c>UITextureFactory</c>)
|
||||
/// 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.
|
||||
/// <para/>
|
||||
/// The factory lives in the characters engine mod; its singleton is also
|
||||
/// published to the kernel through <see cref="AvatarProviderRegistry"/> by
|
||||
/// <c>CharactersModInitializer</c>, so core-side consumers resolve
|
||||
/// avatars without referencing this assembly.
|
||||
/// </summary>
|
||||
public class AvatarFactory : Texture2DFactory, IAvatarProvider
|
||||
{
|
||||
internal AvatarFactory() : base("avatars", 64) { }
|
||||
|
||||
/// <summary>Process-wide singleton for this mod assembly; the public API for avatar lookups.</summary>
|
||||
public static AvatarFactory Instance { get; } = new AvatarFactory();
|
||||
|
||||
/// <summary>Mod asset group scanned by this factory — files live under <c>mods/*/</c><b>avatars</b>.</summary>
|
||||
public override string GroupName => "avatars";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -64,16 +64,17 @@ namespace Cthangover.Core.Factories.Impls
|
||||
/// </summary>
|
||||
private Dictionary<string, CharacterInfo> EnsureInfos()
|
||||
{
|
||||
if (_allInfos != null)
|
||||
return _allInfos;
|
||||
|
||||
GameLogger.Log("FACTORY", $"CharacterFactory.EnsureInfos: START, ModRegistry.IsInitialized={ModRegistry.Instance.IsInitialized}, mods count={ModRegistry.Instance.Mods?.Count ?? 0}", LogLevel.Debug);
|
||||
|
||||
// Entities.Collect is cached by the entity resolver until the next
|
||||
// mod reload, so its dictionary identity is the data snapshot: a new
|
||||
// one (reload, data edit, game switch with this mod kept) drops the
|
||||
// built templates instead of serving the previous mod set's data.
|
||||
var infos = Entities.Collect<CharacterInfo>();
|
||||
if (ModManager.Instance.IsInitialized)
|
||||
_allInfos = infos;
|
||||
if (ReferenceEquals(infos, _allInfos))
|
||||
return infos;
|
||||
|
||||
GameLogger.Log("FACTORY", $"CharacterFactory.EnsureInfos: END, loaded {infos?.Count ?? 0} infos", LogLevel.Debug);
|
||||
_allInfos = infos;
|
||||
_cache.Clear();
|
||||
GameLogger.Log("FACTORY", $"CharacterFactory.EnsureInfos: {infos?.Count ?? 0} infos (new data snapshot)", LogLevel.Debug);
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user