mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 07:38:05 +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:
@@ -102,6 +102,8 @@
|
||||
#define COMSIG_ATOM_USED_IN_CRAFT "atom_used_in_craft"
|
||||
///from base of atom/blob_act(): (/obj/structure/blob)
|
||||
#define COMSIG_ATOM_BLOB_ACT "atom_blob_act"
|
||||
/// if returned, forces nothing to happen when the atom is attacked by a blob
|
||||
#define COMPONENT_CANCEL_BLOB_ACT (1<<0)
|
||||
///from base of atom/acid_act(): (acidpwr, acid_volume)
|
||||
#define COMSIG_ATOM_ACID_ACT "atom_acid_act"
|
||||
///from base of atom/emag_act(): (/mob/user)
|
||||
@@ -112,7 +114,7 @@
|
||||
#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act"
|
||||
///from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode)
|
||||
#define COMSIG_ATOM_RCD_ACT "atom_rcd_act"
|
||||
///from base of atom/singularity_pull(): (S, current_size)
|
||||
///from base of atom/singularity_pull(): (/datum/component/singularity, current_size)
|
||||
#define COMSIG_ATOM_SING_PULL "atom_sing_pull"
|
||||
///from obj/machinery/bsa/full/proc/fire(): ()
|
||||
#define COMSIG_ATOM_BSA_BEAM "atom_bsa_beam_pass"
|
||||
@@ -193,6 +195,12 @@
|
||||
///called on /living when someone starts pulling it (atom/movable/puller, state, force)
|
||||
#define COMSIG_LIVING_START_PULL "living_start_pull"
|
||||
|
||||
/// from /datum/component/singularity/proc/can_move(), as well as /obj/energy_ball/proc/can_move()
|
||||
/// if a callback returns `SINGULARITY_TRY_MOVE_BLOCK`, then the singularity will not move to that turf
|
||||
#define COMSIG_ATOM_SINGULARITY_TRY_MOVE "atom_singularity_try_move"
|
||||
/// When returned from `COMSIG_ATOM_SINGULARITY_TRY_MOVE`, the singularity will move to that turf
|
||||
#define SINGULARITY_TRY_MOVE_BLOCK (1 << 0)
|
||||
|
||||
/////////////////
|
||||
|
||||
///from base of area/Entered(): (/area)
|
||||
|
||||
@@ -48,6 +48,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
#define UNPAINTABLE_1 (1 << 20)
|
||||
/// Is the thing currently spinning?
|
||||
#define IS_SPINNING_1 (1 << 21)
|
||||
/// Does the supermatter skip over this atom?
|
||||
#define SUPERMATTER_IGNORES_1 (1 << 22)
|
||||
|
||||
/// If the thing can reflect light (lasers/energy)
|
||||
#define RICOCHET_SHINY (1<<0)
|
||||
|
||||
@@ -9,7 +9,7 @@ GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report
|
||||
|
||||
|
||||
//TODO clear this one up too
|
||||
GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
|
||||
GLOBAL_DATUM(cult_narsie, /obj/narsie)
|
||||
|
||||
///We want reality_smash_tracker to exist only once and be accesable from anywhere.
|
||||
GLOBAL_DATUM_INIT(reality_smash_track, /datum/reality_smash_tracker, new)
|
||||
|
||||
@@ -13,7 +13,7 @@ GLOBAL_LIST_EMPTY(deliverybeacontags) //list of all tags associated with d
|
||||
GLOBAL_LIST_EMPTY(wayfindingbeacons) //list of all navigation beacons used by wayfinding pinpointers
|
||||
GLOBAL_LIST_EMPTY(nuke_list)
|
||||
GLOBAL_LIST_EMPTY(alarmdisplay) //list of all machines or programs that can display station alerts
|
||||
GLOBAL_LIST_EMPTY(singularities) //list of all singularities on the station (actually technically all engines)
|
||||
GLOBAL_LIST_EMPTY_TYPED(singularities, /datum/component/singularity) //list of all singularities on the station
|
||||
GLOBAL_LIST_EMPTY(mechpad_list) //list of all /obj/machinery/mechpad
|
||||
|
||||
GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datums. Used during chemical reactions
|
||||
|
||||
@@ -130,7 +130,7 @@ SUBSYSTEM_DEF(traumas)
|
||||
/obj/item/melee/cultblade, /obj/item/cult_bastard,
|
||||
/obj/item/restraints/legcuffs/bola/cult, /obj/item/clothing/suit/space/hardsuit/cult,
|
||||
/obj/item/clothing/suit/hooded/cultrobes, /obj/item/clothing/head/hooded/cult_hoodie, /obj/effect/rune,
|
||||
/obj/item/stack/sheet/runed_metal, /obj/machinery/door/airlock/cult, /obj/singularity/narsie,
|
||||
/obj/item/stack/sheet/runed_metal, /obj/machinery/door/airlock/cult, /obj/narsie,
|
||||
/obj/item/soulstone,
|
||||
/obj/item/clothing/suit/wizrobe, /obj/item/clothing/head/wizard, /obj/item/spellbook, /obj/item/staff,
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/wizard, /obj/item/clothing/suit/space/hardsuit/wizard,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
var/static/list/falling_atoms = list() // Atoms currently falling into chasms
|
||||
var/static/list/forbidden_types = typecacheof(list(
|
||||
/obj/singularity,
|
||||
/obj/energy_ball,
|
||||
/obj/narsie,
|
||||
/obj/docking_port,
|
||||
/obj/structure/lattice,
|
||||
/obj/structure/stone_tile,
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
/// The range at which a singularity is considered "contained" to admins
|
||||
#define FIELD_CONTAINMENT_DISTANCE 30
|
||||
|
||||
/// What's the chance that, when a singularity moves, it'll go to its target?
|
||||
#define CHANCE_TO_MOVE_TO_TARGET 60
|
||||
|
||||
/// Things that maybe move around and does stuff to things around them
|
||||
/// Used for the singularity (duh) and Nar'Sie
|
||||
/datum/component/singularity
|
||||
/// Callback for consuming objects (for example, Nar'Sie replaces this to call narsie_act)
|
||||
var/datum/callback/consume_callback
|
||||
|
||||
/// The range to pull in stuff around it
|
||||
var/consume_range
|
||||
|
||||
/// Does this singularity move?
|
||||
var/roaming
|
||||
|
||||
/// The chosen direction to drift in
|
||||
var/drifting_dir
|
||||
|
||||
/// How many tiles out to pull in
|
||||
var/grav_pull
|
||||
|
||||
/// The last direction we failed to move in (for example: if we are contained)
|
||||
var/last_failed_movement
|
||||
|
||||
/// How big is the singularity?
|
||||
var/singularity_size
|
||||
|
||||
/// Should we disregard the possibility of failed movements? Used by stage five singularities
|
||||
var/disregard_failed_movements
|
||||
|
||||
/// Can this singularity be BSA'd?
|
||||
var/bsa_targetable
|
||||
|
||||
/// Should the admins be alerted when this is created?
|
||||
var/notify_admins
|
||||
|
||||
/// If specified, the singularity will slowly move to this target
|
||||
var/atom/target
|
||||
|
||||
/datum/component/singularity/Initialize(
|
||||
bsa_targetable = TRUE,
|
||||
consume_range = 0,
|
||||
consume_callback = CALLBACK(src, .proc/default_singularity_act),
|
||||
disregard_failed_movements = FALSE,
|
||||
grav_pull = 4,
|
||||
notify_admins = TRUE,
|
||||
singularity_size = STAGE_ONE,
|
||||
roaming = TRUE,
|
||||
)
|
||||
if (!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.bsa_targetable = bsa_targetable
|
||||
src.consume_callback = consume_callback
|
||||
src.consume_range = consume_range
|
||||
src.disregard_failed_movements = disregard_failed_movements
|
||||
src.grav_pull = grav_pull
|
||||
src.notify_admins = notify_admins
|
||||
src.roaming = roaming
|
||||
src.singularity_size = singularity_size
|
||||
|
||||
/datum/component/singularity/RegisterWithParent()
|
||||
START_PROCESSING(SSdcs, src)
|
||||
|
||||
// The singularity stops drifting for no man!
|
||||
parent.AddElement(/datum/element/forced_gravity, FALSE)
|
||||
|
||||
parent.AddElement(/datum/element/bsa_blocker)
|
||||
RegisterSignal(parent, COMSIG_ATOM_BSA_BEAM, .proc/bluespace_reaction)
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_BLOB_ACT, .proc/block_blob)
|
||||
|
||||
RegisterSignal(parent, list(
|
||||
COMSIG_ATOM_ATTACK_ANIMAL,
|
||||
COMSIG_ATOM_ATTACK_HAND,
|
||||
COMSIG_ATOM_ATTACK_PAW,
|
||||
), .proc/consume_attack)
|
||||
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/consume_attackby)
|
||||
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_PRE_MOVE, .proc/moved)
|
||||
RegisterSignal(parent, list(COMSIG_ATOM_BUMPED, COMSIG_MOVABLE_CROSSED), .proc/consume)
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_BULLET_ACT, .proc/consume_bullets)
|
||||
|
||||
if (notify_admins)
|
||||
admin_investigate_setup()
|
||||
|
||||
GLOB.singularities |= src
|
||||
|
||||
/datum/component/singularity/Destroy(force, silent)
|
||||
GLOB.singularities -= src
|
||||
QDEL_NULL(consume_callback)
|
||||
target = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/component/singularity/UnregisterFromParent()
|
||||
STOP_PROCESSING(SSdcs, src)
|
||||
|
||||
parent.RemoveElement(/datum/element/bsa_blocker)
|
||||
parent.RemoveElement(/datum/element/forced_gravity)
|
||||
|
||||
UnregisterSignal(parent, list(
|
||||
COMSIG_ATOM_ATTACK_ANIMAL,
|
||||
COMSIG_ATOM_ATTACK_HAND,
|
||||
COMSIG_ATOM_ATTACK_PAW,
|
||||
COMSIG_ATOM_BLOB_ACT,
|
||||
COMSIG_ATOM_BSA_BEAM,
|
||||
COMSIG_ATOM_BULLET_ACT,
|
||||
COMSIG_ATOM_BUMPED,
|
||||
COMSIG_MOVABLE_CROSSED,
|
||||
COMSIG_MOVABLE_PRE_MOVE,
|
||||
COMSIG_PARENT_ATTACKBY,
|
||||
))
|
||||
|
||||
/datum/component/singularity/process(delta_time)
|
||||
if (roaming)
|
||||
move()
|
||||
eat()
|
||||
|
||||
/datum/component/singularity/proc/block_blob()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
return COMPONENT_CANCEL_BLOB_ACT
|
||||
|
||||
/datum/component/singularity/proc/consume(datum/source, atom/thing)
|
||||
consume_callback?.Invoke(thing, src)
|
||||
|
||||
/datum/component/singularity/proc/consume_attack(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
consume(source, user)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/datum/component/singularity/proc/consume_attackby(datum/source, obj/item/item, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
consume(source, user)
|
||||
|
||||
// Will there be an impact? Who knows. Will we see it? No.
|
||||
/datum/component/singularity/proc/consume_bullets(obj/projectile/projectile)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
qdel(projectile)
|
||||
|
||||
/// Calls singularity_act on the thing passed, usually destroying the object
|
||||
/datum/component/singularity/proc/default_singularity_act(atom/thing)
|
||||
thing.singularity_act(singularity_size, parent)
|
||||
|
||||
/datum/component/singularity/proc/eat()
|
||||
var/atom/atom_parent = parent
|
||||
|
||||
for (var/_tile in spiral_range_turfs(grav_pull, parent))
|
||||
var/turf/tile = _tile
|
||||
if (!tile || !isturf(atom_parent.loc))
|
||||
continue
|
||||
if (get_dist(tile, parent) > consume_range)
|
||||
tile.singularity_pull(src, singularity_size)
|
||||
else
|
||||
consume(src, tile)
|
||||
|
||||
for (var/_thing in tile)
|
||||
var/atom/thing = _thing
|
||||
|
||||
// Because we can possibly yield in the middle of iteration, let's make sure what were looking at is still there
|
||||
// Without this, you get "Qdeleted thing being thrown around"
|
||||
if (QDELETED(thing))
|
||||
continue
|
||||
|
||||
if (isturf(atom_parent.loc) && thing != parent)
|
||||
var/atom/movable/movable_thing = thing
|
||||
if (get_dist(movable_thing, parent) > consume_range)
|
||||
movable_thing.singularity_pull(parent, singularity_size)
|
||||
else
|
||||
consume(src, movable_thing)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
/datum/component/singularity/proc/move()
|
||||
var/drifting_dir = pick(GLOB.alldirs - last_failed_movement)
|
||||
|
||||
if (!QDELETED(target) && prob(CHANCE_TO_MOVE_TO_TARGET))
|
||||
drifting_dir = get_dir(parent, target)
|
||||
|
||||
step(parent, drifting_dir)
|
||||
|
||||
/datum/component/singularity/proc/moved(datum/source, atom/new_location)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
var/current_direction = atom_parent.dir
|
||||
var/turf/current_turf = get_turf(parent)
|
||||
|
||||
for(var/dir in GLOB.cardinals)
|
||||
if(current_direction & dir)
|
||||
current_turf = get_step(current_turf, dir)
|
||||
if(!current_turf)
|
||||
break
|
||||
// eat the stuff if we're going to move into it so it doesn't mess up our movement
|
||||
for(var/atom/thing_on_turf in current_turf.contents)
|
||||
consume(src, thing_on_turf)
|
||||
consume(src, current_turf)
|
||||
|
||||
if(disregard_failed_movements || check_turfs_in(current_direction))
|
||||
last_failed_movement = null
|
||||
else
|
||||
last_failed_movement = current_direction
|
||||
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
|
||||
|
||||
/datum/component/singularity/proc/can_move(turf/to_move)
|
||||
if (!to_move)
|
||||
return FALSE
|
||||
|
||||
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
|
||||
|
||||
/// Makes sure we don't move out of the z-level by checking the turfs around us.
|
||||
/// Takes in the direction we're going, and optionally how many steps forward to look.
|
||||
/// If steps are not provided, it will be inferred by singularity_size.
|
||||
/datum/component/singularity/proc/check_turfs_in(direction, steps)
|
||||
if (!direction)
|
||||
return FALSE
|
||||
var/atom/atom_parent = parent
|
||||
if (!steps)
|
||||
switch (singularity_size)
|
||||
if (STAGE_ONE)
|
||||
steps = 1
|
||||
if (STAGE_TWO)
|
||||
steps = 3//Yes this is right
|
||||
if (STAGE_THREE)
|
||||
steps = 3
|
||||
if (STAGE_FOUR)
|
||||
steps = 4
|
||||
if (STAGE_FIVE)
|
||||
steps = 5
|
||||
var/list/turfs = list()
|
||||
var/turf/farthest_turf = atom_parent.loc
|
||||
for (var/_ = 1 to steps)
|
||||
farthest_turf = get_step(farthest_turf, direction)
|
||||
if (!isturf(farthest_turf))
|
||||
return FALSE
|
||||
turfs.Add(farthest_turf)
|
||||
var/dir2
|
||||
var/dir3
|
||||
switch (direction)
|
||||
if (NORTH || SOUTH)
|
||||
dir2 = EAST
|
||||
dir3 = WEST
|
||||
if (EAST || WEST)
|
||||
dir2 = NORTH
|
||||
dir3 = SOUTH
|
||||
var/turf/farthest_perpendicular_turf = farthest_turf
|
||||
for (var/_ = 1 to steps - 1)
|
||||
farthest_perpendicular_turf = get_step(farthest_perpendicular_turf, dir2)
|
||||
if (!isturf(farthest_perpendicular_turf))
|
||||
return FALSE
|
||||
turfs.Add(farthest_perpendicular_turf)
|
||||
for (var/_ = 1 to steps - 1)
|
||||
farthest_turf = get_step(farthest_turf, dir3)
|
||||
if (!isturf(farthest_turf))
|
||||
return FALSE
|
||||
turfs.Add(farthest_turf)
|
||||
for (var/turf_in_range in turfs)
|
||||
if (isnull(turf_in_range))
|
||||
continue
|
||||
if (!can_move(turf_in_range))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Logs to admins that a singularity was created
|
||||
/datum/component/singularity/proc/admin_investigate_setup()
|
||||
var/turf/spawned_turf = get_turf(parent)
|
||||
message_admins("A singulo has been created at [ADMIN_VERBOSEJMP(spawned_turf)].")
|
||||
var/atom/atom_parent = parent
|
||||
atom_parent.investigate_log("was made a singularity at [AREACOORD(spawned_turf)].", INVESTIGATE_SINGULO)
|
||||
|
||||
/// Fired when the singularity is fired at with the BSA and deletes it
|
||||
/datum/component/singularity/proc/bluespace_reaction()
|
||||
if (!bsa_targetable)
|
||||
return
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
atom_parent.investigate_log("has been shot by bluespace artillery and destroyed.", INVESTIGATE_SINGULO)
|
||||
qdel(parent)
|
||||
|
||||
#undef CHANCE_TO_MOVE_TO_TARGET
|
||||
#undef FIELD_CONTAINMENT_DISTANCE
|
||||
@@ -23,5 +23,5 @@
|
||||
log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
|
||||
|
||||
user.gib(TRUE, TRUE, TRUE)
|
||||
new/obj/singularity/boh_tear(loccheck)
|
||||
new/obj/boh_tear(loccheck)
|
||||
qdel(A)
|
||||
|
||||
@@ -120,7 +120,9 @@
|
||||
/obj/machinery/atmospherics,
|
||||
/obj/item/ammo_casing,
|
||||
/obj/item/implant,
|
||||
/obj/singularity
|
||||
/obj/singularity,
|
||||
/obj/energy_ball,
|
||||
/obj/narsie,
|
||||
))
|
||||
if(!can_contaminate || !can_contam || blacklisted[thing.type])
|
||||
continue
|
||||
|
||||
+4
-2
@@ -682,8 +682,10 @@
|
||||
* default behaviour is to send the [COMSIG_ATOM_BLOB_ACT] signal
|
||||
*/
|
||||
/atom/proc/blob_act(obj/structure/blob/B)
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B)
|
||||
return
|
||||
var/blob_act_result = SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B)
|
||||
if (blob_act_result & COMPONENT_CANCEL_BLOB_ACT)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/atom/proc/fire_act(exposed_temperature, exposed_volume)
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
|
||||
|
||||
@@ -278,6 +278,7 @@
|
||||
. = ..()
|
||||
if(anchored)
|
||||
connect_to_network()
|
||||
RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, .proc/block_singularity_if_active)
|
||||
|
||||
/obj/machinery/power/shieldwallgen/Destroy()
|
||||
for(var/d in GLOB.cardinals)
|
||||
@@ -364,6 +365,12 @@
|
||||
if(F && (F.gen_primary == src || F.gen_secondary == src)) //it's ours, kill it.
|
||||
qdel(F)
|
||||
|
||||
/obj/machinery/power/shieldwallgen/proc/block_singularity_if_active()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (active)
|
||||
return SINGULARITY_TRY_MOVE_BLOCK
|
||||
|
||||
/obj/machinery/power/shieldwallgen/can_be_unfasten_wrench(mob/user, silent)
|
||||
if(active)
|
||||
if(!silent)
|
||||
@@ -455,6 +462,7 @@
|
||||
for(var/mob/living/L in get_turf(src))
|
||||
visible_message("<span class='danger'>\The [src] is suddenly occupying the same space as \the [L]!</span>")
|
||||
L.gib()
|
||||
RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, .proc/block_singularity)
|
||||
|
||||
/obj/machinery/shieldwall/Destroy()
|
||||
gen_primary = null
|
||||
@@ -489,6 +497,11 @@
|
||||
if(gen_secondary) //using power may cause us to be destroyed
|
||||
gen_secondary.add_load(drain_amount * 0.5)
|
||||
|
||||
/obj/machinery/shieldwall/proc/block_singularity()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
return SINGULARITY_TRY_MOVE_BLOCK
|
||||
|
||||
/obj/machinery/shieldwall/CanAllowThrough(atom/movable/mover, turf/target)
|
||||
. = ..()
|
||||
if(istype(mover) && (mover.pass_flags & PASSGLASS))
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
if(user)
|
||||
to_chat(user, "<span class='notice'>The connected wire doesn't have enough current.</span>")
|
||||
return
|
||||
for(var/obj/singularity/singulo in GLOB.singularities)
|
||||
if(singulo.z == z)
|
||||
for (var/_singulo in GLOB.singularities)
|
||||
var/datum/component/singularity/singulo = _singulo
|
||||
var/atom/singulo_atom = singulo.parent
|
||||
if(singulo_atom.z == z)
|
||||
singulo.target = src
|
||||
icon_state = "[icontype]1"
|
||||
active = TRUE
|
||||
@@ -32,7 +34,8 @@
|
||||
|
||||
|
||||
/obj/machinery/power/singularity_beacon/proc/Deactivate(mob/user = null)
|
||||
for(var/obj/singularity/singulo in GLOB.singularities)
|
||||
for(var/_singulo in GLOB.singularities)
|
||||
var/datum/component/singularity/singulo = _singulo
|
||||
if(singulo.target == src)
|
||||
singulo.target = null
|
||||
icon_state = "[icontype]0"
|
||||
@@ -93,7 +96,9 @@
|
||||
add_load(1500)
|
||||
if(cooldown <= world.time)
|
||||
cooldown = world.time + 80
|
||||
for(var/obj/singularity/singulo in GLOB.singularities)
|
||||
for(var/_singulo_component in GLOB.singularities)
|
||||
var/datum/component/singularity/singulo_component = _singulo_component
|
||||
var/atom/singulo = singulo_component.parent
|
||||
if(singulo.z == z)
|
||||
say("[singulo] is now [get_dist(src,singulo)] standard lengths away to the [dir2text(get_dir(src,singulo))]")
|
||||
else
|
||||
|
||||
@@ -570,7 +570,7 @@
|
||||
/obj/item/melee/roastingstick/Initialize()
|
||||
. = ..()
|
||||
if (!ovens)
|
||||
ovens = typecacheof(list(/obj/singularity, /obj/machinery/power/supermatter_crystal, /obj/structure/bonfire))
|
||||
ovens = typecacheof(list(/obj/singularity, /obj/energy_ball, /obj/machinery/power/supermatter_crystal, /obj/structure/bonfire))
|
||||
|
||||
/obj/item/melee/roastingstick/attack_self(mob/user)
|
||||
on = !on
|
||||
|
||||
@@ -94,6 +94,8 @@
|
||||
return TRUE
|
||||
|
||||
/obj/blob_act(obj/structure/blob/B)
|
||||
if (!..())
|
||||
return
|
||||
if(isturf(loc))
|
||||
var/turf/T = loc
|
||||
if(T.intact && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE))
|
||||
|
||||
@@ -477,7 +477,7 @@
|
||||
to_chat(user, "<span class='warning'>You try to shatter the orb, but it remains as solid as a rock!</span>")
|
||||
to_chat(user, "<span class='danger'><span class='big'>It seems that the blood cult has exhausted its ability to curse the emergency escape shuttle. It would be unwise to create more cursed orbs or to continue to try to shatter this one.</span></span>")
|
||||
return
|
||||
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
|
||||
if(locate(/obj/narsie) in GLOB.poi_list)
|
||||
to_chat(user, "<span class='warning'>Nar'Sie is already on this plane, there is no delaying the end of all things.</span>")
|
||||
return
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
if(!(place in summon_objective.summon_spots))
|
||||
to_chat(user, "<span class='cultlarge'>The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!</span>")
|
||||
return
|
||||
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
|
||||
if(locate(/obj/narsie) in GLOB.poi_list)
|
||||
for(var/M in invokers)
|
||||
to_chat(M, "<span class='warning'>Nar'Sie is already on this plane!</span>")
|
||||
log_game("Nar'Sie rune failed - already summoned")
|
||||
@@ -516,7 +516,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
sleep(40)
|
||||
if(src)
|
||||
color = RUNE_COLOR_RED
|
||||
new /obj/singularity/narsie/large/cult(T) //Causes Nar'Sie to spawn even if the rune has been removed
|
||||
new /obj/narsie(T) //Causes Nar'Sie to spawn even if the rune has been removed
|
||||
|
||||
//Rite of Resurrection: Requires a dead or inactive cultist. When reviving the dead, you can only perform one revival for every three sacrifices your cult has carried out.
|
||||
/obj/effect/rune/raise_dead
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
var/static/list/blacklist_typecache = typecacheof(list(
|
||||
/atom/movable/screen,
|
||||
/obj/singularity,
|
||||
/obj/energy_ball,
|
||||
/obj/narsie,
|
||||
/mob/living/simple_animal/hostile/morph,
|
||||
/obj/effect))
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
var/charges = 1
|
||||
var/spawn_type = /obj/singularity/wizard
|
||||
var/spawn_type = /obj/tear_in_reality
|
||||
var/spawn_amt = 1
|
||||
var/activate_descriptor = "reality"
|
||||
var/rend_desc = "You should run now."
|
||||
@@ -98,29 +98,38 @@
|
||||
rend_desc = "Gently wafting with the sounds of mirthful grunting."
|
||||
icon_state = "clownrender"
|
||||
|
||||
////TEAR IN REALITY
|
||||
#define TEAR_IN_REALITY_CONSUME_RANGE 3
|
||||
#define TEAR_IN_REALITY_SINGULARITY_SIZE STAGE_FOUR
|
||||
|
||||
/obj/singularity/wizard
|
||||
/// Tear in reality, spawned by the veil render
|
||||
/obj/tear_in_reality
|
||||
name = "tear in the fabric of reality"
|
||||
desc = "This isn't right."
|
||||
icon = 'icons/effects/224x224.dmi'
|
||||
icon_state = "reality"
|
||||
pixel_x = -96
|
||||
pixel_y = -96
|
||||
dissipate = 0
|
||||
move_self = 0
|
||||
consume_range = 3
|
||||
grav_pull = 4
|
||||
current_size = STAGE_FOUR
|
||||
allowed_size = STAGE_FOUR
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
move_resist = INFINITY
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
light_range = 6
|
||||
appearance_flags = LONG_GLIDE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
||||
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
||||
|
||||
/obj/singularity/wizard/process()
|
||||
move()
|
||||
eat()
|
||||
return
|
||||
/obj/tear_in_reality/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
AddComponent(
|
||||
/datum/component/singularity, \
|
||||
consume_range = TEAR_IN_REALITY_CONSUME_RANGE, \
|
||||
notify_admins = !mapload, \
|
||||
roaming = FALSE, \
|
||||
singularity_size = TEAR_IN_REALITY_SINGULARITY_SIZE, \
|
||||
)
|
||||
|
||||
/obj/singularity/wizard/attack_tk(mob/user)
|
||||
/obj/tear_in_reality/attack_tk(mob/user)
|
||||
if(!iscarbon(user))
|
||||
return
|
||||
. = COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
@@ -132,18 +141,17 @@
|
||||
insaneinthemembrane.sanity = 0
|
||||
for(var/lore in typesof(/datum/brain_trauma/severe))
|
||||
jedi.gain_trauma(lore)
|
||||
addtimer(CALLBACK(src, /obj/singularity/wizard.proc/deranged, jedi), 10 SECONDS)
|
||||
addtimer(CALLBACK(src, .proc/deranged, jedi), 10 SECONDS)
|
||||
|
||||
|
||||
/obj/singularity/wizard/proc/deranged(mob/living/carbon/C)
|
||||
/obj/tear_in_reality/proc/deranged(mob/living/carbon/C)
|
||||
if(!C || C.stat == DEAD)
|
||||
return
|
||||
C.vomit(0, TRUE, TRUE, 3, TRUE)
|
||||
C.spew_organ(3, 2)
|
||||
C.death()
|
||||
|
||||
/obj/singularity/wizard/mapped/admin_investigate_setup()
|
||||
return
|
||||
#undef TEAR_IN_REALITY_CONSUME_RANGE
|
||||
#undef TEAR_IN_REALITY_SINGULARITY_SIZE
|
||||
|
||||
/////////////////////////////////////////Scrying///////////////////
|
||||
|
||||
|
||||
@@ -60,21 +60,20 @@
|
||||
name = "Pyromancy Evaluation"
|
||||
info = "Current Grade: F. Educator's Notes: No improvement shown despite multiple private lessons. Suggest additional tutelage."
|
||||
|
||||
|
||||
/// The immobile, close pulling singularity seen in the academy away mission
|
||||
/obj/singularity/academy
|
||||
dissipate = FALSE
|
||||
move_self = FALSE
|
||||
grav_pull = TRUE
|
||||
|
||||
/obj/singularity/academy/admin_investigate_setup()
|
||||
return
|
||||
/obj/singularity/academy/Initialize()
|
||||
. = ..()
|
||||
|
||||
var/datum/component/singularity/singularity = singularity_component.resolve()
|
||||
singularity?.grav_pull = 1
|
||||
|
||||
/obj/singularity/academy/process(delta_time)
|
||||
eat()
|
||||
if(DT_PROB(0.5, delta_time))
|
||||
mezzer()
|
||||
|
||||
|
||||
/obj/item/clothing/glasses/meson/truesight
|
||||
name = "The Lens of Truesight"
|
||||
desc = "I can see forever!"
|
||||
|
||||
@@ -135,22 +135,3 @@ GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]")
|
||||
new /obj/item/clothing/suit/space/hardsuit/cult(src)
|
||||
//new /obj/item/teleportation_scroll(src)
|
||||
new /obj/item/stack/ore/diamond(src)
|
||||
|
||||
/*
|
||||
* Modified Nar'Sie
|
||||
*/
|
||||
/obj/singularity/narsie/mini
|
||||
desc = "Your body becomes weak and your feel your mind slipping away as you try to comprehend what you know can't be possible."
|
||||
move_self = 0 //Contianed narsie does not move!
|
||||
grav_pull = 0 //Contained narsie does not pull stuff in!
|
||||
//Override this to prevent no adminlog runtimes and admin warnings about a singularity without containment
|
||||
/obj/singularity/narsie/mini/admin_investigate_setup()
|
||||
return
|
||||
|
||||
/obj/singularity/narsie/mini/process(delta_time)
|
||||
eat()
|
||||
if(DT_PROB(13, delta_time))
|
||||
mezzer()
|
||||
|
||||
/obj/singularity/narsie/mini/ex_act()
|
||||
return
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -38,7 +38,7 @@ All ShuttleMove procs go here
|
||||
|
||||
|
||||
else //non-living mobs shouldn't be affected by shuttles, which is why this is an else
|
||||
if(istype(thing, /obj/singularity) && !istype(thing, /obj/singularity/narsie)) //it's a singularity but not a god, ignore it.
|
||||
if(istype(thing, /obj/singularity) || istype(thing, /obj/energy_ball))
|
||||
continue
|
||||
if(!thing.anchored)
|
||||
step(thing, shuttle_dir)
|
||||
|
||||
@@ -6,8 +6,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
|
||||
/obj/item/disk/nuclear,
|
||||
/obj/machinery/nuclearbomb,
|
||||
/obj/item/beacon,
|
||||
/obj/singularity/narsie,
|
||||
/obj/singularity/wizard,
|
||||
/obj/narsie,
|
||||
/obj/tear_in_reality,
|
||||
/obj/machinery/teleport/station,
|
||||
/obj/machinery/teleport/hub,
|
||||
/obj/machinery/quantumpad,
|
||||
|
||||
@@ -480,6 +480,7 @@
|
||||
#include "code\datums\components\rot.dm"
|
||||
#include "code\datums\components\rotation.dm"
|
||||
#include "code\datums\components\shrink.dm"
|
||||
#include "code\datums\components\singularity.dm"
|
||||
#include "code\datums\components\sitcomlaughter.dm"
|
||||
#include "code\datums\components\sizzle.dm"
|
||||
#include "code\datums\components\slippery.dm"
|
||||
|
||||
Reference in New Issue
Block a user