Painted units correspond to damage types

This commit is contained in:
Ben Calegari 2026-06-09 21:50:03 -07:00
parent 04ead32846
commit 2be2e52fe4
3 changed files with 242 additions and 75 deletions

View file

@ -1,4 +1,5 @@
// Assets/_Project/Scripts/Combat/Projectile.cs
using Unity.Netcode;
using UnityEngine;
using TD.Core;
@ -35,17 +36,32 @@ namespace TD.Combat
private const float HitThresholdSq = 0.09f; // 0.3 world units
private EnemyHealth target;
private float damage;
private DamageType damageType;
private TargetType targetType;
private float splashRadius;
private float slowFactor;
private float dotDamagePerSecond;
private float effectDuration;
private float speed;
private LayerMask enemyLayerMask;
private PlayerSlot sourceOwner;
private bool initialized;
private float damage;
private DamageType damageType;
private TargetType targetType;
private float splashRadius;
private float slowFactor;
private float dotDamagePerSecond;
private float effectDuration;
private float speed;
private LayerMask enemyLayerMask;
private PlayerSlot sourceOwner;
private bool initialized;
// Paint tint. Set pre-spawn (pendingTint), committed to the replicated
// NetworkVariable in OnNetworkSpawn on the server, and applied as a mesh tint
// on every client so a painted tower's projectiles match its color.
private PaintColor pendingTint;
private readonly NetworkVariable<PaintColor> tintColor =
new(
PaintColor.None,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server);
private MaterialPropertyBlock tintBlock;
private static readonly int ColorPropertyId = Shader.PropertyToID("_Color");
private static readonly int BaseColorPropertyId = Shader.PropertyToID("_BaseColor");
private static readonly Collider[] s_overlapBuffer = new Collider[32];
@ -58,35 +74,74 @@ namespace TD.Combat
/// </summary>
public void InitializeServer(
EnemyHealth target,
float damage,
DamageType damageType,
TargetType targetType,
float splashRadius,
float slowFactor,
float dotDamagePerSecond,
float effectDuration,
float speed,
LayerMask enemyLayerMask,
PlayerSlot sourceOwner)
float damage,
DamageType damageType,
TargetType targetType,
float splashRadius,
float slowFactor,
float dotDamagePerSecond,
float effectDuration,
float speed,
LayerMask enemyLayerMask,
PlayerSlot sourceOwner,
PaintColor tint = PaintColor.None)
{
this.target = target;
this.damage = damage;
this.damageType = damageType;
this.targetType = targetType;
this.splashRadius = splashRadius;
this.slowFactor = slowFactor;
this.target = target;
this.damage = damage;
this.damageType = damageType;
this.targetType = targetType;
this.splashRadius = splashRadius;
this.slowFactor = slowFactor;
this.dotDamagePerSecond = dotDamagePerSecond;
this.effectDuration = effectDuration;
this.speed = speed;
this.enemyLayerMask = enemyLayerMask;
this.sourceOwner = sourceOwner;
initialized = true;
this.effectDuration = effectDuration;
this.speed = speed;
this.enemyLayerMask = enemyLayerMask;
this.sourceOwner = sourceOwner;
this.pendingTint = tint;
initialized = true;
if (targetType == TargetType.Chain)
Debug.LogWarning("[Projectile] TargetType.Chain is hitscan-only. " +
"This projectile will hit the primary target only.");
}
// ----- Spawn lifecycle (tint) -------------------------------------
public override void OnNetworkSpawn()
{
// Commit the pre-spawn tint on the server; NGO includes this write in the
// initial sync so every client reads the right value in its OnNetworkSpawn.
if (IsServer)
tintColor.Value = pendingTint;
tintColor.OnValueChanged += HandleTintChanged;
ApplyTint();
}
public override void OnNetworkDespawn()
{
tintColor.OnValueChanged -= HandleTintChanged;
}
private void HandleTintChanged(PaintColor previous, PaintColor current) => ApplyTint();
// Tints all renderers to the paint color via a MaterialPropertyBlock. No-op when
// unpainted so the prefab's authored projectile material shows through.
private void ApplyTint()
{
if (tintColor.Value == PaintColor.None) return;
Color c = PaintColors.Get(tintColor.Value);
c.a = 1f;
tintBlock ??= new MaterialPropertyBlock();
tintBlock.SetColor(ColorPropertyId, c);
tintBlock.SetColor(BaseColorPropertyId, c);
foreach (var rend in GetComponentsInChildren<Renderer>())
rend.SetPropertyBlock(tintBlock);
}
// ----- Server movement + hit detection ----------------------------
private void Update()
@ -138,6 +193,7 @@ namespace TD.Combat
HitEnemy(eh);
}
}
break;
}
}
@ -150,16 +206,16 @@ namespace TD.Combat
{
float magnitude = damageType switch
{
DamageType.Cold => slowFactor,
DamageType.Fire => dotDamagePerSecond,
DamageType.Cold => slowFactor,
DamageType.Fire => dotDamagePerSecond,
DamageType.Poison => dotDamagePerSecond,
_ => 0f,
_ => 0f,
};
if (magnitude > 0f)
eh.GetComponent<EnemyStatus>()
?.ApplyEffect(damageType, magnitude, effectDuration, sourceOwner);
?.ApplyEffect(damageType, magnitude, effectDuration, sourceOwner);
}
}
}
}
}