var/list/obj/machinery/singularity/global_singularity_pool
var/list/obj/machinery/singularity/white_hole_candidates
/obj/machinery/singularity
name = "gravitational singularity" //Lower case
desc = "The destructive, murderous Lord Singuloth, patron saint of Engineering. They harness its power to run the station's lighting and arcades."
icon = 'icons/obj/singularity.dmi'
icon_state = "singularity_s1"
anchored = 1
density = 0
layer = SINGULARITY_LAYER
plane = EFFECTS_PLANE
luminosity = 6
use_power = MACHINE_POWER_USE_NONE
var/obj/machinery/singularity/wormhole_out = null
var/obj/machinery/singularity/wormhole_in = null
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 = 1 //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 = 15 //Prob for event each tick.
var/target = null //Its target. Moves towards the target if it has one.
var/last_movement_dir = 0 //Log the singularity's last movement to produce biased movement (singularity prefers constant movement due to inertia)
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
appearance_flags = LONG_GLIDE|TILE_MOVER
var/chained = 0 //Adminbus chain-grab
var/modifier = "" //for memes
var/repels = FALSE //For pushing stuff out the other end
var/list/speech_messages = list() //Time is occuring in random pockets. The laws of causality no longer apply.
/obj/machinery/singularity/New(loc, var/starting_energy = 50, var/temp = 0)
//CARN: admin-alert for chuckle-fuckery.
icon_state = modifier + icon_state
admin_investigate_setup()
energy = starting_energy
if(temp)
spawn(temp)
qdel(src)
..()
machines -= src
power_machines += src
for(var/obj/machinery/singularity_beacon/singubeacon in machines)
if(singubeacon.active)
target = singubeacon
break
if(!global_singularity_pool)
global_singularity_pool = list()
global_singularity_pool += src
if(!white_hole_candidates)
white_hole_candidates = list()
if(!repels && !(src in white_hole_candidates))
white_hole_candidates += src
if(prob(1) && white_hole_candidates.len > 1)
link_a_wormhole()
/obj/machinery/singularity/arcane_act(mob/user, recursive)
if(current_size <= 1)
var/obj/item/toy/spinningtoy/T = new(loc)
T.arcane_act(user,recursive)
qdel(src)
return
else if(!wormhole_in)
name = "white hole"
desc = "Every action has an equal and opposite reaction. A black hole sucks time and matter out of the universe, a white hole returns it."
repels = TRUE
color= list(-1,0,0,
0,-1,0,
0,0,-1,
1,1,1) //Invert it
else
name = initial(name)
desc = initial(desc)
repels = FALSE
color= initial(color)
return ..()
/obj/machinery/singularity/bless() // yeah good luck doing this
if(!wormhole_in)
name = initial(name)
desc = initial(desc)
repels = FALSE
color= initial(color)
..()
/obj/machinery/singularity/proc/seeks_beacon()
return TRUE
/obj/machinery/singularity/proc/link_a_wormhole()
var/obj/machinery/singularity/other = null
do
other = pick(white_hole_candidates)
while(white_hole_candidates.len > 1 && other == src)
if(other && other != src)
if(prob(50))
link_wormhole(other)
else
other.link_wormhole(src)
/obj/machinery/singularity/proc/link_wormhole(var/obj/machinery/singularity/other)
if(other)
visible_message("[src] pulsates as a distinctive [get_area(other) ? "[get_area(other)]": "place"] becomes visible.")
other.visible_message("[other] pulsates as a distinctive [get_area(src) ? "[get_area(src)]": "place"] becomes visible.")
other.name = "white hole"
other.desc = "Every action has an equal and opposite reaction. A black hole sucks time and matter out of the universe, a white hole returns it."
other.repels = TRUE
other.energy = src.energy
other.color= list(-1,0,0,
0,-1,0,
0,0,-1,
1,1,1) //Invert it
wormhole_out = other
other.wormhole_in = src
if(other in white_hole_candidates)
white_hole_candidates -= other
/obj/machinery/singularity/proc/unlink_wormholes()
if(wormhole_out)
visible_message("[get_area(wormhole_out) ? "[get_area(wormhole_out)]": "The strange place"] is no longer visible as [src] closes its path to it.")
wormhole_out.name = initial(wormhole_out.name)
wormhole_out.desc = initial(wormhole_out.desc)
wormhole_out.repels = FALSE
wormhole_out.color= initial(wormhole_out.color)
if(!(wormhole_out in white_hole_candidates))
white_hole_candidates += wormhole_out
wormhole_out = null
if(wormhole_in)
visible_message("[get_area(wormhole_in) ? "[get_area(wormhole_in)]": "The strange place"] is no longer visible as [src] closes its path to it.")
name = initial(name)
repels = FALSE
color = initial(color)
if(!(src in white_hole_candidates))
white_hole_candidates += src
wormhole_in = null
/obj/machinery/singularity/attack_hand(mob/user as mob)
consume(user)
return 1
/obj/machinery/singularity/blob_act(severity)
return
/obj/machinery/singularity/supermatter_act(atom/source, severity)
return
/obj/machinery/singularity/ex_act(severity)
if(current_size > 10) //IT'S UNSTOPPABLE
return
switch(severity)
if(1.0)
if(prob(25))
investigation_log(I_SINGULO, "has been destroyed by an explosion.")
qdel(src)
return
else
energy += 50
if(2.0 to 3.0)
energy += round((rand(20, 60)/2), 1)
return
/obj/machinery/singularity/to_bump(atom/A)
consume(A)
/obj/machinery/singularity/Bumped(atom/A)
consume(A)
/obj/machinery/singularity/Crossed(atom/movable/A)
consume(A)
/obj/machinery/singularity/attack_tk(mob/user)
to_chat(user, "You attempt to comprehend \the [src]...")
spawn(rand(50,110))
if(!user.gcDestroyed)
if(prob(95))
to_chat(user, "...and fail to do so.")
if(prob(50)) //50/50 of becoming unrecoverable
user.visible_message("\The [user] screams as they are consumed from within!")
if(prob(50))
user.audible_scream()
var/matrix/M = matrix()
M.Scale(0)
animate(user, alpha = 0, transform = M, time = 3 SECONDS, easing = SINE_EASING)
spawn(3 SECONDS)
new /obj/effect/gibspawner/generic(get_turf(user))
qdel(user)
else
playsound(user, get_sfx("soulstone"), 50,1)
make_tracker_effects(get_turf(user), get_turf(src))
user.dust()
else
user.visible_message("\The [user] explodes!")
..()
else
to_chat(user, "...and manage to grab onto something from the depths of \the [src]!")
if(do_after(user, src, 30))
to_chat(user, "You manage to pull something from beyond to within normal space!")
var/obj/structure/losetta_stone/L = new
L.alpha = 0
L.forceMove(get_turf(user))
animate(L, alpha = 255, time = 3 SECONDS)
/obj/machinery/singularity/Hear(var/datum/speech/speech, var/rendered_message="")
if(repels && ismob(speech.speaker))
var/mob/M = speech.speaker
if(M.dna) // Wait a minute... I missed a discussion!
speech_messages[M.dna] = speech.message // We all did
/obj/machinery/singularity/process()
dissipate()
check_energy()
if(current_size >= 3)
move()
pulse()
if(prob(event_chance)) //Chance for it to run a special event TODO: Come up with one or two more that fit.
event()
eat()
if(repels && speech_messages.len) // I've never seen one before, no one has, but I'm guessing it's a white hole.
for(var/mob/M in viewers(src)) // A white hole?
if((M.dna in speech_messages) && prob(10))
if(prob(90)) // So that thing's spewing time? Back into the universe?
M.say(speech_messages[M.dna])
else
M.say("So what is it?") //Only joking
/obj/machinery/singularity/attack_ai() //To prevent AIs from gibbing themselves when they click on one.
return
/obj/machinery/singularity/proc/admin_investigate_setup()
last_warning = world.time
var/count = locate(/obj/machinery/containment_field) in orange(30, src)
if(!count)
message_admins("A singulo has been created without containment fields active ([x], [y], [z] - JMP).")
investigation_log(I_SINGULO,"was created. [count ? "" : "No containment fields were active."]")
/obj/machinery/singularity/proc/dissipate()
if(!dissipate)
return
if(dissipate_track >= dissipate_delay)
energy -= dissipate_strength
dissipate_track = 0
else
dissipate_track++
/obj/machinery/singularity/proc/expand(var/force_size = 0, var/growing = 1)
if(current_size > 10 && !force_size) //If this is happening, this is an error
message_admins("expand() was called on a super singulo. This should not happen.")
return
var/temp_allowed_size = allowed_size
if(force_size)
temp_allowed_size = force_size
if(temp_allowed_size <= STAGE_FIVE && growing && is_near_shield())
move_away_from_shield()
switch(temp_allowed_size)
if(STAGE_ONE)
current_size = 1
icon = 'icons/obj/singularity.dmi'
pixel_x = 0
pixel_y = 0
bound_width = WORLD_ICON_SIZE
bound_x = 0
bound_height = WORLD_ICON_SIZE
bound_y = 0
grav_pull = 4
consume_range = 0
dissipate_delay = 10
dissipate_track = 0
dissipate_strength = 1
overlays = 0
if(chained)
overlays += image(icon = icon, icon_state = "chain_s1")
visible_message("\The [src] shrinks to a rather pitiful size.")
if(STAGE_TWO)
current_size = 3
icon = 'icons/effects/96x96.dmi'
pixel_x = -32 * PIXEL_MULTIPLIER
pixel_y = -32 * PIXEL_MULTIPLIER
bound_width = 3 * WORLD_ICON_SIZE
bound_x = -WORLD_ICON_SIZE
bound_height = 3 * WORLD_ICON_SIZE
bound_y = -WORLD_ICON_SIZE
grav_pull = 6
consume_range = 1
dissipate_delay = 5
dissipate_track = 0
dissipate_strength = 5
overlays = 0
if(chained)
overlays += image(icon = icon, icon_state = "chain_s3")
if(growing)
visible_message("\The [src] noticeably grows in size.")
else
visible_message("\The [src] shrinks to a less powerful size.")
if(STAGE_THREE)
current_size = 5
icon = 'icons/effects/160x160.dmi'
pixel_x = -64 * PIXEL_MULTIPLIER
pixel_y = -64 * PIXEL_MULTIPLIER
bound_width = 5 * WORLD_ICON_SIZE
bound_x = -2 * WORLD_ICON_SIZE
bound_height = 5 * WORLD_ICON_SIZE
bound_y = -2 * WORLD_ICON_SIZE
grav_pull = 8
consume_range = 2
dissipate_delay = 4
dissipate_track = 0
dissipate_strength = 20
overlays = 0
if(chained)
overlays += image(icon = icon, icon_state = "chain_s5")
if(growing)
visible_message("\The [src] expands to a reasonable size.")
else
visible_message("\The [src] has returned to a safe size.")
if(STAGE_FOUR)
current_size = 7
icon = 'icons/effects/224x224.dmi'
pixel_x = -96 * PIXEL_MULTIPLIER
pixel_y = -96 * PIXEL_MULTIPLIER
bound_width = 7 * WORLD_ICON_SIZE
bound_x = -3 * WORLD_ICON_SIZE
bound_height = 7 * WORLD_ICON_SIZE
bound_y = -3 * WORLD_ICON_SIZE
grav_pull = 10
consume_range = 3
dissipate_delay = 10
dissipate_track = 0
dissipate_strength = 10
overlays = 0
if(chained)
overlays += image(icon = icon, icon_state = "chain_s7")
if(growing)
visible_message("\The [src] expands to a dangerous size.")
else
visible_message("Miraculously, \the [src] shrinks back to a containable size.")
if(STAGE_FIVE)
current_size = 9
icon = 'icons/effects/288x288.dmi'
pixel_x = -128 * PIXEL_MULTIPLIER
pixel_y = -128 * PIXEL_MULTIPLIER
bound_width = 9 * WORLD_ICON_SIZE
bound_x = -4 * WORLD_ICON_SIZE
bound_height = 9 * WORLD_ICON_SIZE
bound_y = -4 * WORLD_ICON_SIZE
grav_pull = 10
consume_range = 4
dissipate = 0 //It cant go smaller due to energy loss.
overlays = 0
if(chained)
overlays += image(icon = icon, icon_state = "chain_s9")
if(growing)
visible_message("\The [src] has grown out of control!")
else
visible_message("\The [src] miraculously shrinks and loses its supermatter properties.")
//Literally the only case where it should do that, can only be done by adminbus, so just reset its name and desc to default
name = initial(name)
desc = initial(desc)
if(STAGE_SUPER) //SUPERSINGULO
name = "super [name]" //Super version of whatever it was named. Shouldn't fire more than once
desc = "The final form of Lord Singuloth. It has the power to destroy worlds. It can most likely still be used to power arcades too, if you dare."
current_size = 11
icon = 'icons/effects/352x352.dmi'
pixel_x = -160 * PIXEL_MULTIPLIER
pixel_y = -160 * PIXEL_MULTIPLIER
bound_width = 11 * WORLD_ICON_SIZE
bound_x = -5 * WORLD_ICON_SIZE
bound_height = 11 * WORLD_ICON_SIZE
bound_y = -5 * WORLD_ICON_SIZE
grav_pull = 16
consume_range = 5
dissipate = 0 //It cant go smaller due to e loss
event_chance = 25 //Events will fire off more often.
if(chained)
overlays += image(icon = icon, icon_state = "chain_s9")
visible_message("You witness the creation of a destructive force that cannot possibly be stopped by human hands.")
if(STAGE_SSGSS) //SUPER SINGULO GOD SUPER SINGULO
name = "[name] god [name]" //it gets worse
desc = "The true final form of Lord Singuloth. It has the power to destroy galaxies. It can most likely still be used to power arcades too, if you dare."
current_size = 13
icon = 'icons/effects/384x384.dmi'
pixel_x = -192 * PIXEL_MULTIPLIER
pixel_y = -192 * PIXEL_MULTIPLIER
bound_width = 13 * WORLD_ICON_SIZE
bound_x = -6 * WORLD_ICON_SIZE
bound_height = 13 * WORLD_ICON_SIZE
bound_y = -6 * WORLD_ICON_SIZE
grav_pull = 24
consume_range = 5
plane = 21
visible_message("You witness the creation of a destructive force that challenges that of the very gods.")
if(current_size == allowed_size)
investigation_log(I_SINGULO,"grew to size [current_size].")
return 1
else if(current_size < (--temp_allowed_size) && current_size < 11)
expand(temp_allowed_size)
else
return 0
/obj/machinery/singularity/proc/check_energy()
if(energy <= 0)
investigation_log(I_SINGULO, "collapsed.")
qdel(src)
return 0
switch(energy) //Some of these numbers might need to be changed up later -Mport.
if(1 to 199)
allowed_size = 1
if(200 to 499)
allowed_size = 3
if(500 to 999)
allowed_size = 5
if(1000 to 1999)
allowed_size = 7
if(2000 to INFINITY)
allowed_size = 9
if(current_size != allowed_size && current_size < 11)
if(current_size > allowed_size)
expand(null, 0)
else
expand(null, 1)
if(icon_state != modifier + "singularity_s[current_size]")
icon_state = modifier + "singularity_s[current_size]"
return 1
/obj/machinery/singularity/proc/eat()
var/ngrabbed=0
var/turf/T = get_turf(src)
for(var/z0 in GetOpenConnectedZlevels(T))
var/z_dist = abs(z0 - T.z)
if(z_dist <= grav_pull)
for(var/atom/X in orange(grav_pull - z_dist, z0 == T.z ? src : locate(T.x,T.y,z0)))
if(X.type == /atom/movable/lighting_overlay)//since there's one on every turf
continue
if (current_size > 11 && X.type == /turf/unsimulated/wall/supermatter) // galaxy end ongoing
continue
try
var/dist = get_z_dist(X, src)
if(dist > consume_range || repels)
X.singularity_pull(src, current_size, repels)
else if(dist <= consume_range)
if(consume(X))
ngrabbed++
catch(var/exception/e)
error("Singularity eat() caught exception:")
error(e)
spawn(0) //So the following line doesn't stop execution
throw e //So ALL debug information is sent to the runtime log
continue
if(ngrabbed)
investigation_log(I_SINGULO, "at ([src.x],[src.y],[src.z]) ([get_area(src)]) eat() ate [ngrabbed] item[ngrabbed != 1 ? "s" : ""].")
/*
* Singulo optimization.
* Jump out whenever we've made a decision.
*/
/obj/machinery/singularity/proc/canPull(const/atom/movable/A)
if(A && !A.anchored)
if(A.canSingulothPull(src))
return 1
return 0
/obj/machinery/singularity/proc/isGodSingulo()
if(current_size == STAGE_SSGSS)
return 1
return 0
/obj/machinery/singularity/proc/makeSuperMatterSea(atom/A)
if(isturf(A.loc))
var/turf/newsea = A.loc
if(!istype(newsea, /turf/unsimulated/wall/supermatter))
newsea.ChangeTurf(/turf/unsimulated/wall/supermatter)
/obj/machinery/singularity/proc/consume(const/atom/A)
if(A?.reagents?.has_reagent(HOLYWATER)) // for removing arcane tampers
bless()
if(repels && !(istype(A,/obj/item/bluespace_crystal)))
return
if(wormhole_out && !(istype(A,/obj/item/bluespace_crystal)))
var/turf/T = get_turf(wormhole_out)
do_teleport(A, T)
else
var/gain = A.singularity_act(current_size,src)
src.energy += gain
return gain
/*
* Some modifications have been done in here. The Singularity's movement is now biased instead of truly random
* This means that if it isn't influcenced by a beacon, it will prefer the direction it last moved to
* In general, it's last movement has a 3/4th chance of being the next
*/
/obj/machinery/singularity/proc/move(var/force_move = 0)
if(!move_self && !force_move)
return 0
var/movement_dir = pick(alldirs - last_failed_movement)
if(force_move) //We are forcing the Singularity to move in a particular direction
movement_dir = force_move //Go this way
if(!force_move && target && prob(66)) //Otherwise we have a singularity beacon online
movement_dir = get_dir(src,target) //Moves to a singulo beacon, if there is one
if(!force_move && !target && last_failed_movement != last_movement_dir && prob(66)) //Otherwise we will perform a biased movement
movement_dir = last_movement_dir
last_movement_dir = movement_dir //We have chosen our direction, log it
if(current_size >= 9) //The superlarge one does not care about things in its way
set_glide_size(DELAY2GLIDESIZE(SS_WAIT_MACHINERY/2), min = 0)
spawn(0)
step(src, movement_dir)
spawn(SS_WAIT_MACHINERY/2)
step(src, movement_dir)
if(isGodSingulo())
makeSuperMatterSea(src)
return 1
else if(check_turfs_in(movement_dir))
last_failed_movement = 0 //Reset this because we moved
spawn(0)
set_glide_size(DELAY2GLIDESIZE(SS_WAIT_MACHINERY), min = 0)
step(src, movement_dir)
if(isGodSingulo())
makeSuperMatterSea(src)
return 1
else
last_failed_movement = movement_dir
return 0
/obj/machinery/singularity/proc/check_turfs_in(var/direction = 0, var/step = 0, var/startturf)
if(!direction)
return 0
var/steps = 0
if(!step)
steps = Ceiling(current_size/2)
else
steps = step
var/list/turfs = list()
var/turf/T
if(startturf)
T = get_turf(startturf)
else
T = get_turf(src)
for(var/i = 1 to steps)
T = get_step(T, direction)
if(!isturf(T))
return 0
turfs.Add(T)
var/dir2 = 0
var/dir3 = 0
switch(direction)
if(NORTH, SOUTH)
dir2 = 4
dir3 = 8
if(EAST, WEST)
dir2 = 1
dir3 = 2
var/turf/T2 = T
for(var/j = 1 to steps)
T2 = get_step(T2, dir2)
if(!isturf(T2))
return 0
turfs.Add(T2)
for(var/k = 1 to steps)
T = get_step(T, dir3)
if(!isturf(T))
return 0
turfs.Add(T)
for(var/turf/T3 in turfs)
if(isnull(T3))
continue
if(!can_move(T3))
return 0
return 1
/obj/machinery/singularity/proc/can_move(const/turf/T)
if(!isturf(T))
return 0
if((locate(/obj/machinery/containment_field) in T) || (locate(/obj/machinery/shieldwall) in T))
return 0
else if(locate(/obj/machinery/field_generator) in T)
var/obj/machinery/field_generator/G = locate(/obj/machinery/field_generator) in T
if(G && G.active)
return 0
else if(locate(/obj/machinery/shieldwallgen) in T)
var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T
if(S && S.active)
return 0
return 1
/obj/machinery/singularity/proc/is_near_shield()
for(var/dir in cardinal)
if(!check_turfs_in(dir))
return 1
return 0
/obj/machinery/singularity/proc/move_away_from_shield()
var/list/dirs_to_try = alldirs.Copy()
dirs_while_label:
while(dirs_to_try.len)
var/checkdir = pick(dirs_to_try)
if(!check_turfs_in(checkdir))
dirs_to_try -= checkdir
continue
var/newturf = get_step(src,checkdir)
for(var/dir in cardinal)
if(!check_turfs_in(dir, startturf = newturf))
dirs_to_try -= checkdir
continue dirs_while_label
step(src, checkdir)
return 1
return 0
/obj/machinery/singularity/proc/event()
var/numb = pick(1, 2, 3, 4, 5, 6)
switch(numb)
if(1) //EMP.
emp_area()
if(2, 3) //Tox damage all carbon mobs in area.
toxmob()
if(4) //Stun mobs who lack optic scanners.
mezzer()
else
return 0
if(current_size > 9)
smwave()
return 1
/obj/machinery/singularity/proc/toxmob()
var/toxrange = 10
var/toxdamage = 4
if(src.energy > 200)
toxdamage = round(((src.energy-150)/50)*4,1)
for(var/mob/living/M in view(toxrange, src.loc))
if(M.flags & INVULNERABLE)
continue
toxdamage = (toxdamage - (toxdamage*M.getarmor(null, "rad")))
M.apply_effect(toxdamage, TOX)
return
/obj/machinery/singularity/proc/mezzer()
for(var/mob/living/carbon/M in oviewers(8, src))
if(istype(M, /mob/living/carbon/brain)) //Ignore brains
continue
if(M.flags & INVULNERABLE)
continue
if(M.stat == CONSCIOUS)
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
if((H.hasHUD(HUD_MESON) || istype(H.glasses, /obj/item/clothing/glasses/scanner/meson) || istype(H.glasses, /obj/item/clothing/glasses/scanner/dual/chiefengineer)) && current_size < 11)
to_chat(H, "You stare directly into \the [src], good thing you had your protective eyewear on!")
return
else
to_chat(H, "You stare directly into \the [src] but your eyewear does absolutely nothing to protect you from it!")
M.visible_message("[M] stares blankly at \the [src]!", \
"You stare directly into \the [src] and feel [current_size > 9 ? "helpless" : "weak"].")
M.apply_effect(3, STUN)
/obj/machinery/singularity/proc/emp_area()
if(current_size < 11)
empulse(src, 8, 10)
else
empulse(src, 12, 16)
/obj/machinery/singularity/proc/smwave()
for(var/mob/living/M in view(10, src.loc))
if(prob(67))
M.apply_radiation(rand(energy), RAD_EXTERNAL)
to_chat(M, "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.")
to_chat(M, "Miraculously, it fails to kill you.")
else
to_chat(M, "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.")
to_chat(M, "You don't even have a moment to react as you are reduced to ashes by the intense radiation.")
M.dust()
return
/obj/machinery/singularity/proc/pulse()
emitted_harvestable_radiation(get_turf(src), energy, range = 15)
/obj/machinery/singularity/proc/on_capture()
chained = 1
overlays = 0
move_self = 0
switch(current_size)
if(1)
overlays += image('icons/obj/singularity.dmi',"chain_s1")
if(3)
overlays += image('icons/effects/96x96.dmi',"chain_s3")
if(5)
overlays += image('icons/effects/160x160.dmi',"chain_s5")
if(7)
overlays += image('icons/effects/224x224.dmi',"chain_s7")
if(9)
overlays += image('icons/effects/288x288.dmi',"chain_s9")
/obj/machinery/singularity/proc/on_release()
chained = 0
overlays = 0
move_self = 1
/obj/machinery/singularity/cultify()
var/dist = max((current_size - 2), 1)
explosion(get_turf(src), dist, dist * 2, dist * 4)
qdel(src)
/obj/machinery/singularity/singularity_act(var/other_size=0,var/obj/machinery/singularity/S)
if(S == src) //don't eat yourself idiot
return
if(other_size >= current_size)
var/gain = (energy/2)
var/dist = max((current_size - 2), 1)
explosion(src.loc,(dist),(dist*2),(dist*4))
qdel(src)
return(gain)
/obj/machinery/singularity/shuttle_act() //Shuttles can't kill the singularity honk
return
/*
/obj/machinery/singularity/can_shuttle_move() //The days of destroying centcomm are gone
return
*/ //Fuck you centcomm
/obj/machinery/singularity/Destroy()
unlink_wormholes()
..()
power_machines -= src
global_singularity_pool -= src
white_hole_candidates -= src
/obj/machinery/singularity/bite_act(mob/user)
consume(user)
/obj/machinery/singularity/kick_act(mob/user)
consume(user)
/obj/machinery/singularity/dissolvable()
return 0
/obj/machinery/singularity/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, glide_size_override = 0)
if(timestopped)
return 0
return forceMove(get_step(src,Dir))
////////////This singularity is upgraded to be controlled by deadchat. God save us all.
/datum/deadchat_listener/singulo_listener
name = "deadchat-controlled singularity listener"
var/obj/machinery/singularity/deadchat_controlled/parent
/datum/deadchat_listener/singulo_listener/deadchat_event(var/ckey, var/message)
parent.process_deadchat(ckey,message)
/obj/machinery/singularity/deadchat_controlled
desc = "The destructive, murderous Lord Singuloth, patron saint of Engineering. This one seems... unstable. Oh god."
var/deadchat_mode = "Anarchy"
var/list/ckey_to_cooldown = list()
var/datum/deadchat_listener/singulo_listener/listener
move_self = 0
var/input_cooldown = 60 //In deca-seconds
var/democracy_cooldown = 120
var/list/inputs = list("UP","DOWN","LEFT","RIGHT")
var/deadchat_active = 1
appearance_flags = TILE_MOVER
/obj/machinery/singularity/deadchat_controlled/Destroy()
..()
var/message = "The deadchat-played singularity has been destroyed. Good job, retards."
deadchat_active=0
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)
global_deadchat_listeners -= listener
global_singularity_pool -= src
qdel(listener)
/obj/machinery/singularity/deadchat_controlled/New(loc, var/starting_energy = 50, var/temp = 0)
..()
listener = new /datum/deadchat_listener/singulo_listener
listener.parent = src
global_deadchat_listeners += listener
global_singularity_pool -= src
/obj/machinery/singularity/deadchat_controlled/proc/process_deadchat(var/ckey, var/message)
if(deadchat_mode == "Anarchy")
var/cooldown = ckey_to_cooldown[ckey]
if(!cooldown)
ckey_to_cooldown[ckey] = 0
cooldown = 0
if(cooldown > 0)
return
var/direction
message = uppertext(message)
switch(message) //*shrug
if("UP")
direction = NORTH
if("DOWN")
direction = SOUTH
if("LEFT")
direction = WEST
if("RIGHT")
direction = EAST
if(direction)
set_glide_size(DELAY2GLIDESIZE(0.1 SECONDS))
forceMove(get_step(src,direction))
eat_no_pull()
ckey_to_cooldown[ckey] = 1
spawn(input_cooldown)
ckey_to_cooldown[ckey] = 0
else if(deadchat_mode == "Democracy")
var/vote = ckey_to_cooldown[ckey]
if(!vote)
ckey_to_cooldown[ckey] = 0
vote = -1
message = uppertext(message)
if(inputs.Find(message))
ckey_to_cooldown[ckey] = message
/obj/machinery/singularity/deadchat_controlled/proc/eat_no_pull() //Copied from proc/eat() and altered
for(var/atom/X in orange(consume_range, src))
if(X.type == /atom/movable/lighting_overlay)
continue
if(current_size > 11 && X.type == /turf/unsimulated/wall/supermatter)
continue
consume(X)
/obj/machinery/singularity/deadchat_controlled/proc/begin_democracy_loop()
if(democracy_cooldown < 1)
democracy_cooldown = 1 //setting it to 0 kills the serb so let's not ever let that happen again
spawn(democracy_cooldown)
if(!deadchat_active) //Bit gunky but I'm not entirely certain how src/self works in byond, would if(src == null) work?
return
var/result = count_democracy_votes()
if(result != 5)
set_glide_size(DELAY2GLIDESIZE(0.1 SECONDS))
forceMove(get_step(src,result))
eat()
var/direction_name = "up"
switch(result)
if(2)
direction_name = "down"
if(3)
direction_name = "left"
if(4)
direction_name = "right"
var/message = "The singularity moved [direction_name]!.
New vote started. It will end in [democracy_cooldown/10] seconds." //There should really be a proc for sending messages to deadchat but I'm too lazy to copy/paste it
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)
else
var/message = "No votes were cast this cycle. Remember, type UP, DOWN, LEFT, or RIGHT to cast a vote!"
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)
begin_democracy_loop()
/obj/machinery/singularity/deadchat_controlled/proc/count_democracy_votes() //Will return 5 if empty list
var/list/votes = list(0,0,0,0)
var/found_vote = 0
for(var/vote in ckey_to_cooldown)
switch(ckey_to_cooldown[vote])
if("UP")
votes[1]++
if("DOWN")
votes[2]++
if("LEFT")
votes[3]++
if("RIGHT")
votes[4]++
if(ckey_to_cooldown[vote] != -1)
found_vote = 1
ckey_to_cooldown[vote] = -1
if(!found_vote)
return 5
if(votes[1] >= votes[2] && votes[1] >= votes[3] && votes[1] >= votes[4])
return NORTH
else if(votes[2] >= votes[3] && votes[2] >= votes[4])
return SOUTH
else if(votes[3] >= votes[4])
return WEST
else
return EAST
/client/proc/deadchat_singularity()
set category = "Fun"
set name = "Spawn Deadchat-Controlled Singularity"
if(!src.holder)
return 0
if(!global_singularity_pool.len)
return 0
if(!holder.rights || !check_rights(R_FUN,0))
to_chat(holder, "They (you) do it for free, yet they (you) still don't have R_FUN perms... sad!")
return 0
var/list/organized_list = list()
for(var/obj/machinery/singularity/singularity in global_singularity_pool)
var/organized_hash = "[singularity] - [singularity.x], [singularity.y], [singularity.z]"
organized_list[organized_hash] = singularity
if(!global_singularity_pool.len)
to_chat(holder, "There are no singularities to be transformed into a deadchat-controlled one. Spawn one first... if you dare.")
return 0
var/singulo_name = input(src,"Select a singularity.", "Confirm", null) as null|anything in organized_list
var/obj/machinery/singularity/target_singulo = organized_list[singulo_name]
if(target_singulo)
var/list/singulo_options = list("Democracy","Anarchy")
var/option_chosen = input(src,"Choose a mode.", "Confirm", null) as null|anything in singulo_options
if(option_chosen == "Anarchy")
var/cooldown = input("Please enter the cooldown each player has in seconds.", "Cooldown") as num
if(!cooldown)
return 0
cooldown *= 10 //Decasecond conversion
log_admin("[src] just turned the [singulo_name] into a deadchat-controlled one.")
message_admins("[src] just turned the [singulo_name] into a deadchat-controlled one.")
target_singulo.investigation_log(I_SINGULO,"[src] just turned the [singulo_name] into a deadchat-controlled one. It is on anarchy mode, cooldown [cooldown] decaseconds. If you're reading this, god save the deadmin..")
var/obj/machinery/singularity/deadchat_controlled/new_singulo = new /obj/machinery/singularity/deadchat_controlled(get_turf(target_singulo))
new_singulo.energy = target_singulo.energy
new_singulo.allowed_size = target_singulo.allowed_size
new_singulo.expand(null, 0)
new_singulo.input_cooldown = cooldown
if(target_singulo.current_size >= STAGE_SUPER)
new_singulo.expand(target_singulo.current_size, 1)
qdel(target_singulo)
var/message = "An admin has begun DEADCHAT-CONTROLLED SINGULARITY!
It is on ANARCHY mode.
Simply type UP, DOWN, LEFT, or RIGHT to move the singularity.
Cooldown per person is currently [new_singulo.input_cooldown/10] seconds.
"
for(var/mob/M in get_deadchat_hearers())
to_chat(M, message + formatFollow(new_singulo))
else if(option_chosen == "Democracy")
var/interval = input("Please enter the interval that the singulo makes a move in seconds.", "Interval") as num
if(!interval)
return 0
interval *= 10 //Decasecond conversion
if(interval < 10)
interval = 10
log_admin("[src] just turned the [singulo_name] into a deadchat-controlled one.")
message_admins("[src] just turned the [singulo_name] into a deadchat-controlled one.")
target_singulo.investigation_log(I_SINGULO,"[src] just turned the [singulo_name] into a deadchat-controlled one. It is on democracy mode, cooldown [interval] decaseconds. If you're reading this, god save the deadmin..")
var/obj/machinery/singularity/deadchat_controlled/new_singulo = new /obj/machinery/singularity/deadchat_controlled(get_turf(target_singulo))
new_singulo.energy = target_singulo.energy
new_singulo.allowed_size = target_singulo.allowed_size
new_singulo.expand(null, 0)
new_singulo.democracy_cooldown = interval
new_singulo.deadchat_mode = "Democracy"
new_singulo.begin_democracy_loop()
if(target_singulo.current_size >= STAGE_SUPER)
new_singulo.expand(target_singulo.current_size, 1)
qdel(target_singulo)
var/message = "An admin has begun DEADCHAT-CONTROLLED SINGULARITY!
It is on DEMOCRACY mode.
Simply type UP, DOWN, LEFT, or RIGHT to cast a vote on which direction it should move. Your vote will be your latest message.
The singulo will move every [new_singulo.democracy_cooldown/10] seconds. Votes start now!
"
for(var/mob/M in get_deadchat_hearers())
to_chat(M, message + formatFollow(new_singulo))
/obj/machinery/singularity/special
name = "specialarity"
modifier = "special_"
/obj/machinery/singularity/scrungulartiy
name = "grabibational scrungulartiy"
modifier = "scrung_"
/obj/machinery/singularity/soutgularity
name = "white hole"
desc = "Every action has an equal and opposite reaction. A black hole sucks time and space out of the universe, a white hole returns it."
repels = TRUE
color= list(-1,0,0,
0,-1,0,
0,0,-1,
1,1,1) //Invert it