diff --git a/aurorastation.dme b/aurorastation.dme
index ad06d52c1e1..d5166c42830 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -72,6 +72,7 @@
#include "code\__DEFINES\global.dm"
#include "code\__DEFINES\grab.dm"
#include "code\__DEFINES\gradient.dm"
+#include "code\__DEFINES\gravity.dm"
#include "code\__DEFINES\guns.dm"
#include "code\__DEFINES\hallucinations.dm"
#include "code\__DEFINES\hardsuits.dm"
@@ -1724,7 +1725,6 @@
#include "code\modules\admin\secrets\fun_secrets\triple_ai_mode.dm"
#include "code\modules\admin\secrets\fun_secrets\turn_humans_into_corgies.dm"
#include "code\modules\admin\secrets\fun_secrets\turn_humans_into_monkeys.dm"
-#include "code\modules\admin\secrets\random_events\gravity.dm"
#include "code\modules\admin\secrets\random_events\trigger_cordical_borer_infestation.dm"
#include "code\modules\admin\verbs\access_control.dm"
#include "code\modules\admin\verbs\adminhelp.dm"
@@ -2361,6 +2361,7 @@
#include "code\modules\events\event_container.dm"
#include "code\modules\events\false_alarm.dm"
#include "code\modules\events\gravity.dm"
+#include "code\modules\events\gravity_anomaly.dm"
#include "code\modules\events\grid_check.dm"
#include "code\modules\events\infestation.dm"
#include "code\modules\events\ion_storm.dm"
@@ -3929,6 +3930,7 @@
#include "code\modules\tgui\status_composers.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\tgui_window.dm"
+#include "code\modules\tgui\modules\adpi_panel.dm"
#include "code\modules\tgui\modules\armor_values.dm"
#include "code\modules\tgui\modules\faction_select.dm"
#include "code\modules\tgui\modules\flavor_text.dm"
diff --git a/code/__DEFINES/global.dm b/code/__DEFINES/global.dm
index 2010203e3ef..27ca7f43381 100644
--- a/code/__DEFINES/global.dm
+++ b/code/__DEFINES/global.dm
@@ -97,8 +97,6 @@ GLOBAL_DATUM(debugobj, /datum/debug)
GLOBAL_DATUM_INIT(mods, /datum/moduletypes, new())
-GLOBAL_VAR_INIT(gravity_is_on, 1)
-
GLOBAL_LIST_EMPTY(awaydestinations) // Away missions. A list of landmarks that the warpgate can take you to.
// For FTP requests. (i.e. downloading runtime logs.)
diff --git a/code/__DEFINES/gravity.dm b/code/__DEFINES/gravity.dm
new file mode 100644
index 00000000000..0114ccc9903
--- /dev/null
+++ b/code/__DEFINES/gravity.dm
@@ -0,0 +1,13 @@
+// Gravity behavior.
+// Discomfort and disorientation begin to occur when you exceed a 1/3 deviation from what you're used to.
+// Mechanically, this is set to a 35% deviation, so that staying within one adjacent 'step' of these gravity defines keeps you feeling okay.
+
+/// Zero gravity.
+#define ZERO_GRAVITY 0.00
+/// Low gravity. Hop from z-level to z-level, no fall damage.
+#define LOW_GRAVITY 0.33
+/// Reduced gravity. Somewhat safer falls, easier movement, etc.
+#define REDUCED_GRAVITY 0.67
+/// Earth standard bay-bee.
+#define STANDARD_GRAVITY 1.00
+
diff --git a/code/__DEFINES/space_sectors.dm b/code/__DEFINES/space_sectors.dm
index 561984ed9e1..67e3f2e968e 100644
--- a/code/__DEFINES/space_sectors.dm
+++ b/code/__DEFINES/space_sectors.dm
@@ -31,9 +31,9 @@
#define ALL_COALITION_SECTORS list(SECTOR_COALITION, SECTOR_XANU, SECTOR_WEEPING_STARS, SECTOR_ARUSHA, SECTOR_LIBERTYS_CRADLE, SECTOR_BURZSIA, SECTOR_HANEUNIM, SECTOR_LIGHTS_EDGE, SECTOR_AL_MAQDISI)
//Light's edge, which should have unique properties all around
-#define SECTOR_LIGHTS_EDGE "Light's Edge" //For the area of Light's Edge that is somewhat inhabited. NOTE- this also lives in ALL_COALITION_SECTORS, per lore.
-#define SECTOR_LEMURIAN_SEA "Lemurian Sea" //For the actual black void area
-#define SECTOR_LEMURIAN_SEA_FAR "Lemurian Sea (Uncharted)" //For the actual black void area
+#define SECTOR_LIGHTS_EDGE "Light's Edge" // For the area of Light's Edge that is somewhat inhabited. NOTE- this also lives in ALL_COALITION_SECTORS, per lore.
+#define SECTOR_LEMURIAN_SEA "Lemurian Sea (Outer)" // For the fringes of the Lemurian Sea.
+#define SECTOR_LEMURIAN_SEA_FAR "Lemurian Sea (Inner)" // For the actual Lemurian Sea. Adminbus town.
#define ALL_VOID_SECTORS list(SECTOR_LIGHTS_EDGE, SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR)
//Crescent Expanse & Beyond
diff --git a/code/__HELPERS/overmap.dm b/code/__HELPERS/overmap.dm
index a6493e44953..0aa5bf6e080 100644
--- a/code/__HELPERS/overmap.dm
+++ b/code/__HELPERS/overmap.dm
@@ -1 +1,4 @@
var/global/list/overmap_sectors = list()
+
+/proc/is_lemurian_sea_sector()
+ return SSatlas.current_sector?.name in list(SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR)
diff --git a/code/controllers/subsystems/event.dm b/code/controllers/subsystems/event.dm
index b88b30e1cce..28c74f40f81 100644
--- a/code/controllers/subsystems/event.dm
+++ b/code/controllers/subsystems/event.dm
@@ -35,7 +35,11 @@ SUBSYSTEM_DEF(events)
initialized = TRUE
if(SSatlas.current_map.use_overmap)
- overmap_event_handler.create_events(SSatlas.current_map.overmap_z, SSatlas.current_map.overmap_size, SSatlas.current_map.overmap_event_areas)
+ var/overmap_event_areas = SSatlas.current_map.overmap_event_areas
+ if(SSatlas.current_sector)
+ overmap_event_areas = round(overmap_event_areas * SSatlas.current_sector.overmap_hazards_multiplier)
+ LOG_DEBUG("Creating [overmap_event_areas] events")
+ overmap_event_handler.create_events(SSatlas.current_map.overmap_z, SSatlas.current_map.overmap_size, overmap_event_areas)
return SS_INIT_SUCCESS
diff --git a/code/controllers/subsystems/hallucinations.dm b/code/controllers/subsystems/hallucinations.dm
index 6ec00ff74df..2ed54e2ec99 100644
--- a/code/controllers/subsystems/hallucinations.dm
+++ b/code/controllers/subsystems/hallucinations.dm
@@ -1,10 +1,65 @@
SUBSYSTEM_DEF(hallucinations)
name = "Hallucinations"
- flags = SS_NO_FIRE
+ wait = 1 MINUTE
+ runlevels = RUNLEVELS_PLAYING
var/list/hallucinated_phrases = list()
var/list/hallucinated_actions = list()
var/list/hallucinated_thoughts = list()
+
+ // These lists are only initialized/used when in the Lemurian Sea.
+ var/adpi_loaded = FALSE
+ var/list/adpi_general = list()
+ var/list/adpi_departments = list()
+ var/list/adpi_department_anti = list()
+ var/list/adpi_jobs = list()
+ var/list/adpi_next_message = list()
+ var/tmp/list/current_adpi_targets = list()
+ var/static/list/adpi_sounds = list(
+ 'sound/ambience/ghostly/ghostly1.ogg',
+ 'sound/ambience/ghostly/ghostly2.ogg'
+ )
+ var/static/list/adpi_department_files = list(
+ DEPARTMENT_COMMAND = "adpi_dept_command.txt",
+ DEPARTMENT_ENGINEERING = "adpi_dept_engineering.txt",
+ DEPARTMENT_MEDICAL = "adpi_dept_medical.txt",
+ DEPARTMENT_CARGO = "adpi_dept_operations.txt",
+ DEPARTMENT_SCIENCE = "adpi_dept_science.txt",
+ DEPARTMENT_SECURITY = "adpi_dept_security.txt",
+ DEPARTMENT_SERVICE = "adpi_dept_service.txt"
+ )
+ var/static/list/adpi_department_anti_files = list(
+ DEPARTMENT_COMMAND = "adpi_dept_command_anti.txt",
+ DEPARTMENT_ENGINEERING = "adpi_dept_engineering_anti.txt",
+ DEPARTMENT_MEDICAL = "adpi_dept_medical_anti.txt",
+ DEPARTMENT_CARGO = "adpi_dept_operations_anti.txt",
+ DEPARTMENT_SCIENCE = "adpi_dept_science_anti.txt",
+ DEPARTMENT_SECURITY = "adpi_dept_security_anti.txt",
+ DEPARTMENT_SERVICE = "adpi_dept_service_anti.txt"
+ )
+ var/static/list/adpi_job_files = list(
+ "Atmospheric Technician" = "adpi_job_atmostech.txt",
+ "Environmental Systems Engineer" = "adpi_job_atmostech.txt",
+ "Propulsion Engineer" = "adpi_job_atmostech.txt",
+ "Damage Control Technician" = "adpi_job_atmostech.txt",
+ "Chef" = "adpi_job_cook.txt",
+ "Cook" = "adpi_job_cook.txt",
+ "Hangar Technician" = "adpi_job_hangartech.txt",
+ "Janitor" = "adpi_job_janitor.txt",
+ "Machinist" = "adpi_job_machinist.txt",
+ "Shaft Miner" = "adpi_job_mining.txt",
+ "Paramedic" = "adpi_job_paramedic.txt",
+ "Pharmacist" = "adpi_job_pharmacist.txt",
+ "Physician" = "adpi_job_physician.txt",
+ "Psychiatrist" = "adpi_job_psychiatrist.txt",
+ "Psychologist" = "adpi_job_psychiatrist.txt",
+ "Ship Engineer" = "adpi_job_shipengineer.txt",
+ "Reactor Operator" = "adpi_job_shipengineer.txt",
+ "Maintenance Technician" = "adpi_job_shipengineer.txt",
+ "Systems Engineer" = "adpi_job_shipengineer.txt",
+ "Surgeon" = "adpi_job_surgeon.txt"
+ )
+
var/static/list/hal_emote = list("mutters quietly.", "stares.", "grunts.", "looks around.", "twitches.", "shivers.", "swats at the air.", "wobbles.", "gasps!", "blinks rapidly.", "murmurs.",
"dry heaves!", "twitches violently.", "giggles.", "drools.", "scratches all over.", "grinds their teeth.", "whispers something quietly.")
var/static/list/message_sender = list("Mom", "Dad", "Captain", "Captain(as Captain)", "help", "Home", "MaxBet Online Casino", "IDrist Corp", "Dr. Maxman",
@@ -15,12 +70,54 @@ SUBSYSTEM_DEF(hallucinations)
/datum/controller/subsystem/hallucinations/Initialize()
for(var/T in subtypesof(/datum/hallucination))
all_hallucinations += T
- hallucinated_phrases = file2list("code/modules/hallucinations/text_lists/hallucinated_phrases.txt")
- hallucinated_actions = file2list("code/modules/hallucinations/text_lists/hallucinated_actions.txt") //important note when adding to this file: "you" will always be replaced by the hallucinator's name
- hallucinated_thoughts = file2list("code/modules/hallucinations/text_lists/hallucinated_thoughts.txt")
+ // Generic hallucinations
+ hallucinated_phrases = file2list("config/hallucinations/hallucinated_phrases.txt")
+ hallucinated_actions = file2list("config/hallucinations/hallucinated_actions.txt") //important note when adding to this file: "you" will always be replaced by the hallucinator's name
+ hallucinated_thoughts = file2list("config/hallucinations/hallucinated_thoughts.txt")
+
+ if(!length(hallucinated_phrases))
+ hallucinated_phrases += "We are here."
+ if(!length(hallucinated_actions))
+ hallucinated_actions += "twitches."
+ if(!length(hallucinated_thoughts))
+ hallucinated_thoughts += "Something's buzzing in your ear."
+
+ if(is_lemurian_sea())
+ load_adpi_lists()
return SS_INIT_SUCCESS
+/datum/controller/subsystem/hallucinations/Recover()
+ adpi_loaded = SShallucinations.adpi_loaded
+ adpi_general = SShallucinations.adpi_general
+ adpi_departments = SShallucinations.adpi_departments
+ adpi_department_anti = SShallucinations.adpi_department_anti
+ adpi_jobs = SShallucinations.adpi_jobs
+ adpi_next_message = SShallucinations.adpi_next_message
+
+/datum/controller/subsystem/hallucinations/fire(resumed = FALSE)
+ if(!is_lemurian_sea())
+ adpi_next_message.Cut()
+ return
+
+ if(!adpi_loaded)
+ ensure_adpi_lists_loaded()
+
+ if(!resumed)
+ prune_adpi_schedules()
+ current_adpi_targets = GLOB.living_mob_list.Copy()
+
+ while(length(current_adpi_targets))
+ var/mob/living/target = current_adpi_targets[current_adpi_targets.len]
+ current_adpi_targets.len--
+ if(!istype(target) || QDELETED(target))
+ continue
+
+ process_adpi_target(target)
+
+ if(MC_TICK_CHECK)
+ return
+
/datum/controller/subsystem/hallucinations/proc/get_hallucination(var/mob/living/carbon/C)
var/list/candidates = list()
for(var/T in all_hallucinations)
@@ -30,3 +127,293 @@ SUBSYSTEM_DEF(hallucinations)
if(candidates.len)
var/datum/hallucination/D = pick(candidates)
return D
+
+
+/////// HANDLES ALL ADPI MESSAGING ///////
+/datum/controller/subsystem/hallucinations/proc/is_lemurian_sea()
+ return is_lemurian_sea_sector()
+
+/datum/controller/subsystem/hallucinations/proc/read_adpi_file(var/file_name)
+ var/list/loaded_file = file2list("config/hallucinations/lemurian_sea/[file_name]")
+ if(!loaded_file)
+ loaded_file = list()
+ return loaded_file
+
+/datum/controller/subsystem/hallucinations/proc/load_adpi_lists()
+ adpi_loaded = TRUE
+ adpi_general = read_adpi_file("adpi_general.txt")
+ adpi_departments = list()
+ adpi_department_anti = list()
+ adpi_jobs = list()
+
+ if(!length(adpi_general))
+ adpi_general += "Something vast and invisible is waiting for you to notice it."
+
+ for(var/department in adpi_department_files)
+ adpi_departments[department] = read_adpi_file(adpi_department_files[department])
+ if(!length(adpi_departments[department]))
+ adpi_departments[department] += "We're going to be the ones who let the ship down."
+
+ for(var/department in adpi_department_anti_files)
+ adpi_department_anti[department] = read_adpi_file(adpi_department_anti_files[department])
+ if(!length(adpi_department_anti[department]))
+ adpi_department_anti[department] += "This is all Central Command's fault."
+
+ for(var/job_title in adpi_job_files)
+ adpi_jobs[job_title] = read_adpi_file(adpi_job_files[job_title])
+ if(!length(adpi_jobs[job_title]))
+ adpi_jobs[job_title] += "We're all going to die out here."
+
+/datum/controller/subsystem/hallucinations/proc/ensure_adpi_lists_loaded()
+ if(!adpi_loaded)
+ load_adpi_lists()
+
+/datum/controller/subsystem/hallucinations/proc/prune_adpi_schedules()
+ for(var/mob/living/target as anything in adpi_next_message.Copy())
+ if(QDELETED(target) || !(target in GLOB.living_mob_list) || !target.client || target.stat == DEAD || is_adpi_excluded(target))
+ adpi_next_message -= target
+
+/datum/controller/subsystem/hallucinations/proc/is_adpi_excluded(var/mob/living/target)
+ if(!target)
+ return TRUE
+ if(isvaurca(target) || isipc(target) || issilicon(target))
+ return TRUE
+ return FALSE
+
+/datum/controller/subsystem/hallucinations/proc/is_adpi_blocked(var/mob/living/target)
+ return length(target?.GetComponents(/datum/component/timed_life/psiblock_drugs))
+
+/datum/controller/subsystem/hallucinations/proc/can_receive_adpi(var/mob/living/target)
+ if(!is_lemurian_sea())
+ return FALSE
+ if(!target || !target.client || !target.mind || target.stat)
+ return FALSE
+ if(is_adpi_excluded(target))
+ return FALSE
+ if(is_adpi_blocked(target))
+ return FALSE
+ if(!is_station_level(target.z))
+ return FALSE
+ if(target.isMonkey())
+ return FALSE
+ return TRUE
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_job(var/mob/living/carbon/human/H)
+ if(!H)
+ return
+
+ var/assigned_role
+ if(H.mind?.assigned_role)
+ assigned_role = H.mind.assigned_role
+ else
+ assigned_role = H.job
+
+ return SSjobs.GetJob(assigned_role)
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_departments(var/mob/living/carbon/human/H)
+ var/list/departments = list()
+ var/datum/job/job = get_adpi_job(H)
+ if(job)
+ for(var/department in job.departments)
+ departments |= department
+ return departments
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_job_messages(var/mob/living/carbon/human/H)
+ var/list/job_titles = list()
+ var/datum/job/job = get_adpi_job(H)
+
+ if(H?.mind?.role_alt_title)
+ job_titles |= H.mind.role_alt_title
+ if(job?.title)
+ job_titles |= job.title
+ if(H?.job)
+ job_titles |= H.job
+
+ for(var/job_title in job_titles)
+ var/list/job_messages = adpi_jobs[job_title]
+ if(length(job_messages))
+ return job_messages
+
+ return list()
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_message_pools(var/mob/living/carbon/human/H, var/include_anti = TRUE, var/check_receiver = TRUE)
+ var/list/message_pools = list()
+ if(check_receiver && !can_receive_adpi(H))
+ return message_pools
+
+ if(length(adpi_general))
+ message_pools["general"] = adpi_general
+
+ var/list/departments = get_adpi_departments(H)
+ for(var/department in departments)
+ var/list/department_messages = adpi_departments[department]
+ if(length(department_messages))
+ message_pools["department_[department]"] = department_messages
+
+ var/list/job_messages = get_adpi_job_messages(H)
+ if(length(job_messages))
+ message_pools["job"] = job_messages
+
+ if(include_anti)
+ for(var/department in adpi_department_anti)
+ if(department in departments)
+ continue
+ var/list/anti_messages = adpi_department_anti[department]
+ if(length(anti_messages))
+ message_pools["anti_[department]"] = anti_messages
+
+ return message_pools
+
+/datum/controller/subsystem/hallucinations/proc/has_adpi_messages(var/mob/living/target, var/check_receiver = TRUE)
+ if(is_adpi_excluded(target))
+ return FALSE
+ if(is_adpi_blocked(target))
+ return FALSE
+
+ if(check_receiver && !can_receive_adpi(target))
+ return FALSE
+
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ return length(get_adpi_message_pools(H, TRUE, FALSE)) > 0
+
+ return length(adpi_general) > 0
+
+/datum/controller/subsystem/hallucinations/proc/process_adpi_target(var/mob/living/target)
+ if(!can_receive_adpi(target))
+ adpi_next_message -= target
+ return
+
+ if(!has_adpi_messages(target, FALSE))
+ return
+
+ if(!adpi_next_message[target])
+ schedule_next_adpi_message(target, TRUE)
+ return
+
+ if(world.time < adpi_next_message[target])
+ return
+
+ if(send_adpi_message(target))
+ schedule_next_adpi_message(target)
+ else
+ schedule_next_adpi_message(target, TRUE)
+
+/datum/controller/subsystem/hallucinations/proc/schedule_next_adpi_message(var/mob/living/target, var/initial = FALSE)
+ adpi_next_message[target] = world.time + get_adpi_delay(target, initial)
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_delay(var/mob/living/target, var/initial = FALSE)
+ var/base_delay = initial ? rand(8 MINUTES, 18 MINUTES) : rand(25 MINUTES, 40 MINUTES)
+ var/sensitivity = target.check_psi_sensitivity()
+ var/multiplier = 1
+
+ if(isskrell(target))
+ multiplier *= 0.75
+
+ if(sensitivity >= PSI_RANK_APEX)
+ multiplier *= 0.55
+ else if(sensitivity >= PSI_RANK_HARMONIOUS)
+ multiplier *= 0.65
+ else if(sensitivity >= PSI_RANK_SENSITIVE)
+ multiplier *= 0.75
+ else if(sensitivity > 0)
+ multiplier *= 0.85
+ else if(sensitivity < 0)
+ multiplier *= 1.5
+
+ if(target.is_psi_blocked(null, TRUE))
+ multiplier *= 1.75
+
+ return round(base_delay * clamp(multiplier, 0.4, 2.5))
+
+/datum/controller/subsystem/hallucinations/proc/get_adpi_pool_weight(var/pool_name)
+ if(pool_name == "general")
+ return 40
+ if(pool_name == "job")
+ return 35
+ if(findtext(pool_name, "department_") == 1)
+ return 30
+ if(findtext(pool_name, "anti_") == 1)
+ return 2
+ return 1
+
+/datum/controller/subsystem/hallucinations/proc/pick_adpi_message(var/mob/living/target, var/check_receiver = TRUE)
+ if(!has_adpi_messages(target, check_receiver))
+ return
+
+ if(!ishuman(target))
+ return pick(adpi_general)
+
+ var/mob/living/carbon/human/H = target
+ var/list/message_pools = get_adpi_message_pools(H, TRUE, FALSE)
+ if(!length(message_pools))
+ return
+
+ var/list/pool_weights = list()
+ for(var/pool_name in message_pools)
+ var/list/message_pool = message_pools[pool_name]
+ if(length(message_pool))
+ pool_weights[pool_name] = get_adpi_pool_weight(pool_name)
+ if(!length(pool_weights))
+ return
+
+ var/selected_pool_name = pickweight(pool_weights)
+ var/list/selected_pool = message_pools[selected_pool_name]
+ return pick(selected_pool)
+
+/datum/controller/subsystem/hallucinations/proc/send_adpi_message(var/mob/living/target, var/custom_message = null, var/check_receiver = TRUE)
+ if(is_adpi_blocked(target))
+ return FALSE
+ if(check_receiver && !can_receive_adpi(target))
+ return FALSE
+
+ var/message = custom_message || pick_adpi_message(target, check_receiver)
+ if(!message)
+ return FALSE
+
+ deliver_adpi_message(target, message)
+ return TRUE
+
+/datum/controller/subsystem/hallucinations/proc/send_admin_adpi_message(var/mob/living/target, var/custom_message = null)
+ if(!target || !target.client || !target.mind || target.stat == DEAD)
+ return FALSE
+ if(is_adpi_excluded(target))
+ return FALSE
+ if(is_adpi_blocked(target))
+ return FALSE
+
+ ensure_adpi_lists_loaded()
+
+ var/message = custom_message
+ if(!message)
+ message = pick_adpi_message(target, FALSE)
+
+ if(!message)
+ return FALSE
+
+ deliver_adpi_message(target, message)
+
+ schedule_next_adpi_message(target)
+
+ return TRUE
+
+/datum/controller/subsystem/hallucinations/proc/deliver_adpi_message(var/mob/living/target, var/message)
+ /// % chance to pick one of the general thematic ADPI messages. Enjoy, code peekers; this is all you get!
+ if(prob(15))
+ message = pick("The water is dripping dripping dripping all around you.","Water flowing over stone, but there is no stone.","The waterfall roars o'er the cliff's edge.","Water, water, water. You are drowning.","Water, water, water. You will be awake for it.","Drums, drums, drums, unrelenting.","The drumbeat draws e'er closer.","Tap, ta-tap, ta-tap, tap, ta-tap, ta-tap.","Tap, tap tap, ta-tap, tap-tap, ta-tap.","Ta-ta-tap, tap, ta-tap, tap, tap ta-tap.")
+ target.play_screen_text("[message]", /atom/movable/screen/text/screen_text/adpi_message, COLOR_PURPLE)
+ to_chat(target, SPAN_CULT(FONT_LARGE("[message]")))
+
+ if(prob(33))
+ sound_to(target, pick(adpi_sounds))
+
+ if(isskrell(target))
+ var/mob/living/carbon/human/H = target
+ apply_skrell_adpi_pain(H)
+
+/datum/controller/subsystem/hallucinations/proc/apply_skrell_adpi_pain(var/mob/living/carbon/human/H)
+ H.adjustHalLoss(rand(5, 12))
+ if(prob(35))
+ to_chat(H, SPAN_WARNING("A sharp ache lances through your head as the thought passes."))
+ if(prob(15))
+ H.emote("shiver")
diff --git a/code/controllers/subsystems/machinery.dm b/code/controllers/subsystems/machinery.dm
index d3635d321a3..1d239f8313e 100644
--- a/code/controllers/subsystems/machinery.dm
+++ b/code/controllers/subsystems/machinery.dm
@@ -46,6 +46,7 @@ SUBSYSTEM_DEF(machinery)
var/list/all_cameras = list()
var/list/obj/structure/machinery/hologram/holopad/all_holopads = list()
var/list/obj/structure/machinery/power/apc/all_apcs = list()
+ var/list/obj/structure/machinery/light/all_lights = list()
var/list/all_status_displays = list() // Note: This contains both ai_status_display and status_display.
var/list/gravity_generators = list()
var/list/obj/structure/machinery/telecomms/all_telecomms = list()
@@ -70,10 +71,15 @@ SUBSYSTEM_DEF(machinery)
// Cooking stuff. Not substantial enough to get its own SS, so it's shoved in here.
var/list/recipe_datums = list()
+ // LEMURIAN SEA ARC SNOWFLAKE: Break one random ship light every few minutes. REMOVE AFTER ARC
+ var/next_arc_light_break = 0
+
/datum/controller/subsystem/machinery/Recover()
all_cameras = SSmachinery.all_cameras
all_holopads = SSmachinery.all_holopads
all_apcs = SSmachinery.all_apcs
+ all_lights = SSmachinery.all_lights
+ next_arc_light_break = SSmachinery.next_arc_light_break
recipe_datums = SSmachinery.recipe_datums
breaker_boxes = SSmachinery.breaker_boxes
all_sensors = SSmachinery.all_sensors
@@ -85,6 +91,7 @@ SUBSYSTEM_DEF(machinery)
makepowernets()
build_rcon_lists()
setup_atmos_machinery(machinery)
+ next_arc_light_break = world.time + 3 MINUTES // LEMURIAN SEA, REMOVE AFTER ARC
fire(FALSE, TRUE) // Tick machinery once to pare down the list so we don't hammer the server on round-start.
return SS_INIT_SUCCESS
@@ -115,6 +122,10 @@ SUBSYSTEM_DEF(machinery)
return
current_step = SSMACHINERY_PIPENETS
resumed = FALSE
+ // LEMURIAN SEA, REMOVE AFTER ARC
+ if(!resumed && world.time >= next_arc_light_break)
+ break_random_arc_light()
+ next_arc_light_break = world.time + 3 MINUTES
/datum/controller/subsystem/machinery/proc/makepowernets()
for(var/datum/powernet/powernet as anything in powernets)
@@ -225,6 +236,27 @@ SUBSYSTEM_DEF(machinery)
queue.Cut(i)
return
+/// LEMURIAN SEA, REMOVE AFTER ARC
+/datum/controller/subsystem/machinery/proc/break_random_arc_light()
+ var/list/valid_lights = list()
+ for(var/obj/structure/machinery/light/light as anything in all_lights)
+ if(QDELETED(light))
+ continue
+ if(!is_station_level(light.z))
+ continue
+ if(light.status != LIGHT_OK)
+ continue
+ valid_lights += light
+
+ if(!length(valid_lights))
+ return
+
+ var/number_lights_broken = rand(1,4)
+ var/obj/structure/machinery/light/chosen_light
+ for(var/n = 0 to number_lights_broken)
+ chosen_light = pick(valid_lights)
+ chosen_light.broken()
+
/datum/controller/subsystem/machinery/stat_entry(msg)
msg = {"\n\
Queues: \
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index a8ec8ef5f5a..abe7e493ffe 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -463,18 +463,19 @@ SUBSYSTEM_DEF(ticker)
// Compute and, if available, print the ghost roles in the pre-round lobby. Begone, people who do not ready up to see what ghost roles will be available!
var/list/available_ghostroles = list()
- for(var/s in SSghostroles.spawners)
- var/datum/ghostspawner/G = SSghostroles.spawners[s]
- if(G.enabled \
- && !("Antagonist" in G.tags) \
- && !(G.loc_type == GS_LOC_ATOM && !length(G.spawn_atoms)) \
- && (G.req_perms == null) \
- )
- available_ghostroles |= G.name
+ if(SSatlas.current_sector?.ghostroles_enabled)
+ for(var/s in SSghostroles.spawners)
+ var/datum/ghostspawner/G = SSghostroles.spawners[s]
+ if(G.enabled \
+ && !("Antagonist" in G.tags) \
+ && !(G.loc_type == GS_LOC_ATOM && !length(G.spawn_atoms)) \
+ && (G.req_perms == null) \
+ )
+ available_ghostroles |= G.name
- // Special case, to list the Merchant in case it is available at roundstart
- if(SSjobs.type_occupations[/datum/job/merchant]?.total_positions)
- available_ghostroles |= SSjobs.type_occupations[/datum/job/merchant].title
+ // Special case, to list the Merchant in case it is available at roundstart
+ if(SSjobs.type_occupations[/datum/job/merchant]?.total_positions)
+ available_ghostroles |= SSjobs.type_occupations[/datum/job/merchant].title
if(length(available_ghostroles))
to_world("
" \
diff --git a/code/datums/components/_hivenetechoes.dm b/code/datums/components/_hivenetechoes.dm
index f2c671fabf5..df78397fd1b 100644
--- a/code/datums/components/_hivenetechoes.dm
+++ b/code/datums/components/_hivenetechoes.dm
@@ -24,18 +24,20 @@
owner = parent
- // Check during component init if we're in a sector that blocks hivenet echoes, and if so, skip processing init.
- if(!SSatlas.current_sector.hivenet_echoes)
- if((SSatlas.current_sector.name in list(SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR)))
- to_chat(parent, SPAN_CULT("The Fog cuts you off from the greater Hivenet. Without its echoes, you feel deeply dreadful."))
- else
- to_chat(parent, SPAN_WARNING("The faint echoes of the greater Hivenet fade away. Without them, you feel low in company."))
- return
-
// Setup the first time the echoes will begin playing.
next_broadcastEcho = world.time + rand(180, 300) SECONDS
next_projectionEcho = world.time + rand(240, 480) SECONDS
+ // Check during component init if we're in a sector that blocks hivenet echoes, and if so, skip processing init.
+ if(!SSatlas.current_sector.hivenet_echoes)
+ if(is_lemurian_sea_sector())
+ to_chat(parent, SPAN_CULT("The Fog cuts you off from the greater Hivenet. Without its echoes, you feel deeply dreadful."))
+ else
+ to_chat(parent, SPAN_WARNING("The faint echoes of the greater Hivenet fade away. Without them, you feel low in company."))
+ if(is_lemurian_sea_sector())
+ START_PROCESSING(SSprocessing, src)
+ return
+
// Finally start the clock.
START_PROCESSING(SSprocessing, src)
@@ -45,6 +47,13 @@
return ..()
/datum/component/HiveEchoes/process(seconds_per_tick)
+ if(is_lemurian_sea_sector())
+ return
+
+ if(!SSatlas.current_sector.hivenet_echoes)
+ STOP_PROCESSING(SSprocessing, src)
+ return
+
if(owner.stat != CONSCIOUS)
return
@@ -91,6 +100,11 @@
if(!check_rights(R_ADMIN))
return
+ if(is_lemurian_sea_sector())
+ SSatlas.current_sector.hivenet_echoes = FALSE
+ to_chat(usr, SPAN_WARNING("The Fog prevents Hivenet Echoes from being restored in the Lemurian Sea."))
+ return
+
if(SSatlas.current_sector.hivenet_echoes)
SSatlas.current_sector.hivenet_echoes = FALSE
to_chat(usr, SPAN_INFO("Vaurcae have been cut off from echoes (fluff) of the greater Hivenet."))
diff --git a/code/game/objects/effects/plastic_explosive.dm b/code/game/objects/effects/plastic_explosive.dm
index 35b4c98d7ba..89a5ee5a234 100644
--- a/code/game/objects/effects/plastic_explosive.dm
+++ b/code/game/objects/effects/plastic_explosive.dm
@@ -16,12 +16,12 @@
/obj/effect/plastic_explosive/Initialize(var/atom/owner_pos, var/atom/target, var/obj/item/plastique/c4)
. = ..()
- parent = c4
- parent.effect_overlay = src
- parent.forceMove(src)
- name = parent.name
- desc = parent.desc
- set_position(get_dir(src, target))
+ if(parent)
+ parent = c4
+ parent.forceMove(src)
+ name = parent.name
+ desc = parent.desc
+ set_position(get_dir(src, target))
/obj/effect/plastic_explosive/Destroy()
QDEL_NULL(parent)
diff --git a/code/game/objects/items/devices/lighting/hull_beacon.dm b/code/game/objects/items/devices/lighting/hull_beacon.dm
index fd2dc71bb59..719f9c7c233 100644
--- a/code/game/objects/items/devices/lighting/hull_beacon.dm
+++ b/code/game/objects/items/devices/lighting/hull_beacon.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/lighting.dmi'
anchored = TRUE
light_system = MOVABLE_LIGHT
- light_range = 3
+ light_range = 1.6 // LEMURIAN SEA, AFTER ARC RESTORE TO 3
/obj/item/hullbeacon/Initialize()
. = ..()
diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm
index dff11e63e18..eae3d912f7c 100644
--- a/code/game/objects/items/weapons/explosives.dm
+++ b/code/game/objects/items/weapons/explosives.dm
@@ -15,7 +15,6 @@
var/timer = 300
var/atom/target = null
var/open_panel = 0
- var/obj/effect/plastic_explosive/effect_overlay
/// Type of plastic explosive effect created on the obj we attack.
var/plastic_explosive_type = /obj/effect/plastic_explosive
@@ -49,6 +48,8 @@
/obj/item/plastique/Destroy()
qdel(wires)
wires = null
+ qdel(target)
+ target = null
return ..()
/obj/item/plastique/attackby(obj/item/attacking_item, mob/user)
@@ -120,7 +121,6 @@
target = get_atom_on_turf(src)
if(!target)
target = src
- QDEL_NULL(effect_overlay)
if(location)
explosion(location, devastation_range, heavy_impact_range, light_impact_range, 3, spreading = 0)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ab491137d9c..34d0bb7cd27 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -218,15 +218,16 @@
if(in_use)
var/is_in_use = 0
var/list/nearby = viewers(1, src)
+ var/datum/tgui/ui
for(var/mob/M in nearby)
- if ((M.client && M.machine == src))
- is_in_use = 1
- src.attack_hand(M)
- if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
- if (!(usr in nearby))
- if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
+ if(M.client)
+ if(SStgui.try_update_ui(M, src, ui))
is_in_use = 1
- src.attack_ai(usr)
+ if(istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
+ if(!(usr in nearby))
+ if(usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
+ is_in_use = 1
+ ui = SStgui.try_update_ui(usr, src, ui)
in_use = is_in_use
/obj/proc/updateDialog()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 916fe9716a4..20a16a7a28a 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -14,6 +14,7 @@ GLOBAL_LIST_INIT(admin_verbs_default, list(
GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game.*/
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
+ /client/proc/adpi_panel,
// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage */
/datum/admins/proc/show_game_mode, /*Configuration window for the current game mode.*/
/datum/admins/proc/force_mode_latespawn, /*Force the mode to try a latespawn proc*/
@@ -125,6 +126,7 @@ GLOBAL_LIST_INIT(admin_verbs_sounds, list(
GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/object_talk,
+ /client/proc/adpi_panel,
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_grab_observers,
/client/proc/cmd_admin_gib_self,
@@ -447,6 +449,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list(
/client/proc/odyssey_panel,
/client/proc/jobbans,
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
+ /client/proc/adpi_panel,
/datum/admins/proc/paralyze_mob,
/client/proc/toggleattacklogs,
/client/proc/cmd_admin_check_contents,
diff --git a/code/modules/admin/secrets/random_events/gravity.dm b/code/modules/admin/secrets/random_events/gravity.dm
deleted file mode 100644
index 8ee24bbc7c9..00000000000
--- a/code/modules/admin/secrets/random_events/gravity.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-/**********
-* Gravity *
-**********/
-/datum/admin_secret_item/random_event/gravity/New()
- ..()
- name = "Toggle [station_name(TRUE)] Artificial Gravity"
-
-/datum/admin_secret_item/random_event/gravity/can_execute(var/mob/user)
- if(!(SSticker.mode))
- return 0
-
- return ..()
-
-/datum/admin_secret_item/random_event/gravity/execute(var/mob/user)
- . = ..()
- if(!.)
- return
-
- GLOB.gravity_is_on = !GLOB.gravity_is_on
- for(var/A in SSmachinery.gravity_generators)
- var/obj/structure/machinery/gravity_generator/main/B = A
- B.eventshutofftoggle()
-
- feedback_inc("admin_secrets_fun_used",1)
- feedback_add_details("admin_secrets_fun_used","Grav")
- if(GLOB.gravity_is_on)
- log_admin("[key_name(user)] toggled gravity on.")
- message_admins(SPAN_NOTICE("[key_name_admin(user)] toggled gravity on."), 1)
- command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.")
- else
- log_admin("[key_name(user)] toggled gravity off.")
- message_admins(SPAN_NOTICE("[key_name_admin(usr)] toggled gravity off."), 1)
- command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 9578b8f3ec5..a89df383ce0 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -939,6 +939,9 @@
if(!(GLOB.all_languages[LANGUAGE_VAURCA] in H.languages))
to_chat(usr, "The person you are trying to contact is incapable of recieving Hivenet transmissions.")
return
+ if(is_lemurian_sea_sector())
+ to_chat(usr, "The Lemurian Sea prevents Hivenet transmissions.")
+ return
var/input = sanitize(input(src.owner, "Please enter a message to reply to [key_name(H)] via the Hivenet.", "Outgoing transmission from the Hive...", ""))
if(!input) return
to_chat(src.owner, "You sent [input] to [H] via a secure Hivenet channel.")
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index cc6fd7814fe..753b7a408a9 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1143,3 +1143,17 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("[key_name_admin(usr)] used the Narration Panel")
log_admin("[key_name(usr)] used the Narration Panel")
feedback_add_details("admin_verb", "AONP")
+
+/client/proc/adpi_panel()
+ set category = "Fun"
+ set name = "ADPI Panel"
+
+ if(!check_rights(R_ADMIN|R_FUN, TRUE))
+ return
+
+ var/datum/tgui_module/adpi_panel/panel = new
+ panel.ui_interact(usr)
+
+ message_admins("[key_name_admin(usr)] used the ADPI Panel")
+ log_admin("[key_name(usr)] used the ADPI Panel")
+ feedback_add_details("admin_verb", "ADPI")
diff --git a/code/modules/background/space_sectors/space_sector.dm b/code/modules/background/space_sectors/space_sector.dm
index 5d7b57d51a0..6ef2f797a1d 100644
--- a/code/modules/background/space_sectors/space_sector.dm
+++ b/code/modules/background/space_sectors/space_sector.dm
@@ -12,7 +12,8 @@
var/list/possible_exoplanets = list(/obj/effect/overmap/visitable/sector/exoplanet/snow, /obj/effect/overmap/visitable/sector/exoplanet/desert)
///Guaranteed planets to spawn. This ignores the map exoplanet limit, so don't put too many planets in here.
var/list/guaranteed_exoplanets = list()
- var/list/cargo_price_coef = list( //how much the space sector afffects how expensive is ordering from that cargo supplier
+ /// How much the space sector afffects how expensive is ordering from that cargo supplier
+ var/list/cargo_price_coef = list(
"nanotrasen" = 1,
"orion" = 1,
"hephaestus" = 1,
@@ -60,9 +61,15 @@
/// Does this sector permit communication with Central Command? Reserved for remote/uncharted sectors. The EBS system is unaffected as it is necessary for certain CCIA functions (eg. scuttling).
var/ccia_link = TRUE
- ///Does this sector allow Vaurcae catch fluff echoes of the greater Hivenet? Primarily for Lemurian Sea, but some super remote areas also fit. Obviously, consult w/ lore.
+ /// Does this sector allow Vaurcae catch fluff echoes of the greater Hivenet? Primarily for Lemurian Sea, but some super remote areas also fit. Obviously, consult w/ lore.
var/hivenet_echoes = TRUE
+ /// Whether ghost roles are available in this sector.
+ var/ghostroles_enabled = TRUE
+ /// Whether away sites are loaded in this sector.
+ var/away_sites_enabled = TRUE
+ /// This variable is a multiplier applied to the 'overmap_event_areas' datum/map/var to increase or decrease the total number of hazards spawned in the sector.
+ var/overmap_hazards_multiplier = 1.0
//vars used by the meteor random event
var/list/meteors_minor = list(
@@ -231,6 +238,9 @@
/// Returns a flat list of all possible away sites that can spawn in this sector.
/datum/space_sector/proc/possible_sites_in_sector()
+ if(!away_sites_enabled)
+ return list()
+
var/list/away_sites = list()
for(var/id in SSmapping.away_sites_templates)
var/datum/map_template/ruin/away_site = SSmapping.away_sites_templates[id]
diff --git a/code/modules/background/space_sectors/void.dm b/code/modules/background/space_sectors/void.dm
index f4d431f585e..619a1dc4cd4 100644
--- a/code/modules/background/space_sectors/void.dm
+++ b/code/modules/background/space_sectors/void.dm
@@ -6,6 +6,7 @@
starlight_color = "#2d0850"
starlight_power = 1//slightly darker though for spooky factor
starlight_range = 2
+ overmap_hazards_multiplier = 1.4
possible_exoplanets = list(/obj/effect/overmap/visitable/sector/exoplanet/barren/asteroid, /obj/effect/overmap/visitable/sector/exoplanet/grass/grove, /obj/effect/overmap/visitable/sector/exoplanet/barren, /obj/effect/overmap/visitable/sector/exoplanet/lava, /obj/effect/overmap/visitable/sector/exoplanet/desert, /obj/effect/overmap/visitable/sector/exoplanet/snow)
cargo_price_coef = list(
@@ -38,18 +39,40 @@
name = SECTOR_LEMURIAN_SEA
description = "The Lemurian Sea is an astrological curiosity which is entirely free of stars. This region is a relatively new discovery and classification, having only been officially broken off of Light’s Edge by most astrographical institutions following the rediscovery of Assunzione and limited exploration beyond its position on the border of what would become the Lemurian Sea. Most astrological charts advise avoiding the region as travelers are known to report a feeling of general uneasiness while passing through it and many vessels are known to have disappeared within the Sea. "
skybox_icon = "void"//its just black
- possible_exoplanets = null//nothing should be here
+ possible_exoplanets = list(/obj/effect/overmap/visitable/sector/exoplanet/barren/asteroid)
starlight_color = "#000000"
starlight_power = 0
starlight_range = 0
+ overmap_hazards_multiplier = 2.0
+ cargo_price_coef = list(
+ "nanotrasen" = 6.0,
+ "orion" = 6.0,
+ "hephaestus" = 6.0,
+ "zeng_hu" = 6.0,
+ "eckharts" = 6.0,
+ "getmore" = 6.0,
+ "arizi" = 6.0,
+ "blam" = 6.0,
+ "iac" = 6.0,
+ "zharkov" = 6.0,
+ "virgo" = 6.0,
+ "bishop" = 6.0,
+ "xion" = 6.0,
+ "zavodskoi" = 6.0,
+ )
lobby_tracks = list(
'sound/music/lobby/lights_edge/lights_edge_1.ogg',
'sound/music/lobby/lights_edge/lights_edge_2.ogg',
'sound/music/lobby/dangerous_space/dangerous_space_1.ogg'
)
+
+ sector_welcome_message = 'sound/AI/welcome_lemurian_sea_outer.ogg'
hivenet_echoes = FALSE
+ ghostroles_enabled = FALSE
+ away_sites_enabled = FALSE
+ ccia_link = FALSE
/datum/space_sector/lemurian_sea/far
name = SECTOR_LEMURIAN_SEA_FAR
- ccia_link = FALSE
+ sector_welcome_message = 'sound/AI/welcome_lemurian_sea_inner.ogg'
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 2bbb2ecb45a..16b5528a7aa 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -384,6 +384,10 @@ GLOBAL_LIST_INIT(severity_to_string, alist(EVENT_LEVEL_MUNDANE = "Mundane", EVEN
new /datum/event_meta(EVENT_LEVEL_MAJOR, "APC Damage", /datum/event/apc_damage,
20, list(ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_JANITOR = 20)),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Dark Matter Influx", /datum/event/gravity_anomaly,
+ 5, list(ASSIGNMENT_ENGINEER = 20), is_one_shot = TRUE,
+ pop_needed = 12),
+
)
#undef ASSIGNMENT_ANY
diff --git a/code/modules/events/gravity.dm b/code/modules/events/gravity.dm
index 560b9aca988..87d725f7d4e 100644
--- a/code/modules/events/gravity.dm
+++ b/code/modules/events/gravity.dm
@@ -11,10 +11,7 @@
/datum/event/gravity/start()
..()
-
- // TO-DO: I'm not gonna touch this can of worms right now, but this effectively means that gravity is global for everyone,
- // meaning, if the horizon loses gravity, EVERYONE DOES. this seriously needs to be fixed, and i'm commenting out the gravity flux event until that is done
- GLOB.gravity_is_on = 0
for(var/obj/structure/machinery/gravity_generator/main/generator in SSmachinery.gravity_generators)
if(generator.z in affecting_z)
- generator.eventshutofftoggle()
+ return
+ // generator.eventshutofftoggle()
diff --git a/code/modules/events/gravity_anomaly.dm b/code/modules/events/gravity_anomaly.dm
new file mode 100644
index 00000000000..ac85fbfa8d0
--- /dev/null
+++ b/code/modules/events/gravity_anomaly.dm
@@ -0,0 +1,247 @@
+/datum/event/gravity_anomaly
+ announceWhen = 0
+ startWhen = 90 SECONDS
+ endWhen = 60 SECONDS // Actually gets set in setup()
+ ic_name = "a gravitational anomaly"
+ var/list/valid_victims
+ has_skybox_image = TRUE
+ var/global/lightning_color
+ var/next_camera_drift = 0
+ var/next_gravity_surge = 0
+ var/nausea_minor = list(
+ "You feel the deck lurch beneath you.",
+ "The world feels like it's tilting madly.",
+ "An illusory sense of your center of gravity shifting briefly comes over you."
+ )
+ var/nausea_mild_organic = list(
+ "You feel a sudden lurching in your guts and inner ear.",
+ "You suddenly feel a nauseating sense of vertigo.",
+ "An overwhelming sense of nausea briefly makes you dizzy."
+ )
+ var/nausea_mild_ipc = list(
+ "Your proprioceptive faculties briefly surge with misinputs.",
+ "With a lurch, it feels like you briefly lose any sense of gyroscopic orientation.",
+ "Your visual input registers normal, but your cognition insists the room is spinning rapidly."
+ )
+ var/nausea_strong_organic = list(
+ "A huge surge of a sick feeling rises in your gorge.",
+ "You suddenly feel like you've been spinning in circles for an hour straight.",
+ "Your inner ear and your brain are screaming at you to stop rotating, but you're not."
+ )
+ var/nausea_strong_ipc = list(
+ "ERR: GYROSCOPIC ARRAY COMPILER RETURNING VALUES OF INVALID TYPE",
+ "ERR: CRITICAL DESYNCHRONIZATION IN PRIMARY AND SECONDARY MOTOR FUNCTIONAL POSITIONAL FEEDBACK",
+ "ERR: SENSORY FACULTIES ENCOUNTERING MULTIPLE VALUE DISCONTINUITIES"
+ )
+
+/datum/event/gravity_anomaly/setup()
+ endWhen = rand(5 MINUTES, 25 MINUTES)
+
+/datum/event/gravity_anomaly/announce()
+ command_announcement.Announce(
+ "Feedback surge detected in gravity generation systems due to influx of dark matter. Instability to be expected until clear of the effect; gravity shutdown recommended for the duration of the effect.",
+ "Dark Matter Influx",
+ zlevels = affecting_z
+ )
+
+/datum/event/gravity_anomaly/start()
+ ..()
+ next_camera_drift = activeFor
+ schedule_next_gravity_surge()
+ valid_victims = list()
+ for(var/mob/living/carbon/human/victim in GLOB.player_list)
+ // We don't bother excluding players not on the Horizon here; we check for that whenever we're going to potentially shove them around.
+ // This way, someone entering or leaving the Horizon z-levels will be affected by the event appropriately.
+ valid_victims += victim
+ if(!(victim.z in affecting_z))
+ continue
+ notify_victim_of_start(victim)
+
+/datum/event/gravity_anomaly/end(faked)
+ valid_victims?.Cut()
+ ..()
+
+/datum/event/gravity_anomaly/get_skybox_image()
+ if(!lightning_color)
+ lightning_color = pick( "#aa11ee")
+ var/image/res = overlay_image('icons/skybox/electrobox.dmi', "lightning", lightning_color, RESET_COLOR)
+ res.blend_mode = BLEND_ADD
+ return res
+
+/datum/event/gravity_anomaly/tick()
+ ..()
+
+ if(!has_active_gravity_generator())
+ return
+
+ var/list/current_victims = get_current_victims()
+ if(!length(current_victims))
+ return
+
+ if(activeFor >= next_camera_drift)
+ apply_camera_drift(current_victims)
+ next_camera_drift = activeFor + rand(2, 4)
+
+ for(var/mob/living/carbon/human/victim in current_victims)
+ var/current_effect = pick_victim_disturbance()
+ if(current_effect)
+ apply_effect(victim, current_effect)
+
+/datum/event/gravity_anomaly/proc/notify_victim_of_start(var/mob/living/carbon/human/victim)
+ if(isipc(victim))
+ to_chat(victim, SPAN_MACHINE_WARNING("Your proprioceptive faculties briefly surge with misinputs; something is definitely Wrong with the gravity."))
+ else
+ to_chat(victim, SPAN_WARNING("You suddenly feel a nauseating sense of vertigo; something is definitely Wrong with the gravity."))
+
+/datum/event/gravity_anomaly/proc/has_active_gravity_generator()
+ for(var/obj/structure/machinery/gravity_generator/main/grav_gen in SSmachinery.gravity_generators)
+ if(!(grav_gen?.z in affecting_z))
+ continue
+ if(grav_gen.on)
+ return TRUE
+ return FALSE
+
+/datum/event/gravity_anomaly/proc/pick_victim_disturbance()
+ if(activeFor >= next_gravity_surge && prob(50))
+ schedule_next_gravity_surge()
+ if(prob(10))
+ return "surge_hilarious"
+ return "surge_default"
+
+ switch(rand(1,100))
+ if(1 to 20)
+ return "nausea_minor"
+ if(21 to 28)
+ return "nausea_mild"
+ if(29 to 31)
+ return "nausea_strong"
+ else
+ return null
+
+/datum/event/gravity_anomaly/proc/schedule_next_gravity_surge()
+ next_gravity_surge = activeFor + rand(20, 60)
+
+/datum/event/gravity_anomaly/proc/get_current_victims()
+ var/list/current_victims = list()
+ for(var/mob/living/carbon/human/potential_victim in valid_victims)
+ if(can_affect_victim(potential_victim))
+ current_victims += potential_victim
+ return current_victims
+
+/datum/event/gravity_anomaly/proc/can_affect_victim(var/mob/living/carbon/human/potential_victim)
+ if(!potential_victim)
+ return FALSE
+ // Have they entered or left the Horizon? Skip if not aboard.
+ if(!(potential_victim.z in affecting_z))
+ return FALSE
+ // Is there gravity where they are? IE. is the grav generator on/off OR are they on the holodeck w/ zero gravity, OR are they EVA? Skip if no artificial gravity for whatever reason.
+ if(!potential_victim.has_gravity())
+ return FALSE
+ return TRUE
+
+/datum/event/gravity_anomaly/proc/apply_effect(var/mob/living/carbon/human/victim, var/current_effect)
+ switch(current_effect)
+ if("nausea_minor")
+ apply_nausea_minor(victim)
+ if("nausea_mild")
+ apply_nausea_mild(victim)
+ if("nausea_strong")
+ apply_nausea_strong(victim)
+ if("surge_default")
+ apply_surge_default(victim)
+ if("surge_hilarious")
+ apply_surge_hilarious(victim)
+ else
+ log_admin("Failed to generate grav anom result for user [victim].")
+
+/datum/event/gravity_anomaly/proc/apply_camera_drift(var/list/current_victims)
+ for(var/mob/living/carbon/human/victim in current_victims)
+ if(prob(15))
+ continue
+ shake_camera(victim, rand(3 SECONDS, 10 SECONDS), 0.1, TRUE)
+
+/datum/event/gravity_anomaly/proc/apply_nausea_minor(var/mob/living/carbon/human/victim)
+ victim.dizziness += rand(2, 4)
+ victim.confused += 1
+ if(prob(10))
+ to_chat(victim, SPAN_WARNING(pick(nausea_minor)))
+
+/datum/event/gravity_anomaly/proc/apply_nausea_mild(var/mob/living/carbon/human/victim)
+ victim.dizziness += rand(3, 6)
+ victim.confused += rand(0, 1)
+ if(isipc(victim))
+ if(prob(20))
+ to_chat(victim, SPAN_MACHINE_WARNING(pick(nausea_mild_ipc)))
+ else
+ if(prob(20))
+ to_chat(victim, SPAN_WARNING(pick(nausea_mild_organic)))
+ if(prob(10))
+ victim.vomit()
+
+/datum/event/gravity_anomaly/proc/apply_nausea_strong(var/mob/living/carbon/human/victim)
+ victim.dizziness += rand(8, 15)
+ victim.confused += rand(1, 3)
+ if(prob(50))
+ if(isipc(victim))
+ to_chat(victim, SPAN_MACHINE_WARNING(pick(nausea_strong_ipc)))
+ else
+ to_chat(victim, SPAN_WARNING(pick(nausea_strong_organic)))
+ victim.vomit()
+
+/datum/event/gravity_anomaly/proc/apply_surge_default(var/mob/living/carbon/human/victim)
+ victim.dizziness += rand(4, 10)
+ victim.confused += rand(1, 5)
+ if(prob(50))
+ if(isipc(victim))
+ to_chat(victim, SPAN_MACHINE_WARNING(pick(nausea_strong_ipc)))
+ else
+ to_chat(victim, SPAN_WARNING(pick(nausea_strong_organic)))
+ victim.vomit()
+ if(prob(70))
+ if(victim.buckled_to)
+ to_chat(victim, SPAN_WARNING("Sudden gravity flux presses you into your chair!"))
+ shake_camera(victim, 3, 1)
+ else if(victim.Check_Shoegrip(FALSE))
+ to_chat(victim, SPAN_WARNING("You feel immense pressure in your feet as the artificial gravity surges!"))
+ victim.apply_damage(10, DAMAGE_PAIN, BP_L_FOOT)
+ victim.apply_damage(10, DAMAGE_PAIN, BP_R_FOOT)
+ shake_camera(victim, 5, 1)
+ else
+ to_chat(victim, SPAN_WARNING("The floor lurches beneath you!"))
+ shake_camera(victim, 10, 1)
+ victim.visible_message(SPAN_DANGER("[victim.name] is tossed around by a sudden knot in the artifical gravity field!"))
+ victim.throw_at_random(FALSE, 4, 1)
+ victim.Weaken(3)
+
+/datum/event/gravity_anomaly/proc/apply_surge_hilarious(var/mob/living/carbon/human/victim)
+ victim.dizziness += rand(8, 15)
+ victim.confused += rand(1, 10)
+ if(isipc(victim))
+ to_chat(victim, SPAN_MACHINE_WARNING(pick(nausea_strong_ipc)))
+ else
+ to_chat(victim, SPAN_WARNING(pick(nausea_strong_organic)))
+ if(prob(33))
+ victim.vomit()
+ if(prob(60))
+ if(victim.buckled_to)
+ to_chat(victim, SPAN_WARNING("Sudden gravity flux rattles you in your chair!"))
+ shake_camera(victim, 5, 2)
+ else if(victim.Check_Shoegrip(FALSE))
+ victim.apply_damage(20, DAMAGE_PAIN, BP_L_FOOT)
+ victim.apply_damage(10, DAMAGE_BRUTE, BP_L_FOOT)
+ victim.apply_damage(20, DAMAGE_PAIN, BP_R_FOOT)
+ victim.apply_damage(10, DAMAGE_BRUTE, BP_R_FOOT)
+ to_chat(victim, SPAN_WARNING("You feel like your ankles are about to be ripped apart as the artificial gravity surges!"))
+ shake_camera(victim, 8, 2)
+ else
+ victim.visible_message(SPAN_DANGER("[victim.name] is flung violently by a horrible gravity flux!"))
+ victim.Weaken(8)
+ if(prob(90))
+ victim.throw_at_random(FALSE, 7, 1)
+ else
+ victim.fall_impact(1)
+
+/datum/event/gravity_anomaly/announce_end()
+ . = ..()
+ if(.)
+ command_announcement.Announce("Gravimetric sensors indicate reduced levels of dark matter flux; artificial gravity is now safe for regular use.", "Dark Matter Influx", zlevels = affecting_z)
diff --git a/code/modules/ghostroles/spawner/base.dm b/code/modules/ghostroles/spawner/base.dm
index 69984f8fefe..e1091039fdb 100644
--- a/code/modules/ghostroles/spawner/base.dm
+++ b/code/modules/ghostroles/spawner/base.dm
@@ -84,6 +84,9 @@
//Return a error message if the user CANT see the ghost spawner. Otherwise FALSE
/datum/ghostspawner/proc/cant_see(mob/user) //If the user can see the spawner in the menu
+ if(SSatlas?.current_sector && !SSatlas.current_sector.ghostroles_enabled)
+ return "Ghost roles are unavailable in this sector."
+
if(req_perms) //Only those with the correct flags can see restricted roles
if(check_rights(req_perms, show_msg=FALSE, user=user))
return FALSE //Return early and dont perform whitelist checks if staff flags are met
@@ -109,6 +112,8 @@
/datum/ghostspawner/proc/cant_spawn(mob/user) //If the user can spawn using the spawner
if(!ROUND_IS_STARTED)
return "The round is not started yet."
+ if(SSatlas?.current_sector && !SSatlas.current_sector.ghostroles_enabled)
+ return "Ghost roles are unavailable in this sector."
var/cant_see = cant_see(user)
if(cant_see) //If we cant see it, we cant spawn it
return cant_see
@@ -243,6 +248,9 @@
return isobserver(user) && loc_type == GS_LOC_POS
/datum/ghostspawner/proc/is_enabled()
+ if(SSatlas?.current_sector && !SSatlas.current_sector.ghostroles_enabled)
+ return FALSE
+
if(loc_type == GS_LOC_ATOM)
return enabled && !!length(spawn_atoms)
if(max_count)
diff --git a/code/modules/maptext_alerts/screen_alerts.dm b/code/modules/maptext_alerts/screen_alerts.dm
index ad3409ce29e..74671f05ae6 100644
--- a/code/modules/maptext_alerts/screen_alerts.dm
+++ b/code/modules/maptext_alerts/screen_alerts.dm
@@ -76,6 +76,18 @@
style_open = ""
style_close = ""
+/atom/movable/screen/text/screen_text/adpi_message
+ maptext_height = 64
+ maptext_width = 480
+ maptext_x = 0
+ maptext_y = 0
+ screen_loc = "LEFT,TOP-3"
+
+ letters_per_update = 1
+ fade_out_delay = 8 SECONDS
+ style_open = ""
+ style_close = ""
+
///proc for actually playing this screen_text on a mob.
/atom/movable/screen/text/screen_text/proc/play_to_client()
player?.add_to_screen(src)
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 8c0ca4433e5..6f1b60d139f 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -1430,7 +1430,6 @@ GLOBAL_LIST_INIT_TYPED(total_extraction_beacons, /obj/structure/extraction_point
target = get_atom_on_turf(src)
if(!target)
target = src
- QDEL_NULL(effect_overlay)
if(location)
new /obj/effect/overlay/temp/explosion(location)
playsound(location, 'sound/effects/Explosion1.ogg', 100, 1)
diff --git a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm
index f985deb5102..366baf9676c 100644
--- a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm
+++ b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm
@@ -197,6 +197,11 @@
set category = "Storyteller"
set desc = "Switch if Vaurcae can hear faint echoes (fluff) of the greater Hivenet. They will notice."
+ if(is_lemurian_sea_sector())
+ SSatlas.current_sector.hivenet_echoes = FALSE
+ to_chat(src, "The Fog prevents Hivenet Echoes from being restored in the Lemurian Sea.")
+ return
+
if(SSatlas.current_sector.hivenet_echoes)
SSatlas.current_sector.hivenet_echoes = FALSE
to_chat(src, "Vaurcae have been cut off from echoes (fluff) of the greater Hivenet.")
diff --git a/code/modules/mob/language/station.dm b/code/modules/mob/language/station.dm
index d8b66e43432..4a25ac54bfb 100644
--- a/code/modules/mob/language/station.dm
+++ b/code/modules/mob/language/station.dm
@@ -234,6 +234,10 @@
/datum/language/bug/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
log_say("[key_name(speaker)] : ([name]) [message]")
+ if(is_lemurian_sea_sector())
+ to_chat(speaker, SPAN_WARNING("You attempt to reach the Hivenet, but find nothing!"))
+ return
+
var/mob/living/carbon/human/H = speaker //Check for Preimminent Shaper robes, which obscure Hive affiliation
var/obj/item/clothing/head/shaper/helmet = H.get_equipped_item(slot_head)
if(!speaker_mask)
@@ -304,6 +308,9 @@
return "[verb], "
/datum/language/bug/check_special_condition(var/mob/other)
+ if(is_lemurian_sea_sector())
+ return 0
+
if(istype(other, /mob/living/silicon))
var/mob/living/silicon/S = other
if(S.can_hear_hivenet)
@@ -340,6 +347,10 @@
return 0
/datum/language/bug/check_speech_restrict(var/mob/speaker)
+ if(is_lemurian_sea_sector())
+ to_chat(speaker, SPAN_WARNING("You attempt to reach the Hivenet, but find nothing!"))
+ return FALSE
+
var/mob/living/carbon/human/H = speaker
var/obj/item/organ/internal/vaurca/neuralsocket/S = H.internal_organs_by_name[BP_NEURAL_SOCKET]
var/obj/item/organ/internal/augment/language/vekatak/V = H.internal_organs_by_name[BP_AUG_LANGUAGE_VEKATAK]
diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm
index d117a220282..f680e8bb26f 100644
--- a/code/modules/mob/living/carbon/human/human_powers.dm
+++ b/code/modules/mob/living/carbon/human/human_powers.dm
@@ -1658,6 +1658,10 @@
to_chat(src, SPAN_DANGER("Your mind is dark, unable to communicate with the Hive."))
return
+ if(is_lemurian_sea_sector())
+ to_chat(src, SPAN_DANGER("The Fog cuts you off from the Hivenet."))
+ return
+
if(!istype(S) && !istype(V))
to_chat(src, SPAN_WARNING("You do not have a functional connection to the Hivenet!"))
return
@@ -2050,6 +2054,9 @@
if(!istype(S))
to_chat(src, SPAN_WARNING("You require a functional neural socket to do this!"))
return FALSE
+ if(is_lemurian_sea_sector())
+ to_chat(src, SPAN_DANGER("The Fog cuts you off from the Hivenet."))
+ return FALSE
if(S.last_action > world.time)
to_chat(src, SPAN_WARNING("You must wait before attempting another Hivenet action!"))
return FALSE
@@ -2185,6 +2192,10 @@
to_chat(src, SPAN_WARNING("You are not connected to the Hivenet!"))
return
+ if(is_lemurian_sea_sector())
+ to_chat(src, SPAN_WARNING("You attempt to reach the Hivenet, but find nothing!"))
+ return
+
if(within_jamming_range(src))
to_chat(src, SPAN_WARNING("You attempt to reach the Hivenet, but find nothing!"))
return
@@ -2204,6 +2215,10 @@
set desc = "Get a list of all vaurca currently on the Hivenet."
set category = "Hivenet"
+ if(is_lemurian_sea_sector())
+ to_chat(src, SPAN_WARNING("You attempt to query the Hivenet, but find nothing."))
+ return
+
var/list/all_vaurca = list()
for(var/mob/living/carbon/human/vaurca in GLOB.human_mob_list)
if(!vaurca.stat && isvaurca(vaurca) && vaurca.internal_organs_by_name[BP_NEURAL_SOCKET])
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index c0723e651d8..7c5600ba309 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -169,8 +169,8 @@
return default_language
return GLOB.all_languages[LANGUAGE_LOCAL_DRONE]
-/mob/living/silicon/robot/drone/fall_impact()
- ..(damage_mod = 0.05) //reduces fall damage by 95%
+/mob/living/silicon/robot/drone/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
+ ..(levels_fallen, stopped_early, damage_mod * 0.05) //reduces fall damage by 95%
/mob/living/silicon/robot/drone/construction
// Look and feel
diff --git a/code/modules/mob/living/simple_animal/friendly/carp.dm b/code/modules/mob/living/simple_animal/friendly/carp.dm
index c3555d2079b..e483b6d02d9 100644
--- a/code/modules/mob/living/simple_animal/friendly/carp.dm
+++ b/code/modules/mob/living/simple_animal/friendly/carp.dm
@@ -64,7 +64,7 @@
blood_overlay_icon = initial(blood_overlay_icon)
handle_blood(TRUE)
-/mob/living/simple_animal/carp/fall_impact()
+/mob/living/simple_animal/carp/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
src.visible_message(SPAN_NOTICE("\The [src] gently floats to a stop."))
return FALSE
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 0cef2f3c4ec..f89c328603d 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -209,7 +209,7 @@
. = ..()
set_flee_target(throwingdatum?.thrower?.resolve() ? throwingdatum.thrower.resolve() : src.loc)
-/mob/living/simple_animal/cat/fall_impact()
+/mob/living/simple_animal/cat/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
src.visible_message(SPAN_NOTICE("\The [src] lands softly on \the [loc]!"))
return FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
index 162e1d5e6b0..0fd47ca6a71 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
@@ -211,7 +211,7 @@
/mob/living/simple_animal/hostile/retaliate/minedrone/adjustHalLoss(var/damage)
return
-/mob/living/simple_animal/hostile/retaliate/minedrone/fall_impact()
+/mob/living/simple_animal/hostile/retaliate/minedrone/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
visible_message(SPAN_DANGER("\The [src] bounces harmlessly on its inflated wheels."))
return FALSE
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index b0aac7de318..002aa468e90 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -481,11 +481,13 @@ GLOBAL_LIST_INIT(organ_rel_size, list(
#define TILES_PER_SECOND 0.7
/// Shake the camera of the person viewing the mob SO REAL!
-/// Takes the mob to shake, the time span to shake for, and the amount of tiles we're allowed to shake by in tiles
+/// Takes the mob to shake, the time span to shake for, and the amount of tiles we're allowed to shake by in tiles.
+/// Set drift to exactly TRUE for slower, gentler movement instead of impact-like shaking.
/// Duration isn't taken as a strict limit, since we don't trust our coders to not make things feel shitty. So it's more like a soft cap.
-/proc/shake_camera(mob/M, duration, strength=1)
+/proc/shake_camera(mob/M, duration, strength=1, drift = FALSE)
if(!M || !M.client || duration < 1)
return
+ var/drift_mode = (drift == TRUE)
var/client/C = M.client
var/oldx = C.pixel_x
var/oldy = C.pixel_y
@@ -505,8 +507,12 @@ GLOBAL_LIST_INIT(organ_rel_size, list(
var/x_pos = rand(min_x, max_x) + oldx
var/y_pos = rand(min_y, max_y) + oldy
- //We take the smaller of our two distances so things still have the propencity to feel somewhat jerky
- var/time = round(max(min(abs(last_x - x_pos), abs(last_y - y_pos)) * time_scalar, 1))
+ var/time
+ if(drift_mode)
+ time = round(clamp(max(abs(last_x - x_pos), abs(last_y - y_pos)) * time_scalar * 6, 6, 20))
+ else
+ //We take the smaller of our two distances so things still have the propencity to feel somewhat jerky
+ time = round(max(min(abs(last_x - x_pos), abs(last_y - y_pos)) * time_scalar, 1))
if (time_spent == 0)
animate(C, pixel_x=x_pos, pixel_y=y_pos, time=time)
@@ -518,7 +524,7 @@ GLOBAL_LIST_INIT(organ_rel_size, list(
//We go based on time spent, so there is a chance we'll overshoot our duration. Don't care
time_spent += time
- animate(pixel_x=oldx, pixel_y=oldy, time=3)
+ animate(pixel_x=oldx, pixel_y=oldy, time = drift_mode ? 10 : 3)
#undef TILES_PER_SECOND
diff --git a/code/modules/overmap/events/event.dm b/code/modules/overmap/events/event.dm
index 914afe6964f..a671c194d4d 100644
--- a/code/modules/overmap/events/event.dm
+++ b/code/modules/overmap/events/event.dm
@@ -13,11 +13,14 @@
// Acquire the list of not-yet utilized overmap turfs on this Z-level
var/list/candidate_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, z_level),locate(overmap_size - OVERMAP_EDGE, overmap_size - OVERMAP_EDGE,z_level))
candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap/visitable)
+ var/list/available_event_types = get_available_event_types()
+ if(!length(available_event_types))
+ return
for(var/i = 1 to number_of_events)
if(!candidate_turfs.len)
break
- var/overmap_event_type = pick(subtypesof(/datum/overmap_event))
+ var/overmap_event_type = pick(available_event_types)
var/datum/overmap_event/datum_spawn = new overmap_event_type
var/list/event_turfs = acquire_event_turfs(datum_spawn.count, datum_spawn.radius, candidate_turfs, datum_spawn.continuous)
@@ -29,6 +32,15 @@
qdel(datum_spawn)//idk help how do I do this better?
+/singleton/overmap_event_handler/proc/get_available_event_types()
+ var/list/available_event_types = list()
+ for(var/overmap_event_type in subtypesof(/datum/overmap_event))
+ var/datum/overmap_event/datum_spawn = new overmap_event_type
+ if(datum_spawn.spawns_in_current_sector())
+ available_event_types += overmap_event_type
+ qdel(datum_spawn)
+ return available_event_types
+
/singleton/overmap_event_handler/proc/acquire_event_turfs(var/number_of_turfs, var/distance_from_origin, var/list/candidate_turfs, var/continuous = TRUE)
number_of_turfs = min(number_of_turfs, candidate_turfs.len)
candidate_turfs = candidate_turfs.Copy() // Not this proc's responsibility to adjust the given lists
@@ -195,6 +207,8 @@
var/ship_delay_time = 10
/// Ticks up each process until move speed is matched, at which point the event will move
var/ship_delay_counter = 0
+ /// Text that appears in the tooltip.
+ var/tooltip_text = "A hazard."
/obj/effect/overmap/event/Initialize()
. = ..()
@@ -266,6 +280,7 @@
opacity = 1
event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4")
difficulty = EVENT_LEVEL_MAJOR
+ tooltip_text = "Large rocks and debris traveling at high speed that can destroy entire hull sections."
/obj/effect/overmap/event/electric
name = "electrical storm"
@@ -273,6 +288,7 @@
event_icon_states = list("electrical1", "electrical2")
difficulty = EVENT_LEVEL_MAJOR
can_be_destroyed = FALSE
+ tooltip_text = "Electromagnetic storm effects that can damage or disable electrical grids."
/obj/effect/overmap/event/dust
name = "dust cloud"
@@ -280,6 +296,7 @@
opacity = 1
event_icon_states = list("dust1", "dust2", "dust3", "dust4")
can_be_destroyed = FALSE
+ tooltip_text = "Small dust and debris traveling at high speed that can damage or destroy external windows."
/obj/effect/overmap/event/ion
name = "ion cloud"
@@ -287,6 +304,7 @@
event_icon_states = list("ion1", "ion2", "ion3", "ion4")
difficulty = EVENT_LEVEL_MAJOR
can_be_destroyed = FALSE
+ tooltip_text = "Ionic effects that can damage synthetics like IPCs or AI, as well as disrupt telecommunications."
/obj/effect/overmap/event/carp
name = "carp shoal"
@@ -294,6 +312,7 @@
difficulty = EVENT_LEVEL_MODERATE
event_icon_states = list("carp")
movable_event_chance = 30
+ tooltip_text = "Xenofauna: usually hostile."
/obj/effect/overmap/event/carp/major
name = "carp school"
@@ -301,22 +320,40 @@
difficulty = EVENT_LEVEL_MAJOR
movable_event_chance = 15
-// see comment at code/modules/events/gravity.dm
-// tl;dr gravity is handled globally, meaning if the horizon loses gravity, everyone does
-// /obj/effect/overmap/event/gravity
-// name = "dark matter influx"
-// events = list(/datum/event/gravity)
-// can_be_destroyed = FALSE
+/obj/effect/overmap/event/gravity_anomaly
+ name = "gravitic anomaly"
+ events = list(/datum/event/gravity_anomaly)
+ difficulty = EVENT_LEVEL_MODERATE
+ event_icon_states = list("grav1")
+ can_be_destroyed = FALSE
+ tooltip_text = "Unstable gravitic shear effects detected; wide-field artificial gravity should be powered down before transit."
-///These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards
+/obj/effect/overmap/event/MouseEntered(location, control, params)
+ . = ..()
+ openToolTip(usr, src, params, tooltip_text)
+
+/obj/effect/overmap/event/MouseExited(location, control, params)
+ . = ..()
+ closeToolTip(usr)
+
+/// These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards
/datum/overmap_event
var/name = "map event"
var/radius = 2
var/count = 6
var/hazards
var/opacity = 0
- /// If it should form continous blobs, or can have gaps
+ /// If it should form continous blobs, or can have gaps.
var/continuous = TRUE
+ /// If set, this event will only spawn in the named space sectors.
+ var/list/sectors = list()
+
+/datum/overmap_event/proc/spawns_in_current_sector()
+ if(!length(sectors))
+ return TRUE
+ if(!SSatlas.current_sector)
+ return FALSE
+ return (SSatlas.current_sector.name in sectors)
/datum/overmap_event/meteor
name = "asteroid field"
@@ -359,11 +396,9 @@
opacity = 1
hazards = /obj/effect/overmap/event/carp/major
-// see comment at code/modules/events/gravity.dm
-// tl;dr gravity is handled globally, meaning if the horizon loses gravity, everyone does
-// this needs to be fixed before we can uncomment this
-// /datum/overmap_event/gravity
-// name = "dark matter influx"
-// count = 12
-// radius = 4
-// hazards = /obj/effect/overmap/event/gravity
+/datum/overmap_event/gravity
+ name = "dark matter influx"
+ count = 15
+ radius = 8
+ hazards = /obj/effect/overmap/event/gravity_anomaly
+ sectors = list(SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR)
diff --git a/code/modules/overmap/overmap_object.dm b/code/modules/overmap/overmap_object.dm
index df64352feb9..486df6dfbfd 100644
--- a/code/modules/overmap/overmap_object.dm
+++ b/code/modules/overmap/overmap_object.dm
@@ -32,15 +32,23 @@
var/list/map_z = list()
- var/known = 0 //shows up on nav computers automatically
- var/scannable //if set to TRUE will show up on ship sensors for detailed scans
- var/unknown_id // A unique identifier used when this entity is scanned. Assigned in Initialize().
- var/requires_contact = TRUE //whether or not the effect must be identified by ship sensors before being seen.
- var/instant_contact = FALSE //do we instantly identify ourselves to any ship in sensors range?
- var/sensor_range_override = FALSE //When true, this overmap object will be scanned with range instead of view.
+ /// Shows up on nav computers automatically
+ var/known = 0
+ /// If set to TRUE will show up on ship sensors for detailed scans
+ var/scannable
+ /// A unique identifier used when this entity is scanned. Assigned in Initialize().
+ var/unknown_id
+ /// Whether or not the effect must be identified by ship sensors before being seen.
+ var/requires_contact = TRUE
+ /// Do we instantly identify ourselves to any ship in sensors range?
+ var/instant_contact = FALSE
+ /// When true, this overmap object will be scanned with range instead of view.
+ var/sensor_range_override = FALSE
- var/sensor_visibility = 10 //how likely it is to increase identification process each scan.
- var/vessel_mass = 10000 // metric tonnes, very rough number, affects acceleration provided by engines
+ /// How likely it is to increase identification process each scan.
+ var/sensor_visibility = 10
+ /// Metric tonnes, very rough number, affects acceleration provided by engines
+ var/vessel_mass = 10000
var/image/targeted_overlay
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index fb61651e687..6134b3e1bea 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -2,10 +2,11 @@
#define POWER_UP 1
#define POWER_DOWN 2
-#define GRAV_NEEDS_SCREWDRIVER 0
-#define GRAV_NEEDS_WELDING 1
-#define GRAV_NEEDS_PLASTEEL 2
-#define GRAV_NEEDS_WRENCH 3
+#define GRAV_OPERATIONAL 0
+#define GRAV_NEEDS_SCREWDRIVER 1
+#define GRAV_NEEDS_WELDING 2
+#define GRAV_NEEDS_PLASTEEL 3
+#define GRAV_NEEDS_WRENCH 4
#define AREA_ERRNONE 0
#define AREA_STATION 1
@@ -16,18 +17,18 @@
//
/obj/structure/machinery/gravity_generator
- name = "gravitational generator"
- desc = "A device which produces a gravaton field when set up."
+ name = "gravity generator"
+ desc = "A complex and energy-hungry device which produces a graviton field over a modest radius when active."
icon = 'icons/obj/machinery/gravity_generator.dmi'
- anchored = 1
- density = 1
+ anchored = TRUE
+ density = TRUE
use_power = POWER_USE_OFF
- unacidable = 1
+ unacidable = TRUE
var/sprite_number = 0
light_color = LIGHT_COLOR_CYAN
- light_power = 1
- light_range = 8
- var/datum/looping_sound/gravgen/soundloop
+ light_power = 1.4
+ light_range = 6
+ interact_offline = TRUE
/obj/structure/machinery/gravity_generator/ex_act(severity)
if(severity == 1) // Very sturdy.
@@ -108,23 +109,47 @@
/obj/structure/machinery/gravity_generator/main
icon_state = "on_8"
- idle_power_usage = 0
- active_power_usage = 3000
+ idle_power_usage = 5 KILO WATTS
+ active_power_usage = 10 KILO WATTS
power_channel = AREA_USAGE_ENVIRON
sprite_number = 8
interact_offline = 1
- var/on = 1
- var/breaker = 1
+ /// Whether the gravity generator is currently active.
+ var/on = TRUE
+ /// If the main breaker is on/off, to enable/disable gravity.
+ var/breaker = TRUE
+ /// If the generator is idle, charging, or down.
+ var/charging_state = POWER_IDLE
+ /// How much charge the gravity generator has, goes down when breaker is shut, and shuts down at 0.
+ var/charge_count = 100
+ /// The gravity core overlay currently used.
+ var/current_overlay = null
+ /// Currently configured gravity strength.
+ var/setting = 1.0
+ /// Audio for when the gravgen is on
+ var/datum/looping_sound/gravgen/soundloop
var/list/sprite_parts = list()
var/obj/middle = null
- var/charging_state = POWER_IDLE
- var/charge_count = 100
- var/current_overlay = null
- var/broken_state = 0
+ /// When broken, what stage it is at (GRAV_NEEDS_SCREWDRIVER:0) (GRAV_NEEDS_WELDING:1) (GRAV_NEEDS_PLASTEEL:2) (GRAV_NEEDS_WRENCH:3)
+ var/broken_state = GRAV_OPERATIONAL
var/list/localareas = list()
var/round_start = 2 //To help stop a bug with round start
- var/backpanelopen = 0
- var/eventon = 0
+ var/panel_open_heavy = 0
+
+/obj/structure/machinery/gravity_generator/main/Initialize()
+ . = ..()
+ soundloop = new(src, start_immediately = FALSE)
+ addtimer(CALLBACK(src, PROC_REF(updateareas)), 10)
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/structure/machinery/gravity_generator/main/LateInitialize()
+ ..()
+ if(SSatlas.current_map.use_overmap && !linked)
+ var/my_sector = GLOB.map_sectors["[z]"]
+ if (istype(my_sector, /obj/effect/overmap/visitable))
+ attempt_hook_up(my_sector)
+ if(linked)
+ linked.gravity_generator = src
/obj/structure/machinery/gravity_generator/main/Destroy()
LOG_DEBUG("Gravity Generator Destroyed")
@@ -138,20 +163,6 @@
linked?.gravity_generator = null
return ..()
-/obj/structure/machinery/gravity_generator/main/proc/eventshutofftoggle() // Used by the gravity event. Bypasses charging and all of that stuff.
- breaker = 0
- set_state(eventon)
- sleep(20)
- charge_count = 0
- breaker = 1
- charging_state = POWER_UP
- set_power()
- eventon = !eventon
- addtimer(CALLBACK(src, PROC_REF(reset_event)), 100) // Because it takes 100 seconds for it to recharge. And we need to make sure we resen this var
-
-/obj/structure/machinery/gravity_generator/main/proc/reset_event()
- eventon = !eventon
-
/obj/structure/machinery/gravity_generator/main/proc/setup_parts()
var/turf/our_turf = get_turf(src)
// 9x9 block obtained from the bottom middle of the block
@@ -184,7 +195,7 @@
charge_count = 0
breaker = 0
set_power()
- set_state(0)
+ disable()
investigate_log("has broken down.", "gravity")
/obj/structure/machinery/gravity_generator/main/set_fix()
@@ -232,65 +243,50 @@
else
..()
if(attacking_item.tool_behaviour == TOOL_CROWBAR)
- if(backpanelopen)
+ if(panel_open_heavy)
attacking_item.play_tool_sound(get_turf(src), 50)
to_chat(user, SPAN_NOTICE("You replace the back panel."))
- backpanelopen = 0
+ panel_open_heavy = 0
else
attacking_item.play_tool_sound(get_turf(src), 50)
to_chat(user, SPAN_NOTICE("You open the back panel."))
- backpanelopen = 1
+ panel_open_heavy = 1
if(old_broken_state != broken_state)
update_icon()
/obj/structure/machinery/gravity_generator/main/attack_hand(mob/user as mob)
- if(!..())
- return interact(user)
-
-/obj/structure/machinery/gravity_generator/main/interact(mob/user as mob)
- if(stat & BROKEN)
+ if(..(user))
return
- var/dat = "Gravity Generator Breaker: "
- if(!eventon)
- if(breaker)
- dat += "ON OFF"
- else
- dat += "ON OFF "
- if(backpanelopen)
- dat += "
Emergency shutoff:
"
- dat += "Red Button"
+ ui_interact(user)
- dat += "
Generator Status: