Files
Continentis/Assets/Scripts/MainGame/MainGameManager.cs

247 lines
11 KiB
C#
Raw Normal View History

2025-10-03 00:02:43 -04:00
using System.Collections.Generic;
using Continentis.MainGame.Character;
2026-04-01 12:23:27 -04:00
using Continentis.MainGame.Saving;
2025-10-23 00:49:44 -04:00
using SLSFramework.General;
using SLSFramework.UModAssistance;
2025-10-03 00:02:43 -04:00
using UnityEngine;
2026-04-01 12:23:27 -04:00
using UnityEngine.SceneManagement;
2025-10-03 00:02:43 -04:00
namespace Continentis.MainGame
{
2026-04-01 12:23:27 -04:00
// ── 常量与核心字段 ──────────────────────────────────────────────────────────
2025-10-03 00:02:43 -04:00
public partial class MainGameManager : Singleton<MainGameManager>
{
2026-04-01 12:23:27 -04:00
private const string CombatSceneName = "GameScene";
private const string MenuSceneName = "MenuScene";
2025-10-23 00:49:44 -04:00
public readonly HashSet<string> elementTags = new HashSet<string>()
{
2026-04-01 12:23:27 -04:00
"Wind", "Fire", "Ice", "Earth", "Storm", "Light", "Darkness", "Physics"
2025-10-23 00:49:44 -04:00
};
2026-04-01 12:23:27 -04:00
2025-10-03 00:02:43 -04:00
public BasePrefabs basePrefabs;
2025-10-23 00:49:44 -04:00
}
2026-04-01 12:23:27 -04:00
// ── 游戏数据字段 ────────────────────────────────────────────────────────────
2025-10-23 00:49:44 -04:00
public partial class MainGameManager
{
2026-04-01 12:23:27 -04:00
/// <summary>全关键词合并表,由各 Mod 的 KeywordData 合并而来。</summary>
2025-10-23 00:49:44 -04:00
public KeywordData keywordData;
2026-04-01 12:23:27 -04:00
/// <summary>当前跑局的存档快照既是运行时状态也是持久化来源。null 表示无进行中的跑局。</summary>
public RunSave currentRun;
/// <summary>当前战斗的玩家英雄 CharacterData 列表,由 PrepareCombat() 填充。</summary>
public List<CharacterData> playerHeroDataList = new List<CharacterData>();
/// <summary>当前战斗的敌方 CharacterData 列表,由 PrepareCombat() 填充。</summary>
public List<CharacterData> enemyDataList = new List<CharacterData>();
2025-10-23 00:49:44 -04:00
}
2026-04-01 12:23:27 -04:00
// ── 初始化 ──────────────────────────────────────────────────────────────────
2025-10-23 00:49:44 -04:00
public partial class MainGameManager
{
protected override void Awake()
2025-10-03 00:02:43 -04:00
{
2025-10-23 00:49:44 -04:00
base.Awake();
2026-04-01 12:23:27 -04:00
// 合并所有 Mod 的 KeywordData 到全局查询表
2025-10-23 00:49:44 -04:00
keywordData = ScriptableObject.CreateInstance<KeywordData>();
2026-04-01 12:23:27 -04:00
foreach (KeyValuePair<string, ScriptableObject> pair in ModManager.Database[typeof(KeywordData)])
2025-10-23 00:49:44 -04:00
{
KeywordData data = pair.Value as KeywordData;
foreach (var keyword in data!.interpretedKeywords)
{
keywordData.interpretedKeywords.TryAdd(keyword.Key, keyword.Value);
}
}
2026-04-01 12:23:27 -04:00
// 消费 InformationTransistor 中的主菜单意图
InfoTransistor transistor = InfoTransistor.Instance;
if (transistor == null)
2025-10-03 00:02:43 -04:00
{
2026-04-01 12:23:27 -04:00
Debug.LogError("[MainGame] InformationTransistor 未找到,无法确定跑局意图。");
return;
2025-10-03 00:02:43 -04:00
}
2026-04-01 12:23:27 -04:00
InfoTransistor.Menu.MenuIntent intent = transistor.menuToMainGame.menuIntent;
RunConfig config = transistor.menuToMainGame.pendingRunConfig;
Debug.Log($"[MainGame] Awake收到主菜单意图 {intent}RunConfig {(config != null ? config.name : "null")}。");
// 消费后重置,避免场景重新加载时重复执行
transistor.menuToMainGame.menuIntent = InfoTransistor.Menu.MenuIntent.None;
transistor.menuToMainGame.pendingRunConfig = null;
2025-10-03 00:02:43 -04:00
2026-04-01 12:23:27 -04:00
switch (intent)
{
case InfoTransistor.Menu.MenuIntent.StartNewRun:
StartNewRun(config);
break;
case InfoTransistor.Menu.MenuIntent.ContinueRun:
ContinueRun();
break;
default:
Debug.LogWarning("[MainGame] Awake 时未收到有效的跑局意图,跳过初始化。");
break;
}
}
}
// ── 跑局流程 API ────────────────────────────────────────────────────────────
public partial class MainGameManager
{
/// <summary>
/// 开始一次新跑局:从 RunConfig 初始化英雄和关卡序列,写入开局存档,准备首场战斗数据。
/// RunConfig 仅在此处读取一次,结果完整拷贝到 RunSave后续不再查询它。
/// 由 Awake 消费 StartNewRun 意图时调用。
/// </summary>
private void StartNewRun(RunConfig config)
{
currentRun = new RunSave(
Random.Range(int.MinValue, int.MaxValue),
config.startingGold,
new List<string>(config.encounterSequenceIDs)
);
foreach (string heroID in config.initialHeroIDs)
{
CharacterData data = CharacterData.Get(heroID);
if (data == null)
{
Debug.LogError($"[MainGame] StartNewRun找不到角色数据 '{heroID}',已跳过。");
continue;
}
int maxHP = Mathf.RoundToInt(data.coreAttributes.TryGetValue("MaximumHealth", out float hp) ? hp : 0f);
HeroSave hero = new HeroSave(heroID, maxHP, maxHP);
foreach (string cardID in data.initialDeckRef)
hero.deck.Add(new CardSave(cardID));
currentRun.heroes.Add(hero);
}
SaveManager.Instance.SaveRunSave(currentRun);
Debug.Log($"[MainGame] 新跑局开始(配置:{config.name},节点数:{currentRun.combatNodeIDs.Count},种子:{currentRun.randomSeed}");
PrepareCombat(currentRun.currentNodeIndex);
}
/// <summary>
/// 从 SaveManager 缓存恢复跑局并准备当前节点的战斗数据。
/// 由 Awake 消费 ContinueRun 意图时调用。
/// </summary>
private void ContinueRun()
{
currentRun = SaveManager.Instance.GetRunSave();
if (currentRun == null)
{
Debug.LogError("[MainGame] ContinueRunSaveManager 缓存中无进行中的跑局。");
return;
}
Debug.Log($"[MainGame] 继续跑局(节点 {currentRun.currentNodeIndex} / {currentRun.combatNodeIDs.Count}");
PrepareCombat(currentRun.currentNodeIndex);
}
/// <summary>
/// 根据节点索引填充玩家英雄和敌方角色列表。
/// 仅准备运行时数据供 CombatMainManager 使用,不涉及场景加载。
/// </summary>
private void PrepareCombat(int nodeIndex)
{
if (nodeIndex >= currentRun.combatNodeIDs.Count)
{
Debug.Log("[MainGame] 所有关卡已通关,跑局结束!");
// TODO: Phase 4 — 显示通关结算界面
return;
}
// 填充玩家英雄列表
playerHeroDataList.Clear();
foreach (HeroSave hero in currentRun.heroes)
{
CharacterData data = CharacterData.Get(hero.characterDataID);
if (data != null)
playerHeroDataList.Add(data);
else
Debug.LogError($"[MainGame] PrepareCombat找不到角色数据 '{hero.characterDataID}'。");
}
// 填充敌方列表
string nodeID = currentRun.combatNodeIDs[nodeIndex];
CombatNodeData nodeData = CombatNodeData.Get(nodeID);
if (nodeData == null)
{
Debug.LogError($"[MainGame] PrepareCombat找不到 CombatNodeData '{nodeID}'。");
return;
}
enemyDataList.Clear();
foreach (string enemyID in nodeData.enemyCharacterIDs)
{
CharacterData data = CharacterData.Get(enemyID);
if (data != null)
enemyDataList.Add(data);
else
Debug.LogError($"[MainGame] PrepareCombat找不到敌方角色数据 '{enemyID}'。");
}
}
/// <summary>
/// 战斗结束回调,由 CombatMainManager 调用。
/// 胜利时回写 HP、推进节点并存档然后通过 InfoTransistor 写入 ContinueRun 意图并重载场景。
/// 失败时清除跑局存档(保留 PlayerSave并返回主菜单。
/// 注意MainGameManager 是场景局部单例,重载场景后当前实例会被销毁,
/// 新实例的 Awake 会从 InfoTransistor 消费意图并重新初始化。
/// </summary>
public void ExitCombat(bool isVictory, IReadOnlyList<(string heroID, int currentHP)> heroResults = null)
{
if (isVictory)
{
if (heroResults != null)
{
foreach (var (heroID, hp) in heroResults)
{
HeroSave hero = currentRun.heroes.Find(h => h.characterDataID == heroID);
if (hero != null)
hero.currentHP = hp;
}
}
currentRun.currentNodeIndex++;
SaveManager.Instance.SaveRunSave(currentRun);
Debug.Log($"[MainGame] 战斗胜利,节点推进至 {currentRun.currentNodeIndex},存档已写入。");
// 检查是否所有节点已通关
if (currentRun.currentNodeIndex >= currentRun.combatNodeIDs.Count)
{
Debug.Log("[MainGame] 所有关卡已通关,跑局结束!");
// TODO: Phase 4 — 显示通关结算界面,之后返回主菜单
return;
}
// 写入 ContinueRun 意图,重载场景以启动下一场战斗
// TODO: Phase 4 — 先进入奖励选择界面,完成后再走此流程
InfoTransistor transistor = InfoTransistor.Instance;
transistor.menuToMainGame.menuIntent = InfoTransistor.Menu.MenuIntent.ContinueRun;
transistor.menuToMainGame.pendingRunConfig = null;
SceneManager.LoadScene(CombatSceneName);
}
else
2025-10-03 00:02:43 -04:00
{
2026-04-01 12:23:27 -04:00
SaveManager.Instance.DeleteRunSave();
Debug.Log("[MainGame] 战斗失败跑局存档已清除PlayerSave 保留),返回主菜单。");
SceneManager.LoadScene(MenuSceneName);
2025-10-03 00:02:43 -04:00
}
}
}
2026-04-01 12:23:27 -04:00
// ── 静态工具方法 ────────────────────────────────────────────────────────────
2025-10-03 00:02:43 -04:00
public partial class MainGameManager
{
2026-04-01 12:23:27 -04:00
public static void GenerateInfoText(string content, CombatCharacterViewBase characterView, Color color = default, float size = 1)
2025-10-24 09:11:22 -04:00
=> Instance.basePrefabs.GenerateInfoText(content, characterView, color, size);
2025-10-03 00:02:43 -04:00
}
}