mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 16:47:13 +01:00
[MIRROR] Singularity component (#2082)
* Singularity component (#55096) Adds singularity component * Singularity component Co-authored-by: Jared-Fogle <35135081+Jared-Fogle@users.noreply.github.com>
This commit is contained in:
@@ -1,51 +1,47 @@
|
||||
/// BoH tear
|
||||
/// The BoH tear is a stationary singularity with a really high gravitational pull, which collapses briefly after being created
|
||||
/// The BoH isn't deleted for 10 minutes (only moved to nullspace) so that admins may retrieve the things back in case of a grief
|
||||
/obj/singularity/boh_tear
|
||||
#define BOH_TEAR_CONSUME_RANGE 1
|
||||
#define BOH_TEAR_GRAV_PULL 25
|
||||
|
||||
/obj/boh_tear
|
||||
name = "tear in the fabric of reality"
|
||||
desc = "Your own comprehension of reality starts bending as you stare this."
|
||||
anchored = TRUE
|
||||
appearance_flags = LONG_GLIDE
|
||||
density = TRUE
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "boh_tear"
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
light_range = 6
|
||||
move_resist = INFINITY
|
||||
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
dissipate = 0
|
||||
move_self = 0
|
||||
consume_range = 1
|
||||
grav_pull = 25
|
||||
current_size = STAGE_SIX
|
||||
allowed_size = STAGE_SIX
|
||||
var/ghosts = list()
|
||||
var/old_loc
|
||||
var/start_time = 0
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
||||
flags_1 = SUPERMATTER_IGNORES_1
|
||||
|
||||
/obj/singularity/boh_tear/Initialize()
|
||||
/obj/boh_tear/Initialize()
|
||||
. = ..()
|
||||
old_loc = loc
|
||||
start_time = world.time
|
||||
QDEL_IN(src, 5 SECONDS) // vanishes after 5 seconds
|
||||
|
||||
/obj/singularity/boh_tear/process()
|
||||
//Backup to catch timerss errors
|
||||
if(start_time + (10 SECONDS) < world.time)
|
||||
stack_trace("The timer subsytem isn't firing properly, yell at your local coders posthaste")
|
||||
qdel(src)
|
||||
eat()
|
||||
AddComponent(
|
||||
/datum/component/singularity, \
|
||||
consume_range = BOH_TEAR_CONSUME_RANGE, \
|
||||
grav_pull = BOH_TEAR_GRAV_PULL, \
|
||||
roaming = FALSE, \
|
||||
singularity_size = STAGE_SIX, \
|
||||
)
|
||||
|
||||
/obj/singularity/boh_tear/consume(atom/A)
|
||||
A.singularity_act(current_size, src)
|
||||
|
||||
/obj/singularity/boh_tear/admin_investigate_setup()
|
||||
var/turf/T = get_turf(src)
|
||||
message_admins("A BoH tear has been created at [ADMIN_VERBOSEJMP(T)].")
|
||||
investigate_log("was created at [AREACOORD(T)].", INVESTIGATE_SINGULO)
|
||||
|
||||
|
||||
/obj/singularity/boh_tear/attack_tk(mob/user)
|
||||
/obj/boh_tear/attack_tk(mob/user)
|
||||
if(!isliving(user))
|
||||
return
|
||||
var/mob/living/jedi = user
|
||||
to_chat(jedi, "<span class='userdanger'>You don't feel like you are real anymore.</span>")
|
||||
jedi.dust_animation()
|
||||
jedi.spawn_dust()
|
||||
addtimer(CALLBACK(src, .proc/consume, jedi), 0.5 SECONDS)
|
||||
addtimer(CALLBACK(src, /atom/proc/attack_hand, jedi), 0.5 SECONDS)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
#undef BOH_TEAR_CONSUME_RANGE
|
||||
#undef BOH_TEAR_GRAV_PULL
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
/obj/machinery/field/containment/Initialize()
|
||||
. = ..()
|
||||
air_update_turf(TRUE)
|
||||
RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, .proc/block_singularity)
|
||||
|
||||
/obj/machinery/field/containment/Destroy()
|
||||
FG1.fields -= src
|
||||
@@ -79,6 +80,11 @@
|
||||
FG2 = master2
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/field/containment/proc/block_singularity()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
return SINGULARITY_TRY_MOVE_BLOCK
|
||||
|
||||
/obj/machinery/field/containment/shock(mob/living/user)
|
||||
if(!FG1 || !FG2)
|
||||
qdel(src)
|
||||
|
||||
@@ -58,6 +58,7 @@ no power level overlay is currently in the overlays list.
|
||||
. = ..()
|
||||
fields = list()
|
||||
connected_gens = list()
|
||||
RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, .proc/block_singularity_if_active)
|
||||
|
||||
/obj/machinery/field/generator/anchored/Initialize()
|
||||
. = ..()
|
||||
@@ -340,11 +341,6 @@ no power level overlay is currently in the overlays list.
|
||||
clean_up = 0
|
||||
update_icon()
|
||||
|
||||
//This is here to help fight the "hurr durr, release singulo cos nobody will notice before the
|
||||
//singulo eats the evidence". It's not fool-proof but better than nothing.
|
||||
//I want to avoid using global variables.
|
||||
INVOKE_ASYNC(src, .proc/notify_admins)
|
||||
|
||||
move_resist = initial(move_resist)
|
||||
|
||||
/obj/machinery/field/generator/proc/shield_floor(create)
|
||||
@@ -386,17 +382,11 @@ no power level overlay is currently in the overlays list.
|
||||
if(S)
|
||||
qdel(S)
|
||||
|
||||
/obj/machinery/field/generator/proc/notify_admins()
|
||||
var/temp = TRUE //stops spam
|
||||
for(var/obj/singularity/O in GLOB.singularities)
|
||||
if(O.last_warning && temp)
|
||||
if((world.time - O.last_warning) > 50) //to stop message-spam
|
||||
temp = FALSE
|
||||
var/turf/T = get_turf(src)
|
||||
message_admins("A singulo exists and a containment field has failed at [ADMIN_VERBOSEJMP(T)].")
|
||||
investigate_log("has <font color='red'>failed</font> whilst a singulo exists at [AREACOORD(T)].", INVESTIGATE_SINGULO)
|
||||
notify_ghosts("IT'S LOOSE", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, ghost_sound = 'sound/machines/warning-buzzer.ogg', header = "IT'S LOOSE", notify_volume = 75)
|
||||
O.last_warning = world.time
|
||||
/obj/machinery/field/generator/proc/block_singularity_if_active()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (active)
|
||||
return SINGULARITY_TRY_MOVE_BLOCK
|
||||
|
||||
/obj/machinery/field/generator/shock(mob/living/user)
|
||||
if(fields.len)
|
||||
|
||||
@@ -1,74 +1,204 @@
|
||||
/obj/singularity/narsie //Moving narsie to a child object of the singularity so it can be made to function differently. --NEO
|
||||
name = "Nar'Sie's Avatar"
|
||||
#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
|
||||
|
||||
/// 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/narsie_small.dmi'
|
||||
icon = 'icons/obj/narsie.dmi'
|
||||
icon_state = "narsie"
|
||||
pixel_x = -89
|
||||
pixel_y = -85
|
||||
anchored = TRUE
|
||||
appearance_flags = LONG_GLIDE
|
||||
density = FALSE
|
||||
current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO
|
||||
contained = 0 //Are we going to move around?
|
||||
dissipate = 0 //Do we lose energy over time?
|
||||
move_self = 1 //Do we move on our own?
|
||||
grav_pull = 5 //How many tiles out do we pull?
|
||||
consume_range = 6 //How many tiles out do we eat
|
||||
gender = FEMALE
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
light_color = COLOR_RED
|
||||
light_power = 0.7
|
||||
light_range = 15
|
||||
light_color = COLOR_RED
|
||||
gender = FEMALE
|
||||
|
||||
/obj/singularity/narsie/large
|
||||
name = "Nar'Sie"
|
||||
icon = 'icons/obj/narsie.dmi'
|
||||
// Pixel stuff centers Narsie.
|
||||
light_range = 6
|
||||
move_resist = INFINITY
|
||||
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
||||
pixel_x = -236
|
||||
pixel_y = -256
|
||||
current_size = 12
|
||||
grav_pull = 10
|
||||
consume_range = 12 //How many tiles out do we eat
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
||||
flags_1 = SUPERMATTER_IGNORES_1
|
||||
|
||||
/obj/singularity/narsie/large/Initialize()
|
||||
. = ..()
|
||||
send_to_playing_players("<span class='narsie'>NAR'SIE HAS RISEN</span>")
|
||||
sound_to_playing_players('sound/creatures/narsie_rises.ogg')
|
||||
/// 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/area/A = get_area(src)
|
||||
if(A)
|
||||
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult_effects.dmi', "ghostalertsie")
|
||||
notify_ghosts("Nar'Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK)
|
||||
narsie_spawn_animation()
|
||||
UnregisterSignal(src, COMSIG_ATOM_BSA_BEAM) //set up in /singularity/Initialize()
|
||||
|
||||
/obj/singularity/narsie/large/cult // For the new cult ending, guaranteed to end the round within 3 minutes
|
||||
var/list/souls_needed = list()
|
||||
var/soul_goal = 0
|
||||
var/souls = 0
|
||||
var/resolved = FALSE
|
||||
|
||||
/obj/singularity/narsie/large/cult/Initialize()
|
||||
/obj/narsie/Initialize()
|
||||
. = ..()
|
||||
|
||||
singularity = WEAKREF(AddComponent(
|
||||
/datum/component/singularity, \
|
||||
bsa_targetable = FALSE, \
|
||||
consume_callback = CALLBACK(src, .proc/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 class='narsie'>NAR'SIE HAS RISEN</span>")
|
||||
sound_to_playing_players('sound/creatures/narsie_rises.ogg')
|
||||
|
||||
var/area/area = get_area(src)
|
||||
if(area)
|
||||
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult_effects.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, alert_overlay = alert_overlay, action = NOTIFY_ATTACK)
|
||||
narsie_spawn_animation()
|
||||
|
||||
GLOB.cult_narsie = src
|
||||
var/list/all_cults = list()
|
||||
for(var/datum/antagonist/cult/C in GLOB.antagonists)
|
||||
if(!C.owner)
|
||||
|
||||
for (var/datum/antagonist/cult/cultist in GLOB.antagonists)
|
||||
if (!cultist.owner)
|
||||
continue
|
||||
all_cults |= C.cult_team
|
||||
for(var/datum/team/cult/T in all_cults)
|
||||
deltimer(T.blood_target_reset_timer)
|
||||
T.blood_target = src
|
||||
var/datum/objective/eldergod/summon_objective = locate() in T.objectives
|
||||
all_cults |= cultist.cult_team
|
||||
|
||||
for (var/_cult_team in all_cults)
|
||||
var/datum/team/cult/cult_team = _cult_team
|
||||
deltimer(cult_team.blood_target_reset_timer)
|
||||
cult_team.blood_target = src
|
||||
var/datum/objective/eldergod/summon_objective = locate() in cult_team.objectives
|
||||
if(summon_objective)
|
||||
summon_objective.summoned = TRUE
|
||||
for(var/datum/mind/cult_mind in SSticker.mode.cult)
|
||||
if(isliving(cult_mind.current))
|
||||
|
||||
for (var/_cult_mind in SSticker.mode.cult)
|
||||
var/datum/mind/cult_mind = _cult_mind
|
||||
if (isliving(cult_mind.current))
|
||||
var/mob/living/L = cult_mind.current
|
||||
L.narsie_act()
|
||||
for(var/mob/living/player in GLOB.player_list)
|
||||
if(player.stat != DEAD && player.loc && is_station_level(player.loc.z) && !iscultist(player) && !isanimal(player))
|
||||
|
||||
for (var/mob/living/carbon/player in GLOB.player_list)
|
||||
if (player.stat != DEAD && is_station_level(player.loc?.z) && !iscultist(player))
|
||||
souls_needed[player] = TRUE
|
||||
|
||||
soul_goal = round(1 + LAZYLEN(souls_needed) * 0.75)
|
||||
INVOKE_ASYNC(GLOBAL_PROC, .proc/begin_the_end)
|
||||
|
||||
/obj/narsie/Destroy()
|
||||
send_to_playing_players("<span class='narsie'>\"<b>[pick("Nooooo...", "Not die. How-", "Die. Mort-", "Sas tyen re-")]\"</b></span>")
|
||||
sound_to_playing_players('sound/magic/demon_dies.ogg', 50)
|
||||
|
||||
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
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/narsie/attack_ghost(mob/user)
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/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 (!iscultist(victim))
|
||||
to_chat(victim, "<span class='cultsmall'>You feel conscious thought crumble away in an instant as you gaze upon [src]...</span>")
|
||||
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 (iscultist(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 class='cultsmall'>NAR'SIE HAS LOST INTEREST IN YOU.</span>")
|
||||
singularity_component.target = food
|
||||
if(ishuman(food))
|
||||
to_chat(food, "<span class='cult'>NAR'SIE HUNGERS FOR YOUR SOUL.</span>")
|
||||
else
|
||||
to_chat(food, "<span class='cult'>NAR'SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.</span>")
|
||||
|
||||
/// 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/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
|
||||
|
||||
/proc/begin_the_end()
|
||||
sleep(50)
|
||||
if(QDELETED(GLOB.cult_narsie)) // uno
|
||||
@@ -105,21 +235,6 @@
|
||||
sound_to_playing_players('sound/machines/alarm.ogg')
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/cult_ending_helper), 120)
|
||||
|
||||
/obj/singularity/narsie/large/cult/Destroy()
|
||||
send_to_playing_players("<span class='narsie'>\"<b>[pick("Nooooo...", "Not die. How-", "Die. Mort-", "Sas tyen re-")]\"</b></span>")
|
||||
sound_to_playing_players('sound/magic/demon_dies.ogg', 50)
|
||||
var/list/all_cults = list()
|
||||
for(var/datum/antagonist/cult/C in GLOB.antagonists)
|
||||
if(!C.owner)
|
||||
continue
|
||||
all_cults |= C.cult_team
|
||||
for(var/datum/team/cult/T in all_cults)
|
||||
var/datum/objective/eldergod/summon_objective = locate() in T.objectives
|
||||
if(summon_objective)
|
||||
summon_objective.summoned = FALSE
|
||||
summon_objective.killed = TRUE
|
||||
return ..()
|
||||
|
||||
/proc/ending_helper()
|
||||
SSticker.force_ending = 1
|
||||
|
||||
@@ -131,106 +246,9 @@
|
||||
else // explosion
|
||||
Cinematic(CINEMATIC_CULT_NUKE,world,CALLBACK(GLOBAL_PROC,/proc/ending_helper))
|
||||
|
||||
//ATTACK GHOST IGNORING PARENT RETURN VALUE
|
||||
/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob)
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, user, cultoverride = TRUE, loc_override = src.loc)
|
||||
|
||||
/obj/singularity/narsie/process()
|
||||
eat()
|
||||
if(!target || prob(5))
|
||||
pickcultist()
|
||||
move()
|
||||
if(prob(25))
|
||||
mezzer()
|
||||
|
||||
|
||||
/obj/singularity/narsie/Bump(atom/A)
|
||||
var/turf/T = get_turf(A)
|
||||
if(T == loc)
|
||||
T = get_step(A, A.dir) //please don't slam into a window like a bird, Nar'Sie
|
||||
forceMove(T)
|
||||
|
||||
|
||||
/obj/singularity/narsie/mezzer()
|
||||
for(var/mob/living/carbon/M in viewers(consume_range, src))
|
||||
if(M.stat == CONSCIOUS)
|
||||
if(!iscultist(M))
|
||||
to_chat(M, "<span class='cultsmall'>You feel conscious thought crumble away in an instant as you gaze upon [src.name]...</span>")
|
||||
M.apply_effect(60, EFFECT_STUN)
|
||||
|
||||
|
||||
/obj/singularity/narsie/consume(atom/A)
|
||||
if(isturf(A))
|
||||
A.narsie_act()
|
||||
|
||||
|
||||
/obj/singularity/narsie/ex_act() //No throwing bombs at her either.
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/narsie/proc/pickcultist() //Narsie rewards her cultists with being devoured first, then picks a ghost to follow.
|
||||
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(iscultist(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
|
||||
|
||||
|
||||
/obj/singularity/narsie/proc/acquire(atom/food)
|
||||
if(food == target)
|
||||
return
|
||||
to_chat(target, "<span class='cultsmall'>NAR'SIE HAS LOST INTEREST IN YOU.</span>")
|
||||
target = food
|
||||
if(ishuman(target))
|
||||
to_chat(target, "<span class='cult'>NAR'SIE HUNGERS FOR YOUR SOUL.</span>")
|
||||
else
|
||||
to_chat(target, "<span class='cult'>NAR'SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.</span>")
|
||||
|
||||
//Wizard narsie
|
||||
/obj/singularity/narsie/wizard
|
||||
grav_pull = 0
|
||||
|
||||
/obj/singularity/narsie/wizard/eat()
|
||||
// if(defer_powernet_rebuild != 2)
|
||||
// defer_powernet_rebuild = 1
|
||||
for(var/atom/X in urange(consume_range,src,1))
|
||||
if(isturf(X) || ismovable(X))
|
||||
consume(X)
|
||||
// if(defer_powernet_rebuild != 2)
|
||||
// defer_powernet_rebuild = 0
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/narsie/proc/narsie_spawn_animation()
|
||||
setDir(SOUTH)
|
||||
move_self = FALSE
|
||||
flick("narsie_spawn_anim",src)
|
||||
addtimer(CALLBACK(src, .proc/narsie_spawn_animation_end), 3.5 SECONDS)
|
||||
|
||||
/obj/singularity/narsie/proc/narsie_spawn_animation_end()
|
||||
move_self = TRUE
|
||||
#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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
/// The gravitational singularity
|
||||
/obj/singularity
|
||||
name = "gravitational singularity"
|
||||
desc = "A gravitational singularity."
|
||||
@@ -11,41 +10,51 @@
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
light_range = 6
|
||||
appearance_flags = LONG_GLIDE
|
||||
|
||||
/// The singularity component itself.
|
||||
/// A weak ref in case an admin removes the component to preserve the functionality.
|
||||
var/datum/weakref/singularity_component
|
||||
|
||||
var/current_size = 1
|
||||
var/allowed_size = 1
|
||||
var/contained = 1 //Are we going to move around?
|
||||
var/energy = 100 //How strong are we?
|
||||
var/dissipate = TRUE //Do we lose energy over time?
|
||||
var/dissipate_delay = 10
|
||||
var/dissipate_track = 0
|
||||
var/dissipate_strength = 1 //How much energy do we lose?
|
||||
var/move_self = 1 //Do we move on our own?
|
||||
var/grav_pull = 4 //How many tiles out do we pull?
|
||||
var/consume_range = 0 //How many tiles out do we eat
|
||||
var/event_chance = 10 //Prob for event each tick
|
||||
var/target = null //its target. moves towards the target if it has one
|
||||
var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing
|
||||
var/last_warning
|
||||
var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six
|
||||
var/drifting_dir = 0 // Chosen direction to drift in
|
||||
var/move_self = TRUE
|
||||
var/consumed_supermatter = FALSE //If the singularity has eaten a supermatter shard and can go to stage six
|
||||
|
||||
flags_1 = SUPERMATTER_IGNORES_1
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
||||
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
||||
|
||||
/obj/singularity/Initialize(mapload, starting_energy = 50)
|
||||
//CARN: admin-alert for chuckle-fuckery.
|
||||
admin_investigate_setup()
|
||||
|
||||
src.energy = starting_energy
|
||||
. = ..()
|
||||
|
||||
energy = starting_energy
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list |= src
|
||||
GLOB.singularities |= src
|
||||
for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines)
|
||||
if(singubeacon.active)
|
||||
target = singubeacon
|
||||
|
||||
var/datum/component/singularity/new_component = AddComponent(
|
||||
/datum/component/singularity, \
|
||||
consume_callback = CALLBACK(src, .proc/consume), \
|
||||
)
|
||||
|
||||
singularity_component = WEAKREF(new_component)
|
||||
|
||||
expand(current_size)
|
||||
|
||||
for (var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines)
|
||||
if (singubeacon.active)
|
||||
new_component.target = singubeacon
|
||||
break
|
||||
AddElement(/datum/element/bsa_blocker)
|
||||
RegisterSignal(src, COMSIG_ATOM_BSA_BEAM, .proc/bluespace_reaction)
|
||||
|
||||
if (!mapload)
|
||||
notify_ghosts("IT'S LOOSE", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, ghost_sound = 'sound/machines/warning-buzzer.ogg', header = "IT'S LOOSE", notify_volume = 75)
|
||||
|
||||
/obj/singularity/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
@@ -53,49 +62,6 @@
|
||||
GLOB.singularities.Remove(src)
|
||||
return ..()
|
||||
|
||||
/obj/singularity/Move(atom/newloc, direct)
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/dir in GLOB.cardinals)
|
||||
if(direct & dir)
|
||||
T = get_step(T, dir)
|
||||
if(!T)
|
||||
break
|
||||
// eat the stuff if we're going to move into it so it doesn't mess up our movement
|
||||
for(var/atom/A in T.contents)
|
||||
consume(A)
|
||||
consume(T)
|
||||
|
||||
if(current_size >= STAGE_FIVE || check_turfs_in(direct))
|
||||
last_failed_movement = 0//Reset this because we moved
|
||||
return ..()
|
||||
else
|
||||
last_failed_movement = direct
|
||||
return FALSE
|
||||
|
||||
/obj/singularity/attack_hand(mob/user)
|
||||
consume(user)
|
||||
return TRUE
|
||||
|
||||
/obj/singularity/attack_paw(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attack_alien(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attack_animal(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attackby(obj/item/W, mob/user, params)
|
||||
consume(user)
|
||||
return TRUE
|
||||
|
||||
/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man!
|
||||
return FALSE
|
||||
|
||||
/obj/singularity/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/attack_tk(mob/user)
|
||||
if(!iscarbon(user))
|
||||
return
|
||||
@@ -150,7 +116,6 @@
|
||||
rip_u.dismember(BURN)
|
||||
qdel(rip_u)
|
||||
|
||||
|
||||
/obj/singularity/ex_act(severity, target)
|
||||
switch(severity)
|
||||
if(1)
|
||||
@@ -164,52 +129,14 @@
|
||||
energy -= round(((energy+1)/3),1)
|
||||
if(3)
|
||||
energy -= round(((energy+1)/4),1)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/bullet_act(obj/projectile/P)
|
||||
qdel(P)
|
||||
return BULLET_ACT_HIT //Will there be an impact? Who knows. Will we see it? No.
|
||||
|
||||
|
||||
/obj/singularity/Bump(atom/A)
|
||||
consume(A)
|
||||
if(QDELETED(A)) // don't keep moving into objects that weren't destroyed infinitely
|
||||
step(src, drifting_dir)
|
||||
return
|
||||
|
||||
/obj/singularity/Crossed(atom/A)
|
||||
..()
|
||||
consume(A)
|
||||
|
||||
/obj/singularity/Bumped(atom/movable/AM)
|
||||
consume(AM)
|
||||
|
||||
|
||||
/obj/singularity/process()
|
||||
if(current_size >= STAGE_TWO)
|
||||
move()
|
||||
if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit
|
||||
event()
|
||||
eat()
|
||||
dissipate()
|
||||
check_energy()
|
||||
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one.
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/admin_investigate_setup()
|
||||
var/turf/T = get_turf(src)
|
||||
last_warning = world.time
|
||||
var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1)
|
||||
if(!count)
|
||||
message_admins("A singulo has been created without containment fields active at [ADMIN_VERBOSEJMP(T)].")
|
||||
investigate_log("was created at [AREACOORD(T)]. [count?"":"<font color='red'>No containment fields were active</font>"]", INVESTIGATE_SINGULO)
|
||||
|
||||
/obj/singularity/proc/dissipate()
|
||||
if(!dissipate)
|
||||
return
|
||||
@@ -219,13 +146,18 @@
|
||||
else
|
||||
dissipate_track++
|
||||
|
||||
|
||||
/obj/singularity/proc/expand(force_size = 0)
|
||||
/obj/singularity/proc/expand(force_size)
|
||||
var/temp_allowed_size = src.allowed_size
|
||||
|
||||
if(force_size)
|
||||
temp_allowed_size = force_size
|
||||
if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter)
|
||||
|
||||
if(temp_allowed_size >= STAGE_SIX && !consumed_supermatter)
|
||||
temp_allowed_size = STAGE_FIVE
|
||||
|
||||
var/new_grav_pull
|
||||
var/new_consume_range
|
||||
|
||||
switch(temp_allowed_size)
|
||||
if(STAGE_ONE)
|
||||
current_size = STAGE_ONE
|
||||
@@ -233,8 +165,8 @@
|
||||
icon_state = "singularity_s1"
|
||||
pixel_x = 0
|
||||
pixel_y = 0
|
||||
grav_pull = 4
|
||||
consume_range = 0
|
||||
new_grav_pull = 4
|
||||
new_consume_range = 0
|
||||
dissipate_delay = 10
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 1
|
||||
@@ -245,8 +177,8 @@
|
||||
icon_state = "singularity_s3"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
grav_pull = 6
|
||||
consume_range = 1
|
||||
new_grav_pull = 6
|
||||
new_consume_range = 1
|
||||
dissipate_delay = 5
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 5
|
||||
@@ -257,8 +189,8 @@
|
||||
icon_state = "singularity_s5"
|
||||
pixel_x = -64
|
||||
pixel_y = -64
|
||||
grav_pull = 8
|
||||
consume_range = 2
|
||||
new_grav_pull = 8
|
||||
new_consume_range = 2
|
||||
dissipate_delay = 4
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 20
|
||||
@@ -269,8 +201,8 @@
|
||||
icon_state = "singularity_s7"
|
||||
pixel_x = -96
|
||||
pixel_y = -96
|
||||
grav_pull = 10
|
||||
consume_range = 3
|
||||
new_grav_pull = 10
|
||||
new_consume_range = 3
|
||||
dissipate_delay = 10
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 10
|
||||
@@ -280,18 +212,27 @@
|
||||
icon_state = "singularity_s9"
|
||||
pixel_x = -128
|
||||
pixel_y = -128
|
||||
grav_pull = 10
|
||||
consume_range = 4
|
||||
dissipate = 0 //It cant go smaller due to e loss
|
||||
new_grav_pull = 10
|
||||
new_consume_range = 4
|
||||
dissipate = FALSE //It cant go smaller due to e loss
|
||||
if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard.
|
||||
current_size = STAGE_SIX
|
||||
icon = 'icons/effects/352x352.dmi'
|
||||
icon_state = "singularity_s11"
|
||||
pixel_x = -160
|
||||
pixel_y = -160
|
||||
grav_pull = 15
|
||||
consume_range = 5
|
||||
dissipate = 0
|
||||
new_grav_pull = 15
|
||||
new_consume_range = 5
|
||||
dissipate = FALSE
|
||||
|
||||
var/datum/component/singularity/resolved_singularity = singularity_component.resolve()
|
||||
if (!isnull(resolved_singularity))
|
||||
resolved_singularity.consume_range = new_consume_range
|
||||
resolved_singularity.grav_pull = new_grav_pull
|
||||
resolved_singularity.disregard_failed_movements = current_size >= STAGE_FIVE
|
||||
resolved_singularity.roaming = move_self && current_size >= STAGE_TWO
|
||||
resolved_singularity.singularity_size = current_size
|
||||
|
||||
if(current_size == allowed_size)
|
||||
investigate_log("<font color='red'>grew to size [current_size]</font>", INVESTIGATE_SINGULO)
|
||||
return TRUE
|
||||
@@ -316,7 +257,7 @@
|
||||
if(1000 to 1999)
|
||||
allowed_size = STAGE_FOUR
|
||||
if(2000 to INFINITY)
|
||||
if(energy >= 3000 && consumedSupermatter)
|
||||
if(energy >= 3000 && consumed_supermatter)
|
||||
allowed_size = STAGE_SIX
|
||||
else
|
||||
allowed_size = STAGE_FIVE
|
||||
@@ -324,51 +265,14 @@
|
||||
expand()
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/singularity/proc/eat()
|
||||
for(var/tile in spiral_range_turfs(grav_pull, src))
|
||||
var/turf/T = tile
|
||||
if(!T || !isturf(loc))
|
||||
continue
|
||||
if(get_dist(T, src) > consume_range)
|
||||
T.singularity_pull(src, current_size)
|
||||
else
|
||||
consume(T)
|
||||
for(var/thing in T)
|
||||
if(isturf(loc) && thing != src)
|
||||
var/atom/movable/X = thing
|
||||
if(get_dist(X, src) > consume_range)
|
||||
X.singularity_pull(src, current_size)
|
||||
else
|
||||
consume(X)
|
||||
CHECK_TICK
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/consume(atom/A)
|
||||
var/gain = A.singularity_act(current_size, src)
|
||||
src.energy += gain
|
||||
if(istype(A, /obj/machinery/power/supermatter_crystal) && !consumedSupermatter)
|
||||
/obj/singularity/proc/consume(atom/thing)
|
||||
var/gain = thing.singularity_act(current_size, src)
|
||||
energy += gain
|
||||
if(istype(thing, /obj/machinery/power/supermatter_crystal) && !consumed_supermatter)
|
||||
desc = "[initial(desc)] It glows fiercely with inner fire."
|
||||
name = "supermatter-charged [initial(name)]"
|
||||
consumedSupermatter = 1
|
||||
consumed_supermatter = TRUE
|
||||
set_light(10)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/move(force_move = 0)
|
||||
if(!move_self)
|
||||
return FALSE
|
||||
|
||||
var/drifting_dir = pick(GLOB.alldirs - last_failed_movement)
|
||||
|
||||
if(force_move)
|
||||
drifting_dir = force_move
|
||||
|
||||
if(target && prob(60))
|
||||
drifting_dir = get_dir(src,target) //moves to a singulo beacon, if there is one
|
||||
|
||||
step(src, drifting_dir)
|
||||
|
||||
/obj/singularity/proc/check_cardinals_range(steps, retry_with_move = FALSE)
|
||||
. = length(GLOB.cardinals) //Should be 4.
|
||||
@@ -497,18 +401,13 @@
|
||||
|
||||
/obj/singularity/proc/emp_area()
|
||||
empulse(src, 8, 10)
|
||||
return
|
||||
|
||||
/obj/singularity/singularity_act()
|
||||
var/gain = (energy/2)
|
||||
var/dist = max((current_size - 2),1)
|
||||
explosion(src.loc,(dist),(dist*2),(dist*4))
|
||||
qdel(src)
|
||||
return(gain)
|
||||
|
||||
/obj/singularity/proc/bluespace_reaction()
|
||||
investigate_log("has been shot by bluespace artillery and destroyed.", INVESTIGATE_SINGULO)
|
||||
qdel(src)
|
||||
return gain
|
||||
|
||||
/obj/singularity/deadchat_controlled
|
||||
move_self = FALSE
|
||||
|
||||
Reference in New Issue
Block a user