Please Come Save Me, I'm In A Lifepod Just Outside! (#22868)

<img width="592" height="283" alt="image"
src="https://github.com/user-attachments/assets/0a3f1e59-b0f7-47e8-9575-adc161fc12ba"
/>

Please come outside, I'd love to meet you. Leave your space suit, you
won't need it.

<img width="250" height="250" alt="image"
src="https://github.com/user-attachments/assets/f1866082-6360-4274-959a-a91336ac2df7"
/>
This commit is contained in:
VMSolidus
2026-07-25 13:53:55 +00:00
committed by GitHub
parent 2c91c8bba4
commit 90f676eb19
12 changed files with 376 additions and 214 deletions
+7 -3
View File
@@ -49,15 +49,19 @@
while (i <= num_groups)
var/group_size = rand(group_size_min, group_size_max)
for(var/j = 1, j <= group_size, j++)
if(prob(95))
if(prob(75))
var/basic_psiren
if (prob(50))
basic_psiren = new /mob/living/simple_animal/hostile/psiren(spawn_locations[i])
else basic_psiren = new /mob/living/simple_animal/hostile/psiren/ranged(spawn_locations[i])
spawned_psirens += WEAKREF(basic_psiren)
else
var/mob/living/simple_animal/hostile/psiren/omen/new_omen = new(spawn_locations[i])
spawned_psirens += WEAKREF(new_omen)
if (prob(75))
var/mob/living/simple_animal/hostile/psiren/omen/new_omen = new(spawn_locations[i])
spawned_psirens += WEAKREF(new_omen)
else
var/mob/living/simple_animal/hostile/psiren/omen/beckoner/new_beckoner = new(spawn_locations[i])
spawned_psirens += WEAKREF(new_beckoner)
CHECK_TICK
i++
@@ -1,6 +1,6 @@
/// Psiren "Lasher"
/mob/living/simple_animal/hostile/psiren
name = "Psiren"
name = "psiren lasher"
desc = "A strange, squid-like xenofauna. Its eye is full of malevolence."
icon = 'icons/mob/npc/psirens.dmi'
icon_state = "psiren_lasher"
@@ -10,10 +10,10 @@
blood_overlay_icon = 'icons/mob/npc/blood_overlay.dmi'
health = 65
maxhealth = 65
melee_damage_lower = 10
melee_damage_upper = 10
melee_damage_lower = 8
melee_damage_upper = 8
armor_penetration = 40
attack_flags = DAMAGE_FLAG_SHARP|DAMAGE_FLAG_EDGE|DAMAGE_FLAG_PSIONIC
attack_flags = DAMAGE_FLAG_EDGE|DAMAGE_FLAG_PSIONIC
destroy_surroundings = FALSE
break_stuff_probability = 0
attacktext = "lashes"
@@ -49,6 +49,7 @@
smart_melee = TRUE
smart_ranged = FALSE
/// Messages used by the psiren for its psychic damage effects.
var/list/cursed_messages = list("DRINK DEEPLY OF THE FINAL NIGHT.",
"YOUR LOVED ONES WILL NEVER KNOW OF YOUR JOY AGAIN.",
"LEAVE THIS PLACE THROUGH THE NEAREST AIRLOCK.",
@@ -80,6 +81,19 @@
"YOU ARE GUILTY OF YOUR FOREFATHERS SINS.",
"YOUR MISSION FAILED BEFORE IT BEGAN, THE PHORON YOU SEEK WAS NEVER REAL. YOUR NATION WILL DROWN FOR ITS WANT. IT'S ALL YOUR FAULT.")
/// How much damage the psiren's attacks deal directly to the victim's morale on a successful hit.
var/psi_damage = -0.1
/// How much extra psychic damage is dealt per point of psi-sensitivity.
var/psi_sensitivity_multiplier = -0.05
/// The chance per successful hit of triggering an ADPI message on the target.
var/adpi_chance = 5
/**
* Psirens have a unique ability to telepathically retaliate against anyone who tries to examine them.
* It only works if the psiren is alive, and the target isn't mindshielded.
*/
/mob/living/simple_animal/hostile/psiren/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
// If the victim has any psi-protection, we give them a completely normal examine proc.
if (stat == DEAD || user.is_psi_blocked(src, FALSE))
@@ -88,147 +102,22 @@
// Otherwise HIJACK IT.
return list(FONT_HUGE(SPAN_CULT(pick(cursed_messages))))
/datum/moodlet/psiren_psi_damage
// Descriptor and Initial logic is bespoke for this moodlet.
moodlet_descriptor = "residual psionic pressure."
initial_descriptor = null
duration = 15.0 MINUTES
var/list/cursed_messages = list("DRINK DEEPLY OF THE FINAL NIGHT.",
"YOUR LOVED ONES WILL NEVER KNOW OF YOUR JOY AGAIN.",
"LEAVE THIS PLACE THROUGH THE NEAREST AIRLOCK.",
"DEATH IS BUT THE FIRST DANCE IN ETERNITY.",
"YOU DESERVE ALL OF YOUR SUFFERING.",
"THE STARS HAVE FORGOTTEN YOUR NAME.",
"YOUR BONES REMEMBER CRIMES YOU NEVER COMMITTED.",
"EVERY GOD YOU HAVE EVER PRAYED TO IS DEAD.",
"THE UNIVERSE REGRETS YOUR CREATION.",
"YOU HAVE ALREADY FAILED.",
"YOU WILL NEVER WAKE UP FROM THIS.",
"YOUR SCREAMS ARRIVE BEFORE YOU MAKE THEM.",
"YOU WERE WARNED.",
"THE RIVERS RUN COLD WITH MEMORY.",
"THE LEMURIAN SEA HUNGERS.",
"THE LEMURIAN SEA HAS BECOME YOUR TOMB.",
"THERE IS A DRUMBEAT BELOW REALITY.",
"THE SUNSET LASTS FOREVER HERE.",
"YOU CANNOT HIDE INSIDE YOUR OWN HEAD.",
"I KNOW WHAT YOU DREAM ABOUT.",
"I AM WEARING YOUR VOICE.",
"YOU INVITED ME IN",
"THE SCC SENT YOU HERE AS A FEAST FOR GIBBERING MOUTHS. YOUR LEADERS SENT YOU HERE TO DIE.",
"YOUR MEMORY FURNISHES A REPAST FOR THE MATRIARCH.",
"THE NIGHT SHEDS A TEAR TO TELL YOU OF FEAR, OF SORROW, OF PAIN.",
"THE SCC HAS ALREADY VOIDED YOUR CONTRACT, THEY KNEW YOU WOULD DIE HERE.",
"I SAW YOU WHEN YOU THOUGHT YOU WERE ALONE.",
"THE BLOOD WILL NEVER WASH FROM YOUR HANDS.",
"YOU ARE GUILTY OF YOUR FOREFATHERS SINS.",
"YOUR MISSION FAILED BEFORE IT BEGAN, THE PHORON YOU SEEK WAS NEVER REAL. YOUR NATION WILL DROWN FOR ITS WANT. IT'S ALL YOUR FAULT.")
/datum/moodlet/psiren_psi_damage/get_moodlet_descriptor()
// This SHOULD exist but I'm doing my due diligence in checking.
var/mob/grandparent = astype(astype(morale_component?.resolve(), MORALE_COMPONENT)?.parent, /mob)
if (!grandparent)
return // No message, nobody to send a message to.
// Having any psi-protection will let you see the underlying moodlet.
if (grandparent.is_psi_blocked(null, FALSE))
/// Protection from the ranged attacks of other psirens.
/mob/living/simple_animal/hostile/psiren/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
var/mob/shooter = astype(hitting_projectile?.firer, /mob)
if (!shooter || shooter.faction != "psiren")
return ..()
var/spooky_message = pick(cursed_messages)
if (prob(10))
grandparent.play_screen_text(spooky_message, /atom/movable/screen/text/screen_text/adpi_message, COLOR_PURPLE)
return BULLET_ACT_BLOCK
return FONT_HUGE(SPAN_CULT(spooky_message))
/datum/moodlet/psiren_psi_damage/send_initial_description(datum/owner)
astype(astype(morale_component?.resolve(), MORALE_COMPONENT)?.parent, /mob)?.play_screen_text(pick(cursed_messages), /atom/movable/screen/text/screen_text/adpi_message, COLOR_PURPLE)
return
/**
* Special bullet type used for Psiren Darters
* Very low damage, but very high penetration. Resisted (or strengthened) by psi-sensitivity.
*/
/obj/projectile/bullet/pistol/psiren
name = "kinesis bolt"
damage = 8
armor_penetration = 40
sharp = 1
embed = FALSE
damage_flags = DAMAGE_FLAG_BULLET | DAMAGE_FLAG_PSIONIC
muzzle_type = /obj/effect/projectile/muzzle/laser/psiren
tracer_type = /obj/effect/projectile/tracer/laser/psiren
impact_type = /obj/effect/projectile/impact/laser/psiren
icon_state = "purple_laser"
/// How much damage this attack deals directly to the victim's morale on a successful hit.
var/psi_damage = -0.1
/// How much extra psychic damage is dealt per point of psi-sensitivity.
var/psi_sensitivity_multiplier = -0.05
/// The chance per successful hit of triggering an ADPI message on the target.
var/adpi_chance = 0
/obj/projectile/bullet/pistol/psiren/omen
damage = 3
armor_penetration = 60
psi_damage = -5.0
psi_sensitivity_multiplier = -2.5
adpi_chance = 5
/**
* Psiren ranged attacks deal a unique form of "psychic damage" which is governed by the full suite of rules regarding Psionic Sensitivity.
* This is in addition to their physical "Telekinetic" damage component. Being immune to the psychic damage does not make one immune to the physical damage.
* One can be immune to the psychic damage for a very large variety of reasons, not all of them static.
*
* "Psychic damage" attacks a character's Morale statistic rather than physical health, which inflicts gradually stacking penalties to Skill Tests.
*/
/obj/projectile/bullet/pistol/psiren/on_hit(atom/target, blocked, def_zone)
/// Psiren melee attacks deal psychic damage, just like their ranged attacks.
/mob/living/simple_animal/hostile/psiren/on_attack_mob(mob/hit_mob, obj/item/organ/external/limb)
. = ..()
if (. != BULLET_ACT_HIT || !firer)
return .
try_deal_psychic_damage(hit_mob, src, psi_damage, psi_sensitivity_multiplier, adpi_chance)
var/mob/living/victim = target
// Immunity from telepathy will fully negate the psychic damage (but not physical damage) component.
// This is getting checked before the morale_comp because I want to give Active Psi Protection a chance to give the player feedback that a psychic effect occurred.
if (!istype(victim) || victim.stat == DEAD || victim.is_psi_blocked(firer, FALSE))
return .
// A morale component is also required for psychic damage.
var/datum/component/morale/morale_comp = victim.GetComponent(MORALE_COMPONENT)
if (!morale_comp)
return .
var/victim_psi_sensitivity = victim.check_psi_sensitivity()
var/datum/moodlet/psi_damage_moodlet = morale_comp.load_moodlet(/datum/moodlet/psiren_psi_damage, FALSE)
psi_damage_moodlet.set_moodlet(psi_damage_moodlet.get_morale_modifier() + psi_damage + min(0, (victim_psi_sensitivity * psi_sensitivity_multiplier)))
psi_damage_moodlet.refresh_moodlet()
if (!prob(adpi_chance) || !SShallucinations)
return .
// At this point in time all checks that would be satisfied by the ADPI subsystem have already been handled,
// now we just need to fetch a message and send one.
var/adpi_message = SShallucinations.pick_adpi_message(victim, TRUE)
if (!adpi_message)
return .
SShallucinations.deliver_adpi_message(victim, adpi_message)
/obj/effect/projectile/muzzle/laser/psiren
icon_state = "muzzle_scc" // I just like the pattern here
light_color = COLOR_VIOLET
/obj/effect/projectile/tracer/laser/psiren
icon_state = "beam_scc"
light_color = COLOR_VIOLET
/obj/effect/projectile/impact/laser/psiren
icon_state = "impact_scc" // I also just like the pattern here
light_color = COLOR_VIOLET
/// Psiren "Darter"
/// Psiren "Darter", a simple ranged attacker with weak damage, high penetration, and weak psychic damage.
/mob/living/simple_animal/hostile/psiren/ranged
name = "psiren darter"
icon_state = "psiren_darter"
icon_living = "psiren_darter"
icon_dead = "psiren_darter_dead"
@@ -236,79 +125,12 @@
projectilesound = 'sound/weapons/wave.ogg'
projectiletype = /obj/projectile/bullet/pistol/psiren
/// Smoke effect used for the Omen.
/datum/effect/effect/system/smoke_spread/psiren
smoke_type = /obj/effect/smoke/psiren
smoke_duration = 20 SECONDS
/// "Ink Cloud" colored smoke effect.
/obj/effect/smoke/psiren
color = "#21093a"
time_to_live = 20 SECONDS
/**
* Psiren "Omen", a support-caster type enemy that primarily inflicts direct psychic damage.
* Relatively squishy for a medium enemy, they can react exactly once per being shot with a cloud of violet smoke.
*/
/mob/living/simple_animal/hostile/psiren/omen
icon_state = "psiren_omen"
icon_living = "psiren_omen"
icon_dead = "psiren_omen_dead"
health = 120
maxhealth = 120
ranged = TRUE
projectilesound = 'sound/weapons/wave.ogg'
projectiletype = /obj/projectile/bullet/pistol/psiren/omen
var/has_ink = TRUE
/mob/living/simple_animal/hostile/psiren/omen/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
if (has_ink)
var/datum/effect/effect/system/smoke_spread/psiren/S = new/datum/effect/effect/system/smoke_spread/psiren()
S.set_up(5, 0, loc, null)
S.start()
has_ink = FALSE
return ..()
/mob/living/simple_animal/hostile/psiren/matriarch
name = "Matriarch"
icon = 'icons/mob/npc/psiren_matriarch.dmi'
icon_state = "psiren_matriarch"
icon_living = "psiren_matriarch"
icon_dead = "psiren_matriarch_dead"
health = 9999
maxhealth = 9999
// Speaks via Xenoglossia.
universal_speak = TRUE
universal_understand = TRUE
mob_size = MOB_LARGE
mob_weight = MOB_WEIGHT_SUPERHEAVY
melee_damage_lower = 35
melee_damage_upper = 40
melee_reach = 2
armor_penetration = 30
meat_amount = 5
meat_type = /obj/item/reagent_containers/food/snacks/squidmeat/psiren_tentacle_meat
butchering_products = list(/obj/item/reagent_containers/food/snacks/squidmeat/psiren_body_meat = 5)
/mob/living/simple_animal/hostile/psiren/matriarch/Initialize()
. = ..()
add_spell(new /spell/targeted/eject_ink_cloud, "const_spell_ready")
/spell/targeted/eject_ink_cloud
name = "Eject Ink Cloud"
desc = "Eject a cloud of Lemurian fog."
feedback = "CEIB"
range = 0
spell_flags = INCLUDEUSER
charge_max = 30 SECONDS
max_targets = 1
invocation_type = SpI_EMOTE
invocation = "ejects a cloud of Lemurian fog."
hud_state = "cloud"
/spell/targeted/eject_ink_cloud/cast(mob/target, mob/living/user as mob)
..()
var/datum/effect/effect/system/smoke_spread/psiren/S = new/datum/effect/effect/system/smoke_spread/psiren()
S.set_up(15, 0, user.loc, null)
S.start()
return TRUE
@@ -0,0 +1,81 @@
/**
* A larger and older variant of an Omen. The Beckoner is a tanky medium support enemy.
* They share their attack and defense pattern with the Omen.
* But also gain a periodic psychic damage aura.
*/
/mob/living/simple_animal/hostile/psiren/omen/beckoner
name = "psiren beckoner"
icon = 'icons/mob/npc/psiren_beckoner.dmi'
icon_state = "psiren_beckoner"
icon_living = "psiren_beckoner"
icon_dead = "psiren_beckoner_dead"
health = 300
maxhealth = 300
ranged = TRUE
projectilesound = 'sound/weapons/wave.ogg'
projectiletype = /obj/projectile/bullet/pistol/psiren/omen
psi_damage = -5.0
psi_sensitivity_multiplier = -2.5
adpi_chance = 20
cursed_messages = list(
"Please help me, I'm trapped outside the airlocks!",
"You feel drawn toward the nearest airlock.",
"Fresh air... no, vacuum... feels right somehow.",
"The void feels strangely comforting.",
"Please come save me, I'm in a lifepod just outside!",
"The ship feels unbearably cramped.",
"You need fresh air, go for a walk outside.",
"You don't need a space suit.",
"Did someone just knock on the hull?",
"I left something outside. I should go get it.",
"Come outside.",
"The nearest airlock suddenly seems very important.",
"Someone is calling for help outside!")
/// Next scheduled time to fire a "beckon" pulse.
var/next_aura_pulse
/// Minimum time between beckoning pulses.
var/min_pulse_time = 3 MINUTES
/// Maximum time between beckoning pulses.
var/max_pulse_time = 9 MINUTES
/// Maximum radial distance at which the Omen can hit a target with a psi damage pulse.
var/pulse_range_meters = 50 // Meters
/// Base psi-damage dealt per pulse. This falls off with radial distance.
var/aura_psi_damage = -2.5
/mob/living/simple_animal/hostile/psiren/omen/beckoner/Initialize()
. = ..()
next_aura_pulse = REALTIMEOFDAY + rand(min_pulse_time, max_pulse_time)
hunting_pulse()
/mob/living/simple_animal/hostile/psiren/omen/beckoner/Life(seconds_per_tick, times_fired)
. = ..()
if (stat == DEAD || next_aura_pulse > REALTIMEOFDAY)
return
next_aura_pulse = REALTIMEOFDAY + rand(min_pulse_time, max_pulse_time)
hunting_pulse()
/mob/living/simple_animal/hostile/psiren/omen/beckoner/proc/hunting_pulse()
// TODO: Really should port tg's handling of per-z-level mob lists.
for (var/mob/player as anything in GLOB.player_list)
if (!player || z != player.z || player.stat == DEAD)
continue
var/dx = player.x - x
var/dy = player.y - y
var/r = sqrt((dx * dx) + (dy * dy))
if (r > pulse_range_meters)
continue
try_deal_psychic_damage(player,
src,
r <= 1 ? aura_psi_damage : aura_psi_damage / r,
r <= 1 ? psi_sensitivity_multiplier : psi_sensitivity_multiplier / r,
100,
/datum/moodlet/psiren_psi_damage/beckoner,
pick(cursed_messages))
@@ -0,0 +1,45 @@
/mob/living/simple_animal/hostile/psiren/matriarch
name = "Matriarch"
icon = 'icons/mob/npc/psiren_matriarch.dmi'
icon_state = "psiren_matriarch"
icon_living = "psiren_matriarch"
icon_dead = "psiren_matriarch_dead"
health = 9999
maxhealth = 9999
// Speaks via Xenoglossia.
universal_speak = TRUE
universal_understand = TRUE
mob_size = MOB_LARGE
mob_weight = MOB_WEIGHT_SUPERHEAVY
melee_damage_lower = 35
melee_damage_upper = 40
melee_reach = 2
armor_penetration = 30
meat_amount = 5
meat_type = /obj/item/reagent_containers/food/snacks/squidmeat/psiren_tentacle_meat
butchering_products = list(/obj/item/reagent_containers/food/snacks/squidmeat/psiren_body_meat = 5)
/mob/living/simple_animal/hostile/psiren/matriarch/Initialize()
. = ..()
add_spell(new /spell/targeted/eject_ink_cloud, "const_spell_ready")
/spell/targeted/eject_ink_cloud
name = "Eject Ink Cloud"
desc = "Eject a cloud of Lemurian fog."
feedback = "CEIB"
range = 0
spell_flags = INCLUDEUSER
charge_max = 30 SECONDS
max_targets = 1
invocation_type = SpI_EMOTE
invocation = "ejects a cloud of Lemurian fog."
hud_state = "cloud"
/spell/targeted/eject_ink_cloud/cast(mob/target, mob/living/user as mob)
..()
var/datum/effect/effect/system/smoke_spread/psiren/S = new/datum/effect/effect/system/smoke_spread/psiren()
S.set_up(15, 0, user.loc, null)
S.start()
return TRUE
@@ -0,0 +1,26 @@
/**
* Psiren "Omen", a ranged support-caster type enemy.
* Their attacks deal almost no physical damage at all, but very high psychic damage.
* Relatively squishy for a medium enemy, they can react exactly once per being shot with a cloud of violet smoke.
*/
/mob/living/simple_animal/hostile/psiren/omen
name = "psiren omen"
icon_state = "psiren_omen"
icon_living = "psiren_omen"
icon_dead = "psiren_omen_dead"
health = 120
maxhealth = 120
ranged = TRUE
projectilesound = 'sound/weapons/wave.ogg'
projectiletype = /obj/projectile/bullet/pistol/psiren/omen
var/has_ink = TRUE
/mob/living/simple_animal/hostile/psiren/omen/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if (. == BULLET_ACT_BLOCK || !has_ink)
return
var/datum/effect/effect/system/smoke_spread/psiren/S = new/datum/effect/effect/system/smoke_spread/psiren()
S.set_up(5, 0, loc, null)
S.start()
has_ink = FALSE
@@ -0,0 +1,62 @@
/**
* Special bullet type used for Psiren Darters
* Very low damage, but very high penetration. Resisted (or strengthened) by psi-sensitivity.
*/
/obj/projectile/bullet/pistol/psiren
name = "kinesis bolt"
damage = 4
armor_penetration = 40
sharp = 1
embed = FALSE
damage_flags = DAMAGE_FLAG_BULLET | DAMAGE_FLAG_PSIONIC
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSRAILING
muzzle_type = /obj/effect/projectile/muzzle/laser/psiren
tracer_type = /obj/effect/projectile/tracer/laser/psiren
impact_type = /obj/effect/projectile/impact/laser/psiren
icon_state = "purple_laser"
/// How much damage this attack deals directly to the victim's morale on a successful hit.
var/psi_damage = -0.1
/// How much extra psychic damage is dealt per point of psi-sensitivity.
var/psi_sensitivity_multiplier = -0.05
/// The chance per successful hit of triggering an ADPI message on the target.
var/adpi_chance = 0
/**
* Variant psiren bullet type for Omens.
* Almost no physical damage at all, but very high psychic damage.
*/
/obj/projectile/bullet/pistol/psiren/omen
damage = 1
armor_penetration = 60
psi_damage = -5.0
psi_sensitivity_multiplier = -2.5
adpi_chance = 5
/**
* Psiren ranged attacks deal a unique form of "psychic damage" which is governed by the full suite of rules regarding Psionic Sensitivity.
* This is in addition to their physical "Telekinetic" damage component. Being immune to the psychic damage does not make one immune to the physical damage.
* One can be immune to the psychic damage for a very large variety of reasons, not all of them static.
*
* "Psychic damage" attacks a character's Morale statistic rather than physical health, which inflicts gradually stacking penalties to Skill Tests.
*/
/obj/projectile/bullet/pistol/psiren/on_hit(atom/target, blocked, def_zone)
. = ..()
if (. != BULLET_ACT_HIT || !firer)
return
try_deal_psychic_damage(target, firer, psi_damage, psi_sensitivity_multiplier, adpi_chance)
/obj/effect/projectile/muzzle/laser/psiren
icon_state = "muzzle_scc" // I just like the pattern here
light_color = COLOR_VIOLET
/obj/effect/projectile/tracer/laser/psiren
icon_state = "beam_scc"
light_color = COLOR_VIOLET
/obj/effect/projectile/impact/laser/psiren
icon_state = "impact_scc" // I also just like the pattern here
light_color = COLOR_VIOLET
@@ -0,0 +1,106 @@
/**
* All of the moodlet functionality required for the psychic damage effect,
* is contained within this file.
*/
/datum/moodlet/psiren_psi_damage
// Descriptor and Initial logic is bespoke for this moodlet.
moodlet_descriptor = "residual psionic pressure."
initial_descriptor = null
duration = 15.0 MINUTES
var/list/cursed_messages = list("DRINK DEEPLY OF THE FINAL NIGHT.",
"YOUR LOVED ONES WILL NEVER KNOW OF YOUR JOY AGAIN.",
"LEAVE THIS PLACE THROUGH THE NEAREST AIRLOCK.",
"DEATH IS BUT THE FIRST DANCE IN ETERNITY.",
"YOU DESERVE ALL OF YOUR SUFFERING.",
"THE STARS HAVE FORGOTTEN YOUR NAME.",
"YOUR BONES REMEMBER CRIMES YOU NEVER COMMITTED.",
"EVERY GOD YOU HAVE EVER PRAYED TO IS DEAD.",
"THE UNIVERSE REGRETS YOUR CREATION.",
"YOU HAVE ALREADY FAILED.",
"YOU WILL NEVER WAKE UP FROM THIS.",
"YOUR SCREAMS ARRIVE BEFORE YOU MAKE THEM.",
"YOU WERE WARNED.",
"THE RIVERS RUN COLD WITH MEMORY.",
"THE LEMURIAN SEA HUNGERS.",
"THE LEMURIAN SEA HAS BECOME YOUR TOMB.",
"THERE IS A DRUMBEAT BELOW REALITY.",
"THE SUNSET LASTS FOREVER HERE.",
"YOU CANNOT HIDE INSIDE YOUR OWN HEAD.",
"I KNOW WHAT YOU DREAM ABOUT.",
"I AM WEARING YOUR VOICE.",
"YOU INVITED ME IN",
"THE SCC SENT YOU HERE AS A FEAST FOR GIBBERING MOUTHS. YOUR LEADERS SENT YOU HERE TO DIE.",
"YOUR MEMORY FURNISHES A REPAST FOR THE MATRIARCH.",
"THE NIGHT SHEDS A TEAR TO TELL YOU OF FEAR, OF SORROW, OF PAIN.",
"THE SCC HAS ALREADY VOIDED YOUR CONTRACT, THEY KNEW YOU WOULD DIE HERE.",
"I SAW YOU WHEN YOU THOUGHT YOU WERE ALONE.",
"THE BLOOD WILL NEVER WASH FROM YOUR HANDS.",
"YOU ARE GUILTY OF YOUR FOREFATHERS SINS.",
"YOUR MISSION FAILED BEFORE IT BEGAN, THE PHORON YOU SEEK WAS NEVER REAL. YOUR NATION WILL DROWN FOR ITS WANT. IT'S ALL YOUR FAULT.")
/// Unique psi-damage variant used by the beckoner
/datum/moodlet/psiren_psi_damage/beckoner
duration = 10.0 MINUTES
cursed_messages = list(
"Please help me, I'm trapped outside the airlocks!",
"You feel drawn toward the nearest airlock.",
"Fresh air... no, vacuum... feels right somehow.",
"The void feels strangely comforting.",
"Please come save me, I'm in a lifepod just outside!",
"The ship feels unbearably cramped.",
"You need fresh air, go for a walk outside.",
"You don't need a space suit.",
"Did someone just knock on the hull?",
"I left something outside. I should go get it.",
"Come outside.",
"The nearest airlock suddenly seems very important.",
"Someone is calling for help outside!")
/datum/moodlet/psiren_psi_damage/get_moodlet_descriptor()
// This SHOULD exist but I'm doing my due diligence in checking.
var/mob/grandparent = astype(astype(morale_component?.resolve(), MORALE_COMPONENT)?.parent, /mob)
if (!grandparent)
return // No message, nobody to send a message to.
// Having any psi-protection will let you see the underlying moodlet.
if (grandparent.is_psi_blocked(null, FALSE))
return ..()
var/spooky_message = pick(cursed_messages)
if (prob(10))
grandparent.play_screen_text(spooky_message, /atom/movable/screen/text/screen_text/adpi_message/psiren_spam, COLOR_PURPLE)
return FONT_HUGE(SPAN_CULT(spooky_message))
/datum/moodlet/psiren_psi_damage/send_initial_description(datum/owner)
astype(astype(morale_component?.resolve(), MORALE_COMPONENT)?.parent, /mob)?.play_screen_text(pick(cursed_messages), /atom/movable/screen/text/screen_text/adpi_message/psiren_spam, COLOR_PURPLE)
return
/proc/try_deal_psychic_damage(mob/living/victim, mob/attacker, psi_damage = -0.1, psi_sensitivity_multiplier = -0.05, adpi_chance = 0, moodlet_type = /datum/moodlet/psiren_psi_damage, message_override)
// Immunity from telepathy will fully negate the psychic damage (but not physical damage) component.
// This is getting checked before the morale_comp because I want to give Active Psi Protection a chance to give the player feedback that a psychic effect occurred.
if (!istype(victim) || victim.stat == DEAD || victim.is_psi_blocked(attacker, FALSE))
return FALSE
// A morale component is also required for psychic damage.
var/datum/component/morale/morale_comp = victim.GetComponent(MORALE_COMPONENT)
if (!morale_comp)
return FALSE
var/victim_psi_sensitivity = victim.check_psi_sensitivity()
var/datum/moodlet/psi_damage_moodlet = morale_comp.load_moodlet(moodlet_type, FALSE)
psi_damage_moodlet.set_moodlet(psi_damage_moodlet.get_morale_modifier() + psi_damage + min(0, (victim_psi_sensitivity * psi_sensitivity_multiplier)))
psi_damage_moodlet.refresh_moodlet()
if (!prob(adpi_chance) || !SShallucinations)
return FALSE
// At this point in time all checks that would be satisfied by the ADPI subsystem have already been handled,
// now we just need to fetch a message and send one.
var/adpi_message = message_override ? message_override : SShallucinations.pick_adpi_message(victim, TRUE)
if (!adpi_message)
return FALSE
victim.play_screen_text("[adpi_message]", /atom/movable/screen/text/screen_text/adpi_message/psiren_spam, COLOR_PURPLE)
to_chat(victim, SPAN_CULT(FONT_LARGE("[adpi_message]")))
return TRUE