//Insurgencydude exists because I love all of insurgency's voice clips and I wanted
//to give a character as much potential to react to situations as possible
//ergo I will JAM this file full of lua designed to detect and react to events with voice lines
//also it will probably have like 8 MB of voice lines

//this script is by freeman2651
//the lovely art is by kel in kelchars pack
//so as long as kel is okay with it, you can repurpose this script for what you would like
//(unless your name is retrostation)
//regardless of what you use the script for, the art/skin is NOT reusable without kel's explicit permission

//Hi there, Mr. Freeman sir.
//I said I'd maybe end up using this script for Yosuke some... what, two years ago? Three?
//probably three years ago by now, ha.
//lets get on it, baby!

local id_logging = CV_RegisterVar({
	name = "insurgencydude_logging",
	defaultvalue = "Off",
	flags = CV_NOSHOWHELP,
	possiblevalue = CV_OnOff
})

local id_stressreadout = CV_RegisterVar({
	name = "insurgencydude_stresshud",
	defaultvalue = "Off",
	flags = CV_NOSHOWHELP,
	possiblevalue = CV_OnOff
})



local FRACUNIT = FRACUNIT
local TICRATE = TICRATE

//constants for easy access
local pmo = nil
local STRESSED = TICRATE*15 //level of stress at which we switch to panicked voicelines
local MAXSTRESS = TICRATE*25 //caps stress so it doesn't last forever
local VOICEWAIT = TICRATE*3 //wait this long between playing clips where possible
local VOICEWAITLONG = TICRATE*8 //wait this long for playing low priority voiceclips (overtake, boost)
local MOBJGRACETIME = TICRATE/2 //how long mobjs have to wait after being fired from the player until the player can near-miss their own thrown item
local NEARMISSCOOLDOWN = 10 //how often something can be grazed
local lottery = 0 //placeholder random number generated every frame
local nearmissbox = nil

freeslot("MT_YOCOLLIDER")

mobjinfo[MT_YOCOLLIDER] = {
	doomednum = -1,
	spawnstate = S_NULL,
	radius = FRACUNIT*96,
	height = FRACUNIT*96,
	flags = MF_NOGRAVITY|MF_NOTHINK,
}
//boost
freeslot("sfx_yobst1","sfx_yobst2","sfx_yobst3",
		"sfx_yobsta","sfx_yobstb","sfx_yobstc")
//damaged
freeslot("sfx_yodmg1","sfx_yodmg2","sfx_yodmg3","sfx_yodmg4",
		"sfx_yodmga","sfx_yodmgb","sfx_yodmgc","sfx_yodmgd","sfx_yodmge")
//damagedself (hit yourself with your own orbi dumbass)
freeslot("sfx_yowtf1","sfx_yowtf2","sfx_yowtf3")
//hitconfirm
freeslot("sfx_yohit1","sfx_yohit2","sfx_yohit3","sfx_yohit4","sfx_yohit5",
		"sfx_yohita","sfx_yohitb","sfx_yohitc","sfx_yohitd")//,"sfx_yohite")
//generic item placement
freeslot("sfx_yoplc2","sfx_yoplc3","sfx_yoplc1",
		"sfx_yoplca","sfx_yoplcc")
//mine item placement
freeslot("sfx_yoplc3",
		"sfx_yoplcb")
//generic item thrown
freeslot("sfx_yoatk1","sfx_yoatk2","sfx_yoatk3",
		"sfx_yoatka","sfx_yoatkb")//,"sfx_yoatkc","sfx_yoatkd","sfx_yoatke")
//mine thrown
freeslot("sfx_yoatk3","sfx_yoatk4",
		"sfx_yoatkf","sfx_yoatkg","sfx_yoatkh")
//loss (| || || |-)
freeslot("sfx_yolse1","sfx_yolse2","sfx_yolse3","sfx_yolse4","sfx_yolse5")
//nearby same skin was hit
freeslot("sfx_yodwn1")
//nearmiss
freeslot("sfx_yomis1","sfx_yomis2","sfx_yomis3",
		"sfx_yomisa","sfx_yomisb")//,"sfx_yomisc")
//overtake
freeslot("sfx_yoovr1","sfx_yoovr2","sfx_yoovr3","sfx_yoovr4",
		"sfx_yoovra","sfx_yoovrb")
//being seeked by jawz
freeslot("sfx_yojwz1","sfx_yojwz2",
		"sfx_yojwza","sfx_yojwzb")
//being seeked by SPB
freeslot("sfx_yospb1","sfx_yospb2","sfx_yospb3",
		"sfx_yospba","sfx_yospbb")
//victory
freeslot("sfx_yowin1","sfx_yowin2","sfx_yowin3","sfx_yowin4")

//I almost forgot I needed to mess with the soundbank lol. Lots of little instances I needed to put together...
local soundbank = {
	YOboost = {sfx_yobst1,sfx_yobst2,sfx_yobst3},
	YOboost_S = {sfx_yobsta,sfx_yobstb,sfx_yobstc},

	YOdamage = {sfx_yodmg1,sfx_yodmg2,sfx_yodmg3,sfx_yodmg4},
	YOdamage_S = {sfx_yodmga,sfx_yodmgb,sfx_yodmgc,sfx_yodmgd,sfx_yodmge},
	YOdamageself = {sfx_yowtf1,sfx_yowtf2,sfx_yowtf3},

	YOhitconfirm = {sfx_yohit1,sfx_yohit2,sfx_yohit3,sfx_yohit4,sfx_yohit5},
	YOhitconfirm_S = {sfx_yohita,sfx_yohitb,sfx_yohitc,sfx_yohitd},//sfx_yohite},

	YOitemplaced = {sfx_yoplc2,sfx_yoplc3,sfx_yoplc1},
	YOitemplaced_S = {sfx_yoplca,sfx_yoplcc},
	YOmineplaced = {sfx_yoplc3},
	YOmineplaced_S = {sfx_yoplcb},

	YOitemthrown = {sfx_yoatk1,sfx_yoatk2,sfx_yoatk3},
	YOitemthrown_S = {sfx_yoatka,sfx_yoatkb},//sfx_yoatkc,sfx_yoatkd,sfx_yoatke},
	YOminethrown = {sfx_yoatk3,sfx_yoatk4},
	YOminethrown_S = {sfx_yoatkf,sfx_yoatkg,sfx_yoatkh},

	YOloss = {sfx_yolse1,sfx_yolse2,sfx_yolse3,sfx_yolse4,sfx_yolse5},
	YOnearbyyohit = {sfx_yodwn1},

	YOnearmiss = {sfx_yomis1,sfx_yomis2,sfx_yomis3},
	YOnearmiss_S = {sfx_yomisa,sfx_yomisb},//sfx_yomisc},

	YOovertake = {sfx_yoovr1,sfx_yoovr2,sfx_yoovr3,sfx_yoovr4},
	YOovertake_S = {sfx_yoovra,sfx_yoovrb},

	YOseekerjawz = {sfx_yojwz1,sfx_yojwz2},
	YOseekerjawz_S = {sfx_yojwza,sfx_yojwzb},
	YOseekerspb = {sfx_yospb1,sfx_yospb2,sfx_yospb3,sfx_yospba,sfx_yospbb},

	YOwin = {sfx_yowin1,sfx_yowin2,sfx_yowin3,sfx_yowin4}
	}

//the idea here is to try and make things somewhat more organized and use less upvalues
//whenever we want a sound of a specific group we'll just playrandomsound(soundbank[IDdamage])

//generic logging function, drawn upon by how tyron did it in friendmod, thanks pal
local function YOlog(p, msg)
	if not id_logging.value then return end
	if p and p.valid then
		print("\131"..leveltime.."\128: "..p.name.." - "..msg)
	else
		print("\131"..leveltime.."\128: "..msg)
	end
end
//initialize shit so I don't get nil errors out the ass
local function YOinit(p)
	p.YOstuff = {
		YOstresslevel = 0,
		YOchasedbyspb = false,
		YOchasedbyspbmo = nil,
		YOchasedbyjawz = false,
		YOchasedbyjawzmo = nil,
		YOlastposition = 0,
		YOlastitem = 0, //last item in box
		YOlastitemamount = 0, //last amount of items in box
		YOlastbuttons = 0, //last buttons held
		YOlastvoice = 0, //last leveltime we played a clip (some will bypass this)
		YOlastvoicelong = 0, //time remaining to play a low priority voiceclip like boost or overtake
		YOnearmisscooldown = 0, //how long before another object can near-miss you
		YOjawzchasetime = 0,
		YOspbeventdelay = 0, //delays the SPB chase event so the BEWWW doesn't drown it out
		YOlastsneakertimer = 0,
		YOstartedexiting = false //used to flag when someone wins/loses so the voice only plays once
		}
	p.YOinit = true
	YOlog(p,"YO init fired")
end

//why did I even make this its own function
local function stresscheck(p)
	return p.YOstuff.YOstresslevel >= STRESSED
end

local function randsound(tbl, pl, forced)
	//tbl - the table from soundbank we'll try to use, provided as a string (this will try to evaluate to the proper table)
	//pl - the player to play the sound
	//forced - if this will bypass needing to wait for voice cooldown
	//this will automatically check if pl is past stress threshold and try to use stress voices if available
	if not tbl then print("\133ID error: no table provided to randsound") end
	if not pl then print("\133ID error: no player provided to randsound") end
	if not forced and pl.YOstuff.YOlastvoice>0 then
		YOlog(pl,"aborted voice call on "..tbl.." - waiting for cooldown ("..pl.YOstuff.YOlastvoice.." remains)")
		return
	end
	local testtable = soundbank[tbl]
	local realtable = nil
	if not testtable then print("\133ID error: no table could be found at the entry: "..tbl) end
	if not stresscheck(pl) then
		realtable = testtable
	else
		testtable = tbl.."_S"
		testtable = soundbank[testtable]
		if testtable then
			realtable = testtable
		else
			realtable = soundbank[tbl]
		end
	end
	//print(lottery..": lottery % realtable is: "..lottery%#realtable)
	S_StartSound(pl.mo, realtable[(lottery%#realtable)+1] or realtable[1])
	pl.YOstuff.YOlastvoice = VOICEWAIT
	return
	//return tbl[(lottery%#tbl)+1] or tbl[1]
end

local function addstress(p, amt)
	if not p or not amt then return end
	p.YOstuff.YOstresslevel = $+amt
	return
end


//as ashnal has pointed out this could be made better, more efficient, indexed with tables, ect
//but I cannot give enough of a fuck to standardize all of this and I don't really care I just want this done
//if my guess is correct the lua interpreter is so bad anyways that performance gains would be marginal
local function YO_event(event, p)
	if not p then print("\133ID EVENT WITH NO PLAYER DIPSHIT") end
	if event then YOlog(p,"ID event activated: "..event) else print("\133 ID event error: no event") end
	if event == "damaged" then
		randsound("YOdamage", p, true)
		addstress(p,10*TICRATE)
	elseif event == "damagedself" then
		randsound("YOdamageself", p, true)
		addstress(p,10*TICRATE)
	elseif event == "nearbyidhit" then
		randsound("YOnearbyidhit", p)
		addstress(p,4*TICRATE)
	elseif event == "hitconfirm" then
		randsound("YOhitconfirm", p, true)
	elseif event == "nearmiss" then
		randsound("YOnearmiss", p)
		addstress(p,5*TICRATE)
		p.YOstuff.YOnearmisscooldown = NEARMISSCOOLDOWN
	elseif event == "nearmissnovoice" then //for bananas, they're not that dangerous to dodge
		addstress(p,3*TICRATE)
		p.YOstuff.YOnearmisscooldown = NEARMISSCOOLDOWN
	elseif event == "boost" then
		if p.YOstuff.YOlastvoicelong <= 0 then
			p.YOstuff.YOlastvoicelong = VOICEWAITLONG
			randsound("YOboost", p)
		end
		addstress(p,TICRATE)
	elseif event == "overtake" then
		if p.YOstuff.YOlastvoicelong <= 0 then
			p.YOstuff.YOlastvoicelong = VOICEWAITLONG
			randsound("YOovertake", p)
		end
	elseif event == "bumpplayer" then
		addstress(p,3*TICRATE)
	elseif event == "win" then
		randsound("YOwin", p, true)
	elseif event == "lose" then
		randsound("YOloss", p, true)
	elseif event == "throwgeneric" then
		randsound("YOitemthrown", p)
	elseif event == "placegeneric" then
		randsound("YOitemplaced", p)
	elseif event == "throwmine" then
		randsound("YOminethrown", p)
	elseif event == "placemine" then
		randsound("YOmineplaced", p)
	elseif event == "seekerspb" then
		randsound("YOseekerspb", p)
		addstress(p,MAXSTRESS)
	elseif event == "seekerjawz" then
		randsound("YOseekerjawz", p)
		addstress(p,7*TICRATE)
	end
	YOlog(p,"stress level now at "..p.YOstuff.YOstresslevel.." ["..p.YOstuff.YOstresslevel/TICRATE.."]")
end

addHook("ThinkFrame", do
	lottery = P_RandomByte()
	for p in players.iterate do
		if leveltime == 3 or not p.YOinit then
			YOinit(p)
		end
		if p.mo and p.mo.valid then
			nearmissbox = p.yocollider
			pmo = p.mo
			if pmo.skin ~= "yosuke" then
				if nearmissbox and nearmissbox.valid then
					P_RemoveMobj(nearmissbox)
					YOlog(p,"removed invalid collider")
					return
				end
			else
				if (nearmissbox == nil or (not nearmissbox.valid))
					YOlog(p,"generating new collider")
					p.yocollider = P_SpawnMobj(0,0,0,MT_YOCOLLIDER)
					nearmissbox = p.yocollider
					nearmissbox.lastapproach = 0
				end
				if p.YOstuff.YOstresslevel > 0 and not(p.kartstuff[k_spinouttimer]) then
					if p.YOstuff.YOstresslevel > MAXSTRESS then
						p.YOstuff.YOstresslevel = MAXSTRESS
					else
						p.YOstuff.YOstresslevel = $-1
					end
				end
				P_MoveOrigin(nearmissbox,pmo.x,pmo.y,pmo.z)
				//print("collider pos: "..idcollider.x..","..idcollider.y..","..idcollider.z)
				//print("player   pos: "..pmo.x..","..pmo.y..","..pmo.z)
				nearmissbox.scale = pmo.scale
				nearmissbox.target = pmo
				nearmissbox.flags2 = pmo.flags2
				nearmissbox.eflags = pmo.eflags

				if p.YOstuff.YOchasedbyspb then
					if (not(p.YOstuff.YOchasedbyspbmo and p.YOstuff.YOchasedbyspbmo.valid)) or
					(p.YOstuff.YOchasedbyspbmo.tracer and p.YOstuff.YOchasedbyspbmo.tracer.player and p.YOstuff.YOchasedbyspbmo.tracer.player~=p) or
					p.YOstuff.YOchasedbyspbmo.extravalue1 ~= 1 then
						p.YOstuff.YOchasedbyspb = false
						p.YOstuff.YOchasedbyspbmo = nil
					end
				end

				if p.YOstuff.YOchasedbyjawz then
					if (not(p.YOstuff.YOchasedbyjawzmo and p.YOstuff.YOchasedbyjawzmo.valid)) or
					(p.YOstuff.YOchasedbyjawzmo.tracer and p.YOstuff.YOchasedbyjawzmo.tracer.player and p.YOstuff.YOchasedbyjawzmo.tracer.player~=p) or
					p.YOstuff.YOchasedbyjawzmo.extravalue1 then
						p.YOstuff.YOchasedbyjawz = false
						p.YOstuff.YOchasedbyjawzmo = nil
						p.YOstuff.YOjawzchasetime = 0
					else
						if p.YOstuff.YOchasedbyjawzmo then
							p.YOstuff.YOjawzchasetime = $ + 1
							if p.YOstuff.YOjawzchasetime % 5*TICRATE == 0 then
								//long jawz chases generate continual events
								YO_event("seekerjawz",p)
							end
						end
					end
				end
				if not p.exiting then
					if leveltime > 15*TICRATE and p.kartstuff[k_position] < p.YOstuff.YOlastposition then YO_event("overtake", p) end
					if leveltime > 10*TICRATE and p.kartstuff[k_sneakertimer] > p.YOstuff.YOlastsneakertimer then YO_event("boost", p) end
				end
				//IDlog(p, "last buttons: "..p.IDstuff.IDlastbuttons.." cur buttons: "..p.cmd.buttons)
				//not (p.kartstuff[k_stolentimer] or p.kartstuff[k_spinouttimer])
				if (p.YOstuff.YOlastitem~=p.kartstuff[k_itemtype] or p.YOstuff.YOlastitemamount > p.kartstuff[k_itemamount]) and
				not (p.kartstuff[k_stolentimer] or p.kartstuff[k_spinouttimer]) and p.cmd.buttons & BT_ATTACK then
					//we've used an item holey shite!!!
					YOlog(p, "item use fired")
					if p.YOstuff.YOlastitem == KITEM_ORBINAUT or p.YOstuff.YOlastitem == KITEM_JAWZ or p.YOstuff.YOlastitem == KITEM_SPB or p.YOstuff.YOlastitem == KITEM_BALLHOG or
					(p.YOstuff.YOlastbuttons & BT_FORWARD and (p.YOstuff.YOlastitem == KITEM_BANANA or p.YOstuff.YOlastitem == KITEM_EGGMAN)) then
						YO_event("throwgeneric", p)
					elseif p.YOstuff.YOlastitem == KITEM_BANANA or p.YOstuff.YOlastitem == KITEM_EGGMAN then
						YO_event("placegeneric", p)
					elseif p.YOstuff.YOlastbuttons & BT_BACKWARD and p.YOstuff.YOlastitem == KITEM_MINE then
						YO_event("placemine", p)
					elseif p.YOstuff.YOlastitem == KITEM_MINE then
						YO_event("throwmine", p)
					end
				end
				if p.exiting and not p.YOstuff.YOstartedexiting then
					p.YOstuff.YOstartedexiting = true
					local totalp = 0
					for q in players.iterate
						if q and q.valid and q.mo and q.mo.valid and not q.spectator then
							totalp = $+1
						end
					end
					if p.kartstuff[k_position] <= totalp/2 or p.kartstuff[k_position] == 1 then YO_event("win",p, true) else YO_event("lose",p, true) end
				end
				if p.YOstuff.YOspbeventdelay > 0 then p.YOstuff.YOspbeventdelay = $-1 end
				if p.YOstuff.YOspbeventdelay == 1 and p.YOstuff.YOchasedbyspbmo then
					YO_event("seekerspb", p, true)
				end
				p.YOstuff.YOlastitem = p.kartstuff[k_itemtype]
				p.YOstuff.YOlastitemamount = p.kartstuff[k_itemamount]
				p.YOstuff.YOlastbuttons = p.cmd.buttons
				p.YOstuff.YOlastsneakertimer = p.kartstuff[k_sneakertimer]
				p.YOstuff.YOlastposition = p.kartstuff[k_position]
				if p.YOstuff.YOnearmisscooldown > 0 then p.YOstuff.YOnearmisscooldown = $-1 end
				if p.YOstuff.YOlastvoice > 0 then p.YOstuff.YOlastvoice = $-1 end
				if p.YOstuff.YOlastvoicelong > 0 then p.YOstuff.YOlastvoicelong = $-1 end
			end
		end
	end
end)


//quick function that returns distance between two things
local function quickdist(mo1, mo2)
	if not(mo1 and mo1.valid) then
		print("quickdist: mo1 is not valid")
		return 0
	end
	if not(mo2 and mo2.valid) then
		print("quickdist: mo2 is not valid")
		return 0
	end
	return abs(FixedHypot(FixedHypot(mo1.x-mo2.x,mo1.y-mo2.y),mo1.z-mo2.z))/FRACUNIT
end

local mobjtypes = {MT_ORBINAUT, MT_JAWZ, MT_JAWZ_DUD, MT_BANANA, MT_BALLHOG, MT_SINK, MT_SPB, MT_SSMINE, MT_MINE}
local function nearmisscheck(collider, mobj)
	//this is used to detect near-misses to the player
	//print("IDCollider fired a mobj collide")
	if leveltime > 5 and not collider or not collider.target then
		//print(leveltime..":\133 WARNING: Insurgency dude collider collided into something but has no parent mobj to reference.")
		return
	end
	if not ((collider.z >= mobj.z and collider.z < mobj.z + mobj.height)
        or (mobj.z >= collider.z and mobj.z < collider.z + collider.height)) then
		return //SERIOUSLY
	end
	local p = collider.target.player
	//print(leveltime..": mobj source: "..mobj.target.player.name.."; spawntime: "..mobj.spawnedtime.."; must exceed: "..mobj.spawnedtime+MOBJGRACETIME)
	if p and p.speed and p.speed < 20*FRACUNIT and mobj and mobj.speed and mobj.speed < 10*FRACUNIT then return end //can't nearmiss if its too slow, but this is arbitrary
	if mobj.target and mobj.target.player == p and ((mobj.spawnedtime and leveltime <= mobj.spawnedtime+MOBJGRACETIME) or mobj.type == MT_SSMINE) then return end
	if p.powers[pw_invulnerability] or p.powers[pw_flashing] or p.kartstuff[k_growshrinktimer] > 0 or p.kartstuff[k_squishedtimer] then return end
	for i=1,#mobjtypes
		if mobj.type ~= mobjtypes[i] then continue end
		//local dist = P_AproxDistance(P_AproxDistance(p.mo.x - mobj.x, p.mo.y - mobj.y),p.mo.z - mobj.z)/FRACUNIT
		local dist = quickdist(p.mo,mobj)
		if (not p.YOstuff.YOnearmisscooldown) and collider.lastapproach ~= 0 and collider.lastmobj and collider.lastmobj == mobj and collider.lastapproach < dist then
			if mobj.type == MT_BANANA then
				YO_event("nearmissnovoice",p)
			else
				YO_event("nearmiss",p)
			end
		end
		collider.lastmobj = mobj
		collider.lastapproach = dist
		//print(leveltime..": lastapproach changed to "..dist)
		break
	end
end

addHook("MobjThinker", function(mo)
	if not(mo and mo.valid) then return end
	if not mo.lastvalue1 then mo.lastvalue1 = 0 end
	if mo.extravalue1 == 1 and mo.tracer and mo.tracer.player then
		local pl = mo.tracer.player
		if pl.mo.skin == "yosuke" and not pl.YOstuff.YOchasedbyspb then
			pl.YOstuff.YOspbeventdelay = 3*TICRATE
			//ID_event("seekerspb",pl, true)
		end
		pl.YOstuff.YOchasedbyspb = true
		pl.YOstuff.YOchasedbyspbmo = mo
	end
	if mo.lastvalue1 == 0 and mo.extravalue1 == 1 and mo.tracer and mo.tracer.player and mo.tracer.player.skin == "yosuke" then
		pl.YOstuff.YOspbeventdelay = 3*TICRATE
		//ID_event("seekerspb",pl, true)
	end
end, MT_SPB)

addHook("MobjThinker", function(mo)
	if not(mo and mo.valid) or mo.extravalue1 or mo.YOthinked then return end
	mo.YOthinked = true
	local pl = mo.tracer.player
	if pl.mo.skin == "yosuke" then
		pl.YOstuff.YOchasedbyjawz = true
		pl.YOstuff.YOchasedbyjawzmo = mo
		YO_event("seekerjawz",pl)
	end
end, MT_JAWZ)

addHook("MobjCollide", nearmisscheck, MT_YOCOLLIDER)
addHook("MobjMoveCollide", nearmisscheck, MT_YOCOLLIDER)

addHook("MobjCollide", function(mobj1,mobj2)
	if mobj1 and mobj1.valid and mobj2 and mobj2.valid then
		if not ((mobj1.z >= mobj2.z and mobj1.z < mobj2.z + mobj2.height)
        or (mobj2.z >= mobj1.z and mobj2.z < mobj1.z + mobj1.height)) then
			return
		end
		if (mobj1.player and mobj1.player.kartstuff[k_hyudorotimer]) or
		(mobj2.player and mobj2.player.kartstuff[k_hyudorotimer]) then return end
		if mobj1.player and mobj1.player.skin == "yosuke" then YO_event("bumpplayer",mobj1.player) end
		if mobj2.player and mobj2.player.skin == "yosuke" then YO_event("bumpplayer",mobj2.player) end
	end
end, MT_PLAYER)

for i=1,#mobjtypes
	addHook("MobjThinker", function(mo)
		if mo.thinked then return end
		mo.spawnedtime = leveltime
		//print(leveltime..": new object spawned, must exceed "..leveltime+MOBJGRACETIME.." to become nearmissable")
		mo.thinked = true
	end, mobjtypes[i])
end

local function insurgencydudedamage(p, mo, pmo)
	if p and p.valid and p.mo and p.mo.valid and not p.spectator
		local plr = nil
		if pmo and pmo.player and pmo.player.valid then
			plr = pmo.player
		elseif mo and mo.valid and mo.target and mo.target.player then
			plr = mo.target.player
		end
		if p.mo.skin == "yosuke" and p.YOinit then
			if p == plr then YO_event("damagedself", p) else YO_event("damaged", p) end
			for q in players.iterate
				if not(q and q.valid and q.mo and q.mo.valid and (not q.spectator)) then continue end
				if q.mo.skin ~= "yosuke" then continue end
				if q == p then continue end
				YOlog(q, "fired a nearbyidhit event, relative positions: "..p.kartstuff[k_position].." vs "..q.kartstuff[k_position].." and relative distance is "..quickdist(q.mo, p.mo))
				if q.kartstuff[k_position] >= p.kartstuff[k_position] and quickdist(q.mo, p.mo) <= 2000 then
					YO_event("nearbyidhit", q)
					break
				end
			end
		end
		if p~=plr and plr and plr.mo.skin == "yosuke" then
			YO_event("hitconfirm", plr)
		end
	end
end

addHook("PlayerSpin", insurgencydudedamage)
addHook("PlayerExplode", insurgencydudedamage)
addHook("PlayerSquish", insurgencydudedamage)

local col = "\128"
hud.add(function(v, p, c)
	if not id_stressreadout.value then return end
	if p and p.valid and p.YOinit then
		col = "\128"
		if stresscheck(p) then
			col = "\133"
		elseif p.YOstuff.YOstresslevel > 0 then
			col = "\130"
		end
		v.drawString(50, 150, col.."STRESS: "..p.YOstuff.YOstresslevel.." ["..p.YOstuff.YOstresslevel/TICRATE.."]", 0, "center")
	end
end)