--	Underwater dust and caustic screen effects by Martini and Krys. Remember to credit us!
-- 
-- 		Please make sure you properly followed the instructions of Krys' Particle System Plugin prior to implementing this module.		
--
--		Look for any information provided after the syntax with ''--'' for customization of the particles. 
--		Changing values outside of the remarks might result in undesirable effects and is not advised.
--		Edit with caution.
--  
--		In FLEP, enable ''GFX2 Customizer'' and change ''Underwater dust amount'' to 0 to prevent default dust from spawning.
--
--	    import the .BMP's to the SPRITE_SLOT of your choice and match the spriteIndex numbers, or edit the numbers to your liking in this code:
--		to change the sprite index of particles, edit the values of part.spriteIndex in ever function fx(). 
--  	current sprite slot used is CUSTOM_SPRITES. To change the sprite slot, edit the particleGroup.spriteSlot CUSTOM_SPRITES to the one you wish to use.
--  
--  	Texture 3,  5		;generally used for dust.
--  	Texture 6,  7		;used for underwater specks that have motion.
--		Texture 9,  10		;textures used for the 3D caustics.
--		Texture 14, 15, 16	;textures used for creatures, think jellyfish, crustaceans or underwater insects.
--		Texture 17, 18 		;textures used the screen caustics.  
--
--			HAPPY BUILDING, 
--					Martini
--

local waterdust
local dust 
local specks
local creatures 

local function dustfx()

	local part = createSpritePart (dust)
	part.roomIndex = Camera.roomIndex

	part.pos.x = randomFloat (-4096, 4096) 
	part.pos.z = randomFloat (-4096, 4096) 
	part.pos.y = randomFloat (-4096, 4096) 
	
	part.pos = part.pos + Camera.pos
	
	local dir = projectToScreen(part.pos)
		
	if not dir then
		return
	end

	partDetach(part)

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)
	
	if hasAllFlags(roomFlags, ROOM_WATER) then

		local basecol = randomInt (200, 255) -- a number from 0 (black) to 255 (white) this local used used to determine the RGB colour.  in part.Col

		part.lifeSpan  = 22 + randomFloat (1, 11)
		part.sizeStart = randomFloat (40, 170)
		part.sizeEnd   = part.sizeStart
		part.sizeRatio = randomFloat (-0.3, 0.3)
	
		--manipulate the number behind 'basecol' below to edit the colours of the particle.
		--Numbers go from 0 (no colour) to 1 (max colour, 255 on RGB)
	
		part.colStart.r = (basecol * 0.7)  
		part.colStart.g = (basecol * 0.9)  
		part.colStart.b = (basecol * 0.9)  
		
		part.fadeIn = part.lifeSpan * 0.5
		part.fadeOut= part.lifeSpan * 0.4
	
		part.vel.y = randomFloat (-5, 5)
		part.vel.x = randomFloat (-5, 5)
		part.vel.z = randomFloat (-5, 5)
		
		part.accel.x = (part.vel.x * 0.08)
		part.accel.y = (part.vel.y * 0.08)
		part.accel.z = (part.vel.z * 0.08)
		
		part.rot = degToRad(randomInt(0, 360))
		part.rotVel = randomFloat (-0.12, 0.12)
		
		part.spriteIndex = randomInt (3, 5) -- change these numbers to whatever sprite Index (number) you wish to use.
	end
end

local function updatedust(part)
		
		local dir = projectToScreen(part.pos)
		local dir2 = projectToScreen(part.pos + part.vel)
		
	if dir and dir2 then
		dir = dir2 - dir
		local angle = atan2(dir.y, dir.x)
 	
		angle = wrapAngle(angle + PI_QUART)
		
		part.rot = angle
	end

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if not hasAllFlags(roomFlags, ROOM_WATER) then
		
		partKill(part)		
	end

end

local function orientedSprite(part)

    local v1 = part.pos
    local v2 = part.pos + vectorNormalize(part.vel)
    local p1 = projectToScreen(v1)
    local p2 = projectToScreen(v2)
	
    if p1 and p2 then
        local pdif = p2 - p1
        local angle = wrapAngle(atan2(-pdif.y, pdif.x))
        part.rot = angle
    end
	
	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if not hasAllFlags(roomFlags, ROOM_WATER) then
		
		partKill(part)		
	end

end

local function speckfx()

	local part = createSpritePart (specks)

	part.roomIndex = Camera.roomIndex

	part.pos.x = randomFloat (-5120, 5120)
	part.pos.z = randomFloat (-5120, 5120)
	part.pos.y = randomFloat (-5120, 5120)

	part.pos = part.pos + Camera.pos

	local dir = projectToScreen(part.pos)
		
	if not dir then
		return
	end

	partDetach(part)

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if hasAllFlags(roomFlags, ROOM_WATER) then

		part.lifeSpan  = 40 + randomFloat (1, 71)
		part.sizeStart = randomFloat (part.lifeSpan * 0.1, part.lifeSpan * 1.2)
		part.sizeEnd   = part.lifeSpan * 0.7
		
		part.fadeIn = part.lifeSpan * 0.1
		part.fadeOut= part.lifeSpan * 0.5
		
		part.vel.y = randomFloat (-3, 3)
		part.vel.x = randomFloat (-3, 3)
		part.vel.z = randomFloat (-3, 3)
		
		local magnitude = vectorLength(part.vel)
		local brightness = magnitude / 5.2

		part.colStart.r = part.lifeSpan * 0.6 * brightness
		part.colStart.g = clamp(part.lifeSpan * randomInt (1.1, 2), 128, 225) * brightness
		part.colStart.b = clamp(part.lifeSpan * randomInt (1.1, 2), 128, 225) * brightness
				
		part.rot = degToRad( randomInt(0, 360) )
		part.rotVel = randomFloat (-0.01, 0.01)
		
		part.spriteIndex = randomInt(6, 7)
	end
	
end

local function creaturesfx()

	local part = createSpritePart (creatures)

	part.roomIndex = Camera.roomIndex

	part.pos.x = randomFloat (-5120, 5120)
	part.pos.z = randomFloat (-5120, 5120)
	part.pos.y = randomFloat (-5120, 5120)

	part.pos = part.pos + Camera.pos

	local dir = projectToScreen(part.pos)
		
	if not dir then
		return
	end

	partDetach(part)

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if hasAllFlags(roomFlags, ROOM_WATER) then

		part.lifeSpan  = randomFloat(55, 95) + 30
		part.sizeStart =  64
		part.sizeEnd   =  64
		
		part.fadeIn = part.lifeSpan * 0.2
		part.fadeOut= part.lifeSpan * 0.2
		
		part.vel.x = randomFloat (-8, 8)		
		part.vel.y = randomFloat (-8, 2)
		part.vel.z = randomFloat (-8, 8)
		

		
		local magnitude = vectorLength(part.vel)
		local brightness = magnitude / 5.2

		part.colStart.r = part.lifeSpan * 0.6 * brightness
		part.colStart.g = part.lifeSpan * 0.9 * brightness
		part.colStart.b = part.lifeSpan * 0.9 * brightness
		
		part.spriteIndex = randomInt(14, 16)
		
		part.data.velx = part.vel.x
		part.data.vely = part.vel.y
		part.data.velz = part.vel.z
		
		local velVec = vectorNormalize(part.vel)
        local randVec = randomSpherePoint(1)
        local perpVec = vectorNormalize(randVec - (velVec * vectorDot(randVec, velVec)))

        part.data.perpx = perpVec.x
        part.data.perpy = perpVec.y
        part.data.perpz = perpVec.z
		
	end
end

local function creaturesupdate(part)

    local t = part.t * PI * 2 * (1)

    local input = createVector(part.data.perpx, part.data.perpy, part.data.perpz)
    local axis = createVector(part.data.velx, part.data.vely, part.data.velz)

    local rotated = rotateVectorByAxisAngle(input, axis, wrapAngle(t))

    part.vel = axis + rotated * randomFloat(3, 6) -- last number is the spiral diameter, the higher the number, the large the ''circle'' that the creature particle makes.
	
	local v1 = part.pos
    local v2 = part.pos + vectorNormalize(part.vel)
    local p1 = projectToScreen(v1)
    local p2 = projectToScreen(v2)
	
    if p1 and p2 then
        local pdif = p2 - p1
        local angle = wrapAngle(atan2(-pdif.y, pdif.x))
        part.rot = angle
    end

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if not hasAllFlags(roomFlags, ROOM_WATER) then
		
		partKill(part)		
	end

end

local caustics
local function causticsfx()

	local part = createSpritePart (caustics)

	part.roomIndex = Camera.roomIndex


	part.pos.x = randomFloat (-4096, 4096)
	part.pos.z = randomFloat (-4096, 4096)
	part.pos.y = randomFloat (-4096, 4096)
	
	part.pos = part.pos + Camera.pos

	partDetach(part)

	partCollideWalls(part, 1)
	partCollideFloors(part, 1)

	local roomFlags = getRoomFlags(part.roomIndex)

	if hasAllFlags(roomFlags, ROOM_WATER) then

		part.spriteIndex = randomInt (9, 10)

		local col = randomInt (128, 255) --col can be changed for a min to max range of 0, 255

		part.lifeSpan  = randomFloat (32, 52)
		part.sizeStart = randomFloat (768, 1536)
		part.sizeEnd   = part.sizeStart

		part.fadeIn  = part.lifeSpan * 0.5
		part.fadeOut = part.lifeSpan * 0.5

		-- edit the number after ''col'' to determine the R, G, B values on a scale from 200 to 255.
		-- 0 = 128
		-- 1 = 255

		part.colStart.r = col * 0.12
		part.colStart.g = col * 0.15
		part.colStart.b = col * 0.15
		
		part.vel.x = randomFloat (-5, 5)
		part.vel.y = randomFloat (-5, 5)
		part.vel.z = randomFloat (-5, 5)
		
		part.rot = degToRad( randomInt(0, 360))
		part.rotVel = randomFloat(-0.001, 0.001)
	end
end

local screen
local function screenfx()

	local part = createSpritePart (screen)

	part.roomIndex = Camera.roomIndex

	local x = randomFloat(0, 1)
	local y = randomFloat(0, 1)

	part.pos = screenCoords (x, y)

	local roomFlags = getRoomFlags(part.roomIndex)

	if hasAllFlags(roomFlags, ROOM_WATER) then

		part.spriteIndex = randomInt(17, 18)

		local col = randomInt (200, 255) --col can be changed for a min to max range of 0, 255
		local dist = distance(createVector(x, y, 0), createVector(0.5, 0.5, 0)) 
		local blend = (dist / 0.71) ^ 3 -- Can edit the last number. A higher number results in a stronger fade out in the center of the screen. Lowering does the opposite.

		part.lifeSpan  = randomFloat (32, 52)
		part.sizeStart = randomFloat (500, 584)
		part.sizeEnd   = part.sizeStart * randomFloat(1.1, 1.5)

		part.fadeIn  = part.lifeSpan * 0.5
		part.fadeOut = part.lifeSpan * 0.5

		-- edit the number after ''col'' to determine the R, G, B values on a scale from 200 to 255.
		-- 0 = 200
		-- 1 = 255

		part.colStart.r = col * 0.42 * blend
		part.colStart.g = col * 0.65 * blend
		part.colStart.b = col * 0.65 * blend
		
		part.vel.x = randomFloat (-0.001, 0.001)
		part.vel.y = randomFloat (-0.001, 0.001)

		part.rot = degToRad( randomInt(0, 360))
		part.rotVel = randomFloat(-0.012, 0.012)
	end
end

local function screenupdate(part)

	local roomFlags = getRoomFlags(Camera.roomIndex)
	
		if not  hasAllFlags(roomFlags, ROOM_WATER) then
			partKill(part)	
		end

end	

local function initwaterdust()
--	edit the last number in this line to edit the amount of particles per frame. 
--
--	These numbers are on the higher side as they spawn in a radius around the camera, but only those in front of the camera will generate a particle. 
--	''Causticfx()'' is the only exception to this rule, it will spawn in a radius around the camera, and also behind it. Increasing in the for loop is not recommended
--
--	if effect is undesired, block out the whole ''for'' loop out entirely using ''--'' as sucH
--
--
--	for i = 1, spawnnumber do  
--		example()
--	end
--
-- Below follows a list what each particlefx() does in the level.
-- dustfx() 	 :generic dust. Usable in most levels
-- speckfx() 	 :additional clutter / dust. Can be used in most waters, especially murky waters and the seas/oceans.
-- causticsfx()  :3D caustics spawning in a wide radius. Usable in almost every environment.
-- creaturesfx() :tiny jellyfish and crustaceans. Use in warm and cold seas and oceans. Might work well in river levels, too.
-- screenfx() 	 ;the screen caustics effect when the camera submerges.


	for i = 1, 24 do  
        dustfx()
    end
    
    for i = 1, 20 do  
        speckfx()
    end

    for i = 1, 12 do  
		causticsfx()
    end

    for i = 1, 2 do  
		creaturesfx()
    end
	
    for i = 1, 4 do  
		screenfx()
    end

end

dust = createGroup(nil, updatedust)
dust.lightMode = LIGHT_DYNAMIC
dust.spriteSlot = SLOT_CUSTOM_SPRITES

specks = createGroup(nil, orientedSprite)
specks.lightMode = LIGHT_DYNAMIC
specks.spriteSlot = SLOT_CUSTOM_SPRITES

creatures = createGroup(creaturesfx, creaturesupdate)
creatures.spriteSlot = SLOT_CUSTOM_SPRITES
creatures.lightMode = LIGHT_DYNAMIC

waterdust = createGroup(initwaterdust, nil)
waterdust.lightMode = LIGHT_DYNAMIC
waterdust.spriteSlot = SLOT_CUSTOM_SPRITES

caustics = createGroup(nil, nil)
caustics.spriteSlot = SLOT_CUSTOM_SPRITES

screen = createGroup(nil, screenupdate)
screen.spriteSlot = SLOT_CUSTOM_SPRITES
screen.screenSpace = true