Every project defines attacking and damage differently. The Tactical Pack isolates game-specific combat behind IAttackAgent and IDamageable.

IAttackAgent

Implement IAttackAgent on every tactical agent that can attack. Its distances and angle tell the task when engagement is valid; RotateTowards and Attack perform the project response.

public interface IAttackAgent
{
    float MinAttackDistance { get; }
    float MaxAttackDistance { get; }
    float AttackAngleThreshold { get; }

    void RotateTowards(Vector3 direction);
    void Attack(Transform targetTransform, IDamageable targetDamageable);
}

Keep distance properties consistent with the current weapon. RotateTowards should use the project’s normal aiming rules. Attack should request an attack rather than bypassing cooldown, ammunition, animation, authority, or equipment locks.

If equipment changes at runtime, the properties should reflect the active configuration.

IDamageable

Implement IDamageable on every valid target. IsAlive prevents continued engagement with a defeated target, and Damage applies the amount chosen by the attack implementation.

public interface IDamageable
{
    bool IsAlive { get; }
    void Damage(float amount);
}

IsAlive should become false as soon as the target is no longer tactically valid, even when death presentation continues. Damage should enter the same authoritative pipeline used elsewhere so armor, teams, networking, and callbacks remain consistent.

Separate movement and combat failures

Reaching a tactical slot proves only coordination and navigation. If the group arrives but does not attack, inspect:

  1. The attacker has an IAttackAgent implementation.
  2. The target has an IDamageable implementation and reports IsAlive.
  3. Distance and angle match current weapon values.
  4. RotateTowards aligns through the expected aiming system.
  5. Attack is not being rejected by cooldown, ammunition, authority, or animation state.

Use Integrations and Troubleshooting when an imported bridge supplies these interfaces.