Files
2025TapTapGameJam/Assets/Script/Gameplay/Combat/CombatFlowManager.cs

177 lines
6.2 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;
using Core;
using Gameplay.Enemy;
using Gameplay.Player;
using Interface;
using UnityEngine;
namespace Gameplay
{
/// <summary>
/// 战斗流程管理器(回合制、循环抽牌)
/// 使用方法:在场景中挂载一个空物体并配置 player 与 enemy 的 Combatant 引用。
/// 该管理器通过事件回调让 UI 或其它系统接入“双方牌都打空时”的选择(继续/逃跑)。
/// </summary>
public class CombatFlowManager : MonoSingleton<CombatFlowManager>
{
[Header("Participants")]
public PlayerController player;
public EnemyController enemy;
[Header("Flow Settings")]
[Tooltip("玩家是否先手true=玩家先出一张)")]
public bool playerStarts = true;
[Tooltip("每出一张牌后的间隔(秒)")]
public float turnDelay = 0.8f;
// Events
public event Action OnCombatStarted;
public event Action<ICharacter, Card> OnCardPlayed;
public event Action<ICharacter> OnCombatEnded; // 参数为胜利方null 表示平局或逃跑)
public event Action OnBothEmpty; // 当双方都没有卡可以出时触发UI 需要通过 ContinueAfterBothEmpty 或 EscapeFromCombat 响应
// internal state
private Coroutine runningRoutine;
private bool waitingForDecision = false;
private bool decisionContinue = false;
private bool decisionEscape = false;
/// <summary>
/// 启动战斗流程(也可以在 Inspector 中提前绑定 player/enemy然后只调用 StartCombat()
/// </summary>
public void StartCombat(PlayerController playerController = null, EnemyController enemyController = null)
{
if (playerController != null) player = playerController;
if (enemyController != null) enemy = enemyController;
if (player == null || enemy == null)
{
Debug.LogError("CombatFlowManager: player 或 enemy 未设置。");
return;
}
// 初始化双方状态
player.StartCombat();
enemy.StartCombat();
player.InitializeDeckCycle();
enemy.InitializeDeckCycle();
runningRoutine = StartCoroutine(CombatRoutine());
OnCombatStarted?.Invoke();
}
public void StopCombat()
{
if (runningRoutine != null) StopCoroutine(runningRoutine);
runningRoutine = null;
}
private IEnumerator CombatRoutine()
{
ICharacter current = playerStarts ? player : enemy;
while (true)
{
// 结束检查
if (player.IsDead || enemy.IsDead)
{
ICharacter winner = player.IsDead ? enemy : player;
OnCombatEnded?.Invoke(winner);
runningRoutine = null;
yield break;
}
bool playerHas = player.HasCardsLeft();
bool enemyHas = enemy.HasCardsLeft();
if (!playerHas && !enemyHas)
{
// 双方都打空,等待玩家选择(由 UI 调用 ContinueAfterBothEmpty 或 EscapeFromCombat
waitingForDecision = true;
decisionContinue = false;
decisionEscape = false;
OnBothEmpty?.Invoke();
// 等待选择
while (waitingForDecision)
yield return null;
if (decisionEscape)
{
// 玩家选择逃跑 -> 结束战斗,胜者设为 null或按需要设成敌人/玩家)
OnCombatEnded?.Invoke(null);
runningRoutine = null;
yield break;
}
if (decisionContinue)
{
// 重新从卡册读取(重置抽牌队列),保持生命与其它状态不变
player.InitializeDeckCycle();
enemy.InitializeDeckCycle();
// 继续循环(当前先手不变)
yield return null;
continue;
}
}
// 当前回合:如果当前方没有牌则跳过(空过)
if (current.HasCardsLeft())
{
var card = current.GetNextCard();
ICharacter target;
if (current is PlayerController)
{
target = enemy;
}
else
{
target = player;
}
// 触发卡牌效果(当前仅支持直接伤害)
if (card != null)
{
card.PlayCard(new CardContext(current, target));
OnCardPlayed?.Invoke(current, card);
}
// 检查死亡(将在下一循环顶部处理)
yield return new WaitForSeconds(turnDelay);
}
else
{
// 跳过一轮,不延迟太久
yield return new WaitForSeconds(0.1f);
}
// 切换行动方
current = (current == player) ? enemy : player;
}
}
/// <summary>
/// 当双方都打空时UI 调用此方法选择继续下一轮(重新从卡册读取卡牌)
/// </summary>
public void ContinueAfterBothEmpty()
{
if (!waitingForDecision) return;
decisionContinue = true;
waitingForDecision = false;
}
/// <summary>
/// 当双方都打空时UI 调用此方法选择逃跑
/// </summary>
public void EscapeFromCombat()
{
if (!waitingForDecision) return;
decisionEscape = true;
waitingForDecision = false;
}
}
}