How to correctly spawn Armed NPCs (Raiders) via UE4SS World:SpawnActor?
Hey everyone! I’m currently using a custom UE4SS Lua mod to spawn custom Horde events in SCUM. I have a script that writes target coordinates to a text file, which my Lua mod reads to distribute entities in a grid using World:SpawnActor.
The Current Implementation: My main.lua successfully spawns zombies using: World:SpawnActor(ZombieClass, SpawnLocation, SpawnRotation) This works perfectly for zombies—they spawn, register, and engage players.
The Problem: When I change the ZombieClassPath to an Armed NPC Blueprint (e.g., BP_Guard_Lvl_3_C), the spawn fails completely (no entities appear) or causes a game crash/Lua runtime error. It seems these NPCs refuse to instantiate outside of the Encounter Manager.
My Questions for the modding wizards:
- Is there a way to manually register an actor spawned via
SpawnActorinto theGlobalEncounterManagerso the game accepts it? - Are there any hidden initialization functions I need to call on the NPC Blueprint after spawning it to prevent the crash?
- Or is it strictly impossible to use
SpawnActorfor NPCs, and I should focus on finding a way to trigger anEncounterPresetvia Kismet?
Code Reference: local Config = require("config")
local ZombieClassPath = "/Game/ConZ_Files/Characters/Zombies2/Blueprints/BP_Zombie_Civilian.BP_Zombie_Civilian_C"
function SpawnZumbi(ContextObj, X, Y, Z, deslocamentoX, deslocamentoY)
local ZombieClass = StaticFindObject(ZombieClassPath)
if not ZombieClass then ZombieClass = StaticFindObject("BlueprintGeneratedClass " .. ZombieClassPath) end
if ZombieClass then
local SpawnLocation = {
X = tonumber(X) + deslocamentoX,
Y = tonumber(Y) + deslocamentoY,
Z = tonumber(Z) + 20
}
local SpawnRotation = {Pitch = 0, Yaw = math.random(0, 360), Roll = 0}
local World = nil
if ContextObj then
local RealObj = ContextObj:get()
if RealObj then World = RealObj:GetWorld() end
end
if not World then
local GameMode = FindFirstOf("ConZGameMode")
if not GameMode or not GameMode:IsValid() then
GameMode = FindFirstOf("GameModeBase")
end
if GameMode and GameMode:IsValid() then
World = GameMode:GetWorld()
end
end
if World then
World:SpawnActor(ZombieClass, SpawnLocation, SpawnRotation)
end
end
end
-- =====================================================================
-- FUNÇÃO CENTRAL: LÓGICA DE DISTRIBUIÇÃO EM GRADE (RETÂNGULO)
-- =====================================================================
local function DistribuirHorda(ContextObj, qtdTotal, baseX, baseY, baseZ)
local maxPorPonto = 2 -- Máximo de zumbis colados um no outro
local distancia = 700 -- Distância entre os grupos
local limite = 1400 -- Limite máximo para não passar dos 1500 que você pediu
-- Começa no canto superior esquerdo do "retângulo" imaginário
local offsetX = -limite
local offsetY = -limite
local subContador = 0
for i = 1, qtdTotal do
SpawnZumbi(ContextObj, baseX, baseY, baseZ, offsetX, offsetY)
subContador = subContador + 1
-- Se atingiu 2 zumbis neste ponto, move 700 unidades pro lado (Eixo X)
if subContador >= maxPorPonto then
subContador = 0
offsetX = offsetX + distancia
-- Se o X bater no limite (1400), "pula de linha" (Avança o Y e reseta o X)
if offsetX > limite then
offsetX = -limite
offsetY = offsetY + distancia
-- Se o Y também passar do limite, volta pro centro pra não vazar o raio máximo
if offsetY > limite then
offsetY = -limite
offsetX = -limite
end
end
end
end
end
-- =====================================================================
-- 1. SISTEMA ORIGINAL (Funciona pelo Chat do Jogo)
-- =====================================================================
RegisterHook("/Script/SCUM.PlayerRpcChannel:Chat_Server_BroadcastChatMessage", function(self, Message, Channel)
local msgObj = Message:get()
local msg = (type(msgObj) == "userdata") and msgObj:ToString() or tostring(msgObj)
if string.find(msg, "!spawnhorda") then
local partes = {}
for palavra in string.gmatch(msg, "%S+") do table.insert(partes, palavra) end
if #partes >= 5 then
local qtdTotal = tonumber(partes[2])
local baseX = tonumber(partes[3])
local baseY = tonumber(partes[4])
local baseZ = tonumber(partes[5])
print("[SpawnZumbiCoords] [CHAT] Spawnando " .. tostring(qtdTotal) .. " zumbis em grade!")
DistribuirHorda(self, qtdTotal, baseX, baseY, baseZ)
end
end
end)
-- =====================================================================
-- 2. SISTEMA VIA ARQUIVO (Comunicação direta com o Bot do Discord)
-- =====================================================================
ExecuteAsync(function()
while true do
-- Truque do Ping
os.execute("ping -n 3 127.0.0.1 > nul")
local caminho_arquivo = Config.CaminhoArquivoQueue
local file = io.open(caminho_arquivo, "r")
if file then
local content = file:read("*a")
file:close()
if content and string.len(content) > 5 then
os.remove(caminho_arquivo)
local partes = {}
for palavra in string.gmatch(content, "%S+") do table.insert(partes, palavra) end
if #partes >= 4 then
local qtdTotal = tonumber(partes[1])
local baseX = tonumber(partes[2])
local baseY = tonumber(partes[3])
local baseZ = tonumber(partes[4])
print("[SpawnZumbiCoords] [ARQUIVO] Horda detectada! (" .. tostring(qtdTotal) .. " zumbis em grade)")
ExecuteInGameThread(function()
DistribuirHorda(nil, qtdTotal, baseX, baseY, baseZ)
print("[SpawnZumbiCoords] [ARQUIVO] Horda spawnada e distribuida com sucesso!")
end)
end
end
end
end
end)
Posted Jul 10, 2026, 5:51 PM UTC