Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into prisoners
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -129,13 +129,12 @@
|
||||
/mob/living/simple_animal/slaughter/proc/release_victims()
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
T = find_safe_turf()
|
||||
for(var/mob/living/M in consumed_mobs)
|
||||
if(!M)
|
||||
continue
|
||||
var/turf/T = find_safe_turf()
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
M.forceMove(T)
|
||||
|
||||
/mob/living/simple_animal/slaughter/proc/refresh_consumed_buff()
|
||||
@@ -263,12 +262,12 @@
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
T = find_safe_turf()
|
||||
for(var/mob/living/M in consumed_mobs)
|
||||
if(!M)
|
||||
continue
|
||||
var/turf/T = find_safe_turf()
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
continue
|
||||
M.forceMove(T)
|
||||
if(M.revive(full_heal = TRUE, admin_revive = TRUE))
|
||||
M.grab_ghost(force = TRUE)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -157,6 +157,8 @@
|
||||
id = GAS_METHANE
|
||||
specific_heat = 30
|
||||
name = "Methane"
|
||||
powerloss_inhibition = 1
|
||||
heat_resistance = 3
|
||||
breath_results = GAS_METHYL_BROMIDE
|
||||
fire_products = list(GAS_CO2 = 1, GAS_H2O = 2)
|
||||
fire_burn_rate = 0.5
|
||||
@@ -177,6 +179,8 @@
|
||||
id = GAS_METHYL_BROMIDE
|
||||
specific_heat = 42
|
||||
name = "Methyl Bromide"
|
||||
powermix = 1
|
||||
heat_penalty = -1
|
||||
flags = GAS_FLAG_DANGEROUS
|
||||
breath_alert_info = list(
|
||||
not_enough_alert = list(
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
if(!blocks_air)
|
||||
air = new(2500,src)
|
||||
air.copy_from_turf(src)
|
||||
if(planetary_atmos && !(initial_gas_mix in SSair.planetary))
|
||||
var/datum/gas_mixture/mix = new
|
||||
mix.parse_gas_string(initial_gas_mix)
|
||||
mix.mark_immutable()
|
||||
SSair.planetary[initial_gas_mix] = mix
|
||||
update_air_ref(planetary_atmos ? 1 : 2)
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -261,6 +261,8 @@ we use a hook instead
|
||||
return 1
|
||||
|
||||
/datum/gas_mixture/parse_gas_string(gas_string)
|
||||
gas_string = SSair.preprocess_gas_string(gas_string)
|
||||
|
||||
var/list/gas = params2list(gas_string)
|
||||
if(gas["TEMP"])
|
||||
var/temp = text2num(gas["TEMP"])
|
||||
|
||||
@@ -648,7 +648,7 @@
|
||||
//Replace miasma with oxygen
|
||||
var/cleaned_air = min(air.get_moles(GAS_MIASMA), 20 + (air.return_temperature() - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20)
|
||||
air.adjust_moles(GAS_MIASMA, -cleaned_air)
|
||||
air.adjust_moles(GAS_O2, cleaned_air)
|
||||
air.adjust_moles(GAS_METHANE, cleaned_air)
|
||||
|
||||
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
|
||||
air.set_temperature(air.return_temperature() + cleaned_air * 0.002)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -60,6 +60,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
//autocorrected this round, not that you'd need to check that.
|
||||
|
||||
var/UI_style = null
|
||||
var/outline_enabled = TRUE
|
||||
var/outline_color = COLOR_BLUE_GRAY
|
||||
var/buttons_locked = FALSE
|
||||
var/hotkeys = FALSE
|
||||
|
||||
@@ -117,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
|
||||
@@ -503,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<a href='?_src_=prefs;preference=previous_facehair_style;task=input'><</a> <a href='?_src_=prefs;preference=next_facehair_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[facial_hair_color];'> </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'><</a> <a href='?_src_=prefs;preference=next_grad_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[grad_color];'> </span> <a href='?_src_=prefs;preference=grad_color;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "</td>"
|
||||
//Mutant stuff
|
||||
var/mutant_category = 0
|
||||
@@ -774,6 +784,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
|
||||
dat += "<h2>General Settings</h2>"
|
||||
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
|
||||
dat += "<b>Outline:</b> <a href='?_src_=prefs;preference=outline_enabled'>[outline_enabled ? "Enabled" : "Disabled"]</a><br>"
|
||||
dat += "<b>Outline Color:</b> <span style='border:1px solid #161616; background-color: [outline_color];'> </span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
|
||||
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
|
||||
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
|
||||
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
|
||||
@@ -1707,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)
|
||||
|
||||
@@ -2706,6 +2735,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
buttons_locked = !buttons_locked
|
||||
if("tgui_fancy")
|
||||
tgui_fancy = !tgui_fancy
|
||||
if("outline_enabled")
|
||||
outline_enabled = !outline_enabled
|
||||
if("outline_color")
|
||||
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
|
||||
if(pickedOutlineColor)
|
||||
outline_color = pickedOutlineColor
|
||||
if("tgui_lock")
|
||||
tgui_lock = !tgui_lock
|
||||
if("winflash")
|
||||
@@ -3006,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
|
||||
@@ -3070,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
|
||||
|
||||
@@ -45,6 +45,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
|
||||
force_reset_keybindings_direct(TRUE)
|
||||
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
|
||||
if(current_version < 30)
|
||||
outline_enabled = TRUE
|
||||
outline_color = COLOR_BLUE_GRAY
|
||||
|
||||
/datum/preferences/proc/update_character(current_version, savefile/S)
|
||||
if(current_version < 19)
|
||||
@@ -377,6 +380,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["ooccolor"] >> ooccolor
|
||||
S["lastchangelog"] >> lastchangelog
|
||||
S["UI_style"] >> UI_style
|
||||
S["outline_color"] >> outline_color
|
||||
S["outline_enabled"] >> outline_enabled
|
||||
S["hotkeys"] >> hotkeys
|
||||
S["chat_on_map"] >> chat_on_map
|
||||
S["max_chat_length"] >> max_chat_length
|
||||
@@ -555,6 +560,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["ooccolor"], ooccolor)
|
||||
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
||||
WRITE_FILE(S["UI_style"], UI_style)
|
||||
WRITE_FILE(S["outline_enabled"], outline_enabled)
|
||||
WRITE_FILE(S["outline_color"], outline_color)
|
||||
WRITE_FILE(S["hotkeys"], hotkeys)
|
||||
WRITE_FILE(S["chat_on_map"], chat_on_map)
|
||||
WRITE_FILE(S["max_chat_length"], max_chat_length)
|
||||
@@ -676,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
|
||||
@@ -868,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)
|
||||
@@ -1037,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,13 +27,27 @@
|
||||
if(prob(low_threat_perc))
|
||||
severity = "low; the supermatter should return to normal operation shortly."
|
||||
else
|
||||
severity = "medium; the supermatter should return to normal operation, but check NT CIMS to ensure this."
|
||||
severity = "medium; the supermatter should return to normal operation, but regardless, check if the emitters may need to be turned off temporarily."
|
||||
else
|
||||
severity = "high; if the supermatter's cooling is not fortified, coolant may need to be added."
|
||||
severity = "high; the emitters likely need to be turned off, and if the supermatter's cooling loop is not fortified, pre-cooled gas may need to be added."
|
||||
if(100000 to INFINITY)
|
||||
severity = "extreme; emergency action is likely to be required even if coolant loop is fine."
|
||||
severity = "extreme; emergency action is likely to be required even if coolant loop is fine. Turn off the emitters and make sure the loop is properly cooling gases."
|
||||
if(power > 20000 || prob(round(power/200)))
|
||||
priority_announce("Supermatter surge detected. Estimated severity is [severity]", "Anomaly Alert")
|
||||
|
||||
/datum/round_event/supermatter_surge/start()
|
||||
GLOB.main_supermatter_engine.matter_power += power
|
||||
var/obj/machinery/power/supermatter_crystal/supermatter = GLOB.main_supermatter_engine
|
||||
var/power_proportion = supermatter.powerloss_inhibitor/2 // what % of the power goes into matter power, at most 50%
|
||||
// we reduce the proportion that goes into actual matter power based on powerloss inhibitor
|
||||
// primarily so the supermatter doesn't tesla the instant these happen
|
||||
supermatter.matter_power += power * power_proportion
|
||||
var/datum/gas_mixture/methane_puff = new
|
||||
var/selected_gas = pick(4;GAS_CO2, 10;GAS_METHANE, 4;GAS_H2O, 1;GAS_BZ, 1;GAS_METHYL_BROMIDE)
|
||||
methane_puff.set_moles(selected_gas, 500)
|
||||
methane_puff.set_temperature(500)
|
||||
var/energy_ratio = (power * 500 * (1-power_proportion)) / methane_puff.thermal_energy()
|
||||
if(energy_ratio < 1) // energy output we want is lower than current energy, reduce the amount of gas we puff out
|
||||
methane_puff.set_moles(GAS_METHANE, energy_ratio * 500)
|
||||
else // energy output we want is higher than current energy, increase its actual heat
|
||||
methane_puff.set_temperature(energy_ratio * 500)
|
||||
supermatter.assume_air(methane_puff)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -542,6 +542,20 @@
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails/human/takahiro_kitsune
|
||||
name = "Takahiro Kitsune Tails" //takahiro had five tails i just wanted to follow the 'T' naming convention vs. tamamo and triple
|
||||
icon_state = "7sune"
|
||||
color_src = MATRIXED
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails_animated/human/takahiro_kitsune
|
||||
name = "Takahiro Kitsune Tails"
|
||||
icon_state = "7sune"
|
||||
color_src = MATRIXED
|
||||
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
|
||||
matrixed_sections = MATRIX_RED_GREEN
|
||||
|
||||
/datum/sprite_accessory/tails/human/tentacle
|
||||
name = "Tentacle"
|
||||
icon_state = "tentacle"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -98,6 +98,16 @@
|
||||
qdel(bigcheese)
|
||||
evolve()
|
||||
return
|
||||
for(var/obj/item/trash/garbage in range(1, src))
|
||||
if(prob(2))
|
||||
qdel(garbage)
|
||||
evolve_plague()
|
||||
return
|
||||
for(var/obj/effect/decal/cleanable/blood/gibs/leftovers in range(1, src))
|
||||
if(prob(2))
|
||||
qdel(leftovers)
|
||||
evolve_plague()
|
||||
return
|
||||
|
||||
/**
|
||||
*Checks the mouse cap, if it's above the cap, doesn't spawn a mouse. If below, spawns a mouse and adds it to cheeserats.
|
||||
@@ -123,6 +133,13 @@
|
||||
mind.transfer_to(regalrat)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/mouse/proc/evolve_plague()
|
||||
var/mob/living/simple_animal/hostile/plaguerat = new /mob/living/simple_animal/hostile/plaguerat(loc)
|
||||
visible_message("<span class='warning'>[src] devours the food! He rots into something worse!</span>")
|
||||
if(mind)
|
||||
mind.transfer_to(plaguerat)
|
||||
qdel(src)
|
||||
|
||||
/*
|
||||
* Mouse types
|
||||
*/
|
||||
@@ -169,3 +186,16 @@ GLOBAL_VAR(tom_existed)
|
||||
/obj/item/reagent_containers/food/snacks/deadmouse/on_grind()
|
||||
reagents.clear_reagents()
|
||||
|
||||
/mob/living/simple_animal/mouse/proc/miasma(datum/gas_mixture/environment, check_temp = FALSE)
|
||||
if(isturf(src.loc) && isopenturf(src.loc))
|
||||
var/turf/open/ST = src.loc
|
||||
var/miasma_moles = ST.air.get_moles(GAS_MIASMA)
|
||||
if(prob(5) && miasma_moles >= 5)
|
||||
evolve_plague()
|
||||
else if(miasma_moles >= 20)
|
||||
evolve_plague()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/mouse/handle_environment(datum/gas_mixture/environment)
|
||||
. = ..()
|
||||
miasma()
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
#define GREMLIN_VENT_CHANCE 1.75
|
||||
|
||||
//Gremlins
|
||||
//Small monsters that don't attack humans or other animals. Instead they mess with electronics, computers and machinery
|
||||
|
||||
//List of objects that gremlins can't tamper with (because nobody coded an interaction for it)
|
||||
//List starts out empty. Whenever a gremlin finds a machine that it couldn't tamper with, the machine's type is added here, and all machines of such type are ignored from then on (NOT SUBTYPES)
|
||||
GLOBAL_LIST(bad_gremlin_items)
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin
|
||||
name = "gremlin"
|
||||
desc = "This tiny creature finds great joy in discovering and using technology. Nothing excites it more than pushing random buttons on a computer to see what it might do."
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "gremlin"
|
||||
icon_living = "gremlin"
|
||||
icon_dead = "gremlin_dead"
|
||||
|
||||
var/in_vent = FALSE
|
||||
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
search_objects = 3 //Completely ignore mobs
|
||||
|
||||
//Tampering is handled by the 'npc_tamper()' obj proc
|
||||
wanted_objects = list(
|
||||
/obj/machinery,
|
||||
/obj/item/reagent_containers/food,
|
||||
/obj/structure/sink
|
||||
)
|
||||
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent
|
||||
|
||||
dextrous = TRUE
|
||||
possible_a_intents = list(INTENT_HELP, INTENT_GRAB, INTENT_DISARM, INTENT_HARM)
|
||||
faction = list("meme", "gremlin")
|
||||
speed = 0.5
|
||||
gold_core_spawnable = 2
|
||||
unique_name = TRUE
|
||||
|
||||
//Ensure gremlins don't attack other mobs
|
||||
melee_damage_upper = 0
|
||||
melee_damage_lower = 0
|
||||
attack_sound = null
|
||||
obj_damage = 0
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
|
||||
//List of objects that we don't even want to try to tamper with
|
||||
//Subtypes of these are calculated too
|
||||
var/list/unwanted_objects = list(/obj/machinery/atmospherics/pipe, /turf, /obj/structure) //ensure gremlins dont try to fuck with walls / normal pipes / glass / etc
|
||||
|
||||
var/min_next_vent = 0
|
||||
|
||||
//Amount of ticks spent pathing to the target. If it gets above a certain amount, assume that the target is unreachable and stop
|
||||
var/time_chasing_target = 0
|
||||
|
||||
//If you're going to make gremlins slower, increase this value - otherwise gremlins will abandon their targets too early
|
||||
var/max_time_chasing_target = 2
|
||||
|
||||
var/next_eat = 0
|
||||
|
||||
//Last 20 heard messages are remembered by gremlins, and will be used to generate messages for comms console tampering, etc...
|
||||
var/list/hear_memory = list()
|
||||
var/const/max_hear_memory = 20
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
access_card = new /obj/item/card/id(src)
|
||||
var/datum/job/captain/C = new /datum/job/captain
|
||||
access_card.access = C.get_access()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/AttackingTarget()
|
||||
var/is_hungry = world.time >= next_eat || prob(25)
|
||||
if(istype(target, /obj/item/reagent_containers/food) && is_hungry) //eat food if we're hungry or bored
|
||||
visible_message("<span class='danger'>[src] hungrily devours [target]!</span>")
|
||||
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
||||
qdel(target)
|
||||
LoseTarget()
|
||||
next_eat = world.time + rand(700, 3000) //anywhere from 70 seconds to 5 minutes until the gremlin is hungry again
|
||||
return
|
||||
if(istype(target, /obj))
|
||||
var/obj/M = target
|
||||
tamper(M)
|
||||
if(prob(50)) //50% chance to move to the next machine
|
||||
LoseTarget()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode)
|
||||
. = ..()
|
||||
if(message)
|
||||
hear_memory.Insert(1, raw_message)
|
||||
if(hear_memory.len > max_hear_memory)
|
||||
hear_memory.Cut(hear_memory.len)
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/generate_markov_input()
|
||||
var/result = ""
|
||||
|
||||
for(var/memory in hear_memory)
|
||||
result += memory + " "
|
||||
|
||||
return result
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/generate_markov_chain()
|
||||
return markov_chain(generate_markov_input(), rand(2,5), rand(100,700)) //The numbers are chosen arbitarily
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/tamper(obj/M)
|
||||
switch(M.npc_tamper_act(src))
|
||||
if(NPC_TAMPER_ACT_FORGET)
|
||||
visible_message(pick(
|
||||
"<span class='notice'>\The [src] plays around with \the [M], but finds it rather boring.</span>",
|
||||
"<span class='notice'>\The [src] tries to think of some more ways to screw \the [M] up, but fails miserably.</span>",
|
||||
"<span class='notice'>\The [src] decides to ignore \the [M], and starts looking for something more fun.</span>"))
|
||||
|
||||
LAZYADD(GLOB.bad_gremlin_items,M.type)
|
||||
return FALSE
|
||||
if(NPC_TAMPER_ACT_NOMSG)
|
||||
//Don't create a visible message
|
||||
return TRUE
|
||||
|
||||
else
|
||||
visible_message(pick(
|
||||
"<span class='danger'>\The [src]'s eyes light up as \he tampers with \the [M].</span>",
|
||||
"<span class='danger'>\The [src] twists some knobs around on \the [M] and bursts into laughter!</span>",
|
||||
"<span class='danger'>\The [src] presses a few buttons on \the [M] and giggles mischievously.</span>",
|
||||
"<span class='danger'>\The [src] rubs its hands devilishly and starts messing with \the [M].</span>",
|
||||
"<span class='danger'>\The [src] turns a small valve on \the [M].</span>"))
|
||||
|
||||
//Add a clue for detectives to find. The clue is only added if no such clue already existed on that machine
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/CanAttack(atom/new_target)
|
||||
if(LAZYFIND(GLOB.bad_gremlin_items,new_target.type))
|
||||
return FALSE
|
||||
if(is_type_in_list(new_target, unwanted_objects))
|
||||
return FALSE
|
||||
if(istype(new_target, /obj/machinery))
|
||||
var/obj/machinery/M = new_target
|
||||
if(M.stat) //Unpowered or broken
|
||||
return FALSE
|
||||
else if(istype(new_target, /obj/machinery/door/firedoor))
|
||||
var/obj/machinery/door/firedoor/F = new_target
|
||||
//Only tamper with firelocks that are closed, opening them!
|
||||
if(!F.density)
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/death(gibbed)
|
||||
walk(src,0)
|
||||
QDEL_NULL(access_card)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Life()
|
||||
. = ..()
|
||||
if(!health || stat == DEAD)
|
||||
return
|
||||
//Don't try to path to one target for too long. If it takes longer than a certain amount of time, assume it can't be reached and find a new one
|
||||
if(!client) //don't do this shit if there's a client, they're capable of ventcrawling manually
|
||||
if(in_vent)
|
||||
target = null
|
||||
if(entry_vent && get_dist(src, entry_vent) <= 1)
|
||||
var/list/vents = list()
|
||||
var/datum/pipeline/entry_vent_parent = entry_vent.parents[1]
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in entry_vent_parent.other_atmosmch)
|
||||
vents += temp_vent
|
||||
if(!vents.len)
|
||||
entry_vent = null
|
||||
in_vent = FALSE
|
||||
return
|
||||
exit_vent = pick(vents)
|
||||
visible_message("<span class='notice'>[src] crawls into the ventilation ducts!</span>")
|
||||
|
||||
loc = exit_vent
|
||||
var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
|
||||
addtimer(CALLBACK(src, .proc/exit_vents), travel_time) //come out at exit vent in 2 to 20 seconds
|
||||
|
||||
|
||||
if(world.time > min_next_vent && !entry_vent && !in_vent && prob(GREMLIN_VENT_CHANCE)) //small chance to go into a vent
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/v in view(7,src))
|
||||
if(!v.welded)
|
||||
entry_vent = v
|
||||
in_vent = TRUE
|
||||
walk_to(src, entry_vent)
|
||||
break
|
||||
if(!target)
|
||||
time_chasing_target = 0
|
||||
else
|
||||
if(++time_chasing_target > max_time_chasing_target)
|
||||
LoseTarget()
|
||||
time_chasing_target = 0
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/EscapeConfinement()
|
||||
if(istype(loc, /obj) && CanAttack(loc)) //If we're inside a machine, screw with it
|
||||
var/obj/M = loc
|
||||
tamper(M)
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/exit_vents()
|
||||
if(!exit_vent || exit_vent.welded)
|
||||
loc = entry_vent
|
||||
entry_vent = null
|
||||
return
|
||||
loc = exit_vent.loc
|
||||
entry_vent = null
|
||||
exit_vent = null
|
||||
in_vent = FALSE
|
||||
var/area/new_area = get_area(loc)
|
||||
message_admins("[src] came out at [new_area][ADMIN_JMP(loc)]!")
|
||||
if(new_area)
|
||||
new_area.Entered(src)
|
||||
visible_message("<span class='notice'>[src] climbs out of the ventilation ducts!</span>")
|
||||
min_next_vent = world.time + 900 //90 seconds between ventcrawls
|
||||
|
||||
//This allows player-controlled gremlins to tamper with machinery
|
||||
/mob/living/simple_animal/hostile/gremlin/UnarmedAttack(var/atom/A)
|
||||
if(istype(A, /obj/machinery) || istype(A, /obj/structure))
|
||||
tamper(A)
|
||||
if(istype(target, /obj/item/reagent_containers/food)) //eat food
|
||||
visible_message("<span class='danger'>[src] hungrily devours [target]!</span>", "<span class='danger'>You hungrily devour [target]!</span>")
|
||||
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
||||
qdel(target)
|
||||
LoseTarget()
|
||||
next_eat = world.time + rand(700, 3000) //anywhere from 70 seconds to 5 minutes until the gremlin is hungry again
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/IsAdvancedToolUser()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/proc/divide()
|
||||
//Health is halved and then reduced by 2. A new gremlin is spawned with the same health as the parent
|
||||
//Need to have at least 6 health for this, otherwise resulting health would be less than 1
|
||||
if(health < 7.5)
|
||||
return
|
||||
|
||||
visible_message("<span class='notice'>\The [src] splits into two!</span>")
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = new /mob/living/simple_animal/hostile/gremlin(get_turf(src))
|
||||
|
||||
if(mind)
|
||||
mind.transfer_to(G)
|
||||
|
||||
health = round(health * 0.5) - 2
|
||||
maxHealth = health
|
||||
resize *= 0.9
|
||||
|
||||
G.health = health
|
||||
G.maxHealth = maxHealth
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/traitor
|
||||
health = 85
|
||||
maxHealth = 85
|
||||
gold_core_spawnable = 0
|
||||
@@ -0,0 +1,214 @@
|
||||
/obj/proc/npc_tamper_act(mob/living/L)
|
||||
return NPC_TAMPER_ACT_FORGET
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/passive_gate/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
target_pressure = rand(0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/pump/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
target_pressure = rand(0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/volume_pump/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //Turn on/off
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
else //Change pressure
|
||||
transfer_rate = rand(0, MAX_TRANSFER_RATE)
|
||||
investigate_log("was set to [transfer_rate] L/s by [key_name(L)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/binary/valve/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/space_heater/npc_tamper_act(mob/living/L)
|
||||
var/list/choose_modes = list("standby", "heat", "cool")
|
||||
if(prob(50))
|
||||
choose_modes -= mode
|
||||
mode = pick(choose_modes)
|
||||
else
|
||||
on = !on
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/shield_gen/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/firealarm/npc_tamper_act(mob/living/L)
|
||||
alarm()
|
||||
|
||||
/obj/machinery/airalarm/npc_tamper_act(mob/living/L)
|
||||
if(panel_open)
|
||||
wires.npc_tamper(L)
|
||||
else
|
||||
panel_open = !panel_open
|
||||
|
||||
/obj/machinery/ignition_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/flasher_button/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/crema_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/camera/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/npc_tamper_act(mob/living/L)
|
||||
if(prob(50))
|
||||
if(beaker)
|
||||
beaker.forceMove(loc)
|
||||
beaker = null
|
||||
else
|
||||
if(occupant)
|
||||
if(state_open)
|
||||
if (close_machine() == usr)
|
||||
on = TRUE
|
||||
else
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/door_control/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/door/airlock/npc_tamper_act(mob/living/L)
|
||||
//Open the firelocks as well, otherwise they block the way for our gremlin which isn't fun
|
||||
for(var/obj/machinery/door/firedoor/F in get_turf(src))
|
||||
if(F.density)
|
||||
F.npc_tamper_act(L)
|
||||
|
||||
if(prob(40)) //40% - mess with wires
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
else //60% - just open it
|
||||
open()
|
||||
|
||||
/obj/machinery/gibber/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/light_switch/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/turretid/npc_tamper_act(mob/living/L)
|
||||
enabled = rand(0, 1)
|
||||
lethal = rand(0, 1)
|
||||
updateTurrets()
|
||||
|
||||
/obj/machinery/vending/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/shower/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
|
||||
/obj/machinery/deepfryer/npc_tamper_act(mob/living/L)
|
||||
//Deepfry a random nearby item
|
||||
var/list/pickable_items = list()
|
||||
|
||||
for(var/obj/item/I in range(1, L))
|
||||
pickable_items.Add(I)
|
||||
|
||||
if(!pickable_items.len)
|
||||
return
|
||||
|
||||
var/obj/item/I = pick(pickable_items)
|
||||
|
||||
attackby(I, L) //shove the item in, even if it can't be deepfried normally
|
||||
|
||||
/obj/machinery/power/apc/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/power/rad_collector/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/power/emitter/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/npc_tamper_act(mob/living/L)
|
||||
if(!panel_open)
|
||||
panel_open = !panel_open
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/computer/communications/npc_tamper_act(mob/living/user)
|
||||
if(!authenticated)
|
||||
if(prob(20)) //20% chance to log in
|
||||
authenticated = TRUE
|
||||
|
||||
else //Already logged in
|
||||
if(prob(50)) //50% chance to log off
|
||||
authenticated = FALSE
|
||||
else if(istype(user, /mob/living/simple_animal/hostile/gremlin)) //make a hilarious public message
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = user
|
||||
var/result = G.generate_markov_chain()
|
||||
|
||||
if(result)
|
||||
if(prob(85))
|
||||
SScommunications.make_announcement(G, FALSE, result)
|
||||
var/turf/T = get_turf(G)
|
||||
log_say("[key_name(usr)] ([ADMIN_JMP(T)]) has made a captain announcement: [result]")
|
||||
message_admins("[key_name_admin(G)] has made a captain announcement.", 1)
|
||||
else
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_IDLE)
|
||||
SSshuttle.requestEvac(G, result)
|
||||
else if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
|
||||
SSshuttle.cancelEvac(G)
|
||||
|
||||
/obj/machinery/button/door/npc_tamper_act(mob/living/L)
|
||||
attack_hand(L)
|
||||
|
||||
/obj/machinery/sleeper/npc_tamper_act(mob/living/L)
|
||||
if(prob(75))
|
||||
inject_chem(pick(available_chems))
|
||||
else
|
||||
if(state_open)
|
||||
close_machine()
|
||||
else
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/power/smes/npc_tamper_act(mob/living/L)
|
||||
if(prob(50)) //mess with input
|
||||
input_level = rand(0, input_level_max)
|
||||
else //mess with output
|
||||
output_level = rand(0, output_level_max)
|
||||
|
||||
/obj/machinery/syndicatebomb/npc_tamper_act(mob/living/L) //suicide bomber gremlins
|
||||
if(!open_panel)
|
||||
open_panel = !open_panel
|
||||
if(wires)
|
||||
wires.npc_tamper(L)
|
||||
|
||||
/obj/machinery/computer/bank_machine/npc_tamper_act(mob/living/L)
|
||||
siphoning = !siphoning
|
||||
|
||||
/obj/machinery/computer/slot_machine/npc_tamper_act(mob/living/L)
|
||||
spin(L)
|
||||
|
||||
/obj/structure/sink/npc_tamper_act(mob/living/L)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/gremlin))
|
||||
visible_message("<span class='danger'>\The [L] climbs into \the [src] and turns the faucet on!</span>")
|
||||
|
||||
var/mob/living/simple_animal/hostile/gremlin/G = L
|
||||
G.divide()
|
||||
|
||||
return NPC_TAMPER_ACT_NOMSG
|
||||
@@ -0,0 +1,44 @@
|
||||
/datum/round_event_control/gremlin
|
||||
name = "Spawn Gremlins"
|
||||
typepath = /datum/round_event/gremlin
|
||||
weight = 15
|
||||
max_occurrences = 2
|
||||
earliest_start = 20 MINUTES
|
||||
min_players = 5
|
||||
|
||||
|
||||
|
||||
/datum/round_event/gremlin
|
||||
var/static/list/acceptable_spawns = list("xeno_spawn", "generic event spawn", "blobstart", "Assistant")
|
||||
|
||||
/datum/round_event/gremlin/announce()
|
||||
priority_announce("Bioscans indicate that some gremlins entered through the vents. Deal with them!", "Gremlin Alert", 'sound/announcer/classic/attention.ogg')
|
||||
|
||||
/datum/round_event/gremlin/start()
|
||||
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
for(var/obj/effect/landmark/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc) && !isspaceturf(L.loc))
|
||||
if(L.name in acceptable_spawns)
|
||||
spawn_locs += L.loc
|
||||
if(!spawn_locs.len) //If we can't find any gremlin spawns, try the xeno spawns
|
||||
for(var/obj/effect/landmark/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
switch(L.name)
|
||||
if("Assistant")
|
||||
spawn_locs += L.loc
|
||||
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
|
||||
return MAP_ERROR
|
||||
|
||||
var/gremlins_to_spawn = rand(2,5)
|
||||
var/list/gremlin_areas = list()
|
||||
for(var/i = 0, i <= gremlins_to_spawn, i++)
|
||||
var/spawnat = pick(spawn_locs)
|
||||
spawn_locs -= spawnat
|
||||
gremlin_areas += get_area(spawnat)
|
||||
new /mob/living/simple_animal/hostile/gremlin(spawnat)
|
||||
var/grems = gremlin_areas.Join(", ")
|
||||
message_admins("Gremlins have been spawned at the areas: [grems]")
|
||||
log_game("Gremlins have been spawned at the areas: [grems]")
|
||||
return SUCCESSFUL_SPAWN
|
||||
@@ -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."
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/mob/living/simple_animal/hostile/plaguerat
|
||||
name = "plague rat"
|
||||
desc = "A large decaying rat. It spreads its filth and emits a putrid odor to create more of its kind."
|
||||
icon_state = "plaguerat"
|
||||
icon_living = "plaguerat"
|
||||
icon_dead = "plaguerat_dead"
|
||||
speak = list("Skree!","SKREEE!","Squeak?")
|
||||
speak_emote = list("squeaks")
|
||||
emote_hear = list("Hisses.")
|
||||
emote_see = list("runs in a circle.", "stands on its hind legs.")
|
||||
gender = NEUTER
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
maxHealth = 15
|
||||
health = 15
|
||||
see_in_dark = 6
|
||||
obj_damage = 10
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1)
|
||||
response_help_continuous = "glares at"
|
||||
response_help_simple = "glare at"
|
||||
response_disarm_continuous = "skoffs at"
|
||||
response_disarm_simple = "skoff at"
|
||||
response_harm_continuous = "slashes"
|
||||
response_harm_simple = "slash"
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 7
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
faction = list("rat")
|
||||
density = FALSE
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
var/datum/action/cooldown/scavenge
|
||||
var/last_spawn_time = 0
|
||||
///Number assigned to rats and mice, checked when determining infighting.
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/Initialize()
|
||||
. = ..()
|
||||
SSmobs.cheeserats += src
|
||||
AddComponent(/datum/component/swarming)
|
||||
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
|
||||
scavenge = new /datum/action/cooldown/scavenge
|
||||
scavenge.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/Destroy()
|
||||
SSmobs.cheeserats -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/BiologicalLife(seconds, times_fired)
|
||||
if(!(. = ..()))
|
||||
return
|
||||
if(isopenturf(loc))
|
||||
var/turf/open/T = src.loc
|
||||
if(T.air)
|
||||
T.atmos_spawn_air("miasma=5;TEMP=293.15")
|
||||
if(prob(40))
|
||||
scavenge.Trigger()
|
||||
if(prob(50))
|
||||
var/turf/open/floor/F = get_turf(src)
|
||||
if(istype(F) && !F.intact)
|
||||
var/obj/structure/cable/C = locate() in F
|
||||
if(C && C.avail())
|
||||
visible_message("<span class='warning'>[src] chews through the [C]. It looks unharmed!</span>")
|
||||
playsound(src, 'sound/effects/sparks2.ogg', 100, TRUE)
|
||||
C.deconstruct()
|
||||
for(var/obj/O in range(1,src))
|
||||
if(istype(O, /obj/item/trash) || istype(O, /obj/effect/decal/cleanable/blood/gibs))
|
||||
qdel(O)
|
||||
be_fruitful()
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/CanAttack(atom/the_target)
|
||||
if(istype(the_target,/mob/living/simple_animal))
|
||||
var/mob/living/A = the_target
|
||||
if(istype(the_target, /mob/living/simple_animal/hostile/plaguerat) && A.stat == CONSCIOUS)
|
||||
var/mob/living/simple_animal/hostile/plaguerat/R = the_target
|
||||
if(R.faction_check_mob(src, TRUE))
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/**
|
||||
*Checks the mouse cap, if it's above the cap, doesn't spawn a mouse. If below, spawns a mouse and adds it to cheeserats.
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/plaguerat/proc/be_fruitful()
|
||||
var/cap = CONFIG_GET(number/ratcap)
|
||||
if(LAZYLEN(SSmobs.cheeserats) >= cap)
|
||||
visible_message("<span class='warning'>[src] gnaws into its food, [cap] rats are now on the station!</span>")
|
||||
return
|
||||
var/mob/living/newmouse = new /mob/living/simple_animal/hostile/plaguerat(loc)
|
||||
SSmobs.cheeserats += newmouse
|
||||
visible_message("<span class='notice'>[src] gnaws into its food, attracting another rat!</span>")
|
||||
|
||||
/**
|
||||
*Creates a chance to spawn more trash or gibs to repopulate. Otherwise, spawns a corpse or dirt.
|
||||
*/
|
||||
|
||||
/datum/action/cooldown/scavenge
|
||||
name = "Scavenge"
|
||||
desc = "Spread the plague, scavenge for trash and fresh meat to reproduce."
|
||||
icon_icon = 'icons/mob/actions/actions_animal.dmi'
|
||||
background_icon_state = "bg_clock"
|
||||
button_icon_state = "coffer"
|
||||
cooldown_time = 50
|
||||
|
||||
/datum/action/cooldown/scavenge/Trigger()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/turf/T = get_turf(owner)
|
||||
var/loot = rand(1,100)
|
||||
switch(loot)
|
||||
if(1 to 3)
|
||||
var/pickedtrash = pick(GLOB.ratking_trash)
|
||||
to_chat(owner, "<span class='notice'>Excellent, you find more trash to spread your filth!</span>")
|
||||
new /obj/effect/decal/cleanable/dirt(T)
|
||||
new pickedtrash(T)
|
||||
if(4 to 6)
|
||||
to_chat(owner, "<span class='notice'>You find blood and gibs to feed your young!</span>")
|
||||
new /obj/effect/decal/cleanable/blood/gibs(T)
|
||||
new /obj/effect/decal/cleanable/blood/(T)
|
||||
if(7 to 18)
|
||||
to_chat(owner, "<span class='notice'>A corpse rises from the ground. Best to leave it alone.</span>")
|
||||
new /obj/effect/mob_spawn/human/corpse/assistant(T)
|
||||
if(19 to 100)
|
||||
to_chat(owner, "<span class='notice'>Drat. Nothing.</span>")
|
||||
new /obj/effect/decal/cleanable/dirt(T)
|
||||
StartCooldown()
|
||||
@@ -109,7 +109,7 @@
|
||||
continue
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
visible_message("<span class='danger'>[src] vomits up [consumed_mob]!</span>")
|
||||
consumed_mob.forceMove(loc)
|
||||
consumed_mob.forceMove(get_turf(src))
|
||||
consumed_mob.Paralyze(50)
|
||||
if((rifts_charged == 3 || (SSshuttle.emergency.mode == SHUTTLE_DOCKED && rifts_charged > 0)) && !objective_complete)
|
||||
victory()
|
||||
@@ -123,6 +123,7 @@
|
||||
to_chat(src, "<span class='boldwarning'>You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!</span>")
|
||||
destroy_rifts()
|
||||
playsound(src, 'sound/magic/demon_dies.ogg', 100, TRUE)
|
||||
empty_contents()
|
||||
QDEL_NULL(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/AttackingTarget()
|
||||
@@ -351,7 +352,7 @@
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/empty_contents()
|
||||
for(var/atom/movable/AM in src)
|
||||
AM.forceMove(loc)
|
||||
AM.forceMove(get_turf(src))
|
||||
if(prob(90))
|
||||
step(AM, pick(GLOB.alldirs))
|
||||
|
||||
@@ -529,7 +530,7 @@
|
||||
/obj/structure/carp_rift
|
||||
name = "carp rift"
|
||||
desc = "A rift akin to the ones space carp use to travel long distances."
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
max_integrity = 300
|
||||
icon = 'icons/obj/carp_rift.dmi'
|
||||
icon_state = "carp_rift_carpspawn"
|
||||
@@ -636,7 +637,7 @@
|
||||
icon_state = "carp_rift_charged"
|
||||
light_color = LIGHT_COLOR_YELLOW
|
||||
update_light()
|
||||
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
dragon.rifts_charged += 1
|
||||
if(dragon.rifts_charged != 3 && !dragon.objective_complete)
|
||||
|
||||
@@ -238,6 +238,7 @@
|
||||
|
||||
if(istype(W))
|
||||
if(equip_to_slot_if_possible(W, slot, FALSE, FALSE, FALSE, FALSE, TRUE))
|
||||
W.apply_outline()
|
||||
return TRUE
|
||||
|
||||
if(!W)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -66,8 +66,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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
/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)
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
#define DAMAGE_INCREASE_MULTIPLIER 0.25
|
||||
|
||||
|
||||
#define THERMAL_RELEASE_MODIFIER 5 //Higher == less heat released during reaction, not to be confused with the above values
|
||||
#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
|
||||
|
||||
@@ -500,7 +501,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling + clamp(powerloss_inhibition_gas - powerloss_dynamic_scaling, -0.02, 0.02), 0, 1)
|
||||
else
|
||||
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling - 0.05, 0, 1)
|
||||
//Ranges from 0 to 1(1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
|
||||
//Ranges from 0 to 1 (1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
|
||||
//0 means full inhibition, 1 means no inhibition
|
||||
//We take the mol count, and scale it to be our inhibitor
|
||||
powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD, 1, 1.5)), 0, 1)
|
||||
|
||||
@@ -536,15 +538,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
|
||||
|
||||
removed.set_temperature(removed.return_temperature() + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER))
|
||||
//We don't want our output to be too hot
|
||||
removed.set_temperature(max(0, min(removed.return_temperature(), 2500 * dynamic_heat_modifier)))
|
||||
var/effective_temperature = min(removed.return_temperature(), 2500 * dynamic_heat_modifier)
|
||||
|
||||
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 + removed.return_temperature() * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0))
|
||||
removed.adjust_moles(GAS_O2, max(((device_energy + effective_temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0))
|
||||
|
||||
if(removed.return_temperature() < max_temp_increase)
|
||||
removed.adjust_heat(device_energy * dynamic_heat_modifier * THERMAL_RELEASE_MODIFIER)
|
||||
removed.set_temperature(min(removed.return_temperature(), max_temp_increase))
|
||||
|
||||
|
||||
if(produces_gas)
|
||||
env.merge(removed)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
|
||||
var/SA_para_min = 1 //nitrous values
|
||||
var/SA_sleep_min = 5
|
||||
var/BZ_trip_balls_min = 1 //BZ gas
|
||||
var/BZ_trip_balls_min = 0.1 //BZ gas
|
||||
var/BZ_brain_damage_min = 1
|
||||
var/gas_stimulation_min = 0.002 //Nitryl and Stimulum
|
||||
|
||||
var/cold_message = "your face freezing and an icicle forming"
|
||||
@@ -269,13 +270,13 @@
|
||||
// BZ
|
||||
|
||||
var/bz_pp = PP(breath, GAS_BZ)
|
||||
if(bz_pp > BZ_trip_balls_min)
|
||||
if(bz_pp > BZ_brain_damage_min)
|
||||
H.hallucination += 10
|
||||
H.reagents.add_reagent(/datum/reagent/bz_metabolites,5)
|
||||
if(prob(33))
|
||||
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150)
|
||||
|
||||
else if(bz_pp > 0.01)
|
||||
else if(bz_pp > BZ_trip_balls_min)
|
||||
H.hallucination += 5
|
||||
H.reagents.add_reagent(/datum/reagent/bz_metabolites,1)
|
||||
|
||||
@@ -476,7 +477,7 @@
|
||||
)
|
||||
SA_para_min = 30
|
||||
SA_sleep_min = 50
|
||||
BZ_trip_balls_min = 30
|
||||
BZ_brain_damage_min = 30
|
||||
emp_vulnerability = 3
|
||||
|
||||
cold_level_1_threshold = 200
|
||||
@@ -509,8 +510,29 @@
|
||||
heat_level_2_threshold = 600 // up 200 from level 1, 1000 is silly but w/e for level 3
|
||||
|
||||
/obj/item/organ/lungs/ashwalker/populate_gas_info()
|
||||
// humans usually breathe 21 but require 16/17, so 80% - 1, which is more lenient but it's fine
|
||||
#define SAFE_THRESHOLD_RATIO 0.8
|
||||
var/datum/gas_mixture/breath = SSair.planetary[LAVALAND_DEFAULT_ATMOS] // y'all know
|
||||
var/pressure = breath.return_pressure()
|
||||
var/total_moles = breath.total_moles()
|
||||
for(var/id in breath.get_gases())
|
||||
var/this_pressure = PP(breath, id)
|
||||
var/req_pressure = (this_pressure * SAFE_THRESHOLD_RATIO) - 1
|
||||
if(req_pressure > 0)
|
||||
gas_min[id] = req_pressure
|
||||
if(id in gas_max)
|
||||
gas_max[id] += this_pressure
|
||||
var/bz = breath.get_moles(GAS_BZ)
|
||||
if(bz)
|
||||
BZ_trip_balls_min += bz
|
||||
BZ_brain_damage_min += bz
|
||||
|
||||
gas_max[GAS_N2] = PP(breath, GAS_N2) + 5
|
||||
var/o2_pp = PP(breath, GAS_O2)
|
||||
safe_breath_min = 0.3 * o2_pp
|
||||
safe_breath_max = 1.3 * o2_pp
|
||||
..()
|
||||
gas_max[GAS_N2] = 28
|
||||
#undef SAFE_THRESHOLD_RATIO
|
||||
|
||||
/obj/item/organ/lungs/slime
|
||||
name = "vacuole"
|
||||
|
||||
@@ -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 />")
|
||||
|
||||
@@ -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?
|
||||
@@ -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,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))
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user