Skip to content

TacticsUnit

namespace Griff.Modules.Tactics;
public sealed class TacticsUnit : Component, IGridDriver, ITurnReady, IPlaytestReport

A unit in a tactics game, with no code: on its team’s turn you pick it, see the cells it can walk to (and the cells it can hit from them), then click one to walk there and click an enemy in reach to attack it. Each turn it moves up to Move cells, around walls and other units, then attacks something MinRange to Range steps away for Damage, or waits. Give it a TurnTaker with 1 action naming its team: attacking or waiting uses the action, so once every unit on a team has had its go, the turn passes. A TacticsControls in the scene does the picking and draws the ranges. On a team the computer plays (the turn order’s Computer Teams), the computer takes each unit’s go in turn, the way its Stance says: the best attack it can make (a foe it can finish off first, then the weakest), or a walk toward the nearest foe.

Its enemies are the units on other teams (with no teams, every other unit) and anything else with a Health on another team (see Teams), or whatever with Health is on its Layers. Ranges are counted in steps and reach over walls. Units block each other, friend or foe. In a network game the host moves the units: a player’s clicks are sent to it.

This is a component: its saved properties are on its component page.

public string? AttackSound { get; set; }

A sound played with each attack, e.g. “Sounds/hit.wav”.

public bool CanAct { get; }

True while it can take its go: it’s its team’s turn (if it takes turns) and it hasn’t used its action.

public float Damage { get; set; }

Hit points each attack takes away.

public string? DamageRoll { get; set; }

Dice rolled for each hit’s damage in place of Damage, e.g. “1d8+2”; empty hits for Damage every time. Table Values sets it from a dice column given for Damage.

public bool HasMoved { get; set; }

True once it has walked this go and has yet to attack or wait. The game clears it as its go ends and as each of its team’s turns starts.

public string? HitEffect { get; set; }

A prefab spawned on what it hits, e.g. “Sparkle”: a burst of particles, a damage number.

public float HitStop { get; set; }

Freezes the game for this many seconds when an attack lands, so it hits hard. 0 for none.

public bool IsDone { get; }

True on its team’s turn once it has had its go: drawn dimmed until its team’s next turn.

public bool IsWalking { get; }

True while it’s walking to where it was sent.

public string? Layers { get; set; }

The collision layers it attacks, e.g. “Enemy”. Empty attacks the units on other teams (see TurnTaker).

public int MinRange { get; set; }

How close something has to be at least for it to hit it: 2 for an archer that can’t hit what’s next to it.

public int Move { get; set; }

How many cells it can walk in a turn, around walls and other units.

public int Range { get; set; }

How many steps away it can hit: 1 is next to it, more for an archer or a mage.

public IReadOnlyList<ValueTuple<int, int>> Reachable { get; }

The cells it can walk to on its go, column then row on its map (row 0 at the top), not counting the one it stands in. The game keeps it up to date while it can act and hasn’t moved; empty otherwise.

public TacticsStance Stance { get; set; }

How it plays when the computer plays its team (see ComputerTeams): Charge goes after the nearest foe, Guard stays put until a foe comes within a walk and a hit, Hold never walks and only attacks from where it stands.

public IReadOnlyList<Entity> Targets { get; }

What it can attack from where it stands now. The game keeps it up to date while it can act.

public void Attack(Entity target)

Attacks target at the start of the next frame, as a click on it does: from where it stands if it’s in reach, else walking first to the nearest cell it can hit it from (if it hasn’t moved yet).

public bool AttacksLayer(string? layer)

True if it attacks things on collision layer layer (always, with no Layers).

public void MoveTo(ValueTuple<int, int> cell)

Walks to cell (one of its Reachable cells) at the start of the next frame, as a click there does. It still has to attack or wait afterwards; with nothing in reach from there, its go ends by itself.

public float RollDamage()

The damage of one hit: DamageRoll rolled, or Damage.

public void UndoMove()

Takes back the walk it took this go, if it hasn’t attacked or waited yet, putting it back where it was.

public void Wait()

Ends its go where it stands, without attacking, at the start of the next frame.

public event Action<TacticsUnit, Entity>? Attacked

Raised as it attacks, with what it attacks.

public event Action<TacticsUnit>? Done

Raised as its go ends, after it attacks or waits.

public event Action<TacticsUnit, ValueTuple<int, int>>? Moved

Raised as it sets off on a walk, with the cell it’s going to.