

--start position of the arrow when fired (relative to the player)
--this is only used to check ray cast when we don't hit an entity (i.e. to place arrow on terrain)
local xoffset = 0
local yoffset = 28.5
local zoffset = 0
--if the player is crouching the y offset should be less
local duckyoffset = 8.5
--how far the ray cast should check in front for a valid hit (only used for finding terrain)
local maxArrowRange = 3000

--how long the arrow is - determines how far out the target the arrow will stick 
--this depends on the entity you use as your arrow
--larger values move the arrow back more from the point of impact compared to the origin of the model
local arrowLength = 12

--the GunID of the bow (use Prompt(g_PlayerGunID) while holding in test mode and put the value here)
bowGunID = 62

--how long to wait before checking for a hit (to let animation play etc)
--try to make this delay line up with when you see the impact particles
--if you are getting weird results adjust this value till it works for your weapon
local fireDelay = 0.7

--position offset of the burning decal if firing a fire arrow 
--this is the decal placed at the arrow location (i.e. not the character particles)
local burnoffsetx = 0
local burnoffsety = 12
local burnoffsetz = -2

--damage per tick for burning characters 
local burnDamage = 10
--delay between damage when character is burning (in seconds)
--some characters will moan or react to damage so might not want this too low
local burnDamageDelay = 1
--how long a limb will burn for after being hit by a fire arrow 
local limbBurnTime = 9
--how long a non-character fire (i.e. wooden entity) will burn for 
local entityBurnTime = 4
--how much burn damage to deal to non-character entitys per tick 
local entityBurnDamage = 20
--how long the water particle emitter lives for
local waterEmitterLife = 0.5

--adjust the arrow type icons on screen
--use "[" and "]" to swap ammo type
local arrowIconPosX = 89
local arrowIconPosY = 1
local arrowIconSizeX = 10
local arrowIconSizeY = -1


------**anything below should only be changed if you know what it does**
local matdelay = fireDelay + 0.05 --adds a delay for GetBulletHit() info to be returned - otherwise we hit terrain
local newFireDelay,burnFireEntitys,extinguishFireEntitys = {},{},{}
local arrows,fireDecals,arrowUsed = {},{},{}
local newEntityBurnTime,burningEntities,newWaterTime,waterEmitter = {},{},{},{}
local newBurnDamageDelay,newyoffset,newmatdelay,state = 0,yoffset,matdelay,""
local firedRecently,checkHit = false,false
local bullethitx, bullethity, bullethitz, materialhit, thit
bowAmmoIndex = 0

--arrow types are "normal", "water" or "fire"
local normalarrowi, firearrowi, waterarrowi, arrowIconspr

local rad = math.rad
U = U or require "scriptbank\\utillib"
P = P or require "scriptbank\\physlib"


--sounds: slot 0 = pick up
function bowarrow_init(e)
	state = ""
end 

function bowarrow_main(e)	
	--Prompt(g_PlayerGunID)
	--get frame time for timer stuff later
	local frameTime = GetElapsedTime()
	--adds a tiny delay so other entities can load in (just to make sure all values are in lua)
	if state == "" then 
		state = "setup"
	elseif state == "setup" then 	
		SetUp() --store other entities like arrows and decals etc
		state = "wait for hit"
	elseif state == "wait for hit" then 	
		--handles changing arrow type 
		local key = GetKeyPressed(e,g_InKey,true)
		if key == "[" then 
			if currentArrowType == "normal" then 
				currentArrowType = "water"
				SetSpriteImage(arrowIconspr,waterarrowi)
			elseif currentArrowType == "fire" then 
				currentArrowType = "normal"
				SetSpriteImage(arrowIconspr,normalarrowi)
			else 
				currentArrowType = "fire"
				SetSpriteImage(arrowIconspr,firearrowi)
			end 
		elseif key == "]" then			
			if currentArrowType == "normal" then 
				currentArrowType = "fire"
				SetSpriteImage(arrowIconspr,firearrowi)
			elseif currentArrowType == "fire" then 
				currentArrowType = "water"
				SetSpriteImage(arrowIconspr,waterarrowi)
			else 
				currentArrowType = "normal"
				SetSpriteImage(arrowIconspr,normalarrowi)
			end 
		end 
		--shows the arrow type on screen 
		if g_PlayerGunID == bowGunID then 
			PasteSpritePosition(arrowIconspr, arrowIconPosX, arrowIconPosY)
		end 
		--Prompt("current ammo = "..currentArrowType)
		
		--handles checking the player is firing the bow 
		CheckFiredRecently()
		
		--try get information about last shot		
		local bx, by, bz, mat, t = GetBulletHit()
		--if information was got we can store it for later use 
		--(need to use new variables as the temporary ones are reset every frame)
		if mat ~= nil then 
			bullethitx, bullethity, bullethitz = bx,by,bz
			materialhit, newmatdelay = mat, matdelay
		end
		--if we have shot information 
		if checkHit or materialhit ~= nil then
			--there's a small delay because we might not get any result so we allow time
			newmatdelay = newmatdelay - frameTime
			if newmatdelay < 0 then 
				newmatdelay,materialhit = matdelay,-1
			end								
			if materialhit ~= nil then 				
				--check what we hit 
				--store some position and rotation info for ray casts and such 
				local pax,pay,paz = g_PlayerAngX,g_PlayerAngY,g_PlayerAngZ
				local rpax,rpay,rpaz = rad(pax),rad(pay),rad(paz)
				local ax,ay,az = pax+rad(180),pay+rad(180),paz+rad(180)
				local ox,oy,oz = U.Rotate3D(0,0,-arrowLength, rpax,rpay,rpaz)				
				local x3,y3,z3,fx,fy,fz = 0,0,0,0,0,0				
				--PromptDuration("hit material = "..materialhit,1000)						
				
				--get and store a free arrow of desired type 
				local arrowE = 0
				arrowE = GetFurthestEntity(arrows, bullethitx,bullethity,bullethitz)				
				if arrowE ~= nil then
					--PromptDuration("placed arrow "..arrowE,1000)
					local arrowObj = g_Entity[arrowE]['obj']
					--if materialhit > -1 we didn't hit terrain so we can use the bullethit info
					if materialhit > -1 then 
						x3,y3,z3 = bullethitx, bullethity, bullethitz						
						--we hit a character
						if materialhit == 2 then 
							HandleHittingACharacter()													
						
						else
							--try see if we hit a dynamic object 
							local ox4,oy4,oz4 = U.Rotate3D(0,0.1,-arrowLength*0.5, rpax,rpay,rpaz)
							local ox5,oy5,oz5 = U.Rotate3D(0,0.1,arrowLength*0.5, rpax,rpay,rpaz)
							local eray = IntersectAll(x3+ox4,y3+oy4,z3+oz4, x3+ox5,y3+oy5,z3+oz5, arrowObj)
							local ehit = nil 
							if eray ~= nil then 
								--PromptDuration("object hit "..eray,1000)
								ehit = P.ObjectToEntity(eray)
								if ehit ~= nil then 									
									--PromptDuration("entity hit "..ehit,1000)
								end 
							end 
							local ox6,oy6,oz6 = U.Rotate3D(0,0,-arrowLength, rpax,rpay,rpaz)
							fx,fy,fz = x3+ox6,y3+oy6,z3+oz6
							if currentArrowType == "fire" then 
								PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "fire", ehit)
								local decalE = GetFurthestEntity(fireDecals, fx,fy,fz)
								if decalE ~= nil then 					
									local newburnTimer = entityBurnTime / 2
									--material 13 = wood so we can burn it 
									if materialhit == 13 then 
										if ehit ~= nil then 					
											if newEntityBurnTime[ehit] == nil then 
												newEntityBurnTime[ehit] = {}
											end 
											for a = 0, 30 do 
												if newEntityBurnTime[ehit][a] == nil then 											
													newEntityBurnTime[ehit][a] = entityBurnTime	
													break 
												end 
											end 
										end
										newburnTimer = entityBurnTime
									end 
									local ox7,oy7,oz7 = U.Rotate3D(burnoffsetx,burnoffsety,burnoffsetz, rpax,rpay,rpaz)
									PlaceFireDecal(decalE, x3+ox7,y3+oy7,z3+oz7, newburnTimer, ehit)									
									--PromptDuration("placed fire decal "..decalE,1000)
								else 
									--PromptDuration("no fire decals found",1000)
								end 
							elseif currentArrowType == "normal" then 
								PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "normal", ehit)
							elseif currentArrowType == "water" then 
								PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "water", ehit)
								waterEmitter[arrowE] = WaterSplash(arrowE)
								if waterEmitter[arrowE] ~= false then 
									newWaterTime[arrowE] = waterEmitterLife
								end 
							end 
						end
					--we hit the terrain so we need to raycast where we hit 
					else 
						x3,y3,z3 = CheckForPointOnTerrain(rpax,rpay,rpaz)
						local ox6,oy6,oz6 = U.Rotate3D(0,0,-arrowLength, rpax,rpay,rpaz)
						fx,fy,fz = x3+ox6,y3+oy6,z3+oz6
						if currentArrowType == "fire" then 
							PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "fire")
							local decalE = GetFurthestEntity(fireDecals, fx,fy,fz)
							if decalE ~= nil then 
								local ox7,oy7,oz7 = U.Rotate3D(burnoffsetx,burnoffsety,burnoffsetz, rpax,rpay,rpaz)
								PlaceFireDecal(decalE, x3+ox7,y3+oy7,z3+oz7, entityBurnTime/2, nil)
								--PromptDuration("placed fire decal "..decalE,1000)
							else 
								--PromptDuration("no fire decals found",1000)
							end 
						elseif currentArrowType == "normal" then 
							PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "normal")
						elseif currentArrowType == "water" then 
							PlaceArrow(arrowE, fx,fy,fz, ax,ay,az, "water")
							waterEmitter[arrowE] = WaterSplash(arrowE)
							if waterEmitter[arrowE] ~= false then 
								newWaterTime[arrowE] = waterEmitterLife
							end 
						end 
					end 
				else 
					--PromptDuration("no arrows found",1000)
				end --arrowE
				materialhit = nil 
				checkHit = false 
			end --material hit 
		end --check hit
	end --state 
	
	--deal with burn damage and lifetime of fire effects
	newBurnDamageDelay = newBurnDamageDelay - frameTime
	if newBurnDamageDelay < 0 then 
		newBurnDamageDelay = burnDamageDelay
		for a,b in pairs (newEntityBurnTime) do  
			local dmg = 0
			for c = 0, 30 do 
				if newEntityBurnTime[a][c] ~= nil then 
					newEntityBurnTime[a][c] = newEntityBurnTime[a][c] - burnDamageDelay
					if newEntityBurnTime[a][c] < 0 then 
						if burningEntities[a] ~= nil then --is a character so we need to remove the particles
							for d = 0, 30 do 					
								if burningEntities[a][d] ~= nil then 
									ParticlesDeleteEmitter(burningEntities[a][d])			
									burningEntities[a][d] = nil 
								end 
							end 
							local allout = true 
							for d = 0, 30 do 
								if burningEntities[a][d] ~= nil then
									allout = false 
									break 
								end 
							end 
							if allout then --all the emitters are out
								burningEntities[a] = nil
							end 
						end 	
						newEntityBurnTime[a][c] = nil 
					else 
						dmg = dmg + 1
					end 
				end 
			end 
			local allout = true 
			for c = 0,30 do 
				if newEntityBurnTime[a][c] ~= nil then 
					allout = false 
					break 
				end 
			end 
			if allout then 
				newEntityBurnTime[a] = nil 
			end 
			SetEntityHealth(a,g_Entity[a]['health']-burnDamage*dmg)
			--PromptDuration("applied "..dmg.." ticks of damage",100)
		end
	end 
	
	--handles removing water particles from water arrows
	for a,b in pairs (newWaterTime) do 
		newWaterTime[a] = b - frameTime
		if b < 0 then 
			ParticlesDeleteEmitter(waterEmitter[a])
			waterEmitter[a] = nil 
			newWaterTime[a] = nil 
		end 
	end 
			
end 

function HandleHittingACharacter()
	if currentArrowType == "fire" then 
		local limbInd = 0
		local hitE = 0
		for a,b in pairs (ai_bot_state) do 							
			limbHit = g_Entity[a]['limbhit']		
			if limbHit ~= "" then 
				hitE = a 
				--PromptDuration("got char e = "..a,1000)
				break 
			end 
		end 	
		if hitE > 0 then 
			local hitObj = g_Entity[hitE]['obj']
			for a = 1, 30 do 
				if GetLimbName(hitObj, a) == limbHit then 
					limbInd = a 
					break 
				end 
			end 
			while limbInd == 0 do 
				local tl = math.random(1,30)
				if GetLimbName(hitObj, tl) ~= nil then 
					limbInd = tl 
				end 
				--TextCenterOnX(50,50,3,"got random limb")
			end 
			
			if burningEntities[hitE] == nil then 
				burningEntities[hitE] = {}
			end
			if newEntityBurnTime[hitE] == nil then 
				newEntityBurnTime[hitE] = {}
			end 
			local cf = nil 
			if burningEntities[hitE][limbInd] == nil then 
				cf = CatchFire(hitE, limbInd)
			else 
				cf = burningEntities[hitE][limbInd]
			end 
			burningEntities[hitE][limbInd] = cf
			for a = 0, 30 do 
				if newEntityBurnTime[hitE][a] == nil then 											
					newEntityBurnTime[hitE][a] = limbBurnTime
					break 
				end 
			end 
			ResetLimbHit(hitE)
		end 
	end 
end 

function CheckFiredRecently()
	if g_PlayerGunID == bowGunID then 
		bowAmmoIndex = GetFireModeSettingsPoolIndex()								
		if g_PlayerGunFired > 0 then 
			if not firedRecently then 					
				firedRecently = true 
				checkHit = true 
			end 
		else 
			firedRecently = false 
		end 
	end
end 

function CheckForPointOnTerrain(rpax,rpay,rpaz)
	if GetGamePlayerStatePlayerDucking() ~= 0 then 
		newyoffset = duckyoffset
	else 
		newyoffset = yoffset
	end				
	local px,py,pz = g_PlayerPosX,g_PlayerPosY,g_PlayerPosZ
	local ox2,oy2,oz2 = U.Rotate3D(xoffset,newyoffset,zoffset, rpax,rpay,rpaz)
	local x1,y1,z1 = px+ox2,py+oy2,pz+oz2
	local ox3,oy3,oz3 = U.Rotate3D(xoffset,newyoffset,zoffset+maxArrowRange, rpax,rpay,rpaz)
	local x2,y2,z2 = px+ox3, py+oy3, pz+oz3				
	local ray = RayTerrain(x1,y1,z1, x2,y2,z2)
	local x3,y3,z3
	if ray == nil then ray = 0 end 
	if ray > 0 then 
		x3 = GetRayCollisionX()
		y3 = GetRayCollisionY()
		z3 = GetRayCollisionZ()
	else 
		x3,y3,z3 = x2,y2,z2
	end 
	return x3,y3,z3
end 

function GetFurthestEntity(entityArray, x,y,z)
	local entityE = nil
	local dist1 = 0
	local x1,y1,z1 = x,y,z
	if x == nil then x1 = g_PlayerPosX end 
	if y == nil then y1 = g_PlayerPosY end 
	if z == nil then z1 = g_PlayerPosZ end 
	for a,b in pairs (entityArray) do 
		local x2,y2,z2 = GetEntityPosAng(a)				
		local dist2 = GetDistance(x1,y1,z1, x2,y2,z2)
		if dist2 > dist1 then  
			dist1 = dist2 
			entityE = a
		end 	
	end 
	return entityE
end 

function SetUp()
	arrows = isArrowE
	fireDecals = fireDecalE
	local fp = "scriptbank\\smallg\\bow and arrows\\images\\"
	normalarrowi = LoadImage(fp.."normalarrowIcon.png")
	firearrowi = LoadImage(fp.."firearrowIcon.png")
	waterarrowi = LoadImage(fp.."waterarrowIcon.png")
	arrowIconspr = CreateSprite(normalarrowi)
	SetSpritePosition(arrowIconspr,1000,1000)
	SetSpriteSize(arrowIconspr,arrowIconSizeX,arrowIconSizeY)
	currentArrowType = "normal"
	SetSpriteImage(arrowIconspr,normalarrowi)
end 

function GetKeyPressed(e, key, ignorecase)
	key = key or ""
	ignorecase = ignorecase or false 
	lastpressed = lastpressed or {}
	e = e or 0
	lastpressed[e] = lastpressed[e] or {}
	local inkey = g_InKey
	if ignorecase then 
		key = string.lower(key)
		inkey = string.lower(g_InKey)
	end
	local waspressed
	if inkey == key and lastpressed[e] ~= g_InKey then
		waspressed = g_InKey 
	else 
		waspressed = "false"
	end 
	lastpressed[e] = g_InKey
	return waspressed
end 
		


local emitter = {}
function CatchFire(t, l)
	emitter[t] = emitter[t] or {}
	if emitter[t][l] ~= nil then
		ParticlesDeleteEmitter(emitter[t][l])
	end 

	emitter[t][l] = ParticlesGetFreeEmitter()

	ParticlesAddEmitterEx( emitter[t][l], 1, 0,
					-- xpl ypl zpl xph yph zph
						0,  0,  0,  0,  0,  0,
					-- ssl ssh sel seh 
						 10,  15, 20, 30,
					-- xsl ysl zsl xsh ysh zsh
						-0.3,  0.5,  -0.3,  0.3,  0.7,  0.3,
					-- rotl roth lifl  lifh
						0,   0,   1000, 2000,
					-- asl ash ael aeh
						100, 100,  0,  0,
					-- frq  ent  limb   img frames
						150, t,   l,     2,   64 )
	return emitter[t][l]
end

local waterimg = ParticlesLoadImage( "effectbank\\particles\\water.dds" )
function WaterSplash(t)
	if emitter[ t ] == nil then

			emitter[ t ] = ParticlesGetFreeEmitter()
	
			ParticlesAddEmitterEx( emitter[ t ], 1, 0,
							-- xpl ypl zpl xph yph zph
								0,  0,  0,  0,  0,  0,
							-- ssl ssh sel seh 
								 10,  15, 1, 3,
							-- xsl ysl zsl xsh ysh zsh
								-0.3,  1.5,  -0.3,  0.3,  1.7,  0.3,
							-- rotl roth lifl  lifh
								0,   0,   1000, 2000,
							-- asl ash ael aeh
								100, 100,  0,  0,
							-- frq  ent  limb   img frames
								15, t,   0,     waterimg,   64 )
			ParticlesSetGravity( emitter[t], 0, 9 )
			return emitter[ t ]
		end
	return false
end

function GetDistance(tx1,ty1,tz1, tx2,ty2,tz2)
	local x3 = tx2-tx1 
	local y3 = ty2-ty1 
	local z3 = tz2-tz1 
	return x3*x3+y3*y3+z3*z3 
end