local CLASS_INDEX = { [1] = "Scout", [2] = "Soldier", [3] = "Pyro", [4] = "Demoman", [5] = "Heavyweapons", [6] = "Engineer", [7] = "Medic", [8] = "Sniper", [9] = "Spy", } local TANK_INDEX = 10 local TEAM_TOGGLE_INDEX = 11 local GATEKEEP = true local ALLOWED_STEAM_IDs = { [76561198835403557] = true, -- royal [76561198071835699] = true, -- timey } local function getSortTokens(name) local tokens = {} for part in name:gmatch("[^_]+") do for word in part:gmatch("[A-Z][a-z0-9]*") do table.insert(tokens, word) end end return tokens end local function spawnBotFromSpawner(clonedTemplate, teamNum, activator) if teamNum == TEAM_RED then if clonedTemplate.Conds then Extend(clonedTemplate.Conds, { TF_COND_REPROGRAMMED }) else clonedTemplate.Conds = { TF_COND_REPROGRAMMED } end end local botSpawn = SpawnBot(clonedTemplate) if not botSpawn then return botSpawn end if teamNum == TEAM_RED then local origin = activator:GetAbsOrigin() botSpawn:SetAbsOrigin(origin + (origin:GetForward() * 10)) -- medic sticks to spawner until the spawner is dead if clonedTemplate.ClassIndex == TF_CLASS_MEDIC then SquadMedicBehavior(botSpawn, activator, clonedTemplate) end end return botSpawn end function Menu_ChooseTemplate(activator, class, teamNum) local classTemplates = {} local isTank = class == "Tank" if isTank then for name, template in pairs(TEMPLATES) do if template.IsTank then classTemplates[name] = DeepClone(template) end end else for name, template in pairs(TEMPLATES) do if template.Class == class then classTemplates[name] = DeepClone(template) end end end local title if teamNum == TEAM_BLUE then title = "Template - BLU" elseif teamNum == TEAM_RED then title = "Template - RED" else title = "Template - " end local menu = { timeout = 0, title = title, itemsPerPage = 10, flags = MENUFLAG_BUTTON_EXIT, onSelect = function(_player, _index, templateName) if isTank then if templateName == "_Default" then SpawnTank({ Health = 10000, Speed = 75, TeamNum = teamNum, }, true) else SpawnTank(classTemplates[templateName]) end else local template = classTemplates[templateName] local botSpawn = spawnBotFromSpawner(template, teamNum, activator) if template.Mission and template.Mission == BOT_MISSIONS.MISSION_DESTROY_SENTRIES then local sentry = ents.FindByClass("obj_sentrygun") if botSpawn and sentry and IsValid(sentry) then botSpawn:RunScriptCode("activator.SetMissionTarget(caller)", botSpawn, sentry) end end end Menu_ChooseTemplate(activator, class, teamNum) end, onCancel = function() timer.Simple(0.015, function() Menu_ChooseClass(activator, {}, teamNum) end) end, } local sortedTemplates = {} for name in pairs(classTemplates) do table.insert(sortedTemplates, name) end -- sort alphabetically table.sort(sortedTemplates, function(a, b) local ta = getSortTokens(a) local tb = getSortTokens(b) local n = math.max(#ta, #tb) for i = 1, n do local wa, wb = ta[i], tb[i] if wa == nil then return true elseif wb == nil then return false end if wa ~= wb then return wa < wb end end return a < b end) if isTank then menu[1] = { text = "Default", value = "_Default", disabled = false, } end for i, name in ipairs(sortedTemplates) do menu[i] = { text = name, value = name, disabled = false, } end activator:DisplayMenu(menu) end -- menu function Menu_ChooseClass(activator, choiceInfo, teamNum) local title if teamNum == TEAM_BLUE then title = "Choose Class - BLU" elseif teamNum == TEAM_RED then title = "Choose Class - RED" else title = "Choose Class - " end local menu = { timeout = 0, title = title, itemsPerPage = 11, flags = MENUFLAG_BUTTON_EXIT, onSelect = function(player, index) if index == TANK_INDEX then if TEMPLATES then Menu_ChooseTemplate(activator, "Tank", teamNum) else SpawnTank({ Health = 10000, Speed = 75, }, true) Menu_ChooseClass(activator, {}, teamNum) end return elseif index == TEAM_TOGGLE_INDEX then if teamNum == TEAM_BLUE then teamNum = TEAM_RED else teamNum = TEAM_BLUE end Menu_ChooseClass(activator, {}, teamNum) return end if TEMPLATES then Menu_ChooseTemplate(activator, CLASS_INDEX[index], teamNum) else choiceInfo.ClassIndex = index choiceInfo.Class = CLASS_INDEX[choiceInfo.ClassIndex] spawnBotFromSpawner(choiceInfo, teamNum, activator) Menu_ChooseClass(activator, {}, teamNum) end end, onCancel = nil, } for index, className in pairs(CLASS_INDEX) do menu[index] = { text = className, value = index, disabled = false } end menu[TANK_INDEX] = { text = "Tank", value = TANK_INDEX, disabled = false } local teamText if teamNum == TEAM_BLUE then teamText = "Set Team to RED" else teamText = "Set Team to BLU" end menu[TEAM_TOGGLE_INDEX] = { text = teamText, value = TEAM_TOGGLE_INDEX, disabled = false } activator:DisplayMenu(menu) end -- ran externally function BotSpawnerPromptMenu(_, activator) if GATEKEEP then if not ALLOWED_STEAM_IDs[activator:GetSteamId()] then print("disallowed player from using bot spawner") return end end Menu_ChooseClass(activator, {}, TEAM_BLUE) end