function init()
	-- Registers tool
	RegisterTool("gypsyairstrike", "Atomic AC-130", "MOD/vox/airstrike.vox")
	SetBool("game.tool.gypsyairstrike.enabled", true)
	SetString("game.tool.gypsyairstrike.ammo.display","")
	
	-- Handles airborne shells from time between firing to impact
	airstrikeprojectileHandler = {
	shellNum = 1,
	shells = {},
	defaultShell = {active=false}
	}
	
	-- Populates airstrikeprojectileHandler with inactive shells
	for i=1, 250 do
		airstrikeprojectileHandler.shells[i] = deepcopy(airstrikeprojectileHandler.defaultShell)
	end
	
	-- Sets projectile properties
	velocity = 0.6
	shotDelay = 0.05
	
	-- Setup no cooldown if required
	nocd = GetBool("savegame.mod.nocd")
	
	-- Setup inverted mouse if required
	invertedMouse = 1
	if GetBool("savegame.mod.invert") then
		invertedMouse = 1
		else
		invertedMouse = -1
	end

	-- Setup shockwave size
	desiredSize = math.floor(GetInt("savegame.mod.desiredSize")/10 + (0 or .5))

	-- Sets initial weapon state
	flycamenabled = false
	airstrikeenabled = false
	firing = false
	radioactivated = false
	selectedshell = 4
	zoomlevel = 6
	maxzoom = 12
	minzoom = 0.5
	shockwaveSize = 0

	-- Initialise timers (shot timers)
	lightTimer = 0
	flashTimer = 0
	equipTimer = 0
	shoottimers = {}
	for i=1, 4 do
		shoottimers[i] = 0
	end

	-- Initialise projectile image and sound arrrays
	shellsprites = {}
	shellsprites[1] = LoadSprite("MOD/img/25mm.png")
	shellsprites[2] = LoadSprite("MOD/img/40mm.png")
	shellsprites[3] = LoadSprite("MOD/img/gypsy.png")
	shellsprites[4] = LoadSprite("MOD/img/nuke.png")

	planesound = LoadLoop("MOD/snd/planeloop.ogg")
	switchsound = LoadSound("MOD/snd/switch.ogg")
	beepsound = LoadSound("MOD/snd/radiobeep.ogg")
	triggersound = LoadSound("MOD/snd/triggerbeep.ogg")
	
	gunsounds = {}
	gunsounds[1] = LoadLoop("MOD/snd/gun1.ogg")
	gunsounds[2] = LoadSound("MOD/snd/gun2.ogg")
	gunsounds[3] = LoadSound("MOD/snd/gun3.ogg")
	gunsounds[4] = LoadSound("MOD/snd/gun4.ogg")
	
	explosionsounds = {}
	explosionsounds[1] = LoadSound("explosion/s0.ogg")
	explosionsounds[2] = LoadSound("explosion/s0.ogg")
	explosionsounds[3] = LoadSound("explosion/s0.ogg")
	explosionsounds[4] = LoadSound("MOD/snd/nukeExplosion.ogg")

	-- Sets aircraft state
	plane = {}
	plane.pos = Vec(0, 0 ,0)
	plane.pos[1] = 100
	plane.pos[2] = 100
	plane.rot = QuatRotateQuat(Quat(), QuatEuler(-90, -90, 0))
	plane.barrel = {}
	plane.barrel.rot = Quat()
end

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else
        copy = orig
    end
    return copy
end

function GetAimPos()
	local ct = GetCameraTransform()
	local forwardPos = TransformToParentPoint(ct, Vec(0, 0, -150))
    local direction = VecSub(forwardPos, ct.pos)
    local distance = VecLength(direction)
	local direction = VecNormalize(direction)
	local hit, hitDistance = QueryRaycast(ct.pos, direction, distance)
	if hit then
		forwardPos = TransformToParentPoint(ct, Vec(0, 0, -hitDistance))
		distance = hitDistance
	end
	return forwardPos, hit, distance
end

function GetCamLookPos()
	local forwardPos = TransformToParentPoint(plane.barrel, Vec(0, 0, -150))
    local direction = VecSub(forwardPos, plane.barrel.pos)
    local distance = VecLength(direction)
	local direction = VecNormalize(direction)
	local hit, hitDistance = QueryRaycast(plane.barrel.pos, direction, distance)
	if hit then
		forwardPos = TransformToParentPoint(plane.barrel, Vec(0, 0, -hitDistance))
		distance = hitDistance
	end
	return forwardPos, hit, distance
end

function GetShootDistance()
    local direction = VecSub(aimpos, plane.barrel.pos)
    local distance = VecLength(direction)
	local direction = VecNormalize(direction)
	local hit, hitDistance = QueryRaycast(plane.barrel.pos, direction, distance)
	if hit then
		forwardPos = TransformToParentPoint(plane.barrel, Vec(0, 0, -hitDistance))
		distance = hitDistance
	end
	return distance
end

-- Controls minimum and maximum FlyCam zoom level
function clamp(value, mi, ma)
	if value < mi then value = mi end
	if value > ma then value = ma end
	return value
end

-- Controls FlyCam movement when called every tick(dt)
function FlyCam()
	local mousewheel = InputValue("mousewheel")
	if mousewheel ~= 0 then
		zoomlevel = clamp(zoomlevel - mousewheel/2, minzoom, maxzoom)
	end

	local mx, my = InputValue("mousedx"), invertedMouse*InputValue("mousedy")

	local rotdiv = 200/zoomlevel
	if planevox ~= 0 then rotdiv = rotdiv * 2 end

	local CameraRot = QuatLookAt(plane.barrel.pos, aimpos)
	CameraRot = QuatRotateQuat(CameraRot, QuatEuler(-my/rotdiv, -mx/rotdiv, 0))
	plane.barrel.rot = CameraRot

	local newT = Transform(plane.barrel.pos, CameraRot)
	SetCameraTransform(newT, zoomlevel*10)
	SetPlayerTransform(GetPlayerTransform())
	PlayLoop(planesound, GetCameraTransform().pos, 0.5)
end

-- Controls shooting, called every tick(dt) when firing
function Shoot()
	if shoottimers[selectedshell] > 0 then
		return
	end

	local distance = GetShootDistance()
	local barrelEnd = TransformToParentPoint(plane.barrel, Vec(0, -1, -2))
	local dir = VecSub(aimpos, barrelEnd)

	airstrikeprojectileHandler.shells[airstrikeprojectileHandler.shellNum] = deepcopy(airstrikeprojectileHandler.defaultShell)
	loadedShell = airstrikeprojectileHandler.shells[airstrikeprojectileHandler.shellNum]
	loadedShell.active = true
	loadedShell.pos = barrelEnd
	loadedShell.cannonLoc = plane.barrel
	loadedShell.type = selectedshell
	loadedShell.predictedBulletVelocity = VecScale(dir, velocity)
	airstrikeprojectileHandler.shellNum = (airstrikeprojectileHandler.shellNum%#airstrikeprojectileHandler.shells) +1

	-- Shottimers (with and without cooldown)
	if nocd then
		if selectedshell == 2 or selectedshell == 3  or selectedshell == 4 then
			shoottimers[selectedshell] = shotDelay * selectedshell
		else
			shoottimers[selectedshell] = shotDelay
		end
	else
		if selectedshell == 1 then
			shoottimers[selectedshell] = shotDelay
		elseif selectedshell == 2 then
			shoottimers[selectedshell] = shotDelay * 7
		elseif selectedshell == 3 then
			shoottimers[selectedshell] = shotDelay * 50
		elseif selectedshell == 4 then
			shoottimers[selectedshell] = shotDelay * 80
			-- Plays trigger beep for nuke, only when cooldown is enabled
			if flycamenabled == false then 
				PlaySound(triggersound, GetCameraTransform().pos, 1) 
			end
		end
	end
	
	if selectedshell ~= 1 then PlaySound(gunsounds[selectedshell], plane.barrel.pos, 10) end
	lightTimer = 0.05
end

function ProjectileOperations(projectile, dt)

    projectile.predictedBulletVelocity = VecAdd(projectile.predictedBulletVelocity,(VecScale(Vec(0, 0, 0), dt)))
    local point2 = VecAdd(projectile.pos,VecScale(projectile.predictedBulletVelocity,dt))
	local dir = VecNormalize(VecSub(point2, projectile.pos))
    local hit, dist1 = QueryRaycast(projectile.pos, dir, VecLength(VecSub(point2, projectile.pos)))
	
	-- If projectile hits something, then starts process of doing damage
    if hit then 
	
		-- Determine the hit position and remove flying projectile
        hitPos = VecAdd(projectile.pos, VecScale(VecNormalize(VecSub(point2,projectile.pos)),dist1))
		projectile.active = false
		
		-- If hit with 25mm projectile
		if projectile.type == 1 then
			MakeHole(hitPos, 1, 0.85, 0.7)
			SpawnParticle("smoke", hitPos, Vec(0, 1.0+math.random(1,10)*0.1, 0), 1.5, 1.5)
			PlaySound(explosionsounds[selectedshell], hitPos, 0.5)
			
		-- If hit with 40mm projectile
		elseif projectile.type == 2 then
			Explosion(hitPos, 1.5)
			
		-- If hit with Gypsy Shell (240mm) projectile	
		elseif projectile.type == 3 then
			local interval = 360 / 9
			for degrees = 1, 360, interval do
				local x = hitPos[1] + 5 * math.sin(degrees) 
				local z = hitPos[3] + 5 * math.cos(degrees)
				local hitPosition = Vec(x, hitPos[2],z)				
				Explosion(hitPosition, 6)
			end
			Explosion(hitPos, 4)
			
		-- If hit with Nuclear Shell
		elseif projectile.type == 4 then
		
			-- Play the sound effect
			PlaySound(explosionsounds[selectedshell], hitPos, 6)
			
			-- Start Explosion (including light flash and shockwave)
			Explosion(hitPos, 4)
			MakeHole(hitPos,4,4,4,"silent")
			flashTimer = 2500
			shockwaveSize = desiredSize
			
			-- Mushroom Cloud
			local mushHeight = 35
			ParticleReset()
			ParticleType("plain")
			ParticleCollide(1, 1, "constant", 0.05)
			ParticleDrag(0.1,0.2,"easein")
			ParticleGravity(1)
			ParticleRadius(6.0)
			ParticleColor(.7,.7,.7)
			ParticleRotation(.2)
			ParticleAlpha(0.9,0, "smooth",0.1, 0.3)
			
			-- Trunk of mushroom cloud
			for i=0, mushHeight-2, 5 do
				local spot = VecAdd(hitPos, Vec(0, i, 0))
				SpawnParticle(spot,Vec(0,1,0),7.0)
			end
			
			-- Cap of mushroom cloud
			ParticleColor(.5,.5,.5)
			ParticleRadius(15.0)
			local centreTop = VecAdd(hitPos, Vec(0, mushHeight, 0))
			SpawnParticle(centreTop,Vec(0,-1,0), 12)
			for degrees = 1, 360, 30 do
				local x = centreTop[1] + 18 * math.sin(degrees) 
				local z = centreTop[3] + 18 * math.cos(degrees)
				local cap = Vec(x, centreTop[2],z)
				SpawnParticle(cap,Vec(0,1,0), 10)	
			end
		end
	end
	
	-- Projectile smoke trail
	SpawnParticle("smoke", projectile.pos, Vec(0, 0, 0), 0.5*projectile.type, 0.4)

	-- Draws image of projectile shape
	local width = 0.35
	local length = 1
	local rot = QuatLookAt(projectile.pos, point2)
	local spritepos = TransformToParentPoint(Transform(projectile.pos, rot), Vec(0, 0, -1))
	rot = QuatRotateQuat(rot, QuatEuler(-90, 0, 0))
	local transform = Transform(spritepos, rot)
	DrawSprite(shellsprites[projectile.type], transform, width, length, 1, 1, 1, 1, true, false)
    projectile.pos = point2
end

-- One iteration of nuclear shockwave
function shockwave(hitPos,size)
	ParticleReset()
	ParticleRadius(3)
	ParticleAlpha(0.5,0, "smooth",0.1, 0.3)
	ParticleCollide(0.0)
	ParticleGravity(-0.2)
	
	local ring = desiredSize + 1 - size	
	local holeSize = 5
	local ringSpacing = 5
	local circumference = 2 * math.pi * ring * ringSpacing
	local interval = 360 / (circumference / holeSize)
	
	for degrees = 1, 360, interval do
		local x = hitPos[1] + ringSpacing * ring * math.sin(degrees) * math.random(8,10) * 0.1
		local y = hitPos[2]
		local z = hitPos[3] + ringSpacing * ring * math.cos(degrees) * math.random(8,10) * 0.1
		local hitMe = Vec(x,y,z)
		MakeHole(hitMe, holeSize, holeSize, holeSize, "silent")
		MakeHole(VecAdd(hitMe,Vec(0,holeSize,0)), holeSize, holeSize, holeSize, "silent")
		MakeHole(VecAdd(hitMe,Vec(0,-holeSize,0)), holeSize, holeSize, holeSize, "silent")
		SpawnParticle(VecAdd(hitMe,Vec(0,1.5,0)), Vec(0,0,0), 6)
		SpawnFire(hitMe)
	end
	-- Sets shockwave to the next ring outwards
	shockwaveSize = shockwaveSize - 1
end
	
function tick(dt)

	-- Updates all airborne projectiles
	for key, shell in ipairs(airstrikeprojectileHandler.shells) do
		if shell.active then
			ProjectileOperations(shell, dt)
		end
	end

	if GetString("game.player.tool") == "gypsyairstrike" and GetPlayerVehicle() == 0 then
		SetBool("game.input.locktool", flycamenabled)
		firing = InputDown("usetool")
		airstrikeenabled = true
		if equipTimer < 1 then
			equipTimer = equipTimer + dt * 2
		end

		if firing then
			Shoot()
			if selectedshell == 1 then PlayLoop(gunsounds[selectedshell], plane.barrel.pos, 0.4) end
			if selectedshell == 4 then PlayLoop(gunsounds[selectedshell], plane.barrel.pos, 0.4) end
		end

		if InputPressed("R") then
			PlaySound(switchsound, GetCameraTransform().pos, 0.4)
			if selectedshell == 4 then selectedshell = 1 else selectedshell = selectedshell + 1 end
		end

		if InputPressed("rmb") then flycamenabled = not flycamenabled end
		if InputPressed("esc") then flycamenabled = false end
		if flycamenabled then FlyCam() end
		
		-- Nuclear shell flash
		if flashTimer > 0 then
			PointLight(hitPos, 1, 0.1, 0, flashTimer)
			flashTimer = flashTimer - dt*500
		end
		
		-- Iterates shockwave for nuke
		if shockwaveSize > 0 then
			shockwave(hitPos,shockwaveSize)
		end
		
		aimpos = flycamenabled and GetCamLookPos() or GetAimPos()

		local b = GetToolBody()
		if b ~= 0 then
			toolTrans = GetBodyTransform(b)
			toolPos = TransformToParentPoint(toolTrans, Vec(0.35, -0.6, -2.1))

			-- AC-130 muzzle flash
			if lightTimer > 0 then
				PointLight(plane.barrel.pos, 1, 1, 1, 1)
				lightTimer = lightTimer - dt
			end

			-- Sets radio hand controller properties
			if body ~= b then
				body = b
				local shapes = GetBodyShapes(b)
				radioshape = shapes[1]
				antennashape = shapes[2]
				planeshape = shapes[3]
				blinkshape = shapes[4]
				radiotransformation = GetShapeLocalTransform(radioshape)
				antennatransformation = GetShapeLocalTransform(antennashape)
				planetransformation = GetShapeLocalTransform(planeshape)
				blinktransformation = GetShapeLocalTransform(blinkshape)
			end
			
			if equipTimer > 1 and not radioactivated then
				PlaySound(beepsound, GetCameraTransform().pos, 0.4)
				radioactivated = true
			end

			if radioactivated then
				SetShapeEmissiveScale(radioshape, 0.5)
			else
				SetShapeEmissiveScale(radioshape, 0)
			end

			if InputDown("lmb") then
				SetShapeEmissiveScale(blinkshape, 5)
			else
				SetShapeEmissiveScale(blinkshape, 0)
			end

			rt = TransformCopy(radiotransformation)
			rt.rot = QuatRotateQuat(rt.rot, QuatEuler(0, 0, -15))
			SetShapeLocalTransform(radioshape, rt)

			bt = TransformCopy(blinktransformation)
			bt.rot = QuatRotateQuat(bt.rot, QuatEuler(0, 0, -15))
			SetShapeLocalTransform(blinkshape, bt)

			-- Radio antenna
			at = TransformCopy(antennatransformation)
			at.pos = VecAdd(at.pos, Vec(0, equipTimer*0.4, 0))
			at.rot = QuatRotateQuat(at.rot, QuatEuler(0, 0, -15))
			SetShapeLocalTransform(antennashape, at)

			SetShapeLocalTransform(planeshape, TransformToLocalTransform(GetBodyTransform(GetToolBody()), plane))
		end

		planefwdpos = TransformToParentPoint(plane, Vec(-0.15, 0, 0))
		planerotation = QuatRotateQuat(plane.rot, QuatEuler(0, 0, 0.1))
		plane.pos = planefwdpos
		plane.rot = planerotation
		plane.barrel.pos = TransformToParentPoint(plane, Vec(2.5, 4.5, 0))
	else
		airstrikeenabled = false
		radioactivated = false
		equipTimer = 0
	end

	for i=1, #shoottimers do
		if shoottimers[i] > 0 then
			shoottimers[i] = shoottimers[i] - dt
		else
			shoottimers[i] = 0
		end
	end
end

function draw()
	-- Crosshairs and text for flycam
	if flycamenabled and GetPlayerVehicle() == 0 then
		UiPush()
			UiTranslate(UiCenter(), UiMiddle())
			UiColor(1, 1, 1, 0.5)
			UiAlign("center middle")
			local crosshairid = selectedshell > 4 and 2 or selectedshell
			UiImage("MOD/img/AC130crosshair"..crosshairid..".png")
		UiPop()

		UiPush()
			UiTranslate(UiCenter(), UiMiddle()+UiMiddle()/2+50)
			UiColor(1, 1, 1)
			UiAlign("center")
			UiFont("regular.ttf", 26)
			if selectedshell ~= 3 and selectedshell ~= 4 or shoottimers[selectedshell] == 0 then
				UiText("Ready")
			else
				UiText(tostring(math.ceil(shoottimers[selectedshell])))
			end
		UiPop()
	end
	if airstrikeenabled and GetPlayerVehicle() == 0 then
		UiPush()
			UiTranslate(80, UiMiddle()+UiMiddle()/2)
			UiColor(0.4, 0.4, 0.4)
			UiAlign("left")
			UiFont("regular.ttf", 26)
			UiTextOutline(0,0,0,1,0.2)

			UiPush()
				UiColor(1, 1, 1)
				UiText("R key: Change ammo")
				UiTranslate(0, 30)
				UiText("RMB: Gun camera")
			UiPop()

			UiTranslate(0, 70)
			UiPush()
				if selectedshell == 1 then UiColor(1, 1, 1) end
				UiText("25mm")
			UiPop()

			UiTranslate(0, 30)
			UiPush()
				if selectedshell == 2 then UiColor(1, 1, 1) end
				UiText("40mm")
			UiPop()

			UiTranslate(0, 30)
			UiPush()
				if selectedshell == 3 then UiColor(1, 1, 1) end
				UiText("Gypsy Shell (240mm)")
			UiPop()
			UiTranslate(0, 30)
			UiPush()
				if selectedshell == 4 then UiColor(1, 1, 1) end
				UiText("Nuclear Shell")
			UiPop()
			UiTranslate(0, 30)
			UiPush()
		UiPop()
	end
end