-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- 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 .. " () - " .. 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 --------------------------------------------------------------------------------