diff --git a/code/__DEFINES/gravity.dm b/code/__DEFINES/gravity.dm index f61734cd55f..da81c0465ca 100644 --- a/code/__DEFINES/gravity.dm +++ b/code/__DEFINES/gravity.dm @@ -12,6 +12,34 @@ /// Singularity is stage 6 (11x11) #define STAGE_SIX 11 //From supermatter shard +// Minimum energy needed to reach a stage +/// Singularity stage 1 energy requirement +#define STAGE_ONE_ENERGY_REQUIREMENT 1 +/// Singularity stage 2 energy requirement +#define STAGE_TWO_ENERGY_REQUIREMENT 200 +/// Singularity stage 3 energy requirement +#define STAGE_THREE_ENERGY_REQUIREMENT 500 +/// Singularity stage 4 energy requirement +#define STAGE_FOUR_ENERGY_REQUIREMENT 1000 +/// Singularity stage 5 energy requirement +#define STAGE_FIVE_ENERGY_REQUIREMENT 2000 +/// Singularity stage 6 energy requirement (also needs to consume a SM shard) +#define STAGE_SIX_ENERGY_REQUIREMENT 3000 + +// These values get the median number between two stages to prevent expansion/shrinkage immediately +/// Singularity stage 1 +#define STAGE_ONE_ENERGY ((STAGE_TWO_ENERGY_REQUIREMENT - STAGE_ONE_ENERGY_REQUIREMENT) * 0.5) + STAGE_ONE_ENERGY_REQUIREMENT +/// Singularity stage 2 +#define STAGE_TWO_ENERGY ((STAGE_THREE_ENERGY_REQUIREMENT - STAGE_TWO_ENERGY_REQUIREMENT) * 0.5) + STAGE_TWO_ENERGY_REQUIREMENT +/// Singularity stage 3 +#define STAGE_THREE_ENERGY ((STAGE_FOUR_ENERGY_REQUIREMENT - STAGE_THREE_ENERGY_REQUIREMENT) * 0.5) + STAGE_THREE_ENERGY_REQUIREMENT +/// Singularity stage 4 +#define STAGE_FOUR_ENERGY ((STAGE_FIVE_ENERGY_REQUIREMENT - STAGE_FOUR_ENERGY_REQUIREMENT) * 0.5) + STAGE_FOUR_ENERGY_REQUIREMENT +/// Singularity stage 5 +#define STAGE_FIVE_ENERGY ((STAGE_SIX_ENERGY_REQUIREMENT - STAGE_FIVE_ENERGY_REQUIREMENT) * 0.5) + STAGE_FIVE_ENERGY_REQUIREMENT +/// Singularity stage 6 (hardcoded at 4000 since there is no stage 7) +#define STAGE_SIX_ENERGY 4000 + /** * The point where gravity is negative enough to pull you upwards. * That means walking checks for a ceiling instead of a floor, and you can fall "upwards" diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index c9ed74caa02..97ac006bcd5 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -11,10 +11,12 @@ density = TRUE layer = BELOW_MOB_LAYER //so people can't hide it and it's REALLY OBVIOUS verb_say = "states" - var/cooldown = 0 + /// Cooldown each time singularity is pulled in our direction + COOLDOWN_DECLARE(singularity_beacon_cd) var/active = FALSE var/icontype = "beacon" + var/energy_used = 1.5 KILO JOULES /obj/machinery/power/singularity_beacon/proc/Activate(mob/user = null) @@ -42,11 +44,9 @@ if(user) to_chat(user, span_notice("You deactivate the beacon.")) - /obj/machinery/power/singularity_beacon/attack_ai(mob/user) return - /obj/machinery/power/singularity_beacon/attack_hand(mob/user, list/modifiers) . = ..() if(.) @@ -93,10 +93,10 @@ if(!active) return - if(surplus() >= 1500) - add_load(1500) - if(cooldown <= world.time) - cooldown = world.time + 80 + if(surplus() >= energy_used) + add_load(energy_used) + if(COOLDOWN_FINISHED(src, singularity_beacon_cd)) + COOLDOWN_START(src, singularity_beacon_cd, 8 SECONDS) for(var/_singulo_component in GLOB.singularities) var/datum/component/singularity/singulo_component = _singulo_component var/atom/singulo = singulo_component.parent @@ -106,6 +106,95 @@ Deactivate() say("Insufficient charge detected - powering down") +// Used for the No Escape final objective that attracts a singularity to the escape shuttle +// needs to be charged with an inducer to work +/obj/machinery/power/singularity_beacon/syndicate/no_escape + name = "ominous beacon" + desc = "This looks very suspicious..." + processing_flags = START_PROCESSING_MANUALLY + /// The cell we spawn with + var/obj/item/stock_parts/power_store/cell/cell = /obj/item/stock_parts/power_store/cell/super/empty + /// The black hole shuttle event that is triggered + var/datum/shuttle_event/simple_spawner/black_hole/no_escape/no_escape_event + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/Initialize(mapload) + . = ..() + cell = new cell(src) + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/Destroy() + if(active) + Deactivate() + QDEL_NULL(cell) + // destroying the beacon doesn't automatically stop the event + no_escape_event = null + return ..() + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/examine(mob/user) + . = ..() + . += "\The [src] is [active ? "on" : "off"]." + if(cell) + . += "The charge meter reads [cell ? round(cell.percent(), 1) : 0]%." + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/get_cell() + return cell + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/attack_hand(mob/user, list/modifiers) + return active ? Deactivate(user) : Activate(user) + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/Activate(mob/user = null) + if(!cell.charge()) + say("Insufficient charge detected") + return + + icon_state = "[icontype]1" + active = TRUE + begin_processing() + if(user) + to_chat(user, span_notice("You activate the beacon.")) + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/Deactivate(mob/user = null) + icon_state = "[icontype]0" + active = FALSE + end_processing() + if(user) + to_chat(user, span_notice("You deactivate the beacon.")) + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/wrench_act(mob/living/user, obj/item/tool) + . = TRUE + + tool.play_tool_sound(src, 50) + if(anchored) + set_anchored(FALSE) + to_chat(user, span_notice("You unbolt \the [src] from the floor.")) + return + else + set_anchored(TRUE) + to_chat(user, span_notice("You bolt \the [src] to the floor.")) + return + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/screwdriver_act(mob/living/user, obj/item/tool) + return + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/emp_act(severity) + . = ..() + if(machine_stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_CONTENTS) + return + cell?.emp_act(severity) + +/obj/machinery/power/singularity_beacon/syndicate/no_escape/process() + if(cell.charge()) + cell.use(energy_used, force = TRUE) + + if(!no_escape_event) + var/area/escape_shuttle_area = get_area(src) + // beacon must be on the traveling escape shuttle (not a pod) + if(istype(escape_shuttle_area, /area/shuttle/escape) && (SSshuttle.emergency.mode == SHUTTLE_ESCAPE) && SSshuttle.emergency.is_in_shuttle_bounds(src)) + var/obj/docking_port/mobile/port = SSshuttle.emergency + no_escape_event = port.add_shuttle_event(/datum/shuttle_event/simple_spawner/black_hole/no_escape) + no_escape_event.beacon = src + else + Deactivate() + say("Insufficient charge detected - powering down") /obj/machinery/power/singularity_beacon/syndicate icontype = "beaconsynd" @@ -131,6 +220,10 @@ qdel(src) return +/obj/item/sbeacondrop/no_escape + name = "very suspicious beacon" + droptype = /obj/machinery/power/singularity_beacon/syndicate/no_escape + /obj/item/sbeacondrop/bomb desc = "A label on it reads: Warning: Activating this device will send a high-ordinance explosive to your location." droptype = /obj/machinery/syndicatebomb diff --git a/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm b/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm index 6e722b1515e..33675402397 100644 --- a/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm +++ b/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm @@ -7,6 +7,7 @@ /datum/traitor_objective/ultimate/infect_ai = 1, /datum/traitor_objective/ultimate/romerol = 1, /datum/traitor_objective/ultimate/supermatter_cascade = 1, + /datum/traitor_objective/ultimate/no_escape = 1, ) weight = 100 diff --git a/code/modules/antagonists/traitor/objectives/final_objective/no_escape.dm b/code/modules/antagonists/traitor/objectives/final_objective/no_escape.dm new file mode 100644 index 00000000000..12cbdcf2d01 --- /dev/null +++ b/code/modules/antagonists/traitor/objectives/final_objective/no_escape.dm @@ -0,0 +1,48 @@ +/datum/traitor_objective/ultimate/no_escape + name = "Attach a beacon to the escape shuttle that will attract a singularity to consume everything." + description = "Go to %AREA%, and receive the smuggled beacon. Set up the beacon anywhere on the shuttle, \ + and charge it using an inducer then, IT COMES. Warning: The singularity will consume all in it's path, you included." + + ///area type the objective owner must be in to receive the satellites + var/area/beacon_spawn_area_type + ///checker on whether we have sent the beacon yet + var/sent_beacon = FALSE + +/datum/traitor_objective/ultimate/no_escape/generate_objective(datum/mind/generating_for, list/possible_duplicates) + var/list/possible_areas = GLOB.the_station_areas.Copy() + for(var/area/possible_area as anything in possible_areas) + if(!ispath(possible_area, /area/station/maintenance/solars) && !ispath(possible_area, /area/station/solars)) + possible_areas -= possible_area + if(length(possible_areas) == 0) + return FALSE + beacon_spawn_area_type = pick(possible_areas) + replace_in_name("%AREA%", initial(beacon_spawn_area_type.name)) + return TRUE + +/datum/traitor_objective/ultimate/no_escape/generate_ui_buttons(mob/user) + var/list/buttons = list() + if(!sent_beacon) + buttons += add_ui_button("", "Pressing this will call down a pod with the smuggled beacon.", "beacon", "beacon") + return buttons + +/datum/traitor_objective/ultimate/no_escape/ui_perform_action(mob/living/user, action) + . = ..() + switch(action) + if("beacon") + if(sent_beacon) + return + var/area/delivery_area = get_area(user) + if(delivery_area.type != beacon_spawn_area_type) + to_chat(user, span_warning("You must be in [initial(beacon_spawn_area_type.name)] to receive the smuggled beacon.")) + return + sent_beacon = TRUE + podspawn(list( + "target" = get_turf(user), + "style" = /datum/pod_style/syndicate, + "spawn" = list( + /obj/item/sbeacondrop/no_escape, + /obj/item/inducer/syndicate, + /obj/item/wrench + ) + )) + diff --git a/code/modules/power/singularity/dark_matter_singularity.dm b/code/modules/power/singularity/dark_matter_singularity.dm index bd379162b13..294c2063b1b 100644 --- a/code/modules/power/singularity/dark_matter_singularity.dm +++ b/code/modules/power/singularity/dark_matter_singularity.dm @@ -13,11 +13,12 @@ icon_state = "dark_matter_s1" singularity_icon_variant = "dark_matter" maximum_stage = STAGE_FOUR + energy = 250 singularity_component_type = /datum/component/singularity/bloodthirsty ///to avoid cases of the singuloth getting blammed out of existence by the very meteor it rode in on... COOLDOWN_DECLARE(initial_explosion_immunity) -/obj/singularity/dark_matter/Initialize(mapload, starting_energy = 250) +/obj/singularity/dark_matter/Initialize(mapload, starting_energy) . = ..() COOLDOWN_START(src, initial_explosion_immunity, 5 SECONDS) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 0475736f6a5..40385624007 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -28,7 +28,7 @@ var/maximum_stage = STAGE_SIX ///How strong are we? - var/energy = 100 + var/energy = 50 ///Do we lose energy over time? var/dissipate = TRUE /// How long should it take for us to dissipate in seconds? @@ -55,10 +55,10 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF | SHUTTLE_CRUSH_PROOF obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION -/obj/singularity/Initialize(mapload, starting_energy = 50) +/obj/singularity/Initialize(mapload, starting_energy) . = ..() - energy = starting_energy + energy = starting_energy || energy START_PROCESSING(SSsinguloprocess, src) SSpoints_of_interest.make_point_of_interest(src) @@ -70,7 +70,7 @@ singularity_component = WEAKREF(new_component) - expand(current_size) + check_energy() for (var/obj/machinery/power/singularity_beacon/singu_beacon as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/power/singularity_beacon)) if (singu_beacon.active) @@ -293,19 +293,19 @@ qdel(src) return FALSE switch(energy)//Some of these numbers might need to be changed up later -Mport - if(1 to 199) + if(STAGE_ONE_ENERGY_REQUIREMENT to STAGE_TWO_ENERGY_REQUIREMENT) allowed_size = STAGE_ONE - if(200 to 499) + if(STAGE_TWO_ENERGY_REQUIREMENT to STAGE_THREE_ENERGY_REQUIREMENT) allowed_size = STAGE_TWO - if(500 to 999) + if(STAGE_THREE_ENERGY_REQUIREMENT to STAGE_FOUR_ENERGY_REQUIREMENT) allowed_size = STAGE_THREE - if(1000 to 1999) + if(STAGE_FOUR_ENERGY_REQUIREMENT to STAGE_FIVE_ENERGY_REQUIREMENT) allowed_size = STAGE_FOUR - if(2000 to INFINITY) - if(energy >= 3000 && consumed_supermatter) - allowed_size = STAGE_SIX - else - allowed_size = STAGE_FIVE + if(STAGE_FIVE_ENERGY_REQUIREMENT to STAGE_SIX_ENERGY_REQUIREMENT) + allowed_size = STAGE_FIVE + if(STAGE_SIX_ENERGY_REQUIREMENT to INFINITY) + allowed_size = consumed_supermatter ? STAGE_SIX : STAGE_FIVE + if(current_size != allowed_size) expand() return TRUE @@ -496,10 +496,6 @@ . = ..() deadchat_plays(mode = DEMOCRACY_MODE) -/// Special singularity that spawns for shuttle events only -/obj/singularity/shuttle_event - anchored = FALSE - /// Special singularity spawned by being sucked into a black hole during emagged orion trail. /obj/singularity/orion move_self = FALSE @@ -512,3 +508,11 @@ /obj/singularity/orion/process(seconds_per_tick) if(SPT_PROB(0.5, seconds_per_tick)) mezzer() + +/// Special singularity that spawns for shuttle events only +/obj/singularity/shuttle_event + anchored = FALSE // this is required to work with shuttle event otherwise singularity gets stuck and doesn't move + +/obj/singularity/shuttle_event/no_escape + energy = STAGE_SIX_ENERGY + consumed_supermatter = TRUE // so we can get to the final stage diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 3bc21d52d35..bfb5038e16f 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -1226,6 +1226,7 @@ event_list.Add(event) if(launch_status == ENDGAME_LAUNCHED) event.start_up_event(0) + return event #ifdef TESTING #undef DOCKING_PORT_HIGHLIGHT diff --git a/code/modules/shuttle/shuttle_events/blackhole.dm b/code/modules/shuttle/shuttle_events/blackhole.dm new file mode 100644 index 00000000000..c73245be926 --- /dev/null +++ b/code/modules/shuttle/shuttle_events/blackhole.dm @@ -0,0 +1,82 @@ +///Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through +/datum/shuttle_event/simple_spawner/black_hole + name = "Black Hole (Oh no!)" + event_probability = 0 // only admin spawnable + spawn_probability_per_process = 10 + activation_fraction = 0.35 + spawning_flags = SHUTTLE_EVENT_HIT_SHUTTLE + spawning_list = list(/obj/singularity/shuttle_event = 1) + // only spawn it once + remove_from_list_when_spawned = TRUE + self_destruct_when_empty = TRUE + +///Kobayashi Maru version +/datum/shuttle_event/simple_spawner/black_hole/adminbus + name = "Black Holes (OH GOD!)" + spawn_probability_per_process = 50 + activation_fraction = 0.2 + spawning_list = list(/obj/singularity/shuttle_event = 10) + remove_from_list_when_spawned = TRUE + +/// No Escape traitor final objective +/datum/shuttle_event/simple_spawner/black_hole/no_escape + name = "Black Hole Massive (is not admin spawnable)" + spawn_probability_per_process = -1.875 // starts in the negative but increases over time + activation_fraction = 0 // no delay + spawning_list = list(/obj/singularity/shuttle_event/no_escape = 1) + remove_from_list_when_spawned = TRUE + /// How much the spawn_probability_per_process increases or decreases over time + /// since spawn_probability starts negative after 15 seconds the prob reaches 0% + /// then every 8 seconds after, the prob increases by ~1% + var/probability_rate_of_change = 0.125 + /// The beacon that is drawing the singularity closer to the escape shuttle + var/obj/machinery/power/singularity_beacon/syndicate/no_escape/beacon + +/datum/shuttle_event/simple_spawner/black_hole/no_escape/proc/announcement() + priority_announce( + text = "Sensors indicate that a black hole's gravitational field is affecting the region of space we are heading through.", + title = "The Orion Trail", + sound = 'sound/announcer/notice/notice1.ogg', + has_important_message = TRUE, + sender_override = "Emergency Shuttle", + color_override = "red", + ) + +/datum/shuttle_event/simple_spawner/black_hole/no_escape/activate() + . = ..() + + addtimer(CALLBACK(src, PROC_REF(announcement)), 5 SECONDS) + port.setTimer(port.timeLeft(1) + 1 MINUTES) // the singularity causes a time distortion + +/datum/shuttle_event/simple_spawner/black_hole/no_escape/event_process() + . = ..() + if(!.) + return + + if((SSshuttle.emergency.mode == SHUTTLE_ESCAPE)) // only while shuttle is in transit + if(beacon && beacon.active) + var/area/escape_shuttle_area = get_area(beacon) + if(istype(escape_shuttle_area, /area/shuttle/escape) && SSshuttle.emergency.is_in_shuttle_bounds(beacon)) + spawn_probability_per_process += probability_rate_of_change + else // beacon is not on shuttle and likely got jettisoned in space + // since the beacon is still powered and attracting the singularity it results in x2 rate of decrease + spawn_probability_per_process -= (probability_rate_of_change * 2) + else // beacon is unpowered or destroyed + spawn_probability_per_process -= probability_rate_of_change + + if(prob(spawn_probability_per_process)) + spawn_movable(get_type_to_spawn()) + return SHUTTLE_EVENT_CLEAR + +/datum/shuttle_event/simple_spawner/black_hole/no_escape/get_spawn_turf() + RETURN_TYPE(/turf) + + if(beacon && beacon.active) + var/area/escape_shuttle_area = get_area(beacon) + if(istype(escape_shuttle_area, /area/shuttle/escape) && SSshuttle.emergency.is_in_shuttle_bounds(beacon)) + // beacon is active and on shuttle so singularity will directly hit the shuttle + return pick(spawning_turfs_hit) + + // otherwise beacon is turned off, destroyed, or spaced so there is a chance to miss + // the singularity is 11x11 so even a miss can have a glancing hit against the shuttle + return pick(spawning_turfs_hit + spawning_turfs_miss) diff --git a/code/modules/shuttle/shuttle_events/misc.dm b/code/modules/shuttle/shuttle_events/misc.dm index 4891d5af799..9f6db855a76 100644 --- a/code/modules/shuttle/shuttle_events/misc.dm +++ b/code/modules/shuttle/shuttle_events/misc.dm @@ -38,25 +38,3 @@ while(islist(spawn_list)) spawn_list = pick_weight(spawn_list) return spawn_list - -///Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through -/datum/shuttle_event/simple_spawner/black_hole - name = "Black Hole (Oh no! Just one though!)" - event_probability = 0 // only admin spawnable - spawn_probability_per_process = 10 - activation_fraction = 0.35 - spawning_flags = SHUTTLE_EVENT_HIT_SHUTTLE - spawning_list = list(/obj/singularity/shuttle_event = 1) - // only spawn it once - remove_from_list_when_spawned = TRUE - self_destruct_when_empty = TRUE - -///Kobayashi Maru version -/datum/shuttle_event/simple_spawner/black_hole/adminbus - name = "Black Holes (OH GOD! Will literally kill everyone!)" - event_probability = 0 - spawn_probability_per_process = 50 - activation_fraction = 0.2 - spawning_list = list(/obj/singularity/shuttle_event = 10) - remove_from_list_when_spawned = TRUE - diff --git a/tgstation.dme b/tgstation.dme index 9b8e0395b63..1b8d3607be3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3456,6 +3456,7 @@ #include "code\modules\antagonists\traitor\objectives\final_objective\battlecruiser.dm" #include "code\modules\antagonists\traitor\objectives\final_objective\final_objective.dm" #include "code\modules\antagonists\traitor\objectives\final_objective\infect_ai.dm" +#include "code\modules\antagonists\traitor\objectives\final_objective\no_escape.dm" #include "code\modules\antagonists\traitor\objectives\final_objective\objective_dark_matteor.dm" #include "code\modules\antagonists\traitor\objectives\final_objective\romerol.dm" #include "code\modules\antagonists\traitor\objectives\final_objective\supermatter_cascade.dm" @@ -6020,6 +6021,7 @@ #include "code\modules\shuttle\syndicate.dm" #include "code\modules\shuttle\white_ship.dm" #include "code\modules\shuttle\shuttle_events\_shuttle_events.dm" +#include "code\modules\shuttle\shuttle_events\blackhole.dm" #include "code\modules\shuttle\shuttle_events\carp.dm" #include "code\modules\shuttle\shuttle_events\humans.dm" #include "code\modules\shuttle\shuttle_events\meteors.dm"