diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 5d704e243c9..558ccf7067c 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -110,16 +110,6 @@ #define DETOMATIX_RESIST_MINOR 1 #define DETOMATIX_RESIST_MAJOR 2 -// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! -#define SUPERMATTER_ERROR -1 // Unknown status, shouldn't happen but just in case. -#define SUPERMATTER_INACTIVE 0 // No or minimal energy -#define SUPERMATTER_NORMAL 1 // Normal operation -#define SUPERMATTER_NOTIFY 2 // Ambient temp > 80% of CRITICAL_TEMPERATURE -#define SUPERMATTER_WARNING 3 // Ambient temp > CRITICAL_TEMPERATURE OR integrity damaged -#define SUPERMATTER_DANGER 4 // Integrity < 50% -#define SUPERMATTER_EMERGENCY 5 // Integrity < 25% -#define SUPERMATTER_DELAMINATING 6 // Pretty obvious. - #define HYPERTORUS_INACTIVE 0 // No or minimal energy #define HYPERTORUS_NOMINAL 1 // Normal operation #define HYPERTORUS_WARNING 2 // Integrity damaged diff --git a/code/__DEFINES/supermatter.dm b/code/__DEFINES/supermatter.dm index 20dd3e82cc3..634d3c8d681 100644 --- a/code/__DEFINES/supermatter.dm +++ b/code/__DEFINES/supermatter.dm @@ -90,7 +90,7 @@ /// All humans within this range will be irradiated #define DETONATION_RADIATION_RANGE 20 -#define WARNING_DELAY 60 +#define SUPERMATTER_WARNING_DELAY 60 SECONDS #define HALLUCINATION_RANGE(P) (min(7, round(P ** 0.25))) @@ -102,13 +102,6 @@ #define HALLUCINATION_ANOMALY "hallucination_anomaly" #define VORTEX_ANOMALY "vortex_anomaly" -//If integrity percent remaining is less than these values, the monitor sets off the relevant alarm. -#define SUPERMATTER_DELAM_PERCENT 5 -#define SUPERMATTER_EMERGENCY_PERCENT 25 -#define SUPERMATTER_DANGER_PERCENT 50 -#define SUPERMATTER_WARNING_PERCENT 100 -#define CRITICAL_TEMPERATURE 10000 - #define SUPERMATTER_COUNTDOWN_TIME 30 SECONDS ///to prevent accent sounds from layering @@ -128,3 +121,32 @@ #define POWERLOSS_LINEAR_RATE 0.83 /// How much a psychologist can reduce power loss. #define PSYCHOLOGIST_POWERLOSS_REDUCTION 0.2 + +/// Means it's not forced, sm decides itself by checking the [/datum/sm_delam/proc/can_select] +#define SM_DELAM_PRIO_NONE 0 +/// In-game factors like the destabilizing crystal [/obj/item/destabilizing_crystal]. +/// Purged when SM heals to 100 +#define SM_DELAM_PRIO_IN_GAME 1 + +/// Purge the current forced delam and make it zero again (back to normal). +/// Needs to be higher priority than current forced_delam though. +#define SM_DELAM_STRATEGY_PURGE null + +// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! +// [/obj/machinery/power/supermatter_crystal/proc/get_status] +/// Unknown status, shouldn't happen but just in case. +#define SUPERMATTER_ERROR -1 +/// No or minimal energy +#define SUPERMATTER_INACTIVE 0 +/// Normal operation +#define SUPERMATTER_NORMAL 1 +/// Ambient temp 80% of the default temp for SM to take damage. +#define SUPERMATTER_NOTIFY 2 +/// Integrity below [/obj/machinery/power/supermatter_crystal/var/warning_point]. Start complaining on comms. +#define SUPERMATTER_WARNING 3 +/// Integrity below [/obj/machinery/power/supermatter_crystal/var/danger_point]. Start spawning anomalies. +#define SUPERMATTER_DANGER 4 +/// Integrity below [/obj/machinery/power/supermatter_crystal/var/emergency_point]. Start complaining to more people. +#define SUPERMATTER_EMERGENCY 5 +/// Currently counting down to delamination. True [/obj/machinery/power/supermatter_crystal/var/final_countdown] +#define SUPERMATTER_DELAMINATING 6 diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm index b9161800685..05d8838c89c 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm @@ -46,8 +46,8 @@ desc = "Your benefactors conveinently neglected to mention it's already assembled." anchored = TRUE radio_key = /obj/item/encryptionkey/syndicate - engineering_channel = "Syndicate" - common_channel = "Syndicate" + emergency_channel = "Syndicate" + warning_channel = "Syndicate" include_in_cims = FALSE /obj/machinery/power/supermatter_crystal/shard/syndicate/attackby(obj/item/item, mob/living/user, params) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 36e002eae6a..d74444ebcfc 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -12,10 +12,6 @@ #define OBJECT (LOWEST + 1) #define LOWEST (1) -#define CASCADING_ADMIN "Admin" -#define CASCADING_CRITICAL_GAS "Critical gas point" -#define CASCADING_DESTAB_CRYSTAL "Destabilizing crystal" - GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal @@ -38,30 +34,27 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/static/gl_uid = 1 ///Tracks the bolt color we are using var/zap_icon = DEFAULT_ZAP_ICON_STATE - ///The portion of the gasmix we're on that we should remove - var/gasefficency = 0.15 ///Are we exploding? var/final_countdown = FALSE ///The amount of damage we have currently var/damage = 0 - ///The damage we had before this cycle. Used to limit the damage we can take each cycle, and for safe_alert + /// The damage we had before this cycle. + /// Used to limit the damage we can take each cycle, and to check if we are currently taking damage or healing. var/damage_archived = 0 - ///Our "Shit is no longer fucked" message. We send it when damage is less then damage_archived - var/safe_alert = "Crystalline hyperstructure returning to safe operating parameters." - ///The point at which we should start sending messeges about the damage to the engi channels. + + ///The point at which we consider the supermatter to be [SUPERMATTER_STATUS_WARNING] var/warning_point = 50 - ///The alert we send when we've reached warning_point - var/warning_alert = "Danger! Crystal hyperstructure integrity faltering!" - ///The point at which we start sending messages to the common channel - var/emergency_point = 700 - ///The alert we send when we've reached emergency_point - var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT." - ///The point at which we delam + var/warning_channel = RADIO_CHANNEL_ENGINEERING + ///The point at which we consider the supermatter to be [SUPERMATTER_STATUS_DANGER] + ///Spawns anomalies when more damaged than this too. + var/danger_point = 550 + ///The point at which we consider the supermatter to be [SUPERMATTER_STATUS_EMERGENCY] + var/emergency_point = 675 + var/emergency_channel = null // Need null to actually broadcast, lol. + ///The point at which we delam [SUPERMATTER_STATUS_DELAMINATING] var/explosion_point = 900 - ///When we pass this amount of damage we start shooting bolts - var/damage_penalty_point = 550 ///A scaling value that affects the severity of explosions. var/explosion_power = 35 @@ -220,20 +213,14 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/obj/item/radio/radio ///The key our internal radio uses var/radio_key = /obj/item/encryptionkey/headset_eng - ///The engineering channel - var/engineering_channel = "Engineering" - ///The common channel - var/common_channel = null ///Boolean used for logging if we've been powered var/has_been_powered = FALSE - ///Boolean used for logging if we've passed the emergency point - var/has_reached_emergency = FALSE ///An effect we show to admins and ghosts the percentage of delam we're at var/obj/effect/countdown/supermatter/countdown - ///Used along with a global var to track if we can give out the sm sliver stealing objective + ///Only main engines can have their sliver stolen, can trigger cascades, and can spawn stationwide anomalies. var/is_main_engine = FALSE ///Our soundloop var/datum/looping_sound/supermatter/soundloop @@ -264,16 +251,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/include_in_cims = TRUE var/freonbonus = 0 - ///Can the crystal trigger the station wide anomaly spawn? - var/anomaly_event = TRUE ///Hue shift of the zaps color based on the power of the crystal var/hue_angle_shift = 0 - ///If an admin wants a sure cascade with the delamination just set this to true (don't be a badmin) - var/admin_cascade = FALSE - ///Do we have a destabilizing crystal attached? - var/has_destabilizing_crystal = FALSE - ///Has the cascade been triggered? - var/cascade_initiated = FALSE ///Reference to the warp effect var/atom/movable/supermatter_warp_effect/warp ///The power threshold required to transform the powerloss function into a linear function from a cubic function. @@ -281,9 +260,21 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) ///The offset of the linear powerloss function set so the transition is differentiable. var/powerloss_linear_offset = 0 + ///The portion of the gasmix we're on that we should remove + var/absorption_ratio = 0.15 + /// The gasmix we just recently absorbed. Tile's air multiplied by absorption_ratio + var/datum/gas_mixture/absorbed_gasmix + + /// How we are delaminating. + var/datum/sm_delam/delamination_strategy + /// Whether the sm is forced in a specific delamination_strategy or not. All truthy values means it's forced. + /// Only values greater or equal to the current one can change the strat. + var/delam_priority = SM_DELAM_PRIO_NONE + /obj/machinery/power/supermatter_crystal/Initialize(mapload) . = ..() uid = gl_uid++ + set_delam(SM_DELAM_PRIO_NONE, /datum/sm_delam/explosive) SSair.start_processing_machine(src) countdown = new(src) countdown.start() @@ -297,7 +288,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) GLOB.main_supermatter_engine = src AddElement(/datum/element/bsa_blocker) - RegisterSignal(src, COMSIG_ATOM_BSA_BEAM, .proc/call_delamination_event) + RegisterSignal(src, COMSIG_ATOM_BSA_BEAM, .proc/force_delam) var/static/list/loc_connections = list( COMSIG_TURF_INDUSTRIAL_LIFT_ENTER = .proc/tram_contents_consume, @@ -344,8 +335,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/immune = HAS_TRAIT(user, TRAIT_MADNESS_IMMUNE) || (user.mind && HAS_TRAIT(user.mind, TRAIT_MADNESS_IMMUNE)) if(isliving(user) && !immune && (get_dist(user, src) < HALLUCINATION_RANGE(power))) . += span_danger("You get headaches just from looking at it.") - if(cascade_initiated) - . += span_bolddanger("The crystal is vibrating at immense speeds, warping space around it!") + . += delamination_strategy.examine(src) + return . // SupermatterMonitor UI for ghosts only. Inherited attack_ghost will call this. /obj/machinery/power/supermatter_crystal/ui_interact(mob/user, datum/tgui/ui) @@ -371,7 +362,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) data["SM_power"] = power data["SM_ambienttemp"] = air.temperature data["SM_ambientpressure"] = air.return_pressure() - data["SM_bad_moles_amount"] = MOLE_PENALTY_THRESHOLD / gasefficency + data["SM_bad_moles_amount"] = MOLE_PENALTY_THRESHOLD / absorption_ratio data["SM_moles"] = 0 data["SM_uid"] = uid var/area/active_supermatter_area = get_area(src) @@ -396,6 +387,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) return data +/// Encodes the current state of the supermatter. /obj/machinery/power/supermatter_crystal/proc/get_status() var/turf/local_turf = get_turf(src) if(!local_turf) @@ -403,46 +395,20 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/datum/gas_mixture/air = local_turf.return_air() if(!air) return SUPERMATTER_ERROR - - var/integrity = get_integrity_percent() - if(integrity < SUPERMATTER_DELAM_PERCENT) + if(final_countdown) return SUPERMATTER_DELAMINATING - - if(integrity < SUPERMATTER_EMERGENCY_PERCENT) + if(damage >= emergency_point) return SUPERMATTER_EMERGENCY - - if(integrity < SUPERMATTER_DANGER_PERCENT) + if(damage >= danger_point) return SUPERMATTER_DANGER - - if((integrity < SUPERMATTER_WARNING_PERCENT) || (air.temperature > CRITICAL_TEMPERATURE)) + if(damage >= warning_point) return SUPERMATTER_WARNING - - if(air.temperature > (CRITICAL_TEMPERATURE * 0.8)) + if(absorbed_gasmix.temperature > (T0C + HEAT_PENALTY_THRESHOLD) * 0.8) return SUPERMATTER_NOTIFY - - if(power > 5) + if(power) return SUPERMATTER_NORMAL return SUPERMATTER_INACTIVE -/obj/machinery/power/supermatter_crystal/proc/alarm() - switch(get_status()) - if(SUPERMATTER_DELAMINATING) - playsound(src, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10) - // SKYRAT EDIT ADDITION - alert_sound_to_playing('modular_skyrat/master_files/sound/effects/reactor/meltdown.ogg', override_volume = TRUE) - alert_sound_to_playing('modular_skyrat/modules/alerts/sound/alerts/alert1.ogg', override_volume = TRUE) - // SKYRAT EDIT END - if(SUPERMATTER_EMERGENCY) - // SKYRAT EDIT ADDITION - alert_sound_to_playing('modular_skyrat/master_files/sound/effects/reactor/core_overheating.ogg', override_volume = TRUE) - alert_sound_to_playing('modular_skyrat/modules/alerts/sound/alerts/alert1.ogg', override_volume = TRUE) - // SKYRAT EDIT END - playsound(src, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10) - if(SUPERMATTER_DANGER) - playsound(src, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10) - if(SUPERMATTER_WARNING) - playsound(src, 'sound/machines/terminal_alert.ogg', 75) - /obj/machinery/power/supermatter_crystal/proc/get_integrity_percent() var/integrity = damage / explosion_point integrity = round(100 - integrity * 100, 0.01) @@ -451,93 +417,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/update_overlays() . = ..() - if(final_countdown && !cascade_initiated) - . += "casuality_field" + . += delamination_strategy.overlays(src) + return . -/obj/machinery/power/supermatter_crystal/proc/countdown() - set waitfor = FALSE - - if(final_countdown) // We're already doing it go away - return - final_countdown = TRUE - update_appearance() - - var/cascading = cascade_initiated - - var/speaking = "[emergency_alert] The supermatter has reached critical integrity failure." - - if(cascading) - speaking += " Harmonic frequency limits exceeded. Causality destabilization field could not be engaged." - else - speaking += " Emergency causality destabilization field has been activated." - - radio.talk_into(src, speaking, common_channel, language = get_selected_language()) - for(var/i in SUPERMATTER_COUNTDOWN_TIME to 0 step -10) - if(damage < explosion_point) // Cutting it a bit close there engineers - if(cascading) - radio.talk_into(src, "[safe_alert] Harmonic frequency restored within emergency bounds. Anti-resonance filter initiated.", common_channel) - else - radio.talk_into(src, "[safe_alert] Failsafe has been disengaged.", common_channel) - final_countdown = FALSE - update_appearance() - return - else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individualy - sleep(10) - continue - else if(i > 50) - if(cascading) - speaking = "[DisplayTimeText(i, TRUE)] remain before resonance-induced stabilization." - else - speaking = "[DisplayTimeText(i, TRUE)] remain before causality stabilization." - else - speaking = "[i*0.1]..." - radio.talk_into(src, speaking, common_channel) - sleep(1 SECONDS) - - delamination_event() - -/obj/machinery/power/supermatter_crystal/proc/delamination_event() - var/can_spawn_anomalies = is_station_level(loc.z) && is_main_engine && anomaly_event - - var/is_cascading = cascade_initiated - - new /datum/supermatter_delamination(power, combined_gas, get_turf(src), explosion_power, gasmix_power_ratio, can_spawn_anomalies, is_cascading) - qdel(src) - -//this is here to eat arguments -/obj/machinery/power/supermatter_crystal/proc/call_delamination_event() +/obj/machinery/power/supermatter_crystal/proc/force_delam() SIGNAL_HANDLER - delamination_event() - -/** - * Checks if and why the supermatter is in a state where it can cascade - * - * Returns: cause of the cascade, for logging - */ -/obj/machinery/power/supermatter_crystal/proc/check_cascade_requirements() - if(admin_cascade) - return CASCADING_ADMIN - - if(!anomaly_event) - return FALSE - - if(has_destabilizing_crystal) - return CASCADING_DESTAB_CRYSTAL - - var/critical_gas_exceeded = TRUE - var/list/required_gases = list(/datum/gas/hypernoblium, /datum/gas/antinoblium) - if(environment_total_moles < MOLE_PENALTY_THRESHOLD) - critical_gas_exceeded = FALSE - else - for(var/gas_path in required_gases) - if(gas_comp[gas_path] < 0.4) - critical_gas_exceeded = FALSE - break - - if(critical_gas_exceeded) - return CASCADING_CRITICAL_GAS - - return FALSE + investigate_log("was forcefully delaminated", INVESTIGATE_ENGINE) + INVOKE_ASYNC(delamination_strategy, /datum/sm_delam/proc/delaminate, src) /obj/machinery/power/supermatter_crystal/proc/supermatter_pull(turf/center, pull_range = 3) playsound(center, 'sound/weapons/marauder.ogg', 100, TRUE, extrarange = pull_range - world.view) @@ -553,6 +439,61 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) continue //You can't pull someone nailed to the deck step_towards(movable_atom,center) +/** + * Count down, spout some messages, and then execute the delam itself. + * We guard for last second delam strat changes here, mostly because some have diff messages. + * + * By last second changes, we mean that it's possible for say, a tesla delam to + * just explode normally if at the absolute last second it loses power and switches to default one. + * Even after countdown is already in progress. + */ +/obj/machinery/power/supermatter_crystal/proc/count_down() + set waitfor = FALSE + + if(final_countdown) // We're already doing it go away + stack_trace("[src] told to delaminate again while it's already delaminating.") + return + + final_countdown = TRUE + update_appearance() + + var/datum/sm_delam/last_delamination_strategy = delamination_strategy + var/list/count_down_messages = delamination_strategy.count_down_messages() + + radio.talk_into( + src, + count_down_messages[1], + emergency_channel + ) + + for(var/i in SUPERMATTER_COUNTDOWN_TIME to 0 step -10) + if(last_delamination_strategy != delamination_strategy) + count_down_messages = delamination_strategy.count_down_messages() + + var/message + var/healed = FALSE + + if(damage < explosion_point) // Cutting it a bit close there engineers + message = count_down_messages[2] + healed = TRUE + else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individualy + sleep(1 SECONDS) + continue + else if(i > 50) + message = "[DisplayTimeText(i, TRUE)] [count_down_messages[3]]" + else + message = "[i*0.1]..." + + radio.talk_into(src, message, emergency_channel) + + if(healed) + final_countdown = FALSE + update_appearance() + return // delam averted + sleep(1 SECONDS) + + delamination_strategy.delaminate(src) + /proc/supermatter_anomaly_gen(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5, has_changed_lifespan = TRUE) var/turf/local_turf = pick(orange(anomalyrange, anomalycenter)) if(!local_turf) @@ -721,13 +662,12 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) base_icon_state = "darkmatter_shard" icon_state = "darkmatter_shard" anchored = FALSE - gasefficency = 0.125 + absorption_ratio = 0.125 explosion_power = 12 layer = ABOVE_MOB_LAYER plane = GAME_PLANE_UPPER moveable = TRUE psyOverlay = /obj/overlay/psy/shard - anomaly_event = FALSE /obj/machinery/power/supermatter_crystal/shard/engine name = "anchored supermatter shard" @@ -767,10 +707,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) pixel_x = -176 pixel_y = -176 -#undef CASCADING_ADMIN -#undef CASCADING_CRITICAL_GAS -#undef CASCADING_DESTAB_CRYSTAL - #undef BIKE #undef COIL #undef ROD diff --git a/code/modules/power/supermatter/supermatter_delamination.dm b/code/modules/power/supermatter/supermatter_delamination.dm deleted file mode 100644 index c927c58007e..00000000000 --- a/code/modules/power/supermatter/supermatter_delamination.dm +++ /dev/null @@ -1,349 +0,0 @@ -///Minimum distance that a crystal mass must have from the rift -#define MIN_RIFT_SAFE_DIST 30 - -/datum/supermatter_delamination - ///Power amount of the SM at the moment of death - var/supermatter_power = 0 - ///Amount of total gases interacting with the SM - var/supermatter_gas_amount = 0 - ///Base number of anomalies to spawn (can go up or down with a random small amount) - var/anomalies_to_spawn = 10 - ///Can we spawn anomalies after dealing with the delamination type? - var/should_spawn_anomalies = TRUE - ///Reference to the supermatter turf - var/turf/supermatter_turf - ///Baseline strenght of the explosion caused by the SM - var/supermatter_explosion_power = 0 - ///Amount the gasmix will affect the explosion size - var/supermatter_gasmix_power_ratio = 0 - ///Are we triggering a supermatter cascade? - var/supermatter_cascade = FALSE - ///The rift in space that will be created by the cascade - var/obj/cascade_portal/cascade_rift - -/datum/supermatter_delamination/New(supermatter_power, supermatter_gas_amount, turf/supermatter_turf, supermatter_explosion_power, supermatter_gasmix_power_ratio, can_spawn_anomalies, supermatter_cascade) - . = ..() - - src.supermatter_power = supermatter_power - src.supermatter_gas_amount = supermatter_gas_amount - src.supermatter_turf = supermatter_turf - src.supermatter_explosion_power = supermatter_explosion_power - src.supermatter_gasmix_power_ratio = supermatter_gasmix_power_ratio - - if(supermatter_cascade) - start_supermatter_cascade() - return - - setup_mob_interaction() - setup_delamination_type() - - if(!should_spawn_anomalies || !can_spawn_anomalies) - qdel(src) - return - - setup_anomalies() - -/** - * What the mobs should deal with when a delamination happens - */ -/datum/supermatter_delamination/proc/setup_mob_interaction() - for(var/mob/living/victim as anything in GLOB.alive_mob_list) - if(!istype(victim) || victim.z != supermatter_turf.z) - continue - - if(ishuman(victim)) - //Hilariously enough, running into a closet should make you get hit the hardest. - var/mob/living/carbon/human/human = victim - human.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(victim, src) + 1)) ) ) - - if (get_dist(victim, src) <= DETONATION_RADIATION_RANGE) - SSradiation.irradiate(victim) - - for(var/mob/victim as anything in GLOB.player_list) - var/turf/mob_turf = get_turf(victim) - if(supermatter_turf.z != mob_turf.z) - continue - - SEND_SOUND(victim, 'sound/magic/charge.ogg') - - if (victim.z != supermatter_turf.z) - to_chat(victim, span_boldannounce("You hold onto \the [victim.loc] as hard as you can, as reality distorts around you. You feel safe.")) - continue - - to_chat(victim, span_boldannounce("You feel reality distort for a moment...")) - SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "delam", /datum/mood_event/delam) - -/** - * Setup for the types of possible delaminations and their effects (singulo, tesla or normal) - */ -/datum/supermatter_delamination/proc/setup_delamination_type() - if(supermatter_gas_amount > MOLE_PENALTY_THRESHOLD) - call_singulo() - return - if(supermatter_power > POWER_PENALTY_THRESHOLD) - call_tesla() - return - - call_explosion() - -/** - * Spawns the singularity - */ -/datum/supermatter_delamination/proc/call_singulo() - if(!supermatter_turf) //If something fucks up we blow anyhow. This fix is 4 years old and none ever said why it's here. help. - call_explosion() - return - var/obj/singularity/created_singularity = new(supermatter_turf) - created_singularity.energy = 800 - created_singularity.consume(src) - should_spawn_anomalies = FALSE - -/** - * Spawns the tesla - */ -/datum/supermatter_delamination/proc/call_tesla() - if(supermatter_turf) - var/obj/energy_ball/created_tesla = new(supermatter_turf) - created_tesla.energy = 200 //Gets us about 9 balls - call_explosion() - should_spawn_anomalies = FALSE - -/** - * Spawns the explosion - */ -/datum/supermatter_delamination/proc/call_explosion() - //Dear mappers, balance the sm max explosion radius to 17.5, 37, 39, 41 - explosion(origin = supermatter_turf, - devastation_range = supermatter_explosion_power * max(supermatter_gasmix_power_ratio, 0.205) * 0.5, - heavy_impact_range = supermatter_explosion_power * max(supermatter_gasmix_power_ratio, 0.205) + 2, - light_impact_range = supermatter_explosion_power * max(supermatter_gasmix_power_ratio, 0.205) + 4, - flash_range = supermatter_explosion_power * max(supermatter_gasmix_power_ratio, 0.205) + 6, - adminlog = TRUE, - ignorecap = TRUE - ) - -/** - * Setups how many anomalies to spawn - */ -/datum/supermatter_delamination/proc/setup_anomalies() - anomalies_to_spawn = max(round(0.004 * supermatter_power, 1) + rand(-2, 2), 1) - spawn_anomalies() - -/** - * Spawns the first half anomalies instantly and calls the second half - */ -/datum/supermatter_delamination/proc/spawn_anomalies() - var/list/anomaly_types = list(GRAVITATIONAL_ANOMALY = 55, HALLUCINATION_ANOMALY = 45, BIOSCRAMBLER_ANOMALY = 35, FLUX_ANOMALY = 25, PYRO_ANOMALY = 5, VORTEX_ANOMALY = 1) - var/list/anomaly_places = GLOB.generic_event_spawns - var/currently_spawning_anomalies = round(anomalies_to_spawn * 0.5, 1) - anomalies_to_spawn -= currently_spawning_anomalies - for(var/i in 1 to currently_spawning_anomalies) - var/anomaly_to_spawn = pick_weight(anomaly_types) - var/anomaly_location = pick_n_take(anomaly_places) - supermatter_anomaly_gen(anomaly_location, anomaly_to_spawn, has_changed_lifespan = FALSE) - - spawn_overtime() - -/** - * Spawns the second half anomalies after a delay - */ -/datum/supermatter_delamination/proc/spawn_overtime() - - var/list/anomaly_types = list(GRAVITATIONAL_ANOMALY = 55, HALLUCINATION_ANOMALY = 45, BIOSCRAMBLER_ANOMALY = 35, FLUX_ANOMALY = 25, PYRO_ANOMALY = 5, VORTEX_ANOMALY = 1) - var/list/anomaly_places = GLOB.generic_event_spawns - - var/current_spawn = rand(5 SECONDS, 10 SECONDS) - for(var/i in 1 to anomalies_to_spawn) - var/anomaly_to_spawn = pick_weight(anomaly_types) - var/anomaly_location = pick_n_take(anomaly_places) - var/next_spawn = rand(5 SECONDS, 10 SECONDS) - var/extended_spawn = 0 - if(DT_PROB(1, next_spawn)) - extended_spawn = rand(5 MINUTES, 15 MINUTES) - addtimer(CALLBACK(src, .proc/spawn_anomaly, anomaly_location, anomaly_to_spawn), current_spawn + extended_spawn) - current_spawn += next_spawn - -/** - * Callback for the anomalies to spawn after some time - */ -/datum/supermatter_delamination/proc/spawn_anomaly(location, type) - supermatter_anomaly_gen(location, type, has_changed_lifespan = FALSE) - -/** - * Setup for the cascade delamination - */ -/datum/supermatter_delamination/proc/start_supermatter_cascade() - // buncha shuttle manipulation incoming - - // set timer to infinity, so shuttle never arrives - SSshuttle.emergency.setTimer(INFINITY) - // disallow shuttle recalls, so people cannot cheese the timer - SSshuttle.emergency_no_recall = TRUE - // set supermatter cascade to true, to prevent auto evacuation due to no way of calling the shuttle - SSshuttle.supermatter_cascade = TRUE - // This logic is to keep uncalled shuttles uncalled - // In SSshuttle, there is not much of a way to prevent shuttle calls, unless we mess with admin panel vars - // SHUTTLE_STRANDED is different here, because it *can* block the shuttle from being called, however if we don't register a hostile - // environment, it gets unset immediately. Internally, it checks if the count of HEs is zero - // and that the shuttle is in stranded mode, then frees it with an announcement. - // This is a botched solution to a problem that could be solved with a small change in shuttle code, however- - if(SSshuttle.emergency.mode == SHUTTLE_IDLE) - SSshuttle.emergency.mode = SHUTTLE_STRANDED - SSshuttle.registerHostileEnvironment(src) - // set hijack completion timer to infinity, so that you cant prematurely end the round with a hijack - for(var/obj/machinery/computer/emergency_shuttle/console in GLOB.machines) - console.hijack_completion_flight_time_set = INFINITY - - for(var/mob/player as anything in GLOB.player_list) - if(!isdead(player)) - to_chat(player, span_boldannounce("Everything around you is resonating with a powerful energy. This can't be good.")) - SEND_SIGNAL(player, COMSIG_ADD_MOOD_EVENT, "cascade", /datum/mood_event/cascade) - SEND_SOUND(player, 'sound/magic/charge.ogg') - - call_explosion() - create_cascade_ambience() - warn_crew() - - var/rift_loc = pick_rift_location() - new /obj/crystal_mass(supermatter_turf) - - var/list/mass_loc_candidates = GLOB.generic_event_spawns.Copy() - mass_loc_candidates.Remove(rift_loc) // this should now actually get rid of stalemates - for(var/i in 1 to rand(4,6)) - var/list/loc_list = mass_loc_candidates.Copy() - var/mass_loc - do - mass_loc = pick_n_take(loc_list) - while(get_dist(mass_loc, rift_loc) < MIN_RIFT_SAFE_DIST) - new /obj/crystal_mass(get_turf(mass_loc)) - - SSsupermatter_cascade.cascade_initiated = TRUE - -/** - * Adds a bit of spiciness to the cascade by breaking lights and turning emergency maint access on - */ -/datum/supermatter_delamination/proc/create_cascade_ambience() - if(SSsecurity_level.get_current_level_as_number() != SEC_LEVEL_DELTA) - SSsecurity_level.set_level(SEC_LEVEL_DELTA) // skip the announcement and shuttle timer adjustment in set_security_level() - make_maint_all_access() - break_lights_on_station() - -/** - * Picks a random location for the rift - * Returns: ref to rift location - */ -/datum/supermatter_delamination/proc/pick_rift_location() - var/rift_spawn = pick(GLOB.generic_event_spawns) - var/turf/rift_turf = get_turf(rift_spawn) - cascade_rift = new /obj/cascade_portal(rift_turf) - message_admins("Exit rift created at [get_area_name(rift_turf)]. [ADMIN_JMP(cascade_rift)]") - log_game("Bluespace Exit Rift was created at [get_area_name(rift_turf)].") - cascade_rift.investigate_log("created at [get_area_name(rift_turf)].", INVESTIGATE_ENGINE) - RegisterSignal(cascade_rift, COMSIG_PARENT_QDELETING, .proc/deleted_portal) - return rift_spawn - -/** - * Warns the crew about the cascade start and the rift location - */ -/datum/supermatter_delamination/proc/warn_crew() - priority_announce("A Type-C resonance shift event has occurred in your sector. Scans indicate local oscillation flux affecting spatial and gravitational substructure. \ - Multiple resonance hotspots have formed. Please standby.", "Nanotrasen Star Observation Association", ANNOUNCER_SPANOMALIES) - - if(SSshuttle.emergency.mode != SHUTTLE_STRANDED) - addtimer(CALLBACK(src, .proc/announce_shuttle_gone), 2 SECONDS) - - addtimer(CALLBACK(src, .proc/announce_beginning), 5 SECONDS) - -/** - * Logs the deletion of the bluespace rift, and starts countdown to the end of the round. - */ -/datum/supermatter_delamination/proc/deleted_portal() - SIGNAL_HANDLER - message_admins("[cascade_rift] deleted at [get_area_name(cascade_rift.loc)]. [ADMIN_JMP(cascade_rift.loc)]") - log_game("[cascade_rift] was deleted.") - cascade_rift.investigate_log("was deleted.", INVESTIGATE_ENGINE) - - priority_announce("[Gibberish("The rift has been destroyed, we can no longer help you.", FALSE, 5)]") - - addtimer(CALLBACK(src, .proc/announce_gravitation_shift), 25 SECONDS) - addtimer(CALLBACK(src, .proc/last_message), 50 SECONDS) - if(SSshuttle.emergency.mode != SHUTTLE_ESCAPE) // if the shuttle is enroute to centcom, we let the shuttle end the round - addtimer(CALLBACK(src, .proc/the_end), 1 MINUTES) - -/** - * Announces the halfway point to the end. - */ -/datum/supermatter_delamination/proc/announce_gravitation_shift() - priority_announce("Reports indicate formation of crystalline seeds following resonance shift event. \ - Rapid expansion of crystal mass proportional to rising gravitational force. \ - Matter collapse due to gravitational pull foreseeable.", - "Nanotrasen Star Observation Association") - -/** - * This proc manipulates the shuttle if it's enroute to centcom, to remain in hyperspace. Otherwise, it just plays an announcement if - * the shuttle was in any other state except stranded (idle) - */ -/datum/supermatter_delamination/proc/announce_shuttle_gone() - // say goodbye to that shuttle of yours - if(SSshuttle.emergency.mode != SHUTTLE_ESCAPE) - priority_announce("Fatal error occurred in emergency shuttle uplink during transit. Unable to reestablish connection.", - "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg') - else - // except if you are on it already, then you are safe c: - minor_announce("ERROR: Corruption detected in navigation protocols. Connection with Transponder #XCC-P5831-ES13 lost. \ - Backup exit route protocol decrypted. Calibrating route...", - "Emergency Shuttle", TRUE) // wait out until the rift on the station gets destroyed and the final message plays - var/list/mobs = mobs_in_area_type(list(/area/shuttle/escape)) - for(var/mob/living/mob as anything in mobs) // emulate mob/living/lateShuttleMove() behaviour - if(mob.buckled) - continue - if(mob.client) - shake_camera(mob, 3 SECONDS * 0.25, 1) - mob.Paralyze(3 SECONDS, TRUE) - -/** - * Announces the last message to the station, frees the shuttle from purgatory if applicable - */ -/datum/supermatter_delamination/proc/last_message() - priority_announce("[Gibberish("All attempts at evacuation have now ceased, and all assets have been retrieved from your sector.\n \ - To the remaining survivors of [station_name()], farewell.", FALSE, 5)]") - - if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE) - // special message for hijacks - var/shuttle_msg = "Navigation protocol set to [SSshuttle.emergency.is_hijacked() ? "\[ERROR\]" : "backup route"]. \ - Reorienting bluespace vessel to exit vector. ETA 15 seconds." - // garble the special message - if(SSshuttle.emergency.is_hijacked()) - shuttle_msg = Gibberish(shuttle_msg, TRUE, 15) - minor_announce(shuttle_msg, "Emergency Shuttle", TRUE) - SSshuttle.emergency.setTimer(15 SECONDS) - -/** - * Announce detail about the event, as well as rift location - */ -/datum/supermatter_delamination/proc/announce_beginning() - priority_announce("We have been hit by a sector-wide electromagnetic pulse. All of our systems are heavily damaged, including those \ - required for shuttle navigation. We can only reasonably conclude that a supermatter cascade is occurring on or near your station.\n\n\ - Evacuation is no longer possible by conventional means; however, we managed to open a rift near the [get_area_name(cascade_rift)]. \ - All personnel are hereby required to enter the rift by any means available.\n\n\ - [Gibberish("Retrieval of survivors will be conducted upon recovery of necessary facilities.", FALSE, 5)] \ - [Gibberish("Good luck--", FALSE, 25)]") - -/** - * Ends the round - */ -/datum/supermatter_delamination/proc/the_end() - SSticker.news_report = SUPERMATTER_CASCADE - SSticker.force_ending = TRUE - -/** - * Break the lights on the station, have 35% of them be set to emergency - */ -/datum/supermatter_delamination/proc/break_lights_on_station() - for(var/obj/machinery/light/light_to_break in GLOB.machines) - if(prob(35)) - light_to_break.set_major_emergency_light() - continue - light_to_break.break_light_tube() - -#undef MIN_RIFT_SAFE_DIST diff --git a/code/modules/power/supermatter/supermatter_delamination/_sm_delam.dm b/code/modules/power/supermatter/supermatter_delamination/_sm_delam.dm new file mode 100644 index 00000000000..06e883257d1 --- /dev/null +++ b/code/modules/power/supermatter/supermatter_delamination/_sm_delam.dm @@ -0,0 +1,106 @@ +/// Priority is top to bottom. +GLOBAL_LIST_INIT(sm_delam_list, list( + /datum/sm_delam/cascade = new /datum/sm_delam/cascade, + /datum/sm_delam/singularity = new /datum/sm_delam/singularity, + /datum/sm_delam/tesla = new /datum/sm_delam/tesla, + /datum/sm_delam/explosive = new /datum/sm_delam/explosive, +)) + +/// Logic holder for supermatter delaminations, goes off the strategy design pattern. +/// Selected by [/obj/machinery/power/supermatter_crystal/proc/set_delam] +/datum/sm_delam + +/// Whether we are eligible for this delamination or not. TRUE if valid, FALSE if not. +/// [/obj/machinery/power/supermatter_crystal/proc/set_delam] +/datum/sm_delam/proc/can_select(obj/machinery/power/supermatter_crystal/sm) + return FALSE + +/// Called when the count down has been finished, do the nasty work. +/// [/obj/machinery/power/supermatter_crystal/proc/count_down] +/datum/sm_delam/proc/delaminate(obj/machinery/power/supermatter_crystal/sm) + qdel(sm) + +/// Whatever we're supposed to do when a delam is currently in progress. +/// Mostly just to tell people how useless engi is, and play some alarm sounds. +/// Returns TRUE if we just told people a delam is going on. FALSE if its healing or we didnt say anything. +/// [/obj/machinery/power/supermatter_crystal/proc/process_atmos] +/datum/sm_delam/proc/delam_progress(obj/machinery/power/supermatter_crystal/sm) + if(sm.damage <= sm.warning_point) // Damage is too low, lets not + return FALSE + + if (sm.damage >= sm.emergency_point && sm.damage_archived < sm.emergency_point) + sm.investigate_log("has entered the emergency point.", INVESTIGATE_ENGINE) + message_admins("[sm] has entered the emergency point [ADMIN_VERBOSEJMP(sm)].") + + if((REALTIMEOFDAY - sm.lastwarning) < SUPERMATTER_WARNING_DELAY) + return FALSE + sm.lastwarning = REALTIMEOFDAY + + switch(sm.get_status()) + if(SUPERMATTER_DELAMINATING) + // SKYRAT EDIT ADDITION + alert_sound_to_playing('modular_skyrat/master_files/sound/effects/reactor/meltdown.ogg', override_volume = TRUE) + alert_sound_to_playing('modular_skyrat/modules/alerts/sound/alerts/alert1.ogg', override_volume = TRUE) + // SKYRAT EDIT END + playsound(sm, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10) + if(SUPERMATTER_EMERGENCY) + // SKYRAT EDIT ADDITION + alert_sound_to_playing('modular_skyrat/master_files/sound/effects/reactor/core_overheating.ogg', override_volume = TRUE) + alert_sound_to_playing('modular_skyrat/modules/alerts/sound/alerts/alert1.ogg', override_volume = TRUE) + // SKYRAT EDIT END + playsound(sm, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10) + if(SUPERMATTER_DANGER) + playsound(sm, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10) + if(SUPERMATTER_WARNING) + playsound(sm, 'sound/machines/terminal_alert.ogg', 75) + + if(sm.damage < sm.damage_archived) // Healing + sm.radio.talk_into(sm,"Crystalline hyperstructure returning to safe operating parameters. Integrity: [sm.get_integrity_percent()]%", sm.damage_archived >= sm.emergency_point ? sm.emergency_channel : sm.warning_channel) + return FALSE + + if(sm.damage >= sm.emergency_point) // Taking damage, in emergency + sm.radio.talk_into(sm, "CRYSTAL DELAMINATION IMMINENT Integrity: [sm.get_integrity_percent()]%", sm.emergency_channel) + sm.lastwarning = REALTIMEOFDAY - (SUPERMATTER_WARNING_DELAY / 2) // Cut the time to next announcement in half. + else // Taking damage, in warning + sm.radio.talk_into(sm, "Danger! Crystal hyperstructure integrity faltering! Integrity: [sm.get_integrity_percent()]%", sm.warning_channel) + if(sm.damage_archived < sm.warning_point) + SEND_SIGNAL(sm, COMSIG_SUPERMATTER_DELAM_START_ALARM) + + SEND_SIGNAL(sm, COMSIG_SUPERMATTER_DELAM_ALARM) + return TRUE + +/// Called when a supermatter switches it's strategy from another one to us. +/// [/obj/machinery/power/supermatter_crystal/proc/set_delam] +/datum/sm_delam/proc/on_select(obj/machinery/power/supermatter_crystal/sm) + return + +/// Called when a supermatter switches it's strategy from us to something else. +/// [/obj/machinery/power/supermatter_crystal/proc/set_delam] +/datum/sm_delam/proc/on_deselect(obj/machinery/power/supermatter_crystal/sm) + return + +/// Added to an examine return value. +/// [/obj/machinery/power/supermatter_crystal/proc/examine] +/datum/sm_delam/proc/examine(obj/machinery/power/supermatter_crystal/sm) + return list() + +/// Add whatever overlay to the sm. +/// [/obj/machinery/power/supermatter_crystal/proc/overlays] +/datum/sm_delam/proc/overlays(obj/machinery/power/supermatter_crystal/sm) + if(sm.final_countdown) + return list(mutable_appearance(sm.icon, "causality_field")) + return list() + +/// Modifies the damage dealt to the sm. +/// [/obj/machinery/power/supermatter_crystal/proc/deal_damage] +/datum/sm_delam/proc/damage_multiplier(obj/machinery/power/supermatter_crystal/sm) + return 1 + +/// Returns a set of messages to be spouted during delams +/// First message is start of count down, second message is quitting of count down (if sm healed), third is 5 second intervals +/datum/sm_delam/proc/count_down_messages(obj/machinery/power/supermatter_crystal/sm) + var/list/messages = list() + messages += "CRYSTAL DELAMINATION IMMINENT. The supermatter has reached critical integrity failure. Emergency causality destabilization field has been activated." + messages += "Crystalline hyperstructure returning to safe operating parameters. Failsafe has been disengaged." + messages += "remain before causality stabilization." + return messages diff --git a/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm b/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm new file mode 100644 index 00000000000..a81b6f0dd8c --- /dev/null +++ b/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm @@ -0,0 +1,97 @@ +/datum/sm_delam/cascade + +/datum/sm_delam/cascade/can_select(obj/machinery/power/supermatter_crystal/sm) + if(!sm.is_main_engine) + return FALSE + var/total_moles = sm.absorbed_gasmix.total_moles() + if(total_moles < MOLE_PENALTY_THRESHOLD * sm.absorption_ratio) + return FALSE + for (var/gas_path in list(/datum/gas/antinoblium, /datum/gas/hypernoblium)) + var/percent = sm.absorbed_gasmix.gases[gas_path]?[MOLES] / total_moles + if(!percent || percent < 0.4) + return FALSE + return TRUE + +/datum/sm_delam/cascade/delam_progress(obj/machinery/power/supermatter_crystal/sm) + . = ..() + if(!.) + return FALSE + sm.radio.talk_into( + sm, + "DANGER: HYPERSTRUCTURE OSCILLATION FREQUENCY OUT OF BOUNDS.", + sm.damage >= sm.emergency_point ? sm.emergency_channel : sm.warning_channel + ) + var/list/messages = list( + "Space seems to be shifting around you...", + "You hear a high-pitched ringing sound.", + "You feel tingling going down your back.", + "Something feels very off.", + "A drowning sense of dread washes over you.", + ) + for(var/mob/victim as anything in GLOB.player_list) + to_chat(victim, span_danger(pick(messages))) + +/datum/sm_delam/cascade/on_select(obj/machinery/power/supermatter_crystal/sm) + message_admins("[sm] is heading towards a cascade. [ADMIN_VERBOSEJMP(sm)]") + sm.investigate_log("is heading towards a cascade.", INVESTIGATE_ENGINE) + + sm.warp = new(sm) + sm.vis_contents += sm.warp + animate(sm.warp, time = 1, transform = matrix().Scale(0.5,0.5)) + animate(time = 9, transform = matrix()) + + addtimer(CALLBACK(src, .proc/announce_cascade, sm), 2 MINUTES) + +/datum/sm_delam/cascade/on_deselect(obj/machinery/power/supermatter_crystal/sm) + message_admins("[sm] will no longer cascade. [ADMIN_VERBOSEJMP(sm)]") + sm.investigate_log("will no longer cascade.", INVESTIGATE_ENGINE) + + sm.vis_contents -= sm.warp + QDEL_NULL(sm.warp) + +/datum/sm_delam/cascade/delaminate(obj/machinery/power/supermatter_crystal/sm) + message_admins("Supermatter [sm] at [ADMIN_VERBOSEJMP(sm)] triggered a cascade delam.") + sm.investigate_log("triggered a cascade delam.", INVESTIGATE_ENGINE) + + effect_explosion(sm) + effect_emergency_state() + effect_cascade_demoralize() + priority_announce("A Type-C resonance shift event has occurred in your sector. Scans indicate local oscillation flux affecting spatial and gravitational substructure. \ + Multiple resonance hotspots have formed. Please standby.", "Nanotrasen Star Observation Association", ANNOUNCER_SPANOMALIES) + sleep(2 SECONDS) + effect_strand_shuttle() + sleep(5 SECONDS) + var/obj/cascade_portal/rift = effect_evac_rift_start() + RegisterSignal(rift, COMSIG_PARENT_QDELETING, .proc/end_round_holder) + effect_crystal_mass(sm, rift) + SSsupermatter_cascade.cascade_initiated = TRUE + qdel(sm) + +/datum/sm_delam/cascade/examine(obj/machinery/power/supermatter_crystal/sm) + return list(span_bolddanger("The crystal is vibrating at immense speeds, warping space around it!")) + +/datum/sm_delam/cascade/overlays(obj/machinery/power/supermatter_crystal/sm) + return list() + +/datum/sm_delam/cascade/damage_multiplier(obj/machinery/power/supermatter_crystal/sm) + return 0.25 + +/datum/sm_delam/cascade/count_down_messages(obj/machinery/power/supermatter_crystal/sm) + var/list/messages = list() + messages += "CRYSTAL DELAMINATION IMMINENT. The supermatter has reached critical integrity failure. Harmonic frequency limits exceeded. Causality destabilization field could not be engaged." + messages += "Crystalline hyperstructure returning to safe operating parameters. Harmonic frequency restored within emergency bounds. Anti-resonance filter initiated." + messages += "remain before resonance-induced stabilization." + return messages + +/datum/sm_delam/cascade/proc/announce_cascade(obj/machinery/power/supermatter_crystal/sm) + if(!can_select(sm)) + return FALSE + priority_announce("Attention: Long range anomaly scans indicate abnormal quantities of harmonic flux originating from \ + a subject within [station_name()], a resonance collapse may occur.", + "Nanotrasen Star Observation Association") + return TRUE + +/// Signal calls cant sleep, we gotta do this. +/datum/sm_delam/cascade/proc/end_round_holder() + SIGNAL_HANDLER + INVOKE_ASYNC(src, .proc/effect_evac_rift_end) diff --git a/code/modules/power/supermatter/supermatter_cascade_components.dm b/code/modules/power/supermatter/supermatter_delamination/cascade_delam_objects.dm similarity index 89% rename from code/modules/power/supermatter/supermatter_cascade_components.dm rename to code/modules/power/supermatter/supermatter_delamination/cascade_delam_objects.dm index 6851760b6ac..68f9fdc3856 100644 --- a/code/modules/power/supermatter/supermatter_cascade_components.dm +++ b/code/modules/power/supermatter/supermatter_delamination/cascade_delam_objects.dm @@ -110,6 +110,22 @@ pixel_y = -96 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF +/obj/cascade_portal/Initialize(mapload) + . = ..() + var/turf/location = get_turf(src) + var/area_name = get_area_name(src) + message_admins("Exit rift created at [area_name]. [ADMIN_VERBOSEJMP(location)]") + log_game("Bluespace Exit Rift was created at [area_name].") + investigate_log("created at [area_name].", INVESTIGATE_ENGINE) + +/obj/cascade_portal/Destroy(force) + var/turf/location = get_turf(src) + var/area_name = get_area_name(src) + message_admins("Exit rift at [area_name] deleted. [ADMIN_VERBOSEJMP(location)]") + log_game("Bluespace Exit Rift at [area_name] was deleted.") + investigate_log("was deleted.", INVESTIGATE_ENGINE) + return ..() + /obj/cascade_portal/Bumped(atom/movable/hit_object) consume(hit_object) new /obj/effect/particle_effect/sparks(loc) @@ -135,7 +151,7 @@ while(!is_safe_turf(arrival_turf)) var/mob/living/consumed_mob = consumed_object - message_admins("[key_name_admin(consumed_mob)] has entered [src] [ADMIN_JMP(src)].") + message_admins("[key_name_admin(consumed_mob)] has entered [src] [ADMIN_VERBOSEJMP(src)].") investigate_log("was entered by [key_name(consumed_mob)].", INVESTIGATE_ENGINE) consumed_mob.forceMove(arrival_turf) consumed_mob.Paralyze(100) @@ -149,3 +165,4 @@ span_hear("You hear a loud crack as a small distortion passes through you.")) qdel(consumed_object) + diff --git a/code/modules/power/supermatter/supermatter_delamination/common_delams.dm b/code/modules/power/supermatter/supermatter_delamination/common_delams.dm new file mode 100644 index 00000000000..d1dd277c554 --- /dev/null +++ b/code/modules/power/supermatter/supermatter_delamination/common_delams.dm @@ -0,0 +1,74 @@ +// Singulo, tesla, and explosive delam + +/// When we have too much gas. +/datum/sm_delam/singularity + +/datum/sm_delam/singularity/can_select(obj/machinery/power/supermatter_crystal/sm) + return (sm.absorbed_gasmix.total_moles() >= MOLE_PENALTY_THRESHOLD) + +/datum/sm_delam/singularity/delam_progress(obj/machinery/power/supermatter_crystal/sm) + . = ..() + if(!.) + return FALSE + sm.radio.talk_into( + sm, + "Warning: Critical coolant mass reached.", + sm.damage > sm.emergency_point ? sm.emergency_channel : sm.warning_channel + ) + +/datum/sm_delam/singularity/delaminate(obj/machinery/power/supermatter_crystal/sm) + message_admins("Supermatter [sm] at [ADMIN_VERBOSEJMP(sm)] triggered a singularity delam.") + sm.investigate_log("triggered a singularity delam.", INVESTIGATE_ENGINE) + + effect_irradiate(sm) + effect_demoralize(sm) + if(sm.is_main_engine) + effect_anomaly(sm) + if(!effect_singulo(sm)) + effect_explosion(sm) + qdel(sm) + +/// When we have too much power. +/datum/sm_delam/tesla + +/datum/sm_delam/tesla/can_select(obj/machinery/power/supermatter_crystal/sm) + return (sm.power > POWER_PENALTY_THRESHOLD) + +/datum/sm_delam/tesla/delam_progress(obj/machinery/power/supermatter_crystal/sm) + . = ..() + if(!.) + return FALSE + sm.radio.talk_into( + sm, + "DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.", + sm.damage > sm.emergency_point ? sm.emergency_channel : sm.warning_channel + ) + +/datum/sm_delam/tesla/delaminate(obj/machinery/power/supermatter_crystal/sm) + message_admins("Supermatter [sm] at [ADMIN_VERBOSEJMP(sm)] triggered a tesla delam.") + sm.investigate_log("triggered a tesla delam.", INVESTIGATE_ENGINE) + + effect_irradiate(sm) + effect_demoralize(sm) + if(sm.is_main_engine) + effect_anomaly(sm) + effect_tesla(sm) + effect_explosion(sm) + qdel(sm) + +/// Default delam. +/datum/sm_delam/explosive + +/datum/sm_delam/explosive/can_select(obj/machinery/power/supermatter_crystal/sm) + return TRUE + +/datum/sm_delam/explosive/delaminate(obj/machinery/power/supermatter_crystal/sm) + message_admins("Supermatter [sm] at [ADMIN_VERBOSEJMP(sm)] triggered a normal delam.") + sm.investigate_log("triggered a normal delam.", INVESTIGATE_ENGINE) + + effect_irradiate(sm) + effect_demoralize(sm) + if(sm.is_main_engine) + effect_anomaly(sm) + effect_explosion(sm) + qdel(sm) diff --git a/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm new file mode 100644 index 00000000000..c6e4413c2ab --- /dev/null +++ b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm @@ -0,0 +1,212 @@ +// These are supposed to be discrete effects so we can tell at a glance what does each override +// of [/datum/sm_delam/proc/delaminate] does. +// Please keep them discrete and give them proper, descriptive function names. +// Oh and all of them returns true if the effect succeeded. + +/// Irradiates mobs around 20 tiles of the sm. +/// Just the mobs apparently. +/datum/sm_delam/proc/effect_irradiate(obj/machinery/power/supermatter_crystal/sm) + var/turf/sm_turf = get_turf(sm) + for (var/mob/living/victim in range(20, sm)) + if(victim.z != sm_turf.z) + continue + SSradiation.irradiate(victim) + return TRUE + +/// Hallucinates and makes mobs in Z level sad. +/datum/sm_delam/proc/effect_demoralize(obj/machinery/power/supermatter_crystal/sm) + var/turf/sm_turf = get_turf(sm) + for(var/mob/living/victim as anything in GLOB.alive_mob_list) + if(!istype(victim) || victim.z != sm_turf.z) + continue + if(ishuman(victim)) + //Hilariously enough, running into a closet should make you get hit the hardest. + var/mob/living/carbon/human/human = victim + human.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(victim, sm) + 1)) ) ) + + for(var/mob/victim as anything in GLOB.player_list) + var/turf/mob_turf = get_turf(victim) + if(sm_turf.z != mob_turf.z) + continue + SEND_SOUND(victim, 'sound/magic/charge.ogg') + if (victim.z != sm_turf.z) + to_chat(victim, span_boldannounce("You hold onto \the [victim.loc] as hard as you can, as reality distorts around you. You feel safe.")) + continue + to_chat(victim, span_boldannounce("You feel reality distort for a moment...")) + SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "delam", /datum/mood_event/delam) + return TRUE + +/// Spawns anomalies all over the station. Half instantly, the other half over time. +/datum/sm_delam/proc/effect_anomaly(obj/machinery/power/supermatter_crystal/sm) + var/anomalies = 10 + var/list/anomaly_types = list(GRAVITATIONAL_ANOMALY = 55, HALLUCINATION_ANOMALY = 45, DELIMBER_ANOMALY = 35, FLUX_ANOMALY = 25, PYRO_ANOMALY = 5, VORTEX_ANOMALY = 1) + var/list/anomaly_places = GLOB.generic_event_spawns + + // Spawns this many anomalies instantly. Spawns the rest with callbacks. + var/cutoff_point = round(anomalies * 0.5, 1) + + for(var/i in 1 to anomalies) + var/anomaly_to_spawn = pick_weight(anomaly_types) + var/anomaly_location = pick_n_take(anomaly_places) + + if(i < cutoff_point) + supermatter_anomaly_gen(anomaly_location, anomaly_to_spawn, has_changed_lifespan = FALSE) + continue + + var/current_spawn = rand(5 SECONDS, 10 SECONDS) + var/next_spawn = rand(5 SECONDS, 10 SECONDS) + var/extended_spawn = 0 + if(DT_PROB(1, next_spawn)) + extended_spawn = rand(5 MINUTES, 15 MINUTES) + addtimer(CALLBACK(GLOBAL_PROC, .proc/supermatter_anomaly_gen, anomaly_location, anomaly_to_spawn, TRUE), current_spawn + extended_spawn) + return TRUE + +/// Explodes +/datum/sm_delam/proc/effect_explosion(obj/machinery/power/supermatter_crystal/sm) + var/explosion_power = sm.explosion_power + var/power_scaling = sm.gasmix_power_ratio + var/turf/sm_turf = get_turf(sm) + //Dear mappers, balance the sm max explosion radius to 17.5, 37, 39, 41 + explosion(origin = sm_turf, + devastation_range = explosion_power * max(power_scaling, 0.205) * 0.5, + heavy_impact_range = explosion_power * max(power_scaling, 0.205) + 2, + light_impact_range = explosion_power * max(power_scaling, 0.205) + 4, + flash_range = explosion_power * max(power_scaling, 0.205) + 6, + adminlog = TRUE, + ignorecap = TRUE + ) + return TRUE + +/// Spawns a scrung and eat the SM. +/datum/sm_delam/proc/effect_singulo(obj/machinery/power/supermatter_crystal/sm) + var/turf/sm_turf = get_turf(sm) + if(!sm_turf) + stack_trace("Supermatter [sm] failed to spawn singularity, cant get current turf.") + return FALSE + var/obj/singularity/created_singularity = new(sm_turf) + created_singularity.energy = 800 + created_singularity.consume(sm) + return TRUE + +/// Teslas +/datum/sm_delam/proc/effect_tesla(obj/machinery/power/supermatter_crystal/sm) + var/turf/sm_turf = get_turf(sm) + if(!sm_turf) + stack_trace("Supermatter [sm] failed to spawn tesla, cant get current turf.") + return FALSE + var/obj/energy_ball/created_tesla = new(sm_turf) + created_tesla.energy = 200 //Gets us about 9 balls + return TRUE + +/// Mail the shuttle off to buy milk. +/datum/sm_delam/proc/effect_strand_shuttle() + set waitfor = FALSE + // set timer to infinity, so shuttle never arrives + SSshuttle.emergency.setTimer(INFINITY) + // disallow shuttle recalls, so people cannot cheese the timer + SSshuttle.emergency_no_recall = TRUE + // set supermatter cascade to true, to prevent auto evacuation due to no way of calling the shuttle + SSshuttle.supermatter_cascade = TRUE + // set hijack completion timer to infinity, so that you cant prematurely end the round with a hijack + for(var/obj/machinery/computer/emergency_shuttle/console in GLOB.machines) + console.hijack_completion_flight_time_set = INFINITY + + /* This logic is to keep uncalled shuttles uncalled + In SSshuttle, there is not much of a way to prevent shuttle calls, unless we mess with admin panel vars + SHUTTLE_STRANDED is different here, because it *can* block the shuttle from being called, however if we don't register a hostile + environment, it gets unset immediately. Internally, it checks if the count of HEs is zero + and that the shuttle is in stranded mode, then frees it with an announcement. + This is a botched solution to a problem that could be solved with a small change in shuttle code, however- + */ + if(SSshuttle.emergency.mode == SHUTTLE_IDLE) + SSshuttle.emergency.mode = SHUTTLE_STRANDED + SSshuttle.registerHostileEnvironment(src) + return + + // say goodbye to that shuttle of yours + if(SSshuttle.emergency.mode != SHUTTLE_ESCAPE) + priority_announce("Fatal error occurred in emergency shuttle uplink during transit. Unable to reestablish connection.", + "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg') + else + // except if you are on it already, then you are safe c: + minor_announce("ERROR: Corruption detected in navigation protocols. Connection with Transponder #XCC-P5831-ES13 lost. \ + Backup exit route protocol decrypted. Calibrating route...", + "Emergency Shuttle", TRUE) // wait out until the rift on the station gets destroyed and the final message plays + var/list/mobs = mobs_in_area_type(list(/area/shuttle/escape)) + for(var/mob/living/mob as anything in mobs) // emulate mob/living/lateShuttleMove() behaviour + if(mob.buckled) + continue + if(mob.client) + shake_camera(mob, 3 SECONDS * 0.25, 1) + mob.Paralyze(3 SECONDS, TRUE) + +/datum/sm_delam/proc/effect_cascade_demoralize() + for(var/mob/player as anything in GLOB.player_list) + if(!isdead(player)) + to_chat(player, span_boldannounce("Everything around you is resonating with a powerful energy. This can't be good.")) + SEND_SIGNAL(player, COMSIG_ADD_MOOD_EVENT, "cascade", /datum/mood_event/cascade) + SEND_SOUND(player, 'sound/magic/charge.ogg') + +/datum/sm_delam/proc/effect_emergency_state() + if(SSsecurity_level.get_current_level_as_number() != SEC_LEVEL_DELTA) + SSsecurity_level.set_level(SEC_LEVEL_DELTA) // skip the announcement and shuttle timer adjustment in set_security_level() + make_maint_all_access() + for(var/obj/machinery/light/light_to_break in GLOB.machines) + if(prob(35)) + light_to_break.set_major_emergency_light() + continue + light_to_break.break_light_tube() + +/// Spawn an evacuation rift for people to go through. +/datum/sm_delam/proc/effect_evac_rift_start() + var/obj/cascade_portal/rift = new /obj/cascade_portal(get_turf(pick(GLOB.generic_event_spawns))) + priority_announce("We have been hit by a sector-wide electromagnetic pulse. All of our systems are heavily damaged, including those \ + required for shuttle navigation. We can only reasonably conclude that a supermatter cascade is occurring on or near your station.\n\n\ + Evacuation is no longer possible by conventional means; however, we managed to open a rift near the [get_area_name(rift)]. \ + All personnel are hereby required to enter the rift by any means available.\n\n\ + [Gibberish("Retrieval of survivors will be conducted upon recovery of necessary facilities.", FALSE, 5)] \ + [Gibberish("Good luck--", FALSE, 25)]") + return rift + +/// Announce the destruction of the rift and end the round. +/datum/sm_delam/proc/effect_evac_rift_end() + priority_announce("[Gibberish("The rift has been destroyed, we can no longer help you.", FALSE, 5)]") + + sleep(25 SECONDS) + + priority_announce("Reports indicate formation of crystalline seeds following resonance shift event. \ + Rapid expansion of crystal mass proportional to rising gravitational force. \ + Matter collapse due to gravitational pull foreseeable.", + "Nanotrasen Star Observation Association") + + sleep(25 SECONDS) + + priority_announce("[Gibberish("All attempts at evacuation have now ceased, and all assets have been retrieved from your sector.\n \ + To the remaining survivors of [station_name()], farewell.", FALSE, 5)]") + + if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE) + // special message for hijacks + var/shuttle_msg = "Navigation protocol set to [SSshuttle.emergency.is_hijacked() ? "\[ERROR\]" : "backup route"]. \ + Reorienting bluespace vessel to exit vector. ETA 15 seconds." + // garble the special message + if(SSshuttle.emergency.is_hijacked()) + shuttle_msg = Gibberish(shuttle_msg, TRUE, 15) + minor_announce(shuttle_msg, "Emergency Shuttle", TRUE) + SSshuttle.emergency.setTimer(15 SECONDS) + return + + sleep(10 SECONDS) + + SSticker.news_report = SUPERMATTER_CASCADE + SSticker.force_ending = TRUE + +/// Scatters crystal mass over the event spawns as long as they are at least 30 tiles away from whatever we want to avoid. +/datum/sm_delam/proc/effect_crystal_mass(obj/machinery/power/supermatter_crystal/sm, avoid) + new /obj/crystal_mass(get_turf(sm)) + var/list/possible_spawns = GLOB.generic_event_spawns.Copy() + for(var/i in 1 to rand(4,6)) + var/spawn_location + do + spawn_location = pick_n_take(possible_spawns) + while(get_dist(spawn_location, avoid) < 30) + new /obj/crystal_mass(get_turf(spawn_location)) diff --git a/code/modules/power/supermatter/supermatter_hit_procs.dm b/code/modules/power/supermatter/supermatter_hit_procs.dm index 4824e69168b..ce1d657aab7 100644 --- a/code/modules/power/supermatter/supermatter_hit_procs.dm +++ b/code/modules/power/supermatter/supermatter_hit_procs.dm @@ -39,7 +39,7 @@ has_been_powered = TRUE else if(takes_damage) damage += (projectile.damage * bullet_energy) * clamp((emergency_point - damage) / emergency_point, 0, 1) - if(damage > damage_penalty_point) + if(damage > danger_point) visible_message(span_notice("[src] compresses under stress, resisting further impacts!")) return BULLET_ACT_HIT @@ -88,7 +88,7 @@ if(istype(item, /obj/item/destabilizing_crystal)) var/obj/item/destabilizing_crystal/destabilizing_crystal = item - if(!anomaly_event) + if(!is_main_engine) to_chat(user, span_warning("You can't use \the [destabilizing_crystal] on \a [name].")) return @@ -102,11 +102,9 @@ log_game("[key_name(user)] attached [destabilizing_crystal] to the supermatter at [AREACOORD(src)]") investigate_log("[key_name(user)] attached [destabilizing_crystal] to a supermatter crystal.", INVESTIGATE_ENGINE) to_chat(user, span_danger("\The [destabilizing_crystal] snaps onto \the [src].")) - has_destabilizing_crystal = TRUE - cascade_initiated = TRUE + set_delam(SM_DELAM_PRIO_IN_GAME, /datum/sm_delam/cascade) damage += 100 matter_power += 500 - addtimer(CALLBACK(src, .proc/announce_incoming_cascade), 2 MINUTES) qdel(destabilizing_crystal) return diff --git a/code/modules/power/supermatter/supermatter_process.dm b/code/modules/power/supermatter/supermatter_process.dm index bce171c7de5..3c3f7941499 100644 --- a/code/modules/power/supermatter/supermatter_process.dm +++ b/code/modules/power/supermatter/supermatter_process.dm @@ -20,13 +20,12 @@ //Ok, get the air from the turf var/datum/gas_mixture/env = local_turf.return_air() environment_total_moles = env.total_moles() - var/datum/gas_mixture/removed if(produces_gas) //Remove gas from surrounding area - removed = env.remove(gasefficency * env.total_moles()) + absorbed_gasmix = env.remove(absorption_ratio * env.total_moles()) else // Pass all the gas related code an empty gas container - removed = new() + absorbed_gasmix = new() overlays -= psyOverlay if(psy_overlay) overlays -= psyOverlay @@ -36,61 +35,38 @@ else psy_overlay = FALSE damage_archived = damage - if(!removed || !removed.total_moles() || isspaceturf(local_turf)) //we're in space or there is no gas to process + if(!absorbed_gasmix || !absorbed_gasmix.total_moles() || isspaceturf(local_turf)) //we're in space or there is no gas to process if(takes_damage) damage += max((power / 1000) * DAMAGE_INCREASE_MULTIPLIER, 0.1) // always does at least some damage if(!istype(env, /datum/gas_mixture/immutable) && produces_gas && power) //There is no gas to process, but we are not in a space turf. Lets make them. //Power * 0.55 * a value between 1 and 0.8 var/device_energy = power * REACTION_POWER_MODIFIER * (1 - (psyCoeff * 0.2)) //Can't do stuff if it's null, so lets make a new gasmix. - removed = new() + absorbed_gasmix = new() //Since there is no gas to process, we will produce as if heat penalty is 1 and temperature at TCMB. - removed.assert_gases(/datum/gas/plasma, /datum/gas/oxygen) - removed.temperature = ((device_energy) / THERMAL_RELEASE_MODIFIER) - removed.temperature = max(TCMB, min(removed.temperature, 2500)) - removed.gases[/datum/gas/plasma][MOLES] = max((device_energy) / PLASMA_RELEASE_MODIFIER, 0) - removed.gases[/datum/gas/oxygen][MOLES] = max(((device_energy + TCMB) - T0C) / OXYGEN_RELEASE_MODIFIER, 0) - removed.garbage_collect() - env.merge(removed) + absorbed_gasmix.assert_gases(/datum/gas/plasma, /datum/gas/oxygen) + absorbed_gasmix.temperature = ((device_energy) / THERMAL_RELEASE_MODIFIER) + absorbed_gasmix.temperature = max(TCMB, min(absorbed_gasmix.temperature, 2500)) + absorbed_gasmix.gases[/datum/gas/plasma][MOLES] = max((device_energy) / PLASMA_RELEASE_MODIFIER, 0) + absorbed_gasmix.gases[/datum/gas/oxygen][MOLES] = max(((device_energy + TCMB) - T0C) / OXYGEN_RELEASE_MODIFIER, 0) + absorbed_gasmix.garbage_collect() + env.merge(absorbed_gasmix) air_update_turf(FALSE, FALSE) else if(takes_damage) //causing damage - deal_damage(removed) + deal_damage(absorbed_gasmix) //registers the current enviromental gases in the various lists and vars - setup_lists(removed) + setup_lists(absorbed_gasmix) //some gases can have special interactions - special_gases_interactions(env, removed) + special_gases_interactions(env, absorbed_gasmix) //main power calculations proc - power_calculations(env, removed) + power_calculations(env, absorbed_gasmix) //irradiate at this point emit_radiation() //handles temperature increase and gases made by the crystal - temperature_gas_production(env, removed) - - var/cascading = check_cascade_requirements() - if(cascading) - if(!cascade_initiated) - addtimer(CALLBACK(src, .proc/announce_incoming_cascade), 2 MINUTES, TIMER_UNIQUE | TIMER_OVERRIDE) - log_game("[src] has begun a cascade.") - message_admins("[src] has begun a cascade, reasons: [cascading]. [ADMIN_JMP(src)]") - investigate_log("has begun a cascade, reasons: [cascading].", INVESTIGATE_ENGINE) - cascade_initiated = TRUE - if(!warp) - warp = new(src) - vis_contents += warp - animate(warp, time = 1, transform = matrix().Scale(0.5,0.5)) - animate(time = 9, transform = matrix()) - else - if(warp) - vis_contents -= warp - QDEL_NULL(warp) - if(cascade_initiated) - log_game("[src] has stopped its cascade.") - message_admins("[src] has stopped its cascade. [ADMIN_JMP(src)]") - investigate_log("has stopped its cascade.", INVESTIGATE_ENGINE) - cascade_initiated = FALSE + temperature_gas_production(env, absorbed_gasmix) //handles hallucinations and the presence of a psychiatrist psychological_examination() @@ -105,16 +81,19 @@ //After this point power is lowered //This wraps around to the begining of the function //Handle high power zaps/anomaly generation - handle_high_power(removed) + handle_high_power(absorbed_gasmix) if(prob(15)) supermatter_pull(loc, min(power/850, 3))//850, 1700, 2550 - //Tells the engi team to get their butt in gear - handle_emergency_alerts() + if(damage == 0) // Clear any in game forced delams if on full health. + set_delam(SM_DELAM_PRIO_IN_GAME, SM_DELAM_STRATEGY_PURGE) + else + set_delam() // This one cant clear any forced delams. + delamination_strategy.delam_progress(src) - if(damage == 0 && has_destabilizing_crystal) - has_destabilizing_crystal = FALSE + if(damage > explosion_point && !final_countdown) + count_down() return TRUE @@ -148,7 +127,7 @@ has_holes = TRUE break - var/cascade_multiplier = cascade_initiated ? 0.25 : 1 + var/delam_damage_multipler = delamination_strategy.damage_multiplier(src) //Due to DAMAGE_INCREASE_MULTIPLIER, we only deal one 4th of the damage the statements otherwise would cause //((((some value between 0.5 and 1 * temp - ((273.15 + 40) * some values between 1 and 10)) * some number between 0.25 and knock your socks off / 150) * 0.25 @@ -156,9 +135,9 @@ //Mols start to have a positive effect on damage after 350 damage = max(damage + (max(clamp(removed.total_moles() / 200, 0.5, 1) * removed.temperature - ((T0C + HEAT_PENALTY_THRESHOLD)*dynamic_heat_resistance), 0) * mole_heat_penalty / 150 ) * DAMAGE_INCREASE_MULTIPLIER, 0) //Power only starts affecting damage when it is above 5000 (1250 when a cascade is occurring) - damage = max(damage + (max(power - (POWER_PENALTY_THRESHOLD * cascade_multiplier), 0)/500) * DAMAGE_INCREASE_MULTIPLIER, 0) + damage = max(damage + (max(power - (POWER_PENALTY_THRESHOLD * delam_damage_multipler), 0)/500) * DAMAGE_INCREASE_MULTIPLIER, 0) //Molar count only starts affecting damage when it is above 1800 (450 when a cascade is occurring) - damage = max(damage + (max(combined_gas - (MOLE_PENALTY_THRESHOLD * cascade_multiplier), 0)/80) * DAMAGE_INCREASE_MULTIPLIER, 0) + damage = max(damage + (max(combined_gas - (MOLE_PENALTY_THRESHOLD * delam_damage_multipler), 0)/80) * DAMAGE_INCREASE_MULTIPLIER, 0) //There might be a way to integrate healing and hurting via heat //healing damage @@ -360,7 +339,7 @@ psyCoeff = clamp(psyCoeff + psy_coeff_diff, 0, 1) /obj/machinery/power/supermatter_crystal/proc/handle_high_power(datum/gas_mixture/removed) - if(power <= POWER_PENALTY_THRESHOLD && damage <= damage_penalty_point) //If the power is above 5000 or if the damage is above 550 + if(power <= POWER_PENALTY_THRESHOLD && damage <= danger_point) //If the power is above 5000 or if the damage is above 550 return var/range = 4 zap_cutoff = 1500 @@ -390,7 +369,7 @@ flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE) zap_count = 4 //Now we deal with damage shit - if (damage > damage_penalty_point && prob(20)) + if (damage > danger_point && prob(20)) zap_count += 1 if(zap_count >= 1) @@ -407,59 +386,38 @@ if((power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2)) || (prob(0.3) && power > POWER_PENALTY_THRESHOLD)) supermatter_anomaly_gen(src, PYRO_ANOMALY, rand(5, 10)) -/obj/machinery/power/supermatter_crystal/proc/handle_emergency_alerts() - if(damage <= warning_point) // while the core is still damaged and it's still worth noting its status - return - if(damage_archived < warning_point) //If damage_archive is under the warning point, this is the very first cycle that we've reached said point. - SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_START_ALARM) - if((REALTIMEOFDAY - lastwarning) / 10 >= WARNING_DELAY) - alarm() +/** + * Sets the delam of our sm. + * + * Arguments: + * * priority: Truthy values means a forced delam. If current forced_delam is higher than priority we dont run. + * Set to a number higher than [SM_DELAM_PRIO_IN_GAME] to fully force an admin delam. + * * delam_path: Typepath of a [/datum/sm_delam]. [SM_DELAM_STRATEGY_PURGE] means reset and put prio back to zero. + * + * Returns: Not used for anything, just returns true on succesful set, manual and automatic. Helps admins check stuffs. + */ +/obj/machinery/power/supermatter_crystal/proc/set_delam(priority = SM_DELAM_PRIO_NONE, manual_delam_path = SM_DELAM_STRATEGY_PURGE) + if(priority < delam_priority) + return FALSE + var/datum/sm_delam/new_delam = null - //Oh shit it's bad, time to freak out - if(damage > emergency_point) - radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity_percent()]%", common_channel) - SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM) - lastwarning = REALTIMEOFDAY - if(!has_reached_emergency) - investigate_log("has reached the emergency point for the first time.", INVESTIGATE_ENGINE) - message_admins("[src] has reached the emergency point [ADMIN_JMP(src)].") - has_reached_emergency = TRUE - else if(damage >= damage_archived) // The damage is still going up - radio.talk_into(src, "[warning_alert] Integrity: [get_integrity_percent()]%", engineering_channel) - SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM) - lastwarning = REALTIMEOFDAY - (WARNING_DELAY * 5) + if(manual_delam_path == SM_DELAM_STRATEGY_PURGE) + for (var/delam_path in GLOB.sm_delam_list) + var/datum/sm_delam/delam = GLOB.sm_delam_list[delam_path] + if(!delam.can_select(src)) + continue + if(delam == delamination_strategy) + return FALSE + new_delam = delam + break + delam_priority = SM_DELAM_PRIO_NONE + else + new_delam = GLOB.sm_delam_list[manual_delam_path] + delam_priority = priority - else // Phew, we're safe - radio.talk_into(src, "[safe_alert] Integrity: [get_integrity_percent()]%", engineering_channel) - lastwarning = REALTIMEOFDAY - - if(power > POWER_PENALTY_THRESHOLD) - radio.talk_into(src, "Warning: Hyperstructure has reached dangerous power level.", engineering_channel) - if(powerloss_inhibitor < 0.5) - radio.talk_into(src, "DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.", engineering_channel) - - if(combined_gas > MOLE_PENALTY_THRESHOLD) - radio.talk_into(src, "Warning: Critical coolant mass reached.", engineering_channel) - - if(check_cascade_requirements()) - var/channel_to_talk_to = damage > emergency_point ? common_channel : engineering_channel - radio.talk_into(src, "DANGER: HYPERSTRUCTURE OSCILLATION FREQUENCY OUT OF BOUNDS.", channel_to_talk_to) - for(var/mob/victim as anything in GLOB.player_list) - var/list/messages = list( - "Space seems to be shifting around you...", - "You hear a high-pitched ringing sound.", - "You feel tingling going down your back.", - "Something feels very off.", - "A drowning sense of dread washes over you." - ) - to_chat(victim, span_danger(pick(messages))) - - //Boom (Mind blown) - if(damage > explosion_point) - countdown() - -/obj/machinery/power/supermatter_crystal/proc/announce_incoming_cascade() - if(check_cascade_requirements()) - priority_announce("Attention: Long range anomaly scans indicate abnormal quantities of harmonic flux originating from \ - a subject within [station_name()], a resonance collapse may occur.", - "Nanotrasen Star Observation Association") + if(!new_delam) + return FALSE + delamination_strategy?.on_deselect(src) + delamination_strategy = new_delam + delamination_strategy.on_select(src) + return TRUE diff --git a/tgstation.dme b/tgstation.dme index 04d00f8679f..af09ceaefa9 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3967,11 +3967,14 @@ #include "code\modules\power\singularity\narsie.dm" #include "code\modules\power\singularity\singularity.dm" #include "code\modules\power\supermatter\supermatter.dm" -#include "code\modules\power\supermatter\supermatter_cascade_components.dm" -#include "code\modules\power\supermatter\supermatter_delamination.dm" #include "code\modules\power\supermatter\supermatter_hit_procs.dm" #include "code\modules\power\supermatter\supermatter_process.dm" #include "code\modules\power\supermatter\supermatter_radiation.dm" +#include "code\modules\power\supermatter\supermatter_delamination\_sm_delam.dm" +#include "code\modules\power\supermatter\supermatter_delamination\cascade_delam.dm" +#include "code\modules\power\supermatter\supermatter_delamination\cascade_delam_objects.dm" +#include "code\modules\power\supermatter\supermatter_delamination\common_delams.dm" +#include "code\modules\power\supermatter\supermatter_delamination\delamination_effects.dm" #include "code\modules\power\tesla\coil.dm" #include "code\modules\power\tesla\energy_ball.dm" #include "code\modules\power\turbine\turbine.dm"