211 lines
8.5 KiB
C#
211 lines
8.5 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using Continentis.MainGame.Card;
|
||
using Continentis.MainGame.Character;
|
||
using SLSFramework.General;
|
||
using UnityEngine;
|
||
|
||
namespace Continentis.MainGame.Combat
|
||
{
|
||
public enum CombatLogType
|
||
{
|
||
RoundStart,
|
||
ActionStart,
|
||
Attack,
|
||
BuffApply,
|
||
BuffRemove,
|
||
CardPlay,
|
||
Death
|
||
}
|
||
|
||
public class CombatLogEntry
|
||
{
|
||
public int round;
|
||
public int actionIndex;
|
||
public float timestamp;
|
||
public CombatLogType type;
|
||
public string message;
|
||
public Dictionary<string, object> metadata;
|
||
|
||
public CombatLogEntry(int round, int actionIndex, CombatLogType type, string message)
|
||
{
|
||
this.round = round;
|
||
this.actionIndex = actionIndex;
|
||
this.timestamp = Time.time;
|
||
this.type = type;
|
||
this.message = message;
|
||
this.metadata = new Dictionary<string, object>();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 战斗日志系统:记录战斗中的关键事件,支持查询与格式化输出。
|
||
/// 挂载在战斗场景的 CombatMainManager 同级或子对象上。
|
||
/// </summary>
|
||
public class CombatLogs : Singleton<CombatLogs>
|
||
{
|
||
private const string LogPrefix = "[CombatLog]";
|
||
|
||
private readonly List<CombatLogEntry> allEntries = new List<CombatLogEntry>();
|
||
private CombatMainManager combatManager;
|
||
|
||
// ── 初始化与事件订阅 ──────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 由 CombatMainManager.StartCombat() 调用,订阅全局及各角色事件。
|
||
/// </summary>
|
||
public void Initialize(CombatMainManager manager)
|
||
{
|
||
allEntries.Clear();
|
||
combatManager = manager;
|
||
|
||
// 订阅全局回合开始事件
|
||
manager.eventCollection.onRoundStart.InsertByPriority(
|
||
"CombatLogs_RoundStart",
|
||
new PrioritizedAction(() => LogRoundStart(manager.currentRound), 0));
|
||
|
||
// 订阅角色攻击完成事件
|
||
foreach (CharacterBase character in manager.characterController.characters)
|
||
{
|
||
SubscribeCharacterEvents(character);
|
||
}
|
||
|
||
// 订阅死亡事件
|
||
manager.eventCollection.onCharacterDeath.InsertByPriority(
|
||
"CombatLogs_Death",
|
||
new PrioritizedAction<CharacterBase>(LogDeath, 0));
|
||
}
|
||
|
||
/// <summary>为单个角色订阅攻击完成事件(可供动态加入战场的角色调用)。</summary>
|
||
public void SubscribeCharacterEvents(CharacterBase character)
|
||
{
|
||
CharacterBase captured = character;
|
||
character.eventSubmodule.onFinishAttack.InsertByPriority(
|
||
"CombatLogs_FinishAttack",
|
||
new PrioritizedAction<CharacterBase, AttackResult>(
|
||
(target, result) => LogAttack(result), 0));
|
||
}
|
||
|
||
// ── 日志写入 API ──────────────────────────────────────────────────
|
||
|
||
/// <summary>记录攻击结果。</summary>
|
||
public void LogAttack(AttackResult result)
|
||
{
|
||
string msg = result.isDodged
|
||
? $"{result.attacker.data.displayName} 攻击 {result.target.data.displayName}(闪避)"
|
||
: $"{result.attacker.data.displayName} 攻击 {result.target.data.displayName}:" +
|
||
$"起始伤害={result.startDamage}, 格挡={result.blockedDamage}, " +
|
||
$"护盾={result.shieldedDamage}, 实际={result.hurtDamage}";
|
||
|
||
var entry = new CombatLogEntry(
|
||
combatManager.currentRound, combatManager.currentActionIndex,
|
||
CombatLogType.Attack, msg);
|
||
entry.metadata["attacker"] = result.attacker.data.displayName;
|
||
entry.metadata["target"] = result.target.data.displayName;
|
||
entry.metadata["hurtDamage"] = result.hurtDamage;
|
||
entry.metadata["isDodged"] = result.isDodged;
|
||
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
/// <summary>记录 Buff 施加事件。</summary>
|
||
public void LogBuffApply(CharacterCombatBuffBase buff, CharacterBase target)
|
||
{
|
||
string msg = $"Buff 施加:{buff.GetType().Name} → {target.data.displayName}";
|
||
var entry = new CombatLogEntry(
|
||
combatManager.currentRound, combatManager.currentActionIndex,
|
||
CombatLogType.BuffApply, msg);
|
||
entry.metadata["buffType"] = buff.GetType().Name;
|
||
entry.metadata["target"] = target.data.displayName;
|
||
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
/// <summary>记录 Buff 移除事件。</summary>
|
||
public void LogBuffRemove(CharacterCombatBuffBase buff)
|
||
{
|
||
string msg = $"Buff 移除:{buff.GetType().Name} ← {buff.attachedCharacter?.data.displayName}";
|
||
var entry = new CombatLogEntry(
|
||
combatManager.currentRound, combatManager.currentActionIndex,
|
||
CombatLogType.BuffRemove, msg);
|
||
entry.metadata["buffType"] = buff.GetType().Name;
|
||
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
/// <summary>记录卡牌使用事件。</summary>
|
||
public void LogCardPlay(CardInstance card, List<CharacterBase> targets)
|
||
{
|
||
StringBuilder targetNames = new StringBuilder();
|
||
foreach (CharacterBase t in targets)
|
||
{
|
||
if (targetNames.Length > 0) targetNames.Append(", ");
|
||
targetNames.Append(t.data.displayName);
|
||
}
|
||
string msg = $"卡牌使用:{card.cardData.displayName} → [{targetNames}]";
|
||
var entry = new CombatLogEntry(
|
||
combatManager.currentRound, combatManager.currentActionIndex,
|
||
CombatLogType.CardPlay, msg);
|
||
entry.metadata["cardName"] = card.cardData.displayName;
|
||
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
/// <summary>记录角色死亡事件。</summary>
|
||
public void LogDeath(CharacterBase character)
|
||
{
|
||
string msg = $"{character.data.displayName} 死亡(回合 {combatManager.currentRound})";
|
||
var entry = new CombatLogEntry(
|
||
combatManager.currentRound, combatManager.currentActionIndex,
|
||
CombatLogType.Death, msg);
|
||
entry.metadata["character"] = character.data.displayName;
|
||
entry.metadata["fraction"] = character.fraction.ToString();
|
||
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
/// <summary>记录回合开始事件。</summary>
|
||
public void LogRoundStart(int round)
|
||
{
|
||
string msg = $"── 第 {round} 回合开始 ──";
|
||
var entry = new CombatLogEntry(round, 0, CombatLogType.RoundStart, msg);
|
||
WriteEntry(entry);
|
||
}
|
||
|
||
// ── 查询 API ──────────────────────────────────────────────────────
|
||
|
||
/// <summary>按类型和数量查询日志条目。</summary>
|
||
public List<CombatLogEntry> Query(CombatLogType type, int lastN = 10)
|
||
{
|
||
var result = new List<CombatLogEntry>();
|
||
for (int i = allEntries.Count - 1; i >= 0 && result.Count < lastN; i--)
|
||
{
|
||
if (allEntries[i].type == type)
|
||
result.Add(allEntries[i]);
|
||
}
|
||
result.Reverse();
|
||
return result;
|
||
}
|
||
|
||
/// <summary>获取所有日志条目。</summary>
|
||
public IReadOnlyList<CombatLogEntry> GetAllEntries() => allEntries;
|
||
|
||
// ── 格式化 ────────────────────────────────────────────────────────
|
||
|
||
/// <summary>将条目格式化为人类可读字符串。</summary>
|
||
public string FormatEntry(CombatLogEntry entry)
|
||
{
|
||
return $"[R{entry.round:D2}|A{entry.actionIndex:D2}|{entry.timestamp:F2}s] {entry.type}: {entry.message}";
|
||
}
|
||
|
||
// ── 内部写入 ──────────────────────────────────────────────────────
|
||
|
||
private void WriteEntry(CombatLogEntry entry)
|
||
{
|
||
allEntries.Add(entry);
|
||
Debug.Log($"{LogPrefix} {FormatEntry(entry)}");
|
||
}
|
||
}
|
||
}
|