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

221 lines
6.4 KiB
C#
Raw Permalink 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.Collections.Generic;
using Core;
using Godot;
using Models;
/// <summary>
/// 游戏主控制节点MVC 的桥接层)
/// 设计说明:
/// 1) 负责初始化 GameSession并把输入/信号转交给 Controller/System。
/// 2) 仅做“流程调度”,不直接计算复杂数值,避免逻辑堆积。
/// 注意事项:
/// - View 与 Model 的绑定应逐步迁移到独立 View 脚本,避免在此类膨胀。
/// 未来扩展:
/// - 可拆分为 SceneRoot + UIController + DebugController分层管理。
/// </summary>
public partial class GameManager : Node {
// --- Signals ---
/// <summary>
/// 阶段变更信号
/// </summary>
/// <param name="phase">阶段枚举的整数值</param>
[Signal]
public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase
/// <summary>
/// 回合变更信号
/// </summary>
/// <param name="turn">当前回合数</param>
[Signal]
public delegate void TurnChangedEventHandler(int turn);
/// <summary>
/// 双倍大小的箭头光标资源
/// </summary>
public static readonly Resource Arrow2X =
ResourceLoader.Load("res://temp_res/kenney_ui-pack-space-expansion/PNG/Extra/Double/cursor_f.png");
/// <summary>
/// 最大回合数
/// </summary>
[Export] public int MaxTurns = 30;
/// <summary>
/// 指示当前是否处于教程模式
/// </summary>
public static bool IsTutorial { get; private set; }
/// <summary>
/// 下一个场景的路径
/// </summary>
public static string NextScene { get; set; } = null;
// --- Global State ---
/// <summary>
/// 游戏会话实例
/// </summary>
public GameSession Session { get; private set; }
/// <summary>
/// 当前游戏状态
/// </summary>
public GameState State => Session?.State;
/// <summary>
/// 游戏控制器
/// </summary>
public GameController Controller { get; private set; }
/// <summary>
/// 当前游戏阶段
/// </summary>
public GamePhase CurrentPhase => State?.Turn.Phase ?? GamePhase.Planning;
/// <summary>
/// 当前回合数
/// </summary>
public int CurrentTurn => State?.Turn.CurrentTurn ?? 1;
// --- Domain Model ---
/// <summary>
/// 导师模型
/// </summary>
public MentorModel Mentor => State?.Roster.Mentor;
/// <summary>
/// 学生列表
/// </summary>
public List<StudentModel> Students => State?.Roster.Students;
/// <summary>
/// 当前活动任务列表
/// </summary>
public List<TaskModel> ActiveTasks => State?.Tasks.ActiveTasks;
/// <summary>
/// 经济状态
/// </summary>
public EconomyState Economy => State?.Economy;
// Singleton instance access (if needed, though Godot uses node paths)
/// <summary>
/// 单例实例
/// </summary>
public static GameManager Instance { get; private set; }
/// <summary>
/// 进入场景树时调用
/// </summary>
public override void _EnterTree() {
Instance = this;
}
/// <summary>
/// 准备就绪时调用
/// </summary>
public override void _Ready() {
Input.SetCustomMouseCursor(Arrow2X);
// MVP Initialization
InitializeGame();
}
/// <summary>
/// 初始化游戏数据
/// </summary>
private void InitializeGame() {
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 TaskModel("深度学习导论", TaskKind.AcademicExploration, 1000f, 3);
t1.Reward.Money = 1000;
t1.Reward.Reputation = 5;
ActiveTasks.Add(t1);
GD.Print("Game Initialized. Phase: Planning, Turn: 1");
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧调用
/// </summary>
/// <param name="delta">距离上一帧的时间间隔</param>
public override void _Process(double delta) {
if (CurrentPhase == GamePhase.Execution) {
// Update game logic (timers, etc.)
// In a real implementation, this might manage the global timer for the day/week
}
Session?.Tick((float)delta);
}
/// <summary>
/// 结束筹备阶段,开始执行
/// </summary>
public void StartExecution() {
if (CurrentPhase != GamePhase.Planning) return;
Controller.StartExecution();
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
GD.Print("Phase Changed: Execution");
// Notify all agents to start working (this would be handled by a system listening to the signal)
}
/// <summary>
/// 结束执行阶段(通常由时间耗尽或玩家手动触发),进入结算
/// </summary>
public void EndExecution() {
if (CurrentPhase != GamePhase.Execution) return;
Controller.EndExecution();
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
GD.Print("Phase Changed: Review");
PerformReview();
}
/// <summary>
/// 执行结算逻辑,并准备下一回合
/// </summary>
private void PerformReview() {
Session.Systems.Task.ResolveEndOfTurn();
Session.Events.Publish(new TurnEndedEvent(CurrentTurn));
GD.Print("Review Complete. Waiting for Next Turn confirmation.");
}
/// <summary>
/// 开始新的回合
/// </summary>
public void StartNextTurn() {
if (CurrentPhase != GamePhase.Review) return;
Controller.StartNextTurn();
if (CurrentTurn > State.Turn.MaxTurns) {
GD.Print("Game Over!");
// Handle Game Over
return;
}
State.Turn.Phase = GamePhase.Planning;
EmitSignal(SignalName.TurnChanged, CurrentTurn);
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
// Refresh resources/AP
Mentor.Resources.Energy.Current.Value = Mentor.Resources.Energy.UpperThreshold;
GD.Print($"Turn {CurrentTurn} Started. Phase: Planning");
}
}