Lua
--/ServerStorage/CannonModule
local DebrisService = game:GetService("Debris")
local GRAVITY = game.Workspace.Gravity
local LAUNCH_ANGLE = math.pi / 4
local CANNON = game.Workspace.Cannon
local CANNONBALL = Instance.new("Part")
local FIRE_SOUND = Instance.new("Sound")
local COLLIDE_WAV = 'rbxassetid://12221984'
local FIRE_WAV = 'rbxassetid://4812797067'
CANNONBALL.Shape = Enum.PartType.Ball
CANNONBALL.Size = Vector3.one * 4
CANNONBALL.Material = Enum.Material.Neon
CANNONBALL.BrickColor = BrickColor.new("Flame yellowish orange")
FIRE_SOUND.SoundId = FIRE_WAV
FIRE_SOUND.Volume = 5
FIRE_SOUND.Parent = CANNON
do
local explodSound = Instance.new("Sound")
explodSound.SoundId = COLLIDE_WAV
explodSound.Volume = 5
explodSound.Parent = CANNONBALL
local fire = Instance.new("Fire")
fire.Parent = CANNONBALL
end
local function getLaunchVelocity(range, height)
local cosAngle2 = 2 * math.cos(LAUNCH_ANGLE) * math.cos(LAUNCH_ANGLE)
local tanAngle = math.tan(LAUNCH_ANGLE)
local numer = GRAVITY * (range * range)
local denom = (height + (range * tanAngle)) * cosAngle2
return math.sqrt(numer / denom)
end
local CannonModule = {}
function CannonModule:attackPlayer(character)
if not character or not character.PrimaryPart then return false end
local pos0 = CANNON.Position
local pos1 = character.PrimaryPart.Position
local direction = pos1 - pos0
local rotationY = math.atan2(-direction.X, -direction.Z)
local height = -direction.Y
local distance = (direction * Vector3.new(1, 0, 1)).Magnitude
CANNON.CFrame = CFrame.fromEulerAnglesYXZ(LAUNCH_ANGLE, rotationY, 0) + CANNON.Position
local launchV = getLaunchVelocity(distance, height)
local newCannonball = CANNONBALL:Clone()
local mass = newCannonball.Mass
local f = mass * launchV
local launchCf = CANNON.CFrame
newCannonball.CFrame = launchCf
newCannonball.Parent = game.Workspace
newCannonball:SetNetworkOwner(nil)
newCannonball:ApplyImpulse(launchCf.LookVector * f)
FIRE_SOUND:Play()
newCannonball.Touched:Once(function()
newCannonball.Anchored = true
newCannonball.Transparency = 1
newCannonball.CanCollide = false
newCannonball.CanTouch = false
local explosion = Instance.new("Explosion")
explosion.Position = newCannonball.Position
explosion.Parent = newCannonball
newCannonball.Fire:Destroy()
newCannonball.Sound:Play()
DebrisService:AddItem(newCannonball, 3)
end)
end
return CannonModule
Lua
--/ServerStorage/UfoModule
local CannonModule = require(game.ServerStorage.CannonModule)
local SPOTLIGHT_ID = 'http://www.roblox.com/asset/?id=17695999717'
local UFO_MESH_ID = 'http://www.roblox.com/asset?id=161270281'
local TEXTURE_ID = 'http://www.roblox.com/asset?id=161270249'
local DOWN_VECTOR = -Vector3.yAxis
local FOV = math.rad(15)
local SHIP_SPEED = 15
local SHIP_ALTITUDE = 100
local SPOTLIGHT_HEIGHT = 0.1
local HOME_POSITION = Vector3.new(-76.013, SHIP_ALTITUDE, -242.909)
local MOVE_RANGE = {100, 200}
local SCAN_RANGE = {100, 200}
local SCAN_SPEED = 50
local SEEK_COOLDOWN = 5
local ufoData = {
ship = Instance.new("Part"),
spotlight = Instance.new("Part"),
spotlightPos = Vector3.new(),
moveTarget = Vector3.new(),
lightTarget = Vector3.new(),
lastDetect = 0,
seeking = false
}
local function randomSpanV3(range)
local distance = math.random(range[1], range[2])
local rad = math.random() * math.pi * 2
local x = math.cos(rad) * distance
local z = math.sin(rad) * distance
return Vector3.new(x, 0, z)
end
local function getSpotSizeCF(origin, targetPosition)
local direction = targetPosition - origin
local distance = direction.Magnitude
local look = direction.Unit
local alpha = DOWN_VECTOR:Angle(look)
local theta = math.pi / 2 - alpha
local forwardVector = Vector3.new(look.X, 0, look.Z).Unit
local beta = math.pi / 2 + FOV
local gamma = math.pi / 2 - FOV
local C = distance * math.tan(FOV)
local major1 = C * (math.sin(beta) / math.sin(gamma - alpha))
local major2 = C * (math.sin(gamma) / math.sin(beta - alpha))
local e = math.cos(math.pi / 2 - alpha) / math.cos(FOV)
local e2 = e * e
local inner = 1 - e2
local semimajor = (major1 + major2) / 2
local semimajor2 = semimajor * semimajor
local b = math.sqrt(inner * semimajor2)
local minorAxis = 2 * b
local incident = 0.5 * major1 * (1 - (major2 / major1))
local spotPos = targetPosition + forwardVector * incident
return Vector3.new(minorAxis, SPOTLIGHT_HEIGHT, major1 + major2), CFrame.new(spotPos, spotPos + forwardVector)
end
local function scanForPlayers(pos, targetPosition)
local look = (targetPosition - pos).Unit
local spotted = {}
for _,player in ipairs(game.Players:GetPlayers()) do
local character = player.Character
if character then
local charPos = character.PrimaryPart.Position
local charDirection = (charPos - pos).Unit
if look:Angle(charDirection) < FOV then
spotted[character.Name] = true
table.insert(spotted, character)
end
end
end
return spotted
end
local function updateSpotlight(dt, shipData)
local ufo = shipData["ship"]
local spotlight = shipData["spotlight"]
local targetPosition = shipData["lightTarget"]
local lightPosition = shipData["spotlightPos"]
local shipPos = ufo.Position
local direction = targetPosition - lightPosition
local distance = direction.Magnitude
local look = direction.Unit
local velocity = look * SCAN_SPEED * dt
local newPosition = lightPosition + velocity
shipData["spotlightPos"] = newPosition
if shipData["seeking"] then
local size, spotCf = getSpotSizeCF(shipPos, newPosition)
spotlight.CFrame = spotCf
spotlight.Size = size
else
spotlight.Position = Vector3.new(0, 100, 0)
spotlight.Size = Vector3.one
end
if distance < 1 then
local groundPos = Vector3.new(shipPos.X, SPOTLIGHT_HEIGHT, shipPos.Z)
local nextPosition = randomSpanV3(SCAN_RANGE)
shipData["lightTarget"] = groundPos + nextPosition
end
end
local function updateShip(dt, shipData)
local position = shipData["ship"].Position
local direction = shipData["moveTarget"] - position
local distance = direction.Magnitude
local look = direction.Unit
local velocity = look * SHIP_SPEED * dt
local newPosition = position + velocity
shipData["ship"].Position = newPosition
if distance < 1 then
shipData["moveTarget"] = HOME_POSITION + randomSpanV3(MOVE_RANGE)
end
end
local lastTick = tick()
local function update()
local t2 = tick()
local dt = t2 - lastTick
updateShip(dt, ufoData)
updateSpotlight(dt, ufoData)
if ufoData["seeking"] then
local foundPlayers = scanForPlayers(ufoData["ship"].Position, ufoData["spotlightPos"])
if #foundPlayers > 0 then
CannonModule:attackPlayer(foundPlayers[math.random(#foundPlayers)])
ufoData["lastDetect"] = t2
end
end
ufoData["seeking"] = t2 - ufoData["lastDetect"] > SEEK_COOLDOWN
lastTick = t2
end
do
local specialMesh = Instance.new("SpecialMesh")
specialMesh.MeshId = UFO_MESH_ID
specialMesh.TextureId = TEXTURE_ID
specialMesh.Parent = ufoData["ship"]
local decal = Instance.new("Decal")
decal.Texture = SPOTLIGHT_ID
decal.Face = Enum.NormalId.Top
decal.Color3 = Color3.new(0, 1, 0)
decal.Transparency = 0.75
decal.Parent = ufoData["spotlight"]
ufoData["spotlight"].Anchored = true
ufoData["spotlight"].CanCollide = false
ufoData["spotlight"].CanTouch = false
ufoData["spotlight"].Position = Vector3.new(0, 100, 0)
ufoData["spotlight"].Size = Vector3.new(4, SPOTLIGHT_HEIGHT, 4)
ufoData["spotlight"].Transparency = 1.0
ufoData["spotlight"].Parent = game.Workspace
ufoData["ship"].Anchored = true
ufoData["ship"].Name = "UFO"
ufoData["ship"].Position = Vector3.new(0, SHIP_ALTITUDE, 0)
ufoData["ship"].Parent = game.Workspace
ufoData["spotlightPos"] = Vector3.new(ufoData["ship"].Position.X, SPOTLIGHT_HEIGHT, ufoData["ship"].Position.Z)
ufoData["moveTarget"] = HOME_POSITION + randomSpanV3(MOVE_RANGE)
ufoData["lightTarget"] = ufoData["spotlightPos"] + randomSpanV3(SCAN_RANGE)
end
local UfoModule = {}
function UfoModule:update()
update()
end
return UfoModule
Lua
--/ServerScriptService/Script
local ufoModule = require(game.ServerStorage.UfoModule)
while task.wait() do
ufoModule:update()
end