script_name('BetterSkybox')
script_author('guru_guru')
script_description('its just a bunch of semi-spheres with some nice clouds textures that follows you around everywhere')
script_dependencies('MoonAdditions')
script_url('http://gtaforums.com/topic/894333-better-skybox/')
script_properties('work-in-pause')


bEnable = true
bDebug = true -- Prints info to moonloader.log .
bDisableClouds = true -- This also disables the moon.If you want more control over this,download MixSets (http://mixmods.blogspot.com.br/2015/12/mod-mix-sets.html) and search for DisaVlmtCloud , DisaLowCloud and DisaSkyStuff functions
bGetWeatherReportEachIngameHour = true  -- This should improve script perfomance at the cost of changing the skies each hour.If you are going to mess around with the weather I recommend to disable this.

iDawn = 5 -- Hour which the sun starts to rise.
iDusk = 22 -- Hour which the sun starts to fall.

-- How much the skybox will rotate on each fram
fRotationSpeed = {
	Sunny 	= 0.001,
	Cloudy 	= 0.002,
	Night 	= 0.0
}

spModelsID = {
	LS_Sunny =	15134,
	SF_Sunny =	15135,
	LV_Sunny =	15136,
	CS_Sunny =	15137,
	DS_Sunny =	15138,

	LS_Cloudy =	15139,
	SF_Cloudy =	15140,
	LV_Cloudy =	15141,
	CS_Cloudy =	15142,
	DS_Cloudy =	15143,

	LS_Night = 	15144,
	SF_Night = 	15145,
	LV_Night = 	15146,
	CS_Night = 	15147,
	DS_Night = 	15148
}

sampModelsID = {
	LS_Sunny =	3890,
	SF_Sunny =	3892,
	LV_Sunny =	3893,
	CS_Sunny =	3895,
	DS_Sunny =	3897,

	LS_Cloudy =	3898,
	SF_Cloudy = 3899,
	LV_Cloudy =	3900,
	CS_Cloudy =	3901,
	DS_Cloudy =	3905,

	LS_Night = 	3906,
	SF_Night = 	3910,
	LV_Night = 	3914,
	CS_Night = 	3915,
	DS_Night = 	3916
}

centerID = 4227


local mad = require 'MoonAdditions'
local memory = require 'memory'

function main()
	wait(0) 
	readSettings()
	loadSky()
	while true do
		local sky = createSkies()
		drawSky(sky)
		deleteSky(sky)
		while bInSavegameMenu() do wait(0) end
	end
end

function readSettings()
	if bEnable == false then wait(-1) end
	if bDisableClouds == true then
		memory.fill(0x53E121, 0x90, 5, true)
		memory.fill(0x53E1B4, 0x90, 5, true)
		memory.fill(0x53DCA2, 0x90, 5, true)
		memory.fill(0x53DFA0, 0x90, 5, true)
		memory.fill(0x53DCA2, 0x90, 5, true)
	end

	return
end

function loadSky()

	local tIDs = getIDsTable()
	local key,id
	for key,id in pairs(tIDs) do
		requestModel(tIDs)
		if bDebug then print("@load_sky(): Requesting "..key.." w/ ID "..id) end
	end
	requestModel(centerID)
	loadAllModelsNow()
	return
end

function createSkies()
	local sky = {}
	sky.Sunny = createSky("Sunny")
	sky.Cloudy = createSky("Cloudy")
	sky.Night = createSky("Night")
	return sky
end

function createSky(skyType)
	local new_sky = {}
 	local z = 0.0
	local key,id

	new_sky.center = createObjectNoOffset(centerID, 0.0,0.0,0.0)
	if bDebug then print("@createSkies():	"..skyType) end
	hideObject(new_sky.center)
	local tIDs = getIDsTable()
	for key,id in pairs(tIDs) do
		if string.find(key,skyType) then
			if bDebug then print("@createSkies():	Attemping to create "..key.." w/ modelID "..id.."...") end
			new_sky[key] = createObjectNoOffset(id, 0.0, 0.0, 0.0)
			hideObject(new_sky[key])
			attachObjectToObject(new_sky[key], new_sky.center, 0.0, 0.0, z, 0.0, 0.0, 0.0)
			z = z + 0.1
			if bDebug then print("@createSkies():	Created sky "..key.." ,modelID "..id.." and handle "..new_sky[key]) end
		end
	end

	return new_sky
end

function drawSky(skies)
	local tSkiesToDraw = getSkiesToDraw()

	while not bInSavegameMenu() do

		while bInInterior() do wait(0) end
		wait(0)
		local fWeatherInterpolation = memory.getfloat(0xC8130C,false)
		if bGetWeatherReportEachIngameHour then
			if fWeatherInterpolation >= 0.0 and fWeatherInterpolation <= 0.05 then
				tSkiesToDraw = getSkiesToDraw()
				clearSky(skies,tSkiesToDraw)
			end
		else
			tSkiesToDraw = getSkiesToDraw()
			clearSky(skies,tSkiesToDraw)
		end

		local x,y,z = getActiveCameraCoordinates()
		local kType,tType , kSky, hSky
		local kStatus,kDummy

		for kSky,kStatus in pairs(tSkiesToDraw) do
			for kType,tType in pairs(skies) do
				if tType[kSky] ~= nil then
					drawThisSky(tType[kSky], kType, kStatus)
					moveThisSky(tType.center, kType, x,y,z)
				end
			end
		end

	end
	return
end

function clearSky( tSky,SkiesToDraw )
	local sType,tType , key,handle
	for sType,tType in pairs(tSky) do
		for key,handle in pairs(tType) do
			if SkiesToDraw[key] == nil then
				setObjectScale(handle,0.0)
				SkiesToDraw[key] = "HIDE"
				if bDebug and testCheat("getclearSky") then print("@clearSky: set sky "..key.." scale to 0.0") end
			end
		end
	end
	return
end

function drawThisSky(hSky,typeID,status)
	if typeID == "Sunny" then setSkySunnyAlpha(hSky,status) end
	if typeID == "Cloudy" then setSkyCloudyAlpha(hSky,status) end
	if typeID == "Night" then setSkyNightAlpha(hSky,status) end
	setObjectScale(hSky, getScale())
	return
end

function moveThisSky(hCenter,kType,x,y,z)
	setObjectCoordinates(hCenter, x, y, z)
	local rotationSpeed = fRotationSpeed[kType] * getTimeStepValue()
	local fromAngle = getObjectHeading(hCenter)
	local toAngle = fromAngle + rotationSpeed
	setObjectHeading(hCenter, toAngle)
	return
end

function deleteSky(sky)
	local kType,tType , kSky, hSky
	for kType,tType in pairs(sky) do
		for kSky, hSky in pairs(tType) do
			deleteObject(hSky)
			if bDebug then print("@deleteSky: deleting obj "..kSky.." w/ handle "..hSky) end
		end
	end
	return
end

function bInSavegameMenu()
	if bInMenu() then
		local menuID = readMemory(0xBA68A5, 1, false)
		if menuID == 6 or menuID == 9 or menuID == 11 or menuID == 13 or menuID == 17 or menuID == 18 or menuID == 19 then
			return true
		end
	end
	return false
end

function bInMenu()
	local bInMenu = memory.read(0xBA67A4, 1, false)
	if bInMenu == 1 then return true end
	return false
end

function bInInterior()
	if getActiveInterior() == 0 then return false end
	if bDebug and testCheat("interior") then printHelpString("Yeah,you are on a Interior") end
	return true
end

function getSkiesToDraw()
	local weather= {[1] = readMemory(0xC81320, 4, false),[2]=readMemory(0xC8131C, 4, false)}
	local draw = {}
	local bIsDay,bIsNight = bIsDayHours(),bIsNightHours()
	local i
	for i = 1, #weather do
		while true do
			if weather[i] >= 0 and weather[i] <= 3 then
				if bIsDay then setStatus(draw,"LS_Sunny",i) end
				if bIsNight then setStatus(draw,"LS_Night",i) end
				break
			end
			if weather[i] == 5 or weather[i] == 6 then
				if bIsDay then setStatus(draw,"SF_Sunny",i) end
				if bIsNight then setStatus(draw,"SF_Night",i) end
				break
			end
			if weather[i] == 10 or weather[i] == 11 then
				if bIsDay then setStatus(draw,"LV_Sunny",i) end
				if bIsNight then setStatus(draw,"LV_Night",i) end
				break
			end
			if weather[i] == 13 or weather[i] == 14 then
				if bIsDay then setStatus(draw,"CS_Sunny",i) end
				if bIsNight then setStatus(draw,"CS_Night",i) end
				break
			end
			if weather[i] == 17 or weather[i] == 18 then
				if bIsDay then setStatus(draw,"DS_Sunny",i) end
				if bIsNight then setStatus(draw,"DS_Night",i) end
				break
			end
			if weather[i] == 4 then  setStatus(draw,"LS_Cloudy",i) break end
			if weather[i] >= 7 and weather[i] <= 9 then  setStatus(draw,"SF_Cloudy",i) break end
			if weather[i] == 12 then  setStatus(draw,"LV_Cloudy",i) break end
			if weather[i] == 15 or weather[i] == 16 then  setStatus(draw,"CS_Cloudy",i) break end
			if weather[i] == 19 then  setStatus(draw,"DS_Cloudy",i) break end
			break
		end
	end

	if bDebug and testCheat("getSkies") then
		local kSky,kStatus
		for kSky,kStatus in pairs(draw) do
			print("@getSkiesToDraw(): "..kSky.." = "..kStatus)
		end
		printHelpString("Printed Skies to draw.")

	end

	return draw
end

function setStatus(tDraw,kSky,curr_or_next_weather)
	if curr_or_next_weather == 1 and tDraw[kSky] == nil then tDraw[kSky] = "FADE_OUT" return end
	if curr_or_next_weather == 2 and tDraw[kSky] == nil then tDraw[kSky] = "FADE_IN" return end
	if tDraw[kSky] ~= nil then tDraw[kSky] = "SHOW" return end
	if bDebug then printHelpString("setStatus is broken,pls fix  xdDDD") end
	return
end

function setSkySunnyAlpha(sky,status)
	local bIsDay = bIsDayHours()
	if status == nil or status == "HIDE"  then mad.set_object_model_alpha(sky,0)	return end
	if status == "FADE_IN" and bIsDay then doFadeIn(sky) return end
	if status == "FADE_OUT" and bIsDay then doFadeOut(sky) return end
	if status == "SHOW" then
		local hh,mm = getTimeOfDay()
		local dawn,dusk = iDawn,iDusk

		if hh == dawn then doFadeIn(sky) return end
		if hh > dawn and hh < dusk then mad.set_object_model_alpha(sky,255) return end
		if hh == dusk then doFadeOut(sky) return end
		mad.set_object_model_alpha(sky,0) return
	end
	return
end

function setSkyCloudyAlpha(sky, status)
	if status == nil or status == "HIDE"  then mad.set_object_model_alpha(sky,0)	return end
	if status == "FADE_IN" then doFadeIn(sky) return end
	if status == "FADE_OUT" then doFadeOut(sky) return end
	if status == "SHOW" then mad.set_object_model_alpha(sky,255) return end
	return
end

function setSkyNightAlpha(sky, status)
	local bIsNight = bIsNightHours()
	if status == nil or status == "HIDE"  then mad.set_object_model_alpha(sky,0) return end
	if status == "FADE_IN" and bIsNight then doFadeIn(sky) return end
	if status == "FADE_OUT" and bIsNight then doFadeOut(sky) return end
	if status == "SHOW" then
		local hh,mm = getTimeOfDay()
		local dawn,dusk = iDawn,iDusk

		if hh == dusk then doFadeIn(sky)	return end
		if hh > dusk or hh < dawn then mad.set_object_model_alpha(sky,255) return end
		if hh == dawn then doFadeOut(sky) return end
		mad.set_object_model_alpha(sky,0) return
	end
	return
end

function doFadeIn(sky)
	local fWeatherInterpolation = memory.getfloat(0xC8130C,false) * 255.0
	local alpha = math.floor(fWeatherInterpolation)
	mad.set_object_model_alpha(sky,alpha)
	return
end

function doFadeOut(sky)
	local fWeatherInterpolation = memory.getfloat(0xC8130C,false)  * -255.0
	fWeatherInterpolation = fWeatherInterpolation + 255.0
	local alpha = math.floor(fWeatherInterpolation)
	mad.set_object_model_alpha(sky,alpha)
	return
end

function bIsDayHours()
	local hh,mm =  getTimeOfDay()
	if hh >= iDawn and hh <= iDusk then return true end
	return false
end

function bIsNightHours()
	local hh,mm =  getTimeOfDay()
	if hh >= iDusk or hh <= iDawn then return true end
	return false
end

function hideObject(obj)
	mad.set_object_model_alpha(obj, 0)
	setObjectScale(obj, 0.0)
	setObjectCollision(obj, 0.0)
	return
end

function getScale()
	local fScale = memory.getfloat(0xB76848,false)
	fScale = fScale / 2100.0
	return fScale
end


function onSaveGame()
	if bDebug then print("GAME HAS BEEN SAVED!") end
	return
end

function getIDsTable()
	if isSampLoaded() then
		if bDebug then print("@getIDsTable:	Using SAMP models table... ") end
		return sampModelsID
	end

	if bDebug then print("@getIDsTable:	Using SP models table... ") end

	return spModelsID
end
