Add support for lua-based aiscripts

This commit is contained in:
Pr0nogo 2026-09-09 10:52:21 -04:00
parent eb4319fe4f
commit 5d34cfd887
37 changed files with 5958 additions and 0 deletions

1
mpq/lua/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.luarc.json

View File

@ -0,0 +1,151 @@
-- Setup main variables
local player = Player()
if player.type ~= PlayerType.Computer then
return
end
local ai = AI()
local config = PlayerVars().ai_config or {}
local main_town = ai:start_town()
-- Neat way to organize stuff is to use a table
local brain = {
saw_air_units = false,
}
---@param town Town
local function flyer_watch(town)
wait_until(function() return brain.saw_air_units end)
for i = 1, 5 do
town:build(i, UnitId.TerranWatchdog, 70-i)
wait(secs(30 + 5 * i))
end
end
---@param town Town
local function build_main_town(town)
town:build(20, UnitId.TerranMason, 90)
wait(secs(30))
town:build(1, UnitId.TerranStockade, 100)
wait(secs(90))
town:build(2, UnitId.TerranStockade, 90)
wait(secs(5))
town:build(3, UnitId.TerranStockade, 70)
wait(mins(1))
town:multirun(flyer_watch)
town:build(4, UnitId.TerranStockade, 70)
end
---@param town Town
local function build_expo(town)
cprint(TextColor.Orange, "Expanding:")
print(town)
town:build(1, UnitId.TerranMinistry, 90)
town:wait_build(1, UnitId.TerranMinistry)
town:transfer_workers(4)
town:build(20, UnitId.TerranMason, 85)
town:wait_build(10, UnitId.TerranMason)
town:multirun(flyer_watch)
town:build(1, UnitId.TerranStockade, 80)
wait(secs(30))
town:build(2, UnitId.TerranStockade, 70)
-- TODO: town:build_at(UnitId.TerranStockage, Point(69, 69), 90)
-- TODO: town:new_guard(...)
end
local function expand_loop()
wait(secs(90))
while true do
local town = ai:expand()
town:multirun(build_expo) -- town:multirun manages script lifetime based on town alive state
if town.is_alive then
wait(math.random(mins(3), mins(4)))
else
wait(math.random(secs(30), mins(1))) -- if town didn't create we wait for shorter
end
-- NOTE: if we didn't need the town.is_alive check we can use nice shorthand:
-- ai:expand:multirun(build_expo)
end
end
local function attack_waves()
main_town:wait_build(1, UnitId.TerranStockade)
while true do
-- Classic attack wave with counts
--[[
local attack = ai:new_attack()
attack.from = Point(3800, 500)
if Time.elapsed <= mins(5) then
attack:add(5 + (Time.elapsed // mins(2)), UnitId.TerranMaverick)
else
attack:add(4 + (Time.elapsed // mins(4)), UnitId.TerranMaverick)
attack:add(2 + (Time.elapsed // mins(4)), UnitId.TerranHarakan)
end
]]
-- Composing attack wave from power value
local attack_budget = player:unit_count(UnitId.TerranMason) * 50
+ (player.minerals + player.gas) // 2
local attack = ai:new_attack(attack_budget)
attack.budget = attack_budget
attack.timeout = 120
-- attack.from = Point(3800, 500)
if player:unit_count(UnitId.TerranStockade) > 0 then
attack:compose(config.pref_maverick or 1, UnitId.TerranMaverick)
attack:compose(config.pref_harakan or 1, UnitId.TerranHarakan)
attack:compose(config.pref_cyprian or 1, UnitId.TerranCyprian)
end
cprint(TextColor.Red, "Sending attack:")
print(attack)
attack:send()
wait(math.random(secs(5), secs(15)))
end
end
local function dump_defenses()
cprint(TextColor.Yellow3, "Defenses:")
print("build.gg:", ai.defense.build.gg)
print("use.gg:", ai.defense.use.gg)
print("build.ag:", ai.defense.build.ag)
print("use.ag:", ai.defense.use.ag)
print("build.ga:", ai.defense.build.ga)
print("use.ga:", ai.defense.use.ga)
print("build.aa:", ai.defense.build.aa)
print("use.aa:", ai.defense.use.aa)
end
---@param unit_id UnitId
---@param cond fun(): boolean
local function defense_watch_impl(unit_id, cond)
while true do
wait_until(cond, secs(1))
ai.defense:auto_set(unit_id)
-- dump_defenses()
wait_while(cond, secs(1))
ai.defense:auto_remove(unit_id)
-- dump_defenses()
end
end
---@param factory_id UnitId
---@param unit_id UnitId
local function defense_watch(factory_id, unit_id)
defense_watch_impl(unit_id, function()
return player:unit_count(factory_id) > 0
end)
end
-- Run all the functions
-- Special kind of multirun, this script will die when town dies
-- Equivalent to `multirun(function(town) town:auto_kill() ... end, town)`
main_town:multirun(build_main_town)
multirun(expand_loop)
multirun(attack_waves)
multirun(defense_watch, UnitId.TerranStockade, UnitId.TerranMaverick)
multirun(defense_watch, UnitId.TerranStockade, UnitId.TerranHarakan)
wait(mins(8))
brain.saw_air_units = true -- let's pretend, since we don't have api for detecting units yet

235
mpq/lua/melee/melee_ai.lua Normal file
View File

@ -0,0 +1,235 @@
require("melee.melee_common")
require("melee.personalities")
---@class AIPersonality
---@field name string
---@field unit_composition table<UnitId, number>
---@field main_build_order fun(melee_ai: MeleeAI, town: Town)[]
---@field expansion_build_order fun(melee_ai: MeleeAI, town: Town)[]
---@field build_anti_ground_defenses fun(melee_ai: MeleeAI, town: Town)
---@field build_anti_air_defenses fun(melee_ai: MeleeAI, town: Town)
---@field expand_rate? number Default is 3 minutes 30 seconds
---@field expand_wait_after_fail? number Default is 30 seconds
---@field attack_wave_budget_scaling? number Default is 1.0
---@field attack_wave_cost_bias? number Default derived from AI:new_attack()
---@class TownState
---@field started_anti_ground_defenses boolean
---@field started_anti_air_defenses boolean
---@class MeleeAI
---@field debugging {enabled: boolean, last_tick: number, last_script_id: number}
---@field player Player
---@field personality AIPersonality
---@field ai AI
---@field main_town Town
---@field towns table<Town, TownState>
---@field started_expanding boolean
---@field started_attacking boolean
---@field seen_air_threats boolean
MeleeAI = {}
MeleeAI.__index = MeleeAI
setmetatable(MeleeAI, {
__call = function(cls, ...)
return cls.new(...)
end,
})
---@param config? table
---@param player? Player
function MeleeAI.new(config, player)
local self = setmetatable({}, MeleeAI)
self.debugging = {
enabled = PlayerVars(player).ai_debug or false,
last_tick = -1,
last_script_id = -1,
}
self.player = player or Player()
self.personality = config or Personalities.pick_random(player)
self.ai = AI(self.player.id)
self.main_town = self.ai:start_town()
self.towns = {
[self.main_town] = {
started_anti_ground_defenses = false,
started_anti_air_defenses = false,
},
}
self.started_expanding = false
self.started_attacking = false
self.seen_air_threats = true -- TODO: detect dynamically or after timeout
-- TODO: maybe have a dynamic unit loss threshold check that triggers a stronger defense response
self:cdebug(TextColor.Yellow3, "Starting AI with personality: " .. self.personality.name)
return self
end
function MeleeAI:debug_header()
if self.debugging.last_tick ~= Time.elapsed or self.debugging.last_script_id ~= Script.id then
self.debugging.last_tick = Time.elapsed
self.debugging.last_script_id = Script.id
print()
cprint(TextColor.Yellow3, "-- " .. tostring(self.player.name) .. " (P" .. tostring(self.player.id) .. "):\n")
end
end
function MeleeAI:debug(...)
if self.debugging.enabled then
self:debug_header()
print(...)
end
end
function MeleeAI:cdebug(color, ...)
if self.debugging.enabled then
self:debug_header()
cprint(color, ...)
end
end
-- How often the banked-resource snapshot is refreshed while a dynamic_wait is
-- in progress, so a long wait reacts to resources changing mid-wait instead
-- of freezing the scale from a single sample taken at the start.
local DYNAMIC_WAIT_RESAMPLE_INTERVAL = secs(2)
---@param duration integer
function MeleeAI:dynamic_wait(duration)
local elapsed = 0
while elapsed < duration do
local chunk = math.min(DYNAMIC_WAIT_RESAMPLE_INTERVAL, duration - elapsed)
local resources = self.player.minerals + self.player.gas
local resource_scale = math.max(0, math.min(1, (1100 - resources) / 600))
local scaled_chunk = math.floor(chunk * resource_scale)
if scaled_chunk > 0 then
wait(scaled_chunk)
end
elapsed = elapsed + chunk
end
end
---@protected
---@return table<UnitId, number>
function MeleeAI:get_final_unit_composition()
local unit_composition = {}
for unit_id, preference in pairs(self.personality.unit_composition) do
unit_composition[unit_id] = preference
end
return unit_composition
end
function MeleeAI:run()
for unit_id, preference in pairs(self:get_final_unit_composition()) do
if preference > 0 then
multirun(function()
wait_until(function() return self.player:can_make(unit_id) end, secs(1))
self:cdebug(TextColor.Teal, "Added " .. UnitId.name(unit_id) .. " to defense")
self.ai.defense:auto_set(unit_id)
wait_while(function() return self.player:can_make(unit_id) end, secs(1))
self:cdebug(TextColor.Teal, "Added " .. UnitId.name(unit_id) .. " to defense")
self.ai.defense:auto_remove(unit_id)
end)
end
end
multirun(function()
self.main_town:auto_kill_script()
self.main_town:build(1, MeleeCommon.default_town_building(self.player.race), 120)
self.main_town:build(self.main_town.max_workers, MeleeCommon.default_worker(self.player.race), 100)
for i, build_order_step in ipairs(self.personality.main_build_order) do
build_order_step(self, self.main_town)
if i == 1 then
-- If opener didn't enable this, we now should
self:start_attacking()
self:start_expanding()
end
end
end)
end
function MeleeAI:start_expanding()
if self.started_expanding then
return
end
self.started_expanding = true
multirun(function()
while true do
local town = self.ai:expand()
self:cdebug(TextColor.Green, "Expanding:")
self:debug(town)
if town.is_alive then
self.towns[town] = {
started_anti_ground_defenses = false,
started_anti_air_defenses = false,
}
town:multirun(function()
for i, build_order_step in ipairs(self.personality.expansion_build_order) do
build_order_step(self, town)
if i == 1 then
self:start_anti_ground_defenses(town)
self:start_anti_air_defenses(town)
end
end
end)
wait(self.personality.expand_rate or (mins(3) + secs(30)))
else
wait(self.personality.expand_wait_after_fail or secs(30))
end
end
end)
end
function MeleeAI:start_attacking()
if self.started_attacking then
return
end
self.started_attacking = true
multirun(function()
while true do
local budget = self.player:unit_count(MeleeCommon.default_worker(self.player.race)) * 50
+ (self.player.minerals + self.player.gas) / 2
budget = budget * (self.personality.attack_wave_budget_scaling or 1.0)
local attack = self.ai:new_attack(budget, self.personality.attack_wave_cost_bias or nil)
for unit_id, preference in pairs(self:get_final_unit_composition()) do
if preference > 0 and self.player:can_make(unit_id) then
attack:compose(preference, unit_id)
end
end
self:cdebug(TextColor.Orange, "Sending attack:")
self:debug(attack)
attack:send()
wait(secs(5))
end
end)
end
function MeleeAI:start_anti_ground_defenses(town)
if self.personality.build_anti_ground_defenses == nil then
return
end
if self.towns[town].started_anti_ground_defenses then
return
end
self.towns[town].started_anti_ground_defenses = true
multirun(function()
self.personality.build_anti_ground_defenses(self, town)
end)
end
function MeleeAI:start_anti_air_defenses(town)
if self.personality.build_anti_air_defenses == nil then
return
end
if self.towns[town].started_anti_air_defenses then
return
end
self.towns[town].started_anti_air_defenses = true
multirun(function()
wait_until(function() return self.seen_air_threats end, secs(1))
self.personality.build_anti_air_defenses(self, town)
end)
end

View File

@ -0,0 +1,33 @@
MeleeCommon = {}
---@param race RaceId
---@return UnitId worker_unit_id
function MeleeCommon.default_worker(race)
if race == RaceId.Terran then
return UnitId.TerranMason
elseif race == RaceId.Zerg then
return UnitId.ZergDroleth
elseif race == RaceId.Protoss then
return UnitId.ProtossScribe
elseif race == RaceId.Askosi then
return UnitId.AskosiPilgrim
end
error("Race " .. tostring(race) .. " has no default worker set")
end
---@param race RaceId
---@return UnitId town_building_unit_id
function MeleeCommon.default_town_building(race)
if race == RaceId.Terran then
return UnitId.TerranMinistry
elseif race == RaceId.Zerg then
return UnitId.ZergHachirosk
elseif race == RaceId.Protoss then
return UnitId.ProtossNexus
elseif race == RaceId.Askosi then
return UnitId.AskosiLandlock
end
error("Race " .. tostring(race) .. " has no default town set")
end
return MeleeCommon

View File

@ -0,0 +1,141 @@
local TerranArmor = require("melee.terran.armor")
local TerranRaider = require("melee.terran.raider")
local TerranOrbital = require("melee.terran.orbital")
local TerranGeneral = require("melee.terran.general")
local ZergAssault = require("melee.zerg.assault")
local ZergSwarm = require("melee.zerg.swarm")
local ZergSunblot = require("melee.zerg.sunblot")
local ZergGeneral = require("melee.zerg.general")
local ProtossRanger = require("melee.protoss.ranger")
local ProtossTemplar = require("melee.protoss.templar")
local ProtossSkylord = require("melee.protoss.skylord")
local ProtossGeneral = require("melee.protoss.general")
---@class Personalities
Personalities = {
Terran = {
ArmorTankPush = TerranArmor.tank_push,
ArmorFeFiveFulc = TerranArmor.fe_five_fulc,
ArmorTwoFulcVulture = TerranArmor.two_fulc_vulture,
ArmorWalkerPower = TerranArmor.walker_power,
ArmorIronCrusade = TerranArmor.iron_crusade,
ArmorDavidAndGoliath = TerranArmor.david_and_goliath,
RaiderEightRax = TerranRaider.eight_rax,
RaiderThreeRaxBio = TerranRaider.three_rax_bio,
RaiderFastAnvil = TerranRaider.fast_anvil,
RaiderFourRaxPressure = TerranRaider.four_rax_pressure,
RaiderUprising = TerranRaider.uprising,
RaiderHolyAckmeds = TerranRaider.holy_ackmeds,
OrbitalRapidStarpads = TerranOrbital.rapid_starpads,
OrbitalTheDon = TerranOrbital.the_don,
OrbitalWraithBio = TerranOrbital.wraith_bio,
OrbitalHotdrop = TerranOrbital.hotdrop,
OrbitalSuddenStarport = TerranOrbital.sudden_starport,
OrbitalAcesHigh = TerranOrbital.aces_high,
GeneralTwoFulcrumMech = TerranGeneral.two_fulcrum_mech,
GeneralEarlyRiser = TerranGeneral.early_riser,
GeneralTheCube = TerranGeneral.the_cube,
GeneralDoublingUp = TerranGeneral.doubling_up,
GeneralMechIntoAir = TerranGeneral.mech_into_air,
GeneralObservant = TerranGeneral.observant,
},
Zerg = {
AssaultThreeBaseHydrith = ZergAssault.three_base_hydrith,
AssaultTwoBaseZorkiz = ZergAssault.two_base_zorkiz,
AssaultTwoBaseHydrithQuazeth = ZergAssault.two_base_hydrith_quazeth,
AssaultTwoBaseAlkag = ZergAssault.two_base_alkag,
AssaultPotency = ZergAssault.potency,
AssaultEarthenTides = ZergAssault.earthen_tides,
SwarmThreeBaseFlood = ZergSwarm.three_base_flood,
SwarmThreeBaseUltrav = ZergSwarm.three_base_ultrav,
SwarmInstantQuazeth = ZergSwarm.instant_quazeth,
SwarmThreeBaseOthstol = ZergSwarm.three_base_othstol,
SwarmGatesOfHell = ZergSwarm.gates_of_hell,
SwarmPrimitivePressure = ZergSwarm.primitive_pressure,
SunblotBigAndLittle = ZergSunblot.big_and_little,
SunblotTheTriad = ZergSunblot.the_triad,
SunblotSkitteringDuo = ZergSunblot.skittering_duo,
SunblotEarlyBirds = ZergSunblot.early_birds,
SunblotFlyingFeint = ZergSunblot.flying_feint,
SunblotDescendingHorizon = ZergSunblot.descending_horizon,
GeneralSleepingGiant = ZergGeneral.sleeping_giant,
GeneralShriekingSwarm = ZergGeneral.shrieking_swarm,
GeneralSubjugation = ZergGeneral.subjugation,
GeneralBeforeTheStorm = ZergGeneral.before_the_storm,
GeneralTimeAfterTime = ZergGeneral.time_after_time,
GeneralConstrainedCoil = ZergGeneral.constrained_coil,
},
Protoss = {
RangerTwoGateStrider = ProtossRanger.two_gate_strider,
RangerEmissarySquared = ProtossRanger.emissary_squared,
RangerOneGateAuthority = ProtossRanger.one_gate_authority,
RangerLatticeExpo = ProtossRanger.lattice_expo,
RangerSimulantSwarm = ProtossRanger.simulant_swarm,
RangerTheRegistry = ProtossRanger.the_registry,
TemplarThreeGateStrider = ProtossTemplar.three_gate_strider,
TemplarSuffocatingShadow = ProtossTemplar.suffocating_shadow,
TemplarLegion = ProtossTemplar.legion,
TemplarInitiates = ProtossTemplar.initiates,
TemplarMartyrdom = ProtossTemplar.martyrdom,
TemplarLegionsHeralds = ProtossTemplar.legions_heralds,
SkylordSolarEscort = ProtossSkylord.solar_escort,
SkylordDeadlyOmen = ProtossSkylord.deadly_omen,
SkylordTwoGatePanoptus = ProtossSkylord.two_gate_panoptus,
SkylordExalted = ProtossSkylord.exalted,
SkylordSorrowFleet = ProtossSkylord.sorrow_fleet,
SkylordShipsAndSwords = ProtossSkylord.ships_and_swords,
GeneralLunaticLegion = ProtossGeneral.lunatic_legion,
GeneralStridentStriders = ProtossGeneral.strident_striders,
GeneralWorship = ProtossGeneral.worship,
GeneralRoboticsFe = ProtossGeneral.robotics_fe,
GeneralTheSwordsmiths = ProtossGeneral.the_swordsmiths,
GeneralMatriarchy = ProtossGeneral.matriarchy,
},
Askosi = {},
}
---@class RacePersonalities
---@param race RaceId
---@return RacePersonalities
local function by_race(race)
if race == RaceId.Terran then
return Personalities.Terran
elseif race == RaceId.Zerg then
return Personalities.Zerg
elseif race == RaceId.Protoss then
return Personalities.Protoss
elseif race == RaceId.Askosi then
return Personalities.Askosi
end
error("Race " .. tostring(race) .. " is not a valid AI race")
end
---@param dict table
---@return AIPersonality
local function pick_random(dict)
local keys = {}
for key, _ in pairs(dict) do
table.insert(keys, key)
end
local random_key = keys[math.random(#keys)]
return dict[random_key]
end
---@param player? Player
---@return AIPersonality
function Personalities.pick_random(player)
player = player or Player()
local race_personalities = by_race(player.race)
return pick_random(race_personalities)
end
---@param personality AIPersonality
---@return AIPersonality
function Personalities.copy(personality)
error("not implemented yet")
end
return Personalities

View File

@ -0,0 +1,31 @@
local Basic = {}
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_expansion(melee_ai, town)
town:build(1, UnitId.ProtossNexus, 120)
town:wait_build(1, UnitId.ProtossNexus)
town:build(town.max_workers, UnitId.ProtossScribe, 100)
town:transfer_workers(8)
town:wait_build(10, UnitId.ProtossScribe)
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_ground(melee_ai, town)
for i = 1, 4 do
town:build(i, UnitId.ProtossWarden, 70 - i * 5)
wait(secs(25 + i * 5))
end
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_air(melee_ai, town)
for i = 1, 3 do
town:build(i, UnitId.ProtossMatrix, 80 - i * 5)
wait(secs(25 + i * 5))
end
end
return Basic

View File

@ -0,0 +1,424 @@
---@class ProtossOpeners
local Openers = {}
-- Ranger
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_gate_strider(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(30))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.emissary_squared(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ProtossPylon, 90)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(55))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.one_gate_authority(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.lattice_expo(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(50))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(3, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.simulant_swarm(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(20))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.the_registry(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
end
-- Templar
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_gate_strider(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.suffocating_shadow(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.legion(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(35))
town:build(3, UnitId.ProtossGateway, 90)
wait(secs(1))
town:build(3, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(30))
town:build(4, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(5, UnitId.ProtossGateway, 90)
wait(secs(1))
town:build(4, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(20))
town:build(6, UnitId.ProtossGateway, 90)
wait(secs(1))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.initiates(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
town:wait_build(2, UnitId.ProtossGateway)
melee_ai:dynamic_wait(secs(10))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(40))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.martyrdom(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(2) + secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.legions_heralds(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
end
-- Skylord
---@param melee_ai MeleeAI
---@param town Town
function Openers.solar_escort(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.deadly_omen(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossStargate, 90)
town:wait_build(1, UnitId.ProtossStargate)
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
town:build(2, UnitId.ProtossPylon, 90)
town:wait_build(2, UnitId.ProtossPylon)
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_gate_panoptus(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(50))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(5))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.exalted(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossStargate, 90)
town:build(1, UnitId.ProtossWarden, 79)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossWarden, 79)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.sorrow_fleet(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(40))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.ships_and_swords(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(5))
town:wait_build(2, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_attacking()
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Openers.lunatic_legion(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossPylon, 90)
wait(secs(3))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.matriarchy(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(5))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.robotics_fe(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ProtossPylon, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.strident_striders(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.the_swordsmiths(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossLattice, 90)
town:wait_build(1, UnitId.ProtossGateway)
town:wait_build(1, UnitId.ProtossLattice)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.worship(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossPylon, 110)
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
return Openers

View File

@ -0,0 +1,199 @@
---@class ProtossTier1
local Tier1 = {}
-- Ranger
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_gate_strider(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
town:build(4, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.one_gate_authority(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(45))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.simulant_swarm(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(5, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(20))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.the_registry(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(40))
town:wait_build(2, UnitId.ProtossPylon)
town:build(4, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(30))
end
-- Templar
---@param melee_ai MeleeAI
---@param town Town
function Tier1.three_gate_strider(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(mins(1))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(45))
town:build(5, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.suffocating_shadow(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.legion(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(9, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.initiates(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(4, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.legions_heralds(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(20))
end
-- Skylord
---@param melee_ai MeleeAI
---@param town Town
function Tier1.solar_escort(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.deadly_omen(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(mins(1))
town:build(4, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(50))
town:build(5, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_gate_panoptus(melee_ai, town)
melee_ai:dynamic_wait(mins(1) + secs(35))
town:build(3, UnitId.ProtossGateway, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.exalted(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(mins(1))
town:build(3, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.ships_and_swords(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(55))
town:build(3, UnitId.ProtossStargate, 90)
melee_ai:dynamic_wait(secs(50))
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Tier1.matriarchy(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.robotics_fe(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:wait_build(1, UnitId.ProtossPylon)
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.strident_striders(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(50))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(mins(1) + secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.the_swordsmiths(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossPylon, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.worship(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.ProtossGateway, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(15))
end
return Tier1

View File

@ -0,0 +1,179 @@
---@class ProtossTier2
local Tier2 = {}
--- Requests a Lattice and an extra Gateway. The generic tier-2 scale-up, for
--- personalities without bespoke tier-2 content.
---@param melee_ai MeleeAI
---@param town Town
function Tier2.acquire(melee_ai, town)
town:build(1, UnitId.ProtossLattice, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossGateway, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.strider_lattice_library_gateway(melee_ai, town)
town:build(1, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(15))
town:build(5, UnitId.ProtossGateway, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.emissary_ardent_lattice_gateway(melee_ai, town)
town:build(1, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossLattice, 70)
wait(secs(3))
town:build(2, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.authority_ardent_gateway_expand_lattice(melee_ai, town)
town:build(1, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.ProtossGateway, 70)
wait(secs(1))
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(5))
town:build(4, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.expo_ardent_authority(melee_ai, town)
town:build(1, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.strider3_library_expand(melee_ai, town)
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(20) + secs(40))
town:build(2, UnitId.ProtossGrandLibrary, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.shadow_roguegallery_expand_library(melee_ai, town)
town:build(1, UnitId.ProtossRogueGallery, 70)
town:build(2, UnitId.ProtossRogueGallery, 70)
melee_ai:dynamic_wait(secs(30) + secs(40))
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(mins(1) + secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.legion_library_x2(melee_ai, town)
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossGrandLibrary, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.martyrdom_pylon_roguegallery(melee_ai, town)
town:build(4, UnitId.ProtossPylon, 70)
wait(secs(3))
town:build(2, UnitId.ProtossRogueGallery, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.heralds_library_gateway_buildup(melee_ai, town)
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.ProtossPylon, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(40) + mins(1))
town:build(5, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(mins(1))
town:build(9, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(mins(1) + secs(55))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.escort_stargate_buildup(melee_ai, town)
town:build(1, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(45))
town:build(3, UnitId.ProtossStargate, 70)
wait(secs(1))
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.legion_stargate_gateway_library(melee_ai, town)
town:build(1, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(40) + secs(5))
town:build(3, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(20))
town:build(4, UnitId.ProtossGateway, 70)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.matriarchy_library_lattice(melee_ai, town)
town:build(1, UnitId.ProtossGrandLibrary, 70)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ProtossLattice, 70)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.robotics_ardent_x2(melee_ai, town)
town:build(1, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.swordsmiths_ardent_stargate(melee_ai, town)
town:build(1, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossStargate, 70)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossArdentAuthority, 70)
melee_ai:dynamic_wait(secs(25))
end
return Tier2

View File

@ -0,0 +1,86 @@
---@class ProtossTier3
local Tier3 = {}
--- Requests a Prostration Stage and waits for it. The generic tier-3 tech
--- transition, for personalities without bespoke tier-3 content.
---@param melee_ai MeleeAI
---@param town Town
function Tier3.acquire(melee_ai, town)
town:build(1, UnitId.ProtossProstrationStage, 65)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.authority_prostration_ardent2(melee_ai, town)
town:build(1, UnitId.ProtossProstrationStage, 65)
melee_ai:dynamic_wait(secs(50))
town:build(2, UnitId.ProtossArdentAuthority, 50)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.martyrdom_prostration_crucible(melee_ai, town)
town:build(1, UnitId.ProtossProstrationStage, 65)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossCrucible, 50)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.escort_argosy_buildup(melee_ai, town)
town:build(1, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.exalted_argosy_crucible(melee_ai, town)
town:build(1, UnitId.ProtossArgosy, 50)
town:wait_build(1, UnitId.ProtossArgosy, false)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossCrucible, 50)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.fleet_argosy_buildup(melee_ai, town)
town:build(1, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(mins(1))
town:build(2, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ProtossArgosy, 50)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.legion_argosy_only(melee_ai, town)
town:build(1, UnitId.ProtossArgosy, 50)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.matriarchy_prostration(melee_ai, town)
town:build(1, UnitId.ProtossProstrationStage, 65)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.robotics_prostration(melee_ai, town)
town:build(1, UnitId.ProtossProstrationStage, 65)
melee_ai:dynamic_wait(mins(2) + secs(20))
end
return Tier3

View File

@ -0,0 +1,174 @@
---@class ProtossTier4
local Tier4 = {}
---@param melee_ai MeleeAI
---@param town Town
function Tier4.emissary_synthetic_synod(melee_ai, town)
town:build(1, UnitId.ProtossSyntheticSynod, 45)
end
--- Generic tier-4 capstone for the Templar category, for personalities
--- without bespoke tier-4 content.
---@param melee_ai MeleeAI
---@param town Town
function Tier4.templar_archives_acquire(melee_ai, town)
town:build(1, UnitId.ProtossAncestralArchives, 45)
melee_ai:dynamic_wait(secs(15))
end
--- Generic tier-4 capstone for the Skylord category, for personalities
--- without bespoke tier-4 content.
---@param melee_ai MeleeAI
---@param town Town
function Tier4.skylord_monument_acquire(melee_ai, town)
town:build(1, UnitId.ProtossMonumentOfSin, 45)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.authority_synthetic_synod(melee_ai, town)
town:build(1, UnitId.ProtossSyntheticSynod, 45)
wait(secs(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.ranger_expansion(melee_ai, town)
town:build(1, UnitId.ProtossLattice, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossSyntheticSynod, 45, true)
wait(secs(1))
town:build(1, UnitId.ProtossGateway, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossArdentAuthority, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ProtossEmbassy, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossStargate, 70, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossArgosy, 50, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ProtossGrandLibrary, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossAncestralArchives, 45, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossMonumentOfSin, 45, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.martyrdom_ancestral_archives_scaling(melee_ai, town)
town:build(2, UnitId.ProtossAncestralArchives, 45)
melee_ai:dynamic_wait(mins(1))
town:wait_build(2, UnitId.ProtossAncestralArchives, false)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.ProtossAncestralArchives, 45)
melee_ai:dynamic_wait(secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.templar_expansion(melee_ai, town)
town:build(1, UnitId.ProtossGateway, 90, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossGrandLibrary, 70, true)
town:build(1, UnitId.ProtossAncestralArchives, 45, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossLattice, 90, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossEmbassy, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossArdentAuthority, 70, true)
town:build(1, UnitId.ProtossSyntheticSynod, 45, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossStargate, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossArgosy, 50, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossMonumentOfSin, 45, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.exalted_monument_of_sin(melee_ai, town)
town:build(1, UnitId.ProtossMonumentOfSin, 45)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.fleet_monument_of_sin(melee_ai, town)
town:build(1, UnitId.ProtossMonumentOfSin, 45)
melee_ai:dynamic_wait(mins(2) + secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.skylord_expansion(melee_ai, town)
town:build(1, UnitId.ProtossGateway, 90, true)
town:wait_build(1, UnitId.ProtossGateway)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ProtossStargate, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossEmbassy, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossArgosy, 50, true)
melee_ai:dynamic_wait(secs(5))
town:wait_build(1, UnitId.ProtossStargate)
melee_ai:dynamic_wait(secs(50))
town:wait_build(1, UnitId.ProtossArgosy)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ProtossLattice, 90)
melee_ai:dynamic_wait(secs(40))
town:wait_build(1, UnitId.ProtossArgosy)
town:build(1, UnitId.ProtossMonumentOfSin, 45, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossGrandLibrary, 70)
town:wait_build(1, UnitId.ProtossGrandLibrary)
town:build(1, UnitId.ProtossAncestralArchives, 45, true)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ProtossLattice)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossArdentAuthority, 70)
town:wait_build(1, UnitId.ProtossArdentAuthority)
town:build(1, UnitId.ProtossGrandLibrary, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossUnitaryUnion, 50, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ProtossSyntheticSynod, 45, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossProstrationStage, 65, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.matriarchy_synthetic_synod_x2(melee_ai, town)
town:build(2, UnitId.ProtossSyntheticSynod, 45)
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.robotics_synthetic_synod(melee_ai, town)
town:build(1, UnitId.ProtossSyntheticSynod, 45)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier4.general_expansion(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossGateway, 90, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossEmbassy, 70, true)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ProtossLattice, 90, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ProtossStargate, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ProtossGrandLibrary, 70, true)
town:build(1, UnitId.ProtossAncestralArchives, 45, true)
end
return Tier4

View File

@ -0,0 +1,164 @@
local Basic = require("melee.protoss.build.basic")
local Openers = require("melee.protoss.build.openers")
local Tier1 = require("melee.protoss.build.tier1")
local Tier2 = require("melee.protoss.build.tier2")
local Tier3 = require("melee.protoss.build.tier3")
local Tier4 = require("melee.protoss.build.tier4")
local expansion_build_order = {
Basic.build_expansion,
Tier4.general_expansion,
}
local attack_wave_cost_bias = 0.5
return {
---@type AIPersonality
lunatic_legion = {
name = "General: Lunatic Legion",
unit_composition = {
-- signature units
[UnitId.ProtossLegionnaire] = 2,
[UnitId.ProtossSimulacrum] = 2,
-- filler units
[UnitId.ProtossEcclesiast] = 1,
[UnitId.ProtossAurora] = 1,
[UnitId.ProtossMagister] = 1,
[UnitId.ProtossDidact] = 1,
},
main_build_order = {
Openers.lunatic_legion,
Tier2.legion_stargate_gateway_library,
Tier3.legion_argosy_only,
Tier4.robotics_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
matriarchy = {
name = "General: Matriarchy",
unit_composition = {
-- signature units (Prostration Stage / Synthetic Synod)
[UnitId.ProtossZealot] = 2,
[UnitId.ProtossAmaranth] = 2,
[UnitId.ProtossVassal] = 2,
-- filler units
[UnitId.ProtossEmpress] = 1,
[UnitId.ProtossAnalogue] = 1,
[UnitId.ProtossPariah] = 1,
[UnitId.ProtossMindTyrant] = 1,
},
main_build_order = {
Openers.matriarchy,
Tier1.matriarchy,
Tier2.matriarchy_library_lattice,
Tier3.matriarchy_prostration,
Tier4.matriarchy_synthetic_synod_x2,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
robotics_fe = {
name = "General: Robotics FE",
unit_composition = {
-- signature units (Ardent Authority x2)
[UnitId.ProtossZealot] = 2,
[UnitId.ProtossVassal] = 2,
-- filler units
[UnitId.ProtossServitor] = 1,
[UnitId.ProtossPositron] = 1,
[UnitId.ProtossAccantor] = 1,
[UnitId.ProtossClarion] = 1,
},
main_build_order = {
Openers.robotics_fe,
Tier1.robotics_fe,
Tier2.robotics_ardent_x2,
Tier3.robotics_prostration,
Tier4.robotics_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
strident_striders = {
name = "General: Strident Striders",
unit_composition = {
-- signature unit
[UnitId.ProtossDracadin] = 2,
-- filler units
[UnitId.ProtossAtreus] = 1,
},
main_build_order = {
Openers.strident_striders,
Tier1.strident_striders,
Tier2.acquire,
Tier3.acquire,
Tier4.robotics_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
the_swordsmiths = {
name = "General: The Swordsmiths",
unit_composition = {
-- signature units (Ardent Authority x2)
[UnitId.ProtossZealot] = 2,
[UnitId.ProtossManifold] = 2,
-- filler units
[UnitId.ProtossServitor] = 1,
[UnitId.ProtossPositron] = 1,
[UnitId.ProtossAccantor] = 1,
[UnitId.ProtossClarion] = 1,
},
main_build_order = {
Openers.the_swordsmiths,
Tier1.the_swordsmiths,
Tier2.swordsmiths_ardent_stargate,
Tier3.acquire,
Tier4.robotics_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
worship = {
name = "General: Worship",
unit_composition = {
-- signature units
[UnitId.ProtossAmaranth] = 2,
[UnitId.ProtossSimulacrum] = 2,
-- filler units
[UnitId.ProtossGolem] = 1,
[UnitId.ProtossDracadin] = 1,
},
main_build_order = {
Openers.worship,
Tier1.worship,
Tier2.acquire,
Tier3.acquire,
Tier4.robotics_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,159 @@
local Basic = require("melee.protoss.build.basic")
local Openers = require("melee.protoss.build.openers")
local Tier1 = require("melee.protoss.build.tier1")
local Tier2 = require("melee.protoss.build.tier2")
local Tier3 = require("melee.protoss.build.tier3")
local Tier4 = require("melee.protoss.build.tier4")
local expansion_build_order = {
Basic.build_expansion,
Tier4.ranger_expansion,
}
local attack_wave_cost_bias = 0.5
return {
---@type AIPersonality
two_gate_strider = {
name = "Ranger: 2 Gate Strider",
unit_composition = {
-- signature units
[UnitId.ProtossVassal] = 2,
[UnitId.ProtossDracadin] = 2,
-- filler units
[UnitId.ProtossIdol] = 1,
[UnitId.ProtossAccantor] = 1,
},
main_build_order = {
Openers.two_gate_strider,
Tier1.two_gate_strider,
Tier2.strider_lattice_library_gateway,
Tier3.acquire,
Tier4.emissary_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
emissary_squared = {
name = "Ranger: Emissary Squared",
unit_composition = {
-- signature units (Synthetic Synod)
[UnitId.ProtossAnalogue] = 2,
[UnitId.ProtossDemiurge] = 2,
[UnitId.ProtossEmpress] = 2,
-- filler units
[UnitId.ProtossVassal] = 1,
[UnitId.ProtossGolem] = 1,
[UnitId.ProtossAccantor] = 1,
[UnitId.ProtossDracadin] = 1,
},
main_build_order = {
Openers.emissary_squared,
Tier2.emissary_ardent_lattice_gateway,
Tier3.acquire,
Tier4.emissary_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
one_gate_authority = {
name = "Ranger: 1 Gate Authority",
unit_composition = {
-- signature unit
[UnitId.ProtossManifold] = 2,
-- filler units (Prostration Stage)
[UnitId.ProtossPariah] = 1,
[UnitId.ProtossPotentate] = 1,
[UnitId.ProtossSycophant] = 1,
[UnitId.ProtossMindTyrant] = 1,
[UnitId.ProtossIdol] = 1,
[UnitId.ProtossAccantor] = 1,
},
main_build_order = {
Openers.one_gate_authority,
Tier1.one_gate_authority,
Tier2.authority_ardent_gateway_expand_lattice,
Tier3.authority_prostration_ardent2,
Tier4.authority_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
lattice_expo = {
name = "Ranger: Lattice Expo",
unit_composition = {
-- signature unit
[UnitId.ProtossSimulacrum] = 2,
-- filler units
[UnitId.ProtossIdol] = 1,
[UnitId.ProtossDracadin] = 1,
},
main_build_order = {
Openers.lattice_expo,
Tier2.expo_ardent_authority,
Tier3.acquire,
Tier4.emissary_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
simulant_swarm = {
name = "Ranger: Simulant Swarm",
unit_composition = {
-- signature unit
[UnitId.ProtossSimulacrum] = 2,
-- filler units
[UnitId.ProtossIdol] = 1,
[UnitId.ProtossEcclesiast] = 1,
},
main_build_order = {
Openers.simulant_swarm,
Tier1.simulant_swarm,
Tier2.acquire,
Tier3.acquire,
Tier4.emissary_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
the_registry = {
name = "Ranger: The Registry",
unit_composition = {
-- signature unit
[UnitId.ProtossManifold] = 2,
-- filler units
[UnitId.ProtossIdol] = 1,
[UnitId.ProtossAccantor] = 1,
},
main_build_order = {
Openers.the_registry,
Tier1.the_registry,
Tier2.acquire,
Tier3.acquire,
Tier4.emissary_synthetic_synod,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,164 @@
local Basic = require("melee.protoss.build.basic")
local Openers = require("melee.protoss.build.openers")
local Tier1 = require("melee.protoss.build.tier1")
local Tier2 = require("melee.protoss.build.tier2")
local Tier3 = require("melee.protoss.build.tier3")
local Tier4 = require("melee.protoss.build.tier4")
local expansion_build_order = {
Basic.build_expansion,
Tier4.skylord_expansion,
}
local attack_wave_cost_bias = 0.6
return {
---@type AIPersonality
solar_escort = {
name = "Skylord: Solar Escort",
unit_composition = {
-- signature units
[UnitId.ProtossSolarion] = 2,
[UnitId.ProtossLegionnaire] = 2,
[UnitId.ProtossVassal] = 2,
-- filler units
[UnitId.ProtossEcclesiast] = 1,
[UnitId.ProtossPanoptus] = 1,
[UnitId.ProtossGladius] = 1,
[UnitId.ProtossEmpyrean] = 1,
},
main_build_order = {
Openers.solar_escort,
Tier1.solar_escort,
Tier2.escort_stargate_buildup,
Tier3.escort_argosy_buildup,
Tier4.skylord_monument_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
deadly_omen = {
name = "Skylord: Deadly Omen",
unit_composition = {
-- signature units
[UnitId.ProtossZealot] = 2,
[UnitId.ProtossVassal] = 2,
-- filler units
[UnitId.ProtossPanoptus] = 1,
[UnitId.ProtossExemplar] = 1,
},
main_build_order = {
Openers.deadly_omen,
Tier1.deadly_omen,
Tier2.acquire,
Tier3.acquire,
Tier4.skylord_monument_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
two_gate_panoptus = {
name = "Skylord: 2 Gate Panoptus",
unit_composition = {
-- signature units
[UnitId.ProtossPanoptus] = 3,
[UnitId.ProtossVassal] = 2,
-- filler units
[UnitId.ProtossHierophant] = 1,
},
main_build_order = {
Openers.two_gate_panoptus,
Tier1.two_gate_panoptus,
Tier2.acquire,
Tier3.acquire,
Tier4.skylord_monument_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
exalted = {
name = "Skylord: Exalted",
unit_composition = {
-- signature units (Monument of Sin)
[UnitId.ProtossVassal] = 2,
[UnitId.ProtossStarSovereign] = 2,
[UnitId.ProtossAnthelion] = 2,
-- filler units
[UnitId.ProtossPanoptus] = 1,
[UnitId.ProtossExemplar] = 1,
[UnitId.ProtossDidact] = 1,
},
main_build_order = {
Openers.exalted,
Tier1.exalted,
Tier2.acquire,
Tier3.exalted_argosy_crucible,
Tier4.exalted_monument_of_sin,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
sorrow_fleet = {
name = "Skylord: Sorrow Fleet",
unit_composition = {
-- signature units (Monument of Sin)
[UnitId.ProtossVassal] = 2,
[UnitId.ProtossStarSovereign] = 2,
[UnitId.ProtossAnthelion] = 1.5,
-- filler units
[UnitId.ProtossPanoptus] = 1,
[UnitId.ProtossGladius] = 1,
[UnitId.ProtossEmpyrean] = 1,
},
main_build_order = {
Openers.sorrow_fleet,
Tier2.acquire,
Tier3.fleet_argosy_buildup,
Tier4.fleet_monument_of_sin,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
ships_and_swords = {
name = "Skylord: Ships and Swords",
unit_composition = {
-- signature units
[UnitId.ProtossVassal] = 2,
[UnitId.ProtossAurora] = 1.5,
[UnitId.ProtossMagister] = 1.5,
-- filler units
[UnitId.ProtossGladius] = 1,
[UnitId.ProtossStarSovereign] = 1,
},
main_build_order = {
Openers.ships_and_swords,
Tier1.ships_and_swords,
Tier2.acquire,
Tier3.acquire,
Tier4.skylord_monument_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,163 @@
local Basic = require("melee.protoss.build.basic")
local Openers = require("melee.protoss.build.openers")
local Tier1 = require("melee.protoss.build.tier1")
local Tier2 = require("melee.protoss.build.tier2")
local Tier3 = require("melee.protoss.build.tier3")
local Tier4 = require("melee.protoss.build.tier4")
local expansion_build_order = {
Basic.build_expansion,
Tier4.templar_expansion,
}
local attack_wave_cost_bias = 0.4
return {
---@type AIPersonality
three_gate_strider = {
name = "Templar: 3 Gate Strider",
unit_composition = {
-- signature unit
[UnitId.ProtossLegionnaire] = 2,
-- filler units
[UnitId.ProtossAccantor] = 1,
[UnitId.ProtossDracadin] = 1,
[UnitId.ProtossArchon] = 1,
},
main_build_order = {
Openers.three_gate_strider,
Tier1.three_gate_strider,
Tier2.strider3_library_expand,
Tier3.acquire,
Tier4.templar_archives_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
suffocating_shadow = {
name = "Templar: Suffocating Shadow",
unit_composition = {
-- signature units (Rogue Gallery)
[UnitId.ProtossVagrant] = 2,
[UnitId.ProtossCabalist] = 2,
[UnitId.ProtossCharlatan] = 2,
[UnitId.ProtossBarghest] = 2,
[UnitId.ProtossZealot] = 2,
-- filler units
[UnitId.ProtossAtreus] = 1,
[UnitId.ProtossEcclesiast] = 1,
},
main_build_order = {
Openers.suffocating_shadow,
Tier1.suffocating_shadow,
Tier2.shadow_roguegallery_expand_library,
Tier3.acquire,
Tier4.templar_archives_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
legion = {
name = "Templar: Legion",
unit_composition = {
-- signature unit
[UnitId.ProtossLegionnaire] = 2,
-- filler units
[UnitId.ProtossEcclesiast] = 1,
[UnitId.ProtossArchon] = 1,
},
main_build_order = {
Openers.legion,
Tier1.legion,
Tier2.legion_library_x2,
Tier3.acquire,
Tier4.templar_archives_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
initiates = {
name = "Templar: Initiates",
unit_composition = {
-- signature unit
[UnitId.ProtossZealot] = 2,
-- filler units
[UnitId.ProtossEcclesiast] = 1,
[UnitId.ProtossCantavis] = 1,
[UnitId.ProtossAccantor] = 1,
[UnitId.ProtossArchon] = 1,
},
main_build_order = {
Openers.initiates,
Tier1.initiates,
Tier2.acquire,
Tier3.acquire,
Tier4.templar_archives_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
martyrdom = {
name = "Templar: Martyrdom",
unit_composition = {
-- signature units (Ancestral Archives x4)
[UnitId.ProtossArchon] = 2,
[UnitId.ProtossLegionnaire] = 2,
-- filler units
[UnitId.ProtossAtreus] = 1,
[UnitId.ProtossMindTyrant] = 1,
[UnitId.ProtossOptecton] = 1,
[UnitId.ProtossPatriarch] = 1,
},
main_build_order = {
Openers.martyrdom,
Tier2.martyrdom_pylon_roguegallery,
Tier3.martyrdom_prostration_crucible,
Tier4.martyrdom_ancestral_archives_scaling,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
legions_heralds = {
name = "Templar: Legion's Heralds",
unit_composition = {
-- signature unit
[UnitId.ProtossLegionnaire] = 2,
-- filler units
[UnitId.ProtossEcclesiast] = 1,
[UnitId.ProtossAtreus] = 1,
[UnitId.ProtossHerald] = 1,
[UnitId.ProtossPatriarch] = 1,
},
main_build_order = {
Openers.legions_heralds,
Tier1.legions_heralds,
Tier2.heralds_library_gateway_buildup,
Tier3.acquire,
Tier4.templar_archives_acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,164 @@
local Basic = require("melee.terran.build.basic")
local Openers = require("melee.terran.build.openers")
local Tier1 = require("melee.terran.build.tier1")
local Tier2 = require("melee.terran.build.tier2")
local Tier3 = require("melee.terran.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier2.acquire,
Tier2.fulcrum_buildup,
Tier3.acquire,
}
local attack_wave_cost_bias = 0.6
return {
---@type AIPersonality
tank_push = {
name = "Armor: Tank Push",
unit_composition = {
-- signature units
[UnitId.TerranMatadorTankMode] = 2,
[UnitId.TerranPhalanxTankMode] = 2,
-- healers
[UnitId.TerranCleric] = 1,
-- filler units
[UnitId.TerranCyprian] = 1,
[UnitId.TerranBlackjack] = 1,
[UnitId.TerranMadrigalImperiosoMode] = 1,
},
main_build_order = {
Openers.tank_push,
Tier2.acquire,
Tier2.stockade_requeue,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
two_fulc_vulture = {
name = "Armor: 2Fulc Vulture",
unit_composition = {
-- signature unit
[UnitId.TerranVulture] = 3,
-- filler units
[UnitId.TerranMaverick] = 1,
[UnitId.TerranCyclops] = 1,
},
main_build_order = {
Openers.two_fulc_vulture,
Tier1.two_fulc_vulture,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
fe_five_fulc = {
name = "Armor: FE 5Fulc",
unit_composition = {
-- signature units
[UnitId.TerranVulture] = 2,
[UnitId.TerranBlackjack] = 2,
[UnitId.TerranPhalanxTankMode] = 2,
-- filler units
[UnitId.TerranMaverick] = 1,
[UnitId.TerranAzazel] = 1,
[UnitId.TerranSeraph] = 1,
},
main_build_order = {
Openers.fe_five_fulc,
Tier1.fe_five_fulc,
Tier2.starport_commandment_buildup,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
david_and_goliath = {
name = "Armor: David and Goliath",
unit_composition = {
-- signature units
[UnitId.TerranGoliath] = 3,
[UnitId.TerranMaverick] = 2,
-- filler units
[UnitId.TerranMatadorTankMode] = 1,
[UnitId.TerranPhalanxTankMode] = 1,
[UnitId.TerranWraith] = 1,
[UnitId.TerranBlackjack] = 1,
},
main_build_order = {
Openers.david_and_goliath,
Tier1.david_and_goliath,
Tier2.acquire,
Tier2.fulcrum_scaling,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
iron_crusade = {
name = "Armor: Iron Crusade",
unit_composition = {
-- signature units (Iron Foundry x4 is the real investment of this build)
[UnitId.TerranPaladin] = 3,
[UnitId.TerranCuirassTankMode] = 2,
[UnitId.TerranPazuzu] = 2,
-- filler units
[UnitId.TerranVulture] = 1,
[UnitId.TerranWraith] = 1,
},
main_build_order = {
Openers.expand_first,
Tier1.iron_crusade,
Tier2.acquire,
Tier2.fulcrum_buildup,
Tier3.acquire,
Tier3.iron_foundries,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
walker_power = {
name = "Armor: Walker Power",
unit_composition = {
-- signature units (walkers)
[UnitId.TerranGoliath] = 3,
[UnitId.TerranCyclops] = 2,
[UnitId.TerranBlackjack] = 2,
-- filler units
[UnitId.TerranPhalanxTankMode] = 1,
[UnitId.TerranMaverick] = 1,
},
main_build_order = {
Openers.walker_power,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,31 @@
local Basic = {}
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_expansion(melee_ai, town)
town:build(1, UnitId.TerranMinistry, 120)
town:wait_build(1, UnitId.TerranMinistry)
town:build(town.max_workers, UnitId.TerranMason, 100)
town:transfer_workers(8)
town:wait_build(10, UnitId.TerranMason)
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_ground(melee_ai, town)
for i = 1, 2 do
town:build(i, UnitId.TerranAnchor, 70 - i * 5)
wait(secs(25 + i * 5))
end
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_air(melee_ai, town)
for i = 1, 4 do
town:build(i, UnitId.TerranWatchdog, 80 - i * 5)
wait(secs(25 + i * 5))
end
end
return Basic

View File

@ -0,0 +1,416 @@
---@class TerranOpeners
local Openers = {}
-- Shared short openers (still used as-is by personalities whose own build
-- order never repositions an attack/expand trigger within this phase).
---@param melee_ai MeleeAI
---@param town Town
function Openers.expand_first(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.expand_first_slow(melee_ai, town)
melee_ai:dynamic_wait(secs(50))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.expand_first_very_slow(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.fulcrum_start_medium(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranFulcrum, 110)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.stockade_quick_start(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
end
-- Armor
---@param melee_ai MeleeAI
---@param town Town
function Openers.tank_push(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(5))
town:wait_build(1, UnitId.TerranFulcrum)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.TerranScrapyard, 90)
town:multirun(function()
wait(secs(18))
town:wait_build(2, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 89)
town:wait_build(3, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 89)
town:wait_build(4, UnitId.TerranFulcrum)
town:build(3, UnitId.TerranPalladium, 89)
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
end)
town:wait_build(1, UnitId.TerranScrapyard, false)
town:build(2, UnitId.TerranFulcrum, 90)
town:wait_build(2, UnitId.TerranFulcrum, false)
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_fulc_vulture(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(mins(1) + secs(15))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(50))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.fe_five_fulc(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(35))
town:build(1, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.david_and_goliath(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.TerranStockade, 90)
town:wait_build(2, UnitId.TerranStockade, false)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.TerranFulcrum, 90)
town:wait_build(2, UnitId.TerranFulcrum, false)
town:multirun(function()
town:wait_build(2, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 89)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 89)
melee_ai:dynamic_wait(secs(5))
town:wait_build(3, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 89)
melee_ai:dynamic_wait(secs(15))
town:wait_build(5, UnitId.TerranFulcrum)
town:build(3, UnitId.TerranPalladium, 89)
melee_ai:dynamic_wait(secs(15))
end)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.walker_power(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(35))
town:multirun(function()
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 89)
melee_ai:dynamic_wait(secs(15))
town:wait_build(2, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 89)
end)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(5))
town:wait_build(1, UnitId.TerranFulcrum)
town:wait_build(2, UnitId.TerranFulcrum, false)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(mins(1) + secs(10))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1))
end
-- Raider
---@param melee_ai MeleeAI
---@param town Town
function Openers.eight_rax(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(5, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(6, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(15))
town:wait_build(6, UnitId.TerranStockade, false)
melee_ai:dynamic_wait(mins(1) + secs(15))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_rax_bio(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(mins(1) + secs(5))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(2, UnitId.TerranStockade)
town:build(2, UnitId.TerranVestry, 89)
end)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(10))
town:wait_build(3, UnitId.TerranStockade, false)
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.fast_anvil(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.four_rax_pressure(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.holy_ackmeds(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(45))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:wait_build(4, UnitId.TerranStockade, false)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
-- Orbital
---@param melee_ai MeleeAI
---@param town Town
function Openers.rapid_starpads(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStarpad, 110)
melee_ai:dynamic_wait(secs(50))
town:build(2, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(mins(1) + secs(5))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1) + secs(15))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.wraith_bio(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(5))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.hotdrop(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(35))
town:build(2, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(45))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(35))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:wait_build(3, UnitId.TerranStarpad)
town:wait_build(2, UnitId.TerranStockade, false)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.sudden_starport(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(50))
town:wait_build(3, UnitId.TerranStockade, false)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.aces_high(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
town:wait_build(2, UnitId.TerranFulcrum, false)
town:multirun(function()
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 89)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranScrapyard, 89)
town:wait_build(3, UnitId.TerranFulcrum)
town:build(3, UnitId.TerranScrapyard, 89)
end)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_fulcrum_mech(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.doubling_up(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.TerranStockade, 90)
wait(secs(1))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(mins(1) + secs(30))
town:build(1, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 89)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranScrapyard, 89)
town:wait_build(2, UnitId.TerranStockade)
town:build(1, UnitId.TerranCovenant, 89)
melee_ai:dynamic_wait(secs(5))
town:wait_build(3, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 89)
end)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.mech_into_air(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranFulcrum, 110)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.observant(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 110)
melee_ai:dynamic_wait(secs(5))
end
return Openers

View File

@ -0,0 +1,238 @@
---@class TerranTier1
local Tier1 = {}
-- Shared expansion-order helpers (unrelated to the opener/tier1 split).
---@param melee_ai MeleeAI
---@param town Town
function Tier1.vestry_request(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranVestry, 89)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.covenant_request(melee_ai, town)
town:build(1, UnitId.TerranCovenant, 89)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
-- Armor (scale-up after the opener; unlisted personalities fold entirely
-- into their opener and skip tier1)
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_fulc_vulture(melee_ai, town)
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.TerranStockade, 90)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.fe_five_fulc(melee_ai, town)
melee_ai:dynamic_wait(secs(50))
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.david_and_goliath(melee_ai, town)
melee_ai:dynamic_wait(secs(50))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(mins(1) + secs(20))
town:wait_build(1, UnitId.TerranFulcrum)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.iron_crusade(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(mins(1))
town:wait_build(1, UnitId.TerranFulcrum)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(mins(1) + secs(20))
end
-- Raider
---@param melee_ai MeleeAI
---@param town Town
function Tier1.eight_rax(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(8, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.fast_anvil(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(40))
town:wait_build(3, UnitId.TerranStockade, false)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(2, UnitId.TerranStockade)
town:build(1, UnitId.TerranCovenant, 89)
town:wait_build(1, UnitId.TerranCovenant, false)
town:build(1, UnitId.TerranAnvil, 89)
town:wait_build(3, UnitId.TerranStockade)
town:wait_build(1, UnitId.TerranAnvil, false)
town:build(2, UnitId.TerranCovenant, 89)
town:wait_build(4, UnitId.TerranStockade)
town:build(3, UnitId.TerranCovenant, 89)
end)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.four_rax_pressure(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(2, UnitId.TerranStockade)
town:build(2, UnitId.TerranVestry, 89)
town:wait_build(3, UnitId.TerranStockade)
town:build(1, UnitId.TerranCovenant, 89)
town:wait_build(4, UnitId.TerranStockade)
town:build(2, UnitId.TerranCovenant, 89)
end)
melee_ai:dynamic_wait(secs(5))
town:wait_build(4, UnitId.TerranStockade)
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.uprising(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(4, UnitId.TerranStockade)
town:build(2, UnitId.TerranVestry, 89)
end)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:wait_build(4, UnitId.TerranStockade)
town:wait_build(2, UnitId.TerranVestry)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
-- Orbital
---@param melee_ai MeleeAI
---@param town Town
function Tier1.rapid_starpads(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:wait_build(2, UnitId.TerranStarpad)
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(mins(2) + secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.orbital_stockade_settle(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(mins(1))
town:wait_build(1, UnitId.TerranStockade)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.wraith_bio(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:wait_build(3, UnitId.TerranStockade, false)
town:build(1, UnitId.TerranVestry, 89)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.aces_high(melee_ai, town)
melee_ai:dynamic_wait(secs(50))
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(mins(1) + secs(20))
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_fulcrum_mech(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:build(3, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.general_stockade_fulcrum_vestry_palladium_starpad(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.TerranFulcrum, 90)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 89)
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 89)
end)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.general_stockade_starpad(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStockade, 90)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.doubling_up(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
town:build(1, UnitId.TerranStarpad, 90)
melee_ai:dynamic_wait(secs(25))
end
return Tier1

View File

@ -0,0 +1,322 @@
---@class TerranTier2
local Tier2 = {}
--- Requests Atlas and waits for it. The generic tier-2 tech transition.
---@param melee_ai MeleeAI
---@param town Town
function Tier2.acquire(melee_ai, town)
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.TerranAtlas, 70, true)
end)
end
--- Requests a bare Starport once Atlas exists, for base air units.
---@param melee_ai MeleeAI
---@param town Town
function Tier2.starport(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.TerranStarport, 69, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.stockade_requeue(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.TerranStockade, 70)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.starport_commandment_buildup(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 69)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 69)
town:wait_build(3, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 69)
wait(secs(2))
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranCommandment, 69)
end)
melee_ai:dynamic_wait(secs(25))
town:wait_build(2, UnitId.TerranFulcrum, false)
melee_ai:dynamic_wait(secs(70) + secs(25))
town:build(5, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(mins(2) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.fulcrum_scaling(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(4, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(25))
town:build(5, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.fulcrum_buildup(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(3, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(5))
town:wait_build(2, UnitId.TerranFulcrum)
town:wait_build(3, UnitId.TerranFulcrum, false)
wait(secs(1))
town:wait_build(1, UnitId.TerranAtlas)
melee_ai:dynamic_wait(secs(20))
town:build(4, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(20))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.raider_vestry_covenant_buildup(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 69)
town:wait_build(2, UnitId.TerranStockade)
town:build(2, UnitId.TerranVestry, 69)
town:wait_build(3, UnitId.TerranStockade)
town:build(1, UnitId.TerranCovenant, 69)
town:wait_build(4, UnitId.TerranStockade)
town:build(2, UnitId.TerranCovenant, 69)
end)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.raider_stockade_requeue(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.TerranStockade, 70)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.raider_vestry_starport_rotary(melee_ai, town)
melee_ai:dynamic_wait(secs(5))
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 69)
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
end)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.orbital_starport_rotary_commandment(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
town:build(2, UnitId.TerranStarport, 70)
town:wait_build(2, UnitId.TerranStarport)
town:build(2, UnitId.TerranRotary, 69)
town:build(3, UnitId.TerranStarport, 70)
town:wait_build(3, UnitId.TerranStarport)
town:build(1, UnitId.TerranCommandment, 69)
end)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.orbital_covenant_anvil_starport_rotary(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:multirun(function()
town:build(1, UnitId.TerranCovenant, 69)
town:wait_build(1, UnitId.TerranCovenant)
town:build(1, UnitId.TerranAnvil, 69)
wait(secs(1))
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
town:build(2, UnitId.TerranStarport, 70)
town:wait_build(2, UnitId.TerranStarport)
town:build(2, UnitId.TerranRotary, 69)
melee_ai:dynamic_wait(secs(50))
end)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.orbital_vestry_starport_rotary_x4(melee_ai, town)
town:multirun(function()
town:wait_build(2, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 69)
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport, false)
town:build(4, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
town:wait_build(2, UnitId.TerranStarport)
town:build(2, UnitId.TerranRotary, 69)
town:wait_build(3, UnitId.TerranStarport)
town:build(3, UnitId.TerranRotary, 69)
town:wait_build(4, UnitId.TerranStarport)
town:build(4, UnitId.TerranRotary, 69)
melee_ai:dynamic_wait(secs(15))
end)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_expansion_tail(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.TerranStarpad, 69, true)
melee_ai:dynamic_wait(secs(15))
Tier2.acquire(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranStockade, 69, true)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.TerranCovenant, 69, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.TerranStarport, 69, true)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:multirun(function()
wait(secs(1))
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranDaedala, 65, true)
end)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_scrapyard_palladium_mantle(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 69)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranScrapyard, 69)
town:wait_build(3, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 69)
town:wait_build(4, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 69)
end)
melee_ai:dynamic_wait(secs(40) + secs(40))
town:build(2, UnitId.TerranMantle, 70)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_vestry_palladium_starport_rotary(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 69)
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 69)
town:wait_build(1, UnitId.TerranAtlas)
wait(secs(1))
town:build(1, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
end)
melee_ai:dynamic_wait(secs(40) + secs(25) + secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_fulcrum_follow_up(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_mega_tech_buildup(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 69)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranScrapyard, 69)
town:build(1, UnitId.TerranQuarry, 69)
town:wait_build(3, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranPalladium, 69)
town:wait_build(4, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 69)
town:wait_build(1, UnitId.TerranAtlas)
town:build(2, UnitId.TerranStarport, 70)
town:wait_build(1, UnitId.TerranStarport)
town:build(1, UnitId.TerranRotary, 69)
town:wait_build(2, UnitId.TerranStarport)
town:build(2, UnitId.TerranRotary, 69)
town:wait_build(5, UnitId.TerranFulcrum)
town:build(3, UnitId.TerranPalladium, 69)
town:wait_build(6, UnitId.TerranFulcrum)
town:build(4, UnitId.TerranPalladium, 69)
town:wait_build(3, UnitId.TerranStarport)
town:build(3, UnitId.TerranRotary, 69)
town:wait_build(4, UnitId.TerranStarport)
town:build(4, UnitId.TerranRotary, 69)
town:wait_build(5, UnitId.TerranStarport)
town:build(5, UnitId.TerranRotary, 69)
town:wait_build(1, UnitId.TerranDaedala)
town:wait_build(6, UnitId.TerranStarport)
town:build(1, UnitId.TerranCommandment, 69)
town:wait_build(7, UnitId.TerranStarport)
town:build(2, UnitId.TerranCommandment, 69)
town:wait_build(8, UnitId.TerranStarport)
town:build(3, UnitId.TerranCommandment, 69)
town:build(2, UnitId.TerranNaniteAssembly, 69)
town:wait_build(1, UnitId.TerranNaniteAssembly)
town:build(1, UnitId.TerranHadron, 69)
end)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(1, UnitId.TerranStarpad, 70)
melee_ai:dynamic_wait(secs(15) + mins(1) + secs(15))
town:build(6, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.general_vestry_scrapyard_palladium_starport_commandment(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.TerranStockade)
town:build(1, UnitId.TerranVestry, 69)
town:wait_build(1, UnitId.TerranFulcrum)
town:build(1, UnitId.TerranScrapyard, 69)
town:wait_build(2, UnitId.TerranFulcrum)
town:build(2, UnitId.TerranPalladium, 69)
town:wait_build(3, UnitId.TerranFulcrum)
wait(secs(1))
town:wait_build(1, UnitId.TerranAtlas)
town:build(2, UnitId.TerranStarport, 70)
town:wait_build(2, UnitId.TerranStarport)
town:build(2, UnitId.TerranCommandment, 69)
town:wait_build(1, UnitId.TerranCommandment)
melee_ai:dynamic_wait(secs(15))
end)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(5) + secs(35))
town:build(1, UnitId.TerranStarpad, 70)
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(25) + secs(50))
Tier2.acquire(melee_ai, town)
town:build(3, UnitId.TerranFulcrum, 70)
melee_ai:dynamic_wait(secs(50))
end
return Tier2

View File

@ -0,0 +1,49 @@
---@class TerranTier3
local Tier3 = {}
--- Requests Daedala and waits for it. The generic tier-3 tech transition.
---@param melee_ai MeleeAI
---@param town Town
function Tier3.acquire(melee_ai, town)
town:multirun(function()
wait(secs(1))
town:wait_build(1, UnitId.TerranAtlas)
town:build(1, UnitId.TerranDaedala, 65, true)
end)
wait(secs(1))
town:wait_build(1, UnitId.TerranDaedala)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.iron_foundries(melee_ai, town)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.TerranIronFoundry, 50)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.TerranIronFoundry, 50)
melee_ai:dynamic_wait(secs(5))
town:build(3, UnitId.TerranIronFoundry, 50)
melee_ai:dynamic_wait(secs(5))
town:build(4, UnitId.TerranIronFoundry, 50)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.nanite_assembly_buildup(melee_ai, town)
town:build(2, UnitId.TerranNaniteAssembly, 50)
melee_ai:dynamic_wait(secs(20))
town:build(3, UnitId.TerranNaniteAssembly, 50)
melee_ai:dynamic_wait(secs(20))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.general_starport_scaling(melee_ai, town)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(4, UnitId.TerranStarport, 50)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(8, UnitId.TerranStarport, 50)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
return Tier3

View File

@ -0,0 +1,161 @@
local Basic = require("melee.terran.build.basic")
local Openers = require("melee.terran.build.openers")
local Tier1 = require("melee.terran.build.tier1")
local Tier2 = require("melee.terran.build.tier2")
local Tier3 = require("melee.terran.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier2.general_expansion_tail,
}
local attack_wave_cost_bias = 0.5
return {
---@type AIPersonality
two_fulcrum_mech = {
name = "General: 2 Fulcrum Mech",
unit_composition = {
-- signature units (Mantle x2)
[UnitId.TerranPhalanxTankMode] = 2,
[UnitId.TerranRamesses] = 2,
[UnitId.TerranDurendal] = 2,
[UnitId.TerranCataphract] = 2,
-- filler units
[UnitId.TerranBlackjack] = 1,
[UnitId.TerranMadrigalImperiosoMode] = 1,
},
main_build_order = {
Openers.two_fulcrum_mech,
Tier1.two_fulcrum_mech,
Tier2.acquire,
Tier2.general_scrapyard_palladium_mantle,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
early_riser = {
name = "General: Early Riser",
unit_composition = {
-- signature unit
[UnitId.TerranMaverick] = 2,
-- healers
[UnitId.TerranCleric] = 1,
-- filler units
[UnitId.TerranCyclops] = 1,
},
main_build_order = {
Openers.expand_first_very_slow,
Tier1.general_stockade_fulcrum_vestry_palladium_starpad,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
the_cube = {
name = "General: The Cube",
unit_composition = {
-- signature unit
[UnitId.TerranMaverick] = 2,
-- filler units
[UnitId.TerranVulture] = 1,
[UnitId.TerranPhalanxTankMode] = 1,
[UnitId.TerranSalamander] = 1,
},
main_build_order = {
Openers.fulcrum_start_medium,
Tier1.general_stockade_starpad,
Tier2.general_vestry_palladium_starport_rotary,
Tier2.acquire,
Tier2.general_fulcrum_follow_up,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
doubling_up = {
name = "General: Doubling Up",
unit_composition = {
-- signature units
[UnitId.TerranMaverick] = 2,
[UnitId.TerranGoliath] = 2,
-- healers
[UnitId.TerranShamanSupportMode] = 1,
-- filler units
[UnitId.TerranCyclops] = 1,
[UnitId.TerranBlackjack] = 1,
},
main_build_order = {
Openers.doubling_up,
Tier1.doubling_up,
Tier2.acquire,
Tier2.general_fulcrum_follow_up,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
mech_into_air = {
name = "General: Mech into Air",
unit_composition = {
-- signature units (the mech-into-air transition)
[UnitId.TerranPhalanxTankMode] = 2,
[UnitId.TerranMinotaur] = 2,
-- filler units
[UnitId.TerranMatadorTankMode] = 1,
[UnitId.TerranSalamander] = 1,
[UnitId.TerranSeraph] = 1,
[UnitId.TerranHippogriff] = 1,
[UnitId.TerranPhobos] = 1,
},
main_build_order = {
Openers.mech_into_air,
Tier2.acquire,
Tier2.general_mega_tech_buildup,
Tier3.acquire,
Tier3.general_starport_scaling,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
observant = {
name = "General: Observant",
unit_composition = {
-- signature unit
[UnitId.TerranCyprian] = 2,
-- filler units
[UnitId.TerranSeraph] = 1,
[UnitId.TerranBlackjack] = 1,
},
main_build_order = {
Openers.observant,
Tier2.general_vestry_scrapyard_palladium_starport_commandment,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,153 @@
local Basic = require("melee.terran.build.basic")
local Openers = require("melee.terran.build.openers")
local Tier1 = require("melee.terran.build.tier1")
local Tier2 = require("melee.terran.build.tier2")
local Tier3 = require("melee.terran.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier2.acquire,
Tier3.acquire,
Tier2.starport,
}
local attack_wave_cost_bias = 0.6
return {
---@type AIPersonality
rapid_starpads = {
name = "Orbital: 2 Starpad Wraith",
unit_composition = {
-- signature unit
[UnitId.TerranWraith] = 3,
-- filler units
[UnitId.TerranSalamander] = 1,
[UnitId.TerranAzazel] = 1,
[UnitId.TerranSeraph] = 1,
},
main_build_order = {
Openers.rapid_starpads,
Tier1.rapid_starpads,
Tier2.acquire,
Tier2.orbital_starport_rotary_commandment,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
the_don = {
name = "Orbital: The Don",
unit_composition = {
-- signature units (Nanite Assembly x3)
[UnitId.TerranMinotaur] = 2,
[UnitId.TerranCentaur] = 2,
[UnitId.TerranMagnetar] = 2,
-- filler units
[UnitId.TerranGorgon] = 1,
},
main_build_order = {
Openers.expand_first_slow,
Tier1.orbital_stockade_settle,
Tier2.acquire,
Tier3.acquire,
Tier3.nanite_assembly_buildup,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
wraith_bio = {
name = "Orbital: Wraith Bio",
unit_composition = {
-- signature units
[UnitId.TerranMaverick] = 3,
[UnitId.TerranWraith] = 3,
-- filler units
[UnitId.TerranValkyrie] = 1,
},
main_build_order = {
Openers.wraith_bio,
Tier1.wraith_bio,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
hotdrop = {
name = "Orbital: Hotdrop",
unit_composition = {
-- signature unit
[UnitId.TerranHarakan] = 3,
-- filler units
[UnitId.TerranWraith] = 1,
[UnitId.TerranSalamander] = 1,
},
main_build_order = {
Openers.hotdrop,
Tier2.acquire,
Tier2.orbital_covenant_anvil_starport_rotary,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
sudden_starport = {
name = "Orbital: Sudden Starport",
unit_composition = {
-- signature units
[UnitId.TerranMaverick] = 2,
[UnitId.TerranSundog] = 2,
[UnitId.TerranSalamander] = 2,
-- healers
[UnitId.TerranCleric] = 1.5,
},
main_build_order = {
Openers.sudden_starport,
Tier2.orbital_vestry_starport_rotary_x4,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
aces_high = {
name = "Orbital: Aces High",
unit_composition = {
-- signature units
[UnitId.TerranVulture] = 3,
[UnitId.TerranWraith] = 3,
-- filler units
[UnitId.TerranBlackjack] = 1,
[UnitId.TerranCyclops] = 1,
},
main_build_order = {
Openers.aces_high,
Tier1.aces_high,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,153 @@
local Basic = require("melee.terran.build.basic")
local Openers = require("melee.terran.build.openers")
local Tier1 = require("melee.terran.build.tier1")
local Tier2 = require("melee.terran.build.tier2")
local Tier3 = require("melee.terran.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier1.vestry_request,
Tier2.acquire,
Tier1.covenant_request,
Tier3.acquire,
}
local attack_wave_cost_bias = 0.4
return {
---@type AIPersonality
eight_rax = {
name = "Raider: 8Rax",
unit_composition = {
-- signature unit
[UnitId.TerranMaverick] = 3,
-- healers
[UnitId.TerranCleric] = 1.5,
-- filler units
[UnitId.TerranHarakan] = 1,
[UnitId.TerranCyprian] = 1,
},
main_build_order = {
Openers.eight_rax,
Tier1.eight_rax,
Tier2.acquire,
Tier2.raider_vestry_covenant_buildup,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
three_rax_bio = {
name = "Raider: 3Rax Maverick & Cleric",
unit_composition = {
-- signature units
[UnitId.TerranMaverick] = 3,
-- healers
[UnitId.TerranCleric] = 2,
-- filler units
[UnitId.TerranEidolon] = 1,
},
main_build_order = {
Openers.three_rax_bio,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
fast_anvil = {
name = "Raider: Fast Anvil",
unit_composition = {
-- signature units
[UnitId.TerranCyprian] = 2,
[UnitId.TerranEidolon] = 2,
-- filler units
[UnitId.TerranMaverick] = 1,
},
main_build_order = {
Openers.fast_anvil,
Tier1.fast_anvil,
Tier2.acquire,
Tier2.raider_stockade_requeue,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
four_rax_pressure = {
name = "Raider: 4Rax Pressure",
unit_composition = {
-- healers
[UnitId.TerranShamanSupportMode] = 2,
-- filler units
[UnitId.TerranSavantEntropyGauntlets] = 1,
[UnitId.TerranCyprian] = 1,
},
main_build_order = {
Openers.four_rax_pressure,
Tier1.four_rax_pressure,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
uprising = {
name = "Raider: Uprising",
unit_composition = {
-- signature units
[UnitId.TerranCyprian] = 3,
-- healers
[UnitId.TerranCleric] = 1.5,
-- filler units
[UnitId.TerranEidolon] = 1,
},
main_build_order = {
Openers.stockade_quick_start,
Tier1.uprising,
Tier2.acquire,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
holy_ackmeds = {
name = "Raider: Holy Ackmeds!",
unit_composition = {
-- signature unit
[UnitId.TerranHarakan] = 3,
-- filler units
[UnitId.TerranSundog] = 1,
[UnitId.TerranSalamander] = 1,
},
main_build_order = {
Openers.holy_ackmeds,
Tier2.acquire,
Tier2.raider_vestry_starport_rotary,
Tier3.acquire,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,177 @@
local Basic = require("melee.zerg.build.basic")
local Openers = require("melee.zerg.build.openers")
local Tier1 = require("melee.zerg.build.tier1")
local Tier2 = require("melee.zerg.build.tier2")
local Tier3 = require("melee.zerg.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier1.saturated_production,
Tier3.assault_expansion,
}
local attack_wave_cost_bias = 0.6
return {
---@type AIPersonality
three_base_hydrith = {
name = "Assault: 3 Base Hydrith",
unit_composition = {
-- signature units
[UnitId.ZergHydralisk] = 3,
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergUltrokor] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
},
main_build_order = {
Openers.three_base_hydrith,
Tier1.three_base_hydrith,
Tier2.hydrith_iroliris_hachirosk_zorkiz,
Tier3.hydrith_othstol_hachirosk,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
two_base_zorkiz = {
name = "Assault: 2 Base Hydrith-Zorkiz",
unit_composition = {
-- signature units
[UnitId.ZergHydralisk] = 3,
[UnitId.ZergUltrokor] = 2,
[UnitId.ZergZoryusthaleth] = 2,
-- filler units
[UnitId.ZergIzirokor] = 1,
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergSovroleth] = 1,
},
main_build_order = {
Openers.two_base_zorkiz,
Tier1.two_base_zorkiz,
Tier2.zorkiz_iroliris_tethvith,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
two_base_hydrith_quazeth = {
name = "Assault: 2 Base Hydrith-Quazeth",
unit_composition = {
-- signature units
[UnitId.ZergHydralisk] = 3,
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergUltrokor] = 2,
-- filler units
[UnitId.ZergQuazilisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.two_base_hydrith_quazeth,
Tier1.two_base_hydrith_quazeth,
Tier2.hydrith_quazeth_iroliris_vornath_kalkiir,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
two_base_alkag = {
name = "Assault: 2 Base Alkag",
unit_composition = {
-- signature units (Alkag Iteth)
[UnitId.ZergKagralisk] = 2,
[UnitId.ZergEvigrilisk] = 2,
[UnitId.ZergAlmaksalisk] = 2,
-- filler units
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
},
main_build_order = {
Openers.two_base_alkag,
Tier2.alkag_iroliris_hachirosk,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
potency = {
name = "Assault: Potency",
unit_composition = {
-- signature units
[UnitId.ZergZoryusthaleth] = 2,
[UnitId.ZergHydralisk] = 2,
[UnitId.ZergKalkalisk] = 2,
-- filler units
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergKagralisk] = 1,
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergEvigrilisk] = 1,
[UnitId.ZergAlmaksalisk] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
},
main_build_order = {
Openers.hydrith_den_start,
Tier1.hachirosk_zorkiz_skibact_quazeth_scaleup,
Tier2.potency_iroliris_hachirosk_alkag,
Tier3.potency_othstol_settle,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
earthen_tides = {
name = "Assault: Earthen Tides",
unit_composition = {
-- signature units
[UnitId.ZergIzirokor] = 2,
[UnitId.ZergLakizilisk] = 2,
[UnitId.ZergProtathalor] = 2,
-- filler units
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergKeskathalor] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.hydrith_den_start,
Tier1.hachirosk_zorkiz_skibact_quazeth_scaleup,
Tier2.earthen_iroliris_hachirosk_vornath_mutath_matravil,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,31 @@
local Basic = {}
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_expansion(melee_ai, town)
town:build(1, UnitId.ZergHachirosk, 120)
town:wait_build(1, UnitId.ZergHachirosk)
town:build(town.max_workers, UnitId.ZergDroleth, 100)
town:transfer_workers(8)
town:wait_build(10, UnitId.ZergDroleth)
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_ground(melee_ai, town)
for i = 1, 3 do
town:build(i, UnitId.ZergSurkithCesiant, 80 - i * 5)
wait(secs(25 + i * 5))
end
end
---@param melee_ai MeleeAI
---@param town Town
function Basic.build_anti_air(melee_ai, town)
for i = 1, 3 do
town:build(i, UnitId.ZergSpraithCesiant, 80 - i * 5)
wait(secs(25 + i * 5))
end
end
return Basic

View File

@ -0,0 +1,298 @@
---@class ZergOpeners
local Openers = {}
-- Shared opener still used unmodified by a personality whose own build order
-- never repositions an attack/expand trigger within this phase.
---@param melee_ai MeleeAI
---@param town Town
function Openers.quazeth_pool_check_then_expand(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergQuazethPool, 110, true)
town:wait_build(1, UnitId.ZergQuazethPool, false)
melee_ai:start_expanding()
end
-- Assault
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_base_hydrith(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergHydrithDen, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_base_zorkiz(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergHydrithDen, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_base_hydrith_quazeth(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.two_base_alkag(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 90, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergAlkagIteth, 90, true)
town:build(1, UnitId.ZergOthstolOviform, 90, true)
end)
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
end
--- Shared by Potency and Earthen Tides (identical build up to this point).
---@param melee_ai MeleeAI
---@param town Town
function Openers.hydrith_den_start(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergHydrithDen, 110, true)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
end
-- Swarm
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_base_flood(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergQuazethPool, 110, true)
melee_ai:dynamic_wait(secs(50))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(15))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_base_ultrav(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(50))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.instant_quazeth(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergQuazethPool, 110, true)
melee_ai:dynamic_wait(secs(5))
town:wait_build(1, UnitId.ZergQuazethPool, false)
melee_ai:dynamic_wait(secs(5))
town:build(2, UnitId.ZergHachirosk, 115)
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:start_attacking()
wait(secs(4))
town:build(4, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(mins(1) + secs(20))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.three_base_othstol(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(55))
town:build(1, UnitId.ZergQuazethPool, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.gates_of_hell(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(55))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.primitive_pressure(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
end
-- Sunblot
---@param melee_ai MeleeAI
---@param town Town
function Openers.big_and_little(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.the_triad(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergQuazethPool, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.skittering_duo(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(50))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.early_birds(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(25))
town:build(1, UnitId.ZergQuazethPool, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.flying_feint(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergQuazethPool, 110, true)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(secs(30))
melee_ai:start_expanding()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.descending_horizon(melee_ai, town)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergQuazethPool, 110, true)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_expanding()
melee_ai:dynamic_wait(mins(1) + secs(50))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(25))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(1))
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Openers.sleeping_giant(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergQuazethPool, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.shrieking_swarm(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.subjugation(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergDroleth, 100)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergHydrithDen, 90, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.before_the_storm(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergQuazethPool, 110, true)
town:wait_build(1, UnitId.ZergQuazethPool, false)
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergDroleth, 100)
melee_ai:dynamic_wait(secs(50))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(secs(15))
melee_ai:start_attacking()
melee_ai:dynamic_wait(mins(2) + secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Openers.time_after_time(melee_ai, town)
melee_ai:dynamic_wait(secs(45))
melee_ai:start_expanding()
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(40))
melee_ai:start_attacking()
end
return Openers

View File

@ -0,0 +1,229 @@
---@class ZergTier1
local Tier1 = {}
--- Saturated hatchery/larva production ramp, reused by every category's main build order.
---@param melee_ai MeleeAI
---@param town Town
function Tier1.saturated_production(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.ZergIrolIris)
town:build(5, UnitId.ZergLarvoskCesiant, 90)
melee_ai:dynamic_wait(secs(45))
town:build(6, UnitId.ZergHachirosk, 90)
melee_ai:dynamic_wait(secs(45))
town:build(6, UnitId.ZergCaphrolosk, 90)
town:wait_build(1, UnitId.ZergOthstolOviform)
melee_ai:dynamic_wait(secs(45))
town:build(6, UnitId.ZergGathtelosk, 50)
end)
end
-- Assault (scale-up after the opener; unlisted personalities fold entirely
-- into their opener and skip tier1)
---@param melee_ai MeleeAI
---@param town Town
function Tier1.three_base_hydrith(melee_ai, town)
melee_ai:dynamic_wait(mins(1) + secs(30))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_base_zorkiz(melee_ai, town)
melee_ai:dynamic_wait(secs(55))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.two_base_hydrith_quazeth(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(50))
end
--- Shared by Potency and Earthen Tides (identical build up to this point).
---@param melee_ai MeleeAI
---@param town Town
function Tier1.hachirosk_zorkiz_skibact_quazeth_scaleup(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(30))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(50))
end
-- Swarm
---@param melee_ai MeleeAI
---@param town Town
function Tier1.three_base_flood(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.instant_quazeth(melee_ai, town)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(55))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(mins(2) + secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.three_base_othstol(melee_ai, town)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.gates_of_hell(melee_ai, town)
melee_ai:dynamic_wait(secs(55))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(20))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.primitive_pressure(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(30))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(secs(50))
end
-- Sunblot
---@param melee_ai MeleeAI
---@param town Town
function Tier1.big_and_little(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(55))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.the_triad(melee_ai, town)
melee_ai:dynamic_wait(mins(1) + secs(10))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(45))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
melee_ai:dynamic_wait(mins(1))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.skittering_duo(melee_ai, town)
melee_ai:dynamic_wait(secs(75))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(mins(2) + secs(20))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.early_birds(melee_ai, town)
melee_ai:dynamic_wait(secs(55))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.flying_feint(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(40))
end
-- General
---@param melee_ai MeleeAI
---@param town Town
function Tier1.sleeping_giant(melee_ai, town)
melee_ai:dynamic_wait(mins(1))
town:wait_build(15, UnitId.ZergDroleth)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergVornathPond, 90, true)
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.shrieking_swarm(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.subjugation(melee_ai, town)
melee_ai:dynamic_wait(secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.time_after_time(melee_ai, town)
melee_ai:dynamic_wait(secs(75))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(secs(75))
town:build(1, UnitId.ZergSkibactScab, 90, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergZorkizShroud, 90, true)
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier1.coil_buildup(melee_ai, town)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergDroleth, 100)
wait(secs(1))
town:build(1, UnitId.ZergQuazethPool, 90, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergHydrithDen, 90, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergVornathPond, 90, true)
melee_ai:dynamic_wait(mins(1))
end
return Tier1

View File

@ -0,0 +1,512 @@
---@class ZergTier2
local Tier2 = {}
---@param melee_ai MeleeAI
---@param town Town
function Tier2.hydrith_iroliris_hachirosk_zorkiz(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.zorkiz_iroliris_tethvith(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(20) + secs(15))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(secs(45))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.hydrith_quazeth_iroliris_vornath_kalkiir(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
end)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.alkag_iroliris_hachirosk(melee_ai, town)
melee_ai:dynamic_wait(secs(55))
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(mins(1) + secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.potency_iroliris_hachirosk_alkag(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergAlkagIteth, 70, true)
end)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(mins(1) + secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.earthen_iroliris_hachirosk_vornath_mutath_matravil(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergMatravilNest, 70, true)
end)
melee_ai:dynamic_wait(mins(2) + secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.flood_vornath_kalkiir_mutath_tosvrol_then_iroliris_caphrolosk_hachirosk(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
town:wait_build(3, UnitId.ZergHachirosk, false)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergMutathSpire, 70, true)
town:wait_build(1, UnitId.ZergMutathSpire)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
end)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(mins(1) + secs(20))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.ultrav_iroliris_caphrolosk_buildup(melee_ai, town)
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(55))
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergZorkizShroud)
town:wait_build(2, UnitId.ZergHachirosk, false)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(secs(20))
town:wait_build(1, UnitId.ZergHydrithDen)
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergMatravilNest, 70, true)
town:wait_build(1, UnitId.ZergOthstolOviform)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergGathtelosk, 50)
end)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(45))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergHydrithDen, 70, true)
melee_ai:dynamic_wait(secs(50))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.instant_iroliris_vornath_kalkiir(melee_ai, town)
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(3) + secs(20))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
end)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.othstol_iroliris_buildup(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:wait_build(1, UnitId.ZergHydrithDen)
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:wait_build(1, UnitId.ZergZorkizShroud)
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergZozarcGorge, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergMatravilNest, 70, true)
town:wait_build(1, UnitId.ZergOthstolOviform)
town:build(1, UnitId.ZergGathtelosk, 50)
town:wait_build(1, UnitId.ZergGathtelosk, false)
town:wait_build(2, UnitId.ZergHachirosk, false)
end)
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergHydrithDen, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(5))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.gates_buildup_vornath_hachirosk_iroliris(melee_ai, town)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(20))
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(20))
town:wait_build(1, UnitId.ZergZorkizShroud)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergZozarcGorge, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(mins(8) + secs(20))
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergOthstolOviform, 65, true)
end)
end)
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.pressure_iroliris_vornath_skibact_tethvith(melee_ai, town)
town:multirun(function()
wait(secs(1))
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
end)
melee_ai:dynamic_wait(secs(40))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.big_little_iroliris_buildup_caphrolosk_hachirosk(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
wait(secs(1))
town:multirun(function()
wait(secs(1))
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergZorkizShroud)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergMutathSpire, 70, true)
town:wait_build(1, UnitId.ZergMutathSpire)
melee_ai:dynamic_wait(mins(3) + secs(20))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
end)
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(mins(1) + secs(45))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.birds_iroliris_alkag(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergAlkagIteth, 70, true)
end)
melee_ai:dynamic_wait(mins(1) + secs(30))
town:build(2, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(mins(1) + secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.feint_iroliris_buildup_vornath_hydrith_skibact(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(3) + secs(20))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergMutathSpire, 70, true)
end)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergHydrithDen, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergSkibactScab, 70, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.horizon_iroliris_buildup(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
town:multirun(function()
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ZergQuazethPool)
town:build(1, UnitId.ZergVornathPond, 70, true)
town:wait_build(1, UnitId.ZergVornathPond)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(25))
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergMutathSpire, 70, true)
town:wait_build(1, UnitId.ZergMutathSpire)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergMatravilNest, 70, true)
end)
melee_ai:dynamic_wait(mins(2))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.giant_iroliris_caphrolosk_hachirosk_zorkiz_buildup(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(15) + secs(5))
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(secs(30))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergMatravilNest, 70, true)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(mins(2) + secs(45))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
end)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.swarm_iroliris_vornathkalkiir_zorkiz_caphrolosk_hachirosk(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
end)
melee_ai:dynamic_wait(mins(1))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergCaphrolosk, 70)
melee_ai:dynamic_wait(secs(30))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.subjugation_quazeth_iroliris_buildup(melee_ai, town)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergQuazethPool, 70, true)
melee_ai:dynamic_wait(secs(25))
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(50))
town:multirun(function()
town:wait_build(1, UnitId.ZergHydrithDen)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergSkibactScab, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
town:wait_build(1, UnitId.ZergIrolIris)
town:build(1, UnitId.ZergMatravilNest, 70, true)
end)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergCaphrolosk, 70)
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(45))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.storm_iroliris_hachirosk_vornathkalkiir(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(15))
town:build(3, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
town:build(4, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(30))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
end)
melee_ai:dynamic_wait(secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.time_iroliris_vornathkalkiir(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(15))
town:multirun(function()
town:wait_build(1, UnitId.ZergQuazethPool)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergVornathPond, 70, true)
melee_ai:dynamic_wait(mins(1) + secs(40))
town:build(1, UnitId.ZergKalkiirLake, 70, true)
end)
melee_ai:dynamic_wait(secs(30))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier2.coil_iroliris_hachirosk(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergIrolIris, 70, true)
end)
melee_ai:dynamic_wait(secs(30) + mins(1) + secs(5))
town:build(2, UnitId.ZergHachirosk, 115)
end
return Tier2

View File

@ -0,0 +1,168 @@
---@class ZergTier3
local Tier3 = {}
--- Requests Othstol Oviform and waits for it. The generic tier-3 tech
--- transition, for personalities without bespoke tier-3 content.
---@param melee_ai MeleeAI
---@param town Town
function Tier3.acquire(melee_ai, town)
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.hydrith_othstol_hachirosk(melee_ai, town)
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(25))
town:build(4, UnitId.ZergHachirosk, 115)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.potency_othstol_settle(melee_ai, town)
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.horizon_othstol(melee_ai, town)
town:multirun(function()
melee_ai:dynamic_wait(mins(1) + secs(15))
town:build(1, UnitId.ZergOthstolOviform, 65, true)
end)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.giant_othstol_gathtelosk(melee_ai, town)
town:multirun(function()
town:build(1, UnitId.ZergOthstolOviform, 65, true)
end)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergGathtelosk, 50)
melee_ai:dynamic_wait(secs(15))
end
-- Category expansion tails: reuse the same shared node-acquisition pattern per
-- category, run once a new town is expanded into.
---@param melee_ai MeleeAI
---@param town Town
function Tier3.assault_expansion(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergCaphrolosk, 70)
town:build(1, UnitId.ZergQuazethPool, 70, true)
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergHachirosk, 70)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergHydrithDen, 70, true)
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergMatravilNest, 70, true)
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergGathtelosk, 50)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergAlmakisAntre, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergGeszkathGrotto, 50, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.swarm_expansion(melee_ai, town)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergIrolIris, 70, true)
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergMatravilNest, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergHydrithDen, 70, true)
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergOthstolOviform, 65, true)
town:build(1, UnitId.ZergGathtelosk, 50)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergAlmakisAntre, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergGeszkathGrotto, 50, true)
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.sunblot_expansion(melee_ai, town)
town:build(1, UnitId.ZergIrolIris, 70, true)
town:build(1, UnitId.ZergQuazethPool, 70, true)
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergHydrithDen, 70, true)
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
town:build(1, UnitId.ZergKalkiirLake, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergMatravilNest, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergAlmakisAntre, 70, true)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergGathtelosk, 50)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergGeszkathGrotto, 50, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(secs(25))
end
---@param melee_ai MeleeAI
---@param town Town
function Tier3.general_expansion(melee_ai, town)
town:build(1, UnitId.ZergHydrithDen, 70, true)
melee_ai:dynamic_wait(secs(20))
town:build(1, UnitId.ZergQuazethPool, 70, true)
town:build(1, UnitId.ZergKalkiirLake, 70, true)
town:build(1, UnitId.ZergIrolIris, 70, true)
melee_ai:dynamic_wait(secs(5))
town:build(1, UnitId.ZergMutathSpire, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergZorkizShroud, 70, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergTethvithSwamp, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergOthstolOviform, 65, true)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergGathtelosk, 50)
melee_ai:dynamic_wait(secs(15))
town:build(1, UnitId.ZergUlgorgCavern, 70, true)
melee_ai:dynamic_wait(secs(40))
town:build(1, UnitId.ZergTosvrolHaunt, 70, true)
melee_ai:dynamic_wait(secs(25))
town:build(1, UnitId.ZergMatravilNest, 70, true)
town:build(1, UnitId.ZergAlmakisAntre, 70, true)
melee_ai:dynamic_wait(secs(50))
town:build(1, UnitId.ZergGeszkathGrotto, 50, true)
end
return Tier3

View File

@ -0,0 +1,176 @@
local Basic = require("melee.zerg.build.basic")
local Openers = require("melee.zerg.build.openers")
local Tier1 = require("melee.zerg.build.tier1")
local Tier2 = require("melee.zerg.build.tier2")
local Tier3 = require("melee.zerg.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier1.saturated_production,
Tier3.general_expansion,
}
local attack_wave_cost_bias = 0.5
return {
---@type AIPersonality
sleeping_giant = {
name = "General: Sleeping Giant",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergUltrokor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.sleeping_giant,
Tier1.sleeping_giant,
Tier2.giant_iroliris_caphrolosk_hachirosk_zorkiz_buildup,
Tier3.giant_othstol_gathtelosk,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
shrieking_swarm = {
name = "General: Shrieking Swarm",
unit_composition = {
-- signature units (Vornath Pond)
[UnitId.ZergVorvrokor] = 2,
[UnitId.ZergNathrokor] = 2,
-- filler units
[UnitId.ZergUltrokor] = 1,
[UnitId.ZergHydralisk] = 1,
[UnitId.ZergLakizilisk] = 1,
[UnitId.ZergSovroleth] = 1,
},
main_build_order = {
Openers.shrieking_swarm,
Tier1.shrieking_swarm,
Tier2.swarm_iroliris_vornathkalkiir_zorkiz_caphrolosk_hachirosk,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
subjugation = {
name = "General: Subjugation",
unit_composition = {
-- signature units (Skibact Scab)
[UnitId.ZergSkithrokor] = 2,
[UnitId.ZergBactalisk] = 2,
[UnitId.ZergZethrokor] = 2,
-- filler units
[UnitId.ZergKeskathalor] = 1,
[UnitId.ZergHydralisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergVithrilisk] = 1,
},
main_build_order = {
Openers.subjugation,
Tier1.subjugation,
Tier2.subjugation_quazeth_iroliris_buildup,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
before_the_storm = {
name = "General: Before the Storm",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergKalkalisk] = 2,
-- filler units
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.before_the_storm,
Tier2.storm_iroliris_hachirosk_vornathkalkiir,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
time_after_time = {
name = "General: Time After Time",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergSkithrokor] = 2,
[UnitId.ZergLakizilisk] = 2,
-- filler units
[UnitId.ZergLiiralisk] = 1,
[UnitId.ZergCikralisk] = 1,
[UnitId.ZergBactalisk] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.time_after_time,
Tier1.time_after_time,
Tier2.time_iroliris_vornathkalkiir,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
constrained_coil = {
name = "General: Constrained Coil",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergKalkalisk] = 2,
-- filler units
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.quazeth_pool_check_then_expand,
Tier1.coil_buildup,
Tier2.coil_iroliris_hachirosk,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,174 @@
local Basic = require("melee.zerg.build.basic")
local Openers = require("melee.zerg.build.openers")
local Tier1 = require("melee.zerg.build.tier1")
local Tier2 = require("melee.zerg.build.tier2")
local Tier3 = require("melee.zerg.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier1.saturated_production,
Tier3.sunblot_expansion,
}
local attack_wave_cost_bias = 0.6
return {
---@type AIPersonality
big_and_little = {
name = "Sunblot: Big and Little",
unit_composition = {
-- signature units
[UnitId.ZergNathrokor] = 2,
[UnitId.ZergMutalisk] = 2,
[UnitId.ZergGorgrokor] = 2,
-- filler units
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergVilgorokor] = 1,
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.big_and_little,
Tier1.big_and_little,
Tier2.big_little_iroliris_buildup_caphrolosk_hachirosk,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
the_triad = {
name = "Sunblot: The Triad",
unit_composition = {
-- signature units
[UnitId.ZergNathrokor] = 2,
[UnitId.ZergGorgrokor] = 2,
[UnitId.ZergKalkalisk] = 2,
-- filler units
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergIsthrathaleth] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.the_triad,
Tier1.the_triad,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
skittering_duo = {
name = "Sunblot: Skittering Duo",
unit_composition = {
-- signature units (Skibact Scab)
[UnitId.ZergSkithrokor] = 2,
[UnitId.ZergBactalisk] = 2,
[UnitId.ZergNathrokor] = 2,
-- filler units
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.skittering_duo,
Tier1.skittering_duo,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
early_birds = {
name = "Sunblot: Early Birds",
unit_composition = {
-- signature units (Alkag Iteth)
[UnitId.ZergKagralisk] = 2,
[UnitId.ZergEvigrilisk] = 2,
[UnitId.ZergAlmaksalisk] = 2,
-- filler units
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.early_birds,
Tier1.early_birds,
Tier2.birds_iroliris_alkag,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
flying_feint = {
name = "Sunblot: Flying Feint",
unit_composition = {
-- signature units
[UnitId.ZergNathrokor] = 2,
[UnitId.ZergSkithrokor] = 2,
-- filler units
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergBactalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.flying_feint,
Tier1.flying_feint,
Tier2.feint_iroliris_buildup_vornath_hydrith_skibact,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
descending_horizon = {
name = "Sunblot: Descending Horizon",
unit_composition = {
-- signature units (Skibact Scab)
[UnitId.ZergSkithrokor] = 2,
[UnitId.ZergBactalisk] = 2,
-- filler units
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergGeszithalor] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.descending_horizon,
Tier2.horizon_iroliris_buildup,
Tier3.horizon_othstol,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,185 @@
local Basic = require("melee.zerg.build.basic")
local Openers = require("melee.zerg.build.openers")
local Tier1 = require("melee.zerg.build.tier1")
local Tier2 = require("melee.zerg.build.tier2")
local Tier3 = require("melee.zerg.build.tier3")
local expansion_build_order = {
Basic.build_expansion,
Tier1.saturated_production,
Tier3.swarm_expansion,
}
local attack_wave_cost_bias = 0.4
return {
---@type AIPersonality
three_base_flood = {
name = "Swarm: 3 Base Flood",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergUltrokor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.three_base_flood,
Tier1.three_base_flood,
Tier2.flood_vornath_kalkiir_mutath_tosvrol_then_iroliris_caphrolosk_hachirosk,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
three_base_ultrav = {
name = "Swarm: 3 Base Ultrav",
unit_composition = {
-- signature units
[UnitId.ZergUltrokor] = 3,
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergMutalisk] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
},
main_build_order = {
Openers.three_base_ultrav,
Tier2.ultrav_iroliris_caphrolosk_buildup,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
instant_quazeth = {
name = "Swarm: Instant Quazeth",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 3,
[UnitId.ZergUltrokor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergQuazilisk] = 1,
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergNathrokor] = 1,
[UnitId.ZergVorvrokor] = 1,
},
main_build_order = {
Openers.instant_quazeth,
Tier1.instant_quazeth,
Tier2.instant_iroliris_vornath_kalkiir,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
three_base_othstol = {
name = "Swarm: 3 Base Othstol",
unit_composition = {
-- signature units
[UnitId.ZergIzirokor] = 2,
[UnitId.ZergUltrokor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergZoryusthaleth] = 1,
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
[UnitId.ZergZobriolisk] = 1,
[UnitId.ZergZarcavrokor] = 1,
},
main_build_order = {
Openers.three_base_othstol,
Tier1.three_base_othstol,
Tier2.othstol_iroliris_buildup,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
gates_of_hell = {
name = "Swarm: Gates of Hell",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergKeskathalor] = 2,
[UnitId.ZergHydralisk] = 2,
-- filler units
[UnitId.ZergKalkalisk] = 1,
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergGorgrokor] = 1,
[UnitId.ZergSkithrokor] = 1,
[UnitId.ZergBactalisk] = 1,
},
main_build_order = {
Openers.gates_of_hell,
Tier1.gates_of_hell,
Tier2.gates_buildup_vornath_hachirosk_iroliris,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
---@type AIPersonality
primitive_pressure = {
name = "Swarm: Primitive Pressure",
unit_composition = {
-- signature units
[UnitId.ZergZethrokor] = 2,
[UnitId.ZergHydralisk] = 2,
[UnitId.ZergZoryusthaleth] = 2,
-- filler units
[UnitId.ZergLiiralisk] = 1,
[UnitId.ZergProtathalor] = 1,
[UnitId.ZergMatraleth] = 1,
[UnitId.ZergSovroleth] = 1,
[UnitId.ZergKeskathalor] = 1,
},
main_build_order = {
Openers.primitive_pressure,
Tier1.primitive_pressure,
Tier2.pressure_iroliris_vornath_skibact_tethvith,
Tier3.acquire,
Tier1.saturated_production,
},
expansion_build_order = expansion_build_order,
build_anti_ground_defenses = Basic.build_anti_ground,
build_anti_air_defenses = Basic.build_anti_air,
attack_wave_cost_bias = attack_wave_cost_bias,
},
}

View File

@ -0,0 +1,10 @@
require "melee.melee_ai"
local player = Player()
if player.type == PlayerType.Computer then
if player.race == RaceId.Askosi then
cprint(TextColor.Orange, "Player " .. player.name .. "(P" .. player.id .. "): Askosi AI is not supported!")
return
end
MeleeAI():run()
end

Binary file not shown.

View File

@ -10,6 +10,13 @@
- **Triggers:**
- New custom trigger subcommand for **set_unit_stat**
- **transport_capacity** (b. Lucifirius) -- enables transporting via any unit, modifies transport capacity on any transport
- **User-defined resource areas:** (c. DF)
- Our methodology for user-defined resource areas has changed from the previous system!
- **The location-based system has been removed.**
- To use the new system:
- Select the resource nodes you wish to assign to a specific resarea
- Tick **burrowed** (bottom-left-most status flag) in the unit property panel
- Set the **HP** field to the intended resarea id (a number between 1 and 255)
## Bugfixes
- **Gameplay:**

Binary file not shown.