Initial commit - RefoselBots

This commit is contained in:
Refosel
2026-03-28 14:49:46 +03:00
commit 4ee71d6bcb
16 changed files with 1577 additions and 0 deletions

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# RefoselBots
Bot system designed specifically for RefoselTeamWork gamemodes.
## Features
- Team-based AI bots for Team Deathmatch
- NextBot navigation system
- Quota system (auto-balance bot count)
- VJ Base weapon support
## Console Commands
- `refoselbots_add [amount]` - Add bots
- `refoselbots_kick [name/all]` - Kick bots
- `refoselbots_quota [number]` - Auto-balance bots
## Installation
1. Copy `refoselbots` folder to `garrysmod/addons`
2. Ensure navmesh is generated on your map (`nav_generate`)
3. Add bots with `refoselbots_add` or set quota
---
## Credits
### Original Code
**LeadBot** - https://github.com/LeadKiller/leadbot

View File

@@ -0,0 +1,15 @@
--if game.SinglePlayer() or SERVER then return end
-- Modules
local _, dir = file.Find("refoselbots/modules/*", "LUA")
for k, v in pairs(dir) do
local f = table.Add(file.Find("refoselbots/modules/" .. v .. "/cl_*.lua", "LUA"), file.Find("refoselbots/modules/" .. v .. "/sh_*.lua", "LUA"))
for i, o in pairs(f) do
local file = "refoselbots/modules/" .. v .. "/" .. o
include(file)
end
end

View File

@@ -0,0 +1,59 @@
--if game.SinglePlayer() or CLIENT then return end
LeadBot = {}
LeadBot.NoNavMesh = {}
LeadBot.Models = {} -- Models, leave as {} if random is desired
--[[-----
CONFIG START CONFIG START
CONFIG START CONFIG START
CONFIG START CONFIG START
--]]-----
-- Name Prefix
LeadBot.Prefix = ""
--[[-----
CONFIG END CONFIG END
CONFIG END CONFIG END
CONFIG END CONFIG END
--]]-----
include("refoselbots/base.lua")
-- Modules
local _, dir = file.Find("refoselbots/modules/*", "LUA")
for k, v in pairs(dir) do
local f = table.Add(file.Find("refoselbots/modules/" .. v .. "/sv_*.lua", "LUA"), file.Find("refoselbots/modules/" .. v .. "/sh_*.lua", "LUA"))
f = table.Add(f, file.Find("refoselbots/modules/" .. v .. "/cl_*.lua", "LUA"))
for i, o in pairs(f) do
local file = "refoselbots/modules/" .. v .. "/" .. o
if string.StartWith(o, "cl_") then
AddCSLuaFile(file)
else
include(file)
if string.StartWith(o, "sh_") then
AddCSLuaFile(file)
end
end
end
end
-- Configs
local map = game.GetMap()
local gamemode = engine.ActiveGamemode()
if file.Find("refoselbots/gamemodes/" .. map .. ".lua", "LUA")[1] then
include("refoselbots/gamemodes/" .. map .. ".lua")
elseif file.Find("refoselbots/gamemodes/" .. gamemode .. ".lua", "LUA")[1] then
include("refoselbots/gamemodes/" .. gamemode .. ".lua")
end

View File

@@ -0,0 +1,125 @@
if SERVER then AddCSLuaFile() end
ENT.Base = "base_nextbot"
ENT.Type = "nextbot"
function ENT:Initialize()
if CLIENT then return end
self:SetModel("models/player.mdl")
self:SetNoDraw(!GetConVar("developer"):GetBool())
self:SetSolid(SOLID_NONE)
local fov_convar = GetConVar("refoselbots_fov")
self:SetFOV((fov_convar:GetBool() and math.Clamp(fov_convar:GetInt(), 75, 100)) or 90)
self.PosGen = nil
self.NextJump = -1
self.NextDuck = 0
self.cur_segment = 2
self.Target = nil
self.LastSegmented = 0
self.ForgetTarget = 0
self.NextCenter = 0
self.LookAt = Angle(0, 0, 0)
self.LookAtTime = 0
self.goalPos = Vector(0, 0, 0)
self.strafeAngle = 0
self.nextStuckJump = 0
if RefoselBots.AddControllerOverride then
RefoselBots.AddControllerOverride(self)
end
end
function ENT:ChasePos()
self.P = Path("Follow")
self.P:SetMinLookAheadDistance(10)
self.P:SetGoalTolerance(20)
self.P:Compute(self, self.PosGen)
if !self.P:IsValid() then return end
while self.P:IsValid() do
if self.PosGen then
self.P:Compute(self, self.PosGen)
self.cur_segment = 2
end
coroutine.wait(1)
coroutine.yield()
end
end
function ENT:OnInjured()
return false
end
function ENT:OnKilled()
return false
end
function ENT:IsNPC()
return false
end
function ENT:Health()
return nil
end
-- remade this in lua so we can finally ignore the controller's bot
-- for some reason it's not really possible to overwrite IsAbleToSee
local function PointWithinViewAngle(pos, targetpos, lookdir, fov)
pos = targetpos - pos
local diff = lookdir:Dot(pos)
if diff < 0 then return false end
local len = pos:LengthSqr()
return diff * diff > len * fov * fov
end
function ENT:InFOV(pos, fov)
local owner = self:GetOwner()
if IsEntity(pos) then
-- we must check eyepos and worldspacecenter
-- maybe in the future add more points
if PointWithinViewAngle(owner:EyePos(), pos:WorldSpaceCenter(), owner:GetAimVector(), fov) then
return true
end
return PointWithinViewAngle(owner:EyePos(), pos:EyePos(), owner:GetAimVector(), fov)
else
return PointWithinViewAngle(owner:EyePos(), pos, owner:GetAimVector(), fov)
end
end
function ENT:CanSee(ply, fov)
if ply:GetPos():DistToSqr(self:GetPos()) > self:GetMaxVisionRange() * self:GetMaxVisionRange() then
return false
end
-- TODO: check fog farz and compare with distance
-- half fov or something
-- probably should move this to a variable
fov = fov or true
if fov and !self:InFOV(ply, math.cos(0.5 * (self:GetFOV() or 90) * math.pi / 180)) then
return false
end
-- TODO: we really should check worldspacecenter too
local owner = self:GetOwner()
return util.QuickTrace(owner:EyePos(), ply:EyePos() - owner:EyePos(), {owner, self}).Entity == ply
end
function ENT:RunBehaviour()
while (true) do
if self.PosGen then
self:ChasePos({})
end
coroutine.yield()
end
end

632
lua/refoselbots/base.lua Normal file
View File

@@ -0,0 +1,632 @@
RefoselBots.RespawnAllowed = true -- allows bots to respawn automatically when dead
RefoselBots.PlayerColor = true -- disable this to get the default gmod style players
RefoselBots.NoNavMesh = false -- disable the nav mesh check
RefoselBots.TeamPlay = false -- don't hurt players on the bots team
RefoselBots.LerpAim = true -- interpolate aim (smooth aim)
RefoselBots.AFKBotOverride = false -- allows for gamemodes such as Dogfight which use IsBot() to pass real humans as bots
RefoselBots.SuicideAFK = false -- kill the player when entering/exiting afk
RefoselBots.NoFlashlight = false -- disable flashlight being enabled in dark areas
RefoselBots.Strategies = 1 -- how many strategies can the bot pick from
--[[ COMMANDS ]]--
concommand.Add("refoselbots_add", function(ply, _, args) if IsValid(ply) and !ply:IsSuperAdmin() then return end local amount = 1 if tonumber(args[1]) then amount = tonumber(args[1]) end for i = 1, amount do timer.Simple(i * 0.1, function() RefoselBots.AddBot() end) end end, nil, "Adds a RefoselBots")
concommand.Add("refoselbots_kick", function(ply, _, args) if !args[1] or IsValid(ply) and !ply:IsSuperAdmin() then return end if args[1] ~= "all" then for k, v in pairs(player.GetBots()) do if string.find(v:GetName(), args[1]) then v:Kick() return end end else for k, v in pairs(player.GetBots()) do v:Kick() end end end, nil, "Kicks RefoselBotss (all is avaliable!)")
CreateConVar("refoselbots_strategy", "1", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "Enables the strategy system for newly created bots.")
CreateConVar("refoselbots_names", "", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "Bot names, seperated by commas.")
CreateConVar("refoselbots_models", "", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "Bot models, seperated by commas.")
CreateConVar("refoselbots_name_prefix", "", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "Bot name prefix")
CreateConVar("refoselbots_fov", "0", {FCVAR_ARCHIVE, FCVAR_NOTIFY}, "RefoselBots FOV\nSet to 0 to use the preset FOV.")
--[[ FUNCTIONS ]]--
local name_Default = {
alyx = "Alyx Vance",
kleiner = "Isaac Kleiner",
breen = "Dr. Wallace Breen",
gman = "The G-Man",
odessa = "Odessa Cubbage",
eli = "Eli Vance",
monk = "Father Grigori",
mossman = "Judith Mossman",
mossmanarctic = "Judith Mossman",
barney = "Barney Calhoun",
dod_american = "American Soldier",
dod_german = "German Soldier",
css_swat = "GIGN",
css_leet = "Elite Crew",
css_arctic = "Artic Avengers",
css_urban = "SEAL Team Six",
css_riot = "GSG-9",
css_gasmask = "SAS",
css_phoenix = "Phoenix Connexion",
css_guerilla = "Guerilla Warfare",
hostage01 = "Art",
hostage02 = "Sandro",
hostage03 = "Vance",
hostage04 = "Cohrt",
police = "Civil Protection",
policefem = "Civil Protection",
chell = "Chell",
combine = "Combine Soldier",
combineprison = "Combine Prison Guard",
combineelite = "Elite Combine Soldier",
stripped = "Stripped Combine Soldier",
zombie = "Zombie",
zombiefast = "Fast Zombie",
zombine = "Zombine",
corpse = "Corpse",
charple = "Charple",
skeleton = "Skeleton",
male01 = "Van",
male02 = "Ted",
male03 = "Joe",
male04 = "Eric",
male05 = "Art",
male06 = "Sandro",
male07 = "Mike",
male08 = "Vance",
male09 = "Erdin",
male10 = "Van",
male11 = "Ted",
male12 = "Joe",
male13 = "Eric",
male14 = "Art",
male15 = "Sandro",
male16 = "Mike",
male17 = "Vance",
male18 = "Erdin",
female01 = "Joey",
female02 = "Kanisha",
female03 = "Kim",
female04 = "Chau",
female05 = "Naomi",
female06 = "Lakeetra",
female07 = "Joey",
female08 = "Kanisha",
female09 = "Kim",
female10 = "Chau",
female11 = "Naomi",
female12 = "Lakeetra",
medic01 = "Van",
medic02 = "Ted",
medic03 = "Joe",
medic04 = "Eric",
medic05 = "Art",
medic06 = "Sandro",
medic07 = "Mike",
medic08 = "Vance",
medic09 = "Erdin",
medic10 = "Joey",
medic11 = "Kanisha",
medic12 = "Kim",
medic13 = "Chau",
medic14 = "Naomi",
medic15 = "Lakeetra",
refugee01 = "Ted",
refugee02 = "Eric",
refugee03 = "Sandro",
refugee04 = "Vance",
}
function RefoselBots.AddBot()
if !FindMetaTable("NextBot").GetFOV then
ErrorNoHalt("You must be using the dev version of Garry's mod!\nhttps://wiki.facepunch.com/gmod/Dev_Branch\n")
return
end
if !navmesh.IsLoaded() and !RefoselBots.NoNavMesh then
ErrorNoHalt("There is no navmesh! Generate one using \"nav_generate\"!\n")
return
end
if player.GetCount() == game.MaxPlayers() then
MsgN("[RefoselBots] Player limit reached!")
return
end
local original_name
local generated = "Leadbot #" .. #player.GetBots() + 1
local model = ""
local color = Vector(-1, -1, -1)
local weaponcolor = Vector(0.30, 1.80, 2.10)
local strategy = 0
if GetConVar("refoselbots_names"):GetString() ~= "" then
generated = table.Random(string.Split(GetConVar("refoselbots_names"):GetString(), ","))
elseif GetConVar("refoselbots_models"):GetString() == "" then
local name, _ = table.Random(player_manager.AllValidModels())
local translate = player_manager.TranslateToPlayerModelName(name)
name = translate
for _, ply in pairs(player.GetBots()) do
if ply.OriginalName == name or string.lower(ply:Nick()) == name or name_Default[name] and ply:Nick() == name_Default[name] then
name = ""
end
end
if name == "" then
local i = 0
while name == "" do
i = i + 1
local str = player_manager.TranslateToPlayerModelName(table.Random(player_manager.AllValidModels()))
for _, ply in pairs(player.GetBots()) do
if ply.OriginalName == str or string.lower(ply:Nick()) == str or name_Default[str] and ply:Nick() == name_Default[str] then
str = ""
end
end
if str == "" and i < #player_manager.AllValidModels() then continue end
name = str
end
end
original_name = name
model = name
name = string.lower(name)
name = name_Default[name] or name
local name_Generated = string.Split(name, "/")
name_Generated = name_Generated[#name_Generated]
name_Generated = string.Split(name_Generated, " ")
for i, namestr in pairs(name_Generated) do
name_Generated[i] = string.upper(string.sub(namestr, 1, 1)) .. string.sub(namestr, 2)
end
name_Generated = table.concat(name_Generated, " ")
generated = name_Generated
end
if RefoselBots.PlayerColor == "default" then
generated = "Kleiner"
end
generated = GetConVar("refoselbots_name_prefix"):GetString() .. generated
local name = RefoselBots.Prefix .. generated
local bot = player.CreateNextBot(name)
if !IsValid(bot) then
MsgN("[RefoselBots] Unable to create bot!")
return
end
if GetConVar("refoselbots_strategy"):GetBool() then
strategy = math.random(0, RefoselBots.Strategies)
end
if RefoselBots.PlayerColor ~= "default" then
if model == "" then
if GetConVar("refoselbots_models"):GetString() ~= "" then
model = table.Random(string.Split(GetConVar("refoselbots_models"):GetString(), ","))
else
model = player_manager.TranslateToPlayerModelName(table.Random(player_manager.AllValidModels()))
end
end
if color == Vector(-1, -1, -1) then
local botcolor = ColorRand()
local botweaponcolor = ColorRand()
color = Vector(botcolor.r / 255, botcolor.g / 255, botcolor.b / 255)
weaponcolor = Vector(botweaponcolor.r / 255, botweaponcolor.g / 255, botweaponcolor.b / 255)
end
else
model = "kleiner"
color = Vector(0.24, 0.34, 0.41)
end
bot.RefoselBots_Config = {model, color, weaponcolor, strategy}
-- for legacy purposes, will be removed soon when gamemodes are updated
bot.BotStrategy = strategy
bot.OriginalName = original_name
bot.ControllerBot = ents.Create("refoselbots_navigator")
bot.ControllerBot:Spawn()
bot.ControllerBot:SetOwner(bot)
bot.RefoselBots = true
RefoselBots.AddBotOverride(bot)
RefoselBots.AddBotControllerOverride(bot, bot.ControllerBot)
MsgN("[RefoselBots] Bot " .. name .. " with strategy " .. bot.BotStrategy .. " added!")
end
--[[ DEFAULT DM AI ]]--
function RefoselBots.AddBotOverride(bot)
if math.random(2) == 1 then
timer.Simple(math.random(1, 4), function()
RefoselBots.TalkToMe(bot, "join")
end)
end
end
function RefoselBots.AddBotControllerOverride(bot, controller)
end
function RefoselBots.PlayerSpawn(bot)
end
function RefoselBots.Think()
for _, bot in pairs(player.GetBots()) do
if bot:IsLBot() then
if RefoselBots.RespawnAllowed and bot.NextSpawnTime and !bot:Alive() and bot.NextSpawnTime < CurTime() then
bot:Spawn()
return
end
local wep = bot:GetActiveWeapon()
if IsValid(wep) then
local ammoty = wep:GetPrimaryAmmoType() or wep.Primary.Ammo
bot:SetAmmo(999, ammoty)
end
end
end
end
function RefoselBots.PostPlayerDeath(bot)
end
function RefoselBots.PlayerHurt(ply, bot, hp, dmg)
if bot:IsPlayer() then
if hp <= dmg and math.random(3) == 1 and bot:IsLBot() then
RefoselBots.TalkToMe(bot, "taunt")
end
local controller = ply:GetController()
controller.LookAtTime = CurTime() + 2
controller.LookAt = ((bot:GetPos() + VectorRand() * 128) - ply:GetPos()):Angle()
end
end
function RefoselBots.StartCommand(bot, cmd)
local buttons = IN_SPEED
local botWeapon = bot:GetActiveWeapon()
local controller = bot.ControllerBot
local target = controller.Target
if !IsValid(controller) then return end
if RefoselBots.NoSprint then
buttons = 0
end
if IsValid(botWeapon) and (botWeapon:Clip1() == 0 or !IsValid(target) and botWeapon:Clip1() <= botWeapon:GetMaxClip1() / 2) then
buttons = buttons + IN_RELOAD
end
if IsValid(target) and math.random(2) == 1 then
buttons = buttons + IN_ATTACK
end
if bot:GetMoveType() == MOVETYPE_LADDER then
local pos = controller.goalPos
local ang = ((pos + bot:GetCurrentViewOffset()) - bot:GetShootPos()):Angle()
if pos.z > controller:GetPos().z then
controller.LookAt = Angle(-30, ang.y, 0)
else
controller.LookAt = Angle(30, ang.y, 0)
end
controller.LookAtTime = CurTime() + 0.1
controller.NextJump = -1
buttons = buttons + IN_FORWARD
end
if controller.NextDuck > CurTime() then
buttons = buttons + IN_DUCK
elseif controller.NextJump == 0 then
controller.NextJump = CurTime() + 1
buttons = buttons + IN_JUMP
end
if !bot:IsOnGround() and controller.NextJump > CurTime() then
buttons = buttons + IN_DUCK
end
bot:SelectWeapon((IsValid(controller.Target) and controller.Target:GetPos():DistToSqr(controller:GetPos()) < 129000 and "weapon_shotgun") or "weapon_smg1")
cmd:ClearButtons()
cmd:ClearMovement()
cmd:SetButtons(buttons)
end
function RefoselBots.PlayerMove(bot, cmd, mv)
local controller = bot.ControllerBot
if !IsValid(controller) then
bot.ControllerBot = ents.Create("refoselbots_navigator")
bot.ControllerBot:Spawn()
bot.ControllerBot:SetOwner(bot)
controller = bot.ControllerBot
end
--[[local min, max = controller:GetModelBounds()
debugoverlay.Box(controller:GetPos(), min, max, 0.1, Color(255, 0, 0, 0), true)]]
-- force a recompute
if controller.PosGen and controller.P and controller.TPos ~= controller.PosGen then
controller.TPos = controller.PosGen
controller.P:Compute(controller, controller.PosGen)
end
if controller:GetPos() ~= bot:GetPos() then
controller:SetPos(bot:GetPos())
end
if controller:GetAngles() ~= bot:EyeAngles() then
controller:SetAngles(bot:EyeAngles())
end
mv:SetForwardSpeed(1200)
if (bot.NextSpawnTime and bot.NextSpawnTime + 1 > CurTime()) or !IsValid(controller.Target) or controller.ForgetTarget < CurTime() or controller.Target:Health() < 1 then
controller.Target = nil
end
if !IsValid(controller.Target) then
for _, ply in ipairs(player.GetAll()) do
if ply ~= bot and ((ply:IsPlayer() and (!RefoselBots.TeamPlay or (RefoselBots.TeamPlay and (ply:Team() ~= bot:Team())))) or ply:IsNPC()) and ply:GetPos():DistToSqr(bot:GetPos()) < 2250000 then
--[[local targetpos = ply:EyePos() - Vector(0, 0, 10)
local trace = util.TraceLine({
start = bot:GetShootPos(),
endpos = targetpos,
filter = function(ent) return ent == ply end
})]]
if ply:Alive() and controller:CanSee(ply) then
controller.Target = ply
controller.ForgetTarget = CurTime() + 2
end
end
end
elseif controller.ForgetTarget < CurTime() and controller:CanSee(controller.Target) then
controller.ForgetTarget = CurTime() + 2
end
local dt = util.QuickTrace(bot:EyePos(), bot:GetForward() * 45, bot)
if IsValid(dt.Entity) and dt.Entity:GetClass() == "prop_door_rotating" then
dt.Entity:Fire("OpenAwayFrom", bot, 0)
end
if !IsValid(controller.Target) and (!controller.PosGen or bot:GetPos():DistToSqr(controller.PosGen) < 1000 or controller.LastSegmented < CurTime()) then
-- find a random spot on the map, and in 10 seconds do it again!
controller.PosGen = controller:FindSpot("random", {radius = 12500})
controller.LastSegmented = CurTime() + 10
elseif IsValid(controller.Target) then
-- move to our target
local distance = controller.Target:GetPos():DistToSqr(bot:GetPos())
controller.PosGen = controller.Target:GetPos()
-- back up if the target is really close
-- TODO: find a random spot rather than trying to back up into what could just be a wall
-- something like controller.PosGen = controller:FindSpot("random", {pos = bot:GetPos() - bot:GetForward() * 350, radius = 1000})?
if distance <= 90000 then
mv:SetForwardSpeed(-1200)
end
end
-- movement also has a similar issue, but it's more severe...
if !controller.P then
return
end
local segments = controller.P:GetAllSegments()
if !segments then return end
local cur_segment = controller.cur_segment
local curgoal = segments[cur_segment]
-- got nowhere to go, why keep moving?
if !curgoal then
mv:SetForwardSpeed(0)
return
end
-- think every step of the way!
if segments[cur_segment + 1] and Vector(bot:GetPos().x, bot:GetPos().y, 0):DistToSqr(Vector(curgoal.pos.x, curgoal.pos.y)) < 100 then
controller.cur_segment = controller.cur_segment + 1
curgoal = segments[controller.cur_segment]
end
local goalpos = curgoal.pos
-- waaay too slow during gamplay
--[[if bot:GetVelocity():Length2DSqr() <= 225 and controller.NextCenter == 0 and controller.NextCenter < CurTime() then
controller.NextCenter = CurTime() + math.Rand(0.5, 0.65)
end
if controller.NextCenter ~= 0 and controller.NextCenter < CurTime() then
if bot:GetVelocity():Length2DSqr() <= 225 then
controller.strafeAngle = ((controller.strafeAngle == 1 and 2) or 1)
end
controller.NextCenter = 0
end]]
if bot:GetVelocity():Length2DSqr() <= 225 then
if controller.NextCenter < CurTime() then
controller.strafeAngle = ((controller.strafeAngle == 1 and 2) or 1)
controller.NextCenter = CurTime() + math.Rand(0.3, 0.65)
elseif controller.nextStuckJump < CurTime() then
if !bot:Crouching() then
controller.NextJump = 0
end
controller.nextStuckJump = CurTime() + math.Rand(1, 2)
end
end
if controller.NextCenter > CurTime() then
if controller.strafeAngle == 1 then
mv:SetSideSpeed(1500)
elseif controller.strafeAngle == 2 then
mv:SetSideSpeed(-1500)
else
mv:SetForwardSpeed(-1500)
end
end
-- jump
if controller.NextJump ~= 0 and curgoal.type > 1 and controller.NextJump < CurTime() then
controller.NextJump = 0
end
-- duck
if curgoal.area:GetAttributes() == NAV_MESH_CROUCH then
controller.NextDuck = CurTime() + 0.1
end
controller.goalPos = goalpos
if GetConVar("developer"):GetBool() then
controller.P:Draw()
end
-- eyesight
local lerp = FrameTime() * math.random(8, 10)
local lerpc = FrameTime() * 8
if !RefoselBots.LerpAim then
lerp = 1
lerpc = 1
end
local mva = ((goalpos + bot:GetCurrentViewOffset()) - bot:GetShootPos()):Angle()
mv:SetMoveAngles(mva)
if IsValid(controller.Target) then
bot:SetEyeAngles(LerpAngle(lerp, bot:EyeAngles(), (controller.Target:EyePos() - bot:GetShootPos()):Angle()))
return
else
if controller.LookAtTime > CurTime() then
local ang = LerpAngle(lerpc, bot:EyeAngles(), controller.LookAt)
bot:SetEyeAngles(Angle(ang.p, ang.y, 0))
else
local ang = LerpAngle(lerpc, bot:EyeAngles(), mva)
bot:SetEyeAngles(Angle(ang.p, ang.y, 0))
end
end
end
--[[ HOOKS ]]--
hook.Add("PlayerDisconnected", "RefoselBots_Disconnect", function(bot)
if IsValid(bot.ControllerBot) then
bot.ControllerBot:Remove()
end
end)
hook.Add("SetupMove", "RefoselBots_Control", function(bot, mv, cmd)
if bot:IsLBot() then
RefoselBots.PlayerMove(bot, cmd, mv)
end
end)
hook.Add("StartCommand", "RefoselBots_Control", function(bot, cmd)
if bot:IsLBot() then
RefoselBots.StartCommand(bot, cmd)
end
end)
hook.Add("PostPlayerDeath", "RefoselBots_Death", function(bot)
if bot:IsLBot() then
RefoselBots.PostPlayerDeath(bot)
end
end)
hook.Add("EntityTakeDamage", "RefoselBots_Hurt", function(ply, dmgi)
local bot = dmgi:GetAttacker()
local hp = ply:Health()
local dmg = dmgi:GetDamage()
if IsValid(ply) and ply:IsPlayer() and ply:IsLBot() then
RefoselBots.PlayerHurt(ply, bot, hp, dmg)
end
end)
hook.Add("Think", "RefoselBots_Think", function()
RefoselBots.Think()
end)
hook.Add("PlayerSpawn", "RefoselBots_Spawn", function(bot)
if bot:IsLBot() then
RefoselBots.PlayerSpawn(bot)
end
end)
--[[ META ]]--
local player_meta = FindMetaTable("Player")
local oldInfo = player_meta.GetInfo
function player_meta.IsLBot(self, realbotsonly)
if realbotsonly == true then
return self.RefoselBots and self:IsBot() or false
end
return self.RefoselBots or false
end
function player_meta.LBGetStrategy(self)
if self.RefoselBots_Config then
return self.RefoselBots_Config[4]
else
return 0
end
end
function player_meta.LBGetModel(self)
if self.RefoselBots_Config then
return self.RefoselBots_Config[1]
else
return "kleiner"
end
end
function player_meta.LBGetColor(self, weapon)
if self.RefoselBots_Config then
if weapon == true then
return self.RefoselBots_Config[3]
else
return self.RefoselBots_Config[2]
end
else
return Vector(0, 0, 0)
end
end
function player_meta.GetInfo(self, convar)
if self:IsBot() and self:IsLBot() then
if convar == "cl_playermodel" then
return self:LBGetModel() --self.RefoselBots_Config[1]
elseif convar == "cl_playercolor" then
return self:LBGetColor() --self.RefoselBots_Config[2]
elseif convar == "cl_weaponcolor" then
return self:LBGetColor(true) --self.RefoselBots_Config[3]
else
return ""
end
else
return oldInfo(self, convar)
end
end
function player_meta.GetController(self)
if self:IsLBot() then
return self.ControllerBot
end
end

View File

@@ -0,0 +1,6 @@
LeadBot.RespawnAllowed = true
LeadBot.SetModel = false
LeadBot.Gamemode = "cod_custom"
LeadBot.TeamPlay = true
LeadBot.LerpAim = true
LeadBot.NoFlashlight = true

View File

@@ -0,0 +1,139 @@
surface.CreateFont("LeadBot_AFK", {
font = "Roboto",
size = 34,
weight = 500
})
surface.CreateFont("LeadBot_AFK2", {
font = "Roboto",
size = 30,
weight = 500
})
hook.Add("HUDPaint", "AFKT", function()
if LocalPlayer():GetNWBool("LeadBot_AFK") then
draw.SimpleTextOutlined("You are AFK.", "LeadBot_AFK", ScrW() / 2, ScrH() / 2.85, Color(255, 255, 255, 235), TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, 1, Color(0, 0, 0, 255))
draw.SimpleTextOutlined("Press any key to rejoin.", "LeadBot_AFK2", ScrW() / 2, ScrH() / 2.675, Color(255, 255, 255, 235), TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, 1, Color(0, 0, 0, 255))
end
end)
local tp = false
local last_TP = false
local newangle = Angle(0, 0, 0)
local scroll = 0
local lastsend = CurTime()
local tp_Gamemodes = {}
local NoAFKCamera = {}
tp_Gamemodes["sandbox"] = true
tp_Gamemodes["darkestdays"] = true
NoAFKCamera["assassins"] = true
NoAFKCamera["cavefight"] = true
hook.Add("CreateMove", "LeadBot_AFK", function(cmd)
local ply = LocalPlayer()
if ply:GetNWBool("LeadBot_AFK") then
if lastsend < CurTime() and cmd:GetButtons() ~= 0 and !cmd:KeyDown(IN_ATTACK) and !cmd:KeyDown(IN_WALK) and !cmd:KeyDown(IN_SCORE) then
net.Start("LeadBot_AFK_Off")
net.SendToServer()
lastsend = CurTime() + 0.1
end
if cmd:KeyDown(IN_ATTACK) and tp_Gamemodes[engine.ActiveGamemode()] then
if !last_TP then
tp = !tp
last_TP = true
end
else
last_TP = false
end
if input.WasMousePressed(MOUSE_WHEEL_DOWN) then
scroll = math.Clamp(scroll + 4, -35, 45)
elseif input.WasMousePressed(MOUSE_WHEEL_UP) then
scroll = math.Clamp(scroll - 4, -35, 45)
end
if tp then
local s = GetConVar("m_pitch"):GetFloat()
newangle.pitch = math.Clamp(newangle.pitch + cmd:GetMouseY() * s, -90, 90)
newangle.yaw = newangle.yaw - cmd:GetMouseX() * s
end
cmd:ClearButtons()
cmd:ClearMovement()
cmd:SetImpulse(0)
cmd:SetMouseX(0)
cmd:SetMouseY(0)
end
end)
hook.Add("InputMouseApply", "LeadBot_AFK", function(cmd)
if LocalPlayer():GetNWBool("LeadBot_AFK") then
cmd:SetMouseX(0)
cmd:SetMouseY(0)
return true
end
end)
hook.Add("HUDShouldDraw", "LeadBot_AFK", function(hud)
if hud == "CHudWeaponSelection" and LocalPlayer():GetNWBool("LeadBot_AFK") then
return false
end
end)
local ang
local lerp = 0
hook.Add("CalcView", "LeadBot_AFK", function(ply, origin, angles)
if !ang then ang = angles end
if ply:ShouldDrawLocalPlayer() or !ply:GetNWBool("LeadBot_AFK") or NoAFKCamera[engine.ActiveGamemode()] then return end
local view = {}
if tp or lerp ~= 0 then
if tp then
lerp = math.Clamp(lerp + FrameTime() * 5, 0, 1)
else
lerp = math.Clamp(lerp - FrameTime() * 5, 0, 1)
end
local pos = ply:EyePos()
local trace = util.TraceHull({
start = pos + newangle:Forward() * Lerp(lerp, 0, -5),
endpos = pos + newangle:Forward() * Lerp(lerp, 0, -75 - scroll),
filter = ply,
mins = Vector(-8, -8, -8),
maxs = Vector(8, 8, 8),
})
view.angles = newangle
view.origin = trace.HitPos
view.drawviewer = true
else
ang = LerpAngle(FrameTime() * 16, ang, angles)
ang = Angle(ang.p, ang.y, 0)
newangle = ang
view.angles = ang
end
return view
end)
hook.Add("CalcViewModelView", "LeadBot_AFK", function(wep, vm, oldpos, oldang, newpos, newang)
local ply = LocalPlayer()
if ply:ShouldDrawLocalPlayer() or !ply:GetNWBool("LeadBot_AFK") or NoAFKCamera[engine.ActiveGamemode()] then return end
if wep.GetViewModelPosition then
newpos, newang = wep:GetViewModelPosition(newpos, newang)
end
if wep.CalcViewModelView then
newpos, newang = wep:CalcViewModelView(vm, oldpos, oldang, newpos, newang)
end
return newpos, ang
end)

View File

@@ -0,0 +1,73 @@
concommand.Add("refoselbots_afk", function(ply, _, args) RefoselBots.Botize(ply) end, nil, "Adds a RefoselBots ;)")
local time = CreateConVar("refoselbots_afk_timetoafk", "300", {FCVAR_ARCHIVE})
local meta = FindMetaTable("Player")
local oldFunc = meta.IsBot
util.AddNetworkString("RefoselBots_AFK_Off")
net.Receive("RefoselBots_AFK_Off", function(_, ply)
RefoselBots.Botize(ply, false)
ply.LastAFKCheck = CurTime() + time:GetFloat()
end)
hook.Add("PlayerTick", "RefoselBots_AFK", function(ply)
if !time:GetBool() then return end
ply.LastAFKCheck = ply.LastAFKCheck or CurTime() + time:GetFloat()
if ply:KeyDown(IN_FORWARD) or ply:KeyDown(IN_BACK) or ply:KeyDown(IN_MOVELEFT) or ply:KeyDown(IN_MOVERIGHT) or ply:KeyDown(IN_ATTACK) then
ply.LastAFKCheck = CurTime() + time:GetFloat()
end
if ply.LastAFKCheck < CurTime() and !ply:IsBot() and !ply:GetNWBool("RefoselBots_AFK") then
RefoselBots.Botize(ply, true)
end
end)
function RefoselBots.Botize(ply, togg)
if togg == nil then togg = !ply.RefoselBots end
if ((!togg and ply:IsLBot()) or (togg and !ply:IsLBot())) and RefoselBots.SuicideAFK and ply:Alive() then
ply:Kill()
end
if !togg then
ply:SetNWBool("RefoselBots_AFK", false)
ply.RefoselBots = false
if IsValid(ply.ControllerBot) then
ply.ControllerBot:Remove()
end
ply.LastSegmented = CurTime()
ply.CurSegment = 2
else
ply:SetNWBool("RefoselBots_AFK", true)
ply.RefoselBots = true
ply.BotColor = ply:GetPlayerColor()
ply.BotSkin = ply:GetSkin()
ply.BotModel = ply:GetModel()
ply.BotWColor = ply:GetWeaponColor()
ply.ControllerBot = ents.Create("refoselbots_navigator")
ply.ControllerBot:Spawn()
ply.ControllerBot:SetOwner(ply)
ply.ControllerBot:SetFOV(ply:GetFOV())
ply.LastSegmented = CurTime()
ply.CurSegment = 2
if GetConVar("refoselbots_strategy"):GetBool() then
ply.BotStrategy = math.random(0, RefoselBots.Strategies)
end
ply.RefoselBots_Config = {}
ply.RefoselBots_Config[1] = ply.BotModel
ply.RefoselBots_Config[2] = ply.BotColor
ply.RefoselBots_Config[3] = ply.BotWColor
ply.RefoselBots_Config[4] = ply.BotStrategy
end
end
function meta:IsBot()
if self:IsLBot() and RefoselBots.AFKBotOverride then
return true
else
return oldFunc(self)
end
end

View File

@@ -0,0 +1,111 @@
-- TODO: store avatar in dhtml png images or something, alpha fade out is weird with dmodelpanel
local meta = FindMetaTable("Panel")
local oldfunc = meta.SetPlayer
local disabledGamemodes = {}
disabledGamemodes["assassins"] = true
function meta:SetPlayer(ply, size)
if !IsValid(ply) or !ply:IsPlayer() then return end
if disabledGamemodes[engine.ActiveGamemode()] then return oldfunc(self, ply, size) end
if ply:IsBot() and ply:GetNWString("LeadBot_AvatarModel", "none") ~= "none" then
function self:Paint(w, h)
draw.RoundedBox(0, 0, 0, w, h, Color(255, 255, 255))
end
local background = vgui.Create("DPanel", self)
background:Dock(FILL)
-- background.Texture = Material("entities/monster_scientist.png")
function background:Paint(w, h)
if !ispanel(self:GetParent()) then
self:Remove()
return
end
draw.RoundedBox(0, 0, 0, w, h, Color(255, 255, 255))
--[[surface.SetMaterial(self.Texture)
surface.SetDrawColor(255, 255, 255)
surface.DrawTexturedRect(0, 0, w, h)]]
end
local model = vgui.Create("DModelPanel", self)
model:SetModel("models/player.mdl")
model:Dock(FILL)
model.Player = ply
if !ply.AvatarSeq then
ply.AvatarSeq = table.Random({"death_01", "death_02", "death_03", "death_04"})
ply.AvatarCycle = math.Rand(0.025, (ply.AvatarSeq == "death_04" and 0.06) or (ply.AvatarSeq == "death_01" and 0.3) or 0.1)
end
local playermodel = ply:GetNWString("LeadBot_AvatarModel", ply:GetModel())
function model:LayoutEntity(ent)
if !ispanel(self:GetParent()) then
self:Remove()
return
end
if !IsValid(self.Player) or !IsValid(ent) or !self.Player:IsPlayer() then return end
self.ModelCache = self.ModelCache or ""
if !ent.GetPlayerColor then
ent.Player = self.Player
function ent:GetPlayerColor()
if !IsValid(self.Player) then return Vector(1, 1, 1) end
return self.Player:GetNWVector("LeadBot_AvatarColor", self.Player:GetPlayerColor())
end
end
ent:SetRenderMode(RENDERMODE_TRANSALPHA)
ent:SetLOD(0)
-- voicechat fix
local alpha = 255
if ispanel(self:GetParent()) and ispanel(self:GetParent():GetParent()) then
alpha = self:GetParent():GetParent():GetAlpha()
end
self:SetColor(Color(255, 255, 255, alpha))
if self.ModelCache ~= playermodel then
self:SetModel(playermodel)
ent = self:GetEntity()
local seq_name = self.Player.AvatarSeq
local seq = ent:LookupSequence(seq_name or 0) -- "menu_walk")
if seq < 0 then
seq = ent:LookupSequence("menu_walk") -- "reload_dual_original")
end
ent:SetSequence(seq) -- table.Random({"swimming_duel", "zombie_slump_idle_02"})))
ent:SetCycle(self.Player.AvatarCycle)
local att = ent:LookupAttachment("eyes")
if att > 0 then
att = ent:GetAttachment(att)
if seq_name ~= "death_05" then
self:SetFOV(23)
self:SetCamPos(att.Pos + att.Ang:Forward() * 36 + att.Ang:Up() * 2)
self:SetLookAt(att.Pos - Vector(0, 0, 1))
else
self:SetFOV(27)
self:SetCamPos(att.Pos + att.Ang:Forward() * 36)
self:SetLookAt(att.Pos - Vector(0, 0, 3))
end
end
self.ModelCache = self.Player:GetModel()
end
end
else
oldfunc(self, ply, size)
end
end

View File

@@ -0,0 +1,12 @@
local oldAddBot = LeadBot.AddBotOverride
function LeadBot.AddBotOverride(bot)
oldAddBot(bot)
timer.Simple(0, function()
if IsValid(bot) then
bot:SetNWString("LeadBot_AvatarModel", player_manager.TranslatePlayerModel(bot:LBGetModel()))
bot:SetNWVector("LeadBot_AvatarColor", bot:LBGetColor())
end
end)
end

View File

@@ -0,0 +1,10 @@
net.Receive("refoselbots_Flashlight", function()
local flashlights = {}
for _, ply in pairs(player.GetAll()) do
flashlights[ply] = render.GetLightColor(ply:EyePos())
end
net.Start("refoselbots_Flashlight")
net.WriteTable(flashlights)
net.SendToServer()
end)

View File

@@ -0,0 +1,52 @@
util.AddNetworkString("refoselbots_Flashlight")
local convar = CreateConVar("refoselbots_flashlight", "1", {FCVAR_ARCHIVE}, "Flashlight for bots")
local updateply
local updatetime
local updatetime_2 = 0
hook.Add("Think", "refoselbots_Flashlight", function()
if !convar:GetBool() or RefoselBots.NoFlashlight then return end
local tab = player.GetHumans()
if updatetime_2 < CurTime() and #tab > 0 then
local ply = table.Random(tab)
updateply = ply
updatetime = CurTime() + 0.5
net.Start("refoselbots_Flashlight")
net.Send(ply)
updatetime_2 = CurTime() + 0.5
end
end)
net.Receive("refoselbots_Flashlight", function(_, ply)
if ply ~= updateply then return end
if updatetime < CurTime() then return end
local tab = net.ReadTable()
if !istable(tab) then return end
for bot, light in pairs(tab) do
bot.LastLight2 = bot.LastLight2 or 0
light = Vector(math.Round(light.x, 2), math.Round(light.y, 2), math.Round(light.z, 2))
local lighton = light == Vector(0, 0, 0)
if lighton then
bot.LastLight2 = math.Clamp(bot.LastLight2 + 1, 0, 3)
else
bot.LastLight2 = 0
end
bot.FlashlightOn = lighton and bot.LastLight2 == 3
end
end)
hook.Add("StartCommand", "refoselbots_Flashlight", function(ply, cmd)
if ply:IsLBot() and updatetime_2 - 0.1 < CurTime() and (ply.FlashlightOn and !ply:FlashlightIsOn() or !ply.FlashlightOn and ply:FlashlightIsOn()) then
cmd:SetImpulse(100)
end
end)

View File

@@ -0,0 +1,30 @@
local meta = FindMetaTable("Player")
local oldFunc = meta.Ping
local ping = 0
local fakeping = true
local convar = CreateConVar("refoselbots_fakeping", "0", {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, "Enables fakeping (Do not use this in public servers!)\n2 to make it say BOT")
function meta:Ping()
if convar:GetBool() and self:IsBot() then
if fakeping then
self.FakePing = self.FakePing or math.random(35, 105)
self.OFakePing = self.OFakePing or self.FakePing
if math.random(125) == 1 then
self.FakePing = self.OFakePing + math.random(3)
elseif math.random(125) == 1 then
self.FakePing = self.OFakePing - math.random(3)
end
if convar:GetInt() == 2 then
self.FakePing = "BOT"
end
return self.FakePing
end
return ping
else
return oldFunc(self)
end
end

View File

@@ -0,0 +1,46 @@
local convar1 = CreateConVar("refoselbots_quota", "0", {FCVAR_ARCHIVE}, "TF2 Style Quota for bots\nUse refoselbots_add if you want unkickable bots")
local nextCheck = 0
cvars.AddChangeCallback("refoselbots_quota", function(_, oldval, val)
oldval = tonumber(oldval)
val = tonumber(val)
if oldval and val and oldval > 0 and val < 1 then
RunConsoleCommand("refoselbots_kick", "all")
end
end)
hook.Add("Think", "RefoselBots_Quota", function()
if !convar1:GetBool() or RefoselBots.AFKBotOverride then return end
if nextCheck < CurTime() then
local bots = {}
local max = convar1:GetInt() - #player.GetHumans()
for _, ply in pairs(player.GetBots()) do
if ply:IsLBot(true) then
table.insert(bots, ply)
end
end
for i = 1, #bots do
if i >= convar1:GetInt() then
bots[i]:Kick()
end
end
if #bots < max then
nextCheck = CurTime() + 0.5
for i = 1, max - #bots do
timer.Simple(0.1 + (i * 0.5), function()
RefoselBots.AddBot()
end)
nextCheck = nextCheck + 0.5
end
else
nextCheck = CurTime() + 1
end
end
end)

View File

@@ -0,0 +1,67 @@
net.Receive("botVoiceStart", function()
local ply = net.ReadEntity()
local soundn = net.ReadString()
local time = SoundDuration(soundn) -- - 0.1
if !IsValid(ply) or !ply:IsBot() or (IsValid(ply.ChattingS) and ply.ChattingS:GetState() == GMOD_CHANNEL_PLAYING) then return end
-- tts tests
-- sound.PlayURL([[https://translate.google.com/translate_tts?ie=UTF-8&tl=en-us&client=tw-ob&q="]] .. voiceline .. [["]], "mono", function(station)
sound.PlayFile("sound/" .. soundn, "mono", function(station)
if IsValid(station) then
ply.ChattingS = station
station:SetPlaybackRate(math.random(95, 105) * 0.01)
station:Play()
hook.Call("PlayerStartVoice", gmod.GetGamemode(), ply)
timer.Simple(time, function()
if IsValid(ply) then
hook.Call("PlayerEndVoice", gmod.GetGamemode(), ply)
station:Stop()
--ply.ChattingB = false
end
end)
end
end)
end)
local voice = Material("voice/icntlk_pl")
-- is there no way to force this on?
hook.Add("PostPlayerDraw", "LeadBot_VoiceIcon", function(ply)
if !IsValid(ply) or !ply:IsPlayer() or !ply:IsBot() or !IsValid(ply.ChattingS) or !GetConVar("mp_show_voice_icons"):GetBool() then return end
local ang = EyeAngles()
local pos = ply:GetPos() + ply:GetCurrentViewOffset() + Vector(0, 0, 14)
ang:RotateAroundAxis(ang:Up(), -90)
ang:RotateAroundAxis(ang:Forward(), 90)
cam.Start3D2D(pos, ang, 1)
surface.SetMaterial(voice)
surface.SetDrawColor(255, 255, 255)
surface.DrawTexturedRect(-8, -8, 16, 16)
cam.End3D2D()
end)
local meta = FindMetaTable("Player")
local oldFunc = meta.VoiceVolume
local oldFunc2 = meta.IsSpeaking
function meta:VoiceVolume()
if self:IsBot() then
if IsValid(self.ChattingS) then
return self.ChattingS:GetLevel() * 0.6
else
return 0
end
else
return oldFunc(self)
end
end
function meta:IsSpeaking()
if self:IsBot() then
return IsValid(self.ChattingS) or false
else
return oldFunc2(self)
end
end

View File

@@ -0,0 +1,171 @@
util.AddNetworkString("botVoiceStart")
RefoselBots.VoicePreset = {}
RefoselBots.VoiceModels = {}
local convar = CreateConVar("refoselbots_voice", "random", {FCVAR_ARCHIVE}, "Voice Preset.\nOptions are: \n- \"random\"\n- \"" .. table.concat(table.GetKeys(RefoselBots.VoicePreset), "\"\n- \"") .. "\"")
function RefoselBots.TalkToMe(ply, type)
if !ply:IsLBot(true) then return end
local hear = {}
local sound = ""
local selectedvoice = "metropolice"
local voice = convar:GetString()
if voice == "random" then
if !ply.RefoselBots_Voice then
local model = ply:Nick()
if RefoselBots.VoiceModels[model] then
ply.RefoselBots_Voice = RefoselBots.VoiceModels[model]
else
local _, selectedplyvoice = table.Random(RefoselBots.VoicePreset)
ply.RefoselBots_Voice = selectedplyvoice
end
end
if !RefoselBots.VoicePreset[ply.RefoselBots_Voice] then
ply.RefoselBots_Voice = "metropolice"
end
selectedvoice = ply.RefoselBots_Voice
elseif RefoselBots.VoicePreset[voice] then
selectedvoice = voice
elseif voice == "" then
return
end
for k, v in pairs(player.GetAll()) do
if hook.Call("PlayerCanHearPlayersVoice", gmod.GetGamemode(), v, ply) then
table.insert(hear, v)
end
end
if type and RefoselBots.VoicePreset[selectedvoice][type] then
sound = table.Random(RefoselBots.VoicePreset[selectedvoice][type])
end
net.Start("botVoiceStart")
net.WriteEntity(ply)
net.WriteString(sound)
net.Send(hear)
end
-- Valve Games
if IsMounted("cstrike") then
RefoselBots.VoicePreset["css"] = {}
RefoselBots.VoicePreset["css"]["join"] = {"bot/its_a_party.wav", "bot/oh_boy2.wav", "bot/whoo.wav"}
RefoselBots.VoicePreset["css"]["taunt"] = {"bot/do_not_mess_with_me.wav", "bot/i_am_dangerous.wav", "bot/i_am_on_fire.wav", "bot/i_wasnt_worried_for_a_minute.wav", "bot/nice2.wav", "bot/owned.wav", "bot/made_him_cry.wav", "bot/they_never_knew_what_hit_them.wav", "bot/who_wants_some_more.wav", "bot/whos_the_man.wav", "bot/yea_baby.wav", "bot/wasted_him.wav"}
RefoselBots.VoicePreset["css"]["help"] = {"bot/help.wav", "bot/i_could_use_some_help.wav", "bot/i_could_use_some_help_over_here.wav", "bot/im_in_trouble.wav", "bot/need_help.wav", "bot/need_help2.wav", "bot/the_actions_hot_here.wav"}
RefoselBots.VoicePreset["css"]["downed"] = RefoselBots.VoicePreset["css"]["help"]
end
-- Citizens
RefoselBots.VoicePreset["male"] = {}
RefoselBots.VoicePreset["male"]["join"] = {"vo/npc/male01/hi01.wav", "vo/npc/male01/hi02.wav", "vo/npc/male01/yeah02.wav", "vo/npc/male01/squad_reinforce_single04.wav", "vo/npc/male01/squad_affirm06.wav", "vo/npc/male01/readywhenyouare01.wav", "vo/npc/male01/okimready03.wav", "vo/npc/male01/letsgo02.wav"}
RefoselBots.VoicePreset["male"]["taunt"] = {"vo/coast/odessa/male01/nlo_cheer03.wav", "vo/npc/male01/gotone02.wav", "vo/npc/male01/likethat.wav", "vo/npc/male01/nice01.wav", "vo/npc/male01/question17.wav", "vo/npc/male01/yeah02.wav", "vo/npc/male01/vquestion01.wav"}
RefoselBots.VoicePreset["male"]["pain"] = {"vo/npc/male01/help01.wav", "vo/npc/male01/startle01.wav", "vo/npc/male01/startle02.wav", "vo/npc/male01/uhoh.wav"}
RefoselBots.VoicePreset["female"] = {}
RefoselBots.VoicePreset["female"]["join"] = {"vo/npc/female01/hi01.wav", "vo/npc/female01/hi02.wav", "vo/npc/female01/yeah02.wav", "vo/npc/female01/squad_reinforce_single04.wav", "vo/npc/female01/squad_affirm06.wav", "vo/npc/female01/readywhenyouare01.wav", "vo/npc/female01/okimready03.wav", "vo/npc/female01/letsgo02.wav"}
RefoselBots.VoicePreset["female"]["taunt"] = {"vo/coast/odessa/female01/nlo_cheer03.wav", "vo/npc/female01/gotone02.wav", "vo/npc/female01/likethat.wav", "vo/npc/female01/nice01.wav", "vo/npc/female01/question17.wav", "vo/npc/female01/yeah02.wav", "vo/npc/female01/vquestion01.wav"}
RefoselBots.VoicePreset["female"]["pain"] = {"vo/npc/female01/help01.wav", "vo/npc/female01/startle01.wav", "vo/npc/female01/startle02.wav", "vo/npc/female01/uhoh.wav"}
-- Main Characters
RefoselBots.VoicePreset["alyx"] = {}
RefoselBots.VoicePreset["alyx"]["join"] = {"vo/npc/alyx/al_excuse03.wav", "vo/npc/alyx/getback01.wav", "vo/npc/alyx/lookout01.wav", "vo/npc/alyx/getback02.wav"}
RefoselBots.VoicePreset["alyx"]["taunt"] = {"vo/npc/alyx/al_excuse03.wav", "vo/npc/alyx/lookout01.wav", "vo/npc/alyx/lookout03.wav", "vo/npc/alyx/brutal02.wav", "vo/npc/alyx/youreload02.wav"}
RefoselBots.VoicePreset["alyx"]["pain"] = {"vo/npc/alyx/gasp03.wav", "vo/npc/alyx/ohgod01.wav", "vo/npc/alyx/ohno_startle01.wav"}
RefoselBots.VoicePreset["barney"] = {}
RefoselBots.VoicePreset["barney"]["join"] = {"vo/npc/barney/ba_ohyeah.wav", "vo/npc/barney/ba_yell.wav", "vo/npc/barney/ba_bringiton.wav"}
RefoselBots.VoicePreset["barney"]["taunt"] = {"vo/npc/barney/ba_downyougo.wav", "vo/npc/barney/ba_losttouch.wav", "vo/npc/barney/ba_yell.wav", "vo/npc/barney/ba_getaway.wav"}
RefoselBots.VoicePreset["barney"]["help"] = {"vo/npc/barney/ba_no01.wav", "vo/npc/barney/ba_no02.wav", "vo/npc/barney/ba_damnit.wav"}
RefoselBots.VoicePreset["grigori"] = {}
RefoselBots.VoicePreset["grigori"]["join"] = {"vo/ravenholm/monk_death07.wav", "vo/ravenholm/exit_nag02.wav", "vo/ravenholm/engage04.wav", "vo/ravenholm/engage05.wav", "vo/ravenholm/engage06.wav", "vo/ravenholm/engage07.wav", "vo/ravenholm/engage08.wav", "vo/ravenholm/engage09.wav"}
RefoselBots.VoicePreset["grigori"]["taunt"] = {"vo/ravenholm/madlaugh01.wav", "vo/ravenholm/madlaugh02.wav", "vo/ravenholm/madlaugh03.wav", "vo/ravenholm/madlaugh04.wav", "vo/ravenholm/monk_kill01.wav", "vo/ravenholm/monk_kill02.wav", "vo/ravenholm/monk_kill03.wav", "vo/ravenholm/monk_kill04.wav", "vo/ravenholm/monk_kill05.wav", "vo/ravenholm/monk_kill06.wav", "vo/ravenholm/monk_kill07.wav", "vo/ravenholm/monk_kill08.wav", "vo/ravenholm/monk_kill09.wav", "vo/ravenholm/firetrap_vigil.wav"}
RefoselBots.VoicePreset["grigori"]["pain"] = {"vo/ravenholm/monk_pain12.wav", "vo/ravenholm/monk_rant13.wav", "vo/ravenholm/monk_pain06.wav", "vo/ravenholm/engage08.wav"}
-- Enemies
RefoselBots.VoicePreset["metropolice"] = {}
RefoselBots.VoicePreset["metropolice"]["join"] = {"npc/metropolice/vo/lookingfortrouble.wav", "npc/metropolice/vo/pickupthecan1.wav", "npc/metropolice/vo/youwantamalcomplianceverdict.wav", "npc/metropolice/vo/unitisonduty10-8.wav", "npc/metropolice/vo/unitis10-8standingby.wav", "npc/metropolice/vo/readytoamputate.wav", "npc/metropolice/vo/prepareforjudgement.wav", "npc/metropolice/vo/readytoprosecutefinalwarning.wav"}
RefoselBots.VoicePreset["metropolice"]["taunt"] = {"npc/metropolice/vo/finalverdictadministered.wav", "npc/metropolice/vo/firstwarningmove.wav", "npc/metropolice/vo/isaidmovealong.wav", "npc/metropolice/vo/nowgetoutofhere.wav", "npc/metropolice/vo/pickupthecan2.wav", "npc/metropolice/vo/pickupthecan3.wav", "npc/metropolice/vo/putitinthetrash1.wav", "npc/metropolice/vo/putitinthetrash2.wav", "npc/metropolice/vo/suspectisbleeding.wav", "npc/metropolice/vo/thisisyoursecondwarning.wav"}
RefoselBots.VoicePreset["metropolice"]["pain"] = {"npc/metropolice/vo/11-99officerneedsassistance.wav", "npc/metropolice/vo/wehavea10-108.wav", "npc/metropolice/vo/runninglowonverdicts.wav", "npc/metropolice/pain4.wav"}
-- Player Config
RefoselBots.VoiceModels["Alyx Vance"] = "alyx"
RefoselBots.VoiceModels["Isaac Kleiner"] = "male"
RefoselBots.VoiceModels["Dr. Wallace Breen"] = "male"
RefoselBots.VoiceModels["The G-Man"] = "male"
RefoselBots.VoiceModels["Odessa Cubbage"] = "male"
RefoselBots.VoiceModels["Eli Vance"] = "male"
RefoselBots.VoiceModels["Father Grigori"] = "grigori"
RefoselBots.VoiceModels["Judith Mossman"] = "female"
RefoselBots.VoiceModels["Barney Calhoun"] = "barney"
RefoselBots.VoiceModels["Magnusson"] = "male"
RefoselBots.VoiceModels["American Soldier"] = "male"
RefoselBots.VoiceModels["German Soldier"] = "male"
RefoselBots.VoiceModels["GIGN"] = "css"
RefoselBots.VoiceModels["Elite Crew"] = "css"
RefoselBots.VoiceModels["Artic Avengers"] = "css"
RefoselBots.VoiceModels["SEAL Team Six"] = "css"
RefoselBots.VoiceModels["GSG-9"] = "css"
RefoselBots.VoiceModels["SAS"] = "css"
RefoselBots.VoiceModels["Phoenix Connexion"] = "css"
RefoselBots.VoiceModels["Guerilla Warfare"] = "css"
RefoselBots.VoiceModels["Cohrt"] = "male"
RefoselBots.VoiceModels["Chell"] = "female"
RefoselBots.VoiceModels["Civil Protection"] = "metropolice"
RefoselBots.VoiceModels["Combine Soldier"] = "metropolice"
RefoselBots.VoiceModels["Combine Prison Guard"] = "metropolice"
RefoselBots.VoiceModels["Elite Combine Soldier"] = "metropolice"
RefoselBots.VoiceModels["Stripped Combine Soldier"] = "metropolice"
RefoselBots.VoiceModels["Zombie"] = "css"
RefoselBots.VoiceModels["Fast Zombie"] = "css"
RefoselBots.VoiceModels["Zombine"] = "css"
RefoselBots.VoiceModels["Corpse"] = "css"
RefoselBots.VoiceModels["Charple"] = "css"
RefoselBots.VoiceModels["Skeleton"] = "css"
RefoselBots.VoiceModels["Van"] = "male"
RefoselBots.VoiceModels["Ted"] = "male"
RefoselBots.VoiceModels["Joe"] = "male"
RefoselBots.VoiceModels["Eric"] = "male"
RefoselBots.VoiceModels["Art"] = "male"
RefoselBots.VoiceModels["Sandro"] = "male"
RefoselBots.VoiceModels["Mike"] = "male"
RefoselBots.VoiceModels["Vance"] = "male"
RefoselBots.VoiceModels["Erdin"] = "male"
RefoselBots.VoiceModels["Van"] = "male"
RefoselBots.VoiceModels["Ted"] = "male"
RefoselBots.VoiceModels["Joe"] = "male"
RefoselBots.VoiceModels["Eric"] = "male"
RefoselBots.VoiceModels["Art"] = "male"
RefoselBots.VoiceModels["Sandro"] = "male"
RefoselBots.VoiceModels["Mike"] = "male"
RefoselBots.VoiceModels["Vance"] = "male"
RefoselBots.VoiceModels["Erdin"] = "male"
RefoselBots.VoiceModels["Joey"] = "female"
RefoselBots.VoiceModels["Kanisha"] = "female"
RefoselBots.VoiceModels["Kim"] = "female"
RefoselBots.VoiceModels["Chau"] = "female"
RefoselBots.VoiceModels["Naomi"] = "female"
RefoselBots.VoiceModels["Lakeetra"] = "female"
RefoselBots.VoiceModels["Joey"] = "female"
RefoselBots.VoiceModels["Kanisha"] = "female"
RefoselBots.VoiceModels["Kim"] = "female"
RefoselBots.VoiceModels["Chau"] = "female"
RefoselBots.VoiceModels["Naomi"] = "female"
RefoselBots.VoiceModels["Lakeetra"] = "female"