From 252c96e8973dec9be9eec936a73fcfb8e7fd355f Mon Sep 17 00:00:00 2001 From: spartanbobby <71467726+spartanbobby@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:43:53 +0000 Subject: [PATCH 001/162] I change LV624 T-comms and engineering areas (#5674) # About the pull request Basically I think that the little grassy area inbetween them looks a bit shit and I wanted to kind of make the buildings two parts of the same building and make them look alittle nicer # Explain why it's good for the game I think it looks quite pretty and I've made some changes to the doorwars and stuff to make it easier to move thru the building such as removing some of the 1x1 doorways and and clearing the "halls" there was also some flowers on the outside of the building that kind of weirdly overlayed onto fences making them appear broken at first glance and I've moved those # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: SpartanBobby maptweak: changes to LV624 Engineering and T-comms, mostly detailing but changes some doors and object placement /:cl: --- maps/map_files/LV624/LV624.dmm | 1577 ++++++++++++++++++++++---------- 1 file changed, 1109 insertions(+), 468 deletions(-) diff --git a/maps/map_files/LV624/LV624.dmm b/maps/map_files/LV624/LV624.dmm index 5a0bb9dfc88d..cd28e7341917 100644 --- a/maps/map_files/LV624/LV624.dmm +++ b/maps/map_files/LV624/LV624.dmm @@ -6359,8 +6359,9 @@ }, /area/lv624/lazarus/sleep_male) "aCV" = ( +/obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/grass/grass1, -/area/lv624/lazarus/sleep_male) +/area/lv624/ground/jungle/central_jungle) "aCX" = ( /turf/open/floor/grass, /area/lv624/lazarus/main_hall) @@ -7934,11 +7935,11 @@ "aIE" = ( /obj/structure/flora/bush/ausbushes/var3/stalkybush, /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 11; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 11; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -9548,6 +9549,7 @@ /area/lv624/ground/caves/north_east_caves) "aPN" = ( /obj/structure/window_frame/colony/reinforced, +/obj/item/stack/rods, /turf/open/floor/plating, /area/lv624/lazarus/comms) "aPO" = ( @@ -9558,8 +9560,7 @@ req_one_access = null }, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "delivery" }, /area/lv624/lazarus/comms) "aPR" = ( @@ -9845,38 +9846,28 @@ "aRd" = ( /obj/structure/surface/table, /obj/item/device/mmi/radio_enabled, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, -/area/lv624/lazarus/comms) -"aRe" = ( -/obj/structure/surface/table, -/obj/item/device/flashlight, /obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) -"aRf" = ( -/obj/structure/surface/table, -/obj/item/device/radio/headset{ - frequency = 1469; - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/device/radio/headset{ - frequency = 1469 - }, -/obj/structure/machinery/light{ - dir = 1 +"aRe" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/power/monitor, +/obj/item/ashtray/glass{ + pixel_x = 3; + pixel_y = 15 }, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) +"aRf" = ( +/obj/structure/flora/bush/ausbushes/lavendergrass, +/turf/open/gm/grass/grass1, +/area/lv624/ground/jungle/west_central_jungle) "aRg" = ( /turf/open/floor/plating, /area/lv624/lazarus/landing_zones/lz2) @@ -9888,10 +9879,11 @@ }, /area/lv624/lazarus/chapel) "aRi" = ( -/obj/structure/surface/table, -/obj/item/ashtray/glass, -/obj/item/tool/crowbar, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, +/obj/structure/machinery/light{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/cm_vending/sorted/tech/electronics_storage, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -10120,8 +10112,14 @@ /turf/open/floor/plating, /area/lv624/lazarus/landing_zones/lz1) "aSd" = ( -/obj/structure/bed/chair/office/light{ - dir = 1 +/obj/structure/machinery/blackbox_recorder, +/obj/item/prop/almayer/flight_recorder/colony{ + pixel_x = -6; + pixel_y = 10 + }, +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 }, /turf/open/floor{ dir = 9; @@ -10129,8 +10127,12 @@ }, /area/lv624/lazarus/comms) "aSe" = ( -/obj/structure/surface/table, -/obj/item/device/multitool, +/obj/structure/machinery/cm_vending/sorted/tech/comp_storage, +/obj/effect/decal/warning_stripes{ + icon_state = "E"; + pixel_x = 1 + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -10270,30 +10272,24 @@ }, /area/lv624/lazarus/canteen) "aSI" = ( -/obj/structure/machinery/computer/telecomms/server, +/obj/structure/machinery/light{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) "aSJ" = ( -/obj/item/device/radio/intercom{ - freerange = 1; - frequency = 1469; - name = "General Listening Channel"; - pixel_x = -30 - }, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "podhatchfloor" }, /area/lv624/lazarus/comms) "aSK" = ( -/obj/structure/machinery/computer/telecomms/traffic, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, +/obj/structure/window_frame/colony/reinforced, +/obj/item/shard, +/turf/open/floor/plating, /area/lv624/lazarus/comms) "aSL" = ( /turf/closed/wall/r_wall, @@ -10382,25 +10378,25 @@ "aTg" = ( /turf/closed/wall, /area/lv624/lazarus/comms) -"aTh" = ( -/obj/structure/reagent_dispensers/watertank, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, -/area/lv624/lazarus/comms) "aTi" = ( -/obj/structure/reagent_dispensers/fueltank, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) "aTj" = ( -/obj/structure/machinery/blackbox_recorder, -/obj/item/prop/almayer/flight_recorder/colony{ - pixel_x = -6; - pixel_y = 10 +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 + }, +/obj/structure/machinery/computer/telecomms/traffic{ + layer = 3.1; + pixel_y = 16 + }, +/obj/structure/bed/chair/office/light{ + dir = 1; + layer = 3.2 }, /turf/open/floor{ dir = 9; @@ -10410,8 +10406,7 @@ "aTk" = ( /obj/effect/landmark/static_comms/net_one, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "podhatchfloor" }, /area/lv624/lazarus/comms) "aTo" = ( @@ -10446,6 +10441,11 @@ /obj/structure/machinery/colony_floodlight_switch{ pixel_y = 30 }, +/obj/item/device/assembly/voice, +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 + }, /turf/open/floor{ icon_state = "dark" }, @@ -10537,7 +10537,6 @@ /obj/structure/machinery/power/apc{ dir = 1; name = "Telecomms APC"; - pixel_y = 30; start_charge = 15 }, /turf/open/floor{ @@ -10545,7 +10544,12 @@ }, /area/lv624/lazarus/comms) "aTK" = ( -/turf/open/gm/dirt, +/obj/structure/surface/rack, +/obj/item/device/multitool, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, /area/lv624/lazarus/comms) "aTM" = ( /turf/open/floor{ @@ -10553,6 +10557,10 @@ }, /area/lv624/lazarus/engineering) "aTN" = ( +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, /turf/open/floor/plating{ icon_state = "warnplate" }, @@ -10679,22 +10687,18 @@ req_one_access = null }, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "delivery" }, /area/lv624/lazarus/comms) "aUl" = ( -/obj/item/device/multitool, -/obj/structure/surface/rack, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, -/area/lv624/lazarus/comms) +/turf/open/gm/dirtgrassborder/south, +/area/lv624/ground/colony/north_tcomms_road) "aUo" = ( /obj/effect/spawner/random/toolbox, /obj/structure/surface/table/reinforced/prison, /obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, +/obj/effect/decal/cleanable/dirt, +/obj/structure/foamed_metal, /turf/open/floor{ icon_state = "dark" }, @@ -10711,17 +10715,18 @@ }, /area/lv624/lazarus/engineering) "aUt" = ( -/obj/structure/surface/table/reinforced/prison, -/obj/item/device/assembly/timer, -/obj/item/device/assembly/voice, -/obj/item/tool/crowbar, -/turf/open/floor{ - icon_state = "dark" - }, -/area/lv624/lazarus/engineering) +/obj/structure/flora/jungle/vines/heavy, +/obj/structure/window_frame/colony, +/obj/item/shard, +/turf/open/floor/plating, +/area/lv624/lazarus/yggdrasil) "aUu" = ( /obj/structure/machinery/light, /obj/structure/bed/stool, +/obj/effect/decal/warning_stripes{ + icon_state = "N"; + pixel_y = 2 + }, /turf/open/floor{ icon_state = "dark" }, @@ -10887,6 +10892,7 @@ /area/lv624/ground/jungle/south_west_jungle) "aVb" = ( /obj/structure/machinery/light, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -10899,6 +10905,7 @@ /obj/structure/machinery/power/geothermal{ fail_rate = 5 }, +/obj/structure/platform, /turf/open/floor/plating{ dir = 6; icon_state = "warnplate" @@ -10917,6 +10924,10 @@ /obj/structure/machinery/power/geothermal{ fail_rate = 5 }, +/obj/item/clothing/head/hardhat/orange{ + pixel_x = -7; + pixel_y = 13 + }, /turf/open/floor/plating{ dir = 8; icon_state = "warnplate" @@ -10935,6 +10946,9 @@ }, /area/lv624/lazarus/engineering) "aVj" = ( +/obj/structure/platform_decoration{ + dir = 1 + }, /obj/structure/machinery/door/airlock/almayer/engineering/colony{ dir = 1; locked = 1; @@ -10943,7 +10957,7 @@ req_one_access = null }, /turf/open/floor{ - icon_state = "dark" + icon_state = "delivery" }, /area/lv624/lazarus/engineering) "aVk" = ( @@ -11044,11 +11058,12 @@ /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_west_jungle) "aVF" = ( -/obj/structure/machinery/power/apc{ - dir = 1; - name = "Geothermal APC"; - pixel_y = 30; - start_charge = 0 +/obj/structure/extinguisher_cabinet{ + pixel_y = 30 + }, +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 }, /turf/open/floor{ icon_state = "dark" @@ -11061,22 +11076,28 @@ name = "General Listening Channel"; pixel_y = 30 }, +/obj/structure/stairs/perspective{ + dir = 4; + icon_state = "p_stair_full" + }, +/obj/structure/platform/stair_cut, +/obj/item/clothing/head/hardhat/orange, /turf/open/floor{ icon_state = "dark" }, /area/lv624/lazarus/engineering) "aVH" = ( -/obj/item/clothing/head/hardhat/orange, /obj/structure/machinery/light{ dir = 1 }, +/obj/structure/foamed_metal, /turf/open/floor{ icon_state = "dark" }, /area/lv624/lazarus/engineering) "aVI" = ( -/obj/structure/foamed_metal, /obj/structure/machinery/vending/coffee, +/obj/structure/foamed_metal, /turf/open/floor{ icon_state = "dark" }, @@ -11178,17 +11199,17 @@ /turf/open/floor/plating, /area/lv624/lazarus/secure_storage) "aWd" = ( -/obj/structure/machinery/cm_vending/sorted/tech/comp_storage, +/obj/item/shard, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "podhatchfloor" }, /area/lv624/lazarus/comms) "aWe" = ( -/obj/structure/machinery/computer/telecomms/monitor, +/obj/structure/machinery/computer/telecomms/monitor{ + pixel_y = 16 + }, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "dark" }, /area/lv624/lazarus/comms) "aWf" = ( @@ -11197,9 +11218,8 @@ req_access_txt = "100"; req_one_access = null }, -/obj/structure/foamed_metal, /turf/open/floor{ - icon_state = "dark" + icon_state = "delivery" }, /area/lv624/lazarus/engineering) "aWg" = ( @@ -11207,6 +11227,7 @@ /obj/structure/lattice{ layer = 2.9 }, +/obj/structure/platform, /turf/open/floor/plating{ dir = 10; icon_state = "warnplate" @@ -11302,28 +11323,38 @@ /obj/effect/decal/remains/xeno, /turf/open/gm/dirt, /area/lv624/ground/colony/south_medbay_road) -"aWx" = ( -/obj/structure/bed/chair/office/light, +"aWy" = ( +/obj/structure/surface/table, +/obj/item/device/radio/headset{ + frequency = 1469; + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/device/radio/headset{ + frequency = 1469 + }, +/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) -"aWy" = ( -/obj/structure/foamed_metal, -/turf/open/gm/dirt, -/area/lv624/lazarus/comms) "aWz" = ( /obj/structure/window_frame/colony/reinforced, /turf/open/floor/plating, /area/lv624/lazarus/engineering) "aWA" = ( -/obj/structure/foamed_metal, -/obj/structure/machinery/cm_vending/sorted/tech/electronics_storage, +/obj/structure/machinery/computer/telecomms/server{ + pixel_y = 16 + }, +/obj/structure/bed/chair/office/light{ + dir = 1 + }, /turf/open/floor{ - icon_state = "dark" + dir = 9; + icon_state = "brown" }, -/area/lv624/lazarus/engineering) +/area/lv624/lazarus/comms) "aWC" = ( /obj/structure/machinery/colony_floodlight, /turf/open/gm/grass/grass1, @@ -11335,31 +11366,35 @@ }, /area/lv624/lazarus/engineering) "aWE" = ( -/obj/structure/sign/safety/high_voltage{ - pixel_x = 7; - pixel_y = -32 +/obj/effect/decal/warning_stripes{ + icon_state = "E"; + pixel_x = 1 }, -/obj/structure/surface/table/reinforced/prison, -/obj/item/tool/extinguisher, -/obj/effect/spawner/random/powercell, +/obj/item/prop/alien/hugger, /turf/open/floor{ - icon_state = "dark" + dir = 9; + icon_state = "brown" }, -/area/lv624/lazarus/engineering) +/area/lv624/lazarus/comms) "aWF" = ( +/obj/effect/spawner/random/toolbox, +/obj/structure/surface/table/reinforced/prison, +/obj/item/device/radio, +/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, /obj/structure/machinery/light, -/obj/structure/machinery/power/monitor{ - name = "Main Power Grid Monitoring" - }, /turf/open/floor{ icon_state = "dark" }, /area/lv624/lazarus/engineering) "aWG" = ( -/obj/effect/spawner/random/toolbox, -/obj/structure/surface/table/reinforced/prison, -/obj/item/device/radio, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, +/obj/structure/stairs/perspective{ + dir = 4; + icon_state = "p_stair_full" + }, +/obj/structure/platform{ + layer = 3.1 + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -11368,6 +11403,10 @@ /obj/structure/surface/table/reinforced/prison, /obj/item/folder, /obj/item/device/assembly/signaller, +/obj/structure/platform_decoration{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -11377,6 +11416,7 @@ /obj/item/device/flashlight, /obj/effect/spawner/random/tool, /obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -11456,46 +11496,35 @@ /turf/open/floor/greengrid, /area/lv624/lazarus/secure_storage) "aWX" = ( -/obj/structure/surface/table, -/obj/item/tool/weldingtool, -/obj/item/tool/wrench, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, -/turf/open/floor{ - dir = 9; - icon_state = "brown" +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 }, -/area/lv624/lazarus/comms) -"aWY" = ( -/obj/structure/surface/table, -/obj/item/device/radio/off{ - frequency = 1469 +/obj/structure/machinery/light{ + dir = 4 }, +/obj/effect/decal/cleanable/dirt, +/obj/item/stack/rods, /turf/open/floor{ dir = 9; icon_state = "brown" }, /area/lv624/lazarus/comms) +"aWY" = ( +/turf/closed/wall, +/area/lv624/lazarus/yggdrasil) "aWZ" = ( -/obj/structure/surface/table, -/obj/item/clothing/head/soft/blue, -/obj/structure/machinery/light, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, -/area/lv624/lazarus/comms) +/obj/structure/flora/bush/ausbushes/lavendergrass, +/turf/open/gm/grass/grass1, +/area/lv624/ground/colony/west_tcomms_road) "aXa" = ( -/obj/structure/surface/table, -/obj/item/device/radio/off{ - frequency = 1469 - }, -/obj/item/tool/crowbar, /obj/structure/machinery/light, -/obj/item/tool/hatchet{ - pixel_x = 6; - pixel_y = 4 +/obj/structure/stairs/perspective{ + dir = 8; + icon_state = "p_stair_full" }, +/obj/structure/platform, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -11508,11 +11537,24 @@ }, /area/lv624/lazarus/sleep_male) "aXd" = ( -/turf/open/gm/dirtgrassborder/south, +/obj/structure/bed/chair/office/light{ + dir = 1 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, /area/lv624/lazarus/comms) "aXe" = ( -/obj/structure/foamed_metal, -/turf/open/gm/dirtgrassborder/south, +/obj/effect/decal/warning_stripes{ + icon_state = "N"; + pixel_y = 1 + }, +/obj/item/tool/crowbar, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, /area/lv624/lazarus/comms) "aXf" = ( /obj/structure/machinery/door/airlock/almayer/engineering/colony{ @@ -11523,7 +11565,7 @@ req_one_access = null }, /turf/open/floor{ - icon_state = "dark" + icon_state = "delivery" }, /area/lv624/lazarus/engineering) "aXg" = ( @@ -11534,7 +11576,7 @@ req_one_access = null }, /turf/open/floor{ - icon_state = "dark" + icon_state = "delivery" }, /area/lv624/lazarus/engineering) "aXh" = ( @@ -11559,19 +11601,29 @@ /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_west_jungle) "aXn" = ( -/obj/structure/fence, -/turf/open/gm/grass/grass1, -/area/lv624/ground/jungle/south_central_jungle) +/obj/effect/decal/warning_stripes{ + icon_state = "NE-out"; + pixel_x = 1; + pixel_y = 1 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "aXo" = ( /obj/effect/landmark/crap_item, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/east_central_jungle) "aXs" = ( -/obj/structure/extinguisher_cabinet{ - pixel_y = 30 +/obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/power/apc{ + dir = 1; + name = "Geothermal APC"; + start_charge = 0 }, /turf/open/floor{ - icon_state = "dark" + icon_state = "delivery" }, /area/lv624/lazarus/engineering) "aXt" = ( @@ -11592,6 +11644,7 @@ dir = 1 }, /obj/structure/reagent_dispensers/fueltank, +/obj/effect/spawner/random/tool, /turf/open/floor{ icon_state = "dark" }, @@ -11684,6 +11737,7 @@ /obj/structure/machinery/light{ dir = 4 }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -11698,6 +11752,7 @@ /area/lv624/lazarus/security) "aXN" = ( /obj/effect/spawner/random/powercell, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -11744,7 +11799,14 @@ /area/lv624/lazarus/landing_zones/lz2) "aXZ" = ( /obj/structure/surface/rack, -/obj/effect/spawner/random/tool, +/obj/item/weapon/gun/smg/nailgun, +/obj/item/weapon/gun/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, +/obj/item/ammo_magazine/smg/nailgun, /turf/open/floor{ icon_state = "dark" }, @@ -11773,19 +11835,13 @@ /turf/open/floor/greengrid, /area/lv624/lazarus/secure_storage) "aYh" = ( -/obj/structure/surface/rack, -/obj/item/weapon/gun/smg/nailgun, -/obj/item/weapon/gun/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, -/obj/item/ammo_magazine/smg/nailgun, +/obj/effect/decal/cleanable/dirt, +/obj/item/stack/rods, /turf/open/floor{ - icon_state = "dark" + dir = 9; + icon_state = "brown" }, -/area/lv624/lazarus/engineering) +/area/lv624/lazarus/comms) "aYj" = ( /obj/effect/landmark/lv624/xeno_tunnel, /obj/structure/flora/jungle/vines/light_3, @@ -11982,15 +12038,6 @@ /obj/effect/landmark/lv624/fog_blocker, /turf/open/gm/dirt, /area/lv624/ground/river/west_river) -"aZc" = ( -/obj/structure/surface/table, -/obj/item/weapon/twohanded/fireaxe, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, -/turf/open/floor{ - dir = 9; - icon_state = "brown" - }, -/area/lv624/lazarus/comms) "aZd" = ( /obj/structure/closet/radiation, /obj/item/device/radio/intercom{ @@ -11999,6 +12046,7 @@ name = "General Listening Channel"; pixel_x = -30 }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12009,6 +12057,7 @@ /obj/item/book/manual/engineering_guide, /obj/item/book/manual/engineering_hacking, /obj/item/book/manual/atmospipes, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12019,6 +12068,7 @@ /obj/item/device/analyzer, /obj/item/device/multitool, /obj/item/device/assembly/prox_sensor, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12036,9 +12086,11 @@ "aZh" = ( /obj/structure/surface/table, /obj/effect/landmark/objective_landmark/close, +/obj/item/tool/crowbar, +/obj/effect/decal/cleanable/dirt, +/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, /turf/open/floor{ - dir = 9; - icon_state = "brown" + icon_state = "dark" }, /area/lv624/lazarus/comms) "aZi" = ( @@ -12112,6 +12164,7 @@ /obj/structure/machinery/light{ dir = 8 }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12119,6 +12172,7 @@ "aZy" = ( /obj/structure/closet/secure_closet/engineering_personal, /obj/effect/landmark/objective_landmark/close, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12191,14 +12245,28 @@ }, /area/lv624/lazarus/hop) "aZG" = ( -/obj/structure/filingcabinet, -/obj/effect/landmark/objective_landmark/close, +/obj/effect/decal/warning_stripes{ + icon_state = "N"; + pixel_y = 1 + }, /turf/open/floor{ - icon_state = "dark" + dir = 9; + icon_state = "brown" }, -/area/lv624/lazarus/engineering) +/area/lv624/lazarus/comms) "aZI" = ( -/obj/structure/filingcabinet/chestdrawer, +/obj/structure/filingcabinet/chestdrawer{ + density = 0; + pixel_x = -8; + pixel_y = 16 + }, +/obj/structure/filingcabinet{ + density = 0; + pixel_x = 7; + pixel_y = 16 + }, +/obj/effect/landmark/objective_landmark/close, +/obj/effect/decal/cleanable/dirt, /turf/open/floor{ icon_state = "dark" }, @@ -12212,8 +12280,11 @@ }, /area/lv624/lazarus/engineering) "aZK" = ( -/obj/structure/surface/rack, -/obj/item/clothing/mask/gas, +/obj/effect/decal/warning_stripes{ + icon_state = "NW-out"; + pixel_x = -1; + pixel_y = 1 + }, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -12347,11 +12418,11 @@ /area/lv624/ground/caves/west_caves) "bcU" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_east_caves) @@ -12741,6 +12812,13 @@ }, /turf/open/gm/coast/beachcorner/north_east, /area/lv624/ground/caves/sand_temple) +"bJe" = ( +/obj/structure/reagent_dispensers/watertank, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "bJz" = ( /obj/effect/decal/cleanable/blood, /obj/effect/landmark/corpsespawner/wygoon, @@ -12760,6 +12838,18 @@ "bLE" = ( /turf/open/gm/dirtgrassborder/south, /area/lv624/ground/caves/sand_temple) +"bLH" = ( +/obj/structure/machinery/power/monitor{ + name = "Main Power Grid Monitoring" + }, +/obj/effect/decal/warning_stripes{ + icon_state = "N"; + pixel_y = 2 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "bMu" = ( /turf/open/gm/dirtgrassborder/west, /area/lv624/ground/jungle/east_jungle) @@ -12790,10 +12880,10 @@ /area/lv624/ground/barrens/south_eastern_barrens) "bOm" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/barrens/north_east_barrens) @@ -12815,6 +12905,13 @@ /obj/structure/flora/bush/ausbushes/pointybush, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_west_caves) +"bQf" = ( +/obj/structure/machinery/power/port_gen/pacman/super, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "bQz" = ( /obj/effect/landmark/structure_spawner/setup/distress/xeno_weed_node, /obj/effect/decal/grass_overlay/grass1{ @@ -12855,6 +12952,13 @@ }, /turf/open/floor/vault, /area/lv624/lazarus/quartstorage) +"bUc" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/shard, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "bUs" = ( /obj/structure/surface/table/woodentable/poor, /obj/item/ammo_magazine/shotgun/buckshot, @@ -13001,11 +13105,11 @@ /area/lv624/ground/caves/west_caves) "chi" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -13137,9 +13241,12 @@ /obj/effect/landmark/structure_spawner/setup/distress/xeno_weed_node, /turf/open/gm/dirt, /area/lv624/ground/barrens/central_barrens) +"ctS" = ( +/obj/structure/fence, +/turf/open/gm/dirt, +/area/lv624/ground/colony/west_tcomms_road) "cvk" = ( /obj/structure/fence, -/obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/dirtgrassborder/east, /area/lv624/ground/jungle/west_central_jungle) "cwv" = ( @@ -13164,6 +13271,18 @@ /obj/structure/foamed_metal, /turf/open/gm/dirtgrassborder/south, /area/lv624/ground/jungle/east_central_jungle) +"cyP" = ( +/obj/structure/stairs/perspective{ + dir = 8; + icon_state = "p_stair_full" + }, +/obj/structure/platform/stair_cut/alt, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "czq" = ( /obj/effect/landmark/objective_landmark/science, /turf/open/auto_turf/strata_grass/layer1, @@ -13323,6 +13442,17 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/north_west_caves) +"cNE" = ( +/obj/structure/machinery/light{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/item/shard, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "cNH" = ( /obj/structure/surface/rack, /obj/item/storage/box/beakers, @@ -13356,6 +13486,14 @@ /obj/structure/machinery/colony_floodlight, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_west_jungle) +"cQU" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/device/multitool, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "cQX" = ( /obj/structure/stairs/perspective{ color = "#b29082"; @@ -13587,6 +13725,15 @@ icon_state = "dark" }, /area/lv624/ground/barrens/north_east_barrens/ceiling) +"drX" = ( +/obj/effect/decal/warning_stripes{ + icon_state = "N"; + pixel_y = 1 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "dsi" = ( /obj/effect/landmark/monkey_spawn, /turf/open/gm/dirt, @@ -13680,10 +13827,10 @@ /area/lv624/ground/caves/south_central_caves) "dBS" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_west_caves) @@ -13761,6 +13908,13 @@ /obj/effect/landmark/lv624/fog_blocker, /turf/open/gm/dirtgrassborder/grassdirt_corner/south_east, /area/lv624/ground/jungle/north_jungle) +"dHr" = ( +/obj/item/weapon/twohanded/fireaxe, +/obj/effect/decal/cleanable/blood, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "dId" = ( /obj/structure/machinery/cm_vending/sorted/medical/no_access, /turf/open/floor{ @@ -14137,6 +14291,16 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/east_caves) +"elO" = ( +/obj/structure/platform_decoration{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "enn" = ( /obj/effect/landmark/structure_spawner/setup/distress/xeno_weed_node, /obj/effect/decal/grass_overlay/grass1{ @@ -14223,6 +14387,16 @@ /obj/effect/landmark/objective_landmark/medium, /turf/open/gm/dirt, /area/lv624/ground/caves/south_west_caves) +"euU" = ( +/obj/structure/machinery/light{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "euW" = ( /obj/structure/surface/table/woodentable/poor, /obj/item/tool/candle, @@ -14245,10 +14419,18 @@ /turf/open/gm/dirtgrassborder/north, /area/lv624/ground/jungle/south_west_jungle) "eyb" = ( -/obj/structure/fence, -/obj/structure/flora/bush/ausbushes/lavendergrass, -/turf/open/gm/dirtgrassborder/west, -/area/lv624/ground/colony/west_tcomms_road) +/obj/effect/decal/warning_stripes{ + icon_state = "NW-out"; + pixel_x = -1; + pixel_y = 1 + }, +/obj/effect/decal/cleanable/dirt, +/obj/item/shard, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "eyn" = ( /obj/structure/flora/jungle/vines/light_3, /turf/closed/wall/strata_ice/jungle, @@ -14497,11 +14679,11 @@ /area/lv624/ground/jungle/east_jungle) "fcQ" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -5; - pixel_y = -5; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -5; + pixel_y = -5 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -14561,6 +14743,13 @@ /obj/structure/flora/jungle/vines/light_2, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_east_jungle) +"fij" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/bed/chair/office/light, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "fio" = ( /obj/structure/flora/grass/tallgrass/jungle/corner{ dir = 9 @@ -14725,6 +14914,12 @@ icon_state = "squareswood" }, /area/lv624/ground/caves/sand_temple) +"fAs" = ( +/obj/item/tool/crowbar, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "fAz" = ( /obj/structure/machinery/cm_vending/sorted/medical/no_access, /turf/open/floor{ @@ -14858,10 +15053,26 @@ /obj/structure/flora/bush/ausbushes/ausbush, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) +"fLf" = ( +/obj/effect/landmark/survivor_spawner, +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "fLh" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/angel, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) +"fMa" = ( +/obj/item/prop/alien/hugger, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "fMl" = ( /obj/structure/machinery/door/airlock/multi_tile/almayer/generic{ dir = 1; @@ -14884,6 +15095,16 @@ "fPH" = ( /turf/open/gm/dirtgrassborder/south, /area/lv624/ground/jungle/west_central_jungle) +"fQx" = ( +/obj/effect/decal/warning_stripes{ + icon_state = "NW-out"; + pixel_x = -1; + pixel_y = 2 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "fQL" = ( /turf/open/gm/coast/beachcorner2/south_east, /area/lv624/ground/river/west_river) @@ -14974,11 +15195,11 @@ /area/lv624/ground/jungle/south_central_jungle) "gcB" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) @@ -15069,11 +15290,11 @@ /area/lv624/ground/barrens/south_eastern_jungle_barrens) "gnt" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -5; - pixel_y = -5; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -5; + pixel_y = -5 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_west_caves) @@ -15096,10 +15317,15 @@ /turf/open/gm/dirtgrassborder/grassdirt_corner/south_west, /area/lv624/ground/jungle/west_central_jungle) "gqG" = ( -/obj/item/device/assembly/infra, +/obj/structure/sign/safety/high_voltage{ + pixel_x = 7; + pixel_y = -32 + }, /obj/structure/surface/table/reinforced/prison, +/obj/item/tool/extinguisher, +/obj/effect/spawner/random/powercell, +/obj/item/device/assembly/infra, /obj/effect/spawner/random/powercell, -/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, /turf/open/floor{ icon_state = "dark" }, @@ -15269,12 +15495,20 @@ /turf/open/gm/dirt, /area/lv624/ground/caves/south_central_caves) "gKg" = ( -/obj/item/clothing/head/hardhat/orange, /turf/open/floor/plating{ dir = 5; icon_state = "warnplate" }, /area/lv624/lazarus/engineering) +"gKN" = ( +/obj/structure/platform_decoration{ + dir = 8 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "gMe" = ( /obj/effect/landmark/nightmare{ insert_tag = "lv-rightsidepass" @@ -15469,11 +15703,11 @@ /area/lv624/lazarus/landing_zones/lz2) "hez" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) @@ -15580,11 +15814,11 @@ /area/lv624/ground/caves/north_central_caves) "hpK" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_central_caves) @@ -15625,10 +15859,10 @@ /area/lv624/ground/river/central_river) "htV" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) @@ -15689,6 +15923,13 @@ /obj/effect/landmark/structure_spawner/setup/distress/xeno_membrane, /turf/open/gm/dirt, /area/lv624/ground/caves/central_caves) +"hDe" = ( +/obj/structure/bed/stool, +/obj/item/prop/alien/hugger, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "hDX" = ( /obj/effect/decal/remains/xeno, /turf/open/gm/dirt, @@ -15748,6 +15989,20 @@ /obj/effect/landmark/objective_landmark/far, /turf/open/floor/vault, /area/lv624/lazarus/quartstorage) +"hJh" = ( +/obj/structure/coatrack{ + pixel_x = 11; + pixel_y = 14 + }, +/obj/item/clothing/head/soft/blue{ + pixel_x = 7; + pixel_y = 28 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "hJn" = ( /obj/structure/flora/jungle/vines/light_2, /turf/open/gm/grass/grass2, @@ -15806,6 +16061,18 @@ /obj/item/reagent_container/food/snacks/grown/mushroom/plumphelmet, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) +"hOo" = ( +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, +/obj/effect/decal/cleanable/dirt, +/obj/item/stack/rods, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "hPV" = ( /obj/effect/decal/remains/xeno{ pixel_x = 31 @@ -15974,11 +16241,11 @@ /area/lv624/lazarus/landing_zones/lz1) "ieN" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) @@ -16021,6 +16288,16 @@ /obj/effect/landmark/queen_spawn, /turf/open/gm/dirt, /area/lv624/ground/caves/east_caves) +"iiO" = ( +/obj/effect/decal/warning_stripes{ + icon_state = "NW-out"; + pixel_x = -1; + pixel_y = 1 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "ikA" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/chanterelle, /turf/open/auto_turf/strata_grass/layer1, @@ -16076,8 +16353,11 @@ /turf/open/gm/dirt, /area/lv624/ground/caves/north_east_caves) "isR" = ( -/obj/effect/landmark/objective_landmark/medium, -/obj/structure/largecrate/random, +/obj/structure/surface/table, +/obj/item/device/flashlight, +/obj/item/device/radio/off{ + frequency = 1469 + }, /turf/open/floor{ dir = 9; icon_state = "brown" @@ -16348,6 +16628,19 @@ /obj/structure/flora/bush/ausbushes/var3/fullgrass, /turf/open/gm/grass/grass1, /area/lv624/ground/colony/west_tcomms_road) +"iYL" = ( +/obj/item/device/radio/intercom{ + freerange = 1; + frequency = 1469; + name = "General Listening Channel"; + pixel_y = 26 + }, +/obj/item/prop/alien/hugger, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "iZG" = ( /obj/effect/landmark/crap_item, /turf/open/gm/dirt, @@ -16388,9 +16681,25 @@ /obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_west_jungle) +"jdL" = ( +/obj/structure/foamed_metal, +/obj/structure/flora/jungle/vines/light_2, +/turf/open/floor/plating{ + icon_state = "warnplate" + }, +/area/lv624/lazarus/engineering) "jeL" = ( /turf/closed/wall/r_wall, /area/lv624/ground/caves/north_central_caves) +"jfN" = ( +/obj/structure/platform{ + dir = 4 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "jga" = ( /turf/open/gm/river, /area/lv624/ground/jungle/west_jungle) @@ -16516,6 +16825,11 @@ /turf/open/gm/dirt, /area/lv624/ground/caves/west_caves) "jwW" = ( +/obj/structure/platform_decoration, +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, /turf/open/floor/plating{ dir = 6; icon_state = "warnplate" @@ -16807,11 +17121,11 @@ /area/lv624/ground/jungle/north_west_jungle) "jRL" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/east_caves) @@ -17054,6 +17368,11 @@ "kvo" = ( /turf/open/gm/dirtgrassborder/grassdirt_corner2/south_west, /area/lv624/ground/jungle/east_central_jungle) +"kvv" = ( +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "kvE" = ( /obj/structure/machinery/landinglight/ds2/delaythree, /turf/open/floor/plating, @@ -17086,6 +17405,16 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/south_west_caves) +"kyz" = ( +/obj/effect/decal/warning_stripes{ + icon_state = "NE-out"; + pixel_x = 1; + pixel_y = 1 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "kyN" = ( /obj/structure/flora/bush/ausbushes/var3/ywflowers, /turf/open/gm/grass/grass1, @@ -17113,6 +17442,13 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/north_west_caves) +"kzp" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/foamed_metal, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "kzu" = ( /obj/structure/flora/bush/ausbushes/grassybush, /turf/open/gm/grass/grass1, @@ -17208,6 +17544,20 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/north_west_caves) +"kKL" = ( +/obj/structure/machinery/light{ + dir = 1 + }, +/obj/effect/decal/warning_stripes{ + icon_state = "E"; + pixel_x = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "kLl" = ( /obj/effect/decal/grass_overlay/grass1/inner{ dir = 8 @@ -17318,11 +17668,13 @@ /turf/open/gm/dirt, /area/lv624/ground/caves/west_caves) "kWX" = ( +/obj/effect/landmark/objective_landmark/medium, +/obj/structure/largecrate/random, /turf/open/floor{ - dir = 8; - icon_state = "asteroidwarning" + dir = 9; + icon_state = "brown" }, -/area/lv624/ground/colony/telecomm/sw_lz2) +/area/lv624/lazarus/comms) "kXE" = ( /obj/structure/flora/bush/ausbushes/ausbush, /turf/open/gm/dirtgrassborder/north, @@ -17517,10 +17869,12 @@ /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/central_jungle) "lud" = ( -/obj/structure/fence, -/obj/structure/flora/bush/ausbushes/lavendergrass, -/turf/open/gm/dirtgrassborder/west, -/area/lv624/ground/colony/north_tcomms_road) +/obj/structure/foamed_metal, +/obj/structure/flora/jungle/vines/heavy, +/turf/open/floor/plating{ + icon_state = "warnplate" + }, +/area/lv624/lazarus/engineering) "lxr" = ( /obj/structure/flora/jungle/vines/light_2, /turf/open/gm/dirtgrassborder/south, @@ -17561,6 +17915,16 @@ icon_state = "desert0" }, /area/lv624/ground/barrens/west_barrens) +"lzW" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/warning_stripes{ + icon_state = "E"; + pixel_x = 1 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "lAX" = ( /obj/structure/flora/bush/ausbushes/reedbush, /turf/open/gm/grass/grass1, @@ -17701,11 +18065,11 @@ /area/lv624/lazarus/landing_zones/lz2) "lKl" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) @@ -17742,11 +18106,11 @@ /area/lv624/lazarus/corporate_dome) "lNG" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_east_caves) @@ -17758,6 +18122,11 @@ "lQC" = ( /turf/open/gm/dirt, /area/lv624/ground/barrens/east_barrens) +"lQP" = ( +/obj/structure/window_frame/colony, +/obj/item/shard, +/turf/open/floor/plating, +/area/lv624/lazarus/comms) "lRd" = ( /obj/item/stack/sheet/wood{ amount = 2 @@ -17911,11 +18280,11 @@ /area/lv624/ground/jungle/east_jungle) "mca" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_central_caves) @@ -17928,11 +18297,11 @@ /area/lv624/ground/jungle/south_east_jungle) "mfn" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/east_caves) @@ -18018,7 +18387,9 @@ "mkU" = ( /obj/structure/foamed_metal, /obj/structure/flora/jungle/vines/light_2, -/turf/open/floor/plating, +/turf/open/floor{ + icon_state = "delivery" + }, /area/lv624/lazarus/engineering) "mkW" = ( /obj/structure/flora/grass/tallgrass/jungle/corner{ @@ -18026,6 +18397,15 @@ }, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/north_east_jungle) +"mkZ" = ( +/obj/structure/platform_decoration{ + dir = 4 + }, +/obj/structure/foamed_metal, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "mmu" = ( /obj/structure/flora/jungle/plantbot1, /turf/open/gm/grass/grass1, @@ -18145,6 +18525,15 @@ /obj/structure/flora/jungle/vines/light_2, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_west_jungle) +"mBH" = ( +/obj/structure/foamed_metal{ + layer = 3.1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "mBL" = ( /turf/open/floor{ dir = 1; @@ -18523,6 +18912,17 @@ icon_state = "desert3" }, /area/lv624/ground/barrens/west_barrens) +"nrK" = ( +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "nrP" = ( /obj/structure/transmitter/colony_net{ phone_category = "Lazarus Landing"; @@ -18533,10 +18933,10 @@ /area/lv624/lazarus/quartstorage) "nrR" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_east_caves) @@ -18615,6 +19015,13 @@ /turf/open/gm/dirt, /area/lv624/lazarus/landing_zones/lz2) "nwR" = ( +/obj/structure/platform_decoration{ + dir = 1 + }, +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, /turf/open/floor/plating{ dir = 10; icon_state = "warnplate" @@ -18828,11 +19235,11 @@ /area/lv624/lazarus/corporate_dome) "nNu" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/barrens/north_east_barrens) @@ -18869,11 +19276,11 @@ /area/lv624/ground/caves/sand_temple) "nQH" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -19011,6 +19418,11 @@ /obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/grass/grass2, /area/lv624/ground/barrens/south_eastern_jungle_barrens) +"oas" = ( +/turf/open/floor{ + icon_state = "delivery" + }, +/area/lv624/lazarus/engineering) "oaL" = ( /obj/structure/flora/bush/ausbushes/ausbush, /turf/open/gm/grass/grass1, @@ -19097,6 +19509,7 @@ /turf/open/gm/dirt, /area/lv624/ground/river/east_river) "ogR" = ( +/obj/item/prop/alien/hugger, /turf/open/floor/plating{ dir = 9; icon_state = "warnplate" @@ -19137,6 +19550,16 @@ /obj/effect/landmark/objective_landmark/close, /turf/open/floor/plating, /area/lv624/ground/barrens/central_barrens) +"oiR" = ( +/obj/effect/decal/warning_stripes{ + icon_state = "W"; + pixel_x = -1 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "omu" = ( /obj/structure/flora/bush/ausbushes/var3/sparsegrass, /turf/open/gm/dirt, @@ -19186,11 +19609,11 @@ /area/lv624/ground/colony/telecomm/cargo) "osf" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 11; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 11; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) @@ -19384,6 +19807,16 @@ icon_state = "asteroidplating" }, /area/lv624/ground/caves/north_central_caves) +"oIO" = ( +/obj/structure/surface/table, +/obj/effect/decal/cleanable/dirt, +/obj/item/device/radio/off{ + frequency = 1469 + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "oJL" = ( /obj/effect/landmark/crap_item, /turf/open/gm/grass/grass1, @@ -19455,6 +19888,15 @@ "oRH" = ( /turf/open/gm/dirtgrassborder/south, /area/lv624/ground/jungle/north_east_jungle) +"oRY" = ( +/obj/structure/surface/rack, +/obj/item/clothing/mask/gas, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "oSh" = ( /obj/item/stack/sheet/wood{ amount = 2 @@ -19825,6 +20267,12 @@ icon_state = "white" }, /area/lv624/lazarus/medbay) +"pzP" = ( +/obj/effect/decal/cleanable/blood/oil, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "pAE" = ( /obj/structure/flora/bush/ausbushes/var3/leafybush, /turf/open/auto_turf/strata_grass/layer1, @@ -19955,8 +20403,8 @@ /area/lv624/lazarus/quartstorage/outdoors) "pKm" = ( /turf/open/floor{ - icon_state = "asteroidwarning"; - dir = 8 + dir = 8; + icon_state = "asteroidwarning" }, /area/lv624/ground/colony/telecomm/sw_lz2) "pKp" = ( @@ -19976,10 +20424,10 @@ /area/lv624/ground/colony/west_nexus_road) "pLv" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_west_caves) @@ -20110,10 +20558,10 @@ /area/lv624/ground/caves/east_caves) "pVZ" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) @@ -20143,6 +20591,18 @@ /obj/item/clothing/mask/gas, /turf/open/floor/vault, /area/lv624/lazarus/quartstorage) +"qap" = ( +/obj/structure/machinery/light, +/obj/structure/stairs/perspective{ + dir = 9; + icon_state = "p_stair_full" + }, +/obj/structure/platform, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "qaE" = ( /obj/effect/landmark/hunter_primary, /obj/effect/landmark/queen_spawn, @@ -20176,6 +20636,13 @@ icon_state = "white" }, /area/lv624/lazarus/corporate_dome) +"qez" = ( +/obj/effect/landmark/good_item, +/obj/effect/decal/cleanable/blood/xeno, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "qeW" = ( /turf/open/gm/coast/beachcorner2/north_west, /area/lv624/ground/caves/sand_temple) @@ -20210,6 +20677,21 @@ }, /turf/open/gm/dirt, /area/lv624/ground/caves/west_caves) +"qit" = ( +/obj/item/prop/alien/hugger{ + pixel_x = -20; + pixel_y = 8 + }, +/obj/item/tool/hatchet{ + pixel_x = -14; + pixel_y = 6 + }, +/obj/effect/decal/cleanable/blood/xeno, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "qiL" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/amanita, /turf/open/auto_turf/strata_grass/layer1, @@ -20418,7 +20900,9 @@ "qGR" = ( /obj/structure/foamed_metal, /obj/structure/flora/jungle/vines/heavy, -/turf/open/floor/plating, +/turf/open/floor{ + icon_state = "delivery" + }, /area/lv624/lazarus/engineering) "qHC" = ( /obj/structure/largecrate/random/barrel/red, @@ -20475,6 +20959,24 @@ /obj/structure/flora/jungle/alienplant1, /turf/open/gm/river, /area/lv624/ground/jungle/west_jungle) +"qNl" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/reagent_dispensers/fueltank, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) +"qNz" = ( +/obj/structure/stairs/perspective{ + dir = 1; + icon_state = "p_stair_full" + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "qNQ" = ( /obj/structure/machinery/landinglight/ds2/delayone{ dir = 4 @@ -20556,11 +21058,11 @@ /area/lv624/ground/caves/north_west_caves) "qVi" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_east_caves) @@ -20584,6 +21086,12 @@ icon_state = "vault" }, /area/lv624/lazarus/quartstorage) +"qXt" = ( +/obj/item/stack/rods, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "qYF" = ( /obj/structure/flora/bush/ausbushes/var3/sunnybush, /turf/open/gm/dirt, @@ -20697,10 +21205,10 @@ /area/lv624/ground/jungle/north_east_jungle) "rhi" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/east_caves) @@ -20714,32 +21222,40 @@ icon_state = "green" }, /area/lv624/lazarus/hydroponics) +"rkZ" = ( +/obj/item/shard, +/obj/item/stack/rods, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "rmg" = ( -/obj/structure/machinery/power/monitor, -/obj/structure/foamed_metal, /obj/structure/machinery/light{ dir = 1 }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/foamed_metal, /turf/open/floor{ icon_state = "dark" }, /area/lv624/lazarus/engineering) "rmt" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_central_caves) "rmW" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 11; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 11; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) @@ -20875,11 +21391,11 @@ /area/lv624/lazarus/corporate_dome) "rAU" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/east_caves) @@ -20928,11 +21444,11 @@ /area/lv624/lazarus/quartstorage) "rGE" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 6; - pixel_y = -8; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 6; + pixel_y = -8 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_east_caves) @@ -21166,6 +21682,9 @@ icon_state = "vault" }, /area/lv624/lazarus/quartstorage) +"sfg" = ( +/turf/open/gm/dirtgrassborder/grassdirt_corner2/south_east, +/area/lv624/ground/jungle/south_central_jungle) "sfH" = ( /obj/structure/inflatable, /turf/open/gm/dirt, @@ -21221,10 +21740,10 @@ /area/lv624/ground/caves/sand_temple) "snc" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 4; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 4 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -21324,6 +21843,15 @@ /obj/structure/flora/bush/ausbushes/grassybush, /turf/open/gm/grass/grass1, /area/lv624/lazarus/landing_zones/lz2) +"svv" = ( +/obj/structure/extinguisher_cabinet{ + pixel_y = 30 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "swR" = ( /obj/structure/flora/jungle/vines/light_3, /obj/structure/flora/jungle/vines/heavy, @@ -21638,11 +22166,11 @@ /area/lv624/ground/caves/sand_temple) "sXg" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -10; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -10; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/west_caves) @@ -21666,8 +22194,6 @@ }, /area/lv624/lazarus/landing_zones/lz1) "taK" = ( -/obj/structure/fence, -/obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/dirtgrassborder/grassdirt_corner2/south_east, /area/lv624/ground/colony/north_tcomms_road) "tbV" = ( @@ -21739,11 +22265,11 @@ /area/lv624/ground/river/west_river) "thk" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -5; - pixel_y = -5; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -5; + pixel_y = -5 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) @@ -21912,11 +22438,11 @@ /area/lv624/ground/barrens/south_eastern_barrens) "tuJ" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -5; - pixel_y = -5; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -5; + pixel_y = -5 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_east_caves) @@ -21976,6 +22502,16 @@ /obj/effect/landmark/crap_item, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/south_central_jungle) +"tzP" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/bed/chair/office/light{ + dir = 1 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "tBB" = ( /obj/structure/machinery/colony_floodlight, /turf/open/gm/grass/grass1, @@ -22102,6 +22638,10 @@ icon_state = "whiteyellowfull" }, /area/lv624/ground/barrens/south_eastern_barrens) +"tMP" = ( +/obj/structure/window_frame/colony, +/turf/open/floor/plating, +/area/lv624/lazarus/comms) "tMQ" = ( /obj/effect/decal/grass_overlay/grass1/inner{ dir = 9 @@ -22126,11 +22666,11 @@ /area/lv624/ground/caves/south_central_caves) "tQU" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -22222,6 +22762,16 @@ "tZD" = ( /turf/closed/wall/r_wall, /area/lv624/lazarus/landing_zones/lz2) +"uaz" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/bed/chair/office/light{ + dir = 8 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "uaL" = ( /obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/auto_turf/strata_grass/layer1, @@ -22350,6 +22900,16 @@ icon_state = "desert1" }, /area/lv624/ground/barrens/south_eastern_barrens) +"ukS" = ( +/obj/structure/reagent_dispensers/fueltank, +/obj/item/device/flashlight{ + pixel_y = 5 + }, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "ukY" = ( /obj/effect/landmark/structure_spawner/xvx_hive/xeno_door, /obj/effect/landmark/structure_spawner/setup/distress/xeno_door, @@ -22403,6 +22963,13 @@ /obj/structure/flora/bush/ausbushes/reedbush, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/east_caves) +"upp" = ( +/obj/structure/platform_decoration, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "upM" = ( /obj/effect/landmark/crap_item, /turf/open/gm/grass/grass1, @@ -22415,8 +22982,8 @@ /area/lv624/ground/caves/east_caves) "upV" = ( /obj/item/stack/cable_coil/random{ - pixel_y = 9; - pixel_x = 7 + pixel_x = 7; + pixel_y = 9 }, /turf/open/gm/dirt, /area/lv624/ground/jungle/east_jungle) @@ -22471,11 +23038,11 @@ /area/lv624/ground/caves/north_central_caves) "uxL" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 11; - pixel_y = -2; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 11; + pixel_y = -2 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_central_caves) @@ -22719,6 +23286,12 @@ "uWJ" = ( /turf/closed/wall/strata_ice/jungle, /area/lv624/ground/caves/south_west_caves) +"uXT" = ( +/obj/item/device/assembly/timer, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "uXV" = ( /obj/structure/flora/bush/ausbushes/lavendergrass, /turf/open/gm/dirt, @@ -22789,6 +23362,15 @@ /obj/structure/machinery/colony_floodlight, /turf/open/gm/grass/grass1, /area/lv624/lazarus/landing_zones/lz2) +"vdl" = ( +/obj/structure/stairs/perspective{ + dir = 4; + icon_state = "p_stair_full" + }, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "vdt" = ( /obj/effect/landmark/structure_spawner/xvx_hive/xeno_door, /obj/effect/landmark/structure_spawner/setup/distress/xeno_door, @@ -22897,11 +23479,11 @@ dir = 10 }, /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/gm/dirt, /area/lv624/ground/caves/south_east_caves) @@ -23119,6 +23701,12 @@ /obj/item/bananapeel, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/west_jungle) +"vJM" = ( +/obj/item/shard, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/comms) "vKc" = ( /obj/effect/landmark/crap_item, /turf/open/gm/grass/grass1, @@ -23130,6 +23718,16 @@ "vLO" = ( /turf/open/gm/dirtgrassborder/south, /area/lv624/lazarus/quartstorage/outdoors) +"vMC" = ( +/obj/structure/extinguisher_cabinet{ + pixel_y = 30 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "vMV" = ( /obj/effect/landmark/structure_spawner/xvx_hive/xeno_door, /obj/structure/blocker/forcefield/multitile_vehicles, @@ -23183,6 +23781,17 @@ /obj/structure/flora/bush/ausbushes/var3/sunnybush, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_east_caves) +"vRe" = ( +/obj/structure/surface/table, +/obj/structure/machinery/light, +/obj/item/tool/wrench, +/obj/item/tool/weldingtool, +/obj/effect/landmark/item_pool_spawner/survivor_ammo/buckshot, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "vSG" = ( /obj/structure/flora/jungle/vines/light_1, /turf/open/gm/grass/grass1, @@ -23242,6 +23851,12 @@ /obj/structure/flora/jungle/vines/heavy, /turf/open/gm/grass/grass1, /area/lv624/ground/jungle/north_west_jungle) +"vYL" = ( +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + icon_state = "dark" + }, +/area/lv624/lazarus/engineering) "vZT" = ( /obj/effect/landmark/objective_landmark/close, /turf/open/gm/dirt, @@ -23315,11 +23930,11 @@ /area/lv624/ground/caves/sand_temple) "whk" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_west_caves) @@ -23563,11 +24178,11 @@ /area/lv624/lazarus/landing_zones/lz2) "wHE" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = -1; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = -1; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/south_west_caves) @@ -23615,11 +24230,11 @@ /area/lv624/ground/jungle/west_central_jungle) "wMr" = ( /obj/item/reagent_container/food/snacks/grown/mushroom/glowshroom{ - pixel_x = 2; - pixel_y = 7; light_on = 1; light_range = 1; - light_system = 1 + light_system = 1; + pixel_x = 2; + pixel_y = 7 }, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/north_east_caves) @@ -23886,6 +24501,14 @@ /obj/structure/flora/jungle/vines/light_2, /turf/open/gm/grass/grass2, /area/lv624/ground/jungle/south_west_jungle) +"xfa" = ( +/obj/structure/reagent_dispensers/watertank, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "xfP" = ( /obj/item/stack/rods, /obj/item/shard, @@ -23970,6 +24593,14 @@ /obj/effect/landmark/lv624/fog_blocker, /turf/open/gm/dirt, /area/lv624/ground/river/west_river) +"xvz" = ( +/obj/structure/platform_decoration, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor{ + dir = 9; + icon_state = "brown" + }, +/area/lv624/lazarus/comms) "xvN" = ( /obj/structure/barricade/handrail/strata{ dir = 1 @@ -24274,6 +24905,16 @@ /obj/structure/blocker/forcefield/multitile_vehicles, /turf/open/auto_turf/strata_grass/layer1, /area/lv624/ground/caves/central_caves) +"ydX" = ( +/obj/structure/machinery/door/airlock/multi_tile/almayer/engidoor/glass/colony{ + name = "\improper Communications Dome"; + req_access_txt = "100"; + req_one_access = null + }, +/turf/open/floor{ + icon_state = "delivery" + }, +/area/lv624/lazarus/engineering) "yfe" = ( /obj/effect/decal/grass_overlay/grass1{ dir = 8 @@ -31164,7 +31805,7 @@ uiW sqs oTJ qKC -kWX +pKm hdh oKP aAp @@ -35989,7 +36630,7 @@ rAo fkJ fau xgE -efp +aWZ efp efp efp @@ -36218,7 +36859,7 @@ xgE sFY xgE xgE -eyb +bgL bgL bgL bgL @@ -37585,7 +38226,7 @@ xgE sxY sxY xgE -cPV +aRf cPV uSq qIO @@ -38959,8 +39600,8 @@ uSq qIO qIO qIO -qIO -qIO +ctS +ctS ntL kxI kxI @@ -39187,9 +39828,9 @@ uSq njC qIO qIO -qIO -qIO -ntL +ctS +sfg +vVe kxI jxG qZv @@ -39415,9 +40056,9 @@ uSq qIO qIO qIO -qIO -qIO -ntL +ctS +hLu +kxI kxI oAJ qZv @@ -39644,8 +40285,8 @@ aPT aUk aPT aPt -qIO -ntL +hLu +kxI kxI gzd eqs @@ -39868,9 +40509,9 @@ aPT aPT aPT aPt -aTG -aQn +vMC aQn +qNl aPt aPT aPT @@ -40092,17 +40733,17 @@ bwR cPV bCH aPT +aWy +aXd aQn -aON -aQn -aQn -aQn -aQn +aTi +aTi aQn aQn +aTi aQn isR -aSg +vRe aPT ooM nuU @@ -40325,12 +40966,12 @@ aQn aQn aQn aTH -aUl aQn aQn aQn aQn -aWX +aQn +uaz aPT kxI kxI @@ -40546,19 +41187,19 @@ byY fTf nhi cPV -cPV +aRf aPT -aRe -aSd -aSI -aTg +aWA aQn +aON +aTg +iYL aQn aQn aTg -aWd -aSd -aWY +hJh +aQn +aTi aPT kxI kxI @@ -40773,10 +41414,10 @@ ylL byY mun oGs -lud +oGs aPt aPt -aRf +euU aQn aTg aTg @@ -40785,8 +41426,8 @@ aQn aVb aTg aTg +aTG aQn -aWZ aPt aPt kxI @@ -41003,19 +41644,19 @@ byY byY byY aPN +cNE +aTi aQn aQn aQn -aSJ -aTh -aQn -aQn aQn -aTh aQn aQn +cQU +aTi aQn aQn +xfa aPT kxI lAX @@ -41233,9 +41874,9 @@ byY aPO aQn aQn +rkZ aQn -aQn -aQn +aTi vEj aTg rVH @@ -41458,20 +42099,20 @@ byY byY byY byY -aPN -aQn -aQn +aSK +aSI +aYh aQn aQn aTi -aQn -aQn -aQn aTi -aQn -aQn -aQn -aQn +qit +upp +jfN +jfN +gKN +xvz +elO aPT kxI lIU @@ -41688,16 +42329,16 @@ byY taK aPt aPt -aTI +aQn aQn aTg aTg -aTI -aQn -aVb +kKL +aXn +qap aTg aTg -aTG +cyP aXa aPt aPt @@ -41913,20 +42554,20 @@ uiN byY byY byY -fTf +aUl aPT aRi +aTi aQn aQn -aSK aTg aTJ -aQn -aQn +aZG +qNz aTg aWe -aWx -aZc +kvv +kvv aPT kxI kxI @@ -42141,19 +42782,19 @@ uiN byY byY byY -fTf +aUl aPT aSe +aWE +aXn aQn aQn -aQn -aQn -aQn +oiR aZK -aQn -aQn -aQn -aQn +qNz +kvv +kvv +kvv aZh aPT kxI @@ -42369,20 +43010,20 @@ uiN byY byY byY -fTf +aUl aPT -aQn +aSJ aTk +aXe aQn aQn -aTj -aQn -aQn -aQn -aTH -aQn aQn aQn +qNz +qez +kvv +fij +oIO aPT sBJ kxI @@ -42592,25 +43233,25 @@ aLv aJr iSg aJr -aJr +aCV aIO aMN aNA aMN aIO aPt -aTI -aQn -aQn +aWd +aSJ +aZG aQn -aPt aQn aQn aQn +qNz +kvv +dHr +bUc aPt -aPT -aPT -aPN aPt tzK kxI @@ -42826,20 +43467,20 @@ aMO aKB aMP aIO -aSg +aSd +aTj +aWX +eyb aQn aQn aQn aQn -aPt -aPT -aUk -aPT -aPt -aTK -aTK -aXd -aXn +nrK +kvv +kvv +qXt +tMP +kxI kxI tsa kxI @@ -43054,20 +43695,20 @@ aMP aNB aNJ aIO +aUt aIL -aIL -aIO +aWY aRe -aQn -aPT -aTK -aTK -aTK -aTK -aTK -aWy -aXe -aXn +tzP +bJe +ukS +aTi +hOo +vJM +kvv +pzP +lQP +kxI kxI kxI kxI @@ -43287,15 +43928,15 @@ aQo aJz aIO aSg -aPT -aTK +aPt aTK +oRY aSL +aWf aVB aWf -fFN aSL -aXn +sBJ kxI kxI lUc @@ -43514,14 +44155,14 @@ aEt aQp aRo aIO -aSg +kWX aSL aSL aSL aSL rmg aTq -aWA +aTq aSL aSL aVB @@ -43747,9 +44388,9 @@ aSL aVg tem aVd -aTM +kzp +aTq aTq -aZG aSX aZI aXJ @@ -43976,12 +44617,12 @@ ogR aUs nwR aTM -aTM -aTM +aTq +kzp aXf aTM aTM -aXW +mBH aVB kxI kxI @@ -44199,12 +44840,12 @@ aQs aQo aSi mkU -qGR +lud aWD aUq aTN aTM -aTM +fMa aUo aSX aZJ @@ -44427,13 +45068,13 @@ aQt aQo aSj qGR -mkU +jdL aWD aUq aTN udM aTM -aWE +aUx aSX aSX aSX @@ -44666,7 +45307,7 @@ aSX aZd aZx aXZ -aYh +vYL aVB kxI kxI @@ -44887,14 +45528,14 @@ aSL aVf jMk aWg -aTM -aTM +lzW +kyz aWF aSX -aXs +svv aTM aZM -aTM +vYL aXg kyN kxI @@ -45116,9 +45757,9 @@ aSX aSX aSX aXs +drX aTM -aTM -aXg +ydX aTM aVC aTM @@ -45341,12 +45982,12 @@ aJz aSL aTs aTP -aUt +bLH aSX aVF -aTM -aTM -aXg +iiO +uXT +oas aTM aTM aZf @@ -45572,12 +46213,12 @@ aTR aUu aSX aVG -aTM +vdl aWG aSX aXt aWi -aYc +bQf aVB dEp kxI @@ -45796,11 +46437,11 @@ xuk oUy aSL aTu -aZM -aTM +fLf +fQx aVj -aTM -aWi +mkZ +hDe aWH aSX aXu @@ -46025,7 +46666,7 @@ mNO fFN aTM aTM -aUx +fAs aSX aVH aTq @@ -46483,7 +47124,7 @@ aSm aSm oUy aSL -hHc +aVB aWf aVB aSL @@ -47594,7 +48235,7 @@ oUy aMD bbJ bbO -aCV +oUy aDS aDS aFh From 93026667e87faa4424fdba133d4e45eb76f5de39 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:52:12 +0000 Subject: [PATCH 002/162] Automatic changelog for PR #5674 [ci skip] --- html/changelogs/AutoChangeLog-pr-5674.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5674.yml diff --git a/html/changelogs/AutoChangeLog-pr-5674.yml b/html/changelogs/AutoChangeLog-pr-5674.yml new file mode 100644 index 000000000000..f47ae7028fca --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5674.yml @@ -0,0 +1,4 @@ +author: "SpartanBobby" +delete-after: True +changes: + - maptweak: "changes to LV624 Engineering and T-comms, mostly detailing but changes some doors and object placement" \ No newline at end of file From cda482d950b9364e3c6554a485c8b98674716614 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Thu, 8 Feb 2024 01:06:13 +0000 Subject: [PATCH 003/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5674.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5675.yml | 4 ---- html/changelogs/archive/2024-02.yml | 6 ++++++ 3 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5674.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5675.yml diff --git a/html/changelogs/AutoChangeLog-pr-5674.yml b/html/changelogs/AutoChangeLog-pr-5674.yml deleted file mode 100644 index f47ae7028fca..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5674.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SpartanBobby" -delete-after: True -changes: - - maptweak: "changes to LV624 Engineering and T-comms, mostly detailing but changes some doors and object placement" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5675.yml b/html/changelogs/AutoChangeLog-pr-5675.yml deleted file mode 100644 index 49b0fa10963b..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5675.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Megaddd" -delete-after: True -changes: - - imageadd: "added 4 bone-gel fill level sprite states" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 3c50da6c4d1f..7177625ef87d 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -115,3 +115,9 @@ Steelpoint: - rscadd: USCM Chestrig has been added to squad prep vendors, alternative sprite of a satchel, similar in appearance to the welder-chestrig. +2024-02-08: + Megaddd: + - imageadd: added 4 bone-gel fill level sprite states + SpartanBobby: + - maptweak: changes to LV624 Engineering and T-comms, mostly detailing but changes + some doors and object placement From 52b3076cad0a0e3b45f8719e273357c7429d7c7f Mon Sep 17 00:00:00 2001 From: Julian56 <117036822+Huffie56@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:59:55 +0100 Subject: [PATCH 004/162] refactor : turning newscasters into "props" (#5522) # About the pull request fallowing what TGMC did there https://github.com/tgstation/TerraGov-Marine-Corps/blob/master/code/game/objects/machinery/newscaster.dm # Explain why it's good for the game # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: refactor: turning newscasters into "props". /:cl: --------- Co-authored-by: Julien --- code/game/machinery/newscaster.dm | 920 +-------------------------- code/modules/admin/holder2.dm | 6 - code/modules/economy/economy_misc.dm | 21 - 3 files changed, 1 insertion(+), 946 deletions(-) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index d9aa42dbd1f1..96e1635bb0b8 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -1,742 +1,12 @@ -//############################################## -//################### NEWSCASTERS BE HERE! #### -//###-Agouri################################### - -/datum/feed_message - var/author ="" - var/body ="" - var/message_type ="Story" - //var/parent_channel - var/backup_body ="" - var/backup_author ="" - var/is_admin_message = 0 - var/icon/img = null - var/icon/backup_img - -/datum/feed_channel - var/channel_name="" - var/list/datum/feed_message/messages = list() - //var/message_count = 0 - var/locked=0 - var/author="" - var/backup_author="" - var/censored=0 - var/is_admin_channel=0 - //var/page = null //For newspapers - -/datum/feed_message/proc/clear() - src.author = "" - src.body = "" - src.backup_body = "" - src.backup_author = "" - src.img = null - src.backup_img = null - -/datum/feed_channel/proc/clear() - src.channel_name = "" - src.messages = list() - src.locked = 0 - src.author = "" - src.backup_author = "" - src.censored = 0 - src.is_admin_channel = 0 - -/datum/feed_network - var/list/datum/feed_channel/network_channels = list() - var/datum/feed_message/wanted_issue - -GLOBAL_DATUM_INIT(news_network, /datum/feed_network, new()) //The global news-network, which is coincidentally a global list. - -GLOBAL_LIST_INIT_TYPED(allCasters, /obj/structure/machinery/newscaster, list()) //Global list that will contain reference to all newscasters in existence. - /obj/structure/machinery/newscaster name = "newscaster" desc = "A standard Weyland-Yutani-licensed newsfeed handler for use in commercial space stations. All the news you absolutely have no use for, in one place!" icon = 'icons/obj/structures/machinery/terminals.dmi' icon_state = "newscaster_normal" - var/isbroken = FALSE //1 if someone banged it with something heavy - var/ispowered = TRUE //starts powered, changes with power_change() - //var/list/datum/feed_channel/channel_list = list() //This list will contain the names of the feed channels. Each name will refer to a data region where the messages of the feed channels are stored. - //OBSOLETE: We're now using a global news network - var/screen = 0 //Or maybe I'll make it into a list within a list afterwards... whichever I prefer, go fuck yourselves :3 - // 0 = welcome screen - main menu - // 1 = view feed channels - // 2 = create feed channel - // 3 = create feed story - // 4 = feed story submited sucessfully - // 5 = feed channel created successfully - // 6 = ERROR: Cannot create feed story - // 7 = ERROR: Cannot create feed channel - // 8 = print newspaper - // 9 = viewing channel feeds - // 10 = censor feed story - // 11 = censor feed channel - //Holy shit this is outdated, made this when I was still starting newscasters :3 - var/paper_remaining = 0 - var/securityCaster = 0 - // 0 = Caster cannot be used to issue wanted posters - // 1 = the opposite - var/unit_no = 0 //Each newscaster has a unit number - //var/datum/feed_message/wanted //We're gonna use a feed_message to store data of the wanted person because fields are similar - //var/wanted_issue = 0 //OBSOLETE - // 0 = there's no WANTED issued, we don't need a special icon_state - // 1 = Guess what. - var/alert_delay = 500 - var/alert = 0 - // 0 = there hasn't been a news/wanted update in the last alert_delay - // 1 = there has - var/scanned_user = "Unknown" //Will contain the name of the person who currently uses the newscaster - var/msg = ""; //Feed message - var/obj/item/photo/photo = null - var/channel_name = ""; //the feed channel which will be receiving the feed, or being created - var/c_locked=0; //Will our new channel be locked to public submissions? - var/hitstaken = 0 //Death at 3 hits from an item with force>=15 - var/datum/feed_channel/viewing_channel = null anchored = TRUE - -/obj/structure/machinery/newscaster/security_unit //Security unit +/obj/structure/machinery/newscaster/security_unit name = "Security Newscaster" - securityCaster = 1 - -/obj/structure/machinery/newscaster/security_unit/New() //Constructor, ho~ - GLOB.allCasters += src - src.paper_remaining = 15 // Will probably change this to something better - for(var/obj/structure/machinery/newscaster/NEWSCASTER in GLOB.allCasters) // Let's give it an appropriate unit number - src.unit_no++ - src.update_icon() //for any custom ones on the map... - ..() //I just realised the newscasters weren't in the global machines list. The superconstructor call will tend to that - -/obj/structure/machinery/newscaster/security_unit/Destroy() - GLOB.allCasters -= src - return ..() - -/obj/structure/machinery/newscaster/update_icon() - if(!ispowered || isbroken) - icon_state = "newscaster_off" - if(isbroken) //If the thing is smashed, add crack overlay on top of the unpowered sprite. - src.overlays.Cut() - src.overlays += image(src.icon, "crack3") - return - - src.overlays.Cut() //reset overlays - - if(GLOB.news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message - icon_state = "newscaster_wanted" - return - - if(alert) //new message alert overlay - src.overlays += "newscaster_alert" - - if(hitstaken > 0) //Cosmetic damage overlay - src.overlays += image(src.icon, "crack[hitstaken]") - - icon_state = "newscaster_normal" - return - -/obj/structure/machinery/newscaster/power_change() - if(isbroken) //Broken shit can't be powered. - return - ..() - if( !(stat & NOPOWER) ) - src.ispowered = TRUE - src.update_icon() - else - spawn(rand(0, 15)) - src.ispowered = FALSE - src.update_icon() - - -/obj/structure/machinery/newscaster/ex_act(severity) - switch(severity) - if(0 to EXPLOSION_THRESHOLD_LOW) - if(prob(50)) - src.isbroken=1 - src.update_icon() - return - if(EXPLOSION_THRESHOLD_LOW to EXPLOSION_THRESHOLD_MEDIUM) - src.isbroken=1 - if(prob(50)) - deconstruct(FALSE) - else - src.update_icon() //can't place it above the return and outside the if-else. or we might get runtimes of null.update_icon() if(prob(50)) goes in. - return - if(EXPLOSION_THRESHOLD_MEDIUM to INFINITY) - deconstruct(FALSE) - return - return - -/obj/structure/machinery/newscaster/attack_remote(mob/user as mob) - return src.attack_hand(user) - -/obj/structure/machinery/newscaster/attack_hand(mob/user as mob) //########### THE MAIN BEEF IS HERE! And in the proc below this...############ - if(!src.ispowered || src.isbroken) - return - if(istype(user, /mob/living/carbon/human) || istype(user,/mob/living/silicon) ) - var/mob/living/human_or_robot_user = user - var/dat - dat = text("Newscaster

Newscaster Unit #[src.unit_no]

") - - src.scan_user(human_or_robot_user) //Newscaster scans you - - switch(screen) - if(0) - dat += "Welcome to Newscasting Unit #[src.unit_no].
Interface & News networks Operational." - dat += "
Property of Weyland-Yutani" - if(GLOB.news_network.wanted_issue) - dat+= "
Read Wanted Issue" - dat+= "

Create Feed Channel" - dat+= "
View Feed Channels" - dat+= "
Submit new Feed story" - dat+= "
Print newspaper" - dat+= "
Re-scan User" - dat+= "

Exit" - if(src.securityCaster) - var/wanted_already = 0 - if(GLOB.news_network.wanted_issue) - wanted_already = 1 - - dat+="
Feed Security functions:
" - dat+="
[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue" - dat+="
Censor Feed Stories" - dat+="
Mark Feed Channel with Weyland-Yutani D-Notice" - dat+="

The newscaster recognises you as: [src.scanned_user]" - if(1) - dat+= "Station Feed Channels
" - if(!length(GLOB.news_network.network_channels) ) - dat+="No active channels found..." - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - if(CHANNEL.is_admin_channel) - dat+="[CHANNEL.channel_name]
" - else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
" - /*for(var/datum/feed_channel/CHANNEL in src.channel_list) - dat+="[CHANNEL.channel_name]:
\[created by: [CHANNEL.author]\]

" - if(!length(CHANNEL.messages) ) - dat+="No feed messages found in channel...

" - else - for(var/datum/feed_message/MESSAGE in CHANNEL.messages) - dat+="-[MESSAGE.body]
\[[MESSAGE.message_type] by [MESSAGE.author]\]
"*/ - - dat+="

Refresh" - dat+="
Back" - if(2) - dat+="Creating new Feed Channel..." - dat+="
Channel Name: [src.channel_name]
" - dat+="Channel Author: [src.scanned_user]
" - dat+="Will Accept Public Feeds: [(src.c_locked) ? ("NO") : ("YES")]

" - dat+="
Submit

Cancel
" - if(3) - dat+="Creating new Feed Message..." - dat+="
Receiving Channel: [src.channel_name]
" //MARK - dat+="Message Author: [src.scanned_user]
" - dat+="Message Body: [src.msg]
" - dat+="Attach Photo: [(src.photo ? "Photo Attached" : "No Photo")]
" - dat+="
Submit

Cancel
" - if(4) - dat+="Feed story successfully submitted to [src.channel_name].

" - dat+="
Return
" - if(5) - dat+="Feed Channel [src.channel_name] created successfully.

" - dat+="
Return
" - if(6) - dat+="ERROR: Could not submit Feed story to Network.

" - if(src.channel_name=="") - dat+="�Invalid receiving channel name.
" - if(src.scanned_user=="Unknown") - dat+="�Channel author unverified.
" - if(src.msg == "" || src.msg == "\[REDACTED\]") - dat+="�Invalid message body.
" - - dat+="
Return
" - if(7) - dat+="ERROR: Could not submit Feed Channel to Network.

" - //var/list/existing_channels = list() //Let's get dem existing channels - OBSOLETE - var/list/existing_authors = list() - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - //existing_channels += FC.channel_name //OBSOLETE - if(FC.author == "\[REDACTED\]") - existing_authors += FC.backup_author - else - existing_authors += FC.author - if(src.scanned_user in existing_authors) - dat+="�There already exists a Feed channel under your name.
" - if(src.channel_name=="" || src.channel_name == "\[REDACTED\]") - dat+="�Invalid channel name.
" - var/check = 0 - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - if(FC.channel_name == src.channel_name) - check = 1 - break - if(check) - dat+="�Channel name already in use.
" - if(src.scanned_user=="Unknown") - dat+="�Channel author unverified.
" - dat+="
Return
" - if(8) - var/total_num=length(GLOB.news_network.network_channels) - var/active_num=total_num - var/message_num=0 - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - if(!FC.censored) - message_num += length(FC.messages) //Dont forget, datum/feed_channel's var messages is a list of datum/feed_message - else - active_num-- - dat+="Network currently serves a total of [total_num] Feed channels, [active_num] of which are active, and a total of [message_num] Feed Stories." //TODO: CONTINUE - dat+="

Liquid Paper remaining: [(src.paper_remaining) *100 ] cm^3" - dat+="

Print Paper" - dat+="
Cancel" - if(9) - dat+="[src.viewing_channel.channel_name]: \[created by: [src.viewing_channel.author]\]
" - if(src.viewing_channel.censored) - dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Weyland-Yutani D-Notice.
" - dat+="No further feed story additions are allowed while the D-Notice is in effect.

" - else - if(!length(src.viewing_channel.messages) ) - dat+="No feed messages found in channel...
" - else - var/i = 0 - for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages) - i++ - dat+="-[MESSAGE.body]
" - if(MESSAGE.img) - usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - dat+="

" - dat+="\[[MESSAGE.message_type] by [MESSAGE.author]\]
" - dat+="

Refresh" - dat+="
Back" - if(10) - dat+="Weyland-Yutani Feed Censorship Tool
" - dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
" - dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
" - dat+="
Select Feed channel to get Stories from:
" - if(!length(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
" - dat+="
Cancel" - if(11) - dat+="Weyland-Yutani D-Notice Handler
" - dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's" - dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed" - dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
" - if(!length(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
" - - dat+="
Back" - if(12) - dat+="[src.viewing_channel.channel_name]: \[ created by: [src.viewing_channel.author] \]
" - dat+="[(src.viewing_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
" - - - if(!length(src.viewing_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages) - dat+="-[MESSAGE.body]
\[[MESSAGE.message_type] by [MESSAGE.author]\]
" - dat+="[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
" - dat+="
Back" - if(13) - dat+="[src.viewing_channel.channel_name]: \[ created by: [src.viewing_channel.author] \]
" - dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
" - if(src.viewing_channel.censored) - dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Weyland-Yutani D-Notice.
" - dat+="No further feed story additions are allowed while the D-Notice is in effect.

" - else - if(!length(src.viewing_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages) - dat+="-[MESSAGE.body]
\[[MESSAGE.message_type] by [MESSAGE.author]\]
" - - dat+="
Back" - if(14) - dat+="Wanted Issue Handler:" - var/wanted_already = 0 - var/end_param = 1 - if(GLOB.news_network.wanted_issue) - wanted_already = 1 - end_param = 2 - - if(wanted_already) - dat+="
A wanted issue is already in Feed Circulation. You can edit or cancel it below.
" - dat+="
" - dat+="Criminal Name: [src.channel_name]
" - dat+="Description: [src.msg]
" - dat+="Attach Photo: [(src.photo ? "Photo Attached" : "No Photo")]
" - if(wanted_already) - dat+="Wanted Issue created by: [GLOB.news_network.wanted_issue.backup_author]
" - else - dat+="Wanted Issue will be created under prosecutor: [src.scanned_user]
" - dat+="
[(wanted_already) ? ("Edit Issue") : ("Submit")]" - if(wanted_already) - dat+="
Take down Issue" - dat+="
Cancel" - if(15) - dat+="Wanted issue for [src.channel_name] is now in Network Circulation.

" - dat+="
Return
" - if(16) - dat+="ERROR: Wanted Issue rejected by Network.

" - if(src.channel_name=="" || src.channel_name == "\[REDACTED\]") - dat+="�Invalid name for person wanted.
" - if(src.scanned_user=="Unknown") - dat+="�Issue author unverified.
" - if(src.msg == "" || src.msg == "\[REDACTED\]") - dat+="�Invalid description.
" - dat+="
Return
" - if(17) - dat+="Wanted Issue successfully deleted from Circulation
" - dat+="
Return
" - if(18) - dat+="-- STATIONWIDE WANTED ISSUE --
\[Submitted by: [GLOB.news_network.wanted_issue.backup_author]\]
" - dat+="Criminal: [GLOB.news_network.wanted_issue.author]
" - dat+="Description: [GLOB.news_network.wanted_issue.body]
" - dat+="Photo:: " - if(GLOB.news_network.wanted_issue.img) - usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") - dat+="
" - else - dat+="None" - dat+="

Back
" - if(19) - dat+="Wanted issue for [src.channel_name] successfully edited.

" - dat+="
Return
" - if(20) - dat+="Printing successful. Please receive your newspaper from the bottom of the machine.

" - dat+="Return" - if(21) - dat+="Unable to print newspaper. Insufficient paper. Please notify maintenance personnel to refill machine storage.

" - dat+="Return" - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - - - human_or_robot_user << browse(dat, "window=newscaster_main;size=400x600") - onclose(human_or_robot_user, "newscaster_main") - - /*if(src.isbroken) //debugging shit - return - src.hitstaken++ - if(src.hitstaken==3) - src.isbroken = TRUE - src.update_icon()*/ - - -/obj/structure/machinery/newscaster/Topic(href, href_list) - if(..()) - return - if ((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (isRemoteControlling(usr))) - usr.set_interaction(src) - if(href_list["set_channel_name"]) - src.channel_name = strip_html(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) - while (findtext(src.channel_name," ") == 1) - src.channel_name = copytext(src.channel_name,2,length(src.channel_name)+1) - src.updateUsrDialog() - //src.update_icon() - - else if(href_list["set_channel_lock"]) - src.c_locked = !src.c_locked - src.updateUsrDialog() - //src.update_icon() - - else if(href_list["submit_new_channel"]) - //var/list/existing_channels = list() //OBSOLETE - var/list/existing_authors = list() - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - //existing_channels += FC.channel_name - if(FC.author == "\[REDACTED\]") - existing_authors += FC.backup_author - else - existing_authors +=FC.author - var/check = 0 - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - if(FC.channel_name == src.channel_name) - check = 1 - break - if(src.channel_name == "" || src.channel_name == "\[REDACTED\]" || src.scanned_user == "Unknown" || check || (src.scanned_user in existing_authors) ) - src.screen=7 - else - var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") - if(choice=="Confirm") - var/datum/feed_channel/newChannel = new /datum/feed_channel - newChannel.channel_name = src.channel_name - newChannel.author = src.scanned_user - newChannel.locked = c_locked - GLOB.news_network.network_channels += newChannel //Adding channel to the global network - src.screen=5 - src.updateUsrDialog() - //src.update_icon() - - else if(href_list["set_channel_receiving"]) - //var/list/datum/feed_channel/available_channels = list() - var/list/available_channels = list() - for(var/datum/feed_channel/F in GLOB.news_network.network_channels) - if( (!F.locked || F.author == scanned_user) && !F.censored) - available_channels += F.channel_name - src.channel_name = strip_html(tgui_input_list(usr, "Choose receiving Feed Channel", "Network Channel Handler", available_channels )) - src.updateUsrDialog() - - else if(href_list["set_new_message"]) - src.msg = strip_html(input(usr, "Write your Feed story", "Network Channel Handler", "")) - while (findtext(src.msg," ") == 1) - src.msg = copytext(src.msg,2,length(src.msg)+1) - src.updateUsrDialog() - - else if(href_list["set_attachment"]) - AttachPhoto(usr) - src.updateUsrDialog() - - else if(href_list["submit_new_message"]) - if(src.msg =="" || src.msg=="\[REDACTED\]" || src.scanned_user == "Unknown" || src.channel_name == "" ) - src.screen=6 - else - var/datum/feed_message/newMsg = new /datum/feed_message - newMsg.author = src.scanned_user - newMsg.body = src.msg - if(photo) - newMsg.img = photo.img - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - if(FC.channel_name == src.channel_name) - FC.messages += newMsg //Adding message to the network's appropriate feed_channel - break - src.screen=4 - for(var/obj/structure/machinery/newscaster/NEWSCASTER in GLOB.allCasters) - NEWSCASTER.newsAlert(src.channel_name) - - src.updateUsrDialog() - - else if(href_list["create_channel"]) - src.screen=2 - src.updateUsrDialog() - - else if(href_list["create_feed_story"]) - src.screen=3 - src.updateUsrDialog() - - else if(href_list["menu_paper"]) - src.screen=8 - src.updateUsrDialog() - else if(href_list["print_paper"]) - if(!src.paper_remaining) - src.screen=21 - else - src.print_paper() - src.screen = 20 - src.updateUsrDialog() - - else if(href_list["menu_censor_story"]) - src.screen=10 - src.updateUsrDialog() - - else if(href_list["menu_censor_channel"]) - src.screen=11 - src.updateUsrDialog() - - else if(href_list["menu_wanted"]) - var/already_wanted = 0 - if(GLOB.news_network.wanted_issue) - already_wanted = 1 - - if(already_wanted) - src.channel_name = GLOB.news_network.wanted_issue.author - src.msg = GLOB.news_network.wanted_issue.body - src.screen = 14 - src.updateUsrDialog() - - else if(href_list["set_wanted_name"]) - src.channel_name = strip_html(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) - while (findtext(src.channel_name," ") == 1) - src.channel_name = copytext(src.channel_name,2,length(src.channel_name)+1) - src.updateUsrDialog() - - else if(href_list["set_wanted_desc"]) - src.msg = strip_html(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) - while (findtext(src.msg," ") == 1) - src.msg = copytext(src.msg,2,length(src.msg)+1) - src.updateUsrDialog() - - else if(href_list["submit_wanted"]) - var/input_param = text2num(href_list["submit_wanted"]) - if(src.msg == "" || src.channel_name == "" || src.scanned_user == "Unknown") - src.screen = 16 - else - var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below - var/datum/feed_message/WANTED = new /datum/feed_message - WANTED.author = src.channel_name - WANTED.body = src.msg - WANTED.backup_author = src.scanned_user //I know, a bit wacky - if(photo) - WANTED.img = photo.img - GLOB.news_network.wanted_issue = WANTED - for(var/obj/structure/machinery/newscaster/NEWSCASTER in GLOB.allCasters) - NEWSCASTER.newsAlert() - NEWSCASTER.update_icon() - src.screen = 15 - else - if(GLOB.news_network.wanted_issue.is_admin_message) - alert("The wanted issue has been distributed by a Weyland-Yutani higherup. You cannot edit it.","Ok") - return - GLOB.news_network.wanted_issue.author = src.channel_name - GLOB.news_network.wanted_issue.body = src.msg - GLOB.news_network.wanted_issue.backup_author = src.scanned_user - if(photo) - GLOB.news_network.wanted_issue.img = photo.img - src.screen = 19 - - src.updateUsrDialog() - - else if(href_list["cancel_wanted"]) - if(GLOB.news_network.wanted_issue.is_admin_message) - alert("The wanted issue has been distributed by a Weyland-Yutani higherup. You cannot take it down.","Ok") - return - var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - GLOB.news_network.wanted_issue = null - for(var/obj/structure/machinery/newscaster/NEWSCASTER in GLOB.allCasters) - NEWSCASTER.update_icon() - src.screen=17 - src.updateUsrDialog() - - else if(href_list["view_wanted"]) - src.screen=18 - src.updateUsrDialog() - else if(href_list["censor_channel_author"]) - var/datum/feed_channel/FC = locate(href_list["censor_channel_author"]) - if(FC.is_admin_channel) - alert("This channel was created by a Weyland-Yutani Officer. You cannot censor it.","Ok") - return - if(FC.author != "\[REDACTED\]") - FC.backup_author = FC.author - FC.author = "\[REDACTED\]" - else - FC.author = FC.backup_author - src.updateUsrDialog() - - else if(href_list["censor_channel_story_author"]) - var/datum/feed_message/MSG = locate(href_list["censor_channel_story_author"]) - if(MSG.is_admin_message) - alert("This message was created by a Weyland-Yutani Officer. You cannot censor its author.","Ok") - return - if(MSG.author != "\[REDACTED\]") - MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" - else - MSG.author = MSG.backup_author - src.updateUsrDialog() - - else if(href_list["censor_channel_story_body"]) - var/datum/feed_message/MSG = locate(href_list["censor_channel_story_body"]) - if(MSG.is_admin_message) - alert("This channel was created by a Weyland-Yutani Officer. You cannot censor it.","Ok") - return - if(MSG.img != null) - MSG.backup_img = MSG.img - MSG.img = null - else - MSG.img = MSG.backup_img - if(MSG.body != "\[REDACTED\]") - MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" - else - MSG.body = MSG.backup_body - src.updateUsrDialog() - - else if(href_list["pick_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["pick_d_notice"]) - src.viewing_channel = FC - src.screen=13 - src.updateUsrDialog() - - else if(href_list["toggle_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["toggle_d_notice"]) - if(FC.is_admin_channel) - alert("This channel was created by a Weyland-Yutani Officer. You cannot place a D-Notice upon it.","Ok") - return - FC.censored = !FC.censored - src.updateUsrDialog() - - else if(href_list["view"]) - src.screen=1 - src.updateUsrDialog() - - else if(href_list["setScreen"]) //Brings us to the main menu and resets all fields~ - src.screen = text2num(href_list["setScreen"]) - if (src.screen == 0) - src.scanned_user = "Unknown"; - msg = ""; - src.c_locked=0; - channel_name=""; - src.viewing_channel = null - src.updateUsrDialog() - - else if(href_list["show_channel"]) - var/datum/feed_channel/FC = locate(href_list["show_channel"]) - src.viewing_channel = FC - src.screen = 9 - src.updateUsrDialog() - - else if(href_list["pick_censor_channel"]) - var/datum/feed_channel/FC = locate(href_list["pick_censor_channel"]) - src.viewing_channel = FC - src.screen = 12 - src.updateUsrDialog() - - else if(href_list["refresh"]) - src.updateUsrDialog() - - -/obj/structure/machinery/newscaster/attackby(obj/item/I as obj, mob/user as mob) - - if (src.isbroken) - playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 25, 1) - for (var/mob/O in hearers(5, src.loc)) - O.show_message("[user.name] further abuses the shattered [src.name].", SHOW_MESSAGE_VISIBLE) - else - if(!(I.flags_item & NOBLUDGEON) && I.force) - if(I.force <15) - for (var/mob/O in hearers(5, src.loc)) - O.show_message("[user.name] hits the [src.name] with the [I.name] with no visible effect.", SHOW_MESSAGE_VISIBLE) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 25, 1) - else - src.hitstaken++ - if(src.hitstaken==3) - for (var/mob/O in hearers(5, src.loc)) - O.show_message("[user.name] smashes the [src.name]!", SHOW_MESSAGE_VISIBLE) - src.isbroken=1 - playsound(src.loc, 'sound/effects/Glassbr3.ogg', 50, 1) - else - for (var/mob/O in hearers(5, src.loc)) - O.show_message("[user.name] forcefully slams the [src.name] with the [I.name]!", SHOW_MESSAGE_VISIBLE) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 25, 1) - else - to_chat(user, "This does nothing.") - src.update_icon() - -/obj/structure/machinery/newscaster/attack_remote(mob/user as mob) - return src.attack_hand(user) //or maybe it'll have some special functions? No idea. - -/obj/structure/machinery/newscaster/proc/AttachPhoto(mob/user as mob) - if(photo) - if(!isRemoteControlling(user)) - photo.forceMove(loc) - user.put_in_inactive_hand(photo) - photo = null - var/obj/item/photo/PH = user.get_active_hand() - if(istype(PH)) - if(user.drop_inv_item_to_loc(photo, src)) - photo = PH - - -//######################################################################################################################## -//###################################### NEWSPAPER! ###################################################################### -//######################################################################################################################## /obj/item/newspaper name = "newspaper" @@ -745,191 +15,3 @@ GLOBAL_LIST_INIT_TYPED(allCasters, /obj/structure/machinery/newscaster, list()) icon_state = "newspaper" w_class = SIZE_TINY //Let's make it fit in trashbags! attack_verb = list("bapped") - var/screen = 0 - var/pages = 0 - var/curr_page = 0 - var/list/datum/feed_channel/news_content = list() - var/datum/feed_message/important_message = null - var/scribble="" - var/scribble_page = null - -/obj/item/newspaper/attack_self(mob/user as mob) - ..() - if(!ishuman(user)) - to_chat(user, "The paper is full of intelligible symbols!") - return - - var/mob/living/carbon/human/human_user = user - var/dat - src.pages = 0 - switch(screen) - if(0) //Cover - dat+="
The Griffon
" - dat+="
Weyland-Yutani-standard newspaper, for use on Weyland-Yutani� Space Facilities

" - if(!length(src.news_content)) - if(src.important_message) - dat+="Contents:
    **Important Security Announcement** \[page [src.pages+2]\]
" - else - dat+="Other than the title, the rest of the newspaper is unprinted..." - else - dat+="Contents:
    " - for(var/datum/feed_channel/NP in src.news_content) - src.pages++ - if(src.important_message) - dat+="**Important Security Announcement** \[page [src.pages+2]\]
    " - var/temp_page=0 - for(var/datum/feed_channel/NP in src.news_content) - temp_page++ - dat+="[NP.channel_name] \[page [temp_page+1]\]
    " - dat+="
" - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[src.scribble]\"" - dat+= "
Next Page
Done reading
" - if(1) // X channel pages inbetween. - for(var/datum/feed_channel/NP in src.news_content) - src.pages++ //Let's get it right again. - var/datum/feed_channel/C = src.news_content[src.curr_page] - dat+="[C.channel_name] \[created by: [C.author]\]

" - if(C.censored) - dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. Its contents were not transferred to the newspaper at the time of printing." - else - if(!length(C.messages)) - dat+="No Feed stories stem from this channel..." - else - dat+="
    " - var/i = 0 - for(var/datum/feed_message/MESSAGE in C.messages) - i++ - dat+="-[MESSAGE.body]
    " - if(MESSAGE.img) - user << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - dat+="
    " - dat+="\[[MESSAGE.message_type] by [MESSAGE.author]\]

    " - dat+="
" - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[src.scribble]\"" - dat+= "

Previous Page
Next Page
" - if(2) //Last page - for(var/datum/feed_channel/NP in src.news_content) - src.pages++ - if(src.important_message!=null) - dat+="
Wanted Issue:


" - dat+="Criminal name: [important_message.author]
" - dat+="Description: [important_message.body]
" - dat+="Photo:: " - if(important_message.img) - user << browse_rsc(important_message.img, "tmp_photow.png") - dat+="
" - else - dat+="None" - else - dat+="Apart from some uninteresting Classified ads, there's nothing on this page..." - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[src.scribble]\"" - dat+= "
Previous Page
" - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - - dat+="

[src.curr_page+1]
" - human_user << browse(dat, "window=newspaper_main;size=300x400") - onclose(human_user, "newspaper_main") - - -/obj/item/newspaper/Topic(href, href_list) - var/mob/living/U = usr - ..() - if ((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) )) - U.set_interaction(src) - if(href_list["next_page"]) - if(curr_page==src.pages+1) - return //Don't need that at all, but anyway. - if(src.curr_page == src.pages) //We're at the middle, get to the end - src.screen = 2 - else - if(curr_page == 0) //We're at the start, get to the middle - src.screen=1 - src.curr_page++ - playsound(src.loc, "pageturn", 15, 1) - - else if(href_list["prev_page"]) - if(curr_page == 0) - return - if(curr_page == 1) - src.screen = 0 - - else - if(curr_page == src.pages+1) //we're at the end, let's go back to the middle. - src.screen = 1 - src.curr_page-- - playsound(src.loc, "pageturn", 15, 1) - - if (istype(src.loc, /mob)) - src.attack_self(src.loc) - - -/obj/item/newspaper/attackby(obj/item/W as obj, mob/user as mob) - if(HAS_TRAIT(W, TRAIT_TOOL_PEN)) - if(src.scribble_page == src.curr_page) - to_chat(user, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?") - else - var/s = strip_html( input(user, "Write something", "Newspaper", "") ) - s = strip_html(s) - if (!s) - return - if (!in_range(src, usr) && src.loc != usr) - return - src.scribble_page = src.curr_page - src.scribble = s - src.attack_self(user) - return - - -////////////////////////////////////helper procs - - -/obj/structure/machinery/newscaster/proc/scan_user(mob/living/user as mob) - if(istype(user,/mob/living/carbon/human)) //User is a human - var/mob/living/carbon/human/human_user = user - if(human_user.wear_id) //Newscaster scans you - if(istype(human_user.wear_id, /obj/item/card/id) ) - var/obj/item/card/id/ID = human_user.wear_id - src.scanned_user ="[ID.registered_name] ([ID.assignment])" - else - src.scanned_user ="Unknown" - else - src.scanned_user ="Unknown" - else - var/mob/living/silicon/ai_user = user - src.scanned_user = "[ai_user.name] ([ai_user.job])" - - -/obj/structure/machinery/newscaster/proc/print_paper() - var/obj/item/newspaper/NEWSPAPER = new /obj/item/newspaper - for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) - NEWSPAPER.news_content += FC - if(GLOB.news_network.wanted_issue) - NEWSPAPER.important_message = GLOB.news_network.wanted_issue - NEWSPAPER.forceMove(get_turf(src)) - src.paper_remaining-- - return - -//Removed for now so these aren't even checked every tick. Left this here in-case Agouri needs it later. -///obj/structure/machinery/newscaster/process() //Was thinking of doing the icon update through process, but multiple iterations per second does not -// return //bode well with a newscaster network of 10+ machines. Let's just return it, as it's added in the machines list. - -/obj/structure/machinery/newscaster/proc/newsAlert(channel) //This isn't Agouri's work, for it is ugly and vile. - var/turf/T = get_turf(src) //Who the fuck uses spawn(600) anyway, jesus christ - if(channel) - for(var/mob/O in hearers(GLOB.world_view_size-1, T)) - O.show_message(SPAN_NEWSCASTER("[src.name] beeps, \"Breaking news from [channel]!\""), SHOW_MESSAGE_AUDIBLE) - src.alert = 1 - src.update_icon() - spawn(30 SECONDS) - src.alert = 0 - src.update_icon() - playsound(src.loc, 'sound/machines/twobeep.ogg', 25, 1) - else - for(var/mob/O in hearers(GLOB.world_view_size-1, T)) - O.show_message(SPAN_NEWSCASTER("[src.name] beeps, \"Attention! Wanted issue distributed!\""), SHOW_MESSAGE_AUDIBLE) - playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 25, 1) - return diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 82f25a6ebe3e..3a3ce661e231 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -10,11 +10,6 @@ GLOBAL_PROTECT(href_token) var/rights = 0 var/fakekey = null - var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description - var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as admin_holders. - var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel - var/admincaster_signature //What you'll sign the newsfeeds as - var/href_token var/datum/marked_datum @@ -31,7 +26,6 @@ GLOBAL_PROTECT(href_token) error("Admin datum created without a ckey argument. Datum has been deleted") qdel(src) return - admincaster_signature = "Weyland-Yutani Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" rank = initial_rank rights = initial_rights href_token = GenerateToken() diff --git a/code/modules/economy/economy_misc.dm b/code/modules/economy/economy_misc.dm index a5f061e1a727..6e22fd12d58a 100644 --- a/code/modules/economy/economy_misc.dm +++ b/code/modules/economy/economy_misc.dm @@ -75,27 +75,6 @@ GLOBAL_VAR_INIT(economy_init, FALSE) if(GLOB.economy_init) return 2 - var/datum/feed_channel/newChannel = new /datum/feed_channel - newChannel.channel_name = "Public Station Announcements" - newChannel.author = "Automated Announcement Listing" - newChannel.locked = 1 - newChannel.is_admin_channel = 1 - GLOB.news_network.network_channels += newChannel - - newChannel = new /datum/feed_channel - newChannel.channel_name = "Nyx Daily" - newChannel.author = "CentComm Minister of Information" - newChannel.locked = 1 - newChannel.is_admin_channel = 1 - GLOB.news_network.network_channels += newChannel - - newChannel = new /datum/feed_channel - newChannel.channel_name = "The Gibson Gazette" - newChannel.author = "Editor Mike Hammers" - newChannel.locked = 1 - newChannel.is_admin_channel = 1 - GLOB.news_network.network_channels += newChannel - for(var/loc_type in typesof(/datum/trade_destination) - /datum/trade_destination) var/datum/trade_destination/D = new loc_type GLOB.weighted_randomevent_locations[D] = D.viable_random_events.len From b5957258928d82386860a1b6c39230a1e41240ef Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:09:12 +0000 Subject: [PATCH 005/162] Automatic changelog for PR #5522 [ci skip] --- html/changelogs/AutoChangeLog-pr-5522.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5522.yml diff --git a/html/changelogs/AutoChangeLog-pr-5522.yml b/html/changelogs/AutoChangeLog-pr-5522.yml new file mode 100644 index 000000000000..649a9a6b44db --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5522.yml @@ -0,0 +1,4 @@ +author: "Huffie56" +delete-after: True +changes: + - refactor: "turning newscasters into \"props\"." \ No newline at end of file From 94510505c4fea5724812fefa7091c8a988a3801a Mon Sep 17 00:00:00 2001 From: Julian56 <117036822+Huffie56@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:18:08 +0100 Subject: [PATCH 006/162] added squads marine ability to buy more sidearm magazines and moved/changed some section in vendors around. (#5605) # About the pull request 1-Main idea is to give players more option when they are equipping... 2-while i was at it i moved some section in team leader vendor to be more in line with other squad marine vendors... 3-concerning the price i fallowed what was already in place. i preserved the old imbalance that is between different roles concerning ammo prices... took as a model the intelligence officer vendor concerning prices of new magazine etc... # Explain why it's good for the game More option is always good. my main concern would be balance but since those magazine are available freely in limited quantity i don't think it would cause an issue... i think it also will help players to know where things are... # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: qol: added two new section to replace ammunition section primary ammunition and sidearm ammunition and rearranged it for team leader. balance: added the ability for comtech, medic, and squad leader to buy M4A3 (HP and AP) and VP78 magazines for 3 points and M44 Heavy for 6 points. balance: added the ability for SG to buy M4A3 (HP and AP) and VP78 magazines for 5 points and M44 Heavy for 10 points. balance: added the ability for rifleman to buy M4A3 (HP and AP) and VP78 magazines for 5 points. /:cl: Co-authored-by: Julien --- .../vendor_types/squad_prep/squad_engineer.dm | 8 ++++++- .../vendor_types/squad_prep/squad_leader.dm | 10 +++++++- .../vendor_types/squad_prep/squad_medic.dm | 8 ++++++- .../vendor_types/squad_prep/squad_rifleman.dm | 7 +++++- .../squad_prep/squad_smartgunner.dm | 6 +++++ .../vendor_types/squad_prep/squad_tl.dm | 24 ++++++++++--------- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm index 6d015c203bd7..4053b9294c13 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm @@ -36,13 +36,19 @@ GLOBAL_LIST_INIT(cm_vending_gear_engi, list( list("M20 Mine Box (x4 mines)", 18, /obj/item/storage/box/explosive_mines, null, VENDOR_ITEM_REGULAR), list("M40 MFHS Metal Foam Grenade", 5, /obj/item/explosive/grenade/metal_foam, null, VENDOR_ITEM_REGULAR), - list("AMMUNITION", 0, null, null, null), + list("PRIMARY AMMUNITION", 0, null, null, null), list("M4RA AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), list("M39 AP Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), list("M39 Extended Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), list("M41A AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), list("M41A Extended Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), + list("SIDEARM AMMUNITION", 0, null, null, null), + list("M44 Heavy Speed Loader (.44)", 6, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 3, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 3, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 3, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), + list("ARMORS", 0, null, null, null), list("M3 B12 Pattern Marine Armor", 24, /obj/item/clothing/suit/storage/marine/medium/leader, null, VENDOR_ITEM_REGULAR), list("M4 Pattern Armor", 30, /obj/item/clothing/suit/storage/marine/medium/rto, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm index 6c7cbf2db740..845d169a701a 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm @@ -79,12 +79,20 @@ GLOBAL_LIST_INIT(cm_vending_gear_leader, list( list("Health Analyzer", 4, /obj/item/device/healthanalyzer, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 2, /obj/item/roller, null, VENDOR_ITEM_REGULAR), - list("SPECIAL AMMUNITION", 0, null, null, null), + list("PRIMARY AMMUNITION", 0, null, null, null), list("M4RA AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), list("M39 AP Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), list("M39 Extended Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), list("M41A AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), list("M41A Extended Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), + + list("SIDEARM AMMUNITION", 0, null, null, null), + list("M44 Heavy Speed Loader (.44)", 6, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 3, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 3, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 3, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), + + list("SPECIAL AMMUNITION", 0, null, null, null), list("M240 Incinerator Tank (Napthal)", 3, /obj/item/ammo_magazine/flamer_tank, null, VENDOR_ITEM_REGULAR), list("M240 Incinerator Tank (B-Gel)", 3, /obj/item/ammo_magazine/flamer_tank/gellied, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm index 6b4954ee5e92..7d16d15af6fd 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm @@ -58,13 +58,19 @@ GLOBAL_LIST_INIT(cm_vending_gear_medic, list( list("M74 AGM-Hornet Airburst Packet (x3 airburst grenades", 20, /obj/item/storage/box/packet/hornet, null, VENDOR_ITEM_REGULAR), list("M20 Mine Box (x4 mines)", 20, /obj/item/storage/box/explosive_mines, null, VENDOR_ITEM_REGULAR), - list("AMMUNITION", 0, null, null, null), + list("PRIMARY AMMUNITION", 0, null, null, null), list("M4RA AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), list("M39 AP Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), list("M39 Extended Magazine (10x20mm)", 6, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), list("M41A AP Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), list("M41A Extended Magazine (10x24mm)", 6, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), + list("SIDEARM AMMUNITION", 0, null, null, null), + list("M44 Heavy Speed Loader (.44)", 6, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 3, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 3, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 3, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), + list("ARMORS", 0, null, null, null), list("M3 B12 Pattern Marine Armor", 28, /obj/item/clothing/suit/storage/marine/medium/leader, null, VENDOR_ITEM_REGULAR), list("M4 Pattern Armor", 28, /obj/item/clothing/suit/storage/marine/medium/rto, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm index 15661cc4b661..b132d8d4f13d 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm @@ -67,13 +67,18 @@ GLOBAL_LIST_INIT(cm_vending_clothing_marine, list( list("M74 AGM-Hornet Airburst Packet (x3 airburst grenades", 15, /obj/item/storage/box/packet/hornet, null, VENDOR_ITEM_REGULAR), list("M20 Mine Box (x4 mines)", 20, /obj/item/storage/box/explosive_mines, null, VENDOR_ITEM_REGULAR), - list("AMMUNITION", 0, null, null, null), + list("PRIMARY AMMUNITION", 0, null, null, null), list("M4RA AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), list("M39 AP Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), list("M39 Extended Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), list("M41A AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), list("M41A Extended Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), + + list("SIDEARM AMMUNITION", 0, null, null, null), list("M44 Heavy Speed Loader (.44)", 10, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 5, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 5, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 5, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), list("ARMORS", 0, null, null, null), list("M3 B12 Pattern Marine Armor", 30, /obj/item/clothing/suit/storage/marine/medium/leader, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm index 04061370168d..c022f87abc6d 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm @@ -20,6 +20,12 @@ GLOBAL_LIST_INIT(cm_vending_gear_smartgun, list( list("M74 AGM-Hornet Airburst Packet (x3 airburst grenades", 20, /obj/item/storage/box/packet/hornet, null, VENDOR_ITEM_REGULAR), list("M20 Mine Box (x4 mines)", 20, /obj/item/storage/box/explosive_mines, null, VENDOR_ITEM_REGULAR), + list("SIDEARM AMMUNITION", 0, null, null, null), + list("M44 Heavy Speed Loader (.44)", 10, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 5, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 5, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 5, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), + list("CLOTHING ITEMS", 0, null, null, null), list("Machete Scabbard (Full)", 6, /obj/item/storage/large_holster/machete/full, null, VENDOR_ITEM_REGULAR), list("Fuel Tank Strap Pouch", 5, /obj/item/storage/pouch/flamertank, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm index d9ba7ee97c26..a013ddb15212 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm @@ -1,17 +1,6 @@ //------------GEAR VENDOR--------------- GLOBAL_LIST_INIT(cm_vending_gear_tl, list( - - list("AMMUNITION", 0, null, null, null), - list("M4RA AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), - list("M39 AP Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), - list("M39 Extended Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), - list("M41A AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), - list("M41A Extended Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), - list("M44 Heavy Speed Loader (.44)", 10, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), - list("M4A3 HP Magazine", 5, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), - list("M4A3 AP Magazine", 5, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), - list("EXPLOSIVES", 0, null, null, null), list("M40 HEDP High Explosive Packet (x3 grenades)", 18, /obj/item/storage/box/packet/high_explosive, null, VENDOR_ITEM_REGULAR), list("M40 HIDP Incendiary Packet (x3 grenades)", 18, /obj/item/storage/box/packet/incendiary, null, VENDOR_ITEM_REGULAR), @@ -24,6 +13,19 @@ GLOBAL_LIST_INIT(cm_vending_gear_tl, list( list("M20 Mine Box (x4 mines)", 20, /obj/item/storage/box/explosive_mines, null, VENDOR_ITEM_REGULAR), list("M40 MFHS Metal Foam Grenade", 5, /obj/item/explosive/grenade/metal_foam, null, VENDOR_ITEM_REGULAR), + list("PRIMARY AMMUNITION", 0, null, null, null), + list("M4RA AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/m4ra/ap, null, VENDOR_ITEM_REGULAR), + list("M39 AP Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/ap , null, VENDOR_ITEM_REGULAR), + list("M39 Extended Magazine (10x20mm)", 10, /obj/item/ammo_magazine/smg/m39/extended , null, VENDOR_ITEM_REGULAR), + list("M41A AP Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/ap , null, VENDOR_ITEM_REGULAR), + list("M41A Extended Magazine (10x24mm)", 10, /obj/item/ammo_magazine/rifle/extended , null, VENDOR_ITEM_REGULAR), + + list("SIDEARM AMMUNITION", 0, null, null, null), + list("M44 Heavy Speed Loader (.44)", 10, /obj/item/ammo_magazine/revolver/heavy, null, VENDOR_ITEM_REGULAR), + list("M4A3 HP Magazine", 5, /obj/item/ammo_magazine/pistol/hp, null, VENDOR_ITEM_REGULAR), + list("M4A3 AP Magazine", 5, /obj/item/ammo_magazine/pistol/ap, null, VENDOR_ITEM_REGULAR), + list("VP78 Magazine", 5, /obj/item/ammo_magazine/pistol/vp78, null, VENDOR_ITEM_REGULAR), + list("RESTRICTED FIREARMS", 0, null, null, null), list("VP78 Pistol", 10, /obj/item/storage/box/guncase/vp78, null, VENDOR_ITEM_REGULAR), list("SU-6 Smart Pistol", 15, /obj/item/storage/box/guncase/smartpistol, null, VENDOR_ITEM_REGULAR), From 0547c2fb946faf572064dd05e6e3d5018d5d0497 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:27:03 +0000 Subject: [PATCH 007/162] Automatic changelog for PR #5605 [ci skip] --- html/changelogs/AutoChangeLog-pr-5605.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5605.yml diff --git a/html/changelogs/AutoChangeLog-pr-5605.yml b/html/changelogs/AutoChangeLog-pr-5605.yml new file mode 100644 index 000000000000..c2904105aa9a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5605.yml @@ -0,0 +1,7 @@ +author: "Huffie56" +delete-after: True +changes: + - qol: "added two new section to replace ammunition section primary ammunition and sidearm ammunition and rearranged it for team leader." + - balance: "added the ability for comtech, medic, and squad leader to buy M4A3 (HP and AP) and VP78 magazines for 3 points and M44 Heavy for 6 points." + - balance: "added the ability for SG to buy M4A3 (HP and AP) and VP78 magazines for 5 points and M44 Heavy for 10 points." + - balance: "added the ability for rifleman to buy M4A3 (HP and AP) and VP78 magazines for 5 points." \ No newline at end of file From 9aa33b977df309b5136da50fa58058ab74322cfe Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 9 Feb 2024 01:06:32 +0000 Subject: [PATCH 008/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5522.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5605.yml | 7 ------- html/changelogs/archive/2024-02.yml | 11 +++++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5522.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5605.yml diff --git a/html/changelogs/AutoChangeLog-pr-5522.yml b/html/changelogs/AutoChangeLog-pr-5522.yml deleted file mode 100644 index 649a9a6b44db..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5522.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Huffie56" -delete-after: True -changes: - - refactor: "turning newscasters into \"props\"." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5605.yml b/html/changelogs/AutoChangeLog-pr-5605.yml deleted file mode 100644 index c2904105aa9a..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5605.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: "Huffie56" -delete-after: True -changes: - - qol: "added two new section to replace ammunition section primary ammunition and sidearm ammunition and rearranged it for team leader." - - balance: "added the ability for comtech, medic, and squad leader to buy M4A3 (HP and AP) and VP78 magazines for 3 points and M44 Heavy for 6 points." - - balance: "added the ability for SG to buy M4A3 (HP and AP) and VP78 magazines for 5 points and M44 Heavy for 10 points." - - balance: "added the ability for rifleman to buy M4A3 (HP and AP) and VP78 magazines for 5 points." \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 7177625ef87d..f326e08a7ddb 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -121,3 +121,14 @@ SpartanBobby: - maptweak: changes to LV624 Engineering and T-comms, mostly detailing but changes some doors and object placement +2024-02-09: + Huffie56: + - refactor: turning newscasters into "props". + - qol: added two new section to replace ammunition section primary ammunition and + sidearm ammunition and rearranged it for team leader. + - balance: added the ability for comtech, medic, and squad leader to buy M4A3 (HP + and AP) and VP78 magazines for 3 points and M44 Heavy for 6 points. + - balance: added the ability for SG to buy M4A3 (HP and AP) and VP78 magazines for + 5 points and M44 Heavy for 10 points. + - balance: added the ability for rifleman to buy M4A3 (HP and AP) and VP78 magazines + for 5 points. From 1c37a6b57137b8378124dfeba13461e7bb2fb777 Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:21:45 -0800 Subject: [PATCH 009/162] Fix PB Burst Oversight (#5669) # About the pull request This PR fixes an oversight that allowed PB burst fires to be fired without restriction during the burst sequence. # Explain why it's good for the game The gun shouldn't have a huge firerate increase just because you click quickly point blank. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Drathek fix: Fixed an oversight with burst point blank firing /:cl: --- code/modules/projectiles/gun.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 497b95061d6c..3f7533f26620 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1344,7 +1344,7 @@ and you're good to go. user.next_move = world.time //No click delay on PBs. //Point blanking doesn't actually fire the projectile. Instead, it simulates firing the bullet proper. - if(!able_to_fire(user)) //If it's a valid PB aside from that you can't fire the gun, do nothing. + if(flags_gun_features & GUN_BURST_FIRING || !able_to_fire(user)) //If it's a valid PB aside from that you can't fire the gun, do nothing. return TRUE //The following relating to bursts was borrowed from Fire code. From 4a01f9b1b814003a310aee56423e351855ff25b7 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 9 Feb 2024 23:29:54 +0000 Subject: [PATCH 010/162] Automatic changelog for PR #5669 [ci skip] --- html/changelogs/AutoChangeLog-pr-5669.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5669.yml diff --git a/html/changelogs/AutoChangeLog-pr-5669.yml b/html/changelogs/AutoChangeLog-pr-5669.yml new file mode 100644 index 000000000000..f33b5b319706 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5669.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - bugfix: "Fixed an oversight with burst point blank firing" \ No newline at end of file From 8fe3ee590a8844088c4114e48e2a01ee3d909cb8 Mon Sep 17 00:00:00 2001 From: BlackDragon <31581761+blackdragonTOW@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:23:04 -0800 Subject: [PATCH 011/162] Admin logs for 3rd Party Victory events (#5658) # About the pull request Adds logging in the event that W-Y, UPP, or CLF win during a xeno minor. # Explain why it's good for the game Would be good to track how common (or rare) these sorts of things are, which would also be good feedback on if its worthwhile to make more faction victory moments. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: admin: added logging for 3rd party victory events /:cl: --- code/game/gamemodes/colonialmarines/colonialmarines.dm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/game/gamemodes/colonialmarines/colonialmarines.dm b/code/game/gamemodes/colonialmarines/colonialmarines.dm index 6dfe50542b00..192277a250cc 100644 --- a/code/game/gamemodes/colonialmarines/colonialmarines.dm +++ b/code/game/gamemodes/colonialmarines/colonialmarines.dm @@ -372,10 +372,16 @@ var/living = headcount["total_headcount"] if ((headcount["WY_headcount"] / living) > MAJORITY) musical_track = pick('sound/theme/lastmanstanding_wy.ogg') + log_game("3rd party victory: Weyland-Yutani") + message_admins("3rd party victory: Weyland-Yutani") else if ((headcount["UPP_headcount"] / living) > MAJORITY) musical_track = pick('sound/theme/lastmanstanding_upp.ogg') + log_game("3rd party victory: Union of Progressive Peoples") + message_admins("3rd party victory: Union of Progressive Peoples") else if ((headcount["CLF_headcount"] / living) > MAJORITY) musical_track = pick('sound/theme/lastmanstanding_clf.ogg') + log_game("3rd party victory: Colonial Liberation Front") + message_admins("3rd party victory: Colonial Liberation Front") else if ((headcount["marine_headcount"] / living) > MAJORITY) musical_track = pick('sound/theme/neutral_melancholy2.ogg') //This is the theme song for Colonial Marines the game, fitting else From 9b8e22cd3d09c639ed09c5b0fbd6a8722d0c33d9 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 9 Feb 2024 23:45:52 +0000 Subject: [PATCH 012/162] Automatic changelog for PR #5658 [ci skip] --- html/changelogs/AutoChangeLog-pr-5658.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5658.yml diff --git a/html/changelogs/AutoChangeLog-pr-5658.yml b/html/changelogs/AutoChangeLog-pr-5658.yml new file mode 100644 index 000000000000..ab498c88d581 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5658.yml @@ -0,0 +1,4 @@ +author: "blackdragonTOW" +delete-after: True +changes: + - admin: "added logging for 3rd party victory events" \ No newline at end of file From 9103818251d93b26d5238997cba73f827d4d9378 Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Fri, 9 Feb 2024 23:29:22 +0000 Subject: [PATCH 013/162] Fixes trash carts not working with fultons (#5664) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # About the pull request Fixes #5621, which was caused by a runtime in `/obj/structure/dropship_equipment/fulton_system/proc/automate_interact()`. The cart's entry in `possible_fultons` has a key of `"�\u{16}trash cart"`, but the `fulton_choice` passed from the UI is just `"trash cart"`. Because they don't match, `fult` is unable to be set, and an error is thrown on L1154. https://github.com/cmss13-devs/cmss13/blob/2657502ad1c5de88fd0aef35072900a736be84fe/code/modules/cm_marines/dropship_equipment.dm#L1141-L1154 # Explain why it's good for the game Bugfix! # Testing Photographs and Procedure
Screenshots & Videos **Before:** Runtime error. *(I can't be bothered to go through the set up again just to record this)* **After:** https://github.com/cmss13-devs/cmss13/assets/57483089/9ee496a4-d201-43b2-95d2-963dcc7c8569
# Changelog :cl: fix: Fixed trash carts not being pick-uppable by the dropship's fulton recovery system. /:cl: --- code/game/objects/structures/crates_lockers/crates.dm | 3 +-- code/modules/cm_marines/dropship_equipment.dm | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index cfb62812f0c6..d891119a8404 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -335,7 +335,7 @@ icon_closed = "closed_supply" /obj/structure/closet/crate/trashcart - name = "\improper trash cart" + name = "trash cart" desc = "A heavy, metal trashcart with wheels." icon_state = "closed_trashcart" icon_opened = "open_trashcart" @@ -437,4 +437,3 @@ density = TRUE icon_opened = "open_mcart_y" icon_closed = "closed_mcart_y" - diff --git a/code/modules/cm_marines/dropship_equipment.dm b/code/modules/cm_marines/dropship_equipment.dm index 785283541eb8..b162fb7c6a87 100644 --- a/code/modules/cm_marines/dropship_equipment.dm +++ b/code/modules/cm_marines/dropship_equipment.dm @@ -1144,9 +1144,11 @@ var/list/possible_fultons = get_targets() - var/obj/item/stack/fulton/fult = possible_fultons[fulton_choice] if(!fulton_choice) return + // Strip any \proper or \improper in order to match the entry in possible_fultons. + fulton_choice = strip_improper(fulton_choice) + var/obj/item/stack/fulton/fult = possible_fultons[fulton_choice] if(!ship_base) //system was uninstalled midway return From b327bbe4ef0994bf4e6ddaafe4eba0c2965290f6 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:00:39 +0000 Subject: [PATCH 014/162] Automatic changelog for PR #5664 [ci skip] --- html/changelogs/AutoChangeLog-pr-5664.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5664.yml diff --git a/html/changelogs/AutoChangeLog-pr-5664.yml b/html/changelogs/AutoChangeLog-pr-5664.yml new file mode 100644 index 000000000000..fa90c74c97ec --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5664.yml @@ -0,0 +1,4 @@ +author: "SabreML" +delete-after: True +changes: + - bugfix: "Fixed trash carts not being pick-uppable by the dropship's fulton recovery system." \ No newline at end of file From 5b45db6ec934d50d68058457037c3b72eb562ebd Mon Sep 17 00:00:00 2001 From: private-tristan <54422837+private-tristan@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:31:31 -0500 Subject: [PATCH 015/162] WY Research Consultant gets reagent scanner goggles (#5682) # About the pull request WY consultant (chem goon ERT) gets scigoggles so that they can identify the various chemicals that research gets. # Explain why it's good for the game It's a bit strange how the research consultant doesn't actually have a way to identify whether the vial they're given is the same one as the DDI paper tells them it is without using a chem machine. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: add: Chem Goon Research Consultant now spawns with reagent scanner goggles. /:cl: --- code/modules/gear_presets/wy_goons.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/gear_presets/wy_goons.dm b/code/modules/gear_presets/wy_goons.dm index 54b436c20854..a8f3a443311e 100644 --- a/code/modules/gear_presets/wy_goons.dm +++ b/code/modules/gear_presets/wy_goons.dm @@ -135,6 +135,7 @@ /datum/equipment_preset/goon/researcher/load_gear(mob/living/carbon/human/new_human) new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/WY, WEAR_L_EAR) + new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/science, WEAR_EYES) new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/corporate_formal, WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/labcoat, WEAR_JACKET) new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/corporate, WEAR_FEET) From 0d0f51f0a851c9b0b179695fd9b485af2f78b7fc Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:18:21 +0000 Subject: [PATCH 016/162] Automatic changelog for PR #5682 [ci skip] --- html/changelogs/AutoChangeLog-pr-5682.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5682.yml diff --git a/html/changelogs/AutoChangeLog-pr-5682.yml b/html/changelogs/AutoChangeLog-pr-5682.yml new file mode 100644 index 000000000000..cdfc7586a04b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5682.yml @@ -0,0 +1,4 @@ +author: "private-tristan" +delete-after: True +changes: + - rscadd: "Chem Goon Research Consultant now spawns with reagent scanner goggles." \ No newline at end of file From c1e9529813ca3a2aa5e7087e2c1d749061aed781 Mon Sep 17 00:00:00 2001 From: private-tristan <54422837+private-tristan@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:34:32 -0500 Subject: [PATCH 017/162] Pizza ERT readded, fixes(?) UPP spawn chance (#5662) # About the pull request Pizza ERT now has a 1 prob chance to spawn. UPP ERT variants now have their prob set to 0 (they were inheriting prob 20) so UPP had 3 prob20 rolls instead of 1. note: basic UPP ert text is always scrambled, regardless of whether they're hostile or not, the friendly text will only show up if an admin forces a friendly UPP ERT to appear. # Explain why it's good for the game Pizza ERT was a funny (and incredibly rare) occurrence that got removed when #2318 was added. with prob 1 it's incredibly unlikely to roll, (roughly 1 in 146 distress beacons) meaning that the one time that it **does** occur that will likely be memorable to every ghost in dchat (and marines too, if the pizza manages to get delivered) UPP prob change was (presumably) unintended and led to UPP being incredibly likely to roll compared to any other ERT. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: add: Pizza ERT is back, although incredibly unlikely to occur fix: UPP ERT no longer rolls 3 times (1 for random, 1 for friendly, and 1 for hostile), due to this, UPP comms will always be scrambled at the beginning, no way to tell intentions before meeting. /:cl: --- code/datums/emergency_calls/pizza.dm | 2 +- code/datums/emergency_calls/upp.dm | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/datums/emergency_calls/pizza.dm b/code/datums/emergency_calls/pizza.dm index a35ce584c68d..d80ea7e21139 100644 --- a/code/datums/emergency_calls/pizza.dm +++ b/code/datums/emergency_calls/pizza.dm @@ -8,7 +8,7 @@ objectives = "Make sure you get a tip!" shuttle_id = "Distress_Small" name_of_spawn = /obj/effect/landmark/ert_spawns/distress_pizza - probability = 0 + probability = 1 /datum/emergency_call/pizza/create_member(datum/mind/M, turf/override_spawn_loc) var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() diff --git a/code/datums/emergency_calls/upp.dm b/code/datums/emergency_calls/upp.dm index d68171981355..036e6f9e8b77 100644 --- a/code/datums/emergency_calls/upp.dm +++ b/code/datums/emergency_calls/upp.dm @@ -99,18 +99,20 @@ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), H, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) -/datum/emergency_call/upp/hostile +/datum/emergency_call/upp/hostile //if admins want to specifically call in friendly ones name = "UPP Naval Infantry (Squad) (Hostile)" hostility = TRUE + probability = 0 /datum/emergency_call/upp/hostile/New() ..() arrival_message = "[MAIN_SHIP_NAME] t*is i* UP* d^sp^*ch`. STr*&e teaM, #*u are cLe*% for a*pr*%^h. Pr*mE a*l wE*p^ns and pR*epr# t% r@nd$r a(tD." objectives = "Eliminate the UA Forces to ensure the UPP presence in this sector is continued. Listen to your superior officers and take over the [MAIN_SHIP_NAME] at all costs." -/datum/emergency_call/upp/friendly +/datum/emergency_call/upp/friendly //ditto name = "UPP Naval Infantry (Squad) (Friendly)" hostility = FALSE + probability = 0 /datum/emergency_call/upp/friendly/New() ..() From c1489ebc90aa24c04c3a0994832bfc243da195a4 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:40:12 +0000 Subject: [PATCH 018/162] Automatic changelog for PR #5662 [ci skip] --- html/changelogs/AutoChangeLog-pr-5662.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5662.yml diff --git a/html/changelogs/AutoChangeLog-pr-5662.yml b/html/changelogs/AutoChangeLog-pr-5662.yml new file mode 100644 index 000000000000..551b0e84a57e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5662.yml @@ -0,0 +1,5 @@ +author: "private-tristan" +delete-after: True +changes: + - rscadd: "Pizza ERT is back, although incredibly unlikely to occur" + - bugfix: "UPP ERT no longer rolls 3 times (1 for random, 1 for friendly, and 1 for hostile), due to this, UPP comms will always be scrambled at the beginning, no way to tell intentions before meeting." \ No newline at end of file From b27d968867cf65e719df67e7f376116f1cd5b25b Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sat, 10 Feb 2024 01:05:13 +0000 Subject: [PATCH 019/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5658.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5662.yml | 5 ----- html/changelogs/AutoChangeLog-pr-5664.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5669.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5682.yml | 4 ---- html/changelogs/archive/2024-02.yml | 14 ++++++++++++++ 6 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5658.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5662.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5664.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5669.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5682.yml diff --git a/html/changelogs/AutoChangeLog-pr-5658.yml b/html/changelogs/AutoChangeLog-pr-5658.yml deleted file mode 100644 index ab498c88d581..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5658.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "blackdragonTOW" -delete-after: True -changes: - - admin: "added logging for 3rd party victory events" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5662.yml b/html/changelogs/AutoChangeLog-pr-5662.yml deleted file mode 100644 index 551b0e84a57e..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5662.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "private-tristan" -delete-after: True -changes: - - rscadd: "Pizza ERT is back, although incredibly unlikely to occur" - - bugfix: "UPP ERT no longer rolls 3 times (1 for random, 1 for friendly, and 1 for hostile), due to this, UPP comms will always be scrambled at the beginning, no way to tell intentions before meeting." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5664.yml b/html/changelogs/AutoChangeLog-pr-5664.yml deleted file mode 100644 index fa90c74c97ec..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5664.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - bugfix: "Fixed trash carts not being pick-uppable by the dropship's fulton recovery system." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5669.yml b/html/changelogs/AutoChangeLog-pr-5669.yml deleted file mode 100644 index f33b5b319706..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5669.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fixed an oversight with burst point blank firing" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5682.yml b/html/changelogs/AutoChangeLog-pr-5682.yml deleted file mode 100644 index cdfc7586a04b..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5682.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "private-tristan" -delete-after: True -changes: - - rscadd: "Chem Goon Research Consultant now spawns with reagent scanner goggles." \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index f326e08a7ddb..03ee5e5c0504 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -132,3 +132,17 @@ 5 points and M44 Heavy for 10 points. - balance: added the ability for rifleman to buy M4A3 (HP and AP) and VP78 magazines for 5 points. +2024-02-10: + Drathek: + - bugfix: Fixed an oversight with burst point blank firing + SabreML: + - bugfix: Fixed trash carts not being pick-uppable by the dropship's fulton recovery + system. + blackdragonTOW: + - admin: added logging for 3rd party victory events + private-tristan: + - rscadd: Pizza ERT is back, although incredibly unlikely to occur + - bugfix: UPP ERT no longer rolls 3 times (1 for random, 1 for friendly, and 1 for + hostile), due to this, UPP comms will always be scrambled at the beginning, + no way to tell intentions before meeting. + - rscadd: Chem Goon Research Consultant now spawns with reagent scanner goggles. From 31d7f576c075cd265b60ae473d52afbd4186539a Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Sat, 10 Feb 2024 03:40:50 -0800 Subject: [PATCH 020/162] Fix fruit counting and refactor out delete_fruit (#5686) # About the pull request This PR is a follow up to #5514 where we set picked true to have more accurate messages sent, however this was also checked to skip updating the fruit count in the bound_xeno's action. This PR also refactors out delete_fruit since it just effectively was Destroy anyways. # Explain why it's good for the game Fixes #5666 # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/76988376/4020e86d-df91-4d44-aaf5-0a338584cbc2
# Changelog :cl: Drathek fix: Fix gardener fruit count not always updating /:cl: --------- Co-authored-by: Birdtalon --- code/modules/cm_aliens/structures/fruit.dm | 34 ++++++++++------------ 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/code/modules/cm_aliens/structures/fruit.dm b/code/modules/cm_aliens/structures/fruit.dm index 384da5da39a7..02fdb2da415b 100644 --- a/code/modules/cm_aliens/structures/fruit.dm +++ b/code/modules/cm_aliens/structures/fruit.dm @@ -93,22 +93,6 @@ qdel(src) . = ..() -/obj/effect/alien/resin/fruit/proc/delete_fruit() - //Notify and update the xeno count - if(!QDELETED(bound_xeno)) - if(!picked) - to_chat(bound_xeno, SPAN_XENOHIGHDANGER("We sense one of our fruit has been destroyed.")) - bound_xeno.current_fruits.Remove(src) - var/datum/action/xeno_action/onclick/plant_resin_fruit/prf = get_xeno_action_by_type(bound_xeno, /datum/action/xeno_action/onclick/plant_resin_fruit) - prf.update_button_icon() - - if(picked) // No need to update the number, since the fruit still exists (just as a different item) - return - var/number_of_fruit = length(bound_xeno.current_fruits) - prf.button.set_maptext(SMALL_FONTS_COLOR(7, number_of_fruit, "#e69d00"), 19, 2) - prf.update_button_icon() - bound_xeno = null - /obj/effect/alien/resin/fruit/proc/reduce_timer(maturity_increase) if (mature || timer_id == TIMER_ID_NULL) return @@ -193,7 +177,19 @@ return FALSE /obj/effect/alien/resin/fruit/Destroy() - delete_fruit() + //Notify and update the xeno count + if(!QDELETED(bound_xeno)) + if(!picked) + to_chat(bound_xeno, SPAN_XENOHIGHDANGER("We sense one of our fruit has been destroyed.")) + bound_xeno.current_fruits.Remove(src) + + var/number_of_fruit = length(bound_xeno.current_fruits) + var/datum/action/xeno_action/onclick/plant_resin_fruit/plant_action = get_xeno_action_by_type(bound_xeno, /datum/action/xeno_action/onclick/plant_resin_fruit) + plant_action.button.set_maptext(SMALL_FONTS_COLOR(7, number_of_fruit, "#e69d00"), 19, 2) + plant_action.update_button_icon() + + bound_xeno = null + return ..() //Greater @@ -281,9 +277,9 @@ ..() START_PROCESSING(SSobj, src) -/obj/effect/alien/resin/fruit/spore/delete_fruit() +/obj/effect/alien/resin/fruit/spore/Destroy() STOP_PROCESSING(SSobj, src) - ..() + return ..() /obj/effect/alien/resin/fruit/spore/process() if(mature) From 98c314ff6c7e707db9a3b1bc8ed04ea87510a09c Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:53:21 +0000 Subject: [PATCH 021/162] Automatic changelog for PR #5686 [ci skip] --- html/changelogs/AutoChangeLog-pr-5686.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5686.yml diff --git a/html/changelogs/AutoChangeLog-pr-5686.yml b/html/changelogs/AutoChangeLog-pr-5686.yml new file mode 100644 index 000000000000..886879511c48 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5686.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - bugfix: "Fix gardener fruit count not always updating" \ No newline at end of file From bd6392964e5cb8bc16fec5c00ae2e27f1b697eeb Mon Sep 17 00:00:00 2001 From: Vicacrov <49321394+Vicacrov@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:20:23 +0100 Subject: [PATCH 022/162] Fixes Warden Praetorians being able to move around the ovipositored Queen (#5689) # About the pull request Fixes this bug: - Queen gets on ovipositor - Rests - Warden Praetorian uses the "Retrieve" skill on the Queen - Queen moves https://github.com/cmss13-devs/cmss13/assets/49321394/9b2f8415-4961-4541-b6dc-c79a81ddab15 # Explain why it's good for the game This is a silly bug. The original anchored check checked if the Praetorian was anchored, not its target. # Testing Photographs and Procedure No longer works:
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/49321394/402cda65-b605-4c78-9b72-952563fc104b
# Changelog :cl: fix: Fixed Warden Praetorians being able to move around the ovipositored Queen with their Retrieve ability. /:cl: --- .../xenomorph/abilities/praetorian/praetorian_powers.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm index ee19a8c9de88..605fe290b468 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm @@ -926,16 +926,16 @@ to_chat(X, SPAN_XENODANGER("We cannot retrieve ourself!")) return - if(X.anchored) - to_chat(X, SPAN_XENODANGER("That sister cannot move!")) - return - if(!(A in view(7, X))) to_chat(X, SPAN_XENODANGER("That sister is too far away!")) return var/mob/living/carbon/xenomorph/targetXeno = A + if(targetXeno.anchored) + to_chat(X, SPAN_XENODANGER("That sister cannot move!")) + return + if(!(targetXeno.resting || targetXeno.stat == UNCONSCIOUS)) if(targetXeno.mob_size > MOB_SIZE_BIG) to_chat(X, SPAN_WARNING("[targetXeno] is too big to retrieve while standing up!")) From ab81a8578d201b7472ebd6163a21312060544d8a Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 10 Feb 2024 13:30:36 +0000 Subject: [PATCH 023/162] Automatic changelog for PR #5689 [ci skip] --- html/changelogs/AutoChangeLog-pr-5689.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5689.yml diff --git a/html/changelogs/AutoChangeLog-pr-5689.yml b/html/changelogs/AutoChangeLog-pr-5689.yml new file mode 100644 index 000000000000..8aa463f11ae6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5689.yml @@ -0,0 +1,4 @@ +author: "Vicacrov" +delete-after: True +changes: + - bugfix: "Fixed Warden Praetorians being able to move around the ovipositored Queen with their Retrieve ability." \ No newline at end of file From 65aec78b19591e6468872e4d501023fb7077d4e9 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 11 Feb 2024 01:12:01 +0000 Subject: [PATCH 024/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5686.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5689.yml | 4 ---- html/changelogs/archive/2024-02.yml | 6 ++++++ 3 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5686.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5689.yml diff --git a/html/changelogs/AutoChangeLog-pr-5686.yml b/html/changelogs/AutoChangeLog-pr-5686.yml deleted file mode 100644 index 886879511c48..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5686.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fix gardener fruit count not always updating" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5689.yml b/html/changelogs/AutoChangeLog-pr-5689.yml deleted file mode 100644 index 8aa463f11ae6..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5689.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Vicacrov" -delete-after: True -changes: - - bugfix: "Fixed Warden Praetorians being able to move around the ovipositored Queen with their Retrieve ability." \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 03ee5e5c0504..b588f879a491 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -146,3 +146,9 @@ hostile), due to this, UPP comms will always be scrambled at the beginning, no way to tell intentions before meeting. - rscadd: Chem Goon Research Consultant now spawns with reagent scanner goggles. +2024-02-11: + Drathek: + - bugfix: Fix gardener fruit count not always updating + Vicacrov: + - bugfix: Fixed Warden Praetorians being able to move around the ovipositored Queen + with their Retrieve ability. From dde13a9d5fc34a64c407d45ec7ed6cc0e3432f59 Mon Sep 17 00:00:00 2001 From: harryob Date: Mon, 12 Feb 2024 00:35:43 +0000 Subject: [PATCH 025/162] removes dead tor code (#5629) this isn't enabled on the live server, nor would it ever be :cl: server: tor banning functionality is removed /:cl: --- .../configuration/entries/general.dm | 2 - code/game/world.dm | 4 - code/modules/admin/IsBanned.dm | 8 +- code/modules/admin/ToRban.dm | 88 ------------------- code/modules/admin/admin_verbs.dm | 1 - colonialmarines.dme | 1 - config/example/config.txt | 3 - 7 files changed, 1 insertion(+), 106 deletions(-) delete mode 100644 code/modules/admin/ToRban.dm diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 385cbcb8d446..eb1b0540fb54 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -491,8 +491,6 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/flag/respawn -/datum/config_entry/flag/ToRban - /datum/config_entry/flag/ooc_country_flags /datum/config_entry/flag/record_rounds diff --git a/code/game/world.dm b/code/game/world.dm index f68263412715..627e245bc4c1 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -87,10 +87,6 @@ GLOBAL_LIST_INIT(reboot_sfx, file2list("config/reboot_sfx.txt")) GLOB.obfs_x = rand(-500, 500) //A number between -100 and 100 GLOB.obfs_y = rand(-500, 500) //A number between -100 and 100 - spawn(3000) //so we aren't adding to the round-start lag - if(CONFIG_GET(flag/ToRban)) - ToRban_autoupdate() - // If the server's configured for local testing, get everything set up ASAP. // Shamelessly stolen from the test manager's host_tests() proc if(testing_locally) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 94f40629fc6a..4a7307b247a5 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -17,6 +17,7 @@ message_admins("Failed Login: [key] - Guests not allowed") return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") + // wait for database to be ready WAIT_DB_READY if(GLOB.admin_datums[ckey] && (GLOB.admin_datums[ckey].rights & R_MOD)) return ..() @@ -26,13 +27,6 @@ var/datum/entity/player/P = get_player_from_key(ckey) - //check if the IP address is a known TOR node - if(CONFIG_GET(flag/ToRban) && ToRban_isbanned(address)) - log_access("Failed Login: [src] - Banned: ToR") - message_admins("Failed Login: [src] - Banned: ToR") - return list("reason"="Using ToR", "desc"="\nReason: The network you are using to connect has been banned.\nIf you believe this is a mistake, please request help at [CONFIG_GET(string/banappeals)]") - - // wait for database to be ready . = P.check_ban(computer_id, address) if(.) diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm deleted file mode 100644 index 549353facfb8..000000000000 --- a/code/modules/admin/ToRban.dm +++ /dev/null @@ -1,88 +0,0 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_misc("Downloading updated ToR data...") - var/http[] = world.Export("https://check.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - F["last_update"] << world.realtime - log_misc("ToR data updated!") - if(usr) to_chat(usr, "ToRban updated.") - return - log_misc("ToR data update aborted: no data.") - return - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToR Ban Settings" - set category = "Server" - if(!admin_holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(CONFIG_GET(flag/ToRban)) - CONFIG_SET(flag/ToRban, FALSE) - message_admins("ToR banning disabled.") - else - CONFIG_SET(flag/ToRban, TRUE) - message_admins("ToR banning enabled.") - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "#[i] [F.dir[i]]" - dat = "[dat]
" - else - dat = "No addresses in list." - src << browse(dat,"window=ToRban_show") - if("remove") - var/savefile/F = new(TORFILE) - var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) - if(choice) - F.dir.Remove(choice) - to_chat(src, "Address removed") - if("remove all") - to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") - if("find") - var/input = input(src,"Please input an IP address to search for:","Find ToR ban",null) as null|text - if(input) - if(ToRban_isbanned(input)) - to_chat(src, "Address is a known ToR address") - else - to_chat(src, "Address is not a known ToR address") - return - -#undef TORFILE -#undef TOR_UPDATE_INTERVAL diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 56002d139599..43b06b6d80bc 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -242,7 +242,6 @@ GLOBAL_LIST_INIT(admin_verbs_possess, list( )) GLOBAL_LIST_INIT(admin_verbs_permissions, list( - /client/proc/ToRban, /client/proc/whitelist_panel, )) diff --git a/colonialmarines.dme b/colonialmarines.dme index 0b39e89042d2..6beec448e4b4 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -1397,7 +1397,6 @@ #include "code\modules\admin\stickyban.dm" #include "code\modules\admin\STUI.dm" #include "code\modules\admin\tag.dm" -#include "code\modules\admin\ToRban.dm" #include "code\modules\admin\medal_panel\medals_panel.dm" #include "code\modules\admin\medal_panel\medals_panel_tgui.dm" #include "code\modules\admin\player_panel\player_action.dm" diff --git a/config/example/config.txt b/config/example/config.txt index 8a976e02a580..f9e0956593a9 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -151,9 +151,6 @@ BANAPPEALS https://cm-ss13.com/viewforum.php?f=76 ##Defines the ticklag for the world. 0.9 is the normal one, 0.5 is smoother. TICKLAG 0.5 -## Uncomment this to ban use of ToR -#TOR_BAN - ## Uncomment this to have country flags pop up in OOC alongside names if the user has the pref turned on (uses IP-API) #OOC_COUNTRY_FLAGS From ee5f6a9d7e663d72d3399f360f9748503067a105 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 12 Feb 2024 00:45:50 +0000 Subject: [PATCH 026/162] Automatic changelog for PR #5629 [ci skip] --- html/changelogs/AutoChangeLog-pr-5629.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5629.yml diff --git a/html/changelogs/AutoChangeLog-pr-5629.yml b/html/changelogs/AutoChangeLog-pr-5629.yml new file mode 100644 index 000000000000..c3d38d74f0bb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5629.yml @@ -0,0 +1,4 @@ +author: "harryob" +delete-after: True +changes: + - server: "tor banning functionality is removed" \ No newline at end of file From ebafd1772d4f1f2d5f5142d16ed2a082d19420f4 Mon Sep 17 00:00:00 2001 From: Staykeu <79605233+Staykeu@users.noreply.github.com> Date: Sun, 11 Feb 2024 19:52:33 -0500 Subject: [PATCH 027/162] Adds One ACCESSORY ONLY Slot for Helmets (#5573) # About the pull request Allows for the insertion of an extra cosmetic item in helmets that subtype the M10. # Explain why it's good for the game Adds more personalization to people who don the helmet. Change is purely cosmetic. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Stakeyng qol: Lets you put an extra accessory item in your helmet. /:cl: --- code/modules/clothing/head/helmet.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index b5775652d8f6..2e551f65d674 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -380,7 +380,7 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list( var/obj/item/storage/internal/headgear/pockets var/storage_slots = 2 // keep in mind, one slot is reserved for garb items - var/storage_slots_reserved_for_garb = 1 + var/storage_slots_reserved_for_garb = 2 var/storage_max_w_class = SIZE_TINY // can hold tiny items only, EXCEPT for glasses & metal flask. var/storage_max_storage_space = 4 From 34ac6ba3e3ce45be087e7da7f898a3e4c2161f45 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 12 Feb 2024 01:01:38 +0000 Subject: [PATCH 028/162] Automatic changelog for PR #5573 [ci skip] --- html/changelogs/AutoChangeLog-pr-5573.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5573.yml diff --git a/html/changelogs/AutoChangeLog-pr-5573.yml b/html/changelogs/AutoChangeLog-pr-5573.yml new file mode 100644 index 000000000000..70597b919bbf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5573.yml @@ -0,0 +1,4 @@ +author: "Stakeyng" +delete-after: True +changes: + - qol: "Lets you put an extra accessory item in your helmet." \ No newline at end of file From 7f073669922e982a1c2fd468cb490366772a23b1 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Mon, 12 Feb 2024 01:09:38 +0000 Subject: [PATCH 029/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5573.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5629.yml | 4 ---- html/changelogs/archive/2024-02.yml | 5 +++++ 3 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5573.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5629.yml diff --git a/html/changelogs/AutoChangeLog-pr-5573.yml b/html/changelogs/AutoChangeLog-pr-5573.yml deleted file mode 100644 index 70597b919bbf..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5573.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Stakeyng" -delete-after: True -changes: - - qol: "Lets you put an extra accessory item in your helmet." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5629.yml b/html/changelogs/AutoChangeLog-pr-5629.yml deleted file mode 100644 index c3d38d74f0bb..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5629.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "harryob" -delete-after: True -changes: - - server: "tor banning functionality is removed" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index b588f879a491..156a3c6c33be 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -152,3 +152,8 @@ Vicacrov: - bugfix: Fixed Warden Praetorians being able to move around the ovipositored Queen with their Retrieve ability. +2024-02-12: + Stakeyng: + - qol: Lets you put an extra accessory item in your helmet. + harryob: + - server: tor banning functionality is removed From c5c89b89e29e522214ef711d882ae799cbae583b Mon Sep 17 00:00:00 2001 From: Julian56 <117036822+Huffie56@users.noreply.github.com> Date: Mon, 12 Feb 2024 01:57:02 +0100 Subject: [PATCH 030/162] Refactor the file watercloset.dm leaving toilet code in it and moving the rest in their own files. (#5596) # About the pull request # Explain why it's good for the game # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: refactor: refactored watercloset.dm file. /:cl: --------- Co-authored-by: Julien --- code/game/objects/items/toys/toys.dm | 8 + code/game/objects/structures/shower.dm | 222 +++++++++++ code/game/objects/structures/sink.dm | 122 ++++++ code/game/objects/structures/urinal.dm | 22 ++ code/game/objects/structures/watercloset.dm | 393 +------------------- colonialmarines.dme | 3 + 6 files changed, 388 insertions(+), 382 deletions(-) create mode 100644 code/game/objects/structures/shower.dm create mode 100644 code/game/objects/structures/sink.dm create mode 100644 code/game/objects/structures/urinal.dm diff --git a/code/game/objects/items/toys/toys.dm b/code/game/objects/items/toys/toys.dm index 6fa420df35d5..91d8164dcf38 100644 --- a/code/game/objects/items/toys/toys.dm +++ b/code/game/objects/items/toys/toys.dm @@ -396,6 +396,14 @@ src.add_fingerprint(user) addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), 2 SECONDS) +// rubber duck +/obj/item/toy/bikehorn/rubberducky + name = "rubber ducky" + desc = "Rubber ducky you're so fine, you make bathtime lots of fuuun. Rubber ducky I'm awfully fooooond of yooooouuuu~" //thanks doohl + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "rubberducky" + item_state = "rubberducky" + /obj/item/computer3_part name = "computer part" desc = "Holy jesus you donnit now" diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm new file mode 100644 index 000000000000..b731a2c0e242 --- /dev/null +++ b/code/game/objects/structures/shower.dm @@ -0,0 +1,222 @@ +/obj/structure/machinery/shower + name = "shower" + desc = "The HS-451. Installed in the 2050s by the Weyland Hygiene Division." + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "shower" + density = FALSE + anchored = TRUE + use_power = USE_POWER_NONE + var/on = 0 + var/obj/effect/mist/mymist = null + /// needs a var so we can make it linger~ + var/ismist = 0 + /// freezing, normal, or boiling + var/watertemp = "normal" + /// true if there is a mob on the shower's loc, this is to ease process() + var/mobpresent = 0 + var/is_washing = 0 + +/obj/structure/machinery/shower/Initialize() + . = ..() + create_reagents(2) + +//add heat controls? when emagged, you can freeze to death in it? + +/obj/effect/mist + name = "mist" + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "mist" + layer = FLY_LAYER + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + +/obj/structure/machinery/shower/attack_hand(mob/M as mob) + on = !on + update_icon() + if(on) + start_processing() + if (M.loc == loc) + wash(M) + check_heat(M) + for (var/atom/movable/G in src.loc) + G.clean_blood() + else + stop_processing() + + +/obj/structure/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) + if(I.type == /obj/item/device/analyzer) + to_chat(user, SPAN_NOTICE("The water temperature seems to be [watertemp].")) + if(HAS_TRAIT(I, TRAIT_TOOL_WRENCH)) + to_chat(user, SPAN_NOTICE("You begin to adjust the temperature valve with \the [I].")) + if(do_after(user, 50, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + switch(watertemp) + if("normal") + watertemp = "freezing" + if("freezing") + watertemp = "boiling" + if("boiling") + watertemp = "normal" + user.visible_message(SPAN_NOTICE("[user] adjusts the shower with \the [I]."), SPAN_NOTICE("You adjust the shower with \the [I].")) + add_fingerprint(user) + + +/obj/structure/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up + overlays.Cut() //once it's been on for a while, in addition to handling the water overlay. + QDEL_NULL(mymist) + + if(on) + overlays += image('icons/obj/structures/props/watercloset.dmi', src, "water", MOB_LAYER + 1, dir) + if(watertemp == "freezing") + return + if(!ismist) + spawn(50) + if(src && on) + ismist = 1 + mymist = new /obj/effect/mist(loc) + else + ismist = 1 + mymist = new /obj/effect/mist(loc) + else if(ismist) + ismist = 1 + mymist = new /obj/effect/mist(loc) + spawn(250) + if(src && !on) + QDEL_NULL(mymist) + ismist = 0 + + +/obj/structure/machinery/shower/Crossed(atom/movable/O) + ..() + wash(O) + if(ismob(O)) + mobpresent++ + check_heat(O) + + +/obj/structure/machinery/shower/Uncrossed(atom/movable/O) + if(ismob(O)) + mobpresent-- + ..() + +//Yes, showers are super powerful as far as washing goes. +/obj/structure/machinery/shower/proc/wash(atom/movable/O as obj|mob) + if(!on) return + + + if(isliving(O)) + var/mob/living/L = O + L.ExtinguishMob() + L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily + to_chat(L, SPAN_WARNING("You've been drenched in water!")) + if(iscarbon(O)) + var/mob/living/carbon/M = O + if(M.r_hand) + M.r_hand.clean_blood() + if(M.l_hand) + M.l_hand.clean_blood() + if(M.back) + if(M.back.clean_blood()) + M.update_inv_back(0) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/washgloves = 1 + var/washshoes = 1 + var/washmask = 1 + var/washears = 1 + var/washglasses = 1 + + if(H.wear_suit) + washgloves = !(H.wear_suit.flags_inv_hide & HIDEGLOVES) + washshoes = !(H.wear_suit.flags_inv_hide & HIDESHOES) + + if(H.head) + washmask = !(H.head.flags_inv_hide & HIDEMASK) + washglasses = !(H.head.flags_inv_hide & HIDEEYES) + washears = !(H.head.flags_inv_hide & HIDEEARS) + + if(H.wear_mask) + if (washears) + washears = !(H.wear_mask.flags_inv_hide & HIDEEARS) + if (washglasses) + washglasses = !(H.wear_mask.flags_inv_hide & HIDEEYES) + + if(H.head) + if(H.head.clean_blood()) + H.update_inv_head() + if(H.wear_suit) + if(H.wear_suit.clean_blood()) + H.update_inv_wear_suit() + else if(H.w_uniform) + if(H.w_uniform.clean_blood()) + H.update_inv_w_uniform() + if(H.gloves && washgloves) + if(H.gloves.clean_blood()) + H.update_inv_gloves() + if(H.shoes && washshoes) + if(H.shoes.clean_blood()) + H.update_inv_shoes() + if(H.wear_mask && washmask) + if(H.wear_mask.clean_blood()) + H.update_inv_wear_mask() + if(H.glasses && washglasses) + if(H.glasses.clean_blood()) + H.update_inv_glasses() + if((H.wear_l_ear || H.wear_r_ear) && washears) + if((H.wear_l_ear && H.wear_l_ear.clean_blood()) ||(H.wear_r_ear && H.wear_r_ear.clean_blood())) + H.update_inv_ears() + if(H.belt) + if(H.belt.clean_blood()) + H.update_inv_belt() + H.clean_blood(washshoes) + else + if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags + if(M.wear_mask.clean_blood()) + M.update_inv_wear_mask() + M.clean_blood() + else + O.clean_blood() + + if(isturf(loc)) + var/turf/tile = loc + tile.clean_blood() + for(var/obj/effect/E in tile) + if(istype(E,/obj/effect/decal/cleanable) || istype(E,/obj/effect/overlay)) + qdel(E) + + +/obj/structure/machinery/shower/process() + if(!on) return + wash_floor() + if(!mobpresent) return + for(var/mob/living/carbon/C in loc) + check_heat(C) + + +/obj/structure/machinery/shower/proc/wash_floor() + if(!ismist && is_washing) + return + is_washing = 1 + var/turf/T = get_turf(src) +// reagents.add_reagent("water", 2) + T.clean(src) + addtimer(VARSET_CALLBACK(src, is_washing, FALSE), 10 SECONDS) + + +/obj/structure/machinery/shower/proc/check_heat(mob/M as mob) + if(!on || watertemp == "normal") return + if(iscarbon(M)) + var/mob/living/carbon/C = M + + if(watertemp == "freezing") + C.bodytemperature = max(80, C.bodytemperature - 80) + C.recalculate_move_delay = TRUE + to_chat(C, SPAN_WARNING("The water is freezing!")) + return + if(watertemp == "boiling") + C.bodytemperature = min(500, C.bodytemperature + 35) + C.recalculate_move_delay = TRUE + C.apply_damage(5, BURN) + to_chat(C, SPAN_DANGER("The water is searing!")) + return diff --git a/code/game/objects/structures/sink.dm b/code/game/objects/structures/sink.dm new file mode 100644 index 000000000000..6bac40ea7da4 --- /dev/null +++ b/code/game/objects/structures/sink.dm @@ -0,0 +1,122 @@ +/obj/structure/sink + name = "sink" + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "sink_emptied_animation" + desc = "A sink used for washing one's hands and face." + anchored = TRUE + /// if something's being washed at the moment + var/busy = FALSE + + +/obj/structure/sink/Initialize() + . = ..() + if(prob(50)) + icon_state = "sink_emptied" + + +/obj/structure/sink/proc/stop_flow() //sets sink animation to normal sink (without running water) + + if(prob(50)) + icon_state = "sink_emptied_animation" + else + icon_state = "sink_emptied" + flick("sink_animation_empty", src) + + +/obj/structure/sink/attack_hand(mob/user) + if(isRemoteControlling(user)) + return + + if(!Adjacent(user)) + return + + if(busy) + to_chat(user, SPAN_DANGER("Someone's already washing here.")) + return + + to_chat(usr, SPAN_NOTICE(" You start washing your hands.")) + flick("sink_animation_fill", src) //<- play the filling animation then automatically switch back to the loop + icon_state = "sink_animation_fill_loop" //<- set it to the loop + addtimer(CALLBACK(src, PROC_REF(stop_flow)), 6 SECONDS) + playsound(loc, 'sound/effects/sinkrunning.ogg', 25, TRUE) + + busy = TRUE + sleep(40) + busy = FALSE + + if(!Adjacent(user)) return //Person has moved away from the sink + + user.clean_blood() + if(ishuman(user)) + user:update_inv_gloves() + for(var/mob/V in viewers(src, null)) + V.show_message(SPAN_NOTICE("[user] washes their hands using \the [src]."), SHOW_MESSAGE_VISIBLE) + + +/obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) + if(busy) + to_chat(user, SPAN_DANGER("Someone's already washing here.")) + return + + var/obj/item/reagent_container/RG = O + if (istype(RG) && RG.is_open_container()) + RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) + user.visible_message(SPAN_NOTICE("[user] fills \the [RG] using \the [src]."),SPAN_NOTICE("You fill \the [RG] using \the [src].")) + return + + else if (istype(O, /obj/item/weapon/baton)) + var/obj/item/weapon/baton/B = O + if(B.bcell) + if(B.bcell.charge > 0 && B.status == 1) + flick("baton_active", src) + user.apply_effect(10, STUN) + user.stuttering = 10 + user.apply_effect(10, WEAKEN) + B.deductcharge(B.hitcost) + user.visible_message( \ + SPAN_DANGER("[user] was stunned by \his wet [O]!"), \ + SPAN_DANGER("You were stunned by your wet [O]!")) + return + + var/turf/location = user.loc + if(!isturf(location)) return + + var/obj/item/I = O + if(!I || !istype(I,/obj/item)) return + + to_chat(usr, SPAN_NOTICE(" You start washing \the [I].")) + + busy = TRUE + sleep(40) + busy = FALSE + + if(user.loc != location) return //User has moved + if(!I) return //Item's been destroyed while washing + if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands + + O.clean_blood() + user.visible_message( \ + SPAN_NOTICE("[user] washes \a [I] using \the [src]."), \ + SPAN_NOTICE("You wash \a [I] using \the [src].")) + + +/obj/structure/sink/kitchen + name = "kitchen sink" + icon_state = "sink_alt" + + +/obj/structure/sink/puddle //splishy splashy ^_^ + name = "puddle" + icon_state = "puddle" + + +/obj/structure/sink/puddle/attack_hand(mob/M as mob) + icon_state = "puddle-splash" + ..() + icon_state = "puddle" + + +/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob) + icon_state = "puddle-splash" + ..() + icon_state = "puddle" diff --git a/code/game/objects/structures/urinal.dm b/code/game/objects/structures/urinal.dm new file mode 100644 index 000000000000..c6d14f46540a --- /dev/null +++ b/code/game/objects/structures/urinal.dm @@ -0,0 +1,22 @@ +/obj/structure/urinal + name = "urinal" + desc = "The HU-452, an experimental urinal." + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "urinal" + density = FALSE + anchored = TRUE + +/obj/structure/urinal/attackby(obj/item/I, mob/living/user) + if(istype(I, /obj/item/grab)) + if(isxeno(user)) return + var/obj/item/grab/G = I + if(isliving(G.grabbed_thing)) + var/mob/living/GM = G.grabbed_thing + if(user.grab_level > GRAB_PASSIVE) + if(!GM.loc == get_turf(src)) + to_chat(user, SPAN_NOTICE("[GM.name] needs to be on the urinal.")) + return + user.visible_message(SPAN_DANGER("[user] slams [GM.name] into [src]!"), SPAN_NOTICE("You slam [GM.name] into [src]!")) + GM.apply_damage(8, BRUTE) + else + to_chat(user, SPAN_NOTICE("You need a tighter grip.")) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index ea93d868c5a1..1014e8ab7a96 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -1,5 +1,3 @@ -//todo: toothbrushes, and some sort of "toilet-filthinator" for the hos - /obj/structure/toilet name = "toilet" desc = "The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean." @@ -9,16 +7,19 @@ anchored = TRUE can_buckle = TRUE buckle_lying = 0 - var/open = 0 //if the lid is up - var/cistern = 0 //if the cistern bit is open - var/w_items = 0 //the combined w_class of all the items in the cistern - var/mob/living/swirlie = null //the mob being given a swirlie + /// if the lid is up + var/open = 0 + /// if the cistern bit is open + var/cistern = 0 + /// the combined w_class of all the items in the cistern + var/w_items = 0 + /// the mob being given a swirlie + var/mob/living/swirlie = null var/list/buckling_y = list("north" = 1, "south" = 4, "east" = 0, "west" = 0) var/list/buckling_x = list("north" = 0, "south" = 0, "east" = -5, "west" = 4) var/atom/movable/overlay/cistern_overlay - /obj/structure/toilet/Initialize() . = ..() open = round(rand(0, 1)) @@ -29,6 +30,7 @@ vis_contents += cistern_overlay update_icon() + /obj/structure/toilet/attack_hand(mob/living/user as mob) if(buckled_mob) manual_unbuckle(user) @@ -69,7 +71,6 @@ flick("cistern[cistern]_flush", cistern_overlay) - /obj/structure/toilet/send_buckling_message(mob/M, mob/user) if (M == user) to_chat(M, SPAN_NOTICE("You seat yourself onto the toilet")) @@ -77,10 +78,10 @@ to_chat(user, SPAN_NOTICE("[M] has been seated onto the toilet by [user].")) to_chat(M, SPAN_NOTICE("You have been seated onto the toilet by [user].")) + /obj/structure/toilet/afterbuckle(mob/M) . = ..() - if(. && buckled_mob == M) var/direction = dir2text(dir) M.pixel_y = buckling_y[direction] + pixel_y @@ -106,7 +107,6 @@ M.overlays -= image("toilet00") - /obj/structure/toilet/verb/flip_lid() set name = "Flip lid" set category = "Object" @@ -116,11 +116,11 @@ update_icon() - /obj/structure/toilet/update_icon() icon_state = "toilet[open][cistern]" cistern_overlay.icon_state = "cistern[cistern]" + /obj/structure/toilet/attackby(obj/item/I, mob/living/user) if(HAS_TRAIT(I, TRAIT_TOOL_CROWBAR)) to_chat(user, SPAN_NOTICE("You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"].")) @@ -168,374 +168,3 @@ w_items += I.w_class to_chat(user, "You carefully place \the [I] into the cistern.") return - - - -/obj/structure/urinal - name = "urinal" - desc = "The HU-452, an experimental urinal." - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "urinal" - density = FALSE - anchored = TRUE - -/obj/structure/urinal/attackby(obj/item/I, mob/living/user) - if(istype(I, /obj/item/grab)) - if(isxeno(user)) return - var/obj/item/grab/G = I - if(isliving(G.grabbed_thing)) - var/mob/living/GM = G.grabbed_thing - if(user.grab_level > GRAB_PASSIVE) - if(!GM.loc == get_turf(src)) - to_chat(user, SPAN_NOTICE("[GM.name] needs to be on the urinal.")) - return - user.visible_message(SPAN_DANGER("[user] slams [GM.name] into [src]!"), SPAN_NOTICE("You slam [GM.name] into [src]!")) - GM.apply_damage(8, BRUTE) - else - to_chat(user, SPAN_NOTICE("You need a tighter grip.")) - - - -/obj/structure/machinery/shower - name = "shower" - desc = "The HS-451. Installed in the 2050s by the Weyland Hygiene Division." - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "shower" - density = FALSE - anchored = TRUE - use_power = USE_POWER_NONE - var/on = 0 - var/obj/effect/mist/mymist = null - var/ismist = 0 //needs a var so we can make it linger~ - var/watertemp = "normal" //freezing, normal, or boiling - var/mobpresent = 0 //true if there is a mob on the shower's loc, this is to ease process() - var/is_washing = 0 - -/obj/structure/machinery/shower/Initialize() - . = ..() - create_reagents(2) - -//add heat controls? when emagged, you can freeze to death in it? - -/obj/effect/mist - name = "mist" - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "mist" - layer = FLY_LAYER - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/structure/machinery/shower/attack_hand(mob/M as mob) - on = !on - update_icon() - if(on) - start_processing() - if (M.loc == loc) - wash(M) - check_heat(M) - for (var/atom/movable/G in src.loc) - G.clean_blood() - else - stop_processing() - -/obj/structure/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) - if(I.type == /obj/item/device/analyzer) - to_chat(user, SPAN_NOTICE("The water temperature seems to be [watertemp].")) - if(HAS_TRAIT(I, TRAIT_TOOL_WRENCH)) - to_chat(user, SPAN_NOTICE("You begin to adjust the temperature valve with \the [I].")) - if(do_after(user, 50, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) - switch(watertemp) - if("normal") - watertemp = "freezing" - if("freezing") - watertemp = "boiling" - if("boiling") - watertemp = "normal" - user.visible_message(SPAN_NOTICE("[user] adjusts the shower with \the [I]."), SPAN_NOTICE("You adjust the shower with \the [I].")) - add_fingerprint(user) - -/obj/structure/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up - overlays.Cut() //once it's been on for a while, in addition to handling the water overlay. - QDEL_NULL(mymist) - - if(on) - overlays += image('icons/obj/structures/props/watercloset.dmi', src, "water", MOB_LAYER + 1, dir) - if(watertemp == "freezing") - return - if(!ismist) - spawn(50) - if(src && on) - ismist = 1 - mymist = new /obj/effect/mist(loc) - else - ismist = 1 - mymist = new /obj/effect/mist(loc) - else if(ismist) - ismist = 1 - mymist = new /obj/effect/mist(loc) - spawn(250) - if(src && !on) - QDEL_NULL(mymist) - ismist = 0 - -/obj/structure/machinery/shower/Crossed(atom/movable/O) - ..() - wash(O) - if(ismob(O)) - mobpresent++ - check_heat(O) - -/obj/structure/machinery/shower/Uncrossed(atom/movable/O) - if(ismob(O)) - mobpresent-- - ..() - -//Yes, showers are super powerful as far as washing goes. -/obj/structure/machinery/shower/proc/wash(atom/movable/O as obj|mob) - if(!on) return - - - if(isliving(O)) - var/mob/living/L = O - L.ExtinguishMob() - L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily - to_chat(L, SPAN_WARNING("You've been drenched in water!")) - if(iscarbon(O)) - var/mob/living/carbon/M = O - if(M.r_hand) - M.r_hand.clean_blood() - if(M.l_hand) - M.l_hand.clean_blood() - if(M.back) - if(M.back.clean_blood()) - M.update_inv_back(0) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/washgloves = 1 - var/washshoes = 1 - var/washmask = 1 - var/washears = 1 - var/washglasses = 1 - - if(H.wear_suit) - washgloves = !(H.wear_suit.flags_inv_hide & HIDEGLOVES) - washshoes = !(H.wear_suit.flags_inv_hide & HIDESHOES) - - if(H.head) - washmask = !(H.head.flags_inv_hide & HIDEMASK) - washglasses = !(H.head.flags_inv_hide & HIDEEYES) - washears = !(H.head.flags_inv_hide & HIDEEARS) - - if(H.wear_mask) - if (washears) - washears = !(H.wear_mask.flags_inv_hide & HIDEEARS) - if (washglasses) - washglasses = !(H.wear_mask.flags_inv_hide & HIDEEYES) - - if(H.head) - if(H.head.clean_blood()) - H.update_inv_head() - if(H.wear_suit) - if(H.wear_suit.clean_blood()) - H.update_inv_wear_suit() - else if(H.w_uniform) - if(H.w_uniform.clean_blood()) - H.update_inv_w_uniform() - if(H.gloves && washgloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves() - if(H.shoes && washshoes) - if(H.shoes.clean_blood()) - H.update_inv_shoes() - if(H.wear_mask && washmask) - if(H.wear_mask.clean_blood()) - H.update_inv_wear_mask() - if(H.glasses && washglasses) - if(H.glasses.clean_blood()) - H.update_inv_glasses() - if((H.wear_l_ear || H.wear_r_ear) && washears) - if((H.wear_l_ear && H.wear_l_ear.clean_blood()) ||(H.wear_r_ear && H.wear_r_ear.clean_blood())) - H.update_inv_ears() - if(H.belt) - if(H.belt.clean_blood()) - H.update_inv_belt() - H.clean_blood(washshoes) - else - if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags - if(M.wear_mask.clean_blood()) - M.update_inv_wear_mask() - M.clean_blood() - else - O.clean_blood() - - if(isturf(loc)) - var/turf/tile = loc - tile.clean_blood() - for(var/obj/effect/E in tile) - if(istype(E,/obj/effect/decal/cleanable) || istype(E,/obj/effect/overlay)) - qdel(E) - -/obj/structure/machinery/shower/process() - if(!on) return - wash_floor() - if(!mobpresent) return - for(var/mob/living/carbon/C in loc) - check_heat(C) - -/obj/structure/machinery/shower/proc/wash_floor() - if(!ismist && is_washing) - return - is_washing = 1 - var/turf/T = get_turf(src) -// reagents.add_reagent("water", 2) - T.clean(src) - addtimer(VARSET_CALLBACK(src, is_washing, FALSE), 10 SECONDS) - -/obj/structure/machinery/shower/proc/check_heat(mob/M as mob) - if(!on || watertemp == "normal") return - if(iscarbon(M)) - var/mob/living/carbon/C = M - - if(watertemp == "freezing") - C.bodytemperature = max(80, C.bodytemperature - 80) - C.recalculate_move_delay = TRUE - to_chat(C, SPAN_WARNING("The water is freezing!")) - return - if(watertemp == "boiling") - C.bodytemperature = min(500, C.bodytemperature + 35) - C.recalculate_move_delay = TRUE - C.apply_damage(5, BURN) - to_chat(C, SPAN_DANGER("The water is searing!")) - return - - - -/obj/item/toy/bikehorn/rubberducky - name = "rubber ducky" - desc = "Rubber ducky you're so fine, you make bathtime lots of fuuun. Rubber ducky I'm awfully fooooond of yooooouuuu~" //thanks doohl - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "rubberducky" - item_state = "rubberducky" - - - -/obj/structure/sink - name = "sink" - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "sink_emptied_animation" - desc = "A sink used for washing one's hands and face." - anchored = TRUE - var/busy = FALSE //Something's being washed at the moment - -/obj/structure/sink/Initialize() - . = ..() - if(prob(50)) - icon_state = "sink_emptied" - - - -/obj/structure/sink/proc/stop_flow() //sets sink animation to normal sink (without running water) - - if(prob(50)) - icon_state = "sink_emptied_animation" - else - icon_state = "sink_emptied" - flick("sink_animation_empty", src) - - - -/obj/structure/sink/attack_hand(mob/user) - if(isRemoteControlling(user)) - return - - if(!Adjacent(user)) - return - - if(busy) - to_chat(user, SPAN_DANGER("Someone's already washing here.")) - return - - to_chat(usr, SPAN_NOTICE(" You start washing your hands.")) - flick("sink_animation_fill", src) //<- play the filling animation then automatically switch back to the loop - icon_state = "sink_animation_fill_loop" //<- set it to the loop - addtimer(CALLBACK(src, PROC_REF(stop_flow)), 6 SECONDS) - playsound(loc, 'sound/effects/sinkrunning.ogg', 25, TRUE) - - busy = TRUE - sleep(40) - busy = FALSE - - if(!Adjacent(user)) return //Person has moved away from the sink - - user.clean_blood() - if(ishuman(user)) - user:update_inv_gloves() - for(var/mob/V in viewers(src, null)) - V.show_message(SPAN_NOTICE("[user] washes their hands using \the [src]."), SHOW_MESSAGE_VISIBLE) - - -/obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) - if(busy) - to_chat(user, SPAN_DANGER("Someone's already washing here.")) - return - - var/obj/item/reagent_container/RG = O - if (istype(RG) && RG.is_open_container()) - RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) - user.visible_message(SPAN_NOTICE("[user] fills \the [RG] using \the [src]."),SPAN_NOTICE("You fill \the [RG] using \the [src].")) - return - - else if (istype(O, /obj/item/weapon/baton)) - var/obj/item/weapon/baton/B = O - if(B.bcell) - if(B.bcell.charge > 0 && B.status == 1) - flick("baton_active", src) - user.apply_effect(10, STUN) - user.stuttering = 10 - user.apply_effect(10, WEAKEN) - B.deductcharge(B.hitcost) - user.visible_message( \ - SPAN_DANGER("[user] was stunned by \his wet [O]!"), \ - SPAN_DANGER("You were stunned by your wet [O]!")) - return - - var/turf/location = user.loc - if(!isturf(location)) return - - var/obj/item/I = O - if(!I || !istype(I,/obj/item)) return - - to_chat(usr, SPAN_NOTICE(" You start washing \the [I].")) - - busy = TRUE - sleep(40) - busy = FALSE - - if(user.loc != location) return //User has moved - if(!I) return //Item's been destroyed while washing - if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands - - O.clean_blood() - user.visible_message( \ - SPAN_NOTICE("[user] washes \a [I] using \the [src]."), \ - SPAN_NOTICE("You wash \a [I] using \the [src].")) - - -/obj/structure/sink/kitchen - name = "kitchen sink" - icon_state = "sink_alt" - - -/obj/structure/sink/puddle //splishy splashy ^_^ - name = "puddle" - icon_state = "puddle" - -/obj/structure/sink/puddle/attack_hand(mob/M as mob) - icon_state = "puddle-splash" - ..() - icon_state = "puddle" - -/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob) - icon_state = "puddle-splash" - ..() - icon_state = "puddle" diff --git a/colonialmarines.dme b/colonialmarines.dme index 6beec448e4b4..64760c4f2bc9 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -1284,10 +1284,13 @@ #include "code\game\objects\structures\props.dm" #include "code\game\objects\structures\reagent_dispensers.dm" #include "code\game\objects\structures\safe.dm" +#include "code\game\objects\structures\shower.dm" #include "code\game\objects\structures\signs.dm" +#include "code\game\objects\structures\sink.dm" #include "code\game\objects\structures\surface.dm" #include "code\game\objects\structures\tables_racks.dm" #include "code\game\objects\structures\tank_dispenser.dm" +#include "code\game\objects\structures\urinal.dm" #include "code\game\objects\structures\vulture_spotter.dm" #include "code\game\objects\structures\watercloset.dm" #include "code\game\objects\structures\windoor_assembly.dm" From 1e676579e218e15cb785da11df57d6c324bf9771 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 12 Feb 2024 01:21:03 +0000 Subject: [PATCH 031/162] Automatic changelog for PR #5596 [ci skip] --- html/changelogs/AutoChangeLog-pr-5596.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5596.yml diff --git a/html/changelogs/AutoChangeLog-pr-5596.yml b/html/changelogs/AutoChangeLog-pr-5596.yml new file mode 100644 index 000000000000..9d1f19e74f38 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5596.yml @@ -0,0 +1,4 @@ +author: "Huffie56" +delete-after: True +changes: + - refactor: "refactored watercloset.dm file." \ No newline at end of file From 153b7cf09286f9ee1b85ee724316236e3e3859bf Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:09:08 +0000 Subject: [PATCH 032/162] Thour tiny tutorial tweaks (and a unit test) (#5695) # About the pull request
  1. Commit 5014927bc4ae6f3bd03f3ea1866cb6ba6c9e7c54: Makes the 'Tutorial Menu' automatically close when a tutorial is started.
    • Also adds an abort_tutorial() call to /datum/tutorial/proc/start_tutorial(), since it may have been possible to for player to get trapped in the tutorial otherwise.
  2. Commit b10974c0471ef14c3c66ec10910e8c993cbb29d1: Adds a tutorial_id to the basic Xenomorph tutorial.
  3. Commit 38c03c987dbad50f7d8a2354a63c75f88d5efcce: Edited the tutorial Xenomorph a bit so that it's always "You are Inquisitive Drone (XX-123)", as opposed to "You are Inquisitive Young/Mature/etc. Drone (XX-123)".
  4. Commits a595b971c489922510bd984e3d324ac844450efe + 27e38e654a611aeefd548c41035bbff035caa76b: Adds a basic unit test for /datum/tutorial subtypes to verify that they have set some basic variables.
    • Also lightly organises the #includes in code/modules/unit_tests/_unit_tests.dm.
# Explain why it's good for the game Just a couple of things I noticed while exploring tutorial code. The third commit is maybe a bit subjective, but I personally think it makes the tutorials seem like more of a separate game state, if that makes sense. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: ui: Made the Tutorial Menu automatically close when a tutorial is started. code: Made the tutorial Xenomorph not inherit the user's 'age' prefix. code: Added a basic unit test for tutorials. /:cl: --- code/datums/tutorial/_tutorial.dm | 1 + code/datums/tutorial/_tutorial_menu.dm | 5 +++-- code/datums/tutorial/xenomorph/_xenomorph.dm | 4 ++++ .../tutorial/xenomorph/xenomorph_basic.dm | 5 ++++- code/modules/unit_tests/_unit_tests.dm | 12 ++++++++---- code/modules/unit_tests/tutorials.dm | 19 +++++++++++++++++++ 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 code/modules/unit_tests/tutorials.dm diff --git a/code/datums/tutorial/_tutorial.dm b/code/datums/tutorial/_tutorial.dm index 5423453bbdb9..7dd7ac85c04d 100644 --- a/code/datums/tutorial/_tutorial.dm +++ b/code/datums/tutorial/_tutorial.dm @@ -52,6 +52,7 @@ GLOBAL_LIST_EMPTY_TYPED(ongoing_tutorials, /datum/tutorial) reservation = SSmapping.RequestBlockReservation(initial(tutorial_template.width), initial(tutorial_template.height)) if(!reservation) + abort_tutorial() return FALSE var/turf/bottom_left_corner_reservation = locate(reservation.bottom_left_coords[1], reservation.bottom_left_coords[2], reservation.bottom_left_coords[3]) diff --git a/code/datums/tutorial/_tutorial_menu.dm b/code/datums/tutorial/_tutorial_menu.dm index 42eb3f6aabfa..951b9654ef0e 100644 --- a/code/datums/tutorial/_tutorial_menu.dm +++ b/code/datums/tutorial/_tutorial_menu.dm @@ -79,5 +79,6 @@ return path = new path - path.start_tutorial(usr) - return TRUE + if(path.start_tutorial(usr)) + ui.close() + return TRUE diff --git a/code/datums/tutorial/xenomorph/_xenomorph.dm b/code/datums/tutorial/xenomorph/_xenomorph.dm index bd85cdb35f44..caa33d8eed43 100644 --- a/code/datums/tutorial/xenomorph/_xenomorph.dm +++ b/code/datums/tutorial/xenomorph/_xenomorph.dm @@ -22,6 +22,10 @@ // We don't want people talking to other xenomorphs across tutorials new_character.can_hivemind_speak = FALSE + // No age prefix or HUD element + new_character.age = XENO_NO_AGE + new_character.show_age_prefix = FALSE + new_character.generate_name() tutorial_mob = new_character xeno = new_character diff --git a/code/datums/tutorial/xenomorph/xenomorph_basic.dm b/code/datums/tutorial/xenomorph/xenomorph_basic.dm index 0415977835aa..e91c85e1e1e4 100644 --- a/code/datums/tutorial/xenomorph/xenomorph_basic.dm +++ b/code/datums/tutorial/xenomorph/xenomorph_basic.dm @@ -4,6 +4,7 @@ name = "Xenomorph - Basic" desc = "A tutorial to get you acquainted with the very basics of how to play a xenomorph." icon_state = "xeno" + tutorial_id = "xeno_basic_1" tutorial_template = /datum/map_template/tutorial/s12x12 starting_xenomorph_type = /mob/living/carbon/xenomorph/drone @@ -105,7 +106,7 @@ UnregisterSignal(human_dummy, COMSIG_MOB_DEATH) message_to_player("Well done. Killing humans is one of many ways to help the hive.") - message_to_player("Another way is to capture them. This will grow a new xenomorph inside them which will eventually burst into a new playable xenomorph!") + message_to_player("Another way is to capture them. This will grow a new xenomorph inside them which will eventually burst into a new playable xenomorph!") addtimer(CALLBACK(human_dummy, TYPE_PROC_REF(/mob/living, rejuvenate)), 8 SECONDS) addtimer(CALLBACK(src, PROC_REF(proceed_to_tackle_phase)), 10 SECONDS) @@ -227,3 +228,5 @@ /datum/tutorial/xenomorph/basic/init_map() loc_from_corner(9,0).ChangeTurf(/turf/closed/wall/resin/tutorial) + +#undef WAITING_HEALTH_THRESHOLD diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 90040ec9acf9..3967ee70e391 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -74,19 +74,23 @@ /// A trait source when adding traits through unit tests #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" +// Unit tests #include "autowiki.dm" +#include "check_runtimes.dm" #include "create_and_destroy.dm" -#include "focus_only_tests.dm" +#include "emote_panels.dm" #include "missing_icons.dm" #include "resist.dm" +#include "spawn_humans.dm" #include "spritesheets.dm" #include "subsystem_init.dm" #include "tgui_create_message.dm" #include "timer_sanity.dm" +#include "tutorials.dm" + +// Unit tests backend +#include "focus_only_tests.dm" #include "unit_test.dm" -#include "spawn_humans.dm" -#include "check_runtimes.dm" -#include "emote_panels.dm" #undef TEST_ASSERT #undef TEST_ASSERT_EQUAL diff --git a/code/modules/unit_tests/tutorials.dm b/code/modules/unit_tests/tutorials.dm new file mode 100644 index 000000000000..d835a4f272cb --- /dev/null +++ b/code/modules/unit_tests/tutorials.dm @@ -0,0 +1,19 @@ +/datum/unit_test/tutorials + +/datum/unit_test/tutorials/Run() + var/datum/tutorial/base_path = /datum/tutorial + for(var/datum/tutorial/tutorial_path as anything in subtypesof(base_path)) + if(initial(tutorial_path.parent_path) == tutorial_path) + continue + + // Make sure these variables are overridden on any subtypes. + TEST_ASSERT_NOTEQUAL(initial(tutorial_path.name), initial(base_path.name), + "[tutorial_path] does not have a name set.") + TEST_ASSERT_NOTEQUAL(initial(tutorial_path.tutorial_id), initial(base_path.tutorial_id), + "[tutorial_path] does not have a tutorial_id set.") + TEST_ASSERT_NOTEQUAL(initial(tutorial_path.desc), initial(base_path.desc), + "[tutorial_path] does not have a desc set.") + TEST_ASSERT_NOTEQUAL(initial(tutorial_path.icon_state), initial(base_path.icon_state), + "[tutorial_path] does not have an icon_state set.") + +// TODO: Add a test verifying that a basic tutorial can be started and completed. (Requires unit test client handling) From 41a39b96009f3d97206fd63416a2fb5a47c89525 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:18:16 +0000 Subject: [PATCH 033/162] Automatic changelog for PR #5695 [ci skip] --- html/changelogs/AutoChangeLog-pr-5695.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5695.yml diff --git a/html/changelogs/AutoChangeLog-pr-5695.yml b/html/changelogs/AutoChangeLog-pr-5695.yml new file mode 100644 index 000000000000..3c88e26a8e5f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5695.yml @@ -0,0 +1,6 @@ +author: "SabreML" +delete-after: True +changes: + - ui: "Made the Tutorial Menu automatically close when a tutorial is started." + - code_imp: "Made the tutorial Xenomorph not inherit the user's 'age' prefix." + - code_imp: "Added a basic unit test for tutorials." \ No newline at end of file From fe2f2b893f1feed94b3719002c60de97a5d36f7d Mon Sep 17 00:00:00 2001 From: Changelogs Date: Tue, 13 Feb 2024 01:07:41 +0000 Subject: [PATCH 034/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5596.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5695.yml | 6 ------ html/changelogs/archive/2024-02.yml | 7 +++++++ 3 files changed, 7 insertions(+), 10 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5596.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5695.yml diff --git a/html/changelogs/AutoChangeLog-pr-5596.yml b/html/changelogs/AutoChangeLog-pr-5596.yml deleted file mode 100644 index 9d1f19e74f38..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5596.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Huffie56" -delete-after: True -changes: - - refactor: "refactored watercloset.dm file." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5695.yml b/html/changelogs/AutoChangeLog-pr-5695.yml deleted file mode 100644 index 3c88e26a8e5f..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5695.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - ui: "Made the Tutorial Menu automatically close when a tutorial is started." - - code_imp: "Made the tutorial Xenomorph not inherit the user's 'age' prefix." - - code_imp: "Added a basic unit test for tutorials." \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 156a3c6c33be..f8d85764a2a3 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -157,3 +157,10 @@ - qol: Lets you put an extra accessory item in your helmet. harryob: - server: tor banning functionality is removed +2024-02-13: + Huffie56: + - refactor: refactored watercloset.dm file. + SabreML: + - ui: Made the Tutorial Menu automatically close when a tutorial is started. + - code_imp: Made the tutorial Xenomorph not inherit the user's 'age' prefix. + - code_imp: Added a basic unit test for tutorials. From f0251fefebc79f199331ee716d276d2ccfc17ccc Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Tue, 13 Feb 2024 05:32:02 -0800 Subject: [PATCH 035/162] Revive Lesser Drone and Facehugger temporary communication restrictions (#5688) # About the pull request This PR revives and expands #5352. By expand I mean that in addition to a 3 minute restriction for say and hivemind speak, custom emotes and pointing are also restricted during the 3 minute window. Past that I did some additional refactoring. # Explain why it's good for the game As a ghost role, this acts as a measure to mechanically limit the communication of meta info to other members in the hive. For now the restriction is only 3 minutes. # Testing Photographs and Procedure
Screenshots & Videos ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/fd64c2f4-9142-4fe1-9953-b29c480c17ee)
# Changelog :cl: Zonespace Drathek balance: Lesser drones and Facehuggers cannot speak, custom emote, or point for 3 minutes after spawning /:cl: --------- Co-authored-by: John Doe --- .../signals/atom/mob/living/signals_xeno.dm | 4 + .../dcs/signals/atom/mob/signals_mob.dm | 6 ++ code/datums/components/temporary_mute.dm | 90 +++++++++++++++++++ code/modules/mob/emote.dm | 12 +-- .../carbon/xenomorph/castes/Facehugger.dm | 14 +++ .../carbon/xenomorph/castes/lesser_drone.dm | 14 +++ .../mob/living/carbon/xenomorph/say.dm | 3 + code/modules/mob/mob_verbs.dm | 31 ++++--- colonialmarines.dme | 1 + 9 files changed, 154 insertions(+), 21 deletions(-) create mode 100644 code/datums/components/temporary_mute.dm diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm index 73b10d700247..e6e1e64e9c7e 100644 --- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm +++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm @@ -74,3 +74,7 @@ /// From /mob/living/carbon/xenomorph/proc/handle_crit() #define COMSIG_XENO_ENTER_CRIT "xeno_entering_critical" + +/// From /mob/living/carbon/xenomorph/proc/hivemind_talk(): (message) +#define COMSIG_XENO_TRY_HIVEMIND_TALK "xeno_try_hivemind_talk" + #define COMPONENT_OVERRIDE_HIVEMIND_TALK (1<<0) diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm index 710e4d9ae20a..61eb757e9c4d 100644 --- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm @@ -109,6 +109,12 @@ #define COMSIG_MOB_EMOTED(emote_key) "mob_emoted_[emote_key]" +#define COMSIG_MOB_TRY_EMOTE "mob_try_emote" + #define COMPONENT_OVERRIDE_EMOTE (1<<0) + +#define COMSIG_MOB_TRY_POINT "mob_try_point" + #define COMPONENT_OVERRIDE_POINT (1<<0) + //from /mob/living/set_stat() #define COMSIG_MOB_STAT_SET_ALIVE "mob_stat_set_alive" //from /mob/living/set_stat() diff --git a/code/datums/components/temporary_mute.dm b/code/datums/components/temporary_mute.dm new file mode 100644 index 000000000000..0fed49c4778e --- /dev/null +++ b/code/datums/components/temporary_mute.dm @@ -0,0 +1,90 @@ +/datum/component/temporary_mute + dupe_mode = COMPONENT_DUPE_UNIQUE + /// A message to tell the user when they attempt to speak, if any + var/on_speak_message = "" + /// A message to tell the user when they attempt to emote, if any + var/on_emote_message = "" + /// A message to tell the user when they become no longer mute, if any + var/on_unmute_message = "" + /// How long after the component's initialization it should be deleted. -1 means it will never delete + var/time_until_unmute = 3 MINUTES + +/datum/component/temporary_mute/Initialize(on_speak_message = "", on_emote_message = "", on_unmute_message = "", time_until_unmute = 3 MINUTES) + . = ..() + if(!ismob(parent)) + return COMPONENT_INCOMPATIBLE + + src.on_speak_message = on_speak_message + src.on_emote_message = on_emote_message + src.on_unmute_message = on_unmute_message + src.time_until_unmute = time_until_unmute + if(time_until_unmute != -1) + QDEL_IN(src, time_until_unmute) + +/datum/component/temporary_mute/RegisterWithParent() + ..() + RegisterSignal(parent, COMSIG_LIVING_SPEAK, PROC_REF(on_speak)) + RegisterSignal(parent, COMSIG_XENO_TRY_HIVEMIND_TALK, PROC_REF(on_hivemind)) + RegisterSignal(parent, COMSIG_MOB_TRY_EMOTE, PROC_REF(on_emote)) + RegisterSignal(parent, COMSIG_MOB_TRY_POINT, PROC_REF(on_point)) + +/datum/component/temporary_mute/UnregisterFromParent() + ..() + if(parent) + UnregisterSignal(parent, COMSIG_LIVING_SPEAK) + UnregisterSignal(parent, COMSIG_XENO_TRY_HIVEMIND_TALK) + UnregisterSignal(parent, COMSIG_MOB_TRY_EMOTE) + UnregisterSignal(parent, COMSIG_MOB_TRY_POINT) + if(on_unmute_message) + to_chat(parent, SPAN_NOTICE(on_unmute_message)) + +/datum/component/temporary_mute/proc/on_speak( + mob/user, + message, + datum/language/speaking = null, + verb = "says", + alt_name = "", + italics = FALSE, + message_range = GLOB.world_view_size, + sound/speech_sound, + sound_vol, + nolog = FALSE, + message_mode = null +) + SIGNAL_HANDLER + + if(!nolog) + log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to say the following before their spawn mute ended: [message] (CKEY: [user.key]) (JOB: [user.job])") + if(on_speak_message) + to_chat(parent, SPAN_BOLDNOTICE(on_speak_message)) + return COMPONENT_OVERRIDE_SPEAK + +/datum/component/temporary_mute/proc/on_hivemind(mob/user, message) + SIGNAL_HANDLER + + log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to hivemind the following before their spawn mute ended: [message] (CKEY: [user.key]) (JOB: [user.job])") + if(on_speak_message) + to_chat(parent, SPAN_BOLDNOTICE(on_speak_message)) + return COMPONENT_OVERRIDE_HIVEMIND_TALK + +/datum/component/temporary_mute/proc/on_emote(mob/user, datum/emote/current_emote, act, m_type, param, intentional) + SIGNAL_HANDLER + + // Allow involuntary emotes or non-custom emotes + if(!intentional) + return + if(!param && !istype(current_emote, /datum/emote/custom)) + return + + log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to emote the following before their spawn mute ended: [param] (CKEY: [user.key]) (JOB: [user.job])") + if(on_emote_message) + to_chat(parent, SPAN_BOLDNOTICE(on_emote_message)) + return COMPONENT_OVERRIDE_EMOTE + +/datum/component/temporary_mute/proc/on_point(mob/user, atom/target) + SIGNAL_HANDLER + + log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to point at the following before their spawn mute ended: [target] (CKEY: [user.key]) (JOB: [user.job])") + if(on_emote_message) + to_chat(parent, SPAN_BOLDNOTICE(on_emote_message)) + return COMPONENT_OVERRIDE_POINT diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index f1b600179450..fa7c7ec3176e 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -14,13 +14,15 @@ to_chat(src, SPAN_NOTICE("'[act]' emote does not exist. Say *help for a list.")) return FALSE var/silenced = FALSE - for(var/datum/emote/P in key_emotes) - if(!P.check_cooldown(src, intentional)) + for(var/datum/emote/current_emote in key_emotes) + if(!current_emote.check_cooldown(src, intentional)) silenced = TRUE continue - if(P.run_emote(src, param, m_type, intentional)) - SEND_SIGNAL(src, COMSIG_MOB_EMOTE, P, act, m_type, message, intentional) - SEND_SIGNAL(src, COMSIG_MOB_EMOTED(P.key)) + if(SEND_SIGNAL(src, COMSIG_MOB_TRY_EMOTE, current_emote, act, m_type, param, intentional) & COMPONENT_OVERRIDE_EMOTE) + silenced = TRUE + continue + if(current_emote.run_emote(src, param, m_type, intentional)) + SEND_SIGNAL(src, COMSIG_MOB_EMOTE, current_emote, act, m_type, message, intentional) return TRUE if(intentional && !silenced && !force_silence) to_chat(src, SPAN_NOTICE("Unusable emote '[act]'. Say *help for a list.")) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm index 7ce3a8750568..ab7b22b73d81 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm @@ -64,6 +64,20 @@ weed_food_states = list("Facehugger_1","Facehugger_2","Facehugger_3") weed_food_states_flipped = list("Facehugger_1","Facehugger_2","Facehugger_3") +/mob/living/carbon/xenomorph/facehugger/Login() + var/last_ckey_inhabited = persistent_ckey + . = ..() + if(ckey == last_ckey_inhabited) + return + + AddComponent(\ + /datum/component/temporary_mute,\ + "We aren't old enough to vocalize anything yet.",\ + "We aren't old enough to communicate like this yet.",\ + "We feel old enough to be able to vocalize and speak to the hivemind.",\ + 3 MINUTES,\ + ) + /mob/living/carbon/xenomorph/facehugger/initialize_pass_flags(datum/pass_flags_container/PF) ..() if (PF) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm index f050a0dcfe8a..5d3f4b38c8f8 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm @@ -83,6 +83,20 @@ weed_food_states = list("Lesser_Drone_1","Lesser_Drone_2","Lesser_Drone_3") weed_food_states_flipped = list("Lesser_Drone_1","Lesser_Drone_2","Lesser_Drone_3") +/mob/living/carbon/xenomorph/lesser_drone/Login() + var/last_ckey_inhabited = persistent_ckey + . = ..() + if(ckey == last_ckey_inhabited) + return + + AddComponent(\ + /datum/component/temporary_mute,\ + "We aren't old enough to vocalize anything yet.",\ + "We aren't old enough to communicate like this yet.",\ + "We feel old enough to be able to vocalize and speak to the hivemind.",\ + 3 MINUTES,\ + ) + /mob/living/carbon/xenomorph/lesser_drone/age_xeno() if(stat == DEAD || !caste || QDELETED(src) || !client) return diff --git a/code/modules/mob/living/carbon/xenomorph/say.dm b/code/modules/mob/living/carbon/xenomorph/say.dm index 5b8ce1ecd292..c40a50ce7523 100644 --- a/code/modules/mob/living/carbon/xenomorph/say.dm +++ b/code/modules/mob/living/carbon/xenomorph/say.dm @@ -98,6 +98,9 @@ to_chat(src, SPAN_WARNING("A headhunter temporarily cut off your psychic connection!")) return + if(SEND_SIGNAL(src, COMSIG_XENO_TRY_HIVEMIND_TALK, message) & COMPONENT_OVERRIDE_HIVEMIND_TALK) + return + hivemind_broadcast(message, hive) /mob/living/carbon/proc/hivemind_broadcast(message, datum/hive_status/hive) diff --git a/code/modules/mob/mob_verbs.dm b/code/modules/mob/mob_verbs.dm index 1ba8985d56bd..f12d00cc0988 100644 --- a/code/modules/mob/mob_verbs.dm +++ b/code/modules/mob/mob_verbs.dm @@ -50,34 +50,33 @@ to_chat(usr, SPAN_DANGER("This mob type cannot throw items.")) return -/mob/proc/point_to(atom/A in view()) +/mob/proc/point_to(atom/target in view()) //set name = "Point To" //set category = "Object" - if(!isturf(src.loc) || !(A in view(src)))//target is no longer visible to us - return 0 + if(!isturf(src.loc) || !(target in view(src)))//target is no longer visible to us + return FALSE - if(!A.mouse_opacity)//can't click it? can't point at it. - return 0 + if(!target.mouse_opacity)//can't click it? can't point at it. + return FALSE if(is_mob_incapacitated() || (status_flags & FAKEDEATH)) //incapacitated, can't point - return 0 + return FALSE - var/tile = get_turf(A) - if (!tile) - return 0 + var/tile = get_turf(target) + if(!tile) + return FALSE if(recently_pointed_to > world.time) - return 0 - - next_move = world.time + 2 - - point_to_atom(A, tile) - return 1 - + return FALSE + if(SEND_SIGNAL(src, COMSIG_MOB_TRY_POINT, target) & COMPONENT_OVERRIDE_POINT) + return FALSE + next_move = world.time + 2 + point_to_atom(target, tile) + return TRUE /mob/verb/memory() set name = "Notes" diff --git a/colonialmarines.dme b/colonialmarines.dme index 64760c4f2bc9..09370e73b4b1 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -406,6 +406,7 @@ #include "code\datums\components\overlay_lighting.dm" #include "code\datums\components\rename.dm" #include "code\datums\components\speed_modifier.dm" +#include "code\datums\components\temporary_mute.dm" #include "code\datums\components\toxin_buildup.dm" #include "code\datums\components\tutorial_status.dm" #include "code\datums\components\weed_damage_reduction.dm" From 881b3e9069f40c205bdf96661d79cfea88b61c9c Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:42:40 +0000 Subject: [PATCH 036/162] Automatic changelog for PR #5688 [ci skip] --- html/changelogs/AutoChangeLog-pr-5688.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5688.yml diff --git a/html/changelogs/AutoChangeLog-pr-5688.yml b/html/changelogs/AutoChangeLog-pr-5688.yml new file mode 100644 index 000000000000..0adffe654ac1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5688.yml @@ -0,0 +1,4 @@ +author: "Zonespace Drathek" +delete-after: True +changes: + - balance: "Lesser drones and Facehuggers cannot speak, custom emote, or point for 3 minutes after spawning" \ No newline at end of file From f6853a16caa3eeccaca7aec6889f3d9492973aaa Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:46:44 +0000 Subject: [PATCH 037/162] Fixes crates layering under the fulton recovery system (#5673) # About the pull request Fixes crates and some other fultonable objects being on a layer below the dropship's fulton recovery system. This was happening because crates (any closet subtypes really) are on `BELOW_OBJ_LAYER`, and dropship equipment is on `ABOVE_OBJ_LAYER`. Changing the layer of `/obj/structure/dropship_equipment` to something lower would probably work, but I felt the safest solution would be to just set the equipment's `plane` to whatever the attach point has. This has no effect on the outside attachment points, but fixes the layering issue since the inside ones are on `FLOOR_PLANE`. I should also mention that the actual fix is in the first commit (b8e44956cfb4ed11d971456be8059fd9eaeb928f) and is only one line. All the rest of this is just because the `install_equipment()` proc was a bit confusing as it was. # Explain why it's good for the game Unless I've missed something in git blame, this has probably been a bug for 4+ years. # Testing Photographs and Procedure
Screenshots & Videos **Before:** ![image](https://github.com/cmss13-devs/cmss13/assets/57483089/77d8e10c-6354-4902-a1c1-982a69709612) **After:** (Also a regular crate too) ![dreamseeker_htRSczUHZh](https://github.com/cmss13-devs/cmss13/assets/57483089/a7a93420-c7c0-4203-b4df-c3f3415cc98c) ![dreamseeker_yV0pi23XYY](https://github.com/cmss13-devs/cmss13/assets/57483089/8e4155ee-5b5e-437a-9cde-788755cc164a) **Verifying that other hardpoints aren't affected:** ![ds](https://github.com/cmss13-devs/cmss13/assets/57483089/1e67da09-910a-452e-ba26-27df763d0a31)
# Changelog :cl: fix: Fixed crates and similar objects layering below the dropship's fulton recovery system. /:cl: --- .../dropships/attach_points/attach_point.dm | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/code/modules/dropships/attach_points/attach_point.dm b/code/modules/dropships/attach_points/attach_point.dm index 7230e67e0af1..6fef4d58e785 100644 --- a/code/modules/dropships/attach_points/attach_point.dm +++ b/code/modules/dropships/attach_points/attach_point.dm @@ -27,44 +27,45 @@ /obj/effect/attach_point/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/powerloader_clamp)) - var/obj/item/powerloader_clamp/PC = I - install_equipment(PC, user) + var/obj/item/powerloader_clamp/clamp = I + install_equipment(clamp, user) return TRUE return ..() /// Called when a real user with a powerloader attempts to install an equipment on the attach point -/obj/effect/attach_point/proc/install_equipment(obj/item/powerloader_clamp/PC, mob/living/user) - if(!istype(PC.loaded, /obj/structure/dropship_equipment)) +/obj/effect/attach_point/proc/install_equipment(obj/item/powerloader_clamp/clamp, mob/living/user) + if(!istype(clamp.loaded, /obj/structure/dropship_equipment)) + return + var/obj/structure/dropship_equipment/ds_equipment = clamp.loaded + if(!(base_category in ds_equipment.equip_categories)) + to_chat(user, SPAN_WARNING("[ds_equipment] doesn't fit on [src].")) return - var/obj/structure/dropship_equipment/SE = PC.loaded - if(!(base_category in SE.equip_categories)) - to_chat(user, SPAN_WARNING("[SE] doesn't fit on [src].")) - return TRUE if(installed_equipment) - return TRUE - playsound(loc, 'sound/machines/hydraulics_1.ogg', 40, 1) + return + playsound(loc, 'sound/machines/hydraulics_1.ogg', 40, TRUE) var/point_loc = loc if(!user || !do_after(user, (7 SECONDS) * user.get_skill_duration_multiplier(SKILL_ENGINEER), INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) - return TRUE + return if(loc != point_loc)//dropship flew away - return TRUE - if(installed_equipment || PC.loaded != SE) - return TRUE - to_chat(user, SPAN_NOTICE("You install [SE] on [src].")) - SE.forceMove(loc) - PC.loaded = null - playsound(loc, 'sound/machines/hydraulics_2.ogg', 40, 1) - PC.update_icon() - installed_equipment = SE - SE.ship_base = src + return + if(installed_equipment || clamp.loaded != ds_equipment) + return + to_chat(user, SPAN_NOTICE("You install [ds_equipment] on [src].")) + ds_equipment.forceMove(loc) + clamp.loaded = null + playsound(loc, 'sound/machines/hydraulics_2.ogg', 40, TRUE) + clamp.update_icon() + installed_equipment = ds_equipment + ds_equipment.ship_base = src + ds_equipment.plane = plane for(var/obj/docking_port/mobile/marine_dropship/shuttle in SSshuttle.mobile) if(shuttle.id == ship_tag) - SE.linked_shuttle = shuttle - SEND_SIGNAL(shuttle, COMSIG_DROPSHIP_ADD_EQUIPMENT, SE) + ds_equipment.linked_shuttle = shuttle + SEND_SIGNAL(shuttle, COMSIG_DROPSHIP_ADD_EQUIPMENT, ds_equipment) break - SE.update_equipment() + ds_equipment.update_equipment() /// Weapon specific attachment point /obj/effect/attach_point/weapon From 9e91055d12eccc26e89f040253560273925c654d Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:55:38 +0000 Subject: [PATCH 038/162] Automatic changelog for PR #5673 [ci skip] --- html/changelogs/AutoChangeLog-pr-5673.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5673.yml diff --git a/html/changelogs/AutoChangeLog-pr-5673.yml b/html/changelogs/AutoChangeLog-pr-5673.yml new file mode 100644 index 000000000000..04b28818d34c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5673.yml @@ -0,0 +1,4 @@ +author: "SabreML" +delete-after: True +changes: + - bugfix: "Fixed crates and similar objects layering below the dropship's fulton recovery system." \ No newline at end of file From 696a7ef3e848dc001c91db8485893abdfbc87b1d Mon Sep 17 00:00:00 2001 From: Changelogs Date: Wed, 14 Feb 2024 01:07:49 +0000 Subject: [PATCH 039/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5673.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5688.yml | 4 ---- html/changelogs/archive/2024-02.yml | 7 +++++++ 3 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5673.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5688.yml diff --git a/html/changelogs/AutoChangeLog-pr-5673.yml b/html/changelogs/AutoChangeLog-pr-5673.yml deleted file mode 100644 index 04b28818d34c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5673.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - bugfix: "Fixed crates and similar objects layering below the dropship's fulton recovery system." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5688.yml b/html/changelogs/AutoChangeLog-pr-5688.yml deleted file mode 100644 index 0adffe654ac1..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5688.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Zonespace Drathek" -delete-after: True -changes: - - balance: "Lesser drones and Facehuggers cannot speak, custom emote, or point for 3 minutes after spawning" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index f8d85764a2a3..3137467ae515 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -164,3 +164,10 @@ - ui: Made the Tutorial Menu automatically close when a tutorial is started. - code_imp: Made the tutorial Xenomorph not inherit the user's 'age' prefix. - code_imp: Added a basic unit test for tutorials. +2024-02-14: + SabreML: + - bugfix: Fixed crates and similar objects layering below the dropship's fulton + recovery system. + Zonespace Drathek: + - balance: Lesser drones and Facehuggers cannot speak, custom emote, or point for + 3 minutes after spawning From 213896bd56314e9643c37d86f849a5c08fc83f2a Mon Sep 17 00:00:00 2001 From: InsaneRed <47158596+InsaneRed@users.noreply.github.com> Date: Wed, 14 Feb 2024 07:44:17 +0300 Subject: [PATCH 040/162] fixes drop pouch not being given to vendors and makes it scale the same as base webbing (#5706) # About the pull request makes the droppouch scale with pop, fixes it from being constantly 0 # Explain why it's good for the game bugfix, old value was 0.75 which meant squad vendors were not receiving any. makes it scale like the webbing as requested by drathek # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: balance: Drop pouch now scales the same as normal webbing /:cl: --------- Co-authored-by: InsaneRed --- code/game/machinery/vending/cm_vending.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm index 2ba1ac089826..4f825030d627 100644 --- a/code/game/machinery/vending/cm_vending.dm +++ b/code/game/machinery/vending/cm_vending.dm @@ -900,7 +900,7 @@ GLOBAL_LIST_EMPTY(vending_products) for(var/list/vendspec in listed_products) var/multiplier = vendspec[2] if(multiplier > 0) - var/awarded = round(vendspec[2] * scale) // Starting amount + var/awarded = round(vendspec[2] * scale, 1) // Starting amount //Record the multiplier and how many have actually been given out dynamic_stock_multipliers[vendspec] = list(vendspec[2], awarded) vendspec[2] = awarded // Override starting amount @@ -920,8 +920,8 @@ GLOBAL_LIST_EMPTY(vending_products) var/list/metadata = dynamic_stock_multipliers[vendspec] var/multiplier = metadata[1] // How much do we multiply scales by var/previous_max_amount = metadata[2] // How many we already handed out at old scale - var/projected_max_amount = round(new_scale * multiplier) // How much we would have had total now in total - var/amount_to_add = round(projected_max_amount - previous_max_amount) // Rounding just in case + var/projected_max_amount = round(new_scale * multiplier, 1) // How much we would have had total now in total + var/amount_to_add = round(projected_max_amount - previous_max_amount, 1) // Rounding just in case if(amount_to_add > 0) metadata[2] += amount_to_add vendspec[2] += amount_to_add From cfef302bcc8ed5d72b05b48cac2e237a4cc5165e Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:52:33 +0000 Subject: [PATCH 041/162] Automatic changelog for PR #5706 [ci skip] --- html/changelogs/AutoChangeLog-pr-5706.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5706.yml diff --git a/html/changelogs/AutoChangeLog-pr-5706.yml b/html/changelogs/AutoChangeLog-pr-5706.yml new file mode 100644 index 000000000000..2e10412afb03 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5706.yml @@ -0,0 +1,4 @@ +author: "InsaneRed" +delete-after: True +changes: + - balance: "Drop pouch now scales the same as normal webbing" \ No newline at end of file From 51dbf6f177231f433bf1a86887f6ffbe6129ef56 Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:42:17 +0000 Subject: [PATCH 042/162] Changelog icon size fix (tgfont updates port) (#5708) # About the pull request Ports https://github.com/tgstation/tgstation/pull/71808 and https://github.com/tgstation/tgstation/pull/69786 from TG. The first PR fixes an issue where some changelog icons (specifically those for `soundadd`, `sounddel`, `imageadd`, and `imagedel`) appeared a lot smaller than others. (See images below) The second PR adds a VSCode task to rebuild tgfont and copy the result to `tgui\packages\tgfont\static` automatically, rather than having to run `yarn tgfont:build` and copy them there yourself. This task is already mentioned in [tgui\packages\tgfont\README_ICON_TUTORIAL.txt](https://github.com/cmss13-devs/cmss13/blob/41a39b96009f3d97206fd63416a2fb5a47c89525/tgui/packages/tgfont/README_ICON_TUTORIAL.txt#L7), so I'm guessing it was just missed when the rest was ported over. # Explain why it's good for the game Fixes a bug, and makes it easier to add new font icons in the future. # Testing Photographs and Procedure
Screenshots & Videos **Before:** ![before](https://github.com/cmss13-devs/cmss13/assets/57483089/cdb85eeb-03a9-4768-863d-354f3599c3a1) **After:** ![after](https://github.com/cmss13-devs/cmss13/assets/57483089/9c360b58-818e-46cd-b2dd-ff764f53eb7b)
# Changelog :cl: fix: Fixed some changelog icons being smaller than others. /:cl: --- .vscode/tasks.json | 13 ++++++++ bin/tgfont.cmd | 2 ++ tgui/packages/tgfont/config.cjs | 4 +++ tgui/packages/tgfont/icons/image-minus.svg | 1 + tgui/packages/tgfont/icons/image-plus.svg | 1 + tgui/packages/tgfont/icons/sound-minus.svg | 1 + tgui/packages/tgfont/icons/sound-plus.svg | 1 + tgui/packages/tgfont/static/tgfont.css | 36 ++++----------------- tgui/packages/tgfont/static/tgfont.eot | Bin 4220 -> 2360 bytes tgui/packages/tgfont/static/tgfont.woff2 | Bin 1924 -> 880 bytes tools/build/build.js | 20 ++++++++++++ 11 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 bin/tgfont.cmd create mode 100644 tgui/packages/tgfont/icons/image-minus.svg create mode 100644 tgui/packages/tgfont/icons/image-plus.svg create mode 100644 tgui/packages/tgfont/icons/sound-minus.svg create mode 100644 tgui/packages/tgfont/icons/sound-plus.svg diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8d7b4133840c..e2390c9fe817 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -89,6 +89,19 @@ ], "group": "build", "label": "tgui: sonar" + }, + { + "type": "shell", + "command": "bin/tgfont", + "windows": { + "command": ".\\bin\\tgfont.cmd" + }, + "problemMatcher": [ + "$tsc", + "$eslint-stylish" + ], + "group": "build", + "label": "tgui: rebuild tgfont" } ] } diff --git a/bin/tgfont.cmd b/bin/tgfont.cmd new file mode 100644 index 000000000000..b768c81d653a --- /dev/null +++ b/bin/tgfont.cmd @@ -0,0 +1,2 @@ +@echo off +call "%~dp0\..\tools\build\build.bat" --wait-on-error tg-font %* diff --git a/tgui/packages/tgfont/config.cjs b/tgui/packages/tgfont/config.cjs index 4f6b58f1061e..73d96ac6ce90 100644 --- a/tgui/packages/tgfont/config.cjs +++ b/tgui/packages/tgfont/config.cjs @@ -7,8 +7,12 @@ module.exports = { name: 'tgfont', inputDir: './icons', + normalize: true, outputDir: './dist', fontTypes: ['woff2', 'eot'], assetTypes: ['css'], prefix: 'tg', + formatOptions: { + preserveAspectRatio: true, + }, }; diff --git a/tgui/packages/tgfont/icons/image-minus.svg b/tgui/packages/tgfont/icons/image-minus.svg new file mode 100644 index 000000000000..8c3231917ff9 --- /dev/null +++ b/tgui/packages/tgfont/icons/image-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/image-plus.svg b/tgui/packages/tgfont/icons/image-plus.svg new file mode 100644 index 000000000000..1658509429e3 --- /dev/null +++ b/tgui/packages/tgfont/icons/image-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/sound-minus.svg b/tgui/packages/tgfont/icons/sound-minus.svg new file mode 100644 index 000000000000..df51179d4b53 --- /dev/null +++ b/tgui/packages/tgfont/icons/sound-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/sound-plus.svg b/tgui/packages/tgfont/icons/sound-plus.svg new file mode 100644 index 000000000000..c5f40d53b560 --- /dev/null +++ b/tgui/packages/tgfont/icons/sound-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/static/tgfont.css b/tgui/packages/tgfont/static/tgfont.css index 992f088913c4..49e59cdd6959 100644 --- a/tgui/packages/tgfont/static/tgfont.css +++ b/tgui/packages/tgfont/static/tgfont.css @@ -1,7 +1,7 @@ @font-face { font-family: "tgfont"; - src: url("./tgfont.woff2?b809fa4bdd4aff7084540f130e4e524b") format("woff2"), -url("./tgfont.eot?b809fa4bdd4aff7084540f130e4e524b#iefix") format("embedded-opentype"); + src: url("./tgfont.woff2?0843e2c3dacddd319720859dcaeda508") format("woff2"), +url("./tgfont.eot?0843e2c3dacddd319720859dcaeda508#iefix") format("embedded-opentype"); } i[class^="tg-"]:before, i[class*=" tg-"]:before { @@ -15,39 +15,15 @@ i[class^="tg-"]:before, i[class*=" tg-"]:before { -moz-osx-font-smoothing: grayscale; } -.tg-air-tank-slash:before { - content: "\f101"; -} -.tg-air-tank:before { - content: "\f102"; -} -.tg-bad-touch:before { - content: "\f103"; -} .tg-image-minus:before { - content: "\f104"; + content: "\f101"; } .tg-image-plus:before { - content: "\f105"; -} -.tg-nanotrasen-logo:before { - content: "\f106"; -} -.tg-non-binary:before { - content: "\f107"; -} -.tg-prosthetic-full:before { - content: "\f108"; -} -.tg-prosthetic-leg:before { - content: "\f109"; + content: "\f102"; } .tg-sound-minus:before { - content: "\f10a"; + content: "\f103"; } .tg-sound-plus:before { - content: "\f10b"; -} -.tg-syndicate-logo:before { - content: "\f10c"; + content: "\f104"; } diff --git a/tgui/packages/tgfont/static/tgfont.eot b/tgui/packages/tgfont/static/tgfont.eot index e879c327d3f1b6fa8c7922ea7d5b6ae3329a1b12..17e3790d026c9382dc524864096eb031297842af 100644 GIT binary patch delta 1052 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 diff --git a/tgui/packages/tgfont/static/tgfont.woff2 b/tgui/packages/tgfont/static/tgfont.woff2 index 6a3be3d2cb40ce330faa5cc94b428791a7a4faa0..a4cc01a984a1fa09a5a622b6fa2c6d6f91cd200e 100644 GIT binary patch delta 866 zcmV-o1D*VY5AX&UcTYw#00961000AU01E&B000P>0009ckr*9+gBl8j7=%UvHUcCA z6blRh1Rw>2eg|C}atFvBk|ZgC1>XQT2QaZfJbb_QdhS~YlO?o7mUw(vMut^^jH$2E zRqj{S?f>e`uUH4bI->A&fK)U{4$IG$ORxdbn%9wVDo+#7MI7922bLf(3#^G_#!IpU zz{sTNvPj$wXakIYOwtLx82?eb0yQoC?0Anrqj^0X+aps;koh2s7?i@Q#$a`ihoCM) za>$h<+(W%k0t=%Yoq+=X;RhzipZ|hZJ0K}CsAle84QueahJInx24JYiiIXBQW|%gI zAD5u?m^2%vNg6W)HV(|{7S!uxXM_ zVbidUe;7v;pb@S32M`a2-tBfT6q3}QlADrI*L@*ByDnc%J$3=ST}(?mei@;|msTJ( z(vBC*UtBtN385Qk+G6SW)k7DKT{?VyITf{DK3>qgXtUK+25PDRA|IltjBpfgniC_P zzj&Fw*rjEEBU6`}NW_N@KGg6a&XI<&hKHmaJb14MPfp?l-&NmiS;CQoIXiJ@Vs;Do zu-WTdP5)U`6@SsqiAxnMCoxC=)Y&jN_`AA>9gxkbspf*-#6M+#uX=y@x^Br!t*XoC zkM|wDPuv-QvR&sw@GHl`@$6Q3@aZNoGU!)nVihHSjrgb5=kLPfZ`-oE{x$8*Z+aSu zA9A?y88X&O5aUQ9;uEMUv|kEB#V8Qi7QDh0Ann8%E}>B)DwVw5M=?=fHCK{4x8u>-()x(+ct!@i^UwOGasonzEo8QFaq+hwc@E5oYoz89^LI zBN0vvlZU7(oB<(>4ws8Tf9zCw6a*c^#Rv;1oh?FUhB2#n1$CvCV9=iMaB?F7qtqAy zs!X6XU&U^OWk*Q$3KKIV3^j34#0Z<1iqr-{jxgX}A(TpR3NYvl9b8U6Mk;kuh2Wl5 sIsBvUP=o_X2%`j)2u-(9S*PvMV9d5sSC$d+T+%FV$FwV)gRwaP0Ba$C>;M1& delta 1918 zcmV-^2Z8wT280h7cTYw#00961000Ms01E&B000l%000Lxkr*9+gbE6gM2&p`HUcCA zGz%O61Rw>2eg}bA8#E0O6|7+(`Mo%{{wzpdSp@Ia8`g5pODO8cy%u}q5-}O2ml_XP#&~u^}0@k z^=vn=H=@9T1`@z%PkMIi(hmB&1FTOff!?BH?5B`_zybIR=K*9WKmHHGP!FKs7|?Sy z>|CuQ_P!(Uh7k3QGdhCNpqQe6^DKi!o*dZA3@9k{r`oVkLRfhPgCt~{3>Jei zNo*9Q*IPn=%p7OxStrtBO0rbev-Ilm=~mgV`fMI&hDQr1-c(hR{F3Yy)0KectF6c+ zP1S6Iw#1xPx6Ne@iyr&-s$cfH(u!r2%!nZ_XGWE{NDwcaEV-Q9x7@!J#Jeo8!nHX^ zRW(64AD8F-veoIVD$9yutD$MOXrCe$cUrd5Y#vR2Digo0nxbi{CrB~iRU#}yh);Ec zCqkeI!C8Kl5D##cQ7G`2MFwB; z)j$bf)w(qnMYt51r%fc{A#OG6e3?gPd|L$b2&|}CkS4@0#d(*>zhP+$|H7qHz0;O0 z3M^TFF~c`~`C?~@hb_CEcekw2#8%K zc1YExFSq8+h+ZtIs!gj*(IO#G*vDN&MYdXtD~nv3s`XXXx|L6bPpwK336uapt_7MW zt#usfO5W4CI%w?eJ$wp99=|MKtjZ#OIe2A%-mj_>4<#r^IWTpx+5xCkq>8ALpeQ!4 z9MLWbL)1q!;^q|LXG;<5#8*QX6}Q)F{VP4X-0du~+~n$#Q>FT}DmAMWSz(IJ?7D-i zWai9%6PLjKmsoClC3mumSh_mwy|N-ZPHx+FGNN?B;d_I{-_md4WA$oXzWs#(k4M9Q z?kQg5LCtS<2486TcjIcg-8(xkzJJu-XpV9i5$1?8mu2TrKph?j zO;4vx!fW$trV3N60-MGB&t|gSNu#cRE><_$$b}5ure@tnrX9qN699vgDO6i5G=W@2 zHGwC7)Ic2LpK9gbHcHF#A?6BuTM*MzJNl8qc99W*$ZoL?D7UQM98vH;MfglQY1Nkr6i?i zt{;2;$=8mv8k9=ds@!GT#W_=t>e));eECTowRaJpQDqWoeA86ha;Sfslw^f3buiJI zVlOQy*4#Gj7k=pSWF3K zIs1;xaa<2>T$}9Nrl>Md3(XaJ7^oRbnuS|*vC$>A)D414;UQ-3R63P6Mr;eiq3$7E z&Bdt`a#+1)2q!V7jhO+XE8gdJx63{B5S9ACQ9rKL3eCz zX;<-}5f(ATQX0t)(vJ|W*jij@7N7mi8w7@54*d)g(~FSwc%CvE_G&{Hk}!q{-v zC#0Wxpi-#GSQ<&lIXl>Y4;O)&Nx|Svoj~7`wALT0HQ}&-PdnRn&d8|XIEC>M z>{<`*B@~qv4OUu7ovGPDL7b z(t&+M`mA>&D25YVBt^41441baG>s1YAC17~C-YgKN0_2Z&+}_rpkiDacYAqUd!=rl z3@ol-$dRPV3k%1wG3ev7;XiLaqqiJOC;G7VC9JO|ou|a4vw)@__TZ`IE)`$)VE_OC E02bMUIsgCw diff --git a/tools/build/build.js b/tools/build/build.js index ecde79ff4f2e..2126cfd77d4c 100644 --- a/tools/build/build.js +++ b/tools/build/build.js @@ -170,6 +170,26 @@ export const YarnTarget = new Juke.Target({ executes: ({ get }) => yarn("install", get(CiParameter) && "--immutable"), }); +export const TgFontTarget = new Juke.Target({ + dependsOn: [YarnTarget], + inputs: [ + 'tgui/.yarn/install-target', + 'tgui/packages/tgfont/**/*.+(js|cjs|svg)', + 'tgui/packages/tgfont/package.json', + ], + outputs: [ + 'tgui/packages/tgfont/dist/tgfont.css', + 'tgui/packages/tgfont/dist/tgfont.eot', + 'tgui/packages/tgfont/dist/tgfont.woff2', + ], + executes: async () => { + await yarn('tgfont:build'); + fs.copyFileSync('tgui/packages/tgfont/dist/tgfont.css', 'tgui/packages/tgfont/static/tgfont.css'); + fs.copyFileSync('tgui/packages/tgfont/dist/tgfont.eot', 'tgui/packages/tgfont/static/tgfont.eot'); + fs.copyFileSync('tgui/packages/tgfont/dist/tgfont.woff2', 'tgui/packages/tgfont/static/tgfont.woff2'); + } +}); + export const TguiTarget = new Juke.Target({ dependsOn: [YarnTarget], inputs: [ From ab283fccfbe0d08ba6a8ed2fefc58d0b02aca584 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:51:33 +0000 Subject: [PATCH 043/162] Automatic changelog for PR #5708 [ci skip] --- html/changelogs/AutoChangeLog-pr-5708.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5708.yml diff --git a/html/changelogs/AutoChangeLog-pr-5708.yml b/html/changelogs/AutoChangeLog-pr-5708.yml new file mode 100644 index 000000000000..c8a48bd94763 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5708.yml @@ -0,0 +1,4 @@ +author: "SabreML" +delete-after: True +changes: + - bugfix: "Fixed some changelog icons being smaller than others." \ No newline at end of file From cf0f0a1dee0cb79286ebda17962fc5e25b46c646 Mon Sep 17 00:00:00 2001 From: Vile Beggar Date: Wed, 14 Feb 2024 10:50:02 +0100 Subject: [PATCH 044/162] Bipod now gives you an option to switch to full auto when deploying (#5638) # About the pull request Attaching the bipod to weapons with full auto capability or firearms marked with the GUN_SUPPORT_PLATFORM flag will give the user a button toggle to immediately switch to full auto when deploying the bipod. ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/cea29ccc-5761-4a7b-99bd-cbdd035bc52b) If the full auto switch is turned on, the weapon will simply switch to full auto when the bipod is deployed. If you undeploy it, it will switch back to whatever previous firemode the weapon was in, e.g: 1. You set your pulse rifle to semi auto. 2. You deploy the bipod. 3. The pulse rifle is set to full auto. 4. You undeploy the bipod. 5. The pulse rifle will switch back to semi auto. In cases where you switch the weapon's firemode when it's already switched to full auto, it won't revert back to whatever firemode was set before it was deployed, e.g: 1. You set your pulse rifle to semi auto. 2. You deploy the bipod. 3. The pulse rifle is set to full auto. 4. You set your pulse rifle to burst fire. 5. You undeploy the bipod. 6. The pulse rifle will stay on burst fire. Also renames a couple of one letter variables. # Explain why it's good for the game Quality of life. Less messing around with firemode selectors which is a terrible headache, especially for the HPR, which you usually want to have on full auto 90% of the time. Those who want to stick with the old way don't have to change anything, as the full auto switch is entirely optional. # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/17518895/35bcc6e7-947d-4f82-971f-ac22b723537b
# Changelog :cl: qol: Attaching the bipod to weapons that have full auto (HPR included) will give you a button toggle for immediately switching to full auto when deploying the bipod. /:cl: --- code/modules/projectiles/gun_attachables.dm | 116 ++++++++++++++------ code/modules/projectiles/gun_helpers.dm | 2 +- icons/mob/hud/actions.dmi | Bin 22493 -> 24987 bytes 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm index 1807213f6226..37094bb12775 100644 --- a/code/modules/projectiles/gun_attachables.dm +++ b/code/modules/projectiles/gun_attachables.dm @@ -3301,6 +3301,10 @@ Defined in conflicts.dm of the #defines folder. var/bipod_deployed = FALSE /// If this should anchor the user while in use var/heavy_bipod = FALSE + // Are switching to full auto when deploying the bipod + var/full_auto_switch = FALSE + // Store our old firemode so we can switch to it when undeploying the bipod + var/old_firemode = null /obj/item/attachable/bipod/New() ..() @@ -3311,16 +3315,33 @@ Defined in conflicts.dm of the #defines folder. scatter_mod = SCATTER_AMOUNT_TIER_9 recoil_mod = RECOIL_AMOUNT_TIER_5 -/obj/item/attachable/bipod/Attach(obj/item/weapon/gun/G) +/obj/item/attachable/bipod/Attach(obj/item/weapon/gun/gun, mob/user) ..() - RegisterSignal(G, COMSIG_ITEM_DROPPED, PROC_REF(handle_drop)) + if((GUN_FIREMODE_AUTOMATIC in gun.gun_firemode_list) || (gun.flags_gun_features & GUN_SUPPORT_PLATFORM)) + var/given_action = FALSE + if(user && (gun == user.l_hand || gun == user.r_hand)) + give_action(user, /datum/action/item_action/bipod/toggle_full_auto_switch, src, gun) + given_action = TRUE + if(!given_action) + new /datum/action/item_action/bipod/toggle_full_auto_switch(src, gun) + + RegisterSignal(gun, COMSIG_ITEM_DROPPED, PROC_REF(handle_drop)) /obj/item/attachable/bipod/Detach(mob/user, obj/item/weapon/gun/detaching_gub) UnregisterSignal(detaching_gub, COMSIG_ITEM_DROPPED) + //clear out anything related to full auto switching + full_auto_switch = FALSE + old_firemode = null + for(var/item_action in detaching_gub.actions) + var/datum/action/item_action/bipod/toggle_full_auto_switch/target_action = item_action + if(target_action.target == src) + qdel(item_action) + break + if(bipod_deployed) - undeploy_bipod(detaching_gub) + undeploy_bipod(detaching_gub, user) ..() /obj/item/attachable/bipod/update_icon() @@ -3334,60 +3355,62 @@ Defined in conflicts.dm of the #defines folder. if(istype(loc, /obj/item/weapon/gun)) var/obj/item/weapon/gun/gun = loc gun.update_attachable(slot) - for(var/datum/action/A as anything in gun.actions) - A.update_button_icon() + for(var/datum/action/item_action as anything in gun.actions) + if(!istype(item_action, /datum/action/item_action/bipod/toggle_full_auto_switch)) + item_action.update_button_icon() -/obj/item/attachable/bipod/proc/handle_drop(obj/item/weapon/gun/G, mob/living/carbon/human/user) +/obj/item/attachable/bipod/proc/handle_drop(obj/item/weapon/gun/gun, mob/living/carbon/human/user) SIGNAL_HANDLER UnregisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK) if(bipod_deployed) - undeploy_bipod(G) + undeploy_bipod(gun, user) user.apply_effect(1, SUPERSLOW) user.apply_effect(2, SLOW) -/obj/item/attachable/bipod/proc/undeploy_bipod(obj/item/weapon/gun/G) - REMOVE_TRAIT(G, TRAIT_GUN_BIPODDED, "attached_bipod") +/obj/item/attachable/bipod/proc/undeploy_bipod(obj/item/weapon/gun/gun, mob/user) + REMOVE_TRAIT(gun, TRAIT_GUN_BIPODDED, "attached_bipod") bipod_deployed = FALSE accuracy_mod = -HIT_ACCURACY_MULT_TIER_5 scatter_mod = SCATTER_AMOUNT_TIER_9 recoil_mod = RECOIL_AMOUNT_TIER_5 burst_scatter_mod = 0 delay_mod = FIRE_DELAY_TIER_12 - G.recalculate_attachment_bonuses() - G.stop_fire() - var/mob/living/user - if(isliving(G.loc)) - user = G.loc - SEND_SIGNAL(user, COMSIG_MOB_UNDEPLOYED_BIPOD) - UnregisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK) + //if we are no longer on full auto, don't bother switching back to the old firemode + if(full_auto_switch && gun.gun_firemode == GUN_FIREMODE_AUTOMATIC && gun.gun_firemode != old_firemode) + gun.do_toggle_firemode(user, null, old_firemode) - if(G.flags_gun_features & GUN_SUPPORT_PLATFORM) - G.remove_firemode(GUN_FIREMODE_AUTOMATIC) + gun.recalculate_attachment_bonuses() + gun.stop_fire() + SEND_SIGNAL(user, COMSIG_MOB_UNDEPLOYED_BIPOD) + UnregisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK) + + if(gun.flags_gun_features & GUN_SUPPORT_PLATFORM) + gun.remove_firemode(GUN_FIREMODE_AUTOMATIC) if(heavy_bipod) user.anchored = FALSE - if(!QDELETED(G)) + if(!QDELETED(gun)) playsound(user,'sound/items/m56dauto_rotate.ogg', 55, 1) update_icon() -/obj/item/attachable/bipod/activate_attachment(obj/item/weapon/gun/G,mob/living/user, turn_off) +/obj/item/attachable/bipod/activate_attachment(obj/item/weapon/gun/gun, mob/living/user, turn_off) if(turn_off) if(bipod_deployed) - undeploy_bipod(G) + undeploy_bipod(gun, user) else - var/obj/support = check_bipod_support(G, user) + var/obj/support = check_bipod_support(gun, user) if(!support&&!bipod_deployed) to_chat(user, SPAN_NOTICE("You start deploying [src] on the ground.")) - if(!do_after(user, 15, INTERRUPT_ALL, BUSY_ICON_HOSTILE, G,INTERRUPT_DIFF_LOC)) + if(!do_after(user, 15, INTERRUPT_ALL, BUSY_ICON_HOSTILE, gun, INTERRUPT_DIFF_LOC)) return FALSE bipod_deployed = !bipod_deployed if(user) if(bipod_deployed) - ADD_TRAIT(G, TRAIT_GUN_BIPODDED, "attached_bipod") + ADD_TRAIT(gun, TRAIT_GUN_BIPODDED, "attached_bipod") to_chat(user, SPAN_NOTICE("You deploy [src] [support ? "on [support]" : "on the ground"].")) SEND_SIGNAL(user, COMSIG_MOB_DEPLOYED_BIPOD) playsound(user,'sound/items/m56dauto_rotate.ogg', 55, 1) @@ -3395,25 +3418,29 @@ Defined in conflicts.dm of the #defines folder. scatter_mod = -SCATTER_AMOUNT_TIER_10 recoil_mod = -RECOIL_AMOUNT_TIER_4 burst_scatter_mod = -SCATTER_AMOUNT_TIER_8 - if(istype(G,/obj/item/weapon/gun/rifle/sniper/M42A)) + if(istype(gun, /obj/item/weapon/gun/rifle/sniper/M42A)) delay_mod = -FIRE_DELAY_TIER_7 else delay_mod = -FIRE_DELAY_TIER_12 - G.recalculate_attachment_bonuses() - G.stop_fire() + gun.recalculate_attachment_bonuses() + gun.stop_fire() initial_mob_dir = user.dir RegisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK, PROC_REF(handle_mob_move_or_look)) - if(G.flags_gun_features & GUN_SUPPORT_PLATFORM) - G.add_firemode(GUN_FIREMODE_AUTOMATIC) + if(gun.flags_gun_features & GUN_SUPPORT_PLATFORM) + gun.add_firemode(GUN_FIREMODE_AUTOMATIC) if(heavy_bipod) user.anchored = TRUE + old_firemode = gun.gun_firemode + if(full_auto_switch && gun.gun_firemode != GUN_FIREMODE_AUTOMATIC) + gun.do_toggle_firemode(user, null, GUN_FIREMODE_AUTOMATIC) + else to_chat(user, SPAN_NOTICE("You retract [src].")) - undeploy_bipod(G) + undeploy_bipod(gun, user) update_icon() @@ -3430,10 +3457,10 @@ Defined in conflicts.dm of the #defines folder. //when user fires the gun, we check if they have something to support the gun's bipod. -/obj/item/attachable/proc/check_bipod_support(obj/item/weapon/gun/G, mob/living/user) +/obj/item/attachable/proc/check_bipod_support(obj/item/weapon/gun/gun, mob/living/user) return 0 -/obj/item/attachable/bipod/check_bipod_support(obj/item/weapon/gun/G, mob/living/user) +/obj/item/attachable/bipod/check_bipod_support(obj/item/weapon/gun/gun, mob/living/user) var/turf/T = get_turf(user) for(var/obj/O in T) if(O.throwpass && O.density && O.dir == user.dir && O.flags_atom & ON_BORDER) @@ -3445,6 +3472,31 @@ Defined in conflicts.dm of the #defines folder. return O2 return 0 +//item actions for handling deployment to full auto. +/datum/action/item_action/bipod/toggle_full_auto_switch/New(Target, obj/item/holder) + . = ..() + name = "Toggle Full Auto Switch" + action_icon_state = "full_auto_switch" + button.name = name + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions.dmi', button, action_icon_state) + +/datum/action/item_action/bipod/toggle_full_auto_switch/action_activate() + var/obj/item/weapon/gun/holder_gun = holder_item + var/obj/item/attachable/bipod/attached_bipod = holder_gun.attachments["under"] + + attached_bipod.full_auto_switch = !attached_bipod.full_auto_switch + to_chat(owner, SPAN_NOTICE("[icon2html(holder_gun, owner)] You will [attached_bipod.full_auto_switch? "start" : "stop"] switching to full auto when deploying the bipod.")) + playsound(owner, 'sound/weapons/handling/gun_burst_toggle.ogg', 15, 1) + + if(attached_bipod.full_auto_switch) + button.icon_state = "template_on" + else + button.icon_state = "template" + + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions.dmi', button, action_icon_state) + /obj/item/attachable/bipod/m60 name = "bipod" diff --git a/code/modules/projectiles/gun_helpers.dm b/code/modules/projectiles/gun_helpers.dm index 66e3a65f2f77..781db8fc1222 100644 --- a/code/modules/projectiles/gun_helpers.dm +++ b/code/modules/projectiles/gun_helpers.dm @@ -390,7 +390,7 @@ DEFINES in setup.dm, referenced here. user.visible_message(SPAN_NOTICE("[user] attaches [attachment] to [src]."), SPAN_NOTICE("You attach [attachment] to [src]."), null, 4) user.temp_drop_inv_item(attachment) - attachment.Attach(src) + attachment.Attach(src, user) update_attachable(attachment.slot) playsound(user, 'sound/handling/attachment_add.ogg', 15, 1, 4) return TRUE diff --git a/icons/mob/hud/actions.dmi b/icons/mob/hud/actions.dmi index 820da22ea5b1e2349e70aeb683ed3e9c8a778ad7..3c3e6dd9de3dd71172b4b46bb4f150a23b4e0752 100644 GIT binary patch literal 24987 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 A8UO$Q literal 22493 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 From 46c1ba79f3e854630ccf278d0b501c74a2be9883 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:05:49 +0000 Subject: [PATCH 045/162] Automatic changelog for PR #5638 [ci skip] --- html/changelogs/AutoChangeLog-pr-5638.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5638.yml diff --git a/html/changelogs/AutoChangeLog-pr-5638.yml b/html/changelogs/AutoChangeLog-pr-5638.yml new file mode 100644 index 000000000000..0f47b8dda2fe --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5638.yml @@ -0,0 +1,4 @@ +author: "VileBeggar" +delete-after: True +changes: + - qol: "Attaching the bipod to weapons that have full auto (HPR included) will give you a button toggle for immediately switching to full auto when deploying the bipod." \ No newline at end of file From b1ffddb56f3d3c8208bc31bdec08cff2c40ff155 Mon Sep 17 00:00:00 2001 From: Vicacrov <49321394+Vicacrov@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:51:45 +0100 Subject: [PATCH 046/162] Changes LV-624 tfort's landing floodlights to colony floodlights (#5694) # About the pull request This PR changes the landing floodlights found at LV-624's tfort to the colony floodlight variant. # Explain why it's good for the game This is not going to be popular, but please read the reasoning: LV-624 is the _only_ map that uses the "landing floodlight" variant outside of the actual landing zones (namely, at tfort) - these floodlights are unslashable, unmeltable, and unmoveable without any visual indicator (that is, they are not near LZs indicating their game-critical importance). This PR changes these landing floodlights to colony floodlight variants. - **Why colony floodlights:** Colony floodlights are the exact same floodlights that you can find around this map (even southeast of tfort itself): these ones cannot be melted (ever), and they cannot be slashed until they were turned on, after which they are still not despawned, just a comtech has to fix them. - **Why not regular floodlights:** Regular floodlights can be slashed. Every LV-624 round would start with xenos slashing/melting them immediately and marines would lose their light source on tfort. I feel the pain of every hivelord/drone who ever had to build around these light sources, but they have a strategic reasoning to be there. - **Is this a marine nerf:** Yes and no. Yes, they no longer have infinite, undestroyable-by-xenos floodlights at tfort and they actually have to set up power to have light here. No, because 1) marines usually CAS/OB tfort immediately, removing everything, and 2) colony floodlights have 150 health as opposed to the landing lights's 100, so they are slightly more resistant to marine fuckery. - **Why is this good apart from map unification:** It gives marines one more reason to properly fortify tfort: they have to protect their light sources. (Also, maybe stop OBing/CASing a strategically good place :P) # Testing Photographs and Procedure Tfort lights:

Screenshots & Videos ![image](https://github.com/cmss13-devs/cmss13/assets/49321394/6856fa49-aecd-4804-b0d4-342eb13b92e6)
# Changelog :cl: maptweak: LV-624 tfort's floodlights are now colony floodlights. /:cl: --- maps/map_files/LV624/LV624.dmm | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/maps/map_files/LV624/LV624.dmm b/maps/map_files/LV624/LV624.dmm index cd28e7341917..0e1948deadf3 100644 --- a/maps/map_files/LV624/LV624.dmm +++ b/maps/map_files/LV624/LV624.dmm @@ -1126,9 +1126,7 @@ /area/lv624/ground/barrens/containers) "afu" = ( /obj/item/ammo_casing, -/obj/structure/machinery/floodlight/landing{ - name = "bolted floodlight" - }, +/obj/structure/machinery/colony_floodlight, /turf/open/floor/plating{ dir = 9; icon_state = "warnplate" @@ -1162,9 +1160,7 @@ }, /area/lv624/ground/barrens/central_barrens) "afy" = ( -/obj/structure/machinery/floodlight/landing{ - name = "bolted floodlight" - }, +/obj/structure/machinery/colony_floodlight, /turf/open/floor/plating{ dir = 5; icon_state = "warnplate" @@ -1409,9 +1405,7 @@ /area/lv624/ground/barrens/central_barrens) "agF" = ( /obj/item/ammo_casing, -/obj/structure/machinery/floodlight/landing{ - name = "bolted floodlight" - }, +/obj/structure/machinery/colony_floodlight, /turf/open/floor/plating, /area/lv624/ground/barrens/central_barrens) "agG" = ( @@ -1588,9 +1582,7 @@ }, /area/lv624/ground/barrens/west_barrens/ceiling) "ahM" = ( -/obj/structure/machinery/floodlight/landing{ - name = "bolted floodlight" - }, +/obj/structure/machinery/colony_floodlight, /turf/open/floor/plating{ dir = 10; icon_state = "warnplate" @@ -1630,9 +1622,7 @@ }, /area/lv624/ground/barrens/central_barrens) "ahT" = ( -/obj/structure/machinery/floodlight/landing{ - name = "bolted floodlight" - }, +/obj/structure/machinery/colony_floodlight, /turf/open/floor/plating{ dir = 6; icon_state = "warnplate" From 660a5f290146cf35445e5176e4dfb8dd872b6a54 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:00:28 +0000 Subject: [PATCH 047/162] Automatic changelog for PR #5694 [ci skip] --- html/changelogs/AutoChangeLog-pr-5694.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5694.yml diff --git a/html/changelogs/AutoChangeLog-pr-5694.yml b/html/changelogs/AutoChangeLog-pr-5694.yml new file mode 100644 index 000000000000..55389b1f6ef3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5694.yml @@ -0,0 +1,4 @@ +author: "Vicacrov" +delete-after: True +changes: + - maptweak: "LV-624 tfort's floodlights are now colony floodlights." \ No newline at end of file From 97aa393855d0f322690edbdcfbbaf9d5d19e6446 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Thu, 15 Feb 2024 01:07:27 +0000 Subject: [PATCH 048/162] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5638.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5694.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5706.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5708.yml | 4 ---- html/changelogs/archive/2024-02.yml | 11 +++++++++++ 5 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5638.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5694.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5706.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5708.yml diff --git a/html/changelogs/AutoChangeLog-pr-5638.yml b/html/changelogs/AutoChangeLog-pr-5638.yml deleted file mode 100644 index 0f47b8dda2fe..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5638.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "VileBeggar" -delete-after: True -changes: - - qol: "Attaching the bipod to weapons that have full auto (HPR included) will give you a button toggle for immediately switching to full auto when deploying the bipod." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5694.yml b/html/changelogs/AutoChangeLog-pr-5694.yml deleted file mode 100644 index 55389b1f6ef3..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5694.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Vicacrov" -delete-after: True -changes: - - maptweak: "LV-624 tfort's floodlights are now colony floodlights." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5706.yml b/html/changelogs/AutoChangeLog-pr-5706.yml deleted file mode 100644 index 2e10412afb03..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5706.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "InsaneRed" -delete-after: True -changes: - - balance: "Drop pouch now scales the same as normal webbing" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5708.yml b/html/changelogs/AutoChangeLog-pr-5708.yml deleted file mode 100644 index c8a48bd94763..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5708.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - bugfix: "Fixed some changelog icons being smaller than others." \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 3137467ae515..ae8011f08dda 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -171,3 +171,14 @@ Zonespace Drathek: - balance: Lesser drones and Facehuggers cannot speak, custom emote, or point for 3 minutes after spawning +2024-02-15: + InsaneRed: + - balance: Drop pouch now scales the same as normal webbing + SabreML: + - bugfix: Fixed some changelog icons being smaller than others. + Vicacrov: + - maptweak: LV-624 tfort's floodlights are now colony floodlights. + VileBeggar: + - qol: Attaching the bipod to weapons that have full auto (HPR included) will give + you a button toggle for immediately switching to full auto when deploying the + bipod. From bbfc6adf787135f3128556717d084e26fd4ea6a0 Mon Sep 17 00:00:00 2001 From: harryob Date: Thu, 15 Feb 2024 06:25:43 +0000 Subject: [PATCH 049/162] handle permanent and sticky bans in database (#5628) a long time coming :cl: admin: permanent and sticky bans are now handled in game, instead of being managed externally /:cl: --- code/__DEFINES/subsystems.dm | 5 +- .../configuration/entries/general.dm | 2 + code/controllers/subsystem/stickyban.dm | 284 ++++++++++++++++++ code/datums/entities/player.dm | 105 ++++--- code/datums/entities/player_sticky_ban.dm | 179 ++++++----- code/defines/procs/admin.dm | 18 +- code/modules/admin/IsBanned.dm | 8 +- code/modules/admin/NewBan.dm | 80 ++++- code/modules/admin/admin_verbs.dm | 5 +- .../admin/player_panel/actions/punish.dm | 52 ++++ code/modules/admin/stickyban.dm | 66 ---- code/modules/admin/tabs/admin_tab.dm | 6 + code/modules/admin/topic/topic.dm | 194 ++++++++++++ code/modules/admin/verbs/autoreplace.dm | 6 +- code/modules/clans/client.dm | 22 +- .../living/carbon/xenomorph/castes/Queen.dm | 2 +- code/modules/tgui_panel/telemetry.dm | 4 +- colonialmarines.dme | 2 +- tgui/packages/tgui/interfaces/PlayerPanel.jsx | 46 ++- .../tgui/styles/components/Button.scss | 5 - tgui/public/tgui-panel.bundle.css | 4 +- 21 files changed, 852 insertions(+), 243 deletions(-) create mode 100644 code/controllers/subsystem/stickyban.dm delete mode 100644 code/modules/admin/stickyban.dm diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 96335a3c1acf..47aa0e732c76 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -146,8 +146,9 @@ #define SS_INIT_DATABASE -27 #define SS_INIT_ENTITYMANAGER -28 #define SS_INIT_PLAYTIME -29 -#define SS_INIT_PREDSHIPS -30 -#define SS_INIT_OBJECTIVES -31 +#define SS_INIT_STICKY -30 +#define SS_INIT_PREDSHIPS -31 +#define SS_INIT_OBJECTIVES -32 #define SS_INIT_MINIMAP -34 #define SS_INIT_STATPANELS -98 #define SS_INIT_CHAT -100 //Should be last to ensure chat remains smooth during init. diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index eb1b0540fb54..83929ecf8803 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -531,6 +531,8 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/string/round_results_webhook_url +/datum/config_entry/string/important_log_channel + /// InfluxDB v2 Host to connect to for sending statistics (over HTTP API) /datum/config_entry/string/influxdb_host /// InfluxDB v2 Bucket to send staistics to diff --git a/code/controllers/subsystem/stickyban.dm b/code/controllers/subsystem/stickyban.dm new file mode 100644 index 000000000000..48e934addc1a --- /dev/null +++ b/code/controllers/subsystem/stickyban.dm @@ -0,0 +1,284 @@ +SUBSYSTEM_DEF(stickyban) + name = "Sticky Ban" + init_order = SS_INIT_STICKY + flags = SS_NO_FIRE + +/datum/controller/subsystem/stickyban/Initialize() + var/list/all_bans = world.GetConfig("ban") + + for(var/existing_ban in all_bans) + var/list/ban_data = params2list(world.GetConfig("ban", existing_ban)) + INVOKE_ASYNC(src, PROC_REF(import_sticky), existing_ban, ban_data) + + return SS_INIT_SUCCESS + +/** + * Returns a list of [/datum/view_record/stickyban]s, or null, if no stickybans are found. All arguments are optional, but you should pass at least one if you want any results. + */ +/datum/controller/subsystem/stickyban/proc/check_for_sticky_ban(ckey, address, computer_id) + var/list/stickyban_ids = list() + + for(var/datum/view_record/stickyban_matched_ckey/matched_ckey as anything in get_impacted_ckey_records(ckey)) + stickyban_ids += matched_ckey.linked_stickyban + + for(var/datum/view_record/stickyban_matched_cid/matched_cid as anything in get_impacted_cid_records(computer_id)) + stickyban_ids += matched_cid.linked_stickyban + + for(var/datum/view_record/stickyban_matched_ip/matched_ip as anything in get_impacted_ip_records(address)) + stickyban_ids += matched_ip.linked_stickyban + + if(!length(stickyban_ids)) + return FALSE + + var/list/datum/view_record/stickyban/stickies = DB_VIEW(/datum/view_record/stickyban, + DB_AND( + DB_COMP("id", DB_IN, stickyban_ids), + DB_COMP("active", DB_EQUALS, TRUE) + ) + ) + + for(var/datum/view_record/stickyban/current_sticky in stickies) + if(length(get_whitelisted_ckey_records(current_sticky.id, ckey))) + stickies -= current_sticky + + if(!length(stickies)) + return FALSE + + return stickies + +/** + * Associates an existing stickyban with a new match, either of a ckey, address, or computer_id. Or all three. + * + * Arguments: + * - existing_ban_id, int, required + * - ckey, string, optional + * - address, string, optional + * - computer_id, string, optional + */ +/datum/controller/subsystem/stickyban/proc/match_sticky(existing_ban_id, ckey, address, computer_id) + if(!existing_ban_id) + return + + if(ckey) + add_matched_ckey(existing_ban_id, ckey) + + if(address) + add_matched_ip(existing_ban_id, address) + + if(computer_id) + add_matched_cid(existing_ban_id, computer_id) + +/** + * Adds a new tracked stickyban, and returns a [/datum/entity/stickyban] if it was successful. Blocking, sleeps. + */ +/datum/controller/subsystem/stickyban/proc/add_stickyban(identifier, reason, message, datum/entity/player/banning_admin, override_date) + var/datum/entity/stickyban/new_sticky = DB_ENTITY(/datum/entity/stickyban) + new_sticky.identifier = identifier + new_sticky.reason = reason + new_sticky.message = message + + if(banning_admin) + new_sticky.adminid = banning_admin.id + + new_sticky.date = override_date ? override_date : "[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]" + new_sticky.save() + new_sticky.sync() + + return new_sticky + +/// Adds a ckey match to the specified sticky ban. +/datum/controller/subsystem/stickyban/proc/add_matched_ckey(existing_ban_id, key) + key = ckey(key) + + if(length(DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_AND( + DB_COMP("linked_stickyban", DB_EQUALS, existing_ban_id), + DB_COMP("ckey", DB_EQUALS, key) + ) + ))) + return + + var/datum/entity/stickyban_matched_ckey/matched_ckey = DB_ENTITY(/datum/entity/stickyban_matched_ckey) + + matched_ckey.ckey = key + matched_ckey.linked_stickyban = existing_ban_id + + matched_ckey.save() + +/// Adds an IP match to the specified stickyban. +/datum/controller/subsystem/stickyban/proc/add_matched_ip(existing_ban_id, ip) + if(length(DB_VIEW(/datum/view_record/stickyban_matched_ip, + DB_AND( + DB_COMP("linked_stickyban", DB_EQUALS, existing_ban_id), + DB_COMP("ip", DB_EQUALS, ip) + ) + ))) + return + + var/datum/entity/stickyban_matched_ip/matched_ip = DB_ENTITY(/datum/entity/stickyban_matched_ip) + + matched_ip.ip = ip + matched_ip.linked_stickyban = existing_ban_id + + matched_ip.save() + +/// Adds a CID match to the specified stickyban. +/datum/controller/subsystem/stickyban/proc/add_matched_cid(existing_ban_id, cid) + if(length(DB_VIEW(/datum/view_record/stickyban_matched_cid, + DB_AND( + DB_COMP("linked_stickyban", DB_EQUALS, existing_ban_id), + DB_COMP("cid", DB_EQUALS, cid) + ) + ))) + return + + + var/datum/entity/stickyban_matched_cid/matched_cid = DB_ENTITY(/datum/entity/stickyban_matched_cid) + + matched_cid.cid = cid + matched_cid.linked_stickyban = existing_ban_id + + matched_cid.save() + +/// Whitelists a specific CKEY to the specified stickyban, which will allow connection, even with matching CIDs and IPs. +/datum/controller/subsystem/stickyban/proc/whitelist_ckey(existing_ban_id, key) + key = ckey(key) + + if(!key) + return + + var/id_to_select + + var/list/datum/view_record/stickyban_matched_ckey/existing_matches = DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_AND( + DB_COMP("linked_stickyban", DB_EQUALS, existing_ban_id), + DB_COMP("ckey", DB_EQUALS, key) + ) + ) + + if(length(existing_matches)) + var/datum/view_record/stickyban_matched_ckey/match = existing_matches[1] + id_to_select = match.id + + var/datum/entity/stickyban_matched_ckey/whitelisted_ckey = DB_ENTITY(/datum/entity/stickyban_matched_ckey, id_to_select) + + whitelisted_ckey.ckey = key + whitelisted_ckey.linked_stickyban = existing_ban_id + whitelisted_ckey.whitelisted = TRUE + + whitelisted_ckey.save() + +/** + * Returns a [/list] of [/datum/view_record/stickyban_matched_ckey] where the ckey provided has not been + * whitelisted from the stickyban, and would be prevented from joining - provided that the stickyban itself + * remains active. + */ +/datum/controller/subsystem/stickyban/proc/get_impacted_ckey_records(key) + key = ckey(key) + + return DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_AND( + DB_COMP("ckey", DB_EQUALS, key), + DB_COMP("whitelisted", DB_EQUALS, FALSE) + ) + ) + +/** + * Returns a [/list] of [/datum/view_record/stickyban_matched_ckey] which have been manually whitelisted by an admin and matches the provided existing_ban_id and key. + */ +/datum/controller/subsystem/stickyban/proc/get_whitelisted_ckey_records(existing_ban_id, key) + key = ckey(key) + + return DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_AND( + DB_COMP("linked_stickyban", DB_EQUALS, existing_ban_id), + DB_COMP("ckey", DB_EQUALS, key), + DB_COMP("whitelisted", DB_EQUALS, TRUE), + ) + ) + +/** + * Returns a [/list] of [/datum/view_record/stickyban_matched_cid] where the impacted CID matches the CID provided. + * Connections matching this CID will be blocked - provided the linked stickyban is active. + */ +/datum/controller/subsystem/stickyban/proc/get_impacted_cid_records(cid) + return DB_VIEW(/datum/view_record/stickyban_matched_cid, + DB_COMP("cid", DB_EQUALS, cid) + ) + +/** + * Returns a [/list] of [/datum/view_record/stickyban_matched_ip] where the impacted IP matches the IP provided. + * Connections matchin this IP will be blocked - provided the linked stickyban is active. + */ +/datum/controller/subsystem/stickyban/proc/get_impacted_ip_records(ip) + return DB_VIEW(/datum/view_record/stickyban_matched_ip, + DB_COMP("ip", DB_EQUALS, ip) + ) + +/// Legacy import from pager bans to database bans. +/datum/controller/subsystem/stickyban/proc/import_sticky(identifier, list/ban_data) + WAIT_DB_READY + + if(ban_data["type"] != "sticky") + handle_old_perma(identifier, ban_data) + return + + if(!ban_data["message"]) + ban_data["message"] = "Evasion" + + add_stickyban(identifier, ban_data["reason"], ban_data["message"], override_date = "LEGACY") + +/** + * We abuse the on_insert from ndatabase here to ensure we have the synced ID of the new stickyban when applying a *lot* of associated bans. If we don't have a matching pager ban with the new sticky's identifier, we stop. + */ +/datum/entity_meta/stickyban/on_insert(datum/entity/stickyban/new_sticky) + var/list/ban_data = params2list(world.GetConfig("ban", new_sticky.identifier)) + + if(!length(ban_data)) + return + + var/list/whitelisted = list() + if(ban_data["whitelist"]) + whitelisted = splittext(ban_data["whitelist"], ",") + for(var/key in whitelisted) + SSstickyban.whitelist_ckey(new_sticky.id, key) + + if(ban_data["keys"]) + var/list/keys = splittext(ban_data["keys"], ",") + keys -= whitelisted + for(var/key in keys) + SSstickyban.add_matched_ckey(new_sticky.id, key) + + if(ban_data["computer_id"]) + var/list/cids = splittext(ban_data["computer_id"], ",") + for(var/cid in cids) + SSstickyban.add_matched_cid(new_sticky.id, cid) + + if(ban_data["IP"]) + var/list/ips = splittext(ban_data["IP"], ",") + for(var/ip in ips) + SSstickyban.add_matched_ip(new_sticky.id, ip) + + world.SetConfig("ban", new_sticky.identifier, null) + +/// Imports permabans from the old ban.txt, and does *not* ban people that have been whitelisted. +/datum/controller/subsystem/stickyban/proc/handle_old_perma(identifier, list/ban_data) + var/list/keys_to_ban = list() + + keys_to_ban += splittext(ban_data["keys"], ",") + + for(var/x in 1 to length(keys_to_ban)) + keys_to_ban[x] = ckey(keys_to_ban[x]) + + var/list/keys = splittext(ban_data["whitelist"], ",") + for(var/key in keys) + keys_to_ban -= ckey(key) + + for(var/key in keys_to_ban) + var/datum/entity/player/player_entity = get_player_from_key(key) + if(!player_entity) + continue + + INVOKE_ASYNC(player_entity, TYPE_PROC_REF(/datum/entity/player, add_perma_ban), ban_data["message"]) + + world.SetConfig("ban", identifier, null) diff --git a/code/datums/entities/player.dm b/code/datums/entities/player.dm index 2973a174858f..f0b601652fe0 100644 --- a/code/datums/entities/player.dm +++ b/code/datums/entities/player.dm @@ -305,6 +305,36 @@ BSQL_PROTECT_DATUM(/datum/entity/player) return TRUE +/// Permanently bans this user, with the provided reason. The banner ([/datum/entity/player]) argument is optional, as this can be done without admin intervention. +/datum/entity/player/proc/add_perma_ban(reason, internal_reason, datum/entity/player/banner) + if(is_permabanned) + return FALSE + + is_permabanned = TRUE + permaban_date = "[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]" + permaban_reason = reason + + if(banner) + permaban_admin_id = banner.id + message_admins("[key_name_admin(banner.owning_client)] has permanently banned [ckey] for '[reason]'.") + var/datum/tgs_chat_embed/field/reason_embed + if(internal_reason) + reason_embed = new("Permaban Reason", internal_reason) + important_message_external("[banner.owning_client] has permanently banned [ckey] for '[reason]'.", "Permaban Placed", reason_embed ? list(reason_embed) : null) + + add_note("Permanently banned | [reason]", FALSE, NOTE_ADMIN, TRUE) + if(internal_reason) + add_note("Internal reason: [internal_reason]", TRUE, NOTE_ADMIN) + + if(owning_client) + to_chat_forced(owning_client, SPAN_LARGE("You have been permanently banned by [banner.ckey].\nReason: [reason].")) + to_chat_forced(owning_client, SPAN_LARGE("This is a permanent ban. It will not be removed.")) + QDEL_NULL(owning_client) + + save() + + return TRUE + /datum/entity/player/proc/auto_unban() if(!is_time_banned) return @@ -447,54 +477,45 @@ BSQL_PROTECT_DATUM(/datum/entity/player) record_login_triplet(player.ckey, address, computer_id) player_data.sync() -/datum/entity/player/proc/check_ban(computer_id, address) +/datum/entity/player/proc/check_ban(computer_id, address, is_telemetry) . = list() - var/list/linked_bans = check_for_sticky_ban(address, computer_id) - if(islist(linked_bans)) - var/datum/view_record/stickyban_list_view/SLW = LAZYACCESS(linked_bans, 1) - if(SLW) - var/reason = "" - - if(SLW.address == address) - reason += "IP Address Matches; " - if(SLW.computer_id == computer_id) - reason += "CID Matches; " - if(SLW.ckey == ckey) - reason += "Ckey Matches; " - - var/source_id = SLW.linked_stickyban - var/source_reason = SLW.linked_reason - var/source_ckey = SLW.linked_ckey - if(!source_id) - source_id = "[SLW.entry_id]" - source_reason = SLW.reason - source_ckey = SLW.ckey - - log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Stickybanned (Linked to [source_ckey]; Reason: [source_reason])") - message_admins("Failed Login: [ckey] (IP: [last_known_ip], CID: [last_known_cid]) - Stickybanned (Linked to ckey [source_ckey]; Reason: [source_reason])") - - DB_FILTER(/datum/entity/player_sticky_ban, - DB_AND( - DB_COMP("ckey", DB_EQUALS, ckey), - DB_COMP("address", DB_EQUALS, address), - DB_COMP("computer_id", DB_EQUALS, computer_id) - ), CALLBACK(src, PROC_REF(process_stickyban), address, computer_id, source_id, reason, null)) - - .["desc"] = "\nReason: Stickybanned\nExpires: PERMANENT" - .["reason"] = "ckey/id" - return . + var/list/datum/view_record/stickyban/all_stickies = SSstickyban.check_for_sticky_ban(ckey, address, computer_id) + + if(length(all_stickies)) + var/datum/view_record/stickyban/sticky = all_stickies[1] + + if(!is_telemetry) + log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Stickybanned (Reason: [sticky.reason])") + message_admins("Failed Login: [ckey] (IP: [last_known_ip], CID: [last_known_cid]) - Stickybanned (Reason: [sticky.reason])") + + var/appeal + if(CONFIG_GET(string/banappeals)) + appeal = "\nFor more information on your ban, or to appeal, head to [CONFIG_GET(string/banappeals)]" + + .["desc"] = "\nReason: Stickybanned - [sticky.message] Identifier: [sticky.identifier]\n[appeal]" + .["reason"] = "ckey/id" + + if(!is_telemetry) + SSstickyban.match_sticky(sticky.id, ckey, address, computer_id) + return + if(!is_time_banned && !is_permabanned) return null + var/appeal if(CONFIG_GET(string/banappeals)) appeal = "\nFor more information on your ban, or to appeal, head to [CONFIG_GET(string/banappeals)]" if(is_permabanned) - permaban_admin.sync() - log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Banned [permaban_reason]") - message_admins("Failed Login: [ckey] id:[last_known_cid] ip:[last_known_ip] - Banned [permaban_reason]") - .["desc"] = "\nReason: [permaban_reason]\nExpires: PERMANENT\nBy: [permaban_admin.ckey][appeal]" + var/banner = "Host" + if(permaban_admin_id) + var/datum/view_record/players/banning_admin = locate() in DB_VIEW(/datum/view_record/players, DB_COMP("id", DB_EQUALS, permaban_admin_id)) + banner = banning_admin.ckey + if(!is_telemetry) + log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Banned [permaban_reason]") + message_admins("Failed Login: [ckey] id:[last_known_cid] ip:[last_known_ip] - Banned [permaban_reason]") + .["desc"] = "\nReason: [permaban_reason]\nExpires: PERMANENT\nBy: [banner][appeal]" .["reason"] = "ckey/id" return . if(is_time_banned) @@ -509,8 +530,9 @@ BSQL_PROTECT_DATUM(/datum/entity/player) timeleftstring = "[round(time_left / 60, 0.1)] Hours" else timeleftstring = "[time_left] Minutes" - log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Banned [time_ban_reason]") - message_admins("Failed Login: [ckey] id:[last_known_cid] ip:[last_known_ip] - Banned [time_ban_reason]") + if(!is_telemetry) + log_access("Failed Login: [ckey] [last_known_cid] [last_known_ip] - Banned [time_ban_reason]") + message_admins("Failed Login: [ckey] id:[last_known_cid] ip:[last_known_ip] - Banned [time_ban_reason]") .["desc"] = "\nReason: [time_ban_reason]\nExpires: [timeleftstring]\nBy: [time_ban_admin.ckey][appeal]" .["reason"] = "ckey/id" return . @@ -681,7 +703,6 @@ BSQL_PROTECT_DATUM(/datum/entity/player) parent_entity = /datum/entity/player child_entity = /datum/entity/player child_field = "permaban_admin_id" - parent_name = "permabanning_admin" /datum/view_record/players diff --git a/code/datums/entities/player_sticky_ban.dm b/code/datums/entities/player_sticky_ban.dm index d79befddb04e..752334e8e001 100644 --- a/code/datums/entities/player_sticky_ban.dm +++ b/code/datums/entities/player_sticky_ban.dm @@ -1,94 +1,133 @@ -/datum/entity/player_sticky_ban - var/player_id - var/admin_id +BSQL_PROTECT_DATUM(/datum/entity/stickyban) + +/datum/entity/stickyban + var/identifier var/reason + var/message var/date - var/ckey - var/address - var/computer_id - - var/linked_stickyban - -BSQL_PROTECT_DATUM(/datum/entity/player_sticky_ban) + var/active = TRUE + var/adminid -/datum/entity_meta/player_sticky_ban - entity_type = /datum/entity/player_sticky_ban - table_name = "player_sticky_bans" +/datum/entity_meta/stickyban + entity_type = /datum/entity/stickyban + table_name = "stickyban" field_types = list( - "player_id"=DB_FIELDTYPE_BIGINT, - "admin_id"=DB_FIELDTYPE_BIGINT, - "reason"=DB_FIELDTYPE_STRING_MAX, - "date"=DB_FIELDTYPE_STRING_LARGE, - "address"=DB_FIELDTYPE_STRING_LARGE, - "ckey" = DB_FIELDTYPE_STRING_LARGE, - "computer_id"=DB_FIELDTYPE_STRING_LARGE, - "linked_stickyban"=DB_FIELDTYPE_BIGINT, + "identifier" = DB_FIELDTYPE_STRING_LARGE, + "reason" = DB_FIELDTYPE_STRING_LARGE, + "message" = DB_FIELDTYPE_STRING_LARGE, + "date" = DB_FIELDTYPE_STRING_LARGE, + "active" = DB_FIELDTYPE_INT, + "adminid" = DB_FIELDTYPE_BIGINT, ) +/datum/view_record/stickyban + var/id + var/identifier + var/reason + var/message + var/date + var/active + var/admin -/datum/entity_link/linked_sticky_bans - parent_entity = /datum/entity/player_sticky_ban - child_entity = /datum/entity/player_sticky_ban - child_field = "linked_stickyban" - - parent_name = "linked_ban" - child_name = "linked_bans" - -/datum/entity_link/player_to_player_sticky_bans - parent_entity = /datum/entity/player - child_entity = /datum/entity/player_sticky_ban - child_field = "player_id" - - parent_name = "player" - child_name = "stickybans" +/datum/entity_view_meta/stickyban + root_record_type = /datum/entity/stickyban + destination_entity = /datum/view_record/stickyban + fields = list( + "id", + "identifier", + "reason", + "message", + "date", + "active", + "admin" = DB_CASE(DB_COMP("adminid", DB_ISNOT), "stickybanning_admin.ckey", DB_CONST("AdminBot")) + ) -/datum/entity_link/admin_to_player_sticky_bans +/datum/entity_link/stickyban_to_banning_admin parent_entity = /datum/entity/player - child_entity = /datum/entity/player_sticky_ban - child_field = "admin_id" + child_entity = /datum/entity/stickyban + child_field = "adminid" + parent_name = "stickybanning_admin" - parent_name = "admin" +/datum/entity/stickyban_matched_ckey + var/ckey + var/linked_stickyban + var/whitelisted = FALSE -/datum/view_record/stickyban_list_view - var/entry_id - var/player_id - var/admin_id +/datum/entity_meta/stickyban_matched_ckey + entity_type = /datum/entity/stickyban_matched_ckey + table_name = "stickyban_matched_ckey" + field_types = list( + "ckey" = DB_FIELDTYPE_STRING_LARGE, + "linked_stickyban" = DB_FIELDTYPE_BIGINT, + "whitelisted" = DB_FIELDTYPE_INT, + ) - var/reason - var/date - var/address - var/computer_id +/datum/view_record/stickyban_matched_ckey + var/id var/ckey + var/linked_stickyban var/whitelisted +/datum/entity_view_meta/stickyban_matched_ckey + root_record_type = /datum/entity/stickyban_matched_ckey + destination_entity = /datum/view_record/stickyban_matched_ckey + fields = list( + "id", + "ckey", + "linked_stickyban", + "whitelisted", + ) + + +/datum/entity/stickyban_matched_cid + var/cid var/linked_stickyban - var/linked_ckey - var/linked_reason - var/admin_ckey - var/linked_admin_ckey +/datum/entity_meta/stickyban_matched_cid + entity_type = /datum/entity/stickyban_matched_cid + table_name = "stickyban_matched_cid" + field_types = list( + "cid" = DB_FIELDTYPE_STRING_LARGE, + "linked_stickyban" = DB_FIELDTYPE_BIGINT, + ) +/datum/view_record/stickyban_matched_cid + var/id + var/cid + var/linked_stickyban -/datum/entity_view_meta/stickyban_list_view - root_record_type = /datum/entity/player_sticky_ban - destination_entity = /datum/view_record/stickyban_list_view +/datum/entity_view_meta/stickyban_matched_cid + root_record_type = /datum/entity/stickyban_matched_cid + destination_entity = /datum/view_record/stickyban_matched_cid fields = list( - "entry_id" = "id", - "player_id", - "admin_id", + "id", + "cid", + "linked_stickyban", + ) - "reason", - "date", - "address", - "computer_id", - "ckey" = "player.ckey", - "whitelisted" = "player.stickyban_whitelisted", - "linked_stickyban", - "linked_ckey" = "linked_ban.player.ckey", - "linked_reason" = "linked_ban.reason", +/datum/entity/stickyban_matched_ip + var/ip + var/linked_stickyban - "admin_ckey" = "admin.ckey", - "linked_admin_ckey" = "linked_ban.admin.ckey" +/datum/entity_meta/stickyban_matched_ip + entity_type = /datum/entity/stickyban_matched_ip + table_name = "stickyban_matched_ip" + field_types = list( + "ip" = DB_FIELDTYPE_STRING_LARGE, + "linked_stickyban" = DB_FIELDTYPE_BIGINT, + ) + +/datum/view_record/stickyban_matched_ip + var/id + var/ip + var/linked_stickyban + +/datum/entity_view_meta/stickyban_matched_ip + root_record_type = /datum/entity/stickyban_matched_ip + destination_entity = /datum/view_record/stickyban_matched_ip + fields = list( + "id", + "ip", + "linked_stickyban", ) - order_by = list("entry_id" = DB_ORDER_BY_DESC) diff --git a/code/defines/procs/admin.dm b/code/defines/procs/admin.dm index 1e4f02e95cc0..d48c02127ea9 100644 --- a/code/defines/procs/admin.dm +++ b/code/defines/procs/admin.dm @@ -1,3 +1,15 @@ -/proc/log_and_message_admins(message as text) - log_admin("[key_name(usr)] [message]") - message_admins("[key_name(usr)] [message]") +/proc/important_message_external(message, title, list/datum/tgs_chat_embed/field/fields) + if(CONFIG_GET(string/important_log_channel)) + var/datum/tgs_message_content/to_send = new("") + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = title ? title : "Important Log" + embed.description = message + embed.timestamp = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") + embed.colour = "#ED2939" + if(length(fields)) + embed.fields = fields + + to_send.embed = embed + + send2chat(to_send, CONFIG_GET(string/important_log_channel)) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 4a7307b247a5..85a1028c2296 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -1,6 +1,6 @@ #ifndef OVERRIDE_BAN_SYSTEM //Blocks an attempt to connect before even creating our client datum thing. -/world/IsBanned(key,address,computer_id, type, real_bans_only=FALSE) +/world/IsBanned(key,address,computer_id, type, real_bans_only=FALSE, is_telemetry = FALSE) var/ckey = ckey(key) // This is added siliently. Thanks to MSO for this fix. You will see it when/if we go OS @@ -28,11 +28,7 @@ var/datum/entity/player/P = get_player_from_key(ckey) - . = P.check_ban(computer_id, address) - if(.) - return . - - return ..() //default pager ban stuff + . = P.check_ban(computer_id, address, is_telemetry) #endif diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 7dca354129ff..f4394738fb2d 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -180,15 +180,44 @@ GLOBAL_DATUM(Banlist, /savefile) expiry = "Removal Pending" else expiry = "Permaban" - var/unban_link = "(U)" + var/unban_link + if(ban.is_permabanned) + unban_link = "(UP)" + else + unban_link = "(UT)" - dat += "[unban_link] Key: [ban.ckey]ComputerID: [ban.last_known_cid]IP: [ban.last_known_ip] [expiry](By: [ban.admin])(Reason: [ban.reason])" + dat += "[unban_link] Key: [ban.ckey]ComputerID: [ban.last_known_cid]IP: [ban.last_known_ip] [expiry](By: [ban.admin ? ban.admin : "AdminBot"])(Reason: [ban.reason])" dat += "" - var/dat_header = "
Bans: (U) = Unban" + var/dat_header = "
Bans: (UP) = Unban Perma (UT) = Unban Timed" dat_header += " - Ban Listing
[dat]" show_browser(usr, dat_header, "Unban Panel", "unbanp", "size=875x400") +/datum/admins/proc/stickypanel() + var/add_sticky = "Add Sticky Ban" + var/find_sticky = "Find Sticky Ban" + + var/data = "
Sticky Bans: [add_sticky] [find_sticky]
" + + var/list/datum/view_record/stickyban/stickies = DB_VIEW(/datum/view_record/stickyban, + DB_COMP("active", DB_EQUALS, TRUE) + ) + + for(var/datum/view_record/stickyban/current_sticky in stickies) + var/whitelist_link = "(WHITELIST)" + var/remove_sticky_link = "(REMOVE)" + var/add_to_sticky_link = "(ADD)" + + var/impacted_ckey_link = "CKEYs" + var/impacted_ip_link = "IPs" + var/impacted_cid_link = "CIDs" + + data += "" + + data += "
[whitelist_link][remove_sticky_link][add_to_sticky_link]Identifier: [current_sticky.identifier]Reason: [current_sticky.reason]Message: [current_sticky.message] Admin: [current_sticky.admin] View: [impacted_ckey_link][impacted_ip_link][impacted_cid_link]
" + + show_browser(owner, data, "Stickyban Panel", "sticky", "size=875x400") + //////////////////////////////////// DEBUG //////////////////////////////////// /proc/CreateBans() @@ -251,3 +280,48 @@ GLOBAL_DATUM(Banlist, /savefile) if(P.is_time_banned && alert(usr, "Ban already exists. Proceed?", "Confirmation", "Yes", "No") != "Yes") return P.add_timed_ban(reason, mins) + +/client/proc/cmd_admin_do_stickyban(identifier, reason, message, list/impacted_ckeys, list/impacted_cids, list/impacted_ips) + if(!identifier) + identifier = tgui_input_text(src, "Name of the primary CKEY you are adding a stickyban to.", "BuildABan") + if(!identifier) + return + + if(!message) + message = tgui_input_text(src, "What message should be given to the impacted users?", "BuildABan", encode = FALSE) + if(!message) + return + + if(!reason) + reason = tgui_input_text(src, "What's the reason for the ban? This is shown internally, and not displayed in public notes and ban messages. Include as much detail as necessary.", "BuildABan", multiline = TRUE, encode = FALSE) + if(!reason) + return + + if(!length(impacted_ckeys)) + impacted_ckeys = splittext(tgui_input_text(src, "Which CKEYs should be impacted by this ban? Include the primary ckey, separated by semicolons.", "BuildABan", "player1;player2;player3"), ";") + + if(!length(impacted_cids)) + impacted_cids = splittext(tgui_input_text(src, "Which CIDs should be impacted by this ban? Separate with semicolons.", "BuildABan", "12345678;87654321"), ";") + + if(!length(impacted_ips)) + impacted_ips = splittext(tgui_input_text(src, "Which IPs should be impacted by this ban? Separate with semicolons.", "BuildABan", "1.1.1.1;8.8.8.8"), ";") + + var/datum/entity/stickyban/new_sticky = SSstickyban.add_stickyban(identifier, reason, message, player_data) + + if(!new_sticky) + to_chat(src, SPAN_ADMIN("Failed to apply stickyban.")) + return + + for(var/ckey in impacted_ckeys) + SSstickyban.add_matched_ckey(new_sticky.id, ckey) + + for(var/cid in impacted_cids) + SSstickyban.add_matched_cid(new_sticky.id, cid) + + for(var/ip in impacted_ips) + SSstickyban.add_matched_ip(new_sticky.id, ip) + + log_admin("STICKYBAN: Identifier: [identifier] Reason: [reason] Message: [message] CKEYs: [english_list(impacted_ckeys)] IPs: [english_list(impacted_ips)] CIDs: [english_list(impacted_cids)]") + message_admins("[key_name_admin(src)] has added a new stickyban with the identifier '[identifier]'.") + var/datum/tgs_chat_embed/field/reason_embed = new("Stickyban Reason", reason) + important_message_external("[src] has added a new stickyban with the identifier '[identifier]'.", "Stickyban Placed", list(reason_embed)) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 43b06b6d80bc..f3eae1447ba0 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -96,8 +96,9 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( )) GLOBAL_LIST_INIT(admin_verbs_ban, list( - /client/proc/unban_panel - // /client/proc/jobbans // Disabled temporarily due to 15-30 second lag spikes. Don't forget the comma in the line above when uncommenting this! + /client/proc/unban_panel, + /client/proc/stickyban_panel, + // /client/proc/jobbans // Disabled temporarily due to 15-30 second lag spikes. )) GLOBAL_LIST_INIT(admin_verbs_sounds, list( diff --git a/code/modules/admin/player_panel/actions/punish.dm b/code/modules/admin/player_panel/actions/punish.dm index 576de30ae7ff..eb80632d8afe 100644 --- a/code/modules/admin/player_panel/actions/punish.dm +++ b/code/modules/admin/player_panel/actions/punish.dm @@ -45,6 +45,58 @@ return TRUE +/datum/player_action/permanent_ban + action_tag = "permanent_ban" + name = "Permanent Ban" + permissions_required = R_BAN + +/datum/player_action/permanent_ban/act(client/user, mob/target, list/params) + var/reason = tgui_input_text(user, "What message should be given to the permabanned user?", "Permanent Ban", encode = FALSE) + if(!reason) + return + + var/internal_reason = tgui_input_text(user, "What's the reason for the ban? This is shown internally, and not displayed in public notes and ban messages. Include as much detail as necessary.", "Permanent Ban", multiline = TRUE, encode = FALSE) + if(!internal_reason) + return + + var/datum/entity/player/target_entity = target.client?.player_data + if(!target_entity) + target_entity = get_player_from_key(target.ckey || target.persistent_ckey) + + if(!target_entity) + return + + if(!target_entity.add_perma_ban(reason, internal_reason, user.player_data)) + to_chat(user, SPAN_ADMIN("The user is already permabanned! If necessary, you can remove the permaban, and place a new one.")) + +/datum/player_action/sticky_ban + action_tag = "sticky_ban" + name = "Sticky Ban" + permissions_required = R_BAN + +/datum/player_action/sticky_ban/act(client/user, mob/target, list/params) + var/datum/entity/player/player = get_player_from_key(target.ckey || target.persistent_ckey) + if(!player) + return + + var/persistent_ip = target.client?.address || player.last_known_ip + var/persistent_cid = target.client?.computer_id || player.last_known_cid + + var/message = tgui_input_text(user, "What message should be given to the impacted users?", "BuildABan", encode = FALSE) + if(!message) + return + + var/reason = tgui_input_text(user, "What's the reason for the ban? This is shown internally, and not displayed in public notes and ban messages. Include as much detail as necessary.", "BuildABan", multiline = TRUE, encode = FALSE) + if(!reason) + return + + user.cmd_admin_do_stickyban(target.ckey, reason, message, impacted_ckeys = list(target.ckey), impacted_cids = list(persistent_cid), impacted_ips = list(persistent_ip)) + player.add_note("Stickybanned | [message]", FALSE, NOTE_ADMIN, TRUE) + player.add_note("Internal reason: [reason]", TRUE, NOTE_ADMIN) + + if(target.client) + qdel(target.client) + /datum/player_action/mute action_tag = "mob_mute" name = "Mute" diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm deleted file mode 100644 index 69793a599596..000000000000 --- a/code/modules/admin/stickyban.dm +++ /dev/null @@ -1,66 +0,0 @@ -// BLOCKING PROC, RUN ASYNC - -/proc/stickyban_internal(ckey, address, cid, reason, linked_stickyban, datum/entity/player/banning_admin) - - if(!ckey || !address || !cid) - CRASH("Incorrect data passed to stickyban_internal ([ckey], [address], [cid])") - - var/datum/entity/player/P = get_player_from_key(ckey) - - if(!P) - message_admins("Tried stickybanning ckey \"[ckey]\", player entity was unable to be found. Please try again later.") - return - - var/datum/entity/player_sticky_ban/PSB = DB_ENTITY(/datum/entity/player_sticky_ban) - PSB.player_id = P.id - if(reason) - PSB.reason = reason - PSB.address = address - PSB.computer_id = cid - PSB.ckey = P.ckey - PSB.date = "[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]" - - if(linked_stickyban) - PSB.linked_stickyban = linked_stickyban - - if(!reason) - reason = "No reason given." - - if(banning_admin) - PSB.admin_id = banning_admin.id - if(banning_admin.owning_client) - message_admins("[banning_admin.owning_client.ckey] has stickybanned [ckey].") - - message_admins("[ckey] (IP: [address], CID: [cid]) has been stickybanned for: \"[reason]\".") - - if(P.owning_client) - to_chat_forced(P.owning_client, SPAN_WARNING("You have been sticky banned by [banning_admin? banning_admin.ckey : "Host"].\nReason: [sanitize(reason)].")) - to_chat_forced(P.owning_client, SPAN_WARNING("This is a permanent ban")) - QDEL_NULL(P.owning_client) - - PSB.save() - -/datum/entity/player/proc/process_stickyban(address, computer_id, source_id, reason, datum/entity/player/banning_admin, list/PSB) - if(length(PSB) > 0) // sticky ban with identical data already exists, no need for another copy - if(banning_admin) - to_chat(banning_admin, SPAN_WARNING("Failed to add stickyban to [ckey]. Reason: Stickyban already exists.")) - return - - stickyban_internal(ckey, address, computer_id, reason, source_id, banning_admin) - - -/datum/entity/player/proc/check_for_sticky_ban(address, computer_id) - var/list/datum/view_record/stickyban_list_view/SBLW = DB_VIEW(/datum/view_record/stickyban_list_view, - DB_OR( - DB_COMP("ckey", DB_EQUALS, ckey), - DB_COMP("address", DB_EQUALS, address), - DB_COMP("computer_id", DB_EQUALS, computer_id) - )) - - if(length(SBLW) == 0) - return - - if(stickyban_whitelisted) - return - - return SBLW diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm index 8dce41ac8235..356762b5edd7 100644 --- a/code/modules/admin/tabs/admin_tab.dm +++ b/code/modules/admin/tabs/admin_tab.dm @@ -53,6 +53,12 @@ admin_holder.unbanpanel() return +/client/proc/stickyban_panel() + set name = "Stickyban Panel" + set category = "Admin.Panels" + + admin_holder?.stickypanel() + /client/proc/player_panel_new() set name = "Player Panel" set category = "Admin.Panels" diff --git a/code/modules/admin/topic/topic.dm b/code/modules/admin/topic/topic.dm index 825f7c3cdbf4..ecef2627ed3c 100644 --- a/code/modules/admin/topic/topic.dm +++ b/code/modules/admin/topic/topic.dm @@ -229,6 +229,200 @@ alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") unbanpanel() + else if(href_list["unban_perma"]) + var/datum/entity/player/unban_player = get_player_from_key(href_list["unban_perma"]) + if(!(tgui_alert(owner, "Do you want to unban [unban_player.ckey]? They are currently permabanned for: [unban_player.permaban_reason], since [unban_player.permaban_date].", "Unban Player", list("Yes", "No")) == "Yes")) + return + + if(!unban_player.is_permabanned) + to_chat(owner, "The player is not currently permabanned.") + + unban_player.is_permabanned = FALSE + unban_player.permaban_admin_id = null + unban_player.permaban_date = null + unban_player.permaban_reason = null + + unban_player.save() + + message_admins("[key_name_admin(owner)] has removed the permanent ban on [unban_player.ckey].") + important_message_external("[owner] has removed the permanent ban on [unban_player.ckey].", "Permaban Removed") + + else if(href_list["sticky"]) + if(href_list["view_all_ckeys"]) + var/list/datum/view_record/stickyban_matched_ckey/all_ckeys = DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_COMP("linked_stickyban", DB_EQUALS, href_list["sticky"]) + ) + + var/list/keys = list() + var/list/whitelisted = list() + for(var/datum/view_record/stickyban_matched_ckey/match as anything in all_ckeys) + if(match.whitelisted) + whitelisted += match.ckey + else + keys += match.ckey + + show_browser(owner, "Impacted: [english_list(keys)]

Whitelisted: [english_list(whitelisted)]", "Stickyban Keys", "stickykeys") + return + + if(href_list["view_all_cids"]) + var/list/datum/view_record/stickyban_matched_cid/all_cids = DB_VIEW(/datum/view_record/stickyban_matched_cid, + DB_COMP("linked_stickyban", DB_EQUALS, href_list["sticky"]) + ) + + var/list/cids = list() + for(var/datum/view_record/stickyban_matched_cid/match as anything in all_cids) + cids += match.cid + + show_browser(owner, english_list(cids), "Stickyban CIDs", "stickycids") + return + + if(href_list["view_all_ips"]) + var/list/datum/view_record/stickyban_matched_ip/all_ips = DB_VIEW(/datum/view_record/stickyban_matched_ip, + DB_COMP("linked_stickyban", DB_EQUALS, href_list["sticky"]) + ) + + var/list/ips = list() + for(var/datum/view_record/stickyban_matched_ip/match as anything in all_ips) + ips += match.ip + + show_browser(owner, english_list(ips), "Stickyban IPs", "stickycips") + return + + if(href_list["find_sticky"]) + var/ckey = ckey(tgui_input_text(owner, "Which CKEY should we attempt to find stickybans for?", "FindABan")) + if(!ckey) + return + + var/list/datum/view_record/stickyban/stickies = SSstickyban.check_for_sticky_ban(ckey) + if(!stickies) + to_chat(owner, SPAN_ADMIN("Could not locate any stickbans impacting [ckey].")) + return + + var/list/impacting_stickies = list() + + for(var/datum/view_record/stickyban/sticky as anything in stickies) + impacting_stickies += sticky.identifier + + to_chat(owner, SPAN_ADMIN("Found the following stickybans for [ckey]: [english_list(impacting_stickies)]")) + + if(!check_rights_for(owner, R_BAN)) + return + + if(href_list["new_sticky"]) + owner.cmd_admin_do_stickyban() + return + + var/datum/entity/stickyban/sticky = DB_ENTITY(/datum/entity/stickyban, href_list["sticky"]) + if(!sticky) + return + + sticky.sync() + + if(href_list["whitelist_ckey"]) + var/ckey_to_whitelist = ckey(tgui_input_text(owner, "What CKEY should be whitelisted? Editing stickyban: [sticky.identifier]")) + if(!ckey_to_whitelist) + return + + SSstickyban.whitelist_ckey(sticky.id, ckey_to_whitelist) + message_admins("[key_name_admin(owner)] has whitelisted [ckey_to_whitelist] against stickyban '[sticky.identifier]'.") + important_message_external("[owner] has whitelisted [ckey_to_whitelist] against stickyban '[sticky.identifier]'.", "CKEY Whitelisted") + + if(href_list["add"]) + var/option = tgui_input_list(owner, "What do you want to add?", "AddABan", list("CID", "CKEY", "IP")) + if(!option) + return + + var/to_add = tgui_input_text(owner, "Provide the [option] to add to the stickyban.", "AddABan") + if(!to_add) + return + + switch(option) + if("CID") + SSstickyban.add_matched_cid(sticky.id, to_add) + if("CKEY") + SSstickyban.add_matched_ckey(sticky.id, to_add) + if("IP") + SSstickyban.add_matched_ip(sticky.id, to_add) + + message_admins("[key_name_admin(owner)] has added a [option] ([to_add]) to stickyban '[sticky.identifier]'.") + important_message_external("[owner] has added a [option] ([to_add]) to stickyban '[sticky.identifier]'.", "[option] Added to Stickyban") + + if(href_list["remove"]) + var/option = tgui_input_list(owner, "What do you want to remove?", "DelABan", list("Entire Stickyban", "CID", "CKEY", "IP")) + switch(option) + if("Entire Stickyban") + if(!(tgui_alert(owner, "Are you sure you want to remove this stickyban? Identifier: [sticky.identifier] Reason: [sticky.reason]", "Confirm", list("Yes", "No")) == "Yes")) + return + + sticky.active = FALSE + sticky.save() + + message_admins("[key_name_admin(owner)] has deactivated stickyban '[sticky.identifier]'.") + important_message_external("[owner] has deactivated stickyban '[sticky.identifier]'.", "Stickyban Deactivated") + + if("CID") + var/list/datum/view_record/stickyban_matched_cid/all_cids = DB_VIEW(/datum/view_record/stickyban_matched_cid, + DB_COMP("linked_stickyban", DB_EQUALS, sticky.id) + ) + + var/list/cid_to_record_id = list() + for(var/datum/view_record/stickyban_matched_cid/match in all_cids) + cid_to_record_id["[match.cid]"] = match.id + + var/picked = tgui_input_list(owner, "Which CID to remove?", "DelABan", cid_to_record_id) + if(!picked) + return + + var/selected = cid_to_record_id[picked] + + var/datum/entity/stickyban_matched_cid/sticky_cid = DB_ENTITY(/datum/entity/stickyban_matched_cid, selected) + sticky_cid.delete() + + message_admins("[key_name_admin(owner)] has removed a CID ([picked]) from stickyban '[sticky.identifier]'.") + important_message_external("[owner] has removed a CID ([picked]) from stickyban '[sticky.identifier]'.", "CID Removed from Stickyban") + + if("CKEY") + var/list/datum/view_record/stickyban_matched_ckey/all_ckeys = DB_VIEW(/datum/view_record/stickyban_matched_ckey, + DB_COMP("linked_stickyban", DB_EQUALS, sticky.id) + ) + + var/list/ckey_to_record_id = list() + for(var/datum/view_record/stickyban_matched_ckey/match in all_ckeys) + ckey_to_record_id["[match.ckey]"] = match.id + + var/picked = tgui_input_list(owner, "Which CKEY to remove?", "DelABan", ckey_to_record_id) + if(!picked) + return + + var/selected = ckey_to_record_id[picked] + + var/datum/entity/stickyban_matched_ckey/sticky_ckey = DB_ENTITY(/datum/entity/stickyban_matched_ckey, selected) + sticky_ckey.delete() + + message_admins("[key_name_admin(owner)] has removed a CKEY ([picked]) from stickyban '[sticky.identifier]'.") + important_message_external("[owner] has removed a CKEY ([picked]) from stickyban '[sticky.identifier]'.", "CKEY Removed from Stickyban") + + if("IP") + var/list/datum/view_record/stickyban_matched_ip/all_ips = DB_VIEW(/datum/view_record/stickyban_matched_ip, + DB_COMP("linked_stickyban", DB_EQUALS, sticky.id) + ) + + var/list/ip_to_record_id = list() + for(var/datum/view_record/stickyban_matched_ip/match in all_ips) + ip_to_record_id["[match.ip]"] = match.id + + var/picked = tgui_input_list(owner, "Which IP to remove?", "DelABan", ip_to_record_id) + if(!picked) + return + + var/selected = ip_to_record_id[picked] + + var/datum/entity/stickyban_matched_ip/sticky_ip = DB_ENTITY(/datum/entity/stickyban_matched_ip, selected) + sticky_ip.delete() + + message_admins("[key_name_admin(owner)] has removed an IP ([picked]) from stickyban [sticky.identifier].") + important_message_external("[owner] has removed an IP ([picked]) from stickyban '[sticky.identifier].", "IP Removed from Stickyban") + else if(href_list["warn"]) usr.client.warn(href_list["warn"]) diff --git a/code/modules/admin/verbs/autoreplace.dm b/code/modules/admin/verbs/autoreplace.dm index b2fe04cfb4a3..a896c751f5ed 100644 --- a/code/modules/admin/verbs/autoreplace.dm +++ b/code/modules/admin/verbs/autoreplace.dm @@ -32,7 +32,7 @@ GLOBAL_LIST_INIT_TYPED(admin_runtime_decorators, /datum/decorator/manual/admin_r GLOB.admin_runtime_decorators.Add(SSdecorator.add_decorator(/datum/decorator/manual/admin_runtime, types, subtypes, field, value)) - log_and_message_admins("[src] activated new decorator id: [GLOB.admin_runtime_decorators.len] set for [hint_text] `[types]` for field `[field]` set value `[value]`") + message_admins("[src] activated new decorator id: [GLOB.admin_runtime_decorators.len] set for [hint_text] `[types]` for field `[field]` set value `[value]`") /client/proc/deactivate_autoreplacer() set category = "Admin.Events" @@ -49,7 +49,7 @@ GLOBAL_LIST_INIT_TYPED(admin_runtime_decorators, /datum/decorator/manual/admin_r GLOB.admin_runtime_decorators[num_value].enabled = FALSE - log_and_message_admins("[src] deactivated decorator id: [num_value]") + message_admins("[src] deactivated decorator id: [num_value]") /client/proc/rerun_decorators() set category = "Admin.Events" @@ -65,4 +65,4 @@ GLOBAL_LIST_INIT_TYPED(admin_runtime_decorators, /datum/decorator/manual/admin_r SSdecorator.force_update() - log_and_message_admins("[src] rerun all decorators.") + message_admins("[src] rerun all decorators.") diff --git a/code/modules/clans/client.dm b/code/modules/clans/client.dm index c4948b2a6923..3450b7553c51 100644 --- a/code/modules/clans/client.dm +++ b/code/modules/clans/client.dm @@ -234,7 +234,7 @@ return - log_and_message_admins("[key_name_admin(src)] has set the name of [target_clan.name] to [input].") + message_admins("[key_name_admin(src)] has set the name of [target_clan.name] to [input].") to_chat(src, SPAN_NOTICE("Set the name of [target_clan.name] to [input].")) target_clan.name = trim(input) @@ -247,7 +247,7 @@ if(!input || input == target_clan.description) return - log_and_message_admins("[key_name_admin(src)] has set the description of [target_clan.name].") + message_admins("[key_name_admin(src)] has set the description of [target_clan.name].") to_chat(src, SPAN_NOTICE("Set the description of [target_clan.name].")) target_clan.description = trim(input) @@ -261,7 +261,7 @@ return target_clan.color = color - log_and_message_admins("[key_name_admin(src)] has set the color of [target_clan.name] to [color].") + message_admins("[key_name_admin(src)] has set the color of [target_clan.name] to [color].") to_chat(src, SPAN_NOTICE("Set the name of [target_clan.name] to [color].")) if(CLAN_ACTION_CLAN_SETHONOR) if(!has_clan_permission(CLAN_PERMISSION_ADMIN_MANAGER)) @@ -272,7 +272,7 @@ if((!input && input != 0) || input == target_clan.honor) return - log_and_message_admins("[key_name_admin(src)] has set the honor of clan [target_clan.name] from [target_clan.honor] to [input].") + message_admins("[key_name_admin(src)] has set the honor of clan [target_clan.name] from [target_clan.honor] to [input].") to_chat(src, SPAN_NOTICE("Set the honor of [target_clan.name] from [target_clan.honor] to [input].")) target_clan.honor = input @@ -286,7 +286,7 @@ to_chat(src, "You have decided not to delete [target_clan.name].") return - log_and_message_admins("[key_name_admin(src)] has deleted the clan [target_clan.name].") + message_admins("[key_name_admin(src)] has deleted the clan [target_clan.name].") to_chat(src, SPAN_NOTICE("You have deleted [target_clan.name].")) var/list/datum/view_record/clan_playerbase_view/CPV = DB_VIEW(/datum/view_record/clan_playerbase_view, DB_COMP("clan_id", DB_EQUALS, target_clan.id)) @@ -339,7 +339,7 @@ return var/target_clan = target.clan_id - log_and_message_admins("[key_name_admin(src)] has purged [player_name]'s clan profile.") + message_admins("[key_name_admin(src)] has purged [player_name]'s clan profile.") to_chat(src, SPAN_NOTICE("You have purged [player_name]'s clan profile.")) target.delete() @@ -379,20 +379,20 @@ target.clan_id = null target.clan_rank = GLOB.clan_ranks_ordered[CLAN_RANK_YOUNG] to_chat(src, SPAN_NOTICE("Removed [player_name] from their clan.")) - log_and_message_admins("[key_name_admin(src)] has removed [player_name] from their current clan.") + message_admins("[key_name_admin(src)] has removed [player_name] from their current clan.") else if(input == "Remove from Ancient") target.clan_rank = GLOB.clan_ranks_ordered[CLAN_RANK_YOUNG] target.permissions = GLOB.clan_ranks[CLAN_RANK_YOUNG].permissions to_chat(src, SPAN_NOTICE("Removed [player_name] from ancient.")) - log_and_message_admins("[key_name_admin(src)] has removed [player_name] from ancient.") + message_admins("[key_name_admin(src)] has removed [player_name] from ancient.") else if(input == "Make Ancient" && is_clan_manager) target.clan_rank = GLOB.clan_ranks_ordered[CLAN_RANK_ADMIN] target.permissions = CLAN_PERMISSION_ADMIN_ANCIENT to_chat(src, SPAN_NOTICE("Made [player_name] an ancient.")) - log_and_message_admins("[key_name_admin(src)] has made [player_name] an ancient.") + message_admins("[key_name_admin(src)] has made [player_name] an ancient.") else to_chat(src, SPAN_NOTICE("Moved [player_name] to [input].")) - log_and_message_admins("[key_name_admin(src)] has moved [player_name] to clan [input].") + message_admins("[key_name_admin(src)] has moved [player_name] to clan [input].") target.clan_id = clans[input] @@ -455,7 +455,7 @@ target.clan_rank = GLOB.clan_ranks_ordered[chosen_rank.name] target.permissions = chosen_rank.permissions - log_and_message_admins("[key_name_admin(src)] has set the rank of [player_name] to [chosen_rank.name] for their clan.") + message_admins("[key_name_admin(src)] has set the rank of [player_name] to [chosen_rank.name] for their clan.") to_chat(src, SPAN_NOTICE("Set [player_name]'s rank to [chosen_rank.name]")) target.save() diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm index 2e5c6a9112a3..d89e8572caff 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm @@ -595,7 +595,7 @@ xeno_announcement(input, hivenumber, "The words of the [name] reverberate in our head...") - log_and_message_admins("[key_name_admin(src)] has created a Word of the Queen report:") + message_admins("[key_name_admin(src)] has created a Word of the Queen report:") log_admin("[key_name_admin(src)] Word of the Queen: [input]") return TRUE diff --git a/code/modules/tgui_panel/telemetry.dm b/code/modules/tgui_panel/telemetry.dm index eb5c7d96a4d9..4ef1f06bfac0 100644 --- a/code/modules/tgui_panel/telemetry.dm +++ b/code/modules/tgui_panel/telemetry.dm @@ -86,7 +86,7 @@ // Check for a malformed history object if (!row || row.len < 3 || (!row["ckey"] || !row["address"] || !row["computer_id"])) - return + continue /* TODO - Reintroduce this when we get a proper round ID tracking, and we want to log it to database @@ -103,7 +103,7 @@ continue */ - if (world.IsBanned(row["ckey"], row["address"], row["computer_id"], real_bans_only = TRUE)) + if (world.IsBanned(row["ckey"], row["address"], row["computer_id"], real_bans_only = TRUE, is_telemetry = TRUE)) found = row break diff --git a/colonialmarines.dme b/colonialmarines.dme index 09370e73b4b1..16bb16212f71 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -298,6 +298,7 @@ #include "code\controllers\subsystem\sound_loops.dm" #include "code\controllers\subsystem\soundscape.dm" #include "code\controllers\subsystem\statpanel.dm" +#include "code\controllers\subsystem\stickyban.dm" #include "code\controllers\subsystem\techtree.dm" #include "code\controllers\subsystem\tgui.dm" #include "code\controllers\subsystem\ticker.dm" @@ -1398,7 +1399,6 @@ #include "code\modules\admin\NewBan.dm" #include "code\modules\admin\player_notes.dm" #include "code\modules\admin\server_verbs.dm" -#include "code\modules\admin\stickyban.dm" #include "code\modules\admin\STUI.dm" #include "code\modules\admin\tag.dm" #include "code\modules\admin\medal_panel\medals_panel.dm" diff --git a/tgui/packages/tgui/interfaces/PlayerPanel.jsx b/tgui/packages/tgui/interfaces/PlayerPanel.jsx index 85f73b581c67..88b30aa51802 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel.jsx +++ b/tgui/packages/tgui/interfaces/PlayerPanel.jsx @@ -236,7 +236,6 @@ const GeneralActions = (props) => { /> { />