Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 159 additions & 53 deletions _maps/map_files/IceBoxStation/IceBoxStation.dmm

Large diffs are not rendered by default.

16,298 changes: 8,162 additions & 8,136 deletions _maps/map_files/KiloStation/KiloStation.dmm

Large diffs are not rendered by default.

8,581 changes: 4,294 additions & 4,287 deletions _maps/map_files/LimaStation/LimaStation.dmm

Large diffs are not rendered by default.

540 changes: 314 additions & 226 deletions _maps/map_files/MetaStation/MetaStation.dmm

Large diffs are not rendered by default.

428 changes: 164 additions & 264 deletions _maps/map_files/NebulaStation/NebulaStation.dmm

Large diffs are not rendered by default.

5,473 changes: 2,740 additions & 2,733 deletions _maps/map_files/PubbyStation/PubbyStation.dmm

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions code/__DEFINES/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@
#define MIDDLE_LIP "Middle"
#define LOWER_LIP "Lower"

// Latejoin Spawnpoints
#define SPAWNPOINT_CRYO "Long-Term Crew Storage"
#define SPAWNPOINT_ARRIVALS "Arrivals Shuttle"

// Job greyscale colors for loadout items
#define COLOR_JOB_ASSISTANT /obj/item/clothing/under/color/grey::greyscale_colors
#define COLOR_JOB_BOTANIST "#33cc33"
Expand Down
4 changes: 4 additions & 0 deletions code/_onclick/hud/alert.dm
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@
/// Boolean. If TRUE, the Click() proc will attempt to Click() on the master first if there is a master.
var/click_master = TRUE

/atom/movable/screen/alert/Initialize(mapload, datum/hud/hud_owner)
. = ..()
if(mouse_over_pointer == MOUSE_HAND_POINTER)
add_filter("clickglow", 2, outline_filter(color = COLOR_GOLD, size = 1))

/atom/movable/screen/alert/MouseEntered(location,control,params)
. = ..()
Expand Down
24 changes: 20 additions & 4 deletions code/datums/records/manifest.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,39 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
if(!(person.mind?.assigned_role.job_flags & JOB_CREW_MANIFEST))
return

// replace any existing record with a fresh one
// this has the obvious glaring issue that two people with the same name will result in one record,
// but records have that issue in general so it's not exactly a new problem
var/datum/record/crew/existing_record = find_record(person.real_name, FALSE)
if(existing_record)
qdel(existing_record)
var/datum/record/locked/existing_locked = find_record(person.real_name, TRUE)
if(existing_locked)
qdel(existing_locked)

// Attempt to get assignment from ID, otherwise default to mind.
var/obj/item/card/id/id_card = person.get_idcard(hand_first = FALSE)
var/assignment = id_card?.assignment || id_card?.trim?.assignment || person.mind.assigned_role.title
var/mutable_appearance/character_appearance = new(person.appearance)
// some niche circumstances can result in us being lying down before injection - we need to undo that for the photo
if(person.body_position == LYING_DOWN)
UNLINT(character_appearance.transform = character_appearance.transform.Turn(-person.lying_angle))

var/person_gender = "Other"
if(person.gender == "male")
if(person.gender == MALE)
person_gender = "Male"
if(person.gender == "female")
if(person.gender == FEMALE)
person_gender = "Female"
person_gender += " ([person.p_they()]/[person.p_them()])"

var/datum/dna/stored/record_dna = new()
person.dna.copy_dna(record_dna)

SEND_SIGNAL(src, COMSIG_MANIFEST_HUMAN_INJECTED, person)

var/datum/record/locked/lockfile = new(
age = person.age,
blood_type = "[person.get_blood_type() || "None"]", // NON-MODULE CHANGE / NEEDS TESTING
blood_type = "[person.get_blood_type() || "None"]",
character_appearance = character_appearance,
dna_string = record_dna.unique_enzymes,
fingerprint = md5(record_dna.unique_identity),
Expand All @@ -137,7 +153,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)

new /datum/record/crew(
age = person.age,
blood_type = "[person.get_blood_type() || "None"]", // NON-MODULE CHANGE / NEEDS TESTING
blood_type = "[person.get_blood_type() || "None"]",
character_appearance = character_appearance,
dna_string = record_dna.unique_enzymes,
fingerprint = md5(record_dna.unique_identity),
Expand Down
2 changes: 2 additions & 0 deletions code/game/machinery/announcement_system.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ GLOBAL_LIST_EMPTY(announcement_systems)
message = CompileText(newhead, user, rank)
else if(message_type == "ARRIVALS_BROKEN")
message = "The arrivals shuttle has been damaged. Docking for repairs..."
else if(message_type == "DESPAWN")
message = CompileText("%PERSON, %RANK, has entered long-term crew storage.", user, rank)

broadcast(message, channels)

Expand Down
220 changes: 217 additions & 3 deletions code/game/machinery/sleepers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
///Message sent when a user enters the machine.
var/enter_message = span_boldnotice("You feel cool air surround you. You go numb as your senses turn inward.")

var/resist_time = 0 SECONDS

///List of currently available chems.
var/list/available_chems = list()
///Used when emagged to scramble which chem is used, eg: mutadone -> morphine
Expand Down Expand Up @@ -63,6 +65,13 @@
update_appearance()
reset_chem_buttons()

/obj/machinery/sleeper/on_set_panel_open(old_value)
. = ..()
if(panel_open)
set_machine_stat(machine_stat | MAINT)
else
set_machine_stat(machine_stat & ~MAINT)

/obj/machinery/sleeper/RefreshParts()
. = ..()
var/matterbin_rating
Expand All @@ -73,7 +82,7 @@

available_chems.Cut()
for(var/datum/stock_part/servo/servos in component_parts)
for(var/i in 1 to servos.tier)
for(var/i in 1 to min(servos.tier, length(possible_chems)))
available_chems |= possible_chems[i]

reset_chem_buttons()
Expand All @@ -83,8 +92,15 @@
return ..()

/obj/machinery/sleeper/container_resist_act(mob/living/user)
visible_message(span_notice("[occupant] emerges from [src]!"),
span_notice("You climb out of [src]!"))
if(resist_time > 0)
to_chat(user, span_notice("You pull at the release lever."))
if(!do_after(user, resist_time, src))
return
user.visible_message(
span_notice("[occupant] emerges from [src]!"),
span_notice("You climb out of [src]!"),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
open_machine()

/obj/machinery/sleeper/Exited(atom/movable/gone, direction)
Expand Down Expand Up @@ -368,3 +384,201 @@
log_combat(user, occupant, "sprayed [chem] into", addition = "via [src]")
return TRUE
return ..()

GLOBAL_LIST_INIT(cryo_sleepers, list())

#define IS_SPAWNING "spawning"

/obj/machinery/sleeper/cryo
name = "long-term crew storage pod"
desc = "An enclosed machine designed to put subjects in a state of suspended animation for long-term storage."
icon = 'maplestation_modules/icons/obj/machines/sleeper.dmi'
icon_state = "cryopod"
base_icon_state = "cryopod"
// circuit = /obj/item/circuitboard/machine/sleeper/cryo
enter_message = span_boldnotice("You feel a cold chill as you enter the pod. \
You feel your body go numb as you enter a state of suspended animation.")
possible_chems = null
state_open = FALSE
density = TRUE
resist_time = 0.5 SECONDS

var/throw_alert = TRUE

/obj/machinery/sleeper/cryo/Initialize(mapload)
. = ..()
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
GLOB.cryo_sleepers += src
open_machine()

/obj/machinery/sleeper/cryo/Destroy()
GLOB.cryo_sleepers -= src
return ..()

/obj/machinery/sleeper/cryo/examine(mob/user)
. = ..()
if(isliving(occupant) && user != occupant)
var/mob/living/occupant_l = occupant
var/obj/item/card/id/their_id = occupant_l.get_idcard()
. += span_notice("Inside, you can see [occupant][their_id ? ", the [their_id.assignment]" : ""][(HAS_TRAIT(occupant, TRAIT_KNOCKEDOUT) || (occupant_l.key && !occupant_l.client)) ? " - sound asleep" : ""].")
else if(isliving(user))
. += span_info(span_slightly_smaller("You can enter the pod to be put into stasis temporarily, or to despawn your character."))
. += span_info(span_slightly_smaller("This machine is not intended to be used medicinally. Avoid placing wounded patients inside, even in emergencies."))

/obj/machinery/sleeper/cryo/set_occupant(atom/movable/new_occupant)
var/mob/living/old_occupant = occupant
. = ..()
var/mob/living/new_occupant_l = new_occupant
var/skey = REF(src)
if(istype(old_occupant))
old_occupant.remove_status_effect(/datum/status_effect/grouped/stasis, skey)
REMOVE_TRAIT(old_occupant, TRAIT_BLOCK_HEADSET_USE, skey)
REMOVE_TRAIT(old_occupant, TRAIT_SOFTSPOKEN, skey)
REMOVE_TRAIT(old_occupant, TRAIT_RADSTORM_IMMUNE, skey)
UnregisterSignal(old_occupant, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE)
if(throw_alert)
old_occupant.clear_alert(skey)
if(istype(new_occupant_l))
new_occupant_l.apply_status_effect(/datum/status_effect/grouped/stasis, skey)
ADD_TRAIT(new_occupant_l, TRAIT_BLOCK_HEADSET_USE, skey)
ADD_TRAIT(new_occupant_l, TRAIT_SOFTSPOKEN, skey)
ADD_TRAIT(new_occupant_l, TRAIT_RADSTORM_IMMUNE, skey)
RegisterSignal(new_occupant_l, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, PROC_REF(early_move_check))
if(throw_alert)
new_occupant_l.throw_alert(skey, /atom/movable/screen/alert/cryosleep)
// if they have a mind, we can consider despawning them, however:
// ...if the mind is active, they are being forced to be "here", so we should be on the safe side and leave them
// ...if they have a key, but no client, they're just disconnected. leave them in the pod for if they return
// ...if they have a client, they're actively in the game, definitely don't despawn them
// ...if they have an ai controller, then it's probably just an NPC, so it doesn't need to be despawned
if(new_occupant_l.mind && !new_occupant_l.mind.active && !new_occupant_l.key && !new_occupant_l.client && !new_occupant_l.ai_controller)
addtimer(CALLBACK(src, PROC_REF(auto_despawn), new_occupant_l), 5 MINUTES)

/obj/machinery/sleeper/cryo/proc/early_move_check(mob/living/mob_occupant, new_loc, direct)
SIGNAL_HANDLER

if(mob_occupant.incapacitated(IGNORE_STASIS) || DOING_INTERACTION_WITH_TARGET(mob_occupant, src))
return NONE
INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, relaymove), mob_occupant, direct)
return COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE

/obj/machinery/sleeper/cryo/proc/auto_despawn(mob/living/mob_occupant)
if(QDELETED(mob_occupant) || occupant != mob_occupant)
return
despawn_occupant()

/obj/machinery/sleeper/cryo/close_machine(mob/user, density_to_set)
. = ..()
if(isliving(occupant))
playsound(src, 'sound/effects/spray.ogg', 5, TRUE, frequency = 0.5)

/obj/machinery/sleeper/cryo/close_machine(mob/user, density_to_set)
. = ..()
if(isliving(occupant))
playsound(src, 'sound/machines/fan_stop.ogg', 50, TRUE)

/obj/machinery/sleeper/cryo/JoinPlayerHere(mob/living/joining_mob, buckle)
if(occupant || !ishuman(joining_mob))
return ..()
throw_alert = FALSE
if(state_open)
close_machine()
set_occupant(joining_mob)
joining_mob.forceMove(src)
ADD_TRAIT(joining_mob, TRAIT_NO_EYELIDS, IS_SPAWNING) // this is solely here to prevent the record picture from having closed eyes
ADD_TRAIT(joining_mob, TRAIT_KNOCKEDOUT, IS_SPAWNING)
addtimer(CALLBACK(src, PROC_REF(finish_joining_player), joining_mob), rand(8, 12) SECONDS)
throw_alert = TRUE

/obj/machinery/sleeper/cryo/proc/finish_joining_player(mob/living/joining_mob)
REMOVE_TRAITS_IN(joining_mob, IS_SPAWNING)

/obj/machinery/sleeper/cryo/default_deconstruction_crowbar(obj/item/crowbar, ignore_panel = 0, custom_deconstruct = FALSE)
return FALSE

/obj/machinery/sleeper/cryo/default_deconstruction_screwdriver(mob/living/user, icon_state, base_icon_state, obj/item/screwdriver)
return FALSE

/obj/machinery/sleeper/cryo/default_change_direction_wrench(mob/living/user, obj/item/wrench)
return FALSE

/obj/machinery/sleeper/cryo/open_machine(drop = TRUE, density_to_set = FALSE)
density_to_set = TRUE
return ..()

/// Checks if this can generically be used as a latejoin spawnpoint
/obj/machinery/sleeper/cryo/proc/can_latejoin(datum/job/joining)
if(!isnull(occupant))
return FALSE
if(istype(joining, /datum/job/prisoner))
if(!istype(get_area(src), /area/station/security/prison))
return FALSE
else
if(!istype(get_area(src), /area/station/commons))
return FALSE
return TRUE

/// Checks if the passed item should avoid deletion when being despawned
/obj/machinery/sleeper/cryo/proc/saveable_item(obj/item/save_me)
if(save_me.resistance_flags & INDESTRUCTIBLE)
return TRUE
if(GLOB.steal_item_handler.objectives_by_path[save_me.type])
return TRUE
return FALSE

/obj/machinery/sleeper/cryo/proc/despawn_occupant()
if(!isliving(occupant))
return
var/drop_loc = drop_location()
var/mob/living/mob_occupant = occupant

set_occupant(null)
mob_occupant.ghostize(FALSE)

for(var/obj/item/save_me in mob_occupant.get_all_contents())
if(saveable_item(save_me))
mob_occupant.transferItemToLoc(save_me, drop_loc, force = TRUE, silent = TRUE)

qdel(mob_occupant)
open_machine()

var/datum/record/crew/associated_record = find_record(mob_occupant.real_name)
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
if(!QDELETED(associated_record) && !QDELETED(announcer))
announcer.announce("DESPAWN", mob_occupant.name, associated_record.rank, list())
associated_record.physical_status = PHYSICAL_UNCONSCIOUS
associated_record.medical_notes += new /datum/medical_note("Record Database", "In long-term crew storage.")

deadchat_broadcast(
" has entered long-term crew storage storage.",
"[span_name(mob_occupant.real_name)] ([associated_record?.rank || "Unknown"])",
turf_target = get_turf(src),
message_type = DEADCHAT_ARRIVALRATTLE,
)

#undef IS_SPAWNING

/atom/movable/screen/alert/cryosleep
name = "Enter Crew Storage"
desc = "You are free to idle in stasis for as long as you need, but you may also click this button to despawn your character."
mouse_over_pointer = MOUSE_HAND_POINTER
icon_state = "cold"

/atom/movable/screen/alert/cryosleep/Click(location, control, params)
. = ..()
if(!.)
return .
var/obj/machinery/sleeper/cryo/sleeper = owner.loc
if(!istype(sleeper))
stack_trace("[type] was clicked by [usr] without being in a sleeper.")
return FALSE

var/are_you_sure = tgui_alert(usr, "Are you sure you want to despawn your character?", "Despawn Character", list("Yes", "No"), 5 SECONDS)
if(are_you_sure != "Yes" || QDELETED(src) || QDELETED(sleeper) || sleeper.occupant != usr)
return FALSE

sleeper.despawn_occupant()
return TRUE

/area/station/commons/long_term_storage
name = "Long-Term Crew Storage"
9 changes: 8 additions & 1 deletion code/modules/jobs/job_types/_job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,16 @@
log_mapping("Job [title] ([type]) couldn't find a round start spawn point.")

/// Finds a valid latejoin spawn point, checking for events and special conditions.
/datum/job/proc/get_latejoin_spawn_point()
/datum/job/proc/get_latejoin_spawn_point(datum/preferences/prefs)
if(length(GLOB.jobspawn_overrides[title])) //We're doing something special today.
return pick(GLOB.jobspawn_overrides[title])
if(prefs?.read_preference(/datum/preference/choiced/preferred_latejoin_spawn) == SPAWNPOINT_CRYO)
var/list/common_sleepers = list()
for(var/obj/machinery/sleeper/cryo/sleeper as anything in GLOB.cryo_sleepers)
if(sleeper.can_latejoin(src))
common_sleepers += sleeper
if(length(common_sleepers))
return pick(common_sleepers)
if(length(SSjob.latejoin_trackers))
return pick(SSjob.latejoin_trackers)
return SSjob.get_last_resort_spawn_points()
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/dead/new_player/new_player.dm
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
return FALSE

mind.late_joiner = TRUE
var/atom/destination = mind.assigned_role.get_latejoin_spawn_point()
var/atom/destination = mind.assigned_role.get_latejoin_spawn_point(client.prefs)
if(!destination)
CRASH("Failed to find a latejoin spawn point.")
var/mob/living/character = create_character(destination)
Expand Down
1 change: 1 addition & 0 deletions maplestation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -6474,6 +6474,7 @@
#include "maplestation_modules\code\modules\client\preferences\ready_anominity.dm"
#include "maplestation_modules\code\modules\client\preferences\runechat_color.dm"
#include "maplestation_modules\code\modules\client\preferences\sound_frequency.dm"
#include "maplestation_modules\code\modules\client\preferences\spawn_location.dm"
#include "maplestation_modules\code\modules\client\preferences\toggle_radio.dm"
#include "maplestation_modules\code\modules\client\preferences\toggle_speech.dm"
#include "maplestation_modules\code\modules\client\preferences\species\lizard.dm"
Expand Down
Loading
Loading