Skip to content

Instantly share code, notes, and snippets.

@dhkatz
Last active September 14, 2018 05:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhkatz/d7cade1adbfcb04ca98b7e2817a5ee64 to your computer and use it in GitHub Desktop.
Save dhkatz/d7cade1adbfcb04ca98b7e2817a5ee64 to your computer and use it in GitHub Desktop.
Garry's Mod Boss Framework
-- Put in either addons/boss_framework/lua/autorun/boss_framework.lua or just lua/autorun/boss_framework.lua
if SERVER then
local BossEnabled = CreateConVar("boss_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Enable/disable the boss system.")
local BossCooldown = CreateConVar("boss_cooldown", "300", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Cooldown between when a boss can even possibly spawn.")
local BossThreshold = CreateConVar("boss_threshold", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Number of players required for a boss to spawn.")
AddCSLuaFile()
--------------------
-- BOSS FRAMEWORK --
--------------------
boss = {}
boss.Active = false
boss.Current = NULL
boss.Next = CurTime()
local Bosses = {}
local Indexes = {}
function boss.Register(name, tbl)
tbl.Name = name
-- You should probably add some validation here
Bosses[string.lower(name)] = tbl
local index = #Indexes + 1
Indexes[index] = string.lower(name)
for _, ply in ipairs(player.GetAll()) do
net.Start("BossRegister")
net.WriteString(name)
net.Send(ply)
end
return index
end
function boss.GetByName(name)
return Bosses[string.lower(name)]
end
function boss.GetByIndex(index)
return Bosses[Indexes[index]]
end
function boss.GetAllBosses()
return Bosses
end
function boss.GetCurrent()
return boss.Current
end
function boss.IsActive()
return boss.Active
end
function boss.Think()
if player.GetCount() < BossThreshold:GetInt() then return end
if boss.Active then return end
if boss.Next > CurTime() then return end
for name, b in pairs(Bosses) do
if math.random() > b.Chance then continue end
local ent = ents.Create(b.Class)
ent:SetPos(table.Random(ents.FindByClass("info_player_start")):GetPos())
ent:SetHealth(b.Health)
if b.Spawn then
b:Spawn(ent)
end
ent:Spawn()
ent.IsBoss = true
ent.BossName = b.Name
boss.Active = true
boss.Current = ent
hook.Run("BossSpawned", b.Name, ent)
return
end
end
-----------------------
-- CUSTOM BOSS HOOKS --
-----------------------
-- Called when the boss is spawned, you can create your own custom "BossSpawned" hooks too!
hook.Add("BossSpawned", "BossSpawned.Alert", function(name, ent, force)
for _, ply in ipairs(player.GetAll()) do
ply:ChatPrint("[ALERT] World boss '" .. name .. "' has entered the area!")
end
end)
-- Called when the boss is killed, you can create your own custom "BossSpawned" hooks too!
hook.Add("BossKilled", "BossKilled.Alert", function(name, ent, killer)
for _, ply in ipairs(player.GetAll()) do
ply:ChatPrint("[ALERT] World boss '" .. name .. "' has been defeated!")
end
end)
----------------
-- NETWORKING --
----------------
util.AddNetworkString("BossRegister")
util.AddNetworkString("BossForce")
util.AddNetworkString("BossReset")
net.Receive("BossForce", function(ply)
local data = net.ReadTable()
if not ply:IsAdmin() then return end
local b = Bosses[string.lower(data.Name)]
if not b then return end
local ent = ents.Create(b.Class)
ent:SetPos(data.Pos or table.Random(ents.FindByClass("info_player_start")):GetPos())
ent:SetHealth(b.Health)
if b.Spawn then
b:Spawn(ent)
end
ent:Spawn()
ent.IsBoss = true
ent.BossForced = true
ent.BossName = b.Name
boss.Active = true
boss.Current = ent
hook.Run("BossSpawned", b.Name, ent, true)
end)
net.Receive("BossReset", function(ply)
if not ply:IsAdmin() then return end
boss.Next = CurTime()
end)
----------------
-- HOOK LOGIC --
----------------
hook.Add("Initialize", "BOSS.Initialize", function()
local files, _ = file.Find("bosses/*.lua", "LUA")
print("-------------------------------")
print(" INITIALIZING BOSSES FRAMEWORK ")
print("-------------------------------")
print(" LOADING CUSTOM BOSSES ")
print("-------------------------------")
for _, f in pairs(files) do
print("Loaded boss '" .. (string.len(f) > 13 and string.sub(f, 1, 13) or f) .. "'.")
include("bosses/" .. f)
end
print("-------------------------------")
print(" FINISHED LOADING BOSSES ")
print("-------------------------------")
end)
hook.Add("PlayerInitialSpawn", "BOSS.PlayerInitialSpawn", function(ply)
for name in pairs(Bosses) do
net.Start("BossRegister")
net.WriteString(name)
net.Send(ply)
end
end)
hook.Add("Think", "BOSS.Think", function()
if not BossEnabled:GetBool() then return end
boss.Think()
end)
local function BossDied(ent, ply)
if not ent.IsBoss then return end
if not (IsValid(boss.Current) or boss.Active) then return end
if not Bosses[string.lower(ent.BossName)] then return end
hook.Run("BossKilled", ent.BossName, ent, ply)
boss.Current = NULL
boss.Active = false
if not ent.BossForced then
boss.Next = CurTime() + BossCooldown:GetFloat()
end
if not isentity(ply) or not ply:IsPlayer() then
return
end
local b = Bosses[string.lower(ent.BossName)]
if b.Killed then
b:Killed(ply)
end
end
hook.Add("OnNPCKilled", "BOSS.OnNPCKilled", BossDied)
hook.Add("EntityRemoved", "BOSS.EntityRemoved", BossDied)
end
if CLIENT then
local Bosses = {}
-----------------------
-- NETWORKING BOSSES --
----------------------
net.Receive("BossRegister", function()
local name = net.ReadString()
table.insert(Bosses, name)
end)
-----------------------
-- CUSTOM COMMANDS --
-----------------------
concommand.Add("boss_list", function(ply, cmd, args)
print("--------------------------------")
print(" BOSS TABLE ")
print("--------------------------------")
PrintTable(Bosses)
print("--------------------------------")
end, nil, "Print all bosses to console.")
concommand.Add("boss_force", function(ply, cmd, args)
if not ply:IsAdmin() then
return
end
local name = isnumber(tonumber(args[1])) and Bosses[tonumber(args[1])] or isstring(args[1]) and args[1] or table.Random(Bosses)
net.Start("BossForce")
net.WriteTable({
Name = name
})
net.SendToServer()
end, nil, "Force spawn a random boss. (Admin only)")
concommand.Add("boss_spawn", function(ply, cmd, args)
if not ply:IsAdmin() then return end
local name = isnumber(tonumber(args[1])) and Bosses[tonumber(args[1])] or isstring(args[1]) and args[1] or table.Random(Bosses)
local tr = ply:GetEyeTrace()
if not tr.Hit then print("You are not looking anywhere!") return end
net.Start("BossForce")
net.WriteTable({
Name = name,
Pos = tr.HitPos
})
net.SendToServer()
end, nil, "Force spawn a boss at the location you are looking.")
concommand.Add("boss_reset", function(ply, cmd, args)
if not ply:IsAdmin() then return end
net.Start("BossReset")
net.SendToServer()
end, nil, "Reset the cooldown.")
end
-- Put in either addons/boss_framework/lua/bosses/super_combine.lua or lua/bosses/super_combine.lua
local BOSS = {}
BOSS.Health = 5000 -- Starting health of boss
BOSS.Class = "npc_combine_s" -- NPC class of the boss
BOSS.Chance = 1 / 10000 -- Chance the boss will spawn each tick, experiment with this
-- This gets run when the boss gets killed, the player that killed the boss is provided
function BOSS:Killed(ply)
ply:ChatPrint("You DEFEATED world boss '" .. self.Name .. "'!")
ply:SetHealth(ply:GetMaxHealth())
ply:SetArmor(100)
end
-- This gets run when the boss is about to be spawned, the entity created is passed. You can modify the NPC right before it is spawned here.
function BOSS:Spawn(ent)
ent:SetKeyValue("additionalequipment", "weapon_smg1")
end
BOSS_SUPERCOMBINE = boss.Register("Super Combine", BOSS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment