--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function gadget:GetInfo()
return {
name = "BA Chicken Spawner",
desc = "Spawns burrows and chickens",
author = "TheFatController/quantum",
date = "16 November, 2008",
license = "GNU GPL, v2 or later",
layer = 0,
enabled = true -- loaded by default?
}
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
if (gadgetHandler:IsSyncedCode()) then
-- BEGIN SYNCED
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Speed-ups
--
local GetUnitNeutral = Spring.GetUnitNeutral
local GetTeamList = Spring.GetTeamList
local GetTeamLuaAI = Spring.GetTeamLuaAI
local GetGaiaTeamID = Spring.GetGaiaTeamID
local SetGameRulesParam = Spring.SetGameRulesParam
local GetGameRulesParam = Spring.GetGameRulesParam
local GetTeamUnitsCounts = Spring.GetTeamUnitsCounts
local GetTeamUnitCount = Spring.GetTeamUnitCount
local GetGameSpeed = Spring.GetGameSpeed
local GetGameFrame = Spring.GetGameFrame
local GetPlayerList = Spring.GetPlayerList
local GetPlayerInfo = Spring.GetPlayerInfo
local GetGameSeconds = Spring.GetGameSeconds
local DestroyUnit = Spring.DestroyUnit
local GetTeamUnits = Spring.GetTeamUnits
local GetUnitsInCylinder = Spring.GetUnitsInCylinder
local GetUnitNearestEnemy = Spring.GetUnitNearestEnemy
local GetUnitPosition = Spring.GetUnitPosition
local GiveOrderToUnit = Spring.GiveOrderToUnit
local TestBuildOrder = Spring.TestBuildOrder
local GetGroundBlocked = Spring.GetGroundBlocked
local CreateUnit = Spring.CreateUnit
local SetUnitBlocking = Spring.SetUnitBlocking
local GetGroundHeight = Spring.GetGroundHeight
local GetUnitTeam = Spring.GetUnitTeam
local GetUnitHealth = Spring.GetUnitHealth
local GetUnitCommands = Spring.GetUnitCommands
local SetUnitExperience = Spring.SetUnitExperience
local GetUnitDefID = Spring.GetUnitDefID
local SetUnitHealth = Spring.SetUnitHealth
local GetUnitIsDead = Spring.GetUnitIsDead
local GetCommandQueue = Spring.GetCommandQueue
local GetUnitDirection = Spring.GetUnitDirection
local mRandom = math.random
local math = math
local Game = Game
local table = table
local ipairs = ipairs
local pairs = pairs
local MAPSIZEX = Game.mapSizeX
local MAPSIZEZ = Game.mapSizeZ
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local queenLifePercent = 100
local maxTries = 100
local currentWave = 1
local targetCache = 1
local minBurrows = 1
local timeOfLastSpawn = 0
local timeOfLastFakeSpawn = 0
local highTeam = 0
local timeOfLastWave = 0
local expMod = 0
local burrowTarget = 0
local qDamage = 0
local lastTeamID = 0
local targetCacheCount = 0
local nextSquadSize = 0
local chickenCount = 0
local t = 0
local timeCounter = 0
local queenAnger = 0
local bonusPoints = 0
local burrowSpawnProgress = 0
local queenMaxHP = 0
local chickenDebtCount = 0
local firstSpawn = true
local showScoresOnce = true
local gameOver = false
local qMove = false
local warningMessage = false
local computerTeams = {}
local humanTeams = {}
local disabledUnits = {}
local spawnQueue = {}
local idleOrderQueue = {}
local stunList = {}
local endScores = {}
local queenResistance = {}
local queenID
local chickenTeamID
local luaAI
local lsx1,lsz1,lsx2,lsz2
local turrets = {}
local chickenBirths = {}
local failChickens = {}
local chickenTargets = {}
local burrows = {}
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Teams
--
local VERYEASY = "Chicken: Very Easy"
local EASY = "Chicken: Easy"
local NORMAL = "Chicken: Normal"
local HARD = "Chicken: Hard"
local VERYHARD = "Chicken: Very Hard"
local CUSTOM = "Chicken: Custom"
local SURVIVAL = "Chicken: Survival"
local modes = {
[1] = VERYEASY,
[2] = EASY,
[3] = NORMAL,
[4] = HARD,
[5] = VERYHARD,
[6] = CUSTOM,
[7] = SURVIVAL,
}
for i, v in ipairs(modes) do -- make it bi-directional
modes[v] = i
end
local teams = GetTeamList()
local highestLevel = 0
for _, teamID in ipairs(teams) do
local teamLuaAI = GetTeamLuaAI(teamID)
if (teamLuaAI and teamLuaAI ~= "") then
luaAI = teamLuaAI
if (modes[teamLuaAI] > highestLevel) then
highestLevel = modes[teamLuaAI]
end
chickenTeamID = teamID
computerTeams[teamID] = true
else
humanTeams[teamID] = true
end
end
if (modes[highestLevel] == SURVIVAL) then
return false
end
luaAI = modes[highestLevel]
local gaiaTeamID = GetGaiaTeamID()
if not chickenTeamID then
chickenTeamID = gaiaTeamID
warningMessage = true
else
computerTeams[gaiaTeamID] = nil
end
humanTeams[gaiaTeamID] = nil
if (gameMode and luaAI == 0) then
return false
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Utility
--
local function SetToList(set)
local list = {}
for k in pairs(set) do
table.insert(list, k)
end
return list
end
local function SetCount(set)
local count = 0
for k in pairs(set) do
count = count + 1
end
return count
end
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
local function getSqrDistance(x1,z1,x2,z2)
local dx,dz = x1-x2,z1-z2
return (dx*dx)+(dz*dz)
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Difficulty
--
do -- load config file
local CONFIG_FILE = "LuaRules/Configs/spawn_defs.lua"
local VFSMODE = VFS.RAW_FIRST
local s = assert(VFS.LoadFile(CONFIG_FILE, VFSMODE))
local chunk = assert(loadstring(s, file))
setfenv(chunk, gadget)
chunk()
end
local function SetGlobals(difficulty)
for key, value in pairs(gadget.difficulties[difficulty]) do
gadget[key] = value
end
gadget.difficulties = nil
end
SetGlobals(luaAI or "Chicken: Custom") -- set difficulty
local expIncrement = ((SetCount(humanTeams) * expStep) / queenTime)
if expStep < 0 then expIncrement = ((expStep * -1) / queenTime) end
local nextWave = ((queenTime / 8) / 60)
local gracePenalty = math.max(math.floor(((gracePeriod - 220) / burrowSpawnRate) + 0.5), 0)
chickensPerPlayer = (chickensPerPlayer * SetCount(humanTeams))
maxBurrows = maxBurrows + math.floor(SetCount(humanTeams) * 1.334)
queenTime = (queenTime + gracePeriod)
chickenDebtCount = math.ceil((math.max((gracePeriod - 220),0) / 3))
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Game Rules
--
local UPDATE = 10
local unitCounts = {}
local chickenDefTypes = {}
for unitName in pairs(chickenTypes) do
Spring.Echo("unit names with resulting id")
Spring.Echo(unitName)
Spring.Echo(UnitDefNames[unitName].id)
chickenDefTypes[UnitDefNames[unitName].id] = unitName
unitCounts[string.sub(unitName,1,-2)] = {count = 0, lastCount = 0}
end
local defendersDefs = {}
for unitName in pairs(defenders) do
defendersDefs[UnitDefNames[unitName].id] = unitName
end
local function SetupUnit(unitName)
SetGameRulesParam(unitName.."Count", 0)
SetGameRulesParam(unitName.."Kills", 0)
end
SetGameRulesParam("queenTime", queenTime)
SetGameRulesParam("queenLife", queenLifePercent)
SetGameRulesParam("queenAnger", queenAnger)
SetGameRulesParam("gracePeriod", gracePeriod)
for unitName in pairs(chickenTypes) do
SetupUnit(string.sub(unitName,1,-2))
end
for unitName in pairs(defenders) do
SetupUnit(string.sub(unitName,1,-2))
end
SetupUnit(burrowName)
local difficulty = modes[luaAI] or 6
SetGameRulesParam("difficulty", difficulty)
local function UpdateUnitCount()
local teamUnitCounts = GetTeamUnitsCounts(chickenTeamID)
local total = 0
for shortName in pairs(unitCounts) do
unitCounts[shortName].count = 0
end
for unitDefID, number in pairs(teamUnitCounts) do
if UnitDefs[unitDefID] then
local shortName = string.sub(UnitDefs[unitDefID].name,1,-2)
if unitCounts[shortName] then
unitCounts[shortName].count = unitCounts[shortName].count + number
end
end
end
for shortName, counts in pairs(unitCounts) do
if (counts.count ~= counts.lastCount) then
SetGameRulesParam(shortName.."Count", counts.count)
counts.lastCount = counts.count
end
total = total + counts.count
end
return total
end
local EMP_GOO = {}
EMP_GOO[WeaponDefNames['chickenr1_goolauncher'].id] = WeaponDefNames['chickenr1_goolauncher'].damages[1]
EMP_GOO[WeaponDefNames['weaver_death'].id] = WeaponDefNames['weaver_death'].damages[1]
local LOBBER = UnitDefNames["chickenr1"].id
local SKIRMISH = { [UnitDefNames["chickens1"].id] = 270, [UnitDefNames["chickens2"].id] = 620 }
local COWARD = { [UnitDefNames["chickenh1"].id] = 150, [UnitDefNames["chickenr1"].id] = 300 }
local JUNO = WeaponDefNames["juno_pulse"].id
local KROW_ID = UnitDefNames["corcrw"].id
local KROW_LASER = "krow_laser_index"
local SMALL_UNIT = UnitDefNames["corllt"].id
local MEDIUM_UNIT = UnitDefNames["armwin"].id
local LARGE_UNIT = UnitDefNames["armsolar"].id
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Clean up
--
local function KillOldChicken()
for unitID, birthDate in pairs(chickenBirths) do
local age = (t - birthDate)
if (age > maxAge) then
if (unitID ~= queenID) then
DestroyUnit(unitID)
chickenCount = chickenCount - 1
chickenDebtCount = chickenDebtCount + 1
end
end
end
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Game End Stuff
--
local function KillAllChicken()
local chickenUnits = GetTeamUnits(chickenTeamID)
for _, unitID in pairs(chickenUnits) do
if disabledUnits[unitID] then
DestroyUnit(unitID, false, true)
else
DestroyUnit(unitID, true)
end
end
end
local function KillAllComputerUnits()
for teamID in pairs(computerTeams) do
local teamUnits = GetTeamUnits(teamID)
for _, unitID in pairs(teamUnits) do
if disabledUnits[unitID] then
DestroyUnit(unitID, false, true)
else
DestroyUnit(unitID, true)
end
end
end
end
local function CalculateScores()
local totalDamage = 0
local teamScores = {}
local totalScore = bonusPoints
for teamID in pairs(humanTeams) do
local foo = Spring.GetTeamStatsHistory(teamID,0,t)
teamScores[teamID] = foo[#foo].damageDealt
totalDamage = totalDamage + teamScores[teamID]
local teamUnits = GetTeamUnits(teamID)
for _, unitID in pairs(teamUnits) do
local defID = GetUnitDefID(unitID)
if defID then
totalScore = totalScore + UnitDefs[defID].metalCost
totalScore = totalScore + (UnitDefs[defID].energyCost / 50)
end
end
end
for teamID in pairs(humanTeams) do
teamScores[teamID] = (teamScores[teamID] / totalDamage)
teamScores[teamID] = math.ceil((totalScore * teamScores[teamID]))
if (teamID > highTeam) then highTeam = teamID end
end
highTeam = (highTeam + 1)
return teamScores
end
local function DisplayScores(scoreList)
Spring.Echo(" ")
Spring.Echo("Team Scores")
Spring.Echo("-----------")
for i = 0,highTeam,1 do
if scoreList[i] then
local teamID = i
local teamScore = scoreList[i]
local playerList = Spring.GetPlayerList(teamID, false)
if playerList[1] then
Spring.SendMessage("Team" .. teamID .. " (<PLAYER" .. playerList[1] .. ">) - " .. comma_value(teamScore) .. " Points")
else
Spring.Echo("Team" .. teamID .. " (AI) - " .. comma_value(teamScore) .. " Points")
end
end
end
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Spawn Dynamics
--
local function addChickenTarget(chickenID, targetID)
if (not targetID) or (GetUnitTeam(targetID) == chickenTeamID) or (GetUnitTeam(chickenID) ~= chickenTeamID) then return end
--debug--Spring.Echo(t .. " addChickenTarget " .. chickenID .. "," .. targetID)
if chickenTargets[chickenID] and chickenTargets[chickenTargets[chickenID]] then
chickenTargets[chickenTargets[chickenID]][chickenID] = nil
end
if (chickenTargets[targetID] == nil) then
chickenTargets[targetID] = {}
chickenTargets[targetID][chickenID] = targetID
chickenTargets[chickenID] = targetID
else
chickenTargets[targetID][chickenID] = targetID
chickenTargets[chickenID] = targetID
end
end
local function AttackNearestEnemy(unitID, unitDefID, unitTeam)
local targetID = GetUnitNearestEnemy(unitID)
if (targetID) and (not GetUnitIsDead(targetID)) and (not GetUnitNeutral(targetID)) then
--debug--Spring.Echo(t .. " AttackNearestEnemy " .. unitID .. " -> " .. targetID)
local defID = GetUnitDefID(targetID)
if UnitDefs[defID] and (UnitDefs[defID].speed > 75) then
return false
end
local x,y,z = GetUnitPosition(targetID)
idleOrderQueue[unitID] = {cmd = CMD.FIGHT, params = {x,y,z}, opts = {}}
addChickenTarget(unitID, targetID)
return true
else
return false
end
end
local function ChooseTarget()
local humanTeamList = SetToList(humanTeams)
if (#humanTeamList == 0) then
return {mRandom(1,MAPSIZEX-1),0,mRandom(1,MAPSIZEZ-1)}
end
if (targetCacheCount >= nextSquadSize) or GetUnitIsDead(targetCache) then
local tries = 0
repeat
local teamID = humanTeamList[mRandom(#humanTeamList)]
if (teamID == lastTeamID) then teamID = humanTeamList[mRandom(#humanTeamList)] end
lastTeamID = teamID
local units = GetTeamUnits(teamID)
if units[2] then
targetCache = units[mRandom(1,#units)]
else
targetCache = units[1]
end
local slowunit = true
if tries < 5 then
local defID = GetUnitDefID(targetCache)
if UnitDefs[defID] and (UnitDefs[defID].speed > 75) then
slowunit = false
end
end
tries = (tries + 1)
until ((not GetUnitIsDead(targetCache)) and (not GetUnitNeutral(targetCache)) and slowunit) or (tries > maxTries)
targetCacheCount = 0
nextSquadSize = 6 + mRandom(0,4)
else
targetCacheCount = targetCacheCount + 1
end
--debug--Spring.Echo(t .. " ChooseTarget " .. targetCache)
return {GetUnitPosition(targetCache)}
end
local function getChickenSpawnLoc(burrowID, size)
local x, y, z
local bx, by, bz = GetUnitPosition(burrowID)
if (not bx or not bz) then
return false
end
local tries = 0
local s = spawnSquare
repeat
x = mRandom(bx - s, bx + s)
z = mRandom(bz - s, bz + s)
s = s + spawnSquareIncrement
tries = tries + 1
if (x >= MAPSIZEX) then x = (MAPSIZEX - mRandom(1,40)) elseif (x <= 0) then x = mRandom(1,40) end
if (z >= MAPSIZEZ) then z = (MAPSIZEZ - mRandom(1,40)) elseif (z <= 0) then z = mRandom(1,40) end
until ((TestBuildOrder(size, x, by, z, 1) == 2) and (not GetGroundBlocked(x, z))) or (tries > maxTries)
y = GetGroundHeight(x,z)
return x, y, z
end
local function SpawnTurret(burrowID, turret)
if (mRandom() > defenderChance) or (not turret) or (burrows[burrowID] >= maxTurrets) then
return
end
local x, y, z
local bx, by, bz = GetUnitPosition(burrowID)
local tries = 0
local s = spawnSquare
repeat
x = mRandom(bx - s, bx + s)
z = mRandom(bz - s, bz + s)
s = s + spawnSquareIncrement
tries = tries + 1
if (x >= MAPSIZEX) then x = (MAPSIZEX - mRandom(1,40)) elseif (x <= 0) then x = mRandom(1,40) end
if (z >= MAPSIZEZ) then z = (MAPSIZEZ - mRandom(1,40)) elseif (z <= 0) then z = mRandom(1,40) end
until (not GetGroundBlocked(x, z) or tries > maxTries)
y = GetGroundHeight(x,z)
local unitID = CreateUnit(turret, x, y, z, "n", chickenTeamID)
idleOrderQueue[unitID] = {cmd = CMD.GUARD, params ={burrowID}, opts = {}}
SetUnitBlocking(unitID, false)
turrets[unitID] = {burrowID, t}
burrows[burrowID] = burrows[burrowID] + 1
end
local function SpawnBurrow(number)
if (queenID) then
return
end
local unitDefID = UnitDefNames[burrowName].id
for i=1, (number or 1) do
local x, z
local tries = 0
repeat
if (burrowSpawnType == "initialbox") then
x = mRandom(lsx1, lsx2)
z = mRandom(lsz1, lsz2)
elseif ((burrowSpawnType == "alwaysbox") and (tries < (maxTries / 2))) then
x = mRandom(lsx1, lsx2)
z = mRandom(lsz1, lsz2)
else
x = mRandom(spawnSquare, MAPSIZEX - spawnSquare)
z = mRandom(spawnSquare, MAPSIZEZ - spawnSquare)
end
local y = GetGroundHeight(x, z)
tries = tries + 1
local blocking = TestBuildOrder(MEDIUM_UNIT, x, y, z, 1)
if (blocking == 2) and (burrowSpawnType == "avoid") then
local proximity = GetUnitsInCylinder(x, z, minBaseDistance)
local vicinity = GetUnitsInCylinder(x, z, maxBaseDistance)
local humanUnitsInVicinity = false
local humanUnitsInProximity = false
for i=1,vicinity['n'],1 do
if (GetUnitTeam(vicinity[i]) ~= chickenTeamID) then
humanUnitsInVicinity = true
break
end
end
for i=1,proximity['n'],1 do
if (GetUnitTeam(proximity[i]) ~= chickenTeamID) then
humanUnitsInProximity = true
break
end
end
if (humanUnitsInProximity or not humanUnitsInVicinity) then
blocking = 1
end
end
until (blocking == 2 or tries > maxTries)
local unitID = CreateUnit(burrowName, x, 0, z, "n", chickenTeamID)
burrows[unitID] = 0
SetUnitBlocking(unitID, false)
end
end
local function updateQueenLife()
if not queenID then return end
local curH, maxH = GetUnitHealth(queenID)
local lifeCheck = math.ceil(((curH/maxH)*100)-0.5)
if queenLifePercent ~= lifeCheck then
queenLifePercent = lifeCheck
SetGameRulesParam("queenLife", queenLifePercent)
end
end
local function stunUnit(unitID, seconds)
seconds = (seconds * 30)
if stunList[unitID] then seconds = math.max(stunList[unitID], seconds) end
stunList[unitID] = seconds
SetUnitHealth(unitID, {paralyze=99999999})
end
local function SpawnQueen()
local x, y, z
local tries = 0
repeat
x = mRandom(1, (MAPSIZEX-1))
z = mRandom(1, (MAPSIZEZ-1))
y = GetGroundHeight(x, z)
tries = tries + 1
local blocking = TestBuildOrder(LARGE_UNIT, x, y, z, 1)
local proximity = GetUnitsInCylinder(x, z, minBaseDistance)
local vicinity = GetUnitsInCylinder(x, z, maxBaseDistance)
local humanUnitsInVicinity = false
local humanUnitsInProximity = false
for i=1,vicinity['n'],1 do
if (GetUnitTeam(vicinity[i]) ~= chickenTeamID) then
humanUnitsInVicinity = true
break
end
end
for i=1,proximity['n'],1 do
if (GetUnitTeam(proximity[i]) ~= chickenTeamID) then
humanUnitsInProximity = true
break
end
end
if (humanUnitsInProximity or not humanUnitsInVicinity) then
blocking = 1
end
until (blocking == 2 or tries > maxTries)
return CreateUnit(queenName, x, y, z, "n", chickenTeamID)
end
local function Wave()
--debug--Spring.Echo(t .. "Wave()")
if gameOver then return end
currentWave = math.min(math.ceil((((t-gracePeriod+queenAnger) / 60) / nextWave)), 8)
if currentWave > #waves then currentWave = #waves end
if (currentWave == 8) then
COWARD[UnitDefNames["chickenc1"].id] = (150 + mRandom(1,150))
COWARD[UnitDefNames["chicken2"].id] = (200 + mRandom(1,200))
end
local cCount = 0
if queenID then
if queenSpawnMult > 0 then
for i = 1,queenSpawnMult,1 do
local squad = waves[9][mRandom(1,#waves[9])]
for i,sString in pairs(squad) do
local nEnd,_ = string.find(sString, " ")
local unitNumber = string.sub(sString,1,(nEnd-1))
local chickenName = string.sub(sString,(nEnd+1))
for i = 1,unitNumber,1 do
table.insert(spawnQueue, {burrow = queenID, unitName = chickenName, team = chickenTeamID})
end
cCount = cCount + unitNumber
end
end
end
return cCount
end
for burrowID in pairs(burrows) do
if (t > (queenTime * 0.33)) then SpawnTurret(burrowID, bonusTurret) end
local squad = waves[currentWave][mRandom(1,#waves[currentWave])]
for i,sString in pairs(squad) do
local skipSpawn = false
if (cCount > chickensPerPlayer) and (mRandom() > spawnChance) then skipSpawn = true end
if skipSpawn and (chickenDebtCount > 0) and (mRandom() > 0.5) then
chickenDebtCount = (chickenDebtCount - 1)
skipSpawn = false
end
if not skipSpawn then
local nEnd,_ = string.find(sString, " ")
local unitNumber = string.sub(sString,1,(nEnd-1))
local chickenName = string.sub(sString,(nEnd+1))
for i = 1,unitNumber,1 do
table.insert(spawnQueue, {burrow = burrowID, unitName = chickenName, team = chickenTeamID})
end
cCount = cCount + unitNumber
end
end
end
return cCount
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Get rid of the AI
--
local function DisableUnit(unitID)
Spring.MoveCtrl.Enable(unitID)
Spring.MoveCtrl.SetNoBlocking(unitID, true)
Spring.MoveCtrl.SetPosition(unitID, Game.mapSizeX, 10000, Game.mapSizeZ)
Spring.SetUnitCloak(unitID, true)
Spring.SetUnitHealth(unitID, {paralyze=99999999})
Spring.SetUnitNoDraw(unitID, true)
Spring.SetUnitStealth(unitID, true)
Spring.SetUnitNoSelect(unitID, true)
Spring.SetUnitNoMinimap(unitID, true)
disabledUnits[unitID] = true
end
local function DisableComputerUnits()
for teamID in pairs(computerTeams) do
local teamUnits = GetTeamUnits(teamID)
for _, unitID in ipairs(teamUnits) do
DisableUnit(unitID)
end
end
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Call-ins
--
function gadget:UnitMoveFailed(unitID, unitDefID, unitTeam)
if gameOver then return end
if (unitTeam ~= chickenTeamID) or (unitID == queenID) or idleOrderQueue[unitID] then return end
local px,py,pz = GetUnitPosition(unitID)
if failChickens[unitID] then
if (getSqrDistance(failChickens[unitID].x,failChickens[unitID].z,px,pz) < 5000) then
failChickens[unitID].count = failChickens[unitID].count + 1
if (failChickens[unitID].count > 3) then
DestroyUnit(unitID, false, true)
chickenDebtCount = chickenDebtCount + 1
chickenCount = chickenCount - 1
return
end
else
failChickens[unitID].x = px
failChickens[unitID].z = pz
end
else
failChickens[unitID] = { x = px, z = pz, count = 1 }
end
if (mRandom(1,2) == 1) then px = px + mRandom(50,100) else px = px - mRandom(50,100) end
if (mRandom(1,2) == 1) then pz = pz + mRandom(50,100) else pz = pz - mRandom(50,100) end
idleOrderQueue[unitID] = {cmd = CMD.MOVE, params = {px,py,pz}, opts = {}}
end
function gadget:UnitIdle(unitID, unitDefID, unitTeam)
if gameOver then return end
if (unitTeam == chickenTeamID) and (chickenDefTypes[unitDefID] or (unitID == queenID)) then
--debug--Spring.Echo(t .. " unitIdle " .. unitID)
if not AttackNearestEnemy(unitID, unitDefID, unitTeam) then
local chickenParams = ChooseTarget()
if targetCache and (unitID ~= queenID) and (mRandom(1,10) == 5) then
idleOrderQueue[unitID] = {cmd = CMD.ATTACK, params = {targetCache}, opts = {}}
else
idleOrderQueue[unitID] = {cmd = CMD.FIGHT, params = chickenParams, opts = {}}
end
if targetCache then
if GetUnitNeutral(targetCache) then
idleOrderQueue[unitID] = {cmd = CMD.ATTACK, params = {targetCache}, opts = {}}
end
addChickenTarget(unitID, targetCache)
end
end
end
end
function gadget:UnitCreated(unitID, unitDefID, unitTeam)
if chickenTargets[unitID] then
chickenTargets[unitID] = nil
end
end
function gadget:UnitPreDamaged(unitID, unitDefID, unitTeam, damage, paralyzer,
weaponID, attackerID, attackerDefID, attackerTeam)
if (attackerTeam == chickenTeamID) then
return (damage * damageMod)
end
if (unitID == queenID) then
if (weaponID == -1) and (damage > 25000) then
return 25000
end
if attackerDefID then
if (attackerDefID == KROW_ID) then
weaponID = KROW_LASER
end
if not queenResistance[weaponID] then
queenResistance[weaponID] = {}
queenResistance[weaponID].damage = damage
queenResistance[weaponID].notify = 0
end
local resistPercent = (math.min(queenResistance[weaponID].damage/queenMaxHP, 0.75) + 0.2)
if resistPercent > 0.35 then
if queenResistance[weaponID].notify == 0 then
local weaponName
if (attackerDefID == KROW_ID) then
weaponName = "HighEnergyLaser"
else
weaponName = WeaponDefs[weaponID].description
end
Spring.Echo("Queen is becoming resistant to " .. UnitDefs[attackerDefID].humanName .. "'s attacks (" .. weaponName .. ")")
queenResistance[weaponID].notify = 1
for i = 1,30,1 do
table.insert(spawnQueue, {burrow = queenID, unitName = "chickenh4", team = chickenTeamID})
end
end
damage = damage - (damage * resistPercent)
end
queenResistance[weaponID].damage = queenResistance[weaponID].damage + damage
return damage
end
end
return damage
end
function gadget:UnitDamaged(unitID, unitDefID, unitTeam, damage, paralyzer,
weaponID, attackerID, attackerDefID, attackerTeam)
if EMP_GOO[weaponID] and (unitTeam ~= chickenTeamID) and (lobberEMPTime > 0) then
stunUnit(unitID, ((damage / EMP_GOO[weaponID]) * lobberEMPTime))
end
if chickenBirths[attackerID] then chickenBirths[attackerID] = t end
if failChickens[attackerID] then failChickens[attackerID] = nil end
if failChickens[unitID] then failChickens[unitID] = nil end
if SKIRMISH[attackerDefID] and (unitTeam ~= chickenTeamID) and attackerID then
local ux,_,uz = GetUnitPosition(unitID)
local x,y,z = GetUnitPosition(attackerID)
if x and ux then
local angle = math.atan2(ux-x,uz-z)
idleOrderQueue[attackerID] = {cmd = CMD.MOVE, params = {x - (math.sin(angle) * SKIRMISH[attackerDefID]),y,z - (math.cos(angle) * SKIRMISH[attackerDefID])}, opts = {}}
end
elseif COWARD[unitDefID] and (not idleOrderQueue[unitID]) and (unitTeam == chickenTeamID) and attackerID then
local curH, maxH = GetUnitHealth(attackerID)
if curH < (maxH * 0.8) then
local ax,_,az = GetUnitPosition(attackerID)
local x,y,z = GetUnitPosition(unitID)
if x and ax then
local angle = math.atan2(ax-x,az-z)
idleOrderQueue[unitID] = {cmd = CMD.MOVE, params = {x - (math.sin(angle) * COWARD[unitDefID]),y,z - (math.cos(angle) * COWARD[unitDefID])}, opts = {}}
end
end
end
if (unitDefID == LOBBER) then
local nSpawn = false
if ((GetUnitHealth(unitID) < 2475) and (damage < (2000 + mRandom(1,500)))) then nSpawn = true end
if ((weaponID == JUNO) and (mRandom(1,2) == 1)) then nSpawn = true end
if nSpawn then
local bx, by, bz = GetUnitPosition(unitID)
local h = Spring.GetUnitHeading(unitID)
SetUnitBlocking(unitID, false)
local newUnitID = CreateUnit("chickenr2", bx, by, bz, "n", unitTeam)
Spring.SetUnitNoDraw(newUnitID,true)
Spring.MoveCtrl.Enable(newUnitID)
Spring.MoveCtrl.SetHeading(newUnitID, h)
Spring.MoveCtrl.Disable(newUnitID)
SetUnitExperience(newUnitID, expMod)
Spring.SetUnitNoDraw(newUnitID,false)
DestroyUnit(unitID, false, true)
idleOrderQueue[newUnitID] = {cmd = CMD.STOP, params = {}, opts = {}}
return
end
elseif (unitID == queenID) then
if paralyzer then
SetUnitHealth(unitID, {paralyze=0})
return
end
qDamage = (qDamage + damage)
if (qDamage > (queenMaxHP/10)) then
if qMove then
idleOrderQueue[queenID] = {cmd = CMD.STOP, params = {}, opts = {}}
qMove = false
qDamage = 0 - mRandom(1,25000)
else
local cC = ChooseTarget()
local xQ, _, zQ = GetUnitPosition(queenID)
if cC then
local angle = math.atan2((cC[1]-xQ), (cC[3]-zQ))
local dist = math.sqrt(((cC[1]-xQ)*(cC[1]-xQ))+((cC[3]-zQ)*(cC[3]-zQ))) * 0.75
if (dist < 1500) then
GiveOrderToUnit(queenID, CMD.MOVE, {(xQ + (math.sin(angle) * dist)), cC[2], (zQ + (math.cos(angle) * dist))}, {})
GiveOrderToUnit(queenID, CMD.FIGHT, cC, {"shift"})
if targetCache then addChickenTarget(queenID, targetCache) end
qDamage = (0 - mRandom(1,25000))
Wave()
qMove = true
else
idleOrderQueue[queenID] = {cmd = CMD.STOP, params = {}, opts = {}}
qDamage = 0
Wave()
end
else
idleOrderQueue[queenID] = {cmd = CMD.STOP, params = {}, opts = {}}
qDamage = 0
end
end
end
end
end
function gadget:GameStart()
if warningMessage then
Spring.Echo("Warning: No Chicken team available, add a Chicken bot")
Spring.Echo("(Assigning Chicken Team to Gaia - AI: Custom)")
end
if (burrowSpawnType == "initialbox") or (burrowSpawnType == "alwaysbox") then
local _,_,_,_,_,luaAllyID = Spring.GetTeamInfo(chickenTeamID)
if luaAllyID then
lsx1,lsz1,lsx2,lsz2 = Spring.GetAllyTeamStartBox(luaAllyID)
if (not lsx1) or (not lsz1) or (not lsx2) or (not lsz2) then
burrowSpawnType = "avoid"
Spring.Echo("No Chicken start box available, Burrow Placement set to 'Avoid Players'")
elseif (lsx1 == 0) and (lsz1 == 0) and (lsx2 == Game.mapSizeX) and (lsz2 == Game.mapSizeX) then
burrowSpawnType = "avoid"
Spring.Echo("No Chicken start box available, Burrow Placement set to 'Avoid Players'")
end
end
end
end
function gadget:GameFrame(n)
if gameOver then return end
if n == 15 then
DisableComputerUnits()
end
if (chickenCount < maxChicken) then
local i,defs = next(spawnQueue)
if i and defs then
local x, y, z = getChickenSpawnLoc(defs.burrow, SMALL_UNIT)
if x and y and z then
local unitID = CreateUnit(defs.unitName, x,y,z, "n", defs.team)
if unitID then
SetUnitExperience(unitID, expMod)
if (queenID) then
local angle = math.rad(mRandom(1,360))
idleOrderQueue[unitID] = {cmd = CMD.MOVE, params = {x - (math.sin(angle) * 360),y,z - (math.cos(angle) * 360)}, opts = {}}
else
local chickenParams = ChooseTarget()
if targetCache and (unitID ~= queenID) and (mRandom(1,15) == 5) then
idleOrderQueue[unitID] = {cmd = CMD.ATTACK, params = {targetCache}, opts = {}}
else
idleOrderQueue[unitID] = {cmd = CMD.FIGHT, params = chickenParams, opts = {}}
end
if targetCache then
if GetUnitNeutral(targetCache) then
idleOrderQueue[unitID] = {cmd = CMD.ATTACK, params = {targetCache}, opts = {}}
end
addChickenTarget(unitID, targetCache)
end
end
chickenBirths[unitID] = t
chickenCount = chickenCount + 1
end
end
spawnQueue[i] = nil
end
end
for unitID in pairs(stunList) do
stunList[unitID] = (stunList[unitID] - 1)
if (stunList[unitID] <= 0) then
SetUnitHealth(unitID, {paralyze=0})
stunList[unitID] = nil
end
end
if (n >= timeCounter) then
timeCounter = (n + UPDATE)
t = GetGameSeconds()
KillOldChicken()
if (t < gracePeriod) then return end
expMod = (expMod + expIncrement)
if next(idleOrderQueue) then
local processOrderQueue = {}
for unitID,order in pairs(idleOrderQueue) do
if GetUnitDefID(unitID) then
processOrderQueue[unitID] = order
end
end
idleOrderQueue = {}
for unitID,order in pairs(processOrderQueue) do
GiveOrderToUnit(unitID, order.cmd, order.params, order.opts)
GiveOrderToUnit(unitID, CMD.MOVE_STATE, { mRandom(0,2) }, {})
end
end
if (t >= (queenTime - queenAnger)) then
if (not queenID) and (not gameOver) then
queenID = SpawnQueen()
idleOrderQueue[queenID] = {cmd = CMD.STOP, params = {}, opts = {}}
_G.chickenEventArgs = {type="queen"}
SendToUnsynced("ChickenEvent")
_G.chickenEventArgs = nil
_,queenMaxHP = GetUnitHealth(queenID)
SetUnitExperience(queenID, (expMod*1.5))
timeOfLastWave = t
SKIRMISH[UnitDefNames["chickenc1"].id] = 150
SKIRMISH[UnitDefNames["chickenf1"].id] = 1200
SKIRMISH[UnitDefNames["chickenw1"].id] = 1800
COWARD[UnitDefNames["chicken2"].id] = nil
COWARD[UnitDefNames["chicken_dodo1"].id] = 300
for i = 1,80,1 do
table.insert(spawnQueue, {burrow = queenID, unitName = "chickenh4", team = chickenTeamID})
end
end
updateQueenLife()
end
local quicken = 0
local burrowCount = SetCount(burrows)
if (burrowTarget > 0) and (burrowTarget ~= burrowCount) then
quicken = (burrowSpawnRate * (1 - (burrowCount / burrowTarget)))
end
local burrowSpawnTime = (burrowSpawnRate - quicken)
if (burrowSpawnRate < (t - timeOfLastFakeSpawn) and burrowCount < maxBurrows) then
if firstSpawn then
minBurrows = SetCount(humanTeams)
local hteamID = next(humanTeams)
local ranCount = GetTeamUnitCount(hteamID)
for i = 1,ranCount,1 do
mRandom()
end
burrowTarget = math.min(math.ceil(minBurrows * 1.5) + gracePenalty, 40)
else
burrowTarget = burrowTarget + 1
end
timeOfLastFakeSpawn = t
end
if (burrowCount < minBurrows) or (burrowSpawnTime < (t - timeOfLastSpawn) and burrowCount < maxBurrows) then
if firstSpawn then
for i = 1,math.min(math.ceil((SetCount(humanTeams) * 1.5)) + gracePenalty, 40),1 do
SpawnBurrow()
end
timeOfLastWave = (t - (chickenSpawnRate - 6))
firstSpawn = false
if (burrowSpawnType == "initialbox") then burrowSpawnType = "avoid" end
else
SpawnBurrow()
end
if (burrowCount >= minBurrows) then timeOfLastSpawn = t end
_G.chickenEventArgs = {type="burrowSpawn"}
SendToUnsynced("ChickenEvent")
_G.chickenEventArgs = nil
SetGameRulesParam("roostCount", SetCount(burrows))
end
if (burrowCount > 0) and (chickenSpawnRate < (t - timeOfLastWave)) then
local cCount = Wave()
if cCount and cCount > 0 and (not queenID) then
_G.chickenEventArgs = {type="wave", number=cCount, tech=currentWave}
SendToUnsynced("ChickenEvent")
_G.chickenEventArgs = nil
end
timeOfLastWave = t
end
chickenCount = UpdateUnitCount()
end
end
function gadget:UnitDestroyed(unitID, unitDefID, unitTeam)
--debug--Spring.Echo(t .. " UnitDestroyed " .. unitID)
if gameOver then return end
if chickenBirths[unitID] then chickenBirths[unitID] = nil end
if turrets[unitID] then turrets[unitID] = nil end
if idleOrderQueue[unitID] then idleOrderQueue[unitID] = nil end
if failChickens[unitID] then failChickens[unitID] = nil end
if chickenTargets[unitID] then
if (unitTeam ~= chickenTeamID) then
--debug--Spring.Echo(t .. " chickenTargets " .. unitID)
for chickenID in pairs(chickenTargets[unitID]) do
--debug--Spring.Echo(t .. " stopChicken " .. chickenID)
if GetUnitDefID(chickenID) then
idleOrderQueue[chickenID] = {cmd = CMD.STOP, params = {}, opts = {}}
end
end
elseif chickenTargets[chickenTargets[unitID]] then
chickenTargets[chickenTargets[unitID]][unitID] = nil
end
chickenTargets[unitID] = nil
end
if (unitID == targetCache) then
targetCache = nil
targetCacheCount = math.huge
end
if (unitTeam == chickenTeamID) and chickenDefTypes[unitDefID] then
local name = UnitDefs[unitDefID].name
if unitDefID ~= burrowDef then name = string.sub(name,1,-2) end
local kills = GetGameRulesParam(name.."Kills")
SetGameRulesParam(name.."Kills", kills + 1)
chickenCount = chickenCount - 1
end
if (unitID == queenID) then
queenID = nil
gameOver = true
endScores = CalculateScores()
endScores.type = "scores"
_G.chickenEventArgs = endScores
SendToUnsynced("ChickenEvent")
_G.chickenEventArgs = nil
KillAllComputerUnits()
KillAllChicken()
end
if (unitDefID == burrowDef) and (not gameOver) then
local kills = GetGameRulesParam(burrowName.."Kills")
SetGameRulesParam(burrowName.."Kills", kills + 1)
burrows[unitID] = nil
if (addQueenAnger == 1) then
queenAnger = (queenAnger + angerBonus)
expMod = (expMod + (expIncrement * angerBonus))
SetGameRulesParam("queenAnger", queenAnger)
bonusPoints = (bonusPoints + 2500)
end
for turretID,v in pairs(turrets) do
if (v[1] == unitID) then
DestroyUnit(turretID, true)
turrets[turretID] = nil
end
end
for burrowID in pairs(burrows) do
SpawnTurret(burrowID, bonusTurret)
end
for i,defs in pairs(spawnQueue) do
if (defs.burrow == unitID) then
spawnQueue[i] = nil
end
end
SetGameRulesParam("roostCount", SetCount(burrows))
end
end
function gadget:TeamDied(teamID)
if humanTeams[teamID] then
if (minBurrows > 1) then minBurrows = (minBurrows - 1) end
end
humanTeams[teamID] = nil
computerTeams[teamID] = nil
if gameOver and showScoresOnce then
DisplayScores(endScores)
showScoresOnce = false
end
end
function gadget:UnitTaken(unitID, unitDefID, oldTeam, newTeam)
if (oldTeam == chickenTeamID) then
DestroyUnit(unitID, true)
end
end
function gadget:AllowUnitTransfer(unitID, unitDefID, oldTeam, newTeam, capture)
if (newTeam == chickenTeamID) then
return false
else
return true
end
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
else
-- END SYNCED
-- BEGIN UNSYNCED
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local Script = Script
local SYNCED = SYNCED
function WrapToLuaUI()
if (Script.LuaUI('ChickenEvent')) then
local chickenEventArgs = {}
for k, v in spairs(SYNCED.chickenEventArgs) do
chickenEventArgs[k] = v
end
Script.LuaUI.ChickenEvent(chickenEventArgs)
end
end
function gadget:Initialize()
gadgetHandler:AddSyncAction('ChickenEvent', WrapToLuaUI)
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
end
-- END UNSYNCED
--------------------------------------------------------------------------------