Your first script
A map's rules are a Go program. It is standard Go, written in your own code editor. The W4 editor builds it to WebAssembly and the game runs it in a sandbox beside the simulation. Go is small, and a few lines already do a lot.
1. Install Go
Open the Triggers module (Ctrl 3). The Script compiler card at the top shows Go (recommended) and TinyGo. If the one you want says Not installed yet, press Install: it downloads and sets itself up, with a progress bar.
The second line of the card is gopls, Go's language server, which gives your code editor completion and errors as you type. Install gopls runs go install golang.org/x/tools/gopls@latest in the background; the card updates when it is done.
2. Create the script
New script creates scripts/main.go in the map from a starter:
package main
import (
"fmt"
"w4/event"
"w4/game"
"w4/player"
)
func main() {
event.Register(event.MapInit, func() {
game.Print("map started")
fmt.Println("stdout works too")
footman := game.SpawnUnit("Footman", -256, 0, player.Red)
footman.Move(256, 0)
})
event.OnUnitDied(func(u game.Unit) {
game.Printf("unit %d of %v died at %.0f, %.0f", u.ID(), u.Owner(), u.X(), u.Y())
})
event.Every(10, func() {
game.Printf("%.0f seconds in, %d units", game.Time(), len(game.Units()))
})
}The game runs main once before the map starts. In it you say what should happen when: event.Register for the map's first step, event.OnUnitDied for every death, event.Every for a timer. game.SpawnUnit takes the name of a definition from your Objects module, a position in world units from the middle of the map (the numbers of the Objects module's position fields) and the player who gets the unit (player.Red is the first one). The starter needs a definition called Footman. If you named yours differently, change the name, or the log will tell you it found no such definition.
3. Open it in your editor
Open in editor opens the map's scripts/ folder as a project together with the file. The editor uses the command from Settings, Code editor ({dir} and {file} are placeholders), or finds Zed, VS Code or Sublime Text on your PATH. Because the folder is opened as the project, gopls finds the w4 packages at once and completes game. as you type.
Change the printed text, or spawn a second unit for the blue player and make it attack the first:
a := game.SpawnUnit("Footman", -256, 0, player.Red)
b := game.SpawnUnit("Footman", 256, 0, player.Blue)
b.Attack(a)4. Build and play
Build compiles the scripts to scripts/map.wasm. An error goes to a toast and the Activity panel with the file and line; the state under the file list says not built, changed since the build or up to date. Or just press Play: it builds what changed first and refuses to start on a build error.
In the game press F12: the log window shows map started, the fmt.Println line, and a line every ten seconds. Kill a unit with the /kill cheat (select it, Enter, /kill) and the death is reported.
What just happened
- The Go module
w4ships with the editor and is written once per computer to%LOCALAPPDATA%\W4\go-sdk\<version>\w4. Your map'sscripts/go.modrequires it and a machine-localgo.workpoints at it; nothing of the API is copied into the map. - The build is standard:
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared. You can run it yourself inscripts/; the editor notices the newmap.wasm. - The script runs inside the simulation, at fixed steps, with a limit on how much it may compute per call and on its memory. It cannot read the disk or the network. The same script gives the same game on every computer, which multiplayer needs. The multiplayer rules page lists what to avoid.
- If something goes wrong, Build, play and errors says where each kind of mistake shows up.
Next: export the map, or go to the scripting guide and the API reference.