mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-20 14:45:05 +00:00
333 lines
12 KiB
Plaintext
333 lines
12 KiB
Plaintext
#define NARSIE_CHANCE_TO_PICK_NEW_TARGET 5
|
|
#define NARSIE_CONSUME_RANGE 12
|
|
#define NARSIE_GRAV_PULL 10
|
|
#define NARSIE_MESMERIZE_CHANCE 25
|
|
#define NARSIE_MESMERIZE_EFFECT 60
|
|
#define NARSIE_SINGULARITY_SIZE 12
|
|
|
|
#define ADMIN_WARNING_MESSAGE "Invoking this will begin the Nar'Sie roundender. Assume that this WILL end the round in a few minutes. Are you sure?"
|
|
|
|
/// Nar'Sie, the God of the blood cultists
|
|
/obj/narsie
|
|
name = "Nar'Sie"
|
|
desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees."
|
|
icon = 'icons/obj/antags/cult/narsie.dmi'
|
|
icon_state = "narsie"
|
|
anchored = TRUE
|
|
appearance_flags = LONG_GLIDE
|
|
density = FALSE
|
|
gender = FEMALE
|
|
plane = MASSIVE_OBJ_PLANE
|
|
light_color = COLOR_RED
|
|
light_power = 0.7
|
|
light_range = 15
|
|
light_range = 6
|
|
move_resist = INFINITY
|
|
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
|
pixel_x = -236
|
|
pixel_y = -256
|
|
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
|
flags_1 = SUPERMATTER_IGNORES_1
|
|
|
|
/// The singularity component to move around Nar'Sie.
|
|
/// A weak ref in case an admin removes the component to preserve the functionality.
|
|
var/datum/weakref/singularity
|
|
|
|
var/list/souls_needed = list()
|
|
var/soul_goal = 0
|
|
var/souls = 0
|
|
var/resolved = FALSE
|
|
|
|
/obj/narsie/Initialize(mapload)
|
|
. = ..()
|
|
|
|
narsie_spawn_animation()
|
|
|
|
/obj/narsie/Destroy()
|
|
if (GLOB.cult_narsie == src)
|
|
fall_of_the_harbinger()
|
|
GLOB.cult_narsie = null
|
|
|
|
return ..()
|
|
|
|
/// This proc sets up all of Nar'Sie's abilities, stats, and begins her round-ending capabilities. She does not do anything unless this proc is invoked.
|
|
/// This is only meant to be invoked after this instance is initialized in specific pro-sumer procs, as it WILL derail the entire round.
|
|
/obj/narsie/proc/start_ending_the_round()
|
|
GLOB.cult_narsie = src
|
|
SSpoints_of_interest.make_point_of_interest(src)
|
|
|
|
singularity = WEAKREF(AddComponent(
|
|
/datum/component/singularity, \
|
|
bsa_targetable = FALSE, \
|
|
consume_callback = CALLBACK(src, PROC_REF(consume)), \
|
|
consume_range = NARSIE_CONSUME_RANGE, \
|
|
disregard_failed_movements = TRUE, \
|
|
grav_pull = NARSIE_GRAV_PULL, \
|
|
roaming = FALSE, /* This is set once the animation finishes */ \
|
|
singularity_size = NARSIE_SINGULARITY_SIZE, \
|
|
))
|
|
|
|
send_to_playing_players(span_narsie("NAR'SIE HAS RISEN"))
|
|
sound_to_playing_players('sound/music/antag/bloodcult/narsie_rises.ogg')
|
|
|
|
var/area/area = get_area(src)
|
|
if(area)
|
|
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult.dmi', "ghostalertsie")
|
|
notify_ghosts(
|
|
"Nar'Sie has risen in [area]. Reach out to the Geometer to be given a new shell for your soul.",
|
|
source = src,
|
|
header = "Nar'Sie has risen!",
|
|
click_interact = TRUE,
|
|
alert_overlay = alert_overlay,
|
|
)
|
|
|
|
var/list/all_cults = list()
|
|
|
|
for (var/datum/antagonist/cult/cultist in GLOB.antagonists)
|
|
if (!cultist.owner)
|
|
continue
|
|
all_cults |= cultist.cult_team
|
|
|
|
for (var/_cult_team in all_cults)
|
|
var/datum/team/cult/cult_team = _cult_team
|
|
cult_team.set_blood_target(src, duration = INFINITY)
|
|
var/datum/objective/eldergod/summon_objective = locate() in cult_team.objectives
|
|
if(summon_objective)
|
|
summon_objective.summoned = TRUE
|
|
|
|
for (var/datum/mind/cult_mind as anything in get_antag_minds(/datum/antagonist/cult))
|
|
if (isliving(cult_mind.current))
|
|
var/mob/living/L = cult_mind.current
|
|
L.narsie_act()
|
|
|
|
for (var/mob/living/carbon/player in GLOB.player_list)
|
|
if (player.stat != DEAD && is_station_level(player.loc?.z) && !IS_CULTIST(player))
|
|
souls_needed[player] = TRUE
|
|
|
|
soul_goal = round(1 + LAZYLEN(souls_needed) * 0.75)
|
|
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(begin_the_end))
|
|
|
|
/// Cleans up all of Nar'Sie's abilities, stats, and ends her round-ending capabilities. This should only be called if `start_ending_the_round()` successfully started.
|
|
/obj/narsie/proc/fall_of_the_harbinger()
|
|
var/list/all_cults = list()
|
|
|
|
for (var/datum/antagonist/cult/cultist in GLOB.antagonists)
|
|
if (!cultist.owner)
|
|
continue
|
|
all_cults |= cultist.cult_team
|
|
|
|
for(var/_cult_team in all_cults)
|
|
var/datum/team/cult/cult_team = _cult_team
|
|
var/datum/objective/eldergod/summon_objective = locate() in cult_team.objectives
|
|
if (summon_objective)
|
|
summon_objective.summoned = FALSE
|
|
summon_objective.killed = TRUE
|
|
|
|
send_to_playing_players(span_narsie(span_bold(pick("Nooooo...", "Not die. How-", "Die. Mort-", "Sas tyen re-"))))
|
|
sound_to_playing_players('sound/effects/magic/demon_dies.ogg', 50)
|
|
|
|
/obj/narsie/vv_get_dropdown()
|
|
. = ..()
|
|
VV_DROPDOWN_OPTION("", "---------")
|
|
VV_DROPDOWN_OPTION(VV_HK_BEGIN_NARSIE_ROUNDEND, "Begin Nar'Sie Roundender")
|
|
|
|
/obj/narsie/vv_do_topic(list/href_list)
|
|
. = ..()
|
|
|
|
if(!.)
|
|
return
|
|
|
|
if(isnull(usr) || !href_list[VV_HK_BEGIN_NARSIE_ROUNDEND] || !check_rights(R_FUN, show_msg = TRUE))
|
|
return
|
|
|
|
if(tgui_alert(usr, ADMIN_WARNING_MESSAGE, "Begin Nar'Sie Roundender", list("I'm Sure", "Abort")) != "I'm Sure")
|
|
return
|
|
|
|
log_admin("[key_name(usr)] has triggered the Nar'Sie roundender.")
|
|
start_ending_the_round()
|
|
|
|
/obj/narsie/attack_ghost(mob/user)
|
|
make_new_construct(/mob/living/basic/construct/harvester, user, cultoverride = TRUE, loc_override = loc)
|
|
|
|
/obj/narsie/process()
|
|
var/datum/component/singularity/singularity_component = singularity.resolve()
|
|
|
|
if (!isnull(singularity_component) && (!singularity_component?.target || prob(NARSIE_CHANCE_TO_PICK_NEW_TARGET)))
|
|
pickcultist()
|
|
|
|
if (prob(NARSIE_MESMERIZE_CHANCE))
|
|
mesmerize()
|
|
|
|
/obj/narsie/Bump(atom/target)
|
|
var/turf/target_turf = get_turf(target)
|
|
if (target_turf == loc)
|
|
target_turf = get_step(target, target.dir) //please don't slam into a window like a bird, Nar'Sie
|
|
forceMove(target_turf)
|
|
|
|
/// Stun people around Nar'Sie that aren't cultists
|
|
/obj/narsie/proc/mesmerize()
|
|
for (var/mob/living/carbon/victim in viewers(NARSIE_CONSUME_RANGE, src))
|
|
if (victim.stat == CONSCIOUS)
|
|
if (!IS_CULTIST(victim))
|
|
to_chat(victim, span_cult("You feel conscious thought crumble away in an instant as you gaze upon [src]..."))
|
|
victim.apply_effect(NARSIE_MESMERIZE_EFFECT, EFFECT_STUN)
|
|
|
|
/// Narsie rewards her cultists with being devoured first, then picks a ghost to follow.
|
|
/obj/narsie/proc/pickcultist()
|
|
var/list/cultists = list()
|
|
var/list/noncultists = list()
|
|
|
|
for (var/mob/living/carbon/food in GLOB.alive_mob_list) //we don't care about constructs or cult-Ians or whatever. cult-monkeys are fair game i guess
|
|
var/turf/pos = get_turf(food)
|
|
if (!pos || (pos.z != z))
|
|
continue
|
|
|
|
if (IS_CULTIST(food))
|
|
cultists += food
|
|
else
|
|
noncultists += food
|
|
|
|
if (cultists.len) //cultists get higher priority
|
|
acquire(pick(cultists))
|
|
return
|
|
|
|
if (noncultists.len)
|
|
acquire(pick(noncultists))
|
|
return
|
|
|
|
//no living humans, follow a ghost instead.
|
|
for (var/mob/dead/observer/ghost in GLOB.player_list)
|
|
var/turf/pos = get_turf(ghost)
|
|
if (!pos || (pos.z != z))
|
|
continue
|
|
cultists += ghost
|
|
if (cultists.len)
|
|
acquire(pick(cultists))
|
|
return
|
|
|
|
/// Nar'Sie gets a taste of something, and will start to gravitate towards it
|
|
/obj/narsie/proc/acquire(atom/food)
|
|
var/datum/component/singularity/singularity_component = singularity.resolve()
|
|
|
|
if (isnull(singularity_component))
|
|
return
|
|
|
|
var/old_target = singularity_component.target
|
|
if (food == old_target)
|
|
return
|
|
|
|
to_chat(old_target, span_cult("NAR'SIE HAS LOST INTEREST IN YOU."))
|
|
singularity_component.target = food
|
|
if(ishuman(food))
|
|
to_chat(food, span_cult("NAR'SIE HUNGERS FOR YOUR SOUL."))
|
|
else
|
|
to_chat(food, span_cult("NAR'SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL."))
|
|
|
|
/// Called to make Nar'Sie convert objects to cult stuff, or to eat
|
|
/obj/narsie/proc/consume(atom/target)
|
|
if (isturf(target))
|
|
target.narsie_act()
|
|
|
|
/obj/narsie/proc/narsie_spawn_animation()
|
|
setDir(SOUTH)
|
|
flick("narsie_spawn_anim", src)
|
|
addtimer(CALLBACK(src, PROC_REF(narsie_spawn_animation_end)), 3.5 SECONDS)
|
|
|
|
/obj/narsie/proc/narsie_spawn_animation_end()
|
|
var/datum/component/singularity/singularity_component = singularity?.resolve()
|
|
singularity_component?.roaming = TRUE
|
|
|
|
/**
|
|
* Begins the process of ending the round via cult narsie win
|
|
* Consists of later called procs (in order of called):
|
|
* * [/proc/narsie_end_begin_check()]
|
|
* * [/proc/narsie_end_second_check()]
|
|
* * [/proc/narsie_start_destroy_station()]
|
|
* * [/proc/narsie_apocalypse()]
|
|
* * [/proc/narsie_last_second_win()]
|
|
* * [/proc/cult_ending_helper()]
|
|
*/
|
|
/proc/begin_the_end()
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(narsie_end_begin_check)), 5 SECONDS)
|
|
|
|
///First crew last second win check and flufftext for [/proc/begin_the_end()]
|
|
/proc/narsie_end_begin_check()
|
|
if(QDELETED(GLOB.cult_narsie)) // uno
|
|
priority_announce("Status report? We detected an anomaly, but it disappeared almost immediately.","[command_name()] Higher Dimensional Affairs", 'sound/announcer/notice/notice1.ogg')
|
|
GLOB.cult_narsie = null
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper), CULT_FAILURE_NARSIE_KILLED), 2 SECONDS)
|
|
return
|
|
priority_announce(
|
|
text = "An acausal dimensional event has been detected in your sector. Event has been flagged EXTINCTION-CLASS. Directing all available assets toward simulating solutions. SOLUTION ETA: 60 SECONDS.",
|
|
title = "[command_name()] Higher Dimensional Affairs",
|
|
sound = 'sound/announcer/alarm/airraid.ogg',
|
|
)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(narsie_end_second_check)), 50 SECONDS)
|
|
|
|
///Second crew last second win check and flufftext for [/proc/begin_the_end()]
|
|
/proc/narsie_end_second_check()
|
|
if(QDELETED(GLOB.cult_narsie)) // dos
|
|
priority_announce("Simulations aborted, sensors report that the acasual event is normalizing. Good work, crew.","[command_name()] Higher Dimensional Affairs", 'sound/announcer/notice/notice1.ogg')
|
|
GLOB.cult_narsie = null
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper), CULT_FAILURE_NARSIE_KILLED), 2 SECONDS)
|
|
return
|
|
priority_announce("Simulations on acausal dimensional event complete. Deploying solution package now. Deployment ETA: ONE MINUTE. ","[command_name()] Higher Dimensional Affairs")
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(narsie_start_destroy_station)), 5 SECONDS)
|
|
|
|
///security level and shuttle lockdowns for [/proc/begin_the_end()]
|
|
/proc/narsie_start_destroy_station()
|
|
SSsecurity_level.set_level(SEC_LEVEL_GAMMA) //SKYRAT EDIT CHANGE - ALERTS - ORIGINAL "delta"
|
|
SSshuttle.registerHostileEnvironment(GLOB.cult_narsie)
|
|
SSshuttle.lockdown = TRUE
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(narsie_apocalypse)), 1 MINUTES)
|
|
|
|
///Third crew last second win check and flufftext for [/proc/begin_the_end()]
|
|
/proc/narsie_apocalypse()
|
|
if(QDELETED(GLOB.cult_narsie)) // tres
|
|
priority_announce("Normalization detected! Abort the solution package!","[command_name()] Higher Dimensional Affairs", 'sound/announcer/notice/notice1.ogg')
|
|
SSshuttle.clearHostileEnvironment(GLOB.cult_narsie)
|
|
GLOB.cult_narsie = null
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(narsie_last_second_win)), 2 SECONDS)
|
|
return
|
|
if(GLOB.cult_narsie.resolved == FALSE)
|
|
GLOB.cult_narsie.resolved = TRUE
|
|
sound_to_playing_players('sound/announcer/alarm/nuke_alarm.ogg', 70)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper)), 12 SECONDS)
|
|
|
|
///Called only if the crew managed to destroy narsie at the very last second for [/proc/begin_the_end()]
|
|
/proc/narsie_last_second_win()
|
|
SSsecurity_level.set_level(SEC_LEVEL_RED)
|
|
SSshuttle.lockdown = FALSE
|
|
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper), CULT_FAILURE_NARSIE_KILLED)
|
|
|
|
///Helper to set the round to end asap. Current usage Cult round end code
|
|
/proc/ending_helper()
|
|
SSticker.force_ending = FORCE_END_ROUND
|
|
|
|
/**
|
|
* Selects cinematic to play as part of the cult end depending on the outcome then ends the round afterward
|
|
* called either when narsie eats everyone, or when [/proc/begin_the_end()] reaches its conclusion
|
|
*/
|
|
/proc/cult_ending_helper(ending_type = CULT_VICTORY_NUKE)
|
|
switch(ending_type)
|
|
// Narsie was killed
|
|
if(CULT_FAILURE_NARSIE_KILLED)
|
|
play_cinematic(/datum/cinematic/cult_fail, world, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ending_helper)))
|
|
|
|
// The cult "converted" (harvested) most of the station
|
|
if(CULT_VICTORY_MASS_CONVERSION)
|
|
play_cinematic(/datum/cinematic/cult_arm, world, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ending_helper)))
|
|
|
|
// The cult won, but centcom deployed a nuke. Default
|
|
if(CULT_VICTORY_NUKE)
|
|
play_cinematic(/datum/cinematic/nuke/cult, world, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ending_helper)))
|
|
|
|
#undef NARSIE_CHANCE_TO_PICK_NEW_TARGET
|
|
#undef NARSIE_CONSUME_RANGE
|
|
#undef NARSIE_GRAV_PULL
|
|
#undef NARSIE_MESMERIZE_CHANCE
|
|
#undef NARSIE_MESMERIZE_EFFECT
|
|
#undef NARSIE_SINGULARITY_SIZE
|
|
|
|
#undef ADMIN_WARNING_MESSAGE
|