Last active
March 10, 2025 05:46
-
-
Save dhkatz/6534e3b8ab27110c7b97672c72d2bc18 to your computer and use it in GitHub Desktop.
Spawn NPCs with a custom class and/or playermodel using a tool. Install to "garrysmod\lua\weapons\gmod_tool\stools"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TOOL.Category = "Event" | |
TOOL.Name = "Spawner" | |
TOOL.ClientConVar["type"] = "npc_citizen" | |
TOOL.ClientConVar["health"] = "500" | |
TOOL.ClientConVar["weapon"] = "none" | |
TOOL.ClientConVar["model"] = "" | |
TOOL.ClientConVar["proficiency"] = "AVERAGE" | |
TOOL.Information = { | |
{ | |
name = "info", | |
stage = 1 | |
}, | |
{ | |
name = "left" | |
}, | |
{ | |
name = "right" | |
} | |
} | |
if (CLIENT) then | |
language.Add("tool.swe_spawner.name", "Spawner") | |
language.Add("tool.swe_spawner.desc", "Spawn an NPC with custom attributes") | |
language.Add("tool.swe_spawner.left", "Spawn the custom NPC") | |
language.Add("tool.swe_spawner.right", "Get the class of the targeted NPC") | |
end | |
local proficiency = { | |
["POOR"] = WEAPON_PROFICIENCY_POOR, | |
["AVERAGE"] = WEAPON_PROFICIENCY_AVERAGE, | |
["GOOD"] = WEAPON_PROFICIENCY_GOOD, | |
["VERY GOOD"] = WEAPON_PROFICIENCY_VERY_GOOD, | |
["PERFECT"] = WEAPON_PROFICIENCY_PERFECT | |
} | |
function TOOL:SpawnNPC(Player, Position, Normal, Class, Equipment) | |
local NPCList = list.Get("NPC") | |
local NPCData = NPCList[Class] | |
if (not NPCData) then return end | |
if (NPCData.AdminOnly and not Player:IsAdmin()) then return end | |
local bDropToFloor = false | |
if (NPCData.OnCeiling and Vector(0, 0, -1):Dot(Normal) < 0.95) then return nil end | |
if (NPCData.OnFloor and Vector(0, 0, 1):Dot(Normal) < 0.95) then | |
return nil | |
else | |
bDropToFloor = true | |
end | |
if (NPCData.NoDrop) then | |
bDropToFloor = false | |
end | |
local Offset = NPCData.Offset or 32 | |
Position = Position + Normal * Offset | |
local NPC = ents.Create(NPCData.Class) | |
if (not IsValid(NPC)) then return end | |
NPC:SetPos(Position) | |
local Angles = Angle(0, 0, 0) | |
if (IsValid(Player)) then | |
Angles = Player:GetAngles() | |
end | |
Angles.pitch = 0 | |
Angles.roll = 0 | |
Angles.yaw = Angles.yaw + 180 | |
if (NPCData.Rotate) then | |
Angles = Angles + NPCData.Rotate | |
end | |
NPC:SetAngles(Angles) | |
if (NPCData.Model or self:GetClientInfo("model") ~= "") then | |
NPC:SetModel(self:GetClientInfo("model") or NPCData.Model) | |
end | |
if (NPCData.Material) then | |
NPC:SetMaterial(NPCData.Material) | |
end | |
local SpawnFlags = bit.bor(SF_NPC_FADE_CORPSE, SF_NPC_ALWAYSTHINK) | |
if (NPCData.SpawnFlags) then | |
SpawnFlags = bit.bor(SpawnFlags, NPCData.SpawnFlags) | |
end | |
if (NPCData.TotalSpawnFlags) then | |
SpawnFlags = NPCData.TotalSpawnFlags | |
end | |
NPC:SetKeyValue("spawnflags", SpawnFlags) | |
if (NPCData.KeyValues) then | |
for k, v in pairs(NPCData.KeyValues) do | |
NPC:SetKeyValue(k, v) | |
end | |
end | |
if (NPCData.Skin) then | |
NPC:SetSkin(NPCData.Skin) | |
end | |
local valid = false | |
for _, v in pairs(list.Get("NPCUsableWeapons")) do | |
if v.class == Equipment then | |
valid = true | |
break | |
end | |
end | |
if (Equipment and Equipment ~= "none" and valid) then | |
NPC:SetKeyValue("additionalequipment", Equipment) | |
NPC.Equipment = Equipment | |
end | |
DoPropSpawnedEffect(NPC) | |
NPC:Spawn() | |
NPC:Activate() | |
if (bDropToFloor and not NPCData.OnCeiling) then | |
NPC:DropToFloor() | |
end | |
return NPC | |
end | |
function TOOL:LeftClick(trace) | |
if (CLIENT) then return true end | |
local NPC_type = self:GetClientInfo("type") | |
local NPC_wep = self:GetClientInfo("weapon") | |
local NPC_health = self:GetClientInfo("health") | |
local NPC_prof = self:GetClientInfo("proficiency") | |
local ent = self:SpawnNPC(self:GetOwner(), trace.HitPos, trace.HitNormal, NPC_type, NPC_wep) | |
if (not ent:IsValid()) then return true end | |
ent:SetHealth(NPC_health) | |
ent:SetCurrentWeaponProficiency(proficiency[NPC_prof]) | |
undo.Create("NPC") | |
undo.AddEntity(ent) | |
undo.SetPlayer(self:GetOwner()) | |
undo.Finish() | |
self:GetOwner():AddCleanup("npcs", ent) | |
return true | |
end | |
function TOOL:RightClick(trace) | |
if game.SinglePlayer() then | |
self:GetOwner():PrintMessage(3, "Right click does not work in singleplayer!") | |
return false | |
end | |
if not (trace.Hit and IsValid(trace.Entity) and trace.Entity:IsNPC()) then | |
return false | |
end | |
if CLIENT then | |
self:GetOwner():PrintMessage(3, "Found NPC!") | |
SetClipboardText(trace.Entity:GetClass()) | |
end | |
return true | |
end | |
function GetNPCModels(dir) | |
local realDir = dir .. "/*" | |
local files, folders = file.Find(realDir, "MOD") | |
local ModelFiles = {} | |
for _, v in pairs(folders) do | |
if not v then continue end | |
if string.GetExtensionFromFilename(v) == nil then | |
table.Add(ModelFiles, GetNPCModels(dir .. "/" .. v)) | |
end | |
end | |
for _, v in pairs(files) do | |
if string.GetExtensionFromFilename(v) == "mdl" then | |
-- for a, pm in pairs(player_manager.AllValidModels()) do | |
-- if string.find(pm, v) then | |
-- fileFound = true | |
-- break | |
-- end | |
-- end | |
--if not fileFound and util.IsValidRagdoll(realDir .. v) then | |
-- if string.find(v, "keller") then | |
-- print(string.sub( realDir, 1, string.len( realDir ) - 1 ) .. v) | |
-- end | |
local mdl = string.sub(realDir, 1, string.len(realDir) - 1) .. v | |
--if util.IsValidRagdoll(mdl) then | |
--print(string.sub( realDir, 1, string.len( realDir ) - 1 ) .. v) | |
table.insert(ModelFiles, mdl) | |
--end | |
end | |
end | |
return ModelFiles | |
end | |
local ConVarsDefault = TOOL:BuildConVarList() | |
function TOOL.BuildCPanel(CPanel) | |
-- START PRESET DROPDOWN -- | |
local Preset = vgui.Create("ControlPresets") | |
Preset:SetPreset("event_spawner") | |
Preset:AddOption("Default", ConVarsDefault) | |
for k, v in pairs(table.GetKeys(ConVarsDefault)) do | |
Preset:AddConVar(v) | |
end | |
CPanel:AddPanel(Preset) | |
-- END PRESET DROPDOWN -- | |
local TypeList = vgui.Create("DListView") | |
TypeList:SizeToContents(true) | |
TypeList:SetHeight(300) | |
TypeList:SetMultiSelect(false) | |
TypeList:AddColumn("NPC") | |
local prevClass = {} | |
for k, v in pairs(list.Get("NPC")) do | |
if (prevClass[v.Class]) then continue end | |
TypeList:AddLine(v.Class) | |
prevClass[v.Class] = true | |
end | |
TypeList:SortByColumn(1) | |
TypeList.OnRowSelected = function(panel, row) | |
RunConsoleCommand("swe_spawner_type", panel:GetLine(row):GetColumnText(1)) | |
end | |
CPanel:AddItem(TypeList) | |
local WeaponList = vgui.Create("DListView") | |
WeaponList:SizeToContents(true) | |
WeaponList:SetHeight(300) | |
WeaponList:SetMultiSelect(false) | |
WeaponList:AddColumn("Weapons") | |
for k, v in pairs(list.Get("NPCUsableWeapons")) do | |
WeaponList:AddLine(v.class) | |
end | |
WeaponList:SortByColumn(1) | |
WeaponList.OnRowSelected = function(panel, row) | |
RunConsoleCommand("swe_spawner_weapon", panel:GetLine(row):GetColumnText(1)) | |
end | |
CPanel:AddItem(WeaponList) | |
-- START MODEL LIST -- | |
-- local ModelList = vgui.Create("DListView") | |
-- ModelList:SizeToContents(true) | |
-- ModelList:SetHeight(300) | |
-- ModelList:SetMultiSelect(true) | |
-- ModelList:AddColumn("Models") | |
-- local NPCModels = GetNPCModels("models") | |
-- local prevModel = {} | |
-- for k, v in pairs(NPCModels) do | |
-- if prevModel[v] then continue end | |
-- ModelList:AddLine(v) | |
-- prevModel[v] = true | |
-- end | |
-- ModelList:SortByColumn(1) | |
-- ModelList.OnRowSelected = function(panel, row) | |
-- RunConsoleCommand("swe_spawner_model", panel:GetLine(row):GetColumnText(1)) | |
-- end | |
-- CPanel:AddItem(ModelList) | |
-- END MODEL LIST -- | |
CPanel:TextEntry("Model Override", "swe_spawner_model") | |
CPanel:NumSlider("Health:", "swe_spawner_health", 1, 10000, 2) | |
local Prof = vgui.Create("DComboBox") | |
Prof:SetValue("Proficiency") | |
for k, v in pairs(proficiency) do | |
Prof:AddChoice(k) | |
end | |
Prof.OnSelect = function(panel, index, value) | |
RunConsoleCommand("swe_spawner_proficiency", value) | |
end | |
CPanel:AddItem(Prof) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment