Merge remote-tracking branch 'upstream/master'

This commit is contained in:
DeltaFire
2021-09-13 20:08:39 +02:00
237 changed files with 14450 additions and 6737 deletions
+3
View File
@@ -75,6 +75,9 @@
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
if(occupant)
to_chat(user, "<span class='boldnotice'>The VR Sleeper is already occupied!</span>")
return
close_machine(target)
ui_interact(user)
+1 -1
View File
@@ -195,7 +195,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
// #endif
/datum/admins/proc/create_or_modify_area,
/datum/admins/proc/fixcorruption,
#ifdef REFERENCE_TRACKING
#ifdef EXTOOLS_REFERENCE_TRACKING
/datum/admins/proc/view_refs,
/datum/admins/proc/view_del_failures,
#endif
+13 -2
View File
@@ -2862,7 +2862,7 @@
return
if(!CONFIG_GET(string/centcom_ban_db))
to_chat(usr, "<span class='warning'>Centcom Galactic Ban DB is disabled!</span>")
to_chat(usr, span_warning("Centcom Galactic Ban DB is disabled!"))
return
var/ckey = href_list["centcomlookup"]
@@ -2889,8 +2889,19 @@
dat += "<center><b>0 bans detected for [ckey]</b></center>"
else
bans = json_decode(response["body"])
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
//Ignore bans from non-whitelisted sources, if a whitelist exists
var/list/valid_sources
if(CONFIG_GET(string/centcom_source_whitelist))
valid_sources = splittext(CONFIG_GET(string/centcom_source_whitelist), ",")
dat += "<center><b>Bans detected for [ckey]</b></center>"
else
//Ban count is potentially inaccurate if they're using a whitelist
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
for(var/list/ban in bans)
if(valid_sources && !(ban["sourceName"] in valid_sources))
continue
dat += "<b>Server: </b> [sanitize(ban["sourceName"])]<br>"
dat += "<b>RP Level: </b> [sanitize(ban["sourceRoleplayLevel"])]<br>"
dat += "<b>Type: </b> [sanitize(ban["type"])]<br>"
@@ -1,4 +1,4 @@
#ifdef REFERENCE_TRACKING
#ifdef EXTOOLS_REFERENCE_TRACKING
GLOBAL_LIST_EMPTY(deletion_failures)
@@ -102,29 +102,21 @@ GLOBAL_LIST_EMPTY(deletion_failures)
#endif
#ifdef LEGACY_REFERENCE_TRACKING
#ifdef REFERENCE_TRACKING
/datum/verb/legacy_find_refs()
set category = "Debug"
set name = "Find References"
set src in world
find_references_legacy(FALSE)
/datum/proc/find_references_legacy(skip_alert)
/datum/proc/find_references(skip_alert)
running_find_references = type
if(usr?.client)
if(usr.client.running_find_references)
testing("CANCELLED search for references to a [usr.client.running_find_references].")
log_reftracker("CANCELLED search for references to a [usr.client.running_find_references].")
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
SSgarbage.update_nextfire(reset_time = TRUE)
return
if(!skip_alert && alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
if(!skip_alert && tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes")
running_find_references = null
return
@@ -134,92 +126,122 @@ GLOBAL_LIST_EMPTY(deletion_failures)
if(usr?.client)
usr.client.running_find_references = type
testing("Beginning search for references to a [type].")
last_find_references = world.time
log_reftracker("Beginning search for references to a [type].")
var/starting_time = world.time
//Time to search the whole game for our ref
DoSearchVar(GLOB, "GLOB") //globals
log_reftracker("Finished searching globals")
DoSearchVar(GLOB) //globals
for(var/datum/thing in world) //atoms (don't beleive its lies)
DoSearchVar(thing, "World -> [thing]")
DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time)
log_reftracker("Finished searching atoms")
for(var/datum/thing) //datums
DoSearchVar(thing, "World -> [thing]")
DoSearchVar(thing, "Datums -> [thing.type]", search_time = starting_time)
log_reftracker("Finished searching datums")
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
for(var/client/thing) //clients
DoSearchVar(thing, "World -> [thing]")
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
log_reftracker("Finished searching clients")
log_reftracker("Completed search for references to a [type].")
testing("Completed search for references to a [type].")
if(usr?.client)
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
SSgarbage.update_nextfire(reset_time = TRUE)
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time)
#ifdef REFERENCE_TRACKING_DEBUG
if(!found_refs && SSgarbage.should_save_refs)
found_refs = list()
#endif
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
set src in world
qdel(src, TRUE) //force a qdel
if(!running_find_references)
find_references_legacy(TRUE)
/datum/verb/qdel_then_if_fail_find_references()
set category = "Debug"
set name = "qdel() then Find References if GC failure"
set src in world
qdel_and_find_ref_if_fail(src, TRUE)
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64)
if(usr?.client && !usr.client.running_find_references)
return
if(!recursive_limit)
log_reftracker("Recursion limit reached. [container_name]")
return
if(istype(potential_container, /datum))
var/datum/datum_container = potential_container
if(datum_container.last_find_references == last_find_references)
return
datum_container.last_find_references = last_find_references
var/list/vars_list = datum_container.vars
for(var/varname in vars_list)
if (varname == "vars")
continue
var/variable = vars_list[varname]
if(variable == src)
testing("Found [type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]")
else if(islist(variable))
DoSearchVar(variable, "[container_name] -> list", recursive_limit - 1)
else if(islist(potential_container))
var/normal = IS_NORMAL_LIST(potential_container)
for(var/element_in_list in potential_container)
if(element_in_list == src)
testing("Found [type] \ref[src] in list [container_name].")
else if(element_in_list && !isnum(element_in_list) && normal && potential_container[element_in_list] == src)
testing("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
else if(islist(element_in_list))
DoSearchVar(element_in_list, "[container_name] -> list", recursive_limit - 1)
//Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all
#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
if(istype(potential_container, /datum))
var/datum/datum_container = potential_container
if(datum_container.last_find_references == search_time)
return
datum_container.last_find_references = search_time
var/list/vars_list = datum_container.vars
for(var/varname in vars_list)
#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
continue
var/variable = vars_list[varname]
if(variable == src)
#ifdef REFERENCE_TRACKING_DEBUG
if(SSgarbage.should_save_refs)
found_refs[varname] = TRUE
#endif
log_reftracker("Found [type] \ref[src] in [datum_container.type]'s \ref[datum_container] [varname] var. [container_name]")
continue
if(islist(variable))
DoSearchVar(variable, "[container_name] \ref[datum_container] -> [varname] (list)", recursive_limit - 1, search_time)
else if(islist(potential_container))
var/normal = IS_NORMAL_LIST(potential_container)
var/list/potential_cache = potential_container
for(var/element_in_list in potential_cache)
#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
//Check normal entrys
if(element_in_list == src)
#ifdef REFERENCE_TRACKING_DEBUG
if(SSgarbage.should_save_refs)
found_refs[potential_cache] = TRUE
#endif
log_reftracker("Found [type] \ref[src] in list [container_name].")
continue
var/assoc_val = null
if(!isnum(element_in_list) && normal)
assoc_val = potential_cache[element_in_list]
//Check assoc entrys
if(assoc_val == src)
#ifdef REFERENCE_TRACKING_DEBUG
if(SSgarbage.should_save_refs)
found_refs[potential_cache] = TRUE
#endif
log_reftracker("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
continue
//We need to run both of these checks, since our object could be hiding in either of them
//Check normal sublists
if(islist(element_in_list))
DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
//Check assoc sublists
if(islist(assoc_val))
DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", recursive_limit - 1, search_time)
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
SSgarbage.reference_find_on_fail[REF(thing_to_del)] = TRUE
qdel(thing_to_del, force)
thing_to_del.qdel_and_find_ref_if_fail(force)
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
SSgarbage.reference_find_on_fail["\ref[src]"] = TRUE
qdel(src, force)
#endif
@@ -47,7 +47,7 @@
usr.client.debug_variables(src)
return
#ifdef REFERENCE_TRACKING
#ifdef EXTOOLS_REFERENCE_TRACKING
if(href_list[VV_HK_VIEW_REFERENCES])
var/datum/D = locate(href_list[VV_HK_TARGET])
if(!D)
@@ -253,7 +253,7 @@
purpose_fulfilled = TRUE
make_glow()
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 125)
sound_to_playing_players(volume = 100, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/ratvar_rises.ogg')) //End the sounds
sound_to_playing_players('sound/effects/ratvar_rises.ogg', 90, FALSE, channel = CHANNEL_JUSTICAR_ARK)
sleep(125)
make_glow()
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
@@ -318,19 +318,19 @@
if(-INFINITY to GATEWAY_REEBE_FOUND)
if(!second_sound_played)
sound_to_playing_players('sound/magic/clockwork/invoke_general.ogg', 30, FALSE)
sound_to_playing_players(volume = 10, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
sound_to_playing_players('sound/effects/clockcult_gateway_charging.ogg', 10, FALSE, channel = CHANNEL_JUSTICAR_ARK)
second_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_charging"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
if(!third_sound_played)
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', TRUE))
sound_to_playing_players('sound/effects/clockcult_gateway_active.ogg', 30, FALSE, channel = CHANNEL_JUSTICAR_ARK)
third_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_active"
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
if(!fourth_sound_played)
sound_to_playing_players(volume = 70, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', TRUE))
sound_to_playing_players('sound/effects/clockcult_gateway_closing.ogg', 70, FALSE, channel = CHANNEL_JUSTICAR_ARK)
fourth_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_closing"
@@ -260,7 +260,7 @@
if((IS_HERETIC(local_user) || IS_HERETIC_MONSTER(local_user)) && HAS_TRAIT(src,TRAIT_NODROP))
REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
for(var/mob/living/carbon/human/human_in_range in spiral_range(9,local_user))
for(var/mob/living/carbon/human/human_in_range in viewers(9,local_user))
if(IS_HERETIC(human_in_range) || IS_HERETIC_MONSTER(human_in_range))
continue
@@ -2,7 +2,7 @@
name = "Prison Escapee"
uniform = /obj/item/clothing/under/rank/prisoner
shoes = /obj/item/clothing/shoes/sneakers/orange
r_pocket = /obj/item/kitchen/knife
r_pocket = /obj/item/kitchen/knife/shiv
/datum/outfit/prisoner/post_equip(mob/living/carbon/human/H, visualsOnly=FALSE)
if(visualsOnly)
@@ -45,5 +45,5 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
// Any effects that need to be cleaned up if traitor class is being swapped.
/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
/datum/traitor_class/proc/on_process(datum/antagonist/traitor/T)
// only for processing traitor classes; runs once an SSprocessing tick
+10 -8
View File
@@ -3,21 +3,21 @@
/datum/asset/simple/tgui_common
keep_local_name = TRUE
assets = list(
"tgui-common.bundle.js" = 'tgui/public/tgui-common.bundle.js',
"tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
)
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
"tgui.bundle.js" = 'tgui/public/tgui.bundle.js',
"tgui.bundle.css" = 'tgui/public/tgui.bundle.css',
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
)
/datum/asset/simple/tgui_panel
keep_local_name = TRUE
assets = list(
"tgui-panel.bundle.js" = 'tgui/public/tgui-panel.bundle.js',
"tgui-panel.bundle.css" = 'tgui/public/tgui-panel.bundle.css',
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
)
/datum/asset/simple/headers
@@ -168,10 +168,12 @@
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = 'tgui/packages/tgfont/dist/tgfont.eot',
"tgfont.woff2" = 'tgui/packages/tgfont/dist/tgfont.woff2',
"tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"),
"tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"),
)
parents = list(
"tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"),
)
parents = list("tgfont.css" = 'tgui/packages/tgfont/dist/tgfont.css')
/datum/asset/spritesheet/chat
name = "chat"
@@ -75,6 +75,9 @@
/obj/machinery/atmospherics/pipe/setPipenet(datum/pipeline/P)
parent = P
/obj/machinery/atmospherics/pipe/zap_act(power, zap_flags)
return 0 // they're not really machines in the normal sense, probably shouldn't explode
/obj/machinery/atmospherics/pipe/Destroy()
QDEL_NULL(parent)
+5
View File
@@ -59,6 +59,11 @@
unit_name = "alien hide"
export_types = list(/obj/item/stack/sheet/animalhide/xeno)
/datum/export/stack/licenseplate
cost = 25
unit_name = "license plate"
export_types = list(/obj/item/stack/license_plates/filled)
// Common materials.
// For base materials, see materials.dm
+6
View File
@@ -70,6 +70,12 @@
cost = 1500
contains = list(/obj/item/toy/plush/beeplushie)
/datum/supply_pack/goody/dyespray
name = "Hair Dye Spray"
desc = "A cool spray to dye your hair with awesome colors!"
cost = PAYCHECK_EASY * 2
contains = list(/obj/item/dyespray)
/datum/supply_pack/goody/beach_ball
name = "Beach Ball"
desc = "The simple beach ball is one of Nanotrasen's most popular products. 'Why do we make beach balls? Because we can! (TM)' - Nanotrasen"
+7
View File
@@ -20,6 +20,13 @@
cost = 300 //thrice their export value
contains = list(/obj/item/stack/sheet/cardboard/fifty)
/datum/supply_pack/materials/license50
name = "50 Empty License Plates"
desc = "Create a bunch of boxes."
cost = 1000 // 50 * 25 + 700 - 1000 = 950 credits profit
contains = list(/obj/item/stack/license_plates/empty/fifty)
crate_name = "empty license plate crate"
/datum/supply_pack/materials/glass50
crate_type = /obj/structure/closet/secure_closet/cargo
name = "50 Glass Sheets"
+28
View File
@@ -119,6 +119,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/hair_color = "000000" //Hair color
var/facial_hair_style = "Shaved" //Face hair type
var/facial_hair_color = "000000" //Facial hair color
var/grad_style //Hair gradient style
var/grad_color = "FFFFFF" //Hair gradient color
var/skin_tone = "caucasian1" //Skin color
var/use_custom_skin_tone = FALSE
var/left_eye_color = "000000" //Eye color
@@ -505,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<a href='?_src_=prefs;preference=previous_facehair_style;task=input'>&lt;</a> <a href='?_src_=prefs;preference=next_facehair_style;task=input'>&gt;</a><BR>"
dat += "<span style='border: 1px solid #161616; background-color: #[facial_hair_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=facial;task=input'>Change</a><BR>"
dat += "<h3>Hair Gradient</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=grad_style;task=input'>[grad_style]</a>"
dat += "<a href='?_src_=prefs;preference=previous_grad_style;task=input'>&lt;</a> <a href='?_src_=prefs;preference=next_grad_style;task=input'>&gt;</a><BR>"
dat += "<span style='border: 1px solid #161616; background-color: #[grad_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=grad_color;task=input'>Change</a><BR>"
dat += "</td>"
//Mutant stuff
var/mutant_category = 0
@@ -1711,6 +1719,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("previous_facehair_style")
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
if("grad_color")
var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null
if(new_grad_color)
grad_color = sanitize_hexcolor(new_grad_color, 6)
if("grad_style")
var/new_grad_style
new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list
if(new_grad_style)
grad_style = new_grad_style
if("next_grad_style")
grad_style = next_list_item(grad_style, GLOB.hair_gradients_list)
if("previous_grad_style")
grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list)
if("cycle_bg")
bgstate = next_list_item(bgstate, bgstate_options)
@@ -3016,6 +3041,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.dna.skin_tone_override = use_custom_skin_tone ? skin_tone : null
character.hair_style = hair_style
character.facial_hair_style = facial_hair_style
character.grad_style = grad_style
character.grad_color = grad_color
character.underwear = underwear
character.saved_underwear = underwear
@@ -3080,6 +3107,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(additional_language && additional_language != "None")
var/language_entry = GLOB.roundstart_languages[additional_language]
if(language_entry)
character.additional_language = language_entry
character.grant_language(language_entry, TRUE, TRUE)
//limb stuff, only done when initially spawning in
@@ -683,6 +683,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["skin_tone"] >> skin_tone
S["hair_style_name"] >> hair_style
S["facial_style_name"] >> facial_hair_style
S["grad_style"] >> grad_style
S["grad_color"] >> grad_color
S["underwear"] >> underwear
S["undie_color"] >> undie_color
S["undershirt"] >> undershirt
@@ -875,6 +877,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None")
grad_color = sanitize_hexcolor(grad_color, 6, FALSE)
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
@@ -1044,6 +1048,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["skin_tone"] , skin_tone)
WRITE_FILE(S["hair_style_name"] , hair_style)
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
WRITE_FILE(S["grad_style"] , grad_style)
WRITE_FILE(S["grad_color"] , grad_color)
WRITE_FILE(S["underwear"] , underwear)
WRITE_FILE(S["undie_color"] , undie_color)
WRITE_FILE(S["undershirt"] , undershirt)
+2
View File
@@ -83,6 +83,7 @@
icon_state = "clown"
item_state = "clown_hat"
dye_color = "clown"
w_class = WEIGHT_CLASS_SMALL
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/item_action/adjust)
@@ -131,6 +132,7 @@
clothing_flags = ALLOWINTERNALS
icon_state = "mime"
item_state = "mime"
w_class = WEIGHT_CLASS_SMALL
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/item_action/adjust)
+17 -17
View File
@@ -30,7 +30,7 @@
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
back = /obj/item/storage/backpack/captain
belt = /obj/item/storage/belt/security/full
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer=1,\
/obj/item/gun/energy/e_gun=1)
@@ -50,7 +50,7 @@
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert
glasses = /obj/item/clothing/glasses/thermal/eyepatch
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/e_gun=1)
@@ -58,7 +58,7 @@
/datum/outfit/ert/commander/alert/red
name = "ERT Commander - Red Alert"
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/pulse/pistol/loyalpin=1)
@@ -71,7 +71,7 @@
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
belt = /obj/item/storage/belt/security/full
back = /obj/item/storage/backpack/security
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/storage/box/handcuffs=1,\
/obj/item/clothing/mask/gas/sechailer=1,\
/obj/item/gun/energy/e_gun/stun=1,\
@@ -91,7 +91,7 @@
name = "ERT Security - Amber Alert"
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/sec
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/storage/box/handcuffs=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/melee/baton/loaded=1,\
@@ -99,7 +99,7 @@
/datum/outfit/ert/security/alert/red
name = "ERT Security - Red Alert"
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/storage/box/handcuffs=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/melee/baton/loaded=1,\
@@ -114,7 +114,7 @@
back = /obj/item/storage/backpack/satchel/med
belt = /obj/item/storage/belt/medical
r_hand = /obj/item/storage/firstaid/regular
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer=1,\
/obj/item/gun/energy/e_gun=1,\
@@ -135,7 +135,7 @@
name = "ERT Medic - Amber Alert"
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/med
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/e_gun=1,\
@@ -144,7 +144,7 @@
/datum/outfit/ert/medic/alert/red
name = "ERT Medic - Red Alert"
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
@@ -161,7 +161,7 @@
belt = /obj/item/storage/belt/utility/full
l_pocket = /obj/item/rcd_ammo/large
r_hand = /obj/item/storage/firstaid/regular
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer=1,\
/obj/item/gun/energy/e_gun=1,\
@@ -181,7 +181,7 @@
name = "ERT Engineer - Amber Alert"
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/engi
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/e_gun=1,\
@@ -189,7 +189,7 @@
/datum/outfit/ert/engineer/alert/red
name = "ERT Engineer - Red Alert"
backpack_contents = list(/obj/item/storage/box/engineer=1,\
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/melee/baton/loaded=1,\
/obj/item/clothing/mask/gas/sechailer/swat=1,\
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
@@ -260,7 +260,7 @@
name = "Inquisition Commander"
r_hand = /obj/item/nullrod/scythe/talking/chainsword
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal
backpack_contents = list(/obj/item/storage/box/engineer=1,
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
/obj/item/clothing/mask/gas/sechailer=1,
/obj/item/gun/energy/e_gun=1)
@@ -269,7 +269,7 @@
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
backpack_contents = list(/obj/item/storage/box/engineer=1,
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
/obj/item/storage/box/handcuffs=1,
/obj/item/clothing/mask/gas/sechailer=1,
/obj/item/gun/energy/e_gun/stun=1,
@@ -281,7 +281,7 @@
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
backpack_contents = list(/obj/item/storage/box/engineer=1,
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
/obj/item/melee/baton/loaded=1,
/obj/item/clothing/mask/gas/sechailer=1,
/obj/item/gun/energy/e_gun=1,
@@ -307,7 +307,7 @@
glasses = /obj/item/clothing/glasses/hud/health
back = /obj/item/storage/backpack/cultpack
belt = /obj/item/storage/belt/soulstone
backpack_contents = list(/obj/item/storage/box/engineer=1,
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
/obj/item/nullrod=1,
/obj/item/clothing/mask/gas/sechailer=1,
/obj/item/gun/energy/e_gun=1,
@@ -319,7 +319,7 @@
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
belt = /obj/item/storage/belt/soulstone/full/chappy
backpack_contents = list(/obj/item/storage/box/engineer=1,
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
/obj/item/grenade/chem_grenade/holy=1,
/obj/item/nullrod=1,
/obj/item/clothing/mask/gas/sechailer=1,
@@ -71,6 +71,12 @@
head = /obj/item/clothing/head/helmet/space/plasmaman/security/hos
uniform = /obj/item/clothing/under/plasmaman/security/hos
/datum/outfit/plasmaman/prisoner
name = "Prisoner Plasmaman"
head = /obj/item/clothing/head/helmet/space/plasmaman/prisoner
uniform = /obj/item/clothing/under/plasmaman/prisoner
/datum/outfit/plasmaman/cargo
name = "Cargo Plasmaman"
+1 -1
View File
@@ -28,7 +28,7 @@
id = /obj/item/card/id/syndicate/locked_banking
belt = /obj/item/gun/ballistic/automatic/pistol
l_pocket = /obj/item/paper/fluff/vr/fluke_ops
backpack_contents = list(/obj/item/storage/box/syndie=1,\
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
/obj/item/kitchen/knife/combat/survival)
starting_funds = 0 //Should be operating, not shopping.
@@ -237,12 +237,28 @@
/obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
/obj/item/clothing/head/helmet/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
SIGNAL_HANDLER
if(amount)
name = "reinforced [initial(name)]"
hardsuit_type = "mining_goliath"
if(amount == maxamount)
hardsuit_type = "mining_goliath_full"
icon_state = "hardsuit[on]-[hardsuit_type]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
if(wearer.head == src)
wearer.update_inv_head()
/obj/item/clothing/suit/space/hardsuit/mining
icon_state = "hardsuit-mining"
name = "mining hardsuit"
desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating for wildlife encounters."
item_state = "mining_hardsuit"
hardsuit_type = "mining"
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF
armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75, "wound" = 15)
@@ -254,6 +270,21 @@
/obj/item/clothing/suit/space/hardsuit/mining/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
/obj/item/clothing/suit/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
SIGNAL_HANDLER
if(amount)
name = "reinforced [initial(name)]"
hardsuit_type = "mining_goliath"
if(amount == maxamount)
hardsuit_type = "mining_goliath_full"
icon_state = "hardsuit-[hardsuit_type]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
if(wearer.wear_suit == src)
wearer.update_inv_wear_suit()
//Syndicate hardsuit
/obj/item/clothing/head/helmet/space/hardsuit/syndi
@@ -169,6 +169,10 @@
icon_state = "hos_envirohelm"
item_state = "hos_envirohelm"
/obj/item/clothing/head/helmet/space/plasmaman/prisoner
name = "prisoner's plasma envirosuit helmet"
desc = "A plasmaman containment helmet for prisoners."
/obj/item/clothing/head/helmet/space/plasmaman/medical
name = "medical's plasma envirosuit helmet"
desc = "An envriohelmet designed for plasmaman medical doctors, having two stripes down it's length to denote as much."
+36 -3
View File
@@ -136,17 +136,24 @@
icon_state = "syndievest"
mutantrace_variation = STYLE_DIGITIGRADE
/obj/item/clothing/suit/armor/vest/capcarapace/alt
/obj/item/clothing/suit/toggle/captains_parade
name = "captain's parade jacket"
desc = "For when an armoured vest isn't fashionable enough."
icon_state = "capformal"
item_state = "capspacesuit"
body_parts_covered = CHEST|GROIN|ARMS
armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90, "wound" = 10)
togglename = "buttons"
/obj/item/clothing/suit/toggle/captains_parade/Initialize()
. = ..()
allowed = GLOB.security_wintercoat_allowed
/obj/item/clothing/suit/armor/riot
name = "riot suit"
desc = "A suit of semi-flexible polycarbonate body armor with heavy padding to protect against melee attacks. Helps the wearer resist shoving in close quarters."
icon_state = "swat"
item_state = "swat_suit"
icon_state = "riot"
item_state = "riot"
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
@@ -319,3 +326,29 @@
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50, "wound" = 10)
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal
name = "\improper CentCom formal coat"
desc = "A stylish coat given to CentCom Commanders. Perfect for sending ERTs to suicide missions with style!"
icon_state = "centcom_formal"
item_state = "centcom"
body_parts_covered = CHEST|GROIN|ARMS
armor = list("melee" = 35, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 35, "bio" = 10, "rad" = 10, "fire" = 10, "acid" = 60)
togglename = "buttons"
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal/Initialize()
. = ..()
allowed = GLOB.security_wintercoat_allowed
/obj/item/clothing/suit/toggle/armor/hos/hos_formal
name = "\improper Head of Security's parade jacket"
desc = "For when an armoured vest isn't fashionable enough."
icon_state = "hosformal"
item_state = "hostrench"
body_parts_covered = CHEST|GROIN|ARMS
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 10)
togglename = "buttons"
/obj/item/clothing/suit/toggle/armor/hos/hos_formal/Initialize()
. = ..()
allowed = GLOB.security_wintercoat_allowed
+3
View File
@@ -6,6 +6,7 @@
var/hoodtype = /obj/item/clothing/head/hooded/winterhood //so the chaplain hoodie or other hoodies can override this
///Alternative mode for hiding the hood, instead of storing the hood in the suit it qdels it, useful for when you deal with hooded suit with storage.
var/alternative_mode = FALSE
var/no_t //do not update sprites when pulling up hood so we can avoid oddities with certain mechanics
/obj/item/clothing/suit/hooded/Initialize()
. = ..()
@@ -51,6 +52,8 @@
update_icon()
/obj/item/clothing/suit/hooded/update_icon_state()
if(no_t)
return
icon_state = "[initial(icon_state)]"
if(ishuman(hood?.loc))
var/mob/living/carbon/human/H = hood.loc
@@ -18,3 +18,9 @@
desc = "A slick black and red plasmaman containment suit designed for the head of security, also called the LAW."
icon_state = "hos_envirosuit"
item_state = "hos_envirosuit"
/obj/item/clothing/under/plasmaman/prisoner
name = "prisoner envirosuit"
desc = "An orange envirosuit identifying and protecting a criminal plasmaman."
icon_state = "prisoner_envirosuit"
item_state = "prisoner_envirosuit"
@@ -19,7 +19,7 @@
custom_food_type = /obj/item/reagent_containers/food/snacks/customizable/sandwich
filling_color = "#FFA500"
list_reagents = list(/datum/reagent/consumable/nutriment = 2)
slot_flags = ITEM_SLOT_HEAD
slot_flags = ITEM_SLOT_MASK
customfoodfilling = 0 //to avoid infinite bread-ception
foodtype = GRAIN
dunkable = TRUE
@@ -8,8 +8,6 @@
#define CONE_WAFFLE 8
#define CONE_CHOC 9
/obj/machinery/icecream_vat
name = "ice cream vat"
desc = "Ding-aling ding dong. Get your Nanotrasen-approved ice cream!"
@@ -35,6 +33,8 @@
/datum/reagent/consumable/ethanol/singulo = 6,
/datum/reagent/consumable/peachjuice = 6,
/datum/reagent/consumable/grapejuice = 6)
var/custom_taste
var/custom_color
/obj/machinery/icecream_vat/proc/get_ingredient_list(type)
switch(type)
@@ -99,7 +99,10 @@
dat += "<b>Peach ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_PEACH]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=5'><b>x5</b></a> [product_types[ICECREAM_PEACH]] dollops left. (Ingredients: milk, ice, peach juice)<br>"
dat += "<b>Grape ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_GRAPE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=5'><b>x5</b></a> [product_types[ICECREAM_GRAPE]] dollops left. (Ingredients: milk, ice, grape juice)<br>"
dat += "<b>Blue ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_BLUE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=5'><b>x5</b></a> [product_types[ICECREAM_BLUE]] dollops left. (Ingredients: milk, ice, singulo)<br>"
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br></div>"
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br>"
dat += "<a href='?src=[REF(src)];custom_taste=1;'><b>Change custom taste: [custom_taste ? custom_taste : "Default"]</b></a>"
dat += "<br><a href='?src=[REF(src)];custom_color=1;'><b>Change custom color: [custom_color ? custom_color : "Default"]</b></a>"
dat += "<br><a href='?src=[REF(src)];reset_custom=1;'><b>Reset custom ice cream taste and color to defaults</b></a></div>"
dat += "<br><b>CONES</b><br><div class='statusDisplay'>"
dat += "<b>Waffle cones:</b> <a href='?src=[REF(src)];cone=[CONE_WAFFLE]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=5'><b>x5</b></a> [product_types[CONE_WAFFLE]] cones left. (Ingredients: flour, sugar)<br>"
dat += "<b>Chocolate cones:</b> <a href='?src=[REF(src)];cone=[CONE_CHOC]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=5'><b>x5</b></a> [product_types[CONE_CHOC]] cones left. (Ingredients: flour, sugar, coco powder)<br></div>"
@@ -128,7 +131,7 @@
visible_message("[icon2html(src, viewers(src))] <span class='info'>[user] scoops delicious [flavour_name] ice cream into [I].</span>")
product_types[dispense_flavour] -= 1
if(beaker && beaker.reagents.total_volume)
I.add_ice_cream(flavour_name, beaker.reagents)
I.add_ice_cream(flavour_name, beaker.reagents, custom_color, custom_taste)
else
I.add_ice_cream(flavour_name)
if(I.reagents.total_volume < 10)
@@ -213,14 +216,25 @@
if(href_list["refill"])
RefillFromBeaker()
updateDialog()
if(href_list["refresh"])
updateDialog()
if(href_list["close"])
usr.unset_machine()
usr << browse(null,"window=icecreamvat")
if(href_list["custom_taste"])
custom_taste = stripped_input(usr, "Set a custom taste for the custom icecream. 50 characters max, leave blank to go back to the default option.", max_length = 50)
if(href_list["custom_color"])
custom_color = input(usr, "Choose a color for the custom icecream. Cancel to go back to the default option.") as color|null
if(href_list["reset_custom"])
custom_taste = null
custom_color = null
updateDialog() // i have no clue why we even have refresh when this is a thing but sure
return
/obj/item/reagent_containers/food/snacks/icecream
@@ -251,7 +265,7 @@
desc = "Delicious [cone_name] cone, but no ice cream."
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R)
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R, custom_color, custom_taste)
name = "[flavour_name] icecream"
switch (flavour_name) // adding the actual reagents advertised in the ingredient list
if ("vanilla")
@@ -286,8 +300,11 @@
if(R && R.total_volume >= 4) //consumable reagents have stronger taste so higher volume will allow non-food flavourings to break through better.
var/mutable_appearance/flavoring = mutable_appearance(icon,"icecream_custom")
var/datum/reagent/master = R.get_master_reagent()
flavoring.color = master.color
filling_color = master.color
flavoring.color = custom_color ? custom_color : master.color
filling_color = custom_color ? custom_color : master.color
if(custom_taste)
tastes = list("[custom_taste]" = 1)
reagents.force_alt_taste = TRUE
name = "[master.name] icecream"
desc = "A delicious [cone_type] cone filled with artisanal icecream. Made with real [master.name]. Ain't that something."
R.trans_to(src, 4)
+12
View File
@@ -641,6 +641,18 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
/datum/holiday/easter/getStationPrefix()
return pick("Fluffy","Bunny","Easter","Egg")
/datum/holiday/ianbirthday
name = "Ian's Birthday" //github.com/tgstation/tgstation/commit/de7e4f0de0d568cd6e1f0d7bcc3fd34700598acb
begin_month = SEPTEMBER
begin_day = 9
end_day = 10
/datum/holiday/ianbirthday/greet()
return "Happy birthday, Ian!"
/datum/holiday/ianbirthday/getStationPrefix()
return pick("Ian", "Corgi", "Erro")
//Random citadel thing for halloween species
/proc/force_enable_halloween_species()
var/list/oldlist = SSevents.holidays
+1 -1
View File
@@ -29,7 +29,7 @@
/obj/item/reagent_containers/food/snacks/grown/carrot/attackby(obj/item/I, mob/user, params)
if(I.get_sharpness())
to_chat(user, "<span class='notice'>You sharpen the carrot into a shiv with [I].</span>")
var/obj/item/kitchen/knife/carrotshiv/Shiv = new /obj/item/kitchen/knife/carrotshiv
var/obj/item/kitchen/knife/shiv/carrot/Shiv = new /obj/item/kitchen/knife/shiv/carrot
remove_item_from_storage(user)
qdel(src)
user.put_in_hands(Shiv)
+1 -1
View File
@@ -355,7 +355,7 @@
return list("Assistant", "Captain", "Head of Personnel", "Bartender", "Cook", "Botanist", "Quartermaster", "Cargo Technician",
"Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer",
"Atmospheric Technician", "Chief Medical Officer", "Medical Doctor", "Chemist", "Geneticist", "Virologist", "Paramedic",
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer")
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer", "Prisoner")
/proc/get_all_job_icons() //For all existing HUD icons
return get_all_jobs() + list("Prisoner")
@@ -39,7 +39,7 @@
backpack = /obj/item/storage/backpack/industrial
satchel = /obj/item/storage/backpack/satchel/eng
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
box = /obj/item/storage/box/engineer
box = /obj/item/storage/box/survival/engineer
pda_slot = SLOT_L_STORE
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
@@ -54,7 +54,7 @@
backpack = /obj/item/storage/backpack/industrial
satchel = /obj/item/storage/backpack/satchel/eng
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
box = /obj/item/storage/box/engineer
box = /obj/item/storage/box/survival/engineer
pda_slot = SLOT_L_STORE
chameleon_extras = /obj/item/stamp/ce
@@ -58,7 +58,7 @@
backpack = /obj/item/storage/backpack/security
satchel = /obj/item/storage/backpack/satchel/sec
duffelbag = /obj/item/storage/backpack/duffelbag/sec
box = /obj/item/storage/box/security
box = /obj/item/storage/box/survival/security
implants = list(/obj/item/implant/mindshield)
+24
View File
@@ -0,0 +1,24 @@
datum/job/prisoner
title = "Prisoner"
flag = PRISONER
department_head = list("The Security Team")
department_flag = CIVILIAN
faction = "Station"
total_positions = 2
spawn_positions = 2
supervisors = "the security team"
outfit = /datum/outfit/job/prisoner
plasma_outfit = /datum/outfit/plasmaman/prisoner
display_order = JOB_DISPLAY_ORDER_PRISONER
/datum/outfit/job/prisoner
name = "Prisoner"
jobtype = /datum/job/prisoner
uniform = /obj/item/clothing/under/rank/prisoner
shoes = /obj/item/clothing/shoes/sneakers/orange
id = /obj/item/card/id/prisoner
ears = /obj/item/radio/headset/headset_prisoner
belt = null
@@ -133,7 +133,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
backpack = /obj/item/storage/backpack/security
satchel = /obj/item/storage/backpack/satchel/sec
duffelbag = /obj/item/storage/backpack/duffelbag/sec
box = /obj/item/storage/box/security
box = /obj/item/storage/box/survival/security
implants = list(/obj/item/implant/mindshield)
+1 -1
View File
@@ -45,7 +45,7 @@
backpack = /obj/item/storage/backpack/explorer
satchel = /obj/item/storage/backpack/satchel/explorer
duffelbag = /obj/item/storage/backpack/duffelbag
box = /obj/item/storage/box/survival_mining
box = /obj/item/storage/box/survival/mining
chameleon_extras = /obj/item/gun/energy/kinetic_accelerator
@@ -42,7 +42,7 @@
backpack = /obj/item/storage/backpack/industrial
satchel = /obj/item/storage/backpack/satchel/eng
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
box = /obj/item/storage/box/engineer
box = /obj/item/storage/box/survival/engineer
pda_slot = SLOT_L_STORE
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
+1 -1
View File
@@ -54,7 +54,7 @@
backpack = /obj/item/storage/backpack/security
satchel = /obj/item/storage/backpack/satchel/sec
duffelbag = /obj/item/storage/backpack/duffelbag/sec
box = /obj/item/storage/box/security
box = /obj/item/storage/box/survival/security
implants = list(/obj/item/implant/mindshield)
+1
View File
@@ -43,6 +43,7 @@ GLOBAL_LIST_INIT(civilian_positions, list(
"Chaplain",
"Clown",
"Mime",
"Prisoner",
"Assistant"))
GLOBAL_LIST_INIT(security_positions, list(
@@ -8,61 +8,73 @@
..()
return late ? INITIALIZE_HINT_LATELOAD : INITIALIZE_HINT_QDEL
//airlock helpers
/obj/effect/mapping_helpers/airlock
layer = DOOR_HELPER_LAYER
/obj/effect/mapping_helpers/airlock/Initialize(mapload)
. = ..()
if(!mapload)
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(!airlock)
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
else
payload(airlock)
/obj/effect/mapping_helpers/airlock/proc/payload(obj/machinery/door/airlock/payload)
return
/obj/effect/mapping_helpers/airlock/cyclelink_helper
name = "airlock cyclelink helper"
icon_state = "airlock_cyclelink_helper"
/obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload)
. = ..()
if(!mapload)
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
if(airlock.cyclelinkeddir)
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
else
airlock.cyclelinkeddir = dir
/obj/effect/mapping_helpers/airlock/cyclelink_helper/payload(obj/machinery/door/airlock/airlock)
if(airlock.cyclelinkeddir)
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
else
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
airlock.cyclelinkeddir = dir
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi
name = "airlock multi-cyclelink helper"
icon_state = "airlock_multicyclelink_helper"
var/cycle_id
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi/payload(obj/machinery/door/airlock/airlock)
if(airlock.closeOtherId)
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] closeOtherId, but it's already set!")
else
airlock.closeOtherId = cycle_id
/obj/effect/mapping_helpers/airlock/locked
name = "airlock lock helper"
icon_state = "airlock_locked_helper"
/obj/effect/mapping_helpers/airlock/locked/Initialize(mapload)
. = ..()
if(!mapload)
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
if(airlock.locked)
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
else
airlock.locked = TRUE
/obj/effect/mapping_helpers/airlock/locked/payload(obj/machinery/door/airlock/airlock)
if(airlock.locked)
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
else
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
airlock.locked = TRUE
/obj/effect/mapping_helpers/airlock/unres
name = "airlock unresctricted side helper"
icon_state = "airlock_unres_helper"
/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
. = ..()
if(!mapload)
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
airlock.unres_sides ^= dir
/obj/effect/mapping_helpers/airlock/unres/payload(obj/machinery/door/airlock/airlock)
airlock.unres_sides ^= dir
/obj/effect/mapping_helpers/airlock/abandoned
name = "airlock abandoned helper"
icon_state = "airlock_abandoned"
/obj/effect/mapping_helpers/airlock/abandoned/payload(obj/machinery/door/airlock/airlock)
if(airlock.abandoned)
log_mapping("[src] at [AREACOORD(src)] tried to make [airlock] abandoned but it's already abandoned!")
else
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
airlock.abandoned = TRUE
//needs to do its thing before spawn_rivers() is called
@@ -79,9 +91,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
//This helper applies components to things on the map directly.
/obj/effect/mapping_helpers/component_injector
name = "Component Injector"
icon_state = "component"
late = TRUE
var/target_type
var/target_name
var/all = FALSE //Will inject into all fitting the criteria if true, otherwise first found
var/target_type //Will inject into atoms of this type
var/target_name //Will inject into atoms with this name
var/component_type
//Late init so everything is likely ready and loaded (no warranty)
@@ -98,8 +112,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
continue
var/cargs = build_args()
A._AddComponent(cargs)
if(!all)
qdel(src)
return
if(all)
qdel(src)
return
/obj/effect/mapping_helpers/component_injector/proc/build_args()
return list(component_type)
@@ -115,3 +132,276 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
CRASH("Wrong disease type passed in.")
var/datum/disease/D = new disease_type()
return list(component_type,D)
// /obj/effect/mapping_helpers/component_injector/areabound
// name = "Areabound Injector"
// icon_state = "component_areabound"
// component_type = /datum/component/areabound
// target_type = /atom/movable
/obj/effect/mapping_helpers/dead_body_placer
name = "Dead Body placer"
late = TRUE
icon_state = "deadbodyplacer"
var/bodycount = 2 //number of bodies to spawn
/obj/effect/mapping_helpers/dead_body_placer/LateInitialize()
var/area/a = get_area(src)
var/list/trays = list()
for (var/i in a.contents)
if (istype(i, /obj/structure/bodycontainer/morgue))
trays += i
if(!trays.len)
log_mapping("[src] at [x],[y] could not find any morgues.")
return
for (var/i = 1 to bodycount)
var/obj/structure/bodycontainer/morgue/j = pick(trays)
var/mob/living/carbon/human/h = new /mob/living/carbon/human(j, 1)
h.death()
for (var/part in h.internal_organs) //randomly remove organs from each body, set those we keep to be in stasis
if (prob(40))
qdel(part)
else
var/obj/item/organ/O = part
O.organ_flags |= ORGAN_FROZEN
j.update_appearance()
qdel(src)
//On Ian's birthday, the hop's office is decorated.
/obj/effect/mapping_helpers/ianbirthday
name = "Ian's Bday Helper"
late = TRUE
icon_state = "iansbdayhelper"
var/balloon_clusters = 2
/obj/effect/mapping_helpers/ianbirthday/LateInitialize()
if(locate(/datum/holiday/ianbirthday) in SSevents.holidays)
birthday()
qdel(src)
/obj/effect/mapping_helpers/ianbirthday/proc/birthday()
var/area/a = get_area(src)
var/list/table = list()//should only be one aka the front desk, but just in case...
var/list/openturfs = list()
//confetti and a corgi balloon! (and some list stuff for more decorations)
for(var/thing in a.contents)
if(istype(thing, /obj/structure/table/reinforced))
table += thing
if(isopenturf(thing))
// new /obj/effect/decal/cleanable/confetti(thing)
// if(locate(/obj/structure/bed/dogbed/ian) in thing)
// new /obj/item/toy/balloon/corgi(thing)
// else
openturfs += thing
//cake + knife to cut it!
if(length(table))
var/turf/food_turf = get_turf(pick(table))
new /obj/item/kitchen/knife(food_turf)
var/obj/item/reagent_containers/food/snacks/store/cake/birthday/iancake = new(food_turf)
iancake.desc = "Happy birthday, Ian!"
// remind me to give ian proper baloons!
//some balloons! this picks an open turf and pops a few balloons in and around that turf, yay.
// for(var/i in 1 to balloon_clusters)
// var/turf/clusterspot = pick_n_take(openturfs)
// new /obj/item/toy/balloon(clusterspot)
// var/balloons_left_to_give = 3 //the amount of balloons around the cluster
// var/list/dirs_to_balloon = GLOB.cardinals.Copy()
// while(balloons_left_to_give > 0)
// balloons_left_to_give--
// var/chosen_dir = pick_n_take(dirs_to_balloon)
// var/turf/balloonstep = get_step(clusterspot, chosen_dir)
// var/placed = FALSE
// if(isopenturf(balloonstep))
// var/obj/item/toy/balloon/B = new(balloonstep)//this clumps the cluster together
// placed = TRUE
// if(chosen_dir == NORTH)
// B.pixel_y -= 10
// if(chosen_dir == SOUTH)
// B.pixel_y += 10
// if(chosen_dir == EAST)
// B.pixel_x -= 10
// if(chosen_dir == WEST)
// B.pixel_x += 10
// if(!placed)
// new /obj/item/toy/balloon(clusterspot)
//remind me to add wall decor!
/obj/effect/mapping_helpers/ianbirthday/admin//so admins may birthday any room
name = "generic birthday setup"
icon_state = "bdayhelper"
/obj/effect/mapping_helpers/ianbirthday/admin/LateInitialize()
birthday()
qdel(src)
//Ian, like most dogs, loves a good new years eve party.
/obj/effect/mapping_helpers/iannewyear
name = "Ian's New Years Helper"
late = TRUE
icon_state = "iansnewyrshelper"
/obj/effect/mapping_helpers/iannewyear/LateInitialize()
if(SSevents.holidays && SSevents.holidays[NEW_YEAR])
fireworks()
qdel(src)
/obj/effect/mapping_helpers/iannewyear/proc/fireworks()
var/area/a = get_area(src)
var/list/table = list()//should only be one aka the front desk, but just in case...
var/list/openturfs = list()
for(var/thing in a.contents)
if(istype(thing, /obj/structure/table/reinforced))
table += thing
else if(isopenturf(thing))
if(locate(/obj/structure/bed/dogbed/ian) in thing)
new /obj/item/clothing/head/festive(thing)
var/obj/item/reagent_containers/food/drinks/bottle/champagne/iandrink = new(thing)
iandrink.name = "dog champagne"
iandrink.pixel_y += 8
iandrink.pixel_x += 8
else
openturfs += thing
var/turf/fireworks_turf = get_turf(pick(table))
var/obj/item/storage/box/matches/matchbox = new(fireworks_turf)
matchbox.pixel_y += 8
matchbox.pixel_x -= 3
// new /obj/item/storage/box/fireworks/dangerous(fireworks_turf) //dangerous version for extra holiday memes.
//lets mappers place notes on airlocks with custom info or a pre-made note from a path
/obj/effect/mapping_helpers/airlock_note_placer
name = "Airlock Note Placer"
late = TRUE
icon_state = "airlocknoteplacer"
var/note_info //for writing out custom notes without creating an extra paper subtype
var/note_name //custom note name
var/note_path //if you already have something wrote up in a paper subtype, put the path here
/obj/effect/mapping_helpers/airlock_note_placer/LateInitialize()
var/turf/turf = get_turf(src)
if(note_path && !istype(note_path, /obj/item/paper)) //don't put non-paper in the paper slot thank you
log_mapping("[src] at [x],[y] had an improper note_path path, could not place paper note.")
qdel(src)
if(locate(/obj/machinery/door/airlock) in turf)
var/obj/machinery/door/airlock/found_airlock = locate(/obj/machinery/door/airlock) in turf
if(note_path)
found_airlock.note = note_path
found_airlock.update_appearance()
qdel(src)
if(note_info)
var/obj/item/paper/paper = new /obj/item/paper(src)
if(note_name)
paper.name = note_name
paper.info = "[note_info]"
found_airlock.note = paper
paper.forceMove(found_airlock)
found_airlock.update_appearance()
qdel(src)
log_mapping("[src] at [x],[y] had no note_path or note_info, cannot place paper note.")
qdel(src)
log_mapping("[src] at [x],[y] could not find an airlock on current turf, cannot place paper note.")
qdel(src)
//This helper applies traits to things on the map directly.
/obj/effect/mapping_helpers/trait_injector
name = "Trait Injector"
icon_state = "trait"
late = TRUE
///Will inject into all fitting the criteria if false, otherwise first found.
var/first_match_only = TRUE
///Will inject into atoms of this type.
var/target_type
///Will inject into atoms with this name.
var/target_name
///Name of the trait, in the lower-case text (NOT the upper-case define) form.
var/trait_name
//Late init so everything is likely ready and loaded (no warranty)
/obj/effect/mapping_helpers/trait_injector/LateInitialize()
if(!GLOB.trait_name_map)
GLOB.trait_name_map = generate_trait_name_map()
if(!GLOB.trait_name_map.Find(trait_name))
CRASH("Wrong trait in [type] - [trait_name] is not a trait")
var/turf/target_turf = get_turf(src)
var/matches_found = 0
for(var/a in target_turf.GetAllContents())
var/atom/atom_on_turf = a
if(atom_on_turf == src)
continue
if(target_name && atom_on_turf.name != target_name)
continue
if(target_type && !istype(atom_on_turf,target_type))
continue
ADD_TRAIT(atom_on_turf, trait_name, MAPPING_HELPER_TRAIT)
matches_found++
if(first_match_only)
qdel(src)
return
if(!matches_found)
stack_trace("Trait mapper found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name] | trait name: [trait_name]")
qdel(src)
/// Fetches an external dmi and applies to the target object
/obj/effect/mapping_helpers/custom_icon
name = "Custom Icon Helper"
icon_state = "trait"
late = TRUE
///Will inject into all fitting the criteria if false, otherwise first found.
var/first_match_only = TRUE
///Will inject into atoms of this type.
var/target_type
///Will inject into atoms with this name.
var/target_name
/// This is the var tha will be set with the fetched icon. In case you want to set some secondary icon sheets like inhands and such.
var/target_variable = "icon"
/// This should return raw dmi in response to http get request. For example: "https://github.com/tgstation/SS13-sprites/raw/master/mob/medu.dmi?raw=true"
var/icon_url
/obj/effect/mapping_helpers/custom_icon/LateInitialize()
///TODO put this injector stuff under common root
var/I = fetch_icon(icon_url)
var/turf/target_turf = get_turf(src)
var/matches_found = 0
for(var/a in target_turf.GetAllContents())
var/atom/atom_on_turf = a
if(atom_on_turf == src)
continue
if(target_name && atom_on_turf.name != target_name)
continue
if(target_type && !istype(atom_on_turf,target_type))
continue
atom_on_turf.vars[target_variable] = I
matches_found++
if(first_match_only)
qdel(src)
return
if(!matches_found)
stack_trace("[src] found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name]")
qdel(src)
/obj/effect/mapping_helpers/custom_icon/proc/fetch_icon(url)
var/static/icon_cache = list()
var/static/query_in_progress = FALSE //We're using a single tmp file so keep it linear.
if(query_in_progress)
UNTIL(!query_in_progress)
if(icon_cache[url])
return icon_cache[url]
log_asset("Custom Icon Helper fetching dmi from: [url]")
var/datum/http_request/request = new()
var/file_name = "tmp/custom_map_icon.dmi"
request.prepare(RUSTG_HTTP_METHOD_GET, url , "", "", file_name)
query_in_progress = TRUE
request.begin_async()
UNTIL(request.is_complete())
var/datum/http_response/response = request.into_response()
if(response.errored || response.status_code != 200)
query_in_progress = FALSE
CRASH("Failed to fetch mapped custom icon from url [url], code: [response.status_code], error: [response.error]")
var/icon/I = new(file_name)
icon_cache[url] = I
query_in_progress = FALSE
return I
@@ -30,8 +30,8 @@
qdel(src)
/obj/effect/baseturf_helper/proc/replace_baseturf(turf/thing)
var/list/baseturf_cache = thing.baseturfs
if(length(baseturf_cache))
if(length(thing.baseturfs))
var/list/baseturf_cache = thing.baseturfs.Copy()
for(var/i in baseturf_cache)
if(baseturf_to_replace[i])
baseturf_cache -= i
@@ -44,6 +44,8 @@
else
thing.PlaceOnBottom(null, baseturf)
/obj/effect/baseturf_helper/space
name = "space baseturf editor"
baseturf = /turf/open/space
@@ -79,4 +81,3 @@
/obj/effect/baseturf_helper/lava_land/surface
name = "lavaland baseturf editor"
baseturf = /turf/open/lava/smooth/lava_land_surface
+38 -3
View File
@@ -2,8 +2,9 @@
/obj/item/clothing/suit/hooded/explorer
name = "explorer suit"
desc = "An armoured suit for exploring harsh environments."
icon_state = "explorer"
item_state = "explorer"
icon_state = "explorer-normal"
item_state = "explorer-normal"
var/suit_type = "normal"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
cold_protection = CHEST|GROIN|LEGS|ARMS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
@@ -13,11 +14,15 @@
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe)
resistance_flags = FIRE_PROOF
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC
no_t = TRUE
/obj/item/clothing/head/hooded/explorer
name = "explorer hood"
desc = "An armoured hood for exploring harsh environments."
icon_state = "explorer"
icon_state = "explorer-normal"
item_state = "explorer-normal"
var/suit_type = "normal"
var/basestate = "normal"
body_parts_covered = HEAD
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
@@ -33,10 +38,40 @@
/obj/item/clothing/suit/hooded/explorer/standard/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
/obj/item/clothing/suit/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
SIGNAL_HANDLER
if(amount)
name = "reinforced [initial(name)]"
suit_type = "normal_goliath"
if(amount == maxamount)
suit_type = "normal_goliath_full"
icon_state = "explorer-[suit_type]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
if(wearer.wear_suit == src)
wearer.update_inv_wear_suit()
/obj/item/clothing/head/hooded/explorer/standard/Initialize()
. = ..()
AddComponent(/datum/component/armor_plate)
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
/obj/item/clothing/head/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
SIGNAL_HANDLER
if(amount)
name = "reinforced [initial(name)]"
suit_type = "normal_goliath"
if(amount == maxamount)
suit_type = "normal_goliath_full"
icon_state = "explorer-[suit_type]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
if(wearer.head == src)
wearer.update_inv_head()
/obj/item/clothing/mask/gas/explorer
name = "explorer gas mask"
@@ -1058,3 +1058,58 @@
/datum/sprite_accessory/hair/zone
name = "Zone"
icon_state = "hair_zone"
/datum/sprite_accessory/hair_gradient
icon = 'icons/mob/hair_gradients.dmi'
/datum/sprite_accessory/hair_gradient/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/hair_gradient/fadeup
name = "Fade Up"
icon_state = "fadeup"
/datum/sprite_accessory/hair_gradient/fadedown
name = "Fade Down"
icon_state = "fadedown"
/datum/sprite_accessory/hair_gradient/vertical_split
name = "Vertical Split"
icon_state = "vsplit"
/datum/sprite_accessory/hair_gradient/_split
name = "Horizontal Split"
icon_state = "bottomflat"
/datum/sprite_accessory/hair_gradient/reflected
name = "Reflected"
icon_state = "reflected_high"
/datum/sprite_accessory/hair_gradient/reflected_inverse
name = "Reflected Inverse"
icon_state = "reflected_inverse_high"
/datum/sprite_accessory/hair_gradient/wavy
name = "Wavy"
icon_state = "wavy"
/datum/sprite_accessory/hair_gradient/long_fade_up
name = "Long Fade Up"
icon_state = "long_fade_up"
/datum/sprite_accessory/hair_gradient/long_fade_down
name = "Long Fade Down"
icon_state = "long_fade_down"
/datum/sprite_accessory/hair_gradient/short_fade_up
name = "Short Fade Up"
icon_state = "short_fade_up"
/datum/sprite_accessory/hair_gradient/short_fade_down
name = "Short Fade Down"
icon_state = "short_fade_down"
/datum/sprite_accessory/hair_gradient/wavy_spike
name = "Spiked Wavy"
icon_state = "wavy_spiked"
@@ -38,14 +38,14 @@
icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
name = "Synthetic Lizard - Pecs Light"
icon_state = "synthlizpecslight"
covered_limbs = list("Chest" = MATRIX_GREEN_BLUE)
covered_limbs = list("Chest" = MATRIX_GREEN_BLUE, "Left Arm" = MATRIX_BLUE, "Right Arm" = MATRIX_BLUE, "Left Leg" = MATRIX_GREEN, "Right Leg" = MATRIX_GREEN)
/datum/sprite_accessory/mam_body_markings/synthliz
recommended_species = list("synthliz")
icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi'
name = "Synthetic Lizard - Plates"
icon_state = "synthlizscutes"
covered_limbs = list("Chest" = MATRIX_GREEN)
covered_limbs = list("Chest" = MATRIX_GREEN, "Left Leg" = MATRIX_GREEN, "Right Leg" = MATRIX_GREEN)
//Synth tails
/datum/sprite_accessory/tails/mam_tails/synthliz
+1 -1
View File
@@ -176,7 +176,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
* Hair will always update its dir, so if your sprite has no dirs the haircut will go all over the place.
* |- Ricotez
*/
/mob/dead/observer/update_icon(new_form)
/mob/dead/observer/update_icon(updates=ALL, new_form=null)
. = ..()
if(client) //We update our preferences in case they changed right before update_icon was called.
ghost_accs = client.prefs.ghost_accs
@@ -20,6 +20,11 @@
var/hair_color = "000"
var/hair_style = "Bald"
///Colour used for the hair gradient.
var/grad_color = "000"
///Style used for the hair gradient.
var/grad_style
//Facial hair colour and style
var/facial_hair_color = "000"
var/facial_hair_style = "Shaved"
@@ -81,6 +86,8 @@
tooltips = TRUE
var/additional_language //the additional language this human can speak from their preference selection
/// Unarmed parry data for human
/datum/block_parry_data/unarmed/human
parry_respect_clickdelay = TRUE
@@ -52,6 +52,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/hair_color
///The alpha used by the hair. 255 is completely solid, 0 is invisible.
var/hair_alpha = 255
///The gradient style used for the mob's hair.
var/grad_style
///The gradient color used to color the gradient.
var/grad_color
///Does the species use skintones or not? As of now only used by humans.
var/use_skintones = FALSE
@@ -678,6 +682,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!hair_hidden || dynamic_hair_suffix)
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -HAIR_LAYER)
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
if(!(NOBLOOD in species_traits))
hair_overlay.icon = 'icons/mob/human_parts.dmi'
@@ -713,8 +718,21 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
hair_overlay.color = "#" + hair_color
else
hair_overlay.color = "#" + H.hair_color
//Gradients
grad_style = H.grad_style
grad_color = H.grad_color
if(grad_style)
var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
var/icon/temp = icon(gradient.icon, gradient.icon_state)
var/icon/temp_hair = icon(hair_file, hair_state)
temp.Blend(temp_hair, ICON_ADD)
gradient_overlay.icon = temp
gradient_overlay.color = "#" + grad_color
else
hair_overlay.color = forced_colour
hair_overlay.alpha = hair_alpha
if(OFFSET_HAIR in H.dna.species.offset_features)
@@ -723,6 +741,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(hair_overlay.icon)
standing += hair_overlay
standing += gradient_overlay
if(standing.len)
H.overlays_standing[HAIR_LAYER] = standing
@@ -103,13 +103,16 @@
var/_method = override[thing]
if(_method == ITEM_PARRY)
using_item = thing
tool = using_item
method = ITEM_PARRY
data = using_item.block_parry_data
else if(_method == UNARMED_PARRY)
method = UNARMED_PARRY
tool = src
data = thing
if(!using_item && !method && length(other_items))
using_item = other_items[1]
tool = using_item
method = ITEM_PARRY
data = using_item.block_parry_data
if(!method)
+1 -1
View File
@@ -118,7 +118,7 @@
throwpower = I.throwforce
var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
var/list/block_return = list()
var/total_damage = I.throwforce
var/total_damage = AM.throwforce
if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, block_return) & BLOCK_SUCCESS)
hitpush = FALSE
skipcatch = TRUE
@@ -3,12 +3,15 @@
/**
* Returns the thing in our active hand (whatever is in our active module-slot, in this case)
*
* Arguments
* * get_gripper - If the active module is a gripper, should we return the gripper or the contained item? (if the gripper contains nothing, returns the gripper anyways)
*/
/mob/living/silicon/robot/get_active_held_item()
/mob/living/silicon/robot/get_active_held_item(get_gripper = FALSE)
var/item = module_active
// snowflake handler for the gripper
if(istype(item, /obj/item/weapon/gripper))
var/obj/item/weapon/gripper/G = item
if(istype(item, /obj/item/gripper) && !get_gripper)
var/obj/item/gripper/G = item
if(G.wrapped)
if(G.wrapped.loc != G)
G.wrapped = null
@@ -284,9 +287,14 @@
/**
* Unequips the active held item, if there is one.
*
* Will always consider dropping gripper contents first.
*/
/mob/living/silicon/robot/proc/uneq_active()
if(module_active)
var/obj/item/gripper/gripper = get_active_held_item(TRUE)
if(istype(gripper) && gripper.drop_held())
return
unequip_module_from_slot(module_active, get_selected_module())
/**
@@ -302,11 +310,12 @@
* Checks if the item is currently in a slot.
*
* If the item is found in a slot, this returns TRUE. Otherwise, it returns FALSE
* Modified to accept items inside of grippers, used for `code\modules\tgui\states\hands.dm:27`
* Arguments
* * item_module - the item being checked
*/
/mob/living/silicon/robot/proc/activated(obj/item/item_module)
if(item_module in held_items)
if(get_active_held_item() == item_module || (item_module in held_items))
return TRUE
return FALSE
@@ -328,7 +328,7 @@
/obj/item/crowbar/cyborg,
/obj/item/healthanalyzer,
/obj/item/reagent_containers/borghypo,
/obj/item/weapon/gripper/medical,
/obj/item/gripper/medical,
/obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/syringe,
/obj/item/surgical_drapes,
@@ -448,7 +448,7 @@
/obj/item/analyzer,
/obj/item/storage/part_replacer/cyborg,
/obj/item/holosign_creator/combifan,
/obj/item/weapon/gripper,
/obj/item/gripper,
/obj/item/lightreplacer/cyborg,
/obj/item/geiger_counter/cyborg,
/obj/item/assembly/signaler/cyborg,
@@ -923,7 +923,7 @@
/obj/item/gun/energy/kinetic_accelerator/cyborg,
/obj/item/gun/energy/plasmacutter/cyborg,
/obj/item/gps/cyborg,
/obj/item/weapon/gripper/mining,
/obj/item/gripper/mining,
/obj/item/cyborg_clamp,
/obj/item/stack/marker_beacon,
/obj/item/destTagger,
@@ -1075,7 +1075,7 @@
/obj/item/multitool/cyborg,
/obj/item/storage/part_replacer/cyborg,
/obj/item/holosign_creator/atmos,
/obj/item/weapon/gripper,
/obj/item/gripper,
/obj/item/lightreplacer/cyborg,
/obj/item/stack/sheet/metal/cyborg,
/obj/item/stack/sheet/glass/cyborg,
@@ -147,6 +147,7 @@ GLOBAL_LIST(bad_gremlin_items)
/mob/living/simple_animal/hostile/gremlin/death(gibbed)
walk(src,0)
QDEL_NULL(access_card)
return ..()
/mob/living/simple_animal/hostile/gremlin/Life()
@@ -528,21 +528,7 @@ Difficulty: Very Hard
/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method)
if(..())
var/obj/item/projectile/P = new generated_projectile(get_turf(src))
P.setDir(dir)
switch(dir)
if(NORTH)
P.yo = 20
P.xo = 0
if(EAST)
P.yo = 0
P.xo = 20
if(WEST)
P.yo = 0
P.xo = -20
else
P.yo = -20
P.xo = 0
P.fire()
P.fire(angle2dir(dir))
/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down.
observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again."
@@ -32,7 +32,7 @@ Key procs
/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
var/id
/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
/// Determines order. Lower priorities are applied first.
var/priority = 0
var/flags = NONE
@@ -41,9 +41,9 @@ Key procs
/// Next two variables depend on this: Should we do advanced calculations?
var/complex_calculation = FALSE
/// Absolute max tiles we can boost to
var/absolute_max_tiles_per_second
var/absolute_max_tiles_per_second = INFINITY
/// Max tiles per second we can boost
var/max_tiles_per_second_boost
var/max_tiles_per_second_boost = INFINITY
/// Movetypes this applies to
var/movetypes = ALL
@@ -54,6 +54,8 @@ Key procs
/// Other modification datums this conflicts with.
var/conflicts_with
/datum/movespeed_modifier/New()
. = ..()
if(!id)
@@ -66,8 +68,10 @@ Key procs
if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet.
return existing + multiplicative_slowdown
var/current_tiles = 10 / max(existing, world.tick_lag)
var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second))
return max(minimum_speed, existing + multiplicative_slowdown)
// multiplicative_slowdown is negative due to our first check
var/max_buff_to = max(existing + multiplicative_slowdown, 10 / absolute_max_tiles_per_second, 10 / (current_tiles + max_tiles_per_second_boost))
// never slow the user
return min(existing, max_buff_to)
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
+4 -4
View File
@@ -7,7 +7,7 @@
/datum/movespeed_modifier/reagent/ephedrine
// strong painkiller effect that caps out at slightly above runspeed
multiplicative_slowdown = -1.5
priority = -100
priority = 500
complex_calculation = TRUE
absolute_max_tiles_per_second = 7
@@ -21,14 +21,14 @@
// extremely strong painkiller effect: allows user to run at old sprint speeds but not over by cancelling out slowdowns.
// however, will not make user go faster than that
multiplicative_slowdown = -4
priority = -100
priority = 500
complex_calculation = TRUE
absolute_max_tiles_per_second = 8
absolute_max_tiles_per_second = 10
/datum/movespeed_modifier/reagent/methamphetamine
// very strong painkiller effect that caps out at slightly above runspeed
multiplicative_slowdown = -2.5
priority = -100
priority = 500
complex_calculation = TRUE
absolute_max_tiles_per_second = 7.5
@@ -19,14 +19,14 @@
/datum/movespeed_modifier/status_effect/tased
multiplicative_slowdown = 1.5
priority = 50
priority = 1500
/datum/movespeed_modifier/status_effect/domain
multiplicative_slowdown = 3
/datum/movespeed_modifier/status_effect/tased/no_combat_mode
multiplicative_slowdown = 8
priority = 100
priority = 1500
/datum/movespeed_modifier/status_effect/electrostaff
multiplicative_slowdown = 1
@@ -55,7 +55,7 @@
/datum/movespeed_modifier/status_effect/slime/light_pink
// decently good painkiller + speedup effect
blacklisted_movetypes = FLYING | FLOATING
priority = -150 // someday we really need to make these defines lmao
priority = 500 // someday we really need to make these defines lmao
multiplicative_slowdown = -2
complex_calculation = TRUE
absolute_max_tiles_per_second = 7
@@ -5,12 +5,6 @@
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
circuit = /obj/item/circuitboard/machine/hydroponics/automagic
/obj/machinery/hydroponics/constructable/automagic/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/reagent_containers))
return FALSE //avoid fucky wuckies
..()
/obj/machinery/hydroponics/constructable/automagic/default_unfasten_wrench(mob/user, obj/item/I, time = 20)
. = ..()
if(. == SUCCESSFUL_UNFASTEN)
+17 -14
View File
@@ -41,8 +41,9 @@
#define THERMAL_RELEASE_MODIFIER 350 //Higher == more heat released during reaction, not to be confused with the above values
#define THERMAL_RELEASE_CAP_MODIFIER 250 //Higher == lower cap on how much heat can be released per tick--currently 1.3x old value
#define PLASMA_RELEASE_MODIFIER 750 //Higher == less plasma released by reaction
#define OXYGEN_RELEASE_MODIFIER 325 //Higher == less oxygen released at high temperature/power
#define GAS_RELEASE_MODIFIER 800 //Higher == less gas released by reaction
#define MAX_OXY_MULT 2 //The ratio between oxygen and plasma will approach this as temperature increases
#define OXY_POINT 80 // the temperature above which oxygen output > plasma output
#define REACTION_POWER_MODIFIER 0.55 //Higher == more overall power
@@ -538,14 +539,19 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
//Power * 0.55 * a value between 1 and 0.8
var/device_energy = power * REACTION_POWER_MODIFIER
var/effective_temperature = min(removed.return_temperature(), 2500 * dynamic_heat_modifier)
var/cur_temp = removed.return_temperature() // more readable, better-performing anyway
var/max_temp_increase = effective_temperature + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_CAP_MODIFIER)
//Calculate how much gas to release
//Varies based on power and gas content
removed.adjust_moles(GAS_PLASMA, max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0))
//Varies based on power, gas content, and heat
removed.adjust_moles(GAS_O2, max(((device_energy + effective_temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0))
// we don't want to cap the temperature like the old supermatter but we do want to stop adding more when it's too hot
var/max_temp_increase = min(cur_temp, 2500 * dynamic_heat_modifier) + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_CAP_MODIFIER)
// oxygen ratio increases as temperature does
var/oxy_ratio = HYPERBOLIC_GROWTH(MAX_OXY_MULT, 1 / OXY_POINT, cur_temp, (-OXY_POINT / 2))
// total moles also increases as temperature does
var/released_plasma = min(max((device_energy * dynamic_heat_modifier) / GAS_RELEASE_MODIFIER, 0) / (1+oxy_ratio), 2)
removed.adjust_moles(GAS_PLASMA, released_plasma)
removed.adjust_moles(GAS_O2, released_plasma * oxy_ratio)
if(removed.return_temperature() < max_temp_increase)
removed.adjust_heat(device_energy * dynamic_heat_modifier * THERMAL_RELEASE_MODIFIER)
@@ -599,13 +605,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE
//Uncaps the zap damage, it's maxed by the input power
//Objects take damage now
flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
flags |= (ZAP_MOB_DAMAGE)
zap_count = 3
if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
zap_icon = OVER_9000_ZAP_ICON_STATE
//It'll stun more now, and damage will hit harder, gloves are no garentee.
//Machines go boom
flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
flags |= (ZAP_MOB_STUN | ZAP_MOB_DAMAGE)
zap_count = 4
//Now we deal with damage shit
if (damage > damage_penalty_point && prob(20))
@@ -1058,9 +1064,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(zapdir)
. = zapdir
//Going boom should be rareish
if(prob(80))
zap_flags &= ~ZAP_MACHINE_EXPLOSIVE
if(target_type == COIL)
//In the best situation we can expect this to grow up to 2120kw before a delam/IT'S GONE TOO FAR FRED SHUT IT DOWN
//The formula for power gen is zap_str * zap_mod / 2 * capacitor rating, between 1 and 4
+5 -5
View File
@@ -44,7 +44,7 @@
cell.use(round(cell.charge * severity/100))
chambered = null //we empty the chamber
recharge_newshot() //and try to charge a new shot
update_icon()
update_appearance()
/obj/item/gun/energy/get_cell()
return cell
@@ -61,7 +61,7 @@
recharge_newshot(TRUE)
if(selfcharge)
START_PROCESSING(SSobj, src)
update_icon()
update_appearance()
/obj/item/gun/energy/ComponentInitialize()
. = ..()
@@ -74,7 +74,7 @@
/obj/item/gun/energy/handle_atom_del(atom/A)
if(A == cell)
cell = null
update_icon()
update_appearance()
return ..()
/obj/item/gun/energy/examine(mob/user)
@@ -100,7 +100,7 @@
cell.give(100)
if(!chambered) //if empty chamber we try to charge a new shot
recharge_newshot(TRUE)
update_icon()
update_appearance()
// ATTACK SELF IGNORING PARENT RETURN VALUE
/obj/item/gun/energy/attack_self(mob/living/user)
@@ -174,7 +174,7 @@
if(user_for_feedback)
to_chat(user_for_feedback, "<span class='notice'>[src] is now set to [C.select_name || C].</span>")
post_set_firemode()
update_icon(TRUE)
update_appearance()
/obj/item/gun/energy/proc/post_set_firemode(recharge_newshot = TRUE)
if(recharge_newshot)
+46 -38
View File
@@ -62,6 +62,7 @@
var/fermiIsReacting = FALSE //that prevents multiple reactions from occurring (i.e. add_reagent calls to process_reactions(), this stops any extra reactions.)
var/fermiReactID //instance of the chem reaction used during a fermireaction, kept here so it's cache isn't lost between loops/procs.
var/value_multiplier = DEFAULT_REAGENTS_VALUE //used for cargo reagents selling.
var/force_alt_taste = FALSE
/datum/reagents/New(maximum=100, new_flags = NONE, new_value = DEFAULT_REAGENTS_VALUE)
maximum_volume = maximum
@@ -1120,47 +1121,54 @@
. = locate(type) in cached_reagents
/datum/reagents/proc/generate_taste_message(minimum_percent=15)
// the lower the minimum percent, the more sensitive the message is.
var/list/out = list()
var/list/tastes = list() //descriptor = strength
if(minimum_percent <= 100)
for(var/datum/reagent/R in reagent_list)
if(!R.taste_mult)
continue
if(istype(R, /datum/reagent/consumable/nutriment))
var/list/taste_data = R.data
for(var/taste in taste_data)
var/ratio = taste_data[taste]
var/amount = ratio * R.taste_mult * R.volume
if(taste in tastes)
tastes[taste] += amount
else
tastes[taste] = amount
else
var/taste_desc = R.taste_description
var/taste_amount = R.volume * R.taste_mult
if(taste_desc in tastes)
tastes[taste_desc] += taste_amount
else
tastes[taste_desc] = taste_amount
//deal with percentages
// TODO it would be great if we could sort these from strong to weak
var/total_taste = counterlist_sum(tastes)
if(total_taste > 0)
for(var/taste_desc in tastes)
var/percent = tastes[taste_desc]/total_taste * 100
if(percent < minimum_percent)
if(!force_alt_taste)
// the lower the minimum percent, the more sensitive the message is.
var/list/tastes = list() //descriptor = strength
if(minimum_percent <= 100)
for(var/datum/reagent/R in reagent_list)
if(!R.taste_mult)
continue
var/intensity_desc = "a hint of"
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
intensity_desc = ""
else if(percent > minimum_percent * 3)
intensity_desc = "the strong flavor of"
if(intensity_desc != "")
out += "[intensity_desc] [taste_desc]"
if(istype(R, /datum/reagent/consumable/nutriment))
var/list/taste_data = R.data
for(var/taste in taste_data)
var/ratio = taste_data[taste]
var/amount = ratio * R.taste_mult * R.volume
if(taste in tastes)
tastes[taste] += amount
else
tastes[taste] = amount
else
out += "[taste_desc]"
var/taste_desc = R.taste_description
var/taste_amount = R.volume * R.taste_mult
if(taste_desc in tastes)
tastes[taste_desc] += taste_amount
else
tastes[taste_desc] = taste_amount
//deal with percentages
// TODO it would be great if we could sort these from strong to weak
var/total_taste = counterlist_sum(tastes)
if(total_taste > 0)
for(var/taste_desc in tastes)
var/percent = tastes[taste_desc]/total_taste * 100
if(percent < minimum_percent)
continue
var/intensity_desc = "a hint of"
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
intensity_desc = ""
else if(percent > minimum_percent * 3)
intensity_desc = "the strong flavor of"
if(intensity_desc != "")
out += "[intensity_desc] [taste_desc]"
else
out += "[taste_desc]"
else
// alternate taste is to force the taste of the atom if its a food item
if(my_atom && isfood(my_atom))
var/obj/item/reagent_containers/food/snacks/F = my_atom
out = F.tastes
return english_list(out, "something indescribable")
@@ -554,6 +554,13 @@
color = "#302000" // rgb: 48, 32, 0
taste_description = "wet and cheap noodles"
/datum/reagent/consumable/nutraslop
name = "Nutraslop"
description = "Mixture of leftover prison foods served on previous days."
nutriment_factor = 5 * REAGENTS_METABOLISM
color = "#3E4A00" // rgb: 62, 74, 0
taste_description = "your imprisonment"
/datum/reagent/consumable/hot_ramen/on_mob_life(mob/living/carbon/M)
M.adjust_bodytemperature(10 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL)
..()
@@ -1451,7 +1451,7 @@
/datum/reagent/medicine/changelingadrenaline/on_mob_life(mob/living/carbon/metabolizer, delta_time, times_fired)
..()
metabolizer.AdjustAllImmobility(-20 * REM * delta_time)
metabolizer.adjustStaminaLoss(-10 * REM * delta_time, 0)
metabolizer.adjustStaminaLoss(-30 * REM * delta_time, 0)
metabolizer.Jitter(10 * REM * delta_time)
metabolizer.Dizzy(10 * REM * delta_time)
return TRUE
@@ -1477,7 +1477,7 @@
/datum/reagent/medicine/changelinghaste
name = "Changeling Haste"
description = "Drastically increases movement speed, but deals toxin damage."
description = "Drastically increases movement speed."
color = "#AE151D"
metabolization_rate = 2.5 * REAGENTS_METABOLISM
@@ -1489,12 +1489,6 @@
L.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/changelinghaste)
..()
/datum/reagent/medicine/changelinghaste/on_mob_life(mob/living/carbon/metabolizer, delta_time, times_fired)
metabolizer.adjustToxLoss(2 * REM * delta_time, 0)
..()
return TRUE
/datum/reagent/medicine/corazone
// Heart attack code will not do damage if corazone is present
// because it's SPACE MAGIC ASPIRIN
@@ -58,6 +58,7 @@
name = "combat stimulant injector"
desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat and get people back in the fight."
amount_per_transfer_from_this = 10
item_state = "combat_hypo"
icon_state = "combat_hypo"
volume = 100
ignore_flags = 1 // So they can heal their comrades.
@@ -69,17 +70,27 @@
list_reagents = list(/datum/reagent/medicine/epinephrine = 30, /datum/reagent/medicine/omnizine = 30, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/atropine = 15)
/obj/item/reagent_containers/hypospray/combat/nanites
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical compounds for rapid healing."
name = "experimental combat stimulant injector"
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical nanites and a stimulant for rapid healing and a combat boost."
item_state = "nanite_hypo"
icon_state = "nanite_hypo"
volume = 100
list_reagents = list(/datum/reagent/medicine/adminordrazine/quantum_heal = 80, /datum/reagent/medicine/synaptizine = 20)
/obj/item/reagent_containers/hypospray/magillitis
name = "experimental autoinjector"
desc = "A modified air-needle autoinjector with a small single-use reservoir. It contains an experimental serum."
icon_state = "combat_hypo"
volume = 5
reagent_flags = NONE
list_reagents = list(/datum/reagent/magillitis = 5)
/obj/item/reagent_containers/hypospray/combat/nanites/update_icon()
if(reagents.total_volume > 0)
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]0"
/obj/item/reagent_containers/hypospray/combat/heresypurge
name = "holy water piercing injector"
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water and pacifier mixture. Not for use on your teammates."
item_state = "holy_hypo"
icon_state = "holy_hypo"
volume = 250
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
amount_per_transfer_from_this = 50
//MediPens
@@ -136,6 +147,8 @@
/obj/item/reagent_containers/hypospray/medipen/ekit
name = "emergency first-aid autoinjector"
desc = "An epinephrine medipen with extra coagulant and antibiotics to help stabilize bad cuts and burns."
icon_state = "firstaid"
item_state = "firstaid"
volume = 15
amount_per_transfer_from_this = 15
list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5)
@@ -143,15 +156,19 @@
/obj/item/reagent_containers/hypospray/medipen/blood_loss
name = "hypovolemic-response autoinjector"
desc = "A medipen designed to stabilize and rapidly reverse severe bloodloss."
icon_state = "hypovolemic"
item_state = "hypovolemic"
volume = 15
amount_per_transfer_from_this = 15
list_reagents = list(/datum/reagent/medicine/epinephrine = 5, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/iron = 3.5, /datum/reagent/medicine/salglu_solution = 4)
/obj/item/reagent_containers/hypospray/medipen/stimulants
name = "illegal stimpack medipen"
desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained"
name = "stimpack medipen"
desc = "Contains stimulants."
icon_state = "syndipen"
item_state = "syndipen"
volume = 50
amount_per_transfer_from_this = 10
amount_per_transfer_from_this = 50
list_reagents = list(/datum/reagent/medicine/stimulants = 50)
/obj/item/reagent_containers/hypospray/medipen/stimulants/baseball
@@ -166,6 +183,7 @@
name = "stimpack medipen"
desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor."
icon_state = "stimpen"
item_state = "stimpen"
volume = 20
amount_per_transfer_from_this = 20
list_reagents = list(/datum/reagent/medicine/ephedrine = 10, /datum/reagent/consumable/coffee = 10)
@@ -177,20 +195,79 @@
/obj/item/reagent_containers/hypospray/medipen/morphine
name = "morphine medipen"
desc = "A rapid way to get you out of a tight situation and fast! You'll feel rather drowsy, though."
icon_state = "morphen"
item_state = "morphen"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/morphine = 10)
/obj/item/reagent_containers/hypospray/medipen/penacid
name = "pentetic acid medipen"
desc = "A autoinjector containing pentetic acid, used to reduce high levels of radiations and moderate toxins."
icon_state = "penacid"
item_state = "penacid"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/pen_acid = 10)
/obj/item/reagent_containers/hypospray/medipen/atropine
name = "atropine autoinjector"
desc = "A rapid way to save a person from a critical injury state!"
icon_state = "atropen"
item_state = "atropen"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/atropine = 10)
/obj/item/reagent_containers/hypospray/medipen/salacid
name = "salicyclic acid medipen"
desc = "A autoinjector containing salicyclic acid, used to treat severe brute damage."
icon_state = "salacid"
item_state = "salacid"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/sal_acid = 10)
/obj/item/reagent_containers/hypospray/medipen/oxandrolone
name = "oxandrolone medipen"
desc = "A autoinjector containing oxandrolone, used to treat severe burns."
icon_state = "oxapen"
item_state = "oxapen"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/oxandrolone = 10)
/obj/item/reagent_containers/hypospray/medipen/salbutamol
name = "salbutamol medipen"
desc = "A autoinjector containing salbutamol, used to heal oxygen damage quickly."
icon_state = "salpen"
item_state = "salpen"
volume = 10
amount_per_transfer_from_this = 10
list_reagents = list(/datum/reagent/medicine/salbutamol = 10)
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure
name = "BVAK autoinjector"
desc = "Bio Virus Antidote Kit autoinjector. Has a two use system for yourself, and someone else. Inject when infected."
icon_state = "stimpen"
icon_state = "tbpen"
item_state = "tbpen"
volume = 60
amount_per_transfer_from_this = 30
list_reagents = list(/datum/reagent/medicine/atropine = 10, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/salbutamol = 20, /datum/reagent/medicine/spaceacillin = 20)
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure/update_icon()
if(reagents.total_volume > 30)
icon_state = initial(icon_state)
else if (reagents.total_volume > 0)
icon_state = "[initial(icon_state)]1"
else
icon_state = "[initial(icon_state)]0"
/obj/item/reagent_containers/hypospray/medipen/survival
name = "survival medipen"
desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards. WARNING: Do not inject more than one pen in quick succession."
icon_state = "stimpen"
icon_state = "minepen"
item_state = "minepen"
volume = 52
amount_per_transfer_from_this = 52
list_reagents = list(/datum/reagent/medicine/salbutamol = 10, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/neo_jelly = 15, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/lavaland_extract = 2)
@@ -198,16 +275,21 @@
/obj/item/reagent_containers/hypospray/medipen/firelocker
name = "fire treatment medipen"
desc = "A medipen that has been fulled with burn healing chemicals for personnel without advanced medical knowledge."
icon_state = "firepen"
item_state = "firepen"
volume = 15
amount_per_transfer_from_this = 15
list_reagents = list(/datum/reagent/medicine/oxandrolone = 5, /datum/reagent/medicine/kelotane = 10)
/obj/item/reagent_containers/hypospray/combat/heresypurge
name = "holy water autoinjector"
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water mixture."
volume = 250
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
amount_per_transfer_from_this = 50
/obj/item/reagent_containers/hypospray/medipen/magillitis
name = "experimental autoinjector"
desc = "A custom-frame needle injector with a small single-use reservoir, containing an experimental serum. Unlike the more common medipen frame, it cannot pierce through protective armor or hardsuits, nor can the chemical inside be extracted."
icon_state = "gorillapen"
item_state = "gorillapen"
volume = 5
ignore_flags = 0
reagent_flags = NONE
list_reagents = list(/datum/reagent/magillitis = 5)
#define HYPO_SPRAY 0
#define HYPO_INJECT 1
@@ -218,6 +218,31 @@
volume = 50
list_reagents = list(/datum/reagent/medicine/stimulants = 50)
/obj/item/reagent_containers/syringe/contraband
name = "unlabeled syringe"
desc = "A syringe containing some sort of unknown chemical cocktail."
/obj/item/reagent_containers/syringe/contraband/space_drugs
list_reagents = list(/datum/reagent/drug/space_drugs = 15)
/obj/item/reagent_containers/syringe/contraband/krokodil
list_reagents = list(/datum/reagent/drug/krokodil = 15)
/obj/item/reagent_containers/syringe/contraband/crank
list_reagents = list(/datum/reagent/drug/crank = 15)
/obj/item/reagent_containers/syringe/contraband/methamphetamine
list_reagents = list(/datum/reagent/drug/methamphetamine = 15)
/obj/item/reagent_containers/syringe/contraband/bath_salts
list_reagents = list(/datum/reagent/drug/bath_salts = 15)
/obj/item/reagent_containers/syringe/contraband/fentanyl
list_reagents = list(/datum/reagent/toxin/fentanyl = 15)
/obj/item/reagent_containers/syringe/contraband/morphine
list_reagents = list(/datum/reagent/medicine/morphine = 15)
/obj/item/reagent_containers/syringe/calomel
name = "syringe (calomel)"
desc = "Contains calomel."
@@ -233,6 +233,14 @@
anchored = TRUE
reagent_id = /datum/reagent/consumable/cooking_oil
/obj/structure/reagent_dispensers/servingdish
name = "serving dish"
desc = "A dish full of food slop for your bowl."
icon = 'icons/obj/kitchen.dmi'
icon_state = "serving"
anchored = TRUE
reagent_id = /datum/reagent/consumable/nutraslop
////////
//Kegs//
////////
@@ -30,13 +30,21 @@
category = list("initial","Dinnerware")
/datum/design/tray
name = "Tray"
id = "tray"
name = "Serving Tray"
id = "servingtray"
build_type = AUTOLATHE
materials = list(/datum/material/iron = 3000)
build_path = /obj/item/storage/bag/tray
category = list("initial","Dinnerware")
/datum/design/tray
name = "Cafeteria Tray"
id = "foodtray"
build_type = AUTOLATHE
materials = list(/datum/material/iron = 3000)
build_path = /obj/item/storage/bag/tray/cafeteria
category = list("initial","Dinnerware")
/datum/design/bowl
name = "Bowl"
id = "bowl"
@@ -573,6 +573,10 @@
if(batteries.len)
var/obj/item/stock_parts/cell/ToCharge = pick(batteries)
ToCharge.charge += min(ToCharge.maxcharge - ToCharge.charge, ToCharge.maxcharge/10) //10% of the cell, or to maximum.
ToCharge.update_appearance() //make sure the cell gets their appearance updated.
var/atom/l = ToCharge.loc
if(isgun(l)) //updates the gun appearance as well if the cell is inside one.
l.update_appearance()
to_chat(owner, "<span class='notice'>[linked_extract] discharges some energy into a device you have.</span>")
return ..()
+2 -2
View File
@@ -63,10 +63,10 @@
name = "DNA Sampler"
desc = "Can be used to take chemical and genetic samples of pretty much anything."
icon = 'icons/obj/syringe.dmi'
item_state = "hypo"
item_state = "sampler"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
icon_state = "hypo"
icon_state = "sampler"
item_flags = NOBLUDGEON
var/list/animals = list()
var/list/plants = list()
+1 -1
View File
@@ -137,7 +137,7 @@ Notes:
/atom/movable/MouseEntered(location, control, params)
. = ..()
if(tooltips)
if(!QDELETED(src))
if(!QDELETED(src) && usr.client.prefs.enable_tips)
var/list/tooltip_data = get_tooltip_data()
if(length(tooltip_data))
var/examine_data = tooltip_data.Join("<br />")
+33 -1
View File
@@ -32,47 +32,79 @@
/// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)`
#define TEST_FOCUS(test_path) ##test_path { focus = TRUE; }
/// Constants indicating unit test completion status
#define UNIT_TEST_PASSED 0
#define UNIT_TEST_FAILED 1
#define UNIT_TEST_SKIPPED 2
#define TEST_DEFAULT 1
#define TEST_DEL_WORLD INFINITY
/// A trait source when adding traits through unit tests
#define TRAIT_SOURCE_UNIT_TESTS "unit_tests"
#include "anchored_mobs.dm"
#include "bespoke_id.dm"
#include "binary_insert.dm"
// #include "bloody_footprints.dm"
// #include "breath.dm"
// #include "card_mismatch.dm"
#include "chain_pull_through_space.dm"
// #include "combat.dm"
#include "component_tests.dm"
// #include "connect_loc.dm"
// #include "confusion.dm"
// #include "crayons.dm"
// #include "create_and_destroy.dm"
// #include "designs.dm"
// #include "dynamic_ruleset_sanity.dm"
// #include "egg_glands.dm"
// #include "emoting.dm"
// #include "food_edibility_check.dm"
// #include "greyscale_config.dm"
// #include "heretic_knowledge.dm"
// #include "holidays.dm"
#include "initialize_sanity.dm"
// #include "hydroponics_harvest.dm"
// #include "keybinding_init.dm"
#include "machine_disassembly.dm"
#include "medical_wounds.dm"
#include "merge_type.dm"
// #include "metabolizing.dm"
// #include "ntnetwork_tests.dm"
// #include "outfit_sanity.dm"
// #include "pills.dm"
// #include "plantgrowth_tests.dm"
// #include "projectiles.dm"
// #include "rcd.dm"
#include "reagent_id_typos.dm"
// #include "reagent_mod_expose.dm"
// #include "reagent_mod_procs.dm"
#include "reagent_recipe_collisions.dm"
#include "resist.dm"
// #include "say.dm"
// #include "security_officer_distribution.dm"
// #include "serving_tray.dm"
// #include "siunit.dm"
#include "spawn_humans.dm"
#include "spawn_mobs.dm"
// #include "species_whitelists.dm"
// #include "stomach.dm"
// #include "strippable.dm"
#include "subsystem_init.dm"
#include "surgeries.dm"
#include "teleporters.dm"
#include "tgui_create_message.dm"
#include "timer_sanity.dm"
#include "unit_test.dm"
// #include "wizard.dm"
/// CIT TESTS
#include "character_saving.dm"
#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
#include "find_reference_sanity.dm"
#endif
#undef TEST_ASSERT
#undef TEST_ASSERT_EQUAL
#undef TEST_ASSERT_NOTEQUAL
@@ -1,6 +1,6 @@
/datum/unit_test/chain_pull_through_space
var/turf/open/space/space_tile
var/turf/claimed_tile
var/claimed_tile
var/mob/living/carbon/human/alice
var/mob/living/carbon/human/bob
var/mob/living/carbon/human/charlie
@@ -9,25 +9,25 @@
..()
// Create a space tile that goes to another z-level
claimed_tile = run_loc_bottom_left
claimed_tile = run_loc_floor_bottom_left.type
space_tile = new(locate(run_loc_bottom_left.x, run_loc_bottom_left.y, run_loc_bottom_left.z))
space_tile = new(locate(run_loc_floor_bottom_left.x, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
space_tile.destination_x = 100
space_tile.destination_y = 100
space_tile.destination_z = 5
// Create our list of humans, all adjacent to one another
alice = new(locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z))
alice = new(locate(run_loc_floor_bottom_left.x + 2, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
alice.name = "Alice"
bob = new(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z))
bob = new(locate(run_loc_floor_bottom_left.x + 3, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
bob.name = "Bob"
charlie = new(locate(run_loc_bottom_left.x + 4, run_loc_bottom_left.y, run_loc_bottom_left.z))
charlie = new(locate(run_loc_floor_bottom_left.x + 4, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
charlie.name = "Charlie"
/datum/unit_test/chain_pull_through_space/Destroy()
space_tile.copyTurf(claimed_tile)
space_tile.ChangeTurf(claimed_tile)
qdel(alice)
qdel(bob)
qdel(charlie)
@@ -40,14 +40,14 @@
bob.start_pulling(charlie)
// Walk normally to the left, make sure we're still a chain
alice.Move(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z))
if (bob.x != run_loc_bottom_left.x + 2)
alice.Move(locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
if (bob.x != run_loc_floor_bottom_left.x + 2)
return Fail("During normal move, Bob was not at the correct x ([bob.x])")
if (charlie.x != run_loc_bottom_left.x + 3)
if (charlie.x != run_loc_floor_bottom_left.x + 3)
return Fail("During normal move, Charlie was not at the correct x ([charlie.x])")
// We're going through the space turf now that should teleport us
alice.Move(run_loc_bottom_left)
alice.Move(run_loc_floor_bottom_left)
if (alice.z != space_tile.destination_z)
return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])")
@@ -0,0 +1,111 @@
///Used to test the completeness of the reference finder proc.
/datum/unit_test/find_reference_sanity
/atom/movable/ref_holder
var/atom/movable/ref_test/test
var/list/test_list = list()
var/list/test_assoc_list = list()
/atom/movable/ref_holder/Destroy()
test = null
test_list.Cut()
test_assoc_list.Cut()
return ..()
/atom/movable/ref_test
var/atom/movable/ref_test/self_ref
/atom/movable/ref_test/Destroy(force)
self_ref = null
return ..()
/datum/unit_test/find_reference_sanity/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Sanity check
victim.DoSearchVar(testbed, "Sanity Check", search_time = 1) //We increment search time to get around an optimization
TEST_ASSERT(!victim.found_refs.len, "The ref-tracking tool found a ref where none existed")
SSgarbage.should_save_refs = FALSE
/datum/unit_test/find_reference_baseline/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Set up for the first round of tests
testbed.test = victim
testbed.test_list += victim
testbed.test_assoc_list["baseline"] = victim
victim.DoSearchVar(testbed, "First Run", search_time = 2)
TEST_ASSERT(victim.found_refs["test"], "The ref-tracking tool failed to find a regular value")
TEST_ASSERT(victim.found_refs[testbed.test_list], "The ref-tracking tool failed to find a list entry")
TEST_ASSERT(victim.found_refs[testbed.test_assoc_list], "The ref-tracking tool failed to find an assoc list value")
SSgarbage.should_save_refs = FALSE
/datum/unit_test/find_reference_exotic/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Second round, bit harder this time
testbed.overlays += victim
testbed.vis_contents += victim
testbed.test_assoc_list[victim] = TRUE
victim.DoSearchVar(testbed, "Second Run", search_time = 3)
//This is another sanity check
TEST_ASSERT(!victim.found_refs[testbed.overlays], "The ref-tracking tool found an overlays entry? That shouldn't be possible")
TEST_ASSERT(victim.found_refs[testbed.vis_contents], "The ref-tracking tool failed to find a vis_contents entry")
TEST_ASSERT(victim.found_refs[testbed.test_assoc_list], "The ref-tracking tool failed to find an assoc list key")
SSgarbage.should_save_refs = FALSE
/datum/unit_test/find_reference_esoteric/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Let's get a bit esoteric
victim.self_ref = victim
var/list/to_find = list(victim)
testbed.test_list += list(to_find)
var/list/to_find_assoc = list(victim)
testbed.test_assoc_list["Nesting"] = to_find_assoc
victim.DoSearchVar(victim, "Third Run Self", search_time = 4)
victim.DoSearchVar(testbed, "Third Run Testbed", search_time = 4)
TEST_ASSERT(victim.found_refs["self_ref"], "The ref-tracking tool failed to find a self reference")
TEST_ASSERT(victim.found_refs[to_find], "The ref-tracking tool failed to find a nested list entry")
TEST_ASSERT(victim.found_refs[to_find_assoc], "The ref-tracking tool failed to find a nested assoc list entry")
SSgarbage.should_save_refs = FALSE
/datum/unit_test/find_reference_null_key_entry/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Calm before the storm
testbed.test_assoc_list = list(null = victim)
victim.DoSearchVar(testbed, "Fourth Run", search_time = 5)
TEST_ASSERT(testbed.test_assoc_list, "The ref-tracking tool failed to find a null key'd assoc list entry")
/datum/unit_test/find_reference_assoc_investigation/Run()
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
SSgarbage.should_save_refs = TRUE
//Let's do some more complex assoc list investigation
var/list/to_find_in_key = list(victim)
testbed.test_assoc_list[to_find_in_key] = list("memes")
var/list/to_find_null_assoc_nested = list(victim)
testbed.test_assoc_list[null] = to_find_null_assoc_nested
victim.DoSearchVar(testbed, "Fifth Run", search_time = 6)
TEST_ASSERT(victim.found_refs[to_find_in_key], "The ref-tracking tool failed to find a nested assoc list key")
TEST_ASSERT(victim.found_refs[to_find_null_assoc_nested], "The ref-tracking tool failed to find a null key'd nested assoc list entry")
SSgarbage.should_save_refs = FALSE
@@ -1,11 +0,0 @@
/datum/unit_test/initialize_sanity/Run()
if(length(SSatoms.BadInitializeCalls))
Fail("Bad Initialize() calls detected. Please read logs.")
var/list/init_failures_to_text = list(
"[BAD_INIT_QDEL_BEFORE]" = "Qdeleted Before Initialized",
"[BAD_INIT_DIDNT_INIT]" = "Did Not Initialize",
"[BAD_INIT_SLEPT]" = "Initialize() Slept",
"[BAD_INIT_NO_HINT]" = "No Initialize() Hint Returned",
)
for(var/failure in SSatoms.BadInitializeCalls)
log_world("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets?
-23
View File
@@ -1,23 +0,0 @@
/// Test to verify message mods are parsed correctly
/datum/unit_test/get_message_mods
var/mob/host_mob
/datum/unit_test/get_message_mods/Run()
host_mob = allocate(/mob/living/carbon/human)
test("Hello", "Hello", list())
test(";HELP", "HELP", list(MODE_HEADSET = TRUE))
test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE))
test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY))
test("...What", "...What", list())
/datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods)
var/list/mods = list()
TEST_ASSERT_EQUAL(host_mob.get_message_mods(message, mods), expected_message, "Chopped message was not what we expected. Message: [message]")
for (var/mod_key in mods)
TEST_ASSERT_EQUAL(mods[mod_key], expected_mods[mod_key], "The value for [mod_key] was not what we expected. Message: [message]")
expected_mods -= mod_key
if (expected_mods.len)
Fail("Some message mods were expected, but were not returned by get_message_mods: [json_encode(expected_mods)]. Message: [message]")
+1 -1
View File
@@ -1,5 +1,5 @@
/datum/unit_test/spawn_humans/Run()
var/locs = block(run_loc_bottom_left, run_loc_top_right)
var/locs = block(run_loc_floor_bottom_left, run_loc_floor_top_right)
for(var/I in 1 to 5)
new /mob/living/carbon/human(pick(locs))
+8
View File
@@ -0,0 +1,8 @@
///Unit test that spawns all mobs that can be spawned by golden slimes
/datum/unit_test/spawn_mobs
/datum/unit_test/spawn_mobs/Run()
for(var/_animal in typesof(/mob/living/simple_animal))
var/mob/living/simple_animal/animal = _animal
if (initial(animal.gold_core_spawnable) == HOSTILE_SPAWN || initial(animal.gold_core_spawnable) == FRIENDLY_SPAWN)
allocate(_animal)
+2 -2
View File
@@ -1,8 +1,8 @@
/datum/unit_test/auto_teleporter_linking/Run()
// Put down the teleporter machinery
var/obj/machinery/teleport/hub/hub = allocate(/obj/machinery/teleport/hub)
var/obj/machinery/computer/teleporter/computer = allocate(/obj/machinery/computer/teleporter, locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z))
var/obj/machinery/teleport/station/station = allocate(/obj/machinery/teleport/station, locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z))
var/obj/machinery/computer/teleporter/computer = allocate(/obj/machinery/computer/teleporter, locate(run_loc_floor_bottom_left.x + 2, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
var/obj/machinery/teleport/station/station = allocate(/obj/machinery/teleport/station, locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
TEST_ASSERT_EQUAL(hub.power_station, station, "Hub didn't link to the station")
TEST_ASSERT_EQUAL(station.teleporter_console, computer, "Station didn't link to the teleporter console")
@@ -0,0 +1,28 @@
/// Test that `TGUI_CREATE_MESSAGE` is correctly implemented
/datum/unit_test/tgui_create_message
/datum/unit_test/tgui_create_message/Run()
var/type = "something/here"
var/list/payload = list(
"name" = "Terry McTider",
"heads_caved" = 100,
"accomplishments" = list(
"nothing",
"literally nothing",
list(
"something" = "just kidding",
),
),
)
var/message = TGUI_CREATE_MESSAGE(type, payload)
// Ensure consistent output to compare by performing a round-trip.
var/output = json_encode(json_decode(url_decode(message)))
var/expected = json_encode(list(
"type" = type,
"payload" = payload,
))
TEST_ASSERT_EQUAL(expected, output, "TGUI_CREATE_MESSAGE didn't round trip properly")
+68 -41
View File
@@ -7,7 +7,7 @@ Call Fail() to fail the test (You should specify a reason)
You may use /New() and /Destroy() for setup/teardown respectively
You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing
You can use the run_loc_floor_bottom_left and run_loc_floor_top_right to get turfs for testing
*/
@@ -19,39 +19,46 @@ GLOBAL_VAR(test_log)
//Bit of metadata for the future maybe
var/list/procs_tested
/// The bottom left turf of the testing zone
var/turf/run_loc_bottom_left
/// The top right turf of the testing zone
var/turf/run_loc_top_right
/// The type of turf to allocate for the testing zone
var/test_turf_type = /turf/open/floor/plasteel
/// The bottom left floor turf of the testing zone
var/turf/run_loc_floor_bottom_left
/// The top right floor turf of the testing zone
var/turf/run_loc_floor_top_right
///The priority of the test, the larger it is the later it fires
var/priority = TEST_DEFAULT
//internal shit
var/focus = FALSE
var/succeeded = TRUE
var/list/allocated
var/list/fail_reasons
var/static/datum/turf_reservation/turf_reservation
var/static/datum/turf_reservation/reservation
/proc/cmp_unit_test_priority(datum/unit_test/a, datum/unit_test/b)
return initial(a.priority) - initial(b.priority)
/datum/unit_test/New()
if (isnull(turf_reservation))
turf_reservation = SSmapping.RequestBlockReservation(5, 5)
if (isnull(reservation))
reservation = SSmapping.RequestBlockReservation(5, 5)
for (var/turf/reserved_turf in turf_reservation.reserved_turfs)
reserved_turf.ChangeTurf(test_turf_type)
for (var/turf/reserved_turf in reservation.reserved_turfs)
reserved_turf.ChangeTurf(/turf/open/floor/plasteel)
allocated = new
run_loc_bottom_left = locate(turf_reservation.bottom_left_coords[1], turf_reservation.bottom_left_coords[2], turf_reservation.bottom_left_coords[3])
run_loc_top_right = locate(turf_reservation.top_right_coords[1], turf_reservation.top_right_coords[2], turf_reservation.top_right_coords[3])
run_loc_floor_bottom_left = locate(reservation.bottom_left_coords[1], reservation.bottom_left_coords[2], reservation.bottom_left_coords[3])
run_loc_floor_top_right = locate(reservation.top_right_coords[1], reservation.top_right_coords[2], reservation.top_right_coords[3])
TEST_ASSERT(isfloorturf(run_loc_floor_bottom_left), "run_loc_floor_bottom_left was not a floor ([run_loc_floor_bottom_left])")
TEST_ASSERT(isfloorturf(run_loc_floor_top_right), "run_loc_floor_top_right was not a floor ([run_loc_floor_top_right])")
/datum/unit_test/Destroy()
//clear the test area
for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right))
qdel(AM)
QDEL_LIST(allocated)
// clear the test area
for (var/turf/turf in block(locate(1, 1, run_loc_floor_bottom_left.z), locate(world.maxx, world.maxy, run_loc_floor_bottom_left.z)))
for (var/content in turf.contents)
if (istype(content, /obj/effect/landmark))
continue
qdel(content)
return ..()
/datum/unit_test/proc/Run()
@@ -70,44 +77,64 @@ GLOBAL_VAR(test_log)
/datum/unit_test/proc/allocate(type, ...)
var/list/arguments = args.Copy(2)
if (!arguments.len)
arguments = list(run_loc_bottom_left)
arguments = list(run_loc_floor_bottom_left)
else if (arguments[1] == null)
arguments[1] = run_loc_bottom_left
arguments[1] = run_loc_floor_bottom_left
var/instance = new type(arglist(arguments))
allocated += instance
return instance
/proc/RunUnitTest(test_path, list/test_results)
var/datum/unit_test/test = new test_path
GLOB.current_test = test
var/duration = REALTIMEOFDAY
test.Run()
duration = REALTIMEOFDAY - duration
GLOB.current_test = null
GLOB.failed_any_test |= !test.succeeded
var/list/log_entry = list("[test.succeeded ? "PASS" : "FAIL"]: [test_path] [duration / 10]s")
var/list/fail_reasons = test.fail_reasons
for(var/J in 1 to LAZYLEN(fail_reasons))
log_entry += "\tREASON #[J]: [fail_reasons[J]]"
var/message = log_entry.Join("\n")
log_test(message)
test_results[test_path] = list("status" = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED, "message" = message, "name" = test_path)
qdel(test)
/proc/RunUnitTests()
CHECK_TICK
var/tests_to_run = subtypesof(/datum/unit_test)
var/list/tests_to_run = subtypesof(/datum/unit_test)
for (var/_test_to_run in tests_to_run)
var/datum/unit_test/test_to_run = _test_to_run
if (initial(test_to_run.focus))
tests_to_run = list(test_to_run)
break
for(var/I in tests_to_run)
var/datum/unit_test/test = new I
tests_to_run = sortTim(tests_to_run, /proc/cmp_unit_test_priority)
GLOB.current_test = test
var/duration = REALTIMEOFDAY
var/list/test_results = list()
test.Run()
for(var/unit_path in tests_to_run)
CHECK_TICK //We check tick first because the unit test we run last may be so expensive that checking tick will lock up this loop forever
RunUnitTest(unit_path, test_results)
duration = REALTIMEOFDAY - duration
GLOB.current_test = null
GLOB.failed_any_test |= !test.succeeded
var/list/log_entry = list("[test.succeeded ? "PASS" : "FAIL"]: [I] [duration / 10]s")
var/list/fail_reasons = test.fail_reasons
qdel(test)
for(var/J in 1 to LAZYLEN(fail_reasons))
log_entry += "\tREASON #[J]: [fail_reasons[J]]"
log_test(log_entry.Join("\n"))
CHECK_TICK
var/file_name = "data/unit_tests.json"
fdel(file_name)
file(file_name) << json_encode(test_results)
SSticker.force_ending = TRUE
//We have to call this manually because del_text can preceed us, and SSticker doesn't fire in the post game
SSticker.ready_for_reboot = TRUE
SSticker.standard_reboot()
// /datum/map_template/unit_tests
// name = "Unit Tests Zone"
// mappath = "_maps/templates/unit_tests.dmm"
@@ -213,7 +213,7 @@
name = "Stimpack"
desc = "Stimpacks, the tool of many great heroes. Makes you nearly immune to non-lethal weaponry for about \
5 minutes after injection."
item = /obj/item/reagent_containers/syringe/stimulants
item = /obj/item/reagent_containers/hypospray/medipen/stimulants
cost = 5
surplus = 90
@@ -195,7 +195,7 @@
name = "Magillitis Serum Autoinjector"
desc = "A single-use autoinjector which contains an experimental serum that causes rapid muscular growth in Hominidae. \
Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
item = /obj/item/reagent_containers/hypospray/magillitis
item = /obj/item/reagent_containers/hypospray/medipen/magillitis
cost = 8
restricted_roles = list("Geneticist", "Chief Medical Officer")
+1
View File
@@ -9,6 +9,7 @@
/obj/item/toy/cards/deck/cas/black = 3,
/obj/item/toy/cards/deck/unum = 3,
/obj/item/cardpack/series_one = 10,
/obj/item/dyespray=3,
/obj/item/tcgcard_binder = 5)
contraband = list(/obj/item/dice/fudge = 9)
premium = list(/obj/item/melee/skateboard/pro = 3,