diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
index 2f276160fe..809cc987eb 100644
--- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
@@ -176,7 +176,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
index 191e6291ea..1c501e87f0 100644
--- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
@@ -151,7 +151,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index e2483fdbef..0df0217497 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -128,7 +128,7 @@ Thus, the two variables affect pump operation are set in New():
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
index 3c342c3cdb..5eeab2c8e4 100644
--- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm
+++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
@@ -107,7 +107,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm
index af8020f9e1..6675eef38b 100644
--- a/code/ATMOSPHERICS/components/unary/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm
@@ -247,7 +247,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index ff97f259a5..1c1c696b98 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -93,7 +93,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
"area" = area_uid,
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index d01ff407a2..6039f9f65b 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -74,6 +74,21 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
// Those networks can only be accessed by pre-existing terminals. AIs and new terminals can't use them.
var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret", NETWORK_COMMUNICATORS)
+#define TRANSMISSION_WIRE 0 //Is this ever used? I don't think it is.
+#define TRANSMISSION_RADIO 1 //Radio transmissions (like airlock controller to pump)
+#define TRANSMISSION_SUBSPACE 2 //Like headsets
+#define TRANSMISSION_BLUESPACE 3 //Point-to-point links
+
+#define SIGNAL_NORMAL 0 //Normal subspace signals
+#define SIGNAL_SIMPLE 1 //Normal inter-machinery(?) signals
+#define SIGNAL_FAKE 2 //Untrackable signals
+#define SIGNAL_TEST 4 //Unlogged signals
+
+#define DATA_NORMAL 0 //Normal data
+#define DATA_INTERCOM 1 //Intercoms only
+#define DATA_LOCAL 2 //Intercoms and SBRs
+#define DATA_ANTAG 3 //Antag interception
+#define DATA_FAKE 4 //Not from a real mob
//singularity defines
#define STAGE_ONE 1
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index f35061b3c0..7be9093596 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -246,7 +246,7 @@
var/turf/speaker = get_turf(R)
if(speaker)
for(var/turf/T in hear(R.canhear_range,speaker))
- speaker_coverage[T] = T
+ speaker_coverage[T] = R
// Try to find all the players who can hear the message
diff --git a/code/controllers/subsystems/shuttles.dm b/code/controllers/subsystems/shuttles.dm
index 907701f0bf..0bce940385 100644
--- a/code/controllers/subsystems/shuttles.dm
+++ b/code/controllers/subsystems/shuttles.dm
@@ -104,7 +104,7 @@ SUBSYSTEM_DEF(shuttles)
try_add_landmark_tag(shuttle_landmark_tag, O)
landmarks_still_needed -= shuttle_landmark_tag
else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically
- O = map_sectors["[shuttle_landmark.z]"]
+ O = get_overmap_sector(get_z(shuttle_landmark))
O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark)
/datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag)
diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm
index 404525b439..356458517d 100644
--- a/code/controllers/subsystems/skybox.dm
+++ b/code/controllers/subsystems/skybox.dm
@@ -87,7 +87,7 @@ SUBSYSTEM_DEF(skybox)
res.overlays += base
if(global.using_map.use_overmap && settings.use_overmap_details)
- var/obj/effect/overmap/visitable/O = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/O = get_overmap_sector(z)
if(istype(O))
var/image/overmap = image(settings.icon)
overmap.overlays += O.generate_skybox()
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index 7fd6745814..b17640430b 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -1,564 +1,564 @@
-//
-// Ticker controls the state of the game, being responsible for round start, game mode, and round end.
-//
-SUBSYSTEM_DEF(ticker)
- name = "Gameticker"
- wait = 2 SECONDS
- init_order = INIT_ORDER_TICKER
- priority = FIRE_PRIORITY_TICKER
- flags = SS_NO_TICK_CHECK | SS_KEEP_TIMING
- runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // Every runlevel!
-
- var/const/restart_timeout = 3 MINUTES // Default time to wait before rebooting in desiseconds.
- var/current_state = GAME_STATE_INIT // We aren't even at pregame yet // TODO replace with CURRENT_GAME_STATE
-
- /* Relies upon the following globals (TODO move those in here) */
- // var/master_mode = "extended" //The underlying game mode (so "secret" or the voted mode).
- // Set by SSvote when VOTE_GAMEMODE finishes.
- // var/round_progressing = 1 //Whether the lobby clock is ticking down.
-
- var/pregame_timeleft = 0 // Time remaining until game starts in seconds. Set by config
- var/start_immediately = FALSE // If true there is no lobby phase, the game starts immediately.
-
- var/hide_mode = FALSE // If the true game mode should be hidden (because we chose "secret")
- var/datum/game_mode/mode = null // The actual gamemode, if selected.
-
- var/end_game_state = END_GAME_NOT_OVER // Track where we are ending game/round
- var/restart_timeleft // Time remaining until restart in desiseconds
- var/last_restart_notify // world.time of last restart warning.
- var/delay_end = FALSE // If set, the round will not restart on its own.
-
- var/login_music // music played in pregame lobby
-
- var/list/datum/mind/minds = list() // The people in the game. Used for objective tracking.
-
- // TODO - I am sure there is a better place these can go.
- var/Bible_icon_state // icon_state the chaplain has chosen for his bible
- var/Bible_item_state // item_state the chaplain has chosen for his bible
- var/Bible_name // name of the bible
- var/Bible_deity_name
-
- var/random_players = FALSE // If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
-
- // TODO - Should this go here or in the job subsystem?
- var/triai = FALSE // Global flag for Triumvirate AI being enabled
-
- //station_explosion used to be a variable for every mob's hud. Which was a waste!
- //Now we have a general cinematic centrally held within the gameticker....far more efficient!
- var/obj/screen/cinematic = null
-
-// This global variable exists for legacy support so we don't have to rename every 'ticker' to 'SSticker' yet.
-var/global/datum/controller/subsystem/ticker/ticker
-/datum/controller/subsystem/ticker/PreInit()
- global.ticker = src // TODO - Remove this! Change everything to point at SSticker intead
- login_music = pick(\
- /*'sound/music/halloween/skeletons.ogg',\
- 'sound/music/halloween/halloween.ogg',\
- 'sound/music/halloween/ghosts.ogg'*/
- 'sound/music/space.ogg',\
- 'sound/music/traitor.ogg',\
- 'sound/music/title2.ogg',\
- 'sound/music/clouds.s3m',\
- 'sound/music/space_oddity.ogg') //Ground Control to Major Tom, this song is cool, what's going on?
-
-/datum/controller/subsystem/ticker/Initialize()
- pregame_timeleft = config.pregame_time
- send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]")
- return ..()
-
-/datum/controller/subsystem/ticker/fire(resumed = FALSE)
- switch(current_state)
- if(GAME_STATE_INIT)
- pregame_welcome()
- current_state = GAME_STATE_PREGAME
- if(GAME_STATE_PREGAME)
- pregame_tick()
- if(GAME_STATE_SETTING_UP)
- setup_tick()
- if(GAME_STATE_PLAYING)
- playing_tick()
- if(GAME_STATE_FINISHED)
- post_game_tick()
-
-/datum/controller/subsystem/ticker/proc/pregame_welcome()
- to_world("Welcome to the pregame lobby!")
- to_world("Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.")
-
-// Called during GAME_STATE_PREGAME (RUNLEVEL_LOBBY)
-/datum/controller/subsystem/ticker/proc/pregame_tick()
- if(round_progressing && last_fire)
- pregame_timeleft -= (world.time - last_fire) / (1 SECOND)
-
- if(start_immediately)
- pregame_timeleft = 0
- else if(SSvote.time_remaining)
- return // vote still going, wait for it.
-
- // Time to start the game!
- if(pregame_timeleft <= 0)
- current_state = GAME_STATE_SETTING_UP
- Master.SetRunLevel(RUNLEVEL_SETUP)
- if(start_immediately)
- fire() // Don't wait for next tick, do it now!
- return
-
- if(pregame_timeleft <= config.vote_autogamemode_timeleft && !SSvote.gamemode_vote_called)
- SSvote.autogamemode() // Start the game mode vote (if we haven't had one already)
-
-// Called during GAME_STATE_SETTING_UP (RUNLEVEL_SETUP)
-/datum/controller/subsystem/ticker/proc/setup_tick(resumed = FALSE)
- if(!setup_choose_gamemode())
- // It failed, go back to lobby state and re-send the welcome message
- pregame_timeleft = config.pregame_time
- SSvote.gamemode_vote_called = FALSE // Allow another autogamemode vote
- current_state = GAME_STATE_PREGAME
- Master.SetRunLevel(RUNLEVEL_LOBBY)
- pregame_welcome()
- return
- // If we got this far we succeeded in picking a game mode. Punch it!
- setup_startgame()
- return
-
-// Formerly the first half of setup() - The part that chooses the game mode.
-// Returns 0 if failed to pick a mode, otherwise 1
-/datum/controller/subsystem/ticker/proc/setup_choose_gamemode()
- //Create and announce mode
- if(master_mode == "secret")
- src.hide_mode = TRUE
-
- var/list/runnable_modes = config.get_runnable_modes()
- if((master_mode == "random") || (master_mode == "secret"))
- if(!runnable_modes.len)
- to_world("Unable to choose playable game mode. Reverting to pregame lobby.")
- return 0
- if(secret_force_mode != "secret")
- src.mode = config.pick_mode(secret_force_mode)
- if(!src.mode)
- var/list/weighted_modes = list()
- for(var/datum/game_mode/GM in runnable_modes)
- weighted_modes[GM.config_tag] = config.probabilities[GM.config_tag]
- src.mode = gamemode_cache[pickweight(weighted_modes)]
- else
- src.mode = config.pick_mode(master_mode)
-
- if(!src.mode)
- to_world("Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context.
- return 0
-
- job_master.ResetOccupations()
- src.mode.create_antagonists()
- src.mode.pre_setup()
- job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly.
-
- if(!src.mode.can_start())
- to_world("Unable to start [mode.name]. Not enough players readied, [config.player_requirements[mode.config_tag]] players needed. Reverting to pregame lobby.")
- mode.fail_setup()
- mode = null
- job_master.ResetOccupations()
- return 0
-
- if(hide_mode)
- to_world("The current game mode is - Secret!")
- if(runnable_modes.len)
- var/list/tmpmodes = new
- for (var/datum/game_mode/M in runnable_modes)
- tmpmodes+=M.name
- tmpmodes = sortList(tmpmodes)
- if(tmpmodes.len)
- to_world("Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]")
- else
- src.mode.announce()
- return 1
-
-// Formerly the second half of setup() - The part that actually initializes everything and starts the game.
-/datum/controller/subsystem/ticker/proc/setup_startgame()
- setup_economy()
- create_characters() //Create player characters and transfer them.
- collect_minds()
- equip_characters()
- data_core.manifest()
-
- callHook("roundstart")
-
- spawn(0)//Forking here so we dont have to wait for this to finish
- mode.post_setup()
- //Cleanup some stuff
- for(var/obj/effect/landmark/start/S in landmarks_list)
- //Deleting Startpoints but we need the ai point to AI-ize people later
- if (S.name != "AI")
- qdel(S)
- to_world("Enjoy the game!")
- world << sound('sound/AI/welcome.ogg') // Skie
- //Holiday Round-start stuff ~Carn
- Holiday_Game_Start()
-
- var/list/adm = get_admin_counts()
- if(adm["total"] == 0)
- send2adminirc("A round has started with no admins online.")
-
-/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler
- master_controller.process() //Start master_controller.process()
- lighting_controller.process() //Start processing DynamicAreaLighting updates
- */
-
- processScheduler.start()
- current_state = GAME_STATE_PLAYING
- Master.SetRunLevel(RUNLEVEL_GAME)
-
- if(config.sql_enabled)
- statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
-
- return 1
-
-
-// Called during GAME_STATE_PLAYING (RUNLEVEL_GAME)
-/datum/controller/subsystem/ticker/proc/playing_tick(resumed = FALSE)
- mode.process() // So THIS is where we run mode.process() huh? Okay
-
- if(mode.explosion_in_progress)
- return // wait until explosion is done.
-
- // Calculate if game and/or mode are finished (Complicated by the continuous_rounds config option)
- var/game_finished = FALSE
- var/mode_finished = FALSE
- if (config.continous_rounds) // Game keeps going after mode ends.
- game_finished = (emergency_shuttle.returned() || mode.station_was_nuked)
- mode_finished = ((end_game_state >= END_GAME_MODE_FINISHED) || mode.check_finished()) // Short circuit if already finished.
- else // Game ends when mode does
- game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || universe_has_ended
- mode_finished = game_finished
-
- if(game_finished && mode_finished)
- end_game_state = END_GAME_READY_TO_END
- current_state = GAME_STATE_FINISHED
- Master.SetRunLevel(RUNLEVEL_POSTGAME)
- INVOKE_ASYNC(src, .proc/declare_completion)
- else if (mode_finished && (end_game_state < END_GAME_MODE_FINISHED))
- end_game_state = END_GAME_MODE_FINISHED // Only do this cleanup once!
- mode.cleanup()
- //call a transfer shuttle vote
- to_world("The round has ended!")
- SSvote.autotransfer()
-
-// Called during GAME_STATE_FINISHED (RUNLEVEL_POSTGAME)
-/datum/controller/subsystem/ticker/proc/post_game_tick()
- switch(end_game_state)
- if(END_GAME_READY_TO_END)
- callHook("roundend")
-
- if (mode.station_was_nuked)
- feedback_set_details("end_proper", "nuke")
- restart_timeleft = 1 MINUTE // No point waiting five minutes if everyone's dead.
- if(!delay_end)
- to_world("Rebooting due to destruction of [station_name()] in [round(restart_timeleft/600)] minute\s.")
- last_restart_notify = world.time
- else
- feedback_set_details("end_proper", "proper completion")
- restart_timeleft = restart_timeout
-
- if(blackbox)
- blackbox.save_all_data_to_sql() // TODO - Blackbox or statistics subsystem
-
- end_game_state = END_GAME_ENDING
- return
- if(END_GAME_ENDING)
- restart_timeleft -= (world.time - last_fire)
- if(delay_end)
- to_world("An admin has delayed the round end.")
- end_game_state = END_GAME_DELAYED
- else if(restart_timeleft <= 0)
- world.Reboot()
- else if (world.time - last_restart_notify >= 1 MINUTE)
- to_world("Restarting in [round(restart_timeleft/600, 1)] minute\s.")
- last_restart_notify = world.time
- return
- if(END_GAME_DELAYED)
- restart_timeleft -= (world.time - last_fire)
- if(!delay_end)
- end_game_state = END_GAME_ENDING
- else
- log_error("Ticker arrived at round end in an unexpected endgame state '[end_game_state]'.")
- end_game_state = END_GAME_READY_TO_END
-
-
-// ----------------------------------------------------------------------
-// These two below are not used! But they could be
-
-// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
-
-/datum/controller/subsystem/ticker/proc/PreRoundStart()
- return (current_state < GAME_STATE_PLAYING)
-
-/datum/controller/subsystem/ticker/proc/IsSettingUp()
- return (current_state == GAME_STATE_SETTING_UP)
-
-/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
- return (current_state == GAME_STATE_PLAYING)
-
-/datum/controller/subsystem/ticker/proc/HasRoundStarted()
- return (current_state >= GAME_STATE_PLAYING)
-
-// ------------------------------------------------------------------------
-// HELPER PROCS!
-// ------------------------------------------------------------------------
-
-//Plus it provides an easy way to make cinematics for other events. Just use this as a template :)
-/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
- if( cinematic ) return //already a cinematic in progress!
-
- //initialise our cinematic screen object
- cinematic = new(src)
- cinematic.icon = 'icons/effects/station_explosion.dmi'
- cinematic.icon_state = "station_intact"
- cinematic.layer = 100
- cinematic.plane = PLANE_PLAYER_HUD
- cinematic.mouse_opacity = 0
- cinematic.screen_loc = "1,0"
-
- var/obj/structure/bed/temp_buckle = new(src)
- //Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
- if(station_missed)
- for(var/mob/living/M in living_mob_list)
- M.buckled = temp_buckle //buckles the mob so it can't do anything
- if(M.client)
- M.client.screen += cinematic //show every client the cinematic
- else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
- for(var/mob/living/M in living_mob_list)
- M.buckled = temp_buckle
- if(M.client)
- M.client.screen += cinematic
-
- switch(M.z)
- if(0) //inside a crate or something
- var/turf/T = get_turf(M)
- if(T && T.z in using_map.station_levels) //we don't use M.death(0) because it calls a for(/mob) loop and
- M.health = 0
- M.set_stat(DEAD)
- if(1) //on a z-level 1 turf.
- M.health = 0
- M.set_stat(DEAD)
-
- //Now animate the cinematic
- switch(station_missed)
- if(1) //nuke was nearby but (mostly) missed
- if( mode && !override )
- override = mode.name
- switch( override )
- if("mercenary") //Nuke wasn't on station when it blew up
- flick("intro_nuke",cinematic)
- sleep(35)
- world << sound('sound/effects/explosionfar.ogg')
- flick("station_intact_fade_red",cinematic)
- cinematic.icon_state = "summary_nukefail"
- else
- flick("intro_nuke",cinematic)
- sleep(35)
- world << sound('sound/effects/explosionfar.ogg')
- //flick("end",cinematic)
-
-
- if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
- sleep(50)
- world << sound('sound/effects/explosionfar.ogg')
-
-
- else //station was destroyed
- if( mode && !override )
- override = mode.name
- switch( override )
- if("mercenary") //Nuke Ops successfully bombed the station
- flick("intro_nuke",cinematic)
- sleep(35)
- flick("station_explode_fade_red",cinematic)
- world << sound('sound/effects/explosionfar.ogg')
- cinematic.icon_state = "summary_nukewin"
- if("AI malfunction") //Malf (screen,explosion,summary)
- flick("intro_malf",cinematic)
- sleep(76)
- flick("station_explode_fade_red",cinematic)
- world << sound('sound/effects/explosionfar.ogg')
- cinematic.icon_state = "summary_malf"
- if("blob") //Station nuked (nuke,explosion,summary)
- flick("intro_nuke",cinematic)
- sleep(35)
- flick("station_explode_fade_red",cinematic)
- world << sound('sound/effects/explosionfar.ogg')
- cinematic.icon_state = "summary_selfdes"
- else //Station nuked (nuke,explosion,summary)
- flick("intro_nuke",cinematic)
- sleep(35)
- flick("station_explode_fade_red", cinematic)
- world << sound('sound/effects/explosionfar.ogg')
- cinematic.icon_state = "summary_selfdes"
- for(var/mob/living/M in living_mob_list)
- if(M.loc.z in using_map.station_levels)
- M.death()//No mercy
- //If its actually the end of the round, wait for it to end.
- //Otherwise if its a verb it will continue on afterwards.
- sleep(300)
-
- if(cinematic) qdel(cinematic) //end the cinematic
- if(temp_buckle) qdel(temp_buckle) //release everybody
- return
-
-
-/datum/controller/subsystem/ticker/proc/create_characters()
- for(var/mob/new_player/player in player_list)
- if(player && player.ready && player.mind)
- if(player.mind.assigned_role=="AI")
- player.close_spawn_windows()
- player.AIize()
- else if(!player.mind.assigned_role)
- continue
- else
- player.create_character()
- qdel(player)
-
-
-/datum/controller/subsystem/ticker/proc/collect_minds()
- for(var/mob/living/player in player_list)
- if(player.mind)
- minds += player.mind
-
-
-/datum/controller/subsystem/ticker/proc/equip_characters()
- var/captainless=1
- for(var/mob/living/carbon/human/player in player_list)
- if(player && player.mind && player.mind.assigned_role)
- if(player.mind.assigned_role == "Colony Director")
- captainless=0
- if(!player_is_antag(player.mind, only_offstation_roles = 1))
- job_master.EquipRank(player, player.mind.assigned_role, 0)
- UpdateFactionList(player)
- equip_custom_items(player)
- player.apply_traits()
- if(captainless)
- for(var/mob/M in player_list)
- if(!istype(M,/mob/new_player))
- to_chat(M, "Colony Directorship not forced on anyone.")
-
-
-/datum/controller/subsystem/ticker/proc/declare_completion()
- to_world("
A round of [mode.name] has ended!
")
- for(var/mob/Player in player_list)
- if(Player.mind && !isnewplayer(Player))
- if(Player.stat != DEAD)
- var/turf/playerTurf = get_turf(Player)
- if(emergency_shuttle.departed && emergency_shuttle.evac)
- if(isNotAdminLevel(playerTurf.z))
- to_chat(Player, "You survived the round, but remained on [station_name()] as [Player.real_name].")
- else
- to_chat(Player, "You managed to survive the events on [station_name()] as [Player.real_name].")
- else if(isAdminLevel(playerTurf.z))
- to_chat(Player, "You successfully underwent crew transfer after events on [station_name()] as [Player.real_name].")
- else if(issilicon(Player))
- to_chat(Player, "You remain operational after the events on [station_name()] as [Player.real_name].")
- else
- to_chat(Player, "You missed the crew transfer after the events on [station_name()] as [Player.real_name].")
- else
- if(istype(Player,/mob/observer/dead))
- var/mob/observer/dead/O = Player
- if(!O.started_as_observer)
- to_chat(Player, "You did not survive the events on [station_name()]...")
- else
- to_chat(Player, "You did not survive the events on [station_name()]...")
- to_world("
")
-
- for (var/mob/living/silicon/ai/aiPlayer in mob_list)
- if (aiPlayer.stat != 2)
- to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the round were:")
- else
- to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:")
- aiPlayer.show_laws(1)
-
- if (aiPlayer.connected_robots.len)
- var/robolist = "The AI's loyal minions were: "
- for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
- robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]"
- to_world("[robolist]")
-
- var/dronecount = 0
-
- for (var/mob/living/silicon/robot/robo in mob_list)
-
- if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm))
- dronecount++
- continue
-
- if (!robo.connected_ai)
- if (robo.stat != 2)
- to_world("[robo.name] (Played by: [robo.key]) survived as an AI-less stationbound synthetic! Its laws were:")
- else
- to_world("[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:")
-
- if(robo) //How the hell do we lose robo between here and the world messages directly above this?
- robo.laws.show_laws(world)
-
- if(dronecount)
- to_world("There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] at the end of this round.")
-
- mode.declare_completion()//To declare normal completion.
-
- //Ask the event manager to print round end information
- SSevents.RoundEnd()
-
- //Print a list of antagonists to the server log
- var/list/total_antagonists = list()
- //Look into all mobs in world, dead or alive
- for(var/datum/mind/Mind in minds)
- var/temprole = Mind.special_role
- if(temprole) //if they are an antagonist of some sort.
- if(temprole in total_antagonists) //If the role exists already, add the name to it
- total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
- else
- total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
- total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
-
- //Now print them all into the log!
- log_game("Antagonists at round end were...")
- for(var/i in total_antagonists)
- log_game("[i]s[total_antagonists[i]].")
-
- return 1
-
-/datum/controller/subsystem/ticker/stat_entry()
- switch(current_state)
- if(GAME_STATE_INIT)
- ..()
- if(GAME_STATE_PREGAME) // RUNLEVEL_LOBBY
- ..("START [round_progressing ? "[round(pregame_timeleft)]s" : "(PAUSED)"]")
- if(GAME_STATE_SETTING_UP) // RUNLEVEL_SETUP
- ..("SETUP")
- if(GAME_STATE_PLAYING) // RUNLEVEL_GAME
- ..("GAME")
- if(GAME_STATE_FINISHED) // RUNLEVEL_POSTGAME
- switch(end_game_state)
- if(END_GAME_MODE_FINISHED)
- ..("MODE OVER, WAITING")
- if(END_GAME_READY_TO_END)
- ..("ENDGAME PROCESSING")
- if(END_GAME_ENDING)
- ..("END IN [round(restart_timeleft/10)]s")
- if(END_GAME_DELAYED)
- ..("END PAUSED")
- else
- ..("ENDGAME ERROR:[end_game_state]")
-
-/datum/controller/subsystem/ticker/Recover()
- flags |= SS_NO_INIT // Don't initialize again
-
- current_state = SSticker.current_state
- mode = SSticker.mode
- pregame_timeleft = SSticker.pregame_timeleft
-
- end_game_state = SSticker.end_game_state
- delay_end = SSticker.delay_end
- restart_timeleft = SSticker.restart_timeleft
-
- minds = SSticker.minds
-
- Bible_icon_state = SSticker.Bible_icon_state
- Bible_item_state = SSticker.Bible_item_state
- Bible_name = SSticker.Bible_name
- Bible_deity_name = SSticker.Bible_deity_name
- random_players = SSticker.random_players
+//
+// Ticker controls the state of the game, being responsible for round start, game mode, and round end.
+//
+SUBSYSTEM_DEF(ticker)
+ name = "Gameticker"
+ wait = 2 SECONDS
+ init_order = INIT_ORDER_TICKER
+ priority = FIRE_PRIORITY_TICKER
+ flags = SS_NO_TICK_CHECK | SS_KEEP_TIMING
+ runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // Every runlevel!
+
+ var/const/restart_timeout = 3 MINUTES // Default time to wait before rebooting in desiseconds.
+ var/current_state = GAME_STATE_INIT // We aren't even at pregame yet // TODO replace with CURRENT_GAME_STATE
+
+ /* Relies upon the following globals (TODO move those in here) */
+ // var/master_mode = "extended" //The underlying game mode (so "secret" or the voted mode).
+ // Set by SSvote when VOTE_GAMEMODE finishes.
+ // var/round_progressing = 1 //Whether the lobby clock is ticking down.
+
+ var/pregame_timeleft = 0 // Time remaining until game starts in seconds. Set by config
+ var/start_immediately = FALSE // If true there is no lobby phase, the game starts immediately.
+
+ var/hide_mode = FALSE // If the true game mode should be hidden (because we chose "secret")
+ var/datum/game_mode/mode = null // The actual gamemode, if selected.
+
+ var/end_game_state = END_GAME_NOT_OVER // Track where we are ending game/round
+ var/restart_timeleft // Time remaining until restart in desiseconds
+ var/last_restart_notify // world.time of last restart warning.
+ var/delay_end = FALSE // If set, the round will not restart on its own.
+
+ var/login_music // music played in pregame lobby
+
+ var/list/datum/mind/minds = list() // The people in the game. Used for objective tracking.
+
+ // TODO - I am sure there is a better place these can go.
+ var/Bible_icon_state // icon_state the chaplain has chosen for his bible
+ var/Bible_item_state // item_state the chaplain has chosen for his bible
+ var/Bible_name // name of the bible
+ var/Bible_deity_name
+
+ var/random_players = FALSE // If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
+
+ // TODO - Should this go here or in the job subsystem?
+ var/triai = FALSE // Global flag for Triumvirate AI being enabled
+
+ //station_explosion used to be a variable for every mob's hud. Which was a waste!
+ //Now we have a general cinematic centrally held within the gameticker....far more efficient!
+ var/obj/screen/cinematic = null
+
+// This global variable exists for legacy support so we don't have to rename every 'ticker' to 'SSticker' yet.
+var/global/datum/controller/subsystem/ticker/ticker
+/datum/controller/subsystem/ticker/PreInit()
+ global.ticker = src // TODO - Remove this! Change everything to point at SSticker intead
+ login_music = pick(\
+ /*'sound/music/halloween/skeletons.ogg',\
+ 'sound/music/halloween/halloween.ogg',\
+ 'sound/music/halloween/ghosts.ogg'*/
+ 'sound/music/space.ogg',\
+ 'sound/music/traitor.ogg',\
+ 'sound/music/title2.ogg',\
+ 'sound/music/clouds.s3m',\
+ 'sound/music/space_oddity.ogg') //Ground Control to Major Tom, this song is cool, what's going on?
+
+/datum/controller/subsystem/ticker/Initialize()
+ pregame_timeleft = config.pregame_time
+ send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]")
+ return ..()
+
+/datum/controller/subsystem/ticker/fire(resumed = FALSE)
+ switch(current_state)
+ if(GAME_STATE_INIT)
+ pregame_welcome()
+ current_state = GAME_STATE_PREGAME
+ if(GAME_STATE_PREGAME)
+ pregame_tick()
+ if(GAME_STATE_SETTING_UP)
+ setup_tick()
+ if(GAME_STATE_PLAYING)
+ playing_tick()
+ if(GAME_STATE_FINISHED)
+ post_game_tick()
+
+/datum/controller/subsystem/ticker/proc/pregame_welcome()
+ to_world("Welcome to the pregame lobby!")
+ to_world("Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.")
+
+// Called during GAME_STATE_PREGAME (RUNLEVEL_LOBBY)
+/datum/controller/subsystem/ticker/proc/pregame_tick()
+ if(round_progressing && last_fire)
+ pregame_timeleft -= (world.time - last_fire) / (1 SECOND)
+
+ if(start_immediately)
+ pregame_timeleft = 0
+ else if(SSvote.time_remaining)
+ return // vote still going, wait for it.
+
+ // Time to start the game!
+ if(pregame_timeleft <= 0)
+ current_state = GAME_STATE_SETTING_UP
+ Master.SetRunLevel(RUNLEVEL_SETUP)
+ if(start_immediately)
+ fire() // Don't wait for next tick, do it now!
+ return
+
+ if(pregame_timeleft <= config.vote_autogamemode_timeleft && !SSvote.gamemode_vote_called)
+ SSvote.autogamemode() // Start the game mode vote (if we haven't had one already)
+
+// Called during GAME_STATE_SETTING_UP (RUNLEVEL_SETUP)
+/datum/controller/subsystem/ticker/proc/setup_tick(resumed = FALSE)
+ if(!setup_choose_gamemode())
+ // It failed, go back to lobby state and re-send the welcome message
+ pregame_timeleft = config.pregame_time
+ SSvote.gamemode_vote_called = FALSE // Allow another autogamemode vote
+ current_state = GAME_STATE_PREGAME
+ Master.SetRunLevel(RUNLEVEL_LOBBY)
+ pregame_welcome()
+ return
+ // If we got this far we succeeded in picking a game mode. Punch it!
+ setup_startgame()
+ return
+
+// Formerly the first half of setup() - The part that chooses the game mode.
+// Returns 0 if failed to pick a mode, otherwise 1
+/datum/controller/subsystem/ticker/proc/setup_choose_gamemode()
+ //Create and announce mode
+ if(master_mode == "secret")
+ src.hide_mode = TRUE
+
+ var/list/runnable_modes = config.get_runnable_modes()
+ if((master_mode == "random") || (master_mode == "secret"))
+ if(!runnable_modes.len)
+ to_world("Unable to choose playable game mode. Reverting to pregame lobby.")
+ return 0
+ if(secret_force_mode != "secret")
+ src.mode = config.pick_mode(secret_force_mode)
+ if(!src.mode)
+ var/list/weighted_modes = list()
+ for(var/datum/game_mode/GM in runnable_modes)
+ weighted_modes[GM.config_tag] = config.probabilities[GM.config_tag]
+ src.mode = gamemode_cache[pickweight(weighted_modes)]
+ else
+ src.mode = config.pick_mode(master_mode)
+
+ if(!src.mode)
+ to_world("Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context.
+ return 0
+
+ job_master.ResetOccupations()
+ src.mode.create_antagonists()
+ src.mode.pre_setup()
+ job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly.
+
+ if(!src.mode.can_start())
+ to_world("Unable to start [mode.name]. Not enough players readied, [config.player_requirements[mode.config_tag]] players needed. Reverting to pregame lobby.")
+ mode.fail_setup()
+ mode = null
+ job_master.ResetOccupations()
+ return 0
+
+ if(hide_mode)
+ to_world("The current game mode is - Secret!")
+ if(runnable_modes.len)
+ var/list/tmpmodes = new
+ for (var/datum/game_mode/M in runnable_modes)
+ tmpmodes+=M.name
+ tmpmodes = sortList(tmpmodes)
+ if(tmpmodes.len)
+ to_world("Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]")
+ else
+ src.mode.announce()
+ return 1
+
+// Formerly the second half of setup() - The part that actually initializes everything and starts the game.
+/datum/controller/subsystem/ticker/proc/setup_startgame()
+ setup_economy()
+ create_characters() //Create player characters and transfer them.
+ collect_minds()
+ equip_characters()
+ data_core.manifest()
+
+ callHook("roundstart")
+
+ spawn(0)//Forking here so we dont have to wait for this to finish
+ mode.post_setup()
+ //Cleanup some stuff
+ for(var/obj/effect/landmark/start/S in landmarks_list)
+ //Deleting Startpoints but we need the ai point to AI-ize people later
+ if (S.name != "AI")
+ qdel(S)
+ to_world("Enjoy the game!")
+ world << sound('sound/AI/welcome.ogg') // Skie
+ //Holiday Round-start stuff ~Carn
+ Holiday_Game_Start()
+
+ var/list/adm = get_admin_counts()
+ if(adm["total"] == 0)
+ send2adminirc("A round has started with no admins online.")
+
+/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler
+ master_controller.process() //Start master_controller.process()
+ lighting_controller.process() //Start processing DynamicAreaLighting updates
+ */
+
+ processScheduler.start()
+ current_state = GAME_STATE_PLAYING
+ Master.SetRunLevel(RUNLEVEL_GAME)
+
+ if(config.sql_enabled)
+ statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
+
+ return 1
+
+
+// Called during GAME_STATE_PLAYING (RUNLEVEL_GAME)
+/datum/controller/subsystem/ticker/proc/playing_tick(resumed = FALSE)
+ mode.process() // So THIS is where we run mode.process() huh? Okay
+
+ if(mode.explosion_in_progress)
+ return // wait until explosion is done.
+
+ // Calculate if game and/or mode are finished (Complicated by the continuous_rounds config option)
+ var/game_finished = FALSE
+ var/mode_finished = FALSE
+ if (config.continous_rounds) // Game keeps going after mode ends.
+ game_finished = (emergency_shuttle.returned() || mode.station_was_nuked)
+ mode_finished = ((end_game_state >= END_GAME_MODE_FINISHED) || mode.check_finished()) // Short circuit if already finished.
+ else // Game ends when mode does
+ game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || universe_has_ended
+ mode_finished = game_finished
+
+ if(game_finished && mode_finished)
+ end_game_state = END_GAME_READY_TO_END
+ current_state = GAME_STATE_FINISHED
+ Master.SetRunLevel(RUNLEVEL_POSTGAME)
+ INVOKE_ASYNC(src, .proc/declare_completion)
+ else if (mode_finished && (end_game_state < END_GAME_MODE_FINISHED))
+ end_game_state = END_GAME_MODE_FINISHED // Only do this cleanup once!
+ mode.cleanup()
+ //call a transfer shuttle vote
+ to_world("The round has ended!")
+ SSvote.autotransfer()
+
+// Called during GAME_STATE_FINISHED (RUNLEVEL_POSTGAME)
+/datum/controller/subsystem/ticker/proc/post_game_tick()
+ switch(end_game_state)
+ if(END_GAME_READY_TO_END)
+ callHook("roundend")
+
+ if (mode.station_was_nuked)
+ feedback_set_details("end_proper", "nuke")
+ restart_timeleft = 1 MINUTE // No point waiting five minutes if everyone's dead.
+ if(!delay_end)
+ to_world("Rebooting due to destruction of [station_name()] in [round(restart_timeleft/600)] minute\s.")
+ last_restart_notify = world.time
+ else
+ feedback_set_details("end_proper", "proper completion")
+ restart_timeleft = restart_timeout
+
+ if(blackbox)
+ blackbox.save_all_data_to_sql() // TODO - Blackbox or statistics subsystem
+
+ end_game_state = END_GAME_ENDING
+ return
+ if(END_GAME_ENDING)
+ restart_timeleft -= (world.time - last_fire)
+ if(delay_end)
+ to_world("An admin has delayed the round end.")
+ end_game_state = END_GAME_DELAYED
+ else if(restart_timeleft <= 0)
+ world.Reboot()
+ else if (world.time - last_restart_notify >= 1 MINUTE)
+ to_world("Restarting in [round(restart_timeleft/600, 1)] minute\s.")
+ last_restart_notify = world.time
+ return
+ if(END_GAME_DELAYED)
+ restart_timeleft -= (world.time - last_fire)
+ if(!delay_end)
+ end_game_state = END_GAME_ENDING
+ else
+ log_error("Ticker arrived at round end in an unexpected endgame state '[end_game_state]'.")
+ end_game_state = END_GAME_READY_TO_END
+
+
+// ----------------------------------------------------------------------
+// These two below are not used! But they could be
+
+// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
+
+/datum/controller/subsystem/ticker/proc/PreRoundStart()
+ return (current_state < GAME_STATE_PLAYING)
+
+/datum/controller/subsystem/ticker/proc/IsSettingUp()
+ return (current_state == GAME_STATE_SETTING_UP)
+
+/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
+ return (current_state == GAME_STATE_PLAYING)
+
+/datum/controller/subsystem/ticker/proc/HasRoundStarted()
+ return (current_state >= GAME_STATE_PLAYING)
+
+// ------------------------------------------------------------------------
+// HELPER PROCS!
+// ------------------------------------------------------------------------
+
+//Plus it provides an easy way to make cinematics for other events. Just use this as a template :)
+/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
+ if( cinematic ) return //already a cinematic in progress!
+
+ //initialise our cinematic screen object
+ cinematic = new(src)
+ cinematic.icon = 'icons/effects/station_explosion.dmi'
+ cinematic.icon_state = "station_intact"
+ cinematic.layer = 100
+ cinematic.plane = PLANE_PLAYER_HUD
+ cinematic.mouse_opacity = 0
+ cinematic.screen_loc = "1,0"
+
+ var/obj/structure/bed/temp_buckle = new(src)
+ //Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
+ if(station_missed)
+ for(var/mob/living/M in living_mob_list)
+ M.buckled = temp_buckle //buckles the mob so it can't do anything
+ if(M.client)
+ M.client.screen += cinematic //show every client the cinematic
+ else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
+ for(var/mob/living/M in living_mob_list)
+ M.buckled = temp_buckle
+ if(M.client)
+ M.client.screen += cinematic
+
+ switch(M.z)
+ if(0) //inside a crate or something
+ var/turf/T = get_turf(M)
+ if(T && T.z in using_map.station_levels) //we don't use M.death(0) because it calls a for(/mob) loop and
+ M.health = 0
+ M.set_stat(DEAD)
+ if(1) //on a z-level 1 turf.
+ M.health = 0
+ M.set_stat(DEAD)
+
+ //Now animate the cinematic
+ switch(station_missed)
+ if(1) //nuke was nearby but (mostly) missed
+ if( mode && !override )
+ override = mode.name
+ switch( override )
+ if("mercenary") //Nuke wasn't on station when it blew up
+ flick("intro_nuke",cinematic)
+ sleep(35)
+ world << sound('sound/effects/explosionfar.ogg')
+ flick("station_intact_fade_red",cinematic)
+ cinematic.icon_state = "summary_nukefail"
+ else
+ flick("intro_nuke",cinematic)
+ sleep(35)
+ world << sound('sound/effects/explosionfar.ogg')
+ //flick("end",cinematic)
+
+
+ if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
+ sleep(50)
+ world << sound('sound/effects/explosionfar.ogg')
+
+
+ else //station was destroyed
+ if( mode && !override )
+ override = mode.name
+ switch( override )
+ if("mercenary") //Nuke Ops successfully bombed the station
+ flick("intro_nuke",cinematic)
+ sleep(35)
+ flick("station_explode_fade_red",cinematic)
+ world << sound('sound/effects/explosionfar.ogg')
+ cinematic.icon_state = "summary_nukewin"
+ if("AI malfunction") //Malf (screen,explosion,summary)
+ flick("intro_malf",cinematic)
+ sleep(76)
+ flick("station_explode_fade_red",cinematic)
+ world << sound('sound/effects/explosionfar.ogg')
+ cinematic.icon_state = "summary_malf"
+ if("blob") //Station nuked (nuke,explosion,summary)
+ flick("intro_nuke",cinematic)
+ sleep(35)
+ flick("station_explode_fade_red",cinematic)
+ world << sound('sound/effects/explosionfar.ogg')
+ cinematic.icon_state = "summary_selfdes"
+ else //Station nuked (nuke,explosion,summary)
+ flick("intro_nuke",cinematic)
+ sleep(35)
+ flick("station_explode_fade_red", cinematic)
+ world << sound('sound/effects/explosionfar.ogg')
+ cinematic.icon_state = "summary_selfdes"
+ for(var/mob/living/M in living_mob_list)
+ if(M.loc.z in using_map.station_levels)
+ M.death()//No mercy
+ //If its actually the end of the round, wait for it to end.
+ //Otherwise if its a verb it will continue on afterwards.
+ sleep(300)
+
+ if(cinematic) qdel(cinematic) //end the cinematic
+ if(temp_buckle) qdel(temp_buckle) //release everybody
+ return
+
+
+/datum/controller/subsystem/ticker/proc/create_characters()
+ for(var/mob/new_player/player in player_list)
+ if(player && player.ready && player.mind)
+ if(player.mind.assigned_role=="AI")
+ player.close_spawn_windows()
+ player.AIize()
+ else if(!player.mind.assigned_role)
+ continue
+ else
+ player.create_character()
+ qdel(player)
+
+
+/datum/controller/subsystem/ticker/proc/collect_minds()
+ for(var/mob/living/player in player_list)
+ if(player.mind)
+ minds += player.mind
+
+
+/datum/controller/subsystem/ticker/proc/equip_characters()
+ var/captainless=1
+ for(var/mob/living/carbon/human/player in player_list)
+ if(player && player.mind && player.mind.assigned_role)
+ if(player.mind.assigned_role == "Colony Director")
+ captainless=0
+ if(!player_is_antag(player.mind, only_offstation_roles = 1))
+ job_master.EquipRank(player, player.mind.assigned_role, 0)
+ UpdateFactionList(player)
+ equip_custom_items(player)
+ player.apply_traits()
+ if(captainless)
+ for(var/mob/M in player_list)
+ if(!istype(M,/mob/new_player))
+ to_chat(M, "Colony Directorship not forced on anyone.")
+
+
+/datum/controller/subsystem/ticker/proc/declare_completion()
+ to_world("
A round of [mode.name] has ended!
")
+ for(var/mob/Player in player_list)
+ if(Player.mind && !isnewplayer(Player))
+ if(Player.stat != DEAD)
+ var/turf/playerTurf = get_turf(Player)
+ if(emergency_shuttle.departed && emergency_shuttle.evac)
+ if(isNotAdminLevel(playerTurf.z))
+ to_chat(Player, "You survived the round, but remained on [station_name()] as [Player.real_name].")
+ else
+ to_chat(Player, "You managed to survive the events on [station_name()] as [Player.real_name].")
+ else if(isAdminLevel(playerTurf.z))
+ to_chat(Player, "You successfully underwent crew transfer after events on [station_name()] as [Player.real_name].")
+ else if(issilicon(Player))
+ to_chat(Player, "You remain operational after the events on [station_name()] as [Player.real_name].")
+ else
+ to_chat(Player, "You missed the crew transfer after the events on [station_name()] as [Player.real_name].")
+ else
+ if(istype(Player,/mob/observer/dead))
+ var/mob/observer/dead/O = Player
+ if(!O.started_as_observer)
+ to_chat(Player, "You did not survive the events on [station_name()]...")
+ else
+ to_chat(Player, "You did not survive the events on [station_name()]...")
+ to_world("
")
+
+ for (var/mob/living/silicon/ai/aiPlayer in mob_list)
+ if (aiPlayer.stat != 2)
+ to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the round were:")
+ else
+ to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:")
+ aiPlayer.show_laws(1)
+
+ if (aiPlayer.connected_robots.len)
+ var/robolist = "The AI's loyal minions were: "
+ for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
+ robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]"
+ to_world("[robolist]")
+
+ var/dronecount = 0
+
+ for (var/mob/living/silicon/robot/robo in mob_list)
+
+ if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm))
+ dronecount++
+ continue
+
+ if (!robo.connected_ai)
+ if (robo.stat != 2)
+ to_world("[robo.name] (Played by: [robo.key]) survived as an AI-less stationbound synthetic! Its laws were:")
+ else
+ to_world("[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:")
+
+ if(robo) //How the hell do we lose robo between here and the world messages directly above this?
+ robo.laws.show_laws(world)
+
+ if(dronecount)
+ to_world("There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] at the end of this round.")
+
+ mode.declare_completion()//To declare normal completion.
+
+ //Ask the event manager to print round end information
+ SSevents.RoundEnd()
+
+ //Print a list of antagonists to the server log
+ var/list/total_antagonists = list()
+ //Look into all mobs in world, dead or alive
+ for(var/datum/mind/Mind in minds)
+ var/temprole = Mind.special_role
+ if(temprole) //if they are an antagonist of some sort.
+ if(temprole in total_antagonists) //If the role exists already, add the name to it
+ total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
+ else
+ total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
+ total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
+
+ //Now print them all into the log!
+ log_game("Antagonists at round end were...")
+ for(var/i in total_antagonists)
+ log_game("[i]s[total_antagonists[i]].")
+
+ return 1
+
+/datum/controller/subsystem/ticker/stat_entry()
+ switch(current_state)
+ if(GAME_STATE_INIT)
+ ..()
+ if(GAME_STATE_PREGAME) // RUNLEVEL_LOBBY
+ ..("START [round_progressing ? "[round(pregame_timeleft)]s" : "(PAUSED)"]")
+ if(GAME_STATE_SETTING_UP) // RUNLEVEL_SETUP
+ ..("SETUP")
+ if(GAME_STATE_PLAYING) // RUNLEVEL_GAME
+ ..("GAME")
+ if(GAME_STATE_FINISHED) // RUNLEVEL_POSTGAME
+ switch(end_game_state)
+ if(END_GAME_MODE_FINISHED)
+ ..("MODE OVER, WAITING")
+ if(END_GAME_READY_TO_END)
+ ..("ENDGAME PROCESSING")
+ if(END_GAME_ENDING)
+ ..("END IN [round(restart_timeleft/10)]s")
+ if(END_GAME_DELAYED)
+ ..("END PAUSED")
+ else
+ ..("ENDGAME ERROR:[end_game_state]")
+
+/datum/controller/subsystem/ticker/Recover()
+ flags |= SS_NO_INIT // Don't initialize again
+
+ current_state = SSticker.current_state
+ mode = SSticker.mode
+ pregame_timeleft = SSticker.pregame_timeleft
+
+ end_game_state = SSticker.end_game_state
+ delay_end = SSticker.delay_end
+ restart_timeleft = SSticker.restart_timeleft
+
+ minds = SSticker.minds
+
+ Bible_icon_state = SSticker.Bible_icon_state
+ Bible_item_state = SSticker.Bible_item_state
+ Bible_name = SSticker.Bible_name
+ Bible_deity_name = SSticker.Bible_deity_name
+ random_players = SSticker.random_players
diff --git a/code/datums/repositories/cameras.dm b/code/datums/repositories/cameras.dm
index 7bac2a4634..d516113394 100644
--- a/code/datums/repositories/cameras.dm
+++ b/code/datums/repositories/cameras.dm
@@ -14,10 +14,25 @@ var/global/datum/repository/cameras/camera_repository = new()
networks = list()
..()
-/datum/repository/cameras/proc/cameras_in_network(var/network)
+/datum/repository/cameras/proc/cameras_in_network(var/network, var/list/zlevels)
setup_cache()
var/list/network_list = networks[network]
- return network_list
+ if(LAZYLEN(zlevels))
+ var/list/filtered_cameras = list()
+ for(var/list/C in network_list)
+ //Camera is marked as always-visible
+ if(C["omni"])
+ filtered_cameras[++filtered_cameras.len] = C
+ continue
+ //Camera might be in an adjacent zlevel
+ var/camz = C["z"]
+ if(!camz) //It's inside something (helmet, communicator, etc) or nullspace or who knows
+ camz = get_z(locate(C["camera"]) in cameranet.cameras)
+ if(camz in zlevels)
+ filtered_cameras[++filtered_cameras.len] = C //Can't add lists to lists with +=
+ return filtered_cameras
+ else
+ return network_list
/datum/repository/cameras/proc/setup_cache()
if(!invalidated)
diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm
index 0b4aafda94..ec9327b2dc 100644
--- a/code/defines/procs/announce.dm
+++ b/code/defines/procs/announce.dm
@@ -30,7 +30,7 @@
title = "Security Announcement"
announcement_type = "Security Announcement"
-/datum/announcement/proc/Announce(var/message as text, var/new_title = "", var/new_sound = null, var/do_newscast = newscast, var/msg_sanitized = 0)
+/datum/announcement/proc/Announce(var/message as text, var/new_title = "", var/new_sound = null, var/do_newscast = newscast, var/msg_sanitized = 0, var/zlevel)
if(!message)
return
var/message_title = new_title ? new_title : title
@@ -40,13 +40,17 @@
message = sanitize(message, extra = 0)
message_title = sanitizeSafe(message_title)
- Message(message, message_title)
+ var/list/zlevels
+ if(zlevel)
+ zlevels = using_map.get_map_levels(zlevel, TRUE)
+
+ Message(message, message_title, zlevels)
if(do_newscast)
NewsCast(message, message_title)
- Sound(message_sound)
+ Sound(message_sound, zlevels)
Log(message, message_title)
-datum/announcement/proc/Message(message as text, message_title as text)
+datum/announcement/proc/Message(message as text, message_title as text, var/list/zlevels)
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player) && !isdeaf(M))
to_chat(M, "
[title]
")
@@ -54,6 +58,7 @@ datum/announcement/proc/Message(message as text, message_title as text)
if (announcer)
to_chat(M, " -[html_encode(announcer)]")
+// You'll need to update these to_world usages if you want to make these z-level specific ~Aro
datum/announcement/minor/Message(message as text, message_title as text)
to_world("[message]")
@@ -64,7 +69,7 @@ datum/announcement/priority/Message(message as text, message_title as text)
to_world(" -[html_encode(announcer)]")
to_world("
")
-datum/announcement/priority/command/Message(message as text, message_title as text)
+datum/announcement/priority/command/Message(message as text, message_title as text, var/list/zlevels)
var/command
command += "[command_name()] Update
"
if (message_title)
@@ -73,6 +78,8 @@ datum/announcement/priority/command/Message(message as text, message_title as te
command += "
[message]
"
command += "
"
for(var/mob/M in player_list)
+ if(zlevels && !(get_z(M) in zlevels))
+ continue
if(!istype(M,/mob/new_player) && !isdeaf(M))
to_chat(M, command)
@@ -92,15 +99,18 @@ datum/announcement/proc/NewsCast(message as text, message_title as text)
news.can_be_redacted = 0
announce_newscaster_news(news)
-datum/announcement/proc/PlaySound(var/message_sound)
+datum/announcement/proc/PlaySound(var/message_sound, var/list/zlevels)
if(!message_sound)
return
+
for(var/mob/M in player_list)
+ if(zlevels && !(M.z in zlevels))
+ continue
if(!istype(M,/mob/new_player) && !isdeaf(M))
M << message_sound
-datum/announcement/proc/Sound(var/message_sound)
- PlaySound(message_sound)
+datum/announcement/proc/Sound(var/message_sound, var/list/zlevels)
+ PlaySound(message_sound, zlevels)
datum/announcement/priority/Sound(var/message_sound)
if(message_sound)
@@ -124,12 +134,12 @@ datum/announcement/proc/Log(message as text, message_title as text)
/proc/ion_storm_announcement()
command_announcement.Announce("It has come to our attention that \the [station_name()] passed through an ion storm. Please monitor all electronic equipment for malfunctions.", "Anomaly Alert")
-/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message)
+/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message, var/channel = "Common", var/zlevel)
if (ticker.current_state == GAME_STATE_PLAYING)
+ var/list/zlevels = zlevel ? using_map.get_map_levels(zlevel, TRUE) : null
if(character.mind.role_alt_title)
rank = character.mind.role_alt_title
- AnnounceArrivalSimple(character.real_name, rank, join_message)
+ AnnounceArrivalSimple(character.real_name, rank, join_message, channel, zlevels)
-
-/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive to the station shortly by shuttle", new_sound = 'sound/misc/notice3.ogg')
- global_announcer.autosay("[name], [rank], [join_message].", "Arrivals Announcement Computer")
+/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive at the station shortly", var/channel = "Common", var/list/zlevels)
+ global_announcer.autosay("[name], [rank], [join_message].", "Arrivals Announcement Computer", channel, zlevels)
\ No newline at end of file
diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm
index 9e01eb8108..3d76b02e0f 100644
--- a/code/game/machinery/air_alarm.dm
+++ b/code/game/machinery/air_alarm.dm
@@ -383,7 +383,7 @@
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = command
@@ -445,7 +445,7 @@
var/datum/signal/alert_signal = new
alert_signal.source = src
- alert_signal.transmission_method = 1
+ alert_signal.transmission_method = TRANSMISSION_RADIO
alert_signal.data["zone"] = alarm_area.name
alert_signal.data["type"] = "Atmospheric"
diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm
index ccab21e889..5ebcfeef43 100644
--- a/code/game/machinery/atmo_control.dm
+++ b/code/game/machinery/atmo_control.dm
@@ -29,7 +29,7 @@
/obj/machinery/air_sensor/process()
if(on)
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.data["tag"] = id_tag
signal.data["timestamp"] = world.time
@@ -212,7 +212,7 @@ obj/machinery/computer/general_air_control/Destroy()
if(!radio_connection)
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
if(href_list["in_refresh_status"])
input_info = null
@@ -322,7 +322,7 @@ obj/machinery/computer/general_air_control/Destroy()
if(!radio_connection)
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
if(href_list["in_refresh_status"])
input_info = null
@@ -383,7 +383,7 @@ obj/machinery/computer/general_air_control/Destroy()
injecting = 1
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
@@ -445,7 +445,7 @@ obj/machinery/computer/general_air_control/Destroy()
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
"tag" = device_tag,
@@ -463,7 +463,7 @@ obj/machinery/computer/general_air_control/Destroy()
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
"tag" = device_tag,
@@ -478,7 +478,7 @@ obj/machinery/computer/general_air_control/Destroy()
return 0
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.source = src
signal.data = list(
"tag" = device_tag,
diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm
index 84b18c31ea..82ce3d66aa 100644
--- a/code/game/machinery/atmoalter/meter.dm
+++ b/code/game/machinery/atmoalter/meter.dm
@@ -67,7 +67,7 @@
var/datum/signal/signal = new
signal.source = src
- signal.transmission_method = 1
+ signal.transmission_method = TRANSMISSION_RADIO
signal.data = list(
"tag" = id,
"device" = "AM",
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 9fc72b0a17..27e7b5a7bd 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -33,6 +33,7 @@
var/busy = 0
var/on_open_network = 0
+ var/always_visible = FALSE //Visable from any map, good for entertainment network cameras
var/affected_by_emp_until = 0
@@ -467,6 +468,7 @@
cam["name"] = sanitize(c_tag)
cam["deact"] = !can_use()
cam["camera"] = "\ref[src]"
+ cam["omni"] = always_visible
cam["x"] = x
cam["y"] = y
cam["z"] = z
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 66ad38cf36..15b5f7abca 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -102,6 +102,7 @@ var/global/list/engineering_networks = list(
/obj/machinery/camera/network/thunder
network = list(NETWORK_THUNDER)
invuln = 1
+ always_visible = TRUE
// EMP
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index f1ba39c7b2..11d1660746 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -43,11 +43,14 @@
data["current_camera"] = current_camera ? current_camera.nano_structure() : null
data["current_network"] = current_network
data["networks"] = network ? network : list()
+
+ var/map_levels = using_map.get_map_levels(src.z, TRUE)
+ data["map_levels"] = map_levels
+
if(current_network)
- data["cameras"] = camera_repository.cameras_in_network(current_network)
+ data["cameras"] = camera_repository.cameras_in_network(current_network, map_levels)
if(current_camera)
switch_to_camera(user, current_camera)
- data["map_levels"] = using_map.get_map_levels(src.z)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
@@ -91,9 +94,6 @@
. = ..()
/obj/machinery/computer/security/attack_hand(var/mob/user as mob)
- if (using_map && !(src.z in using_map.contact_levels))
- to_chat(user, "Unable to establish a connection: You're too far away from the station!")
- return
if(stat & (NOPOWER|BROKEN)) return
if(!isAI(user))
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 6d25dd0b5c..a0f6e13385 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -544,7 +544,7 @@
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
switch(command)
diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm
index 04f0c61c52..1d58a61df1 100644
--- a/code/game/machinery/computer/prisonshuttle.dm
+++ b/code/game/machinery/computer/prisonshuttle.dm
@@ -121,7 +121,7 @@ var/prison_shuttle_timeleft = 0
if(!frequency) return
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
frequency.post_signal(src, status_signal)
return
diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm
index 05b4e65292..a310b33f78 100644
--- a/code/game/machinery/computer/supply.dm
+++ b/code/game/machinery/computer/supply.dm
@@ -421,7 +421,7 @@
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
frequency.post_signal(src, status_signal)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index cad7e07c24..aa21a2d1d5 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -215,6 +215,7 @@
var/on_enter_occupant_message = "You feel cool air surround you. You go numb as your senses turn inward."
var/on_store_visible_message_1 = "hums and hisses as it moves" //We need two variables because byond doesn't let us have variables inside strings at compile-time.
var/on_store_visible_message_2 = "into storage."
+ var/announce_channel = "Common"
var/allow_occupant_types = list(/mob/living/carbon/human)
var/disallow_occupant_types = list()
@@ -467,7 +468,7 @@
control_computer._admin_logs += "[key_name(to_despawn)] ([to_despawn.mind.role_alt_title]) at [stationtime2text()]"
log_and_message_admins("[key_name(to_despawn)] ([to_despawn.mind.role_alt_title]) entered cryostorage.")
- announce.autosay("[to_despawn.real_name], [to_despawn.mind.role_alt_title], [on_store_message]", "[on_store_name]")
+ announce.autosay("[to_despawn.real_name], [to_despawn.mind.role_alt_title], [on_store_message]", "[on_store_name]", announce_channel, using_map.get_map_levels(z, TRUE))
//visible_message("\The [initial(name)] hums and hisses as it moves [to_despawn.real_name] into storage.", 3)
visible_message("\The [initial(name)] [on_store_visible_message_1] [to_despawn.real_name] [on_store_visible_message_2].", 3)
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index e5ff8bab16..ca7092708d 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -91,7 +91,7 @@ obj/machinery/door/airlock/proc/command_completed(var/command)
obj/machinery/door/airlock/proc/send_status(var/bumped = 0)
if(radio_connection)
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.data["tag"] = id_tag
signal.data["timestamp"] = world.time
@@ -172,7 +172,7 @@ obj/machinery/airlock_sensor/update_icon()
obj/machinery/airlock_sensor/attack_hand(mob/user)
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.data["tag"] = master_tag
signal.data["command"] = command
@@ -186,7 +186,7 @@ obj/machinery/airlock_sensor/process()
if(abs(pressure - previousPressure) > 0.001 || previousPressure == null)
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.data["tag"] = id_tag
signal.data["timestamp"] = world.time
signal.data["pressure"] = num2text(pressure)
@@ -263,7 +263,7 @@ obj/machinery/access_button/attack_hand(mob/user)
else if(radio_connection)
var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
+ signal.transmission_method = TRANSMISSION_RADIO //radio signal
signal.data["tag"] = master_tag
signal.data["command"] = command
diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm
index dc8e06e508..26a7936e1c 100644
--- a/code/game/machinery/magnet.dm
+++ b/code/game/machinery/magnet.dm
@@ -275,7 +275,7 @@
// Prepare signal beforehand, because this is a radio operation
var/datum/signal/signal = new
- signal.transmission_method = 1 // radio transmission
+ signal.transmission_method = TRANSMISSION_RADIO // radio transmission
signal.source = src
signal.frequency = frequency
signal.data["code"] = code
@@ -341,7 +341,7 @@
// Prepare the radio signal
var/datum/signal/signal = new
- signal.transmission_method = 1 // radio transmission
+ signal.transmission_method = TRANSMISSION_RADIO // radio transmission
signal.source = src
signal.frequency = frequency
signal.data["code"] = code
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index f22bf58a9f..8a4c88b51a 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -23,6 +23,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
produces_heat = 0
delay = 7
circuit = /obj/item/weapon/circuitboard/telecomms/broadcaster
+ //Vars only used if you're using the overmap
+ var/overmap_range = 0
+ var/overmap_range_min = 0
+ var/overmap_range_max = 5
+ //Linked bluespace radios
+ var/list/linked_radios_weakrefs = list()
/obj/machinery/telecomms/processor/Initialize()
. = ..()
@@ -34,6 +40,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
component_parts += new /obj/item/weapon/stock_parts/micro_laser/high(src)
component_parts += new /obj/item/stack/cable_coil(src, 1)
+/obj/machinery/telecomms/broadcaster/proc/link_radio(var/obj/item/device/radio/R)
+ if(!istype(R))
+ return
+ linked_radios_weakrefs |= weakref(R)
+
/obj/machinery/telecomms/broadcaster/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
// Don't broadcast rejected signals
if(signal.data["reject"])
@@ -58,46 +69,50 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
if(signal.data["slow"] > 0)
sleep(signal.data["slow"]) // simulate the network lag if necessary
- signal.data["level"] |= listening_level
+ signal.data["level"] |= using_map.get_map_levels(listening_level, TRUE, overmap_range)
+
+ var/list/forced_radios
+ for(var/weakref/wr in linked_radios_weakrefs)
+ var/obj/item/device/radio/R = wr.resolve()
+ if(istype(R))
+ LAZYDISTINCTADD(forced_radios, R)
/** #### - Normal Broadcast - #### **/
-
- if(signal.data["type"] == 0)
-
+ if(signal.data["type"] == SIGNAL_NORMAL)
/* ###### Broadcast a message using signal.data ###### */
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"],,
+ signal.data["realname"], signal.data["vname"], DATA_NORMAL,
signal.data["compression"], signal.data["level"], signal.frequency,
- signal.data["verb"])
-
+ signal.data["verb"], forced_radios)
/** #### - Simple Broadcast - #### **/
- if(signal.data["type"] == 1)
+ if(signal.data["type"] == SIGNAL_SIMPLE)
/* ###### Broadcast a message using signal.data ###### */
Broadcast_SimpleMessage(signal.data["name"], signal.frequency,
- signal.data["message"],null, null,
- signal.data["compression"], listening_level)
+ signal.data["message"], DATA_NORMAL, null,
+ signal.data["compression"], listening_level, forced_radios)
/** #### - Artificial Broadcast - #### **/
// (Imitates a mob)
- if(signal.data["type"] == 2)
+ if(signal.data["type"] == SIGNAL_FAKE)
/* ###### Broadcast a message using signal.data ###### */
- // Parameter "data" as 4: AI can't track this person/mob
+ // Parameter "data" as DATA_FAKE: AI can't track this person/mob
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency,
- signal.data["verb"])
+ signal.data["realname"], signal.data["vname"], DATA_FAKE,
+ signal.data["compression"], signal.data["level"], signal.frequency,
+ signal.data["verb"], forced_radios)
if(!message_delay)
message_delay = 1
@@ -118,6 +133,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/*
Basically just an empty shell for receiving and broadcasting radio messages. Not
very flexible, but it gets the job done.
+ NOTE: This AIO device listens on *every* zlevel (it does not even check)
*/
/obj/machinery/telecomms/allinone
@@ -126,15 +142,98 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
icon_state = "comm_server"
desc = "A compact machine used for portable subspace telecommuniations processing."
density = 1
+ use_power = USE_POWER_IDLE
+ idle_power_usage = 20
anchored = 1
- use_power = USE_POWER_OFF
- idle_power_usage = 0
machinetype = 6
produces_heat = 0
var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel
+ var/overmap_range = 0 //Same turf
+
+ var/list/linked_radios_weakrefs = list()
+
+/obj/machinery/telecomms/allinone/proc/link_radio(var/obj/item/device/radio/R)
+ if(!istype(R))
+ return
+ linked_radios_weakrefs |= weakref(R)
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal)
+ // Has to be on to receive messages
+ if(!on)
+ return
+
+ // Why did you use this subtype?
+ if(!using_map.use_overmap)
+ return
+
+ // Someone else handling it?
+ if(signal.data["done"])
+ return
+
+ // Where are we able to hear from (and talk to, since we're AIO) anyway?
+ var/map_levels = using_map.get_map_levels(z, TRUE, overmap_range)
+
+ //Bluespace can skip this check
+ if(signal.transmission_method != TRANSMISSION_BLUESPACE)
+ var/list/signal_levels = list()
+ signal_levels += signal.data["level"] //If it's text/number, it'll be the only entry, if it's a list, it'll get combined
+ var/list/overlap = map_levels & signal_levels //Returns a list of similar levels
+ if(!overlap.len)
+ return
+
+ if(is_freq_listening(signal)) // detect subspace signals
+
+ signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster
+ signal.data["compression"] = 0 // decompress since we're a processor
+
+ // Search for the original signal and mark it as done as well
+ var/datum/signal/original = signal.data["original"]
+ if(original)
+ original.data["done"] = 1
+
+ // For some reason level is both used as a list and not a list, and now it needs to be a list.
+ signal.data["level"] = map_levels
+
+ if(signal.data["slow"] > 0)
+ sleep(signal.data["slow"]) // simulate the network lag if necessary
+
+ /* ###### Broadcast a message using signal.data ###### */
+
+ var/datum/radio_frequency/connection = signal.data["connection"]
+
+ var/list/forced_radios
+ for(var/weakref/wr in linked_radios_weakrefs)
+ var/obj/item/device/radio/R = wr.resolve()
+ if(istype(R))
+ LAZYDISTINCTADD(forced_radios, R)
+
+ Broadcast_Message(
+ signal.data["connection"],
+ signal.data["mob"],
+ signal.data["vmask"],
+ signal.data["vmessage"],
+ signal.data["radio"],
+ signal.data["message"],
+ signal.data["name"],
+ signal.data["job"],
+ signal.data["realname"],
+ signal.data["vname"],
+ DATA_NORMAL,
+ signal.data["compression"],
+ signal.data["level"],
+ connection.frequency,
+ signal.data["verb"],
+ signal.data["language"],
+ forced_radios
+ )
+
+//Antag version with unlimited range (doesn't even check) and uses no power, to enable antag comms to work anywhere.
+/obj/machinery/telecomms/allinone/antag
+ use_power = USE_POWER_OFF
+ idle_power_usage = 0
+
+/obj/machinery/telecomms/allinone/antag/receive_signal(datum/signal/signal)
if(!on) // has to be on to receive messages
return
@@ -159,23 +258,29 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/datum/radio_frequency/connection = signal.data["connection"]
+ var/list/forced_radios
+ for(var/weakref/wr in linked_radios_weakrefs)
+ var/obj/item/device/radio/R = wr.resolve()
+ if(istype(R))
+ LAZYDISTINCTADD(forced_radios, R)
+
if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency,
- signal.data["verb"])
+ signal.data["realname"], signal.data["vname"], DATA_NORMAL,
+ signal.data["compression"], list(0), connection.frequency,
+ signal.data["verb"], forced_radios)
else
if(intercept)
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency,
- signal.data["verb"])
-
-
+ signal.data["realname"], signal.data["vname"], DATA_ANTAG,
+ signal.data["compression"], list(0), connection.frequency,
+ signal.data["verb"], forced_radios)
/**
@@ -237,8 +342,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M,
var/vmask, var/list/vmessage_pieces, var/obj/item/device/radio/radio,
var/list/message_pieces, var/name, var/job, var/realname, var/vname,
- var/data, var/compression, var/list/level, var/freq, var/verbage = "says")
-
+ var/data, var/compression, var/list/level, var/freq, var/verbage = "says",
+ var/list/forced_radios)
/* ###### Prepare the radio connection ###### */
@@ -246,17 +351,22 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/list/obj/item/device/radio/radios = list()
+ for(var/obj/item/device/radio/R in forced_radios)
+ //Cursory check to ensure they are 'on' and stuff
+ if(R.receive_range(display_freq, list(0)))
+ radios |= R
+
// --- Broadcast only to intercom devices ---
- if(data == 1)
+ if(data == DATA_INTERCOM)
for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"])
if(R.receive_range(display_freq, level) > -1)
- radios += R
+ radios |= R
// --- Broadcast only to intercoms and station-bounced radios ---
- else if(data == 2)
+ else if(data == DATA_LOCAL)
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
@@ -264,16 +374,16 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
continue
if(R.receive_range(display_freq, level) > -1)
- radios += R
+ radios |= R
// --- Broadcast to antag radios! ---
- else if(data == 3)
+ else if(data == DATA_ANTAG)
for(var/antag_freq in ANTAG_FREQS)
var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(antag_freq)
for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
if(R.receive_range(antag_freq, level) > -1)
- radios += R
+ radios |= R
// --- Broadcast to ALL radio devices ---
@@ -281,7 +391,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
if(R.receive_range(display_freq, level) > -1)
- radios += R
+ radios |= R
// Get a list of mobs who can hear from the radios we collected.
var/list/receive = get_mobs_in_radio_ranges(radios)
@@ -307,7 +417,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
continue
// Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates
- if(data == 3 && istype(R, /mob/observer/dead) && R.is_preference_enabled(/datum/client_preference/ghost_radio))
+ if(data == DATA_ANTAG && istype(R, /mob/observer/dead) && R.is_preference_enabled(/datum/client_preference/ghost_radio))
continue
// --- Check for compression ---
@@ -346,7 +456,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/freq_text = get_frequency_name(display_freq)
var/part_b_extra = ""
- if(data == 3) // intercepted radio message
+ if(data == DATA_ANTAG) // intercepted radio message
part_b_extra = " (Intercepted)"
var/part_a = "[bicon(radio)]\[[freq_text]\][part_b_extra] " // goes in the actual output
@@ -429,7 +539,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
return 1
-/proc/Broadcast_SimpleMessage(var/source, var/frequency, list/message_pieces, var/data, var/mob/M, var/compression, var/level)
+/proc/Broadcast_SimpleMessage(var/source, var/frequency, list/message_pieces, var/data, var/mob/M, var/compression, var/level, var/list/forced_radios)
var/text = multilingual_to_message(message_pieces)
/* ###### Prepare the radio connection ###### */
@@ -443,10 +553,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/list/receive = list()
+ for(var/obj/item/device/radio/R in forced_radios)
+ receive |= R.send_hear(display_freq)
// --- Broadcast only to intercom devices ---
- if(data == 1)
+ if(data == DATA_INTERCOM)
for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"])
var/turf/position = get_turf(R)
if(position && position.z == level)
@@ -455,7 +567,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
// --- Broadcast only to intercoms and station-bounced radios ---
- else if(data == 2)
+ else if(data == DATA_LOCAL)
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
if(istype(R, /obj/item/device/radio/headset))
@@ -467,7 +579,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
// --- Broadcast to antag radios! ---
- else if(data == 3)
+ else if(data == DATA_ANTAG)
for(var/freq in ANTAG_FREQS)
var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(freq)
for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
@@ -532,7 +644,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
// --- Some more pre-message formatting ---
var/part_b_extra = ""
- if(data == 3) // intercepted radio message
+ if(data == DATA_ANTAG) // intercepted radio message
part_b_extra = " (Intercepted)"
// Create a radio headset for the sole purpose of using its icon
@@ -608,15 +720,15 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/atom/proc/test_telecomms()
var/datum/signal/signal = src.telecomms_process()
- var/turf/position = get_turf(src)
- return (position.z in signal.data["level"] && signal.data["done"])
+ var/pos_z = get_z(src)
+ return (pos_z in signal.data["level"] && signal.data["done"])
/atom/proc/telecomms_process(var/do_sleep = 1)
// First, we want to generate a new radio signal
var/datum/signal/signal = new
- signal.transmission_method = 2 // 2 would be a subspace transmission.
- var/turf/pos = get_turf(src)
+ signal.transmission_method = TRANSMISSION_SUBSPACE
+ var/pos_z = get_z(src)
// --- Finally, tag the actual signal with the appropriate values ---
signal.data = list(
@@ -624,10 +736,10 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
"message" = "TEST",
"compression" = rand(45, 50), // If the signal is compressed, compress our message too.
"traffic" = 0, // dictates the total traffic sum that the signal went through
- "type" = 4, // determines what type of radio input it is: test broadcast
+ "type" = SIGNAL_TEST, // determines what type of radio input it is: test broadcast
"reject" = 0,
"done" = 0,
- "level" = pos.z // The level it is being broadcasted at.
+ "level" = pos_z // The level it is being broadcasted at.
)
signal.frequency = PUB_FREQ// Common channel
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index 3e3d89643a..802c75bb42 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -219,6 +219,38 @@
temp = "-% Frequency changing deactivated %-"
+// BROADCASTER
+/obj/machinery/telecomms/broadcaster/Options_Menu()
+ // Note the machine 'displays' 1 higher than overmap_range to save users from the abstraction that range '0' is valid and everything on the same turf.
+ var/dat = "
Broadcast Range (affects power usage)
- [overmap_range+1] gigameter\s +"
+ return dat
+
+/obj/machinery/telecomms/broadcaster/Options_Topic(href, href_list)
+ if(href_list["range_down"])
+ if(overmap_range > overmap_range_min)
+ overmap_range--
+ idle_power_usage = initial(idle_power_usage)**(overmap_range+1)
+ if(href_list["range_up"])
+ if(overmap_range < overmap_range_max)
+ overmap_range++
+ idle_power_usage = initial(idle_power_usage)**(overmap_range+1)
+
+// RECEIVER
+/obj/machinery/telecomms/receiver/Options_Menu()
+ // Note the machine 'displays' 1 higher than overmap_range to save users from the abstraction that range '0' is valid and everything on the same turf.
+ var/dat = "
Receive Range (affects power usage)
- [overmap_range+1] gigameter\s +"
+ return dat
+
+/obj/machinery/telecomms/receiver/Options_Topic(href, href_list)
+ if(href_list["range_down"])
+ if(overmap_range > overmap_range_min)
+ overmap_range--
+ idle_power_usage = initial(idle_power_usage)**(overmap_range+1)
+ if(href_list["range_up"])
+ if(overmap_range < overmap_range_max)
+ overmap_range++
+ idle_power_usage = initial(idle_power_usage)**(overmap_range+1)
+
/obj/machinery/telecomms/Topic(href, href_list)
if(!issilicon(usr))
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index ff073359de..55a06fd234 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -68,7 +68,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/datum/signal/copy
if(copysig)
copy = new
- copy.transmission_method = 2
+ copy.transmission_method = TRANSMISSION_SUBSPACE
copy.frequency = signal.frequency
copy.data = signal.data.Copy()
@@ -141,9 +141,9 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
// Used in auto linking
/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T)
- var/turf/position = get_turf(src)
- var/turf/T_position = get_turf(T)
- if((position.z == T_position.z) || (src.long_range_link && T.long_range_link))
+ var/pos_z = get_z(src)
+ var/tpos_z = get_z(T)
+ if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link))
for(var/x in autolinkers)
if(T.autolinkers.Find(x))
if(src != T)
@@ -256,6 +256,12 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
machinetype = 1
produces_heat = 0
circuit = /obj/item/weapon/circuitboard/telecomms/receiver
+ //Vars only used if you're using the overmap
+ var/overmap_range = 0
+ var/overmap_range_min = 0
+ var/overmap_range_max = 5
+
+ var/list/linked_radios_weakrefs = list()
/obj/machinery/telecomms/receiver/Initialize()
. = ..()
@@ -267,8 +273,12 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
RefreshParts()
-/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal)
+/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R)
+ if(!istype(R))
+ return
+ linked_radios_weakrefs |= weakref(R)
+/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal)
if(!on) // has to be on to receive messages
return
if(!signal)
@@ -276,7 +286,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
if(!check_receive_level(signal))
return
- if(signal.transmission_method == 2)
+ if(signal.transmission_method == TRANSMISSION_SUBSPACE)
if(is_freq_listening(signal)) // detect subspace signals
@@ -288,14 +298,31 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
relay_information(signal, "/obj/machinery/telecomms/bus") // Send it to a bus instead, if it's linked to one
/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal)
+ // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally
+ if(signal.transmission_method == TRANSMISSION_BLUESPACE)
+ var/obj/item/device/radio/R = signal.data["radio"]
- if(signal.data["level"] != listening_level)
+ //Who're you?
+ if(!(weakref(R) in linked_radios_weakrefs))
+ signal.data["reject"] = 1
+ return 0
+
+ //We'll resend this for you
+ signal.data["level"] = z
+ signal.transmission_method = TRANSMISSION_SUBSPACE
+ return 1
+
+ //Where can we hear?
+ var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range)
+
+ // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it
+ if(!(signal.data["level"] in listening_levels))
for(var/obj/machinery/telecomms/hub/H in links)
- var/list/connected_levels = list()
+ var/list/relayed_levels = list()
for(var/obj/machinery/telecomms/relay/R in H.links)
if(R.can_receive(signal))
- connected_levels |= R.listening_level
- if(signal.data["level"] in connected_levels)
+ relayed_levels |= R.listening_level
+ if(signal.data["level"] in relayed_levels)
return 1
return 0
return 1
@@ -405,7 +432,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
// Add our level and send it back
if(can_send(signal))
- signal.data["level"] |= listening_level
+ signal.data["level"] |= using_map.get_map_levels(listening_level)
// Checks to see if it can send/receive.
@@ -601,7 +628,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
totaltraffic += traffic // add current traffic to total traffic
//Is this a test signal? Bypass logging
- if(signal.data["type"] != 4)
+ if(signal.data["type"] != SIGNAL_TEST)
// If signal has a message and appropriate frequency
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 2a662ea5ff..1ca1ae6506 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -256,7 +256,7 @@ var/list/civilian_cartridges = list(
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
switch(command)
diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm
index 56ec64d8a7..133798e4b1 100644
--- a/code/game/objects/items/devices/PDA/radio.dm
+++ b/code/game/objects/items/devices/PDA/radio.dm
@@ -22,7 +22,7 @@
var/datum/signal/signal = new()
signal.source = src
- signal.transmission_method = 1
+ signal.transmission_method = TRANSMISSION_RADIO
signal.data[key] = value
if(key2)
signal.data[key2] = value2
diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm
index b1c3bd41f2..120df593e9 100644
--- a/code/game/objects/items/devices/communicator/cartridge.dm
+++ b/code/game/objects/items/devices/communicator/cartridge.dm
@@ -329,7 +329,7 @@
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
switch(command)
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index 0cac902796..562ed56990 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -83,7 +83,7 @@ var/list/GPS_list = list()
dat["curr_z_name"] = using_map.get_zlevel_name(curr.z)
dat["gps_list"] = list()
dat["z_level_detection"] = using_map.get_map_levels(curr.z, long_range)
-
+
for(var/obj/item/device/gps/G in GPS_list - src)
if(!G.tracking || G.emped || G.hide_signal)
continue
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 80a2b5e4a8..587c5b3551 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -49,6 +49,13 @@ var/global/list/default_medbay_channels = list(
w_class = ITEMSIZE_SMALL
show_messages = 1
+ // Bluespace radios talk directly to telecomms equipment
+ var/bluespace_radio = FALSE
+ var/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO
+ // For mappers or subtypes, to start them prelinked to these devices
+ var/bs_tx_preload_id
+ var/bs_rx_preload_id
+
matter = list("glass" = 25,DEFAULT_WALL_MATERIAL = 75)
var/const/FREQ_LISTENING = 1
var/list/internal_channels
@@ -87,6 +94,43 @@ var/global/list/default_medbay_channels = list(
for (var/ch_name in channels)
secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT)
+ if(bluespace_radio)
+ if(bs_tx_preload_id)
+ //Try to find a receiver
+ for(var/obj/machinery/telecomms/receiver/RX in telecomms_list)
+ if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver.
+ bs_tx_weakref = weakref(RX)
+ RX.link_radio(src)
+ break
+ //Hmm, howabout an AIO machine
+ if(!bs_tx_weakref)
+ for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list)
+ if(AIO.id == bs_tx_preload_id)
+ bs_tx_weakref = weakref(AIO)
+ AIO.link_radio(src)
+ break
+ if(!bs_tx_weakref)
+ testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.")
+
+ if(bs_rx_preload_id)
+ var/found = 0
+ //Try to find a transmitter
+ for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list)
+ if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter.
+ TX.link_radio(src)
+ found = 1
+ break
+ //Hmm, howabout an AIO machine
+ if(!found)
+ for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list)
+ if(AIO.id == bs_rx_preload_id)
+ AIO.link_radio(src)
+ found = 1
+ break
+ if(!found)
+ testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.")
+
+
/obj/item/device/radio/attack_self(mob/user as mob)
user.set_machine(src)
interact(user)
@@ -237,11 +281,10 @@ var/global/list/default_medbay_channels = list(
if(.)
SSnanoui.update_uis(src)
-/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel) //BS12 EDIT
+/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels) //BS12 EDIT
var/datum/radio_frequency/connection = null
if(channel && channels && channels.len > 0)
if(channel == "department")
- //to_world("DEBUG: channel=\"[channel]\" switching to \"[channels[1]]\"")
channel = channels[1]
connection = secure_radio_connections[channel]
else
@@ -250,12 +293,15 @@ var/global/list/default_medbay_channels = list(
if(!istype(connection))
return
+ if(!LAZYLEN(zlevels))
+ zlevels = list(0)
+
var/static/mob/living/silicon/ai/announcer/A = new /mob/living/silicon/ai/announcer(src, null, null, 1)
A.SetName(from)
Broadcast_Message(connection, A,
0, "*garbled automated announcement*", src,
message_to_multilingual(message), from, "Automated Announcement", from, "synthesized voice",
- 4, 0, list(0), connection.frequency, "states")
+ DATA_FAKE, 0, zlevels, connection.frequency, "states")
// Interprets the message mode when talking into a radio, possibly returning a connection datum
/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode)
@@ -313,8 +359,8 @@ var/global/list/default_medbay_channels = list(
if(!istype(message_mode, /datum/radio_frequency)) //if not a special case, it should be returning a radio connection
return FALSE
+ var/pos_z = get_z(src)
var/datum/radio_frequency/connection = message_mode
- var/turf/position = get_turf(src)
//#### Tagging the signal with all appropriate identity values ####//
@@ -363,90 +409,12 @@ var/global/list/default_medbay_channels = list(
jobname = "Unknown"
voicemask = 1
-
-
- /* ###### Radio headsets can only broadcast through subspace ###### */
-
- if(subspace_transmission)
- var/list/jamming = is_jammed(src)
- if(jamming)
- var/distance = jamming["distance"]
- to_chat(M, "[bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.")
- return FALSE
-
- // First, we want to generate a new radio signal
- var/datum/signal/signal = new
- signal.transmission_method = 2 // 2 would be a subspace transmission.
- // transmission_method could probably be enumerated through #define. Would be neater.
-
- // --- Finally, tag the actual signal with the appropriate values ---
- signal.data = list(
- // Identity-associated tags:
- "mob" = M, // store a reference to the mob
- "mobtype" = M.type, // the mob's type
- "realname" = real_name, // the mob's real name
- "name" = displayname, // the mob's display name
- "job" = jobname, // the mob's job
- "key" = mobkey, // the mob's key
- "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood
- "vname" = M.voice_name, // the name to display if the voice wasn't understood
- "vmask" = voicemask, // 1 if the mob is using a voice gas mask
-
- // We store things that would otherwise be kept in the actual mob
- // so that they can be logged even AFTER the mob is deleted or something
-
- // Other tags:
- "compression" = rand(45,50), // compressed radio signal
- "message" = message_pieces, // the actual sent message
- "connection" = connection, // the radio connection to use
- "radio" = src, // stores the radio used for transmission
- "slow" = 0, // how much to sleep() before broadcasting - simulates net lag
- "traffic" = 0, // dictates the total traffic sum that the signal went through
- "type" = 0, // determines what type of radio input it is: normal broadcast
- "server" = null, // the last server to log this signal
- "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery
- "level" = position.z, // The source's z level
- "verb" = verb
- )
- signal.frequency = connection.frequency // Quick frequency set
-
- //#### Sending the signal to all subspace receivers ####//
-
- for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
- R.receive_signal(signal)
-
- // Allinone can act as receivers.
- for(var/obj/machinery/telecomms/allinone/R in telecomms_list)
- R.receive_signal(signal)
-
- // Receiving code can be located in Telecommunications.dm
- if(signal.data["done"] && position.z in signal.data["level"])
- return TRUE //Huzzah, sent via subspace
-
- else if(adhoc_fallback) //Less huzzah, we have to fallback
- to_chat(loc, "\The [src] pings as it falls back to local radio transmission.")
- subspace_transmission = FALSE
- return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
- src, message_pieces, displayname, jobname, real_name, M.voice_name,
- signal.transmission_method, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb)
-
- /* ###### Intercoms and station-bounced radios ###### */
-
- var/filter_type = 2
-
- /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */
- if(istype(src, /obj/item/device/radio/intercom))
- filter_type = 1
-
-
+ // First, we want to generate a new radio signal
var/datum/signal/signal = new
- signal.transmission_method = 2
-
-
- /* --- Try to send a normal subspace broadcast first */
+ // --- Finally, tag the actual signal with the appropriate values ---
signal.data = list(
-
+ // Identity-associated tags:
"mob" = M, // store a reference to the mob
"mobtype" = M.type, // the mob's type
"realname" = real_name, // the mob's real name
@@ -455,40 +423,107 @@ var/global/list/default_medbay_channels = list(
"key" = mobkey, // the mob's key
"vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood
"vname" = M.voice_name, // the name to display if the voice wasn't understood
- "vmask" = voicemask, // 1 if the mob is using a voice gas mas
+ "vmask" = voicemask, // 1 if the mob is using a voice gas mask
- "compression" = 0, // uncompressed radio signal
+ // We store things that would otherwise be kept in the actual mob
+ // so that they can be logged even AFTER the mob is deleted or something
+
+ // Other tags:
+ "compression" = rand(45,50), // compressed radio signal
"message" = message_pieces, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
- "slow" = 0,
- "traffic" = 0,
- "type" = 0,
- "server" = null,
- "reject" = 0,
- "level" = position.z,
+ "slow" = 0, // how much to sleep() before broadcasting - simulates net lag
+ "traffic" = 0, // dictates the total traffic sum that the signal went through
+ "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast
+ "server" = null, // the last server to log this signal
+ "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery
+ "level" = pos_z, // The source's z level
"verb" = verb
)
signal.frequency = connection.frequency // Quick frequency set
+ var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var.
+
+ /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */
+ if(bluespace_radio)
+ //Nothing to transmit to
+ if(!bs_tx_weakref)
+ to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.")
+ return FALSE
+
+ var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve()
+ //Was linked, now destroyed or something
+ if(!tx_to)
+ bs_tx_weakref = null
+ to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.")
+ return FALSE
+
+ //Transmitted in the blind. If we get a message back, cool. If not, oh well.
+ signal.transmission_method = TRANSMISSION_BLUESPACE
+ return tx_to.receive_signal(signal)
+
+ /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */
+ else if(subspace_transmission)
+ var/list/jamming = is_jammed(src)
+ if(jamming)
+ var/distance = jamming["distance"]
+ to_chat(M, "[bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.")
+ return FALSE
+
+ // First, we want to generate a new radio signal
+ signal.transmission_method = TRANSMISSION_SUBSPACE
+
+ //#### Sending the signal to all subspace receivers ####//
+ for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
+ R.receive_signal(signal)
+
+ // Allinone can act as receivers.
+ for(var/obj/machinery/telecomms/allinone/R in telecomms_list)
+ R.receive_signal(signal)
+
+ // Receiving code can be located in Telecommunications.dm
+ if(signal.data["done"] && (pos_z in signal.data["level"]))
+ return TRUE //Huzzah, sent via subspace
+
+ else if(adhoc_fallback) //Less huzzah, we have to fallback
+ to_chat(loc, "\The [src] pings as it falls back to local radio transmission.")
+ subspace_transmission = FALSE
+
+ else //Oh well
+ return FALSE
+
+ /* ###### Intercoms and station-bounced radios ###### */
+ else
+ /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */
+ if(istype(src, /obj/item/device/radio/intercom))
+ filter_type = DATA_INTERCOM
+
+ /* --- Try to send a normal subspace broadcast first */
+ signal.transmission_method = TRANSMISSION_SUBSPACE
+ signal.data["compression"] = 0
+
+ for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
+ R.receive_signal(signal)
+
+ // Allinone can act as receivers.
+ for(var/obj/machinery/telecomms/allinone/R in telecomms_list)
+ R.receive_signal(signal)
+
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
R.receive_signal(signal)
- if(signal.data["done"] && position.z in signal.data["level"])
- if(adhoc_fallback)
- to_chat(loc, "\The [src] pings as it reestablishes subspace communications.")
- subspace_transmission = TRUE
- // we're done here.
- return TRUE
+ if(signal.data["done"] && pos_z in signal.data["level"])
+ if(adhoc_fallback)
+ to_chat(loc, "\The [src] pings as it reestablishes subspace communications.")
+ subspace_transmission = TRUE
+ // we're done here.
+ return TRUE
- // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level.
- // Send a mundane broadcast with limited targets:
-
- //THIS IS TEMPORARY. YEAH RIGHT
- if(!connection) return FALSE //~Carn
+ //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel.
return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
- src, message_pieces, displayname, jobname, real_name, M.voice_name,
- filter_type, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency, verb)
+ src, message_pieces, displayname, jobname, real_name, M.voice_name,
+ filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb)
/obj/item/device/radio/hear_talk(mob/M, list/message_pieces, verb)
@@ -496,25 +531,10 @@ var/global/list/default_medbay_channels = list(
if(get_dist(src, M) <= canhear_range)
talk_into(M, message_pieces, null, verb)
-
-/*
-/obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message)
-
- if((R.frequency == frequency && message))
- return TRUE
- else if
-
- else
- return null
- return
-*/
-
-
/obj/item/device/radio/proc/receive_range(freq, level)
// check if this radio can receive on the given frequency, and if so,
// what the range is in which mobs will hear the radio
// returns: -1 if can't receive, range otherwise
-
if(wires.IsIndexCut(WIRE_RECEIVE))
return -1
if(!listening)
@@ -522,8 +542,8 @@ var/global/list/default_medbay_channels = list(
if(is_jammed(src))
return -1
if(!(0 in level))
- var/turf/position = get_turf(src)
- if(!position || !(position.z in level))
+ var/pos_z = get_z(src)
+ if(!(pos_z in level))
return -1
if(freq in ANTAG_FREQS)
if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys
diff --git a/code/game/objects/items/devices/radio/radiopack.dm b/code/game/objects/items/devices/radio/radiopack.dm
new file mode 100644
index 0000000000..c2ba25ecc2
--- /dev/null
+++ b/code/game/objects/items/devices/radio/radiopack.dm
@@ -0,0 +1,153 @@
+/obj/item/device/bluespaceradio
+ name = "bluespace radio"
+ desc = "A powerful radio that uses a tiny bluespace wormhole to send signals directly to subspace receivers and transmitters, bypassing the limitations of subspace."
+ icon = 'icons/obj/radio.dmi'
+ icon_state = "radiopack"
+ item_state = "radiopack"
+ slot_flags = SLOT_BACK
+ force = 5
+ throwforce = 6
+ preserve_item = 1
+ w_class = ITEMSIZE_LARGE
+ action_button_name = "Remove/Replace Handset"
+
+ var/obj/item/device/radio/bluespacehandset/linked/handset = /obj/item/device/radio/bluespacehandset/linked
+
+/obj/item/device/bluespaceradio/Initialize()
+ . = ..()
+ if(ispath(handset))
+ handset = new handset(src, src)
+
+/obj/item/device/bluespaceradio/Destroy()
+ . = ..()
+ QDEL_NULL(handset)
+
+/obj/item/device/bluespaceradio/ui_action_click()
+ toggle_handset()
+
+/obj/item/device/bluespaceradio/attack_hand(var/mob/user)
+ if(loc == user)
+ toggle_handset()
+ else
+ ..()
+
+/obj/item/device/bluespaceradio/MouseDrop()
+ if(ismob(loc))
+ if(!CanMouseDrop(src))
+ return
+ var/mob/M = loc
+ if(!M.unEquip(src))
+ return
+ add_fingerprint(usr)
+ M.put_in_any_hand_if_possible(src)
+
+/obj/item/device/bluespaceradio/attackby(var/obj/item/weapon/W, var/mob/user, var/params)
+ if(W == handset)
+ reattach_handset(user)
+ else
+ return ..()
+
+/obj/item/device/bluespaceradio/verb/toggle_handset()
+ set name = "Toggle Handset"
+ set category = "Object"
+
+ var/mob/living/carbon/human/user = usr
+ if(!handset)
+ to_chat(user, "The handset is missing!")
+ return
+
+ if(handset.loc != src)
+ reattach_handset(user) //Remove from their hands and back onto the defib unit
+ return
+
+ if(!slot_check())
+ to_chat(user, "You need to equip [src] before taking out [handset].")
+ else
+ if(!usr.put_in_hands(handset)) //Detach the handset into the user's hands
+ to_chat(user, "You need a free hand to hold the handset!")
+ update_icon() //success
+
+//checks that the base unit is in the correct slot to be used
+/obj/item/device/bluespaceradio/proc/slot_check()
+ var/mob/M = loc
+ if(!istype(M))
+ return 0 //not equipped
+
+ if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src)
+ return 1
+ if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src)
+ return 1
+
+ return 0
+
+/obj/item/device/bluespaceradio/dropped(var/mob/user)
+ ..()
+ reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit
+
+/obj/item/device/bluespaceradio/proc/reattach_handset(var/mob/user)
+ if(!handset) return
+
+ if(ismob(handset.loc))
+ var/mob/M = handset.loc
+ if(M.drop_from_inventory(handset, src))
+ to_chat(user, "\The [handset] snaps back into the main unit.")
+ else
+ handset.forceMove(src)
+
+//Subspace Radio Handset
+/obj/item/device/radio/bluespacehandset
+ name = "bluespace radio handset"
+ desc = "A large walkie talkie attached to the bluespace radio by a retractable cord. It sits comfortably on a slot in the radio when not in use."
+ bluespace_radio = TRUE
+ icon_state = "signaller"
+ slot_flags = null
+ w_class = ITEMSIZE_LARGE
+ canhear_range = 1
+
+/obj/item/device/radio/bluespacehandset/linked
+ var/obj/item/device/bluespaceradio/base_unit
+
+/obj/item/device/radio/bluespacehandset/linked/Initialize(mapload, var/obj/item/device/bluespaceradio/radio)
+ base_unit = radio
+ . = ..()
+
+/obj/item/device/radio/bluespacehandset/linked/Destroy()
+ if(base_unit)
+ //ensure the base unit's icon updates
+ if(base_unit.handset == src)
+ base_unit.handset = null
+ base_unit = null
+ return ..()
+
+/obj/item/device/radio/bluespacehandset/linked/dropped(var/mob/user)
+ ..() //update twohanding
+ if(base_unit)
+ base_unit.reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit
+
+/obj/item/device/radio/bluespacehandset/linked/receive_range(var/freq, var/list/level)
+ //Only care about megabroadcasts or things that are targeted at us
+ if(!(0 in level))
+ return -1
+ if(wires.IsIndexCut(WIRE_RECEIVE))
+ return -1
+ if(!listening)
+ return -1
+ if(is_jammed(src))
+ return -1
+ if (!on)
+ return -1
+ if (!freq) //recieved on main frequency
+ if (!listening)
+ return -1
+ else
+ var/accept = (freq==frequency && listening)
+ if (!accept)
+ for (var/ch_name in channels)
+ var/datum/radio_frequency/RF = secure_radio_connections[ch_name]
+ if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING))
+ accept = 1
+ break
+ if (!accept)
+ return -1
+
+ return canhear_range
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 75ba6dae11..78c764f789 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -248,7 +248,7 @@ var/const/enterloopsanity = 100
/turf/proc/inertial_drift(atom/movable/A as mob|obj)
if(!(A.last_move)) return
- if((istype(A, /mob/) && src.x > 2 && src.x < (world.maxx - 1) && src.y > 2 && src.y < (world.maxy-1)))
+ if((istype(A, /mob/) && src.x > 1 && src.x < (world.maxx) && src.y > 1 && src.y < (world.maxy)))
var/mob/M = A
if(M.Process_Spacemove(1))
M.inertia_dir = 0
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 4c129fbf73..9b7b676d0e 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -505,7 +505,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
//If we're announcing their arrival
if(announce)
- AnnounceArrival(new_character, new_character.mind.assigned_role)
+ AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z)
log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].")
message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1)
diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm
index 22b069093a..cbec4c25b6 100644
--- a/code/modules/alarm/alarm_handler.dm
+++ b/code/modules/alarm/alarm_handler.dm
@@ -47,16 +47,17 @@
existing.clear(source)
return check_alarm_cleared(existing)
-/datum/alarm_handler/proc/major_alarms()
- return visible_alarms()
+/datum/alarm_handler/proc/major_alarms(var/z)
+ return visible_alarms(z)
-/datum/alarm_handler/proc/has_major_alarms()
- if(alarms && alarms.len)
- return 1
- return 0
+/datum/alarm_handler/proc/has_major_alarms(var/z)
+ if(!LAZYLEN(alarms))
+ return 0
-/datum/alarm_handler/proc/minor_alarms()
- return visible_alarms()
+ return LAZYLEN(major_alarms(z))
+
+/datum/alarm_handler/proc/minor_alarms(var/z)
+ return visible_alarms(z)
/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
@@ -101,9 +102,15 @@
for(var/listener in listeners)
call(listener, listeners[listener])(src, alarm, was_raised)
-/datum/alarm_handler/proc/visible_alarms()
+/datum/alarm_handler/proc/visible_alarms(var/z)
+ if(!LAZYLEN(alarms))
+ return list()
+
+ var/list/map_levels = using_map.get_map_levels(z)
+
var/list/visible_alarms = new()
for(var/datum/alarm/A in alarms)
- if(!A.hidden)
- visible_alarms.Add(A)
+ if(A.hidden || (z && !(A.origin?.z in map_levels)))
+ continue
+ visible_alarms.Add(A)
return visible_alarms
\ No newline at end of file
diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm
index 94f2e91e05..84302f1b65 100644
--- a/code/modules/alarm/atmosphere_alarm.dm
+++ b/code/modules/alarm/atmosphere_alarm.dm
@@ -1,16 +1,22 @@
/datum/alarm_handler/atmosphere
category = "Atmosphere Alarms"
-/datum/alarm_handler/atmosphere/major_alarms()
+/datum/alarm_handler/atmosphere/major_alarms(var/z)
var/list/major_alarms = new()
+ var/list/map_levels = using_map.get_map_levels(z)
for(var/datum/alarm/A in visible_alarms())
+ if(z && !(A.origin?.z in map_levels))
+ continue
if(A.max_severity() > 1)
major_alarms.Add(A)
return major_alarms
-/datum/alarm_handler/atmosphere/minor_alarms()
+/datum/alarm_handler/atmosphere/minor_alarms(var/z)
var/list/minor_alarms = new()
+ var/list/map_levels = using_map.get_map_levels(z)
for(var/datum/alarm/A in visible_alarms())
+ if(z && !(A.origin?.z in map_levels))
+ continue
if(A.max_severity() == 1)
minor_alarms.Add(A)
return minor_alarms
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index d38b40ccf1..064f6f9e99 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -364,6 +364,7 @@
var/turf/T = join_props["turf"]
var/join_message = join_props["msg"]
+ var/announce_channel = join_props["channel"] || "Common"
if(!T || !join_message)
return 0
@@ -413,18 +414,19 @@
//Grab some data from the character prefs for use in random news procs.
- AnnounceArrival(character, rank, join_message)
+ AnnounceArrival(character, rank, join_message, announce_channel, character.z)
else
- AnnounceCyborg(character, rank, join_message)
+ AnnounceCyborg(character, rank, join_message, announce_channel, character.z)
qdel(src)
-/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message)
+/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message, var/channel, var/zlevel)
if (ticker.current_state == GAME_STATE_PLAYING)
+ var/list/zlevels = zlevel ? using_map.get_map_levels(zlevel, TRUE) : null
if(character.mind.role_alt_title)
rank = character.mind.role_alt_title
// can't use their name here, since cyborg namepicking is done post-spawn, so we'll just say "A new Cyborg has arrived"/"A new Android has arrived"/etc.
- global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer")
+ global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer", channel, zlevels)
/mob/new_player/proc/LateChoices()
var/name = client.prefs.be_random_name ? "friend" : client.prefs.real_name
diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm
index 71e7179566..fc2efa8820 100644
--- a/code/modules/modular_computers/file_system/programs/command/comm.dm
+++ b/code/modules/modular_computers/file_system/programs/command/comm.dm
@@ -268,7 +268,7 @@
var/datum/signal/status_signal = new
status_signal.source = src
- status_signal.transmission_method = 1
+ status_signal.transmission_method = TRANSMISSION_RADIO
status_signal.data["command"] = command
switch(command)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm
index c887034754..bb45191f2f 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm
@@ -61,31 +61,35 @@
AH.unregister_alarm(object)
/datum/nano_module/alarm_monitor/proc/all_alarms()
+ var/z = get_z(nano_host())
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.visible_alarms()
+ all_alarms += AH.visible_alarms(z)
return all_alarms
/datum/nano_module/alarm_monitor/proc/major_alarms()
+ var/z = get_z(nano_host())
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.major_alarms()
+ all_alarms += AH.major_alarms(z)
return all_alarms
// Modified version of above proc that uses slightly less resources, returns 1 if there is a major alarm, 0 otherwise.
/datum/nano_module/alarm_monitor/proc/has_major_alarms()
+ var/z = get_z(nano_host())
for(var/datum/alarm_handler/AH in alarm_handlers)
- if(AH.has_major_alarms())
+ if(AH.has_major_alarms(z))
return 1
return 0
/datum/nano_module/alarm_monitor/proc/minor_alarms()
+ var/z = get_z(nano_host())
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.minor_alarms()
+ all_alarms += AH.minor_alarms(z)
return all_alarms
@@ -104,9 +108,10 @@
var/list/data = host.initial_data()
var/categories[0]
+ var/z = get_z(nano_host())
for(var/datum/alarm_handler/AH in alarm_handlers)
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
- for(var/datum/alarm/A in AH.major_alarms())
+ for(var/datum/alarm/A in AH.major_alarms(z))
var/cameras[0]
var/lost_sources[0]
diff --git a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm
index 776e73ec7a..bfcaadee3e 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm
@@ -47,12 +47,17 @@
/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
var/alarms[0]
- var/turf/T = get_turf(nano_host())
+
+ var/z = get_z(nano_host())
+ var/list/map_levels = using_map.get_map_levels(z)
+ data["map_levels"] = map_levels
// TODO: Move these to a cache, similar to cameras
for(var/obj/machinery/alarm/alarm in (monitored_alarms.len ? monitored_alarms : machines))
if(!monitored_alarms.len && alarm.alarms_hidden)
continue
+ if(!(alarm.z in map_levels))
+ continue
alarms[++alarms.len] = list(
"name" = sanitize(alarm.name),
"ref"= "\ref[alarm]",
@@ -61,7 +66,6 @@
"y" = alarm.y,
"z" = alarm.z)
data["alarms"] = alarms
- data["map_levels"] = using_map.get_map_levels(T.z)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm
index 32117e845c..b5c086dc26 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm
@@ -53,10 +53,15 @@
var/list/sensors = list()
// Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading.
var/obj/machinery/power/sensor/focus = null
- var/turf/T = get_turf(nano_host())
+
+ var/z = get_z(nano_host())
+ var/list/map_levels = using_map.get_map_levels(z)
+ data["map_levels"] = map_levels
// Build list of data from sensor readings.
for(var/obj/machinery/power/sensor/S in grid_sensors)
+ if(!(S.z in map_levels))
+ continue
sensors.Add(list(list(
"name" = S.name_tag,
"alarm" = S.check_grid_warning()
@@ -67,7 +72,6 @@
data["all_sensors"] = sensors
if(focus)
data["focus"] = focus.return_reading_data()
- data["map_levels"] = using_map.get_map_levels(T.z)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm
index d5ee88b670..7603b1115e 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm
@@ -122,11 +122,19 @@
// Description: Refreshes local list of known devices.
/datum/nano_module/rcon/proc/FindDevices()
known_SMESs = new /list()
+
+ var/z = get_z(nano_host())
+ var/list/map_levels = using_map.get_map_levels(z)
+
for(var/obj/machinery/power/smes/buildable/SMES in machines)
+ if(!(SMES.z in map_levels))
+ continue
if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon)
known_SMESs.Add(SMES)
known_breakers = new /list()
for(var/obj/machinery/power/breakerbox/breaker in machines)
+ if(!(breaker.z in map_levels))
+ continue
if(breaker.RCon_tag != "NO_TAG")
known_breakers.Add(breaker)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm
index 78a774be0b..421bb65047 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm
@@ -41,10 +41,10 @@
// Refreshes list of active supermatter crystals
/datum/nano_module/supermatter_monitor/proc/refresh()
supermatters = list()
- var/turf/T = get_turf(nano_host())
- if(!T)
+ var/z = get_z(nano_host())
+ if(!z)
return
- var/valid_z_levels = (GetConnectedZlevels(T.z) & using_map.station_levels)
+ var/valid_z_levels = using_map.get_map_levels(z)
for(var/obj/machinery/power/supermatter/S in machines)
// Delaminating, not within coverage, not on a tile.
if(S.grav_pulling || S.exploded || !(S.z in valid_z_levels) || !istype(S.loc, /turf/))
diff --git a/code/modules/modular_computers/file_system/programs/generic/camera.dm b/code/modules/modular_computers/file_system/programs/generic/camera.dm
index a722dea6b6..9f0cade5e9 100644
--- a/code/modules/modular_computers/file_system/programs/generic/camera.dm
+++ b/code/modules/modular_computers/file_system/programs/generic/camera.dm
@@ -62,8 +62,10 @@
data["networks"] = all_networks
+ var/list/map_levels = using_map.get_map_levels(get_z(nano_host()), TRUE)
+
if(current_network)
- data["cameras"] = camera_repository.cameras_in_network(current_network)
+ data["cameras"] = camera_repository.cameras_in_network(current_network, map_levels)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
diff --git a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm
index a332a064dc..bf8fe50909 100644
--- a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm
+++ b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm
@@ -34,16 +34,21 @@
/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
- var/turf/T = get_turf(nano_host())
data["isAI"] = isAI(user)
- data["map_levels"] = using_map.get_map_levels(T.z, FALSE)
+
+ var/z = get_z(nano_host())
+ var/list/map_levels = using_map.get_map_levels(z, TRUE)
+ data["map_levels"] = map_levels
+
data["crewmembers"] = list()
- for(var/z in (data["map_levels"] | T.z)) // Always show crew from the current Z even if we can't show a map
- data["crewmembers"] += crew_repository.health_data(z)
+ for(var/zlevel in map_levels)
+ data["crewmembers"] += crew_repository.health_data(zlevel)
if(!data["map_levels"].len)
to_chat(user, "The crew monitor doesn't seem like it'll work here.")
+ if(ui)
+ ui.close()
return
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm
index 69f4a8bb64..2f382e0f4d 100644
--- a/code/modules/modular_computers/hardware/network_card.dm
+++ b/code/modules/modular_computers/hardware/network_card.dm
@@ -79,20 +79,25 @@ var/global/ntnet_card_uid = 1
return 0
if(holder2)
- var/turf/T = get_turf(holder2)
- if(!istype(T)) //no reception in nullspace
+ var/holderz = get_z(holder2)
+ if(!holderz) //no reception in nullspace
return 0
- if(T.z in using_map.station_levels)
- // Computer is on station. Low/High signal depending on what type of network card you have
- if(long_range)
- return 2
- else
- return 1
- if(T.z in using_map.contact_levels) //not on station, but close enough for radio signal to travel
- if(long_range) // Computer is not on station, but it has upgraded network card. Low signal.
- return 1
-
- return 0 // Computer is not on station and does not have upgraded network card. No signal.
+ var/list/zlevels_in_range = using_map.get_map_levels(holderz, long_range)
+ var/best = 0
+ for(var/relay in ntnet_global.relays)
+ var/obj/machinery/ntnet_relay/R = relay
+ //Relay is down
+ if(!R.operable())
+ continue
+ //We're on the same z
+ if(R.z == holderz)
+ best = 2
+ break // No point in going further
+ //Not on the same z but within range anyway
+ if(R.z in zlevels_in_range)
+ best = 1
+ return best
+ return 0 // No computer!
/obj/item/weapon/computer_hardware/network_card/Destroy()
if(holder2 && (holder2.network_card == src))
diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm
deleted file mode 100644
index 6cfde46793..0000000000
--- a/code/modules/overmap/_defines.dm
+++ /dev/null
@@ -1,145 +0,0 @@
-//How far from the edge of overmap zlevel could randomly placed objects spawn
-#define OVERMAP_EDGE 2
-
-#define SHIP_SIZE_TINY 1
-#define SHIP_SIZE_SMALL 2
-#define SHIP_SIZE_LARGE 3
-
-//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship
-#define SHIP_SPEED_SLOW 1/(40 SECONDS)
-#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed
-
-#define OVERMAP_WEAKNESS_NONE 0
-#define OVERMAP_WEAKNESS_FIRE 1
-#define OVERMAP_WEAKNESS_EMP 2
-#define OVERMAP_WEAKNESS_MINING 4
-#define OVERMAP_WEAKNESS_EXPLOSIVE 8
-
-//Dimension of overmap (squares 4 lyfe)
-var/global/list/map_sectors = list()
-
-/area/overmap/
- name = "System Map"
- icon_state = "start"
- requires_power = 0
- base_turf = /turf/unsimulated/map
-
-/turf/unsimulated/map
- icon = 'icons/turf/space.dmi'
- icon_state = "map"
- initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary!
-
-/turf/unsimulated/map/edge
- opacity = 1
- density = 1
-
-/turf/unsimulated/map/Initialize()
- . = ..()
- name = "[x]-[y]"
- var/list/numbers = list()
-
- if(x == 1 || x == global.using_map.overmap_size)
- numbers += list("[round(y/10)]","[round(y%10)]")
- if(y == 1 || y == global.using_map.overmap_size)
- numbers += "-"
- if(y == 1 || y == global.using_map.overmap_size)
- numbers += list("[round(x/10)]","[round(x%10)]")
-
- for(var/i = 1 to numbers.len)
- var/image/I = image('icons/effects/numbers.dmi',numbers[i])
- I.pixel_x = 5*i - 2
- I.pixel_y = world.icon_size/2 - 3
- if(y == 1)
- I.pixel_y = 3
- I.pixel_x = 5*i + 4
- if(y == global.using_map.overmap_size)
- I.pixel_y = world.icon_size - 9
- I.pixel_x = 5*i + 4
- if(x == 1)
- I.pixel_x = 5*i - 2
- if(x == global.using_map.overmap_size)
- I.pixel_x = 5*i + 2
- add_overlay(I)
-
-//list used to track which zlevels are being 'moved' by the proc below
-var/list/moving_levels = list()
-//Proc to 'move' stars in spess
-//yes it looks ugly, but it should only fire when state actually change.
-//null direction stops movement
-proc/toggle_move_stars(zlevel, direction)
- if(!zlevel)
- return
-
- if (moving_levels["[zlevel]"] != direction)
- moving_levels["[zlevel]"] = direction
-
- var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))
- for(var/turf/space/T in spaceturfs)
- T.toggle_transit(direction)
- CHECK_TICK
-/*
-//list used to cache empty zlevels to avoid nedless map bloat
-var/list/cached_space = list()
-
-proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
- var/obj/effect/map/M = map_sectors["[T.z]"]
- if (!M)
- return
- var/mapx = M.x
- var/mapy = M.y
- var/nx = 1
- var/ny = 1
- var/nz = M.map_z
-
- if(T.x <= TRANSITIONEDGE)
- nx = world.maxx - TRANSITIONEDGE - 2
- ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
- mapx = max(1, mapx-1)
-
- else if (A.x >= (world.maxx - TRANSITIONEDGE - 1))
- nx = TRANSITIONEDGE + 2
- ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
- mapx = min(world.maxx, mapx+1)
-
- else if (T.y <= TRANSITIONEDGE)
- ny = world.maxy - TRANSITIONEDGE -2
- nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
- mapy = max(1, mapy-1)
-
- else if (A.y >= (world.maxy - TRANSITIONEDGE - 1))
- ny = TRANSITIONEDGE + 2
- nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
- mapy = min(world.maxy, mapy+1)
-
- testing("[A] moving from [M] ([M.x], [M.y]) to ([mapx],[mapy]).")
-
- var/turf/map = locate(mapx,mapy,OVERMAP_ZLEVEL)
- var/obj/effect/map/TM = locate() in map
- if(TM)
- nz = TM.map_z
- testing("Destination: [TM]")
- else
- if(cached_space.len)
- var/obj/effect/map/sector/temporary/cache = cached_space[cached_space.len]
- cached_space -= cache
- nz = cache.map_z
- cache.x = mapx
- cache.y = mapy
- testing("Destination: *cached* [TM]")
- else
- world.maxz++
- nz = world.maxz
- TM = new /obj/effect/map/sector/temporary(mapx, mapy, nz)
- testing("Destination: *new* [TM]")
-
- var/turf/dest = locate(nx,ny,nz)
- if(dest)
- A.loc = dest
-
- if(istype(M, /obj/effect/map/sector/temporary))
- var/obj/effect/map/sector/temporary/source = M
- if (source.can_die())
- testing("Catching [M] for future use")
- source.loc = null
- cached_space += source
-*/
\ No newline at end of file
diff --git a/code/modules/overmap/helpers.dm b/code/modules/overmap/helpers.dm
new file mode 100644
index 0000000000..e07be07ac0
--- /dev/null
+++ b/code/modules/overmap/helpers.dm
@@ -0,0 +1,5 @@
+/proc/get_overmap_sector(var/z)
+ if(using_map.use_overmap)
+ return map_sectors["[z]"]
+ else
+ return null
diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm
index c3195c99de..b614137db4 100644
--- a/code/modules/overmap/overmap_shuttle.dm
+++ b/code/modules/overmap/overmap_shuttle.dm
@@ -1,4 +1,4 @@
-#define waypoint_sector(waypoint) map_sectors["[waypoint.z]"]
+#define waypoint_sector(waypoint) get_overmap_sector(get_z(waypoint))
/datum/shuttle/autodock/overmap
warmup_time = 10
diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm
index da05a9908b..6e6304f1f8 100644
--- a/code/modules/overmap/ships/computers/ship.dm
+++ b/code/modules/overmap/ships/computers/ship.dm
@@ -17,7 +17,7 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
return 1
/obj/machinery/computer/ship/proc/sync_linked(var/user = null)
- var/obj/effect/overmap/visitable/ship/sector = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/ship/sector = get_overmap_sector(z)
if(!sector)
return
. = attempt_hook_up_recursive(sector)
diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm
index 967fdeb571..0a28eefd8d 100644
--- a/code/modules/overmap/ships/landable.dm
+++ b/code/modules/overmap/ships/landable.dm
@@ -80,7 +80,7 @@
. = ..()
/obj/effect/shuttle_landmark/ship/Destroy()
- var/obj/effect/overmap/visitable/ship/landable/ship = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/ship/landable/ship = get_overmap_sector(z)
if(istype(ship) && ship.landmark == src)
ship.landmark = null
. = ..()
@@ -141,7 +141,7 @@
on_landing(from, into)
/obj/effect/overmap/visitable/ship/landable/proc/on_landing(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into)
- var/obj/effect/overmap/visitable/target = map_sectors["[into.z]"]
+ var/obj/effect/overmap/visitable/target = get_overmap_sector(get_z(into))
var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle]
if(into.landmark_tag == shuttle_datum.motherdock) // If our motherdock is a landable ship, it won't be found properly here so we need to find it manually.
for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttles.ships)
diff --git a/code/modules/overmap/spacetravel.dm b/code/modules/overmap/spacetravel.dm
index adb7e80269..aec5c06f75 100644
--- a/code/modules/overmap/spacetravel.dm
+++ b/code/modules/overmap/spacetravel.dm
@@ -57,7 +57,7 @@ proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
if (!T || !A)
return
- var/obj/effect/overmap/visitable/M = map_sectors["[T.z]"]
+ var/obj/effect/overmap/visitable/M = get_overmap_sector(T.z)
if (!M)
return
diff --git a/code/modules/overmap/turfs.dm b/code/modules/overmap/turfs.dm
index 3508203a6e..fc9f0b16eb 100644
--- a/code/modules/overmap/turfs.dm
+++ b/code/modules/overmap/turfs.dm
@@ -1,7 +1,7 @@
//Dimension of overmap (squares 4 lyfe)
var/global/list/map_sectors = list()
-/area/overmap/
+/area/overmap
name = "System Map"
icon_state = "start"
requires_power = 0
diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm
index 99761dfe25..38f2d41f25 100644
--- a/code/modules/shuttles/landmarks.dm
+++ b/code/modules/shuttles/landmarks.dm
@@ -55,14 +55,14 @@
if(!istype(docking_controller))
log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.")
if(using_map.use_overmap)
- var/obj/effect/overmap/visitable/location = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/location = get_overmap_sector(z)
if(location && location.docking_codes)
docking_controller.docking_codes = location.docking_codes
/obj/effect/shuttle_landmark/forceMove()
- var/obj/effect/overmap/visitable/map_origin = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/map_origin = get_overmap_sector(z)
. = ..()
- var/obj/effect/overmap/visitable/map_destination = map_sectors["[z]"]
+ var/obj/effect/overmap/visitable/map_destination = get_overmap_sector(z)
if(map_origin != map_destination)
if(map_origin)
map_origin.remove_landmark(src, shuttle_restricted)
diff --git a/code/modules/shuttles/shuttle_autodock.dm b/code/modules/shuttles/shuttle_autodock.dm
index b9b1801205..4181af5b15 100644
--- a/code/modules/shuttles/shuttle_autodock.dm
+++ b/code/modules/shuttles/shuttle_autodock.dm
@@ -30,7 +30,7 @@
if(active_docking_controller)
set_docking_codes(active_docking_controller.docking_codes)
else if(global.using_map.use_overmap)
- var/obj/effect/overmap/visitable/location = map_sectors["[current_location.z]"]
+ var/obj/effect/overmap/visitable/location = get_overmap_sector(get_z(current_location))
if(location && location.docking_codes)
set_docking_codes(location.docking_codes)
dock()
diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm
index e07166799b..6d277edec1 100644
--- a/code/modules/shuttles/shuttle_console.dm
+++ b/code/modules/shuttles/shuttle_console.dm
@@ -166,7 +166,7 @@ GLOBAL_LIST_BOILERPLATE(papers_dockingcode, /obj/item/weapon/paper/dockingcodes)
var/dockingcodes = null
var/z_to_check = codes_from_z ? codes_from_z : z
if(using_map.use_overmap)
- var/obj/effect/overmap/visitable/location = map_sectors["[z_to_check]"]
+ var/obj/effect/overmap/visitable/location = get_overmap_sector(z_to_check)
if(location && location.docking_codes)
dockingcodes = location.docking_codes
diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi
index 99f3f4fd4c..5555d0e1ad 100644
Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ
diff --git a/icons/obj/radio.dmi b/icons/obj/radio.dmi
index 98871c1a02..c565483599 100644
Binary files a/icons/obj/radio.dmi and b/icons/obj/radio.dmi differ
diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm
index f40ed991a6..5ad22d0192 100644
--- a/maps/~map_system/maps.dm
+++ b/maps/~map_system/maps.dm
@@ -144,20 +144,44 @@ var/list/all_maps = list()
empty_levels = list(world.maxz)
return pick(empty_levels)
-// Get the list of zlevels that a computer on srcz can see maps of (for power/crew monitor, cameras, etc)
-// The long_range parameter expands the coverage. Default is to return map_levels for long range otherwise just srcz.
-// zLevels outside station_levels will return an empty list.
-/datum/map/proc/get_map_levels(var/srcz, var/long_range = TRUE)
- if (long_range && (srcz in map_levels))
- return map_levels
- else if (srcz in station_levels)
- return list(srcz)
+// Get a list of 'nearby' or 'connected' zlevels.
+// You should at least return a list with the given z if nothing else.
+/datum/map/proc/get_map_levels(var/srcz, var/long_range = FALSE, var/om_range = -1)
+ //Overmap behavior
+ if(use_overmap)
+ //Get what sector we're in
+ var/obj/effect/overmap/visitable/O = get_overmap_sector(srcz)
+ if(!istype(O))
+ //Not in a sector, just the passed zlevel
+ return list(srcz)
+
+ //Just the sector we're in
+ if(om_range == -1)
+ return O.map_z.Copy()
+
+ //Otherwise every sector we're on top of
+ var/list/connections = list()
+ var/turf/T = get_turf(O)
+ var/turfrange = long_range ? max(0, om_range) : om_range
+ for(var/obj/effect/overmap/visitable/V in range(turfrange, T))
+ connections += V.map_z // Adding list to list adds contents
+ return connections
+
+ //Traditional behavior
else
- return list()
+ //If long range, and they're at least in contact levels, return contact levels.
+ if (long_range && (srcz in contact_levels))
+ return contact_levels.Copy()
+ //If in station levels, return station levels
+ else if (srcz in station_levels)
+ return station_levels.Copy()
+ //Just give them back their zlevel
+ else
+ return list(srcz)
/datum/map/proc/get_zlevel_name(var/index)
var/datum/map_z_level/Z = zlevels["[index]"]
- return Z.name
+ return Z?.name
// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't
// This list needs to be purged but people insist on adding more cruft to the radio.
diff --git a/nano/templates/sec_camera.tmpl b/nano/templates/sec_camera.tmpl
index 1d46f1868f..21ecea71e7 100644
--- a/nano/templates/sec_camera.tmpl
+++ b/nano/templates/sec_camera.tmpl
@@ -8,7 +8,8 @@ Used In File(s): \code\game\machinery\computer\camera.dm
{{:helper.link('Reset', 'refresh', {'reset' : 1})}}
-
Current Camera:
+
Current Camera:
+
{{if data.current_camera}}
{{:data.current_camera.name}}
{{else}}
diff --git a/polaris.dme b/polaris.dme
index 1b6cc35def..3f1a6e94fa 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -996,6 +996,7 @@
#include "code\game\objects\items\devices\radio\intercom.dm"
#include "code\game\objects\items\devices\radio\jammer.dm"
#include "code\game\objects\items\devices\radio\radio.dm"
+#include "code\game\objects\items\devices\radio\radiopack.dm"
#include "code\game\objects\items\robot\robot_items.dm"
#include "code\game\objects\items\robot\robot_parts.dm"
#include "code\game\objects\items\robot\robot_upgrades.dm"
@@ -2435,6 +2436,7 @@
#include "code\modules\organs\subtypes\unseverable.dm"
#include "code\modules\organs\subtypes\vox.dm"
#include "code\modules\organs\subtypes\xenos.dm"
+#include "code\modules\overmap\helpers.dm"
#include "code\modules\overmap\overmap_object.dm"
#include "code\modules\overmap\overmap_shuttle.dm"
#include "code\modules\overmap\sectors.dm"