Units

Units live in the simulation. A script holds a game.Unit, which is a small handle (really a number), and uses it to give orders or to ask questions.

Spawn

footman := game.SpawnUnit("Footman", -256, 0, 1)

The arguments are the name of a definition from your Objects module, a position, and the owner. Positions are in world units, 128 to a tile, counted from the middle of the map: 0, 0 is the centre, negative numbers go left and down. These are the numbers of the Objects module's position fields. Positions you read from a unit and targets of Move use the same units and origin. The game looks the name up without caring about upper and lower case. If there is no such definition, the log says SpawnUnit: there is no definition named "..." and you get back a unit that is not alive. Orders to it do nothing.

A definition name can change when you rename it in the editor. If you want a script that survives renames, use the definition's id, the uuid you can find in definitions.toml:

footman := game.SpawnUnitByID("0a1b2c3d-...", -256, 0, 1)

SpawnUnitByID works like SpawnUnit in every other way.

Owners are explained in Players and owners.

Orders

footman.Move(512, 0)  // walk to a point, in world units
footman.Attack(enemy) // fight another unit until one of them is dead
footman.Kill()        // dies where it stands
footman.SetPosition(0, 0) // jumps to the middle of the map at once, no walking

For movement the game does by itself, glide:

footman.Glide(25, 0) // drifts 25 world units a second to the right, until you change it
footman.Glide(0, 0)  // stops

Glide takes a velocity in world units a second. You call it once and the game keeps the unit moving, so it costs the script nothing after that, however many units glide. It ignores paths and obstacles but stays on the map. It is not an order but a drift added to whatever the unit does, so Move, Attack and the rest do not stop it; only Glide(0, 0) does (or the unit's death). It does not change the unit's animation or the way it faces.

SetPosition takes the same world units from the map's middle as SpawnUnit and Move. The unit goes on with whatever it was doing (a walk is planned again from where it lands), and a point outside the map is moved to the nearest edge. Buildings and dead units are not moved.

You can order a unit in the same function that spawned it:

a := game.SpawnUnit("Footman", -256, 0, 1)
b := game.SpawnUnit("Footman", 256, 0, 2)
b.Attack(a)

Orders are not carried out the moment you call them. The game collects what the script asked for and applies it, in order, when your function returns. The same goes for spawning. This matters for one thing: inside the function that spawned a unit, the unit does not exist yet, so Alive() is false and X() is 0. Ask in a later call, a timer for example.

Questions

u.ID()     // the unit's number, stable for the whole game
u.X()      // position in world units from the map's middle
u.Y()
u.Owner()  // a player.ID: player.Red and so on, player.Neutral or player.NeutralAggressive
u.Alive()  // exists and has health left
u.Health() // health left

The answers come from a copy of the game state taken just before your function runs. They do not change while the function runs, even after you give orders.

A unit that died or was removed is still a valid handle. It compares equal to itself, Alive() says false, and orders to it do nothing. You can keep units in variables and slices without worrying that they go stale.

All units

game.Units() returns every unit on the map, dead ones included until the game removes them. Filter it yourself:

func countAlive(p player.ID) int {
	n := 0
	for _, u := range game.Units() {
		if u.Owner() == p && u.Alive() {
			n++
		}
	}
	return n
}

Units spawned during the current function are not in the list yet. The list is rebuilt on every call, so on a map with thousands of units do not run it every step. A timer every second or two is plenty.

By number

game.UnitByID(n) turns a number back into a handle, for instance one you stored. The unit does not have to exist; Alive() tells you. The numbers start at 0.

Time and the log

game.Time() is the game time in seconds. game.Print and game.Printf write to the log window, F12 in the game. See Build, play and errors.

Not there yet

A script cannot yet change a unit's health, owner or position directly, read its definition or its orders, or find units near a point. For a search by area, loop over game.Units() and compare positions. See What is next.