Docs Gameplay extensions

JASS channel

The 1291 JASS functions available to map makers can now be called by name from outside the game: create units, change properties, effects, boards, dialogs, sound, camera, fog… Four ways to use it: the Farsight console, the command line, HTTP and Python.

The 1291 JASS natives that map makers can use in map scripts can now all be called by name from outside the game: create units, change properties, draw effects, pop up boards and dialogs, play sounds, move the camera, change the fog… Use them to customize the game further — RPG helpers, an AI companion, homemade mini-games, debugging tools.

UsageGood forWhere
Farsight “JASS console” pageTrying things by hand, tweaking as you watchSidebar “System → JASS console”: write a script and click Run; the right side lists functions by category, click one to insert it into the script
Command lineTrying things by hand, or saving a script file to run again and againpython -m openwar3 jass --inst 20 (interactive), -e "code", my_script.j, --list keyword
HTTPExternal programs in any languagePOST /api/instances/{n}/jass and others (see below); the Farsight backend listens only on the local machine
PythonWriting schemes, companions and toolsg.jass.AnyFunction(...); common visuals and interactions are wrapped in openwar3.visual

Three boundaries, all dictated by how it works:

  • Only single-player games (against the computer on your machine) can change the world. Creating objects or changing units unilaterally on your machine would desync the other players in a multiplayer game — so in multiplayer games only read-only functions (Get*, Is*, Count*…) are allowed.
  • It’s only for your machine’s own tools; calls made while connected as a player (Game(player=N)) or in fair mode are rejected.
  • Only for single-player and self-hosted LAN games.

To add things to the screen in a multiplayer game, use the canvas: the runtime draws it itself, and it doesn’t change game state.

Script syntax

The console, the command line and HTTP all use the same scripts. One statement per line; you can paste JASS directly (call / set / local, true / false / null, 'Hpal' four-character codes, // comments), or write it Python-style:

set h = hero()                                   // built-in: our main hero
local texttag t = CreateTextTag()
call SetTextTagText(t, "|cffffcc00+128 Critical!|r", 0.024)
call SetTextTagPosUnit(t, h, 60)
call SetTextTagVelocity(t, 0, 0.03)
call SetTextTagPermanent(t, false)
call SetTextTagLifespan(t, 4)
call SetTextTagVisibility(t, true)
call PingMinimapEx(h.x, h.y + 300, 3, 255, 0, 0, false)
set u = CreateUnit(Player(0), 'hfoo', h.x + 200, h.y, 270)
print("created", u, "hero level", GetHeroLevel(h))
  • Variables persist: within the same instance and the same game, variables you set in one snippet can be used in the next; they’re cleared automatically when a new game starts, and you can also clear them manually.
  • Built-in functions: hero() our main hero, me() the local player, unit('hfoo') finds a unit, unit_at(x, y), wait(seconds), print(...). Units expose .x, .y, .hp, .hp_max, .mana, .type, .owner and .level, and arithmetic and comparisons are supported.
  • Not supported: if, loop, function — for logic, use Python’s g.jass (it’s just ordinary function calls), or write a scheme.
  • Errors tell you which line failed and why (no such function, wrong number of arguments, undefined variable…); statements before the error have already taken effect.

Arguments and return values:

In the signatureWhat to passNotes
integerA number; 'Hpal' four-character codes are converted automatically
realA numberThe runtime converts it to the format the engine expects
booleantrue / false
string"..."Chinese text and the game’s color codes are supported; strings the game keeps (floating text, boards, buttons, chat commands) are copied on the spot, so it’s safe
handleA handle held in a variable, or a unit (things like hero() are converted to handles automatically)
function (code)Only nullA JASS function can’t be passed in from outside; something like TimerStart(t, 60, false, null) works
string return value—The engine returns a string table index, so the text can’t be read back. For unit names, use g.map_data.name_of

Categories

Functions are grouped into categories by name; the console’s right panel and --list both use these:

CategoryCountExamples
Visual effects80Floating text, lightning links, special effects, ground images, ground splats, unit tint / scale / animations
Interface and boards146Multiboards, leaderboards, timer windows, dialogs, quests, on-screen text, minimap pings, portrait dialogue, full-screen filters
Camera44Camera fields, panning, camera shake
Sound and music50Creating and playing sounds, playing music
Fog and vision25Visibility modifiers, toggling fog of war
Item / hero / unit63 / 32 / 161Create items, set hero level, change owner, add abilities
Player / alliance / resources71Set alliances, change gold and lumber
Trigger / event / timer62Create triggers, register events, timers
Terrain / weather / destructable45Weather effects, changing terrain, creating destructables
Game flow57Game speed, pause, time of day
Other…Unit groups and regions, storage, computer AI scripts, type conversion and math, event responses…

On 2026-09-24, 94 of them were called one by one in a live game and their effects checked by eye; the rest go through the same path, just without each effect being checked individually.

Event response functions (GetTriggerUnit, GetClickedButton…) only have a value at the moment a trigger runs; called from outside, they return 0 or null. To find out whether something happened, use the event counters described below.

HTTP

Farsight backend (default 127.0.0.1:8866, listens only on the local machine):

GET  /api/jass/natives?q=TextTag&cat=visual
POST /api/instances/20/jass        {"code": "set h = hero()\ncall PingMinimapEx(h.x, h.y, 3, 255, 0, 0, false)"}
     -> {"ok": true, "rows": [...], "printed": [...], "vars": {...}}
     -> on error: {"ok": false, "error": "第 2 行:...", "line": 2}
POST /api/instances/20/jass/call   {"name": "SetUnitScale", "args": [{"unit": 599669636}, 1.4, 1.4, 1.4]}
POST /api/instances/20/jass/reset  clear the remembered variables

For a unit argument, write {"unit": addr}, where addr is the unit’s addr in the snapshot. Measured at 60–90 ms per request.

Python: g.jass and openwar3.visual

j = g.jass
t = j.CreateTextTag()
j.SetTextTagText(t, "Hello", 0.024)    # same argument rules as scripts; unit and item objects from the snapshot can be passed directly
j.signature("CreateImage")             # look up a signature

openwar3.visual.Visual(g) wraps the tested, commonly used visual effects into one-liners (call v.tick() once per tick: it removes expired effects and moves the lines and circles that follow units; v.clear() removes everything):

MethodEffect
float_text(text, unit_or_point, ...)Floating text: damage numbers, overhead hints; Chinese text and colors both work
link(a, b, kind)A line between two units that follows them: magic leash / spirit link / life drain / healing wave
effect(model, unit_or_point, ...)Special effect model: overhead, at the feet, or played once (explosion, pillar of light)
ring(unit_or_point, radius, color)Range circle on the ground: ability range, danger zone, rally point; can follow a unit
ping(point, color)Minimap ping
board(title, rows...)Multi-row board in the top-right corner (with icons); cells can be updated one by one
countdown(title, seconds)Timer window in the top-right corner; the game counts down the seconds itself
scene(name, line, portrait)Portrait dialogue: the portrait at the bottom switches to a talking unit, and a “Name: line” subtitle appears on screen
screen_tint(color, alpha)Full-screen filter (by default a red glow around the edges: a low-health warning)
sound(path) / reveal(point, radius, seconds) / look(unit, ...)Play a sound / clear the fog over an area / tint, scale, animate or flash a unit

Interaction: knowing what the player did without writing JASS functions

Responding to the player in JASS means writing trigger functions, and a function can’t be passed in from outside. The workaround: create an empty trigger with no conditions and no actions, register only the event, and count how many times it has run. Testing confirmed that an empty trigger still counts.

MethodUse
chat_commands(["-follow", "-stay"]) → .poll()Commands the player types in the chat box (exact match, or prefix match)
menu(title, [buttons...]) → .clicked()A button menu in the middle of the screen, and which button was clicked
hotkeys(("left", "right", "up", "down", "esc")) → .poll()How many times the arrow keys and Esc were pressed
on("TriggerRegister...Event", args...) → .poll()How many times any JASS event happened: unit death, entering a region, taking damage, timers…

The limitation is that you only know how many times something happened, not who did it or what they typed. To tell who, create a separate counter for each object. That’s how the AI companion’s chat commands are wired up.

Caveats

  • Clean up what you create: floating text, links, images, boards, triggers… stay around until removed (Visual.clear() removes the ones it created). The game can show at most about 100 floating texts at once.
  • BJ functions aren’t natives: functions like CreateTextTagUnitBJ are built out of natives in the map script and aren’t available here — call the natives the way their implementation does.
  • Some constants need converting first: e.g. ConvertPlayerColor(1), ConvertFogState(4) (see common.j for the values).
  • One call takes about 13 ms (including handle conversion); at the protocol level these are W3P opcodes 70–72; see W3P protocol.