-- Neurotrauma human update functions
-- Hooks Lua event "think" to update and use for applying NT specific character data (its called 'c') with
-- values/functions defined here in NT.UpdateHuman, NT.LimbAfflictions and NT.Afflictions
NT.UpdateCooldown = 0
NT.UpdateInterval = 120
NT.Deltatime = NT.UpdateInterval / 60 -- Time in seconds that transpires between updates

Hook.Add("think", "NT.update", function()
	if HF.GameIsPaused() then
		return
	end

	NT.UpdateCooldown = NT.UpdateCooldown - 1
	if NT.UpdateCooldown <= 0 then
		NT.UpdateCooldown = NT.UpdateInterval
		NT.Update()
	end

	NT.TickUpdate()
end)

-- gets run once every two seconds
function NT.Update()
	local updateHumans = {}
	local amountHumans = 0
	local updateMonsters = {}
	local amountMonsters = 0

	-- fetchcharacters to update
	for key, character in pairs(Character.CharacterList) do
		if not character.IsDead then
			if character.IsHuman then
				table.insert(updateHumans, character)
				amountHumans = amountHumans + 1
			else
				table.insert(updateMonsters, character)
				amountMonsters = amountMonsters + 1
			end
		end
	end

	-- we spread the characters out over the duration of an update so that the load isnt done all at once
	for key, value in pairs(updateHumans) do
		-- make sure theyre still alive and human
		if value ~= nil and not value.Removed and value.IsHuman and not value.IsDead then
			Timer.Wait(function()
				if value ~= nil and not value.Removed and value.IsHuman and not value.IsDead then
					NT.UpdateHuman(value)
				end
			end, ((key + 1) / amountHumans) * NT.Deltatime * 1000)
		end
	end

	-- we spread the monsters out over the duration of an update so that the load isnt done all at once
	for key, value in pairs(updateMonsters) do
		-- make sure theyre still alive
		if value ~= nil and not value.Removed and not value.IsDead then
			Timer.Wait(function()
				if value ~= nil and not value.Removed and not value.IsDead then
					NT.UpdateMonster(value)
				end
			end, ((key + 1) / amountMonsters) * NT.Deltatime * 1000)
		end
	end
end

-- some local functions to avoid code duplicates
local function limbLockedInitial(c, limbtype, key)
	return not NTC.GetSymptomFalse(c.character, key)
		and (
			NTC.GetSymptom(c.character, key)
			or c.afflictions.t_paralysis.strength > 0
			or NT.LimbIsAmputated(c.character, limbtype)
			or (HF.GetAfflictionStrengthLimb(c.character, limbtype, "bandaged", 0) <= 0 and HF.GetAfflictionStrengthLimb(
				c.character,
				limbtype,
				"dirtybandage",
				0
			) <= 0 and NT.LimbIsDislocated(c.character, limbtype))
			or (
				HF.GetAfflictionStrengthLimb(c.character, limbtype, "gypsumcast", 0) <= 0
				and NT.LimbIsBroken(c.character, limbtype)
			)
		)
end
NT.organDamageCalc = function(c, damagevalue, nomaxstrength)
	if damagevalue >= 99 and (nomaxstrength == nil or nomaxstrength == false) then
		return 100
	end
	return damagevalue - 0.01 * c.stats.healingrate * c.stats.specificOrganDamageHealMultiplier * NT.Deltatime
end
local function kidneyDamageCalc(c, damagevalue)
	if damagevalue >= 99 then
		return 100
	end
	if damagevalue >= 50 then
		if damagevalue <= 51 then
			return damagevalue
		end
		return damagevalue - 0.01 * c.stats.healingrate * c.stats.specificOrganDamageHealMultiplier * NT.Deltatime
	end
	return damagevalue - 0.02 * c.stats.healingrate * c.stats.specificOrganDamageHealMultiplier * NT.Deltatime
end
local function isExtremity(type)
	return type ~= LimbType.Torso and type ~= LimbType.Head
end
local limbtypes = {
	LimbType.Torso,
	LimbType.Head,
	LimbType.LeftArm,
	LimbType.RightArm,
	LimbType.LeftLeg,
	LimbType.RightLeg,
}

-- define all the afflictions and their update functions
NT.Afflictions = {
	-- Arterial cuts
	t_arterialcut = {},
	-- Fractures and amputations
	t_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.Torso))
						* NT.Deltatime
			end
		end,
	},
	h_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.Head))
						* NT.Deltatime
			end
		end,
	},
	la_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.LeftArm))
						* NT.Deltatime
			end
		end,
	},
	ra_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.RightArm))
						* NT.Deltatime
			end
		end,
	},
	ll_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.LeftLeg))
						* NT.Deltatime
			end
		end,
	},
	rl_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.RightLeg))
						* NT.Deltatime
			end
		end,
	},
	n_fracture = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ 2
						* HF.BoolToNum(not HF.HasAfflictionLimb(c.character, "gypsumcast", LimbType.Head))
						* NT.Deltatime
			end
		end,
	},
	tla_amputation = {},
	tra_amputation = {},
	tll_amputation = {},
	trl_amputation = {},
	sla_amputation = {},
	sra_amputation = {},
	sll_amputation = {},
	srl_amputation = {},
	t_paralysis = {},
	alv = {}, -- artificial ventilation
	needlec = {
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - 0.15 * NT.Deltatime
		end,
	},
	forceprone = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and not c.character.IsClimbing
					and (
						NTC.GetSymptom(c.character, i)
						or (c.stats.lockleftleg and c.stats.lockrightleg and not c.stats.wheelchaired)
						or c.character.IsKeyDown(InputType.Attack)
					),
				2
			)
		end,
	},
	onwheelchair = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.stats.wheelchaired),
				2
			)
		end,
	},

	-- Organ conditions
	cardiacarrest = {
		update = function(c, i)
			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_cardiacarrest")
				and (
					NTC.GetSymptom(c.character, "triggersym_cardiacarrest")
					or c.stats.stasis
					or c.afflictions.heartremoved.strength > 0
					or c.afflictions.brainremoved.strength > 0
					or (c.afflictions.heartdamage.strength > 99 and HF.Chance(0.3))
					or (c.afflictions.traumaticshock.strength > 40 and HF.Chance(0.1))
					or (c.afflictions.coma.strength > 40 and HF.Chance(0.03))
					or (c.afflictions.hypoxemia.strength > 80 and HF.Chance(0.01))
					or (
						c.afflictions.fibrillation.strength > 20
						and HF.Chance((c.afflictions.fibrillation.strength / 100) ^ 4)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 10
			end
		end,
	},
	respiratoryarrest = {
		update = function(c, i)
			-- passive regen
			c.afflictions[i].strength = c.afflictions[i].strength
				- (0.05 + HF.BoolToNum(c.afflictions.sym_unconsciousness.strength < 0.1, 0.45)) * NT.Deltatime
			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_respiratoryarrest")
				and (
					NTC.GetSymptom(c.character, "triggersym_respiratoryarrest")
					or c.stats.stasis
					or c.afflictions.lungremoved.strength > 0
					or c.afflictions.brainremoved.strength > 0
					or c.afflictions.opiateoverdose.strength > 60
					or (c.afflictions.lungdamage.strength > 99 and HF.Chance(0.8))
					or (c.afflictions.traumaticshock.strength > 30 and HF.Chance(0.2))
					or (
						(c.afflictions.cerebralhypoxia.strength > 100 or c.afflictions.hypoxemia.strength > 70)
						and HF.Chance(0.05)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 10
			end
		end,
	},
	pneumothorax = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = HF.Clamp(
					c.afflictions[i].strength
						+ NT.Deltatime
							* (
								0.5 -- gain 0.5/s
								- HF.BoolToNum(c.afflictions[i].strength > 15)
									* HF.Clamp(c.afflictions.needlec.strength, 0, 1)
							), -- ...except if needled and >15%, then lose 0.5/s
					0,
					100
				)
			end
		end,
	},
	tamponade = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = c.afflictions[i].strength + NT.Deltatime * 0.5
			end

			if c.afflictions.heartremoved.strength > 0 then
				c.afflictions[i].strength = 0
			end
		end,
	},
	heartattack = {
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime

			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_heartattack")
				and not c.stats.stasis
				and c.afflictions.afstreptokinase.strength <= 0
				and c.afflictions.heartremoved.strength <= 0
				and (
					NTC.GetSymptom(c.character, "triggersym_heartattack")
					or (
						c.afflictions.bloodpressure.strength > 150
						and HF.Chance(
							NTConfig.Get("NT_heartattackChance", 1)
								* ((c.afflictions.bloodpressure.strength - 150) / 50 * 0.02)
						)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 50
			end

			if c.afflictions.heartremoved.strength > 0 then
				c.afflictions[i].strength = 0
			end
		end,
	},
	-- Organs removed
	brainremoved = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = 1
					+ HF.BoolToNum(HF.HasAfflictionLimb(c.character, "retractedskin", LimbType.Head, 99), 99)
			end
		end,
	},
	heartremoved = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = 1
					+ HF.BoolToNum(HF.HasAfflictionLimb(c.character, "retractedskin", LimbType.Torso, 99), 99)
			end
		end,
	},
	lungremoved = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = 1
					+ HF.BoolToNum(HF.HasAfflictionLimb(c.character, "retractedskin", LimbType.Torso, 99), 99)
			end
		end,
	},
	liverremoved = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = 1
					+ HF.BoolToNum(HF.HasAfflictionLimb(c.character, "retractedskin", LimbType.Torso, 99), 99)
			end
		end,
	},
	kidneyremoved = {
		update = function(c, i)
			if c.afflictions[i].strength > 0 then
				c.afflictions[i].strength = 1
					+ HF.BoolToNum(HF.HasAfflictionLimb(c.character, "retractedskin", LimbType.Torso, 99), 99)
			end
		end,
	},
	-- Organ damage
	cerebralhypoxia = {
		max = 200,
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			-- calculate new neurotrauma
			local gain = (
				-0.1 * c.stats.healingrate -- passive regen
				+ c.afflictions.hypoxemia.strength / 100 -- from hypoxemia
				+ HF.Clamp(c.afflictions.stroke.strength, 0, 20) * 0.1 -- from stroke
				+ c.afflictions.sepsis.strength / 100 * 0.4 -- from sepsis
				+ c.afflictions.liverdamage.strength / 800 -- from liverdamage
				+ c.afflictions.kidneydamage.strength / 1000 -- from kidneydamage
				+ c.afflictions.traumaticshock.strength / 100 -- from traumatic shock
			) * NT.Deltatime

			if gain > 0 then
				gain = gain
					* NTC.GetMultiplier(c.character, "neurotraumagain") -- NTC multiplier
					* NTConfig.Get("NT_neurotraumaGain", 1) -- Config multiplier
					* (1 - HF.Clamp(c.afflictions.afmannitol.strength, 0, 0.5)) -- half if mannitol
			end

			c.afflictions[i].strength = c.afflictions[i].strength + gain

			c.afflictions[i].strength = HF.Clamp(c.afflictions[i].strength, 0, 200)
		end,
	},
	heartdamage = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = NT.organDamageCalc(
				c,
				c.afflictions[i].strength
					+ NTC.GetMultiplier(c.character, "heartdamagegain")
						* (c.stats.neworgandamage + HF.Clamp(c.afflictions.heartattack.strength, 0, 0.5) * NT.Deltatime)
			)
		end,
	},
	lungdamage = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = NT.organDamageCalc(
				c,
				c.afflictions.lungdamage.strength
					+ NTC.GetMultiplier(c.character, "lungdamagegain")
						* (c.stats.neworgandamage + math.max(c.afflictions.radiationsickness.strength - 25, 0) / 800 * NT.Deltatime)
			)
		end,
	},
	liverdamage = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = NT.organDamageCalc(
				c,
				c.afflictions.liverdamage.strength
					+ NTC.GetMultiplier(c.character, "liverdamagegain") * c.stats.neworgandamage
			)
			if
				c.afflictions[i].strength >= 99
				and not NTC.GetSymptom(c.character, "sym_hematemesis")
				and HF.Chance(0.05)
			then
				-- if liver failed: 5% chance for 6-20 seconds of blood vomiting and internal bleeding
				NTC.SetSymptomTrue(c.character, "sym_hematemesis", math.random(3, 10))
				c.afflictions.internalbleeding.strength = c.afflictions.internalbleeding.strength + 2
			end
		end,
	},
	kidneydamage = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = kidneyDamageCalc(
				c,
				c.afflictions.kidneydamage.strength
					+ NTC.GetMultiplier(c.character, "kidneydamagegain")
						* (c.stats.neworgandamage + HF.Clamp((c.afflictions.bloodpressure.strength - 120) / 160, 0, 0.5) * NT.Deltatime * 0.5)
			)
			if
				c.afflictions[i].strength >= 60
				and not NTC.GetSymptom(c.character, "sym_vomiting")
				and HF.Chance((c.afflictions[i].strength - 60) / 40 * 0.07)
			then
				-- at 60% kidney damage: 0% chance for vomiting
				-- at 100% kidney damage: 7% chance for vomiting
				NTC.SetSymptomTrue(c.character, "sym_vomiting", math.random(3, 10))
			end
		end,
	},
	bonedamage = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = NT.organDamageCalc(
				c,
				c.afflictions.bonedamage.strength
					+ NTC.GetMultiplier(c.character, "bonedamagegain")
						* (c.afflictions.sepsis.strength / 500 + c.afflictions.hypoxemia.strength / 1000 + math.max(
							c.afflictions.radiationsickness.strength - 25,
							0
						) / 600)
						* NT.Deltatime
			)
			if c.afflictions[i].strength < 90 then
				c.afflictions[i].strength = c.afflictions[i].strength - (c.stats.bonegrowthCount * 0.3) * NT.Deltatime
			elseif c.stats.bonegrowthCount >= 6 then
				c.afflictions[i].strength = c.afflictions[i].strength - 2 * NT.Deltatime
			end
			if c.afflictions.kidneydamage.strength > 70 then
				c.afflictions[i].strength = c.afflictions[i].strength
					+ (c.afflictions.kidneydamage.strength - 70) / 30 * 0.15 * NT.Deltatime
			end
		end,
	},
	organdamage = {
		max = 200,
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength =
				NT.organDamageCalc(c, c.afflictions.organdamage.strength + c.stats.neworgandamage, true)
		end,
	},
	-- Blood
	sepsis = {
		update = function(c, i)
			if c.afflictions.afantibiotics.strength > 0.1 then
				c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime
			end
			if c.stats.stasis then
				return
			end
			if c.afflictions[i].strength > 0.1 then
				c.afflictions[i].strength = c.afflictions[i].strength + 0.05 * NT.Deltatime
			end
		end,
	},
	immunity = {
		default = -1,
		min = 5,
		update = function(c, i)
			if c.afflictions[i].strength == -1 then
				-- no immunity affliction!
				-- assume it has been wiped by "revive" or "heal all", attempt to assign new blood type
				if NT.HasBloodtype(c.character) then
					-- if blood type is already here, set immunity to the minimum
					c.afflictions[i].strength = 5
				else
					-- no bloodtype -> all afflictions have been cleared, set immunity to maximum
					c.afflictions[i].strength = 100
					NT.TryRandomizeBlood(c.character)
				end
			end
			if c.stats.stasis then
				return
			end

			-- immunity regeneration
			c.afflictions[i].strength =
				HF.Clamp(c.afflictions[i].strength + (0.5 + c.afflictions[i].strength / 100) * NT.Deltatime, 5, 100)
		end,
	},
	bloodloss = { max = 200 },
	bloodpressure = {
		min = 5,
		max = 200,
		default = 100,
		update = function(c, i)
			-- fix people not having a blood pressure
			if not HF.HasAffliction(c.character, i) then
				HF.SetAffliction(c.character, i, 100)
			end

			if c.stats.stasis then
				return
			end
			-- calculate new blood pressure
			local desiredbloodpressure = (
				c.stats.bloodamount
				- c.afflictions.tamponade.strength / 2 -- -50 if full tamponade
				- HF.Clamp(c.afflictions.afpressuredrug.strength * 5, 0, 45) -- -45 if blood pressure medication
				- HF.Clamp(c.afflictions.anesthesia.strength, 0, 15) -- -15 if propofol (fuck propofol)
				+ HF.Clamp(c.afflictions.afadrenaline.strength * 10, 0, 30) -- +30 if adrenaline
				+ HF.Clamp(c.afflictions.afsaline.strength * 5, 0, 30) -- +30 if saline
				+ HF.Clamp(c.afflictions.afringerssolution.strength * 5, 0, 30) -- +30 if ringers
			)
				* (1 + 0.5 * ((c.afflictions.liverdamage.strength / 100) ^ 2)) -- elevated if full liver damage
				* (1 + 0.5 * ((c.afflictions.kidneydamage.strength / 100) ^ 2)) -- elevated if full kidney damage
				* (1 + c.afflictions.alcoholwithdrawal.strength / 200) -- elevated if alcohol withdrawal
				* HF.Clamp((100 - c.afflictions.traumaticshock.strength * 2) / 100, 0, 1) -- none if half or more traumatic shock
				* ((100 - c.afflictions.fibrillation.strength) / 100) -- lowered if fibrillated
				* (1 - math.min(1, c.afflictions.cardiacarrest.strength)) -- none if cardiac arrest
				* NTC.GetMultiplier(c.character, "bloodpressure")

			local bloodpressurelerp = 0.2 * NTC.GetMultiplier(c.character, "bloodpressurerate")
			-- adjust three times slower to heightened blood pressure
			if desiredbloodpressure > c.afflictions.bloodpressure.strength then
				bloodpressurelerp = bloodpressurelerp / 3
			end
			c.afflictions.bloodpressure.strength = HF.Clamp(
				HF.Round(HF.Lerp(c.afflictions.bloodpressure.strength, desiredbloodpressure, bloodpressurelerp), 2),
				5,
				200
			)
		end,
	},
	hypoxemia = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			-- completely cancel out hypoxemia regeneration if penumothorax is full
			c.stats.availableoxygen = math.min(c.stats.availableoxygen, 100 - c.afflictions.pneumothorax.strength / 2)

			local hypoxemiagain = NTC.GetMultiplier(c.character, "hypoxemiagain")
			local regularHypoxemiaChange = (-c.stats.availableoxygen + 50) / 8
			if regularHypoxemiaChange > 0 then
				-- not enough oxygen, increase hypoxemia
				regularHypoxemiaChange = regularHypoxemiaChange * hypoxemiagain
			else
				-- enough oxygen, decrease hypoxemia
				regularHypoxemiaChange =
					HF.Lerp(regularHypoxemiaChange * 2, 0, HF.Clamp((50 - c.stats.bloodamount) / 50, 0, 1))
			end
			c.afflictions.hypoxemia.strength = HF.Clamp(
				c.afflictions.hypoxemia.strength
					+ (
							-math.min(0, (c.afflictions.bloodpressure.strength - 70) / 7) * hypoxemiagain -- loss because of low blood pressure (+10 at 0 bp)
							- math.min(0, (c.stats.bloodamount - 60) / 4) * hypoxemiagain -- loss because of low blood amount (+15 at 0 blood)
							+ regularHypoxemiaChange -- change because of oxygen in lungs (+6.25 <> -12.5)
						)
						* NT.Deltatime,
				0,
				100
			)
		end,
	},
	hemotransfusionshock = {},
	-- Other
	oxygenlow = {
		max = 200,
		update = function(c, i)
			-- respiratory arrest? -> oxygen in lungs rapidly decreases
			if c.afflictions.respiratoryarrest.strength > 0 then
				c.afflictions.oxygenlow.strength = c.afflictions.oxygenlow.strength + 15 * NT.Deltatime
			end
		end,
	},
	radiationsickness = {
		max = 200,
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime * 0.02
		end,
	},
	stasis = {},
	table = {},
	internalbleeding = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime * 0.02 * c.stats.clottingrate
			if c.afflictions[i].strength > 0 then
				c.afflictions.bloodloss.strength = c.afflictions.bloodloss.strength
					+ c.afflictions[i].strength * (1 / 40) * NT.Deltatime
			end
		end,
	},
	acidosis = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = c.afflictions[i].strength
				+ HF.BoolToNum(c.afflictions.hypoventilation.strength > 0 and c.afflictions.alv.strength <= 0.1) * 0.09 * NT.Deltatime
				+ HF.BoolToNum(
					(
						c.afflictions.cardiacarrest.strength
						+ c.afflictions.respiratoryarrest.strength * HF.BoolToNum(c.afflictions.alv.strength <= 0.1)
					) > 0
				) * 0.18 * NT.Deltatime
				+ math.max(0, c.afflictions.kidneydamage.strength - 80) / 20 * 0.1 * NT.Deltatime
				- NT.Deltatime * 0.03
		end,
	},
	alkalosis = {
		update = function(c, i)
			if not c.stats.stasis then
				c.afflictions[i].strength = c.afflictions[i].strength
					- NT.Deltatime * 0.03
					+ HF.Clamp(c.afflictions.hyperventilation.strength, 0, 1) * 0.09 * NT.Deltatime
					+ HF.Clamp(c.afflictions.sym_vomiting.strength, 0, 1) * 0.1 * NT.Deltatime
					+ HF.Clamp(HF.GetAfflictionStrength(c.character, "nausea", 0), 0, 1) * 0.1 * NT.Deltatime
			end
			if c.afflictions.acidosis.strength > 1 and c.afflictions.alkalosis.strength > 1 then
				local min = math.min(c.afflictions.acidosis.strength, c.afflictions.alkalosis.strength)
				c.afflictions.acidosis.strength = c.afflictions.acidosis.strength - min
				c.afflictions.alkalosis.strength = c.afflictions.alkalosis.strength - min
			end
		end,
	},
	seizure = {
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime

			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_seizure")
				and not c.stats.stasis
				and (
					NTC.GetSymptom(c.character, "triggersym_seizure")
					or (c.afflictions.stroke.strength > 1 and HF.Chance(0.05))
					or (c.afflictions.acidosis.strength > 60 and HF.Chance(0.05))
					or (c.afflictions.alkalosis.strength > 60 and HF.Chance(0.05))
					or HF.Chance(HF.Minimum(c.afflictions.radiationsickness.strength, 50, 0) / 200 * 0.1)
					or (c.afflictions.alcoholwithdrawal.strength > 50 and HF.Chance(
						c.afflictions.alcoholwithdrawal.strength / 1000
					))
					or (
						c.afflictions.opiateoverdose.strength > 60
						and HF.Chance(c.afflictions.opiateoverdose.strength / 500)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 10
			end

			-- check for spasm trigger
			if c.afflictions[i].strength > 0.1 then
				for type in limbtypes do
					if HF.Chance(0.5) then
						HF.AddAfflictionLimb(c.character, "spasm", type, 10)
					end
				end
			end
		end,
	},
	stroke = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = c.afflictions[i].strength - (1 / 20) * c.stats.clottingrate * NT.Deltatime

			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_stroke")
				and not c.stats.stasis
				and (
					NTC.GetSymptom(c.character, "triggersym_stroke")
					or (
						c.afflictions.bloodpressure.strength > 150
						and HF.Chance(
							NTConfig.Get("NT_strokeChance", 1)
								* (
									(c.afflictions.bloodpressure.strength - 150) / 50 * 0.02
									+ HF.Clamp(c.afflictions.afstreptokinase.strength, 0, 1) * 0.05
								)
						)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 5
			end
		end,
	},
	coma = {
		update = function(c, i)
			if c.stats.stasis then
				return
			end
			c.afflictions[i].strength = c.afflictions[i].strength - NT.Deltatime / 5

			-- triggers
			if
				not NTC.GetSymptomFalse(c.character, "triggersym_coma")
				and not c.stats.stasis
				and (
					NTC.GetSymptom(c.character, "triggersym_coma")
					or (c.afflictions.cardiacarrest.strength > 1 and HF.Chance(0.05))
					or (c.afflictions.stroke.strength > 1 and HF.Chance(0.05))
					or (
						c.afflictions.acidosis.strength > 60
						and HF.Chance(0.05 + (c.afflictions.acidosis.strength - 60) / 100)
					)
				)
			then
				c.afflictions[i].strength = c.afflictions[i].strength + 14
			end
		end,
	},
	stun = {
		max = 30,
		update = function(c, i)
			if c.afflictions.t_paralysis.strength > 0 or c.afflictions.anesthesia.strength > 15 then
				c.afflictions[i].strength = math.max(5, c.afflictions[i].strength)
			end
		end,
		apply = function(c, i, newval)
			-- using the character stun property to apply instead of an affliction so that the networking doesnt shit itself (hopefully)
			c.character.Stun = newval
		end,
	},
	slowdown = {
		lateupdate = function(c, i)
			c.afflictions[i].strength = HF.Clamp(100 * (1 - c.stats.speedmultiplier), 0, 100)
		end,
	},
	givein = {
		max = 1,
		update = function(c, i)
			c.afflictions[i].strength =
				HF.BoolToNum(c.afflictions.t_paralysis.strength > 0 or c.afflictions.sym_unconsciousness.strength > 0)
		end,
	},
	lockedhands = {
		update = function(c, i)
			-- arm locking
			local leftlockitem = c.character.Inventory.FindItemByIdentifier("armlock2", false)
			local rightlockitem = c.character.Inventory.FindItemByIdentifier("armlock1", false)

			-- handcuffs
			local handcuffs = c.character.Inventory.FindItemByIdentifier("handcuffs", false)
			local handcuffed = handcuffs ~= nil and c.character.Inventory.FindIndex(handcuffs) <= 6
			if handcuffed then
				-- drop non-handcuff items
				local leftHandItem = HF.GetItemInLeftHand(c.character)
				local rightHandItem = HF.GetItemInRightHand(c.character)
				if leftHandItem ~= nil and leftHandItem ~= handcuffs and leftlockitem == nil then
					leftHandItem.Drop(c.character)
				end
				if rightHandItem ~= nil and rightHandItem ~= handcuffs and rightlockitem == nil then
					rightHandItem.Drop(c.character)
				end
			end

			local leftarmlocked = leftlockitem ~= nil and not handcuffed
			local rightarmlocked = rightlockitem ~= nil and not handcuffed

			if leftarmlocked and not c.stats.lockleftarm then
				HF.RemoveItem(leftlockitem)
			end
			if rightarmlocked and not c.stats.lockrightarm then
				HF.RemoveItem(rightlockitem)
			end

			if not leftarmlocked and c.stats.lockleftarm then
				HF.ForceArmLock(c.character, "armlock2")
			end
			if not rightarmlocked and c.stats.lockrightarm then
				HF.ForceArmLock(c.character, "armlock1")
			end

			c.afflictions[i].strength = HF.BoolToNum((c.stats.lockleftarm and c.stats.lockrightarm) or handcuffed, 100)
		end,
	},
	traumaticshock = {
		update = function(c, i)
			local shouldReduce = (c.stats.sedated and c.afflictions.table.strength > 0)
				or c.afflictions.anesthesia.strength > 15
			c.afflictions[i].strength = c.afflictions[i].strength
				- (0.5 + HF.BoolToNum(shouldReduce, 1.5)) * NT.Deltatime

			if c.afflictions[i].strength > 5 and c.afflictions.sym_unconsciousness.strength < 0.1 then
				HF.AddAffliction(c.character, "shockpain", 10 * NT.Deltatime)
				HF.AddAffliction(c.character, "psychosis", c.afflictions[i].strength / 100 * NT.Deltatime)
			end
		end,
	},
	alcoholwithdrawal = {},
	opiatewithdrawal = {},
	chemwithdrawal = {},
	opiateoverdose = {},
	-- Drugs
	analgesia = { max = 200 },

	-- propofol (i hate it)
	anesthesia = {
		update = function(c, i)
			if c.afflictions[i].strength <= 0 then
				return
			end
			-- cause bloody vomiting or hallucinations sometimes (real sideeffects of propofol!)
			if HF.Chance(0.06) then
				local case = math.random()
				local casecount = 7

				if case < 1 / casecount then
					NTC.SetSymptomTrue(c.character, "sym_hematemesis", 5 + math.random() * 10)
				elseif case < 2 / casecount then
					NTC.SetSymptomTrue(c.character, "sym_blurredvision", 5 + math.random() * 10)
				elseif case < 3 / casecount then
					NTC.SetSymptomTrue(c.character, "sym_confusion", 5 + math.random() * 10)
				elseif case < 4 / casecount then
					NTC.SetSymptomTrue(c.character, "sym_fever", 5 + math.random() * 10)
				elseif case < 5 / casecount then
					NTC.SetSymptomTrue(c.character, "triggersym_seizure", 1 + math.random() * 2)
				elseif case < 6 / casecount then
					NT.Fibrillate(c.character, 5 + math.random() * 30)
				else
					HF.AddAffliction(c.character, "psychosis", 10)
				end
			end
		end,
	},
	drunk = { max = 200 },
	afadrenaline = {},
	afantibiotics = {},
	afthiamine = {},
	afsaline = {},
	afringerssolution = {},
	afstreptokinase = {},
	afmannitol = {},
	afpressuredrug = {
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - 0.25 * NT.Deltatime
		end,
	},
	concussion = {
		update = function(c, i)
			c.afflictions[i].strength = c.afflictions[i].strength - 0.01 * NT.Deltatime
			if c.afflictions[i].strength <= 0 then
				return
			end

			-- cause headaches, blurred vision, nausea, confusion
			if HF.Chance(HF.Clamp(c.afflictions[i].strength / 10 * 0.08, 0.02, 0.08)) then
				local case = math.random()

				if case < 0.25 then
					NTC.SetSymptomTrue(c.character, "sym_nausea", 5 + math.random() * 10)
				elseif case < 0.5 then
					NTC.SetSymptomTrue(c.character, "sym_blurredvision", 5 + math.random() * 9)
				elseif case < 0.75 then
					NTC.SetSymptomTrue(c.character, "sym_headache", 6 + math.random() * 8)
				else
					NTC.SetSymptomTrue(c.character, "sym_confusion", 6 + math.random() * 8)
				end
			end
		end,
	},

	-- /// Symptoms ///
	--==============================================================================
	sym_unconsciousness = {
		update = function(c, i)
			local isUnconscious = not NTC.GetSymptomFalse(c.character, i)
				and (
					NTC.GetSymptom(c.character, i)
					or c.stats.stasis
					or c.afflictions.brainremoved.strength > 0
					or (not HF.HasAffliction(c.character, "implacable", 0.05) and (c.character.Vitality <= 0 or c.afflictions.hypoxemia.strength > 80))
					or c.afflictions.cerebralhypoxia.strength > 100
					or c.afflictions.coma.strength > 15
					or c.afflictions.t_arterialcut.strength > 0
					or c.afflictions.seizure.strength > 0.1
					or c.afflictions.opiateoverdose.strength > 60
				)
			c.afflictions[i].strength = HF.BoolToNum(isUnconscious, 2)
			if isUnconscious then
				c.afflictions.stun.strength = math.max(7, c.afflictions.stun.strength)
			end
		end,
	},
	tachycardia = {
		update = function(c, i)
			-- harmless symptom (doesnt lead to fibrillation)
			local hasSymHarmless = not NTC.GetSymptomFalse(c.character, i)
				and c.afflictions.cardiacarrest.strength < 1
				and c.afflictions.heartremoved.strength < 1
				and (
					NTC.GetSymptom(c.character, i)
					or c.afflictions.sepsis.strength > 20
					or c.stats.bloodamount < 60
					or c.afflictions.acidosis.strength > 20
					or c.afflictions.pneumothorax.strength > 30
					or c.afflictions.afadrenaline.strength > 1
					or c.afflictions.alcoholwithdrawal.strength > 75
				)
			c.afflictions[i].strength = math.max(c.afflictions[i].strength, HF.BoolToNum(hasSymHarmless, 2))

			-- harmful symptom (leads to fibrillation and cardiac arrest)
			local fibrillationSpeed = -0.1
				+ HF.Clamp(c.afflictions.t_arterialcut.strength, 0, 2) -- aortic rupture (very fast)
				+ HF.Clamp(c.afflictions.acidosis.strength / 200, 0, 0.5) -- acidosis (slow)
				+ HF.Clamp(
						0.9
							- ( -- low blood pressure (varies)
								(
									c.afflictions.bloodpressure.strength
									+ HF.Clamp(c.afflictions.afpressuredrug.strength * 5, 0, 20) -- less fibrillation from low blood pressure if blood pressure reducing medicines active
								) / 90
							),
						0,
						1
					)
					* 2
				+ HF.Clamp(c.afflictions.hypoxemia.strength / 100, 0, 1) * 1.5 -- hypoxemia (varies)
				+ HF.Clamp((c.afflictions.traumaticshock.strength - 5) / 40, 0, 3) -- traumatic shock (fast)
				- HF.Clamp(c.afflictions.afadrenaline.strength, 0, 0.9) -- faster defib if adrenaline

			if fibrillationSpeed > 0 and c.afflictions.afadrenaline.strength > 0 then
				-- if adrenaline, fibrillate half as fast
				fibrillationSpeed = fibrillationSpeed / 2
			end

			if c.afflictions.cardiacarrest.strength > 0 or c.afflictions.heartremoved.strength > 0 then
				fibrillationSpeed = -1000
				c.afflictions.fibrillation.strength = 0
				c.afflictions[i].strength = 0
			end

			-- fibrillation multiplier
			if fibrillationSpeed > 0 then
				fibrillationSpeed = fibrillationSpeed
					* NTC.GetMultiplier(c.character, "fibrillation")
					* NTConfig.Get("NT_fibrillationSpeed", 1)
			end

			if c.afflictions.fibrillation.strength <= 0 then -- havent reached fibrillation yet
				c.afflictions[i].strength = c.afflictions[i].strength + fibrillationSpeed * 5 * NT.Deltatime
				-- we reached max tachycardia, switch over to fibrillation
				if c.afflictions[i].strength >= 100 then
					c.afflictions.fibrillation.strength = 5
					c.afflictions[i].strength = 0
				end
			else -- have reached fibrillation
				c.afflictions[i].strength = 0 -- set tachycardia to 0
				c.afflictions.fibrillation.strength = c.afflictions.fibrillation.strength
					+ fibrillationSpeed * NT.Deltatime
			end
		end,
	},
	fibrillation = {
		update = function(c, i)
			-- see above for vfib accumulation logic
			if
				NTC.GetSymptomFalse(c.character, i)
				or c.afflictions.cardiacarrest.strength >= 1
				or c.afflictions.heartremoved.strength >= 1
			then
				c.afflictions[i].strength = 0
			end
		end,
	},
	hyperventilation = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.respiratoryarrest.strength < 1
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.hypoxemia.strength > 10
						or c.afflictions.bloodpressure.strength < 80
						or c.afflictions.afadrenaline.strength > 1
						or c.afflictions.pneumothorax.strength > 15
						or c.afflictions.sepsis.strength > 15
					),
				2
			)
		end,
	},
	hypoventilation = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.respiratoryarrest.strength < 1
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.analgesia.strength > 20
						or c.afflictions.anesthesia.strength > 40
						or c.afflictions.opiateoverdose.strength > 30
					),
				2
			)
			if c.afflictions.hyperventilation.strength > 0 and c.afflictions.hypoventilation.strength > 0 then
				c.afflictions.hyperventilation.strength = 0
				c.afflictions.hypoventilation.strength = 0
			end
		end,
	},
	dyspnea = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.respiratoryarrest.strength <= 0
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.heartattack.strength > 1
						or c.afflictions.heartdamage.strength > 80
						or c.afflictions.hypoxemia.strength > 20
						or c.afflictions.lungdamage.strength > 45
						or c.afflictions.pneumothorax.strength > 40
						or c.afflictions.tamponade.strength > 10
						or (
							c.afflictions.hemotransfusionshock.strength > 0
							and c.afflictions.hemotransfusionshock.strength < 70
						)
					),
				2
			)
		end,
	},
	sym_cough = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and c.afflictions.lungremoved.strength <= 0
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.lungdamage.strength > 50
						or c.afflictions.heartdamage.strength > 50
						or c.afflictions.tamponade.strength > 20
					),
				2
			)
		end,
	},
	sym_paleskin = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.stats.bloodamount < 60
						or c.afflictions.bloodpressure.strength < 50
					),
				2
			)
		end,
	},
	sym_lightheadedness = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.afflictions.bloodpressure.strength < 60),
				2
			)
		end,
	},
	sym_blurredvision = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.afflictions.bloodpressure.strength < 55),
				2
			)
		end,
	},
	sym_confusion = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.acidosis.strength > 15
						or c.afflictions.bloodpressure.strength < 30
						or c.afflictions.hypoxemia.strength > 50
						or c.afflictions.sepsis.strength > 40
						or c.afflictions.alcoholwithdrawal.strength > 80
					),
				2
			)
		end,
	},
	sym_headache = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and not c.stats.sedated
					and (
						NTC.GetSymptom(c.character, i)
						or c.stats.bloodamount < 50
						or c.afflictions.acidosis.strength > 20
						or c.afflictions.stroke.strength > 1
						or c.afflictions.hypoxemia.strength > 40
						or c.afflictions.bloodpressure.strength < 60
						or c.afflictions.alcoholwithdrawal.strength > 50
						or c.afflictions.h_fracture.strength > 0
					),
				2
			)
		end,
	},
	sym_legswelling = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and HF.GetAfflictionStrength(c.character, "rl_cyber", 0) < 0.1
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.liverdamage.strength > 40
						or c.afflictions.kidneydamage.strength > 60
						or c.afflictions.heartdamage.strength > 80
					),
				2
			)
		end,
	},
	sym_weakness = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.tamponade.strength > 30
						or c.stats.bloodamount < 40
						or c.afflictions.acidosis.strength > 35
					),
				2
			)
		end,
	},
	sym_wheezing = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.respiratoryarrest.strength <= 0
					and (
						NTC.GetSymptom(c.character, i)
						or (
							c.afflictions.hemotransfusionshock.strength > 0
							and c.afflictions.hemotransfusionshock.strength < 90
						)
					),
				2
			)
		end,
	},
	sym_vomiting = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.drunk.strength > 100
						or (c.afflictions.hemotransfusionshock.strength > 0 and c.afflictions.hemotransfusionshock.strength < 40)
						or c.afflictions.alcoholwithdrawal.strength > 60
					),
				2
			)
		end,
	},
	sym_nausea = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.kidneydamage.strength > 60
						or c.afflictions.radiationsickness.strength > 80
						or (c.afflictions.hemotransfusionshock.strength > 0 and c.afflictions.hemotransfusionshock.strength < 90)
						or c.stats.withdrawal > 40
					),
				2
			)
		end,
	},
	sym_hematemesis = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (NTC.GetSymptom(c.character, i) or c.afflictions.internalbleeding.strength > 50),
				2
			)
		end,
	},
	fever = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.sepsis.strength > 5
						or c.afflictions.alcoholwithdrawal.strength > 90
					),
				2
			)
		end,
	},
	sym_abdomdiscomfort = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.afflictions.liverdamage.strength > 65),
				2
			)
		end,
	},
	sym_bloating = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (NTC.GetSymptom(c.character, i) or c.afflictions.liverdamage.strength > 50),
				2
			)
		end,
	},
	sym_jaundice = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (NTC.GetSymptom(c.character, i) or c.afflictions.liverdamage.strength > 80),
				2
			)
		end,
	},
	sym_sweating = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and (
						NTC.GetSymptom(c.character, i)
						or c.afflictions.heartattack.strength > 1
						or c.stats.withdrawal > 30
					),
				2
			)
		end,
	},
	sym_palpitations = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.cardiacarrest.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.afflictions.alkalosis.strength > 20),
				2
			)
		end,
	},
	sym_craving = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (NTC.GetSymptom(c.character, i) or c.stats.withdrawal > 20),
				2
			)
		end,
	},
	pain_abdominal = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and not c.stats.sedated
					and (
						NTC.GetSymptom(c.character, i)
						or (c.afflictions.hemotransfusionshock.strength > 0 and c.afflictions.hemotransfusionshock.strength < 80)
						or c.afflictions.t_arterialcut.strength > 0
					),
				2
			)
		end,
	},
	pain_chest = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(
				not NTC.GetSymptomFalse(c.character, i)
					and c.afflictions.sym_unconsciousness.strength <= 0
					and (
						NTC.GetSymptom(c.character, i)
						or (c.afflictions.hemotransfusionshock.strength > 0 and c.afflictions.hemotransfusionshock.strength < 60)
						or c.afflictions.t_fracture.strength > 0
						or c.afflictions.t_arterialcut.strength > 0
					),
				2
			)
		end,
	},
	luabotomy = {
		update = function(c, i)
			c.afflictions[i].strength = 0
		end,
	},
	modconflict = {
		update = function(c, i)
			c.afflictions[i].strength = HF.BoolToNum(NT.modconflict, 1)
		end,
	},
	-- Heelge: this really does the same thing as vanilla burns, why does it even exists?
	--sym_scorched = {
	--	update = function(c, i)
	--		c.afflictions[i].strength = HF.BoolToNum(c.stats.burndamage > 500, 10)
	--	end,
	--},
}
-- define all the limb specific afflictions and their update functions
NT.LimbAfflictions = {
	bandaged = {
		update = function(c, limbaff, i)
			-- turning a bandage into a dirty bandage
			local wounddamage = limbaff.burn.strength
				+ limbaff.lacerations.strength
				+ limbaff.gunshotwound.strength
				+ limbaff.bitewounds.strength
				+ limbaff.explosiondamage.strength

			local bandageDirtifySpeed = 0.1 + HF.Clamp(wounddamage / 100, 0, 0.4) + limbaff.bleeding.strength / 20

			if limbaff[i].strength > 0 then
				limbaff[i].strength = limbaff[i].strength - bandageDirtifySpeed * NT.Deltatime
				if limbaff[i].strength <= 0 then
					-- transition to dirty bandage
					limbaff.dirtybandage.strength = math.max(limbaff.dirtybandage.strength, 1)
					limbaff[i].strength = 0
				end
			end
			if limbaff.dirtybandage.strength > 0 then
				limbaff.dirtybandage.strength = limbaff.dirtybandage.strength + bandageDirtifySpeed * NT.Deltatime
			end

			-- bandage slowdown
			if limbaff[i].strength > 0 or limbaff.dirtybandage.strength > 0 then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.9
			end
		end,
	},
	dirtybandage = {}, -- for bandage dirtifaction logic see above
	iced = {
		update = function(c, limbaff, i, type)
			-- over time skin temperature goes up again
			if limbaff[i].strength > 0 then
				limbaff[i].strength = limbaff[i].strength - 1.7 * NT.Deltatime
			end
			-- iced slowdown
			if limbaff[i].strength > 0 then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.95
			end
		end,
	},
	gypsumcast = {
		update = function(c, limbaff, i, type)
			-- gypsum slowdown and fracture healing
			if limbaff[i].strength > 0 then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.8
				NT.BreakLimb(c.character, type, -(100 / 300) * NT.Deltatime)
			end
		end,
	},
	ointmented = {},
	bonegrowth = {
		update = function(c, limbaff, i, type)
			if limbaff[i].strength <= 0 then
				-- check for bone death fracture triggers
				if c.afflictions.bonedamage.strength > 90 and HF.Chance(0.01) then
					NT.BreakLimb(c.character, type)
				end
			end
		end,
	},
	arteriesclamp = {},
	-- damage
	bleeding = {
		update = function(c, limbaff, i)
			if limbaff[i].strength > 0 and math.abs(c.stats.clottingrate - 1) > 0.05 then
				limbaff[i].strength = limbaff[i].strength - (c.stats.clottingrate - 1) * 0.1 * NT.Deltatime
			end
		end,
	},
	burn = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 50 then
				limbaff[i].strength = limbaff[i].strength
					- (c.afflictions.immunity.prev / 3000 + HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	acidburn = {
		max = 200,
		update = function(c, limbaff, i)
			-- convert acid burns to regular burns
			if limbaff[i].strength > 0 then
				limbaff.burn.strength = limbaff.burn.strength + limbaff[i].strength
				limbaff[i].strength = 0
			end
		end,
	},
	lacerations = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 50 then
				limbaff[i].strength = limbaff[i].strength
					- (c.afflictions.immunity.prev / 3000 + HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	gunshotwound = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 50 then
				limbaff[i].strength = limbaff[i].strength
					- (c.afflictions.immunity.prev / 3000 + HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	bitewounds = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 100 then
				limbaff[i].strength = limbaff[i].strength
					- (c.afflictions.immunity.prev / 3000 + HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	explosiondamage = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 50 then
				limbaff[i].strength = limbaff[i].strength
					- (c.afflictions.immunity.prev / 3000 + HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	blunttrauma = {
		max = 200,
		update = function(c, limbaff, i)
			if limbaff[i].strength < 100 then
				limbaff[i].strength = limbaff[i].strength
					- (
							c.afflictions.immunity.prev / 8000
							+ HF.Clamp(limbaff.bandaged.strength, 0, 1) * 0.1
							+ HF.Clamp(limbaff.iced.strength, 0, 1) * 0.3
						)
						* c.stats.healingrate
						* NT.Deltatime
			end
		end,
	},
	internaldamage = {
		max = 200,
		update = function(c, limbaff, i, type)
			limbaff[i].strength = limbaff[i].strength
				+ (
						-HF.BoolToNum(limbaff[i].strength < 50, 0.05 * c.stats.healingrate)
						+ HF.BoolToNum(
							not c.stats.sedated
								and limbaff.gypsumcast.strength <= 0
								and (
									(
										NT.LimbIsBroken(c.character, type)
										and (
											HF.LimbIsExtremity(type)
											or (limbaff.bandaged.strength <= 0 and limbaff.dirtybandage.strength <= 0)
										)
									)
									or (
										NT.LimbIsDislocated(c.character, type)
										and limbaff.bandaged.strength <= 0
										and limbaff.dirtybandage.strength <= 0
									)
								),
							0.1
						)
					)
					* NT.Deltatime
		end,
	},
	-- other
	infectedwound = {
		update = function(c, limbaff, i)
			if c.stats.stasis then
				return
			end
			local infectindex = (
				-c.afflictions.immunity.prev / 200
				- HF.Clamp(limbaff.bandaged.strength, 0, 1) * 1.5
				- limbaff.ointmented.strength * 3
				+ limbaff.burn.strength / 20
				+ limbaff.lacerations.strength / 40
				+ limbaff.bitewounds.strength / 30
				+ limbaff.gunshotwound.strength / 40
				+ limbaff.explosiondamage.strength / 40
			) * NT.Deltatime

			local wounddamage = limbaff.burn.strength
				+ limbaff.lacerations.strength
				+ limbaff.gunshotwound.strength
				+ limbaff.bitewounds.strength
				+ limbaff.explosiondamage.strength

			-- open wounds and a dirty bandage? :grimacing:
			if limbaff.dirtybandage.strength > 10 and wounddamage > 5 then
				infectindex = infectindex + (wounddamage / 40 + limbaff.dirtybandage.strength / 20) * NT.Deltatime
			end

			if infectindex > 0 then
				infectindex = infectindex * NTConfig.Get("NT_infectionRate", 1)
			end

			limbaff[i].strength = limbaff[i].strength + infectindex / 5
			c.afflictions.immunity.strength = c.afflictions.immunity.strength - HF.Clamp(infectindex / 3, 0, 10)
		end,
	},
	foreignbody = {
		update = function(c, limbaff, i, type)
			if limbaff[i].strength < 15 then
				limbaff[i].strength = limbaff[i].strength - 0.05 * c.stats.healingrate * NT.Deltatime
			end

			-- check for arterial cut triggers and foreign body sepsis
			local foreignbodycutchance = ((HF.Minimum(limbaff[i].strength, 20) / 100) ^ 6) * 0.5
			if limbaff.bleeding.strength > 80 or HF.Chance(foreignbodycutchance) then
				NT.ArteryCutLimb(c.character, type)
			end

			-- sepsis
			local sepsischance = HF.Minimum(limbaff.gangrene.strength, 15, 0) / 400
				+ HF.Minimum(limbaff.infectedwound.strength, 50) / 1000
				+ foreignbodycutchance
			if HF.Chance(sepsischance) then
				c.afflictions.sepsis.strength = c.afflictions.sepsis.strength + NT.Deltatime
			end
		end,
	},
	gangrene = {
		update = function(c, limbaff, i, type)
			-- see foreignbody for sepsis chance
			if isExtremity(type) then
				-- surgical amputation prevents all gangrene on that stump
				if NT.LimbIsSurgicallyAmputated(c.character, type) then
					limbaff[i].strength = 0
					return
				end

				if limbaff[i].strength < 15 and limbaff[i].strength > 0 then
					limbaff[i].strength = limbaff[i].strength - 0.01 * c.stats.healingrate * NT.Deltatime
				end
				if c.afflictions.sepsis.strength > 5 then
					limbaff[i].strength = limbaff[i].strength
						+ HF.BoolToNum(HF.Chance(0.04), 0.5 + c.afflictions.sepsis.strength / 150)
							* NTConfig.Get("NT_gangrenespeed", 1)
							* NT.Deltatime
				end
				if limbaff.arteriesclamp.strength > 0 then
					limbaff[i].strength = limbaff[i].strength
						+ HF.BoolToNum(HF.Chance(0.1), 1) * 0.5 * NTConfig.Get("NT_gangrenespeed", 1) * NT.Deltatime
				end
			end
		end,
	},
	pain_extremity = {
		max = 10,
		update = function(c, limbaff, i, type)
			if c.afflictions.sym_unconsciousness.strength > 0 then
				limbaff[i].strength = 0
				return
			end
			limbaff[i].strength = limbaff[i].strength
				+ (
						-0.5
						+ HF.BoolToNum(
							type ~= LimbType.Torso
								and limbaff.gypsumcast.strength <= 0
								and (
									(
										NT.LimbIsBroken(c.character, type)
										and (
											HF.LimbIsExtremity(type)
											or (limbaff.bandaged.strength <= 0 and limbaff.dirtybandage.strength <= 0)
										)
									)
									or (
										NT.LimbIsDislocated(c.character, type)
										and limbaff.bandaged.strength <= 0
										and limbaff.dirtybandage.strength <= 0
									)
								),
							2
						)
						- HF.BoolToNum(c.stats.sedated, 100)
					)
					* NT.Deltatime
		end,
	},
	-- limb symptoms
	inflammation = {
		update = function(c, limbaff, i)
			limbaff[i].strength = limbaff[i].strength
				+ (
						-0.1
						+ HF.BoolToNum(limbaff.infectedwound.strength > 10 or limbaff.foreignbody.strength > 15, 0.15)
					)
					* NT.Deltatime
		end,
	},
	burn_deg1 = {
		update = function(c, limbaff, i)
			if limbaff.burn.strength < 1 or limbaff.burn.strength > 20 then
				limbaff[i].strength = 0
			else
				limbaff[i].strength = limbaff.burn.strength * 5
			end
		end,
	},
	burn_deg2 = {
		update = function(c, limbaff, i)
			if limbaff.burn.strength <= 20 or limbaff.burn.strength > 50 then
				limbaff[i].strength = 0
			else
				limbaff[i].strength = math.max(5, (limbaff.burn.strength - 20) / 30 * 100)
			end
		end,
	},
	burn_deg3 = {
		update = function(c, limbaff, i)
			if limbaff.burn.strength <= 50 then
				limbaff[i].strength = 0
			else
				limbaff[i].strength = HF.Clamp((limbaff.burn.strength - 50) / 50 * 100, 5, 100)
			end
		end,
	},
}
-- define the stats and multipliers
NT.CharStats = {
	healingrate = {
		getter = function(c)
			return NTC.GetMultiplier(c.character, "healingrate")
		end,
	},
	specificOrganDamageHealMultiplier = {
		getter = function(c)
			return NTC.GetMultiplier(c.character, "anyspecificorgandamage")
				+ HF.Clamp(c.afflictions.afthiamine.strength, 0, 1) * 4
		end,
	},
	neworgandamage = {
		getter = function(c)
			return (
				c.afflictions.sepsis.strength / 300
				+ c.afflictions.hypoxemia.strength / 400
				+ math.max(c.afflictions.radiationsickness.strength - 25, 0) / 400
			)
				* NTC.GetMultiplier(c.character, "anyorgandamage")
				* NTConfig.Get("NT_organDamageGain", 1)
				* NT.Deltatime
		end,
	},
	clottingrate = {
		getter = function(c)
			return HF.Clamp(1 - c.afflictions.liverdamage.strength / 100, 0, 1)
				* c.stats.healingrate
				* HF.Clamp(1 - c.afflictions.afstreptokinase.strength, 0, 1)
				* NTC.GetMultiplier(c.character, "clottingrate")
		end,
	},

	bloodamount = {
		getter = function(c)
			return HF.Clamp(100 - c.afflictions.bloodloss.strength, 0, 100)
		end,
	},
	stasis = {
		getter = function(c)
			return c.afflictions.stasis.strength > 0
		end,
	},
	sedated = {
		getter = function(c)
			return c.afflictions.analgesia.strength > 0
				or c.afflictions.anesthesia.strength > 10
				or c.afflictions.drunk.strength > 30
				or c.stats.stasis
		end,
	},
	withdrawal = {
		getter = function(c)
			return math.max(
				c.afflictions.opiatewithdrawal.strength,
				c.afflictions.chemwithdrawal.strength,
				c.afflictions.alcoholwithdrawal.strength
			)
		end,
	},
	availableoxygen = {
		getter = function(c)
			local res = HF.Clamp(c.character.Oxygen, 0, 100)
			-- heart isnt pumping blood? no new oxygen is getting into the bloodstream, no matter how oxygen rich the air in the lungs
			res = res * (1 - c.afflictions.fibrillation.strength / 100)
			-- and uuuh, maybe also dont let people without lungs use the oxygen where their lungs should be
			if c.afflictions.cardiacarrest.strength > 1 or c.afflictions.lungremoved.strength > 0.1 then
				res = 0
			end
			return res
		end,
	},
	speedmultiplier = {
		getter = function(c)
			local res = 1
			if c.afflictions.t_paralysis.strength > 0 then
				res = -9001
			end

			if c.afflictions.sym_vomiting.strength > 0 then
				res = res * 0.8
			end
			if c.afflictions.sym_nausea.strength > 0 then
				res = res * 0.9
			end
			if c.afflictions.anesthesia.strength > 0 then
				res = res * 0.5
			end
			if c.afflictions.opiateoverdose.strength > 50 then
				res = res * 0.5
			end

			if c.stats.withdrawal > 80 then
				res = res * 0.5
			elseif c.stats.withdrawal > 40 then
				res = res * 0.7
			elseif c.stats.withdrawal > 20 then
				res = res * 0.9
			end

			if c.afflictions.drunk.strength > 80 then
				res = res * 0.5
			elseif c.afflictions.drunk.strength > 40 then
				res = res * 0.7
			elseif c.afflictions.drunk.strength > 20 then
				res = res * 0.8
			end

			res = res + c.afflictions.afadrenaline.strength / 100 -- mitigate slowing effects if doped up on epinephrine

			res = res * NTC.GetSpeedMultiplier(c.character)

			return res
		end,
	},

	lockleftarm = {
		getter = function(c)
			return limbLockedInitial(c, LimbType.LeftArm, "lockleftarm")
		end,
	},
	lockrightarm = {
		getter = function(c)
			return limbLockedInitial(c, LimbType.RightArm, "lockrightarm")
		end,
	},
	lockleftleg = {
		getter = function(c)
			return limbLockedInitial(c, LimbType.LeftLeg, "lockleftleg")
		end,
	},
	lockrightleg = {
		getter = function(c)
			return limbLockedInitial(c, LimbType.RightLeg, "lockrightleg")
		end,
	},

	wheelchaired = {
		getter = function(c)
			local outerwearItem = c.character.Inventory.GetItemAt(4)
			local res = outerwearItem ~= nil and outerwearItem.Prefab.Identifier.Value == "wheelchair"
			if res then
				c.stats.lockleftleg = c.stats.lockleftarm
				c.stats.lockrightleg = c.stats.lockrightarm
			end
			-- leg and wheelchair slowdown
			if c.stats.lockleftleg or c.stats.lockrightleg or res then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.5
			end
			local isProne = c.stats.lockleftleg and c.stats.lockrightleg
			-- okay climbing ability
			if isProne and c.character.IsClimbing then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.5
			end
			-- moving prone with one arm or 95% slowdown when no arms
			if (isProne or res) and c.stats.lockleftarm and c.stats.lockrightarm then
				c.stats.speedmultiplier = 0.05
			elseif isProne and (c.stats.lockleftarm or c.stats.lockrightarm) then
				c.stats.speedmultiplier = c.stats.speedmultiplier * 0.8
			end
			-- if isProne then
			-- c.character.AnimController.RagdollParams.ColliderHeightFromFloor = 4.0
			-- end - Heelge: collider adjustment scrapped for now, lets wait for proper method in Workshop
			return res
		end,
	},

	bonegrowthCount = {
		getter = function(c)
			local res = 0
			for type in limbtypes do
				if HF.GetAfflictionStrengthLimb(c.character, type, "bonegrowth", 0) > 0 then
					res = res + 1
				end
			end
			return res
		end,
	},
	burndamage = {
		getter = function(c)
			local res = 0
			for type in limbtypes do
				res = res + HF.GetAfflictionStrengthLimb(c.character, type, "burn", 0)
			end
			return res
		end,
	},
}

function NT.UpdateHuman(character)
	-- pre humanupdate hooks
	for key, val in pairs(NTC.PreHumanUpdateHooks) do
		val(character)
	end

	local charData = { character = character, afflictions = {}, stats = {} }

	-- fetch all the current affliction data
	for identifier, data in pairs(NT.Afflictions) do
		local strength = HF.GetAfflictionStrength(character, identifier, data.default or 0)
		charData.afflictions[identifier] = { prev = strength, strength = strength }
	end
	-- fetch and calculate all the current stats
	for identifier, data in pairs(NT.CharStats) do
		if data.getter ~= nil then
			charData.stats[identifier] = data.getter(charData)
		else
			charData.stats[identifier] = data.default or 1
		end
	end
	-- update non-limb-specific afflictions
	for identifier, data in pairs(NT.Afflictions) do
		if data.update ~= nil then
			data.update(charData, identifier)
		end
	end

	-- update and apply limb specific stuff
	local function FetchLimbData(type)
		local keystring = tostring(type) .. "afflictions"
		charData[keystring] = {}
		for identifier, data in pairs(NT.LimbAfflictions) do
			local strength = HF.GetAfflictionStrengthLimb(character, type, identifier, data.default or 0)
			charData[keystring][identifier] = { prev = strength, strength = strength }
		end
	end
	local function UpdateLimb(type)
		local keystring = tostring(type) .. "afflictions"
		for identifier, data in pairs(NT.LimbAfflictions) do
			if data.update ~= nil then
				data.update(charData, charData[keystring], identifier, type)
			end
		end
	end
	local function ApplyLimb(type)
		local keystring = tostring(type) .. "afflictions"
		for identifier, data in pairs(charData[keystring]) do
			local newval = HF.Clamp(
				data.strength,
				NT.LimbAfflictions[identifier].min or 0,
				NT.LimbAfflictions[identifier].max or 100
			)
			if newval ~= data.prev then
				if NT.LimbAfflictions[identifier].apply == nil then
					HF.SetAfflictionLimb(character, identifier, type, newval)
				else
					NT.LimbAfflictions[identifier].apply(charData, identifier, type, newval)
				end
			end
		end
	end

	-- stasis completely halts activity in limbs
	if not charData.stats.stasis then
		for type in limbtypes do
			FetchLimbData(type)
		end
		for type in limbtypes do
			UpdateLimb(type)
		end
		for type in limbtypes do
			ApplyLimb(type)
		end
	end

	-- non-limb-specific late update (useful for things that use stats that are altered by limb specifics)
	for identifier, data in pairs(NT.Afflictions) do
		if data.lateupdate ~= nil then
			data.lateupdate(charData, identifier)
		end
	end

	-- apply non-limb-specific changes
	for identifier, data in pairs(charData.afflictions) do
		local newval =
			HF.Clamp(data.strength, NT.Afflictions[identifier].min or 0, NT.Afflictions[identifier].max or 100)
		if newval ~= data.prev then
			if NT.Afflictions[identifier].apply == nil then
				HF.SetAffliction(character, identifier, newval)
			else
				NT.Afflictions[identifier].apply(charData, identifier, newval)
			end
		end
	end

	-- compatibility
	NTC.TickCharacter(character)
	-- humanupdate hooks
	for key, val in pairs(NTC.HumanUpdateHooks) do
		val(character)
	end

	NTC.CharacterSpeedMultipliers[character] = nil
end

function NT.UpdateMonster(character)
	-- trade bloodloss on this creature for organ damage so that creatures can still bleed out
	local bloodloss = HF.GetAfflictionStrength(character, "bloodloss", 0)
	local oxygenlow = HF.GetAfflictionStrength(character, "oxygenlow", 0)
	if bloodloss > 0 then
		HF.AddAffliction(character, "organdamage", bloodloss * 2)
		HF.SetAffliction(character, "bloodloss", 0)
	elseif oxygenlow > 50 then
		HF.AddAffliction(character, "organdamage", (oxygenlow - 50) * 2)
		HF.SetAffliction(character, "oxygenlow", 50)
	end
end

-- gets run every tick, shouldnt be used unless necessary

function NT.TickUpdate()
	for key, value in pairs(NT.tickTasks) do
		value.duration = value.duration - 1
		if value.duration <= 0 then
			NT.tickTasks[key] = nil
		end
	end
end

NT.tickTasks = {}
NT.tickTaskID = 0
function NT.AddTickTask(type, duration, character)
	local newtask = {}
	newtask.type = type
	newtask.duration = duration
	newtask.character = character
	NT.tickTasks[NT.tickTaskID] = newtask
	NT.tickTaskID = NT.tickTaskID + 1
end
