// lua\Bot_Zbot.lua if (not Server) then error("Should only be included on the Server") end Script.Load("lua/Bot.lua") class 'BotZbot' (Bot) // VARIABLES BotZbot.kBotNames = { "Zeikko (bot)", "ale (bot)", "Yuuki (bot)", "Hackepeter (bot)", "Koruyo (bot)", "SiesSan (bot)", "Tek (bot)", "Wizard (bot)", "Ender (bot)", "GeneralBowser (bot)", "Gh0ti (bot)" } BotZbot.kDebugMode = true BotZbot.kInitialized = false BotZbot.kTargetAcquireRange = 50 BotZbot.kAccuracy = 0.8 BotZbot.kVisibilityCheckInterval = 3 // STATES function BotZbot:Idle() self:StateTrace("Idle") local player = self:GetPlayer() local order = player:GetCurrentOrder() if order ~= nil then // move? local orderLocation = self:GetOrderLocation() if orderLocation and (player:GetEyePos() - orderLocation):GetLengthSquared() > 20 then return self.Move end // build? if self:GetOrderTarget(6) then return self.Build end end //self:GetExploreOrder() if (self.stateTime > Shared.GetRandomInt(2,5)) then if Shared.GetRandomInt(1,100) > 50 then return self.RandomWalk else self:GetExploreOrder() end end return self.Idle end function BotZbot:Build() self:StateTrace("Build") // build? local buildTarget = self:GetOrderTarget(6) if buildTarget == nil then return self.Idle end // is target reachable? local player = self:GetPlayer() local engagementPoint = buildTarget:GetEngagementPoint() if (player:GetEyePos() - engagementPoint):GetLengthSquared() > 5 then self:MoveToPoint(engagementPoint) end // look at build object self:LookAtPoint(buildTarget:GetEngagementPoint(), true) // build self.move.commands = bit.bor(self.move.commands, Move.Use) return self.Build end function BotZbot:Move() self:StateTrace("Move") local player = self:GetPlayer() // need to get close on build orders local order = player:GetCurrentOrder() local destinationRange = 20 if order ~= nil then local orderType = order:GetType() local orderTarget = Shared.GetEntity(order:GetParam()) if (orderType == kTechId.build) and orderTarget ~= nil then destinationRange = 5 end local orderLocation = self:GetOrderLocation() if orderLocation == nil then return self.Idle end // move to target self:MoveToPoint(orderLocation) // target reached? if (player:GetEyePos() - orderLocation):GetLengthSquared() < destinationRange then self.lastOrderLocation = orderLocation return self.Idle end return self.Move end return self.Idle end function BotZbot:Attack() self:StateTrace("Attack") local player = self:GetPlayer() local order = player:GetCurrentOrder() //Has an attack order? if order ~= nil and (order:GetType() == kTechId.Attack) then local target = Shared.GetEntity(order:GetParam()) //Has target? if target then //Can see target? if ( self.stateTime < self.kVisibilityCheckInterval) then local attackObject = Shared.GetEntity(order:GetParam()) local attackPoint = attackObject:GetEngagementPoint() attackPoint.x = attackPoint.x + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) attackPoint.z = attackPoint.z + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) attackPoint.y = attackPoint.y + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) self:LookAtPoint(attackPoint) self.move.commands = bit.bor(self.move.commands, Move.PrimaryAttack) return self.Attack else local filter = EntityFilterTwo(player, activeWeapon) local trace = Shared.TraceRay(player:GetEyePos(), target:GetModelOrigin(), PhysicsMask.AllButPCs, filter) if trace.entity == target then Print("aim") local attackObject = Shared.GetEntity(order:GetParam()) local attackPoint = attackObject:GetEngagementPoint() attackPoint.x = attackPoint.x + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) attackPoint.z = attackPoint.z + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) attackPoint.y = attackPoint.y + Shared.GetRandomFloat(-self.kAccuracy, self.kAccuracy) self:LookAtPoint(attackPoint) self.move.commands = bit.bor(self.move.commands, Move.PrimaryAttack) return self.Attack else //follow enemy? if Shared.GetRandomInt(1,100) > 50 then local attackObject = Shared.GetEntity(order:GetParam()) local attackPoint = attackObject:GetEngagementPoint() self:MoveToPoint(attackPoint) else player:GiveOrder(kTechId.Move, player:GetId(), player:GetEngagementPoint(), nil, true, true) return self.Idle end end end end end return self.Idle end function BotZbot:RandomWalk() self:StateTrace("random walk") local player = self:GetPlayer() if self.randomWalkTarget == nil then self.randomWalkTarget = player:GetEyePos() self.randomWalkTarget.x = self.randomWalkTarget.x + math.random(-8, 8) self.randomWalkTarget.z = self.randomWalkTarget.z + math.random(-8, 8) end self:MoveToPoint(self.randomWalkTarget) if (player:GetEyePos() - self.randomWalkTarget):GetLengthSquared() < 4 or self.stateTime > 4 then self.randomWalkTarget = nil return self.Idle end return self.RandomWalk end // HELPER FUNCTIONS function BotZbot:GetExploreOrder(move) Print("Explore") local player = self:GetPlayer() // Go to nearest unbuilt tech point or nozzle local className = ConditionalValue(math.random() < .2 , "TechPoint", "ResourcePoint") local ents = Shared.GetEntitiesWithClassname(className) if ents:GetSize() > 0 then local index = math.floor(math.random() * ents:GetSize()) local destination = ents:GetEntityAtIndex(index) player:GiveOrder(kTechId.Move, 0, destination:GetEngagementPoint(), nil, true, true) end return self.RandomWalk end function BotZbot:GetOrderTarget(type) local player = self:GetPlayer() local order = player:GetCurrentOrder() if order and order:GetType() == type then return Shared.GetEntity(order:GetParam()) end end function BotZbot:GetOrderLocation() local player = self:GetPlayer() local order = player:GetCurrentOrder() if order then local orderType = order:GetType() local orderTarget = Shared.GetEntity(order:GetParam()) if (orderType == kTechId.Attack or orderType == kTechId.build) and orderTarget then self.orderLocation = orderTarget:GetEngagementPoint() else self.orderLocation = order:GetLocation() end end return self.orderLocation end function BotZbot:MoveToPoint(toPoint) local player = self:GetPlayer() // use pathfinder if self:BuildPath(player:GetEyePos(), toPoint) then local points = self:GetPoints() if points then toPoint = points[1] end if table.maxn(points) > 1 and (player:GetEyePos() - toPoint):GetLengthSquared() < 4 then toPoint = points[2] end end // look at target self:LookAtPoint(toPoint) // walk forwards self.move.move.z = 1 end function BotZbot:LookAtPoint(toPoint, direct) local player = self:GetPlayer() // compute direction to target local diff = toPoint - player:GetEyePos() local direction = GetNormalizedVector(diff) // look at target if direct then self.move.yaw = GetYawFromVector(direction) - player.baseYaw self.move.pitch = GetPitchFromVector(direction) - player.basePitch else self.move.yaw = SlerpRadians(self.move.yaw, GetYawFromVector(direction) - player.baseYaw, .4) self.move.pitch = SlerpRadians(self.move.pitch, GetPitchFromVector(direction) - player.basePitch, .4) end end /* * Gives order to attack the closest visible enemy */ function BotZbot:VisibleEnemy() local player = self:GetPlayer() // Are there any visible enemy players or structures nearby? local success = false if not self.timeLastTargetCheck or (Shared.GetTime() - self.timeLastTargetCheck > 2) then local nearestTarget = nil local nearestTargetDistance = nil local targets = GetEntitiesWithMixinForTeamWithinRange("Live", GetEnemyTeamNumber(player:GetTeamNumber()), player:GetOrigin(), self.kTargetAcquireRange) for index, target in pairs(targets) do if target:GetIsAlive() and target:GetIsVisible() and target:GetCanTakeDamage() and target ~= player then local activeWeapon = player:GetActiveWeapon() local filter = EntityFilterTwo(player, activeWeapon) local trace = Shared.TraceRay(player:GetEyePos(), target:GetModelOrigin(), PhysicsMask.AllButPCs, filter) //Make sure target is visible if trace.entity == target then // Prioritize players over non-players local dist = (target:GetEngagementPoint() - player:GetModelOrigin()):GetLength() local newTarget = (not nearestTarget) or (target:isa("Player") and not nearestTarget:isa("Player")) if not newTarget then if dist < nearestTargetDistance then newTarget = not nearestTarget:isa("Player") or target:isa("Player") end end if newTarget then nearestTarget = target nearestTargetDistance = dist end end end end if nearestTarget then local name = SafeClassName(nearestTarget) if nearestTarget:isa("Player") then name = nearestTarget:GetName() end player:GiveOrder(kTechId.Attack, nearestTarget:GetId(), nearestTarget:GetEngagementPoint(), nil, true, true) success = true end self.timeLastTargetCheck = Shared.GetTime() end return success end function BotZbot:SetName() self:StateTrace("SetName") // wait a few seconds, set name and start idling if self.stateTime > 6 then local player = self:GetPlayer() local name = player:GetName() if name and string.find(string.lower(name), string.lower(kDefaultPlayerName)) then local numNames = table.maxn(BotZbot.kBotNames) local index = Clamp(math.ceil(math.random() * numNames), 1, numNames) OnCommandSetName(self.client, BotZbot.kBotNames[index]) end self.kInitialized = true Print(ToString(self.kInitialized)) return self.Idle end return self.SetName end // INTERFACE TO Bot.lua function BotZbot:GenerateMove() local player = self:GetPlayer() local move = Move() // keep the current yaw/pitch as default move.yaw = player:GetAngles().yaw - player.baseYaw move.pitch = player:GetAngles().pitch - player.basePitch // use a state machine to generate a move local currentTime = Shared.GetTime() self.move = move //self.state = nil if self.state == nil then if self.kInitialized then self.state = self.Idle else self.state = self.SetName end self.stateEnterTime = currentTime end if self:VisibleEnemy() then self.state = self.Attack end self.stateTime = currentTime - self.stateEnterTime local newState = self.state(self) if newState ~= self.state then self.stateEnterTime = currentTime end self.state = newState return self.move end function BotZbot:OnThink() Bot.OnThink(self) end // DEBUGGING function BotZbot:StateTrace(name) if BotZbot.kDebugMode and self.stateName ~= name then Print("%s", name) self.stateName = name end end