156 lines
4.3 KiB
Plaintext
156 lines
4.3 KiB
Plaintext
local WeaponUtils = {}
|
|
|
|
local rBotUtils = require(game.ReplicatedStorage.Shared.SharedUtils.sharedBotUtils)
|
|
local BotUtils = require("./BotUtils")
|
|
|
|
--////////////////////////////////////////////////////////
|
|
-- CONFIG
|
|
--////////////////////////////////////////////////////////
|
|
|
|
local VOXEL_SIZE = 0.5
|
|
local MAX_VOXELS_PER_EXPLOSION = 1500
|
|
|
|
local MATERIAL_RESISTANCE = {
|
|
[Enum.Material.Concrete] = 0.75,
|
|
[Enum.Material.Slate] = 0.65,
|
|
[Enum.Material.Wood] = 0.35,
|
|
[Enum.Material.Plastic] = 0.2,
|
|
Default = 0.5
|
|
}
|
|
|
|
local overlapParams = OverlapParams.new()
|
|
overlapParams.FilterType = Enum.RaycastFilterType.Include
|
|
|
|
local function getVoxelFolder()
|
|
return workspace:FindFirstChild("VoxelDebris")
|
|
end
|
|
|
|
local function snap(v)
|
|
return math.floor(v / VOXEL_SIZE + 0.5)
|
|
end
|
|
|
|
local DEBUG = true
|
|
local DEBUG_LIFETIME = 0.35
|
|
|
|
local function spawnDebugSphere(position: Vector3, radius: number, color: Color3)
|
|
local sphere = Instance.new("Part")
|
|
sphere.Shape = Enum.PartType.Ball
|
|
sphere.Anchored = true
|
|
sphere.CanCollide = false
|
|
sphere.Material = Enum.Material.Neon
|
|
sphere.Transparency = 0.75
|
|
sphere.Color = color
|
|
sphere.Size = Vector3.new(radius * 2, radius * 2, radius * 2)
|
|
sphere.Position = position
|
|
sphere.Parent = workspace
|
|
|
|
game:GetService("Debris"):AddItem(sphere, DEBUG_LIFETIME)
|
|
end
|
|
|
|
--////////////////////////////////////////////////////////
|
|
-- EXPLOSION (2 PHASE COLUMN SYSTEM)
|
|
--////////////////////////////////////////////////////////
|
|
|
|
function WeaponUtils.ExplosionQuery(origin: Vector3, innerRadius: number, outerRadius: number, maxDamage: number)
|
|
--------------------------------------------------------
|
|
-- BOT DAMAGE
|
|
--------------------------------------------------------
|
|
for _, model in pairs(workspace.Bots:GetChildren()) do
|
|
local bot = BotUtils.GetBotByModel(model)
|
|
if not bot then continue end
|
|
|
|
local position = rBotUtils.GetBotPosition(bot.key)
|
|
local damage = WeaponUtils.CalculateExplosionDamage(origin, position, outerRadius, maxDamage)
|
|
|
|
if damage > 0 then
|
|
bot.Components.Health:TakeDamage(damage)
|
|
end
|
|
end
|
|
if DEBUG then
|
|
spawnDebugSphere(origin, outerRadius, Color3.fromRGB(255, 60, 60)) -- red outer
|
|
spawnDebugSphere(origin, innerRadius, Color3.fromRGB(255, 140, 0)) -- orange inner
|
|
end
|
|
|
|
local folder = getVoxelFolder()
|
|
if not folder then return end
|
|
|
|
overlapParams.FilterDescendantsInstances = {folder}
|
|
|
|
local parts = workspace:GetPartBoundsInRadius(origin, innerRadius, overlapParams)
|
|
if #parts == 0 then return end
|
|
|
|
local radiusSq = innerRadius * innerRadius
|
|
|
|
--------------------------------------------------------
|
|
-- PHASE 1: FIND VALID VOXELS (SEEDS)
|
|
--------------------------------------------------------
|
|
local validColumns = {}
|
|
local processed = 0
|
|
|
|
for i = 1, #parts do
|
|
if processed > MAX_VOXELS_PER_EXPLOSION then break end
|
|
|
|
local part = parts[i]
|
|
if not part:IsA("BasePart") then continue end
|
|
|
|
local offset = part.Position - origin
|
|
local distSq = offset:Dot(offset)
|
|
|
|
if distSq > radiusSq then continue end
|
|
|
|
local dist = math.sqrt(distSq)
|
|
local falloff = 1 - (dist / innerRadius)
|
|
local power = falloff * falloff
|
|
|
|
local resistance = MATERIAL_RESISTANCE[part.Material] or MATERIAL_RESISTANCE.Default
|
|
|
|
if power > resistance then
|
|
-- STORE COLUMN KEY (X + Y ONLY)
|
|
local gx = snap(part.Position.X)
|
|
local gy = snap(part.Position.Y)
|
|
|
|
local key = gx .. "," .. gy
|
|
validColumns[key] = true
|
|
end
|
|
|
|
processed += 1
|
|
end
|
|
|
|
--------------------------------------------------------
|
|
-- PHASE 2: DESTROY FULL Z COLUMNS (NO CHECKS)
|
|
--------------------------------------------------------
|
|
for i = 1, #parts do
|
|
local part = parts[i]
|
|
if not part:IsA("BasePart") then continue end
|
|
|
|
local gx = snap(part.Position.X)
|
|
local gy = snap(part.Position.Y)
|
|
|
|
local key = gx .. "," .. gy
|
|
|
|
if validColumns[key] then
|
|
part:Destroy()
|
|
end
|
|
end
|
|
|
|
|
|
end
|
|
|
|
--////////////////////////////////////////////////////////
|
|
-- DAMAGE
|
|
--////////////////////////////////////////////////////////
|
|
|
|
function WeaponUtils.CalculateExplosionDamage(origin: Vector3, position: Vector3, radius: number, maxDamage: number)
|
|
local offset = position - origin
|
|
local distSq = offset:Dot(offset)
|
|
|
|
if distSq >= radius * radius then return 0 end
|
|
|
|
local dist = math.sqrt(distSq)
|
|
local normalized = dist / radius
|
|
|
|
local damage = maxDamage * (1 - normalized)^2
|
|
return math.clamp(damage, 0, maxDamage)
|
|
end
|
|
|
|
return WeaponUtils |