diff --git a/README.md b/README.md
index bbceb0f05b..cc485278cc 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
## Citadel Station 13
Based and maintained from /tg/station.
-[](https://travis-ci.org/Citadel-Station-13/Citadel-Station-13)
+[](https://github.com/Citadel-Station-13/Citadel-Station-13/actions?query=workflow%3A%22CI+Suite%22)
[](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Percentage of issues still open")
[](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Average time to resolve an issue")
@@ -100,6 +100,14 @@ install, overwriting when prompted except if we've specified otherwise, and
recompile the game. Once you start the server up again, you should be running
the new version.
+## :exclamation: How to compile :exclamation:
+
+On **2021-01-04** we have changed the way to compile the codebase.
+
+Find `Build.cmd` in this folder, and double click it to initiate the build. It consists of multiple steps and might take around 1-5 minutes to compile. If it closes, it means it has finished its job. You can then setup the server normally by opening `tgstation.dmb` in DreamDaemon.
+
+**Building tgstation in DreamMaker directly is now deprecated and might produce errors**, such as `'tgui.bundle.js': cannot find file`.
+
## HOSTING
If you'd like a more robust server hosting option for tgstation and its
diff --git a/auxmos.dll b/auxmos.dll
index be117d82da..19e019e568 100644
Binary files a/auxmos.dll and b/auxmos.dll differ
diff --git a/auxmos.pdb b/auxmos.pdb
index 2552298fdd..49269317d4 100644
Binary files a/auxmos.pdb and b/auxmos.pdb differ
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 0a17f0d1df..19aac1aee0 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -78,6 +78,12 @@
var/datum/emote/E = new path()
E.emote_list[E.key] = E
+ // Hair Gradients - Initialise all /datum/sprite_accessory/hair_gradient into an list indexed by gradient-style name
+ for(var/path in subtypesof(/datum/sprite_accessory/hair_gradient))
+ var/datum/sprite_accessory/hair_gradient/H = new path()
+ GLOB.hair_gradients_list[H.name] = H
+
+ // Keybindings
init_keybindings()
//Uplink Items
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 28f657828b..64c96cae3b 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -6,6 +6,7 @@ GLOBAL_LIST_EMPTY(hair_styles_female_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_list) //stores /datum/sprite_accessory/facial_hair indexed by name
GLOBAL_LIST_EMPTY(facial_hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_female_list) //stores only hair names
+GLOBAL_LIST_EMPTY(hair_gradients_list) //stores /datum/sprite_accessory/hair_gradient indexed by name
//Underwear
GLOBAL_LIST_EMPTY_TYPED(underwear_list, /datum/sprite_accessory/underwear/bottom) //stores bottoms indexed by name
GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 9321427387..1b72cc71b1 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -63,6 +63,8 @@
return ShiftClickOn(A)
if(modifiers["alt"]) // alt and alt-gr (rightalt)
return AltClickOn(A)
+ if(modifiers["ctrl"] && modifiers["right"]) //CIT CHANGE - right click ctrl for a new form of dropping items
+ return CtrlRightClickOn(A, params) //CIT CHANGE
if(modifiers["ctrl"])
return CtrlClickOn(A)
@@ -295,6 +297,46 @@
if(!(flags & COMPONENT_DENY_EXAMINATE) && user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE))
user.examinate(src)
+/*
+ Ctrl + Right click
+ Combat mode feature
+ Drop item in hand at position.
+*/
+/atom/proc/CtrlRightClickOn(atom/A, params)
+ if(isliving(src) && Adjacent(A)) //honestly only humans can do this given it's combat mode but if it's implemented for any other mobs...
+ var/mob/living/L = src
+ if(L.incapacitated())
+ return
+ var/obj/item/I = L.get_active_held_item()
+ var/turf/T = get_turf(A)
+ if(T)
+ if(I) //drop item at cursor.
+ if(T.density) //no, you can't use your funny blue cube or red cube to clip into the fucking wall.
+ return
+ for(var/atom/C in T.contents) //nor can you clip into a window or a door/false wall that's not open.
+ if(C.opacity || (((C.flags_1 & PREVENT_CLICK_UNDER_1) > 0) != (istype(C,/obj/machinery/door) && !C.density))) //XOR operation within because doors always have PREVENT_CLICK_UNDER_1 flag enabled. Dumb, I know.
+ return
+ if(L.transferItemToLoc(I, T))
+ var/list/click_params = params2list(params)
+ //Center the icon where the user clicked. (shamelessly stole code from tables)
+ if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
+ return
+ //Clamp it so that the icon never moves more than 16 pixels in either direction
+ I.pixel_x = clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ I.pixel_y = clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ return TRUE
+ else if(isitem(A) && L.has_active_hand()) //if they have an open hand they'll rotate the item instead.
+ var/obj/item/I2 = A
+ if(!I2.anchored)
+ var/matrix/ntransform = matrix(I2.transform)
+ ntransform.Turn(15)
+ animate(I2, transform = ntransform, time = 2)
+ return TRUE
+ else
+ A.CtrlClick(src)
+
+
+
/*
Ctrl click
For most objects, pull
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index 260a4c1b29..91e92feee9 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -30,6 +30,7 @@ SUBSYSTEM_DEF(air)
var/list/networks = list()
var/list/pipenets_needing_rebuilt = list()
var/list/deferred_airs = list()
+ var/cur_deferred_airs = 0
var/max_deferred_airs = 0
var/list/obj/machinery/atmos_machinery = list()
var/list/obj/machinery/atmos_air_machinery = list()
@@ -54,14 +55,22 @@ SUBSYSTEM_DEF(air)
var/equalize_turf_limit = 10
// Max number of turfs to look for a space turf, and max number of turfs that will be decompressed.
var/equalize_hard_turf_limit = 2000
- // Whether equalization should be enabled at all.
+ // Whether equalization is enabled. Can be disabled for performance reasons.
var/equalize_enabled = TRUE
+ // Whether equalization should be enabled.
+ var/should_do_equalization = TRUE
+ // When above 0, won't equalize; performance handling
+ var/eq_cooldown = 0
// Whether turf-to-turf heat exchanging should be enabled.
var/heat_enabled = FALSE
// Max number of times process_turfs will share in a tick.
var/share_max_steps = 3
+ // Target for share_max_steps; can go below this, if it determines the thread is taking too long.
+ var/share_max_steps_target = 3
// Excited group processing will try to equalize groups with total pressure difference less than this amount.
var/excited_group_pressure_goal = 1
+ // Target for excited_group_pressure_goal; can go below this, if it determines the thread is taking too long.
+ var/excited_group_pressure_goal_target = 1
// If this is set to 0, monstermos won't process planet atmos
var/planet_equalize_enabled = 0
@@ -221,6 +230,7 @@ SUBSYSTEM_DEF(air)
// This also happens to do all the commented out stuff below, all in a single separate thread. This is mostly so that the
// waiting is consistent.
if(currentpart == SSAIR_ACTIVETURFS)
+ run_delay_heuristics()
timer = TICK_USAGE_REAL
process_turfs(resumed)
cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
@@ -278,7 +288,8 @@ SUBSYSTEM_DEF(air)
pipenets_needing_rebuilt += atmos_machine
/datum/controller/subsystem/air/proc/process_deferred_airs(resumed = 0)
- max_deferred_airs = max(deferred_airs.len,max_deferred_airs)
+ cur_deferred_airs = deferred_airs.len
+ max_deferred_airs = max(cur_deferred_airs,max_deferred_airs)
while(deferred_airs.len)
var/list/cur_op = deferred_airs[deferred_airs.len]
deferred_airs.len--
@@ -378,6 +389,24 @@ SUBSYSTEM_DEF(air)
return
*/
+/datum/controller/subsystem/air/proc/run_delay_heuristics()
+ if(!equalize_enabled)
+ cost_equalize = 0
+ if(should_do_equalization)
+ eq_cooldown--
+ if(eq_cooldown <= 0)
+ equalize_enabled = TRUE
+ var/total_thread_time = cost_turfs + cost_equalize + cost_groups + cost_post_process
+ if(total_thread_time)
+ var/wait_ms = wait * 100
+ var/delay_threshold = 1-(total_thread_time/wait_ms + cur_deferred_airs / 50)
+ share_max_steps = max(1,round(share_max_steps_target * delay_threshold, 1))
+ eq_cooldown += (1-delay_threshold) * (cost_equalize / total_thread_time) * 2
+ if(eq_cooldown > 0.5)
+ equalize_enabled = FALSE
+ excited_group_pressure_goal = max(0,excited_group_pressure_goal_target * (1 - delay_threshold))
+
+
/datum/controller/subsystem/air/proc/process_turfs(resumed = 0)
if(process_turfs_auxtools(resumed,TICK_REMAINING_MS))
pause()
@@ -402,7 +431,7 @@ SUBSYSTEM_DEF(air)
pause()
/datum/controller/subsystem/air/proc/finish_turf_processing(resumed = 0)
- if(finish_turf_processing_auxtools(TICK_REMAINING_MS))
+ if(finish_turf_processing_auxtools(TICK_REMAINING_MS) || thread_running())
pause()
/datum/controller/subsystem/air/proc/post_process_turfs(resumed = 0)
diff --git a/code/datums/atmosphere/planetary.dm b/code/datums/atmosphere/planetary.dm
index 248873562f..a8b77f7394 100644
--- a/code/datums/atmosphere/planetary.dm
+++ b/code/datums/atmosphere/planetary.dm
@@ -12,9 +12,7 @@
GAS_CO2=10,
)
restricted_gases = list(
- GAS_PLASMA=0.1,
- GAS_BZ=1.2,
- GAS_METHANE=1.0,
+ GAS_BZ=0.1,
GAS_METHYL_BROMIDE=0.1,
)
restricted_chance = 30
@@ -23,7 +21,7 @@
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
minimum_temp = BODYTEMP_COLD_DAMAGE_LIMIT + 1
- maximum_temp = 350
+ maximum_temp = 320
/datum/atmosphere/icemoon
id = ICEMOON_DEFAULT_ATMOS
@@ -38,8 +36,6 @@
GAS_CO2=10,
)
restricted_gases = list(
- GAS_PLASMA=0.1,
- GAS_METHANE=1.0,
GAS_METHYL_BROMIDE=0.1,
)
restricted_chance = 10
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index 2f0667d942..36712ea6bb 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -162,3 +162,20 @@
gain_text = "You feel like munching on a can of soda."
lose_text = "You no longer feel like you should be eating trash."
mob_trait = TRAIT_TRASHCAN
+
+/datum/quirk/colorist
+ name = "Colorist"
+ desc = "You like carrying around a hair dye spray to quickly apply color patterns to your hair."
+ value = 0
+ medical_record_text = "Patient enjoys dyeing their hair with pretty colors."
+
+/datum/quirk/colorist/on_spawn()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/dyespray/spraycan = new(get_turf(quirk_holder))
+ H.equip_to_slot(spraycan, SLOT_IN_BACKPACK)
+ H.regenerate_icons()
+
+/datum/quirk/colorist/post_add()
+ var/mob/living/carbon/human/H = quirk_holder
+ SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
+ to_chat(quirk_holder, "You brought some extra dye with you! It's in your bag if you forgot.")
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index d8ebf6f20c..64ca226c2a 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -151,14 +151,14 @@ Credit where due:
var/datum/team/clockcult/main_clockcult
/datum/game_mode/clockwork_cult/pre_setup() //Gamemode and job code is pain. Have fun codediving all of that stuff, whoever works on this next - Delta
- /*var/list/errorList = list()
+ var/list/errorList = list()
var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE)
if(errorList.len) // reebe failed to load
message_admins("Reebe failed to load!")
log_game("Reebe failed to load!")
return FALSE
for(var/datum/parsed_map/PM in reebes) //Temporarily commented because of z-level loading reliably segfaulting the server.
- PM.initTemplateBounds()*/
+ PM.initTemplateBounds()
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
@@ -275,7 +275,7 @@ Credit where due:
ears = /obj/item/radio/headset
gloves = /obj/item/clothing/gloves/color/yellow
belt = /obj/item/storage/belt/utility/servant
- backpack_contents = list(/obj/item/storage/box/engineer = 1, \
+ backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/clockwork/replica_fabricator = 1, /obj/item/stack/tile/brass/fifty = 1, /obj/item/reagent_containers/food/drinks/bottle/holyoil = 1)
id = /obj/item/pda
var/plasmaman //We use this to determine if we should activate internals in post_equip()
diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm
index 659d2de105..fa73e2cca9 100644
--- a/code/game/gamemodes/clown_ops/clown_ops.dm
+++ b/code/game/gamemodes/clown_ops/clown_ops.dm
@@ -43,7 +43,7 @@
l_pocket = /obj/item/pinpointer/nuke/syndicate
r_pocket = /obj/item/bikehorn
id = /obj/item/card/id/syndicate
- 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,
/obj/item/reagent_containers/spray/waterflower/lube)
implants = list(/obj/item/implant/sad_trombone)
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index dcf84e84db..a82abe9b3d 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -127,7 +127,7 @@
l_pocket = /obj/item/pinpointer/nuke/syndicate
id = /obj/item/card/id/syndicate
belt = /obj/item/gun/ballistic/automatic/pistol
- 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)
var/tc = 25
@@ -173,7 +173,7 @@
internals_slot = SLOT_R_STORE
belt = /obj/item/storage/belt/military
r_hand = /obj/item/gun/ballistic/automatic/shotgun/bulldog
- backpack_contents = list(/obj/item/storage/box/syndie=1,\
+ backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
/obj/item/tank/jetpack/oxygen/harness=1,\
/obj/item/gun/ballistic/automatic/pistol=1,\
/obj/item/kitchen/knife/combat/survival)
@@ -188,7 +188,7 @@
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
internals_slot = SLOT_R_STORE
belt = /obj/item/storage/belt/military
- backpack_contents = list(/obj/item/storage/box/syndie=1,\
+ backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
/obj/item/tank/jetpack/oxygen/harness=1,\
/obj/item/gun/ballistic/automatic/pistol=1,\
/obj/item/kitchen/knife/combat/survival)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index af7b69516d..3dbeda5ff5 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -879,7 +879,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/MouseEntered(location, control, params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
- if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr.client.prefs.enable_tips && !QDELETED(src) || isobserver(usr))
+ if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr.client.prefs.enable_tips && !QDELETED(src))
var/timedelay = usr.client.prefs.tip_delay/100
var/user = usr
tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
@@ -900,7 +900,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
remove_outline()
/obj/item/proc/apply_outline(colour = null)
- if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src))
+ if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
return
if(usr.client)
if(!usr.client.prefs.outline_enabled)
diff --git a/code/game/objects/items/dyekit.dm b/code/game/objects/items/dyekit.dm
new file mode 100644
index 0000000000..43a05c7939
--- /dev/null
+++ b/code/game/objects/items/dyekit.dm
@@ -0,0 +1,40 @@
+/obj/item/dyespray
+ name = "hair dye spray"
+ desc = "A spray to dye your hair any gradients you'd like."
+ icon = 'icons/obj/dyespray.dmi'
+ icon_state = "dyespray"
+
+/obj/item/dyespray/attack_self(mob/user)
+ dye(user)
+
+/obj/item/dyespray/pre_attack(atom/target, mob/living/user, params)
+ dye(target)
+ return ..()
+
+/**
+ * Applies a gradient and a gradient color to a mob.
+ *
+ * Arguments:
+ * * target - The mob who we will apply the gradient and gradient color to.
+ */
+
+/obj/item/dyespray/proc/dye(mob/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/human_target = target
+
+ var/new_grad_style = input(usr, "Choose a color pattern:", "Character Preference") as null|anything in GLOB.hair_gradients_list
+ if(!new_grad_style)
+ return
+
+ var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference","#"+human_target.grad_color) as color|null
+ if(!new_grad_color)
+ return
+
+ human_target.grad_style = new_grad_style
+ human_target.grad_color = sanitize_hexcolor(new_grad_color)
+ to_chat(human_target, "You start applying the hair dye...")
+ if(!do_after(usr, 30, target = human_target))
+ return
+ playsound(src, 'sound/effects/spray.ogg', 5, TRUE, 5)
+ human_target.update_hair()
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index 1be570b62b..275536d370 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -184,7 +184,7 @@
hitcost = 75 //Costs more than a standard cyborg esword
w_class = WEIGHT_CLASS_NORMAL
sharpness = SHARP_EDGED
- light_color = "#40ceff"
+ light_color = LIGHT_COLOR_RED
tool_behaviour = TOOL_SAW
toolspeed = 0.7
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 15153521ed..504bff9e29 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -502,6 +502,8 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
new /datum/stack_recipe("sterile masks box", /obj/item/storage/box/masks), \
new /datum/stack_recipe("body bag box", /obj/item/storage/box/bodybags), \
new /datum/stack_recipe("prescription glasses box", /obj/item/storage/box/rxglasses), \
+ new /datum/stack_recipe("oxygen tank box", /obj/item/storage/box/emergencytank), \
+ new /datum/stack_recipe("extended oxygen tank box", /obj/item/storage/box/engitank), \
null, \
new /datum/stack_recipe("disk box", /obj/item/storage/box/disks), \
new /datum/stack_recipe("light tubes box", /obj/item/storage/box/lights/tubes), \
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index ea40b49c5f..8b897cb6e5 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -102,12 +102,22 @@
new /obj/item/disk/nanite_program(src)
// Ordinary survival box
+/obj/item/storage/box/survival
+ name = "survival box"
+ desc = "A box with the bare essentials of ensuring the survival of you and others."
+ icon_state = "internals"
+ illustration = "emergencytank"
+ var/mask_type = /obj/item/clothing/mask/breath
+ var/internal_type = /obj/item/tank/internals/emergency_oxygen
+ var/medipen_type = /obj/item/reagent_containers/hypospray/medipen
+
/obj/item/storage/box/survival/PopulateContents()
- new /obj/item/clothing/mask/breath(src)
- new /obj/item/reagent_containers/hypospray/medipen(src)
+ new mask_type(src)
+ if(!isnull(medipen_type))
+ new medipen_type(src)
if(!isplasmaman(loc))
- new /obj/item/tank/internals/emergency_oxygen(src)
+ new internal_type(src)
else
new /obj/item/tank/internals/plasmaman/belt(src)
@@ -115,10 +125,13 @@
..() // we want the survival stuff too.
new /obj/item/radio/off(src)
-/obj/item/storage/box/survival_mining/PopulateContents()
- new /obj/item/clothing/mask/gas/explorer(src)
+// Mining survival box
+/obj/item/storage/box/survival/mining
+ mask_type = /obj/item/clothing/mask/gas/explorer
+
+/obj/item/storage/box/survival/mining/PopulateContents()
+ ..()
new /obj/item/crowbar/red(src)
- new /obj/item/reagent_containers/hypospray/medipen(src)
if(!isplasmaman(loc))
new /obj/item/tank/internals/emergency_oxygen(src)
@@ -126,39 +139,30 @@
new /obj/item/tank/internals/plasmaman/belt(src)
// Engineer survival box
-/obj/item/storage/box/engineer/PopulateContents()
- new /obj/item/clothing/mask/breath(src)
- new /obj/item/reagent_containers/hypospray/medipen(src)
+/obj/item/storage/box/survival/engineer
+ name = "extended-capacity survival box"
+ desc = "A box with the bare essentials of ensuring the survival of you and others. This one is labelled to contain an extended-capacity tank."
+ illustration = "extendedtank"
+ internal_type = /obj/item/tank/internals/emergency_oxygen/engi
- if(!isplasmaman(loc))
- new /obj/item/tank/internals/emergency_oxygen/engi(src)
- else
- new /obj/item/tank/internals/plasmaman/belt(src)
-
-/obj/item/storage/box/engineer/radio/PopulateContents()
+/obj/item/storage/box/survival/engineer/radio/PopulateContents()
..() // we want the regular items too.
new /obj/item/radio/off(src)
// Syndie survival box
-/obj/item/storage/box/syndie/PopulateContents()
- new /obj/item/clothing/mask/gas/syndicate(src)
-
- if(!isplasmaman(loc))
- new /obj/item/tank/internals/emergency_oxygen/engi(src)
- else
- new /obj/item/tank/internals/plasmaman/belt(src)
+/obj/item/storage/box/survival/syndie //why is this its own thing if it's just the engi box with a syndie mask and medipen?
+ name = "extended-capacity survival box"
+ desc = "A box with the bare essentials of ensuring the survival of you and others. This one is labelled to contain an extended-capacity tank."
+ illustration = "extendedtank"
+ mask_type = /obj/item/clothing/mask/gas/syndicate
+ internal_type = /obj/item/tank/internals/emergency_oxygen/engi
+ medipen_type = null
// Security survival box
-/obj/item/storage/box/security/PopulateContents()
- new /obj/item/clothing/mask/gas/sechailer(src)
- new /obj/item/reagent_containers/hypospray/medipen(src)
+/obj/item/storage/box/survival/security
+ mask_type = /obj/item/clothing/mask/gas/sechailer
- if(!isplasmaman(loc))
- new /obj/item/tank/internals/emergency_oxygen(src)
- else
- new /obj/item/tank/internals/plasmaman/belt(src)
-
-/obj/item/storage/box/security/radio/PopulateContents()
+/obj/item/storage/box/survival/security/radio/PopulateContents()
..() // we want the regular stuff too
new /obj/item/radio/off(src)
@@ -1244,6 +1248,26 @@
icon_state = "box_pink"
illustration = null
+/obj/item/storage/box/emergencytank
+ name = "emergency oxygen tank box"
+ desc = "A box of emergency oxygen tanks."
+ illustration = "emergencytank"
+
+/obj/item/storage/box/emergencytank/PopulateContents()
+ ..()
+ for(var/i in 1 to 7)
+ new /obj/item/tank/internals/emergency_oxygen(src) //in case anyone ever wants to do anything with spawning them, apart from crafting the box
+
+/obj/item/storage/box/engitank
+ name = "extended-capacity emergency oxygen tank box"
+ desc = "A box of extended-capacity emergency oxygen tanks."
+ illustration = "extendedtank"
+
+/obj/item/storage/box/engitank/PopulateContents()
+ ..()
+ for(var/i in 1 to 7)
+ new /obj/item/tank/internals/emergency_oxygen/engi(src) //in case anyone ever wants to do anything with spawning them, apart from crafting the box
+
/obj/item/storage/box/mre //base MRE type.
name = "Nanotrasen MRE Ration Kit Menu 0"
desc = "A package containing food suspended in an outdated bluespace pocket which lasts for centuries. If you're lucky you may even be able to enjoy the meal without getting food poisoning."
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 1d23903d03..cd10f9209c 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -7,7 +7,7 @@
new /obj/item/clothing/neck/petcollar(src) //I considered removing the pet stuff too but eh, who knows. We might get Renault back. Plus I guess you could use that collar for... other means. Aren't you supposed to be guarding the disk?
new /obj/item/pet_carrier(src)
new /obj/item/clothing/suit/armor/vest/capcarapace(src)
- new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src)
+ new /obj/item/clothing/suit/toggle/captains_parade(src)
new /obj/item/clothing/head/crown/fancy(src)
new /obj/item/cartridge/captain(src)
new /obj/item/storage/box/silver_ids(src)
@@ -56,6 +56,7 @@
/obj/structure/closet/secure_closet/hos/PopulateContents()
..()
new /obj/item/clothing/neck/cloak/hos(src)
+ new /obj/item/clothing/suit/toggle/armor/hos/hos_formal(src)
new /obj/item/cartridge/hos(src)
new /obj/item/radio/headset/heads/hos(src)
new /obj/item/clothing/under/rank/security/head_of_security/parade/female(src)
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
index 87a4ddf7eb..28228aedd6 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
@@ -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
diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm
index e05502dbad..e286cbbe7f 100644
--- a/code/modules/atmospherics/machinery/pipes/pipes.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipes.dm
@@ -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)
diff --git a/code/modules/cargo/packs/goodies.dm b/code/modules/cargo/packs/goodies.dm
index 5d4598fd58..5151845221 100644
--- a/code/modules/cargo/packs/goodies.dm
+++ b/code/modules/cargo/packs/goodies.dm
@@ -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"
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 8b214e85a4..cb262e57f3 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -119,6 +119,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/hair_color = "000000" //Hair color
var/facial_hair_style = "Shaved" //Face hair type
var/facial_hair_color = "000000" //Facial hair color
+ var/grad_style //Hair gradient style
+ var/grad_color = "FFFFFF" //Hair gradient color
var/skin_tone = "caucasian1" //Skin color
var/use_custom_skin_tone = FALSE
var/left_eye_color = "000000" //Eye color
@@ -505,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "< >
"
dat += " Change
"
+ dat += "
Hair Gradient
"
+
+ dat += "[grad_style]"
+ dat += "< >
"
+ dat += " Change
"
+
dat += ""
//Mutant stuff
var/mutant_category = 0
@@ -1711,6 +1719,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("previous_facehair_style")
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
+ if("grad_color")
+ var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null
+ if(new_grad_color)
+ grad_color = sanitize_hexcolor(new_grad_color, 6)
+
+ if("grad_style")
+ var/new_grad_style
+ new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list
+ if(new_grad_style)
+ grad_style = new_grad_style
+
+ if("next_grad_style")
+ grad_style = next_list_item(grad_style, GLOB.hair_gradients_list)
+
+ if("previous_grad_style")
+ grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list)
+
if("cycle_bg")
bgstate = next_list_item(bgstate, bgstate_options)
@@ -3016,6 +3041,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.dna.skin_tone_override = use_custom_skin_tone ? skin_tone : null
character.hair_style = hair_style
character.facial_hair_style = facial_hair_style
+ character.grad_style = grad_style
+ character.grad_color = grad_color
character.underwear = underwear
character.saved_underwear = underwear
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 4d62bb149d..96ffd09af8 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -683,6 +683,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["skin_tone"] >> skin_tone
S["hair_style_name"] >> hair_style
S["facial_style_name"] >> facial_hair_style
+ S["grad_style"] >> grad_style
+ S["grad_color"] >> grad_color
S["underwear"] >> underwear
S["undie_color"] >> undie_color
S["undershirt"] >> undershirt
@@ -875,6 +877,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
+ grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None")
+ grad_color = sanitize_hexcolor(grad_color, 6, FALSE)
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
@@ -1044,6 +1048,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["skin_tone"] , skin_tone)
WRITE_FILE(S["hair_style_name"] , hair_style)
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
+ WRITE_FILE(S["grad_style"] , grad_style)
+ WRITE_FILE(S["grad_color"] , grad_color)
WRITE_FILE(S["underwear"] , underwear)
WRITE_FILE(S["undie_color"] , undie_color)
WRITE_FILE(S["undershirt"] , undershirt)
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 525db577e0..2e3c544d08 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -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)
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index 7cd2e9b394..86aa0748df 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -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,
diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm
index acf015c845..ac852a35a8 100644
--- a/code/modules/clothing/outfits/vr.dm
+++ b/code/modules/clothing/outfits/vr.dm
@@ -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.
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 254ed60c03..c245a30b92 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -136,11 +136,18 @@
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"
@@ -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
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index bff56d1d16..de66c32138 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -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)
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index 902be0bdc8..804952dbb2 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -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
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index c772a8acae..d612b6a56d 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -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)
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index bdae7fe028..3462fb96c9 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -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)
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index 04d3fb53b8..0c04380afe 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -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
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 2396728ad8..25bd2196a0 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -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)
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index dae3094ebe..074ccac09f 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -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)
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
index 88dda923a0..e002fa369d 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
@@ -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"
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index e15f5b8be0..6723d2d4a6 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -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"
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 6f94be2d41..80edd142b7 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -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
diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm
index e34c5ce053..13240b2734 100644
--- a/code/modules/mob/living/living_active_parry.dm
+++ b/code/modules/mob/living/living_active_parry.dm
@@ -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)
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 143025aed1..9d2e4a8e31 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -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
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index b68a289e85..69f2d5454e 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -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()
diff --git a/code/modules/uplink/uplink_items/uplink_devices.dm b/code/modules/uplink/uplink_items/uplink_devices.dm
index ad1cc31ba7..21889219cf 100644
--- a/code/modules/uplink/uplink_items/uplink_devices.dm
+++ b/code/modules/uplink/uplink_items/uplink_devices.dm
@@ -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
diff --git a/code/modules/uplink/uplink_items/uplink_roles.dm b/code/modules/uplink/uplink_items/uplink_roles.dm
index 774c2d9794..72e0111c41 100644
--- a/code/modules/uplink/uplink_items/uplink_roles.dm
+++ b/code/modules/uplink/uplink_items/uplink_roles.dm
@@ -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")
diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm
index cea9c5ae70..a1d9bc1691 100644
--- a/code/modules/vending/games.dm
+++ b/code/modules/vending/games.dm
@@ -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,
diff --git a/html/changelog.html b/html/changelog.html
index b1844fa0a9..4eee5b8770 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,15 @@
-->
+
08 September 2021
+
keronshb updated:
+
+ - Adds the parade outfit for the HoS and Centcomm
+ - Recolors the parade outfit for the Captain
+ - Adds a toggle option for the parade outfits
+ - Observers can no longer highlight your items
+
+
07 September 2021
bunny232 updated:
@@ -450,12 +459,6 @@
- Makes the RD APC automatically connect to the powernet with the correct wire on pubby.
- Added another wall for the AM engine so it doesn't space the airlock roundstart.
-
-
07 July 2021
-
DeltaFire15 updated:
-
- - Golem / Plasmaman species color should work again.
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 5d85fe7ccb..4ab3186cff 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -29911,3 +29911,9 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- bugfix: Gremlins no longer have AA when dead
zeroisthebiggay:
- spellcheck: you can just about
+2021-09-08:
+ keronshb:
+ - imageadd: Adds the parade outfit for the HoS and Centcomm
+ - imageadd: Recolors the parade outfit for the Captain
+ - imageadd: Adds a toggle option for the parade outfits
+ - bugfix: Observers can no longer highlight your items
diff --git a/html/changelogs/AutoChangeLog-pr-15039.yml b/html/changelogs/AutoChangeLog-pr-15039.yml
new file mode 100644
index 0000000000..b5de8fe5ae
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15039.yml
@@ -0,0 +1,7 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Added the ability to dye your hair with gradients by using a hair dye spray."
+ - rscadd: "The new Colorist quirk, allowing you to spawn with a hair dye spray."
+ - rscadd: "Adds Hair gradients to preferences"
+ - imageadd: "Three new hair gradients, a pair of shorter fades and a spiky wave."
diff --git a/html/changelogs/AutoChangeLog-pr-15043.yml b/html/changelogs/AutoChangeLog-pr-15043.yml
new file mode 100644
index 0000000000..f7fcc7f55b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15043.yml
@@ -0,0 +1,4 @@
+author: "BlueWildrose"
+delete-after: True
+changes:
+ - rscadd: "CTRL + (combat mode) Right Click - positional dropping and item rotation."
diff --git a/html/changelogs/AutoChangeLog-pr-15056.yml b/html/changelogs/AutoChangeLog-pr-15056.yml
new file mode 100644
index 0000000000..a743725daa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15056.yml
@@ -0,0 +1,4 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Readds Reebe"
diff --git a/html/changelogs/AutoChangeLog-pr-15058.yml b/html/changelogs/AutoChangeLog-pr-15058.yml
new file mode 100644
index 0000000000..cef9e8e5bc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15058.yml
@@ -0,0 +1,13 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - bugfix: "Replaced the DNA probe's old sprite (Hypospray) with a unique sprite"
+ - imageadd: "added some icons and images for hyposprays and medipens so they stand out"
+ - imageadd: "added inhands for the cautery, retractor, drapes and hemostat."
+ - imageadd: "New icon and sprites for the DNA probe"
+ - imageadd: "Emergency survival boxes now have an unique blue sprite and description to tell them apart from regular boxes."
+ - imageadd: "Added craftable normal/extended emergency oxygen tank boxes to put your emergency oxygen tank collection inside."
+ - imageadd: "Emergency first aid kits now look visually consistent with full first aid kits."
+ - imageadd: "Ports new Hypospray, Combat Autoinjector, Pestle, Mortar and Dropper sprites from Shiptest!"
+ - imageadd: "Adds a new sprite for pill bottles!"
+ - imageadd: "new surgical tool sprites"
diff --git a/html/changelogs/AutoChangeLog-pr-15064.yml b/html/changelogs/AutoChangeLog-pr-15064.yml
new file mode 100644
index 0000000000..d8900667c7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15064.yml
@@ -0,0 +1,5 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - imageadd: "Updates pride hammer sprites!"
+ - imageadd: "Replaced old Mjolnir sprites with new Mjolnir sprites."
diff --git a/html/changelogs/AutoChangeLog-pr-15069.yml b/html/changelogs/AutoChangeLog-pr-15069.yml
new file mode 100644
index 0000000000..16e85af2bf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15069.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - imageadd: "The cyborg toolset is now all new and improved, with a new coat of paint!"
diff --git a/html/changelogs/AutoChangeLog-pr-15089.yml b/html/changelogs/AutoChangeLog-pr-15089.yml
new file mode 100644
index 0000000000..cebf6ba51b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15089.yml
@@ -0,0 +1,4 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - bugfix: "Adds viewers for mask of madness so it doesn't wallhack"
diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi
index 39ad5337a7..b5f7a0992a 100644
Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ
diff --git a/icons/mob/clothing/suit_digi.dmi b/icons/mob/clothing/suit_digi.dmi
index f80cb4a426..115c2b7f6b 100644
Binary files a/icons/mob/clothing/suit_digi.dmi and b/icons/mob/clothing/suit_digi.dmi differ
diff --git a/icons/mob/hair_gradients.dmi b/icons/mob/hair_gradients.dmi
new file mode 100644
index 0000000000..ceb3b52eab
Binary files /dev/null and b/icons/mob/hair_gradients.dmi differ
diff --git a/icons/mob/inhands/equipment/medical_lefthand.dmi b/icons/mob/inhands/equipment/medical_lefthand.dmi
index 232f3f9e65..7cbb240b93 100644
Binary files a/icons/mob/inhands/equipment/medical_lefthand.dmi and b/icons/mob/inhands/equipment/medical_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/medical_righthand.dmi b/icons/mob/inhands/equipment/medical_righthand.dmi
index 8133cca9bd..3b2b906404 100644
Binary files a/icons/mob/inhands/equipment/medical_righthand.dmi and b/icons/mob/inhands/equipment/medical_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/hammers_lefthand.dmi b/icons/mob/inhands/weapons/hammers_lefthand.dmi
index 612066728a..023dfeed89 100644
Binary files a/icons/mob/inhands/weapons/hammers_lefthand.dmi and b/icons/mob/inhands/weapons/hammers_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/hammers_righthand.dmi b/icons/mob/inhands/weapons/hammers_righthand.dmi
index 9341cc8e4c..f87d8c9b46 100644
Binary files a/icons/mob/inhands/weapons/hammers_righthand.dmi and b/icons/mob/inhands/weapons/hammers_righthand.dmi differ
diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi
index 3916fcb694..36db68b7bb 100644
Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index f689ee5068..69b26e5cfc 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/dyespray.dmi b/icons/obj/dyespray.dmi
new file mode 100644
index 0000000000..eb05603679
Binary files /dev/null and b/icons/obj/dyespray.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index b468d0d4c9..39f1845f43 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/icons/obj/items_cyborg.dmi b/icons/obj/items_cyborg.dmi
index 769d5492c6..5d87bee320 100644
Binary files a/icons/obj/items_cyborg.dmi and b/icons/obj/items_cyborg.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index b4f223e57d..8a17a8f448 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index 52172bbe29..58ba5fadd8 100755
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ
diff --git a/icons/obj/syringe.dmi b/icons/obj/syringe.dmi
index b474dc68ba..6d0c779e92 100644
Binary files a/icons/obj/syringe.dmi and b/icons/obj/syringe.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index d6a59998a4..408ca3ae54 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -178,8 +178,8 @@
#include "code\__HELPERS\icon_smoothing.dm"
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\level_traits.dm"
-#include "code\__HELPERS\markov.dm"
#include "code\__HELPERS\lighting.dm"
+#include "code\__HELPERS\markov.dm"
#include "code\__HELPERS\matrices.dm"
#include "code\__HELPERS\mobs.dm"
#include "code\__HELPERS\mouse_control.dm"
@@ -1101,6 +1101,7 @@
#include "code\game\objects\items\dna_injector.dm"
#include "code\game\objects\items\documents.dm"
#include "code\game\objects\items\dualsaber.dm"
+#include "code\game\objects\items\dyekit.dm"
#include "code\game\objects\items\eightball.dm"
#include "code\game\objects\items\electrostaff.dm"
#include "code\game\objects\items\extinguisher.dm"
diff --git a/tools/requirements.txt b/tools/requirements.txt
index 81b49fedfd..a9abe5e826 100644
--- a/tools/requirements.txt
+++ b/tools/requirements.txt
@@ -1,6 +1,6 @@
pygit2==1.0.1
bidict==0.13.1
-Pillow==8.2.0
+Pillow==8.3.2
# changelogs
PyYaml==5.4