<commit_before>//Revenants: based off of wraiths from Goon
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invsible
//Don't hear deadchat and are NOT normal ghosts
//Admin-spawn or random event

#define INVISIBILITY_REVENANT 30

/mob/living/simple_animal/revenant
	name = "revenant"
	desc = "A malevolent spirit."
	icon = 'icons/mob/mob.dmi'
	icon_state = "revenant_idle"
	incorporeal_move = 3
	invisibility = INVISIBILITY_REVENANT
	health = 25
	maxHealth = 25
	healable = 0
	see_invisible = INVISIBILITY_REVENANT
	languages = ALL
	response_help   = "passes through"
	response_disarm = "swings at"
	response_harm   = "punches through"
	unsuitable_atmos_damage = 0
	atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
	minbodytemp = 0
	maxbodytemp = INFINITY
	harm_intent_damage = 0
	friendly = "touches"
	status_flags = 0
	wander = 0
	density = 0
	flying = 1
	anchored = 1

	var/essence = 25 //The resource of revenants. Max health is equal to three times this amount
	var/essence_regen_cap = 25 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
	var/essence_regenerating = 1 //If the revenant regenerates essence or not; 1 for yes, 0 for no
	var/essence_regen_amount = 2 //How much essence regenerates
	var/essence_min = 1 //The minimum amount of essence a revenant can have; by default, it never drops below one
	var/essence_accumulated = 0 //How much essence the revenant has stolen
	var/revealed = 0 //If the revenant can take damage from normal sources.
	var/inhibited = 0 //If the revenant's abilities are blocked by a chaplain's power.
	var/essence_drained = 0 //How much essence the revenant has drained.
	var/draining = 0 //If the revenant is draining someone.
	var/list/drained_mobs = list() //Cannot harvest the same mob twice

/mob/living/simple_animal/revenant/Life()
	..()
	if(essence < essence_min)
		essence = essence_min
	if(essence_regenerating && !inhibited && essence < essence_regen_cap) //While inhibited, essence will not regenerate
		essence += essence_regen_amount
		if(essence > essence_regen_cap)
			essence = essence_regen_cap
	maxHealth = essence * 3
	if(!revealed)
		health = maxHealth //Heals to full when not revealed

/mob/living/simple_animal/revenant/ex_act(severity, target)
	return 1 //Immune to the effects of explosions.

/mob/living/simple_animal/revenant/blob_act()
	return //blah blah blobs aren't in tune with the spirit world, or something.

/mob/living/simple_animal/revenant/ClickOn(atom/A, params) //Copypaste from ghost code - revenants can't interact with the world directly.
	if(client.buildmode)
		build_click(src, client.buildmode, params, A)
		return

	var/list/modifiers = params2list(params)
	if(modifiers["middle"])
		MiddleClickOn(A)
		return
	if(modifiers["shift"])
		ShiftClickOn(A)
		return
	if(modifiers["alt"])
		AltClickOn(A)
		return
	if(modifiers["ctrl"])
		CtrlClickOn(A)
		return

	if(world.time <= next_move)
		return
	A.attack_ghost(src)
	if(ishuman(A) && in_range(src, A))
		Harvest(A)


/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
	if(!castcheck(0))
		return
	if(draining)
		src << "<span class='warning'>You are already siphoning the essence of a soul!</span>"
		return
	if(target in drained_mobs)
		src << "<span class='warning'>[target]'s soul is dead and empty.</span>"
		return
	if(!target.stat)
		src << "<span class='notice'>This being's soul is too strong to harvest.</span>"
		if(prob(10))
			target << "You feel as if you are being watched."
		return
	draining = 1
	essence_drained = 2
	src << "<span class='notice'>You search for the soul of [target].</span>"
	if(do_after(src, 10, 3, 0, target)) //did they get deleted in that second?
		if(target.ckey)
			src << "<span class='notice'>Their soul burns with intelligence.</span>"
			essence_drained += 2
		if(target.stat != DEAD)
			src << "<span class='notice'>Their soul blazes with life!</span>"
			essence_drained += 2
		else
			src << "<span class='notice'>Their soul is weak and faltering.</span>"
		if(do_after(src, 20, 6, 0, target)) //did they get deleted NOW?
			switch(essence_drained)
				if(1 to 2)
					src << "<span class='info'>[target] will not yield much essence. Still, every bit counts.</span>"
				if(3 to 4)
					src << "<span class='info'>[target] will yield an average amount of essence.</span>"
				if(5 to INFINITY)
					src << "<span class='info'>Such a feast! [target] will yield much essence to you.</span>"
			if(do_after(src, 30, 9, 0, target)) //how about now
				if(!target.stat)
					src << "<span class='warning'>They are now powerful enough to fight off your draining.</span>"
					target << "<span class='boldannounce'>You feel something tugging across your body before subsiding.</span>"
					draining = 0
					return //hey, wait a minute...
				src << "<span class='danger'>You begin siphoning essence from [target]'s soul.</span>"
				if(target.stat != DEAD)
					target << "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>"
				icon_state = "revenant_draining"
				reveal(65)
				stun(65)
				target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, their skin turning an ashy gray.</span>")
				target.Beam(src,icon_state="drain_life",icon='icons/effects/effects.dmi',time=60)
				if(target) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
					change_essence_amount(essence_drained * 5, 0, target)
					src << "<span class='info'>[target]'s soul has been considerably weakened and will yield no more essence for the time being.</span>"
					target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
										   "<span class='userdanger'>Violets lights, dancing in your vision, getting clo--</span>")
					drained_mobs.Add(target)
					target.death(0)
				icon_state = "revenant_idle"
			else
				src << "<span class='warning'>You are not close enough to siphon [target]'s soul. The link has been broken.</span>"
				draining = 0
				return
	draining = 0
	return


/mob/living/simple_animal/revenant/say(message)
	for(var/mob/M in mob_list)
		if(istype(M, /mob/living/simple_animal/revenant) || M.stat == DEAD)
			M << "<span class='deadsay'><b>REVENANT: [src]</b> says, \"[message]\"" //Can commune with the dead
	return

/mob/living/simple_animal/revenant/Stat()
	..()
	if(statpanel("Status"))
		stat(null, "Current essence: [essence]E")
		stat(null, "Stolen essence: [essence_accumulated]E")

/mob/living/simple_animal/revenant/New()
	..()
	spawn(5)
		if(src.mind)
			src.mind.remove_all_antag()
			src.mind.wipe_memory()<commit_msg>Fixes Revenant speech being heard in the lobby<commit_after>//Revenants: based off of wraiths from Goon
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invsible
//Don't hear deadchat and are NOT normal ghosts
//Admin-spawn or random event

#define INVISIBILITY_REVENANT 30

/mob/living/simple_animal/revenant
	name = "revenant"
	desc = "A malevolent spirit."
	icon = 'icons/mob/mob.dmi'
	icon_state = "revenant_idle"
	incorporeal_move = 3
	invisibility = INVISIBILITY_REVENANT
	health = 25
	maxHealth = 25
	healable = 0
	see_invisible = INVISIBILITY_REVENANT
	languages = ALL
	response_help   = "passes through"
	response_disarm = "swings at"
	response_harm   = "punches through"
	unsuitable_atmos_damage = 0
	atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
	minbodytemp = 0
	maxbodytemp = INFINITY
	harm_intent_damage = 0
	friendly = "touches"
	status_flags = 0
	wander = 0
	density = 0
	flying = 1
	anchored = 1

	var/essence = 25 //The resource of revenants. Max health is equal to three times this amount
	var/essence_regen_cap = 25 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
	var/essence_regenerating = 1 //If the revenant regenerates essence or not; 1 for yes, 0 for no
	var/essence_regen_amount = 2 //How much essence regenerates
	var/essence_min = 1 //The minimum amount of essence a revenant can have; by default, it never drops below one
	var/essence_accumulated = 0 //How much essence the revenant has stolen
	var/revealed = 0 //If the revenant can take damage from normal sources.
	var/inhibited = 0 //If the revenant's abilities are blocked by a chaplain's power.
	var/essence_drained = 0 //How much essence the revenant has drained.
	var/draining = 0 //If the revenant is draining someone.
	var/list/drained_mobs = list() //Cannot harvest the same mob twice

/mob/living/simple_animal/revenant/Life()
	..()
	if(essence < essence_min)
		essence = essence_min
	if(essence_regenerating && !inhibited && essence < essence_regen_cap) //While inhibited, essence will not regenerate
		essence += essence_regen_amount
		if(essence > essence_regen_cap)
			essence = essence_regen_cap
	maxHealth = essence * 3
	if(!revealed)
		health = maxHealth //Heals to full when not revealed

/mob/living/simple_animal/revenant/ex_act(severity, target)
	return 1 //Immune to the effects of explosions.

/mob/living/simple_animal/revenant/blob_act()
	return //blah blah blobs aren't in tune with the spirit world, or something.

/mob/living/simple_animal/revenant/ClickOn(atom/A, params) //Copypaste from ghost code - revenants can't interact with the world directly.
	if(client.buildmode)
		build_click(src, client.buildmode, params, A)
		return

	var/list/modifiers = params2list(params)
	if(modifiers["middle"])
		MiddleClickOn(A)
		return
	if(modifiers["shift"])
		ShiftClickOn(A)
		return
	if(modifiers["alt"])
		AltClickOn(A)
		return
	if(modifiers["ctrl"])
		CtrlClickOn(A)
		return

	if(world.time <= next_move)
		return
	A.attack_ghost(src)
	if(ishuman(A) && in_range(src, A))
		Harvest(A)


/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
	if(!castcheck(0))
		return
	if(draining)
		src << "<span class='warning'>You are already siphoning the essence of a soul!</span>"
		return
	if(target in drained_mobs)
		src << "<span class='warning'>[target]'s soul is dead and empty.</span>"
		return
	if(!target.stat)
		src << "<span class='notice'>This being's soul is too strong to harvest.</span>"
		if(prob(10))
			target << "You feel as if you are being watched."
		return
	draining = 1
	essence_drained = 2
	src << "<span class='notice'>You search for the soul of [target].</span>"
	if(do_after(src, 10, 3, 0, target)) //did they get deleted in that second?
		if(target.ckey)
			src << "<span class='notice'>Their soul burns with intelligence.</span>"
			essence_drained += 2
		if(target.stat != DEAD)
			src << "<span class='notice'>Their soul blazes with life!</span>"
			essence_drained += 2
		else
			src << "<span class='notice'>Their soul is weak and faltering.</span>"
		if(do_after(src, 20, 6, 0, target)) //did they get deleted NOW?
			switch(essence_drained)
				if(1 to 2)
					src << "<span class='info'>[target] will not yield much essence. Still, every bit counts.</span>"
				if(3 to 4)
					src << "<span class='info'>[target] will yield an average amount of essence.</span>"
				if(5 to INFINITY)
					src << "<span class='info'>Such a feast! [target] will yield much essence to you.</span>"
			if(do_after(src, 30, 9, 0, target)) //how about now
				if(!target.stat)
					src << "<span class='warning'>They are now powerful enough to fight off your draining.</span>"
					target << "<span class='boldannounce'>You feel something tugging across your body before subsiding.</span>"
					draining = 0
					return //hey, wait a minute...
				src << "<span class='danger'>You begin siphoning essence from [target]'s soul.</span>"
				if(target.stat != DEAD)
					target << "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>"
				icon_state = "revenant_draining"
				reveal(65)
				stun(65)
				target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, their skin turning an ashy gray.</span>")
				target.Beam(src,icon_state="drain_life",icon='icons/effects/effects.dmi',time=60)
				if(target) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
					change_essence_amount(essence_drained * 5, 0, target)
					src << "<span class='info'>[target]'s soul has been considerably weakened and will yield no more essence for the time being.</span>"
					target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
										   "<span class='userdanger'>Violets lights, dancing in your vision, getting clo--</span>")
					drained_mobs.Add(target)
					target.death(0)
				icon_state = "revenant_idle"
			else
				src << "<span class='warning'>You are not close enough to siphon [target]'s soul. The link has been broken.</span>"
				draining = 0
				return
	draining = 0
	return


/mob/living/simple_animal/revenant/say(message)
	for(var/mob/M in mob_list)
		if (istype(M, /mob/new_player))
			continue
		if(istype(M, /mob/living/simple_animal/revenant) || M.stat == DEAD)
			M << "<span class='deadsay'><b>REVENANT: [src]</b> says, \"[message]\"" //Can commune with the dead
	return

/mob/living/simple_animal/revenant/Stat()
	..()
	if(statpanel("Status"))
		stat(null, "Current essence: [essence]E")
		stat(null, "Stolen essence: [essence_accumulated]E")

/mob/living/simple_animal/revenant/New()
	..()
	spawn(5)
		if(src.mind)
			src.mind.remove_all_antag()
			src.mind.wipe_memory()
{'fraction_non_alphanumeric': 0.12415152178673089, 'fraction_numerical': 0.01196992920224801, 'mean_word_length': 4.091787439613527, 'pattern_counts': {'":': 0, '<': 163, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6343291', 'n_tokens_mistral': 5189, 'n_tokens_neox': 4546, 'n_words': 1406}