#include "script/common.lua"
--handler for weapon systems for armoured basilisco unit
multiMissileSprite = LoadSprite("MOD/alicanto/gfx/bluelensflare.png")
glare = LoadSprite("gfx/glare.png")

--projectile handlers
railGunShotHandler = {
	railShotNum = 1,
	railShots = {},
	defaultShot = {active = false}
} --credits to Cresta, a projectile handler for multiple projectiles in world

multiMissileHandler = {
	multiMissileNum = 1,
	multiMissiles = {},
	defaultMultiMissile = {active = false}
} --handler for multi-missiles

autoCannonShellHandler = {
	shellNum = 1,
	shells = {},
	defaultShell = {active = false}
} --for secondary autocannon

pulseLaserProjectiles = {
	pulseNum = 1,
	pulses = {}
} 

mortarProjectileHandler = {
	mortarNum = 1,
	mortarShots = {}
}--mortar projectile handler

poisonClouds = {}

heavyCannonShots = {} --wing mounted cannons

--init weapon bodies
function initRailGun()
	--sounds
	railGunFireSound = LoadSound("snd/puncher.ogg") --from jrjr3000/plasmaguy's punch rifle
	railGunSpoolUp = LoadSound("snd/basilisco_railgun_spoolup.ogg")
	
	--location for settings
	railGunSettings = FindLocation("railguntags")--seriously why is there even a character limit for tags
	--bodies
	railGun = FindBody("basiliscorailgun")
	railGunShroud = FindBody("railgunshroud")
	railGunBarrel = FindBody("railgunbarrel")
	
	--local transforms
	railGunLocalTransform = TransformToLocalTransform(headTransform,GetBodyTransform(railGun))
	railGunShroudLocalTransform = TransformToLocalTransform(headTransform,GetBodyTransform(railGunBarrel))
	railGunBarrelLocalTransform = TransformToLocalTransform(headTransform,GetBodyTransform(railGunBarrel))
	
	--extras
	shroudDeploy = 2.6 --how far the shroud should extend in voxels
	shroudDeployAmt = 0 --actual controller
	shroudExtended = false
	barrelDeploy = 2.6 --how far the barrel should extend if railgun is on high setting
	barrelDeployAmt = 0
	barrelExtended = false
	barrelY = 0.6 --the origin point of the body is at the railgun's mount base so the barrel's height is a bit lower
	
	--weapon parameters
	hasRailGun = railGunSettings ~= 0
	railGunMode = "off" -- "off", "charging", "fire"
	railShotPenetration = tonumber(GetTagValue(railGunSettings,"railpen")) or 7 --penetrative power of projectiles
	railShotVelocity = tonumber(GetTagValue(railGunSettings,"railvel")) or 225
	railGunTimer = 0 --timer for railGun fire delay
	railDelayTimer = 0
	railGunFireDelay = 0.13 --todo: rapidfire mode
	railGunRapidCounter = 0 --for rapidfire limit
	for i=1, railShotPenetration*6 do --increase to have more projectiles available
		railGunShotHandler.railShots[i] = deepcopy(railGunShotHandler.defaultShot)
	end
	railParticleTrailDecay = 6 --decay time for railgun trail
	railParticleRingDecay = 0.6 --decay time for railgun sonic shockwave rings
	railTrailScale = 0.5
end

function initAutoCannon()
	--sounds
	autoCannonSound = LoadSound("snd/excalibur-howitzer-shot.ogg")
	
	autoCannonSettings = FindLocation("autocannontags")
	autoCannonVelocity = tonumber(GetTagValue(autoCannonSettings, "autocannonvel")) or 260
	autoCannonFireDelay = tonumber(GetTagValue(autoCannonSettings, "autocannonfirerate")) or 0.13 --target fire delay
	autoCannonFireDelayOrig = autoCannonFireDelay --backup of original fire delay
	autoCannonTimer = 0 --actual timer for fire delay
	autoCannonCounter = 0
	autoCannonSpreadTimer = 0
	
	for i=1, 69 do
		autoCannonShellHandler.shells[i] = deepcopy(autoCannonShellHandler.defaultShell)
	end
	autoCannonLocalMuzzle = GetLocationTransform(FindLocation("autocannonmuzzle"))
	autoCannonLocalMuzzle = TransformToLocalTransform(headTransform,autoCannonLocalMuzzle)
end

function initLaser() --initializes laser weapon
	--sound
	laserSound = LoadLoop("snd/basilisco_laser.ogg")
	laserChargeSound = LoadSound("chopper-charge.ogg")
	
	--location
	laserSettings = FindLocation("lasertags")

	hasLaser = FindLocation("lasertags") ~= 0
	laserMode = "inactive" --"inactive" when not shooting, "charge" for windup, "fire" to fire
	laserTimer = 0 --for the laser windup
	laserDelay = tonumber(GetTagValue(laserSettings,"laserwindup")) or 1.15 --target windup time for laser
	laserDelayTimer = 0
	laserFireTimer = 0 --continuous laser firing time
	laserFireTime = tonumber(GetTagValue(laserSettings,"lasertime")) or 5 --how long laser to fire
	laserAxis = Transform() --axis to sway laser firing from
end

function initMultiMissiles() --initializes missile swarm
	--sound
	multiMissileAlarm = LoadSound("snd/basilisco_missile_warning.ogg")
	multiMissileLaunchSound = LoadSound("snd/basilisco_missile_launch.ogg")
	
	--location
	multiMissileSettings = FindLocation("multimissiletags")
	hasMultiMissiles = multiMissileSettings ~= 0

	multiMissileID = "multimissiles"
	multiMissileInitialVel = 6
	multiMissileMaxVel =  tonumber(GetTagValue(multiMissileSettings,"maxspeed")) or 500
	multiMissileFlightTime = tonumber(GetTagValue(multiMissileSettings,"flighttime")) or 3.4
	-- (above)maximum duration of active missiles to kill them in case they miss and hit nothing
	multiMissileLaunchDelay = 0.1 --timer for when to fire missiles
	multiMissileTargetRadius = tonumber(GetTagValue(multiMissileSettings, "targetradius")) or 13
	multiMissileCooldown = tonumber(GetTagValue(multiMissileSettings,"mcooldown")) or 10 --cooldown between salvoes
	multiMissileCooldownTimer = 0
	multiMissileLaunchTargetDelay = tonumber(GetTagValue(multiMissileSettings,"delay")) or 0.075 --intended delay for firing missiles
	multiMissileBlindFireThreshold = tonumber(GetTagValue(multiMissileSettings, "threshold")) or 1 
	--when attack counter is greater than this num, consider using multi-missiles
	multiMissileHearAttack = HasTag(multiMissileSettings, "hearattack")
	multiMissileVolleys = 0
	missilesReady = false
	for m=1, 60 do --mini missiles
		multiMissileHandler.multiMissiles[m] = deepcopy(multiMissileHandler.defaultMultiMissile)
	end
	multiMissileLaunchPorts = {--each key to the master table corresponds to the launcher unit 
							{--[key number] for the launcher coords, .unit for the whole launch unit
							{Vec(-2.1,1.3,-1.4),Vec(-2.3,1.3,-0.6), Vec(-2.4,1.3,-0), Vec(-2.6,1.3,1), dir = VecNormalize(Vec(0,3,-1)), activePort = 1}, 
							{Vec(2.1,1.3,-1.4),Vec(2.3,1.3,-0.6), Vec(2.4,1.3,-0.5), Vec(2.6,1.3,1), dir = VecNormalize(Vec(0,3,-1)), activePort = 1},
							{Vec(-2.1,-1.0,-1.4),Vec(-2.3,-1.0,-0.6), Vec(-2.4,-1.0,-0.5), Vec(-2.6,-1.0,1), dir = VecNormalize(Vec(0,-3,-1)), activePort = 1},
							{Vec(2.1,-1.0,-1.4),Vec(2.3,-1.0,-0.6), Vec(2.4,-1.0,-0), Vec(2.6,-1.0,1), dir = VecNormalize(Vec(0,-3,-1)), activePort = 1},
							unit = 2,
							unitType = "tail"}, --leaving the option open for applique missile units
							{--[key number] for the launcher coords, .unit for the whole launch unit
							{Vec(-2.1,1.3,-1.4),Vec(-2.3,1.3,-0.6), Vec(-2.4,1.3,-0), Vec(-2.6,1.3,1), dir = VecNormalize(Vec(-1.4,3,-0.5)), activePort = 1}, 
							{Vec(2.1,1.3,-1.4),Vec(2.3,1.3,-0.6), Vec(2.4,1.3,-0.5), Vec(2.6,1.3,1), dir = VecNormalize(Vec(1.4,3,-0.5)), activePort = 1},
							{Vec(-2.1,-1.0,-1.4),Vec(-2.3,-1.0,-0.6), Vec(-2.4,-1.0,-0.5), Vec(-2.6,-1.0,1), dir = VecNormalize(Vec(-0.4,-2,-3)), activePort = 1},
							{Vec(2.1,-1.0,-1.4),Vec(2.3,-1.0,-0.6), Vec(2.4,-1.0,-0), Vec(2.6,-1.0,1), dir = VecNormalize(Vec(0.4,-2,-3)), activePort = 1},
							unit = 6,
							unitType = "tail"}
							}
							--launch ports, a direction vector and active port for each
							--launch ports must be relative to the shape of the body in editor
end

function initRCWS() --not physically rotating turrets sadly
	--sound
	pulseShotSound = LoadSound("snd/pulseshot.ogg")
	
	local domestemp = FindShapes("rcwsdome")
	rcwsSettings = FindLocation("rcwstags")
	hasRCWS = FindLocation("rcwstags") ~= 0
	
	for i=1,500 do
		pulseLaserProjectiles.pulses[i] = { active = false }
	end
	
	pulseShotVelocity = 300
	pulseShotFireRate = 800
	rcwsBaseChance = tonumber(GetTagValue(rcwsSettings,"basechance")) or 0 --chance of the stations firing upon losing sight at the start
	rcwsThreshold = tonumber(GetTagValue(rcwsSettings,"threshold")) or 1 --how many main weapon attacks until considering subweapon
	rcwsCD = tonumber(GetTagValue(rcwsSettings,"cooldown")) or 4
	rcwsID = "rcws"
	
	rcws = {}
	for i,shape in ipairs(domestemp) do
		rcws[i] = {}
		rcws[i].shape = shape
		rcws[i].attachbody = GetShapeBody(shape) --the intended body of the shape, if it changed it broke
		rcws[i].localrot = Quat()
		rcws[i].dir = Vec()
		rcws[i].pulsecounter = 0
		rcws[i].pulsetimer = 0 --firing delay
		rcws[i].pulsecountrange = tagToVec(GetTagValue(rcws[i].shape,"burstcount")) or {5,30,0} --third value is unused
		rcws[i].damage = tonumber(GetTagValue(rcws[i].shape,"dmg")) or 1.5
		rcws[i].explosivepulse = HasTag(rcws[i].shape,"expulse") -- true or false, btw DO NOT USE FAST FIRE RATE WITH EXPLOSIVE
		rcws[i].firerate = tonumber(GetTagValue(rcws[i].shape,"rof")) or pulseShotFireRate
		rcws[i].firerate = 60/rcws[i].firerate
		rcws[i].spreadmod = 0
		rcws[i].color = tagToVec(GetTagValue(rcws[i].shape,"color")) or {1,1,1} --for impact lights 
		rcws[i].linecolor = tagToVec(GetTagValue(rcws[i].shape,"linecolor")) or {1,1,1} --for trails
		rcws[i].ready = false
		rcws[i].isattacking = false --whether or not it is attacking
		rcws[i].cooldowntimer = 0 --cooldown before station is ready to attack again
	end
end

function initMortar() --the mortar
	mortarSettings = FindLocation("mortartags")
	hasMortars = FindLocation("mortartags") ~= 0
	
	--sound
	smokeLauncherSound = LoadSound("snd/canister-shot.ogg")
	
	mortarID = "mortar"
	mortarBaseChance = tonumber(GetTagValue(mortarSettings,"basechance")) or 0
	mortarVelocity = 420
	mortarReload = 2.5
	mortarThreshold = tonumber(GetTagValue(mortarSettings,"threshold")) or 1 --how many main weapon attacks until considering subweapon
	
	for m=1,70 do
		mortarProjectileHandler.mortarShots[m] = {active = false} --screw deepcopy?
	end
	
	local mortarshapes = FindShapes("mortar")
	mortars = {}
	for m, shape in ipairs(mortarshapes) do
		mortars[m] = {}
		mortars[m].shape = shape
		mortars[m].attachbody = GetShapeBody(shape) --the intended body of the shape, if it changed it broke
		mortars[m].dmg = tonumber(GetTagValue(mortars[m].shape,"blastrad")) or 1.8
		mortars[m].linecolor = tagToVec(GetTagValue(mortars[m].shape,"linecolor")) or{1,1,1}
		mortars[m].color = tagToVec(GetTagValue(mortars[m].shape,"color")) or {1,1,1}
		mortars[m].rld = mortarReload
		mortars[m].timer = 0 --mortars are cheap and so is the lua for the timer
	end
end

function initRam() --ramming attack
	--sound
	ramStart = LoadSound("snd/basilisco_ram.ogg")
	
	ramSettings = FindLocation("ramtags")
	hasRam = true --cause it initialized
	recoverVec = Vec()
	
	ramDetonator = FindShape("ramdet") --probably mounted on a probe 
	if ramDetonator ~= 0 then
		ramDetLight = FindLight("ramdetlight")
	end
	ramExploded = false --only trigger once
	ramStarted = false
	rammed = false --the cue for initiating recovery phase is when true in "ram" flight mode
	rammedPlayer = false --whether player got hit already
	ramSmashed = false --if makehole already occurred
	ramFast = tonumber(GetTagValue(ramSettings,"ramfast")) or 150 --ramming speed
	ramSlow = tonumber(GetTagValue(ramSettings,"ramslow")) or 14 --recovery speed
end

function initNuke()--klaxons inbound (is it really a nuke tho?)
	--sound 
	klaxon = LoadLoop("snd/klaxon.ogg")
	
	--location
	nukeSettings = FindLocation("nuketags")

	nukeSpawnPoint = GetLocationTransform(FindLocation("nukespawnpoint")).pos
	nukeSpawnPoint = TransformToLocalPoint(GetBodyTransform(launcher),nukeSpawnPoint)
	
	--nuke settings
	launchTimer = 0
	launchCountdown = tonumber(GetTagValue(nukeSettings,"nukecountdown")) or 8
	nukeThreshold = tonumber(GetTagValue(nukeSettings,"threshold")) or 4 --consider nuke after these many failed attacks
	nukeAdded = false --nuke activation key is added to the special weapons list
	nukeSpeed = tonumber(GetTagValue(nukeSettings,"nukespeed")) or 500
	nukeStarted = false --initiate launch sequence
	nukeLaunched = false
	nukeExploded = false --only one active nuke at a time
	nukeDive = false --screw gravity
	nukeMode = "off"
	nukeGravity = tonumber(GetTagValue(nukeSettings,"gravity")) or 10
	nukeKilledPlayer = false  --kill you so hard you're not even safe in sandbox
	nukeKilledTimer = 0
	--nuke = {active = false} --table for nuke settings
	
end

function initPoison() --it's bluetides
	--sounds
	poisonAlarm = LoadLoop("snd/poison_alarm.ogg")
	
	--location
	poisonSettings = FindLocation("poisontags") --deadly caffeine, doubles as launch point
	hasPoison = true

	poisonLaunchPoint = GetLocationTransform(poisonSettings).pos
	poisonLaunchPoint = TransformToLocalPoint(GetBodyTransform(launcher), poisonLaunchPoint)
	poisonSequenceStarted = false
	poisonLaunched = false
	poisonDive = false
	poisonSet = false
	poisonMode = "off"
	
	--projectile operators
	poisonLaunchTimer = 0
	poisonLaunchTime = 4 --4 is bad number
	
	--poison clouds
	poisonDamage = 0.52 --how much of your health to chip away per second
	poisonBuff = 6.3 --movement buff cause it's bluetides
	poisonBuffTimer = 0
	poisonBuffTime = 1.1
	poisonBuffed = false
	poisonHurtTime = 0.3
	poisonHurtTimer = 0
	poisonHurt = false
	poisonRadius = 1 --radius of puff
	poisonTime = 11 --how long poison cloud lasts
end

function initHeavyCannons()
	--sounds
	heavyCannonFireSound = LoadSound("snd/hcannon_fire.ogg")
	heavyCannonChargeSound = LoadSound("snd/hcannon_charge.ogg")
	
	--location
	heavyCannonSettings = FindLocation("heavycannontags")
	hasHeavyCannons = true
	heavyCannonsArmed = false
	heavyCannons = FindShapes("hcannonpt") --spawn points for projectiles
	
	--cannon properties
	heavyCannonTimer = 0
	heavyCannonWindup = 4--time to charge
	heavyCannonFired = false
	heavyCannonChargeSoundTrigger = false
	heavyCannonBeginTick = false
	heavyCannonObserveTime = 7 --"cooldown" time
	heavyCannonProjectileColor = tagToVec(GetTagValue(heavyCannonSettings,"shotrgb")) or {0.1,0.5,1} --all cannons will use the same colors
	heavyCannonDotColor = tagToVec(GetTagValue(heavyCannonSettings,"dotrgb")) or {0.3,0.6,1}
end

function initStrafe() --strafing run settings
	strafeStartVec = Vec() --begin strafe about this heading
	--settings
	strafeSettings = FindLocation("strafetags")
	
	strafeBaseChance = tonumber(GetTagValue(strafeSettings,"basechance")) or 0.016
	strafeStart = false
	strafeWeaponStart = false
	strafeEnd = false
	strafeWhoosh = false
	strafeEndVec = Vec() --end strafe at this point
end

function initReconFlare()--flare or FLR
	--sound
	smokeLauncherSound = LoadSound("snd/canister-shot.ogg")
	lightBeep = LoadSound("snd/lightbeep.ogg")

	reconFlareSettings = FindLocation("reconflaretags")
	hasReconFlare = FindLocation("reconflaretags") ~= 0
	reconFlareStart = false --begin the sequence
	reconFlareDayMode = tonumber(GetTagValue(reconFlareSettings,"flrday")) or true --daylight alternative
	reconFlareChance = tonumber(GetTagValue(reconFlareSettings,"basechance")) or 0
	reconFlareHearChance = tonumber(GetTagValue(reconFlareSettings,"hearchance")) or 0.19 --on hearsound()
	reconFlareMaxChance = tonumber(GetTagValue(reconFlareSettings,"maxchance")) or 0.81
	
	reconFlareLaunched = false --launch flare
	reconFlareLocalLaunchPos = TransformToLocalPoint(headTransform,GetLocationTransform(reconFlareSettings).pos) --head launcher TODO: dedicated launching body
	reconFlareLaunchupSpeed = 100 --launch flare up at this speed
	reconFlareGravity = 55
	reconFlareSpeed = 0 --horizontal speed
	reconFlareTarget = Vec() --where the flare should be activating
	reconFlarePos = Vec() --where the flare is now
	reconFlareVel = Vec() --velocity vector
	
	reconFlareActive = false --flare is lit
	reconFlareSeePlayer = false
	reconFlareIntensityRef = tonumber(GetTagValue(reconFlareSettings,"flrglow")) or 2500
	reconFlareIntensity = 0
	reconFlareTime = tonumber(GetTagValue(reconFlareSettings,"flrtime")) or 8
	reconFlareTimer = 0 --the actual timer
	reconFlareEnd = false --flare is done and gone

end

function equipWeapons()--loads all initialized weapons
	if FindLocation("lasertags") ~= 0 then --enable laser
		initLaser() 
	else hasLaser = false
	end 
	
	if FindLocation("railguntags") ~= 0 then --mount railgun
		initRailGun() 
	else hasRailGun = false
	end
	
	if FindLocation("ramtags") ~= 0 then --ram attack
		initRam()
		table.insert(specialWeapons,"ramStarted")
	else hasRam = false
	end
	
	if FindLocation("nuketags") ~= 0 and launcher ~= 0 then --mount nuke
		initNuke() 
		-- table.insert(specialWeapons,"nukeStarted")
	end
	
	if FindLocation("poisontags") ~= 0 and launcher ~= 0 then --equip poison, aka bluetides
		initPoison()
		
		table.insert(specialWeapons,"poisonSequenceStarted")
	else hasPoison = false
	end
	
	if FindLocation("heavycannontags") ~= 0 then
		initHeavyCannons()
		table.insert(specialWeapons,"heavyCannonsArmed")
	else hasHeavyCannons = false
	end
	
	if FindLocation("autocannontags") ~= 0 then --mount railgun
		initAutoCannon() --secondary weapon for railGun
		hasAutoCannon = true
	else hasAutoCannon = false
	end
	
	if FindLocation("multimissiletags") ~= 0 then 
		initMultiMissiles()
	else hasMultiMissiles = false
	end
	
	if FindLocation("reconflaretags") ~= 0 then
		initReconFlare()
	else hasReconFlare = false
	end
	
	if FindLocation("rcwstags") ~= 0 then 
		initRCWS()
	else hasRCWS = false
	end
	
	if FindLocation("mortartags") ~=0 then
		initMortar()
	else hasMortars = false
	end
	
	if FindLocation("strafetags") ~=0 then
		initStrafe()
		canStrafe = true
	else canStrafe = false
	end
end

--weapon functions
function weaponSelection() --selects between LOS attacks to use after sighting player
	local selectedWeapon = "none"
	if #LOSweapons > 0 then selectedWeapon = LOSweapons[math.random(1,#LOSweapons)] end
	--DebugPrint(selectedWeapon)
	return selectedWeapon
end

function considerNLOS() --you may get attacked by all of them at once
	--subweapons for when sight is lost
	if FindLocation("multimissiletags") ~= 0 then
		if HasTag(multiMissileSettings,"active") then
			if not missilesReady and multiMissileCooldownTimer<= 0 then
				considerMultiMissile()
			end
		end
	end
	if FindLocation("rcwstags") ~= 0 then
		if HasTag(rcwsSettings,"active") then considerRCWS() end
	end
	if FindLocation("mortartags") ~= 0 then
		if HasTag(mortarSettings,"active") then considerMortar() end
	end
end

function considerNLOSOnHear() --for use with hearing function
	if math.random() < hearAttackChance then 
		considerNLOS() 
		targetPosShiftDown() --prevent shooting self if explosion next to it
		return true 
	end
	return false
end

--LASER
function laser()
	if FindLocation("lasertags") == 0 then return end --safeguard for calling laser without the laser tag
	
	local s = GetBodyTransform(searchLight) --location of the searchlight body in world space
	local t = TransformToParentVec(s,Vec(0,0,-1)) --forward vector of the searchlight body to, for raycast purposes
	local maxDist = 300
	
	local playerCheck = VecSub(GetPlayerPos(),s.pos) --vector between playerPos and searchLight
	local playerDist = VecLength(playerCheck) --as a simple distance number
	local playerCheck = TransformToParentPoint(s, Vec(0,0,-clamp(VecLength(playerCheck),0,maxDist)))--playerCheck is now the distance to player if forward of s
	local hitPoint = TransformToParentPoint(s, Vec(0,0,-1))
	
	--prepare for raycast
	rejectSelf()

	laserHit, hitDist = QueryRaycast(s.pos,t, maxDist,0, false) --raycast in general
	if laserHit then
		hitPoint = TransformToParentPoint(s,Vec(0,0,-hitDist))
		MakeHole(hitPoint, 1.75,1.2,0.75,false)
		--SpawnParticle("smoke",hitPoint,Vec(0,0.2,0), 2.1, 0.5)
	else
		hitPoint = TransformToParentPoint(s,Vec(0,0,-maxDist))
	end
	--firestarting
	if math.random()<0.15 then
		fireVec = Vec(rndFloat(-1.1,1.1),rndFloat(-1.1,1.1),rndFloat(-1.1,1.1))
		SpawnFire(VecAdd(hitPoint,fireVec))
	end
	
	local radi=Vec(1.8,1.8,1.8)
	local bodies = QueryAabbBodies(VecSub(hitPoint,radi),VecAdd(hitPoint,radi))
	--loop through bodies
	for i,body in ipairs(bodies) do
		--compute body center and dist
		local bmi,bma = GetBodyBounds(body)
		local bc = VecLerp(bmi,bma,0.5) --get center
		local dir = VecSub(bc, hitPoint)
		local dist = VecLength(dir)
		dir = VecScale(dir,1/dist)
		local mass = GetBodyMass(body)--get mass
		
		--dir forward
		dir = VecLerp(dir,TransformToParentVec(s,Vec(0,0,-100)),0.8)
		dir = VecNormalize(dir)
		
		local distScale = 1 - math.min(dist/(1.8),1)^(1/3)
		local force = math.min(16*mass,10000)*distScale
		local add = VecScale(dir,force)
			
		ApplyBodyImpulse(body,hitPoint,add)
	end

	local playerdist,nearestpt = getDistanceToLineSegment(GetPlayerPos(),headTransform.pos,hitPoint)
	if playerdist < 1.7 and not playerDead() then
		SetPlayerHealth(GetPlayerHealth()-0.13)
		PlaySound(LoadSound("light/spark0.ogg"))
	end
	
	PlayLoop(laserSound,s.pos,14, false)
	--DrawLine(s.pos,hitPoint,1,0.1,0.1)
	
	ParticleReset() --ready up the particle system
	local laserAimDist = VecLength(VecSub(hitPoint,s.pos))
	local partivel = 400+math.random()*150
	local timeneeded = laserAimDist/partivel
	ParticleType("plain")
	ParticleTile(5) --atlas is hard
	ParticleColor(1,0.0,0.0)
	ParticleAlpha(1,0.06,"easein")
	ParticleRadius(0.44) --also can add end radius, and fade ratios at start and end
	ParticleGravity(0)
	ParticleDrag(0.0)
	ParticleEmissive(2,1,"easein",0.92)
	ParticleStretch(1)
	ParticleSticky(0.1)
	ParticleCollide(0)
	SpawnParticle(s.pos,TransformToParentVec(s,Vec(0,0,-partivel)), timeneeded)
	partivel = 400+math.random()*150
	timeneeded = laserAimDist/partivel
	SpawnParticle(s.pos,TransformToParentVec(s,Vec(0,0,-partivel)), timeneeded)
	partivel = 500+math.random()*150
	timeneeded = laserAimDist/partivel
	ParticleColor(1,0.33,0.33)
	ParticleRadius(0.5)
	SpawnParticle(s.pos,TransformToParentVec(s,Vec(0,0,-partivel)), timeneeded)
	--solid beam particles
	ParticleTile(4)
	for i=0, laserAimDist, 0.05 do
		local pt = TransformToParentPoint(s,Vec(0,0,-i))
		ParticleAlpha(0.25)
		ParticleRadius(0.2)
		ParticleColor(1,0,0)
		ParticleEmissive(3)
		SpawnParticle(pt,Vec(),GetTimeStep()*2)
		ParticleTile(0)
		ParticleColor(1,1,1)
		ParticleAlpha(1)
		ParticleRadius(0.1)
		ParticleEmissive(6)
		SpawnParticle(pt,Vec(),GetTimeStep()*2)
	end

	if laserHit then
		ParticleTile(0)
		ParticleRadius(0.4,rndFloat(0.6,1))
		ParticleAlpha(1,0,"easeout")
		ParticleColor(1,0.2,0.2,0.07,0.06,0.06)
		ParticleEmissive(3,0.2,"easeout",0,0.3)
		ParticleCollide(1)
		ParticleGravity(-6)
		ParticleDrag(0.2)
		ParticleSticky(0.1)
		SpawnParticle(hitPoint,Vec(rndFloat(-13,13),0.2,rndFloat(-13,13)), 0.3)
		ParticleRadius(0.6,rndFloat(1,1.6))
		ParticleColor(0.8,0.14,0.14,0.1,0.1,0.1)
		ParticleEmissive(0.6,0.1, "easeout")
		SpawnParticle(hitPoint,Vec(rndFloat(-22,22),1,rndFloat(-22,22)), 0.3)
		
		ParticleTile(4)
		ParticleRadius(0.07)
		ParticleColor(1,0,0,0.1,0.01,0.01)
		ParticleStretch(0)
		ParticleEmissive(0.4,0)
		ParticleSticky(0.6)
		SpawnParticle(hitPoint,Vec(math.random(-30,30),3,math.random(-30,30)), 0.5)
		SpawnParticle(hitPoint,Vec(math.random(-30,30),3,math.random(-30,30)), 0.5)
	end
end

--RAILGUN
function railGunFire(inaccurize,strafe)
	if FindLocation("railguntags") == 0 then return end --safeguard against calling this without the railgun tag 

	local pen = railShotPenetration
	local origin = GetBodyTransform(railGunBarrel)
	origin.pos = VecAdd(origin.pos, TransformToParentVec(origin,Vec(0,-barrelY,-3.55)))
	local dir = TransformToParentVec(origin,Vec(0,0,-1))
	local targetDist = VecLength(VecSub(targetPos,origin.pos))
	
	if inaccurize~=nil then
		spreadx = rndFloat(-0.01,0.01)
		spready = rndFloat(-0.01,0.01)
		dir = VecAdd(dir,TransformToParentVec(origin,Vec(spreadx,spready,0)))
		dir = VecNormalize(dir)
	end
	
	for i=1, pen do
		railGunShotHandler.railShots[railGunShotHandler.railShotNum] = deepcopy(railGunShotHandler.defaultShot)
		loadedShot = railGunShotHandler.railShots[railGunShotHandler.railShotNum]
		loadedShot.active = true
		loadedShot.pen = pen
		loadedShot.pos = origin.pos
		loadedShot.vel = VecScale(dir,railShotVelocity)
		loadedShot.index = i --whether it is the last penetration of a shot or not
		loadedShot.targetTime = railParticleTrailDecay --timersfor the particle trail
		loadedShot.trailTime = 3
		loadedShot.travelDist = 0 
		loadedShot.targetDist = targetDist + 3 --sets shot to detonate near target if it misses or penetrates floor 
		if strafe ~= nil then loadedShot.targetDist = targetDist*1.66 end
		railGunShotHandler.railShotNum = (railGunShotHandler.railShotNum%#railGunShotHandler.railShots)+1
	end
	PlaySound(railGunFireSound, origin.pos, 69) --what is loud?
	DrawSprite(multiMissileSprite,origin,23,25,1,0.2,0.16,1,true, true)
	--recoil 
	barrelDeployAmt = 2
	PointLight(origin.pos,1,0.5,0.37, 220)
end

function railShotDippinDots() --cordium
	ParticleTile(4)
	ParticleRadius(rndFloat(0.03,0.07))
	ParticleStretch(0)	
	ParticleEmissive(2,0.1,"smooth")
	ParticleGravity(rndFloat(0.27,-1.6))
	ParticleAlpha(1,0.89,"easein",0,0.4)
	ParticleDrag(0,0.33)
	ParticleSticky(0,1)
	ParticleCollide(1,1,"constant",0.02)
end
function railShotOperation(shot)
	local ahead = VecAdd(shot.pos, VecScale(shot.vel,GetTimeStep()))
	local dir = VecNormalize(VecSub(ahead,shot.pos))
	rejectSelf() --don't shoot yourself
	QueryRequire("physical") --ignore unphysical
	local hit, dist = QueryRaycast(shot.pos, dir, VecLength(VecSub(ahead,shot.pos)))
			ParticleReset() --trail
			ParticleType("plain")
			ParticleTile(6)
			ParticleColor(railtrailR,railtrailG,railtrailB)
			ParticleGravity(0)
			ParticleSticky(0)
			ParticleRadius(0.17, 0.02,"smooth")
			ParticleEmissive(2,0.3, "easeout")
			ParticleCollide(0)
	if hit then
		local hitPos = VecAdd(shot.pos,VecScale(dir, dist))
		if shot.index == shot.pen then
			for i = 0, dist, 0.06 do --trail for impact moment
				SpawnParticle(VecAdd(shot.pos,VecScale(dir,i)),Vec(),shot.trailTime+i*0.06*GetTimeStep()*(shot.targetTime-shot.trailTime+1))
			end
			shot.trailTime = math.min(shot.trailTime+GetTimeStep()*(shot.targetTime-shot.trailTime)*1.3, shot.targetTime)
		end
		shot.active = false
		
		railShotDippinDots()
		for i=1, 35 do
			SpawnParticle(hitPos,Vec(rndFloat(-5,5),rndFloat(2,7),rndFloat(-5,5)),rndFloat(1,20))
		end
		if shot.index < shot.pen then
			--SpawnParticle("smoke", hitPos, Vec(0, 1.0+math.random(1,10)*0.1, 0), 2, 1)
			MakeHole(hitPos, 3, 2.6, 2.4)
		else
			Explosion(hitPos, 4)
		end
	else
		if shot.index == shot.pen then
			local t = Transform(ahead, QuatLookAt(shot.pos,ahead))
			for i = 0, VecLength(VecScale(shot.vel,GetTimeStep())), 0.06 do
				SpawnParticle(VecAdd(shot.pos,VecScale(dir,i)),Vec(),shot.trailTime+i*0.06*GetTimeStep()*(shot.targetTime-shot.trailTime+1))
			end
			shot.trailTime = math.min(shot.trailTime+GetTimeStep()*(shot.targetTime-shot.trailTime)*1.3, shot.targetTime)
			ParticleTile(4)
			ParticleRadius(0.089)
			ParticleColor(shockringR,shockringG,shockringB)
			ParticleDrag(0.57)
			ParticleEmissive(2,0.27)
			ParticleAlpha(0.6,0,"linear")
			for r = 0, math.pi*2,0.04 do--shock rings
				local dir = VecNormalize(TransformToParentVec(t,Vec(math.cos(r),math.sin(r),0)))
				SpawnParticle(ahead,VecScale(dir,3),shot.trailTime*0.38)
			end
		end
		--DrawLine(shot.pos, ahead, 1, 0.5, 0.4)
		shot.pos = ahead
		shot.travelDist = shot.travelDist+VecLength(shot.vel)*GetTimeStep()
		if shot.travelDist > shot.targetDist then --kill shot at max range
			shot.active = false
			if shot.index == shot.pen then --the one that's supposed to explode
				Explosion(ahead, 4) --self destruction basically
				railShotDippinDots()
				for i=1,26 do
					SpawnParticle(hitPos,Vec(rndFloat(-5,5),rndFloat(2,7),rndFloat(-5,5)),rndFloat(0.5,10))
				end
			end
		end	
		PointLight(ahead, 1, 0.5, 0.35, 4)
	end
end

function extendRailBarrel()
	if FindLocation("railguntags") ~= 0 then
		barrelExtended = true
		shroudExtended = true
	end
end
function retractRailBarrel()
	if FindLocation("railguntags") ~= 0 then
		barrelExtended = false
		shroudExtended = false
	end
end

--MISSILE SWARM
function multiMissileLaunch() --DRIVE, DRIVE
	if FindLocation("multimissiletags") == 0 then return end
	for o = 1, #multiMissileLaunchPorts do
		local origin = nil
		local body = nil 
		if multiMissileLaunchPorts[o].unitType == "tail" then
			 origin = GetBodyTransform(tailTable[multiMissileLaunchPorts[o].unit].body)
			 body = tailTable[multiMissileLaunchPorts[o].unit].body
		end
		for i=1, #multiMissileLaunchPorts[o] do
			
			multiMissileHandler.multiMissiles[multiMissileHandler.multiMissileNum] = deepcopy(multiMissileHandler.defaultMultiMissile)
			activeMissile = multiMissileHandler.multiMissiles[multiMissileHandler.multiMissileNum]
			activeMissile.active = true
			activeMissile.pos = TransformToParentPoint(origin, multiMissileLaunchPorts[o][i][multiMissileLaunchPorts[o][i].activePort])
			local launchDir =  TransformToParentVec(origin, multiMissileLaunchPorts[o][i].dir)
			activeMissile.rot = QuatLookAt(activeMissile.pos,VecAdd(activeMissile.pos,launchDir)) --points the launched missile the right way
			--activeMissile.transform = Transform(activeMissile.pos,activeMissile.rot)
			activeMissile.transform = {pos = activeMissile.pos, rot = activeMissile.rot}
			activeMissile.slerprate = 0.0 --rapidly increasing homing
			activeMissile.vel = multiMissileInitialVel
			local targetx = rndFloat(-multiMissileTargetRadius,multiMissileTargetRadius)
			local targety = rndFloat(-multiMissileTargetRadius,multiMissileTargetRadius)
			activeMissile.target = VecAdd(lookPos,Vec(targetx,0.1,targety))--missile destination as an area weapon
			local hit, dist = QueryRaycast(activeMissile.target,Vec(0,-1,0),2)--grounding so the missiles don't spin in thin air
			if hit then
				activeMissile.target[2] = activeMissile.target[2]-dist
			else
				activeMissile.target[2] = activeMissile.target[2]-2
			end
			activeMissile.time = 0 --timer to limit missile lifespan
			PlaySound(multiMissileLaunchSound,activeMissile.pos,29,false)
			multiMissileLaunchPorts[o][i].activePort = multiMissileLaunchPorts[o][i].activePort%(#multiMissileLaunchPorts[o][i])+1
			multiMissileHandler.multiMissileNum = (multiMissileHandler.multiMissileNum%#multiMissileHandler.multiMissiles)+1
		end
	end
end

function considerMultiMissile()
	if math.random() < 0.29 and attackCounter >=multiMissileBlindFireThreshold then
		multiMissileVolleys = 1 + math.random(4)
	else
		multiMissileVolleys = 0
	end--]]
end

function multiMissileOperation(nail) --flight handler for multiMissiles 
	local dir = TransformToParentVec(nail.transform,Vec(0,0,-1))
	local toTarget = QuatLookAt (nail.pos,nail.target)
	local ahead = TransformToParentVec(nail.transform,Vec(0,0,-nail.vel*GetTimeStep()))
	local previousT = Transform(nail.pos,QuatLookAt(nail.pos,VecAdd(nail.pos,ahead))) --for use with the particle system
	rejectSelf() --don't shoot yourself
	QueryRequire("physical")
	local hit, dist = QueryRaycast (nail.pos,dir,nail.vel*GetTimeStep())
	if hit then
		local hitPos = VecAdd(nail.pos,VecScale(dir,dist))
		nail.active = false
		Explosion(hitPos,1.5)
	else
		nail.transform.rot = QuatSlerp(nail.transform.rot,toTarget,nail.slerprate)
		nail.slerprate = clamp(nail.slerprate + GetTimeStep()/9,0,1) --increase missile tracking rate
		nail.vel = clamp(nail.vel+1.4,nail.vel,multiMissileMaxVel) --missile speed control
		nail.pos = VecAdd(nail.pos,ahead)
		--DrawLine(nail.pos,VecAdd(nail.pos,ahead))
		PointLight(nail.pos, 1, 0.65, 0.4, 16)
		for m = 0.0, VecLength(ahead), 0.22 do
		SpawnParticle("smoke",VecAdd(nail.pos,TransformToParentVec(previousT,Vec(0,0,m))),Vec(0,0,0),0.35,0.9-GetTimeStep()*VecLength(ahead)*0.22*m) --last part should be same value as for loop increment
		end
		DrawSprite(multiMissileSprite,Transform(nail.pos,GetCameraTransform().rot),9,13,1,0.5,0.4,1,true, true)
		nail.time = nail.time+GetTimeStep()
	end
	if nail.time > multiMissileFlightTime then
		nail.active = false
		Explosion(nail.pos,1)
	end
end

--RCWS
function rcwsPulse(node) --burst of laser
	if FindLocation("rcwstags") == 0 then return end
	if not weaponShapeCondition(node) or not node.isattacking then --if it broke or inactive then don't operate
	else
		local mi, ma = GetShapeBounds(node.shape)
		local origin = VecLerp(mi,ma,0.5)--magica doesn't center voxes
		local dir = node.dir
		local aimtrans = Transform(origin,QuatLookAt(Vec(),dir))
		local dist = VecLength(dir)
		
		local spread = (math.min(node.spreadmod,6)+3)*dist/100
		spread = math.min(spread,7)
		spreadx = (math.random()-0.5)*spread
		spready = (math.random()-0.5)*spread
		dirspread = TransformToParentVec(aimtrans,Vec(spreadx,spready,0))
		dir = VecNormalize(VecAdd(dir,dirspread))
		
		local shot = {}
		shot.pos = origin
		shot.vel = VecScale(dir,pulseShotVelocity)
		shot.time = 0 
		shot.maxtime = 2
		shot.active = true
		shot.linecolor = node.linecolor
		shot.impactcolor=node.color
		shot.dmg = node.damage
		
		pulseLaserProjectiles.pulses[pulseLaserProjectiles.pulseNum] = shot
		pulseLaserProjectiles.pulseNum = pulseLaserProjectiles.pulseNum%#pulseLaserProjectiles.pulses + 1
		
		node.pulsetimer = node.firerate
		node.spreadmod = node.spreadmod + 0.6
		PlaySound(pulseShotSound,shot.pos,11,false)
	end
end

function rcwsPulseOperation(pulse)
	local ahead = VecAdd(pulse.pos,VecScale(pulse.vel,GetTimeStep()))
	local dir = VecNormalize(VecSub(ahead,pulse.pos))
	local r,g,b = pulse.impactcolor[1],pulse.impactcolor[2],pulse.impactcolor[3]
	local lr,lg,lb = pulse.linecolor[1],pulse.linecolor[2],pulse.linecolor[3]
	
	rejectSelf()
	QueryRequire("physical")
	
	local hit, dist = QueryRaycast(pulse.pos,dir,VecLength(VecSub(ahead,pulse.pos)))
	
	--trail particle
	ParticleReset()
	ParticleTile(3)
	ParticleColor(lr,lg,lb)
	ParticleGravity(0)
	ParticleSticky(0)
	ParticleRadius(0.2, 0.15,"smooth")
	ParticleEmissive(2,0.55, "easeout")
	ParticleCollide(0)
	ParticleStretch(1)
	
	if hit then 
		local hitPos = VecAdd(pulse.pos,VecScale(dir,dist))
		pulse.active = false
		PointLight(hitPos,r,g,b,10)
		ahead=hitPos
		local dmg = pulse.dmg
		MakeHole(hitPos,dmg,dmg*.9,0.1,true)
		
		--particles
		railShotDippinDots()
		for i=1, 20 do
			SpawnParticle(hitPos,Vec(rndFloat(-6,6),rndFloat(0,7),rndFloat(-6,6)),rndFloat(0.05,0.5))
		end
		
		--impact physics
		local bbox = dmg*1.25
		local mi = VecAdd(hitPos,Vec(-bbox,-bbox,-bbox))
		local ma = VecAdd(hitPos,Vec(bbox,bbox,bbox))
		QueryRequire("physical")
		local bodies = QueryAabbBodies(mi,ma)
		for i,body in ipairs(bodies) do
			--compute body center and dist
			local bmi,bma = GetBodyBounds(body)
			local bc = VecLerp(bmi,bma,0.5) --get center
			local dir = VecSub(bc, hitPos)
			local dist = VecLength(dir)
			dir = VecScale(dir,1/dist)
			local mass = GetBodyMass(body)--get mass
			
			--dir forward
			dir = VecLerp(dir,pulse.vel,0.75)
			dir = VecNormalize(dir)
			
			local massScale = 1 - math.min(mass/(60),1)
			--local distScale = 1 - math.min(dist/dmg,1)
			local df = math.min(dist/dmg,1)
			local distScale = 1 - df*(2-df)
			--local force = 1333*distScale
			local force = math.min(75*mass,9000)*distScale
			--local force = (155*mass*massScale*distScale)
			local add = VecScale(dir,force)
			
			ApplyBodyImpulse(body,bc,add)
		end
	else
		PointLight(ahead,r,g,b,6) 
	end
	
	--player impact
	local playerdist,nearestpt = getDistanceToLineSegment(GetPlayerPos(),pulse.pos,ahead)
	if playerdist < 0.9 then
		SetPlayerHealth(GetPlayerHealth()-0.4) --inaccuracy hurts
		pulse.active = false
	end
	
	
	DrawLine(pulse.pos,ahead,lr,lg,lb,0.45)
	for i=0, VecLength(VecSub(ahead,pulse.pos)),0.15 do 
		SpawnParticle(VecAdd(ahead,VecScale(dir,-i)),Vec(),0.06-i*0.15*GetTimeStep())
	end
	
	pulse.pos = ahead
	pulse.time = pulse.time + GetTimeStep()
	if pulse.time > pulse.maxtime then
		pulse.active = false
	end
end

function rcwsLine()
	for r,node in ipairs(rcws) do
		if not weaponShapeCondition(node) then --if it broke then don't operate
		else
			local mi, ma = GetShapeBounds(node.shape)
			local origin = VecLerp(mi,ma,0.5)--magica doesn't center voxes
			local shapeTrans = GetShapeWorldTransform(node.shape) --get transform of shape
			shapeTrans.pos = origin 
			
			local relativeTargetPos = TransformToLocalPoint(shapeTrans, targetPos)
			local relativeTargetDist = VecLength(relativeTargetPos) --distance to target
			relativeDir = VecNormalize(relativeTargetPos)
			local impactpoint = VecScale(relativeDir,relativeTargetDist)
			impactpoint = TransformToParentPoint(shapeTrans,impactpoint)
			
			--[[if relativeDir[2] < 0.2 then 
				DrawLine(origin,impactpoint,1,1,1,0.9)
			end--]]
			--DebugPrint(VecNormalize(relativeTargetPos)[2])
			--DebugPrint(VecNormalize(Vec(5,1,0))[2])
		end
	end
end

function considerRCWS()
	if not FindLocation("rcwstags") then return end
	local probability = rcwsBaseChance
	
	if attackCounter >= rcwsThreshold then
		probability = 0.4
		if probability < rcwsBaseChance then probability = rcwsBaseChance end
	end

	for r, node in ipairs(rcws) do
		if weaponShapeCondition(node) then
			node.ready = false
			if math.random() < probability then
				if node.cooldowntimer <= 0 and not node.ready then
					node.ready = true --ready
				end
			end
		end
	end
end

--MORTAR
function mortarLaunch(node)
	if FindLocation("mortartags") == 0 then return end
	if not weaponShapeCondition(node) then 
	elseif node.timer <= 0 then
		local mi, ma = GetShapeBounds(node.shape)
		local origin = VecLerp(mi,ma,0.5)--magica doesn't center voxes
		local dir = Vec(0,1,0) --aim straight up
		
		mortarProjectileHandler.mortarShots[mortarProjectileHandler.mortarNum] = {active = false}
		local loadedShell = mortarProjectileHandler.mortarShots[mortarProjectileHandler.mortarNum]
		loadedShell.pos = origin
		loadedShell.launchpos = VecCopy(origin)
		loadedShell.active = true
		loadedShell.vel = VecScale(dir,mortarVelocity)
		loadedShell.dmg = node.dmg
		loadedShell.linecolor = node.linecolor
		loadedShell.color = node.color
		
		mortarProjectileHandler.mortarNum = (mortarProjectileHandler.mortarNum%#mortarProjectileHandler.mortarShots) + 1
		
		PlaySound(smokeLauncherSound,origin,40,false)
	end
end

function mortarProjectileOperation(shell)
	
	local r,g,b = shell.color[1],shell.color[2],shell.color[3]
	local lr,lg,lb = shell.linecolor[1],shell.linecolor[2],shell.linecolor[3]
	--trail particle
	ParticleReset()
	ParticleTile(5)
	ParticleColor(lr,lg,lb)
	ParticleGravity(0)
	ParticleSticky(0)
	ParticleRadius(0.35, 0.17,"smooth")
	ParticleEmissive(2,0.55, "easeout")
	ParticleCollide(0)
	ParticleStretch(1)
	
	local ahead = VecAdd(shell.pos,VecScale(shell.vel,GetTimeStep()))
	local dir = VecNormalize(shell.vel)
	local axis = Transform(shell.pos,QuatLookAt(shell.pos,ahead))
	
	if shell.pos[2] >= shell.launchpos[2] + 750 then
		 --axis.rot = QuatLookAt(shell.pos,shell.target) --unguided
		 local rand = Vec(rndFloat(-10,10),0,rndFloat(-10,10))
		 local target = VecAdd(targetPos,rand)
		 axis.rot = QuatLookAt(shell.pos,target) --last minute apogee course adjustment
		 dir = TransformToParentVec(axis,Vec(0,0,-1))
		 local newSpeed = VecLength(shell.vel)*rndFloat(1.15,1.35)
		 shell.vel = VecScale(dir,newSpeed)
	end
	
	QueryRequire("physical")
	rejectSelf()
	local hit, dist = QueryRaycast(shell.pos,dir,VecLength(VecSub(ahead,shell.pos)))
	if hit then
		local hitPos = VecAdd(shell.pos,VecScale(dir,dist))
		shell.active = false
		Explosion(hitPos,shell.dmg)
		--PointLight(hitPos,r,g,b,50)
		--particles
		railShotDippinDots()
		for i=1, 25 do
			SpawnParticle(hitPos,Vec(rndFloat(-4,4),rndFloat(0,4),rndFloat(-4,4)),rndFloat(0.4,5))
		end
	else
		--DrawLine(shell.pos,ahead,lr,lg,lb)
		PointLight(ahead,r,g,b,6)
		for i=0, VecLength(VecSub(ahead,shell.pos)),0.2 do 
			SpawnParticle(VecAdd(ahead,VecScale(dir,-i)),Vec(),0.2-i*0.2*GetTimeStep())
		end
	end
	shell.pos = ahead
	
	if shell.pos[2] < -35 then --under the map
		shell.active = false
	end
end

function considerMortar()
	local probability = 0.4
	if mortarBaseChance > probability then probability = mortarBaseChance end
	
	for m, node in ipairs(mortars) do
		if weaponShapeCondition(node) then
			if math.random() < probability and attackCounter >= mortarThreshold or math.random() < mortarBaseChance then
				node.timer = node.rld+math.random()*2
			elseif math.random() then
				node.timer = 0
			end
		end
	end
end

function considerMortarReload(node)
	local probability = 0.15
	if weaponShapeCondition(node) then
		if math.random() < probability then
			node.timer = node.rld+math.random()*2
		else
			node.timer = 0
		end
	end
end

function weaponShapeCondition(node)
	if IsShapeBroken(node.shape) or not IsHandleValid(node.shape) or GetShapeBody(node.shape) ~= node.attachbody then
		return false
	end

	return true
end
--COAXIAL AUTOCANNON
function autoCannonFire()
	if FindLocation("autocannontags") == 0 then return end
	
	local origin = TransformToParentTransform(headTransform,autoCannonLocalMuzzle) --where the shell should fire from
	local railGunAlignTrans = TransformCopy(headTransform)
	railGunAlignTrans.pos = TransformToParentPoint(headTransform,Vec(0,-1.5,0)) --shift aim point down to railGun level
	local dir = TransformToParentVec(railGunAlignTrans,Vec(0,0,-1)) --"aim" dir of head, align with railgun
	local fwd = TransformToParentPoint(railGunAlignTrans,Vec(0,0,-365)) --if no raycast hit, set aimpoint here
	local dist = 365
	
	rejectSelf()--prepare for raycast
	local hit,hitDist = QueryRaycast(railGunAlignTrans.pos,dir,dist)
	if hit then
		fwd = TransformToParentPoint(railGunAlignTrans,Vec(0,0,-hitDist))
		dist = hitDist
	end
	
	local aimDir = VecSub(fwd,origin.pos)
	local aimDirTrans = Transform(origin.pos,QuatLookAt(origin.pos,aimDir)) --axis to work spread on
	local spreadamount = autoCannonSpreadTimer*dist/100
	local spreadx = rndFloat(-spreadamount,spreadamount)
	local spready = rndFloat(-spreadamount,spreadamount)
	local spread = TransformToParentVec(aimDirTrans,Vec(spreadx,spready,0))
	aimDir = VecAdd(aimDir, spread)
	autoCannonShellHandler.shells[autoCannonShellHandler.shellNum] = deepcopy(autoCannonShellHandler.defaultShell)
	loadedShell = autoCannonShellHandler.shells[autoCannonShellHandler.shellNum]
	loadedShell.active = true
	loadedShell.pos = origin.pos
	loadedShell.vel = VecScale(VecNormalize(aimDir),autoCannonVelocity)
	loadedShell.maxtime = 7
	loadedShell.time = 0
	autoCannonShellHandler.shellNum = (autoCannonShellHandler.shellNum%#autoCannonShellHandler.shells)+1
	
	PointLight(origin.pos, 1, 0.5, 0.3, 6)
	PlaySound(autoCannonSound, origin.pos, 20)
	DrawSprite(glare,origin, 20, 20,1,0.5,0.35,1,true,true)
	autoCannonSpreadTimer = autoCannonSpreadTimer + 0.07
end

function autoCannonShellOperation(shell)
	local ahead = VecAdd(shell.pos,VecScale(shell.vel,GetTimeStep()))
	local dir = VecNormalize(VecSub(ahead,shell.pos))
	local length = VecLength(VecSub(ahead,shell.pos))
	local particlet = Transform(shell.pos,QuatLookAt(shell.pos,ahead))
	QueryRequire("physical")
	rejectSelf() --don't shoot yourself
	local hit,dist = QueryRaycast(shell.pos,dir,VecLength(VecSub(ahead,shell.pos)))
	if hit then
		local hitPos = VecAdd(shell.pos, VecScale(VecNormalize(VecSub(ahead, shell.pos)), dist))
		shell.active = false
		for a = 0,VecLength(VecSub(hitPos,shell.pos)), 0.1 do
			SpawnParticle("smoke",TransformToParentPoint(particlet,Vec(0,0,-a)),Vec(0,0,0),0.13,0.06+GetTimeStep()*0.1*a)
		end
		Explosion(hitPos,1.3)
	else
		for a = 0,VecLength(VecSub(ahead,shell.pos)), 0.1 do
			SpawnParticle("smoke",TransformToParentPoint(particlet,Vec(0,0,-a)),Vec(0,0,0),0.13,0.06+GetTimeStep()*0.1*a)
		end
		PointLight(ahead,1,0.9,0.7,5.3)
		DrawLine(shell.pos,ahead)
	end
	shell.time = shell.time+GetTimeStep()
	shell.pos = ahead
	if shell.time > shell.maxtime then shell.active = false end
end

--STRAFE
function considerStrafing()
	if FindLocation("strafetags") == 0 then return false end
	local probability = strafeBaseChance
	if attackCounter > 0 then 
		probability = 0.2 + strafeBaseChance
	elseif attackCounter > 3 then 
		probability = attackCounter/16 + strafeBaseChance
		probability = math.min(probability,0.8)
	end
	if math.random() <= probability and attackCounter >= 0 then
		strafeStartVec = VecSub(headTargetPos,targetPos)
		strafeStartVec[2] = 0 --flat heading
		strafeStartVec = VecNormalize(strafeStartVec)
		strafeEndVec = VecSub(targetPos,headTargetPos)
		strafeEndVec[2] = 0
		strafeEndVec = VecScale(VecNormalize(strafeEndVec), 500)
		strafeEndVec[2] = math.max(300,targetPos[2]+34)
		PlaySound(basiliscoAccelerate,headTargetPos,58, false)
		flightMode = "strafe"
		--reset alertSeenTimer if called from alert mode
		alertSeenTimer = 0
		
		targetPosShiftDown()
		return true --yes
	end
	return false--no
end

--NUKE
function extendLauncher()
	if launcher ~= 0 then
		launcherLocalTransform.rot = QuatSlerp(launcherLocalTransform.rot,QuatEuler(60,0,0),0.04)
	end
end
function retractLauncher()
	if launcher ~= 0 then
		launcherLocalTransform.rot = QuatSlerp(launcherLocalTransform.rot,QuatEuler(13,0,0),0.04)
	end
end

function explosion20(pos) --huge explosion
	Explosion(VecAdd(pos,Vec(0,6,0)),4)
	Explosion(VecAdd(pos,Vec(0,-6,0)),4)
	--the first radius ring, 12
	for r = 0,math.pi * 2, 1 do
		-- Explosion(VecAdd(pos,Vec(math.cos(r)*8,0,math.sin(r)*8)),4)
		-- PointLight(VecAdd(pos,Vec(math.cos(r)*8,0,math.sin(r)*8)),nuketrailR,nuketrailG,nuketrailB,2333)
		createLight(VecAdd(pos,Vec(math.cos(r)*8,0,math.sin(r)*8)),nuketrailR,nuketrailG,nuketrailB,4444,0.5,5)
		Explosion(VecAdd(pos,Vec(math.cos(r)*8,6,math.sin(r)*8)),4)
		Explosion(VecAdd(pos,Vec(math.cos(r)*8,-6,math.sin(r)*8)),4)
	end
	--second radius ring, 20
	for r = 0,math.pi * 2, 0.6 do
		Explosion(VecAdd(pos,Vec(math.cos(r)*16,0,math.sin(r)*16)),4)
	end--]]
	--main explosion
	Explosion(pos,4)--does calling the center explosion last do anything?
	PointLight(hitPos,nuketrailR,nuketrailG,nuketrailB,5555)
end
function considerNuke()
	if attackCounter > guaranteeNuke then --YOU'RE DEAD NOW 
		nukeStarted = true
		return true
	elseif attackCounter >=minConsiderNuke and math.random() < 0.27 then
		nukeStarted = true
		return true
	end
return false
end

--POISON (IT'S BLUETIDES)
function dispersePoison(origin,vel) --disperse over impact zone
	local poisonCloudProjectile = {} --stores pos, velocity vector,
	poisonCloudProjectile.pos = VecCopy(origin)
	poisonCloudProjectile.vel = vel
	poisonCloudProjectile.timer = poisonTime
	poisonCloudProjectile.radius = 0
	poisonCloudProjectile.dmg = poisonDamage
	poisonCloudProjectile.touchdown = false
	poisonCloudProjectile.step = 1/60
	poisonCloudProjectile.dt = 0 --for particle spawn control especially at slow mo
	table.insert(poisonClouds,poisonCloudProjectile)
end

function poisonCloudOperation(key)
	local puff = poisonClouds[key]

	if puff.timer <= 0 then
		table.remove(poisonClouds,key)
		return
	end
	puff.vel[2] = puff.vel[2] - 9.81*GetTimeStep() --gravity
	local ahead = VecAdd(puff.pos,VecScale(puff.vel,GetTimeStep()))
	puff.timer = puff.timer - GetTimeStep()
	
	if not puff.touchdown then
		QueryRequire("physical")
		rejectSelf()
		local hit, dist = QueryRaycast(puff.pos,VecNormalize(puff.vel),1.4)
		if hit or IsPointInWater(puff.pos) then
			puff.vel = Vec()
			ahead = puff.pos
			puff.touchdown = true
		else
			puff.pos = ahead
		end
	end
	
	--corrosion
	local corrodedir = VecNormalize(Vec(rndFloat(-1,1),rndFloat(-1,1),rndFloat(-1,1)))
	QueryRequire("physical")
	rejectSelf()
	local hit,dist = QueryRaycast(puff.pos,corrodedir,puff.radius)
	if hit then
		local hitPoint = VecAdd(puff.pos,VecScale(corrodedir,dist))
		MakeHole(hitPoint,0.3,0.2,0)
	end
	
	--smoke 
	ParticleReset()
	ParticleType("smoke")
	ParticleRadius(0,math.min(1,puff.radius),"easeout",0,0.1)
	ParticleColor(0.3,0.5,0.7)
	ParticleAlpha(0.07)
	ParticleDrag(0.5)
	ParticleCollide(1)
	ParticleEmissive(1.6,0,"linear",0,0.01)
	ParticleGravity(-1.2)
	local particleVel = Vec(0,1+math.random(),0)
	if puff.timer > 0.7 and puff.dt <= 0 then
	SpawnParticle(puff.pos,particleVel,1)
	SpawnParticle(puff.pos,particleVel,1)
	SpawnParticle(puff.pos,particleVel,1)
	SpawnParticle(puff.pos,particleVel,1)
	SpawnParticle(puff.pos,particleVel,1)
	end
	puff.radius = math.min(3.6,puff.radius + GetTimeStep())
	if puff.dt <=0 then
		puff.dt = puff.step
	else
		puff.dt = puff.dt - GetTimeStep()
	end
	
	if VecDist(GetPlayerPos(),puff.pos) < 3.6 then --bluetides will buff you while it kills you
		QueryRequire("physical")
		local dir = VecNormalize(VecSub(GetPlayerPos(),puff.pos))
		if not QueryRaycast(puff.pos,dir,VecDist(GetPlayerPos(),puff.pos)) then
			poisonBuffTimer = poisonBuffTime
			poisonHurtTimer = poisonHurtTime
		end
	end
end

--HEAVY CANNONS
function heavyCannonFire(shape,indirect) --origin location and indirect fire bool
	local heavyShot = {}
	local mi,ma = GetShapeBounds(shape)
	local origin = VecLerp(mi,ma,0.5)
	if indirect then 
		dir = Vec(0,1,0)
	else
		local dir = VecNormalize(VecSub(targetPos,origin))--indirect straight upwards
	end
	local clr = heavyCannonProjectileColor
	
	--initialize projectile
	heavyShot.indirect = indirect
	heavyShot.pos = origin
	heavyShot.target = VecCopy(targetPos)
	heavyShot.vel = VecScale(dir,3300)
	heavyShot.time = 0
	heavyShot.divetime = 1
	heavyShot.maxtime = 2.2
	heavyShot.dive = false
	heavyShot.impact = false --stop if it hits something then explode
	heavyShot.explosions = 21 --max number of explosions for mega explosion
	heavyShot.explosioncount = 0
	heavyShot.kill = false --killswitch for projectile
	heavyShot.step = 1/60 --60fps
	heavyShot.dt = 0
	table.insert(heavyCannonShots,heavyShot)
	PlaySound(heavyCannonFireSound, origin, 50) --what is loud?
	-- PointLight(origin,0,0.5,1,1000)
	createLight(origin,clr[1],clr[2],clr[3],1600,0,0.2) --set position, r,g,b, intensity, and max time at full intensity, then max time of the light
	createLight(origin,clr[1],clr[2],clr[3],400,0,0.6) --set position, r,g,b, intensity, and max time at full intensity, then max time of the light
	--muzzle flash
	ParticleReset()
	ParticleType("plain")
	ParticleTile(5)
	ParticleCollide(0)
	--ParticleColor(clr[1],clr[2],clr[3])
	ParticleRadius(2)
	ParticleStretch(1)
	for i=1,13 do
		SpawnParticle(origin,Vec(rndFloat(-7,7),rndFloat(9,36),rndFloat(-7,7)),rndFloat(0.4,1))
	end
end

function heavyShotOperation(key)
	local shotclr = heavyCannonProjectileColor
	local shot = heavyCannonShots[key]
	if shot.kill or shot.time > shot.maxtime then
		table.remove(heavyCannonShots,key)
		return
	end
	local ahead = VecAdd(shot.pos,VecScale(shot.vel,GetTimeStep()))
	if shot.impact then ahead = shot.pos end
	
	QueryRequire("physical")
	rejectSelf()
	local dir = VecNormalize(shot.vel)
	local hit, dist = QueryRaycast(shot.pos,dir,VecDist(shot.pos,ahead))
	if hit then --if hit, then stop projectile and start exploding
		ahead = VecAdd(shot.pos,VecScale(dir,math.max(0,dist-2))) -- airburst
		
		ParticleReset()
		ParticleTile(0)
		ParticleColor(shotclr[1],shotclr[2],shotclr[3])
		ParticleRadius(1)
		ParticleCollide(0)
		ParticleEmissive(3,0)
		for i=0,1,1/math.max(0.0009,VecDist(shot.pos,ahead)*2) do
			SpawnParticle(VecLerp(ahead,shot.pos,i),Vec(),1.4-i*GetTimeStep())
		end
		heavyShotExplosionParticles(ahead)
		
		shot.impact = true
		createLight(ahead,shotclr[1],shotclr[2],shotclr[3],7777,0,2)
		createLight(ahead,shotclr[1],shotclr[2],shotclr[3],6000,0.5,2.6)
		--Explosion(ahead,2)
	end
	
	if not shot.impact then --particle trail
		--particle trail
		ParticleReset()
		ParticleTile(0)
		ParticleColor(shotclr[1],shotclr[2],shotclr[3])
		ParticleRadius(1)
		ParticleCollide(0)
		ParticleEmissive(3,0)
		
		for i=0,1,1/math.max(0.0009,VecDist(shot.pos,ahead)*2) do --on a scale of veclerp
			SpawnParticle(VecLerp(ahead,shot.pos,i),Vec(),1.4-i*GetTimeStep())
		end
		PointLight(ahead,shotclr[1],shotclr[2],shotclr[3],101)
	end
	shot.pos = ahead
	--special explosion
	if shot.impact then
		if shot.explosioncount < shot.explosions and shot.dt <= 0 then --while there's still explosions left, and when the shot timestep is ready
				local rand = Vec(rndFloat(-1,1),rndFloat(-1,1),rndFloat(-1,1))--random vector for explosion
				rand = VecScale(VecNormalize(rand),rndFloat(3,10)) --scale normalized vec by size such as 4
				Explosion(VecAdd(shot.pos,rand),4)
				shot.explosioncount = shot.explosioncount + 1
		elseif shot.explosioncount >= shot.explosions then
			Explosion(shot.pos,4)
			shot.kill = true --delete projectile once exploding finishes
		end
	end
	
	if shot.time > shot.divetime then
		shot.vel = VecNormalize(VecSub(shot.target,shot.pos)) --home in on targetpos ideally out of render distance
		shot.vel = VecScale(shot.vel,3500)
	end
	if shot.dt <=0 then shot.dt = shot.step else shot.dt = shot.dt - GetTimeStep() end --timer for the shot effects timestep
	
	if not shot.impact then shot.time = shot.time+GetTimeStep() end
end
function heavyShotExplosionParticles(pos)
	local dotclr = heavyCannonDotColor
	for r=0,130 do
		railShotDippinDots() --cordium
		ParticleRadius(rndFloat(0.03,0.12))
		ParticleColor(dotclr[1],dotclr[2],dotclr[3])
		local rndVec = VecNormalize(Vec(rndFloat(-1,1),rndFloat(0,1),rndFloat(-1,1))) --random dir vector
		rndVec = VecScale(rndVec,rndFloat(2,10))
		SpawnParticle(pos,rndVec,rndFloat(7,17))
	end
end

--RECON FLARE
function reconFlareTrail() --particle
	ParticleReset()
	ParticleType("plain")
	--ParticleColor(idlelightR,idlelightG,idlelightB)
	ParticleTile(0)
	ParticleRadius(0.6,0.23,"linear")
	ParticleAlpha(1,0,"easein")
	ParticleGravity(0,5,"easein")
	ParticleDrag(0.5)
end

function considerReconFlare(extrachance) --When patrolling or investigating
	if extrachance == nil then extrachance = 0 end
	local chance = math.min(reconFlareMaxChance,reconFlareChance+extrachance+attackCounter/25) --each failed attack increases chance
	if math.random()<chance then
			reconFlareStart = true
	end
end

--tickspace
function railGunAttach(dt)
	if FindLocation("railguntags") == 0 then return end
	local railGunTransform = TransformToParentTransform(headTransform,railGunLocalTransform)
	local shroudTrans = TransformCopy(railGunShroudLocalTransform)
	shroudTrans.pos[3] = shroudTrans.pos[3]-shroudDeployAmt
	local railGunShroudTransform = TransformToParentTransform(headTransform,shroudTrans)
	local barrelTrans = TransformCopy(railGunBarrelLocalTransform)
	barrelTrans.pos[3] = barrelTrans.pos[3] - shroudDeployAmt
	barrelTrans.pos[3] = barrelTrans.pos[3] - barrelDeployAmt
	
	if shroudExtended then --extend shroud
		shroudDeployAmt = math.min(shroudDeployAmt+shroudDeploy/31,shroudDeploy)
	else
		shroudDeployAmt = math.max(0,shroudDeployAmt-shroudDeploy/21)
	end
	if barrelExtended then --extend shroud
		barrelDeployAmt = math.min(barrelDeployAmt+barrelDeploy/31,barrelDeploy)
	else
		barrelDeployAmt = math.max(0,barrelDeployAmt-barrelDeploy/21)
	end
	
	local railGunBarrelTransform = TransformToParentTransform(headTransform,barrelTrans)
	SetBodyTransform(railGun, railGunTransform)
	SetBodyTransform(railGunShroud, railGunShroudTransform)
	SetBodyTransform(railGunBarrel, railGunBarrelTransform)
end

function railGunStatic() --forces railgun bodies static
	SetBodyDynamic(railGun)
	SetBodyDynamic(railGunShroud)
	SetBodyDynamic(railGunBarrel)
end

function tickLaser(dt)
	if FindLocation("lasertags") == 0 then return end
	if laserDelayTimer > 0 then
		laserDelayTimer = laserDelayTimer - dt
		return --delay
	end
	if laserMode == "inactive" then
		if laserTriggered then
			laserMode = "charge"
			laserDelayTimer = laserDelay
			PlaySound(laserChargeSound,headTransform.pos,25)
			laserAxis.rot = QuatLookAt(headTransform.pos,GetPlayerPos())
		end
	elseif laserMode == "charge" then
		laserMode = "fire" 
		laserFireTimer = laserFireTime
	elseif laserMode == "fire" then
		if laserFireTimer > 0 then
			laserFireTimer = laserFireTimer-dt
			laser()
		elseif playerSeen and math.random() < 0.2 then --continue lasering
			laserMode = "charge"
			laserDelayTimer = laserDelay
			laserFireTimer = laserFireTime
			timeSinceChoosePatrol = 0
			attackCounter = attackCounter + 1 --laser was used
			laserAxis.rot = QuatLookAt(headTransform.pos,GetPlayerPos())
		else
			timeSinceChoosePatrol = 0
			laserMode = "inactive"
			laserTriggered = false
			--laserDelayTimer = 1.15
			--alertSeenTimer = 0
			laserDelayTimer = 0
			--attackCounter = attackCounter + 1 --laser was used
			--activeWeapon = "none" --deactivate laser and choose weapon again when sighting player again
		end
	end
end

function tickRailGun(dt)
	if FindLocation("railguntags") == 0 then return end
	if railDelayTimer > 0 then
		railDelayTimer = railDelayTimer - dt
	--	timeSinceLastSeen = 0 --no swaying
		--targetPos = GetPlayerPos()
		return--halt function here for delay
	end
	if railGunMode == "off" then
		if railGunArmed then
			if FindLocation("autocannontags") ~= 0 then autoCannonCounter = 6; autoCannonTimer = 1.42 end
			railGunMode = "charging"
			railDelayTimer = 2.1
			PlaySound(railGunSpoolUp, headTransform.pos, 13)
		end
	elseif railGunMode == "charging" then
		railGunMode = "fire"
		--attackCounter = attackCounter + 1
	elseif railGunMode == "fire" then
		railGunFire()
		railGunFire() --tandem fire because two explosions at once is instakill
		railGunMode = "off"
		activeWeapon = "none"
		railGunArmed = false
		railDelayTimer = 0
		timeSinceChoosePatrol = 0
	end
end

function tickRailGunStrafe(dt) --railgun tick version for strafing	
	if FindLocation("railguntags") == 0 then return end
	if railGunRapidCounter > 0 then
		if railGunTimer <= 0 then
			railGunFire(true,true)
			railGunTimer = railGunFireDelay
			railGunRapidCounter = railGunRapidCounter - 1
		end
	end
	railGunTimer = math.max(0,railGunTimer - dt)
end

function tickAutoCannon(dt)
	if FindLocation("autocannontags") == 0 then return end
	if autoCannonCounter > 0 then
		if autoCannonTimer <=0 then 
			autoCannonFire()
			autoCannonTimer = autoCannonFireDelay
			autoCannonCounter = autoCannonCounter - 1
		end
		autoCannonTimer = autoCannonTimer - dt
	end
	autoCannonSpreadTimer = math.max(0,autoCannonSpreadTimer - dt)
end

function tickMultiMissile(dt)
	local lostSight = true
	if playerSeeMeter >= subWeaponConsiderThreshold then
		lostSight = false
	end
	if multiMissileVolleys > 0 and lostSight and not playerDead() and not missilesReady then
		PlaySound(multiMissileAlarm,headTransform.pos,9,false)
		multiMissileLaunchDelay = 1
		missilesReady = true
		multiMissileCooldownTimer = multiMissileCooldown 
	elseif missilesReady and multiMissileVolleys > 0 then		
		multiMissileLaunchDelay = multiMissileLaunchDelay-dt
		if multiMissileLaunchDelay <= 0 then
			multiMissileLaunch()
			blindFireTimer = math.max(blindFireTimer,3.4)
			multiMissileVolleys = multiMissileVolleys-1
			multiMissileLaunchDelay = multiMissileLaunchTargetDelay
		end
	elseif GetFloat("game.player.health") == 0.0 or rammed or multiMissileVolleys == 0 then --don't launch missiles when dead player respawns or during recovery from ram
		multiMissileVolleys = 0
		missilesReady = false
	end
	multiMissileCooldownTimer = math.max(0,multiMissileCooldownTimer - dt)
end

function tickRCWS(dt)
	local lostSight = true
	if playerSeeMeter >= subWeaponConsiderThreshold then
		lostSight = false
	end
	if not HasTag(rcwsSettings,"active") then return end
	for r,node in ipairs(rcws) do
		if not weaponShapeCondition(node) then --if it broke then don't operate
		else
			--rotation, always on until the shape breaks
			local mi, ma = GetShapeBounds(node.shape)
			local origin = VecLerp(mi,ma,0.5)--magica doesn't center voxes
			local shapeTrans = GetShapeWorldTransform(node.shape) --get transform of shape
			local shapeDir = TransformToParentVec(shapeTrans,Vec(0,0,-1))
			shapeTrans.pos = origin 
			
			local aiming = Transform(Vec(), node.localrot)
			local aimDir = TransformToParentVec(aiming,Vec(0,0,-1)) --the forward dir of the aim transform
			local ax, ay, az = aimDir[1], aimDir[2], aimDir[3] --split into axes
			local aimRot = math.deg(math.atan2(az,ax))--relative to base
			local aimFlat = math.sqrt(az^2+ax^2)
			local targetVert = math.deg(math.atan(ay,aimFlat))
			local rax,ray = GetQuatEuler(node.localrot) -- euler of current aim rot
			
			local leadTarget = VecScale(GetPlayerVelocity(),dt*3)
			if not playerSeen then leadTarget = Vec() end
			local relativeTargetPos = TransformToLocalPoint(shapeTrans,VecAdd(lookPos,leadTarget))
			
			local relativeDir = VecNormalize(relativeTargetPos)
			local targetQuat = QuatLookAt(Vec(),relativeTargetPos) --the quat to look at target
			local rtx, rty, rtz = GetQuatEuler(targetQuat)
			
			local driftAngle = math.deg(math.acos(math.min(1,VecDot(relativeDir,aimDir))))
			local headingDiff = rty-ray
			if headingDiff > 180 then
				headingDiff = headingDiff-360
			elseif headingDiff <= -180 then
				headingDiff = 360+headingDiff
			end
			-- DebugPrint(headingDiff)
			ray=ray+(clamp(headingDiff*15,-180,180))*dt
			if rax < rtx then
				rax = math.min(rtx,rax + 200*dt)
			else rax = math.max(rtx,rax - 200*dt)
			end
			node.localrot = QuatEuler(math.min(rax,5),ray,0)
			
			aimDir = TransformToParentVec(aiming,Vec(0,0,-1))
			node.dir = TransformToParentVec(shapeTrans,aimDir)
			-- DrawLine(origin,TransformToParentPoint(shapeTrans,VecScale(aimDir,225)),1,0.7,0.2)--debug line
		
			--weapon firing operation
			node.pulsetimer = math.max(node.pulsetimer-dt, 0)
			if node.ready and not node.isattacking and lostSight then
				node.isattacking = true
				node.pulsecounter = math.random(node.pulsecountrange[1],node.pulsecountrange[2])
			--pulse
			elseif node.isattacking then
				if node.pulsecounter > 0 then
					if node.pulsetimer <= 0 then
						if VecDot(aimDir,relativeDir) >=math.cos(math.rad(7)) then
							rcwsPulse(node)
							blindFireTimer = math.max(blindFireTimer,1.75)
							node.pulsecounter = node.pulsecounter - 1
						end
					end
				end
			end
			--deactivate 
			if node.pulsecounter == 0 and node.isattacking then --emptied
				node.isattacking = false
				node.ready = false
				node.spreadmod = 0 --reset spread
				node.cooldowntimer = rcwsCD --cooldown time
			end
			
			node.cooldowntimer = math.max(node.cooldowntimer - dt,0)
		end
	end
end

function tickMortar(dt)
	--DebugPrint(mortars[1].timer)
	if not HasTag(mortarSettings,"active") then return end
	
	local lostSight = true
	if playerSeeMeter >= subWeaponConsiderThreshold then
		lostSight = false
	end
	for m, node in ipairs(mortars) do
		if node.timer > 0 and lostSight then
			node.timer = node.timer - dt
			if node.timer <= 0 then
				for i=1, math.random(1,10) do
					mortarLaunch(node)
				end
				blindFireTimer = math.max(blindFireTimer,3)
				timeSinceDistraction = 0
				timeSinceChoosePatrol = 0
				considerMortarReload(node)
			end
		end
	end
end

function tickRam()
	if not hasRam then return end
	if ramStarted then
		flightMode = "ram"
		PlaySound(ramStart,headTransform.pos,69)
		ramStarted = false
	end
	
end

function basiliscoRamOperation(dt) --a bit different as a flight mode rather than ranged weapon
	local nose = TransformToParentPoint(headTransform,Vec(0,0,-5.25))
		
	if not ramSmashed then
		rejectSelf() --prepare for raycasting
		QueryRequire("physical")
		local dir = TransformToParentVec(headTransform,Vec(0,0,-1))
		local hit, dist = QueryRaycast(headTransform.pos, dir, 5.25+math.max(1,speed*dt), 2.8)
		if hit then
			local rammingPoint = TransformToParentPoint(headTransform,Vec(0,0,-dist))
			MakeHole(rammingPoint, 4,4,1) --I'M THE JUGGERNAUT @#%#$%!
			local aabbMin = VecAdd(rammingPoint,Vec(-6,-6,-6))
			local aabbMax = VecAdd(rammingPoint,Vec(6,6,6))
			local debris = QueryAabbBodies(aabbMin,aabbMax) --gets aabb bodies around just-made hole
			local ramForce = ramFast*1.2
			for d,body in ipairs(debris) do
				local bodyPos = GetBodyTransform(body).pos
				local bodyMass = GetBodyMass(body)
				local bodydir = VecSub(bodyPos,rammingPoint)
				local dist = VecLength(VecSub(bodyPos,rammingPoint))
				local impulse = VecScale(dir,ramForce)
				bodydir = VecNormalize(VecLerp(bodydir,impulse,0.69))
				impulse = VecScale(bodydir,ramForce*bodyMass)
				-- impulse[2] = math.abs(impulse[2]) --UPWARDS
				if dist < 4.45 then --radius
					if VecLength(GetBodyVelocity(body)) < ramForce then
						ApplyBodyImpulse(body,rammingPoint,impulse)
					end
				end
			end
			--ramSmashed = true --ram already hit something, now disable
		end
	end
	if not ramExploded and ramDetonator ~= 0 then --if equipped with explosive ram
		local detTransform = GetShapeWorldTransform(ramDetonator)
		local mi,ma = GetShapeBounds(ramDetonator)
		detTransform.pos = VecLerp(mi,ma,0.5)--shape transforms always start at the corner dangit
		local dir = TransformToParentVec(detTransform,Vec(0,0,-1))
		rejectSelf()
		QueryRequire("physical large")
		local hit, dist = QueryRaycast(detTransform.pos,dir,math.max(1,speed*dt))
		if hit then
			local hitPoint = TransformToParentPoint(detTransform,Vec(0,0,-dist))
			Explosion(VecAdd(hitPoint,Vec(-2,0,2)),5)
			Explosion(VecAdd(hitPoint,Vec(2,0,-4)),5)
			Explosion(hitPoint,5)
			--thanks truxterminator for below
			ParticleTile(5)
			ParticleCollide(0)
			ParticleColor(1, 0.6, 0.4, 1, 0.3, 0.2)
			ParticleDrag(0,1,"easein")
			ParticleEmissive(6.0, 0.0, "easeout")
			ParticleAlpha(1, 0)
			ParticleRadius(0.3, 5.0)
			for i=1, 30 do
				SpawnParticle(hitPoint, Vec(rndFloat(-19, 19), rndFloat(0, 8), rndFloat(-19,19)), rndFloat(0.5, 1.0)*2)	
			end

			ParticleReset()
			ParticleCollide(0,1,"easein",0,0.3)
			ParticleType("plain")
			ParticleDrag(0,1,"easein")
			ParticleColor(0.8, 0.75, 0.7)
			ParticleAlpha(1, 0)
			ParticleRadius(0.4, 3.0)
			for i=1, 40 do
				SpawnParticle(hitPoint, Vec(rndFloat(-12, 12), rndFloat(0, 4), rndFloat(-12,12)), rndFloat(0.5, 1.0)*4)	
			end
			PointLight(VecAdd(hitPoint, Vec(0, 1, 0)), 1, 0.5, 0.2, 1000)
			
			SetLightEnabled(ramDetLight, false)
			ramExploded = true
		end
	end
	
	local playerdist,nearestpt = getDistanceToLineSegment(GetPlayerPos(),headTransform.pos,nose)
	if playerdist < 4 and not rammedPlayer then--punt player
		local impulseDir = VecSub(GetPlayerPos(),nearestpt)
		local impulse = TransformToParentVec(headTransform,Vec(0,0,-speed))--PUNTED
		impulseDir = VecNormalize(VecLerp(impulseDir,impulse, 0.7))
		impulseDir[2] = math.abs(impulseDir[2])
		SetPlayerVelocity(VecAdd(GetPlayerVelocity(),VecScale(impulseDir,speed*0.75)))
		SetPlayerHealth(0) --instakill
		rammedPlayer = true --only 1 tick
	end
	if VecLength(VecSub(targetPos, headTargetPos)) < math.max(1.25*(speed*dt),1.8) and not rammed then --ram complete, recover
		rammed = true
		local heading = TransformToParentVec(headTransform,Vec(rndFloat(-1,1),0,-2))
		heading[2] = 0
		heading = VecNormalize(heading)
		recoverVec = VecAdd(targetPos,VecScale(heading,rndFloat(15,78)))
		recoverVec[2] = targetPos[2] + 85
	end
	if rammed and VecLength(VecSub(recoverVec,headTargetPos)) < math.max(1.25*(speed*dt),1) then --end ram and reset variables
		makeBodyBreakable()
		makeWingsBreakable()
		rammed = false
		rammedPlayer = false
		ramSmashed = false
		if ramDetonator ~= 0 then 
			ramExploded = false 
			SetLightEnabled(ramDetLight, true)
		end
		flightMode = "patrol"
		timeSinceDistraction = 0
		timeSinceChoosePatrol = 0
	end
end

function basiliscoStrafeOperation(dt)
	if VecLength(VecSub(headTargetPos,targetPos)) > maxSpeed*9 then strafeStart = true end
	if VecLength(VecSub(headTargetPos,targetPos)) < maxSpeed*2.1 and strafeStart then 
		strafeEnd = true 
		if not strafeWhoosh then PlaySound(flybySound,headTargetPos,100,false); strafeWhoosh = true end
	end
	if VecLength(VecSub(headTargetPos,targetPos)) < maxSpeed*5.47 and strafeStart and not strafeWeaponStart then
		strafeWeaponStart = true
		if FindLocation("autocannontags") ~= 0 then
			if math.random()<0.62 then
				autoCannonFireDelay = 0
				autoCannonCounter = math.random(24,45) --brrrrrrt
			else
				autoCannonCounter = math.random(4,16) --slow autocannon
			end
		end
		if FindLocation("railguntags") ~= 0 then
			if HasTag(railGunSettings,"active") then
				if math.random()<0.3 then
					railGunFire(nil,true)
					railGunFire(nil,true)
				elseif math.random()<0.6 then
					railGunRapidCounter = 4
				end
			end
		end
	end
	if VecLength(VecSub(headTargetPos,targetPos)) > 230 and strafeEnd then --exit strafing mode
		strafeEnd = false
		strafeStart = false --resets flags
		strafeWeaponStart = false
		strafeWhoosh = false
		if FindLocation("autocannontags") ~= 0 then 
			autoCannonFireDelay = autoCannonFireDelayOrig --restore slow firing autocannon
		end
		flightMode = "patrol"
		timeSinceChoosePatrol = 0
		timeSinceDistraction = 0
	end
	tickRailGunStrafe(dt)
end

function launcherAttach()
	local launcherTransform = TransformToParentTransform(launcherUnitTransform,launcherLocalTransform)
	SetBodyTransform(launcher,launcherTransform)
end

function launcherStatic()-- force back mounted launcher to stay static cause 0.8.0
	SetBodyDynamic(launcher, false)
end

function tickNuke(dt)
	if launchTimer > 0 then
		launchTimer = launchTimer - dt
		if not nukeLaunched then PlayLoop(klaxon) end
		return
	end
	if nukeMode == "off" then
		launchTimer = launchCountdown
		SetLightIntensity(lightSpot, 3888)
		SetLightIntensity(lightSource, 5)
		flightMode = "emplaced"
		nukeMode = "preparing"
	elseif nukeMode == "preparing" then
		nukeMode = "launch"
	elseif nukeMode == "launch" then
		local origin = TransformToParentPoint(GetBodyTransform(launcher),nukeSpawnPoint)
		local dir = VecSub(targetPos,origin)
		local dirTrans = Transform(origin,QuatLookAt(Vec(),dir))
		--get angle,  given gravity
		local g = nukeGravity
		local y = dir[2]
		local dirFlat = VecCopy(dir)
		dirFlat[2]= 0
		local x = VecLength(dirFlat)
		local angleSub = math.deg(math.atan2(y,x))
		local v = nukeSpeed
		local v2 = v*v--i need simplicity ffs
		local v4 = v2*v2
		local x2 = x*x
		local angle = math.atan2(v2 + math.sqrt( v4 - g*(g*x2+2*y*v2) ), g*x)
		angle = math.deg(angle)-angleSub
		
		--launch the nuke
		dir = TransformToParentTransform(dirTrans,Transform(Vec(),QuatEuler(angle,0,0)))
		dir = TransformToParentVec(dir,Vec(0,0,-1))
		nukeVel = VecScale(dir,nukeSpeed)
		nukePos = origin
		nukeTarget = VecCopy(targetPos)
		nukeLaunched = true
		nukeMode = "observe"
		
		--recoil?
		hoverVertiVel = -30
		for t,body in ipairs(tailTable) do
			body.targetPos[2] = body.targetPos[2]-20 --pushed down by the force
		end
		
		PlaySound(railGunFireSound,origin,300,false)
	elseif nukeMode == "observe" then--observe the nuke impact (also operate the nuke projectile)
		local ahead = VecAdd(nukePos,VecScale(nukeVel,GetTimeStep()))
		local dir = VecSub(ahead,nukePos)
		QueryRequire("physical")
		rejectSelf()
		local hit, dist = QueryRaycast(nukePos,VecNormalize(dir),VecLength(dir)+0.02)
		if hit then
			local hitPos = VecAdd(nukePos, VecScale(VecNormalize(dir), dist))
			nukeExploded = true
			--Explosion(hitPos,5)
			explosion20(hitPos)
			local dis = VecLength(VecSub(GetPlayerPos(),hitPos))
			if dis <36 then
				local forceTrans = Transform(Vec(),QuatLookAt(hitPos,GetPlayerPos()))
				local force = TransformToParentVec(forceTrans,Vec(0,0,-40-dis))
				force[2] = math.max(60)
				SetPlayerVelocity(force)
				nukeKilledPlayer = true --not even godmode will save you
				SetString("hud.notification","You are well and truly DEAD.")
			end --YEET

		else
			--DrawLine(nukePos,ahead,1,0.95,0.5)
			--particle generator
			ParticleReset()
			ParticleType("plain")
			ParticleTile(5)
			ParticleColor(nuketrailR,nuketrailG,nuketrailB)
			ParticleRadius(0.8)
			ParticleAlpha(1,0.25)
			ParticleGravity(0)
			ParticleDrag(0.01)
			ParticleEmissive(2,0.3)
			ParticleSticky(0.2,0.7)
			ParticleCollide(1,1,"constant",0.1,1)
			for i=0,1, 1/(VecDist(nukePos,ahead)*2) do
				ParticleRotation(rndFloat(-5,5))
				SpawnParticle(VecLerp(nukePos,ahead,i),Vec(),0.3+i*GetTimeStep())
			end
		end
		nukePos = ahead
		if nukePos[2] <= -37 then
			nukeExploded = true
			Explosion(nukePos,5)
		end--]]
		
		if nukePos[2] > math.max(969,GetPlayerPos()[2]+200) and not nukeDive then --dive now
			nukeDive = true
			local offset = VecNormalize(Vec(rndFloat(-1,1),0,rndFloat(-1,1)))
			offset = VecScale(offset,rndFloat(0,20))
			local toPlayer = VecNormalize(VecSub(VecAdd(GetPlayerPos(),offset),nukePos))
			--local toPlayer = VecNormalize(VecSub(GetPlayerPos(),nukePos))
			nukeVel = VecScale(toPlayer,nukeSpeed*2)
		end
		
		if nukeExploded then --deactivate tick
			flightMode = "alert"
			nukeExploded = false
			nukeLaunched = false
			nukeStarted = false
			nukeDive = false
			nukeMode = "off"
			activeWeapon = "none"
		end
	end
end

function tickNukeKill(dt)
	if nukeKilledPlayer then
		if not nukeKillPos then nukeKillPos = GetPlayerCameraTransform().pos end
		SetTimeScale(0.5)
		local cameraLook = QuatLookAt(GetPlayerCameraTransform().pos,GetBodyTransform(searchLight).pos)
		local camPos = VecLerp(nukeKillPos,GetBodyTransform(searchLight).pos,nukeKilledTimer/2.3)
		SetCameraTransform(Transform(GetPlayerCameraTransform().pos,cameraLook))
		nukeKilledTimer = nukeKilledTimer + dt
		
		if nukeKilledTimer > 1.7 then
			Command("game.quit") --killed you so hard the game quits
		end
	end
end

function tickPoison(dt)
	alertLight(lightSource)
	alertLight(lightSpot)
	weaponArmed = true --as a single-projectile weapon the entire tick only runs when the weapon is selected
	timeSinceChoosePatrol = 0
	timeSinceDistraction = 0
	if poisonLaunchTimer > 0 then
		poisonLaunchTimer = poisonLaunchTimer - dt
		--insert sound loop here
		if not poisonLaunched then PlayLoop(poisonAlarm) end
		return
	end
	if poisonMode == "off" then
		poisonLaunchTimer = poisonLaunchTime
		SetLightIntensity(lightSpot, 3888)
		SetLightIntensity(lightSource, 5)
		poisonMode = "preparing"
	elseif poisonMode == "preparing" then
		poisonMode = "launch"
	elseif poisonMode == "launch" then--send off a projectile containing the poi-- bluetides
		local origin = TransformToParentPoint(GetBodyTransform(launcher),poisonLaunchPoint)
		local dir = TransformToParentVec(GetBodyTransform(launcher),Vec(0,0,-1))
		local dirTrans = Transform(origin,QuatLookAt(Vec(),dir))
		poisonVel = 125
		poisonTrans = TransformCopy(dirTrans)
		poisonOrigin = origin
		poisonTarget = targetPos
		poisonLaunched = true
		poisonMode = "observe"
		PlaySound(railGunFireSound,origin,100,false)
		PlaySound(smokeLauncherSound,origin,40,false)
		--insert recoil here
		hoverVertiVel = -13
	elseif poisonMode == "observe" then
		if poisonSet then
			poisonSet = false
			poisonSequenceStarted = false
			poisonDive = false
			poisonLaunched = false
			poisonMode = "off"
		end
		
		if poisonLaunched then 
			local ahead = TransformToParentPoint(poisonTrans,Vec(0,0,-poisonVel*dt))
			if playerTracked then 
				poisonTarget = targetPos
			end --guided onto your position
			
			QueryRequire("physical")
			rejectSelf()
			local hit, dist = QueryRaycast(poisonTrans.pos,TransformToParentVec(poisonTrans,Vec(0,0,-1)),poisonVel*dt)
			if hit then --premature detonation
				Explosion(poisonTrans.pos,2)
				poisonSet = true
				poisonLaunchTimer = 5 --double as post impact observation time
				for i=0,59 do
					local poisonTransCopy = TransformCopy(poisonTrans)
					poisonTransCopy.rot = QuatRotateQuat(poisonTransCopy.rot,QuatEuler(0,0,math.random(360)))
					poisonTransCopy.rot = QuatRotateQuat(poisonTransCopy.rot,QuatEuler(rndFloat(0,50),0,0))
					local poisonVelVec = TransformToParentVec(poisonTransCopy,Vec(0,0,10))
					dispersePoison(poisonTrans.pos,poisonVelVec)
				end
			end
			
			if VecDist(poisonTarget,poisonTrans.pos) < 15 and poisonDive then --about to disperse bluetides over target
				Explosion(poisonTrans.pos,0.5)
				poisonSet = true
				poisonLaunchTimer = 6
				for i=0,77 do
					local poisonTransCopy = TransformCopy(poisonTrans)
					poisonTransCopy.rot = QuatRotateQuat(poisonTransCopy.rot,QuatEuler(0,0,math.random(360)))
					poisonTransCopy.rot = QuatRotateQuat(poisonTransCopy.rot,QuatEuler(rndFloat(0,50),0,0))
					local poisonVelVec = TransformToParentVec(poisonTransCopy,Vec(0,0,-20))
					dispersePoison(poisonTrans.pos,poisonVelVec)
				end
			end
			--DrawLine(poisonTrans.pos,ahead)
			poisonTrans.pos = ahead
			PointLight(ahead,0.2,0.6,1,50)
			
			if VecDist(poisonOrigin,poisonTrans.pos) > 120 and not poisonDive then
				poisonDive = true
			end
			--particle trail
			ParticleReset()
			ParticleType("plain")
			ParticleTile(5)
			ParticleColor(0.1,.5,1) --it's bluetides
			ParticleRadius(0.7)
			ParticleAlpha(1,0.25)
			ParticleGravity(0)
			ParticleDrag(0.1)
			ParticleEmissive(3,0.3)
			ParticleStretch(0)
			ParticleSticky(0.2,0.7)
			ParticleCollide(1,1,"constant",0.1,1)
			SpawnParticle(poisonTrans.pos,0,0.3)
			
			--orientation of projectile
			if poisonDive and VecDist(poisonTarget,poisonTrans.pos) > 10 then
				poisonTrans.rot = QuatSlerp(poisonTrans.rot,QuatLookAt(poisonTrans.pos,poisonTarget),0.09)
			end
		end
	end
end

function tickHeavyCannons(dt)
	weaponArmed = true
	timeSinceChoosePatrol = 0
	timeSinceDistraction = 0
	if heavyCannonTimer > 0 then
		heavyCannonTimer = heavyCannonTimer - dt
		if not heavyCannonFired then 
			if heavyCannonTimer < 2 and not heavyCannonChargeSoundTrigger then
				heavyCannonChargeSoundTrigger = true
				PlaySound(heavyCannonChargeSound,headTransform.pos,40,false)
			end
		end --windup sound
		return
	end
	if not heavyCannonBeginTick then
		heavyCannonTimer = heavyCannonWindup
		heavyCannonBeginTick = true
		return
	end
	if heavyCannonFired then --disarm until next firing cycle
		heavyCannonFired = false
		heavyCannonBeginTick = false
		heavyCannonChargeSoundTrigger = false
		heavyCannonsArmed = false
	else --fire
		heavyCannonFire(heavyCannons[math.random(#heavyCannons)], true)
		heavyCannonTimer = heavyCannonObserveTime
		heavyCannonFired = true
	end
end

function tickReconFlare(dt)
	reconFlareTrail() --particle setup
	if not reconFlareLaunched then--prepare launch sequence
		local launchpos = TransformToParentPoint(headTransform,reconFlareLocalLaunchPos)
		reconFlarePos = launchpos
		reconFlareVel = Vec(0,reconFlareLaunchupSpeed,0)
		reconFlareLaunched = true
		local dir = VecNormalize(Vec(rndFloat(-1,1), 0, rndFloat(-1,1)))
		local r = rndFloat(0, 100) --rubberbanding to the player
		reconFlareTarget = VecAdd(GetPlayerPos(), VecScale(dir, r))
		PlaySound(smokeLauncherSound,launchpos,33,false)
		return
	end
	if reconFlareLaunched and not reconFlareActive then --flare going up
		SpawnParticle(reconFlarePos,Vec(),3.4)
		reconFlareVel[2] = reconFlareVel[2]-reconFlareGravity*dt--gravity
		local flareToTarget = VecSub(reconFlareTarget,reconFlarePos) --vector distance of flare to target
		flareToTarget[2] = 0--horizontalize
		local flareDist = VecLength(flareToTarget) --distance in number
		if reconFlareSpeed < flareDist then --control horizontal speed of flare
			reconFlareSpeed = reconFlareSpeed + 21*dt --speed up
		else
			reconFlareSpeed = flareDist --slow down
		end
		flareToTarget = VecNormalize(flareToTarget)
		local fallcopy = reconFlareVel[2]
		reconFlareVel = VecScale(flareToTarget,reconFlareSpeed)
		reconFlareVel[2] = fallcopy
		reconFlarePos = VecAdd(reconFlarePos,VecScale(reconFlareVel,dt))
		
		if reconFlareVel[2] < 0 then --when falling
			rejectSelf() 
			rejectAll()
			rejectChoppers()
			if QueryRaycast(reconFlarePos,Vec(0,-1,0),60,0,false) or reconFlarePos[2] <= 60 then --arming height above ground or sea	
				reconFlareIntensity = reconFlareIntensityRef
				reconFlareActive = true --turn on the flare
				PlaySound(lightBeep,reconFlarePos,25,false)
			end
		end
		PointLight(reconFlarePos,idlelightR,idlelightG,idlelightB,32)
	elseif reconFlareLaunched and reconFlareActive then --flare is on
		SpawnParticle(reconFlarePos,Vec(),3.4)
		
		local colorR, colorG, colorB = 1,1,1
		if reconFlareSeePlayer then
			colorR, colorG, colorB =  alertlightR,alertlightG,alertlightB
		else
			colorR, colorG, colorB = idlelightR,idlelightG,idlelightB
		end
		PointLight(reconFlarePos, colorR,colorG,colorB, reconFlareIntensity) --the light
		reconFlarePos[2] = reconFlarePos[2]-45/reconFlareTime*dt --slow parachute down
		
		reconFlareTimer = reconFlareTimer + dt
		if reconFlareTime-reconFlareTimer<=1.1 then
			reconFlareIntensity = reconFlareIntensity - reconFlareIntensityRef*dt
		end
		
		local playerDist = VecLength(VecSub(GetPlayerCameraTransform().pos,reconFlarePos))
		local dir = VecNormalize(VecSub(GetPlayerCameraTransform().pos,reconFlarePos))
		
		reconFlareSeePlayer = false
		if playerDist < 130 then --player in range
			if not QueryRaycast(reconFlarePos,dir, playerDist, 0, true) then --sighted
				timeSinceLastSeen = 0
				timeSinceChoosePatrol = 0
				timeSinceDistraction = 0
				reconFlareSeePlayer = true
				playerSeeMeter = math.max(0.9,playerSeeMeter)
			end
			--day mode linear
			if reconFlareDayMode then
				local lineTarget = VecAdd(GetPlayerPos(),Vec(rndFloat(-7,7),rndFloat(-4,7),rndFloat(-7,7)))
				local linedir = VecNormalize(VecSub(lineTarget,reconFlarePos))
				local dist = 130
				local hit, dist = QueryRaycast(reconFlarePos,linedir, 130,0,true)
				DrawLine(reconFlarePos,VecAdd(reconFlarePos,VecScale(linedir,dist)),colorR,colorG,colorB,0.75)
			end
		end
		if reconFlareSeePlayer then considerNLOS() end
		if reconFlareTimer >= reconFlareTime then --time's up
			reconFlareEnd = true
		end
	end
	if reconFlareEnd then
		reconFlareStart = false --reset whole routine
		reconFlareSpeed = 0 --no speed for next flare
		reconFlareLaunched = false
		reconFlareActive = false
		reconFlareSeePlayer = false
		reconFlareTimer = 0 --reset timer
		reconFlareEnd = false
	end
end

function weaponsTickMaster(dt)
	if revWeapons[activeWeapon] and activeWeapon == "laser" then
		tickLaser(dt)
	end
	if revWeapons[activeWeapon] and activeWeapon == "railgun" then
		tickRailGun(dt)
	end
	if revWeapons[activeWeapon] and activeWeapon == "ram" then
		tickRam(dt)
	end
	
	if FindLocation("autocannontags") ~= 0 then
		tickAutoCannon(dt)
	end
	
	if FindLocation("multimissiletags") ~= 0 then
		tickMultiMissile(dt)
	end
	
	if FindLocation("nuketags") ~= 0 then
		if nukeStarted then
			tickNuke(dt)
		end
	end
	
	if FindLocation("reconflaretags") ~=0 then
		if reconFlareStart then
			tickReconFlare(dt)
		end
	end
	
	if FindLocation("rcwstags") ~= 0 then
		tickRCWS(dt)
	end
	
	if FindLocation("mortartags") ~= 0 then
		tickMortar(dt)
	end
end

--execute weapon operations in tickspace
function tickRailGunOperation()
	if hasRailGun then --only if weapon is equipped
		for key, shot in ipairs(railGunShotHandler.railShots) do --operation of railGun shots in tickspace
			if shot.active then 
				railShotOperation(shot)
			end
		end
	end
end

function tickAutoCannonOperation()
	if FindLocation("autocannontags") ~= 0 then --only if weapon is equipped
		for key, shell in ipairs(autoCannonShellHandler.shells) do --operation of autoCannon shells in tickspace
			if shell.active then 
				autoCannonShellOperation(shell)
			end
		end
	end
end

function tickMultiMissileOperation()
	if FindLocation("multimissiletags") ~= 0 then --only if weapon is equipped
		for key, nail in ipairs(multiMissileHandler.multiMissiles) do --operation of multi missiles in tickspace
			if nail.active then 
				multiMissileOperation(nail)
			end
		end
	end
end

function tickPulseLaserOperation()
	--DebugPrint(#pulseLaserProjectiles)
	if FindLocation("rcwstags") ~=0 then
		for key, pulse in ipairs(pulseLaserProjectiles.pulses) do
			if pulse.active then
				rcwsPulseOperation(pulse)
			end
		end
	end
end

function tickMortarOperation()
	if FindLocation("mortartags") ~= 0 then
		for key, shell in ipairs(mortarProjectileHandler.mortarShots) do
			if shell.active then
				mortarProjectileOperation(shell)
			end
		end
	end
end

function tickPoisonCloudOperation(dt)
	if hasPoison then
		for id=#poisonClouds,1,-1 do
			poisonCloudOperation(id)
		end
		
		--player dmg and buff
		if poisonBuffTimer > 0 then
			poisonBuffed = true
			poisonBuffTimer = poisonBuffTimer - dt
		else
			poisonBuffed = false
		end
		if poisonHurtTimer > 0 then
			poisonHurt = true
			poisonHurtTimer = poisonHurtTimer - dt
		else
			poisonHurt = false
		end
		if poisonHurt then
			-- if GetPlayerHealth()> 0.05 then 
				SetPlayerHealth(GetPlayerHealth()-poisonDamage*dt) --amanatides illegal ingredients
			-- end
		end
	end
end

function tickHeavyShotOperation()
	if hasHeavyCannons then
		for id=#heavyCannonShots,1,-1 do
			heavyShotOperation(id)
		end
	end
end	

function projectileTickMaster(dt)
	tickRailGunOperation()
	tickAutoCannonOperation()
	tickMultiMissileOperation()
	tickPulseLaserOperation()
	tickMortarOperation()
	tickPoisonCloudOperation(dt)
end


--UPDATE
--poison (bluetides)
function poisonPlayerBuff() --update space for physical sim
	if poisonBuffed then
		local buff = poisonBuff
		local moveX = (InputValue("right") - InputValue("left"))*buff
		local moveY = (InputValue("up") - InputValue("down"))*buff
		local inputVel = Vec(moveX,0,-moveY)
		inputVel = TransformToParentVec(GetPlayerTransform(),inputVel)
		SetPlayerGroundVelocity(inputVel) 
	end
end

--DRAWSPACE

--POISON (IT's BLUETIDES)
function drawPoisonHud()
	UiPush()
		if poisonBuffed then
			UiColor(0.2, 0.4, 1.0, poisonBuffTimer * 0.5)
			local w,h = UiGetImageSize("ui/hud/steroid.png")
			if w > 0 and h > 0 then
				UiScale(UiWidth()/w, UiHeight()/h)
				UiImage("ui/hud/steroid.png")
			end
		end
	UiPop()
end
