已把模型层改成组合式结构并补齐 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:
parent
71f841fd23
commit
b8fecd080f
137
docs/角色与羁绊系统.md
137
docs/角色与羁绊系统.md
@ -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,而是连接“单卡”与“团队”的粘合剂。
|
||||
128
scripts/Core/ContentRegistry.cs
Normal file
128
scripts/Core/ContentRegistry.cs
Normal 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
58
scripts/Core/EventBus.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Core;
|
||||
|
||||
/// <summary>
|
||||
/// 轻量事件总线(解耦系统之间的通信)
|
||||
/// 设计说明:
|
||||
/// 1) 系统层发布 DomainEvent,View/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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
scripts/Core/GameController.cs
Normal file
43
scripts/Core/GameController.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
47
scripts/Core/GameSession.cs
Normal file
47
scripts/Core/GameSession.cs
Normal 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
122
scripts/Core/GameSystems.cs
Normal 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)
|
||||
{
|
||||
// 预留:人员分配、交接惩罚等
|
||||
}
|
||||
}
|
||||
|
||||
46
scripts/Core/LocalizationService.cs
Normal file
46
scripts/Core/LocalizationService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
29
scripts/Core/ModManifest.cs
Normal file
29
scripts/Core/ModManifest.cs
Normal 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
39
scripts/Core/Mvc.cs
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
55
scripts/Models/CoreIds.cs
Normal 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";
|
||||
}
|
||||
|
||||
32
scripts/Models/DefinitionSupport.cs
Normal file
32
scripts/Models/DefinitionSupport.cs
Normal 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();
|
||||
}
|
||||
|
||||
31
scripts/Models/DisciplineDefinitions.cs
Normal file
31
scripts/Models/DisciplineDefinitions.cs
Normal 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();
|
||||
}
|
||||
|
||||
43
scripts/Models/DomainEnums.cs
Normal file
43
scripts/Models/DomainEnums.cs
Normal 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
|
||||
}
|
||||
|
||||
26
scripts/Models/GameContentDatabase.cs
Normal file
26
scripts/Models/GameContentDatabase.cs
Normal 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();
|
||||
}
|
||||
|
||||
84
scripts/Models/GameState.cs
Normal file
84
scripts/Models/GameState.cs
Normal 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; }
|
||||
}
|
||||
|
||||
59
scripts/Models/ItemDefinitions.cs
Normal file
59
scripts/Models/ItemDefinitions.cs
Normal 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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
43
scripts/Models/Modifiers.cs
Normal file
43
scripts/Models/Modifiers.cs
Normal 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();
|
||||
}
|
||||
|
||||
18
scripts/Models/PaperDefinitions.cs
Normal file
18
scripts/Models/PaperDefinitions.cs
Normal 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; }
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
27
scripts/Models/RogueliteDefinitions.cs
Normal file
27
scripts/Models/RogueliteDefinitions.cs
Normal 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;
|
||||
}
|
||||
|
||||
47
scripts/Models/StaffModel.cs
Normal file
47
scripts/Models/StaffModel.cs
Normal 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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
43
scripts/Models/SynergyDefinitions.cs
Normal file
43
scripts/Models/SynergyDefinitions.cs
Normal 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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
101
scripts/Models/TaskDefinitions.cs
Normal file
101
scripts/Models/TaskDefinitions.cs
Normal 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();
|
||||
}
|
||||
|
||||
110
scripts/Models/UnitComponents.cs
Normal file
110
scripts/Models/UnitComponents.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user