"
var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
return
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 74cceacd12..46d419a8fe 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -353,7 +353,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
dat = list("", header.Join(), body, " ")
var/datum/browser/popup = new(user, "id_com", src.name, 900, 620)
popup.set_content(dat.Join())
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/card/Topic(href, href_list)
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 400ce041c7..2a05b359d8 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -276,7 +276,6 @@
var/datum/browser/popup = new(user, "cloning", "Cloning System Control")
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/cloning/Topic(href, href_list)
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 6a99b248e3..3ef887b156 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -1,3 +1,15 @@
+#define STATE_DEFAULT 1
+#define STATE_CALLSHUTTLE 2
+#define STATE_CANCELSHUTTLE 3
+#define STATE_MESSAGELIST 4
+#define STATE_VIEWMESSAGE 5
+#define STATE_DELMESSAGE 6
+#define STATE_STATUSDISPLAY 7
+#define STATE_ALERT_LEVEL 8
+#define STATE_CONFIRM_LEVEL 9
+#define STATE_TOGGLE_EMERGENCY 10
+#define STATE_PURCHASE 11
+
// The communications computer
/obj/machinery/computer/communications
name = "communications console"
@@ -6,6 +18,7 @@
icon_keyboard = "tech_key"
req_access = list(ACCESS_HEADS)
circuit = /obj/item/circuitboard/computer/communications
+ light_color = LIGHT_COLOR_BLUE
var/auth_id = "Unknown" //Who is currently logged in?
var/list/datum/comm_message/messages = list()
var/datum/comm_message/currmsg
@@ -16,22 +29,10 @@
var/ai_message_cooldown = 0
var/tmp_alertlevel = 0
var/static/security_level_cd // used to stop mass spam.
- var/const/STATE_DEFAULT = 1
- var/const/STATE_CALLSHUTTLE = 2
- var/const/STATE_CANCELSHUTTLE = 3
- var/const/STATE_MESSAGELIST = 4
- var/const/STATE_VIEWMESSAGE = 5
- var/const/STATE_DELMESSAGE = 6
- var/const/STATE_STATUSDISPLAY = 7
- var/const/STATE_ALERT_LEVEL = 8
- var/const/STATE_CONFIRM_LEVEL = 9
- var/const/STATE_TOGGLE_EMERGENCY = 10
- var/const/STATE_PURCHASE = 11
var/stat_msg1
var/stat_msg2
- light_color = LIGHT_COLOR_BLUE
/obj/machinery/computer/communications/proc/checkCCcooldown()
var/obj/item/circuitboard/computer/communications/CM = circuit
@@ -46,7 +47,7 @@
/obj/machinery/computer/communications/Topic(href, href_list)
if(..())
return
- if(!usr.canUseTopic(src))
+ if(!usr.canUseTopic(src, !issilicon(usr)))
return
if(!is_station_level(z) && !is_reserved_level(z)) //Can only use in transit and on SS13
to_chat(usr, "Unable to establish a connection: \black You're too far away from the station!")
@@ -132,15 +133,20 @@
if("crossserver")
if(authenticated==2)
+ var/dest = href_list["cross_dest"]
if(!checkCCcooldown())
to_chat(usr, "Arrays recycling. Please stand by.")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
- var/input = stripped_multiline_input(usr, "Please choose a message to transmit to allied stations. Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "")
+ var/warning = dest == "all" ? "Please choose a message to transmit to allied stations." : "Please choose a message to transmit to [dest] sector station."
+ var/input = stripped_multiline_input(usr, "[warning] Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- send2otherserver("[station_name()]", input,"Comms_Console")
+ if(dest == "all")
+ send2otherserver("[station_name()]", input,"Comms_Console")
+ else
+ send2otherserver("[station_name()]", input,"Comms_Console", list(dest))
minor_announce(input, title = "Outgoing message to allied station")
usr.log_talk(input, LOG_SAY, tag="message to the other server")
message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server.")
@@ -156,12 +162,12 @@
var/datum/map_template/shuttle/S = locate(href_list["chosen_shuttle"]) in shuttles
if(S && istype(S))
if(SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
- to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?")
+ to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?")
return
if(SSshuttle.shuttle_purchased)
- to_chat(usr, "A replacement shuttle has already been purchased.")
+ to_chat(usr, "A replacement shuttle has already been purchased.")
else if(!S.prerequisites_met())
- to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
+ to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
else
var/points_to_check
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
@@ -183,7 +189,7 @@
if("callshuttle")
state = STATE_DEFAULT
- if(authenticated)
+ if(authenticated && SSshuttle.canEvac(usr))
state = STATE_CALLSHUTTLE
if("callshuttle2")
if(authenticated)
@@ -284,12 +290,12 @@
if("MessageCentCom")
if(authenticated)
if(!checkCCcooldown())
- to_chat(usr, "Arrays recycling. Please stand by.")
+ to_chat(usr, "Arrays recycling. Please stand by.")
return
var/input = stripped_input(usr, "Please choose a message to transmit to CentCom via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send a message to CentCom.", "")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
CentCom_announce(input, usr)
to_chat(usr, "Message transmitted to Central Command.")
for(var/client/X in GLOB.admins)
@@ -302,7 +308,7 @@
// OMG SYNDICATE ...LETTERHEAD
if("MessageSyndicate")
- if((authenticated==2) && (obj_flags & EMAGGED))
+ if((authenticated) && (obj_flags & EMAGGED))
if(!checkCCcooldown())
to_chat(usr, "Arrays recycling. Please stand by.")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
@@ -332,7 +338,7 @@
if(!checkCCcooldown())
to_chat(usr, "Arrays recycling. Please stand by.")
return
- var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","")
+ var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self-Destruct Code Request.","")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
Nuke_request(input, usr)
@@ -347,7 +353,9 @@
aicurrmsg = null
aistate = STATE_DEFAULT
if("ai-callshuttle")
- aistate = STATE_CALLSHUTTLE
+ aistate = STATE_DEFAULT
+ if(SSshuttle.canEvac(usr))
+ aistate = STATE_CALLSHUTTLE
if("ai-callshuttle2")
SSshuttle.requestEvac(usr, href_list["call"])
aistate = STATE_DEFAULT
@@ -460,9 +468,8 @@
var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
- if(issilicon(user) || (hasSiliconAccessInArea(user) && !in_range(user,src)))
+ if(issilicon(user))
var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access
if(dat2)
dat += dat2
@@ -493,9 +500,15 @@
if (authenticated==2)
dat += "
Captain Functions"
dat += " \[ Make a Captain's Announcement \]"
- var/cross_servers_count = length(CONFIG_GET(keyed_list/cross_server))
- if(cross_servers_count)
- dat += " \[ Send a message to [cross_servers_count == 1 ? "an " : ""]allied station[cross_servers_count > 1 ? "s" : ""] \]"
+ var/list/cross_servers = CONFIG_GET(keyed_list/cross_server)
+ var/our_id = CONFIG_GET(string/cross_comms_name)
+ if(cross_servers.len)
+ for(var/server in cross_servers)
+ if(server == our_id)
+ continue
+ dat += " \[ Send a message to station in [server] sector. \]"
+ if(cross_servers.len > 2)
+ dat += " \[ Send a message to all allied stations \]"
if(SSmapping.config.allow_custom_shuttles)
dat += " \[ Purchase Shuttle \]"
dat += " \[ Change Alert Level \]"
@@ -721,8 +734,13 @@
to_chat(user, "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))
+ if(!input || !user.canUseTopic(src, !issilicon(usr)))
return
+ if(!(user.can_speak())) //No more cheating, mime/random mute guy!
+ input = "..."
+ to_chat(user, "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_silicon, input)
deadchat_broadcast("[user.real_name] made an priority announcement from [get_area_name(usr, TRUE)].", user)
@@ -771,3 +789,15 @@
content = new_content
if(new_possible_answers)
possible_answers = new_possible_answers
+
+#undef STATE_DEFAULT
+#undef STATE_CALLSHUTTLE
+#undef STATE_CANCELSHUTTLE
+#undef STATE_MESSAGELIST
+#undef STATE_VIEWMESSAGE
+#undef STATE_DELMESSAGE
+#undef STATE_STATUSDISPLAY
+#undef STATE_ALERT_LEVEL
+#undef STATE_CONFIRM_LEVEL
+#undef STATE_TOGGLE_EMERGENCY
+#undef STATE_PURCHASE
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 286c106788..3d212ec0cb 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -217,9 +217,6 @@
// already discovered mutations
stored_research = SSresearch.science_tech
-/obj/machinery/computer/scan_consolenew/examine(mob/user)
- . = ..()
-
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, datum/tgui/ui)
// Most of ui_interact is spent setting variables for passing to the tgui
// interface.
@@ -266,6 +263,10 @@
if(!ui)
ui = new(user, src, "DnaConsole")
ui.open()
+
+/obj/machinery/computer/scan_consolenew/ui_assets()
+ . = ..() || list()
+ . += get_asset_datum(/datum/asset/simple/genetics)
/obj/machinery/computer/scan_consolenew/ui_data(mob/user)
var/list/data = list()
@@ -357,7 +358,7 @@
return data
-/obj/machinery/computer/scan_consolenew/ui_act(action, var/list/params)
+/obj/machinery/computer/scan_consolenew/ui_act(action, list/params)
if(..())
return TRUE
@@ -482,6 +483,7 @@
// Resolve mutation's BYOND path from the alias
var/alias = params["alias"]
var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias)
+
// Make sure the occupant still has this mutation
if(!(path in scanner_occupant.dna.mutation_index))
return
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index d8a5f856f1..4e99f67157 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -178,7 +178,6 @@
dat += "{Log In}"
var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/med_data/Topic(href, href_list)
diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm
index 53f7cb7e32..ca64d538b9 100644
--- a/code/game/machinery/computer/pod.dm
+++ b/code/game/machinery/computer/pod.dm
@@ -67,7 +67,6 @@
add_fingerprint(usr)
var/datum/browser/popup = new(user, "computer", title, 400, 500)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/pod/process()
diff --git a/code/game/machinery/computer/prisoner/management.dm b/code/game/machinery/computer/prisoner/management.dm
index 4b4b39740a..43aa978558 100644
--- a/code/game/machinery/computer/prisoner/management.dm
+++ b/code/game/machinery/computer/prisoner/management.dm
@@ -66,7 +66,6 @@
dat += "{Log Out}"
var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 5c280eeace..844f14465a 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -250,7 +250,6 @@
dat += "{Log In}"
var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return
diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm
index 82e8f46ab6..87b7159125 100644
--- a/code/game/machinery/computer/telecrystalconsoles.dm
+++ b/code/game/machinery/computer/telecrystalconsoles.dm
@@ -103,7 +103,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/datum/browser/popup = new(user, "computer", "Telecrystal Upload/Receive Station", 700, 500)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/telecrystals/uplinker/Topic(href, href_list)
@@ -185,7 +184,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/datum/browser/popup = new(user, "computer", "Team Telecrystal Management Console", 700, 500)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/telecrystals/boss/Topic(href, href_list)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 9330a30555..f7019e75ef 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -76,7 +76,6 @@
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/cryopod/Topic(href, href_list)
@@ -308,7 +307,7 @@
var/mob/living/mob_occupant = occupant
var/list/obj/item/cryo_items = list()
-
+
investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
//Handle Borg stuff first
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 228c2e1f52..d88a9c9072 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -74,12 +74,6 @@
/obj/machinery/door/firedoor/Bumped(atom/movable/AM)
if(panel_open || operating || welded)
return
- if(ismob(AM))
- var/mob/user = AM
- if(density && !welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
- add_fingerprint(user)
- open()
- return TRUE
return FALSE
/obj/machinery/door/firedoor/power_change()
@@ -90,14 +84,6 @@
stat |= NOPOWER
/obj/machinery/door/firedoor/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
- if(!welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
- add_fingerprint(user)
- if(density)
- emergency_close_timer = world.time + 30 // prevent it from instaclosing again if in space
- open()
- else
- close()
- return TRUE
if(operating || !density)
return
diff --git a/code/game/machinery/embedded_controller/access_controller.dm b/code/game/machinery/embedded_controller/access_controller.dm
index f74de23171..8eebd779f4 100644
--- a/code/game/machinery/embedded_controller/access_controller.dm
+++ b/code/game/machinery/embedded_controller/access_controller.dm
@@ -260,7 +260,6 @@
/obj/machinery/doorButtons/airlock_controller/ui_interact(mob/user)
var/datum/browser/popup = new(user, "computer", name)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_content(returnText())
popup.open()
diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm
index 6fd351bcff..5d0165189d 100644
--- a/code/game/machinery/embedded_controller/embedded_controller_base.dm
+++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm
@@ -29,7 +29,6 @@
. = ..()
user.set_machine(src)
var/datum/browser/popup = new(user, "computer", name) // Set up the popup browser window
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_content(return_text())
popup.open()
diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm
index a8fa31d5fb..0b89b117c4 100644
--- a/code/game/machinery/mass_driver.dm
+++ b/code/game/machinery/mass_driver.dm
@@ -38,3 +38,14 @@
if(stat & (BROKEN|NOPOWER))
return
drive()
+
+/obj/machinery/mass_driver/pressure_plate
+ name = "pressure plated mass driver"
+ var/drive_delay = 10
+
+/obj/machinery/mass_driver/pressure_plate/Crossed(atom/movable/O)
+ . = ..()
+ if(isliving(O))
+ var/mob/living/L = O
+ to_chat(L, "You feel something click beneath you!")
+ addtimer(CALLBACK(src, .proc/drive), drive_delay)
\ No newline at end of file
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index e1f55da551..2915c2f34b 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -218,10 +218,10 @@ GLOBAL_LIST_EMPTY(allConsoles)
dat += "Message Authentication
"
dat += "Message for [dpt]: [message]
"
dat += "
You may authenticate your message now by scanning your ID or your stamp
"
dat += "<< Discard Message "
@@ -271,7 +271,6 @@ GLOBAL_LIST_EMPTY(allConsoles)
var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/requests_console/Topic(href, href_list)
@@ -279,7 +278,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
return
usr.set_machine(src)
add_fingerprint(usr)
-
+
if(href_list["write"])
dpt = ckey(reject_bad_text(href_list["write"])) //write contains the string of the receiving department's name
var/new_message = stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN)
@@ -358,7 +357,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
workingServer = TRUE
if(!workingServer)
- screen = 7
+ screen = 7
say("NOTICE: No server detected! Please contact your local engineering team.")
updateUsrDialog()
return
@@ -539,7 +538,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
to_chat(user, "You are not authorized to send announcements!")
updateUsrDialog()
return
-
+
if(istype(O, /obj/item/stamp))
if(screen == 9)
var/obj/item/stamp/T = O
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 84b205b585..4740adaffb 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -132,7 +132,6 @@
var/datum/browser/popup = new(user, "slotmachine", "Slot Machine")
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/slot_machine/Topic(href, href_list)
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index a2d4b9eb7e..8bca58a7de 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -100,7 +100,7 @@
// So he can't jump out the gate right away.
R.SetLockdown()
if(masterAI)
- R.connected_ai = masterAI
+ R.set_connected_ai(masterAI)
R.lawsync()
R.lawupdate = 1
addtimer(CALLBACK(src, .proc/unlock_new_robot, R), 50)
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 40336fb01e..e2d3af149f 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -1,12 +1,10 @@
/obj/mecha/proc/get_armour_facing(relative_dir)
switch(relative_dir)
- if(0) // BACKSTAB!
+ if(180) // BACKSTAB!
return facing_modifiers[BACK_ARMOUR]
- if(45, 90, 270, 315)
- return facing_modifiers[SIDE_ARMOUR]
- if(225, 180, 135)
+ if(0, 45) // direct or 45 degrees off
return facing_modifiers[FRONT_ARMOUR]
- return 1 //always return non-0
+ return facing_modifiers[SIDE_ARMOUR] //if its not a front hit or back hit then assume its from the side
/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
@@ -43,7 +41,7 @@
break
if(attack_dir)
- var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src))
+ var/facing_modifier = get_armour_facing(abs(dir2angle(dir) - dir2angle(attack_dir)))
booster_damage_modifier /= facing_modifier
booster_deflection_modifier *= facing_modifier
if(prob(deflect_chance * booster_deflection_modifier))
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 41270bb09e..7e3a5205de 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -17,11 +17,15 @@
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
-/obj/effect/anomaly/Initialize(mapload, new_lifespan)
+ /// chance we drop a core when neutralized
+ var/core_drop_chance = 100
+
+/obj/effect/anomaly/Initialize(mapload, new_lifespan, core_drop_chance = 100)
. = ..()
GLOB.poi_list |= src
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
+ src.core_drop_chance = core_drop_chance
if (!impact_area)
return INITIALIZE_HINT_QDEL
@@ -54,6 +58,8 @@
GLOB.poi_list.Remove(src)
STOP_PROCESSING(SSobj, src)
qdel(countdown)
+ if(aSignal)
+ QDEL_NULL(aSignal)
return ..()
/obj/effect/anomaly/proc/anomalyEffect()
@@ -70,12 +76,12 @@
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/particle_effect/smoke/bad(loc)
- for(var/atom/movable/O in src)
- O.forceMove(drop_location())
+ if(prob(core_drop_chance))
+ aSignal.forceMove(drop_location())
+ aSignal = null
qdel(src)
-
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed
to_chat(user, "Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].")
@@ -285,7 +291,7 @@
S.rabid = TRUE
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
- offer_control(S)
+ offer_control(S,POLL_IGNORE_SENTIENCE_POTION)
/////////////////////
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 25b262a146..5f312f2bf3 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -46,4 +46,5 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
- T.AddElement(/datum/element/decal, icon, icon_state, turn(dir, -dir2angle(T.dir)), CLEAN_GOD, color, null, null, alpha)
+ var/turn_dir = 180 - dir2angle(T.dir) //Turning a dir by 0 results in a roulette of random dirs.
+ T.AddElement(/datum/element/decal, icon, icon_state, turn_dir ? turn(dir, turn_dir) : dir, CLEAN_GOD, color, null, null, alpha)
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index c89e318714..07ffe8896e 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -242,7 +242,6 @@ RLD
var/datum/browser/popup = new(user, "rcd_access", "Access Control", 900, 500, src)
popup.set_content(t1)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/item/construction/rcd/Topic(href, href_list)
diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/broom.dm
index 225644109f..4cbd75ed94 100644
--- a/code/game/objects/items/broom.dm
+++ b/code/game/objects/items/broom.dm
@@ -28,39 +28,37 @@
/// triggered on wield of two handed item
/obj/item/broom/proc/on_wield(obj/item/source, mob/user)
to_chat(user, "You brace the [src] against the ground in a firm sweeping stance.")
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
+ RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, .proc/sweep)
/// triggered on unwield of two handed item
/obj/item/broom/proc/on_unwield(obj/item/source, mob/user)
- UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE)
/obj/item/broom/afterattack(atom/A, mob/user, proximity)
. = ..()
if(!proximity)
return
- sweep(user, A, FALSE)
+ sweep(user, A)
-/obj/item/broom/proc/sweep(mob/user, atom/A, moving = TRUE)
- var/turf/target
- if (!moving)
- if (isturf(A))
- target = A
- else
- target = A.loc
- else
- target = user.loc
- if (!isturf(target))
+/obj/item/broom/proc/sweep(datum/source, atom/newLoc)
+ if(!ismob(source) || !isturf(newLoc) || (get_dist(source, newLoc) > 1))
return
- if (locate(/obj/structure/table) in target.contents)
+ var/turf/target = newLoc
+ var/atom/movable/AM
+ var/sweep_dir = get_dir(source, target)
+ if(!sweep_dir)
return
+ for(var/i in target.contents)
+ AM = i
+ if(AM.density) // eh good enough heuristic check
+ return
var/i = 0
for(var/obj/item/garbage in target.contents)
if(!garbage.anchored)
- garbage.Move(get_step(target, user.dir), user.dir)
- i++
- if(i >= 20)
+ step(garbage, sweep_dir)
+ if(++i > 20)
break
- if(i >= 1)
+ if(i)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 30, TRUE, -1)
/obj/item/broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta
diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm
index 5ed719ff94..9b658bc5f1 100644
--- a/code/game/objects/items/charter.dm
+++ b/code/game/objects/items/charter.dm
@@ -13,6 +13,7 @@
var/ignores_timeout = FALSE
var/response_timer_id = null
var/approval_time = 600
+ var/allow_unicode = FALSE
var/static/regex/standard_station_regex
@@ -48,6 +49,9 @@
if(!new_name)
return
+ if(!allow_unicode && (length(new_name) != length_char(new_name)))
+ to_chat(user, "Unicode is not allowed. Adminhelp if you want to use it so badly.")
+ return
log_game("[key_name(user)] has proposed to name the station as \
[new_name]")
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 37d19783b5..8e59e91e38 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -390,7 +390,7 @@
/obj/item/circuitboard/machine/thermomachine/examine()
. = ..()
- . += "It is set to layer [pipe_layer]."
+ . += "It is set to layer [pipe_layer]. Use a Multitool on the circuit to change this."
/obj/item/circuitboard/machine/thermomachine/heater
name = "Heater (Machine Board)"
@@ -1146,3 +1146,8 @@
build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater
req_components = list(/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/matter_bin = 1)
+
+/obj/item/circuitboard/machine/explosive_compressor
+ name = "Explosive Compressor (Machine Board)"
+ build_path = /obj/machinery/research/explosive_compressor
+ req_components = list(/obj/item/stock_parts/matter_bin = 3)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 7f8b720509..2a9944f770 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -735,7 +735,11 @@
if(isobj(target))
if(actually_paints)
var/list/hsl = rgb2hsl(hex2num(copytext(paint_color,2,4)),hex2num(copytext(paint_color,4,6)),hex2num(copytext(paint_color,6,8)))
- if(hsl[3] < 0.25 && !istype(target, /obj/structure/window) && !istype(target, /obj/effect/decal/cleanable/crayon)) //Colors too dark are rejected
+ var/static/whitelisted = typecacheof(list(/obj/structure/window,
+ /obj/effect/decal/cleanable/crayon,
+ /obj/machinery/door/window)
+ )
+ if(hsl[3] < 0.25 && !whitelisted[target]) //Colors too dark are rejected
to_chat(usr, "A color that dark on an object like this? Surely not...")
return FALSE
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 767f8fc395..6396a4fe3d 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -394,8 +394,6 @@
to_chat(user, "[src] are recharging!")
return
- user.stop_pulling() //User has hands full, and we don't care about anyone else pulling on it, their problem. CLEAR!!
-
if(user.a_intent == INTENT_DISARM)
do_disarm(M, user)
return
@@ -447,8 +445,7 @@
if(do_after(user, isnull(defib?.disarm_shock_time)? disarm_shock_time : defib.disarm_shock_time, target = M))
M.visible_message("[user] zaps [M] with [src]!", \
"[user] zaps [M] with [src]!")
- M.adjustStaminaLoss(50)
- M.DefaultCombatKnockdown(100)
+ M.DefaultCombatKnockdown(140)
M.updatehealth() //forces health update before next life tick
playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
M.emote("gasp")
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index ec68cdb40c..10b2b68646 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -266,7 +266,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda)
assets.send(user)
- var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/goonchat)
+ var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat)
emoji_s.send(user) //Already sent by chat but no harm doing this
user.set_machine(src)
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 6eae9f127a..4ca0b86bc0 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -590,7 +590,7 @@ Code:
var/static/list/emoji_icon_states
var/static/emoji_table
if(!emoji_table)
- var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
var/list/collate = list("
")
for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi'))))
var/tag = sheet.icon_tag("emoji-[emoji]")
diff --git a/code/game/objects/items/devices/portable_chem_mixer.dm b/code/game/objects/items/devices/portable_chem_mixer.dm
new file mode 100644
index 0000000000..3e1b20cfc2
--- /dev/null
+++ b/code/game/objects/items/devices/portable_chem_mixer.dm
@@ -0,0 +1,206 @@
+/obj/item/storage/portable_chem_mixer
+ name = "Portable Chemical Mixer"
+ desc = "A portable device that dispenses and mixes chemicals. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "portablechemicalmixer_open"
+ w_class = WEIGHT_CLASS_HUGE
+ slot_flags = ITEM_SLOT_BELT
+ custom_price = 2000
+ custom_premium_price = 2000
+
+ var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into
+ var/amount = 30 ///The amount of reagent that is to be dispensed currently
+
+ var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go
+
+/obj/item/storage/portable_chem_mixer/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 200
+ STR.max_items = 50
+ STR.insert_preposition = "in"
+ STR.can_hold = typecacheof(list(
+ /obj/item/reagent_containers/glass/beaker,
+ ))
+
+/obj/item/storage/portable_chem_mixer/Destroy()
+ QDEL_NULL(beaker)
+ return ..()
+
+/obj/item/storage/portable_chem_mixer/ex_act(severity, target)
+ if(severity < 3)
+ ..()
+
+/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (I.tool_behaviour == TOOL_SCREWDRIVER)
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
+ if (!locked)
+ update_contents()
+ if (locked)
+ replace_beaker(user)
+ update_icon()
+ I.play_tool_sound(src, 50)
+ return
+
+ else if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked)
+ var/obj/item/reagent_containers/B = I
+ . = TRUE //no afterattack
+ if(!user.transferItemToLoc(B, src))
+ return
+ replace_beaker(user, B)
+ update_icon()
+ updateUsrDialog()
+ return
+
+ return ..()
+
+/**
+ * Updates the contents of the portable chemical mixer
+ *
+ * A list of dispensable reagents is created by iterating through each source beaker in the portable chemical beaker and reading its contents
+ */
+/obj/item/storage/portable_chem_mixer/proc/update_contents()
+ dispensable_reagents.Cut()
+
+ for (var/obj/item/reagent_containers/glass/beaker/B in contents)
+ var/key = B.reagents.get_master_reagent_id()
+ if (!(key in dispensable_reagents))
+ dispensable_reagents[key] = list()
+ dispensable_reagents[key]["reagents"] = list()
+ dispensable_reagents[key]["reagents"] += B.reagents
+
+ return
+
+/obj/item/storage/portable_chem_mixer/update_icon_state()
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (!locked)
+ icon_state = "portablechemicalmixer_open"
+ else if (beaker)
+ icon_state = "portablechemicalmixer_full"
+ else
+ icon_state = "portablechemicalmixer_empty"
+
+
+/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (!locked)
+ return ..()
+ if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ replace_beaker(user)
+ update_icon()
+
+/**
+ * Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently
+ *
+ * Checks if a valid user and a valid new beaker exist and attempts to replace the current beaker in the portable chemical mixer with the one in hand. Simply places the new beaker in if no beaker is currently loaded
+ * Arguments:
+ * * mob/living/user - The user who is trying to exchange beakers
+ * * obj/item/reagent_containers/new_beaker - The new beaker that the user wants to put into the device
+ */
+/obj/item/storage/portable_chem_mixer/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
+ if(!user)
+ return FALSE
+ if(beaker)
+ user.put_in_hands(beaker)
+ beaker = null
+ if(new_beaker)
+ beaker = new_beaker
+ return TRUE
+
+/obj/item/storage/portable_chem_mixer/attack_hand(mob/user)
+ if (loc != user)
+ return ..()
+ if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
+ INVOKE_ASYNC(src, /datum.proc/ui_interact, user)
+
+/obj/item/storage/portable_chem_mixer/attack_self(mob/user)
+ if(loc == user)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (locked)
+ ui_interact(user)
+ return
+ else
+ to_chat(user, "The portable chemical mixer is currently open and its contents can be accessed.")
+ return
+ return
+
+/obj/item/storage/portable_chem_mixer/MouseDrop(obj/over_object)
+ . = ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
+ var/obj/screen/inventory/hand/H = over_object
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
+
+/obj/item/storage/portable_chem_mixer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PortableChemMixer", name)
+ if(user.hallucinating())
+ // to not ruin the immersion by constantly changing the fake chemicals
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/obj/item/storage/portable_chem_mixer/ui_data(mob/user)
+ var/list/data = list()
+ data["amount"] = amount
+ data["isBeakerLoaded"] = beaker ? 1 : 0
+ data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
+ data["beakerMaxVolume"] = beaker ? beaker.volume : null
+ data["beakerTransferAmounts"] = beaker ? beaker.possible_transfer_amounts : null
+ var/chemicals[0]
+ var/is_hallucinating = user.hallucinating()
+ if(user.hallucinating())
+ is_hallucinating = TRUE
+ for(var/re in dispensable_reagents)
+ var/value = dispensable_reagents[re]
+ var/datum/reagent/temp = GLOB.chemical_reagents_list[re]
+ if(temp)
+ var/chemname = temp.name
+ var/total_volume = 0
+ for (var/datum/reagents/rs in value["reagents"])
+ total_volume += rs.total_volume
+ if(is_hallucinating && prob(5))
+ chemname = "[pick_list_replacements("hallucination.json", "chemicals")]"
+ chemicals.Add(list(list("title" = chemname, "id" = ckey(temp.name), "volume" = total_volume )))
+ data["chemicals"] = chemicals
+ var/beakerContents[0]
+ if(beaker)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list...
+ data["beakerContents"] = beakerContents
+ return data
+
+/obj/item/storage/portable_chem_mixer/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("amount")
+ var/target = text2num(params["target"])
+ amount = target
+ . = TRUE
+ if("dispense")
+ var/reagent_name = params["reagent"]
+ var/datum/reagent/reagent = GLOB.name2reagent[reagent_name]
+ var/entry = dispensable_reagents[reagent]
+ if(beaker)
+ var/datum/reagents/R = beaker.reagents
+ var/actual = min(amount, 1000, R.maximum_volume - R.total_volume)
+ // todo: add check if we have enough reagent left
+ for (var/datum/reagents/source in entry["reagents"])
+ var/to_transfer = min(source.total_volume, actual)
+ source.trans_to(beaker, to_transfer)
+ actual -= to_transfer
+ if (actual <= 0)
+ break
+ . = TRUE
+ if("remove")
+ var/amount = text2num(params["amount"])
+ beaker.reagents.remove_all(amount)
+ . = TRUE
+ if("eject")
+ replace_beaker(usr)
+ update_icon()
+ . = TRUE
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index e65325587c..c51edc8106 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -16,9 +16,6 @@
var/on = TRUE
var/shock_cooldown = FALSE
- var/ui_x = 260
- var/ui_y = 137
-
/obj/item/electropack/suicide_act(mob/living/carbon/user)
user.visible_message("[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide!")
return (FIRELOSS)
@@ -201,17 +198,7 @@
else
return ..()
-/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey
- var/dat = {"
-
-Frequency/Code for shock collar:
-Frequency:
-[format_frequency(src.frequency)]
-Set
-Code:
-[src.code]
-Set
-"}
- user << browse(dat, "window=radio")
- onclose(user, "radio")
- return
+/obj/item/electropack/ui_act(action, params)
+ if(action == "power") // DO. NOT.
+ return FALSE
+ return ..()
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index fae2833c8a..db29614f20 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -718,8 +718,9 @@ GENETICS SCANNER
to_chat(user, "Pressure: [round(pressure,0.01)] kPa")
for(var/id in air_contents.get_gases())
- var/gas_concentration = air_contents.get_moles(id)/total_moles
- to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)")
+ if(air_contents.get_moles(id) >= 0.005)
+ var/gas_concentration = air_contents.get_moles(id)/total_moles
+ to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)")
to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)")
else
@@ -729,10 +730,10 @@ GENETICS SCANNER
to_chat(user, "[target] is empty!")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
- var/fusion_power = round(cached_scan_results["fusion"], 0.01)
- var/tier = fusionpower2text(fusion_power)
+ var/instability = round(cached_scan_results["fusion"], 0.01)
+ var/tier = instability2text(instability)
to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.")
- to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.")
+ to_chat(user, "Instability of the last fusion reaction: [instability]\n This indicates it was [tier].")
return
/obj/item/analyzer/proc/scan_turf(mob/user, turf/location)
@@ -783,10 +784,10 @@ GENETICS SCANNER
to_chat(user, "Temperature: [round(environment.return_temperature()-T0C, 0.01)] °C ([round(environment.return_temperature(), 0.01)] K)")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
- var/fusion_power = round(cached_scan_results["fusion"], 0.01)
- var/tier = fusionpower2text(fusion_power)
+ var/instability = round(cached_scan_results["fusion"], 0.01)
+ var/tier = instability2text(instability)
to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.")
- to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.")
+ to_chat(user, "Instability of the last fusion reaction: [instability]\n This indicates it was [tier].")
/obj/item/analyzer/ranged
desc = "A hand-held scanner which uses advanced spectroscopy and infrared readings to analyze gases as a distance. Alt-Click to use the built in barometer function."
@@ -992,4 +993,4 @@ GENETICS SCANNER
#undef SCANMODE_CHEMICAL
#undef SCANMODE_WOUND
#undef SCANNER_CONDENSED
-#undef SCANNER_VERBOSE
\ No newline at end of file
+#undef SCANNER_VERBOSE
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 32d9c02a27..8b0d46be44 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -289,3 +289,9 @@
. = TRUE
update_icon()
+
+/**
+ * Returns if this is ready to be detonated. Checks if both tanks are in place.
+ */
+/obj/item/transfer_valve/proc/ready()
+ return tank_one && tank_two
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
index cf5c3d4fc5..6cf6a524de 100644
--- a/code/game/objects/items/dualsaber.dm
+++ b/code/game/objects/items/dualsaber.dm
@@ -26,6 +26,7 @@
wound_bonus = -110
bare_wound_bonus = 20
block_parry_data = /datum/block_parry_data/dual_esword
+ block_chance = 60
var/hacked = FALSE
/// Can this reflect all energy projectiles?
var/can_reflect = TRUE
@@ -38,7 +39,8 @@
var/wielded = FALSE // track wielded status on item
var/slowdown_wielded = 0
-/datum/block_parry_data/dual_esword
+/datum/block_parry_data/dual_esword // please run at the man going apeshit with his funny doublesword
+ can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST | BLOCK_DIR_WEST | BLOCK_DIR_EAST
block_damage_absorption = 2
block_damage_multiplier = 0.15
block_damage_multiplier_override = list(
@@ -50,10 +52,10 @@
block_lock_sprinting = TRUE
// no attacking while blocking
block_lock_attacking = TRUE
- block_projectile_mitigation = 75
+ block_projectile_mitigation = 85
// more efficient vs projectiles
block_stamina_efficiency_override = list(
- TEXT_ATTACK_TYPE_PROJECTILE = 4
+ TEXT_ATTACK_TYPE_PROJECTILE = 6
)
parry_time_windup = 0
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 01d2924a90..52ff0f740e 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -234,6 +234,9 @@
/obj/item/melee/rapier/attack(mob/living/target, mob/living/user)
. = ..()
if(iscarbon(target))
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ visible_message("[user] gently taps [target] with [src].",null,null,COMBAT_MESSAGE_RANGE)
+ log_combat(user, target, "slept", src)
var/mob/living/carbon/H = target
H.Dizzy(10)
H.adjustStaminaLoss(30)
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index 6f1aec287b..7e14fb9d35 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -255,7 +255,7 @@
/obj/item/choice_beacon/box/plushie/generate_display_names()
var/list/plushie_list = list()
//plushie set 1: just subtypes of /obj/item/toy/plush
- var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
+ var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/plushling, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
for(var/V in plushies_set_one)
var/atom/A = V
plushie_list[initial(A.name)] = A
@@ -272,4 +272,3 @@
icon_state = "skub"
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("skubbed")
-
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index a3da49ed89..9a8f1214f0 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -167,7 +167,7 @@
return
log_game("[key_name(user)] activated a hidden grenade in [src].")
grenade.preprime(user, msg = FALSE, volume = 10)
- SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
else
to_chat(user, "You try to pet [src], but it has no stuffing. Aww...")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_nostuffing", /datum/mood_event/plush_nostuffing)
@@ -688,18 +688,6 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
icon_state = "scrubpuppy"
item_state = "scrubpuppy"
-/obj/item/toy/plush/borgplushie/meddrake
- name = "MediDrake Plushie"
- desc = "An adorable stuffed toy of a Medidrake."
- icon_state = "meddrake"
- item_state = "meddrake"
-
-/obj/item/toy/plush/borgplushie/secdrake
- name = "SecDrake Plushie"
- desc = "An adorable stuffed toy of a Secdrake."
- icon_state = "secdrake"
- item_state = "secdrake"
-
/obj/item/toy/plush/aiplush
name = "AI plushie"
desc = "A little stuffed toy AI core... it appears to be malfunctioning."
@@ -766,8 +754,8 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
attack_verb = list("headbutt", "scritched", "bit")
squeak_override = list('modular_citadel/sound/voice/nya.ogg' = 1)
can_random_spawn = FALSE
-
-
+
+
/obj/item/toy/plush/hairball
name = "Hairball"
desc = "A bundle of undigested fibers and scales. Yuck."
@@ -777,3 +765,78 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
squeak_override = list('sound/misc/splort.ogg'=1)
attack_verb = list("sploshed", "splorted", "slushed")
can_random_spawn = FALSE
+
+/obj/item/toy/plush/plushling
+ name = "peculiar plushie"
+ desc = "An adorable stuffed toy- wait, did it just move?"
+ can_random_spawn = FALSE
+ var/absorb_cooldown = 100 //ticks cooldown between absorbs
+ var/next_absorb = 0 //When can it absorb another plushie
+ var/check_interval = 20
+ var/next_check = 0
+
+//Overrides parent proc
+/obj/item/toy/plush/plushling/attack_self(mob/user)
+ if(!user) //hmmmmm
+ return
+ to_chat(user, "You try to pet the plushie, but recoil as it bites your hand instead! OW!")
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_bite", /datum/mood_event/plush_bite)
+ var/mob/living/carbon/human/H = user
+ if(!H)
+ return //Type safety.
+ H.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ addtimer(CALLBACK(H, /mob/living/carbon/human.proc/dropItemToGround, src, TRUE), 1)
+
+/obj/item/toy/plush/plushling/New()
+ var/initial_state = pick("plushie_lizard", "plushie_snake", "plushie_slime", "fox")
+ icon_state = initial_state
+ item_state = initial_state
+ START_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/toy/plush/plushling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/toy/plush/plushling/process()
+ if(world.time < next_absorb || world.time < next_check)
+ return
+ next_check = world.time + check_interval
+ var/obj/item/toy/plush/target
+ for(var/obj/item/toy/plush/possible_target in loc) //First, it tries to get anything in its same location, be it a tile or a backpack
+ if(possible_target == src || istype(possible_target, /obj/item/toy/plush/plushling))
+ continue
+ target = possible_target
+ break
+ if(!target)
+ if(!isturf(loc))
+ return
+ for(var/obj/item/toy/plush/P in oview(1, src)) //If that doesn't work, it hunts for plushies adjacent to its own tile
+ if(istype(P, /obj/item/toy/plush/plushling)) //These do not hunt their own kind
+ continue
+ src.throw_at(P, 1, 2)
+ visible_message("[src] leaps at [P]!")
+ break
+ return
+ if(istype(target, /obj/item/toy/plush/plushling)) //These do not consume their own.
+ return
+ next_absorb = world.time + absorb_cooldown
+ plushie_absorb(target)
+
+/obj/item/toy/plush/plushling/proc/plushie_absorb(obj/item/toy/plush/victim)
+ if(!victim)
+ return
+ visible_message("[src] gruesomely mutilliates [victim], leaving nothing more than dust!")
+ name = victim.name
+ desc = victim.desc + " Wait, did it just move..?"
+ icon_state = victim.icon_state
+ item_state = victim.item_state
+ squeak_override = victim.squeak_override
+ attack_verb = victim.attack_verb
+ new /obj/effect/decal/cleanable/ash(get_turf(victim))
+ qdel(victim)
+
+/obj/item/toy/plush/plushling/love(obj/item/toy/plush/Kisser, mob/living/user) //You shouldn't have come here, poor plush.
+ if(!Kisser)
+ return
+ plushie_absorb(Kisser)
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 6621095b72..a9cd4c33a7 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -281,12 +281,12 @@
O.custom_name = created_name
O.locked = panel_locked
if(!aisync)
- lawsync = 0
- O.connected_ai = null
+ lawsync = FALSE
+ O.set_connected_ai(null)
else
O.notify_ai(NEW_BORG)
if(forced_ai)
- O.connected_ai = forced_ai
+ O.set_connected_ai(forced_ai)
if(!lawsync)
O.lawupdate = 0
if(M.laws.id == DEFAULT_AI_LAWID)
@@ -337,10 +337,10 @@
if(!aisync)
lawsync = FALSE
- O.connected_ai = null
+ O.set_connected_ai(null)
else
if(forced_ai)
- O.connected_ai = forced_ai
+ O.set_connected_ai(forced_ai)
O.notify_ai(AI_SHELL)
if(!lawsync)
O.lawupdate = FALSE
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 7ba2196184..a96b0104f1 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -269,8 +269,8 @@
to_chat(user, "[M] is at full health.")
return FALSE
user.visible_message("[user] applies \the [src] on [M].", "You apply \the [src] on [M].")
- return heal_carbon(M, user, heal_brute, heal_burn)
-
+ M.heal_bodypart_damage(heal_brute)
+ return TRUE
to_chat(user, "You can't heal [M] with \the [src]!")
/obj/item/stack/medical/ointment
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 7692278ba3..426958b99a 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -117,6 +117,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \
new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \
+// new/datum/stack_recipe("diamond brick", /obj/item/ingot/diamond, 6, time = 100), \ not yet
))
/obj/item/stack/sheet/mineral/diamond/get_main_recipes()
@@ -145,6 +146,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \
new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("uranium ingot", /obj/item/ingot/uranium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/uranium/get_main_recipes()
@@ -177,6 +179,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \
new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \
+// new/datum/stack_recipe("plasma ingot", /obj/item/ingot/plasma, 6, time = 100), \ no
))
/obj/item/stack/sheet/mineral/plasma/get_main_recipes()
@@ -221,6 +224,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \
new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \
new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("gold ingot", /obj/item/ingot/gold, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/gold/get_main_recipes()
@@ -252,6 +256,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("silver ingot", /obj/item/ingot/silver, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/silver/get_main_recipes()
@@ -278,6 +283,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
GLOBAL_LIST_INIT(bananium_recipes, list ( \
new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \
new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("hilarious ingot", /obj/item/ingot/bananium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/bananium/get_main_recipes()
@@ -306,6 +312,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
GLOBAL_LIST_INIT(titanium_recipes, list ( \
new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \
+ new/datum/stack_recipe("titanic ingot", /obj/item/ingot/titanium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/titanium/get_main_recipes()
@@ -353,6 +360,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
*/
GLOBAL_LIST_INIT(adamantine_recipes, list(
new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount=1, res_amount=1),
+ new/datum/stack_recipe("adamant ingot", /obj/item/ingot/adamantine, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/adamantine
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 645051b7c2..c3028c2fd5 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -121,6 +121,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("iron ingot", /obj/item/ingot/iron, 6, time = 100), \
))
/obj/item/stack/sheet/metal
@@ -556,6 +557,9 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
new/datum/stack_recipe("forge", /obj/structure/destructible/cult/forge, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("archives", /obj/structure/destructible/cult/tome, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("altar", /obj/structure/destructible/cult/talisman, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("anvil", /obj/structure/anvil/obtainable/narsie, 4, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("runic ingot", /obj/item/ingot/cult, 2, time = 100), \
+ new/datum/stack_recipe("rune smith's hammer", /obj/item/melee/smith/hammer/narsie, 6), \
))
/obj/item/stack/sheet/runed_metal
@@ -618,6 +622,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("brass bar stool", /obj/structure/chair/stool/bar/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass stool", /obj/structure/chair/stool/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("brass anvil", /obj/structure/anvil/obtainable/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("brass furnace", /obj/structure/furnace/infinite/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("sender - pressure sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("sender - mech sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
@@ -629,6 +635,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("receiver - power nullifier", /obj/structure/destructible/clockwork/trap/power_nullifier, 5, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
null, \
new/datum/stack_recipe("brass flask", /obj/item/reagent_containers/food/drinks/bottle/holyoil/empty), \
+ new/datum/stack_recipe("brass smith's hammer", /obj/item/melee/smith/hammer/ratvar, 6), \
+ new/datum/stack_recipe("brass ingot", /obj/item/ingot/ratvar, 6, time = 100), \
))
/obj/item/stack/tile/brass
@@ -684,7 +692,10 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
new/datum/stack_recipe("bronze chair", /obj/structure/chair/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze bar stool", /obj/structure/chair/stool/bar/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze stool", /obj/structure/chair/stool/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
- new /datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
+ new/datum/stack_recipe("bronze anvil",/obj/structure/anvil/obtainable/bronze, 20, time = 110, one_per_turf = TRUE, on_floor = TRUE), \
+ null,
+ new/datum/stack_recipe("bronze ingot", /obj/item/ingot/bronze, 6, time = 100), \
+ new/datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
))
/obj/item/stack/sheet/bronze
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 0001494fdd..590df34cde 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -815,3 +815,18 @@
attack_verb = list("bashed", "slashes", "prods", "pokes")
fitting_swords = list(/obj/item/melee/rapier)
starting_sword = /obj/item/melee/rapier
+
+/obj/item/storage/belt/sabre/twin
+ name = "twin sheath"
+ desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai."
+ icon_state = "twinsheath"
+ item_state = "quiver" //this'll do.
+ w_class = WEIGHT_CLASS_BULKY
+ fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
+ starting_sword = null
+
+/obj/item/storage/belt/sabre/twin/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 2
+ STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 653e95c21e..3f2dc2506a 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -237,6 +237,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+/obj/item/katana/lavaland
+ desc = "Woefully underpowered in Lavaland."
+ block_chance = 30
+ force = 25 //Like a fireaxe but one handed and can block!
+
/obj/item/katana/cursed
slot_flags = null
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index f5003e035b..3de28b38e8 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -143,10 +143,18 @@
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
+#define BLACKLISTED_OBJECTS list(/obj/machinery/power/apc, /obj/machinery/airalarm, /obj/machinery/power/smes, /obj/structure/cable)
+
/obj/attack_slime(mob/living/simple_animal/slime/user)
if(!user.is_adult)
return
- attack_generic(user, rand(10, 15), "melee", 1)
+ if(src.type in BLACKLISTED_OBJECTS)
+ return
+ if(istype(src, /obj/machinery/atmospherics))
+ return
+ attack_generic(user, rand(10, 15), BRUTE, "melee", 1)
+
+#undef BLACKLISTED_OBJECTS
/obj/mech_melee_attack(obj/mecha/M)
M.do_attack_animation(src)
@@ -196,9 +204,6 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
/obj/proc/acid_processing()
. = 1
if(!(resistance_flags & ACID_PROOF))
- for(var/armour_value in armor)
- if(armour_value != "acid" && armour_value != "fire")
- armor = armor.modifyAllRatings(0 - round(sqrt(acid_level)*0.1))
if(prob(33))
playsound(loc, 'sound/items/welder.ogg', 150, 1)
take_damage(min(1 + round(sqrt(acid_level)*0.3), 300), BURN, "acid", 0)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 99b302e9ce..3abee4db33 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -304,18 +304,20 @@
/obj/proc/reskin_obj(mob/M)
if(!LAZYLEN(unique_reskin))
return
- var/dat = "Reskin options for [name]:\n"
- for(var/V in unique_reskin)
- var/output = icon2html(src, M, unique_reskin[V])
- dat += "[V]: [output]\n"
- to_chat(M, dat)
-
- var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin
- if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin)
- return
- current_skin = choice
+ var/list/skins = list()
+ for(var/S in unique_reskin)
+ skins[S] = image(icon = icon, icon_state = unique_reskin[S])
+ var/choice = show_radial_menu(M, src, skins, custom_check = CALLBACK(src, .proc/check_skinnable, M), radius = 40, require_near = TRUE)
+ if(!choice)
+ return FALSE
icon_state = unique_reskin[choice]
- to_chat(M, "[src] is now skinned as '[choice]'.")
+ current_skin = choice
+ return
+
+/obj/proc/check_skinnable(/mob/M)
+ if(current_skin || !always_reskinnable)
+ return FALSE
+ return TRUE
/obj/update_overlays()
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
index beb14ff48c..d127a87b20 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
@@ -4,25 +4,6 @@
req_access = list(ACCESS_ALL_PERSONAL_LOCKERS)
var/registered_name = null
-/obj/structure/closet/secure_closet/personal/examine(mob/user)
- . = ..()
- if(registered_name)
- . += "The display reads, \"Owned by [registered_name]\"."
-
-/obj/structure/closet/secure_closet/personal/check_access(obj/item/I)
- . = ..()
- if(!I || !istype(I))
- return
- if(istype(I,/obj/item/modular_computer/tablet))
- var/obj/item/modular_computer/tablet/ourTablet = I
- var/obj/item/computer_hardware/card_slot/card_slot = ourTablet.all_components[MC_CARD]
- if(card_slot)
- return registered_name == card_slot.stored_card.registered_name || registered_name == card_slot.stored_card2.registered_name
- var/obj/item/card/id/ID = I.GetID()
- if(ID && registered_name == ID.registered_name)
- return TRUE
- return FALSE
-
/obj/structure/closet/secure_closet/personal/PopulateContents()
..()
if(prob(50))
@@ -54,15 +35,24 @@
/obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params)
var/obj/item/card/id/I = W.GetID()
- if(!I || !istype(I))
- return ..()
- if(!can_lock(user, FALSE)) //Can't do anything if there isn't a lock!
- return
- if(I.registered_name && !registered_name)
- to_chat(user, "You claim [src].")
- registered_name = I.registered_name
+ if(istype(I))
+ if(broken)
+ to_chat(user, "It appears to be broken.")
+ return
+ if(!I || !I.registered_name)
+ return
+ if(allowed(user) || !registered_name || (istype(I) && (registered_name == I.registered_name)))
+ //they can open all lockers, or nobody owns this, or they own this locker
+ locked = !locked
+ update_icon()
+
+ if(!registered_name)
+ registered_name = I.registered_name
+ desc = "Owned by [I.registered_name]."
+ else
+ to_chat(user, "Access Denied.")
else
- ..()
+ return ..()
/obj/structure/closet/secure_closet/personal/handle_lock_addition() //If lock construction is successful we don't care what access the electronics had, so we override it
if(..())
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index 2911babd83..edc4f0c91f 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -10,6 +10,7 @@
max_mobs = 3
max_integrity = 250
mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril)
+ var/loot_type = /obj/structure/closet/crate/necropolis/tendril/all
move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
anchored = TRUE
@@ -41,7 +42,7 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/deconstruct(disassembled)
new /obj/effect/collapse(loc)
- new /obj/structure/closet/crate/necropolis/tendril(loc)
+ new loot_type(loc)
return ..()
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 0feb01c223..b012ba4e2a 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -130,6 +130,10 @@
/proc/get_sfx(soundin)
if(istext(soundin))
switch(soundin)
+ if ("explosion_creaking") // from skyrat-ss13/skyrat13/pull/3295
+ soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg')
+ if ("hull_creaking") // from skyrat-ss13/skyrat13/pull/3295
+ soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg')
if ("shatter")
soundin = pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg')
if ("explosion")
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 6552e29246..1ff056c572 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -210,7 +210,7 @@
if(!get_excited() && air.compare(enemy_air))
//testing("Active turf found. Return value of compare(): [is_active]")
set_excited(TRUE)
- SSair.active_turfs |= src
+ SSair.add_to_active_extools(src)
/turf/open/proc/GetHeatCapacity()
. = air.heat_capacity()
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 758f824727..877d236e84 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -13,7 +13,7 @@
var/icon_regular_floor = "floor" //used to remember what icon the tile should have by default
var/icon_plating = "plating"
- thermal_conductivity = 0.040
+ thermal_conductivity = 0.004
heat_capacity = 10000
intact = 1
var/broken = 0
diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm
index 03045674e4..42b4707b50 100644
--- a/code/game/turfs/simulated/floor/reinf_floor.dm
+++ b/code/game/turfs/simulated/floor/reinf_floor.dm
@@ -3,7 +3,7 @@
name = "reinforced floor"
desc = "Extremely sturdy."
icon_state = "engine"
- thermal_conductivity = 0.025
+ thermal_conductivity = 0.0025
heat_capacity = INFINITY
floor_tile = /obj/item/stack/rods
footstep = FOOTSTEP_PLATING
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index ed48c24462..5d928f377e 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -90,7 +90,7 @@
icon = 'icons/turf/walls/plasma_wall.dmi'
icon_state = "plasma"
sheet_type = /obj/item/stack/sheet/mineral/plasma
- thermal_conductivity = 0.04
+ thermal_conductivity = 0.004
canSmoothWith = list(/turf/closed/wall/mineral/plasma, /obj/structure/falsewall/plasma)
/turf/closed/wall/mineral/plasma/attackby(obj/item/W, mob/user, params)
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 9ff4f668a4..402ab36947 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -138,7 +138,7 @@
if(prob(hardness))
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
- hulk_recoil(arm, user)
+ //hulk_recoil(arm, user) // citadel edit - no, hulks are already subject to stamina combat
dismantle_wall(1)
else
diff --git a/code/game/world.dm b/code/game/world.dm
index a342200b3d..42c90d8be1 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -11,6 +11,11 @@ GLOBAL_LIST(topic_status_cache)
/world/New()
if (fexists(EXTOOLS))
call(EXTOOLS, "maptick_initialize")()
+ #ifdef EXTOOLS_LOGGING
+ call(EXTOOLS, "init_logging")()
+ else
+ CRASH("[EXTOOLS] does not exist!")
+ #endif
enable_debugger()
#ifdef REFERENCE_TRACKING
enable_reference_tracking()
@@ -20,8 +25,6 @@ GLOBAL_LIST(topic_status_cache)
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
- SetupExternalRSC()
-
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
@@ -87,17 +90,6 @@ GLOBAL_LIST(topic_status_cache)
#endif
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, cb, 10 SECONDS))
-/world/proc/SetupExternalRSC()
-#if (PRELOAD_RSC == 0)
- GLOB.external_rsc_urls = world.file2list("[global.config.directory]/external_rsc_urls.txt","\n")
- var/i=1
- while(i<=GLOB.external_rsc_urls.len)
- if(GLOB.external_rsc_urls[i])
- i++
- else
- GLOB.external_rsc_urls.Cut(i,i+1)
-#endif
-
/world/proc/SetupLogs()
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
if(!override_dir)
@@ -281,12 +273,7 @@ GLOBAL_LIST(topic_status_cache)
..()
/world/Del()
- // memory leaks bad
- var/num_deleted = 0
- for(var/datum/gas_mixture/GM)
- GM.__gasmixture_unregister()
- num_deleted++
- log_world("Deallocated [num_deleted] gas mixtures")
+ shutdown_logging() // makes sure the thread is closed before end, else we terminate
..()
/world/proc/update_status()
diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm
index d86ad792fe..2a2820d289 100644
--- a/code/modules/NTNet/network.dm
+++ b/code/modules/NTNet/network.dm
@@ -206,7 +206,7 @@
for(var/datum/ntnet_conversation/chan in chat_channels)
if(chan.id == id)
return chan
-
+
// Resets the IDS alarm
/datum/ntnet/proc/resetIDS()
intrusion_detection_alarm = FALSE
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 8cfae3820a..8421d9955d 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -1,12 +1,12 @@
////////////////////////////////
/proc/message_admins(msg)
- msg = "ADMIN LOG:[msg]"
- to_chat(GLOB.admins, msg)
+ msg = "ADMIN LOG:[msg]"
+ to_chat(GLOB.admins, msg, confidential = TRUE)
/proc/relay_msg_admins(msg)
- msg = "RELAY:[msg]"
- to_chat(GLOB.admins, msg)
+ msg = "RELAY:[msg]"
+ to_chat(GLOB.admins, msg, confidential = TRUE)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
@@ -22,7 +22,7 @@
log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].")
if(!M)
- to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.")
+ to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.", confidential = TRUE)
return
var/body = "Options for [M.key]"
@@ -65,6 +65,7 @@
body += "VV - "
if(M.mind)
body += "TP - "
+ // body += "SKILLS - "
else
body += "Init Mind - "
if (iscyborg(M))
@@ -122,6 +123,7 @@
body += "Traitor panel | "
body += "Narrate to | "
body += "Subtle message | "
+ // body += "Play sound to | "
body += "Language Menu"
if (M.client)
@@ -216,7 +218,7 @@
if (!istype(src, /datum/admins))
src = usr.client.holder
if (!istype(src, /datum/admins))
- to_chat(usr, "Error: you are not an admin!")
+ to_chat(usr, "Error: you are not an admin!", confidential = TRUE)
return
var/dat
dat = text("Admin Newscaster
Admin Newscaster Unit
")
@@ -242,7 +244,7 @@
dat+=" The newscaster recognises you as: [src.admin_signature]"
if(1)
dat+= "Station Feed Channels"
- if( isemptylist(GLOB.news_network.network_channels) )
+ if( !length(GLOB.news_network.network_channels) )
dat+="No active channels found..."
else
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
@@ -295,7 +297,7 @@
dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice. "
dat+="No further feed story additions are allowed while the D-Notice is in effect.
"
else
- if( isemptylist(src.admincaster_feed_channel.messages) )
+ if( !length(src.admincaster_feed_channel.messages) )
dat+="No feed messages found in channel... "
else
var/i = 0
@@ -317,7 +319,7 @@
dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible. "
dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it."
dat+="Select Feed channel to get Stories from: "
- if(isemptylist(GLOB.news_network.network_channels))
+ if(!length(GLOB.news_network.network_channels))
dat+="No feed channels found active... "
else
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
@@ -328,7 +330,7 @@
dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's"
dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed"
dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time."
- if(isemptylist(GLOB.news_network.network_channels))
+ if(!length(GLOB.news_network.network_channels))
dat+="No feed channels found active... "
else
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
@@ -339,7 +341,7 @@
dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \] "
dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]"
- if( isemptylist(src.admincaster_feed_channel.messages) )
+ if( !length(src.admincaster_feed_channel.messages) )
dat+="No feed messages found in channel... "
else
for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
@@ -356,7 +358,7 @@
dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice. "
dat+="No further feed story additions are allowed while the D-Notice is in effect.
"
else
- if( isemptylist(src.admincaster_feed_channel.messages) )
+ if( !length(src.admincaster_feed_channel.messages) )
dat+="No feed messages found in channel... "
else
for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
@@ -426,7 +428,6 @@
"}
if(GLOB.master_mode == "secret")
dat += "(Force Secret Mode) "
-
if(GLOB.master_mode == "dynamic")
if(SSticker.current_state <= GAME_STATE_PREGAME)
dat += "(Force Roundstart Rulesets) "
@@ -449,7 +450,6 @@
dat += ""
if(SSticker.IsRoundInProgress())
dat += "(Game Mode Panel) "
-
dat += {"
Create Object
@@ -461,7 +461,7 @@
if(marked_datum && istype(marked_datum, /atom))
dat += "Duplicate Marked Datum "
- usr << browse(dat, "window=admin2;size=210x200")
+ usr << browse(dat, "window=admin2;size=240x280")
return
/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge
@@ -475,33 +475,42 @@
if (!usr.client.holder)
return
- var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
+ var/localhost_addresses = list("127.0.0.1", "::1")
+ var/list/options = list("Regular Restart", "Regular Restart (with delay)", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
if(world.TgsAvailable())
options += "Server Restart (Kill and restart DD)";
- var/rebootconfirm
if(SSticker.admin_delay_notice)
- if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") == "Yes")
- rebootconfirm = TRUE
- else
- rebootconfirm = TRUE
- if(rebootconfirm)
- var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
- if(result)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
- switch(result)
- if("Regular Restart")
- SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
- if("Hard Restart (No Delay, No Feeback Reason)")
- to_chat(world, "World reboot - [init_by]")
- world.Reboot()
- if("Hardest Restart (No actions, just reboot)")
- to_chat(world, "Hard world reboot - [init_by]")
- world.Reboot(fast_track = TRUE)
- if("Server Restart (Kill and restart DD)")
- to_chat(world, "Server restart - [init_by]")
- world.TgsEndProcess()
+ if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") != "Yes")
+ return FALSE
+
+ var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
+ if(result)
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
+ switch(result)
+ if("Regular Restart")
+ if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
+ if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart")
+ return FALSE
+ SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
+ if("Regular Restart (with delay)")
+ var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null
+ if(!delay)
+ return FALSE
+ if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
+ if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart")
+ return FALSE
+ SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay * 10)
+ if("Hard Restart (No Delay, No Feeback Reason)")
+ to_chat(world, "World reboot - [init_by]")
+ world.Reboot()
+ if("Hardest Restart (No actions, just reboot)")
+ to_chat(world, "Hard world reboot - [init_by]")
+ world.Reboot(fast_track = TRUE)
+ if("Server Restart (Kill and restart DD)")
+ to_chat(world, "Server restart - [init_by]")
+ world.TgsEndProcess()
/datum/admins/proc/end_round()
set category = "Server"
@@ -529,7 +538,7 @@
if(message)
if(!check_rights(R_SERVER,0))
message = adminscrub(message,500)
- to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]")
+ to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]", confidential = TRUE)
log_admin("Announce: [key_name(usr)] : [message]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -551,7 +560,7 @@
else
message_admins("[key_name(usr)] set the admin notice.")
log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]")
- to_chat(world, "Admin Notice:\n \t [new_admin_notice]")
+ to_chat(world, "Admin Notice:\n \t [new_admin_notice]", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
GLOB.admin_notice = new_admin_notice
return
@@ -598,20 +607,29 @@
set desc="Start the round RIGHT NOW"
set name="Start Now"
if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP)
- SSticker.start_immediately = TRUE
- log_admin("[usr.key] has started the game.")
- var/msg = ""
- if(SSticker.current_state == GAME_STATE_STARTUP)
- msg = " (The server is still setting up, but the round will be \
- started as soon as possible.)"
- message_admins("\
- [usr.key] has started the game.[msg]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return 1
+ if(!SSticker.start_immediately)
+ var/localhost_addresses = list("127.0.0.1", "::1")
+ if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
+ if(alert("Are you sure you want to start the round?","Start Now","Start Now","Cancel") != "Start Now")
+ return FALSE
+ SSticker.start_immediately = TRUE
+ log_admin("[usr.key] has started the game.")
+ var/msg = ""
+ if(SSticker.current_state == GAME_STATE_STARTUP)
+ msg = " (The server is still setting up, but the round will be \
+ started as soon as possible.)"
+ message_admins("[usr.key] has started the game.[msg]")
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ return TRUE
+ SSticker.start_immediately = FALSE
+ SSticker.SetTimeLeft(1800)
+ to_chat(world, "The game will start in 180 seconds.")
+ SEND_SOUND(world, sound(get_announcer_sound("attention")))
+ message_admins("[usr.key] has cancelled immediate game start. Game will start in 180 seconds.")
+ log_admin("[usr.key] has cancelled immediate game start.")
else
to_chat(usr, "Error: Start Now: Game has already started.")
-
- return 0
+ return FALSE
/datum/admins/proc/toggleenter()
set category = "Server"
@@ -619,9 +637,9 @@
set name="Toggle Entering"
GLOB.enter_allowed = !( GLOB.enter_allowed )
if (!( GLOB.enter_allowed ))
- to_chat(world, "New players may no longer enter the game.")
+ to_chat(world, "New players may no longer enter the game.", confidential = TRUE)
else
- to_chat(world, "New players may now enter the game.")
+ to_chat(world, "New players may now enter the game.", confidential = TRUE)
log_admin("[key_name(usr)] toggled new player game entering.")
message_admins("[key_name_admin(usr)] toggled new player game entering.")
world.update_status()
@@ -634,9 +652,9 @@
var/alai = CONFIG_GET(flag/allow_ai)
CONFIG_SET(flag/allow_ai, !alai)
if (alai)
- to_chat(world, "The AI job is no longer chooseable.")
+ to_chat(world, "The AI job is no longer chooseable.", confidential = TRUE)
else
- to_chat(world, "The AI job is chooseable now.")
+ to_chat(world, "The AI job is chooseable now.", confidential = TRUE)
log_admin("[key_name(usr)] toggled AI allowed.")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -654,7 +672,7 @@
aiPlayer.end_multicam()
log_admin("[key_name(usr)] toggled AI multicam.")
world.update_status()
- to_chat(GLOB.ai_list | GLOB.admins, "The AI [almcam ? "no longer" : "now"] has multicam.")
+ to_chat(GLOB.ai_list | GLOB.admins, "The AI [almcam ? "no longer" : "now"] has multicam.", confidential = TRUE)
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Multicam", "[!almcam ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleaban()
@@ -664,9 +682,9 @@
var/new_nores = !CONFIG_GET(flag/norespawn)
CONFIG_SET(flag/norespawn, new_nores)
if (!new_nores)
- to_chat(world, "You may now respawn.")
+ to_chat(world, "You may now respawn.", confidential = TRUE)
else
- to_chat(world, "You may no longer respawn :(")
+ to_chat(world, "You may no longer respawn :(", confidential = TRUE)
message_admins("[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"].")
log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].")
world.update_status()
@@ -675,7 +693,7 @@
/datum/admins/proc/delay()
set category = "Server"
set desc="Delay the game start"
- set name="Delay pre-game"
+ set name="Delay Pre-Game"
var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null
if(SSticker.current_state > GAME_STATE_PREGAME)
@@ -683,11 +701,12 @@
if(newtime)
newtime = newtime*10
SSticker.SetTimeLeft(newtime)
+ SSticker.start_immediately = FALSE
if(newtime < 0)
- to_chat(world, "The game start has been delayed.")
+ to_chat(world, "The game start has been delayed.", confidential = TRUE)
log_admin("[key_name(usr)] delayed the round start.")
else
- to_chat(world, "The game will start in [DisplayTimeText(newtime)].")
+ to_chat(world, "The game will start in [DisplayTimeText(newtime)].", confidential = TRUE)
SEND_SOUND(world, sound(get_announcer_sound("attention")))
log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -724,20 +743,28 @@
set desc = "(atom path) Spawn an atom"
set name = "Spawn"
- if(!check_rights(R_SPAWN))
+ if(!check_rights(R_SPAWN) || !object)
+ return
+
+ var/list/preparsed = splittext(object,":")
+ var/path = preparsed[1]
+ var/amount = 1
+ if(preparsed.len > 1)
+ amount = clamp(text2num(preparsed[2]),1, 50) //50 at a time!
+
+ var/chosen = pick_closest_path(path)
+ if(!chosen)
return
var/turf/T = get_turf(usr)
- var/chosen = pick_closest_path(object)
- if(!chosen)
- return
if(ispath(chosen, /turf))
T.ChangeTurf(chosen)
else
- var/atom/A = new chosen(T)
- A.flags_1 |= ADMIN_SPAWNED_1
+ for(var/i in 1 to amount)
+ var/atom/A = new chosen(T)
+ A.flags_1 |= ADMIN_SPAWNED_1
- log_admin("[key_name(usr)] spawned [chosen] at [AREACOORD(usr)]")
+ log_admin("[key_name(usr)] spawned [amount] x [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/podspawn_atom(object as text)
@@ -782,20 +809,18 @@
log_admin("[key_name(usr)] spawned cargo pack [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Cargo") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/datum/admins/proc/show_traitor_panel(mob/M in GLOB.mob_list)
+/datum/admins/proc/show_traitor_panel(mob/target_mob in GLOB.mob_list)
set category = "Admin"
set desc = "Edit mobs's memory and role"
set name = "Show Traitor Panel"
-
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ var/datum/mind/target_mind = target_mob.mind
+ if(!target_mind)
+ to_chat(usr, "This mob has no mind!", confidential = TRUE)
return
- if(!M.mind)
- to_chat(usr, "This mob has no mind!")
+ if(!istype(target_mob) && !istype(target_mind))
+ to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE)
return
-
- M.mind.traitor_panel()
+ target_mind.traitor_panel()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -805,9 +830,9 @@
set name="Toggle tinted welding helmes"
GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh )
if (GLOB.tinted_weldhelh)
- to_chat(world, "The tinted_weldhelh has been enabled!")
+ to_chat(world, "The tinted_weldhelh has been enabled!", confidential = TRUE)
else
- to_chat(world, "The tinted_weldhelh has been disabled!")
+ to_chat(world, "The tinted_weldhelh has been disabled!", confidential = TRUE)
log_admin("[key_name(usr)] toggled tinted_weldhelh.")
message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Tinted Welding Helmets", "[GLOB.tinted_weldhelh ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -819,9 +844,9 @@
var/new_guest_ban = !CONFIG_GET(flag/guest_ban)
CONFIG_SET(flag/guest_ban, new_guest_ban)
if (new_guest_ban)
- to_chat(world, "Guests may no longer enter the game.")
+ to_chat(world, "Guests may no longer enter the game.", confidential = TRUE)
else
- to_chat(world, "Guests may now enter the game.")
+ to_chat(world, "Guests may now enter the game.", confidential = TRUE)
log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
message_admins("[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -832,37 +857,37 @@
var/mob/living/silicon/S = i
ai_number++
if(isAI(S))
- to_chat(usr, "AI [key_name(S, usr)]'s laws:")
+ to_chat(usr, "AI [key_name(S, usr)]'s laws:", confidential = TRUE)
else if(iscyborg(S))
var/mob/living/silicon/robot/R = S
- to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:")
+ to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:", confidential = TRUE)
else if (ispAI(S))
- to_chat(usr, "pAI [key_name(S, usr)]'s laws:")
+ to_chat(usr, "pAI [key_name(S, usr)]'s laws:", confidential = TRUE)
else
- to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:")
+ to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:", confidential = TRUE)
if (S.laws == null)
- to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.")
+ to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.", confidential = TRUE)
else
S.laws.show_laws(usr)
if(!ai_number)
- to_chat(usr, "No AIs located" )
+ to_chat(usr, "No AIs located" , confidential = TRUE)
/datum/admins/proc/output_all_devil_info()
var/devil_number = 0
for(var/datum/mind/D in SSticker.mode.devils)
devil_number++
var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil)
- to_chat(usr, "Devil #[devil_number]:
"
dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"] "
+ dat += "Genital examine text:[(cit_toggles & GENITAL_EXAMINE) ? "Enabled" : "Disabled"] "
+ dat += "Vore examine text:[(cit_toggles & VORE_EXAMINE) ? "Enabled" : "Disabled"] "
dat += "Voracious MediHound sleepers:[(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"] "
dat += "Hear Vore Sounds:[(cit_toggles & EATING_NOISES) ? "Yes" : "No"] "
dat += "Hear Vore Digestion Sounds:[(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"] "
@@ -1240,6 +1252,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
for(var/V in all_quirks)
var/datum/quirk/T = SSquirks.quirks[V]
bal -= initial(T.value)
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ return bal + 1 //max 1 point regardless of how many prosthetics
return bal
/datum/preferences/proc/GetPositiveQuirkCount()
@@ -1457,6 +1472,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!isnull(msg))
features["ooc_notes"] = msg
+ if("hide_ckey")
+ hide_ckey = !hide_ckey
+ if(user)
+ user.mind?.hide_ckey = hide_ckey
+
if("hair")
var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null
if(new_hair)
@@ -1494,6 +1514,29 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("cycle_bg")
bgstate = next_list_item(bgstate, bgstate_options)
+ if("modify_limbs")
+ var/limb_type = input(user, "Choose the limb to modify:", "Character Preference") as null|anything in LOADOUT_ALLOWED_LIMB_TARGETS
+ if(limb_type)
+ var/modification_type = input(user, "Choose the modification to the limb:", "Character Preference") as null|anything in LOADOUT_LIMBS
+ if(modification_type)
+ if(modification_type == LOADOUT_LIMB_PROSTHETIC)
+ var/prosthetic_type = input(user, "Choose the type of prosthetic", "Character Preference") as null|anything in (list("prosthetic") + GLOB.prosthetic_limb_types)
+ if(prosthetic_type)
+ var/number_of_prosthetics = 0
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ number_of_prosthetics += 1
+ if(number_of_prosthetics >= MAXIMUM_LOADOUT_PROSTHETICS && !(limb_type in modified_limbs && modified_limbs[limb_type][1] == LOADOUT_LIMB_PROSTHETIC))
+ to_chat(user, "You can only have up to two prosthetic limbs!")
+ else
+ //save the actual prosthetic data
+ modified_limbs[limb_type] = list(modification_type, prosthetic_type)
+ else
+ if(modification_type == LOADOUT_LIMB_NORMAL)
+ modified_limbs -= limb_type
+ else
+ modified_limbs[limb_type] = list(modification_type)
+
if("underwear")
var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_list
if(new_underwear)
@@ -2379,6 +2422,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
parent.mob.hud_used.update_parallax_pref(parent.mob)
// Citadel edit - Prefs don't work outside of this. :c
+
+ if("genital_examine")
+ cit_toggles ^= GENITAL_EXAMINE
+
+ if("vore_examine")
+ cit_toggles ^= VORE_EXAMINE
+
if("hound_sleeper")
cit_toggles ^= MEDIHOUND_SLEEPER
@@ -2488,7 +2538,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ShowChoices(user)
return 1
-/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE)
+/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE, initial_spawn = FALSE)
if(be_random_name)
real_name = pref_species.random_name(gender)
@@ -2586,6 +2636,34 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(custom_speech_verb != "default")
character.dna.species.say_mod = custom_speech_verb
+ //limb stuff, only done when initially spawning in
+ if(initial_spawn)
+ //delete any existing prosthetic limbs to make sure no remnant prosthetics are left over
+ for(var/obj/item/bodypart/part in character.bodyparts)
+ if(part.status == BODYPART_ROBOTIC)
+ qdel(part)
+ character.regenerate_limbs() //regenerate limbs so now you only have normal limbs
+ for(var/modified_limb in modified_limbs)
+ var/modification = modified_limbs[modified_limb][1]
+ var/obj/item/bodypart/old_part = character.get_bodypart(modified_limb)
+ if(modification == LOADOUT_LIMB_PROSTHETIC)
+ var/obj/item/bodypart/new_limb
+ switch(modified_limb)
+ if(BODY_ZONE_L_ARM)
+ new_limb = new/obj/item/bodypart/l_arm/robot/surplus(character)
+ if(BODY_ZONE_R_ARM)
+ new_limb = new/obj/item/bodypart/r_arm/robot/surplus(character)
+ if(BODY_ZONE_L_LEG)
+ new_limb = new/obj/item/bodypart/l_leg/robot/surplus(character)
+ if(BODY_ZONE_R_LEG)
+ new_limb = new/obj/item/bodypart/r_leg/robot/surplus(character)
+ var/prosthetic_type = modified_limbs[modified_limb][2]
+ if(prosthetic_type != "prosthetic") //lets just leave the old sprites as they are
+ new_limb.icon = file("icons/mob/augmentation/cosmetic_prosthetic/[prosthetic_type].dmi")
+ new_limb.replace_limb(character)
+ qdel(old_part)
+
+ character.regenerate_icons()
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
@@ -2594,6 +2672,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.update_body()
character.update_hair()
+/datum/preferences/proc/post_copy_to(mob/living/carbon/human/character)
+ //if no legs, and not a paraplegic or a slime, give them a free wheelchair
+ if(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !character.has_quirk(/datum/quirk/paraplegic) && !isjellyperson(character))
+ if(character.buckled)
+ character.buckled.unbuckle_mob(character)
+ var/turf/T = get_turf(character)
+ var/obj/structure/chair/spawn_chair = locate() in T
+ var/obj/vehicle/ridden/wheelchair/wheels = new(T)
+ if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
+ wheels.setDir(spawn_chair.dir)
+ wheels.buckle_mob(character)
+
/datum/preferences/proc/get_default_name(name_id)
switch(name_id)
if("human")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index f1df0fe158..fb8a6b17bd 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -519,8 +519,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["scars3"] >> scars_list["3"]
S["scars4"] >> scars_list["4"]
S["scars5"] >> scars_list["5"]
+ var/limbmodstr
+ S["modified_limbs"] >> limbmodstr
+ if(length(limbmodstr))
+ modified_limbs = safe_json_decode(limbmodstr)
+ else
+ modified_limbs = list()
S["chosen_limb_id"] >> chosen_limb_id
-
+ S["hide_ckey"] >> hide_ckey //saved per-character
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
@@ -852,8 +858,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_ooc_notes"], features["ooc_notes"])
- WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
-
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
@@ -866,6 +870,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["joblessrole"] , joblessrole)
//Write prefs
WRITE_FILE(S["job_preferences"] , job_preferences)
+ WRITE_FILE(S["hide_ckey"] , hide_ckey)
//Quirks
WRITE_FILE(S["all_quirks"] , all_quirks)
@@ -880,6 +885,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["scars3"] , scars_list["3"])
WRITE_FILE(S["scars4"] , scars_list["4"])
WRITE_FILE(S["scars5"] , scars_list["5"])
+ if(islist(modified_limbs))
+ WRITE_FILE(S["modified_limbs"] , safe_json_encode(modified_limbs))
+ WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
+
//gear loadout
if(chosen_gear.len)
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 951e36f9a6..e16a491c61 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -145,8 +145,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)()
to_chat(usr, "You will no longer hear sounds uploaded by admins")
usr.stop_sound_channel(CHANNEL_ADMIN)
var/client/C = usr.client
- if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
- C.chatOutput.stopMusic()
+ C?.tgui_panel?.stop_music()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Hearing Midis", "[usr.client.prefs.toggles & SOUND_MIDI ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/verbs/menu/Settings/Sound/togglemidis/Get_checked(client/C)
return C.prefs.toggles & SOUND_MIDI
@@ -234,8 +233,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)()
set desc = "Stop Current Sounds"
SEND_SOUND(usr, sound(null))
var/client/C = usr.client
- if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
- C.chatOutput.stopMusic()
+ C?.tgui_panel?.stop_music()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Stop Self Sounds")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index fa1145ecea..dd1ebd74f9 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -158,88 +158,6 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
else
to_chat(src, "There are no admin notices at the moment.")
-/client/verb/fix_chat()
- set name = "Fix chat"
- set category = "OOC"
- if (!chatOutput || !istype(chatOutput))
- var/action = alert(src, "Invalid Chat Output data found!\nRecreate data?", "Wot?", "Recreate Chat Output data", "Cancel")
- if (action != "Recreate Chat Output data")
- return
- chatOutput = new /datum/chatOutput(src)
- chatOutput.start()
- action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
- if (action == "Fixed")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum")
- else
- chatOutput.load()
- action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
- if (action == "Yes")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum and forcing a load()")
- else
- action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
- if (action == "Switch to old chat")
- winset(src, "output", "is-visible=true;is-disabled=false")
- winset(src, "browseroutput", "is-visible=false")
- log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after recreating the chatOutput and forcing a load()")
-
- else if (chatOutput.loaded)
- var/action = alert(src, "ChatOutput seems to be loaded\nDo you want me to force a reload, wiping the chat log or just refresh the chat window because it broke/went away?", "Hmmm", "Force Reload", "Refresh", "Cancel")
- switch (action)
- if ("Force Reload")
- chatOutput.loaded = FALSE
- chatOutput.start() //this is likely to fail since it asks , but we should try it anyways so we know.
- action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
- if (action == "Fixed")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a start()")
- else
- chatOutput.load()
- action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
- if (action == "Yes")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
- else
- action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
- if (action == "Switch to old chat")
- winset(src, "output", "is-visible=true;is-disabled=false")
- winset(src, "browseroutput", "is-visible=false")
- log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a start() and forcing a load()")
-
- if ("Refresh")
- chatOutput.showChat()
- action = alert(src, "Goon chat refreshing, wait a bit and tell me if it's fixed", "", "Fixed", "Nope, force a reload")
- if (action == "Fixed")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a show()")
- else
- chatOutput.loaded = FALSE
- chatOutput.load()
- action = alert(src, "How about now? (give it a moment)", "", "Yes", "No")
- if (action == "Yes")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
- else
- action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
- if (action == "Switch to old chat")
- winset(src, "output", "is-visible=true;is-disabled=false")
- winset(src, "browseroutput", "is-visible=false")
- log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a show() and forcing a load()")
- return
-
- else
- chatOutput.start()
- var/action = alert(src, "Manually loading Chat, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
- if (action == "Fixed")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start()")
- else
- chatOutput.load()
- alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
- if (action == "Yes")
- log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start() and forcing a load()")
- else
- action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
- if (action == "Switch to old chat")
- winset(src, "output", list2params(list("on-show" = "", "is-disabled" = "false", "is-visible" = "true")))
- winset(src, "browseroutput", "is-disabled=true;is-visible=false")
- log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after manually calling start() and forcing a load()")
-
-
/client/verb/motd()
set name = "MOTD"
@@ -362,3 +280,9 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
pct += delta
winset(src, "mainwindow.split", "splitter=[pct]")
+
+/client/verb/fix_stat_panel()
+ set name = "Fix Stat Panel"
+ set hidden = TRUE
+
+ init_verbs()
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index cf7dbc7462..6732f1c86c 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -80,7 +80,9 @@
item_state = "hostrench"
flags_inv = 0
strip_delay = 80
- unique_reskin = list("Coat" = "hostrench", "Cloak" = "trenchcloak")
+ unique_reskin = list("Coat" = "hostrench",
+ "Cloak" = "trenchcloak"
+ )
/obj/item/clothing/suit/armor/vest/warden
name = "warden's jacket"
diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm
index 632d59187f..f496468371 100644
--- a/code/modules/clothing/suits/toggles.dm
+++ b/code/modules/clothing/suits/toggles.dm
@@ -5,9 +5,9 @@
var/obj/item/clothing/head/hooded/hood
var/hoodtype = /obj/item/clothing/head/hooded/winterhood //so the chaplain hoodie or other hoodies can override this
-/obj/item/clothing/suit/hooded/New()
+/obj/item/clothing/suit/hooded/Initialize()
+ . = ..()
hood = MakeHelmet()
- ..()
/obj/item/clothing/suit/hooded/Destroy()
. = ..()
@@ -48,7 +48,7 @@
/obj/item/clothing/suit/hooded/update_icon_state()
icon_state = "[initial(icon_state)]"
- if(ishuman(hood.loc))
+ if(ishuman(hood?.loc))
var/mob/living/carbon/human/H = hood.loc
if(H.head == hood)
icon_state += "_t"
@@ -131,8 +131,8 @@
//Hardsuit toggle code
/obj/item/clothing/suit/space/hardsuit/Initialize()
- helmet = MakeHelmet()
. = ..()
+ helmet = MakeHelmet()
/obj/item/clothing/suit/space/hardsuit/Destroy()
if(helmet)
diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index 18cd104ff8..f8292738ee 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -267,7 +267,7 @@
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON|USE_TAUR_CLIP_MASK
/obj/item/clothing/under/costume/christmas/croptop/green
- name = "green feminine christmas suit"
+ name = "green croptop christmas suit"
desc = "A simple green christmas suit. Smells minty!"
icon_state = "christmasfemaleg"
item_state = "christmasfemaleg"
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 27fb0cc00d..5d750f3c75 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -118,7 +118,6 @@
icon_state = "plasmaman"
item_state = "plasmaman"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95)
- slowdown = 1
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
mutantrace_variation = USE_TAUR_CLIP_MASK
can_adjust = FALSE
@@ -326,3 +325,22 @@
desc = "A blue bathrobe."
icon_state = "bathrobe"
item_state = "bathrobe"
+
+/obj/item/clothing/under/misc/mechsuitred
+ name = "red mech suit"
+ desc = "What are you, stupid?"
+ icon_state = "red_mech_suit"
+ item_state = "red_mech_suit"
+
+/obj/item/clothing/under/misc/mechsuitwhite
+ name = "white mech suit"
+ desc = "...Mom?"
+ icon_state = "white_mech_suit"
+ item_state = "white_mech_suit"
+
+/obj/item/clothing/under/misc/mechsuitblue
+ name = "blue mech suit"
+ desc = "Get in the damn robot already!"
+ icon_state = "blue_mech_suit"
+ item_state = "blue_mech_suit"
+
diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm
index 3fd83899c9..64c07f5a38 100644
--- a/code/modules/emoji/emoji_parse.dm
+++ b/code/modules/emoji/emoji_parse.dm
@@ -17,7 +17,7 @@
if(search)
emoji = lowertext(copytext(text, pos + length(text[pos]), search))
var/isthisapath = (emoji[1] == "/") && text2path(emoji)
- var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
var/tag = sheet.icon_tag("emoji-[emoji]")
if(tag)
parsed += "[tag]" //evil way of enforcing 16x16
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index 395b3b88a5..7f0dedaab6 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -4,7 +4,6 @@
max_occurrences = 1
weight = 5
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_bluespace
startWhen = 3
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index a9a7ed50b9..8047976330 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -5,7 +5,6 @@
min_players = 10
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_flux
startWhen = 10
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index cabd7face8..7d2bb33889 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -4,7 +4,6 @@
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_grav
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 8c8fbd6d36..350c8fc946 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -4,7 +4,6 @@
max_occurrences = 5
weight = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_pyro
startWhen = 3
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 96d084873d..e2a4ceadf3 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -5,7 +5,6 @@
min_players = 20
max_occurrences = 2
weight = 5
- gamemode_blacklist = list("dynamic")
/datum/round_event/anomaly/anomaly_vortex
startWhen = 10
diff --git a/code/modules/events/brain_trauma.dm b/code/modules/events/brain_trauma.dm
index 3e8182a827..75c514774c 100644
--- a/code/modules/events/brain_trauma.dm
+++ b/code/modules/events/brain_trauma.dm
@@ -3,6 +3,13 @@
typepath = /datum/round_event/brain_trauma
weight = 25
+/datum/round_event_control/brain_trauma/canSpawnEvent(var/players_amt, var/gamemode)
+ var/list/enemy_roles = list("Medical Doctor","Chief Medical Officer","Paramedic")
+ for (var/mob/M in GLOB.alive_mob_list)
+ if(M.stat != DEAD && (M.mind?.assigned_role in enemy_roles))
+ return TRUE
+ return FALSE
+
/datum/round_event/brain_trauma
fakeable = FALSE
@@ -14,19 +21,21 @@
continue
if(!H.getorgan(/obj/item/organ/brain)) // If only I had a brain
continue
-
+ if(HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS))
+ continue
+ if(!is_station_level(H.z))
+ continue
traumatize(H)
break
/datum/round_event/brain_trauma/proc/traumatize(mob/living/carbon/human/H)
var/resistance = pick(
65;TRAUMA_RESILIENCE_BASIC,
- 30;TRAUMA_RESILIENCE_SURGERY,
- 5;TRAUMA_RESILIENCE_LOBOTOMY)
+ 35;TRAUMA_RESILIENCE_SURGERY)
var/trauma_type = pickweight(list(
- BRAIN_TRAUMA_MILD = 60,
- BRAIN_TRAUMA_SEVERE = 30,
+ BRAIN_TRAUMA_MILD = 80,
+ BRAIN_TRAUMA_SEVERE = 10,
BRAIN_TRAUMA_SPECIAL = 10
))
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 1c88e68377..7c55bbfcd1 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -5,7 +5,6 @@
min_players = 15
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
/datum/round_event/brand_intelligence
announceWhen = 21
@@ -65,7 +64,7 @@
originMachine.visible_message("[originMachine] beeps and seems lifeless.")
kill()
return
- vendingMachines = removeNullsFromList(vendingMachines)
+ vendingMachines = listclearnulls(vendingMachines)
if(!vendingMachines.len) //if every machine is infected
for(var/obj/machinery/vending/upriser in infectedMachines)
if(prob(70) && !QDELETED(upriser))
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 2c553fc8a7..d08e6267a0 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -5,7 +5,6 @@
min_players = 2
earliest_start = 10 MINUTES
max_occurrences = 6
- gamemode_blacklist = list("dynamic")
/datum/round_event/carp_migration
announceWhen = 3
diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm
index 45fa1c8a01..cb62e0df22 100644
--- a/code/modules/events/communications_blackout.dm
+++ b/code/modules/events/communications_blackout.dm
@@ -2,7 +2,6 @@
name = "Communications Blackout"
typepath = /datum/round_event/communications_blackout
weight = 30
- gamemode_blacklist = list("dynamic")
/datum/round_event/communications_blackout
announceWhen = 1
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 860685c787..eb7edcafbf 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -5,7 +5,6 @@
max_occurrences = 1000
earliest_start = 0 MINUTES
alert_observers = FALSE
- gamemode_blacklist = list("dynamic")
/datum/round_event/space_dust
startWhen = 1
@@ -29,4 +28,4 @@
fakeable = FALSE
/datum/round_event/sandstorm/tick()
- spawn_meteors(10, GLOB.meteorsC)
\ No newline at end of file
+ spawn_meteors(10, GLOB.meteorsC)
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index 5e5e318e3c..b850b4db62 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -5,7 +5,6 @@
min_players = 5
weight = 40
alert_observers = FALSE
- gamemode_blacklist = list("dynamic")
/datum/round_event/electrical_storm
var/lightsoutAmount = 1
diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm
index 77520198e8..cebf1ed14b 100644
--- a/code/modules/events/fake_virus.dm
+++ b/code/modules/events/fake_virus.dm
@@ -6,7 +6,7 @@
/datum/round_event/fake_virus/start()
var/list/fake_virus_victims = list()
for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list))
- if(!H.client || H.stat == DEAD || H.InCritical())
+ if(!H.client || H.stat == DEAD || H.InCritical() || HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS))
continue
fake_virus_victims += H
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index b3bc571a4a..8c33e69107 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -4,7 +4,6 @@
weight = 20
max_occurrences = 2
min_players = 40 // To avoid shafting lowpop
- gamemode_blacklist = list("dynamic")
/datum/round_event/heart_attack/start()
var/list/heart_attack_contestants = list()
@@ -20,4 +19,4 @@
var/mob/living/carbon/human/winner = pickweight(heart_attack_contestants)
var/datum/disease/D = new /datum/disease/heart_failure()
winner.ForceContractDisease(D, FALSE, TRUE)
- announce_to_ghosts(winner)
\ No newline at end of file
+ announce_to_ghosts(winner)
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index b15e9d1f99..c75aff0c4a 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -3,7 +3,6 @@
/datum/round_event_control/ion_storm
name = "Ion Storm"
typepath = /datum/round_event/ion_storm
- gamemode_blacklist = list("dynamic")
weight = 15
min_players = 2
diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm
index c594d7b3c0..d7d8f1aec8 100644
--- a/code/modules/events/major_dust.dm
+++ b/code/modules/events/major_dust.dm
@@ -2,7 +2,6 @@
name = "Major Space Dust"
typepath = /datum/round_event/meteor_wave/major_dust
weight = 8
- gamemode_blacklist = list("dynamic")
/datum/round_event/meteor_wave/major_dust
wave_name = "space dust"
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index 7763f9950d..e69af1df13 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -10,7 +10,6 @@
min_players = 15
max_occurrences = 3
earliest_start = 25 MINUTES
- gamemode_blacklist = list("dynamic")
/datum/round_event/meteor_wave
startWhen = 6
@@ -23,7 +22,7 @@
/datum/round_event/meteor_wave/setup()
announceWhen = 1
- startWhen = rand(90, 180) // Apparently it is by 2 seconds, so 90 is actually 180 seconds, and 180 is 360 seconds. So this is 3-6 minutes
+ startWhen = 150 // 5 minutes
if(GLOB.singularity_counter)
startWhen *= 1 - min(GLOB.singularity_counter * SINGULO_BEACON_DISTURBANCE, SINGULO_BEACON_MAX_DISTURBANCE)
endWhen = startWhen + 60
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index 6e5512a617..698f5130f1 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -2,7 +2,6 @@
name = "Spawn Nightmare"
typepath = /datum/round_event/ghost_role/nightmare
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
min_players = 20
/datum/round_event/ghost_role/nightmare
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 9ab5e8d517..4cbfb8ae9a 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -5,7 +5,7 @@
max_occurrences = 1
min_players = 10
earliest_start = 30 MINUTES
- gamemode_blacklist = list("nuclear","dynamic")
+ gamemode_blacklist = list("nuclear")
/datum/round_event_control/pirates/preRunEvent()
if (!SSmapping.empty_space)
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 457b5bd4ec..5ef30d0030 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -4,7 +4,6 @@
weight = 2
min_players = 15
earliest_start = 30 MINUTES
- gamemode_blacklist = list("dynamic")
/datum/round_event/portal_storm/syndicate_shocktroop
boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2)
diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm
index 22e475a8ef..6bedce6b4b 100644
--- a/code/modules/events/processor_overload.dm
+++ b/code/modules/events/processor_overload.dm
@@ -3,7 +3,6 @@
typepath = /datum/round_event/processor_overload
weight = 15
min_players = 20
- gamemode_blacklist = list("dynamic")
/datum/round_event/processor_overload
announceWhen = 1
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index 0a5bedb464..36e16bb2c9 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -2,7 +2,6 @@
name = "Radiation Storm"
typepath = /datum/round_event/radiation_storm
max_occurrences = 1
- gamemode_blacklist = list("dynamic")
/datum/round_event/radiation_storm
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index d1c327e0f7..23ce6ce730 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -2,7 +2,6 @@
name = "Spider Infestation"
typepath = /datum/round_event/spider_infestation
weight = 5
- gamemode_blacklist = list("dynamic")
max_occurrences = 1
min_players = 15
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index dc672cec45..cd7b23a577 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -3,7 +3,6 @@
typepath = /datum/round_event/vent_clog
weight = 10
max_occurrences = 3
- gamemode_blacklist = list("dynamic")
min_players = 25
/datum/round_event/vent_clog
diff --git a/code/modules/events/wizard/ghost.dm b/code/modules/events/wizard/ghost.dm
index d5366c5769..c288953efb 100644
--- a/code/modules/events/wizard/ghost.dm
+++ b/code/modules/events/wizard/ghost.dm
@@ -21,6 +21,6 @@
/datum/round_event/wizard/possession/start()
for(var/mob/dead/observer/G in GLOB.player_list)
- G.verbs += /mob/dead/observer/verb/boo
- G.verbs += /mob/dead/observer/verb/possess
+ add_verb(G, /mob/dead/observer/verb/boo)
+ add_verb(G, /mob/dead/observer/verb/possess)
to_chat(G, "You suddenly feel a welling of new spooky powers...")
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index 1b4716b407..0920c9ccb6 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -4,7 +4,6 @@
max_occurrences = 3
weight = 2
min_players = 2
- gamemode_blacklist = list("dynamic")
/datum/round_event/wormholes
diff --git a/code/modules/food_and_drinks/food/snacks_cake.dm b/code/modules/food_and_drinks/food/snacks_cake.dm
index 1117dbc3d3..be07826a7f 100644
--- a/code/modules/food_and_drinks/food/snacks_cake.dm
+++ b/code/modules/food_and_drinks/food/snacks_cake.dm
@@ -133,7 +133,7 @@
slices_num = 5
bonus_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/nutriment/vitamin = 10)
tastes = list("cake" = 5, "sweetness" = 2, "unbearable sourness" = 2)
- foodtype = GRAIN | DAIRY | FRUIT | SUGAR
+ foodtype = GRAIN | DAIRY | FRUIT | SUGAR | ANTITOXIC
/obj/item/reagent_containers/food/snacks/cakeslice/lime
name = "lime cake slice"
@@ -141,7 +141,7 @@
icon_state = "limecake_slice"
filling_color = "#00FF00"
tastes = list("cake" = 5, "sweetness" = 2, "unbearable sourness" = 2)
- foodtype = GRAIN | DAIRY | FRUIT | SUGAR
+ foodtype = GRAIN | DAIRY | FRUIT | SUGAR | ANTITOXIC
/obj/item/reagent_containers/food/snacks/store/cake/lemon
name = "lemon cake"
diff --git a/code/modules/food_and_drinks/food/snacks_frozen.dm b/code/modules/food_and_drinks/food/snacks_frozen.dm
index 39c9c6c04f..32a89dbd58 100644
--- a/code/modules/food_and_drinks/food/snacks_frozen.dm
+++ b/code/modules/food_and_drinks/food/snacks_frozen.dm
@@ -103,7 +103,7 @@
icon_state = "lime_sc"
list_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/limejuice = 5)
tastes = list("ice" = 1, "water" = 1, "limes" = 5)
- foodtype = FRUIT
+ foodtype = FRUIT | ANTITOXIC
/obj/item/reagent_containers/food/snacks/snowcones/lemon
name = "lemon snowcone"
@@ -191,7 +191,7 @@
icon_state = "fruitsalad_sc"
list_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/lemonjuice = 5, /datum/reagent/consumable/limejuice = 5, /datum/reagent/consumable/orangejuice = 5)
tastes = list("ice" = 1, "water" = 1, "oranges" = 5, "limes" = 5, "lemons" = 5, "citrus" = 5, "salad" = 5)
- foodtype = FRUIT
+ foodtype = FRUIT | ANTITOXIC
/obj/item/reagent_containers/food/snacks/snowcones/pineapple
name = "pineapple snowcone"
diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm
index e0697cecea..b771a90907 100644
--- a/code/modules/food_and_drinks/food/snacks_other.dm
+++ b/code/modules/food_and_drinks/food/snacks_other.dm
@@ -232,7 +232,7 @@
list_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/toxin = 1, /datum/reagent/iron = 10, /datum/reagent/consumable/sugar = 5, /datum/reagent/medicine/omnizine = 2) //lollipop, but vitamins = toxins
filling_color = "#00800"
tastes = list("cobwebs" = 1, "sugar" = 2)
- foodtype = JUNKFOOD | SUGAR
+ foodtype = JUNKFOOD | SUGAR | ANTITOXIC
/obj/item/reagent_containers/food/snacks/tobiko
name = "tobiko"
@@ -451,7 +451,7 @@
var/mutable_appearance/head
var/headcolor = rgb(0, 0, 0)
tastes = list("candy" = 1)
- foodtype = JUNKFOOD | SUGAR
+ foodtype = JUNKFOOD | SUGAR | ANTITOXIC
/obj/item/reagent_containers/food/snacks/lollipop/Initialize()
. = ..()
@@ -756,4 +756,4 @@
bitesize = 2
name = "hot-cross bun"
desc = "The Cross represents the Assistants that died for your sins."
- icon_state = "hotcrossbun"
\ No newline at end of file
+ icon_state = "hotcrossbun"
diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm
index d65907daea..13a46395ea 100644
--- a/code/modules/food_and_drinks/food/snacks_pastry.dm
+++ b/code/modules/food_and_drinks/food/snacks_pastry.dm
@@ -509,7 +509,7 @@
list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/medicine/omnizine = 3)
cooked_type = null
tastes = list("meat" = 2, "dough" = 2, "laziness" = 1)
- foodtype = GRAIN
+ foodtype = GRAIN | ANTITOXIC
/obj/item/reagent_containers/food/snacks/dankpocket
name = "\improper Dank-pocket"
@@ -556,9 +556,11 @@
name = "exceptional plump helmet biscuit"
desc = "Microwave is taken by a fey mood! It has cooked an exceptional plump helmet biscuit!"
bonus_reagents = list(/datum/reagent/medicine/omnizine = 5, /datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1)
+ foodtype += ANTITOXIC
. = ..()
if(fey)
reagents.add_reagent(/datum/reagent/medicine/omnizine, 5)
+ foodtype += ANTITOXIC
/obj/item/reagent_containers/food/snacks/cracker
name = "cracker"
diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm
index 81805f5529..24522503c1 100644
--- a/code/modules/food_and_drinks/food/snacks_pie.dm
+++ b/code/modules/food_and_drinks/food/snacks_pie.dm
@@ -171,8 +171,10 @@
name = "exceptional plump pie"
desc = "Microwave is taken by a fey mood! It has cooked an exceptional plump pie!"
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/medicine/omnizine = 5, /datum/reagent/consumable/nutriment/vitamin = 4)
+ foodtype += ANTITOXIC
if(fey)
reagents.add_reagent(/datum/reagent/medicine/omnizine, 5)
+ foodtype += ANTITOXIC
/obj/item/reagent_containers/food/snacks/pie/xemeatpie
diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm
index f30c182963..75cf8ffb82 100644
--- a/code/modules/food_and_drinks/food/snacks_pizza.dm
+++ b/code/modules/food_and_drinks/food/snacks_pizza.dm
@@ -109,7 +109,7 @@
bonus_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/nutriment/vitamin = 5)
list_reagents = list(/datum/reagent/consumable/nutriment = 25, /datum/reagent/consumable/tomatojuice = 6, /datum/reagent/medicine/omnizine = 10, /datum/reagent/consumable/nutriment/vitamin = 5)
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "meat" = 1, "laziness" = 1)
- foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | JUNKFOOD
+ foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | JUNKFOOD | ANTITOXIC
/obj/item/reagent_containers/food/snacks/pizzaslice/donkpocket
name = "donkpocket pizza slice"
@@ -117,7 +117,7 @@
icon_state = "donkpocketpizzaslice"
filling_color = "#FFA500"
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "meat" = 1, "laziness" = 1)
- foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | JUNKFOOD
+ foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | JUNKFOOD | ANTITOXIC
/obj/item/reagent_containers/food/snacks/pizza/dank
name = "dank pizza"
@@ -127,7 +127,7 @@
bonus_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/nutriment/vitamin = 6)
list_reagents = list(/datum/reagent/consumable/nutriment = 25, /datum/reagent/consumable/doctor_delight = 5, /datum/reagent/consumable/tomatojuice = 6, /datum/reagent/consumable/nutriment/vitamin = 5)
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "meat" = 1)
- foodtype = GRAIN | VEGETABLES | FRUIT | DAIRY
+ foodtype = GRAIN | VEGETABLES | FRUIT | DAIRY | ANTITOXIC
/obj/item/reagent_containers/food/snacks/pizzaslice/dank
name = "dank pizza slice"
@@ -135,7 +135,7 @@
icon_state = "dankpizzaslice"
filling_color = "#2E8B57"
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "meat" = 1)
- foodtype = GRAIN | VEGETABLES | FRUIT | DAIRY
+ foodtype = GRAIN | VEGETABLES | FRUIT | DAIRY | ANTITOXIC
/obj/item/reagent_containers/food/snacks/pizza/sassysage
name = "sassysage pizza"
@@ -187,6 +187,7 @@
slice_path = /obj/item/reagent_containers/food/snacks/pizzaslice/arnold
bonus_reagents = list(/datum/reagent/consumable/nutriment = 30, /datum/reagent/consumable/nutriment/vitamin = 6, /datum/reagent/iron = 10, /datum/reagent/medicine/omnizine = 30)
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pepperoni" = 2, "9 millimeter bullets" = 2)
+
/obj/item/reagent_containers/food/snacks/proc/try_break_off(mob/living/M, mob/living/user) //maybe i give you a pizza maybe i break off your arm
var/obj/item/bodypart/l_arm = user.get_bodypart(BODY_ZONE_L_ARM)
@@ -244,4 +245,4 @@
icon_state = "meatpizzaslice"
filling_color = "#A52A2A"
tastes = list("cardboard" = 1, "tomato" = 1, "cheese" = 1, "pepperoni" = 2)
- foodtype = GRAIN | VEGETABLES | DAIRY | MEAT
\ No newline at end of file
+ foodtype = GRAIN | VEGETABLES | DAIRY | MEAT
diff --git a/code/modules/food_and_drinks/food/snacks_salad.dm b/code/modules/food_and_drinks/food/snacks_salad.dm
index be7c3d25da..8c6eecb829 100644
--- a/code/modules/food_and_drinks/food/snacks_salad.dm
+++ b/code/modules/food_and_drinks/food/snacks_salad.dm
@@ -20,7 +20,7 @@
bonus_reagents = list(/datum/reagent/medicine/omnizine = 2, /datum/reagent/consumable/nutriment/vitamin = 6)
list_reagents = list(/datum/reagent/consumable/nutriment = 8, /datum/reagent/medicine/omnizine = 8, /datum/reagent/consumable/nutriment/vitamin = 6)
tastes = list("leaves" = 1)
- foodtype = VEGETABLES
+ foodtype = VEGETABLES | ANTITOXIC
/obj/item/reagent_containers/food/snacks/salad/herbsalad
name = "herb salad"
@@ -38,7 +38,7 @@
bonus_reagents = list(/datum/reagent/consumable/doctor_delight = 5, /datum/reagent/consumable/nutriment/vitamin = 4)
list_reagents = list(/datum/reagent/consumable/nutriment = 8, /datum/reagent/consumable/doctor_delight = 5, /datum/reagent/consumable/nutriment/vitamin = 2)
tastes = list("leaves" = 1, "potato" = 1, "meat" = 1, "valids" = 1)
- foodtype = VEGETABLES | MEAT | FRIED | JUNKFOOD | FRUIT
+ foodtype = VEGETABLES | MEAT | FRIED | JUNKFOOD | FRUIT | ANTITOXIC
/obj/item/reagent_containers/food/snacks/salad/oatmeal
name = "oatmeal"
@@ -133,7 +133,7 @@
trash = /obj/item/reagent_containers/glass/bowl
list_reagents = list(/datum/reagent/consumable/nutriment = 7, /datum/reagent/consumable/nutriment/vitamin = 5, /datum/reagent/medicine/earthsblood = 3, /datum/reagent/medicine/omnizine = 5, /datum/reagent/drug/happiness = 2)
tastes = list("hope" = 1)
- foodtype = VEGETABLES
+ foodtype = VEGETABLES | ANTITOXIC
/obj/item/reagent_containers/food/snacks/salad/gumbo
name = "black eyed gumbo"
diff --git a/code/modules/food_and_drinks/food/snacks_soup.dm b/code/modules/food_and_drinks/food/snacks_soup.dm
index ffafcc6b1e..eea7cdb87d 100644
--- a/code/modules/food_and_drinks/food/snacks_soup.dm
+++ b/code/modules/food_and_drinks/food/snacks_soup.dm
@@ -89,7 +89,7 @@
icon_state = "nettlesoup"
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/medicine/omnizine = 5, /datum/reagent/consumable/nutriment/vitamin = 5)
tastes = list("nettles" = 1)
- foodtype = VEGETABLES
+ foodtype = VEGETABLES | ANTITOXIC
/obj/item/reagent_containers/food/snacks/soup/mystery
name = "mystery soup"
diff --git a/code/modules/food_and_drinks/food/snacks_vend.dm b/code/modules/food_and_drinks/food/snacks_vend.dm
index b4c7c89b74..4e8687fc23 100644
--- a/code/modules/food_and_drinks/food/snacks_vend.dm
+++ b/code/modules/food_and_drinks/food/snacks_vend.dm
@@ -89,7 +89,7 @@
list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/doctor_delight = 5)
filling_color = "#F5F5DC"
tastes = list("sweetness" = 3, "cake" = 1)
- foodtype = GRAIN | FRUIT | VEGETABLES
+ foodtype = GRAIN | FRUIT | VEGETABLES | ANTITOXIC
custom_price = PRICE_CHEAP
/obj/item/reagent_containers/food/snacks/energybar
diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm
deleted file mode 100644
index ce27dccb74..0000000000
--- a/code/modules/goonchat/browserOutput.dm
+++ /dev/null
@@ -1,341 +0,0 @@
-/*********************************
-For the main html chat area
-*********************************/
-
-/// Should match the value set in the browser js
-#define MAX_COOKIE_LENGTH 5
-
-//Precaching a bunch of shit. Someone ship this out of here
-GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of icons for the browser output
-
-//lazy renaming to chat_output, instead renamed to old chatOutput
-/**
- * The chatOutput datum exists to handle the goonchat browser.
- * On client, created on Client/New()
- */
-/datum/chatOutput
- /// The client that owns us.
- var/client/owner
- /// How many times client data has been checked
- var/total_checks = 0
- /// When to next clear the client data checks counter
- var/next_time_to_clear = 0
- /// Has the client loaded the browser output area?
- var/loaded = FALSE
- /// If they haven't loaded chat, this is where messages will go until they do
- var/list/messageQueue
- var/cookieSent = FALSE // Has the client sent a cookie for analysis
- var/broken = FALSE
- var/list/connectionHistory //Contains the connection history passed from chat cookie
- var/adminMusicVolume = 25 //This is for the Play Global Sound verb
-
-/datum/chatOutput/New(client/C)
- owner = C
- messageQueue = list()
- connectionHistory = list()
-
-/**
- * start: Tries to load the chat browser
- * Aborts if a problem is encountered.
- * Async because this is called from Client/New.
- */
-/datum/chatOutput/proc/start()
- set waitfor = FALSE
- //Check for existing chat
- if(!owner)
- return FALSE
-
- if(!winexists(owner, "browseroutput")) // Oh goddamnit.
- broken = TRUE
- message_admins("Couldn't start chat for [key_name_admin(owner)]!")
- . = FALSE
- alert(owner.mob, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.")
- return
-
- if(winget(owner, "browseroutput", "is-visible") == "true") //Already setup
- doneLoading()
-
- else //Not setup
- load()
-
- return TRUE
-
-/// Loads goonchat and sends assets.
-/datum/chatOutput/proc/load()
- set waitfor = FALSE
- if(!owner)
- return
-
- var/datum/asset/stuff = get_asset_datum(/datum/asset/group/goonchat)
- stuff.send(owner)
-
- owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput")
-
-/// Interprets input from the client. Will send data back if required.
-/datum/chatOutput/Topic(href, list/href_list)
- if(usr.client != owner)
- return TRUE
-
- // Build arguments.
- // Arguments are in the form "param[paramname]=thing"
- var/list/params = list()
- for(var/key in href_list)
- if(length_char(key) > 7 && findtext(key, "param")) // 7 is the amount of characters in the basic param key template.
- var/param_name = copytext_char(key, 7, -1)
- var/item = href_list[key]
-
- params[param_name] = item
-
- var/data // Data to be sent back to the chat.
- switch(href_list["proc"])
- if("doneLoading")
- data = doneLoading(arglist(params))
-
- if("debug")
- data = debug(arglist(params))
-
- if("ping")
- data = ping(arglist(params))
-
- if("analyzeClientData")
- data = analyzeClientData(arglist(params))
-
- if("setMusicVolume")
- data = setMusicVolume(arglist(params))
- if("colorPresetPost") //User just swapped color presets in their goonchat preferences. Do we do anything else?
- switch(href_list["preset"])
- if("light")
- owner.force_white_theme()
- if("dark" || "normal")
- owner.force_dark_theme()
- // if("swaptodarkmode")
- // swaptodarkmode()
- // if("swaptolightmode")
- // swaptolightmode()
-
- if(data)
- ehjax_send(data = data)
-
-
-/// Called on chat output done-loading by JS.
-/datum/chatOutput/proc/doneLoading()
- if(loaded)
- return
-
- testing("Chat loaded for [owner.ckey]")
- loaded = TRUE
- showChat()
-
-
- for(var/message in messageQueue)
- // whitespace has already been handled by the original to_chat
- to_chat(owner, message, handle_whitespace=FALSE)
-
- messageQueue = null
- sendClientData()
-
- syncRegex()
-
- //do not convert to to_chat()
- SEND_TEXT(owner, "Failed to load fancy chat, reverting to old chat. Certain features won't work.")
-
-/// Hides the standard output and makes the browser visible.
-/datum/chatOutput/proc/showChat()
- winset(owner, "output", "is-visible=false")
- winset(owner, "browseroutput", "is-disabled=false;is-visible=true")
-
-/// Calls syncRegex on all currently owned chatOutput datums
-/proc/syncChatRegexes()
- for (var/user in GLOB.clients)
- var/client/C = user
- var/datum/chatOutput/Cchat = C.chatOutput
- if (Cchat && !Cchat.broken && Cchat.loaded)
- Cchat.syncRegex()
-
-/// Used to dynamically add regexes to the browser output. Currently only used by the IC filter.
-/datum/chatOutput/proc/syncRegex()
- var/list/regexes = list()
- /*
- if (config.ic_filter_regex)
- regexes["show_filtered_ic_chat"] = list(
- config.ic_filter_regex.name,
- "ig",
- "$1"
- )
- */
- if (regexes.len)
- ehjax_send(data = list("syncRegex" = regexes))
-
-/// Sends json encoded data to the browser.
-/datum/chatOutput/proc/ehjax_send(client/C = owner, window = "browseroutput", data)
- if(islist(data))
- data = json_encode(data)
- C << output("[data]", "[window]:ehjaxCallback")
-
-/**
- * Sends music data to the browser. If enabled by the browser, it will start playing.
- * Arguments:
- * music must be a https adress.
- * extra_data is a list. The keys "pitch", "start" and "end" are used.
- ** "pitch" determines the playback rate
- ** "start" determines the start time of the sound
- ** "end" determines when the musics stops playing
- */
-/datum/chatOutput/proc/sendMusic(music, pitch, list/extra_data) //someone remove pitch
- if(!findtext(music, GLOB.is_http_protocol))
- return
- var/list/music_data = list("adminMusic" = url_encode(url_encode(music)))
-
- if(extra_data?.len)
- music_data["musicRate"] = extra_data["pitch"] || pitch
- music_data["musicSeek"] = extra_data["start"]
- music_data["musicHalt"] = extra_data["end"]
-
- ehjax_send(data = music_data)
-
-/// Stops music playing throw the browser.
-/datum/chatOutput/proc/stopMusic()
- ehjax_send(data = "stopMusic")
-
-/// Setter for adminMusicVolume. Sanitizes the value to between 0 and 100.
-/datum/chatOutput/proc/setMusicVolume(volume = "")
- if(volume)
- adminMusicVolume = clamp(text2num(volume), 0, 100)
-
-/// Sends client connection details to the chat to handle and save
-/datum/chatOutput/proc/sendClientData()
- //Get dem deets
- var/list/deets = list("clientData" = list())
- deets["clientData"]["ckey"] = owner.ckey
- deets["clientData"]["ip"] = owner.address
- deets["clientData"]["compid"] = owner.computer_id
- var/data = json_encode(deets)
- ehjax_send(data = data)
-
-/// Called by client, sent data to investigate (cookie history so far)
-/datum/chatOutput/proc/analyzeClientData(cookie = "")
- //Spam check
- if(world.time > next_time_to_clear)
- next_time_to_clear = world.time + (3 SECONDS)
- total_checks = 0
-
- total_checks += 1
-
- if(total_checks > SPAM_TRIGGER_AUTOMUTE)
- message_admins("[key_name(owner)] kicked for goonchat topic spam")
- qdel(owner)
- return
-
- if(!cookie)
- return
-
- if(cookie != "none")
- var/list/connData = json_decode(cookie)
- if (connData && islist(connData) && connData.len > 0 && connData["connData"])
- connectionHistory = connData["connData"] //lol fuck
- var/list/found = new()
-
- if(connectionHistory.len > MAX_COOKIE_LENGTH)
- message_admins("[key_name(src.owner)] was kicked for an invalid ban cookie)")
- qdel(owner)
- return
-
- for(var/i in connectionHistory.len to 1 step -1)
- if(QDELETED(owner))
- //he got cleaned up before we were done
- return
- var/list/row = src.connectionHistory[i]
- if (!row || row.len < 3 || (!row["ckey"] || !row["compid"] || !row["ip"])) //Passed malformed history object
- return
- if (world.IsBanned(row["ckey"], row["ip"], row["compid"], real_bans_only=TRUE))
- found = row
- break
- CHECK_TICK
-
- //Uh oh this fucker has a history of playing on a banned account!!
- if (found.len > 0)
- message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
- log_admin_private("[key_name(owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
-
- cookieSent = TRUE
-
-/// Called by js client every 60 seconds
-/datum/chatOutput/proc/ping()
- return "pong"
-
-/// Called by js client on js error
-/datum/chatOutput/proc/debug(error)
- log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]")
-
-/// Global chat proc. to_chat_immediate will circumvent SSchat and send data as soon as possible.
-/proc/to_chat_immediate(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
- if(!target || !message)
- return
-
- if(target == world)
- target = GLOB.clients
-
- var/original_message = message
- if(handle_whitespace)
- message = replacetext(message, "\n", " ")
- message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") //EIGHT SPACES IN TOTAL!!
- if(trailing_newline)
- message += " "
-
- if(islist(target))
- // Do the double-encoding outside the loop to save nanoseconds
- var/twiceEncoded = url_encode(url_encode(message))
- for(var/I in target)
- var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
-
- if (!C)
- continue
-
- //Send it to the old style output window.
- SEND_TEXT(C, original_message)
-
- if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
- continue
-
- if(!C.chatOutput.loaded)
- //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- continue
-
- C << output(twiceEncoded, "browseroutput:output")
- else
- var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
-
- if (!C)
- return
-
- //Send it to the old style output window.
- SEND_TEXT(C, original_message)
-
- if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
- return
-
- if(!C.chatOutput.loaded)
- //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- return
-
- // url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
- C << output(url_encode(url_encode(message)), "browseroutput:output")
-
-/// Sends a text message to the target.
-/proc/to_chat(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
- if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
- to_chat_immediate(target, message, handle_whitespace, trailing_newline, confidential)
- return
- SSchat.queue(target, message, handle_whitespace, trailing_newline, confidential)
-
-/// Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation)
-/datum/chatOutput/proc/swaptolightmode()
- owner.force_white_theme()
-
-/// Light mode stuff. (See darkmode.dm for documentation)
-/datum/chatOutput/proc/swaptodarkmode()
- owner.force_dark_theme()
-
-#undef MAX_COOKIE_LENGTH
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
deleted file mode 100644
index 2669a3634a..0000000000
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ /dev/null
@@ -1,464 +0,0 @@
-/*****************************************
-*
-* GLOBAL STYLES
-*
-******************************************/
-html, body {
- padding: 0;
- margin: 0;
- height: 100%;
- color: #000000;
-}
-body {
- background: #E0E0E0; /*CIT CHANGE - darkens chatbox a lil*/
- font-family: Verdana, sans-serif;
- font-size: 13px;
- line-height: 1.2;
- overflow-x: hidden;
- overflow-y: scroll;
- word-wrap: break-word;
-}
-
-em {
- font-style: normal;
- font-weight: bold;
-}
-
-img {
- margin: 0;
- padding: 0;
- line-height: 1;
- -ms-interpolation-mode: nearest-neighbor;
- image-rendering: pixelated;
-}
-img.icon {
- height: 1em;
- min-height: 16px;
- width: auto;
- vertical-align: bottom;
-}
-
-
-.r:before { /* "repeated" badge class for combined messages */
- content: 'x';
-}
-.r {
- display: inline-block;
- min-width: 0.5em;
- font-size: 0.7em;
- padding: 0.2em 0.3em;
- line-height: 1;
- color: white;
- text-align: center;
- white-space: nowrap;
- vertical-align: middle;
- background-color: crimson;
- border-radius: 10px;
-}
-
-a {color: #0000ff;}
-a.visited {color: #ff00ff;}
-a:visited {color: #ff00ff;}
-a.popt {text-decoration: none;}
-
-/*****************************************
-*
-* OUTPUT NOT RELATED TO ACTUAL MESSAGES
-*
-******************************************/
-#loading {
- position: fixed;
- width: 300px;
- height: 150px;
- text-align: center;
- left: 50%;
- top: 50%;
- margin: -75px 0 0 -150px;
-}
-#loading i {display: block; padding-bottom: 3px;}
-
-#messages {
- font-size: 13px;
- padding: 3px;
- margin: 0;
- word-wrap: break-word;
-}
-#newMessages {
- position: fixed;
- display: block;
- bottom: 0;
- right: 0;
- padding: 8px;
- background: #d0d0d0;
- text-decoration: none;
- font-variant: small-caps;
- font-size: 1.1em;
- font-weight: bold;
- color: #333;
-}
-#newMessages:hover {background: #ccc;}
-#newMessages i {vertical-align: middle; padding-left: 3px;}
-#ping {
- position: fixed;
- top: 0;
- right: 135px;
- width: 45px;
- background: #d0d0d0;
- height: 30px;
- padding: 8px 0 2px 0;
-}
-#ping i {display: block; text-align: center;}
-#ping .ms {
- display: block;
- text-align: center;
- font-size: 8pt;
- padding-top: 2px;
-}
-#userBar {
- position: fixed;
- top: 0;
- right: 0;
-}
-#userBar .subCell {
- background: #d0d0d0;
- height: 30px;
- padding: 5px 0;
- display: block;
- color: #333;
- text-decoration: none;
- line-height: 28px;
- border-top: 1px solid #b4b4b4;
-}
-#userBar .subCell:hover {background: #ccc;}
-#userBar .toggle {
- width: 45px;
- background: #ccc;
- border-top: 0;
- float: right;
- text-align: center;
-}
-#userBar .sub {clear: both; display: none; width: 180px;}
-#userBar .sub.scroll {overflow-y: scroll;}
-#userBar .sub.subCell {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;}
-#userBar .sub span {
- display: block;
- line-height: 30px;
- float: left;
-}
-#userBar .sub i {
- display: block;
- padding: 0 5px;
- font-size: 1.1em;
- width: 22px;
- text-align: center;
- line-height: 30px;
- float: right;
-}
-#userBar .sub input {
- position: absolute;
- padding: 7px 5px;
- width: 121px;
- line-height: 30px;
- float: left;
-}
-#userBar .topCell {border-top: 0;}
-
-/* POPUPS */
-.popup {
- position: fixed;
- top: 50%;
- left: 50%;
- background: #d0d0d0;
-}
-.popup .close {
- position: absolute;
- background: #aaa;
- top: 0;
- right: 0;
- color: #333;
- text-decoration: none;
- z-index: 2;
- padding: 0 10px;
- height: 30px;
- line-height: 30px;
-}
-.popup .close:hover {background: #999;}
-.popup .head {
- background: #999;
- color: #d0d0d0;
- padding: 0 10px;
- height: 30px;
- line-height: 30px;
- text-transform: uppercase;
- font-size: 0.9em;
- font-weight: bold;
- border-bottom: 2px solid green;
-}
-.popup input {border: 1px solid #999; background: #fff; margin: 0; padding: 5px; outline: none; color: #333;}
-.popup input[type=text]:hover, .popup input[type=text]:active, .popup input[type=text]:focus {border-color: green;}
-.popup input[type=submit] {padding: 5px 10px; background: #999; color: #d0d0d0; text-transform: uppercase; font-size: 0.9em; font-weight: bold;}
-.popup input[type=submit]:hover, .popup input[type=submit]:focus, .popup input[type=submit]:active {background: #aaa; cursor: pointer;}
-
-.changeFont {padding: 10px;}
-.changeFont a {display: block; text-decoration: none; padding: 3px; color: #333;}
-.changeFont a:hover {background: #ccc;}
-
-.highlightPopup {padding: 10px; text-align: center;}
-.highlightPopup input[type=text] {display: block; width: 215px; text-align: left; margin-top: 5px;}
-.highlightPopup input.highlightColor {background-color: #FFFF00;}
-.highlightPopup input.highlightTermSubmit {margin-top: 5px;}
-
-/* ADMIN CONTEXT MENU */
-.contextMenu {
- background-color: #d0d0d0;
- position: fixed;
- margin: 2px;
- width: 150px;
-}
-.contextMenu a {
- display: block;
- padding: 2px 5px;
- text-decoration: none;
- color: #333;
-}
-
-.contextMenu a:hover {
- background-color: #ccc;
-}
-
-/* ADMIN FILTER MESSAGES MENU */
-.filterMessages {padding: 5px;}
-.filterMessages div {padding: 2px 0;}
-.filterMessages input {}
-.filterMessages label {}
-
-.icon-stack {height: 1em; line-height: 1em; width: 1em; vertical-align: middle; margin-top: -2px;}
-
-
-/*****************************************
-*
-* OUTPUT ACTUALLY RELATED TO MESSAGES
-*
-******************************************/
-
-/* MOTD */
-.motd {color: #638500; font-family: Verdana, sans-serif;}
-.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #638500; text-decoration: underline;}
-.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;}
-
-/* ADD HERE FOR BOLD */
-.bold, .name, .prefix, .ooc, .looc, .adminooc, .admin, .medal, .yell {font-weight: bold;}
-
-/* ADD HERE FOR ITALIC */
-.italic, .italics, .emote {font-style: italic;}
-
-/* OUTPUT COLORS */
-.highlight {background: yellow;}
-
-h1, h2, h3, h4, h5, h6 {color: #0000ff;font-family: Georgia, Verdana, sans-serif;}
-h1.alert, h2.alert {color: #000000;}
-
-em {font-style: normal; font-weight: bold;}
-
-.ooc {color: #002eb8; font-weight: bold;}
-.looc {color: #6699CC; font-weight: bold;}
-.antagooc {color: #b8002e; font-weight: bold;}
-.adminobserverooc {color: #0099cc; font-weight: bold;}
-.adminooc {color: #700038; font-weight: bold;}
-
-.adminsay {color: #FF4500}
-.admin {color: #386aff; font-weight: bold;}
-
-.name { font-weight: bold;}
-
-.say {}
-.deadsay {color: #5c00e6;}
-.binarysay {color: #20c20e; background-color: #000000; display: block;}
-.binarysay a {color: #00ff00;}
-.binarysay a:active, .binarysay a:visited {color: #88ff88;}
-.radio {color: #008000;}
-.sciradio {color: #993399;}
-.comradio {color: #948f02;}
-.secradio {color: #a30000;}
-.medradio {color: #337296;}
-.engradio {color: #fb5613;}
-.suppradio {color: #a8732b;}
-.servradio {color: #6eaa2c;}
-.syndradio {color: #6d3f40;}
-.centcomradio {color: #686868;}
-.aiprivradio {color: #ff00ff;}
-.redteamradio {color: #ff0000;}
-.blueteamradio {color: #0000ff;}
-
-.yell { font-weight: bold;}
-
-.alert {color: #ff0000;}
-h1.alert, h2.alert {color: #000000;}
-
-.emote { font-style: italic;}
-.selecteddna {color: #ffffff; background-color: #001B1B}
-
-.attack {color: #ff0000;}
-.disarm {color: #990000;}
-.passive {color: #660000;}
-
-.userdanger {color: #ff0000; font-weight: bold; font-size: 185%;}
-.bolddanger {color: #c51e1e;font-weight: bold;}
-.danger {color: #ff0000;}
-.tinydanger {color: #c51e1e; font-size: 85%;}
-.smalldanger {color: #c51e1e; font-size: 90%;}
-.warning {color: #ff0000; font-style: italic;}
-.alertwarning {color: #FF0000; font-weight: bold}
-.boldwarning {color: #ff0000; font-style: italic; font-weight: bold}
-.announce {color: #228b22; font-weight: bold;}
-.boldannounce {color: #ff0000; font-weight: bold;}
-.greenannounce {color: #00ff00; font-weight: bold;}
-.rose {color: #ff5050;}
-.info {color: #0000CC;}
-.notice {color: #000099;}
-.tinynotice {color: #6685f5; font-style: italic; font-size: 85%;}
-.smallnotice {color: #6685f5; font-size: 90%;}
-.smallnoticeital {color: #6685f5; font-style: italic; font-size: 90%;}
-.boldnotice {color: #000099; font-weight: bold;}
-.adminnotice {color: #0000ff;}
-.adminhelp {color: #ff0000; font-weight: bold;}
-.unconscious {color: #0000ff; font-weight: bold;}
-.suicide {color: #ff5050; font-style: italic;}
-.green {color: #03ff39;}
-.red {color: #FF0000;}
-.pink {color: #FF69Bf;}
-.blue {color: #0000FF;}
-.nicegreen {color: #14a833;}
-.userlove {color: #FF1493; font-style: italic; font-weight: bold; text-shadow: 0 0 6px #ff6dbc;}
-.love {color: #ff006a; font-style: italic; text-shadow: 0 0 6px #ff6d6d;}
-.shadowling {color: #3b2769;}
-.cult {color: #960000;}
-
-.cultitalic {color: #960000; font-style: italic;}
-.cultbold {color: #960000; font-style: italic; font-weight: bold;}
-.cultboldtalic {color: #960000; font-weight: bold; font-size: 185%;}
-
-.cultlarge {color: #960000; font-weight: bold; font-size: 185%;}
-.narsie {color: #960000; font-weight: bold; font-size: 925%;}
-.narsiesmall {color: #960000; font-weight: bold; font-size: 370%;}
-.colossus {color: #7F282A; font-size: 310%;}
-.hierophant {color: #660099; font-weight: bold; font-style: italic;}
-.hierophant_warning {color: #660099; font-style: italic;}
-.purple {color: #5e2d79;}
-.holoparasite {color: #35333a;}
-
-.revennotice {color: #1d2953;}
-.revenboldnotice {color: #1d2953; font-weight: bold;}
-.revenbignotice {color: #1d2953; font-weight: bold; font-size: 185%;}
-.revenminor {color: #823abb}
-.revenwarning {color: #760fbb; font-style: italic;}
-.revendanger {color: #760fbb; font-weight: bold; font-size: 185%;}
-.umbra {color: #5000A0;}
-.umbra_emphasis {color: #5000A0; font-weight: bold; font-style: italic;}
-.umbra_large {color: #5000A0; font-size: 185%; font-weight: bold; font-style: italic;}
-
-.deconversion_message {color: #5000A0; font-size: 185%; font-style: italic;}
-
-.brass {color: #BE8700;}
-.heavy_brass {color: #BE8700; font-weight: bold; font-style: italic;}
-.large_brass {color: #BE8700; font-size: 185%;}
-.big_brass {color: #BE8700; font-size: 185%; font-weight: bold; font-style: italic;}
-.ratvar {color: #BE8700; font-size: 370%; font-weight: bold; font-style: italic;}
-.alloy {color: #42474D;}
-.heavy_alloy {color: #42474D; font-weight: bold; font-style: italic;}
-.nezbere_large {color: #42474D; font-size: 185%; font-weight: bold; font-style: italic;}
-.nezbere {color: #42474D; font-weight: bold; font-style: italic;}
-.nezbere_small {color: #42474D;}
-.sevtug_large {color: #AF0AAF; font-size: 185%; font-weight: bold; font-style: italic;}
-.sevtug {color: #AF0AAF; font-weight: bold; font-style: italic;}
-.sevtug_small {color: #AF0AAF;}
-.inathneq_large {color: #1E8CE1; font-size: 185%; font-weight: bold; font-style: italic;}
-.inathneq {color: #1E8CE1; font-weight: bold; font-style: italic;}
-.inathneq_small {color: #1E8CE1;}
-.nzcrentr_large {color: #DAAA18; font-size: 185%; font-weight: bold; font-style: italic;}
-.nzcrentr {color: #DAAA18; font-weight: bold; font-style: italic;}
-.nzcrentr_small {color: #DAAA18;}
-.neovgre_large {color: #6E001A; font-size: 185%; font-weight: bold; font-style: italic;}
-.neovgre {color: #6E001A; font-weight: bold; font-style: italic;}
-.neovgre_small {color: #6E001A;}
-
-.newscaster {color: #800000;}
-.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;}
-
-.alien {color: #543354;}
-.noticealien {color: #00c000;}
-.alertalien {color: #00c000; font-weight: bold;}
-.changeling {color: #800080; font-style: italic;}
-
-.spider {color: #4d004d; font-weight: bold; font-size: 185%;}
-
-.interface {color: #330033;}
-
-.sans {font-family: "Comic Sans MS", cursive, sans-serif;}
-.papyrus {font-family: "Papyrus", cursive, sans-serif;}
-.robot {font-family: "Courier New", cursive, sans-serif;}
-
-.command_headset {font-weight: bold; font-size: 160%;}
-.small {font-size: 60%;}
-.big {font-size: 185%;}
-.reallybig {font-size: 245%;}
-.extremelybig {font-size: 310%;}
-.greentext {color: #00FF00; font-size: 185%;}
-.redtext {color: #FF0000; font-size: 185%;}
-.clown {color: #FF69Bf; font-size: 160%; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
-.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
-.spooky {color: #FF6100;}
-.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
-
-.lethal {color: #bf3d3d; font-weight: bold;}
-.stun {color: #0f81bc; font-weight: bold;}
-.ion {color: #d084d6; font-weight: bold;}
-.xray {color: #32c025; font-weight: bold;}
-
-@keyframes velvet {
- 0% { color: #400020; }
- 40% { color: #FF0000; }
- 50% { color: #FF8888; }
- 60% { color: #FF0000; }
- 100% { color: #400020; }
-}
-
-.hypnophrase {color: #202020; font-weight: bold; animation: hypnocolor 1500ms infinite;}
-@keyframes hypnocolor {
- 0% { color: #202020; }
- 25% { color: #4b02ac; }
- 50% { color: #9f41f1; }
- 75% { color: #541c9c; }
- 100% { color: #7adbf3; }
-}
-
-.phobia {color: #dd0000; font-weight: bold; animation: phobia 750ms infinite;}
-@keyframes phobia {
- 0% { color: #f75a5a; }
- 50% { color: #dd0000; }
- 100% { color: #f75a5a; }
-}
-
-
-.icon {height: 1em; width: auto;}
-
-.memo {color: #638500; text-align: center;}
-.memoedit {text-align: center; font-size: 125%;}
-.abductor {color: #800080; font-style: italic;}
-.mind_control {color: #A00D6F; font-size: 100%; font-weight: bold; font-style: italic;}
-.slime {color: #00CED1;}
-.drone {color: #848482;}
-.monkey {color: #975032;}
-.swarmer {color: #2C75FF;}
-.resonate {color: #298F85;}
-
-.monkeyhive {color: #774704;}
-.monkeylead {color: #774704; font-size: 125%;}
-
-.connectionClosed, .fatalError {background: red; color: white; padding: 5px;}
-.connectionClosed.restored {background: green;}
-.internal.boldnshit {color: #000099; font-weight: bold;}
-
-/* HELPER CLASSES */
-.text-normal {font-weight: normal; font-style: normal;}
-.hidden {display: none; visibility: hidden;}
diff --git a/code/modules/goonchat/browserassets/css/browserOutput_dark.css b/code/modules/goonchat/browserassets/css/browserOutput_dark.css
deleted file mode 100644
index 3377bcf72c..0000000000
--- a/code/modules/goonchat/browserassets/css/browserOutput_dark.css
+++ /dev/null
@@ -1,159 +0,0 @@
-html, body {color: #E0E0E0;}
-body {
- background: #171717;
- font-color: #E0E0E0;
- scrollbar-face-color:#1A1A1A;
- scrollbar-track-color:#171717;
- scrollbar-highlight-color:#171717;
-}
-
-a {color: #397ea5;}
-a.visited {color: #7c00e6;}
-a:visited {color: #7c00e6;}
-
-#newMessages {
- background: #242424;
- color: #E0E0E0;
-}
-#newMessages:hover {background: #272727;}
-
-#ping {background: #272727;}
-
-#userBar .subCell {
- background: #272727;
- color: #E0E0E0;
- border-top: 1px solid #171717;
-}
-#userBar .subCell:hover {background: #272727;}
-#userBar .toggle {background: #272727;}
-
-/* MOTD */
-.motd {color: #E0E0E0;}
-.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #E0E0E0;}
-.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #E0E0E0;}
-
-h1, h2, h3, h4, h5, h6 {color: #E0E0E0;}
-h1.alert, h2.alert {color: #E0E0E0;}
-
-.ooc {color: #cca300;}
-.looc {color: #d8b555;}
-.antagooc {color: #ce254f;}
-.adminobserverooc {color: #0099cc;}
-.adminooc {color: #3d5bc3;}
-
-.admin {color: #5975da;}
-
-.deadsay {color: #e2c1ff;}
-.radio {color: #1ecc43;}
-.sciradio {color: #c68cfa;}
-.comradio {color: #5177ff;}
-.secradio {color: #dd3535;}
-.medradio {color: #57b8f0;}
-.engradio {color: #f37746;}
-.suppradio {color: #b88646;}
-.servradio {color: #6ca729;}
-.syndradio {color: #8f4a4b;}
-.centcomradio {color: #2681a5;}
-.aiprivradio {color: #d65d95;}
-.redteamradio {color: #ff4444;}
-.blueteamradio {color: #3434fd;}
-
-.alert {color: #d82020;}
-h1.alert, h2.alert {color: #99aab5;}
-
-.attack {color: #e01c1c;}
-.disarm {color: #b42525;}
-.passive {color: #a00f0f;}
-
-.userdanger {color: #c51e1e;}
-.danger {color: #c51e1e;}
-.warning {color: #c51e1e;}
-.alertwarning {color: #c51e1e;}
-.boldwarning {color: #c51e1e;}
-.announce {color: #c51e1e;}
-.boldannounce {color: #c51e1e;}
-.greenannounce {color: #059223;}
-.info {color: #6685f5;}
-.notice {color: #6685f5;}
-.boldnotice {color: #6685f5;}
-.adminnotice {color: #6685f5;}
-.adminhelp {color: #ff0000;}
-.unconscious {color: #E0E0E0;}
-.red {color: #FF0000;}
-.pink {color: #ff70c1;}
-.blue {color: #215cff;}
-.green {color: #059223;}
-.nicegreen {color: #059223;}
-.userlove {color: #ff42a6; text-shadow: 0 0 6px #82365e;}
-.love {color: #ff4591; text-shadow: 0 0 6px #994449;}
-.shadowling {color: #8e8a99;}
-.cult {color: #aa1c1c;}
-
-.cultitalic {color: #aa1c1c;}
-.cultbold {color: #aa1c1c;}
-.cultboldtalic {color: #aa1c1c;}
-
-.cultlarge {color: #aa1c1c;}
-.narsie {color: #aa1c1c;}
-.narsiesmall {color: #aa1c1c;}
-.hierophant {color: #b441ee;}
-.hierophant_warning {color: #c56bf1;}
-.purple {color: #9956d3;}
-.holoparasite {color: #88809c;}
-
-.revennotice {color: #3645aa;}
-.revenboldnotice {color: #3645aa;}
-.revenbignotice {color: #3645aa;}
-.revenminor {color: #823ddd;}
-.revenwarning {color: #8911d9;}
-.revendanger {color: #8911d9;}
-.umbra {color: #7c00e6;}
-.umbra_emphasis {color: #7c00e6;}
-.umbra_large {color: #7c00e6;}
-
-.deconversion_message {color: #a947ff;}
-
-.alloy {color: #545b64;}
-.heavy_alloy {color: #545b64;}
-.nezbere_large {color: #545b64;}
-.nezbere {color: #545b64;}
-.nezbere_small {color: #545b64;}
-.inathneq_large {color: #1d7dc7;}
-.inathneq {color: #1d7dc7;}
-.inathneq_small {color: #1d7dc7;}
-.neovgre_large {color: #7c0622;}
-.neovgre {color: #7c0622;}
-.neovgre_small {color: #7c0622;}
-
-.newscaster {color: #c05d5d;}
-.ghostalert {color: #6600ff;}
-
-.alien {color: #855d85;}
-.noticealien {color: #059223;}
-.alertalien {color: #059223;}
-.changeling {color: #059223;}
-
-.spider {color: #8800ff;}
-
-.interface {color: #750e75;}
-
-.greentext {color: #059223;}
-.redtext {color: #c51e1e;}
-.clown {color: #ff70c1;}
-.velvet {color: #660015;}
-@keyframes velvet {
- 0% { color: #890020; }
- 40% { color: #c51e1e; }
- 50% { color: #FF8888; }
- 60% { color: #c51e1e; }
- 100% { color: #890020; }
-}
-
-.abductor {color: #c204c2;}
-.mind_control {color: #df3da9;}
-.drone {color: #979795;}
-
-.monkeyhive {color: #a56408;}
-.monkeylead {color: #af6805;}
-
-.internal.boldnshit {color: #3d5bc3;}
diff --git a/code/modules/goonchat/browserassets/css/browserOutput_light.css b/code/modules/goonchat/browserassets/css/browserOutput_light.css
deleted file mode 100644
index b7036c74d8..0000000000
--- a/code/modules/goonchat/browserassets/css/browserOutput_light.css
+++ /dev/null
@@ -1,14 +0,0 @@
-body {background: #F1F1F1;}
-
-#newMessages {background: #ddd;}
-#ping {background: #ddd;}
-
-#userBar .subCell {background: #ddd;}
-
-/* POPUPS */
-.popup {background: #ddd;}
-.popup .head {color: #ddd;}
-.popup input[type=submit] {color: #ddd;}
-
-/* ADMIN CONTEXT MENU */
-.contextMenu {background-color: #ddd;}
diff --git a/code/modules/goonchat/browserassets/html/browserOutput.html b/code/modules/goonchat/browserassets/html/browserOutput.html
deleted file mode 100644
index ce51cd8de8..0000000000
--- a/code/modules/goonchat/browserassets/html/browserOutput.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
- Chat
-
-
-
-
-
-
-
-
-
-
-
-
-
- Loading...
- If this takes longer than 30 seconds, it will automatically reload a maximum of 5 times.
- If it still doesn't work, use the bug report button at the top right of the window.
-
Choose up to '+opts.highlightLimit+' strings that will highlight the line when they appear in chat.
' +
- '' +
- '
';
- createPopup(popupContent, 250);
- });
-
- $('body').on('keyup', '#highlightColor', function() {
- var color = $('#highlightColor').val();
- color = color.trim();
- if (!color || color.charAt(0) != '#') return;
- $('#highlightColor').css('background-color', color);
- });
-
- $('body').on('submit', '#highlightTermForm', function(e) {
- e.preventDefault();
-
- opts.highlightTerms = [];
- for (var count = 0; count < opts.highlightLimit; count++) {
- var term = $('#highlightTermInput'+count).val();
- if (term !== null && /\S/.test(term)) {
- opts.highlightTerms.push(term.trim().toLowerCase());
- }
- }
-
- var color = $('#highlightColor').val();
- color = color.trim();
- if (color == '' || color.charAt(0) != '#') {
- opts.highlightColor = '#FFFF00';
- } else {
- opts.highlightColor = color;
- }
- var $popup = $('#highlightPopup').closest('.popup');
- $popup.remove();
-
- setCookie('highlightterms', JSON.stringify(opts.highlightTerms), 365);
- setCookie('highlightcolor', opts.highlightColor, 365);
- });
-
- $('#clearMessages').click(function() {
- $messages.empty();
- opts.messageCount = 0;
- });
-
- $('#changeColorPreset').click(function() { //CIT SPECIFIC
- opts.colorPreset = (opts.colorPreset+1) % colorPresets.length;
- updateColorPreset();
- setCookie('colorpreset', opts.colorPreset, 365);
- internalOutput('Changed color preset to: '+colorPresets[opts.colorPreset]);
- });
-
- $('#musicVolumeSpan').hover(function() {
- $('#musicVolumeText').addClass('hidden');
- $('#musicVolume').removeClass('hidden');
- }, function() {
- $('#musicVolume').addClass('hidden');
- $('#musicVolumeText').removeClass('hidden');
- });
-
- $('#musicVolume').change(function() {
- var newVolume = $('#musicVolume').val();
- newVolume = clamp(newVolume, 0, 100);
- $('#adminMusic').prop('volume', newVolume / 100);
- setCookie('musicVolume', newVolume, 365);
- opts.updatedVolume = newVolume;
- if(!opts.volumeUpdating) {
- setTimeout(sendVolumeUpdate, opts.volumeUpdateDelay);
- opts.volumeUpdating = true;
- }
- });
-
- $('#toggleCombine').click(function(e) {
- opts.messageCombining = !opts.messageCombining;
- setCookie('messagecombining', (opts.messageCombining ? 'true' : 'false'), 365);
- });
-
- $('img.icon').error(iconError);
-
-
-
-
- /*****************************************
- *
- * KICK EVERYTHING OFF
- *
- ******************************************/
-
- runByond('?_src_=chat&proc=doneLoading');
- if ($('#loading').is(':visible')) {
- $('#loading').remove();
- }
- $('#userBar').show();
- opts.priorChatHeight = $(window).height();
-});
diff --git a/code/modules/goonchat/browserassets/js/json2.min.js b/code/modules/goonchat/browserassets/js/json2.min.js
deleted file mode 100644
index d867407f26..0000000000
--- a/code/modules/goonchat/browserassets/js/json2.min.js
+++ /dev/null
@@ -1 +0,0 @@
-"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function this_value(){return this.valueOf()}function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(t){var e=meta[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var r,n,o,u,f,a=gap,i=e[t];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(t)),"function"==typeof rep&&(i=rep.call(e,t,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,f=[],"[object Array]"===Object.prototype.toString.apply(i)){for(u=i.length,r=0;u>r;r+=1)f[r]=str(r,i)||"null";return o=0===f.length?"[]":gap?"[\n"+gap+f.join(",\n"+gap)+"\n"+a+"]":"["+f.join(",")+"]",gap=a,o}if(rep&&"object"==typeof rep)for(u=rep.length,r=0;u>r;r+=1)"string"==typeof rep[r]&&(n=rep[r],o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));else for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));return o=0===f.length?"{}":gap?"{\n"+gap+f.join(",\n"+gap)+"\n"+a+"}":"{"+f.join(",")+"}",gap=a,o}}var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,r){var n;if(gap="",indent="","number"==typeof r)for(n=0;r>n;n+=1)indent+=" ";else"string"==typeof r&&(indent=r);if(rep=e,e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(t,e){var r,n,o=t[e];if(o&&"object"==typeof o)for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(n=walk(o,r),void 0!==n?o[r]=n:delete o[r]);return reviver.call(t,e,o)}var j;if(text=String(text),rx_dangerous.lastIndex=0,rx_dangerous.test(text)&&(text=text.replace(rx_dangerous,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),rx_one.test(text.replace(rx_two,"@").replace(rx_three,"]").replace(rx_four,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();
\ No newline at end of file
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
index 4561e0ae3e..573b04a296 100644
--- a/code/modules/holiday/halloween/jacqueen.dm
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -334,8 +334,7 @@
to_chat(C, " You feel an overwhelming desire to [message]")
if(2)
visible_message("[src] waves their arms around, \"If only you had a better upbringing, your ears are now full of my singing!\"")
- var/client/C2 = C.client
- C2.chatOutput.sendMusic("https://puu.sh/ExBbv.mp4", 1)//I hope this works!
+ C.client.tgui_panel?.play_music("https://puu.sh/ExBbv.mp4")
if(3)
visible_message("[src] waves their arms around, \"You're cute little bumpkin, On your head is a pumpkin!\"")
if(C.head)
diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm
index e84025208a..b8480a1901 100644
--- a/code/modules/hydroponics/grown/ambrosia.dm
+++ b/code/modules/hydroponics/grown/ambrosia.dm
@@ -49,6 +49,7 @@
seed = /obj/item/seeds/ambrosia/deus
name = "ambrosia deus branch"
desc = "Eating this makes you feel immortal!"
+ foodtype = VEGETABLES | ANTITOXIC
icon_state = "ambrosiadeus"
filling_color = "#008B8B"
wine_power = 50
diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm
index 6525ac42d4..e4fe7fcdbc 100644
--- a/code/modules/hydroponics/grown/cannabis.dm
+++ b/code/modules/hydroponics/grown/cannabis.dm
@@ -104,6 +104,7 @@
seed = /obj/item/seeds/cannabis/death
name = "death cannabis leaf"
desc = "Looks a bit dark. Oh well."
+ foodtype = VEGETABLES | TOXIC
icon_state = "blackcannabis"
wine_power = 40
@@ -111,6 +112,7 @@
seed = /obj/item/seeds/cannabis/white
name = "white cannabis leaf"
desc = "It feels smooth and nice to the touch."
+ foodtype = VEGETABLES | ANTITOXIC
icon_state = "whitecannabis"
wine_power = 10
diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm
index d130d50aa5..e199c2c6b1 100644
--- a/code/modules/hydroponics/grown/citrus.dm
+++ b/code/modules/hydroponics/grown/citrus.dm
@@ -29,6 +29,7 @@
seed = /obj/item/seeds/lime
name = "lime"
desc = "It's so sour, your face will twist."
+ foodtype = FRUIT | ANTITOXIC
icon_state = "lime"
filling_color = "#00FF00"
juice_results = list(/datum/reagent/consumable/limejuice = 0)
diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm
index 8c5171667a..e385eed142 100644
--- a/code/modules/instruments/songs/editor.dm
+++ b/code/modules/instruments/songs/editor.dm
@@ -82,7 +82,6 @@
var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500)
popup.set_content(dat.Join(""))
- popup.set_title_image(user.browse_rsc_icon(parent.icon, parent.icon_state))
popup.open()
/datum/song/proc/ParseSong(text)
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 1238b37919..0feceaf1a8 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -80,6 +80,9 @@
if(mind_traits)
for(var/t in mind_traits)
ADD_TRAIT(H.mind, t, JOB_TRAIT)
+ if(/datum/quirk/paraplegic in blacklisted_quirks)
+ H.regenerate_limbs() //if you can't be a paraplegic, attempt to regenerate limbs to stop amputated limb selection
+ H.set_resting(FALSE, TRUE) //they probably shouldn't be on the floor because they had no legs then suddenly had legs
/datum/job/proc/announce(mob/living/carbon/human/H)
if(head_announce)
diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm
index 536bf06e8d..42b439ba03 100644
--- a/code/modules/language/language.dm
+++ b/code/modules/language/language.dm
@@ -35,7 +35,7 @@
return TRUE
/datum/language/proc/get_icon()
- var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
return sheet.icon_tag("language-[icon_state]")
/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2)
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index 80ce2522ff..a4d88158e2 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -22,7 +22,7 @@
max_integrity = 200
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
var/state = 0
- var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book) //Things allowed in the bookcase
+ var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book, /obj/item/gun/magic/wand/book) //Things allowed in the bookcase
/obj/structure/bookcase/examine(mob/user)
. = ..()
@@ -192,7 +192,7 @@
desc = "Crack it open, inhale the musk of its pages, and learn something new."
throw_speed = 1
throw_range = 5
- w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
+ w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
attack_verb = list("bashed", "whacked", "educated")
resistance_flags = FLAMMABLE
var/dat //Actual page content
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index f777246453..3953f5e28c 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -66,7 +66,6 @@
dat += "\[Go Back\] "
var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/libraryconsole/Topic(href, href_list)
@@ -314,7 +313,6 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
var/datum/browser/popup = new(user, "library", name, 600, 400)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/libraryconsole/bookmanagement/proc/findscanner(viewrange)
@@ -537,7 +535,6 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
dat += " "
var/datum/browser/popup = new(user, "scanner", name, 600, 400)
popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/libraryscanner/Topic(href, href_list)
diff --git a/code/modules/mapping/minimaps.dm b/code/modules/mapping/minimaps.dm
index 7a62e9ab44..e0eb174cb3 100644
--- a/code/modules/mapping/minimaps.dm
+++ b/code/modules/mapping/minimaps.dm
@@ -4,7 +4,7 @@
// The map icons
var/icon/map_icon
var/icon/meta_icon
-
+
var/list/color_area_names = list()
var/minx
@@ -33,7 +33,7 @@
meta_icon = new('html/blank.png')
map_icon.Scale(x2 - x1 + 1, y2 - y1 + 1) // arrays start at 1
meta_icon.Scale(x2 - x1 + 1, y2 - y1 + 1)
-
+
var/list/area_to_color = list()
for(var/turf/T in block(locate(x1, y1, z_level), locate(x2, y2, z_level)))
var/area/A = T.loc
@@ -44,7 +44,7 @@
crop_x2 = max(crop_x2, T.x)
crop_y1 = min(crop_y1, T.y)
crop_y2 = max(crop_y2, T.y)
-
+
var/meta_color = area_to_color[A]
if(!meta_color)
meta_color = rgb(rand(0, 255), rand(0, 255), rand(0, 255)) // technically conflicts could happen but it's like very unlikely and it's not that big of a deal if one happens
@@ -73,13 +73,15 @@
overlay_icon = new(map_icon)
overlay_icon.Scale(16, 16)
//we're done baking, now we ship it.
- register_asset("minimap-[id].png", map_icon)
- register_asset("minimap-[id]-meta.png", meta_icon)
+ if (!SSassets.cache["minimap-[id].png"])
+ SSassets.transport.register_asset("minimap-[id].png", map_icon)
+ if (!SSassets.cache["minimap-[id]-meta.png"])
+ SSassets.transport.register_asset("minimap-[id]-meta.png", meta_icon)
/datum/minimap/proc/send(mob/user)
if(!id)
CRASH("ERROR: send called, but the minimap id is null/missing. ID: [id]")
- send_asset_list(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon))
+ SSassets.transport.send_assets(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon))
/datum/minimap_group
var/list/minimaps = list()
@@ -100,15 +102,17 @@
var/list/datas = list()
var/list/info = list()
-
+
for(var/i in 1 to length(minimaps))// OLD: for(var/i in 1 to length(minimaps))
var/datum/minimap/M = minimaps[i]
+ var/map_name = "minimap-[M.id].png"
+ var/meta_name = "minimap-[M.id]-meta.png"
M.send(user)
info += {"
-
-
+
+
@@ -183,6 +187,4 @@
var/datum/browser/popup = new(user, "minimap_[id]", name, 500, 700)
popup.add_head_content(headerJS) //set the head
popup.set_content(info)
- var/datum/minimap/MICO = minimaps[1]
- popup.set_title_image(MICO.overlay_icon)
popup.open(FALSE)
diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm
index 006065d048..b10177ebaf 100644
--- a/code/modules/mining/aux_base.dm
+++ b/code/modules/mining/aux_base.dm
@@ -70,7 +70,6 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
var/datum/browser/popup = new(user, "computer", "base management", 550, 300) //width, height
popup.set_content("
[dat]
")
- popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm
index 9ccb7c0efc..b2cd840468 100644
--- a/code/modules/mining/equipment/mineral_scanner.dm
+++ b/code/modules/mining/equipment/mineral_scanner.dm
@@ -12,13 +12,14 @@
slot_flags = ITEM_SLOT_BELT
var/cooldown = 35
var/current_cooldown = 0
+ var/range = 7
/obj/item/mining_scanner/attack_self(mob/user)
if(!user.client)
return
if(current_cooldown <= world.time)
current_cooldown = world.time + cooldown
- mineral_scan_pulse(get_turf(user))
+ mineral_scan_pulse(get_turf(user), range)
//Debug item to identify all ore spread quickly
/obj/item/mining_scanner/admin
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 27259ce812..31ee37c3b0 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -66,6 +66,16 @@
force = 19
custom_materials = list(/datum/material/diamond=4000)
+/obj/item/pickaxe/rosegold
+ name = "rose gold pickaxe"
+ icon_state = "rgpickaxe"
+ item_state = "rgpickaxe"
+ toolspeed = 0.1
+ desc = "A pickaxe with a light rose gold head and some red glowing runes. Extremely robust at cracking rock walls and digging up dirt."
+ force = 19
+ custom_materials = list(/datum/material/gold=4000)
+ digrange = 3
+
/obj/item/pickaxe/plasteel
name = "plasteel-tipped pickaxe"
icon_state = "titaxe"
diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm
index c17b62ba6c..e9f8079400 100644
--- a/code/modules/mining/equipment/wormhole_jaunter.dm
+++ b/code/modules/mining/equipment/wormhole_jaunter.dm
@@ -18,6 +18,15 @@
SSblackbox.record_feedback("tally", "jaunter", 1, "User") // user activated
activate(user, TRUE)
+/obj/item/wormhole_jaunter/equipped(mob/user, slot)
+ . = ..()
+ if(slot == SLOT_BELT)
+ RegisterSignal(user, COMSIG_MOVABLE_CHASM_DROP, .proc/chasm_react)
+
+/obj/item/wormhole_jaunter/dropped(mob/user)
+ . = ..()
+ UnregisterSignal(user, COMSIG_MOVABLE_CHASM_DROP)
+
/obj/item/wormhole_jaunter/proc/turf_check(mob/user)
var/turf/device_turf = get_turf(user)
if(!device_turf || is_centcom_level(device_turf.z) || is_reserved_level(device_turf.z))
@@ -71,13 +80,14 @@
SSblackbox.record_feedback("tally", "jaunter", 1, "EMP") // EMP accidental activation
activate(M)
-/obj/item/wormhole_jaunter/proc/chasm_react(mob/user)
- if(user.get_item_by_slot(SLOT_BELT) == src)
- to_chat(user, "Your [name] activates, saving you from the chasm!")
- SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation
- activate(user, FALSE, TRUE)
- else
- to_chat(user, "[src] is not attached to your belt, preventing it from saving you from the chasm. RIP.")
+/obj/item/wormhole_jaunter/proc/chasm_react(mob/source, datum/component/chasm/C)
+ to_chat(source, "Your [name] activates, saving you from the chasm!")
+ SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation
+ activate(source, FALSE, TRUE)
+ if(C)
+ var/atom/A = C.parent
+ A.visible_message("[source] falls into [A]!")
+ return TRUE
//jaunter tunnel
/obj/effect/portal/jaunt_tunnel
diff --git a/code/modules/mining/laborcamp/laborshuttle.dm b/code/modules/mining/laborcamp/laborshuttle.dm
index dbe607f42f..2923e8447e 100644
--- a/code/modules/mining/laborcamp/laborshuttle.dm
+++ b/code/modules/mining/laborcamp/laborshuttle.dm
@@ -6,7 +6,6 @@
possible_destinations = "laborcamp_home;laborcamp_away"
req_access = list(ACCESS_BRIG)
-
/obj/machinery/computer/shuttle/labor/one_way
name = "prisoner shuttle console"
desc = "A one-way shuttle console, used to summon the shuttle to the labor camp."
@@ -14,14 +13,19 @@
circuit = /obj/item/circuitboard/computer/labor_shuttle/one_way
req_access = list( )
-/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list)
- if(href_list["move"])
- var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp")
- if(!M)
- to_chat(usr, "Cannot locate shuttle!")
- return 0
- var/obj/docking_port/stationary/S = M.get_docked()
- if(S && S.name == "laborcamp_away")
- to_chat(usr, "Shuttle is already at the outpost!")
- return 0
- ..()
\ No newline at end of file
+/obj/machinery/computer/shuttle/labor/one_way/ui_act(action, params)
+ if(!allowed(usr))
+ to_chat(usr, "Access denied.")
+ return
+
+ switch(action)
+ if("move")
+ var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp")
+ if(!M)
+ to_chat(usr, "Cannot locate shuttle!")
+ return
+ var/obj/docking_port/stationary/S = M.get_docked()
+ if(S?.name == "laborcamp_away")
+ to_chat(usr, "Shuttle is already at the outpost!")
+ return
+ return ..()
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 73851283b1..a967fea75a 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -7,81 +7,199 @@
icon_state = "necrocrate"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/structure/closet/crate/necropolis/tendril/random
+ name = "necropolis crate"
+ desc = "A chest for a chest, a head for a head."
+
+/obj/structure/closet/crate/necropolis/tendril/random/PopulateContents()
+ var/loot = rand(1,3)
+ switch(loot)
+ if(1)
+ new /obj/structure/closet/crate/necropolis/tendril/magic(src)
+ if(2)
+ new /obj/structure/closet/crate/necropolis/tendril/weapon_armor(src)
+ if(3)
+ new /obj/structure/closet/crate/necropolis/tendril/misc(src)
+
/obj/structure/closet/crate/necropolis/tendril
desc = "It's watching you suspiciously."
-/obj/structure/closet/crate/necropolis/tendril/PopulateContents()
+/obj/structure/closet/crate/necropolis/tendril/magic
+ name = "relic necropolis chest"
+
+/obj/structure/closet/crate/necropolis/tendril/weapon_armor
+ name = "armament necropolis chest"
+
+/obj/structure/closet/crate/necropolis/tendril/misc
+
+/obj/structure/closet/crate/necropolis/tendril/all
+ desc = "It's watching you suspiciously."
+
+/obj/structure/closet/crate/necropolis/tendril/magic/PopulateContents()
+ var/loot = rand(1,10)
+ switch(loot)
+ if(1)
+ new /obj/item/soulstone/anybody(src)
+ if(2)
+ new /obj/item/rod_of_asclepius(src)
+ if(3)
+ new /obj/item/organ/heart/cursed/wizard(src)
+ if(4)
+ new /obj/item/book/granter/spell/summonitem(src)
+ if(5)
+ new /obj/item/borg/upgrade/modkit/lifesteal(src)
+ new /obj/item/bedsheet/cult(src)
+ if(6)
+ new /obj/item/clothing/neck/necklace/memento_mori(src)
+ if(7)
+ new /obj/item/warp_cube/red(src)
+ if(8)
+ new /obj/item/immortality_talisman(src)
+ if(9)
+ new /obj/item/gun/magic/wand/book/healing(src)
+ if(10)
+ new /obj/item/reagent_containers/glass/bottle/ichor/red(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/blue(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/green(src)
+
+/obj/structure/closet/crate/necropolis/tendril/weapon_armor/PopulateContents()
+ var/loot = rand(1,11)
+ switch(loot)
+ if(1)
+ new /obj/item/clothing/suit/space/hardsuit/cult(src)
+ if(2)
+ new /obj/item/katana/lavaland(src)
+ if(3)
+ if(prob(50))
+ new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src)
+ else
+ new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
+ if(4)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker/old(src)
+ if(5)
+ new /obj/item/nullrod/scythe/talking(src)
+ if(6)
+ new /obj/item/nullrod/armblade(src)
+ if(7)
+ new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor/old(src)
+ if(8)
+ new /obj/item/grenade/clusterbuster/inferno(src)
+ if(9)
+ new /obj/item/gun/magic/wand/book/shock(src)
+ if(10)
+ new /obj/item/gun/magic/wand/book/page(src)
+ if(11)
+ new /obj/item/gun/magic/wand/book/spark(src)
+
+/obj/structure/closet/crate/necropolis/tendril/misc/PopulateContents()
+ var/loot = rand(1,14)
+ switch(loot)
+ if(1)
+ new /obj/item/shared_storage/red(src)
+ if(2)
+ new /obj/item/reagent_containers/glass/bottle/potion/flight(src)
+ if(3)
+ new /obj/item/ship_in_a_bottle(src)
+ if(4)
+ new /obj/item/voodoo(src)
+ if(5)
+ new /obj/item/book_of_babel(src)
+ if(6)
+ new /obj/item/jacobs_ladder(src)
+ if(7)
+ if(prob(50))
+ new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src)
+ else
+ new /obj/item/disk/design_disk/modkit_disc/bounty(src)
+ if(8)
+ new /obj/item/wisp_lantern(src)
+ if(9)
+ new /obj/item/pickaxe/rosegold(src)
+ if(10)
+ new /obj/item/bedsheet/cosmos(src)
+ new /obj/item/melee/skateboard/hoverboard(src)
+ if(11)
+ new /obj/item/disk/tech_disk/illegal(src)
+ if(12)
+ new /obj/item/clothing/suit/space/hardsuit/cult(src)
+ if(13)
+ new /obj/item/katana/lavaland(src)
+ if(14)
+ if(prob(50))
+ new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src)
+ else
+ new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
+
+/obj/structure/closet/crate/necropolis/tendril/all/PopulateContents()
var/loot = rand(1,29)
switch(loot)
if(1)
new /obj/item/shared_storage/red(src)
if(2)
- new /obj/item/clothing/suit/space/hardsuit/cult(src)
- if(3)
- new /obj/item/soulstone/anybody(src)
- if(4)
- new /obj/item/katana/cursed(src)
- if(5)
- new /obj/item/clothing/glasses/godeye(src)
- if(6)
new /obj/item/reagent_containers/glass/bottle/potion/flight(src)
- if(7)
- new /obj/item/pickaxe/diamond(src)
- if(8)
- if(prob(50))
- new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src)
- else
- new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
- if(9)
- new /obj/item/rod_of_asclepius(src)
- if(10)
- new /obj/item/organ/heart/cursed/wizard(src)
- if(11)
+ if(3)
new /obj/item/ship_in_a_bottle(src)
- if(12)
- new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker/old(src)
- if(13)
+ if(4)
+ new /obj/item/voodoo(src)
+ if(5)
+ new /obj/item/book_of_babel(src)
+ if(6)
new /obj/item/jacobs_ladder(src)
- if(14)
- new /obj/item/nullrod/scythe/talking(src)
- if(15)
- new /obj/item/nullrod/armblade(src)
- if(16)
- new /obj/item/guardiancreator(src)
- if(17)
+ if(7)
if(prob(50))
new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src)
else
new /obj/item/disk/design_disk/modkit_disc/bounty(src)
- if(18)
- new /obj/item/warp_cube/red(src)
- if(19)
+ if(8)
new /obj/item/wisp_lantern(src)
- if(20)
- new /obj/item/immortality_talisman(src)
- if(21)
- new /obj/item/gun/magic/hook(src)
- if(22)
- new /obj/item/voodoo(src)
- if(23)
- new /obj/item/grenade/clusterbuster/inferno(src)
- if(24)
+ if(9)
+ new /obj/item/pickaxe/rosegold(src)
+ if(10)
+ new /obj/item/bedsheet/cosmos(src)
+ new /obj/item/melee/skateboard/hoverboard(src)
+ if(11)
+ new /obj/item/disk/tech_disk/illegal(src)
+ if(12)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker/old(src)
+ if(13)
+ new /obj/item/nullrod/scythe/talking(src)
+ if(14)
+ new /obj/item/nullrod/armblade(src)
+ if(15)
new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src)
new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor/old(src)
- if(25)
+ if(16)
+ new /obj/item/grenade/clusterbuster/inferno(src)
+ if(17)
+ new /obj/item/gun/magic/wand/book/shock(src)
+ if(18)
+ new /obj/item/gun/magic/wand/book/page(src)
+ if(19)
+ new /obj/item/gun/magic/wand/book/spark(src)
+ if(20)
+ new /obj/item/soulstone/anybody(src)
+ if(21)
+ new /obj/item/rod_of_asclepius(src)
+ if(22)
+ new /obj/item/organ/heart/cursed/wizard(src)
+ if(23)
new /obj/item/book/granter/spell/summonitem(src)
- if(26)
- new /obj/item/book_of_babel(src)
- if(27)
+ if(24)
new /obj/item/borg/upgrade/modkit/lifesteal(src)
new /obj/item/bedsheet/cult(src)
- if(28)
+ if(25)
new /obj/item/clothing/neck/necklace/memento_mori(src)
+ if(26)
+ new /obj/item/warp_cube/red(src)
+ if(27)
+ new /obj/item/immortality_talisman(src)
+ if(28)
+ new /obj/item/gun/magic/wand/book/healing(src)
if(29)
- if(prob(50))
- new /obj/item/malf_upgrade(src)
- else
- new /obj/item/disk/tech_disk/illegal(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/red(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/blue(src)
+ new /obj/item/reagent_containers/glass/bottle/ichor/green(src)
//KA modkit design discs
/obj/item/disk/design_disk/modkit_disc
@@ -747,7 +865,7 @@
new /obj/item/lava_staff(src)
if(3)
new /obj/item/book/granter/spell/sacredflame(src)
- new /obj/item/gun/magic/wand/fireball(src)
+ new /obj/item/gun/magic/hook(src)
if(4)
new /obj/item/dragons_blood(src)
@@ -987,7 +1105,7 @@
if(2)
new /obj/item/gun/ballistic/revolver/doublebarrel/super(src)
if(3)
- new /obj/item/gun/magic/staff/spellblade(src)
+ new /obj/item/guardiancreator(src)
/obj/structure/closet/crate/necropolis/bubblegum/crusher
name = "bloody bubblegum chest"
@@ -1076,6 +1194,7 @@
var/random_crystal = pick(choices)
new random_crystal(src)
new /obj/item/organ/vocal_cords/colossus(src)
+ new /obj/item/clothing/glasses/godeye(src)
/obj/structure/closet/crate/necropolis/colossus/crusher
name = "angelic colossus chest"
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 143aba3fe4..cae9f5b742 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -76,7 +76,7 @@
circuit = /obj/item/circuitboard/computer/mining_shuttle
shuttleId = "mining"
possible_destinations = "mining_home;mining_away;landing_zone_dock;mining_public"
- no_destination_swap = 1
+ no_destination_swap = TRUE
var/static/list/dumb_rev_heads = list()
/obj/machinery/computer/shuttle/mining/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm
index bd3a3d304a..87f9d716bd 100644
--- a/code/modules/mob/dead/dead.dm
+++ b/code/modules/mob/dead/dead.dm
@@ -17,7 +17,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
prepare_huds()
if(length(CONFIG_GET(keyed_list/cross_server)))
- verbs += /mob/dead/proc/server_hop
+ add_verb(src, /mob/dead/proc/server_hop)
set_focus(src)
return INITIALIZE_HINT_NORMAL
@@ -42,27 +42,25 @@ INITIALIZE_IMMEDIATE(/mob/dead)
loc = destination
Moved(oldloc, NONE, TRUE)
-/mob/dead/Stat()
- ..()
-
- if(!statpanel("Status"))
- return
- //stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]")
+/mob/dead/get_status_tab_items()
+ . = ..()
+ . += ""
+ //. += "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]"
if(SSticker.HasRoundStarted())
return
var/time_remaining = SSticker.GetTimeLeft()
if(time_remaining > 0)
- stat(null, "Time To Start: [round(time_remaining/10)]s")
+ . += "Time To Start: [round(time_remaining/10)]s"
else if(time_remaining == -10)
- stat(null, "Time To Start: DELAYED")
+ . += "Time To Start: DELAYED"
else
- stat(null, "Time To Start: SOON")
+ . += "Time To Start: SOON"
- stat(null, "Players: [SSticker.totalPlayers]")
+ . += "Players: [SSticker.totalPlayers]"
if(client.holder)
- stat(null, "Players Ready: [SSticker.totalPlayersReady]")
+ . += "Players Ready: [SSticker.totalPlayersReady]"
/mob/dead/proc/server_hop()
set category = "OOC"
@@ -74,7 +72,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
var/pick
switch(csa.len)
if(0)
- verbs -= /mob/dead/proc/server_hop
+ remove_verb(src, /mob/dead/proc/server_hop)
to_chat(src, "Server Hop has been disabled.")
if(1)
pick = csa[0]
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 768c4c943e..1fdf3102a9 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -295,6 +295,7 @@
if(observer.client && observer.client.prefs)
observer.real_name = observer.client.prefs.real_name
observer.name = observer.real_name
+ observer.client.init_verbs()
observer.update_icon()
observer.stop_sound_channel(CHANNEL_LOBBYMUSIC)
QDEL_NULL(mind)
@@ -392,7 +393,7 @@
job.standard_assign_skills(character.mind)
SSticker.minds += character.mind
-
+ character.client.init_verbs() // init verbs for the late join
var/mob/living/carbon/human/humanc
if(ishuman(character))
humanc = character //Let's retypecast the var to be human,
@@ -414,6 +415,8 @@
give_magic(humanc)
if(GLOB.curse_of_madness_triggered)
give_madness(humanc, GLOB.curse_of_madness_triggered)
+ if(humanc.client)
+ humanc.client.prefs.post_copy_to(humanc)
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
@@ -567,7 +570,7 @@
client.prefs.scars_list["[cur_scar_index]"] = valid_scars
client.prefs.save_character()
- client.prefs.copy_to(H)
+ client.prefs.copy_to(H, initial_spawn = TRUE)
H.dna.update_dna_identity()
if(mind)
if(transfer_after)
@@ -577,7 +580,7 @@
mind.original_character = H
H.name = real_name
-
+ client.init_verbs()
. = H
new_character = .
if(transfer_after)
diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm
index b6d9a3d476..84f5e97a3b 100644
--- a/code/modules/mob/dead/new_player/poll.dm
+++ b/code/modules/mob/dead/new_player/poll.dm
@@ -229,7 +229,7 @@
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x250")
if(POLLTYPE_IRV)
- var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/IRV)
+ var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/irv)
irv_assets.send(src)
var/datum/DBQuery/query_irv_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index 19e6ca4720..c3c0787aa8 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -43,7 +43,7 @@
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
// Apply the Dummy's preview background first so we properly layer everything else on top of it.
mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER))
- copy_to(mannequin)
+ copy_to(mannequin, initial_spawn = TRUE)
if(previewJob && equip_job)
mannequin.job = previewJob.title
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index a0df1ee938..e1d078c632 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -1,6 +1,9 @@
GLOBAL_LIST_EMPTY(ghost_images_default) //this is a list of the default (non-accessorized, non-dir) images of the ghosts themselves
GLOBAL_LIST_EMPTY(ghost_images_simple) //this is a list of all ghost images as the simple white ghost
-
+GLOBAL_LIST_INIT(ghost_verbs, list(
+ /mob/dead/observer/proc/dead_tele,
+ /mob/dead/observer/proc/open_spawners_menu,
+ /mob/dead/observer/proc/view_gas))
GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer
@@ -60,10 +63,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer/Initialize(mapload, mob/body)
set_invisibility(GLOB.observer_default_invisibility)
- verbs += list(
- /mob/dead/observer/proc/dead_tele,
- /mob/dead/observer/proc/open_spawners_menu,
- /mob/dead/observer/proc/view_gas)
+ add_verb(src, GLOB.ghost_verbs)
if(icon_state in GLOB.ghost_forms_with_directions_list)
ghostimage_default = image(src.icon,src,src.icon_state + "_nodir")
@@ -118,8 +118,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
real_name = name
if(!fun_verbs)
- verbs -= /mob/dead/observer/verb/boo
- verbs -= /mob/dead/observer/verb/possess
+ remove_verb(src, /mob/dead/observer/verb/boo)
+ remove_verb(src, /mob/dead/observer/verb/possess)
animate(src, pixel_y = 2, time = 10, loop = -1)
@@ -278,6 +278,7 @@ Works together with spawning an observer, noted above.
if (!(client.prefs.chat_toggles & CHAT_OOC))
client.prefs.chat_toggles ^= CHAT_OOC
transfer_ckey(ghost, FALSE)
+ ghost.client.init_verbs()
if(penalize)
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
@@ -408,9 +409,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(usr, "Another consciousness is in your body...It is resisting you.")
return
client.change_view(CONFIG_GET(string/default_view))
- SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
transfer_ckey(mind.current, FALSE)
- return 1
+ SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
+ mind.current.client.init_verbs()
+ return TRUE
/mob/dead/observer/verb/stay_dead()
set category = "Ghost"
@@ -847,11 +849,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
ghostimage_simple.icon_state = icon_state
if(NAMEOF(src, fun_verbs))
if(fun_verbs)
- verbs += /mob/dead/observer/verb/boo
- verbs += /mob/dead/observer/verb/possess
+ add_verb(src, /mob/dead/observer/verb/boo)
+ add_verb(src, /mob/dead/observer/verb/possess)
else
- verbs -= /mob/dead/observer/verb/boo
- verbs -= /mob/dead/observer/verb/possess
+ remove_verb(src, /mob/dead/observer/verb/boo)
+ remove_verb(src, /mob/dead/observer/verb/possess)
/mob/dead/observer/reset_perspective(atom/A)
if(client)
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index b81172afad..3aa5f8e302 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -80,3 +80,8 @@
data["npcs"] = npcs
return data
+
+/datum/orbit_menu/ui_assets()
+ . = ..() || list()
+ . += get_asset_datum(/datum/asset/simple/orbit)
+
diff --git a/code/modules/mob/living/brain/emote.dm b/code/modules/mob/living/brain/emote.dm
index 540c7b3949..e1cb1a29cc 100644
--- a/code/modules/mob/living/brain/emote.dm
+++ b/code/modules/mob/living/brain/emote.dm
@@ -1,13 +1,3 @@
-/datum/emote/brain
- mob_type_allowed_typecache = list(/mob/living/brain)
- mob_type_blacklist_typecache = list()
-
-/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE)
- . = ..()
- var/mob/living/brain/B = user
- if(!istype(B) || (!(B.container && istype(B.container, /obj/item/mmi))))
- return FALSE
-
/datum/emote/brain/alarm
key = "alarm"
message = "sounds an alarm."
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index 951185ee92..e7ad67d240 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -33,8 +33,8 @@
var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?")
/mob/living/carbon/alien/Initialize()
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
create_bodyparts() //initialize bodyparts
@@ -93,11 +93,9 @@
/mob/living/carbon/alien/IsAdvancedToolUser()
return has_fine_manipulation
-/mob/living/carbon/alien/Stat()
- ..()
-
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
+/mob/living/carbon/alien/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
/mob/living/carbon/alien/getTrail()
if(getBruteLoss() < 200)
diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm
index 0ab448d2c5..963981b51e 100644
--- a/code/modules/mob/living/carbon/alien/emote.dm
+++ b/code/modules/mob/living/carbon/alien/emote.dm
@@ -1,6 +1,3 @@
-/datum/emote/living/alien
- mob_type_allowed_typecache = list(/mob/living/carbon/alien)
-
/datum/emote/living/alien/gnarl
key = "gnarl"
key_third_person = "gnarls"
diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
index 3827aef8c1..399efc4b29 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
@@ -148,10 +148,10 @@ Doesn't work on other aliens/AI.*/
action_icon_state = "alien_acid"
/obj/effect/proc_holder/alien/acid/on_gain(mob/living/carbon/user)
- user.verbs.Add(/mob/living/carbon/proc/corrosive_acid)
+ add_verb(user, /mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/on_lose(mob/living/carbon/user)
- user.verbs.Remove(/mob/living/carbon/proc/corrosive_acid)
+ remove_verb(user, /mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/proc/corrode(atom/target,mob/living/carbon/user = usr)
if(target in oview(1,user))
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index 93050a4f9d..8f0ef2b384 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -30,10 +30,9 @@
..()
//This needs to be fixed
-/mob/living/carbon/alien/larva/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Progress: [amount_grown]/[max_grown]")
+/mob/living/carbon/alien/larva/get_status_tab_items()
+ . = ..()
+ . += "Progress: [amount_grown]/[max_grown]"
/mob/living/carbon/alien/larva/adjustPlasma(amount)
if(stat != DEAD && amount > 0)
diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm
index d63686691d..0a2434055f 100644
--- a/code/modules/mob/living/carbon/alien/life.dm
+++ b/code/modules/mob/living/carbon/alien/life.dm
@@ -40,9 +40,6 @@
if(move_delay_add > 0)
move_delay_add = max(0, move_delay_add - rand(1, 2))
-/mob/living/carbon/alien/handle_changeling()
- return
-
/mob/living/carbon/alien/handle_fire()//Aliens on fire code
if(..())
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index b7d67e49a6..1706763f6b 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -481,16 +481,17 @@
var/turf/target = get_turf(loc)
I.safe_throw_at(target,I.throw_range,I.throw_speed,src, force = move_force)
-/mob/living/carbon/Stat()
- ..()
- if(statpanel("Status"))
- var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
- if(vessel)
- stat(null, "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]")
- if(locate(/obj/item/assembly/health) in src)
- stat(null, "Health: [health]")
+/mob/living/carbon/get_status_tab_items()
+ . = ..()
+ var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
+ if(vessel)
+ . += "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]"
+ if(locate(/obj/item/assembly/health) in src)
+ . += "Health: [health]"
- add_abilities_to_panel()
+/mob/living/carbon/get_proc_holders()
+ . = ..()
+ . += add_abilities_to_panel()
/mob/living/carbon/attack_ui(slot)
if(!has_hand_for_held_index(active_hand_index))
diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm
index e3512e3929..49501e0315 100644
--- a/code/modules/mob/living/carbon/emote.dm
+++ b/code/modules/mob/living/carbon/emote.dm
@@ -1,6 +1,3 @@
-/datum/emote/living/carbon
- mob_type_allowed_typecache = list(/mob/living/carbon)
-
/datum/emote/living/carbon/airguitar
key = "airguitar"
message = "is strumming the air and headbanging like a safari chimp."
@@ -22,7 +19,6 @@
muzzle_ignore = TRUE
restraint_check = TRUE
emote_type = EMOTE_AUDIBLE
- mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
/datum/emote/living/carbon/clap/run_emote(mob/living/user, params)
. = ..()
@@ -41,7 +37,6 @@
key = "gnarl"
key_third_person = "gnarls"
message = "gnarls and shows its teeth..."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/moan
key = "moan"
@@ -55,27 +50,23 @@
key = "roll"
key_third_person = "rolls"
message = "rolls."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/scratch
key = "scratch"
key_third_person = "scratches"
message = "scratches."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/screech
key = "screech"
key_third_person = "screeches"
message = "screeches."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/sign
key = "sign"
key_third_person = "signs"
message_param = "signs the number %t."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/sign/select_param(mob/user, params)
@@ -87,13 +78,11 @@
key = "signal"
key_third_person = "signals"
message_param = "raises %t fingers."
- mob_type_allowed_typecache = list(/mob/living/carbon/human)
restraint_check = TRUE
/datum/emote/living/carbon/tail
key = "tail"
message = "waves their tail."
- mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/wink
key = "wink"
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 2f5c94b784..ed1ba3852f 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -18,6 +18,9 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/mob/living/carbon/human/dummy/Life()
return
+/mob/living/carbon/human/dummy/update_mobility()
+ return
+
/mob/living/carbon/human/dummy/proc/wipe_state()
delete_equipment()
icon_render_key = null
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 04747ffcb4..837b4f73cf 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -1,6 +1,3 @@
-/datum/emote/living/carbon/human
- mob_type_allowed_typecache = list(/mob/living/carbon/human)
-
/datum/emote/living/carbon/human/cry
key = "cry"
key_third_person = "cries"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index ecd484032f..6af171568b 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -113,14 +113,14 @@
. += effects_exam
//CIT CHANGES START HERE - adds genital details to examine text
- if(LAZYLEN(internal_organs))
+ if(LAZYLEN(internal_organs) && CHECK_BITFIELD(user.client?.prefs.cit_toggles, GENITAL_EXAMINE))
for(var/obj/item/organ/genital/dicc in internal_organs)
if(istype(dicc) && dicc.is_exposed())
. += "[dicc.desc]"
-
- var/cursed_stuff = attempt_vr(src,"examine_bellies",args) //vore Code
- if(cursed_stuff)
- . += cursed_stuff
+ if(CHECK_BITFIELD(user.client?.prefs.cit_toggles, VORE_EXAMINE))
+ var/cursed_stuff = attempt_vr(src,"examine_bellies",args) //vore Code
+ if(cursed_stuff)
+ . += cursed_stuff
//END OF CIT CHANGES
//Jitters
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 38b420aaba..389787df67 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -6,10 +6,11 @@
appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE
/mob/living/carbon/human/Initialize()
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
- verbs += /mob/living/carbon/human/proc/underwear_toggle //fwee
-
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
+ add_verb(src, /mob/living/carbon/human/verb/underwear_toggle)
+ add_verb(src, /mob/living/verb/subtle)
+ add_verb(src, /mob/living/verb/subtler)
//initialize limbs first
create_bodyparts()
@@ -61,55 +62,51 @@
//...and display them.
add_to_all_human_data_huds()
-/mob/living/carbon/human/Stat()
- ..()
-
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
- stat(null, "Move Mode: [m_intent]")
- if (internal)
- if (!internal.air_contents)
- qdel(internal)
- else
- stat("Internal Atmosphere Info", internal.name)
- stat("Tank Pressure", internal.air_contents.return_pressure())
- stat("Distribution Pressure", internal.distribute_pressure)
-
- if(mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
- stat("Absorbed DNA", changeling.absorbedcount)
-
-
+/mob/living/carbon/human/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
+ . += "Move Mode: [m_intent]"
+ if(internal)
+ if(!internal.air_contents)
+ qdel(internal)
+ else
+ . += ""
+ . += "Internal Atmosphere Info: [internal.name]"
+ . += "Tank Pressure: [internal.air_contents.return_pressure()]"
+ . += "Distribution Pressure: [internal.distribute_pressure]"
+ if(mind)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ . += ""
+ . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
+ . += "Absorbed DNA: [changeling.absorbedcount]"
//NINJACODE
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
- if(statpanel("SpiderOS"))
- stat("SpiderOS Status:","[SN.s_initialized ? "Initialized" : "Disabled"]")
- stat("Current Time:", "[STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]")
- if(SN.s_initialized)
- //Suit gear
- stat("Energy Charge:", "[round(SN.cell.charge/100)]%")
- stat("Smoke Bombs:", "\Roman [SN.s_bombs]")
- //Ninja status
- stat("Fingerprints:", "[md5(dna.uni_identity)]")
- stat("Unique Identity:", "[dna.unique_enzymes]")
- stat("Overall Status:", "[stat > 1 ? "dead" : "[health]% healthy"]")
- stat("Nutrition Status:", "[nutrition]")
- stat("Oxygen Loss:", "[getOxyLoss()]")
- stat("Toxin Levels:", "[getToxLoss()]")
- stat("Burn Severity:", "[getFireLoss()]")
- stat("Brute Trauma:", "[getBruteLoss()]")
- stat("Radiation Levels:","[radiation] rad")
- stat("Body Temperature:","[bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)")
+ . += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]"
+ . += "Current Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]"
+ if(SN.s_initialized)
+ //Suit gear
+ . += "Energy Charge: [round(SN.cell.charge/100)]%"
+ . += "Smoke Bombs: \Roman [SN.s_bombs]"
+ //Ninja status
+ . += "Fingerprints: [md5(dna.uni_identity)]"
+ . += "Unique Identity: [dna.unique_enzymes]"
+ . += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]"
+ . += "Nutrition Status: [nutrition]"
+ . += "Oxygen Loss: [getOxyLoss()]"
+ . += "Toxin Levels: [getToxLoss()]"
+ . += "Burn Severity: [getFireLoss()]"
+ . += "Brute Trauma: [getBruteLoss()]"
+ . += "Radiation Levels: [radiation] rad"
+ . += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)"
- //Diseases
- if(diseases.len)
- stat("Viruses:", null)
- for(var/thing in diseases)
- var/datum/disease/D = thing
- stat("*", "[D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]")
+ //Diseases
+ if(length(diseases))
+ . += "Viruses:"
+ for(var/thing in diseases)
+ var/datum/disease/D = thing
+ . += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]"
/mob/living/carbon/human/show_inv(mob/user)
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index af95b9e1b7..5f30d902b1 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -75,12 +75,8 @@
. = pda.owner
else if(istype(tablet))
var/obj/item/computer_hardware/card_slot/card_slot = tablet.all_components[MC_CARD]
- if(card_slot && (card_slot.stored_card2 || card_slot.stored_card))
- if(card_slot.stored_card2) //The second card is the one used for authorization in the ID changing program, so we prioritize it here for consistency
- . = card_slot.stored_card2.registered_name
- else
- if(card_slot.stored_card)
- . = card_slot.stored_card.registered_name
+ if(card_slot?.stored_card)
+ . = card_slot.stored_card.registered_name
if(!.)
. = if_no_id //to prevent null-names making the mob unclickable
return
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 6991b3efcd..d433ca1821 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1381,6 +1381,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
+ if(!affecting) //Maybe the bodypart is missing? Or things just went wrong..
+ affecting = target.get_bodypart(BODY_ZONE_CHEST) //target chest instead, as failsafe. Or hugbox? You decide.
+
var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
if(attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)
miss_chance = 0
@@ -1596,6 +1599,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H, attackchain_flags = NONE, damage_multiplier = 1)
var/totitemdamage = H.pre_attacked_by(I, user) * damage_multiplier
+
+ if(!affecting) //Something went wrong. Maybe the limb is missing?
+ affecting = H.get_bodypart(BODY_ZONE_CHEST) //If the limb is missing, or something went terribly wrong, just hit the chest instead
+
// Allows you to put in item-specific reactions based on species
if(user != H)
var/list/block_return = list()
@@ -1607,8 +1614,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
return 0
var/hit_area
- if(!affecting) //Something went wrong. Maybe the limb is missing?
- affecting = H.bodyparts[1]
hit_area = affecting.name
var/def_zone = affecting.body_zone
@@ -1958,19 +1963,19 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
switch((loc_temp - H.bodytemperature)*thermal_protection)
if(-INFINITY to -50)
- H.throw_alert("temp", /obj/screen/alert/cold, 3)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 3)
if(-50 to -35)
- H.throw_alert("temp", /obj/screen/alert/cold, 2)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 2)
if(-35 to -20)
- H.throw_alert("temp", /obj/screen/alert/cold, 1)
+ H.throw_alert("tempfeel", /obj/screen/alert/cold, 1)
if(-20 to 0) //This is the sweet spot where air is considered normal
- H.clear_alert("temp")
+ H.clear_alert("tempfeel")
if(0 to 15) //When the air around you matches your body's temperature, you'll start to feel warm.
- H.throw_alert("temp", /obj/screen/alert/hot, 1)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 1)
if(15 to 30)
- H.throw_alert("temp", /obj/screen/alert/hot, 2)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 2)
if(30 to INFINITY)
- H.throw_alert("temp", /obj/screen/alert/hot, 3)
+ H.throw_alert("tempfeel", /obj/screen/alert/hot, 3)
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
@@ -1988,6 +1993,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
firemodifier = min(firemodifier, 0)
burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) // this can go below 5 at log 2.5
+ if (burn_damage)
+ switch(burn_damage)
+ if(0 to 2)
+ H.throw_alert("temp", /obj/screen/alert/sweat, 1)
+ if(2 to 4)
+ H.throw_alert("temp", /obj/screen/alert/sweat, 2)
+ else
+ H.throw_alert("temp", /obj/screen/alert/sweat, 3)
burn_damage = burn_damage * heatmod * H.physiology.heat_mod
if (H.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) //40% for level 3 damage on humans
H.emote("scream")
@@ -2000,14 +2013,18 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
switch(H.bodytemperature)
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
+ H.throw_alert("temp", /obj/screen/alert/shiver, 1)
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod*H.physiology.cold_mod, BURN)
if(120 to 200)
+ H.throw_alert("temp", /obj/screen/alert/shiver, 2)
H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod*H.physiology.cold_mod, BURN)
else
+ H.throw_alert("temp", /obj/screen/alert/shiver, 3)
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod*H.physiology.cold_mod, BURN)
else
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
+ H.clear_alert("temp")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
index d8dfe63b35..f3b499093b 100644
--- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm
+++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
@@ -83,17 +83,7 @@
/obj/item/organ/tongue/dullahan
zone = "abstract"
- modifies_speech = TRUE
-
-/obj/item/organ/tongue/dullahan/handle_speech(datum/source, list/speech_args)
- if(ishuman(owner))
- var/mob/living/carbon/human/H = owner
- if(isdullahan(H))
- var/datum/species/dullahan/D = H.dna.species
- if(isobj(D.myhead.loc))
- var/obj/O = D.myhead.loc
- O.say(speech_args[SPEECH_MESSAGE])
- speech_args[SPEECH_MESSAGE] = ""
+ accents = list(/datum/accent/dullahan)
/obj/item/organ/ears/dullahan
zone = "abstract"
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 49c55f70b3..43f5ca5526 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -18,7 +18,8 @@
var/datum/action/innate/regenerate_limbs/regenerate_limbs
var/datum/action/innate/slime_change/slime_change //CIT CHANGE
liked_food = TOXIC | MEAT
- toxic_food = null
+ disliked_food = null
+ toxic_food = ANTITOXIC
coldmod = 6 // = 3x cold damage
heatmod = 0.5 // = 1/4x heat damage
burnmod = 0.5 // = 1/2x generic burn damage
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 723c4848fe..857fb6d8ed 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -114,6 +114,14 @@
#undef VAMP_DRAIN_AMOUNT
+
+/mob/living/carbon/get_status_tab_items()
+ . = ..()
+ var/obj/item/organ/heart/vampire/darkheart = getorgan(/obj/item/organ/heart/vampire)
+ if(darkheart)
+ . += "Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM]."
+
+
/obj/item/organ/heart/vampire
name = "vampire heart"
actions_types = list(/datum/action/item_action/organ_action/vampire_heart)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index e29c6b9ffe..8f181d47fd 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -1,8 +1,4 @@
/mob/living/carbon/BiologicalLife(seconds, times_fired)
- //Updates the number of stored chemicals for powers
- handle_changeling()
- //Handles the unique mentabolism of bloodsuckers, look at /datum/antagonist/bloodsucker/proc/LifeTick()
- handle_bloodsucker()
//Reagent processing needs to come before breathing, to prevent edge cases.
handle_organs()
. = ..() // if . is false, we are dead.
@@ -410,25 +406,7 @@
for(var/thing in all_wounds)
var/datum/wound/W = thing
if(W.processes) // meh
- W.handle_process()
-
-//todo generalize this and move hud out
-/mob/living/carbon/proc/handle_changeling()
- if(mind && hud_used && hud_used.lingchemdisplay)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- changeling.regenerate()
- hud_used.lingchemdisplay.invisibility = 0
- hud_used.lingchemdisplay.maptext = "
[round(changeling.chem_charges)]
"
- else
- hud_used.lingchemdisplay.invisibility = INVISIBILITY_ABSTRACT
-
-
-/mob/living/carbon/proc/handle_bloodsucker()
- if(mind && AmBloodsucker(src))
- var/datum/antagonist/bloodsucker/B = mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
- B.LifeTick()
-
+ W.handle_process()
/mob/living/carbon/handle_mutations_and_radiation()
if(dna && dna.temporary_mutations.len)
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index 5b91119789..1f3e0a1b81 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -18,8 +18,8 @@
hud_type = /datum/hud/monkey
/mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner)
- verbs += /mob/living/proc/mob_sleep
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
if(unique_name) //used to exclude pun pun
gender = pick(MALE, FEMALE)
@@ -90,16 +90,16 @@
return
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_temperature_speedmod, TRUE, slow)
-/mob/living/carbon/monkey/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
- stat(null, "Move Mode: [m_intent]")
- if(client && mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
- stat("Absorbed DNA", changeling.absorbedcount)
+/mob/living/carbon/monkey/get_status_tab_items()
+ . = ..()
+ . += "Intent: [a_intent]"
+ . += "Move Mode: [m_intent]"
+ if(client && mind)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ . += ""
+ . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
+ . += "Absorbed DNA: [changeling.absorbedcount]"
/mob/living/carbon/monkey/verb/removeinternal()
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index a735baceae..55764d8d50 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -1,9 +1,3 @@
-
-/* EMOTE DATUMS */
-/datum/emote/living
- mob_type_allowed_typecache = /mob/living
- mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain)
-
/datum/emote/living/blush
key = "blush"
key_third_person = "blushes"
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 89321082c9..9de75702db 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -43,6 +43,7 @@
* Returns TRUE or FALSE based on if we were interrupted. This is used by overridden variants to check if they should stop.
*/
/mob/living/proc/BiologicalLife(seconds, times_fired)
+ SEND_SIGNAL(src,COMSIG_LIVING_BIOLOGICAL_LIFE, seconds, times_fired)
handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
handle_wounds()
@@ -78,6 +79,7 @@
* Returns TRUE or FALSE based on if we were interrupted. This is used by overridden variants to check if they should stop.
*/
/mob/living/proc/PhysicalLife(seconds, times_fired)
+ SEND_SIGNAL(src,COMSIG_LIVING_PHYSICAL_LIFE, seconds, times_fired)
if(digitalinvis)
handle_diginvis() //AI becomes unable to see mob
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 2a461b5921..8dac56a8c9 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1163,8 +1163,10 @@
A.action.Remove(src)
/mob/living/proc/add_abilities_to_panel()
+ var/list/L = list()
for(var/obj/effect/proc_holder/A in abilities)
- statpanel("[A.panel]",A.get_panel_text(),A)
+ L[++L.len] = list("[A.panel]",A.get_panel_text(),A.name,"[REF(A)]")
+ return L
/mob/living/lingcheck()
if(mind)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index aab741edd6..7ce1c1e7cc 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -23,6 +23,7 @@
a_intent = INTENT_HARM //so we always get pushed instead of trying to swap
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_in_dark = 8
+ deathsound = 'sound/voice/scream/android_scream.ogg'
hud_type = /datum/hud/ai
med_hud = DATA_HUD_MEDICAL_BASIC
sec_hud = DATA_HUD_SECURITY_BASIC
@@ -139,7 +140,7 @@
spark_system.set_up(5, 0, src)
spark_system.attach(src)
- verbs += /mob/living/silicon/ai/proc/show_laws_verb
+ add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb)
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = name
@@ -153,10 +154,10 @@
deploy_action.Grant(src)
if(isturf(loc))
- verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
+ add_verb(src, list(/mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, \
- /mob/living/silicon/ai/proc/set_automatic_say_channel)
+ /mob/living/silicon/ai/proc/set_automatic_say_channel))
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
@@ -208,26 +209,26 @@
display_icon_override = ai_core_icon
set_core_display_icon(ai_core_icon)
-/mob/living/silicon/ai/Stat()
- ..()
- if(statpanel("Status"))
- if(!stat)
- stat(null, text("System integrity: [(health+100)/2]%"))
- stat(null, text("Connected cyborgs: [connected_robots.len]"))
- for(var/mob/living/silicon/robot/R in connected_robots)
- var/robot_status = "Nominal"
- if(R.shell)
- robot_status = "AI SHELL"
- else if(R.stat || !R.client)
- robot_status = "OFFLINE"
- else if(!R.cell || R.cell.charge <= 0)
- robot_status = "DEPOWERED"
- //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
- stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
- Module: [R.designation] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
- stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
- else
- stat(null, text("Systems nonfunctional"))
+/mob/living/silicon/ai/get_status_tab_items()
+ . = ..()
+ if(stat != CONSCIOUS)
+ . += text("Systems nonfunctional")
+ return
+ . += text("System integrity: [(health + 100) * 0.5]%")
+ . += text("Connected cyborgs: [length(connected_robots)]")
+ for(var/r in connected_robots)
+ var/mob/living/silicon/robot/connected_robot = r
+ var/robot_status = "Nominal"
+ if(connected_robot.shell)
+ robot_status = "AI SHELL"
+ else if(connected_robot.stat != CONSCIOUS || !connected_robot.client)
+ robot_status = "OFFLINE"
+ else if(!connected_robot.cell || connected_robot.cell.charge <= 0)
+ robot_status = "DEPOWERED"
+ //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
+ . += text("[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \
+ Module: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]")
+ . += text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]") //Count of total AI shells
/mob/living/silicon/ai/proc/ai_alerts()
var/dat = "Current Station Alerts\n"
@@ -858,7 +859,7 @@
to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.")
to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.")
view_core() //A BYOND bug requires you to be viewing your core before your verbs update
- verbs += /mob/living/silicon/ai/proc/choose_modules
+ add_verb(src, /mob/living/silicon/ai/proc/choose_modules)
malf_picker = new /datum/module_picker
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 704a23b8db..6d2da187a1 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -184,13 +184,12 @@
else
client.eye = card
-/mob/living/silicon/pai/Stat()
- ..()
- if(statpanel("Status"))
- if(!stat)
- stat(null, text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]"))
- else
- stat(null, text("Systems nonfunctional"))
+/mob/living/silicon/pai/get_status_tab_items()
+ . += ..()
+ if(!stat)
+ . += text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]")
+ else
+ . += text("Systems nonfunctional")
/mob/living/silicon/pai/restrained(ignore_grab)
. = FALSE
diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm
index c0fca997f5..c532e44ff5 100644
--- a/code/modules/mob/living/silicon/robot/emote.dm
+++ b/code/modules/mob/living/silicon/robot/emote.dm
@@ -5,7 +5,7 @@
/datum/emote/sound/silicon
mob_type_allowed_typecache = list(/mob/living/silicon, /mob/living/carbon/human)
emote_type = EMOTE_AUDIBLE
- var/unrestricted = FALSE
+ var/unrestricted = TRUE
/datum/emote/sound/silicon/run_emote(mob/user, params)
if(!unrestricted && !(issilicon(user) || isipcperson(user)))
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index d8299d42ad..6143bde602 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -72,8 +72,8 @@
toner = tonermax
diag_hud_set_borgcell()
- verbs += /mob/living/proc/lay_down //CITADEL EDIT gimmie rest verb kthx
- verbs += /mob/living/silicon/robot/proc/rest_style
+ add_verb(src, /mob/living/proc/lay_down) //CITADEL EDIT gimmie rest verb kthx
+ add_verb(src, /mob/living/silicon/robot/proc/rest_style)
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy()
@@ -99,7 +99,7 @@
radio.keyslot = null
//END CITADEL EDIT
if(connected_ai)
- connected_ai.connected_robots -= src
+ set_connected_ai(null)
if(shell)
GLOB.available_ai_shells -= src
else
@@ -222,19 +222,19 @@
if(thruster_button)
thruster_button.icon_state = "ionpulse[ionpulse_on]"
-/mob/living/silicon/robot/Stat()
- ..()
- if(statpanel("Status"))
- if(cell)
- stat("Charge Left:", "[cell.charge]/[cell.maxcharge]")
- else
- stat(null, text("No Cell Inserted!"))
+/mob/living/silicon/robot/get_status_tab_items()
+ . = ..()
+ . += ""
+ if(cell)
+ . += "Charge Left: [cell.charge]/[cell.maxcharge]"
+ else
+ . += text("No Cell Inserted!")
- if(module)
- for(var/datum/robot_energy_storage/st in module.storages)
- stat("[st.name]:", "[st.energy]/[st.max_energy]")
- if(connected_ai)
- stat("Master AI:", connected_ai.name)
+ if(module)
+ for(var/datum/robot_energy_storage/st in module.storages)
+ . += "[st.name]: [st.energy]/[st.max_energy]"
+ if(connected_ai)
+ . += "Master AI: [connected_ai.name]"
/mob/living/silicon/robot/restrained(ignore_grab)
. = 0
@@ -560,11 +560,10 @@
gib()
/mob/living/silicon/robot/proc/UnlinkSelf()
- if(src.connected_ai)
- connected_ai.connected_robots -= src
- src.connected_ai = null
- lawupdate = 0
- scrambledcodes = 1
+ set_connected_ai(null)
+ lawupdate = FALSE
+ locked_down = FALSE
+ scrambledcodes = TRUE
//Disconnect it's camera so it's not so easily tracked.
if(!QDELETED(builtInCamera))
QDEL_NULL(builtInCamera)
@@ -586,10 +585,10 @@
W.attack_self(src)
-/mob/living/silicon/robot/proc/SetLockdown(state = 1)
+/mob/living/silicon/robot/proc/SetLockdown(state = TRUE)
// They stay locked down if their wire is cut.
if(wires.is_cut(WIRE_LOCKDOWN))
- state = 1
+ state = TRUE
if(state)
throw_alert("locked", /obj/screen/alert/locked)
else
@@ -991,7 +990,7 @@
builtInCamera.c_tag = real_name //update the camera name too
mainframe = AI
deployed = TRUE
- connected_ai = mainframe
+ set_connected_ai(mainframe)
mainframe.connected_robots |= src
lawupdate = TRUE
lawsync()
@@ -1090,9 +1089,8 @@
. = ..(user)
/mob/living/silicon/robot/proc/TryConnectToAI()
- connected_ai = select_active_ai_with_fewest_borgs()
+ set_connected_ai(select_active_ai_with_fewest_borgs(z))
if(connected_ai)
- connected_ai.connected_robots += src
lawsync()
lawupdate = 1
return TRUE
@@ -1134,7 +1132,7 @@
/mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1)
if(istype(cell))
- cell.charge -= amount*5
+ cell.charge -= amount * 5
/mob/living/silicon/robot/verb/viewmanifest()
set category = "Robot Commands"
@@ -1143,3 +1141,14 @@
if(usr.stat == DEAD)
return //won't work if dead
ai_roster()
+
+/mob/living/silicon/robot/proc/set_connected_ai(new_ai)
+ if(connected_ai == new_ai)
+ return
+ . = connected_ai
+ connected_ai = new_ai
+ if(.)
+ var/mob/living/silicon/ai/old_ai = .
+ old_ai.connected_robots -= src
+ if(connected_ai)
+ connected_ai.connected_robots |= src
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 746c4f469d..a0ea9b735c 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -138,8 +138,8 @@
/mob/living/silicon/robot/proc/beep_boop_rogue_bot(mob/user)
SetEmagged(1)
SetStun(60) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown
- lawupdate = 0
- connected_ai = null
+ lawupdate = FALSE
+ set_connected_ai(null)
message_admins("[ADMIN_LOOKUPFLW(user)] emagged cyborg [ADMIN_LOOKUPFLW(src)]. Laws overridden.")
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
var/time = time2text(world.realtime,"hh:mm:ss")
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 6b58988c31..7c4125fbc6 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -361,8 +361,7 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"),
"Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakemedbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed")
)
var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed")
for(var/a in L)
@@ -423,13 +422,6 @@
moduleselect_icon = "medihound"
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakemed"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakemedsleeper"
- moduleselect_icon = "medihound"
- moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
- dogborg = TRUE
else
return FALSE
return ..()
@@ -486,8 +478,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeengbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng")
)
var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng")
for(var/a in L)
@@ -545,11 +536,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "alinasleeper"
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakeeng"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -588,8 +574,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakesecbox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec")
)
var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark")
for(var/a in L)
@@ -645,11 +630,6 @@
sleeper_overlay = "valesecsleeper"
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakesec"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -693,8 +673,7 @@
var/static/list/peace_icons = sortList(list(
"Default" = image(icon = 'icons/mob/robots.dmi', icon_state = "peace"),
"Borgi" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "borgi"),
- "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakepeacebox")
+ "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider")
))
var/peace_borg_icon = show_radial_menu(R, R , peace_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE)
switch(peace_borg_icon)
@@ -710,11 +689,6 @@
hat_offset = INFINITY
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
has_snowflake_deadsprite = TRUE
- if("Drake")
- cyborg_base_icon = "drakepeace"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakepeacesleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -853,8 +827,7 @@
"(Janitor) Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinajan"),
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
- "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
- "(Janitor) Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox")
+ "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan")
)
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
"(Janitor) Scrubpuppy" = "scrubpup")
@@ -926,11 +899,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "jsleeper"
dogborg = TRUE
- if("(Janitor) Drake")
- cyborg_base_icon = "drakejanit"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakesecsleeper"
- dogborg = TRUE
else
return FALSE
return ..()
@@ -976,8 +944,7 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmin"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"),
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"),
- "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeminebox")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin")
)
var/list/L = list("Blade" = "blade", "Vale" = "valemine")
for(var/a in L)
@@ -1021,11 +988,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "valeminesleeper"
dogborg = TRUE
- if("Drake")
- cyborg_base_icon = "drakemine"
- cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
- sleeper_overlay = "drakeminesleeper"
- dogborg = TRUE
else
return FALSE
return ..()
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index c6aee397e4..944b909463 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -13,6 +13,7 @@
mob_biotypes = MOB_ROBOTIC
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
speech_span = SPAN_ROBOT
+ deathsound = 'sound/voice/borg_deathsound.ogg'
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
vore_flags = NO_VORE
diff --git a/code/modules/mob/living/simple_animal/astral.dm b/code/modules/mob/living/simple_animal/astral.dm
index f79a2b5b3e..ec2180ccb3 100644
--- a/code/modules/mob/living/simple_animal/astral.dm
+++ b/code/modules/mob/living/simple_animal/astral.dm
@@ -24,6 +24,7 @@
incorporeal_move = 1
alpha = 50
speak_emote = list("echos")
+ rad_flags = RAD_NO_CONTAMINATE
movement_type = FLYING
var/pseudo_death = FALSE
var/posses_safe = FALSE
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index d9bf8cc553..ceaf7a6f96 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -438,6 +438,16 @@
+/mob/living/simple_animal/bot/mulebot/get_status_tab_items()
+ . = ..()
+ if(cell)
+ . += "Charge Left: [cell.charge]/[cell.maxcharge]"
+ else
+ . += text("No Cell Inserted!")
+ if(load)
+ . += "Current Load: [load.name]"
+
+
/mob/living/simple_animal/bot/mulebot/call_bot()
..()
if(path && path.len)
diff --git a/code/modules/mob/living/simple_animal/friendly/bumbles.dm b/code/modules/mob/living/simple_animal/friendly/bumbles.dm
index 3707aa33f8..0debb7b98c 100644
--- a/code/modules/mob/living/simple_animal/friendly/bumbles.dm
+++ b/code/modules/mob/living/simple_animal/friendly/bumbles.dm
@@ -34,7 +34,7 @@
/mob/living/simple_animal/pet/bumbles/Initialize()
. = ..()
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/lay_down)
/mob/living/simple_animal/pet/bumbles/ComponentInitialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index c21875d1db..71ae3b4c03 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -39,7 +39,7 @@
/mob/living/simple_animal/pet/cat/Initialize()
. = ..()
- verbs += /mob/living/proc/lay_down
+ add_verb(src, /mob/living/proc/lay_down)
/mob/living/simple_animal/pet/cat/ComponentInitialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
index da39fb71cf..55339a02fd 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -142,8 +142,8 @@
set_light(2, 0.5)
qdel(access_card) //we don't have free access
access_card = null
- verbs -= /mob/living/simple_animal/drone/verb/check_laws
- verbs -= /mob/living/simple_animal/drone/verb/drone_ping
+ remove_verb(src, /mob/living/simple_animal/drone/verb/check_laws)
+ remove_verb(src, /mob/living/simple_animal/drone/verb/drone_ping)
/mob/living/simple_animal/drone/cogscarab/Login()
..()
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 7abecc7c81..954e968bd7 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -196,18 +196,17 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
REMOVE_TRAIT(summoner, TRAIT_NODEATH, "memento_mori")
to_chat(summoner,"You feel incredibly vulnerable as the memento mori pulls your life force in one too many directions!")
-/mob/living/simple_animal/hostile/guardian/Stat()
- ..()
- if(statpanel("Status"))
- if(summoner)
- var/resulthealth
- if(iscarbon(summoner))
- resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100)
- else
- resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5)
- stat(null, "Summoner Health: [resulthealth]%")
- if(cooldown >= world.time)
- stat(null, "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/get_status_tab_items()
+ . += ..()
+ if(summoner)
+ var/resulthealth
+ if(iscarbon(summoner))
+ resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100)
+ else
+ resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5)
+ . += "Summoner Health: [resulthealth]%"
+ if(cooldown >= world.time)
+ . += "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/Move() //Returns to summoner if they move out of range
. = ..()
@@ -477,13 +476,13 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
to_chat(src, "[G.real_name] has been caught!")
guardians -= G
if(!guardians.len)
- verbs -= /mob/living/proc/guardian_reset
+ remove_verb(src, /mob/living/proc/guardian_reset)
else
to_chat(src, "There were no ghosts willing to take control of [G.real_name]. Looks like you're stuck with it for now.")
else
to_chat(src, "You decide not to reset [guardians.len > 1 ? "any of your guardians":"your guardian"].")
else
- verbs -= /mob/living/proc/guardian_reset
+ remove_verb(src, /mob/living/proc/guardian_reset)
////////parasite tracking/finding procs
@@ -608,9 +607,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
if("carp")
to_chat(user, "[G.carp_fluff_string]")
to_chat(user, "[G.real_name] has been caught!")
- user.verbs += /mob/living/proc/guardian_comm
- user.verbs += /mob/living/proc/guardian_recall
- user.verbs += /mob/living/proc/guardian_reset
+ add_verb(user, list(/mob/living/proc/guardian_comm, \
+ /mob/living/proc/guardian_recall, \
+ /mob/living/proc/guardian_reset))
/obj/item/guardiancreator/choose
random = FALSE
diff --git a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
index 885f907997..3bc079f6d5 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm
@@ -26,11 +26,10 @@
if(loc == summoner && toggle)
ToggleMode(0)
-/mob/living/simple_animal/hostile/guardian/assassin/Stat()
- ..()
- if(statpanel("Status"))
- if(stealthcooldown >= world.time)
- stat(null, "Stealth Cooldown Remaining: [DisplayTimeText(stealthcooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/assassin/get_status_tab_items()
+ . = ..()
+ if(stealthcooldown >= world.time)
+ . += "Stealth Cooldown Remaining: [DisplayTimeText(stealthcooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/assassin/AttackingTarget()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
index 26e5d791f3..b9037ff4a4 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
@@ -7,11 +7,10 @@
carp_fluff_string = "CARP CARP CARP! Caught one! It's an explosive carp! Boom goes the fishy."
var/bomb_cooldown = 0
-/mob/living/simple_animal/hostile/guardian/bomb/Stat()
- ..()
- if(statpanel("Status"))
- if(bomb_cooldown >= world.time)
- stat(null, "Bomb Cooldown Remaining: [DisplayTimeText(bomb_cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/bomb/get_status_tab_items()
+ . = ..()
+ if(bomb_cooldown >= world.time)
+ . += "Bomb Cooldown Remaining: [DisplayTimeText(bomb_cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/bomb/AttackingTarget()
. = ..()
@@ -29,7 +28,7 @@
/mob/living/simple_animal/hostile/guardian/bomb/AltClickOn(atom/movable/A)
if(!istype(A))
- altclick_listed_turf(A)
+ AltClickNoInteract(src, A)
return
if(loc == summoner)
to_chat(src, "You must be manifested to create bombs!")
diff --git a/code/modules/mob/living/simple_animal/guardian/types/support.dm b/code/modules/mob/living/simple_animal/guardian/types/support.dm
index 21460b5c6b..eee3eb0cdb 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/support.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/support.dm
@@ -18,11 +18,10 @@
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
-/mob/living/simple_animal/hostile/guardian/healer/Stat()
- ..()
- if(statpanel("Status"))
- if(beacon_cooldown >= world.time)
- stat(null, "Beacon Cooldown Remaining: [DisplayTimeText(beacon_cooldown - world.time)]")
+/mob/living/simple_animal/hostile/guardian/healer/get_status_tab_items()
+ . = ..()
+ if(beacon_cooldown >= world.time)
+ . += "Beacon Cooldown Remaining: [DisplayTimeText(beacon_cooldown - world.time)]"
/mob/living/simple_animal/hostile/guardian/healer/AttackingTarget()
. = ..()
@@ -106,7 +105,7 @@
/mob/living/simple_animal/hostile/guardian/healer/AltClickOn(atom/movable/A)
if(!istype(A))
- altclick_listed_turf(A)
+ AltClickNoInteract(src, A)
return
if(loc == summoner)
to_chat(src, "You must be manifested to warp a target!")
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
index 100db06174..8fb531f1fb 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
@@ -1,6 +1,5 @@
/datum/emote/sound/gorilla
mob_type_allowed_typecache = /mob/living/simple_animal/hostile/gorilla
- mob_type_blacklist_typecache = list()
/datum/emote/sound/gorilla/ooga
key = "ooga"
@@ -8,4 +7,3 @@
message = "oogas."
message_param = "oogas at %t."
sound = 'sound/creatures/gorilla.ogg'
-
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index 362432b2af..b98ee5adfb 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -128,7 +128,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
. = ..()
- verbs -= /mob/living/verb/pulled
+ remove_verb(src, /mob/living/verb/pulled)
/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
face_atom(A)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 883ad39261..debd7f2e7b 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -642,8 +642,8 @@ Difficulty: Very Hard
/mob/living/simple_animal/hostile/lightgeist/Initialize()
. = ..()
- verbs -= /mob/living/verb/pulled
- verbs -= /mob/verb/me_verb
+ remove_verb(src, /mob/living/verb/pulled)
+ remove_verb(src, /mob/verb/me_verb)
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
@@ -732,7 +732,7 @@ Difficulty: Very Hard
L.mind.transfer_to(holder_animal)
var/obj/effect/proc_holder/spell/targeted/exit_possession/P = new /obj/effect/proc_holder/spell/targeted/exit_possession
holder_animal.mind.AddSpell(P)
- holder_animal.verbs -= /mob/living/verb/pulled
+ remove_verb(holder_animal, /mob/living/verb/pulled)
/obj/structure/closet/stasis/dump_contents(override = TRUE, kill = 1)
STOP_PROCESSING(SSobj, src)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 4dfd4561d6..f0ab3fba4c 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -274,7 +274,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A)
if(!istype(A))
- altclick_listed_turf(A)
+ AltClickNoInteract(src, A)
return
if(swoop_cooldown >= world.time)
to_chat(src, "You need to wait 20 seconds between swoop attacks!")
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index bd3a6e8232..006bef974d 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -445,7 +445,7 @@ Difficulty: Normal
/mob/living/simple_animal/hostile/megafauna/hierophant/AltClickOn(atom/A) //player control handler(don't give this to a player holy fuck)
if(!istype(A) || get_dist(A, src) <= 2)
- return altclick_listed_turf(A)
+ return AltClickNoInteract(src, A)
blink(A)
//Hierophant overlays
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 795184bad5..3de4c8b41b 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -141,7 +141,7 @@ Difficulty: Medium
loot = list(/obj/item/staff/storm)
elimination = 0
else if(prob(20))
- loot = list(/obj/structure/closet/crate/necropolis/tendril)
+ loot = list(/obj/structure/closet/crate/necropolis/tendril/random) //This one spawns a chest that could be any of the three types
..()
/obj/item/gps/internal/legion
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index e3edd171c0..e347056924 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -25,6 +25,7 @@
var/list/attack_action_types = list()
var/can_talk = FALSE
var/obj/loot_drop = null
+ var/crate_type = /obj/structure/closet/crate/necropolis/tendril
var/owner
//Gives player-controlled variants the ability to swap attacks
@@ -182,7 +183,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
activator = null
-obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
+/obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
var/selectedspawn = pick(potentialspawns)
mychild = new selectedspawn(loc)
visible_message("[mychild] emerges from [src]!")
@@ -193,7 +194,7 @@ obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind)
icon_state = "tumor_popped"
INVOKE_ASYNC(src, .proc/arena_checks)
-obj/structure/elite_tumor/proc/return_elite()
+/obj/structure/elite_tumor/proc/return_elite()
mychild.forceMove(loc)
visible_message("[mychild] emerges from [src]!")
playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE)
@@ -271,11 +272,11 @@ obj/structure/elite_tumor/proc/return_elite()
visible_message("[mychild] suddenly reappears above [src]!")
playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE)
-obj/structure/elite_tumor/proc/onEliteLoss()
+/obj/structure/elite_tumor/proc/onEliteLoss()
playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, 0, 50, TRUE, TRUE)
visible_message("[src] begins to convulse violently before beginning to dissipate.")
visible_message("As [src] closes, something is forced up from down below.")
- var/obj/structure/closet/crate/necropolis/tendril/lootbox = new /obj/structure/closet/crate/necropolis/tendril(loc)
+ var/obj/structure/closet/crate/necropolis/tendril/lootbox = new mychild.crate_type(loc)
if(!boosted)
mychild = null
activator = null
@@ -290,7 +291,7 @@ obj/structure/elite_tumor/proc/onEliteLoss()
activator = null
qdel(src)
-obj/structure/elite_tumor/proc/onEliteWon()
+/obj/structure/elite_tumor/proc/onEliteWon()
activity = TUMOR_PASSIVE
activator = null
mychild.revive(full_heal = TRUE, admin_revive = TRUE)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
index d9f3cfba3d..4e8f4f6160 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
@@ -40,6 +40,7 @@
mouse_opacity = MOUSE_OPACITY_ICON
deathmessage = "explodes into gore!"
loot_drop = /obj/item/crusher_trophy/broodmother_tongue
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/weapon_armor
attack_action_types = list(/datum/action/innate/elite_attack/tentacle_patch,
/datum/action/innate/elite_attack/spawn_children,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
index fce5b636e4..032bc2d60d 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/demon_dies.ogg'
deathmessage = "begins to shudder as it becomes transparent..."
loot_drop = /obj/item/clothing/neck/cloak/herald_cloak
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/magic
can_talk = 1
attack_action_types = list(/datum/action/innate/elite_attack/herald_trishot,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
index 15ed0135fe..6c13bb903e 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/curse.ogg'
deathmessage = "'s arms reach out before it falls apart onto the floor, lifeless."
loot_drop = /obj/item/crusher_trophy/legionnaire_spine
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/misc
attack_action_types = list(/datum/action/innate/elite_attack/legionnaire_charge,
/datum/action/innate/elite_attack/head_detach,
/datum/action/innate/elite_attack/bonfire_teleport,
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
index 7995e4d20f..b3300a86bb 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
@@ -38,7 +38,7 @@
deathsound = 'sound/magic/repulse.ogg'
deathmessage = "'s lights flicker, before its top part falls down."
loot_drop = /obj/item/clothing/accessory/pandora_hope
-
+ crate_type = /obj/structure/closet/crate/necropolis/tendril/magic
attack_action_types = list(/datum/action/innate/elite_attack/singular_shot,
/datum/action/innate/elite_attack/magic_box,
/datum/action/innate/elite_attack/pandora_teleport,
@@ -190,4 +190,4 @@
/obj/item/clothing/accessory/pandora_hope/on_uniform_dropped(obj/item/clothing/under/U, user)
var/mob/living/L = user
if(L && L.mind)
- SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "hope_lavaland")
+ SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "hope_lavaland")
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 4fcb1ed5bf..4bab410d66 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -115,12 +115,12 @@
parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var
- verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
+ add_verb(src, list(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
/mob/living/simple_animal/parrot/proc/steal_from_mob, \
/mob/living/simple_animal/parrot/verb/drop_held_item_player, \
/mob/living/simple_animal/parrot/proc/perch_player, \
/mob/living/simple_animal/parrot/proc/toggle_mode,
- /mob/living/simple_animal/parrot/proc/perch_mob_player)
+ /mob/living/simple_animal/parrot/proc/perch_mob_player))
/mob/living/simple_animal/parrot/examine(mob/user)
@@ -142,11 +142,11 @@
..(gibbed)
-/mob/living/simple_animal/parrot/Stat()
- ..()
- if(statpanel("Status"))
- stat("Held Item", held_item)
- stat("Mode",a_intent)
+/mob/living/simple_animal/parrot/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Held Item: [held_item]"
+ . += "Mode: [a_intent]"
/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index be0338a60e..b192182c71 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -345,11 +345,10 @@
remove_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed, multiplicative_slowdown = speed)
-/mob/living/simple_animal/Stat()
- ..()
- if(statpanel("Status"))
- stat(null, "Health: [round((health / maxHealth) * 100)]%")
- return 1
+/mob/living/simple_animal/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Health: [round((health / maxHealth) * 100)]%"
/mob/living/simple_animal/proc/drop_loot()
if(loot.len)
diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm
index 070cd48a70..9440caf3fc 100644
--- a/code/modules/mob/living/simple_animal/slime/emote.dm
+++ b/code/modules/mob/living/simple_animal/slime/emote.dm
@@ -1,6 +1,5 @@
/datum/emote/slime
mob_type_allowed_typecache = /mob/living/simple_animal/slime
- mob_type_blacklist_typecache = list()
/datum/emote/slime/bounce
key = "bounce"
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 1bdd988694..1f45e57718 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -106,6 +106,7 @@
set_colour(new_colour)
. = ..()
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_SLIME, 7.5)
+ set_nutrition(rand(650, 800))
/mob/living/simple_animal/slime/Destroy()
for (var/A in actions)
@@ -208,21 +209,20 @@
/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0)
return 2
-/mob/living/simple_animal/slime/Stat()
- if(..())
-
- if(!docile)
- stat(null, "Nutrition: [nutrition]/[get_max_nutrition()]")
- if(amount_grown >= SLIME_EVOLUTION_THRESHOLD)
- if(is_adult)
- stat(null, "You can reproduce!")
- else
- stat(null, "You can evolve!")
+/mob/living/simple_animal/slime/get_status_tab_items()
+ . = ..()
+ if(!docile)
+ . += "Nutrition: [nutrition]/[get_max_nutrition()]"
+ if(amount_grown >= SLIME_EVOLUTION_THRESHOLD)
+ if(is_adult)
+ . += "You can reproduce!"
+ else
+ . += "You can evolve!"
if(stat == UNCONSCIOUS)
- stat(null,"You are knocked out by high levels of BZ!")
+ . += "You are knocked out by high levels of BZ!"
else
- stat(null,"Power Level: [powerlevel]")
+ . += "Power Level: [powerlevel]"
/mob/living/simple_animal/slime/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 31bfb5621f..0d4afd0915 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -50,6 +50,8 @@
var/datum/callback/CB = foo
CB.Invoke()
+ mind?.hide_ckey = client?.prefs?.hide_ckey
+
log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP)
SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 358eff7a47..e4434c0c0e 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -586,87 +586,34 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
/mob/proc/is_muzzled()
return FALSE
-/mob/Stat()
- ..()
+/// Adds this list to the output to the stat browser
+/mob/proc/get_status_tab_items()
+ . = list()
- SSvote?.render_statpanel(src)
-
- //This is only called from client/Stat(), let's assume client exists.
-
- if(statpanel("Status"))
- var/list/L = list()
- L += "Ping: [round(client.lastping,1)]ms (Avg: [round(client.avgping,1)]ms)"
- L += SSmapping.stat_map_name
- L += "Round ID: [GLOB.round_id || "NULL"]"
- L += SStime_track.stat_time_text
- L += SSshuttle.emergency_shuttle_stat_text
- stat(null, "[L.Join("\n\n")]")
-
- if(client.holder)
- if(statpanel("MC"))
- var/turf/T = get_turf(client.eye)
- stat("Location:", COORD(T))
- stat("CPU:", "[world.cpu]")
- stat("Instances:", "[num2text(world.contents.len, 10)]")
- stat("World Time:", "[world.time]")
- GLOB.stat_entry()
- config.stat_entry()
- stat(null)
- if(Master)
- Master.stat_entry()
- else
- stat("Master Controller:", "ERROR")
- if(Failsafe)
- Failsafe.stat_entry()
- else
- stat("Failsafe Controller:", "ERROR")
- if(Master)
- stat(null)
- for(var/datum/controller/subsystem/SS in Master.statworthy_subsystems)
- SS.stat_entry()
- GLOB.cameranet.stat_entry()
- if(statpanel("Tickets"))
- GLOB.ahelp_tickets.stat_entry()
- if(length(GLOB.sdql2_queries))
- if(statpanel("SDQL2"))
- stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
- for(var/i in GLOB.sdql2_queries)
- var/datum/SDQL2_query/Q = i
- Q.generate_stat()
- if(listed_turf && client)
- if(!TurfAdjacent(listed_turf))
- listed_turf = null
- else
- statpanel(listed_turf.name, null, listed_turf)
- var/list/overrides = list()
- for(var/image/I in client.images)
- if(I.loc && I.loc.loc == listed_turf && I.override)
- overrides += I.loc
- for(var/atom/A in listed_turf)
- if(!A.mouse_opacity)
- continue
- if(A.invisibility > see_invisible)
- continue
- if(overrides.len && (A in overrides))
- continue
- statpanel(listed_turf.name, null, A)
+/// Gets all relevant proc holders for the browser statpenl
+/mob/proc/get_proc_holders()
+ . = list()
if(mind)
- add_spells_to_statpanel(mind.spell_list)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- add_stings_to_statpanel(changeling.purchasedpowers)
- add_spells_to_statpanel(mob_spell_list)
+ . += get_spells_for_statpanel(mind.spell_list)
+ . += get_spells_for_statpanel(mob_spell_list)
-/mob/proc/add_spells_to_statpanel(list/spells)
+/**
+ * Convert a list of spells into a displyable list for the statpanel
+ *
+ * Shows charge and other important info
+ */
+/mob/proc/get_spells_for_statpanel(list/spells)
+ var/list/L = list()
for(var/obj/effect/proc_holder/spell/S in spells)
if((!S.mobs_blacklist || !S.mobs_blacklist[src]) && (!S.mobs_whitelist || S.mobs_whitelist[src]))
switch(S.charge_type)
if("recharge")
- statpanel("[S.panel]","[S.charge_counter/10.0]/[S.charge_max/10]",S)
+ L[++L.len] = list("[S.panel]", "[S.charge_counter/10.0]/[S.charge_max/10]", S.name, REF(S))
if("charges")
- statpanel("[S.panel]","[S.charge_counter]/[S.charge_max]",S)
+ L[++L.len] = list("[S.panel]", "[S.charge_counter]/[S.charge_max]", S.name, REF(S))
if("holdervar")
- statpanel("[S.panel]","[S.holder_var_type] [S.holder_var_amount]",S)
+ L[++L.len] = list("[S.panel]", "[S.holder_var_type] [S.holder_var_amount]", S.name, REF(S))
+ return L
/mob/proc/add_stings_to_statpanel(list/stings)
for(var/obj/effect/proc_holder/changeling/S in stings)
@@ -798,6 +745,8 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
if(istype(S, spell))
mob_spell_list -= S
qdel(S)
+ if(client)
+ client << output(null, "statbrowser:check_spells")
/mob/proc/anti_magic_check(magic = TRUE, holy = FALSE, tinfoil = FALSE, chargecost = 1, self = FALSE)
if(!magic && !holy && !tinfoil)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 52b755d926..aa6635f73d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -431,7 +431,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
/mob/living/getImplant(type)
return locate(type) in implants
-/proc/offer_control(mob/M)
+/proc/offer_control(mob/M,ignore_category=null)
to_chat(M, "Control of your mob has been offered to dead players.")
if(usr)
log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.")
@@ -445,7 +445,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
if(A)
poll_message = "[poll_message] Status:[A.name]."
- var/list/mob/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M)
+ var/list/mob/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M, ignore_category)
if(LAZYLEN(candidates))
var/mob/C = pick(candidates)
diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm
index 8ca93e8347..9dde9a0c49 100644
--- a/code/modules/modular_computers/computers/_modular_computer_shared.dm
+++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm
@@ -44,18 +44,20 @@
. += "It has a slot installed for an intelliCard."
var/obj/item/computer_hardware/card_slot/card_slot = get_modular_computer_part(MC_CARD)
+ var/obj/item/computer_hardware/card_slot/card_slot2 = get_modular_computer_part(MC_CARD2)
+ var/multiple_slots = istype(card_slot) && istype(card_slot2)
if(card_slot)
- if(card_slot.stored_card || card_slot.stored_card2)
+ if(card_slot?.stored_card || card_slot2?.stored_card)
var/obj/item/card/id/first_ID = card_slot.stored_card
- var/obj/item/card/id/second_ID = card_slot.stored_card2
+ var/obj/item/card/id/second_ID = card_slot2.stored_card
var/multiple_cards = istype(first_ID) && istype(second_ID)
if(user_is_adjacent)
- . += "It has two slots for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
+ . += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
else
- . += "It has two slots for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
+ . += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
. += "Alt-click [src] to eject the identification card[multiple_cards ? "s":""]."
else
- . += "It has two slots installed for identification cards."
+ . += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards."
var/obj/item/computer_hardware/printer/printer_slot = get_modular_computer_part(MC_PRINT)
if(printer_slot)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 67c04de13f..63cb1cc5fa 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -4,6 +4,12 @@
/obj/item/modular_computer
name = "modular microcomputer"
desc = "A small portable microcomputer."
+ icon = 'icons/obj/computer.dmi'
+ icon_state = "laptop-open"
+ var/light_on = FALSE
+ integrity_failure = 0.5
+ max_integrity = 100
+ armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
@@ -22,8 +28,6 @@
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
- icon = 'icons/obj/computer.dmi'
- icon_state = "laptop-open"
var/icon_state_unpowered = null // Icon state when the computer is turned off.
var/icon_state_powered = null // Icon state when the computer is turned on.
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
@@ -31,20 +35,16 @@
var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
- integrity_failure = 0.5
- max_integrity = 100
- armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
-
- // Important hardware (must be installed for computer to work)
-
- // Optional hardware (improves functionality, but is not critical for computer to work)
-
- var/list/all_components = list() // List of "connection ports" in this computer and the components with which they are plugged
+ /// List of "connection ports" in this computer and the components with which they are plugged
+ var/list/all_components = list()
+ /// Lazy List of extra hardware slots that can be used modularly.
+ var/list/expansion_bays
+ /// Number of total expansion bays this computer has available.
+ var/max_bays = 0
var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
- var/light_on = FALSE //If that light is enabled
var/comp_light_luminosity = 3 //The brightness of that light
var/comp_light_color //The color of that light
@@ -71,80 +71,15 @@
physical = null
return ..()
-
-/obj/item/modular_computer/proc/add_verb(var/path)
- switch(path)
- if(MC_CARD)
- verbs += /obj/item/modular_computer/proc/eject_id
- if(MC_SDD)
- verbs += /obj/item/modular_computer/proc/eject_disk
- if(MC_AI)
- verbs += /obj/item/modular_computer/proc/eject_card
-
-/obj/item/modular_computer/proc/remove_verb(path)
- switch(path)
- if(MC_CARD)
- verbs -= /obj/item/modular_computer/proc/eject_id
- if(MC_SDD)
- verbs -= /obj/item/modular_computer/proc/eject_disk
- if(MC_AI)
- verbs -= /obj/item/modular_computer/proc/eject_card
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_id()
- set name = "Eject ID"
- set category = "Object"
- set src in view(1)
-
- if(issilicon(usr))
- return
- var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(usr.canUseTopic(src, BE_CLOSE))
- card_slot.try_eject(null, usr)
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_card()
- set name = "Eject Intellicard"
- set category = "Object"
-
- if(issilicon(usr))
- return
- var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
- if(usr.canUseTopic(src, BE_CLOSE))
- ai_slot.try_eject(null, usr,1)
-
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_disk()
- set name = "Eject Data Disk"
- set category = "Object"
-
- if(issilicon(usr))
- return
-
- if(usr.canUseTopic(src, BE_CLOSE))
- var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
- if(uninstall_component(portable_drive, usr))
- portable_drive.verb_pickup()
-
/obj/item/modular_computer/AltClick(mob/user)
..()
if(issilicon(user))
return
if(user.canUseTopic(src, BE_CLOSE))
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
- var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
- if(portable_drive)
- if(uninstall_component(portable_drive, user))
- portable_drive.verb_pickup()
- else
- if(card_slot && card_slot.try_eject(null, user))
- return
- if(ai_slot)
- ai_slot.try_eject(null, user)
-
+ return (card_slot2?.try_eject(user) || card_slot?.try_eject(user)) //Try the secondary one first.
// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs.
/obj/item/modular_computer/GetAccess()
@@ -160,19 +95,25 @@
return ..()
/obj/item/modular_computer/RemoveID()
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(!card_slot)
- return
- return card_slot.RemoveID()
+ return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first.
/obj/item/modular_computer/InsertID(obj/item/inserting_item)
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(!card_slot)
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
+ if(!(card_slot || card_slot2))
+ //to_chat(user, "There isn't anywhere you can fit a card into on this computer.")
return FALSE
+
var/obj/item/card/inserting_id = inserting_item.RemoveID()
if(!inserting_id)
return FALSE
- return card_slot.try_insert(inserting_id)
+
+ if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id)))
+ return TRUE
+ //to_chat(user, "This computer doesn't have an open card slot.")
+ return FALSE
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
var/mob/M = usr
diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm
index 3c94a66384..8668b279cf 100644
--- a/code/modules/modular_computers/computers/item/computer_components.dm
+++ b/code/modules/modular_computers/computers/item/computer_components.dm
@@ -6,6 +6,14 @@
to_chat(user, "This component is too large for \the [src]!")
return FALSE
+ if(H.expansion_hw)
+ if(LAZYLEN(expansion_bays) >= max_bays)
+ to_chat(user, "All of the computer's expansion bays are filled.")
+ return FALSE
+ if(LAZYACCESS(expansion_bays, H.device_type))
+ to_chat(user, "The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\".")
+ return FALSE
+
if(all_components[H.device_type])
to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].")
return FALSE
@@ -20,6 +28,8 @@
if(user && !user.transferItemToLoc(H, src))
return FALSE
+ if(H.expansion_hw)
+ LAZYSET(expansion_bays, H.device_type, H)
all_components[H.device_type] = H
to_chat(user, "You install \the [H] into \the [src].")
@@ -32,7 +42,9 @@
/obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/H, mob/living/user = null)
if(H.holder != src) // Not our component at all.
return FALSE
+ if(H.expansion_hw)
+ LAZYREMOVE(expansion_bays, H.device_type)
all_components.Remove(H.device_type)
to_chat(user, "You remove \the [H] from \the [src].")
@@ -43,6 +55,7 @@
if(enabled && !use_power())
shutdown_computer()
update_icon()
+ return TRUE
// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index fd017e2b0f..4a985b93c1 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -44,6 +44,33 @@
/obj/item/modular_computer/ui_data(mob/user)
var/list/data = get_header_data()
data["device_theme"] = device_theme
+
+ data["login"] = list()
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD]
+ if(cardholder)
+ var/obj/item/card/id/stored_card = cardholder.GetID()
+ if(stored_card)
+ var/stored_name = stored_card.registered_name
+ var/stored_title = stored_card.assignment
+ if(!stored_name)
+ stored_name = "Unknown"
+ if(!stored_title)
+ stored_title = "Unknown"
+ data["login"] = list(
+ IDName = stored_name,
+ IDJob = stored_title,
+ )
+
+ data["removable_media"] = list()
+ if(all_components[MC_SDD])
+ data["removable_media"] += "removable storage disk"
+ var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI]
+ if(intelliholder?.stored_card)
+ data["removable_media"] += "intelliCard"
+ var/obj/item/computer_hardware/card_slot/secondarycardholder = all_components[MC_CARD2]
+ if(secondarycardholder?.stored_card)
+ data["removable_media"] += "secondary RFID card"
+
data["programs"] = list()
var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
for(var/datum/computer_file/program/P in hard_drive.stored_files)
@@ -157,6 +184,36 @@
light_color = new_color
update_light()
return TRUE
+
+ if("PC_Eject_Disk")
+ var/param = params["name"]
+ var/mob/user = usr
+ switch(param)
+ if("removable storage disk")
+ var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
+ if(!portable_drive)
+ return
+ if(uninstall_component(portable_drive, usr))
+ user.put_in_hands(portable_drive)
+ playsound(src, 'sound/machines/card_slide.ogg', 50)
+ if("intelliCard")
+ var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI]
+ if(!intelliholder)
+ return
+ if(intelliholder.try_eject(user))
+ playsound(src, 'sound/machines/card_slide.ogg', 50)
+ if("ID")
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD]
+ if(!cardholder)
+ return
+ cardholder.try_eject(user)
+ if("secondary RFID card")
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD2]
+ if(!cardholder)
+ return
+ cardholder.try_eject(user)
+
+
else
return
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index 5927d57a0b..ef83140a8f 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -12,6 +12,7 @@
hardware_flag = PROGRAM_LAPTOP
max_hardware_size = 2
w_class = WEIGHT_CLASS_NORMAL
+ max_bays = 4
// No running around with open laptops in hands.
item_flags = SLOWS_WHILE_IN_HAND
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index c79d7a9361..0d7b567877 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -8,6 +8,7 @@
icon_state_unpowered = null
icon_state_menu = null
hardware_flag = 0
+ max_bays = 4
var/obj/machinery/modular_computer/machinery_computer = null
@@ -18,7 +19,7 @@
machinery_computer = null
. = ..()
-/obj/item/modular_computer/processor/New(comp) //intentional new probably
+/obj/item/modular_computer/processor/New(comp)
..()
STOP_PROCESSING(SSobj, src) // Processed by its machine
@@ -56,23 +57,5 @@
machinery_computer.update_icon()
return
-/obj/item/modular_computer/processor/add_verb(path)
- switch(path)
- if(MC_CARD)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_id
- if(MC_SDD)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_disk
- if(MC_AI)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_card
-
-/obj/item/modular_computer/processor/remove_verb(path)
- switch(path)
- if(MC_CARD)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_id
- if(MC_SDD)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk
- if(MC_AI)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card
-
/obj/item/modular_computer/processor/attack_ghost(mob/user)
ui_interact(user)
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index 41a256467f..67e8118c7a 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -5,10 +5,11 @@
icon_state_unpowered = "tablet"
icon_state_powered = "tablet"
icon_state_menu = "menu"
- //worn_icon_state = "tablet"
+ // worn_icon_state = "tablet"
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
w_class = WEIGHT_CLASS_SMALL
+ max_bays = 3
steel_sheet_cost = 1
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
has_light = TRUE //LED flashlight!
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index 7cca8ea5b4..8ac669d2bf 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -26,11 +26,21 @@
install_component(new /obj/item/computer_hardware/processor_unit/small)
install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
install_component(hard_drive)
+ install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/printer/mini)
hard_drive.store_file(new /datum/computer_file/program/bounty)
//hard_drive.store_file(new /datum/computer_file/program/shipping)
+/obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done
+ . = ..()
+ install_component(new /obj/item/computer_hardware/sensorpackage)
+
+/obj/item/modular_computer/tablet/preset/advanced/command/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/sensorpackage)
+ install_component(new /obj/item/computer_hardware/card_slot/secondary)
+
/// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink.
/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize()
. = ..()
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 9d29b23e76..12b2f6d25a 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -1,6 +1,6 @@
/obj/machinery/modular_computer/console/preset
// Can be changed to give devices specific hardware
- var/_has_id_slot = FALSE
+ var/_has_second_id_slot = FALSE
var/_has_printer = FALSE
var/_has_battery = FALSE
var/_has_ai = FALSE
@@ -11,8 +11,9 @@
return
cpu.install_component(new /obj/item/computer_hardware/processor_unit)
- if(_has_id_slot)
- cpu.install_component(new /obj/item/computer_hardware/card_slot)
+ cpu.install_component(new /obj/item/computer_hardware/card_slot)
+ if(_has_second_id_slot)
+ cpu.install_component(new /obj/item/computer_hardware/card_slot/secondary)
if(_has_printer)
cpu.install_component(new /obj/item/computer_hardware/printer)
if(_has_battery)
@@ -59,7 +60,7 @@
console_department = "Command"
name = "command console"
desc = "A stationary computer. This one comes preloaded with command programs."
- _has_id_slot = TRUE
+ _has_second_id_slot = TRUE
_has_printer = TRUE
/obj/machinery/modular_computer/console/preset/command/install_programs()
@@ -73,7 +74,7 @@
console_department = "Identification"
name = "identification console"
desc = "A stationary computer. This one comes preloaded with identification modification programs."
- _has_id_slot = TRUE
+ _has_second_id_slot = TRUE
_has_printer = TRUE
/obj/machinery/modular_computer/console/preset/id/install_programs()
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index 6f016ad147..0e6f4d161a 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -75,30 +75,6 @@
add_overlay("bsod")
add_overlay("broken")
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/machinery/modular_computer/proc/eject_id()
- set name = "Eject ID"
- set category = "Object"
-
- if(cpu)
- cpu.eject_id()
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/machinery/modular_computer/proc/eject_disk()
- set name = "Eject Data Disk"
- set category = "Object"
-
- if(cpu)
- cpu.eject_disk()
-
-/obj/machinery/modular_computer/proc/eject_card()
- set name = "Eject Intellicard"
- set category = "Object"
- set src in view(1)
-
- if(cpu)
- cpu.eject_card()
-
/obj/machinery/modular_computer/AltClick(mob/user)
if(cpu)
cpu.AltClick(user)
@@ -136,7 +112,7 @@
return
. = ..()
-/obj/machinery/modular_computer/attackby(var/obj/item/W as obj, mob/user)
+/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user)
if(cpu && !(flags_1 & NODECONSTRUCT_1))
return cpu.attackby(W, user)
return ..()
@@ -169,5 +145,4 @@
// "Brute" damage mostly damages the casing.
/obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj)
if(cpu)
- return cpu.bullet_act(Proj)
- return ..()
+ cpu.bullet_act(Proj)
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index 12e5ef6e95..6d6a48d567 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -25,8 +25,6 @@
var/ntnet_status = 1
/// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
var/usage_flags = PROGRAM_ALL
- /// Optional string that describes what NTNet server/system this program connects to. Used in default logging.
- var/network_destination = null
/// Whether the program can be downloaded from NTNet. Set to 0 to disable.
var/available_on_ntnet = 1
/// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
@@ -82,10 +80,18 @@
/datum/computer_file/program/proc/process_tick()
return 1
-// Check if the user can run program. Only humans can operate computer. Automatically called in run_program()
-// User has to wear their ID for ID Scan to work.
-// Can also be called manually, with optional parameter being access_to_check to scan the user's ID
-/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE)
+/**
+ *Check if the user can run program. Only humans can operate computer. Automatically called in run_program()
+ *ID must be inserted into a card slot to be read. If the program is not currently installed (as is the case when
+ *NT Software Hub is checking available software), a list can be given to be used instead.
+ *Arguments:
+ *user is a ref of the mob using the device.
+ *loud is a bool deciding if this proc should use to_chats
+ *access_to_check is an access level that will be checked against the ID
+ *transfer, if TRUE and access_to_check is null, will tell this proc to use the program's transfer_access in place of access_to_check
+ *access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID.
+*/
+/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, var/list/access)
// Defaults to required_access
if(!access_to_check)
if(transfer && transfer_access)
@@ -104,29 +110,24 @@
if(issilicon(user))
return TRUE
- if(ishuman(user))
+ if(!length(access))
var/obj/item/card/id/D
var/obj/item/computer_hardware/card_slot/card_slot
- if(computer && card_slot)
+ if(computer)
card_slot = computer.all_components[MC_CARD]
- D = card_slot.GetID()
- var/mob/living/carbon/human/h = user
- var/obj/item/card/id/I = h.get_idcard(TRUE)
+ D = card_slot?.GetID()
- if(!I && !D)
+ if(!D)
if(loud)
to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.")
return FALSE
+ access = D.GetAccess()
- if(I)
- if(access_to_check in I.GetAccess())
- return TRUE
- else if(D)
- if(access_to_check in D.GetAccess())
- return TRUE
- if(loud)
- to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.")
- return 0
+ if(access_to_check in access)
+ return TRUE
+ if(loud)
+ to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.")
+ return FALSE
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
@@ -139,8 +140,12 @@
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(mob/living/user)
if(can_run(user, 1))
- if(requires_ntnet && network_destination)
- generate_network_log("Connection opened to [network_destination].")
+ if(requires_ntnet)
+ var/obj/item/card/id/ID
+ var/obj/item/computer_hardware/card_slot/card_holder = computer.all_components[MC_CARD]
+ if(card_holder)
+ ID = card_holder.GetID()
+ generate_network_log("Connection opened -- Program ID: [filename] User:[ID?"[ID.registered_name]":"None"]")
program_state = PROGRAM_STATE_ACTIVE
return 1
return 0
@@ -162,8 +167,12 @@
// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client.
/datum/computer_file/program/proc/kill_program(forced = FALSE)
program_state = PROGRAM_STATE_KILLED
- if(network_destination)
- generate_network_log("Connection to [network_destination] closed.")
+ if(requires_ntnet)
+ var/obj/item/card/id/ID
+ var/obj/item/computer_hardware/card_slot/card_holder = computer.all_components[MC_CARD]
+ if(card_holder)
+ ID = card_holder.GetID()
+ generate_network_log("Connection closed -- Program ID: [filename] User:[ID?"[ID.registered_name]":"None"]")
return 1
/datum/computer_file/program/ui_interact(mob/user, datum/tgui/ui)
diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm
index 3c1daa5af3..1cb74a227b 100644
--- a/code/modules/modular_computers/file_system/program_events.dm
+++ b/code/modules/modular_computers/file_system/program_events.dm
@@ -2,7 +2,7 @@
// Always include a parent call when overriding an event.
// Called when the ID card is removed from computer. ID is removed AFTER this proc.
-/datum/computer_file/program/proc/event_idremoved(background, slot)
+/datum/computer_file/program/proc/event_idremoved(background)
return
// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss.
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 364ad79737..7ae6dd203a 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -1,8 +1,8 @@
/datum/computer_file/program/aidiag
filename = "aidiag"
- filedesc = "AI Integrity Restorer"
+ filedesc = "NT FRK"
program_icon_state = "generic"
- extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
+ extended_desc = "Firmware Restoration Kit, capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
size = 12
requires_ntnet = FALSE
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
@@ -48,7 +48,7 @@
if(computer.all_components[MC_AI])
var/obj/item/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI]
if(ai_slot && ai_slot.stored_card)
- ai_slot.try_eject(0,usr)
+ ai_slot.try_eject(usr)
return TRUE
/datum/computer_file/program/aidiag/process_tick()
@@ -71,14 +71,19 @@
ai_slot.locked = FALSE
restoring = FALSE
return
- ai_slot.locked =TRUE
+ ai_slot.locked = TRUE
A.adjustOxyLoss(-5, 0)//, FALSE)
A.adjustFireLoss(-5, 0)//, FALSE)
A.adjustToxLoss(-5, 0)
A.adjustBruteLoss(-5, 0)
+
+ // Please don't forget to update health, otherwise the below if statements will probably always fail.
A.updatehealth()
+
if(A.health >= 0 && A.stat == DEAD)
A.revive(full_heal = FALSE, admin_revive = FALSE)
+ cardhold.update_icon()
+
// Finished restoring
if(A.health >= 100)
ai_slot.locked = FALSE
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
index 577fad83d0..55dea600e3 100644
--- a/code/modules/modular_computers/file_system/programs/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -1,11 +1,10 @@
/datum/computer_file/program/alarm_monitor
filename = "alarmmonitor"
- filedesc = "Alarm Monitor"
+ filedesc = "Canary"
ui_header = "alarm_green.gif"
program_icon_state = "alert-green"
- extended_desc = "This program provides visual interface for station's alarm system."
+ extended_desc = "This program provides visual interface for a station's alarm system."
requires_ntnet = 1
- network_destination = "alarm monitoring network"
size = 5
tgui_id = "NtosStationAlertConsole"
var/has_alert = 0
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
index 3accb8e02d..aa361d4544 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
@@ -14,7 +14,7 @@
var/assigned = FALSE
var/first_load = TRUE
-/datum/computer_file/program/contract_uplink/run_program(var/mob/living/user)
+/datum/computer_file/program/contract_uplink/run_program(mob/living/user)
. = ..(user)
/datum/computer_file/program/contract_uplink/ui_act(action, params)
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
index 2ba3d69fe6..4f1c488b9e 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -10,7 +10,7 @@
tgui_id = "NtosRevelation"
var/armed = 0
-/datum/computer_file/program/revelation/run_program(var/mob/living/user)
+/datum/computer_file/program/revelation/run_program(mob/living/user)
. = ..(user)
if(armed)
activate()
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
index 2503073f9a..87a3f1ec94 100644
--- a/code/modules/modular_computers/file_system/programs/arcade.dm
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -1,10 +1,9 @@
/datum/computer_file/program/arcade
- filename = "arcade"
- filedesc = "Nanotrasen Micro Arcade"
+ filename = "dsarcade"
+ filedesc = "Donksoft Micro Arcade"
program_icon_state = "arcade"
extended_desc = "This port of the classic game 'Outbomb Cuban Pete', redesigned to run on tablets, with thrilling graphics and chilling storytelling."
requires_ntnet = FALSE
- network_destination = "arcade network"
size = 6
tgui_id = "NtosArcade"
@@ -25,7 +24,7 @@
/datum/computer_file/program/arcade/proc/game_check(mob/user)
sleep(5)
- //user?.mind?.adjust_experience(/datum/skill/gaming, 1) No gaming(TM) Yet
+ // user?.mind?.adjust_experience(/datum/skill/gaming, 1)
if(boss_hp <= 0)
heads_up = "You have crushed [boss_name]! Rejoice!"
playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10)
@@ -34,7 +33,7 @@
if(istype(computer))
computer.update_icon()
ticket_count += 1
- //user?.mind?.adjust_experience(/datum/skill/gaming, 50)
+ // user?.mind?.adjust_experience(/datum/skill/gaming, 50)
sleep(10)
else if(player_hp <= 0 || player_mp <= 0)
heads_up = "You have been defeated... how will the station survive?"
@@ -43,7 +42,7 @@
program_icon_state = "arcade_off"
if(istype(computer))
computer.update_icon()
- //user?.mind?.adjust_experience(/datum/skill/gaming, 10)
+ // user?.mind?.adjust_experience(/datum/skill/gaming, 10)
sleep(10)
/datum/computer_file/program/arcade/proc/enemy_check(mob/user)
@@ -98,8 +97,8 @@
if(computer)
printer = computer.all_components[MC_PRINT]
- //var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming)
- //var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)
+ // var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming)
+ // var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)
switch(action)
if("Attack")
var/attackamt = 0 //Spam prevention.
@@ -119,8 +118,8 @@
if(pause_state == FALSE)
healamt = rand(6,8)// + rand(0, gamerSkill)
var/maxPointCost = 3
- //if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN)
- // maxPointCost = 2
+ // if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN)
+ // maxPointCost = 2
healcost = rand(1, maxPointCost)
pause_state = TRUE
heads_up = "You heal for [healamt] damage."
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index 2df751bebd..c4b9951838 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -1,17 +1,25 @@
/datum/computer_file/program/atmosscan
filename = "atmosscan"
- filedesc = "Atmospheric Scanner"
+ filedesc = "AtmoZphere"
program_icon_state = "air"
extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
- network_destination = "atmos scan"
size = 4
tgui_id = "NtosAtmos"
+/datum/computer_file/program/atmosscan/run_program(mob/living/user)
+ . = ..()
+ if (!.)
+ return
+ if(!computer?.get_modular_computer_part(MC_SENSORS)) //Giving a clue to users why the program is spitting out zeros.
+ to_chat(user, "\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\".")
+
+
/datum/computer_file/program/atmosscan/ui_data(mob/user)
var/list/data = get_header_data()
var/list/airlist = list()
var/turf/T = get_turf(ui_host())
- if(T)
+ var/obj/item/computer_hardware/sensorpackage/sensors = computer?.get_modular_computer_part(MC_SENSORS)
+ if(T && sensors?.check_functionality())
var/datum/gas_mixture/environment = T.return_air()
var/list/env_gases = environment.get_gases()
var/pressure = environment.return_pressure()
@@ -24,6 +32,10 @@
if(gas_level > 0)
airlist += list(list("name" = "[GLOB.meta_gas_names[id]]", "percentage" = round(gas_level*100, 0.01)))
data["AirData"] = airlist
+ else
+ data["AirPressure"] = 0
+ data["AirTemp"] = 0
+ data["AirData"] = list(list())
return data
/datum/computer_file/program/atmosscan/ui_act(action, list/params)
diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
index c2160a0e92..13caab27ef 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -1,12 +1,11 @@
/datum/computer_file/program/borg_monitor
- filename = "cyborgmonitor"
- filedesc = "Cyborg Remote Monitoring"
+ filename = "siliconnect"
+ filedesc = "SiliConnect"
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of station cyborgs."
requires_ntnet = TRUE
transfer_access = ACCESS_ROBOTICS
- network_destination = "cyborg remote monitoring"
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
@@ -32,7 +31,7 @@
var/list/cyborg_data = list(
name = R.name,
- locked_down = R.lockcharge,
+ locked_down = R.locked_down,
status = R.stat,
shell_discon = shell,
charge = R.cell ? round(R.cell.percent()) : null,
@@ -81,8 +80,8 @@
return ID.registered_name
/datum/computer_file/program/borg_monitor/syndicate
- filename = "scyborgmonitor"
- filedesc = "Mission-Specific Cyborg Remote Monitoring"
+ filename = "roboverlord"
+ filedesc = "Roboverlord"
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
@@ -90,7 +89,6 @@
available_on_ntnet = FALSE
available_on_syndinet = TRUE
transfer_access = null
- network_destination = "cyborg remote monitoring"
tgui_id = "NtosCyborgRemoteMonitorSyndicate"
/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm
index 46fde84f65..2e7d3cc87f 100644
--- a/code/modules/modular_computers/file_system/programs/bounty_board.dm
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -4,7 +4,6 @@
program_icon_state = "bountyboard"
extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
requires_ntnet = TRUE
- network_destination = "bounty board interface"
size = 10
tgui_id = "NtosRequestKiosk"
///Reference to the currently logged in user.
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index 842d6e2588..6d45914add 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -7,8 +7,8 @@
#define CARDCON_DEPARTMENT_COMMAND "Command"
/datum/computer_file/program/card_mod
- filename = "cardmod"
- filedesc = "ID Card Modification"
+ filename = "plexagonidwriter"
+ filedesc = "Plexagon Access Management"
program_icon_state = "id"
extended_desc = "Program for programming employee ID cards to access parts of the station."
transfer_access = ACCESS_HEADS
@@ -98,17 +98,19 @@
return TRUE
var/obj/item/computer_hardware/card_slot/card_slot
+ var/obj/item/computer_hardware/card_slot/card_slot2
var/obj/item/computer_hardware/printer/printer
if(computer)
card_slot = computer.all_components[MC_CARD]
+ card_slot2 = computer.all_components[MC_CARD2]
printer = computer.all_components[MC_PRINT]
- if(!card_slot)
+ if(!card_slot || !card_slot2)
return
var/mob/user = usr
- var/obj/item/card/id/user_id_card = user.get_idcard(FALSE)
+ var/obj/item/card/id/user_id_card = card_slot.stored_card
- var/obj/item/card/id/id_card = card_slot.stored_card
+ var/obj/item/card/id/target_id_card = card_slot2.stored_card
switch(action)
if("PRG_authenticate")
@@ -129,14 +131,14 @@
return
var/contents = {"
Access Report
Prepared By: [user_id_card && user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
- For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
+ For: [target_id_card.registered_name ? target_id_card.registered_name : "Unregistered"]
- Assignment: [id_card.assignment]
+ Assignment: [target_id_card.assignment] Access:
"}
var/known_access_rights = get_all_accesses()
- for(var/A in id_card.access)
+ for(var/A in target_id_card.access)
if(A in known_access_rights)
contents += " [get_access_desc(A)]"
@@ -148,43 +150,40 @@
computer.visible_message("\The [computer] prints out a paper.")
return TRUE
if("PRG_eject")
- if(!computer || !card_slot)
+ if(!computer || !card_slot2)
return
- if(id_card)
- GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment)
- card_slot.try_eject(TRUE, user)
+ if(target_id_card)
+ GLOB.data_core.manifest_modify(target_id_card.registered_name, target_id_card.assignment)
+ return card_slot2.try_eject(user)
else
var/obj/item/I = user.get_active_held_item()
if(istype(I, /obj/item/card/id))
- if(!user.transferItemToLoc(I, computer))
- return
- card_slot.stored_card = I
- playsound(computer, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
- return TRUE
+ return card_slot2.try_insert(I)
+ return FALSE
if("PRG_terminate")
if(!computer || !authenticated)
return
if(minor)
- if(!(id_card.assignment in head_subordinates) && id_card.assignment != "Assistant")
+ if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != "Assistant")
return
- id_card.access -= get_all_centcom_access() + get_all_accesses()
- id_card.assignment = "Unassigned"
- id_card.update_label()
+ target_id_card.access -= get_all_centcom_access() + get_all_accesses()
+ target_id_card.assignment = "Unassigned"
+ target_id_card.update_label()
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
if("PRG_edit")
- if(!computer || !authenticated || !id_card)
+ if(!computer || !authenticated || !target_id_card)
return
var/new_name = params["name"]
if(!new_name)
return
- id_card.registered_name = new_name
- id_card.update_label()
+ target_id_card.registered_name = new_name
+ target_id_card.update_label()
playsound(computer, "terminal_type", 50, FALSE)
return TRUE
if("PRG_assign")
- if(!computer || !authenticated || !id_card)
+ if(!computer || !authenticated || !target_id_card)
return
var/target = params["assign_target"]
if(!target)
@@ -193,8 +192,8 @@
if(target == "Custom")
var/custom_name = params["custom_name"]
if(custom_name)
- id_card.assignment = custom_name
- id_card.update_label()
+ target_id_card.assignment = custom_name
+ target_id_card.update_label()
else
if(minor && !(target in head_subordinates))
return
@@ -212,10 +211,10 @@
to_chat(user, "No class exists for this job: [target]")
return
new_access = job.get_access()
- id_card.access -= get_all_centcom_access() + get_all_accesses()
- id_card.access |= new_access
- id_card.assignment = target
- id_card.update_label()
+ target_id_card.access -= get_all_centcom_access() + get_all_accesses()
+ target_id_card.access |= new_access
+ target_id_card.assignment = target
+ target_id_card.update_label()
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_access")
@@ -223,22 +222,22 @@
return
var/access_type = text2num(params["access_target"])
if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses()))
- if(access_type in id_card.access)
- id_card.access -= access_type
+ if(access_type in target_id_card.access)
+ target_id_card.access -= access_type
else
- id_card.access |= access_type
+ target_id_card.access |= access_type
playsound(computer, "terminal_type", 50, FALSE)
return TRUE
if("PRG_grantall")
if(!computer || !authenticated || minor)
return
- id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses())
+ target_id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses())
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_denyall")
if(!computer || !authenticated || minor)
return
- id_card.access.Cut()
+ target_id_card.access.Cut()
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
if("PRG_grantregion")
@@ -247,7 +246,7 @@
var/region = text2num(params["region"])
if(isnull(region))
return
- id_card.access |= get_region_accesses(region)
+ target_id_card.access |= get_region_accesses(region)
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_denyregion")
@@ -256,7 +255,7 @@
var/region = text2num(params["region"])
if(isnull(region))
return
- id_card.access -= get_region_accesses(region)
+ target_id_card.access -= get_region_accesses(region)
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
@@ -321,17 +320,17 @@
/datum/computer_file/program/card_mod/ui_data(mob/user)
var/list/data = get_header_data()
- var/obj/item/computer_hardware/card_slot/card_slot
+ var/obj/item/computer_hardware/card_slot/card_slot2
var/obj/item/computer_hardware/printer/printer
if(computer)
- card_slot = computer.all_components[MC_CARD]
+ card_slot2 = computer.all_components[MC_CARD2]
printer = computer.all_components[MC_PRINT]
data["station_name"] = station_name()
if(computer)
- data["have_id_slot"] = !!card_slot
+ data["have_id_slot"] = !!(card_slot2)
data["have_printer"] = !!printer
else
data["have_id_slot"] = FALSE
@@ -340,7 +339,7 @@
data["authenticated"] = authenticated
if(computer)
- var/obj/item/card/id/id_card = card_slot.stored_card
+ var/obj/item/card/id/id_card = card_slot2.stored_card
data["has_id"] = !!id_card
data["id_name"] = id_card ? id_card.name : "-----"
if(id_card)
diff --git a/code/modules/modular_computers/file_system/programs/cargobounty.dm b/code/modules/modular_computers/file_system/programs/cargobounty.dm
index d9bc65c98d..74ac44ade3 100644
--- a/code/modules/modular_computers/file_system/programs/cargobounty.dm
+++ b/code/modules/modular_computers/file_system/programs/cargobounty.dm
@@ -5,7 +5,6 @@
extended_desc = "A basic interface for supply personnel to check and claim bounties."
requires_ntnet = TRUE
transfer_access = ACCESS_CARGO
- network_destination = "cargo claims interface"
size = 10
tgui_id = "NtosBountyConsole"
///cooldown var for printing paper sheets.
diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm
index 3ba08a3719..db8d6d9f82 100644
--- a/code/modules/modular_computers/file_system/programs/cargoship.dm
+++ b/code/modules/modular_computers/file_system/programs/cargoship.dm
@@ -1,9 +1,8 @@
/datum/computer_file/program/shipping
filename = "shipping"
- filedesc = "Nanotrasen Shipping Scanner"
+ filedesc = "GrandArk Exporter"
program_icon_state = "shipping"
extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping."
- network_destination = "ship scanner"
size = 6
tgui_id = "NtosShipping"
///Account used for creating barcodes.
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index a1503ce3a8..4f2688d8f1 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -1,10 +1,10 @@
/datum/computer_file/program/crew_manifest
- filename = "crewmani"
- filedesc = "Crew Manifest"
+ filename = "plexagoncrew"
+ filedesc = "Plexagon Crew List"
program_icon_state = "id"
extended_desc = "Program for viewing and printing the current crew manifest"
transfer_access = ACCESS_HEADS
- requires_ntnet = FALSE
+ requires_ntnet = TRUE
size = 4
tgui_id = "NtosCrewManifest"
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index bccc6e4dbe..b88b793b66 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -1,10 +1,10 @@
/datum/computer_file/program/job_management
- filename = "job_manage"
- filedesc = "Job Manager"
+ filename = "plexagoncore"
+ filedesc = "Plexagon HR Core"
program_icon_state = "id"
extended_desc = "Program for viewing and changing job slot avalibility."
transfer_access = ACCESS_HEADS
- requires_ntnet = 0
+ requires_ntnet = TRUE
size = 4
tgui_id = "NtosJobManager"
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index 6401d6207f..8fbcfd0b01 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -1,14 +1,14 @@
/datum/computer_file/program/ntnetdownload
- filename = "ntndownloader"
- filedesc = "Software Download Tool"
+ filename = "ntsoftwarehub"
+ filedesc = "NT Software Hub"
program_icon_state = "generic"
extended_desc = "This program allows downloads of software from official NT repositories"
- unsendable = 1
- undeletable = 1
+ unsendable = TRUE
+ undeletable = TRUE
size = 4
- requires_ntnet = 1
+ requires_ntnet = TRUE
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
- available_on_ntnet = 0
+ available_on_ntnet = FALSE
ui_header = "downloader_finished.gif"
tgui_id = "NtosNetDownloader"
@@ -125,6 +125,8 @@
if(!istype(my_computer))
return
+ var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
+ var/list/access = card_slot?.GetAccess()
var/list/data = get_header_data()
@@ -146,7 +148,7 @@
for(var/A in main_repo)
var/datum/computer_file/program/P = A
// Only those programs our user can run will show in the list
- if(!P.can_run(user,transfer = 1) || hard_drive.find_file_by_name(P.filename))
+ if(!P.can_run(user,transfer = 1, access = access) || hard_drive.find_file_by_name(P.filename))
continue
all_entries.Add(list(list(
"filename" = P.filename,
diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
index 7d6d89f32c..bbbde14780 100644
--- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
@@ -1,6 +1,6 @@
/datum/computer_file/program/ntnetmonitor
- filename = "ntmonitor"
- filedesc = "NTNet Diagnostics and Monitoring"
+ filename = "wirecarp"
+ filedesc = "WireCarp" //wireshark.
program_icon_state = "comm_monitor"
extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes"
size = 12
diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
index df9b02d8ec..f03ff3f8fd 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -6,7 +6,6 @@
size = 8
requires_ntnet = 1
requires_ntnet_feature = NTNET_COMMUNICATION
- network_destination = "NTNRC server"
ui_header = "ntnrc_idle.gif"
available_on_ntnet = 1
tgui_id = "NtosNetChat"
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index bd11474858..e87a731a40 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -1,15 +1,14 @@
//normal computer version is located in code\modules\power\monitor.dm, /obj/machinery/computer/monitor
/datum/computer_file/program/power_monitor
- filename = "powermonitor"
- filedesc = "Power Monitor"
+ filename = "ampcheck"
+ filedesc = "AmpCheck"
program_icon_state = "power_monitor"
extended_desc = "This program connects to sensors around the station to provide information about electrical systems"
ui_header = "power_norm.gif"
transfer_access = ACCESS_ENGINE
usage_flags = PROGRAM_CONSOLE
requires_ntnet = 0
- network_destination = "power monitoring system"
size = 9
tgui_id = "NtosPowerMonitor"
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index 9b0e09ef99..216365d6ea 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -7,7 +7,6 @@
transfer_access = null
available_on_ntnet = FALSE
usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
- network_destination = "tracking program"
size = 5
tgui_id = "NtosRadar"
///List of trackable entities. Updated by the scan() proc.
@@ -207,7 +206,7 @@
///A program that tracks crew members via suit sensors
/datum/computer_file/program/radar/lifeline
- filename = "Lifeline"
+ filename = "lifeline"
filedesc = "Lifeline"
extended_desc = "This program allows for tracking of crew members via their suit sensors."
requires_ntnet = TRUE
@@ -252,9 +251,9 @@
//Nuke Disk Finder App//
////////////////////////
-///A program that tracks crew members via suit sensors
+///A program that tracks nukes and nuclear accessories
/datum/computer_file/program/radar/fission360
- filename = "Fission360"
+ filename = "fission360"
filedesc = "Fission360"
program_icon_state = "radarsyndicate"
extended_desc = "This program allows for tracking of nuclear authorization disks and warheads."
@@ -276,8 +275,6 @@
objects = list()
for(var/i in GLOB.nuke_list)
var/obj/machinery/nuclearbomb/nuke = i
- if(!trackable(nuke))
- continue
var/list/nukeinfo = list(
ref = REF(nuke),
@@ -285,9 +282,8 @@
)
objects += list(nukeinfo)
var/obj/item/disk/nuclear/disk = locate() in GLOB.poi_list
- if(trackable(disk))
- var/list/nukeinfo = list(
- ref = REF(disk),
- name = disk.name,
- )
- objects += list(nukeinfo)
+ var/list/nukeinfo = list(
+ ref = REF(disk),
+ name = "Nuke Auth. Disk",
+ )
+ objects += list(nukeinfo)
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 8644ce09b4..c0b82b9c95 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -1,12 +1,11 @@
/datum/computer_file/program/robocontrol
- filename = "robocontrol"
- filedesc = "Bot Remote Controller"
+ filename = "botkeeper"
+ filedesc = "Botkeeper"
program_icon_state = "robot"
extended_desc = "A remote controller used for giving basic commands to non-sentient robots."
transfer_access = ACCESS_ROBOTICS
requires_ntnet = TRUE
- network_destination = "robotics control network"
size = 12
tgui_id = "NtosRoboControl"
///Number of simple robots on-station.
@@ -78,7 +77,7 @@
return
if(id_card)
GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment)
- card_slot.try_eject(TRUE, current_user)
+ card_slot.try_eject(current_user)
else
playsound(get_turf(ui_host()) , 'sound/machines/buzz-sigh.ogg', 25, FALSE)
return
diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
index 32ad102871..e4cf590930 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -1,12 +1,11 @@
/datum/computer_file/program/supermatter_monitor
- filename = "smmonitor"
- filedesc = "Supermatter Monitoring"
+ filename = "ntcims"
+ filedesc = "NT CIMS"
ui_header = "smmon_0.gif"
program_icon_state = "smmon_0"
- extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
+ extended_desc = "Crystal Integrity Monitoring System, connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
requires_ntnet = TRUE
transfer_access = ACCESS_CONSTRUCTION
- network_destination = "supermatter monitoring system"
size = 5
tgui_id = "NtosSupermatterMonitor"
var/last_status = SUPERMATTER_INACTIVE
diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm
index b33442f99b..81555340b2 100644
--- a/code/modules/modular_computers/hardware/_hardware.dm
+++ b/code/modules/modular_computers/hardware/_hardware.dm
@@ -10,9 +10,11 @@
// Computer that holds this hardware, if any.
var/power_usage = 0 // If the hardware uses extra power, change this.
- var/enabled = 1 // If the hardware is turned off set this to 0.
- var/critical = 0 // Prevent disabling for important component, like the CPU.
- var/can_install = 1 // Prevents direct installation of removable media.
+ var/enabled = TRUE // If the hardware is turned off set this to 0.
+ var/critical = FALSE // Prevent disabling for important component, like the CPU.
+ var/can_install = TRUE // Prevents direct installation of removable media.
+ var/expansion_hw = FALSE // Hardware that fits into expansion bays.
+ var/removable = TRUE // Whether the hardware is removable or not.
var/damage = 0 // Current damage level
var/max_damage = 100 // Maximal damage level.
var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
@@ -20,7 +22,7 @@
var/malfunction_probability = 10// Chance of malfunction when the component is damaged
var/device_type
-/obj/item/computer_hardware/New(var/obj/L)
+/obj/item/computer_hardware/New(obj/L)
..()
pixel_x = rand(-8, 8)
pixel_y = rand(-8, 8)
@@ -56,7 +58,7 @@
return TRUE
// Called on multitool click, prints diagnostic information to the user.
-/obj/item/computer_hardware/proc/diagnostics(var/mob/user)
+/obj/item/computer_hardware/proc/diagnostics(mob/user)
to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]")
// Handles damage checks
@@ -73,7 +75,7 @@
return TRUE // Good to go.
-/obj/item/computer_hardware/examine(var/mob/user)
+/obj/item/computer_hardware/examine(mob/user)
. = ..()
if(damage > damage_failure)
. += "It seems to be severely damaged!"
diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm
index 0ad157afcb..c874d786a0 100644
--- a/code/modules/modular_computers/hardware/ai_slot.dm
+++ b/code/modules/modular_computers/hardware/ai_slot.dm
@@ -5,6 +5,7 @@
icon_state = "card_mini"
w_class = WEIGHT_CLASS_SMALL
device_type = MC_AI
+ expansion_hw = TRUE
var/obj/item/aicard/stored_card = null
var/locked = FALSE
@@ -19,12 +20,6 @@
if(stored_card)
. += "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it."
-/obj/item/computer_hardware/ai_slot/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/ai_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- M.remove_verb(device_type)
-
/obj/item/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
return FALSE
@@ -44,7 +39,7 @@
return TRUE
-/obj/item/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0)
+/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null,forced = FALSE)
if(!stored_card)
to_chat(user, "There is no card in \the [src].")
return FALSE
diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm
index 6e3193abfd..0668248315 100644
--- a/code/modules/modular_computers/hardware/battery_module.dm
+++ b/code/modules/modular_computers/hardware/battery_module.dm
@@ -21,7 +21,7 @@
/obj/item/computer_hardware/battery/handle_atom_del(atom/A)
if(A == battery)
- try_eject(0, null, TRUE)
+ try_eject(forced = TRUE)
. = ..()
/obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null)
@@ -48,7 +48,7 @@
return TRUE
-/obj/item/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0)
+/obj/item/computer_hardware/battery/try_eject(mob/living/user = null, forced = FALSE)
if(!battery)
to_chat(user, "There is no power cell connected to \the [src].")
return FALSE
diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm
index 18b423a42e..c243bf7db1 100644
--- a/code/modules/modular_computers/hardware/card_slot.dm
+++ b/code/modules/modular_computers/hardware/card_slot.dm
@@ -7,13 +7,10 @@
device_type = MC_CARD
var/obj/item/card/id/stored_card = null
- var/obj/item/card/id/stored_card2 = null
/obj/item/computer_hardware/card_slot/handle_atom_del(atom/A)
if(A == stored_card)
- try_eject(1, null, TRUE)
- if(A == stored_card2)
- try_eject(2, null, TRUE)
+ try_eject(null, TRUE)
. = ..()
/obj/item/computer_hardware/card_slot/Destroy()
@@ -21,37 +18,25 @@
return ..()
/obj/item/computer_hardware/card_slot/GetAccess()
- if(stored_card && stored_card2) // Best of both worlds
- return (stored_card.GetAccess() | stored_card2.GetAccess())
- else if(stored_card)
- return stored_card.GetAccess()
- else if(stored_card2)
- return stored_card2.GetAccess()
- return ..()
+ var/list/total_access
+ if(stored_card)
+ total_access = stored_card.GetAccess()
+ var/obj/item/computer_hardware/card_slot/card_slot2 = holder?.all_components[MC_CARD2] //Best of both worlds
+ if(card_slot2?.stored_card)
+ total_access |= card_slot2.stored_card.GetAccess()
+ return total_access
/obj/item/computer_hardware/card_slot/GetID()
if(stored_card)
return stored_card
- else if(stored_card2)
- return stored_card2
return ..()
/obj/item/computer_hardware/card_slot/RemoveID()
if(stored_card)
. = stored_card
- if(!try_eject(1))
+ if(!try_eject())
return null
return
- if(stored_card2)
- . = stored_card2
- if(!try_eject(2))
- return null
-
-/obj/item/computer_hardware/card_slot/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/card_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- M.remove_verb(device_type)
/obj/item/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
@@ -60,8 +45,7 @@
if(!istype(I, /obj/item/card/id))
return FALSE
- if(stored_card && stored_card2)
- to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.")
+ if(stored_card)
return FALSE
if(user)
if(!user.transferItemToLoc(I, src))
@@ -69,11 +53,8 @@
else
I.forceMove(src)
- if(!stored_card)
- stored_card = I
- else
- stored_card2 = I
- to_chat(user, "You insert \the [I] into \the [src].")
+ stored_card = I
+ to_chat(user, "You insert \the [I] into \the [expansion_hw ? "secondary":"primary"] [src].")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
if(ishuman(user))
var/mob/living/carbon/human/H = user
@@ -82,53 +63,58 @@
return TRUE
-/obj/item/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0)
- if(!stored_card && !stored_card2)
+/obj/item/computer_hardware/card_slot/try_eject(mob/living/user = null, forced = FALSE)
+ if(!stored_card)
to_chat(user, "There are no cards in \the [src].")
return FALSE
- var/ejected = 0
- if(stored_card && (!slot || slot == 1))
- if(user)
- user.put_in_hands(stored_card)
- else
- stored_card.forceMove(drop_location())
- stored_card = null
- ejected++
+ if(user)
+ user.put_in_hands(stored_card)
+ else
+ stored_card.forceMove(drop_location())
+ stored_card = null
- if(stored_card2 && (!slot || slot == 2))
- if(user)
- user.put_in_hands(stored_card2)
- else
- stored_card2.forceMove(drop_location())
- stored_card2 = null
- ejected++
+ if(holder)
+ if(holder.active_program)
+ holder.active_program.event_idremoved(0)
- if(ejected)
- if(holder)
- if(holder.active_program)
- holder.active_program.event_idremoved(0, slot)
-
- for(var/I in holder.idle_threads)
- var/datum/computer_file/program/P = I
- P.event_idremoved(1, slot)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.sec_hud_set_ID()
- to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
- return TRUE
- return FALSE
+ for(var/p in holder.idle_threads)
+ var/datum/computer_file/program/computer_program = p
+ computer_program.event_idremoved(1)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_user = user
+ human_user.sec_hud_set_ID()
+ to_chat(user, "You remove the card from \the [src].")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ return TRUE
/obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user)
if(..())
return
if(I.tool_behaviour == TOOL_SCREWDRIVER)
- to_chat(user, "You press down on the manual eject button with \the [I].")
- try_eject(0,user)
- return
+ if(stored_card)
+ to_chat(user, "You press down on the manual eject button with \the [I].")
+ try_eject(user)
+ return
+ swap_slot()
+ to_chat(user, "You adjust the connecter to fit into [expansion_hw ? "an expansion bay" : "the primary ID bay"].")
+
+/**
+ *Swaps the card_slot hardware between using the dedicated card slot bay on a computer, and using an expansion bay.
+*/
+/obj/item/computer_hardware/card_slot/proc/swap_slot()
+ expansion_hw = !expansion_hw
+ if(expansion_hw)
+ device_type = MC_CARD2
+ else
+ device_type = MC_CARD
/obj/item/computer_hardware/card_slot/examine(mob/user)
. = ..()
- if(stored_card || stored_card2)
+ . += "The connector is set to fit into [expansion_hw ? "an expansion bay" : "a computer's primary ID bay"], but can be adjusted with a screwdriver."
+ if(stored_card)
. += "There appears to be something loaded in the card slots."
+
+/obj/item/computer_hardware/card_slot/secondary
+ device_type = MC_CARD2
+ expansion_hw = TRUE
diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm
index b8b9624388..e5c133de20 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -22,14 +22,14 @@
. = ..()
. += "It has [max_capacity] GQ of storage capacity."
-/obj/item/computer_hardware/hard_drive/diagnostics(var/mob/user)
+/obj/item/computer_hardware/hard_drive/diagnostics(mob/user)
..()
// 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space.
to_chat(user, "NT-NFS File Table Status: [stored_files.len]/999")
to_chat(user, "Storage capacity: [used_capacity]/[max_capacity]GQ")
// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks.
-/obj/item/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F)
+/obj/item/computer_hardware/hard_drive/proc/store_file(datum/computer_file/F)
if(!F || !istype(F))
return 0
@@ -52,7 +52,7 @@
return 1
// Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks.
-/obj/item/computer_hardware/hard_drive/proc/remove_file(var/datum/computer_file/F)
+/obj/item/computer_hardware/hard_drive/proc/remove_file(datum/computer_file/F)
if(!F || !istype(F))
return 0
@@ -78,7 +78,7 @@
used_capacity = total_size
// Checks whether file can be stored on the hard drive. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file.
-/obj/item/computer_hardware/hard_drive/proc/can_store_file(var/datum/computer_file/F)
+/obj/item/computer_hardware/hard_drive/proc/can_store_file(datum/computer_file/F)
if(!F || !istype(F))
return 0
@@ -101,7 +101,7 @@
// Tries to find the file by filename. Returns null on failure
-/obj/item/computer_hardware/hard_drive/proc/find_file_by_name(var/filename)
+/obj/item/computer_hardware/hard_drive/proc/find_file_by_name(filename)
if(!check_functionality())
return null
diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm
index fe1b1879cb..04bf494fe4 100644
--- a/code/modules/modular_computers/hardware/network_card.dm
+++ b/code/modules/modular_computers/hardware/network_card.dm
@@ -11,7 +11,7 @@
device_type = MC_NET
var/static/ntnet_card_uid = 1
-/obj/item/computer_hardware/network_card/diagnostics(var/mob/user)
+/obj/item/computer_hardware/network_card/diagnostics(mob/user)
..()
to_chat(user, "NIX Unique ID: [identification_id]")
to_chat(user, "NIX User Tag: [identification_string]")
@@ -22,7 +22,7 @@
if(ethernet)
to_chat(user, "OpenEth (Physical Connection) - Physical network connection port")
-/obj/item/computer_hardware/network_card/New(var/l)
+/obj/item/computer_hardware/network_card/New(l)
..()
identification_id = ntnet_card_uid++
@@ -31,7 +31,7 @@
return "[identification_string] (NID [identification_id])"
// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection
-/obj/item/computer_hardware/network_card/proc/get_signal(var/specific_action = 0)
+/obj/item/computer_hardware/network_card/proc/get_signal(specific_action = 0)
if(!holder) // Hardware is not installed in anything. No signal. How did this even get called?
return 0
diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm
index b5a957be04..89b0382e86 100644
--- a/code/modules/modular_computers/hardware/portable_disk.dm
+++ b/code/modules/modular_computers/hardware/portable_disk.dm
@@ -8,12 +8,8 @@
max_capacity = 16
device_type = MC_SDD
-/obj/item/computer_hardware/hard_drive/portable/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- ..()
- M.remove_verb(device_type)
+/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/MC, mob/user)
+ return //this is a floppy disk, let's not shut the computer down when it gets pulled out.
/obj/item/computer_hardware/hard_drive/portable/install_default_programs()
return // Empty by default
diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm
index ebe40c1922..3bd5946435 100644
--- a/code/modules/modular_computers/hardware/printer.dm
+++ b/code/modules/modular_computers/hardware/printer.dm
@@ -5,6 +5,7 @@
icon_state = "printer"
w_class = WEIGHT_CLASS_NORMAL
device_type = MC_PRINT
+ expansion_hw = TRUE
var/stored_paper = 20
var/max_paper = 30
diff --git a/code/modules/modular_computers/hardware/sensor_package.dm b/code/modules/modular_computers/hardware/sensor_package.dm
new file mode 100644
index 0000000000..c0363bc809
--- /dev/null
+++ b/code/modules/modular_computers/hardware/sensor_package.dm
@@ -0,0 +1,8 @@
+//This item doesn't do much on its own, but is required by apps such as AtmoZphere.
+/obj/item/computer_hardware/sensorpackage
+ name = "sensor package"
+ desc = "An integrated sensor package allowing a computer to take readings from the environment. Required by certain programs."
+ icon_state = "servo"
+ w_class = WEIGHT_CLASS_TINY
+ device_type = MC_SENSORS
+ expansion_hw = TRUE
diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm
index a8d30bad21..83bb057d66 100644
--- a/code/modules/modular_computers/laptop_vendor.dm
+++ b/code/modules/modular_computers/laptop_vendor.dm
@@ -52,6 +52,7 @@
var/obj/item/computer_hardware/battery/battery_module = null
if(fabricate)
fabricated_laptop = new /obj/item/modular_computer/laptop/buildable(src)
+ fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot)
fabricated_laptop.install_component(new /obj/item/computer_hardware/battery)
battery_module = fabricated_laptop.all_components[MC_CELL]
total_price = 99
@@ -107,7 +108,7 @@
if(dev_card)
total_price += 199
if(fabricate)
- fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot)
+ fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot/secondary)
return total_price
else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this.
@@ -116,6 +117,7 @@
fabricated_tablet = new(src)
fabricated_tablet.install_component(new /obj/item/computer_hardware/battery)
fabricated_tablet.install_component(new /obj/item/computer_hardware/processor_unit/small)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot)
battery_module = fabricated_tablet.all_components[MC_CELL]
total_price = 199
switch(dev_battery)
@@ -154,11 +156,11 @@
if(dev_printer)
total_price += 99
if(fabricate)
- fabricated_tablet.install_component(new/obj/item/computer_hardware/printer)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/printer/mini)
if(dev_card)
total_price += 199
if(fabricate)
- fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot/secondary)
return total_price
return FALSE
@@ -257,7 +259,7 @@
say("Insufficient credits on card to purchase!")
return
credits += target_credits
- say("[target_credits] cr has been deposited from your account.")
+ say("[target_credits] cr have been withdrawn from your account.")
return
return ..()
diff --git a/code/modules/newscaster/newscaster_machine.dm b/code/modules/newscaster/newscaster_machine.dm
index 470b34e82c..7e515bf0f5 100644
--- a/code/modules/newscaster/newscaster_machine.dm
+++ b/code/modules/newscaster/newscaster_machine.dm
@@ -340,7 +340,6 @@ GLOBAL_LIST_EMPTY(allCasters)
dat+="Return"
var/datum/browser/popup = new(human_or_robot_user, "newscaster_main", "Newscaster Unit #[unit_no]", 400, 600)
popup.set_content(dat)
- popup.set_title_image(human_or_robot_user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/newscaster/Topic(href, href_list)
diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm
new file mode 100644
index 0000000000..dc8f172069
--- /dev/null
+++ b/code/modules/paperwork/carbonpaper.dm
@@ -0,0 +1,45 @@
+/obj/item/paper/carbon
+ name = "sheet of carbon"
+ icon_state = "paper_stack"
+ item_state = "paper"
+ // inhand_icon_state = "paper"
+ show_written_words = FALSE
+ var/copied = FALSE
+ var/iscopy = FALSE
+
+/obj/item/paper/carbon/update_icon_state()
+ if(iscopy)
+ icon_state = "cpaper"
+ else if(copied)
+ icon_state = "paper"
+ else
+ icon_state = "paper_stack"
+ if(info)
+ icon_state = "[icon_state]_words"
+
+/obj/item/paper/carbon/proc/removecopy(mob/living/user)
+ if(!copied)
+ var/obj/item/paper/carbon/C = src
+ var/copycontents = C.info
+ var/obj/item/paper/carbon/Copy = new /obj/item/paper/carbon(user.loc)
+
+ if(info)
+ copycontents = replacetext(copycontents, ""
+ Copy.name = "Copy - [C.name]"
+ to_chat(user, "You tear off the carbon-copy!")
+ C.copied = TRUE
+ Copy.iscopy = TRUE
+ Copy.update_icon_state()
+ C.update_icon_state()
+ user.put_in_hands(Copy)
+ else
+ to_chat(user, "There are no more carbon copies attached to this paper!")
+
+/obj/item/paper/carbon/on_attack_hand(mob/living/user)
+ if(loc == user && user.is_holding(src))
+ removecopy(user)
+ return
+ return ..()
diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm
index 1a93661c76..5b576a2438 100644
--- a/code/modules/paperwork/clipboard.dm
+++ b/code/modules/paperwork/clipboard.dm
@@ -3,6 +3,8 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "clipboard"
item_state = "clipboard"
+ // inhand_icon_state = "clipboard"
+ // worn_icon_state = "clipboard"
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
@@ -34,7 +36,6 @@
. += "clipboard_pen"
. += "clipboard_over"
-
/obj/item/clipboard/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/paper))
if(!user.transferItemToLoc(W, src))
@@ -92,14 +93,14 @@
to_chat(usr, "You slot [W] into [src].")
if(href_list["write"])
- var/obj/item/P = locate(href_list["write"])
- if(istype(P) && P.loc == src)
+ var/obj/item/P = locate(href_list["write"]) in src
+ if(istype(P))
if(usr.get_active_held_item())
P.attackby(usr.get_active_held_item(), usr)
if(href_list["remove"])
- var/obj/item/P = locate(href_list["remove"])
- if(istype(P) && P.loc == src)
+ var/obj/item/P = locate(href_list["remove"]) in src
+ if(istype(P))
P.forceMove(usr.loc)
usr.put_in_hands(P)
if(P == toppaper)
@@ -111,13 +112,13 @@
toppaper = null
if(href_list["read"])
- var/obj/item/paper/P = locate(href_list["read"])
- if(istype(P) && P.loc == src)
+ var/obj/item/paper/P = locate(href_list["read"]) in src
+ if(istype(P))
usr.examinate(P)
if(href_list["top"])
- var/obj/item/P = locate(href_list["top"])
- if(istype(P) && P.loc == src)
+ var/obj/item/P = locate(href_list["top"]) in src
+ if(istype(P))
toppaper = P
to_chat(usr, "You move [P.name] to the top.")
diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm
index b8f2c95762..390cd0cf83 100644
--- a/code/modules/paperwork/filingcabinet.dm
+++ b/code/modules/paperwork/filingcabinet.dm
@@ -35,7 +35,7 @@
. = ..()
if(mapload)
for(var/obj/item/I in loc)
- if(istype(I, /obj/item/paper) || istype(I, /obj/item/folder) || istype(I, /obj/item/photo))
+ if(I.w_class < WEIGHT_CLASS_NORMAL) //there probably shouldn't be anything placed ontop of filing cabinets in a map that isn't meant to go in them
I.forceMove(src)
/obj/structure/filingcabinet/deconstruct(disassembled = TRUE)
@@ -46,7 +46,12 @@
qdel(src)
/obj/structure/filingcabinet/attackby(obj/item/P, mob/user, params)
- if(istype(P, /obj/item/paper) || istype(P, /obj/item/folder) || istype(P, /obj/item/photo) || istype(P, /obj/item/documents))
+ if(P.tool_behaviour == TOOL_WRENCH && user.a_intent != INTENT_HELP)
+ to_chat(user, "You begin to [anchored ? "unwrench" : "wrench"] [src].")
+ if(P.use_tool(src, user, 20, volume=50))
+ to_chat(user, "You successfully [anchored ? "unwrench" : "wrench"] [src].")
+ anchored = !anchored
+ else if(P.w_class < WEIGHT_CLASS_NORMAL)
if(!user.transferItemToLoc(P, src))
return
to_chat(user, "You put [P] in [src].")
@@ -54,11 +59,6 @@
sleep(5)
icon_state = initial(icon_state)
updateUsrDialog()
- else if(istype(P, /obj/item/wrench))
- to_chat(user, "You begin to [anchored ? "unwrench" : "wrench"] [src].")
- if(P.use_tool(src, user, 20, volume=50))
- to_chat(user, "You successfully [anchored ? "unwrench" : "wrench"] [src].")
- anchored = !anchored
else if(user.a_intent != INTENT_HARM)
to_chat(user, "You can't put [P] in [src]!")
else
@@ -67,9 +67,6 @@
/obj/structure/filingcabinet/ui_interact(mob/user)
. = ..()
- if(isobserver(user))
- return
-
if(contents.len <= 0)
to_chat(user, "[src] is empty.")
return
@@ -100,16 +97,17 @@
to_chat(user, "You find nothing in [src].")
/obj/structure/filingcabinet/Topic(href, href_list)
+ if(!usr.canUseTopic(src, BE_CLOSE, ismonkey(usr)))
+ return
if(href_list["retrieve"])
usr << browse("", "window=filingcabinet") // Close the menu
- var/obj/item/P = locate(href_list["retrieve"])//contents[retrieveindex]
- if(istype(P) && P.loc == src && in_range(src, usr))
+ var/obj/item/P = locate(href_list["retrieve"]) in src //contents[retrieveindex]
+ if(istype(P) && in_range(src, usr))
usr.put_in_hands(P)
updateUsrDialog()
icon_state = "[initial(icon_state)]-open"
- sleep(5)
- icon_state = initial(icon_state)
+ addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), 5)
/*
@@ -170,6 +168,7 @@
virgin = 0 //tabbing here is correct- it's possible for people to try and use it
//before the records have been generated, so we do this inside the loop.
+//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/filingcabinet/medical/on_attack_hand()
populate()
. = ..()
diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm
index c32afab342..305099d115 100644
--- a/code/modules/paperwork/folders.dm
+++ b/code/modules/paperwork/folders.dm
@@ -33,7 +33,10 @@
if(contents.len)
. += "folder_paper"
+
/obj/item/folder/attackby(obj/item/W, mob/user, params)
+ if(burn_paper_product_attackby_check(W, user))
+ return
if(istype(W, /obj/item/paper) || istype(W, /obj/item/photo) || istype(W, /obj/item/documents))
if(!user.transferItemToLoc(W, src))
return
@@ -43,11 +46,14 @@
if(!user.is_literate())
to_chat(user, "You scribble illegibly on the cover of [src]!")
return
+
var/inputvalue = stripped_input(user, "What would you like to label the folder?", "Folder Labelling", "", MAX_NAME_LEN)
+
if(!inputvalue)
return
+
if(user.canUseTopic(src, BE_CLOSE))
- name = "folder - '[inputvalue]'"
+ name = "folder[(inputvalue ? " - '[inputvalue]'" : null)]"
/obj/item/folder/Destroy()
@@ -76,14 +82,14 @@
if(usr.contents.Find(src))
if(href_list["remove"])
- var/obj/item/I = locate(href_list["remove"])
- if(istype(I) && I.loc == src)
+ var/obj/item/I = locate(href_list["remove"]) in src
+ if(istype(I))
I.forceMove(usr.loc)
usr.put_in_hands(I)
if(href_list["read"])
- var/obj/item/I = locate(href_list["read"])
- if(istype(I) && I.loc == src)
+ var/obj/item/I = locate(href_list["read"]) in src
+ if(istype(I))
usr.examinate(I)
//Update everything
diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index d054e5ff12..da9fdc4ca4 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -1,9 +1,10 @@
/obj/item/hand_labeler
name = "hand labeler"
- desc = "A combined label printer and applicator in a portable device, designed to be easy to operate and use."
+ desc = "A combined label printer, applicator, and remover, all in a single portable device. Designed to be easy to operate and use."
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "labeler0"
item_state = "flight"
+ // inhand_icon_state = "flight"
var/label = null
var/labels_left = 30
var/mode = 0
@@ -55,9 +56,10 @@
to_chat(user, "You can't label creatures!") // use a collar
return
- user.visible_message("[user] labels [A] as [label].", \
- "You label [A] as [label].")
- A.name = "[A.name] ([label])"
+ user.visible_message("[user] labels [A] with \"[label]\".", \
+ "You label [A] with \"[label]\".")
+ A.AddComponent(/datum/component/label, label)
+ // playsound(A, 'sound/items/handling/component_pickup.ogg', 20, TRUE)
labels_left--
@@ -90,7 +92,9 @@
name = "cyborg-hand labeler"
/obj/item/hand_labeler/borg/afterattack(atom/A, mob/user, proximity)
- . = ..(A, user, proximity)
+ . = ..()
+ if(!proximity)
+ return
if(!iscyborg(user))
return
@@ -114,6 +118,7 @@
desc = "A roll of paper. Use it on a hand labeler to refill it."
icon_state = "labeler_refill"
item_state = "electropack"
+ // inhand_icon_state = "electropack"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 5d842ef11a..20ec678e45 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -11,41 +11,6 @@
#define MODE_WRITING 1
#define MODE_STAMPING 2
-/**
- * This is a custom ui state. All it really does is keep track of pen
- * being used and if they are editing it or not. This way we can keep
- * the data with the ui rather than on the paper
- */
-/datum/ui_state/default/paper_state
- /// What edit mode we are in and who is
- /// writing on it right now
- var/edit_mode = MODE_READING
- /// Setup for writing to a sheet
- var/pen_color = "black"
- var/pen_font = ""
- var/is_crayon = FALSE
- /// Setup for stamping a sheet
- // Why not the stamp obj? I have no idea
- // what happens to states out of scope so
- // don't want to put instances in this
- var/stamp_icon_state = ""
- var/stamp_name = ""
- var/stamp_class = ""
-
-/datum/ui_state/default/paper_state/proc/copy_from(datum/ui_state/default/paper_state/from)
- switch(from.edit_mode)
- if(MODE_READING)
- edit_mode = MODE_READING
- if(MODE_WRITING)
- edit_mode = MODE_WRITING
- pen_color = from.pen_color
- pen_font = from.pen_font
- is_crayon = from.is_crayon
- if(MODE_STAMPING)
- edit_mode = MODE_STAMPING
- stamp_icon_state = from.stamp_icon_state
- stamp_class = from.stamp_class
- stamp_name = from.stamp_name
/**
* Paper is now using markdown (like in github pull notes) for ALL rendering
@@ -58,6 +23,9 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paper"
item_state = "paper"
+ // inhand_icon_state = "paper"
+ // worn_icon_state = "paper"
+ // custom_fire_overlay = "paper_onfire_overlay"
throwforce = 0
w_class = WEIGHT_CLASS_TINY
throw_range = 1
@@ -80,34 +48,19 @@
var/list/stamps /// Positioning for the stamp in tgui
var/list/stamped /// Overlay info
- /// This REALLY should be a componenet. Basicly used during, april fools
- /// to honk at you
- var/rigged = 0
- var/spam_flag = 0
-
var/contact_poison // Reagent ID to transfer on contact
var/contact_poison_volume = 0
- // Ok, so WHY are we caching the ui's?
- // Since we are not using autoupdate we
- // need some way to update the ui's of
- // other people looking at it and if
- // its been updated. Yes yes, lame
- // but canot be helped. However by
- // doing it this way, we can see
- // live updates and have multipule
- // people look at it
- var/list/viewing_ui = list()
-
/// When the sheet can be "filled out"
/// This is an associated list
var/list/form_fields = list()
var/field_counter = 1
/obj/item/paper/Destroy()
- close_all_ui()
stamps = null
stamped = null
+ form_fields = null
+ stamped = null
. = ..()
/**
@@ -162,7 +115,7 @@
set category = "Object"
set src in usr
- if(usr.incapacitated() || !usr.is_literate())
+ if(!usr.can_read(src) || usr.incapacitated(TRUE, TRUE) || (isobserver(usr) && !IsAdminGhost(usr)))
return
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
@@ -180,18 +133,6 @@
user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...")
return (BRUTELOSS)
-/// ONLY USED FOR APRIL FOOLS
-/obj/item/paper/proc/reset_spamflag()
- spam_flag = FALSE
-
-/obj/item/paper/attack_self(mob/user)
- if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
- if(!spam_flag)
- spam_flag = TRUE
- playsound(loc, 'sound/items/bikehorn.ogg', 50, TRUE)
- addtimer(CALLBACK(src, .proc/reset_spamflag), 20)
- . = ..()
-
/obj/item/paper/proc/clearpaper()
info = ""
stamps = null
@@ -199,28 +140,39 @@
cut_overlays()
update_icon_state()
-/obj/item/paper/examine_more(mob/user)
- ui_interact(user)
- return list("You try to read [src]...")
+/obj/item/paper/examine(mob/user)
+ . = ..()
+ if(!in_range(user, src) && !isobserver(user))
+ . += "You're too far away to read it!"
+ return
+ if(user.can_read(src))
+ ui_interact(user)
+ return
+ . += "You cannot read it!"
+
+/obj/item/paper/ui_status(mob/user,/datum/ui_state/state)
+ // Are we on fire? Hard ot read if so
+ if(resistance_flags & ON_FIRE)
+ return UI_CLOSE
+ if(!in_range(user,src))
+ return UI_CLOSE
+ if(user.incapacitated(TRUE, TRUE) || (isobserver(user) && !IsAdminGhost(user)))
+ return UI_UPDATE
+ // Even harder to read if your blind...braile? humm
+ // .. or if you cannot read
+ if(!user.can_read(src))
+ return UI_CLOSE
+ if(in_contents_of(/obj/machinery/door/airlock))
+ return UI_INTERACTIVE
+ return ..()
+
+
/obj/item/paper/can_interact(mob/user)
- if(!..())
- return FALSE
- // Are we on fire? Hard ot read if so
- if(resistance_flags & ON_FIRE)
- return FALSE
- // Even harder to read if your blind...braile? humm
- if(user.is_blind())
- return FALSE
- // checks if the user can read.
- return user.can_read(src)
+ if(in_contents_of(/obj/machinery/door/airlock))
+ return TRUE
+ return ..()
-/**
- * This creates the ui, since we are using a custom state but not much else
- * just makes it easyer to make it.
- */
-/obj/item/paper/proc/create_ui(mob/user, datum/ui_state/default/paper_state/state)
- ui_interact(user, state = state)
/obj/item/proc/burn_paper_product_attackby_check(obj/item/I, mob/living/user, bypass_clumsy)
var/ignition_message = I.ignition_effect(src, user)
@@ -244,49 +196,22 @@
/obj/item/paper/attackby(obj/item/P, mob/living/user, params)
if(burn_paper_product_attackby_check(P, user))
- close_all_ui()
+ SStgui.close_uis(src)
return
if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
if(length(info) >= MAX_PAPER_LENGTH) // Sheet must have less than 1000 charaters
to_chat(user, "This sheet of paper is full!")
return
-
- var/datum/ui_state/default/paper_state/state = new
- state.edit_mode = MODE_WRITING
- // should a crayon be in the same subtype as a pen? How about a brush or charcoal?
- // TODO: Convert all writing stuff to one type, /obj/item/art_tool maybe?
- state.is_crayon = istype(P, /obj/item/toy/crayon);
- if(state.is_crayon)
- var/obj/item/toy/crayon/PEN = P
- state.pen_font = CRAYON_FONT
- state.pen_color = PEN.paint_color
- else
- var/obj/item/pen/PEN = P
- state.pen_font = PEN.font
- state.pen_color = PEN.colour
-
- create_ui(user, state)
+ ui_interact(user)
return
else if(istype(P, /obj/item/stamp))
-
- var/datum/ui_state/default/paper_state/state = new
- state.edit_mode = MODE_STAMPING // we are read only becausse the sheet is full
- state.stamp_icon_state = P.icon_state
- state.stamp_name = P.name
-
- var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/simple/paper)
- state.stamp_class = sheet.icon_class_name(P.icon_state)
-
to_chat(user, "You ready your stamp over the paper! ")
-
- create_ui(user, state)
+ ui_interact(user)
return /// Normaly you just stamp, you don't need to read the thing
else
// cut paper? the sky is the limit!
- var/datum/ui_state/default/paper_state/state = new
- state.edit_mode = MODE_READING
- create_ui(user, state) // The other ui will be created with just read mode outside of this
+ ui_interact(user) // The other ui will be created with just read mode outside of this
return ..()
@@ -301,68 +226,64 @@
get_asset_datum(/datum/asset/spritesheet/simple/paper),
)
-/obj/item/paper/ui_interact(mob/user, datum/tgui/ui,
- datum/ui_state/default/paper_state/state)
- // Update the state
- ui = ui || SStgui.get_open_ui(user, src)
- if(ui && state)
- var/datum/ui_state/default/paper_state/current_state = ui.state
- current_state.copy_from(state)
+/obj/item/paper/ui_interact(mob/user, datum/tgui/ui)
// Update the UI
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PaperSheet", name)
- state = new
- ui.set_state(state)
- ui.set_autoupdate(FALSE)
- viewing_ui[user] = ui
ui.open()
-/obj/item/paper/ui_close(mob/user)
- /// close the editing window and change the mode
- viewing_ui[user] = null
- . = ..()
-// Again, we have to do this as autoupdate is off
-/obj/item/paper/proc/update_all_ui()
- for(var/datum/tgui/ui in viewing_ui)
- ui.process(force = TRUE)
+/obj/item/paper/ui_static_data(mob/user)
+ . = list()
+ .["text"] = info
+ .["max_length"] = MAX_PAPER_LENGTH
+ .["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set
+ .["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling?
+ .["stamps"] = stamps
+
-// Again, we have to do this as autoupdate is off
-/obj/item/paper/proc/close_all_ui()
- for(var/datum/tgui/ui in viewing_ui)
- ui.close()
- viewing_ui = list()
/obj/item/paper/ui_data(mob/user)
var/list/data = list()
-
- var/datum/tgui/ui = viewing_ui[user]
- var/datum/ui_state/default/paper_state/state = ui.state
-
- // Should all this go in static data and just do a forced update?
- data["text"] = info
- data["max_length"] = MAX_PAPER_LENGTH
- data["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling?
- data["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set
- data["stamps"] = stamps
-
- data["edit_mode"] = state.edit_mode
- data["edit_usr"] = "[ui.user]";
-
- // pen info for editing
- data["is_crayon"] = state.is_crayon
- data["pen_font"] = state.pen_font
- data["pen_color"] = state.pen_color
- // stamping info for..stamping
- data["stamp_class"] = state.stamp_class
-
+ var/obj/O = user.get_active_held_item()
+ if(istype(O, /obj/item/toy/crayon))
+ var/obj/item/toy/crayon/PEN = O
+ data["pen_font"] = CRAYON_FONT
+ data["pen_color"] = PEN.paint_color
+ data["edit_mode"] = MODE_WRITING
+ data["is_crayon"] = TRUE
+ data["stamp_class"] = "FAKE"
+ data["stamp_icon_state"] = "FAKE"
+ else if(istype(O, /obj/item/pen))
+ var/obj/item/pen/PEN = O
+ data["pen_font"] = PEN.font
+ data["pen_color"] = PEN.colour
+ data["edit_mode"] = MODE_WRITING
+ data["is_crayon"] = FALSE
+ data["stamp_class"] = "FAKE"
+ data["stamp_icon_state"] = "FAKE"
+ else if(istype(O, /obj/item/stamp))
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/simple/paper)
+ data["stamp_icon_state"] = O.icon_state
+ data["stamp_class"] = sheet.icon_class_name(O.icon_state)
+ data["edit_mode"] = MODE_STAMPING
+ data["pen_font"] = "FAKE"
+ data["pen_color"] = "FAKE"
+ data["is_crayon"] = FALSE
+ else
+ data["edit_mode"] = MODE_READING
+ data["pen_font"] = "FAKE"
+ data["pen_color"] = "FAKE"
+ data["is_crayon"] = FALSE
+ data["stamp_icon_state"] = "FAKE"
+ data["stamp_class"] = "FAKE"
data["field_counter"] = field_counter
data["form_fields"] = form_fields
return data
-/obj/item/paper/ui_act(action, params, datum/tgui/ui, datum/ui_state/default/paper_state/state)
+/obj/item/paper/ui_act(action, params,datum/tgui/ui)
if(..())
return
switch(action)
@@ -370,34 +291,33 @@
var/stamp_x = text2num(params["x"])
var/stamp_y = text2num(params["y"])
var/stamp_r = text2num(params["r"]) // rotation in degrees
-
+ var/stamp_icon_state = params["stamp_icon_state"]
+ var/stamp_class = params["stamp_class"]
if (isnull(stamps))
- stamps = new/list()
+ stamps = list()
if(stamps.len < MAX_PAPER_STAMPS)
// I hate byond when dealing with freaking lists
- stamps += list(list(state.stamp_class, stamp_x, stamp_y,stamp_r)) /// WHHHHY
+ stamps[++stamps.len] = list(stamp_class, stamp_x, stamp_y, stamp_r) /// WHHHHY
/// This does the overlay stuff
if (isnull(stamped))
- stamped = new/list()
+ stamped = list()
if(stamped.len < MAX_PAPER_STAMPS_OVERLAYS)
- var/mutable_appearance/stampoverlay = mutable_appearance('icons/obj/bureaucracy.dmi', "paper_[state.stamp_icon_state]")
+ var/mutable_appearance/stampoverlay = mutable_appearance('icons/obj/bureaucracy.dmi', "paper_[stamp_icon_state]")
stampoverlay.pixel_x = rand(-2, 2)
stampoverlay.pixel_y = rand(-3, 2)
add_overlay(stampoverlay)
- LAZYADD(stamped, state.stamp_icon_state)
+ LAZYADD(stamped, stamp_icon_state)
- ui.user.visible_message("[ui.user] stamps [src] with [state.stamp_name]!", "You stamp [src] with [state.stamp_name]!")
+ update_static_data(usr,ui)
+ ui.user.visible_message("[ui.user] stamps [src] with [stamp_class]!", "You stamp [src] with [stamp_class]!")
else
to_chat(usr, pick("You try to stamp but you miss!", "There is no where else you can stamp!"))
-
- update_all_ui()
. = TRUE
if("save")
var/in_paper = params["text"]
var/paper_len = length(in_paper)
- var/list/fields = params["form_fields"]
field_counter = params["field_counter"] ? text2num(params["field_counter"]) : field_counter
if(paper_len > MAX_PAPER_LENGTH)
@@ -413,14 +333,10 @@
if(info != in_paper)
to_chat(ui.user, "You have added to your paper masterpiece!");
info = in_paper
-
- for(var/key in fields)
- form_fields[key] = fields[key];
+ update_static_data(usr,ui)
- update_all_ui()
update_icon()
-
. = TRUE
/**
diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm
index 3937720f74..a3b9b23141 100644
--- a/code/modules/paperwork/paper_cutter.dm
+++ b/code/modules/paperwork/paper_cutter.dm
@@ -26,11 +26,11 @@
var/obj/item/bodypart/BP = C.get_bodypart(BODY_ZONE_HEAD)
if(BP)
BP.drop_limb()
- playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
+ playsound(loc, pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg'),50, TRUE, -1)
return (BRUTELOSS)
else
user.visible_message("[user] repeatedly bashes [src.name] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
+ playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
return (BRUTELOSS)
@@ -42,11 +42,12 @@
if(storedpaper)
. += "paper"
+
/obj/item/papercutter/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/paper) && !storedpaper)
if(!user.transferItemToLoc(P, src))
return
- playsound(loc, "pageturn", 60, 1)
+ playsound(loc, "pageturn", 60, TRUE)
to_chat(user, "You place [P] in [src].")
storedpaper = P
update_icon()
@@ -59,17 +60,17 @@
storedcutter = P
update_icon()
return
- if(istype(P, /obj/item/screwdriver) && storedcutter)
+ if(P.tool_behaviour == TOOL_SCREWDRIVER && storedcutter)
P.play_tool_sound(src)
to_chat(user, "[storedcutter] has been [cuttersecured ? "unsecured" : "secured"].")
cuttersecured = !cuttersecured
return
..()
-/obj/item/papercutter/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/obj/item/papercutter/on_attack_hand(mob/user)
add_fingerprint(user)
if(!storedcutter)
- to_chat(user, "The cutting blade is gone! You can't use [src] now.")
+ to_chat(user, "The cutting blade is gone! You can't use [src] now.")
return
if(!cuttersecured)
@@ -79,7 +80,7 @@
update_icon()
if(storedpaper)
- playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1)
+ playsound(src.loc, 'sound/weapons/slash.ogg', 50, TRUE)
to_chat(user, "You neatly cut [storedpaper].")
storedpaper = null
qdel(storedpaper)
@@ -88,6 +89,7 @@
update_icon()
/obj/item/papercutter/MouseDrop(atom/over_object)
+ . = ..()
var/mob/M = usr
if(M.incapacitated() || !Adjacent(M))
return
@@ -98,10 +100,6 @@
else if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
-
- else
- . = ..()
-
add_fingerprint(M)
/obj/item/paperslip
@@ -112,6 +110,12 @@
resistance_flags = FLAMMABLE
max_integrity = 50
+/obj/item/paperslip/attackby(obj/item/I, mob/living/user, params)
+ if(burn_paper_product_attackby_check(I, user))
+ return
+ return ..()
+
+
/obj/item/paperslip/Initialize()
. = ..()
pixel_x = rand(-5, 5)
@@ -124,5 +128,6 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "cutterblade"
item_state = "knife"
+ // inhand_icon_state = "knife"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
diff --git a/code/modules/paperwork/paper_premade.dm b/code/modules/paperwork/paper_premade.dm
index 392eba5f56..5d6d3d45e8 100644
--- a/code/modules/paperwork/paper_premade.dm
+++ b/code/modules/paperwork/paper_premade.dm
@@ -4,53 +4,22 @@
/obj/item/paper/fluff/sop
name = "paper- 'Standard Operating Procedure'"
- info = {"
-Alert Levels:
-* Blue - Emergency
- * Caused by fire
- * Caused by manual interaction
- * Action: Close all fire doors. These can only be opened by resetting the alarm
-* Red- Ejection/Self Destruct
- * Caused by module operating computer.
- * Action: After the specified time the module will eject completely.
-Engine Maintenance Instructions:
-1. Shut off ignition systems:
-2. Activate internal power
-3. Activate orbital balance matrix
-4. Remove volatile liquids from area
-5. Wear a fire suit
-6. After Decontaminate Visit medical examiner
-Toxin Laboratory Procedure:
-1. Wear a gas mask regardless
-2. Get an oxygen tank.
-3. Activate internal atmosphere
-4. After Decontaminate Visit medical examiner
-Disaster Procedure:
-Fire:
-1. Activate sector fire alarm.
-2. Move to a safe area.
-3. Get a fire suit
-* After:
- 1. Assess Damage
- 2. Repair damages
- 3. If needed, Evacuate
-Meteor Shower:
-1. Activate fire alarm
-2. Move to the back of ship
-* After
- 1. Repair damage
- 2. If needed, Evacuate
-Accidental Reentry:
-1. Activate fire alarms in front of ship.
-2. Move volatile matter to a fire proof area!
-3. Get a fire suit.
-4. Stay secure until an emergency ship arrives.
-5. If ship does not arrive-Evacuate to a nearby safe area!
-"};
+ info = "Alert Levels: \nBlue- Emergency \n\t1. Caused by fire \n\t2. Caused by manual interaction \n\tAction: \n\t\tClose all fire doors. These can only be opened by resetting the alarm \nRed- Ejection/Self-Destruct \n\t1. Caused by module operating computer. \n\tAction: \n\t\tAfter the specified time the module will eject completely. \n \nEngine Maintenance Instructions: \n\tShut off ignition systems: \n\tActivate internal power \n\tActivate orbital balance matrix \n\tRemove volatile liquids from area \n\tWear a fire suit \n \n\tAfter \n\t\tDecontaminate \n\t\tVisit medical examiner \n \nToxin Laboratory Procedure: \n\tWear a gas mask regardless \n\tGet an oxygen tank. \n\tActivate internal atmosphere \n \n\tAfter \n\t\tDecontaminate \n\t\tVisit medical examiner \n \nDisaster Procedure: \n\tFire: \n\t\tActivate sector fire alarm. \n\t\tMove to a safe area. \n\t\tGet a fire suit \n\t\tAfter: \n\t\t\tAssess Damage \n\t\t\tRepair damages \n\t\t\tIf needed, Evacuate \n\tMeteor Shower: \n\t\tActivate fire alarm \n\t\tMove to the back of ship \n\t\tAfter \n\t\t\tRepair damage \n\t\t\tIf needed, Evacuate \n\tAccidental Reentry: \n\t\tActivate fire alarms in front of ship. \n\t\tMove volatile matter to a fire proof area! \n\t\tGet a fire suit. \n\t\tStay secure until an emergency ship arrives. \n \n\t\tIf ship does not arrive- \n\t\t\tEvacuate to a nearby safe area!"
/obj/item/paper/fluff/shuttles/daniel
info = "i love daniel daniel is my best friend
you are tearing me apart elise"
+/obj/item/paper/fluff/jobs/prisoner/letter
+ name = "letter from home"
+ info = {"Dearest sweetheart,
+ It is truly saddening you must spend your time locked up in an awful prison on that dangerous station. I have spoken to your lawyer who will attempt to appeal to the judge so your sentence may hopefully be reduced.
+ Regardless, I just want you to understand that all of us out here still love you, and want to see you released safely some day! I know that prison can be a very vicious place, so please promise us you'll avoid getting into any fights or trouble, okay?
+ We all care for your safety deeply, and could not live with ourselves if you ended up getting hurt. We've scheduled a visit to see you, and with any luck, hopefully our request will be granted soon.
+ Anyways, please do your best to make it by in that place, and never forget we'll be always here for you, no matter if we're separated.
+
+ Please stay safe,
+ -Love, Your Dearest"}
+
//////////// Job guides n' fluff
@@ -90,9 +59,42 @@ Accidental Reentry:
name = "paper- 'Chemical Information'"
info = "Known Onboard Toxins: \n\tGrade A Semi-Liquid Plasma: \n\t\tHighly poisonous. You cannot sustain concentrations above 15 units. \n\t\tA gas mask fails to filter plasma after 50 units. \n\t\tWill attempt to diffuse like a gas. \n\t\tFiltered by scrubbers. \n\t\tThere is a bottled version which is very different \n\t\t\tfrom the version found in canisters! \n \n\t\tWARNING: Highly Flammable. Keep away from heat sources \n\t\texcept in an enclosed fire area! \n\t\tWARNING: It is a crime to use this without authorization. \nKnown Onboard Anti-Toxin: \n\tAnti-Toxin Type 01P: Works against Grade A Plasma. \n\t\tBest if injected directly into bloodstream. \n\t\tA full injection is in every regular Med-Kit. \n\t\tSpecial toxin Kits hold around 7. \n \nKnown Onboard Chemicals (other): \n\tRejuvenation T#001: \n\t\tEven 1 unit injected directly into the bloodstream \n\t\t\twill cure unconscious and sleep toxins. \n\t\tIf administered to a dying patient it will prevent \n\t\t\tfurther damage for about units*3 seconds. \n\t\t\tit will not cure them or allow them to be cured. \n\t\tIt can be administered to a non-dying patient \n\t\t\tbut the chemicals disappear just as fast. \n\tMorphine T#054: \n\t\t5 units will induce precisely 1 minute of sleep. \n\t\t\tThe effect are cumulative. \n\t\tWARNING: It is a crime to use this without authorization"
+/obj/item/paper/fluff/jobs/medical/hippocratic
+ name = "paper- 'Hippocratic Oath'"
+ info = {"I swear to fulfill, to the best of my ability and judgment, this covenant:
+
+ I will respect the hard-won scientific gains of those physicians in whose steps I walk,
+ and gladly share such knowledge as is mine with those who are to follow.
+
+ I will apply, for the benefit of the sick, all measures that are required,
+ avoiding those twin traps of overtreatment and therapeutic nihilism.
+
+ I will remember that there is art to medicine as well as science,
+ and that warmth, sympathy, and understanding may outweigh the surgeon's knife or the chemist's drug.
+
+ I will not be ashamed to say "I know not,"
+ nor will I fail to call in my colleagues when the skills of another are needed for a patient's recovery.
+
+ I will respect the privacy of my patients, for their problems are not disclosed to me that the world may know. Most especially must I tread with care in matters of life and death.
+ If it is given me to save a life, all thanks. But it may also be within my power to take a life;
+ this awesome responsibility must be faced with great humbleness and awareness of my own frailty. Above all, I must not play at God.
+
+ I will remember that I do not treat a fever chart, a cancerous growth, but a sick human being, whose illness may affect the person's family and economic stability.
+ My responsibility includes these related problems, if I am to care adequately for the sick.
+
+ I will prevent disease whenever I can, for prevention is preferable to cure.
+
+ I will remember that I remain a member of society, with special obligations to all my fellow human beings,
+ those sound of mind and body as well as the infirm.
+
+ If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter.
+ May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.
+ "}
+
/*
* Stations
*/
+////////// cogstation.
/obj/item/paper/guides/cogstation/job_changes
name = "MEMO: Job Changes"
diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm
index 28cb5ffae9..b8bbd0a30e 100644
--- a/code/modules/paperwork/paperbin.dm
+++ b/code/modules/paperwork/paperbin.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paper_bin1"
item_state = "sheet-metal"
+ // inhand_icon_state = "sheet-metal"
lefthand_file = 'icons/mob/inhands/misc/sheets_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/sheets_righthand.dmi'
throwforce = 0
@@ -43,6 +44,7 @@
..()
/obj/item/paper_bin/MouseDrop(atom/over_object)
+ . = ..()
var/mob/living/M = usr
if(!istype(M) || M.incapacitated() || !Adjacent(M))
return
@@ -54,17 +56,18 @@
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
- else
- . = ..()
-
add_fingerprint(M)
/obj/item/paper_bin/attack_paw(mob/user)
return attack_hand(user)
-/obj/item/paper_bin/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
- if(user.lying)
- return
+//ATTACK HAND IGNORING PARENT RETURN VALUE
+/obj/item/paper_bin/on_attack_hand(mob/user)
+ if(isliving(user))
+ var/mob/living/L = user
+ if(!(L.mobility_flags & MOBILITY_PICKUP))
+ return
+ // user.changeNext_move(CLICK_CD_MELEE)
if(bin_pen)
var/obj/item/pen/P = bin_pen
P.add_fingerprint(user)
@@ -85,8 +88,8 @@
P = new papertype(src)
if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
if(prob(30))
- P.info = "*HONK HONK HONK HONK HONK HONK HONK HOOOOOOOOOOOOOOOOOOOOOONK*\n*APRIL FOOLS*\n"
- P.rigged = 1
+ P.info = "HONK HONK HONK HONK HONK HONK HONK HOOOOOOOOOOOOOOOOOOOOOONK APRIL FOOLS"
+ P.AddComponent(/datum/component/honkspam)
P.add_fingerprint(user)
P.forceMove(user.loc)
@@ -148,8 +151,7 @@
papertype = /obj/item/paper/natural
resistance_flags = FLAMMABLE
-/obj/item/paper_bin/bundlenatural/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
- ..()
+/obj/item/paper_bin/bundlenatural/on_attack_hand(mob/user)
if(total_paper < 1)
qdel(src)
@@ -173,3 +175,9 @@
qdel(src)
else
..()
+
+/obj/item/paper_bin/carbon
+ name = "carbon paper bin"
+ desc = "Contains all the paper you'll ever need, in duplicate!"
+ icon_state = "paper_bin_carbon"
+ papertype = /obj/item/paper/carbon
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index c6a1ee1389..53fe886d65 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -1,9 +1,9 @@
-
/obj/item/paperplane
name = "paper plane"
desc = "Paper, folded in the shape of a plane."
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paperplane"
+ // custom_fire_overlay = "paperplane_onfire"
throw_range = 7
throw_speed = 1
throwforce = 0
@@ -11,7 +11,7 @@
resistance_flags = FLAMMABLE
max_integrity = 50
- var/hit_probability = 2//%
+ var/hit_probability = 2 //%
var/obj/item/paper/internalPaper
/obj/item/paperplane/origami
@@ -41,6 +41,13 @@
qdel(src)
return ..()
+/obj/item/paperplane/Exited(atom/movable/AM, atom/newLoc)
+ . = ..()
+ if (AM == internalPaper)
+ internalPaper = null
+ if(!QDELETED(src))
+ qdel(src)
+
/obj/item/paperplane/Destroy()
QDEL_NULL(internalPaper)
return ..()
@@ -71,31 +78,19 @@
user.put_in_hands(internal_paper_tmp)
/obj/item/paperplane/attackby(obj/item/P, mob/living/carbon/human/user, params)
- ..()
+ if(burn_paper_product_attackby_check(P, user))
+ return
if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
- to_chat(user, "You should unfold [src] before changing it.")
+ to_chat(user, "You should unfold [src] before changing it!")
return
else if(istype(P, /obj/item/stamp)) //we don't randomize stamps on a paperplane
internalPaper.attackby(P, user) //spoofed attack to update internal paper.
update_icon()
+ add_fingerprint(user)
+ return
- else if(P.get_temperature())
- if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10))
- user.visible_message("[user] accidentally ignites [user.p_them()]self!", \
- "You miss [src] and accidentally light yourself on fire!")
- user.dropItemToGround(P)
- user.adjust_fire_stacks(1)
- user.IgniteMob()
- return
-
- if(!(in_range(user, src))) //to prevent issues as a result of telepathically lighting a paper
- return
- user.dropItemToGround(src)
- user.visible_message("[user] lights [src] ablaze with [P]!", "You light [src] on fire!")
- fire_act()
-
- add_fingerprint(user)
+ return ..()
/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback)
@@ -112,26 +107,28 @@
if(..() || !ishuman(hit_atom))//if the plane is caught or it hits a nonhuman
return
var/mob/living/carbon/human/H = hit_atom
+ var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
if(prob(hit_probability))
if(H.is_eyes_covered())
return
- var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
- visible_message("\The [src] hits [H] in the eye!")
+ visible_message("\The [src] hits [H] in the eye[eyes ? "" : " socket"]!")
H.adjust_blurriness(6)
- if(eyes)
- eyes.applyOrganDamage(rand(6,8))
+ eyes?.applyOrganDamage(rand(6,8))
H.DefaultCombatKnockdown(40)
H.emote("scream")
-
/obj/item/paper/examine(mob/user)
. = ..()
. += "Alt-click [src] to fold it into a paper plane."
/obj/item/paper/AltClick(mob/living/carbon/user, obj/item/I)
- . = ..()
- if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user), NO_TK))
+ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
+ if(istype(src, /obj/item/paper/carbon))
+ var/obj/item/paper/carbon/Carbon = src
+ if(!Carbon.iscopy && !Carbon.copied)
+ to_chat(user, "Take off the carbon copy first.")
+ return
to_chat(user, "You fold [src] into the shape of a plane!")
user.temporarilyRemoveItemFromInventory(src)
var/obj/item/paperplane/plane_type = /obj/item/paperplane
@@ -142,4 +139,3 @@
I = new plane_type(user, src)
user.put_in_hands(I)
- return TRUE
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 91b8a6719b..13890ae69e 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -16,6 +16,8 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "pen"
item_state = "pen"
+ // inhand_icon_state = "pen"
+ // worn_icon_state = "pen"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_EARS
throwforce = 0
w_class = WEIGHT_CLASS_TINY
@@ -76,6 +78,22 @@
icon_state = "pen-fountain"
font = FOUNTAIN_PEN_FONT
+/obj/item/pen/charcoal
+ name = "charcoal stylus"
+ desc = "It's just a wooden stick with some compressed ash on the end. At least it can write."
+ icon_state = "pen-charcoal"
+ colour = "dimgray"
+ font = CHARCOAL_FONT
+ custom_materials = null
+ grind_results = list(/datum/reagent/ash = 5, /datum/reagent/cellulose = 10)
+
+/datum/crafting_recipe/charcoal_stylus
+ name = "Charcoal Stylus"
+ result = /obj/item/pen/charcoal
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 1, /datum/reagent/ash = 30)
+ time = 30
+ category = CAT_PRIMAL
+
/obj/item/pen/fountain/captain
name = "captain's fountain pen"
desc = "It's an expensive Oak fountain pen. The nib is quite sharp."
@@ -93,6 +111,7 @@
"Black and Silver" = "pen-fountain-b",
"Command Blue" = "pen-fountain-cb"
)
+ embedding = list("embed_chance" = 75)
/obj/item/pen/fountain/captain/Initialize()
. = ..()
@@ -139,20 +158,18 @@
if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE))
return
if(oldname == input)
- to_chat(user, "You changed \the [O.name] to... well... \the [O.name].")
+ to_chat(user, "You changed \the [O.name] to... well... \the [O.name].")
else
O.name = input
- to_chat(user, "\The [oldname] has been successfully been renamed to \the [input].")
+ to_chat(user, "\The [oldname] has been successfully been renamed to \the [input].")
O.renamedByPlayer = TRUE
- log_game("[user] [key_name(user)] has renamed [O] to [input]")
if(penchoice == "Change description")
- var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 2048)
+ var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 100)
if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE))
return
O.desc = input
- to_chat(user, "You have successfully changed \the [O.name]'s description.")
- log_game("[user] [key_name(user)] has changed [O]'s description to to [input]")
+ to_chat(user, "You have successfully changed \the [O.name]'s description.")
/*
* Sleepypens
@@ -181,9 +198,10 @@
*/
/obj/item/pen/edagger
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") //these wont show up if the pen is off
+ // attack_verb_continuous = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") //these won't show up if the pen is off
+ // attack_verb_simple = list("slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_EDGED
var/on = FALSE
- embedding = list(embed_chance = EMBED_CHANCE)
/obj/item/pen/edagger/ComponentInitialize()
. = ..()
@@ -193,29 +211,38 @@
/obj/item/pen/edagger/get_sharpness()
return on * sharpness
+/obj/item/pen/edagger/suicide_act(mob/user)
+ . = BRUTELOSS
+ if(on)
+ user.visible_message("[user] forcefully rams the pen into their mouth!")
+ else
+ user.visible_message("[user] is holding a pen up to their mouth! It looks like [user.p_theyre()] trying to commit suicide!")
+ attack_self(user)
+
/obj/item/pen/edagger/attack_self(mob/living/user)
if(on)
on = FALSE
force = initial(force)
+ throw_speed = initial(throw_speed)
w_class = initial(w_class)
name = initial(name)
hitsound = initial(hitsound)
- embedding = null
+ embedding = list(embed_chance = EMBED_CHANCE)
throwforce = initial(throwforce)
- playsound(user, 'sound/weapons/saberoff.ogg', 5, 1)
+ playsound(user, 'sound/weapons/saberoff.ogg', 5, TRUE)
to_chat(user, "[src] can now be concealed.")
- updateEmbedding()
else
on = TRUE
force = 18
+ throw_speed = 4
w_class = WEIGHT_CLASS_NORMAL
name = "energy dagger"
hitsound = 'sound/weapons/blade1.ogg'
- embedding = list(embed_chance = 100, fall_chance = 0) //rule of cool
+ embedding = list(embed_chance = 100) //rule of cool
throwforce = 35
- playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
+ playsound(user, 'sound/weapons/saberon.ogg', 5, TRUE)
to_chat(user, "[src] is now active.")
- updateEmbedding()
+ updateEmbedding()
update_icon()
/obj/item/pen/edagger/update_icon_state()
@@ -235,6 +262,8 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "digging_pen"
item_state = "pen"
+ // inhand_icon_state = "pen"
+ // worn_icon_state = "pen"
force = 3
w_class = WEIGHT_CLASS_TINY
custom_materials = list(/datum/material/iron=10, /datum/material/diamond=100, /datum/material/titanium = 10)
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 0b1f3bb01d..25a9cf0043 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -1,12 +1,20 @@
-/* Photocopiers!
- * Contains:
- * Photocopier
- * Toner Cartridge
- */
-/*
- * Photocopier
- */
+/// For use with the `color_mode` var. Photos will be printed in greyscale while the var has this value.
+#define PHOTO_GREYSCALE "Greyscale"
+/// For use with the `color_mode` var. Photos will be printed in full color while the var has this value.
+#define PHOTO_COLOR "Color"
+
+/// How much toner is used for making a copy of a paper.
+#define PAPER_TONER_USE 0.125
+/// How much toner is used for making a copy of a photo.
+#define PHOTO_TONER_USE 0.625
+/// How much toner is used for making a copy of a document.
+#define DOCUMENT_TONER_USE 0.75
+/// How much toner is used for making a copy of an ass.
+#define ASS_TONER_USE 0.625
+/// The maximum amount of copies you can make with one press of the copy button.
+#define MAX_COPIES_AT_ONCE 10
+
/obj/machinery/photocopier
name = "photocopier"
desc = "Used to copy important documents and anatomy studies."
@@ -19,183 +27,317 @@
power_channel = EQUIP
max_integrity = 300
integrity_failure = 0.33
- var/obj/item/paper/copy = null //what's in the copier!
- var/obj/item/photo/photocopy = null
- var/obj/item/documents/doccopy = null
- var/copies = 1 //how many copies to print!
- var/toner = 40 //how much toner is left! woooooo~
- var/maxcopies = 10 //how many copies can be copied at once- idea shamelessly stolen from bs12's copier!
- var/greytoggle = "Greyscale"
- var/mob/living/ass //i can't believe i didn't write a stupid-ass comment about this var when i first coded asscopy.
+ /// A reference to an `/obj/item/paper` inside the copier, if one is inserted. Otherwise null.
+ var/obj/item/paper/paper_copy
+ /// A reference to an `/obj/item/photo` inside the copier, if one is inserted. Otherwise null.
+ var/obj/item/photo/photo_copy
+ /// A reference to an `/obj/item/documents` inside the copier, if one is inserted. Otherwise null.
+ var/obj/item/documents/document_copy
+ /// A reference to a mob on top of the photocopier trying to copy their ass. Null if there is no mob.
+ var/mob/living/ass
+ /// A reference to the toner cartridge that's inserted into the copier. Null if there is no cartridge.
+ var/obj/item/toner/toner_cartridge
+ /// How many copies will be printed with one click of the "copy" button.
+ var/num_copies = 1
+ /// Used with photos. Determines if the copied photo will be in greyscale or color.
+ var/color_mode = PHOTO_COLOR
+ /// Indicates whether the printer is currently busy copying or not.
var/busy = FALSE
-/obj/machinery/photocopier/ui_interact(mob/user)
+/obj/machinery/photocopier/Initialize()
. = ..()
- var/list/dat = list("Photocopier
")
- if(copy || photocopy || doccopy || (ass && (ass.loc == src.loc)))
- dat += "Remove Paper "
- if(toner)
- dat += "Copy "
- dat += "Printing: [copies] copies."
- dat += "- "
- dat += "+
"
- if(photocopy)
- dat += "Printing in [greytoggle]
"
- else if(toner)
- dat += "Please insert paper to copy.
")
- popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
-
-/obj/machinery/computer/shuttle/Topic(href, href_list)
- if(..())
+/obj/machinery/computer/shuttle/ui_act(action, params)
+ . = ..()
+ if(.)
return
- usr.set_machine(src)
- src.add_fingerprint(usr)
if(!allowed(usr))
to_chat(usr, "Access denied.")
return
- if(href_list["move"])
- var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
- if(M.launch_status == ENDGAME_LAUNCHED)
- to_chat(usr, "You've already escaped. Never going back to that place again!")
- return
- if(no_destination_swap)
- if(M.mode != SHUTTLE_IDLE)
- to_chat(usr, "Shuttle already in transit.")
+ switch(action)
+ if("move")
+ var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
+ if(M.launch_status == ENDGAME_LAUNCHED)
+ to_chat(usr, "You've already escaped. Never going back to that place again!")
return
- switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1))
- if(0)
- say("Shuttle departing. Please stand away from the doors.")
- log_shuttle("[key_name(usr)] has sent shuttle \"[M]\" towards \"[href_list["move"]]\", using [src].")
- if(1)
- to_chat(usr, "Invalid shuttle requested.")
- else
- to_chat(usr, "Unable to comply.")
+ if(no_destination_swap)
+ if(M.mode == SHUTTLE_RECHARGING)
+ to_chat(usr, "Shuttle engines are not ready for use.")
+ return
+ if(M.mode != SHUTTLE_IDLE)
+ to_chat(usr, "Shuttle already in transit.")
+ return
+ var/list/options = params2list(possible_destinations)
+ if(!(params["shuttle_id"] in options))
+ log_admin("[usr] attempted to href dock exploit on [src] with target location \"[params["shuttle_id"]]\"")
+ message_admins("[usr] just attempted to href dock exploit on [src] with target location \"[params["shuttle_id"]]\"")
+ return
+ switch(SSshuttle.moveShuttle(shuttleId, params["shuttle_id"], 1))
+ if(0)
+ say("Shuttle departing. Please stand away from the doors.")
+ log_shuttle("[key_name(usr)] has sent shuttle \"[M]\" towards \"[params["shuttle_id"]]\", using [src].")
+ return TRUE
+ if(1)
+ to_chat(usr, "Invalid shuttle requested.")
+ else
+ to_chat(usr, "Unable to comply.")
+ if("set_destination")
+ var/target_destination = params["destination"]
+ if(target_destination)
+ destination = target_destination
+ return TRUE
+ if("request")
+ if(!COOLDOWN_FINISHED(src, request_cooldown))
+ to_chat(usr, "CentCom is still processing last authorization request!")
+ return
+ COOLDOWN_START(src, request_cooldown, 1 MINUTES)
+ to_chat(usr, "Your request has been received by CentCom.")
+ to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.")
+ return TRUE
/obj/machinery/computer/shuttle/emag_act(mob/user)
. = ..()
diff --git a/code/modules/shuttle/custom_shuttle.dm b/code/modules/shuttle/custom_shuttle.dm
index c71b6ba9a8..8047e972db 100644
--- a/code/modules/shuttle/custom_shuttle.dm
+++ b/code/modules/shuttle/custom_shuttle.dm
@@ -68,7 +68,6 @@
popup = new(user, "computer", M ? M.name : "shuttle", 350, 450)
popup.set_content("
[dat]
")
- popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/custom_shuttle/Topic(href, href_list)
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 810cadcd2c..15f9b31302 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -528,7 +528,7 @@
/obj/machinery/computer/shuttle/pod
name = "pod control computer"
- admin_controlled = 1
+ admin_controlled = TRUE
possible_destinations = "pod_asteroid"
icon = 'icons/obj/terminals.dmi'
icon_state = "dorm_available"
diff --git a/code/modules/shuttle/ferry.dm b/code/modules/shuttle/ferry.dm
index 39bde5e541..892e135bdf 100644
--- a/code/modules/shuttle/ferry.dm
+++ b/code/modules/shuttle/ferry.dm
@@ -5,7 +5,6 @@
shuttleId = "ferry"
possible_destinations = "ferry_home;ferry_away"
req_access = list(ACCESS_CENT_GENERAL)
-
var/allow_silicons = FALSE
var/allow_emag = FALSE
@@ -24,17 +23,6 @@
/obj/machinery/computer/shuttle/ferry/request
name = "ferry console"
circuit = /obj/item/circuitboard/computer/ferry/request
- var/last_request //prevents spamming admins
- var/cooldown = 600
possible_destinations = "ferry_home;ferry_away"
req_access = list(ACCESS_CENT_GENERAL)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
-
-/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list)
- ..()
- if(href_list["request"])
- if(last_request && (last_request + cooldown > world.time))
- return
- last_request = world.time
- to_chat(usr, "Your request has been received by CentCom.")
- to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.")
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index d7a6d4a583..03ff509086 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -711,6 +711,26 @@
else
return "00:00"
+/**
+ * Gets shuttle location status in a form of string for tgui interfaces
+ */
+/obj/docking_port/mobile/proc/get_status_text_tgui()
+ var/obj/docking_port/stationary/dockedAt = get_docked()
+ var/docked_at = dockedAt?.name || "Unknown"
+ if(istype(dockedAt, /obj/docking_port/stationary/transit))
+ if(timeLeft() > 1 HOURS)
+ return "Hyperspace"
+ else
+ var/obj/docking_port/stationary/dst
+ if(mode == SHUTTLE_RECALL)
+ dst = previous
+ else
+ dst = destination
+ return "In transit to [dst?.name || "unknown location"]"
+ else if(mode == SHUTTLE_RECHARGING)
+ return "[docked_at], recharging [getTimerStr()]"
+ else
+ return docked_at
/obj/docking_port/mobile/proc/getStatusText()
var/obj/docking_port/stationary/dockedAt = get_docked()
@@ -730,7 +750,6 @@
else
return docked_at
-
/obj/docking_port/mobile/proc/getDbgStatusText()
var/obj/docking_port/stationary/dockedAt = get_docked()
. = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown"
diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm
index 0076f584e9..bdbdc08c6f 100644
--- a/code/modules/shuttle/syndicate.dm
+++ b/code/modules/shuttle/syndicate.dm
@@ -12,26 +12,34 @@
possible_destinations = "syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s;syndicate_custom"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
-/obj/machinery/computer/shuttle/syndicate/recall
- name = "syndicate shuttle recall terminal"
- desc = "Use this if your friends left you behind."
- possible_destinations = "syndicate_away"
-
-
-/obj/machinery/computer/shuttle/syndicate/Topic(href, href_list)
- if(href_list["move"])
- var/obj/item/circuitboard/computer/syndicate_shuttle/board = circuit
- if(board.challenge && ((world.time - SSticker.round_start_time) < SYNDICATE_CHALLENGE_TIMER))
- to_chat(usr, "You've issued a combat challenge to the station! You've got to give them at least [DisplayTimeText(SYNDICATE_CHALLENGE_TIMER - (world.time - SSticker.round_start_time))] more to allow them to prepare.")
- return 0
- board.moved = TRUE
- ..()
-
/obj/machinery/computer/shuttle/syndicate/allowed(mob/M)
if(issilicon(M) && !(ROLE_SYNDICATE in M.faction))
return FALSE
return ..()
+/obj/machinery/computer/shuttle/syndicate/ui_act(action, params)
+ if(!allowed(usr))
+ to_chat(usr, "Access denied.")
+ return
+
+ switch(action)
+ if("move")
+ if(istype(src, /obj/machinery/computer/shuttle/syndicate/drop_pod))
+ if(!is_centcom_level(z))
+ to_chat(usr, "Pods are one way!")
+ return
+ var/obj/item/circuitboard/computer/syndicate_shuttle/board = circuit
+ if(board?.challenge && world.time < SYNDICATE_CHALLENGE_TIMER)
+ to_chat(usr, "You've issued a combat challenge to the station! You've got to give them at least [DisplayTimeText(SYNDICATE_CHALLENGE_TIMER - world.time)] more to allow them to prepare.")
+ return
+ board.moved = TRUE
+ return ..()
+
+/obj/machinery/computer/shuttle/syndicate/recall
+ name = "syndicate shuttle recall terminal"
+ desc = "Use this if your friends left you behind."
+ possible_destinations = "syndicate_away"
+
/obj/machinery/computer/shuttle/syndicate/drop_pod
name = "syndicate assault pod control"
desc = "Controls the drop pod's launch system."
@@ -43,13 +51,6 @@
possible_destinations = null
clockwork = TRUE //it'd look weird
-/obj/machinery/computer/shuttle/syndicate/drop_pod/Topic(href, href_list)
- if(href_list["move"])
- if(!is_centcom_level(z))
- to_chat(usr, "Pods are one way!")
- return 0
- ..()
-
/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate
name = "syndicate shuttle navigation computer"
desc = "Used to designate a precise transit location for the syndicate shuttle."
diff --git a/code/modules/smithing/anvil.dm b/code/modules/smithing/anvil.dm
new file mode 100644
index 0000000000..19d48119e6
--- /dev/null
+++ b/code/modules/smithing/anvil.dm
@@ -0,0 +1,315 @@
+#define WORKPIECE_PRESENT 1
+#define WORKPIECE_INPROGRESS 2
+#define WORKPIECE_FINISHED 3
+#define WORKPIECE_SLAG 5
+
+#define RECIPE_SMALLPICK "dbp" //draw bend punch
+#define RECIPE_LARGEPICK "ddbp" //draw draw bend punch
+#define RECIPE_SHOVEL "dfup" //draw fold upset punch
+#define RECIPE_HAMMER "sfp" //shrink fold punch
+
+
+#define RECIPE_SMALLKNIFE "sdd" //shrink draw draw
+#define RECIPE_SHORTSWORD "dff" //draw fold fold
+#define RECIPE_WAKI "dfsf" //draw fold shrink fold
+#define RECIPE_SCIMITAR "dfb" //draw fold bend
+#define RECIPE_SABRE "ddsf" //draw draw shrink fold
+#define RECIPE_RAPIER "sdfd" //shrink draw fold draw
+#define RECIPE_BROADSWORD "dfuf" //draw fold upset fold
+#define RECIPE_ZWEIHANDER "udfsf" //upset draw fold shrink fold
+#define RECIPE_KATANA "fffff" //fold fold fold fold fold
+
+
+#define RECIPE_SCYTHE "bdf" //bend draw fold
+#define RECIPE_COGHEAD "bsf" //bend shrink fold.
+
+
+#define RECIPE_JAVELIN "dbf" //draw bend fold
+#define RECIPE_HALBERD "duffp" //draw upset fold fold punch
+#define RECIPE_GLAIVE "usfp" //upset shrink fold punch
+#define RECIPE_PIKE "ddbf" //draw draw bend fold
+
+/obj/structure/anvil
+ name = "anvil"
+ desc = "Base class of anvil. This shouldn't exist, but is useable."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "anvil"
+ density = TRUE
+ anchored = TRUE
+ var/workpiece_state = FALSE
+ var/datum/material/workpiece_material
+ var/anvilquality = 0
+ var/currentquality = 0 //lolman? what the fuck do these vars do?
+ var/currentsteps = 0 //even i don't know
+ var/outrightfailchance = 1 //todo: document this shit
+ var/stepsdone = ""
+ var/rng = FALSE
+ var/debug = FALSE //vv this if you want an artifact
+ var/artifactrolled = FALSE
+ var/itemqualitymax = 20
+ var/list/smithrecipes = list(RECIPE_HAMMER = /obj/item/smithing/hammerhead,
+ RECIPE_SCYTHE = /obj/item/smithing/scytheblade,
+ RECIPE_SHOVEL = /obj/item/smithing/shovelhead,
+ RECIPE_COGHEAD = /obj/item/smithing/cogheadclubhead,
+ RECIPE_JAVELIN = /obj/item/smithing/javelinhead,
+ RECIPE_LARGEPICK = /obj/item/smithing/pickaxehead,
+ RECIPE_SMALLPICK = /obj/item/smithing/prospectingpickhead,
+ RECIPE_SHORTSWORD = /obj/item/smithing/shortswordblade,
+ RECIPE_SCIMITAR = /obj/item/smithing/scimitarblade,
+ RECIPE_WAKI = /obj/item/smithing/wakiblade,
+ RECIPE_RAPIER = /obj/item/smithing/rapierblade,
+ RECIPE_SABRE = /obj/item/smithing/sabreblade,
+ RECIPE_SMALLKNIFE = /obj/item/smithing/knifeblade,
+ RECIPE_BROADSWORD = /obj/item/smithing/broadblade,
+ RECIPE_ZWEIHANDER = /obj/item/smithing/zweiblade,
+ RECIPE_KATANA = /obj/item/smithing/katanablade,
+ RECIPE_HALBERD = /obj/item/smithing/halberdhead,
+ RECIPE_GLAIVE = /obj/item/smithing/glaivehead,
+ RECIPE_PIKE = /obj/item/smithing/pikehead)
+
+/obj/structure/anvil/Initialize()
+ ..()
+ currentquality = anvilquality
+
+/obj/structure/anvil/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/ingot))
+ var/obj/item/ingot/notsword = I
+ if(workpiece_state)
+ to_chat(user, "There's already a workpiece! Finish it or take it off.")
+ return FALSE
+ if(notsword.workability == "shapeable")
+ workpiece_state = WORKPIECE_PRESENT
+ workpiece_material = notsword.custom_materials
+ to_chat(user, "You place the [notsword] on the [src].")
+ currentquality = anvilquality
+ var/skillmod = 0
+ if(user.mind.skill_holder)
+ skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/2
+ currentquality += skillmod
+ qdel(notsword)
+ else
+ to_chat(user, "The ingot isn't workable yet!")
+ return FALSE
+ return
+ else if(istype(I, /obj/item/melee/smith/hammer))
+ var/obj/item/melee/smith/hammer/hammertime = I
+ if(workpiece_state == WORKPIECE_PRESENT || workpiece_state == WORKPIECE_INPROGRESS)
+ do_shaping(user, hammertime.qualitymod)
+ return
+ else
+ to_chat(user, "You can't work an empty anvil!")
+ return FALSE
+ return ..()
+
+/obj/structure/anvil/wrench_act(mob/living/user, obj/item/I)
+ ..()
+ default_unfasten_wrench(user, I, 5)
+ return TRUE
+
+
+/obj/structure/anvil/proc/do_shaping(mob/user, var/qualitychange)
+ currentquality += qualitychange
+ var/list/shapingsteps = list("weak hit", "strong hit", "heavy hit", "fold", "draw", "shrink", "bend", "punch", "upset") //weak/strong/heavy hit affect strength. All the other steps shape.
+ workpiece_state = WORKPIECE_INPROGRESS
+ var/stepdone = input(user, "How would you like to work the metal?") in shapingsteps
+ var/steptime = 50
+ if(user.mind.skill_holder)
+ var/skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/10 + 1
+ steptime = 50 / skillmod
+ playsound(src, 'sound/effects/clang2.ogg',40, 2)
+ if(!do_after(user, steptime, target = src))
+ return FALSE
+ switch(stepdone)
+ if("weak hit")
+ currentsteps += 1
+ outrightfailchance += 5
+ currentquality += 1
+ if("strong hit")
+ currentsteps += 2
+ outrightfailchance += 9.5
+ currentquality += 2
+ if("heavy hit")
+ currentsteps += 3
+ outrightfailchance += 12.5
+ currentquality += 3
+ if("fold")
+ stepsdone += "f"
+ currentsteps += 1
+ currentquality -= 1
+ if("draw")
+ stepsdone += "d"
+ currentsteps += 1
+ currentquality -= 1
+ if("shrink")
+ stepsdone += "s"
+ currentsteps += 1
+ currentquality -= 1
+ if("bend")
+ stepsdone += "b"
+ currentsteps += 1
+ currentquality -= 1
+ if("punch")
+ stepsdone += "p"
+ currentsteps += 1
+ currentquality -= 1
+ if("upset")
+ stepsdone += "u"
+ currentsteps += 1
+ currentquality -= 1
+ user.visible_message("[user] works the metal on the anvil with their hammer with a loud clang!", \
+ "You [stepdone] the metal with a loud clang!")
+ playsound(src, 'sound/effects/clang2.ogg',40, 2)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/effects/clang2.ogg', 40, 2), 15)
+ if(length(stepsdone) >= 3)
+ tryfinish(user)
+
+/obj/structure/anvil/proc/tryfinish(mob/user)
+ var/artifactchance = 0
+ if(!artifactrolled)
+ artifactchance = (1+(user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/4))/2500
+ artifactrolled = TRUE
+ var/artifact = max(prob(artifactchance), debug)
+ var/finalfailchance = outrightfailchance
+ if(user.mind.skill_holder)
+ var/skillmod = user.mind.get_skill_level(/datum/skill/level/dorfy/blacksmithing)/10 + 1
+ finalfailchance = max(0, finalfailchance / skillmod) //lv 2 gives 20% less to fail, 3 30%, etc
+ if((currentsteps > 10 || (rng && prob(finalfailchance))) && !artifact)
+ to_chat(user, "You overwork the metal, causing it to turn into useless slag!")
+ var/turf/T = get_turf(user)
+ workpiece_state = FALSE
+ new /obj/item/stack/ore/slag(T)
+ currentquality = anvilquality
+ stepsdone = ""
+ currentsteps = 0
+ outrightfailchance = 1
+ artifactrolled = FALSE
+ if(user.mind.skill_holder)
+ user.mind.auto_gain_experience(/datum/skill/level/dorfy/blacksmithing, 25, 400, silent = FALSE)
+ for(var/i in smithrecipes)
+ if(i == stepsdone)
+ var/turf/T = get_turf(user)
+ var/obj/item/smithing/create = smithrecipes[stepsdone]
+ var/obj/item/smithing/finisheditem = new create(T)
+ to_chat(user, "You finish your [finisheditem]!")
+ if(artifact)
+ to_chat(user, "It is an artifact, a creation whose legacy shall live on forevermore.") //todo: SSblackbox
+ currentquality = max(currentquality, 2)
+ finisheditem.quality = currentquality * 3//this is insane i know it's 1/2500 for most of the time and 0.8% at best
+ finisheditem.artifact = TRUE
+ else
+ finisheditem.quality = min(currentquality, itemqualitymax)
+ switch(finisheditem.quality)
+ if(-1000 to -8)
+ finisheditem.desc = "It looks to be the most awfully made object you've ever seen."
+ if(-8)
+ finisheditem.desc = "It looks to be the second most awfully made object you've ever seen."
+ if(-8 to 0)
+ finisheditem.desc = "It looks to be barely passable as... whatever it's trying to pass for."
+ if(0)
+ finisheditem.desc = "It looks to be totally average."
+ if(0 to INFINITY)
+ finisheditem.desc = "It looks to be better than average."
+ workpiece_state = FALSE
+ finisheditem.set_custom_materials(workpiece_material)
+ currentquality = anvilquality
+ stepsdone = ""
+ currentsteps = 0
+ outrightfailchance = 1
+ artifactrolled = FALSE
+ if(user.mind.skill_holder)
+ user.mind.auto_gain_experience(/datum/skill/level/dorfy/blacksmithing, 50, 10000000, silent = FALSE)
+ break
+
+/obj/structure/anvil/debugsuper
+ name = "super ultra epic anvil of debugging."
+ desc = "WOW. A DEBUG ITEM STRUCTURE. EPIC."
+ icon_state = "anvil"
+ anvilquality = 10
+ itemqualitymax = 9001
+ outrightfailchance = 0
+
+/obj/structure/anvil/obtainable
+ name = "anvil"
+ desc = "Base class of anvil. This shouldn't exist, but is useable."
+ anvilquality = 0
+ outrightfailchance = 5
+ rng = TRUE
+
+/obj/structure/anvil/obtainable/table
+ name = "table anvil"
+ desc = "A slightly reinforced table. Good luck."
+ icon_state = "tablevil"
+ anvilquality = -2
+ itemqualitymax = 0
+
+
+/obj/structure/anvil/obtainable/table/do_shaping(mob/user, var/qualitychange)
+ if(prob(5))
+ to_chat(user, "The [src] breaks under the strain!")
+ take_damage(max_integrity)
+ return FALSE
+ else
+ ..()
+
+/obj/structure/anvil/obtainable/bronze
+ name = "slab of bronze"
+ desc = "A big block of bronze. Useable as an anvil."
+ custom_materials = list(/datum/material/bronze=8000)
+ icon_state = "ratvaranvil"
+ anvilquality = -0.5
+ itemqualitymax = 2
+
+/obj/structure/anvil/obtainable/sandstone
+ name = "sandstone brick anvil"
+ desc = "A big block of sandstone. Useable as an anvil."
+ custom_materials = list(/datum/material/sandstone=8000)
+ icon_state = "sandvil"
+ anvilquality = -1
+ itemqualitymax = 2
+
+/obj/structure/anvil/obtainable/basalt
+ name = "basalt brick anvil"
+ desc = "A big block of basalt. Useable as an anvil, better than sandstone. Igneous!"
+ icon_state = "sandvilnoir"
+ anvilquality = -0.5
+ itemqualitymax = 4
+
+/obj/structure/anvil/obtainable/basic
+ name = "anvil"
+ desc = "An anvil. It's got wheels bolted to the bottom."
+ anvilquality = 0
+ itemqualitymax = 6
+
+/obj/structure/anvil/obtainable/ratvar
+ name = "brass anvil"
+ desc = "A big block of what appears to be brass. Useable as an anvil, if whatever's holding the brass together lets you."
+ custom_materials = list(/datum/material/bronze=8000)
+ icon_state = "ratvaranvil"
+ anvilquality = 1
+ itemqualitymax = 8
+
+/obj/structure/anvil/obtainable/ratvar/attackby(obj/item/I, mob/user)
+ if(is_servant_of_ratvar(user))
+ return ..()
+ else
+ to_chat(user, "KNPXWN, QNJCQNW!") //rot13 then rot22 if anyone wants to decode
+
+/obj/structure/anvil/obtainable/narsie
+ name = "runic anvil"
+ desc = "An anvil made of a strange, runic metal."
+ custom_materials = list(/datum/material/runedmetal=8000)
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "evil"
+ anvilquality = 1
+ itemqualitymax = 8
+
+/obj/structure/anvil/obtainable/narsie/attackby(obj/item/I, mob/user)
+ if(iscultist(user))
+ return ..()
+ else
+ to_chat(user, "That is not yours to use!")
+
+#undef WORKPIECE_PRESENT
+#undef WORKPIECE_INPROGRESS
+#undef WORKPIECE_FINISHED
+#undef WORKPIECE_SLAG
diff --git a/code/modules/smithing/finished_items.dm b/code/modules/smithing/finished_items.dm
new file mode 100644
index 0000000000..5db6b09452
--- /dev/null
+++ b/code/modules/smithing/finished_items.dm
@@ -0,0 +1,329 @@
+
+/obj/item/melee/smith
+ name = "base class obj/item/melee/smith" //tin. handles overlay and quality and shit.
+ desc = "cringe"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "claymore"
+ item_state = "claymore"
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON //yeah ok
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_NORMAL
+ force = 6
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ var/quality
+ var/overlay_state = "stick"
+ var/mutable_appearance/overlay
+ var/wielded_mult = 1
+ var/wield_force = 15
+
+/obj/item/melee/smith/Initialize()
+ ..()
+ if(desc == "cringe")
+ desc = "A handmade [name]."
+ overlay = mutable_appearance(icon, overlay_state)
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+
+/obj/item/melee/smith/twohand
+ item_flags = NEEDS_PERMIT //it's a bigass sword/spear. beepsky is going to give you shit for it.
+ sharpness = SHARP_EDGED
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ force = 10
+ wielded_mult = 1.75
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/melee/smith/twohand/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 100, 70) //decent in a pinch, but pretty bad.
+ AddElement(/datum/element/sword_point)
+
+
+
+///////////////////////////
+// Mining //
+///////////////////////////
+/obj/item/mining_scanner/prospector
+ name = "prospector's pickaxe"
+ desc = "A pickaxe that can sound rocks to find mineral deposits and stop gibtonite detonations."
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "minipick" //todo:sprite
+ sharpness = SHARP_POINTY//it doesnt have a blade it has a point
+
+/obj/item/mining_scanner/prospector/Initialize()
+ ..()
+ var/mutable_appearance/overlay
+ desc = "A handmade [name]."
+ overlay = mutable_appearance(icon, "minihandle")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+/obj/item/pickaxe/smithed
+ name = "pickaxe"
+ desc = "A pickaxe."
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "pickaxe"
+ sharpness = SHARP_POINTY
+
+/obj/item/pickaxe/smithed/Initialize()
+ ..()
+ desc = "A handmade [name]."
+ var/mutable_appearance/overlay
+ overlay = mutable_appearance(icon, "stick")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+/obj/item/pickaxe/smithed/attack_self(mob/user)
+ to_chat(user, "Tool does not have a configureable dig range.")
+
+/obj/item/shovel/smithed
+ name = "shovel"
+ desc = "A shovel."
+ icon = 'icons/obj/smith.dmi'
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ icon_state = "shovel"
+ sharpness = SHARP_EDGED //it cuts through the earth
+
+/obj/item/shovel/smithed/Initialize()
+ ..()
+ desc = "A handmade [name]."
+ var/mutable_appearance/overlay
+ overlay = mutable_appearance(icon, "shovelhandle")
+ overlay.appearance_flags = RESET_COLOR
+ add_overlay(overlay)
+ if(force < 0)
+ force = 0
+
+
+///////////////////////////
+// Spears //
+///////////////////////////
+
+
+/obj/item/melee/smith/twohand/halberd
+ name = "halberd"
+ icon_state = "halberd"
+ w_class = WEIGHT_CLASS_HUGE
+ overlay_state = "spearhandle"
+ slot_flags = ITEM_SLOT_BACK
+ wielded_mult = 2.5
+
+/obj/item/melee/smith/twohand/halberd/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+/obj/item/melee/smith/twohand/javelin
+ name = "javelin"
+ icon_state = "javelin"
+ overlay_state = "longhandle"
+ wielded_mult = 1.5
+ slot_flags = ITEM_SLOT_BACK
+ sharpness = SHARP_POINTY
+
+
+/obj/item/melee/smith/twohand/javelin/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+/obj/item/melee/smith/twohand/glaive
+ name = "glaive"
+ icon_state = "glaive"
+ overlay_state = "longhandle"
+ slot_flags = ITEM_SLOT_BACK
+ wielded_mult = 2
+
+/obj/item/melee/smith/twohand/glaive/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+
+/obj/item/melee/smith/twohand/pike
+ name = "pike"
+ icon_state = "pike"
+ w_class = WEIGHT_CLASS_HUGE
+ overlay_state = "longhandle"
+ reach = 2 //yeah ok
+ slot_flags = ITEM_SLOT_BACK
+ sharpness = SHARP_POINTY
+
+//////////////////////////
+// Other Melee //
+///////////////////////////
+
+
+/obj/item/melee/smith/hammer//blacksmithing, not warhammer.
+ name = "hammer"
+ icon_state = "hammer"
+ overlay_state = "hammerhandle"
+ var/qualitymod = 0
+
+/obj/item/scythe/smithed //we need to inherit scythecode, but that's about it.
+ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+
+/obj/item/melee/smith/cogheadclub
+ name = "coghead club"
+ icon_state = "coghead"
+ item_flags = NEEDS_PERMIT
+ overlay_state = "stick"
+
+/obj/item/melee/smith/shortsword
+ name = "gladius"
+ force = 9
+ item_flags = NEEDS_PERMIT
+ sharpness = SHARP_EDGED
+ icon_state = "gladius"
+ overlay_state = "gladiushilt"
+
+/obj/item/melee/smith/shortsword/scimitar
+ name = "scimitar"
+ sharpness = SHARP_EDGED
+ icon_state = "scimitar"
+ overlay_state = "scimitarhilt"
+
+/obj/item/melee/smith/wakizashi
+ name = "wakizashi"
+ sharpness = SHARP_EDGED
+ force = 7
+ item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY
+ obj_flags = UNIQUE_RENAME
+ icon_state = "waki"
+ overlay_state = "wakihilt"
+ block_parry_data = /datum/block_parry_data/waki
+
+/datum/block_parry_data/waki //like longbokken but worse reflect
+ parry_stamina_cost = 6
+ parry_time_windup = 0
+ parry_time_active = 15 //decent window
+ parry_time_spindown = 0
+ parry_time_perfect = 2
+ parry_time_perfect_leeway = 0.75
+ parry_imperfect_falloff_percent = 7.5
+ parry_efficiency_to_counterattack = 100
+ parry_efficiency_considered_successful = 80
+ parry_efficiency_perfect = 120
+ parry_failed_stagger_duration = 3 SECONDS
+ parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1.9)
+
+/obj/item/melee/smith/twohand/broadsword
+ name = "broadsword"
+ icon_state = "broadsword"
+ overlay_state = "broadhilt"
+ wielded_mult = 1.8
+
+/obj/item/melee/smith/twohand/zweihander
+ name = "zweihander"
+ icon_state = "zwei"
+ overlay_state = "zweihilt"
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON * 2
+ force = 4
+ wielded_mult = 3 //affected more by quality. a -1 is 25% less damage, a +1 is 25% more. These bonuses are tripled when wielded.
+
+/obj/item/melee/smith/twohand/katana
+ name = "katana"
+ icon_state = "katana"
+ overlay_state = "katanahilt"
+ force = 7
+ wielded_mult = 2
+ item_flags = ITEM_CAN_PARRY | NEEDS_PERMIT //want to name your katana "DEMON BLADE" or some shit? go ahead, idiot.
+ obj_flags = UNIQUE_RENAME
+ block_parry_data = /datum/block_parry_data/captain_saber //todo
+
+/obj/item/melee/smith/sabre
+ name = "sabre"
+ icon_state = "sabre"
+ sharpness = SHARP_EDGED
+ overlay_state = "sabrehilt"
+ armour_penetration = 15
+ force = 9
+ hitsound = 'sound/weapons/rapierhit.ogg'
+ item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY
+ block_parry_data = /datum/block_parry_data/captain_saber //yeah this is fine i guess
+
+/obj/item/melee/smith/sabre/rapier
+ name = "rapier"
+ icon_state = "rapier"
+ sharpness = SHARP_EDGED
+ overlay_state = "rapierhilt"
+ force = 6 //less force, stronger parry
+ sharpness = SHARP_POINTY
+ armour_penetration = 30
+ block_parry_data = /datum/block_parry_data/smithrapier
+
+/datum/block_parry_data/smithrapier //parry into riposte. i am pretty sure this is going to be nearly fucking impossible to land.
+ parry_stamina_cost = 12 //dont miss
+ parry_time_active = 4
+ parry_time_perfect = 2
+ parry_time_perfect_leeway = 2
+ parry_failed_stagger_duration = 3 SECONDS
+ parry_failed_clickcd_duration = 3 SECONDS
+ parry_time_windup = 0
+ parry_time_spindown = 0
+ parry_imperfect_falloff_percent = 0
+ parry_efficiency_to_counterattack = 100
+ parry_efficiency_considered_successful = 120
+ parry_efficiency_perfect = 120
+ parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 4)
+
+//unique hammers
+/obj/item/melee/smith/hammer/toolbox
+ name = "toolbox hammer"
+ desc = "A metal filled toolbox on a stick. Useable as a really shitty hammer."
+ w_class = WEIGHT_CLASS_BULKY
+ icon_state = "toolbox"
+ overlay_state = "hammerhandle"
+ qualitymod = -2
+
+/obj/item/melee/smith/hammer/narsie
+ name = "runemetal hammer"
+ custom_materials = list(/datum/material/runedmetal = 12000)
+ desc = "A metal hammer inscribed with geometeric runes."
+ qualitymod = 1
+
+/obj/item/melee/smith/hammer/narsie/attack(mob/living/target, mob/living/carbon/human/user)
+ if(!iscultist(user))
+ user.DefaultCombatKnockdown(100)
+ user.dropItemToGround(src, TRUE)
+ user.visible_message("A powerful force shoves [user] away from [target]!", \
+ "\"You shouldn't be touching tools that aren't yours.\"")
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(rand(force/2, force), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ else
+ user.adjustBruteLoss(rand(force/2,force))
+ return
+ ..()
+
+/obj/item/melee/smith/hammer/ratvar
+ name = "brass hammer"
+ custom_materials = list(/datum/material/bronze = 12000)
+ desc = "A brass hammer inscribed with... writing? You can't read it."
+ qualitymod = 1
+
+/obj/item/melee/smith/hammer/ratvar/attack(mob/living/target, mob/living/carbon/human/user)
+ if(!is_servant_of_ratvar(user))
+ user.DefaultCombatKnockdown(100)
+ user.dropItemToGround(src, TRUE)
+ user.visible_message("A powerful force shoves [user] away from [target]!", "\"You shouldn't be touching tools that aren't yours.\"")
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(rand(force/2, force), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ else
+ user.adjustBruteLoss(rand(force/2,force))
+ return
+ ..()
+
+/obj/item/melee/smith/hammer/debug
+ name = "debugging hammer"
+ desc = "A DEBUGGING HAMMER!! EPIC!!."
+ qualitymod = 10
diff --git a/code/modules/smithing/furnace.dm b/code/modules/smithing/furnace.dm
new file mode 100644
index 0000000000..ee1d6a1b93
--- /dev/null
+++ b/code/modules/smithing/furnace.dm
@@ -0,0 +1,75 @@
+/obj/structure/furnace
+ name = "furnace"
+ desc = "A furnace."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "furnace0"
+ density = TRUE
+ anchored = TRUE
+ var/debug = FALSE //debugging only
+ var/working = TRUE
+ var/fueluse = 1
+
+
+/obj/structure/furnace/Initialize()
+ ..()
+ create_reagents(250, TRANSPARENT)
+ START_PROCESSING(SSobj, src)
+
+/obj/structure/furnace/Destroy()
+ ..()
+ STOP_PROCESSING(SSobj, src)
+
+/obj/structure/furnace/process()
+ if(debug)
+ reagents.add_reagent(/datum/reagent/fuel, 1)
+ return TRUE
+ if(reagents.remove_reagent(/datum/reagent/fuel, fueluse))
+ working = TRUE
+ if(icon_state == "furnace0")
+ icon_state = "furnace1"
+ else
+ working = FALSE
+ icon_state = "furnace0"
+
+/obj/structure/furnace/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/ingot))
+ var/obj/item/ingot/notsword = I
+ if(working)
+ to_chat(user, "You heat the [notsword] in the [src].")
+ notsword.workability = "shapeable"
+ else
+ to_chat(user, "The furnace isn't working!.")
+ else
+ ..()
+
+/obj/structure/furnace/wrench_act(mob/living/user, obj/item/I)
+ ..()
+ default_unfasten_wrench(user, I, 5)
+ return TRUE
+
+/obj/structure/furnace/attackby(obj/item/W, mob/user, params)
+ if(W.reagents)
+ W.reagents.trans_to(src, 250)
+ else
+ return ..()
+
+/obj/structure/furnace/plunger_act(obj/item/plunger/P, mob/living/user, reinforced)
+ to_chat(user, "You start furiously plunging [name].")
+ if(do_after(user, 30, target = src))
+ to_chat(user, "You finish plunging the [name].")
+ reagents.reaction(get_turf(src), TOUCH) //splash on the floor
+ reagents.clear_reagents()
+
+/obj/structure/furnace/infinite
+ name = "fuelless furnace"
+ debug = TRUE
+ icon_state = "ratfurnace"
+
+
+/obj/structure/furnace/infinite/ratvar
+ name = "brass furnace"
+ desc = "A brass furnace. Powered by... something, but seems otherwise safe." //todo:sprites they're safe for noncultists because you're just putting ingots in them. also there';s a reason to steal them ig
+
+/obj/structure/furnace/infinite/narsie
+ name = "rune furnace"
+ desc = "A runed furnace. Powered by... something, but seems otherwise safe."
diff --git a/code/modules/smithing/smithed_items.dm b/code/modules/smithing/smithed_items.dm
new file mode 100644
index 0000000000..6d10d33a75
--- /dev/null
+++ b/code/modules/smithing/smithed_items.dm
@@ -0,0 +1,419 @@
+/obj/item/basaltblock
+ name = "basalt block"
+ desc = "A block of basalt."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "sandvilnoir"
+
+
+/obj/item/smithing
+ name = "base class /obj/item/smithing"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "unfinished"
+ material_flags = MATERIAL_COLOR | MATERIAL_ADD_PREFIX
+ var/quality = 0 //quality. Changed by the smithing process.
+ var/obj/item/finishingitem = /obj/item/stick //What this item needs to be hit by to create finalitem
+ var/obj/item/finalitem
+ var/artifact = FALSE
+
+/obj/item/ingot
+ name = "ingot"
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "ingot"
+ material_flags = MATERIAL_COLOR | MATERIAL_ADD_PREFIX
+ var/workability = 0
+
+
+/obj/item/ingot/on_attack_hand(mob/user)
+ var/mob/living/carbon/human/H
+ if(workability != "shapeable")
+ return ..()
+ var/prot = 0
+ if(ishuman(user))
+ H = user
+ if(H.gloves)
+ var/obj/item/clothing/gloves/G = H.gloves
+ if(G.max_heat_protection_temperature)
+ prot = (G.max_heat_protection_temperature > 360)
+ else
+ prot = 1
+ if(prot > 0 || HAS_TRAIT(user, TRAIT_RESISTHEAT) || HAS_TRAIT(user, TRAIT_RESISTHEATHANDS))
+ to_chat(user, "You pick up the [src].")
+ return ..()
+ else
+ to_chat(user, "You try to move the [src], but you burn your hand on it!")
+ if(H)
+ var/obj/item/bodypart/affecting = H.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
+ if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage
+ H.update_damage_overlays()
+
+/obj/item/ingot/iron
+ custom_materials = list(/datum/material/iron=12000)
+
+/obj/item/ingot/diamond
+ custom_materials = list(/datum/material/diamond=12000) //yeah ok
+
+/obj/item/ingot/uranium
+ custom_materials = list(/datum/material/uranium=12000)
+
+/obj/item/ingot/plasma
+ custom_materials = list(/datum/material/plasma=12000)//yeah ok
+
+/obj/item/ingot/gold
+ custom_materials = list(/datum/material/gold=12000)
+
+/obj/item/ingot/silver
+ custom_materials = list(/datum/material/silver=12000)
+
+/obj/item/ingot/bananium
+ custom_materials = list(/datum/material/bananium=12000)
+
+/obj/item/ingot/titanium
+ custom_materials = list(/datum/material/titanium=12000)
+
+/obj/item/ingot/adamantine
+ custom_materials = list(/datum/material/adamantine=12000)
+
+/obj/item/ingot/cult
+ custom_materials = list(/datum/material/runedmetal=12000)
+
+/obj/item/ingot/bronze
+ custom_materials = list(/datum/material/bronze=12000)
+
+/obj/item/ingot/ratvar
+ custom_materials = list(/datum/material/brass=12000)
+ desc = "On closer inspection, what appears to be wholly-unsuitable-for-smithing brass is actually primarily replicant alloy. Nezbere must have switched it while you weren't looking."
+
+
+/obj/item/smithing/attackby(obj/item/I, mob/user)
+ if(istype(I, finishingitem))
+ qdel(I)
+ startfinish()
+ else
+ return ..()
+
+/obj/item/smithing/proc/startfinish()
+ dofinish()
+
+/obj/item/smithing/proc/dofinish()
+ var/qualname
+ switch(quality)
+ if(-1000 to -5)
+ qualname = "awful"
+ if(-1000 to -2)
+ qualname = "shoddy"
+ if(-1000 to -1)
+ qualname = "poor"
+ if(-1 to 1)
+ qualname = "normal"
+ if(10 to INFINITY)
+ qualname = "legendary"
+ if(7.5 to 10)
+ qualname = "masterwork"
+ if(5.5 to 7.5)
+ qualname = "excellent"
+ if(3.5 to 5.5)
+ qualname = "good"
+ if(0 to 3.5)
+ qualname = "above-average"
+ var/datum/material/mat = custom_materials[1]
+ finalitem.set_custom_materials(custom_materials)
+ mat = mat.name
+ if(artifact)
+ dwarfyartifact(finalitem, mat)
+ else
+ finalitem.name = "[qualname] [mat] [initial(finalitem.name)]"
+ finalitem.desc = "A [qualname] [initial(finalitem.name)]. Its quality is [quality]."
+ finalitem.forceMove(get_turf(src))
+ qdel(src)
+
+
+/obj/item/smithing/proc/dwarfyartifact(var/obj/item/finalitem, var/mat)
+ var/finaldesc = "A [initial(finalitem.name)] made of [mat], all craftsmanship is of the highest quality. It "
+ switch(pick(1,2,3,4,5))
+ if(1)
+ finaldesc += "is encrusted with [pick("","synthetic ","multi-faceted ","magical ","sparkling ") + pick("rubies","emeralds","jade","opals","lapiz lazuli")]."
+ if(2)
+ finaldesc += "is laced with studs of [pick("gold","silver","aluminium","titanium")]."
+ if(3)
+ finaldesc += "is encircled with bands of [pick("durasteel","metallic hydrogen","ferritic-alloy","plasteel","duranium")]."
+ if(4)
+ finaldesc += "menaces with spikes of [pick("ytterbium","uranium","white pearl","black steel")]."
+ if(5)
+ finaldesc += "is encrusted with [pick("","synthetic ","multi-faceted ","magical ","sparkling ") + pick("rubies","emeralds","jade","opals","lapis lazuli")],laced with studs of [pick("gold","silver","aluminium","titanium")], encircled with bands of [pick("durasteel","metallic hydrogen","ferritic-alloy","plasteel","duranium")] and menaces with spikes of [pick("ytterbium","uranium","white pearl","black steel")]."
+ finalitem.desc = finaldesc
+ finalitem.name = pick("Delersibnir", "Nekolangrir", "Zanoreshik","Öntakrítin", "Nogzatan", "Vunomam", "Nazushagsaldôbar", "Sergeb", "Zafaldastot", "Vudnis", "Dostust", "Shotom", "Mugshith", "Angzak", "Oltud", "Deleratîs", "Nökornomal") //one of these is literally BLOOD POOL CREATE.iirc its Nazushagsaldôbar.
+
+/obj/item/smithing/hammerhead
+ name = "smithed hammer head"
+ finalitem = /obj/item/melee/smith/hammer
+ icon_state = "hammer"
+
+/obj/item/smithing/hammerhead/startfinish()
+ var/obj/item/melee/smith/hammer/finalforreal = new /obj/item/melee/smith/hammer(src)
+ finalforreal.force += quality/2
+ finalforreal.qualitymod = quality/4
+ finalitem = finalforreal
+ ..()
+
+
+
+/obj/item/smithing/scytheblade
+ name = "smithed scythe head"
+ finalitem = /obj/item/scythe/smithed
+ icon_state = "scythe"
+
+/obj/item/smithing/scytheblade/startfinish()
+ finalitem = new /obj/item/scythe/smithed(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/shovelhead
+ name = "smithed shovel head"
+ finalitem = /obj/item/shovel/smithed
+ icon_state = "shovel"
+
+/obj/item/smithing/shovelhead/startfinish()
+ finalitem = new /obj/item/shovel/smithed(src)
+ finalitem.force += quality/2
+ if(quality > 0)
+ finalitem.toolspeed = max(0.05,(1-(quality/10)))
+ else
+ finalitem.toolspeed *= max(1, (quality * -1))
+ ..()
+
+/obj/item/smithing/cogheadclubhead
+ name = "smithed coghead club head"
+ finalitem = /obj/item/melee/smith/cogheadclub
+ icon_state = "coghead"
+
+/obj/item/smithing/cogheadclubhead/startfinish()
+ finalitem = new /obj/item/melee/smith/cogheadclub(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/javelinhead
+ name = "smithed javelin head"
+ finalitem = /obj/item/melee/smith/twohand/javelin
+ icon_state = "javelin"
+
+/obj/item/smithing/javelinhead/startfinish()
+ var/obj/item/melee/smith/twohand/javelin/finalforreal = new /obj/item/melee/smith/twohand/javelin(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalforreal.throwforce = finalforreal.force*2
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/pikehead
+ name = "smithed pike head"
+ finalitem = /obj/item/melee/smith/twohand/pike
+ icon_state = "pike"
+
+/obj/item/smithing/pikehead/startfinish()
+ var/obj/item/melee/smith/twohand/pike/finalforreal = new /obj/item/melee/smith/twohand/pike(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalforreal.throwforce = finalforreal.force/10 //its a pike not a javelin
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/pickaxehead
+ name = "smithed pickaxe head"
+ finalitem = /obj/item/pickaxe/smithed
+ icon_state = "pickaxe"
+
+/obj/item/smithing/pickaxehead/startfinish()
+ var/obj/item/pickaxe/smithed/finalforreal = new /obj/item/pickaxe/smithed(src)
+ finalforreal.force += quality/2
+ if(quality > 0)
+ finalforreal.toolspeed = max(0.05,(1-(quality/10)))
+ else
+ finalforreal.toolspeed *= max(1, (quality * -1))
+ switch(quality)
+ if(10 to INFINITY)
+ finalforreal.digrange = 4
+ if(5 to 9)
+ finalforreal.digrange = 3
+ if(3,4)
+ finalforreal.digrange = 2
+ else
+ finalforreal.digrange = 1
+ finalitem = finalforreal
+ ..()
+
+
+/obj/item/smithing/prospectingpickhead
+ name = "smithed prospector's pickaxe head"
+ finalitem = /obj/item/mining_scanner/prospector
+ icon_state = "minipick"
+
+/obj/item/smithing/prospectingpickhead/startfinish()
+ var/obj/item/mining_scanner/prospector/finalforreal = new /obj/item/mining_scanner/prospector(src)
+ finalforreal.range = 2 + quality
+ if(quality)
+ finalforreal.cooldown = 100/quality
+ finalitem = finalforreal
+ ..()
+
+
+/obj/item/smithing/shortswordblade
+ name = "smithed gladius blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/shortsword
+ icon_state = "gladius"
+
+/obj/item/smithing/shortswordblade/startfinish()
+ finalitem = new /obj/item/melee/smith/shortsword(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/scimitarblade
+ name = "smithed scimitar blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/shortsword/scimitar
+ icon_state = "scimitar"
+
+/obj/item/smithing/scimitarblade/startfinish()
+ finalitem = new /obj/item/melee/smith/shortsword/scimitar(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/wakiblade
+ name = "smithed wakizashi blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/wakizashi
+ icon_state = "waki"
+
+/obj/item/smithing/wakiblade/startfinish()
+ finalitem = new /obj/item/melee/smith/wakizashi(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/sabreblade
+ name = "smithed sabre blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/sabre
+ icon_state = "sabre"
+
+/obj/item/smithing/sabreblade/startfinish()
+ finalitem = new /obj/item/melee/smith/sabre(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/rapierblade
+ name = "smithed rapier blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/sabre/rapier
+ icon_state = "rapier"
+
+/obj/item/smithing/rapierblade/startfinish()
+ finalitem = new /obj/item/melee/smith/sabre/rapier(src)
+ finalitem.force += quality
+ ..()
+
+/obj/item/smithing/knifeblade
+ name = "smithed knife blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/kitchen/knife
+ icon_state = "dagger"
+
+/obj/item/smithing/knifeblade/startfinish()
+ finalitem = new /obj/item/kitchen/knife(src)
+ finalitem.force = 4 + quality/2
+ finalitem.icon = 'icons/obj/smith.dmi'
+ finalitem.icon_state = "dagger"
+ finalitem.name = "dagger"
+ finalitem.desc = "A dagger."
+ var/mutable_appearance/overlay = mutable_appearance('icons/obj/smith.dmi', "daggerhilt")
+ overlay.appearance_flags = RESET_COLOR
+ finalitem.add_overlay(overlay)
+ if(finalitem.force < 0)
+ finalitem.force = 0
+ finalitem.material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+ ..()
+
+/obj/item/smithing/broadblade
+ name = "smithed broadsword blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/broadsword
+ icon_state = "broadsword"
+
+/obj/item/smithing/broadblade/startfinish()
+ var/obj/item/melee/smith/twohand/broadsword/finalforreal = new /obj/item/melee/smith/twohand/broadsword(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/zweiblade
+ name = "smithed zweihander blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/zweihander
+ icon_state = "zwei"
+
+/obj/item/smithing/zweiblade/startfinish()
+ var/obj/item/melee/smith/twohand/zweihander/finalforreal = new /obj/item/melee/smith/twohand/zweihander(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/halberdhead
+ name = "smithed halberd head"
+ finalitem = /obj/item/melee/smith/twohand/halberd
+ icon_state = "halberd"
+
+/obj/item/smithing/halberdhead/startfinish()
+ var/obj/item/melee/smith/twohand/halberd/finalforreal = new /obj/item/melee/smith/twohand/halberd(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.throwforce = finalforreal.force/3
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/glaivehead
+ name = "smithed glaive head"
+ finalitem = /obj/item/melee/smith/twohand/glaive
+ icon_state = "glaive"
+
+/obj/item/smithing/glaivehead/startfinish()
+ var/obj/item/melee/smith/twohand/glaive/finalforreal = new /obj/item/melee/smith/twohand/glaive(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.throwforce = finalforreal.force
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/smithing/katanablade
+ name = "smithed katana blade"
+ finishingitem = /obj/item/swordhandle
+ finalitem = /obj/item/melee/smith/twohand/katana
+ icon_state = "katana"
+
+
+/obj/item/smithing/katanablade/startfinish()
+ var/obj/item/melee/smith/twohand/katana/finalforreal = new /obj/item/melee/smith/twohand/katana(src)
+ finalforreal.force += quality
+ finalforreal.wield_force = finalforreal.force*finalforreal.wielded_mult
+ finalforreal.AddComponent(/datum/component/two_handed, force_unwielded=finalforreal.force, force_wielded=finalforreal.wield_force, icon_wielded="[icon_state]")
+ finalitem = finalforreal
+ ..()
+
+/obj/item/stick
+ name = "wooden rod"
+ desc = "It's a rod, suitable for use of a handle of a tool. Also could serve as a weapon, in a pinch."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "stick"
+ force = 7
+
+/obj/item/swordhandle
+ name = "sword handle"
+ desc = "It's a crudlely shaped wooden sword hilt."
+ icon = 'icons/obj/smith.dmi'
+ icon_state = "shorthilt"
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 472734b74b..9ebcfe91d6 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -31,6 +31,7 @@
anchored = TRUE
/obj/machinery/bsa/wrench_act(mob/living/user, obj/item/I)
+ ..()
default_unfasten_wrench(user, I, 10)
return TRUE
@@ -142,17 +143,17 @@
/obj/machinery/bsa/full/proc/get_front_turf()
switch(dir)
if(WEST)
- return locate(x - 6,y,z)
+ return locate(x - 7,y,z)
if(EAST)
- return locate(x + 4,y,z)
+ return locate(x + 7,y,z)
return get_turf(src)
/obj/machinery/bsa/full/proc/get_back_turf()
switch(dir)
if(WEST)
- return locate(x + 4,y,z)
+ return locate(x + 5,y,z)
if(EAST)
- return locate(x - 6,y,z)
+ return locate(x - 5,y,z)
return get_turf(src)
/obj/machinery/bsa/full/proc/get_target_turf()
@@ -169,11 +170,12 @@
switch(cannon_direction)
if(WEST)
setDir(WEST)
- pixel_x = -192
top_layer.icon_state = "top_west"
icon_state = "cannon_west"
if(EAST)
setDir(EAST)
+ pixel_x = -128
+ bound_x = -128
top_layer.icon_state = "top_east"
icon_state = "cannon_east"
add_overlay(top_layer)
@@ -211,6 +213,7 @@
message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)] but it was blocked by [blocker] at [ADMIN_VERBOSEJMP(target)].")
log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)] but it was blocked by [blocker] at [AREACOORD(target)].")
+
/obj/machinery/bsa/full/proc/reload()
ready = FALSE
use_power(power_used_per_shot)
@@ -293,15 +296,16 @@
/obj/machinery/computer/bsa_control/proc/get_target_name()
if(istype(target, /area))
return get_area_name(target, TRUE)
- else if(istype(target, /obj/item/gps))
- var/obj/item/gps/G = target
+ else if(istype(target, /datum/component/gps))
+ var/datum/component/gps/G = target
return G.gpstag
/obj/machinery/computer/bsa_control/proc/get_impact_turf()
if(istype(target, /area))
return pick(get_area_turfs(target))
- else if(istype(target, /obj/item/gps))
- return get_turf(target)
+ else if(istype(target, /datum/component/gps))
+ var/datum/component/gps/G = target
+ return get_turf(G.parent)
/obj/machinery/computer/bsa_control/proc/fire(mob/user)
if(cannon.stat)
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 80dc29f905..e3ab44213f 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -280,10 +280,6 @@
var/C = input(owner, "Select Color", "Select color", "#ffffff") as color|null
if(!C || QDELETED(src) || QDELETED(user) || QDELETED(owner) || owner != user)
return
- var/list/hsv = ReadHSV(RGBtoHSV(C))
- if(hsv[2] > 125)
- to_chat(user, "A color that saturated? Surely not!")
- return
var/range = input(user, "Enter range (0 - [max_light_beam_distance])", "Range Select", 0) as null|num
if(!isnum(range))
return
@@ -291,16 +287,26 @@
set_distance(clamp(range, 0, max_light_beam_distance))
assume_rgb(C)
+#define MAX_SATURATION 192
+#define MAX_LIGHTNESS 256
+
/obj/item/organ/eyes/robotic/glow/proc/assume_rgb(newcolor)
- current_color_string = newcolor
- var/current_color = RGB2EYECOLORSTRING(current_color_string)
+ var/current_color = RGB2EYECOLORSTRING(newcolor)
left_eye_color = current_color
right_eye_color = current_color
+ var/list/hsv = ReadHSV(RGBtoHSV(newcolor))
+ hsv[2] = clamp(hsv[2], 0, MAX_SATURATION)
+ hsv[3] = clamp(hsv[3], 0, MAX_LIGHTNESS)
+ var/new_hsv = hsv(hsv[1], hsv[2], hsv[3])
+ current_color_string = HSVtoRGB(new_hsv)
sync_light_effects()
cycle_mob_overlay()
if(!QDELETED(owner) && ishuman(owner)) //Other carbon mobs don't have eye color.
owner.dna.species.handle_body(owner)
+#undef MAX_SATURATION
+#undef MAX_LIGHTNESS
+
/obj/item/organ/eyes/robotic/glow/proc/cycle_mob_overlay()
remove_mob_overlay()
mob_overlay.color = current_color_string
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 7090ab62e2..929b42f96d 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -11,7 +11,8 @@
var/say_mod = null
var/taste_sensitivity = 15 // lower is more sensitive.
maxHealth = TONGUE_MAX_HEALTH
- var/modifies_speech = FALSE
+ var/list/initial_accents //the ones the tongue starts with, not what it currently has
+ var/list/accents = list() //done in order of priority (please always apply abductor accent and stuttering last)
var/static/list/languages_possible_base = typecacheof(list(
/datum/language/common,
/datum/language/draconic,
@@ -30,6 +31,8 @@
/obj/item/organ/tongue/Initialize(mapload)
. = ..()
+ for(var/accent in initial_accents)
+ accents += new accent
low_threshold_passed = "Your [name] feels a little sore."
low_threshold_cleared = "Your [name] soreness has subsided."
high_threshold_passed = "Your [name] is really starting to hurt."
@@ -38,12 +41,13 @@
now_fixed = "The excruciating pain of your [name] has subsided."
languages_possible = languages_possible_base
-/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args)
- return
+/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) //this wont proc unless there's initial_accents on the tongue
+ for(var/datum/accent/speech_modifier in accents)
+ speech_args = speech_modifier.modify_speech(speech_args, source, owner)
/obj/item/organ/tongue/applyOrganDamage(d, maximum = maxHealth)
. = ..()
- if (damage >= maxHealth)
+ if(damage >= maxHealth)
to_chat(owner, "Your tongue is singed beyond recognition, and disintegrates!")
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Tongues lost to Fermi")
qdel(src)
@@ -52,7 +56,7 @@
..()
if(say_mod && M.dna && M.dna.species)
M.dna.species.say_mod = say_mod
- if (modifies_speech)
+ if(length(initial_accents) || length(accents))
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
M.UnregisterSignal(M, COMSIG_MOB_SAY)
@@ -74,16 +78,7 @@
say_mod = "hisses"
taste_sensitivity = 10 // combined nose + tongue, extra sensitive
maxHealth = 40 //extra sensitivity means tongue is more susceptible to damage
- modifies_speech = TRUE
-
-/obj/item/organ/tongue/lizard/handle_speech(datum/source, list/speech_args)
- var/static/regex/lizard_hiss = new("s+", "g")
- var/static/regex/lizard_hiSS = new("S+", "g")
- var/message = speech_args[SPEECH_MESSAGE]
- if(message[1] != "*")
- message = lizard_hiss.Replace(message, "sss")
- message = lizard_hiSS.Replace(message, "SSS")
- speech_args[SPEECH_MESSAGE] = message
+ initial_accents = list(/datum/accent/lizard)
/obj/item/organ/tongue/fly
name = "proboscis"
@@ -92,16 +87,7 @@
say_mod = "buzzes"
taste_sensitivity = 25 // you eat vomit, this is a mercy
maxHealth = 80 //years of eatting trash has made your tongue strong
- modifies_speech = TRUE
-
-/obj/item/organ/tongue/fly/handle_speech(datum/source, list/speech_args)
- var/static/regex/fly_buzz = new("z+", "g")
- var/static/regex/fly_buZZ = new("Z+", "g")
- var/message = speech_args[SPEECH_MESSAGE]
- if(message[1] != "*")
- message = fly_buzz.Replace(message, "zzz")
- message = fly_buZZ.Replace(message, "ZZZ")
- speech_args[SPEECH_MESSAGE] = message
+ initial_accents = list(/datum/accent/fly)
/obj/item/organ/tongue/abductor
name = "superlingual matrix"
@@ -110,7 +96,7 @@
say_mod = "gibbers"
taste_sensitivity = 101 // ayys cannot taste anything.
maxHealth = 120 //Ayys probe a lot
- modifies_speech = TRUE
+ initial_accents = list(/datum/accent/abductor)
var/mothership
/obj/item/organ/tongue/abductor/attack_self(mob/living/carbon/human/H)
@@ -138,26 +124,6 @@
else
. += "It is attuned to [mothership]."
-/obj/item/organ/tongue/abductor/handle_speech(datum/source, list/speech_args)
- //Hacks
- var/message = speech_args[SPEECH_MESSAGE]
- var/mob/living/carbon/human/user = source
- var/rendered = "[user.name]: [message]"
- user.log_talk(message, LOG_SAY, tag="abductor")
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE)
- if(!T || T.type != type)
- continue
- if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor")
- var/datum/antagonist/abductor/A = user.mind.has_antag_datum(/datum/antagonist/abductor)
- if(!A || !(H.mind in A.team.members))
- continue
- to_chat(H, rendered)
- for(var/mob/M in GLOB.dead_mob_list)
- var/link = FOLLOW_LINK(M, user)
- to_chat(M, "[link] [rendered]")
- speech_args[SPEECH_MESSAGE] = ""
-
/obj/item/organ/tongue/zombie
name = "rotting tongue"
desc = "Between the decay and the fact that it's just lying there you doubt a tongue has ever seemed less sexy."
@@ -165,23 +131,7 @@
say_mod = "moans"
taste_sensitivity = 32
maxHealth = 65 //Stop! It's already dead...!
- modifies_speech = TRUE
-
-/obj/item/organ/tongue/zombie/handle_speech(datum/source, list/speech_args)
- var/list/message_list = splittext(speech_args[SPEECH_MESSAGE], " ")
- var/maxchanges = max(round(message_list.len / 1.5), 2)
-
- for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--)
- var/insertpos = rand(1, message_list.len - 1)
- var/inserttext = message_list[insertpos]
-
- if(!(copytext(inserttext, -3) == "..."))//3 == length("...")
- message_list[insertpos] = inserttext + "..."
-
- if(prob(20) && message_list.len > 3)
- message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...")
-
- speech_args[SPEECH_MESSAGE] = jointext(message_list, " ")
+ initial_accents = list(/datum/accent/zombie)
/obj/item/organ/tongue/alien
name = "alien tongue"
@@ -190,7 +140,7 @@
say_mod = "hisses"
taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED
maxHealth = 500 //They've a little mouth for a tongue, so it's pretty rhobust
- modifies_speech = TRUE // not really, they just hiss
+ initial_accents = list(/datum/accent/alien)
var/static/list/languages_possible_alien = typecacheof(list(
/datum/language/xenocommon,
/datum/language/common,
@@ -202,9 +152,6 @@
. = ..()
languages_possible = languages_possible_alien
-/obj/item/organ/tongue/alien/handle_speech(datum/source, list/speech_args)
- playsound(owner, "hiss", 25, 1, 1)
-
/obj/item/organ/tongue/bone
name = "bone \"tongue\""
desc = "Apparently skeletons alter the sounds they produce through oscillation of their teeth, hence their characteristic rattling."
@@ -214,14 +161,13 @@
attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned")
taste_sensitivity = 101 // skeletons cannot taste anything
maxHealth = 75 //Take brute damage instead
- modifies_speech = TRUE
var/chattering = FALSE
var/phomeme_type = "sans"
- var/list/phomeme_types = list("sans", "papyrus")
+ var/list/phomeme_types = list(/datum/accent/span/sans, /datum/accent/span/papyrus)
/obj/item/organ/tongue/bone/Initialize()
+ initial_accents += pick(phomeme_types)
. = ..()
- phomeme_type = pick(phomeme_types)
/obj/item/organ/tongue/bone/applyOrganDamage(var/d, var/maximum = maxHealth)
if(d < 0)
@@ -234,19 +180,14 @@
return
/obj/item/organ/tongue/bone/handle_speech(datum/source, list/speech_args)
- if (chattering)
+ if(chattering)
chatter(speech_args[SPEECH_MESSAGE], phomeme_type, source)
- switch(phomeme_type)
- if("sans")
- speech_args[SPEECH_SPANS] |= SPAN_SANS
- if("papyrus")
- speech_args[SPEECH_SPANS] |= SPAN_PAPYRUS
+ ..()
/obj/item/organ/tongue/bone/plasmaman
name = "plasma bone \"tongue\""
desc = "Like animated skeletons, Plasmamen vibrate their teeth in order to produce speech."
icon_state = "tongueplasma"
- modifies_speech = FALSE
/obj/item/organ/tongue/robot
name = "robotic voicebox"
@@ -256,7 +197,7 @@
icon_state = "tonguerobot"
say_mod = "states"
attack_verb = list("beeped", "booped")
- modifies_speech = TRUE
+ initial_accents = list(/datum/accent/span/robot)
taste_sensitivity = 25 // not as good as an organic tongue
maxHealth = 100 //RoboTongue!
var/electronics_magic = TRUE
@@ -264,34 +205,19 @@
/obj/item/organ/tongue/robot/could_speak_language(language)
return ..() || electronics_magic
-/obj/item/organ/tongue/robot/handle_speech(datum/source, list/speech_args)
- speech_args[SPEECH_SPANS] |= SPAN_ROBOT
-
/obj/item/organ/tongue/fluffy
name = "fluffy tongue"
desc = "OwO what's this?"
icon_state = "tonguefluffy"
taste_sensitivity = 10 // extra sensitive and inquisitive uwu
maxHealth = 35 //Sensitive tongue!
- modifies_speech = TRUE
-
-/obj/item/organ/tongue/fluffy/handle_speech(datum/source, list/speech_args)
- var/message = speech_args[SPEECH_MESSAGE]
- if(message[1] != "*")
- message = replacetext(message, "ne", "nye")
- message = replacetext(message, "nu", "nyu")
- message = replacetext(message, "na", "nya")
- message = replacetext(message, "no", "nyo")
- message = replacetext(message, "ove", "uv")
- message = replacetext(message, "l", "w")
- message = replacetext(message, "r", "w")
- message = lowertext(message)
- speech_args[SPEECH_MESSAGE] = message
+ initial_accents = list(/datum/accent/fluffy)
/obj/item/organ/tongue/cybernetic
name = "cybernetic tongue"
desc = "A state of the art robotic tongue that can detect the pH of anything drank."
icon_state = "tonguecybernetic"
+ initial_accents = list(/datum/accent/span/robot)
taste_sensitivity = 10
maxHealth = 60 //It's robotic!
organ_flags = ORGAN_SYNTHETIC
@@ -303,9 +229,6 @@
var/errormessage = list("Runtime in tongue.dm, line 39: Undefined operation \"zapzap ow my tongue\"", "afhsjifksahgjkaslfhashfjsak", "-1.#IND", "Graham's number", "inside you all along", "awaiting at least 1 approving review before merging this taste request")
owner.say("The pH is appropriately [pick(errormessage)].", forced = "EMPed synthetic tongue")
-/obj/item/organ/tongue/cybernetic/handle_speech(datum/source, list/speech_args)
- speech_args[SPEECH_SPANS] |= SPAN_ROBOT
-
/obj/item/organ/tongue/robot/ipc
name = "positronic voicebox"
say_mod = "beeps"
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index c4ded65e9c..0f1004221c 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -10,6 +10,12 @@
tool_behaviour = TOOL_RETRACTOR
toolspeed = 1
+/obj/item/retractor/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/retractor/advanced
name = "mechanical pinches"
desc = "An agglomerate of rods and gears."
@@ -55,6 +61,12 @@
tool_behaviour = TOOL_HEMOSTAT
toolspeed = 1
+/obj/item/hemostat/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/hemostat/augment
name = "hemostat"
desc = "Tiny servos power a pair of pincers to stop bleeding."
@@ -79,6 +91,12 @@
tool_behaviour = TOOL_CAUTERY
toolspeed = 1
+/obj/item/cautery/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/cautery/augment
name = "cautery"
desc = "A heated element that cauterizes wounds."
@@ -107,6 +125,12 @@
tool_behaviour = TOOL_DRILL
toolspeed = 1
+/obj/item/surgicaldrill/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/surgicaldrill/advanced
name = "searing tool"
desc = "It projects a high power laser used for medical application."
@@ -172,6 +196,12 @@
. = ..()
AddComponent(/datum/component/butchering, 80 * toolspeed, 100, 0)
+/obj/item/scalpel/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
+
/obj/item/scalpel/advanced
name = "laser scalpel"
desc = "An advanced scalpel which uses laser technology to cut."
@@ -255,6 +285,11 @@
. = ..()
AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering
+/obj/item/circular_saw/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
/obj/item/circular_saw/augment
name = "circular saw"
@@ -392,3 +427,9 @@
attack_verb = list("corrected", "properly set")
tool_behaviour = TOOL_BONESET
toolspeed = 1
+
+/obj/item/bonesetter/attack(mob/living/L, mob/user)
+ if(user.a_intent == INTENT_HELP)
+ to_chat(user, "You refrain from hitting [L] with [src], as you are in help intent.")
+ return
+ return ..()
diff --git a/code/modules/tgchat/message.dm b/code/modules/tgchat/message.dm
new file mode 100644
index 0000000000..2e9bfe784e
--- /dev/null
+++ b/code/modules/tgchat/message.dm
@@ -0,0 +1,17 @@
+/**
+ * Message-related procs
+ *
+ * Message format (/list):
+ * - type - Message type, must be one of defines in `code/__DEFINES/chat.dm`
+ * - text - Plain message text
+ * - html - HTML message text
+ * - Optional metadata, can be any key/value pair.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/proc/message_to_html(message)
+ // Here it is possible to add a switch statement
+ // to custom-handle various message types.
+ return message["html"] || message["text"]
diff --git a/code/modules/tgchat/to_chat.dm b/code/modules/tgchat/to_chat.dm
new file mode 100644
index 0000000000..a50bf4595e
--- /dev/null
+++ b/code/modules/tgchat/to_chat.dm
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/**
+ * Circumvents the message queue and sends the message
+ * to the recipient (target) as soon as possible.
+ */
+/proc/to_chat_immediate(target, html,
+ type = null,
+ text = null,
+ avoid_highlighting = FALSE,
+ // FIXME: These flags are now pointless and have no effect
+ handle_whitespace = TRUE,
+ trailing_newline = TRUE,
+ confidential = FALSE)
+ if(!target || (!html && !text))
+ return
+ if(target == world)
+ target = GLOB.clients
+ // Build a message
+ var/message = list()
+ if(type) message["type"] = type
+ if(text) message["text"] = text
+ if(html) message["html"] = html
+ if(avoid_highlighting) message["avoidHighlighting"] = avoid_highlighting
+ var/message_blob = TGUI_CREATE_MESSAGE("chat/message", message)
+ var/message_html = message_to_html(message)
+ if(islist(target))
+ for(var/_target in target)
+ var/client/client = CLIENT_FROM_VAR(_target)
+ if(client)
+ // Send to tgchat
+ client.tgui_panel?.window.send_raw_message(message_blob)
+ // Send to old chat
+ SEND_TEXT(client, message_html)
+ return
+ var/client/client = CLIENT_FROM_VAR(target)
+ if(client)
+ // Send to tgchat
+ client.tgui_panel?.window.send_raw_message(message_blob)
+ // Send to old chat
+ SEND_TEXT(client, message_html)
+
+/**
+ * Sends the message to the recipient (target).
+ *
+ * Recommended way to write to_chat calls:
+ * to_chat(client,
+ * type = MESSAGE_TYPE_INFO,
+ * html = "You have found [object]")
+ */
+/proc/to_chat(target, html,
+ type = null,
+ text = null,
+ avoid_highlighting = FALSE,
+ // FIXME: These flags are now pointless and have no effect
+ handle_whitespace = TRUE,
+ trailing_newline = TRUE,
+ confidential = FALSE)
+ if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
+ to_chat_immediate(target, html, type, text)
+ return
+ if(!target || (!html && !text))
+ return
+ if(target == world)
+ target = GLOB.clients
+ // Build a message
+ var/message = list()
+ if(type) message["type"] = type
+ if(text) message["text"] = text
+ if(html) message["html"] = html
+ if(avoid_highlighting) message["avoidHighlighting"] = avoid_highlighting
+ SSchat.queue(target, message)
diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm
index 46b324e151..8f47c609b2 100644
--- a/code/modules/tgui/external.dm
+++ b/code/modules/tgui/external.dm
@@ -130,6 +130,13 @@
*/
/client/var/list/tgui_windows = list()
+/**
+ * global
+ *
+ * TRUE if cache was reloaded by tgui dev server at least once.
+ */
+/client/var/tgui_cache_reloaded = FALSE
+
/**
* public
*
@@ -159,25 +166,44 @@
/**
* Middleware for /client/Topic.
*
- * return bool Whether the topic is passed (TRUE), or cancelled (FALSE).
+ * return bool If TRUE, prevents propagation of the topic call.
*/
/proc/tgui_Topic(href_list)
// Skip non-tgui topics
if(!href_list["tgui"])
- return TRUE
+ return FALSE
var/type = href_list["type"]
// Unconditionally collect tgui logs
if(type == "log")
- log_tgui(usr, href_list["message"])
+ var/context = href_list["window_id"]
+ if (href_list["ns"])
+ context += " ([href_list["ns"]])"
+ log_tgui(usr, href_list["message"],
+ context = context)
+ // Reload all tgui windows
+ if(type == "cacheReloaded")
+ if(!check_rights(R_ADMIN) || usr.client.tgui_cache_reloaded)
+ return TRUE
+ // Mark as reloaded
+ usr.client.tgui_cache_reloaded = TRUE
+ // Notify windows
+ var/list/windows = usr.client.tgui_windows
+ for(var/window_id in windows)
+ var/datum/tgui_window/window = windows[window_id]
+ if (window.status == TGUI_WINDOW_READY)
+ window.on_message(type, null, href_list)
+ return TRUE
// Locate window
var/window_id = href_list["window_id"]
var/datum/tgui_window/window
if(window_id)
window = usr.client.tgui_windows[window_id]
if(!window)
- log_tgui(usr, "Error: Couldn't find the window datum, force closing.")
+ log_tgui(usr,
+ "Error: Couldn't find the window datum, force closing.",
+ context = window_id)
SStgui.force_close_window(usr, window_id)
- return FALSE
+ return TRUE
// Decode payload
var/payload
if(href_list["payload"])
@@ -185,4 +211,4 @@
// Pass message to window
if(window)
window.on_message(type, payload, href_list)
- return FALSE
+ return TRUE
diff --git a/code/modules/tgui/states/debug.dm b/code/modules/tgui/states/debug.dm
new file mode 100644
index 0000000000..6c600b38ce
--- /dev/null
+++ b/code/modules/tgui/states/debug.dm
@@ -0,0 +1,6 @@
+GLOBAL_DATUM_INIT(debug_state, /datum/ui_state/debug_state, new)
+
+/datum/ui_state/debug_state/can_use_topic(src_object, mob/user)
+ if(check_rights_for(user.client, R_DEBUG))
+ return UI_INTERACTIVE
+ return UI_CLOSE
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index d0d5ff8ebb..9393b9641f 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -49,7 +49,9 @@
* return datum/tgui The requested UI.
*/
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
- log_tgui(user, "new [interface] fancy [user.client.prefs.tgui_fancy]")
+ log_tgui(user,
+ "new [interface] fancy [user.client.prefs.tgui_fancy]",
+ src_object = src_object)
src.user = user
src.src_object = src_object
src.window_key = "[REF(src_object)]-main"
@@ -80,14 +82,20 @@
opened_at = world.time
window.acquire_lock(src)
if(!window.is_ready())
- window.initialize(inline_assets = list(
- get_asset_datum(/datum/asset/simple/tgui),
- ))
+ window.initialize(
+ fancy = user.client.prefs.tgui_fancy,
+ inline_assets = list(
+ get_asset_datum(/datum/asset/simple/tgui_common),
+ get_asset_datum(/datum/asset/simple/tgui),
+ ))
else
window.send_message("ping")
- window.send_asset(get_asset_datum(/datum/asset/simple/fontawesome))
+ var/flush_queue = window.send_asset(get_asset_datum(
+ /datum/asset/simple/namespaced/fontawesome))
for(var/datum/asset/asset in src_object.ui_assets(user))
- window.send_asset(asset)
+ flush_queue |= window.send_asset(asset)
+ if (flush_queue)
+ user.client.browse_queue_flush()
window.send_message("update", get_payload(
with_data = TRUE,
with_static_data = TRUE))
@@ -143,11 +151,13 @@
* Makes an asset available to use in tgui.
*
* required asset datum/asset
+ *
+ * return bool - true if an asset was actually sent
*/
/datum/tgui/proc/send_asset(datum/asset/asset)
if(!window)
CRASH("send_asset() can only be called after open().")
- window.send_asset(asset)
+ return window.send_asset(asset)
/**
* public
@@ -199,13 +209,17 @@
"key" = window_key,
"size" = window_size,
"fancy" = user.client.prefs.tgui_fancy,
- "locked" = user.client.prefs.tgui_lock
+ "locked" = user.client.prefs.tgui_lock,
+ ),
+ "client" = list(
+ "ckey" = user.client.ckey,
+ "address" = user.client.address,
+ "computer_id" = user.client.computer_id,
),
"user" = list(
"name" = "[user]",
- "ckey" = "[user.ckey]",
- "observer" = isobserver(user)
- )
+ "observer" = isobserver(user),
+ ),
)
var/data = custom_data || with_data && src_object.ui_data(user)
if(data)
@@ -233,11 +247,9 @@
return
// Validate ping
if(!initialized && world.time - opened_at > TGUI_PING_TIMEOUT)
- log_tgui(user, \
- "Error: Zombie window detected, killing it with fire.\n" \
- + "window_id: [window.id]\n" \
- + "opened_at: [opened_at]\n" \
- + "world.time: [world.time]")
+ log_tgui(user, "Error: Zombie window detected, closing.",
+ window = window,
+ src_object = src_object)
close(can_be_suspended = FALSE)
return
// Update through a normal call to ui_interact
@@ -270,8 +282,12 @@
/datum/tgui/proc/on_message(type, list/payload, list/href_list)
// Pass act type messages to ui_act
if(type && copytext(type, 1, 5) == "act/")
+ var/act_type = copytext(type, 5)
+ log_tgui(user, "Action: [act_type] [href_list["payload"]]",
+ window = window,
+ src_object = src_object)
process_status()
- if(src_object.ui_act(copytext(type, 5), payload, src, state))
+ if(src_object.ui_act(act_type, payload, src, state))
SStgui.update_uis(src_object)
return FALSE
switch(type)
diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm
index 3f271163c9..23583e8b53 100644
--- a/code/modules/tgui/tgui_window.dm
+++ b/code/modules/tgui/tgui_window.dm
@@ -8,12 +8,18 @@
var/client/client
var/pooled
var/pool_index
+ var/is_browser = FALSE
var/status = TGUI_WINDOW_CLOSED
var/locked = FALSE
var/datum/tgui/locked_by
+ var/datum/subscriber_object
+ var/subscriber_delegate
var/fatally_errored = FALSE
var/message_queue
var/sent_assets = list()
+ // Vars passed to initialize proc (and saved for later)
+ var/inline_assets
+ var/fancy
/**
* public
@@ -26,9 +32,9 @@
/datum/tgui_window/New(client/client, id, pooled = FALSE)
src.id = id
src.client = client
+ src.client.tgui_windows[id] = src
src.pooled = pooled
if(pooled)
- client.tgui_windows[id] = src
src.pool_index = TGUI_WINDOW_INDEX(id)
/**
@@ -39,18 +45,26 @@
* will be put into the queue until the window finishes loading.
*
* optional inline_assets list List of assets to inline into the html.
+ * optional inline_html string Custom HTML to inject.
+ * optional fancy bool If TRUE, will hide the window titlebar.
*/
-/datum/tgui_window/proc/initialize(inline_assets = list())
- log_tgui(client, "[id]/initialize")
+/datum/tgui_window/proc/initialize(
+ inline_assets = list(),
+ inline_html = "",
+ fancy = FALSE)
+ log_tgui(client,
+ context = "[id]/initialize",
+ window = src)
if(!client)
return
+ src.inline_assets = inline_assets
+ src.fancy = fancy
status = TGUI_WINDOW_LOADING
fatally_errored = FALSE
- message_queue = null
// Build window options
var/options = "file=[id].html;can_minimize=0;auto_format=0;"
// Remove titlebar and resize handles for a fancy window
- if(client.prefs.tgui_fancy)
+ if(fancy)
options += "titlebar=0;can_resize=0;"
else
options += "titlebar=1;can_resize=1;"
@@ -69,13 +83,17 @@
inline_styles += "\n"
else if(copytext(name, -3) == ".js")
inline_scripts += "\n"
- asset.send()
+ asset.send(client)
html = replacetextEx(html, "\n", inline_styles)
html = replacetextEx(html, "\n", inline_scripts)
+ // Inject custom HTML
+ html = replacetextEx(html, "\n", inline_html)
// Open the window
client << browse(html, "window=[id];[options]")
// Instruct the client to signal UI when the window is closed.
winset(client, id, "on-close=\"uiclose [id]\"")
+ // Detect whether the control is a browser
+ is_browser = winexists(client, id) == "BROWSER"
/**
* public
@@ -107,8 +125,8 @@
* Acquire the window lock. Pool will not be able to provide this window
* to other UIs for the duration of the lock.
*
- * Can be given an optional tgui datum, which will hook its on_message
- * callback into the message stream.
+ * Can be given an optional tgui datum, which will be automatically
+ * subscribed to incoming messages via the on_message proc.
*
* optional ui /datum/tgui
*/
@@ -117,6 +135,8 @@
locked_by = ui
/**
+ * public
+ *
* Release the window lock.
*/
/datum/tgui_window/proc/release_lock()
@@ -126,6 +146,28 @@
locked = FALSE
locked_by = null
+/**
+ * public
+ *
+ * Subscribes the datum to consume window messages on a specified proc.
+ *
+ * Note, that this supports only one subscriber, because code for that
+ * is simpler and therefore faster. If necessary, this can be rewritten
+ * to support multiple subscribers.
+ */
+/datum/tgui_window/proc/subscribe(datum/object, delegate)
+ subscriber_object = object
+ subscriber_delegate = delegate
+
+/**
+ * public
+ *
+ * Unsubscribes the datum. Do not forget to call this when cleaning up.
+ */
+/datum/tgui_window/proc/unsubscribe(datum/object)
+ subscriber_object = null
+ subscriber_delegate = null
+
/**
* public
*
@@ -137,11 +179,15 @@
if(!client)
return
if(can_be_suspended && can_be_suspended())
- log_tgui(client, "[id]/close: suspending")
+ log_tgui(client,
+ context = "[id]/close (suspending)",
+ window = src)
status = TGUI_WINDOW_READY
send_message("suspend")
return
- log_tgui(client, "[id]/close")
+ log_tgui(client,
+ context = "[id]/close",
+ window = src)
release_lock()
status = TGUI_WINDOW_CLOSED
message_queue = null
@@ -159,25 +205,40 @@
* required payload list Message payload
* optional force bool Send regardless of the ready status.
*/
-/datum/tgui_window/proc/send_message(type, list/payload, force)
+/datum/tgui_window/proc/send_message(type, payload, force)
if(!client)
return
- var/message = json_encode(list(
- "type" = type,
- "payload" = payload,
- ))
- // Strip #255/improper.
- message = replacetext(message, "\proper", "")
- message = replacetext(message, "\improper", "")
- // Pack for sending via output()
- message = url_encode(message)
+ var/message = TGUI_CREATE_MESSAGE(type, payload)
// Place into queue if window is still loading
if(!force && status != TGUI_WINDOW_READY)
if(!message_queue)
message_queue = list()
message_queue += list(message)
return
- client << output(message, "[id].browser:update")
+ client << output(message, is_browser \
+ ? "[id]:update" \
+ : "[id].browser:update")
+
+/**
+ * public
+ *
+ * Sends a raw payload to tgui window.
+ *
+ * required message string JSON+urlencoded blob to send.
+ * optional force bool Send regardless of the ready status.
+ */
+/datum/tgui_window/proc/send_raw_message(message, force)
+ if(!client)
+ return
+ // Place into queue if window is still loading
+ if(!force && status != TGUI_WINDOW_READY)
+ if(!message_queue)
+ message_queue = list()
+ message_queue += list(message)
+ return
+ client << output(message, is_browser \
+ ? "[id]:update" \
+ : "[id].browser:update")
/**
* public
@@ -185,16 +246,18 @@
* Makes an asset available to use in tgui.
*
* required asset datum/asset
+ *
+ * return bool - TRUE if any assets had to be sent to the client
*/
/datum/tgui_window/proc/send_asset(datum/asset/asset)
if(!client || !asset)
return
+ sent_assets |= list(asset)
+ . = asset.send(client)
if(istype(asset, /datum/asset/spritesheet))
var/datum/asset/spritesheet/spritesheet = asset
send_message("asset/stylesheet", spritesheet.css_filename())
send_message("asset/mappings", asset.get_url_mappings())
- sent_assets += list(asset)
- asset.send(client)
/**
* private
@@ -205,7 +268,9 @@
if(!client || !message_queue)
return
for(var/message in message_queue)
- client << output(message, "[id].browser:update")
+ client << output(message, is_browser \
+ ? "[id]:update" \
+ : "[id].browser:update")
message_queue = null
/**
@@ -213,26 +278,45 @@
*
* Callback for handling incoming tgui messages.
*/
-/datum/tgui_window/proc/on_message(type, list/payload, list/href_list)
- switch(type)
- if("ready")
- // Status can be READY if user has refreshed the window.
- if(status == TGUI_WINDOW_READY)
- // Resend the assets
- for(var/asset in sent_assets)
- send_asset(asset)
- status = TGUI_WINDOW_READY
- if("log")
- if(href_list["fatal"])
- fatally_errored = TRUE
+/datum/tgui_window/proc/on_message(type, payload, href_list)
+ // Status can be READY if user has refreshed the window.
+ if(type == "ready" && status == TGUI_WINDOW_READY)
+ // Resend the assets
+ for(var/asset in sent_assets)
+ send_asset(asset)
+ // Mark this window as fatally errored which prevents it from
+ // being suspended.
+ if(type == "log" && href_list["fatal"])
+ fatally_errored = TRUE
+ // Mark window as ready since we received this message from somewhere
+ if(status != TGUI_WINDOW_READY)
+ status = TGUI_WINDOW_READY
+ flush_message_queue()
// Pass message to UI that requested the lock
if(locked && locked_by)
- locked_by.on_message(type, payload, href_list)
- flush_message_queue()
- return
+ var/prevent_default = locked_by.on_message(type, payload, href_list)
+ if(prevent_default)
+ return
+ // Pass message to the subscriber
+ else if(subscriber_object)
+ var/prevent_default = call(
+ subscriber_object,
+ subscriber_delegate)(type, payload, href_list)
+ if(prevent_default)
+ return
// If not locked, handle these message types
switch(type)
+ if("ping")
+ send_message("pingReply", payload)
if("suspend")
close(can_be_suspended = TRUE)
if("close")
close(can_be_suspended = FALSE)
+ if("openLink")
+ client << link(href_list["url"])
+ if("cacheReloaded")
+ // Reinitialize
+ initialize(inline_assets = inline_assets, fancy = fancy)
+ // Resend the assets
+ for(var/asset in sent_assets)
+ send_asset(asset)
diff --git a/code/modules/tgui_panel/audio.dm b/code/modules/tgui_panel/audio.dm
new file mode 100644
index 0000000000..e62c4b5bc1
--- /dev/null
+++ b/code/modules/tgui_panel/audio.dm
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/// Admin music volume, from 0 to 1.
+/client/var/admin_music_volume = 1
+
+/**
+ * public
+ *
+ * Sends music data to the browser.
+ *
+ * Optional settings:
+ * - pitch: the playback rate
+ * - start: the start time of the sound
+ * - end: when the musics stops playing
+ *
+ * required url string Must be an https URL.
+ * optional extra_data list Optional settings.
+ */
+/datum/tgui_panel/proc/play_music(url, extra_data)
+ if(!is_ready())
+ return
+ if(!findtext(url, GLOB.is_http_protocol))
+ return
+ var/list/payload = list()
+ if(length(extra_data) > 0)
+ for(var/key in extra_data)
+ payload[key] = extra_data[key]
+ payload["url"] = url
+ window.send_message("audio/playMusic", payload)
+
+/**
+ * public
+ *
+ * Stops playing music through the browser.
+ */
+/datum/tgui_panel/proc/stop_music()
+ if(!is_ready())
+ return
+ window.send_message("audio/stopMusic")
diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm
new file mode 100644
index 0000000000..35aa31eca7
--- /dev/null
+++ b/code/modules/tgui_panel/external.dm
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/client/var/datum/tgui_panel/tgui_panel
+
+/**
+ * tgui panel / chat troubleshooting verb
+ */
+/client/verb/fix_tgui_panel()
+ set name = "Fix chat"
+ set category = "OOC"
+ var/action
+ log_tgui(src, "Started fixing.",
+ context = "verb/fix_tgui_panel")
+ // Not ready
+ if(!tgui_panel?.is_ready())
+ log_tgui(src, "Panel is not ready",
+ context = "verb/fix_tgui_panel")
+ tgui_panel.window.send_message("ping", force = TRUE)
+ action = alert(src, "Method: Pinging the panel.\nWait a bit and tell me if it's fixed", "", "Fixed", "Nope")
+ if(action == "Fixed")
+ log_tgui(src, "Fixed by sending a ping",
+ context = "verb/fix_tgui_panel")
+ return
+ // Catch all solution
+ if(!tgui_panel || !istype(tgui_panel))
+ log_tgui(src, "tgui_panel datum is missing",
+ context = "verb/fix_tgui_panel")
+ tgui_panel = new(src)
+ tgui_panel.initialize(force = TRUE)
+ // Force show the panel to see if there are any errors
+ winset(src, "output", "is-disabled=1&is-visible=0")
+ winset(src, "browseroutput", "is-disabled=0;is-visible=1")
+ action = alert(src, "Method: Reinitializing the panel.\nWait a bit and tell me if it's fixed", "", "Fixed", "Nope")
+ if(action == "Fixed")
+ log_tgui(src, "Fixed by calling 'initialize'",
+ context = "verb/fix_tgui_panel")
+ return
+ // Failed to fix
+ action = alert(src, "Welp, I'm all out of ideas. Try closing BYOND and reconnecting.\nWe could also disable tgui_panel and re-enable the old UI", "", "Thanks anyways", "Switch to old UI")
+ if (action == "Switch to old UI")
+ winset(src, "output", "on-show=&is-disabled=0&is-visible=1")
+ winset(src, "browseroutput", "is-disabled=1;is-visible=0")
+ log_tgui(src, "Failed to fix.",
+ context = "verb/fix_tgui_panel")
diff --git a/code/modules/tgui_panel/telemetry.dm b/code/modules/tgui_panel/telemetry.dm
new file mode 100644
index 0000000000..79087d8500
--- /dev/null
+++ b/code/modules/tgui_panel/telemetry.dm
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/**
+ * Maximum number of connection records allowed to analyze.
+ * Should match the value set in the browser.
+ */
+#define TGUI_TELEMETRY_MAX_CONNECTIONS 5
+
+/**
+ * Maximum time allocated for sending a telemetry packet.
+ */
+#define TGUI_TELEMETRY_RESPONSE_WINDOW 30 SECONDS
+
+/// Time of telemetry request
+/datum/tgui_panel/var/telemetry_requested_at
+/// Time of telemetry analysis completion
+/datum/tgui_panel/var/telemetry_analyzed_at
+/// List of previous client connections
+/datum/tgui_panel/var/list/telemetry_connections
+
+/**
+ * private
+ *
+ * Requests some telemetry from the client.
+ */
+/datum/tgui_panel/proc/request_telemetry()
+ telemetry_requested_at = world.time
+ telemetry_analyzed_at = null
+ window.send_message("telemetry/request", list(
+ "limits" = list(
+ "connections" = TGUI_TELEMETRY_MAX_CONNECTIONS,
+ ),
+ ))
+
+/**
+ * private
+ *
+ * Analyzes a telemetry packet.
+ *
+ * Is currently only useful for detecting ban evasion attempts.
+ */
+/datum/tgui_panel/proc/analyze_telemetry(payload)
+ if(world.time > telemetry_requested_at + TGUI_TELEMETRY_RESPONSE_WINDOW)
+ message_admins("[key_name(client)] sent telemetry outside of the allocated time window.")
+ return
+ if(telemetry_analyzed_at)
+ message_admins("[key_name(client)] sent telemetry more than once.")
+ return
+ telemetry_analyzed_at = world.time
+ if(!payload)
+ return
+ telemetry_connections = payload["connections"]
+ var/len = length(telemetry_connections)
+ if(len == 0)
+ return
+ if(len > TGUI_TELEMETRY_MAX_CONNECTIONS)
+ message_admins("[key_name(client)] was kicked for sending a huge telemetry payload")
+ qdel(client)
+ return
+ var/list/found
+ for(var/i in 1 to len)
+ if(QDELETED(client))
+ // He got cleaned up before we were done
+ return
+ var/list/row = telemetry_connections[i]
+ // Check for a malformed history object
+ if (!row || row.len < 3 || (!row["ckey"] || !row["address"] || !row["computer_id"]))
+ return
+ if (world.IsBanned(row["ckey"], row["address"], row["computer_id"], real_bans_only = TRUE))
+ found = row
+ break
+ CHECK_TICK
+ // This fucker has a history of playing on a banned account.
+ if(found)
+ var/msg = "[key_name(client)] has a banned account in connection history! (Matched: [found["ckey"]], [found["address"]], [found["computer_id"]])"
+ message_admins(msg)
+ log_admin_private(msg)
diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm
new file mode 100644
index 0000000000..b983484046
--- /dev/null
+++ b/code/modules/tgui_panel/tgui_panel.dm
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/**
+ * tgui_panel datum
+ * Hosts tgchat and other nice features.
+ */
+/datum/tgui_panel
+ var/client/client
+ var/datum/tgui_window/window
+ var/broken = FALSE
+ var/initialized_at
+
+/datum/tgui_panel/New(client/client)
+ src.client = client
+ window = new(client, "browseroutput")
+ window.subscribe(src, .proc/on_message)
+
+/datum/tgui_panel/Del()
+ window.unsubscribe(src)
+ window.close()
+ return ..()
+
+/**
+ * public
+ *
+ * TRUE if panel is initialized and ready to receive messages.
+ */
+/datum/tgui_panel/proc/is_ready()
+ return !broken && window.is_ready()
+
+/**
+ * public
+ *
+ * Initializes tgui panel.
+ */
+/datum/tgui_panel/proc/initialize(force = FALSE)
+ initialized_at = world.time
+ // Perform a clean initialization
+ window.initialize(inline_assets = list(
+ get_asset_datum(/datum/asset/simple/tgui_common),
+ get_asset_datum(/datum/asset/simple/tgui_panel),
+ ))
+ window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
+ window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat))
+ request_telemetry()
+ addtimer(CALLBACK(src, .proc/on_initialize_timed_out), 2 SECONDS)
+
+/**
+ * private
+ *
+ * Called when initialization has timed out.
+ */
+/datum/tgui_panel/proc/on_initialize_timed_out()
+ // Currently does nothing but sending a message to old chat.
+ SEND_TEXT(client, "Failed to load fancy chat, reverting to old chat. Certain features won't work.")
+
+/**
+ * private
+ *
+ * Callback for handling incoming tgui messages.
+ */
+/datum/tgui_panel/proc/on_message(type, payload)
+ if(type == "ready")
+ broken = FALSE
+ window.send_message("update", list(
+ "config" = list(
+ "client" = list(
+ "ckey" = client.ckey,
+ "address" = client.address,
+ "computer_id" = client.computer_id,
+ ),
+ "window" = list(
+ "fancy" = FALSE,
+ "locked" = FALSE,
+ ),
+ ),
+ ))
+ return TRUE
+ if(type == "audio/setAdminMusicVolume")
+ client.admin_music_volume = payload["volume"]
+ return TRUE
+ if(type == "telemetry")
+ analyze_telemetry(payload)
+ return TRUE
+
+/**
+ * public
+ *
+ * Sends a round restart notification.
+ */
+/datum/tgui_panel/proc/send_roundrestart()
+ window.send_message("roundrestart")
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index c739d426c2..e758a43589 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -3,6 +3,7 @@
#ifdef UNIT_TESTS
#include "anchored_mobs.dm"
+#include "character_saving.dm"
#include "component_tests.dm"
#include "reagent_id_typos.dm"
#include "reagent_recipe_collisions.dm"
diff --git a/code/modules/unit_tests/character_saving.dm b/code/modules/unit_tests/character_saving.dm
new file mode 100644
index 0000000000..bdcb0f0276
--- /dev/null
+++ b/code/modules/unit_tests/character_saving.dm
@@ -0,0 +1,14 @@
+/datum/unit_test/character_saving/Run()
+ try
+ var/datum/preferences/P = new
+ P.load_path("test")
+ P.features["flavor_text"] = "Foo"
+ P.features["ooc_notes"] = "Bar"
+ P.save_character()
+ P.load_character()
+ if(P.features["flavor_text"] != "Foo")
+ Fail("Flavor text is failing to save.")
+ if(P.features["ooc_notes"] != "Bar")
+ Fail("OOC text is failing to save.")
+ catch(var/exception/e)
+ Fail("Failed to save and load character due to exception [e.file]:[e.line], [e.name]")
diff --git a/code/modules/unit_tests/reactions.dm b/code/modules/unit_tests/reactions.dm
new file mode 100644
index 0000000000..66d9b49099
--- /dev/null
+++ b/code/modules/unit_tests/reactions.dm
@@ -0,0 +1,6 @@
+/datum/unit_test/reactions/Run()
+ for(var/datum/gas_reaction/G in SSair.gas_reactions)
+ var/test_info = G.test()
+ if(!test_info["success"])
+ var/message = test_info["message"]
+ Fail("Gas reaction [G.name] is failing its unit test with the following message: [message]")
diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm
index c26a9ae1f0..745eddcc07 100644
--- a/code/modules/uplink/uplink_items/uplink_clothing.dm
+++ b/code/modules/uplink/uplink_items/uplink_clothing.dm
@@ -90,6 +90,7 @@
desc = "Through bluespace magic stolen from an organisation that hoards technology, these boots simply allow you to slip through the atoms that make up anything, but only while walking, for safety reasons. As well as this, they unfortunately cause minor breath loss as the majority of atoms in your lungs are sucked out into any solid object you walk through."
item = /obj/item/clothing/shoes/wallwalkers
cost = 6
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
/datum/uplink_item/device_tools/guerillagloves
name = "Guerilla Gloves"
diff --git a/code/modules/uplink/uplink_items/uplink_dangerous.dm b/code/modules/uplink/uplink_items/uplink_dangerous.dm
index 58cb43996d..898b716775 100644
--- a/code/modules/uplink/uplink_items/uplink_dangerous.dm
+++ b/code/modules/uplink/uplink_items/uplink_dangerous.dm
@@ -20,6 +20,7 @@
desc = "A sleek box containing a brutally simple Syndicate revolver that fires .357 Magnum rounds and has 7 chambers, and an extra speedloader."
item = /obj/item/storage/box/syndie_kit/revolver
cost = 13
+ player_minimum = 15
surplus = 50
exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
diff --git a/code/modules/vending/kinkmate.dm b/code/modules/vending/kinkmate.dm
index a78a4e6ef4..e522583772 100644
--- a/code/modules/vending/kinkmate.dm
+++ b/code/modules/vending/kinkmate.dm
@@ -26,7 +26,12 @@
/obj/item/clothing/under/shorts/polychromic/pantsu = 3,
/obj/item/clothing/under/misc/poly_bottomless = 3,
/obj/item/clothing/under/misc/poly_tanktop = 3,
- /obj/item/clothing/under/misc/poly_tanktop/female = 3
+ /obj/item/clothing/under/misc/poly_tanktop/female = 3,
+ /obj/item/autosurgeon/penis = 3,
+ /obj/item/autosurgeon/testicles = 3,
+ /obj/item/storage/pill_bottle/penis_enlargement = 5,
+ /obj/item/storage/pill_bottle/breast_enlargement = 5,
+ /obj/item/storage/daki = 4
)
contraband = list(
/obj/item/clothing/neck/petcollar/locked = 2,
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index 26beccaac0..37c21fc2eb 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -160,7 +160,10 @@
/obj/item/clothing/shoes/sneakers/black = 3,
/obj/item/clothing/gloves/fingerless = 3,
/obj/item/clothing/head/soft/black = 3,
- /obj/item/clothing/mask/bandana/skull = 2)
+ /obj/item/clothing/mask/bandana/skull = 2,
+ /obj/item/clothing/under/misc/mechsuitred = 1,
+ /obj/item/clothing/under/misc/mechsuitwhite = 1,
+ /obj/item/clothing/under/misc/mechsuitblue = 1)
contraband = list(/obj/item/clothing/suit/hooded/techpriest = 2)
refill_canister = /obj/item/vending_refill/wardrobe/robo_wardrobe
payment_department = ACCOUNT_SCI
diff --git a/code/modules/vore/eating/living.dm b/code/modules/vore/eating/living.dm
index 9395cef952..6949a7030f 100644
--- a/code/modules/vore/eating/living.dm
+++ b/code/modules/vore/eating/living.dm
@@ -11,13 +11,11 @@
// Hook for generic creation of stuff on new creatures
//
/hook/living_new/proc/vore_setup(mob/living/M)
- M.verbs += /mob/living/proc/preyloop_refresh
- M.verbs += /mob/living/proc/lick
- M.verbs += /mob/living/proc/escapeOOC
+ add_verb(M, list(/mob/living/proc/preyloop_refresh, /mob/living/proc/lick, /mob/living/proc/escapeOOC))
if(M.vore_flags & NO_VORE) //If the mob isn't supposed to have a stomach, let's not give it an insidepanel so it can make one for itself, or a stomach.
return TRUE
- M.verbs += /mob/living/proc/insidePanel
+ add_verb(M, /mob/living/proc/insidePanel)
//Tries to load prefs if a client is present otherwise gives freebie stomach
spawn(2 SECONDS) // long delay because the server delays in its startup. just on the safe side.
diff --git a/config/config.txt b/config/config.txt
index 46f9a0cdc4..a550e6bf8a 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -4,6 +4,8 @@ $include game_options.txt
$include dbconfig.txt
$include comms.txt
$include antag_rep.txt
+$include resources.txt
+# Cit-specific imports
$include donator_groupings.txt
$include dynamic_config.txt
$include plushies/defines.txt
@@ -431,6 +433,8 @@ AUTOADMIN_RANK Game Master
#CLIENT_WARN_MESSAGE Byond released 511 as the stable release. You can set the framerate your client runs at, which makes the game feel very different and cool. Shortly after its release we will end up using 511 client features and you will be forced to update.
CLIENT_ERROR_VERSION 511
CLIENT_ERROR_MESSAGE Your version of byond is not supported. Please upgrade.
+## The minimum build needed for joining the server, if using 512, a good minimum build would be 1421 as that disables the Middle Mouse Button exploit.
+CLIENT_ERROR_BUILD 1421
## TOPIC RATE LIMITING
## This allows you to limit how many topic calls (clicking on an interface window) the client can do in any given game second and/or game minute.
diff --git a/config/policy.json b/config/policy.json
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/config/policy.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/config/resources.txt b/config/resources.txt
new file mode 100644
index 0000000000..909bef0809
--- /dev/null
+++ b/config/resources.txt
@@ -0,0 +1,38 @@
+# External resources
+# Set this to the location of a .zip with the server's .rsc inside of it.
+# If you set this mutiple times, the server will rotate between the links.
+# To use this, the compile option PRELOAD_RSC must be set to 0 to keep byond from preloading resources
+
+EXTERNAL_RSC_URLS http://tgstation13.download/byond/tgstationv2.zip
+
+
+########################
+# Browser Asset Config #
+########################
+# Browser assets are any file included in interfaces. css, images, javascript, etc.
+# This handles configuring how we get these to the player so interfaces can access them.
+
+# Asset Transport
+# The normal way of getting assets to clients is to use the internal byond system. This can be slow and delay the opening of interface windows. It also doesn't allow the internal IE windows byond uses to cache anything.
+# You can instead have the server save them to a website via a folder within the game server that the web server can read. This could be a simple webserver or something backed by a CDN.
+# Valid values: simple, webroot. Simple is the default.
+#ASSET_TRANSPORT webroot
+
+
+# Simple asset transport configurable values.
+
+# Uncomment this to have the server passively send all browser assets to each client in the background. (instead of waiting for them to be needed)
+# This should be uncommented in production and commented in development
+#ASSET_SIMPLE_PRELOAD
+
+
+# Webroot asset transport configurable values.
+
+# Local folder to save assets to.
+# Assets will be saved in the format of asset.MD5HASH.EXT or in namespaces/hash/ as ASSET_FILE_NAME or asset.MD5HASH.EXT
+#ASSET_CDN_WEBROOT data/asset-store/
+
+# URL the folder from above can be accessed from.
+# for best results the webserver powering this should return a long cache validity time, as all assets sent via this transport use hash based urls
+# if you want to test this locally, you simpily run the `localhost-asset-webroot-server.py` python3 script to host assets stored in `data/asset-store/` via http://localhost:58715/
+#ASSET_CDN_URL http://localhost:58715/
diff --git a/html/panels.css b/html/admin/panels.css
similarity index 97%
rename from html/panels.css
rename to html/admin/panels.css
index 1f969c690e..22373ee0ca 100644
--- a/html/panels.css
+++ b/html/admin/panels.css
@@ -1,10 +1,10 @@
-body {padding:0px;margin:0px;}
-#top {position:fixed;top:5px;left:10%;width:80%;text-align:center;background-color:#fff;border:2px solid #ccc;}
-#main {position:relative;top:10px;left:3%;width:96%;text-align:center;z-index:0;}
-#searchable {table-layout:fixed;width:100%;text-align:center;"#f4f4f4";}
-tr.norm {background-color:#f4f4f4;}
-tr.title {background-color:#ccc;}
-tr.alt {background-color:#e7e7e7;}
-.small {font-size:80%;}
-a {text-decoration:none;}
-a:hover {color:#d3d;}
+body {padding:0px;margin:0px;}
+#top {position:fixed;top:5px;left:10%;width:80%;text-align:center;background-color:#fff;border:2px solid #ccc;}
+#main {position:relative;top:10px;left:3%;width:96%;text-align:center;z-index:0;}
+#searchable {table-layout:fixed;width:100%;text-align:center;"#f4f4f4";}
+tr.norm {background-color:#f4f4f4;}
+tr.title {background-color:#ccc;}
+tr.alt {background-color:#e7e7e7;}
+.small {font-size:80%;}
+a {text-decoration:none;}
+a:hover {color:#d3d;}
diff --git a/html/search.js b/html/admin/search.js
similarity index 100%
rename from html/search.js
rename to html/admin/search.js
diff --git a/html/changelog.html b/html/changelog.html
index 0a2a1f3086..5650ae6a8a 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,356 @@
-->
+
24 September 2020
+
Putnam3145 updated:
+
+
Atmos is free.
+
+
+
22 September 2020
+
Arturlang updated:
+
+
TGUI Statpanel
+
+
YakumoChen updated:
+
+
Mechsuits, robotics jumpsuits added to RoboDrobe
+
+
timothyteakettle updated:
+
+
character previews should be more consistent now
+
+
+
20 September 2020
+
DeltaFire15 updated:
+
+
Sutures work on simplemobs again.
+
Attacking dismembered bodyparts now targets the chest instead, for weapons aswell as unarmed attacks.
+
+
MrJWhit updated:
+
+
New sprites for chess pieces! You can craft them in-game with metal sheets.
+
+
silicons updated:
+
+
hulks can smash again (walls no longer break their hands)
+
acid no longer degrades armor
+
+
+
17 September 2020
+
DeltaFire15 updated:
+
+
Failing the plushmium reaction can now create peculiar plushies, depending on reaction volume.
+
The mood-buff from petting a plushie now works properly again.
+
Fixed wacky necropolis loot chest behavior
+
+
EmeraldSundisk updated:
+
+
Adds the Research Director's office to Omega Station
+
Adds 2 new solar arrays (and control rooms)
+
Adds some action figures that weren't there previously
+
The CMO's office now has a light switch
+
Slight readjustments to impacted areas
+
Readjusts the toxins air supply line to (ideally) be easier to service
+
Department camera consoles should now be able to actually check appropriate cameras
+
Xenobiology can now be locked down (by the Research Director)
+
+
MrJWhit updated:
+
+
Adds a brain damage line
+
+
Putnam3145 updated:
+
+
Your balls finally feel full, again.
+
+
timothyteakettle updated:
+
+
due to changes in policy, and several lawsuits, Nanotrasen has been forced to allow disabled people to sign up
+
+
+
16 September 2020
+
timothyteakettle updated:
+
+
fixed an icon path
+
+
+
12 September 2020
+
BlueWildrose updated:
+
+
The Polychromic winter coat's hoodie will now polychrome, alongside any other new polychromic items with toggleable headwear.
+
Minesweeper will no longer blow up the player's ears when they select "Play on the same board"
+
Slimepeople will find warm donk pockets among other toxin healing items even more repulsive, as they are anti-toxic.
+
Slimepeople are now neutral to gross foods.
+
+
DeltaFire15 updated:
+
+
AIs can no longer qdel() the gravity generator
+
+
Putnam3145 updated:
+
+
Added some unit tests for reactions.
+
replaced handle_changeling and handle_bloodsucker with signal registration
+
+
Sonic121x updated:
+
+
Fixed pill button on chemical press
+
+
TheObserver-sys updated:
+
+
Brass now has a proper datum. Aspiring Forgetenders rejoice!
+
+
Trilbyspaceclone updated:
+
+
Race based drinks will no longer stay inside your blood for ever.
+
+
Tupinambis updated:
+
+
Redid Cogstation atmos pipes to make it less cluttered.
+
Removed a few doors from the main hallway to mitigate chokepoint issues
+
All belt hell conveyers are now on by default, so that belt hell actually works.
+
IDs for poddoors and belts and the like. Everything is now properly hooked and should work as expected (except for the pressure triggered mass drivers)
+
addresses most if not all roundstart active turfs.
+
Issue where wires were connected to the SMES improperly, and SMES were not properly precharged, resulting in power failure earlier than intended.
+
various rogue turfs and wirings.
+
security office APC being hooked to maintenance for some reason.
+
TEG is now directly wired to the SMES.
+
adds a subtype of mass drivers that is triggered by things being on it. TODO: Make these mass drivers trigger poddoors, to make belt hell fully functional.
+
+
lolman360 updated:
+
+
glaives now work again
+
+
zeroisthebiggay updated:
+
+
Revolver is now poplocked down to fifteen people.
+
+
+
11 September 2020
+
Putnam3145 updated:
+
+
Superconducting turfs now can't go below 0 celsius.
+
+
+
09 September 2020
+
Putnam3145 updated:
+
+
Made superconductivity work for the first time literally ever.
+
+
timothyteakettle updated:
+
+
accents work better
+
+
+
08 September 2020
+
Ghommie updated:
+
+
fixed names of the Electrical Toolbox goodie pack and green croptop christmas suit.
+
Fixed turf visuals for real. Original PR by AnturK on tgstation.
+
+
KeRSedChaplain updated:
+
+
added borg_deathsound.ogg and android_scream.ogg
+
+
silicons updated:
+
+
meteor waves now have a static 5 minute timer.
+
+
+
07 September 2020
+
DeltaFire15 updated:
+
+
fixed a typo causing a span not to show.
+
+
Putnam for debugging ammo loading! Thx!! updated:
+
+
Removed spell blade, diamond picaxe, and fire wand from lava land loot tables
+
Each mini boss now has its own type of crate.
+
Each spike has its own type of crate that it pulls its now smaller loot table from
+
Moved god eye from spike loot table to hard spawn collosses fight
+
Moved holoparasight from spike loot table to bubble gum
+
Moved magic meat hook from spike loot table to drake
+
2 more crates to Arena Shuttle as well as 4-4-4 of each new type of chest
+
Replaced the diamond pick loot with a better one
+
Replaced the cursted katana with a non cursted verson that deals half the damage and has less block!
+
Three new potions, blue heals the mind like a mama potion, Green heals the soul aka the organs! Lastly Red heals the body, by 100 damage of each main types. Best not to waste them!
+
Four more "wands" Spellbooks! These fun little guys shoot out its own pages to do the affect, one will set the target on fire like a bullet, one will harm them a bit, one will heal the target a small bit - How nice! Last one will give them a few statis affects like a taser bolt but without as much power or tasing affect
+
+
+
06 September 2020
+
Putnam3145 updated:
+
+
Dynamic no longer pushes events.
+
Made spontaneous brain trauma a good deal less annoying.
+
+
lolman360, NecromancerAnne updated:
+
+
The ancient art of blacksmithing, now in pixels.
+
cool swords and shit (thanks anne)
+
+
raspy-on-osu updated:
+
+
thermomachine examine text
+
+
+
05 September 2020
+
Bhijn updated:
+
+
Readded the old method of temperature notifications in the form of a new pair of shivering/sweating notifications.
+
+
Putnam3145 updated:
+
+
Hilbert hotel flavor text for one particular snowflake hotel changed.
+
admins can now actually reduce threat level in dynamic
+
Made owo.ogg smaller.
+
Character saving unit test is now more verbose on failure.
+
Added an extools proc hook alternative to rust_g logging.
+
+
raspy-on-osu updated:
+
+
supermatter shard examine text
+
protolathe item categories
+
+
+
04 September 2020
+
timothyteakettle updated:
+
+
ipcs can speak
+
+
+
03 September 2020
+
Ghommie updated:
+
+
Jaunters should now work with magic mirror chasming.
+
The photocopier can now print more than one copy at a time.
+
Alkali perspiration infos don't crash the Pandemic UI anymore.
+
Windoors can be actually tinted now.
+
+
+
02 September 2020
+
Putnam3145 updated:
+
+
Added a unit test for character saving.
+
Plastitanium rapier no longer silently sleeps with no chance at counterplay when used by pacifists.
+
Fusion scan is now actually useful.
+
+
Tupinambis updated:
+
+
moved the dakis, genital growth pills, and genital autosurgeons out of the maintenance loot table and into kinkmates.
+
+
raspy-on-osu updated:
+
+
pyroclastic anomaly client spam
+
+
timothyteakettle updated:
+
+
you can hide your ckey now from the roundend report
+
+
+
01 September 2020
+
BlueWildrose updated:
+
+
fixed slimes starting off hungry
+
+
+
31 August 2020
+
Arturlang updated:
+
+
Slimes can now damage structures, don't leave them unfed!
+
+
Chiirno updated:
+
+
Moves pill_bottles/dice to box/dice on CogStation.
+
+
Couls, ported by NecromancerAnne updated:
+
+
cleans up mech backstabbing code
+
+
DeltaFire15 updated:
+
+
teleport-to-ark ability of the eminence, commented out
+
teleport-to-obelisk ability for the eminence
+
+
Detective-Google updated:
+
+
plasmamen have no more slowdown
+
object reskins now use very nice and cool radials
+
+
EmeraldSundisk updated:
+
+
Adds a pool to MetaStation
+
Slight readjustments to the surrounding area
+
Fixes a handful of external airlocks
+
+
ForrestWick updated:
+
+
removes wall walking boots from nukie uplink
+
+
Ghommie updated:
+
+
e-gun overlays and some floor decals should have been fixed.
+
+
LetterN updated:
+
+
tgchat
+
+
Lynxless updated:
+
+
Changed anatomic panacea into a direct buff, instead of a chem injection
+
Changed the values of anatomic panacea
+
Added a new icon for panacea's buff alert
+
+
Putnam3145 updated:
+
+
Pref for genital/vore examine text
+
Fixed a couple events having ghost roles eligible.
+
Buffed slaughter demon: gets stronger as it eats people
+
Nerfed slaughter demon: no longer permanently round-removes all who are eaten by it, instead releasing their now-heartless bodies
+
Dynamic storytellers now calculate property weights properly.
+
+
Sonic121x updated:
+
+
Fix the four type of new tea that will stuck inside your vein.
+
drinking glass sprite for those tea.
+
+
kappa-sama updated:
+
+
miners can no longer acquire funny antag item
+
+
lolman360 updated:
+
+
shuttle engine/heater sprites now face the right way
+
+
raspy-on-osu updated:
+
+
TEG power output
+
tesla movement priorities
+
tesla counterplay
+
tesla containment check (containment variable now usable)
+
+
silicons updated:
+
+
brooms now sweep objects on MOVABLE_PRE_MOVE rather than MOVABLE_MOVED
+
firedoors no longer automatically open on touch when there's no pressure differences.
+
+
timothyteakettle updated:
+
+
buzz, buzz2 and ping are now all unrestricted emotes and can be used by anyone
+
the drake credit and pickle credit sprites have been removed
+
tongue speech handling is now done by accent datums
+
+
zeroisthebiggay updated:
+
+
waffleco
+
+
+
30 August 2020
+
raspy-on-osu updated:
+
+
new explosion echoes
+
explosion echo range
+
5 new explosion related sounds
+
+
28 August 2020
EmeraldSundisk updated:
@@ -938,555 +1288,6 @@
rad and kravglove sprites
-
-
23 July 2020
-
DeltaFire15 updated:
-
-
Traits are no longer fucked
-
-
Putnam3145 updated:
-
-
Slight optimization in chat code.
-
-
kappa-sama updated:
-
-
tendril chests being empty
-
-
zeroisthebiggay updated:
-
-
fetish content
-
-
-
22 July 2020
-
Ludox updated:
-
-
You can no longer be brainwashed into giving birth to a fake baby
-
-
kappa-sama updated:
-
-
brainwashing disk has lost its cost buffs (3->5) and is role restricted once more (medical doctor/roboticist)
-
-
-
21 July 2020
-
Arturlang updated:
-
-
Decal painter ui now works, yay?
-
-
CameronWoof updated:
-
-
Adds aloe, a new growable plant
-
Adds medicated sutures and advanced regenerative meshes
-
Polypyrylium oligomers and liquid electricity now correctly populate
-
-
Chiirno updated:
-
-
Alt-click pill bottles places top-most pill into active hand.
-
Moved dice bags from pill_bottle/dice to box/dice to avoid dice bags being affected from medical specific pill bottle changes.
-
-
DeltaFire15 updated:
-
-
Swarmers can once again eat items as long as there's nothing living in them.
-
-
Funce updated:
-
-
Genetics spiderwebs are no longer impassable walls
-
-
Kraseo updated:
-
-
Damp rags no longer instantly apply their chemicals onto someone.
-
-
SiliconMain updated:
-
-
Geigers can no longer be contaminated
-
-
TheSpaghetti updated:
-
-
new snowflake trait
-
-
kappa-sama updated:
-
-
advanced surgery duffel bag no longer comes with a nukie medkit. it costs 4 less telecrystals to make up for this colossal nerf.
-
quartered the weight of the stray cargo pod event from 2x normal to 1/2 normal
-
-
silicons updated:
-
-
survivalists (from summon guns/magic) are proper objective'd pseudoantagonists again
-
summon guns/magic can only be used once each
-
summon guns/magic now only cost one point each
-
:dsmile: :dfrown: :dhsmile: :dpog: :dneutral:
-
gravity should update for mobs a fair bit faster
-
voting can now be done from the stat panel if the system is plurality and approval
-
-
timothyteakettle updated:
-
-
new fried component used for frying objects
-
various frying bugs fixed such as being able to unfry items and frying turfs with cold cooking oil
-
-
-
20 July 2020
-
lolman360 updated:
-
-
Service borgs now have synthesizers instead of a violin and guitar.
-
borg RSF
-
-
-
19 July 2020
-
Arturlang updated:
-
-
TGUI 3.0 and enables all the UIs, plus the smart asset cache, and all the things required for them
-
-
EmeraldSundisk updated:
-
-
Adds a pool to Delta Station
-
Adds light fixtures to specified areas
-
Relocates objects in impacted areas of Delta's starboard maintenance
-
-
MrJWhit updated:
-
-
Adds a small light next to the kitchen counter
-
-
Putnam3145 updated:
-
-
Pen uplinks no longer broken
-
-
TheObserver-sys updated:
-
-
Adds a new reaction: Slime Extractification. Take 30u Slime Jelly, 5u Frost Oil, and 5u plasma to generate a fresh grey slime extract.
-
-
Yakumo Chen updated:
-
-
Hierophant club now checks for friendly fire by default.
-
-
b1tt3r1n0 updated:
-
-
Added the updated circle game
-
-
silicons updated:
-
-
Unarmed parry is now a thing.
-
-
timothyteakettle updated:
-
-
plushies in the loadout have been replaced with a box that lets you choose one instead
-
wrestling should no longer have the ability to permanently rotate people
-
you can now select to wear a snail shell as your backpack in the customization menu
-
-
zeroisthebiggay updated:
-
-
martial arts twenty minpop
-
records
-
-
-
17 July 2020
-
ShizCalev, Fikou updated:
-
-
Added some sanity checking for varedit values.
-
Fixed an exploit involving coins and mints that could crash the server.
-
Fixed an exploit that would allow you to destroy round-critical / indestructible items with folders.
-
Swarmers can no longer cut power lines by deconstructing catwalks underneath them.
-
Fixed a scenario that allowed infinite resource generation via ore machines.
-
you can no longer inject html in ahelps
-
you cant either, jannies
-
-
timothyteakettle updated:
-
-
fixes a small pickle related issue
-
recent culinary and scientific advancements have brought forth new pickle related technologies
-
-
-
16 July 2020
-
DeltaFire15 updated:
-
-
Fixes a zeolite runtime caused by a missing check.
-
-
Sneakyrat6 updated:
-
-
Fixes being able to meta people real name with OOC Notes
-
-
timothyteakettle updated:
-
-
travelling traders from another dimension can now visit the station in search of something specific, and reward you for giving it to them
-
small error with pet carrier logic fixed and also making sure simple mobs are catered for properly inside bluespace jars
-
fixes coin related issue
-
-
-
15 July 2020
-
Sonic121x updated:
-
-
Paramedic jumpsuit
-
-
-
14 July 2020
-
silicons updated:
-
-
chemical reactions now are sorted by priority first and temperature second.
-
sec and medical records have been added to character setup.
-
circuit reagent heaters are now sanitized for temperature from 2.7 to 1000.
-
-
timothyteakettle updated:
-
-
ports a money bag exploit
-
-
-
13 July 2020
-
Linzolle updated:
-
-
you can no longer vore and digest people regardless of vore preferences
-
-
Owai-Seek updated:
-
-
Trashbags can now hold most shoes, and organs.
-
You can no longer nest nuke disks or hold brains in the trash.
-
-
-
12 July 2020
-
DeltaFire15 updated:
-
-
Sentinels compromise now heals augmented bodyparts.
-
-
EmeraldSundisk updated:
-
-
Adds turnstiles to CogStation's security wing
-
Readds robotics to the Corpse Disposal Network
-
Readds chemistry's ability to send items directly to the experimentation lab
-
Visual renovation and slight adjustments to CogStation's robotics lab
-
Slight visual adjustments elsewhere (the library)
-
CogStation's mail and disposal pipes are once again complete
-
CogStation's robotics lab now has spawners, lights, and other room essentials
-
-
HeroWithYay updated:
-
-
Added Telecrystal Dust
-
Telecrystals can be sold at cargo
-
-
LetterN updated:
-
-
Added d[thing] emojis
-
bye xss
-
-
MrJWhit updated:
-
-
Removes northern tunnel to the monastery on Pubby
-
-
Yakumo Chen updated:
-
-
Adds a wedding crate to cargo full of wedding attire.
-
-
kappa-sama updated:
-
-
wisdom cow is half as common and is wise enough to lag the server 66% less
-
-
silicons updated:
-
-
policy configuration added, plus support hooks for assisting enforcement of clone memory disorder. logging: added logging of revival by defib, cloning, strangereagent, and revival surgery
-
You can now "audibly emote" by having ! at the start of a sentence.
-
-
timothyteakettle updated:
-
-
due to recent biological advancements, you can now make eye contact with people.
-
-
zeroisthebiggay updated:
-
-
a singular stray pixel
-
-
-
11 July 2020
-
Putnam3145 updated:
-
-
Gas mixtures now live entirely in a DLL.
-
-
-
10 July 2020
-
Chiirno updated:
-
-
Gave jellypeople a unique brain object /obj/item/organ/brain/jelly
-
added an icon for jellypeople brains.
-
-
EmeraldSundisk updated:
-
-
Adds a pool to PubbyStation
-
Slight adjustments to the surrounding area as to fit said pool
-
-
Sneakyrat6 updated:
-
-
Fixes hair falling out of hoodies.
-
-
TheObserver-sys updated:
-
-
Actually adds the juice reagent to make laugh peas donuts.
-
-
-
09 July 2020
-
timothyteakettle updated:
-
-
bluespace tray added, allowing twice as many items as the regular tray, printable at the service lathe, researched through science
-
bluespace jar added, a kind of pet carrier that allows human sized mobs inside, and smashes when thrown, researched and printed through science
-
-
-
08 July 2020
-
DeltaFire15 updated:
-
-
The kill-once objective now works properly.
-
-
EmeraldSundisk updated:
-
-
CogStation now has an apothecary
-
Removes an outdated note on sleepers
-
Readjusts CogStation's chemistry lab
-
Slight area designation adjustments for Robotics
-
The arrivals plaque should be readable now
-
-
Owai-Seek updated:
-
-
Margarine, Chili Cheese Fries.
-
Egg Wraps are now categorized under egg foods.
-
Tuna Sandwich crafting/sprite is now visible.
-
Icons for chicken, cooked chicken, steak, grilled carp, corndogs
-
Icons for chili cheese fries, margarine, BLT sandwich
Fixed the slowdown formula for small character sprites; you guys don't use custom sprite sizes so just ignore these changes.
-
Fixed the "Move it to the threshold" button; it now does what it says.
-
Reworded some text to be clearer.
-
-
-
05 July 2020
-
Ghommie updated:
-
-
You can now actually gain wiring experience from using cable coils.
-
Opening the View Skill Panel shouldn't trigger messages about insufficient admin priviledges anymore.
-
-
Yakumo Chen, kappa-sama updated:
-
-
Removes improvised handguns
-
removed handsaws, improvised gun barrels (you can use atmos pipes again)
-
Guncrafting is less time and resource intensive
-
Item names in guncrafting are user-friendly.
-
-
kappa-sama updated:
-
-
cloth string to replace durathread string
-
durathread string
-
All bows and arrows have had crafting times significantly reduced, coming out at up to 6 times faster crafting speeds. Improvised bows no longer require durathread; instead, they use cloth materials.
-
-
silicons updated:
-
-
active blocking now has a toggle keybind
-
auto bunker override verb has been added
-
shields take 2.5 stam instead of 3.5 stam per second to maintain block
-
Cybernetic implant shields will auto-extend and be used to block if the user has no item to block with
-
-
timothyteakettle updated:
-
-
cooking oil is now far less lethal, requiring a higher volume of the reagent to deal more damage
-
-
-
04 July 2020
-
Sonic121x updated:
-
-
crushed Soldry sodacan
-
digitigrade version of chief medical officer's turtleneck and captain's female formal outfit.
-
-
silicons updated:
-
-
blood_DNA["color"] is now a single variable instead of a list
-
-
-
03 July 2020
-
Arturlang updated:
-
-
You can now toggle hardsuit helmets from the strip menu
-
-
Ghommie updated:
-
-
fixed custom speech/tongue stuff.
-
Lowered shaft miners' paycheck, they have other ways to make cash.
-
You can't (un)equip garments on/from obscured inventory slots anymore.
-
The stamina cost multiplier for swinging melee weapons against mobs has been brought back to 1 from 0.8
-
The stamina cost for throwing mobs now scales with their mob size variable.
-
-
LetterN updated:
-
-
Ported some tags from tgui-3.0 to Vending.js
-
vending icons
-
r&d icons
-
chem master icons
-
-
Onule updated:
-
-
titanium wall man good
-
-
Sonic121x updated:
-
-
Bringback the ChemMaster pill type button.
-
Fix Technode icon.
-
-
bunny232 updated:
-
-
Witchhunter hat no longer obscures mask ears ,eyes, face and mouth
-
-
timothyteakettle updated:
-
-
bloodpacks initialise correctly now
-
-
-
02 July 2020
-
Ghommie updated:
-
-
Fixing a few issues with twohanded items.
-
Unum decks now work correctly.
-
Abductor walls are once again buildable with alien alloy.
-
-
Trilbyspaceclone updated:
-
-
Makes pride and envy ruin a bit smaller!
-
Pride now has rings, lipstick wigs and silver walls/door making a nice and polished look then cyan blue walls.
-
more trash and better dagger placement on food ruin
-
Snowboim now has snowballs and toy gifts for the two skeles daw!
-
Beach boim now has carp light branding beer, as well as soap!
-
Greed ruin now uses nice slick walls and carpet!
-
Founten ruin looks a lot better with its carpets and well maintained fluff things, but walls suffered and no longer can salvage ruined metal...
-
Alien nest has a bit more glowy floors of resin looking a bit more lived in by the drones. As well as the "door" now being see through resin rather then the thicker stuff that you cant see through
-
Pizza party has a few more gifts, some candy and snap pops yay!
-
Sloth ruin is about 15~ tiles shorter, has and has more fruit for a bowl. How lazy!
-
-
silicons updated:
-
-
bohbombing is a thing now
-
-
-
30 June 2020
-
Fikou updated:
-
-
spray cans, airlock painters, and decal painters added to engineering/service/autolathe (where applicable)
-
-
Ghommie updated:
-
-
Fixed a gap on the male insect anthro torso sprite when facing south.
-
Fixed mecha ID access not being removable.
-
Fixed a peeve with the hypno trance status effect not sanitizing some heard hypnosis inputs (i.e. custom say messages like say"honks*clownem ipsum dolor")
-
fixed an issue about using stacks with only 1 amount left.
-
Fixed a peeve on attack messages against carbons/humans.
-
Fixed missing hypnochair board.
-
Fixed material walls and tiles. My bad on that port.
-
-
Ghommie (inspired by MrDoomBringer's work on tgstation) updated:
-
-
New check skills UI.
-
-
Ghommie (porting PRs by XTDM, coiax, MrDoomBringer) updated:
-
-
Random Events now have a follow link for ghosts!
-
Adds the Spontaneous Brain Trauma to the event pool. Sometimes your brain just goes a little wrong.
-
Sometimes a low level cloning pod will make errors in replicating your brain, leaving you with a mild brain trauma.
-
When a person is cloned, any mental traumas are cloned as well.
-
The wizard federation announces that the Curse of Madness is out of beta and is now available for purchase for 4 points. It causes long-lasting brain traumas to all inhabitants of a target space station.
-
The wizard federation declines responsibility for any self-harm caused by curses cast while inside the targeted station.
-
Due to the extensive testing of the Curse of Madness some unique new trauma types have appeared across Nanotrasen-controlled space.
-
Curse of Madness can now be triggered by a wizard's Summon Events, at the same chance as Summon Guns or Summon Magic.
-
When an admin triggers Curse of Madness manually, they can specify their own dark truth to horrify the station with.
-
-
nightred updated:
-
-
Created two_handed component
-
Updated all existing two handed items to use the new component
-
-
silicons updated:
-
-
typing indicators no longer generates duplicate message boxes.
-
config errors now have line numbers.
-
outgoing mentorpms are now blue instead of green for the sender.
-
*squish
-
-
timothyteakettle updated:
-
-
you can now select your tongue and speech verb in the character customization menu!
-
skeleton is now split into two more types, greater and lesser
-
non-carbon blood is now not white
-
fixed a bunch of grammar/spelling mistakes
-
-
-
29 June 2020
-
b1tt3r1n0 updated:
-
-
Made teratomas from sdgf less powergame
-
-
timothyteakettle updated:
-
-
slimes no longer have white blood by default
-
-
-
28 June 2020
-
Detective-Google updated:
-
-
cog is less the suck
-
piggybacking is no longer absolutely inferior
-
-
Ghommie updated:
-
-
Fixing windows interaction with spraycans.
-
Fixing kinetic accelerator guns not working well with gun circuitries.
-
Fixing Zoomba borgs lights overlays.
-
Fixing the "absorb another ling" and "absorb the most dna" objectives rolling when no other changeling is around.
-
Clarified a pet peeve about the spread infestation ability.
-
BEPIS nodes won't show up anymore in the expert mode ui of the r&d console anymore (good thing they weren't researchable).
-
Hopefully fixing sound loop edge cases.
-
Fixing pAI radios being permanently disabled by EMPs at times.
-
Windoors can now be obscured with spraycans just like windows.
-
-
Ghommie porting PRs by Qustinnus/Floyd, Willow, cacogen, nemvar, Ghilker and EOBGames (Inept) updated:
-
-
Fixes a material duplication bug.
-
unique combinations of custom_materials lists are now shared between objects
-
meat material. yes.
-
materials can now be used to build walls/floors. meat house
-
edible component now does not try to attack if you eat something with it
-
Texture support for mat datums with thanks to 4DPlanner!
-
you no longer hit yourself with organs when eating
-
A whole bunch of materials are now datumised! Check out bronze, runed metal, sand, sandstone, snow, paper, cardboard, bone and bamboo. Oh, and pizza. Yes, pizza.
-
Buffs material floor tiles' throwforces from 1 to 10 (same as iron) to better showcase the effect of different materials (e.g. meat vs. titanium)
-
Radioactive items no longer output a single . when examined at a distance
-
-
MrJWhit updated:
-
-
Removed air alarm in Snow Snaxi in Tcomms Sat
-
Removed trash bins in genetics and mining
-
Gives cargo techs a cargolathe
-
-
Putnam3145 updated:
-
-
lost my mind just a couple of times
-
-
b1tt3r1n0 updated:
-
-
pouches, again, and and material pouches.
-
-
timothyteakettle updated:
-
-
support for custom blood colours implemented, slimes blood colour now equivalent to their body colour
-
-
-
27 June 2020
-
Detective-Google updated:
-
-
Lying down is better
-
-
timothyteakettle updated:
-
-
felinids now nya when tabled
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 139ca99af0..9d46e34381 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -27127,3 +27127,240 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- rscadd: you can now choose a body sprite as an anthromorph or anthromorphic insect,
and can choose from aquatic/avian and apid respectively (and obviously back
to the defaults too)
+2020-08-30:
+ raspy-on-osu:
+ - rscadd: new explosion echoes
+ - tweak: explosion echo range
+ - soundadd: 5 new explosion related sounds
+2020-08-31:
+ Arturlang:
+ - tweak: Slimes can now damage structures, don't leave them unfed!
+ Chiirno:
+ - bugfix: Moves pill_bottles/dice to box/dice on CogStation.
+ Couls, ported by NecromancerAnne:
+ - code_imp: cleans up mech backstabbing code
+ DeltaFire15:
+ - rscdel: teleport-to-ark ability of the eminence, commented out
+ - rscadd: teleport-to-obelisk ability for the eminence
+ Detective-Google:
+ - tweak: plasmamen have no more slowdown
+ - rscadd: object reskins now use very nice and cool radials
+ EmeraldSundisk:
+ - rscadd: Adds a pool to MetaStation
+ - tweak: Slight readjustments to the surrounding area
+ - bugfix: Fixes a handful of external airlocks
+ ForrestWick:
+ - balance: removes wall walking boots from nukie uplink
+ - tweak: removes wall walking boots from nukie uplink
+ Ghommie:
+ - bugfix: e-gun overlays and some floor decals should have been fixed.
+ LetterN:
+ - rscadd: tgchat
+ Lynxless:
+ - tweak: Changed anatomic panacea into a direct buff, instead of a chem injection
+ - balance: Changed the values of anatomic panacea
+ - imageadd: Added a new icon for panacea's buff alert
+ Putnam3145:
+ - tweak: Pref for genital/vore examine text
+ - bugfix: Fixed a couple events having ghost roles eligible.
+ - balance: 'Buffed slaughter demon: gets stronger as it eats people'
+ - balance: 'Nerfed slaughter demon: no longer permanently round-removes all who
+ are eaten by it, instead releasing their now-heartless bodies'
+ - bugfix: Dynamic storytellers now calculate property weights properly.
+ Sonic121x:
+ - bugfix: Fix the four type of new tea that will stuck inside your vein.
+ - rscadd: drinking glass sprite for those tea.
+ kappa-sama:
+ - balance: miners can no longer acquire funny antag item
+ lolman360:
+ - bugfix: shuttle engine/heater sprites now face the right way
+ raspy-on-osu:
+ - tweak: TEG power output
+ - tweak: tesla movement priorities
+ - rscadd: tesla counterplay
+ - rscadd: tesla containment check (containment variable now usable)
+ silicons:
+ - bugfix: brooms now sweep objects on MOVABLE_PRE_MOVE rather than MOVABLE_MOVED
+ - balance: firedoors no longer automatically open on touch when there's no pressure
+ differences.
+ timothyteakettle:
+ - tweak: buzz, buzz2 and ping are now all unrestricted emotes and can be used by
+ anyone
+ - imagedel: the drake credit and pickle credit sprites have been removed
+ - refactor: tongue speech handling is now done by accent datums
+ zeroisthebiggay:
+ - rscdel: waffleco
+2020-09-01:
+ BlueWildrose:
+ - bugfix: fixed slimes starting off hungry
+2020-09-02:
+ Putnam3145:
+ - code_imp: Added a unit test for character saving.
+ - balance: Plastitanium rapier no longer silently sleeps with no chance at counterplay
+ when used by pacifists.
+ - bugfix: Fusion scan is now actually useful.
+ Tupinambis:
+ - tweak: moved the dakis, genital growth pills, and genital autosurgeons out of
+ the maintenance loot table and into kinkmates.
+ raspy-on-osu:
+ - bugfix: pyroclastic anomaly client spam
+ timothyteakettle:
+ - rscadd: you can hide your ckey now from the roundend report
+2020-09-03:
+ Ghommie:
+ - bugfix: Jaunters should now work with magic mirror chasming.
+ - bugfix: The photocopier can now print more than one copy at a time.
+ - bugfix: Alkali perspiration infos don't crash the Pandemic UI anymore.
+ - bugfix: Windoors can be actually tinted now.
+2020-09-04:
+ timothyteakettle:
+ - bugfix: ipcs can speak
+2020-09-05:
+ Bhijn:
+ - rscadd: Readded the old method of temperature notifications in the form of a new
+ pair of shivering/sweating notifications.
+ Putnam3145:
+ - tweak: Hilbert hotel flavor text for one particular snowflake hotel changed.
+ - admin: admins can now actually reduce threat level in dynamic
+ - tweak: Made owo.ogg smaller.
+ - tweak: Character saving unit test is now more verbose on failure.
+ - refactor: Added an extools proc hook alternative to rust_g logging.
+ raspy-on-osu:
+ - tweak: supermatter shard examine text
+ - tweak: protolathe item categories
+2020-09-06:
+ Putnam3145:
+ - rscdel: Dynamic no longer pushes events.
+ - balance: Made spontaneous brain trauma a good deal less annoying.
+ lolman360, NecromancerAnne:
+ - rscadd: The ancient art of blacksmithing, now in pixels.
+ - imageadd: cool swords and shit (thanks anne)
+ raspy-on-osu:
+ - tweak: thermomachine examine text
+2020-09-07:
+ DeltaFire15:
+ - spellcheck: fixed a typo causing a span not to show.
+ Putnam for debugging ammo loading! Thx!!:
+ - rscdel: Removed spell blade, diamond picaxe, and fire wand from lava land loot
+ tables
+ - tweak: Each mini boss now has its own type of crate.
+ - tweak: Each spike has its own type of crate that it pulls its now smaller loot
+ table from
+ - balance: Moved god eye from spike loot table to hard spawn collosses fight
+ - balance: Moved holoparasight from spike loot table to bubble gum
+ - balance: Moved magic meat hook from spike loot table to drake
+ - rscadd: 2 more crates to Arena Shuttle as well as 4-4-4 of each new type of chest
+ - balance: Replaced the diamond pick loot with a better one
+ - tweak: Replaced the cursted katana with a non cursted verson that deals half the
+ damage and has less block!
+ - rscadd: Three new potions, blue heals the mind like a mama potion, Green heals
+ the soul aka the organs! Lastly Red heals the body, by 100 damage of each main
+ types. Best not to waste them!
+ - rscadd: Four more "wands" Spellbooks! These fun little guys shoot out its own
+ pages to do the affect, one will set the target on fire like a bullet, one will
+ harm them a bit, one will heal the target a small bit - How nice! Last one will
+ give them a few statis affects like a taser bolt but without as much power or
+ tasing affect
+2020-09-08:
+ Ghommie:
+ - spellcheck: fixed names of the Electrical Toolbox goodie pack and green croptop
+ christmas suit.
+ - bugfix: Fixed turf visuals for real. Original PR by AnturK on tgstation.
+ KeRSedChaplain:
+ - soundadd: added borg_deathsound.ogg and android_scream.ogg
+ silicons:
+ - balance: meteor waves now have a static 5 minute timer.
+2020-09-09:
+ Putnam3145:
+ - bugfix: Made superconductivity work for the first time literally ever.
+ timothyteakettle:
+ - bugfix: accents work better
+2020-09-11:
+ Putnam3145:
+ - balance: Superconducting turfs now can't go below 0 celsius.
+2020-09-12:
+ BlueWildrose:
+ - bugfix: The Polychromic winter coat's hoodie will now polychrome, alongside any
+ other new polychromic items with toggleable headwear.
+ - bugfix: Minesweeper will no longer blow up the player's ears when they select
+ "Play on the same board"
+ - rscadd: Slimepeople will find warm donk pockets among other toxin healing items
+ even more repulsive, as they are anti-toxic.
+ - tweak: Slimepeople are now neutral to gross foods.
+ DeltaFire15:
+ - balance: AIs can no longer qdel() the gravity generator
+ Putnam3145:
+ - code_imp: Added some unit tests for reactions.
+ - refactor: replaced handle_changeling and handle_bloodsucker with signal registration
+ Sonic121x:
+ - bugfix: Fixed pill button on chemical press
+ TheObserver-sys:
+ - rscadd: Brass now has a proper datum. Aspiring Forgetenders rejoice!
+ Trilbyspaceclone:
+ - bugfix: Race based drinks will no longer stay inside your blood for ever.
+ Tupinambis:
+ - tweak: Redid Cogstation atmos pipes to make it less cluttered.
+ - tweak: Removed a few doors from the main hallway to mitigate chokepoint issues
+ - bugfix: All belt hell conveyers are now on by default, so that belt hell actually
+ works.
+ - bugfix: IDs for poddoors and belts and the like. Everything is now properly hooked
+ and should work as expected (except for the pressure triggered mass drivers)
+ - bugfix: addresses most if not all roundstart active turfs.
+ - bugfix: Issue where wires were connected to the SMES improperly, and SMES were
+ not properly precharged, resulting in power failure earlier than intended.
+ - bugfix: various rogue turfs and wirings.
+ - bugfix: security office APC being hooked to maintenance for some reason.
+ - bugfix: TEG is now directly wired to the SMES.
+ - code_imp: 'adds a subtype of mass drivers that is triggered by things being on
+ it. TODO: Make these mass drivers trigger poddoors, to make belt hell fully
+ functional.'
+ lolman360:
+ - bugfix: glaives now work again
+ zeroisthebiggay:
+ - balance: Revolver is now poplocked down to fifteen people.
+2020-09-16:
+ timothyteakettle:
+ - tweak: fixed an icon path
+2020-09-17:
+ DeltaFire15:
+ - rscadd: Failing the plushmium reaction can now create peculiar plushies, depending
+ on reaction volume.
+ - bugfix: The mood-buff from petting a plushie now works properly again.
+ - bugfix: Fixed wacky necropolis loot chest behavior
+ EmeraldSundisk:
+ - rscadd: Adds the Research Director's office to Omega Station
+ - rscadd: Adds 2 new solar arrays (and control rooms)
+ - rscadd: Adds some action figures that weren't there previously
+ - rscadd: The CMO's office now has a light switch
+ - tweak: Slight readjustments to impacted areas
+ - tweak: Readjusts the toxins air supply line to (ideally) be easier to service
+ - bugfix: Department camera consoles should now be able to actually check appropriate
+ cameras
+ - bugfix: Xenobiology can now be locked down (by the Research Director)
+ MrJWhit:
+ - rscadd: Adds a brain damage line
+ Putnam3145:
+ - bugfix: Your balls finally feel full, again.
+ timothyteakettle:
+ - rscadd: due to changes in policy, and several lawsuits, Nanotrasen has been forced
+ to allow disabled people to sign up
+2020-09-20:
+ DeltaFire15:
+ - bugfix: Sutures work on simplemobs again.
+ - bugfix: Attacking dismembered bodyparts now targets the chest instead, for weapons
+ aswell as unarmed attacks.
+ MrJWhit:
+ - rscadd: New sprites for chess pieces! You can craft them in-game with metal sheets.
+ silicons:
+ - balance: hulks can smash again (walls no longer break their hands)
+ - rscdel: acid no longer degrades armor
+2020-09-22:
+ Arturlang:
+ - rscadd: TGUI Statpanel
+ YakumoChen:
+ - imageadd: Mechsuits, robotics jumpsuits added to RoboDrobe
+ timothyteakettle:
+ - tweak: character previews should be more consistent now
+2020-09-24:
+ Putnam3145:
+ - refactor: Atmos is free.
diff --git a/html/changelogs/AutoChangeLog-pr-13430.yml b/html/changelogs/AutoChangeLog-pr-13430.yml
new file mode 100644
index 0000000000..94c68481da
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13430.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "Removed a non-working proc that already had its functionality implemented in another proc in the same file."
diff --git a/code/modules/goonchat/browserassets/js/jquery.min.js b/html/jquery.min.js
similarity index 100%
rename from code/modules/goonchat/browserassets/js/jquery.min.js
rename to html/jquery.min.js
diff --git a/html/statbrowser.html b/html/statbrowser.html
new file mode 100644
index 0000000000..fbdbb61449
--- /dev/null
+++ b/html/statbrowser.html
@@ -0,0 +1,1004 @@
+
+
+
+Stat Browser
+
+
+
+
+
+
+
+