"
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..7f5fafcd80 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)
@@ -542,7 +541,7 @@
. = ..()
if(!(stat & (BROKEN|NOPOWER)) && !(. & EMP_PROTECT_SELF))
for(var/datum/data/record/R in GLOB.data_core.medical)
- if(prob(10/severity))
+ if(prob(severity/10))
switch(rand(1,6))
if(1)
if(prob(10))
@@ -561,7 +560,7 @@
R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable")
continue
- else if(prob(1))
+ else if(prob(severity/80))
qdel(R)
continue
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..0157f4ca87 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
@@ -770,7 +769,7 @@ What a mess.*/
return
for(var/datum/data/record/R in GLOB.data_core.security)
- if(prob(10/severity))
+ if(prob(severity/10))
switch(rand(1,8))
if(1)
if(prob(10))
@@ -795,7 +794,7 @@ What a mess.*/
R.fields["photo_side"] = G.fields["photo_side"]
continue
- else if(prob(1))
+ else if(prob(severity/80))
qdel(R)
continue
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..b48742d885 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
@@ -356,23 +355,23 @@
cryo_items -= I
//Update any existing objectives involving this mob.
- for(var/datum/objective/O in GLOB.objectives)
+ for(var/i in GLOB.objectives)
+ var/datum/objective/O = i
// We don't want revs to get objectives that aren't for heads of staff. Letting
// them win or lose based on cryo is silly so we remove the objective.
if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind)
qdel(O)
- else if(O.target && istype(O.target, /datum/mind))
- if(O.target == mob_occupant.mind)
- if(O.owner && O.owner.current)
- to_chat(O.owner.current, " You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")
- O.target = null
- spawn(10) //This should ideally fire after the occupant is deleted.
- if(!O)
- return
- O.find_target()
- O.update_explanation_text()
- if(!(O.target))
- qdel(O)
+ else if(O.target && istype(O.target, /datum/mind) && !O.check_completion())
+ if(O.target == mob_occupant.mind && O.owner?.current)
+ to_chat(O.owner.current, " You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")
+ O.target = null
+ spawn(10) //This should ideally fire after the occupant is deleted.
+ if(!O)
+ return
+ O.find_target()
+ O.update_explanation_text()
+ if(!(O.target))
+ qdel(O)
if(mob_occupant.mind)
//Handle job slot/tater cleanup.
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index b34e97d374..fe2bdf90b1 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -612,7 +612,7 @@
. += "The cogwheel has been loosened, but remains connected loosely to the door!"
/obj/machinery/door/airlock/clockwork/emp_act(severity)
- if(prob(80/severity))
+ if(prob(severity/1.25))
open()
/obj/machinery/door/airlock/clockwork/canAIControl(mob/user)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index d4ba70bb6c..1faff76626 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -243,13 +243,8 @@
. = ..()
if (. & EMP_PROTECT_SELF)
return
- if(prob(20/severity) && (istype(src, /obj/machinery/door/airlock) || istype(src, /obj/machinery/door/window)) )
+ if(prob(severity/5) && (istype(src, /obj/machinery/door/airlock) || istype(src, /obj/machinery/door/window)) )
INVOKE_ASYNC(src, .proc/open)
- if(prob(severity*10 - 20))
- if(secondsElectrified == MACHINE_NOT_ELECTRIFIED)
- secondsElectrified = MACHINE_ELECTRIFIED_PERMANENT
- LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\]EM Pulse")
- addtimer(CALLBACK(src, .proc/unelectrify), 300)
/obj/machinery/door/proc/unelectrify()
secondsElectrified = MACHINE_NOT_ELECTRIFIED
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/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 22bacf6aa1..3cd2011334 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -397,7 +397,7 @@
return ..()
/obj/machinery/door/window/clockwork/emp_act(severity)
- if(prob(80/severity))
+ if(prob(severity/1.25))
open()
/obj/machinery/door/window/clockwork/ratvar_act()
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/firealarm.dm b/code/game/machinery/firealarm.dm
index 000b3dc7b5..ca9ce09ced 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -103,7 +103,7 @@
if (. & EMP_PROTECT_SELF)
return
- if(prob(50 / severity))
+ if(prob(severity/1.8))
alarm()
/obj/machinery/firealarm/emag_act(mob/user)
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index bf71786a05..ff6f96a29f 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -3,13 +3,14 @@
/obj/machinery/iv_drip
name = "\improper IV drip"
- desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate."
+ desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. Alt-Click to change the transfer rate."
icon = 'icons/obj/iv_drip.dmi'
icon_state = "iv_drip"
anchored = FALSE
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
var/mob/living/carbon/attached
var/mode = IV_INJECTING
+ var/dripfeed = FALSE
var/obj/item/reagent_containers/beaker
var/static/list/drip_containers = typecacheof(list(/obj/item/reagent_containers/blood,
/obj/item/reagent_containers/food,
@@ -132,9 +133,11 @@
if(mode)
if(beaker.reagents.total_volume)
var/transfer_amount = 5
+ if (dripfeed)
+ transfer_amount = 1
if(istype(beaker, /obj/item/reagent_containers/blood))
// speed up transfer on blood packs
- transfer_amount = 10
+ transfer_amount *= 2
var/fraction = min(transfer_amount/beaker.reagents.total_volume, 1) //the fraction that is transfered of the total volume
beaker.reagents.reaction(attached, INJECT, fraction, FALSE) //make reagents reacts, but don't spam messages
beaker.reagents.trans_to(attached, transfer_amount)
@@ -169,6 +172,16 @@
else
toggle_mode()
+/obj/machinery/iv_drip/AltClick(mob/living/user)
+ if(!user.canUseTopic(src, be_close=TRUE))
+ return
+ if(dripfeed)
+ dripfeed = FALSE
+ to_chat(usr, "You loosen the valve to speed up the [src].")
+ else
+ dripfeed = TRUE
+ to_chat(usr, "You tighten the valve to slowly drip-feed the contents of [src].")
+
/obj/machinery/iv_drip/attack_robot(mob/user)
if(Adjacent(user))
attack_hand(user)
@@ -227,7 +240,7 @@
/obj/machinery/iv_drip/telescopic
name = "telescopic IV drip"
- desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down."
+ desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down.Alt-Click with a beaker attached to change the transfer rate."
icon_state = "iv_drip"
/obj/machinery/iv_drip/telescopic/update_icon_state()
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/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 54d320e93b..34745a65f6 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -300,6 +300,7 @@
else
to_chat(user, "You remove the turret but did not manage to salvage anything.")
qdel(src)
+ return
else if((istype(I, /obj/item/wrench)) && (!on))
if(raised)
@@ -718,9 +719,7 @@
/obj/machinery/porta_turret/syndicate/ComponentInitialize()
. = ..()
- // AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
- AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) //this one or ^ one?
-
+ AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
/obj/machinery/porta_turret/syndicate/setup()
return
diff --git a/code/game/machinery/porta_turret/portable_turret_construct.dm b/code/game/machinery/porta_turret/portable_turret_construct.dm
index 7c2000175e..2a73ee8d89 100644
--- a/code/game/machinery/porta_turret/portable_turret_construct.dm
+++ b/code/game/machinery/porta_turret/portable_turret_construct.dm
@@ -151,6 +151,7 @@
turret.installation = installed_gun.type
turret.setup(installed_gun)
qdel(src)
+ return
else if(istype(I, /obj/item/crowbar))
I.play_tool_sound(src, 75)
diff --git a/code/game/machinery/poweredfans/fan_assembly.dm b/code/game/machinery/poweredfans/fan_assembly.dm
new file mode 100644
index 0000000000..651b3342ac
--- /dev/null
+++ b/code/game/machinery/poweredfans/fan_assembly.dm
@@ -0,0 +1,100 @@
+/obj/machinery/fan_assembly
+ name = "fan assembly"
+ desc = "A basic microfan assembly."
+ icon = 'icons/obj/poweredfans.dmi'
+ icon_state = "mfan_assembly"
+ max_integrity = 150
+ use_power = NO_POWER_USE
+ power_channel = ENVIRON
+ idle_power_usage = 0
+ active_power_usage = 0
+ layer = ABOVE_NORMAL_TURF_LAYER
+ anchored = FALSE
+ density = FALSE
+ CanAtmosPass = ATMOS_PASS_YES
+ stat = 1
+ var/buildstacktype = /obj/item/stack/sheet/plasteel
+ var/buildstackamount = 5
+ /*
+ 1 = Wrenched in place
+ 2 = Welded in place
+ 3 = Wires attached to it, this makes it change to the full thing.
+ */
+
+/obj/machinery/fan_assembly/attackby(obj/item/W, mob/living/user, params)
+ switch(stat)
+ if(1)
+ // Stat 1
+ if(istype(W, /obj/item/weldingtool))
+ if(weld(W, user))
+ to_chat(user, "You weld the fan assembly securely into place.")
+ setAnchored(TRUE)
+ stat = 2
+ update_icon_state()
+ return
+ if(2)
+ // Stat 2
+ if(istype(W, /obj/item/stack/cable_coil))
+ if(!W.tool_start_check(user, amount=2))
+ to_chat(user, "You need two lengths of cable to wire the fan assembly!")
+ return
+ to_chat(user, "You start to add wires to the assembly...")
+ if(W.use_tool(src, user, 30, volume=50, amount=2))
+ to_chat(user, "You add wires to the fan assembly.")
+ stat = 3
+ var/obj/machinery/poweredfans/F = new(loc, src)
+ forceMove(F)
+ F.setDir(src.dir)
+ return
+ else if(istype(W, /obj/item/weldingtool))
+ if(weld(W, user))
+ to_chat(user, "You unweld the fan assembly from its place.")
+ stat = 1
+ update_icon_state()
+ setAnchored(FALSE)
+ return
+ return ..()
+
+/obj/machinery/fan_assembly/wrench_act(mob/user, obj/item/I)
+ if(stat != 1)
+ return FALSE
+ user.visible_message("[user] disassembles [src].",
+ "You start to disassemble [src]...", "You hear wrenching noises.")
+ if(I.use_tool(src, user, 30, volume=50))
+ deconstruct()
+ return TRUE
+
+/obj/machinery/fan_assembly/proc/weld(obj/item/weldingtool/W, mob/living/user)
+ if(!W.tool_start_check(user, amount=0))
+ return FALSE
+ switch(stat)
+ if(1)
+ to_chat(user, "You start to weld \the [src]...")
+ if(2)
+ to_chat(user, "You start to unweld \the [src]...")
+ if(W.use_tool(src, user, 30, volume=50))
+ return TRUE
+ return FALSE
+
+/obj/machinery/fan_assembly/deconstruct(disassembled = TRUE)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ new buildstacktype(loc,buildstackamount)
+ qdel(src)
+
+/obj/machinery/fan_assembly/examine(mob/user)
+ . = ..()
+ switch(stat)
+ if(1)
+ to_chat(user, "The fan assembly seems to be unwelded and loose.")
+ if(2)
+ to_chat(user, "The fan assembly seems to be welded, but missing wires.")
+ if(3)
+ to_chat(user, "The outer plating is wired firmly in place.")
+
+/obj/machinery/fan_assembly/update_icon_state()
+ . = ..()
+ switch(stat)
+ if(1)
+ icon_state = "mfan_assembly"
+ if(2)
+ icon_state = "mfan_welded"
diff --git a/code/game/machinery/poweredfans/poweredfans.dm b/code/game/machinery/poweredfans/poweredfans.dm
new file mode 100644
index 0000000000..3349a6894e
--- /dev/null
+++ b/code/game/machinery/poweredfans/poweredfans.dm
@@ -0,0 +1,56 @@
+/obj/machinery/poweredfans
+ icon = 'icons/obj/poweredfans.dmi'
+ icon_state = "mfan_powered"
+ name = "micro powered fan"
+ desc = "A handmade fan, releasing a thin gust of air."
+ use_power = ACTIVE_POWER_USE
+ power_channel = ENVIRON
+ idle_power_usage = 5
+ active_power_usage = 10
+ max_integrity = 150
+ layer = ABOVE_NORMAL_TURF_LAYER
+ anchored = TRUE
+ density = FALSE
+ CanAtmosPass = ATMOS_PASS_NO
+ var/obj/machinery/fan_assembly/assembly
+
+/obj/machinery/poweredfans/deconstruct(disassembled = TRUE)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ if(!assembly)
+ assembly = new()
+ assembly.forceMove(drop_location())
+ assembly.stat = 2
+ assembly.setAnchored(TRUE)
+ assembly.setDir(dir)
+ assembly = null
+ new /obj/item/stack/cable_coil(loc, 2)
+ qdel(src)
+
+/obj/machinery/poweredfans/wirecutter_act(mob/living/user, obj/item/I)
+ user.visible_message("[user] removes the wires from the [src].",
+ "You start to remove the wires from the [src]...", "You hear clanking and banging noises.")
+ if(I.use_tool(src, user, 30, volume=50))
+ deconstruct()
+ return TRUE
+
+/obj/machinery/poweredfans/Initialize(mapload, obj/machinery/fan_assembly/FA)
+ . = ..()
+ if(FA)
+ assembly = FA
+ else
+ assembly = new(src)
+ assembly.stat = 3
+ air_update_turf(1)
+
+/obj/machinery/poweredfans/power_change()
+ ..()
+ if(powered())
+ icon_state = "mfan_powered"
+ CanAtmosPass = ATMOS_PASS_NO
+ air_update_turf(1)
+ else
+ icon_state = "mfan_unpowered"
+ CanAtmosPass = ATMOS_PASS_YES
+ air_update_turf(1)
+ update_icon_state()
+
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/shieldgen.dm b/code/game/machinery/shieldgen.dm
index 6793d361b5..8a0f376b0f 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -25,11 +25,10 @@
. = ..()
if (. & EMP_PROTECT_SELF)
return
- switch(severity)
- if(1)
- qdel(src)
- if(2)
- take_damage(50, BRUTE, "energy", 0)
+ if(severity >= 70)
+ qdel(src)
+ else
+ take_damage(severity/1.3, BRUTE, "energy", 0)
/obj/structure/emergency_shield/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 84b205b585..6282d74346 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)
@@ -151,9 +150,9 @@
. = ..()
if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
return
- if(prob(15 * severity))
+ if(prob(1500 / severity))
return
- if(prob(1)) // :^)
+ if(prob(1 * severity/100)) // :^)
obj_flags |= EMAGGED
var/severity_ascending = 4 - severity
money = max(rand(money - (200 * severity_ascending), money + (200 * severity_ascending)), 0)
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index e1b345f507..f75eef0b21 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -119,7 +119,7 @@ GLOBAL_LIST_EMPTY(telecomms_list)
/obj/machinery/telecomms/proc/update_power()
if(toggled)
// if powered, on. if not powered, off. if too damaged, off
- if(CHECK_BITFIELD(stat, (BROKEN | NOPOWER | EMPED)))
+ if(CHECK_BITFIELD(stat, (BROKEN | NOPOWER | EMPED)))
on = FALSE
else
on = TRUE
@@ -139,9 +139,9 @@ GLOBAL_LIST_EMPTY(telecomms_list)
. = ..()
if(CHECK_BITFIELD(., EMP_PROTECT_SELF))
return
- if(prob(100 / severity))
+ if(prob(severity))
if(!CHECK_BITFIELD(stat, EMPED))
ENABLE_BITFIELD(stat, EMPED)
- var/duration = (300 * 10) / severity
+ var/duration = severity * 35
spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot.
DISABLE_BITFIELD(stat, EMPED)
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/combat/neovgre.dm b/code/game/mecha/combat/neovgre.dm
index 1bd68546c8..584a2d007e 100644
--- a/code/game/mecha/combat/neovgre.dm
+++ b/code/game/mecha/combat/neovgre.dm
@@ -64,6 +64,8 @@
/obj/mecha/combat/neovgre/process()
..()
+ if(!obj_integrity) //Integrity is zero but we would heal out of that state if we went into this before it recognises it being zero
+ return
if(GLOB.ratvar_awakens) // At this point only timley intervention by lord singulo could hope to stop the superweapon
cell.charge = INFINITY
max_integrity = INFINITY
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index 50d18c6fda..0e47872221 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -131,7 +131,7 @@
if(recharging)
return
if(chassis)
- chassis.emp_act(EMP_HEAVY)
+ chassis.emp_act(80)
addtimer(CALLBACK(src, /obj/item/mecha_parts/mecha_tracking/proc/recharge), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
recharging = TRUE
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 40336fb01e..d5b27a9636 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))
@@ -142,8 +140,8 @@
if (. & EMP_PROTECT_SELF)
return
if(get_charge())
- use_power((cell.charge/3)/(severity*2))
- take_damage(30 / severity, BURN, "energy", 1)
+ use_power(cell.charge*severity/100)
+ take_damage(severity/3, BURN, "energy", 1)
mecha_log_message("EMP detected", color="red")
if(istype(src, /obj/mecha/combat))
@@ -268,12 +266,8 @@
return 1
else if(istype(W, /obj/item/mecha_parts/mecha_tracking))
- if(!user.transferItemToLoc(W, src))
- to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]!")
- return
- trackers += W
- user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].")
- diag_hud_set_mechtracking()
+ var/obj/item/mecha_parts/mecha_tracking/tracker = W
+ tracker.try_attach_part(user, src)
return
else
return ..()
@@ -315,7 +309,7 @@
clearInternalDamage(MECHA_INT_CONTROL_LOST)
/obj/mecha/narsie_act()
- emp_act(EMP_HEAVY)
+ emp_act(100)
/obj/mecha/ratvar_act()
if((GLOB.ratvar_awakens || GLOB.clockwork_gateway_activated) && occupant)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 41270bb09e..9d6d034364 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,19 @@
S.rabid = TRUE
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
- offer_control(S)
+ var/list/candidates = pollCandidatesForMob("Do you want to play as a pyroclastic anomaly slime?",ROLE_SENTIENCE,null,ROLE_SENTIENCE,100,S,POLL_IGNORE_SENTIENCE_POTION)
+ if(length(candidates))
+ var/mob/C = pick(candidates)
+ message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(S)])")
+ C.transfer_ckey(S, FALSE)
+ var/list/policies = CONFIG_GET(keyed_list/policyconfig)
+ var/policy = policies[POLICYCONFIG_ON_PYROCLASTIC_SENTIENT]
+ if(policy)
+ to_chat(S,policy)
+ return TRUE
+ else
+ message_admins("No ghosts were willing to take control of [ADMIN_LOOKUPFLW(S)])")
+ return FALSE
/////////////////////
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/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm
index 19b0dc76dd..a388d3bc67 100644
--- a/code/game/objects/effects/effect_system/effects_sparks.dm
+++ b/code/game/objects/effects/effect_system/effects_sparks.dm
@@ -63,3 +63,23 @@
/datum/effect_system/lightning_spread
effect_type = /obj/effect/particle_effect/sparks/electricity
+
+//fake sparks, not subtyped because we don't want light/heat, nor checks inside an often used proc for a rare subcase for saving like 10 lines of code
+/obj/effect/particle_effect/fake_sparks
+ name = "lightning"
+ icon_state = "electricity"
+
+/obj/effect/particle_effect/fake_sparks/Initialize()
+ . = ..()
+ flick(icon_state, src) // replay the animation
+ playsound(src, "sparks", 100, TRUE)
+ QDEL_IN(src, 20)
+
+/datum/effect_system/fake_spark_spread
+ effect_type = /obj/effect/particle_effect/fake_sparks
+
+/proc/do_fake_sparks(n, c, source)
+ var/datum/effect_system/fake_spark_spread/sparks = new
+ sparks.set_up(n, c, source)
+ sparks.autocleanup = TRUE
+ sparks.start()
diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm
index b63a126c5d..235fbf6a9f 100644
--- a/code/game/objects/empulse.dm
+++ b/code/game/objects/empulse.dm
@@ -1,32 +1,28 @@
-/proc/empulse(turf/epicenter, heavy_range, light_range, log=0)
+/proc/empulse(turf/epicenter, power, log=0)
if(!epicenter)
return
if(!isturf(epicenter))
epicenter = get_turf(epicenter.loc)
- if(log)
- message_admins("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
- log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
+ var/max_distance = max(round((power/7)^0.7), 1)
- if(heavy_range >= 1)
+ if(log)
+ message_admins("EMP with power [power], max distance [max_distance] in area [epicenter.loc.name] ")
+ log_game("EMP with power [power], max distance [max_distance] in area [epicenter.loc.name] ")
+
+ if(power > 100)
new /obj/effect/temp_visual/emp/pulse(epicenter)
- if(heavy_range > light_range)
- light_range = heavy_range
-
- for(var/A in spiral_range(light_range, epicenter))
+ for(var/A in spiral_range(max_distance, epicenter))
var/atom/T = A
var/distance = get_dist(epicenter, T)
- if(distance < 0)
- distance = 0
- if(distance < heavy_range)
- T.emp_act(EMP_HEAVY)
- else if(distance == heavy_range)
- if(prob(50))
- T.emp_act(EMP_HEAVY)
- else
- T.emp_act(EMP_LIGHT)
- else if(distance <= light_range)
- T.emp_act(EMP_LIGHT)
+ var/severity = 100
+ if(distance != 0) //please dont divide by 0
+ severity = min(max((max_distance / distance^0.3) * (100/max_distance), 1),100) //if it goes below 1 or above 100 stuff gets bad
+ T.emp_act(severity)
return 1
+
+/proc/empulse_using_range(turf/epicenter, range, log=0) //make an emp using range instead of power
+ var/power_from_range = (7*(range^(1/0.7)))
+ empulse(epicenter, power_from_range, log)
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/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm
index 0acb225772..21cfd1fbad 100644
--- a/code/game/objects/items/credit_holochip.dm
+++ b/code/game/objects/items/credit_holochip.dm
@@ -101,7 +101,7 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- var/wipe_chance = 60 / severity
+ var/wipe_chance = severity/1.5
if(prob(wipe_chance))
visible_message("[src] fizzles and disappears!")
qdel(src) //rip cash
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 767f8fc395..484901990a 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -140,7 +140,7 @@
/obj/item/defibrillator/emp_act(severity)
. = ..()
if(cell && !(. & EMP_PROTECT_CONTENTS))
- deductcharge(1000 / severity)
+ deductcharge(severity*10)
if (. & EMP_PROTECT_SELF)
return
if(safety)
@@ -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..c09d23aafb 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)
@@ -1206,7 +1206,7 @@ GLOBAL_LIST_EMPTY(PDAs)
A.emp_act(severity)
if (!(. & EMP_PROTECT_SELF))
emped += 1
- spawn(200 * severity)
+ spawn(2 * severity)
emped -= 1
/proc/get_viewable_pdas()
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/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm
index 6b53a9ba46..e20321414d 100644
--- a/code/game/objects/items/devices/camera_bug.dm
+++ b/code/game/objects/items/devices/camera_bug.dm
@@ -225,7 +225,7 @@
if(C && istype(C) && C.bug == src)
if(!same_z_level(C))
return
- C.emp_act(EMP_HEAVY)
+ C.emp_act(80)
C.bug = null
bugged_cameras -= C.c_tag
interact()
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 75024aec26..035375fdaf 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -428,7 +428,7 @@
else
A.visible_message("[user] blinks \the [src] at \the [A].")
to_chat(user, "\The [src] now has [emp_cur_charges] charge\s.")
- A.emp_act(EMP_HEAVY)
+ A.emp_act(80)
else
to_chat(user, "\The [src] needs time to recharge!")
return
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index c931d92379..6d10d31f5c 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -128,7 +128,7 @@
else if(istype(target, /obj/machinery/camera))
var/obj/machinery/camera/C = target
if(prob(effectchance * diode.rating))
- C.emp_act(EMP_HEAVY)
+ C.emp_act(80)
outmsg = "You hit the lens of [C] with [src], temporarily disabling the camera!"
log_combat(user, C, "EMPed", src)
else
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/electrostaff.dm b/code/game/objects/items/electrostaff.dm
index 9750994c87..65d2fdd699 100644
--- a/code/game/objects/items/electrostaff.dm
+++ b/code/game/objects/items/electrostaff.dm
@@ -261,4 +261,4 @@
if (!(. & EMP_PROTECT_SELF))
turn_off()
if(!iscyborg(loc))
- deductcharge(1000 / severity, TRUE, FALSE)
+ deductcharge(severity*10, TRUE, FALSE)
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index 979fa7e958..6ab12b7e39 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -351,7 +351,7 @@
/obj/item/book/granter/spell/charge/recoil(mob/user)
..()
to_chat(user,"[src] suddenly feels very warm!")
- empulse(src, 1, 1)
+ empulse_using_range(src, 1)
/obj/item/book/granter/spell/summonitem
spell = /obj/effect/proc_holder/spell/targeted/summonitem
diff --git a/code/game/objects/items/grenades/emgrenade.dm b/code/game/objects/items/grenades/emgrenade.dm
index 815a3b343c..56b00bd2d6 100644
--- a/code/game/objects/items/grenades/emgrenade.dm
+++ b/code/game/objects/items/grenades/emgrenade.dm
@@ -7,5 +7,5 @@
/obj/item/grenade/empgrenade/prime(mob/living/lanced_by)
. = ..()
update_mob()
- empulse(src, 4, 10)
+ empulse_using_range(src, 14)
qdel(src)
diff --git a/code/game/objects/items/implants/implant_hijack.dm b/code/game/objects/items/implants/implant_hijack.dm
index 5dd610059c..36e58d8887 100644
--- a/code/game/objects/items/implants/implant_hijack.dm
+++ b/code/game/objects/items/implants/implant_hijack.dm
@@ -7,7 +7,8 @@
activated = 1
var/toggled = FALSE
icon_state = "hijack"
- var/eye_color
+ var/left_eye_color
+ var/right_eye_color
var/stealthmode = FALSE
var/stealthcooldown = 0
var/hijacking = FALSE
@@ -25,8 +26,10 @@
return
var/on = toggled && !stealthmode
var/mob/living/carbon/human/H = imp_in
- H.eye_color = on ? "ff0" : eye_color
- H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
+ H.left_eye_color = on ? "ff0" : left_eye_color
+ H.right_eye_color = on ? "ff0" : right_eye_color
+ H.dna.update_ui_block(DNA_LEFT_EYE_COLOR_BLOCK)
+ H.dna.update_ui_block(DNA_RIGHT_EYE_COLOR_BLOCK)
H.update_body()
/obj/item/implant/hijack/implant(mob/living/target, mob/user, silent = FALSE)
@@ -34,7 +37,8 @@
ADD_TRAIT(target, TRAIT_HIJACKER, "implant")
if (ishuman(target))
var/mob/living/carbon/human/H = target
- eye_color = H.eye_color
+ left_eye_color = H.left_eye_color
+ right_eye_color = H.right_eye_color
return TRUE
/obj/item/implant/hijack/removed(mob/living/source, silent = FALSE, special = 0)
@@ -49,7 +53,8 @@
apc.update_icon()
if (ishuman(source))
var/mob/living/carbon/human/H = source
- H.eye_color = eye_color
+ H.left_eye_color = left_eye_color
+ H.right_eye_color = left_eye_color
return TRUE
/obj/item/implant/hijack/proc/InterceptClickOn(mob/living/user,params,atom/object)
diff --git a/code/game/objects/items/implants/implant_misc.dm b/code/game/objects/items/implants/implant_misc.dm
index 08d6a586c8..36f82b599c 100644
--- a/code/game/objects/items/implants/implant_misc.dm
+++ b/code/game/objects/items/implants/implant_misc.dm
@@ -65,7 +65,7 @@
/obj/item/implant/emp/activate()
. = ..()
uses--
- empulse(imp_in, 3, 5)
+ empulse_using_range(imp_in, 7)
if(!uses)
qdel(src)
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..9bc20565a6 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -150,6 +150,7 @@
if(squeak_override)
var/datum/component/squeak/S = GetComponent(/datum/component/squeak)
S?.override_squeak_sounds = squeak_override
+ snowflake_id = id
/obj/item/toy/plush/handle_atom_del(atom/A)
if(A == grenade)
@@ -167,7 +168,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 +689,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 +755,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 +766,82 @@ 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!")
+ if(victim.snowflake_id) //Snowflake code for snowflake plushies.
+ set_snowflake_from_config(victim.snowflake_id)
+ desc += " Wait, did it just move..?"
+ else
+ 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/rods.dm b/code/game/objects/items/stacks/rods.dm
index efcc075110..1277bb3d4e 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -2,6 +2,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("scooter frame", /obj/item/scooter_frame, 10, time = 25, one_per_turf = 0), \
+ new/datum/stack_recipe("railing", /obj/structure/railing, 3, time = 18, window_checks = TRUE), \
))
/obj/item/stack/rods
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..32d65041bc 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
@@ -178,6 +179,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
GLOBAL_LIST_INIT(plasteel_recipes, list ( \
new/datum/stack_recipe("AI core", /obj/structure/AIcore, 4, time = 50, one_per_turf = TRUE), \
new/datum/stack_recipe("bomb assembly", /obj/machinery/syndicatebomb/empty, 10, time = 50), \
+ new/datum/stack_recipe("micro powered fan assembly", /obj/machinery/fan_assembly, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
new /datum/stack_recipe_list("crates", list( \
new /datum/stack_recipe("gray crate", /obj/structure/closet/crate, 5, time = 50, one_per_turf = 1, on_floor = 1), \
new /datum/stack_recipe("internals crate", /obj/structure/closet/crate/internals, 5, time = 50, one_per_turf = 1, on_floor = 1), \
@@ -231,9 +233,13 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \
GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("wooden sandals", /obj/item/clothing/shoes/sandal, 1), \
new/datum/stack_recipe("tiki mask", /obj/item/clothing/mask/gas/tiki_mask, 2), \
- new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20), \
new/datum/stack_recipe("wood table frame", /obj/structure/table_frame/wood, 2, time = 10), \
null, \
+ new/datum/stack_recipe("wooden floor tile", /obj/item/stack/tile/wood, 1, 4, 20), \
+ new/datum/stack_recipe("large wooden floor tile", /obj/item/stack/tile/wood/wood_large, 1, 4, 20), \
+ new/datum/stack_recipe("tiled wooden floor tile", /obj/item/stack/tile/wood/wood_tiled, 1, 4, 20), \
+ new/datum/stack_recipe("diagonal wooden floor tile", /obj/item/stack/tile/wood/wood_diagonal, 1, 4, 20), \
+ null, \
new/datum/stack_recipe_list("pews", list(
new /datum/stack_recipe("pew (middle)", /obj/structure/chair/pew, 3, one_per_turf = TRUE, on_floor = TRUE),\
new /datum/stack_recipe("pew (left)", /obj/structure/chair/pew/left, 3, one_per_turf = TRUE, on_floor = TRUE),\
@@ -556,6 +562,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 +627,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 +640,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 +697,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/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 0d85e897d2..7a33134afd 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -154,13 +154,31 @@
//Wood
/obj/item/stack/tile/wood
- name = "wood floor tile"
+ name = "wooden plank floor tile"
singular_name = "wood floor tile"
desc = "An easy to fit wood floor tile."
icon_state = "tile-wood"
turf_type = /turf/open/floor/wood
resistance_flags = FLAMMABLE
+/obj/item/stack/tile/wood/wood_large
+ name = "large wooden plank floor tile"
+ singular_name = "large wooden plank floor tile"
+ icon_state = "tile-wood_large"
+ turf_type = /turf/open/floor/wood/wood_large
+
+/obj/item/stack/tile/wood/wood_tiled
+ name = "tiled wooden plank floor tile"
+ singular_name = "tiled wooden plank floor tile"
+ icon_state = "tile-wood_tile"
+ turf_type = /turf/open/floor/wood/wood_tiled
+
+/obj/item/stack/tile/wood/wood_diagonal
+ name = "diagonal wooden plank floor tile"
+ singular_name = "diagonal wooden plank floor tile"
+ icon_state = "tile-wood_diagonal"
+ turf_type = /turf/open/floor/wood/wood_diagonal
+
//Cloth Floors
/obj/item/stack/tile/padded
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/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index a6b34c59e3..73246276c2 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -281,6 +281,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
/obj/item/clothing/suit/armor/vest/infiltrator,
/obj/item/clothing/under/syndicate/bloodred,
/obj/item/clothing/gloves/color/latex/nitrile/infiltrator,
+ /obj/item/clothing/gloves/tackler/combat/insulated/infiltrator,
/obj/item/clothing/mask/infiltrator,
/obj/item/clothing/shoes/combat/sneakboots,
/obj/item/gun/ballistic/automatic/pistol,
@@ -292,7 +293,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
new /obj/item/clothing/head/helmet/infiltrator(src)
new /obj/item/clothing/suit/armor/vest/infiltrator(src)
new /obj/item/clothing/under/syndicate/bloodred(src)
- new /obj/item/clothing/gloves/color/latex/nitrile/infiltrator(src)
+ new /obj/item/clothing/gloves/tackler/combat/insulated/infiltrator(src)
new /obj/item/clothing/mask/infiltrator(src)
new /obj/item/clothing/shoes/combat/sneakboots(src)
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index 1a84b85a1a..5e76d506ba 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -232,7 +232,7 @@
if (!(. & EMP_PROTECT_SELF))
switch_status(FALSE)
if(!iscyborg(loc))
- deductcharge(1000 / severity, TRUE, FALSE)
+ deductcharge(severity*10, TRUE, FALSE)
/obj/item/melee/baton/stunsword
name = "stunsword"
diff --git a/code/game/objects/items/tanks/tank_types.dm b/code/game/objects/items/tanks/tank_types.dm
index 1c90a83c0a..d16d1b29f1 100644
--- a/code/game/objects/items/tanks/tank_types.dm
+++ b/code/game/objects/items/tanks/tank_types.dm
@@ -182,3 +182,20 @@
/obj/item/tank/internals/emergency_oxygen/double/empty/populate_gas()
return
+
+/*
+ * Methyl Bromide
+ */
+/obj/item/tank/internals/methyl_bromide
+ name = "mantid gas reactor"
+ desc = "A mantid gas processing plant that continuously synthesises 'breathable' atmosphere."
+ icon_state = "methyl_bromide"
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ force = 6
+ distribute_pressure = 14
+ volume = 10
+
+/obj/item/tank/internals/methyl_bromide/populate_gas()
+ air_contents.set_moles(/datum/gas/methyl_bromide, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ return
\ No newline at end of file
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index 2e12a2ab8c..4cada03307 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -110,13 +110,18 @@
var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected))
if(affecting && affecting.status == BODYPART_ROBOTIC && user.a_intent != INTENT_HARM)
+ //only heal to 25 if limb is damaged to or past 25 brute, otherwise heal normally
+ var/difference = affecting.brute_dam - 25
+ var/heal_amount = 15
+ if(difference >= 0)
+ heal_amount = difference
if(src.use_tool(H, user, 0, volume=50, amount=1))
if(user == H)
user.visible_message("[user] starts to fix some of the dents on [H]'s [affecting.name].",
"You start fixing some of the dents on [H]'s [affecting.name].")
if(!do_mob(user, H, 50))
return
- item_heal_robotic(H, user, 15, 0)
+ item_heal_robotic(H, user, heal_amount, 0)
else
return ..()
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index f462050789..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
@@ -291,7 +296,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
- force = 9
+ force = 7 //how much harm mode damage we do
+ var/stamina_damage_increment = 4 //how much extra damage do we do when in non-harm mode
throwforce = 10
damtype = STAMINA
attack_verb = list("whacked", "smacked", "struck")
@@ -342,17 +348,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/melee/bokken/Initialize()
. = ..()
AddElement(/datum/element/sword_point)
+ if(!harm) //if initialised in non-harm mode, setup force accordingly
+ force = force + stamina_damage_increment
/obj/item/melee/bokken/attack_self(mob/user)
harm = !harm
if(harm)
- force -= 2
+ force -= stamina_damage_increment
damtype = BRUTE
attack_verb = list("bashed", "smashed", "attacked")
bare_wound_bonus = 15 // having your leg smacked by a wooden stick is probably not great for it if it's naked
wound_bonus = 0
else
- force += 2
+ force += stamina_damage_increment
damtype = STAMINA
attack_verb = list("whacked", "smacked", "struck")
bare_wound_bonus = 0
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/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 905888c904..2a4856f60e 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -104,9 +104,6 @@
set_sign(new /datum/barsign/hiddensigns/empbarsign)
broken = TRUE
-
-
-
/obj/structure/sign/barsign/emag_act(mob/user)
. = ..()
if(broken || (obj_flags & EMAGGED))
@@ -128,8 +125,6 @@
return
set_sign(picked_name)
-
-
//Code below is to define useless variables for datums. It errors without these
@@ -140,95 +135,77 @@
var/desc = "desc"
var/hidden = FALSE
-
//Anything below this is where all the specific signs are. If people want to add more signs, add them below.
-
-
-
/datum/barsign/maltesefalcon
name = "Maltese Falcon"
icon = "maltesefalcon"
desc = "The Maltese Falcon, Space Bar and Grill."
-
/datum/barsign/thebark
name = "The Bark"
icon = "thebark"
desc = "Ian's bar of choice."
-
/datum/barsign/harmbaton
name = "The Harmbaton"
icon = "theharmbaton"
desc = "A great dining experience for both security members and assistants."
-
/datum/barsign/thesingulo
name = "The Singulo"
icon = "thesingulo"
desc = "Where people go that'd rather not be called by their name."
-
/datum/barsign/thedrunkcarp
name = "The Drunk Carp"
icon = "thedrunkcarp"
desc = "Don't drink and swim."
-
/datum/barsign/scotchservinwill
name = "Scotch Servin Willy's"
icon = "scotchservinwill"
desc = "Willy sure moved up in the world from clown to bartender."
-
/datum/barsign/officerbeersky
name = "Officer Beersky's"
icon = "officerbeersky"
desc = "Man eat a dong, these drinks are great."
-
/datum/barsign/thecavern
name = "The Cavern"
icon = "thecavern"
desc = "Fine drinks while listening to some fine tunes."
-
/datum/barsign/theouterspess
name = "The Outer Spess"
icon = "theouterspess"
desc = "This bar isn't actually located in outer space."
-
/datum/barsign/slipperyshots
name = "Slippery Shots"
icon = "slipperyshots"
desc = "Slippery slope to drunkeness with our shots!"
-
/datum/barsign/thegreytide
name = "The Grey Tide"
icon = "thegreytide"
desc = "Abandon your toolboxing ways and enjoy a lazy beer!"
-
/datum/barsign/honkednloaded
name = "Honked 'n' Loaded"
icon = "honkednloaded"
desc = "Honk."
-
/datum/barsign/thenest
name = "The Nest"
icon = "thenest"
desc = "A good place to retire for a drink after a long night of crime fighting."
-
/datum/barsign/thecoderbus
name = "The Coderbus"
icon = "thecoderbus"
desc = "A very controversial bar known for its wide variety of constantly-changing drinks."
-
/datum/barsign/theadminbus
name = "The Adminbus"
icon = "theadminbus"
@@ -313,7 +290,6 @@
hidden = TRUE
//Hidden signs list below this point
-
/datum/barsign/hiddensigns/empbarsign
name = "Haywire Barsign"
icon = "empbarsign"
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 5c5b2bb132..46eb82342a 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -571,10 +571,10 @@
O.emp_act(severity)
if(!secure || broken)
return ..()
- if(prob(50 / severity))
+ if(prob(severity/2))
locked = !locked
update_icon()
- if(prob(20 / severity) && !opened)
+ if(prob(severity/5) && !opened)
if(!locked)
open()
else
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm
index 7f22a49df9..ee8773c384 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm
@@ -8,6 +8,7 @@
new /obj/item/clothing/neck/cloak/qm(src)
new /obj/item/clothing/head/beret/qm(src)
new /obj/item/storage/lockbox/medal/cargo(src)
+ new /obj/item/clothing/suit/toggle/labcoat/depjacket/sup/qm(src)
new /obj/item/clothing/under/rank/cargo/qm(src)
new /obj/item/clothing/under/rank/cargo/qm/skirt(src)
new /obj/item/clothing/shoes/sneakers/brown(src)
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/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index ad7680f2f9..2c31303f79 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -25,6 +25,7 @@
/obj/structure/closet/secure_closet/hop/PopulateContents()
..()
new /obj/item/clothing/neck/cloak/hop(src)
+ new /obj/item/clothing/under/rank/civilian/head_of_personnel/whimsy(src)
new /obj/item/clothing/under/rank/civilian/head_of_personnel(src)
new /obj/item/clothing/under/rank/civilian/head_of_personnel/skirt(src)
new /obj/item/clothing/head/hopcap(src)
@@ -59,6 +60,7 @@
new /obj/item/radio/headset/heads/hos(src)
new /obj/item/clothing/under/rank/security/head_of_security/parade/female(src)
new /obj/item/clothing/under/rank/security/head_of_security/parade(src)
+ new /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/hoscorp(src)
new /obj/item/clothing/suit/armor/vest/leather(src)
new /obj/item/clothing/suit/armor/hos(src)
new /obj/item/clothing/under/rank/security/head_of_security/skirt(src)
@@ -95,6 +97,7 @@
new /obj/item/clothing/head/warden/drill(src)
new /obj/item/clothing/head/beret/sec/navywarden(src)
new /obj/item/clothing/suit/armor/vest/warden/alt(src)
+ new /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/wardencorp(src)
new /obj/item/clothing/under/rank/security/warden/formal(src)
new /obj/item/clothing/under/rank/security/warden/skirt(src)
new /obj/item/clothing/glasses/hud/security/sunglasses(src)
@@ -156,17 +159,6 @@
/obj/structure/closet/secure_closet/detective/PopulateContents()
..()
- new /obj/item/clothing/under/rank/security/detective(src)
- new /obj/item/clothing/under/rank/security/detective/skirt(src)
- new /obj/item/clothing/suit/det_suit(src)
- new /obj/item/clothing/head/fedora/det_hat(src)
- new /obj/item/clothing/gloves/color/black(src)
- new /obj/item/clothing/under/rank/security/detective/grey(src)
- new /obj/item/clothing/under/rank/security/detective/grey/skirt(src)
- new /obj/item/clothing/accessory/waistcoat(src)
- new /obj/item/clothing/suit/det_suit/grey(src)
- new /obj/item/clothing/head/fedora(src)
- new /obj/item/clothing/shoes/laceup(src)
new /obj/item/storage/box/evidence(src)
new /obj/item/radio/headset/headset_sec(src)
new /obj/item/detective_scanner(src)
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 4d500837d9..fadf33cb85 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -75,6 +75,8 @@
if(ishuman(new_spawn))
var/mob/living/carbon/human/H = new_spawn
H.underwear = "Nude"
+ H.undershirt = "Nude"
+ H.socks = "Nude"
H.update_body()
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 36e4f825da..7853b9619c 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -86,6 +86,10 @@
/obj/structure/grille/attack_animal(mob/user)
. = ..()
+ if(!user.CheckActionCooldown(CLICK_CD_MELEE))
+ return
+ user.DelayNextAction(flush = TRUE)
+ user.do_attack_animation(src)
if(!shock(user, 70) && !QDELETED(src)) //Last hit still shocks but shouldn't deal damage to the grille)
take_damage(rand(5,10), BRUTE, "melee", 1)
@@ -114,12 +118,12 @@
/obj/structure/grille/attack_alien(mob/living/user)
if(!user.CheckActionCooldown(CLICK_CD_MELEE))
return
+ user.DelayNextAction(flush = TRUE)
user.do_attack_animation(src)
user.visible_message("[user] mangles [src].", null, null, COMBAT_MESSAGE_RANGE)
if(!shock(user, 70))
take_damage(20, BRUTE, "melee", 1)
-
/obj/structure/grille/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && (mover.pass_flags & PASSGRILLE))
return TRUE
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index dc5355e9d8..1e278a9f88 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -9,6 +9,7 @@
armor = list("melee" = 0, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 20)
var/obj/item/holosign_creator/projector
var/init_vis_overlay = TRUE
+ rad_flags = RAD_NO_CONTAMINATE
/obj/structure/holosign/Initialize(mapload, source_projector)
. = ..()
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/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 237cfdbf0b..fa6bab0fcc 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -163,7 +163,7 @@
var/custom_tone = input(user, "Choose your custom skin tone:", "Race change", default) as color|null
if(custom_tone)
var/temp_hsv = RGBtoHSV(new_s_tone)
- if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
+ if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
to_chat(H,"Invalid color. Your color is not bright enough.")
else
H.skin_tone = custom_tone
@@ -177,7 +177,7 @@
if(new_mutantcolor)
var/temp_hsv = RGBtoHSV(new_mutantcolor)
- if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright
+ if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright
H.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor)
else
@@ -229,17 +229,32 @@
H.update_hair()
if(BODY_ZONE_PRECISE_EYES)
- var/new_eye_color = input(H, "Choose your eye color", "Eye Color","#"+H.eye_color) as color|null
- if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
- return
- if(new_eye_color)
- var/n_color = sanitize_hexcolor(new_eye_color)
- var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
- if(eyes)
- eyes.eye_color = n_color
- H.eye_color = n_color
- H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
- H.dna.species.handle_body()
+ var/eye_type = input(H, "Choose the eye you want to color", "Eye Color") as null|anything in list("Both Eyes", "Left Eye", "Right Eye")
+ if(eye_type)
+ var/input_color = H.left_eye_color
+ if(eye_type == "Right Eye")
+ input_color = H.right_eye_color
+ var/new_eye_color = input(H, "Choose your eye color", "Eye Color","#"+input_color) as color|null
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ if(new_eye_color)
+ var/n_color = sanitize_hexcolor(new_eye_color)
+ var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
+ var/left_color = n_color
+ var/right_color = n_color
+ if(eye_type == "Left Eye")
+ right_color = H.right_eye_color
+ else
+ if(eye_type == "Right Eye")
+ left_color = H.left_eye_color
+ if(eyes)
+ eyes.left_eye_color = left_color
+ eyes.right_eye_color = right_color
+ H.left_eye_color = left_color
+ H.right_eye_color = right_color
+ H.dna.update_ui_block(DNA_LEFT_EYE_COLOR_BLOCK)
+ H.dna.update_ui_block(DNA_RIGHT_EYE_COLOR_BLOCK)
+ H.dna.species.handle_body()
if(choice)
curse(user)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
new file mode 100644
index 0000000000..21cf97ba70
--- /dev/null
+++ b/code/game/objects/structures/railings.dm
@@ -0,0 +1,107 @@
+/obj/structure/railing
+ name = "railing"
+ desc = "Basic railing meant to protect idiots like you from falling."
+ icon = 'icons/obj/fluff.dmi'
+ icon_state = "railing"
+ density = TRUE
+ anchored = TRUE
+ climbable = TRUE
+ ///Initial direction of the railing.
+ var/ini_dir
+
+/obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch
+ icon_state = "railing_corner"
+ density = FALSE
+ climbable = FALSE
+
+/obj/structure/railing/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ,null,CALLBACK(src, .proc/can_be_rotated),CALLBACK(src,.proc/after_rotation))
+
+/obj/structure/railing/Initialize()
+ . = ..()
+ ini_dir = dir
+
+/obj/structure/railing/attackby(obj/item/I, mob/living/user, params)
+ ..()
+ add_fingerprint(user)
+ if(I.tool_behaviour == TOOL_WELDER && user.a_intent == INTENT_HELP)
+ if(obj_integrity < max_integrity)
+ if(!I.tool_start_check(user, amount=0))
+ return
+ to_chat(user, "You begin repairing [src]...")
+ if(I.use_tool(src, user, 40, volume=50))
+ obj_integrity = max_integrity
+ to_chat(user, "You repair [src].")
+ else
+ to_chat(user, "[src] is already in good condition!")
+ return
+
+/obj/structure/railing/wirecutter_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!anchored)
+ to_chat(user, "You cut apart the railing.")
+ I.play_tool_sound(src, 100)
+ deconstruct()
+ return TRUE
+
+/obj/structure/railing/deconstruct(disassembled)
+ . = ..()
+ if(!loc) //quick check if it's qdeleted already.
+ return
+ if(!(flags_1 & NODECONSTRUCT_1))
+ var/obj/item/stack/rods/rod = new /obj/item/stack/rods(drop_location(), 3)
+ transfer_fingerprints_to(rod)
+ qdel(src)
+///Implements behaviour that makes it possible to unanchor the railing.
+/obj/structure/railing/wrench_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(flags_1&NODECONSTRUCT_1)
+ return
+ to_chat(user, "You begin to [anchored ? "unfasten the railing from":"fasten the railing to"] the floor...")
+ if(I.use_tool(src, user, volume = 75, extra_checks = CALLBACK(src, .proc/check_anchored, anchored)))
+ setAnchored(!anchored)
+ to_chat(user, "You [anchored ? "fasten the railing to":"unfasten the railing from"] the floor.")
+ return TRUE
+
+/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
+ . = ..()
+ if(get_dir(loc, target) & dir)
+ var/checking = FLYING | FLOATING
+ return . || mover.throwing || mover.movement_type & checking
+ return TRUE
+
+/obj/structure/railing/corner/CanPass()
+ ..()
+ return TRUE
+
+/obj/structure/railing/CheckExit(atom/movable/mover, turf/target)
+ ..()
+ if(get_dir(loc, target) & dir)
+ var/checking = UNSTOPPABLE | FLYING | FLOATING
+ return !density || mover.throwing || mover.movement_type & checking || mover.move_force >= MOVE_FORCE_EXTREMELY_STRONG
+ return TRUE
+
+/obj/structure/railing/corner/CheckExit()
+ return TRUE
+
+/obj/structure/railing/proc/can_be_rotated(mob/user,rotation_type)
+ if(anchored)
+ to_chat(user, "[src] cannot be rotated while it is fastened to the floor!")
+ return FALSE
+
+ var/target_dir = turn(dir, rotation_type == ROTATION_CLOCKWISE ? -90 : 90)
+
+ if(!valid_window_location(loc, target_dir)) //Expanded to include rails, as well!
+ to_chat(user, "[src] cannot be rotated in that direction!")
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/proc/check_anchored(checked_anchored)
+ if(anchored == checked_anchored)
+ return TRUE
+
+/obj/structure/railing/proc/after_rotation(mob/user,rotation_type)
+ air_update_turf(1)
+ ini_dir = dir
+ add_fingerprint(user)
diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm
index 1358b2e296..8472926679 100644
--- a/code/game/objects/structures/stairs.dm
+++ b/code/game/objects/structures/stairs.dm
@@ -16,6 +16,18 @@
var/terminator_mode = STAIR_TERMINATOR_AUTOMATIC
var/turf/listeningTo
+/obj/structure/stairs/north
+ dir = NORTH
+
+/obj/structure/stairs/south
+ dir = SOUTH
+
+/obj/structure/stairs/east
+ dir = EAST
+
+/obj/structure/stairs/west
+ dir = WEST
+
/obj/structure/stairs/Initialize(mapload)
if(force_open_above)
force_open_above()
@@ -105,6 +117,9 @@
T.ChangeTurf(/turf/open/transparent/openspace, flags = CHANGETURF_INHERIT_AIR)
/obj/structure/stairs/proc/on_multiz_new(turf/source, dir)
+ //SIGNAL_HANDLER
+ SHOULD_NOT_SLEEP(TRUE) //the same thing.
+
if(dir == UP)
var/turf/open/transparent/openspace/T = get_step_multiz(get_turf(src), UP)
if(T && !istype(T))
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/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index 5024a4bada..eae45bfd76 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -74,6 +74,24 @@
/turf/open/floor/wood/airless
initial_gas_mix = AIRLESS_ATMOS
+/turf/open/floor/wood/wood_large
+ desc = "Stylish dark wood."
+ icon_state = "large_wood"
+ floor_tile = /obj/item/stack/tile/wood/wood_large
+ broken_states = list("large_wood-broken", "large_wood-broken2", "large_wood-broken3")
+
+/turf/open/floor/wood/wood_tiled
+ desc = "Stylish dark wood."
+ icon_state = "wood_tile"
+ floor_tile = /obj/item/stack/tile/wood/wood_tiled
+ broken_states = list("wood_tile-broken", "wood_tile-broken2", "wood_tile-broken3")
+
+/turf/open/floor/wood/wood_diagonal
+ desc = "Stylish dark wood."
+ icon_state = "diagonal_wood"
+ floor_tile = /obj/item/stack/tile/wood/wood_diagonal
+ broken_states = list("diagonal_wood-broken", "diagonal_wood-broken2", "diagonal_wood-broken3")
+
/turf/open/floor/grass
name = "grass patch"
desc = "You can't tell if this is real grass or just cheap plastic imitation."
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 += "Change "
+ dat += ""
else if(use_skintones || mutant_colors)
dat += ""
@@ -521,6 +543,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += ""
mutant_category = 0
+ if(length(pref_species.allowed_limb_ids))
+ if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids))
+ chosen_limb_id = pref_species.limbs_id || pref_species.id
+ dat += "
Body sprite
"
+ dat += "[chosen_limb_id]"
+
if(mutant_category)
dat += ""
mutant_category = 0
@@ -849,6 +877,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "
"
dat += "
Fetish content prefs
"
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"] "
@@ -1223,6 +1253,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()
@@ -1350,7 +1383,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
socks = random_socks()
socks_color = random_short_color()
if(BODY_ZONE_PRECISE_EYES)
- eye_color = random_eye_color()
+ var/random_eye_color = random_eye_color()
+ left_eye_color = random_eye_color
+ right_eye_color = random_eye_color
if("s_tone")
skin_tone = random_skin_tone()
use_custom_skin_tone = null
@@ -1438,6 +1473,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)
@@ -1475,6 +1515,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)
@@ -1506,9 +1569,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
socks_color = sanitize_hexcolor(n_socks_color, 6)
if("eyes")
- var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+eye_color) as color|null
+ var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+left_eye_color) as color|null
if(new_eyes)
- eye_color = sanitize_hexcolor(new_eyes, 6)
+ left_eye_color = sanitize_hexcolor(new_eyes, 6)
+ right_eye_color = sanitize_hexcolor(new_eyes, 6)
+
+ if("eye_left")
+ var/new_eyes = input(user, "Choose your character's left eye colour:", "Character Preference","#"+left_eye_color) as color|null
+ if(new_eyes)
+ left_eye_color = sanitize_hexcolor(new_eyes, 6)
+
+ if("eye_right")
+ var/new_eyes = input(user, "Choose your character's right eye colour:", "Character Preference","#"+right_eye_color) as color|null
+ if(new_eyes)
+ right_eye_color = sanitize_hexcolor(new_eyes, 6)
+
+ if("toggle_split_eyes")
+ split_eye_colors = !split_eye_colors
+ right_eye_color = left_eye_color
if("species")
var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_race_names
@@ -1552,7 +1630,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_mutantcolor)
if(new_mutantcolor == "#000000")
features["mcolor"] = pref_species.default_color
- else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
+ else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
features["mcolor"] = sanitize_hexcolor(new_mutantcolor, 6)
else
to_chat(user, "Invalid color. Your color is not bright enough.")
@@ -1563,7 +1641,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_mutantcolor)
if(new_mutantcolor == "#000000")
features["mcolor2"] = pref_species.default_color
- else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
+ else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
features["mcolor2"] = sanitize_hexcolor(new_mutantcolor, 6)
else
to_chat(user, "Invalid color. Your color is not bright enough.")
@@ -1574,7 +1652,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_mutantcolor)
if(new_mutantcolor == "#000000")
features["mcolor3"] = pref_species.default_color
- else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
+ else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
features["mcolor3"] = sanitize_hexcolor(new_mutantcolor, 6)
else
to_chat(user, "Invalid color. Your color is not bright enough.")
@@ -1890,7 +1968,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_cockcolor)
if(new_cockcolor == "#000000")
features["cock_color"] = pref_species.default_color
- else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
+ else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
features["cock_color"] = sanitize_hexcolor(new_cockcolor, 6)
else
to_chat(user,"Invalid color. Your color is not bright enough.")
@@ -1930,7 +2008,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_ballscolor)
if(new_ballscolor == "#000000")
features["balls_color"] = pref_species.default_color
- else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
+ else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
features["balls_color"] = sanitize_hexcolor(new_ballscolor, 6)
else
to_chat(user,"Invalid color. Your color is not bright enough.")
@@ -1957,7 +2035,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_breasts_color)
if(new_breasts_color == "#000000")
features["breasts_color"] = pref_species.default_color
- else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
+ else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
features["breasts_color"] = sanitize_hexcolor(new_breasts_color, 6)
else
to_chat(user,"Invalid color. Your color is not bright enough.")
@@ -1979,7 +2057,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(new_vagcolor)
if(new_vagcolor == "#000000")
features["vag_color"] = pref_species.default_color
- else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
+ else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
features["vag_color"] = sanitize_hexcolor(new_vagcolor, 6)
else
to_chat(user,"Invalid color. Your color is not bright enough.")
@@ -2092,8 +2170,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
features["body_model"] = chosengender
gender = chosengender
- facial_hair_style = random_facial_hair_style(gender)
- hair_style = random_hair_style(gender)
if("body_size")
var/min = CONFIG_GET(number/body_size_min)
@@ -2119,6 +2195,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/selected_custom_speech_verb = input(user, "Choose your desired speech verb (none means your species speech verb)", "Character Preference") as null|anything in GLOB.speech_verbs
if(selected_custom_speech_verb)
custom_speech_verb = selected_custom_speech_verb
+
+ if("bodysprite")
+ var/selected_body_sprite = input(user, "Choose your desired body sprite", "Character Preference") as null|anything in pref_species.allowed_limb_ids
+ if(selected_body_sprite)
+ chosen_limb_id = selected_body_sprite //this gets sanitized before loading
else
switch(href_list["preference"])
//CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj
@@ -2342,6 +2423,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
@@ -2451,7 +2539,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)
@@ -2475,12 +2563,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.gender = gender
character.age = age
- character.eye_color = eye_color
+ character.left_eye_color = left_eye_color
+ character.right_eye_color = right_eye_color
var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes)
if(organ_eyes)
- if(!initial(organ_eyes.eye_color))
- organ_eyes.eye_color = eye_color
- organ_eyes.old_eye_color = eye_color
+ if(!initial(organ_eyes.left_eye_color))
+ organ_eyes.left_eye_color = left_eye_color
+ organ_eyes.right_eye_color = right_eye_color
+ organ_eyes.old_left_eye_color = left_eye_color
+ organ_eyes.old_right_eye_color = right_eye_color
character.hair_color = hair_color
character.facial_hair_color = facial_hair_color
character.skin_tone = skin_tone
@@ -2510,6 +2601,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.dna.features = features.Copy()
character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
+ if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids))
+ character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id
character.dna.real_name = character.real_name
character.dna.nameless = character.nameless
character.dna.custom_species = character.custom_species
@@ -2544,6 +2637,35 @@ 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 - But DO NOT delete those that are species-related
+ for(var/obj/item/bodypart/part in character.bodyparts)
+ if(part.status == BODYPART_ROBOTIC && !part.render_like_organic)
+ 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)
//let's be sure the character updates
@@ -2551,6 +2673,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 a8183e4940..fb1178fd8e 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 35
+#define SAVEFILE_VERSION_MAX 36
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -204,30 +204,39 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(S["species"] == "lizard")
features["mam_snouts"] = features["snout"]
+ if(current_version < 36)
+ left_eye_color = S["eye_color"]
+ right_eye_color = S["eye_color"]
+
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
+ vr_path = "data/player_saves/[ckey[1]]/[ckey]/vore"
/datum/preferences/proc/load_preferences()
if(!path)
- return 0
+ return FALSE
if(world.time < loadprefcooldown)
if(istype(parent))
to_chat(parent, "You're attempting to load your preferences a little too fast. Wait half a second, then try again.")
- return 0
+ return FALSE
loadprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!fexists(path))
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/"
var/needs_update = savefile_needs_update(S)
if(needs_update == -2) //fatal, can't load any data
- return 0
+ var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
+ if (fexists(bacpath))
+ fdel(bacpath) //only keep 1 version of backup
+ fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
+ return FALSE
. = TRUE
@@ -289,8 +298,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//try to fix any outdated data if necessary
if(needs_update >= 0)
+ var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
+ if (fexists(bacpath))
+ fdel(bacpath) //only keep 1 version of backup
+ fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer)
+
+
//Sanitize
ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor)))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
@@ -332,7 +347,25 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
- return 1
+ if(needs_update >= 0) //save the updated version
+ var/old_default_slot = default_slot
+ var/old_max_save_slots = max_save_slots
+
+ for (var/slot in S.dir) //but first, update all current character slots.
+ if (copytext(slot, 1, 10) != "character")
+ continue
+ var/slotnum = text2num(copytext(slot, 10))
+ if (!slotnum)
+ continue
+ max_save_slots = max(max_save_slots, slotnum) //so we can still update byond member slots after they lose memeber status
+ default_slot = slotnum
+ if (load_character()) // this updtates char slots
+ save_character()
+ default_slot = old_default_slot
+ max_save_slots = old_max_save_slots
+ save_preferences()
+
+ return TRUE
/datum/preferences/proc/verify_keybindings_valid()
// Sanitize the actual keybinds to make sure they exist.
@@ -422,17 +455,17 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
/datum/preferences/proc/load_character(slot)
if(!path)
- return 0
+ return FALSE
if(world.time < loadcharcooldown) //This is before the check to see if the filepath exists to ensure that BYOND can't get hung up on read attempts when the hard drive is a little slow
if(istype(parent))
to_chat(parent, "You're attempting to load your character a little too fast. Wait half a second, then try again.")
return "SLOW THE FUCK DOWN" //the reason this isn't null is to make sure that people don't have their character slots overridden by random chars if they accidentally double-click a slot
loadcharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!fexists(path))
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/"
if(!slot)
slot = default_slot
@@ -444,7 +477,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S.cd = "/character[slot]"
var/needs_update = savefile_needs_update(S)
if(needs_update == -2) //fatal, can't load any data
- return 0
+ return FALSE
. = TRUE
@@ -462,7 +495,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
pref_species = new newtype
- scars_index = rand(1,5)
+ scars_index = rand(1,5) // WHY
//Character
S["real_name"] >> real_name
@@ -476,7 +509,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["age"] >> age
S["hair_color"] >> hair_color
S["facial_hair_color"] >> facial_hair_color
- S["eye_color"] >> eye_color
+ S["left_eye_color"] >> left_eye_color
+ S["right_eye_color"] >> right_eye_color
S["use_custom_skin_tone"] >> use_custom_skin_tone
S["skin_tone"] >> skin_tone
S["hair_style_name"] >> hair_style
@@ -514,7 +548,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)
@@ -597,8 +638,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["vore_flags"] >> vore_flags
S["vore_taste"] >> vore_taste
- S["belly_prefs"] >> belly_prefs
-
+ var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
+ if(fexists(char_vr_path))
+ var/list/json_from_file = json_decode(file2text(char_vr_path))
+ if(json_from_file)
+ belly_prefs = json_from_file["belly_prefs"]
//gear loadout
var/text_to_load
S["loadout"] >> text_to_load
@@ -615,6 +659,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
gear_points -= init_cost
//try to fix any outdated data if necessary
+ //preference updating will handle saving the updated data for us.
if(needs_update >= 0)
update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer)
@@ -647,7 +692,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
- eye_color = sanitize_hexcolor(eye_color, 6, FALSE)
+ left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
+ right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
var/static/allow_custom_skintones
if(isnull(allow_custom_skintones))
@@ -776,7 +822,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["age"] , age)
WRITE_FILE(S["hair_color"] , hair_color)
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
- WRITE_FILE(S["eye_color"] , eye_color)
+ WRITE_FILE(S["left_eye_color"] , left_eye_color)
+ WRITE_FILE(S["right_eye_color"] , right_eye_color)
WRITE_FILE(S["use_custom_skin_tone"] , use_custom_skin_tone)
WRITE_FILE(S["skin_tone"] , skin_tone)
WRITE_FILE(S["hair_style_name"] , hair_style)
@@ -856,13 +903,18 @@ 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)
WRITE_FILE(S["vore_flags"] , vore_flags)
WRITE_FILE(S["vore_taste"] , vore_taste)
- WRITE_FILE(S["belly_prefs"] , belly_prefs)
+ var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
+ var/belly_prefs_json = safe_json_encode(list("belly_prefs" = belly_prefs))
+ if(fexists(char_vr_path))
+ fdel(char_vr_path)
+ text2file(belly_prefs_json,char_vr_path)
WRITE_FILE(S["persistent_scars"] , persistent_scars)
WRITE_FILE(S["scars1"] , scars_list["1"])
@@ -870,6 +922,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/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 3f6d21bcd2..11de0124cb 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -373,7 +373,7 @@
/obj/item/clothing/glasses/sunglasses/blindfold/white/update_icon(mob/living/carbon/human/user)
if(ishuman(user) && !colored_before)
- add_atom_colour("#[user.eye_color]", FIXED_COLOUR_PRIORITY)
+ add_atom_colour("#[user.left_eye_color]", FIXED_COLOUR_PRIORITY)
colored_before = TRUE
/obj/item/clothing/glasses/sunglasses/blindfold/white/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
@@ -382,7 +382,7 @@
var/mob/living/carbon/human/H = loc
var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/eyes.dmi', "blindfoldwhite")
M.appearance_flags |= RESET_COLOR
- M.color = "#[H.eye_color]"
+ M.color = "#[H.left_eye_color]"
. += M
/obj/item/clothing/glasses/sunglasses/big
diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm
index 11b2afa968..f4b4140a1a 100644
--- a/code/modules/clothing/gloves/tacklers.dm
+++ b/code/modules/clothing/gloves/tacklers.dm
@@ -72,6 +72,25 @@
siemens_coefficient = 0
permeability_coefficient = 0.05
+/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator
+ name = "insidious guerrilla gloves"
+ desc = "Specialized combat gloves for carrying people around. Transfers tactical kidnapping and tackling knowledge to the user via the use of nanochips."
+ icon_state = "infiltrator"
+ item_state = "infiltrator"
+ siemens_coefficient = 0
+ permeability_coefficient = 0.05
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ var/carrytrait = TRAIT_QUICKER_CARRY
+
+/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/equipped(mob/user, slot)
+ . = ..()
+ if(slot == SLOT_GLOVES)
+ ADD_TRAIT(user, carrytrait, GLOVE_TRAIT)
+
+/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/dropped(mob/user)
+ . = ..()
+ REMOVE_TRAIT(user, carrytrait, GLOVE_TRAIT)
+
/obj/item/clothing/gloves/tackler/rocket
name = "rocket gloves"
desc = "The ultimate in high risk, high reward, perfect for when you need to stop a criminal from fifty feet away or die trying. Banned in most Spinward gridiron football and rugby leagues."
diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm
index 3f77a2befc..57659a70c4 100644
--- a/code/modules/clothing/spacesuits/chronosuit.dm
+++ b/code/modules/clothing/spacesuits/chronosuit.dm
@@ -62,13 +62,12 @@
if(. & EMP_PROTECT_SELF)
return
var/mob/living/carbon/human/user = src.loc
- switch(severity)
- if(1)
- if(activated && user && ishuman(user) && (user.wear_suit == src))
- to_chat(user, "E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD")
- to_chat(user, "An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!")
- user.emote("scream")
- deactivate(1, 1)
+ if(severity >= 70)
+ if(activated && user && ishuman(user) && (user.wear_suit == src))
+ to_chat(user, "E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD")
+ to_chat(user, "An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!")
+ user.emote("scream")
+ deactivate(1, 1)
/obj/item/clothing/suit/space/chronos/proc/finish_chronowalk(mob/living/carbon/human/user, turf/to_turf)
if(!user)
diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm
index 5124c5d62e..dd46d7a5bb 100644
--- a/code/modules/clothing/spacesuits/miscellaneous.dm
+++ b/code/modules/clothing/spacesuits/miscellaneous.dm
@@ -525,7 +525,7 @@ Contains:
icon_state = "hunter"
item_state = "swat_suit"
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/kitchen/knife/combat)
- armor = list("melee" = 60, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "wpound" = 25)
+ armor = list("melee" = 60, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "wound" = 25)
strip_delay = 130
resistance_flags = FIRE_PROOF | ACID_PROOF
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/cloaks.dm b/code/modules/clothing/suits/cloaks.dm
index d5f65e4fa8..133956e44e 100644
--- a/code/modules/clothing/suits/cloaks.dm
+++ b/code/modules/clothing/suits/cloaks.dm
@@ -102,3 +102,20 @@
/obj/item/clothing/neck/cloak/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, poly_colors, 3)
+
+/obj/item/clothing/neck/cloak/alt
+ name = "cloak"
+ desc = "A ragged up white cloak. It reminds you of a place not far from here."
+ icon_state = "cloak"
+ item_state = "cloak"
+
+/obj/item/clothing/neck/cloak/alt/polychromic
+ name = "polychromic cloak"
+ desc = "A ragged up cloak. It reminds you of a place not far from here."
+ icon_state = "cloak"
+ item_state = "cloak"
+ var/list/poly_colors = list("#FFFFFF", "#676767", "#4C4C4C")
+
+/obj/item/clothing/neck/cloak/alt/polychromic/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, poly_colors, 3)
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index fa55dcf1c1..54b2c17f36 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -117,6 +117,27 @@
icon_state = "greydet"
item_state = "greydet"
+/obj/item/clothing/suit/det_suit/forensicsred
+ name = "red forensics coat"
+ icon_state = "forensics_red"
+ item_state = "forensics_red"
+
+/obj/item/clothing/suit/det_suit/forensicsred/long
+ name = "long red forensics coat"
+ icon_state = "forensics_red_long"
+ item_state = "forensics_red_long"
+
+/obj/item/clothing/suit/det_suit/forensicsblue
+ name = "blue forensics coat"
+ icon_state = "forensics_blue"
+ item_state = "forensics_blue"
+
+/obj/item/clothing/suit/det_suit/forensicsblue/long
+ name = "long blue forensics coat"
+ icon_state = "forensics_blue_long"
+ item_state = "forensics_blue_long"
+
+
//Engineering
/obj/item/clothing/suit/hazardvest
name = "hazard vest"
@@ -222,4 +243,16 @@
desc = "A dark blue vest with reflective strips for emergency medical technicians."
icon_state = "labcoat_emt"
item_state = "labcoat_emt"
- mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
\ No newline at end of file
+ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+
+/obj/item/clothing/suit/toggle/labcoat/emt/red
+ name = "red emt vest"
+ desc = "A red vest with reflective strips for emergency medical technicians."
+ icon_state = "labcoat_emtred"
+ item_state = "labcoat_emtred"
+
+/obj/item/clothing/suit/toggle/labcoat/emt/highvis
+ name = "high visibility emt vest"
+ desc = "a high visibility vest for emergency medical technicians."
+ icon_state = "labcoat_highvis"
+ item_state = "labcoat_highvis"
diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm
index 195712b814..32a4ab7ad0 100644
--- a/code/modules/clothing/suits/labcoat.dm
+++ b/code/modules/clothing/suits/labcoat.dm
@@ -62,3 +62,48 @@
name = "scientist labcoat"
desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder."
icon_state = "labcoat_tox"
+
+// Departmental Jackets
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/sci
+ name = "science jacket"
+ desc = "A comfortable jacket in science purple."
+ icon_state = "sci_dep_jacket"
+ item_state = "sci_dep_jacket"
+ allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/med
+ name = "medical jacket"
+ desc = "A comfortable jacket in medical blue."
+ icon_state = "med_dep_jacket"
+ item_state = "med_dep_jacket"
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/sec
+ name = "security jacket"
+ desc = "A comfortable jacket in security red."
+ icon_state = "sec_dep_jacket"
+ item_state = "sec_dep_jacket"
+ armor = list("melee" = 25, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45)
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/sup
+ name = "supply jacket"
+ desc = "A comfortable jacket in supply brown."
+ icon_state = "supply_dep_jacket"
+ item_state = "supply_dep_jacket"
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/sup/qm
+ name = "quartermaster's jacket"
+ desc = "A loose covering often warn by station quartermasters."
+ icon_state = "qmjacket"
+ item_state = "qmjacket"
+
+/obj/item/clothing/suit/toggle/labcoat/depjacket/eng
+ name = "engineering jacket"
+ desc = "A comfortable jacket in engineering yellow."
+ icon_state = "engi_dep_jacket"
+ item_state = "engi_dep_jacket"
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/t_scanner, /obj/item/construction/rcd, /obj/item/pipe_dispenser, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter)
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 6364a9a1c1..3b45d2bc14 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -94,6 +94,23 @@
flags_inv = HIDEJUMPSUIT
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+/obj/item/clothing/suit/tailcoat
+ name = "tailcoat"
+ desc = "A rather impractical, long coat."
+ icon_state = "tailcoat"
+ item_state = "tailcoat"
+
+/obj/item/clothing/suit/vickyblack
+ name = "black victorian coat"
+ desc = "An overbearing black coat, it looks far older than you are."
+ icon_state = "vickyblack"
+ item_state = "vickyblack"
+
+/obj/item/clothing/suit/vickyred
+ name = "red victorian coat"
+ desc = "An overbearing red coat, it looks far older than you are."
+ icon_state = "vickyred"
+ item_state = "vickyred"
/obj/item/clothing/suit/apron/overalls
name = "coveralls"
@@ -625,7 +642,7 @@
desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. Snazzy."
icon_state = "coatmedical"
item_state = "coatmedical"
- allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/medical
@@ -638,7 +655,7 @@
desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. The normal liner is replaced with an exceptionally thick, soft layer of fur."
icon_state = "coatcmo"
item_state = "coatcmo"
- allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 0)
hoodtype = /obj/item/clothing/head/hooded/winterhood/cmo
@@ -651,7 +668,7 @@
desc = "A lab-grade winter coat made with acid resistant polymers. For the enterprising chemist who was exiled to a frozen wasteland on the go."
icon_state = "coatchemistry"
item_state = "coatchemistry"
- allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 30, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/chemistry
@@ -664,7 +681,7 @@
desc = "A white winter coat with green markings. Warm, but wont fight off the common cold or any other disease. Might make people stand far away from you in the hallway. The zipper tab looks like an oversized bacteriophage."
icon_state = "coatviro"
item_state = "coatviro"
- allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 0, "acid" = 0)
hoodtype = /obj/item/clothing/head/hooded/winterhood/viro
@@ -677,7 +694,7 @@
desc = "A winter coat with blue markings. Warm, but probably won't protect from biological agents. For the cozy doctor on the go."
icon_state = "coatparamed"
item_state = "coatparamed"
- allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
+ allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/paramedic
@@ -1064,3 +1081,9 @@
desc = "An outfit used by traditional japanese warriors."
icon_state = "samurai"
item_state = "samurai"
+
+/obj/item/clothing/suit/bomber
+ name = "old bomber jacket"
+ desc = "It looks like someone dragged this out of a muddy lake."
+ icon_state = "bomberalt"
+ item_state = "bomberalt"
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/jobs/cargo.dm b/code/modules/clothing/under/jobs/cargo.dm
index 8fbc343598..832589a6b7 100644
--- a/code/modules/clothing/under/jobs/cargo.dm
+++ b/code/modules/clothing/under/jobs/cargo.dm
@@ -23,6 +23,14 @@
body_parts_covered = CHEST|GROIN|ARMS
alt_covers_chest = TRUE
+/obj/item/clothing/under/rank/cargo/util
+ name = "cargo utility uniform"
+ desc = "a utility uniform for cargo personnel"
+ icon_state = "utilsupply"
+ item_state = "utilsupply"
+ can_adjust = FALSE
+
+
/obj/item/clothing/under/rank/cargo/tech/skirt
name = "cargo technician's jumpskirt"
desc = "Skiiiiirts! They're comfy and easy to wear"
@@ -33,6 +41,13 @@
fitted = FEMALE_UNIFORM_TOP
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+/obj/item/clothing/under/rank/cargo/tech/long
+ name = "cargo technician's jumpsuit (trousers)"
+ desc = "A variant of the cargo technician's uniform with proper trousers, suited for colder climates and modest workers."
+ icon_state = "cargo_long"
+ item_state = "lb_suit"
+ alt_covers_chest = TRUE
+
/obj/item/clothing/under/rank/cargo/miner
name = "shaft miner's jumpsuit"
desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty."
diff --git a/code/modules/clothing/under/jobs/civilian/civilian.dm b/code/modules/clothing/under/jobs/civilian/civilian.dm
index 5dffc8f88d..9bfac2a81c 100644
--- a/code/modules/clothing/under/jobs/civilian/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian/civilian.dm
@@ -23,6 +23,68 @@
fitted = FEMALE_UNIFORM_TOP
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+/obj/item/clothing/under/rank/civilian/util
+ name = "generic utility uniform"
+ desc = "A utility uniform worn by various crew."
+ icon_state = "utilgen"
+ item_state = "utilgen"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/util/greyshirt
+ name = "grey uniform"
+ desc = "A bland, grey uniform."
+ icon_state = "greyshirt"
+ item_state = "greyshirt"
+
+/obj/item/clothing/under/rank/civilian/victorian_redsleeves
+ name = "black vest red victorian suit"
+ desc = "A stark red victorian suit with a black vest."
+ icon_state = "victorianblred"
+ item_state = "victorianblred"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/victorian_redvest
+ name = "red-vested victorian suit"
+ desc = "A victorian suit with a red vest."
+ icon_state = "victorianredvest"
+ item_state = "victorianredvest"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/victorian_vest
+ name = "black victorian suit"
+ desc = "A simple black victorian suit."
+ icon_state = "victorianvest"
+ item_state = "victorianvest"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/victorian_purple
+ name = "purple victorian suit"
+ desc = "A victorian suit in regal purple."
+ icon_state = "victorianlightfire"
+ item_state = "victorianlightfire"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/victorianreddress
+ name = "red victorian dress"
+ desc = "A flashy red victorian dress."
+ icon_state = "victorianreddress"
+ item_state = "victorianreddress"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/victorianblackdress
+ name = "black victorian dress"
+ desc = "A victorian dress in somber black."
+ icon_state = "victorianblackdress"
+ item_state = "victorianblackdress"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/civilian/dutch
+ name = "dutch suit"
+ desc = "A dapper dutch suit."
+ icon_state = "dutch"
+ item_state = "dutch"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/civilian/chaplain
desc = "It's a black jumpsuit, often worn by religious folk."
name = "chaplain's jumpsuit"
@@ -62,6 +124,12 @@
item_state = "b_suit"
can_adjust = FALSE
+/obj/item/clothing/under/rank/civilian/head_of_personnel/whimsy
+ name = "casual head of personnel's jumpsuit"
+ desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\", this one seems to be a bit more casual."
+ icon_state = "hop_casual"
+ item_state = "hop_casual"
+
/obj/item/clothing/under/rank/civilian/head_of_personnel/skirt
name = "head of personnel's jumpskirt"
desc = "It's a jumpskirt worn by someone who works in the position of \"Head of Personnel\"."
diff --git a/code/modules/clothing/under/jobs/command.dm b/code/modules/clothing/under/jobs/command.dm
index 8272c36cf3..8a6c372272 100644
--- a/code/modules/clothing/under/jobs/command.dm
+++ b/code/modules/clothing/under/jobs/command.dm
@@ -6,6 +6,13 @@
sensor_mode = SENSOR_COORDS
random_sensor = FALSE
+/obj/item/clothing/under/rank/captain/util
+ name = "Command Utiltiy Uniform"
+ desc = "A utility uniform for command personnel."
+ icon_state = "utilcom"
+ item_state = "utilcom"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/captain/skirt
name = "captain's jumpskirt"
desc = "It's a blue jumpskirt with some gold markings denoting the rank of \"Captain\"."
diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm
index 5693468b3b..98dee44cb2 100644
--- a/code/modules/clothing/under/jobs/engineering.dm
+++ b/code/modules/clothing/under/jobs/engineering.dm
@@ -42,13 +42,35 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20, "wound" = 5)
resistance_flags = NONE
+/obj/item/clothing/under/rank/engineering/engineer/mechanic
+ name = "mechanic's jumpsuit"
+ desc = "an old mechanic's jumpsuit"
+ icon_state = "mechanic"
+ item_state = "mechanic"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/engineering/engineer/util
+ name = "engineering utility uniform"
+ desc = "a utility jumpsuit for engineering personnel"
+ icon_state = "utileng"
+ item_state = "utileng"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/engineering/engineer/hazard
name = "engineer's hazard jumpsuit"
desc = "A high visibility jumpsuit made from heat and radiation resistant materials."
- icon_state = "hazard"
- item_state = "suit-orange"
+ icon_state = "hazard_orange"
+ item_state = "engi_suit"
alt_covers_chest = TRUE
+/obj/item/clothing/under/rank/engineering/engineer/hazard/green
+ icon_state = "hazard_green"
+ item_state = "g_suit"
+
+/obj/item/clothing/under/rank/engineering/engineer/hazard/white
+ icon_state = "hazard_white"
+ item_state = "gy_suit"
+
/obj/item/clothing/under/rank/engineering/engineer/skirt
name = "engineer's jumpskirt"
desc = "It's an orange high visibility jumpskirt worn by engineers."
diff --git a/code/modules/clothing/under/jobs/medical.dm b/code/modules/clothing/under/jobs/medical.dm
index c66b972624..de285b0470 100644
--- a/code/modules/clothing/under/jobs/medical.dm
+++ b/code/modules/clothing/under/jobs/medical.dm
@@ -86,6 +86,12 @@
permeability_coefficient = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
+/obj/item/clothing/under/rank/medical/paramedic/red
+ name = "red paramedic jumpsuit"
+ icon_state = "pmedred"
+ item_state = "pmedred"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/medical/paramedic/light
desc = "It's made of a special fiber that provides minor protection against biohazards. It has a dark blue cross on the chest denoting that the wearer is a trained paramedic."
icon_state = "paramedic-light"
@@ -124,6 +130,13 @@
permeability_coefficient = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
+/obj/item/clothing/under/rank/medical/doctor/util
+ name = "Medical Utility Uniform"
+ desc = "Utility jumpsuit for medical personnel"
+ icon_state = "utilmed"
+ item_state = "utilmed"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/medical/doctor/blue
name = "blue medical scrubs"
desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue."
diff --git a/code/modules/clothing/under/jobs/rnd.dm b/code/modules/clothing/under/jobs/rnd.dm
index 03eb910736..7443cd9d39 100644
--- a/code/modules/clothing/under/jobs/rnd.dm
+++ b/code/modules/clothing/under/jobs/rnd.dm
@@ -61,6 +61,13 @@
permeability_coefficient = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
+/obj/item/clothing/under/rank/rnd/scientist/util
+ name = "Science Utility Uniform"
+ desc = "A utility uniform for science personnel"
+ icon_state = "utilsci"
+ item_state = "utilsci"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/rnd/scientist/skirt
name = "scientist's jumpskirt"
desc = "It's made of a special fiber that provides minor protection against explosives. It has markings that denote the wearer as a scientist."
@@ -78,6 +85,12 @@
item_state = "robotics"
resistance_flags = NONE
+/obj/item/clothing/under/rank/rnd/roboticist/sleek
+ name = "sleek roboticist's jumpsuit"
+ icon_state = "robosleek"
+ item_state = "robosleek"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/rnd/roboticist/skirt
name = "roboticist's jumpskirt"
desc = "It's a slimming black with reinforced seams; great for industrial work."
diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm
index 7747d2e181..4438298dea 100644
--- a/code/modules/clothing/under/jobs/security.dm
+++ b/code/modules/clothing/under/jobs/security.dm
@@ -21,6 +21,13 @@
item_state = "r_suit"
armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10)
+/obj/item/clothing/under/rank/security/officer/util
+ name = "security utility uniform"
+ desc = "a grey security uniform"
+ icon_state = "utilsec"
+ item_state = "utilsec"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/security/officer/grey
name = "grey security jumpsuit"
desc = "A tactical relic of years past before Nanotrasen decided it was cheaper to dye the suits red instead of washing out the blood."
@@ -51,6 +58,28 @@
item_state = "blueshift"
can_adjust = FALSE
+/obj/item/clothing/under/rank/security/officer/blueshirt/seccorp
+ name = "corporate security uniform"
+ desc = "How positively stuffy."
+ icon_state = "seccorp"
+ item_state = "seccorp"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/wardencorp
+ name = "corporate warden uniform"
+ icon_state = "wardencorp"
+ item_state = "wardencorp"
+
+/obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/hoscorp
+ name = "corporate head of security's uniform"
+ icon_state = "hoscorp"
+ item_state = "hoscorp"
+
+/obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/detcorp
+ name = "corporate detective's uniform"
+ icon_state = "detcorp"
+ item_state = "detcorp"
+
/obj/item/clothing/under/rank/security/officer/spacepol
name = "police uniform"
desc = "Space not controlled by megacorporations, planets, or pirates is under the jurisdiction of Spacepol."
@@ -103,6 +132,16 @@
item_state = "det"
armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10)
+/obj/item/clothing/under/rank/security/detective/brown
+ name = "dark boiled suit"
+ icon_state = "detectivebrown"
+ item_state = "detectivebrown"
+
+/obj/item/clothing/under/rank/security/detective/brown/brown2
+ name = "padded suit"
+ icon_state = "detectivebrown2"
+ item_state = "detectivebrown2"
+
/obj/item/clothing/under/rank/security/detective/skirt
name = "detective's suitskirt"
desc = "Someone who wears this means business."
@@ -202,4 +241,4 @@
desc = "Space not controlled by megacorporations, planets, or pirates is under the jurisdiction of Spacepol."
icon_state = "spacepol"
item_state = "spacepol"
- can_adjust = FALSE
\ No newline at end of file
+ can_adjust = FALSE
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/clothing/under/suits.dm b/code/modules/clothing/under/suits.dm
index fe23003b38..87e6e94b6b 100644
--- a/code/modules/clothing/under/suits.dm
+++ b/code/modules/clothing/under/suits.dm
@@ -116,3 +116,19 @@
/obj/item/clothing/under/suit/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, list("#FFFFFF", "#FFFFFF", "#808080"), 3)
+
+/obj/item/clothing/under/suit/turtle
+ desc = "a debatably stylish turtleneck."
+ can_adjust = FALSE
+
+/obj/item/clothing/under/suit/turtle/teal
+ name = "teal turtleneck"
+ icon_state = "tealturtle"
+ item_state = "tealturtle"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/suit/turtle/grey
+ name = "grey turtleneck"
+ icon_state = "greyturtle"
+ item_state = "greyturtle"
+ can_adjust = FALSE
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..0342ca4643 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
@@ -24,4 +23,4 @@
/datum/round_event/communications_blackout/start()
for(var/obj/machinery/telecomms/T in GLOB.telecomms_list)
- T.emp_act(EMP_HEAVY)
+ T.emp_act(80)
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..3bcec9f471 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)
@@ -141,7 +141,7 @@
for(var/obj/machinery/rnd/server/S in GLOB.machines)
if(S.stat & (NOPOWER|BROKEN))
continue
- S.emp_act(1)
+ S.emp_act(80)
new /obj/effect/temp_visual/emp(get_turf(S))
/obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user)
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..6f20d954ed 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
@@ -37,4 +36,4 @@
// at all
P.ex_act(EXPLODE_DEVASTATE)
else
- P.emp_act(EMP_HEAVY)
+ P.emp_act(80)
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.dm b/code/modules/food_and_drinks/food/snacks.dm
index 895699434a..4d5f4e2dd0 100644
--- a/code/modules/food_and_drinks/food/snacks.dm
+++ b/code/modules/food_and_drinks/food/snacks.dm
@@ -97,9 +97,12 @@ All foods are distributed among various categories. Use common sense.
return
-/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, def_zone)
+/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
if(user.a_intent == INTENT_HARM)
return ..()
+ INVOKE_ASYNC(src, .proc/attempt_forcefeed, M, user)
+
+/obj/item/reagent_containers/food/snacks/proc/attempt_forcefeed(mob/living/M, mob/living/user)
if(!eatverb)
eatverb = pick("bite","chew","nibble","gnaw","gobble","chomp")
if(!reagents.total_volume) //Shouldn't be needed but it checks to see if it has anything left in it.
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..a38a9237ca 100644
--- a/code/modules/hydroponics/grown/ambrosia.dm
+++ b/code/modules/hydroponics/grown/ambrosia.dm
@@ -22,6 +22,7 @@
endurance = 25
yield = 6
potency = 5
+ instability = 30
icon_dead = "ambrosia-dead"
genes = list(/datum/plant_gene/trait/repeated_harvest)
mutatelist = list(/obj/item/seeds/ambrosia/deus)
@@ -49,6 +50,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/banana.dm b/code/modules/hydroponics/grown/banana.dm
index 81318f8fe1..e825c88524 100644
--- a/code/modules/hydroponics/grown/banana.dm
+++ b/code/modules/hydroponics/grown/banana.dm
@@ -8,6 +8,7 @@
product = /obj/item/reagent_containers/food/snacks/grown/banana
lifespan = 50
endurance = 30
+ instability = 10
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_dead = "banana-dead"
genes = list(/datum/plant_gene/trait/slip, /datum/plant_gene/trait/repeated_harvest)
diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm
index eba7a3d3c4..ca9e1f8a0e 100644
--- a/code/modules/hydroponics/grown/berries.dm
+++ b/code/modules/hydroponics/grown/berries.dm
@@ -10,6 +10,7 @@
maturation = 5
production = 5
yield = 2
+ instability = 30
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_grow = "berry-grow" // Uses one growth icons set for all the subtypes
icon_dead = "berry-dead" // Same for the dead icon
diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm
index 6525ac42d4..1882d5fe97 100644
--- a/code/modules/hydroponics/grown/cannabis.dm
+++ b/code/modules/hydroponics/grown/cannabis.dm
@@ -9,6 +9,7 @@
maturation = 8
potency = 20
growthstages = 1
+ instability = 40
growing_icon = 'goon/icons/obj/hydroponics.dmi'
icon_grow = "cannabis-grow" // Uses one growth icons set for all the subtypes
icon_dead = "cannabis-dead" // Same for the dead icon
@@ -104,6 +105,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 +113,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/cereals.dm b/code/modules/hydroponics/grown/cereals.dm
index 1d1309acb8..9bfdb2979c 100644
--- a/code/modules/hydroponics/grown/cereals.dm
+++ b/code/modules/hydroponics/grown/cereals.dm
@@ -9,6 +9,7 @@
production = 1
yield = 4
potency = 15
+ instability = 20
icon_dead = "wheat-dead"
mutatelist = list(/obj/item/seeds/wheat/oat, /obj/item/seeds/wheat/meat)
reagents_add = list(/datum/reagent/consumable/nutriment = 0.04)
diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm
index 001a90b441..2f8fb12724 100644
--- a/code/modules/hydroponics/grown/chili.dm
+++ b/code/modules/hydroponics/grown/chili.dm
@@ -11,6 +11,7 @@
production = 5
yield = 4
potency = 20
+ instability = 30
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_grow = "chili-grow" // Uses one growth icons set for all the subtypes
icon_dead = "chili-dead" // Same for the dead icon
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/hydroponics/grown/cocoa_vanilla.dm b/code/modules/hydroponics/grown/cocoa_vanilla.dm
index 9ac705aa83..5de0740b87 100644
--- a/code/modules/hydroponics/grown/cocoa_vanilla.dm
+++ b/code/modules/hydroponics/grown/cocoa_vanilla.dm
@@ -10,6 +10,7 @@
maturation = 5
production = 5
yield = 2
+ instability = 20
growthstages = 5
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_grow = "cocoapod-grow"
diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm
index ad09751e44..ae046c9ff3 100644
--- a/code/modules/hydroponics/grown/corn.dm
+++ b/code/modules/hydroponics/grown/corn.dm
@@ -8,6 +8,7 @@
product = /obj/item/reagent_containers/food/snacks/grown/corn
maturation = 8
potency = 20
+ instability = 50 // Corn used to be wheatgrass, but through generations of selective cultivation...
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_grow = "corn-grow" // Uses one growth icons set for all the subtypes
diff --git a/code/modules/hydroponics/grown/cotton.dm b/code/modules/hydroponics/grown/cotton.dm
index a898b76ee8..beff751479 100644
--- a/code/modules/hydroponics/grown/cotton.dm
+++ b/code/modules/hydroponics/grown/cotton.dm
@@ -12,6 +12,7 @@
production = 1
yield = 2
potency = 50
+ instability = 15
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing.dmi'
icon_dead = "cotton-dead"
diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm
index a74850f3be..e6fd991169 100644
--- a/code/modules/hydroponics/grown/grass_carpet.dm
+++ b/code/modules/hydroponics/grown/grass_carpet.dm
@@ -11,6 +11,7 @@
maturation = 2
production = 5
yield = 5
+ instability = 10
growthstages = 2
icon_grow = "grass-grow"
icon_dead = "grass-dead"
diff --git a/code/modules/hydroponics/grown/kudzu.dm b/code/modules/hydroponics/grown/kudzu.dm
index 5f1fdcdaa9..73026e546a 100644
--- a/code/modules/hydroponics/grown/kudzu.dm
+++ b/code/modules/hydroponics/grown/kudzu.dm
@@ -11,6 +11,7 @@
lifespan = 20
endurance = 10
yield = 4
+ instability = 55
growthstages = 4
rarity = 30
var/list/mutations = list()
diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm
index 940508f66b..b988261770 100644
--- a/code/modules/hydroponics/grown/melon.dm
+++ b/code/modules/hydroponics/grown/melon.dm
@@ -8,6 +8,7 @@
product = /obj/item/reagent_containers/food/snacks/grown/watermelon
lifespan = 50
endurance = 40
+ instability = 20
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_dead = "watermelon-dead"
genes = list(/datum/plant_gene/trait/repeated_harvest)
@@ -75,4 +76,4 @@
to_chat(user, "[src] rapidly turns into ash!")
qdel(src)
new /obj/effect/decal/cleanable/ash(drop_location())
-*/
\ No newline at end of file
+*/
diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm
index fe60e9f397..59301732c6 100644
--- a/code/modules/hydroponics/grown/misc.dm
+++ b/code/modules/hydroponics/grown/misc.dm
@@ -11,6 +11,7 @@
production = 1
yield = 6
potency = 10
+ instability = 35
growthstages = 3
grind_results = list(/datum/reagent/mustardgrind = 1)
growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi'
@@ -111,6 +112,7 @@
maturation = 3
production = 5
yield = 4
+ instability = 10
growthstages = 1
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
genes = list(/datum/plant_gene/trait/repeated_harvest)
@@ -140,6 +142,7 @@
endurance = 50
maturation = 3
yield = 4
+ instability = 15
growthstages = 2
reagents_add = list(/datum/reagent/consumable/sugar = 0.25)
mutatelist = list(/obj/item/seeds/bamboo)
@@ -534,4 +537,4 @@
/obj/item/reagent_containers/food/snacks/grown/aloe/microwave_act(obj/machinery/microwave/M)
new /obj/item/stack/medical/aloe(drop_location(), 2)
- qdel(src)
\ No newline at end of file
+ qdel(src)
diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm
index 699a1c798d..80ba6b7375 100644
--- a/code/modules/hydroponics/grown/mushrooms.dm
+++ b/code/modules/hydroponics/grown/mushrooms.dm
@@ -18,6 +18,7 @@
production = 5
yield = 4
potency = 15
+ instability = 30
growthstages = 4
genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism)
growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
@@ -43,6 +44,7 @@
maturation = 10
production = 5
yield = 4
+ instability = 30
growthstages = 3
genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism)
growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
@@ -187,6 +189,7 @@
production = 1
yield = 5
potency = 15
+ instability = 20
growthstages = 3
genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism)
growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
@@ -244,6 +247,7 @@
production = 1
yield = 3 //-> spread
potency = 30 //-> brightness
+ instability = 20
growthstages = 4
rarity = 20
genes = list(/datum/plant_gene/trait/glow, /datum/plant_gene/trait/plant_type/fungal_metabolism)
@@ -334,4 +338,4 @@
/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/shadowshroom/attack_self(mob/user)
. = ..()
if(.)
- investigate_log("was planted by [key_name(user)] at [AREACOORD(user)]", INVESTIGATE_BOTANY)
\ No newline at end of file
+ investigate_log("was planted by [key_name(user)] at [AREACOORD(user)]", INVESTIGATE_BOTANY)
diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm
index cee7748c59..e4aedfd4b0 100644
--- a/code/modules/hydroponics/grown/nettle.dm
+++ b/code/modules/hydroponics/grown/nettle.dm
@@ -8,6 +8,7 @@
lifespan = 30
endurance = 40 // tuff like a toiger
yield = 4
+ instability = 25
growthstages = 5
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/plant_type/weed_hardy)
mutatelist = list(/obj/item/seeds/nettle/death)
diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm
index a75b9caf5f..f6fcd3ae1b 100644
--- a/code/modules/hydroponics/grown/onion.dm
+++ b/code/modules/hydroponics/grown/onion.dm
@@ -10,6 +10,7 @@
production = 4
yield = 6
endurance = 25
+ instability = 10
growthstages = 3
weed_chance = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
diff --git a/code/modules/hydroponics/grown/peas.dm b/code/modules/hydroponics/grown/peas.dm
index 79d506cf56..0775f7c45d 100644
--- a/code/modules/hydroponics/grown/peas.dm
+++ b/code/modules/hydroponics/grown/peas.dm
@@ -8,6 +8,7 @@
product = /obj/item/reagent_containers/food/snacks/grown/peas
maturation = 3
potency = 25
+ instability = 15
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_grow = "peas-grow"
@@ -79,6 +80,7 @@
potency = 75
yield = 1
production = 10
+ instability = 45 //The world is a very unstable place. Constantly changing.
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_grow = "worldpeas-grow"
diff --git a/code/modules/hydroponics/grown/root.dm b/code/modules/hydroponics/grown/root.dm
index ac171b4e45..5f9b55b881 100644
--- a/code/modules/hydroponics/grown/root.dm
+++ b/code/modules/hydroponics/grown/root.dm
@@ -9,6 +9,7 @@
maturation = 10
production = 1
yield = 5
+ instability = 15
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
mutatelist = list(/obj/item/seeds/carrot/parsnip)
@@ -69,6 +70,7 @@
lifespan = 60
endurance = 50
yield = 6
+ instability = 10
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_dead = "whitebeet-dead"
mutatelist = list(/obj/item/seeds/redbeet)
diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm
index 223b2c7bce..1c8055f821 100644
--- a/code/modules/hydroponics/grown/tea_coffee.dm
+++ b/code/modules/hydroponics/grown/tea_coffee.dm
@@ -79,6 +79,7 @@
maturation = 5
production = 5
yield = 5
+ instability = 20
growthstages = 5
icon_dead = "coffee-dead"
genes = list(/datum/plant_gene/trait/repeated_harvest)
diff --git a/code/modules/hydroponics/grown/tomato.dm b/code/modules/hydroponics/grown/tomato.dm
index d3525c951d..af7d192e00 100644
--- a/code/modules/hydroponics/grown/tomato.dm
+++ b/code/modules/hydroponics/grown/tomato.dm
@@ -6,6 +6,7 @@
species = "tomato"
plantname = "Tomato Plants"
product = /obj/item/reagent_containers/food/snacks/grown/tomato
+ instability = 25
maturation = 8
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_grow = "tomato-grow"
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index abf6632939..b032b84a77 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -1,7 +1,7 @@
// Plant analyzer
/obj/item/plant_analyzer
name = "plant analyzer"
- desc = "A scanner used to evaluate a plant's various areas of growth."
+ desc = "A scanner used to evaluate a plant's various areas of growth, and genetic traits."
icon = 'icons/obj/device.dmi'
icon_state = "hydro"
item_state = "analyzer"
@@ -10,6 +10,25 @@
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_BELT
custom_materials = list(/datum/material/iron=30, /datum/material/glass=20)
+ var/scan_mode = PLANT_SCANMODE_STATS
+
+/obj/item/plant_analyzer/attack_self(mob/user)
+ . = ..()
+ scan_mode = !scan_mode
+ to_chat(user, "You switch [src] to [scan_mode == PLANT_SCANMODE_CHEMICALS ? "scan for chemical reagents and traits" : "scan for plant growth statistics"].")
+
+/obj/item/plant_analyzer/attack(mob/living/M, mob/living/carbon/human/user)
+ //Checks if target is a podman
+ if(ispodperson(M))
+ user.visible_message("[user] analyzes [M]'s vitals.", \
+ "You analyze [M]'s vitals.")
+ if(scan_mode== PLANT_SCANMODE_STATS)
+ healthscan(user, M, advanced = TRUE)
+ else
+ chemscan(user, M)
+ add_fingerprint(user)
+ return
+ return ..()
// *************************************
// Hydroponics Tools
@@ -103,6 +122,7 @@
throwforce = 5
throw_speed = 2
throw_range = 3
+ attack_speed = CLICK_CD_MELEE
w_class = WEIGHT_CLASS_BULKY
flags_1 = CONDUCT_1
armour_penetration = 20
@@ -125,9 +145,12 @@
playsound(src,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
return (BRUTELOSS)
-/obj/item/scythe/pre_attack(atom/A, mob/living/user, params)
+/obj/item/scythe/pre_attack(atom/A, mob/living/user, params, attackchain_flags, damage_multiplier)
+ . = ..()
+ if(. & STOP_ATTACK_PROC_CHAIN)
+ return
if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user))
- return ..()
+ return
else
var/turf/user_turf = get_turf(user)
var/dir_to_target = get_dir(user_turf, get_turf(A))
@@ -138,11 +161,12 @@
var/turf/T = get_step(user_turf, turn(dir_to_target, i))
for(var/obj/structure/spacevine/V in T)
if(user.Adjacent(V))
- melee_attack_chain(user, V)
+ melee_attack_chain(user, V, attackchain_flags = ATTACK_IGNORE_CLICKDELAY)
stam_gain += 5 //should be hitcost
swiping = FALSE
stam_gain += 2 //Initial hitcost
user.adjustStaminaLoss(-stam_gain)
+ user.DelayNextAction()
// *************************************
// Nutrient defines for hydroponics
@@ -192,4 +216,4 @@
/obj/item/reagent_containers/glass/bottle/killer/pestkiller
name = "bottle of pest spray"
desc = "Contains a pesticide."
- list_reagents = list(/datum/reagent/toxin/pestkiller = 50)
\ No newline at end of file
+ list_reagents = list(/datum/reagent/toxin/pestkiller = 50)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index a208f2de3c..d6022c1345 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -6,9 +6,10 @@
pixel_z = 8
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
circuit = /obj/item/circuitboard/machine/hydroponics
+ idle_power_usage = 0
var/waterlevel = 100 //The amount of water in the tray (max 100)
var/maxwater = 100 //The maximum amount of water in the tray
- var/nutrilevel = 10 //The amount of nutrient in the tray (max 10)
+ var/nutridrain = 1 // How many units of nutrient will be drained in the tray
var/maxnutri = 10 //The maximum nutrient of water in the tray
var/pestlevel = 0 //The amount of pests in the tray (max 10)
var/weedlevel = 0 //The amount of weeds in the tray (max 10)
@@ -16,21 +17,25 @@
var/mutmod = 1 //Nutriment's effect on mutations
var/toxic = 0 //Toxicity in the tray?
var/age = 0 //Current age
- var/dead = 0 //Is it dead?
+ var/dead = FALSE //Is it dead?
var/plant_health //Its health
var/lastproduce = 0 //Last time it was harvested
var/lastcycle = 0 //Used for timing of cycles.
var/cycledelay = 200 //About 10 seconds / cycle
- var/harvest = 0 //Ready to harvest?
+ var/harvest = FALSE //Ready to harvest?
var/obj/item/seeds/myseed = null //The currently planted seed
var/rating = 1
- var/unwrenchable = 1
+ var/unwrenchable = TRUE
var/recent_bee_visit = FALSE //Have we been visited by a bee recently, so bees dont overpollinate one plant
- var/using_irrigation = FALSE //If the tray is connected to other trays via irrigation hoses
- var/self_sufficiency_req = 20 //Required total dose to make a self-sufficient hydro tray. 1:1 with earthsblood.
- var/self_sufficiency_progress = 0
+ var/mob/lastuser //Last user to add reagents to a tray. Mostly for logging.
var/self_sustaining = FALSE //If the tray generates nutrients and water on its own
- var/canirrigate = TRUE //tin
+ // Here lies irrigation. You won't be missed, because you were never used.
+
+/obj/machinery/hydroponics/Initialize()
+ //Here lies "nutrilevel", killed by ArcaneMusic 20??-2019. Finally, we strive for a better future. Please use "reagents" instead
+ create_reagents(20)
+ reagents.add_reagent(/datum/reagent/plantnutriment/eznutriment, 10) //Half filled nutrient trays for dirt trays to have more to grow with in prison/lavaland.
+ . = ..()
/obj/machinery/hydroponics/constructable
name = "hydroponics tray"
@@ -43,14 +48,18 @@
tmp_capacity += M.rating
for (var/obj/item/stock_parts/manipulator/M in component_parts)
rating = M.rating
- maxwater = tmp_capacity * 50 // Up to 300
- maxnutri = tmp_capacity * 5 // Up to 30
+ maxwater = tmp_capacity * 50 // Up to 400
+ maxnutri = (tmp_capacity * 5) + STATIC_NUTRIENT_CAPACITY // Up to 50 Maximum
+ reagents.maximum_volume = maxnutri
+ nutridrain = 1/rating
/obj/machinery/hydroponics/constructable/examine(mob/user)
. = ..()
+ . += "Use Ctrl-Click to activate autogrow. Alt-Click to empty the tray's nutrients."
if(in_range(user, src) || isobserver(user))
. += "The status display reads: Tray efficiency at [rating*100]%."
+
/obj/machinery/hydroponics/Destroy()
if(myseed)
qdel(myseed)
@@ -62,34 +71,11 @@
// handle opening the panel
if(default_deconstruction_screwdriver(user, icon_state, icon_state, I))
return
-
- // handle deconstructing the machine, if permissible
- if (I.tool_behaviour == TOOL_CROWBAR && using_irrigation)
- to_chat(user, "Disconnect the hoses first!")
- return
- else if(default_deconstruction_crowbar(I))
+ if(default_deconstruction_crowbar(I))
return
return ..()
-/obj/machinery/hydroponics/proc/FindConnected()
- var/list/connected = list()
- var/list/processing_atoms = list(src)
-
- while(processing_atoms.len)
- var/atom/a = processing_atoms[1]
- for(var/step_dir in GLOB.cardinals)
- var/obj/machinery/hydroponics/h = locate() in get_step(a, step_dir)
- // Soil plots aren't dense
- if(h && h.using_irrigation && h.density && !(h in connected) && !(h in processing_atoms))
- processing_atoms += h
-
- processing_atoms -= a
- connected += a
-
- return connected
-
-
/obj/machinery/hydroponics/bullet_act(obj/item/projectile/Proj) //Works with the Somatoray to modify plant variables.
if(!myseed)
return ..()
@@ -107,12 +93,16 @@
if(myseed && (myseed.loc != src))
myseed.forceMove(src)
- if(self_sustaining)
- adjustNutri(1)
- adjustWater(rand(3,5))
- adjustWeeds(-2)
- adjustPests(-2)
- adjustToxic(-2)
+ if(!powered() && self_sustaining)
+ visible_message("[name]'s auto-grow functionality shuts off!")
+ idle_power_usage = 0
+ self_sustaining = FALSE
+ update_icon()
+
+ else if(self_sustaining)
+ adjustWater(rand(1,2))
+ adjustWeeds(-1)
+ adjustPests(-1)
if(world.time > (lastcycle + cycledelay))
lastcycle = world.time
@@ -124,13 +114,17 @@
needs_update = 1
-//Nutrients//////////////////////////////////////////////////////////////
- // Nutrients deplete slowly
- if(prob(50))
- adjustNutri(-1 / rating)
+//Nutrients//////////////////////////////////////////////////////////////
+ // Nutrients deplete at a constant rate, since new nutrients can boost stats far easier.
+ apply_chemicals(lastuser)
+ if(self_sustaining)
+ reagents.remove_any(min(0.5, nutridrain))
+ else
+ reagents.remove_any(nutridrain)
+
// Lack of nutrients hurts non-weeds
- if(nutrilevel <= 0 && !myseed.get_gene(/datum/plant_gene/trait/plant_type/weed_hardy))
+ if(reagents.total_volume <= 0 && !myseed.get_gene(/datum/plant_gene/trait/plant_type/weed_hardy))
adjustHealth(-rand(1,3))
//Photosynthesis/////////////////////////////////////////////////////////
@@ -156,7 +150,7 @@
adjustHealth(-rand(0,2) / rating)
// Sufficient water level and nutrient level = plant healthy but also spawns weeds
- else if(waterlevel > 10 && nutrilevel > 0)
+ else if(waterlevel > 10 && reagents.total_volume > 0)
adjustHealth(rand(1,2) / rating)
if(myseed && prob(myseed.weed_chance))
adjustWeeds(myseed.weed_rate)
@@ -201,6 +195,21 @@
if(weedlevel >= 5 && !myseed.get_gene(/datum/plant_gene/trait/plant_type/weed_hardy))
adjustHealth(-1 / rating)
+//This is where stability mutations exist now.
+ if(myseed.instability >= 80)
+ var/mutation_chance = myseed.instability - 75
+ mutate(0, 0, 0, 0, 0, 0, 0, mutation_chance, 0) //Scaling odds of a random trait or chemical
+ if(myseed.instability >= 60)
+ if(prob((myseed.instability)/2) && !self_sustaining && length(myseed.mutatelist)) //Minimum 30%, Maximum 50% chance of mutating every age tick when not on autogrow.
+ mutatespecie()
+ myseed.instability = myseed.instability/2
+ if(myseed.instability >= 40)
+ if(prob(myseed.instability))
+ hardmutate()
+ if(myseed.instability >= 20 )
+ if(prob(myseed.instability))
+ mutate()
+
//Health & Age///////////////////////////////////////////////////////////
// Plant dies if plant_health <= 0
@@ -214,15 +223,14 @@
// Harvest code
if(age > myseed.production && (age - lastproduce) > myseed.production && (!harvest && !dead))
- nutrimentMutation()
if(myseed && myseed.yield != -1) // Unharvestable shouldn't be harvested
- harvest = 1
+ harvest = TRUE
else
lastproduce = age
if(prob(5)) // On each tick, there's a 5 percent chance the pest population will increase
adjustPests(1 / rating)
else
- if(waterlevel > 10 && nutrilevel > 0 && prob(10)) // If there's no plant, the percentage chance is 10%
+ if(waterlevel > 10 && reagents.total_volume > 0 && prob(10)) // If there's no plant, the percentage chance is 10%
adjustWeeds(1 / rating)
// Weeeeeeeeeeeeeeedddssss
@@ -243,25 +251,6 @@
selectedtrait.on_grow(src)
return
-/obj/machinery/hydroponics/proc/nutrimentMutation()
- if (mutmod == 0)
- return
- if (mutmod == 1)
- if(prob(80)) //80%
- mutate()
- else if(prob(75)) //15%
- hardmutate()
- return
- if (mutmod == 2)
- if(prob(50)) //50%
- mutate()
- else if(prob(50)) //25%
- hardmutate()
- else if(prob(50)) //12.5%
- mutatespecie()
- return
- return
-
/obj/machinery/hydroponics/update_icon()
//Refreshes the icon and sets the luminosity
cut_overlays()
@@ -273,8 +262,6 @@
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing"))
set_light(3)
- update_icon_hoses()
-
if(myseed)
update_icon_plant()
update_icon_lights()
@@ -288,15 +275,6 @@
return
-/obj/machinery/hydroponics/proc/update_icon_hoses()
- var/n = 0
- for(var/Dir in GLOB.cardinals)
- var/obj/machinery/hydroponics/t = locate() in get_step(src,Dir)
- if(t && t.using_irrigation && using_irrigation)
- n += Dir
-
- icon_state = "hoses-[n]"
-
/obj/machinery/hydroponics/proc/update_icon_plant()
var/mutable_appearance/plant_overlay = mutable_appearance(myseed.growing_icon, layer = OBJ_LAYER + 0.01)
if(dead)
@@ -314,7 +292,7 @@
/obj/machinery/hydroponics/proc/update_icon_lights()
if(waterlevel <= 10)
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "over_lowwater3"))
- if(nutrilevel <= 2)
+ if(reagents.total_volume <= 2)
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "over_lownutri3"))
if(plant_health <= (myseed.endurance / 2))
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "over_lowhealth3"))
@@ -337,19 +315,17 @@
else
. += "It's empty."
- if(!self_sustaining)
- . += "Water: [waterlevel]/[maxwater]."
- . += "Nutrient: [nutrilevel]/[maxnutri]."
- if(self_sufficiency_progress > 0)
- var/percent_progress = round(self_sufficiency_progress * 100 / self_sufficiency_req)
- . += "Treatment for self-sustenance are [percent_progress]% complete."
- else
- . += "It doesn't require any water or nutrients."
+ . += "Water: [waterlevel]/[maxwater].\n"+\
+ "Nutrient: [reagents.total_volume]/[maxnutri]."
+ if(self_sustaining)
+ . += "The tray's autogrow is active, halving active reagent drain, and actively maintaning the plant."
if(weedlevel >= 5)
- . += "It's filled with weeds!"
+ to_chat(user, "It's filled with weeds!")
if(pestlevel >= 5)
- . += "It's filled with tiny worms!"
+ to_chat(user, "It's filled with tiny worms!")
+ to_chat(user, "" )
+
/obj/machinery/hydroponics/proc/weedinvasion() // If a weed growth is sufficient, this happens.
@@ -424,11 +400,7 @@
sleep(5) // Wait a while
update_icon()
visible_message("[oldPlantName] suddenly mutates into [myseed.plantname]!")
- name = "hydroponics tray ([myseed.plantname])"
- if(myseed.product)
- desc = initial(myseed.product.desc)
- else
- desc = initial(desc)
+ TRAY_NAME_UPDATE
/obj/machinery/hydroponics/proc/mutateweed() // If the weeds gets the mutagent instead. Mind you, this pretty much destroys the old plant
if( weedlevel > 5 )
@@ -448,18 +420,23 @@
sleep(5) // Wait a while
update_icon()
visible_message("The mutated weeds in [src] spawn some [myseed.plantname]!")
+ TRAY_NAME_UPDATE
else
to_chat(usr, "The few weeds in [src] seem to react, but only for a moment...")
-/obj/machinery/hydroponics/proc/plantdies() // OH NOES!!!!! I put this all in one function to make things easier
+/**
+ * Plant Death Proc.
+ * Cleans up various stats for the plant upon death, including pests, harvestability, and plant health.
+ */
+/obj/machinery/hydroponics/proc/plantdies()
plant_health = 0
- harvest = 0
+ harvest = FALSE
pestlevel = 0 // Pests die
+ lastproduce = 0
if(!dead)
update_icon()
- dead = 1
-
+ dead = TRUE
/obj/machinery/hydroponics/proc/mutatepest(mob/user)
@@ -471,250 +448,6 @@
else
to_chat(user, "The pests seem to behave oddly, but quickly settle down...")
-/obj/machinery/hydroponics/proc/applyChemicals(datum/reagents/S, mob/user)
- if(myseed)
- myseed.on_chem_reaction(S) //In case seeds have some special interactions with special chems, currently only used by vines
-
- // Requires 5 mutagen to possibly change species.// Poor man's mutagen.
- if(S.has_reagent(/datum/reagent/toxin/mutagen, 5) || S.has_reagent(/datum/reagent/radium, 10) || S.has_reagent(/datum/reagent/uranium, 10))
- switch(rand(100))
- if(91 to 100)
- adjustHealth(-10)
- to_chat(user, "The plant shrivels and burns.")
- if(81 to 90)
- mutatespecie()
- if(66 to 80)
- hardmutate()
- if(41 to 65)
- mutate()
- if(21 to 41)
- to_chat(user, "The plants don't seem to react...")
- if(11 to 20)
- mutateweed()
- if(1 to 10)
- mutatepest(user)
- else
- to_chat(user, "Nothing happens...")
-
- // 2 or 1 units is enough to change the yield and other stats.// Can change the yield and other stats, but requires more than mutagen
- else if(S.has_reagent(/datum/reagent/toxin/mutagen, 2) || S.has_reagent(/datum/reagent/radium, 5) || S.has_reagent(/datum/reagent/uranium, 5))
- hardmutate()
- else if(S.has_reagent(/datum/reagent/toxin/mutagen, 1) || S.has_reagent(/datum/reagent/radium, 2) || S.has_reagent(/datum/reagent/uranium, 2))
- mutate()
-
- // After handling the mutating, we now handle the damage from adding crude radioactives...
- if(S.has_reagent(/datum/reagent/uranium, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/uranium) * 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/uranium) * 2))
- if(S.has_reagent(/datum/reagent/radium, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/radium) * 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/radium) * 3)) // Radium is harsher (OOC: also easier to produce)
-
- // Nutriments
- if(S.has_reagent(/datum/reagent/plantnutriment/eznutriment, 1))
- yieldmod = 1
- mutmod = 1
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/plantnutriment/eznutriment) * 1))
-
- if(S.has_reagent(/datum/reagent/plantnutriment/left4zednutriment, 1))
- yieldmod = 0
- mutmod = 2
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/plantnutriment/left4zednutriment) * 1))
-
- if(S.has_reagent(/datum/reagent/plantnutriment/robustharvestnutriment, 1))
- yieldmod = 1.3
- mutmod = 0
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/plantnutriment/robustharvestnutriment) *1 ))
-
- // Ambrosia Gaia produces earthsblood.
- if(S.has_reagent(/datum/reagent/medicine/earthsblood))
- self_sufficiency_progress += S.get_reagent_amount(/datum/reagent/medicine/earthsblood)
- if(self_sufficiency_progress >= self_sufficiency_req)
- become_self_sufficient()
- else if(!self_sustaining)
- to_chat(user, "[src] warms as it might on a spring day under a genuine Sun.")
-
- // Antitoxin binds shit pretty well. So the tox goes significantly down
- if(S.has_reagent(/datum/reagent/medicine/charcoal, 1))
- adjustToxic(-round(S.get_reagent_amount(/datum/reagent/medicine/charcoal) * 2))
-
- // Toxins, not good for anything
- if(S.has_reagent(/datum/reagent/toxin, 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin) * 2))
-
- // Milk is good for humans, but bad for plants. The sugars canot be used by plants, and the milk fat fucks up growth. Not shrooms though. I can't deal with this now...
- if(S.has_reagent(/datum/reagent/consumable/milk, 1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/milk) * 0.1))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/consumable/milk) * 0.9))
-
- // Beer is a chemical composition of alcohol and various other things. It's a shitty nutrient but hey, it's still one. Also alcohol is bad, mmmkay?
- if(S.has_reagent(/datum/reagent/consumable/ethanol/beer, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/consumable/ethanol/beer) * 0.05))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/ethanol/beer) * 0.25))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/consumable/ethanol/beer) * 0.7))
-
- // Fluorine one of the most corrosive and deadly gasses
- if(S.has_reagent(/datum/reagent/fluorine, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/fluorine) * 2))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/fluorine) * 2.5))
- adjustWater(-round(S.get_reagent_amount(/datum/reagent/fluorine) * 0.5))
- adjustWeeds(-rand(1,4))
-
- // Chlorine one of the most corrosive and deadly gasses
- if(S.has_reagent(/datum/reagent/chlorine, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/chlorine) * 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/chlorine) * 1.5))
- adjustWater(-round(S.get_reagent_amount(/datum/reagent/chlorine) * 0.5))
- adjustWeeds(-rand(1,3))
-
- // White Phosphorous + water -> phosphoric acid. That's not a good thing really.
- // Phosphoric salts are beneficial though. And even if the plant suffers, in the long run the tray gets some nutrients. The benefit isn't worth that much.
- if(S.has_reagent(/datum/reagent/phosphorus, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/phosphorus) * 0.75))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/phosphorus) * 0.1))
- adjustWater(-round(S.get_reagent_amount(/datum/reagent/phosphorus) * 0.5))
- adjustWeeds(-rand(1,2))
-
- // Plants should not have sugar, they can't use it and it prevents them getting water/nutients, it is good for mold though...
- if(S.has_reagent(/datum/reagent/consumable/sugar, 1))
- adjustWeeds(rand(1,2))
- adjustPests(rand(1,2))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/sugar) * 0.1))
-
- // It is water!
- if(S.has_reagent(/datum/reagent/water, 1))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/water) * 1))
-
- // Holy water. Mostly the same as water, it also heals the plant a little with the power of the spirits~
- if(S.has_reagent(/datum/reagent/water/holywater, 1))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/water/holywater) * 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/water/holywater) * 0.1))
-
- // A variety of nutrients are dissolved in club soda, without sugar.
- // These nutrients include carbon, oxygen, hydrogen, phosphorous, potassium, sulfur and sodium, all of which are needed for healthy plant growth.
- if(S.has_reagent(/datum/reagent/consumable/sodawater, 1))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/consumable/sodawater) * 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/consumable/sodawater) * 0.1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/sodawater) * 0.1))
-
- // Sulphuric Acid
- if(S.has_reagent(/datum/reagent/toxin/acid, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/toxin/acid) * 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin/acid) * 1.5))
- adjustWeeds(-rand(1,2))
-
- // Acid
- if(S.has_reagent(/datum/reagent/toxin/acid/fluacid, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/toxin/acid/fluacid) * 2))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin/acid/fluacid) * 3))
- adjustWeeds(-rand(1,4))
-
- // Plant-B-Gone is just as bad
- if(S.has_reagent(/datum/reagent/toxin/plantbgone, 1))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/toxin/plantbgone) * 5))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin/plantbgone) * 6))
- adjustWeeds(-rand(4,8))
-
- // Napalm, not known for being good for anything organic
- if(S.has_reagent(/datum/reagent/napalm, 1))
- if(!(myseed.resistance_flags & FIRE_PROOF))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/napalm) * 6))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/napalm) * 7))
- adjustWeeds(-rand(5,9))
-
- //Weed Spray
- if(S.has_reagent(/datum/reagent/toxin/plantbgone/weedkiller, 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin/plantbgone/weedkiller) * 0.5))
- //old toxicity was 4, each spray is default 10 (minimal of 5) so 5 and 2.5 are the new ammounts
- adjustWeeds(-rand(1,2))
-
- //Pest Spray
- if(S.has_reagent(/datum/reagent/toxin/pestkiller, 1))
- adjustToxic(round(S.get_reagent_amount(/datum/reagent/toxin/pestkiller) * 0.5))
- adjustPests(-rand(1,2))
-
- // Healing
- if(S.has_reagent(/datum/reagent/medicine/cryoxadone, 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/medicine/cryoxadone) * 3))
- adjustToxic(-round(S.get_reagent_amount(/datum/reagent/medicine/cryoxadone) * 3))
-
- // Ammonia is bad ass.
- if(S.has_reagent(/datum/reagent/ammonia, 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/ammonia) * 0.5))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/ammonia) * 1))
- if(myseed)
- myseed.adjust_yield(round(S.get_reagent_amount(/datum/reagent/ammonia) * 0.01))
-
- // Saltpetre is used for gardening IRL, to simplify highly, it speeds up growth and strengthens plants
- if(S.has_reagent(/datum/reagent/saltpetre, 1))
- var/salt = S.get_reagent_amount(/datum/reagent/saltpetre)
- adjustHealth(round(salt * 0.25))
- if (myseed)
- myseed.adjust_production(-round(salt/100)-prob(salt%100))
- myseed.adjust_potency(round(salt*0.5))
-
- // Ash is also used IRL in gardening, as a fertilizer enhancer and weed killer
- if(S.has_reagent(/datum/reagent/ash, 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/ash) * 0.25))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/ash) * 0.5))
- adjustWeeds(-1)
-
- // Diethylamine is more bad ass, and pests get hurt by the corrosive nature of it, not the plant.
- if(S.has_reagent(/datum/reagent/diethylamine, 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/diethylamine) * 1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/diethylamine) * 2))
- if(myseed)
- myseed.adjust_yield(round(S.get_reagent_amount(/datum/reagent/diethylamine) * 0.02))
- adjustPests(-rand(1,2))
-
- // Nutriment Compost, effectively
- if(S.has_reagent(/datum/reagent/consumable/nutriment, 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/consumable/nutriment) * 0.5))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/nutriment) * 1))
-
- // Virusfood Compost for EVERYTHING
- if(S.has_reagent(/datum/reagent/toxin/mutagen/mutagenvirusfood, 1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/toxin/mutagen/mutagenvirusfood) * 0.5))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/toxin/mutagen/mutagenvirusfood) * 0.5))
-
- // Blood
- if(S.has_reagent(/datum/reagent/blood, 1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/blood) * 1))
- adjustPests(rand(2,4))
-
- // Strange reagent
- if(S.has_reagent(/datum/reagent/medicine/strange_reagent, 1))
- spawnplant()
-
- // Honey, Pests are dieing of sugar, so is the plant
- if(S.has_reagent(/datum/reagent/consumable/honey, 1))
- adjustPests(-rand(2,5))
- adjustHealth(-round(S.get_reagent_amount(/datum/reagent/consumable/honey) * 1))
-
- // Buzz Fuzz, a drink seemingly made for plants...
- if(S.has_reagent(/datum/reagent/consumable/buzz_fuzz, 1))
- adjustPests(-rand(2,5))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/consumable/buzz_fuzz) * 0.1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/consumable/buzz_fuzz) * 0.5))
-
- // Adminordrazine the best stuff there is. For testing/debugging.
- if(S.has_reagent(/datum/reagent/medicine/adminordrazine, 1))
- adjustWater(round(S.get_reagent_amount(/datum/reagent/medicine/adminordrazine) * 1))
- adjustHealth(round(S.get_reagent_amount(/datum/reagent/medicine/adminordrazine) * 1))
- adjustNutri(round(S.get_reagent_amount(/datum/reagent/medicine/adminordrazine) * 1))
- adjustPests(-rand(1,5))
- adjustWeeds(-rand(1,5))
- if(S.has_reagent(/datum/reagent/medicine/adminordrazine, 5))
- switch(rand(100))
- if(66 to 100)
- mutatespecie()
- if(33 to 65)
- mutateweed()
- if(1 to 32)
- mutatepest(user)
- else
- to_chat(user, "Nothing happens...")
-
/obj/machinery/hydroponics/attackby(obj/item/O, mob/user, params)
//Called when mob user "attacks" it with object O
if(istype(O, /obj/item/reagent_containers) ) // Syringe stuff (and other reagent containers now too)
@@ -730,13 +463,20 @@
to_chat(user, "[reagent_source] is empty.")
return 1
+ if(reagents.total_volume >= reagents.maximum_volume && !reagent_source.reagents.has_reagent(/datum/reagent/water, 1))
+ to_chat(user, "[src] is full.")
+ return
+
var/list/trays = list(src)//makes the list just this in cases of syringes and compost etc
var/target = myseed ? myseed.plantname : src
var/visi_msg = ""
- var/irrigate = 0 //How am I supposed to irrigate pill contents?
var/transfer_amount
if(istype(reagent_source, /obj/item/reagent_containers/food/snacks) || istype(reagent_source, /obj/item/reagent_containers/pill))
+ if(istype(reagent_source, /obj/item/reagent_containers/food/snacks))
+ var/obj/item/reagent_containers/food/snacks/R = reagent_source
+ if (R.trash)
+ R.generate_trash(get_turf(user))
visi_msg="[user] composts [reagent_source], spreading it through [target]"
transfer_amount = reagent_source.reagents.total_volume
else
@@ -746,41 +486,27 @@
visi_msg="[user] injects [target] with [syr]"
if(syr.reagents.total_volume <= syr.amount_per_transfer_from_this)
syr.mode = 0
- else if(istype(reagent_source, /obj/item/reagent_containers/spray/))
- visi_msg="[user] sprays [target] with [reagent_source]"
- playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6)
- irrigate = 1
- else if(transfer_amount) // Droppers, cans, beakers, what have you.
- visi_msg="[user] uses [reagent_source] on [target]"
- irrigate = 1
// Beakers, bottles, buckets, etc.
if(reagent_source.is_drainable())
- playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
-
- if(irrigate && transfer_amount > 30 && reagent_source.reagents.total_volume >= 30 && using_irrigation)
- trays = FindConnected()
- if (trays.len > 1)
- visi_msg += ", setting off the irrigation system"
+ playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
if(visi_msg)
visible_message("[visi_msg].")
- var/split = round(transfer_amount/trays.len)
-
+
for(var/obj/machinery/hydroponics/H in trays)
//cause I don't want to feel like im juggling 15 tamagotchis and I can get to my real work of ripping flooring apart in hopes of validating my life choices of becoming a space-gardener
-
- var/datum/reagents/S = new /datum/reagents() //This is a strange way, but I don't know of a better one so I can't fix it at the moment...
- S.my_atom = H
-
- reagent_source.reagents.trans_to(S,split)
+ //This was originally in apply_chemicals, but due to apply_chemicals only holding nutrients, we handle it here now.
+ if(reagent_source.reagents.has_reagent(/datum/reagent/water, 1))
+ var/water_amt = reagent_source.reagents.get_reagent_amount(/datum/reagent/water) * transfer_amount / reagent_source.reagents.total_volume
+ H.adjustWater(round(water_amt))
+ reagent_source.reagents.remove_reagent(/datum/reagent/water, water_amt)
+ reagent_source.reagents.trans_to(H.reagents, transfer_amount)
if(istype(reagent_source, /obj/item/reagent_containers/food/snacks) || istype(reagent_source, /obj/item/reagent_containers/pill))
qdel(reagent_source)
-
- H.applyChemicals(S, user)
-
- S.clear_reagents()
- qdel(S)
+ lastuser = user
+ H.update_icon()
+ return 1
H.update_icon()
if(reagent_source) // If the source wasn't composted and destroyed
reagent_source.update_icon()
@@ -793,39 +519,43 @@
if(!user.transferItemToLoc(O, src))
return
to_chat(user, "You plant [O].")
- dead = 0
+ dead = FALSE
myseed = O
- name = "hydroponics tray ([myseed.plantname])"
- if(!myseed.productdesc) //we haven't changed our produce's description
- if(myseed.product)
- myseed.productdesc = initial(myseed.product.desc)
- else if(myseed.desc)
- myseed.productdesc = myseed.desc
- else
- myseed.productdesc = "A fascinating specimen."
- desc = myseed.productdesc
+ TRAY_NAME_UPDATE
age = 1
plant_health = myseed.endurance
lastcycle = world.time
update_icon()
+ return
else
to_chat(user, "[src] already has seeds in it!")
+ return
else if(istype(O, /obj/item/plant_analyzer))
+ var/obj/item/plant_analyzer/P_analyzer = O
if(myseed)
- to_chat(user, "*** [myseed.plantname] ***" )
- to_chat(user, "- Plant Age: [age]")
- var/list/text_string = myseed.get_analyzer_text()
- if(text_string)
- to_chat(user, text_string)
+ if(P_analyzer.scan_mode == PLANT_SCANMODE_STATS)
+ to_chat(user, "*** [myseed.plantname] ***" )
+ to_chat(user, "- Plant Age: [age]")
+ var/list/text_string = myseed.get_analyzer_text()
+ if(text_string)
+ to_chat(user, text_string)
+ to_chat(user, "*---------*")
+ if(myseed.reagents_add && P_analyzer.scan_mode == PLANT_SCANMODE_CHEMICALS)
+ to_chat(user, "- Plant Reagents -")
+ to_chat(user, "*---------*")
+ for(var/datum/plant_gene/reagent/G in myseed.genes)
+ to_chat(user, "- [G.get_name()] -")
+ to_chat(user, "*---------*")
else
to_chat(user, "No plant found.")
to_chat(user, "- Weed level: [weedlevel] / 10")
to_chat(user, "- Pest level: [pestlevel] / 10")
to_chat(user, "- Toxicity level: [toxic] / 100")
to_chat(user, "- Water level: [waterlevel] / [maxwater]")
- to_chat(user, "- Nutrition level: [nutrilevel] / [maxnutri]")
+ to_chat(user, "- Nutrition level: [reagents.total_volume] / [maxnutri]")
to_chat(user, "")
+ return
else if(istype(O, /obj/item/cultivator))
if(weedlevel > 0)
@@ -843,18 +573,6 @@
else if(default_unfasten_wrench(user, O))
return
- else if(istype(O, /obj/item/wirecutters) && unwrenchable)
- if (!anchored)
- to_chat(user, "Anchor the tray first!")
- return
- if(canirrigate)
- using_irrigation = !using_irrigation
- O.play_tool_sound(src)
- user.visible_message("[user] [using_irrigation ? "" : "dis"]connects [src]'s irrigation hoses.", \
- "You [using_irrigation ? "" : "dis"]connect [src]'s irrigation hoses.")
- for(var/obj/machinery/hydroponics/h in range(1,src))
- h.update_icon()
-
else if(istype(O, /obj/item/shovel/spade))
if(!myseed && !weedlevel)
to_chat(user, "[src] doesn't have any plants or weeds!")
@@ -882,55 +600,75 @@
if (!unwrenchable) // case also covered by NODECONSTRUCT checks in default_unfasten_wrench
return CANT_UNFASTEN
- if (using_irrigation)
- if (!silent)
- to_chat(user, "Disconnect the hoses first!")
- return FAILED_UNFASTEN
-
return ..()
-/obj/machinery/hydroponics/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/obj/machinery/hydroponics/attack_hand(mob/user)
+ . = ..()
+ if(.)
+ return
if(issilicon(user)) //How does AI know what plant is?
return
if(harvest)
- return myseed.harvest(user)
+ myseed.harvest(user)
+ return
else if(dead)
- dead = 0
+ dead = FALSE
to_chat(user, "You remove the dead plant from [src].")
qdel(myseed)
myseed = null
update_icon()
- name = initial(name)
- desc = initial(desc)
+ TRAY_NAME_UPDATE
else
if(user)
examine(user)
+/obj/machinery/hydroponics/CtrlClick(mob/user)
+ . = ..()
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ if(!powered())
+ to_chat(user, "[name] has no power.")
+ return
+ if(!anchored)
+ return
+ self_sustaining = !self_sustaining
+ idle_power_usage = self_sustaining ? 2500 : 0
+ to_chat(user, "You [self_sustaining ? "activate" : "deactivated"] [src]'s autogrow function[self_sustaining ? ", maintaining the tray's health while using high amounts of power" : ""].")
+ update_icon()
+
+/obj/machinery/hydroponics/AltClick(mob/user)
+ . = ..()
+ if(!anchored)
+ update_icon()
+ return FALSE
+ var/warning = alert(user, "Are you sure you wish to empty the tray's nutrient beaker?","Empty Tray Nutrients?", "Yes", "No")
+ if(warning == "Yes" && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ reagents.clear_reagents()
+ to_chat(user, "You empty [src]'s nutrient tank.")
+
/obj/machinery/hydroponics/proc/update_tray(mob/user)
- harvest = 0
+ harvest = FALSE
lastproduce = age
if(istype(myseed, /obj/item/seeds/replicapod))
- if(user)//runtimes
- to_chat(user, "You harvest from the [myseed.plantname].")
+ to_chat(user, "You harvest from the [myseed.plantname].")
else if(myseed.getYield() <= 0)
- if(user)
- to_chat(user, "You fail to harvest anything useful!")
+ to_chat(user, "You fail to harvest anything useful!")
else
- if(user)
- to_chat(user, "You harvest [myseed.getYield()] items from the [myseed.plantname].")
+ to_chat(user, "You harvest [myseed.getYield()] items from the [myseed.plantname].")
if(!myseed.get_gene(/datum/plant_gene/trait/repeated_harvest))
qdel(myseed)
myseed = null
- dead = 0
+ dead = FALSE
name = initial(name)
desc = initial(desc)
+ TRAY_NAME_UPDATE
+ if(self_sustaining) //No reason to pay for an empty tray.
+ idle_power_usage = 0
+ self_sustaining = FALSE
update_icon()
-
+
/// Tray Setters - The following procs adjust the tray or plants variables, and make sure that the stat doesn't go out of bounds.///
-/obj/machinery/hydroponics/proc/adjustNutri(adjustamt)
- nutrilevel = clamp(nutrilevel + adjustamt, 0, maxnutri)
-
/obj/machinery/hydroponics/proc/adjustWater(adjustamt)
waterlevel = clamp(waterlevel + adjustamt, 0, maxwater)
@@ -956,11 +694,6 @@
var/mob/living/simple_animal/hostile/C = new chosen
C.faction = list("plants")
-/obj/machinery/hydroponics/proc/become_self_sufficient() // Ambrosia Gaia effect
- visible_message("[src] begins to glow with a beautiful light!")
- self_sustaining = TRUE
- update_icon()
-
///////////////////////////////////////////////////////////////////////////////
/obj/machinery/hydroponics/soil //Not actually hydroponics at all! Honk!
name = "soil"
@@ -973,9 +706,6 @@
flags_1 = NODECONSTRUCT_1
unwrenchable = FALSE
-/obj/machinery/hydroponics/soil/update_icon_hoses()
- return // Has no hoses
-
/obj/machinery/hydroponics/soil/update_icon_lights()
return // Has no lights
@@ -985,3 +715,6 @@
qdel(src)
else
return ..()
+
+/obj/machinery/hydroponics/soil/CtrlClick(mob/user)
+ return //Dirt doesn't have electricity, last I checked.
diff --git a/code/modules/hydroponics/hydroponics_chemreact.dm b/code/modules/hydroponics/hydroponics_chemreact.dm
new file mode 100644
index 0000000000..a4ef78a21f
--- /dev/null
+++ b/code/modules/hydroponics/hydroponics_chemreact.dm
@@ -0,0 +1,32 @@
+/**
+ *This is NOW the gradual affects that each chemical applies on every process() proc. Nutrients now use a more robust reagent holder in order to apply less insane
+ * stat changes as opposed to 271 lines of individual statline effects. Shoutout to the original comments on chems, I just cleaned a few up.
+ */
+/obj/machinery/hydroponics/proc/apply_chemicals(mob/user)
+ ///Contains the reagents within the tray.
+ if(myseed)
+ myseed.on_chem_reaction(reagents) //In case seeds have some special interactions with special chems, currently only used by vines
+ for(var/c in reagents.reagent_list)
+ var/datum/reagent/chem = c
+ chem.on_hydroponics_apply(myseed, reagents, src, user)
+
+
+/obj/machinery/hydroponics/proc/mutation_roll(mob/user)
+ switch(rand(100))
+ if(91 to 100)
+ adjustHealth(-10)
+ visible_message("\The [myseed.plantname] starts to wilt and burn!")
+ return
+ if(41 to 90)
+ if(myseed && !self_sustaining) //Stability
+ myseed.adjust_instability(5)
+ return
+ if(21 to 40)
+ visible_message("\The [myseed.plantname] appears unusually reactive...")
+ return
+ if(11 to 20)
+ mutateweed()
+ return
+ if(1 to 10)
+ mutatepest(user)
+ return
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 887ea0417c..da19fa11f8 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -89,6 +89,12 @@
/datum/plant_gene/core/potency/apply_stat(obj/item/seeds/S)
S.potency = value
+/datum/plant_gene/core/instability
+ name = "Stability"
+ value = 10
+
+/datum/plant_gene/core/instability/apply_stat(obj/item/seeds/S)
+ S.instability = value
/datum/plant_gene/core/weed_rate
name = "Weed Growth Rate"
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index c7314bf180..2dc80cd8fc 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -24,6 +24,7 @@
var/yield = 3 // Amount of growns created per harvest. If is -1, the plant/shroom/weed is never meant to be harvested.
var/potency = 10 // The 'power' of a plant. Generally effects the amount of reagent in a plant, also used in other ways.
var/growthstages = 6 // Amount of growth sprites the plant has.
+ var/instability = 5 //Chance that a plant will mutate in each stage of it's life.
var/rarity = 0 // How rare the plant is. Used for giving points to cargo when shipping off to CentCom.
var/list/mutatelist = list() // The type of plants that this plant can mutate into.
var/list/genes = list() // Plant genes are stored here, see plant_genes.dm for more info.
@@ -36,6 +37,7 @@
var/weed_rate = 1 //If the chance below passes, then this many weeds sprout during growth
var/weed_chance = 5 //Percentage chance per tray update to grow weeds
+ var/seed_flags = MUTATE_EARLY //Determines if a plant is allowed to mutate early at 30+ instability
/obj/item/seeds/Initialize(mapload, nogenes = 0)
. = ..()
@@ -61,6 +63,7 @@
genes += new /datum/plant_gene/core/production(production)
if(potency != -1)
genes += new /datum/plant_gene/core/potency(potency)
+ genes += new /datum/plant_gene/core/instability(instability)
for(var/p in genes)
if(ispath(p))
@@ -74,6 +77,10 @@
/obj/item/seeds/examine(mob/user)
. = ..()
. += "Use a pen on it to rename it or change its description."
+ if(reagents_add && user.can_see_reagents())
+ . += "- Plant Reagents -"
+ for(var/datum/plant_gene/reagent/G in genes)
+ . += "- [G.get_name()] -"
/obj/item/seeds/proc/Copy()
var/obj/item/seeds/S = new type(null, 1)
@@ -85,6 +92,7 @@
S.yield = yield
S.potency = potency
S.weed_rate = weed_rate
+ S.instability = instability
S.weed_chance = weed_chance
S.name = name
S.plantname = plantname
@@ -121,18 +129,20 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
if(g)
g.mutability_flags &= ~mutability
-/obj/item/seeds/proc/mutate(lifemut = 2, endmut = 5, productmut = 1, yieldmut = 2, potmut = 25, wrmut = 2, wcmut = 5, traitmut = 0)
+/obj/item/seeds/proc/mutate(lifemut = 2, endmut = 5, productmut = 1, yieldmut = 2, potmut = 25, wrmut = 2, wcmut = 5, traitmut = 0, stabmut = 3)
adjust_lifespan(rand(-lifemut,lifemut))
adjust_endurance(rand(-endmut,endmut))
adjust_production(rand(-productmut,productmut))
adjust_yield(rand(-yieldmut,yieldmut))
adjust_potency(rand(-potmut,potmut))
+ adjust_instability(rand(-stabmut,stabmut))
adjust_weed_rate(rand(-wrmut, wrmut))
adjust_weed_chance(rand(-wcmut, wcmut))
if(prob(traitmut))
- add_random_traits(1, 1)
-
-
+ if(prob(50))
+ add_random_traits(1, 1)
+ else
+ add_random_reagents(1, 1)
/obj/item/seeds/bullet_act(obj/item/projectile/Proj) //Works with the Somatoray to modify plant variables.
if(istype(Proj, /obj/item/projectile/energy/florayield))
@@ -165,13 +175,40 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
/obj/item/seeds/proc/harvest(mob/user)
+ ///Reference to the tray/soil the seeds are planted in.
var/obj/machinery/hydroponics/parent = loc //for ease of access
+ ///Count used for creating the correct amount of results to the harvest.
var/t_amount = 0
+ ///List of plants all harvested from the same batch.
var/list/result = list()
+ ///Tile of the harvester to deposit the growables.
var/output_loc = parent.Adjacent(user) ? user.loc : parent.loc //needed for TK
+ ///Name of the grown products.
var/product_name
- while(t_amount < getYield())
- var/obj/item/reagent_containers/food/snacks/grown/t_prod = new product(output_loc, src)
+ ///The Number of products produced by the plant, typically the yield.
+ var/product_count = getYield()
+
+ while(t_amount < product_count)
+ var/obj/item/reagent_containers/food/snacks/grown/t_prod
+ if(instability >= 30 && (seed_flags & MUTATE_EARLY) && LAZYLEN(mutatelist) && prob(instability/3))
+ var/obj/item/seeds/new_prod = pick(mutatelist)
+ t_prod = initial(new_prod.product)
+ if(!t_prod)
+ continue
+ t_prod = new t_prod(output_loc, src)
+ t_prod.seed = new new_prod
+ t_prod.seed.name = initial(new_prod.name)
+ t_prod.seed.desc = initial(new_prod.desc)
+ t_prod.seed.plantname = initial(new_prod.plantname)
+ t_prod.transform = initial(t_prod.transform)
+ t_prod.transform *= TRANSFORM_USING_VARIABLE(t_prod.seed.potency, 100) + 0.5
+ t_amount++
+ if(t_prod.seed)
+ //t_prod.seed = new new_prod
+ t_prod.seed.instability = round(instability * 0.5)
+ continue
+ else
+ t_prod = new product(output_loc, src)
if(parent.myseed.plantname != initial(parent.myseed.plantname))
t_prod.name = lowertext(parent.myseed.plantname)
if(productdesc)
@@ -186,8 +223,8 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
product_name = parent.myseed.plantname
if(getYield() >= 1)
SSblackbox.record_feedback("tally", "food_harvested", getYield(), product_name)
- parent.investigate_log("[user] harvested [getYield()] of [src], with seed traits [english_list(genes)] and reagents_add [english_list(reagents_add)] and potency [potency].", INVESTIGATE_BOTANY)
parent.update_tray(user)
+
return result
/obj/item/seeds/proc/harvest_userless()
@@ -263,6 +300,14 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
if(C)
C.value = production
+/obj/item/seeds/proc/adjust_instability(adjustamt)
+ if(instability == -1)
+ return
+ instability = clamp(instability + adjustamt, 0, 100)
+ var/datum/plant_gene/core/C = get_gene(/datum/plant_gene/core/instability)
+ if(C)
+ C.value = instability
+
/obj/item/seeds/proc/adjust_potency(adjustamt)
if(potency != -1)
potency = clamp(potency + adjustamt, 0, 100)
@@ -320,6 +365,14 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
if(C)
C.value = potency
+/obj/item/seeds/proc/set_instability(adjustamt)
+ if(instability == -1)
+ return
+ instability = clamp(adjustamt, 0, 100)
+ var/datum/plant_gene/core/C = get_gene(/datum/plant_gene/core/instability)
+ if(C)
+ C.value = instability
+
/obj/item/seeds/proc/set_weed_rate(adjustamt)
weed_rate = clamp(adjustamt, 0, 10)
var/datum/plant_gene/core/C = get_gene(/datum/plant_gene/core/weed_rate)
@@ -352,6 +405,7 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
text += "- Production speed: [production]\n"
text += "- Endurance: [endurance]\n"
text += "- Lifespan: [lifespan]\n"
+ text += "- Instability: [instability]\n"
text += "- Weed Growth Rate: [weed_rate]\n"
text += "- Weed Vulnerability: [weed_chance]\n"
if(rarity)
@@ -362,9 +416,7 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
continue
all_traits += " [traits.get_name()]"
text += "- Plant Traits:[all_traits]\n"
-
text += "*---------*"
-
return text
/obj/item/seeds/proc/on_chem_reaction(datum/reagents/S) //in case seeds have some special interaction with special chems
@@ -373,9 +425,19 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
/obj/item/seeds/attackby(obj/item/O, mob/user, params)
if (istype(O, /obj/item/plant_analyzer))
to_chat(user, "*---------*\n This is \a [src].")
- var/text = get_analyzer_text()
- if(text)
- to_chat(user, "[text]")
+ var/text
+ var/obj/item/plant_analyzer/P_analyzer = O
+ if(P_analyzer.scan_mode == PLANT_SCANMODE_STATS)
+ text = get_analyzer_text()
+ if(text)
+ to_chat(user, "[text]")
+ if(reagents_add && P_analyzer.scan_mode == PLANT_SCANMODE_CHEMICALS)
+ to_chat(user, "- Plant Reagents -")
+ to_chat(user, "*---------*")
+ for(var/datum/plant_gene/reagent/G in genes)
+ to_chat(user, "- [G.get_name()] -")
+ to_chat(user, "*---------*")
+
return
diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm
index d842dbc003..a0d96658e6 100644
--- a/code/modules/instruments/songs/_song.dm
+++ b/code/modules/instruments/songs/_song.dm
@@ -2,6 +2,12 @@
#define MUSIC_MAXLINES 1000
#define MUSIC_MAXLINECHARS 300
+/**
+ * # Song datum
+ *
+ * These are the actual backend behind instruments.
+ * They attach to an atom and provide the editor + playback functionality.
+ */
/datum/song
/// Name of the song
var/name = "Untitled"
@@ -15,6 +21,9 @@
/// delay between notes in deciseconds
var/tempo = 5
+ /// How far we can be heard
+ var/instrument_range = 15
+
/// Are we currently playing?
var/playing = FALSE
@@ -53,17 +62,24 @@
/////////////////// Playing variables ////////////////
/**
- * Only used in synthesized playback - The chords we compiled. Non assoc list of lists:
- * list(list(key1, key2, key3..., tempo_divisor), list(key1, key2..., tempo_divisor), ...)
- * tempo_divisor always exists
- * if key1 (and so if there's no keys) doesn't exist it's a rest
+ * Build by compile_chords()
+ * Must be rebuilt on instrument switch.
* Compilation happens when we start playing and is cleared after we finish playing.
+ * Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
*/
var/list/compiled_chords
+ /// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
+ var/elapsed_delay
+ /// Amount of delay to wait before playing the next chord
+ var/delay_by
+ /// Current chord we're on.
+ var/current_chord
/// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
var/list/channels_playing = list()
/// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
var/list/channels_idle = list()
+ /// Person playing us
+ var/mob/user_playing
//////////////////////////////////////////////////////
/// Last world.time we checked for who can hear us
@@ -72,8 +88,6 @@
var/list/hearing_mobs
/// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
var/debug_mode = FALSE
- /// Last time we processed decay
- var/last_process_decay
/// Max sound channels to occupy
var/max_sound_channels = CHANNELS_PER_INSTRUMENT
/// Current channels, so we can save a length() call.
@@ -113,7 +127,7 @@
var/cached_exponential_dropoff = 1.045
/////////////////////////////////////////////////////////////////////////
-/datum/song/New(atom/parent, list/instrument_ids)
+/datum/song/New(atom/parent, list/instrument_ids, new_range)
SSinstruments.on_song_new(src)
lines = list()
tempo = sanitize_tempo(tempo)
@@ -125,6 +139,8 @@
hearing_mobs = list()
volume = clamp(volume, min_volume, max_volume)
update_sustain()
+ if(new_range)
+ instrument_range = new_range
/datum/song/Destroy()
stop_playing()
@@ -135,12 +151,15 @@
parent = null
return ..()
+/**
+ * Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
+ */
/datum/song/proc/do_hearcheck()
last_hearcheck = world.time
var/list/old = hearing_mobs.Copy()
hearing_mobs.len = 0
var/turf/source = get_turf(parent)
- for(var/mob/M in get_hearers_in_view(15, source))
+ for(var/mob/M in get_hearers_in_view(instrument_range, source))
if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS))
continue
hearing_mobs[M] = get_dist(M, source)
@@ -148,10 +167,15 @@
for(var/i in exited)
terminate_sound_mob(i)
-/// I can either be a datum, id, or path (if the instrument has no id).
+/**
+ * Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
+ */
/datum/song/proc/set_instrument(datum/instrument/I)
+ terminate_all_sounds()
+ var/old_legacy
if(using_instrument)
using_instrument.songs_using -= src
+ old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
using_instrument = null
cached_samples = null
cached_legacy_ext = null
@@ -162,7 +186,7 @@
if(istype(I))
using_instrument = I
I.songs_using += src
- var/instrument_legacy = CHECK_BITFIELD(I.instrument_flags, INSTRUMENT_LEGACY)
+ var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
if(instrument_legacy)
cached_legacy_ext = I.legacy_instrument_ext
cached_legacy_dir = I.legacy_instrument_path
@@ -170,23 +194,37 @@
else
cached_samples = I.samples
legacy = FALSE
+ if(isnull(old_legacy) || (old_legacy != instrument_legacy))
+ if(playing)
+ compile_chords()
-/// THIS IS A BLOCKING CALL.
+/**
+ * Attempts to start playing our song.
+ */
/datum/song/proc/start_playing(mob/user)
if(playing)
return
if(!using_instrument?.ready())
to_chat(user, "An error has occured with [src]. Please reset the instrument.")
return
+ compile_chords()
+ if(!length(compiled_chords))
+ to_chat(user, "Song is empty.")
+ return
playing = TRUE
- updateDialog()
+ updateDialog(user_playing)
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
//wrap the rest of the stuff to ensure stop_playing() is called.
- last_process_decay = world.time
+ do_hearcheck()
+ elapsed_delay = 0
+ delay_by = 0
+ current_chord = 1
+ user_playing = user
START_PROCESSING(SSinstruments, src)
- . = do_play_lines(user)
- stop_playing()
+/**
+ * Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
+ */
/datum/song/proc/stop_playing()
if(!playing)
return
@@ -196,42 +234,93 @@
STOP_PROCESSING(SSinstruments, src)
terminate_all_sounds(TRUE)
hearing_mobs.len = 0
- updateDialog()
+ user_playing = null
-/// THIS IS A BLOCKING CALL.
-/datum/song/proc/do_play_lines(user)
- if(!playing)
+/**
+ * Processes our song.
+ */
+/datum/song/proc/process_song(wait)
+ if(!length(compiled_chords) || should_stop_playing(user_playing))
+ stop_playing()
return
- do_hearcheck()
- if(legacy)
- do_play_lines_legacy(user)
- else
- do_play_lines_synthesized(user)
+ var/list/chord = compiled_chords[current_chord]
+ if(++elapsed_delay >= delay_by)
+ play_chord(chord)
+ elapsed_delay = 0
+ delay_by = tempodiv_to_delay(chord[length(chord)])
+ current_chord++
+ if(current_chord > length(compiled_chords))
+ if(repeat)
+ repeat--
+ current_chord = 1
+ return
+ else
+ stop_playing()
+ return
+/**
+ * Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
+ */
+/datum/song/proc/tempodiv_to_delay(tempodiv)
+ if(!tempodiv)
+ tempodiv = 1 // no division by 0. some song converters tend to use 0 for when it wants to have no div, for whatever reason.
+ return max(1, round((tempo/tempodiv) / world.tick_lag, 1))
+
+/**
+ * Compiles chords.
+ */
+/datum/song/proc/compile_chords()
+ legacy? compile_legacy() : compile_synthesized()
+
+/**
+ * Plays a chord.
+ */
+/datum/song/proc/play_chord(list/chord)
+ // last value is timing information
+ for(var/i in 1 to (length(chord) - 1))
+ legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
+
+/**
+ * Checks if we should halt playback.
+ */
/datum/song/proc/should_stop_playing(mob/user)
return QDELETED(parent) || !using_instrument || !playing
+/**
+ * Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
+ */
/datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo)
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
+/**
+ * Gets our beats per minute based on our tempo.
+ */
/datum/song/proc/get_bpm()
return 600 / tempo
+/**
+ * Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
+ */
/datum/song/proc/set_bpm(bpm)
tempo = sanitize_tempo(600 / bpm)
-/// Updates the window for our user. Override in subtypes.
-/datum/song/proc/updateDialog(mob/user = usr)
+/**
+ * Updates the window for our users. Override down the line.
+ */
+/datum/song/proc/updateDialog(mob/user)
ui_interact(user)
/datum/song/process(wait)
if(!playing)
return PROCESS_KILL
- var/delay = world.time - last_process_decay
- process_decay(delay)
- last_process_decay = world.time
+ // it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
+ process_song(world.tick_lag)
+ process_decay(world.tick_lag)
+/**
+ * Updates our cached linear/exponential falloff stuff, saving calculations down the line.
+ */
/datum/song/proc/update_sustain()
// Exponential is easy
cached_exponential_dropoff = sustain_exponential_dropoff
@@ -241,21 +330,33 @@
var/volume_decrease_per_decisecond = volume_diff / target_duration
cached_linear_dropoff = volume_decrease_per_decisecond
+/**
+ * Setter for setting output volume.
+ */
/datum/song/proc/set_volume(volume)
src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
update_sustain()
updateDialog()
+/**
+ * Setter for setting how low the volume has to get before a note is considered "dead" and dropped
+ */
/datum/song/proc/set_dropoff_volume(volume)
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
update_sustain()
updateDialog()
+/**
+ * Setter for setting exponential falloff factor.
+ */
/datum/song/proc/set_exponential_drop_rate(drop)
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
update_sustain()
updateDialog()
+/**
+ * Setter for setting linear falloff duration.
+ */
/datum/song/proc/set_linear_falloff_duration(duration)
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
update_sustain()
@@ -277,10 +378,8 @@
// subtype for handheld instruments, like violin
/datum/song/handheld
-/datum/song/handheld/updateDialog(mob/user = usr)
- if(user.machine != src)
- return
- parent.ui_interact(user)
+/datum/song/handheld/updateDialog(mob/user)
+ parent.ui_interact(user || usr)
/datum/song/handheld/should_stop_playing(mob/user)
. = ..()
@@ -292,10 +391,8 @@
// subtype for stationary structures, like pianos
/datum/song/stationary
-/datum/song/stationary/updateDialog(mob/user = usr)
- if(user.machine != src)
- return
- parent.ui_interact(user)
+/datum/song/stationary/updateDialog(mob/user)
+ parent.ui_interact(user || usr)
/datum/song/stationary/should_stop_playing(mob/user)
. = ..()
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/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm
index fa64656ebc..eee9be3cc7 100644
--- a/code/modules/instruments/songs/play_legacy.dm
+++ b/code/modules/instruments/songs/play_legacy.dm
@@ -1,48 +1,52 @@
-/// Playing legacy instruments - None of the "advanced" like sound reservations and decay are invoked.
-/datum/song/proc/do_play_lines_legacy(mob/user)
- while(repeat >= 0)
- var/cur_oct[7]
- var/cur_acc[7]
- for(var/i = 1 to 7)
- cur_oct[i] = 3
- cur_acc[i] = "n"
+/**
+ * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
+ */
+/datum/song/proc/compile_legacy()
+ if(!length(src.lines))
+ return
+ var/list/lines = src.lines //cache for hyepr speed!
+ compiled_chords = list()
+ var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
+ var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
+ for(var/line in lines)
+ var/list/chords = splittext(lowertext(line), ",")
+ for(var/chord in chords)
+ var/list/compiled_chord = list()
+ var/tempodiv = 1
+ var/list/notes_tempodiv = splittext(chord, "/")
+ var/len = length(notes_tempodiv)
+ if(len >= 2)
+ tempodiv = text2num(notes_tempodiv[2])
+ if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
+ var/list/notes = splittext(notes_tempodiv[1], "-")
+ for(var/note in notes)
+ if(length(note) == 0)
+ continue
+ // 1-7, A-G
+ var/key = text2ascii(note) - 96
+ if((key < 1) || (key > 7))
+ continue
+ for(var/i in 2 to length(note))
+ var/oct_acc = copytext(note, i, i + 1)
+ var/num = text2num(oct_acc)
+ if(!num) //it's an accidental
+ accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
+ else //octave
+ octaves[key] = clamp(num, octave_min, octave_max)
+ compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
+ compiled_chord += tempodiv //this goes last
+ if(length(compiled_chord))
+ compiled_chords[++compiled_chords.len] = compiled_chord
- for(var/line in lines)
- for(var/beat in splittext(lowertext(line), ","))
- if(should_stop_playing(user))
- return
- var/list/notes = splittext(beat, "/")
- if(length(notes)) //because some jack-butts are going to do ,,,, to symbolize 3 rests instead of something reasonable like ,/1.
- for(var/note in splittext(notes[1], "-"))
- if(length(note) == 0)
- continue
- var/cur_note = text2ascii(note) - 96
- if(cur_note < 1 || cur_note > 7)
- continue
- for(var/i=2 to length(note))
- var/ni = copytext(note,i,i+1)
- if(!text2num(ni))
- if(ni == "#" || ni == "b" || ni == "n")
- cur_acc[cur_note] = ni
- else if(ni == "s")
- cur_acc[cur_note] = "#" // so shift is never required
- else
- cur_oct[cur_note] = text2num(ni)
- playnote_legacy(cur_note, cur_acc[cur_note], cur_oct[cur_note])
- if(notes.len >= 2 && text2num(notes[2]))
- sleep(sanitize_tempo(tempo / text2num(notes[2])))
- else
- sleep(tempo)
- if(should_stop_playing(user))
- return
- repeat--
- updateDialog()
- repeat = 0
-
-// note is a number from 1-7 for A-G
-// acc is either "b", "n", or "#"
-// oct is 1-8 (or 9 for C)
-/datum/song/proc/playnote_legacy(note, acc as text, oct)
+/**
+ * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
+ *
+ * Arguments:
+ * * note is a number from 1-7 for A-G
+ * * acc is either "b", "n", or "#"
+ * * oct is 1-8 (or 9 for C)
+ */
+/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
// handle accidental -> B<>C of E<>F
if(acc == "b" && (note == 3 || note == 6)) // C or F
if(note == 3)
diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm
index 5e7c5652a0..4df54f5e6b 100644
--- a/code/modules/instruments/songs/play_synthesized.dm
+++ b/code/modules/instruments/songs/play_synthesized.dm
@@ -1,27 +1,7 @@
-/datum/song/proc/do_play_lines_synthesized(mob/user)
- compile_lines()
- while(repeat >= 0)
- if(should_stop_playing(user))
- return
- var/warned = FALSE
- for(var/_chord in compiled_chords)
- if(should_stop_playing(user))
- return
- var/list/chord = _chord
- var/tempodiv = chord[chord.len]
- for(var/i in 1 to chord.len - 1)
- var/key = chord[i]
- if(!playkey_synth(key))
- if(!warned)
- warned = TRUE
- to_chat(user, "Your instrument has ran out of channels. You might be playing your song too fast or be setting sustain to too high of a value. This warning will be suppressed for the rest of this cycle.")
- sleep(sanitize_tempo(tempo / (tempodiv || 1)))
- repeat--
- updateDialog()
- repeat = 0
-
-/// C-Db2-A-A4/2,A-B#4-C/3,/4,A,A-B-C as an example
-/datum/song/proc/compile_lines()
+/**
+ * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
+ */
+/datum/song/proc/compile_synthesized()
if(!length(src.lines))
return
var/list/lines = src.lines //cache for hyepr speed!
@@ -57,10 +37,12 @@
compiled_chord += tempodiv //this goes last
if(length(compiled_chord))
compiled_chords[++compiled_chords.len] = compiled_chord
- CHECK_TICK
- return compiled_chords
-/datum/song/proc/playkey_synth(key)
+/**
+ * Plays a specific numerical key from our instrument to anyone who can hear us.
+ * Does a hearing check if enough time has passed.
+ */
+/datum/song/proc/playkey_synth(key, mob/user)
if(can_noteshift)
key = clamp(key + note_shift, key_min, key_max)
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
@@ -83,6 +65,9 @@
M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
// Could do environment and echo later but not for now
+/**
+ * Stops all sounds we are "responsible" for. Only works in synthesized mode.
+ */
/datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
for(var/i in hearing_mobs)
terminate_sound_mob(i)
@@ -93,10 +78,16 @@
using_sound_channels = 0
SSsounds.free_datum_channels(src)
+/**
+ * Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
+ */
/datum/song/proc/terminate_sound_mob(mob/M)
for(var/channel in channels_playing)
M.stop_sound_channel(text2num(channel))
+/**
+ * Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
+ */
/datum/song/proc/pop_channel()
if(length(channels_idle)) //just pop one off of here if we have one available
. = text2num(channels_idle[1])
@@ -108,6 +99,12 @@
if(!isnull(.))
using_sound_channels++
+/**
+ * Decays our channels and updates their volumes to mobs who can hear us.
+ *
+ * Arguments:
+ * * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
+ */
/datum/song/proc/process_decay(wait_ds)
var/linear_dropoff = cached_linear_dropoff * wait_ds
var/exponential_dropoff = cached_exponential_dropoff ** wait_ds
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index e8981ed685..9abf5d86a0 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -261,13 +261,11 @@
set_pin_data(IC_OUTPUT, 12, H.pestlevel)
set_pin_data(IC_OUTPUT, 13, H.toxic)
set_pin_data(IC_OUTPUT, 14, H.waterlevel)
- set_pin_data(IC_OUTPUT, 15, H.nutrilevel)
+ set_pin_data(IC_OUTPUT, 15, H.reagents.total_volume)
set_pin_data(IC_OUTPUT, 16, H.harvest)
set_pin_data(IC_OUTPUT, 17, H.dead)
set_pin_data(IC_OUTPUT, 18, H.plant_health)
set_pin_data(IC_OUTPUT, 19, H.self_sustaining)
- set_pin_data(IC_OUTPUT, 20, H.using_irrigation)
- set_pin_data(IC_OUTPUT, 21, H.FindConnected())
push_data()
activate_pin(2)
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index 991c806f43..f69fa08aaf 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -19,30 +19,26 @@
// Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way.
// This is a dirty hack to make injecting reagents into them work.
// TODO: refactor that.
-/obj/item/integrated_circuit/reagent/proc/inject_tray(obj/machinery/hydroponics/tray, atom/movable/source, amount)
- var/atom/movable/acting_object = get_object()
- var/list/trays = list(tray)
- var/visi_msg = "[acting_object] transfers fluid into [tray]"
+//Time for someone to refactor this. Trays can now hold reagents.
+//obj/item/integrated_circuit/reagent/proc/inject_tray(obj/machinery/hydroponics/tray, atom/movable/source, amount)
+ //var/atom/movable/acting_object = get_object()
+ //var/list/trays = list(tray)
+ //var/visi_msg = "[acting_object] transfers fluid into [tray]"
- if(amount > 30 && source.reagents.total_volume >= 30 && tray.using_irrigation)
- trays = tray.FindConnected()
- if (trays.len > 1)
- visi_msg += ", setting off the irrigation system"
+ //acting_object.visible_message("[visi_msg].")
+ //playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
- acting_object.visible_message("[visi_msg].")
- playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
+ //var/split = round(amount/trays.len)
- var/split = round(amount/trays.len)
+ //for(var/obj/machinery/hydroponics/H in trays)
+ //var/datum/reagents/temp_reagents = new /datum/reagents()
+ //temp_reagents.my_atom = H
- for(var/obj/machinery/hydroponics/H in trays)
- var/datum/reagents/temp_reagents = new /datum/reagents()
- temp_reagents.my_atom = H
+ //source.reagents.trans_to(temp_reagents, split)
+ //H.on_hydroponics_apply(temp_reagents)
- source.reagents.trans_to(temp_reagents, split)
- H.applyChemicals(temp_reagents)
-
- temp_reagents.clear_reagents()
- qdel(temp_reagents)
+ //temp_reagents.clear_reagents()
+ //qdel(temp_reagents)
/obj/item/integrated_circuit/reagent/injector
name = "integrated hypo-injector"
@@ -116,10 +112,10 @@
return
if(!AM.reagents)
- if(istype(AM, /obj/machinery/hydroponics) && direction_mode == SYRINGE_INJECT && reagents.total_volume && transfer_amount)//injection into tray.
- inject_tray(AM, src, transfer_amount)
- activate_pin(2)
- return
+ //if(istype(AM, /obj/machinery/hydroponics) && direction_mode == SYRINGE_INJECT && reagents.total_volume && transfer_amount)//injection into tray.
+ //inject_tray(AM, src, transfer_amount)
+ //activate_pin(2)
+ //return
activate_pin(3)
return
@@ -242,13 +238,14 @@
if(!source.reagents)
return
- if(!target.reagents)
+ //if(!target.reagents)
// Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way.
// This is a dirty hack to make injecting reagents into them work.
- if(istype(target, /obj/machinery/hydroponics) && source.reagents.total_volume)
- inject_tray(target, source, transfer_amount)
- activate_pin(2)
- return
+ //Someone should redo this. Trays should hold reagents now.
+ //if(istype(target, /obj/machinery/hydroponics) && source.reagents.total_volume)
+ //inject_tray(target, source, transfer_amount)
+ //activate_pin(2)
+ //return
if(!source.is_drainable() || !target.is_refillable())
return
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/keybindings/keybind/robot.dm b/code/modules/keybindings/keybind/robot.dm
index b9f6ce83eb..4f249cf243 100644
--- a/code/modules/keybindings/keybind/robot.dm
+++ b/code/modules/keybindings/keybind/robot.dm
@@ -59,3 +59,17 @@
var/mob/living/silicon/robot/R = user.mob
R.uneq_active()
return TRUE
+
+/datum/keybinding/robot/drone/can_use(client/user)
+ return isdrone(user.mob)
+
+/datum/keybinding/robot/drone/quick_equip_drone // QOL: Drone quickequip
+ hotkey_keys = list("E")
+ name = "quick_equip_drone"
+ full_name = "Quick Equip (Drone)"
+ description = "Quickly puts an item in the best slot available"
+
+/datum/keybinding/robot/drone/quick_equip_drone/down(client/user)
+ var/mob/living/simple_animal/drone/D = user.mob
+ D.quick_equip()
+ return TRUE
\ No newline at end of file
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 c31008fa62..9c503279d4 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))
@@ -35,47 +44,44 @@
return destinations
-/obj/item/wormhole_jaunter/proc/activate(mob/user, adjacent)
+/obj/item/wormhole_jaunter/proc/activate(mob/user, adjacent, force_entry = FALSE)
if(!turf_check(user))
return
var/list/L = get_destinations(user)
if(!L.len)
- to_chat(user, "The [src.name] found no beacons in the world to anchor a wormhole to.")
+ to_chat(user, "The [name] found no beacons in the world to anchor a wormhole to.")
return
var/chosen_beacon = pick(L)
- var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), src, 100, null, FALSE, get_turf(chosen_beacon))
+ var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), 100, null, FALSE, get_turf(chosen_beacon))
if(adjacent)
try_move_adjacent(J)
+ if(force_entry)
+ J.teleport(user, force = TRUE)
playsound(src,'sound/effects/sparks4.ogg',50,1)
qdel(src)
-/obj/item/wormhole_jaunter/emp_act(power)
+/obj/item/wormhole_jaunter/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
var/mob/M = loc
if(istype(M))
- var/triggered = FALSE
if(M.get_item_by_slot(SLOT_BELT) == src)
- if(power == 1)
- triggered = TRUE
- else if(power == 2 && prob(50))
- triggered = TRUE
+ if(prob(severity))
+ M.visible_message("[src] overloads and activates!")
+ SSblackbox.record_feedback("tally", "jaunter", 1, "EMP") // EMP accidental activation
+ activate(M)
- if(triggered)
- M.visible_message("[src] overloads and activates!")
- 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)
- 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
@@ -84,9 +90,10 @@
icon_state = "bhole3"
desc = "A stable hole in the universe made by a wormhole jaunter. Turbulent doesn't even begin to describe how rough passage through one of these is, but at least it will always get you somewhere near a beacon."
mech_sized = TRUE //save your ripley
+ teleport_channel = TELEPORT_CHANNEL_WORMHOLE
innate_accuracy_penalty = 6
-/obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M)
+/obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M, force = FALSE)
. = ..()
if(.)
// KERPLUNK
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 e03de83e02..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)
@@ -868,7 +986,8 @@
if(1)
to_chat(user, "Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities.")
H.dna.features = list("mcolor" = "A02720", "tail_lizard" = "Dark Tiger", "tail_human" = "None", "snout" = "Sharp", "horns" = "Curled", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "Long", "body_markings" = "Dark Tiger Body", "legs" = "Digitigrade")
- H.eye_color = "fee5a3"
+ H.left_eye_color = "fee5a3"
+ H.right_eye_color = "fee5a3"
H.set_species(/datum/species/lizard)
if(2)
to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.")
@@ -986,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"
@@ -1075,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 abce0954a4..2e1ffe718f 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -17,7 +17,9 @@
facial_hair_style = random_facial_hair_style(gender)
hair_color = random_short_color()
facial_hair_color = hair_color
- eye_color = random_eye_color()
+ var/random_eye_color = random_eye_color()
+ left_eye_color = random_eye_color
+ right_eye_color = random_eye_color
if(!pref_species)
var/rando_race = pick(GLOB.roundstart_races)
pref_species = new rando_race()
@@ -42,7 +44,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(current_tab == 3)
//give it its loadout if not on the appearance tab
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm
index 899bf91081..4faef230df 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm
@@ -14,14 +14,14 @@
name = "Standard"
icon_state = "standard"
-/datum/sprite_accessory/xeno_dorsal/royal
- name = "Royal"
- icon_state = "royal"
-
/datum/sprite_accessory/xeno_dorsal/down
name = "Dorsal Down"
icon_state = "down"
+/datum/sprite_accessory/xeno_dorsal/royal
+ name = "Royal"
+ icon_state = "royal"
+
/******************************************
************* Xeno Tails ******************
*******************************************/
@@ -51,20 +51,20 @@
/datum/sprite_accessory/xeno_head/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return (!H.dna.features["xenohead"] || H.dna.features["xenohead"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return (!H.dna.features["xenohead"] || H.dna.features["xenohead"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
/datum/sprite_accessory/xeno_head/standard
name = "Standard"
icon_state = "standard"
-/datum/sprite_accessory/xeno_head/royal
- name = "royal"
- icon_state = "royal"
-
/datum/sprite_accessory/xeno_head/hollywood
name = "hollywood"
icon_state = "hollywood"
+/datum/sprite_accessory/xeno_head/royal
+ name = "royal"
+ icon_state = "royal"
+
/datum/sprite_accessory/xeno_head/warrior
name = "warrior"
icon_state = "warrior"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm b/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm
index a04838a360..5286acb33a 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm
@@ -16,6 +16,12 @@
icon_state = "dtiger"
gender_specific = 1
+/datum/sprite_accessory/body_markings/guilmon
+ name = "Guilmon"
+ icon_state = "guilmon"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
+
/datum/sprite_accessory/body_markings/ltiger
name = "Light Tiger Body"
icon_state = "ltiger"
@@ -49,11 +55,6 @@
icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
relevant_layers = null
-/datum/sprite_accessory/mam_body_markings/plain
- name = "Plain"
- icon_state = "plain"
- icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
-
/datum/sprite_accessory/mam_body_markings/redpanda
name = "Redpanda"
icon_state = "redpanda"
@@ -77,14 +78,14 @@
icon_state = "bellyslim"
icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
-/datum/sprite_accessory/mam_body_markings/corgi
- name = "Corgi"
- icon_state = "corgi"
-
/datum/sprite_accessory/mam_body_markings/cow
name = "Bovine"
icon_state = "bovine"
+/datum/sprite_accessory/mam_body_markings/corgi
+ name = "Corgi"
+ icon_state = "corgi"
+
/datum/sprite_accessory/mam_body_markings/corvid
name = "Corvid"
icon_state = "corvid"
@@ -139,15 +140,19 @@
name = "Hyena"
icon_state = "hyena"
-/datum/sprite_accessory/mam_body_markings/lab
- name = "Lab"
- icon_state = "lab"
-
/datum/sprite_accessory/mam_body_markings/insect
name = "Insect"
icon_state = "insect"
icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
+/datum/sprite_accessory/mam_body_markings/lab
+ name = "Lab"
+ icon_state = "lab"
+
+/datum/sprite_accessory/mam_body_markings/orca
+ name = "Orca"
+ icon_state = "orca"
+
/datum/sprite_accessory/mam_body_markings/otie
name = "Otie"
icon_state = "otie"
@@ -156,14 +161,15 @@
name = "Otter"
icon_state = "otter"
-/datum/sprite_accessory/mam_body_markings/orca
- name = "Orca"
- icon_state = "orca"
-
/datum/sprite_accessory/mam_body_markings/panther
name = "Panther"
icon_state = "panther"
+/datum/sprite_accessory/mam_body_markings/plain
+ name = "Plain"
+ icon_state = "plain"
+ icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
+
/datum/sprite_accessory/mam_body_markings/possum
name = "Possum"
icon_state = "possum"
@@ -172,6 +178,10 @@
name = "Raccoon"
icon_state = "raccoon"
+/datum/sprite_accessory/mam_body_markings/sergal
+ name = "Sergal"
+ icon_state = "sergal"
+
/datum/sprite_accessory/mam_body_markings/pede
name = "Scolipede"
icon_state = "scolipede"
@@ -181,18 +191,14 @@
name = "Shark"
icon_state = "shark"
-/datum/sprite_accessory/mam_body_markings/skunk
- name = "Skunk"
- icon_state = "skunk"
-
-/datum/sprite_accessory/mam_body_markings/sergal
- name = "Sergal"
- icon_state = "sergal"
-
/datum/sprite_accessory/mam_body_markings/shepherd
name = "Shepherd"
icon_state = "shepherd"
+/datum/sprite_accessory/mam_body_markings/skunk
+ name = "Skunk"
+ icon_state = "skunk"
+
/datum/sprite_accessory/mam_body_markings/tajaran
name = "Tajaran"
icon_state = "tajaran"
@@ -232,75 +238,10 @@
icon_state = "none"
relevant_layers = null
-/datum/sprite_accessory/insect_fluff/plain
- name = "Plain"
- icon_state = "plain"
-
-/datum/sprite_accessory/insect_fluff/reddish
- name = "Reddish"
- icon_state = "redish"
-
-/datum/sprite_accessory/insect_fluff/royal
- name = "Royal"
- icon_state = "royal"
-
-/datum/sprite_accessory/insect_fluff/gothic
- name = "Gothic"
- icon_state = "gothic"
-
-/datum/sprite_accessory/insect_fluff/lovers
- name = "Lovers"
- icon_state = "lovers"
-
-/datum/sprite_accessory/insect_fluff/whitefly
- name = "White Fly"
- icon_state = "whitefly"
-
/datum/sprite_accessory/insect_fluff/punished
name = "Burnt Off"
icon_state = "punished"
-/datum/sprite_accessory/insect_fluff/firewatch
- name = "Firewatch"
- icon_state = "firewatch"
-
-/datum/sprite_accessory/insect_fluff/deathhead
- name = "Deathshead"
- icon_state = "deathhead"
-
-/datum/sprite_accessory/insect_fluff/poison
- name = "Poison"
- icon_state = "poison"
-
-/datum/sprite_accessory/insect_fluff/ragged
- name = "Ragged"
- icon_state = "ragged"
-
-/datum/sprite_accessory/insect_fluff/moonfly
- name = "Moon Fly"
- icon_state = "moonfly"
-
-/datum/sprite_accessory/insect_fluff/snow
- name = "Snow"
- icon_state = "snow"
-
-/datum/sprite_accessory/insect_fluff/oakworm
- name = "Oak Worm"
- icon_state = "oakworm"
-
-/datum/sprite_accessory/insect_fluff/jungle
- name = "Jungle"
- icon_state = "jungle"
-
-/datum/sprite_accessory/insect_fluff/witchwing
- name = "Witch Wing"
- icon_state = "witchwing"
-
-/datum/sprite_accessory/insect_fluff/colored
- name = "Colored (Hair)"
- icon_state = "snow"
- color_src = HAIR
-
/datum/sprite_accessory/insect_fluff/colored1
name = "Colored (Primary)"
icon_state = "snow"
@@ -314,4 +255,69 @@
/datum/sprite_accessory/insect_fluff/colored3
name = "Colored (Tertiary)"
icon_state = "snow"
- color_src = MUTCOLORS3
\ No newline at end of file
+ color_src = MUTCOLORS3
+
+/datum/sprite_accessory/insect_fluff/colored
+ name = "Colored (Hair)"
+ icon_state = "snow"
+ color_src = HAIR
+
+/datum/sprite_accessory/insect_fluff/deathhead
+ name = "Deathshead"
+ icon_state = "deathhead"
+
+/datum/sprite_accessory/insect_fluff/firewatch
+ name = "Firewatch"
+ icon_state = "firewatch"
+
+/datum/sprite_accessory/insect_fluff/gothic
+ name = "Gothic"
+ icon_state = "gothic"
+
+/datum/sprite_accessory/insect_fluff/jungle
+ name = "Jungle"
+ icon_state = "jungle"
+
+/datum/sprite_accessory/insect_fluff/lovers
+ name = "Lovers"
+ icon_state = "lovers"
+
+/datum/sprite_accessory/insect_fluff/moonfly
+ name = "Moon Fly"
+ icon_state = "moonfly"
+
+/datum/sprite_accessory/insect_fluff/oakworm
+ name = "Oak Worm"
+ icon_state = "oakworm"
+
+/datum/sprite_accessory/insect_fluff/plain
+ name = "Plain"
+ icon_state = "plain"
+
+/datum/sprite_accessory/insect_fluff/poison
+ name = "Poison"
+ icon_state = "poison"
+
+/datum/sprite_accessory/insect_fluff/ragged
+ name = "Ragged"
+ icon_state = "ragged"
+
+/datum/sprite_accessory/insect_fluff/reddish
+ name = "Reddish"
+ icon_state = "redish"
+
+/datum/sprite_accessory/insect_fluff/royal
+ name = "Royal"
+ icon_state = "royal"
+
+/datum/sprite_accessory/insect_fluff/snow
+ name = "Snow"
+ icon_state = "snow"
+
+/datum/sprite_accessory/insect_fluff/whitefly
+ name = "White Fly"
+ icon_state = "whitefly"
+
+/datum/sprite_accessory/insect_fluff/witchwing
+ name = "Witch Wing"
+ icon_state = "witchwing"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm
index 1e6c797364..1d9138465f 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm
@@ -5,7 +5,7 @@
/datum/sprite_accessory/ears/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return (!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return (!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
/datum/sprite_accessory/ears/none
name = "None"
@@ -41,7 +41,7 @@
extra = TRUE
extra_color_src = NONE
-/datum/sprite_accessory/ears/human/bigwolfdark
+/datum/sprite_accessory/ears/human/bigwolfdark //ignore alphabetical sort here for ease-of-use
name = "Dark Big Wolf"
icon_state = "bigwolfdark"
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
@@ -55,6 +55,12 @@
extra = TRUE
extra_color_src = NONE
+/datum/sprite_accessory/ears/bunny
+ name = "Bunny"
+ icon_state = "bunny"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_ears.dmi'
+
/datum/sprite_accessory/ears/cat
name = "Cat"
icon_state = "cat"
@@ -74,6 +80,12 @@
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
color_src = MUTCOLORS3
+/datum/sprite_accessory/ears/lab
+ name = "Dog, Floppy"
+ icon_state = "lab"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_ears.dmi'
+
/datum/sprite_accessory/ears/human/eevee
name = "Eevee"
icon_state = "eevee"
@@ -115,12 +127,6 @@
icon_state = "jellyfish"
color_src = HAIR
-/datum/sprite_accessory/ears/lab
- name = "Dog, Floppy"
- icon_state = "lab"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_ears.dmi'
-
/datum/sprite_accessory/ears/murid
name = "Murid"
icon_state = "murid"
@@ -133,18 +139,18 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
-/datum/sprite_accessory/ears/human/pede
- name = "Scolipede"
- icon_state = "pede"
- icon = 'modular_citadel/icons/mob/mam_ears.dmi'
- color_src = MATRIXED
-
/datum/sprite_accessory/ears/human/rabbit
name = "Rabbit"
icon_state = "rabbit"
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
+/datum/sprite_accessory/ears/human/pede
+ name = "Scolipede"
+ icon_state = "pede"
+ icon = 'modular_citadel/icons/mob/mam_ears.dmi'
+ color_src = MATRIXED
+
/datum/sprite_accessory/ears/human/sergal
name = "Sergal"
icon_state = "sergal"
@@ -169,12 +175,6 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
-/datum/sprite_accessory/ears/bunny
- name = "Bunny"
- icon_state = "bunny"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_ears.dmi'
-
/******************************************
*************** Furry Ears ****************
*******************************************/
@@ -187,7 +187,7 @@
/datum/sprite_accessory/ears/mam_ears/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return (!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return (!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
/datum/sprite_accessory/ears/mam_ears/none
name = "None"
@@ -216,7 +216,7 @@
extra = TRUE
extra_color_src = NONE
-/datum/sprite_accessory/ears/mam_ears/bigwolfdark
+/datum/sprite_accessory/ears/mam_ears/bigwolfdark //alphabetical sort ignored here for ease-of-use
name = "Dark Big Wolf"
icon_state = "bigwolfdark"
@@ -226,6 +226,10 @@
extra = TRUE
extra_color_src = NONE
+/datum/sprite_accessory/ears/mam_ears/bunny
+ name = "Bunny"
+ icon_state = "bunny"
+
/datum/sprite_accessory/ears/mam_ears/cat
name = "Cat"
icon_state = "cat"
@@ -256,13 +260,11 @@
name = "Eevee"
icon_state = "eevee"
-
/datum/sprite_accessory/ears/mam_ears/elf
name = "Elf"
icon_state = "elf"
color_src = MUTCOLORS3
-
/datum/sprite_accessory/ears/mam_ears/elephant
name = "Elephant"
icon_state = "elephant"
@@ -283,15 +285,15 @@
name = "Husky"
icon_state = "wolf"
-/datum/sprite_accessory/ears/mam_ears/kangaroo
- name = "kangaroo"
- icon_state = "kangaroo"
-
/datum/sprite_accessory/ears/mam_ears/jellyfish
name = "Jellyfish"
icon_state = "jellyfish"
color_src = HAIR
+/datum/sprite_accessory/ears/mam_ears/kangaroo
+ name = "kangaroo"
+ icon_state = "kangaroo"
+
/datum/sprite_accessory/ears/mam_ears/lab
name = "Dog, Long"
icon_state = "lab"
@@ -304,18 +306,14 @@
name = "Otusian"
icon_state = "otie"
-/datum/sprite_accessory/ears/mam_ears/squirrel
- name = "Squirrel"
- icon_state = "squirrel"
+/datum/sprite_accessory/ears/mam_ears/rabbit
+ name = "Rabbit"
+ icon_state = "rabbit"
/datum/sprite_accessory/ears/mam_ears/pede
name = "Scolipede"
icon_state = "pede"
-/datum/sprite_accessory/ears/mam_ears/rabbit
- name = "Rabbit"
- icon_state = "rabbit"
-
/datum/sprite_accessory/ears/mam_ears/sergal
name = "Sergal"
icon_state = "sergal"
@@ -324,10 +322,10 @@
name = "skunk"
icon_state = "skunk"
+/datum/sprite_accessory/ears/mam_ears/squirrel
+ name = "Squirrel"
+ icon_state = "squirrel"
+
/datum/sprite_accessory/ears/mam_ears/wolf
name = "Wolf"
icon_state = "wolf"
-
-/datum/sprite_accessory/ears/mam_ears/bunny
- name = "Bunny"
- icon_state = "bunny"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm
index 18a76df116..49013161a9 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm
@@ -11,6 +11,10 @@
icon_state = "none"
relevant_layers = null
+/datum/sprite_accessory/frills/aquatic
+ name = "Aquatic"
+ icon_state = "aqua"
+
/datum/sprite_accessory/frills/simple
name = "Simple"
icon_state = "simple"
@@ -18,7 +22,3 @@
/datum/sprite_accessory/frills/short
name = "Short"
icon_state = "short"
-
-/datum/sprite_accessory/frills/aquatic
- name = "Aquatic"
- icon_state = "aqua"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm
index 34988f5656..a07fdaa5d3 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm
@@ -6,6 +6,10 @@
gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P)
// please make sure they're sorted alphabetically and categorized
+/datum/sprite_accessory/facial_hair/shaved //this is exempt from the alphabetical sort
+ name = "Shaved"
+ icon_state = null
+ gender = NEUTER
/datum/sprite_accessory/facial_hair/threeoclock
name = "Beard (3 o\'Clock)"
@@ -135,11 +139,6 @@
name = "Mutton Chops with Moustache"
icon_state = "facial_muttonmus"
-/datum/sprite_accessory/facial_hair/shaved
- name = "Shaved"
- icon_state = null
- gender = NEUTER
-
/datum/sprite_accessory/facial_hair/sideburn
name = "Sideburns"
icon_state = "facial_sideburns"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
index 138704067d..8e4e6ad617 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
@@ -9,6 +9,10 @@
// try to spell
// you do not need to define _s or _l sub-states, game automatically does this for you
+/datum/sprite_accessory/hair/bald //this is exempt from the alphabetical sort
+ name = "Bald"
+ icon_state = "bald"
+
/datum/sprite_accessory/hair/afro
name = "Afro"
icon_state = "hair_afro"
@@ -25,10 +29,6 @@
name = "Ahoge"
icon_state = "hair_antenna"
-/datum/sprite_accessory/hair/bald
- name = "Bald"
- icon_state = "bald"
-
/datum/sprite_accessory/hair/balding
name = "Balding Hair"
icon_state = "hair_e"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm
index 3a65eaebfa..accbc4ee36 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm
@@ -5,29 +5,17 @@
/datum/sprite_accessory/horns/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return (!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return (!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
/datum/sprite_accessory/horns/none
name = "None"
icon_state = "none"
relevant_layers = null
-/datum/sprite_accessory/horns/simple
- name = "Simple"
- icon_state = "simple"
-
-/datum/sprite_accessory/horns/short
- name = "Short"
- icon_state = "short"
-
/datum/sprite_accessory/horns/curled
name = "Curled"
icon_state = "curled"
-/datum/sprite_accessory/horns/ram
- name = "Ram"
- icon_state = "ram"
-
/datum/sprite_accessory/horns/angler
name = "Angeler"
icon_state = "angler"
@@ -40,3 +28,15 @@
/datum/sprite_accessory/horns/guilmon
name = "Guilmon"
icon_state = "guilmon"
+
+/datum/sprite_accessory/horns/ram
+ name = "Ram"
+ icon_state = "ram"
+
+/datum/sprite_accessory/horns/simple
+ name = "Simple"
+ icon_state = "simple"
+
+/datum/sprite_accessory/horns/short
+ name = "Short"
+ icon_state = "short"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm b/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm
index 110ac69201..6019245b80 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm
@@ -11,96 +11,90 @@
name = "Blank"
icon_state = "blank"
-/datum/sprite_accessory/screen/pink
- name = "Pink"
- icon_state = "pink"
-
-/datum/sprite_accessory/screen/green
- name = "Green"
- icon_state = "green"
-
-/datum/sprite_accessory/screen/red
- name = "Red"
- icon_state = "red"
-
/datum/sprite_accessory/screen/blue
name = "Blue"
icon_state = "blue"
-/datum/sprite_accessory/screen/yellow
- name = "Yellow"
- icon_state = "yellow"
-
-/datum/sprite_accessory/screen/shower
- name = "Shower"
- icon_state = "shower"
-
-/datum/sprite_accessory/screen/nature
- name = "Nature"
- icon_state = "nature"
-
-/datum/sprite_accessory/screen/eight
- name = "Eight"
- icon_state = "eight"
-
-/datum/sprite_accessory/screen/goggles
- name = "Goggles"
- icon_state = "goggles"
-
-/datum/sprite_accessory/screen/heart
- name = "Heart"
- icon_state = "heart"
-
-/datum/sprite_accessory/screen/monoeye
- name = "Mono eye"
- icon_state = "monoeye"
-
/datum/sprite_accessory/screen/breakout
name = "Breakout"
icon_state = "breakout"
-/datum/sprite_accessory/screen/purple
- name = "Purple"
- icon_state = "purple"
-
-/datum/sprite_accessory/screen/scroll
- name = "Scroll"
- icon_state = "scroll"
+/datum/sprite_accessory/screen/bsod
+ name = "BSOD"
+ icon_state = "bsod"
/datum/sprite_accessory/screen/console
name = "Console"
icon_state = "console"
-/datum/sprite_accessory/screen/rgb
- name = "RGB"
- icon_state = "rgb"
+/datum/sprite_accessory/screen/eight
+ name = "Eight"
+ icon_state = "eight"
+
+/datum/sprite_accessory/screen/eyes
+ name = "Eyes"
+ icon_state = "eyes"
+
+/datum/sprite_accessory/screen/ecgwave
+ name = "ECG wave"
+ icon_state = "ecgwave"
+
+/datum/sprite_accessory/screen/green
+ name = "Green"
+ icon_state = "green"
+
+/datum/sprite_accessory/screen/goggles
+ name = "Goggles"
+ icon_state = "goggles"
/datum/sprite_accessory/screen/golglider
name = "Gol Glider"
icon_state = "golglider"
+/datum/sprite_accessory/screen/heart
+ name = "Heart"
+ icon_state = "heart"
+
+/datum/sprite_accessory/screen/pink
+ name = "Pink"
+ icon_state = "pink"
+
+/datum/sprite_accessory/screen/red
+ name = "Red"
+ icon_state = "red"
+
+/datum/sprite_accessory/screen/monoeye
+ name = "Mono eye"
+ icon_state = "monoeye"
+
+/datum/sprite_accessory/screen/nature
+ name = "Nature"
+ icon_state = "nature"
+
+/datum/sprite_accessory/screen/purple
+ name = "Purple"
+ icon_state = "purple"
+
/datum/sprite_accessory/screen/rainbow
name = "Rainbow"
icon_state = "rainbow"
-/datum/sprite_accessory/screen/sunburst
- name = "Sunburst"
- icon_state = "sunburst"
-
-/datum/sprite_accessory/screen/static
- name = "Static"
- icon_state = "static"
-
-//Oracle Station sprites
-
-/datum/sprite_accessory/screen/bsod
- name = "BSOD"
- icon_state = "bsod"
-
/datum/sprite_accessory/screen/redtext
name = "Red Text"
icon_state = "retext"
+/datum/sprite_accessory/screen/rgb
+ name = "RGB"
+ icon_state = "rgb"
+
+/datum/sprite_accessory/screen/scroll
+ name = "Scroll"
+ icon_state = "scroll"
+
+/datum/sprite_accessory/screen/shower
+ name = "Shower"
+ icon_state = "shower"
+
/datum/sprite_accessory/screen/sinewave
name = "Sine wave"
icon_state = "sinewave"
@@ -109,22 +103,25 @@
name = "Square wave"
icon_state = "squarwave"
-/datum/sprite_accessory/screen/ecgwave
- name = "ECG wave"
- icon_state = "ecgwave"
+/datum/sprite_accessory/screen/stars
+ name = "Stars"
+ icon_state = "stars"
-/datum/sprite_accessory/screen/eyes
- name = "Eyes"
- icon_state = "eyes"
+/datum/sprite_accessory/screen/static
+ name = "Static"
+ icon_state = "static"
+
+/datum/sprite_accessory/screen/sunburst
+ name = "Sunburst"
+ icon_state = "sunburst"
/datum/sprite_accessory/screen/textdrop
name = "Text drop"
icon_state = "textdrop"
-/datum/sprite_accessory/screen/stars
- name = "Stars"
- icon_state = "stars"
-
+/datum/sprite_accessory/screen/yellow
+ name = "Yellow"
+ icon_state = "yellow"
/******************************************
************** IPC Antennas ***************
@@ -145,14 +142,6 @@
name = "Angled Antennae"
icon_state = "antennae"
-/datum/sprite_accessory/antenna/tvantennae
- name = "TV Antennae"
- icon_state = "tvantennae"
-
-/datum/sprite_accessory/antenna/cyberhead
- name = "Cyberhead"
- icon_state = "cyberhead"
-
/datum/sprite_accessory/antenna/antlers
name = "Antlers"
icon_state = "antlers"
@@ -160,3 +149,11 @@
/datum/sprite_accessory/antenna/crowned
name = "Crowned"
icon_state = "crowned"
+
+/datum/sprite_accessory/antenna/cyberhead
+ name = "Cyberhead"
+ icon_state = "cyberhead"
+
+/datum/sprite_accessory/antenna/tvantennae
+ name = "TV Antennae"
+ icon_state = "tvantennae"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm b/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm
index d41f5ace22..6f7b955d8f 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm
@@ -49,6 +49,13 @@
relevant_layers = null
hide_legs = FALSE
+/datum/sprite_accessory/taur/canine
+ name = "Canine"
+ icon_state = "canine"
+ taur_mode = STYLE_PAW_TAURIC
+ color_src = MUTCOLORS
+ extra = TRUE
+
/datum/sprite_accessory/taur/cow
name = "Cow"
icon_state = "cow"
@@ -95,6 +102,13 @@
color_src = MUTCOLORS
extra = TRUE
+/datum/sprite_accessory/taur/feline
+ name = "Feline"
+ icon_state = "feline"
+ taur_mode = STYLE_PAW_TAURIC
+ color_src = MUTCOLORS
+ extra = TRUE
+
/datum/sprite_accessory/taur/horse
name = "Horse"
icon_state = "horse"
@@ -126,17 +140,3 @@
taur_mode = STYLE_SNEK_TAURIC
color_src = MUTCOLORS
hide_legs = USE_SNEK_CLIP_MASK
-
-/datum/sprite_accessory/taur/canine
- name = "Canine"
- icon_state = "canine"
- taur_mode = STYLE_PAW_TAURIC
- color_src = MUTCOLORS
- extra = TRUE
-
-/datum/sprite_accessory/taur/feline
- name = "Feline"
- icon_state = "feline"
- taur_mode = STYLE_PAW_TAURIC
- color_src = MUTCOLORS
- extra = TRUE
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm
index f514ef54db..0cd7d301db 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm
@@ -5,16 +5,21 @@
/datum/sprite_accessory/snouts/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
-/datum/sprite_accessory/snouts/sharp
- name = "Sharp"
- icon_state = "sharp"
+/datum/sprite_accessory/snout/guilmon
+ name = "Guilmon"
+ icon_state = "guilmon"
+ color_src = MATRIXED
/datum/sprite_accessory/snouts/round
name = "Round"
icon_state = "round"
+/datum/sprite_accessory/snouts/sharp
+ name = "Sharp"
+ icon_state = "sharp"
+
/datum/sprite_accessory/snouts/sharplight
name = "Sharp + Light"
icon_state = "sharplight"
@@ -23,11 +28,6 @@
name = "Round + Light"
icon_state = "roundlight"
-/datum/sprite_accessory/snout/guilmon
- name = "Guilmon"
- icon_state = "guilmon"
- color_src = MATRIXED
-
//christ this was a mistake, but it's here just in case someone wants to selectively fix -- Pooj
/************* Lizard compatable snoots ***********
/datum/sprite_accessory/snouts/bird
@@ -163,7 +163,7 @@
/datum/sprite_accessory/snouts/mam_snouts/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
- return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC)
+ return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || (HD.status == BODYPART_ROBOTIC && !HD.render_like_organic))
/datum/sprite_accessory/snouts/mam_snouts/none
name = "None"
@@ -192,11 +192,19 @@
extra = TRUE
extra_color_src = MUTCOLORS3
-/datum/sprite_accessory/snouts/mam_snouts/skulldog
- name = "Skulldog"
- icon_state = "skulldog"
+/datum/sprite_accessory/snouts/mam_snouts/husky
+ name = "Husky"
+ icon_state = "husky"
+
+/datum/sprite_accessory/snouts/mam_snouts/rhino
+ name = "Horn"
+ icon_state = "rhino"
extra = TRUE
- extra_color_src = MATRIXED
+ extra = MUTCOLORS3
+
+/datum/sprite_accessory/snouts/mam_snouts/rodent
+ name = "Rodent"
+ icon_state = "rodent"
/datum/sprite_accessory/snouts/mam_snouts/lcanid
name = "Mammal, Long"
@@ -226,32 +234,20 @@
name = "Mammal, Thick ALT"
icon_state = "wolfalt"
-/datum/sprite_accessory/snouts/mam_snouts/redpanda
- name = "WahCoon"
- icon_state = "wah"
-
-/datum/sprite_accessory/snouts/mam_snouts/redpandaalt
- name = "WahCoon ALT"
- icon_state = "wahalt"
-
-/datum/sprite_accessory/snouts/mam_snouts/rhino
- name = "Horn"
- icon_state = "rhino"
- extra = TRUE
- extra = MUTCOLORS3
-
-/datum/sprite_accessory/snouts/mam_snouts/rodent
- name = "Rodent"
- icon_state = "rodent"
-
-/datum/sprite_accessory/snouts/mam_snouts/husky
- name = "Husky"
- icon_state = "husky"
-
/datum/sprite_accessory/snouts/mam_snouts/otie
name = "Otie"
icon_state = "otie"
+/datum/sprite_accessory/snouts/mam_snouts/round
+ name = "Round"
+ icon_state = "round"
+ color_src = MUTCOLORS
+
+/datum/sprite_accessory/snouts/mam_snouts/roundlight
+ name = "Round + Light"
+ icon_state = "roundlight"
+ color_src = MUTCOLORS
+
/datum/sprite_accessory/snouts/mam_snouts/pede
name = "Scolipede"
icon_state = "pede"
@@ -268,30 +264,33 @@
name = "hShark"
icon_state = "hshark"
-/datum/sprite_accessory/snouts/mam_snouts/toucan
- name = "Toucan"
- icon_state = "toucan"
-
/datum/sprite_accessory/snouts/mam_snouts/sharp
name = "Sharp"
icon_state = "sharp"
color_src = MUTCOLORS
-/datum/sprite_accessory/snouts/mam_snouts/round
- name = "Round"
- icon_state = "round"
- color_src = MUTCOLORS
-
/datum/sprite_accessory/snouts/mam_snouts/sharplight
name = "Sharp + Light"
icon_state = "sharplight"
color_src = MUTCOLORS
-/datum/sprite_accessory/snouts/mam_snouts/roundlight
- name = "Round + Light"
- icon_state = "roundlight"
- color_src = MUTCOLORS
+/datum/sprite_accessory/snouts/mam_snouts/skulldog
+ name = "Skulldog"
+ icon_state = "skulldog"
+ extra = TRUE
+ extra_color_src = MATRIXED
+/datum/sprite_accessory/snouts/mam_snouts/toucan
+ name = "Toucan"
+ icon_state = "toucan"
+
+/datum/sprite_accessory/snouts/mam_snouts/redpanda
+ name = "WahCoon"
+ icon_state = "wah"
+
+/datum/sprite_accessory/snouts/mam_snouts/redpandaalt
+ name = "WahCoon ALT"
+ icon_state = "wahalt"
/******************************************
**************** Snouts *******************
@@ -318,6 +317,16 @@
extra = TRUE
extra_color_src = MUTCOLORS3
+/datum/sprite_accessory/snouts/mam_snouts/frhino
+ name = "Horn (Top)"
+ icon_state = "frhino"
+ extra = TRUE
+ extra = MUTCOLORS3
+
+/datum/sprite_accessory/snouts/mam_snouts/fhusky
+ name = "Husky (Top)"
+ icon_state = "fhusky"
+
/datum/sprite_accessory/snouts/mam_snouts/flcanid
name = "Mammal, Long (Top)"
icon_state = "flcanid"
@@ -346,27 +355,23 @@
name = "Mammal, Thick ALT (Top)"
icon_state = "fwolfalt"
-/datum/sprite_accessory/snouts/mam_snouts/fredpanda
- name = "WahCoon (Top)"
- icon_state = "fwah"
-
-/datum/sprite_accessory/snouts/mam_snouts/frhino
- name = "Horn (Top)"
- icon_state = "frhino"
- extra = TRUE
- extra = MUTCOLORS3
+/datum/sprite_accessory/snouts/mam_snouts/fotie
+ name = "Otie (Top)"
+ icon_state = "fotie"
/datum/sprite_accessory/snouts/mam_snouts/frodent
name = "Rodent (Top)"
icon_state = "frodent"
-/datum/sprite_accessory/snouts/mam_snouts/fhusky
- name = "Husky (Top)"
- icon_state = "fhusky"
+/datum/sprite_accessory/snouts/mam_snouts/fround
+ name = "Round (Top)"
+ icon_state = "fround"
+ color_src = MUTCOLORS
-/datum/sprite_accessory/snouts/mam_snouts/fotie
- name = "Otie (Top)"
- icon_state = "fotie"
+/datum/sprite_accessory/snouts/mam_snouts/froundlight
+ name = "Round + Light (Top)"
+ icon_state = "froundlight"
+ color_src = MUTCOLORS
/datum/sprite_accessory/snouts/mam_snouts/fpede
name = "Scolipede (Top)"
@@ -380,26 +385,20 @@
name = "Shark (Top)"
icon_state = "fshark"
-/datum/sprite_accessory/snouts/mam_snouts/ftoucan
- name = "Toucan (Top)"
- icon_state = "ftoucan"
-
/datum/sprite_accessory/snouts/mam_snouts/fsharp
name = "Sharp (Top)"
icon_state = "fsharp"
color_src = MUTCOLORS
-/datum/sprite_accessory/snouts/mam_snouts/fround
- name = "Round (Top)"
- icon_state = "fround"
- color_src = MUTCOLORS
-
/datum/sprite_accessory/snouts/mam_snouts/fsharplight
name = "Sharp + Light (Top)"
icon_state = "fsharplight"
color_src = MUTCOLORS
-/datum/sprite_accessory/snouts/mam_snouts/froundlight
- name = "Round + Light (Top)"
- icon_state = "froundlight"
- color_src = MUTCOLORS
+/datum/sprite_accessory/snouts/mam_snouts/ftoucan
+ name = "Toucan (Top)"
+ icon_state = "ftoucan"
+
+/datum/sprite_accessory/snouts/mam_snouts/fredpanda
+ name = "WahCoon (Top)"
+ icon_state = "fwah"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm
index 19ec677a72..ffb808eede 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm
@@ -20,6 +20,10 @@
name = "Knee-high - Bee"
icon_state = "bee_knee"
+/datum/sprite_accessory/underwear/socks/christmas_knee
+ name = "Knee-High - Christmas"
+ icon_state = "christmas_knee"
+
/datum/sprite_accessory/underwear/socks/commie_knee
name = "Knee-High - Commie"
icon_state = "commie_knee"
@@ -32,6 +36,14 @@
name = "Knee-high - Rainbow"
icon_state = "rainbow_knee"
+/datum/sprite_accessory/underwear/socks/candycaner_knee
+ name = "Knee-High - Red Candy Cane"
+ icon_state = "candycaner_knee"
+
+/datum/sprite_accessory/underwear/socks/candycaneg_knee //ignore alphabetisation for ease of use in scenarios like this
+ name = "Knee-High - Green Candy Cane"
+ icon_state = "candycaneg_knee"
+
/datum/sprite_accessory/underwear/socks/striped_knee
name = "Knee-high - Striped"
icon_state = "striped_knee"
@@ -46,18 +58,6 @@
name = "Knee-High - UK"
icon_state = "uk_knee"
-/datum/sprite_accessory/underwear/socks/christmas_knee
- name = "Knee-High - Christmas"
- icon_state = "christmas_knee"
-
-/datum/sprite_accessory/underwear/socks/candycaner_knee
- name = "Knee-High - Red Candy Cane"
- icon_state = "candycaner_knee"
-
-/datum/sprite_accessory/underwear/socks/candycaneg_knee
- name = "Knee-High - Green Candy Cane"
- icon_state = "candycaneg_knee"
-
/datum/sprite_accessory/underwear/socks/socks_norm
name = "Normal"
icon_state = "socks_norm"
@@ -129,22 +129,34 @@
name = "Thigh-high - Bee"
icon_state = "bee_thigh"
+/datum/sprite_accessory/underwear/socks/christmas_thigh
+ name = "Thigh-high - Christmas"
+ icon_state = "christmas_thigh"
+
/datum/sprite_accessory/underwear/socks/commie_thigh
name = "Thigh-high - Commie"
icon_state = "commie_thigh"
-/datum/sprite_accessory/underwear/socks/usa_thigh
- name = "Thigh-high - Freedom"
- icon_state = "assblastusa_thigh"
-
/datum/sprite_accessory/underwear/socks/fishnet
name = "Thigh-high - Fishnet"
icon_state = "fishnet"
+/datum/sprite_accessory/underwear/socks/usa_thigh
+ name = "Thigh-high - Freedom"
+ icon_state = "assblastusa_thigh"
+
/datum/sprite_accessory/underwear/socks/rainbow_thigh
name = "Thigh-high - Rainbow"
icon_state = "rainbow_thigh"
+/datum/sprite_accessory/underwear/socks/candycaner_thigh
+ name = "Thigh-high - Red Candy Cane"
+ icon_state = "candycaner_thigh"
+
+/datum/sprite_accessory/underwear/socks/candycaneg_thigh
+ name = "Thigh-high - Green Candy Cane"
+ icon_state = "candycaneg_thigh"
+
/datum/sprite_accessory/underwear/socks/striped_thigh
name = "Thigh-high - Striped"
icon_state = "striped_thigh"
@@ -157,16 +169,4 @@
/datum/sprite_accessory/underwear/socks/uk_thigh
name = "Thigh-high - UK"
- icon_state = "uk_thigh"
-
-/datum/sprite_accessory/underwear/socks/christmas_thigh
- name = "Thigh-high - Christmas"
- icon_state = "christmas_thigh"
-
-/datum/sprite_accessory/underwear/socks/candycaner_thigh
- name = "Thigh-high - Red Candy Cane"
- icon_state = "candycaner_thigh"
-
-/datum/sprite_accessory/underwear/socks/candycaneg_thigh
- name = "Thigh-high - Green Candy Cane"
- icon_state = "candycaneg_thigh"
\ No newline at end of file
+ icon_state = "uk_thigh"
\ No newline at end of file
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/spines.dm b/code/modules/mob/dead/new_player/sprite_accessories/spines.dm
index 83415ed375..5d7207c934 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/spines.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/spines.dm
@@ -21,21 +21,13 @@
name = "None"
icon_state = "none"
-/datum/sprite_accessory/spines/short
- name = "Short"
- icon_state = "short"
+/datum/sprite_accessory/spines/aqautic
+ name = "Aquatic"
+ icon_state = "aqua"
-/datum/sprite_accessory/spines_animated/short
- name = "Short"
- icon_state = "short"
-
-/datum/sprite_accessory/spines/shortmeme
- name = "Short + Membrane"
- icon_state = "shortmeme"
-
-/datum/sprite_accessory/spines_animated/shortmeme
- name = "Short + Membrane"
- icon_state = "shortmeme"
+/datum/sprite_accessory/spines_animated/aqautic
+ name = "Aquatic"
+ icon_state = "aqua"
/datum/sprite_accessory/spines/long
name = "Long"
@@ -53,10 +45,18 @@
name = "Long + Membrane"
icon_state = "longmeme"
-/datum/sprite_accessory/spines/aqautic
- name = "Aquatic"
- icon_state = "aqua"
+/datum/sprite_accessory/spines/short
+ name = "Short"
+ icon_state = "short"
-/datum/sprite_accessory/spines_animated/aqautic
- name = "Aquatic"
- icon_state = "aqua"
+/datum/sprite_accessory/spines_animated/short
+ name = "Short"
+ icon_state = "short"
+
+/datum/sprite_accessory/spines/shortmeme
+ name = "Short + Membrane"
+ icon_state = "shortmeme"
+
+/datum/sprite_accessory/spines_animated/shortmeme
+ name = "Short + Membrane"
+ icon_state = "shortmeme"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm
index 16531d8f06..9addd15dca 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm
@@ -25,12 +25,6 @@
icon_state = "synthliz_tertunder"
//Synth body markings
-/datum/sprite_accessory/mam_body_markings/synthliz
- recommended_species = list("synthliz")
- icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
- name = "Synthetic Lizard - Plates"
- icon_state = "synthlizscutes"
-
/datum/sprite_accessory/mam_body_markings/synthliz/synthliz_pecs
icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
name = "Synthetic Lizard - Pecs"
@@ -41,6 +35,12 @@
name = "Synthetic Lizard - Pecs Light"
icon_state = "synthlizpecslight"
+/datum/sprite_accessory/mam_body_markings/synthliz
+ recommended_species = list("synthliz")
+ icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
+ name = "Synthetic Lizard - Plates"
+ icon_state = "synthlizscutes"
+
//Synth tails
/datum/sprite_accessory/tails/mam_tails/synthliz
recommended_species = list("synthliz")
@@ -70,17 +70,17 @@
name = "Synthetic Lizard - Curled"
icon_state = "synth_curled"
-/datum/sprite_accessory/antenna/synthliz/synthliz_thick
+/datum/sprite_accessory/antenna/synthliz/synth_horns
icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi'
color_src = MUTCOLORS
- name = "Synthetic Lizard - Thick"
- icon_state = "synth_thick"
+ name = "Synthetic Lizard - Horns"
+ icon_state = "synth_horns"
-/datum/sprite_accessory/antenna/synthliz/synth_thicklight
+/datum/sprite_accessory/antenna/synthliz/synth_hornslight
icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi'
color_src = MATRIXED
- name = "Synthetic Lizard - Thick Light"
- icon_state = "synth_thicklight"
+ name = "Synthetic Lizard - Horns Light"
+ icon_state = "synth_hornslight"
/datum/sprite_accessory/antenna/synthliz/synth_short
icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi'
@@ -100,17 +100,17 @@
name = "Synthetic Lizard - Sharp Light"
icon_state = "synth_sharplight"
-/datum/sprite_accessory/antenna/synthliz/synth_horns
+/datum/sprite_accessory/antenna/synthliz/synthliz_thick
icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi'
color_src = MUTCOLORS
- name = "Synthetic Lizard - Horns"
- icon_state = "synth_horns"
+ name = "Synthetic Lizard - Thick"
+ icon_state = "synth_thick"
-/datum/sprite_accessory/antenna/synthliz/synth_hornslight
+/datum/sprite_accessory/antenna/synthliz/synth_thicklight
icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi'
color_src = MATRIXED
- name = "Synthetic Lizard - Horns Light"
- icon_state = "synth_hornslight"
+ name = "Synthetic Lizard - Thick Light"
+ icon_state = "synth_thicklight"
//Synth Taurs (Ported from Virgo)
/datum/sprite_accessory/taur/synthliz
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm
index 812e0c052c..d9e2de1525 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm
@@ -18,38 +18,7 @@
/datum/sprite_accessory/tails_animated/lizard/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
return (((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric) || H.dna.species.mutant_bodyparts["tail_lizard"])
-/datum/sprite_accessory/tails/lizard/smooth
- name = "Smooth"
- icon_state = "smooth"
-
-/datum/sprite_accessory/tails_animated/lizard/smooth
- name = "Smooth"
- icon_state = "smooth"
-
-/datum/sprite_accessory/tails/lizard/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
-
-/datum/sprite_accessory/tails_animated/lizard/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
-
-/datum/sprite_accessory/tails/lizard/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
-
-/datum/sprite_accessory/tails_animated/lizard/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
-
-/datum/sprite_accessory/tails/lizard/spikes
- name = "Spikes"
- icon_state = "spikes"
-
-/datum/sprite_accessory/tails_animated/lizard/spikes
- name = "Spikes"
- icon_state = "spikes"
-
+//this goes first regardless of alphabetical order
/datum/sprite_accessory/tails/lizard/none
name = "None"
icon_state = "None"
@@ -72,11 +41,13 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/datum/sprite_accessory/body_markings/guilmon
- name = "Guilmon"
- icon_state = "guilmon"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/markings_notmammals.dmi'
+/datum/sprite_accessory/tails/lizard/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
+
+/datum/sprite_accessory/tails_animated/lizard/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
/datum/sprite_accessory/tails/lizard/guilmon
name = "Guilmon"
@@ -90,6 +61,30 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+/datum/sprite_accessory/tails/lizard/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+
+/datum/sprite_accessory/tails_animated/lizard/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+
+/datum/sprite_accessory/tails/lizard/smooth
+ name = "Smooth"
+ icon_state = "smooth"
+
+/datum/sprite_accessory/tails_animated/lizard/smooth
+ name = "Smooth"
+ icon_state = "smooth"
+
+/datum/sprite_accessory/tails/lizard/spikes
+ name = "Spikes"
+ icon_state = "spikes"
+
+/datum/sprite_accessory/tails_animated/lizard/spikes
+ name = "Spikes"
+ icon_state = "spikes"
+
/******************************************
************** Human Tails ****************
*******************************************/
@@ -107,18 +102,6 @@
/datum/sprite_accessory/tails_animated/human/is_not_visible(var/mob/living/carbon/human/H, var/tauric)
return (((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)|| H.dna.species.mutant_bodyparts["tail_human"])
-/datum/sprite_accessory/tails/human/ailurus
- name = "Red Panda"
- icon_state = "wah"
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
- color_src = MATRIXED
-
-/datum/sprite_accessory/tails_animated/human/ailurus
- name = "Red Panda"
- icon_state = "wah"
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
- color_src = MATRIXED
-
/datum/sprite_accessory/tails/human/axolotl
name = "Axolotl"
icon_state = "axolotl"
@@ -199,6 +182,14 @@
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
color_src = MATRIXED
+/datum/sprite_accessory/tails/human/corvid
+ name = "Corvid"
+ icon_state = "crow"
+
+/datum/sprite_accessory/tails_animated/human/corvid
+ name = "Corvid"
+ icon_state = "crow"
+
/datum/sprite_accessory/tails/human/cow
name = "Cow"
icon_state = "cow"
@@ -211,13 +202,25 @@
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
color_src = MATRIXED
-/datum/sprite_accessory/tails/human/corvid
- name = "Corvid"
- icon_state = "crow"
+/datum/sprite_accessory/tails/human/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
-/datum/sprite_accessory/tails_animated/human/corvid
- name = "Corvid"
- icon_state = "crow"
+/datum/sprite_accessory/tails_animated/human/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
+
+/datum/sprite_accessory/tails/human/datashark
+ name = "datashark"
+ icon_state = "datashark"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+
+/datum/sprite_accessory/tails_animated/human/datashark
+ name = "datashark"
+ icon_state = "datashark"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
/datum/sprite_accessory/tails/human/eevee
name = "Eevee"
@@ -298,7 +301,7 @@
color_src = MATRIXED
/datum/sprite_accessory/tails_animated/human/insect
- name = "insect"
+ name = "Insect"
icon_state = "insect"
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
color_src = MATRIXED
@@ -315,6 +318,14 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+/datum/sprite_accessory/tails/human/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+
+/datum/sprite_accessory/tails_animated/human/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+
/datum/sprite_accessory/tails/human/murid
name = "Murid"
icon_state = "murid"
@@ -327,18 +338,6 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/datum/sprite_accessory/tails/human/otie
- name = "Otusian"
- icon_state = "otie"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
-/datum/sprite_accessory/tails_animated/human/otie
- name = "Otusian"
- icon_state = "otie"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
/datum/sprite_accessory/tails/orca
name = "Orca"
icon_state = "orca"
@@ -351,15 +350,15 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/datum/sprite_accessory/tails/human/pede
- name = "Scolipede"
- icon_state = "pede"
+/datum/sprite_accessory/tails/human/otie
+ name = "Otusian"
+ icon_state = "otie"
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/datum/sprite_accessory/tails_animated/human/pede
- name = "Scolipede"
- icon_state = "pede"
+/datum/sprite_accessory/tails_animated/human/otie
+ name = "Otusian"
+ icon_state = "otie"
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
@@ -375,6 +374,30 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+/datum/sprite_accessory/tails/human/ailurus
+ name = "Red Panda"
+ icon_state = "wah"
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+ color_src = MATRIXED
+
+/datum/sprite_accessory/tails_animated/human/ailurus
+ name = "Red Panda"
+ icon_state = "wah"
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+ color_src = MATRIXED
+
+/datum/sprite_accessory/tails/human/pede
+ name = "Scolipede"
+ icon_state = "pede"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+
+/datum/sprite_accessory/tails_animated/human/pede
+ name = "Scolipede"
+ icon_state = "pede"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+
/datum/sprite_accessory/tails/human/sergal
name = "Sergal"
icon_state = "sergal"
@@ -387,6 +410,18 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+/datum/sprite_accessory/tails/human/shark
+ name = "Shark"
+ icon_state = "shark"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+
+/datum/sprite_accessory/tails_animated/human/shark
+ name = "Shark"
+ icon_state = "shark"
+ color_src = MATRIXED
+ icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+
/datum/sprite_accessory/tails/human/skunk
name = "skunk"
icon_state = "skunk"
@@ -415,30 +450,6 @@
name = "Spikes"
icon_state = "spikes"
-/datum/sprite_accessory/tails/human/shark
- name = "Shark"
- icon_state = "shark"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
-/datum/sprite_accessory/tails_animated/human/shark
- name = "Shark"
- icon_state = "shark"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
-/datum/sprite_accessory/tails/human/datashark
- name = "datashark"
- icon_state = "datashark"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
-/datum/sprite_accessory/tails_animated/human/datashark
- name = "datashark"
- icon_state = "datashark"
- color_src = MATRIXED
- icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-
/datum/sprite_accessory/tails/human/straighttail
name = "Straight Tail"
icon_state = "straighttail"
@@ -495,22 +506,6 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/datum/sprite_accessory/tails/human/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
-
-/datum/sprite_accessory/tails_animated/human/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
-
-/datum/sprite_accessory/tails/human/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
-
-/datum/sprite_accessory/tails_animated/human/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
-
/datum/sprite_accessory/tails/human/wolf
name = "Wolf"
icon_state = "wolf"
@@ -554,16 +549,6 @@
icon_state = "none"
relevant_layers = null
-/datum/sprite_accessory/tails/mam_tails/ailurus
- name = "Red Panda"
- icon_state = "wah"
- extra = TRUE
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/ailurus
- name = "Red Panda"
- icon_state = "wah"
- extra = TRUE
-
/datum/sprite_accessory/tails/mam_tails/axolotl
name = "Axolotl"
icon_state = "axolotl"
@@ -638,6 +623,18 @@
name = "Cow"
icon_state = "cow"
+/datum/sprite_accessory/tails/mam_tails/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
+ color_src = MUTCOLORS
+ icon = 'icons/mob/mutant_bodyparts.dmi'
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/dtiger
+ name = "Dark Tiger"
+ icon_state = "dtiger"
+ color_src = MUTCOLORS
+ icon = 'icons/mob/mutant_bodyparts.dmi'
+
/datum/sprite_accessory/tails/mam_tails/eevee
name = "Eevee"
icon_state = "eevee"
@@ -728,6 +725,18 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Lab"
icon_state = "lab"
+/datum/sprite_accessory/tails/mam_tails/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+ color_src = MUTCOLORS
+ icon = 'icons/mob/mutant_bodyparts.dmi'
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/ltiger
+ name = "Light Tiger"
+ icon_state = "ltiger"
+ color_src = MUTCOLORS
+ icon = 'icons/mob/mutant_bodyparts.dmi'
+
/datum/sprite_accessory/tails/mam_tails/murid
name = "Murid"
icon_state = "murid"
@@ -736,14 +745,6 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Murid"
icon_state = "murid"
-/datum/sprite_accessory/tails/mam_tails/otie
- name = "Otusian"
- icon_state = "otie"
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/otie
- name = "Otusian"
- icon_state = "otie"
-
/datum/sprite_accessory/tails/mam_tails/orca
name = "Orca"
icon_state = "orca"
@@ -752,13 +753,13 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Orca"
icon_state = "orca"
-/datum/sprite_accessory/tails/mam_tails/pede
- name = "Scolipede"
- icon_state = "pede"
+/datum/sprite_accessory/tails/mam_tails/otie
+ name = "Otusian"
+ icon_state = "otie"
-/datum/sprite_accessory/tails_animated/mam_tails_animated/pede
- name = "Scolipede"
- icon_state = "pede"
+/datum/sprite_accessory/tails_animated/mam_tails_animated/otie
+ name = "Otusian"
+ icon_state = "otie"
/datum/sprite_accessory/tails/mam_tails/rabbit
name = "Rabbit"
@@ -768,6 +769,24 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Rabbit"
icon_state = "rabbit"
+/datum/sprite_accessory/tails/mam_tails/ailurus
+ name = "Red Panda"
+ icon_state = "wah"
+ extra = TRUE
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/ailurus
+ name = "Red Panda"
+ icon_state = "wah"
+ extra = TRUE
+
+/datum/sprite_accessory/tails/mam_tails/pede
+ name = "Scolipede"
+ icon_state = "pede"
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/pede
+ name = "Scolipede"
+ icon_state = "pede"
+
/datum/sprite_accessory/tails/mam_tails/sergal
name = "Sergal"
icon_state = "sergal"
@@ -776,6 +795,22 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Sergal"
icon_state = "sergal"
+/datum/sprite_accessory/tails/mam_tails/shark
+ name = "Shark"
+ icon_state = "shark"
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/shark
+ name = "Shark"
+ icon_state = "shark"
+
+/datum/sprite_accessory/tails/mam_tails/shepherd
+ name = "Shepherd"
+ icon_state = "shepherd"
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/shepherd
+ name = "Shepherd"
+ icon_state = "shepherd"
+
/datum/sprite_accessory/tails/mam_tails/skunk
name = "Skunk"
icon_state = "skunk"
@@ -808,22 +843,6 @@ datum/sprite_accessory/tails/mam_tails/insect
color_src = MUTCOLORS
icon = 'icons/mob/mutant_bodyparts.dmi'
-/datum/sprite_accessory/tails/mam_tails/shark
- name = "Shark"
- icon_state = "shark"
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/shark
- name = "Shark"
- icon_state = "shark"
-
-/datum/sprite_accessory/tails/mam_tails/shepherd
- name = "Shepherd"
- icon_state = "shepherd"
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/shepherd
- name = "Shepherd"
- icon_state = "shepherd"
-
/datum/sprite_accessory/tails/mam_tails/straighttail
name = "Straight Tail"
icon_state = "straighttail"
@@ -864,30 +883,6 @@ datum/sprite_accessory/tails/mam_tails/insect
name = "Tiger"
icon_state = "tiger"
-/datum/sprite_accessory/tails/mam_tails/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
- color_src = MUTCOLORS
- icon = 'icons/mob/mutant_bodyparts.dmi'
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/dtiger
- name = "Dark Tiger"
- icon_state = "dtiger"
- color_src = MUTCOLORS
- icon = 'icons/mob/mutant_bodyparts.dmi'
-
-/datum/sprite_accessory/tails/mam_tails/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
- color_src = MUTCOLORS
- icon = 'icons/mob/mutant_bodyparts.dmi'
-
-/datum/sprite_accessory/tails_animated/mam_tails_animated/ltiger
- name = "Light Tiger"
- icon_state = "ltiger"
- color_src = MUTCOLORS
- icon = 'icons/mob/mutant_bodyparts.dmi'
-
/datum/sprite_accessory/tails/mam_tails/wolf
name = "Wolf"
icon_state = "wolf"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm
index b8282931d2..1be02c207e 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm
@@ -12,6 +12,38 @@
// please make sure they're sorted alphabetically and categorized
+/datum/sprite_accessory/underwear/top/cowboyshirt
+ name = "Cowboy Shirt Black"
+ icon_state = "cowboyshirt"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/red
+ name = "Cowboy Shirt Red"
+ icon_state = "cowboyshirt_red"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/navy
+ name = "Cowboy Shirt Navy"
+ icon_state = "cowboyshirt_navy"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/white
+ name = "Cowboy Shirt White"
+ icon_state = "cowboyshirt_white"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/s
+ name = "Cowboy Shirt Shortsleeved Black"
+ icon_state = "cowboyshirt_s"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/red/s
+ name = "Cowboy Shirt Shortsleeved Red"
+ icon_state = "cowboyshirt_reds"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/navy/s
+ name = "Cowboy Shirt Shortsleeved Navy"
+ icon_state = "cowboyshirt_navys"
+
+/datum/sprite_accessory/underwear/top/cowboyshirt/white/s
+ name = "Cowboy Shirt Shortsleeved White"
+ icon_state = "cowboyshirt_whites"
+
/datum/sprite_accessory/underwear/top/longjon
name = "Long John Shirt"
icon_state = "ljont"
@@ -30,36 +62,6 @@
icon_state = "undershirt"
has_color = TRUE
-/datum/sprite_accessory/underwear/top/bowlingw
- name = "Shirt - Bowling"
- icon_state = "bowlingw"
- has_color = TRUE
-
-/datum/sprite_accessory/underwear/top/bowling
- name = "Shirt, Bowling - Red"
- icon_state = "bowling"
-
-/datum/sprite_accessory/underwear/top/bowlingp
- name = "Shirt, Bowling - Pink"
- icon_state = "bowlingp"
-
-/datum/sprite_accessory/underwear/top/bowlinga
- name = "Shirt, Bowling - Aqua"
- icon_state = "bowlinga"
-
-/datum/sprite_accessory/underwear/top/bluejersey
- name = "Shirt, Jersey - Blue"
- icon_state = "shirt_bluejersey"
-
-/datum/sprite_accessory/underwear/top/redjersey
- name = "Shirt, Jersey - Red"
- icon_state = "shirt_redjersey"
-
-/datum/sprite_accessory/underwear/top/polo
- name = "Shirt - Polo"
- icon_state = "polo"
- has_color = TRUE
-
/datum/sprite_accessory/underwear/top/alienshirt
name = "Shirt - Alien"
icon_state = "shirt_alien"
@@ -72,6 +74,23 @@
name = "Shirt - Bee"
icon_state = "bee_shirt"
+/datum/sprite_accessory/underwear/top/bowlingw
+ name = "Shirt - Bowling"
+ icon_state = "bowlingw"
+ has_color = TRUE
+
+/datum/sprite_accessory/underwear/top/bowlinga
+ name = "Shirt, Bowling - Aqua"
+ icon_state = "bowlinga"
+
+/datum/sprite_accessory/underwear/top/bowling
+ name = "Shirt, Bowling - Red"
+ icon_state = "bowling"
+
+/datum/sprite_accessory/underwear/top/bowlingp
+ name = "Shirt, Bowling - Pink"
+ icon_state = "bowlingp"
+
/datum/sprite_accessory/underwear/top/clownshirt
name = "Shirt - Clown"
icon_state = "shirt_clown"
@@ -88,6 +107,14 @@
name = "Shirt - I Love NT"
icon_state = "ilovent"
+/datum/sprite_accessory/underwear/top/bluejersey
+ name = "Shirt, Jersey - Blue"
+ icon_state = "shirt_bluejersey"
+
+/datum/sprite_accessory/underwear/top/redjersey
+ name = "Shirt, Jersey - Red"
+ icon_state = "shirt_redjersey"
+
/datum/sprite_accessory/underwear/top/lover
name = "Shirt - Lover"
icon_state = "lover"
@@ -112,6 +139,11 @@
name = "Shirt - Pogoman"
icon_state = "pogoman"
+/datum/sprite_accessory/underwear/top/polo
+ name = "Shirt - Polo"
+ icon_state = "polo"
+ has_color = TRUE
+
/datum/sprite_accessory/underwear/top/question
name = "Shirt - Question"
icon_state = "shirt_question"
@@ -120,6 +152,23 @@
name = "Shirt - Skull"
icon_state = "shirt_skull"
+/datum/sprite_accessory/underwear/top/shortsleeve
+ name = "Shirt - Short Sleeved"
+ icon_state = "shortsleeve"
+ has_color = TRUE
+
+/datum/sprite_accessory/underwear/top/blueshirtsport
+ name = "Shirt, Sports - Blue"
+ icon_state = "blueshirtsport"
+
+/datum/sprite_accessory/underwear/top/greenshirtsport
+ name = "Shirt, Sports - Green"
+ icon_state = "greenshirtsport"
+
+/datum/sprite_accessory/underwear/top/redshirtsport
+ name = "Shirt, Sports - Red"
+ icon_state = "redshirtsport"
+
/datum/sprite_accessory/underwear/top/ss13
name = "Shirt - SS13"
icon_state = "shirt_ss13"
@@ -141,27 +190,6 @@
name = "Shirt - USA"
icon_state = "shirt_assblastusa"
-/datum/sprite_accessory/underwear/top/shortsleeve
- name = "Shirt - Short Sleeved"
- icon_state = "shortsleeve"
- has_color = TRUE
-
-/datum/sprite_accessory/underwear/top/blueshirtsport
- name = "Shirt, Sports - Blue"
- icon_state = "blueshirtsport"
-
-/datum/sprite_accessory/underwear/top/greenshirtsport
- name = "Shirt, Sports - Green"
- icon_state = "greenshirtsport"
-
-/datum/sprite_accessory/underwear/top/redshirtsport
- name = "Shirt, Sports - Red"
- icon_state = "redshirtsport"
-
-/datum/sprite_accessory/underwear/top/tankfire
- name = "Tank Top - Fire"
- icon_state = "tank_fire"
-
/datum/sprite_accessory/underwear/top/tanktop
name = "Tank Top"
icon_state = "tanktop"
@@ -172,6 +200,10 @@
icon_state = "tanktop_alt"
has_color = TRUE
+/datum/sprite_accessory/underwear/top/tankfire
+ name = "Tank Top - Fire"
+ icon_state = "tank_fire"
+
/datum/sprite_accessory/underwear/top/tanktop_midriff
name = "Tank Top - Midriff"
icon_state = "tank_midriff"
@@ -192,6 +224,8 @@
name = "Tank top - Sun"
icon_state = "tank_sun"
+//feminine accessories from here on
+
/datum/sprite_accessory/underwear/top/babydoll
name = "Baby-Doll"
icon_state = "babydoll"
@@ -210,15 +244,25 @@
has_color = TRUE
gender = FEMALE
-/datum/sprite_accessory/underwear/top/bra_thin
- name = "Bra - Thin"
- icon_state = "bra_thin"
- has_color = TRUE
+/datum/sprite_accessory/underwear/top/bra_beekini
+ name = "Bra - Bee-kini"
+ icon_state = "bra_bee-kini"
gender = FEMALE
-/datum/sprite_accessory/underwear/top/bra_kinky
- name = "Bra - Kinky Black"
- icon_state = "bra_kinky"
+/datum/sprite_accessory/underwear/top/bra_binder
+ name = "Bra (binder)"
+ icon_state = "bra_binder"
+ has_color = TRUE
+
+/datum/sprite_accessory/underwear/top/bra_binder_strapless
+ name = "Bra (binder, strapless)"
+ icon_state = "bra_binder_strapless"
+ has_color = TRUE
+
+
+/datum/sprite_accessory/underwear/top/bra_commie
+ name = "Bra - Commie"
+ icon_state = "bra_commie"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_freedom
@@ -226,33 +270,17 @@
icon_state = "bra_assblastusa"
gender = FEMALE
-/datum/sprite_accessory/underwear/top/bra_commie
- name = "Bra - Commie"
- icon_state = "bra_commie"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/top/bra_beekini
- name = "Bra - Bee-kini"
- icon_state = "bra_bee-kini"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/top/bra_uk
- name = "Bra - UK"
- icon_state = "bra_uk"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/top/bra_neko
- name = "Bra - Neko"
- icon_state = "bra_neko"
- has_color = TRUE
- gender = FEMALE
-
/datum/sprite_accessory/underwear/top/halterneck_bra
name = "Bra - Halterneck"
icon_state = "halterneck_bra"
has_color = TRUE
gender = FEMALE
+/datum/sprite_accessory/underwear/top/bra_kinky
+ name = "Bra - Kinky Black"
+ icon_state = "bra_kinky"
+ gender = FEMALE
+
/datum/sprite_accessory/underwear/top/sports_bra
name = "Bra, Sports"
icon_state = "sports_bra"
@@ -283,9 +311,21 @@
has_color = TRUE
gender = FEMALE
-/datum/sprite_accessory/underwear/top/fishnet_sleeves
- name = "Fishnet - sleeves"
- icon_state = "fishnet_sleeves"
+/datum/sprite_accessory/underwear/top/bra_thin
+ name = "Bra - Thin"
+ icon_state = "bra_thin"
+ has_color = TRUE
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/top/bra_neko
+ name = "Bra - Neko"
+ icon_state = "bra_neko"
+ has_color = TRUE
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/top/bra_uk
+ name = "Bra - UK"
+ icon_state = "bra_uk"
gender = FEMALE
/datum/sprite_accessory/underwear/top/fishnet_gloves
@@ -293,6 +333,11 @@
icon_state = "fishnet_gloves"
gender = FEMALE
+/datum/sprite_accessory/underwear/top/fishnet_sleeves
+ name = "Fishnet - sleeves"
+ icon_state = "fishnet_sleeves"
+ gender = FEMALE
+
/datum/sprite_accessory/underwear/top/fishnet_base
name = "Fishnet - top"
icon_state = "fishnet_body"
@@ -315,45 +360,3 @@
icon_state = "tubetop"
has_color = TRUE
gender = FEMALE
-
-/datum/sprite_accessory/underwear/top/cowboyshirt
- name = "Cowboy Shirt Black"
- icon_state = "cowboyshirt"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/s
- name = "Cowboy Shirt Shortsleeved Black"
- icon_state = "cowboyshirt_s"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/white
- name = "Cowboy Shirt White"
- icon_state = "cowboyshirt_white"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/white/s
- name = "Cowboy Shirt Shortsleeved White"
- icon_state = "cowboyshirt_whites"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/navy
- name = "Cowboy Shirt Navy"
- icon_state = "cowboyshirt_navy"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/navy/s
- name = "Cowboy Shirt Shortsleeved Navy"
- icon_state = "cowboyshirt_navys"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/red
- name = "Cowboy Shirt Red"
- icon_state = "cowboyshirt_red"
-
-/datum/sprite_accessory/underwear/top/cowboyshirt/red/s
- name = "Cowboy Shirt Shortsleeved Red"
- icon_state = "cowboyshirt_reds"
-
-/datum/sprite_accessory/underwear/top/bra_binder
- name = "Bra (binder)"
- icon_state = "bra_binder"
- has_color = TRUE
-
-/datum/sprite_accessory/underwear/top/bra_binder_strapless
- name = "Bra (binder, strapless)"
- icon_state = "bra_binder_strapless"
- has_color = TRUE
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm
index 58d5e1ba88..edfeba79f1 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm
@@ -10,18 +10,6 @@
icon_state = null
covers_groin = FALSE
-/datum/sprite_accessory/underwear/bottom/mankini
- name = "Mankini"
- icon_state = "mankini"
- has_color = TRUE
- gender = MALE
-
-/datum/sprite_accessory/underwear/bottom/male_kinky
- name = "Jockstrap"
- icon_state = "jockstrap"
- has_color = TRUE
- gender = MALE
-
/datum/sprite_accessory/underwear/bottom/briefs
name = "Briefs"
icon_state = "briefs"
@@ -77,6 +65,26 @@
has_digitigrade = TRUE
has_color = TRUE
+/datum/sprite_accessory/underwear/bottom/male_kinky
+ name = "Jockstrap"
+ icon_state = "jockstrap"
+ has_color = TRUE
+ gender = MALE
+
+/datum/sprite_accessory/underwear/bottom/longjon
+ name = "Long John Bottoms"
+ icon_state = "ljonb"
+ has_digitigrade = TRUE
+ has_color = TRUE
+
+/datum/sprite_accessory/underwear/bottom/mankini
+ name = "Mankini"
+ icon_state = "mankini"
+ has_color = TRUE
+ gender = MALE
+
+//feminine underwear from here on
+
/datum/sprite_accessory/underwear/bottom/panties
name = "Panties"
icon_state = "panties"
@@ -89,11 +97,6 @@
has_color = TRUE
gender = FEMALE
-/datum/sprite_accessory/underwear/bottom/fishnet_lower
- name = "Panties - Fishnet"
- icon_state = "fishnet_lower"
- gender = FEMALE
-
/datum/sprite_accessory/underwear/bottom/female_beekini
name = "Panties - Bee-kini"
icon_state = "panties_bee-kini"
@@ -104,6 +107,11 @@
icon_state = "panties_commie"
gender = FEMALE
+/datum/sprite_accessory/underwear/bottom/fishnet_lower
+ name = "Panties - Fishnet"
+ icon_state = "fishnet_lower"
+ gender = FEMALE
+
/datum/sprite_accessory/underwear/bottom/female_usastripe
name = "Panties - Freedom"
icon_state = "panties_assblastusa"
@@ -114,11 +122,6 @@
icon_state = "panties_kinky"
gender = FEMALE
-/datum/sprite_accessory/underwear/bottom/panties_uk
- name = "Panties - UK"
- icon_state = "panties_uk"
- gender = FEMALE
-
/datum/sprite_accessory/underwear/bottom/panties_neko
name = "Panties - Neko"
icon_state = "panties_neko"
@@ -149,17 +152,10 @@
has_color = TRUE
gender = FEMALE
-/datum/sprite_accessory/underwear/bottom/longjon
- name = "Long John Bottoms"
- icon_state = "ljonb"
- has_digitigrade = TRUE
- has_color = TRUE
-
-/datum/sprite_accessory/underwear/bottom/swimsuit_red
- name = "Swimsuit, One Piece - Red"
- icon_state = "swimming_red"
+/datum/sprite_accessory/underwear/bottom/panties_uk
+ name = "Panties - UK"
+ icon_state = "panties_uk"
gender = FEMALE
- covers_chest = TRUE
/datum/sprite_accessory/underwear/bottom/swimsuit
name = "Swimsuit, One Piece - Black"
@@ -173,6 +169,12 @@
gender = FEMALE
covers_chest = TRUE
+/datum/sprite_accessory/underwear/bottom/swimsuit_red
+ name = "Swimsuit, One Piece - Red"
+ icon_state = "swimming_red"
+ gender = FEMALE
+ covers_chest = TRUE
+
/datum/sprite_accessory/underwear/bottom/thong
name = "Thong"
icon_state = "thong"
@@ -184,5 +186,3 @@
icon_state = "thong_babydoll"
has_color = TRUE
gender = FEMALE
-
-
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
index fb03df88a1..fb71bb483d 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
@@ -58,6 +58,10 @@
dimension_y = 34
relevant_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER)
+/datum/sprite_accessory/deco_wings/atlas
+ name = "Atlas"
+ icon_state = "atlas"
+
/datum/sprite_accessory/deco_wings/bat
name = "Bat"
icon_state = "bat"
@@ -66,6 +70,10 @@
name = "Bee"
icon_state = "bee"
+/datum/sprite_accessory/deco_wings/deathhead
+ name = "Deathshead"
+ icon_state = "deathhead"
+
/datum/sprite_accessory/deco_wings/fairy
name = "Fairy"
icon_state = "fairy"
@@ -74,14 +82,6 @@
name = "Feathery"
icon_state = "feathery"
-/datum/sprite_accessory/deco_wings/atlas
- name = "Atlas"
- icon_state = "atlas"
-
-/datum/sprite_accessory/deco_wings/deathhead
- name = "Deathshead"
- icon_state = "deathhead"
-
/datum/sprite_accessory/deco_wings/firewatch
name = "Firewatch"
icon_state = "firewatch"
@@ -150,6 +150,10 @@
icon_state = "none"
relevant_layers = null
+/datum/sprite_accessory/insect_wings/atlas
+ name = "Atlas"
+ icon_state = "atlas"
+
/datum/sprite_accessory/insect_wings/bat
name = "Bat"
icon_state = "bat"
@@ -158,6 +162,10 @@
name = "Bee"
icon_state = "bee"
+/datum/sprite_accessory/insect_wings/deathhead
+ name = "Deathshead"
+ icon_state = "deathhead"
+
/datum/sprite_accessory/insect_wings/fairy
name = "Fairy"
icon_state = "fairy"
@@ -166,14 +174,6 @@
name = "Feathery"
icon_state = "feathery"
-/datum/sprite_accessory/insect_wings/atlas
- name = "Atlas"
- icon_state = "atlas"
-
-/datum/sprite_accessory/insect_wings/deathhead
- name = "Deathshead"
- icon_state = "deathhead"
-
/datum/sprite_accessory/insect_wings/firewatch
name = "Firewatch"
icon_state = "firewatch"
@@ -182,6 +182,10 @@
name = "Gothic"
icon_state = "gothic"
+/datum/sprite_accessory/insect_wings/jungle
+ name = "Jungle"
+ icon_state = "jungle"
+
/datum/sprite_accessory/insect_wings/lovers
name = "Lovers"
icon_state = "lovers"
@@ -198,6 +202,10 @@
name = "Moon Fly"
icon_state = "moonfly"
+/datum/sprite_accessory/insect_wings/oakworm
+ name = "Oak Worm"
+ icon_state = "oakworm"
+
/datum/sprite_accessory/insect_wings/plain
name = "Plain"
icon_state = "plain"
@@ -230,14 +238,6 @@
name = "White Fly"
icon_state = "whitefly"
-/datum/sprite_accessory/insect_wings/oakworm
- name = "Oak Worm"
- icon_state = "oakworm"
-
-/datum/sprite_accessory/insect_wings/jungle
- name = "Jungle"
- icon_state = "jungle"
-
/datum/sprite_accessory/insect_wings/witchwing
name = "Witch Wing"
icon_state = "witchwing"
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/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index 891243496a..85c256f5b0 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -169,13 +169,7 @@
if(!brainmob || iscyborg(loc))
return
else
- switch(severity)
- if(1)
- brainmob.emp_damage = min(brainmob.emp_damage + rand(20,30), 30)
- if(2)
- brainmob.emp_damage = min(brainmob.emp_damage + rand(10,20), 30)
- if(3)
- brainmob.emp_damage = min(brainmob.emp_damage + rand(0,10), 30)
+ brainmob.emp_damage = min(brainmob.emp_damage + rand(-5,5) + severity/3, 30)
brainmob.emote("alarm")
/obj/item/mmi/Destroy()
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index a853416e6f..59a119a89a 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -284,11 +284,19 @@
QDEL_LIST(traumas)
return ..()
+//other types of brains
+
/obj/item/organ/brain/alien
name = "alien brain"
desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?"
icon_state = "brain-x"
+/obj/item/organ/brain/ipc
+ name = "positronic brain"
+ desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. It has an IPC serial number engraved on the top. It is usually slotted into the head of synthetic crewmembers."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "posibrain-ipc"
+
////////////////////////////////////TRAUMAS////////////////////////////////////////
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 edb681e60f..1706763f6b 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -87,7 +87,7 @@
if(user != src && (user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM))
for(var/datum/surgery/S in surgeries)
if(S.next_step(user,user.a_intent))
- return 1
+ return STOP_ATTACK_PROC_CHAIN
if(!all_wounds || !(user.a_intent == INTENT_HELP || user == src))
return ..()
@@ -95,7 +95,7 @@
for(var/i in shuffle(all_wounds))
var/datum/wound/W = i
if(W.try_treating(I, user))
- return 1
+ return STOP_ATTACK_PROC_CHAIN
return ..()
@@ -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 cb0dbef332..837b4f73cf 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -1,12 +1,14 @@
-/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"
message = "cries."
emote_type = EMOTE_AUDIBLE
+/datum/emote/living/carbon/human/cry/run_emote(mob/user, params)
+ . = ..()
+ if(. && isipcperson(user))
+ do_fake_sparks(5,FALSE,user)
+
/datum/emote/living/carbon/human/dap
key = "dap"
key_third_person = "daps"
@@ -187,3 +189,71 @@
key_third_person = "chimes"
message = "chimes."
sound = 'sound/machines/chime.ogg'
+
+//rock paper scissors emote handling
+/mob/living/carbon/human/proc/beginRockPaperScissors(var/chosen_move)
+ GLOB.rockpaperscissors_players[src] = list(chosen_move, ROCKPAPERSCISSORS_NOT_DECIDED)
+ do_after_advanced(src, ROCKPAPERSCISSORS_TIME_LIMIT, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_NO_PROGRESSBAR|DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER, CALLBACK(src, .proc/rockpaperscissors_tick))
+ var/new_entry = GLOB.rockpaperscissors_players[src]
+ if(new_entry[2] == ROCKPAPERSCISSORS_NOT_DECIDED)
+ to_chat(src, "You put your hand back down.")
+ GLOB.rockpaperscissors_players -= src
+
+/mob/living/carbon/human/proc/rockpaperscissors_tick() //called every cycle of the progress bar for rock paper scissors while waiting for an opponent
+ var/mob/living/carbon/human/opponent
+ for(var/mob/living/carbon/human/potential_opponent in (GLOB.rockpaperscissors_players - src)) //dont play against yourself
+ if(get_dist(src, potential_opponent) <= ROCKPAPERSCISSORS_RANGE)
+ opponent = potential_opponent
+ break
+ if(opponent)
+ //we found an opponent before they found us
+ var/move_to_number = list("rock" = 0, "paper" = 1, "scissors" = 2)
+ var/our_move = move_to_number[GLOB.rockpaperscissors_players[src][1]]
+ var/their_move = move_to_number[GLOB.rockpaperscissors_players[opponent][1]]
+ var/result_us = ROCKPAPERSCISSORS_WIN
+ var/result_them = ROCKPAPERSCISSORS_LOSE
+ if(our_move == their_move)
+ result_us = ROCKPAPERSCISSORS_TIE
+ result_them = ROCKPAPERSCISSORS_TIE
+ else
+ if(((our_move + 1) % 3) == their_move)
+ result_us = ROCKPAPERSCISSORS_LOSE
+ result_them = ROCKPAPERSCISSORS_WIN
+ //we decided our results so set them in the list
+ GLOB.rockpaperscissors_players[src][2] = result_us
+ GLOB.rockpaperscissors_players[opponent][2] = result_them
+
+ //show what happened
+ src.visible_message("[src] makes [GLOB.rockpaperscissors_players[src][1]] with their hand!")
+ opponent.visible_message("[opponent] makes [GLOB.rockpaperscissors_players[opponent][1]] with their hands!")
+ switch(result_us)
+ if(ROCKPAPERSCISSORS_TIE)
+ src.visible_message("It was a tie!")
+ if(ROCKPAPERSCISSORS_WIN)
+ src.visible_message("[src] wins!")
+ if(ROCKPAPERSCISSORS_LOSE)
+ src.visible_message("[opponent] wins!")
+
+ //make the progress bar end so that each player can handle the result
+ return DO_AFTER_STOP
+
+ //no opponent was found, so keep searching
+ return DO_AFTER_PROCEED
+
+//the actual emotes
+/datum/emote/living/carbon/human/rockpaperscissors
+ message = "is attempting to play rock paper scissors!"
+
+/datum/emote/living/carbon/human/rockpaperscissors/rock
+ key = "rock"
+
+/datum/emote/living/carbon/human/rockpaperscissors/paper
+ key = "paper"
+
+/datum/emote/living/carbon/human/rockpaperscissors/scissors
+ key = "scissors"
+
+/datum/emote/living/carbon/human/rockpaperscissors/run_emote(mob/living/carbon/human/user, params)
+ if(!(user in GLOB.rockpaperscissors_players)) //no using the emote again while already playing!
+ . = ..()
+ user.beginRockPaperScissors(key)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 65b2931e08..6af171568b 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -92,7 +92,7 @@
if(!(SLOT_GLASSES in obscured))
if(glasses)
. += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes."
- else if(eye_color == BLOODCULT_EYE && iscultist(src) && HAS_TRAIT(src, TRAIT_CULT_EYES))
+ else if((left_eye_color == BLOODCULT_EYE || right_eye_color == BLOODCULT_EYE) && iscultist(src) && HAS_TRAIT(src, TRAIT_CULT_EYES))
. += "[t_His] eyes are glowing an unnatural red!"
else if(HAS_TRAIT(src, TRAIT_HIJACKER))
var/obj/item/implant/hijack/H = user.getImplant(/obj/item/implant/hijack)
@@ -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_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 18501b4ca2..96fda36370 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -407,18 +407,16 @@
if(. & EMP_PROTECT_CONTENTS)
return
var/informed = FALSE
+ if(isrobotic(src))
+ apply_status_effect(/datum/status_effect/no_combat_mode/robotic_emp, severity / 20)
+ severity *= 0.5
for(var/obj/item/bodypart/L in src.bodyparts)
if(L.status == BODYPART_ROBOTIC)
if(!informed)
to_chat(src, "You feel a sharp pain as your robotic limbs overload.")
informed = TRUE
- switch(severity)
- if(1)
- L.receive_damage(0,10)
- Stun(200)
- if(2)
- L.receive_damage(0,5)
- Stun(100)
+ L.receive_damage(0,severity/10)
+ Stun(severity*2)
/mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit)
var/list/damaged = list()
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 13456ed61c..a6a3f408dc 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -24,7 +24,8 @@
var/facial_hair_style = "Shaved"
//Eye colour
- var/eye_color = "000"
+ var/left_eye_color = "000"
+ var/right_eye_color = "000"
var/skin_tone = "caucasian1" //Skin tone
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/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 56ab1f1b10..68e8c5ac29 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -96,6 +96,8 @@
throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(S.breathid == "n2")
throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
+ else if(S.breathid == "ch3br")
+ throw_alert("not_enough_ch3br", /obj/screen/alert/not_enough_ch3br)
return FALSE
else
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 230a634e0e..554013c7bc 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -114,6 +114,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
/// Our default override for typing indicator state
var/typing_indicator_state
+ //the ids you can use for your species, if empty, it means default only and not changeable
+ var/list/allowed_limb_ids
+
///////////
// PROCS //
///////////
@@ -121,7 +124,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
/datum/species/New()
if(!limbs_id) //if we havent set a limbs id to use, just use our own id
- limbs_id = id
+ mutant_bodyparts["limbs_id"] = id //done this way to be non-intrusive to the existing system
+ else
+ mutant_bodyparts["limbs_id"] = limbs_id
..()
//update our mutant bodyparts to include unlocked ones
@@ -359,8 +364,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
C.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod)
+ if(ROBOTIC_LIMBS in species_traits)
+ for(var/obj/item/bodypart/B in C.bodyparts)
+ B.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) // Makes all Bodyparts robotic.
+ B.render_like_organic = TRUE
+
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
+/datum/species/proc/update_species_slowdown(mob/living/carbon/human/H)
+ H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod)
// EDIT ENDS
@@ -397,6 +409,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(F)
qdel(F)
+
+ if(ROBOTIC_LIMBS in species_traits)
+ for(var/obj/item/bodypart/B in C.bodyparts)
+ B.change_bodypart_status(BODYPART_ORGANIC, FALSE, TRUE)
+ B.render_like_organic = FALSE
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
@@ -417,7 +435,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/dynamic_fhair_suffix = ""
//for augmented heads
- if(HD.status == BODYPART_ROBOTIC)
+ if(HD.status == BODYPART_ROBOTIC && !HD.render_like_organic)
return
//we check if our hat or helmet hides our facial hair.
@@ -565,19 +583,21 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
// eyes
if(!(NOEYES in species_traits))
var/has_eyes = H.getorganslot(ORGAN_SLOT_EYES)
- var/mutable_appearance/eye_overlay
if(!has_eyes)
- eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
+ standing += mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
else
- eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes", -BODY_LAYER)
- if((EYECOLOR in species_traits) && has_eyes)
- eye_overlay.color = "#" + H.eye_color
-
- if(OFFSET_EYES in H.dna.species.offset_features)
- eye_overlay.pixel_x += H.dna.species.offset_features[OFFSET_EYES][1]
- eye_overlay.pixel_y += H.dna.species.offset_features[OFFSET_EYES][2]
-
- standing += eye_overlay
+ var/mutable_appearance/left_eye = mutable_appearance('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER)
+ var/mutable_appearance/right_eye = mutable_appearance('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER)
+ if((EYECOLOR in species_traits) && has_eyes)
+ left_eye.color = "#" + H.left_eye_color
+ right_eye.color = "#" + H.right_eye_color
+ if(OFFSET_EYES in offset_features)
+ left_eye.pixel_x += offset_features[OFFSET_EYES][1]
+ left_eye.pixel_y += offset_features[OFFSET_EYES][2]
+ right_eye.pixel_x += offset_features[OFFSET_EYES][1]
+ right_eye.pixel_y += offset_features[OFFSET_EYES][2]
+ standing += left_eye
+ standing += right_eye
//Underwear, Undershirts & Socks
if(!(NO_UNDERWEAR in species_traits))
@@ -778,7 +798,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(FACEHAIR)
accessory_overlay.color = "#[H.facial_hair_color]"
if(EYECOLOR)
- accessory_overlay.color = "#[H.eye_color]"
+ accessory_overlay.color = "#[H.left_eye_color]"
if(HORNCOLOR)
accessory_overlay.color = "#[H.dna.features["horns_color"]]"
if(WINGCOLOR)
@@ -833,7 +853,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(FACEHAIR)
extra_accessory_overlay.color = "#[H.facial_hair_color]"
if(EYECOLOR)
- extra_accessory_overlay.color = "#[H.eye_color]"
+ extra_accessory_overlay.color = "#[H.left_eye_color]"
if(HORNCOLOR)
extra_accessory_overlay.color = "#[H.dna.features["horns_color"]]"
@@ -1374,6 +1394,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
@@ -1589,6 +1612,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()
@@ -1600,8 +1627,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
@@ -1951,19 +1976,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))
@@ -1981,6 +2006,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")
@@ -1993,14 +2026,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/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm
index c5b57b82ab..5d43d18429 100644
--- a/code/modules/mob/living/carbon/human/species_types/android.dm
+++ b/code/modules/mob/living/carbon/human/species_types/android.dm
@@ -2,7 +2,7 @@
name = "Android"
id = "android"
say_mod = "states"
- species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL)
+ species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,ROBOTIC_LIMBS)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_NOFIRE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_LIMBATTACHMENT)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
meat = null
diff --git a/code/modules/mob/living/carbon/human/species_types/angel.dm b/code/modules/mob/living/carbon/human/species_types/angel.dm
index 2cf054c7b9..3cae574a90 100644
--- a/code/modules/mob/living/carbon/human/species_types/angel.dm
+++ b/code/modules/mob/living/carbon/human/species_types/angel.dm
@@ -141,4 +141,5 @@
H.setMovetype(H.movement_type & ~FLYING)
override_float = FALSE
H.pass_flags &= ~PASSTABLE
- H.CloseWings()
\ No newline at end of file
+ H.CloseWings()
+ update_species_slowdown(H)
diff --git a/code/modules/mob/living/carbon/human/species_types/bugmen.dm b/code/modules/mob/living/carbon/human/species_types/bugmen.dm
index 2a955e28a4..16b371c772 100644
--- a/code/modules/mob/living/carbon/human/species_types/bugmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/bugmen.dm
@@ -13,10 +13,11 @@
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect
liked_food = MEAT | FRUIT
disliked_food = TOXIC
- icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
exotic_bloodtype = "BUG"
exotic_blood_color = BLOOD_COLOR_BUG
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
- species_type = "insect"
\ No newline at end of file
+ species_type = "insect"
+
+ allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale")
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/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index cd9b3b80c7..c182defda4 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -84,11 +84,7 @@
EMPeffect = TRUE
spec_updatehealth(H)
to_chat(H, "You feel the light of your body leave you.")
- switch(severity)
- if(EMP_LIGHT)
- addtimer(CALLBACK(src, .proc/stop_emp, H), 10 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //We're out for 10 seconds
- if(EMP_HEAVY)
- addtimer(CALLBACK(src, .proc/stop_emp, H), 20 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //We're out for 20 seconds
+ addtimer(CALLBACK(src, .proc/stop_emp, H), (severity/5) SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //lights out
/datum/species/ethereal/proc/on_emag_act(mob/living/carbon/human/H, mob/user)
if(emageffect)
diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
index 7706d4a9d5..534536d6e7 100644
--- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
@@ -2,7 +2,6 @@
name = "Anthromorph"
id = "mammal"
default_color = "4B4B4B"
- icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "deco_wings" = "None",
@@ -17,3 +16,5 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "furry"
+
+ allowed_limb_ids = list("mammal","aquatic","avian")
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm
index 3aef14c256..9bfe661f47 100644
--- a/code/modules/mob/living/carbon/human/species_types/ipc.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm
@@ -3,21 +3,28 @@
id = "ipc"
say_mod = "beeps"
default_color = "00FF00"
- icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
blacklisted = 0
sexes = 0
- species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE)
+ inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD)
+ species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE,HAIR,ROBOTIC_LIMBS)
+ hair_alpha = 210
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
mutant_bodyparts = list("ipc_screen" = "Blank", "ipc_antenna" = "None")
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
- mutanttongue = /obj/item/organ/tongue/robot/ipc
-//Just robo looking parts.
+
+ //Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
mutantliver = /obj/item/organ/liver/ipc
mutantstomach = /obj/item/organ/stomach/ipc
mutanteyes = /obj/item/organ/eyes/ipc
+ mutantears = /obj/item/organ/ears/ipc
+ mutanttongue = /obj/item/organ/tongue/robot/ipc
+ mutant_brain = /obj/item/organ/brain/ipc
+
+ //special cybernetic organ for getting power from apcs
+ mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord)
exotic_bloodtype = "HF"
exotic_blood_color = BLOOD_COLOR_OIL
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..a2c3248c6a 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
@@ -248,6 +249,8 @@
var/mob/living/carbon/human/spare = new /mob/living/carbon/human(H.loc)
spare.underwear = "Nude"
+ spare.undershirt = "Nude"
+ spare.socks = "Nude"
H.dna.transfer_identity(spare, transfer_SE=1)
spare.dna.features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")
spare.real_name = spare.dna.real_name
@@ -271,7 +274,17 @@
\"steps out\" of [H.p_them()].",
"...and after a moment of disorentation, \
you're besides yourself!")
-
+ if(H != spare && isslimeperson(spare) && isslimeperson(H))
+ // transfer the swap-body ui if it's open
+ var/datum/action/innate/swap_body/this_swap = origin_datum.swap_body
+ var/datum/action/innate/swap_body/other_swap = spare_datum.swap_body
+ var/datum/tgui/ui = SStgui.get_open_ui(H, this_swap, "main") || SStgui.get_open_ui(spare, this_swap, "main")
+ if(ui)
+ SStgui.on_close(ui) // basically removes it from lists is all this proc does.
+ ui.user = spare
+ ui.src_object = other_swap
+ SStgui.on_open(ui) // stick it back on the lists
+ ui.process(force = TRUE)
/datum/action/innate/swap_body
name = "Swap Body"
@@ -324,6 +337,8 @@
stat = "Conscious"
if(UNCONSCIOUS)
stat = "Unconscious"
+ if(SOFT_CRIT)
+ stat = "Barely Conscious"
if(DEAD)
stat = "Dead"
var/occupied
@@ -370,7 +385,6 @@
var/mob/living/carbon/human/selected = locate(params["ref"]) in SS.bodies
if(!can_swap(selected))
return
- SStgui.close_uis(src)
swap_to_dupe(H.mind, selected)
/datum/action/innate/swap_body/proc/can_swap(mob/living/carbon/human/dupe)
@@ -404,6 +418,7 @@
/datum/action/innate/swap_body/proc/swap_to_dupe(datum/mind/M, mob/living/carbon/human/dupe)
if(!can_swap(dupe)) //sanity check
return
+ var/mob/living/carbon/human/old = M.current
if(M.current.stat == CONSCIOUS)
M.current.visible_message("[M.current] \
stops moving and starts staring vacantly into space.",
@@ -415,7 +430,20 @@
dupe.visible_message("[dupe] blinks and looks \
around.",
"...and move this one instead.")
-
+ if(old != M.current && dupe == M.current && isslimeperson(dupe))
+ var/datum/species/jelly/slime/other_spec = dupe.dna.species
+ var/datum/action/innate/swap_body/other_swap = other_spec.swap_body
+ // theoretically the transfer_to proc is supposed to transfer the ui from the mob.
+ // so I try to get the UI from one of the two mobs and schlump it over to the new action button
+ var/datum/tgui/ui = SStgui.get_open_ui(old, src, "main") || SStgui.get_open_ui(dupe, src, "main")
+ if(ui)
+ // transfer the UI over. This code is slightly hacky but it fixes the problem
+ // I'd use SStgui.on_transfer but that doesn't let you transfer the src_object as well s
+ SStgui.on_close(ui) // basically removes it from lists is all this proc does.
+ ui.user = dupe
+ ui.src_object = other_swap
+ SStgui.on_open(ui) // stick it back on the lists
+ ui.process(force = TRUE)
////////////////////////////////////////////////////////Round Start Slimes///////////////////////////////////////////////////////////////////
@@ -434,6 +462,8 @@
heatmod = 1
burnmod = 1
+ allowed_limb_ids = list("slime","stargazer","lum")
+
/datum/action/innate/slime_change
name = "Alter Form"
check_flags = AB_CHECK_CONSCIOUS
@@ -461,7 +491,7 @@
var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null
if(new_color)
var/temp_hsv = RGBtoHSV(new_color)
- if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright
+ if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright
H.dna.features["mcolor"] = sanitize_hexcolor(new_color, 6)
H.update_body()
H.update_hair()
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index f5a2f807e4..0f62953e6c 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -21,6 +21,8 @@
species_type = "plant"
+ allowed_limb_ids = list("pod","mush")
+
/datum/species/pod/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
C.faction |= "plants"
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index 228a69c30f..a7d9ce4473 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -184,7 +184,7 @@
else if(isliving(AM))
var/mob/living/L = AM
if(isethereal(AM))
- AM.emp_act(EMP_LIGHT)
+ AM.emp_act(50)
if(iscyborg(AM))
var/mob/living/silicon/robot/borg = AM
if(borg.lamp_intensity)
diff --git a/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/code/modules/mob/living/carbon/human/species_types/synthliz.dm
index 173411eaa1..b446e3796e 100644
--- a/code/modules/mob/living/carbon/human/species_types/synthliz.dm
+++ b/code/modules/mob/living/carbon/human/species_types/synthliz.dm
@@ -1,21 +1,26 @@
/datum/species/synthliz
name = "Synthetic Lizardperson"
id = "synthliz"
- icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
say_mod = "beeps"
default_color = "00FF00"
- species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,HAS_FLESH,HAS_BONE)
+ species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE)
+ inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
mutant_bodyparts = list("ipc_antenna" = "Synthetic Lizard - Antennae","mam_tail" = "Synthetic Lizard", "mam_snouts" = "Synthetic Lizard - Snout", "legs" = "Digitigrade", "mam_body_markings" = "Synthetic Lizard - Plates", "taur" = "None")
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
- mutanttongue = /obj/item/organ/tongue/robot/ipc
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
mutantliver = /obj/item/organ/liver/ipc
mutantstomach = /obj/item/organ/stomach/ipc
mutanteyes = /obj/item/organ/eyes/ipc
+ mutantears = /obj/item/organ/ears/ipc
+ mutanttongue = /obj/item/organ/tongue/robot/ipc
+ mutant_brain = /obj/item/organ/brain/ipc
+
+ //special cybernetic organ for getting power from apcs
+ mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord)
exotic_bloodtype = "S"
exotic_blood_color = BLOOD_COLOR_OIL
diff --git a/code/modules/mob/living/carbon/human/species_types/synths.dm b/code/modules/mob/living/carbon/human/species_types/synths.dm
index c10521dfd9..3d55ce1027 100644
--- a/code/modules/mob/living/carbon/human/species_types/synths.dm
+++ b/code/modules/mob/living/carbon/human/species_types/synths.dm
@@ -61,7 +61,7 @@
mutant_organs = S.mutant_organs.Copy()
nojumpsuit = S.nojumpsuit
no_equip = S.no_equip.Copy()
- limbs_id = S.limbs_id
+ limbs_id = S.mutant_bodyparts["limbs_id"]
use_skintones = S.use_skintones
fixed_mut_color = S.fixed_mut_color
hair_color = S.hair_color
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/human/species_types/xeno.dm b/code/modules/mob/living/carbon/human/species_types/xeno.dm
index db34d1ae45..ddd1c86f0a 100644
--- a/code/modules/mob/living/carbon/human/species_types/xeno.dm
+++ b/code/modules/mob/living/carbon/human/species_types/xeno.dm
@@ -4,7 +4,6 @@
id = "xeno"
say_mod = "hisses"
default_color = "00FF00"
- icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR)
mutant_bodyparts = list("xenotail"="Xenomorph Tail","xenohead"="Standard","xenodorsal"="Standard", "mam_body_markings" = "Xeno","mcolor" = "0F0","mcolor2" = "0F0","mcolor3" = "0F0","taur" = "None", "legs" = "Digitigrade")
attack_verb = "slash"
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index e399ddf872..cfb35fbb00 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -660,7 +660,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
//produces a key based on the human's limbs
/mob/living/carbon/human/generate_icon_render_key()
- . = "[dna.species.limbs_id]"
+ . = "[dna.species.mutant_bodyparts["limbs_id"]]"
if(dna.check_mutation(HULK))
. += "-coloured-hulk"
@@ -758,17 +758,22 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
// eyes
if(!(NOEYES in dna.species.species_traits))
var/has_eyes = getorganslot(ORGAN_SLOT_EYES)
- var/mutable_appearance/eye_overlay
if(!has_eyes)
- eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
+ add_overlay(mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER))
else
- eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes", -BODY_LAYER)
- if((EYECOLOR in dna.species.species_traits) && has_eyes)
- eye_overlay.color = "#" + eye_color
- if(OFFSET_EYES in dna.species.offset_features)
- eye_overlay.pixel_x += dna.species.offset_features[OFFSET_EYES][1]
- eye_overlay.pixel_y += dna.species.offset_features[OFFSET_EYES][2]
- add_overlay(eye_overlay)
+ var/mutable_appearance/left_eye = mutable_appearance('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER)
+ var/mutable_appearance/right_eye = mutable_appearance('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER)
+ if((EYECOLOR in dna.species.species_traits) && has_eyes)
+ left_eye.color = "#" + left_eye_color
+ right_eye.color = "#" + right_eye_color
+ if(OFFSET_EYES in dna.species.offset_features)
+ left_eye.pixel_x += dna.species.offset_features[OFFSET_EYES][1]
+ left_eye.pixel_y += dna.species.offset_features[OFFSET_EYES][2]
+ right_eye.pixel_x += dna.species.offset_features[OFFSET_EYES][1]
+ right_eye.pixel_y += dna.species.offset_features[OFFSET_EYES][2]
+ add_overlay(left_eye)
+ add_overlay(right_eye)
+
dna.species.handle_hair(src)
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/death.dm b/code/modules/mob/living/death.dm
index ad1a3bc9b9..fe5a78de21 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -8,8 +8,6 @@
spill_organs(no_brain, no_organs, no_bodyparts)
- release_vore_contents(silent = TRUE) // return of the bomb safe internals.
-
if(!no_bodyparts)
spread_bodyparts(no_brain, no_organs)
@@ -46,7 +44,6 @@
buckled.unbuckle_mob(src, force = TRUE)
dust_animation()
- release_vore_contents(silent = TRUE) //technically grief protection, I guess? if they're SM'd it doesn't matter seconds after anyway.
spawn_dust(just_ash)
QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes.
@@ -103,5 +100,5 @@
for(var/s in sharedSoullinks)
var/datum/soullink/S = s
S.sharerDies(gibbed)
-
+ release_vore_contents(silent = TRUE)
return TRUE
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 97de8bfd6c..1d78979cbd 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -1,14 +1,13 @@
-
-/* 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"
message = "blushes."
+/datum/emote/living/blush/run_emote(mob/user, params)
+ . = ..()
+ if(. && isipcperson(user))
+ do_fake_sparks(5,FALSE,user)
+
/datum/emote/living/bow
key = "bow"
key_third_person = "bows"
@@ -228,6 +227,21 @@
return
else if(isinsect(C))
playsound(C, 'sound/voice/moth/mothlaugh.ogg', 50, 1)
+ else if(isjellyperson(C))
+ var/mob/living/carbon/human/H = C
+ if(H.dna.features["mam_ears"] == "Cat" || H.dna.features["mam_ears"] == "Cat, Big") //slime have cat ear. slime go nya.
+ playsound(C, pick('sound/voice/jelly/nyahaha1.ogg',
+ 'sound/voice/jelly/nyahaha2.ogg',
+ 'sound/voice/jelly/nyaha.ogg',
+ 'sound/voice/jelly/nyahehe.ogg'),
+ 50, 1)
+ return
+ else if(user.gender == FEMALE)
+ playsound(C, 'sound/voice/jelly/womanlaugh.ogg', 50, 1)
+ return
+ else
+ playsound(C, pick('sound/voice/jelly/manlaugh1.ogg', 'sound/voice/jelly/manlaugh2.ogg'), 50, 1)
+ return
else if(ishumanbasic(C))
if(user.gender == FEMALE)
playsound(C, 'sound/voice/human/womanlaugh.ogg', 50, 1)
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 89321082c9..e7727fcedc 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -1,5 +1,5 @@
/**
- * Called by SSmobs at (hopefully) an interval of 1 second.
+ * Called by SSmobs at an interval of 2 seconds.
* Splits off into PhysicalLife() and BiologicalLife(). Override those instead of this.
*/
/mob/living/proc/Life(seconds, times_fired)
@@ -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/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm
index b6706d0548..10dece1ffc 100644
--- a/code/modules/mob/living/living_active_parry.dm
+++ b/code/modules/mob/living/living_active_parry.dm
@@ -244,7 +244,14 @@
if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0))
. |= BLOCK_SUCCESS
var/list/effect_text
- if(efficiency >= data.parry_efficiency_to_counterattack)
+ var/pacifist_counter_check = TRUE
+ if(HAS_TRAIT(src, TRAIT_PACIFISM))
+ switch(parrying)
+ if(ITEM_PARRY)
+ pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA)
+ else
+ pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice.
+ if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check)
effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency)
if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK)
handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text)
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/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm
index 9cefa7a12f..b05b5f1aaf 100644
--- a/code/modules/mob/living/silicon/ai/ai_defense.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defense.dm
@@ -19,13 +19,14 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- disconnect_shell()
- if (prob(30))
- switch(pick(1,2))
- if(1)
- view_core()
- if(2)
- SSshuttle.requestEvac(src,"ALERT: Energy surge detected in AI core! Station integrity may be compromised! Initiati--%m091#ar-BZZT")
+ if(severity >= 60)
+ disconnect_shell()
+ if(prob(30))
+ switch(pick(1,2))
+ if(1)
+ view_core()
+ if(2)
+ SSshuttle.requestEvac(src,"ALERT: Energy surge detected in AI core! Station integrity may be compromised! Initiati--%m091#ar-BZZT")
/mob/living/silicon/ai/ex_act(severity, target)
switch(severity)
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/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 64509d2deb..d8c1b8fe7d 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -7,8 +7,8 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- take_holo_damage(50/severity)
- DefaultCombatKnockdown(400/severity)
+ take_holo_damage(severity/2)
+ DefaultCombatKnockdown(severity*4)
silent = max(silent, (PAI_EMP_SILENCE_DURATION) / SSmobs.wait / severity)
if(holoform)
fold_in(force = TRUE)
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 9018c49b2c..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
@@ -289,50 +289,57 @@
return FALSE
return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
-/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src))
+/mob/living/silicon/robot/proc/attempt_welder_repair(obj/item/weldingtool/W, mob/user)
+ if (!getBruteLoss())
+ to_chat(user, "[src] is already in good condition!")
+ return
+ if (!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
+ return
+ user.DelayNextAction(CLICK_CD_MELEE)
+ if(src == user)
+ to_chat(user, "You start fixing yourself...")
+ if(!W.use_tool(src, user, 50))
+ return
+ adjustBruteLoss(-10)
+ else
+ to_chat(user, "You start fixing [src]...")
+ if(!do_after(user, 30, target = src))
+ return
+ adjustBruteLoss(-30)
+ updatehealth()
+ add_fingerprint(user)
+ visible_message("[user] has fixed some of the dents on [src].")
+
+/mob/living/silicon/robot/proc/attempt_cable_repair(obj/item/stack/cable_coil/W, mob/user)
+ if (getFireLoss() > 0 || getToxLoss() > 0)
user.DelayNextAction(CLICK_CD_MELEE)
- if (!getBruteLoss())
- to_chat(user, "[src] is already in good condition!")
- return
- if (!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
- return
if(src == user)
to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50))
+ if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
+ to_chat(user, "You need more cable to repair [src]!")
return
- adjustBruteLoss(-10)
+ adjustFireLoss(-10)
+ adjustToxLoss(-10)
else
to_chat(user, "You start fixing [src]...")
- if(!do_after(user, 30, target = src))
+ if(!W.use_tool(src, user, 30, 1))
+ to_chat(user, "You need more cable to repair [src]!")
return
- adjustBruteLoss(-30)
- updatehealth()
- add_fingerprint(user)
- visible_message("[user] has fixed some of the dents on [src].")
+ adjustFireLoss(-30)
+ adjustToxLoss(-30)
+ updatehealth()
+ user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].")
+ else
+ to_chat(user, "The wires seem fine, there's no need to fix them.")
+
+/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src))
+ INVOKE_ASYNC(src, .proc/attempt_welder_repair, W, user)
return
else if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
- user.DelayNextAction(CLICK_CD_MELEE)
- if (getFireLoss() > 0 || getToxLoss() > 0)
- if(src == user)
- to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-10)
- adjustToxLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!W.use_tool(src, user, 30, 1))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-30)
- adjustToxLoss(-30)
- updatehealth()
- user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].")
- else
- to_chat(user, "The wires seem fine, there's no need to fix them.")
+ INVOKE_ASYNC(src, .proc/attempt_cable_repair, W, user)
+ return
else if(istype(W, /obj/item/crowbar)) // crowbar means open or close the cover
if(opened)
@@ -553,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)
@@ -579,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
@@ -984,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()
@@ -1083,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
@@ -1127,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"
@@ -1136,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..0ed380040b 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -85,12 +85,7 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- switch(severity)
- if(1)
- Paralyze(160)
- if(2)
- Paralyze(60)
-
+ Paralyze(10 + severity/1.2)
/mob/living/silicon/robot/emag_act(mob/user)
if(user == src)//To prevent syndieborgs from emagging themselves
@@ -138,8 +133,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/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 8be2183d3b..3377bf9601 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -105,14 +105,10 @@
to_chat(src, "Warning: Electromagnetic pulse detected.")
if(. & EMP_PROTECT_SELF)
return
- switch(severity)
- if(1)
- src.take_bodypart_damage(20)
- if(2)
- src.take_bodypart_damage(10)
+ src.take_bodypart_damage(severity/5)
to_chat(src, "*BZZZT*")
for(var/mob/living/M in buckled_mobs)
- if(prob(severity*50))
+ if(prob(severity/2))
unbuckle_mob(M)
M.DefaultCombatKnockdown(40)
M.visible_message("[M] is thrown off of [src]!",
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/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index a1772d9281..bb7c437ca7 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -370,7 +370,7 @@
ejectpai(0)
if(on)
turn_off()
- spawn(severity*300)
+ spawn(3 * severity)
stat &= ~EMPED
if(was_on)
turn_on()
@@ -1060,4 +1060,4 @@ Pass a positive integer as an argument to override a bot's default speed.
path.Cut(1, 2)
/mob/living/silicon/rust_heretic_act()
- adjustBruteLoss(500)
+ adjustBruteLoss(500)
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 91462a6713..3f28fd6e01 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -459,12 +459,10 @@ Auto Patrol[]"},
/mob/living/simple_animal/bot/ed209/emp_act(severity)
- if(severity == 2 && prob(70))
- severity = 1
. = ..()
if(. & EMP_PROTECT_SELF)
return
- if (severity >= 2)
+ if (severity >= 65)
new /obj/effect/temp_visual/emp(loc)
var/list/mob/living/carbon/targets = new
for(var/mob/living/carbon/C in view(12,src))
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index d9bf8cc553..9176f410c3 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)
@@ -719,7 +729,6 @@
if(load)
load.emp_act(severity)
-
/mob/living/simple_animal/bot/mulebot/explode()
visible_message("[src] blows apart!")
var/atom/Tsec = drop_location()
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/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index 2a84152088..a8c0e7d207 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -220,11 +220,10 @@
return
Stun(100)
to_chat(src, "ER@%R: MME^RY CO#RU9T! R&$b@0tin)...")
- if(severity == 1)
+ if(severity >= 65)
adjustBruteLoss(heavy_emp_damage)
to_chat(src, "HeAV% DA%^MMA+G TO I/O CIR!%UUT!")
-
/mob/living/simple_animal/drone/proc/triggerAlarm(class, area/A, O, obj/alarmsource)
if(alarmsource.z != z)
return
@@ -240,7 +239,6 @@
L[A.name] = list(A, list(alarmsource))
to_chat(src, "--- [class] alarm detected in [A.name]!")
-
/mob/living/simple_animal/drone/proc/cancelAlarm(class, area/A, obj/origin)
if(stat != DEAD)
var/list/L = alarms[class]
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/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
index ac53ff794b..850beeb099 100644
--- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
@@ -80,7 +80,9 @@
if(C.can_absorb_dna(owner))
C.add_new_profile(owner)
- C.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null)
+ var/obj/effect/proc_holder/changeling/humanform/hf = new
+ C.purchasedpowers += hf
+ hf.on_purchase(origin.current, TRUE)
M.key = origin.key
owner.gib()
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 525388fa61..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
@@ -661,7 +661,7 @@ Difficulty: Normal
continue
to_chat(M.occupant, "Your [M.name] is struck by a [name]!")
playsound(M,'sound/weapons/sear.ogg', 50, 1, -4)
- M.take_damage(damage, BURN, 0, 0)
+ M.take_damage(damage, BURN, 0, 0, null, 50)
/obj/effect/hierophant
name = "hierophant beacon"
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/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
index 12f0b41d4e..fa67fd8e3b 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
@@ -200,6 +200,8 @@
L.Stun(75)
L.adjustBruteLoss(rand(15,20)) // Less stun more harm
latched = TRUE
+ for(var/obj/mecha/M in loc)
+ M.take_damage(20, BRUTE, null, null, null, 25)
if(!latched)
retract()
else
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 4fcb1ed5bf..3ca29b6746 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)
. = ..()
@@ -978,7 +978,8 @@
/mob/living/simple_animal/parrot/Poly/ghost
name = "The Ghost of Poly"
desc = "Doomed to squawk the Earth."
- color = "#FFFFFF77"
+ color = "#FFFFFF"
+ alpha = 77
speak_chance = 20
status_flags = GODMODE
incorporeal_move = INCORPOREAL_MOVE_BASIC
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/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm
index 36a596f42e..6661d0ccea 100644
--- a/code/modules/mob/living/ventcrawling.dm
+++ b/code/modules/mob/living/ventcrawling.dm
@@ -19,8 +19,11 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list(
to_chat(src, "You can't vent crawl while you're restrained!")
return
if(has_buckled_mobs())
- to_chat(src, "You can't vent crawl with other creatures on you!")
- return
+ // attempt once
+ unbuckle_all_mobs()
+ if(has_buckled_mobs())
+ to_chat(src, "You can't vent crawl with other creatures on you!")
+ return
if(buckled)
to_chat(src, "You can't vent crawl while buckled!")
return
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..e1a4ac6118 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -485,11 +485,12 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
if(!ckey)
return
SEND_SIGNAL(new_mob, COMSIG_MOB_PRE_PLAYER_CHANGE, new_mob, src)
- if (client && client.prefs && client.prefs.auto_ooc)
- if (client.prefs.chat_toggles & CHAT_OOC && isliving(new_mob))
- client.prefs.chat_toggles ^= CHAT_OOC
- if (!(client.prefs.chat_toggles & CHAT_OOC) && isdead(new_mob))
- client.prefs.chat_toggles ^= CHAT_OOC
+ if (client)
+ if(client.prefs?.auto_ooc)
+ if (client.prefs.chat_toggles & CHAT_OOC && isliving(new_mob))
+ client.prefs.chat_toggles ^= CHAT_OOC
+ if (!(client.prefs.chat_toggles & CHAT_OOC) && isdead(new_mob))
+ client.prefs.chat_toggles ^= CHAT_OOC
new_mob.ckey = ckey
if(send_signal)
SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob, src)
@@ -586,87 +587,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 +746,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 cb2d49fc64..7e515bf0f5 100644
--- a/code/modules/newscaster/newscaster_machine.dm
+++ b/code/modules/newscaster/newscaster_machine.dm
@@ -95,6 +95,10 @@ GLOBAL_LIST_EMPTY(allCasters)
. = ..()
update_icon()
+/obj/machinery/newscaster/attack_ghost(mob/dead/observer/user)
+ if(istype(user))
+ user.read_news()
+
/obj/machinery/newscaster/ui_interact(mob/user)
. = ..()
if(ishuman(user) || issilicon(user))
@@ -336,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/ninja/suit/n_suit_verbs/ninja_empulse.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm
index ea96c1cdf6..29dc78e5f3 100644
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm
+++ b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm
@@ -6,5 +6,5 @@
if(!ninjacost(250,N_STEALTH_CANCEL))
var/mob/living/carbon/human/H = affecting
playsound(H.loc, 'sound/effects/empulse.ogg', 60, 2)
- empulse(H, 4, 6) //Procs sure are nice. Slightly weaker than wizard's disable tch.
+ empulse_using_range(H, 9) //Procs sure are nice. Slightly weaker than wizard's disable tch.
s_coold = 2
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 414026924a..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,13 +59,46 @@ 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"
- info = "To ensure minimal employee downtime, please take note of the following changes to select professions that CogStation specifically requires: \n \n- Scientists are to have access to chemistry in order to reach the MedSci router. \n \n- Chemists should at the very least be provided with an encryption key for the Science channel, if not basic access to the Research department at large. \n- Roboticists are to have basic Medical and Morgue access. \n- Engineers and Atmospheric Technicians are to have Warehouse and Mining access. \n- The Cook should not have Morgue access \n- The Clown and Mime are to have Maintenance access. This is necessary due to the location of their offices. \n \nGenerated by Organic Resources Bot #2053"
+ info = "To ensure minimal employee downtime, please take note of the following changes to select professions that CogStation specifically requires: \n \n- Engineers and Atmospheric Technicians are to have Warehouse and Mining access. \n- The Cook should not have Morgue access. \n- The Clown and Mime are to have Maintenance access. This is necessary due to the location of their offices. \n \nGenerated by Organic Resources Bot #2053"
/obj/item/paper/guides/cogstation/letter_sec
name = "To future Security personnel"
@@ -104,7 +106,7 @@ Accidental Reentry:
/obj/item/paper/guides/cogstation/disposals
name = "Regarding the disposal system:"
- info = "As you might have noticed, this station has far more disposal pipes than you may expect from your average Nanotrasen research facility. Part of the reason for this is specialization - mail, trash, even corpses have their own disposal systems. Unfortunately, the convenient color-coding was lost in translocation and we've had to compensate by marking the area around each bin. \n \n- WHITE/GRAY STRIPES is for DELIVERIES. \n- RED STRIPES is for CORPSES. \n- EVERYTHING ELSE is for TRASH, barring a few exceptions that should be labeled as such. \n \nIdeally the station won't sustain any heavy structural damage during your time here but if it does, or someone decides to tamper with/sabotage this system, you'll be forgiven if you can't put it back together perfectly. \n \n-C. Donnelly Architectural Analyst"
+ info = "As you might have noticed, this station has far more disposal pipes than you may expect from your average Nanotrasen research facility. Part of the reason for this is specialization - mail, trash, even corpses have their own disposal systems. Unfortunately, the convenient color-coding was lost in translocation and we've had to compensate by marking the area around each bin. \n \n- WHITE/GRAY STRIPES are for DELIVERIES. \n- RED STRIPES are for CORPSES. \n- EVERYTHING ELSE is for TRASH, barring a few exceptions that should be labeled as such. \n \nIdeally the station won't sustain any heavy structural damage during your time here but if it does, or someone decides to tamper with/sabotage this system, you'll be forgiven if you can't put it back together perfectly. \n \n-C. Donnelly Architectural Analyst"
/obj/item/paper/guides/cogstation/janitor
name = "a quick tip"
@@ -128,7 +130,7 @@ Accidental Reentry:
/obj/item/paper/guides/cogstation/letter_eng
name = "To future Engineering staff:"
- info = "I'm not gonna sugarcoat this. Compared to other departments, you might have your work cut out for you. CogStation is an entirely different beast than your standard Box, but everyone's still gonna expect you to keep the place running. \n \n If there's any good news, it's your time to shine if you know how to run a thermo-electric generator. That's what this station runs on, and that isn't likely to change anytime soon. If it's absolutely critical you might be able to run a singularity or tesla engine east of mining, but it won't have any sort of shielding out there. \n \nYou still have three solar arrays to work with, two of them being on each end of the starboard side. The port side array will need you to either get access from a head of staff or security, unless you want to spacewalk around the whole station. Don't be afraid to ask the latter - they're there for you, after all. As for other utilities the air system is a bit different than you'd expect, but fortunately you should have the atmos techs this station needed a long time ago. The disposal network is significantly more complicated, yet more capable. I've already elaborated on it, so I'll let you find and read my write-up for that. As for the routing system, it's just begging to get hit by a stray meteor so consider other utilities a higher priority. \n \nGood luck. You're gonna need it. \n \n-C. Donnelly Architectural Analyst"
+ info = "I'm not gonna sugarcoat this. Compared to other departments, you might have your work cut out for you. CogStation is an entirely different beast than your standard Box, but everyone's still gonna expect you to keep the place running. \n \n If there's any good news, it's your time to shine if you know how to run a thermo-electric generator. That's what this station runs on, and that isn't likely to change anytime soon. If it's absolutely critical you might be able to run a singularity or tesla engine east of mining, but it won't have any sort of protection out there. \n \nYou still have three solar arrays to work with, two of them being on each end of the starboard side. The port side array will need you to either get access from a head of staff or security, unless you want to spacewalk around the whole station. Don't be afraid to ask the latter - they're there for you, after all. As for other utilities the air system is a bit different than you'd expect, but fortunately you should have the atmos techs this station needed a long time ago. The disposal network is significantly more complicated, yet more capable. I've already elaborated on it, so I'll let you find and read my write-up for that. As for the routing system, it's just begging to get hit by a stray meteor so consider other utilities a higher priority. \n \nGood luck. You're gonna need it. \n \n-C. Donnelly Architectural Analyst"
/obj/item/paper/guides/cogstation/letter_atmos
name = "To future Atmospheric Technicians:"
@@ -140,15 +142,15 @@ Accidental Reentry:
/obj/item/paper/guides/cogstation/letter_hos
name = "To the future HoS"
- info = "I'm gonna be rather disappointed if CentCom doesn't brief you about this station, but if they don't I wrote up another letter for your department that should cover it pretty well. Make sure your officers read it if they aren't up to speed. \n \nSomething you in particular should know is that if someone's getting to be too much to handle, the boys and I have constructed a 'discount transfer centre' just behind the router. Use it only as a last resort - the walls may be reinforced but they're still thin, and you'll have big trouble on your hands if the AI or any cyborgs find out about it. \n -LC"
+ info = "I'm gonna be rather disappointed if Central Command doesn't brief you about this station, but if they don't I wrote up another letter for your department that should cover it pretty well. Make sure your officers read it if they aren't up to speed. \n \nSomething you in particular should know is that if someone's getting to be too much to handle, the boys and I have constructed a 'discount transfer centre' just behind the router. Use it only as a last resort - the walls may be reinforced but they're still thin, and you'll have big trouble on your hands if the AI or any cyborgs find out about it. \n -LC"
/obj/item/paper/guides/cogstation/letter_supp
name = "To future Supply Staff:"
- info = "Cargo, move freight. Miners, don't die. Your jobs are pretty straightforward, which is likely why they originally fell under Engineering on this station as opposed to their own department. Although we've considerably readjusted this part of the station to accommodate you, there are potential differences you should be aware of. \n \nEngineeringwill have access to some of your department, namely the warehouse and mining dock. Mining operations on this station were originally asteroid-based, hence the catwalk into the great beyond. Although you won't need to worry about being space-worthy due to a newly installed shuttle dock, they might need to get out there. \n \nYou'll have all your usual means of shipping out goods, but the disposal network is more complex with a separate line for mail and trash. I've left another note that explains this in detail, but know trash is the janitor's responsibility, not yours. \n \nThe biggest difference has to be this station's router system, which allows departments to ship goods between themselves. Even if the belts aren't working properly they'll still have their own request consoles, so you'll want to check for orders regularly. \n \n-C. Donnelly \nArchitectural Analyst"
+ info = "Cargo, move freight. Miners, don't die. Your jobs are pretty straightforward, which is likely why they originally fell under Engineering on this station as opposed to their own department. Although we've considerably readjusted this part of the station to accommodate you, there are potential differences you should be aware of. \n \nEngineeringwill have access to some of your department, namely the warehouse and mining dock. Mining operations on this station were originally asteroid-based, hence the catwalk into the great beyond. Although you won't need to worry about being space-worthy due to a newly installed shuttle dock, they might need to get out there. \n \nYou'll have all your usual means of shipping out goods, but the disposal network is more complex with a separate line for mail and trash. It also isn't fully space-proofed, meanin it may not be the best choice for livestock, monkey cubes, or clowns. I've left another note that explains this in detail, but know trash is the janitor's responsibility, not yours. \n \nThe biggest difference has to be this station's router system, which allows departments to ship goods between themselves. Even if the belts aren't working properly they'll still have their own request consoles, so you'll want to check for orders regularly. \n \n-C. Donnelly \nArchitectural Analyst"
-/obj/item/paper/fluff/cogstation/sleepers
- name = "Re: Sleepers?"
- info = "Yes, the sleepers are meant to be publicly accessible. Policies in this station's original location encouraged crew to visit the clinic or treat themselves when it came to minor injuries. \n \nThis is no excuse for you not to do your jobs. You may wish to keep an eye on the sleepers as to ensure they're being used responsibly. Remember, allowing an overdose to happen under your watch isn't much different from administering that overdose yourself. \n \n- Dr. Halley"
+/obj/item/paper/guides/cogstation/letter_med
+ name = "Re: Future Medical Staff"
+ info = "With this station nearing approval for regular use, I've been told to consolidate anything noteworthy about its general medical department into a single document. As you may be able to guess, this is that document. \n \n- First, you should know a medical clinic is present in the civilian (starboard bow) wing. If you have personnel to spare, it's recommended you have someone staff it - that way people with minor injuries can report there instead of clogging up the research wing. \n \n- Despite recent renovations to bring this station in line with regional policy, you'll still find the robotics lab directly adjacent to your department. I advise you take full advantage of this, whether it's requesting prosthetics in advance or harvesting organs from those who have undergone more...permanent procedures. \n \n- Lastly, please make a habit of checking the morgue on a regular basis. Thanks to the Corpse Disposal Network (or CDN for short), you may find the station's deceased delivered directly to you. Some may be employees capable of being revived - more information can be found in the morgue itself. \n \n- Dr. Halley"
/obj/item/paper/fluff/cogstation/cloner
name = "Re: Issue with the cloner?"
@@ -172,7 +174,7 @@ Accidental Reentry:
/obj/item/paper/fluff/cogstation/letter_chap
name = "A message from the DHDA"
- info = "Regardless of what the name leads you to believe, CogStation is neither Ratvarian in origin nor designed by members of this so-called 'clock cult'. Despite a potential common enemy and instances of exhibiting peaceful behavior, their beliefs have been labeled 'Heretical' by the Department of Higher-Dimensional Affairs and following them is grounds for immediate termination. \nAs the station's designated Chaplain, it is advised you correct anyone who claims this station and/or its designers are Ratvarian. While they are most likely misinformed or 'joking around', untruths gain credibility the more they are repeated. \n \nSoulstone Obelisk \n \nDepartment of Higher-Dimensional Affairs"
+ info = "Regardless of what the name leads you to believe, CogStation is neither Ratvarian in origin nor designed by members of this so-called 'clock cult'. Despite a potential common enemy and instances of exhibiting peaceful behavior, their beliefs have been labeled 'Heretical' by the Department of Higher-Dimensional Affairs and following them is grounds for immediate termination. \nAs the station's designated Chaplain, it is advised you correct anyone who claims this station and/or its designers are Ratvarian. While they are most likely misinformed or 'joking around', untruths gain credibility the more they are repeated. \n \nSoulstone Obelisk \n \nDepartment of Higher-Dimensional Affairs"
/obj/item/paper/fluff/cogstation/cluwne
name = "Mysterious Note"
@@ -184,7 +186,7 @@ Accidental Reentry:
/obj/item/paper/fluff/cogstation/eva
name = "MEMO: Spacesuits"
- info = "As a Head of Personnel, you may be familiar with crew members requesting EVA access, particularly when there is an absence of credible threats on the station. While it is your decision to grant or deny access unless overriden by your superior(s), you should be aware of an abundance of spacesuits on this station. While intended for emergencies, these suits are cheaper to replace. You may find it beneficial to direct aspiring 'space explorers' towards finding one of these suits instead, although it is advised you order replacement suits in advance through the cargo department. \n \n-Generated by Organic Resources Bot #2053"
+ info = "As a Head of Personnel, you may be familiar with crew members requesting EVA access, particularly when there is an absence of credible threats on the station. While it is your decision to grant or deny access unless overriden by your superior(s), you should be aware of an abundance of spacesuits on this station. While intended for emergencies, these suits are cheaper to replace. You may find it beneficial to direct aspiring 'space explorers' towards finding one of these suits instead, although it is advised you order replacement suits in advance through the cargo department. \n \nGenerated by Organic Resources Bot #2053"
/obj/item/paper/fluff/cogstation/chemists
name = "Re: Scientists?!"
@@ -214,9 +216,9 @@ Accidental Reentry:
name = "ROUTER STATUS: LIMITED"
info = "Currently, this router cannot receive deliveries from the Airbridge, MedSci, Security, or Service Routers. Cargo and the recycler are the only points currently accepting deliveries from here, although manual input from the routing depot is currently required. \n \n-C. Donnelly \n \nArchitectural Analyst"
-/obj/item/paper/fluff/cogstation/router_cargo
- name = "ROUTER STATUS: VERY LIMITED"
- info = "Currently, this router cannot receive deliveries from the Airbridge, MedSci, Security, or Service Routers. It is not yet capable of making deliveries, beyond sending items to the recycler. \n \n-C. Donnelly \n \nArchitectural Analyst"
+/obj/item/paper/fluff/cogstation/mulebot
+ name = "MEMO: MULEbots"
+ info = "As you may know, MULEbots have been coded to minimize travel distance for maximum efficiency. In the case of this station, that may include travelling through depressurized areas exposed to space. Please bear this in mind before using them to transport living tissue. \n \nGenerated by Organic Resources Bot #2053"
/////////// CentCom
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/spells/spell_types/emplosion.dm b/code/modules/spells/spell_types/emplosion.dm
index 98723273ae..91706d843e 100644
--- a/code/modules/spells/spell_types/emplosion.dm
+++ b/code/modules/spells/spell_types/emplosion.dm
@@ -3,9 +3,7 @@
desc = "This spell emplodes an area."
charge_max = 250
cooldown_min = 50
-
- var/emp_heavy = 2
- var/emp_light = 3
+ var/emp_range = 4 //same as a 50/50 reaction of uranium and iron
action_icon_state = "emp"
sound = 'sound/weapons/zapbang.ogg'
@@ -15,6 +13,6 @@
for(var/mob/living/target in targets)
if(target.anti_magic_check())
continue
- empulse(target.loc, emp_heavy, emp_light)
+ empulse_using_range(target.loc, emp_range)
- return
\ No newline at end of file
+ return
diff --git a/code/modules/spells/spell_types/taeclowndo.dm b/code/modules/spells/spell_types/taeclowndo.dm
index 59826daf07..9d6d71a89f 100644
--- a/code/modules/spells/spell_types/taeclowndo.dm
+++ b/code/modules/spells/spell_types/taeclowndo.dm
@@ -42,6 +42,8 @@
return
. = ..()
+ if(!.)
+ return
new /obj/item/grown/bananapeel(target)
/obj/effect/proc_holder/spell/aimed/banana_peel/update_icon()
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 14f359ef81..0be8cb9196 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -96,8 +96,7 @@
include_user = 1
cooldown_min = 200 //50 deciseconds reduction per rank
- emp_heavy = 6
- emp_light = 10
+ range = 14
sound = 'sound/magic/disable_tech.ogg'
/obj/effect/proc_holder/spell/targeted/turf_teleport/blink
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/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 3d88282937..90f2185ce6 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -71,6 +71,8 @@
var/medium_burn_msg = "blistered"
var/heavy_burn_msg = "peeling away"
+ var/render_like_organic = FALSE // forces limb to render as if it were an organic limb
+
/// The wounds currently afflicting this body part
var/list/wounds
@@ -355,7 +357,7 @@
else
damage = min(damage, WOUND_MAX_CONSIDERED_DAMAGE)
- var/base_roll = rand(max(damage/1.5,25), round(damage ** WOUND_DAMAGE_EXPONENT))
+ var/base_roll = rand(max(damage/1.5,25), round(damage ** CONFIG_GET(number/wound_exponent))) + (get_damage()*CONFIG_GET(number/wound_damage_multiplier))
var/injury_roll = base_roll
injury_roll += check_woundings_mods(woundtype, damage, wound_bonus, bare_wound_bonus)
var/list/wounds_checking = GLOB.global_wound_types[woundtype]
@@ -555,7 +557,7 @@
if(status == BODYPART_ORGANIC)
icon = base_bp_icon || DEFAULT_BODYPART_ICON_ORGANIC
else if(status == BODYPART_ROBOTIC)
- icon = DEFAULT_BODYPART_ICON_ROBOTIC
+ icon = base_bp_icon || DEFAULT_BODYPART_ICON_ROBOTIC
if(owner)
owner.updatehealth()
@@ -579,7 +581,7 @@
C = owner
no_update = FALSE
- if(HAS_TRAIT(C, TRAIT_HUSK) && is_organic_limb())
+ if(HAS_TRAIT(C, TRAIT_HUSK) && (is_organic_limb() || render_like_organic))
species_id = "husk" //overrides species_id
dmg_overlay_type = "" //no damage overlay shown when husked
should_draw_gender = FALSE
@@ -598,7 +600,7 @@
var/datum/species/S = H.dna.species
base_bp_icon = S?.icon_limbs || DEFAULT_BODYPART_ICON
- species_id = S.limbs_id
+ species_id = S.mutant_bodyparts["limbs_id"]
species_flags_list = H.dna.species.species_traits
//body marking memes
@@ -613,12 +615,14 @@
if(S.use_skintones)
skin_tone = H.skin_tone
- base_bp_icon = (base_bp_icon == DEFAULT_BODYPART_ICON) ? DEFAULT_BODYPART_ICON_ORGANIC : base_bp_icon
else
skin_tone = ""
body_gender = H.dna.features["body_model"]
- should_draw_gender = S.sexes
+ if(GLOB.nongendered_limb_types[species_id])
+ should_draw_gender = FALSE
+ else
+ should_draw_gender = S.sexes
var/mut_colors = (MUTCOLORS in S.species_traits)
if(mut_colors)
@@ -626,13 +630,9 @@
species_color = S.fixed_mut_color
else
species_color = H.dna.features["mcolor"]
- base_bp_icon = (base_bp_icon == DEFAULT_BODYPART_ICON) ? DEFAULT_BODYPART_ICON_ORGANIC : base_bp_icon
else
species_color = ""
- if(base_bp_icon != DEFAULT_BODYPART_ICON)
- color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE)
-
if(S.mutant_bodyparts["legs"])
if(body_zone == BODY_ZONE_L_LEG || body_zone == BODY_ZONE_R_LEG)
if(DIGITIGRADE in S.species_traits)
@@ -652,11 +652,16 @@
body_markings = "plain"
aux_marking = "plain"
markings_color = list(colorlist)
-
else
body_markings = null
aux_marking = null
+ if(species_id in GLOB.greyscale_limb_types) //should they have greyscales?
+ base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC
+
+ if(base_bp_icon != DEFAULT_BODYPART_ICON)
+ color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE)
+
if(!dropping_limb && H.dna.check_mutation(HULK))
mutation_color = "00aa00"
else
@@ -669,8 +674,9 @@
if(status == BODYPART_ROBOTIC)
dmg_overlay_type = "robotic"
- body_markings = null
- aux_marking = null
+ if(!render_like_organic)
+ body_markings = null
+ aux_marking = null
if(dropping_limb)
no_update = TRUE //when attached, the limb won't be affected by the appearance changes of its mob owner.
@@ -736,7 +742,7 @@
if((body_zone != BODY_ZONE_HEAD && body_zone != BODY_ZONE_CHEST))
should_draw_gender = FALSE
- if(is_organic_limb())
+ if(is_organic_limb() || render_like_organic)
limb.icon = base_bp_icon || 'icons/mob/human_parts.dmi'
if(should_draw_gender)
limb.icon_state = "[species_id]_[body_zone]_[icon_gender]"
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index fa003e3f3c..3038e32733 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -434,5 +434,8 @@
var/datum/scar/scaries = new
var/datum/wound/loss/phantom_loss = new // stolen valor, really
scaries.generate(L, phantom_loss)
+ if(HAS_TRAIT(src, ROBOTIC_LIMBS)) //Snowflake trait moment, but needed.
+ L.render_like_organic = TRUE
+ L.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) //Haha what if IPC-lings actually regenerated the right limbs instead of organic ones? That'd be pretty cool, right?
L.attach_limb(src, 1)
return TRUE
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 13b1140527..b6a0a387e2 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -180,13 +180,17 @@
. += lips_overlay
// eyes
- var/image/eyes_overlay = image('icons/mob/human_face.dmi', "eyes", -BODY_LAYER, SOUTH)
- . += eyes_overlay
- if(!eyes)
- eyes_overlay.icon_state = "eyes_missing"
-
- else if(eyes.eye_color)
- eyes_overlay.color = "#" + eyes.eye_color
+ if(eyes)
+ var/image/left_eye = image('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER, SOUTH)
+ var/image/right_eye = image('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER, SOUTH)
+ if(eyes.left_eye_color && eyes.right_eye_color)
+ left_eye.color = "#" + eyes.left_eye_color
+ right_eye.color = "#" + eyes.right_eye_color
+ . += left_eye
+ . += right_eye
+ else
+ var/eyes_overlay = image('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER, SOUTH)
+ . += eyes_overlay
/obj/item/bodypart/head/monkey
icon = 'icons/mob/animal_parts.dmi'
diff --git a/code/modules/surgery/brain_surgery.dm b/code/modules/surgery/brain_surgery.dm
index afaa66dae9..239bd8a98d 100644
--- a/code/modules/surgery/brain_surgery.dm
+++ b/code/modules/surgery/brain_surgery.dm
@@ -10,7 +10,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_HEAD)
- requires_bodypart_type = 0
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery_step/fix_brain
name = "fix brain"
implements = list(TOOL_HEMOSTAT = 85, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) //don't worry, pouring some alcohol on their open brain will get that chance to 100
diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm
index d1d478ddfe..69f5062032 100644
--- a/code/modules/surgery/coronary_bypass.dm
+++ b/code/modules/surgery/coronary_bypass.dm
@@ -3,6 +3,7 @@
steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, /datum/surgery_step/clamp_bleeders,
/datum/surgery_step/incise_heart, /datum/surgery_step/coronary_bypass, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery/coronary_bypass/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/heart/H = target.getorganslot(ORGAN_SLOT_HEART)
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index 87e6d096b3..57803f7461 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -2,6 +2,7 @@
name = "dental implant"
steps = list(/datum/surgery_step/drill, /datum/surgery_step/insert_pill)
possible_locs = list(BODY_ZONE_PRECISE_MOUTH)
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery_step/insert_pill
name = "insert pill"
diff --git a/code/modules/surgery/embalming.dm b/code/modules/surgery/embalming.dm
index c6d2c72caa..a4494c606a 100644
--- a/code/modules/surgery/embalming.dm
+++ b/code/modules/surgery/embalming.dm
@@ -7,7 +7,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_CHEST)
- requires_bodypart_type = 0
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery_step/embalming
name = "embalming body"
diff --git a/code/modules/surgery/emergency_cardioversion_recovery.dm b/code/modules/surgery/emergency_cardioversion_recovery.dm
index 5df90c5e80..2508bf135f 100644
--- a/code/modules/surgery/emergency_cardioversion_recovery.dm
+++ b/code/modules/surgery/emergency_cardioversion_recovery.dm
@@ -3,6 +3,7 @@
steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, /datum/surgery_step/clamp_bleeders,
/datum/surgery_step/incise_heart, /datum/surgery_step/ventricular_electrotherapy, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery_step/ventricular_electrotherapy
name = "ventricular electrotherapy"
diff --git a/code/modules/surgery/eye_surgery.dm b/code/modules/surgery/eye_surgery.dm
index 006dbe2af1..c26fda4158 100644
--- a/code/modules/surgery/eye_surgery.dm
+++ b/code/modules/surgery/eye_surgery.dm
@@ -3,7 +3,8 @@
steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/fix_eyes, /datum/surgery_step/close)
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_PRECISE_EYES)
- requires_bodypart_type = 0
+ requires_bodypart_type = BODYPART_ORGANIC
+
//fix eyes
/datum/surgery_step/fix_eyes
name = "fix eyes"
diff --git a/code/modules/surgery/healing.dm b/code/modules/surgery/healing.dm
index 0680dda404..f5b23e6087 100644
--- a/code/modules/surgery/healing.dm
+++ b/code/modules/surgery/healing.dm
@@ -8,7 +8,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_CHEST)
- requires_bodypart_type = FALSE
+ requires_bodypart_type = BODYPART_ORGANIC
replaced_by = /datum/surgery
ignore_clothes = TRUE
var/healing_step_type
diff --git a/code/modules/surgery/lipoplasty.dm b/code/modules/surgery/lipoplasty.dm
index 5a0fd819f1..ecdc5477f1 100644
--- a/code/modules/surgery/lipoplasty.dm
+++ b/code/modules/surgery/lipoplasty.dm
@@ -2,10 +2,13 @@
name = "Lipoplasty"
steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/cut_fat, /datum/surgery_step/remove_fat, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
+ requires_bodypart_type = BODYPART_ORGANIC
+
/datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(HAS_TRAIT(target, TRAIT_FAT))
return 1
return 0
+
//cut fat
/datum/surgery_step/cut_fat
name = "cut excess fat"
diff --git a/code/modules/surgery/lobectomy.dm b/code/modules/surgery/lobectomy.dm
index 7ef7e4cd7f..34b40258c8 100644
--- a/code/modules/surgery/lobectomy.dm
+++ b/code/modules/surgery/lobectomy.dm
@@ -3,6 +3,7 @@
steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, /datum/surgery_step/clamp_bleeders,
/datum/surgery_step/lobectomy, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
+ requires_bodypart_type = BODYPART_ORGANIC
/datum/surgery/lobectomy/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/lungs/L = target.getorganslot(ORGAN_SLOT_LUNGS)
diff --git a/code/modules/surgery/mechanic_steps.dm b/code/modules/surgery/mechanic_steps.dm
index 9366e585c3..101be7f103 100644
--- a/code/modules/surgery/mechanic_steps.dm
+++ b/code/modules/surgery/mechanic_steps.dm
@@ -85,3 +85,107 @@
display_results(user, target, "You begin to open the hatch holders in [target]'s [parse_zone(target_zone)]...",
"[user] begins to open the hatch holders in [target]'s [parse_zone(target_zone)].",
"[user] begins to open the hatch holders in [target]'s [parse_zone(target_zone)].")
+
+//cut wires
+/datum/surgery_step/cut_wires
+ name = "cut wires"
+ implements = list(
+ TOOL_WIRECUTTER = 100,
+ TOOL_SCALPEL = 75,
+ /obj/item/kitchen/knife = 50,
+ /obj/item = 10) // 10% success with any sharp item.
+ time = 24
+
+/datum/surgery_step/cut_wires/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to cut loose wires in [target]'s [parse_zone(target_zone)]...",
+ "[user] begins to cut loose wires in [target]'s [parse_zone(target_zone)].",
+ "[user] begins to cut loose wires in [target]'s [parse_zone(target_zone)].")
+
+/datum/surgery_step/cut_wires/tool_check(mob/user, obj/item/tool)
+ if(implement_type == /obj/item && !tool.get_sharpness())
+ return FALSE
+ return TRUE
+
+//pry off plating
+/datum/surgery_step/pry_off_plating
+ name = "pry off plating"
+ implements = list(
+ TOOL_CROWBAR = 100,
+ TOOL_HEMOSTAT = 10)
+ time = 24
+
+/datum/surgery_step/pry_off_plating/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ do_sparks(rand(5, 9), FALSE, target.loc)
+ return TRUE
+
+/datum/surgery_step/pry_off_plating/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to pry off [target]'s [parse_zone(target_zone)] plating...",
+ "[user] begins to pry off [target]'s [parse_zone(target_zone)] plating.",
+ "[user] begins to pry off [target]'s [parse_zone(target_zone)] plating.")
+
+//weld plating
+/datum/surgery_step/weld_plating
+ name = "weld plating"
+ implements = list(
+ TOOL_WELDER = 100)
+ time = 24
+
+/datum/surgery_step/weld_plating/tool_check(mob/user, obj/item/tool)
+ if(implement_type == TOOL_WELDER && !tool.use_tool(user, user, 0, volume=50, amount=1))
+ return FALSE
+ return TRUE
+
+/datum/surgery_step/weld_plating/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to weld [target]'s [parse_zone(target_zone)] plating...",
+ "[user] begins to weld [target]'s [parse_zone(target_zone)] plating.",
+ "[user] begins to weld [target]'s [parse_zone(target_zone)] plating.")
+
+//replace wires
+/datum/surgery_step/replace_wires
+ name = "replace wires"
+ implements = list(/obj/item/stack/cable_coil = 100)
+ time = 24
+ var/cableamount = 5
+
+/datum/surgery_step/replace_wires/tool_check(mob/user, obj/item/tool)
+ var/obj/item/stack/cable_coil/coil = tool
+ if(coil.get_amount() < cableamount)
+ to_chat(user, "Not enough cable!")
+ return FALSE
+ return TRUE
+
+/datum/surgery_step/replace_wires/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/stack/cable_coil/coil = tool
+ if(coil && !(coil.get_amount()You begin to replace [target]'s [parse_zone(target_zone)] wiring...",
+ "[user] begins to replace [target]'s [parse_zone(target_zone)] wiring.",
+ "[user] begins to replace [target]'s [parse_zone(target_zone)] wiring.")
+
+//add plating
+/datum/surgery_step/add_plating
+ name = "add plating"
+ implements = list(/obj/item/stack/sheet/metal = 100)
+ time = 24
+ var/metalamount = 5
+
+/datum/surgery_step/add_plating/tool_check(mob/user, obj/item/tool)
+ var/obj/item/stack/sheet/metal/plat = tool
+ if(plat.get_amount() < metalamount)
+ to_chat(user, "Not enough metal!")
+ return FALSE
+ return TRUE
+
+/datum/surgery_step/add_plating/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/stack/sheet/metal/plat = tool
+ if(plat && !(plat.get_amount()You begin to add plating to [target]'s [parse_zone(target_zone)]...",
+ "[user] begins to add plating to [target]'s [parse_zone(target_zone)].",
+ "[user] begins to add plating to [target]'s [parse_zone(target_zone)].")
\ No newline at end of file
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index 4ce51ae119..85b400d2da 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -2,6 +2,7 @@
name = "organ manipulation"
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
+ requires_bodypart_type = BODYPART_ORGANIC
requires_real_bodypart = 1
steps = list(
/datum/surgery_step/incise,
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index b42770723b..e3c32ad009 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -306,3 +306,69 @@
var/obj/item/assembly/flash/armimplant/F = new(src)
items_list += F
F.I = src
+
+/////////////////
+
+
+//IPC/Synth Arm//
+
+
+/////////////////
+
+/obj/item/organ/cyberimp/arm/power_cord
+ name = "power cord implant"
+ desc = "An internal power cord hooked up to a battery. Useful if you run on volts."
+ contents = newlist(/obj/item/apc_powercord)
+ zone = "l_arm"
+
+/obj/item/apc_powercord
+ name = "power cord"
+ desc = "An internal power cord hooked up to a battery. Useful if you run on electricity. Not so much otherwise."
+ icon = 'icons/obj/power.dmi'
+ icon_state = "wire1"
+
+/obj/item/apc_powercord/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ if(!istype(target, /obj/machinery/power/apc) || !ishuman(user) || !proximity_flag)
+ return ..()
+ user.DelayNextAction(CLICK_CD_MELEE)
+ var/obj/machinery/power/apc/A = target
+ var/mob/living/carbon/human/H = user
+ var/obj/item/organ/stomach/ipc/cell = locate(/obj/item/organ/stomach/ipc) in H.internal_organs
+ if(!cell)
+ to_chat(H, "You try to siphon energy from the [A], but your power cell is gone!")
+ return
+
+ if(A.cell && A.cell.charge > 0)
+ if(H.nutrition >= NUTRITION_LEVEL_WELL_FED)
+ to_chat(user, "You are already fully charged!")
+ return
+ else
+ powerdraw_loop(A, H)
+ return
+
+ to_chat(user, "There is no charge to draw from that APC.")
+
+/obj/item/apc_powercord/proc/powerdraw_loop(obj/machinery/power/apc/A, mob/living/carbon/human/H)
+ H.visible_message("[H] inserts a power connector into the [A].", "You begin to draw power from the [A].")
+ while(do_after(H, 10, target = A))
+ if(loc != H)
+ to_chat(H, "You must keep your connector out while charging!")
+ break
+ if(A.cell.charge == 0)
+ to_chat(H, "The [A] doesn't have enough charge to spare.")
+ break
+ A.charging = 1
+ if(A.cell.charge >= 500)
+ do_sparks(1, FALSE, A)
+ H.nutrition += 50
+ A.cell.charge -= 150
+ to_chat(H, "You siphon off some of the stored charge for your own use.")
+ else
+ H.nutrition += A.cell.charge/10
+ A.cell.charge = 0
+ to_chat(H, "You siphon off as much as the [A] can spare.")
+ break
+ if(H.nutrition > NUTRITION_LEVEL_WELL_FED)
+ to_chat(H, "You are now fully charged.")
+ break
+ H.visible_message("[H] unplugs from the [A].", "You unplug from the [A].")
diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm
index fcc3f71b8b..ea8c188346 100644
--- a/code/modules/surgery/organs/augments_chest.dm
+++ b/code/modules/surgery/organs/augments_chest.dm
@@ -33,10 +33,9 @@
. = ..()
if(!owner || . & EMP_PROTECT_SELF)
return
- owner.reagents.add_reagent(/datum/reagent/toxin/bad_food, poison_amount / severity)
+ owner.reagents.add_reagent(/datum/reagent/toxin/bad_food, poison_amount * severity/100)
to_chat(owner, "You feel like your insides are burning.")
-
/obj/item/organ/cyberimp/chest/nutriment/plus
name = "Nutriment pump implant PLUS"
desc = "This implant will synthesize and pump into your bloodstream a small amount of nutriment when you are hungry."
@@ -115,10 +114,10 @@
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- if(H.stat != DEAD && prob(50 / severity) && H.can_heartattack())
+ if(H.stat != DEAD && prob(severity/2) && H.can_heartattack())
H.set_heartattack(TRUE)
to_chat(H, "You feel a horrible agony in your chest!")
- addtimer(CALLBACK(src, .proc/undo_heart_attack), 60 SECONDS / severity)
+ addtimer(CALLBACK(src, .proc/undo_heart_attack), (60 * severity/100) SECONDS)
/obj/item/organ/cyberimp/chest/reviver/proc/undo_heart_attack()
var/mob/living/carbon/human/H = owner
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index 6705b4c7b4..5861f0e48b 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -34,11 +34,10 @@
. = ..()
if(!owner || . & EMP_PROTECT_SELF)
return
- var/stun_amount = 200/severity
+ var/stun_amount = 2*severity
owner.Stun(stun_amount)
to_chat(owner, "Your body seizes up!")
-
/obj/item/organ/cyberimp/brain/anti_drop
name = "anti-drop implant"
desc = "This cybernetic brain implant will allow you to force your hand muscles to contract, preventing item dropping. Twitch ear to toggle."
@@ -68,12 +67,11 @@
release_items()
to_chat(owner, "Your hands relax...")
-
/obj/item/organ/cyberimp/brain/anti_drop/emp_act(severity)
. = ..()
if(!owner || . & EMP_PROTECT_SELF)
return
- var/range = severity ? 10 : 5
+ var/range = severity/10
var/atom/A
if(active)
release_items()
@@ -114,7 +112,7 @@
return
crit_fail = TRUE
organ_flags |= ORGAN_FAILING
- addtimer(CALLBACK(src, .proc/reboot), 90 / severity)
+ addtimer(CALLBACK(src, .proc/reboot), 0.9 * severity)
/obj/item/organ/cyberimp/brain/anti_stun/proc/reboot()
crit_fail = FALSE
@@ -136,6 +134,6 @@
. = ..()
if(!owner || . & EMP_PROTECT_SELF)
return
- if(prob(60/severity))
+ if(prob(0.6*severity))
to_chat(owner, "Your breathing tube suddenly closes!")
owner.losebreath += 2
diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm
index c1e33c7dac..3787d4e781 100644
--- a/code/modules/surgery/organs/ears.dm
+++ b/code/modules/surgery/organs/ears.dm
@@ -131,4 +131,31 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- damage += 40/severity
+ damage += 0.15 * severity
+
+/obj/item/organ/ears/ipc
+ name = "auditory sensors"
+ icon_state = "ears-c"
+ desc = "A pair of microphones intended to be installed in an IPC head, that grant the ability to hear."
+ zone = BODY_ZONE_HEAD
+ slot = ORGAN_SLOT_EARS
+ gender = PLURAL
+ organ_flags = ORGAN_SYNTHETIC
+
+/obj/item/organ/ears/ipc/emp_act(severity)
+ . = ..()
+ if(!owner || . & EMP_PROTECT_SELF)
+ return
+ to_chat(owner, "Alert: Auditory systems corrupted!.")
+ switch(severity)
+ if(1)
+ owner.Jitter(30)
+ owner.Dizzy(30)
+ owner.DefaultCombatKnockdown(80)
+ deaf = 30
+
+ if(2)
+ owner.Jitter(15)
+ owner.Dizzy(15)
+ owner.DefaultCombatKnockdown(40)
+ damage += 0.15 * severity
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 049fc5b5d4..efceefc131 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -26,8 +26,10 @@
var/sight_flags = 0
var/see_in_dark = 2
var/tint = 0
- var/eye_color = "" //set to a hex code to override a mob's eye color
- var/old_eye_color = "fff"
+ var/left_eye_color = "" //set to a hex code to override a mob's eye color
+ var/right_eye_color = ""
+ var/old_left_eye_color = "fff"
+ var/old_right_eye_color = "fff"
var/flash_protect = 0
var/see_invisible = SEE_INVISIBLE_LIVING
var/lighting_alpha
@@ -44,11 +46,19 @@
owner.become_blind(EYE_DAMAGE)
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- old_eye_color = H.eye_color
- if(eye_color)
- H.eye_color = eye_color
+ old_left_eye_color = H.left_eye_color
+ old_right_eye_color = H.right_eye_color
+
+ if(left_eye_color)
+ H.left_eye_color = left_eye_color
else
- eye_color = H.eye_color
+ left_eye_color = H.left_eye_color
+
+ if(right_eye_color)
+ H.right_eye_color = right_eye_color
+ else
+ right_eye_color = H.right_eye_color
+
if(!special)
H.dna?.species?.handle_body(H) //regenerate eyeballs overlays.
M.update_tint()
@@ -64,9 +74,10 @@
C.clear_fullscreen("eye_damage")
if(BLIND_VISION_THREE)
C.cure_blind(EYE_DAMAGE)
- if(ishuman(C) && eye_color)
+ if(ishuman(C) && left_eye_color && right_eye_color)
var/mob/living/carbon/human/H = C
- H.eye_color = old_eye_color
+ H.left_eye_color = old_left_eye_color
+ H.right_eye_color = old_right_eye_color
if(!special)
H.dna.species.handle_body(H)
if(!special)
@@ -155,21 +166,23 @@
return
to_chat(owner, "Static obfuscates your vision!")
owner.flash_act(visual = 1)
- if(severity == EMP_HEAVY)
+ if(severity >= 70)
owner.adjustOrganLoss(ORGAN_SLOT_EYES, 20)
/obj/item/organ/eyes/robotic/xray
name = "\improper X-ray eyes"
desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile."
- eye_color = "000"
+ left_eye_color = "000"
+ right_eye_color = "000"
see_in_dark = 8
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
/obj/item/organ/eyes/robotic/thermals
name = "thermal eyes"
desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
- eye_color = "FC0"
+ left_eye_color = "FC0"
+ right_eye_color = "FC0"
sight_flags = SEE_MOBS
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
flash_protect = -1
@@ -178,7 +191,8 @@
/obj/item/organ/eyes/robotic/flashlight
name = "flashlight eyes"
desc = "It's two flashlights rigged together with some wire. Why would you put these in someone's head?"
- eye_color ="fee5a3"
+ left_eye_color ="fee5a3"
+ right_eye_color ="fee5a3"
icon = 'icons/obj/lighting.dmi'
icon_state = "flashlight_eyes"
flash_protect = 2
@@ -219,7 +233,8 @@
/obj/item/organ/eyes/robotic/glow
name = "High Luminosity Eyes"
desc = "Special glowing eyes, used by snowflakes who want to be special."
- eye_color = "000"
+ left_eye_color = "000"
+ right_eye_color = "000"
actions_types = list(/datum/action/item_action/organ_action/use, /datum/action/item_action/organ_action/toggle)
var/current_color_string = "#ffffff"
var/active = FALSE
@@ -272,14 +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
- eye_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
@@ -394,6 +421,15 @@
name = "ipc eyes"
icon_state = "cybernetic_eyeballs"
+/obj/item/organ/eyes/ipc/emp_act(severity)
+ . = ..()
+ if(!owner || . & EMP_PROTECT_SELF)
+ return
+ to_chat(owner, "Alert: Perception visuals damaged!")
+ owner.flash_act(visual = 1)
+ if(severity >= 70)
+ owner.adjustOrganLoss(ORGAN_SLOT_EYES, 20)
+
#undef BLURRY_VISION_ONE
#undef BLURRY_VISION_TWO
-#undef BLIND_VISION_THREE
\ No newline at end of file
+#undef BLIND_VISION_THREE
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index e251abfd35..25b7e8fa48 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -198,8 +198,8 @@ obj/item/organ/heart/slime
if(. & EMP_PROTECT_SELF)
return
Stop()
- addtimer(CALLBACK(src, .proc/Restart), 20/severity SECONDS)
- damage += 100/severity
+ addtimer(CALLBACK(src, .proc/Restart), 0.2*severity SECONDS)
+ damage += severity
/obj/item/organ/heart/cybernetic/upgraded
name = "upgraded cybernetic heart"
diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm
index f0f98a5fa5..69633c54df 100755
--- a/code/modules/surgery/organs/liver.dm
+++ b/code/modules/surgery/organs/liver.dm
@@ -112,8 +112,4 @@
. = ..()
if(. & EMP_PROTECT_SELF)
return
- switch(severity)
- if(1)
- damage+=100
- if(2)
- damage+=50
+ damage += severity
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index e34fd8e8a9..953c8b2c1b 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -36,6 +36,10 @@
var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
var/safe_toxins_min = 0
var/safe_toxins_max = MOLES_GAS_VISIBLE
+ var/safe_ch3br_min = 0
+ var/safe_ch3br_max = 1 //problematic even at low concentrations
+ var/safe_methane_min = 0
+ var/safe_methane_max = 0
var/SA_para_min = 1 //Sleeping agent
var/SA_sleep_min = 5 //Sleeping agent
var/BZ_trip_balls_min = 1 //BZ gas
@@ -53,6 +57,9 @@
var/tox_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/tox_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/tox_damage_type = TOX
+ var/methane_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
+ var/methane_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
+ var/methane_damage_type = OXY
var/cold_message = "your face freezing and an icicle forming"
var/cold_level_1_threshold = 260
@@ -129,6 +136,8 @@
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(safe_nitro_min)
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
+ else if(safe_ch3br_min)
+ H.throw_alert("not_enough_ch3br", /obj/screen/alert/not_enough_ch3br)
return FALSE
var/gas_breathed = 0
@@ -138,6 +147,8 @@
var/N2_pp = breath.get_breath_partial_pressure(breath.get_moles(/datum/gas/nitrogen))
var/Toxins_pp = breath.get_breath_partial_pressure(breath.get_moles(/datum/gas/plasma))
var/CO2_pp = breath.get_breath_partial_pressure(breath.get_moles(/datum/gas/carbon_dioxide))
+ var/CH4_pp = breath.get_breath_partial_pressure(breath.get_moles(/datum/gas/methane))
+ var/CH3Br_pp = breath.get_breath_partial_pressure(breath.get_moles(/datum/gas/methyl_bromide))
//-- OXY --//
@@ -278,6 +289,58 @@
breath.adjust_moles(/datum/gas/carbon_dioxide, gas_breathed)
gas_breathed = 0
+//-- METHANE --//
+
+ //Too much methane!
+ if(safe_methane_max)
+ if(CH4_pp > safe_methane_max) //Same effect as excess nitrogen, generally nontoxic
+ var/ratio = (breath.get_moles(/datum/gas/methane)/safe_methane_max) * 10
+ H.apply_damage_type(clamp(ratio, methane_breath_dam_min, methane_breath_dam_max), methane_damage_type)
+ H.throw_alert("too_much_ch4", /obj/screen/alert/too_much_ch4)
+ H.losebreath += 2
+ else
+ H.clear_alert("too_much_ch4")
+ //Too little methane!
+ if(safe_methane_min)
+ if(CH4_pp < safe_methane_min)
+ gas_breathed = handle_too_little_breath(H, CH4_pp, safe_methane_min, breath.get_moles(/datum/gas/methane))
+ H.throw_alert("not_enough_ch4", /obj/screen/alert/not_enough_ch4)
+ else
+ H.failed_last_breath = FALSE
+ if(H.health >= H.crit_threshold)
+ H.adjustOxyLoss(-breathModifier)
+ gas_breathed = breath.get_moles(/datum/gas/methane)
+ H.clear_alert("not_enough_ch4")
+
+ //Exhale
+ breath.adjust_moles(/datum/gas/methane, -gas_breathed)
+ breath.adjust_moles(/datum/gas/methyl_bromide, gas_breathed)
+ gas_breathed = 0
+
+//-- CH3BR --//
+
+ //Too much methyl bromide!
+ if(safe_ch3br_max)
+ if(CH3Br_pp > safe_ch3br_max)
+ if(prob(CH3Br_pp/0.5))
+ H.adjustOrganLoss(ORGAN_SLOT_LUNGS, 3, 150) //Inhaling this is a bad idea
+ if(prob(CH3Br_pp/2))
+ to_chat(H, "Your throat closes up!")
+ H.silent = max(H.silent, 3)
+ H.throw_alert("too_much_ch3br", /obj/screen/alert/too_much_ch3br)
+ else
+ H.clear_alert("too_much_ch3br")
+ //Too little methyl bromide!
+ if(safe_ch3br_min)
+ if(CH3Br_pp < safe_ch3br_min)
+ gas_breathed = handle_too_little_breath(H, CH3Br_pp, safe_ch3br_min, breath.get_moles(/datum/gas/methyl_bromide))
+ H.throw_alert("not_enough_ch3br", /obj/screen/alert/not_enough_ch3br)
+ else
+ H.failed_last_breath = FALSE
+ if(H.health >= H.crit_threshold)
+ H.adjustOxyLoss(-breathModifier)
+ gas_breathed = breath.get_moles(/datum/gas/methyl_bromide)
+ H.clear_alert("not_enough_ch3br")
//-- TRACES --//
@@ -458,9 +521,20 @@
failed = FALSE
/obj/item/organ/lungs/ipc
- name = "ipc lungs"
+ name = "ipc cooling system"
icon_state = "lungs-c"
+/obj/item/organ/lungs/ipc/emp_act(severity) //Should probably put it somewhere else later
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ to_chat(owner, "Alert: Critical cooling system failure!")
+ switch(severity)
+ if(1)
+ owner.adjust_bodytemperature(100*TEMPERATURE_DAMAGE_COEFFICIENT)
+ if(2)
+ owner.adjust_bodytemperature(30*TEMPERATURE_DAMAGE_COEFFICIENT)
+
/obj/item/organ/lungs/plasmaman
name = "plasma filter"
desc = "A spongy rib-shaped mass for filtering plasma from the air."
@@ -487,7 +561,6 @@
owner.losebreath = 20
owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 25)
-
/obj/item/organ/lungs/cybernetic/upgraded
name = "upgraded cybernetic lungs"
desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon dioxide."
diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm
old mode 100755
new mode 100644
index 44b4f6362a..cabe49db25
--- a/code/modules/surgery/organs/stomach.dm
+++ b/code/modules/surgery/organs/stomach.dm
@@ -89,9 +89,20 @@
desc = "A strange crystal that is responsible for metabolizing the unseen energy force that feeds plasmamen."
/obj/item/organ/stomach/ipc
- name = "ipc stomach"
+ name = "ipc cell"
icon_state = "stomach-ipc"
+/obj/item/organ/stomach/ipc/emp_act(severity)
+ . = ..()
+ if(!owner || . & EMP_PROTECT_SELF)
+ return
+ switch(severity)
+ if(1)
+ owner.nutrition = min(owner.nutrition - 50, 0)
+ to_chat(owner, "Alert: Detected severe battery discharge!")
+ if(2)
+ owner.nutrition = min(owner.nutrition - 100, 0)
+ to_chat(owner, "Alert: Minor battery discharge!")
/obj/item/organ/stomach/ethereal
name = "biological battery"
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/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm
index c4e056583c..d3597ba516 100644
--- a/code/modules/surgery/plastic_surgery.dm
+++ b/code/modules/surgery/plastic_surgery.dm
@@ -2,6 +2,8 @@
name = "Plastic surgery"
steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/reshape_face, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_HEAD)
+ requires_bodypart_type = BODYPART_ORGANIC
+
//reshape_face
/datum/surgery_step/reshape_face
name = "reshape face"
diff --git a/code/modules/surgery/robot_brain_surgery.dm b/code/modules/surgery/robot_brain_surgery.dm
new file mode 100644
index 0000000000..5feca7382f
--- /dev/null
+++ b/code/modules/surgery/robot_brain_surgery.dm
@@ -0,0 +1,51 @@
+/datum/surgery/robot_brain_surgery
+ name = "Reset posibrain logic (Brain surgery)"
+ steps = list(
+ /datum/surgery_step/mechanic_open,
+ /datum/surgery_step/mechanic_unwrench,
+ /datum/surgery_step/pry_off_plating,
+ /datum/surgery_step/prepare_electronics,
+ /datum/surgery_step/fix_robot_brain,
+ /datum/surgery_step/mechanic_close)
+
+ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
+ possible_locs = list(BODY_ZONE_HEAD)
+ requires_bodypart_type = BODYPART_ROBOTIC
+ desc = "A surgical procedure that restores the default behavior logic and personality matrix of an IPC posibrain."
+
+/datum/surgery_step/fix_robot_brain
+ name = "fix posibrain (multitool)"
+ implements = list(TOOL_MULTITOOL = 100, TOOL_HEMOSTAT = 35, TOOL_SCREWDRIVER = 15)
+ time = 120 //long and complicated
+
+/datum/surgery/robot_brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
+ var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
+ if(!B)
+ return FALSE
+ return TRUE
+
+/datum/surgery_step/fix_robot_brain/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to fix [target]'s posibrain...",
+ "[user] begins to fix [target]'s posibrain.",
+ "[user] begins to perform surgery on [target]'s posibrain.")
+
+/datum/surgery_step/fix_robot_brain/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You succeed in fixing [target]'s posibrain.",
+ "[user] successfully fixes [target]'s posibrain!",
+ "[user] completes the surgery on [target]'s posibrain.")
+ if(target.mind && target.mind.has_antag_datum(/datum/antagonist/brainwashed))
+ target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
+ target.setOrganLoss(ORGAN_SLOT_BRAIN, target.getOrganLoss(ORGAN_SLOT_BRAIN) - 60) //we set damage in this case in order to clear the "failing" flag
+ target.cure_all_traumas(TRAUMA_RESILIENCE_LOBOTOMY) //Lobotomy tier fix cause you can't clone this!
+ return TRUE
+
+/datum/surgery_step/fix_robot_brain/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(target.getorganslot(ORGAN_SLOT_BRAIN))
+ display_results(user, target, "You screw up, causing more damage!",
+ "[user] screws up, causing damage to the circuits!",
+ "[user] completes the surgery on [target]'s posibrain.")
+ target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60)
+ target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
+ else
+ user.visible_message("[user] suddenly notices that the posibrain [user.p_they()] [user.p_were()] working on is not there anymore.", "You suddenly notice that the posibrain you were working on is not there anymore.")
+ return FALSE
diff --git a/code/modules/surgery/robot_healing.dm b/code/modules/surgery/robot_healing.dm
new file mode 100644
index 0000000000..6355410a79
--- /dev/null
+++ b/code/modules/surgery/robot_healing.dm
@@ -0,0 +1,125 @@
+//Almost copypaste of tend wounds, with some changes
+
+/datum/surgery/robot_healing
+ name = "Repair robotic limbs (basic)"
+ desc = "A surgical procedure that provides repairs and maintenance to robotic limbs. Is slightly more efficient when the patient is severely damaged."
+
+ steps = list(/datum/surgery_step/mechanic_open,
+ /datum/surgery_step/pry_off_plating,
+ /datum/surgery_step/cut_wires,
+ /datum/surgery_step/robot_heal,
+ /datum/surgery_step/mechanic_close)
+
+ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
+ possible_locs = list(BODY_ZONE_CHEST)
+ requires_bodypart_type = BODYPART_ROBOTIC
+ ignore_clothes = TRUE
+ var/antispam = FALSE
+ var/healing_step_type = /datum/surgery_step/robot_heal/basic
+
+/datum/surgery/robot_healing/New(surgery_target, surgery_location, surgery_bodypart)
+ ..()
+ if(healing_step_type)
+ steps = list(/datum/surgery_step/mechanic_open,
+ /datum/surgery_step/pry_off_plating,
+ /datum/surgery_step/cut_wires,
+ healing_step_type,
+ /datum/surgery_step/mechanic_close)
+
+/datum/surgery_step/robot_heal
+ name = "repair body (welder/cable)"
+ implements = list(TOOL_WELDER = 100, /obj/item/stack/cable_coil = 100)
+ repeatable = TRUE
+ time = 15
+ var/healsbrute = FALSE
+ var/healsburn = FALSE
+ var/brutehealing = 0
+ var/burnhealing = 0
+ var/missinghpbonus = 0 //heals an extra point of damager per X missing damage of type (burn damage for burn healing, brute for brute). Smaller Number = More Healing!
+
+/datum/surgery_step/robot_heal/tool_check(mob/user, obj/item/tool)
+ if(implement_type == TOOL_WELDER && !tool.tool_use_check(user, 1))
+ return FALSE
+ return TRUE
+
+/datum/surgery_step/robot_heal/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/woundtype
+ if(implement_type == TOOL_WELDER)
+ healsbrute = TRUE
+ healsburn = FALSE
+ woundtype = "dents"
+ else
+ healsbrute = FALSE
+ healsburn = TRUE
+ woundtype = "wiring"
+
+ if(istype(surgery,/datum/surgery/robot_healing))
+ var/datum/surgery/robot_healing/the_surgery = surgery
+ if(!the_surgery.antispam)
+ display_results(user, target, "You attempt to fix some of [target]'s [woundtype].",
+ "[user] attempts to fix some of [target]'s [woundtype].",
+ "[user] attempts to fix some of [target]'s [woundtype].")
+
+/datum/surgery_step/robot_heal/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE)
+ if(..())
+ while((healsbrute && target.getBruteLoss() && tool.tool_use_check(user,1)) || (healsburn && target.getFireLoss() && tool))
+ if(!..())
+ break
+
+/datum/surgery_step/robot_heal/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/umsg = "You succeed in fixing some of [target]'s damage" //no period, add initial space to "addons"
+ var/tmsg = "[user] fixes some of [target]'s damage" //see above
+ var/urhealedamt_brute = 0
+ if(healsbrute)
+ urhealedamt_brute = brutehealing
+ tool.use_tool(target, user, 0, volume=50, amount=1)
+ var/urhealedamt_burn = 0
+ if(healsburn)
+ urhealedamt_burn = burnhealing
+ if(tool)
+ tool.use(1)
+ if(missinghpbonus)
+ if(target.stat != DEAD)
+ urhealedamt_brute += round((target.getBruteLoss()/ missinghpbonus),0.1)
+ urhealedamt_burn += round((target.getFireLoss()/ missinghpbonus),0.1)
+ else //less healing bonus for the dead since they're expected to have lots of damage to begin with (to make TW into defib not TOO simple)
+ urhealedamt_brute += round((target.getBruteLoss()/ (missinghpbonus*5)),0.1)
+ urhealedamt_burn += round((target.getFireLoss()/ (missinghpbonus*5)),0.1)
+ if(!get_location_accessible(target, target_zone))
+ urhealedamt_brute *= 0.55
+ urhealedamt_burn *= 0.55
+ umsg += " as best as you can while they have clothing on"
+ tmsg += " as best as they can while [target] has clothing on"
+ target.heal_bodypart_damage(urhealedamt_brute,urhealedamt_burn, only_organic = FALSE, only_robotic = TRUE)
+ display_results(user, target, "[umsg].",
+ "[tmsg].",
+ "[tmsg].")
+ if(istype(surgery, /datum/surgery/robot_healing))
+ var/datum/surgery/robot_healing/the_surgery = surgery
+ the_surgery.antispam = TRUE
+ return TRUE
+
+/datum/surgery_step/robot_heal/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You screwed up!",
+ "[user] screws up!",
+ "[user] fixes some of [target]'s damage.", TRUE)
+ var/urdamageamt_brute = 0
+ if(healsbrute)
+ urdamageamt_brute = brutehealing * 0.8
+ var/urdamageamt_burn = 0
+ if(healsburn)
+ urdamageamt_burn = burnhealing * 0.8
+ if(missinghpbonus)
+ urdamageamt_brute += round((target.getBruteLoss()/ (missinghpbonus*2)),0.1)
+ urdamageamt_burn += round((target.getFireLoss()/ (missinghpbonus*2)),0.1)
+
+ target.take_bodypart_damage(urdamageamt_brute, urdamageamt_burn)
+ return FALSE
+
+/***************************STEPS***************************/
+
+/datum/surgery_step/robot_heal/basic
+ name = "repair damage"
+ brutehealing = 10
+ burnhealing = 10
+ missinghpbonus = 15
\ No newline at end of file
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..8d978a630d
--- /dev/null
+++ b/code/modules/unit_tests/character_saving.dm
@@ -0,0 +1,18 @@
+/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.")
+ P.save_character()
+ P.load_character()
+ if(P.features["flavor_text"] != "Foo")
+ Fail("Repeated saving and loading possibly causing save deletion.")
+ 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 a43e3fc2ce..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)
@@ -177,6 +178,7 @@
organic host as a home base and source of fuel. Holoparasites come in various types and share damage with their host."
item = /obj/item/storage/box/syndie_kit/guardian
cost = 15
+ limited_stock = 1 // you can only have one holopara apparently?
refundable = TRUE
cant_discount = TRUE
surplus = 0
diff --git a/code/modules/uplink/uplink_items/uplink_explosives.dm b/code/modules/uplink/uplink_items/uplink_explosives.dm
index 6cc1c9fe05..f44966fb3b 100644
--- a/code/modules/uplink/uplink_items/uplink_explosives.dm
+++ b/code/modules/uplink/uplink_items/uplink_explosives.dm
@@ -80,6 +80,7 @@
item = /obj/item/cartridge/virus/syndicate
cost = 5
restricted = TRUE
+ limited_stock = 1
/datum/uplink_item/explosives/emp
name = "EMP Grenades and Implanter Kit"
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index a115300085..26bee38f7b 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -529,7 +529,7 @@ GLOBAL_LIST_EMPTY(vending_products)
if(5) // limb squish!
for(var/i in C.bodyparts)
var/obj/item/bodypart/squish_part = i
- if(squish_part.is_organic_limb())
+ if(squish_part.is_organic_limb() || squish_part.render_like_organic)
var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate))
squish_part.force_wound_upwards(type_wound)
else
diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm
index bc824cc994..14cdd30931 100644
--- a/code/modules/vending/autodrobe.dm
+++ b/code/modules/vending/autodrobe.dm
@@ -29,6 +29,16 @@
/obj/item/clothing/glasses/monocle =1,
/obj/item/clothing/head/bowler = 1,
/obj/item/cane = 1,
+ /obj/item/clothing/under/rank/civilian/victorian_redsleeves = 1,
+ /obj/item/clothing/under/rank/civilian/victorian_redvest = 1,
+ /obj/item/clothing/under/rank/civilian/victorian_vest = 1,
+ /obj/item/clothing/under/rank/civilian/victorian_purple = 1,
+ /obj/item/clothing/suit/tailcoat = 1,
+ /obj/item/clothing/under/rank/civilian/victorianreddress = 1,
+ /obj/item/clothing/suit/vickyred = 1,
+ /obj/item/clothing/under/rank/civilian/victorianblackdress = 1,
+ /obj/item/clothing/suit/vickyblack =1,
+ /obj/item/clothing/under/rank/civilian/dutch = 2,
/obj/item/clothing/under/suit/sl = 1,
/obj/item/clothing/mask/fakemoustache = 1,
/obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1,
diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm
index 7005a0b02f..bd821d80e3 100644
--- a/code/modules/vending/clothesmate.dm
+++ b/code/modules/vending/clothesmate.dm
@@ -21,6 +21,11 @@
/obj/item/clothing/suit/jacket/puffer/vest = 4,
/obj/item/clothing/suit/jacket/puffer = 4,
/obj/item/clothing/suit/hooded/cloak/david = 4,
+ /obj/item/clothing/suit/bomber = 5,
+ /obj/item/clothing/under/suit/turtle/teal = 3,
+ /obj/item/clothing/under/suit/turtle/grey = 3,
+ /obj/item/clothing/under/rank/civilian/util = 5,
+ /obj/item/clothing/under/rank/civilian/util/greyshirt = 5,
/obj/item/clothing/under/suit/navy = 3,
/obj/item/clothing/under/suit/black_really = 3,
/obj/item/clothing/under/suit/burgundy = 3,
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..e15e02f0d5 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -27,7 +27,10 @@
/obj/item/clothing/mask/bandana/red = 5,
/obj/item/clothing/under/rank/security/officer/skirt = 5,
/obj/item/clothing/under/rank/security/officer/grey = 5,
- /obj/item/clothing/under/pants/khaki = 5)
+ /obj/item/clothing/under/pants/khaki = 5,
+ /obj/item/clothing/suit/toggle/labcoat/depjacket/sec = 5,
+ /obj/item/clothing/under/rank/security/officer/util = 5,
+ /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp = 5)
premium = list(/obj/item/clothing/under/rank/security/officer/formal = 5,
/obj/item/clothing/head/beret/sec/navyofficer = 5)
refill_canister = /obj/item/vending_refill/wardrobe/sec_wardrobe
@@ -39,6 +42,44 @@
/obj/item/vending_refill/wardrobe/sec_wardrobe
machine_name = "SecDrobe"
+
+/obj/machinery/vending/wardrobe/det_wardrobe
+ name = "\improper DetDrobe"
+ desc = "A machine for all your detective needs, as long as you need clothes."
+ icon_state = "detdrobe"
+ product_ads = "Apply your brilliant deductive methods in style!"
+ vend_reply = "Thank you for using the DetDrobe!"
+ products = list(/obj/item/clothing/under/rank/security/detective = 2,
+ /obj/item/clothing/under/rank/security/detective/skirt = 2,
+ /obj/item/clothing/under/rank/security/detective/brown = 2,
+ /obj/item/clothing/under/rank/security/detective/brown/brown2 = 2,
+ /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/detcorp = 2,
+ /obj/item/clothing/under/rank/security/officer/util = 2,
+ /obj/item/clothing/shoes/sneakers/brown = 2,
+ /obj/item/clothing/suit/det_suit = 2,
+ /obj/item/clothing/head/fedora/det_hat = 2,
+ /obj/item/clothing/under/rank/security/detective/grey = 2,
+ /obj/item/clothing/under/rank/security/detective/grey/skirt = 2,
+ /obj/item/clothing/accessory/waistcoat = 2,
+ /obj/item/clothing/shoes/laceup = 2,
+ /obj/item/clothing/suit/det_suit/grey = 1,
+ /obj/item/clothing/suit/det_suit/forensicsred = 1,
+ /obj/item/clothing/suit/det_suit/forensicsred/long = 1,
+ /obj/item/clothing/suit/det_suit/forensicsblue = 1,
+ /obj/item/clothing/suit/det_suit/forensicsblue/long = 1,
+ /obj/item/clothing/head/fedora = 2,
+ /obj/item/clothing/gloves/color/black = 2,
+ /obj/item/clothing/gloves/color/latex = 2,
+ /obj/item/reagent_containers/food/drinks/flask/det = 2,
+ /obj/item/storage/fancy/cigarettes = 5)
+ premium = list(/obj/item/clothing/head/flatcap = 1)
+ refill_canister = /obj/item/vending_refill/wardrobe/det_wardrobe
+ extra_price = 350
+ payment_department = ACCOUNT_SEC
+
+/obj/item/vending_refill/wardrobe/det_wardrobe
+ machine_name = "DetDrobe"
+
/obj/machinery/vending/wardrobe/medi_wardrobe
name = "\improper MediDrobe"
desc = "A vending machine rumoured to be capable of dispensing clothing for medical personnel."
@@ -66,10 +107,15 @@
/obj/item/clothing/suit/toggle/labcoat = 5,
/obj/item/clothing/suit/toggle/labcoat/paramedic = 5,
/obj/item/clothing/suit/toggle/labcoat/emt = 5,
+ /obj/item/clothing/suit/toggle/labcoat/depjacket/med = 5,
/obj/item/clothing/shoes/sneakers/white = 5,
/obj/item/clothing/head/soft/emt = 5,
/obj/item/clothing/suit/apron/surgical = 5,
- /obj/item/clothing/mask/surgical = 5)
+ /obj/item/clothing/mask/surgical = 5,
+ /obj/item/clothing/under/rank/medical/doctor/util = 5,
+ /obj/item/clothing/under/rank/medical/paramedic/red = 5,
+ /obj/item/clothing/suit/toggle/labcoat/emt/red = 5,
+ /obj/item/clothing/suit/toggle/labcoat/emt/highvis = 5)
refill_canister = /obj/item/vending_refill/wardrobe/medi_wardrobe
payment_department = ACCOUNT_MED
cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
@@ -91,6 +137,12 @@
/obj/item/clothing/suit/hooded/wintercoat/engineering = 3,
/obj/item/clothing/under/rank/engineering/engineer = 5,
/obj/item/clothing/under/rank/engineering/engineer/skirt = 5,
+ /obj/item/clothing/under/rank/engineering/engineer/hazard = 5,
+ /obj/item/clothing/under/rank/engineering/engineer/hazard/green = 5,
+ /obj/item/clothing/under/rank/engineering/engineer/hazard/white = 5,
+ /obj/item/clothing/suit/toggle/labcoat/depjacket/eng = 5,
+ /obj/item/clothing/under/rank/engineering/engineer/util = 5,
+ /obj/item/clothing/under/rank/engineering/engineer/mechanic = 5,
/obj/item/clothing/suit/hazardvest = 5,
/obj/item/clothing/shoes/workboots = 5,
/obj/item/clothing/head/hardhat = 5,
@@ -134,6 +186,9 @@
products = list(/obj/item/clothing/suit/hooded/wintercoat/cargo = 3,
/obj/item/clothing/under/rank/cargo/tech = 5,
/obj/item/clothing/under/rank/cargo/tech/skirt = 5,
+ /obj/item/clothing/under/rank/cargo/util = 5,
+ /obj/item/clothing/suit/toggle/labcoat/depjacket/sup = 5,
+ /obj/item/clothing/under/rank/cargo/tech/long = 5,
/obj/item/clothing/shoes/sneakers/black = 5,
/obj/item/clothing/gloves/fingerless = 5,
/obj/item/clothing/head/soft = 5,
@@ -154,13 +209,17 @@
products = list(/obj/item/clothing/glasses/hud/diagnostic = 3,
/obj/item/clothing/head/beret/robo = 3,
/obj/item/clothing/under/rank/rnd/roboticist = 3,
+ /obj/item/clothing/under/rank/rnd/roboticist/sleek = 3,
/obj/item/clothing/under/rank/rnd/roboticist/skirt = 3,
/obj/item/clothing/suit/hooded/wintercoat/robotics = 3,
/obj/item/clothing/suit/toggle/labcoat = 3,
/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
@@ -181,8 +240,10 @@
/obj/item/storage/backpack/satchel/tox = 3,
/obj/item/clothing/suit/hooded/wintercoat/science = 3,
/obj/item/clothing/under/rank/rnd/scientist = 4,
+ /obj/item/clothing/under/rank/rnd/scientist/util = 4,
/obj/item/clothing/under/rank/rnd/scientist/skirt = 4,
/obj/item/clothing/suit/toggle/labcoat/science = 4,
+ /obj/item/clothing/suit/toggle/labcoat/depjacket/sci = 4,
/obj/item/clothing/shoes/sneakers/white = 4,
/obj/item/radio/headset/headset_sci = 4,
/obj/item/clothing/mask/gas = 5)
@@ -471,6 +532,7 @@
/obj/item/clothing/neck/cloak/cap = 1,
/obj/item/clothing/shoes/sneakers/brown = 2,
/obj/item/clothing/under/rank/captain = 1,
+ /obj/item/clothing/under/rank/captain/util = 1,
/obj/item/clothing/under/rank/captain/skirt = 1,
/obj/item/clothing/head/caphat = 1,
/obj/item/clothing/head/caphat/parade = 1,
diff --git a/code/modules/vore/eating/living.dm b/code/modules/vore/eating/living.dm
index 9395cef952..8923e9107d 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.
@@ -275,16 +273,7 @@
to_chat(src,"You attempted to apply your vore prefs but somehow you're in this character without a client.prefs variable. Tell a dev.")
return FALSE
ENABLE_BITFIELD(vore_flags,VOREPREF_INIT)
-
- // garbage data coming back the other way or breaking absorbed would be bad, so instead we do this
- vore_flags |= CHECK_BITFIELD(client.prefs.vore_flags,DIGESTABLE) // set to 1 if prefs is 1
- vore_flags |= CHECK_BITFIELD(client.prefs.vore_flags,DEVOURABLE)
- vore_flags |= CHECK_BITFIELD(client.prefs.vore_flags,FEEDING)
-
- vore_flags &= CHECK_BITFIELD(client.prefs.vore_flags,DIGESTABLE) // set to 0 if prefs is 0
- vore_flags &= CHECK_BITFIELD(client.prefs.vore_flags,DEVOURABLE)
- vore_flags &= CHECK_BITFIELD(client.prefs.vore_flags,FEEDING)
-
+ COPY_SPECIFIC_BITFIELDS(vore_flags,client.prefs.vore_flags,DIGESTABLE | DEVOURABLE | FEEDING | LICKABLE)
vore_taste = client.prefs.vore_taste
release_vore_contents(silent = TRUE)
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/config/wounds.txt b/config/wounds.txt
new file mode 100644
index 0000000000..11cd4988eb
--- /dev/null
+++ b/config/wounds.txt
@@ -0,0 +1,7 @@
+## WOUNDS ##
+
+## wound damage exponent
+WOUND_EXPONENT 1.2
+
+## wound damage multiplier (injury rolls get limb damage multiplied by this, added)
+WOUND_DAMAGE_MULTIPLIER 0.333
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 fd590f4673..0de57ebe79 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,653 @@
-->
+
04 October 2020
+
DeltaFire15 updated:
+
+
Synths / IPCs are no longer wound immune.
+
Husked IPCs / Synths should now be rendered correctly.
+
Falling vendors now squish synths / IPCs' limbs again.
+
Synths and IPCs now do not have some fun roundstart oversights anymore.
+
Regenerate_limbs now works for carbons with the ROBOTIC_LIMBS trait.
+
Pacifists no longer counterattack on parries if that attack would be harmful.
+
Heretic sacrifices now husk with the reason of burn, and deal some additional damage.
+
Neovgre can no longer become invincible on clock tiles.
+
Plushlings no longer break when absorbing snowflake plushies.
+
+
Detective-Google updated:
+
+
the snow cabin doors actually bolt now
+
+
Putnam3145 updated:
+
+
Ghosts are no longer incapable of going away.
+
+
monster860 updated:
+
+
The slimeperson swap-body UI stays open when you switch bodies
+
+
timothyteakettle updated:
+
+
limb id entry in mutant bodyparts now supports switching to/from species with gendered body parts
+
the minimum brightness of mutant parts is now a define
+
+
+
02 October 2020
+
ArcaneMusic, with minor tweaks by TheObserver-sys updated:
+
+
Adds and modifies fertilizers, as well as a new stat, Instability.
+
Removes the rather disused functionality of irrigation hoses, temporarily disables circuit use on hydroponics trays.
+
Plant Analyzer have been upgraded. Using one in hand will now switch between a stat view of your plant, and a chemical view of your plant. tweak:Trays now accept and store reagents, as well as coming with an autogrow mode. However, these upgrades came at the cost of old self sufficiency, meaning you must attend to your plants a bit more often.
+
Earthsblood, while not being able to gild trays anymore, has been found to still be quite powerful as a fertilizer.
+
+
CoreFlare updated:
+
+
Altcloaks! Available in loadout.
+
+
Detective-Google updated:
+
+
a smattering of clothes from the RP server
+
detective wardrobe
+
+
EmeraldSundisk updated:
+
+
Adds the "Skelter" space ruin
+
Creates a few new area designations for the Skelter
+
Cargo techs now have access to a "long pants" variant of their standard work uniform
+
Adds said uniform to CargoDrobes and the loadout menu
+
+
ItzGabby updated:
+
+
Three new turf tiles.
+
Turf icons with multiple damage icons, with in-hand icons for each tile.
+
Edited the name and description to wooden.
+
+
LetterN updated:
+
+
craftable railings
+
ports robust savefiles
+
+
MrJWhit updated:
+
+
tweaked heavy suit dmi
+
Fixes drones not being able to quickslot items
+
+
Putnam3145 updated:
+
+
New policy config for pyroclastic slimes.
+
SDGF clones now naked.
+
SDGF is now way more likely to make a clone that will align with the creator's goals, with purity, making it a better antagging tool.
+
SDGF clones now have the same traits as the original.
+
transfer_ckey now resets view, preventing things like SDGF clones with much larger view range.
+
Policy configs have been added for SDGF, currently unused: SDGF, SDGF_ALIGNED, SDGF_UNALIGNED.
+
Shivering now has thresholds and fever's thresholds now work.
+
Made allturfs setup actually set up all turfs.
+
Dynamic is now more aggressive with adding antags.
+
Fixes vore pref saving.
+
+
SandPoot updated:
+
+
Fixes headslugs being unable to recover their human form.
+
+
Tupinambis updated:
+
+
Adds methane and methyl bromide gases.
+
Minor gas name/desc changes to improve consistency
+
Ports the methyl bromide tank from bay. Adds two new canister sprites for the new gases and CH4 screen alerts.
+
fixed a mispelling in the wound armor value for the bounty hunter suit
+
+
dapnee updated:
+
+
atrium, clinic, an extra office, a pseudo public mining area, two more deluxe dorms, micro beach, aux bathroom, two construction areas, mass driver, more intercoms
+
moved all of service, chapel, dorms, garden, holodeck to a different z-level, RnD is more open, maintenance is a bit more random, more firelocks, added more mine-able rock
+
fixed the buttons in xenobio, gave shutters to cargo's storage area, fixed holodeck so it works now, fixed some techfabs being lathes, added sensors to atmos tanks, more decals, couple more signs, little floral areas and sitting areas added to break up hallway monotony, mech chargers are no longer missing their consoles, whiteship won't crash into arrivals anymore while the area it takes up is more telegraphed, APC placement on AI sat entrance, missing wire node for the outer portion of the AI sat, civilian level now has a telecom relay
+
+
lolman360 updated:
+
+
smonk machine runtimes
+
automatic hydro tray has a unique sprite now (fancy robot arm)
+
+
timothyteakettle updated:
+
+
added luminescent and stargazer sprites as selectable body sprites for slimes
+
wound exponent lowered slightly from 1.225 to 1.2
+
wound exponent and limb damage multiplier are now config values
+
ipcs and synthlizards are now treated as actual robots, with robotic limbs, an extra organ, and better emp acts
+
surgeries for healing robotic limbs, and brain surgery for robotic heads
+
androids limbs now show up as intended
+
emps now work from 1-100 severity instead of 1/2 and the severity reduces as you move from the epicentre
+
+
zeroisthebiggay updated:
+
+
ratvar gf is complete
+
+
+
01 October 2020
+
BlueWildrose updated:
+
+
Slimepeople are given unique laughs and screams.
+
Adds "warbles", "chimpers", and "puffs" to the customizable speech verbs for character.
+
+
+
29 September 2020
+
timothyteakettle updated:
+
+
fixed a typo causing your right eye colour to save as the left eye colour
+
+
+
28 September 2020
+
ArchieBeepBoop updated:
+
+
Craftable Micro Powered Fans
+
+
Degirin2120 updated:
+
+
Added engineering hazard jumpsuits, can be found in the engidrobe, comes in 3 varieties.
+
+
Putnam3145 updated:
+
+
Added a brute-force check-every-single-tile step to SSair when it has enough time to run.
+
G fuid production is now much lower.
+
Supermatter sabotage objective's gone.
+
Subterfuge objectives are now all equally likely.
+
Replaced a "(hopefully) 1" with a "2"
+
Cryoing no longer unwins already-won objectives.
+
+
SiliconMain updated:
+
+
Minor adjustment to material cost of long range atmos analyzer
+
+
Trilbyspaceclone updated:
+
+
Most drinks now have some animation in them, from basic soda bubbles fizzing around to ice cubes bobbing just a bit.
+
+
Tupinambis updated:
+
+
Ghost poly's color value is now a hex value instead of an oct value. This has been a thing for OVER FIVE YEARS
+
Updates TEG, Antimatter, Jetpack sprites (CO2 and Oxy from Eris).
+
Replaced old chair sprites with new ones ported and modified from eris.
+
Beds can now be placed both right and left.
+
Subtle changes to stool legs to give them more of a shine.
+
+
raspy-on-osu updated:
+
+
TEG power generation
+
+
thakyZ updated:
+
+
Added the ability to print the Light Replacer at the Engineering Protolathe
+
+
timothyteakettle updated:
+
+
turrets can once again be broken
+
you can now have heterochromia and select individual eye colours
+
+
+
27 September 2020
+
SiliconMain updated:
+
+
Holograms made from projectors (atmos, engi, sec, medical, ect...) can no longer be contaminated by radiation
+
+
+
26 September 2020
+
CoreFlare updated:
+
+
IPC's can have hair. Why wasn't this added earlier. Use the bald hairstyle for no hair.
+
+
+
25 September 2020
+
Putnam3145 updated:
+
+
Removed a non-working proc that already had its functionality implemented in another proc in the same file.
+
+
+
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:
+
+
Adds more paper to the library
+
The law office now has a desk window
+
Expands most of CogStation's exterior airlocks. Slightly adjusts surrounding areas to accommodate this.
+
Updates some of CogStation's paperwork
+
The rat in the morgue turned themselves into a possum. Funniest shit I've ever seen.
+
Adjusts some area designations so cameras should receive power properly
+
Cleans up an errant decal
+
+
Hatterhat updated:
+
+
Traitor holoparasites can now only be bought once, because apparently you can only have one active holopara.
+
PDA bombs can now only be bought once per uplink.
+
+
lolman360 updated:
+
+
atmos = radiation = chemistry.
+
+
shellspeed1 updated:
+
+
Adds slow mode for iv drips
+
+
timothyteakettle updated:
+
+
an ancient game over a thousand years old has re-emerged among crewmembers - rock paper scissors
+
customization features appear in alphabetical order where necessary
+
bokken do two more stamina damage now
+
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)
+
+
+
27 August 2020
+
silicons updated:
+
+
eyebeam lighting can only have 128 maximum HSV saturation now.
+
no more shotgun stripper clips in boxes.
+
goliath tentacles now do 20 damage to mechs at 25% ap
+
+
timothyteakettle updated:
+
+
changing your character's gender won't randomize its hairstyle and facial hairstyle now
+
+
+
26 August 2020
+
ancientpower updated:
+
+
Ghosts can read newscasters by clicking on them.
+
+
silicons updated:
+
+
hierophant vortex blasts now have 50% armor penetration vs mecha
+
ventcrawling now kicks off every attached/buckled mob, even for non humans.
+
+
+
25 August 2020
+
Hatterhat updated:
+
+
Insidious combat gloves have been replaced by insidious guerilla gloves. They're generally the same, except now you can tackle with them.
+
+
Literallynotpickles updated:
+
+
You can now equip handheld crew monitors on all medical-related winter coats.
+
+
Putnam3145 updated:
+
+
vore now ejects occupants on death
+
+
raspy-on-osu updated:
+
+
Thermoelectric Generator power output
+
+
timothyteakettle updated:
+
+
I.P.Cs now short their circuits when expressing emotion, causing sparks to appear around them.
+
+
+
24 August 2020
+
MrJWhit updated:
+
+
Fixes areas on expanded airlocks
+
+
silicons updated:
+
+
wormhole jaunters work
+
wormhole jaunters no longer get interference from bags of holding
+
airlocks now only shock on pulse/wirecutters instead of on tgui panel open.
+
+
timothyteakettle updated:
+
+
three new items are in the loadout for all donators
+
+
zeroisthebiggay updated:
+
+
contraband black evening gloves in kinkvend
+
+
23 August 2020
DeltaFire15 updated:
@@ -555,919 +1202,6 @@
explosive stand bombs can now be examined from any distance
explosive stand bombs are now a component.
-
-
02 August 2020
-
Auris456852 updated:
-
-
Added B.O.O.P. Remote Control cartridges to the PTech.
-
-
Hatterhat updated:
-
-
Durathread reinforcement kits! Sprites by Toriate, sets jumpsuit armor to durathread levels, craft in the crafting menu.
-
-
KeRSedChaplain updated:
-
-
The belligerent scripture and a brass multitool, and a new marauder variant which act similar to holoparasites/guardian spirits.
-
Removed the abductor teleport consoles they get, removes abscond for the time being as I've not seen much use for it other than just spamming it and hoping you end up in the armory.
-
moved around scriptures to make the cult work better as being based around the station, makes the Ark scream more often and work as a summonable object, clockwork armor now has a flat 0 defense up to 10 instead of negatives against laser damage. Makes the Ark work better in a station based setting, as well as the Heralds beacon in case It works for the mode.
-
added powerloaderstep.ogg for Neovgre
-
changes 'Dread_Ipad.dmi' to 'clockwork_slab.dmi'
-
-
MrJWhit updated:
-
-
Adjusts abductor spawntext
-
-
Seris02 updated:
-
-
fixed replica pods
-
-
dapnee updated:
-
-
fixed active turfs on wizard ruin and space hermit, fixed missing APC's and added a light on Delta
-
-
ike709 and bobbahbrown updated:
-
-
Admins can now see your bans on (some) other servers.
-
-
kappa-sama updated:
-
-
chaplain cultists being able to convert people to full clockwork cult status
-
-
timothyteakettle updated:
-
-
combat mode now has weaker buffs in terms of damage dealt and took for being or not being in the mode
-
damage debuff for laying down has been decreased from 0.5x to 0.7x
-
-
-
01 August 2020
-
dapnee updated:
-
-
added cake hat to bar, adds another atmostech spawn
-
sinks point in the right direction, APC won't spawn off the wall in circuits
-
changes commissary APC so it actually powers the room, adds a missing AIR alarm, arrivals no longer has active atmos tiles.
-
-
silicons updated:
-
-
toy shotguns no longer need 2 hands to fire
-
being on fire works again.
-
-
timothyteakettle updated:
-
-
monkeys no longer continuously bleed everywhere
-
-
-
30 July 2020
-
Adelphon updated:
-
-
Created a Cosmetic version of the camo.
-
-
Arturlang updated:
-
-
Bloodsucker LifeTick runs from BiologicalLife now
-
-
Ryll-Ryll ported by silicons updated:
-
-
Shoelaces are now a thing. You can untie them by laying down next to someone.
-
shoes now have lace delays and some can't be laced at all
-
do after now tracks who's interacting with who, meaning some actions now break when the target moves away.
-
-
SiliconMain updated:
-
-
Ported the long range atmos analyzer from sk*rat, credit to NotRanged
-
-
silicons updated:
-
-
energy sword perfect parries now reflect projectiles back at their shooters.
-
any mob can now parry if they have the right item
-
beam rifles now go into emitters properly
-
clickdelay has been refactored into an experimental hybrid system. Check code/modules/mob/clickdelay.dm for more information.
-
Resisting no longer checks clickdelay, but is standardized to a 2 second per resist system for most forms of resisting. It still sets clickdelay, though.
-
Meters have been added for estimating time until next attack/resist. Won't be that useful due to our clickdelay currently being very short, though. They're visible from your hand and resist HUD elements. experimental: Most attacks and forms of attacking (minus unarmed because it's too much of a pain to refactor how hugs/gloves of the north star works) now check for time-since-last-attack rather than making it so you can't attack for said time. This means you can very quickly switch to a gun from a melee weapon, whereas in the old system a melee weapon would put you on lockout for 0.8 seconds, in the new system all the gun cares about is that you did not attack for at least 0.4 seconds.
-
All clickdelay setting/reading are now procs, so it should be trivial to implement another system where drawing/switching to a weapon requires you to have it out for x seconds before using it. I am not personally doing it at this point in time though because it will likely just annoy everyone with no real gain unless we do something like putting a 0.8 second switch-to cooldown for guns (which I did not, yet)
-
attack_hand has been refactored to on_attack_hand remove: sexchems no longer impact click delay
-
turrets now automatically stagger their shots. Happy parrying/blocking.
-
turrets now speed_process, they were shooting slower than they should be
-
anything can now block with the right items
-
-
timothyteakettle updated:
-
-
some crafted crates won't contain items now, and thus have stopped breaking the laws of physics
-
beepskys hats now follow the laws of gravity and move up/down when he bobs up and down
-
-
-
29 July 2020
-
DeltaFire15 updated:
-
-
The 'Naked' outfit is no longer broken.
-
-
Ghommie updated:
-
-
fixed cremator trays, paleness examination strings, chat message for stamping paper, looping sounds, load away missions/VR admin verb, access requirements for using the sechuds.
-
Alt-Clicking a cigarette packet should pull the lighter out first if present, then cigarettes.
-
-
Hatterhat updated:
-
-
Directional windows can now be rotated counterclockwise properly again.
-
-
NecromancerAnne, Sirich96 updated:
-
-
Stunbaton sprites by Sirich96.
-
New sprites for the stunsword.
-
-
necromanceranne updated:
-
-
Adds some in-hands for the rapier sheath.
-
-
timothyteakettle updated:
-
-
tail wagging should work in all cases now
-
bluespace jars work as intended now
-
aliens can remove embeds now
-
bloodsuckers are not affected by bloodloss
-
-
zeroisthebiggay updated:
-
-
Flannel jackets and bartender winter coat
-
-
-
28 July 2020
-
Cacogen updated:
-
-
OSHA has more pull than anyone could have expected. All armor provided by Nanotrasen and the syndicate now have tags that accurately lists defenses and resistances of a piece of clothing.
-
-
CameronWoof updated:
-
-
Exotic seed crates now contain a spaceman's trumpet seed packet.
-
-
EmeraldSundisk updated:
-
-
Renovates Snow Taxi's northeast bathroom to have multiple non-urinal toilets and showers
-
Adds signage/labeling to improve map readability
-
Adds a bathroom to the northwest station
-
Adds a filing cabinet to the cargo department
-
Area designation adjustments to account for the above changes
-
Adds a missing airlock cyclelink near medical
-
-
Ludox235 updated:
-
-
Makes the flavour text that appears when you become a zombie tell you to act like one.
-
-
MrJWhit updated:
-
-
Decluttered toxins and hid the yellow mix line in atmos, for metastation
-
-
SiliconMain updated:
-
-
Paramedic heirloom is now a zippo
-
Durathread belts now protect their contents from radiation, and can hold full sized extinguishers
-
-
Sishen1542 updated:
-
-
removed zoomba
-
-
dapnee updated:
-
-
added a fan to the listening outpost
-
added two missing r-walls near the SM, removed random light and wire node below the engine, and fixed the missing cable in the courtroom on Kilo
-
-
kappa-sama updated:
-
-
Stimpaks cost 5tc once more, up from 3tc.
-
-
silicons updated:
-
-
polychromatic cloaks to loadout
-
no more self healing with medibeam guns
-
oh no, taser buff. alt fire delay dropped to 0.4 seconds.
-
you can now shoot yourself by disarm-intenting yourself with a gun.
-
-
timothyteakettle updated:
-
-
species code is now slightly less messy
-
slight tweak to how material crafting works
-
changed up pet carriers / bluespace jars a bit so you can't fit certain things inside them and also the text shown for resist times is accurate
-
-
zeroisthebiggay updated:
-
-
Black Box theft objective
-
-
-
27 July 2020
-
Hatterhat updated:
-
-
Training bokkens! Make 'em from wood, use 'em in-hand to toggle between harmful and not-so-harmful, practice your parrying with them!
-
Marker beacons should have a sprite again.
-
-
silicons updated:
-
-
clownops and clown mobs now share the same faction. HONK!
-
-
timothyteakettle updated:
-
-
modern pickle technology now allows people who have been turned into pickles, to be retrieved through the medical course of dying
-
-
-
26 July 2020
-
DeltaFire15 updated:
-
-
Organs now decay again.
-
-
Iatots updated:
-
-
Licking wounds now may cause you to spit out a hairball once in a while!
-
You can now craft a catgirl plushie with 3 of a new ingredient occasionally found in medbay!
-
-
dapnee updated:
-
-
removed legacy public mining shuttle area and remade lounge
-
-
-
25 July 2020
-
CameronWoof updated:
-
-
Aloe now has an icon.
-
-
timothyteakettle updated:
-
-
beepsky replaces the word THREAT_LEVEL with the actual threat level
-
-
-
24 July 2020
-
EmeraldSundisk updated:
-
-
Adds a CMO office, along with Virology and Genetics labs to Omega Station
-
Adds a second chemistry station to the chemistry lab
-
Adjusts the locations of some objects in medical to accommodate these new additions
-
Relocates the morgue
-
Relocates items in impacted areas of maintenance as well as the library
sanitization now doesn't cut off 15.7 or something million possible colors from character preferences (instead of only allowing 16 values for R G and B, it now allows 256 each)
-
projectiles are by default 17.5 tiles per second instead of 12.5
-
-
timothyteakettle updated:
-
-
due to recent innovative research in the medical field, you now have bones
-
zombie claws are now sharp and do less damage, but can destroy non-lifeforms far faster
-
zombies now take less stamina damage
-
beepsky can now wear hats
-
-
zeroisthebiggay updated:
-
-
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
-
-
-
26 June 2020
-
Ghommie updated:
-
-
Snore spam.
-
Hostile mobs shouldn't hit their original spawner structures or thoses of the same faction.
-
-
silicons updated:
-
-
soap cleans blood again
-
-
-
25 June 2020
-
Anonymous updated:
-
-
Added kepi and orvilike kepi. Available through loadout.
-
-
Detective Google updated:
-
-
Medigygax
-
-
Detective-Google updated:
-
-
malf AIs can no longer yeet the station while shunted
-
SMESes can now properly use self charging cells
-
ghosts now show up when the round ends
-
away missions
-
-
Funce updated:
-
-
Mentor SQL queries are now deleted properly.
-
-
Linzolle updated:
-
-
analyze function on chem master is no longer broken
-
organs now decay inside dead bodies again
-
-
dapnee updated:
-
-
wataur bottle item
-
wataur bottle and overlay
-
-
-
24 June 2020
-
DeltaFire15 updated:
-
-
Choosing a random item in your uplink will no longer sometimes reroll your contract.
-
Syndicate crate event cannot fire as a random event anymore.
-
-
Detective-Google updated:
-
-
singulos no longer succ infinite rods out of the ice
-
one of the directions for the diag hudpatch was blu instead of orang
-
-
timothyteakettle updated:
-
-
bonfires/grills no longer produce infinite quantities of food
-
slime's alter form ability now updates their hair colour when changing their body colour
-
-
-
22 June 2020
-
Ghommie (porting PRs by zxaber, Ryll-Ryll, AnturK) updated:
-
-
Certain small items purchased through cargo now get grouped into a single box. They also are immune to the 10% private account fee.
-
Added single-order options for several existing products in the Cargo Catalog.
-
Medkit listings are now single-pack items, and considered small items that get grouped into single boxes. Price for medkits is as close to unchanged as is reasonable.
-
You can now beat on vending machines to try and knock loose free stuff! You can also almost kill yourself doing it, so it's your call if your life is worth ten bucks.
-
Cigarette packets now have coupons on the back for small cargo items! Smoking DOES pay!
-
Some single/small items in cargo have been rebranded as goodies, come in lockboxes rather than crates, and can only be purchased with private accounts.
-
-
kevinz000 updated:
-
-
Life() is split into BiologicalLife() and PhysicalLife. A component signal has been added that can prevent either from ticking.
-
-
shellspeed1 updated:
-
-
Adds IV bags.
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index d4243a01ec..99ffc455c3 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -27060,3 +27060,467 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- rscadd: suiciding with the temporal katana omae wa mou shinderius you into the
shadow realm
- soundadd: twilight isnt earrape
+2020-08-24:
+ MrJWhit:
+ - bugfix: Fixes areas on expanded airlocks
+ silicons:
+ - bugfix: wormhole jaunters work
+ - tweak: wormhole jaunters no longer get interference from bags of holding
+ - bugfix: airlocks now only shock on pulse/wirecutters instead of on tgui panel
+ open.
+ timothyteakettle:
+ - rscadd: three new items are in the loadout for all donators
+ zeroisthebiggay:
+ - rscadd: contraband black evening gloves in kinkvend
+2020-08-25:
+ Hatterhat:
+ - rscadd: Insidious combat gloves have been replaced by insidious guerilla gloves.
+ They're generally the same, except now you can tackle with them.
+ Literallynotpickles:
+ - tweak: You can now equip handheld crew monitors on all medical-related winter
+ coats.
+ Putnam3145:
+ - tweak: vore now ejects occupants on death
+ raspy-on-osu:
+ - tweak: Thermoelectric Generator power output
+ timothyteakettle:
+ - tweak: I.P.Cs now short their circuits when expressing emotion, causing sparks
+ to appear around them.
+2020-08-26:
+ ancientpower:
+ - tweak: Ghosts can read newscasters by clicking on them.
+ silicons:
+ - balance: hierophant vortex blasts now have 50% armor penetration vs mecha
+ - balance: ventcrawling now kicks off every attached/buckled mob, even for non humans.
+2020-08-27:
+ silicons:
+ - tweak: eyebeam lighting can only have 128 maximum HSV saturation now.
+ - balance: no more shotgun stripper clips in boxes.
+ - balance: goliath tentacles now do 20 damage to mechs at 25% ap
+ timothyteakettle:
+ - tweak: changing your character's gender won't randomize its hairstyle and facial
+ hairstyle now
+2020-08-28:
+ EmeraldSundisk:
+ - rscadd: Adds more paper to the library
+ - rscadd: The law office now has a desk window
+ - tweak: Expands most of CogStation's exterior airlocks. Slightly adjusts surrounding
+ areas to accommodate this.
+ - tweak: Updates some of CogStation's paperwork
+ - tweak: The rat in the morgue turned themselves into a possum. Funniest shit I've
+ ever seen.
+ - bugfix: Adjusts some area designations so cameras should receive power properly
+ - bugfix: Cleans up an errant decal
+ Hatterhat:
+ - tweak: Traitor holoparasites can now only be bought once, because apparently you
+ can only have one active holopara.
+ - balance: PDA bombs can now only be bought once per uplink.
+ lolman360:
+ - rscadd: atmos = radiation = chemistry.
+ shellspeed1:
+ - rscadd: Adds slow mode for iv drips
+ timothyteakettle:
+ - rscadd: an ancient game over a thousand years old has re-emerged among crewmembers
+ - rock paper scissors
+ - tweak: customization features appear in alphabetical order where necessary
+ - tweak: bokken do two more stamina damage now
+ - 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.
+2020-09-25:
+ Putnam3145:
+ - bugfix: Removed a non-working proc that already had its functionality implemented
+ in another proc in the same file.
+2020-09-26:
+ CoreFlare:
+ - rscadd: IPC's can have hair. Why wasn't this added earlier. Use the bald hairstyle
+ for no hair.
+2020-09-27:
+ SiliconMain:
+ - tweak: Holograms made from projectors (atmos, engi, sec, medical, ect...) can
+ no longer be contaminated by radiation
+2020-09-28:
+ ArchieBeepBoop:
+ - rscadd: Craftable Micro Powered Fans
+ Degirin2120:
+ - rscadd: Added engineering hazard jumpsuits, can be found in the engidrobe, comes
+ in 3 varieties.
+ Putnam3145:
+ - tweak: Added a brute-force check-every-single-tile step to SSair when it has enough
+ time to run.
+ - balance: G fuid production is now much lower.
+ - rscdel: Supermatter sabotage objective's gone.
+ - balance: Subterfuge objectives are now all equally likely.
+ - spellcheck: Replaced a "(hopefully) 1" with a "2"
+ - tweak: Cryoing no longer unwins already-won objectives.
+ SiliconMain:
+ - tweak: Minor adjustment to material cost of long range atmos analyzer
+ Trilbyspaceclone:
+ - tweak: Most drinks now have some animation in them, from basic soda bubbles fizzing
+ around to ice cubes bobbing just a bit.
+ Tupinambis:
+ - bugfix: Ghost poly's color value is now a hex value instead of an oct value. This
+ has been a thing for OVER FIVE YEARS
+ - imageadd: Updates TEG, Antimatter, Jetpack sprites (CO2 and Oxy from Eris).
+ - imageadd: Replaced old chair sprites with new ones ported and modified from eris.
+ - tweak: Beds can now be placed both right and left.
+ - tweak: Subtle changes to stool legs to give them more of a shine.
+ raspy-on-osu:
+ - tweak: TEG power generation
+ thakyZ:
+ - rscadd: Added the ability to print the Light Replacer at the Engineering Protolathe
+ timothyteakettle:
+ - bugfix: turrets can once again be broken
+ - rscadd: you can now have heterochromia and select individual eye colours
+2020-09-29:
+ timothyteakettle:
+ - bugfix: fixed a typo causing your right eye colour to save as the left eye colour
+2020-10-01:
+ BlueWildrose:
+ - soundadd: Slimepeople are given unique laughs and screams.
+ - tweak: Adds "warbles", "chimpers", and "puffs" to the customizable speech verbs
+ for character.
+2020-10-02:
+ ArcaneMusic, with minor tweaks by TheObserver-sys:
+ - rscadd: Adds and modifies fertilizers, as well as a new stat, Instability.
+ - rscdel: Removes the rather disused functionality of irrigation hoses, temporarily
+ disables circuit use on hydroponics trays.
+ - tweak: Plant Analyzer have been upgraded. Using one in hand will now switch between
+ a stat view of your plant, and a chemical view of your plant. tweak:Trays now
+ accept and store reagents, as well as coming with an autogrow mode. However,
+ these upgrades came at the cost of old self sufficiency, meaning you must attend
+ to your plants a bit more often.
+ - tweak: Earthsblood, while not being able to gild trays anymore, has been found
+ to still be quite powerful as a fertilizer.
+ CoreFlare:
+ - rscadd: Altcloaks! Available in loadout.
+ Detective-Google:
+ - rscadd: a smattering of clothes from the RP server
+ - rscadd: detective wardrobe
+ EmeraldSundisk:
+ - rscadd: Adds the "Skelter" space ruin
+ - code_imp: Creates a few new area designations for the Skelter
+ - rscadd: Cargo techs now have access to a "long pants" variant of their standard
+ work uniform
+ - rscadd: Adds said uniform to CargoDrobes and the loadout menu
+ ItzGabby:
+ - rscadd: Three new turf tiles.
+ - imageadd: Turf icons with multiple damage icons, with in-hand icons for each tile.
+ - spellcheck: Edited the name and description to wooden.
+ LetterN:
+ - rscadd: craftable railings
+ - code_imp: ports robust savefiles
+ MrJWhit:
+ - tweak: tweaked heavy suit dmi
+ - bugfix: Fixes drones not being able to quickslot items
+ Putnam3145:
+ - rscadd: New policy config for pyroclastic slimes.
+ - tweak: SDGF clones now naked.
+ - balance: SDGF is now way more likely to make a clone that will align with the
+ creator's goals, with purity, making it a better antagging tool.
+ - bugfix: SDGF clones now have the same traits as the original.
+ - bugfix: transfer_ckey now resets view, preventing things like SDGF clones with
+ much larger view range.
+ - config: 'Policy configs have been added for SDGF, currently unused: SDGF, SDGF_ALIGNED,
+ SDGF_UNALIGNED.'
+ - bugfix: Shivering now has thresholds and fever's thresholds now work.
+ - bugfix: Made allturfs setup actually set up all turfs.
+ - balance: Dynamic is now more aggressive with adding antags.
+ - bugfix: Fixes vore pref saving.
+ SandPoot:
+ - bugfix: Fixes headslugs being unable to recover their human form.
+ Tupinambis:
+ - rscadd: Adds methane and methyl bromide gases.
+ - tweak: Minor gas name/desc changes to improve consistency
+ - imageadd: Ports the methyl bromide tank from bay. Adds two new canister sprites
+ for the new gases and CH4 screen alerts.
+ - bugfix: fixed a mispelling in the wound armor value for the bounty hunter suit
+ dapnee:
+ - rscadd: atrium, clinic, an extra office, a pseudo public mining area, two more
+ deluxe dorms, micro beach, aux bathroom, two construction areas, mass driver,
+ more intercoms
+ - tweak: moved all of service, chapel, dorms, garden, holodeck to a different z-level,
+ RnD is more open, maintenance is a bit more random, more firelocks, added more
+ mine-able rock
+ - bugfix: fixed the buttons in xenobio, gave shutters to cargo's storage area, fixed
+ holodeck so it works now, fixed some techfabs being lathes, added sensors to
+ atmos tanks, more decals, couple more signs, little floral areas and sitting
+ areas added to break up hallway monotony, mech chargers are no longer missing
+ their consoles, whiteship won't crash into arrivals anymore while the area it
+ takes up is more telegraphed, APC placement on AI sat entrance, missing wire
+ node for the outer portion of the AI sat, civilian level now has a telecom relay
+ lolman360:
+ - bugfix: smonk machine runtimes
+ - bugfix: automatic hydro tray has a unique sprite now (fancy robot arm)
+ timothyteakettle:
+ - rscadd: added luminescent and stargazer sprites as selectable body sprites for
+ slimes
+ - tweak: wound exponent lowered slightly from 1.225 to 1.2
+ - config: wound exponent and limb damage multiplier are now config values
+ - rscadd: ipcs and synthlizards are now treated as actual robots, with robotic limbs,
+ an extra organ, and better emp acts
+ - rscadd: surgeries for healing robotic limbs, and brain surgery for robotic heads
+ - bugfix: androids limbs now show up as intended
+ - refactor: emps now work from 1-100 severity instead of 1/2 and the severity reduces
+ as you move from the epicentre
+ zeroisthebiggay:
+ - rscadd: ratvar gf is complete
+2020-10-04:
+ DeltaFire15:
+ - bugfix: Synths / IPCs are no longer wound immune.
+ - bugfix: Husked IPCs / Synths should now be rendered correctly.
+ - bugfix: Falling vendors now squish synths / IPCs' limbs again.
+ - bugfix: Synths and IPCs now do not have some fun roundstart oversights anymore.
+ - bugfix: Regenerate_limbs now works for carbons with the ROBOTIC_LIMBS trait.
+ - bugfix: Pacifists no longer counterattack on parries if that attack would be harmful.
+ - tweak: Heretic sacrifices now husk with the reason of burn, and deal some additional
+ damage.
+ - bugfix: Neovgre can no longer become invincible on clock tiles.
+ - bugfix: Plushlings no longer break when absorbing snowflake plushies.
+ Detective-Google:
+ - bugfix: the snow cabin doors actually bolt now
+ Putnam3145:
+ - bugfix: Ghosts are no longer incapable of going away.
+ monster860:
+ - tweak: The slimeperson swap-body UI stays open when you switch bodies
+ timothyteakettle:
+ - bugfix: limb id entry in mutant bodyparts now supports switching to/from species
+ with gendered body parts
+ - tweak: the minimum brightness of mutant parts is now a define
diff --git a/html/changelogs/AutoChangeLog-pr-13204.yml b/html/changelogs/AutoChangeLog-pr-13204.yml
deleted file mode 100644
index e9033c299d..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13204.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "timothyteakettle"
-delete-after: True
-changes:
- - rscadd: "three new items are in the loadout for all donators"
diff --git a/html/changelogs/AutoChangeLog-pr-13239.yml b/html/changelogs/AutoChangeLog-pr-13239.yml
deleted file mode 100644
index 0f01f680c6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13239.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "zeroisthebiggay"
-delete-after: True
-changes:
- - rscadd: "contraband black evening gloves in kinkvend"
diff --git a/html/changelogs/AutoChangeLog-pr-13243.yml b/html/changelogs/AutoChangeLog-pr-13243.yml
deleted file mode 100644
index a09ae5d607..0000000000
--- a/html/changelogs/AutoChangeLog-pr-13243.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "MrJWhit"
-delete-after: True
-changes:
- - bugfix: "Fixes areas on expanded airlocks"
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
+
+
+
+
+
+
+
+