[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:
SkyratBot
2020-12-08 11:30:39 +00:00
committed by GitHub
co-authored by Jared-Fogle
parent bd29550b77
commit f105b47a55
30 changed files with 749 additions and 514 deletions
+26 -30
View File
@@ -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)
+181 -163
View File
@@ -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
+67 -168
View File
@@ -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
@@ -467,7 +467,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
else if(power > POWER_PENALTY_THRESHOLD)
investigate_log("has spawned additional energy balls.", INVESTIGATE_SUPERMATTER)
if(T)
var/obj/singularity/energy_ball/E = new(T)
var/obj/energy_ball/E = new(T)
E.energy = 200 //Gets us about 9 balls
else if(power > EVENT_POWER_PENALTY_THRESHOLD && prob(power/50) && !istype(src, /obj/machinery/power/supermatter_crystal/shard))
var/datum/round_event_control/crystal_invasion/crystals = new/datum/round_event_control/crystal_invasion
@@ -1008,7 +1008,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
user.dust(force = TRUE)
if(power_changes)
matter_power += 200
else if(istype(AM, /obj/singularity))
else if(AM.flags_1 & SUPERMATTER_IGNORES_1)
return
else if(isobj(AM))
if(!iseffect(AM))
+76 -71
View File
@@ -10,22 +10,26 @@
#define BLOB (STRUCTURE + 1)
#define STRUCTURE (1)
/obj/singularity/energy_ball
/// The Tesla engine
/obj/energy_ball
name = "energy ball"
desc = "An energy ball."
icon = 'icons/obj/tesla_engine/energy_ball.dmi'
icon_state = "energy_ball"
anchored = TRUE
appearance_flags = LONG_GLIDE
density = TRUE
layer = MASSIVE_OBJ_LAYER
light_range = 6
move_resist = INFINITY
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
pixel_x = -32
pixel_y = -32
current_size = STAGE_TWO
move_self = 1
grav_pull = 0
contained = 0
density = TRUE
energy = 0
dissipate = FALSE
dissipate_delay = 5
dissipate_strength = 1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
flags_1 = SUPERMATTER_IGNORES_1
var/energy
var/target
var/list/orbiting_balls = list()
var/miniball = FALSE
var/produced_power
@@ -33,39 +37,37 @@
var/energy_to_lower = -20
var/list/shocked_things = list()
/obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE)
miniball = is_miniball
/obj/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE)
. = ..()
if(!is_miniball)
energy = starting_energy
miniball = is_miniball
START_PROCESSING(SSobj, src)
if (!is_miniball)
set_light(10, 7, "#5e5edd")
/obj/singularity/energy_ball/ex_act(severity, target)
return
var/turf/spawned_turf = get_turf(src)
message_admins("A tesla has been created at [ADMIN_VERBOSEJMP(spawned_turf)].")
investigate_log("(tesla) was created at [AREACOORD(spawned_turf)].", INVESTIGATE_SINGULO)
/obj/singularity/energy_ball/consume(severity, target)
return
/obj/energy_ball/Destroy()
if(orbiting && istype(orbiting.parent, /obj/energy_ball))
var/obj/energy_ball/parent_energy_ball = orbiting.parent
parent_energy_ball.orbiting_balls -= src
/obj/singularity/energy_ball/Destroy()
if(orbiting && istype(orbiting.parent, /obj/singularity/energy_ball))
var/obj/singularity/energy_ball/EB = orbiting.parent
EB.orbiting_balls -= src
QDEL_LIST(orbiting_balls)
STOP_PROCESSING(SSobj, src)
for(var/ball in orbiting_balls)
var/obj/singularity/energy_ball/EB = ball
QDEL_NULL(EB)
. = ..()
return ..()
/obj/singularity/energy_ball/admin_investigate_setup()
if(miniball)
return //don't annnounce miniballs
..()
/obj/singularity/energy_ball/process()
if(!orbiting)
/obj/energy_ball/process()
if(orbiting)
energy = 0 // ensure we dont have miniballs of miniballs
else
handle_energy()
move_the_basket_ball(4 + orbiting_balls.len * 1.5)
move(4 + orbiting_balls.len * 1.5)
playsound(src.loc, 'sound/magic/lightningbolt.ogg', 100, TRUE, extrarange = 30)
@@ -81,40 +83,46 @@
var/list/temp_shock = list()
tesla_zap(ball, range, TESLA_MINI_POWER/7*range, shocked_targets = temp_shock)
shocked_things += temp_shock
else
energy = 0 // ensure we dont have miniballs of miniballs //But it'll be cool broooooooooooooooo
/obj/singularity/energy_ball/examine(mob/user)
/obj/energy_ball/examine(mob/user)
. = ..()
if(orbiting_balls.len)
. += "There are [orbiting_balls.len] mini-balls orbiting it."
/obj/singularity/energy_ball/proc/move_the_basket_ball(move_amount)
/obj/energy_ball/proc/move(move_amount)
var/list/dirs = GLOB.alldirs.Copy()
for(var/I in 1 to 30)
for (var/i in 1 to 30)
var/atom/real_thing = pick(shocked_things)
dirs += get_dir(src, real_thing) //Carry some momentum yeah? Just a bit tho
for(var/i in 0 to move_amount)
for (var/i in 0 to move_amount)
var/move_dir = pick(dirs) //ensures teslas don't just sit around
if(target && prob(10))
move_dir = get_dir(src,target)
var/turf/T = get_step(src, move_dir)
if(can_move(T))
forceMove(T)
if (target && prob(10))
move_dir = get_dir(src, target)
var/turf/turf_to_move = get_step(src, move_dir)
if (can_move(turf_to_move))
forceMove(turf_to_move)
setDir(move_dir)
for(var/mob/living/carbon/C in loc)
dust_mobs(C)
for (var/mob/living/carbon/mob_to_dust in loc)
dust_mobs(mob_to_dust)
/obj/energy_ball/proc/can_move(turf/to_move)
if (!to_move)
return FALSE
/obj/singularity/energy_ball/proc/handle_energy()
for (var/_thing in to_move)
var/atom/thing = _thing
if (SEND_SIGNAL(thing, COMSIG_ATOM_SINGULARITY_TRY_MOVE) & SINGULARITY_TRY_MOVE_BLOCK)
return FALSE
return TRUE
/obj/energy_ball/proc/handle_energy()
if(energy >= energy_to_raise)
energy_to_lower = energy_to_raise - 20
energy_to_raise = energy_to_raise * 1.25
playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, TRUE, extrarange = 30)
addtimer(CALLBACK(src, .proc/new_mini_ball), 100)
else if(energy < energy_to_lower && orbiting_balls.len)
energy_to_raise = energy_to_raise / 1.25
energy_to_lower = (energy_to_raise / 1.25) - 20
@@ -122,31 +130,31 @@
var/Orchiectomy_target = pick(orbiting_balls)
qdel(Orchiectomy_target)
else if(orbiting_balls.len)
dissipate() //sing code has a much better system.
/obj/singularity/energy_ball/proc/new_mini_ball()
/obj/energy_ball/proc/new_mini_ball()
if(!loc)
return
var/obj/singularity/energy_ball/EB = new(loc, 0, TRUE)
EB.transform *= pick(0.3, 0.4, 0.5, 0.6, 0.7)
var/icon/I = icon(icon,icon_state,dir)
var/obj/energy_ball/miniball = new /obj/energy_ball(
loc,
/* starting_energy = */ 0,
/* is_miniball = */ TRUE
)
miniball.transform *= pick(0.3, 0.4, 0.5, 0.6, 0.7)
var/icon/I = icon(icon, icon_state,dir)
var/orbitsize = (I.Width() + I.Height()) * pick(0.4, 0.5, 0.6, 0.7, 0.8)
orbitsize -= (orbitsize / world.icon_size) * (world.icon_size * 0.25)
EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36))
miniball.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36))
/obj/singularity/energy_ball/Bump(atom/A)
/obj/energy_ball/Bump(atom/A)
dust_mobs(A)
/obj/singularity/energy_ball/Bumped(atom/movable/AM)
/obj/energy_ball/Bumped(atom/movable/AM)
dust_mobs(AM)
/obj/singularity/energy_ball/attack_tk(mob/user)
/obj/energy_ball/attack_tk(mob/user)
if(!iscarbon(user))
return
var/mob/living/carbon/jedi = user
@@ -158,25 +166,22 @@
jedi.death()
return COMPONENT_CANCEL_ATTACK_CHAIN
/obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target)
/obj/energy_ball/orbit(obj/energy_ball/target)
if (istype(target))
target.orbiting_balls += src
GLOB.poi_list -= src
target.dissipate_strength = target.orbiting_balls.len
. = ..()
/obj/singularity/energy_ball/stop_orbit()
if (orbiting && istype(orbiting.parent, /obj/singularity/energy_ball))
var/obj/singularity/energy_ball/orbitingball = orbiting.parent
/obj/energy_ball/stop_orbit()
if (orbiting && istype(orbiting.parent, /obj/energy_ball))
var/obj/energy_ball/orbitingball = orbiting.parent
orbitingball.orbiting_balls -= src
orbitingball.dissipate_strength = orbitingball.orbiting_balls.len
. = ..()
if (!QDELETED(src))
qdel(src)
/obj/singularity/energy_ball/proc/dust_mobs(atom/A)
/obj/energy_ball/proc/dust_mobs(atom/A)
if(isliving(A))
var/mob/living/L = A
if(L.incorporeal_move || L.status_flags & GODMODE)