This commit is contained in:
LetterN
2021-10-28 17:34:52 +08:00
parent 49940c373e
commit f7b898a2a9
346 changed files with 13714 additions and 8493 deletions
+11 -2
View File
@@ -48,8 +48,17 @@
continue
var/atom/movable/thing = i
thing.Crossed(src)
//
////////////////////////////////////////
/**
* meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts)
* if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this
* most of the time you want forceMove()
*/
/atom/movable/proc/abstract_move(atom/new_loc)
var/atom/old_loc = loc
// move_stacks++
loc = new_loc
Moved(old_loc)
/atom/movable/Move(atom/newloc, direct, glide_size_override = 0)
var/atom/movable/pullee = pulling
+3 -1
View File
@@ -193,7 +193,9 @@ GLOBAL_LIST_INIT(reverseradiochannels, list(
var/frequency = 0
var/transmission_method
var/list/data
var/logging_data
/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO)
/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO, logging_data = null)
src.data = data || list()
src.transmission_method = transmission_method
src.logging_data = logging_data
+77 -32
View File
@@ -44,6 +44,7 @@
/obj/machinery/computer/communications/Initialize()
. = ..()
GLOB.shuttle_caller_list += src
AddComponent(/datum/component/gps, "Secured Communications Signal")
/// Are we NOT a silicon, AND we're logged in as the captain?
/obj/machinery/computer/communications/proc/authenticated_as_non_silicon_captain(mob/user)
@@ -79,13 +80,11 @@
if (obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
SSshuttle.shuttle_purchase_requirements_met |= "emagged"
if (authenticated)
authorize_access = get_all_accesses()
to_chat(user, "<span class='danger'>You scramble the communication routing circuits!</span>")
to_chat(user, span_danger("You scramble the communication routing circuits!"))
playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
SSshuttle.shuttle_purchase_requirements_met["emagged"] = TRUE
return
/obj/machinery/computer/communications/ui_act(action, list/params)
var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MAIN, STATE_MESSAGES)
@@ -104,6 +103,7 @@
if ("answerMessage")
if (!authenticated(usr))
return
var/answer_index = params["answer"]
var/message_index = params["message"]
@@ -136,11 +136,11 @@
var/obj/item/held_item = usr.get_active_held_item()
var/obj/item/card/id/id_card = held_item?.GetID()
if (!istype(id_card))
to_chat(usr, "<span class='warning'>You need to swipe your ID!</span>")
to_chat(usr, span_warning("You need to swipe your ID!"))
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
if (!(ACCESS_CAPTAIN in id_card.access))
to_chat(usr, "<span class='warning'>You are not authorized to do this!</span>")
to_chat(usr, span_warning("You are not authorized to do this!"))
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
@@ -152,26 +152,28 @@
set_security_level(new_sec_level)
to_chat(usr, "<span class='notice'>Authorization confirmed. Modifying security level.</span>")
to_chat(usr, span_notice("Authorization confirmed. Modifying security level."))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
// Only notify people if an actual change happened
log_game("[key_name(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
alert_level_tick += 1
if ("deleteMessage")
if (!authenticated(usr))
return
var/message_index = params["message"]
if(!isnum(message_index))
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when deleting a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
CRASH("Non-numeric index provided when deleting comms console message.")
var/message_index = text2num(params["message"])
if (!message_index)
return
LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
if ("emergency_meeting")
if(!(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
return
if (!authenticated_as_silicon_or_captain(usr))
return
// emergency_meeting(usr)
if ("makePriorityAnnouncement")
if (!authenticated_as_silicon_or_captain(usr))
return
@@ -188,32 +190,36 @@
var/emagged = obj_flags & EMAGGED
if (emagged)
message_syndicate(message, usr)
to_chat(usr, "<span class='danger'>SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND.</span>")
to_chat(usr, span_danger("SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND."))
else
message_centcom(message, usr)
to_chat(usr, "<span class='notice'>Message transmitted to Central Command.</span>")
to_chat(usr, span_notice("Message transmitted to Central Command."))
var/associates = emagged ? "the Syndicate": "CentCom"
usr.log_talk(message, LOG_SAY, tag = "message to [associates]")
deadchat_broadcast(" has messaged [associates], \"[message]\" at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
deadchat_broadcast(" has messaged [associates], \"[message]\" at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
if ("purchaseShuttle")
var/can_buy_shuttles_or_fail_reason = can_buy_shuttles(usr)
if (can_buy_shuttles_or_fail_reason != TRUE)
if (can_buy_shuttles_or_fail_reason != FALSE)
to_chat(usr, "<span class='alert'>[can_buy_shuttles_or_fail_reason]</span>")
to_chat(usr, span_alert("[can_buy_shuttles_or_fail_reason]"))
return
var/list/shuttles = flatten_list(SSmapping.shuttle_templates)
var/datum/map_template/shuttle/shuttle = locate(params["shuttle"]) in shuttles
if (!istype(shuttle))
return
// if (!can_purchase_this_shuttle(shuttle))
// return
if (!shuttle.prerequisites_met())
to_chat(usr, "<span class='alert'>You have not met the requirements for purchasing this shuttle.</span>")
to_chat(usr, span_alert("You have not met the requirements for purchasing this shuttle."))
return
var/datum/bank_account/bank_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
if (bank_account.account_balance < shuttle.credit_cost)
return
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
// for(var/datum/round_event_control/shuttle_insurance/insurance_event in SSevents.control)
// insurance_event.weight *= 20
SSshuttle.unload_preview()
SSshuttle.existing_shuttle = SSshuttle.emergency
SSshuttle.action_load(shuttle, replace = TRUE)
@@ -235,7 +241,7 @@
return
var/reason = trim(html_encode(params["reason"]), MAX_MESSAGE_LEN)
nuke_request(reason, usr)
to_chat(usr, "<span class='notice'>Request sent.</span>")
to_chat(usr, span_notice("Request sent."))
usr.log_message("has requested the nuclear codes from CentCom with reason \"[reason]\"", LOG_SAY)
priority_announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested", "commandreport")
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
@@ -245,7 +251,7 @@
return
if (!(obj_flags & EMAGGED))
return
to_chat(usr, "<span class='notice'>Backup routing data restored.</span>")
to_chat(usr, span_notice("Backup routing data restored."))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
obj_flags &= ~EMAGGED
if ("sendToOtherSector")
@@ -256,7 +262,7 @@
if (!COOLDOWN_FINISHED(src, important_action_cooldown))
return
var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
var/message = trim(params["message"], MAX_MESSAGE_LEN)
if (!message)
return
@@ -268,12 +274,13 @@
var/network_name = CONFIG_GET(string/cross_comms_network)
if (network_name)
payload["network"] = network_name
payload["sender_ckey"] = usr.ckey
send2otherserver(station_name(), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
send2otherserver(html_decode(station_name()), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
minor_announce(message, title = "Outgoing message to allied station")
usr.log_talk(message, LOG_SAY, tag = "message to the other server")
message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server\[s].")
deadchat_broadcast(" has sent an outgoing message to the other station(s).</span>", "<span class='bold'>[usr.real_name]", usr)
deadchat_broadcast(" has sent an outgoing message to the other station(s).</span>", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
if ("setState")
@@ -315,7 +322,7 @@
authenticated = TRUE
authorize_access = get_all_accesses()
authorize_name = "Unknown"
to_chat(usr, "<span class='warning'>[src] lets out a quiet alarm as its login is overridden.</span>")
to_chat(usr, span_warning("[src] lets out a quiet alarm as its login is overridden."))
playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
else if(isliving(usr))
var/mob/living/L = usr
@@ -334,22 +341,36 @@
revoke_maint_all_access()
log_game("[key_name(usr)] disabled emergency maintenance access.")
message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
deadchat_broadcast(" disabled emergency maintenance access at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
deadchat_broadcast(" disabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
else
make_maint_all_access()
log_game("[key_name(usr)] enabled emergency maintenance access.")
message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
deadchat_broadcast(" enabled emergency maintenance access at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
deadchat_broadcast(" enabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
/obj/machinery/computer/communications/ui_data(mob/user)
var/list/data = list(
"authenticated" = FALSE,
"emagged" = FALSE,
"hasConnection" = has_communication(),
)
var/ui_state = issilicon(user) ? cyborg_state : state
var/has_connection = has_communication()
data["hasConnection"] = has_connection
// if(!SSjob.assigned_captain && !SSjob.safe_code_requested && SSid_access.spare_id_safe_code && has_connection)
// data["canRequestSafeCode"] = TRUE
// data["safeCodeDeliveryWait"] = 0
// else
// data["canRequestSafeCode"] = FALSE
// if(SSjob.safe_code_timer_id && has_connection)
// data["safeCodeDeliveryWait"] = timeleft(SSjob.safe_code_timer_id)
// data["safeCodeDeliveryArea"] = get_area(SSjob.safe_code_request_loc)
// else
// data["safeCodeDeliveryWait"] = 0
// data["safeCodeDeliveryArea"] = null
if (authenticated || issilicon(user))
data["authenticated"] = TRUE
data["canLogOut"] = !issilicon(user)
@@ -371,16 +392,17 @@
data["importantActionReady"] = COOLDOWN_FINISHED(src, important_action_cooldown)
data["shuttleCalled"] = FALSE
data["shuttleLastCalled"] = FALSE
data["aprilFools"] = SSevents.holidays && SSevents.holidays[APRIL_FOOLS]
data["alertLevel"] = NUM2SECLEVEL(GLOB.security_level)
data["authorizeName"] = authorize_name
data["canLogOut"] = !issilicon(user)
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user, TRUE)
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
if (authenticated_as_non_silicon_captain(user))
data["canRequestNuke"] = TRUE
if (authenticated_as_non_silicon_command(user))
data["canMessageAssociates"] = TRUE
if (can_send_messages_to_other_sectors(user))
data["canSendToSectors"] = TRUE
@@ -428,8 +450,13 @@
for (var/shuttle_id in SSmapping.shuttle_templates)
var/datum/map_template/shuttle/shuttle_template = SSmapping.shuttle_templates[shuttle_id]
if (!shuttle_template.can_be_bought || shuttle_template.credit_cost == INFINITY)
if (shuttle_template.credit_cost == INFINITY)
continue
if (!shuttle_template.can_be_bought)
continue
shuttles += list(list(
"name" = shuttle_template.name,
"description" = shuttle_template.description,
@@ -478,6 +505,7 @@
return FALSE
if (!authenticated_as_non_silicon_captain(user))
return FALSE
if (SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
return "The shuttle is already in transit."
if (SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_PURCHASED)
@@ -492,21 +520,38 @@
return length(CONFIG_GET(keyed_list/cross_server)) > 0
/**
* Call an emergency meeting
*
* Comm Console wrapper for the Communications subsystem wrapper for the call_emergency_meeting world proc.
* Checks to make sure the proc can be called, and handles relevant feedback, logging and timing.
* See the SScommunications proc definition for more detail, in short, teleports the entire crew to
* the bridge for a meetup. Should only really happen during april fools.
* Arguments:
* * user - Mob who called the meeting
*/
/obj/machinery/computer/communications/proc/emergency_meeting(mob/living/user)
// if(!SScommunications.can_make_emergency_meeting(user))
// to_chat(user, span_alert("The emergency meeting button doesn't seem to work right now. Please stand by."))
// return
// SScommunications.emergency_meeting(user)
// deadchat_broadcast(" called an emergency meeting from [span_name("[get_area_name(usr, TRUE)]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
/obj/machinery/computer/communications/proc/make_announcement(mob/living/user)
var/is_ai = issilicon(user)
if(!SScommunications.can_announce(user, is_ai))
to_chat(user, "<span class='alert'>Intercomms recharging. Please stand by.</span>")
to_chat(user, span_alert("Intercomms recharging. Please stand by."))
return
var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?")
if(!input || !user.canUseTopic(src, !issilicon(usr)))
return
if(!(user.can_speak())) //No more cheating, mime/random mute guy!
input = "..."
to_chat(user, "<span class='warning'>You find yourself unable to speak.</span>")
to_chat(user, span_warning("You find yourself unable to speak."))
else
input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too.
SScommunications.make_announcement(user, is_ai, input)
deadchat_broadcast(" made a priority announcement from <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[user.real_name]</span>", user)
deadchat_broadcast(" made a priority announcement from [span_name("[get_area_name(usr, TRUE)]")].", user, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
/obj/machinery/computer/communications/proc/post_status(command, data1, data2)
+205 -344
View File
@@ -1,3 +1,5 @@
#define AHELP_FIRST_MESSAGE "Please adminhelp before leaving the round, even if there are no administrators online!"
/*
* Cryogenic refrigeration unit. Basically a despawner.
* Stealing a lot of concepts/code from sleepers due to massive laziness.
@@ -5,170 +7,71 @@
* since time_entered, which is world.time when the occupant moves in.
* ~ Zuhayr
*/
GLOBAL_LIST_EMPTY(cryopod_computers)
//Main cryopod console.
/obj/machinery/computer/cryopod
name = "cryogenic oversight console"
desc = "An interface between crew and the cryogenic storage oversight systems."
icon = 'icons/obj/Cryogenic2.dmi'
icon = 'icons/obj/machines/cryopod.dmi'
icon_state = "cellconsole_1"
circuit = /obj/item/circuitboard/cryopodcontrol
icon_keyboard = null
// circuit = /obj/item/circuitboard/cryopodcontrol
density = FALSE
interaction_flags_machine = INTERACT_MACHINE_OFFLINE
req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) //Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) // Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
var/mode = null
var/menu = 1 //Which menu screen to display
//Used for logging people entering cryosleep and important items they are carrying.
// Used for logging people entering cryosleep and important items they are carrying.
var/list/frozen_crew = list()
var/list/obj/stored_packages = list()
var/allow_items = TRUE
var/storage_type = "crewmembers"
var/storage_name = "Cryogenic Oversight Control"
/obj/machinery/computer/cryopod/deconstruct()
/obj/machinery/computer/cryopod/Initialize()
. = ..()
for(var/i in stored_packages)
var/obj/O = i
O.forceMove(drop_location())
GLOB.cryopod_computers += src
/obj/machinery/computer/cryopod/attack_ai()
attack_hand()
/obj/machinery/computer/cryopod/Destroy()
GLOB.cryopod_computers -= src
return ..()
/obj/machinery/computer/cryopod/ui_interact(mob/user = usr)
if(!is_operational())
/obj/machinery/computer/cryopod/update_icon_state()
if(stat & (NOPOWER|BROKEN))
icon_state = "cellconsole"
return ..()
icon_state = "cellconsole_1"
return ..()
/obj/machinery/computer/cryopod/ui_interact(mob/user, datum/tgui/ui)
if(stat & (NOPOWER|BROKEN))
return
user.set_machine(src)
add_fingerprint(user)
var/dat = ""
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CryopodConsole", name)
ui.open()
dat += "<h2>Welcome, [user.real_name].</h2><hr/>"
dat += "<br><br>"
/obj/machinery/computer/cryopod/ui_data(mob/user)
var/list/data = list()
data["frozen_crew"] = frozen_crew
switch(src.menu)
if(1)
dat += "<a href='byond://?src=[REF(src)];menu=2'>View crew storage log</a><br><br>"
if(allow_items)
dat += "<a href='byond://?src=[REF(src)];menu=3'>View objects storage log</a><br><br>"
dat += "<a href='byond://?src=[REF(src)];item=1'>Recover object</a><br><br>"
dat += "<a href='byond://?src=[REF(src)];allitems=1'>Recover all objects</a><br>"
if(2)
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
dat += "<h3>Recently stored Crew</h3><br/><hr/><br/>"
if(!frozen_crew.len)
dat += "There has been no storage usage at this terminal.<br/>"
else
for(var/person in frozen_crew)
dat += "[person]<br/>"
dat += "<hr/>"
if(3)
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
dat += "<h3>Recently stored objects</h3><br/><hr/><br/>"
if(!stored_packages.len)
dat += "There has been no storage usage at this terminal.<br/>"
else
for(var/obj/O in stored_packages)
dat += "[O.name]<br/>"
dat += "<hr/>"
var/obj/item/card/id/id_card
var/datum/bank_account/current_user
if(isliving(user))
var/mob/living/person = user
id_card = person.get_idcard()
if(id_card?.registered_account)
current_user = id_card.registered_account
if(current_user)
data["account_name"] = current_user.account_holder
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
popup.set_content(dat)
popup.open()
return data
/obj/machinery/computer/cryopod/Topic(href, href_list)
if(..())
return TRUE
var/mob/user = usr
add_fingerprint(user)
if(href_list["item"])
if(!allowed(user) && !(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>Access Denied.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
if(!allow_items)
return
if(stored_packages.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
var/obj/I = tgui_input_list(user, "Please choose which object to retrieve.","Object recovery", stored_packages)
playsound(src, "terminal_type", 25, 0)
if(!I)
return
if(!(I in stored_packages))
to_chat(user, "<span class='notice'>\The [I] is no longer in storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
visible_message("<span class='notice'>The console beeps happily as it disgorges \the [I].</span>")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
I.forceMove(drop_location())
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(I)
stored_packages -= I
updateUsrDialog()
else if(href_list["allitems"])
playsound(src, "terminal_type", 25, 0)
if(!allowed(user) && !(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>Access Denied.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
if(!allow_items)
return
if(stored_packages.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
visible_message("<span class='notice'>The console beeps happily as it disgorges the desired objects.</span>")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
for(var/obj/O in stored_packages)
O.forceMove(get_turf(src))
stored_packages.Cut()
updateUsrDialog()
else if (href_list["menu"])
src.menu = text2num(href_list["menu"])
playsound(src, "terminal_type", 25, 0)
updateUsrDialog()
ui_interact(usr)
updateUsrDialog()
return
/obj/item/circuitboard/cryopodcontrol
name = "Circuit board (Cryogenic Oversight Console)"
build_path = "/obj/machinery/computer/cryopod"
/obj/machinery/computer/cryopod/contents_explosion()
return
/obj/machinery/computer/cryopod/contents_explosion()
return //don't blow everyone's shit up.
/// The box
/obj/item/storage/box/blue/cryostorage_items
w_class = WEIGHT_CLASS_HUGE
//Cryopods themselves.
// Cryopods themselves.
/obj/machinery/cryopod
name = "cryogenic freezer"
desc = "Suited for Cyborgs and Humanoids, the pod is a safe place for personnel affected by the Space Sleep Disorder to get some rest."
@@ -181,47 +84,55 @@
var/on_store_message = "has entered long-term storage."
var/on_store_name = "Cryogenic Oversight"
// 15 minutes-ish safe period before being despawned.
var/time_till_despawn = 15 * 600 // This is reduced by 90% if a player manually enters cryo
var/despawn_world_time = null // Used to keep track of the safe period.
/// Time until despawn when a mob enters a cryopod. You cannot other people in pods unless they're catatonic.
var/time_till_despawn = 30 SECONDS
/// Cooldown for when it's now safe to try an despawn the player.
COOLDOWN_DECLARE(despawn_world_time)
var/obj/machinery/computer/cryopod/control_computer
var/item_storage_type = /obj/item/storage/box/blue/cryostorage_items //with how storage components work this can be anything the player can open or anything with a storage component.
var/last_no_computer_message = 0
///Weakref to our controller
var/datum/weakref/control_computer_weakref
COOLDOWN_DECLARE(last_no_computer_message)
/obj/machinery/cryopod/Initialize(mapload)
. = ..()
/obj/machinery/cryopod/Initialize()
..()
return INITIALIZE_HINT_LATELOAD //Gotta populate the cryopod computer GLOB first
/obj/machinery/cryopod/LateInitialize()
update_icon()
find_control_computer(mapload)
find_control_computer()
// This is not a good situation
/obj/machinery/cryopod/Destroy()
control_computer_weakref = null
return ..()
/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE)
for(var/obj/machinery/computer/cryopod/C in get_area(src))
control_computer = C
if(C)
return C
break
for(var/cryo_console as anything in GLOB.cryopod_computers)
var/obj/machinery/computer/cryopod/console = cryo_console
if(get_area(console) == get_area(src))
control_computer_weakref = WEAKREF(console)
break
// Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged
if(!control_computer && urgent && last_no_computer_message + 5*60*10 < world.time)
if(!control_computer_weakref && urgent && COOLDOWN_FINISHED(src, last_no_computer_message))
COOLDOWN_START(src, last_no_computer_message, 5 MINUTES)
log_admin("Cryopod in [get_area(src)] could not find control computer!")
message_admins("Cryopod in [get_area(src)] could not find control computer!")
last_no_computer_message = world.time
return control_computer != null
return control_computer_weakref != null
/obj/machinery/cryopod/close_machine(mob/user)
if(!control_computer)
/obj/machinery/cryopod/close_machine(atom/movable/target)
if(!control_computer_weakref)
find_control_computer(TRUE)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
if((isnull(target) || isliving(target)) && state_open && !panel_open)
..(target)
var/mob/living/mob_occupant = occupant
investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(user)].", INVESTIGATE_CRYOGENICS)
investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(target)].", INVESTIGATE_CRYOGENICS)
if(mob_occupant && mob_occupant.stat != DEAD)
to_chat(occupant, "<span class='boldnotice'>You feel cool air surround you. You go numb as your senses turn inward.</span>")
if(mob_occupant.client)//if they're logged in
despawn_world_time = world.time + (time_till_despawn * 0.1)
else
despawn_world_time = world.time + time_till_despawn
to_chat(occupant, span_notice("<b>You feel cool air surround you. You go numb as your senses turn inward.</b>"))
COOLDOWN_START(src, despawn_world_time, time_till_despawn)
icon_state = "cryopod"
/obj/machinery/cryopod/open_machine()
@@ -234,8 +145,8 @@
/obj/machinery/cryopod/container_resist(mob/living/user)
investigate_log("Cryogenics machine container resisted by [key_name(user)] with occupant [key_name(occupant)].", INVESTIGATE_CRYOGENICS)
visible_message("<span class='notice'>[occupant] emerges from [src]!</span>",
"<span class='notice'>You climb out of [src]!</span>")
visible_message(span_notice("[occupant] emerges from [src]!"),
span_notice("You climb out of [src]!"))
open_machine()
/obj/machinery/cryopod/relaymove(mob/user)
@@ -246,205 +157,151 @@
return
var/mob/living/mob_occupant = occupant
if(mob_occupant)
// Eject dead people
if(mob_occupant.stat == DEAD)
open_machine()
if(mob_occupant.stat == DEAD)
open_machine()
if(!(world.time > despawn_world_time + 100))//+ 10 seconds
return
if(!mob_occupant.client && COOLDOWN_FINISHED(src, despawn_world_time))
if(!control_computer_weakref)
find_control_computer(urgent = TRUE)
if(!mob_occupant.client && mob_occupant.stat < 2) //Occupant is living and has no client.
if(!control_computer)
find_control_computer(urgent = TRUE)//better hope you found it this time
despawn_occupant()
despawn_occupant()
/obj/machinery/cryopod/proc/handle_objectives()
var/mob/living/mob_occupant = occupant
// Update any existing objectives involving this mob.
for(var/datum/objective/objective in GLOB.objectives)
// We don't want revs to get objectives that aren't for heads of staff. Letting
// them win or lose based on cryo is silly so we remove the objective.
if(istype(objective,/datum/objective/mutiny) && objective.target == mob_occupant.mind)
objective.team.objectives -= objective
qdel(objective)
for(var/datum/mind/mind in objective.team.members)
to_chat(mind.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
mind.announce_objectives()
else if(istype(objective.target) && objective.target == mob_occupant.mind)
if(istype(objective, /datum/objective/contract))
var/datum/antagonist/traitor/affected_traitor = objective.owner.has_antag_datum(/datum/antagonist/traitor)
var/datum/contractor_hub/affected_contractor_hub = affected_traitor.contractor_hub
for(var/datum/syndicate_contract/affected_contract as anything in affected_contractor_hub.assigned_contracts)
if(affected_contract.contract == objective)
affected_contract.generate(affected_contractor_hub.assigned_targets)
affected_contractor_hub.assigned_targets.Add(affected_contract.contract.target)
to_chat(objective.owner.current, "<BR>[span_userdanger("Contract target out of reach. Contract rerolled.")]")
break
else
var/old_target = objective.target
objective.target = null
if(!objective)
return
objective.find_target()
if(!objective.target && objective.owner)
to_chat(objective.owner.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
for(var/datum/antagonist/antag in objective.owner.antag_datums)
antag.objectives -= objective
if (!objective.team)
objective.update_explanation_text()
objective.owner.announce_objectives()
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
else
var/list/objectivestoupdate
for(var/datum/mind/objective_owner in objective.get_owners())
to_chat(objective_owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
for(var/datum/objective/update_target_objective in objective_owner.get_all_objectives())
LAZYADD(objectivestoupdate, update_target_objective)
objectivestoupdate += objective.team.objectives
for(var/datum/objective/update_objective in objectivestoupdate)
if(update_objective.target != old_target || !istype(update_objective,objective.type))
continue
update_objective.target = objective.target
update_objective.update_explanation_text()
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
update_objective.owner.announce_objectives()
qdel(objective)
/obj/machinery/cryopod/proc/should_preserve_item(obj/item/item)
for(var/datum/objective_item/steal/possible_item in GLOB.possible_items)
if(istype(item, possible_item.targetitem))
return TRUE
return FALSE
// This function can not be undone; do not call this unless you are sure
/obj/machinery/cryopod/proc/despawn_occupant()
if(!control_computer)
find_control_computer()
var/mob/living/mob_occupant = occupant
var/list/crew_member = list()
var/list/obj/item/storing = list()
var/list/obj/item/destroying = list()
var/list/obj/item/destroy_later = list()
investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
var/atom/target_store = (control_computer?.allow_items && control_computer) || src //the double control computer check makes it return the control computer.
var/drop_to_ground = !istype(target_store, /obj/machinery/computer/cryopod)
var/mind_identity = mob_occupant.mind?.name
var/occupant_identity = mob_occupant.real_name
if(iscyborg(mob_occupant))
var/mob/living/silicon/robot/R = mob_occupant
if(R.mmi?.brain)
destroy_later += R.mmi
destroy_later += R.mmi.brain
for(var/i in R.module)
if(!isitem(i))
destroying += i
continue
var/obj/item/I = i
// let's be honest we only care about the trash bag don't beat around the bush
if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE))
storing += I.contents
for(var/atom/movable/AM in I.contents)
AM.forceMove(src)
R.module.remove_module(I, TRUE)
else
if(ishuman(mob_occupant))
var/mob/living/carbon/human/H = mob_occupant
if(H.mind && H.client && H.client.prefs && H == H.mind.original_character)
H.SaveTCGCards()
var/list/gear = list()
if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
var/mob/living/carbon/C = mob_occupant
gear = C.get_all_gear()
for(var/i in gear)
var/obj/item/I = i
I.forceMove(src)
if(!istype(I))
destroying += I
continue
if(I.item_flags & (DROPDEL | ABSTRACT))
destroying += I
continue
if(HAS_TRAIT(I, TRAIT_NODROP))
destroying += I
continue
// WEE WOO SNOWFLAKE TIME
if(istype(I, /obj/item/pda))
var/obj/item/pda/P = I
if((P.owner == mind_identity) || (P.owner == occupant_identity))
destroying += P
else
storing += P
else if(istype(I, /obj/item/card/id))
var/obj/item/card/id/idcard = I
if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
destroying += idcard
else
storing += idcard
else
storing += I
// get rid of mobs
for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant)
L.forceMove(drop_location())
if(storing.len)
var/obj/O = new item_storage_type
O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
for(var/i in storing)
var/obj/item/I = i
I.forceMove(O)
O.forceMove(drop_to_ground? target_store.drop_location() : target_store)
if((target_store == control_computer) && !drop_to_ground)
control_computer.stored_packages += O
QDEL_LIST(destroying)
//Update any existing objectives involving this mob.
for(var/i in GLOB.objectives)
var/datum/objective/O = i
// We don't want revs to get objectives that aren't for heads of staff. Letting
// them win or lose based on cryo is silly so we remove the objective.
if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind)
qdel(O)
else if(O.target && istype(O.target, /datum/mind))
if(O.target != mob_occupant.mind)
continue
if(O.check_midround_completion())
continue
if(O.owner && O.owner.current)
to_chat(O.owner.current, "<BR><span class='userdanger'>You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!</span>")
O.target = null
spawn(10) //This should ideally fire after the occupant is deleted.
if(!O)
return
O.find_target()
O.update_explanation_text()
if(!(O.target))
qdel(O)
crew_member["name"] = mob_occupant.real_name
if(mob_occupant.mind)
//Handle job slot/tater cleanup.
if(mob_occupant.mind.assigned_role)
var/job = mob_occupant.mind.assigned_role
SSjob.FreeRole(job)
// Handle job slot/tater cleanup.
var/job = mob_occupant.mind.assigned_role
crew_member["job"] = job
SSjob.FreeRole(job)
// if(LAZYLEN(mob_occupant.mind.objectives))
// mob_occupant.mind.objectives.Cut()
mob_occupant.mind.special_role = null
else
crew_member["job"] = "N/A"
// Delete them from datacore.
var/announce_rank = null
for(var/datum/data/record/R in GLOB.data_core.medical)
if((R.fields["name"] == mob_occupant.real_name))
qdel(R)
for(var/datum/data/record/T in GLOB.data_core.security)
if((T.fields["name"] == mob_occupant.real_name))
qdel(T)
for(var/datum/data/record/G in GLOB.data_core.general)
if((G.fields["name"] == mob_occupant.real_name))
announce_rank = G.fields["rank"]
qdel(G)
for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical)
if(medical_record.fields["name"] == mob_occupant.real_name)
qdel(medical_record)
for(var/datum/data/record/security_record as anything in GLOB.data_core.security)
if(security_record.fields["name"] == mob_occupant.real_name)
qdel(security_record)
for(var/datum/data/record/general_record as anything in GLOB.data_core.general)
if(general_record.fields["name"] == mob_occupant.real_name)
announce_rank = general_record.fields["rank"]
qdel(general_record)
for(var/obj/machinery/computer/cloning/cloner in world)
for(var/datum/data/record/R in cloner.records)
if(R.fields["name"] == mob_occupant.real_name)
cloner.records.Remove(R)
//Make an announcement and log the person entering storage.
if(control_computer)
control_computer.frozen_crew += "[mob_occupant.real_name]"
var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve()
if(!control_computer)
control_computer_weakref = null
else
control_computer.frozen_crew += list(crew_member)
// Make an announcement and log the person entering storage.
if(GLOB.announcement_systems.len)
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list())
visible_message("<span class='notice'>\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.</span>")
// Ghost and delete the mob.
var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE)
if(G)
G.voluntary_ghosted = TRUE
else
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
visible_message(span_notice("[src] hums and hisses as it moves [mob_occupant.real_name] into storage."))
for(var/obj/item/item_content as anything in mob_occupant)
if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP))
continue
mob_occupant.transferItemToLoc(item_content, drop_location(), force = TRUE, silent = TRUE)
handle_objectives()
QDEL_NULL(occupant)
QDEL_LIST(destroy_later)
open_machine()
name = initial(name)
/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user)
if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
return
if(occupant)
to_chat(user, "<span class='boldnotice'>The cryo pod is already occupied!</span>")
to_chat(user, span_notice("[src] is already occupied!"))
return
if(target.stat == DEAD)
to_chat(user, "<span class='notice'>Dead people can not be put into cryo.</span>")
to_chat(user, span_notice("Dead people can not be put into cryo."))
return
if(target.client && user != target)
if(target.key && user != target)
if(iscyborg(target))
to_chat(user, "<span class='danger'>You can't put [target] into [src]. They're online.</span>")
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] online."))
else
to_chat(user, "<span class='danger'>You can't put [target] into [src]. They're conscious.</span>")
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] conscious."))
return
else if(target.client)
if(tgui_alert(target,"Would you like to enter cryosleep?",,list("Yes","No")) == "No")
return
var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!"
if(target == user && (tgalert(target, "Would you like to enter cryosleep?", "Enter Cryopod?", "Yes", "No") != "Yes"))
return
if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)//if we haven't warned them in the last 5 minutes
if(target == user)
var/list/caught_string
var/addendum = ""
if(target.mind.assigned_role in GLOB.command_positions)
@@ -462,30 +319,34 @@
LAZYADD(caught_string, "Revolutionary")
if(caught_string)
tgui_alert(target, "You're a [english_list(caught_string)]![generic_plsnoleave_message][addendum]")
tgui_alert(target, "You're a [english_list(caught_string)]! [AHELP_FIRST_MESSAGE][addendum]")
target.client.cryo_warned = world.time
return
if(!target || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
return
//rerun the checks in case of shenanigans
if(target == user)
visible_message("[user] starts climbing into the cryo pod.")
else
visible_message("[user] starts putting [target] into the cryo pod.")
// rerun the checks in case of shenanigans
if(occupant)
to_chat(user, "<span class='boldnotice'>\The [src] is in use.</span>")
to_chat(user, span_notice("[src] is already occupied!"))
return
close_machine(target)
to_chat(target, "<span class='boldnotice'>If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.</span>")
name = "[name] ([occupant.name])"
log_admin("<span class='notice'>[key_name(target)] entered a stasis pod.</span>")
message_admins("[key_name_admin(target)] entered a stasis pod. (<A HREF='?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
if(target == user)
visible_message("<span class='infoplain'>[user] starts climbing into the cryo pod.</span>")
else
visible_message("<span class='infoplain'>[user] starts putting [target] into the cryo pod.</span>")
to_chat(target, span_warning("<b>If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.</b>"))
log_admin("[key_name(target)] entered a stasis pod.")
message_admins("[key_name_admin(target)] entered a stasis pod. [ADMIN_JMP(src)]")
add_fingerprint(target)
//Attacks/effects.
close_machine(target)
name = "[name] ([target.name])"
// Attacks/effects.
/obj/machinery/cryopod/blob_act()
return //Sorta gamey, but we don't really want these to be destroyed.
return // Sorta gamey, but we don't really want these to be destroyed.
#undef AHELP_FIRST_MESSAGE
+101 -51
View File
@@ -1,8 +1,8 @@
/* Holograms!
* Contains:
* Holopad
* Hologram
* Other stuff
* Holopad
* Hologram
* Other stuff
*/
/*
@@ -24,7 +24,6 @@ Possible to do for anyone motivated enough:
* Holopad
*/
GLOBAL_LIST_EMPTY(network_holopads)
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
@@ -32,6 +31,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
name = "holopad"
desc = "It's a floor-mounted device for projecting holographic images."
icon_state = "holopad0"
base_icon_state = "holopad"
layer = LOW_OBJ_LAYER
plane = FLOOR_PLANE
flags_1 = HEAR_1
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
var/obj/effect/overlay/holo_pad_hologram/replay_holo
/// Calls will be automatically answered after a couple rings, here for debugging
var/static/force_answer_call = FALSE
// var/static/list/holopads = list()
var/static/list/holopads = list()
var/obj/effect/overlay/holoray/ray
var/ringing = FALSE
var/offset = FALSE
@@ -107,26 +107,47 @@ GLOBAL_LIST_EMPTY(network_holopads)
new_disk.forceMove(src)
disk = new_disk
/obj/machinery/holopad/tutorial/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
/obj/machinery/holopad/Moved(atom/OldLoc, Dir)
. = ..()
if(!loc)
return
// move any relevant holograms, basically non-AI, and rays with the pad
if(replay_holo)
replay_holo.abstract_move(loc)
for(var/i in holorays)
var/obj/effect/overlay/holoray/ray = holorays[i]
ray.abstract_move(loc)
var/list/non_call_masters = masters?.Copy()
for(var/datum/holocall/holocall as anything in holo_calls)
if(!holocall.user || !LAZYACCESS(masters, holocall.user))
continue
non_call_masters -= holocall.user
// moving the eye moves the holo which updates the ray too
holocall.eye.setLoc(locate(clamp(x + (holocall.hologram.x - OldLoc.x), 1, world.maxx), clamp(y + (holocall.hologram.y - OldLoc.y), 1, world.maxy), z))
for(var/mob/living/holo_master as anything in non_call_masters)
var/obj/effect/holo = masters[holo_master]
update_holoray(holo_master, holo.loc)
/obj/machinery/holopad/tutorial/attack_hand(mob/user, list/modifiers)
if(!istype(user))
return
if(user.incapacitated() || !is_operational())
return
if(replay_mode)
replay_stop()
else if(disk && disk.record)
else if(disk?.record)
replay_start()
/obj/machinery/holopad/tutorial/HasProximity(atom/movable/AM)
if (!isliving(AM))
return
if(!replay_mode && (disk && disk.record))
if(!replay_mode && (disk?.record))
replay_start()
/obj/machinery/holopad/Initialize()
. = ..()
if(on_network)
GLOB.network_holopads += src
holopads += src
/obj/machinery/holopad/Destroy()
if(outgoing_call)
@@ -146,7 +167,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
QDEL_NULL(disk)
GLOB.network_holopads -= src
holopads -= src
return ..()
/obj/machinery/holopad/power_change()
@@ -172,8 +193,10 @@ GLOBAL_LIST_EMPTY(network_holopads)
/obj/machinery/holopad/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Current projection range: <b>[holo_range]</b> units.</span>"
if(isAI(user))
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units. Use :h to speak through the projection. Right-click to project or cancel a projection. Alt-click to hangup all active and incomming calls. Ctrl-click to end projection without jumping to your last location.")
else if(in_range(user, src) || isobserver(user))
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units.")
/obj/machinery/holopad/attackby(obj/item/P, mob/user, params)
if(default_deconstruction_screwdriver(user, "holopad_open", "holopad0", P))
@@ -190,11 +213,11 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(istype(P,/obj/item/disk/holodisk))
if(disk)
to_chat(user,"<span class='warning'>There's already a disk inside [src]!</span>")
to_chat(user,span_warning("There's already a disk inside [src]!"))
return
if (!user.transferItemToLoc(P,src))
return
to_chat(user,"<span class='notice'>You insert [P] into [src].</span>")
to_chat(user,span_notice("You insert [P] into [src]."))
disk = P
return
@@ -242,24 +265,29 @@ GLOBAL_LIST_EMPTY(network_holopads)
switch(action)
if("AIrequest")
if(isAI(usr))
var/mob/living/silicon/ai/ai_user = usr
ai_user.eyeobj.setLoc(get_turf(src))
to_chat(usr, span_info("AIs can not request AI presence. Jumping instead."))
return
if(last_request + 200 < world.time)
last_request = world.time
to_chat(usr, "<span class='info'>You requested an AI's presence.</span>")
to_chat(usr, span_info("You requested an AI's presence."))
var/area/area = get_area(src)
for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
if(!AI.client)
continue
to_chat(AI, "<span class='info'>Your presence is requested at <a href='?src=[REF(AI)];jumptoholopad=[REF(src)]'>\the [area]</a>.</span>")
to_chat(AI, span_info("Your presence is requested at <a href='?src=[REF(AI)];jumptoholopad=[REF(src)]'>\the [area]</a>.</span>")) // <a href='?src=[REF(AI)];project_to_holopad=[REF(src)]'>Project Hologram?</a>"))
return TRUE
else
to_chat(usr, "<span class='info'>A request for AI presence was already sent recently.</span>")
to_chat(usr, span_info("A request for AI presence was already sent recently."))
return
if("holocall")
if(outgoing_call)
return
if(usr.loc == loc)
var/list/callnames = list()
for(var/I in GLOB.network_holopads)
for(var/I in holopads)
var/area/A = get_area(I)
if(A)
LAZYADD(callnames[A], I)
@@ -274,7 +302,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
calling = TRUE
return TRUE
else
to_chat(usr, "<span class='warning'>You must stand on the holopad to make a call!</span>")
to_chat(usr, span_warning("You must stand on the holopad to make a call!"))
if("connectcall")
var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls
if(!QDELETED(call_to_connect))
@@ -285,6 +313,12 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(!QDELETED(call_to_disconnect))
call_to_disconnect.Disconnect(src)
return TRUE
if("rejectall")
for(var/datum/holocall/call_to_reject as anything in holo_calls)
if(call_to_reject.connected_holopad == src) // do not kill the current connection
continue
call_to_reject.Disconnect(src)
return TRUE
if("disk_eject")
if(disk && !replay_mode)
disk.forceMove(drop_location())
@@ -327,14 +361,13 @@ GLOBAL_LIST_EMPTY(network_holopads)
return TRUE
/**
* hangup_all_calls: Disconnects all current holocalls from the holopad
*/
* hangup_all_calls: Disconnects all current holocalls from the holopad
*/
/obj/machinery/holopad/proc/hangup_all_calls()
for(var/I in holo_calls)
var/datum/holocall/HC = I
HC.Disconnect(src)
//do not allow AIs to answer calls or people will use it to meta the AI sattelite
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
if (!istype(user))
return
@@ -343,12 +376,25 @@ GLOBAL_LIST_EMPTY(network_holopads)
/*There are pretty much only three ways to interact here.
I don't need to check for client since they're clicking on an object.
This may change in the future but for now will suffice.*/
if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already.
user.eyeobj.setLoc(get_turf(src))
else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
activate_holo(user)
else//If there is a hologram, remove it.
else//If there is a hologram, remove it, and jump to your last location.
clear_holo(user)
// if(user.lastloc)//only jump to your last location if your lastloc is set, which only sets if you projected from a request message.
// user.eyeobj.setLoc(user.lastloc)
// user.lastloc = null
/obj/machinery/holopad/AICtrlClick(mob/living/silicon/ai/user)
if (!istype(user))
return
if (!on_network)
return
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, then this button does nothing.
return
else//If there is a hologram, remove it, but dont jump to your last location.
// user.lastloc = null
clear_holo(user)
return
/obj/machinery/holopad/process()
if(LAZYLEN(masters))
@@ -378,25 +424,26 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(outgoing_call)
HC.Disconnect(src)//can't answer calls while calling
else
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
ringing = TRUE
update_icon()
update_appearance()
/obj/machinery/holopad/proc/activate_holo(mob/living/user)
var/mob/living/silicon/ai/AI = user
if(!istype(AI))
AI = null
if(is_operational() && (!AI || AI.eyeobj.loc == loc))//If the projector has power and client eye is on it
if (AI && istype(AI.current, /obj/machinery/holopad))
to_chat(user, "<span class='danger'>ERROR:</span> \black Image feed in progress.")
if(is_operational())//If the projector has power
if(AI && istype(AI.current, /obj/machinery/holopad))
to_chat(user, "[span_danger("ERROR:")] \black Image feed in progress.")
return
var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
if(AI)
Hologram.icon = AI.holo_icon
else //make it like real life
AI.eyeobj.setLoc(get_turf(src)) //ensure the AI camera moves to the holopad
else //make it like real life
Hologram.icon = user.icon
Hologram.icon_state = user.icon_state
Hologram.copy_overlays(user, TRUE)
@@ -407,17 +454,17 @@ GLOBAL_LIST_EMPTY(network_holopads)
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
Hologram.set_light(2) //hologram lighting
Hologram.set_light(2) //hologram lighting
move_hologram()
set_holo(user, Hologram)
visible_message("<span class='notice'>A holographic image of [user] flickers to life before your eyes!</span>")
visible_message(span_notice("A holographic image of [user] flickers to life before your eyes!"))
return Hologram
else
to_chat(user, "<span class='danger'>ERROR:</span> Unable to project hologram.")
to_chat(user, "[span_danger("ERROR:")] Unable to project hologram.")
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
@@ -430,10 +477,13 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
for(var/I in holo_calls)
var/datum/holocall/HC = I
if(HC.connected_holopad == src && speaker != HC.hologram)
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
if(HC.connected_holopad == src)
if(speaker == HC.hologram && HC.user.client?.prefs.chat_on_map)
HC.user.create_chat_message(speaker, message_language, raw_message, spans)
else
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
if(outgoing_call && speaker == outgoing_call.user)
if(outgoing_call?.hologram && speaker == outgoing_call.user)
outgoing_call.hologram.say(raw_message)
if(record_mode && speaker == record_user)
@@ -447,16 +497,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
set_light(2)
else
set_light(0)
update_icon()
update_appearance()
/obj/machinery/holopad/update_icon_state()
var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls)
if(ringing)
icon_state = "holopad_ringing"
else if(total_users || replay_mode)
icon_state = "holopad1"
else
icon_state = "holopad0"
icon_state = "[base_icon_state]_ringing"
return ..()
icon_state = "[base_icon_state][(total_users || replay_mode) ? 1 : 0]"
return ..()
/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h)
LAZYSET(masters, user, h)
@@ -488,7 +537,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner]
if(!h || h.HC) //Holocalls can't change source.
return FALSE
for(var/pad in GLOB.network_holopads)
for(var/pad in holopads)
var/obj/machinery/holopad/another = pad
if(another == src)
continue
@@ -524,7 +573,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
else
transfered = TRUE
//All is good.
holo.forceMove(new_turf)
holo.abstract_move(new_turf)
if(!transfered)
update_holoray(user,new_turf)
return TRUE
@@ -565,10 +614,10 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
holder.selected_language = record.language
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click.
Hologram.set_light(2) //hologram lighting
visible_message("<span class='notice'>A holographic image of [record.caller_name] flickers to life before your eyes!</span>")
Hologram.set_light(2) //hologram lighting
visible_message(span_notice("A holographic image of [record.caller_name] flickers to life before your eyes!"))
return Hologram
/obj/machinery/holopad/proc/replay_start()
@@ -661,7 +710,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
record_user = null
/obj/machinery/holopad/proc/record_clear()
if(disk && disk.record)
if(disk?.record)
QDEL_NULL(disk.record)
/obj/effect/overlay/holo_pad_hologram
@@ -673,6 +722,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
Impersonation = null
if(!QDELETED(HC))
HC.Disconnect(HC.calling_holopad)
HC = null
return ..()
/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0)
+3 -1
View File
@@ -52,6 +52,8 @@
var/author_ckey
var/icon_generated = FALSE
var/icon/generated_icon
///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
var/no_save = FALSE
// Painting overlay offset when framed
var/framed_offset_x = 11
@@ -370,7 +372,7 @@
update_icon()
/obj/structure/sign/painting/proc/save_persistent()
if(!persistence_id || !current_canvas)
if(!persistence_id || !current_canvas || current_canvas.no_save)
return
if(sanitize_filename(persistence_id) != persistence_id)
stack_trace("Invalid persistence_id - [persistence_id]")
+82 -44
View File
@@ -1,3 +1,5 @@
#define MAX_NOTICES 5
/obj/structure/noticeboard
name = "notice board"
desc = "A board for pinning important notices upon."
@@ -7,8 +9,25 @@
density = FALSE
anchored = TRUE
max_integrity = 150
/// Current number of a pinned notices
var/notices = 0
/obj/structure/noticeboard/directional/north
dir = SOUTH
pixel_y = 32
/obj/structure/noticeboard/directional/south
dir = NORTH
pixel_y = -32
/obj/structure/noticeboard/directional/east
dir = WEST
pixel_x = 32
/obj/structure/noticeboard/directional/west
dir = EAST
pixel_x = -32
/obj/structure/noticeboard/Initialize(mapload)
. = ..()
@@ -16,7 +35,7 @@
return
for(var/obj/item/I in loc)
if(notices > 4)
if(notices >= MAX_NOTICES)
break
if(istype(I, /obj/item/paper))
I.forceMove(src)
@@ -27,67 +46,84 @@
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/paper) || istype(O, /obj/item/photo))
if(!allowed(user))
to_chat(user, "<span class='info'>You are not authorized to add notices</span>")
to_chat(user, span_warning("You are not authorized to add notices!"))
return
if(notices < 5)
if(notices < MAX_NOTICES)
if(!user.transferItemToLoc(O, src))
return
notices++
icon_state = "nboard0[notices]"
to_chat(user, "<span class='notice'>You pin the [O] to the noticeboard.</span>")
to_chat(user, span_notice("You pin the [O] to the noticeboard."))
else
to_chat(user, "<span class='notice'>The notice board is full</span>")
to_chat(user, span_warning("The notice board is full!"))
else
return ..()
/obj/structure/noticeboard/interact(mob/user)
ui_interact(user)
/obj/structure/noticeboard/ui_state(mob/user)
return GLOB.physical_state
/obj/structure/noticeboard/ui_interact(mob/user)
/obj/structure/noticeboard/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "NoticeBoard", name)
ui.open()
/obj/structure/noticeboard/ui_data(mob/user)
var/list/data = list()
data["allowed"] = allowed(user)
data["items"] = list()
for(var/obj/item/content in contents)
var/list/content_data = list(
name = content.name,
ref = REF(content)
)
data["items"] += list(content_data)
return data
/obj/structure/noticeboard/ui_act(action, params)
. = ..()
var/auth = allowed(user)
var/dat = "<B>[name]</B><BR>"
for(var/obj/item/P in src)
if(istype(P, /obj/item/paper))
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];write=[REF(P)]'>Write</A> <A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
else
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
user << browse("<HEAD><TITLE>Notices</TITLE></HEAD>[dat]","window=noticeboard")
onclose(user, "noticeboard")
if(.)
return
/obj/structure/noticeboard/Topic(href, href_list)
..()
usr.set_machine(src)
if(href_list["remove"])
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
return
var/obj/item/I = locate(href_list["remove"]) in contents
if(istype(I) && I.loc == src)
I.forceMove(usr.loc)
usr.put_in_hands(I)
notices--
icon_state = "nboard0[notices]"
var/obj/item/item = locate(params["ref"]) in contents
if(!istype(item) || item.loc != src)
return
if(href_list["write"])
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
return
var/obj/item/P = locate(href_list["write"]) in contents
if(istype(P) && P.loc == src)
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/pen)
if(I)
add_fingerprint(usr)
P.attackby(I, usr)
var/mob/user = usr
switch(action)
if("examine")
if(istype(item, /obj/item/paper))
item.ui_interact(user)
else
to_chat(usr, "<span class='notice'>You'll need something to write with!</span>")
user.examinate(item)
return TRUE
if("remove")
if(!allowed(user))
return
remove_item(item, user)
return TRUE
if(href_list["read"])
var/obj/item/I = locate(href_list["read"]) in contents
if(istype(I) && I.loc == src)
usr.examinate(I)
/**
* Removes an item from the notice board
*
* Arguments:
* * item - The item that is to be removed
* * user - The mob that is trying to get the item removed, if there is one
*/
/obj/structure/noticeboard/proc/remove_item(obj/item/item, mob/user)
item.forceMove(drop_location())
if(user)
user.put_in_hands(item)
balloon_alert(user, "removed from board")
notices--
icon_state = "nboard0[notices]"
/obj/structure/noticeboard/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/metal (loc, 1)
new /obj/item/stack/sheet/iron (loc, 1)
for(var/obj/item/content in contents)
remove_item(content)
qdel(src)
// Notice boards for the heads of staff (plus the qm)
@@ -131,3 +167,5 @@
name = "Staff Notice Board"
desc = "Important notices from the heads of staff."
req_access = list(ACCESS_HEADS)
#undef MAX_NOTICES