TH1/Unity/Assets/Scripts/TH1_Logic/Action/ActionVisualEventCollector.cs
2026-06-12 16:02:05 +08:00

207 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using Logic;
using RuntimeData;
using TH1_Anim.Fragments;
using TH1_Logic.Core;
using TH1_Renderer;
using TH1Renderer;
namespace Logic.Action
{
public class ActionVisualEventCollector : IDisposable
{
private readonly List<FragmentStep> _steps = new List<FragmentStep>();
private readonly Dictionary<uint, uint> _deathReplacementUnitIds = new Dictionary<uint, uint>();
private readonly MapData _mapData;
private readonly uint _mainTargetUnitId;
private readonly ActionVisualEventCollector _previous;
private uint _settlingVisualTargetId;
private bool _flushed;
public static ActionVisualEventCollector Current { get; private set; }
public bool MainTargetKilledBeforeAttack { get; private set; }
public int MainTargetPreAttackDamage { get; private set; }
private ActionVisualEventCollector(MapData mapData, uint originUnitId, uint mainTargetUnitId)
{
_mapData = mapData;
_mainTargetUnitId = mainTargetUnitId;
_previous = Current;
Current = this;
}
public static ActionVisualEventCollector Begin(MapData mapData, uint originUnitId, uint mainTargetUnitId)
{
if (mapData != Main.MapData) return null;
return new ActionVisualEventCollector(mapData, originUnitId, mainTargetUnitId);
}
public bool TryRegisterDeathReplacement(uint targetUnitId, uint replacementUnitId)
{
if (targetUnitId == 0 || replacementUnitId == 0) return false;
if (_settlingVisualTargetId != targetUnitId) return false;
_deathReplacementUnitIds[targetUnitId] = replacementUnitId;
return true;
}
public SettlementInfo SettleDamageWithVisual(
UnitData origin,
UnitData target,
int damage,
DamageType damageType,
int phase,
float delay = 0f,
bool markMainTargetPreAttackKill = false,
bool includeDamageVfx = true,
bool showoff = false)
{
if (_mapData == null || origin == null || target == null)
return Main.UnitLogic.DamageSettlement(_mapData, origin, target, damage, damageType);
var targetGrid = target.Grid(_mapData);
var targetCity = target.City(_mapData);
var targetRenderer = target.Renderer(_mapData);
var canBeKilled = target.CanBeKilled(_mapData);
var targetId = target.Id;
SettlementInfo settlement;
var previousSettlingTargetId = _settlingVisualTargetId;
_settlingVisualTargetId = targetId;
try
{
settlement = Main.UnitLogic.DamageSettlement(_mapData, origin, target, damage, damageType);
}
finally
{
_settlingVisualTargetId = previousSettlingTargetId;
}
if (settlement == null) return null;
bool killed = settlement.IsKill || !target.IsAlive();
if (markMainTargetPreAttackKill && killed && targetId == _mainTargetUnitId)
{
MainTargetKilledBeforeAttack = true;
MainTargetPreAttackDamage += damage;
}
if (targetGrid != null)
{
AddDamageStep(
targetGrid,
targetCity,
targetRenderer,
targetId,
damage,
killed,
canBeKilled,
phase,
delay,
includeDamageVfx,
showoff);
}
return settlement;
}
private void AddDamageStep(
GridData targetGrid,
CityData targetCity,
UnitRenderer targetRenderer,
uint targetId,
int damage,
bool killed,
bool canBeKilled,
int phase,
float delay,
bool includeDamageVfx,
bool showoff)
{
_steps.Add(new FragmentStep
{
Phase = phase,
Duration = 0f,
Execute = () =>
{
void RunVisual()
{
if (targetGrid == null) return;
var gridRenderer = targetGrid.Renderer(_mapData);
bool inMainSight = targetGrid.InMainSight();
if (inMainSight)
{
gridRenderer?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
if (includeDamageVfx)
gridRenderer?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage, damage));
}
if (killed)
{
if (inMainSight)
{
gridRenderer?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
if (canBeKilled)
gridRenderer?.PlayVFXInSight(new GridVFXParams(GridVFXType.Die));
}
targetRenderer?.Die();
ShowDeathReplacement(targetId);
targetCity?.SetCityRenderer(_mapData);
MapRenderer.Instance?.UpdateAroundHighlight(_mapData, targetGrid);
}
else
{
targetRenderer?.InstantUpdateUnit(showoff);
}
gridRenderer?.InstantUpdateGrid();
}
if (delay > 0f && Timer.Instance != null)
Timer.Instance.TimerRegister(this, RunVisual, delay, "ActionVisualEventCollector Damage Visual");
else
RunVisual();
}
});
}
private void ShowDeathReplacement(uint targetId)
{
if (!_deathReplacementUnitIds.Remove(targetId, out var replacementUnitId)) return;
if (_mapData?.UnitMap == null ||
!_mapData.UnitMap.GetUnitDataByUnitId(replacementUnitId, out var replacementUnit) ||
replacementUnit == null)
return;
var replacementRenderer = replacementUnit.Renderer(_mapData);
if (replacementRenderer == null)
{
MapRenderer.Instance?.RenderUpdateUnitMap();
replacementRenderer = replacementUnit.Renderer(_mapData);
}
replacementRenderer?.InstantUpdateUnit(true);
}
public void FlushTo(FragmentBase fragment)
{
if (fragment == null) return;
foreach (var step in _steps)
fragment.InjectStep(step);
_flushed = true;
_steps.Clear();
}
public void Dispose()
{
if (!_flushed)
{
foreach (var targetId in new List<uint>(_deathReplacementUnitIds.Keys))
ShowDeathReplacement(targetId);
_deathReplacementUnitIds.Clear();
}
_steps.Clear();
Current = _previous;
}
}
}