diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index de20ccd1a4c..0c7e32a0a76 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -3,6 +3,7 @@ #define GAS_PL (1 << 2) #define GAS_CO2 (1 << 3) #define GAS_N2O (1 << 4) +#define GAS_A_B (1 << 5) //ATMOS //stuff you should probably leave well alone! diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 26b1d2d44aa..4b10bee2a2b 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -198,6 +198,8 @@ // /area signals +///from base of area/proc/power_change(): () +#define COMSIG_AREA_POWER_CHANGE "area_power_change" ///from base of area/Entered(): (atom/movable/M) #define COMSIG_AREA_ENTERED "area_entered" ///from base of area/Exited(): (atom/movable/M) diff --git a/code/controllers/subsystem/tickets/mentor_tickets.dm b/code/controllers/subsystem/tickets/mentor_tickets.dm index 97f84263a45..706a498aa64 100644 --- a/code/controllers/subsystem/tickets/mentor_tickets.dm +++ b/code/controllers/subsystem/tickets/mentor_tickets.dm @@ -10,6 +10,9 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets ticket_system_name = "Mentor Tickets" ticket_name = "Mentor Ticket" span_class = "mentorhelp" + anchor_link_extra = ";is_mhelp=1" + ticket_help_type = "Mentorhelp" + ticket_help_span = "mentorhelp" other_ticket_name = "Admin" other_ticket_permission = R_ADMIN close_rights = R_MENTOR | R_ADMIN @@ -21,8 +24,11 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets "Your [ticket_name] has now been closed.") return ..() -/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg) - message_mentorTicket(msg) +/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg, prefix_type = NONE, important = FALSE) + message_mentorTicket(msg, important) /datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T) - SStickets.newTicket(get_client_by_ckey(T.client_ckey), T.content, T.title) + SStickets.newTicket(get_client_by_ckey(T.client_ckey), T.content, T.raw_title) + +/datum/controller/subsystem/tickets/mentor_tickets/autoRespond(N) + return diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm index 60f3a5fa8dd..30c4c13166c 100644 --- a/code/controllers/subsystem/tickets/tickets.dm +++ b/code/controllers/subsystem/tickets/tickets.dm @@ -10,6 +10,9 @@ #define TICKET_RESOLVED 3 #define TICKET_STALE 4 +#define TICKET_STAFF_MESSAGE_ADMIN_CHANNEL 1 +#define TICKET_STAFF_MESSAGE_PREFIX 2 + SUBSYSTEM_DEF(tickets) name = "Admin Tickets" init_order = INIT_ORDER_TICKETS @@ -24,6 +27,11 @@ SUBSYSTEM_DEF(tickets) var/close_rights = R_ADMIN var/rights_needed = R_ADMIN | R_MOD + /// Text that will be added to the anchor link + var/anchor_link_extra = "" + + var/ticket_help_type = "Adminhelp" + var/ticket_help_span = "adminhelp" /// The name of the other ticket type to convert to var/other_ticket_name = "Mentor" /// Which permission to look for when seeing if there is staff available for the other ticket type @@ -34,7 +42,8 @@ SUBSYSTEM_DEF(tickets) var/ticketCounter = 1 /datum/controller/subsystem/tickets/Initialize() - close_messages = list("- [ticket_name] Rejected! -", + if(!close_messages) + close_messages = list("- [ticket_name] Rejected! -", "Please try to be calm, clear, and descriptive in admin helps, do not assume the staff member has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.", "Your [ticket_name] has now been closed.") return ..() @@ -76,22 +85,56 @@ SUBSYSTEM_DEF(tickets) var/datum/ticket/T = i resolveTicket(T.ticketNum) +/** + * Will either make a new ticket using the given text or will add the text to an existing ticket. + * Staff will get a message + * Arguments: + * C - The client who requests help + * text - The text the client send + */ +/datum/controller/subsystem/tickets/proc/newHelpRequest(client/C, text) + var/ticketNum // Holder for the ticket number + var/datum/ticket/T + // Get the open ticket assigned to the client and add a response. If no open tickets then make a new one + if((T = checkForOpenTicket(C))) + ticketNum = T.ticketNum + T.addResponse(C, text) + T.setCooldownPeriod() + to_chat(C.mob, "Your [ticket_name] #[ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.") + var/url_message = makeUrlMessage(C, text, ticketNum) + message_staff(url_message, NONE, TRUE) + else + newTicket(C, text, text) + +/** + * Will add the URLs usable by staff to the message and return it + * Arguments: + * C - The client who send the message + * msg - The raw message + * ticketNum - Which ticket number the ticket has + */ +/datum/controller/subsystem/tickets/proc/makeUrlMessage(client/C, msg, ticketNum) + var/list/L = list() + L += "[ticket_help_type]: [key_name(C, TRUE, ticket_help_type)] " + L += "([ADMIN_QUE(C.mob,"?")]) ([ADMIN_PP(C.mob,"PP")]) ([ADMIN_VV(C.mob,"VV")]) ([ADMIN_TP(C.mob,"TP")]) ([ADMIN_SM(C.mob,"SM")]) " + L += "([admin_jump_link(C.mob)]) (TICKET) " + L += "[isAI(C.mob) ? "(CL)" : ""] (TAKE) " + L += "(RESOLVE) (AUTO) " + L += "(CONVERT) : [msg]" + return L.Join() + //Open a new ticket and populate details then add to the list of open tickets /datum/controller/subsystem/tickets/proc/newTicket(client/C, passedContent, title) if(!C || !passedContent) return - //Check if the user has an open ticket already within the cooldown period, if so we don't create a new one and re-set the cooldown period - var/datum/ticket/existingTicket = checkForOpenTicket(C) - if(existingTicket) - existingTicket.setCooldownPeriod() - to_chat(C.mob, "Your [ticket_name] #[existingTicket.ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.") - return - if(!title) title = passedContent - var/datum/ticket/T = new(title, passedContent, getTicketCounterAndInc()) + var/new_ticket_num = getTicketCounterAndInc() + var/url_title = makeUrlMessage(C, title, new_ticket_num) + + var/datum/ticket/T = new(url_title, title, passedContent, new_ticket_num) allTickets += T T.client_ckey = C.ckey T.locationSent = C.mob.loc.name @@ -102,6 +145,8 @@ SUBSYSTEM_DEF(tickets) var/ticket_open_sound = sound('sound/effects/adminticketopen.ogg') SEND_SOUND(C, ticket_open_sound) + message_staff(url_title, NONE, TRUE) + //Set ticket state with key N to open /datum/controller/subsystem/tickets/proc/openTicket(N) var/datum/ticket/T = allTickets[N] @@ -148,7 +193,7 @@ SUBSYSTEM_DEF(tickets) /datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T) var/client/C = get_client_by_ckey(T.client_ckey) - SSmentor_tickets.newTicket(C, T.content, T.title) + SSmentor_tickets.newTicket(C, T.content, T.raw_title) /datum/controller/subsystem/tickets/proc/autoRespond(N) if(!check_rights(rights_needed)) @@ -250,6 +295,7 @@ SUBSYSTEM_DEF(tickets) var/client_ckey var/timeOpened // Time the ticket was opened var/title //The initial message with links + var/raw_title // The title without URLs added var/list/content // content of the staff help var/lastStaffResponse // Last staff member who responded var/lastResponseTime // When the staff last responded @@ -260,8 +306,9 @@ SUBSYSTEM_DEF(tickets) var/ticketCooldown // Cooldown before allowing the user to open another ticket. var/client/staffAssigned // Staff member who has assigned themselves to this ticket -/datum/ticket/New(tit, cont, num) +/datum/ticket/New(tit, raw_tit, cont, num) title = tit + raw_title = raw_tit content = list() content += cont timeOpened = worldtime2text() @@ -440,9 +487,21 @@ UI STUFF to_chat(target, text) return TRUE -//Sends a message to the designated staff -/datum/controller/subsystem/tickets/proc/message_staff(var/msg, var/alt = FALSE) - message_adminTicket(msg, alt) +/** + * Sends a message to the designated staff + * Arguments: + * msg - The message being send + * alt - If an alternative prefix should be used or not. Defaults to TICKET_STAFF_MESSAGE_PREFIX + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/datum/controller/subsystem/tickets/proc/message_staff(msg, prefix_type = TICKET_STAFF_MESSAGE_PREFIX, important = FALSE) + switch(prefix_type) + if(TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) + msg = "ADMIN TICKET: [msg]" + if(TICKET_STAFF_MESSAGE_PREFIX) + msg = "ADMIN TICKET: [msg]" + message_adminTicket(msg, important) /datum/controller/subsystem/tickets/Topic(href, href_list) @@ -524,7 +583,7 @@ UI STUFF if(span_class == "mentorhelp") message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]") else - message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]", TRUE) + message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]", TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) to_chat_safe(returnClient(index), "Your [ticket_name] is being handled by [usr.client].") /datum/controller/subsystem/tickets/proc/unassignTicket(index) @@ -535,4 +594,7 @@ UI STUFF if(span_class == "mentorhelp") message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]") else - message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]", TRUE) + message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]", TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) + +#undef TICKET_STAFF_MESSAGE_ADMIN_CHANNEL +#undef TICKET_STAFF_MESSAGE_PREFIX diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index e4268c16cd5..1042d9a8ddd 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -35,12 +35,18 @@ var/rank = t.fields["rank"] var/real_rank = t.fields["real_rank"] if(OOC) - var/active = 0 - for(var/mob/M in GLOB.player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) - active = 1 + var/activetext = "Inactive" + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.real_name != name) + continue + if(H.client && H.client.inactivity <= 6000) + activetext = "Active" break - isactive[name] = active ? "Active" : "Inactive" + if(isLivingSSD(H)) + activetext = "SSD" + break + isactive[name] = activetext else isactive[name] = t.fields["p_stat"] var/department = 0 diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 990e2f1694d..aec3ea58f01 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -420,11 +420,15 @@ /area/space/powered(chan) //Nope.avi return 0 -// called when power status changes - +/** + * Called when the area power status changes + * + * Updates the area icon, calls power change on all machines in the area, and sends the `COMSIG_AREA_POWER_CHANGE` signal. + */ /area/proc/power_change() for(var/obj/machinery/M in src) // for each machine in the area M.power_change() // reverify power status (to update icons etc.) + SEND_SIGNAL(src, COMSIG_AREA_POWER_CHANGE) updateicon() /area/proc/usage(var/chan) diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index a20b0e1654b..c7ce0261bd0 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -43,7 +43,7 @@ protected_jobs = list("Captain") /datum/theft_objective/hoslaser - name = "the head of security's recreated antique laser gun" + name = "the head of security's X-01 multiphase energy gun" typepath = /obj/item/gun/energy/gun/hos protected_jobs = list("Head Of Security") diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 8b9e5e92319..41b3e705f36 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -26,7 +26,7 @@ /obj/effect/anomaly/proc/anomalyEffect() if(prob(50)) - step(src,pick(GLOB.alldirs)) + step(src, pick(GLOB.alldirs)) /obj/effect/anomaly/proc/anomalyNeutralize() @@ -183,3 +183,30 @@ if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) T.ex_act(ex_act_force) return + +///////////////////// + +/obj/effect/anomaly/atmos + name = "transformative gas anomaly" + icon_state = "electricity2" + var/gas_type + +/obj/effect/anomaly/atmos/New() + ..() + gas_type = pick(GAS_N2O, GAS_CO2, GAS_N2) + aSignal.origin_tech = "materials=7" //turning gas into another gas has some interesting implications for material science + //might also work as biotech maybe? Since there's no anomaly for that. + +/obj/effect/anomaly/atmos/anomalyEffect() + ..() + var/turf/simulated/T = get_turf(src) + if(istype(T)) + var/flag + switch(gas_type) + if(GAS_CO2) + flag = LINDA_SPAWN_CO2 + if(GAS_N2O) + flag = LINDA_SPAWN_N2O + else + flag = LINDA_SPAWN_NITROGEN + T.atmos_spawn_air(LINDA_SPAWN_20C | flag, 10) diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index f9ba60ab380..d1d4e900543 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -6,9 +6,7 @@ w_class = WEIGHT_CLASS_BULKY canhear_range = 2 flags = CONDUCT - var/number = 0 var/circuitry_installed = 1 - var/last_tick //used to delay the powercheck var/buildstage = 0 dog_fashion = null @@ -50,7 +48,7 @@ . = ..() buildstage = building if(buildstage) - START_PROCESSING(SSobj, src) + update_operating_status() else if(direction) setDir(direction) @@ -103,20 +101,17 @@ ) /obj/item/radio/intercom/Destroy() - STOP_PROCESSING(SSobj, src) GLOB.global_intercoms.Remove(src) return ..() /obj/item/radio/intercom/attack_ai(mob/user) add_hiddenprint(user) add_fingerprint(user) - spawn(0) - attack_self(user) + attack_self(user) /obj/item/radio/intercom/attack_hand(mob/user) add_fingerprint(user) - spawn(0) - attack_self(user) + attack_self(user) /obj/item/radio/intercom/receive_range(freq, level) if(!is_listening()) @@ -132,7 +127,7 @@ return canhear_range -/obj/item/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) +/obj/item/radio/intercom/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/stack/tape_roll)) //eww return else if(iscoil(W) && buildstage == 1) @@ -182,7 +177,7 @@ buildstage = 3 to_chat(user, "You secure the electronics!") update_icon() - START_PROCESSING(SSobj, src) + update_operating_status() for(var/i, i<= 5, i++) wires.on_cut(i, 1) @@ -198,7 +193,7 @@ b_stat = 1 buildstage = 1 update_icon() - STOP_PROCESSING(SSobj, src) + update_operating_status(FALSE) /obj/item/radio/intercom/welder_act(mob/user, obj/item/I) if(buildstage != 0) @@ -218,20 +213,30 @@ return icon_state = "intercom[!on?"-p":""][b_stat ? "-open":""]" -/obj/item/radio/intercom/process() - if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) - last_tick = world.timeofday +/obj/item/radio/intercom/proc/update_operating_status(on = TRUE) + var/area/current_area = get_area(src) + if(!current_area) + return + if(on) + RegisterSignal(current_area, COMSIG_AREA_POWER_CHANGE, .proc/AreaPowerCheck) + else + UnregisterSignal(current_area, COMSIG_AREA_POWER_CHANGE) - - if(!src.loc) - on = 0 - else - var/area/A = get_area(src) - if(!A) - on = 0 - else - on = A.powered(EQUIP) // set "on" to the power status - update_icon() +/** + * Proc called whenever the intercom's area loses or gains power. Responsible for setting the `on` variable and calling `update_icon()`. + * + * Normally called after the intercom's area recieves the `COMSIG_AREA_POWER_CHANGE` signal, but it can also be called directly. + * Arguments: + * + * source - the area that just had a power change. + */ +/obj/item/radio/intercom/proc/AreaPowerCheck(datum/source) + var/area/current_area = get_area(src) + if(!current_area) + on = FALSE + else + on = current_area.powered(EQUIP) // set "on" to the equipment power status of our area. + update_icon() /obj/item/intercom_electronics name = "intercom electronics" diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 1e34561c441..62716bc51c4 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -143,8 +143,8 @@ AI MODULES target.set_zeroth_law(law) GLOB.lawchanges.Add("The law specified [targetName]") else - to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite - to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") + to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite + to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") GLOB.lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overridden.") /******************** ProtectStation ********************/ @@ -225,7 +225,7 @@ AI MODULES target.laws.clear_supplied_laws() target.laws.clear_ion_laws() - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") target.show_laws() /******************** Purge ********************/ @@ -238,7 +238,7 @@ AI MODULES ..() if(!is_special_character(target)) target.clear_zeroth_law() - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") target.clear_supplied_laws() target.clear_ion_laws() target.clear_inherent_laws() diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index b5bfc01ea79..d6667a32b3a 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -20,22 +20,39 @@ GLOBAL_VAR_INIT(nologevent, 0) if(C.prefs.atklog <= loglevel) to_chat(C, msg) - -/proc/message_adminTicket(var/msg, var/alt = FALSE) - if(alt) - msg = "ADMIN TICKET: [msg]" - else - msg = "ADMIN TICKET: [msg]" +/** + * Sends a message to the staff able to see admin tickets + * Arguments: + * msg - The message being send + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/proc/message_adminTicket(msg, important = FALSE) for(var/client/C in GLOB.admins) if(R_ADMIN & C.holder.rights) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS)) + if(important || (C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS))) to_chat(C, msg) + if(important) + if(C.prefs?.sound & SOUND_ADMINHELP) + SEND_SOUND(C, 'sound/effects/adminhelp.ogg') + window_flash(C) -/proc/message_mentorTicket(var/msg) +/** + * Sends a message to the staff able to see mentor tickets + * Arguments: + * msg - The message being send + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/proc/message_mentorTicket(msg, important = FALSE) for(var/client/C in GLOB.admins) if(check_rights(R_ADMIN | R_MENTOR | R_MOD, 0, C.mob)) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS)) + if(important || (C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS))) to_chat(C, msg) + if(important) + if(C.prefs?.sound & SOUND_MENTORHELP) + SEND_SOUND(C, 'sound/effects/adminhelp.ogg') + window_flash(C) /proc/admin_ban_mobsearch(var/mob/M, var/ckey_to_find, var/mob/admin_to_notify) if(!M || !M.ckey) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 0b56014dd21..1439ede2906 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -26,17 +26,16 @@ message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") - if(href_list["openadminticket"]) - if(!check_rights(R_ADMIN)) - return - var/ticketID = text2num(href_list["openadminticket"]) - SStickets.showDetailUI(usr, ticketID) - - if(href_list["openmentorticket"]) - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - var/ticketID = text2num(href_list["openmentorticket"]) - SSmentor_tickets.showDetailUI(usr, ticketID) + if(href_list["openticket"]) + var/ticketID = text2num(href_list["openticket"]) + if(!href_list["is_mhelp"]) + if(!check_rights(R_ADMIN)) + return + SStickets.showDetailUI(usr, ticketID) + else + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + SSmentor_tickets.showDetailUI(usr, ticketID) if(href_list["stickyban"]) stickyban(href_list["stickyban"],href_list) @@ -1669,6 +1668,9 @@ SStickets.resolveTicket(index) else if(href_list["autorespond"]) + if(href_list["is_mhelp"]) + to_chat(usr, "Auto responses are not available for mentor helps.") + return var/index = text2num(href_list["autorespond"]) if(!check_rights(R_ADMIN|R_MOD)) return diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 0b88d6030bb..4289df8ffbc 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -29,64 +29,12 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) if(!msg) return - var/original_msg = msg + if(selected_type == "Mentorhelp") + SSmentor_tickets.newHelpRequest(src, msg) + else + SStickets.newHelpRequest(src, msg) - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - for(var/mob/M in GLOB.mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, iTICKET) " - finalised_msg += "[ai_found ? "(CL)" : ""] (TAKE) " - finalised_msg += "(RESOLVE) [isMhelp ? "" : "(AUTO)"] " - finalised_msg += "(CONVERT) : [span][msg]" - - if(isMhelp) - //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, finalised_msg) - for(var/client/X in mentorholders + modholders + adminholders) - if(X.prefs.sound & SOUND_MENTORHELP) - SEND_SOUND(X, 'sound/effects/adminhelp.ogg') - to_chat(X, finalised_msg) - else //Ahelp - //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, finalised_msg) - for(var/client/X in modholders + adminholders) - if(X.prefs.sound & SOUND_ADMINHELP) - SEND_SOUND(X, 'sound/effects/adminhelp.ogg') - window_flash(X) - to_chat(X, finalised_msg) - - //show it to the person adminhelping too - to_chat(src, "[selected_type]: [original_msg]") + to_chat(src, "[selected_type]: [msg]") var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") + log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [admin_number_present] non-AFK admins.") if(admin_number_present <= 0) if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") + send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!No admins online!!") else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") + send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!All admins AFK ([admin_number_afk])!!") else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") + send2adminirc("[selected_type] from [key_name(src)]: [msg]") feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return diff --git a/code/modules/events/anomaly_atmos.dm b/code/modules/events/anomaly_atmos.dm new file mode 100644 index 00000000000..fa512cb522b --- /dev/null +++ b/code/modules/events/anomaly_atmos.dm @@ -0,0 +1,52 @@ +/datum/event/anomaly/anomaly_atmos + startWhen = 30 + announceWhen = 3 + endWhen = 100 + +/datum/event/anomaly/anomaly_atmos/announce() + GLOB.event_announcement.Announce("Transformative gas anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + +/datum/event/anomaly/anomaly_atmos/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/atmos(T.loc) + + +/datum/event/anomaly/anomaly_atmos/tick() + if(!newAnomaly) + kill() + return + if(ISMULTIPLE(activeFor, 5)) + newAnomaly.anomalyEffect() + +/datum/event/anomaly/anomaly_atmos/end() + if(!newAnomaly || !newAnomaly.loc) //no anomaly or it is in nullspace + return + var/obj/effect/anomaly/atmos/A = newAnomaly + var/gas_type = A.gas_type + var/area/t_area = get_area(A) + for(var/turf/T in t_area) + if(istype(T, /turf/simulated)) //should not occur on unsimulated turfs without admemery + var/turf/simulated/S = T + fill_with_gas(gas_type, S) + explosion(get_turf(newAnomaly), -1, 0, 2) // a small boom so people know something happened. + ..() + +/** + * Replaces all oxygen and nitrogen on the simulated turf S with the gas type specified in gas, taking N2, N20, CO2 and agent B + */ +/datum/event/anomaly/anomaly_atmos/proc/fill_with_gas(gas, turf/simulated/S) + if(!S.air)//no air to transform + return + var/amount_to_add = S.air.oxygen + S.air.nitrogen + S.air.oxygen = 0 + S.air.nitrogen = 0 + switch(gas) + if(GAS_N2) + S.air.nitrogen += amount_to_add + if(GAS_N2O) + S.air.sleeping_agent += amount_to_add + if(GAS_CO2) + S.air.carbon_dioxide += amount_to_add + S.update_visuals() + S.air_update_turf() diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index bce820dadfd..ad0710e96ed 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -170,10 +170,11 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MODERATE, "Brand Intelligence", /datum/event/brand_intelligence, 50, list(ASSIGNMENT_ENGINEER = 25), TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Dust", /datum/event/dust, 50, list(ASSIGNMENT_ENGINEER = 50)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Dimensional Tear", /datum/event/tear, 0, list(ASSIGNMENT_SECURITY = 35)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Honknomoly", /datum/event/tear/honk, 0), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Honknomoly", /datum/event/tear/honk, 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vent Clog", /datum/event/vent_clog, 250), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Wormholes", /datum/event/wormholes, 150), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Pyro Anomaly", /datum/event/anomaly/anomaly_pyro, 75, list(ASSIGNMENT_ENGINEER = 60)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Pyro Anomaly", /datum/event/anomaly/anomaly_pyro, 75, list(ASSIGNMENT_ENGINEER = 60)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Atmos Anomaly", /datum/event/anomaly/anomaly_atmos, 75, list(ASSIGNMENT_ENGINEER = 60)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vortex Anomaly", /datum/event/anomaly/anomaly_vortex, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Bluespace Anomaly", /datum/event/anomaly/anomaly_bluespace, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Flux Anomaly", /datum/event/anomaly/anomaly_flux, 75, list(ASSIGNMENT_ENGINEER = 50)), @@ -182,7 +183,7 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MODERATE, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, is_one_shot = TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/spawn_morph, 40, is_one_shot = TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Disease Outbreak", /datum/event/disease_outbreak, 0, list(ASSIGNMENT_MEDICAL = 150), TRUE), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Headcrabs", /datum/event/headcrabs, 0, list(ASSIGNMENT_SECURITY = 20)) + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Headcrabs", /datum/event/headcrabs, 0, list(ASSIGNMENT_SECURITY = 20)) ) /datum/event_container/major diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 7c253393e07..dbc01373a9f 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -226,7 +226,7 @@ desc = "Made in Space Switzerland." icon_state = "hot_coco" item_state = "coffee" - list_reagents = list("chocolate" = 30) + list_reagents = list("hot_coco" = 15, "chocolate" = 6, "water" = 9) resistance_flags = FREEZE_PROOF /obj/item/reagent_containers/food/drinks/weightloss diff --git a/code/modules/food_and_drinks/food/foods/ethnic.dm b/code/modules/food_and_drinks/food/foods/ethnic.dm index 0a160967858..6523b7521e3 100644 --- a/code/modules/food_and_drinks/food/foods/ethnic.dm +++ b/code/modules/food_and_drinks/food/foods/ethnic.dm @@ -65,6 +65,7 @@ name = "sweet & sour chicken balls" desc = "Is this chicken cooked? The odds are better than wok paper scissors." icon_state = "chickenball" + item_state = "chinese3" junkiness = 25 list_reagents = list("nutriment" = 2, "msg" = 4, "sugar" = 2) tastes = list("chicken" = 1, "sweetness" = 1) @@ -89,6 +90,7 @@ name = "fried rice" desc = "A timeless classic." icon_state = "chinese4" + item_state = "chinese2" junkiness = 20 list_reagents = list("nutriment" = 1, "rice" = 3, "msg" = 4, "sugar" = 2) tastes = list("rice" = 1) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index d79a8030a59..134e337fa4e 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -323,7 +323,7 @@ else I = image(icon = myseed.growing_icon, icon_state = myseed.icon_harvest) else - var/t_growthstate = min(round((age / myseed.maturation) * myseed.growthstages), myseed.growthstages) + var/t_growthstate = clamp(round((age / myseed.maturation) * myseed.growthstages), 1, myseed.growthstages) I = image(icon = myseed.growing_icon, icon_state = "[myseed.icon_grow][t_growthstate]") I.layer = OBJ_LAYER + 0.01 overlays += I diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 43cce5904e3..f9a98b01483 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -11,6 +11,7 @@ throw_range = 5 origin_tech = "bluespace=2" slot_flags = SLOT_BELT + var/emagged = FALSE /obj/item/wormhole_jaunter/attack_self(mob/user) user.visible_message("[user.name] activates the [name]!") @@ -43,6 +44,7 @@ return var/chosen_beacon = pick(L) var/obj/effect/portal/jaunt_tunnel/J = new(get_turf(src), get_turf(chosen_beacon), src, 100) + J.emagged = emagged if(adjacent) try_move_adjacent(J) else @@ -57,12 +59,26 @@ else to_chat(user, "[src] is not attached to your belt, preventing it from saving you from the chasm. RIP.") +/obj/item/wormhole_jaunter/emag_act(mob/user) + if(!emagged) + emagged = TRUE + to_chat(user, "You emag [src].") + var/turf/T = get_turf(src) + do_sparks(5, 0, T) + playsound(T, "sparks", 50, 1) + /obj/effect/portal/jaunt_tunnel name = "jaunt tunnel" icon = 'icons/effects/effects.dmi' icon_state = "bhole3" desc = "A stable hole in the universe made by a wormhole jaunter. Turbulent doesn't even begin to describe how rough passage through one of these is, but at least it will always get you somewhere near a beacon." failchance = 0 + var/emagged = FALSE + +/obj/effect/portal/jaunt_tunnel/can_teleport(atom/movable/M) + if(!emagged && ismegafauna(M)) + return FALSE + return ..() /obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M) . = ..() diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm index 21bc247b0df..a0ea3eb7639 100644 --- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm +++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm @@ -120,13 +120,18 @@ return var/mob/living/carbon/human/H = user - var/random = rand(1,2) + var/random = rand(1,3) switch(random) if(1) to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.") H.set_species(/datum/species/skeleton) if(2) + to_chat(user, "Power courses through you! You can now shift your form at will.") + if(user.mind) + var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new + user.mind.AddSpell(D) + if(3) to_chat(user, "You feel like you could walk straight through lava now.") H.weather_immunities |= "lava" diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 675de65fb3e..c2da907df61 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -254,7 +254,7 @@ new /obj/item/extinguisher/mini(drop_location) new /obj/item/twohanded/kinetic_crusher(drop_location) if("Mining Conscription Kit") - new /obj/item/storage/backpack/duffel/mining_conscript/full(drop_location) + new /obj/item/storage/backpack/duffel/mining_conscript(drop_location) qdel(voucher) @@ -343,27 +343,6 @@ . = ..() . += "There's [points] points on the card." -/**********************Conscription Kit**********************/ - -/obj/item/card/id/mining_access_card - name = "mining access card" - desc = "A small card, that when used on any ID, will add mining access." - icon_state = "data" - -/obj/item/card/id/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity) - . = ..() - if(istype(AM, /obj/item/card/id) && proximity) - var/obj/item/card/id/I = AM - I.access |= list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO) - to_chat(user, "You upgrade [I] with mining access.") - qdel(src) - -/obj/item/storage/backpack/duffel/mining_conscript/full - name = "mining conscription kit" - desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." - -/obj/item/storage/backpack/duffel/mining_conscript/full/New() - ..() - new /obj/item/card/id/mining_access_card(src) #undef EQUIPMENT + diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index ef5268fa104..2d9966669e8 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -576,7 +576,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/dat dat += "

Crew Manifest

" - dat += GLOB.data_core.get_manifest() + dat += GLOB.data_core.get_manifest(OOC = TRUE) src << browse(dat, "window=manifest;size=370x420;can_close=1") diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 74a5bd749d6..9211a43ae8c 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -47,6 +47,13 @@ if(C.can_inject(null, FALSE, inject_target, FALSE)) C.reagents.add_reagent("spidertoxin", venom_per_bite) +/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup() + . = ..() + // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. + if(!.) + for(var/obj/structure/spider/S in range(1, get_turf(src))) + return S + //nursemaids - these create webs and eggs /mob/living/simple_animal/hostile/poison/giant_spider/nurse desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 6c943b26f53..66f6141c797 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -391,6 +391,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) return TRUE +/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup() + . = ..() + // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. + if(!.) + for(var/obj/structure/spider/S in range(1, get_turf(src))) + return S + /mob/living/simple_animal/hostile/poison/terror_spider/Stat() ..() // Determines what shows in the "Status" tab for player-controlled spiders. Used to help players understand spider health regeneration mechanics. diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 960340e90b6..4d3eb5f51f0 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -144,7 +144,7 @@ field_generator power level display ..() /obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) - if(Proj.flag != "bullet") + if(Proj.flag != "bullet" && !Proj.nodamage) power = min(power + Proj.damage, field_generator_max_power) check_power_level() return 0 diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index c320296e0bd..766975a4aad 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -85,6 +85,7 @@ icon_state = "omnilaser" hitsound = 'sound/weapons/tap.ogg' nodamage = 1 + damage = 0 damage_type = STAMINA flag = "laser" var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm index 565a0d483a8..966e8ae93eb 100644 --- a/code/modules/reagents/chemistry/reagents/food.dm +++ b/code/modules/reagents/chemistry/reagents/food.dm @@ -628,7 +628,7 @@ reagent_state = LIQUID color = "#B4B400" metabolization_rate = 0.2 - nutriment_factor = 2 + nutriment_factor = 2.5 * REAGENTS_METABOLISM taste_description = "broth" /datum/reagent/consumable/cheese diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index 9797951f290..072a22aa7ba 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -121,7 +121,14 @@ /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - if(M.bodytemperature < TCRYO) + var/external_temp + if(istype(M.loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/C = M.loc + external_temp = C.temperature_archived + else + var/turf/T = get_turf(M) + external_temp = T.temperature + if(external_temp < TCRYO) update_flags |= M.adjustCloneLoss(-4, FALSE) update_flags |= M.adjustOxyLoss(-10, FALSE) update_flags |= M.adjustToxLoss(-3, FALSE) diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index 529827ad369..f043b16c0ad 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -307,7 +307,6 @@ h1.alert, h2.alert {color: #FFF;} .info {color: #6685f5;} .notice {color: #6685f5;} -.bnotice {color: #6685f5; font-weight: bold;} .boldnotice {color: #6685f5; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03bb39;} @@ -328,7 +327,7 @@ h1.alert, h2.alert {color: #FFF;} .sans {font-family: 'Comic Sans MS', cursive, sans-serif;} .wingdings {font-family: Wingdings, Webdings;} .robot {font-family: 'PxPlus IBM MDA'; font-size: 1.15em;} -.ancient {color: #008B8B; font-stye: italic;} +.ancient {color: #008B8B; font-style: italic;} .newscaster {color: #CC0000;} .mod {color: #735638; font-weight: bold;} .modooc {color: #184880; font-weight: bold;} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 31da7aa96f5..1ca6700520a 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -304,7 +304,6 @@ h1.alert, h2.alert {color: #000000;} .info {color: #0000CC;} .notice {color: #000099;} -.bnotice {color: #000099; font-weight: bold;} .boldnotice {color: #000099; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03bb39;} @@ -325,7 +324,7 @@ h1.alert, h2.alert {color: #000000;} .sans {font-family: 'Comic Sans MS', cursive, sans-serif;} .wingdings {font-family: Wingdings, Webdings;} .robot {font-family: 'PxPlus IBM MDA'; font-size: 1.15em;} -.ancient {color: #008B8B; font-stye: italic;} +.ancient {color: #008B8B; font-style: italic;} .newscaster {color: #800000;} .mod {color: #735638; font-weight: bold;} .modooc {color: #184880; font-weight: bold;} diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 61224892056..240558ebb80 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 34a1864257b..a3743efa33b 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/paradise.dme b/paradise.dme index 3091b299832..876fbeb711d 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1437,6 +1437,7 @@ #include "code\modules\events\abductor.dm" #include "code\modules\events\alien_infestation.dm" #include "code\modules\events\anomaly.dm" +#include "code\modules\events\anomaly_atmos.dm" #include "code\modules\events\anomaly_bluespace.dm" #include "code\modules\events\anomaly_flux.dm" #include "code\modules\events\anomaly_grav.dm"