supervisor-simulator/scripts/Core/GameSystems.cs
2026-01-18 20:05:23 +08:00

665 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using Models;
namespace Core;
/// <summary>
/// 系统层骨架(负责模型变化,不直接处理输入/显示)
/// 设计说明:
/// 1) 每个系统只关注单一职责,形成高内聚低耦合结构。
/// 2) 系统之间通过 GameSession 共享状态,通过事件总线通讯。
/// 注意事项:
/// - 系统逻辑应尽量“幂等”,便于回合重算与调试。
/// 未来扩展:
/// - 可加入“系统执行顺序配置”,支持 Mod 插入新系统。
/// </summary>
public interface IGameSystem {
/// <summary>
/// 初始化系统
/// </summary>
/// <param name="session">游戏会话</param>
void Initialize(GameSession session);
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
void Tick(float delta);
}
/// <summary>
/// 游戏系统管理器
/// </summary>
public sealed class GameSystems {
/// <summary>
/// 回合系统
/// </summary>
public TurnSystem Turn { get; } = new();
/// <summary>
/// 任务系统
/// </summary>
public TaskSystem Task { get; } = new();
/// <summary>
/// 经济系统
/// </summary>
public EconomySystem Economy { get; } = new();
/// <summary>
/// 羁绊/协同系统
/// </summary>
public SynergySystem Synergy { get; } = new();
/// <summary>
/// 分配系统
/// </summary>
public AssignmentSystem Assignment { get; } = new();
/// <summary>
/// 初始化所有系统
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
Turn.Initialize(session);
Task.Initialize(session);
Economy.Initialize(session);
Synergy.Initialize(session);
Assignment.Initialize(session);
}
/// <summary>
/// 更新所有系统
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
Turn.Tick(delta);
Synergy.Tick(delta);
Task.Tick(delta);
Economy.Tick(delta);
Assignment.Tick(delta);
}
}
/// <summary>
/// 回合系统
/// </summary>
public sealed class TurnSystem : IGameSystem {
private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
_session = session;
}
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
// 预留:回合推进计时器/阶段切换
}
}
/// <summary>
/// 任务系统
/// </summary>
public sealed class TaskSystem : IGameSystem {
private GameSession _session;
private StatResolver _statResolver;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
_session = session;
_statResolver = new StatResolver(session);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
if (_session.State.Turn.Phase != GamePhase.Execution) return;
AdvanceTasks(delta);
}
/// <summary>
/// 回合结算(扣除 Deadline判定完成与失败
/// </summary>
public void ResolveEndOfTurn() {
var state = _session.State;
var completed = new List<TaskModel>();
var failed = new List<TaskModel>();
foreach (var task in state.Tasks.ActiveTasks) {
if (task.Runtime.RemainingTurns > 0) task.Runtime.RemainingTurns--;
if (task.IsCompleted)
completed.Add(task);
else if (task.IsFailed) failed.Add(task);
}
foreach (var task in completed) {
state.Tasks.ActiveTasks.Remove(task);
state.Tasks.CompletedTasks.Add(task);
_session.Events.Publish(new TaskCompletedEvent(task));
}
foreach (var task in failed) {
state.Tasks.ActiveTasks.Remove(task);
state.Tasks.FailedTasks.Add(task);
_session.Events.Publish(new TaskFailedEvent(task));
}
}
/// <summary>
/// 推进任务进度
/// </summary>
/// <param name="delta">时间间隔</param>
private void AdvanceTasks(float delta) {
var state = _session.State;
if (state.Tasks.ActiveTasks.Count == 0) return;
var unitIndex = BuildUnitIndex();
foreach (var task in state.Tasks.ActiveTasks) {
if (task.IsCompleted || task.IsFailed) continue;
if (task.Kind == TaskKind.Milestone) continue;
var taskDef = GetTaskDefinition(task);
var totalPower = 0f;
foreach (var unitId in task.AssignedUnitIds) {
if (!unitIndex.TryGetValue(unitId, out var entry)) continue;
var contribution = GetUnitContribution(entry, task, taskDef, delta);
totalPower += contribution;
}
if (totalPower <= 0f) continue;
var difficultyScale = task.Runtime.DifficultyScale * GetDifficultyScale(task.Difficulty);
task.AddProgress(totalPower * delta / difficultyScale);
}
}
/// <summary>
/// 获取单位对任务的贡献
/// </summary>
private float GetUnitContribution(UnitEntry entry, TaskModel task, TaskDefinition taskDef, float delta) {
var unit = entry.Unit;
var basePower = GetTaskBasePower(unit, task.Kind);
if (basePower <= 0f) return 0f;
var roleMultiplier = GetRoleMultiplier(unit, taskDef);
var disciplineMultiplier = GetDisciplineMultiplier(unit, taskDef);
var statusMultiplier = GetStatusMultiplier(entry);
var requirementMultiplier = GetRequirementMultiplier(unit, taskDef);
var effectivePower = basePower * roleMultiplier * disciplineMultiplier * statusMultiplier *
requirementMultiplier;
TrackContribution(entry, task, effectivePower * delta);
return effectivePower;
}
/// <summary>
/// 获取任务基础效率
/// </summary>
private float GetTaskBasePower(UnitModel unit, TaskKind kind) {
var academic = _statResolver.GetAttribute(unit, AttributeType.Academic);
var engineering = _statResolver.GetAttribute(unit, AttributeType.Engineering);
var writing = _statResolver.GetAttribute(unit, AttributeType.Writing);
var financial = _statResolver.GetAttribute(unit, AttributeType.Financial);
var social = _statResolver.GetAttribute(unit, AttributeType.Social);
var activation = _statResolver.GetAttribute(unit, AttributeType.Activation);
return kind switch {
TaskKind.AcademicExploration => academic * 0.6f + writing * 0.4f,
TaskKind.GrantVertical => writing * 0.4f + social * 0.4f + financial * 0.2f,
TaskKind.GrantHorizontal => engineering * 0.5f + activation * 0.3f + academic * 0.2f,
TaskKind.Administrative => activation * 0.6f + social * 0.4f,
TaskKind.Conference => social * 0.5f + writing * 0.3f + financial * 0.2f,
TaskKind.Milestone => 0f,
_ => academic
};
}
/// <summary>
/// 获取状态乘数
/// </summary>
private float GetStatusMultiplier(UnitEntry entry) {
var mood = entry.Unit.Statuses.Mood.Normalized;
var stress = entry.Unit.Statuses.Stress.Current.Normalized;
var moodMultiplier = 0.5f + mood * 0.5f;
var stressMultiplier = Math.Clamp(1.0f - 0.3f * stress, 0.7f, 1.0f);
var multiplier = moodMultiplier * stressMultiplier;
if (entry.Student != null) {
var stamina = entry.Student.Progress.Stamina.Current.Normalized;
if (stamina < 0.1f) multiplier *= 0.4f;
else if (stamina < 0.2f) multiplier *= 0.7f;
}
return Math.Clamp(multiplier, 0.3f, 1.2f);
}
/// <summary>
/// 获取角色乘数
/// </summary>
private float GetRoleMultiplier(UnitModel unit, TaskDefinition taskDef) {
if (taskDef == null) return 1.0f;
if (taskDef.Requirements.RequiredRoleIds.Count > 0) {
foreach (var roleId in taskDef.Requirements.RequiredRoleIds)
if (unit.Tags.RoleIds.Contains(roleId))
return 1.0f;
return 0.5f;
}
if (taskDef.RecommendedRoleIds.Count > 0) {
foreach (var roleId in taskDef.RecommendedRoleIds)
if (unit.Tags.RoleIds.Contains(roleId))
return 1.1f;
return 0.9f;
}
return 1.0f;
}
/// <summary>
/// 获取需求乘数
/// </summary>
private float GetRequirementMultiplier(UnitModel unit, TaskDefinition taskDef) {
if (taskDef == null) return 1.0f;
var multiplier = 1.0f;
foreach (var requirement in taskDef.Requirements.AttributeChecks) {
var value = _statResolver.GetAttribute(unit, requirement.Type);
if (value < requirement.MinValue) multiplier *= 0.85f;
}
return multiplier;
}
/// <summary>
/// 获取学科乘数
/// </summary>
private float GetDisciplineMultiplier(UnitModel unit, TaskDefinition taskDef) {
if (taskDef == null) return 1.0f;
if (taskDef.AllowedDisciplineIds.Count == 0) return 1.0f;
if (string.IsNullOrWhiteSpace(unit.Tags.DisciplineId)) return 0.8f;
foreach (var disciplineId in taskDef.AllowedDisciplineIds)
if (disciplineId == unit.Tags.DisciplineId)
return 1.0f;
return 0.7f;
}
/// <summary>
/// 获取难度系数
/// </summary>
private float GetDifficultyScale(TaskDifficulty difficulty) {
return difficulty switch {
TaskDifficulty.Water => 0.8f,
TaskDifficulty.Standard => 1.0f,
TaskDifficulty.Hardcore => 1.3f,
TaskDifficulty.BlackBox => 1.6f,
_ => 1.0f
};
}
/// <summary>
/// 获取任务定义
/// </summary>
private TaskDefinition GetTaskDefinition(TaskModel task) {
if (string.IsNullOrWhiteSpace(task.DefinitionId)) return null;
return _session.Content.Tasks.TryGetValue(task.DefinitionId, out var definition) ? definition : null;
}
/// <summary>
/// 构建单位索引
/// </summary>
private Dictionary<Guid, UnitEntry> BuildUnitIndex() {
var index = new Dictionary<Guid, UnitEntry>();
var roster = _session.State.Roster;
if (roster.Mentor != null) {
var unit = roster.Mentor.Core;
index[unit.Identity.Id] = new UnitEntry(unit, roster.Mentor, null, null);
}
foreach (var student in roster.Students) {
var unit = student.Core;
index[unit.Identity.Id] = new UnitEntry(unit, null, student, null);
}
foreach (var staff in roster.Staffs) {
var unit = staff.Core;
index[unit.Identity.Id] = new UnitEntry(unit, null, null, staff);
}
return index;
}
/// <summary>
/// 追踪贡献
/// </summary>
private void TrackContribution(UnitEntry entry, TaskModel task, float deltaContribution) {
if (entry.Student == null) return;
var contributions = entry.Student.Contributions.ByTask;
if (!contributions.TryGetValue(task.Id, out var value)) {
value = new PropertyValue(0, 0, 1000000);
contributions[task.Id] = value;
}
value.Add(deltaContribution);
}
private readonly struct UnitEntry {
public UnitModel Unit { get; }
public MentorModel Mentor { get; }
public StudentModel Student { get; }
public StaffModel Staff { get; }
public UnitEntry(UnitModel unit, MentorModel mentor, StudentModel student, StaffModel staff) {
Unit = unit;
Mentor = mentor;
Student = student;
Staff = staff;
}
}
}
/// <summary>
/// 经济系统
/// </summary>
public sealed class EconomySystem : IGameSystem {
private const int MasterSalary = 500;
private const int DoctorSalary = 800;
private const int PostDocSalary = 1200;
private const int JuniorFacultySalary = 2000;
private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
_session = session;
_session.Events.Subscribe<TaskCompletedEvent>(OnTaskCompleted);
_session.Events.Subscribe<TaskFailedEvent>(OnTaskFailed);
_session.Events.Subscribe<TurnEndedEvent>(OnTurnEnded);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
// 当前为回合驱动,不在 Tick 中结算
}
/// <summary>
/// 任务完成回调
/// </summary>
private void OnTaskCompleted(TaskCompletedEvent evt) {
var task = evt.Task;
var economy = _session.State.Economy;
economy.Money += task.Reward.Money;
economy.Reputation += task.Reward.Reputation;
if (task.Reward.PaperIds.Count > 0) {
foreach (var paperId in task.Reward.PaperIds)
if (_session.Content.Papers.TryGetValue(paperId, out var paper))
AddPaper(paper.Rank);
}
else if (task.Kind == TaskKind.AcademicExploration) {
AddPaper(GetPaperRankByDifficulty(task.Difficulty));
}
foreach (var itemId in task.Reward.ItemIds) AddItem(itemId, 1);
}
/// <summary>
/// 任务失败回调
/// </summary>
private void OnTaskFailed(TaskFailedEvent evt) {
var task = evt.Task;
var penalty = task.Track == TaskTrack.Tenure ? 20 : 10;
if (task.Kind == TaskKind.GrantVertical) penalty += 10;
_session.State.Economy.Reputation -= penalty;
}
/// <summary>
/// 回合结束回调
/// </summary>
private void OnTurnEnded(TurnEndedEvent evt) {
ApplySalaries();
ApplyInterest();
}
/// <summary>
/// 支付薪水
/// </summary>
private void ApplySalaries() {
var economy = _session.State.Economy;
foreach (var student in _session.State.Roster.Students)
economy.Money -= student.Type == StudentModel.StudentType.MasterCandidate
? MasterSalary
: DoctorSalary;
foreach (var staff in _session.State.Roster.Staffs)
economy.Money -= staff.Type == StaffModel.StaffType.PostDoc
? PostDocSalary
: JuniorFacultySalary;
}
/// <summary>
/// 计算利息
/// </summary>
private void ApplyInterest() {
var economy = _session.State.Economy;
UpdateInterestRate();
if (economy.Money <= 0 || economy.InterestRate <= 0) return;
var interest = (int)(economy.Money * economy.InterestRate);
economy.Money += interest;
}
/// <summary>
/// 更新利率
/// </summary>
private void UpdateInterestRate() {
var economy = _session.State.Economy;
var mentor = _session.State.Roster.Mentor;
if (mentor?.Core.Tags.DisciplineId == CoreIds.DisciplineEconomics)
economy.InterestRate = Math.Max(economy.InterestRate, 0.1f);
}
/// <summary>
/// 添加论文
/// </summary>
private void AddPaper(PaperRank rank) {
var inventory = _session.State.Inventory;
if (!inventory.PaperCounts.ContainsKey(rank)) inventory.PaperCounts[rank] = 0;
inventory.PaperCounts[rank] += 1;
}
/// <summary>
/// 添加物品
/// </summary>
private void AddItem(string itemId, int count) {
if (string.IsNullOrWhiteSpace(itemId)) return;
var inventory = _session.State.Inventory;
if (!inventory.ItemCounts.ContainsKey(itemId)) inventory.ItemCounts[itemId] = 0;
inventory.ItemCounts[itemId] += count;
}
/// <summary>
/// 根据难度获取论文等级
/// </summary>
private PaperRank GetPaperRankByDifficulty(TaskDifficulty difficulty) {
return difficulty switch {
TaskDifficulty.Water => PaperRank.C,
TaskDifficulty.Standard => PaperRank.B,
TaskDifficulty.Hardcore => PaperRank.A,
TaskDifficulty.BlackBox => PaperRank.S,
_ => PaperRank.C
};
}
}
/// <summary>
/// 羁绊/协同系统
/// </summary>
public sealed class SynergySystem : IGameSystem {
private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
_session = session;
}
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
RecalculateSynergy();
}
/// <summary>
/// 重新计算协同
/// </summary>
private void RecalculateSynergy() {
var state = _session.State.Synergy;
state.ArchetypeStacks.Clear();
state.RoleStacks.Clear();
state.ActiveSynergyIds.Clear();
ClearModifiers(state.ActiveModifiers);
CountUnitTags(state);
ApplySynergyDefinitions(state);
}
/// <summary>
/// 统计单位标签
/// </summary>
private void CountUnitTags(SynergyState synergy) {
var roster = _session.State.Roster;
AddUnitTags(synergy, roster.Mentor?.Core);
foreach (var student in roster.Students) AddUnitTags(synergy, student.Core);
foreach (var staff in roster.Staffs) AddUnitTags(synergy, staff.Core);
}
/// <summary>
/// 添加单位标签
/// </summary>
private void AddUnitTags(SynergyState synergy, UnitModel unit) {
if (unit == null) return;
foreach (var archetypeId in unit.Tags.ArchetypeIds) AddStack(synergy.ArchetypeStacks, archetypeId);
foreach (var roleId in unit.Tags.RoleIds) AddStack(synergy.RoleStacks, roleId);
}
/// <summary>
/// 增加堆叠计数
/// </summary>
private void AddStack(Dictionary<string, int> stacks, string id) {
if (string.IsNullOrWhiteSpace(id)) return;
if (!stacks.ContainsKey(id)) stacks[id] = 0;
stacks[id] += 1;
}
/// <summary>
/// 应用协同定义
/// </summary>
private void ApplySynergyDefinitions(SynergyState synergy) {
foreach (var archetype in _session.Content.Archetypes.Values)
ApplySynergyTier(archetype.Header.Id, archetype.Tiers, synergy.ArchetypeStacks, synergy);
foreach (var role in _session.Content.Roles.Values)
ApplySynergyTier(role.Header.Id, role.Tiers, synergy.RoleStacks, synergy);
}
/// <summary>
/// 应用协同层级
/// </summary>
private void ApplySynergyTier(string id, List<SynergyTier> tiers, Dictionary<string, int> stacks,
SynergyState synergy) {
if (string.IsNullOrWhiteSpace(id)) return;
stacks.TryGetValue(id, out var count);
foreach (var tier in tiers) {
if (count < tier.RequiredCount) continue;
var synergyId = $"{id}@{tier.RequiredCount}";
synergy.ActiveSynergyIds.Add(synergyId);
MergeModifiers(synergy.ActiveModifiers, tier.Modifiers);
}
}
/// <summary>
/// 合并修正
/// </summary>
private void MergeModifiers(ModifierBundle target, ModifierBundle source) {
if (target == null || source == null) return;
target.AttributeModifiers.AddRange(source.AttributeModifiers);
target.StatusModifiers.AddRange(source.StatusModifiers);
target.ResourceModifiers.AddRange(source.ResourceModifiers);
target.RuleIds.AddRange(source.RuleIds);
}
/// <summary>
/// 清除修正
/// </summary>
private void ClearModifiers(ModifierBundle bundle) {
bundle.AttributeModifiers.Clear();
bundle.StatusModifiers.Clear();
bundle.ResourceModifiers.Clear();
bundle.RuleIds.Clear();
}
}
/// <summary>
/// 分配系统
/// </summary>
public sealed class AssignmentSystem : IGameSystem {
private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) {
_session = session;
}
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) {
// 预留:人员分配、交接惩罚等
}
}