| Name: | [active1.fields["name"]] |
| ID: | [active1.fields["id"]] |
- | Sex: | [active1.fields["sex"]] |
+ | Gender: | [active1.fields["gender"]] |
| Age: | [active1.fields["age"]] | "}
dat += "| Species: | [active1.fields["species"]] | "
dat += {"| Rank: | [active1.fields["rank"]] |
@@ -309,36 +288,39 @@ What a mess.*/
active1 = null
active2 = null
- if("Confirm Identity")
- eject_id(usr)
-
if("Log Out")
authenticated = null
screen = null
active1 = null
active2 = null
+ playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
if("Log In")
- if(issilicon(usr))
- var/mob/living/silicon/borg = usr
+ var/mob/M = usr
+ var/obj/item/card/id/I = M.get_idcard(TRUE)
+ if(issilicon(M))
+ var/mob/living/silicon/borg = M
active1 = null
active2 = null
authenticated = borg.name
rank = "AI"
screen = 1
- else if(IsAdminGhost(usr))
+ else if(IsAdminGhost(M))
active1 = null
active2 = null
- authenticated = usr.client.holder.admin_signature
+ authenticated = M.client.holder.admin_signature
rank = "Central Command"
screen = 1
- else if(istype(scan, /obj/item/card/id))
+ else if(I && check_access(I))
active1 = null
active2 = null
- if(check_access(scan))
- authenticated = scan.registered_name
- rank = scan.assignment
- screen = 1
+ authenticated = I.registered_name
+ rank = I.assignment
+ screen = 1
+ else
+ to_chat(usr, "Unauthorized Access.")
+ playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+
//RECORD FUNCTIONS
if("Record Maintenance")
screen = 2
@@ -346,16 +328,14 @@ What a mess.*/
active2 = null
if("Browse Record")
- var/datum/data/record/R = locate(href_list["d_rec"])
- var/S = locate(href_list["d_rec"])
- if(!( GLOB.data_core.general.Find(R) ))
+ var/datum/data/record/R = locate(href_list["d_rec"]) in GLOB.data_core.general
+ if(!R)
temp = "Record Not Found!"
else
+ active1 = active2 = R
for(var/datum/data/record/E in GLOB.data_core.security)
if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
- S = E
- active1 = R
- active2 = S
+ active2 = E
screen = 3
@@ -368,7 +348,7 @@ What a mess.*/
var/obj/item/paper/P = new /obj/item/paper( loc )
P.info = "Security Record - (SR-[GLOB.data_core.securityPrintCount]) "
if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))
- P.info += text("Name: [] ID: [] \nSex: [] \nAge: [] ", active1.fields["name"], active1.fields["id"], active1.fields["sex"], active1.fields["age"])
+ P.info += text("Name: [] ID: [] \nGender: [] \nAge: [] ", active1.fields["name"], active1.fields["id"], active1.fields["gender"], active1.fields["age"])
P.info += "\nSpecies: [active1.fields["species"]] "
P.info += text("\nFingerprint: [] \nPhysical Status: [] \nMental Status: [] ", active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"])
else
@@ -419,6 +399,7 @@ What a mess.*/
P.info += "Security Record Lost! "
P.name = text("SR-[] '[]'", GLOB.data_core.securityPrintCount, "Record Lost")
P.info += ""
+ P.update_icon()
printing = null
if("Print Poster")
if(!( printing ))
@@ -512,7 +493,7 @@ What a mess.*/
G.fields["name"] = "New Record"
G.fields["id"] = "[num2hex(rand(1, 1.6777215E7), 6)]"
G.fields["rank"] = "Unassigned"
- G.fields["sex"] = "Male"
+ G.fields["gender"] = "Male"
G.fields["age"] = "Unknown"
G.fields["species"] = "Human"
G.fields["photo_front"] = new /icon()
@@ -584,12 +565,14 @@ What a mess.*/
if(!canUseSecurityRecordsConsole(usr, t1, a1))
return
active1.fields["fingerprint"] = t1
- if("sex")
+ if("gender")
if(istype(active1, /datum/data/record))
- if(active1.fields["sex"] == "Male")
- active1.fields["sex"] = "Female"
+ if(active1.fields["gender"] == "Male")
+ active1.fields["gender"] = "Female"
+ else if(active1.fields["gender"] == "Female")
+ active1.fields["gender"] = "Other"
else
- active1.fields["sex"] = "Male"
+ active1.fields["gender"] = "Male"
if("age")
if(istype(active1, /datum/data/record))
var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num
@@ -767,19 +750,14 @@ What a mess.*/
P = user.get_active_held_item()
return P
-/obj/machinery/computer/secure_data/proc/print_photo(icon/temp, name)
+/obj/machinery/computer/secure_data/proc/print_photo(icon/temp, person_name)
if (printing)
return
printing = TRUE
sleep(20)
var/obj/item/photo/P = new/obj/item/photo(drop_location())
- var/icon/small_img = icon(temp)
- var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo")
- small_img.Scale(8, 8)
- ic.Blend(small_img,ICON_OVERLAY, 13, 13)
- P.icon = ic
- P.picture.picture_image = temp
- P.desc = "The photo on file for [name]."
+ var/datum/picture/toEmbed = new(name = person_name, desc = "The photo on file for [person_name].", image = temp)
+ P.set_picture(toEmbed, TRUE, TRUE)
P.pixel_x = rand(-10, 10)
P.pixel_y = rand(-10, 10)
printing = FALSE
@@ -799,7 +777,7 @@ What a mess.*/
else
R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]"
if(2)
- R.fields["sex"] = pick("Male", "Female")
+ R.fields["gender"] = pick("Male", "Female", "Other")
if(3)
R.fields["age"] = rand(5, 85)
if(4)
@@ -823,7 +801,7 @@ What a mess.*/
/obj/machinery/computer/secure_data/proc/canUseSecurityRecordsConsole(mob/user, message1 = 0, record1, record2)
if(user)
if(authenticated)
- if(user.canUseTopic(src))
+ if(user.canUseTopic(src, BE_CLOSE))
if(!trim(message1))
return 0
if(!record1 || record1 == active1)
@@ -831,22 +809,3 @@ What a mess.*/
return 1
return 0
-/obj/machinery/computer/secure_data/AltClick(mob/user)
- if(user.canUseTopic(src))
- eject_id(user)
-
-/obj/machinery/computer/secure_data/proc/eject_id(mob/user)
- if(scan)
- scan.forceMove(drop_location())
- if(!issilicon(user) && Adjacent(user))
- user.put_in_hands(scan)
- scan = null
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
- else //switching the ID with the one you're holding
- if(issilicon(user) || !Adjacent(user))
- return
- var/obj/item/card/id/held_id = user.is_holding_item_of_type(/obj/item/card/id)
- if(QDELETED(held_id) || !user.transferItemToLoc(held_id, src))
- return
- scan = held_id
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 1081cb5fa7..365d19b826 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1280,20 +1280,23 @@
return !density || (check_access(ID) && !locked && hasPower())
/obj/machinery/door/airlock/emag_act(mob/user)
- if(!operating && density && hasPower() && !(obj_flags & EMAGGED))
- operating = TRUE
- update_icon(AIRLOCK_EMAG, 1)
- sleep(6)
- if(QDELETED(src))
- return
- operating = FALSE
- if(!open())
- update_icon(AIRLOCK_CLOSED, 1)
- obj_flags |= EMAGGED
- lights = FALSE
- locked = TRUE
- loseMainPower()
- loseBackupPower()
+ . = ..()
+ if(operating || !density || !hasPower() || obj_flags & EMAGGED)
+ return
+ operating = TRUE
+ update_icon(AIRLOCK_EMAG, 1)
+ addtimer(CALLBACK(src, .proc/open_sesame), 6)
+ return TRUE
+
+/obj/machinery/door/airlock/proc/open_sesame()
+ operating = FALSE
+ if(!open())
+ update_icon(AIRLOCK_CLOSED, 1)
+ obj_flags |= EMAGGED
+ lights = FALSE
+ locked = TRUE
+ loseMainPower()
+ loseBackupPower()
/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
add_fingerprint(user)
diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm
index 2e7e7d5cba..702f700617 100644
--- a/code/game/machinery/doors/unpowered.dm
+++ b/code/game/machinery/doors/unpowered.dm
@@ -13,9 +13,6 @@
else
return ..()
-/obj/machinery/door/unpowered/emag_act()
- return
-
/obj/machinery/door/unpowered/shuttle
icon = 'icons/turf/shuttle.dmi'
name = "door"
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 97ab664b85..933bb4f42a 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -206,15 +206,20 @@
..()
/obj/machinery/door/window/emag_act(mob/user)
- if(!operating && density && !(obj_flags & EMAGGED))
- obj_flags |= EMAGGED
- operating = TRUE
- flick("[src.base_state]spark", src)
- playsound(src, "sparks", 75, 1)
- sleep(6)
- operating = FALSE
- desc += " Its access panel is smoking slightly."
- open(2)
+ . = ..()
+ if(operating || !density || obj_flags & EMAGGED)
+ return
+ obj_flags |= EMAGGED
+ operating = TRUE
+ flick("[src.base_state]spark", src)
+ playsound(src, "sparks", 75, 1)
+ addtimer(CALLBACK(src, .proc/open_windows_me), 6)
+ return TRUE
+
+/obj/machinery/door/window/proc/open_windows_me()
+ operating = FALSE
+ desc += " Its access panel is smoking slightly."
+ open(2)
/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params)
diff --git a/code/game/machinery/embedded_controller/access_controller.dm b/code/game/machinery/embedded_controller/access_controller.dm
index 70ce3d36ae..3b9322d207 100644
--- a/code/game/machinery/embedded_controller/access_controller.dm
+++ b/code/game/machinery/embedded_controller/access_controller.dm
@@ -26,6 +26,7 @@
findObjsByTag()
/obj/machinery/doorButtons/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
@@ -33,6 +34,7 @@
req_one_access = list()
playsound(src, "sparks", 100, 1)
to_chat(user, "You short out the access controller.")
+ return TRUE
/obj/machinery/doorButtons/proc/removeMe()
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 8f0d56a49e..5c2676c2b9 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -97,6 +97,7 @@
alarm()
/obj/machinery/firealarm/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
@@ -104,6 +105,7 @@
user.visible_message("Sparks fly out of [src]!",
"You emag [src], disabling its thermal sensors.")
playsound(src, "sparks", 50, 1)
+ return TRUE
/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume)
if((temperature > T0C + 200 || temperature < BODYTEMP_COLD_DAMAGE_LIMIT) && (last_alarm+FIREALARM_COOLDOWN < world.time) && !(obj_flags & EMAGGED) && detecting && !stat)
diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm
index f6cb58bbbc..f51c145635 100644
--- a/code/game/machinery/gulag_item_reclaimer.dm
+++ b/code/game/machinery/gulag_item_reclaimer.dm
@@ -9,7 +9,6 @@
idle_power_usage = 100
active_power_usage = 2500
var/list/stored_items = list()
- var/obj/item/card/id/prisoner/inserted_id = null
var/obj/machinery/gulag_teleporter/linked_teleporter = null
/obj/machinery/gulag_item_reclaimer/Destroy()
@@ -18,28 +17,15 @@
I.forceMove(get_turf(src))
if(linked_teleporter)
linked_teleporter.linked_reclaimer = null
- if(inserted_id)
- inserted_id.forceMove(get_turf(src))
- inserted_id = null
return ..()
/obj/machinery/gulag_item_reclaimer/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED) // emagging lets anyone reclaim all the items
return
req_access = list()
obj_flags |= EMAGGED
-
-/obj/machinery/gulag_item_reclaimer/attackby(obj/item/I, mob/user)
- if(istype(I, /obj/item/card/id))
- if(!inserted_id)
- if(!user.transferItemToLoc(I, src))
- return
- inserted_id = I
- to_chat(user, "You insert [I].")
- return
- else
- to_chat(user, "There's an ID inserted already.")
- return ..()
+ return TRUE
/obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
@@ -55,15 +41,19 @@
if(allowed(user))
can_reclaim = TRUE
- if(inserted_id)
- data["id"] = inserted_id
- data["id_name"] = inserted_id.registered_name
- if(inserted_id.points >= inserted_id.goal)
+ var/obj/item/card/id/I = user.get_idcard(TRUE)
+ if(istype(I, /obj/item/card/id/prisoner))
+ var/obj/item/card/id/prisoner/P = I
+ if(P.points >= P.goal)
can_reclaim = TRUE
var/list/mobs = list()
for(var/i in stored_items)
var/mob/thismob = i
+ if(QDELETED(thismob))
+ say("Alert! Unable to locate vital signals of a previously processed prisoner. Ejecting equipment!")
+ drop_items(thismob)
+ continue
var/list/mob_info = list()
mob_info["name"] = thismob.real_name
mob_info["mob"] = "[REF(thismob)]"
@@ -78,16 +68,6 @@
/obj/machinery/gulag_item_reclaimer/ui_act(action, list/params)
switch(action)
- if("handle_id")
- if(inserted_id)
- usr.put_in_hands(inserted_id)
- inserted_id = null
- else
- var/obj/item/I = usr.is_holding_item_of_type(/obj/item/card/id)
- if(I)
- if(!usr.transferItemToLoc(I, src))
- return
- inserted_id = I
if("release_items")
var/mob/M = locate(params["mobref"])
if(M == usr || allowed(usr))
@@ -98,8 +78,9 @@
/obj/machinery/gulag_item_reclaimer/proc/drop_items(mob/user)
if(!stored_items[user])
return
+ var/drop_location = drop_location()
for(var/i in stored_items[user])
var/obj/item/W = i
stored_items[user] -= W
- W.forceMove(get_turf(src))
+ W.forceMove(drop_location)
stored_items -= user
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 8be7becfdd..1fccadda21 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -158,11 +158,13 @@
open_machine()
/obj/machinery/harvester/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
allow_living = TRUE
to_chat(user, "You overload [src]'s lifesign scanners.")
+ return TRUE
/obj/machinery/harvester/container_resist(mob/living/user)
if(!harvesting)
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index a3250fe1b0..59806e97a4 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -215,6 +215,7 @@
return dat
/obj/machinery/limbgrower/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
for(var/id in SSresearch.techweb_designs)
@@ -223,3 +224,4 @@
stored_research.add_design(D)
to_chat(user, "A warning flashes onto the screen, stating that safety overrides have been deactivated!")
obj_flags |= EMAGGED
+ return TRUE
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index b0204ddeb8..b0a75c99dc 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -290,6 +290,7 @@
return ..()
/obj/machinery/porta_turret/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "You short out [src]'s threat assessment circuits.")
@@ -300,6 +301,7 @@
update_icon()
sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
on = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
+ return TRUE
/obj/machinery/porta_turret/emp_act(severity)
@@ -837,6 +839,7 @@
to_chat(user, "Access denied.")
/obj/machinery/turretid/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "You short out the turret controls' access analysis module.")
@@ -844,6 +847,7 @@
locked = FALSE
if(user && user.machine == src)
attack_hand(user)
+ return TRUE
/obj/machinery/turretid/attack_ai(mob/user)
if(!ailock || IsAdminGhost(user))
diff --git a/code/game/machinery/porta_turret/portable_turret_cover.dm b/code/game/machinery/porta_turret/portable_turret_cover.dm
index 6ff795c1cc..afb00d4ee2 100644
--- a/code/game/machinery/porta_turret/portable_turret_cover.dm
+++ b/code/game/machinery/porta_turret/portable_turret_cover.dm
@@ -86,10 +86,13 @@
. = 0
/obj/machinery/porta_turret_cover/emag_act(mob/user)
- if(!(parent_turret.obj_flags & EMAGGED))
- to_chat(user, "You short out [parent_turret]'s threat assessment circuits.")
- visible_message("[parent_turret] hums oddly...")
- parent_turret.obj_flags |= EMAGGED
- parent_turret.on = 0
- spawn(40)
- parent_turret.on = 1
+ . = ..()
+ if(parent_turret.obj_flags & EMAGGED)
+ return
+ to_chat(user, "You short out [parent_turret]'s threat assessment circuits.")
+ visible_message("[parent_turret] hums oddly...")
+ parent_turret.obj_flags |= EMAGGED
+ parent_turret.on = 0
+ spawn(40)
+ parent_turret.on = 1
+ return TRUE
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index ff40a8539a..9d431487e0 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -65,6 +65,7 @@
return ..()
/obj/machinery/recycler/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
@@ -73,6 +74,7 @@
update_icon()
playsound(src, "sparks", 75, 1, -1)
to_chat(user, "You use the cryptographic sequencer on [src].")
+ return TRUE
/obj/machinery/recycler/update_icon()
..()
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index e2aebd2370..fc6577a4f1 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -195,6 +195,7 @@
return ..()
/obj/machinery/shieldgen/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
to_chat(user, "The access controller is damaged!")
return
@@ -202,6 +203,7 @@
locked = FALSE
playsound(src, "sparks", 100, 1)
to_chat(user, "You short out the access controller.")
+ return TRUE
/obj/machinery/shieldgen/update_icon()
if(active)
@@ -387,6 +389,7 @@
add_fingerprint(user)
/obj/machinery/shieldwallgen/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
to_chat(user, "The access controller is damaged!")
return
@@ -394,6 +397,7 @@
locked = FALSE
playsound(src, "sparks", 100, 1)
to_chat(user, "You short out the access controller.")
+ return TRUE
//////////////Containment Field START
/obj/machinery/shieldwall
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 298587cdea..4b73032a8a 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -99,6 +99,7 @@
return ..()
/obj/machinery/computer/slot_machine/emag_act()
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
@@ -106,6 +107,7 @@
spark_system.set_up(4, 0, src.loc)
spark_system.start()
playsound(src, "sparks", 50, 1)
+ return TRUE
/obj/machinery/computer/slot_machine/ui_interact(mob/living/user)
. = ..()
diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm
index b11c6c102d..ebb40148d8 100644
--- a/code/game/machinery/telecomms/computers/message.dm
+++ b/code/game/machinery/telecomms/computers/message.dm
@@ -42,21 +42,23 @@
return ..()
/obj/machinery/computer/message_monitor/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
- if(!isnull(linkedServer))
- obj_flags |= EMAGGED
- screen = 2
- spark_system.set_up(5, 0, src)
- spark_system.start()
- var/obj/item/paper/monitorkey/MK = new(loc, linkedServer)
- // Will help make emagging the console not so easy to get away with.
- MK.info += "
�%@%(*$%&(�&?*(%&�/{}"
- var/time = 100 * length(linkedServer.decryptkey)
- addtimer(CALLBACK(src, .proc/UnmagConsole), time)
- message = rebootmsg
- else
+ if(isnull(linkedServer))
to_chat(user, "A no server error appears on the screen.")
+ return
+ obj_flags |= EMAGGED
+ screen = 2
+ spark_system.set_up(5, 0, src)
+ spark_system.start()
+ var/obj/item/paper/monitorkey/MK = new(loc, linkedServer)
+ // Will help make emagging the console not so easy to get away with.
+ MK.info += "
�%@%(*$%&(�&?*(%&�/{}"
+ var/time = 100 * length(linkedServer.decryptkey)
+ addtimer(CALLBACK(src, .proc/UnmagConsole), time)
+ message = rebootmsg
+ return TRUE
/obj/machinery/computer/message_monitor/New()
. = ..()
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 01e304872f..e1dc80911f 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -64,10 +64,15 @@
/obj/machinery/mecha_part_fabricator/emag_act()
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
req_access = list()
+ INVOKE_ASYNC(src, .proc/error_action_sucessful)
+ return TRUE
+
+/obj/machinery/mecha_part_fabricator/proc/error_action_sucessful()
say("DB error \[Code 0x00F1\]")
sleep(10)
say("Attempting auto-repair...")
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index cc69b88cc4..9f072c48b6 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -42,6 +42,9 @@
/obj/effect/decal/cleanable/glass/ex_act()
qdel(src)
+/obj/effect/decal/cleanable/glass/plasma
+ icon_state = "plasmatiny"
+
/obj/effect/decal/cleanable/dirt
name = "dirt"
desc = "Someone should clean that up."
diff --git a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm
index 1708753dc1..1e755bc7e9 100644
--- a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm
+++ b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm
@@ -36,4 +36,203 @@
/obj/effect/turf_decal/tile/neutral
name = "neutral corner"
color = "#D4D4D4"
- alpha = 50
\ No newline at end of file
+ alpha = 50
+
+/obj/effect/turf_decal/trimline
+ layer = TURF_PLATING_DECAL_LAYER
+ alpha = 110
+ icon_state = "trimline_box"
+
+/obj/effect/turf_decal/trimline/white
+ color = "#FFFFFF"
+
+/obj/effect/turf_decal/trimline/white/line
+ name = "trim decal"
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/white/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/white/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/white/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/white/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/white/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/white/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/red
+ color = "#DE3A3A"
+
+/obj/effect/turf_decal/trimline/red/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/red/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/red/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/red/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/red/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/red/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/red/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/green
+ color = "#9FED58"
+
+/obj/effect/turf_decal/trimline/green/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/green/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/green/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/green/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/green/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/green/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/green/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/blue
+ color = "#52B4E9"
+
+/obj/effect/turf_decal/trimline/blue/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/blue/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/blue/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/blue/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/blue/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/blue/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/blue/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/yellow
+ color = "#EFB341"
+
+/obj/effect/turf_decal/trimline/yellow/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/yellow/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/yellow/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/yellow/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/yellow/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/yellow/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/yellow/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/purple
+ color = "#D381C9"
+
+/obj/effect/turf_decal/trimline/purple/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/purple/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/purple/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/purple/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/purple/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/purple/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/purple/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/brown
+ color = "#A46106"
+
+/obj/effect/turf_decal/trimline/brown/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/brown/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/brown/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/brown/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/brown/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/brown/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/brown/filled/end
+ icon_state = "trimline_end_fill"
+
+/obj/effect/turf_decal/trimline/neutral
+ color = "#D4D4D4"
+ alpha = 50
+
+/obj/effect/turf_decal/trimline/neutral/line
+ icon_state = "trimline"
+
+/obj/effect/turf_decal/trimline/neutral/corner
+ icon_state = "trimline_corner"
+
+/obj/effect/turf_decal/trimline/neutral/end
+ icon_state = "trimline_end"
+
+/obj/effect/turf_decal/trimline/neutral/filled
+ icon_state = "trimline_box_fill"
+
+/obj/effect/turf_decal/trimline/neutral/filled/line
+ icon_state = "trimline_fill"
+
+/obj/effect/turf_decal/trimline/neutral/filled/corner
+ icon_state = "trimline_corner_fill"
+
+/obj/effect/turf_decal/trimline/brown/filled/end
+ icon_state = "trimline_end_fill"
\ No newline at end of file
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 566a64577d..c98cef2b87 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -68,6 +68,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/strip_delay = 40 //In deciseconds, how long an item takes to remove from another person
var/breakouttime = 0
var/list/materials
+ var/reskinned = FALSE
var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
var/list/species_exception = null // list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index 1ff3c7df9f..cf706359f7 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -37,6 +37,7 @@ RLD
var/has_ammobar = FALSE //controls whether or not does update_icon apply ammo indicator overlays
var/ammo_sections = 10 //amount of divisions in the ammo indicator overlay/number of ammo indicator states
var/custom_range = 7
+ var/upgrade = FALSE
/obj/item/construction/Initialize()
. = ..()
@@ -82,6 +83,11 @@ RLD
loaded = loadwithsheets(W, sheetmultiplier * 0.25, user) // 1 matter for 1 floortile, as 4 tiles are produced from 1 metal
if(loaded)
to_chat(user, "[src] now holds [matter]/[max_matter] matter-units.")
+ else if(istype(W, /obj/item/rcd_upgrade))
+ to_chat(user, "You upgrade the RCD with the [W]!")
+ upgrade = TRUE
+ playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
+ qdel(W)
else
return ..()
update_icon() //ensures that ammo counters (if present) get updated
@@ -148,6 +154,7 @@ RLD
has_ammobar = TRUE
var/mode = 1
var/ranged = FALSE
+ var/computer_dir = 1
var/airlock_type = /obj/machinery/door/airlock
var/airlock_glass = FALSE // So the floor's rcd_act knows how much ammo to use
var/window_type = /obj/structure/window/fulltile
@@ -270,6 +277,28 @@ RLD
return FALSE
return TRUE
+/obj/item/construction/rcd/proc/change_computer_dir(mob/user)
+ if(!user)
+ return
+ var/list/computer_dirs = list(
+ "NORTH" = image(icon = 'icons/mob/radial.dmi', icon_state = "cnorth"),
+ "EAST" = image(icon = 'icons/mob/radial.dmi', icon_state = "ceast"),
+ "SOUTH" = image(icon = 'icons/mob/radial.dmi', icon_state = "csouth"),
+ "WEST" = image(icon = 'icons/mob/radial.dmi', icon_state = "cwest")
+ )
+ var/computerdirs = show_radial_menu(user, src, computer_dirs, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE)
+ if(!check_menu(user))
+ return
+ switch(computerdirs)
+ if("NORTH")
+ computer_dir = 1
+ if("EAST")
+ computer_dir = 4
+ if("SOUTH")
+ computer_dir = 2
+ if("WEST")
+ computer_dir = 8
+
/obj/item/construction/rcd/proc/change_airlock_setting(mob/user)
if(!user)
return
@@ -434,10 +463,15 @@ RLD
..()
var/list/choices = list(
"Airlock" = image(icon = 'icons/mob/radial.dmi', icon_state = "airlock"),
- "Deconstruct" = image(icon= 'icons/mob/radial.dmi', icon_state = "delete"),
"Grilles & Windows" = image(icon = 'icons/mob/radial.dmi', icon_state = "grillewindow"),
"Floors & Walls" = image(icon = 'icons/mob/radial.dmi', icon_state = "wallfloor")
)
+ if(upgrade)
+ choices += list(
+ "Deconstruct" = image(icon= 'icons/mob/radial.dmi', icon_state = "delete"),
+ "Machine Frames" = image(icon = 'icons/mob/radial.dmi', icon_state = "machine"),
+ "Computer Frames" = image(icon = 'icons/mob/radial.dmi', icon_state = "computer_dir"),
+ )
if(mode == RCD_AIRLOCK)
choices += list(
"Change Access" = image(icon = 'icons/mob/radial.dmi', icon_state = "access"),
@@ -459,6 +493,12 @@ RLD
mode = RCD_DECONSTRUCT
if("Grilles & Windows")
mode = RCD_WINDOWGRILLE
+ if("Machine Frames")
+ mode = RCD_MACHINE
+ if("Computer Frames")
+ mode = RCD_COMPUTER
+ change_computer_dir(user)
+ return
if("Change Access")
change_airlock_access(user)
return
@@ -511,6 +551,7 @@ RLD
no_ammo_message = "Insufficient charge."
desc = "A device used to rapidly build walls and floors."
canRturf = TRUE
+ upgrade = TRUE
/obj/item/construction/rcd/borg/useResource(amount, mob/user)
@@ -542,6 +583,9 @@ RLD
/obj/item/construction/rcd/loaded
matter = 160
+/obj/item/construction/rcd/loaded/upgraded
+ upgrade = TRUE
+
/obj/item/construction/rcd/combat
name = "Combat RCD"
desc = "A device used to rapidly build and deconstruct. Reload with metal, plasteel, glass or compressed matter cartridges. This RCD has been upgraded to be able to remove Rwalls!"
@@ -582,7 +626,7 @@ RLD
name = "admin RCD"
max_matter = INFINITY
matter = INFINITY
-
+ upgrade = TRUE
// Ranged RCD
@@ -776,6 +820,12 @@ RLD
return TRUE
return FALSE
+/obj/item/rcd_upgrade
+ name = "RCD advanced design disk"
+ desc = "It contains the design for machine frames, computer frames, and deconstruction."
+ icon = 'icons/obj/module.dmi'
+ icon_state = "datadisk3"
+
#undef GLOW_MODE
#undef LIGHT_MODE
#undef REMOVE_MODE
diff --git a/code/game/objects/items/RSF.dm b/code/game/objects/items/RSF.dm
index f0d15cc3a2..01205ee889 100644
--- a/code/game/objects/items/RSF.dm
+++ b/code/game/objects/items/RSF.dm
@@ -134,12 +134,14 @@ RSF
return
/obj/item/cookiesynth/emag_act(mob/user)
+ . = ..()
obj_flags ^= EMAGGED
if(obj_flags & EMAGGED)
to_chat(user, "You short out [src]'s reagent safety checker!")
else
to_chat(user, "You reset [src]'s reagent safety checker!")
- toxin = 0
+ toxin = FALSE
+ return TRUE
/obj/item/cookiesynth/attack_self(mob/user)
var/mob/living/silicon/robot/P = null
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 96bbe759ca..05ffcbf2fd 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -93,34 +93,22 @@
/obj/item/card/emag/afterattack(atom/target, mob/user, proximity)
. = ..()
var/atom/A = target
- if(!proximity && prox_check)
+ if(!proximity && prox_check || !(isobj(A) || issilicon(A) || isbot(A) || isdrone(A)))
+ return
+ if(istype(A, /obj/item/storage) && !(istype(A, /obj/item/storage/lockbox) || istype(A, /obj/item/storage/pod)))
return
- //Citadel changes: modular code misfiring, so we're bypassing into main code.
if(!uses)
user.visible_message("[src] emits a weak spark. It's burnt out!")
playsound(src, 'sound/effects/light_flicker.ogg', 100, 1)
return
else if(uses <= 3)
playsound(src, 'sound/effects/light_flicker.ogg', 30, 1) //Tiiiiiiny warning sound to let ya know your emag's almost dead
-
- if(isturf(A))
+ if(!A.emag_act(user))
return
- if(istype(A,/obj/item/storage/lockbox) || istype(A, /obj/item/storage/pod))
- A.emag_act(user)
- uses = max(uses - 1, 0)
- if(!uses)
- user.visible_message("[src] fizzles and sparks. It seems like it's out of charges.")
- playsound(src, 'sound/effects/light_flicker.ogg', 100, 1)
- if(istype(A,/obj/item/storage))
- return
- if(!(isobj(A) || issilicon(A) || isbot(A) || isdrone(A)))
- return
- else
- A.emag_act(user)
- uses = max(uses - 1, 0)
- if(!uses)
- user.visible_message("[src] fizzles and sparks. It seems like it's out of charges.")
- playsound(src, 'sound/effects/light_flicker.ogg', 100, 1)
+ uses = max(uses - 1, 0)
+ if(!uses)
+ user.visible_message("[src] fizzles and sparks. It seems like it's out of charges.")
+ playsound(src, 'sound/effects/light_flicker.ogg', 100, 1)
/obj/item/card/emagfake
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index 000c52ae43..09a128c558 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -764,20 +764,22 @@ CIGARETTE PACKETS ARE IN FANCY.DM
..()
/obj/item/clothing/mask/vape/emag_act(mob/user)// I WON'T REGRET WRITTING THIS, SURLY.
- if(screw)
- if(!(obj_flags & EMAGGED))
- cut_overlays()
- obj_flags |= EMAGGED
- super = FALSE
- to_chat(user, "You maximize the voltage of [src].")
- add_overlay("vapeopen_high")
- var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread //for effect
- sp.set_up(5, 1, src)
- sp.start()
- else
- to_chat(user, "[src] is already emagged!")
- else
+ . = ..()
+ if(!screw)
to_chat(user, "You need to open the cap to do that.")
+ return
+ if(obj_flags & EMAGGED)
+ to_chat(user, "[src] is already emagged!")
+ return
+ cut_overlays()
+ obj_flags |= EMAGGED
+ super = FALSE
+ to_chat(user, "You maximize the voltage of [src].")
+ add_overlay("vapeopen_high")
+ var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread //for effect
+ sp.set_up(5, 1, src)
+ sp.start()
+ return TRUE
/obj/item/clothing/mask/vape/attack_self(mob/user)
if(reagents.total_volume > 0)
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index 7d2ffe0a02..c260a95afd 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -162,10 +162,11 @@
/obj/item/circuitboard/computer/prisoner
name = "Prisoner Management Console (Computer Board)"
- build_path = /obj/machinery/computer/prisoner
+ build_path = /obj/machinery/computer/prisoner/management
+
/obj/item/circuitboard/computer/gulag_teleporter_console
name = "Labor Camp teleporter console (Computer Board)"
- build_path = /obj/machinery/computer/gulag_teleporter_computer
+ build_path = /obj/machinery/computer/prisoner/gulag_teleporter_computer
/obj/item/circuitboard/computer/rdconsole/production
name = "R&D Console Production Only (Computer Board)"
@@ -217,10 +218,13 @@
to_chat(user, "The spectrum chip is unresponsive.")
/obj/item/circuitboard/computer/cargo/emag_act(mob/living/user)
- if(!(obj_flags & EMAGGED))
- contraband = TRUE
- obj_flags |= EMAGGED
- to_chat(user, "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.")
+ . = ..()
+ if(obj_flags & EMAGGED)
+ return
+ contraband = TRUE
+ obj_flags |= EMAGGED
+ to_chat(user, "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.")
+ return TRUE
/obj/item/circuitboard/computer/cargo/express
name = "Express Supply Console (Computer Board)"
@@ -234,8 +238,12 @@
obj_flags &= ~EMAGGED
/obj/item/circuitboard/computer/cargo/express/emag_act(mob/living/user)
- to_chat(user, "You change the routing protocols, allowing the Drop Pod to land anywhere on the station.")
- obj_flags |= EMAGGED
+ . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
+ if(obj_flags & EMAGGED)
+ return
+ to_chat(user, "You change the routing protocols, allowing the Drop Pod to land anywhere on the station.")
+ obj_flags |= EMAGGED
+ return TRUE
/obj/item/circuitboard/computer/cargo/request
name = "Supply Request Console (Computer Board)"
@@ -366,4 +374,4 @@
/obj/item/circuitboard/computer/nanite_cloud_controller
name = "Nanite Cloud Control (Computer Board)"
- build_path = /obj/machinery/computer/nanite_cloud_controller
\ No newline at end of file
+ build_path = /obj/machinery/computer/nanite_cloud_controller
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 55e75b3992..5bef86c6d2 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -140,12 +140,10 @@
return ..()
/obj/item/defibrillator/emag_act(mob/user)
- if(safety)
- safety = FALSE
- to_chat(user, "You silently disable [src]'s safety protocols with the cryptographic sequencer.")
- else
- safety = TRUE
- to_chat(user, "You silently enable [src]'s safety protocols with the cryptographic sequencer.")
+ . = ..()
+ safety = !safety
+ to_chat(user, "You silently [safety ? "enable" : "disable"] [src]'s safety protocols with the cryptographic sequencer.")
+ return TRUE
/obj/item/defibrillator/emp_act(severity)
. = ..()
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index bf352e8eb7..3885a1f4d9 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -498,6 +498,23 @@ Code:
else
menu += "[ldat]"
+ menu += "Pimpin' Ride:"
+
+ ldat = null
+ for (var/obj/vehicle/ridden/janicart/M in world)
+ var/turf/ml = get_turf(M)
+
+ if(ml)
+ if (ml.z != cl.z)
+ continue
+ var/direction = get_dir(src, M)
+ ldat += "Ride - \[[ml.x],[ml.y] ([uppertext(dir2text(direction))])\] "
+
+ if (!ldat)
+ menu += "None"
+ else
+ menu += "[ldat]"
+
menu += "Located Janitorial Cart:"
ldat = null
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index 7823e570e0..38133e37bc 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -192,13 +192,15 @@
update_icon()
/obj/item/geiger_counter/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
if(scanning)
to_chat(user, "Turn off [src] before you perform this action!")
- return 0
+ return
to_chat(user, "You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.")
obj_flags |= EMAGGED
+ return TRUE
/obj/item/geiger_counter/cyborg
var/datum/component/mobhook
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 6615ebe8a8..1e31f14cfd 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -149,9 +149,11 @@
to_chat(user, "You fill \the [src] with lights from \the [S]. " + status_string() + "")
/obj/item/lightreplacer/emag_act()
+ . = ..()
if(obj_flags & EMAGGED)
return
Emag()
+ return TRUE
/obj/item/lightreplacer/attack_self(mob/user)
to_chat(user, status_string())
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 347bb6894d..915fcac504 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -38,11 +38,13 @@
speech_args[SPEECH_SPANS] |= voicespan
/obj/item/megaphone/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "You overload \the [src]'s voice synthesizer.")
obj_flags |= EMAGGED
voicespan = list(SPAN_REALLYBIG, "userdanger")
+ return TRUE
/obj/item/megaphone/sec
name = "security megaphone"
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index a3ab9153ce..207497922e 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -32,6 +32,24 @@
var/datum/integrated_io/selected_io = null //functional for integrated circuits.
var/mode = 0
+/obj/item/multitool/chaplain
+ name = "\improper hypertool"
+ desc = "Used for pulsing wires to test which to cut. Also emits microwaves to fry some brains!"
+ damtype = BRAIN
+ force = 18
+ armour_penetration = 35
+ hitsound = 'sound/effects/sparks4.ogg'
+ var/chaplain_spawnable = TRUE
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+ throw_speed = 3
+ throw_range = 4
+ throwforce = 10
+ obj_flags = UNIQUE_RENAME
+
+/obj/item/multitool/chaplain/Initialize()
+ . = ..()
+ AddComponent(/datum/component/anti_magic, TRUE, TRUE)
+
/obj/item/multitool/examine(mob/user)
..()
if(selected_io)
diff --git a/code/game/objects/items/his_grace.dm b/code/game/objects/items/his_grace.dm
index c6f246ab97..3be57d23f1 100644
--- a/code/game/objects/items/his_grace.dm
+++ b/code/game/objects/items/his_grace.dm
@@ -12,7 +12,7 @@
lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi'
icon = 'icons/obj/items_and_weapons.dmi'
- w_class = WEIGHT_CLASS_GIGANTIC
+ w_class = WEIGHT_CLASS_BULKY
force = 12
total_mass = TOTAL_MASS_NORMAL_ITEM // average toolbox
attack_verb = list("robusted")
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index ad4384f9f5..185875a93f 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -229,8 +229,8 @@
throwforce = 10
w_class = WEIGHT_CLASS_TINY
obj_flags = UNIQUE_RENAME
- var/reskinned = FALSE
var/chaplain_spawnable = TRUE
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
/obj/item/nullrod/Initialize()
. = ..()
@@ -247,9 +247,12 @@
/obj/item/nullrod/proc/reskin_holy_weapon(mob/M)
if(GLOB.holy_weapon_type)
return
- var/obj/item/nullrod/holy_weapon
+ var/obj/item/holy_weapon
var/list/holy_weapons_list = typesof(/obj/item/nullrod) + list(
- /obj/item/melee/transforming/energy/sword/cx/chaplain
+ /obj/item/twohanded/dualsaber/hypereutactic/chaplain,
+ /obj/item/gun/energy/laser/redtag/hitscan/chaplain,
+ /obj/item/multitool/chaplain,
+ /obj/item/melee/baseball_bat/chaplain
)
var/list/display_names = list()
for(var/V in holy_weapons_list)
@@ -273,6 +276,13 @@
qdel(src)
M.put_in_active_hand(holy_weapon)
+/obj/item/nullrod/proc/jedi_spin(mob/living/user)
+ for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
+ user.setDir(i)
+ if(i == WEST)
+ user.emote("flip")
+ sleep(1)
+
/obj/item/nullrod/godhand
icon_state = "disintegrate"
item_state = "disintegrate"
@@ -328,7 +338,6 @@
sharpness = IS_SHARP
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
/obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(attack_type == PROJECTILE_ATTACK)
@@ -522,7 +531,6 @@
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_HUGE
attack_verb = list("smashed", "bashed", "hammered", "crunched")
- total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
/obj/item/nullrod/chainsaw
name = "chainsaw hand"
@@ -585,6 +593,7 @@
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
+ force = 12
reach = 2
attack_verb = list("whipped", "lashed")
hitsound = 'sound/weapons/chainhit.ogg'
@@ -661,6 +670,44 @@
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
+/obj/item/nullrod/claymore/bostaff/attack(mob/target, mob/living/user)
+ add_fingerprint(user)
+ if((HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50))
+ to_chat(user, "You club yourself over the head with [src].")
+ user.Knockdown(60)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(2*force, BRUTE, BODY_ZONE_HEAD)
+ else
+ user.take_bodypart_damage(2*force)
+ return
+ if(iscyborg(target))
+ return ..()
+ if(!isliving(target))
+ return ..()
+ var/mob/living/carbon/C = target
+ if(C.stat || C.health < 0 || C.staminaloss > 130 )
+ to_chat(user, "It would be dishonorable to attack a foe while they cannot retaliate.")
+ return
+ if(user.a_intent == INTENT_DISARM)
+ if(!ishuman(target))
+ return ..()
+ var/mob/living/carbon/human/H = target
+ var/list/fluffmessages = list("[user] clubs [H] with [src]!", \
+ "[user] smacks [H] with the butt of [src]!", \
+ "[user] broadsides [H] with [src]!", \
+ "[user] smashes [H]'s head with [src]!", \
+ "[user] beats [H] with front of [src]!", \
+ "[user] twirls and slams [H] with [src]!")
+ H.visible_message("[pick(fluffmessages)]", \
+ "[pick(fluffmessages)]")
+ playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1)
+ H.adjustStaminaLoss(rand(12,18))
+ if(prob(25))
+ (INVOKE_ASYNC(src, .proc/jedi_spin, user))
+ else
+ return ..()
+
/obj/item/nullrod/tribal_knife
icon_state = "crysknife"
item_state = "crysknife"
@@ -674,7 +721,6 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
item_flags = SLOWS_WHILE_IN_HAND
- total_mass = TOTAL_MASS_NORMAL_ITEM
/obj/item/nullrod/tribal_knife/Initialize(mapload)
. = ..()
@@ -704,8 +750,8 @@
name = "egyptian staff"
desc = "A tutorial in mummification is carved into the staff. You could probably craft the wraps if you had some cloth."
icon = 'icons/obj/guns/magic.dmi'
- icon_state = "pharoah_sceptre"
- item_state = "pharoah_sceptre"
+ icon_state = "pharaoh_sceptre"
+ item_state = "pharaoh_sceptre"
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index cdb9c146e2..f9affc230d 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -229,7 +229,7 @@
target.LAssailant = null
else
target.LAssailant = user
- cooldown = world.time + 40
+ cooldown = world.time
user.adjustStaminaLossBuffered(getweight())//CIT CHANGE - makes swinging batons cost stamina
/obj/item/melee/classic_baton/telescopic
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index 40380dfe80..48588cf9f3 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -629,7 +629,7 @@
/obj/item/toy/plush/mothplushie
name = "insect plushie"
- desc = "An adorable stuffed toy that resembles some kind of insect"
+ desc = "An adorable stuffed toy that resembles some kind of insect."
icon_state = "bumble"
item_state = "bumble"
squeak_override = list('modular_citadel/sound/voice/mothsqueak.ogg' = 1)
@@ -773,8 +773,10 @@
item_state = "blep"
/obj/item/toy/plush/mammal/circe
+ desc = "A luxuriously soft toy that resembles a nine-tailed kitsune."
icon_state = "circe"
item_state = "circe"
+ attack_verb = list("medicated", "tailhugged", "kissed")
/obj/item/toy/plush/mammal/robin
icon_state = "robin"
@@ -839,8 +841,10 @@
item_state = "rae"
/obj/item/toy/plush/mammal/zed
+ desc = "A masked stuffed toy that resembles a fierce miner. He even comes with his own little crusher!"
icon_state = "zed"
item_state = "zed"
+ attack_verb = list("ENDED", "CRUSHED", "GNOMED")
/obj/item/toy/plush/mammal/justin
icon_state = "justin"
@@ -852,6 +856,12 @@
item_state = "reece"
attack_verb = list("healed", "cured", "demoted")
+/obj/item/toy/plush/mammal/redwood
+ desc = "An adorable stuffed toy resembling a Nanotrasen Captain. That just happens to be a bunny."
+ icon_state = "redwood"
+ item_state = "redwood"
+ attack_verb = list("ordered", "bapped", "reprimanded")
+
/obj/item/toy/plush/mammal/dog
desc = "An adorable stuffed toy that resembles a canine."
icon_state = "katlin"
@@ -898,6 +908,12 @@
obj_flags = UNIQUE_RENAME
unique_reskin = list("Goodboye" = "fritz", "Badboye" = "fritz_bad")
+/obj/item/toy/plush/mammal/dog/jesse
+ desc = "An adorable wolf toy that resembles a cream-colored wolf. He has a little pride flag!"
+ icon_state = "jesse"
+ item_state = "jesse"
+ attack_verb = list("greeted", "merc'd", "howdy'd")
+
/obj/item/toy/plush/catgirl
name = "feline plushie"
desc = "An adorable stuffed toy that resembles a feline."
@@ -935,3 +951,15 @@
item_state = "fermis"
attack_verb = list("cuddled", "petpatted", "wigglepurred")
squeak_override = list('modular_citadel/sound/voice/merowr.ogg' = 1)
+
+/obj/item/toy/plush/catgirl/mariaf
+ desc = "An adorable stuffed toy that resembles a very tall cat girl."
+ icon_state = "mariaf"
+ item_state = "mariaf"
+ attack_verb = list("hugged", "stabbed", "licked")
+
+/obj/item/toy/plush/catgirl/maya
+ desc = "An adorable stuffed toy that resembles an angry cat girl. She has her own tiny nuke disk!"
+ icon_state = "maya"
+ item_state = "maya"
+ attack_verb = list("nuked", "arrested", "harmbatonned")
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index 6d7c3036f9..5454b0fdb8 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -282,11 +282,13 @@
var/cooldown = 0
/obj/item/harmalarm/emag_act(mob/user)
+ . = ..()
obj_flags ^= EMAGGED
if(obj_flags & EMAGGED)
to_chat(user, "You short out the safeties on [src]!")
else
to_chat(user, "You reset the safeties on [src]!")
+ return TRUE
/obj/item/harmalarm/attack_self(mob/user)
var/safety = !(obj_flags & EMAGGED)
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index cf967e25ba..47c881bbdf 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -23,7 +23,7 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100)
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/glass
- grind_results = list("silicon" = 20)
+ grind_results = list(/datum/reagent/silicon = 20)
point_value = 1
/obj/item/stack/sheet/glass/suicide_act(mob/living/carbon/user)
@@ -87,7 +87,7 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 100)
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/plasmaglass
- grind_results = list("silicon" = 20, "plasma" = 10)
+ grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10)
/obj/item/stack/sheet/plasmaglass/fifty
amount = 50
@@ -138,7 +138,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100)
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/rglass
- grind_results = list("silicon" = 20, "iron" = 10)
+ grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10)
point_value = 4
/obj/item/stack/sheet/rglass/attackby(obj/item/W, mob/user, params)
@@ -177,11 +177,11 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \
singular_name = "reinforced plasma glass sheet"
icon_state = "sheet-prglass"
item_state = "sheet-prglass"
- materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT, MAT_METAL = MINERAL_MATERIAL_AMOUNT * 0.5,)
+ materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT, MAT_METAL=MINERAL_MATERIAL_AMOUNT * 0.5,)
armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/plasmarglass
- grind_results = list("silicon" = 20, "plasma" = 10, "iron" = 10)
+ grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10)
point_value = 23
/obj/item/stack/sheet/plasmarglass/Initialize(mapload, new_amount, merge = TRUE)
@@ -243,8 +243,9 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
resistance_flags = ACID_PROOF
armor = list("melee" = 100, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100)
max_integrity = 40
- var/cooldown = 0
sharpness = IS_SHARP
+ var/icon_prefix
+
/obj/item/shard/suicide_act(mob/user)
user.visible_message("[user] is slitting [user.p_their()] [pick("wrists", "throat")] with the shard of glass! It looks like [user.p_theyre()] trying to commit suicide.")
@@ -266,9 +267,19 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
if("large")
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
- var/matrix/M = matrix(transform)
- M.Turn(rand(-170, 170))
- transform = M
+ if (icon_prefix)
+ icon_state = "[icon_prefix][icon_state]"
+
+ var/turf/T = get_turf(src)
+ if(T && is_station_level(T.z))
+ SSblackbox.record_feedback("tally", "station_mess_created", 1, name)
+
+/obj/item/shard/Destroy()
+ . = ..()
+
+ var/turf/T = get_turf(src)
+ if(T && is_station_level(T.z))
+ SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name)
/obj/item/shard/afterattack(atom/A as mob|obj, mob/user, proximity)
. = ..()
@@ -298,6 +309,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
return ..()
/obj/item/shard/welder_act(mob/living/user, obj/item/I)
+ ..()
if(I.use_tool(src, user, 0, volume=50))
var/obj/item/stack/sheet/glass/NG = new (user.loc)
for(var/obj/item/stack/sheet/glass/G in user.loc)
@@ -316,4 +328,13 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
playsound(loc, 'sound/effects/glass_step.ogg', 30, 1)
else
playsound(loc, 'sound/effects/glass_step.ogg', 50, 1)
- . = ..()
+ return ..()
+
+/obj/item/shard/plasma
+ name = "purple shard"
+ desc = "A nasty looking shard of plasma glass."
+ force = 6
+ throwforce = 11
+ icon_state = "plasmalarge"
+ materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT)
+ icon_prefix = "plasma"
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c65129ec85..8c808d0e5f 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -201,6 +201,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("dresser", /obj/structure/dresser, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
new/datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("loom", /obj/structure/loom, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \
new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
new/datum/stack_recipe("tiki mask", /obj/item/clothing/mask/gas/tiki_mask, 2), \
@@ -278,6 +279,31 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
/obj/item/stack/sheet/cloth/ten
amount = 10
+//Durathread fuck slash-asterisk comments
+ GLOBAL_LIST_INIT(durathread_recipes, list ( \
+ new/datum/stack_recipe("durathread jumpsuit", /obj/item/clothing/under/durathread, 4, time = 40),
+ new/datum/stack_recipe("durathread beret", /obj/item/clothing/head/beret/durathread, 2, time = 40), \
+ new/datum/stack_recipe("durathread beanie", /obj/item/clothing/head/beanie/durathread, 2, time = 40), \
+ new/datum/stack_recipe("durathread bandana", /obj/item/clothing/mask/bandana/durathread, 1, time = 25), \
+ ))
+
+/obj/item/stack/sheet/durathread
+ name = "durathread"
+ desc = "A fabric sown from incredibly durable threads, known for its usefulness in armor production."
+ singular_name = "durathread roll"
+ icon_state = "sheet-durathread"
+ item_state = "sheet-cloth"
+ resistance_flags = FLAMMABLE
+ force = 0
+ throwforce = 0
+ merge_type = /obj/item/stack/sheet/durathread
+
+/obj/item/stack/sheet/durathread/Initialize(mapload, new_amount, merge = TRUE)
+ recipes = GLOB.durathread_recipes
+ return ..()
+
+
+
/*
* Cardboard
*/
@@ -603,3 +629,29 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
amount = 20
/obj/item/stack/sheet/paperframes/fifty
amount = 50
+
+
+//durathread and cotton raw
+/obj/item/stack/sheet/cotton
+ name = "raw cotton bundle"
+ desc = "A bundle of raw cotton ready to be spun on the loom."
+ singular_name = "raw cotton ball"
+ icon_state = "sheet-cotton"
+ is_fabric = TRUE
+ resistance_flags = FLAMMABLE
+ force = 0
+ throwforce = 0
+ merge_type = /obj/item/stack/sheet/cotton
+ pull_effort = 30
+ loom_result = /obj/item/stack/sheet/cloth
+
+/obj/item/stack/sheet/cotton/durathread
+ name = "raw durathread bundle"
+ desc = "A bundle of raw durathread ready to be spun on the loom."
+ singular_name = "raw durathread ball"
+ icon_state = "sheet-durathreadraw"
+ merge_type = /obj/item/stack/sheet/cotton/durathread
+ pull_effort = 70
+ loom_result = /obj/item/stack/sheet/durathread
+
+
diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm
index 21b43eba20..908fc88383 100644
--- a/code/game/objects/items/stacks/sheets/sheets.dm
+++ b/code/game/objects/items/stacks/sheets/sheets.dm
@@ -12,4 +12,7 @@
novariants = FALSE
var/perunit = MINERAL_MATERIAL_AMOUNT
var/sheettype = null //this is used for girders in the creation of walls/false walls
- var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity.
\ No newline at end of file
+ var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity
+ var/is_fabric = FALSE //is this a valid material for the loom?
+ var/loom_result //result from pulling on the loom
+ var/pull_effort = 0 //amount of delay when pulling on the loom
\ No newline at end of file
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index eb71311c96..b5d7282b07 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -248,12 +248,13 @@
desc = "A bone satchel fashend with watcher wings and large bones from goliath. Can be worn on the belt."
icon = 'icons/obj/mining.dmi'
icon_state = "goliath_saddle"
- slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT
+ slot_flags = ITEM_SLOT_BACK
/obj/item/storage/backpack/satchel/bone/ComponentInitialize()
. = ..()
GET_COMPONENT(STR, /datum/component/storage)
- STR.max_combined_w_class = 10
+ STR.max_combined_w_class = 20
+ STR.max_items = 15
/obj/item/storage/backpack/satchel/cap
name = "captain's satchel"
@@ -355,6 +356,7 @@
new /obj/item/cautery(src)
new /obj/item/surgical_drapes(src)
new /obj/item/clothing/mask/surgical(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
new /obj/item/razor(src)
/obj/item/storage/backpack/duffelbag/sec
@@ -376,6 +378,7 @@
new /obj/item/cautery(src)
new /obj/item/surgical_drapes(src)
new /obj/item/clothing/mask/surgical(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
/obj/item/storage/backpack/duffelbag/engineering
name = "industrial duffel bag"
@@ -383,6 +386,14 @@
icon_state = "duffel-eng"
item_state = "duffel-eng"
+/obj/item/storage/backpack/duffelbag/durathread
+ name = "durathread duffel bag"
+ desc = "A lightweight duffel bag made out of durathread."
+ icon_state = "duffel-durathread"
+ item_state = "duffel-durathread"
+ resistance_flags = FIRE_PROOF
+ slowdown = 0
+
/obj/item/storage/backpack/duffelbag/drone
name = "drone duffel bag"
desc = "A large duffel bag for holding tools and hats."
@@ -398,6 +409,7 @@
new /obj/item/stack/cable_coil/random(src)
new /obj/item/wirecutters(src)
new /obj/item/multitool(src)
+ new /obj/item/pipe_dispenser(src)
/obj/item/storage/backpack/duffelbag/clown
name = "clown's duffel bag"
@@ -460,6 +472,7 @@
new /obj/item/mmi/syndie(src)
new /obj/item/implantcase(src)
new /obj/item/implanter(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
/obj/item/storage/backpack/duffelbag/syndie/surgery_adv
name = "advanced surgery duffel bag"
@@ -479,6 +492,7 @@
new /obj/item/mmi/syndie(src)
new /obj/item/implantcase(src)
new /obj/item/implanter(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
/obj/item/storage/backpack/duffelbag/syndie/ammo
name = "ammunition duffel bag"
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index d71b91c8dc..4495e9da14 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -431,6 +431,48 @@
GET_COMPONENT(STR, /datum/component/storage)
STR.max_items = 6
+/obj/item/storage/belt/durathread
+ name = "durathread toolbelt"
+ desc = "A toolbelt made out of durathread, it seems resistant enough to hold even big tools like an RCD, it also has higher capacity."
+ icon_state = "webbing-durathread"
+ item_state = "webbing-durathread"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/belt/durathread/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 14
+ STR.max_combined_w_class = 32
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/crowbar,
+ /obj/item/screwdriver,
+ /obj/item/weldingtool,
+ /obj/item/wirecutters,
+ /obj/item/wrench,
+ /obj/item/multitool,
+ /obj/item/flashlight,
+ /obj/item/stack/cable_coil,
+ /obj/item/t_scanner,
+ /obj/item/analyzer,
+ /obj/item/geiger_counter,
+ /obj/item/extinguisher/mini,
+ /obj/item/radio,
+ /obj/item/clothing/gloves,
+ /obj/item/holosign_creator/atmos,
+ /obj/item/holosign_creator/engineering,
+ /obj/item/forcefield_projector,
+ /obj/item/assembly/signaler,
+ /obj/item/lightreplacer,
+ /obj/item/rcd_ammo,
+ /obj/item/construction/rcd,
+ /obj/item/pipe_dispenser,
+ /obj/item/stack/rods,
+ /obj/item/stack/tile/plasteel,
+ /obj/item/grenade/chem_grenade/metalfoam,
+ /obj/item/grenade/chem_grenade/smart_metal_foam
+ ))
+
/obj/item/storage/belt/grenade
name = "grenadier belt"
desc = "A belt for holding grenades."
@@ -524,12 +566,15 @@
/obj/item/grenade/chem_grenade,
/obj/item/lightreplacer,
/obj/item/flashlight,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
/obj/item/reagent_containers/spray,
/obj/item/soap,
/obj/item/holosign_creator,
/obj/item/key/janitor,
/obj/item/clothing/gloves,
/obj/item/melee/flyswatter,
+ /obj/item/paint/paint_remover,
/obj/item/assembly/mousetrap
))
@@ -548,6 +593,22 @@
/obj/item/ammo_casing/shotgun
))
+/obj/item/storage/belt/bandolier/durathread
+ name = "durathread bandolier"
+ desc = "An double stacked bandolier made out of durathread."
+ icon_state = "bandolier-durathread"
+ item_state = "bandolier-durathread"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/belt/bandolier/durathread/ComponentInitialize()
+ . = ..()
+ GET_COMPONENT(STR, /datum/component/storage)
+ STR.max_items = 32
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_casing
+ ))
+
/obj/item/storage/belt/medolier
name = "medolier"
desc = "A medical bandolier for holding smartdarts."
diff --git a/code/game/objects/items/storage/briefcase.dm b/code/game/objects/items/storage/briefcase.dm
index bca13f2a45..ed547bc17d 100644
--- a/code/game/objects/items/storage/briefcase.dm
+++ b/code/game/objects/items/storage/briefcase.dm
@@ -40,9 +40,18 @@
/obj/item/storage/briefcase/lawyer/family
name = "battered briefcase"
- desc = "An old briefcase, this one has seen better days in its time. It's clear they don't make them nowadays as good as they used to. Comes with an added belt clip!"
+ icon_state = "gbriefcase"
+ lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi'
+ desc = "An old briefcase with a golden trim. It's clear they don't make them as good as they used to. Comes with an added belt clip!"
slot_flags = ITEM_SLOT_BELT
+/obj/item/storage/briefcase/lawyer/family/ComponentInitialize()
+ . = ..()
+ GET_COMPONENT(STR, /datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_combined_w_class = 14
+
/obj/item/storage/briefcase/lawyer/family/PopulateContents()
new /obj/item/stamp/law(src)
new /obj/item/pen/fountain(src)
diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm
index 4924051855..eeebc6f4c5 100644
--- a/code/game/objects/items/storage/lockbox.dm
+++ b/code/game/objects/items/storage/lockbox.dm
@@ -48,14 +48,16 @@
to_chat(user, "It's locked!")
/obj/item/storage/lockbox/emag_act(mob/user)
- if(!broken)
- broken = TRUE
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
- desc += "It appears to be broken."
- icon_state = src.icon_broken
- if(user)
- visible_message("\The [src] has been broken by [user] with an electromagnetic card!")
- return
+ . = ..()
+ if(broken)
+ return
+ broken = TRUE
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
+ desc += "It appears to be broken."
+ icon_state = src.icon_broken
+ if(user)
+ visible_message("\The [src] has been broken by [user] with an electromagnetic card!")
+ return TRUE
/obj/item/storage/lockbox/Entered()
. = ..()
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index a6a3cea373..6e41527b24 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -132,7 +132,7 @@
new /obj/item/card/emag(src) // 6 tc
if("ninja") // 40~ tc worth
- new /obj/item/katana(src) // Unique , basicly a better esword. 10 tc?
+ new /obj/item/katana(src) // Unique , basicly a better esword. 10 tc?
new /obj/item/implanter/adrenalin(src) // 8 tc
new /obj/item/throwing_star(src) // ~5 tc for all 6
new /obj/item/throwing_star(src)
@@ -294,6 +294,7 @@
new /obj/item/radio/headset/chameleon(src)
new /obj/item/stamp/chameleon(src)
new /obj/item/pda/chameleon(src)
+ new /obj/item/clothing/neck/cloak/chameleon(src)
//5*(2*4) = 5*8 = 45, 45 damage if you hit one person with all 5 stars.
//Not counting the damage it will do while embedded (2*4 = 8, at 15% chance)
diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm
index c263c2669d..cb5790e45f 100644
--- a/code/game/objects/items/storage/wallets.dm
+++ b/code/game/objects/items/storage/wallets.dm
@@ -36,7 +36,8 @@
/obj/item/reagent_containers/syringe,
/obj/item/screwdriver,
/obj/item/valentine,
- /obj/item/stamp))
+ /obj/item/stamp,
+ /obj/item/key))
/obj/item/storage/wallet/Exited(atom/movable/AM)
. = ..()
@@ -60,7 +61,7 @@
/obj/item/storage/wallet/update_icon()
var/new_state = "wallet"
if(front_id)
- new_state = "wallet_[front_id.icon_state]"
+ new_state = "wallet_id"
if(new_state != icon_state) //avoid so many icon state changes.
icon_state = new_state
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index 781a82513e..82bf2c6b5f 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -487,6 +487,7 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
var/obj/item/grenade/explosive = null
var/war_cry = "AAAAARGH!!!"
+ var/icon_prefix = "spearglass"
/obj/item/twohanded/spear/Initialize()
. = ..()
@@ -529,7 +530,7 @@
if(explosive)
icon_state = "spearbomb[wielded]"
else
- icon_state = "spearglass[wielded]"
+ icon_state = "[icon_prefix][wielded]"
/obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity)
. = ..()
@@ -556,6 +557,13 @@
src.war_cry = input
/obj/item/twohanded/spear/CheckParts(list/parts_list)
+ var/obj/item/shard/tip = locate() in parts_list
+ if (istype(tip, /obj/item/shard/plasma))
+ force_wielded = 19
+ force_unwielded = 11
+ throwforce = 21
+ icon_prefix = "spearplasma"
+ qdel(tip)
var/obj/item/twohanded/spear/S = locate() in parts_list
if(S)
if(S.explosive)
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 560731edfd..c6384e7438 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -229,7 +229,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/katana/cursed
slot_flags = null
-/obj/item/katana/Initialize()
+/obj/item/katana/cursed/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT)
@@ -255,7 +255,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear
remove_item_from_storage(user)
- qdel(I)
+ if (!user.transferItemToLoc(I, S))
+ return
+ S.CheckParts(list(I))
qdel(src)
user.put_in_hands(S)
@@ -513,6 +515,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
var/homerun_able = 0
total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google
+/obj/item/melee/baseball_bat/chaplain
+ name = "blessed baseball bat"
+ desc = "There ain't a cult in the league that can withstand a swatter."
+ force = 14
+ throwforce = 14
+ obj_flags = UNIQUE_RENAME
+ var/chaplain_spawnable = TRUE
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+
+/obj/item/melee/baseball_bat/chaplain/Initialize()
+ . = ..()
+ AddComponent(/datum/component/anti_magic, TRUE, TRUE)
+
/obj/item/melee/baseball_bat/homerun
name = "home run bat"
desc = "This thing looks dangerous... Dangerously good at baseball, that is."
diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 1ab28a33de..2093ae5660 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -112,12 +112,16 @@
/obj/structure/sign/barsign/emag_act(mob/user)
+ . = ..()
if(broken || (obj_flags & EMAGGED))
to_chat(user, "Nothing interesting happens!")
return
obj_flags |= EMAGGED
to_chat(user, "You emag the barsign. Takeover in progress...")
- sleep(10 SECONDS)
+ addtimer(CALLBACK(src, .proc/syndie_bar_good), 10 SECONDS)
+ return TRUE
+
+/obj/structure/sign/barsign/proc/syndie_bar_good()
set_sign(new /datum/barsign/hiddensigns/syndibarsign)
req_access = list(ACCESS_SYNDICATE)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index f097d2903a..edcb4a6181 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -1,616 +1,617 @@
-/obj/structure/closet
- name = "closet"
- desc = "It's a basic storage unit."
- icon = 'icons/obj/closet.dmi'
- icon_state = "generic"
- density = TRUE
- layer = BELOW_OBJ_LAYER
- var/icon_door = null
- var/icon_door_override = FALSE //override to have open overlay use icon different to its base's
- var/secure = FALSE //secure locker or not, also used if overriding a non-secure locker with a secure door overlay to add fancy lights
- var/opened = FALSE
- var/welded = FALSE
- var/locked = FALSE
- var/large = TRUE
- var/wall_mounted = 0 //never solid (You can always pass over it)
- max_integrity = 200
- integrity_failure = 50
- armor = list("melee" = 20, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
- var/breakout_time = 1200
- var/message_cooldown
- var/can_weld_shut = TRUE
- var/horizontal = FALSE
- var/allow_objects = FALSE
- var/allow_dense = FALSE
- var/dense_when_open = FALSE //if it's dense when open or not
- var/max_mob_size = MOB_SIZE_HUMAN //Biggest mob_size accepted by the container
- var/mob_storage_capacity = 3 // how many human sized mob/living can fit together inside a closet.
- var/storage_capacity = 30 //This is so that someone can't pack hundreds of items in a locker/crate then open it in a populated area to crash clients.
- var/cutting_tool = /obj/item/weldingtool
- var/open_sound = 'sound/machines/click.ogg'
- var/close_sound = 'sound/machines/click.ogg'
- var/material_drop = /obj/item/stack/sheet/metal
- var/material_drop_amount = 2
- var/delivery_icon = "deliverycloset" //which icon to use when packagewrapped. null to be unwrappable.
- var/anchorable = TRUE
- var/icon_welded = "welded"
- var/obj/item/electronics/airlock/lockerelectronics //Installed electronics
- var/lock_in_use = FALSE //Someone is doing some stuff with the lock here, better not proceed further
- var/eigen_teleport = FALSE //If the closet leads to Mr Tumnus.
- var/obj/structure/closet/eigen_target //Where you go to.
-
-
-/obj/structure/closet/Initialize(mapload)
- . = ..()
- update_icon()
- PopulateContents()
- if(mapload && !opened) // if closed, any item at the crate's loc is put in the contents
- take_contents()
- if(secure)
- lockerelectronics = new(src)
- lockerelectronics.accesses = req_access
-
-//USE THIS TO FILL IT, NOT INITIALIZE OR NEW
-/obj/structure/closet/proc/PopulateContents()
- return
-
-/obj/structure/closet/Destroy()
- dump_contents(override = FALSE)
- return ..()
-
-/obj/structure/closet/update_icon()
- cut_overlays()
- if(opened & icon_door_override)
- add_overlay("[icon_door]_open")
- layer = OBJ_LAYER
- return
- else if(opened)
- add_overlay("[icon_state]_open")
- return
- if(icon_door)
- add_overlay("[icon_door]_door")
- else
- layer = BELOW_OBJ_LAYER
- add_overlay("[icon_state]_door")
- if(welded)
- add_overlay("welded")
- if(!secure)
- return
- if(broken)
- add_overlay("off")
- add_overlay("sparking")
- else if(locked)
- add_overlay("locked")
- else
- add_overlay("unlocked")
-
-/obj/structure/closet/examine(mob/user)
- ..()
- if(welded)
- to_chat(user, "It's welded shut.")
- if(anchored)
- to_chat(user, "It is bolted to the ground.")
- if(opened)
- to_chat(user, "The parts are welded together.")
- else if(secure && !opened)
- else if(broken)
- to_chat(user, "The lock is screwed in.")
- else if(secure)
- to_chat(user, "Alt-click to [locked ? "unlock" : "lock"].")
- if(isliving(user))
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_SKITTISH))
- to_chat(user, "Ctrl-Shift-click [src] to jump inside.")
-
-/obj/structure/closet/CanPass(atom/movable/mover, turf/target)
- if(wall_mounted)
- return TRUE
- return !density
-
-/obj/structure/closet/proc/can_open(mob/living/user)
- if(welded || locked)
- return FALSE
- var/turf/T = get_turf(src)
- for(var/mob/living/L in T)
- if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
- if(user)
- to_chat(user, "There's something large on top of [src], preventing it from opening." )
- return FALSE
- return TRUE
-
-/obj/structure/closet/proc/can_close(mob/living/user)
- var/turf/T = get_turf(src)
- for(var/obj/structure/closet/closet in T)
- if(closet != src && !closet.wall_mounted)
- return FALSE
- for(var/mob/living/L in T)
- if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
- if(user)
- to_chat(user, "There's something too large in [src], preventing it from closing.")
- return FALSE
- return TRUE
-
-/obj/structure/closet/proc/can_lock(mob/living/user, var/check_access = TRUE) //set check_access to FALSE if you only need to check if a locker has a functional lock rather than access
- if(!secure)
- return FALSE
- if(broken)
- to_chat(user, "[src] is broken!")
- return FALSE
- if(QDELETED(lockerelectronics) && !locked) //We want to be able to unlock it regardless of electronics, but only lockable with electronics
- to_chat(user, "[src] is missing locker electronics!")
- return FALSE
- if(!check_access)
- return TRUE
- if(allowed(user))
- return TRUE
- to_chat(user, "Access denied.")
-
-/obj/structure/closet/proc/togglelock(mob/living/user)
- add_fingerprint(user)
- if(eigen_target)
- return
- if(opened)
- return
- if(!can_lock(user))
- return
- locked = !locked
- user.visible_message("[user] [locked ? null : "un"]locks [src].",
- "You [locked ? null : "un"]lock [src].")
- update_icon()
-
-/obj/structure/closet/proc/dump_contents(var/override = TRUE) //Override is for not revealing the locker electronics when you open the locker, for example
- var/atom/L = drop_location()
- for(var/atom/movable/AM in src)
- if(AM == lockerelectronics && override)
- continue
- AM.forceMove(L)
- if(throwing) // you keep some momentum when getting out of a thrown closet
- step(AM, dir)
- if(throwing)
- throwing.finalize(FALSE)
-
-/obj/structure/closet/proc/take_contents()
- var/atom/L = drop_location()
- for(var/atom/movable/AM in L)
- if(AM != src && insert(AM) == -1) // limit reached
- break
-
-/obj/structure/closet/proc/open(mob/living/user)
- if(opened || !can_open(user))
- return
- playsound(loc, open_sound, 15, 1, -3)
- opened = TRUE
- if(!dense_when_open)
- density = FALSE
- climb_time *= 0.5 //it's faster to climb onto an open thing
- dump_contents()
- update_icon()
- return 1
-
-/obj/structure/closet/proc/insert(atom/movable/AM)
- if(contents.len >= storage_capacity)
- return -1
- if(insertion_allowed(AM))
- if(eigen_teleport) // For teleporting people with linked lockers.
- do_teleport(AM, get_turf(eigen_target), 0)
- if(eigen_target.opened == FALSE)
- eigen_target.bust_open()
- else
- AM.forceMove(src)
- return TRUE
- else
- return FALSE
-
-
-/obj/structure/closet/proc/insertion_allowed(atom/movable/AM)
- if(ismob(AM))
- if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets...
- return FALSE
- var/mob/living/L = AM
- if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
- return FALSE
- if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
- if(horizontal && L.density)
- return FALSE
- if(L.mob_size > max_mob_size)
- return FALSE
- var/mobs_stored = 0
- for(var/mob/living/M in contents)
- if(++mobs_stored >= mob_storage_capacity)
- return FALSE
- L.stop_pulling()
-
- else if(istype(AM, /obj/structure/closet))
- return FALSE
-
- else if(istype(AM, /obj/effect))
- return FALSE
-
- else if(isobj(AM))
- if((!allow_dense && AM.density) || AM.anchored || AM.has_buckled_mobs())
- return FALSE
- if(isitem(AM) && !HAS_TRAIT(AM, TRAIT_NODROP))
- return TRUE
- else if(!allow_objects && !istype(AM, /obj/effect/dummy/chameleon))
- return FALSE
- else
- return FALSE
-
- return TRUE
-
-/obj/structure/closet/proc/close(mob/living/user)
- if(!opened || !can_close(user))
- return FALSE
- take_contents()
- playsound(loc, close_sound, 15, 1, -3)
- climb_time = initial(climb_time)
- opened = FALSE
- density = TRUE
- update_icon()
- return TRUE
-
-/obj/structure/closet/proc/toggle(mob/living/user)
- if(opened)
- return close(user)
- else
- return open(user)
-
-/obj/structure/closet/proc/bust_open()
- welded = FALSE //applies to all lockers
- locked = FALSE //applies to critter crates and secure lockers only
- broken = TRUE //applies to secure lockers only
- open()
-
-/obj/structure/closet/proc/handle_lock_addition(mob/user, obj/item/electronics/airlock/E)
- add_fingerprint(user)
- if(lock_in_use)
- to_chat(user, "Wait for work on [src] to be done first!")
- return
- if(secure)
- to_chat(user, "This locker already has a lock!")
- return
- if(broken)
- to_chat(user, "Unscrew the broken lock first!")
- return
- if(!istype(E))
- return
- user.visible_message("[user] begins installing a lock on [src]...","You begin installing a lock on [src]...")
- lock_in_use = TRUE
- playsound(loc, 'sound/items/screwdriver.ogg', 50, 1)
- if(!do_after(user, 60, target = src))
- lock_in_use = FALSE
- return
- lock_in_use = FALSE
- to_chat(user, "You finish the lock on [src]!")
- E.forceMove(src)
- lockerelectronics = E
- req_access = E.accesses
- secure = TRUE
- update_icon()
- return TRUE
-
-/obj/structure/closet/proc/handle_lock_removal(mob/user, obj/item/screwdriver/S)
- if(lock_in_use)
- to_chat(user, "Wait for work on [src] to be done first!")
- return
- if(locked)
- to_chat(user, "Unlock it first!")
- return
- if(!secure)
- to_chat(user, "[src] doesn't have a lock that you can remove!")
- return
- if(!istype(S))
- return
- var/brokenword = broken ? "broken " : null
- user.visible_message("[user] begins removing the [brokenword]lock on [src]...","You begin removing the [brokenword]lock on [src]...")
- playsound(loc, S.usesound, 50, 1)
- lock_in_use = TRUE
- if(!do_after(user, 100 * S.toolspeed, target = src))
- lock_in_use = FALSE
- return
- to_chat(user, "You remove the [brokenword]lock from [src]!")
- if(!QDELETED(lockerelectronics))
- lockerelectronics.add_fingerprint(user)
- lockerelectronics.forceMove(user.loc)
- lockerelectronics = null
- req_access = null
- secure = FALSE
- broken = FALSE
- locked = FALSE
- lock_in_use = FALSE
- update_icon()
- return TRUE
-
-
-/obj/structure/closet/deconstruct(disassembled = TRUE)
- if(ispath(material_drop) && material_drop_amount && !(flags_1 & NODECONSTRUCT_1))
- new material_drop(loc, material_drop_amount)
- qdel(src)
-
-/obj/structure/closet/obj_break(damage_flag)
- if(!broken && !(flags_1 & NODECONSTRUCT_1))
- bust_open()
-
-/obj/structure/closet/attackby(obj/item/W, mob/user, params)
- if(user in src)
- return
- if(src.tool_interact(W,user))
- return 1 // No afterattack
- else
- return ..()
-
-/obj/structure/closet/proc/tool_interact(obj/item/W, mob/user)//returns TRUE if attackBy call shouldnt be continued (because tool was used/closet was of wrong type), FALSE if otherwise
- . = TRUE
- if(opened)
- if(istype(W, cutting_tool))
- if(istype(W, /obj/item/weldingtool))
- if(!W.tool_start_check(user, amount=0))
- return
-
- to_chat(user, "You begin cutting \the [src] apart...")
- if(W.use_tool(src, user, 40, volume=50))
- if(eigen_teleport)
- to_chat(user, "The unstable nature of \the [src] makes it impossible to cut!")
- return
- if(!opened)
- return
- user.visible_message("[user] slices apart \the [src].",
- "You cut \the [src] apart with \the [W].",
- "You hear welding.")
- deconstruct(TRUE)
- return
- else // for example cardboard box is cut with wirecutters
- user.visible_message("[user] cut apart \the [src].", \
- "You cut \the [src] apart with \the [W].")
- deconstruct(TRUE)
- return
- if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too
- return TRUE
- else if(istype(W, /obj/item/electronics/airlock))
- handle_lock_addition(user, W)
- else if(istype(W, /obj/item/screwdriver))
- handle_lock_removal(user, W)
- else if(istype(W, /obj/item/weldingtool) && can_weld_shut)
- if(!W.tool_start_check(user, amount=0))
- return
-
- to_chat(user, "You begin [welded ? "unwelding":"welding"] \the [src]...")
- if(W.use_tool(src, user, 40, volume=50))
- if(eigen_teleport)
- to_chat(user, "The unstable nature of \the [src] makes it impossible to weld!")
- return
- if(opened)
- return
- welded = !welded
- after_weld(welded)
- user.visible_message("[user] [welded ? "welds shut" : "unwelds"] \the [src].",
- "You [welded ? "weld" : "unwelded"] \the [src] with \the [W].",
- "You hear welding.")
- update_icon()
- else if(istype(W, /obj/item/wrench) && anchorable)
- if(isinspace() && !anchored)
- return
- setAnchored(!anchored)
- W.play_tool_sound(src, 75)
- user.visible_message("[user] [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
- "You [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
- "You hear a ratchet.")
- else if(user.a_intent != INTENT_HARM && !(W.item_flags & NOBLUDGEON))
- if(W.GetID() || !toggle(user))
- togglelock(user)
- else
- return FALSE
-
-/obj/structure/closet/proc/after_weld(weld_state)
- return
-
-/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/living/user)
- if(!istype(O) || O.anchored || istype(O, /obj/screen))
- return
- if(!istype(user) || user.incapacitated() || user.lying)
- return
- if(!Adjacent(user) || !user.Adjacent(O))
- return
- if(user == O) //try to climb onto it
- return ..()
- if(!opened)
- return
- if(!isturf(O.loc))
- return
-
- var/actuallyismob = 0
- if(isliving(O))
- actuallyismob = 1
- else if(!isitem(O))
- return
- var/turf/T = get_turf(src)
- var/list/targets = list(O, src)
- add_fingerprint(user)
- user.visible_message("[user] [actuallyismob ? "tries to ":""]stuff [O] into [src].", \
- "You [actuallyismob ? "try to ":""]stuff [O] into [src].", \
- "You hear clanging.")
- if(actuallyismob)
- if(do_after_mob(user, targets, 40))
- user.visible_message("[user] stuffs [O] into [src].", \
- "You stuff [O] into [src].", \
- "You hear a loud metal bang.")
- var/mob/living/L = O
- if(!issilicon(L))
- L.Knockdown(40)
- O.forceMove(T)
- close()
- else
- O.forceMove(T)
- return 1
-
-/obj/structure/closet/relaymove(mob/user)
- if(user.stat || !isturf(loc) || !isliving(user))
- return
- if(locked)
- if(message_cooldown <= world.time)
- message_cooldown = world.time + 50
- to_chat(user, "[src]'s door won't budge!")
- return
- container_resist(user)
-
-/obj/structure/closet/attack_hand(mob/user)
- . = ..()
- if(.)
- return
- if(user.lying && get_dist(src, user) > 0)
- return
-
- if(!toggle(user))
- togglelock(user)
-
-/obj/structure/closet/attack_paw(mob/user)
- return attack_hand(user)
-
-/obj/structure/closet/attack_robot(mob/user)
- if(user.Adjacent(src))
- return attack_hand(user)
-
-// tk grab then use on self
-/obj/structure/closet/attack_self_tk(mob/user)
- return attack_hand(user)
-
-/obj/structure/closet/verb/verb_toggleopen()
- set src in oview(1)
- set category = "Object"
- set name = "Toggle Open"
-
- if(!usr.canmove || usr.stat || usr.restrained())
- return
-
- if(iscarbon(usr) || issilicon(usr) || isdrone(usr))
- return attack_hand(usr)
- else
- to_chat(usr, "This mob type can't use this verb.")
-
-// Objects that try to exit a locker by stepping were doing so successfully,
-// and due to an oversight in turf/Enter() were going through walls. That
-// should be independently resolved, but this is also an interesting twist.
-/obj/structure/closet/Exit(atom/movable/AM)
- open()
- if(AM.loc == src)
- return 0
- return 1
-
-/obj/structure/closet/container_resist(mob/living/user)
- if(opened)
- return
- if(ismovableatom(loc))
- user.changeNext_move(CLICK_CD_BREAKOUT)
- user.last_special = world.time + CLICK_CD_BREAKOUT
- var/atom/movable/AM = loc
- AM.relay_container_resist(user, src)
- return
- if(!welded && !locked)
- open()
- return
-
- //okay, so the closet is either welded or locked... resist!!!
- user.changeNext_move(CLICK_CD_BREAKOUT)
- user.last_special = world.time + CLICK_CD_BREAKOUT
- user.visible_message("[src] begins to shake violently!", \
- "You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \
- "You hear banging from [src].")
- if(do_after(user,(breakout_time), target = src))
- if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) )
- return
- //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting
- user.visible_message("[user] successfully broke out of [src]!",
- "You successfully break out of [src]!")
- bust_open()
- else
- if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded.
- to_chat(user, "You fail to break out of [src]!")
-
-/obj/structure/closet/AltClick(mob/user)
- ..()
- if(!user.canUseTopic(src, be_close=TRUE) || !isturf(loc))
- to_chat(user, "You can't do that right now!")
- return
- togglelock(user)
-
-/obj/structure/closet/CtrlShiftClick(mob/living/user)
- if(!HAS_TRAIT(user, TRAIT_SKITTISH))
- return ..()
- if(!user.canUseTopic(src) || !isturf(user.loc))
- return
- dive_into(user)
-
-/obj/structure/closet/emag_act(mob/user)
- if(secure && !broken)
- user.visible_message("Sparks fly from [src]!",
- "You scramble [src]'s lock, breaking it open!",
- "You hear a faint electrical spark.")
- playsound(src, "sparks", 50, 1)
- broken = TRUE
- locked = FALSE
- if(!QDELETED(lockerelectronics))
- qdel(lockerelectronics)
- lockerelectronics = null
- update_icon()
-
-/obj/structure/closet/get_remote_view_fullscreens(mob/user)
- if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
- user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
-
-/obj/structure/closet/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if (!(. & EMP_PROTECT_CONTENTS))
- for(var/obj/O in src)
- O.emp_act(severity)
- if(!secure || broken)
- return ..()
- if(prob(50 / severity))
- locked = !locked
- update_icon()
- if(prob(20 / severity) && !opened)
- if(!locked)
- open()
- else
- req_access = list()
- req_access += pick(get_all_accesses())
- if(!QDELETED(lockerelectronics))
- lockerelectronics.accesses = req_access
-
-/obj/structure/closet/contents_explosion(severity, target)
- for(var/atom/A in contents)
- A.ex_act(severity, target)
- CHECK_TICK
-
-/obj/structure/closet/singularity_act()
- dump_contents()
- ..()
-
-/obj/structure/closet/AllowDrop()
- return TRUE
-
-
-/obj/structure/closet/return_temperature()
- return
-
-/obj/structure/closet/proc/dive_into(mob/living/user)
- var/turf/T1 = get_turf(user)
- var/turf/T2 = get_turf(src)
- if(!opened)
- if(locked)
- togglelock(user, TRUE)
- if(!open(user))
- to_chat(user, "It won't budge!")
- return
- step_towards(user, T2)
- T1 = get_turf(user)
- if(T1 == T2)
- user.resting = TRUE //so people can jump into crates without slamming the lid on their head
- if(!close(user))
- to_chat(user, "You can't get [src] to close!")
- user.resting = FALSE
- return
- user.resting = FALSE
- togglelock(user)
- T1.visible_message("[user] dives into [src]!")
+/obj/structure/closet
+ name = "closet"
+ desc = "It's a basic storage unit."
+ icon = 'icons/obj/closet.dmi'
+ icon_state = "generic"
+ density = TRUE
+ layer = BELOW_OBJ_LAYER
+ var/icon_door = null
+ var/icon_door_override = FALSE //override to have open overlay use icon different to its base's
+ var/secure = FALSE //secure locker or not, also used if overriding a non-secure locker with a secure door overlay to add fancy lights
+ var/opened = FALSE
+ var/welded = FALSE
+ var/locked = FALSE
+ var/large = TRUE
+ var/wall_mounted = 0 //never solid (You can always pass over it)
+ max_integrity = 200
+ integrity_failure = 50
+ armor = list("melee" = 20, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
+ var/breakout_time = 1200
+ var/message_cooldown
+ var/can_weld_shut = TRUE
+ var/horizontal = FALSE
+ var/allow_objects = FALSE
+ var/allow_dense = FALSE
+ var/dense_when_open = FALSE //if it's dense when open or not
+ var/max_mob_size = MOB_SIZE_HUMAN //Biggest mob_size accepted by the container
+ var/mob_storage_capacity = 3 // how many human sized mob/living can fit together inside a closet.
+ var/storage_capacity = 30 //This is so that someone can't pack hundreds of items in a locker/crate then open it in a populated area to crash clients.
+ var/cutting_tool = /obj/item/weldingtool
+ var/open_sound = 'sound/machines/click.ogg'
+ var/close_sound = 'sound/machines/click.ogg'
+ var/material_drop = /obj/item/stack/sheet/metal
+ var/material_drop_amount = 2
+ var/delivery_icon = "deliverycloset" //which icon to use when packagewrapped. null to be unwrappable.
+ var/anchorable = TRUE
+ var/icon_welded = "welded"
+ var/obj/item/electronics/airlock/lockerelectronics //Installed electronics
+ var/lock_in_use = FALSE //Someone is doing some stuff with the lock here, better not proceed further
+ var/eigen_teleport = FALSE //If the closet leads to Mr Tumnus.
+ var/obj/structure/closet/eigen_target //Where you go to.
+
+
+/obj/structure/closet/Initialize(mapload)
+ . = ..()
+ update_icon()
+ PopulateContents()
+ if(mapload && !opened) // if closed, any item at the crate's loc is put in the contents
+ take_contents()
+ if(secure)
+ lockerelectronics = new(src)
+ lockerelectronics.accesses = req_access
+
+//USE THIS TO FILL IT, NOT INITIALIZE OR NEW
+/obj/structure/closet/proc/PopulateContents()
+ return
+
+/obj/structure/closet/Destroy()
+ dump_contents(override = FALSE)
+ return ..()
+
+/obj/structure/closet/update_icon()
+ cut_overlays()
+ if(opened & icon_door_override)
+ add_overlay("[icon_door]_open")
+ layer = OBJ_LAYER
+ return
+ else if(opened)
+ add_overlay("[icon_state]_open")
+ return
+ if(icon_door)
+ add_overlay("[icon_door]_door")
+ else
+ layer = BELOW_OBJ_LAYER
+ add_overlay("[icon_state]_door")
+ if(welded)
+ add_overlay("welded")
+ if(!secure)
+ return
+ if(broken)
+ add_overlay("off")
+ add_overlay("sparking")
+ else if(locked)
+ add_overlay("locked")
+ else
+ add_overlay("unlocked")
+
+/obj/structure/closet/examine(mob/user)
+ ..()
+ if(welded)
+ to_chat(user, "It's welded shut.")
+ if(anchored)
+ to_chat(user, "It is bolted to the ground.")
+ if(opened)
+ to_chat(user, "The parts are welded together.")
+ else if(secure && !opened)
+ else if(broken)
+ to_chat(user, "The lock is screwed in.")
+ else if(secure)
+ to_chat(user, "Alt-click to [locked ? "unlock" : "lock"].")
+ if(isliving(user))
+ var/mob/living/L = user
+ if(HAS_TRAIT(L, TRAIT_SKITTISH))
+ to_chat(user, "Ctrl-Shift-click [src] to jump inside.")
+
+/obj/structure/closet/CanPass(atom/movable/mover, turf/target)
+ if(wall_mounted)
+ return TRUE
+ return !density
+
+/obj/structure/closet/proc/can_open(mob/living/user)
+ if(welded || locked)
+ return FALSE
+ var/turf/T = get_turf(src)
+ for(var/mob/living/L in T)
+ if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
+ if(user)
+ to_chat(user, "There's something large on top of [src], preventing it from opening." )
+ return FALSE
+ return TRUE
+
+/obj/structure/closet/proc/can_close(mob/living/user)
+ var/turf/T = get_turf(src)
+ for(var/obj/structure/closet/closet in T)
+ if(closet != src && !closet.wall_mounted)
+ return FALSE
+ for(var/mob/living/L in T)
+ if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
+ if(user)
+ to_chat(user, "There's something too large in [src], preventing it from closing.")
+ return FALSE
+ return TRUE
+
+/obj/structure/closet/proc/can_lock(mob/living/user, var/check_access = TRUE) //set check_access to FALSE if you only need to check if a locker has a functional lock rather than access
+ if(!secure)
+ return FALSE
+ if(broken)
+ to_chat(user, "[src] is broken!")
+ return FALSE
+ if(QDELETED(lockerelectronics) && !locked) //We want to be able to unlock it regardless of electronics, but only lockable with electronics
+ to_chat(user, "[src] is missing locker electronics!")
+ return FALSE
+ if(!check_access)
+ return TRUE
+ if(allowed(user))
+ return TRUE
+ to_chat(user, "Access denied.")
+
+/obj/structure/closet/proc/togglelock(mob/living/user)
+ add_fingerprint(user)
+ if(eigen_target)
+ return
+ if(opened)
+ return
+ if(!can_lock(user))
+ return
+ locked = !locked
+ user.visible_message("[user] [locked ? null : "un"]locks [src].",
+ "You [locked ? null : "un"]lock [src].")
+ update_icon()
+
+/obj/structure/closet/proc/dump_contents(var/override = TRUE) //Override is for not revealing the locker electronics when you open the locker, for example
+ var/atom/L = drop_location()
+ for(var/atom/movable/AM in src)
+ if(AM == lockerelectronics && override)
+ continue
+ AM.forceMove(L)
+ if(throwing) // you keep some momentum when getting out of a thrown closet
+ step(AM, dir)
+ if(throwing)
+ throwing.finalize(FALSE)
+
+/obj/structure/closet/proc/take_contents()
+ var/atom/L = drop_location()
+ for(var/atom/movable/AM in L)
+ if(AM != src && insert(AM) == -1) // limit reached
+ break
+
+/obj/structure/closet/proc/open(mob/living/user)
+ if(opened || !can_open(user))
+ return
+ playsound(loc, open_sound, 15, 1, -3)
+ opened = TRUE
+ if(!dense_when_open)
+ density = FALSE
+ climb_time *= 0.5 //it's faster to climb onto an open thing
+ dump_contents()
+ update_icon()
+ return 1
+
+/obj/structure/closet/proc/insert(atom/movable/AM)
+ if(contents.len >= storage_capacity)
+ return -1
+ if(insertion_allowed(AM))
+ if(eigen_teleport) // For teleporting people with linked lockers.
+ do_teleport(AM, get_turf(eigen_target), 0)
+ if(eigen_target.opened == FALSE)
+ eigen_target.bust_open()
+ else
+ AM.forceMove(src)
+ return TRUE
+ else
+ return FALSE
+
+
+/obj/structure/closet/proc/insertion_allowed(atom/movable/AM)
+ if(ismob(AM))
+ if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets...
+ return FALSE
+ var/mob/living/L = AM
+ if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
+ return FALSE
+ if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
+ if(horizontal && L.density)
+ return FALSE
+ if(L.mob_size > max_mob_size)
+ return FALSE
+ var/mobs_stored = 0
+ for(var/mob/living/M in contents)
+ if(++mobs_stored >= mob_storage_capacity)
+ return FALSE
+ L.stop_pulling()
+
+ else if(istype(AM, /obj/structure/closet))
+ return FALSE
+
+ else if(istype(AM, /obj/effect))
+ return FALSE
+
+ else if(isobj(AM))
+ if((!allow_dense && AM.density) || AM.anchored || AM.has_buckled_mobs())
+ return FALSE
+ if(isitem(AM) && !HAS_TRAIT(AM, TRAIT_NODROP))
+ return TRUE
+ else if(!allow_objects && !istype(AM, /obj/effect/dummy/chameleon))
+ return FALSE
+ else
+ return FALSE
+
+ return TRUE
+
+/obj/structure/closet/proc/close(mob/living/user)
+ if(!opened || !can_close(user))
+ return FALSE
+ take_contents()
+ playsound(loc, close_sound, 15, 1, -3)
+ climb_time = initial(climb_time)
+ opened = FALSE
+ density = TRUE
+ update_icon()
+ return TRUE
+
+/obj/structure/closet/proc/toggle(mob/living/user)
+ if(opened)
+ return close(user)
+ else
+ return open(user)
+
+/obj/structure/closet/proc/bust_open()
+ welded = FALSE //applies to all lockers
+ locked = FALSE //applies to critter crates and secure lockers only
+ broken = TRUE //applies to secure lockers only
+ open()
+
+/obj/structure/closet/proc/handle_lock_addition(mob/user, obj/item/electronics/airlock/E)
+ add_fingerprint(user)
+ if(lock_in_use)
+ to_chat(user, "Wait for work on [src] to be done first!")
+ return
+ if(secure)
+ to_chat(user, "This locker already has a lock!")
+ return
+ if(broken)
+ to_chat(user, "Unscrew the broken lock first!")
+ return
+ if(!istype(E))
+ return
+ user.visible_message("[user] begins installing a lock on [src]...","You begin installing a lock on [src]...")
+ lock_in_use = TRUE
+ playsound(loc, 'sound/items/screwdriver.ogg', 50, 1)
+ if(!do_after(user, 60, target = src))
+ lock_in_use = FALSE
+ return
+ lock_in_use = FALSE
+ to_chat(user, "You finish the lock on [src]!")
+ E.forceMove(src)
+ lockerelectronics = E
+ req_access = E.accesses
+ secure = TRUE
+ update_icon()
+ return TRUE
+
+/obj/structure/closet/proc/handle_lock_removal(mob/user, obj/item/screwdriver/S)
+ if(lock_in_use)
+ to_chat(user, "Wait for work on [src] to be done first!")
+ return
+ if(locked)
+ to_chat(user, "Unlock it first!")
+ return
+ if(!secure)
+ to_chat(user, "[src] doesn't have a lock that you can remove!")
+ return
+ if(!istype(S))
+ return
+ var/brokenword = broken ? "broken " : null
+ user.visible_message("[user] begins removing the [brokenword]lock on [src]...","You begin removing the [brokenword]lock on [src]...")
+ playsound(loc, S.usesound, 50, 1)
+ lock_in_use = TRUE
+ if(!do_after(user, 100 * S.toolspeed, target = src))
+ lock_in_use = FALSE
+ return
+ to_chat(user, "You remove the [brokenword]lock from [src]!")
+ if(!QDELETED(lockerelectronics))
+ lockerelectronics.add_fingerprint(user)
+ lockerelectronics.forceMove(user.loc)
+ lockerelectronics = null
+ req_access = null
+ secure = FALSE
+ broken = FALSE
+ locked = FALSE
+ lock_in_use = FALSE
+ update_icon()
+ return TRUE
+
+
+/obj/structure/closet/deconstruct(disassembled = TRUE)
+ if(ispath(material_drop) && material_drop_amount && !(flags_1 & NODECONSTRUCT_1))
+ new material_drop(loc, material_drop_amount)
+ qdel(src)
+
+/obj/structure/closet/obj_break(damage_flag)
+ if(!broken && !(flags_1 & NODECONSTRUCT_1))
+ bust_open()
+
+/obj/structure/closet/attackby(obj/item/W, mob/user, params)
+ if(user in src)
+ return
+ if(src.tool_interact(W,user))
+ return 1 // No afterattack
+ else
+ return ..()
+
+/obj/structure/closet/proc/tool_interact(obj/item/W, mob/user)//returns TRUE if attackBy call shouldnt be continued (because tool was used/closet was of wrong type), FALSE if otherwise
+ . = TRUE
+ if(opened)
+ if(istype(W, cutting_tool))
+ if(istype(W, /obj/item/weldingtool))
+ if(!W.tool_start_check(user, amount=0))
+ return
+
+ to_chat(user, "You begin cutting \the [src] apart...")
+ if(W.use_tool(src, user, 40, volume=50))
+ if(eigen_teleport)
+ to_chat(user, "The unstable nature of \the [src] makes it impossible to cut!")
+ return
+ if(!opened)
+ return
+ user.visible_message("[user] slices apart \the [src].",
+ "You cut \the [src] apart with \the [W].",
+ "You hear welding.")
+ deconstruct(TRUE)
+ return
+ else // for example cardboard box is cut with wirecutters
+ user.visible_message("[user] cut apart \the [src].", \
+ "You cut \the [src] apart with \the [W].")
+ deconstruct(TRUE)
+ return
+ if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too
+ return TRUE
+ else if(istype(W, /obj/item/electronics/airlock))
+ handle_lock_addition(user, W)
+ else if(istype(W, /obj/item/screwdriver))
+ handle_lock_removal(user, W)
+ else if(istype(W, /obj/item/weldingtool) && can_weld_shut)
+ if(!W.tool_start_check(user, amount=0))
+ return
+
+ to_chat(user, "You begin [welded ? "unwelding":"welding"] \the [src]...")
+ if(W.use_tool(src, user, 40, volume=50))
+ if(eigen_teleport)
+ to_chat(user, "The unstable nature of \the [src] makes it impossible to weld!")
+ return
+ if(opened)
+ return
+ welded = !welded
+ after_weld(welded)
+ user.visible_message("[user] [welded ? "welds shut" : "unwelds"] \the [src].",
+ "You [welded ? "weld" : "unwelded"] \the [src] with \the [W].",
+ "You hear welding.")
+ update_icon()
+ else if(istype(W, /obj/item/wrench) && anchorable)
+ if(isinspace() && !anchored)
+ return
+ setAnchored(!anchored)
+ W.play_tool_sound(src, 75)
+ user.visible_message("[user] [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
+ "You [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
+ "You hear a ratchet.")
+ else if(user.a_intent != INTENT_HARM && !(W.item_flags & NOBLUDGEON))
+ if(W.GetID() || !toggle(user))
+ togglelock(user)
+ else
+ return FALSE
+
+/obj/structure/closet/proc/after_weld(weld_state)
+ return
+
+/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/living/user)
+ if(!istype(O) || O.anchored || istype(O, /obj/screen))
+ return
+ if(!istype(user) || user.incapacitated() || user.lying)
+ return
+ if(!Adjacent(user) || !user.Adjacent(O))
+ return
+ if(user == O) //try to climb onto it
+ return ..()
+ if(!opened)
+ return
+ if(!isturf(O.loc))
+ return
+
+ var/actuallyismob = 0
+ if(isliving(O))
+ actuallyismob = 1
+ else if(!isitem(O))
+ return
+ var/turf/T = get_turf(src)
+ var/list/targets = list(O, src)
+ add_fingerprint(user)
+ user.visible_message("[user] [actuallyismob ? "tries to ":""]stuff [O] into [src].", \
+ "You [actuallyismob ? "try to ":""]stuff [O] into [src].", \
+ "You hear clanging.")
+ if(actuallyismob)
+ if(do_after_mob(user, targets, 40))
+ user.visible_message("[user] stuffs [O] into [src].", \
+ "You stuff [O] into [src].", \
+ "You hear a loud metal bang.")
+ var/mob/living/L = O
+ if(!issilicon(L))
+ L.Knockdown(40)
+ O.forceMove(T)
+ close()
+ else
+ O.forceMove(T)
+ return 1
+
+/obj/structure/closet/relaymove(mob/user)
+ if(user.stat || !isturf(loc) || !isliving(user))
+ return
+ if(locked)
+ if(message_cooldown <= world.time)
+ message_cooldown = world.time + 50
+ to_chat(user, "[src]'s door won't budge!")
+ return
+ container_resist(user)
+
+/obj/structure/closet/attack_hand(mob/user)
+ . = ..()
+ if(.)
+ return
+ if(user.lying && get_dist(src, user) > 0)
+ return
+
+ if(!toggle(user))
+ togglelock(user)
+
+/obj/structure/closet/attack_paw(mob/user)
+ return attack_hand(user)
+
+/obj/structure/closet/attack_robot(mob/user)
+ if(user.Adjacent(src))
+ return attack_hand(user)
+
+// tk grab then use on self
+/obj/structure/closet/attack_self_tk(mob/user)
+ return attack_hand(user)
+
+/obj/structure/closet/verb/verb_toggleopen()
+ set src in oview(1)
+ set category = "Object"
+ set name = "Toggle Open"
+
+ if(!usr.canmove || usr.stat || usr.restrained())
+ return
+
+ if(iscarbon(usr) || issilicon(usr) || isdrone(usr))
+ return attack_hand(usr)
+ else
+ to_chat(usr, "This mob type can't use this verb.")
+
+// Objects that try to exit a locker by stepping were doing so successfully,
+// and due to an oversight in turf/Enter() were going through walls. That
+// should be independently resolved, but this is also an interesting twist.
+/obj/structure/closet/Exit(atom/movable/AM)
+ open()
+ if(AM.loc == src)
+ return 0
+ return 1
+
+/obj/structure/closet/container_resist(mob/living/user)
+ if(opened)
+ return
+ if(ismovableatom(loc))
+ user.changeNext_move(CLICK_CD_BREAKOUT)
+ user.last_special = world.time + CLICK_CD_BREAKOUT
+ var/atom/movable/AM = loc
+ AM.relay_container_resist(user, src)
+ return
+ if(!welded && !locked)
+ open()
+ return
+
+ //okay, so the closet is either welded or locked... resist!!!
+ user.changeNext_move(CLICK_CD_BREAKOUT)
+ user.last_special = world.time + CLICK_CD_BREAKOUT
+ user.visible_message("[src] begins to shake violently!", \
+ "You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \
+ "You hear banging from [src].")
+ if(do_after(user,(breakout_time), target = src))
+ if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) )
+ return
+ //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting
+ user.visible_message("[user] successfully broke out of [src]!",
+ "You successfully break out of [src]!")
+ bust_open()
+ else
+ if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded.
+ to_chat(user, "You fail to break out of [src]!")
+
+/obj/structure/closet/AltClick(mob/user)
+ ..()
+ if(!user.canUseTopic(src, be_close=TRUE) || !isturf(loc))
+ to_chat(user, "You can't do that right now!")
+ return
+ togglelock(user)
+
+/obj/structure/closet/CtrlShiftClick(mob/living/user)
+ if(!HAS_TRAIT(user, TRAIT_SKITTISH))
+ return ..()
+ if(!user.canUseTopic(src) || !isturf(user.loc))
+ return
+ dive_into(user)
+
+/obj/structure/closet/emag_act(mob/user)
+ . = ..()
+ if(!secure || broken)
+ return
+ user.visible_message("Sparks fly from [src]!",
+ "You scramble [src]'s lock, breaking it open!",
+ "You hear a faint electrical spark.")
+ playsound(src, "sparks", 50, 1)
+ broken = TRUE
+ locked = FALSE
+ if(!QDELETED(lockerelectronics))
+ QDEL_NULL(lockerelectronics)
+ update_icon()
+
+/obj/structure/closet/get_remote_view_fullscreens(mob/user)
+ if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
+ user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
+
+/obj/structure/closet/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ if (!(. & EMP_PROTECT_CONTENTS))
+ for(var/obj/O in src)
+ O.emp_act(severity)
+ if(!secure || broken)
+ return ..()
+ if(prob(50 / severity))
+ locked = !locked
+ update_icon()
+ if(prob(20 / severity) && !opened)
+ if(!locked)
+ open()
+ else
+ req_access = list()
+ req_access += pick(get_all_accesses())
+ if(!QDELETED(lockerelectronics))
+ lockerelectronics.accesses = req_access
+
+/obj/structure/closet/contents_explosion(severity, target)
+ for(var/atom/A in contents)
+ A.ex_act(severity, target)
+ CHECK_TICK
+
+/obj/structure/closet/singularity_act()
+ dump_contents()
+ ..()
+
+/obj/structure/closet/AllowDrop()
+ return TRUE
+
+
+/obj/structure/closet/return_temperature()
+ return
+
+/obj/structure/closet/proc/dive_into(mob/living/user)
+ var/turf/T1 = get_turf(user)
+ var/turf/T2 = get_turf(src)
+ if(!opened)
+ if(locked)
+ togglelock(user, TRUE)
+ if(!open(user))
+ to_chat(user, "It won't budge!")
+ return
+ step_towards(user, T2)
+ T1 = get_turf(user)
+ if(T1 == T2)
+ user.resting = TRUE //so people can jump into crates without slamming the lid on their head
+ if(!close(user))
+ to_chat(user, "You can't get [src] to close!")
+ user.resting = FALSE
+ return
+ user.resting = FALSE
+ togglelock(user)
+ T1.visible_message("[user] dives into [src]!")
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
index 167823b9be..a7adafdad4 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
@@ -33,6 +33,7 @@
new /obj/item/extinguisher/advanced(src)
new /obj/item/storage/photo_album/CE(src)
new /obj/item/storage/lockbox/medal/engineering(src)
+ new /obj/item/construction/rcd/loaded/upgraded(src)
/obj/structure/closet/secure_closet/engineering_electrical
name = "electrical supplies locker"
diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm
new file mode 100644
index 0000000000..c4e1968e59
--- /dev/null
+++ b/code/game/objects/structures/loom.dm
@@ -0,0 +1,21 @@
+//Loom, turns raw cotton and durathread into their respective fabrics.
+
+/obj/structure/loom
+ name = "loom"
+ desc = "A simple device used to weave cloth and other thread-based fabrics together into usable material."
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "loom"
+ density = TRUE
+ anchored = TRUE
+
+/obj/structure/loom/attackby(obj/item/stack/sheet/W, mob/user)
+ if(W.is_fabric && W.amount > 1)
+ user.show_message("You start weaving the [W.name] through the loom..", 1)
+ if(W.use_tool(src, user, W.pull_effort))
+ new W.loom_result(drop_location())
+ user.show_message("You weave the [W.name] into a workable fabric.", 1)
+ W.amount = (W.amount - 2)
+ if(W.amount < 1)
+ qdel(W)
+ else
+ user.show_message("You need a valid fabric and at least 2 of said fabric before using this.", 1)
\ No newline at end of file
diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm
index e979d4f18e..226d279288 100644
--- a/code/game/objects/structures/table_frames.dm
+++ b/code/game/objects/structures/table_frames.dm
@@ -100,6 +100,14 @@
to_chat(user, "You start adding [B] to [src]...")
if(do_after(user, 20, target = src) && B.use(1))
make_new_table(/obj/structure/table/bronze)
+ else if(istype(I, /obj/item/stack/sheet/plasmaglass))
+ var/obj/item/stack/sheet/plasmaglass/G = I
+ if(G.get_amount() < 1)
+ to_chat(user, "You need one plasmaglass sheet to do this!")
+ return
+ to_chat(user, "You start adding [G] to [src]...")
+ if(do_after(user, 20, target = src) && G.use(1))
+ make_new_table(/obj/structure/table/plasmaglass)
else
return ..()
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 8c29c1b2c5..fcffafb262 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -258,6 +258,53 @@
for(var/obj/item/shard/S in debris)
S.color = NARSIE_WINDOW_COLOUR
+/*
+ * Plasmaglass tables
+ */
+/obj/structure/table/plasmaglass
+ name = "plasmaglass table"
+ desc = "A glasstable, but it's pink and more sturdy. What will Nanotrasen design next with plasma?"
+ icon = 'icons/obj/smooth_structures/plasmaglass_table.dmi'
+ icon_state = "plasmaglass_table"
+ climbable = TRUE
+ buildstack = /obj/item/stack/sheet/plasmaglass
+ canSmoothWith = null
+ max_integrity = 270
+ resistance_flags = ACID_PROOF
+ armor = list("melee" = 10, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
+ var/list/debris = list()
+
+/obj/structure/table/plasmaglass/New()
+ . = ..()
+ debris += new frame
+ debris += new /obj/item/shard/plasma
+
+/obj/structure/table/plasmaglass/Destroy()
+ QDEL_LIST(debris)
+ . = ..()
+
+/obj/structure/table/plasmaglass/proc/check_break(mob/living/M)
+ return
+
+/obj/structure/table/plasmaglass/deconstruct(disassembled = TRUE, wrench_disassembly = 0)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ if(disassembled)
+ ..()
+ return
+ else
+ var/turf/T = get_turf(src)
+ playsound(T, "shatter", 50, 1)
+ for(var/X in debris)
+ var/atom/movable/AM = X
+ AM.forceMove(T)
+ debris -= AM
+ qdel(src)
+
+/obj/structure/table/plasmaglass/narsie_act()
+ color = NARSIE_WINDOW_COLOUR
+ for(var/obj/item/shard/S in debris)
+ S.color = NARSIE_WINDOW_COLOUR
+
/*
* Wooden tables
*/
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index a26d57e3e1..9fc055c2ba 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -281,6 +281,8 @@
. += new /obj/effect/decal/cleanable/glass(location)
if (reinf)
. += new /obj/item/stack/rods(location, (fulltile ? 2 : 1))
+ if (fulltile)
+ . += new /obj/item/shard(location)
/obj/structure/window/proc/can_be_rotated(mob/user,rotation_type)
if(anchored)
@@ -409,6 +411,15 @@
glass_type = /obj/item/stack/sheet/plasmaglass
rad_insulation = RAD_NO_INSULATION
+/obj/structure/window/plasma/spawnDebris(location)
+ . = list()
+ . += new /obj/item/shard/plasma(location)
+ . += new /obj/effect/decal/cleanable/glass/plasma(location)
+ if (reinf)
+ . += new /obj/item/stack/rods(location, (fulltile ? 2 : 1))
+ if (fulltile)
+ . += new /obj/item/shard/plasma(location)
+
/obj/structure/window/plasma/spawner/east
dir = EAST
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index b00efc7ed6..194014b61a 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -236,6 +236,10 @@
return list("mode" = RCD_DECONSTRUCT, "delay" = 50, "cost" = 33)
if(RCD_WINDOWGRILLE)
return list("mode" = RCD_WINDOWGRILLE, "delay" = 10, "cost" = 4)
+ if(RCD_MACHINE)
+ return list("mode" = RCD_MACHINE, "delay" = 20, "cost" = 25)
+ if(RCD_COMPUTER)
+ return list("mode" = RCD_COMPUTER, "delay" = 20, "cost" = 25)
return FALSE
/turf/open/floor/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
@@ -274,4 +278,20 @@
var/obj/structure/grille/G = new(src)
G.anchored = TRUE
return TRUE
+ if(RCD_MACHINE)
+ if(locate(/obj/structure/frame/machine) in src)
+ return FALSE
+ var/obj/structure/frame/machine/M = new(src)
+ M.state = 2
+ M.icon_state = "box_1"
+ M.anchored = TRUE
+ return TRUE
+ if(RCD_COMPUTER)
+ if(locate(/obj/structure/frame/computer) in src)
+ return FALSE
+ var/obj/structure/frame/computer/C = new(src)
+ C.anchored = TRUE
+ C.setDir(the_rcd.computer_dir)
+ return TRUE
+
return FALSE
diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm
index dfc6972578..8b63d60939 100644
--- a/code/game/turfs/simulated/wall/misc_walls.dm
+++ b/code/game/turfs/simulated/wall/misc_walls.dm
@@ -7,6 +7,7 @@
smooth = SMOOTH_MORE
sheet_type = /obj/item/stack/sheet/runed_metal
sheet_amount = 1
+ explosion_block = 10
girder_type = /obj/structure/girder/cult
/turf/closed/wall/mineral/cult/Initialize()
@@ -49,7 +50,7 @@
/turf/closed/wall/clockwork
name = "clockwork wall"
desc = "A huge chunk of warm metal. The clanging of machinery emanates from within."
- explosion_block = 2
+ explosion_block = 5
hardness = 10
slicing_duration = 80
sheet_type = /obj/item/stack/tile/brass
diff --git a/code/game/world.dm b/code/game/world.dm
index dedf822597..e9c8433006 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -137,7 +137,7 @@ GLOBAL_VAR(restart_counter)
// but those are both private, so let's put the commit info in the runtime
// log which is ultimately public.
log_runtime(GLOB.revdata.get_log_message())
-
+
/world/Topic(T, addr, master, key)
TGS_TOPIC //redirect to server tools if necessary
@@ -270,7 +270,8 @@ GLOBAL_VAR(restart_counter)
if (M.client)
n++
- features += "[SSmapping.config.map_name]" //CIT CHANGE - makes the hub entry display the current map
+ if(SSmapping.config) // this just stops the runtime, honk.
+ features += "[SSmapping.config.map_name]" //CIT CHANGE - makes the hub entry display the current map
if(get_security_level())//CIT CHANGE - makes the hub entry show the security level
features += "[get_security_level()] alert"
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index bfbf6b5b79..89e9c33ecc 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -54,12 +54,16 @@
only_current_user_can_interact = TRUE
/obj/machinery/vr_sleeper/hugbox/emag_act(mob/user)
- return
+ return SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
/obj/machinery/vr_sleeper/emag_act(mob/user)
+ . = ..()
+ if(you_die_in_the_game_you_die_for_real)
+ return
you_die_in_the_game_you_die_for_real = TRUE
sparks.start()
addtimer(CALLBACK(src, .proc/emagNotify), 150)
+ return TRUE
/obj/machinery/vr_sleeper/update_icon()
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 8e29b38fe1..0fca957ffe 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -56,6 +56,7 @@
/datum/antagonist/abductor/greet()
to_chat(owner.current, "You are the [owner.special_role]!")
to_chat(owner.current, "With the help of your teammate, kidnap and experiment on station crew members!")
+ to_chat(owner.current, "Try not to disturb the habitat, it could lead to dead specimens.")
to_chat(owner.current, "[greet_text]")
owner.announce_objectives()
diff --git a/code/modules/antagonists/changeling/powers/strained_muscles.dm b/code/modules/antagonists/changeling/powers/strained_muscles.dm
index baeed8b0b2..bdbd38b92d 100644
--- a/code/modules/antagonists/changeling/powers/strained_muscles.dm
+++ b/code/modules/antagonists/changeling/powers/strained_muscles.dm
@@ -5,7 +5,7 @@
name = "Strained Muscles"
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
- chemical_cost = 0
+ chemical_cost = 15
dna_cost = 1
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
@@ -15,13 +15,16 @@
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
+ var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
active = !active
if(active)
to_chat(user, "Our muscles tense and strengthen.")
+ changeling.chem_recharge_slowdown += 0.5
else
REMOVE_TRAIT(user, TRAIT_GOTTAGOFAST, "changeling_muscles")
to_chat(user, "Our muscles relax.")
- if(stacks >= 10)
+ changeling.chem_recharge_slowdown -= 0.5
+ if(stacks >= 20)
to_chat(user, "We collapse in exhaustion.")
user.Knockdown(60)
user.emote("gasp")
@@ -42,9 +45,9 @@
stacks++
//user.take_bodypart_damage(stacks * 0.03, 0)
- user.staminaloss += stacks * 1.3 //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
+ user.adjustStaminaLoss(stacks*1.3) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
- if(stacks == 11) //Warning message that the stacks are getting too high
+ if(stacks == 10) //Warning message that the stacks are getting too high
to_chat(user, "Our legs are really starting to hurt...")
sleep(40)
diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
index 56b36d13e6..f735d6bb29 100644
--- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
@@ -132,9 +132,9 @@
continue
if(isliving(M.current) && M.current.stat != DEAD)
var/turf/t_turf = isAI(M.current) ? get_step(get_step(src, NORTH),NORTH) : get_turf(src) // AI too fat, must make sure it always ends up a 2 tiles north instead of on the ark.
- do_teleport(M, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE)
- M.current.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
- M.current.clear_fullscreen("flash", 5)
+ do_teleport(M.current, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE)
+ M.current.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
+ M.current.clear_fullscreen("flash", 5)
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, FALSE)
recalls_remaining--
recalling = FALSE
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index a69694ced9..29b4e4539c 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -61,8 +61,8 @@ Runes can either be invoked by one's self or with many different cultists. Each
if(do_after(user, 15, target = src))
to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.")
qdel(src)
- else if(istype(I, /obj/item/nullrod))
- user.say("BEGONE FOUL MAGIKS!!", forced = "nullrod")
+ else if(istype(I, /obj/item/storage/book/bible) || istype(I, /obj/item/nullrod))
+ user.say("BEGONE FOUL MAGICKS!!", forced = "bible")
to_chat(user, "You disrupt the magic of [src] with [I].")
qdel(src)
@@ -286,14 +286,14 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/convert/proc/optinalert(mob/living/convertee)
var/alert = alert(convertee, "Will you embrace the Geometer of Blood or perish in futile resistance?", "Choose your own fate", "Join the Blood Cult", "Suffer a horrible demise")
- if(alert == "Join the Blood Cult")
+ if(src && alert == "Join the Blood Cult")
signmeup(convertee)
/obj/effect/rune/convert/proc/signmeup(mob/living/convertee)
- if(currentconversionman == usr)
+ if(currentconversionman == convertee)
conversionresult = TRUE
else
- to_chat(usr, "Your fate has already been set in stone.")
+ to_chat(convertee, "Your fate has already been set in stone.")
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, force_a_sac)
var/mob/living/first_invoker = invokers[1]
diff --git a/code/modules/antagonists/disease/disease_disease.dm b/code/modules/antagonists/disease/disease_disease.dm
index 21d0381982..b4b8ac0956 100644
--- a/code/modules/antagonists/disease/disease_disease.dm
+++ b/code/modules/antagonists/disease/disease_disease.dm
@@ -51,7 +51,7 @@
if(cures.len)
return
var/list/not_used = advance_cures.Copy()
- cures = list(pick_n_take(not_used), pick_n_take(not_used))
+ cures = list(pick(pick_n_take(not_used)), pick(pick_n_take(not_used)))
// Get the cure name from the cure_id
var/datum/reagent/D1 = GLOB.chemical_reagents_list[cures[1]]
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 53f8aa2da1..bb24af4f7a 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -36,30 +36,42 @@
/datum/antagonist/ert/security // kinda handled by the base template but here for completion
-/datum/antagonist/ert/security/red
+/datum/antagonist/ert/security/amber
outfit = /datum/outfit/ert/security/alert
+/datum/antagonist/ert/security/red
+ outfit = /datum/outfit/ert/security/alert/red
+
/datum/antagonist/ert/engineer
role = "Engineer"
outfit = /datum/outfit/ert/engineer
-/datum/antagonist/ert/engineer/red
+/datum/antagonist/ert/engineer/amber
outfit = /datum/outfit/ert/engineer/alert
+/datum/antagonist/ert/engineer/red
+ outfit = /datum/outfit/ert/engineer/alert/red
+
/datum/antagonist/ert/medic
role = "Medical Officer"
outfit = /datum/outfit/ert/medic
-/datum/antagonist/ert/medic/red
+/datum/antagonist/ert/medic/amber
outfit = /datum/outfit/ert/medic/alert
+/datum/antagonist/ert/medic/red
+ outfit = /datum/outfit/ert/medic/alert/red
+
/datum/antagonist/ert/commander
role = "Commander"
outfit = /datum/outfit/ert/commander
-/datum/antagonist/ert/commander/red
+/datum/antagonist/ert/commander/amber
outfit = /datum/outfit/ert/commander/alert
+/datum/antagonist/ert/commander/red
+ outfit = /datum/outfit/ert/commander/alert/red
+
/datum/antagonist/ert/deathsquad
name = "Deathsquad Trooper"
outfit = /datum/outfit/death_commando
diff --git a/code/modules/antagonists/greybois/greybois.dm b/code/modules/antagonists/greybois/greybois.dm
new file mode 100644
index 0000000000..b5e18045e8
--- /dev/null
+++ b/code/modules/antagonists/greybois/greybois.dm
@@ -0,0 +1,23 @@
+/datum/antagonist/greybois
+ name = "Emergency Assistant"
+ show_name_in_check_antagonists = TRUE
+ show_in_antagpanel = FALSE
+ var/mission = "Assist the station."
+ var/datum/outfit/outfit = /datum/outfit/ert/greybois
+
+/datum/antagonist/greybois/greygod
+ outfit = /datum/outfit/ert/greybois/greygod
+
+/datum/antagonist/greybois/greet()
+ to_chat(owner, "You are an Emergency Assistant.")
+ to_chat(owner, "Central Command is sending you to [station_name()] with the task: [mission]")
+
+/datum/antagonist/greybois/on_gain()
+ equipERT()
+ . = ..()
+
+/datum/antagonist/greybois/proc/equipERT()
+ var/mob/living/carbon/human/H = owner.current
+ if(!istype(H))
+ return
+ H.equipOutfit(outfit)
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index 08e00ea0f4..8642484895 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -405,8 +405,12 @@
var/breakout = 0
while(breakout < 50)
if(!T)
- break
+ end_effect(user)
+ return
var/turf/potential_T = find_safe_turf()
+ if(!potential_T)
+ end_effect(user)
+ return
if(T.z != potential_T.z || abs(get_dist_euclidian(potential_T,T)) > 50 - breakout)
do_teleport(user, potential_T, channel = TELEPORT_CHANNEL_MAGIC)
user.canmove = 0
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index 5ad97fb7c8..8e320b2f3e 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -364,15 +364,10 @@
var/list/cached_gases = air.gases
var/temperature = air.temperature
var/pressure = air.return_pressure()
-
var/old_heat_capacity = air.heat_capacity()
var/reaction_efficency = min(1/((pressure/(0.1*ONE_ATMOSPHERE))*(max(cached_gases[/datum/gas/plasma]/cached_gases[/datum/gas/nitrous_oxide],1))),cached_gases[/datum/gas/nitrous_oxide],cached_gases[/datum/gas/plasma]/2)
var/energy_released = 2*reaction_efficency*FIRE_CARBON_ENERGY_RELEASED
- if(cached_gases[/datum/gas/miasma] && cached_gases[/datum/gas/miasma] > 0)
- energy_released /= cached_gases[/datum/gas/miasma]*0.1
- if(cached_gases[/datum/gas/bz] && cached_gases[/datum/gas/bz] > 0)
- energy_released *= cached_gases[/datum/gas/bz]*0.1
- if ((cached_gases[/datum/gas/nitrous_oxide] - reaction_efficency < 0 )|| (cached_gases[/datum/gas/plasma] - (2*reaction_efficency) < 0)) //Shouldn't produce gas from nothing.
+ if ((cached_gases[/datum/gas/nitrous_oxide] - reaction_efficency < 0 )|| (cached_gases[/datum/gas/plasma] - (2*reaction_efficency) < 0) || energy_released <= 0) //Shouldn't produce gas from nothing.
return NO_REACTION
cached_gases[/datum/gas/bz] += reaction_efficency
if(reaction_efficency == cached_gases[/datum/gas/nitrous_oxide])
@@ -381,7 +376,7 @@
cached_gases[/datum/gas/nitrous_oxide] -= reaction_efficency
cached_gases[/datum/gas/plasma] -= 2*reaction_efficency
- SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, (reaction_efficency**0.5)*BZ_RESEARCH_AMOUNT)
+ SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, min((reaction_efficency**2)*BZ_RESEARCH_SCALE),BZ_RESEARCH_MAX_AMOUNT)
if(energy_released > 0)
var/new_heat_capacity = air.heat_capacity()
@@ -477,4 +472,4 @@
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
air.temperature += cleaned_air * 0.002
- SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, cleaned_air*MIASMA_RESEARCH_AMOUNT)//Turns out the burning of miasma is kinda interesting to scientists
\ No newline at end of file
+ SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, cleaned_air*MIASMA_RESEARCH_AMOUNT)//Turns out the burning of miasma is kinda interesting to scientists
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 3bf54a1178..6ca25c0d80 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -1,880 +1,882 @@
-/datum/tlv
- var/min2
- var/min1
- var/max1
- var/max2
-
-/datum/tlv/New(min2 as num, min1 as num, max1 as num, max2 as num)
- if(min2) src.min2 = min2
- if(min1) src.min1 = min1
- if(max1) src.max1 = max1
- if(max2) src.max2 = max2
-
-/datum/tlv/proc/get_danger_level(val as num)
- if(max2 != -1 && val >= max2)
- return 2
- if(min2 != -1 && val <= min2)
- return 2
- if(max1 != -1 && val >= max1)
- return 1
- if(min1 != -1 && val <= min1)
- return 1
- return 0
-
-/datum/tlv/no_checks
- min2 = -1
- min1 = -1
- max1 = -1
- max2 = -1
-
-/datum/tlv/dangerous
- min2 = -1
- min1 = -1
- max1 = 0.2
- max2 = 0.5
-
-/obj/item/electronics/airalarm
- name = "air alarm electronics"
- icon_state = "airalarm_electronics"
-
-/obj/item/wallframe/airalarm
- name = "air alarm frame"
- desc = "Used for building Air Alarms."
- icon = 'icons/obj/monitors.dmi'
- icon_state = "alarm_bitem"
- result_path = /obj/machinery/airalarm
-
-#define AALARM_MODE_SCRUBBING 1
-#define AALARM_MODE_VENTING 2 //makes draught
-#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet)
-#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing
-#define AALARM_MODE_OFF 5
-#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents
-#define AALARM_MODE_SIPHON 7 //Scrubbers suck air
-#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing.
-#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output
-
-#define AALARM_REPORT_TIMEOUT 100
-
-#define AALARM_OVERLAY_OFF "alarm_off"
-#define AALARM_OVERLAY_GREEN "alarm_green"
-#define AALARM_OVERLAY_WARN "alarm_amber"
-#define AALARM_OVERLAY_DANGER "alarm_red"
-
-/obj/machinery/airalarm
- name = "air alarm"
- desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
- icon = 'icons/obj/monitors.dmi'
- icon_state = "alarm0"
- use_power = IDLE_POWER_USE
- idle_power_usage = 4
- active_power_usage = 8
- power_channel = ENVIRON
- req_access = list(ACCESS_ATMOSPHERICS)
- max_integrity = 250
- integrity_failure = 80
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
- resistance_flags = FIRE_PROOF
-
- var/danger_level = 0
- var/mode = AALARM_MODE_SCRUBBING
-
- var/locked = TRUE
- var/aidisabled = 0
- var/shorted = 0
- var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
- var/brightness_on = 1
-
- var/frequency = FREQ_ATMOS_CONTROL
- var/alarm_frequency = FREQ_ATMOS_ALARMS
- var/datum/radio_frequency/radio_connection
-
- var/list/TLV = list( // Breathable air.
- "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa
- "temperature" = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66),
- /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
- /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000),
- /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10),
- /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5),
- /datum/gas/plasma = new/datum/tlv/dangerous,
- /datum/gas/nitrous_oxide = new/datum/tlv/dangerous,
- /datum/gas/bz = new/datum/tlv/dangerous,
- /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic
- /datum/gas/water_vapor = new/datum/tlv/dangerous,
- /datum/gas/tritium = new/datum/tlv/dangerous,
- /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
- /datum/gas/nitryl = new/datum/tlv/dangerous,
- /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
- )
-
-/obj/machinery/airalarm/server // No checks here.
- TLV = list(
- "pressure" = new/datum/tlv/no_checks,
- "temperature" = new/datum/tlv/no_checks,
- /datum/gas/oxygen = new/datum/tlv/no_checks,
- /datum/gas/nitrogen = new/datum/tlv/no_checks,
- /datum/gas/carbon_dioxide = new/datum/tlv/no_checks,
- /datum/gas/miasma = new/datum/tlv/no_checks,
- /datum/gas/plasma = new/datum/tlv/no_checks,
- /datum/gas/nitrous_oxide = new/datum/tlv/no_checks,
- /datum/gas/bz = new/datum/tlv/no_checks,
- /datum/gas/hypernoblium = new/datum/tlv/no_checks,
- /datum/gas/water_vapor = new/datum/tlv/no_checks,
- /datum/gas/tritium = new/datum/tlv/no_checks,
- /datum/gas/stimulum = new/datum/tlv/no_checks,
- /datum/gas/nitryl = new/datum/tlv/no_checks,
- /datum/gas/pluoxium = new/datum/tlv/no_checks
- )
-
-/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures.
- TLV = list(
- "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa
- "temperature" = new/datum/tlv(T0C-73.15, T0C-63.15, T0C, T0C+10),
- /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
- /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000),
- /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10),
- /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5),
- /datum/gas/plasma = new/datum/tlv/dangerous,
- /datum/gas/nitrous_oxide = new/datum/tlv/dangerous,
- /datum/gas/bz = new/datum/tlv/dangerous,
- /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic
- /datum/gas/water_vapor = new/datum/tlv/dangerous,
- /datum/gas/tritium = new/datum/tlv/dangerous,
- /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
- /datum/gas/nitryl = new/datum/tlv/dangerous,
- /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
- )
-
-/obj/machinery/airalarm/unlocked
- locked = FALSE
-
-/obj/machinery/airalarm/engine
- name = "engine air alarm"
- locked = FALSE
- req_access = null
- req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_ENGINE)
-
-/obj/machinery/airalarm/mixingchamber
- name = "chamber air alarm"
- locked = FALSE
- req_access = null
- req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_TOX, ACCESS_TOX_STORAGE)
-
-/obj/machinery/airalarm/all_access
- name = "all-access air alarm"
- desc = "This particular atmos control unit appears to have no access restrictions."
- locked = FALSE
- req_access = null
- req_one_access = null
-
-/obj/machinery/airalarm/syndicate //general syndicate access
- req_access = list(ACCESS_SYNDICATE)
-
-/obj/machinery/airalarm/directional/north //Pixel offsets get overwritten on New()
- dir = SOUTH
- pixel_y = 24
-
-/obj/machinery/airalarm/directional/south
- dir = NORTH
- pixel_y = -24
-
-/obj/machinery/airalarm/directional/east
- dir = WEST
- pixel_x = 24
-
-/obj/machinery/airalarm/directional/west
- dir = EAST
- pixel_x = -24
-
-//all air alarms in area are connected via magic
-/area
- var/list/air_vent_names = list()
- var/list/air_scrub_names = list()
- var/list/air_vent_info = list()
- var/list/air_scrub_info = list()
-
-/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild)
- . = ..()
- wires = new /datum/wires/airalarm(src)
-
- if(ndir)
- setDir(ndir)
-
- if(nbuild)
- buildstage = 0
- panel_open = TRUE
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
- pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0
-
- if(name == initial(name))
- name = "[get_area_name(src)] Air Alarm"
-
- power_change()
- set_frequency(frequency)
-
-/obj/machinery/airalarm/Destroy()
- SSradio.remove_object(src, frequency)
- qdel(wires)
- wires = null
- return ..()
-
-/obj/machinery/airalarm/examine(mob/user)
- . = ..()
- switch(buildstage)
- if(0)
- to_chat(user, "It is missing air alarm electronics.")
- if(1)
- to_chat(user, "It is missing wiring.")
- if(2)
- to_chat(user, "Alt-click to [locked ? "unlock" : "lock"] the interface.")
-
-/obj/machinery/airalarm/ui_status(mob/user)
- if(user.has_unlimited_silicon_privilege && aidisabled)
- to_chat(user, "AI control has been disabled.")
- else if(!shorted)
- return ..()
- return UI_CLOSE
-
-/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state)
- ui.open()
-
-/obj/machinery/airalarm/ui_data(mob/user)
- var/data = list(
- "locked" = locked,
- "siliconUser" = user.has_unlimited_silicon_privilege,
- "emagged" = (obj_flags & EMAGGED ? 1 : 0),
- "danger_level" = danger_level,
- )
-
- var/area/A = get_area(src)
- data["atmos_alarm"] = A.atmosalm
- data["fire_alarm"] = A.fire
-
- var/turf/T = get_turf(src)
- var/datum/gas_mixture/environment = T.return_air()
- var/datum/tlv/cur_tlv
-
- data["environment_data"] = list()
- var/pressure = environment.return_pressure()
- cur_tlv = TLV["pressure"]
- data["environment_data"] += list(list(
- "name" = "Pressure",
- "value" = pressure,
- "unit" = "kPa",
- "danger_level" = cur_tlv.get_danger_level(pressure)
- ))
- var/temperature = environment.temperature
- cur_tlv = TLV["temperature"]
- data["environment_data"] += list(list(
- "name" = "Temperature",
- "value" = temperature,
- "unit" = "K ([round(temperature - T0C, 0.1)]C)",
- "danger_level" = cur_tlv.get_danger_level(temperature)
- ))
- var/total_moles = environment.total_moles()
- var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
- for(var/gas_id in environment.gases)
- if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
- continue
- cur_tlv = TLV[gas_id]
- data["environment_data"] += list(list(
- "name" = GLOB.meta_gas_names[gas_id],
- "value" = environment.gases[gas_id] / total_moles * 100,
- "unit" = "%",
- "danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id] * partial_pressure)
- ))
-
- if(!locked || user.has_unlimited_silicon_privilege)
- data["vents"] = list()
- for(var/id_tag in A.air_vent_names)
- var/long_name = A.air_vent_names[id_tag]
- var/list/info = A.air_vent_info[id_tag]
- if(!info || info["frequency"] != frequency)
- continue
- data["vents"] += list(list(
- "id_tag" = id_tag,
- "long_name" = sanitize(long_name),
- "power" = info["power"],
- "checks" = info["checks"],
- "excheck" = info["checks"]&1,
- "incheck" = info["checks"]&2,
- "direction" = info["direction"],
- "external" = info["external"],
- "internal" = info["internal"],
- "extdefault"= (info["external"] == ONE_ATMOSPHERE),
- "intdefault"= (info["internal"] == 0)
- ))
- data["scrubbers"] = list()
- for(var/id_tag in A.air_scrub_names)
- var/long_name = A.air_scrub_names[id_tag]
- var/list/info = A.air_scrub_info[id_tag]
- if(!info || info["frequency"] != frequency)
- continue
- data["scrubbers"] += list(list(
- "id_tag" = id_tag,
- "long_name" = sanitize(long_name),
- "power" = info["power"],
- "scrubbing" = info["scrubbing"],
- "widenet" = info["widenet"],
- "filter_types" = info["filter_types"]
- ))
- data["mode"] = mode
- data["modes"] = list()
- data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0))
- data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0))
- data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0))
- data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 1))
- data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1))
- data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1))
- data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1))
- data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0))
- if(obj_flags & EMAGGED)
- data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1))
-
- var/datum/tlv/selected
- var/list/thresholds = list()
-
- selected = TLV["pressure"]
- thresholds += list(list("name" = "Pressure", "settings" = list()))
- thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2))
- thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1))
- thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1))
- thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2))
-
- selected = TLV["temperature"]
- thresholds += list(list("name" = "Temperature", "settings" = list()))
- thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2))
- thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1))
- thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1))
- thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2))
-
- for(var/gas_id in GLOB.meta_gas_names)
- if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
- continue
- selected = TLV[gas_id]
- thresholds += list(list("name" = GLOB.meta_gas_names[gas_id], "settings" = list()))
- thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min2", "selected" = selected.min2))
- thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min1", "selected" = selected.min1))
- thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max1", "selected" = selected.max1))
- thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max2", "selected" = selected.max2))
-
- data["thresholds"] = thresholds
- return data
-
-/obj/machinery/airalarm/ui_act(action, params)
- if(..() || buildstage != 2)
- return
- if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
- return
- var/device_id = params["id_tag"]
- switch(action)
- if("lock")
- if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
- locked = !locked
- . = TRUE
- if("power", "toggle_filter", "widenet", "scrubbing")
- send_signal(device_id, list("[action]" = params["val"]), usr)
- . = TRUE
- if("excheck")
- send_signal(device_id, list("checks" = text2num(params["val"])^1), usr)
- . = TRUE
- if("incheck")
- send_signal(device_id, list("checks" = text2num(params["val"])^2), usr)
- . = TRUE
- if("set_external_pressure", "set_internal_pressure")
- var/area/A = get_area(src)
- var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null
- if(!isnull(target) && !..())
- send_signal(device_id, list("[action]" = target), usr)
- . = TRUE
- if("reset_external_pressure")
- send_signal(device_id, list("reset_external_pressure"), usr)
- . = TRUE
- if("reset_internal_pressure")
- send_signal(device_id, list("reset_internal_pressure"), usr)
- . = TRUE
- if("threshold")
- var/env = params["env"]
- if(text2path(env))
- env = text2path(env)
-
- var/name = params["var"]
- var/datum/tlv/tlv = TLV[env]
- if(isnull(tlv))
- return
- var/value = input("New [name] for [env]:", name, tlv.vars[name]) as num|null
- if(!isnull(value) && !..())
- if(value < 0)
- tlv.vars[name] = -1
- else
- tlv.vars[name] = round(value, 0.01)
- investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS)
- . = TRUE
- if("mode")
- mode = text2num(params["mode"])
- investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS)
- apply_mode()
- . = TRUE
- if("alarm")
- var/area/A = get_area(src)
- if(A.atmosalert(2, src))
- post_alert(2)
- . = TRUE
- if("reset")
- var/area/A = get_area(src)
- if(A.atmosalert(0, src))
- post_alert(0)
- . = TRUE
- update_icon()
-
-/obj/machinery/airalarm/proc/reset(wire)
- switch(wire)
- if(WIRE_POWER)
- if(!wires.is_cut(WIRE_POWER))
- shorted = FALSE
- update_icon()
- if(WIRE_AI)
- if(!wires.is_cut(WIRE_AI))
- aidisabled = FALSE
-
-
-/obj/machinery/airalarm/proc/shock(mob/user, prb)
- if((stat & (NOPOWER))) // unpowered, no shock
- return 0
- if(!prob(prb))
- return 0 //you lucked out, no shock for you
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(5, 1, src)
- s.start() //sparks always.
- if (electrocute_mob(user, get_area(src), src, 1, TRUE))
- return 1
- else
- return 0
-
-/obj/machinery/airalarm/proc/refresh_all()
- var/area/A = get_area(src)
- for(var/id_tag in A.air_vent_names)
- var/list/I = A.air_vent_info[id_tag]
- if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
- continue
- send_signal(id_tag, list("status"))
- for(var/id_tag in A.air_scrub_names)
- var/list/I = A.air_scrub_info[id_tag]
- if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
- continue
- send_signal(id_tag, list("status"))
-
-/obj/machinery/airalarm/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM)
-
-/obj/machinery/airalarm/proc/send_signal(target, list/command, mob/user)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new(command)
- signal.data["tag"] = target
- signal.data["sigtype"] = "command"
- signal.data["user"] = user
- radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM)
-
- return 1
-
-/obj/machinery/airalarm/proc/get_mode_name(mode_value)
- switch(mode_value)
- if(AALARM_MODE_SCRUBBING)
- return "Filtering"
- if(AALARM_MODE_CONTAMINATED)
- return "Contaminated"
- if(AALARM_MODE_VENTING)
- return "Draught"
- if(AALARM_MODE_REFILL)
- return "Refill"
- if(AALARM_MODE_PANIC)
- return "Panic Siphon"
- if(AALARM_MODE_REPLACEMENT)
- return "Cycle"
- if(AALARM_MODE_SIPHON)
- return "Siphon"
- if(AALARM_MODE_OFF)
- return "Off"
- if(AALARM_MODE_FLOOD)
- return "Flood"
-
-/obj/machinery/airalarm/proc/apply_mode()
- var/area/A = get_area(src)
- switch(mode)
- if(AALARM_MODE_SCRUBBING)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma),
- "scrubbing" = 1,
- "widenet" = 0,
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 1,
- "checks" = 1,
- "set_external_pressure" = ONE_ATMOSPHERE
- ))
- if(AALARM_MODE_CONTAMINATED)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "set_filters" = list(
- /datum/gas/carbon_dioxide,
- /datum/gas/miasma,
- /datum/gas/plasma,
- /datum/gas/water_vapor,
- /datum/gas/hypernoblium,
- /datum/gas/nitrous_oxide,
- /datum/gas/nitryl,
- /datum/gas/tritium,
- /datum/gas/bz,
- /datum/gas/stimulum,
- /datum/gas/pluoxium
- ),
- "scrubbing" = 1,
- "widenet" = 1,
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 1,
- "checks" = 1,
- "set_external_pressure" = ONE_ATMOSPHERE
- ))
- if(AALARM_MODE_VENTING)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "widenet" = 0,
- "scrubbing" = 0
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 1,
- "checks" = 1,
- "set_external_pressure" = ONE_ATMOSPHERE*2
- ))
- if(AALARM_MODE_REFILL)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma),
- "scrubbing" = 1,
- "widenet" = 0,
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 1,
- "checks" = 1,
- "set_external_pressure" = ONE_ATMOSPHERE * 3
- ))
- if(AALARM_MODE_PANIC,
- AALARM_MODE_REPLACEMENT)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "widenet" = 1,
- "scrubbing" = 0
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 0
- ))
- if(AALARM_MODE_SIPHON)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 1,
- "widenet" = 0,
- "scrubbing" = 0
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 0
- ))
-
- if(AALARM_MODE_OFF)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 0
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 0
- ))
- if(AALARM_MODE_FLOOD)
- for(var/device_id in A.air_scrub_names)
- send_signal(device_id, list(
- "power" = 0
- ))
- for(var/device_id in A.air_vent_names)
- send_signal(device_id, list(
- "power" = 1,
- "checks" = 2,
- "set_internal_pressure" = 0
- ))
-
-/obj/machinery/airalarm/update_icon()
- set_light(0)
- cut_overlays()
- SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
- if(stat & NOPOWER)
- icon_state = "alarm0"
- return
-
- if(stat & BROKEN)
- icon_state = "alarmx"
- return
-
- if(panel_open)
- switch(buildstage)
- if(2)
- icon_state = "alarmx"
- if(1)
- icon_state = "alarm_b2"
- if(0)
- icon_state = "alarm_b1"
- return
-
- icon_state = "alarm1"
- var/overlay_state = AALARM_OVERLAY_OFF
- var/area/A = get_area(src)
- switch(max(danger_level, A.atmosalm))
- if(0)
- add_overlay(AALARM_OVERLAY_GREEN)
- overlay_state = AALARM_OVERLAY_GREEN
- light_color = LIGHT_COLOR_GREEN
- set_light(brightness_on)
- if(1)
- add_overlay(AALARM_OVERLAY_WARN)
- overlay_state = AALARM_OVERLAY_WARN
- light_color = LIGHT_COLOR_LAVA
- set_light(brightness_on)
- if(2)
- add_overlay(AALARM_OVERLAY_DANGER)
- overlay_state = AALARM_OVERLAY_DANGER
- light_color = LIGHT_COLOR_RED
- set_light(brightness_on)
-
- SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
- update_light()
-
-/obj/machinery/airalarm/process()
- if((stat & (NOPOWER|BROKEN)) || shorted)
- return
-
- var/turf/location = get_turf(src)
- if(!location)
- return
-
- var/datum/tlv/cur_tlv
-
- var/datum/gas_mixture/environment = location.return_air()
- var/list/env_gases = environment.gases
- var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
-
- cur_tlv = TLV["pressure"]
- var/environment_pressure = environment.return_pressure()
- var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
-
- cur_tlv = TLV["temperature"]
- var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
-
- var/gas_dangerlevel = 0
- for(var/gas_id in env_gases)
- if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
- continue
- cur_tlv = TLV[gas_id]
- gas_dangerlevel = max(gas_dangerlevel, cur_tlv.get_danger_level(env_gases[gas_id] * partial_pressure))
-
- GAS_GARBAGE_COLLECT(environment.gases)
-
- var/old_danger_level = danger_level
- danger_level = max(pressure_dangerlevel, temperature_dangerlevel, gas_dangerlevel)
-
- if(old_danger_level != danger_level)
- apply_danger_level()
- if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05)
- mode = AALARM_MODE_SCRUBBING
- apply_mode()
-
- return
-
-
-/obj/machinery/airalarm/proc/post_alert(alert_level)
- var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
-
- if(!frequency)
- return
-
- var/datum/signal/alert_signal = new(list(
- "zone" = get_area_name(src),
- "type" = "Atmospheric"
- ))
- if(alert_level==2)
- alert_signal.data["alert"] = "severe"
- else if (alert_level==1)
- alert_signal.data["alert"] = "minor"
- else if (alert_level==0)
- alert_signal.data["alert"] = "clear"
-
- frequency.post_signal(src, alert_signal, range = -1)
-
-/obj/machinery/airalarm/proc/apply_danger_level()
- var/area/A = get_area(src)
-
- var/new_area_danger_level = 0
- for(var/obj/machinery/airalarm/AA in A)
- if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
- new_area_danger_level = max(new_area_danger_level,AA.danger_level)
- if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state
- post_alert(new_area_danger_level)
-
- update_icon()
-
-/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params)
- switch(buildstage)
- if(2)
- if(istype(W, /obj/item/wirecutters) && panel_open && wires.is_all_cut())
- W.play_tool_sound(src)
- to_chat(user, "You cut the final wires.")
- new /obj/item/stack/cable_coil(loc, 5)
- buildstage = 1
- update_icon()
- return
- else if(istype(W, /obj/item/screwdriver)) // Opening that Air Alarm up.
- W.play_tool_sound(src)
- panel_open = !panel_open
- to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].")
- update_icon()
- return
- else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card
- togglelock(user)
- else if(panel_open && is_wire_tool(W))
- wires.interact(user)
- return
- if(1)
- if(istype(W, /obj/item/crowbar))
- user.visible_message("[user.name] removes the electronics from [src.name].",\
- "You start prying out the circuit...")
- W.play_tool_sound(src)
- if (W.use_tool(src, user, 20))
- if (buildstage == 1)
- to_chat(user, "You remove the air alarm electronics.")
- new /obj/item/electronics/airalarm( src.loc )
- playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
- buildstage = 0
- update_icon()
- return
-
- if(istype(W, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/cable = W
- if(cable.get_amount() < 5)
- to_chat(user, "You need five lengths of cable to wire the air alarm!")
- return
- user.visible_message("[user.name] wires the air alarm.", \
- "You start wiring the air alarm...")
- if (do_after(user, 20, target = src))
- if (cable.get_amount() >= 5 && buildstage == 1)
- cable.use(5)
- to_chat(user, "You wire the air alarm.")
- wires.repair()
- aidisabled = 0
- locked = FALSE
- mode = 1
- shorted = 0
- post_alert(0)
- buildstage = 2
- update_icon()
- return
- if(0)
- if(istype(W, /obj/item/electronics/airalarm))
- if(user.temporarilyRemoveItemFromInventory(W))
- to_chat(user, "You insert the circuit.")
- buildstage = 1
- update_icon()
- qdel(W)
- return
-
- if(istype(W, /obj/item/electroadaptive_pseudocircuit))
- var/obj/item/electroadaptive_pseudocircuit/P = W
- if(!P.adapt_circuit(user, 25))
- return
- user.visible_message("[user] fabricates a circuit and places it into [src].", \
- "You adapt an air alarm circuit and slot it into the assembly.")
- buildstage = 1
- update_icon()
- return
-
- if(istype(W, /obj/item/wrench))
- to_chat(user, "You detach \the [src] from the wall.")
- W.play_tool_sound(src)
- new /obj/item/wallframe/airalarm( user.loc )
- qdel(src)
- return
-
- return ..()
-
-/obj/machinery/airalarm/AltClick(mob/user)
- ..()
- if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc))
- return
- else
- togglelock(user)
-
-/obj/machinery/airalarm/proc/togglelock(mob/living/user)
- if(stat & (NOPOWER|BROKEN))
- to_chat(user, "It does nothing!")
- else
- if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
- locked = !locked
- updateUsrDialog()
- to_chat(user, "You [ locked ? "lock" : "unlock"] the air alarm interface.")
- else
- to_chat(user, "Access denied.")
- return
-
-/obj/machinery/airalarm/power_change()
- ..()
- if(stat & NOPOWER)
- set_light(0)
- update_icon()
-
-/obj/machinery/airalarm/emag_act(mob/user)
- if(obj_flags & EMAGGED)
- return
- obj_flags |= EMAGGED
- visible_message("Sparks fly out of [src]!", "You emag [src], disabling its safeties.")
- playsound(src, "sparks", 50, 1)
-
-/obj/machinery/airalarm/obj_break(damage_flag)
- ..()
- update_icon()
- set_light(0)
-
-/obj/machinery/airalarm/deconstruct(disassembled = TRUE)
- if(!(flags_1 & NODECONSTRUCT_1))
- new /obj/item/stack/sheet/metal(loc, 2)
- var/obj/item/I = new /obj/item/electronics/airalarm(loc)
- if(!disassembled)
- I.obj_integrity = I.max_integrity * 0.5
- new /obj/item/stack/cable_coil(loc, 3)
- qdel(src)
-
-#undef AALARM_MODE_SCRUBBING
-#undef AALARM_MODE_VENTING
-#undef AALARM_MODE_PANIC
-#undef AALARM_MODE_REPLACEMENT
-#undef AALARM_MODE_OFF
-#undef AALARM_MODE_FLOOD
-#undef AALARM_MODE_SIPHON
-#undef AALARM_MODE_CONTAMINATED
-#undef AALARM_MODE_REFILL
-#undef AALARM_REPORT_TIMEOUT
+/datum/tlv
+ var/min2
+ var/min1
+ var/max1
+ var/max2
+
+/datum/tlv/New(min2 as num, min1 as num, max1 as num, max2 as num)
+ if(min2) src.min2 = min2
+ if(min1) src.min1 = min1
+ if(max1) src.max1 = max1
+ if(max2) src.max2 = max2
+
+/datum/tlv/proc/get_danger_level(val as num)
+ if(max2 != -1 && val >= max2)
+ return 2
+ if(min2 != -1 && val <= min2)
+ return 2
+ if(max1 != -1 && val >= max1)
+ return 1
+ if(min1 != -1 && val <= min1)
+ return 1
+ return 0
+
+/datum/tlv/no_checks
+ min2 = -1
+ min1 = -1
+ max1 = -1
+ max2 = -1
+
+/datum/tlv/dangerous
+ min2 = -1
+ min1 = -1
+ max1 = 0.2
+ max2 = 0.5
+
+/obj/item/electronics/airalarm
+ name = "air alarm electronics"
+ icon_state = "airalarm_electronics"
+
+/obj/item/wallframe/airalarm
+ name = "air alarm frame"
+ desc = "Used for building Air Alarms."
+ icon = 'icons/obj/monitors.dmi'
+ icon_state = "alarm_bitem"
+ result_path = /obj/machinery/airalarm
+
+#define AALARM_MODE_SCRUBBING 1
+#define AALARM_MODE_VENTING 2 //makes draught
+#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet)
+#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing
+#define AALARM_MODE_OFF 5
+#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents
+#define AALARM_MODE_SIPHON 7 //Scrubbers suck air
+#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing.
+#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output
+
+#define AALARM_REPORT_TIMEOUT 100
+
+#define AALARM_OVERLAY_OFF "alarm_off"
+#define AALARM_OVERLAY_GREEN "alarm_green"
+#define AALARM_OVERLAY_WARN "alarm_amber"
+#define AALARM_OVERLAY_DANGER "alarm_red"
+
+/obj/machinery/airalarm
+ name = "air alarm"
+ desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
+ icon = 'icons/obj/monitors.dmi'
+ icon_state = "alarm0"
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 4
+ active_power_usage = 8
+ power_channel = ENVIRON
+ req_access = list(ACCESS_ATMOSPHERICS)
+ max_integrity = 250
+ integrity_failure = 80
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
+ resistance_flags = FIRE_PROOF
+
+ var/danger_level = 0
+ var/mode = AALARM_MODE_SCRUBBING
+
+ var/locked = TRUE
+ var/aidisabled = 0
+ var/shorted = 0
+ var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
+ var/brightness_on = 1
+
+ var/frequency = FREQ_ATMOS_CONTROL
+ var/alarm_frequency = FREQ_ATMOS_ALARMS
+ var/datum/radio_frequency/radio_connection
+
+ var/list/TLV = list( // Breathable air.
+ "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa
+ "temperature" = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66),
+ /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
+ /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000),
+ /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10),
+ /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5),
+ /datum/gas/plasma = new/datum/tlv/dangerous,
+ /datum/gas/nitrous_oxide = new/datum/tlv/dangerous,
+ /datum/gas/bz = new/datum/tlv/dangerous,
+ /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic
+ /datum/gas/water_vapor = new/datum/tlv/dangerous,
+ /datum/gas/tritium = new/datum/tlv/dangerous,
+ /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
+ /datum/gas/nitryl = new/datum/tlv/dangerous,
+ /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
+ )
+
+/obj/machinery/airalarm/server // No checks here.
+ TLV = list(
+ "pressure" = new/datum/tlv/no_checks,
+ "temperature" = new/datum/tlv/no_checks,
+ /datum/gas/oxygen = new/datum/tlv/no_checks,
+ /datum/gas/nitrogen = new/datum/tlv/no_checks,
+ /datum/gas/carbon_dioxide = new/datum/tlv/no_checks,
+ /datum/gas/miasma = new/datum/tlv/no_checks,
+ /datum/gas/plasma = new/datum/tlv/no_checks,
+ /datum/gas/nitrous_oxide = new/datum/tlv/no_checks,
+ /datum/gas/bz = new/datum/tlv/no_checks,
+ /datum/gas/hypernoblium = new/datum/tlv/no_checks,
+ /datum/gas/water_vapor = new/datum/tlv/no_checks,
+ /datum/gas/tritium = new/datum/tlv/no_checks,
+ /datum/gas/stimulum = new/datum/tlv/no_checks,
+ /datum/gas/nitryl = new/datum/tlv/no_checks,
+ /datum/gas/pluoxium = new/datum/tlv/no_checks
+ )
+
+/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures.
+ TLV = list(
+ "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa
+ "temperature" = new/datum/tlv(T0C-73.15, T0C-63.15, T0C, T0C+10),
+ /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
+ /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000),
+ /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10),
+ /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5),
+ /datum/gas/plasma = new/datum/tlv/dangerous,
+ /datum/gas/nitrous_oxide = new/datum/tlv/dangerous,
+ /datum/gas/bz = new/datum/tlv/dangerous,
+ /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic
+ /datum/gas/water_vapor = new/datum/tlv/dangerous,
+ /datum/gas/tritium = new/datum/tlv/dangerous,
+ /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
+ /datum/gas/nitryl = new/datum/tlv/dangerous,
+ /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
+ )
+
+/obj/machinery/airalarm/unlocked
+ locked = FALSE
+
+/obj/machinery/airalarm/engine
+ name = "engine air alarm"
+ locked = FALSE
+ req_access = null
+ req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_ENGINE)
+
+/obj/machinery/airalarm/mixingchamber
+ name = "chamber air alarm"
+ locked = FALSE
+ req_access = null
+ req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_TOX, ACCESS_TOX_STORAGE)
+
+/obj/machinery/airalarm/all_access
+ name = "all-access air alarm"
+ desc = "This particular atmos control unit appears to have no access restrictions."
+ locked = FALSE
+ req_access = null
+ req_one_access = null
+
+/obj/machinery/airalarm/syndicate //general syndicate access
+ req_access = list(ACCESS_SYNDICATE)
+
+/obj/machinery/airalarm/directional/north //Pixel offsets get overwritten on New()
+ dir = SOUTH
+ pixel_y = 24
+
+/obj/machinery/airalarm/directional/south
+ dir = NORTH
+ pixel_y = -24
+
+/obj/machinery/airalarm/directional/east
+ dir = WEST
+ pixel_x = 24
+
+/obj/machinery/airalarm/directional/west
+ dir = EAST
+ pixel_x = -24
+
+//all air alarms in area are connected via magic
+/area
+ var/list/air_vent_names = list()
+ var/list/air_scrub_names = list()
+ var/list/air_vent_info = list()
+ var/list/air_scrub_info = list()
+
+/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild)
+ . = ..()
+ wires = new /datum/wires/airalarm(src)
+
+ if(ndir)
+ setDir(ndir)
+
+ if(nbuild)
+ buildstage = 0
+ panel_open = TRUE
+ pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
+ pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0
+
+ if(name == initial(name))
+ name = "[get_area_name(src)] Air Alarm"
+
+ power_change()
+ set_frequency(frequency)
+
+/obj/machinery/airalarm/Destroy()
+ SSradio.remove_object(src, frequency)
+ qdel(wires)
+ wires = null
+ return ..()
+
+/obj/machinery/airalarm/examine(mob/user)
+ . = ..()
+ switch(buildstage)
+ if(0)
+ to_chat(user, "It is missing air alarm electronics.")
+ if(1)
+ to_chat(user, "It is missing wiring.")
+ if(2)
+ to_chat(user, "Alt-click to [locked ? "unlock" : "lock"] the interface.")
+
+/obj/machinery/airalarm/ui_status(mob/user)
+ if(user.has_unlimited_silicon_privilege && aidisabled)
+ to_chat(user, "AI control has been disabled.")
+ else if(!shorted)
+ return ..()
+ return UI_CLOSE
+
+/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state)
+ ui.open()
+
+/obj/machinery/airalarm/ui_data(mob/user)
+ var/data = list(
+ "locked" = locked,
+ "siliconUser" = user.has_unlimited_silicon_privilege,
+ "emagged" = (obj_flags & EMAGGED ? 1 : 0),
+ "danger_level" = danger_level,
+ )
+
+ var/area/A = get_area(src)
+ data["atmos_alarm"] = A.atmosalm
+ data["fire_alarm"] = A.fire
+
+ var/turf/T = get_turf(src)
+ var/datum/gas_mixture/environment = T.return_air()
+ var/datum/tlv/cur_tlv
+
+ data["environment_data"] = list()
+ var/pressure = environment.return_pressure()
+ cur_tlv = TLV["pressure"]
+ data["environment_data"] += list(list(
+ "name" = "Pressure",
+ "value" = pressure,
+ "unit" = "kPa",
+ "danger_level" = cur_tlv.get_danger_level(pressure)
+ ))
+ var/temperature = environment.temperature
+ cur_tlv = TLV["temperature"]
+ data["environment_data"] += list(list(
+ "name" = "Temperature",
+ "value" = temperature,
+ "unit" = "K ([round(temperature - T0C, 0.1)]C)",
+ "danger_level" = cur_tlv.get_danger_level(temperature)
+ ))
+ var/total_moles = environment.total_moles()
+ var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
+ for(var/gas_id in environment.gases)
+ if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
+ continue
+ cur_tlv = TLV[gas_id]
+ data["environment_data"] += list(list(
+ "name" = GLOB.meta_gas_names[gas_id],
+ "value" = environment.gases[gas_id] / total_moles * 100,
+ "unit" = "%",
+ "danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id] * partial_pressure)
+ ))
+
+ if(!locked || user.has_unlimited_silicon_privilege)
+ data["vents"] = list()
+ for(var/id_tag in A.air_vent_names)
+ var/long_name = A.air_vent_names[id_tag]
+ var/list/info = A.air_vent_info[id_tag]
+ if(!info || info["frequency"] != frequency)
+ continue
+ data["vents"] += list(list(
+ "id_tag" = id_tag,
+ "long_name" = sanitize(long_name),
+ "power" = info["power"],
+ "checks" = info["checks"],
+ "excheck" = info["checks"]&1,
+ "incheck" = info["checks"]&2,
+ "direction" = info["direction"],
+ "external" = info["external"],
+ "internal" = info["internal"],
+ "extdefault"= (info["external"] == ONE_ATMOSPHERE),
+ "intdefault"= (info["internal"] == 0)
+ ))
+ data["scrubbers"] = list()
+ for(var/id_tag in A.air_scrub_names)
+ var/long_name = A.air_scrub_names[id_tag]
+ var/list/info = A.air_scrub_info[id_tag]
+ if(!info || info["frequency"] != frequency)
+ continue
+ data["scrubbers"] += list(list(
+ "id_tag" = id_tag,
+ "long_name" = sanitize(long_name),
+ "power" = info["power"],
+ "scrubbing" = info["scrubbing"],
+ "widenet" = info["widenet"],
+ "filter_types" = info["filter_types"]
+ ))
+ data["mode"] = mode
+ data["modes"] = list()
+ data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0))
+ data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0))
+ data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0))
+ data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 1))
+ data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1))
+ data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1))
+ data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1))
+ data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0))
+ if(obj_flags & EMAGGED)
+ data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1))
+
+ var/datum/tlv/selected
+ var/list/thresholds = list()
+
+ selected = TLV["pressure"]
+ thresholds += list(list("name" = "Pressure", "settings" = list()))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2))
+
+ selected = TLV["temperature"]
+ thresholds += list(list("name" = "Temperature", "settings" = list()))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2))
+
+ for(var/gas_id in GLOB.meta_gas_names)
+ if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
+ continue
+ selected = TLV[gas_id]
+ thresholds += list(list("name" = GLOB.meta_gas_names[gas_id], "settings" = list()))
+ thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min2", "selected" = selected.min2))
+ thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min1", "selected" = selected.min1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max1", "selected" = selected.max1))
+ thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max2", "selected" = selected.max2))
+
+ data["thresholds"] = thresholds
+ return data
+
+/obj/machinery/airalarm/ui_act(action, params)
+ if(..() || buildstage != 2)
+ return
+ if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
+ return
+ var/device_id = params["id_tag"]
+ switch(action)
+ if("lock")
+ if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
+ locked = !locked
+ . = TRUE
+ if("power", "toggle_filter", "widenet", "scrubbing")
+ send_signal(device_id, list("[action]" = params["val"]), usr)
+ . = TRUE
+ if("excheck")
+ send_signal(device_id, list("checks" = text2num(params["val"])^1), usr)
+ . = TRUE
+ if("incheck")
+ send_signal(device_id, list("checks" = text2num(params["val"])^2), usr)
+ . = TRUE
+ if("set_external_pressure", "set_internal_pressure")
+ var/area/A = get_area(src)
+ var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null
+ if(!isnull(target) && !..())
+ send_signal(device_id, list("[action]" = target), usr)
+ . = TRUE
+ if("reset_external_pressure")
+ send_signal(device_id, list("reset_external_pressure"), usr)
+ . = TRUE
+ if("reset_internal_pressure")
+ send_signal(device_id, list("reset_internal_pressure"), usr)
+ . = TRUE
+ if("threshold")
+ var/env = params["env"]
+ if(text2path(env))
+ env = text2path(env)
+
+ var/name = params["var"]
+ var/datum/tlv/tlv = TLV[env]
+ if(isnull(tlv))
+ return
+ var/value = input("New [name] for [env]:", name, tlv.vars[name]) as num|null
+ if(!isnull(value) && !..())
+ if(value < 0)
+ tlv.vars[name] = -1
+ else
+ tlv.vars[name] = round(value, 0.01)
+ investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS)
+ . = TRUE
+ if("mode")
+ mode = text2num(params["mode"])
+ investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS)
+ apply_mode()
+ . = TRUE
+ if("alarm")
+ var/area/A = get_area(src)
+ if(A.atmosalert(2, src))
+ post_alert(2)
+ . = TRUE
+ if("reset")
+ var/area/A = get_area(src)
+ if(A.atmosalert(0, src))
+ post_alert(0)
+ . = TRUE
+ update_icon()
+
+/obj/machinery/airalarm/proc/reset(wire)
+ switch(wire)
+ if(WIRE_POWER)
+ if(!wires.is_cut(WIRE_POWER))
+ shorted = FALSE
+ update_icon()
+ if(WIRE_AI)
+ if(!wires.is_cut(WIRE_AI))
+ aidisabled = FALSE
+
+
+/obj/machinery/airalarm/proc/shock(mob/user, prb)
+ if((stat & (NOPOWER))) // unpowered, no shock
+ return 0
+ if(!prob(prb))
+ return 0 //you lucked out, no shock for you
+ var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
+ s.set_up(5, 1, src)
+ s.start() //sparks always.
+ if (electrocute_mob(user, get_area(src), src, 1, TRUE))
+ return 1
+ else
+ return 0
+
+/obj/machinery/airalarm/proc/refresh_all()
+ var/area/A = get_area(src)
+ for(var/id_tag in A.air_vent_names)
+ var/list/I = A.air_vent_info[id_tag]
+ if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
+ continue
+ send_signal(id_tag, list("status"))
+ for(var/id_tag in A.air_scrub_names)
+ var/list/I = A.air_scrub_info[id_tag]
+ if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
+ continue
+ send_signal(id_tag, list("status"))
+
+/obj/machinery/airalarm/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM)
+
+/obj/machinery/airalarm/proc/send_signal(target, list/command, mob/user)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new(command)
+ signal.data["tag"] = target
+ signal.data["sigtype"] = "command"
+ signal.data["user"] = user
+ radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM)
+
+ return 1
+
+/obj/machinery/airalarm/proc/get_mode_name(mode_value)
+ switch(mode_value)
+ if(AALARM_MODE_SCRUBBING)
+ return "Filtering"
+ if(AALARM_MODE_CONTAMINATED)
+ return "Contaminated"
+ if(AALARM_MODE_VENTING)
+ return "Draught"
+ if(AALARM_MODE_REFILL)
+ return "Refill"
+ if(AALARM_MODE_PANIC)
+ return "Panic Siphon"
+ if(AALARM_MODE_REPLACEMENT)
+ return "Cycle"
+ if(AALARM_MODE_SIPHON)
+ return "Siphon"
+ if(AALARM_MODE_OFF)
+ return "Off"
+ if(AALARM_MODE_FLOOD)
+ return "Flood"
+
+/obj/machinery/airalarm/proc/apply_mode()
+ var/area/A = get_area(src)
+ switch(mode)
+ if(AALARM_MODE_SCRUBBING)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma),
+ "scrubbing" = 1,
+ "widenet" = 0,
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "checks" = 1,
+ "set_external_pressure" = ONE_ATMOSPHERE
+ ))
+ if(AALARM_MODE_CONTAMINATED)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "set_filters" = list(
+ /datum/gas/carbon_dioxide,
+ /datum/gas/miasma,
+ /datum/gas/plasma,
+ /datum/gas/water_vapor,
+ /datum/gas/hypernoblium,
+ /datum/gas/nitrous_oxide,
+ /datum/gas/nitryl,
+ /datum/gas/tritium,
+ /datum/gas/bz,
+ /datum/gas/stimulum,
+ /datum/gas/pluoxium
+ ),
+ "scrubbing" = 1,
+ "widenet" = 1,
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "checks" = 1,
+ "set_external_pressure" = ONE_ATMOSPHERE
+ ))
+ if(AALARM_MODE_VENTING)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "widenet" = 0,
+ "scrubbing" = 0
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "checks" = 1,
+ "set_external_pressure" = ONE_ATMOSPHERE*2
+ ))
+ if(AALARM_MODE_REFILL)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma),
+ "scrubbing" = 1,
+ "widenet" = 0,
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "checks" = 1,
+ "set_external_pressure" = ONE_ATMOSPHERE * 3
+ ))
+ if(AALARM_MODE_PANIC,
+ AALARM_MODE_REPLACEMENT)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "widenet" = 1,
+ "scrubbing" = 0
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 0
+ ))
+ if(AALARM_MODE_SIPHON)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "widenet" = 0,
+ "scrubbing" = 0
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 0
+ ))
+
+ if(AALARM_MODE_OFF)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 0
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 0
+ ))
+ if(AALARM_MODE_FLOOD)
+ for(var/device_id in A.air_scrub_names)
+ send_signal(device_id, list(
+ "power" = 0
+ ))
+ for(var/device_id in A.air_vent_names)
+ send_signal(device_id, list(
+ "power" = 1,
+ "checks" = 2,
+ "set_internal_pressure" = 0
+ ))
+
+/obj/machinery/airalarm/update_icon()
+ set_light(0)
+ cut_overlays()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
+ if(stat & NOPOWER)
+ icon_state = "alarm0"
+ return
+
+ if(stat & BROKEN)
+ icon_state = "alarmx"
+ return
+
+ if(panel_open)
+ switch(buildstage)
+ if(2)
+ icon_state = "alarmx"
+ if(1)
+ icon_state = "alarm_b2"
+ if(0)
+ icon_state = "alarm_b1"
+ return
+
+ icon_state = "alarm1"
+ var/overlay_state = AALARM_OVERLAY_OFF
+ var/area/A = get_area(src)
+ switch(max(danger_level, A.atmosalm))
+ if(0)
+ add_overlay(AALARM_OVERLAY_GREEN)
+ overlay_state = AALARM_OVERLAY_GREEN
+ light_color = LIGHT_COLOR_GREEN
+ set_light(brightness_on)
+ if(1)
+ add_overlay(AALARM_OVERLAY_WARN)
+ overlay_state = AALARM_OVERLAY_WARN
+ light_color = LIGHT_COLOR_LAVA
+ set_light(brightness_on)
+ if(2)
+ add_overlay(AALARM_OVERLAY_DANGER)
+ overlay_state = AALARM_OVERLAY_DANGER
+ light_color = LIGHT_COLOR_RED
+ set_light(brightness_on)
+
+ SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
+ update_light()
+
+/obj/machinery/airalarm/process()
+ if((stat & (NOPOWER|BROKEN)) || shorted)
+ return
+
+ var/turf/location = get_turf(src)
+ if(!location)
+ return
+
+ var/datum/tlv/cur_tlv
+
+ var/datum/gas_mixture/environment = location.return_air()
+ var/list/env_gases = environment.gases
+ var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume
+
+ cur_tlv = TLV["pressure"]
+ var/environment_pressure = environment.return_pressure()
+ var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
+
+ cur_tlv = TLV["temperature"]
+ var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
+
+ var/gas_dangerlevel = 0
+ for(var/gas_id in env_gases)
+ if(!(gas_id in TLV)) // We're not interested in this gas, it seems.
+ continue
+ cur_tlv = TLV[gas_id]
+ gas_dangerlevel = max(gas_dangerlevel, cur_tlv.get_danger_level(env_gases[gas_id] * partial_pressure))
+
+ GAS_GARBAGE_COLLECT(environment.gases)
+
+ var/old_danger_level = danger_level
+ danger_level = max(pressure_dangerlevel, temperature_dangerlevel, gas_dangerlevel)
+
+ if(old_danger_level != danger_level)
+ apply_danger_level()
+ if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05)
+ mode = AALARM_MODE_SCRUBBING
+ apply_mode()
+
+ return
+
+
+/obj/machinery/airalarm/proc/post_alert(alert_level)
+ var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
+
+ if(!frequency)
+ return
+
+ var/datum/signal/alert_signal = new(list(
+ "zone" = get_area_name(src),
+ "type" = "Atmospheric"
+ ))
+ if(alert_level==2)
+ alert_signal.data["alert"] = "severe"
+ else if (alert_level==1)
+ alert_signal.data["alert"] = "minor"
+ else if (alert_level==0)
+ alert_signal.data["alert"] = "clear"
+
+ frequency.post_signal(src, alert_signal, range = -1)
+
+/obj/machinery/airalarm/proc/apply_danger_level()
+ var/area/A = get_area(src)
+
+ var/new_area_danger_level = 0
+ for(var/obj/machinery/airalarm/AA in A)
+ if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
+ new_area_danger_level = max(new_area_danger_level,AA.danger_level)
+ if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state
+ post_alert(new_area_danger_level)
+
+ update_icon()
+
+/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params)
+ switch(buildstage)
+ if(2)
+ if(istype(W, /obj/item/wirecutters) && panel_open && wires.is_all_cut())
+ W.play_tool_sound(src)
+ to_chat(user, "You cut the final wires.")
+ new /obj/item/stack/cable_coil(loc, 5)
+ buildstage = 1
+ update_icon()
+ return
+ else if(istype(W, /obj/item/screwdriver)) // Opening that Air Alarm up.
+ W.play_tool_sound(src)
+ panel_open = !panel_open
+ to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].")
+ update_icon()
+ return
+ else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card
+ togglelock(user)
+ else if(panel_open && is_wire_tool(W))
+ wires.interact(user)
+ return
+ if(1)
+ if(istype(W, /obj/item/crowbar))
+ user.visible_message("[user.name] removes the electronics from [src.name].",\
+ "You start prying out the circuit...")
+ W.play_tool_sound(src)
+ if (W.use_tool(src, user, 20))
+ if (buildstage == 1)
+ to_chat(user, "You remove the air alarm electronics.")
+ new /obj/item/electronics/airalarm( src.loc )
+ playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
+ buildstage = 0
+ update_icon()
+ return
+
+ if(istype(W, /obj/item/stack/cable_coil))
+ var/obj/item/stack/cable_coil/cable = W
+ if(cable.get_amount() < 5)
+ to_chat(user, "You need five lengths of cable to wire the air alarm!")
+ return
+ user.visible_message("[user.name] wires the air alarm.", \
+ "You start wiring the air alarm...")
+ if (do_after(user, 20, target = src))
+ if (cable.get_amount() >= 5 && buildstage == 1)
+ cable.use(5)
+ to_chat(user, "You wire the air alarm.")
+ wires.repair()
+ aidisabled = 0
+ locked = FALSE
+ mode = 1
+ shorted = 0
+ post_alert(0)
+ buildstage = 2
+ update_icon()
+ return
+ if(0)
+ if(istype(W, /obj/item/electronics/airalarm))
+ if(user.temporarilyRemoveItemFromInventory(W))
+ to_chat(user, "You insert the circuit.")
+ buildstage = 1
+ update_icon()
+ qdel(W)
+ return
+
+ if(istype(W, /obj/item/electroadaptive_pseudocircuit))
+ var/obj/item/electroadaptive_pseudocircuit/P = W
+ if(!P.adapt_circuit(user, 25))
+ return
+ user.visible_message("[user] fabricates a circuit and places it into [src].", \
+ "You adapt an air alarm circuit and slot it into the assembly.")
+ buildstage = 1
+ update_icon()
+ return
+
+ if(istype(W, /obj/item/wrench))
+ to_chat(user, "You detach \the [src] from the wall.")
+ W.play_tool_sound(src)
+ new /obj/item/wallframe/airalarm( user.loc )
+ qdel(src)
+ return
+
+ return ..()
+
+/obj/machinery/airalarm/AltClick(mob/user)
+ ..()
+ if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc))
+ return
+ else
+ togglelock(user)
+
+/obj/machinery/airalarm/proc/togglelock(mob/living/user)
+ if(stat & (NOPOWER|BROKEN))
+ to_chat(user, "It does nothing!")
+ else
+ if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
+ locked = !locked
+ updateUsrDialog()
+ to_chat(user, "You [ locked ? "lock" : "unlock"] the air alarm interface.")
+ else
+ to_chat(user, "Access denied.")
+ return
+
+/obj/machinery/airalarm/power_change()
+ ..()
+ if(stat & NOPOWER)
+ set_light(0)
+ update_icon()
+
+/obj/machinery/airalarm/emag_act(mob/user)
+ . = ..()
+ if(obj_flags & EMAGGED)
+ return
+ obj_flags |= EMAGGED
+ visible_message("Sparks fly out of [src]!", "You emag [src], disabling its safeties.")
+ playsound(src, "sparks", 50, 1)
+ return TRUE
+
+/obj/machinery/airalarm/obj_break(damage_flag)
+ ..()
+ update_icon()
+ set_light(0)
+
+/obj/machinery/airalarm/deconstruct(disassembled = TRUE)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ new /obj/item/stack/sheet/metal(loc, 2)
+ var/obj/item/I = new /obj/item/electronics/airalarm(loc)
+ if(!disassembled)
+ I.obj_integrity = I.max_integrity * 0.5
+ new /obj/item/stack/cable_coil(loc, 3)
+ qdel(src)
+
+#undef AALARM_MODE_SCRUBBING
+#undef AALARM_MODE_VENTING
+#undef AALARM_MODE_PANIC
+#undef AALARM_MODE_REPLACEMENT
+#undef AALARM_MODE_OFF
+#undef AALARM_MODE_FLOOD
+#undef AALARM_MODE_SIPHON
+#undef AALARM_MODE_CONTAMINATED
+#undef AALARM_MODE_REFILL
+#undef AALARM_REPORT_TIMEOUT
diff --git a/code/modules/cargo/bounties/engineering.dm b/code/modules/cargo/bounties/engineering.dm
index e10d48f3bf..cf2cd7d93c 100644
--- a/code/modules/cargo/bounties/engineering.dm
+++ b/code/modules/cargo/bounties/engineering.dm
@@ -14,10 +14,10 @@
return FALSE
return T.air_contents.gases[gas_type] >= moles_required
-/datum/bounty/item/engineering/gas/nitryl_tank
- name = "Full Tank of Nitryl"
- description = "The non-human staff of Station 88 has been volunteered to test performance enhancing drugs. Ship them a tank full of Nitryl so they can get started."
- gas_type = /datum/gas/nitryl
+//datum/bounty/item/engineering/gas/nitryl_tank
+// name = "Full Tank of Nitryl"
+// description = "The non-human staff of Station 88 has been volunteered to test performance enhancing drugs. Ship them a tank full of Nitryl so they can get started."
+// gas_type = /datum/gas/nitryl
/datum/bounty/item/engineering/gas/tritium_tank
name = "Full Tank of Tritium"
@@ -37,6 +37,55 @@
required_count = 10 //easy to make
wanted_types = list(/obj/machinery/portable_atmospherics/canister)
+/datum/bounty/item/engineering/microwave
+ name = "Microwaves"
+ description = "Due to a shortage of microwaves, our chefs are incapable of keeping up with our sheer volume of orders. We need at least three microwaves to keep up with our crew's dietary habits."
+ reward = 2000
+ required_count = 3
+ wanted_types = list(/obj/machinery/microwave)
+
+/datum/bounty/item/engineering/hydroponicstrays
+ name = "Hydroponics Tray"
+ description = "The garden has become a hot spot of late, they need a few more hydroponics tray to grow more flowers."
+ reward = 2500
+ required_count = 5
+ wanted_types = list(/obj/machinery/hydroponics)
+
+/datum/bounty/item/engineering/rcd
+ name = "Spare RCD"
+ description = "Construction and repairs to are shuttles are going slowly. As it turns out, we're a little short on RCDs, can you send us a few?"
+ reward = 2500
+ required_count = 3
+ wanted_types = list(/obj/item/construction/rcd)
+
+/datum/bounty/item/engineering/rpd
+ name = "Spare RPD"
+ description = "Our Atmospheric Technicians are still living in the past, relying on stationary pipe dispensers to produce the pipes necessary to accomplish their strenuous tasks. They could use an upgrade. Could you send us some Rapid Pipe Dispensers?"
+ reward = 3000
+ required_count = 3
+ wanted_types = list(/obj/item/pipe_dispenser)
+
+/datum/bounty/item/engineering/heaters
+ name = "Space Heaters"
+ description = "The kitchen freezer was left open and now the whole place is frozen solid! We need a few space heaters to warm it back up before anyone gets hungry."
+ reward = 3000
+ required_count = 5
+ wanted_types = list(/obj/machinery/space_heater)
+
+/datum/bounty/item/engineering/arcadetrail
+ name = "Orion Trail Arcade Games"
+ description = "The staff have nothing to do when off-work. Can you send us some Orion Trail games to play?"
+ reward = 3000
+ required_count = 5
+ wanted_types = list(/obj/machinery/computer/arcade/orion_trail)
+
+/datum/bounty/item/engineering/arcadebattle
+ name = "Battle Arcade Games"
+ description = "The staff have nothing to do when off-work. Can you send us some Battle Arcade games to play?"
+ reward = 3000
+ required_count = 5
+ wanted_types = list(/obj/machinery/computer/arcade/battle)
+
/datum/bounty/item/engineering/energy_ball
name = "Contained Tesla Ball"
description = "Station 24 is being overrun by hordes of angry Mothpeople. They are requesting the ultimate bug zapper."
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index b5b9a616d0..1a607b4b96 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -36,6 +36,7 @@
cat |= EXPORT_EMAG
/obj/machinery/computer/cargo/emag_act(mob/user)
+ . = ..()
if(obj_flags & EMAGGED)
return
user.visible_message("[user] swipes a suspicious card through [src]!",
@@ -48,6 +49,8 @@
var/obj/item/circuitboard/computer/cargo/board = circuit
board.contraband = TRUE
board.obj_flags |= EMAGGED
+ req_access = list()
+ return TRUE
/obj/machinery/computer/cargo/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 5288c27ff6..0097346a34 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -54,6 +54,7 @@
..()
/obj/machinery/computer/cargo/express/emag_act(mob/living/user)
+ . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
if(obj_flags & EMAGGED)
return
user.visible_message("[user] swipes a suspicious card through [src]!",
@@ -63,6 +64,8 @@
var/obj/item/circuitboard/computer/cargo/board = circuit
board.obj_flags |= EMAGGED
packin_up()
+ req_access = list()
+ return TRUE
/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry
meme_pack_data = list() // sorry for what?
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 5656287c52..1087f1ebb5 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -1699,7 +1699,7 @@
/datum/supply_pack/service/janitor/janpremium
name = "Janitor Premium Supplies"
desc = "Do to the union for better supplies, we have desided to make a deal for you, In this crate you can get a brand new chem, Drying Angent this stuff is the work of slimes or magic! This crate also contains a rag to test out the Drying Angent magic, three wet floor signs, and some spare bottles of ammonia."
- cost = 3000
+ cost = 1750
access = ACCESS_JANITOR
contains = list(/obj/item/caution,
/obj/item/caution,
@@ -1707,9 +1707,20 @@
/obj/item/reagent_containers/rag,
/obj/item/reagent_containers/glass/bottle/ammonia,
/obj/item/reagent_containers/glass/bottle/ammonia,
+ /obj/item/reagent_containers/glass/bottle/ammonia,
/obj/item/reagent_containers/spray/drying_agent)
crate_name = "janitor backpack crate"
+/datum/supply_pack/service/janitor/janpimp
+ name = "Custodial Cruiser"
+ desc = "Clown steal your ride? Assistant lock it in the dorms? Order a new one and get back to cleaning in style!"
+ cost = 3000
+ access = ACCESS_JANITOR
+ contains = list(/obj/vehicle/ridden/janicart,
+ /obj/item/key/janitor)
+ crate_name = "janitor ride crate"
+ crate_type = /obj/structure/closet/crate/large
+
/datum/supply_pack/service/mule
name = "MULEbot Crate"
desc = "Pink-haired Quartermaster not doing her job? Replace her with this tireless worker, today!"
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 06f23574e1..5442fcb932 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -817,6 +817,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
var/file = GLOB.vox_sounds[name]
Export("##action=load_rsc", file)
stoplag()
+ for (var/name in GLOB.vox_sounds_male)
+ var/file = GLOB.vox_sounds_male[name]
+ Export("##action=load_rsc", file)
+ stoplag()
#endif
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 4cf7c0178b..d487b873b1 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -144,6 +144,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"breasts_size" = "C",
"breasts_shape" = "Pair",
"breasts_fluid" = "milk",
+ "breasts_producing" = FALSE,
"has_vag" = FALSE,
"vag_shape" = "Human",
"vag_color" = "fff",
@@ -743,6 +744,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += " Change "
dat += "Cup Size:[features["breasts_size"]]"
dat += "Breast Shape:[features["breasts_shape"]]"
+ dat += "Lactates:[features["breasts_producing"] == TRUE ? "Yes" : "No"]"
dat += ""
dat += ""
dat += " "
@@ -917,11 +919,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += " | Description |
"
for(var/j in GLOB.loadout_items[gear_tab])
var/datum/gear/gear = GLOB.loadout_items[gear_tab][j]
- var/donoritem
- if(gear.ckeywhitelist && gear.ckeywhitelist.len)
- donoritem = TRUE
- if(!(user.ckey in gear.ckeywhitelist))
- continue
+ var/donoritem = gear.donoritem
+ if(donoritem && !gear.donator_ckey_check(user.ckey))
+ continue
var/class_link = ""
if(gear.type in chosen_gear)
class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=0'"
@@ -2056,6 +2056,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["eggsack_internal"] = !features["eggsack_internal"]
if("has_breasts")
features["has_breasts"] = !features["has_breasts"]
+ if(features["has_breasts"] == FALSE)
+ features["breasts_producing"] = FALSE
+ if("breasts_producing")
+ features["breasts_producing"] = !features["breasts_producing"]
if("has_vag")
features["has_vag"] = !features["has_vag"]
if(features["has_vag"] == FALSE)
@@ -2239,7 +2243,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!is_loadout_slot_available(G.category))
to_chat(user, "