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/_maps/RandomRuins/LavaRuins/lavaland_surface_library.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_library.dmm index d1dcf9f11d..829761b042 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_library.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_library.dmm @@ -64,10 +64,6 @@ /obj/item/paper/fluff/ruins/oldstation/protosing, /turf/open/floor/plating/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/buried_library) -"av" = ( -/obj/structure/bookcase/random, -/turf/open/space/basic, -/area/ruin/unpowered/buried_library) "aw" = ( /turf/open/floor/plating, /area/ruin/unpowered/buried_library) @@ -220,7 +216,6 @@ /area/ruin/unpowered/buried_library) "bq" = ( /obj/structure/mineral_door/wood, -/obj/structure/barricade/wooden/crude/snow, /turf/open/floor/wood, /area/ruin/unpowered/buried_library) "br" = ( @@ -369,7 +364,7 @@ bi aC ae aQ -aq +ah bi aq bg @@ -394,7 +389,7 @@ ae bi aM ae -aq +ah bm aq aA @@ -440,7 +435,7 @@ aa ad bi bi -av +aq aA bi aG @@ -465,7 +460,7 @@ aa ad ac as -av +aq aF aD aD @@ -550,7 +545,7 @@ aD bi ae bm -ah +aq ag br ac @@ -573,9 +568,9 @@ ae af bp bi -ah +aq bi -ah +aq bp ac ad @@ -596,9 +591,9 @@ ah ao ah az -ah +aq bi -ah +aq bi bn ao @@ -621,7 +616,7 @@ ah aA ah ao -ah +aq bi bi bl @@ -646,7 +641,7 @@ ah ao aV bi -ah +aq bp bi bm diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_mining_site.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_mining_site.dmm index b4f7fec16f..dfe6a1b335 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_mining_site.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_mining_site.dmm @@ -53,8 +53,8 @@ /turf/open/floor/wood, /area/ruin/unpowered) "m" = ( -/obj/structure/barricade/wooden/crude/snow, /obj/structure/mineral_door/wood, +/obj/effect/decal/cleanable/trail_holder, /turf/open/floor/wood, /area/ruin/unpowered) "n" = ( @@ -68,6 +68,7 @@ /obj/machinery/light/broken{ dir = 4 }, +/obj/effect/decal/cleanable/trail_holder, /turf/open/floor/wood, /area/ruin/unpowered) "q" = ( @@ -95,6 +96,10 @@ /obj/effect/decal/cleanable/trail_holder, /turf/open/floor/wood, /area/ruin/unpowered) +"N" = ( +/obj/effect/decal/cleanable/trail_holder, +/turf/open/floor/plating/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) (1,1,1) = {" i @@ -569,7 +574,7 @@ b l f f -f +s s b b @@ -595,7 +600,7 @@ b b b f -f +s p b b @@ -622,7 +627,7 @@ i b b b -f +s b b b @@ -649,7 +654,7 @@ i i i b -f +s b i i @@ -676,7 +681,7 @@ i i i b -f +s b i i @@ -703,7 +708,7 @@ i i i b -f +s b i i @@ -757,7 +762,7 @@ i i n h -n +N h n i diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 536d5f4aa3..acd2b23ba0 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -54548,8 +54548,8 @@ id = "xenoigniter"; luminosity = 2 }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 8 +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 4 }, /turf/open/floor/engine, /area/science/xenobiology) @@ -55484,7 +55484,7 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/manifold/supply/hidden, +/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden, /turf/open/floor/plasteel/dark, /area/science/xenobiology) "hVx" = ( @@ -55906,6 +55906,7 @@ id = "xenoigniter"; luminosity = 2 }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/engine, /area/science/xenobiology) "iPz" = ( @@ -59460,6 +59461,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/engine, /area/science/xenobiology) "qAx" = ( @@ -60664,6 +60666,7 @@ /area/maintenance/department/science) "tfP" = ( /obj/item/beacon, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/engine, /area/science/xenobiology) "tfZ" = ( @@ -114714,13 +114717,13 @@ abI aby aaa xKc -blX +lCN qAk iPj tfP geN qAk -blX +uzB xKc aaa aby diff --git a/auxmos.dll b/auxmos.dll index 54fb0c6c98..19e019e568 100644 Binary files a/auxmos.dll and b/auxmos.dll differ diff --git a/auxmos.pdb b/auxmos.pdb index 031e66d103..49269317d4 100644 Binary files a/auxmos.pdb and b/auxmos.pdb differ diff --git a/code/__DEFINES/_flags/obj_flags.dm b/code/__DEFINES/_flags/obj_flags.dm index e48146a1d0..ac772c6e8e 100644 --- a/code/__DEFINES/_flags/obj_flags.dm +++ b/code/__DEFINES/_flags/obj_flags.dm @@ -15,6 +15,7 @@ #define BLOCK_Z_IN_UP (1<<12) // Should this object block z uprise from below? #define SHOVABLE_ONTO (1<<13)//called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. #define EXAMINE_SKIP (1<<14) /// Makes the Examine proc not read out this item. +#define IN_STORAGE (1<<15) //is this item in the storage item, such as backpack? used for tooltips /// Integrity defines for clothing (not flags but close enough) #define CLOTHING_PRISTINE 0 // We have no damage on the clothing diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 67d16e6687..1c6b7c21d4 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -155,10 +155,10 @@ //LAVALAND #define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland -#define LAVALAND_DEFAULT_ATMOS "o2=14;n2=23;TEMP=300" +#define LAVALAND_DEFAULT_ATMOS "LAVALAND_ATMOS" //SNOSTATION -#define ICEMOON_DEFAULT_ATMOS "o2=17;n2=63;TEMP=180" +#define ICEMOON_DEFAULT_ATMOS "ICEMOON_ATMOS" //ATMOSIA GAS MONITOR TAGS #define ATMOS_GAS_MONITOR_INPUT_O2 "o2_in" diff --git a/code/__DEFINES/footsteps.dm b/code/__DEFINES/footsteps.dm index 215e287adf..cc0c6547b0 100644 --- a/code/__DEFINES/footsteps.dm +++ b/code/__DEFINES/footsteps.dm @@ -8,6 +8,7 @@ #define FOOTSTEP_LAVA "lava" #define FOOTSTEP_MEAT "meat" #define FOOTSTEP_RUST "rust" +#define FOOTSTEP_CATWALK "catwalk" //barefoot sounds #define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot" @@ -94,6 +95,12 @@ GLOBAL_LIST_INIT(footstep, list( 'sound/effects/footstep/lava3.ogg'), 100, 0), FOOTSTEP_MEAT = list(list( 'sound/effects/meatslap.ogg'), 100, 0), + FOOTSTEP_CATWALK = list(list( + 'sound/effects/footstep/catwalk1.ogg', + 'sound/effects/footstep/catwalk2.ogg', + 'sound/effects/footstep/catwalk3.ogg', + 'sound/effects/footstep/catwalk4.ogg', + 'sound/effects/footstep/catwalk5.ogg'), 100, 1), FOOTSTEP_RUST = list(list( 'sound/effects/footstep/rustystep1.ogg'), 100, 0) )) diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm index ea7b1b2e0a..c7be89721e 100644 --- a/code/__DEFINES/layers_planes.dm +++ b/code/__DEFINES/layers_planes.dm @@ -64,8 +64,9 @@ #define GAS_PIPE_VISIBLE_LAYER 2.47 #define GAS_FILTER_LAYER 2.48 #define GAS_PUMP_LAYER 2.49 - #define LOW_OBJ_LAYER 2.5 +///catwalk overlay of /turf/open/floor/plating/plating_catwalk +#define CATWALK_LAYER 2.51 #define LOW_SIGIL_LAYER 2.52 #define SIGIL_LAYER 2.54 #define HIGH_SIGIL_LAYER 2.56 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 850a2b0f39..6a63ced634 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -347,3 +347,7 @@ // / Breathing types. Lungs can access either by these or by a string, which will be considered a gas ID. #define BREATH_OXY /datum/breathing_class/oxygen #define BREATH_PLASMA /datum/breathing_class/plasma + +//Gremlins +#define NPC_TAMPER_ACT_FORGET 1 //Don't try to tamper with this again +#define NPC_TAMPER_ACT_NOMSG 2 //Don't produce a visible message 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/__HELPERS/markov.dm b/code/__HELPERS/markov.dm new file mode 100644 index 0000000000..7e195f8b9c --- /dev/null +++ b/code/__HELPERS/markov.dm @@ -0,0 +1,61 @@ +#define MAXIMUM_MARKOV_LENGTH 25000 + +/proc/markov_chain(var/text, var/order = 4, var/length = 250) + if(!text || order < 0 || order > 20 || length < 1 || length > MAXIMUM_MARKOV_LENGTH) + return + + var/table = markov_table(text, order) + var/markov = markov_text(length, table, order) + return markov + +/proc/markov_table(var/text, var/look_forward = 4) + if(!text) + return + var/list/table = list() + + for(var/i = 1, i <= length(text), i++) + var/char = copytext(text, i, look_forward+i) + if(!table[char]) + table[char] = list() + + for(var/i = 1, i <= (length(text) - look_forward), i++) + var/char_index = copytext(text, i, look_forward+i) + var/char_count = copytext(text, i+look_forward, (look_forward*2)+i) + + if(table[char_index][char_count]) + table[char_index][char_count]++ + else + table[char_index][char_count] = 1 + + return table + +/proc/markov_text(var/length = 250, var/table, var/look_forward = 4) + if(!table) + return + var/char = pick(table) + var/o = char + + for(var/i = 0, i <= (length / look_forward), i++) + var/newchar = markov_weighted_char(table[char]) + + if(newchar) + char = newchar + o += "[newchar]" + else + char = pick(table) + + return o + +/proc/markov_weighted_char(var/list/array) + if(!array || !array.len) + return + + var/total = 0 + for(var/i in array) + total += array[i] + var/r = rand(1, total) + for(var/i in array) + var/weight = array[i] + if(r <= weight) + return i + r -= weight diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 781fa38b94..c8e5b72f26 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(bitfields, list( "DROPDEL" = DROPDEL, "NOBLUDGEON" = NOBLUDGEON, "ABSTRACT" = ABSTRACT, + "IN_STORAGE" = IN_STORAGE, "ITEM_CAN_BLOCK" = ITEM_CAN_BLOCK, "ITEM_CAN_PARRY" = ITEM_CAN_PARRY ), 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/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index daa4f5879d..b41b7356a8 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -112,7 +112,6 @@ GLOBAL_LIST_INIT(maintenance_loot, list( /obj/item/storage/box/marshmallow = 2, /obj/item/clothing/gloves/tackler/offbrand = 1, /obj/item/stack/sticky_tape = 1, - /obj/effect/spawner/lootdrop/grille_or_trash = 15, "" = 3 )) 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/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index bfbdce41fd..56db868a21 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -141,11 +141,29 @@ /atom/movable/screen/inventory/MouseEntered() ..() add_overlays() + //Apply the outline affect + add_stored_outline() /atom/movable/screen/inventory/MouseExited() ..() cut_overlay(object_overlays) object_overlays.Cut() + remove_stored_outline() + +/atom/movable/screen/inventory/proc/add_stored_outline() + if(hud?.mymob && slot_id) + var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id) + if(inv_item) + if(hud?.mymob.incapacitated()) + inv_item.apply_outline(COLOR_RED_GRAY) + else + inv_item.apply_outline() + +/atom/movable/screen/inventory/proc/remove_stored_outline() + if(hud?.mymob && slot_id) + var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id) + if(inv_item) + inv_item.remove_outline() /atom/movable/screen/inventory/update_icon_state() if(!icon_empty) diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 583cf98259..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() @@ -37,7 +38,8 @@ SUBSYSTEM_DEF(air) //atmos singletons var/list/gas_reactions = list() - + var/list/atmos_gen + var/list/planetary = list() //auxmos already caches static planetary mixes but could be convenient to do so here too //Special functions lists var/list/turf/open/high_pressure_delta = list() @@ -53,14 +55,24 @@ 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 /datum/controller/subsystem/air/stat_entry(msg) msg += "C:{" @@ -218,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)) @@ -275,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-- @@ -375,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() @@ -399,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) @@ -473,6 +505,20 @@ SUBSYSTEM_DEF(air) return pipe_init_dirs_cache[type]["[dir]"] +/datum/controller/subsystem/air/proc/generate_atmos() + atmos_gen = list() + for(var/T in subtypesof(/datum/atmosphere)) + var/datum/atmosphere/atmostype = T + atmos_gen[initial(atmostype.id)] = new atmostype + +/datum/controller/subsystem/air/proc/preprocess_gas_string(gas_string) + if(!atmos_gen) + generate_atmos() + if(!atmos_gen[gas_string]) + return gas_string + var/datum/atmosphere/mix = atmos_gen[gas_string] + return mix.gas_string + #undef SSAIR_PIPENETS #undef SSAIR_ATMOSMACHINERY #undef SSAIR_EXCITEDGROUPS diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 2474a6f272..1153088a91 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -68,7 +68,7 @@ SUBSYSTEM_DEF(vote) //get the highest number of votes var/greatest_votes = 0 var/total_votes = 0 - if(mode == "gamemode" && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) + if((mode == "gamemode" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) for(var/mob/dead/new_player/P in GLOB.player_list) if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey]) choices[choices[voted[P.ckey]]]-- @@ -105,7 +105,7 @@ SUBSYSTEM_DEF(vote) /datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text) // https://en.wikipedia.org/wiki/Schulze_method#Implementation - if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) + if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) for(var/mob/dead/new_player/P in GLOB.player_list) if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey]) voted -= P.ckey @@ -155,7 +155,7 @@ SUBSYSTEM_DEF(vote) for(var/choice in choices) scores_by_choice += "[choice]" scores_by_choice["[choice]"] = list() - if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) + if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode)) for(var/mob/dead/new_player/P in GLOB.player_list) if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey]) voted -= P.ckey diff --git a/code/datums/atmosphere/_atmosphere.dm b/code/datums/atmosphere/_atmosphere.dm new file mode 100644 index 0000000000..5323b070cd --- /dev/null +++ b/code/datums/atmosphere/_atmosphere.dm @@ -0,0 +1,60 @@ +/datum/atmosphere + var/gas_string + var/id + + var/list/base_gases // A list of gases to always have + var/list/normal_gases // A list of allowed gases:base_amount + var/list/restricted_gases // A list of allowed gases like normal_gases but each can only be selected a maximum of one time + var/restricted_chance = 10 // Chance per iteration to take from restricted gases + + var/minimum_pressure + var/maximum_pressure + + var/minimum_temp + var/maximum_temp + +/datum/atmosphere/New() + generate_gas_string() + +/datum/atmosphere/proc/generate_gas_string() + var/list/spicy_gas = restricted_gases.Copy() + var/target_pressure = rand(minimum_pressure, maximum_pressure) + var/pressure_scale = target_pressure / maximum_pressure + + // First let's set up the gasmix and base gases for this template + // We make the string from a gasmix in this proc because gases need to calculate their pressure + var/datum/gas_mixture/gasmix = new + gasmix.set_temperature(rand(minimum_temp, maximum_temp)) + for(var/i in base_gases) + gasmix.set_moles(i, base_gases[i]) + + // Now let the random choices begin + var/gastype + var/amount + while(gasmix.return_pressure() < target_pressure) + if(!prob(restricted_chance) || !length(spicy_gas)) + gastype = pick(normal_gases) + amount = normal_gases[gastype] + else + gastype = pick(spicy_gas) + amount = spicy_gas[gastype] + spicy_gas -= gastype //You can only pick each restricted gas once + + amount *= rand(50, 200) / 100 // Randomly modifes the amount from half to double the base for some variety + amount *= pressure_scale // If we pick a really small target pressure we want roughly the same mix but less of it all + amount = CEILING(amount, 0.1) + + gasmix.adjust_moles(gastype, amount) + + // That last one put us over the limit, remove some of it + if(gasmix.return_pressure() > target_pressure) + var/moles_to_remove = (1 - target_pressure / gasmix.return_pressure()) * gasmix.total_moles() + gasmix.adjust_moles(gastype, -moles_to_remove) + gasmix.set_moles(gastype, FLOOR(gasmix.get_moles(gastype), 0.1)) + + // Now finally lets make that string + var/list/gas_string_builder = list() + for(var/id in gasmix.get_gases()) + gas_string_builder += "[id]=[gasmix.get_moles(id)]" + gas_string_builder += "TEMP=[gasmix.return_temperature()]" + gas_string = gas_string_builder.Join(";") diff --git a/code/datums/atmosphere/planetary.dm b/code/datums/atmosphere/planetary.dm new file mode 100644 index 0000000000..a8b77f7394 --- /dev/null +++ b/code/datums/atmosphere/planetary.dm @@ -0,0 +1,48 @@ +// Atmos types used for planetary airs +/datum/atmosphere/lavaland + id = LAVALAND_DEFAULT_ATMOS + + base_gases = list( + GAS_O2=5, + GAS_N2=10, + ) + normal_gases = list( + GAS_O2=10, + GAS_N2=10, + GAS_CO2=10, + ) + restricted_gases = list( + GAS_BZ=0.1, + GAS_METHYL_BROMIDE=0.1, + ) + restricted_chance = 30 + + minimum_pressure = HAZARD_LOW_PRESSURE + 10 + maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1 + + minimum_temp = BODYTEMP_COLD_DAMAGE_LIMIT + 1 + maximum_temp = 320 + +/datum/atmosphere/icemoon + id = ICEMOON_DEFAULT_ATMOS + + base_gases = list( + GAS_O2=5, + GAS_N2=10, + ) + normal_gases = list( + GAS_O2=10, + GAS_N2=10, + GAS_CO2=10, + ) + restricted_gases = list( + GAS_METHYL_BROMIDE=0.1, + ) + restricted_chance = 10 + + minimum_pressure = HAZARD_LOW_PRESSURE + 10 + maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1 + + minimum_temp = 180 + maximum_temp = 180 + diff --git a/code/datums/components/storage/concrete/_concrete.dm b/code/datums/components/storage/concrete/_concrete.dm index 55c3e1d083..bb226bbfdf 100644 --- a/code/datums/components/storage/concrete/_concrete.dm +++ b/code/datums/components/storage/concrete/_concrete.dm @@ -135,6 +135,8 @@ var/obj/item/I = AM var/mob/M = parent.loc I.dropped(M) + I.item_flags &= ~IN_STORAGE + I.remove_outline() if(new_location) AM.forceMove(new_location) // exited comsig will handle removal reset. //We don't want to call this if the item is being destroyed @@ -180,6 +182,7 @@ I.forceMove(parent.drop_location()) return FALSE I.on_enter_storage(master) + I.item_flags |= IN_STORAGE refresh_mob_views() I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt if(M) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 752fafe6bd..97d6748dc7 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -423,6 +423,9 @@ var/atom/A = parent if(ismob(M)) //all the check for item manipulation are in other places, you can safely open any storages as anything and its not buggy, i checked A.add_fingerprint(M) + if(istype(A, /obj/item)) + var/obj/item/I = A + I.remove_outline() //Removes the outline when we drag if(!over_object) return FALSE if(ismecha(M.loc)) // stops inventory actions in a mech diff --git a/code/datums/components/waddling.dm b/code/datums/components/waddling.dm index f09a92c91c..7b94a14285 100644 --- a/code/datums/components/waddling.dm +++ b/code/datums/components/waddling.dm @@ -10,6 +10,7 @@ var/mob/living/L = parent if(L.incapacitated() || L.lying) return + var/matrix/otransform = matrix(L.transform) //make a copy of the current transform animate(L, pixel_z = 4, time = 0) - animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2) - animate(pixel_z = 0, transform = matrix(), time = 0) + animate(pixel_z = 0, transform = turn(L.transform, pick(-12, 0, 12)), time=2) //waddle. + animate(pixel_z = 0, transform = otransform, time = 0) //return to previous transform. diff --git a/code/datums/dna.dm b/code/datums/dna.dm index a2982a3caf..43dad6cef8 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -386,6 +386,14 @@ qdel(language_holder) var/species_holder = initial(mrace.species_language_holder) language_holder = new species_holder(src) + + var/mob/living/carbon/human/H = src + //provide the user's additional language to the new language holder even if they change species + if(H.additional_language && H.additional_language != "None") + var/language_entry = GLOB.roundstart_languages[H.additional_language] + if(language_entry) + grant_language(language_entry, TRUE, TRUE) + update_atom_languages() /mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE) 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/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 81f99cfa69..abee5ada06 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -329,3 +329,15 @@ to_chat(L, "You need an attachable assembly!") #undef MAXIMUM_EMP_WIRES + +//gremlins +/datum/wires/proc/npc_tamper(mob/living/L) + if(!wires.len) + return + + var/wire_to_screw = pick(wires) + + if(is_color_cut(wire_to_screw) || prob(50)) //CutWireColour() proc handles both cutting and mending wires. If the wire is already cut, always mend it back. Otherwise, 50% to cut it and 50% to pulse it + cut(wire_to_screw) + else + pulse(wire_to_screw, L) 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/machinery/computer/arcade/minesweeper.dm b/code/game/machinery/computer/arcade/minesweeper.dm index dbd9890c81..cd6ff2359c 100644 --- a/code/game/machinery/computer/arcade/minesweeper.dm +++ b/code/game/machinery/computer/arcade/minesweeper.dm @@ -191,6 +191,7 @@ var/x2 = x1 work_squares(y2, x2) //Work squares while in this loop so there's less load reset_board = FALSE + CHECK_TICK web += "
| [MINESWEEPERIMG(7)] | " if(19) web += "[MINESWEEPERIMG(8)] | " + CHECK_TICK web += "" web += "
"
dat += "General Settings" dat += "UI Style: [UI_style]" + dat += "Outline: [outline_enabled ? "Enabled" : "Disabled"] " + dat += "Outline Color: Change " dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"] " dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"] " dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"] " @@ -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 diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index d4d13dc40f..c17bc16551 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -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) + 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) 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 e96883af4c..90fd0b2812 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/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm index 6b0a093440..78b5c9e70c 100644 --- a/code/modules/events/supermatter_surge.dm +++ b/code/modules/events/supermatter_surge.dm @@ -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) 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/dead/new_player/sprite_accessories/synthliz.dm b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm index c7cd699ec9..08897ade34 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm @@ -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 diff --git a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm index b5ba7f0732..64a3c1a24c 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm @@ -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" diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 429f00161b..1aa2863eda 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index de934318b3..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" @@ -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 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/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 8348af43fa..340d2e5598 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -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("[src] devours the food! He rots into something worse!") + 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() diff --git a/code/modules/mob/living/simple_animal/gremlin/gremlin.dm b/code/modules/mob/living/simple_animal/gremlin/gremlin.dm new file mode 100644 index 0000000000..61436cea1c --- /dev/null +++ b/code/modules/mob/living/simple_animal/gremlin/gremlin.dm @@ -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("[src] hungrily devours [target]!") + 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( + "\The [src] plays around with \the [M], but finds it rather boring.", + "\The [src] tries to think of some more ways to screw \the [M] up, but fails miserably.", + "\The [src] decides to ignore \the [M], and starts looking for something more fun.")) + + 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( + "\The [src]'s eyes light up as \he tampers with \the [M].", + "\The [src] twists some knobs around on \the [M] and bursts into laughter!", + "\The [src] presses a few buttons on \the [M] and giggles mischievously.", + "\The [src] rubs its hands devilishly and starts messing with \the [M].", + "\The [src] turns a small valve on \the [M].")) + + //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("[src] crawls into the ventilation ducts!") + + 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("[src] climbs out of the ventilation ducts!") + 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("[src] hungrily devours [target]!", "You hungrily devour [target]!") + 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("\The [src] splits into two!") + 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 diff --git a/code/modules/mob/living/simple_animal/gremlin/gremlin_act.dm b/code/modules/mob/living/simple_animal/gremlin/gremlin_act.dm new file mode 100644 index 0000000000..ff0eb13dc7 --- /dev/null +++ b/code/modules/mob/living/simple_animal/gremlin/gremlin_act.dm @@ -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("\The [L] climbs into \the [src] and turns the faucet on!") + + var/mob/living/simple_animal/hostile/gremlin/G = L + G.divide() + + return NPC_TAMPER_ACT_NOMSG diff --git a/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm b/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm new file mode 100644 index 0000000000..6f5f0e3dba --- /dev/null +++ b/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm @@ -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 diff --git a/code/modules/mob/living/simple_animal/hostile/plaguerat.dm b/code/modules/mob/living/simple_animal/hostile/plaguerat.dm new file mode 100644 index 0000000000..331dc1eb75 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/plaguerat.dm @@ -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("[src] chews through the [C]. It looks unharmed!") + 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("[src] gnaws into its food, [cap] rats are now on the station!") + return + var/mob/living/newmouse = new /mob/living/simple_animal/hostile/plaguerat(loc) + SSmobs.cheeserats += newmouse + visible_message("[src] gnaws into its food, attracting another rat!") + +/** + *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, "Excellent, you find more trash to spread your filth!") + new /obj/effect/decal/cleanable/dirt(T) + new pickedtrash(T) + if(4 to 6) + to_chat(owner, "You find blood and gibs to feed your young!") + new /obj/effect/decal/cleanable/blood/gibs(T) + new /obj/effect/decal/cleanable/blood/(T) + if(7 to 18) + to_chat(owner, "A corpse rises from the ground. Best to leave it alone.") + new /obj/effect/mob_spawn/human/corpse/assistant(T) + if(19 to 100) + to_chat(owner, "Drat. Nothing.") + new /obj/effect/decal/cleanable/dirt(T) + StartCooldown() diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index 5722cfda07..39e9e84ed2 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -109,7 +109,7 @@ continue playsound(src, 'sound/effects/splat.ogg', 50, TRUE) visible_message("[src] vomits up [consumed_mob]!") - 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, "You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!") 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) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index efb4512bca..5ba4769f99 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -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) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index b460cfea51..148535af76 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -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) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 0f5934e8fd..34e9247184 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -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, "[src] is now set to [C.select_name || C].") post_set_firemode() - update_icon(TRUE) + update_appearance() /obj/item/gun/energy/proc/post_set_firemode(recharge_newshot = TRUE) if(recharge_newshot) 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/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index 0c34a251ae..c408ad096e 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -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, "[linked_extract] discharges some energy into a device you have.") return ..() 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/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 93f88192e5..e10b05822b 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -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" diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index ca1b163968..65e44cfbc6 100644 --- a/code/modules/tooltip/tooltip.dm +++ b/code/modules/tooltip/tooltip.dm @@ -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(" ") 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 00188ead51..4eee5b8770 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,111 @@ -->
+
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 9790591c1c..4ab3186cff 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -29848,3 +29848,72 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
BlueWildrose:
- balance: MK ultra explosions (failures at making MKultra) are gone, and replaced
with a gas that just causes a bunch of status effects to you.
+2021-08-26:
+ keronshb:
+ - rscadd: Adds catwalk floors
+2021-08-28:
+ DeltaFire15:
+ - bugfix: 'Demons now drop bodies on their own tile instead of scattering them across
+ the station. tweak: Space dragon content ejection is now slightly safer.'
+ - bugfix: Space dragons no longer delete their contents if they die due to timeout,
+ ejecting them instead.
+ - bugfix: Carp rifts created by space dragons now have their armor work correctly.
+ Putnam3145:
+ - config: Planetary monstermos can now be disabled with varedit
+ - rscadd: Lavaland/ice planet atmos is no longer a preset gas mixture and varies
+ per round
+ keronshb:
+ - rscadd: Ports Inventory Outlines
+ - imageadd: Re-adds the old glue sprite
+ - rscadd: Adds Plague Rats
+ - rscadd: Gives Plague Rat spawn conditions for regular mice
+ - imageadd: Plague Rat sprite
+ - rscadd: Gremlin
+ - imageadd: Gremlin sprites
+ zeroisthebiggay:
+ - rscdel: grilles as maintenance loot
+ - rscadd: sevensune tail from hyperstation
+2021-09-01:
+ BlueWildrose:
+ - bugfix: The waddle component now takes size into account when running rotating
+ animations, thus not reverting character sizes to default size.
+ ma44:
+ - bugfix: Weapon rechargers now have their recharger indicators again.
+ - bugfix: Energy guns now update when switching modes again.
+ - bugfix: Stabilized yellow slime extracts will now update cells and guns it recharges.
+ - rscadd: Weapon rechargers will now be more noticeable when it has finished recharging
+ something.
+ zeroisthebiggay:
+ - bugfix: the fucking chainsaw sprite
+2021-09-03:
+ timothyteakettle:
+ - bugfix: fixes losing your additional language upon changing species
+2021-09-04:
+ Putnam3145:
+ - bugfix: Might've fixed some ghost sprite oddities nobody even knew about
+ WanderingFox95:
+ - bugfix: Lavaland architects don't screw up so badly anymore and do in fact not
+ somehow leave holes through the planet surface all the way into space under
+ their bookshelves.
+ - bugfix: Snow was removed from Lavaland Ruins.
+ keronshb:
+ - bugfix: Fixes entering an occupied VR sleeper bug
+ timothyteakettle:
+ - bugfix: makes the arm/leg markings for synthetic lizards appear as an option again
+2021-09-05:
+ DeltaFire15:
+ - bugfix: Unreadied player gamemode votes now actually get ignored (if the config
+ for this is enabled).
+2021-09-07:
+ bunny232:
+ - bugfix: fixed some jank in pubby's xenobiological secure pen
+ keronshb:
+ - 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/animal.dmi b/icons/mob/animal.dmi
index 68e6170969..34bd587146 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.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/chainsaw_lefthand.dmi b/icons/mob/inhands/weapons/chainsaw_lefthand.dmi
index 3e0aea0e6b..3053fea723 100644
Binary files a/icons/mob/inhands/weapons/chainsaw_lefthand.dmi and b/icons/mob/inhands/weapons/chainsaw_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/chainsaw_righthand.dmi b/icons/mob/inhands/weapons/chainsaw_righthand.dmi
index 0800a52731..4e76c19b7a 100644
Binary files a/icons/mob/inhands/weapons/chainsaw_righthand.dmi and b/icons/mob/inhands/weapons/chainsaw_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/mob/mob.dmi b/icons/mob/mob.dmi
index 3137e8ac26..65efde8697 100644
Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.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/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/stationobjs.dmi b/icons/obj/stationobjs.dmi
index a4fbe53dba..7e04fa0d2a 100644
Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.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/icons/obj/tiles.dmi b/icons/obj/tiles.dmi
index 088b6c2c7b..757208cd9b 100644
Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ
diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi
index a720aff62c..95a887a733 100644
Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ
diff --git a/icons/turf/floors/catwalk_plating.dmi b/icons/turf/floors/catwalk_plating.dmi
new file mode 100644
index 0000000000..24954e4a17
Binary files /dev/null and b/icons/turf/floors/catwalk_plating.dmi differ
diff --git a/modular_citadel/icons/mob/mam_tails.dmi b/modular_citadel/icons/mob/mam_tails.dmi
index c1ffced3d5..f30f7480f6 100644
Binary files a/modular_citadel/icons/mob/mam_tails.dmi and b/modular_citadel/icons/mob/mam_tails.dmi differ
diff --git a/strings/tips.txt b/strings/tips.txt
index d4373b3606..5c6228769e 100644
--- a/strings/tips.txt
+++ b/strings/tips.txt
@@ -136,7 +136,7 @@ As a Security Officer, your security HUDglasses can not only see crewmates' job
As a Security Officer, mindshield implants can only prevent someone from being turned into a cultist: unlike revolutionaries, it will not de-cult them if they have already been converted.
As a Security Officer, examining someone while wearing your security HUDglasses can allow you to swiftly edit their records and criminal status. Be sure to set someone to WANTED if you can't catch up to them, as it'll alert other officers of who's the bad guy, and cause the little security droids to chase after them for you.
As a Security Officer, you can take out the power cell on your baton to replace it with a better or fully charged one. Just use a screwdriver on your baton to remove the old cell.
-As a Security Officer, you can just about any firearm on your vest, this even works with other non-standard armor-substitutes like security winter coats!
+As a Security Officer, you can place just about any firearm on your vest slot, and this even works with other non-standard armor-substitutes like security winter coats!
As the Detective, people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department.
As the Detective, you can use your forensics scanner from a distance. Use this to scan boxes or other storage containers.
As the Detective, your revolver can be loaded with .357 ammunition. Use a screwdriver to permanently modify your revolver into using this type of ammunition, be warned however, firing it has a decent chance to cause the revolver to misfire and shoot you in the foot.
diff --git a/tgstation.dme b/tgstation.dme
index 65c5b7acab..408ca3ae54 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -179,6 +179,7 @@
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\level_traits.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"
@@ -462,6 +463,8 @@
#include "code\datums\achievements\skill_achievements.dm"
#include "code\datums\actions\beam_rifle.dm"
#include "code\datums\actions\ninja.dm"
+#include "code\datums\atmosphere\_atmosphere.dm"
+#include "code\datums\atmosphere\planetary.dm"
#include "code\datums\brain_damage\brain_trauma.dm"
#include "code\datums\brain_damage\hypnosis.dm"
#include "code\datums\brain_damage\imaginary_friend.dm"
@@ -1098,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"
@@ -1389,6 +1393,7 @@
#include "code\game\turfs\closed.dm"
#include "code\game\turfs\open.dm"
#include "code\game\turfs\turf.dm"
+#include "code\game\turfs\open\floor\catwalk_plating.dm"
#include "code\game\turfs\simulated\chasm.dm"
#include "code\game\turfs\simulated\dirtystation.dm"
#include "code\game\turfs\simulated\floor.dm"
@@ -2788,6 +2793,9 @@
#include "code\modules\mob\living\simple_animal\friendly\drone\say.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\verbs.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\visuals_icons.dm"
+#include "code\modules\mob\living\simple_animal\gremlin\gremlin.dm"
+#include "code\modules\mob\living\simple_animal\gremlin\gremlin_act.dm"
+#include "code\modules\mob\living\simple_animal\gremlin\gremlin_event.dm"
#include "code\modules\mob\living\simple_animal\guardian\guardian.dm"
#include "code\modules\mob\living\simple_animal\guardian\types\assassin.dm"
#include "code\modules\mob\living\simple_animal\guardian\types\charger.dm"
@@ -2824,6 +2832,7 @@
#include "code\modules\mob\living\simple_animal\hostile\nanotrasen.dm"
#include "code\modules\mob\living\simple_animal\hostile\netherworld.dm"
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
+#include "code\modules\mob\living\simple_animal\hostile\plaguerat.dm"
#include "code\modules\mob\living\simple_animal\hostile\regalrat.dm"
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
#include "code\modules\mob\living\simple_animal\hostile\sharks.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
08 September 2021+keronshb updated:+
07 September 2021+bunny232 updated:+
keronshb updated:+
zeroisthebiggay updated:+
05 September 2021+DeltaFire15 updated:+
04 September 2021+Putnam3145 updated:+
WanderingFox95 updated:+
keronshb updated:+
timothyteakettle updated:+
03 September 2021+timothyteakettle updated:+
01 September 2021+BlueWildrose updated:+
ma44 updated:+
zeroisthebiggay updated:+
28 August 2021+DeltaFire15 updated:+
Putnam3145 updated:+
keronshb updated:+
zeroisthebiggay updated:+
26 August 2021+keronshb updated:+
24 August 2021BlueWildrose updated:
07 July 2021-DeltaFire15 updated:-
05 July 2021-Putnam3145 updated:-
WanderingFox95 updated:-
zeroisthebiggay updated:-
04 July 2021-cadyn updated:-
03 July 2021-DeltaFire15 updated:-
Putnam3145 updated:-
WanderingFox95 updated:-
02 July 2021-silicons updated:-
30 June 2021-WanderingFox95 updated:-
bunny232 updated:-
qweq12yt updated:-
shellspeed1 updated:-
28 June 2021-Putnam3145 updated:-
WanderingFox95 updated:-
25 June 2021-MrJWhit updated:-
brokenOculus updated:-
24 June 2021-WanderingFox95 updated:-
|