Merge pull request #15317 from LetterN/what-should-i-name-this-branch

TGUI hardsync & stuff
This commit is contained in:
silicons
2022-01-25 00:06:59 -08:00
committed by GitHub
592 changed files with 24650 additions and 10686 deletions
+13 -3
View File
@@ -1291,9 +1291,7 @@
/obj/item/update_filters()
. = ..()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
update_action_buttons()
/atom/proc/get_filter(name)
if(filter_data && filter_data[name])
@@ -1404,3 +1402,15 @@
*/
/atom/proc/setClosed()
return
//Update the screentip to reflect what we're hoverin over
/atom/MouseEntered(location, control, params)
. = ..()
// Statusbar
// status_bar_set_text(usr, name)
// Screentips
// if(usr?.hud_used)
// if(!usr.client?.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1))
// usr.hud_used.screentip_text.maptext = ""
// else
// usr.hud_used.screentip_text.maptext = MAPTEXT("<span style='text-align: center'><span style='font-size: 32px'><span style='color:[usr.client.prefs.screentip_color]: 32px'>[name]</span>")
+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
+71 -38
View File
@@ -247,61 +247,94 @@ Class Procs:
/obj/machinery/proc/auto_use_power()
if(!powered(power_channel))
return 0
return FALSE
if(use_power == 1)
use_power(idle_power_usage,power_channel)
else if(use_power >= 2)
use_power(active_power_usage,power_channel)
return 1
return TRUE
/obj/machinery/proc/is_operational()
return !(stat & (NOPOWER|BROKEN|MAINT))
/obj/machinery/can_interact(mob/user)
var/silicon = hasSiliconAccessInArea(user) || IsAdminGhost(user)
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE))
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) // Check if the machine is broken, and if we can still interact with it if so
return FALSE
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
if(!silicon || !(interaction_flags_machine & INTERACT_MACHINE_OPEN_SILICON))
return FALSE
if(silicon)
if(IsAdminGhost(user))
return TRUE //if you're an admin, you probably know what you're doing (or at least have permission to do what you're doing)
if(!isliving(user))
return FALSE //no ghosts in the machine allowed, sorry
// if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE_INTERACT)
// return FALSE
var/mob/living/living_user = user
var/is_dextrous = FALSE
if(isanimal(user))
var/mob/living/simple_animal/user_as_animal = user
if (user_as_animal.dextrous)
is_dextrous = TRUE
if(!issilicon(user) && !is_dextrous && !user.can_hold_items())
return FALSE //spiders gtfo
if(issilicon(user)) // If we are a silicon, make sure the machine allows silicons to interact with it
if(!(interaction_flags_machine & INTERACT_MACHINE_ALLOW_SILICON))
return FALSE
else
if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON)
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN) && !(interaction_flags_machine & INTERACT_MACHINE_OPEN_SILICON))
return FALSE
if(!Adjacent(user))
var/mob/living/carbon/H = user
if(!(istype(H) && H.has_dna() && H.dna.check_mutation(TK)))
return FALSE
return TRUE
return TRUE //silicons don't care about petty mortal concerns like needing to be next to a machine to use it
if(living_user.incapacitated()) //idk why silicons aren't supposed to care about incapacitation when interacting with machines, but it was apparently like this before
return FALSE
// TODO: nerf blind people
// if((interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SIGHT) && user.is_blind())
// to_chat(user, span_warning("This machine requires sight to use."))
// return FALSE
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
return FALSE
if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON) //if the user was a silicon, we'd have returned out earlier, so the user must not be a silicon
return FALSE
if(!Adjacent(user)) // Next make sure we are next to the machine unless we have telekinesis
var/mob/living/carbon/carbon_user = living_user
if(!istype(carbon_user) || !carbon_user.has_dna() || !carbon_user.dna.check_mutation(TK))
return FALSE
return TRUE // If we passed all of those checks, woohoo! We can interact with this machine.
/obj/machinery/proc/check_nap_violations()
if(!SSeconomy.full_ancap)
return TRUE
if(occupant && !state_open)
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
var/obj/item/card/id/I = H.get_idcard()
if(I)
var/datum/bank_account/insurance = I.registered_account
if(!insurance)
say("[market_verb] NAP Violation: No bank account found.")
nap_violation()
return FALSE
else
if(!insurance.adjust_money(-fair_market_price))
say("[market_verb] NAP Violation: Unable to pay.")
nap_violation()
return FALSE
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(fair_market_price)
else
say("[market_verb] NAP Violation: No ID card found.")
nap_violation()
var/mob/living/L = occupant
var/obj/item/card/id/I = L.get_idcard(TRUE)
if(I)
var/datum/bank_account/insurance = I.registered_account
if(!insurance)
say("[market_verb] NAP Violation: No bank account found.")
nap_violation(L)
return FALSE
else
if(!insurance.adjust_money(-fair_market_price))
say("[market_verb] NAP Violation: Unable to pay.")
nap_violation(L)
return FALSE
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(fair_market_price)
else
say("[market_verb] NAP Violation: No ID card found.")
nap_violation(L)
return FALSE
return TRUE
/obj/machinery/proc/nap_violation(mob/violator)
@@ -322,11 +355,11 @@ Class Procs:
/obj/machinery/Topic(href, href_list)
..()
if(!can_interact(usr))
return 1
return TRUE
if(!usr.canUseTopic(src))
return 1
return TRUE
add_fingerprint(usr)
return 0
return FALSE
////////////////////////////////////////////////////////////////////////////////////////////
+1
View File
@@ -3,6 +3,7 @@
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
icon = 'icons/obj/vending.dmi'
icon_state = "colormate"
// light_mask = "colormate-light-mask"
density = TRUE
anchored = TRUE
circuit = /obj/item/circuitboard/machine/colormate
+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)
+271 -259
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,6 +7,7 @@
* since time_entered, which is world.time when the occupant moves in.
* ~ Zuhayr
*/
GLOBAL_LIST_EMPTY(cryopod_computers)
//Main cryopod console.
@@ -13,159 +16,113 @@
desc = "An interface between crew and the cryogenic storage oversight systems."
icon = 'icons/obj/Cryogenic2.dmi'
icon_state = "cellconsole_1"
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/list/stored_packages = list()
/// Does this console store items? if NOT, will dump all items when the user cryo's instead
var/allow_items = TRUE
var/storage_type = "crewmembers"
var/storage_name = "Cryogenic Oversight Control"
/obj/machinery/computer/cryopod/deconstruct()
. = ..()
for(var/i in stored_packages)
var/obj/O = i
O.forceMove(drop_location())
/obj/machinery/computer/cryopod/attack_ai()
attack_hand()
/obj/machinery/computer/cryopod/Initialize()
. = ..()
GLOB.cryopod_computers += src
/obj/machinery/computer/cryopod/ui_interact(mob/user = usr)
if(!is_operational())
/obj/machinery/computer/cryopod/Destroy()
GLOB.cryopod_computers -= src
return ..()
/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
var/list/item_meta = list()
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/>"
for(var/obj/item/storage/box/blue/cryostorage_items/O as anything in stored_packages)
item_meta += list(list("name" = O.real_name, "ref" = REF(O))) // i am truely livid about byond lists
data["item_meta"] = item_meta
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
popup.set_content(dat)
popup.open()
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 // i do not know why but this uses budget?
/obj/machinery/computer/cryopod/Topic(href, href_list)
return data
/obj/machinery/computer/cryopod/ui_act(action, params)
if(..())
return TRUE
return
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>")
if(action == "item")
if(!allowed(usr) && !(obj_flags & EMAGGED))
to_chat(usr, "<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>")
to_chat(usr, "<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 = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in stored_packages
var/obj/I = locate(params["item"])
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>")
to_chat(usr, "<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)
if(usr && Adjacent(usr) && usr.can_hold_items())
usr.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(severity, target, origin)
return
/// 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."
@@ -178,47 +135,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 can cryo other people in pods.
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()
@@ -231,8 +196,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)
@@ -243,36 +208,122 @@
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()
crew_member["name"] = mob_occupant.real_name
if(mob_occupant.mind)
// 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/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)
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_notice("[src] hums and hisses as it moves [mob_occupant.real_name] into storage."))
/* ============================= */
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/drop_to_ground = !istype(control_computer, /obj/machinery/computer/cryopod) || !control_computer.allow_items
var/mind_identity = mob_occupant.mind?.name
var/occupant_identity = mob_occupant.real_name
@@ -293,7 +344,6 @@
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)
@@ -303,145 +353,89 @@
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
for(var/obj/item/item_content as anything in gear)
if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP))
destroying += item_content
continue
if(I.item_flags & (DROPDEL | ABSTRACT))
destroying += I
continue
if(HAS_TRAIT(I, TRAIT_NODROP))
destroying += I
if(item_content.item_flags & (DROPDEL | ABSTRACT))
destroying += item_content
continue
// destroying stays in mob for a bit
item_content.forceMove(src)
// WEE WOO SNOWFLAKE TIME
if(istype(I, /obj/item/pda))
var/obj/item/pda/P = I
if(istype(item_content, /obj/item/pda))
var/obj/item/pda/P = item_content
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
else if(istype(item_content, /obj/item/card/id))
var/obj/item/card/id/idcard = item_content
if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
destroying += idcard
else
storing += idcard
else
storing += I
storing += item_content
// 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
var/obj/item/storage/box/blue/cryostorage_items/O = new /obj/item/storage/box/blue/cryostorage_items
O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
O.real_name = 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)
O.forceMove(drop_to_ground ? control_computer.drop_location() : control_computer)
if((control_computer == 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)
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)
mob_occupant.mind.special_role = null
// 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/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]"
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.
// they already did ghost verb
var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE)
if(G)
G.voluntary_ghosted = TRUE
// they did not ghost verb
else
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
QDEL_LIST(destroying)
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(user != target && target.client)
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(alert(target,"Would you like to enter cryosleep?",,"Yes","No") == "No")
else if(target.client) // mob has client
if(tgalert(target, "Would you like to enter cryosleep?", "Enter Cryopod?", "Yes", "No") != "Yes")
return
var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!"
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 && world.time - target.client.cryo_warned > 5 MINUTES)
var/list/caught_string
var/addendum = ""
if(target.mind.assigned_role in GLOB.command_positions)
@@ -459,30 +453,48 @@
LAZYADD(caught_string, "Revolutionary")
if(caught_string)
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
/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
var/real_name = "fire coderbus"
+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)
+74 -43
View File
@@ -46,11 +46,23 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
max_integrity = 200
obj_flags = NONE
///Item flags for the item
var/item_flags = NONE
var/hitsound = null
var/usesound = null
var/throwhitsound = null
///Sound played when you hit something with the item
var/hitsound
///Played when the item is used, for example tools
var/usesound
///Used when yate into a mob
var/mob_throw_hit_sound
///Sound used when equipping the item into a valid slot
var/equip_sound
///Sound uses when picking the item up (into your hands)
var/pickup_sound
///Sound uses when dropping the item, or when its thrown.
var/drop_sound
///Whether or not we use stealthy audio levels for this item's attack sounds
var/stealthy_audio = FALSE
/// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it.
var/w_class = WEIGHT_CLASS_NORMAL
@@ -129,7 +141,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/datum/dog_fashion/dog_fashion = null
//Tooltip vars
var/force_string //string form of an item's force. Edit this var only to set a custom force string
///string form of an item's force. Edit this var only to set a custom force string
var/force_string
var/last_force_string_check = 0
var/trigger_guard = TRIGGER_GUARD_NONE
@@ -388,7 +401,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
return
remove_outline()
. = FALSE
pickup(user)
add_fingerprint(user)
if(!user.put_in_active_hand(src, FALSE, FALSE))
@@ -461,9 +474,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
item_flags &= ~(IN_INVENTORY)
item_flags &= ~(IN_STORAGE)
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
remove_outline()
// if(!silent)
// playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
if(!silent)
playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
user?.update_equipment_speed_mods()
// called just as an item is picked up (loc is not yet changed)
@@ -710,8 +722,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0)
if (throwhitsound)
playsound(hit_atom, throwhitsound, volume, TRUE, -1)
if (mob_throw_hit_sound)
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
@@ -719,8 +731,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
// else
// playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
else if (drop_sound)
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
@@ -894,47 +906,55 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
openToolTip(user,src,params,title = name,content = "[desc]<br><b>Force:</b> [force_string]",theme = "")
/obj/item/MouseEntered(location, control, params)
. = ..()
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr?.client.prefs.enable_tips && !QDELETED(src))
var/timedelay = max(usr.client.prefs.tip_delay * 0.01, 0.01) // I heard multiplying is faster, also runtimes from very low/negative numbers
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
var/mob/living/L = usr
if(istype(L) && (L.incapacitated() || (current_equipped_slot in L.check_obscured_slots()) || !L.canUnEquip(src)))
apply_outline(_size = 3)
else
apply_outline()
if(get(src, /mob) == usr && !QDELETED(src))
var/mob/living/L = usr
if(usr.client.prefs.enable_tips)
var/timedelay = usr.client.prefs.tip_delay/100
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
if(usr.client.prefs.outline_enabled)
if(istype(L) && L.incapacitated())
apply_outline(COLOR_RED_GRAY) //if they're dead or handcuffed, let's show the outline as red to indicate that they can't interact with that right now
else
apply_outline(usr.client.prefs.outline_color) //if the player's alive and well we send the command with no color set, so it uses the theme's color
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
remove_outline()
remove_filter("hover_outline") //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
/obj/item/MouseExited(location,control,params)
/obj/item/MouseExited(location, control, params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
deltimer(usr.client.tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
remove_outline()
remove_filter("hover_outline")
/obj/item/proc/apply_outline(colour = null, _size=1)
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
/obj/item/proc/apply_outline(outline_color = null)
if(get(src, /mob) != usr || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
return
if(usr.client)
if(!usr.client.prefs.outline_enabled)
return
if(!colour)
if(usr.client)
colour = usr.client.prefs.outline_color
if(!colour)
colour = COLOR_BLUE_GRAY
else
colour = COLOR_BLUE_GRAY
if(outline_filter)
filters -= outline_filter
outline_filter = filter(type="outline", size=_size, color=colour)
filters += outline_filter
var/theme = lowertext(usr.client.prefs.UI_style)
if(!outline_color) //if we weren't provided with a color, take the theme's color
switch(theme) //yeah it kinda has to be this way
if("midnight")
outline_color = COLOR_THEME_MIDNIGHT
if("plasmafire")
outline_color = COLOR_THEME_PLASMAFIRE
if("retro")
outline_color = COLOR_THEME_RETRO //just as garish as the rest of this theme
if("slimecore")
outline_color = COLOR_THEME_SLIMECORE
if("operative")
outline_color = COLOR_THEME_OPERATIVE
if("clockwork")
outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
if("glass")
outline_color = COLOR_THEME_GLASS
else //this should never happen, hopefully
outline_color = COLOR_WHITE
if(color)
outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
/obj/item/proc/remove_outline()
if(outline_filter)
filters -= outline_filter
outline_filter = null
add_filter("hover_outline", 1, list("type" = "outline", "size" = 1, "color" = outline_color))
// Called when a mob tries to use the item as a tool.
// Handles most checks.
@@ -1216,3 +1236,14 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
return TRUE
/**
* Updates all action buttons associated with this item
*
* Arguments:
* * status_only - Update only current availability status of the buttons to show if they are ready or not to use
* * force - Force buttons update even if the given button icon state has not changed
*/
/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
for(var/datum/action/current_action as anything in actions)
current_action.UpdateButtonIcon(status_only, force)
@@ -19,6 +19,7 @@
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_BELT
rad_flags = RAD_NO_CONTAMINATE
item_flags = NOBLUDGEON
custom_materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
var/grace = RAD_GRACE_PERIOD
@@ -35,17 +36,15 @@
. = ..()
START_PROCESSING(SSobj, src)
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
update_icon()
update_sound()
/obj/item/geiger_counter/process(delta_time)
if(!scanning)
current_tick_amount = 0
return
@@ -64,49 +63,55 @@
current_tick_amount = 0
update_appearance()
update_sound()
/obj/item/geiger_counter/examine(mob/user)
. = ..()
if(!scanning)
return
. += "<span class='info'>Alt-click it to clear stored radiation levels.</span>"
. += span_info("Alt-click it to clear stored radiation levels.")
if(obj_flags & EMAGGED)
. += "<span class='warning'>The display seems to be incomprehensible.</span>"
. += span_warning("The display seems to be incomprehensible.")
return
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
. += "<span class='notice'>Ambient radiation level count reports that all is well.</span>"
. += span_notice("Ambient radiation level count reports that all is well.")
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
. += "<span class='disarm'>Ambient radiation levels slightly above average.</span>"
. += span_alert("Ambient radiation levels slightly above average.")
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
. += "<span class='warning'>Ambient radiation levels above average.</span>"
. += span_warning("Ambient radiation levels above average.")
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
. += "<span class='danger'>Ambient radiation levels highly above average.</span>"
. += span_danger("Ambient radiation levels highly above average.")
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
. += "<span class='suicide'>Ambient radiation levels nearing critical level.</span>"
. += span_suicide("Ambient radiation levels nearing critical level.")
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
. += "<span class='boldannounce'>Ambient radiation levels above critical level!</span>"
. += span_boldannounce("Ambient radiation levels above critical level!")
. += "<span class='notice'>The last radiation amount detected was [last_tick_amount]</span>"
. += span_notice("The last radiation amount detected was [last_tick_amount]")
/obj/item/geiger_counter/update_icon_state()
if(!scanning)
icon_state = "geiger_off"
else if(obj_flags & EMAGGED)
return ..()
if(obj_flags & EMAGGED)
icon_state = "geiger_on_emag"
else
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
/obj/item/geiger_counter/proc/update_sound()
var/datum/looping_sound/geiger/loop = soundloop
@@ -124,21 +129,21 @@
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
return
current_tick_amount += amount
update_icon()
update_appearance()
/obj/item/geiger_counter/attack_self(mob/user)
scanning = !scanning
update_icon()
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
update_appearance()
to_chat(user, span_notice("[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src]."))
/obj/item/geiger_counter/afterattack(atom/target, mob/user)
/obj/item/geiger_counter/afterattack(atom/target, mob/living/user)
. = ..()
if(user.a_intent == INTENT_HELP)
if(!(obj_flags & EMAGGED))
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='notice'>You scan [target]'s radiation levels with [src]...</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_notice("You scan [target]'s radiation levels with [src]..."))
addtimer(CALLBACK(src, .proc/scan, target, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
else
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='danger'>You project [src]'s stored radiation into [target]!</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_danger("You project [src]'s stored radiation into [target]!"))
target.rad_act(radiation_count)
radiation_count = 0
return TRUE
@@ -156,57 +161,61 @@
if(isliving(A))
var/mob/living/M = A
if(!M.radiation)
to_chat(user, "<span class='notice'>[icon2html(src, user)] Radiation levels within normal boundaries.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Radiation levels within normal boundaries."))
else
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation]."))
if(rad_strength)
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]"))
else
to_chat(user, "<span class='notice'>[icon2html(src, user)] Target is free of radioactive contamination.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Target is free of radioactive contamination."))
/obj/item/geiger_counter/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED))
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
return 0
user.visible_message("<span class='notice'>[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...</span>", "<span class='notice'>You begin resetting [src]...</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return FALSE
user.visible_message(span_notice("[user] unscrews [src]'s maintenance panel and begins fiddling with its innards..."), span_notice("You begin resetting [src]..."))
if(!I.use_tool(src, user, 40, volume=50))
return 0
user.visible_message("<span class='notice'>[user] refastens [src]'s maintenance panel!</span>", "<span class='notice'>You reset [src] to its factory settings!</span>")
return FALSE
user.visible_message(span_notice("[user] refastens [src]'s maintenance panel!"), span_notice("You reset [src] to its factory settings!"))
obj_flags &= ~EMAGGED
radiation_count = 0
update_icon()
return 1
update_appearance()
return TRUE
else
return ..()
/obj/item/geiger_counter/AltClick(mob/living/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
return ..()
if(!scanning)
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
return TRUE
to_chat(usr, span_warning("[src] must be on to reset its radiation level!"))
return
radiation_count = 0
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
update_icon()
return TRUE
to_chat(usr, span_notice("You flush [src]'s radiation counts, resetting it to normal."))
update_appearance()
/obj/item/geiger_counter/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return
to_chat(user, "<span class='warning'>You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.</span>")
to_chat(user, span_warning("You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan."))
obj_flags |= EMAGGED
return TRUE
/obj/item/geiger_counter/cyborg
var/mob/listeningTo
/obj/item/geiger_counter/cyborg/cyborg_unequip(mob/user)
if(!scanning)
return
scanning = FALSE
update_appearance()
/obj/item/geiger_counter/cyborg/equipped(mob/user)
. = ..()
if(listeningTo == user)
@@ -217,6 +226,7 @@
listeningTo = user
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
SIGNAL_HANDLER
rad_act(amount)
/obj/item/geiger_counter/cyborg/dropped(mob/user)
@@ -24,8 +24,8 @@
/obj/item/reverse_bear_trap/Initialize()
. = ..()
soundloop = new(list(src))
soundloop2 = new(list(src))
soundloop = new(src)
soundloop2 = new(src)
/obj/item/reverse_bear_trap/Destroy()
QDEL_NULL(soundloop)
@@ -33,16 +33,16 @@
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/item/reverse_bear_trap/process()
/obj/item/reverse_bear_trap/process(delta_time)
if(!ticking)
return
time_left--
time_left -= delta_time
soundloop2.mid_length = max(0.5, time_left - 5) //beepbeepbeepbeepbeep
if(!time_left || !isliving(loc))
if(time_left <= 0 || !isliving(loc))
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
soundloop.stop()
soundloop2.stop()
to_chat(loc, "<span class='userdanger'>*ding*</span>")
to_chat(loc, span_userdanger("*ding*"))
addtimer(CALLBACK(src, .proc/snap), 2)
/obj/item/reverse_bear_trap/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
@@ -116,13 +116,22 @@
/obj/item/reverse_bear_trap/proc/reset()
ticking = FALSE
update_appearance(UPDATE_OVERLAYS)
REMOVE_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
soundloop.stop()
soundloop2.stop()
STOP_PROCESSING(SSprocessing, src)
/obj/item/reverse_bear_trap/update_overlays()
. = ..()
if(ticking != TRUE)
return
/// note: this timer overlay increments one frame every second (to simulate a clock ticking). If you want to instead have it do a full cycle in a minute, set the 'delay' of each frame of the icon overlay to 75 rather than 10, and the worn overlay to twice that.
// . += "rbt_ticking"
/obj/item/reverse_bear_trap/proc/arm() //hulen
ticking = TRUE
update_appearance(UPDATE_OVERLAYS)
escape_chance = initial(escape_chance) //we keep these vars until re-arm, for tracking purposes
time_left = initial(time_left)
ADD_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
@@ -20,4 +20,7 @@
imp_type = /obj/item/implant/uplink/precharged
/obj/item/implant/uplink/precharged
starting_tc = 10
starting_tc = TELECRYSTALS_PRELOADED_IMPLANT
/obj/item/implant/uplink/starting
starting_tc = TELECRYSTALS_DEFAULT - UPLINK_IMPLANT_TELECRYSTAL_COST
+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]")
@@ -25,7 +25,7 @@
/obj/machinery/manned_turret/Destroy()
target = null
target_turf = null
..()
return ..()
//BUCKLE HOOKS
+81 -43
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)
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
+1 -1
View File
@@ -257,7 +257,7 @@
/obj/machinery/shower/Initialize()
. = ..()
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/machinery/shower/Destroy()
QDEL_NULL(soundloop)
@@ -34,11 +34,11 @@
/turf/open/floor/plating/catwalk_floor/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
covered = !covered
to_chat(user, span_notice("[!covered ? "You removed the cover!" : "You added the cover!"]"))
user.balloon_alert(user, "[!covered ? "cover removed" : "cover added"]")
update_icon(UPDATE_OVERLAYS)
/turf/open/floor/plating/catwalk_floor/pry_tile(obj/item/crowbar, mob/user, silent)
if(covered)
to_chat(user, span_notice("You need to remove the cover first!"))
user.balloon_alert(user, "remove cover first!")
return FALSE
. = ..()
+1
View File
@@ -111,6 +111,7 @@ GLOBAL_LIST(topic_status_cache)
GLOB.picture_log_directory = "data/picture_logs/[override_dir]"
GLOB.world_game_log = "[GLOB.log_directory]/game.log"
GLOB.world_suspicious_login_log = "[GLOB.log_directory]/suspicious_logins.log"
GLOB.world_virus_log = "[GLOB.log_directory]/virus.log"
GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
GLOB.world_attack_log = "[GLOB.log_directory]/attack.log"