已把模型层改成组合式结构并补齐 MVC/内容配置骨架:Unit/任务/运行时状态现在通过组件组合,GameManager 作为桥接层改为驱动 GameSession/Controller;同时新增学科/羁绊/装备/任务/肉鸽等定义结构,预留 i18n 与 Mod 内容加载接口。

- 组合式单位与数值基础重构:scripts/Models/UnitModel.cs, scripts/Models/UnitComponents.cs, scripts/Models/StudentModel.cs, scripts/Models/MentorModel.cs, scripts/Models/StaffModel.cs, scripts/Models/PropertyValue.cs, scripts/Models/StatusValue.cs
  - 任务与运行时状态骨架:scripts/Models/Task.cs, scripts/Models/TaskDefinitions.cs, scripts/Models/GameState.cs
  - 配置与规则定义骨架:scripts/Models/DefinitionSupport.cs, scripts/Models/DomainEnums.cs, scripts/Models/Modifiers.cs, scripts/Models/DisciplineDefinitions.cs, scripts/Models/SynergyDefinitions.cs, scripts/Models/ItemDefinitions.cs, scripts/Models/PaperDefinitions.cs, scripts/Models/RogueliteDefinitions.cs, scripts/Models/GameContentDatabase.cs, scripts/Models/CoreIds.cs
  - MVC/会话/系统/i18n/Mod 支撑:scripts/Core/GameSession.cs, scripts/Core/GameSystems.cs, scripts/Core/GameController.cs, scripts/Core/Mvc.cs, scripts/Core/LocalizationService.cs, scripts/Core/ContentRegistry.cs, scripts/Core/ModManifest.cs, scripts/Core/EventBus.cs
  - 主控流程衔接:scripts/GameManager.cs
This commit is contained in:
wjsjwr 2025-12-31 23:54:31 +08:00
parent 71f841fd23
commit b8fecd080f
30 changed files with 1784 additions and 470 deletions

View File

@ -238,4 +238,139 @@
* 你的战斗节奏是什么(平稳输出?憋大招爆发?赌博?)
* 你的团队构成是什么。
这完美解决了“文科生不需要做实验”的逻辑漏洞,同时也大大加深了游戏的策略池。
这完美解决了“文科生不需要做实验”的逻辑漏洞,同时也大大加深了游戏的策略池。
这是一个极具策略深度的改进点。将**个人怪癖 (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而是连接“单卡”与“团队”的粘合剂。

View File

@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using Models;
namespace Core;
/// <summary>
/// 内容加载与合并(基础 + Mod
/// 设计说明:
/// 1) 通过 IContentSource 抽象读取来源,支持 res:// 与 user://mods。
/// 2) ContentRegistry 负责合并,同 Id 以后加载覆盖先加载。
/// 注意事项:
/// - 真实加载逻辑应避免在主线程做大规模 IO。
/// 未来扩展:
/// - 可加入“补丁合并策略”(例如列表合并/字段覆盖)。
/// </summary>
public interface IContentSource
{
int Priority { get; }
IEnumerable<T> LoadAll<T>() where T : class;
}
public enum ContentMergeMode
{
Override,
KeepFirst
}
public sealed class ContentRegistry
{
private readonly List<IContentSource> _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<DisciplineDefinition>(), d => d.Header.Id);
Merge(db.Archetypes, LoadAll<ArchetypeDefinition>(), d => d.Header.Id);
Merge(db.Roles, LoadAll<RoleDefinition>(), d => d.Header.Id);
Merge(db.Traits, LoadAll<TraitDefinition>(), d => d.Header.Id);
Merge(db.Tasks, LoadAll<TaskDefinition>(), d => d.Header.Id);
Merge(db.Items, LoadAll<ItemDefinition>(), d => d.Header.Id);
Merge(db.Papers, LoadAll<PaperDefinition>(), d => d.Header.Id);
Merge(db.RoguelitePerks, LoadAll<RoguelitePerkDefinition>(), d => d.Header.Id);
return db;
}
private IEnumerable<T> LoadAll<T>() where T : class
{
foreach (var source in _sources)
{
foreach (var item in source.LoadAll<T>())
{
yield return item;
}
}
}
private void Merge<T>(Dictionary<string, T> target, IEnumerable<T> items, Func<T, string> 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;
}
}
}
}
/// <summary>
/// 资源读取示例res:// 中的 .tres/.res
/// 这里只给出接口骨架,具体解析留给后续实现。
/// </summary>
public sealed class ResourceContentSource : IContentSource
{
public int Priority { get; }
public List<string> ResourcePaths { get; } = new();
public ResourceContentSource(int priority)
{
Priority = priority;
}
public IEnumerable<T> LoadAll<T>() where T : class
{
yield break;
}
}
/// <summary>
/// 资源读取示例json/yaml/自定义格式Mod 友好)
/// </summary>
public sealed class JsonContentSource : IContentSource
{
public int Priority { get; }
public List<string> DataPaths { get; } = new();
public JsonContentSource(int priority)
{
Priority = priority;
}
public IEnumerable<T> LoadAll<T>() where T : class
{
yield break;
}
}

58
scripts/Core/EventBus.cs Normal file
View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
namespace Core;
/// <summary>
/// 轻量事件总线(解耦系统之间的通信)
/// 设计说明:
/// 1) 系统层发布 DomainEventView/Controller 可订阅。
/// 2) 不依赖 Godot Signal便于纯逻辑测试。
/// 注意事项:
/// - 事件为进程内同步调用,避免在事件里做耗时操作。
/// 未来扩展:
/// - 可加入“事件队列”和“延迟派发”以支持回合结算。
/// </summary>
public sealed class DomainEventBus
{
private readonly Dictionary<Type, List<Delegate>> _handlers = new();
public void Subscribe<T>(Action<T> handler)
{
var type = typeof(T);
if (!_handlers.TryGetValue(type, out var list))
{
list = new List<Delegate>();
_handlers[type] = list;
}
list.Add(handler);
}
public void Unsubscribe<T>(Action<T> handler)
{
var type = typeof(T);
if (_handlers.TryGetValue(type, out var list))
{
list.Remove(handler);
}
}
public void Publish<T>(T evt)
{
var type = typeof(T);
if (!_handlers.TryGetValue(type, out var list))
{
return;
}
foreach (var handler in list)
{
if (handler is Action<T> typedHandler)
{
typedHandler.Invoke(evt);
}
}
}
}

View File

@ -0,0 +1,43 @@
using Models;
namespace Core;
/// <summary>
/// 高层控制器(面向 UI/输入)
/// 设计说明:
/// 1) Controller 只负责“指令式操作”,不直接做数值计算。
/// 2) 复杂结算交给系统层GameSystems
/// 注意事项:
/// - 需要可重入性,避免重复触发同一阶段切换。
/// 未来扩展:
/// - 可加入“命令队列”,用于回放或网络同步。
/// </summary>
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;
}
}

View File

@ -0,0 +1,47 @@
using Models;
namespace Core;
/// <summary>
/// 游戏会话(运行时入口)
/// 设计说明:
/// 1) 将 State + Content + Systems + Services 组织为一个整体。
/// 2) 便于在 Godot Node 中持有,避免散落的静态单例。
/// 注意事项:
/// - 真实项目中应通过工厂或依赖注入创建。
/// 未来扩展:
/// - 可加入“加载存档/保存存档/重置”接口。
/// </summary>
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);
}
}

122
scripts/Core/GameSystems.cs Normal file
View File

@ -0,0 +1,122 @@
using Models;
namespace Core;
/// <summary>
/// 系统层骨架(负责模型变化,不直接处理输入/显示)
/// 设计说明:
/// 1) 每个系统只关注单一职责,形成高内聚低耦合结构。
/// 2) 系统之间通过 GameSession 共享状态,通过事件总线通讯。
/// 注意事项:
/// - 系统逻辑应尽量“幂等”,便于回合重算与调试。
/// 未来扩展:
/// - 可加入“系统执行顺序配置”,支持 Mod 插入新系统。
/// </summary>
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)
{
// 预留:人员分配、交接惩罚等
}
}

View File

@ -0,0 +1,46 @@
using Godot;
using Models;
namespace Core;
/// <summary>
/// i18n 服务抽象
/// 设计说明:
/// 1) Model 只持有 LocalizedText(Key/Fallback),具体翻译交给服务层。
/// 2) 便于后续替换为自定义多语言系统或 Mod 词库。
/// 注意事项:
/// - TranslationServer 的翻译表应通过 Godot Editor 管理。
/// 未来扩展:
/// - 可加入“参数化翻译”和“运行时语言切换事件”。
/// </summary>
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;
}
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Core;
/// <summary>
/// Mod 清单与包信息
/// 设计说明:
/// 1) 通过 Manifest 提供版本/依赖/内容路径。
/// 2) 与 ContentRegistry 配合,实现热加载或启动时加载。
/// 注意事项:
/// - 建议为每个 Mod 指定唯一 Id 与语义化版本。
/// 未来扩展:
/// - 可加入“加载开关/冲突检测/签名校验”。
/// </summary>
public sealed class ModManifest
{
public string Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public List<string> Dependencies { get; } = new();
public List<string> ContentPaths { get; } = new();
}
public sealed class ModPackage
{
public ModManifest Manifest { get; set; } = new();
public string RootPath { get; set; }
}

39
scripts/Core/Mvc.cs Normal file
View File

@ -0,0 +1,39 @@
using Godot;
namespace Core;
/// <summary>
/// MVC 基础接口
/// 设计说明:
/// 1) View 只负责显示与输入Controller 负责修改 Model。
/// 2) View 与 Model 通过 Bind 绑定,避免静态单例。
/// 注意事项:
/// - View 内不要直接写业务结算逻辑。
/// 未来扩展:
/// - 可加入“脏标记刷新”与“差量更新”以减少 UI 开销。
/// </summary>
public interface IView<TModel>
{
void Bind(TModel model);
}
public interface IController
{
void Initialize(GameSession session);
}
public abstract partial class ModelView<TModel> : Node, IView<TModel>
{
public TModel Model { get; private set; }
public virtual void Bind(TModel model)
{
Model = model;
OnModelBound();
}
protected virtual void OnModelBound()
{
}
}

View File

@ -2,7 +2,18 @@ using Godot;
using System;
using System.Collections.Generic;
using Models;
using Core;
/// <summary>
/// 游戏主控制节点MVC 的桥接层)
/// 设计说明:
/// 1) 负责初始化 GameSession并把输入/信号转交给 Controller/System。
/// 2) 仅做“流程调度”,不直接计算复杂数值,避免逻辑堆积。
/// 注意事项:
/// - View 与 Model 的绑定应逐步迁移到独立 View 脚本,避免在此类膨胀。
/// 未来扩展:
/// - 可拆分为 SceneRoot + UIController + DebugController分层管理。
/// </summary>
public partial class GameManager : Node
{
/// <summary>
@ -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<StudentModel> Students { get; private set; } = new List<StudentModel>();
public List<Task> ActiveTasks { get; private set; } = new List<Task>();
public MentorModel Mentor => State?.Roster.Mentor;
public List<StudentModel> Students => State?.Roster.Students;
public List<TaskModel> 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);
}
/// <summary>
@ -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");
}

55
scripts/Models/CoreIds.cs Normal file
View File

@ -0,0 +1,55 @@
namespace Models;
/// <summary>
/// 内置内容 Id方便代码引用避免魔法字符串
/// 设计说明:
/// 1) 仅列出核心/内置内容Mod 内容不应写在这里。
/// 2) Id 使用 “namespace:name” 形式,默认 namespace 为 core。
/// 注意事项:
/// - 更改 Id 会破坏存档兼容性,请谨慎修改。
/// 未来扩展:
/// - 可拆分为多个文件Disciplines/Roles/Items以减少体积。
/// </summary>
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";
}

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 定义数据的通用结构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 冲突排查。
/// </summary>
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<string> Tags { get; } = new();
}

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 学科定义(决定开局流派、核心资源循环与卡池)
/// 设计说明:
/// 1) 学科只描述“规则与池子”,具体资源结算由系统层执行。
/// 2) Buff 使用 ModifierBundle + RuleIds 组合,便于非数值机制扩展。
/// 注意事项:
/// - DisciplineId 应稳定,用于存档与 Mod 兼容。
/// 未来扩展:
/// - 可加入“学科子方向”与“跨学科解锁条件”。
/// </summary>
public sealed class DisciplineDefinition
{
public DefinitionHeader Header { get; set; } = new();
public DisciplineBuff Buff { get; set; } = new();
public List<string> RolePoolIds { get; } = new();
public List<string> ItemPoolIds { get; } = new();
public List<string> 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();
}

View File

@ -0,0 +1,43 @@
namespace Models;
/// <summary>
/// 通用枚举(跨系统共享)
/// 设计说明:
/// 1) 这些枚举用于 Modifier 与系统逻辑,不直接绑定某个 UI。
/// 2) 若未来需要 Mod 扩展新类型,可将枚举替换为 string Id。
/// 注意事项:
/// - 如果确实需要开放扩展,请避免在存档里写枚举的数值。
/// 未来扩展:
/// - 可加入“行动点/灵感/算力”等资源类型。
/// </summary>
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
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 游戏内容数据库Definitions 聚合)
/// 设计说明:
/// 1) 将所有配置型数据集中到一个对象,便于系统层按 Id 索引。
/// 2) 由 ContentRegistry 构建,支持 Mod 覆盖/合并。
/// 注意事项:
/// - 本对象只存配置,不存运行时状态。
/// 未来扩展:
/// - 可加入“索引缓存”(如按学科/稀有度过滤)。
/// </summary>
public sealed class GameContentDatabase
{
public Dictionary<string, DisciplineDefinition> Disciplines { get; } = new();
public Dictionary<string, ArchetypeDefinition> Archetypes { get; } = new();
public Dictionary<string, RoleDefinition> Roles { get; } = new();
public Dictionary<string, TraitDefinition> Traits { get; } = new();
public Dictionary<string, TaskDefinition> Tasks { get; } = new();
public Dictionary<string, ItemDefinition> Items { get; } = new();
public Dictionary<string, PaperDefinition> Papers { get; } = new();
public Dictionary<string, RoguelitePerkDefinition> RoguelitePerks { get; } = new();
}

View File

@ -0,0 +1,84 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 运行时全局状态GameState
/// 设计说明:
/// 1) 将“局内状态”集中在一个聚合对象中,便于存档与调试快照。
/// 2) 具体子状态按职责拆分:回合/经济/人员/任务/库存/羁绊。
/// 3) Controller/System 只依赖这份数据,不直接操作 View。
/// 注意事项:
/// - 这是运行时数据,不是配置数据;配置请看 *Definition 类。
/// - 禁止在这里引用 Godot Node保持纯数据。
/// 未来扩展:
/// - 可加入“存档版本号”和“迁移器”,支持版本升级。
/// - 可加入“局外 Roguelite 状态”,与局内共享接口。
/// </summary>
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<StudentModel> Students { get; } = new();
public List<StaffModel> Staffs { get; } = new();
}
public sealed class TaskState
{
public List<TaskModel> ActiveTasks { get; } = new();
public List<TaskModel> CompletedTasks { get; } = new();
public List<TaskModel> FailedTasks { get; } = new();
}
public sealed class InventoryState
{
public Dictionary<string, int> ItemCounts { get; } = new();
public Dictionary<PaperRank, int> PaperCounts { get; } = new();
}
public sealed class SynergyState
{
public Dictionary<string, int> ArchetypeStacks { get; } = new();
public Dictionary<string, int> RoleStacks { get; } = new();
public List<string> ActiveSynergyIds { get; } = new();
}
public sealed class RogueliteState
{
public List<string> AlumniCardIds { get; } = new();
public List<string> LegacyUnlockIds { get; } = new();
public int TitleRetentionLevel { get; set; }
}

View File

@ -0,0 +1,59 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 装备/设施定义(通用 + 学科专用)
/// 设计说明:
/// 1) Facility 与 Equipment 统一为 ItemDefinition不同类别通过枚举区分。
/// 2) 效果使用 ModifierBundle + RuleIds 组合,便于特殊规则注入。
/// 注意事项:
/// - Placement/Slot 为 UI 与摆放系统提供约束,不做逻辑判断。
/// 未来扩展:
/// - 可加入“耐久度/维护成本/占地大小”等字段。
/// </summary>
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<string> RuleIds { get; } = new();
}

View File

@ -1,70 +1,51 @@
namespace Models;
/// <summary>
/// 导师数据模型 (MentorModel)
/// 玩家的数值状态。
/// 单例模式。
/// 导师数据模型 (MentorModel)
/// 设计说明:
/// 1) 改为组合式结构,不再使用单例,避免全局隐式依赖。
/// 2) 导师只保留“角色自身状态”,资金/声望等全局资源归 EconomyState 管理。
/// 3) 这样便于后续扩展多导师/多人模式或 Mod 玩法。
/// 注意事项:
/// - 构造函数不做复杂逻辑,具体初始数值交由 GameSession 初始化。
/// 未来扩展:
/// - 可加入“导师技能树/天赋树”组件,与肉鸽继承系统对接。
/// - 可加入“导师光环”组件,在系统层计算对学生的影响范围。
/// </summary>
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;
}
/// <summary>
/// 精力值 (Energy)
/// 用于释放技能画饼、PUA、甚至亲自写代码
/// 每回合恢复。
/// </summary>
public StatusValue Energy { get; set; }
public MentorModel(string name)
{
Core = new UnitModel(name);
Resources = new MentorResources();
}
}
/// <summary>
/// 经费 (Money/Funds)
/// 单位:元。用于发工资、买设备。
/// </summary>
public int Money { get; set; } = 50000;
/// <summary>
/// 导师资源组件(角色自身能量池)
/// </summary>
public sealed class MentorResources
{
public StatusValue Energy { get; set; }
/// <summary>
/// 学术声望 (Reputation)
/// 影响招生质量、项目申请成功率。
/// </summary>
public int Reputation { get; set; } = 0;
/// <summary>
/// 算力/数据资源 (ResearchPoints)
/// 用于攻克高难度 AI 课题。
/// </summary>
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);
}
}
public MentorResources()
{
Energy = new StatusValue(100, 100, 0);
}
}

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 统一的数值修饰结构(用于学科 Buff、羁绊、装备、特质
/// 设计说明:
/// 1) 只表达“加成/倍率/规则”,不直接改变模型数值。
/// 2) 计算由系统层统一处理,便于叠加与回滚。
/// 注意事项:
/// - 这里的 Multiplier 默认 1.0f,不是百分比。
/// 未来扩展:
/// - 可加入“条件触发”与“持续时间”字段。
/// </summary>
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<AttributeModifier> AttributeModifiers { get; } = new();
public List<StatusModifier> StatusModifiers { get; } = new();
public List<ResourceModifier> ResourceModifiers { get; } = new();
public List<string> RuleIds { get; } = new();
}

View File

@ -0,0 +1,18 @@
namespace Models;
/// <summary>
/// 论文卡牌定义
/// 设计说明:
/// 1) 论文作为“消耗品资源”,用于申请纵向基金。
/// 2) 论文等级只是一种枚举,真正的效果由系统层决定。
/// 注意事项:
/// - 若需要“引用率/影响因子”,可在此扩展字段。
/// 未来扩展:
/// - 可加入“论文主题标签”,影响基金/任务匹配度。
/// </summary>
public sealed class PaperDefinition
{
public DefinitionHeader Header { get; set; } = new();
public PaperRank Rank { get; set; }
}

View File

@ -3,120 +3,129 @@ using System;
namespace Models;
/// <summary>
/// 属性值类型
/// 封装了整型数值,提供最大值限制(100)和显示转换。
/// 属性值类型(通用数值封装)
/// 设计说明:
/// 1) 过去固定使用 0-100 的范围,导致“精力/算力”等大数值被强行截断。
/// 2) 现在改为“每个实例自带范围”,默认仍然是 0-100兼顾直觉和可扩展性。
/// 3) 这个类仅承担“数值与范围”的职责,不负责业务含义,便于在 MVC 中被复用。
/// 注意事项:
/// - 运算符会保留左操作数的范围(或右操作数的范围),避免范围丢失。
/// - 如果需要无上限/无下限,请显式传入更大的 Max/更小的 Min。
/// 未来扩展:
/// - 可加入“软上限/软下限”和“可见范围”等 UI 表现字段。
/// - 可增加数值变化事件,以便 View 层更高效地刷新。
/// </summary>
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;
/// <summary>
/// 获取或设置当前值。
/// 设置时会自动限制在 [Min, Max] 范围内。
/// </summary>
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);
}
/// <summary>
/// 获取显示值 (Value / 100.0f)
/// </summary>
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);
}
}
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;
}
}

View File

@ -0,0 +1,27 @@
namespace Models;
/// <summary>
/// 肉鸽继承/局外成长定义
/// 设计说明:
/// 1) 对应“校友录/祖传代码/职称保留”等局外系统。
/// 2) 这里只描述解锁项,实际解锁逻辑由系统层处理。
/// 注意事项:
/// - 与存档绑定的解锁项需要稳定 Id。
/// 未来扩展:
/// - 可加入“局外科技树”与“多周目难度曲线”。
/// </summary>
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;
}

View File

@ -0,0 +1,47 @@
using System;
namespace Models;
/// <summary>
/// 雇员/合作人数据模型 (StaffModel)
/// 设计说明:
/// 1) 覆盖“博后/小老师”等非学生角色,强调长期合作与野心值。
/// 2) 继续复用 UnitModel 组件,保证与学生/导师统一的数据接口。
/// 注意事项:
/// - Staff 行为逻辑应由系统层决定(例如背刺/跳槽)。
/// 未来扩展:
/// - 可加入“合作者关系网”和“派系归属”。
/// </summary>
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);
}

View File

@ -4,98 +4,81 @@ namespace Models;
/// <summary>
/// 状态值 (StatusValue)
/// 维护当前状态值以及上限和下限阈值。
/// 设计说明:
/// 1) 这是“带阈值的数值”,用于压力、体力、忠诚等需要触发事件的状态。
/// 2) 当前值由 PropertyValue 承担“范围约束”,阈值只负责触发行为。
/// 3) 事件由 Model 层抛出Controller/系统层订阅View 不直接耦合。
/// 注意事项:
/// - Upper/Lower 默认等于当前范围的上/下限,保持语义直观。
/// - 如果需要“软阈值”,请在系统中根据 Normalized 手动判断。
/// 未来扩展:
/// - 可增加“阈值触发冷却/抑制器”,避免频繁触发。
/// - 可加入“变化速率”以支持回合/时间驱动的增减。
/// </summary>
public class StatusValue
public sealed class StatusValue
{
private PropertyValue _current;
private PropertyValue _upperThreshold;
private PropertyValue _lowerThreshold;
private PropertyValue _current;
/// <summary>
/// 当 Current >= UpperThreshold 时触发此事件
/// </summary>
public event Action OnUpperThresholdReached;
public event Action OnUpperThresholdReached;
public event Action OnLowerThresholdReached;
/// <summary>
/// 当 Current <= LowerThreshold 时触发此事件
/// </summary>
public event Action OnLowerThresholdReached;
public PropertyValue Current
{
get => _current;
set
{
_current = value;
CheckThresholds();
}
}
/// <summary>
/// 当前状态值
/// 修改时会自动检测阈值。
/// </summary>
public PropertyValue Current
{
get => _current;
set
{
_current = value;
CheckThresholds();
}
}
public float UpperThreshold { get; set; }
public float LowerThreshold { get; set; }
/// <summary>
/// 上限阈值
/// </summary>
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;
}
/// <summary>
/// 下限阈值
/// </summary>
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
}
}
public void Add(float value)
{
_current.Add(value);
CheckThresholds();
}
public void Subtract(float value)
{
_current.Subtract(value);
CheckThresholds();
}
}

View File

@ -5,59 +5,70 @@ namespace Models;
/// <summary>
/// 学生数据模型 (StudentModel)
/// 包含学生的数值、状态和特质。
/// 与 Godot 的 Node (View) 分离,便于序列化和逻辑处理。
/// 设计说明:
/// 1) 不再继承 UnitModel而是通过 Core 组合通用组件(属性/状态/标签/装备等)。
/// 2) 学生特有内容(年级/体力/忠诚/署名贡献)放在独立组件中,保持高内聚。
/// 3) Model 层仅负责数据,不做 Godot 行为调用,方便后续存档与 Mod 注入。
/// 注意事项:
/// - Traits 实际上是标签Tag统一走 Core.Tags.TraitIds避免双份真相。
/// - Grade 仅表示“学年进度”,不是学术能力值。
/// 未来扩展:
/// - 可新增“毕业/退学事件队列”,由系统层驱动。
/// - 可新增“论文署名历史”用于复盘与羁绊叠层回溯。
/// </summary>
public class StudentModel: UnitModel
public sealed class StudentModel
{
public enum StudentType
{
MasterCandidate,
DoctorCandidate
}
// --- 静态属性 (Property) ---
/// <summary>
/// 学生类型
/// </summary>
public StudentType Type { get; private set; }
// --- 动态状态 (Status) ---
/// <summary>
/// 体力 (Stamina)
/// 范围 0-100。工作消耗体力休息恢复。体力过低效率下降。
/// </summary>
public StatusValue Stamina { get; set; }
/// <summary>
/// 忠诚度 (Loyalty)
/// 范围 0-100。过低可能跳槽或举报。
/// </summary>
public StatusValue Loyalty { get; set; }
/// <summary>
/// 年级
/// </summary>
public StatusValue Grade { get; set; }
/// <summary>
/// 记录对每个 Task 的贡献量,用于署名分配
/// </summary>
public Dictionary<Guid, PropertyValue> Contributions { get; set; }
public enum StudentType
{
MasterCandidate,
DoctorCandidate
}
/// <summary>
/// 特质列表 (如 "DDL战士", "卷王")
/// </summary>
public List<string> 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<string> 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();
}
}
/// <summary>
/// 学生进度组件(只关心学生生命周期)
/// </summary>
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);
}
}
/// <summary>
/// 署名贡献记录(任务 -> 贡献值)
/// </summary>
public sealed class StudentContributions
{
public Dictionary<Guid, PropertyValue> ByTask { get; } = new();
}

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 羁绊/职业/特质定义(“人群画像 + 职能分工”)
/// 设计说明:
/// 1) Archetype/Role 采用分层 Tier 模式,满足 2/4/6 叠层设计。
/// 2) Trait 为单体特质,不叠层,仅提供修饰与规则。
/// 注意事项:
/// - Tier 的规则效果写在 RuleIds 中,由系统层解释执行。
/// 未来扩展:
/// - 可加入“跨学科混合羁绊”条件与视觉特效引用。
/// </summary>
public sealed class ArchetypeDefinition
{
public DefinitionHeader Header { get; set; } = new();
public List<SynergyTier> Tiers { get; } = new();
}
public sealed class RoleDefinition
{
public DefinitionHeader Header { get; set; } = new();
public List<SynergyTier> Tiers { get; } = new();
// 如果是学科限定角色(如炼金术士/极客),在这里配置允许的学科 Id。
public List<string> AllowedDisciplineIds { get; } = new();
}
public sealed class TraitDefinition
{
public DefinitionHeader Header { get; set; } = new();
public ModifierBundle Modifiers { get; set; } = new();
public List<string> RuleIds { get; } = new();
}
public sealed class SynergyTier
{
public int RequiredCount { get; set; }
public ModifierBundle Modifiers { get; set; } = new();
public List<string> RuleIds { get; } = new();
}

View File

@ -4,94 +4,76 @@ using System.Collections.Generic;
namespace Models;
/// <summary>
/// 任务类型枚举
/// 任务运行时模型 (TaskModel)
/// 设计说明:
/// 1) TaskDefinition 描述“配置”TaskModel 描述“运行时进度”。二者分离利于 Mod 追加内容。
/// 2) 任务状态只保存必要字段,展示文本统一由 Definition + i18n 输出。
/// 3) 任务参与单位只记录 Id避免模型直接引用 Node。
/// 注意事项:
/// - 如果没有 DefinitionId可使用 Name 作为临时演示数据。
/// - Deadline 语义是“剩余回合数”,由 TurnSystem 递减。
/// 未来扩展:
/// - 可加入阶段状态(灵感/写作/实验/审核),与 UI 进度条分段对应。
/// - 可加入失败原因/完成评级,用于结算与肉鸽评分。
/// </summary>
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<Guid> 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;
}
}
}
/// <summary>
/// 任务 (Task)
/// 核心游戏对象之一,相当于地图上的“怪物”。
/// 学生需要“攻击”任务(消耗工作量)来完成它。
/// 任务运行参数(进度与截止)
/// </summary>
public class Task
public sealed class TaskRuntime
{
public Guid Id { get; private set; } = Guid.NewGuid();
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public TaskType Type { get; set; }
/// <summary>
/// 总工作量 (HP)
/// </summary>
public float TotalWorkload { get; set; }
/// <summary>
/// 当前进度
/// </summary>
public float CurrentProgress { get; set; }
/// <summary>
/// 难度系数 (Defense)
/// 影响学生攻克该任务的效率。若学生能力低于难度,效率会大幅下降。
/// </summary>
public float Difficulty { get; set; }
/// <summary>
/// 截止日期 (剩余回合数)
/// 归零时如果未完成,触发失败惩罚。
/// </summary>
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; }
/// <summary>
/// 奖励:经费
/// </summary>
public int RewardMoney { get; set; }
/// <summary>
/// 奖励:声望
/// </summary>
public int RewardReputation { get; set; }
/// <summary>
/// 任务是否已完成
/// </summary>
public bool IsCompleted => CurrentProgress >= TotalWorkload;
/// <summary>
/// 任务是否已失败(超时未完成)
/// </summary>
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;
}
/// <summary>
/// 增加进度
/// </summary>
/// <param name="amount">工作量数值</param>
public void AddProgress(float amount)
{
CurrentProgress += amount;
if (CurrentProgress > TotalWorkload) CurrentProgress = TotalWorkload;
}
public TaskRuntime(float totalWorkload, int remainingTurns)
{
TotalWorkload = totalWorkload;
RemainingTurns = remainingTurns;
}
}
/// <summary>
/// 运行时奖励快照(避免 Definition 被动态修改)
/// </summary>
public sealed class TaskRewardSnapshot
{
public int Money { get; set; }
public int Reputation { get; set; }
public List<string> PaperIds { get; } = new();
}

View File

@ -0,0 +1,101 @@
using System.Collections.Generic;
namespace Models;
/// <summary>
/// 任务定义与枚举(配置层)
/// 设计说明:
/// 1) Definition 描述“任务是什么”TaskModel 描述“任务正在发生什么”。
/// 2) 任务类别与难度与设计文档一致,方便 UI/系统层做分流。
/// 3) Requirements/Rewards 以组合结构表示,便于 Mod 追加新条件。
/// 注意事项:
/// - 不在此处写具体数值表,仅提供结构骨架。
/// - 业务系统应优先读取 Definition而不是硬编码逻辑。
/// 未来扩展:
/// - 可加入“阶段式任务”与“事件触发任务”字段。
/// - 可加入“动态难度曲线”,用于 Roguelite 调整强度。
/// </summary>
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<string> AllowedDisciplineIds { get; } = new();
public List<string> RecommendedRoleIds { get; } = new();
public List<string> 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<PaperRequirement> PaperTickets { get; } = new();
public List<AttributeRequirement> AttributeChecks { get; } = new();
public List<string> 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<string> PaperIds { get; } = new();
public List<string> ItemIds { get; } = new();
public List<string> BuffIds { get; } = new();
}

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
namespace Models;
/// <summary>
/// Unit 组件集合(组合优于继承)
/// 设计说明:
/// 1) UnitModel 只是“组件容器”,具体能力拆分到小组件中,便于复用与替换。
/// 2) 组件保持低内聚小职责,避免“巨型基类”造成的隐式耦合。
/// 3) 这些组件只描述数据,不包含具体行为,行为交给系统/控制器层。
/// 注意事项:
/// - 不引用 Godot 类型,保持纯数据层,利于测试与序列化。
/// - 标识/标签使用 string Id方便 Mod 扩展新增内容。
/// 未来扩展:
/// - 可以给组件增加“快照/脏标记”,用于高效 UI 刷新。
/// - 需要网络联机时,可在这里加入序列化版本号。
/// </summary>
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<string> ArchetypeIds { get; } = new();
public List<string> RoleIds { get; } = new();
public List<string> 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<string> EquippedItemIds { get; } = new();
}
}

View File

@ -1,78 +1,44 @@
using Godot;
using System;
namespace Models;
/// <summary>
/// 所有角色的基类,无论是导师、学生还是博后。
/// UnitModel组合式数据容器
/// 设计说明:
/// 1) 该类不再是“基类”,而是用于聚合组件的纯数据对象。
/// 2) 这样 Student/Mentor/Staff 可以按需组合组件,避免继承链的耦合。
/// 3) 任何业务逻辑应放在 System/Controller 层Model 保持可序列化、可测试。
/// 注意事项:
/// - 不引用 Godot 类型,保证与视图层隔离。
/// - 通过 Name 属性提供便捷访问,便于旧代码过渡。
/// 未来扩展:
/// - 可以增加“能力衰减/成长曲线”等组件,而不影响现有对象结构。
/// - 可引入“只读快照”用于回放/结算等需求。
/// </summary>
public class UnitModel
public sealed class UnitModel
{
public Guid Id { get; private set; } = Guid.NewGuid();
public string Name { get; set; }
// 基础属性
/// <summary>
/// 学术能力,影响科研进度
/// </summary>
public PropertyValue Academic {get; set;}
/// <summary>
/// 工程能力,影响实验进度
/// </summary>
public PropertyValue Engineering {get; set;}
/// <summary>
/// 写作能力,影响论文发表
/// </summary>
public PropertyValue Writing {get; set;}
/// <summary>
/// 财务能力,影响资金相关任务
/// </summary>
public PropertyValue Financial {get; set;}
/// <summary>
/// 社交能力
/// </summary>
public PropertyValue Social {get; set;}
/// <summary>
/// 行动力,影响各种行为的效率和能动性
/// </summary>
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; }
/// <summary>
/// 压力值
/// </summary>
public StatusValue Stress {get; set;}
/// <summary>
/// 精神值 (Sanity) / 抗压能力
/// 范围 0-100。过低会导致崩溃(Breakdown)。
/// </summary>
public StatusValue Sanity { get; set; }
/// <summary>
/// 情绪值,影响工作效率
/// </summary>
public PropertyValue Mood {get; set;}
/// <summary>
/// 移动速度
/// </summary>
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);
}
}
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();
}
}