You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Keep packages focused and below ~500 lines; split by feature, responsibility, or data type.
10
10
- Make changes in the source package, not generated output. Do not edit `_build/` or `_build/dependencies/` as source-of-truth.
11
-
-Prefer Wurst standard-library wrappers and project helpers over raw `common.j`/Jass-style calls.
11
+
-Use Wurst stdlib/library APIs and project helpers; never call a raw `common.j`/Jass native when a wrapper exists, and do not reinvent what stdlib already provides. See **Stdlib-First** below.
12
12
- When unsure about Wurst syntax or local APIs, inspect nearby working code before guessing.
13
13
- Keep tests narrow. Add/update tests for behavior, parsing, compiletime generation, or shared utilities.
14
14
- Avoid broad refactors unless they directly reduce risk or complexity for the requested change.
15
15
16
+
## Stdlib-First: No Raw JASS Natives (Mandatory)
17
+
18
+
The most important coding rule: high-level Wurst packages must use the WurstScript stdlib and library APIs, never ported JASS. The goal is clean, reusable Wurst — not a JASS transliteration.
19
+
20
+
- Never call a raw `common.j` / `Blizzard.j` native when a Wurst wrapper or extension function exists. There is one for almost every native (on `unit`, `player`, `group`, `string`, `rect`, ...). Grep the stdlib (`_build/dependencies/wurstStdlib2/wurst/`) before writing a native call.
21
+
- The only bar for a raw native is that you searched and confirmed no wrapper exists — then add a one-line comment saying so.
22
+
- "It compiles" is not enough. Code that reads like JASS (manual handle juggling, native calls, global trigger callbacks, op-limit chunking) is wrong here; rewrite it idiomatically.
23
+
24
+
Use the stdlib API, not a raw native, for at least:
25
+
26
+
- Timers → `ClosureTimers` (`doAfter`, `doPeriodically`); never `CreateTimer`/`TimerStart`/`PauseTimer`/`DestroyTimer`.
27
+
- Printing → `print` / `printTimed` / `p.print`; never `DisplayText*ToPlayer`/`...ToForce`.
28
+
- Player state → `Player` extensions (`p.addGold`, `p.getGold`, `p.getId`, ...); prefer the `players[i]` array over `Player(i)`.
29
+
- Unit inspection → `Unit` extensions (`u.getTypeId()`, `u.getOwner()`, `u.getAbilityLevel(id)`, ...).
- Group iteration → `ClosureForGroups` (`forUnitsInRange`, `forUnitsInRect`) + `GroupUtils` (`getGroup()` / `group.release()`), not `GroupEnum*` + `ForGroup` globals.
32
+
33
+
The `CreateTrigger()..register...()..addAction() ->` cascade is the accepted idiom and is fine.
34
+
35
+
### Do Not Reinvent Stdlib Infrastructure (Mandatory)
36
+
37
+
Keep custom engine-level infrastructure to a minimum. Stdlib packages are battle-tested and handle the WC3 edge cases (recycling, op-limits, cleanup, desync) that hand-rolled versions get wrong. Grep for an existing system before building one; do not ship a parallel implementation of something stdlib provides:
If stdlib almost fits, prefer a thin wrapper around the stdlib type over a from-scratch system and note why in a comment. Reinventing this is treated as a defect even if it compiles and passes tests, because it reintroduces solved bugs.
46
+
16
47
## Agent Workflow
17
48
18
49
Install dependencies:
@@ -43,6 +74,8 @@ For build changes:
43
74
grill build ExampleMap.w3x --quiet
44
75
```
45
76
77
+
Builds default to production mode, so compiletime `isProductionBuild()` returns `true`. Use `grill build ExampleMap.w3x --dev --quiet` only when validating run/development-mode behavior where `isProductionBuild()` must be `false`; `typecheck` and `test` do not need this flag.
78
+
46
79
Done means relevant errors/warnings are fixed or explicitly explained.
These are recurring real-world Wurst/Warcraft III failure modes. Treat this section as a pre-edit checklist for any non-trivial Wurst change.
144
177
178
+
### Integer overflow
179
+
180
+
WC3 `int` is 32-bit signed and wraps silently at ~2.1 billion (`2^31 - 1`) — no exception, just a negative/garbage value that poisons every downstream comparison and division. Easy to hit when multiplying or summing large game quantities (gold/worth, army totals, damage products, accumulated stats).
181
+
182
+
- Promote to `real` BEFORE multiplying two large quantities: `a.toReal() * b`, never `(a * b).toReal()` (the latter already overflowed).
183
+
- Same for running sums of products: `total += worth.toReal() * count * mult`.
184
+
- Watch `worth * worth`, `count * worth`, `total * total` in scoring/stats; aggregate worths routinely exceed ~46k (the square root of int-max), so their product overflows in ordinary large games.
185
+
- Wurst `/` is real division even for two ints (use `div` for integer division), so division itself does not overflow — but its operands still can. Prefer `real` for any accumulator that fans in many large terms.
186
+
145
187
### Closure capture is by value
146
188
147
189
Wurst closures capture locals by value. If a closure assigns to a local from an outer scope, the outer local is not updated.
0 commit comments