SentinelBot
I give you: SentinelBot - the twisted offspring of s_bot, clonebot and orbitbot!
Usage:
/addbot sentinel [team index] [player name]
Example:
/addbot sentinel 0 raptor
Behavior:
SentinelBot does the following:
- Find the designated player *anywhere* on the map (not just within sight) and will proceed to orbit that player
- Shoot any hostile targets nearby while orbiting. It will shield itself, too.
Yes, this bot makes sure you know how to spell 'sentinel' correctly.
And yes, this bot can be transformed into a terror-bot: just specify your team with an opposing team's player
Enjoy!
Update: SentinelBot now changes its name if used for evil
Update 2 : Updated to work with 018 scripting enginel
Update 3 : Really make it work with 018 this time
Update 4 : Really, really make it work with 018
Update 5 : Requires 019+ now
- Code:
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- SentinelBot: when s_bot, orbitbot, and clonebot merge...
--
-- SentinelBot is a bot that orbits the designated player and shoots enemies
-- when they come within range
--
-- Authors:
-- - raptor
-- - s_bot, orbitbot, clonebot authors
--
function getName()
if isTerror then
return( "TerrorBot" )
else
return( "SentinelBot" )
end
end
function onPlayerLeft(playerinfo_who_left)
guardedShipInfo = nil
end
function fireAtObjects()
local items = bf:findAllObjects(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.Phaser)
--logprint("bot:fire() called!");
return(true)
end
--logprint("Firing solution not found.");
return(false)
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 shieldSelf()
items = bf:findAllObjects(ObjType.Bullet, ObjType.Seeker, ObjType.Asteroid, ObjType.Mine)
local distToShieldAt = bot:getRad() * 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(reusableAngle, bulletVel), point.angleTo(bulletLoc, botLocation)))
--logprint(angleDiff)
if (point.distanceTo(bulletLoc, botLocation) < distToShieldAt + bullet:getRad() + point.distanceTo(reusableAngle, bulletVel) * 50 and angleDiff < math.pi / 4) then
bot:fireModule(Module.Shield)
return(true)
end
end
end
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 onTick(deltaTime)
-- Make sure we have a ship to guard first
if guardedShipInfo == nil then
local items = bf:findAllObjects(ObjType.Ship, ObjType.Robot)
for index, item in ipairs(items) do
if item:getPlayerInfo():getName() == guardedShipName then
guardedShipInfo = item:getPlayerInfo()
end
end
end
-- Wait until we actually get the guarded ship info to continue, the ship may have died
if(guardedShipInfo == nil or guardedShipInfo:getShip() == nil) then
return
end
botLocation = bot:getPos()
guardedShipLocation = guardedShipInfo:getShip():getPos()
local distanceSquared = point.distSquared(botLocation, guardedShipLocation)
-- Change the angle according to the deltaTime; factor of .0035 is an arbitrary value
-- that tries to make angle adjustments seem smooth. Note that a larger value is needed
-- for a smaller orbit radius
orbitAngle = orbitAngle + .0035 * deltaTime
local destination
-- Enter orbit when close enough
if( distanceSquared <= orbitRadius * orbitRadius * 1.1 ) then
destination = point.new(guardedShipLocation.x + orbitRadius * math.cos (orbitAngle),
guardedShipLocation.y + orbitRadius * math.sin (orbitAngle))
if( not isInOrbit ) then
orbitAngle = point.angleTo(botLocation, destination) - math.pi / 2
end
isInOrbit = true
-- Not in orbit
else
isInOrbit = false
destination = guardedShipLocation
-- If it's time to calculate a new route, find the next point in the path
pathTimer = pathTimer - deltaTime
if pathTimer < .01 and not isInOrbit then
waypoint = bot:getWaypoint(guardedShipLocation)
end
-- If we have a waypoint, change destination to that
if(waypoint ~= nil) then
destination = waypoint
end
end
-- Go forth!
bot:setThrustToPt(destination)
-- Fire!
fireAtObjects()
-- Be safe!
shield()
-- Now reset path timer if needed
if(pathTimer < 0)then
pathTimer = pathTimerMax
end
end
function isTerrorBot()
local items = bf:findAllObjects(ObjType.Ship, ObjType.Robot)
for index, item in ipairs(items) do
if item:getPlayerInfo():getName() == guardedShipName then
guardedShipInfo = item:getPlayerInfo()
end
end
-- Ship not found, Abort!
if(guardedShipInfo == nil or guardedShipInfo:getShip() == nil) then
return
end
local guardedShip = guardedShipInfo:getShip()
if guardedShip:getTeamIndex() ~= bot:getTeamIndex() or not game:isTeamGame() then
isTerror = true
end
end
function main()
bf:subscribe(Event.PlayerLeft)
game = bf:getGameInfo()
gameType = game:getGameType()
difficulty = 1
directionThreshold = .25
averageIndex = 1
averageMax = 20
averageArray = {}
for i = 1, averageMax do
averageArray[i] = false
end
myOrbitalDirection = 1
reusableAngle = point.new(0,0)
-- Only recalculate path every 500 ms or so
pathTimerMax = 250
pathTimer = pathTimerMax
waypoint = nil
botLocation = nil
guardedShipLocation = nil
orbitAngle = 0
orbitRadius = 130
isInOrbit = false
guardedShipInfo = nil
-- Who to guard?
guardedShipName = arg[1]
-- Test to see if we are TerrorBot
isTerror = false
isTerrorBot()
end