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:

    + +

    NeroCavalier updated:

    + +

    WickedCybs updated:

    + +

    xxxxyobdab updated:

    + + +

    21 September 2021

    +

    MattAtlas updated:

    + +

    NeroCavalier updated:

    + + +

    17 September 2021

    +

    50_N00b, Alberyk, Desven updated:

    + +

    Desven updated:

    + +

    Geeves updated:

    + +

    NeroCavalier updated:

    + +

    TheGreyWolf updated:

    + +

    TheGreyWolf, bunkyb updated:

    + + +

    16 September 2021

    +

    Desven updated:

    + +

    NeroCavalier updated:

    + + +

    15 September 2021

    +

    TheGreyWolf, Dronz & Ryverstyx updated:

    + + +

    14 September 2021

    +

    Desven updated:

    + +

    Desven, 50N00b updated:

    + +

    Geeves updated:

    + +

    NeroCavalier updated:

    + +

    TheGreyWolf updated:

    + +

    WickedCybs updated:

    + + +

    13 September 2021

    +

    Desven updated:

    + +

    Desven, 50N00b updated:

    + +

    Geeves updated:

    + +

    Lucaken updated:

    + +

    Megarop updated:

    + +

    NeroCavalier updated:

    + +

    WickedCybs updated:

    + + +

    11 September 2021

    +

    NeroCavalier updated:

    + +

    WickedCybs updated:

    + + +

    10 September 2021

    +

    Connorjg1 updated:

    + +

    NeroCavalier updated:

    + +

    Omicega updated:

    + + +

    09 September 2021

    +

    Desven updated:

    + +

    TheGreyWolf & bunkyb updated:

    + +

    WickedCybs updated:

    + + +

    08 September 2021

    +

    Connorjg1 updated:

    + +

    Desven updated:

    + + +

    07 September 2021

    +

    MattAtlas updated:

    + +

    NeroCavalier updated:

    + +

    TheGreyWolf updated:

    + +

    WickedCybs updated:

    + +

    06 September 2021

    Alberyk updated:

    {{: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'})}}