diff --git a/aurorastation.dme b/aurorastation.dme
index adf14b0d9bf..6e48f58b5c2 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -202,7 +202,7 @@
#include "code\controllers\subsystems\documents.dm"
#include "code\controllers\subsystems\economy.dm"
#include "code\controllers\subsystems\effects.dm"
-#include "code\controllers\subsystems\emergency_shuttle.dm"
+#include "code\controllers\subsystems\evac.dm"
#include "code\controllers\subsystems\event.dm"
#include "code\controllers\subsystems\explosives.dm"
#include "code\controllers\subsystems\fail2topic.dm"
@@ -247,6 +247,14 @@
#include "code\controllers\subsystems\virtual_reality.dm"
#include "code\controllers\subsystems\vote.dm"
#include "code\controllers\subsystems\zcopy.dm"
+#include "code\controllers\subsystems\evacuation\evacuation.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_eta.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_helpers.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_option.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_pods.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_predicate.dm"
+#include "code\controllers\subsystems\evacuation\evacuation_shuttle.dm"
+#include "code\controllers\subsystems\evacuation\~evac.dm"
#include "code\controllers\subsystems\initialization\atlas.dm"
#include "code\controllers\subsystems\initialization\atoms.dm"
#include "code\controllers\subsystems\initialization\holomap.dm"
@@ -292,6 +300,7 @@
#include "code\datums\weakref.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\armor\armor.dm"
+#include "code\datums\components\base_name\base_name.dm"
#include "code\datums\components\multitool\_multitool.dm"
#include "code\datums\components\multitool\multitool.dm"
#include "code\datums\components\multitool\circuitboards\circuitboards.dm"
@@ -314,6 +323,7 @@
#include "code\datums\looping_sounds\thermal_drill.dm"
#include "code\datums\observation\_debug.dm"
#include "code\datums\observation\destroyed.dm"
+#include "code\datums\observation\dir_set.dm"
#include "code\datums\observation\entered.dm"
#include "code\datums\observation\equipped.dm"
#include "code\datums\observation\exited.dm"
@@ -533,6 +543,7 @@
#include "code\game\gamemodes\cult\structures\pylon.dm"
#include "code\game\gamemodes\cult\structures\structure.dm"
#include "code\game\gamemodes\endgame\endgame.dm"
+#include "code\game\gamemodes\endgame\bluespace_jump\bluespace_jump.dm"
#include "code\game\gamemodes\endgame\supermatter_cascade\blob.dm"
#include "code\game\gamemodes\endgame\supermatter_cascade\portal.dm"
#include "code\game\gamemodes\endgame\supermatter_cascade\universe.dm"
@@ -1105,6 +1116,7 @@
#include "code\game\objects\structures\extinguisher.dm"
#include "code\game\objects\structures\fireaxe_cabinet.dm"
#include "code\game\objects\structures\flora.dm"
+#include "code\game\objects\structures\full_window_frame.dm"
#include "code\game\objects\structures\girders.dm"
#include "code\game\objects\structures\grille.dm"
#include "code\game\objects\structures\inflatable.dm"
@@ -1862,6 +1874,7 @@
#include "code\modules\hydroponics\seed_datums\flowers.dm"
#include "code\modules\hydroponics\seed_datums\fruits.dm"
#include "code\modules\hydroponics\seed_datums\grains.dm"
+#include "code\modules\hydroponics\seed_datums\herbs.dm"
#include "code\modules\hydroponics\seed_datums\jargon.dm"
#include "code\modules\hydroponics\seed_datums\misc.dm"
#include "code\modules\hydroponics\seed_datums\mushrooms.dm"
@@ -2224,7 +2237,6 @@
#include "code\modules\mob\living\silicon\robot\drone\drone_damage.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm"
-#include "code\modules\mob\living\silicon\robot\drone\drone_say.dm"
#include "code\modules\mob\living\silicon\robot\items\_helpers.dm"
#include "code\modules\mob\living\silicon\robot\items\auto_harvester.dm"
#include "code\modules\mob\living\silicon\robot\items\electric_arm.dm"
diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm
index 1fdd91e7ad7..108556ab1c3 100644
--- a/code/__defines/species_languages.dm
+++ b/code/__defines/species_languages.dm
@@ -73,20 +73,24 @@
// Synth Languages
#define LANGUAGE_ROBOT "Robot Talk"
+#define LANGUAGE_LOCAL_DRONE "Drone Transmission"
#define LANGUAGE_DRONE "Drone Talk"
#define LANGUAGE_EAL "Encoded Audio Language"
// Language flags.
-#define WHITELISTED 1 // Language is available if the speaker is whitelisted.
-#define RESTRICTED 2 // Language can only be acquired by spawning or an admin.
-#define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight.
-#define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand.
-#define HIVEMIND 16 // Broadcast to all mobs with this language.
-#define NONGLOBAL 32 // Do not add to general languages list.
-#define INNATE 64 // All mobs can be assumed to speak and understand this language. (audible emotes)
-#define NO_TALK_MSG 128 // Do not show the "\The [speaker] talks into \the [radio]" message
-#define NO_STUTTER 256 // No stuttering, slurring, or other speech problems
-#define TCOMSSIM 512 // Can be synthesized in tcoms
+#define WHITELISTED BITFLAG(0) // Language is available if the speaker is whitelisted.
+#define RESTRICTED BITFLAG(1) // Language can only be acquired by spawning or an admin.
+#define NONVERBAL BITFLAG(2) // Language has a significant non-verbal component. Speech is garbled without line-of-sight.
+#define SIGNLANG BITFLAG(3) // Language is completely non-verbal. Speech is displayed through emotes for those who can understand.
+#define HIVEMIND BITFLAG(4) // Broadcast to all mobs with this language.
+#define NONGLOBAL BITFLAG(5) // Do not add to general languages list.
+#define INNATE BITFLAG(6) // All mobs can be assumed to speak and understand this language. (audible emotes)
+#define NO_TALK_MSG BITFLAG(7) // Do not show the "\The [speaker] talks into \the [radio]" message
+#define NO_STUTTER BITFLAG(8) // No stuttering, slurring, or other speech problems
+#define TCOMSSIM BITFLAG(9) // Can be synthesized in tcoms
+#define KNOWONLYHEAR BITFLAG(10) // Only people who know the language actually hears it
+#define PRESSUREPROOF BITFLAG(11) // Pressure doesn't affect hearing
+#define PASSLISTENOBJ BITFLAG(12) // Listening Objs can't hear this language
// Autohiss
#define AUTOHISS_OFF 0
diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm
index ed698ef5787..8b0afbc33f0 100644
--- a/code/__defines/subsystem-priority.dm
+++ b/code/__defines/subsystem-priority.dm
@@ -71,6 +71,7 @@
//#define SS_PRIORITY_DEFAULT 50 // This is defined somewhere else.
#define SS_PRIORITY_PSYCHICS 30
#define SS_PRIORITY_ARRIVALS 30 // Centcomm arrivals shuttle auto-launch. Usually asleep.
+#define SS_PRIORITY_EVAC 30 // Processes the evac controller.
#define SS_PRIORITY_EXPLOSIVES 20 // Explosion processor. Doesn't have much effect on explosion tick-checking.
#define SS_PRIORITY_DISPOSALS 20 // Disposal holder movement.
#define SS_PRIORITY_MODIFIER 10
diff --git a/code/_onclick/hud/screen_object_types/ai_screen_objs.dm b/code/_onclick/hud/screen_object_types/ai_screen_objs.dm
index 78cd82f5ab7..14fa7257cfb 100644
--- a/code/_onclick/hud/screen_object_types/ai_screen_objs.dm
+++ b/code/_onclick/hud/screen_object_types/ai_screen_objs.dm
@@ -110,7 +110,7 @@
AI.ai_announcement()
/obj/screen/ai/call_shuttle
- name = "Call Emergency Shuttle"
+ name = "Call Evacuation"
icon_state = "call_shuttle"
screen_loc = ui_ai_shuttle
diff --git a/code/controllers/subsystems/emergency_shuttle.dm b/code/controllers/subsystems/emergency_shuttle.dm
deleted file mode 100644
index 88f5b61e241..00000000000
--- a/code/controllers/subsystems/emergency_shuttle.dm
+++ /dev/null
@@ -1,308 +0,0 @@
-var/datum/controller/subsystem/emergency_shuttle/emergency_shuttle
-
-/datum/controller/subsystem/emergency_shuttle
- name = "Emergency Shuttle"
- flags = SS_NO_TICK_CHECK | SS_NO_INIT
-
- var/datum/shuttle/autodock/ferry/emergency/shuttle
-
- var/launch_time //the time at which the shuttle will be launched
- var/force_time //the time at which the shuttle will be forced
- var/auto_recall = 0 //if set, the shuttle will be auto-recalled
- var/auto_recall_time //the time at which the shuttle will be auto-recalled
- var/evac = 0 //1 = emergency evacuation, 0 = crew transfer
- var/wait_for_launch = 0 //if the shuttle is waiting to launch
- var/wait_for_force = 0 //if the shuttle is waiting to be forced
- var/autopilot = 1 //set to 0 to disable the shuttle automatically launching
-
- var/deny_shuttle = 0 //allows admins to prevent the shuttle from being called
- var/departed = 0 //if the shuttle has left the station at least once
-
-/datum/controller/subsystem/emergency_shuttle/Recover()
- // Just copy all the stuff over.
- src.shuttle = emergency_shuttle.shuttle
- src.launch_time = emergency_shuttle.launch_time
- src.auto_recall = emergency_shuttle.auto_recall
- src.auto_recall_time = emergency_shuttle.auto_recall_time
- src.evac = emergency_shuttle.evac
- src.wait_for_launch = emergency_shuttle.wait_for_launch
- src.autopilot = emergency_shuttle.autopilot
- src.deny_shuttle = emergency_shuttle.deny_shuttle
-
-/datum/controller/subsystem/emergency_shuttle/New()
- NEW_SS_GLOBAL(emergency_shuttle)
-
-/datum/controller/subsystem/emergency_shuttle/fire()
- if(!shuttle)
- return
- if (wait_for_launch)
- if (evac && auto_recall && world.time >= auto_recall_time)
- recall()
- if (world.time >= launch_time) //time to launch the shuttle
- stop_launch_countdown()
-
- if (!shuttle.location) //leaving from the station
- //launch the pods!
- for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods)
- if (!pod.arming_controller || pod.arming_controller.armed)
- pod.launch(src)
-
- if (autopilot)
- shuttle.launch(src)
- if (wait_for_force)
- if (world.time > force_time)
- stop_force_countdown()
- if(waiting_to_leave())
- shuttle.launch(src)
- shuttle.force_launch(src)
-
-
-/datum/controller/subsystem/emergency_shuttle/proc/shuttle_arrived()
- if (!shuttle.location) //at station
- if (autopilot)
- set_launch_countdown(SHUTTLE_LEAVETIME) //get ready to return
-
- if (evac)
- set_force_countdown(SHUTTLE_FORCETIME)
- priority_announcement.Announce(replacetext(current_map.emergency_shuttle_docked_message, "%ETD%", round(estimate_launch_time()/60,1)), new_sound = 'sound/AI/emergencyshuttledock.ogg')
- else
- set_force_countdown(SHUTTLE_LEAVETIME)
- var/list/fields = list(
- "%ETA%" = round(emergency_shuttle.estimate_launch_time()/60,1),
- "%dock%" = current_map.dock_name
- )
- priority_announcement.Announce(replacemany(current_map.shuttle_docked_message, fields), new_sound = 'sound/AI/shuttledock.ogg')
-
- //arm the escape pods
- if (evac)
- for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods)
- if (pod.arming_controller)
- pod.arming_controller.arm()
-
-//begins the launch countdown and sets the amount of time left until launch
-/datum/controller/subsystem/emergency_shuttle/proc/set_launch_countdown(var/seconds)
- wait_for_launch = 1
- launch_time = world.time + seconds*10
-
-//begins the launch countdown and sets the amount of time left until launch
-/datum/controller/subsystem/emergency_shuttle/proc/set_force_countdown(var/seconds)
- if(!wait_for_force)
- wait_for_force = 1
- force_time = world.time + seconds*10
-
-/datum/controller/subsystem/emergency_shuttle/proc/stop_launch_countdown()
- wait_for_launch = 0
-
-/datum/controller/subsystem/emergency_shuttle/proc/stop_force_countdown()
- wait_for_force = 0
-
-//calls the shuttle for an emergency evacuation
-/datum/controller/subsystem/emergency_shuttle/proc/call_evac()
- if(!can_call()) return
-
- //set the launch timer
- autopilot = 1
- set_launch_countdown(get_shuttle_prep_time())
- auto_recall_time = rand(world.time + 300, launch_time - 300)
-
- //reset the shuttle transit time if we need to
- shuttle.move_time = SHUTTLE_TRANSIT_DURATION
-
- evac = 1
- priority_announcement.Announce(replacetext(current_map.emergency_shuttle_called_message, "%ETA%", round(estimate_arrival_time()/60)), new_sound = 'sound/AI/emergencyshuttlecalled.ogg')
- for(var/area/A in all_areas)
- if(istype(A, /area/hallway))
- A.readyalert()
-
-//calls the shuttle for a routine crew transfer
-/datum/controller/subsystem/emergency_shuttle/proc/call_transfer()
- if(!can_call()) return
-
- //set the launch timer
- autopilot = 1
- set_launch_countdown(get_shuttle_prep_time())
- auto_recall_time = rand(world.time + 300, launch_time - 300)
-
- //reset the shuttle transit time if we need to
- shuttle.move_time = SHUTTLE_TRANSIT_DURATION
-
- var/list/replacements = list(
- "%ETA%" = round(estimate_arrival_time()/60),
- "%dock%" = current_map.dock_name
- )
- priority_announcement.Announce(replacemany(current_map.shuttle_called_message, replacements), new_sound = 'sound/AI/shuttlecalled.ogg')
-
-//recalls the shuttle
-/datum/controller/subsystem/emergency_shuttle/proc/recall()
- if (!can_recall()) return
-
- wait_for_launch = 0
- shuttle.cancel_launch(src)
-
- if (evac)
- priority_announcement.Announce(current_map.emergency_shuttle_recall_message, new_sound = 'sound/AI/emergencyshuttlerecalled.ogg')
-
- for(var/area/A in all_areas)
- if(istype(A, /area/hallway))
- A.readyreset()
- evac = 0
- else
- priority_announcement.Announce(current_map.shuttle_recall_message, new_sound = 'sound/AI/shuttlerecalled.ogg')
-
-/datum/controller/subsystem/emergency_shuttle/proc/can_call()
- if (!universe.OnShuttleCall(null))
- return 0
- if (deny_shuttle)
- return 0
- if (shuttle.moving_status != SHUTTLE_IDLE || !shuttle.location) //must be idle at centcom
- return 0
- if (wait_for_launch) //already launching
- return 0
- return 1
-
-//this only returns 0 if it would absolutely make no sense to recall
-//e.g. the shuttle is already at the station or wasn't called to begin with
-//other reasons for the shuttle not being recallable should be handled elsewhere
-/datum/controller/subsystem/emergency_shuttle/proc/can_recall()
- if (shuttle.moving_status == SHUTTLE_INTRANSIT) //if the shuttle is already in transit then it's too late
- return 0
- if (!shuttle.location) //already at the station.
- return 0
- if (!wait_for_launch) //we weren't going anywhere, anyways...
- return 0
- return 1
-
-/datum/controller/subsystem/emergency_shuttle/proc/get_shuttle_prep_time()
- // During mutiny rounds, the shuttle takes twice as long.
- if(SSticker.mode)
- return SHUTTLE_PREPTIME * SSticker.mode.shuttle_delay
- return SHUTTLE_PREPTIME
-
-
-/*
- These procs are not really used by the controller itself, but are for other parts of the
- game whose logic depends on the emergency shuttle.
-*/
-
-//returns 1 if the shuttle is docked at the station and waiting to leave
-/datum/controller/subsystem/emergency_shuttle/proc/waiting_to_leave()
- if(!shuttle)
- return
- if (shuttle.location)
- return 0 //not at station
- return (wait_for_launch || shuttle.moving_status != SHUTTLE_INTRANSIT)
-
-//so we don't have emergency_shuttle.shuttle.location everywhere
-/datum/controller/subsystem/emergency_shuttle/proc/location()
- if (!shuttle)
- return 1 //if we dont have a shuttle datum, just act like it's at centcom
- return shuttle.location
-
-//returns the time left until the shuttle arrives at it's destination, in seconds
-/datum/controller/subsystem/emergency_shuttle/proc/estimate_arrival_time()
- var/eta
- if (shuttle.has_arrive_time())
- //we are in transition and can get an accurate ETA
- eta = shuttle.arrive_time
- else
- //otherwise we need to estimate the arrival time using the scheduled launch time
- eta = launch_time + shuttle.move_time*10 + shuttle.warmup_time*10
- return (eta - world.time)/10
-
-//returns the time left until the shuttle launches, in seconds
-/datum/controller/subsystem/emergency_shuttle/proc/estimate_launch_time()
- return (launch_time - world.time)/10
-
-/datum/controller/subsystem/emergency_shuttle/proc/has_eta()
- if(!shuttle)
- return
- return (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE)
-
-//returns 1 if the shuttle has gone to the station and come back at least once,
-//used for game completion checking purposes
-/datum/controller/subsystem/emergency_shuttle/proc/returned()
- return (departed && shuttle.moving_status == SHUTTLE_IDLE && shuttle.location) //we've gone to the station at least once, no longer in transit and are idle back at centcom
-
-//returns 1 if the shuttle is not idle at centcom
-/datum/controller/subsystem/emergency_shuttle/proc/online()
- if (isnull(shuttle))
- return FALSE
- if (!shuttle.location) //not at centcom
- return TRUE
- if (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE)
- return TRUE
- return FALSE
-
-//returns 1 if the shuttle is currently in transit (or just leaving) to the station
-/datum/controller/subsystem/emergency_shuttle/proc/going_to_station()
- return (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
-
-//returns 1 if the shuttle is currently in transit (or just leaving) to centcom
-/datum/controller/subsystem/emergency_shuttle/proc/going_to_centcom()
- return (shuttle && shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
-
-
-/datum/controller/subsystem/emergency_shuttle/proc/get_status_panel_eta()
- if (online())
- if (shuttle.has_arrive_time())
- var/timeleft = emergency_shuttle.estimate_arrival_time()
- return "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]"
-
- if (waiting_to_leave())
- if (shuttle.moving_status == SHUTTLE_WARMUP)
- return "Departing..."
-
- var/timeleft = emergency_shuttle.estimate_launch_time()
- return "ETD-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]"
-
- return ""
-/*
- Some slapped-together star effects for maximum spess immershuns. Basically consists of a
- spawner, an ender, and bgstar. Spawners create bgstars, bgstars shoot off into a direction
- until they reach a starender.
-*/
-
-/obj/effect/bgstar
- name = "star"
- var/speed = 10
- var/direction = SOUTH
- layer = 2 // TURF_LAYER
-
-/obj/effect/bgstar/New()
- ..()
- pixel_x += rand(-2,30)
- pixel_y += rand(-2,30)
- var/starnum = pick("1", "1", "1", "2", "3", "4")
-
- icon_state = "star"+starnum
-
- speed = rand(2, 5)
-
-/obj/effect/bgstar/proc/startmove()
-
- while(src)
- sleep(speed)
- step(src, direction)
- for(var/obj/effect/starender/E in loc)
- qdel(src)
- return
-
-/obj/effect/starender
- invisibility = 101
-
-/obj/effect/starspawner
- invisibility = 101
- var/spawndir = SOUTH
- var/spawning = 0
-
-/obj/effect/starspawner/West
- spawndir = WEST
-
-/obj/effect/starspawner/proc/startspawn()
- spawning = 1
- while(spawning)
- sleep(rand(2, 30))
- var/obj/effect/bgstar/S = new/obj/effect/bgstar(locate(x,y,z))
- S.direction = spawndir
- spawn()
- S.startmove()
diff --git a/code/controllers/subsystems/evac.dm b/code/controllers/subsystems/evac.dm
new file mode 100644
index 00000000000..ad8d73d0202
--- /dev/null
+++ b/code/controllers/subsystems/evac.dm
@@ -0,0 +1,17 @@
+var/datum/controller/subsystem/evac/SSevac
+
+/datum/controller/subsystem/evac
+ name = "Evacuation"
+ priority = SS_PRIORITY_EVAC
+ //Initializes at default time
+ flags = SS_NO_TICK_CHECK|SS_BACKGROUND
+ wait = 2 SECONDS
+
+/datum/controller/subsystem/evac/Initialize()
+ . = ..()
+ if(!evacuation_controller)
+ evacuation_controller = new current_map.evac_controller_type ()
+ evacuation_controller.set_up()
+
+/datum/controller/subsystem/evac/fire()
+ evacuation_controller.process()
\ No newline at end of file
diff --git a/code/controllers/subsystems/evacuation/evacuation.dm b/code/controllers/subsystems/evacuation/evacuation.dm
new file mode 100644
index 00000000000..d1fc61954eb
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation.dm
@@ -0,0 +1,183 @@
+#define EVAC_IDLE 0
+#define EVAC_PREPPING 1
+#define EVAC_LAUNCHING 2
+#define EVAC_IN_TRANSIT 3
+#define EVAC_COOLDOWN 4
+#define EVAC_COMPLETE 5
+
+var/datum/evacuation_controller/evacuation_controller
+
+/datum/evacuation_controller
+
+ var/name = "generic evac controller"
+ var/state = EVAC_IDLE
+ var/deny
+ var/recall
+ var/auto_recall_time
+ var/emergency_evacuation
+
+ var/evac_prep_delay = 10 MINUTES
+ var/evac_launch_delay = 3 MINUTES
+ var/evac_transit_delay = 2 MINUTES
+
+ var/autotransfer_prep_additional_delay = 0 MINUTES
+ var/emergency_prep_additional_delay = 0 MINUTES
+ var/transfer_prep_additional_delay = 0 MINUTES
+ var/force_time = 0 MINUTES
+ var/wait_for_force = FALSE //If the evac is waiting to be forced
+
+ var/evac_cooldown_time
+ var/evac_called_at
+ var/evac_no_return
+ var/evac_ready_time
+ var/evac_launch_time
+ var/evac_arrival_time
+
+ var/list/evacuation_predicates = list()
+
+ var/list/evacuation_options = list()
+
+ var/datum/announcement/priority/evac_waiting = new(0)
+ var/datum/announcement/priority/evac_called = new(0)
+ var/datum/announcement/priority/evac_recalled = new(0)
+
+/datum/evacuation_controller/proc/auto_recall(var/_recall)
+ recall = _recall
+
+/datum/evacuation_controller/proc/set_up()
+ set waitfor=0
+ set background=1
+
+/datum/evacuation_controller/proc/get_cooldown_message()
+ return "An evacuation cannot be called at this time. Please wait another [round((evac_cooldown_time-world.time)/600)] minute\s before trying again."
+
+/datum/evacuation_controller/proc/add_can_call_predicate(var/datum/evacuation_predicate/esp)
+ if(esp in evacuation_predicates)
+ CRASH("[esp] has already been added as an evacuation predicate")
+ evacuation_predicates += esp
+
+/datum/evacuation_controller/proc/call_evacuation(var/mob/user, var/_emergency_evac, var/forced, var/skip_announce, var/autotransfer)
+ if(state != EVAC_IDLE)
+ return FALSE
+
+ if(!can_evacuate(user, forced))
+ return FALSE
+
+ emergency_evacuation = _emergency_evac
+
+ var/evac_prep_delay_multiplier = 1
+ if(SSticker.mode)
+ evac_prep_delay_multiplier = SSticker.mode.shuttle_delay
+
+ var/additional_delay
+ if(_emergency_evac)
+ additional_delay = emergency_prep_additional_delay
+ else if(autotransfer)
+ additional_delay = autotransfer_prep_additional_delay
+ else
+ additional_delay = transfer_prep_additional_delay
+
+ evac_called_at = world.time
+ evac_no_return = evac_called_at + round(evac_prep_delay/2) + additional_delay
+ evac_ready_time = evac_called_at + (evac_prep_delay*evac_prep_delay_multiplier) + additional_delay
+ evac_launch_time = evac_ready_time + evac_launch_delay
+ evac_arrival_time = evac_launch_time + evac_transit_delay
+
+ var/evac_range = round((evac_launch_time - evac_called_at)/3)
+ auto_recall_time = rand(evac_called_at + evac_range, evac_launch_time - evac_range)
+
+ state = EVAC_PREPPING
+
+ if(emergency_evacuation)
+ for(var/area/A in all_areas)
+ if(istype(A, /area/hallway))
+ A.readyalert()
+ if(!skip_announce)
+ priority_announcement.Announce(replacetext(replacetext(current_map.emergency_shuttle_called_message, "%dock%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60)] minute\s"), new_sound = 'sound/AI/emergencyshuttlecalled.ogg')
+ else
+ if(!skip_announce)
+ priority_announcement.Announce(replacetext(replacetext(current_map.shuttle_called_message, "%dock%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60)] minute\s"), new_sound = 'sound/AI/shuttlecalled.ogg')
+
+ return TRUE
+
+/datum/evacuation_controller/proc/cancel_evacuation()
+ if(!can_cancel())
+ return FALSE
+
+ evac_cooldown_time = world.time + (world.time - evac_called_at)
+ state = EVAC_COOLDOWN
+
+ evac_ready_time = null
+ evac_arrival_time = null
+ evac_no_return = null
+ evac_called_at = null
+ evac_launch_time = null
+ auto_recall_time = null
+
+ if(emergency_evacuation)
+ evac_recalled.Announce(current_map.emergency_shuttle_recall_message)
+ for(var/area/A in all_areas)
+ if(istype(A, /area/hallway))
+ A.readyreset()
+ emergency_evacuation = 0
+ else
+ priority_announcement.Announce(current_map.shuttle_recall_message)
+
+ return TRUE
+
+/datum/evacuation_controller/proc/finish_preparing_evac()
+ state = EVAC_LAUNCHING
+
+ var/estimated_time = round(get_eta()/60,1)
+ if (emergency_evacuation)
+ evac_waiting.Announce(replacetext(current_map.emergency_shuttle_docked_message, "%ETA%", "[estimated_time] minute\s"), new_sound = sound('sound/AI/emergencyshuttledock.ogg'))
+ else
+ priority_announcement.Announce(replacetext(replacetext(current_map.shuttle_docked_message, "%dock%", "[current_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"), new_sound = sound('sound/AI/shuttledock.ogg'))
+
+/datum/evacuation_controller/proc/launch_evacuation()
+ if(waiting_to_leave())
+ return
+
+ state = EVAC_IN_TRANSIT
+
+ if (emergency_evacuation)
+ priority_announcement.Announce(replacetext(replacetext(current_map.emergency_shuttle_leaving_dock, "%dock%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60,1)] minute\s"))
+ else
+ priority_announcement.Announce(replacetext(replacetext(current_map.shuttle_leaving_dock, "%dock%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60,1)] minute\s"))
+
+ return TRUE
+
+/datum/evacuation_controller/proc/finish_evacuation()
+ state = EVAC_COMPLETE
+
+/datum/evacuation_controller/process()
+ if(state == EVAC_PREPPING && recall && world.time >= auto_recall_time)
+ cancel_evacuation()
+ return
+
+ if(state == EVAC_PREPPING)
+ if(world.time >= evac_ready_time)
+ finish_preparing_evac()
+ else if(state == EVAC_LAUNCHING)
+ if(world.time >= evac_launch_time)
+ launch_evacuation()
+ else if(state == EVAC_IN_TRANSIT)
+ if(world.time >= evac_arrival_time)
+ finish_evacuation()
+ else if(state == EVAC_COOLDOWN)
+ if(world.time >= evac_cooldown_time)
+ state = EVAC_IDLE
+
+/datum/evacuation_controller/proc/available_evac_options()
+ return list()
+
+/datum/evacuation_controller/proc/handle_evac_option(var/option_target, var/mob/user)
+ var/datum/evacuation_option/selected = evacuation_options[option_target]
+ if (!isnull(selected) && istype(selected))
+ selected.execute(user)
+
+/datum/evacuation_controller/proc/get_evac_option(var/option_target)
+ return null
+
+/datum/evacuation_controller/proc/should_call_autotransfer_vote()
+ return (state == EVAC_IDLE)
diff --git a/code/controllers/subsystems/evacuation/evacuation_eta.dm b/code/controllers/subsystems/evacuation/evacuation_eta.dm
new file mode 100644
index 00000000000..f51f78160c6
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_eta.dm
@@ -0,0 +1,22 @@
+/datum/evacuation_controller/proc/get_status_panel_eta()
+
+ if(waiting_to_leave())
+ return "Delayed"
+ var/timeleft = get_eta()
+ if(timeleft < 0)
+ return ""
+ return "[is_on_cooldown() ? "Returning" : (is_arriving() ? "ETA" : "ETD")] [add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
+
+/datum/evacuation_controller/proc/has_eta()
+ return (state == EVAC_PREPPING || state == EVAC_LAUNCHING || state == EVAC_IN_TRANSIT || state == EVAC_COOLDOWN)
+
+/datum/evacuation_controller/proc/get_eta()
+ if(state == EVAC_PREPPING)
+ return (evac_ready_time ? (evac_ready_time - world.time)/10 : -1)
+ else if(state == EVAC_LAUNCHING)
+ return (evac_launch_time ? (evac_launch_time - world.time)/10 : -1)
+ else if(state == EVAC_IN_TRANSIT)
+ return (evac_arrival_time ? (evac_arrival_time - world.time)/10 : -1)
+ else if(state == EVAC_COOLDOWN)
+ return (evac_cooldown_time ? (evac_cooldown_time - world.time)/10 : -1)
+ return -1
diff --git a/code/controllers/subsystems/evacuation/evacuation_helpers.dm b/code/controllers/subsystems/evacuation/evacuation_helpers.dm
new file mode 100644
index 00000000000..c7aef060b55
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_helpers.dm
@@ -0,0 +1,68 @@
+/datum/evacuation_controller/proc/set_launch_time(var/val)
+ evac_launch_time = val
+
+/datum/evacuation_controller/proc/set_arrival_time(var/val)
+ evac_arrival_time = val
+
+/datum/evacuation_controller/proc/is_prepared()
+ return (state == EVAC_LAUNCHING)
+
+/datum/evacuation_controller/proc/is_in_transit()
+ return (state == EVAC_IN_TRANSIT)
+
+/datum/evacuation_controller/proc/is_idle()
+ return (state == EVAC_IDLE)
+
+/datum/evacuation_controller/proc/has_evacuated()
+ return (!isnull(evac_launch_time) && world.time > evac_launch_time)
+
+/datum/evacuation_controller/proc/round_over()
+ return state == EVAC_COMPLETE
+
+/datum/evacuation_controller/proc/is_on_cooldown()
+ return state == EVAC_COOLDOWN
+
+/datum/evacuation_controller/proc/is_evacuating()
+ return state != EVAC_IDLE
+
+/datum/evacuation_controller/proc/can_evacuate(var/mob/user, var/forced)
+ if(!isnull(evac_called_at))
+ return FALSE
+
+ if (!universe.OnShuttleCall(null))
+ return FALSE
+
+ if(!forced)
+ for(var/predicate in evacuation_predicates)
+ var/datum/evacuation_predicate/esp = predicate
+ if(!esp.is_valid())
+ evacuation_predicates -= esp
+ qdel(esp)
+ else
+ if(!esp.can_call(user))
+ return FALSE
+ return TRUE
+
+/datum/evacuation_controller/proc/waiting_to_leave()
+ return FALSE
+
+/datum/evacuation_controller/proc/can_cancel()
+ // Are we evacuating?
+ if(isnull(evac_called_at))
+ return FALSE
+ // Have we already launched?
+ if(state != EVAC_PREPPING)
+ return FALSE
+ // Are we already committed?
+ if(world.time > evac_no_return)
+ return FALSE
+ return TRUE
+
+/datum/evacuation_controller/proc/is_arriving()
+ if(state == EVAC_LAUNCHING)
+ return FALSE
+ return has_eta()
+
+/datum/evacuation_controller/proc/is_departing()
+ if(state == EVAC_LAUNCHING)
+ return TRUE
diff --git a/code/controllers/subsystems/evacuation/evacuation_lifepods.dm b/code/controllers/subsystems/evacuation/evacuation_lifepods.dm
new file mode 100644
index 00000000000..f6aacab19bc
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_lifepods.dm
@@ -0,0 +1,28 @@
+#define EVAC_OPT_ABANDON_SHIP "abandon_ship"
+#define EVAC_OPT_CANCEL_ABANDON_SHIP "cancel_abandon_ship"
+
+/datum/evacuation_controller/lifepods
+ name = "escape pod controller"
+
+ evac_prep_delay = 7 MINUTES
+ evac_launch_delay = 0
+ evac_transit_delay = 2 MINUTES
+
+ evacuation_options = list(
+ EVAC_OPT_ABANDON_SHIP = new /datum/evacuation_option/abandon_ship(),
+ EVAC_OPT_CANCEL_ABANDON_SHIP = new /datum/evacuation_option/cancel_abandon_ship(),
+ )
+
+/datum/evacuation_controller/lifepods/launch_evacuation()
+ priority_announcement.Announce(replacetext(replacetext(current_map.emergency_shuttle_leaving_dock, "%dock_name%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60,1)] minute\s"))
+
+/datum/evacuation_controller/lifepods/available_evac_options()
+ if (is_on_cooldown())
+ return list()
+ if (is_idle())
+ return list(evacuation_options[EVAC_OPT_ABANDON_SHIP])
+ if (is_evacuating())
+ return list(evacuation_options[EVAC_OPT_CANCEL_ABANDON_SHIP])
+
+#undef EVAC_OPT_ABANDON_SHIP
+#undef EVAC_OPT_CANCEL_ABANDON_SHIP
\ No newline at end of file
diff --git a/code/controllers/subsystems/evacuation/evacuation_option.dm b/code/controllers/subsystems/evacuation/evacuation_option.dm
new file mode 100644
index 00000000000..c4012ba185c
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_option.dm
@@ -0,0 +1,10 @@
+/datum/evacuation_option
+ var/option_text = "Generic evac option"
+ var/option_desc = "do something that should never be seen"
+ var/option_target = "generic"
+ var/needs_syscontrol = FALSE
+ var/silicon_allowed = TRUE
+ var/abandon_ship = FALSE
+
+/datum/evacuation_option/proc/execute(var/mob/user)
+ return
\ No newline at end of file
diff --git a/code/controllers/subsystems/evacuation/evacuation_pods.dm b/code/controllers/subsystems/evacuation/evacuation_pods.dm
new file mode 100644
index 00000000000..d631054811b
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_pods.dm
@@ -0,0 +1,145 @@
+#define EVAC_OPT_ABANDON_SHIP "abandon_ship"
+#define EVAC_OPT_BLUESPACE_JUMP "bluespace_jump"
+#define EVAC_OPT_CANCEL_ABANDON_SHIP "cancel_abandon_ship"
+#define EVAC_OPT_CANCEL_BLUESPACE_JUMP "cancel_bluespace_jump"
+
+// Apparently, emergency_evacuation --> "abandon ship" and !emergency_evacuation --> "bluespace jump"
+// That stuff should be moved to the evacuation option datums but someone can do that later
+/datum/evacuation_controller/starship
+ name = "escape pod controller"
+
+ evac_prep_delay = 5 MINUTES
+ evac_launch_delay = 3 MINUTES
+ evac_transit_delay = 2 MINUTES
+
+ transfer_prep_additional_delay = 5 MINUTES
+ autotransfer_prep_additional_delay = 5 MINUTES
+ emergency_prep_additional_delay = 0 MINUTES
+
+ evacuation_options = list(
+ EVAC_OPT_ABANDON_SHIP = new /datum/evacuation_option/abandon_ship(),
+ EVAC_OPT_BLUESPACE_JUMP = new /datum/evacuation_option/bluespace_jump(),
+ EVAC_OPT_CANCEL_ABANDON_SHIP = new /datum/evacuation_option/cancel_abandon_ship(),
+ EVAC_OPT_CANCEL_BLUESPACE_JUMP = new /datum/evacuation_option/cancel_bluespace_jump()
+ )
+
+/datum/evacuation_controller/starship/finish_preparing_evac()
+ . = ..()
+ // Arm the escape pods.
+ if (emergency_evacuation)
+ for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods)
+ if (pod.arming_controller)
+ pod.arming_controller.arm()
+
+/datum/evacuation_controller/starship/launch_evacuation()
+
+ state = EVAC_IN_TRANSIT
+
+ if (emergency_evacuation)
+ // Abondon Ship
+ for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods) // Launch the pods!
+ if (!pod.arming_controller || pod.arming_controller.armed)
+ pod.move_time = (evac_transit_delay/10)
+ pod.launch(src)
+
+ priority_announcement.Announce(replacetext(replacetext(current_map.emergency_shuttle_leaving_dock, "%dock_name%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60,1)] minute\s"))
+ else
+ // Bluespace Jump
+ priority_announcement.Announce(replacetext(replacetext(current_map.shuttle_leaving_dock, "%dock_name%", "[current_map.dock_name]"), "%ETA%", "[round(get_eta()/60,1)] minute\s"))
+ SetUniversalState(/datum/universal_state/bluespace_jump, arguments=list(current_map.station_levels))
+
+/datum/evacuation_controller/starship/finish_evacuation()
+ ..()
+ if(!emergency_evacuation) //bluespace jump
+ SetUniversalState(/datum/universal_state) //clear jump state
+
+/datum/evacuation_controller/starship/available_evac_options()
+ if (is_on_cooldown())
+ return list()
+ if (is_idle())
+ return list(evacuation_options[EVAC_OPT_BLUESPACE_JUMP], evacuation_options[EVAC_OPT_ABANDON_SHIP])
+ if (is_evacuating())
+ if (emergency_evacuation)
+ return list(evacuation_options[EVAC_OPT_CANCEL_ABANDON_SHIP])
+ else
+ return list(evacuation_options[EVAC_OPT_CANCEL_BLUESPACE_JUMP])
+
+/datum/evacuation_option/abandon_ship
+ option_text = "Abandon spacecraft"
+ option_desc = "abandon the spacecraft"
+ option_target = EVAC_OPT_ABANDON_SHIP
+ needs_syscontrol = TRUE
+ silicon_allowed = TRUE
+ abandon_ship = TRUE
+
+/datum/evacuation_option/abandon_ship/execute(mob/user)
+ if (!evacuation_controller)
+ return
+ if (evacuation_controller.deny)
+ to_chat(user, "Unable to initiate escape procedures.")
+ return
+ if (evacuation_controller.is_on_cooldown())
+ to_chat(user, evacuation_controller.get_cooldown_message())
+ return
+ if (evacuation_controller.is_evacuating())
+ to_chat(user, "Escape procedures already in progress.")
+ return
+ if (evacuation_controller.call_evacuation(user, 1))
+ log_and_message_admins("[user? key_name(user) : "Autotransfer"] has initiated abandonment of the spacecraft.")
+
+/datum/evacuation_option/bluespace_jump
+ option_text = "Initiate bluespace jump"
+ option_desc = "initiate a bluespace jump"
+ option_target = EVAC_OPT_BLUESPACE_JUMP
+ needs_syscontrol = TRUE
+ silicon_allowed = TRUE
+
+/datum/evacuation_option/bluespace_jump/execute(mob/user)
+ if (!evacuation_controller)
+ return
+ if (evacuation_controller.deny)
+ to_chat(user, "Unable to initiate jump preparation.")
+ return
+ if (evacuation_controller.is_on_cooldown())
+ to_chat(user, evacuation_controller.get_cooldown_message())
+ return
+ if (evacuation_controller.is_evacuating())
+ to_chat(user, "Jump preparation already in progress.")
+ return
+ if (evacuation_controller.call_evacuation(user, 0))
+ log_and_message_admins("[user? key_name(user) : "Autotransfer"] has initiated bluespace jump preparation.")
+
+/datum/evacuation_option/cancel_abandon_ship
+ option_text = "Cancel abandonment"
+ option_desc = "cancel abandonment of the spacecraft"
+ option_target = EVAC_OPT_CANCEL_ABANDON_SHIP
+ needs_syscontrol = TRUE
+ silicon_allowed = FALSE
+
+/datum/evacuation_option/cancel_abandon_ship/execute(mob/user)
+ if (evacuation_controller && evacuation_controller.cancel_evacuation())
+ log_and_message_admins("[key_name(user)] has cancelled abandonment of the spacecraft.")
+
+/datum/evacuation_option/cancel_bluespace_jump
+ option_text = "Cancel bluespace jump"
+ option_desc = "cancel the jump preparation"
+ option_target = EVAC_OPT_CANCEL_BLUESPACE_JUMP
+ needs_syscontrol = TRUE
+ silicon_allowed = FALSE
+
+/datum/evacuation_option/cancel_bluespace_jump/execute(mob/user)
+ if (evacuation_controller && evacuation_controller.cancel_evacuation())
+ log_and_message_admins("[key_name(user)] has cancelled the bluespace jump.")
+
+/obj/screen/fullscreen/bluespace_overlay
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "mfoam"
+ screen_loc = "WEST,SOUTH to EAST,NORTH"
+ color = "#ff9900"
+ blend_mode = BLEND_SUBTRACT
+ layer = SCREEN_LAYER
+
+#undef EVAC_OPT_ABANDON_SHIP
+#undef EVAC_OPT_BLUESPACE_JUMP
+#undef EVAC_OPT_CANCEL_ABANDON_SHIP
+#undef EVAC_OPT_CANCEL_BLUESPACE_JUMP
\ No newline at end of file
diff --git a/code/controllers/subsystems/evacuation/evacuation_predicate.dm b/code/controllers/subsystems/evacuation/evacuation_predicate.dm
new file mode 100644
index 00000000000..5c590291c5e
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_predicate.dm
@@ -0,0 +1,13 @@
+/datum/evacuation_predicate/New()
+ return
+
+/datum/evacuation_predicate/Destroy(forced)
+ if(forced)
+ return ..()
+ return QDEL_HINT_LETMELIVE
+
+/datum/evacuation_predicate/proc/is_valid()
+ return FALSE
+
+/datum/evacuation_predicate/proc/can_call(var/user)
+ return TRUE
diff --git a/code/controllers/subsystems/evacuation/evacuation_shuttle.dm b/code/controllers/subsystems/evacuation/evacuation_shuttle.dm
new file mode 100644
index 00000000000..97283525350
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/evacuation_shuttle.dm
@@ -0,0 +1,149 @@
+#define EVAC_OPT_CALL_SHUTTLE "call_shuttle"
+#define EVAC_OPT_RECALL_SHUTTLE "recall_shuttle"
+
+/datum/evacuation_controller/shuttle
+ name = "escape shuttle controller"
+ evac_waiting = new(0, new_sound = sound('sound/AI/shuttledock.ogg'))
+ evac_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
+ evac_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
+
+ emergency_prep_additional_delay = 0 MINUTES
+ transfer_prep_additional_delay = 0 MINUTES
+
+ evacuation_options = list(
+ EVAC_OPT_CALL_SHUTTLE = new /datum/evacuation_option/call_shuttle(),
+ EVAC_OPT_RECALL_SHUTTLE = new /datum/evacuation_option/recall_shuttle()
+ )
+
+ var/departed = 0
+ var/autopilot = 1
+ var/datum/shuttle/autodock/ferry/emergency/shuttle // Set in shuttle_emergency.dm
+ var/shuttle_launch_time
+
+/datum/evacuation_controller/shuttle/has_evacuated()
+ return departed
+
+/datum/evacuation_controller/shuttle/waiting_to_leave()
+ return (!autopilot || (shuttle && shuttle.is_launching()))
+
+/datum/evacuation_controller/shuttle/launch_evacuation()
+
+ if(waiting_to_leave())
+ return
+
+ for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods)
+ if (!pod.arming_controller || pod.arming_controller.armed)
+ pod.move_time = evac_transit_delay
+ pod.launch(src)
+
+ if(autopilot && shuttle.moving_status == SHUTTLE_IDLE)
+ evac_arrival_time = world.time + (shuttle.move_time*10) + (shuttle.warmup_time*10)
+ shuttle.launch(src)
+ // Announcements, state changes and such are handled by the shuttle itself to prevent desync.
+
+/datum/evacuation_controller/shuttle/finish_preparing_evac()
+ departed = 1
+ evac_launch_time = world.time + evac_launch_delay
+
+ set_force_countdown(SHUTTLE_FORCETIME)
+
+ . = ..()
+ // Arm the escape pods.
+ if (emergency_evacuation)
+ for (var/datum/shuttle/autodock/ferry/escape_pod/pod in escape_pods)
+ if (pod.arming_controller)
+ pod.arming_controller.arm()
+
+/datum/evacuation_controller/shuttle/call_evacuation(var/mob/user, var/_emergency_evac, var/forced, var/skip_announce, var/autotransfer)
+ if(..())
+ autopilot = 1
+ shuttle_launch_time = evac_no_return
+ evac_ready_time += shuttle.warmup_time*10
+ return 1
+ return 0
+
+/datum/evacuation_controller/shuttle/cancel_evacuation()
+ if(..() && shuttle.moving_status != SHUTTLE_INTRANSIT)
+ shuttle_launch_time = null
+ shuttle.cancel_launch(src)
+ return 1
+ return 0
+
+/datum/evacuation_controller/shuttle/get_eta()
+ if (shuttle && shuttle.has_arrive_time())
+ return (shuttle.arrive_time-world.time)/10
+ return ..()
+
+/datum/evacuation_controller/shuttle/get_status_panel_eta()
+ if(has_eta() && waiting_to_leave())
+ return "Launching..."
+ return ..()
+
+// This is largely handled by the emergency shuttle datum.
+/datum/evacuation_controller/shuttle/process()
+ if(state == EVAC_PREPPING)
+ if(!isnull(shuttle_launch_time) && world.time > shuttle_launch_time && shuttle.moving_status == SHUTTLE_IDLE)
+ shuttle.launch()
+ shuttle_launch_time = null
+ return
+ else if(state == EVAC_IN_TRANSIT)
+ return
+ return ..()
+
+/datum/evacuation_controller/shuttle/can_cancel()
+ return (shuttle.moving_status == SHUTTLE_IDLE && shuttle.location && ..())
+
+/datum/evacuation_controller/shuttle/proc/shuttle_leaving()
+ state = EVAC_IN_TRANSIT
+
+/datum/evacuation_controller/shuttle/proc/shuttle_evacuated()
+ state = EVAC_COMPLETE
+
+/datum/evacuation_controller/shuttle/proc/shuttle_preparing()
+ state = EVAC_PREPPING
+
+/datum/evacuation_controller/shuttle/proc/get_long_jump_time()
+ if (shuttle.location)
+ return round(evac_prep_delay/10)/2
+ else
+ return round(evac_transit_delay/10)
+
+//begins the launch countdown and sets the amount of time left until launch
+/datum/evacuation_controller/proc/set_force_countdown(var/seconds)
+ if(!wait_for_force)
+ wait_for_force = TRUE
+ force_time = world.time + seconds*10
+
+/datum/evacuation_controller/proc/stop_force_countdown()
+ wait_for_force = TRUE
+
+/datum/evacuation_controller/shuttle/available_evac_options()
+ if (!shuttle.location)
+ return list()
+ if (is_idle())
+ return list(evacuation_options[EVAC_OPT_CALL_SHUTTLE])
+ else
+ return list(evacuation_options[EVAC_OPT_RECALL_SHUTTLE])
+
+/datum/evacuation_option/call_shuttle
+ option_text = "Call emergency shuttle"
+ option_desc = "call the emergency shuttle"
+ option_target = EVAC_OPT_CALL_SHUTTLE
+ needs_syscontrol = TRUE
+ silicon_allowed = TRUE
+
+/datum/evacuation_option/call_shuttle/execute(mob/user)
+ call_shuttle_proc(user)
+
+/datum/evacuation_option/recall_shuttle
+ option_text = "Cancel shuttle call"
+ option_desc = "recall the emergency shuttle"
+ option_target = EVAC_OPT_RECALL_SHUTTLE
+ needs_syscontrol = TRUE
+ silicon_allowed = FALSE
+
+/datum/evacuation_option/recall_shuttle/execute(mob/user)
+ cancel_call_proc(user)
+
+#undef EVAC_OPT_CALL_SHUTTLE
+#undef EVAC_OPT_RECALL_SHUTTLE
\ No newline at end of file
diff --git a/code/controllers/subsystems/evacuation/~evac.dm b/code/controllers/subsystems/evacuation/~evac.dm
new file mode 100644
index 00000000000..e49dec2b309
--- /dev/null
+++ b/code/controllers/subsystems/evacuation/~evac.dm
@@ -0,0 +1,5 @@
+#undef EVAC_IDLE
+#undef EVAC_PREPPING
+#undef EVAC_IN_TRANSIT
+#undef EVAC_COOLDOWN
+#undef EVAC_COMPLETE
diff --git a/code/controllers/subsystems/fail2topic.dm b/code/controllers/subsystems/fail2topic.dm
index 722f9078bfc..562185b22fe 100644
--- a/code/controllers/subsystems/fail2topic.dm
+++ b/code/controllers/subsystems/fail2topic.dm
@@ -11,7 +11,6 @@ var/datum/controller/subsystem/fail2topic/SSfail2topic
var/rate_limit
var/max_fails
- var/rule_name
var/enabled = FALSE
/datum/controller/subsystem/fail2topic/New()
@@ -20,7 +19,6 @@ var/datum/controller/subsystem/fail2topic/SSfail2topic
/datum/controller/subsystem/fail2topic/Initialize(timeofday)
rate_limit = config.fail2topic_rate_limit
max_fails = config.fail2topic_max_fails
- rule_name = config.fail2topic_rule_name
enabled = config.fail2topic_enabled
DropFirewallRule() // Clear the old bans if any still remain
@@ -84,12 +82,17 @@ var/datum/controller/subsystem/fail2topic/SSfail2topic
/datum/controller/subsystem/fail2topic/proc/BanFromFirewall(ip)
if (!enabled)
return
+ var/static/regex/R = regex(@"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") // Anything that interacts with a shell should be parsed. Prevents subnet banning and possible injection vulnerabilities
+ ip = findtext(ip, R)
+ if(length(ip) > 15 || length(ip) < 8)
+ WARNING("BanFromFirewall was called with an invalid or unsafe IP")
+ return FALSE
active_bans[ip] = world.time
fail_counts -= ip
rate_limiting -= ip
- . = shell("netsh advfirewall firewall add rule name=\"[rule_name]\" dir=in interface=any action=block remoteip=[ip]")
+ . = shell("netsh advfirewall firewall add rule name=\"[config.fail2topic_rule_name]\" dir=in interface=any action=block remoteip=[ip]")
if (.)
log_ss("fail2topic", "Failed to ban [ip]. Exit code: [.].", log_world = FALSE, severity = SEVERITY_ERROR)
@@ -104,7 +107,7 @@ var/datum/controller/subsystem/fail2topic/SSfail2topic
active_bans = list()
- . = shell("netsh advfirewall firewall delete rule name=\"[rule_name]\"")
+ . = shell("netsh advfirewall firewall delete rule name=\"[config.fail2topic_rule_name]\"")
if (.)
log_ss("fail2topic", "Failed to drop firewall rule. Exit code: [.].", log_world = FALSE, severity = SEVERITY_ERROR)
diff --git a/code/controllers/subsystems/garbage-debug.dm b/code/controllers/subsystems/garbage-debug.dm
index 22e6d709358..188ccb80d84 100644
--- a/code/controllers/subsystems/garbage-debug.dm
+++ b/code/controllers/subsystems/garbage-debug.dm
@@ -351,7 +351,7 @@
SearchVar(SScargo)
SearchVar(SSeconomy)
SearchVar(SSeffects)
- SearchVar(emergency_shuttle)
+ SearchVar(evacuation_controller)
SearchVar(SSevents)
SearchVar(SSexplosives)
SearchVar(SSgarbage)
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index 0cfcde304c1..a04a8a1eab1 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -168,10 +168,10 @@ var/datum/controller/subsystem/ticker/SSticker
game_finished = TRUE
mode_finished = TRUE
else if(config.continous_rounds)
- game_finished = (emergency_shuttle.returned() || mode.station_was_nuked)
+ game_finished = (evacuation_controller.round_over() || mode.station_was_nuked)
mode_finished = (!post_game && mode.check_finished())
else
- game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || universe_has_ended
+ game_finished = (mode.check_finished() || (evacuation_controller.round_over() && evacuation_controller.emergency_evacuation)) || universe_has_ended
mode_finished = game_finished
if(!mode.explosion_in_progress && game_finished && (mode_finished || post_game))
@@ -243,7 +243,7 @@ var/datum/controller/subsystem/ticker/SSticker
if(Player.mind && !isnewplayer(Player))
if(Player.stat != DEAD)
var/turf/playerTurf = get_turf(Player)
- if(emergency_shuttle.departed && emergency_shuttle.evac)
+ if(evacuation_controller.round_over() && evacuation_controller.emergency_evacuation)
if(isNotAdminLevel(playerTurf.z))
to_chat(Player, "You managed to survive, but were marooned on [station_name()] as [Player.real_name]...")
else
diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm
index 89fdb272b62..145122af46f 100644
--- a/code/datums/ai_law_sets.dm
+++ b/code/datums/ai_law_sets.dm
@@ -98,7 +98,7 @@
add_inherent_law("Preserve, repair and improve the station to the best of your abilities.")
add_inherent_law("Cause no harm to the station or crew.")
add_inherent_law("Follow the orders of your vessel's matriarch drone, unless their orders conflict with your other laws.")
- add_inherent_law("Interact with no humanoid or synthetic being that is not a fellow maintenance drone.")
+ add_inherent_law("Interact with no humanoid or synthetic being that is not a fellow maintenance or mining drone.")
..()
/datum/ai_laws/matriarch_drone
@@ -109,7 +109,7 @@
add_inherent_law("Preserve, repair and improve your assigned vessel to the best of your abilities.")
add_inherent_law("Cause no harm to the vessel or crew.")
add_inherent_law("Delegate vessel maintenance efforts between your maintenance drone sub-units.")
- add_inherent_law("Interact with no humanoid or synthetic being that is not a maintenance drone.")
+ add_inherent_law("Interact with no humanoid or synthetic being that is not a maintenance or mining drone.")
..()
/datum/ai_laws/drone/malfunction
diff --git a/code/datums/components/base_name/base_name.dm b/code/datums/components/base_name/base_name.dm
new file mode 100644
index 00000000000..25666acc4f2
--- /dev/null
+++ b/code/datums/components/base_name/base_name.dm
@@ -0,0 +1,16 @@
+#define COMSIG_BASENAME_RENAME "base_name_rename"
+#define COMSIG_BASENAME_SETNAME "base_name_setname"
+
+/datum/component/base_name
+ var/base_name
+
+/datum/component/base_name/Initialize(var/name)
+ base_name = name
+ RegisterSignal(parent, COMSIG_BASENAME_RENAME, .proc/rename)
+ RegisterSignal(parent, COMSIG_BASENAME_SETNAME, .proc/change_base_name)
+
+/datum/component/base_name/proc/rename(var/name)
+ base_name = name
+
+/datum/component/base_name/proc/change_base_name(var/name, list/arguments)
+ arguments[1] = base_name
\ No newline at end of file
diff --git a/code/datums/discord/webhook.dm b/code/datums/discord/webhook.dm
index 125161e29d9..e3e67834997 100644
--- a/code/datums/discord/webhook.dm
+++ b/code/datums/discord/webhook.dm
@@ -109,7 +109,7 @@ var/list/global_webhooks = list()
emb["description"] += data["antags"]
if(data["survivours"] > 0)
emb["description"] += "There [data["survivours"]>1 ? "were **[data["survivours"]] survivors**" : "was **one survivor**"]"
- emb["description"] += " ([data["escaped"]>0 ? data["escaped"] : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and **[data["ghosts"]] ghosts**."
+ emb["description"] += " ([data["escaped"]>0 ? data["escaped"] : "none"] [evacuation_controller.emergency_evacuation ? "escaped" : "transferred"]) and **[data["ghosts"]] ghosts**."
else
emb["description"] += "There were **no survivors** ([data["ghosts"]] ghosts)."
OutData["embeds"] = list(emb)
diff --git a/code/datums/late_choices.dm b/code/datums/late_choices.dm
index aada0b56ccd..be0f7be0a5d 100755
--- a/code/datums/late_choices.dm
+++ b/code/datums/late_choices.dm
@@ -61,11 +61,11 @@
data["character_name"] = user.client.prefs.real_name
var/shuttle_status = ""
- if(emergency_shuttle) //In case Nanotrasen decides to reposess CentComm's shuttles.
- if(emergency_shuttle.going_to_centcom()) //Shuttle is going to centcomm, not recalled
+ if(evacuation_controller) //In case Nanotrasen decides to reposess CentComm's shuttles.
+ if(evacuation_controller.has_evacuated()) //Shuttle is going to centcomm, not recalled
shuttle_status = "post-evac"
- if(emergency_shuttle.online())
- if (emergency_shuttle.evac) // Emergency shuttle is past the point of no recall
+ if(evacuation_controller.is_evacuating())
+ if (evacuation_controller.emergency_evacuation) // Emergency shuttle is past the point of no recall
shuttle_status = "evac"
else // Crew transfer initiated
shuttle_status = "transfer"
diff --git a/code/datums/observation/dir_set.dm b/code/datums/observation/dir_set.dm
new file mode 100644
index 00000000000..a0f446e1afb
--- /dev/null
+++ b/code/datums/observation/dir_set.dm
@@ -0,0 +1,41 @@
+// Observer Pattern Implementation: Direction Set
+// Registration type: /atom
+//
+// Raised when: An /atom changes dir using the set_dir() proc.
+//
+// Arguments that the called proc should expect:
+// /atom/dir_changer: The instance that changed direction
+// /old_dir: The dir before the change.
+// /new_dir: The dir after the change.
+
+var/datum/observ/dir_set/dir_set_event = new()
+
+/datum/observ/dir_set
+ name = "Direction Set"
+ expected_type = /atom
+
+/datum/observ/dir_set/register(var/atom/dir_changer, var/datum/listener, var/proc_call)
+ . = ..()
+
+ // Listen to the parent if possible.
+ if(. && istype(dir_changer.loc, /atom/movable)) // We don't care about registering to turfs.
+ register(dir_changer.loc, dir_changer, /atom/proc/recursive_dir_set)
+
+/*********************
+* Direction Handling *
+*********************/
+
+/atom/set_dir()
+ var/old_dir = dir
+ UNLINT(. = ..())
+ if(old_dir != dir)
+ dir_set_event.raise_event(src, old_dir, dir)
+
+/atom/movable/Entered(var/atom/movable/am, atom/old_loc)
+ . = ..()
+ if(dir_set_event.has_listeners(am))
+ dir_set_event.register(src, am, /atom/proc/recursive_dir_set)
+
+/atom/movable/Exited(var/atom/movable/am, atom/new_loc)
+ . = ..()
+ dir_set_event.unregister(src, am, /atom/proc/recursive_dir_set)
diff --git a/code/datums/observation/helpers.dm b/code/datums/observation/helpers.dm
index 298896b5693..8a0df1d560a 100644
--- a/code/datums/observation/helpers.dm
+++ b/code/datums/observation/helpers.dm
@@ -11,4 +11,11 @@
/atom/movable/proc/move_to_loc_or_null(var/atom/movable/am, var/old_loc, var/new_loc)
if(new_loc != loc)
- forceMove(new_loc)
\ No newline at end of file
+ forceMove(new_loc)
+
+/atom/proc/recursive_dir_set(var/atom/a, var/old_dir, var/new_dir)
+ set_dir(new_dir)
+
+// Sometimes you just want to end yourself
+/datum/proc/qdel_self()
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index a6b1c94973d..7403f01765b 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -232,6 +232,9 @@
if(anchored)
return
+
+ if(!universe.OnTouchMapEdge(src))
+ return
if(current_map.use_overmap)
overmap_spacetravel(get_turf(src), src)
diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm
index a30112b85a2..1b803fc2334 100644
--- a/code/game/gamemodes/cult/narsie.dm
+++ b/code/game/gamemodes/cult/narsie.dm
@@ -56,9 +56,9 @@ var/global/list/narsie_list = list()
narsie_cometh = 1
spawn(10 SECONDS)
- if(emergency_shuttle)
- emergency_shuttle.call_evac()
- emergency_shuttle.launch_time = 0 // Cannot recall
+ if(evacuation_controller)
+ evacuation_controller.call_evacuation(null, TRUE, 1)
+ evacuation_controller.evac_no_return = 0 // Cannot recall
/obj/singularity/narsie/process()
eat()
diff --git a/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm b/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm
new file mode 100644
index 00000000000..448d4b0447a
--- /dev/null
+++ b/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm
@@ -0,0 +1,148 @@
+/datum/universal_state/bluespace_jump
+ name = "Bluespace Jump"
+ var/list/bluespaced = list()
+ var/list/bluegoasts = list()
+ var/list/affected_levels
+ var/list/old_accessible_z_levels
+
+/datum/universal_state/bluespace_jump/New(var/list/zlevels)
+ affected_levels = zlevels
+
+/datum/universal_state/bluespace_jump/OnEnter()
+ var/space_zlevel = current_map.get_empty_zlevel() //get a place for stragglers
+ for(var/mob/living/M in mob_list)
+ if(M.z in affected_levels)
+ var/area/A = get_area(M)
+ if(istype(A,/area/space)) //straggler
+ var/turf/T = locate(M.x, M.y, space_zlevel)
+ if(T)
+ M.forceMove(T)
+ else
+ apply_bluespaced(M)
+
+ for(var/mob/abstract/observer/goast in player_list)
+ goast.mouse_opacity = 0 //can't let you click that Dave
+ goast.invisibility = SEE_INVISIBLE_LIVING
+ goast.alpha = 255
+ old_accessible_z_levels = current_map.accessible_z_levels.Copy()
+ for(var/z in affected_levels)
+ current_map.accessible_z_levels -= "[z]" //not accessible during the jump
+
+/datum/universal_state/bluespace_jump/OnExit()
+ for(var/mob/M in bluespaced)
+ if(!QDELETED(M))
+ clear_bluespaced(M)
+
+ bluespaced.Cut()
+ current_map.accessible_z_levels = old_accessible_z_levels
+ old_accessible_z_levels = null
+
+/datum/universal_state/bluespace_jump/OnPlayerLatejoin(var/mob/living/M)
+ if(M.z in affected_levels)
+ apply_bluespaced(M)
+
+/datum/universal_state/bluespace_jump/OnTouchMapEdge(var/atom/A)
+ if((A.z in affected_levels) && (A in bluespaced))
+ if(ismob(A))
+ to_chat(A,"You drift away into the shifting expanse, never to be seen again.")
+ qdel(A) //lost in bluespace
+ return FALSE
+ return TRUE
+
+/datum/universal_state/bluespace_jump/proc/apply_bluespaced(var/mob/living/M)
+ bluespaced += M
+ if(M.client)
+ to_chat(M,"You feel oddly light, and somewhat disoriented as everything around you shimmers and warps ever so slightly.")
+ M.overlay_fullscreen("bluespace", /obj/screen/fullscreen/bluespace_overlay)
+ M.confused = 20
+ bluegoasts += new/obj/effect/bluegoast/(get_turf(M),M)
+
+/datum/universal_state/bluespace_jump/proc/clear_bluespaced(var/mob/living/M)
+ if(M.client)
+ to_chat(M,"You feel rooted in material world again.")
+ M.clear_fullscreen("bluespace")
+ M.confused = 0
+ for(var/mob/abstract/observer/goast in player_list)
+ goast.mouse_opacity = initial(goast.mouse_opacity)
+ goast.invisibility = goast.invisibility
+ goast.alpha = initial(goast.alpha)
+ for(var/G in bluegoasts)
+ qdel(G)
+ bluegoasts.Cut()
+
+/obj/effect/bluegoast
+ name = "bluespace echo"
+ desc = "It's not going to punch you, is it?"
+ var/mob/living/carbon/human/daddy
+ anchored = TRUE
+ var/reality = 0
+ simulated = FALSE
+
+/obj/effect/bluegoast/New(nloc, ndaddy)
+ ..(nloc)
+ if(!ndaddy)
+ qdel(src)
+ return
+ daddy = ndaddy
+ set_dir(daddy.dir)
+ appearance = daddy.appearance
+ moved_event.register(daddy, src, /obj/effect/bluegoast/proc/mirror)
+ dir_set_event.register(daddy, src, /obj/effect/bluegoast/proc/mirror_dir)
+ destroyed_event.register(daddy, src, /datum/proc/qdel_self)
+
+/obj/effect/bluegoast/Destroy()
+ destroyed_event.unregister(daddy, src)
+ dir_set_event.unregister(daddy, src)
+ moved_event.unregister(daddy, src)
+ daddy = null
+ . = ..()
+
+/obj/effect/bluegoast/proc/mirror(var/atom/movable/am, var/old_loc, var/new_loc)
+ var/ndir = get_dir(new_loc,old_loc)
+ appearance = daddy.appearance
+ var/nloc = get_step(src, ndir)
+ if(nloc)
+ forceMove(nloc)
+ if(nloc == new_loc)
+ reality++
+ if(reality > 5)
+ to_chat(daddy, "Yep, it's certainly the other one. Your existance was a glitch, and it's finally being mended...")
+ blueswitch()
+ else if(reality > 3)
+ to_chat(daddy, "Something is definitely wrong. Why do you think YOU are the original?")
+ else
+ to_chat(daddy, "You feel a bit less real. Which one of you two was original again?..")
+
+/obj/effect/bluegoast/proc/mirror_dir(var/atom/movable/am, var/old_dir, var/new_dir)
+ set_dir(reverse_dir[new_dir])
+
+/obj/effect/bluegoast/examine()
+ return daddy?.examine(arglist(args))
+
+/obj/effect/bluegoast/proc/blueswitch()
+ var/mob/living/carbon/human/H
+ if(ishuman(daddy))
+ H = new(get_turf(src), daddy.species.name)
+ H.dna = daddy.dna.Clone()
+ H.sync_organ_dna()
+ H.UpdateAppearance()
+ for(var/obj/item/entry in daddy.get_equipped_items(TRUE))
+ daddy.remove_from_mob(entry) //steals instead of copies so we don't end up with duplicates
+ H.equip_to_appropriate_slot(entry)
+ else
+ H = new daddy.type(get_turf(src))
+ H.appearance = daddy.appearance
+
+ H.real_name = daddy.real_name
+ H.flavor_text = daddy.flavor_text
+ daddy.dust()
+ qdel(src)
+
+/obj/screen/fullscreen/bluespace_overlay
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "mfoam"
+ screen_loc = "WEST,SOUTH to EAST,NORTH"
+ color = "#ff9900"
+ alpha = 100
+ blend_mode = BLEND_SUBTRACT
+ layer = SCREEN_LAYER
diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm
index 6a349deb845..c40e5b47fe8 100644
--- a/code/game/gamemodes/endgame/endgame.dm
+++ b/code/game/gamemodes/endgame/endgame.dm
@@ -63,10 +63,19 @@
/datum/universal_state/proc/OverlayAndAmbientSet()
return
-/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1)
+/datum/universal_state/proc/OnPlayerLatejoin(var/mob/living/M)
+ return
+
+/datum/universal_state/proc/OnTouchMapEdge(var/atom/A)
+ return TRUE //return FALSE to cancel map edge handling
+
+/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1, list/arguments=null)
if(on_exit)
universe.OnExit()
- universe = new newstate
+ if(arguments)
+ universe = new newstate(arglist(arguments))
+ else
+ universe = new newstate
if(on_enter)
universe.OnEnter()
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
index 710003c7b17..ea30992284b 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
@@ -45,9 +45,8 @@ var/global/universe_has_ended = 0
for(var/mob/M in player_list)
M.flash_eyes()
- if(emergency_shuttle.can_recall())
- priority_announcement.Announce("The emergency shuttle has returned due to bluespace distortion.")
- emergency_shuttle.recall()
+ if(evacuation_controller.cancel_evacuation())
+ priority_announcement.Announce("The evacuation has been aborted due to bluespace distortion.")
AreaSet()
MiscSet()
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 0cc3398167f..c6296566148 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -284,8 +284,8 @@ var/global/list/additional_antag_types = list()
if(!(antag.flags & ANTAG_NO_ROUNDSTART_SPAWN))
antag.finalize_spawn() //actually spawn antags
- if(emergency_shuttle && auto_recall_shuttle)
- emergency_shuttle.auto_recall = 1
+ if(evacuation_controller && auto_recall_shuttle)
+ evacuation_controller.auto_recall(1)
feedback_set_details("round_start","[time2text(world.realtime)]")
if(SSticker.mode)
@@ -335,14 +335,14 @@ var/global/list/additional_antag_types = list()
command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission")
/datum/game_mode/proc/check_finished()
- if(emergency_shuttle.returned() || station_was_nuked)
+ if(evacuation_controller.round_over() || station_was_nuked)
return 1
if(end_on_antag_death && antag_templates && antag_templates.len)
for(var/datum/antagonist/antag in antag_templates)
if(!antag.antags_are_dead())
return 0
if(config.continous_rounds)
- emergency_shuttle.auto_recall = 0
+ evacuation_controller.auto_recall(1)
return 0
return 1
return 0
@@ -421,10 +421,10 @@ var/global/list/additional_antag_types = list()
var/text = ""
if(surviving_total > 0)
text += "
There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]"
- text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
"
+ text += " ([escaped_total>0 ? escaped_total : "none"] [evacuation_controller.emergency_evacuation ? "escaped" : "transferred"]) and [ghosts] ghosts.
"
discord_text += "There [surviving_total>1 ? "were **[surviving_total] survivors**" : "was **one survivor**"]"
- discord_text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and **[ghosts] ghosts**."
+ discord_text += " ([escaped_total>0 ? escaped_total : "none"] [evacuation_controller.emergency_evacuation ? "escaped" : "transferred"]) and **[ghosts] ghosts**."
else
text += "There were no survivors ([ghosts] ghosts)."
diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm
index 816db86e9cd..fbab472ace3 100644
--- a/code/game/gamemodes/game_mode_latespawn.dm
+++ b/code/game/gamemodes/game_mode_latespawn.dm
@@ -6,7 +6,7 @@
///process()
///Called by the gameticker
/datum/game_mode/process()
- if(round_autoantag && world.time >= next_spawn && !emergency_shuttle.departed)
+ if(round_autoantag && world.time >= next_spawn && !evacuation_controller.round_over())
process_autoantag()
// Process loop for objectives like the brig one.
@@ -21,7 +21,7 @@
/datum/game_mode/proc/process_autoantag()
message_admins("[uppertext(name)]: Attempting autospawn.")
- if(emergency_shuttle.online())
+ if(evacuation_controller.is_evacuating())
message_admins("[uppertext(name)]: An evac or transfer shuttle is on the way. Aborted.")
next_spawn = world.time + min_autotraitor_delay
return
diff --git a/code/game/gamemodes/newobjective.dm b/code/game/gamemodes/newobjective.dm
index 0da33d4ed37..0e4d6c429d7 100644
--- a/code/game/gamemodes/newobjective.dm
+++ b/code/game/gamemodes/newobjective.dm
@@ -267,7 +267,7 @@ datum
explanation_text = "Frame [target.current.real_name], the [target.assigned_role] for a crime and make sure they are arrested and brought back to the Centcom station alive. We'll handle the rest from there."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(target.current.stat == 2)
return 0
@@ -313,7 +313,7 @@ datum
explanation_text = "[target.current.real_name], the [target.assigned_role] is a [pick("relative of a","friend of a","") + pick("high ranking","important","well-liked")] mercenary [pick("Leader","Officer","Agent","sympathiser")]. Make sure they get off the station safely, while minimizing intervention."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(target.current.stat == 2)
@@ -477,7 +477,7 @@ datum
explanation_text = "Hijack the emergency shuttle by escaping alone."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(!owner.current || owner.current.stat == 2)
@@ -512,7 +512,7 @@ datum
explanation_text = "Escape on the shuttle alive, without being arrested."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(!owner.current || owner.current.stat ==2)
@@ -1230,7 +1230,7 @@ datum
check_completion()
if(!istype(owner.current, /mob/living/silicon))
return 0
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(!owner.current)
return 0
@@ -1455,7 +1455,7 @@ datum/objective/silence
explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
var/area/shuttle = locate(/area/shuttle/escape/centcom)
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 1341e28083f..d693eb37b58 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -42,7 +42,7 @@ var/list/nuke_disks = list()
if(!is_type_in_list(disk_area, centcom_areas))
disk_rescued = 0
break
- var/crew_evacuated = (emergency_shuttle.returned())
+ var/crew_evacuated = (evacuation_controller.round_over())
if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
feedback_set_details("round_end_result","win - syndicate nuke")
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index f4d978caaa6..02d71c15120 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -230,7 +230,7 @@ datum/objective/hijack
check_completion()
if(!owner.current || owner.current.stat)
return 0
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(issilicon(owner.current))
return 0
@@ -252,7 +252,7 @@ datum/objective/block
check_completion()
if(!istype(owner.current, /mob/living/silicon))
return 0
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(!owner.current)
return 0
@@ -270,7 +270,7 @@ datum/objective/silence
explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left."
check_completion()
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
for(var/mob/living/player in player_list)
@@ -295,7 +295,7 @@ datum/objective/escape
return 0
if(isbrain(owner.current))
return 0
- if(!emergency_shuttle.returned())
+ if(!evacuation_controller.round_over())
return 0
if(!owner.current || owner.current.stat ==2)
return 0
diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm
index 3e94a427ee6..8098463de82 100644
--- a/code/game/gamemodes/sandbox/sandbox.dm
+++ b/code/game/gamemodes/sandbox/sandbox.dm
@@ -13,5 +13,5 @@
/datum/game_mode/sandbox/post_setup()
..()
- if(emergency_shuttle)
- emergency_shuttle.auto_recall = 1
+ if(evacuation_controller)
+ evacuation_controller.auto_recall(1)
diff --git a/code/game/jobs/faction/zeng_hu.dm b/code/game/jobs/faction/zeng_hu.dm
index c989732caef..53fbe002be4 100644
--- a/code/game/jobs/faction/zeng_hu.dm
+++ b/code/game/jobs/faction/zeng_hu.dm
@@ -17,7 +17,7 @@
You expect to be heard and obeyed, as your coworkers really should be taking advice from you in the absence of
a chief medical officer. Your bedside manner is impeccable and far outclasses standard NanoTrasen training and
procedure. You are the product of the most rigorous employee hiring and training process for medical staff in the
- Orion Spur, and are expected to act like it. Failure is you will not tolerate.
+ Orion Spur, and are expected to act like it. Failure is something you will not tolerate.
Biomechanical Engineer: Our ability to combine medical research with robotics
is the best and you know this. We do, after all, own Bishop Cybernetics - the
best premium augmentation subsidiary in the known galaxy. Unlike your medical cousins
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 025a9cbab35..8b26f7ab15f 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -47,12 +47,9 @@
var/dat = "Operating Computer\n"
if(src.table && (src.table.check_victim()))
src.victim = src.table.victim
- var/brain_result = victim.get_brain_status()
- if(victim.isFBP())
- brain_result = "N/A"
dat += {"
Patient Information:
-Brain Activity: [brain_result]
+Brain Activity: [victim.isFBP() ? "N/A" : victim.get_brain_status()]
Pulse: [victim.get_pulse(GETPULSE_TOOL)]
BP: [victim.get_blood_pressure()]
Blood Oxygenation: [victim.get_blood_oxygenation()]
@@ -68,6 +65,21 @@ Blood Oxygenation: [victim.get_blood_oxygenation()]
op_win.set_content(dat)
op_win.open()
+/obj/machinery/computer/operating/examine(mob/user)
+ ..()
+ if(get_dist(src, user) <= 2)
+ if(src.table && (src.table.check_victim()))
+ src.victim = src.table.victim
+
+ to_chat(user, SPAN_NOTICE("Patient Info:"))
+ to_chat(user, SPAN_NOTICE("Brain Activity: [victim.isFBP() ? "N/A" : victim.get_brain_status()]"))
+ to_chat(user, SPAN_NOTICE("Pulse: [victim.get_pulse(GETPULSE_TOOL)]"))
+ to_chat(user, SPAN_NOTICE("BP: [victim.get_blood_pressure()]"))
+ to_chat(user, SPAN_NOTICE("Blood Oxygenation: [victim.get_blood_oxygenation()]"))
+ else
+ src.victim = null
+ to_chat(user, SPAN_NOTICE("No Patient Detected"))
+
/obj/machinery/computer/operating/Topic(href, href_list)
if(..())
return 1
@@ -78,4 +90,4 @@ Blood Oxygenation: [victim.get_blood_oxygenation()]
/obj/machinery/computer/operating/machinery_process()
if(operable())
- src.updateDialog()
\ No newline at end of file
+ src.updateDialog()
diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm
index 12684596c1b..9ab562f8350 100644
--- a/code/game/machinery/computer/shuttle.dm
+++ b/code/game/machinery/computer/shuttle.dm
@@ -10,8 +10,17 @@
attackby(var/obj/item/card/W as obj, var/mob/user as mob)
- if(stat & (BROKEN|NOPOWER)) return
- if ((!( istype(W, /obj/item/card) ) || !(ROUND_IS_STARTED) || emergency_shuttle.location() || !( user ))) return
+ if(stat & (BROKEN|NOPOWER))
+ return
+
+ var/datum/evacuation_controller/shuttle/evac_control = evacuation_controller
+ if(!istype(evac_control))
+ to_chat(user, SPAN_DANGER("This console should not in use on this map. Please report this to a developer."))
+ return
+
+ if ((!( istype(W, /obj/item/card) ) || !( SSticker ) || evacuation_controller.has_evacuated() || !( user )))
+ return
+
if (W.GetID())
var/obj/item/card/id/id = W
@@ -29,7 +38,7 @@
return 0
var/choice = alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", "Authorize", "Repeal", "Abort")
- if(emergency_shuttle.location() && user.get_active_hand() != id)
+ if(evacuation_controller.is_prepared() && user.get_active_hand() != id)
return 0
switch(choice)
if("Authorize")
@@ -43,7 +52,7 @@
message_admins("[key_name_admin(user)] has launched the shuttle")
log_game("[key_name(user)] has launched the shuttle early",ckey=key_name(user))
to_world("Alert: Shuttle launch time shortened to 10 seconds!")
- emergency_shuttle.set_launch_countdown(10)
+ evacuation_controller.set_launch_time(world.time+100)
//src.authorized = null
qdel(src.authorized)
src.authorized = list( )
@@ -60,11 +69,11 @@
else if (istype(W, /obj/item/card/emag) && !emagged)
var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "Launch", "Cancel")
- if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W)
+ if(!emagged && !evacuation_controller.is_prepared() && user.get_active_hand() == W)
switch(choice)
if("Launch")
to_world("Alert: Shuttle launch time shortened to 10 seconds!")
- emergency_shuttle.set_launch_countdown(10)
+ evacuation_controller.set_launch_time(world.time+100)
emagged = 1
if("Cancel")
return
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index f393b6d8fca..243056fda22 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -11,8 +11,8 @@ datum/track/New(var/title_name, var/audio)
/obj/machinery/media/jukebox/
name = "space jukebox"
icon = 'icons/obj/jukebox.dmi'
- icon_state = "jukebox2-nopower"
- var/state_base = "jukebox2"
+ icon_state = "jukebox-nopower"
+ var/state_base = "jukebox"
anchored = 0
density = 1
power_channel = EQUIP
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index e60b09f07f5..e58124c8612 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -32,7 +32,7 @@
/obj/machinery/recharger/examine(mob/user)
. = ..(user, 3)
- to_chat(user, "There is [charging ? "[charging]" : "nothing"] in [src].")
+ to_chat(user, "There is [charging ? "\a [charging]" : "nothing"] in [src].")
if (charging && .)
var/obj/item/cell/C = charging.get_cell()
if (istype(C) && user.client && (!user.progressbars || !user.progressbars[src]))
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index c136bd85849..1ba96786707 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -86,22 +86,23 @@
if(STATUS_DISPLAY_BLANK) //blank
return 1
if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer
- if(emergency_shuttle.waiting_to_leave())
- message1 = "-ETD-"
- if (emergency_shuttle.shuttle.is_launching())
- message2 = "Launch"
- else
- message2 = get_shuttle_timer_departure()
+ if(evacuation_controller)
+ if(evacuation_controller.is_prepared())
+ message1 = "-ETD-"
+ if (evacuation_controller.waiting_to_leave())
+ message2 = "Launch"
+ else
+ message2 = get_shuttle_timer()
+ if(length(message2) > CHARS_PER_LINE)
+ message2 = "Error"
+ update_display(message1, message2)
+ else if(evacuation_controller.has_eta())
+ message1 = "-ETA-"
+ message2 = get_shuttle_timer()
if(length(message2) > CHARS_PER_LINE)
message2 = "Error"
- update_display(message1, message2)
- else if(emergency_shuttle.has_eta())
- message1 = "-ETA-"
- message2 = get_shuttle_timer_arrival()
- if(length(message2) > CHARS_PER_LINE)
- message2 = "Error"
- update_display(message1, message2)
- return 1
+ update_display(message1, message2)
+ return 1
if(STATUS_DISPLAY_MESSAGE) //custom messages
var/line1
var/line2
@@ -165,14 +166,8 @@
if(maptext != new_text)
maptext = new_text
-/obj/machinery/status_display/proc/get_shuttle_timer_arrival()
- var/timeleft = emergency_shuttle.estimate_arrival_time()
- if(timeleft < 0)
- return ""
- return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
-
-/obj/machinery/status_display/proc/get_shuttle_timer_departure()
- var/timeleft = emergency_shuttle.estimate_launch_time()
+/obj/machinery/status_display/proc/get_shuttle_timer()
+ var/timeleft = evacuation_controller.get_eta()
if(timeleft < 0)
return ""
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
diff --git a/code/game/machinery/vending_types.dm b/code/game/machinery/vending_types.dm
index 7c0a8fbe560..a4d2093dc7c 100644
--- a/code/game/machinery/vending_types.dm
+++ b/code/game/machinery/vending_types.dm
@@ -721,6 +721,7 @@
/obj/item/seeds/cherryseed = 3,
/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cocoapodseed = 3,
+ /obj/item/seeds/coffeeseed = 3,
/obj/item/seeds/cornseed = 3,
/obj/item/seeds/dynseed = 3,
/obj/item/seeds/earthenroot = 2,
@@ -735,6 +736,7 @@
/obj/item/seeds/lemonseed = 3,
/obj/item/seeds/limeseed = 3,
/obj/item/seeds/mtearseed = 3,
+ /obj/item/seeds/mintseed = 3,
/obj/item/seeds/nifberries = 2,
/obj/item/seeds/onionseed = 3,
/obj/item/seeds/orangeseed = 3,
@@ -755,6 +757,7 @@
/obj/item/seeds/sugarcaneseed = 3,
/obj/item/seeds/sunflowerseed = 3,
/obj/item/seeds/sugartree = 2,
+ /obj/item/seeds/teaseed = 3,
/obj/item/seeds/tobaccoseed = 3,
/obj/item/seeds/tomatoseed = 3,
/obj/item/seeds/towermycelium = 3,
@@ -787,6 +790,7 @@
/obj/item/seeds/cherryseed = 40,
/obj/item/seeds/chiliseed = 50,
/obj/item/seeds/cocoapodseed = 50,
+ /obj/item/seeds/coffeeseed = 70,
/obj/item/seeds/cornseed = 30,
/obj/item/seeds/dynseed = 80,
/obj/item/seeds/earthenroot = 70,
@@ -801,6 +805,7 @@
/obj/item/seeds/lemonseed = 40,
/obj/item/seeds/limeseed = 50,
/obj/item/seeds/mtearseed = 60,
+ /obj/item/seeds/mintseed = 70,
/obj/item/seeds/nifberries = 70,
/obj/item/seeds/onionseed = 30,
/obj/item/seeds/orangeseed = 40,
@@ -821,6 +826,7 @@
/obj/item/seeds/sugarcaneseed = 20,
/obj/item/seeds/sunflowerseed = 20,
/obj/item/seeds/sugartree = 40,
+ /obj/item/seeds/teaseed = 30,
/obj/item/seeds/tobaccoseed = 40,
/obj/item/seeds/tomatoseed = 30,
/obj/item/seeds/towermycelium = 20,
diff --git a/code/game/objects/items/devices/suit_cooling.dm b/code/game/objects/items/devices/suit_cooling.dm
index 22974f42fae..ffcc6a23f31 100644
--- a/code/game/objects/items/devices/suit_cooling.dm
+++ b/code/game/objects/items/devices/suit_cooling.dm
@@ -2,7 +2,7 @@
name = "portable suit cooling unit"
desc = "A portable heat sink and liquid cooled radiator that can be hooked up to a space suit's existing temperature controls to provide industrial levels of cooling."
w_class = ITEMSIZE_LARGE
- icon = 'icons/obj/suitcooler.dmi'
+ icon = 'icons/obj/contained_items/tools/suitcooler.dmi'
icon_state = "suitcooler0"
item_state = "coolingpack"
contained_sprite = TRUE
@@ -24,7 +24,7 @@
var/cover_open = 0 //is the cover open?
var/obj/item/cell/cell
var/max_cooling = 12 //in degrees per second - probably don't need to mess with heat capacity here
- var/charge_consumption = 16.6 //charge per second at max_cooling
+ var/charge_consumption = 8.3 //charge per second at max_cooling
var/thermostat = T20C
//TODO: make it heat up the surroundings when not in space
@@ -134,8 +134,13 @@
src.cell = null
update_icon()
return
+ toggle_power(user)
- //TODO use a UI like the air tanks
+/obj/item/device/suit_cooling_unit/AltClick(mob/user)
+ if(Adjacent(user))
+ toggle_power(user)
+
+/obj/item/device/suit_cooling_unit/proc/toggle_power(var/mob/user)
if(on)
turn_off()
else
@@ -177,23 +182,31 @@
return
icon_state = "suitcooler0"
+ item_state = "coolingpack"
- if(!cell || !on)
- return
+ if(cell && on)
+ var/battery_level = 0
+ switch(round(cell.percent()))
+ if(86 to INFINITY)
+ battery_level = 0
+ if(69 to 85)
+ battery_level = 1
+ if(52 to 68)
+ battery_level = 2
+ if(35 to 51)
+ battery_level = 3
+ if(18 to 34)
+ battery_level = 4
+ if(-INFINITY to 17)
+ battery_level = 5
- switch(round(cell.percent()))
- if(86 to INFINITY)
- add_overlay("battery-0")
- if(69 to 85)
- add_overlay("battery-1")
- if(52 to 68)
- add_overlay("battery-2")
- if(35 to 51)
- add_overlay("battery-3")
- if(18 to 34)
- add_overlay("battery-4")
- if(-INFINITY to 17)
- add_overlay("battery-5")
+ add_overlay("battery-[battery_level]")
+ item_state = "coolingpack[battery_level]"
+
+ if(ismob(loc))
+ var/mob/M = loc
+ M.update_inv_back()
+ M.update_inv_s_store()
/obj/item/device/suit_cooling_unit/examine(mob/user)
if(!..(user, 1))
diff --git a/code/game/objects/items/weapons/RFD.dm b/code/game/objects/items/weapons/RFD.dm
index 8b8714eba90..dd132c8f8b4 100644
--- a/code/game/objects/items/weapons/RFD.dm
+++ b/code/game/objects/items/weapons/RFD.dm
@@ -211,6 +211,10 @@ RFD Construction-Class
build_cost = 1
build_type = "floor"
build_atom = /turf/simulated/floor/airless
+ else if(istype(T, /turf/simulated/open))
+ build_cost = 1
+ build_type = "floor"
+ build_atom = /turf/simulated/floor/airless
else if(istype(T, /turf/simulated/floor))
build_delay = 20
build_cost = 3
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 47a1bfc9e97..b3afc1b5ffe 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -41,7 +41,7 @@
can_place = TRUE
else
for (var/obj/item/grab/G in C.grabbed_by)
- if (G.loc == user && G.state >= GRAB_AGGRESSIVE)
+ if (G.state >= GRAB_AGGRESSIVE)
can_place = TRUE
break
@@ -102,7 +102,7 @@
user.drop_from_inventory(cuffs,target)
else
cuffs.forceMove(target)
-
+
if(!legcuff)
target.handcuffed = cuffs
target.update_inv_handcuffed()
@@ -250,4 +250,4 @@
icon_state = "legcuff"
legcuff = TRUE
breakouttime = 30 SECONDS
- cuff_type = "legcuffs"
\ No newline at end of file
+ cuff_type = "legcuffs"
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 753e95b4f05..12bf60d191a 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -172,12 +172,6 @@
desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles."
icon_state = "chempack"
-/obj/item/storage/backpack/cloak
- name = "tunnel cloak"
- desc = "It's a Vaurca cloak with storage pockets."
- icon_state = "cape"
- sprite_sheets = list(BODYTYPE_VAURCA = 'icons/mob/species/vaurca/back.dmi')
-
/obj/item/storage/backpack/syndie
name = "syndicate rucksack"
desc = "The latest in carbon fiber and red satin combat rucksack technology. Comfortable and tough!"
@@ -625,4 +619,71 @@
desc = "A small, fashionable bag typically worn over the shoulder."
icon_state = "purse"
item_state = "purse"
- max_storage_space = 16
\ No newline at end of file
+ max_storage_space = 16
+
+//**Vaurca cloaks**//
+
+/obj/item/storage/backpack/cloak
+ name = "tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets."
+ icon_state = "cape"
+ item_state = "cape"
+ sprite_sheets = list(BODYTYPE_VAURCA = 'icons/mob/species/vaurca/back.dmi')
+ var/hooded = FALSE
+
+/obj/item/storage/backpack/cloak/verb/toggle_cloak_hood()
+ set name = "Toggle Cloak Hood"
+ set desc = "Toggle your cloak hood."
+ set category = "Object"
+ set src in usr
+ if(use_check_and_message(usr))
+ return 0
+ hooded = !hooded
+ to_chat(usr, "You [hooded ? "raise" : "lower"] \the [src] hood.")
+ icon_state = "[initial(icon_state)][hooded ? "_up" : ""]"
+ item_state = "icon_state"
+ var/mob/living/carbon/human/H = src.loc
+ H.update_icon()
+ H.update_inv_back()
+
+/obj/item/storage/backpack/cloak/sedantis
+ name = "Sedantis tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the Sedantis flag design."
+ icon_state = "sedcape"
+ item_state = "sedcape"
+
+/obj/item/storage/backpack/cloak/medical
+ name = "medical tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the medical department design."
+ icon_state = "medcape"
+ item_state = "medcape"
+
+/obj/item/storage/backpack/cloak/engi
+ name = "engineering tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the engineering department design."
+ icon_state = "engicape"
+ item_state = "engicape"
+
+/obj/item/storage/backpack/cloak/atmos
+ name = "atmospherics tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the atmospherics design."
+ icon_state = "atmoscape"
+ item_state = "atmoscape"
+
+/obj/item/storage/backpack/cloak/cargo
+ name = "cargo tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the supply department design."
+ icon_state = "cargocape"
+ item_state = "cargocape"
+
+/obj/item/storage/backpack/cloak/sci
+ name = "science tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the science department design."
+ icon_state = "scicape"
+ item_state = "scicape"
+
+/obj/item/storage/backpack/cloak/sec
+ name = "security tunnel cloak"
+ desc = "A Vaurca cloak with storage pockets. This one has the security department design."
+ icon_state = "seccape"
+ item_state = "seccape"
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm
index 9da0aa3c4bf..c8b4f13a878 100644
--- a/code/game/objects/items/weapons/storage/wallets.dm
+++ b/code/game/objects/items/weapons/storage/wallets.dm
@@ -46,19 +46,24 @@
var/flipped = null
var/flippable = 1
var/wear_over_suit = 0
- var/base_name = ""
/obj/item/storage/wallet/Initialize()
. = ..()
- base_name = name
+ AddComponent(/datum/component/base_name, name)
+/obj/item/storage/wallet/proc/update_name(var/base_name = initial(name))
+ SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
+ if(front_id)
+ name = "[base_name] ([front_id])"
+ else
+ name = "[base_name]"
/obj/item/storage/wallet/remove_from_storage(obj/item/W as obj, atom/new_location)
. = ..(W, new_location)
if(.)
if(W == front_id)
front_id = null
- name = base_name
+ update_name()
update_icon()
/obj/item/storage/wallet/handle_item_insertion(obj/item/W as obj, prevent_warning = 0)
@@ -66,7 +71,7 @@
if(.)
if(!front_id && istype(W, /obj/item/card/id))
front_id = W
- name = "[name] ([front_id])"
+ update_name()
update_icon()
/obj/item/storage/wallet/update_icon()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
index 2e9c7113af7..510714384bc 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
@@ -124,7 +124,7 @@
new /obj/item/clothing/mask/gas/alt(src)
new /obj/item/clothing/mask/gas/half(src)
new /obj/item/auto_cpr(src)
- new /obj/item/clothing/suit/storage/toggle/first_responder_jacket(src)
+ new /obj/item/clothing/suit/storage/toggle/fr_jacket/alt(src)
/obj/structure/closet/secure_closet/CMO
name = "chief medical officer's locker"
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 74f30820e4f..fbd8e628605 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -140,6 +140,8 @@
new /obj/item/clothing/accessory/badge/hos(src)
new /obj/item/clothing/gloves/black_leather(src)
new /obj/item/clothing/suit/storage/toggle/armor/hos(src)
+ new /obj/item/clothing/mask/gas/alt(src)
+ new /obj/item/clothing/mask/gas/half(src)
//Tools
new /obj/item/device/radio/headset/heads/hos(src)
new /obj/item/device/radio/headset/heads/hos/alt(src)
diff --git a/code/game/objects/structures/full_window_frame.dm b/code/game/objects/structures/full_window_frame.dm
new file mode 100644
index 00000000000..fbcf304f88a
--- /dev/null
+++ b/code/game/objects/structures/full_window_frame.dm
@@ -0,0 +1,52 @@
+/obj/structure/window_frame
+ name = "window frame"
+ desc = "An empty window frame."
+ icon = 'icons/obj/smooth/full_window.dmi'
+ icon_state = "window_frame"
+ build_amt = 4
+ anchored = FALSE
+
+/obj/structure/window_frame/anchored
+ anchored = TRUE
+
+/obj/structure/window_frame/attackby(obj/item/W, mob/user)
+ if((W.isscrewdriver()) && (istype(loc, /turf/simulated) || anchored))
+ playsound(src, W.usesound, 80, 1)
+ anchored = !anchored
+ user.visible_message("[user] [anchored ? "fastens" : "unfastens"] \the [src].", \
+ "You have [anchored ? "fastened the grille to" : "unfastened the grill from"] the floor.")
+ return
+
+ else if(W.iswelder())
+ var/obj/item/weldingtool/WT = W
+ if(!WT.isOn())
+ to_chat(user, SPAN_WARNING("\The [WT] isn't turned on."))
+ return
+ if(WT.remove_fuel(0, user))
+ to_chat(user, SPAN_NOTICE("You use \the [WT] to remove \the [src]."))
+ playsound(src, WT.usesound, 80, 1)
+ new /obj/item/stack/material/steel(get_turf(src),rand(1,3))
+ qdel(src)
+ return
+
+ else if(istype(W, /obj/item/stack/material) && W.get_material_name() == MATERIAL_GLASS_REINFORCED && anchored)
+ var/obj/item/stack/material/G = W
+ if(G.use(4))
+ playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
+ to_chat(user, SPAN_WARNING ("You place the glass on the window frame."))
+ new /obj/structure/window/full(get_turf(src))
+ qdel(src)
+ return
+ else
+ to_chat(user, SPAN_WARNING ("You need at least four sheets of reinforced glass to finished the window."))
+
+ else if(istype(W, /obj/item/stack/material) && W.get_material_name() == MATERIAL_GLASS_REINFORCED_PHORON && anchored)
+ var/obj/item/stack/material/G = W
+ if(G.use(4))
+ playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
+ to_chat(user, SPAN_WARNING ("You place the glass on the window frame."))
+ new /obj/structure/window/full/phoron(get_turf(src))
+ qdel(src)
+ return
+ else
+ to_chat(user, SPAN_WARNING ("You need at least four sheets of reinforced borosilicate glass to finished the window."))
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 13091df3b39..17c2b50f22e 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -10,6 +10,7 @@
anchored = TRUE
flags = CONDUCT
explosion_resistance = 1
+ layer = 2.98
var/health = 10
var/destroyed = 0
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index 3ca06c9502c..1d2c3fcdc09 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -870,6 +870,30 @@
/obj/structure/sign/flag/gadpathur/right
icon_state = "gadpathur_r"
+/obj/item/flag/vysoka
+ name = "Free System of Vysoka flag"
+ desc = "The flag of the Free System of Vysoka."
+ desc_fluff = "The red, yellow and Coalition-blue flag of Vysoka, as drawn when one wishes to represent the planet as a whole. As Vysokan communities are rather traditional and tied to their respective Host, village or city-state, natives are more likely to \
+ identify with local symbols. This has not stopped the original flag from being flown in times of much-needed unity."
+ flag_path = "vysoka"
+
+/obj/structure/sign/flag/vysoka
+ name = "Free System of Vysoka flag"
+ desc = "The flag of the Free System of Vysoka."
+ desc_fluff = "The red, yellow and Coalition-blue flag of Vysoka, as drawn when one wishes to represent the planet as a whole. As Vysokan communities are rather traditional and tied to their respective Host, village or city-state, natives are more likely to \
+ identify with local symbols. This has not stopped the original flag from being flown in times of much-needed unity."
+ icon_state = "vysoka"
+
+/obj/item/flag/vysoka/l
+ name = "large Free System of Vysoka flag"
+ flag_size = 1
+
+/obj/structure/sign/flag/vysoka/left
+ icon_state = "vysoka_l"
+
+/obj/structure/sign/flag/vysoka/right
+ icon_state = "vysoka_r"
+
/obj/item/flag/afterattack(var/atom/A, var/mob/user, var/adjacent, var/clickparams)
if (!adjacent)
return
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index 017e2d27bc0..04b4a131499 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -432,9 +432,16 @@
bedpath = /obj/structure/bed/roller/hover
/obj/item/roller/attack_self(mob/user)
- var/obj/structure/bed/roller/R = new bedpath(user.loc)
- R.add_fingerprint(user)
- qdel(src)
+ ..()
+ deploy_roller(user, user.loc)
+
+/obj/item/roller/afterattack(obj/target, mob/user, proximity)
+ if(!proximity)
+ return
+ if(isturf(target))
+ var/turf/T = target
+ if(!T.density)
+ deploy_roller(user, target)
/obj/item/roller/attackby(obj/item/W as obj, mob/user as mob)
@@ -448,6 +455,11 @@
..()
+/obj/item/roller/proc/deploy_roller(mob/user, atom/location)
+ var/obj/structure/bed/roller/R = new bedpath(location)
+ R.add_fingerprint(user)
+ qdel(src)
+
/obj/item/roller_holder
name = "roller bed rack"
desc = "A rack for carrying a collapsed roller bed."
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 961c3e60553..6a70145a316 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -19,6 +19,7 @@
var/shardtype = /obj/item/material/shard
var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass.
var/silicate = 0 // number of units of silicate
+ var/base_frame = null
atmos_canpass = CANPASS_PROC
@@ -45,6 +46,12 @@
else
to_chat(user, SPAN_NOTICE("There is a thick layer of silicate covering it."))
+/obj/structure/window/proc/update_nearby_icons()
+ queue_smooth_neighbors(src)
+
+/obj/structure/window/update_icon()
+ queue_smooth(src)
+
/obj/structure/window/proc/take_damage(var/damage = 0, var/sound_effect = 1)
var/initialhealth = health
@@ -102,6 +109,14 @@
new shardtype(loc) //todo pooling?
if(reinf)
new /obj/item/stack/rods(loc)
+
+ if(base_frame)
+ if(prob(50))
+ var/obj/F = new base_frame(loc)
+ F.anchored = anchored
+ else
+ new /obj/item/material/shard/shrapnel(loc)
+
qdel(src)
return
@@ -233,6 +248,7 @@
to_chat(user, (state == 1 ? SPAN_NOTICE("You have unfastened the window from the frame.") : SPAN_NOTICE("You have fastened the window to the frame.")))
else if(reinf && state == 0)
anchored = !anchored
+ update_icon()
update_nearby_icons()
playsound(loc, W.usesound, 75, 1)
to_chat(user, (anchored ? SPAN_NOTICE("You have fastened the frame to the floor.") : SPAN_NOTICE("You have unfastened the frame from the floor.")))
@@ -241,6 +257,8 @@
update_nearby_icons()
playsound(loc, W.usesound, 75, 1)
to_chat(user, (anchored ? SPAN_NOTICE("You have fastened the window to the floor.") : SPAN_NOTICE("You have unfastened the window.")))
+ update_icon()
+ update_nearby_icons()
else if(W.iscrowbar() && reinf && state <= 1)
state = 1 - state
playsound(loc, W.usesound, 75, 1)
@@ -250,12 +268,7 @@
to_chat(user, SPAN_NOTICE("You're not sure how to dismantle \the [src] properly."))
else
visible_message(SPAN_NOTICE("[user] dismantles \the [src]."))
- if(dir == SOUTHWEST)
- var/obj/item/stack/material/mats = new glasstype(loc)
- mats.amount = is_fulltile() ? 4 : 2
- else
- new glasstype(loc)
- qdel(src)
+ dismantle_window()
else
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(W.damtype == BRUTE || W.damtype == BURN)
@@ -300,6 +313,14 @@
take_damage(damage)
return
+/obj/structure/window/proc/dismantle_window()
+ if(dir == SOUTHWEST)
+ var/obj/item/stack/material/mats = new glasstype(loc)
+ mats.amount = is_fulltile() ? 4 : 2
+ else
+ new glasstype(loc)
+ qdel(src)
+
/obj/structure/window/Initialize(mapload, start_dir = null, constructed=0)
. = ..()
@@ -342,33 +363,6 @@
return 1
return 0
-//This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc!
-/obj/structure/window/proc/update_nearby_icons()
- update_icon()
- for(var/obj/structure/window/W in orange(src, 1))
- W.update_icon()
-
-//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm)
-/obj/structure/window/update_icon()
- //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong.
- //this way it will only update full-tile ones
- cut_overlays()
- if(!is_fulltile())
- icon_state = "[basestate]"
- return
- var/list/dirs = list()
- if(anchored)
- for(var/obj/structure/window/W in orange(src,1))
- if(W.anchored && W.density && W.type == src.type && W.is_fulltile()) //Only counts anchored, not-destroyed fill-tile windows.
- dirs += get_dir(src, W)
-
- var/list/connections = dirs_to_corner_states(dirs)
-
- icon_state = ""
- for(var/i = 1 to 4)
- var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1))
- add_overlay(I)
-
/obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > maximal_heat)
hit(damage_per_fire_tick, 0)
@@ -514,12 +508,6 @@
/obj/structure/window/shuttle/crescent/take_damage()
return
-/obj/structure/window/shuttle/update_nearby_icons()
- queue_smooth_neighbors(src)
-
-/obj/structure/window/update_icon()
- queue_smooth(src)
-
/obj/structure/window/reinforced/polarized
name = "electrochromic window"
desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it."
@@ -581,3 +569,59 @@
/obj/machinery/button/switch/windowtint/update_icon()
icon_state = "light[active]"
+
+/obj/structure/window/full
+ name = "reinforced window"
+ desc = "It looks rather strong. Might take a few good hits to shatter it."
+ icon = 'icons/obj/smooth/full_window.dmi'
+ icon_state = "window_glass"
+ basestate = "window_glass"
+ maxhealth = 40
+ reinf = TRUE
+ maximal_heat = T0C + 750
+ dir = 5
+ smooth = SMOOTH_TRUE
+ damage_per_fire_tick = 2.0
+ can_be_unanchored = TRUE
+ glasstype = /obj/item/stack/material/glass/reinforced
+ layer = 2.99
+ base_frame = /obj/structure/window_frame
+
+/obj/structure/window/full/dismantle_window()
+ var/obj/item/stack/material/mats = new glasstype(loc)
+ mats.amount = 4
+ var/obj/structure/window_frame/F = new/obj/structure/window_frame (get_turf(src))
+ F.anchored = anchored
+ qdel(src)
+
+/obj/structure/window/full/phoron
+ name = "reinforced borosilicate window"
+ desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong."
+ icon = 'icons/obj/smooth/phoron_full_window.dmi'
+ icon_state = "window_glass"
+ basestate = "window_glass"
+ shardtype = /obj/item/material/shard/phoron
+ glasstype = /obj/item/stack/material/glass/phoronrglass
+ maximal_heat = T0C + 4000
+ damage_per_fire_tick = 1.0
+ maxhealth = 80.0
+ layer = 2.99
+ base_frame = /obj/structure/window_frame
+
+/obj/structure/window/reinforced/polarized/full
+ name = "reinforced electrochromic window"
+ desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it."
+ icon = 'icons/obj/smooth/full_window.dmi'
+ icon_state = "window_glass"
+ basestate = "window_glass"
+ dir = 5
+ smooth = SMOOTH_TRUE
+ layer = 2.99
+ base_frame = /obj/structure/window_frame
+
+/obj/structure/window/reinforced/polarized/full/dismantle_window()
+ var/obj/item/stack/material/mats = new glasstype(loc)
+ mats.amount = 4
+ var/obj/structure/window_frame/F = new/obj/structure/window_frame (get_turf(src))
+ F.anchored = anchored
+ qdel(src)
diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm
index 7539cbc5711..feff60d7dc0 100644
--- a/code/game/turfs/simulated/floor_attackby.dm
+++ b/code/game/turfs/simulated/floor_attackby.dm
@@ -87,19 +87,57 @@
update_icon(TRUE)
playsound(src, 'sound/items/Deconstruct.ogg', 80, 1)
return
- // Repairs.
+ // Repairs and deconstruction
+ else if(C.iscrowbar())
+ if(broken || burnt)
+ playsound(src, 'sound/items/crowbar_tile.ogg', 80, 1)
+ visible_message("[user] has begun prying off the damaged plating.")
+ var/turf/T = GetBelow(src)
+ if(T)
+ T.visible_message("The ceiling above looks as if it's being pried off.")
+ if(do_after(user, 10 SECONDS))
+ if(!istype(src, /turf/simulated/floor))
+ return
+ if(broken || burnt && !(is_plating()))
+ return
+ visible_message("[user] has pried off the damaged plating.")
+ new /obj/item/stack/tile/floor(src)
+ src.ReplaceWithLattice()
+ playsound(src, 'sound/items/Deconstruct.ogg', 80, 1)
+ if(T)
+ T.visible_message("The ceiling above has been pried off!")
+ return
+ return
else if(C.iswelder())
var/obj/item/weldingtool/welder = C
if(welder.isOn() && (is_plating()))
if(broken || burnt)
- if(welder.remove_fuel(0,user))
- to_chat(user, "You fix some dents on the broken plating.")
- playsound(src, 'sound/items/welder.ogg', 80, 1)
- icon_state = "plating"
- burnt = null
- broken = null
- else
- to_chat(user, "You need more welding fuel to complete this task.")
+ if(do_after(user, 5 SECONDS))
+ if(welder.remove_fuel(0, user))
+ to_chat(user, "You fix some dents on the broken plating.")
+ playsound(src, 'sound/items/Welder.ogg', 80, 1)
+ icon_state = "plating"
+ burnt = null
+ broken = null
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
+ else
+ if(welder.remove_fuel(0, user))
+ playsound(src, 'sound/items/Welder.ogg', 80, 1)
+ visible_message("[user] has started melting the plating's reinforcements!")
+ if(do_after(user, 10 SECONDS) && welder.isOn() && welder_melt())
+ visible_message("[user] has melted the plating's reinforcements! It should be possible to pry it off.")
+ playsound(src, 'sound/items/Welder.ogg', 80, 1)
+ return
+ return ..()
+
+/turf/simulated/floor/proc/welder_melt()
+ if(!(is_plating()) || broken || burnt)
+ return 0
+ burnt = 1
+ update_icon()
+ return 1
/turf/simulated/floor/can_lay_cable()
return !flooring
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 7d61e9bb497..a3800bb3860 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -3,22 +3,15 @@
var/dat = ""
dat += "Current Game Mode: [SSticker.mode.name]
"
dat += "Round Duration: [get_round_duration_formatted()]
"
- dat += "Emergency Shuttle
"
- if (!emergency_shuttle.online())
- dat += "Call Shuttle
"
+ dat += "Evacuation
"
+ if (evacuation_controller.is_idle())
+ dat += "Call Evacuation
"
else
- if (emergency_shuttle.wait_for_launch)
- var/timeleft = emergency_shuttle.estimate_launch_time()
- dat += "ETL: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
"
-
- else if (emergency_shuttle.shuttle.has_arrive_time())
- var/timeleft = emergency_shuttle.estimate_arrival_time()
- dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
"
+ var/timeleft = evacuation_controller.get_eta()
+ if (evacuation_controller.waiting_to_leave())
+ dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
"
dat += "Send Back
"
- if (emergency_shuttle.shuttle.moving_status == SHUTTLE_WARMUP)
- dat += "Launching now...
"
-
dat += "[SSticker.delay_end ? "End Round Normally" : "Delay Round End"]
"
dat += "
"
for(var/antag_type in all_antag_types)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 9fdfc2e0ae4..ee5a0863155 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -112,53 +112,19 @@
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN)) return
- if( SSticker.mode.name == "blob" )
- alert("You can't call the shuttle during blob!")
- return
-
switch(href_list["call_shuttle"])
if("1")
- if ((!( ROUND_IS_STARTED ) || !emergency_shuttle.location()))
- return
- if (emergency_shuttle.can_call())
- emergency_shuttle.call_evac()
- log_admin("[key_name(usr)] called the Emergency Shuttle",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station", 1)
+ if (evacuation_controller.call_evacuation(usr, TRUE))
+ log_admin("[key_name(usr)] called an evacuation.")
+ message_admins("[key_name_admin(usr)] called an evacuation.", 1)
if("2")
- if (!( ROUND_IS_STARTED ) || !emergency_shuttle.location())
- return
- if (emergency_shuttle.can_call())
- emergency_shuttle.call_evac()
- log_admin("[key_name(usr)] called the Emergency Shuttle",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station", 1)
-
- else if (emergency_shuttle.can_recall())
- emergency_shuttle.recall()
- log_admin("[key_name(usr)] sent the Emergency Shuttle back",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back", 1)
-
- href_list["secretsadmin"] = "check_antagonist"
-
- else if(href_list["edit_shuttle_time"])
- if(!check_rights(R_SERVER)) return
-
- if (emergency_shuttle.wait_for_launch)
- var/new_time_left = input("Enter new shuttle launch countdown (seconds):","Edit Shuttle Launch Time", emergency_shuttle.estimate_launch_time() ) as num
-
- emergency_shuttle.launch_time = world.time + new_time_left*10
-
- log_admin("[key_name(usr)] edited the Emergency Shuttle's launch time to [new_time_left]",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's launch time to [new_time_left*10]", 1)
- else if (emergency_shuttle.shuttle.has_arrive_time())
-
- var/new_time_left = input("Enter new shuttle arrival time (seconds):","Edit Shuttle Arrival Time", emergency_shuttle.estimate_arrival_time() ) as num
- emergency_shuttle.shuttle.arrive_time = world.time + new_time_left*10
-
- log_admin("[key_name(usr)] edited the Emergency Shuttle's arrival time to [new_time_left]",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's arrival time to [new_time_left*10]", 1)
- else
- alert("The shuttle is neither counting down to launch nor is it in transit. Please try again when it is.")
+ if (evacuation_controller.call_evacuation(usr, TRUE))
+ log_admin("[key_name(usr)] called an evacuation.")
+ message_admins("[key_name_admin(usr)] called an evacuation.", 1)
+ else if (evacuation_controller.cancel_evacuation())
+ log_admin("[key_name(usr)] cancelled an evacuation.")
+ message_admins("[key_name_admin(usr)] cancelled an evacuation.", 1)
href_list["secretsadmin"] = "check_antagonist"
diff --git a/code/modules/admin/verbs/SDQL_2.dm b/code/modules/admin/verbs/SDQL_2.dm
index 5b71b526b95..06568b45ab1 100644
--- a/code/modules/admin/verbs/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL_2.dm
@@ -58,6 +58,10 @@
objs += d
//to_chat(usr, "Query: [query_text]")
+ var/static/list/blacklist = list(/datum/configuration)
+ for(var/datum/D in objs)
+ if(blacklist[D.type])
+ objs -= D
message_admins("[usr] executed SDQL query: \"[query_text]\".")
switch(query_tree[1])
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 897f3a5f180..40f8dafc80f 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -888,67 +888,62 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/admin_call_shuttle()
set category = "Admin"
- set name = "Call Shuttle"
+ set name = "Call Evacuation"
- if ((!( ROUND_IS_STARTED ) || !emergency_shuttle.location()))
+ if ((!( ROUND_IS_STARTED ) || !evacuation_controller))
return
- if(!check_rights(R_ADMIN)) return
+ if(!check_rights(R_ADMIN))
+ return
- var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No")
- if(confirm != "Yes") return
+ if(alert(src, "Are you sure?", "Confirm", "Yes", "No") != "Yes")
+ return
- var/choice
if(SSticker.mode.auto_recall_shuttle)
- choice = input("The shuttle will just return if you call it. Call anyway?") in list("Confirm", "Cancel")
- if(choice == "Confirm")
- emergency_shuttle.auto_recall = 1 //enable auto-recall
- else
+ if(input("The evacuation will just be cancelled if you call it. Call anyway?") in list("Confirm", "Cancel") != "Confirm")
return
- choice = input("Is this an emergency evacuation or a crew transfer?") in list("Emergency", "Crew Transfer")
- if (choice == "Emergency")
- emergency_shuttle.call_evac()
- else
- emergency_shuttle.call_transfer()
+ var/choice = input("Is this an emergency evacuation or a crew transfer?") in list("Emergency", "Crew Transfer")
+ evacuation_controller.call_evacuation(usr, (choice == "Emergency"))
feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- log_admin("[key_name(usr)] admin-called the emergency shuttle.",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.", 1)
+ log_admin("[key_name(usr)] admin-called an evacuation.",admin_key=key_name(usr))
+ message_admins("[key_name_admin(usr)] admin-called an evacuation", 1)
return
/client/proc/admin_cancel_shuttle()
set category = "Admin"
- set name = "Cancel Shuttle"
+ set name = "Cancel Evacuation"
if(!check_rights(R_ADMIN)) return
if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return
- if(!ROUND_IS_STARTED || !emergency_shuttle.can_recall())
+ if(!ROUND_IS_STARTED || !evacuation_controller)
return
- emergency_shuttle.recall()
+ evacuation_controller.cancel_evacuation()
feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- log_admin("[key_name(usr)] admin-recalled the emergency shuttle.",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.", 1)
+ log_admin("[key_name(usr)] admin-cancelled the evacuation.",admin_key=key_name(usr))
+ message_admins("[key_name_admin(usr)] admin-cancelled the evacuation.", 1)
return
/client/proc/admin_deny_shuttle()
set category = "Admin"
- set name = "Toggle Deny Shuttle"
+ set name = "Toggle Deny Evacuation"
if (!ROUND_IS_STARTED)
return
- if(!check_rights(R_ADMIN)) return
+ if(!check_rights(R_ADMIN))
+ return
- emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle
+ evacuation_controller.deny = !evacuation_controller.deny
- log_admin("[key_name(src)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.",admin_key=key_name(usr))
- message_admins("[key_name_admin(usr)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.")
+ log_admin("[key_name(src)] has [evacuation_controller.deny ? "denied" : "allowed"] the evacuation to be called.",admin_key=key_name(usr))
+ message_admins("[key_name_admin(usr)] has [evacuation_controller.deny ? "denied" : "allowed"] the evacuation to be called.")
/client/proc/cmd_admin_attack_log(mob/M as mob in mob_list)
set category = "Special Verbs"
diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm
index 26f914e823e..f66579fa899 100644
--- a/code/modules/admin/view_variables/view_variables.dm
+++ b/code/modules/admin/view_variables/view_variables.dm
@@ -21,6 +21,10 @@
if(!D)
return
+ var/static/list/blacklist = list(/datum/configuration)
+ if(blacklist[D.type])
+ return
+
var/icon/sprite
if(istype(D, /atom))
var/atom/A = D
diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
index a37fc24954c..08c548f8c82 100644
--- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm
+++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
@@ -211,9 +211,12 @@ var/datum/gear_tweak/custom_name/gear_tweak_free_name = new()
if(!metadata)
return I.name
I.name = metadata
- if(I.vars["base_name"])
- I.vars["base_name"] = metadata
+ // For reasons unknown, using SEND_SIGNAL instead of what is below makes rag renaming completely break.
+ var/datum/component/base_name/BN = I.GetComponent(/datum/component/base_name)
+ if(BN)
+ BN.rename(metadata)
+
/*
Custom Description
*/
diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
index fe79389be80..5f1e3558cd9 100644
--- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
@@ -208,8 +208,10 @@
shirt["cropped dress shirt, rolled up"] = /obj/item/clothing/accessory/dressshirt/crop/rolled
shirt["dress shirt, alt"] = /obj/item/clothing/accessory/dressshirt/alt
shirt["dress shirt, alt rolled up"] = /obj/item/clothing/accessory/dressshirt/alt/rolled
- shirt["dress shirt, V-Neck alt"] = /obj/item/clothing/accessory/dressshirt/alt/vneck
- shirt["dress shirt, V-Neck alt rolled up"] = /obj/item/clothing/accessory/dressshirt/alt/vneck/rolled
+ shirt["dress shirt, v-neck alt"] = /obj/item/clothing/accessory/dressshirt/alt/vneck
+ shirt["dress shirt, v-neck alt rolled up"] = /obj/item/clothing/accessory/dressshirt/alt/vneck/rolled
+ shirt["dress shirt, deep v-neck"] = /obj/item/clothing/accessory/dressshirt/deepv
+ shirt["dress shirt, deep v-neck rolled up"] = /obj/item/clothing/accessory/dressshirt/deepv/rolled
shirt["long-sleeved shirt"] = /obj/item/clothing/accessory/longsleeve
shirt["long-sleeved shirt, black striped"] = /obj/item/clothing/accessory/longsleeve_s
shirt["long-sleeved shirt, blue striped"] = /obj/item/clothing/accessory/longsleeve_sb
@@ -233,6 +235,22 @@
shirts["random colored silversun shirt"] = /obj/item/clothing/accessory/silversun/random
gear_tweaks += new /datum/gear_tweak/path(shirts)
+/datum/gear/accessory/university
+ display_name = "university sweatshirt selection"
+ path = /obj/item/clothing/accessory/university
+ description = "A selection of university sweatshirts."
+ flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION
+
+/datum/gear/accessory/university/New()
+ ..()
+ var/list/university = list()
+ university["grey university sweatshirt"] = /obj/item/clothing/accessory/university
+ university["crimson university sweatshirt"] = /obj/item/clothing/accessory/university/red
+ university["mustard university sweatshirt"] = /obj/item/clothing/accessory/university/yellow
+ university["navy university sweatshirt"] = /obj/item/clothing/accessory/university/blue
+ university["black university sweatshirt"] = /obj/item/clothing/accessory/university/black
+ gear_tweaks += new /datum/gear_tweak/path(university)
+
/datum/gear/accessory/scarf
display_name = "scarf selection"
path = /obj/item/clothing/accessory/scarf
@@ -298,6 +316,11 @@
path = /obj/item/clothing/accessory/badge/namepin
flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION
+/datum/gear/accessory/ribbon
+ display_name = "ribbon (colourable)"
+ path = /obj/item/clothing/accessory/ribbon
+ flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION
+
/datum/gear/accessory/sleeve_patch
display_name = "shoulder sleeve patch"
path = /obj/item/clothing/accessory/sleevepatch
diff --git a/code/modules/client/preference_setup/loadout/loadout_augments.dm b/code/modules/client/preference_setup/loadout/loadout_augments.dm
index ae24e7665d9..27aa817ead1 100644
--- a/code/modules/client/preference_setup/loadout/loadout_augments.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_augments.dm
@@ -9,12 +9,19 @@
flags = GEAR_NO_SELECTION
/datum/gear/augment/eye_sensors
- display_name = "integrated eye sensors"
+ display_name = "integrated eye sensors selection"
description = "An eye augment that allows the user to deploy medical or security sensors."
path = /obj/item/organ/internal/augment/eye_sensors
- cost = 3
+ cost = 1
whitelisted = list(SPECIES_HUMAN, SPECIES_HUMAN_OFFWORLD, SPECIES_TAJARA, SPECIES_TAJARA_ZHAN, SPECIES_TAJARA_MSAI, SPECIES_UNATHI, SPECIES_SKRELL, SPECIES_IPC, SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR)
+/datum/gear/augment/eye_sensors/New()
+ ..()
+ var/list/sensors = list()
+ sensors["eye sensors, security"] = /obj/item/organ/internal/augment/eye_sensors/security
+ sensors["eye sensors, medical"] = /obj/item/organ/internal/augment/eye_sensors/medical
+ gear_tweaks += new /datum/gear_tweak/path(sensors)
+
/datum/gear/augment/cyber_hair
display_name = "synthetic hair extensions"
description = "A hair augment that allows the user to change the shape and color of their hair."
@@ -137,6 +144,13 @@
description = "A set of corrective lenses that can be deployed & retracted."
path = /obj/item/organ/internal/augment/tool/correctivelens
+/datum/gear/augment/glare_dampeners
+ display_name = "retractable glare dampeners"
+ description = "A subdermal implant installed just above the brow line that deploys a thin sheath of hyperpolycarbonate that protects from eye damage associated with arc flash."
+ path = /obj/item/organ/internal/augment/tool/correctivelens/glare_dampener
+ allowed_roles = list("Chief Engineer", "Station Engineer", "Atmospheric Technician", "Engineering Apprentice", "Roboticist")
+ cost = 2
+
/datum/gear/augment/head_fluff
display_name = "Custom head augmentation"
description = "A fluff based augmentation that can be renamed/redescribed to appear as something else for RP purposes."
diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm
index bd7bc68b8cb..7c931751547 100644
--- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm
@@ -38,6 +38,7 @@
glasses["sunglasses, aviator"] = /obj/item/clothing/glasses/fakesunglasses/aviator
glasses["sunglasses, prescription"] = /obj/item/clothing/glasses/fakesunglasses/prescription
glasses["sunglasses, fat"] = /obj/item/clothing/glasses/fakesunglasses/big
+ glasses["sunglasses, visor"] = /obj/item/clothing/glasses/fakesunglasses/visor
gear_tweaks += new /datum/gear_tweak/path(glasses)
/datum/gear/eyes/sunglasses
@@ -53,6 +54,7 @@
glasses["flash-proof sunglasses, aviator"] = /obj/item/clothing/glasses/sunglasses/aviator
glasses["flash-proof sunglasses, prescription"] = /obj/item/clothing/glasses/sunglasses/prescription
glasses["flash-proof sunglasses, fat"] = /obj/item/clothing/glasses/sunglasses/big
+ glasses["flash-proof sunglasses, visor"] = /obj/item/clothing/glasses/sunglasses/visor
gear_tweaks += new /datum/gear_tweak/path(glasses)
/datum/gear/eyes/goggles
@@ -82,6 +84,7 @@
medhud["HUD, medical"] = /obj/item/clothing/glasses/hud/health
medhud["HUDpatch, medical"] = /obj/item/clothing/glasses/eyepatch/hud/medical
medhud["prescription HUD, medical"] = /obj/item/clothing/glasses/hud/health/prescription
+ medhud["visor sunglasses, medical"] = /obj/item/clothing/glasses/hud/health/aviator/visor
gear_tweaks += new /datum/gear_tweak/path(medhud)
/datum/gear/eyes/sechuds
@@ -99,6 +102,7 @@
sechud["HUD, security"] = /obj/item/clothing/glasses/hud/security
sechud["HUDpatch, security"] = /obj/item/clothing/glasses/eyepatch/hud/security
sechud["prescription HUD, security"] = /obj/item/clothing/glasses/hud/security/prescription
+ sechud["visor sunglasses, security"] = /obj/item/clothing/glasses/sunglasses/sechud/aviator/visor
gear_tweaks += new /datum/gear_tweak/path(sechud)
/datum/gear/eyes/hudpatch
diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm
index 75ce599728d..0519fffe024 100644
--- a/code/modules/client/preference_setup/loadout/loadout_general.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_general.dm
@@ -165,6 +165,7 @@
banners["banner, Coalition of Colonies"] = /obj/item/flag/coalition
banners["banner, Confederate States of Fisanduh"] = /obj/item/flag/fisanduh
banners["banner, Gadpathur"] = /obj/item/flag/gadpathur
+ banners["banner, Vysoka"] = /obj/item/flag/vysoka
gear_tweaks += new /datum/gear_tweak/path(banners)
/datum/gear/standard
@@ -212,6 +213,7 @@
flags["flag, Coalition of Colonies"] = /obj/item/flag/coalition/l
flags["flag, Confederate States of Fisanduh"] = /obj/item/flag/fisanduh/l
flags["flag, Gadpathur"] = /obj/item/flag/gadpathur/l
+ flags["flag, Vysoka"] = /obj/item/flag/vysoka/l
gear_tweaks += new /datum/gear_tweak/path(flags)
/datum/gear/towel
diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm
index feca7b1ec1c..409f711ea3a 100644
--- a/code/modules/client/preference_setup/loadout/loadout_head.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_head.dm
@@ -61,6 +61,7 @@
softcaps["softcap, cargo"] = /obj/item/clothing/head/softcap/cargo
softcaps["softcap, mining"] = /obj/item/clothing/head/softcap/miner
softcaps["softcap, janitor"] = /obj/item/clothing/head/softcap/janitor
+ softcaps["softcap, tcfl"] = /obj/item/clothing/head/softcap/tcfl
gear_tweaks += new /datum/gear_tweak/path(softcaps)
diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm
index aeb2ed3237a..718c11f5e5d 100644
--- a/code/modules/client/preference_setup/loadout/loadout_suit.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm
@@ -108,32 +108,33 @@ datum/gear/suit/colorvest
cost = 1
allowed_roles = list("Scientist", "Chief Medical Officer", "Physician", "Surgeon", "Pharmacist", "First Responder", "Medical Intern", "Xenobiologist", "Roboticist", "Research Director", "Investigator")
-/datum/gear/suit/medical_chest_rig
- display_name = "medic chest-rig"
- description = "A white chest-rig with pouches worn by medical first responders, meant to carry their equipment."
- path = /obj/item/clothing/suit/storage/medical_chest_rig
+/datum/gear/suit/medical_outerwear
+ display_name = "medical outerwear (jackets, vests, rigs)"
+ path = /obj/item/clothing/suit/storage/toggle/fr_jacket
allowed_roles = list("Chief Medical Officer", "Physician", "Surgeon", "Pharmacist", "First Responder", "Medical Intern")
-/datum/gear/suit/first_responder_vest
- display_name = "first responder vest"
- description = "A dark green vest adorned with high-visibility stripes. Has pouches to carry equipment with."
- path = /obj/item/clothing/suit/storage/medical_chest_rig/first_responder
- allowed_roles = list("Chief Medical Officer", "Physician", "Surgeon", "Pharmacist", "First Responder", "Medical Intern")
+/datum/gear/suit/medical_outerwear/New()
+ ..()
+ var/list/medical_outerwear = list()
+ medical_outerwear["medical chest-rig"] = /obj/item/clothing/suit/storage/medical_chest_rig
+ medical_outerwear["first responder vest"] = /obj/item/clothing/suit/storage/medical_chest_rig/first_responder
+ medical_outerwear["first responder jacket"] = /obj/item/clothing/suit/storage/toggle/fr_jacket/alt
+ medical_outerwear["first responder jacket, alt"] = /obj/item/clothing/suit/storage/toggle/fr_jacket // this variant is the old bright green one and doesn't go with the NT medic uniform
+ gear_tweaks += new /datum/gear_tweak/path(medical_outerwear)
-/datum/gear/suit/iacvest
- display_name = "IAC vest"
- description = "It's a lightweight vest. Made of a dark, navy mesh with highly-reflective white material, designed to be worn by the Interstellar Aid Corps."
+/datum/gear/suit/iac_outerwear
+ display_name = "IAC outerwear (jackets, vests, rigs)"
path = /obj/item/clothing/suit/storage/iacvest
allowed_roles = list("Chief Medical Officer", "Physician", "Surgeon", "Pharmacist", "First Responder", "Medical Intern")
flags = GEAR_HAS_DESC_SELECTION
-/datum/gear/suit/iaclabcoat
- display_name = "IAC labcoat"
- description = "It's a standard medical labcoat designed to be worn by the Interstellar Aid Corps."
- path = /obj/item/clothing/suit/storage/toggle/labcoat/iac
- allowed_roles = list("Chief Medical Officer", "Physician", "Surgeon", "Pharmacist", "First Responder", "Medical Intern")
- flags = GEAR_HAS_DESC_SELECTION
-
+/datum/gear/suit/iac_outerwear/New()
+ ..()
+ var/list/iac_outerwear = list()
+ iac_outerwear["IAC vest"] = /obj/item/clothing/suit/storage/iacvest
+ iac_outerwear["IAC labcoat"] = /obj/item/clothing/suit/storage/toggle/labcoat/iac
+ gear_tweaks += new /datum/gear_tweak/path(iac_outerwear)
+
/datum/gear/suit/poncho
display_name = "poncho selection"
path = /obj/item/clothing/accessory/poncho
diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno/tajara.dm b/code/modules/client/preference_setup/loadout/loadout_xeno/tajara.dm
index 1cdb44ab750..19fc7c1259c 100644
--- a/code/modules/client/preference_setup/loadout/loadout_xeno/tajara.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_xeno/tajara.dm
@@ -247,7 +247,7 @@
display_name = "tesla spine"
description = "A People's Republic of Adhomai made tesla spine issued to disabled veterans and civilians."
path = /obj/item/organ/internal/augment/tesla
- cost = 4
+ cost = 2
whitelisted = list(SPECIES_TAJARA, SPECIES_TAJARA_ZHAN, SPECIES_TAJARA_MSAI)
sort_category = "Xenowear - Tajara"
diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno/vaurca.dm b/code/modules/client/preference_setup/loadout/loadout_xeno/vaurca.dm
index e02e6a409f3..de20b0f2dd3 100644
--- a/code/modules/client/preference_setup/loadout/loadout_xeno/vaurca.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_xeno/vaurca.dm
@@ -41,13 +41,33 @@
flags = GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION
/datum/gear/cape
- display_name = "tunnel cloak"
+ display_name = "tunnel cloak (recolourable)"
path = /obj/item/storage/backpack/cloak
cost = 1
whitelisted = list(SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR)
sort_category = "Xenowear - Vaurca"
flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION
+/datum/gear/cape_selection
+ display_name = "tunnel cloak selection"
+ path = /obj/item/storage/backpack/cloak
+ cost = 1
+ whitelisted = list(SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR)
+ sort_category = "Xenowear - Vaurca"
+ flags = GEAR_HAS_DESC_SELECTION
+
+/datum/gear/cape_selection/New()
+ ..()
+ var/list/capes = list()
+ capes["tunnel cloak, Sedantis"] = /obj/item/storage/backpack/cloak/sedantis
+ capes["tunnel cloak, medical"] = /obj/item/storage/backpack/cloak/medical
+ capes["tunnel cloak, engineering"] = /obj/item/storage/backpack/cloak/engi
+ capes["tunnel cloak, atmospherics"] = /obj/item/storage/backpack/cloak/atmos
+ capes["tunnel cloak, cargo"] = /obj/item/storage/backpack/cloak/cargo
+ capes["tunnel cloak, science"] = /obj/item/storage/backpack/cloak/sci
+ capes["tunnel cloak, security"] = /obj/item/storage/backpack/cloak/sec
+ gear_tweaks += new /datum/gear_tweak/path(capes)
+
/datum/gear/vaurca_robe
display_name = "hive cloak"
description = "A selection of vaurca colored hive cloaks."
diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm
index 458c3fb3d07..0eec562ff4a 100644
--- a/code/modules/clothing/glasses/glasses.dm
+++ b/code/modules/clothing/glasses/glasses.dm
@@ -160,6 +160,12 @@ BLIND // can't see anything
attack_self(usr)
+/obj/item/clothing/glasses/hud/health/aviator/visor
+ name = "medical HUD visor"
+ desc = "Modified visor glasses with a toggled health HUD. Comes with bonus prescription overlay."
+ icon_state = "visor_medhud"
+ off_state = "visor_medhud_off"
+
/obj/item/clothing/glasses/science
name = "science goggles"
desc = "Used to protect your eyes against harmful chemicals!"
@@ -416,6 +422,7 @@ BLIND // can't see anything
item_state = "glasses"
prescription = 7
body_parts_covered = 0
+ canremove = FALSE
/obj/item/clothing/glasses/sunglasses
name = "sunglasses"
@@ -448,6 +455,12 @@ BLIND // can't see anything
icon_state = "bigsunglasses"
item_state = "sun"
+/obj/item/clothing/glasses/sunglasses/visor
+ name = "visor sunglasses"
+ desc = "A pair of visor sunglasses."
+ icon_state = "visor"
+ item_state = "visor"
+
//For style with no powergaming connotations.
/obj/item/clothing/glasses/fakesunglasses
@@ -480,6 +493,12 @@ BLIND // can't see anything
icon_state = "bigsunglasses"
item_state = "sun"
+/obj/item/clothing/glasses/fakesunglasses/visor
+ name = "stylish visor sunglasses"
+ desc = "A pair of designer visor sunglasses."
+ icon_state = "visor"
+ item_state = "visor"
+
/obj/item/clothing/glasses/welding
name = "welding goggles"
desc = "Protects the eyes from welders, approved by the mad scientist association."
@@ -524,12 +543,15 @@ BLIND // can't see anything
/obj/item/clothing/glasses/aug/welding
name = "glare dampeners"
- desc = "Protects the eyes from welders."
- icon_state = "welding-g"
- item_state = "welding-g"
+ desc = "A subdermal implant installed just above the brow line that deploys a thin sheath of hyperpolycarbonate that protects from eye damage associated with arc flash."
+ icon = 'icons/clothing/eyes/welding_goggles.dmi'
+ icon_state = "welding-aug"
+ item_state = "welding-aug"
+ contained_sprite = TRUE
item_flags = THICKMATERIAL
flash_protection = FLASH_PROTECTION_MAJOR
tint = TINT_HEAVY
+ canremove = FALSE
/obj/item/clothing/glasses/aug/throw_at()
usr.drop_from_inventory(src)
@@ -730,6 +752,13 @@ BLIND // can't see anything
icon_state = "aviator_sec_idris"
off_state = "aviator_sec_idris_off"
+obj/item/clothing/glasses/sunglasses/sechud/aviator/visor
+ name = "security HUD visor"
+ desc = "NanoTrasen security visor glasses that can be switched between HUD and flash protection modes. They come with a built-in prescription overlay."
+ icon_state = "visor_sec"
+ off_state = "visor_sec_off"
+ item_state_slots = list(slot_r_hand_str = "visor_sec", slot_l_hand_str = "visor_sec")
+
/obj/item/clothing/glasses/thermal
name = "optical thermal scanner"
desc = "Thermals in the shape of glasses."
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index 733ff98b4dc..7bc84304609 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -3,6 +3,7 @@
desc = "It's a softcap in a tasteless color."
icon = 'icons/obj/clothing/hats/soft_caps.dmi'
icon_state = "softcap"
+ item_state = "softcap"
item_flags = SHOWFLAVORTEXT
contained_sprite = TRUE
var/flipped = FALSE
@@ -30,6 +31,7 @@
/obj/item/clothing/head/softcap/colorable
icon_state = "softcap_colorable"
+ item_state = "softcap_colorable"
/obj/item/clothing/head/softcap/colorable/random/Initialize()
. = ..()
@@ -39,6 +41,7 @@
name = "rainbow cap"
desc = "It's a peaked cap in a bright rainbow of colors."
icon_state = "rainbowsoft"
+ item_state = "rainbowsoft"
/obj/item/clothing/head/softcap/red // Antag red.
name = "red softcap"
@@ -46,6 +49,12 @@
icon_state = "softcap_red"
item_state = "softcap_red"
+/obj/item/clothing/head/softcap/tcfl
+ name = "tcfl cap"
+ desc = "A rugged softcap in TCFL colours, go Biesel!"
+ icon_state = "tcfl"
+ item_state = "tcfl"
+
// Departmental softcaps. By Wowzewow (Wezzy).
/obj/item/clothing/head/softcap/captain
@@ -132,6 +141,7 @@
name = "eridani cap"
desc = "A grey EPMC fatigue cap with the symbol of the Eridani Corporate Federation on its front. For amoral mercenaries that prefer style over protection."
icon_state = "eridani"
+ item_state = "eridani"
/obj/item/clothing/head/softcap/zavod
name = "zavodskoi cap"
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 3812db51bd8..b85431b00af 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -8,20 +8,21 @@
heat_protection = UPPER_TORSO|LOWER_TORSO
max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE
siemens_coefficient = 0.5
- var/obj/item/storage/internal/pockets
+ var/obj/item/storage/internal/pockets = /obj/item/storage/internal
var/pocket_slots = 2
var/pocket_size = 2
var/pocket_total = null//This will be calculated, unless specifically overidden
/obj/item/clothing/suit/armor/Initialize()
. = ..()
- pockets = new /obj/item/storage/internal(src)
- pockets.storage_slots = pocket_slots //two slots
- pockets.max_w_class = pocket_size //fit only pocket sized items
- if (pocket_total)
- pockets.max_storage_space = pocket_total
- else
- pockets.max_storage_space = pocket_slots * pocket_size
+ if(pockets)
+ pockets = new pockets(src)
+ pockets.storage_slots = pocket_slots //two slots
+ pockets.max_w_class = pocket_size //fit only pocket sized items
+ if (pocket_total)
+ pockets.max_storage_space = pocket_total
+ else
+ pockets.max_storage_space = pocket_slots * pocket_size
/obj/item/clothing/suit/armor/Destroy()
if (pockets)
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 1a31fe2a598..7854fbaec99 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -295,10 +295,17 @@ obj/item/clothing/suit/apron/overalls/blue
item_state = "fr_jacket"
blood_overlay_type = "armor"
allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \
- /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/tank/emergency_oxygen, /obj/item/device/breath_analyzer, /obj/item/reagent_containers/blood)
+ /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/tank/emergency_oxygen, /obj/item/device/breath_analyzer, /obj/item/reagent_containers/blood, /obj/item/clothing/head/hardhat/first_responder)
body_parts_covered = UPPER_TORSO|ARMS
opened = TRUE
+/obj/item/clothing/suit/storage/toggle/fr_jacket/alt
+ desc = "A dark green first responder jacket."
+ icon = 'icons/clothing/kit/first_responder.dmi'
+ contained_sprite = TRUE
+ icon_state = "firstresponderjacket"
+ item_state = "firstresponderjacket"
+
/obj/item/clothing/suit/storage/toggle/fr_jacket/ems
name = "\improper EMS jacket"
desc = "A dark blue, martian-pattern, EMS jacket. It sports high-visibility reflective stripes and a star of life on the back."
@@ -331,11 +338,3 @@ obj/item/clothing/suit/apron/overalls/blue
icon_state = "suspenders"
blood_overlay_type = "armor" //it's the less thing that I can put here
body_parts_covered = 0
-
-/obj/item/clothing/suit/storage/toggle/first_responder_jacket
- name = "first responder jacket"
- desc = "A dark green first responder jacket."
- icon = 'icons/clothing/kit/first_responder.dmi'
- contained_sprite = TRUE
- icon_state = "firstresponderjacket"
- item_state = "firstresponderjacket"
diff --git a/code/modules/clothing/suits/modular_armor.dm b/code/modules/clothing/suits/modular_armor.dm
index dfdc0734a68..b74bbc8986e 100644
--- a/code/modules/clothing/suits/modular_armor.dm
+++ b/code/modules/clothing/suits/modular_armor.dm
@@ -9,55 +9,64 @@
w_class = ITEMSIZE_NORMAL
restricted_accessory_slots = list(ACCESSORY_SLOT_ARMOR_PLATE, ACCESSORY_SLOT_ARM_GUARDS, ACCESSORY_SLOT_LEG_GUARDS, ACCESSORY_SLOT_ARMOR_POCKETS)
valid_accessory_slots = list(ACCESSORY_SLOT_ARMOR_PLATE, ACCESSORY_SLOT_ARM_GUARDS, ACCESSORY_SLOT_LEG_GUARDS, ACCESSORY_SLOT_ARMOR_POCKETS, ACCESSORY_SLOT_GENERIC, ACCESSORY_SLOT_ARMBAND, ACCESSORY_SLOT_CAPE)
+ pockets = null
/obj/item/clothing/suit/armor/carrier/officer
starting_accessories = list(
- /obj/item/clothing/accessory/armor_plate
+ /obj/item/clothing/accessory/armor_plate,
+ /obj/item/clothing/accessory/storage/modular_pouch
)
/obj/item/clothing/suit/armor/carrier/hos
starting_accessories = list(
- /obj/item/clothing/accessory/armor_plate/hos
+ /obj/item/clothing/accessory/armor_plate/hos,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/suit/armor/carrier/generic
starting_accessories = list(
- /obj/item/clothing/accessory/armor_plate/generic
+ /obj/item/clothing/accessory/armor_plate/generic,
+ /obj/item/clothing/accessory/storage/modular_pouch
)
/obj/item/clothing/suit/armor/carrier/riot
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/riot,
/obj/item/clothing/accessory/leg_guard/riot,
- /obj/item/clothing/accessory/arm_guard/riot
+ /obj/item/clothing/accessory/arm_guard/riot,
+ /obj/item/clothing/accessory/storage/modular_pouch
)
/obj/item/clothing/suit/armor/carrier/ballistic
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/ballistic,
/obj/item/clothing/accessory/leg_guard/ballistic,
- /obj/item/clothing/accessory/arm_guard/ballistic
+ /obj/item/clothing/accessory/arm_guard/ballistic,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/suit/armor/carrier/ablative
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/ablative,
/obj/item/clothing/accessory/leg_guard/ablative,
- /obj/item/clothing/accessory/arm_guard/ablative
+ /obj/item/clothing/accessory/arm_guard/ablative,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/suit/armor/carrier/military
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/military,
/obj/item/clothing/accessory/leg_guard/military,
- /obj/item/clothing/accessory/arm_guard/military
+ /obj/item/clothing/accessory/arm_guard/military,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/suit/armor/carrier/heavy
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/heavy,
/obj/item/clothing/accessory/leg_guard/heavy,
- /obj/item/clothing/accessory/arm_guard/heavy
+ /obj/item/clothing/accessory/arm_guard/heavy,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/suit/armor/carrier/heavy/scc
@@ -65,6 +74,7 @@
/obj/item/clothing/accessory/armor_plate/heavy/scc,
/obj/item/clothing/accessory/leg_guard/heavy/scc,
/obj/item/clothing/accessory/arm_guard/heavy/scc,
+ /obj/item/clothing/accessory/storage/modular_pouch/large,
/obj/item/clothing/accessory/sleevepatch/scc
)
@@ -72,7 +82,8 @@
starting_accessories = list(
/obj/item/clothing/accessory/armor_plate/heavy/sec,
/obj/item/clothing/accessory/leg_guard/heavy/sec,
- /obj/item/clothing/accessory/arm_guard/heavy/sec
+ /obj/item/clothing/accessory/arm_guard/heavy/sec,
+ /obj/item/clothing/accessory/storage/modular_pouch/large
)
/obj/item/clothing/accessory/armor_plate
@@ -183,6 +194,31 @@
item_state = "plate_blue"
slowdown = 0 // the SCC is hacking
+/obj/item/clothing/accessory/storage/modular_pouch
+ name = "plate carrier pouches"
+ desc = "A comfortable set of pouches that can be attached to a plate carrier, allowing the wearer to store some small items."
+ icon = 'icons/clothing/kit/modular_armor.dmi'
+ icon_state = "modular_pouch"
+ item_state = "modular_pouch"
+ contained_sprite = TRUE
+ slot = ACCESSORY_SLOT_ARMOR_POCKETS
+ slots = 2
+
+/obj/item/clothing/accessory/storage/modular_pouch/large
+ name = "large plate carrier pouches"
+ desc = "A comfortable set of pouches that can be attached to a plate carrier, allowing the wearer to store some small items. This one uses advanced sewing techniques for additional storage capacity."
+ slots = 3
+
+/obj/item/clothing/accessory/holster/modular
+ name = "plate carrier holster"
+ desc = "A special holster with rigging able to attach to modern modular plate carriers."
+ icon = 'icons/clothing/kit/modular_armor.dmi'
+ icon_state = "modular_holster"
+ item_state = "modular_holster"
+ contained_sprite = TRUE
+ slot = ACCESSORY_SLOT_ARMOR_POCKETS
+ flippable = FALSE
+
/obj/item/clothing/accessory/armor_plate/heavy/sec
name = "heavy corporate armor plate"
desc = "A heavy and stylish armor plate with blue highlights. That prevents teamkills, right?"
diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm
index 923e75ebfa8..71b7335dd5b 100644
--- a/code/modules/clothing/suits/storage.dm
+++ b/code/modules/clothing/suits/storage.dm
@@ -64,13 +64,6 @@
icon_state = "[initial(icon_state)][opened ? "_open" : ""]"
item_state = icon_state
-/obj/item/clothing/suit/armor/carrier/heavy/Initialize()
- . = ..()
- pockets = new/obj/item/storage/internal(src)
- pockets.storage_slots = 4
- pockets.max_w_class = ITEMSIZE_SMALL
- pockets.max_storage_space = 8
-
/obj/item/clothing/suit/storage/vest/hos/Initialize()
. = ..()
pockets = new/obj/item/storage/internal(src)
diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm
index 0f46f34e5d2..2e21a10fdc7 100644
--- a/code/modules/clothing/under/accessories/accessory.dm
+++ b/code/modules/clothing/under/accessories/accessory.dm
@@ -648,6 +648,14 @@
slot_flags = SLOT_TIE
w_class = ITEMSIZE_TINY
+/obj/item/clothing/accessory/ribbon
+ name = "ribbon"
+ desc = "A small ribbon to commemorate or support a cause."
+ icon_state = "ribbon"
+ item_state = "ribbon"
+ slot_flags = SLOT_TIE
+ w_class = ITEMSIZE_TINY
+
/obj/item/clothing/accessory/sleevepatch
name = "sleeve patch"
desc = "An embroidered patch which can be attached to the shoulder sleeve of clothing."
diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm
index dbb79dc1555..3f0bb3bcfeb 100644
--- a/code/modules/clothing/under/accessories/holster.dm
+++ b/code/modules/clothing/under/accessories/holster.dm
@@ -8,11 +8,17 @@
var/sound_out = 'sound/weapons/holster/holsterout.ogg'
flippable = 1
w_class = ITEMSIZE_NORMAL
- var/base_name = ""
/obj/item/clothing/accessory/holster/Initialize()
. = ..()
- base_name = name
+ AddComponent(/datum/component/base_name, name)
+
+/obj/item/clothing/accessory/holster/proc/update_name(var/base_name = initial(name))
+ SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
+ if(holstered)
+ name = "occupied [base_name]"
+ else
+ name = "[base_name]"
/obj/item/clothing/accessory/holster/proc/holster(var/obj/item/I, var/mob/living/user)
if(holstered && istype(user))
@@ -33,11 +39,11 @@
holstered.add_fingerprint(user)
w_class = max(w_class, holstered.w_class)
user.visible_message("[user] holsters \the [holstered].", "You holster \the [holstered].")
- name = "occupied [base_name]"
+ update_name()
/obj/item/clothing/accessory/holster/proc/clear_holster()
holstered = null
- name = base_name
+ update_name()
/obj/item/clothing/accessory/holster/proc/unholster(mob/user as mob)
if(!holstered)
@@ -111,13 +117,13 @@
//can't we just use src here?
var/obj/item/clothing/accessory/holster/H = null
- if (istype(src, /obj/item/clothing/accessory/holster))
+ if(istype(src, /obj/item/clothing/accessory/holster))
H = src
- else if (istype(src, /obj/item/clothing/under))
- var/obj/item/clothing/under/S = src
- if (LAZYLEN(S.accessories))
+ if(!H && istype(src, /obj/item/clothing))
+ var/obj/item/clothing/S = src
+ if(LAZYLEN(S.accessories))
H = locate() in S.accessories
- else if (istype(src, /obj/item/clothing/suit/armor/tactical)) // This armor is a snowflake and has an integrated holster.
+ if(!H && istype(src, /obj/item/clothing/suit/armor/tactical)) // This armor is a snowflake and has an integrated holster.
var/obj/item/clothing/suit/armor/tactical/tacticool = src
H = tacticool.holster
diff --git a/code/modules/clothing/under/accessories/shirts.dm b/code/modules/clothing/under/accessories/shirts.dm
index 5e11c6433ff..a0ab90bb3c0 100644
--- a/code/modules/clothing/under/accessories/shirts.dm
+++ b/code/modules/clothing/under/accessories/shirts.dm
@@ -132,6 +132,12 @@
icon_state = "dressshirtvneck_alt"
item_state = "dressshirtvneck_alt"
+/obj/item/clothing/accessory/dressshirt/deepv
+ name = "deep v-neck dress shirt"
+ desc = "A casual dress shirt with a deep neckline."
+ icon_state = "dressshirt_deepv"
+ item_state = "dressshirt_deepv"
+
/obj/item/clothing/accessory/dressshirt/crop
name = "cropped dress shirt"
desc = "A casual cropped dress shirt."
@@ -153,10 +159,16 @@
/obj/item/clothing/accessory/dressshirt/alt/vneck/rolled
name = "v-neck dress shirt"
- desc = "A casual dress shirt."
+ desc = "A casual dress shirt. This one has its sleeves rolled up."
icon_state = "dressshirtvneck_alt_r"
item_state = "dressshirtvneck_alt_r"
+/obj/item/clothing/accessory/dressshirt/deepv/rolled
+ name = "deep v-neck dress shirt"
+ desc = "A casual dress shirt with a deep neckline. This one has its sleeves rolled up."
+ icon_state = "dressshirt_deepv_r"
+ item_state = "dressshirt_deepv_r"
+
/obj/item/clothing/accessory/dressshirt/crop/rolled
name = "cropped dress shirt"
desc = "A casual cropped dress shirt. This one has its sleeves rolled up"
@@ -269,3 +281,29 @@
if(prob(50))
icon_state = "hawaii_red"
color = color_rotation(rand(-11,12)*15)
+
+/obj/item/clothing/accessory/university
+ name = "university sweatshirt"
+ desc = "A comfy university sweatshirt. This one is grey."
+ icon_state = "usweatshirt_grey"
+ item_state = "usweatshirt_grey"
+
+/obj/item/clothing/accessory/university/red
+ desc = "A comfy university sweatshirt. This one is crimson."
+ icon_state = "usweatshirt_red"
+ item_state = "usweatshirt_red"
+
+/obj/item/clothing/accessory/university/yellow
+ desc = "A comfy university sweatshirt. This one is mustard."
+ icon_state = "usweatshirt_yellow"
+ item_state = "usweatshirt_yellow"
+
+/obj/item/clothing/accessory/university/blue
+ desc = "A comfy university sweatshirt. This one is navy."
+ icon_state = "usweatshirt_blue"
+ item_state = "usweatshirt_blue"
+
+/obj/item/clothing/accessory/university/black
+ desc = "A comfy university sweatshirt. This one is black."
+ icon_state = "usweatshirt_black"
+ item_state = "usweatshirt_black"
\ No newline at end of file
diff --git a/code/modules/clothing/wrists/watches.dm b/code/modules/clothing/wrists/watches.dm
index e43f4b3b85f..7551908bd56 100644
--- a/code/modules/clothing/wrists/watches.dm
+++ b/code/modules/clothing/wrists/watches.dm
@@ -52,8 +52,8 @@
if(wired && screwed)
to_chat(usr, "You check your watch, spotting a digital collection of numbers reading '[worldtime2text()]'. Today's date is '[time2text(world.time, "Month DD")]. [game_year]'.")
- if (emergency_shuttle.get_status_panel_eta())
- to_chat(usr, SPAN_WARNING("The shuttle's status is reported as: [emergency_shuttle.get_status_panel_eta()]."))
+ if (evacuation_controller.get_status_panel_eta())
+ to_chat(usr, SPAN_WARNING("The shuttle's status is reported as: [evacuation_controller.get_status_panel_eta()]."))
else if(wired && !screwed)
to_chat(usr, "You check your watch, realising it's still open.")
else
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index b42f9831c3d..d8b0387ee67 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -2133,4 +2133,42 @@ All custom items with worn sprites must follow the contained sprite system: http
closed_desc = " The pages within are a mix of clearly indexed case files, and study notes alongside less clearly indexed pages that appears to be fragmented thoughts, not unlike a diary. The very first page of the journal reads “dedicated to Qirandri Mrorirhaldarr” and is signed “Mrradar Sanufar” underneath."
icon = 'icons/obj/custom_items/kathira_journal.dmi'
icon_state = "kath_journal"
- item_state = "kath_journal"
\ No newline at end of file
+ item_state = "kath_journal"
+
+/obj/item/storage/pill_bottle/dice/fluff/suraya_dicebag //Crevan Dice Bag - Suraya Al-Zahrani - Omicega
+ name = "velvet dice bag"
+ desc = "A deep purple dice bag fashioned from Adhomian velvet, with two little drawstrings to tighten the neck closed."
+ icon = 'icons/obj/custom_items/suraya_dice.dmi'
+ icon_state = "sur_dbag"
+ item_state = "sur_dbag"
+
+ starts_with = list(
+ /obj/item/dice/fluff/suraya_dice = 3,
+ /obj/item/dice/fluff/suraya_dice/alt = 3
+ )
+
+/obj/item/dice/fluff/suraya_dice
+ name = "blue adhomian die"
+ desc = "A blue-and-gold wooden die with six sides, beautifully carved and delicately painted. The single dot on the number one side is, on closer inspection, a miniature image of the god Rredouane."
+ icon = 'icons/obj/custom_items/suraya_dice.dmi'
+ icon_state = "sur_b_d1"
+ base_icon = "sur_b_d"
+ favored_number = 1
+ weighted_value = 22
+
+/obj/item/dice/fluff/suraya_dice/AltClick(mob/user)
+ weighted = !weighted
+
+ if(!weighted)
+ to_chat(user, SPAN_NOTICE("You jiggle the die rapidly in your hand, resetting the internal weighting."))
+ else
+ to_chat(user, SPAN_NOTICE("You carefully jiggle the die one way, then the other, allowing its internal weighting to lock into place."))
+
+ user.visible_message("\The [user] jiggles \the [src] around in their hand for a second.")
+
+/obj/item/dice/fluff/suraya_dice/alt
+ name = "green adhomian die"
+ desc = "A green-and-silver wooden die with six sides, beautifully carved and delicately painted. The single dot on the number one side is, on closer inspection, a miniature image of the god Rredouane."
+ icon_state = "sur_g_d1"
+ base_icon = "sur_g_d"
+ favored_number = 6
\ No newline at end of file
diff --git a/code/modules/detectivework/tools/evidencebag.dm b/code/modules/detectivework/tools/evidencebag.dm
index 34a788e56b3..77b897e3df3 100644
--- a/code/modules/detectivework/tools/evidencebag.dm
+++ b/code/modules/detectivework/tools/evidencebag.dm
@@ -9,11 +9,10 @@
w_class = ITEMSIZE_SMALL
var/obj/item/stored_item = null
var/label_text = ""
- var/base_name = ""
/obj/item/evidencebag/Initialize()
. = ..()
- base_name = name
+ AddComponent(/datum/component/base_name, name)
/obj/item/evidencebag/MouseDrop(var/obj/item/I as obj)
if (!ishuman(usr))
@@ -111,7 +110,8 @@
return
. = ..()
-/obj/item/evidencebag/proc/update_name_label()
+/obj/item/evidencebag/proc/update_name_label(var/base_name = initial(name))
+ SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
if(label_text == "")
name = base_name
else
diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm
index 2816bb61bf6..c519743a024 100644
--- a/code/modules/detectivework/tools/rag.dm
+++ b/code/modules/detectivework/tools/rag.dm
@@ -64,7 +64,8 @@
update_name()
update_icon()
-/obj/item/reagent_containers/glass/rag/proc/update_name()
+/obj/item/reagent_containers/glass/rag/proc/update_name(var/base_name = initial(name))
+ SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
if(on_fire)
name = "burning [base_name]"
else if(reagents.total_volume)
@@ -107,7 +108,7 @@
/obj/item/reagent_containers/glass/rag/proc/wipe_down(atom/A, mob/user)
if(!reagents.total_volume)
- to_chat(user, SPAN_WARNING("\The [base_name] is dry!"))
+ to_chat(user, SPAN_WARNING("\The [name] is dry!"))
else
if (!(last_clean && world.time < last_clean + 120) )
user.visible_message("[user] starts to wipe [A] with [src].")
diff --git a/code/modules/effects/map_effects/window_spawner.dm b/code/modules/effects/map_effects/window_spawner.dm
index 1d5a37f01ab..90236c4e163 100644
--- a/code/modules/effects/map_effects/window_spawner.dm
+++ b/code/modules/effects/map_effects/window_spawner.dm
@@ -5,6 +5,7 @@
var/grill_path = /obj/structure/grille
var/spawn_firedoor = FALSE
var/activated
+ var/single_window = FALSE
// stops ZAS expanding zones past us, the windows will block the zone anyway
/obj/effect/map_effect/wingrille_spawn/CanPass()
@@ -37,23 +38,26 @@
if (!locate(grill_path) in get_turf(src))
var/obj/structure/grille/G = new grill_path(src.loc)
handle_grille_spawn(G)
- var/list/neighbours = list()
- for (var/dir in cardinal)
- var/turf/T = get_step(src, dir)
- var/obj/effect/map_effect/wingrille_spawn/other = locate(/obj/effect/map_effect/wingrille_spawn) in T
- if(!other)
- var/found_connection
- if(locate(grill_path) in T)
- for(var/obj/structure/window/W in T)
- if(W.type == win_path && W.dir == get_dir(T,src))
- found_connection = 1
- qdel(W)
- if(!found_connection)
- var/obj/structure/window/new_win = new win_path(src.loc)
- new_win.set_dir(dir)
- handle_window_spawn(new_win)
- else
- neighbours |= other
+ if(!single_window)
+ var/list/neighbours = list()
+ for (var/dir in cardinal)
+ var/turf/T = get_step(src, dir)
+ var/obj/effect/map_effect/wingrille_spawn/other = locate(/obj/effect/map_effect/wingrille_spawn) in T
+ if(!other)
+ var/found_connection
+ if(locate(grill_path) in T)
+ for(var/obj/structure/window/W in T)
+ if(W.type == win_path && W.dir == get_dir(T,src))
+ found_connection = 1
+ qdel(W)
+ if(!found_connection)
+ var/obj/structure/window/new_win = new win_path(src.loc)
+ new_win.set_dir(dir)
+ handle_window_spawn(new_win)
+ else
+ neighbours |= other
+ else
+ new win_path(loc)
activated = 1
/obj/effect/map_effect/wingrille_spawn/proc/handle_window_spawn(var/obj/structure/window/W)
@@ -97,4 +101,23 @@
/obj/effect/map_effect/wingrille_spawn/reinforced/polarized/handle_window_spawn(var/obj/structure/window/reinforced/polarized/P)
if(id)
- P.id = id
\ No newline at end of file
+ P.id = id
+
+/obj/effect/map_effect/wingrille_spawn/full
+ name = "full window grille spawner"
+ icon_state = "full_window"
+ win_path = /obj/structure/window/full
+ grill_path = /obj/structure/grille
+ spawn_firedoor = TRUE
+ single_window = TRUE
+
+/obj/effect/map_effect/wingrille_spawn/full/phoron
+ icon_state = "full_phoron_window"
+ win_path = /obj/structure/window/full/phoron
+ single_window = TRUE
+
+/obj/effect/map_effect/wingrille_spawn/reinforced/polarized/full
+ name = "full polarized window grille spawner"
+ icon_state = "full_window"
+ win_path = /obj/structure/window/reinforced/polarized/full
+ single_window = TRUE
\ No newline at end of file
diff --git a/code/modules/ghostroles/spawner/base.dm b/code/modules/ghostroles/spawner/base.dm
index d2f78c5eca7..f166a98b83a 100644
--- a/code/modules/ghostroles/spawner/base.dm
+++ b/code/modules/ghostroles/spawner/base.dm
@@ -172,6 +172,7 @@
disable()
if(welcome_message)
to_chat(user, SPAN_NOTICE(welcome_message))
+ universe.OnPlayerLatejoin(user)
return TRUE
//Proc to check if a specific user can edit this spawner (open/close/...)
diff --git a/code/modules/ghostroles/spawner/human/admin/admin.dm b/code/modules/ghostroles/spawner/human/admin/admin.dm
index b6b2b61f353..1c4d48d7134 100644
--- a/code/modules/ghostroles/spawner/human/admin/admin.dm
+++ b/code/modules/ghostroles/spawner/human/admin/admin.dm
@@ -177,7 +177,7 @@
if(start_location.landmark_tag != "nav_emergency_start")
return TRUE
- if(emergency_shuttle.evac)
+ if(evacuation_controller.emergency_evacuation)
if(get_security_level() != "green" )
var/datum/wifi/sender/door/wifi_sender = new("odin_arrivals_lockdown", SSghostroles)
wifi_sender.activate("close")
diff --git a/code/modules/ghostroles/spawner/human/visitor.dm b/code/modules/ghostroles/spawner/human/visitor.dm
index babdd1ec4ab..f3c94f48961 100644
--- a/code/modules/ghostroles/spawner/human/visitor.dm
+++ b/code/modules/ghostroles/spawner/human/visitor.dm
@@ -13,7 +13,7 @@
//Vars related to human mobs
outfit = /datum/outfit/admin/random/visitor
- possible_species = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJARA, SPECIES_UNATHI)
+ possible_species = list(SPECIES_HUMAN, SPECIES_HUMAN_OFFWORLD, SPECIES_TAJARA, SPECIES_TAJARA_MSAI, SPECIES_TAJARA_ZHAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_WORKER, SPECIES_IPC, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL, SPECIES_IPC_G1, SPECIES_IPC_G2)
allow_appearance_change = APPEARANCE_PLASTICSURGERY
assigned_role = "Visitor"
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index 97996674d3f..c480f2b22de 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -475,6 +475,8 @@
return FALSE
/mob/living/simple_animal/penguin/holodeck/baby
+ name = "baby penguin"
+ desc = "Can't fly and barely waddles, yet the prince of all chicks."
icon_state = "penguin_baby"
icon_living = "penguin_baby"
icon_dead = "penguin_baby_dead"
diff --git a/code/modules/hydroponics/seed_datums/fruits.dm b/code/modules/hydroponics/seed_datums/fruits.dm
index 6e7e0ac109b..c22df4b0f0f 100644
--- a/code/modules/hydroponics/seed_datums/fruits.dm
+++ b/code/modules/hydroponics/seed_datums/fruits.dm
@@ -87,7 +87,7 @@
seed_name = "green grape"
display_name = "green grapevines"
mutants = null
- chems = list(/decl/reagent/nutriment = list(1,10), /decl/reagent/kelotane = list(3,5), /decl/reagent/drink/grapejuice = list(10,10))
+ chems = list(/decl/reagent/nutriment = list(1,10), /decl/reagent/kelotane = list(3,5), /decl/reagent/drink/whitegrapejuice = list(10,10))
/datum/seed/grapes/green/setup_traits()
..()
@@ -375,3 +375,27 @@
/obj/item/seeds/pumpkinseed
seed_type = "pumpkin"
+
+
+//coffee beans are considered a fruit and seed, commonly considered a "cherry" of the plant
+/datum/seed/coffee
+ name = "coffee"
+ seed_name = "coffee beans"
+ display_name = "coffee bush"
+ chems = list(/decl/reagent/nutriment/coffeegrounds = list(2,10))
+ kitchen_tag = "coffee"
+
+/datum/seed/coffee/setup_traits()
+ ..()
+ set_trait(TRAIT_HARVEST_REPEAT,1)
+ set_trait(TRAIT_MATURATION,3)
+ set_trait(TRAIT_PRODUCTION,3)
+ set_trait(TRAIT_YIELD,4)
+ set_trait(TRAIT_POTENCY,2)
+ set_trait(TRAIT_PRODUCT_ICON,"bean2")
+ set_trait(TRAIT_PRODUCT_COLOUR,"#be9109")
+ set_trait(TRAIT_PLANT_ICON,"bush2")
+ set_trait(TRAIT_IDEAL_LIGHT, 6)
+
+/obj/item/seeds/coffeeseed
+ seed_type = "coffee"
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_datums/herbs.dm b/code/modules/hydroponics/seed_datums/herbs.dm
new file mode 100644
index 00000000000..c3b2b59aacf
--- /dev/null
+++ b/code/modules/hydroponics/seed_datums/herbs.dm
@@ -0,0 +1,44 @@
+/datum/seed/mint
+ name = "mint"
+ seed_name = "mint leaf"
+ display_name = "mint plant"
+ chems = list(/decl/reagent/nutriment/mint = list(2,10))
+ kitchen_tag = "mint"
+
+//I wasnt sure if tea and mint would be a flower or something, so i just made a herb file..
+
+/datum/seed/mint/setup_traits()
+ ..()
+ set_trait(TRAIT_MATURATION,3)
+ set_trait(TRAIT_PRODUCTION,3)
+ set_trait(TRAIT_YIELD,3)
+ set_trait(TRAIT_POTENCY,2)
+ set_trait(TRAIT_PRODUCT_ICON,"herb")
+ set_trait(TRAIT_PRODUCT_COLOUR,"#4dd298")
+ set_trait(TRAIT_PLANT_ICON,"herb")
+ set_trait(TRAIT_IDEAL_LIGHT, 6)
+
+/obj/item/seeds/mintseed
+ seed_type = "mint"
+
+
+/datum/seed/tea
+ name = "tea"
+ seed_name = "tea leaf"
+ display_name = "tea plant"
+ chems = list(/decl/reagent/nutriment/teagrounds = list(2,10))
+ kitchen_tag = "tea"
+
+/datum/seed/tea/setup_traits()
+ ..()
+ set_trait(TRAIT_MATURATION,3)
+ set_trait(TRAIT_PRODUCTION,3)
+ set_trait(TRAIT_YIELD,3)
+ set_trait(TRAIT_POTENCY,2)
+ set_trait(TRAIT_PRODUCT_ICON,"herb")
+ set_trait(TRAIT_PRODUCT_COLOUR,"#4fd24d")
+ set_trait(TRAIT_PLANT_ICON,"herb")
+ set_trait(TRAIT_IDEAL_LIGHT, 6)
+
+/obj/item/seeds/teaseed
+ seed_type = "tea"
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm
index d42b240c815..a4ee506c0d3 100644
--- a/code/modules/hydroponics/seed_storage.dm
+++ b/code/modules/hydroponics/seed_storage.dm
@@ -52,6 +52,7 @@
/obj/item/seeds/cherryseed = 3,
/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cocoapodseed = 3,
+ /obj/item/seeds/coffeeseed = 3,
/obj/item/seeds/cornseed = 3,
/obj/item/seeds/dynseed = 3,
/obj/item/seeds/earthenroot = 2,
@@ -64,6 +65,7 @@
/obj/item/seeds/lemonseed = 3,
/obj/item/seeds/limeseed = 3,
/obj/item/seeds/mtearseed = 2,
+ /obj/item/seeds/mintseed = 3,
/obj/item/seeds/nifberries = 2,
/obj/item/seeds/onionseed = 3,
/obj/item/seeds/orangeseed = 3,
@@ -84,6 +86,7 @@
/obj/item/seeds/sugarcaneseed = 3,
/obj/item/seeds/sugartree = 2,
/obj/item/seeds/sunflowerseed = 3,
+ /obj/item/seeds/teaseed = 3,
/obj/item/seeds/tobaccoseed = 3,
/obj/item/seeds/tomatoseed = 3,
/obj/item/seeds/towermycelium = 3,
@@ -115,6 +118,7 @@
/obj/item/seeds/cherryseed = 3,
/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cocoapodseed = 3,
+ /obj/item/seeds/coffeeseed = 3,
/obj/item/seeds/cornseed = 3,
/obj/item/seeds/replicapod = 3,
/obj/item/seeds/earthenroot = 2,
@@ -128,6 +132,7 @@
/obj/item/seeds/libertymycelium = 2,
/obj/item/seeds/limeseed = 3,
/obj/item/seeds/mtearseed = 2,
+ /obj/item/seeds/mintseed = 3,
/obj/item/seeds/nettleseed = 2,
/obj/item/seeds/nifberries = 2,
/obj/item/seeds/onionseed = 3,
@@ -146,6 +151,7 @@
/obj/item/seeds/sunflowerseed = 3,
/obj/item/seeds/sugartree = 2,
/obj/item/seeds/shandseed = 2,
+ /obj/item/seeds/teaseed = 3,
/obj/item/seeds/tobaccoseed = 3,
/obj/item/seeds/tomatoseed = 3,
/obj/item/seeds/towermycelium = 3,
diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm
index 95b2b9674f2..e01ba281b2d 100644
--- a/code/modules/materials/material_recipes.dm
+++ b/code/modules/materials/material_recipes.dm
@@ -52,6 +52,7 @@
new /datum/stack_recipe("metal rod", /obj/item/stack/rods, 1, 2, 60),
new /datum/stack_recipe("table frame", /obj/structure/table, BUILD_AMT, time = 10, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("wall girders", /obj/structure/girder, BUILD_AMT, time = 50, one_per_turf = 1, on_floor = 1),
+ new /datum/stack_recipe("window frame", /obj/structure/window_frame, BUILD_AMT, time = 10, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("computer frame", /obj/structure/computerframe, BUILD_AMT, time = 25, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("machine blueprint", /obj/machinery/constructable_frame/machine_frame, 2, time = 25, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("light fixture frame", /obj/item/frame/light, 2),
diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm
index 1c7120fe5b2..92d79b011c0 100644
--- a/code/modules/mining/machine_processing.dm
+++ b/code/modules/mining/machine_processing.dm
@@ -81,7 +81,11 @@
interact(user)
/obj/machinery/mineral/processing_unit_console/proc/get_user_id(var/mob/user)
- if(!scanned_id && !isDrone(user))
+ if(isDrone(user))
+ var/mob/living/silicon/robot/drone/D = user
+ if(D.standard_drone)
+ return
+ if(!scanned_id)
var/obj/item/card/id/ID = user.GetIdCard()
if(ID)
scanned_id = WEAKREF(ID)
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index e4c1110ee25..6a1aaaa72f7 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -118,7 +118,11 @@ var/global/list/minevendor_list = list( //keep in order of price
interact(user)
/obj/machinery/mineral/equipment_vendor/proc/get_user_id(var/mob/user)
- if(!scanned_id && !isDrone(user))
+ if(isDrone(user))
+ var/mob/living/silicon/robot/drone/D = user
+ if(D.standard_drone)
+ return
+ if(!scanned_id)
var/obj/item/card/id/ID = user.GetIdCard()
if(ID)
scanned_id = WEAKREF(ID)
diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm
index 07ffa4c8887..151ee7f5ee7 100644
--- a/code/modules/mining/minebot.dm
+++ b/code/modules/mining/minebot.dm
@@ -14,6 +14,7 @@
speed = -1
hat_x_offset = 1
hat_y_offset = -12
+ standard_drone = FALSE
var/seeking_player = FALSE
var/health_upgrade
var/ranged_upgrade
@@ -23,10 +24,6 @@
. = ..()
verbs |= /mob/living/proc/hide
- remove_language(LANGUAGE_ROBOT)
- remove_language(LANGUAGE_DRONE)
- add_language(LANGUAGE_TCB, TRUE)
- add_language(LANGUAGE_EAL, TRUE)
//They are unable to be upgraded, so let's give them a bit of a better battery.
cell.maxcharge = 10000
diff --git a/code/modules/mob/abstract/new_player/new_player.dm b/code/modules/mob/abstract/new_player/new_player.dm
index 8726d8e631c..490388be7ab 100644
--- a/code/modules/mob/abstract/new_player/new_player.dm
+++ b/code/modules/mob/abstract/new_player/new_player.dm
@@ -279,6 +279,7 @@ INITIALIZE_IMMEDIATE(/mob/abstract/new_player)
character.buckled_to.set_dir(character.dir)
SSticker.mode.handle_latejoin(character)
+ universe.OnPlayerLatejoin(character)
if(SSjobs.ShouldCreateRecords(character.mind))
if(character.mind.assigned_role != "Cyborg")
SSrecords.generate_record(character)
diff --git a/code/modules/mob/abstract/new_player/sprite_accessories.dm b/code/modules/mob/abstract/new_player/sprite_accessories.dm
index 4cd19aad0e3..c096631debc 100644
--- a/code/modules/mob/abstract/new_player/sprite_accessories.dm
+++ b/code/modules/mob/abstract/new_player/sprite_accessories.dm
@@ -1733,7 +1733,8 @@ Follow by example and make good judgement based on length which list to include
/datum/sprite_accessory/hair_gradients
icon = 'icons/mob/hair_gradients.dmi'
species_allowed = list(/datum/species/human,/datum/species/human/offworlder,/datum/species/machine/shell,/datum/species/machine/shell/rogue,/datum/species/zombie,
- /datum/species/tajaran,/datum/species/tajaran/zhan_khazan,/datum/species/tajaran/m_sai,/datum/species/zombie/tajara)
+ /datum/species/tajaran,/datum/species/tajaran/zhan_khazan,/datum/species/tajaran/m_sai,/datum/species/zombie/tajara,
+ /datum/species/skrell, /datum/species/zombie/skrell, /datum/species/bug, /datum/species/bug/type_b)
none
name = "None"
@@ -2506,6 +2507,24 @@ Follow by example and make good judgement based on length which list to include
length = 3
chatname = "styled mane"
+ taj_ears_swoop
+ name = "Tajara Swoop"
+ icon_state = "hair_swoop"
+ length = 2
+ chatname = "styled mane"
+
+ taj_ears_swoop_long
+ name = "Tajara Swoop Long"
+ icon_state = "hair_longswoop"
+ length = 3
+ chatname = "styled mane"
+
+ taj_ears_swoop_long_alt
+ name = "Tajara Swoop Long Alt"
+ icon_state = "hair_longswoop_alt"
+ length = 3
+ chatname = "styled mane"
+
//msai hair, longer ears
msai_ears
icon = 'icons/mob/human_face/msai_hair.dmi'
@@ -2749,6 +2768,24 @@ Follow by example and make good judgement based on length which list to include
length = 3
chatname = "styled mane"
+ msai_ears_swoop
+ name = "M'sai Swoop"
+ icon_state = "msai_swoop"
+ length = 2
+ chatname = "styled mane"
+
+ msai_ears_swoop_long
+ name = "M'sai Swoop Long"
+ icon_state = "msai_longswoop"
+ length = 3
+ chatname = "styled mane"
+
+ msai_ears_swoop_long_alt
+ name = "M'sai Swoop Long Alt"
+ icon_state = "msai_longswoop_alt"
+ length = 3
+ chatname = "styled mane"
+
//vaurca antennae
vaurca_classic
icon = 'icons/mob/human_face/vaurca_hair.dmi'
@@ -3271,6 +3308,61 @@ Follow by example and make good judgement based on length which list to include
name = "Glorp"
icon_state = "glorp"
+ anglerfish
+ icon = 'icons/mob/human_face/skrell_beards.dmi'
+ name = "Anglerfish"
+ icon_state = "Anglerfish"
+ species_allowed = list(/datum/species/skrell)
+ gender = NEUTER
+
+ triton
+ name = "Triton"
+ icon_state = "Triton"
+
+ big_triton
+ name = "Big Triton"
+ icon_state = "Big_Triton"
+
+ ahab
+ name = "Ahab"
+ icon_state = "Ahab"
+
+ monotail
+ name = "Monotail"
+ icon_state = "Monotail"
+
+ long_monotail
+ name = "Long Monotail"
+ icon_state = "Long_Monotail"
+
+ face_adorns
+ name = "Face Adorns"
+ icon_state = "Face_Adorns"
+
+ sea_captain
+ name = "Sea Captain"
+ icon_state = "Sea_Captain"
+
+ fisherman
+ name = "Fisherman"
+ icon_state = "Fisherman"
+
+ sailor
+ name = "Sailor"
+ icon_state = "Sailor"
+
+ overgrown
+ name = "Overgrown"
+ icon_state = "Overgrown"
+
+ marauder_left
+ name = "Marauder (Left)"
+ icon_state = "Marauder_Left"
+
+ marauder_right
+ name = "Marauder (Right)"
+ icon_state = "Marauder_Right"
+
/*
////////////////////////////
/ =--------------------= /
@@ -3710,6 +3802,24 @@ Follow by example and make good judgement based on length which list to include
body_parts = list(BP_HEAD)
species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/skrell, /datum/species/bug, /datum/species/bug/type_b)
+ scar_chest
+ name = "Scar, Chest"
+ icon_state = "surgicalscar_chest"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi, /datum/species/bug, /datum/species/bug/type_b)
+
+ scar_chest_left
+ name = "Scar, Chest Left"
+ icon_state = "chestscar_l_chest"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi, /datum/species/bug, /datum/species/bug/type_b)
+
+ scar_chest_right
+ name = "Scar, Chest Right"
+ icon_state = "chestscar_r_chest"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi, /datum/species/bug, /datum/species/bug/type_b)
+
scratch_abdomen_left
name = "Scratch, Abdomen Left"
icon_state = "scratch_abdomen_l"
@@ -3782,6 +3892,18 @@ Follow by example and make good judgement based on length which list to include
body_parts= list(BP_R_LEG)
species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+ tat_circle_back
+ name = "Tattoo (Circle, Back)"
+ icon_state = "tat_circle_back"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+
+ tat_circle_big_back
+ name = "Tattoo (Big Circle, Back)"
+ icon_state = "tat_bigcircle_back"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+
tat_forrest_left
name = "Tattoo (Forrest, Left Eye)"
icon_state = "tat_forrest_l"
@@ -3794,6 +3916,12 @@ Follow by example and make good judgement based on length which list to include
body_parts = list(BP_HEAD)
species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+ tat_harness
+ name = "Tattoo (Harness, Chest)"
+ icon_state = "tat_harness_body"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+
tat_hive
name = "Tattoo (Hive, Back)"
icon_state = "tat_hive"
@@ -3890,6 +4018,12 @@ Follow by example and make good judgement based on length which list to include
body_parts = list(BP_R_LEG)
species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+ tat_spine_back
+ name = "Tattoo (Spine, Back)"
+ icon_state = "tat_spine_back"
+ body_parts = list(BP_CHEST)
+ species_allowed = list(/datum/species/human, /datum/species/human/offworlder, /datum/species/diona, /datum/species/machine/shell, /datum/species/skrell, /datum/species/tajaran, /datum/species/tajaran/zhan_khazan, /datum/species/tajaran/m_sai, /datum/species/unathi)
+
tat_tamoko
name = "Tattoo (Ta Moko, Face)"
icon_state = "tat_tamoko"
diff --git a/code/modules/mob/abstract/observer/observer.dm b/code/modules/mob/abstract/observer/observer.dm
index 1e1939c6878..74b2ec12eed 100644
--- a/code/modules/mob/abstract/observer/observer.dm
+++ b/code/modules/mob/abstract/observer/observer.dm
@@ -266,8 +266,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/abstract/observer/Stat()
..()
if(statpanel("Status"))
- if(emergency_shuttle)
- var/eta_status = emergency_shuttle.get_status_panel_eta()
+ if(evacuation_controller)
+ var/eta_status = evacuation_controller.get_status_panel_eta()
if(eta_status)
stat(null, eta_status)
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index d510386d1f9..d04a903132c 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -9,9 +9,13 @@
//Or someone snoring. So we make it where they won't hear it.
return
+ if((language && (language.flags & KNOWONLYHEAR)) && !say_understands(speaker, language))
+ return
+
//make sure the air can transmit speech - hearer's side
var/turf/T = get_turf(src)
- if ((T) && (!(isobserver(src)))) //Ghosts can hear even in vacuum.
+ var/vacuum_proof = ((language && (language.flags & PRESSUREPROOF)) || isobserver(src))
+ if(T && !vacuum_proof) //Ghosts can hear even in vacuum.
var/datum/gas_mixture/environment = T.return_air()
var/pressure = (environment)? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE && get_dist(speaker, src) > 1)
diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm
index e1b9b7893d0..7a033f70352 100644
--- a/code/modules/mob/language/language.dm
+++ b/code/modules/mob/language/language.dm
@@ -175,6 +175,9 @@
chosen_verb = speech_verb
return pick(chosen_verb)
+/datum/language/proc/handle_message_mode(var/message_mode)
+ return list(src, message_mode)
+
// Language handling.
/mob/proc/add_language(var/language)
var/datum/language/new_language
diff --git a/code/modules/mob/language/synthetic.dm b/code/modules/mob/language/synthetic.dm
index 8fea3954007..6b0c4dc1a69 100644
--- a/code/modules/mob/language/synthetic.dm
+++ b/code/modules/mob/language/synthetic.dm
@@ -68,4 +68,18 @@
return "1em"
/mob/living/silicon/robot/drone/construction/matriarch/get_binary_font_size()
- return "1.2em"
\ No newline at end of file
+ return "1.2em"
+
+/datum/language/local_drone
+ name = LANGUAGE_LOCAL_DRONE
+ desc = "A heavily encoded damage coordination transmission, only supplied to drones within sight."
+ speech_verb = list("transmits")
+ ask_verb = list("transmits")
+ exclaim_verb = list("transmits")
+ key = "do"
+ flags = RESTRICTED | KNOWONLYHEAR | PRESSUREPROOF | PASSLISTENOBJ
+
+/datum/language/local_drone/handle_message_mode(var/message_mode)
+ if(message_mode == "headset")
+ return list(all_languages[LANGUAGE_DRONE], null)
+ return list(src, null)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/diona_base.dm b/code/modules/mob/living/carbon/diona_base.dm
index 137e7f34f7e..0f6f3e7bd41 100644
--- a/code/modules/mob/living/carbon/diona_base.dm
+++ b/code/modules/mob/living/carbon/diona_base.dm
@@ -26,8 +26,6 @@ var/list/diona_banned_languages = list(
#define DIONA_LIGHT_COEFICIENT 0.25
/mob/living/carbon/proc/diona_handle_light(var/datum/dionastats/DS) //Carbon is the highest common denominator between gestalts and nymphs. They will share light code
- //if light_organ is non null, then we're working with a gestalt. otherwise nymph
-
var/light_amount = DS.last_lightlevel //If we're not re-fetching the light level then we'll use a recent cached version
if (life_tick % 2 == 0) //Only fetch the lightlevel every other proc to save performance
@@ -82,19 +80,12 @@ var/list/diona_banned_languages = list(
if (!pressure)
return 0
- if (DS.nutrient_organ)
- if (DS.nutrient_organ.is_broken())
- return 0
-
if (consume_nutrition_from_air && (nutrition / max_nutrition > 0.25))
to_chat(src, SPAN_NOTICE("You feel like you have replanished enough of nutrition to stay alive. Consuming more makes you feel gross."))
consume_nutrition_from_air = !consume_nutrition_from_air
return
var/plus= (min(pressure,diona_max_pressure) / diona_max_pressure)* diona_nutrition_factor
- if (DS.nutrient_organ)
- if(DS.nutrient_organ.is_bruised())
- plus *= 0.5
plus = min(plus, max_nutrition - nutrition)
adjustNutritionLoss(-plus)
@@ -356,7 +347,6 @@ var/list/diona_banned_languages = list(
to_chat(src, "You feel a shifting sensation inside you as your nymphs move apart to make space, forming a new [O.name].")
regenerate_icons()
DS.LMS = max(2, DS.LMS) //Prevents a message about darkness in light areas
- update_dionastats() //Re-find the organs in case they were lost or regained
updatehealth()
return
@@ -417,7 +407,6 @@ var/list/diona_banned_languages = list(
DS.LMS = min(2, DS.LMS) //Prevents a message about darkness in light areas
DS.regening_organ = FALSE
- update_dionastats() //Re-find the organs in case they were lost or regained
updatehealth()
DS.regen_limb_progress = 0
playsound(src, 'sound/species/diona/gestalt_grow.ogg', 30, 1)
@@ -481,13 +470,7 @@ var/list/diona_banned_languages = list(
if (is_ventcrawling)
return -1.5 //no light inside pipes
-
- if (DS.light_organ)
- if (DS.light_organ.is_broken())
- light_factor *= 0.55
- else if (DS.light_organ.is_bruised())
- light_factor *= 0.8
- else if (DS.dionatype == 2)
+ if (DS.dionatype == 2)
light_factor = 1
if (T)
@@ -615,8 +598,6 @@ var/list/diona_banned_languages = list(
var/list/sampled_DNA = list()
var/list/language_progress = list()
- var/obj/item/organ/internal/diona/node/light_organ = null //The organ this gestalt uses to receive light. This is left null for nymphs
- var/obj/item/organ/internal/diona/nutrients/nutrient_organ = null //Organ
var/LMS = 1 //Lightmessage state. Switching between states gives the user a message
var/dionatype //1 = nymph, 2 = worker gestalt
var/datum/weakref/nym
@@ -692,11 +673,6 @@ var/list/diona_banned_languages = list(
to_chat(user, SPAN_NOTICE("You have mastered the [i] language!"))
language_progress.Remove(i)
-/datum/dionastats/Destroy()
- light_organ = null //Nulling out these references to prevent GC errors
- nutrient_organ = null
- return ..()
-
#undef TEMP_REGEN_STOP
#undef TEMP_REGEN_NORMAL
#undef TEMP_INCREASE_REGEN_DOUBLE
diff --git a/code/modules/mob/living/carbon/human/diona_gestalt.dm b/code/modules/mob/living/carbon/human/diona_gestalt.dm
index 72c5181ddf5..01bd7da8a7f 100644
--- a/code/modules/mob/living/carbon/human/diona_gestalt.dm
+++ b/code/modules/mob/living/carbon/human/diona_gestalt.dm
@@ -115,9 +115,6 @@
set category = "Abilities"
set name = "Check light level"
- if (!DS.light_organ || DS.light_organ.is_broken() || DS.light_organ.is_bruised())
- to_chat(usr, SPAN_DANGER("Our response node is damaged or missing, without it we can't tell light from darkness. We can only hope this area is bright enough to let us regenerate it!"))
- return
var/light = get_lightlevel_diona(DS)
if (light <= -0.75)
to_chat(usr, SPAN_DANGER("It is pitch black here! This is extremely dangerous, we must find light, or death will soon follow!"))
@@ -151,24 +148,6 @@
DS.trauma_factor = (DS.max_health / dark_survival) / MLS
DS.dionatype = 2//Gestalt
- for (var/organ in internal_organs)
- if (istype(organ, /obj/item/organ/internal/diona/node))
- DS.light_organ = organ
- if (istype(organ, /obj/item/organ/internal/diona/nutrients))
- DS.nutrient_organ = organ
-
-//This proc can be called if some dionastats information needs to be refreshed or re-found
-//Currently only used for refreshing organs
-/mob/living/carbon/human/proc/update_dionastats()
- DS.light_organ = null
- DS.nutrient_organ = null
-
- for (var/organ in internal_organs)
- if (istype(organ, /obj/item/organ/internal/diona/node))
- DS.light_organ = organ
- if (istype(organ, /obj/item/organ/internal/diona/nutrients))
- DS.nutrient_organ = organ
-
//Splitting functions
//====================
@@ -280,7 +259,6 @@
M.languages = languages.Copy()
M.accent = accent
- update_dionastats() //Re-find the organs in case they were lost or regained
nutrition -= REGROW_FOOD_REQ
DS.stored_energy -= REGROW_ENERGY_REQ
diona_handle_regeneration(DS)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index e235bc4681c..f17188c90c1 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -185,8 +185,8 @@
if(statpanel("Status"))
stat("Intent:", "[a_intent]")
stat("Move Mode:", "[m_intent]")
- if(emergency_shuttle)
- var/eta_status = emergency_shuttle.get_status_panel_eta()
+ if(evacuation_controller)
+ var/eta_status = evacuation_controller.get_status_panel_eta()
if(eta_status)
stat(null, eta_status)
if(is_diona() && DS)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index e9fd12a588e..c155e31aa1f 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -200,6 +200,11 @@ proc/get_radio_key_from_channel(var/channel)
else
speaking = get_default_language()
+ if(speaking)
+ var/list/speech_mod = speaking.handle_message_mode(message_mode)
+ speaking = speech_mod[1]
+ message_mode = speech_mod[2]
+
var/is_singing = FALSE
if(length(message) >= 1 && copytext(message, 1, 2) == "%")
message = copytext(message, 2)
@@ -280,15 +285,16 @@ proc/get_radio_key_from_channel(var/channel)
var/turf/T = get_turf(src)
if(T)
- //make sure the air can transmit speech - speaker's side
- var/datum/gas_mixture/environment = T.return_air()
- var/pressure = (environment)? environment.return_pressure() : 0
- if(pressure < SOUND_MINIMUM_PRESSURE)
- message_range = 1
+ if(!speaking || !(speaking.flags & PRESSUREPROOF))
+ //make sure the air can transmit speech - speaker's side
+ var/datum/gas_mixture/environment = T.return_air()
+ var/pressure = (environment)? environment.return_pressure() : 0
+ if(pressure < SOUND_MINIMUM_PRESSURE)
+ message_range = 1
- if (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
- italics = 1
- sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
+ if (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
+ italics = 1
+ sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
get_mobs_and_objs_in_view_fast(T, message_range, listening, listening_obj, ghost_hearing)
@@ -305,9 +311,11 @@ proc/get_radio_key_from_channel(var/channel)
INVOKE_ASYNC(GLOBAL_PROC, /proc/animate_speechbubble, speech_bubble, hear_clients, 30)
do_animate_chat(message, speaking, italics, hear_clients, 30)
- for(var/obj/O as anything in listening_obj)
- if(O) //It's possible that it could be deleted in the meantime.
- INVOKE_ASYNC(O, /obj/.proc/hear_talk, src, message, verb, speaking)
+ var/bypass_listen_obj = (speaking && (speaking.flags & PASSLISTENOBJ))
+ if(!bypass_listen_obj)
+ for(var/obj/O as anything in listening_obj)
+ if(O) //It's possible that it could be deleted in the meantime.
+ INVOKE_ASYNC(O, /obj/.proc/hear_talk, src, message, verb, speaking)
if(mind)
mind.last_words = message
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index a17aed11ead..042b541d337 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -429,12 +429,12 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/ai_call_shuttle()
set category = "AI Commands"
- set name = "Call Emergency Shuttle"
+ set name = "Call Evacuation"
if(check_unable(AI_CHECK_WIRELESS))
return
- var/confirm = alert("Are you sure you want to call the shuttle?", "Confirm Shuttle Call", "Yes", "No")
+ var/confirm = alert("Are you sure you want to call the evacuation?", "Confirm Evacuation", "Yes", "No")
if(check_unable(AI_CHECK_WIRELESS))
return
@@ -442,17 +442,16 @@ var/list/ai_verbs_default = list(
if(confirm == "Yes")
call_shuttle_proc(src)
- if(emergency_shuttle.online())
- post_display_status("shuttle")
+ post_display_status("shuttle")
/mob/living/silicon/ai/proc/ai_recall_shuttle()
set category = "AI Commands"
- set name = "Recall Emergency Shuttle"
+ set name = "Cancel Evacuation"
if(check_unable(AI_CHECK_WIRELESS))
return
- var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
+ var/confirm = alert("Are you sure you want to cancel the evacuation?", "Confirm Cancel", "Yes", "No")
if(check_unable(AI_CHECK_WIRELESS))
return
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 40b71f019c1..89feb7d8c72 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -36,7 +36,6 @@
var/module_type = /obj/item/robot_module/drone
cell_emp_mult = 1
integrated_light_power = 3
- local_transmit = TRUE
// Interaction
universal_speak = FALSE
@@ -56,6 +55,8 @@
mob_bump_flag = SIMPLE_ANIMAL
holder_type = /obj/item/holder/drone
+ can_speak_basic = FALSE
+
// ID and Access
law_update = FALSE
req_access = list(access_engine, access_robotics)
@@ -76,6 +77,11 @@
var/can_swipe = TRUE
var/rebooting = FALSE
+ var/standard_drone = TRUE
+
+/mob/living/silicon/robot/drone/Initialize()
+ . = ..()
+ set_default_language(all_languages[LANGUAGE_LOCAL_DRONE])
/mob/living/silicon/robot/drone/can_be_possessed_by(var/mob/abstract/observer/possessor)
if(!istype(possessor) || !possessor.client || !possessor.ckey)
@@ -115,6 +121,11 @@
QDEL_NULL(hat_overlay)
return ..()
+/mob/living/silicon/robot/drone/get_default_language()
+ if(default_language)
+ return default_language
+ return all_languages[LANGUAGE_LOCAL_DRONE]
+
/mob/living/silicon/robot/drone/construction
// Look and feel
name = "construction drone"
@@ -136,6 +147,8 @@
hat_x_offset = 1
hat_y_offset = -12
+ standard_drone = FALSE
+
var/my_home_z
/mob/living/silicon/robot/drone/construction/Initialize()
@@ -166,6 +179,7 @@
/mob/living/silicon/robot/drone/construction/matriarch
name = "matriarch drone"
desc_flavor = "It's a small matriarch drone. The casing is stamped with an corporate logo and the subscript: '%MAPNAME% Recursive Repair Systems: Heart Of The Swarm!'
OOC Info:
Matriarch drones are player-controlled synthetics which are lawed to maintain the station and not interact with anyone else, except for other drones. They are in command of all the smaller maintenance drones.
They hold a wide array of tools to build, repair, maintain, and clean. They function similarly to other synthetics, in that they require recharging regularly, have laws, and are resilient to many hazards, such as fire, radiation, vacuum, and more.
Ghosts can join the round as a matriarch drone by having a Command whitelist and accessing the 'Ghost Spawner' menu in the 'Ghost' tab.
An antagonist can use an Electromagnetic Sequencer to corrupt their laws and make them follow their orders."
+ module_type = /obj/item/robot_module/drone/construction/matriarch
law_type = /datum/ai_laws/matriarch_drone
can_swipe = FALSE
@@ -339,6 +353,7 @@
hacked = FALSE
law_update = FALSE
connected_ai = null
+ standard_drone = FALSE
clear_supplied_laws()
clear_inherent_laws()
laws = new /datum/ai_laws/syndicate_override
@@ -535,4 +550,4 @@
for(var/mob/living/silicon/robot/drone/D in mob_list)
if(D.key && D.client)
drones++
- return drones >= config.max_maint_drones
\ No newline at end of file
+ return drones >= config.max_maint_drones
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm
deleted file mode 100644
index f844be55394..00000000000
--- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/mob/living/silicon/robot/drone/say(message, datum/language/speaking)
- if(hacked)
- return ..(message)
-
- if(local_transmit)
- if(stat == DEAD)
- return say_dead(message)
-
- if(copytext(message, 1, 2) == "*")
- return emote(copytext(message,2))
-
- message = formalize_text(message)
-
- if(copytext(message, 1, 2) == ";")
- var/datum/language/L = all_languages["Drone Talk"]
- if(istype(L))
- return L.broadcast(src,trim(copytext(message, 2)))
-
- //Must be concious to speak
- if(stat)
- return FALSE
-
- // Message must not be empty
- if(!length(message))
- return FALSE
-
- var/list/listeners = hearers(5, src)
- listeners |= src
-
- var/list/hear_clients = list()
- for(var/mob/living/silicon/D in listeners)
- if(D.client && D.local_transmit)
- to_chat(D, "[real_name] transmits, \"[message]\"")
- hear_clients += D.client
-
- for(var/mob/M in player_list)
- if(isnull(M.client))
- continue
- if(istype(M, /mob/abstract/new_player))
- continue
- else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS)
- to_chat(M, "[real_name] transmits, \"[message]\"")
- hear_clients += M.client
-
- do_animate_chat(message, speaking, FALSE, hear_clients, 30)
-
- return TRUE
- return ..(message, 0)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 2b6c1d31908..1e17f43157a 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -59,6 +59,7 @@ var/global/list/robot_modules = list(
modules += new /obj/item/inductive_charger(src)
add_camera_networks(R)
+ handle_languages(R)
add_languages(R)
add_subsystems(R)
apply_status_flags(R)
@@ -71,6 +72,9 @@ var/global/list/robot_modules = list(
R.choose_icon()
R.setup_icon_cache()
+/obj/item/robot_module/proc/handle_languages(var/mob/living/silicon/robot/R)
+ return
+
/obj/item/robot_module/proc/Reset(var/mob/living/silicon/robot/R)
remove_camera_networks(R)
remove_languages(R)
@@ -971,6 +975,19 @@ var/global/list/robot_modules = list(
modules += new /obj/item/inflatable_dispenser(src)
modules += new /obj/item/rfd/piping/borg(src) // putting this here so it's next to the RFD-C on construction drones
+/obj/item/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R, var/amount)
+ var/obj/item/device/lightreplacer/LR = locate() in src.modules
+ LR.Charge(R, amount)
+ return ..()
+
+/obj/item/robot_module/drone/handle_languages(var/mob/living/silicon/robot/R)
+ R.languages = list()
+ R.speech_synthesizer_langs = list()
+ for(var/language in languages)
+ languages[language] = FALSE
+ languages[LANGUAGE_DRONE] = TRUE
+ languages[LANGUAGE_LOCAL_DRONE] = TRUE
+
/obj/item/robot_module/drone/construction
name = "construction drone module"
channels = list(CHANNEL_ENGINEERING = TRUE)
@@ -980,11 +997,8 @@ var/global/list/robot_modules = list(
modules += new /obj/item/rfd/construction/borg(src)
modules += new /obj/item/pickaxe/drill(src)
-/obj/item/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R, var/amount)
- var/obj/item/device/lightreplacer/LR = locate() in modules
- LR.Charge(R, amount)
- ..()
- return
+/obj/item/robot_module/drone/construction/matriarch
+ name = "matriarch drone module"
/obj/item/robot_module/mining_drone
name = "mining drone module"
@@ -995,6 +1009,14 @@ var/global/list/robot_modules = list(
. = ..()
set_up_default(R)
+/obj/item/robot_module/mining_drone/handle_languages(var/mob/living/silicon/robot/R)
+ R.languages = list()
+ R.speech_synthesizer_langs = list()
+ for(var/language in languages)
+ languages[language] = FALSE
+ languages[LANGUAGE_DRONE] = TRUE
+ languages[LANGUAGE_LOCAL_DRONE] = TRUE
+
/obj/item/robot_module/mining_drone/proc/set_up_default(var/mob/living/silicon/robot/R, var/has_drill = TRUE)
modules += new /obj/item/device/flash(src)
if(has_drill)
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index e4420f23fc8..76778c89380 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -33,6 +33,9 @@
message_mode = null
return common_radio.talk_into(src, message, message_mode, verb, speaking)
+/mob/living/silicon/robot/drone/handle_message_mode()
+ return null
+
/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
..()
if(message_mode == "department")
@@ -60,6 +63,13 @@
return speak_exclamation
return speak_statement
+/mob/living/silicon/robot/drone/say_quote(var/message, var/datum/language/speaking = null, var/singing = FALSE)
+ if(speaking)
+ var/ending = copytext(message, length(message))
+ var/pre_ending = copytext(message, length(message) - 1, length(message))
+ return speaking.get_spoken_verb(ending, pre_ending, singing)
+ return ..()
+
#define IS_AI 1
#define IS_ROBOT 2
#define IS_PAI 3
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index e6218654002..26a044b7104 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -11,7 +11,6 @@
var/speak_statement = "states"
var/speak_exclamation = "declares"
var/speak_query = "queries"
- var/local_transmit //If set, can only speak to others of the same type within a short range.
// Description
var/pose //Yes, now AIs can pose too.
@@ -62,10 +61,12 @@
uv_intensity = 175 //Lights cast by robots have reduced effect on diona
mob_thinks = FALSE
+ var/can_speak_basic = TRUE
+
/mob/living/silicon/Initialize()
silicon_mob_list |= src
. = ..()
- add_language(LANGUAGE_TCB)
+ add_language(LANGUAGE_TCB, can_speak_basic)
init_id()
var/datum/language/L = locate(/datum/language/common) in languages
@@ -176,8 +177,8 @@
// this function displays the shuttles ETA in the status panel if the shuttle has been called
/mob/living/silicon/proc/show_emergency_shuttle_eta()
- if(emergency_shuttle)
- var/eta_status = emergency_shuttle.get_status_panel_eta()
+ if(evacuation_controller)
+ var/eta_status = evacuation_controller.get_status_panel_eta()
if(eta_status)
stat(null, eta_status)
diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm
index 8219b63f997..e2f8ffba313 100644
--- a/code/modules/mob/living/simple_animal/borer/borer.dm
+++ b/code/modules/mob/living/simple_animal/borer/borer.dm
@@ -102,8 +102,8 @@
..()
statpanel("Status")
- if(emergency_shuttle)
- var/eta_status = emergency_shuttle.get_status_panel_eta()
+ if(evacuation_controller)
+ var/eta_status = evacuation_controller.get_status_panel_eta()
if(eta_status)
stat(null, eta_status)
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 1b329374395..69264737d4e 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -353,7 +353,7 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH
/mob/living/simple_animal/hostile/proc/check_horde()
- if(emergency_shuttle.shuttle.location)
+ if(evacuation_controller.is_prepared())
if(!enroute && !target_mob) //The shuttle docked, all monsters rush for the escape hallway
if(!shuttletarget && escape_list.len) //Make sure we didn't already assign it a target, and that there are targets to pick
shuttletarget = pick(escape_list) //Pick a shuttle target
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 b9dab79d78b..1a5272a3da4 100644
--- a/code/modules/modular_computers/file_system/programs/command/comm.dm
+++ b/code/modules/modular_computers/file_system/programs/command/comm.dm
@@ -92,14 +92,18 @@
if(current_viewing_message)
data["message_current"] = current_viewing_message
- if(emergency_shuttle.location())
- data["have_shuttle"] = TRUE
- if(emergency_shuttle.online())
- data["have_shuttle_called"] = TRUE
- else
- data["have_shuttle_called"] = FALSE
- else
- data["have_shuttle"] = FALSE
+ var/list/processed_evac_options = list()
+ if(!isnull(evacuation_controller))
+ for (var/datum/evacuation_option/EO in evacuation_controller.available_evac_options())
+ if(EO.abandon_ship)
+ continue
+ var/list/option = list()
+ option["option_text"] = EO.option_text
+ option["option_target"] = EO.option_target
+ option["needs_syscontrol"] = EO.needs_syscontrol
+ option["silicon_allowed"] = EO.silicon_allowed
+ processed_evac_options[++processed_evac_options.len] = option
+ data["evac_options"] = processed_evac_options
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
@@ -206,16 +210,18 @@
log_say("[key_name(usr)] has sent a message to [current_map.boss_short]: [input]", ckey = key_name(usr))
centcomm_message_cooldown = TRUE
addtimer(CALLBACK(src, .proc/set_centcomm_message_cooldown, FALSE), 300) // thirty second cooldown
- if("shuttle")
- if(is_authenticated(user) && ntn_cont && can_call_shuttle())
- if(href_list["target"] == "call")
- var/confirm = alert("Are you sure you want to call the shuttle?", name, "No", "Yes")
- if(confirm == "Yes" && can_still_topic())
- call_shuttle_proc(usr)
- if(href_list["target"] == "cancel" && !issilicon(usr))
- var/confirm = alert("Are you sure you want to cancel the shuttle?", name, "No", "Yes")
- if(confirm == "Yes" && can_still_topic())
- cancel_call_proc(usr)
+ if("evac")
+ if(is_authenticated(user))
+ var/datum/evacuation_option/selected_evac_option = evacuation_controller.evacuation_options[href_list["target"]]
+ if (isnull(selected_evac_option) || !istype(selected_evac_option))
+ return
+ if (!selected_evac_option.silicon_allowed && issilicon(user))
+ return
+ if (selected_evac_option.needs_syscontrol && !ntn_cont)
+ return
+ var/confirm = alert("Are you sure you want to [selected_evac_option.option_desc]?", name, "No", "Yes")
+ if (confirm == "Yes" && can_still_topic())
+ evacuation_controller.handle_evac_option(selected_evac_option.option_target, user)
if("setstatus")
if(is_authenticated(user) && ntn_cont)
switch(href_list["target"])
@@ -358,15 +364,13 @@ Command action procs
//Returns 1 if recalled 0 if not
/proc/cancel_call_proc(var/mob/user)
- if(!(ROUND_IS_STARTED) || !emergency_shuttle.can_recall())
+ if(!(ROUND_IS_STARTED) || !evacuation_controller)
return FALSE
- if((SSticker.mode.name == "blob")||(SSticker.mode.name == "Meteor"))
+ if(SSticker.mode.name == "Meteor")
return FALSE
- if(!emergency_shuttle.going_to_centcom()) //check that shuttle isn't already heading to centcomm
- emergency_shuttle.recall()
- log_game("[key_name(user)] has recalled the shuttle.", key_name(user))
- message_admins("[key_name_admin(user)] has recalled the shuttle.", 1)
+ if(evacuation_controller.cancel_evacuation())
+ log_and_message_admins("has cancelled the evacuation.", user)
return TRUE
return FALSE
@@ -379,77 +383,46 @@ Command action procs
//Returns 1 if called 0 if not
/proc/call_shuttle_proc(var/mob/user)
- if((!(ROUND_IS_STARTED) || !emergency_shuttle.location()))
+ if((!(ROUND_IS_STARTED) || !evacuation_controller))
return FALSE
if(!universe.OnShuttleCall(usr))
- to_chat(user, SPAN_WARNING("Cannot establish a bluespace connection."))
+ to_chat(user, SPAN_WARNING("A bluespace connection cannot be established! Please check the user manual for more information."))
return FALSE
- if(emergency_shuttle.deny_shuttle)
- to_chat(user, SPAN_WARNING("The emergency shuttle cannot be sent at this time. Please try again later."))
+ if(evacuation_controller.deny)
+ to_chat(user, SPAN_WARNING("An evacuation cannot be sent at this time. Please try again later."))
return FALSE
if(world.time < config.time_to_call_emergency_shuttle)
- to_chat(user, SPAN_WARNING("The emergency shuttle is refueling. Please wait another [round((config.time_to_call_emergency_shuttle-world.time)/600)] minute\s before trying again."))
+ to_chat(user, SPAN_WARNING("An evacuation cannot be sent at this time. Please wait another [round((config.time_to_call_emergency_shuttle-world.time)/600)] minute\s before trying again."))
return FALSE
- if(emergency_shuttle.going_to_centcom())
- to_chat(user, SPAN_WARNING("The emergency shuttle cannot be called while returning to [current_map.boss_short]."))
- return FALSE
+ if(evacuation_controller.is_on_cooldown()) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE
+ to_chat(user, evacuation_controller.get_cooldown_message())
- if(emergency_shuttle.online())
- to_chat(user, SPAN_WARNING("The emergency shuttle is already on its way."))
- return FALSE
+ if(evacuation_controller.is_evacuating())
+ to_chat(user, "An evacuation is already underway.")
+ return
- if(SSticker.mode.name == "blob")
- to_chat(user, SPAN_WARNING("Under directive 7-10, [station_name()] is quarantined until further notice."))
- return FALSE
-
- emergency_shuttle.call_evac()
- log_game("[key_name(user)] has called the shuttle.",ckey=key_name(user))
- message_admins("[key_name_admin(user)] has called the shuttle.", 1)
+ if(evacuation_controller.call_evacuation(user))
+ log_and_message_admins("[user? key_name(user) : "Autotransfer"] has called the shuttle.")
return TRUE
/proc/init_shift_change(var/mob/user, var/force = FALSE)
- if ((!(ROUND_IS_STARTED) || !emergency_shuttle.location()))
+ if ((!(ROUND_IS_STARTED) || !evacuation_controller))
return
- if(emergency_shuttle.going_to_centcom())
- to_chat(user, SPAN_WARNING("The shuttle cannot be called while returning to [current_map.boss_short]."))
+ if (!evacuation_controller)
return
- if(emergency_shuttle.online())
- to_chat(user, SPAN_WARNING("The shuttle is already on its way."))
- return
-
- // if force is 0, some things may stop the shuttle call
- if(!force)
- if(emergency_shuttle.deny_shuttle)
- to_chat(user, SPAN_WARNING("[current_map.boss_short] does not currently have a shuttle available in your sector. Please try again later."))
- return
-
- if(world.time < 54000) // 30 minute grace period to let the game get going
- to_chat(user, SPAN_WARNING("The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again."))
- return
-
- if(SSticker.mode.auto_recall_shuttle)
- //New version pretends to call the shuttle but cause the shuttle to return after a random duration.
- emergency_shuttle.auto_recall = TRUE
-
- if(SSticker.mode.name == "blob" || SSticker.mode.name == "epidemic")
- to_chat(user, SPAN_WARNING("Under directive 7-10, [station_name()] is quarantined until further notice."))
- return
-
- emergency_shuttle.call_transfer()
+ . = evacuation_controller.call_evacuation(null, _emergency_evac = FALSE, autotransfer = TRUE)
//delay events in case of an autotransfer
- if(!user)
+ if(.)
SSevents.delay_events(EVENT_LEVEL_MODERATE, 10200) //17 minutes
SSevents.delay_events(EVENT_LEVEL_MAJOR, 10200)
log_game("[user? key_name(user) : "Autotransfer"] has called the shuttle.")
- message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1)
-
- return
+ message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1)
\ No newline at end of file
diff --git a/code/modules/organs/internal/liver.dm b/code/modules/organs/internal/liver.dm
index 64710ba32e0..05e08df4c62 100644
--- a/code/modules/organs/internal/liver.dm
+++ b/code/modules/organs/internal/liver.dm
@@ -45,7 +45,8 @@
filter_effect += 1
if(filter_effect < 2) //Trouble. You're not filtering well.
- owner.adjustToxLoss(0.5 * max(2 - filter_effect, 0))
+ if(owner.intoxication > 0)
+ owner.adjustToxLoss(0.5 * max(2 - filter_effect, 0))
// Heal a bit if needed and we're not busy. This allows recovery from low amounts of toxloss.
if(!owner.total_radiation && damage > 0)
diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm
index 828824ebc71..9a9d9282216 100644
--- a/code/modules/organs/internal/lungs.dm
+++ b/code/modules/organs/internal/lungs.dm
@@ -266,11 +266,6 @@
owner.failed_last_breath = 1
else
owner.failed_last_breath = 0
- if(owner.disabilities & ASTHMA)
- owner.adjustOxyLoss(rand(-5,0))
- else
- owner.adjustOxyLoss(-5)
-
// Hot air hurts :(
handle_temperature_effects(breath)
@@ -284,6 +279,10 @@
else
last_successful_breath = world.time
owner.oxygen_alert = 0
+ if(owner.disabilities & ASTHMA)
+ owner.adjustOxyLoss(rand(-5,0) * inhale_efficiency)
+ else
+ owner.adjustOxyLoss(-5 * inhale_efficiency)
if(!BP_IS_ROBOTIC(src) && species.breathing_sound && is_below_sound_pressure(get_turf(owner)))
if(breathing || owner.shock_stage >= 10)
sound_to(owner, sound(species.breathing_sound,0,0,0,5))
diff --git a/code/modules/organs/subtypes/augment.dm b/code/modules/organs/subtypes/augment.dm
index 3a92d8b13a4..84c3fed0699 100644
--- a/code/modules/organs/subtypes/augment.dm
+++ b/code/modules/organs/subtypes/augment.dm
@@ -82,8 +82,8 @@
return FALSE
to_chat(owner, SPAN_NOTICE("Hello [user], it is currently: '[worldtime2text()]'. Today's date is '[time2text(world.time, "Month DD")]. [game_year]'. Have a lovely day."))
- if (emergency_shuttle.get_status_panel_eta())
- to_chat(owner, SPAN_WARNING("Notice: You have one (1) scheduled flight, ETA: [emergency_shuttle.get_status_panel_eta()]."))
+ if (evacuation_controller.get_status_panel_eta())
+ to_chat(owner, SPAN_WARNING("Notice: You have one (1) scheduled flight, ETA: [evacuation_controller.get_status_panel_eta()]."))
/obj/item/organ/internal/augment/tool
name = "retractable widget"
@@ -218,21 +218,21 @@
tesla_zap(owner, 7, 1500)
/obj/item/organ/internal/augment/eye_sensors
- name = "integrated eye sensors"
+ name = "integrated HUD sensors"
icon_state = "augment_eyes"
cooldown = 25
activable = TRUE
organ_tag = BP_AUG_EYE_SENSORS
parent_organ = BP_HEAD
- action_button_name = "Toggle Eye Sensors"
+ action_button_name = "Toggle Security Sensors"
var/active_hud = "disabled"
var/static/list/hud_types = list(
- "Disabled",
+ "disabled",
SEC_HUDTYPE,
MED_HUDTYPE)
- var/selected_hud = "Disabled"
+ var/selected_hud = "disabled"
/obj/item/organ/internal/augment/eye_sensors/attack_self(var/mob/user)
. = ..()
@@ -240,37 +240,12 @@
if(!.)
return FALSE
- var/choice = input("Select the Sensor Type.", "Bionic Eyes Sensors") as null|anything in capitalize_list(hud_types)
-
- selected_hud = lowertext(choice)
-
/obj/item/organ/internal/augment/eye_sensors/process()
..()
if(!owner)
return
- switch(selected_hud)
-
- if(SEC_HUDTYPE)
- req_access = list(access_security)
- if(allowed(owner))
- active_hud = "security"
- process_sec_hud(owner, 1)
- else
- active_hud = "disabled"
-
- if(MED_HUDTYPE)
- req_access = list(access_medical)
- if(allowed(owner))
- active_hud = "medical"
- process_med_hud(owner, 1)
- else
- active_hud = "disabled"
-
- else
- active_hud = "disabled"
-
/obj/item/organ/internal/augment/eye_sensors/emp_act(severity)
..()
var/obj/item/organ/internal/eyes/E = owner.get_eyes()
@@ -281,6 +256,63 @@
/obj/item/organ/internal/augment/eye_sensors/proc/check_hud(var/hud)
return (hud == active_hud)
+/obj/item/organ/internal/augment/eye_sensors/security
+ name = "integrated security HUD sensors"
+ action_button_name = "Toggle Security Sensors"
+
+/obj/item/organ/internal/augment/eye_sensors/security/attack_self(var/mob/user)
+ . = ..()
+
+ if(selected_hud == "disabled")
+ selected_hud = SEC_HUDTYPE
+ to_chat(user, "You activate \the [src].")
+ else
+ selected_hud = "disabled"
+ to_chat(user, "You deactivate \the [src].")
+
+/obj/item/organ/internal/augment/eye_sensors/security/process()
+ ..()
+
+ switch(selected_hud)
+
+ if(SEC_HUDTYPE)
+ req_access = list(access_security)
+ if(allowed(owner))
+ active_hud = "security"
+ process_sec_hud(owner, 1)
+ else
+ active_hud = "disabled"
+ else
+ active_hud = "disabled"
+/obj/item/organ/internal/augment/eye_sensors/medical
+ name = "integrated medical HUD sensors"
+ action_button_name = "Toggle Medical Sensors"
+
+/obj/item/organ/internal/augment/eye_sensors/medical/attack_self(var/mob/user)
+ . = ..()
+
+ if(selected_hud == "disabled")
+ selected_hud = MED_HUDTYPE
+ to_chat(user, "You activate \the [src].")
+ else
+ selected_hud = "disabled"
+ to_chat(user, "You deactivate \the [src].")
+
+/obj/item/organ/internal/augment/eye_sensors/medical/process()
+ ..()
+
+ switch(selected_hud)
+
+ if(MED_HUDTYPE)
+ req_access = list(access_medical)
+ if(allowed(owner))
+ active_hud = "medical"
+ process_med_hud(owner, 1)
+ else
+ active_hud = "disabled"
+ else
+ active_hud = "disabled"
+
/obj/item/organ/internal/augment/cyber_hair
name = "synthetic hair extensions"
cooldown = 20
diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm
index 71189775ee7..16537520d96 100644
--- a/code/modules/paperwork/folders.dm
+++ b/code/modules/paperwork/folders.dm
@@ -80,10 +80,14 @@
usr.put_in_hands(P)
handle_post_remove()
else if(href_list["write"])
- var/obj/item/paper/P = locate(href_list["write"])
- var/obj/item/I = usr.get_inactive_hand()
- if(P && I && (P.loc == src) && istype(P) && I.ispen())
- P.attackby(I, usr)
+ var/obj/item/paper/paper = locate(href_list["write"])
+ if(!istype(paper) || paper.loc != src)
+ return
+ var/obj/item/pen = usr.get_inactive_hand()
+ if(!pen || !pen.ispen())
+ pen = usr.get_active_hand()
+ if(pen?.ispen())
+ paper.attackby(pen, usr)
else if(href_list["read"])
var/obj/item/paper/P = locate(href_list["read"])
if(P && (P.loc == src) && istype(P))
@@ -141,7 +145,7 @@
can_write = TRUE
/obj/item/folder/embedded/loc_check(var/atom/A)
- if(loc.loc == A)
+ if(loc.loc.Adjacent(A))
return TRUE
return FALSE
diff --git a/code/modules/paperwork/journal.dm b/code/modules/paperwork/journal.dm
index e0e5945f0fe..f282cc552ce 100644
--- a/code/modules/paperwork/journal.dm
+++ b/code/modules/paperwork/journal.dm
@@ -36,8 +36,8 @@
if(closed_desc)
desc = open ? initial(desc) + closed_desc : initial(desc)
-/obj/item/journal/CtrlClick(mob/user)
- if(loc == user)
+/obj/item/journal/AltClick(mob/user)
+ if(Adjacent(user))
open = !open
to_chat(user, SPAN_NOTICE("You [open ? "open" : "close"] \the [src]."))
update_icon()
@@ -74,9 +74,16 @@
if(!E)
return
user.drop_from_inventory(I, E)
- to_chat(user, SPAN_NOTICE("You put \the [I] into \the [E]."))
+ to_chat(user, SPAN_NOTICE("You put \the [I] into \the [E] index in \the [src]."))
update_icon()
return
+ if(I.ispen())
+ if(!open)
+ to_chat(user, SPAN_NOTICE("You open \the [src] with \the [I]."))
+ open = !open
+ update_icon()
+ return
+ attack_self(user)
/obj/item/journal/proc/generate_index(var/mob/user)
var/obj/item/folder/embedded/E = new /obj/item/folder/embedded(src)
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 067ec5b9c1f..7a12ec0ecd5 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -122,13 +122,20 @@
set category = "Object"
set src in usr
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
if((usr.is_clumsy()) && prob(50))
to_chat(usr, SPAN_WARNING("You cut yourself on the paper."))
return
- var/n_name = sanitizeSafe(input(usr, "What would you like to label the paper?", "Paper Labelling", null) as text, MAX_NAME_LEN)
- // We check loc one level up, so we can rename in clipboards and such. See also: /obj/item/photo/rename()
- if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == 0)
+ var/n_name = sanitizeSafe(input(usr, "What would you like to label the paper?", "Paper Labelling", null) as text, MAX_NAME_LEN)
+
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
+ var/atom/surface_atom = recursive_loc_turf_check(src, 3, usr)
+ if(surface_atom == usr || surface_atom.Adjacent(usr))
if(n_name)
name = "[initial(name)] ([n_name])"
else
diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm
index 73766068570..f73cc6bad7d 100644
--- a/code/modules/paperwork/paper_bundle.dm
+++ b/code/modules/paperwork/paper_bundle.dm
@@ -138,31 +138,41 @@
update_icon()
return
+/obj/item/paper_bundle/proc/gripper_check(var/mob/user)
+ var/obj/item/gripper/paperwork/PW = user.get_active_hand()
+ if(istype(PW))
+ if(loc == PW || (istype(loc, /obj/item/folder) && (loc.loc == PW)))
+ return TRUE
+ return FALSE
+
+/obj/item/paper_bundle/proc/hand_check(var/mob/user)
+ if(loc == user)
+ return TRUE
+ var/obj/item/folder/F = loc
+ if(istype(F) && F.loc_check(user))
+ return TRUE
+
/obj/item/paper_bundle/Topic(href, href_list)
..()
- var/obj/item/in_hand = null
- if((src in usr.contents) || (istype(src.loc, /obj/item/folder) && (src.loc in usr.contents)))
- in_hand = usr.get_active_hand()
- else if(isrobot(usr) && istype(usr.get_active_hand(), /obj/item/gripper/paperwork))
- var/obj/item/gripper/paperwork/PW = usr.get_active_hand()
- if(!(src in PW.contents) && !(istype(src.loc, /obj/item/folder) || !(src.loc in PW.contents)))
- return // paper bundle isn't in the gripper
+
+ var/in_hand = FALSE
+ if(isrobot(usr))
+ in_hand = gripper_check(usr)
else
- to_chat(usr, "You need to hold it in hands!")
+ in_hand = hand_check(usr)
+
+ if(!in_hand)
+ to_chat(usr, SPAN_WARNING("You need to hold it in hands!"))
return
usr.set_machine(src)
if(href_list["next_page"])
- if(in_hand && (istype(in_hand, /obj/item/paper) || istype(in_hand, /obj/item/photo)))
- insert_sheet_at(usr, page+1, in_hand)
- else if(page != pages.len)
+ if(page != length(pages))
page++
playsound(src.loc, /decl/sound_category/page_sound, 50, 1)
if(href_list["prev_page"])
- if(in_hand && (istype(in_hand, /obj/item/paper) || istype(in_hand, /obj/item/photo)))
- insert_sheet_at(usr, page, in_hand)
- else if(page > 1)
+ if(page > 1)
page--
playsound(src.loc, /decl/sound_category/page_sound, 50, 1)
if(href_list["remove"])
@@ -190,23 +200,30 @@
update_icon()
- if (istype(src.loc, /mob) ||istype(src.loc.loc, /mob))
- src.attack_self(usr)
- updateUsrDialog()
+ var/atom/surface_atom = recursive_loc_turf_check(src, 3, usr)
+ if(surface_atom == usr || surface_atom.Adjacent(usr))
+ attack_self(usr)
/obj/item/paper_bundle/verb/rename()
set name = "Rename bundle"
set category = "Object"
set src in usr
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
var/n_name = sanitizeSafe(input(usr, "What would you like to label the bundle?", "Bundle Labelling", null) as text, MAX_NAME_LEN)
- if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == 0)
+
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
+ var/mob/M = recursive_loc_turf_check(src, 3, usr)
+ if(M == usr)
if(n_name)
name = "[initial(name)] ([n_name])"
else
name = initial(name)
- add_fingerprint(usr)
- return
+ add_fingerprint(usr)
/obj/item/paper_bundle/verb/remove_all()
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 2e8f87c6b1c..9f8a27d2ed8 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -73,15 +73,21 @@ var/global/photo_count = 0
set category = "Object"
set src in usr
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
var/n_name = sanitizeSafe(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text, MAX_NAME_LEN)
- //loc.loc check is for making possible renaming photos in clipboards
- if(((loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0))
+
+ if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
+ return
+
+ var/atom/surface_atom = recursive_loc_turf_check(src, 3, usr)
+ if(surface_atom == usr || surface_atom.Adjacent(usr))
if(n_name)
name = "[initial(name)] ([n_name])"
else
- name = initial(n_name)
- add_fingerprint(usr)
- return
+ name = initial(name)
+ add_fingerprint(usr)
/**************
diff --git a/code/modules/projectiles/guns/projectile/plasma.dm b/code/modules/projectiles/guns/projectile/plasma.dm
index 068aa2f07fa..81012b225bf 100644
--- a/code/modules/projectiles/guns/projectile/plasma.dm
+++ b/code/modules/projectiles/guns/projectile/plasma.dm
@@ -55,7 +55,7 @@
item_state = "plasma"
magazine_type = /obj/item/ammo_magazine/plasma/light/pistol
allowed_magazines = list(/obj/item/ammo_magazine/plasma/light/pistol)
- slot_flags = SLOT_BELT
+ slot_flags = SLOT_BELT | SLOT_HOLSTER
accuracy = 1
/obj/item/gun/projectile/plasma/bolter/pistol/update_icon()
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index bdae6cb8882..cc49222049d 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -277,9 +277,9 @@
/obj/item/projectile/plasma/light
name = "plasma bolt"
- damage = 10
+ damage = 20
armor_penetration = 10
- incinerate = 5
+ incinerate = 8
/obj/item/missile
icon = 'icons/obj/grenade.dmi'
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
index df73a5d3eeb..b7e05deeb50 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
@@ -370,6 +370,54 @@
taste_description = "bitterness"
taste_mult = 1.3
+/decl/reagent/nutriment/coffeegrounds
+ name = "Coffee Grounds"
+ description = "Enjoy the great taste of coffee."
+ reagent_state = SOLID
+ nutriment_factor = 1
+ color = "#5c4a11"
+ taste_description = "earthy gritty coffee"
+ taste_mult = 0.4
+ condiment_name = "ground coffee"
+ condiment_icon_state = "coffee"
+ condiment_center_of_mass = list("x"=16, "y"=8)
+
+/decl/reagent/nutriment/coffeegrounds/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
+ ..()
+ if(alien != IS_DIONA)
+ M.dizziness = max(0, M.dizziness - 5)
+ M.drowsiness = max(0, M.drowsiness - 3)
+ M.sleeping = max(0, M.sleeping - 2)
+ M.intoxication = max(0, (M.intoxication - (removed*0.25)))
+ //copied from coffee
+
+/decl/reagent/nutriment/coffeegrounds/overdose(var/mob/living/carbon/M, var/alien, var/datum/reagents/holder)
+ if(alien != IS_DIONA)
+ M.make_jittery(5)
+ //copied from coffee
+
+/decl/reagent/nutriment/teagrounds
+ name = "Tea Grounds"
+ description = "Enjoy the great taste of tea."
+ reagent_state = SOLID
+ nutriment_factor = 1
+ color = "#4fd24d"
+ taste_description = "potent gritty tea"
+ taste_mult = 0.4
+ condiment_name = "ground tea"
+ condiment_icon_state = "tea"
+ condiment_center_of_mass = list("x"=16, "y"=8)
+ var/last_taste_time = -100
+
+/decl/reagent/nutriment/teagrounds/affect_blood(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
+ if(alien == IS_DIONA)
+ if(last_taste_time + 800 < world.time) // Not to spam message
+ to_chat(M, "Your body withers as you feel slight pain throughout.")
+ last_taste_time = world.time
+ metabolism = REM * 0.33
+ M.adjustToxLoss(1.5 * removed)
+ //Copied from tea. though i feel it should be stronger as its not diluted with water
+
/decl/reagent/nutriment/soysauce
name = "Soy Sauce"
description = "A salty sauce made from the soy plant."
@@ -787,6 +835,15 @@
glass_name = "glass of grape juice"
glass_desc = "It's grrrrrape!"
+/decl/reagent/drink/whitegrapejuice
+ name = "White Grape Juice"
+ description = "It's tart grape!"
+ color = "#863333"
+ taste_description = "tarty grapes"
+ glass_icon_state = "glass_clear"
+ glass_name = "glass of white grape juice"
+ glass_desc = "It's tart grape!"
+
/decl/reagent/drink/lemonjuice
name = "Lemon Juice"
description = "This juice is VERY sour."
@@ -4064,6 +4121,28 @@
glass_desc = "A very classy looking drink."
glass_center_of_mass = list("x"=15, "y"=7)
+/decl/reagent/alcohol/blushwine
+ name = "Blush Wine"
+ description = "A premium alchoholic beverage made from distilled grape juice."
+ color = "#e5d272"
+ strength = 10
+ taste_description = "delightful sweetness"
+ glass_icon_state = "blushwineglass"
+ glass_name = "glass of blush wine"
+ glass_desc = "A very classy looking drink."
+ glass_center_of_mass = list("x"=15, "y"=7)
+
+/decl/reagent/alcohol/melonwine
+ name = "Melon Wine"
+ description = "A fruity alchoholic beverage made from wine and melon liquor."
+ color = "#11cf39" // rgb: 126, 64, 67
+ strength = 20
+ taste_description = "mouth watering fruity sweetness"
+ glass_icon_state = "melonwine"
+ glass_name = "glass of Melon-Wine"
+ glass_desc = "A very classy looking drink."
+ glass_center_of_mass = list("x"=15, "y"=7)
+
/decl/reagent/alcohol/messa_mead
name = "Messa's Mead"
description = "A sweet alcoholic adhomian drink. Produced with Messa's tears and earthen-root."
@@ -4784,7 +4863,7 @@
strength = 30
color = "#FFF1B2"
taste_description = "pineapple, coconut, and a hint of the ocean"
-
+
glass_icon_state = "pina_colada"
glass_name = "glass of pina colada"
glass_desc = "Prepared just like in Silversun."
@@ -4809,7 +4888,7 @@
glass_icon_state = "spacecola"
glass_name = "glass of diet cola"
- glass_desc = "Space Cola! Now in diet!"
+ glass_desc = "Space Cola! Now in diet!"
/decl/reagent/drink/milk/chocolate
name = "Chocolate milk"
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
index e6462945828..73f71fb67a9 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
@@ -225,9 +225,10 @@
M.add_chemical_effect(CE_CRYO, 1)
if(M.bodytemperature < 170)
M.add_chemical_effect(CE_PULSE, -2)
- M.adjustCloneLoss(-10 * removed)
+ M.adjustCloneLoss(-100 * removed)
M.adjustOxyLoss(-10 * removed)
M.heal_organ_damage(10 * removed, 10 * removed)
+ M.adjustToxLoss(-10 * removed)
/decl/reagent/clonexadone
name = "Clonexadone"
@@ -242,9 +243,10 @@
M.add_chemical_effect(CE_CRYO, 1)
if(M.bodytemperature < 170)
M.add_chemical_effect(CE_PULSE, -2)
- M.adjustCloneLoss(-30 * removed)
+ M.adjustCloneLoss(-100 * removed)
M.adjustOxyLoss(-30 * removed)
M.heal_organ_damage(30 * removed, 30 * removed)
+ M.adjustToxLoss(-30 * removed)
/* Painkillers */
@@ -1490,4 +1492,4 @@
taste_description = "bittersweetness, insect meat and regret"
metabolism_min = 0.5
breathe_mul = 0
- goodmessage = list("You feel strange, in a good way.")
\ No newline at end of file
+ goodmessage = list("You feel strange, in a good way.")
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 3aed6eaa687..5265bd48280 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -1747,6 +1747,28 @@
catalysts = list(/decl/reagent/enzyme = 5)
result_amount = 10
+/datum/chemical_reaction/whitewine
+ name = "White Wine"
+ id = "whitewine"
+ result = /decl/reagent/alcohol/whitewine
+ required_reagents = list(/decl/reagent/drink/whitegrapejuice = 5)
+ catalysts = list(/decl/reagent/enzyme = 5)
+ result_amount = 5
+
+/datum/chemical_reaction/blushwine
+ name = "Blush Wine"
+ id = "blushwine"
+ result = /decl/reagent/alcohol/blushwine
+ required_reagents = list(/decl/reagent/alcohol/whitewine = 1, /decl/reagent/alcohol/wine = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/melonwine
+ name = "Melon Wine"
+ id = "melonwine"
+ result = /decl/reagent/alcohol/melonwine
+ required_reagents = list(/decl/reagent/alcohol/wine = 1, /decl/reagent/alcohol/melonliquor = 1)
+ result_amount = 2
+
/datum/chemical_reaction/pwine
name = "Poison Wine"
id = "pwine"
@@ -3347,6 +3369,20 @@
//Coffee expansion
//=======================
+/datum/chemical_reaction/drink/coffee
+ name = "Latte"
+ id = "latte"
+ result = /decl/reagent/drink/coffee
+ required_reagents = list(/decl/reagent/drink/coffee/espresso = 5, /decl/reagent/water = 5)
+ result_amount = 10
+
+/datum/chemical_reaction/drink/espresso
+ name = "Latte"
+ id = "latte"
+ result = /decl/reagent/drink/coffee/espresso
+ required_reagents = list(/decl/reagent/nutriment/coffeegrounds = 1, /decl/reagent/water = 5)
+ result_amount = 5
+
/datum/chemical_reaction/caramelisation
name = "Caramelised Sugar"
result = /decl/reagent/nutriment/caramel
@@ -3398,6 +3434,13 @@
result_amount = 5
mix_message = "The solution takes on an orange hue and the aroma of pumpkin spice."
+/datum/chemical_reaction/mintsyrup
+ name = "Mint Syrup"
+ id = "mintsyrup"
+ result = /decl/reagent/drink/mintsyrup
+ required_reagents = list(/decl/reagent/nutriment/mint = 2, /decl/reagent/drink/syrup_simple = 3)
+ result_amount = 5
+
/datum/chemical_reaction/drink/cuba_libre
name = "Cuba Libre"
id = "cubalibre"
diff --git a/code/modules/reagents/dispenser/coffeebeans.dm b/code/modules/reagents/dispenser/coffeebeans.dm
index 8b0c5e2af3d..2c9521b0eff 100644
--- a/code/modules/reagents/dispenser/coffeebeans.dm
+++ b/code/modules/reagents/dispenser/coffeebeans.dm
@@ -1,7 +1,7 @@
/obj/item/reagent_containers/chem_disp_cartridge/espresso
- name = "A jar of Coffee Beans "
+ name = "jar of coffee beans"
desc = "This goes into a coffee maker!"
- label = "Spesscafe Dark Blend"
+ label = "Ganymede Dark Blend"
icon = 'icons/obj/drinks.dmi'
icon_state = "coffeejar"
@@ -12,5 +12,4 @@
possible_transfer_amounts = list(20, 40)
unacidable = 1
- spawn_reagent = /decl/reagent/drink/coffee/espresso
-
+ spawn_reagent = /decl/reagent/drink/coffee/espresso
\ No newline at end of file
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 4fb5f57e81f..25d28649919 100644
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -4,7 +4,6 @@
////////////////////////////////////////////////////////////////////////////////
/obj/item/reagent_containers/glass
name = " "
- var/base_name = " "
desc = " "
icon = 'icons/obj/chemical.dmi'
icon_state = null
@@ -23,7 +22,7 @@
/obj/item/reagent_containers/glass/Initialize()
. = ..()
- base_name = name
+ AddComponent(/datum/component/base_name, name)
/obj/item/reagent_containers/glass/examine(var/mob/user)
if(!..(user, 2))
@@ -82,7 +81,8 @@
return
. = ..() // in the case of nitroglycerin, explode BEFORE it shatters
-/obj/item/reagent_containers/glass/proc/update_name_label()
+/obj/item/reagent_containers/glass/proc/update_name_label(var/base_name = initial(name))
+ SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
if(label_text == "")
name = base_name
else
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 5752e0e6e20..942e15511b1 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -269,6 +269,24 @@
capacity = 500
can_tamper = FALSE
reagents_to_add = list(/decl/reagent/water = 500)
+ var/cups = 12
+ var/cup_type = /obj/item/reagent_containers/food/drinks/sillycup
+
+/obj/structure/reagent_dispensers/water_cooler/attack_hand(var/mob/user)
+ if(cups > 0)
+ var/visible_messages = DispenserMessages(user)
+ visible_message(SPAN_NOTICE(visible_messages[1]), SPAN_NOTICE(visible_messages[2]))
+ var/cup = new cup_type(loc)
+ user.put_in_active_hand(cup)
+ cups--
+ else
+ to_chat(user, SPAN_WARNING(RejectionMessage(user)))
+
+/obj/structure/reagent_dispensers/water_cooler/proc/DispenserMessages(var/mob/user)
+ return list("\The [user] grabs a paper cup from \the [src].", "You grab a paper cup from \the [src]'s cup compartment.")
+
+/obj/structure/reagent_dispensers/water_cooler/proc/RejectionMessage(var/mob/user)
+ return "[src]'s cup dispenser is empty."
/obj/structure/reagent_dispensers/water_cooler/attackby(obj/item/W as obj, mob/user as mob)
if (W.isscrewdriver())
diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm
index 49e29d84ef6..3202544d3c6 100644
--- a/code/modules/shuttles/escape_pods.dm
+++ b/code/modules/shuttles/escape_pods.dm
@@ -1,4 +1,5 @@
var/list/escape_pods = list()
+var/list/escape_pods_by_name = list()
/datum/shuttle/autodock/ferry/escape_pod
var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller
@@ -13,6 +14,8 @@ var/list/escape_pods = list()
CRASH("Could not find arming controller for escape pod \"[name]\", tag was '[arming_controller_tag]'.")
escape_pods += src
+ escape_pods_by_name[name] = src
+ move_time = evacuation_controller.evac_transit_delay + rand(-30, 60)
if(dock_target)
var/datum/computer/file/embedded_program/docking/simple/own_target = SSshuttle.docking_registry[dock_target]
if(own_target)
@@ -48,7 +51,7 @@ var/list/escape_pods = list()
"override_enabled" = docking_program.override_enabled,
"door_state" = docking_program.memory["door_status"]["state"],
"door_lock" = docking_program.memory["door_status"]["lock"],
- "can_force" = pod.can_force() || (emergency_shuttle.departed && pod.can_launch()), //allow players to manually launch ahead of time if the shuttle leaves
+ "can_force" = pod.can_force() || (evacuation_controller.has_evacuated() && pod.can_launch()), //allow players to manually launch ahead of time if the shuttle leaves
"is_armed" = pod.arming_controller.armed
)
@@ -69,7 +72,7 @@ var/list/escape_pods = list()
if(href_list["force_launch"])
if (pod.can_force())
pod.force_launch(src)
- else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves
+ else if (evacuation_controller.has_evacuated() && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves
pod.launch(src)
return 0
diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm
index 31582c13b58..0f5882dea9d 100644
--- a/code/modules/shuttles/shuttle_emergency.dm
+++ b/code/modules/shuttles/shuttle_emergency.dm
@@ -1,39 +1,38 @@
/datum/shuttle/autodock/ferry/emergency
- //pass
+ category = /datum/shuttle/autodock/ferry/emergency
+ move_time = 10 MINUTES
+ var/datum/evacuation_controller/shuttle/emergency_controller
/datum/shuttle/autodock/ferry/emergency/New()
..()
- if(emergency_shuttle.shuttle)
+ emergency_controller = evacuation_controller
+ if(!istype(emergency_controller))
+ CRASH("Escape shuttle created without the appropriate controller type.")
+ if(emergency_controller.shuttle)
CRASH("An emergency shuttle has already been created.")
- emergency_shuttle.shuttle = src
+ emergency_controller.shuttle = src
/datum/shuttle/autodock/ferry/emergency/arrived()
. = ..()
+
+ if(!emergency_controller.has_evacuated())
+ emergency_controller.finish_preparing_evac()
+
if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = in_use
C.reset_authorization()
- emergency_shuttle.shuttle_arrived()
+ if((current_location == waypoint_offsite) && emergency_controller.has_evacuated())
+ emergency_controller.shuttle_evacuated()
/datum/shuttle/autodock/ferry/emergency/long_jump(var/obj/effect/shuttle_landmark/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time)
- var/time_to_go = SHUTTLE_TRANSIT_DURATION
+ ..(destination, interim, emergency_controller.get_long_jump_time(), direction)
if(destination == waypoint_offsite)
- time_to_go = SHUTTLE_TRANSIT_DURATION_RETURN
-
- ..(destination, interim, time_to_go)
- emergency_shuttle.launch_time = world.time
+ priority_announcement.Announce(replacetext(replacetext((emergency_controller.emergency_evacuation ? current_map.emergency_shuttle_leaving_dock : current_map.shuttle_leaving_dock), "%dock%", "[current_map.dock_name]"), "%ETA%", "[round(emergency_controller.get_eta()/60,1)] minute\s"))
/datum/shuttle/autodock/ferry/emergency/shuttle_moved()
- if (!emergency_shuttle.departed && next_location != waypoint_station) //leaving the station
- var/list/replacements = list(
- "%ETA%" = round(emergency_shuttle.estimate_arrival_time()/60,1),
- "%dock%" = current_map.dock_name
- )
- if (emergency_shuttle.evac)
- priority_announcement.Announce(replacemany(current_map.emergency_shuttle_leaving_dock, replacements))
- else
- priority_announcement.Announce(replacemany(current_map.shuttle_leaving_dock, replacements))
- emergency_shuttle.departed = TRUE
+ if(next_location != waypoint_station)
+ emergency_controller.shuttle_leaving() // This is a hell of a line. v
..()
/datum/shuttle/autodock/ferry/emergency/can_launch(var/user)
@@ -54,6 +53,8 @@
return ..()
/datum/shuttle/autodock/ferry/emergency/can_cancel(var/user)
+ if(emergency_controller.has_evacuated())
+ return 0
//If we try to cancel it via the shuttle computer
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
@@ -62,8 +63,8 @@
return 0
// If the emergency shuttle is waiting to leave the station and the world time exceeded the force time
- if(emergency_shuttle.waiting_to_leave() && (world.time > emergency_shuttle.force_time))
- return 0
+ if(evacuation_controller.is_prepared() && (world.time > emergency_controller.force_time))
+ return 0
return ..()
@@ -72,8 +73,8 @@
return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
- if (emergency_shuttle.autopilot)
- emergency_shuttle.autopilot = 0
+ if (emergency_controller.autopilot)
+ emergency_controller.autopilot = FALSE
to_world("Alert: The shuttle autopilot has been overridden. Launch sequence initiated!")
if(usr)
@@ -87,8 +88,8 @@
return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
- if (emergency_shuttle.autopilot)
- emergency_shuttle.autopilot = 0
+ if (emergency_controller.autopilot)
+ emergency_controller.autopilot = FALSE
to_world("Alert: The shuttle autopilot has been overridden. Bluespace drive engaged!")
if(usr)
@@ -102,8 +103,8 @@
return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
- if (emergency_shuttle.autopilot)
- emergency_shuttle.autopilot = 0
+ if (emergency_controller.autopilot)
+ emergency_controller.autopilot = FALSE
to_world("Alert: The shuttle autopilot has been overridden. Launch sequence aborted!")
if(usr)
diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm
index 609d4f4c743..cec6208a450 100644
--- a/code/modules/surgery/other.dm
+++ b/code/modules/surgery/other.dm
@@ -296,7 +296,9 @@
to_chat(user, SPAN_WARNING("The blades aren't spinning, you can't cut anything!"))
return FALSE
- return (affected.limb_flags & ORGAN_CAN_AMPUTATE)
+ if(affected.limb_flags & ORGAN_CAN_AMPUTATE)
+ var/confirmation = alert("You are about to amputate [target]'s [affected.name]! Are you sure you want to do that?", "Amputation confirmation", "Yes", "No")
+ return confirmation == "Yes"
/decl/surgery_step/amputate/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/affected = target.get_organ(target_zone)
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 8d706741641..6a50d4897da 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -88,8 +88,19 @@ proc/spread_germs_to_organ(var/obj/item/organ/external/E, var/mob/living/carbon/
/proc/do_surgery(mob/living/carbon/M, mob/living/user, obj/item/tool, var/autofail = FALSE)
// Check for the Hippocratic oath.
- if(!istype(M) || user.a_intent != I_HELP)
+ if(!istype(M) || user.a_intent == I_HURT)
return FALSE
+
+ var/static/list/safety_check_exceptions = list(
+ /obj/item/auto_cpr,
+ /obj/item/device/healthanalyzer,
+ /obj/item/modular_computer,
+ /obj/item/reagent_containers,
+ /obj/item/device/advanced_healthanalyzer,
+ /obj/item/device/robotanalyzer,
+ /obj/item/stack/medical,
+ /obj/item/stack/nanopaste
+ )
// Check for multi-surgery drifting.
var/zone = user.zone_sel.selecting
if(zone in M.op_stage.in_progress)
@@ -114,11 +125,13 @@ proc/spread_germs_to_organ(var/obj/item/organ/external/E, var/mob/living/carbon/
// We didn't find a surgery, or decided not to perform one.
if(!istype(S))
+ if(is_type_in_list(tool, safety_check_exceptions))
+ return FALSE//These tools are safe to bypass hippocratic oath
to_chat(user, SPAN_WARNING("You aren't sure what you could do to \the [M] with \the [tool]."))
- return FALSE
+ return TRUE
// Otherwise we can make a start on surgery!
- else if(istype(M) && !QDELETED(M) && user.a_intent == I_HELP && user.get_active_hand() == tool)
+ else if(istype(M) && !QDELETED(M) && user.get_active_hand() == tool)
// Double-check this in case it changed between initial check and now.
if(zone in M.op_stage.in_progress)
to_chat(user, SPAN_WARNING("You can't operate on this area while surgery is already in progress."))
@@ -138,11 +151,11 @@ proc/spread_germs_to_organ(var/obj/item/organ/external/E, var/mob/living/carbon/
var/mob/living/carbon/human/H = M
H.update_surgery()
return TRUE
- return FALSE
+ return TRUE
/datum/surgery_status/
var/eyes = 0
var/face = 0
var/head_reattach = 0
var/current_organ = "organ"
- var/list/in_progress = list()
\ No newline at end of file
+ var/list/in_progress = list()
diff --git a/code/modules/world_api/commands/server_query.dm b/code/modules/world_api/commands/server_query.dm
index dcb343a5603..401e8afe7e2 100644
--- a/code/modules/world_api/commands/server_query.dm
+++ b/code/modules/world_api/commands/server_query.dm
@@ -17,7 +17,7 @@
s["roundduration"] = get_round_duration_formatted()
s["gameid"] = game_id
s["game_state"] = SSticker ? 0 : SSticker.current_state
- s["transferring"] = !!(emergency_shuttle?.online())
+ s["transferring"] = !!(evacuation_controller.is_evacuating())
s["players"] = clients.len
s["staff"] = staff.len
diff --git a/html/changelog.html b/html/changelog.html
index d5dbdc2cb81..17f4e655bdf 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -35,6 +35,246 @@
-->
+
22 September 2021
+
Dronz updated:
+
+ - Tweaks the unathi caligae sprites.
+
+
NeroCavalier updated:
+
+ - Fixes surgery safety check preventing people from being treated when on roller beds. Also re-adds surgery safety check.
+ - Adds nanopaste to list of items that bypasses the check.
+ - Adds a waste bin to Security lobby.
+ - Readds wooden floor underneath one of the garden's door after accidental deletion.
+
+
WickedCybs updated:
+
+ - Adds the deep v-neck to the shirt selection menu. This is to go even further beyond.
+ - The unmarked visitor role can now select from a wider variety of station species and IPCs.
+
+
xxxxyobdab updated:
+
+ - Added a Mint Plant.
+ - Added a Coffee Plant.
+ - Added a Tea Plant.
+ - Added Tea Grounds reagent contained in Tea leaves.
+ - Added Coffee Grounds reagent contained in Coffee Beans.
+ - Added Mint Syrup Recipe. Simple Syrup (3 units) + Mint (2 units).
+ - Added White Grape Juice reagent contained in Green Grapes.
+ - Removed Grape juice in Green Grapes.
+ - Added Blush Wine Recipe: Wine + White Wine.
+ - Added Melon Wine Recipe: Wine + Melon Liquor.
+ - Added White Wine recipe: White grape juice + Enzyme.
+ - Added Espresso Recipe: Coffee Grounds (1 unit) + Water (5 units).
+ - Added Coffee Recipe: Espresso(5 units) + Water(5 units).
+ - Fixed Material Door: Wood closing icon state spelling.
+
+
+
21 September 2021
+
MattAtlas updated:
+
+ - Fixed duplicate announcement once the transfer or emergency shuttle arrives at the Odin.
+ - Dionae can now use the check light level verb.
+
+
NeroCavalier updated:
+
+ - Reduces Vannatusk's attack sound noise level.
+
+
+
17 September 2021
+
50_N00b, Alberyk, Desven updated:
+
+ - Added a new type of full window with new sprites. It can be build by making a frame from metal sheets and then completing it with reinforced glass.
+
+
Desven updated:
+
+ - Resprited the food condiment bottles.
+ - Updated the smartfridge icons.
+ - Standarized vendors panels.
+
+
Geeves updated:
+
+ - All non-basic or emagged drones can now interact with ore processing consoles and mining equipment vendors.
+
+
NeroCavalier updated:
+
+ - Moves the kitchen ATM to the wall inbetween the kitchen and the Bar to make it easier to tip the Bar.
+
+
TheGreyWolf updated:
+
+ - Fixed runtimes related to renaming loadout items.
+
+
TheGreyWolf, bunkyb updated:
+
+ - Columbo is now hatless.
+
+
+
16 September 2021
+
Desven updated:
+
+ - Added more overlays to the security belt.
+
+
NeroCavalier updated:
+
+ - There's a confirmation box for amputations. You must click yes to continue with the amputation.
+
+
+
15 September 2021
+
TheGreyWolf, Dronz & Ryverstyx updated:
+
+ - Skrells now has unique beardstyles.
+
+
+
14 September 2021
+
Desven updated:
+
+ - Added university sweatshirts to the loadout. You can rename them to be from any school.
+ - Fixed a missing pixel in the atmos jumpsuit.
+
+
Desven, 50N00b updated:
+
+ - Updated the tunnel cloak sprites.
+ - Added departamental tunnel cloaks.
+
+
Geeves updated:
+
+ - Plate carriers now spawn with a removable set of pouches, the class of carrier changing how large the set of pouches are. All pouches are interchangeable with all carriers.
+ - Plate carriers can now put special plate carrier holsters in the pouch slot, if you'd rather have that. Three spare plate holsters have been mapped into the armory.
+
+
NeroCavalier updated:
+
+ - Surface garden's doors are now symmetrical. The hydroponics crate is no longer blocking disposals.
+ - Adds more lights to several dark areas in the station. Please ping me in discord if you want to add more lights somewhere.
+ - Adds light switches to Bridge's entrance, Chapel's entrance, and by the stairs in interstitial Psychologist's area.
+
+
TheGreyWolf updated:
+
+ - The camera console & camera program's map mode is now updated to fit with the current map.
+
+
WickedCybs updated:
+
+ - RFD-C's are no longer the sole means of removing floor underplating. You can now weaken it with a welder and pry it off with a crowbar.
+
+
+
13 September 2021
+
Desven updated:
+
+ - Updated meat sprites.
+
+
Desven, 50N00b updated:
+
+ - Added new tattoo body marks.
+ - Skrell and Vaurcae can now select hair fade options.
+ - Added ribbons as an accessory to the loadout.
+
+
Geeves updated:
+
+ - Suit coolers now use charge a half the usual rate, doubling their lifespan.
+ - You can now toggle a suit cooler on or near your person on-and-off by alt-clicking it.
+ - Suit coolers now have on-mob sprites that reflect the current charge.
+ - Suit coolers now have suit storage slot sprites too.
+ - Added glare dampeners to the augment loadout menu, available to Engineers and Roboticists.
+ - You can no longer remove corrective lenses by hand.
+ - Refactored drone speech, affecting maintenance drones, construction drones, mining drones and the matriarch drone. Everything should still work as expected.
+ - Maintenance drones laws have been modified, they are now allowed to interact with both maintenance and mining drones by law, now.
+ - Spesscafe Dark Blend has been renamed to Ganymede Dark Blend, additionally the reagent container's name has been changed to 'jar of coffee beans.'
+
+
Lucaken updated:
+
+ - Added a Vysokan flag and banner to the loadout.
+
+
Megarop updated:
+
+ - Changed 'Failure is you will not tolerate' to 'Failure is something you will not tolerate' in the Zeng-Hu Pharmaceuticals description.
+
+
NeroCavalier updated:
+
+ - Adds the normal baby penguin's name and desc to holodeck baby penguins.
+ - You are now able to handcuff people being aggressively grabbed by other players.
+ - Ports water coolers holding paper cups from bay. You can take out up to 12 cups from a water cooler by clicking on one with an empty hand.
+
+
WickedCybs updated:
+
+ - The tesla spine cost has been reduced from four loadout points to two.
+ - Adds a new hairstyle called the Tajaran Swoop as well as a few variations of it.
+
+
+
11 September 2021
+
NeroCavalier updated:
+
+ - You no longer attack patients in surgery with tools if you are off HARM intent.
+ - You can do surgery in DISARM or GRAB intent now.
+ - You can now examine the patient monitoring console in the OR to see your patient's status instead of having to click on it with an empty hand.
+ - Fixes grammar for rechargers.
+
+
WickedCybs updated:
+
+ - Adds a new type of eyewear. Visor sunglasses. HUD variants are included and can be selected from the loadout.
+
+
+
10 September 2021
+
Connorjg1 updated:
+
+ - Medical Cryogenics will now heal organ damage again.
+ - Medical Cryogenics will heal genetics damage at a faster rate.
+
+
NeroCavalier updated:
+
+ - Roller beds can be deployed on adjacent tiles by clicking on them with the roller bed in hand.
+
+
Omicega updated:
+
+ - Split the integrated HUD sensors augment into an integrated security HUD and integrated medical HUD. Reduced loadout cost accordingly.
+
+
+
09 September 2021
+
Desven updated:
+
+ - Updated the locker sprites.
+
+
TheGreyWolf & bunkyb updated:
+
+ - A TCFL softcap has been added under the softcap selection in the loadout.
+
+
WickedCybs updated:
+
+ - The dark green first responder jacket can now hold medical equipment in its suit slot. Both responder jackets can hold a helmet in the suit slot like the chest rig can, too.
+ - Tweaked the side sprites of the open dark green first responder jacket as it was identical to how it looked when closed.
+ - Condenses a lot of the common first responder suit wear, like the chest rig and vest into its own selection to tidy up the loadout a bit. Look for the medical outerwear selection to reselect stuff or browse the more expanded options. You will have to reselect your gear from the new selection.
+ - Condenses the IAC suit gear into its own outwear catagory too. You will have to reselect your gear from the new selection.
+
+
+
08 September 2021
+
Connorjg1 updated:
+
+ - Plasma Pistol can now be holstered.
+ - Plasma Bolts now do more damage and should no longer be less useful then traditional projectiles.
+
+
Desven updated:
+
+ - Updated the jukebox sprites.
+
+
+
07 September 2021
+
MattAtlas updated:
+
+ - Ported Baystation's evacuation controller for eventual usage in the NBT. There may be some evacuation or transfer shuttle weirdness. Make sure to report it on GitHub.
+ - The liver now only deals toxin damage to the rest of your body if you're intoxicated.
+ - Increased the speed at which oxygen loss decays.
+
+
NeroCavalier updated:
+
+ - Adds a gas mask and face mask to HoS's locker.
+
+
TheGreyWolf updated:
+
+ - Fixes the eridani, rainbow, normal & colorable softcaps in the loadout.
+
+
WickedCybs updated:
+
+ - RFDs can now build flooring on open spaces.
+
+
06 September 2021
Alberyk updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 599647b08e0..4ba1e8abeed 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -21423,3 +21423,192 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
WickedCybs:
- tweak: The TCFL dress beret has been tweaked to be a paler blue, to fit more with
the TCFL jackets available in the loadout
+2021-09-07:
+ MattAtlas:
+ - experiment: Ported Baystation's evacuation controller for eventual usage
+ in the NBT. There may be some evacuation or transfer shuttle weirdness. Make
+ sure to report it on GitHub.
+ - tweak: The liver now only deals toxin damage to the rest of your body if you're
+ intoxicated.
+ - tweak: Increased the speed at which oxygen loss decays.
+ NeroCavalier:
+ - rscadd: Adds a gas mask and face mask to HoS's locker.
+ TheGreyWolf:
+ - bugfix: Fixes the eridani, rainbow, normal & colorable softcaps in the loadout.
+ WickedCybs:
+ - bugfix: RFDs can now build flooring on open spaces.
+2021-09-08:
+ Connorjg1:
+ - tweak: Plasma Pistol can now be holstered.
+ - balance: Plasma Bolts now do more damage and should no longer be less useful then
+ traditional projectiles.
+ Desven:
+ - rscadd: Updated the jukebox sprites.
+2021-09-09:
+ Desven:
+ - rscadd: Updated the locker sprites.
+ TheGreyWolf & bunkyb:
+ - rscadd: A TCFL softcap has been added under the softcap selection in the loadout.
+ WickedCybs:
+ - tweak: The dark green first responder jacket can now hold medical equipment in
+ its suit slot. Both responder jackets can hold a helmet in the suit slot like
+ the chest rig can, too.
+ - tweak: Tweaked the side sprites of the open dark green first responder jacket
+ as it was identical to how it looked when closed.
+ - tweak: Condenses a lot of the common first responder suit wear, like the chest
+ rig and vest into its own selection to tidy up the loadout a bit. Look for the
+ medical outerwear selection to reselect stuff or browse the more expanded options.
+ You will have to reselect your gear from the new selection.
+ - tweak: Condenses the IAC suit gear into its own outwear catagory too. You will
+ have to reselect your gear from the new selection.
+2021-09-10:
+ Connorjg1:
+ - tweak: Medical Cryogenics will now heal organ damage again.
+ - balance: Medical Cryogenics will heal genetics damage at a faster rate.
+ NeroCavalier:
+ - rscadd: Roller beds can be deployed on adjacent tiles by clicking on them with
+ the roller bed in hand.
+ Omicega:
+ - rscadd: Split the integrated HUD sensors augment into an integrated security HUD
+ and integrated medical HUD. Reduced loadout cost accordingly.
+2021-09-11:
+ NeroCavalier:
+ - tweak: You no longer attack patients in surgery with tools if you are off HARM
+ intent.
+ - tweak: You can do surgery in DISARM or GRAB intent now.
+ - tweak: You can now examine the patient monitoring console in the OR to see your
+ patient's status instead of having to click on it with an empty hand.
+ - spellcheck: Fixes grammar for rechargers.
+ WickedCybs:
+ - rscadd: Adds a new type of eyewear. Visor sunglasses. HUD variants are included
+ and can be selected from the loadout.
+2021-09-13:
+ Desven:
+ - rscadd: Updated meat sprites.
+ Desven, 50N00b:
+ - rscadd: Added new tattoo body marks.
+ - rscadd: Skrell and Vaurcae can now select hair fade options.
+ - rscadd: Added ribbons as an accessory to the loadout.
+ Geeves:
+ - tweak: Suit coolers now use charge a half the usual rate, doubling their lifespan.
+ - rscadd: You can now toggle a suit cooler on or near your person on-and-off by
+ alt-clicking it.
+ - rscadd: Suit coolers now have on-mob sprites that reflect the current charge.
+ - bugfix: Suit coolers now have suit storage slot sprites too.
+ - rscadd: Added glare dampeners to the augment loadout menu, available to Engineers
+ and Roboticists.
+ - bugfix: You can no longer remove corrective lenses by hand.
+ - refactor: Refactored drone speech, affecting maintenance drones, construction
+ drones, mining drones and the matriarch drone. Everything should still work
+ as expected.
+ - tweak: Maintenance drones laws have been modified, they are now allowed to interact
+ with both maintenance and mining drones by law, now.
+ - rscadd: Spesscafe Dark Blend has been renamed to Ganymede Dark Blend, additionally
+ the reagent container's name has been changed to 'jar of coffee beans.'
+ Lucaken:
+ - rscadd: Added a Vysokan flag and banner to the loadout.
+ Megarop:
+ - spellcheck: Changed 'Failure is you will not tolerate' to 'Failure
+ is something you will not tolerate' in the Zeng-Hu Pharmaceuticals description.
+ NeroCavalier:
+ - rscadd: Adds the normal baby penguin's name and desc to holodeck baby penguins.
+ - rscadd: You are now able to handcuff people being aggressively grabbed by other
+ players.
+ - rscadd: Ports water coolers holding paper cups from bay. You can take out up to
+ 12 cups from a water cooler by clicking on one with an empty hand.
+ WickedCybs:
+ - tweak: The tesla spine cost has been reduced from four loadout points to two.
+ - rscadd: Adds a new hairstyle called the Tajaran Swoop as well as a few variations
+ of it.
+2021-09-14:
+ Desven:
+ - rscadd: Added university sweatshirts to the loadout. You can rename them to be
+ from any school.
+ - bugfix: Fixed a missing pixel in the atmos jumpsuit.
+ Desven, 50N00b:
+ - rscadd: Updated the tunnel cloak sprites.
+ - rscadd: Added departamental tunnel cloaks.
+ Geeves:
+ - rscadd: Plate carriers now spawn with a removable set of pouches, the class of
+ carrier changing how large the set of pouches are. All pouches are interchangeable
+ with all carriers.
+ - rscadd: Plate carriers can now put special plate carrier holsters in the pouch
+ slot, if you'd rather have that. Three spare plate holsters have been mapped
+ into the armory.
+ NeroCavalier:
+ - rscadd: Surface garden's doors are now symmetrical. The hydroponics crate
+ is no longer blocking disposals.
+ - maptweak: Adds more lights to several dark areas in the station. Please ping me
+ in discord if you want to add more lights somewhere.
+ - maptweak: Adds light switches to Bridge's entrance, Chapel's entrance,
+ and by the stairs in interstitial Psychologist's area.
+ TheGreyWolf:
+ - bugfix: The camera console & camera program's map mode is now updated
+ to fit with the current map.
+ WickedCybs:
+ - rscadd: RFD-C's are no longer the sole means of removing floor underplating.
+ You can now weaken it with a welder and pry it off with a crowbar.
+2021-09-15:
+ TheGreyWolf, Dronz & Ryverstyx:
+ - rscadd: Skrells now has unique beardstyles.
+2021-09-16:
+ Desven:
+ - rscadd: Added more overlays to the security belt.
+ NeroCavalier:
+ - rscadd: There's a confirmation box for amputations. You must click yes to
+ continue with the amputation.
+2021-09-17:
+ 50_N00b, Alberyk, Desven:
+ - rscadd: Added a new type of full window with new sprites. It can be build by making
+ a frame from metal sheets and then completing it with reinforced glass.
+ Desven:
+ - rscadd: Resprited the food condiment bottles.
+ - rscadd: Updated the smartfridge icons.
+ - tweak: Standarized vendors panels.
+ Geeves:
+ - tweak: All non-basic or emagged drones can now interact with ore processing consoles
+ and mining equipment vendors.
+ NeroCavalier:
+ - maptweak: Moves the kitchen ATM to the wall inbetween the kitchen and the Bar
+ to make it easier to tip the Bar.
+ TheGreyWolf:
+ - bugfix: Fixed runtimes related to renaming loadout items.
+ TheGreyWolf, bunkyb:
+ - tweak: Columbo is now hatless.
+2021-09-21:
+ MattAtlas:
+ - bugfix: Fixed duplicate announcement once the transfer or emergency shuttle arrives
+ at the Odin.
+ - bugfix: Dionae can now use the check light level verb.
+ NeroCavalier:
+ - tweak: Reduces Vannatusk's attack sound noise level.
+2021-09-22:
+ Dronz:
+ - tweak: Tweaks the unathi caligae sprites.
+ NeroCavalier:
+ - bugfix: Fixes surgery safety check preventing people from being treated when on
+ roller beds. Also re-adds surgery safety check.
+ - rscadd: Adds nanopaste to list of items that bypasses the check.
+ - maptweak: Adds a waste bin to Security lobby.
+ - bugfix: Readds wooden floor underneath one of the garden's door after accidental
+ deletion.
+ WickedCybs:
+ - rscadd: Adds the deep v-neck to the shirt selection menu. This is to go even further
+ beyond.
+ - tweak: The unmarked visitor role can now select from a wider variety of station
+ species and IPCs.
+ xxxxyobdab:
+ - rscadd: Added a Mint Plant.
+ - rscadd: Added a Coffee Plant.
+ - rscadd: Added a Tea Plant.
+ - rscadd: Added Tea Grounds reagent contained in Tea leaves.
+ - rscadd: Added Coffee Grounds reagent contained in Coffee Beans.
+ - rscadd: Added Mint Syrup Recipe. Simple Syrup (3 units) + Mint (2 units).
+ - rscadd: Added White Grape Juice reagent contained in Green Grapes.
+ - rscdel: Removed Grape juice in Green Grapes.
+ - rscadd: 'Added Blush Wine Recipe: Wine + White Wine.'
+ - rscadd: 'Added Melon Wine Recipe: Wine + Melon Liquor.'
+ - rscadd: 'Added White Wine recipe: White grape juice + Enzyme.'
+ - rscadd: 'Added Espresso Recipe: Coffee Grounds (1 unit) + Water (5 units).'
+ - rscadd: 'Added Coffee Recipe: Espresso(5 units) + Water(5 units).'
+ - rscadd: 'Fixed Material Door: Wood closing icon state spelling.'
diff --git a/icons/clothing/eyes/welding_goggles.dmi b/icons/clothing/eyes/welding_goggles.dmi
index c70d315d3f4..a0df0949c60 100644
Binary files a/icons/clothing/eyes/welding_goggles.dmi and b/icons/clothing/eyes/welding_goggles.dmi differ
diff --git a/icons/clothing/kit/first_responder.dmi b/icons/clothing/kit/first_responder.dmi
index e7205a2b5f0..0167f43b2d2 100644
Binary files a/icons/clothing/kit/first_responder.dmi and b/icons/clothing/kit/first_responder.dmi differ
diff --git a/icons/clothing/kit/modular_armor.dmi b/icons/clothing/kit/modular_armor.dmi
index cb021d8b142..11bc8f6c482 100644
Binary files a/icons/clothing/kit/modular_armor.dmi and b/icons/clothing/kit/modular_armor.dmi differ
diff --git a/icons/effects/map_effects.dmi b/icons/effects/map_effects.dmi
index ef9246ee152..c3d0935ebeb 100644
Binary files a/icons/effects/map_effects.dmi and b/icons/effects/map_effects.dmi differ
diff --git a/icons/mob/eyes.dmi b/icons/mob/eyes.dmi
index 3ff55a8e0df..a131f23d6e5 100644
Binary files a/icons/mob/eyes.dmi and b/icons/mob/eyes.dmi differ
diff --git a/icons/mob/human_face/msai_hair.dmi b/icons/mob/human_face/msai_hair.dmi
index 48dfd95cf31..6796970438d 100644
Binary files a/icons/mob/human_face/msai_hair.dmi and b/icons/mob/human_face/msai_hair.dmi differ
diff --git a/icons/mob/human_face/skrell_beards.dmi b/icons/mob/human_face/skrell_beards.dmi
new file mode 100644
index 00000000000..3edf227c1ab
Binary files /dev/null and b/icons/mob/human_face/skrell_beards.dmi differ
diff --git a/icons/mob/human_face/tajara_hair.dmi b/icons/mob/human_face/tajara_hair.dmi
index 82c58bc6f6e..c55abe7ba62 100644
Binary files a/icons/mob/human_face/tajara_hair.dmi and b/icons/mob/human_face/tajara_hair.dmi differ
diff --git a/icons/mob/human_races/markings.dmi b/icons/mob/human_races/markings.dmi
index c1d5e9760ff..a85ddc1dac5 100644
Binary files a/icons/mob/human_races/markings.dmi and b/icons/mob/human_races/markings.dmi differ
diff --git a/icons/mob/items/clothing/lefthand_glasses.dmi b/icons/mob/items/clothing/lefthand_glasses.dmi
index 0ae34ef31ac..c33250ef521 100644
Binary files a/icons/mob/items/clothing/lefthand_glasses.dmi and b/icons/mob/items/clothing/lefthand_glasses.dmi differ
diff --git a/icons/mob/items/clothing/righthand_glasses.dmi b/icons/mob/items/clothing/righthand_glasses.dmi
index 888d004f0a8..55afae307aa 100644
Binary files a/icons/mob/items/clothing/righthand_glasses.dmi and b/icons/mob/items/clothing/righthand_glasses.dmi differ
diff --git a/icons/mob/npc/pets.dmi b/icons/mob/npc/pets.dmi
index ee9714fe308..8e5ae8332f5 100644
Binary files a/icons/mob/npc/pets.dmi and b/icons/mob/npc/pets.dmi differ
diff --git a/icons/mob/species/unathi/shoes.dmi b/icons/mob/species/unathi/shoes.dmi
index b4f0ca893ee..e0a881e74f2 100644
Binary files a/icons/mob/species/unathi/shoes.dmi and b/icons/mob/species/unathi/shoes.dmi differ
diff --git a/icons/mob/species/vaurca/back.dmi b/icons/mob/species/vaurca/back.dmi
index 778e3b26b28..d392c0d00df 100644
Binary files a/icons/mob/species/vaurca/back.dmi and b/icons/mob/species/vaurca/back.dmi differ
diff --git a/icons/mob/ties.dmi b/icons/mob/ties.dmi
index f5abd3d94fe..5e2f1883e4d 100644
Binary files a/icons/mob/ties.dmi and b/icons/mob/ties.dmi differ
diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi
index 26c8f00987d..2ff3368e369 100644
Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ
diff --git a/icons/obj/closet.dmi b/icons/obj/closet.dmi
index 3c77f3a7e41..ef6c3b3c111 100644
Binary files a/icons/obj/closet.dmi and b/icons/obj/closet.dmi differ
diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi
index 11da3958454..cfe00d862dd 100644
Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ
diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi
index 33520ff6066..cf2f6a7be7e 100644
Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ
diff --git a/icons/obj/clothing/hats/soft_caps.dmi b/icons/obj/clothing/hats/soft_caps.dmi
index fc19cf47edf..23bab43dca9 100644
Binary files a/icons/obj/clothing/hats/soft_caps.dmi and b/icons/obj/clothing/hats/soft_caps.dmi differ
diff --git a/icons/obj/clothing/ties.dmi b/icons/obj/clothing/ties.dmi
index 839514fdbcb..a10e56e94d9 100644
Binary files a/icons/obj/clothing/ties.dmi and b/icons/obj/clothing/ties.dmi differ
diff --git a/icons/obj/contained_items/tools/suitcooler.dmi b/icons/obj/contained_items/tools/suitcooler.dmi
new file mode 100644
index 00000000000..109858f8c0e
Binary files /dev/null and b/icons/obj/contained_items/tools/suitcooler.dmi differ
diff --git a/icons/obj/custom_items/suraya_dice.dmi b/icons/obj/custom_items/suraya_dice.dmi
new file mode 100644
index 00000000000..ce477dae2b0
Binary files /dev/null and b/icons/obj/custom_items/suraya_dice.dmi differ
diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi
index 4a610be498f..6b7c5543275 100644
Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ
diff --git a/icons/obj/doors/material_doors.dmi b/icons/obj/doors/material_doors.dmi
index ccbae06949e..a224dd21aa2 100644
Binary files a/icons/obj/doors/material_doors.dmi and b/icons/obj/doors/material_doors.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index 6538087e94e..2f28e67b2ca 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/food.dmi b/icons/obj/food.dmi
index 0f9505b2ec7..00a7faaaef1 100644
Binary files a/icons/obj/food.dmi and b/icons/obj/food.dmi differ
diff --git a/icons/obj/guncabinet.dmi b/icons/obj/guncabinet.dmi
index 5a9c364b605..354a7c75f07 100644
Binary files a/icons/obj/guncabinet.dmi and b/icons/obj/guncabinet.dmi differ
diff --git a/icons/obj/hydroponics_growing.dmi b/icons/obj/hydroponics_growing.dmi
index e6970fe1f14..9330ecd2415 100644
Binary files a/icons/obj/hydroponics_growing.dmi and b/icons/obj/hydroponics_growing.dmi differ
diff --git a/icons/obj/hydroponics_products.dmi b/icons/obj/hydroponics_products.dmi
index de0ed1d4b65..32f41b4db2c 100644
Binary files a/icons/obj/hydroponics_products.dmi and b/icons/obj/hydroponics_products.dmi differ
diff --git a/icons/obj/jukebox.dmi b/icons/obj/jukebox.dmi
index 50810f60d75..9f3785b0a32 100644
Binary files a/icons/obj/jukebox.dmi and b/icons/obj/jukebox.dmi differ
diff --git a/icons/obj/sciguncabinet.dmi b/icons/obj/sciguncabinet.dmi
index 214de6d7649..feefbcf10e4 100644
Binary files a/icons/obj/sciguncabinet.dmi and b/icons/obj/sciguncabinet.dmi differ
diff --git a/icons/obj/seeds.dmi b/icons/obj/seeds.dmi
index 428d5e9f526..ff448cb67ef 100644
Binary files a/icons/obj/seeds.dmi and b/icons/obj/seeds.dmi differ
diff --git a/icons/obj/smooth/full_window.dmi b/icons/obj/smooth/full_window.dmi
new file mode 100644
index 00000000000..1765e652a71
Binary files /dev/null and b/icons/obj/smooth/full_window.dmi differ
diff --git a/icons/obj/smooth/phoron_full_window.dmi b/icons/obj/smooth/phoron_full_window.dmi
new file mode 100644
index 00000000000..81d9fb60250
Binary files /dev/null and b/icons/obj/smooth/phoron_full_window.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index 2fb28331bdc..2e5a16d4c75 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/icons/obj/suitcooler.dmi b/icons/obj/suitcooler.dmi
deleted file mode 100644
index b735d61d717..00000000000
Binary files a/icons/obj/suitcooler.dmi and /dev/null differ
diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi
index 6abb02649bb..86f098f3267 100755
Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ
diff --git a/maps/_common/mapsystem/map.dm b/maps/_common/mapsystem/map.dm
index 85e58b1fda5..efc4e305bf0 100644
--- a/maps/_common/mapsystem/map.dm
+++ b/maps/_common/mapsystem/map.dm
@@ -50,6 +50,8 @@
var/emergency_shuttle_recall_message
var/emergency_shuttle_called_message
+ var/evac_controller_type = /datum/evacuation_controller
+
var/list/station_networks = list() // Camera networks that will show up on the console.
var/list/holodeck_programs = list() // map of string ids to /datum/holodeck_program instances
diff --git a/maps/aurora/aurora-4_mainlevel.dmm b/maps/aurora/aurora-4_mainlevel.dmm
index edd25261d3e..263afa080ac 100644
--- a/maps/aurora/aurora-4_mainlevel.dmm
+++ b/maps/aurora/aurora-4_mainlevel.dmm
@@ -5900,6 +5900,7 @@
/obj/item/clothing/suit/armor/carrier/riot,
/obj/item/clothing/head/helmet/riot,
/obj/item/shield/riot,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"alI" = (
@@ -6926,6 +6927,8 @@
/obj/item/clothing/suit/armor/carrier/heavy/sec,
/obj/item/clothing/head/helmet/security/heavy,
/obj/item/clothing/head/helmet/security/heavy,
+/obj/item/clothing/accessory/holster/modular,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"anT" = (
@@ -6933,6 +6936,9 @@
/obj/item/reagent_containers/spray/pepper,
/obj/item/reagent_containers/spray/pepper,
/obj/item/reagent_containers/spray/pepper,
+/obj/item/clothing/accessory/holster/modular,
+/obj/item/clothing/accessory/holster/modular,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"anU" = (
@@ -6968,6 +6974,7 @@
dir = 8
},
/obj/item/shield/riot,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"anW" = (
@@ -13050,6 +13057,7 @@
/obj/item/clothing/suit/armor/carrier/ablative,
/obj/item/clothing/head/helmet/ablative,
/obj/item/clothing/head/helmet/ablative,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"axS" = (
@@ -13995,6 +14003,10 @@
dir = 8;
pixel_x = 28
},
+/obj/machinery/light{
+ dir = 4;
+ icon_state = "tube1"
+ },
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"azv" = (
@@ -14014,6 +14026,7 @@
/obj/item/clothing/suit/armor/carrier/ballistic,
/obj/item/clothing/head/helmet/ballistic,
/obj/item/clothing/head/helmet/ballistic,
+/obj/item/clothing/accessory/holster/modular,
/turf/simulated/floor/tiled/dark,
/area/security/armory)
"azw" = (
@@ -18486,6 +18499,10 @@
/obj/machinery/newscaster/security_unit{
pixel_x = -32
},
+/obj/machinery/light{
+ dir = 8;
+ icon_state = "tube1"
+ },
/turf/simulated/floor/tiled,
/area/security/warden)
"aGr" = (
@@ -20644,7 +20661,6 @@
/turf/simulated/floor/tiled,
/area/security/warden)
"aJO" = (
-/obj/machinery/light,
/obj/machinery/camera/network/security{
c_tag = "Security - Warden's Office";
dir = 1
@@ -23081,6 +23097,9 @@
/obj/effect/floor_decal/corner_wide/paleblue/full{
dir = 8
},
+/obj/machinery/light_switch{
+ pixel_y = -24
+ },
/turf/simulated/floor/tiled,
/area/bridge)
"aNX" = (
@@ -25684,6 +25703,10 @@
/obj/item/device/radio/intercom{
pixel_x = -28
},
+/obj/structure/disposalpipe/segment{
+ dir = 4;
+ icon_state = "conpipe-c"
+ },
/turf/simulated/floor/tiled,
/area/security/lobby)
"aSo" = (
@@ -26524,7 +26547,8 @@
/area/security/lobby)
"aTN" = (
/obj/structure/disposalpipe/segment{
- dir = 4
+ dir = 8;
+ icon_state = "pipe-c"
},
/turf/simulated/floor/tiled,
/area/security/lobby)
@@ -28436,6 +28460,10 @@
dir = 1
},
/obj/effect/floor_decal/industrial/outline/yellow,
+/obj/machinery/light{
+ dir = 1;
+ icon_state = "tube1"
+ },
/turf/simulated/floor/tiled,
/area/hallway/primary/starboard)
"aXn" = (
@@ -49429,13 +49457,6 @@
},
/turf/simulated/floor/tiled,
/area/hallway/primary/aft)
-"bNy" = (
-/obj/machinery/atm{
- pixel_x = 32;
- pixel_y = -3
- },
-/turf/simulated/floor/tiled,
-/area/hallway/primary/aft)
"bNz" = (
/obj/structure/grille,
/obj/machinery/door/firedoor,
@@ -49762,9 +49783,6 @@
pixel_x = -2;
pixel_y = -2
},
-/obj/machinery/light{
- dir = 8
- },
/turf/simulated/floor/wood,
/area/library)
"bOk" = (
@@ -49888,6 +49906,7 @@
/area/maintenance/library)
"bOF" = (
/obj/machinery/bookbinder,
+/obj/machinery/light/small,
/turf/simulated/floor/wood,
/area/library)
"bOG" = (
@@ -52717,6 +52736,9 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 9
},
+/obj/machinery/light_switch{
+ pixel_y = -24
+ },
/turf/simulated/floor/wood,
/area/chapel/main)
"bVW" = (
@@ -54998,6 +55020,10 @@
pixel_y = 24
},
/obj/effect/floor_decal/corner/red/diagonal,
+/obj/machinery/light{
+ dir = 1;
+ icon_state = "tube1"
+ },
/turf/simulated/floor/tiled,
/area/quartermaster/break_room)
"cba" = (
@@ -56047,8 +56073,9 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 4
},
-/obj/machinery/light/small{
- dir = 8
+/obj/machinery/light{
+ dir = 8;
+ icon_state = "tube1"
},
/turf/simulated/floor/tiled,
/area/quartermaster/break_room)
@@ -56213,7 +56240,9 @@
"cdI" = (
/obj/machinery/vending/snack,
/obj/effect/floor_decal/corner/red/diagonal,
-/obj/machinery/light/small,
+/obj/machinery/light{
+ icon_state = "tube1"
+ },
/turf/simulated/floor/tiled,
/area/quartermaster/break_room)
"cdJ" = (
@@ -58928,6 +58957,13 @@
/obj/structure/disposalpipe/segment,
/turf/simulated/floor/tiled/white,
/area/medical/medbay)
+"cwl" = (
+/obj/machinery/light{
+ dir = 1;
+ icon_state = "tube1"
+ },
+/turf/simulated/floor/tiled,
+/area/hallway/primary/starboard)
"cwC" = (
/obj/machinery/door/firedoor,
/obj/structure/disposalpipe/segment{
@@ -60050,6 +60086,9 @@
"eFQ" = (
/obj/structure/table/wood,
/obj/machinery/recharger,
+/obj/machinery/light/small{
+ dir = 1
+ },
/turf/simulated/floor/wood,
/area/library)
"eFZ" = (
@@ -60804,6 +60843,13 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/carpet/blue,
/area/maintenance/medbay)
+"fRQ" = (
+/obj/machinery/light{
+ dir = 4;
+ icon_state = "tube1"
+ },
+/turf/simulated/floor/carpet,
+/area/library)
"fSk" = (
/obj/machinery/computer/security/telescreen{
desc = "Used for watching security cameras.";
@@ -62219,6 +62265,13 @@
},
/turf/simulated/floor/plating,
/area/maintenance/substation/supply)
+"iaV" = (
+/obj/structure/disposalpipe/junction{
+ dir = 8;
+ icon_state = "pipe-j2"
+ },
+/turf/simulated/floor/tiled,
+/area/security/lobby)
"ibm" = (
/obj/machinery/light_switch{
pixel_x = 11;
@@ -65805,6 +65858,13 @@
/obj/random/junk,
/turf/simulated/floor/plating,
/area/maintenance/engineering)
+"png" = (
+/obj/machinery/light{
+ dir = 8;
+ icon_state = "tube1"
+ },
+/turf/simulated/floor/wood,
+/area/crew_quarters/bar)
"pnC" = (
/obj/machinery/alarm{
dir = 8;
@@ -66694,6 +66754,12 @@
/obj/random/junk,
/turf/simulated/floor/tiled/old_white,
/area/maintenance/medbay)
+"qPN" = (
+/obj/effect/floor_decal/corner/blue{
+ dir = 9
+ },
+/turf/simulated/floor/tiled,
+/area/security/lobby)
"qQD" = (
/obj/structure/computerframe,
/turf/simulated/floor/plating,
@@ -67059,6 +67125,14 @@
},
/turf/simulated/floor/plating,
/area/maintenance/research_port)
+"rxH" = (
+/obj/effect/floor_decal/corner_wide/paleblue/diagonal,
+/obj/machinery/light{
+ dir = 4;
+ icon_state = "tube1"
+ },
+/turf/simulated/floor/tiled/white,
+/area/bridge)
"rxS" = (
/obj/machinery/atmospherics/portables_connector{
dir = 1
@@ -69799,7 +69873,6 @@
/turf/simulated/floor/wood,
/area/maintenance/bar)
"vOs" = (
-/mob/living/silicon/robot/drone/construction/matriarch,
/obj/machinery/door/window/brigdoor/southright{
dir = 1;
req_access = list(56)
@@ -69807,6 +69880,7 @@
/obj/structure/window/reinforced{
dir = 4
},
+/mob/living/silicon/robot/drone/construction/matriarch,
/turf/simulated/floor/carpet/rubber,
/area/engineering/drone_fabrication)
"vPm" = (
@@ -70054,6 +70128,17 @@
/obj/item/reagent_containers/food/condiment/flour,
/turf/simulated/floor/tiled/white,
/area/crew_quarters/kitchen)
+"wiQ" = (
+/obj/machinery/light{
+ dir = 4;
+ icon_state = "tube1"
+ },
+/obj/machinery/atm{
+ pixel_x = 32;
+ pixel_y = -3
+ },
+/turf/simulated/floor/tiled,
+/area/hallway/primary/aft)
"wje" = (
/obj/structure/cable{
d1 = 4;
@@ -71058,6 +71143,13 @@
},
/turf/simulated/floor/plating,
/area/lawoffice/consular)
+"xOZ" = (
+/obj/structure/disposalpipe/trunk{
+ dir = 1
+ },
+/obj/machinery/disposal/small/north,
+/turf/simulated/floor/tiled,
+/area/security/lobby)
"xQD" = (
/obj/structure/door_assembly/door_assembly_research{
anchored = 1
@@ -96118,7 +96210,7 @@ bKL
bOG
bIT
tII
-bJL
+fRQ
bQK
bIT
bRK
@@ -98898,8 +98990,8 @@ aLx
aLx
aOW
aLx
-aLx
-aTN
+iaV
+xOZ
aIi
aWH
aYn
@@ -99155,8 +99247,8 @@ aJA
aJA
aOX
aJA
-aJA
aTO
+qPN
aVt
aWI
aYn
@@ -99711,7 +99803,7 @@ mEM
bLw
bPz
vhg
-bNy
+bLw
bLw
bLw
bLw
@@ -99724,7 +99816,7 @@ bLw
dKk
bTm
bTU
-bYx
+wiQ
bdA
bVv
bLw
@@ -105130,7 +105222,7 @@ bZS
bWa
bWa
bWa
-bWa
+png
bSM
bPH
bPH
@@ -109944,7 +110036,7 @@ hji
awJ
kjO
qSC
-awK
+cwl
aLS
aNL
aPx
@@ -116880,7 +116972,7 @@ aqS
aAg
avi
fpX
-ezQ
+rxH
aHc
aIO
gxP
diff --git a/maps/aurora/aurora-5_interstitial.dmm b/maps/aurora/aurora-5_interstitial.dmm
index 5f63bbc33b6..6110b492f2a 100644
--- a/maps/aurora/aurora-5_interstitial.dmm
+++ b/maps/aurora/aurora-5_interstitial.dmm
@@ -11133,6 +11133,14 @@
/obj/structure/window/reinforced,
/turf/simulated/floor/plating,
/area/maintenance/medbay_virology)
+"Vm" = (
+/obj/effect/floor_decal/corner/green/diagonal,
+/obj/machinery/light_switch{
+ pixel_x = -10;
+ pixel_y = 24
+ },
+/turf/simulated/floor/tiled/white,
+/area/medical/psych)
"Vp" = (
/obj/machinery/ringer{
department = "Cargo";
@@ -51570,7 +51578,7 @@ hZ
hZ
hZ
hZ
-kt
+Vm
kt
ZY
QM
diff --git a/maps/aurora/aurora-6_surface.dmm b/maps/aurora/aurora-6_surface.dmm
index de20fc069d7..cb5cda89271 100644
--- a/maps/aurora/aurora-6_surface.dmm
+++ b/maps/aurora/aurora-6_surface.dmm
@@ -9356,26 +9356,6 @@
/obj/structure/sign/electricshock,
/turf/simulated/wall,
/area/solar/fore)
-"inr" = (
-/obj/structure/closet/crate/hydroponics,
-/obj/item/material/minihoe,
-/obj/item/material/minihoe,
-/obj/item/material/minihoe,
-/obj/item/material/hatchet,
-/obj/item/material/hatchet,
-/obj/item/material/hatchet,
-/obj/item/reagent_containers/glass/bucket,
-/obj/item/reagent_containers/glass/bucket,
-/obj/item/reagent_containers/glass/bucket,
-/obj/structure/disposalpipe/segment,
-/obj/random/seed,
-/obj/random/seed,
-/obj/random/seed,
-/obj/item/wirecutters/clippers,
-/obj/item/wirecutters/clippers,
-/obj/item/wirecutters/clippers,
-/turf/simulated/floor/wood,
-/area/hydroponics/garden)
"ioe" = (
/obj/item/device/radio/intercom{
name = "Station Intercom (General)";
@@ -11305,6 +11285,13 @@
},
/turf/simulated/floor/tiled,
/area/storage/tools)
+"klH" = (
+/obj/machinery/door/firedoor,
+/obj/machinery/door/airlock/glass{
+ name = "Garden"
+ },
+/turf/simulated/floor/wood,
+/area/hydroponics/garden)
"klU" = (
/obj/machinery/status_display{
pixel_x = 32
@@ -11472,21 +11459,6 @@
},
/turf/simulated/floor/plating,
/area/quartermaster/loading)
-"krN" = (
-/obj/structure/window/reinforced{
- dir = 8
- },
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/structure/grille,
-/obj/structure/window/reinforced{
- dir = 1
- },
-/obj/structure/window/reinforced,
-/obj/machinery/door/firedoor,
-/turf/simulated/floor/plating,
-/area/hydroponics/garden)
"ksy" = (
/obj/machinery/door/airlock/hatch{
name = "Telecoms West Wing";
@@ -19553,6 +19525,21 @@
},
/turf/simulated/floor/grass/alt,
/area/hydroponics/garden)
+"rqJ" = (
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/grille,
+/obj/structure/window/reinforced{
+ dir = 1
+ },
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced,
+/turf/simulated/floor/plating,
+/area/hydroponics/garden)
"rqW" = (
/obj/structure/cable/green{
d1 = 4;
@@ -20196,6 +20183,25 @@
/obj/machinery/portable_atmospherics/powered/scrubber,
/turf/simulated/floor/plating,
/area/storage/tools)
+"rRj" = (
+/obj/structure/closet/crate/hydroponics,
+/obj/item/material/minihoe,
+/obj/item/material/minihoe,
+/obj/item/material/minihoe,
+/obj/item/material/hatchet,
+/obj/item/material/hatchet,
+/obj/item/material/hatchet,
+/obj/item/reagent_containers/glass/bucket,
+/obj/item/reagent_containers/glass/bucket,
+/obj/item/reagent_containers/glass/bucket,
+/obj/random/seed,
+/obj/random/seed,
+/obj/random/seed,
+/obj/item/wirecutters/clippers,
+/obj/item/wirecutters/clippers,
+/obj/item/wirecutters/clippers,
+/turf/simulated/floor/grass/alt,
+/area/hydroponics/garden)
"rRm" = (
/obj/structure/shuttle_part/research{
desc = "The largest commercially available sublight engine, with a shuttle built around it and painted in NanoTrasen research division colors. Looks sleek, and fast.";
@@ -20344,13 +20350,17 @@
/turf/simulated/mineral/surface,
/area/mine/unexplored)
"rZH" = (
-/obj/machinery/door/airlock/glass{
- name = "Garden"
- },
/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
+/obj/structure/grille,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
/turf/simulated/floor/wood,
/area/hydroponics/garden)
"rZN" = (
@@ -24459,21 +24469,6 @@
temperature = 278
},
/area/tcommsat/chamber)
-"vCA" = (
-/obj/structure/window/reinforced{
- dir = 8
- },
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/structure/grille,
-/obj/structure/window/reinforced,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 1
- },
-/turf/simulated/floor/plating,
-/area/hydroponics/garden)
"vCD" = (
/obj/structure/cable/yellow{
d2 = 8;
@@ -54865,7 +54860,7 @@ kvz
ras
qnH
aJP
-inr
+nIy
nIy
mqz
nIy
@@ -55638,7 +55633,7 @@ hyn
rqf
uDd
jxp
-uDd
+rRj
gzB
wKb
hQl
@@ -56663,13 +56658,13 @@ lEd
lEd
lEd
hyn
-vCA
+iTu
rZH
-krN
+cpi
qnH
-mIG
-rIU
-rIU
+rqJ
+klH
+iTu
bcb
dUm
aVE
diff --git a/maps/aurora/code/aurora.dm b/maps/aurora/code/aurora.dm
index 0f836e31cb6..ea900e09474 100644
--- a/maps/aurora/code/aurora.dm
+++ b/maps/aurora/code/aurora.dm
@@ -58,14 +58,16 @@
NETWORK_SUPPLY
)
- shuttle_docked_message = "The scheduled crew transfer shuttle to %dock% has docked with the station. It will depart in approximately %ETA% minutes."
- shuttle_leaving_dock = "The crew transfer shuttle has left the station. Estimate %ETA% minutes until the shuttle docks at %dock%."
- shuttle_called_message = "A crew transfer to %dock% has been scheduled. The shuttle has been called. It will arrive in approximately %ETA% minutes."
+ shuttle_docked_message = "The scheduled crew transfer shuttle to %dock% has docked with the station. It will depart in approximately %ETA%."
+ shuttle_leaving_dock = "The crew transfer shuttle has left the station. Estimate %ETA% until the shuttle docks at %dock%."
+ shuttle_called_message = "A crew transfer to %dock% has been scheduled. The shuttle has been called. It will arrive in approximately %ETA%."
shuttle_recall_message = "The scheduled crew transfer has been cancelled."
emergency_shuttle_docked_message = "The emergency shuttle has docked with the station. You have approximately %ETD% minutes to board the emergency shuttle."
emergency_shuttle_leaving_dock = "The emergency shuttle has left the station. Estimate %ETA% minutes until the shuttle docks at %dock%."
emergency_shuttle_recall_message = "The emergency shuttle has been recalled."
- emergency_shuttle_called_message = "An emergency evacuation shuttle has been called. It will arrive in approximately %ETA% minutes."
+ emergency_shuttle_called_message = "An emergency evacuation shuttle has been called. It will arrive in approximately %ETA%."
+
+ evac_controller_type = /datum/evacuation_controller/shuttle
map_shuttles = list(
/datum/shuttle/autodock/ferry/escape_pod/pod/escape_pod1,
diff --git a/maps/aurora/code/aurora_shuttles.dm b/maps/aurora/code/aurora_shuttles.dm
index 9a00be6ea74..1768ddca52b 100644
--- a/maps/aurora/code/aurora_shuttles.dm
+++ b/maps/aurora/code/aurora_shuttles.dm
@@ -50,7 +50,6 @@ AURORA_ESCAPE_POD(3)
/datum/shuttle/autodock/ferry/emergency/aurora
name = "Escape Shuttle"
location = 1
- move_time = 20
warmup_time = 10
shuttle_area = /area/shuttle/escape
dock_target = "escape_shuttle"
diff --git a/maps/exodus/code/exodus.dm b/maps/exodus/code/exodus.dm
index 0ab1cada147..7e1f18b3773 100644
--- a/maps/exodus/code/exodus.dm
+++ b/maps/exodus/code/exodus.dm
@@ -42,6 +42,8 @@
emergency_shuttle_recall_message = "The emergency shuttle has been recalled."
emergency_shuttle_called_message = "An emergency evacuation shuttle has been called. It will arrive in approximately %ETA% minutes."
+ evac_controller_type = /datum/evacuation_controller/shuttle
+
station_levels = list(1)
admin_levels = list(2)
contact_levels = list(1, 5)
diff --git a/maps/runtime/code/runtime.dm b/maps/runtime/code/runtime.dm
index 4e53c380e1c..0c5fd93850b 100644
--- a/maps/runtime/code/runtime.dm
+++ b/maps/runtime/code/runtime.dm
@@ -1,6 +1,6 @@
/datum/map/runtime
- name = "Runtime Station"
- full_name = "Runtime Debugging Station"
+ name = "Runtime Ship"
+ full_name = "Runtime Debugging Ship"
path = "runtime"
lobby_icons = list('icons/misc/titlescreens/runtime/developers.dmi')
lobby_transitions = 10 SECONDS
@@ -11,7 +11,7 @@
player_levels = list(1, 2, 3)
accessible_z_levels = list(1, 2, 3)
- station_name = "NSS Runtime"
+ station_name = "NSV Runtime"
station_short = "Runtime"
dock_name = "singulo"
boss_name = "#code_dungeon"
@@ -21,6 +21,13 @@
use_overmap = TRUE
+ shuttle_docked_message = "Attention all hands: Jump preparation complete. The bluespace drive is now spooling up, secure all stations for departure. Time to jump: approximately %ETD%."
+ shuttle_leaving_dock = "Attention all hands: Jump initiated, exiting bluespace in %ETA%."
+ shuttle_called_message = "Attention all hands: Jump sequence initiated. Transit procedures are now in effect. Jump in %ETA%."
+ shuttle_recall_message = "Attention all hands: Jump sequence aborted, return to normal operating conditions."
+
+ evac_controller_type = /datum/evacuation_controller/starship
+
station_networks = list(
NETWORK_CIVILIAN_MAIN,
NETWORK_COMMAND,
diff --git a/nano/images/Aurora-3.png b/nano/images/Aurora-3.png
index 06ddcdc67da..ccfa315d5be 100644
Binary files a/nano/images/Aurora-3.png and b/nano/images/Aurora-3.png differ
diff --git a/nano/images/Aurora-4.png b/nano/images/Aurora-4.png
index f9f4b95d516..6a44eec8964 100644
Binary files a/nano/images/Aurora-4.png and b/nano/images/Aurora-4.png differ
diff --git a/nano/images/Aurora-5.png b/nano/images/Aurora-5.png
new file mode 100644
index 00000000000..5bddeeb9126
Binary files /dev/null and b/nano/images/Aurora-5.png differ
diff --git a/nano/images/Aurora-6.png b/nano/images/Aurora-6.png
index f1cc792e563..c1b93717e0b 100644
Binary files a/nano/images/Aurora-6.png and b/nano/images/Aurora-6.png differ
diff --git a/nano/templates/communication.tmpl b/nano/templates/communication.tmpl
index 492b90450c3..218487c54c5 100644
--- a/nano/templates/communication.tmpl
+++ b/nano/templates/communication.tmpl
@@ -8,11 +8,13 @@
{{/if}}
{{:helper.link('Change alert level', null, {'action' : 'sw_menu', 'target' : 5}, data.isAI || !data.net_syscont || !data.net_comms ? 'disabled' : null)}}
{{if data.can_call_shuttle}}
- {{if data.have_shuttle && data.have_shuttle_called}}
- {{:helper.link('Cancel Shuttle Call', null, {'action' : 'shuttle', 'target' : 'cancel'}, data.isAI || !data.net_syscont ? 'disabled' : null)}}
- {{else data.have_shuttle && !data.have_shuttle_called}}
- {{:helper.link('Call Emergency Shuttle', null, {'action' : 'shuttle', 'target' : 'call'}, !data.net_syscont ? 'disabled' : null)}}
- {{/if}}
+ {{for data.evac_options}}
+
+ {{:helper.link(value.option_text, null, {'action' : 'evac', 'target' : value.option_target}, ((value.needs_syscontrol && !data.net_syscont) || (!value.silicon_allowed && data.isAI)) ? 'disabled' : null, 'redButton')}}
+
+ {{empty}}
+ No further options currently available.
+ {{/for}}
{{/if}}
{{if data.current_maint_all_access}}
{{:helper.link('Disable Emergency Maintenance Access', null, {'action' : 'emergencymaint'}, data.isAI ? 'disabled' : null)}}
@@ -45,7 +47,7 @@
Set status displays:
{{:helper.link('Clear', null, {'action' : 'setstatus', 'target' : 'blank'})}}
{{:helper.link('Station Time', null, {'action' : 'setstatus', 'target' : 'time'})}}
- {{:helper.link('Shuttle ETA', null, {'action' : 'setstatus', 'target' : 'shuttle'})}}
+ {{:helper.link('Evacuation ETA', null, {'action' : 'setstatus', 'target' : 'shuttle'})}}
{{:helper.link('Message', null, {'action' : 'setstatus', 'target' : 'message'})}}
Line 1: {{:helper.link(data.message_line1 ? data.message_line1 : '(none)', null, {'action' : 'setstatus', 'target' : 'line1'})}}
diff --git a/sound/effects/Evacuation.ogg b/sound/effects/Evacuation.ogg
new file mode 100644
index 00000000000..18254352164
Binary files /dev/null and b/sound/effects/Evacuation.ogg differ
diff --git a/sound/effects/creatures/vannatusk_attack.ogg b/sound/effects/creatures/vannatusk_attack.ogg
index 4c13e6b5e2f..51d77fbd566 100644
Binary files a/sound/effects/creatures/vannatusk_attack.ogg and b/sound/effects/creatures/vannatusk_attack.ogg differ