Modding Wishlist

Requests done by Neiv are in green
Requests done by iquare are in orange
Requests done by FaRTy1billion are in blue
Requests done by poiuy_qwert are in brown

New Commands

set_resarea((PosX1, PosY1), (PosX2, PosY2), (PosX3, PosY3)
This would allow allies_watch to be called basically anywhere, useful for proxying and making the AI less static. Alternatively, updating ‘allies_watch` to accept map coordinates/locations instead of resareas would do just as well. Pos1 would be center of resarea. Pos2 and Pos3 would be the two points between which would be searched for resources.
Sort of achieved via create_script, but undesired behavior still occurs with regards to AI mining from "nearby" resareas. Something to turn off mining entirely on a town level could fix this.

worker_id, gas_id, power_id, and creep_id
worker_id(unitId, flags), gas_id(unitID, flags) | power_id(unitID, flags) | creep_id(unitID, flags)
This would set the units used by the AI to harvest gas and power structures. `supply_from` may be necessary if supply structures are hardcoded, haven’t looked into that. `flags` would be 0 or `clear`, e.g. `gas_from(assimilator, clear)` to stop the AI from building/using default gas structures, or to dynamically clear them in the middle of the script for whatever niche reason.

guard_jump(player, operator, quantity, military, (PosX, PosY), block)
Jumps to `block` if `player` does not command `operator` `quantity` guard positions of `military` at (region/location) `(PosX, PosY)`.

attack_priority(unitID, priority)
Tells the AI to prioritize regions with `unitID` for targets when using `attack_prepare`. `unitID` can also be `clear` to remove all instances. Ignored by `attack_to`.

Tells the AI to use transports for the next attack. Cleared with `attack_clear`.

Identical to `max_workers`, but for transports. Would overwrite the limit of 3 transports (or 5 with `check_transports`). Would also limit zerg AI from using more than the specified value of overlords at any given time.

switch_controller(playerId, controllerType)
If run for a vacant human slot, switches the controller of %1playerId to %2controllerType. Vacant = not present, either from map start or due to leaving mid-game.
Triggers do not run for players that don't exist, so some ugly hacks may be necessary for this.

  • controllerType: computer, rescuable, neutral, unused

town_jump(playerID, operator, townType, location, block)
Searches the specified location or region for a town of the specified type owned by the specified player, and jumps/calls if true.

  • operators: Has, HasNone, Has_Call, HasNone_Call
  • townTypes: main, expansion (if possible to differentiate), areatown, specific ID

preference_jump(player, order, operator, quantity, block)
Compares order usage of player and jumps if true

  • order: combinable with `|`

Kills the specified town; if used on a main town, resets all AI flags and region states

pathing(player, operator, location, block)
Compares or writes pathing data at the specified regions and jumps if true.

  • player: 0-14, with 13 being current player and 14 being all players
  • operator: True, False, True_Jump, False_Jump, True_Call, False_Call
  • location: (PosX, PosY ~ R) or Loc.n, selects all pathfinder regions for reading/writing

When comparing, all regions must be pathable (true) or unpathable (false) to trigger a jump.
When writing, all selected regions will be considered pathable or unpathable for the specified player.

start_loc(player, location)
Sets the selected player's start location (without assigning a resarea) to the selected region.

set_region(player, operator, region, state)
Sets the specified regions to the specified state for the specified player.

  • operator: Set, Remove
  • state: 0-9

wait_for(operator, quantity, unitType, townId, flag)
Waits for the computer to own the specified quantity of units in the town the command is called.

  • flags:
    • build (done building/training)
    • buildstart (building/training in progress)
    • dead
    • request (not yet in progress)
  • townId: set by `set_id`, global if set to 255

play_sound(playerId, soundId, location, volume, flags)
Plays the specified sound for the specified player.

  • volume: 0-100
  • flags:
    • Mute - muted if the mute trigger is active
    • Reduce(volume) - reduced to volume by the mute trigger.

town_add(quantity, unitId, location, sourceTown, destTown)
Adds the specified number of units to the specified town, set by set_id.

sprite_color(spriteId, location, color, flag)
Sets the player color of the specified sprites.

  • color: integer based on tminimap.pcx, e.g. 0 is red, 15 is cyan
  • flags:
    • setPlayer(Id) - affects all sprites owned by the specified player; cancelled with removePlayer(Id).
    • gameView - changes the game view color of the specified sprites.
    • minimap - changes the minimap color of the specified sprites.

button_name(playerId, stattxtId1, stattxtId2)
Sets the specified in-game string to the specified user-defined string.

  • stattxtId1: string ID in stat_txt.tbl to forward to Id2
  • stattxtId2: string ID to use instead of Id1

set_race(playerId, race, block)
Reads or writes race.

  • race: Terran, Zerg, Protoss, Independent, Neutral, Inactive, Human, UserSelect, Random.

player_name(playerId, operator, string, block)
Reads or writes the player names seen in the score screen.

mining_efficiency(playerId, operator, unitType, resourceType, quantity)
Modifies mining efficiency for the specified player's units.

  • operator: Set, Add, Subtract, Randomize.
  • resourceType: Ore, Gas, GasDepleted

unit_flags(playerId, unitId, flag)
Modifies flag data for the specified player's units.

  • flags:
    • temp (only affects units that exist at the time the command is called)
    • all other flags from idle_orders 'UnitFlags' and 'WithoutUnitFlags' fields.

create_unit_new(playerId, unitId, quantity, location, flag)
Creates units.

  • flag: lifted, hallucination, invincible, cloaked, burrowed, ignorePlacement.

remove_unit(playerId, unitId, quantity, location, flag)
Removes units.

  • flag: same as create_unit, besides ignorePlacement.

max_builders(unitId, quantity)
Restricts the AI to the specified number of builders at any given time.

tile_jump(operator, area, flag, jumpType, block)
Compares terrain status and jumps if true.

  • operator: True or False
  • area: (PosX, PosY)~R or Loc.n~R
  • flag: creep, power, walkable, unbuildable, heightLow, heightMed, heightHigh
  • jumpType: Jump, Call, Wait, combinable with `|`

define(playerId, operator, quantity, unitId, block)
Reads/writes %1playerId's define_max value of %4(unitId).

force(playerId, operator, quantity, block)
Reads/writes %1playerId's max_force value.

region_force(playerId, area, operator, quantity, block)
Reads/writes %1playerId's requested force in %2area.

build_request(playerId, townId, operator, quantity, unitId, priority, jumpType, block)
Reads/writes %1playerId's build request values of %5unitId and %6priority in %2townId, %7jumpType if true.

  • jumpType: Jump, Wait, Call, combinable with `|`

resource_write(operator, quantity, resourceType, playerId, resourceContainer, area)
Write %2quantity %3resourceType to all %4resourceContainer owned by %5playerId in %6area.

  • operator: Set, Add, Subtract, Randomize
  • resourceType: ore, gas, any
  • resourceContainer: unitId of resource containers to modify, combinable with `|`
  • area: coordinates or Loc.n

enemy_force(defenderId, attackerId, regionState, operator, quantity, quantityType, jumpType, block)
Read %2attackerId's forces in %1defenderId's %3regionState regions, and %7jumpType if true.

  • quantityType: `unit` or `force`

player_combat(playerId1, playerId2, area, operator, jumpType, block)
Checks for combat between %1playerId1 and %2playerId2 in %3area using %4operator, %5jumpType %6block if reading a true comparison

  • operator: True, False

replace_unit(playerId, townId, unitId1, unitId2, flag)
Replaces %1playerId's requests of %3unitId1 in %2townId with identical requests for %3unitId2

  • flag: Always, Once

request_control(playerId, townId, requestType, flag)
Allow %1playerId's %3requestTypes in %2townId to be satisfied by %4flag

  • requestType: build (includes player_need), train (includes train, queue, do_morph, wait_train), defend (includes defensebuild, defenseuse, and defense), attack (includes attack_add and attack_rand), and guard (includes guard and place_guard)
  • unitId: 65535 to ignore this field (all specified requestTypes will be modified)
  • flag: Global, Local

no arguments
If an attack is being prepared, wait until it commences.

id_jump(operator, townId, block)
Check for a town with %2townId and %1operator to %3block if true. Read-only.

resarea_jump(townId, operator, resarea, block)
Compare %1townId's %3resarea and %2operator to %4block if true. Read-only, true/false.

"Just" needs to be ported to 1.20.

start_campaigndone; `unstart_campaign`
When the game mode is UMS, all scripts are considered campaign scripts, regardless of whether or not `start_campaign` has been called. This makes certain opcodes impossible to use in a campaign/UMS setting. Making those opcodes campaign-friendly or making `start_campaign` necessary to override them would be a great fix for this behavior.

wait_attackdodone; `if_attacking(blockname)` | jumps to specified block if an attack is in progress
If an attack is being prepared, this command waits for the same duration as `attack_do` (until the attack is prepared). If no attack is being prepared, does nothing.

upgrade_jump(playerID, operator, upgradeID, upgradeLevel, block)
Compares the specified player's upgrade level and jumps if the comparison is true.

tech_jump(playerID, operator, techID, researchState, block)
Same as above besides accepting 0 (not researched) or 1 (researched) for `researchState`.

random_call(n, block)
Same as random_jump, but calls the desired block instead of jumping.

attack_rand(min, max, unitType)
Randomizes between min and max, then adds that many of unitType to the attack list.

supply(player, operator, quantity, race, unitType, block)
Compares specified player supply and jumps if true.

* race: terran, zerg, protoss, any — combinable with `|`

  • unitType: specific units, Group_Men, Group_Buildings, Any, Max (checks max supply)

resource(player, operator, amount, resourceType, block)
Reads/writes `resourceType` of `player` with specified settings and jumps if true.

  • resourceType: ore, gas (combinable with `|`)

time(timeType, operator, amount, block)
Compares time, read-only.

  • timeType: frames, minutes

sets the town's ID, for use with town_jump, remove_[request], and kill_town

remove_build(count, unit, townID)

remove_tech(tech, townID)
remove_upgrade(level, upgrade, townID)
remove_train(count, unit, townID)
Removes the specified request from the specified town

  • townID: 0-65534

guard(unitID, location, quantity, retrainings)

base_layout(unitID, [(loc1, loc2, etc])

Prints the specified string.

unit_avail(playerId, operator, availability, unit, block)
Reads or writes unit availability.

load_bunkers(location, unitId, unitAmount, bunkerId, bunkerAmount, priority)
Issues enter transport order to specified units for specified bunkers at specified location.

ping(PosX, PosY, color)
Pings the minimap at the specified coordinates/location for all human players.

tech_avail(playerId, operator, tech, level, block)
Reads or writes tech availability.

reveal_area(player, location, time)
Reveals the specified location.

  • location: accepts coordinates and Loc.n, with radius
  • time: amount of frames to reveal the location for

Instantly removes creep in the specified area.

bank_data(operator, quantity, category, key, block)
Along with save_bank and load_bank, manages data to be loaded across multiple maps.

unit_name(playerId, unitId, location, string, flag)
Sets the unit name of the specified units.

  • string: label used for the unit's name; accepts markup like PyTBL or SCMDraft (e.g. <0E> is blue).
  • flag: permanentOn - affects existing units and future units; cancelled with permanentOff

lift_land(playerId, unitId, quantity, liftLocation, landLocation, liftTownId, landTownId, flags)
Orders the specified unit to lift off and land at a new location, changing towns once landed.

  • landTownId: does not reassign the unit if set to 0, uses values from set_id.
  • flag: Return(Life(GreaterThan/LessThan/Equals %)) - the structure will attempt to return to its origin if the conditions are met.

queue(quantity, unitId, factoryId, townId, location, priority)
Adds the specified number of units to factory production queues if available.

  • factoryId: unitId of the production structure intended to queue the units.

container_jump(resourceType, operator, quantity, area, jumpType, block)
Read %1resource %3quantities in %4area, and %5jumpType if true.

  • resourceType: ore, gas, any
  • area: coordinates or Loc.n

local_jump(playerId, townId, operator, quantity, unitId, block)
%2operator (read-only) %3quantity of %4unitId assigned to %1townId, moving to %5blockType if true.

Engine Updates

  • add medic heal
  • add flag that detects when units have been added to attack group
  • allow users to specify %hp required for ‘Deathrattle` orders to be issued
  • add `State` flag for special unit states (`Cloaked|NotCloaked|Sieged|Burrowed|Disabled` etc)
  • add `Targeted` flag for units that are targeting the tgt_unit_id
  • add `NotSelf` to flags (issue_order/idle_orders)
  • add `NearUnit` to flags, accepting specific units and OtherUnit (issue_order/idle_orders)
  • add `Owner` flag for units owned by the specified player IDs (combinable with `|`)
  • add `TileHeight` flag for tile height comparisons (0-2 for default values)
  • add `health %`, `shields %`, `energy %` to flags (issue_order too)
  • add `health #`, `shields #`, `energy #` to flag (issue_order too)
  • add `group_men`, `group_buildings`, `group_factories` to target unit (issue_order too)
  • add flag to ’deathrattle' cast the spell in retaliation to taking near-fatal damage
  • make `source unit` and `target unit` fields accept multiple units with `|` (issue_order too)
  • add `Targeting` flag to detect what the source unit is targeting (`Own|Allied|NotEnemies|ThisUnit` etc)
  • add `Flags` and `WithoutFlags` flags to detect units.dat advanced flags (`Hero|Organic|Mechanical` etc)
  • add `Hangar` flag for counting the hangar of Carriers/Reavers/Vultures/Nuke Silos
  • allow two entries to `rate` with | and make idle_orders randomly select between the two entries
  • add `Count` flag for number of target units and search area (useful for area of effect spells)
  • add `Cargo` flag for number of units loaded into transport/bunker
  • add `TileFlags` flag for units on or adjacent to tiles (ramp/power/creep/(un)walkable/(un)buildable)


  • add ‘Shift` flag that adds it to the units’ queued orders


  • port functionality to aiscript
  • method to increase amount of resources returned per trip


  • add `Force #` check to `owner` field
  • add `Allies`, `Foes`, `Neutral Players`, and `Non Allied Victory Players` check to `owner` field

comparison commands: (deaths, kills, bring_jump, attacking, etc)

  • transform existing commands to use the following syntax, where flag can be Call, Jump, and/or Wait:
    • bring_jump playerId operator quantity unitId location block flag
  • add `_Call` operators (Exactly_Call, AtLeast_Call, AtMost_Call) to make the script call instead of jump if the comparison is true


  • ‘vulture_mines_off` and `vulture_mines_on` for toggling default vulture mine placement
  • `ignore_invincibile` for ignoring regions with only invincible enemy units
  • `nydus(Owner)` where owner can be Own, Allied, Enemy, Neutral, or None, combinable with `|`.
  • `independent_[requestType]` and `dependent_[requestType]` to control the source of guard, build, defense, etc. requests within a given town, where independent = only from the town it’s called in.
  • `no_building_base_layout` to prevent the AI from building structures in base_layout locations.
  • `no_unit_base_layout` to prevent the AI from idling units or placing guards in base_layout locations.
  • `bunkers_off` and `bunkers_on` for toggling default bunker placement
  • `retaliation(value)` where 0 = default behavior and 1 = disabled, for retaliatory spellcasts
  • `dont_focus_disabled` to prevent AI from focusing units disabled by lockdown or maelstrom effects.


  • add `Set_Random` and `Remove_Random` operators for random selection of locations of even priority.
  • add unit mover functionality; orders own and allied units to exit the area when a building is attempting to be placed, rechecks every 30 frames for up to (10? 30?) seconds before considering the location blocked; add aicontrol flag for specifying time before the location is considered blocked.


  • `drop_priority([healthType]([operator] quantity))` to override default transport panic behaviors
    • drop_priority(hp(LessThanPercent 30))
  • `cloak_energy(operator quantity)` to set the minimum amount of energy required for units to cloak
    • cloak_energy(GreaterThan 50)
  • `cloak_range(unitType pixels)` to make valid units cloak when they are within range of enemy units
    • cloak_range(Group_Men|missile_turret|photo_cannon|spore_colony 1280)
  • `safe_nuke(operator unitQuantity pixels) to prevent AI from nuking when %1operator %2unitQuantity are %3pixels from the nuke site
    • safe_nuke(AtLeast 6 720)
  • `exit_range(unitQuantity weaponId pixels)` to make up to %1unitQuantity AI units move %3pixels away from the weapon impact site of %2weaponId
    • exit_range(6 disruption_web 160)

Misc Requests

method to change units.dat, weapons.dat flingy.dat, sprites.dat, images.dat values based on player ID
method to disable `Splash (enemy)` from affecting allied units (for human players only/for co-op maps)
method to change unit name string IDs based on player ID

multiple attacks
Being able to manage multiple attacks from the same AI player would allow for significantly more versatility when constructing AI scripts in large-scale environments.

improved attack logic
Units used in AI attacks should automatically acquire targets while en route to their destination. Right now, they move to attack a certain target, instead of attack-moving to that target. Would be good to have them attack-move to the prepare region as well.
Additionally, ‘attack_prepare` chooses suboptimal targets a lot of the time. Though it’s possible to use randomized attack patterns with ‘attack_to`, making `attack_prepare` choose from a list of X nearest enemy-owned regions would be ideal for when users just want the AI to pick its own targets dynamically without accounting for every possible player action through script jumps and calls.
The ideal general-purpose attack logic would prioritize military/defense in a region, followed by production and then everything else.
Also: if AI could ignore regions with invulnerable units/buildings in them when choosing attack targets, that’d be great (maybe as a header command, like `ignore_invuln`).

Terran AI attempts to repair damaged zerg/protoss units.

The prerequisite unit of morphed guards (both archons, mutalisk morphs, lurkers) tend to travel some distance from their assigned guard position before morphing, and then remain where they are after they complete the morph instead of moving to their assigned guard position.
Excess prerequisites occasionally move to the morphing site. This behavior also happens with drones morphing to buildings and can needlessly slow down the AI while also looking pretty dumb. Excess prerequisites for morphed units (i.e. not drones) will idle near the morphing site until used for something. This may be a separate issue from the first one.
Morphed units do not currently accept define_max values and as a result are less stringently controlled script-side. Fixing this would obviously let modders use appropriate values of morphed units instead of praying that the AI doesn't do something dumb. This can be worked around with intelligent uses of `train` since the aise changes to that command.

carrier/reaver guards
Carrier and reaver guards idle near their factory instead of moving to their guard spots.

units with subunits
Carriers and reavers owned by the AI occasionally "forget" to idly train their subunits.

attack -> guard
Nekron has observed attacks becoming converted to guards while preparing. This behavior is consistent with the attack being cleared before firing, but rare cases of attacks being cleared without attack_clear being called have been observed. More info is still needed on this issue.

using image entries in SC:R
SC:R does not read the circled data. If this were re-enabled, making the same graphic use multiple iscript entries would be possible, improving customization options for modders.

creep/power placement ignored on loss
When rebuilding lost creep structures, zerg AI tend to ignore the `creep` command (e.g. `creep(4)`) and thus rebuild everything in the same spot instead of achieving creep spread.
The same is true for protoss AIs who will seek to rebuild everything wherever they can fit it, leading to really cluttered bases after some structures have been lost. This may be a separate issue entirely.

request limits
defense(build/use) - 20 per type
train - unknown? Assumed 64
build - 30?
attack_add - 64

other limits
towns - 100
military - 1000
units - 1700
sprites - 500 without parent flingy, 500 for fog of war sprites, 2500 total
aiscript/bwscript size - 64bytes

the stuck worker bug
If towns far apart from one another call for workers from each other, workers will often get stuck, hanging the thread that requested the worker.

resource requirements — done: max_workers | parameter of ‘255` reverts to default behavior | town-specific
Currently `start_town` tends to require resources nearby for the script to function correctly. Removing this requirement would be ideal, so we could have scripts running without needing to place resources.
Related to this, terran AI have issues where if they don’t have enough worker slots in their local resarea, they won't research or upgrade certain IDs. This is obviously a dumb blizzard bug and it would be great to have it corrected.

the guard crash — done: native to aise
From Nekron's document:
1) A guard needs to have been killed at least twice while still being a guard
2) Once the AI starts the request to replace the guard, it must be busy enough to take a few seconds before acting on it
3) One unit build time later, the AI needs to have another idle production building which can start creating the second guard.
4) The first guard spawns after the second guard has started production
5) The first guard dies before the second guard finishes
6) The second guard spawns, and stays alive long enough that its guard ai gets reused for some unrelated request
7) The game most usually crashes, often after a brief perio

changing max supply in remastered — done: mtl supply
Porting the exe edit from 1.16.1 to 1.20+ would be great.

the train bug — done: native to aise
From Nekron's document:
Train requests (Using train, do_morph, wait_force and possibly wait_train) used when the AI has a guard of the same type that the script wants to train, while the guard needs to be replenished, while the guard area is not in combat, causes the train request to be ignored and makes the AI train units as if the script had defaultbuild enabled

enabling train order on all zerg buildings in remastered — done: mtl order
Porting the exe edit from 1.16.1 to 1.20+ would be great.

raise thread limits — done: native to aise
Current thread limit is 100

hooking max supply to aiscript — done: supply
Or some other way to change max supply on a per race, per map, and per player basis.

When an SCV is killed while building a structure, the AI will hang up instead of sending another SCV to finish the job.

defense — done: under_attack
AI pause all non-defense actions when attacked and do not resume script execution until the defense request is filled. Making the AI not interrupt script execution during defense would go a long way to making AI on large maps feel more alive. AI also sometimes take units from preparing/executing attacks to defense.

Tool Updates

all programs - warning on overwriting default files
Just a luxury for the idiots among us who mod while half asleep (aka me). I actually think this is partially functional for some programs, but notably PyAI does not have any countermeasure in place.

PyAI - comments
Useful for obvious reasons, especially when editing the aiscript of another modder without access to the original source. Low priority.

PyAI - save position on script save
Annoying bug that resets the main window position when you save a script.

PyMPQ - extract audio files
PyMPQ crashes when extracting audio files from mpq archives created by WinMPQ or whatever Blizzard used to compile the vanilla mpqs.

PyMPQ - fail to write data to archive if in use
PyMPQ crashes when adding files to an archive that's in use (SCMDraft, launched exe).

PyGRP - cmdicon offsets
When using PyGRP to save cmdicons, the icons are saved at an offset and thus are only partially visible in-game. SFGrpConv doesn't have this issue, but it'd be nice to only need to use one grp program.