Further Tests & Progress
This commit is contained in:
@@ -35,7 +35,7 @@ namespace ProjectM.Client
|
||||
[UpdateInGroup(typeof(PresentationSystemGroup))]
|
||||
public partial class CombatFeedbackSystem : SystemBase
|
||||
{
|
||||
struct FxCache { public float Hp; public float3 Pos; public bool IsEnemy; }
|
||||
struct FxCache { public float Hp; public float3 Pos; public bool IsEnemy; public uint Windup; }
|
||||
|
||||
readonly Dictionary<Entity, FxCache> _cache = new();
|
||||
readonly HashSet<Entity> _seen = new();
|
||||
@@ -55,6 +55,7 @@ namespace ProjectM.Client
|
||||
AudioClip _hitClip;
|
||||
AudioClip _deathClip;
|
||||
AudioClip _fireClip;
|
||||
AudioClip _telegraphClip;
|
||||
|
||||
Entity _localPlayer = Entity.Null;
|
||||
uint _lastLocalFireTick;
|
||||
@@ -70,6 +71,7 @@ namespace ProjectM.Client
|
||||
_hitClip = MakeClip("husk_hit", 640f, 180f, 0.10f, 0.5f, noise: true);
|
||||
_deathClip = MakeClip("husk_death", 320f, 50f, 0.34f, 0.55f, noise: false);
|
||||
_fireClip = MakeClip("fire", 880f, 1500f, 0.07f, 0.30f, noise: false);
|
||||
_telegraphClip = MakeClip("telegraph", 680f, 1020f, 0.12f, 0.35f, noise: false);
|
||||
}
|
||||
|
||||
protected override void OnStartRunning()
|
||||
@@ -101,6 +103,7 @@ namespace ProjectM.Client
|
||||
// Make sure predicted/physics jobs writing these are done before this main-thread read.
|
||||
EntityManager.CompleteDependencyBeforeRO<Health>();
|
||||
EntityManager.CompleteDependencyBeforeRO<LocalTransform>();
|
||||
EntityManager.CompleteDependencyBeforeRO<AttackWindup>();
|
||||
|
||||
// Resolve the local player (for hit colouring + fire feedback).
|
||||
_localPlayer = Entity.Null;
|
||||
@@ -121,28 +124,45 @@ namespace ProjectM.Client
|
||||
float cur = health.ValueRO.Current;
|
||||
float3 p = xf.ValueRO.Position;
|
||||
bool isEnemy = SystemAPI.HasComponent<EnemyTag>(entity);
|
||||
uint windup = isEnemy && SystemAPI.HasComponent<AttackWindup>(entity) ? SystemAPI.GetComponent<AttackWindup>(entity).WindUpUntilTick : 0u;
|
||||
bool isLocalPlayer = entity == _localPlayer;
|
||||
|
||||
if (_cache.TryGetValue(entity, out var prev))
|
||||
{
|
||||
if (isEnemy && windup != 0 && prev.Windup == 0)
|
||||
{
|
||||
// Attack telegraph: the wind-up just began -> warn the player ~0.3s before the strike lands.
|
||||
Burst(_hitFx, null, (Vector3)p + Vector3.up * 1.2f, 6);
|
||||
PlayClip(_telegraphClip, (Vector3)p, 0.5f);
|
||||
}
|
||||
|
||||
if (cur < prev.Hp - 0.001f)
|
||||
{
|
||||
SpawnNumber(prev.Hp - cur, (Vector3)p, isLocalPlayer, cam);
|
||||
Burst(_hitFx, cfg != null ? cfg.Hit : null, (Vector3)p + Vector3.up * 0.8f, 10);
|
||||
PlayClip(_hitClip, (Vector3)p, 0.7f);
|
||||
PrototypeCameraRig.AddShake(isLocalPlayer ? 0.32f : 0.10f);
|
||||
Burst(_hitFx, cfg != null ? cfg.Hit : null, (Vector3)p + Vector3.up * 0.8f, FeelConfig.HitBurstCount);
|
||||
PlayClip(_hitClip, (Vector3)p, FeelConfig.HitSfxVolume);
|
||||
PrototypeCameraRig.AddShake(isLocalPlayer ? FeelConfig.HitShakeLocal : FeelConfig.HitShakeRemote);
|
||||
if (isLocalPlayer) PrototypeCameraRig.PunchFov(FeelConfig.HitStopFovKick, FeelConfig.HitStopDurationMs);
|
||||
}
|
||||
|
||||
// Respawn recovery: the LOCAL player's Health rising from <=0 back to positive. No healing
|
||||
// mechanic exists, so a 0 -> positive edge is unambiguously a respawn (observer-only).
|
||||
if (isLocalPlayer && FeelConfig.RespawnShimmerEnabled && cur > prev.Hp + 0.001f && prev.Hp <= 0f)
|
||||
{
|
||||
Burst(_muzzleFx, null, (Vector3)p + Vector3.up * 0.6f, FeelConfig.RespawnShimmerBurst);
|
||||
PrototypeCameraRig.AddShake(FeelConfig.RespawnShimmerShake);
|
||||
}
|
||||
|
||||
// Player death (players don't despawn — they respawn; Husk death is handled on prune).
|
||||
if (!isEnemy && cur <= 0f && prev.Hp > 0f)
|
||||
{
|
||||
Burst(_deathFx, PlayerDeathPrefab(cfg), (Vector3)p + Vector3.up * 0.5f, 28);
|
||||
Burst(_deathFx, PlayerDeathPrefab(cfg), (Vector3)p + Vector3.up * 0.5f, FeelConfig.DeathBurstCount);
|
||||
PlayClip(_deathClip, (Vector3)p, 0.7f);
|
||||
PrototypeCameraRig.AddShake(isLocalPlayer ? 0.5f : 0.25f);
|
||||
PrototypeCameraRig.AddShake(isLocalPlayer ? FeelConfig.PlayerDeathShake : FeelConfig.RemotePlayerDeathShake);
|
||||
}
|
||||
}
|
||||
|
||||
_cache[entity] = new FxCache { Hp = cur, Pos = p, IsEnemy = isEnemy };
|
||||
_cache[entity] = new FxCache { Hp = cur, Pos = p, IsEnemy = isEnemy, Windup = windup };
|
||||
}
|
||||
|
||||
// Prune despawned ghosts. A Husk that vanished was killed -> death VFX at its last position.
|
||||
@@ -157,9 +177,10 @@ namespace ProjectM.Client
|
||||
var c = _cache[_stale[i]];
|
||||
if (c.IsEnemy)
|
||||
{
|
||||
Burst(_deathFx, cfg != null ? cfg.EnemyDeath : null, (Vector3)c.Pos + Vector3.up * 0.5f, 28);
|
||||
PlayClip(_deathClip, (Vector3)c.Pos, 0.65f);
|
||||
PrototypeCameraRig.AddShake(0.16f);
|
||||
Burst(_deathFx, cfg != null ? cfg.EnemyDeath : null, (Vector3)c.Pos + Vector3.up * 0.5f, Mathf.Max(1, Mathf.RoundToInt(FeelConfig.DeathBurstCount * FeelConfig.KillBurstScale)));
|
||||
PlayClip(_deathClip, (Vector3)c.Pos, FeelConfig.KillSfxVolume);
|
||||
PrototypeCameraRig.AddShake(FeelConfig.KillShake);
|
||||
PrototypeCameraRig.PunchFov(FeelConfig.KillFovKick, FeelConfig.HitStopDurationMs);
|
||||
}
|
||||
_cache.Remove(_stale[i]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user