diff --git a/docs/角色与羁绊系统.md b/docs/角色与羁绊系统.md
index abfb9b5..71d3253 100644
--- a/docs/角色与羁绊系统.md
+++ b/docs/角色与羁绊系统.md
@@ -238,4 +238,139 @@
* 你的战斗节奏是什么(平稳输出?憋大招爆发?赌博?)
* 你的团队构成是什么。
-这完美解决了“文科生不需要做实验”的逻辑漏洞,同时也大大加深了游戏的策略池。
\ No newline at end of file
+这完美解决了“文科生不需要做实验”的逻辑漏洞,同时也大大加深了游戏的策略池。
+
+这是一个极具策略深度的改进点。将**个人怪癖 (Traits)** 与 **羁绊叠层 (Stacking)** 挂钩,可以让棋子的价值不再仅仅取决于面板数值,而是取决于他在“构筑 (Build)”中的位置。
+
+这一类怪癖我们称为 **“催化剂特质” (Catalyst Traits)**。它们可以视为“万能牌”、“增幅器”或者“阻断剂”。
+
+以下是针对“实验室生态(人群)”和“职能分工(职业)”设计的 20 个互动型怪癖:
+
+---
+
+### 一、 增益型:为了凑羁绊而生 (Bond Boosters)
+*这些棋子是构筑流派的核心插件,或者是前期的强力打工仔。*
+
+#### 1. 【学术变色龙】 (Academic Chameleon)
+* **效果:** 该单位入场时,自动**模仿**队伍中当前层数最高的“人群画像”羁绊(如场上卷王最多,他自动视为卷王)。
+* **叠层互动:** 视为 1 个单位,但类型灵活。
+* **点评:** “为了混进圈子,他什么都能演。”
+
+#### 2. 【简历注水】 (Resume Padder)
+* **效果:** 该单位在计算“职能分工”羁绊时,**视为 2 个单位**。但他本人的实际工作效率 -30%。
+* **叠层互动:** 快速凑齐 (4) 或 (6) 层羁绊的强力挂件。
+* **点评:** “虽然代码不是他写的,但项目组名单里他排第一个。”
+
+#### 3. 【跨学科天才】 (Interdisciplinary)
+* **效果:** 同时拥有【炼金术士】和【极客】(或随机两个职业)的双重标签。
+* **叠层互动:** 可以同时为两个职业羁绊提供层数 +1。
+* **点评:** “生物里代码写得最好的,码农里最懂分子式的。”
+
+#### 4. 【精神股东】 (Fanatic)
+* **效果:** 如果当前激活了【卷王】或【富家子弟】羁绊,该单位享受的羁绊加成效果 **翻倍**。
+* **叠层互动:** 单体享受双倍Buff。
+* **点评:** “明明拿的是实习生的工资,操的却是CEO的心。”
+
+#### 5. 【气氛组】 (Hype Man)
+* **效果:** 只要他在场,当前激活的所有羁绊层数需求 **-1**(例如原本 4 人触发的效果,现在 3 人即可触发)。但全队压力增长 +10%。
+* **叠层互动:** 降低触发门槛(仅限第二层级以上)。
+* **点评:** “只要我不尴尬,这个组就是世界一流团队。”
+
+#### 6. 【领头羊】 (Alpha)
+* **效果:** 必须作为队长(放在特定核心格子上)才能生效。使当前最高的职业羁绊等级 **+1**(突破上限,如创造出 7 层羁绊效果)。
+* **叠层互动:** 突破上限。
+* **点评:** “他重新定义了这个专业。”
+
+---
+
+### 二、 减益/干扰型:副作用与高回报 (Disruptors)
+*这些棋子通常数值极高,但会破坏团队化学反应,适合“独狼”或特定解法。*
+
+#### 7. 【害群之马】 (Black Sheep)
+* **效果:** 该单位的全属性 +50%(极强),但在计算任何羁绊层数时,**视为 -1 个单位**。
+* **叠层互动:** 扣除层数。可能导致原本激活的 (4) 层掉回 (3) 层。
+* **点评:** “技术是大牛,但只要他在,团队就没法团结。”
+
+#### 8. 【独行侠】 (Lone Wolf)
+* **效果:** 如果该单位**没有**激活任何羁绊(既不是卷王也不享受职业加成),攻击力 +100%。
+* **叠层互动:** 只有不叠层才强。
+* **点评:** “我不抱团,我就是大腿。”
+
+#### 9. 【熵增发生器】 (Entropy Generator)
+* **效果:** 随机**无效化**场上的一个已激活羁绊,转化为全队的“吸血”效果(工作回心情)。
+* **叠层互动:** 献祭羁绊换取生存。
+* **点评:** “毁灭秩序,带来混乱的快乐。”
+
+#### 10. 【鄙视链顶端】 (Elitist)
+* **效果:** 如果他在场,**除他以外**的所有同职业棋子(如其他写手)羁绊收益归零,但他本人获得所有被归零收益的总和。
+* **叠层互动:** 吸星大法,献祭队友养大哥。
+* **点评:** “文无第一,但我就是第一,你们都是垃圾。”
+
+#### 11. 【社恐患者】 (Social Phobia)
+* **效果:** 如果激活了【人群画像】(如卷王/摸鱼党)这种涉及社交氛围的羁绊,他会立刻**逃跑**(离场或停止工作)。只有在“无阵营”状态下才能工作。
+* **叠层互动:** 互斥机制。
+* **点评:** “人一多我就想吐。”
+
+---
+
+### 三、 特殊机制型:动态与随机 (Special Mechanics)
+*增加Roguelike的不可预测性。*
+
+#### 12. 【墙头草】 (Bandwagoner)
+* **效果:** 每回合开始时,随机变成场上人数最多的那个职业。
+* **叠层互动:** 动态补位。
+* **点评:** “这就是所谓的'追逐热点',哪个方向火我就搞哪个。”
+
+#### 13. 【卧底】 (Imposter)
+* **效果:** 表面上显示为【卷王】,实际上在计算层数时视为【摸鱼党】。
+* **叠层互动:** 欺骗玩家(或者在PVP/对抗模式中欺骗对手)。
+* **点评:** “看他在疯狂敲键盘,其实是在和群友对喷。”
+
+#### 14. 【催化剂】 (Catalyst)
+* **效果:** 他不提供层数,但能改变羁绊的**性质**。
+ * 例如:让【卷王】羁绊的“攻速加成”变为“金钱产出”。
+ * 例如:让【摸鱼党】羁绊的“心情恢复”变为“甚至能恢复压力(San值)”。
+* **叠层互动:** 质变。
+* **点评:** “他改变了这股风气的走向。”
+
+#### 15. 【内卷终结者】 (The Breaker)
+* **效果:** 当【卷王】层数达到 (6) 时,他会自爆,清空所有卷王层数,并给予全队一个巨额的“解放”Buff(瞬间完成当前任务)。
+* **叠层互动:** 阈值触发型核弹。
+* **点评:** “同归于尽吧,该死的KPI!”
+
+#### 16. 【学术寄生虫】 (Parasite)
+* **效果:** 只要场上有任何 (4) 层以上的羁绊激活,该单位每回合自动升级(星级/属性提升),但他自己不贡献层数。
+* **叠层互动:** 纯收益者。
+* **点评:** “大树底下好乘凉。”
+
+#### 17. 【教条主义者】 (Purist)
+* **效果:** 只有当全队**所有单位**都是同一个职业(如全是代码工)时,全队属性 +50%。只要混入一个异类,加成消失。
+* **叠层互动:** 纯色队核心。
+* **点评:** “我们的血统必须纯正。”
+
+#### 18. 【客座教授】 (Visiting Scholar)
+* **效果:** 每回合随机获得一个不同的职业标签(本回合是【笔杆子】,下回合是【大忽悠】)。
+* **叠层互动:** 随机性补位。
+* **点评:** “我在很多领域都有涉猎……虽然都不精。”
+
+#### 19. 【镜像】 (Mirror)
+* **效果:** 复制站在他**左边**那个棋子的所有职业和人群标签。
+* **叠层互动:** 位置相关的复制。
+* **点评:** “导师,我也想做师兄那个课题。”
+
+#### 20. 【负重训练】 (Weight Trainer)
+* **效果:** 他使所有羁绊的触发门槛 **+1**(例如需要 3 人才能触发 2 人效果),但一旦触发,效果提升 **150%**。
+* **叠层互动:** 提高门槛换取高强度的后期流派。
+* **点评:** “我们要走精英路线,不是什么阿猫阿狗都能进来的。”
+
+---
+
+### 设计总结
+
+通过引入这些对叠层机制进行**增益(Amplifiers)、伪装(Masqueraders)、破坏(Saboteurs)**的特质,游戏的策略空间被大幅拉伸了:
+
+* **初期:** 玩家可能会为了凑齐 (2) 层羁绊,不得不忍受一个带有【简历注水】但属性很差的棋子。
+* **中期:** 玩家可能会遇到一个【害群之马】的S级强卡,为了用他,不得不重构整个队伍,放弃某些羁绊。
+* **后期:** 玩家可能会利用【领头羊】或【负重训练】来打造一个极致的数值怪物团队。
+
+这使得“特质”不再是单调的Buff,而是连接“单卡”与“团队”的粘合剂。
\ No newline at end of file
diff --git a/scripts/Core/ContentRegistry.cs b/scripts/Core/ContentRegistry.cs
new file mode 100644
index 0000000..c51d215
--- /dev/null
+++ b/scripts/Core/ContentRegistry.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using Models;
+
+namespace Core;
+
+///
+/// 内容加载与合并(基础 + Mod)
+/// 设计说明:
+/// 1) 通过 IContentSource 抽象读取来源,支持 res:// 与 user://mods。
+/// 2) ContentRegistry 负责合并,同 Id 以后加载覆盖先加载。
+/// 注意事项:
+/// - 真实加载逻辑应避免在主线程做大规模 IO。
+/// 未来扩展:
+/// - 可加入“补丁合并策略”(例如列表合并/字段覆盖)。
+///
+public interface IContentSource
+{
+ int Priority { get; }
+ IEnumerable LoadAll() where T : class;
+}
+
+public enum ContentMergeMode
+{
+ Override,
+ KeepFirst
+}
+
+public sealed class ContentRegistry
+{
+ private readonly List _sources = new();
+ public ContentMergeMode MergeMode { get; set; } = ContentMergeMode.Override;
+
+ public void RegisterSource(IContentSource source)
+ {
+ _sources.Add(source);
+ _sources.Sort((a, b) => a.Priority.CompareTo(b.Priority));
+ }
+
+ public GameContentDatabase BuildDatabase()
+ {
+ var db = new GameContentDatabase();
+ Merge(db.Disciplines, LoadAll(), d => d.Header.Id);
+ Merge(db.Archetypes, LoadAll(), d => d.Header.Id);
+ Merge(db.Roles, LoadAll(), d => d.Header.Id);
+ Merge(db.Traits, LoadAll(), d => d.Header.Id);
+ Merge(db.Tasks, LoadAll(), d => d.Header.Id);
+ Merge(db.Items, LoadAll(), d => d.Header.Id);
+ Merge(db.Papers, LoadAll(), d => d.Header.Id);
+ Merge(db.RoguelitePerks, LoadAll(), d => d.Header.Id);
+ return db;
+ }
+
+ private IEnumerable LoadAll() where T : class
+ {
+ foreach (var source in _sources)
+ {
+ foreach (var item in source.LoadAll())
+ {
+ yield return item;
+ }
+ }
+ }
+
+ private void Merge(Dictionary target, IEnumerable items, Func idSelector) where T : class
+ {
+ foreach (var item in items)
+ {
+ var id = idSelector(item);
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ continue;
+ }
+
+ if (target.ContainsKey(id))
+ {
+ if (MergeMode == ContentMergeMode.Override)
+ {
+ target[id] = item;
+ }
+ }
+ else
+ {
+ target[id] = item;
+ }
+ }
+ }
+}
+
+///
+/// 资源读取示例:res:// 中的 .tres/.res
+/// 这里只给出接口骨架,具体解析留给后续实现。
+///
+public sealed class ResourceContentSource : IContentSource
+{
+ public int Priority { get; }
+ public List ResourcePaths { get; } = new();
+
+ public ResourceContentSource(int priority)
+ {
+ Priority = priority;
+ }
+
+ public IEnumerable LoadAll() where T : class
+ {
+ yield break;
+ }
+}
+
+///
+/// 资源读取示例:json/yaml/自定义格式(Mod 友好)
+///
+public sealed class JsonContentSource : IContentSource
+{
+ public int Priority { get; }
+ public List DataPaths { get; } = new();
+
+ public JsonContentSource(int priority)
+ {
+ Priority = priority;
+ }
+
+ public IEnumerable LoadAll() where T : class
+ {
+ yield break;
+ }
+}
+
diff --git a/scripts/Core/EventBus.cs b/scripts/Core/EventBus.cs
new file mode 100644
index 0000000..2ddc389
--- /dev/null
+++ b/scripts/Core/EventBus.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+
+namespace Core;
+
+///
+/// 轻量事件总线(解耦系统之间的通信)
+/// 设计说明:
+/// 1) 系统层发布 DomainEvent,View/Controller 可订阅。
+/// 2) 不依赖 Godot Signal,便于纯逻辑测试。
+/// 注意事项:
+/// - 事件为进程内同步调用,避免在事件里做耗时操作。
+/// 未来扩展:
+/// - 可加入“事件队列”和“延迟派发”以支持回合结算。
+///
+public sealed class DomainEventBus
+{
+ private readonly Dictionary> _handlers = new();
+
+ public void Subscribe(Action handler)
+ {
+ var type = typeof(T);
+ if (!_handlers.TryGetValue(type, out var list))
+ {
+ list = new List();
+ _handlers[type] = list;
+ }
+
+ list.Add(handler);
+ }
+
+ public void Unsubscribe(Action handler)
+ {
+ var type = typeof(T);
+ if (_handlers.TryGetValue(type, out var list))
+ {
+ list.Remove(handler);
+ }
+ }
+
+ public void Publish(T evt)
+ {
+ var type = typeof(T);
+ if (!_handlers.TryGetValue(type, out var list))
+ {
+ return;
+ }
+
+ foreach (var handler in list)
+ {
+ if (handler is Action typedHandler)
+ {
+ typedHandler.Invoke(evt);
+ }
+ }
+ }
+}
+
diff --git a/scripts/Core/GameController.cs b/scripts/Core/GameController.cs
new file mode 100644
index 0000000..a8d5089
--- /dev/null
+++ b/scripts/Core/GameController.cs
@@ -0,0 +1,43 @@
+using Models;
+
+namespace Core;
+
+///
+/// 高层控制器(面向 UI/输入)
+/// 设计说明:
+/// 1) Controller 只负责“指令式操作”,不直接做数值计算。
+/// 2) 复杂结算交给系统层(GameSystems)。
+/// 注意事项:
+/// - 需要可重入性,避免重复触发同一阶段切换。
+/// 未来扩展:
+/// - 可加入“命令队列”,用于回放或网络同步。
+///
+public sealed class GameController : IController
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void StartExecution()
+ {
+ if (_session.State.Turn.Phase != GamePhase.Planning) return;
+ _session.State.Turn.Phase = GamePhase.Execution;
+ }
+
+ public void EndExecution()
+ {
+ if (_session.State.Turn.Phase != GamePhase.Execution) return;
+ _session.State.Turn.Phase = GamePhase.Review;
+ }
+
+ public void StartNextTurn()
+ {
+ if (_session.State.Turn.Phase != GamePhase.Review) return;
+ _session.State.Turn.CurrentTurn++;
+ _session.State.Turn.Phase = GamePhase.Planning;
+ }
+}
+
diff --git a/scripts/Core/GameSession.cs b/scripts/Core/GameSession.cs
new file mode 100644
index 0000000..d668f6c
--- /dev/null
+++ b/scripts/Core/GameSession.cs
@@ -0,0 +1,47 @@
+using Models;
+
+namespace Core;
+
+///
+/// 游戏会话(运行时入口)
+/// 设计说明:
+/// 1) 将 State + Content + Systems + Services 组织为一个整体。
+/// 2) 便于在 Godot Node 中持有,避免散落的静态单例。
+/// 注意事项:
+/// - 真实项目中应通过工厂或依赖注入创建。
+/// 未来扩展:
+/// - 可加入“加载存档/保存存档/重置”接口。
+///
+public sealed class GameSession
+{
+ public GameState State { get; }
+ public GameContentDatabase Content { get; }
+ public DomainEventBus Events { get; }
+ public ILocalizationService Localization { get; }
+ public GameSystems Systems { get; }
+
+ public GameSession(GameState state, GameContentDatabase content, ILocalizationService localization, DomainEventBus events)
+ {
+ State = state;
+ Content = content;
+ Localization = localization;
+ Events = events;
+ Systems = new GameSystems();
+ Systems.Initialize(this);
+ }
+
+ public static GameSession CreateDefault()
+ {
+ var registry = new ContentRegistry();
+ var content = registry.BuildDatabase();
+ var localization = new GodotLocalizationService();
+ var events = new DomainEventBus();
+ return new GameSession(new GameState(), content, localization, events);
+ }
+
+ public void Tick(float delta)
+ {
+ Systems.Tick(delta);
+ }
+}
+
diff --git a/scripts/Core/GameSystems.cs b/scripts/Core/GameSystems.cs
new file mode 100644
index 0000000..81a1374
--- /dev/null
+++ b/scripts/Core/GameSystems.cs
@@ -0,0 +1,122 @@
+using Models;
+
+namespace Core;
+
+///
+/// 系统层骨架(负责模型变化,不直接处理输入/显示)
+/// 设计说明:
+/// 1) 每个系统只关注单一职责,形成高内聚低耦合结构。
+/// 2) 系统之间通过 GameSession 共享状态,通过事件总线通讯。
+/// 注意事项:
+/// - 系统逻辑应尽量“幂等”,便于回合重算与调试。
+/// 未来扩展:
+/// - 可加入“系统执行顺序配置”,支持 Mod 插入新系统。
+///
+public interface IGameSystem
+{
+ void Initialize(GameSession session);
+ void Tick(float delta);
+}
+
+public sealed class GameSystems
+{
+ public TurnSystem Turn { get; } = new();
+ public TaskSystem Task { get; } = new();
+ public EconomySystem Economy { get; } = new();
+ public SynergySystem Synergy { get; } = new();
+ public AssignmentSystem Assignment { get; } = new();
+
+ public void Initialize(GameSession session)
+ {
+ Turn.Initialize(session);
+ Task.Initialize(session);
+ Economy.Initialize(session);
+ Synergy.Initialize(session);
+ Assignment.Initialize(session);
+ }
+
+ public void Tick(float delta)
+ {
+ Turn.Tick(delta);
+ Task.Tick(delta);
+ Economy.Tick(delta);
+ Synergy.Tick(delta);
+ Assignment.Tick(delta);
+ }
+}
+
+public sealed class TurnSystem : IGameSystem
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void Tick(float delta)
+ {
+ // 预留:回合推进计时器/阶段切换
+ }
+}
+
+public sealed class TaskSystem : IGameSystem
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void Tick(float delta)
+ {
+ // 预留:执行阶段推进任务进度
+ }
+}
+
+public sealed class EconomySystem : IGameSystem
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void Tick(float delta)
+ {
+ // 预留:利息结算、工资消耗等
+ }
+}
+
+public sealed class SynergySystem : IGameSystem
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void Tick(float delta)
+ {
+ // 预留:根据 roster 统计羁绊层数
+ }
+}
+
+public sealed class AssignmentSystem : IGameSystem
+{
+ private GameSession _session;
+
+ public void Initialize(GameSession session)
+ {
+ _session = session;
+ }
+
+ public void Tick(float delta)
+ {
+ // 预留:人员分配、交接惩罚等
+ }
+}
+
diff --git a/scripts/Core/LocalizationService.cs b/scripts/Core/LocalizationService.cs
new file mode 100644
index 0000000..a8f8ba3
--- /dev/null
+++ b/scripts/Core/LocalizationService.cs
@@ -0,0 +1,46 @@
+using Godot;
+using Models;
+
+namespace Core;
+
+///
+/// i18n 服务抽象
+/// 设计说明:
+/// 1) Model 只持有 LocalizedText(Key/Fallback),具体翻译交给服务层。
+/// 2) 便于后续替换为自定义多语言系统或 Mod 词库。
+/// 注意事项:
+/// - TranslationServer 的翻译表应通过 Godot Editor 管理。
+/// 未来扩展:
+/// - 可加入“参数化翻译”和“运行时语言切换事件”。
+///
+public interface ILocalizationService
+{
+ string Translate(LocalizedText text);
+ string Translate(string key, string fallback = null);
+}
+
+public sealed class GodotLocalizationService : ILocalizationService
+{
+ public string Translate(LocalizedText text)
+ {
+ if (text == null) return string.Empty;
+ return Translate(text.Key, text.Fallback);
+ }
+
+ public string Translate(string key, string fallback = null)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ {
+ return fallback ?? string.Empty;
+ }
+
+ var translated = TranslationServer.Translate(key);
+ if (translated == key && !string.IsNullOrWhiteSpace(fallback))
+ {
+ return fallback;
+ }
+
+ return translated;
+ }
+}
+
diff --git a/scripts/Core/ModManifest.cs b/scripts/Core/ModManifest.cs
new file mode 100644
index 0000000..96d1a6a
--- /dev/null
+++ b/scripts/Core/ModManifest.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace Core;
+
+///
+/// Mod 清单与包信息
+/// 设计说明:
+/// 1) 通过 Manifest 提供版本/依赖/内容路径。
+/// 2) 与 ContentRegistry 配合,实现热加载或启动时加载。
+/// 注意事项:
+/// - 建议为每个 Mod 指定唯一 Id 与语义化版本。
+/// 未来扩展:
+/// - 可加入“加载开关/冲突检测/签名校验”。
+///
+public sealed class ModManifest
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Version { get; set; }
+ public List Dependencies { get; } = new();
+ public List ContentPaths { get; } = new();
+}
+
+public sealed class ModPackage
+{
+ public ModManifest Manifest { get; set; } = new();
+ public string RootPath { get; set; }
+}
+
diff --git a/scripts/Core/Mvc.cs b/scripts/Core/Mvc.cs
new file mode 100644
index 0000000..db6f81c
--- /dev/null
+++ b/scripts/Core/Mvc.cs
@@ -0,0 +1,39 @@
+using Godot;
+
+namespace Core;
+
+///
+/// MVC 基础接口
+/// 设计说明:
+/// 1) View 只负责显示与输入,Controller 负责修改 Model。
+/// 2) View 与 Model 通过 Bind 绑定,避免静态单例。
+/// 注意事项:
+/// - View 内不要直接写业务结算逻辑。
+/// 未来扩展:
+/// - 可加入“脏标记刷新”与“差量更新”以减少 UI 开销。
+///
+public interface IView
+{
+ void Bind(TModel model);
+}
+
+public interface IController
+{
+ void Initialize(GameSession session);
+}
+
+public abstract partial class ModelView : Node, IView
+{
+ public TModel Model { get; private set; }
+
+ public virtual void Bind(TModel model)
+ {
+ Model = model;
+ OnModelBound();
+ }
+
+ protected virtual void OnModelBound()
+ {
+ }
+}
+
diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs
index 8f67441..d6e7473 100644
--- a/scripts/GameManager.cs
+++ b/scripts/GameManager.cs
@@ -2,7 +2,18 @@ using Godot;
using System;
using System.Collections.Generic;
using Models;
+using Core;
+///
+/// 游戏主控制节点(MVC 的桥接层)
+/// 设计说明:
+/// 1) 负责初始化 GameSession,并把输入/信号转交给 Controller/System。
+/// 2) 仅做“流程调度”,不直接计算复杂数值,避免逻辑堆积。
+/// 注意事项:
+/// - View 与 Model 的绑定应逐步迁移到独立 View 脚本,避免在此类膨胀。
+/// 未来扩展:
+/// - 可拆分为 SceneRoot + UIController + DebugController,分层管理。
+///
public partial class GameManager : Node
{
///
@@ -12,25 +23,22 @@ public partial class GameManager : Node
public static string NextScene { get; set; } = null;
public static readonly Resource Arrow2X = ResourceLoader.Load("res://temp_res/kenney_ui-pack-space-expansion/PNG/Extra/Double/cursor_f.png");
- // --- Core Loop Definitions ---
- public enum GamePhase
- {
- Planning, // 筹备阶段:时间暂停,分配任务,购买设施
- Execution, // 执行阶段:时间流动,学生工作
- Review, // 结算阶段:回合结束,发工资,结算成果
- }
-
[Export]
public int MaxTurns = 30;
// --- Global State ---
- public static GamePhase CurrentPhase { get; private set; } = GamePhase.Planning;
- public static int CurrentTurn { get; private set; } = 1;
-
+ public GameSession Session { get; private set; }
+ public GameState State => Session?.State;
+ public GameController Controller { get; private set; }
+
+ public GamePhase CurrentPhase => State?.Turn.Phase ?? GamePhase.Planning;
+ public int CurrentTurn => State?.Turn.CurrentTurn ?? 1;
+
// --- Domain Model ---
- public MentorModel Mentor { get; private set; }
- public List Students { get; private set; } = new List();
- public List ActiveTasks { get; private set; } = new List();
+ public MentorModel Mentor => State?.Roster.Mentor;
+ public List Students => State?.Roster.Students;
+ public List ActiveTasks => State?.Tasks.ActiveTasks;
+ public EconomyState Economy => State?.Economy;
// --- Signals ---
[Signal] public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase
@@ -54,15 +62,21 @@ public partial class GameManager : Node
private void InitializeGame()
{
- CurrentTurn = 1;
- CurrentPhase = GamePhase.Planning;
+ Session = GameSession.CreateDefault();
+ Controller = new GameController();
+ Controller.Initialize(Session);
+ State.Turn.MaxTurns = MaxTurns;
+ State.Turn.CurrentTurn = 1;
+ State.Turn.Phase = GamePhase.Planning;
// MVP: Add a test student
var s1 = new StudentModel("张三");
Students.Add(s1);
// MVP: Add a test task
- var t1 = new Task("深度学习导论", TaskType.Paper, 1000f, 3);
+ var t1 = new TaskModel("深度学习导论", TaskKind.AcademicExploration, 1000f, 3);
+ t1.Reward.Money = 1000;
+ t1.Reward.Reputation = 5;
ActiveTasks.Add(t1);
GD.Print("Game Initialized. Phase: Planning, Turn: 1");
@@ -76,6 +90,8 @@ public partial class GameManager : Node
// Update game logic (timers, etc.)
// In a real implementation, this might manage the global timer for the day/week
}
+
+ Session?.Tick((float)delta);
}
///
@@ -85,7 +101,7 @@ public partial class GameManager : Node
{
if (CurrentPhase != GamePhase.Planning) return;
- CurrentPhase = GamePhase.Execution;
+ Controller.StartExecution();
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
GD.Print("Phase Changed: Execution");
@@ -99,7 +115,7 @@ public partial class GameManager : Node
{
if (CurrentPhase != GamePhase.Execution) return;
- CurrentPhase = GamePhase.Review;
+ Controller.EndExecution();
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
GD.Print("Phase Changed: Review");
@@ -114,17 +130,17 @@ public partial class GameManager : Node
// 1. Task progress check
foreach (var task in ActiveTasks)
{
- task.Deadline--;
+ task.Runtime.RemainingTurns--;
if (task.IsCompleted)
{
GD.Print($"Task {task.Name} Completed!");
- Mentor.Reputation += task.RewardReputation;
- Mentor.Money += task.RewardMoney;
+ Economy.Reputation += task.Reward.Reputation;
+ Economy.Money += task.Reward.Money;
}
else if (task.IsFailed)
{
GD.Print($"Task {task.Name} Failed!");
- Mentor.Reputation -= 10; // Penalty
+ Economy.Reputation -= 10; // Penalty
}
}
@@ -144,20 +160,20 @@ public partial class GameManager : Node
{
if (CurrentPhase != GamePhase.Review) return;
- CurrentTurn++;
- if (CurrentTurn > MaxTurns)
+ Controller.StartNextTurn();
+ if (CurrentTurn > State.Turn.MaxTurns)
{
GD.Print("Game Over!");
// Handle Game Over
return;
}
- CurrentPhase = GamePhase.Planning;
+ State.Turn.Phase = GamePhase.Planning;
EmitSignal(SignalName.TurnChanged, CurrentTurn);
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
// Refresh resources/AP
- Mentor.Energy.Current = Mentor.Energy.UpperThreshold;
+ Mentor.Resources.Energy.Current.Value = Mentor.Resources.Energy.UpperThreshold;
GD.Print($"Turn {CurrentTurn} Started. Phase: Planning");
}
diff --git a/scripts/Models/CoreIds.cs b/scripts/Models/CoreIds.cs
new file mode 100644
index 0000000..75f603e
--- /dev/null
+++ b/scripts/Models/CoreIds.cs
@@ -0,0 +1,55 @@
+namespace Models;
+
+///
+/// 内置内容 Id(方便代码引用,避免魔法字符串)
+/// 设计说明:
+/// 1) 仅列出核心/内置内容,Mod 内容不应写在这里。
+/// 2) Id 使用 “namespace:name” 形式,默认 namespace 为 core。
+/// 注意事项:
+/// - 更改 Id 会破坏存档兼容性,请谨慎修改。
+/// 未来扩展:
+/// - 可拆分为多个文件(Disciplines/Roles/Items)以减少体积。
+///
+public static class CoreIds
+{
+ public const string Namespace = "core";
+
+ // Disciplines
+ public const string DisciplineBiology = "core:discipline_biology";
+ public const string DisciplineChemistry = "core:discipline_chemistry";
+ public const string DisciplineEnvironment = "core:discipline_environment";
+ public const string DisciplineMaterials = "core:discipline_materials";
+ public const string DisciplineMedicine = "core:discipline_medicine";
+ public const string DisciplineComputer = "core:discipline_computer";
+ public const string DisciplineMath = "core:discipline_math";
+ public const string DisciplinePhysics = "core:discipline_physics";
+ public const string DisciplineMechanical = "core:discipline_mechanical";
+ public const string DisciplinePhilosophy = "core:discipline_philosophy";
+ public const string DisciplineEconomics = "core:discipline_economics";
+ public const string DisciplineLaw = "core:discipline_law";
+ public const string DisciplineLiterature = "core:discipline_literature";
+ public const string DisciplineAgriculture = "core:discipline_agriculture";
+ public const string DisciplineManagement = "core:discipline_management";
+ public const string DisciplineArt = "core:discipline_art";
+
+ // Archetypes
+ public const string ArchetypeGrinder = "core:archetype_grinder";
+ public const string ArchetypeSlacker = "core:archetype_slacker";
+ public const string ArchetypeElite = "core:archetype_elite";
+ public const string ArchetypeProdigy = "core:archetype_prodigy";
+ public const string ArchetypeMascot = "core:archetype_mascot";
+
+ // Roles
+ public const string RoleCoder = "core:role_coder";
+ public const string RoleWriter = "core:role_writer";
+ public const string RoleLabRat = "core:role_lab_rat";
+ public const string RolePresenter = "core:role_presenter";
+ public const string RoleScribe = "core:role_scribe";
+ public const string RoleOrator = "core:role_orator";
+ public const string RoleSteward = "core:role_steward";
+ public const string RoleAlchemist = "core:role_alchemist";
+ public const string RoleGeek = "core:role_geek";
+ public const string RoleSurveyor = "core:role_surveyor";
+ public const string RoleThinker = "core:role_thinker";
+}
+
diff --git a/scripts/Models/DefinitionSupport.cs b/scripts/Models/DefinitionSupport.cs
new file mode 100644
index 0000000..fb95a65
--- /dev/null
+++ b/scripts/Models/DefinitionSupport.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 定义数据的通用结构(i18n + 标识 + 标签)
+/// 设计说明:
+/// 1) LocalizedText 只保存 Key/Fallback,不做翻译,翻译交给服务层。
+/// 2) DefinitionHeader 用组合而非继承,避免所有定义都挂在同一基类。
+/// 3) Tags 统一用 string Id,便于 Mod 增删与过滤。
+/// 注意事项:
+/// - 资源路径统一使用 res:// 且小写下划线。
+/// - Id 建议使用 “namespace:name” 形式,例如 “core:discipline_biology”。
+/// 未来扩展:
+/// - 可加入 Icon/Color/SortOrder 以支持 UI 排序与分组。
+/// - 可加入 Source/Version 字段用于 Mod 冲突排查。
+///
+public sealed class LocalizedText
+{
+ public string Key { get; set; }
+ public string Fallback { get; set; }
+}
+
+public sealed class DefinitionHeader
+{
+ public string Id { get; set; }
+ public LocalizedText Name { get; set; } = new();
+ public LocalizedText Description { get; set; } = new();
+ public string IconPath { get; set; }
+ public List Tags { get; } = new();
+}
+
diff --git a/scripts/Models/DisciplineDefinitions.cs b/scripts/Models/DisciplineDefinitions.cs
new file mode 100644
index 0000000..900aa28
--- /dev/null
+++ b/scripts/Models/DisciplineDefinitions.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 学科定义(决定开局流派、核心资源循环与卡池)
+/// 设计说明:
+/// 1) 学科只描述“规则与池子”,具体资源结算由系统层执行。
+/// 2) Buff 使用 ModifierBundle + RuleIds 组合,便于非数值机制扩展。
+/// 注意事项:
+/// - DisciplineId 应稳定,用于存档与 Mod 兼容。
+/// 未来扩展:
+/// - 可加入“学科子方向”与“跨学科解锁条件”。
+///
+public sealed class DisciplineDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public DisciplineBuff Buff { get; set; } = new();
+
+ public List RolePoolIds { get; } = new();
+ public List ItemPoolIds { get; } = new();
+ public List TaskKeywordIds { get; } = new();
+}
+
+public sealed class DisciplineBuff
+{
+ public LocalizedText Name { get; set; } = new();
+ public LocalizedText Description { get; set; } = new();
+ public ModifierBundle Modifiers { get; set; } = new();
+}
+
diff --git a/scripts/Models/DomainEnums.cs b/scripts/Models/DomainEnums.cs
new file mode 100644
index 0000000..dc54745
--- /dev/null
+++ b/scripts/Models/DomainEnums.cs
@@ -0,0 +1,43 @@
+namespace Models;
+
+///
+/// 通用枚举(跨系统共享)
+/// 设计说明:
+/// 1) 这些枚举用于 Modifier 与系统逻辑,不直接绑定某个 UI。
+/// 2) 若未来需要 Mod 扩展新类型,可将枚举替换为 string Id。
+/// 注意事项:
+/// - 如果确实需要开放扩展,请避免在存档里写枚举的数值。
+/// 未来扩展:
+/// - 可加入“行动点/灵感/算力”等资源类型。
+///
+public enum AttributeType
+{
+ Academic,
+ Engineering,
+ Writing,
+ Financial,
+ Social,
+ Activation
+}
+
+public enum ResourceType
+{
+ Money,
+ Reputation,
+ ResearchPoints,
+ Paper,
+ Inspiration,
+ Time
+}
+
+public enum StatusType
+{
+ Stress,
+ Sanity,
+ Mood,
+ Stamina,
+ Loyalty,
+ Energy,
+ Health
+}
+
diff --git a/scripts/Models/GameContentDatabase.cs b/scripts/Models/GameContentDatabase.cs
new file mode 100644
index 0000000..608ff73
--- /dev/null
+++ b/scripts/Models/GameContentDatabase.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 游戏内容数据库(Definitions 聚合)
+/// 设计说明:
+/// 1) 将所有配置型数据集中到一个对象,便于系统层按 Id 索引。
+/// 2) 由 ContentRegistry 构建,支持 Mod 覆盖/合并。
+/// 注意事项:
+/// - 本对象只存配置,不存运行时状态。
+/// 未来扩展:
+/// - 可加入“索引缓存”(如按学科/稀有度过滤)。
+///
+public sealed class GameContentDatabase
+{
+ public Dictionary Disciplines { get; } = new();
+ public Dictionary Archetypes { get; } = new();
+ public Dictionary Roles { get; } = new();
+ public Dictionary Traits { get; } = new();
+ public Dictionary Tasks { get; } = new();
+ public Dictionary Items { get; } = new();
+ public Dictionary Papers { get; } = new();
+ public Dictionary RoguelitePerks { get; } = new();
+}
+
diff --git a/scripts/Models/GameState.cs b/scripts/Models/GameState.cs
new file mode 100644
index 0000000..e3e2531
--- /dev/null
+++ b/scripts/Models/GameState.cs
@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 运行时全局状态(GameState)
+/// 设计说明:
+/// 1) 将“局内状态”集中在一个聚合对象中,便于存档与调试快照。
+/// 2) 具体子状态按职责拆分:回合/经济/人员/任务/库存/羁绊。
+/// 3) Controller/System 只依赖这份数据,不直接操作 View。
+/// 注意事项:
+/// - 这是运行时数据,不是配置数据;配置请看 *Definition 类。
+/// - 禁止在这里引用 Godot Node,保持纯数据。
+/// 未来扩展:
+/// - 可加入“存档版本号”和“迁移器”,支持版本升级。
+/// - 可加入“局外 Roguelite 状态”,与局内共享接口。
+///
+public sealed class GameState
+{
+ public TurnState Turn { get; } = new();
+ public EconomyState Economy { get; } = new();
+ public RosterState Roster { get; } = new();
+ public TaskState Tasks { get; } = new();
+ public InventoryState Inventory { get; } = new();
+ public SynergyState Synergy { get; } = new();
+ public RogueliteState Roguelite { get; } = new();
+}
+
+public enum GamePhase
+{
+ Planning,
+ Execution,
+ Review
+}
+
+public sealed class TurnState
+{
+ public int CurrentTurn { get; set; } = 1;
+ public int MaxTurns { get; set; } = 30;
+ public GamePhase Phase { get; set; } = GamePhase.Planning;
+}
+
+public sealed class EconomyState
+{
+ public int Money { get; set; } = 50000;
+ public int Reputation { get; set; }
+ public int ResearchPoints { get; set; }
+ public float InterestRate { get; set; } = 0.0f;
+}
+
+public sealed class RosterState
+{
+ public MentorModel Mentor { get; set; } = new("Player");
+ public List Students { get; } = new();
+ public List Staffs { get; } = new();
+}
+
+public sealed class TaskState
+{
+ public List ActiveTasks { get; } = new();
+ public List CompletedTasks { get; } = new();
+ public List FailedTasks { get; } = new();
+}
+
+public sealed class InventoryState
+{
+ public Dictionary ItemCounts { get; } = new();
+ public Dictionary PaperCounts { get; } = new();
+}
+
+public sealed class SynergyState
+{
+ public Dictionary ArchetypeStacks { get; } = new();
+ public Dictionary RoleStacks { get; } = new();
+ public List ActiveSynergyIds { get; } = new();
+}
+
+public sealed class RogueliteState
+{
+ public List AlumniCardIds { get; } = new();
+ public List LegacyUnlockIds { get; } = new();
+ public int TitleRetentionLevel { get; set; }
+}
+
diff --git a/scripts/Models/ItemDefinitions.cs b/scripts/Models/ItemDefinitions.cs
new file mode 100644
index 0000000..6eeb83f
--- /dev/null
+++ b/scripts/Models/ItemDefinitions.cs
@@ -0,0 +1,59 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 装备/设施定义(通用 + 学科专用)
+/// 设计说明:
+/// 1) Facility 与 Equipment 统一为 ItemDefinition,不同类别通过枚举区分。
+/// 2) 效果使用 ModifierBundle + RuleIds 组合,便于特殊规则注入。
+/// 注意事项:
+/// - Placement/Slot 为 UI 与摆放系统提供约束,不做逻辑判断。
+/// 未来扩展:
+/// - 可加入“耐久度/维护成本/占地大小”等字段。
+///
+public enum ItemCategory
+{
+ Facility,
+ Equipment,
+ Consumable
+}
+
+public enum FacilityPlacement
+{
+ Lab,
+ ServerRoom,
+ Admin,
+ Library,
+ RestArea,
+ Field,
+ Global
+}
+
+public enum EquipmentSlot
+{
+ Tool,
+ Accessory,
+ Body,
+ Head,
+ Hand
+}
+
+public sealed class ItemDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public ItemCategory Category { get; set; }
+ public string DisciplineId { get; set; }
+ public FacilityPlacement Placement { get; set; }
+ public EquipmentSlot Slot { get; set; }
+ public ItemEffect Effect { get; set; } = new();
+ public bool IsUnique { get; set; }
+ public int MaxStack { get; set; } = 1;
+}
+
+public sealed class ItemEffect
+{
+ public ModifierBundle Modifiers { get; set; } = new();
+ public List RuleIds { get; } = new();
+}
+
diff --git a/scripts/Models/MentorModel.cs b/scripts/Models/MentorModel.cs
index a1bf6d5..c84b24b 100644
--- a/scripts/Models/MentorModel.cs
+++ b/scripts/Models/MentorModel.cs
@@ -1,70 +1,51 @@
namespace Models;
///
-/// 导师数据模型 (MentorModel)
-/// 玩家的数值状态。
-/// 单例模式。
+/// 导师数据模型 (MentorModel)
+/// 设计说明:
+/// 1) 改为组合式结构,不再使用单例,避免全局隐式依赖。
+/// 2) 导师只保留“角色自身状态”,资金/声望等全局资源归 EconomyState 管理。
+/// 3) 这样便于后续扩展多导师/多人模式或 Mod 玩法。
+/// 注意事项:
+/// - 构造函数不做复杂逻辑,具体初始数值交由 GameSession 初始化。
+/// 未来扩展:
+/// - 可加入“导师技能树/天赋树”组件,与肉鸽继承系统对接。
+/// - 可加入“导师光环”组件,在系统层计算对学生的影响范围。
///
-public class MentorModel : UnitModel
+public sealed class MentorModel
{
- private static MentorModel _instance;
+ public enum MentorModeType
+ {
+ Worker,
+ Manager
+ }
- public static MentorModel Instance
- {
- get
- {
- if (_instance == null)
- {
- // 如果需要默认名称,这里可以硬编码或改为 Lazy 加载
- _instance = new MentorModel("Player");
- }
- return _instance;
- }
- }
+ public UnitModel Core { get; }
+ public MentorResources Resources { get; }
+ public MentorModeType Mode { get; set; } = MentorModeType.Worker;
- public enum MentorModeType
- {
- Worker,
- Manager
- }
+ public string Name
+ {
+ get => Core.Name;
+ set => Core.Name = value;
+ }
- ///
- /// 精力值 (Energy)
- /// 用于释放技能(画饼、PUA、甚至亲自写代码)。
- /// 每回合恢复。
- ///
- public StatusValue Energy { get; set; }
+ public MentorModel(string name)
+ {
+ Core = new UnitModel(name);
+ Resources = new MentorResources();
+ }
+}
- ///
- /// 经费 (Money/Funds)
- /// 单位:元。用于发工资、买设备。
- ///
- public int Money { get; set; } = 50000;
+///
+/// 导师资源组件(角色自身能量池)
+///
+public sealed class MentorResources
+{
+ public StatusValue Energy { get; set; }
- ///
- /// 学术声望 (Reputation)
- /// 影响招生质量、项目申请成功率。
- ///
- public int Reputation { get; set; } = 0;
-
- ///
- /// 算力/数据资源 (ResearchPoints)
- /// 用于攻克高难度 AI 课题。
- ///
- public int ResearchPoints { get; set; } = 0;
-
- public MentorModeType Mode { get; set; } = MentorModeType.Worker;
-
- // 私有构造函数,防止外部实例化
- private MentorModel(string name) : base(name)
- {
- // 初始化 Energy,默认上限 10000 (100.00)
- Energy = new StatusValue(new PropertyValue(10000), new PropertyValue(10000), new PropertyValue(0));
- }
-
- // 如果需要重置单例(例如新游戏),可以添加一个重置方法
- public static void Reset(string name = "Player")
- {
- _instance = new MentorModel(name);
- }
-}
\ No newline at end of file
+ public MentorResources()
+ {
+ Energy = new StatusValue(100, 100, 0);
+ }
+}
diff --git a/scripts/Models/Modifiers.cs b/scripts/Models/Modifiers.cs
new file mode 100644
index 0000000..44485a0
--- /dev/null
+++ b/scripts/Models/Modifiers.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 统一的数值修饰结构(用于学科 Buff、羁绊、装备、特质)
+/// 设计说明:
+/// 1) 只表达“加成/倍率/规则”,不直接改变模型数值。
+/// 2) 计算由系统层统一处理,便于叠加与回滚。
+/// 注意事项:
+/// - 这里的 Multiplier 默认 1.0f,不是百分比。
+/// 未来扩展:
+/// - 可加入“条件触发”与“持续时间”字段。
+///
+public sealed class AttributeModifier
+{
+ public AttributeType Type { get; set; }
+ public float Add { get; set; }
+ public float Multiplier { get; set; } = 1.0f;
+}
+
+public sealed class StatusModifier
+{
+ public StatusType Type { get; set; }
+ public float Add { get; set; }
+ public float Multiplier { get; set; } = 1.0f;
+}
+
+public sealed class ResourceModifier
+{
+ public ResourceType Type { get; set; }
+ public int Add { get; set; }
+ public float Multiplier { get; set; } = 1.0f;
+}
+
+public sealed class ModifierBundle
+{
+ public List AttributeModifiers { get; } = new();
+ public List StatusModifiers { get; } = new();
+ public List ResourceModifiers { get; } = new();
+ public List RuleIds { get; } = new();
+}
+
diff --git a/scripts/Models/PaperDefinitions.cs b/scripts/Models/PaperDefinitions.cs
new file mode 100644
index 0000000..3b1a66a
--- /dev/null
+++ b/scripts/Models/PaperDefinitions.cs
@@ -0,0 +1,18 @@
+namespace Models;
+
+///
+/// 论文卡牌定义
+/// 设计说明:
+/// 1) 论文作为“消耗品资源”,用于申请纵向基金。
+/// 2) 论文等级只是一种枚举,真正的效果由系统层决定。
+/// 注意事项:
+/// - 若需要“引用率/影响因子”,可在此扩展字段。
+/// 未来扩展:
+/// - 可加入“论文主题标签”,影响基金/任务匹配度。
+///
+public sealed class PaperDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public PaperRank Rank { get; set; }
+}
+
diff --git a/scripts/Models/PropertyValue.cs b/scripts/Models/PropertyValue.cs
index 55b9584..b4a128b 100644
--- a/scripts/Models/PropertyValue.cs
+++ b/scripts/Models/PropertyValue.cs
@@ -3,120 +3,129 @@ using System;
namespace Models;
///
-/// 属性值类型
-/// 封装了整型数值,提供最大值限制(100)和显示转换。
+/// 属性值类型(通用数值封装)
+/// 设计说明:
+/// 1) 过去固定使用 0-100 的范围,导致“精力/算力”等大数值被强行截断。
+/// 2) 现在改为“每个实例自带范围”,默认仍然是 0-100,兼顾直觉和可扩展性。
+/// 3) 这个类仅承担“数值与范围”的职责,不负责业务含义,便于在 MVC 中被复用。
+/// 注意事项:
+/// - 运算符会保留左操作数的范围(或右操作数的范围),避免范围丢失。
+/// - 如果需要无上限/无下限,请显式传入更大的 Max/更小的 Min。
+/// 未来扩展:
+/// - 可加入“软上限/软下限”和“可见范围”等 UI 表现字段。
+/// - 可增加数值变化事件,以便 View 层更高效地刷新。
///
-public class PropertyValue
+public sealed class PropertyValue
{
- public const int Min = 0;
- public const int Max = 100;
-
- private float _value;
+ public const float DefaultMin = 0f;
+ public const float DefaultMax = 100f;
- ///
- /// 获取或设置当前值。
- /// 设置时会自动限制在 [Min, Max] 范围内。
- ///
- public float Value
- {
- get => _value;
- set
- {
- _value = value;
- if (_value > Max) _value = Max;
- if (_value < Min) _value = Min;
- }
- }
+ public float Min { get; }
+ public float Max { get; }
- public PropertyValue(int value)
- {
- Value = value;
- }
-
- public PropertyValue(float value)
- {
- Value = value;
- }
+ private float _value;
- public PropertyValue() : this(0) { }
+ public float Value
+ {
+ get => _value;
+ set => _value = Clamp(value, Min, Max);
+ }
- ///
- /// 获取显示值 (Value / 100.0f)
- ///
- public int Display() => (int)_value;
+ public PropertyValue(float value = 0, float min = DefaultMin, float max = DefaultMax)
+ {
+ if (max < min)
+ {
+ // 兜底修正,避免传参错误导致全局异常
+ (min, max) = (max, min);
+ }
- public override string ToString()
- {
- return Display().ToString();
- }
+ Min = min;
+ Max = max;
+ Value = value;
+ }
- // --- 隐式转换 (实现“重载等号”效果) ---
+ public PropertyValue(int value, float min = DefaultMin, float max = DefaultMax)
+ : this((float)value, min, max)
+ {
+ }
- // 允许 int 直接赋值给 PropertyValue (创建新实例)
- public static implicit operator PropertyValue(int value)
- {
- return new PropertyValue(value);
- }
+ public int DisplayInt() => (int)_value;
- // 允许 int 直接赋值给 PropertyValue (创建新实例)
- public static implicit operator PropertyValue(float value)
- {
- return new PropertyValue(value);
- }
-
- // 允许 PropertyValue 像 int 一样使用
- public static implicit operator int(PropertyValue p)
- {
- return (int)p._value;
- }
+ public float Normalized
+ {
+ get
+ {
+ if (Max <= Min) return 0f;
+ return (Value - Min) / (Max - Min);
+ }
+ }
- // 允许 PropertyValue 像 float 一样使用
- public static implicit operator float(PropertyValue p)
- {
- return p._value;
- }
+ public PropertyValue Clone() => new(Value, Min, Max);
- // --- 运算符重载 (支持 int 计算) ---
+ public void Add(float delta) => Value += delta;
- public static PropertyValue operator +(PropertyValue a, int b) => new(a._value + b);
- public static PropertyValue operator +(int a, PropertyValue b) => new(a + b._value);
-
- public static PropertyValue operator -(PropertyValue a, int b) => new(a._value - b);
- public static PropertyValue operator -(int a, PropertyValue b) => new(a - b._value);
+ public void Subtract(float delta) => Value -= delta;
- public static PropertyValue operator *(PropertyValue a, int b) => new(a._value * b);
- public static PropertyValue operator *(int a, PropertyValue b) => new(a * b._value);
+ public override string ToString() => DisplayInt().ToString();
- public static PropertyValue operator /(PropertyValue a, int b)
- {
- return b == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b);
- }
- public static PropertyValue operator /(int a, PropertyValue b)
- {
- return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a / b._value);
- }
+ // --- 隐式转换(用于简化调用,但不改变范围语义) ---
+ public static implicit operator PropertyValue(int value)
+ {
+ return new PropertyValue(value);
+ }
- // --- 运算符重载 (支持 float 计算) ---
+ public static implicit operator PropertyValue(float value)
+ {
+ return new PropertyValue(value);
+ }
- public static PropertyValue operator +(PropertyValue a, float b) => new(a._value + b);
- public static PropertyValue operator +(float a, PropertyValue b) => new(a + b._value);
+ public static implicit operator int(PropertyValue p)
+ {
+ return (int)p._value;
+ }
- public static PropertyValue operator -(PropertyValue a, float b) => new(a._value - b);
- public static PropertyValue operator -(float a, PropertyValue b) => new(a - b._value);
+ public static implicit operator float(PropertyValue p)
+ {
+ return p._value;
+ }
- public static PropertyValue operator *(PropertyValue a, float b) => new(a._value * b);
- public static PropertyValue operator *(float a, PropertyValue b) => new(a * b._value);
+ // --- 运算符重载(保留范围) ---
+ public static PropertyValue operator +(PropertyValue a, int b) => new(a._value + b, a.Min, a.Max);
+ public static PropertyValue operator +(int a, PropertyValue b) => new(a + b._value, b.Min, b.Max);
+ public static PropertyValue operator -(PropertyValue a, int b) => new(a._value - b, a.Min, a.Max);
+ public static PropertyValue operator -(int a, PropertyValue b) => new(a - b._value, b.Min, b.Max);
+ public static PropertyValue operator *(PropertyValue a, int b) => new(a._value * b, a.Min, a.Max);
+ public static PropertyValue operator *(int a, PropertyValue b) => new(a * b._value, b.Min, b.Max);
+ public static PropertyValue operator /(PropertyValue a, int b)
+ {
+ return b == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b, a.Min, a.Max);
+ }
+ public static PropertyValue operator /(int a, PropertyValue b)
+ {
+ return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a / b._value, b.Min, b.Max);
+ }
- public static PropertyValue operator /(PropertyValue a, float b) => new(a._value / b);
- public static PropertyValue operator /(float a, PropertyValue b) => new(a / b._value);
-
- // --- 运算符重载 (PropertyValue 之间) ---
-
- public static PropertyValue operator +(PropertyValue a, PropertyValue b) => new(a._value + b._value);
- public static PropertyValue operator -(PropertyValue a, PropertyValue b) => new(a._value - b._value);
- public static PropertyValue operator *(PropertyValue a, PropertyValue b) => new(a._value * b._value);
- public static PropertyValue operator /(PropertyValue a, PropertyValue b)
- {
- return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b._value);
- }
-}
\ No newline at end of file
+ public static PropertyValue operator +(PropertyValue a, float b) => new(a._value + b, a.Min, a.Max);
+ public static PropertyValue operator +(float a, PropertyValue b) => new(a + b._value, b.Min, b.Max);
+ public static PropertyValue operator -(PropertyValue a, float b) => new(a._value - b, a.Min, a.Max);
+ public static PropertyValue operator -(float a, PropertyValue b) => new(a - b._value, b.Min, b.Max);
+ public static PropertyValue operator *(PropertyValue a, float b) => new(a._value * b, a.Min, a.Max);
+ public static PropertyValue operator *(float a, PropertyValue b) => new(a * b._value, b.Min, b.Max);
+ public static PropertyValue operator /(PropertyValue a, float b) => new(a._value / b, a.Min, a.Max);
+ public static PropertyValue operator /(float a, PropertyValue b) => new(a / b._value, b.Min, b.Max);
+
+ public static PropertyValue operator +(PropertyValue a, PropertyValue b) => new(a._value + b._value, a.Min, a.Max);
+ public static PropertyValue operator -(PropertyValue a, PropertyValue b) => new(a._value - b._value, a.Min, a.Max);
+ public static PropertyValue operator *(PropertyValue a, PropertyValue b) => new(a._value * b._value, a.Min, a.Max);
+ public static PropertyValue operator /(PropertyValue a, PropertyValue b)
+ {
+ return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b._value, a.Min, a.Max);
+ }
+
+ private static float Clamp(float value, float min, float max)
+ {
+ if (value < min) return min;
+ if (value > max) return max;
+ return value;
+ }
+}
diff --git a/scripts/Models/RogueliteDefinitions.cs b/scripts/Models/RogueliteDefinitions.cs
new file mode 100644
index 0000000..160257a
--- /dev/null
+++ b/scripts/Models/RogueliteDefinitions.cs
@@ -0,0 +1,27 @@
+namespace Models;
+
+///
+/// 肉鸽继承/局外成长定义
+/// 设计说明:
+/// 1) 对应“校友录/祖传代码/职称保留”等局外系统。
+/// 2) 这里只描述解锁项,实际解锁逻辑由系统层处理。
+/// 注意事项:
+/// - 与存档绑定的解锁项需要稳定 Id。
+/// 未来扩展:
+/// - 可加入“局外科技树”与“多周目难度曲线”。
+///
+public enum RoguelitePerkType
+{
+ AlumniCard,
+ LegacyProgress,
+ TitleRetention
+}
+
+public sealed class RoguelitePerkDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public RoguelitePerkType Type { get; set; }
+ public ModifierBundle Modifiers { get; set; } = new();
+ public int MaxLevel { get; set; } = 1;
+}
+
diff --git a/scripts/Models/StaffModel.cs b/scripts/Models/StaffModel.cs
new file mode 100644
index 0000000..f7e737f
--- /dev/null
+++ b/scripts/Models/StaffModel.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Models;
+
+///
+/// 雇员/合作人数据模型 (StaffModel)
+/// 设计说明:
+/// 1) 覆盖“博后/小老师”等非学生角色,强调长期合作与野心值。
+/// 2) 继续复用 UnitModel 组件,保证与学生/导师统一的数据接口。
+/// 注意事项:
+/// - Staff 行为逻辑应由系统层决定(例如背刺/跳槽)。
+/// 未来扩展:
+/// - 可加入“合作者关系网”和“派系归属”。
+///
+public sealed class StaffModel
+{
+ public enum StaffType
+ {
+ PostDoc,
+ JuniorFaculty
+ }
+
+ public UnitModel Core { get; }
+ public StaffType Type { get; private set; }
+ public StaffMotivation Motivation { get; }
+
+ public string Name
+ {
+ get => Core.Name;
+ set => Core.Name = value;
+ }
+
+ public StaffModel(string name, StaffType type, Random random = null)
+ {
+ Core = new UnitModel(name, random);
+ Type = type;
+ Motivation = new StaffMotivation();
+ }
+}
+
+public sealed class StaffMotivation
+{
+ public StatusValue Ambition { get; set; } = new(50, 100, 0);
+ public StatusValue Loyalty { get; set; } = new(70, 100, 0);
+ public StatusValue ContractTurns { get; set; } = new(6, 12, 0);
+}
+
diff --git a/scripts/Models/StatusValue.cs b/scripts/Models/StatusValue.cs
index 4722ad7..1c0b677 100644
--- a/scripts/Models/StatusValue.cs
+++ b/scripts/Models/StatusValue.cs
@@ -4,98 +4,81 @@ namespace Models;
///
/// 状态值 (StatusValue)
-/// 维护当前状态值以及上限和下限阈值。
+/// 设计说明:
+/// 1) 这是“带阈值的数值”,用于压力、体力、忠诚等需要触发事件的状态。
+/// 2) 当前值由 PropertyValue 承担“范围约束”,阈值只负责触发行为。
+/// 3) 事件由 Model 层抛出,Controller/系统层订阅,View 不直接耦合。
+/// 注意事项:
+/// - Upper/Lower 默认等于当前范围的上/下限,保持语义直观。
+/// - 如果需要“软阈值”,请在系统中根据 Normalized 手动判断。
+/// 未来扩展:
+/// - 可增加“阈值触发冷却/抑制器”,避免频繁触发。
+/// - 可加入“变化速率”以支持回合/时间驱动的增减。
///
-public class StatusValue
+public sealed class StatusValue
{
- private PropertyValue _current;
- private PropertyValue _upperThreshold;
- private PropertyValue _lowerThreshold;
+ private PropertyValue _current;
- ///
- /// 当 Current >= UpperThreshold 时触发此事件
- ///
- public event Action OnUpperThresholdReached;
+ public event Action OnUpperThresholdReached;
+ public event Action OnLowerThresholdReached;
- ///
- /// 当 Current <= LowerThreshold 时触发此事件
- ///
- public event Action OnLowerThresholdReached;
+ public PropertyValue Current
+ {
+ get => _current;
+ set
+ {
+ _current = value;
+ CheckThresholds();
+ }
+ }
- ///
- /// 当前状态值
- /// 修改时会自动检测阈值。
- ///
- public PropertyValue Current
- {
- get => _current;
- set
- {
- _current = value;
- CheckThresholds();
- }
- }
+ public float UpperThreshold { get; set; }
+ public float LowerThreshold { get; set; }
- ///
- /// 上限阈值
- ///
- public PropertyValue UpperThreshold
- {
- get => _upperThreshold;
- set
- {
- _upperThreshold = value;
- CheckThresholds();
- }
- }
+ public StatusValue(int current = 0, int upper = 100, int lower = 0)
+ {
+ _current = new PropertyValue(current, lower, upper);
+ UpperThreshold = upper;
+ LowerThreshold = lower;
+ }
- ///
- /// 下限阈值
- ///
- public PropertyValue LowerThreshold
- {
- get => _lowerThreshold;
- set
- {
- _lowerThreshold = value;
- CheckThresholds();
- }
- }
+ public StatusValue(float current, float upper, float lower)
+ {
+ _current = new PropertyValue(current, lower, upper);
+ UpperThreshold = upper;
+ LowerThreshold = lower;
+ }
- public StatusValue(int current = 0, int upper = 100, int lower = 0)
- {
- _current = new PropertyValue(current);
- _upperThreshold = new PropertyValue(upper);
- _lowerThreshold = new PropertyValue(lower);
- }
-
- public StatusValue(PropertyValue current, PropertyValue upper, PropertyValue lower)
- {
- _current = current ?? new PropertyValue(0);
- _upperThreshold = upper ?? new PropertyValue(100);
- _lowerThreshold = lower ?? new PropertyValue(0);
- }
+ public StatusValue(PropertyValue current, float upperThreshold, float lowerThreshold)
+ {
+ _current = current ?? new PropertyValue(0);
+ UpperThreshold = upperThreshold;
+ LowerThreshold = lowerThreshold;
+ CheckThresholds();
+ }
- private void CheckThresholds()
- {
- if ((int)_current >= (int)_upperThreshold)
- {
- OnUpperThresholdReached?.Invoke();
- }
+ private void CheckThresholds()
+ {
+ if (_current.Value >= UpperThreshold)
+ {
+ OnUpperThresholdReached?.Invoke();
+ }
- if ((int)_current <= (int)_lowerThreshold)
- {
- OnLowerThresholdReached?.Invoke();
- }
- }
+ if (_current.Value <= LowerThreshold)
+ {
+ OnLowerThresholdReached?.Invoke();
+ }
+ }
- public void Add(int value)
- {
- Current += value; // 触发 setter -> CheckThresholds
- }
-
- public void Subtract(int value)
- {
- Current -= value; // 触发 setter -> CheckThresholds
- }
-}
\ No newline at end of file
+ public void Add(float value)
+ {
+ _current.Add(value);
+ CheckThresholds();
+ }
+
+ public void Subtract(float value)
+ {
+ _current.Subtract(value);
+ CheckThresholds();
+ }
+}
diff --git a/scripts/Models/StudentModel.cs b/scripts/Models/StudentModel.cs
index d475a6e..ca749a7 100644
--- a/scripts/Models/StudentModel.cs
+++ b/scripts/Models/StudentModel.cs
@@ -5,59 +5,70 @@ namespace Models;
///
/// 学生数据模型 (StudentModel)
-/// 包含学生的数值、状态和特质。
-/// 与 Godot 的 Node (View) 分离,便于序列化和逻辑处理。
+/// 设计说明:
+/// 1) 不再继承 UnitModel,而是通过 Core 组合通用组件(属性/状态/标签/装备等)。
+/// 2) 学生特有内容(年级/体力/忠诚/署名贡献)放在独立组件中,保持高内聚。
+/// 3) Model 层仅负责数据,不做 Godot 行为调用,方便后续存档与 Mod 注入。
+/// 注意事项:
+/// - Traits 实际上是标签(Tag),统一走 Core.Tags.TraitIds,避免双份真相。
+/// - Grade 仅表示“学年进度”,不是学术能力值。
+/// 未来扩展:
+/// - 可新增“毕业/退学事件队列”,由系统层驱动。
+/// - 可新增“论文署名历史”用于复盘与羁绊叠层回溯。
///
-public class StudentModel: UnitModel
+public sealed class StudentModel
{
- public enum StudentType
- {
- MasterCandidate,
- DoctorCandidate
- }
-
- // --- 静态属性 (Property) ---
- ///
- /// 学生类型
- ///
- public StudentType Type { get; private set; }
-
- // --- 动态状态 (Status) ---
-
- ///
- /// 体力 (Stamina)
- /// 范围 0-100。工作消耗体力,休息恢复。体力过低效率下降。
- ///
- public StatusValue Stamina { get; set; }
-
- ///
- /// 忠诚度 (Loyalty)
- /// 范围 0-100。过低可能跳槽或举报。
- ///
- public StatusValue Loyalty { get; set; }
-
- ///
- /// 年级
- ///
- public StatusValue Grade { get; set; }
-
- ///
- /// 记录对每个 Task 的贡献量,用于署名分配
- ///
- public Dictionary Contributions { get; set; }
+ public enum StudentType
+ {
+ MasterCandidate,
+ DoctorCandidate
+ }
- ///
- /// 特质列表 (如 "DDL战士", "卷王")
- ///
- public List Traits { get; set; } = [];
+ public UnitModel Core { get; }
+ public StudentType Type { get; private set; }
+ public StudentProgress Progress { get; }
+ public StudentContributions Contributions { get; }
- public StudentModel(string name) : base(name)
- {
- var random = new Random();
- Stamina = new StatusValue(random.Next(100),100, 0);
- Loyalty = new StatusValue(80, 100, 0);
- Type = random.Next(2) == 0 ? StudentType.MasterCandidate : StudentType.DoctorCandidate;
- Grade = new StatusValue(0, Type == StudentType.DoctorCandidate ? 6 : 3);
- }
+ public string Name
+ {
+ get => Core.Name;
+ set => Core.Name = value;
+ }
+
+ public List Traits => Core.Tags.TraitIds;
+
+ public StudentModel(string name, Random random = null)
+ {
+ var rng = random ?? Random.Shared;
+ Core = new UnitModel(name, rng);
+ Type = rng.Next(2) == 0 ? StudentType.MasterCandidate : StudentType.DoctorCandidate;
+ Progress = new StudentProgress(Type);
+ Contributions = new StudentContributions();
+ }
}
+///
+/// 学生进度组件(只关心学生生命周期)
+///
+public sealed class StudentProgress
+{
+ public StatusValue Stamina { get; set; }
+ public StatusValue Loyalty { get; set; }
+ public PropertyValue Grade { get; set; }
+
+ public StudentProgress(StudentModel.StudentType type)
+ {
+ Stamina = new StatusValue(100, 100, 0);
+ Loyalty = new StatusValue(80, 100, 0);
+ var maxGrade = type == StudentModel.StudentType.DoctorCandidate ? 6 : 3;
+ Grade = new PropertyValue(0, 0, maxGrade);
+ }
+}
+
+///
+/// 署名贡献记录(任务 -> 贡献值)
+///
+public sealed class StudentContributions
+{
+ public Dictionary ByTask { get; } = new();
+}
diff --git a/scripts/Models/SynergyDefinitions.cs b/scripts/Models/SynergyDefinitions.cs
new file mode 100644
index 0000000..0d475e2
--- /dev/null
+++ b/scripts/Models/SynergyDefinitions.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 羁绊/职业/特质定义(“人群画像 + 职能分工”)
+/// 设计说明:
+/// 1) Archetype/Role 采用分层 Tier 模式,满足 2/4/6 叠层设计。
+/// 2) Trait 为单体特质,不叠层,仅提供修饰与规则。
+/// 注意事项:
+/// - Tier 的规则效果写在 RuleIds 中,由系统层解释执行。
+/// 未来扩展:
+/// - 可加入“跨学科混合羁绊”条件与视觉特效引用。
+///
+public sealed class ArchetypeDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public List Tiers { get; } = new();
+}
+
+public sealed class RoleDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public List Tiers { get; } = new();
+
+ // 如果是学科限定角色(如炼金术士/极客),在这里配置允许的学科 Id。
+ public List AllowedDisciplineIds { get; } = new();
+}
+
+public sealed class TraitDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public ModifierBundle Modifiers { get; set; } = new();
+ public List RuleIds { get; } = new();
+}
+
+public sealed class SynergyTier
+{
+ public int RequiredCount { get; set; }
+ public ModifierBundle Modifiers { get; set; } = new();
+ public List RuleIds { get; } = new();
+}
+
diff --git a/scripts/Models/Task.cs b/scripts/Models/Task.cs
index c11e96b..b779315 100644
--- a/scripts/Models/Task.cs
+++ b/scripts/Models/Task.cs
@@ -4,94 +4,76 @@ using System.Collections.Generic;
namespace Models;
///
-/// 任务类型枚举
+/// 任务运行时模型 (TaskModel)
+/// 设计说明:
+/// 1) TaskDefinition 描述“配置”,TaskModel 描述“运行时进度”。二者分离利于 Mod 追加内容。
+/// 2) 任务状态只保存必要字段,展示文本统一由 Definition + i18n 输出。
+/// 3) 任务参与单位只记录 Id,避免模型直接引用 Node。
+/// 注意事项:
+/// - 如果没有 DefinitionId,可使用 Name 作为临时演示数据。
+/// - Deadline 语义是“剩余回合数”,由 TurnSystem 递减。
+/// 未来扩展:
+/// - 可加入阶段状态(灵感/写作/实验/审核),与 UI 进度条分段对应。
+/// - 可加入失败原因/完成评级,用于结算与肉鸽评分。
///
-public enum TaskType
-{
- Paper, // 论文:重学术(Intelligence) + 表达(Expression)
- Project, // 项目:重工程(Strength) + 学术(Intelligence)
- Admin // 杂务:消耗体力,低收益,但必须做
+public sealed class TaskModel
+{
+ public Guid Id { get; } = Guid.NewGuid();
+ public string DefinitionId { get; set; }
+ public string Name { get; set; }
+ public TaskTrack Track { get; set; }
+ public TaskKind Kind { get; set; }
+ public TaskDifficulty Difficulty { get; set; }
+
+ public TaskRuntime Runtime { get; }
+ public TaskRewardSnapshot Reward { get; set; } = new();
+ public List AssignedUnitIds { get; } = new();
+
+ public bool IsCompleted => Runtime.CurrentProgress >= Runtime.TotalWorkload;
+ public bool IsFailed => Runtime.RemainingTurns <= 0 && !IsCompleted;
+
+ public TaskModel(string name, TaskKind kind, float workload, int deadline)
+ {
+ Name = name;
+ Kind = kind;
+ Track = TaskTrack.LabProject;
+ Difficulty = TaskDifficulty.Standard;
+ Runtime = new TaskRuntime(workload, deadline);
+ }
+
+ public void AddProgress(float amount)
+ {
+ Runtime.CurrentProgress += amount;
+ if (Runtime.CurrentProgress > Runtime.TotalWorkload)
+ {
+ Runtime.CurrentProgress = Runtime.TotalWorkload;
+ }
+ }
}
///
-/// 任务 (Task)
-/// 核心游戏对象之一,相当于地图上的“怪物”。
-/// 学生需要“攻击”任务(消耗工作量)来完成它。
+/// 任务运行参数(进度与截止)
///
-public class Task
+public sealed class TaskRuntime
{
- public Guid Id { get; private set; } = Guid.NewGuid();
-
- ///
- /// 任务名称
- ///
- public string Name { get; set; }
-
- ///
- /// 任务类型
- ///
- public TaskType Type { get; set; }
-
- ///
- /// 总工作量 (HP)
- ///
- public float TotalWorkload { get; set; }
-
- ///
- /// 当前进度
- ///
- public float CurrentProgress { get; set; }
-
- ///
- /// 难度系数 (Defense)
- /// 影响学生攻克该任务的效率。若学生能力低于难度,效率会大幅下降。
- ///
- public float Difficulty { get; set; }
-
- ///
- /// 截止日期 (剩余回合数)
- /// 归零时如果未完成,触发失败惩罚。
- ///
- public int Deadline { get; set; }
+ public float TotalWorkload { get; set; }
+ public float CurrentProgress { get; set; }
+ public float DifficultyScale { get; set; } = 1.0f;
+ public int RemainingTurns { get; set; }
- ///
- /// 奖励:经费
- ///
- public int RewardMoney { get; set; }
-
- ///
- /// 奖励:声望
- ///
- public int RewardReputation { get; set; }
-
- ///
- /// 任务是否已完成
- ///
- public bool IsCompleted => CurrentProgress >= TotalWorkload;
-
- ///
- /// 任务是否已失败(超时未完成)
- ///
- public bool IsFailed => Deadline <= 0 && !IsCompleted;
-
- public Task(string name, TaskType type, float workload, int deadline)
- {
- Name = name;
- Type = type;
- TotalWorkload = workload;
- Deadline = deadline;
- CurrentProgress = 0;
- Difficulty = 1.0f;
- }
-
- ///
- /// 增加进度
- ///
- /// 工作量数值
- public void AddProgress(float amount)
- {
- CurrentProgress += amount;
- if (CurrentProgress > TotalWorkload) CurrentProgress = TotalWorkload;
- }
+ public TaskRuntime(float totalWorkload, int remainingTurns)
+ {
+ TotalWorkload = totalWorkload;
+ RemainingTurns = remainingTurns;
+ }
}
+///
+/// 运行时奖励快照(避免 Definition 被动态修改)
+///
+public sealed class TaskRewardSnapshot
+{
+ public int Money { get; set; }
+ public int Reputation { get; set; }
+ public List PaperIds { get; } = new();
+}
diff --git a/scripts/Models/TaskDefinitions.cs b/scripts/Models/TaskDefinitions.cs
new file mode 100644
index 0000000..9105193
--- /dev/null
+++ b/scripts/Models/TaskDefinitions.cs
@@ -0,0 +1,101 @@
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// 任务定义与枚举(配置层)
+/// 设计说明:
+/// 1) Definition 描述“任务是什么”,TaskModel 描述“任务正在发生什么”。
+/// 2) 任务类别与难度与设计文档一致,方便 UI/系统层做分流。
+/// 3) Requirements/Rewards 以组合结构表示,便于 Mod 追加新条件。
+/// 注意事项:
+/// - 不在此处写具体数值表,仅提供结构骨架。
+/// - 业务系统应优先读取 Definition,而不是硬编码逻辑。
+/// 未来扩展:
+/// - 可加入“阶段式任务”与“事件触发任务”字段。
+/// - 可加入“动态难度曲线”,用于 Roguelite 调整强度。
+///
+public enum TaskTrack
+{
+ Tenure, // 生涯考核
+ LabProject // 实验室项目
+}
+
+public enum TaskKind
+{
+ AcademicExploration, // 学术探索:产出论文
+ GrantVertical, // 纵向基金:强制门票
+ GrantHorizontal, // 横向项目:搞钱
+ Administrative, // 行政事务:干扰项
+ Milestone, // 目标型考核
+ Conference // 会议大招
+}
+
+public enum TaskDifficulty
+{
+ Water,
+ Standard,
+ Hardcore,
+ BlackBox
+}
+
+public enum PaperRank
+{
+ C,
+ B,
+ A,
+ S,
+ SPlus
+}
+
+public sealed class TaskDefinition
+{
+ public DefinitionHeader Header { get; set; } = new();
+ public TaskTrack Track { get; set; }
+ public TaskKind Kind { get; set; }
+ public TaskDifficulty Difficulty { get; set; }
+ public TaskRuntimeConfig Runtime { get; set; } = new();
+ public TaskRequirements Requirements { get; set; } = new();
+ public TaskRewards Rewards { get; set; } = new();
+
+ public List AllowedDisciplineIds { get; } = new();
+ public List RecommendedRoleIds { get; } = new();
+ public List RecommendedArchetypeIds { get; } = new();
+}
+
+public sealed class TaskRuntimeConfig
+{
+ public float Workload { get; set; }
+ public int DeadlineTurns { get; set; }
+ public float DifficultyScale { get; set; } = 1.0f;
+ public int MaxAssignedSlots { get; set; } = 3;
+}
+
+public sealed class TaskRequirements
+{
+ public List PaperTickets { get; } = new();
+ public List AttributeChecks { get; } = new();
+ public List RequiredRoleIds { get; } = new();
+}
+
+public sealed class PaperRequirement
+{
+ public PaperRank Rank { get; set; }
+ public int Count { get; set; }
+}
+
+public sealed class AttributeRequirement
+{
+ public AttributeType Type { get; set; }
+ public int MinValue { get; set; }
+}
+
+public sealed class TaskRewards
+{
+ public int Money { get; set; }
+ public int Reputation { get; set; }
+ public List PaperIds { get; } = new();
+ public List ItemIds { get; } = new();
+ public List BuffIds { get; } = new();
+}
+
diff --git a/scripts/Models/UnitComponents.cs b/scripts/Models/UnitComponents.cs
new file mode 100644
index 0000000..5c17418
--- /dev/null
+++ b/scripts/Models/UnitComponents.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+
+namespace Models;
+
+///
+/// Unit 组件集合(组合优于继承)
+/// 设计说明:
+/// 1) UnitModel 只是“组件容器”,具体能力拆分到小组件中,便于复用与替换。
+/// 2) 组件保持低内聚小职责,避免“巨型基类”造成的隐式耦合。
+/// 3) 这些组件只描述数据,不包含具体行为,行为交给系统/控制器层。
+/// 注意事项:
+/// - 不引用 Godot 类型,保持纯数据层,利于测试与序列化。
+/// - 标识/标签使用 string Id,方便 Mod 扩展新增内容。
+/// 未来扩展:
+/// - 可以给组件增加“快照/脏标记”,用于高效 UI 刷新。
+/// - 需要网络联机时,可在这里加入序列化版本号。
+///
+public static class UnitComponents
+{
+ public readonly struct GridPosition
+ {
+ public int X { get; }
+ public int Y { get; }
+
+ public GridPosition(int x, int y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public static GridPosition Zero => new(0, 0);
+ }
+
+ public sealed class UnitIdentity
+ {
+ public Guid Id { get; } = Guid.NewGuid();
+ public string Name { get; set; }
+ public string NameKey { get; set; }
+
+ public UnitIdentity(string name, string nameKey = null)
+ {
+ Name = name;
+ NameKey = nameKey;
+ }
+ }
+
+ public sealed class UnitAttributes
+ {
+ public PropertyValue Academic { get; set; }
+ public PropertyValue Engineering { get; set; }
+ public PropertyValue Writing { get; set; }
+ public PropertyValue Financial { get; set; }
+ public PropertyValue Social { get; set; }
+ public PropertyValue Activation { get; set; }
+
+ public UnitAttributes(Random random = null)
+ {
+ var rng = random ?? Random.Shared;
+ Academic = new PropertyValue(rng.Next(0, 100));
+ Engineering = new PropertyValue(rng.Next(0, 100));
+ Writing = new PropertyValue(rng.Next(0, 100));
+ Financial = new PropertyValue(rng.Next(0, 100));
+ Social = new PropertyValue(rng.Next(0, 100));
+ Activation = new PropertyValue(rng.Next(0, 100));
+ }
+ }
+
+ public sealed class UnitStatuses
+ {
+ public StatusValue Stress { get; set; }
+ public StatusValue Sanity { get; set; }
+ public PropertyValue Mood { get; set; }
+ public PropertyValue MoveSpeed { get; set; }
+
+ public UnitStatuses()
+ {
+ Stress = new StatusValue(0, 100, 0);
+ Sanity = new StatusValue(100, 100, 0);
+ Mood = new PropertyValue(50);
+ MoveSpeed = new PropertyValue(50, 0, 200);
+ }
+ }
+
+ public sealed class UnitTags
+ {
+ public string DisciplineId { get; set; }
+ public List ArchetypeIds { get; } = new();
+ public List RoleIds { get; } = new();
+ public List TraitIds { get; } = new();
+
+ public bool HasTrait(string traitId) => TraitIds.Contains(traitId);
+ }
+
+ public sealed class UnitAssignment
+ {
+ public Guid? CurrentTaskId { get; set; }
+ }
+
+ public sealed class UnitPlacement
+ {
+ public GridPosition Current { get; set; } = GridPosition.Zero;
+ public GridPosition Target { get; set; } = GridPosition.Zero;
+ }
+
+ public sealed class UnitEquipment
+ {
+ public List EquippedItemIds { get; } = new();
+ }
+}
diff --git a/scripts/Models/UnitModel.cs b/scripts/Models/UnitModel.cs
index 4e7c711..4ababc1 100644
--- a/scripts/Models/UnitModel.cs
+++ b/scripts/Models/UnitModel.cs
@@ -1,78 +1,44 @@
-using Godot;
using System;
namespace Models;
///
-/// 所有角色的基类,无论是导师、学生还是博后。
+/// UnitModel(组合式数据容器)
+/// 设计说明:
+/// 1) 该类不再是“基类”,而是用于聚合组件的纯数据对象。
+/// 2) 这样 Student/Mentor/Staff 可以按需组合组件,避免继承链的耦合。
+/// 3) 任何业务逻辑应放在 System/Controller 层,Model 保持可序列化、可测试。
+/// 注意事项:
+/// - 不引用 Godot 类型,保证与视图层隔离。
+/// - 通过 Name 属性提供便捷访问,便于旧代码过渡。
+/// 未来扩展:
+/// - 可以增加“能力衰减/成长曲线”等组件,而不影响现有对象结构。
+/// - 可引入“只读快照”用于回放/结算等需求。
///
-public class UnitModel
+public sealed class UnitModel
{
- public Guid Id { get; private set; } = Guid.NewGuid();
- public string Name { get; set; }
-
- // 基础属性
-
- ///
- /// 学术能力,影响科研进度
- ///
- public PropertyValue Academic {get; set;}
- ///
- /// 工程能力,影响实验进度
- ///
- public PropertyValue Engineering {get; set;}
- ///
- /// 写作能力,影响论文发表
- ///
- public PropertyValue Writing {get; set;}
- ///
- /// 财务能力,影响资金相关任务
- ///
- public PropertyValue Financial {get; set;}
- ///
- /// 社交能力
- ///
- public PropertyValue Social {get; set;}
- ///
- /// 行动力,影响各种行为的效率和能动性
- ///
- public PropertyValue Activation {get; set;}
-
- // 状态属性
+ public UnitComponents.UnitIdentity Identity { get; }
+ public UnitComponents.UnitAttributes Attributes { get; }
+ public UnitComponents.UnitStatuses Statuses { get; }
+ public UnitComponents.UnitTags Tags { get; }
+ public UnitComponents.UnitAssignment Assignment { get; }
+ public UnitComponents.UnitPlacement Placement { get; }
+ public UnitComponents.UnitEquipment Equipment { get; }
- ///
- /// 压力值
- ///
- public StatusValue Stress {get; set;}
- ///
- /// 精神值 (Sanity) / 抗压能力
- /// 范围 0-100。过低会导致崩溃(Breakdown)。
- ///
- public StatusValue Sanity { get; set; }
- ///
- /// 情绪值,影响工作效率
- ///
- public PropertyValue Mood {get; set;}
- ///
- /// 移动速度
- ///
- public float MoveSpeed {get; set;}
-
- // 局内属性
-
- public Vector2I TargetPosition {get; set;}
- public Vector2I CurrentPosition {get; set;}
- public Guid TargetTaskId {get; set;}
+ public string Name
+ {
+ get => Identity.Name;
+ set => Identity.Name = value;
+ }
- protected UnitModel(string name)
- {
- Name = name;
- var random = new Random();
- Academic = random.Next(PropertyValue.Min, PropertyValue.Max);
- Engineering = random.Next(PropertyValue.Min, PropertyValue.Max);
- Writing = random.Next(PropertyValue.Min, PropertyValue.Max);
- Financial = random.Next(PropertyValue.Min, PropertyValue.Max);
- Social = random.Next(PropertyValue.Min, PropertyValue.Max);
- Activation = random.Next(PropertyValue.Min, PropertyValue.Max);
- }
-}
\ No newline at end of file
+ public UnitModel(string name, Random random = null)
+ {
+ Identity = new UnitComponents.UnitIdentity(name);
+ Attributes = new UnitComponents.UnitAttributes(random);
+ Statuses = new UnitComponents.UnitStatuses();
+ Tags = new UnitComponents.UnitTags();
+ Assignment = new UnitComponents.UnitAssignment();
+ Placement = new UnitComponents.UnitPlacement();
+ Equipment = new UnitComponents.UnitEquipment();
+ }
+}