[MIRROR] Adds the Ectoplasmic Outburst, a rare ghost-centric anomaly event [MDB IGNORE] (#19292)

* Adds the Ectoplasmic Outburst, a rare ghost-centric anomaly event (#73024)

## About The Pull Request

This PR description might be a bit rushed because I accidentally
submitted the PR and want to bang out a quick explanation of what this
is before people start asking questions.

Adds a new, rare, ghost-themed anomaly event -- The Ectoplasmic
Outburst.

![Animation](https://user-images.githubusercontent.com/28870487/215292488-cc5ce589-39be-490d-bd80-92a2ed6e948b.gif)

This event increases in power as more ghosts orbit it, with three
important thresholds to meet.

If 10% or more of all active observers are orbiting the anomaly, an
effect will occur in the nearby area akin to the revenant's defile
spell, damaging flooring, windows, and making a mess.

If 35% or more are orbiting the anomaly, nearby objects in the effect
radius have a chance to become haunted, and will fling themselves at
anyone nearby for a bit. Spooky!

Now, at 50% or more participation, things get serious. Ghosts orbiting
the anomaly will be polled to be brought back into the world for a brief
period as a vengeful spirit, anchored to a SUPER spooky ghost portal.
The portal closes after two minutes (alternatively, smash it with a
toolbox) and deletes all of the ghosts it spawned. Vengeful spirits are
mostly just meant to smash stuff and be a nuisance while they can. It's
a wonderful opportunity to let deadchat desalinate a little.

![spooky
ghosts](https://user-images.githubusercontent.com/28870487/215293550-f7bbede2-cfa0-49b1-87fe-f38ee45d1df8.gif)

Oh, also, the anomaly is deadchat controlled. With enough ghosts, you
could theoretically outrun anyone trying to neutralize it!

The associated reactive armor has an effect similar to the anomaly,
haunting nearby objects for a time when the wearer is struck. Not
particularly outstanding, but it can introduce an element of chaos into
a fight that your opponent might not expect.

## Why It's Good For The Game

Anomaly events are great for ghosts. Why not make one tailored just for
them!

Gives admins something to spawn when a wizard has killed half of the
crew but it's not quiiiite time for an ERT.

It's a bit of a silly event with a novelty reward, but I think it's rare
enough not to be a huge issue.

## Changelog

🆑 Rhials
add: Ectoplasmic Outburst anomaly event
add: Reactive Ectoplasm Armor
/🆑

* Adds the Ectoplasmic Outburst, a rare ghost-centric anomaly event

---------

Co-authored-by: Rhials <Datguy33456@gmail.com>
Co-authored-by: lessthanthree <83487515+lessthnthree@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-02-13 01:44:26 +01:00
committed by GitHub
parent 63280c408d
commit 668bbcac4a
10 changed files with 363 additions and 0 deletions
@@ -99,3 +99,28 @@
attacker.visible_message(span_warning("[attacker] dispells the ghostly energy from [source]!"), span_warning("You dispel the ghostly energy from [source]!"))
clear_haunting()
return COMPONENT_NO_AFTERATTACK
/**
* Takes a given area and chance, applying the haunted_item component to objects in the area.
*
* Takes an epicenter, and within the range around it, runs a haunt_chance percent chance of
* applying the haunted_item component to nearby objects.
*
* * epicenter - The center of the outburst area.
* * range - The range of the outburst, centered around the epicenter.
* * haunt_chance - The percent chance that an object caught in the epicenter will be haunted.
* * duration - How long the haunting will remain for.
*/
/proc/haunt_outburst(epicenter, range, haunt_chance, duration = 1 MINUTES)
var/effect_area = range(range, epicenter)
for(var/obj/item/object_to_possess in effect_area)
if(!prob(haunt_chance))
continue
object_to_possess.AddComponent(/datum/component/haunted_item, \
haunt_color = "#52336e", \
haunt_duration = duration, \
aggro_radius = range, \
spawn_message = span_revenwarning("[object_to_possess] slowly rises upward, hanging menacingly in the air..."), \
despawn_message = span_revenwarning("[object_to_possess] settles to the floor, lifeless and unmoving."), \
)
@@ -14,6 +14,7 @@
/obj/effect/anomaly/bioscrambler = /obj/item/clothing/suit/armor/reactive/bioscrambling,
/obj/effect/anomaly/hallucination = /obj/item/clothing/suit/armor/reactive/hallucinating,
/obj/effect/anomaly/dimensional = /obj/item/clothing/suit/armor/reactive/barricade,
/obj/effect/anomaly/ectoplasm = /obj/item/clothing/suit/armor/reactive/ectoplasm,
)
if(istype(weapon, /obj/item/assembly/signaler/anomaly))
@@ -491,3 +492,22 @@
qdel(theme)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
/obj/item/clothing/suit/armor/reactive/ectoplasm
name = "reactive possession armor"
desc = "An experimental suit of armor that animates nearby objects with a ghostly possession."
emp_message = span_warning("The reactive armor lets out a horrible noise, and ghostly whispers fill your ears...")
cooldown_message = span_danger("Ectoplasmic Matrix out of balance. Please wait for calibration to complete!")
reactivearmor_cooldown_duration = 40 SECONDS
/obj/item/clothing/suit/armor/reactive/ectoplasm/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
playsound(get_turf(owner),'sound/hallucinations/veryfar_noise.ogg', 100, TRUE)
owner.visible_message(span_danger("The [src] lets loose a burst of otherworldly energy!"))
haunt_outburst(epicenter = get_turf(owner), range = 5, haunt_chance = 85, duration = 30 SECONDS)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/ectoplasm/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
owner.reagents?.add_reagent(/datum/reagent/inverse/helgrasp, 20)
@@ -0,0 +1,76 @@
#define ANOMALY_INTENSITY_MINOR "Minor Intensity"
#define ANOMALY_INTENSITY_MODERATE "Moderate Intensity"
#define ANOMALY_INTENSITY_MAJOR "Major Intensity"
/datum/round_event_control/anomaly/anomaly_ectoplasm
name = "Anomaly: Ectoplasmic Outburst"
description = "Anomaly that produces an effect of varying intensity based on how many ghosts are orbiting it."
typepath = /datum/round_event/anomaly/anomaly_ectoplasm
min_players = 30
max_occurrences = 2
weight = 4 //Rare because of it's wacky and silly nature
category = EVENT_CATEGORY_ANOMALIES
admin_setup = /datum/event_admin_setup/anomaly/anomaly_ectoplasm
/datum/round_event/anomaly/anomaly_ectoplasm
anomaly_path = /obj/effect/anomaly/ectoplasm
start_when = 3
announce_when = 20
///The admin-set impact effect intensity override
var/effect_override
///The admin-set number of ghosts, for use in calculating impact size.
var/orbit_override
/datum/round_event/anomaly/anomaly_ectoplasm/start()
if(!effect_override || !orbit_override)
return ..() //If we provide no override, just run the usual startup.
var/turf/anomaly_turf = placer.findValidTurf(impact_area)
var/obj/effect/anomaly/ectoplasm/newAnomaly
if(anomaly_turf)
newAnomaly = new anomaly_path(anomaly_turf)
newAnomaly.override_ghosts = TRUE
newAnomaly.effect_power = effect_override
newAnomaly.ghosts_orbiting = orbit_override
newAnomaly.intensity_update()
if(newAnomaly)
announce_to_ghosts(newAnomaly)
/datum/round_event/anomaly/anomaly_ectoplasm/announce(fake)
priority_announce("Localized ectoplasmic outburst detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert")
/datum/event_admin_setup/anomaly/anomaly_ectoplasm
///The admin-selected intensity
var/chosen_effect
///The number of ghosts the admin has selected to simulate orbiting the anomaly.
var/ghost_override
/datum/event_admin_setup/anomaly/anomaly_ectoplasm/prompt_admins()
. = ..()
if(tgui_alert(usr, "Override the anomaly effect and power?", "You'll be ruining the authenticity.", list("Yes", "No")) == "Yes")
var/list/power_values = list(ANOMALY_INTENSITY_MINOR, ANOMALY_INTENSITY_MODERATE, ANOMALY_INTENSITY_MAJOR)
chosen_effect = tgui_input_list(usr, "Provide effect override", "Criiiiinge.", power_values)
if(!chosen_effect)
return ADMIN_CANCEL_EVENT
ghost_override = tgui_input_number(usr, "How many ghosts do you want simulate orbiting your anomaly? (determines the effect radius).", "Seriously, CRINGE.", 0, 20, 1)
if(!ghost_override)
return ADMIN_CANCEL_EVENT
switch(chosen_effect) //Converts the text choice into a number for the anomaly to use
if(ANOMALY_INTENSITY_MINOR)
chosen_effect = 10
if(ANOMALY_INTENSITY_MODERATE)
chosen_effect = 35
if(ANOMALY_INTENSITY_MAJOR)
chosen_effect = 50
/datum/event_admin_setup/anomaly/anomaly_ectoplasm/apply_to_event(datum/round_event/anomaly/anomaly_ectoplasm/event)
. = ..()
event.effect_override = chosen_effect
event.orbit_override = ghost_override
#undef ANOMALY_INTENSITY_MINOR
#undef ANOMALY_INTENSITY_MODERATE
#undef ANOMALY_INTENSITY_MAJOR
@@ -107,3 +107,18 @@
/datum/ai_behavior/basic_melee_attack/ghost
action_cooldown = 2 SECONDS
/// Weaker variant of ghosts that can smash structures. Meant to be summoned in swarms via the ectoplasmic anomaly and associated ghost portal.
/mob/living/basic/ghost/swarm
name = "vengeful spirit"
desc = "Back from the grave, and not happy about it."
maxHealth = 30
health = 30
attack_verb_continuous = "smashes"
attack_verb_simple = "smash"
melee_damage_lower = 10
melee_damage_upper = 10
death_message = "wails as it is torn back to the realm from which it came!"
random_identity = FALSE
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
@@ -81,3 +81,9 @@
desc = "The neutralized core of a dimensional anomaly. Objects reflected on its surface don't look quite right. It'd probably be valuable for research."
icon_state = "dimensional_core"
anomaly_type = /obj/effect/anomaly/dimensional
/obj/item/assembly/signaler/anomaly/ectoplasm
name = "\improper ectoplasm anomaly core"
desc = "The neutralized core of an ectoplasmic anomaly. When you hold it close, you can hear faint murmuring from inside. It'd probably be valuable for research."
icon_state = "dimensional_core"
anomaly_type = /obj/effect/anomaly/ectoplasm
@@ -68,6 +68,12 @@
anomaly_type = /obj/item/assembly/signaler/anomaly/dimensional
icon_state = "rawcore_dimensional"
/obj/item/raw_anomaly_core/ectoplasm //Has no cargo order option, but can sometimes be a roundstart pick
name = "\improper ectoplasm anomaly core"
desc = "The raw core of an ectoplasmic anomaly. It wants to share its secrets with you."
anomaly_type = /obj/item/assembly/signaler/anomaly/ectoplasm
icon_state = "dimensional_core"
/obj/item/raw_anomaly_core/random/Initialize(mapload)
. = ..()
var/path = pick(subtypesof(/obj/item/raw_anomaly_core))