using System;
using System.Collections.Generic;
using SepCore.Battle;
using SepCore.Definition;
using SepCore.UI;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.CustomComponent
{
///
/// 单局战斗组件,也是战斗模块的唯一外部入口。
/// 负责本局临时角色状态、计时、探索暂停标志、战斗占用和当前唯一的 BattleRuntime。
/// 外部通过 TryStartBattle 启动战斗、CloseBattle 关闭调试战斗;
/// 战斗规则(调度、指令校验、效果、敌人决策)在内部共享同一个 BattleRuntime,
/// 不为模块内部的函数调用建立请求/响应 DTO。
/// 本局种子与共享随机源由独立的 RandomComponent 持有,战斗只沿用,不创建私有随机源。
///
public class TurnBattleComponent : GameFrameworkComponent
{
private readonly List _players = new List();
private float _elapsedMs;
private bool _timerPaused;
private bool _explorationPaused;
private bool _battleActive;
private BattleRuntime _runtime;
private IBattleConfigProvider _config;
private Action _onCompleted;
private Action _stepListener;
private Coroutine _autoAdvanceRoutine;
///
/// 敌人自动行动前的间歇时间(秒),用于让 UI 展示行动过程。
///
private const float AutoAdvanceDelaySeconds = 0.6f;
///
/// 获取本局临时角色状态列表(只读)。
///
public IReadOnlyList Players => _players;
///
/// 获取本局已流逝时间(毫秒)。
///
public long RunElapsedMs => (long)_elapsedMs;
///
/// 获取单局计时是否已暂停。
///
public bool IsTimerPaused => _timerPaused;
///
/// 获取探索更新是否已暂停。
/// 战斗期间为 true,地图更新统一以此作为门禁。
///
public bool IsExplorationPaused => _explorationPaused;
///
/// 获取当前是否存在战斗占用。
/// 战斗重复进入由此标志拒绝。
///
public bool IsBattleActive => _battleActive;
///
/// 获取当前唯一的战斗运行时;仅战斗期间非 null。
///
internal BattleRuntime Runtime => _runtime;
///
/// 开始一局新的单局:重置全部临时状态。
///
public void BeginRun()
{
ResetRunState();
}
///
/// 结束当前单局,清空全部临时状态。
///
public void EndRun()
{
ResetRunState();
}
///
/// 用指定状态替换本局临时角色列表,保持战备顺序。
///
/// 新的临时角色状态,可为空。
public void ReplacePlayers(IEnumerable players)
{
_players.Clear();
if (players != null)
{
_players.AddRange(players);
}
}
///
/// 尝试开始一场战斗,是战斗模块的唯一外部入口。
/// 校验通过后预留战斗占用、暂停探索更新与单局计时并打开 BattleForm;
/// 校验失败不消耗随机数、不修改单局状态。
/// 战斗完成时通过 onCompleted 一次性回调返回 BattleResult;调试入口可不提供回调。
///
/// 探索层创建的遭遇输入。
/// 战斗结束的一次性完成回调,可为 null。
/// 是否成功开始。
public bool TryStartBattle(BattleEncounter encounter, Action onCompleted)
{
if (encounter == null)
{
Log.Error("Can not start battle with null encounter.");
return false;
}
if (_battleActive)
{
Log.Warning("Can not start battle because battle occupancy is already active.");
return false;
}
if (GameEntry.Random == null || GameEntry.Random.Random == null)
{
Log.Warning("Can not start battle because run random source is not initialized.");
return false;
}
if (_players.Count == 0)
{
Log.Warning("Can not start battle because no run player state exists.");
return false;
}
if (GameEntry.Luban == null || !GameEntry.Luban.IsReady)
{
Log.Warning("Can not start battle because battle config is not ready.");
return false;
}
if (_config == null)
{
_config = new LubanBattleConfigProvider();
}
BattleRuntime runtime = BattleRuntime.Create(encounter, _players, _config, GameEntry.Random.Random);
if (runtime == null)
{
Log.Warning("Can not start battle because battle runtime can not be created from given encounter/players/config.");
return false;
}
_runtime = runtime;
_onCompleted = onCompleted;
_battleActive = true;
_explorationPaused = true;
_timerPaused = true;
GameEntry.UI.OpenUIForm(UIFormType.BattleForm);
Log.Info("Battle started with encounter '{0}'.", encounter.EncounterId);
// M2:敌人速度更高时开局行动者为敌人,同样按间歇节奏自动推进
ScheduleAutoAdvance();
return true;
}
///
/// 提交当前玩家指令并同步推进单次行动,返回本次推进的行动记录、最新视图和可能的最终结果。
/// 非法指令不消耗行动机会;玩家行动后若轮到敌人,由组件按间歇节奏自动推进敌人回合,
/// 每一步通过已注册的推进监听推送给 UI。
/// 战斗完成后向 onCompleted 一次性回调返回 BattleResult。
///
/// 当前玩家行动者的指令。
/// 本次玩家行动的推进结果;当前没有进行中的战斗时返回 null。
public BattleStep SubmitCommand(BattleCommand command)
{
if (_runtime == null || _runtime.IsCompleted)
{
return null;
}
BattleStep step = _runtime.SubmitCommand(command);
if (step.Result != null)
{
ApplyResultWriteback(step.Result);
Action callback = _onCompleted;
_onCompleted = null;
callback?.Invoke(step.Result);
}
else
{
ScheduleAutoAdvance();
}
return step;
}
///
/// 注册战斗推进监听;UI 通过它接收组件自动推进(敌人行动)产生的每一步。
///
/// 推进监听,传入 null 取消注册。
public void SetStepListener(Action listener)
{
_stepListener = listener;
}
///
/// 获取当前战斗的只读视图;没有进行中的战斗时返回 null。
///
public BattleViewState GetViewState()
{
return _runtime != null ? _runtime.BuildViewState() : null;
}
///
/// 关闭当前战斗壳层并恢复探索更新与单局计时。
/// M0 调试入口专用;M5 起由战斗结果统一回写单局状态后关闭。
///
public void CloseBattle()
{
if (!_battleActive)
{
return;
}
UGuiForm form = GameEntry.UI.GetUIForm(UIFormType.BattleForm);
if (form != null)
{
GameEntry.UI.CloseUIForm(form);
}
_runtime = null;
_onCompleted = null;
StopAutoAdvance();
_stepListener = null;
_explorationPaused = false;
_timerPaused = false;
_battleActive = false;
Log.Info("Battle closed.");
}
///
/// 设置探索更新暂停状态。
///
/// 是否暂停探索更新。
public void SetExplorationPaused(bool paused)
{
_explorationPaused = paused;
}
///
/// 设置单局计时暂停状态。
///
/// 是否暂停单局计时。
public void SetTimerPaused(bool paused)
{
_timerPaused = paused;
}
private void Update()
{
if (_timerPaused)
{
return;
}
_elapsedMs += Time.deltaTime * 1000f;
}
///
/// 当前行动者是敌人且战斗未结束时,启动带间歇的自动推进协程。
///
private void ScheduleAutoAdvance()
{
if (_autoAdvanceRoutine != null)
{
return;
}
if (_runtime == null || _runtime.IsCompleted || _runtime.CurrentActor == null ||
_runtime.CurrentActor.Faction != BattleFaction.Enemy)
{
return;
}
_autoAdvanceRoutine = StartCoroutine(AutoAdvanceRoutine());
}
private System.Collections.IEnumerator AutoAdvanceRoutine()
{
// 战斗 UI 打开是异步资源加载,等推进监听注册后再推第一步,避免敌人先手事件丢失
int waitFrames = 0;
while (_stepListener == null && _runtime != null && !_runtime.IsCompleted && waitFrames++ < 600)
{
yield return null;
}
while (_runtime != null && !_runtime.IsCompleted &&
_runtime.CurrentActor != null && _runtime.CurrentActor.Faction == BattleFaction.Enemy)
{
yield return new WaitForSeconds(AutoAdvanceDelaySeconds);
BattleStep step = _runtime.AdvanceEnemyTurn();
if (step.Result != null)
{
ApplyResultWriteback(step.Result);
Action callback = _onCompleted;
_onCompleted = null;
callback?.Invoke(step.Result);
}
_stepListener?.Invoke(step);
}
_autoAdvanceRoutine = null;
}
private void StopAutoAdvance()
{
if (_autoAdvanceRoutine != null)
{
StopCoroutine(_autoAdvanceRoutine);
_autoAdvanceRoutine = null;
}
}
///
/// 非 TotalDefeat 结果把玩家战后 HP/MP 回写到单局临时状态。
/// 首版保留原始战斗值;M5 按结束矩阵统一应用阵亡恢复 1/1 等规则。
///
private void ApplyResultWriteback(BattleResult result)
{
if (result.Outcome == BattleOutcome.TotalDefeat)
{
return;
}
foreach (BattlePlayerResult playerResult in result.Players)
{
RunPlayerState state = _players.Find(player => player.CharacterId == playerResult.CharacterId);
if (state != null)
{
state.CurrentHp = playerResult.CurrentHp;
state.CurrentMp = playerResult.CurrentMp;
}
}
}
private void ResetRunState()
{
_players.Clear();
_elapsedMs = 0;
_timerPaused = false;
_explorationPaused = false;
_battleActive = false;
_runtime = null;
_onCompleted = null;
StopAutoAdvance();
_stepListener = null;
}
}
}