function GreyDespawn(activator) if activator:IsAlive() == false then return end --if we are neither a grey bot, nor a bot marked as "despawnable" give up if activator:GetAttributeValue("special item description") ~= "Despawnable" and activator.m_iTeamNum ~= 1 then return end activator.m_bGlowEnabled = 0 activator:AddCond(5, 99, activator) activator:AddCond(71, 99, activator) local teleParticle = ents.CreateWithKeys("info_particle_system", { effect_name = "teleportedin_blue", start_active = 1, flag_as_weather = 0, }, true, true) timer.Simple(0.4, function() teleParticle:SetAbsOrigin(activator:GetAbsOrigin()) teleParticle:Start() activator:AddCond(66, 99, activator) end) timer.Simple(0.5, function() activator:BotCommand("despawn") teleParticle:Remove() end) end function GreySpawnClient(_, activator) local callbacks = {} local function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end local function terminate() activator.m_bGlowEnabled = 0 removeCallbacks(activator, callbacks) end local despawnDuration = activator:GetAttributeValue("cannot_transmute") local warningDuration = activator:GetAttributeValue("non economy") activator:SetAttributeValue("not solid to players", 1) if warningDuration == nil then warningDuration = 2 end activator:SetAttributeValue("ignored by bots", 1) activator:SetAttributeValue("ignored by enemy sentries", 1) GREYSPAWNER_enableWarning(warningDuration) if despawnDuration ~= nil then timer.Simple(despawnDuration, function() GreyDespawn(activator) end) end timer.Simple(warningDuration, function() local validNodes = GREYSPAWNER_getValidNodes() local priorityNodes = GREYSPAWNER_getPriorityNodes() --This shouldn't be able to happen, but if it does, die. --No need to check priority nodes for this, priority nodes is a subset of valid nodes, --if valid nodes is empty, so is priority nodes if type(next(validNodes)) == "nil" then activator:BotCommand("Despawn") return end if activator:IsValid() ~= true then return end GREYSPAWNER_requestSound(activator.m_bIsMiniBoss) activator:SetAttributeValue("ignored by bots", 0) activator:SetAttributeValue("not solid to players", 0) activator:SetAttributeValue("ignored by enemy sentries", 0) activator:StunPlayer(1, 1, TF_STUNFLAG_SLOWDOWN) --prevent demoknight charges for a second so you don't get violated by them on wakeup. if activator.m_iClass ~= 5 and type(next(priorityNodes)) == "nil" then local teleportDestination = validNodes[math.random(#validNodes)] if teleportDestination == nil or teleportDestination == Vector(0,0,0) then --try again 5 times if we get nil or 0,0,0 as our destination for i=0,4,1 do teleportDestination = validNodes[math.random(#validNodes)] if teleportDestination ~= nil and teleportDestination ~= Vector(0,0,0) then break end end end activator:SetLocalOrigin(teleportDestination) elseif type(next(priorityNodes)) ~= "nil" and activator.m_iClass ~= 5 then local teleportDestination = validNodes[math.random(#priorityNodes)] if teleportDestination == nil or teleportDestination == Vector(0,0,0) then --try again 5 times if we get nil or 0,0,0 as our destination for i=0,4,1 do teleportDestination = validNodes[math.random(#validNodes)] if teleportDestination ~= nil and teleportDestination ~= Vector(0,0,0) then break end end end activator:SetLocalOrigin(teleportDestination) else local activatorMedigun = activator:GetPlayerItemBySlot(1) activatorHealTarget = activatorMedigun.m_hLastHealingTarget if activatorHealTarget == nil then activatorHealTarget = activatorMedigun.m_hHealingTarget end if activatorHealTarget ~= nil then activator:SetLocalOrigin(activatorHealTarget:GetAbsOrigin()) else activator:SetLocalOrigin(validNodes[math.random(#validNodes)]) end end activator.m_bGlowEnabled = 1 callbacks.died = activator:AddCallback(ON_DEATH, function() terminate() end) callbacks.removed = activator:AddCallback(ON_REMOVE, function() terminate() end) callbacks.spawned = activator:AddCallback(ON_SPAWN, function() terminate() end) end) end --Prioritizes teleporting onto the closest living teammate function GreySpawnClient_Red_Boss(_, activator) local callbacks = {} local function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end local function terminate() activator.m_bGlowEnabled = 0 removeCallbacks(activator, callbacks) end local despawnDuration = activator:GetAttributeValue("cannot_transmute") local warningDuration = activator:GetAttributeValue("non economy") activator:SetAttributeValue("not solid to players", 1) if warningDuration == nil then warningDuration = 2 end activator:SetAttributeValue("ignored by bots", 1) GREYSPAWNER_enableWarning(warningDuration) if despawnDuration ~= nil then timer.Simple(despawnDuration, function() GreyDespawn(activator) end) end timer.Simple(warningDuration, function() local validNodes = GREYSPAWNER_getValidNodes() --This shouldn't be able to happen, but if it does, die. if type(next(validNodes)) == "nil" then activator:BotCommand("Despawn") return end GREYSPAWNER_requestSound(activator.m_bIsMiniBoss) activator:SetAttributeValue("ignored by bots", 0) activator:SetAttributeValue("not solid to players", 0) if activator.m_iClass ~= 5 then local allPlayers = ents.GetAllPlayers() --Table to contain the players local targetTeam = {} local teleportDestination --Pick out our target players for _, player in pairs(allPlayers) do if player.m_iTeamNum == activator.m_iTeamNum then if player:IsAlive() == true then table.insert(targetTeam, player) end end end --Necessary, because it is possible to get an empty targetTeam table in the event of a teamwipe. --This is more graceful than hitting an error when it comes time to pick the fulcrum. if next(targetTeam) == nil then teleportDestination = validNodes[math.random(#validNodes)] else local lowestNodeToPlayerDistance = 9999 local winningNode for _, player in pairs(targetTeam) do local playerLocation = player:GetAbsOrigin() for index, Node in ipairs(validNodes) do if Node:Distance(playerLocation) < lowestNodeToPlayerDistance then lowestNodeToPlayerDistance = Node:Distance(playerLocation) winningNode = Node end end end teleportDestination = winningNode end activator:SetLocalOrigin(teleportDestination) else local activatorMedigun = activator:GetPlayerItemBySlot(1) activatorHealTarget = activatorMedigun.m_hLastHealingTarget if activatorHealTarget == nil then activatorHealTarget = activatorMedigun.m_hHealingTarget end if activatorHealTarget ~= nil then activator:SetLocalOrigin(activatorHealTarget:GetAbsOrigin()) else activator:SetLocalOrigin(validNodes[math.random(#validNodes)]) end end activator.m_bGlowEnabled = 1 callbacks.died = activator:AddCallback(ON_DEATH, function() terminate() end) callbacks.removed = activator:AddCallback(ON_REMOVE, function() terminate() end) callbacks.spawned = activator:AddCallback(ON_SPAWN, function() terminate() end) end) end --in case of weird scenarios where a bot somehow spawns again, without triggering the remove, spawn, or death callbacks. --Might be server lag dependent? AddEventCallback("mvm_reset_stats", function () local allPlayers = ents.GetAllPlayers() for _, player in pairs(allPlayers) do player.m_bGlowEnabled = 0 end end)