local module = {}
local enemyNum = 0
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local waveCount = 1
local enemyGroupWeight = {
SmallGruntGroup = {Type = "Goblin Grunt", Count = 6, Delay = 2, Cost = 1},
SmallRunningGroup = {Type = "Goblin Runner", Count = 4, Delay = 3, Cost = 3},
CondenseGruntGroup = {Type = "Goblin Grunt", Count = 10, Delay = 1, Cost = 4},
SmallSpearGroup = {Type = "Spear Goblin", Count = 2, Delay = 5, Cost = 6},
SmallHybridGroup = {
{Type = "Spear Goblin", Count = 1, Delay = 0, Cost = 3},
{Type = "Goblin Grunt", Count = 6, Delay = 2, Cost = 4},
},
}
local function getPackCost(pack)
if type(pack) ~= "table" then return math.huge end
if pack[1] ~= nil then -- composite pack (array of subpacks)
local sum = 0
for _, sub in ipairs(pack) do
sum = sum + (sub.Cost or 0)
end
return sum
else -- single pack
return pack.Cost or math.huge
end
end
local function filterAvailable(list, maxCost)
local out = {}
for _, p in ipairs(list) do
local c = getPackCost(p)
if c <= maxCost and c > 0 then
table.insert(out, p)
end
end
return out
end
local function generateWave(waveCount)
local budget = 5 + (tonumber(waveCount) or 1) * 2
local wave = {}
local available = {}
for name, pack in pairs(enemyGroupWeight) do
local cost = getPackCost(pack)
if cost <= budget and cost > 0 then
table.insert(available, pack)
end
end
if #available == 0 then
return wave
end
local attempts = 0
while budget > 0 and #available > 0 do
attempts = attempts + 1
if attempts > 200 then break end
local index = math.random(1, #available)
local pack = available[index]
local totalCost = getPackCost(pack)
if totalCost <= 0 then
available[index] = available[#available]
table.remove(available)
else
if totalCost <= budget then
table.insert(wave, pack)
budget -= totalCost
end
available = filterAvailable(available, budget)
end
if totalCost <= budget then
table.insert(wave, pack)
budget = budget - totalCost
end
available = filterAvailable(available, budget)
end
return wave
end
local Waves = {
[1] = {
{Type = "Goblin Grunt", Count = 5, Delay = 2}
},
[2] = {
{Type = "Goblin Grunt", Count = 8, Delay = 1}
},
[3] = {
{Type = "Goblin Grunt", Count = 8, Delay = 1},
{Type = "Spear Goblin", Count = 2, Delay = 4},
{Type = "Goblin Runner", Count = 4, Delay = 2}
}
}
local function playWalkAnimation(enemy)
local humanoid = enemy:FindFirstChild("Humanoid")
if not humanoid then return end
local animator = humanoid:FindFirstChild("Animator")
if not animator then
animator = Instance.new("Animator")
animator.Parent = humanoid
end
local walkAnimation = humanoid:FindFirstChild("walk")
if not walkAnimation then return end
local walkAnimTrack = animator:LoadAnimation(walkAnimation)
walkAnimTrack.Looped = true
walkAnimTrack:Play()
return walkAnimTrack
end
local function createEnemy(enemyType)
task.spawn(function()
local paths = workspace:WaitForChild("PathsWaypoints")
local waypoints = paths:GetChildren()
table.sort(waypoints, function(a, b)
local numA = tonumber(a.Name) or 0
local numB = tonumber(b.Name) or 0
return numA < numB
end)
local enemy = ReplicatedStorage.Items.Enemies[enemyType]:Clone()
for i, v in enemy:GetDescendants() do
if v:IsA("BasePart") then
v.CollisionGroup = "Characters"
end
end
enemyNum += 1
enemy.Name = enemyNum.. " ".. enemyType
local status = enemy:WaitForChild("Status")
local stat = require(enemy:WaitForChild("Stats"))
local enHum = enemy:WaitForChild("Humanoid")
status.NameLabel.Text = enemyType
status.healthStatus.Text = stat.Health.. "/".. stat.Health
enHum.WalkSpeed = stat.Speed
enHum.MaxHealth = stat.Health
enHum.Health = stat.Health
enemy.Parent = workspace:WaitForChild("Enemies")
enemy:PivotTo(paths.Start.CFrame)
if not enemy.PrimaryPart then
local root = enemy:FindFirstChild("HumanoidRootPart")
if root then
enemy.PrimaryPart = root
else
for i, v in enemy:GetDescendants() do
if v:IsA("BasePart") then
enemy.PrimaryPart = v
break
end
end
end
end
local walkAnimTrack = playWalkAnimation(enemy)
for i = 1, #waypoints do
local waypoint = waypoints[i]
if not enemy or not enemy.Parent or not enemy.PrimaryPart then
break
end
local reached = false
local conn
enHum:MoveTo(waypoint.Position)
print(waypoint.Position)
conn = enHum.MoveToFinished:Connect(function(success)
if success then
reached = true
end
end)
while not reached do
if enemy.PrimaryPart then
local distance = (enemy.PrimaryPart.Position - waypoint.Position).Magnitude
print(distance)
if distance < 1 then
reached = true
end
end
task.wait(.1)
end
conn:Disconnect()
end
if walkAnimTrack then
walkAnimTrack:Stop()
end
if enemy and enemy.Parent then
enemy:Destroy()
end
end)
end
function module.startWave(waveNumber)
local waveData = generateWave(waveNumber)
if not waveData then
print("No wave data for wave", waveNumber or waveCount)
return
end
print("Starting wave", waveCount)
local EnemiesFolder = workspace:WaitForChild("Enemies")
for _, group in pairs(waveData) do
task.spawn(function()
for i = 1, group.Count do
createEnemy(group.Type)
task.wait(group.Delay)
end
end)
end
while true do
task.wait(2)
local aliveEnemies = 0
for _, enemy in ipairs(EnemiesFolder:GetChildren()) do
local humanoid = enemy:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
aliveEnemies += 1
end
end
if aliveEnemies == 0 then
break
end
print("Waiting for", aliveEnemies, "enemies to be defeated...")
end
print("Wave", waveCount, "completed!")
waveCount += 1
end
return module
thats the module and its called in a server script after 5 seconds after the game is running for testing purposes
enemies begin to move along the path but then stop before they even get to the 2nd point and at this point i can’t figure out what im doing wrong here