Files
Continentis/Assets/Scripts/MainGame/Combat/CombatMainManager.cs
SoulliesOfficial c3b1561375 更新
2026-04-01 12:23:27 -04:00

355 lines
14 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 System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.Character;
using Continentis.MainGame.Commands;
using Continentis.MainGame.Saving;
using Continentis.MainGame.UI;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame.Combat
{
/// <summary>
/// 战斗进行状态枚举
/// </summary>
public enum CombatState
{
InProgress,
ConfirmingRound,
Victory,
Defeat
}
public partial class CombatMainManager : Singleton<CombatMainManager>
{
public CombatCharacterController characterController;
}
public partial class CombatMainManager
{
[Header("Combat State")]
public int currentRound;
public int currentActionIndex;
public CharacterBase currentCharacter;
public CombatState combatState;
}
public partial class CombatMainManager
{
[Header("Events")]
public CombatEventCollection eventCollection;
}
public partial class CombatMainManager
{
protected override void Awake()
{
base.Awake();
characterController = new CombatCharacterController();
eventCollection = new CombatEventCollection();
}
private void Start()
{
characterController.Initialize(
MainGameManager.Instance.playerHeroDataList,
MainGameManager.Instance.enemyDataList);
StartCombat();
CombatUIManager.Instance.UpdateAll();
}
}
public partial class CombatMainManager
{
public void StartCombat()
{
combatState = CombatState.InProgress;
foreach (CharacterBase character in characterController.characters)
character.InitializeCards();
currentRound = 0;
eventCollection.onCombatStart.Invoke();
foreach (CharacterBase character in characterController.characters)
character.DispatchCombatStart();
// 1.2b — 初始化 CombatLogs 并订阅事件
CombatLogs.Instance.Initialize(this);
NextRound();
}
public void NextRound()
{
// 战斗已结束则不再推进
if (combatState != CombatState.InProgress) return;
currentRound++;
// UI 反馈:回合提示动画(同步,纯 UI 无逻辑影响)
CombatUIManager.Instance.combatMainPage.roundHint.PlayRoundHint(currentRound);
// ── 纯逻辑初始化(无视觉反馈,同步执行)────────────────────────────
eventCollection.onRoundStart.Invoke();
foreach (CharacterBase character in characterController.characters)
character.actionCountThisRound = 0;
characterController.SetActionOrder();
CombatUIManager.Instance.combatMainPage.actionOrderDisplayer.InitializeTurnOrder();
currentActionIndex = 0;
// ── NPC 意图决策(同步,不产生视觉效果)────────────────────────────
foreach (CharacterBase character in characterController.characters)
{
if (character is CombatNPC npc)
{
npc.IntentionBrain();
npc.deckSubmodule.PoolPile.ForEach(card => card.weightSubmodule.RefreshCurrentWeight());
npc.intentionSubmodule.getIntendedCards.Invoke();
foreach (IntendedCard intendedCard in npc.intentionSubmodule.intendedCards)
{
intendedCard.cardInstance.GenerateIntentionCardView();
if (intendedCard.targets.Count > 0)
{
CardInstance card = intendedCard.cardInstance;
card.Targeting(intendedCard.targets[0]);
card.contentSubmodule.dirtyMark = true;
}
}
}
}
// ── 进入"回合前确认"阶段:等待玩家点击 Confirm ───────────────────
combatState = CombatState.ConfirmingRound;
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
{
CombatMainPage page = CombatUIManager.Instance.combatMainPage;
page.SetButtonAction(ConfirmRound, "Confirm", true);
}));
CommandBase waitSignal = Cmd.WaitForSignal(out confirmRoundSignal);
CommandQueueManager.Instance.AddCommand(waitSignal);
// ── 玩家确认后回合正式开始Buff 触发链入队 ────────────────────
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
{
combatState = CombatState.InProgress;
CombatUIManager.Instance.combatMainPage.SetButtonAction(
EndAction, "Waiting...", false);
}));
foreach (CharacterBase character in characterController.characters)
{
CharacterBase captured = character;
CommandQueueManager.Instance.AddCommand(Cmd.Do(() => captured.DispatchRoundStart()));
}
// ── 所有回合开始事件处理完毕后,进入第一个行动 ────────────────────
CommandQueueManager.Instance.AddCommand(Cmd.Do(NextAction));
}
/// <summary>
/// 玩家在"回合前确认"阶段点击 Confirm 后调用,释放队列暂停信号。
/// </summary>
public void ConfirmRound()
{
if (combatState != CombatState.ConfirmingRound) return;
confirmRoundSignal?.Invoke();
confirmRoundSignal = null;
}
// 持有当前回合的信号委托ConfirmRound() 调用后置 null 防止重复触发
private Action confirmRoundSignal;
public void NextAction()
{
// 战斗已结束则不再推进
if (combatState != CombatState.InProgress) return;
if (characterController.actionOrderList.Count == 0)
{
// 回合结束:所有角色的 onRoundEnd 和 Buff 触发均入队
foreach (CharacterBase character in characterController.characters)
{
CharacterBase captured = character;
CommandQueueManager.Instance.AddCommand(Cmd.Do(() => captured.DispatchRoundEnd()));
}
CommandQueueManager.Instance.AddCommand(Cmd.Do(NextRound));
return;
}
currentCharacter = characterController.actionOrderList[0];
CharacterBase actionCharacter = currentCharacter;
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
{
actionCharacter.DispatchActionStart();
actionCharacter.recordSubmodule.SetAction(currentRound, ++currentActionIndex);
}));
CombatMainPage combatMainPage = CombatUIManager.Instance.combatMainPage;
if (currentCharacter is PlayerHero playerHero)
{
playerHero.deckSubmodule.SetUpHandCardViews();
combatMainPage.handPile.isUpdatingLayout = false;
CommandQueueManager.Instance.AddCommand(
playerHero.deckSubmodule.DrawCards(playerHero.GetAttribute("DrawCardAmountPerAction")));
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
{
combatMainPage.handPile.isUpdatingLayout = true;
}));
combatMainPage.combatResourcesDisplayer.SetCharacter(playerHero);
combatMainPage.SetButtonAction(EndAction, "End Action", true);
}
else if (currentCharacter is CombatNPC)
{
string npcLabel = currentCharacter.fraction switch
{
Fraction.Enemy => "Enemy Action",
Fraction.Ally => "Ally Action",
_ => "Others Action"
};
combatMainPage.SetButtonAction(null, npcLabel, false);
CommandQueueManager.Instance.AddCommand(Cmd.Wait(0.25f));
// 2.3e — PreAction 钩子NPC 出牌前
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
currentCharacter.intentionSubmodule.currentIntention.PreAction()));
foreach (IntendedCard intendedCard in currentCharacter.intentionSubmodule.intendedCards)
{
IntendedCard captured = intendedCard;
currentCharacter.CheckAvailabilityAndSetTargets(captured.cardInstance, out captured.targets);
CommandQueueManager.Instance.AddCommand(Cmd.Sequential(
Cmd.Do(() => {
captured.cardInstance.Play(captured.targets, currentCharacter);
captured.cardInstance.DestroyIntentionCardView();
}),
Cmd.Wait(0.25f)
));
}
// 2.3e — PostAction 钩子NPC 出完全部卡牌后
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
currentCharacter.intentionSubmodule.currentIntention.PostAction()));
CommandQueueManager.Instance.AddCommand(Cmd.Do(EndAction));
}
currentCharacter.characterView.hudContainer.UpdateAllHUD();
CombatUIManager.Instance.combatMainPage.actionOrderDisplayer.avatars
.Find(avatar => avatar.character == currentCharacter)?.Highlight(true);
currentCharacter.actionCountThisRound++;
}
public void EndAction()
{
CombatUIManager.Instance.combatMainPage.SetButtonAction(null, "Waiting...", false);
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
{
currentCharacter.DispatchActionEnd();
if (currentCharacter is PlayerHero playerHero)
{
List<CardInstance> handPile = new List<CardInstance>(playerHero.deckSubmodule.HandPile);
List<CardInstance> cardToRetain = handPile.Where(c => c.HasKeyword("Retain")).ToList();
List<CardInstance> cardToExhaust = handPile.Where(c => c.HasKeyword("Ethereal")).ToList();
List<CardInstance> cardsToDiscard = handPile.Except(cardToRetain).Except(cardToExhaust).ToList();
CommandQueueManager.Instance.AddCommand(playerHero.deckSubmodule.ExhaustCards(cardToExhaust));
CommandQueueManager.Instance.AddCommand(playerHero.deckSubmodule.DiscardCards(cardsToDiscard, false));
}
characterController.actionOrderList.Remove(currentCharacter);
CombatUIManager.Instance.combatMainPage.actionOrderDisplayer.EndAction();
CommandQueueManager.Instance.AddCommand(Cmd.Do(CombatUIManager.Instance.combatMainPage.ClearAllCardViews));
CommandQueueManager.Instance.AddCommand(Cmd.Wait(0.5f));
CommandQueueManager.Instance.AddCommand(Cmd.Do(NextAction));
}));
}
}
public partial class CombatMainManager
{
/// <summary>
/// 结束战斗,触发胜负流程。由 CombatCharacterController.CheckCombatEnd() 调用。
/// </summary>
public void EndCombat(bool isVictory)
{
// 防止重复触发
if (combatState != CombatState.InProgress) return;
combatState = isVictory ? CombatState.Victory : CombatState.Defeat;
// 触发战斗结束全局事件
eventCollection.onCombatEnd.Invoke();
foreach (CharacterBase character in characterController.characters)
character.DispatchCombatEnd();
// 清理所有卡牌 Logic 的托管订阅
foreach (CharacterBase character in characterController.characters)
foreach (CardInstance card in character.deckSubmodule.GetAllCards())
card.cardLogic?.Dispose();
if (isVictory)
{
CombatVictory();
}
else
{
CombatDefeat();
}
}
/// <summary>
/// 战斗胜利处理:收集战后英雄 HP通知 MainGameManager 推进节点并存档。
/// </summary>
private void CombatVictory()
{
CombatUIManager.Instance.combatMainPage.SetButtonAction(null, "Victory!", false);
// 将玩家英雄的战后 HP 与 RunSave 中的 HeroSave 对应:按 currentRun.heroes 顺序匹配
var heroResults = new List<(string heroID, int currentHP)>();
List<HeroSave> runHeroes = MainGameManager.Instance.currentRun?.heroes;
if (runHeroes != null)
{
for (int i = 0; i < Mathf.Min(characterController.playerHeroes.Count, runHeroes.Count); i++)
{
int hp = Mathf.RoundToInt(characterController.playerHeroes[i].GetAttribute("Health"));
heroResults.Add((runHeroes[i].characterDataID, hp));
}
}
// CommandQueue 执行完毕后再切场景,避免队列中残余命令在新场景报错
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
MainGameManager.Instance.ExitCombat(isVictory: true, heroResults)));
}
/// <summary>
/// 战斗失败处理:通知 MainGameManager 删档并返回主菜单。
/// </summary>
private void CombatDefeat()
{
CombatUIManager.Instance.combatMainPage.SetButtonAction(null, "Defeat...", false);
CommandQueueManager.Instance.AddCommand(Cmd.Do(() =>
MainGameManager.Instance.ExitCombat(isVictory: false)));
}
}
}