ZombieBot
It starts slow and slowly speeds up. The rate that it speeds up is proportional to the lenght of the level. At 80% of the normal speed, it will start using turbo randomly. It's seriously awesome when you add 15 of them
- Code:
- --- from S_bot, modified by fordcars
--- this bot works on all game modes, made for bobdaduck
goalPt = point.new(0,0)
prevtarget = nil
gotoPositionWasNil = true
o = point.new(0,0)
botLoc = nil -- Bot's location, will be updated when onTick() is run
-- This function gets run once during setup. It is the only time we can declare variables
-- and have them be defined as globals.
function main()
botRadius = bot:getRad()
pathTimerMax = 250
pathTimer = pathTimerMax
dirToGo = 0
game = bf:getGameInfo()
difficulty = tonumber(arg[1]) or .5
agression = tonumber(arg[2]) or 0.5
defense = tonumber(arg[3]) or 0
speed = tonumber(arg[4]) or 1
directionThreshold = tonumber(arg[5]) or .25
gameType = game:getGameType()
averageIndex = 1
averageMax = 20
averageArray = { }
for i = 1, averageMax do
averageArray[i] = false
end
myOrbitalDirection = 1
myObjective = math.random(0, 10)
zombSpeed = 0.1
useTurbo = false
turboTimer = false
oldBotSpeed = nil
local totalTime = game:getGameTimeRemaining()
local percTime = totalTime * 80
percTime = percTime / 100
percTime = percTime * 60
zombieRate = 0.8 / percTime
addTimer()
end
items = { } -- Global variable, reusable container for findGlobalObjects. Reusing this table avoids costs of
-- constructing and destructing it every time we call findGlobalObjects(). Be sure to clear the
-- table before reusing it!
function addTimer()
Timer:scheduleRepeating(addBotRate, 16.67)
end
function addBotRate()
zombSpeed = zombSpeed + zombieRate
end
function shieldSelf()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Bullet, ObjType.Seeker, ObjType.Asteroid, ObjType.Mine, ObjType.Burst)
local distToShieldAt = botRadius * 2 + (1 - difficulty) * 100
if (items ~= nil) then
for i,bullet in ipairs(items) do
local bulletLoc = bullet:getPos()
local bulletVel = bullet:getVel()
local angleDiff = math.abs(angleDifference(point.angleTo(o, bulletVel), point.angleTo(bulletLoc, botLoc)))
local bulletfromteam = false
--logprint(angleDiff)
if (game:isTeamGame() == true) and (bullet:getTeamIndex() == bot:getTeamIndex()) then -- If bullet is from team
bulletfromteam = true -- bullet from team is true
else -- If it is not a team game or if bullet is not from team
bulletfromteam = false -- bullet from team is false
end
if (bulletfromteam == false) then
if (point.distanceTo(bulletLoc, botLoc) < distToShieldAt + bullet:getRad() + point.distanceTo(o, bulletVel) * 50 and angleDiff < math.pi / 4) or (bullet == ObjType.Burst and point.distSquared(bulletLoc, botLoc) <= 500 * 500) then
bot:fireModule(Module.Shield)
return(true)
end
end
end
end
end
function angleDifference(angleA, angleB)
return (math.mod(math.mod(angleA - angleB, math.pi * 2) + math.pi * 3, math.pi * 2) - math.pi)
end
function fireAtObjects()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Robot, ObjType.Turret, ObjType.Ship, ObjType.Asteroid,
ObjType.ForceFieldProjector, ObjType.SpyBug, ObjType.Core)
-- Cycle through list of potential items until we find one that we can attack
for index, enemy in ipairs(items) do
if(fireAtObject(enemy, Weapon.Phaser)) then
break
end
end
end
-- Fires at the specified object with the specified weapon if the obj is a good target.
-- Does not fire if object is on the same team or if there is something in the way.
-- Returns whether it fired or not.
function fireAtObject(obj, weapon)
local classId = obj:getObjType()
if(classId == ObjType.Turret or classId == ObjType.ForceFieldProjector) then
if(obj:getHealth() < .1) then
--logprint("He's dead, Jim.")
return(false)
end
end
if(classId == ObjType.Ship or classId == ObjType.Robot or classId == ObjType.ForceFieldProjector or
classId == ObjType.Turret or classId == ObjType.Core) then
if obj:getTeamIndex() == bot:getTeamIndex() and game:isTeamGame() or obj:getTeamIndex() == Team.Neutral then
--logprint("It's an ally.")
return(false)
end
end
local angle = getFiringSolution(obj)
if angle ~= nil and bot:hasWeapon(weapon) then
bot:setAngle(angle + math.rad((math.random()-0.5)*20*(1-difficulty)))
bot:fireWeapon(weapon)
--logprint("bot:fireWeapon() called!");
return(true)
end
--logprint("Firing solution not found.");
return(false)
end
function getName()
return("botdazombie")
end
function shield()
averageArray[averageIndex] = shieldSelf()
averageIndex = math.mod(averageIndex,averageMax) + 1
local shieldPercent = 0
for i = 1, averageMax do
if(averageArray[averageIndex]) then
shieldPercent = shieldPercent + 1
end
end
shieldPercent = shieldPercent / averageMax
if(shieldPercent > directionThreshold) then
myOrbitalDirection = -myOrbitalDirection
for i = 1, averageMax do
averageArray[i] = false
end
end
end
function orbitPoint(pt, dir, inputDist, inputStrictness)
local distAway = botRadius * 7
local strictness = 2
local direction = 1
if(dir ~= nil) then
direction = dir
end
if(inputDist ~= nil) then
distAway = inputDist
end
if(inputStrictness ~= nil) then
strictness = inputStrictness
end
if(pt ~= nil) then
local dist = point.distanceTo(pt, botLoc)
local deltaDistance = (dist - distAway) * strictness / distAway
local sign = 1
if(deltaDistance > 0) then
sign = 1
elseif(deltaDistance < 0) then
sign = -1
end
local changeInAngle = (math.abs(deltaDistance)/(deltaDistance + sign)) * math.pi/2
local angleToPoint = point.angleTo(pt, bot:getPos())
dirToGo = angleToPoint + (math.pi/2 + changeInAngle)*direction
--bot:setThrust(speed, dirToGo)
end
end
function gotoPosition(pt)
if pt ~= nil then
gotoPositionWasNil = false
if pathTimer < .01 then
goalPt = bot:getWaypoint(pt)
if(goalPt ~= nil) then
dirToGo = point.angleTo(botLoc, goalPt)
end
end
end
end
function gotoAndOrbitPosition(pt)
if pt ~= nil then
gotoPositionWasNil = false
if pt~= nil then
gotoPositionWasNil = false
gotoPosition(pt)
else
gotoPositionWasNil = false
orbitPoint(pt, myOrbitalDirection, botRadius * 5, 2)
end
end
end
-- Returns true if it found an enemy to fight
function attackNearbyEnemies(target, agressionLevel)
if target ~= nil then
local targetLoc = target:getPos()
-- local dist = point.distanceTo(botLoc, targetLoc)
local myPow = bot:getEnergy() + bot:getHealth()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Ship, ObjType.Robot)
local otherPow = target:getEnergy() + target:getHealth() * #items
--advantage is between -1 and 1, -1 meaning an extreme disadvantage and 1 meaning an extreme advantage
local advantage = (myPow - otherPow) / math.max(myPow, otherPow)
if(advantage / 2 + .5 >agressionLevel) then
--orbitPoint(targetLoc, myOrbitalDirection, botRadius * 9, 2)
prevtarget = targetLoc
return(false) -- was true
else
end
end
return(false)
end
-- Returns the objective for the bot, in the form of an object the bot can navigate towards. This makes bots choose different defending locations.
-- If onTeam is true, will only return items on specified team. If onTeam is false, will return items *not* on
-- specified team. If called with fewer than three args, will ignore team altogether.
function getObjective(objType, team, onTeam)
table.clear(items)
bf:findAllObjects(items, objType) -- Returns a list of all items of type objType in the game
local itemsOnMyTeam = {}
local currentIndex = 1
for index, item in ipairs(items) do -- Iterate through all found items
local itemTeamIndex = item:getTeamIndex()
if (objType == ObjType.Flag) and (gameType == GameType.Nexus) then
if not item:isOnShip() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
elseif (objType == ObjType.Flag) and
((gameType == GameType.HTF) or (gameType == GameType.Retrieve)) and
item:isInCaptureZone() then
--logprint(item:getCaptureZone():getTeamIndex());
if item:getCaptureZone():getTeamIndex() ~= bot:getTeamIndex() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
elseif (objType == ObjType.GoalZone) and (gameType == GameType.HTF or gameType == GameType.Retrieve) then
if onTeam == nil or ((itemTeamIndex == team) == onTeam) then
if not item:hasFlag() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
end
else
if (itemTeamIndex == Team.Neutral ) and (objType ~= ObjType.GoalZone or gameType ~= GameType.ZC) then
itemTeamIndex = team -- anything Neutral is on our team (except zone control neutral goal zone)
end
if onTeam == nil or ((itemTeamIndex == team) == onTeam) then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
end
end
local listMax = 0
--find max
if itemsOnMyTeam[1] ~= nil then
for index,item in ipairs(itemsOnMyTeam) do
if(item ~= nil) then
listMax = listMax + 1
end
end
local targetNum = math.mod(myObjective, listMax) + 1
return(itemsOnMyTeam[targetNum])
else
return(nil)
end
-- local closestitem = 0 --itemsOnMyTeam[1]
-- local cur = 1
-- local closestdist = 99999999
-- while itemsOnMyTeam[cur] ~= nil do
-- local item1 = itemsOnMyTeam[cur]
-- if item1 ~= nil then
-- local loc = item1.getPos()
-- if loc ~= nil then
-- local dist = point.distanceTo(botLoc, loc)
-- if dist < closestdist then
-- closestdist = dist
-- closesetitem = item1
-- end
-- end
-- end
-- cur=cur+1
-- end
-- return(closestitem)
end
function doObjective(closestEnemy)
gotoPositionWasNil = true
if(gameType == GameType.Bitmatch) then
--Nothing to do here.
elseif(gameType == GameType.Nexus) then
-- Grab any flags that are found, and go to nexus when it opens
local otherFlag = getObjective(ObjType.Flag) -- Find any flags
if otherFlag ~= nil then gotoPosition(otherFlag:getPos()) end
-- logprint(bot:getFlagCount()) -- always 1
if bot:getFlagCount() > 3 and (game:isNexusOpen() or game:getNexusTimeLeft() < 10000) then -- Need to know if nexus is open
local nexusOrFlag = getObjective(ObjType.Nexus) -- unimplemented push function error.
if nexusOrFlag ~= nil then
gotoPosition(nexusOrFlag:getPos())
end
end
elseif(gameType == GameType.Rabbit) then
--Grab a flag, or go after the flag.
if not bot:hasFlag() then
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
gotoPosition(otherFlag:getPos())
end
elseif(gameType == GameType.HTF or gameType == GameType.Retrieve) then
-- Grab the flag and put it into goal zones
-- Robot keeps trying to pick up the flags that is already in the goalZones
if bot:hasFlag() then
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), true) -- Find GoalZone on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
else
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.CTF) then
--defend the flag
local myFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), false) -- Find flags not on our team
if(defense < .5) then
if bot:hasFlag() then
if not myFlag:isOnShip() then
gotoPosition(myFlag:getPos())
else
gotoAndOrbitPosition(myFlag:getPos())
end
--gotoPosition(myFlag:getPos())
elseif(otherFlag ~= nil) then
if myFlag:isOnShip() then
gotoPosition(myFlag:getPos())
elseif not otherFlag:isOnShip() then
gotoPosition(otherFlag:getPos())
else
gotoAndOrbitPosition(otherFlag:getPos())
end
end
else
if bot:hasFlag() then
gotoPosition(myFlag:getPos())
elseif myFlag:isInInitLoc() then
gotoAndOrbitPosition(myFlag:getPos())
else
if myFlag:isOnShip() then
gotoAndOrbitPosition(myFlag:getPos())
else
gotoPosition(myFlag:getPos())
end
end
end
elseif(gameType == GameType.Soccer) then
--grab soccer and put into enemy goal
-- How do we know if we are holding soccer ? (not supported when cannot pickup soccer (016)
if bot:getMountedItems(ObjType.SoccerBallItem)[1] ~= nil then
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), false) -- Find GoalZones not on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
else
local otherFlag = getObjective(ObjType.SoccerBallItem) -- Find SoccerBall
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.ZC) then
-- Grab flag, then go after zones that is not ours.
if not bot:hasFlag() then
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
if otherFlag ~= nil then
if otherFlag:isOnShip() then
gotoAndOrbitPosition(otherFlag:getPos())
else
gotoPosition(otherFlag:getPos())
end
end
else
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), false) -- Find GoalZones on our team
if otherFlag then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.Core) then
local obj = getObjective(ObjType.Core, bot:getTeamIndex(), false) -- Find enemy Core
if obj ~= nil then
gotoAndOrbitPosition(obj:getPos())
end
end
-- If we have no where to go, go to nearest enemy.
if gotoPositionWasNil then
if(closestEnemy ~= nil) then
prevtarget = closestEnemy:getPos()
end
if(prevtarget ~= nil) then
gotoAndOrbitPosition(prevtarget)
end
end
end
function goInDirection()
bot:setThrust(zombSpeed, dirToGo)
end
function turbo()
useTurbo = true
oldBotSpeed = zombSpeed
zombSpeed = 1
local turboLength = math.random(2000, 6000)
Timer:scheduleOnce(stopTurbo, turboLength)
end
function stopTurbo()
useTurbo = false
turboTimer = false
zombSpeed = oldBotSpeed
end
-- This function gets called every game tick; deltaTime is the time that has elapsed since it was last called
function onTick(deltaTime)
botLoc = bot:getPos()
pathTimer = pathTimer - deltaTime
assert(bot:getPos() ~= nil)
local closestEnemy = bot:findClosestEnemy()
-- attackNearbyEnemies returns true if there is an enemy to fight, false if the bot can do something else
attackNearbyEnemies(closestEnemy, 1 - agression)
doObjective(closestEnemy) -- Set bot's objective
goInDirection() -- Move the ship
fireAtObjects() -- Fire weapons
shield() -- Apply shield
if(pathTimer < 0) then
pathTimer = pathTimerMax + math.random(0, pathTimerMax)
end
if (turboTimer == false) and (zombSpeed >= 0.8) then --------------------
local timerTime = math.random(20000, 30000)
turboTimer = true
Timer:scheduleOnce(turbo, timerTime)
end
if (useTurbo == true) then
bot:fireModule(Module.Turbo)
end
end
skybax: Why is this health pack following me?
bobdaduck: uh, it likes you.