6.6.3 Final Scripts

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