diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 9c2a1a2883..75efefd8e1 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -3098,7 +3098,9 @@ /turf/open/floor/plating, /area/maintenance/fore/secondary) "agd" = ( -/obj/machinery/atmospherics/pipe/manifold4w/general/visible, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 8 + }, /turf/open/floor/plasteel, /area/engine/atmos) "age" = ( @@ -6473,16 +6475,6 @@ }, /turf/open/floor/plasteel/white, /area/science/xenobiology) -"alX" = ( -/obj/machinery/button/door{ - id = "atmos"; - name = "Atmospherics Lockdown"; - pixel_x = 24; - pixel_y = 4; - req_access_txt = "24" - }, -/turf/open/floor/plasteel, -/area/engine/atmos) "alY" = ( /obj/structure/cable{ icon_state = "1-2" @@ -39435,7 +39427,10 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bPe" = ( -/obj/machinery/atmospherics/pipe/manifold/yellow/visible, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Pure to Ports" + }, /turf/open/floor/plasteel, /area/engine/atmos) "bPf" = ( @@ -39453,10 +39448,10 @@ /area/engine/atmos) "bPh" = ( /obj/machinery/atmospherics/pipe/simple/cyan/visible, -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/layer_manifold{ dir = 4 }, -/obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/engine/atmos) "bPi" = ( @@ -40719,7 +40714,9 @@ /area/engine/atmos) "bSM" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/general/visible, +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 5 + }, /turf/open/floor/plating, /area/engine/atmos) "bSP" = ( @@ -41475,13 +41472,16 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bUN" = ( -/obj/machinery/atmospherics/pipe/manifold/general/visible, +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, /turf/open/floor/plasteel, /area/engine/atmos) "bUO" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 8; - name = "Ports to External" +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 }, /turf/open/floor/plasteel, /area/engine/atmos) @@ -41912,8 +41912,9 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bVZ" = ( -/obj/machinery/atmospherics/pipe/manifold/general/visible{ - dir = 8 +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Ports to External" }, /turf/open/floor/plasteel, /area/engine/atmos) @@ -46658,8 +46659,8 @@ /turf/closed/wall/r_wall, /area/engine/atmos) "ciu" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible, /obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/layer_manifold, /turf/open/floor/plating, /area/engine/atmos) "civ" = ( @@ -46703,8 +46704,7 @@ /obj/structure/reagent_dispensers/watertank, /obj/item/extinguisher, /obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4; - pixel_y = 0 + dir = 4 }, /turf/open/floor/plasteel, /area/maintenance/disposal/incinerator) @@ -53525,8 +53525,9 @@ /turf/open/floor/plasteel, /area/security/brig) "daq" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible, -/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 + }, /turf/open/floor/plasteel, /area/engine/atmos) "daI" = ( @@ -53948,9 +53949,7 @@ /turf/open/floor/plating, /area/security/prison) "eqq" = ( -/obj/machinery/atmospherics/pipe/manifold/orange/visible{ - dir = 4 - }, +/obj/machinery/atmospherics/pipe/manifold/orange/visible, /turf/open/floor/plasteel, /area/engine/atmos) "eqA" = ( @@ -55365,7 +55364,7 @@ "ivJ" = ( /obj/machinery/atmospherics/pipe/simple/cyan/visible, /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/purple/visible{ +/obj/machinery/atmospherics/pipe/layer_manifold{ dir = 4 }, /turf/open/floor/plating, @@ -55418,7 +55417,7 @@ /obj/machinery/atmospherics/pipe/simple/cyan/visible{ dir = 4 }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/atmospherics/pipe/layer_manifold, /turf/open/floor/plating, /area/engine/atmos) "iLJ" = ( @@ -55709,11 +55708,7 @@ /turf/closed/wall/r_wall, /area/engine/gravity_generator) "jBQ" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 8; - name = "Pure to Ports" - }, -/obj/machinery/atmospherics/pipe/simple/general/visible, +/obj/machinery/atmospherics/pipe/manifold4w/general/visible, /turf/open/floor/plasteel, /area/engine/atmos) "jCq" = ( @@ -56729,6 +56724,10 @@ }, /turf/open/floor/wood, /area/crew_quarters/bar) +"moS" = ( +/obj/machinery/atmospherics/pipe/manifold4w/yellow/visible, +/turf/open/floor/plasteel, +/area/engine/atmos) "mps" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -57774,7 +57773,9 @@ /area/crew_quarters/fitness) "pCj" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ + dir = 5 + }, /turf/open/floor/plating, /area/engine/atmos) "pDe" = ( @@ -57916,10 +57917,11 @@ /turf/open/floor/plasteel, /area/security/processing) "pUP" = ( -/obj/machinery/atmospherics/pipe/simple/orange/visible{ - dir = 5 +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 4 }, -/turf/open/floor/plasteel, +/turf/open/floor/plating, /area/engine/atmos) "qaY" = ( /obj/structure/table/reinforced, @@ -58470,6 +58472,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/maintenance/fore) +"rTo" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "External to Pumps" + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "rTu" = ( /obj/structure/cable{ icon_state = "4-8" @@ -60035,18 +60044,18 @@ /turf/open/floor/plasteel/white, /area/science/circuit) "vPQ" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 8; - name = "External to Pumps" +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ + dir = 10 }, -/turf/open/floor/plasteel, +/turf/open/floor/plating, /area/engine/atmos) "vQf" = ( /obj/structure/cable{ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/cyan/visible, -/obj/machinery/atmospherics/components/binary/pump/on{ +/obj/machinery/atmospherics/components/binary/pump{ dir = 8; name = "Mix to External" }, @@ -93434,7 +93443,7 @@ bQp alk gAu eqq -pUP +bOd bOd bOd bOd @@ -93690,8 +93699,8 @@ bJF bVX bRA bTP +rTo bOd -vPQ bOd bOd bOd @@ -93948,7 +93957,7 @@ bMQ bRC rdl daq -bUN +bUO bOd bOd bOd @@ -94205,7 +94214,7 @@ cBF bRD rdl daq -bUN +bUO bOd bOd bOd @@ -94461,7 +94470,7 @@ bPc bQs cez ceA -bOd +daq bUO bOd bOd @@ -94719,7 +94728,7 @@ bQv bRF bSM bVZ -bUN +bOd bOd bOd bOd @@ -94974,7 +94983,7 @@ bOc udT omk nTU -bSM +pUP jBQ agd cCB @@ -95488,8 +95497,8 @@ oVN bPf bQx bRH -bMQ -bTU +vPQ +moS kGv bUS cCE @@ -96003,15 +96012,15 @@ ivJ bQy bRI bSP -bPh +ivJ jzM bRI cCF -bPh +ivJ bQy bRI bQy -bPh +ivJ bQy bRI bSP diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 75af28d174..9d36d26f6f 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -39336,6 +39336,9 @@ /obj/machinery/atmospherics/pipe/simple/orange/visible{ dir = 4 }, +/obj/effect/turf_decal/stripes/line{ + dir = 6 + }, /turf/open/floor/plasteel, /area/engine/atmos) "byY" = ( @@ -40124,6 +40127,7 @@ /obj/structure/cable/yellow{ icon_state = "2-4" }, +/obj/machinery/light/small, /turf/open/floor/plasteel, /area/engine/atmos) "bAI" = ( @@ -41633,7 +41637,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 5 }, -/turf/closed/wall/r_wall, +/turf/closed/wall, /area/engine/atmos) "bDS" = ( /obj/effect/turf_decal/tile/yellow{ @@ -42650,7 +42654,7 @@ /area/engine/atmos) "bFI" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/closed/wall, +/turf/closed/wall/r_wall, /area/engine/atmos) "bFJ" = ( /obj/structure/disposalpipe/segment{ @@ -42659,7 +42663,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 1 }, -/turf/open/floor/plasteel/dark/corner, +/turf/open/floor/plasteel, /area/engine/atmos) "bFM" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ @@ -42702,15 +42706,13 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bFR" = ( -/obj/machinery/atmospherics/pipe/simple/purple/visible{ - dir = 6 +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 }, -/turf/open/floor/plasteel, +/turf/closed/wall, /area/engine/atmos) "bFS" = ( -/obj/machinery/atmospherics/pipe/simple/purple/visible{ - dir = 9 - }, +/obj/machinery/atmospherics/pipe/simple/purple/visible, /turf/open/floor/plasteel, /area/engine/atmos) "bFT" = ( @@ -43345,9 +43347,8 @@ /area/engine/atmos) "bHx" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/purple/visible, -/obj/machinery/atmospherics/pipe/simple/cyan/visible{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/cyan/visible{ + dir = 1 }, /turf/open/floor/plating, /area/engine/atmos) @@ -44017,7 +44018,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 }, -/turf/closed/wall/r_wall, +/turf/closed/wall, /area/engine/atmos) "bIP" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible, @@ -44047,27 +44048,29 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bIT" = ( -/obj/machinery/atmospherics/pipe/manifold/cyan/visible{ - dir = 4 - }, /obj/machinery/meter, +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ + dir = 9 + }, /turf/open/floor/plasteel, /area/engine/atmos) "bIU" = ( -/obj/machinery/atmospherics/pipe/simple/purple/visible, -/obj/machinery/meter, +/obj/machinery/atmospherics/components/binary/pump{ + name = "Air to Ports" + }, /turf/open/floor/plasteel, /area/engine/atmos) "bIV" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 6 +/obj/machinery/atmospherics/components/binary/pump{ + name = "Mix to Ports" }, -/obj/machinery/meter, /turf/open/floor/plasteel, /area/engine/atmos) "bIW" = ( -/obj/machinery/atmospherics/pipe/manifold/yellow/visible, -/turf/open/floor/plasteel, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 10 + }, +/turf/closed/wall, /area/engine/atmos) "bIX" = ( /obj/machinery/atmospherics/pipe/manifold/yellow/visible{ @@ -44786,8 +44789,12 @@ /turf/open/floor/plasteel, /area/storage/tcom) "bKu" = ( -/obj/effect/turf_decal/bot, -/turf/closed/wall/r_wall, +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/purple/visible, +/turf/open/floor/plating, /area/engine/atmos) "bKw" = ( /obj/structure/cable/yellow{ @@ -44800,7 +44807,7 @@ /obj/machinery/atmospherics/pipe/simple/orange/visible{ dir = 4 }, -/turf/closed/wall/r_wall, +/turf/closed/wall, /area/engine/atmos) "bKz" = ( /obj/machinery/atmospherics/pipe/simple/orange/visible{ @@ -44830,7 +44837,9 @@ /turf/open/floor/plasteel, /area/engine/atmos) "bKE" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/atmospherics/pipe/manifold/yellow/visible{ + dir = 4 + }, /turf/open/floor/plasteel, /area/engine/atmos) "bKF" = ( @@ -45559,9 +45568,9 @@ /obj/machinery/atmospherics/pipe/simple/cyan/visible{ dir = 4 }, -/obj/machinery/atmospherics/components/binary/pump/on{ +/obj/machinery/atmospherics/components/binary/pump{ dir = 1; - name = "Mix to External" + name = "Port to External" }, /turf/open/floor/plasteel, /area/engine/atmos) @@ -45569,7 +45578,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 10 }, -/turf/closed/wall/r_wall, +/turf/closed/wall, /area/engine/atmos) "bMh" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ @@ -46436,7 +46445,12 @@ /obj/structure/cable/yellow{ icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/visible, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ + dir = 9 + }, +/obj/machinery/atmospherics/pipe/simple/dark/visible{ + dir = 6 + }, /turf/open/floor/plasteel, /area/engine/atmos) "bNR" = ( @@ -47128,6 +47142,7 @@ /obj/structure/cable/yellow{ icon_state = "1-2" }, +/obj/machinery/atmospherics/pipe/simple/dark/visible, /turf/open/floor/plasteel, /area/engine/atmos) "bPr" = ( @@ -50108,7 +50123,9 @@ }, /area/engine/atmos) "bVI" = ( -/obj/machinery/atmospherics/pipe/manifold/scrubbers/visible, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ + dir = 9 + }, /turf/open/floor/plasteel/dark/corner{ dir = 1 }, @@ -50860,6 +50877,9 @@ /obj/structure/cable/yellow{ icon_state = "1-8" }, +/obj/machinery/atmospherics/pipe/simple/dark/visible{ + dir = 9 + }, /turf/open/floor/plasteel, /area/engine/atmos) "bXq" = ( @@ -53689,9 +53709,7 @@ /obj/machinery/atmospherics/pipe/simple/green/visible{ dir = 4 }, -/obj/machinery/atmospherics/components/binary/pump{ - name = "Mix to Fuel Pipe" - }, +/obj/machinery/atmospherics/pipe/simple/dark/visible, /turf/open/floor/plasteel/dark, /area/engine/atmos) "ccP" = ( @@ -78788,11 +78806,10 @@ name = "Inner Pipe Access"; req_access_txt = "24" }, -/obj/machinery/atmospherics/components/binary/pump{ - dir = 4; - name = "Mix to Engine" - }, /obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/atmospherics/pipe/simple/orange/visible{ + dir = 4 + }, /turf/open/floor/plasteel/dark, /area/engine/atmos) "dhj" = ( @@ -81294,6 +81311,15 @@ }, /turf/open/floor/plating, /area/quartermaster/storage) +"dLd" = ( +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/visible{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "dLK" = ( /obj/structure/cable/yellow{ icon_state = "4-8" @@ -81536,6 +81562,12 @@ /obj/item/integrated_circuit_printer, /turf/open/floor/plasteel/white, /area/science/circuit) +"gRX" = ( +/obj/machinery/atmospherics/pipe/simple/orange/visible{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "gXY" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 9 @@ -81605,6 +81637,13 @@ }, /turf/open/floor/plasteel/white, /area/science/lab) +"izM" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Pure to Ports" + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "iCn" = ( /obj/machinery/vr_sleeper, /turf/open/floor/plasteel, @@ -81613,6 +81652,13 @@ /obj/structure/table, /turf/open/floor/plating, /area/maintenance/port/aft) +"iRL" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 0; + name = "Mix to Engine" + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "iSt" = ( /obj/machinery/door/airlock/grunge, /obj/structure/cable/yellow{ @@ -81677,6 +81723,15 @@ }, /turf/open/floor/plating, /area/maintenance/solars/port/aft) +"jAj" = ( +/obj/structure/cable/yellow{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/dark/visible{ + dir = 6 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "jBe" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -82064,6 +82119,12 @@ }, /turf/open/floor/plating, /area/maintenance/starboard) +"nBn" = ( +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "nIb" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/mining{ @@ -82143,6 +82204,13 @@ /obj/structure/table/reinforced, /turf/open/floor/plasteel/white, /area/science/circuit) +"oms" = ( +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 + }, +/obj/machinery/meter, +/turf/open/floor/plasteel, +/area/engine/atmos) "omz" = ( /obj/machinery/status_display/supply, /turf/closed/wall, @@ -82218,6 +82286,13 @@ /obj/structure/grille, /turf/open/floor/plating, /area/maintenance/port/aft) +"pbF" = ( +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "pmc" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 5 @@ -82403,6 +82478,12 @@ }, /turf/open/floor/plasteel, /area/security/prison) +"rpR" = ( +/obj/machinery/atmospherics/pipe/simple/orange/visible{ + dir = 5 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "rzX" = ( /obj/structure/chair/office/light{ dir = 1; @@ -82416,6 +82497,12 @@ }, /turf/open/floor/plasteel/white, /area/science/lab) +"rAc" = ( +/obj/machinery/atmospherics/pipe/simple/dark/visible{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "rLV" = ( /obj/structure/table, /obj/item/folder, @@ -82483,6 +82570,13 @@ /obj/structure/grille, /turf/open/floor/plating/airless, /area/space/nearstation) +"siI" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Port to Filter" + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "soe" = ( /obj/effect/turf_decal/tile/green, /obj/effect/turf_decal/tile/green{ @@ -82501,6 +82595,12 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel, /area/crew_quarters/fitness/recreation) +"stP" = ( +/obj/machinery/atmospherics/components/binary/pump{ + name = "Port to Fuel Pipe" + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "syk" = ( /obj/machinery/door/airlock{ id_tag = "AuxShower"; @@ -82649,6 +82749,10 @@ /obj/machinery/cryopod, /turf/open/floor/plasteel/dark, /area/crew_quarters/cryopod) +"uku" = ( +/obj/machinery/atmospherics/pipe/manifold4w/general/visible, +/turf/open/floor/plasteel, +/area/engine/atmos) "upN" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -82867,6 +82971,12 @@ /obj/machinery/vending/cola/random, /turf/open/floor/plasteel, /area/hallway/primary/port) +"xgC" = ( +/obj/machinery/atmospherics/pipe/simple/dark/visible{ + dir = 9 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "xkG" = ( /obj/item/integrated_electronics/wirer, /obj/structure/table/reinforced, @@ -122977,7 +123087,7 @@ bpg brA bep bvd -bxd +bxc byR bAB bCh @@ -123234,7 +123344,7 @@ bpg brB bep bve -bxd +bxc byS bAC dBJ @@ -123491,7 +123601,7 @@ bpg brC bep bCK -bxd +bxc byT bAD bCj @@ -124005,7 +124115,7 @@ bpi brE btz bvh -bxd +bxc byV bAF bCk @@ -124013,7 +124123,7 @@ dBJ bFH bHq bCk -bKu +bxd bxc bxc bxc @@ -124522,10 +124632,10 @@ bep bxc bKy bAG -bxg +bFR dBM bza -bxl +bIW bIO bMg bMb @@ -124536,8 +124646,8 @@ bSg bDS bDS bVI -dBJ -bCi +jAj +bYw bYw cbe ccO @@ -124784,7 +124894,7 @@ bDT bFM bHu bIP -bKw +dLd bKw bNQ bPq @@ -125041,9 +125151,9 @@ bDU bFN bAO bIS +siI bCi -bCi -bCi +rAc bCi bCi bCi @@ -125298,9 +125408,9 @@ bDV bFO bHv bIR -bCi -bCi -bCi +nBn +stP +xgC bCi bCi bCi @@ -125555,8 +125665,8 @@ bMe bFP bMe bMf -bCi -bCi +uku +pbF bCi bCi bCi @@ -125812,8 +125922,8 @@ bDW bFQ bHw bIT -bCi -bCi +oms +pbF bCi bCi bCi @@ -126066,11 +126176,11 @@ bzc bAL ddF bDX -bFR +bCi bHx bIU -bCi -bCi +uku +pbF bCi bCi bKD @@ -126324,11 +126434,11 @@ bAM bCt bDY bFS -bHy +bKu bIV -bCi -bCi -bCi +uku +iRL +rpR bCi bCi bCi @@ -126582,10 +126692,10 @@ bCu bDZ bFT bHz -bIW +bNR +izM bKD -bCi -bCi +gRX bCi bCi bCi @@ -126842,7 +126952,7 @@ bHy bIX bKE bNR -bCi +gRX bCi bCi bCi diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 5c054c0bb4..d015547ac4 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -16752,7 +16752,7 @@ }, /area/centcom/holding) "Nf" = ( -/obj/machinery/autoylathe, +/obj/machinery/autolathe/toy, /turf/open/indestructible/hotelwood, /area/centcom/holding) "Ni" = ( diff --git a/byond-extools.dll b/byond-extools.dll new file mode 100644 index 0000000000..a83ea1e543 Binary files /dev/null and b/byond-extools.dll differ diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 10b815e547..fc6147c430 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -1,13 +1,28 @@ -#define TICK_LIMIT_RUNNING 80 +/// Percentage of tick to leave for master controller to run +#define MAPTICK_MC_MIN_RESERVE 70 +/// internal_tick_usage is updated every tick by extools +#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100) +/// Tick limit while running normally +#define TICK_BYOND_RESERVE 2 +#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE)) +/// Tick limit used to resume things in stoplag #define TICK_LIMIT_TO_RUN 70 +/// Tick limit for MC while running #define TICK_LIMIT_MC 70 -#define TICK_LIMIT_MC_INIT_DEFAULT 98 +/// Tick limit while initializing +#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE) -#define TICK_USAGE world.tick_usage //for general usage -#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked +/// for general usage of tick_usage +#define TICK_USAGE world.tick_usage +/// to be used where the result isn't checked +#define TICK_USAGE_REAL world.tick_usage +/// Returns true if tick_usage is above the limit #define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit ) +/// runs stoplag if tick_usage is above the limit #define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) +/// Returns true if tick usage is above 95, for high priority usage #define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 ) +/// runs stoplag if tick_usage is above 95, for high priority usage #define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm index a547ae6b68..a6c31bf965 100644 --- a/code/__DEFINES/layers_planes.dm +++ b/code/__DEFINES/layers_planes.dm @@ -91,6 +91,8 @@ #define MASSIVE_OBJ_LAYER 11 #define POINT_LAYER 12 +#define CHAT_LAYER 12.1 + #define EMISSIVE_BLOCKER_PLANE 12 #define EMISSIVE_BLOCKER_LAYER 12 #define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE" diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index eee0676dec..5a26feb624 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -22,14 +22,14 @@ //used in design to specify which machine can build it #define IMPRINTER (1<<0) //For circuits. Uses glass/chemicals. -#define PROTOLATHE (1<<1) //New stuff. Uses glass/metal/chemicals -#define AUTOLATHE (1<<2) //Uses glass/metal only. -#define CRAFTLATHE (1<<3) //Uses fuck if I know. For use eventually. -#define MECHFAB (1<<4) //Remember, objects utilising this flag should have construction_time and construction_cost vars. -#define BIOGENERATOR (1<<5) //Uses biomass -#define LIMBGROWER (1<<6) //Uses synthetic flesh -#define SMELTER (1<<7) //uses various minerals -#define AUTOYLATHE (1<<8) // CITADEL ADD +#define PROTOLATHE (1<<1) //New stuff. Uses materials/chemicals +#define AUTOLATHE (1<<2) //Uses materials only. +#define TOYLATHE (1<<3) //Glass/metal/plastic. Meant for toys. +#define NO_PUBLIC_LATHE (1<<4) //prevents the design from being auto-unlocked by public auto(y)lathes. +#define MECHFAB (1<<5) //Remember, objects utilising this flag should have construction_time and construction_cost vars. +#define BIOGENERATOR (1<<6) //Uses biomass +#define LIMBGROWER (1<<7) //Uses synthetic flesh +#define SMELTER (1<<8) //uses various minerals #define NANITE_COMPILER (1<<9) //Prints nanite disks #define AUTOBOTTLER (1<<10) //Uses booze, for printing //Note: More then one of these can be added to a design but imprinter and lathe designs are incompatable. diff --git a/code/__DEFINES/materials.dm b/code/__DEFINES/materials.dm index 89553d9c6c..e2ae22345f 100644 --- a/code/__DEFINES/materials.dm +++ b/code/__DEFINES/materials.dm @@ -7,5 +7,5 @@ /// Flag for atoms, this flag ensures it isn't re-colored by materials. Useful for snowflake icons such as default toolboxes. #define MATERIAL_COLOR (1<<0) #define MATERIAL_ADD_PREFIX (1<<1) -#define MATERIAL_NO_EFFECTS (1<<2) +#define MATERIAL_EFFECTS (1<<2) #define MATERIAL_AFFECT_STATISTICS (1<<3) \ No newline at end of file diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index d7d3cf5da3..42095aba41 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -515,3 +515,17 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define NIGHTSHIFT_AREA_NONE 4 //default/highest. #define UNTIL(X) while(!(X)) stoplag() + + +//Scavenging element defines for special loot "events". +#define SCAVENGING_FOUND_NOTHING "found_nothing" +#define SCAVENGING_SPAWN_MOUSE "spawn_mouse" +#define SCAVENGING_SPAWN_MICE "spawn_mice" +#define SCAVENGING_SPAWN_TOM "spawn_tom_the_mouse" + +//Scavenging element defines for ckey/mind restrictions. +#define NO_LOOT_RESTRICTION 0 +#define LOOT_RESTRICTION_MIND 1 +#define LOOT_RESTRICTION_CKEY 2 +#define LOOT_RESTRICTION_MIND_PILE 3 //limited to the current pile. +#define LOOT_RESTRICTION_CKEY_PILE 4 //Idem diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index d78de86d25..8284f66f50 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -52,3 +52,4 @@ #define ORGAN_EXTERNAL (1<<3) //Was this organ implanted/inserted/etc, if true will not be removed during species change. #define ORGAN_VITAL (1<<4) //Currently only the brain #define ORGAN_NO_SPOIL (1<<5) //Do not spoil under any circumstances +#define ORGAN_NO_DISMEMBERMENT (1<<6) //Immune to disembowelment. diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index cadc1113c5..3abef3a61c 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -132,8 +132,15 @@ #define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) - - +// SSair run section +#define SSAIR_PIPENETS 1 +#define SSAIR_ATMOSMACHINERY 2 +#define SSAIR_REACTQUEUE 3 +#define SSAIR_EXCITEDGROUPS 4 +#define SSAIR_HIGHPRESSURE 5 +#define SSAIR_HOTSPOTS 6 +#define SSAIR_SUPERCONDUCTIVITY 7 +#define SSAIR_REBUILD_PIPENETS 8 #define COMPILE_OVERLAYS(A)\ if (TRUE) {\ diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 38e25b04c5..bcfaa1bed2 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -88,6 +88,7 @@ init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL() + INVOKE_ASYNC(GLOBAL_PROC, /proc/setupGenetics) //creates every subtype of prototype (excluding prototype) and adds it to list L. //if no list/L is provided, one is created. @@ -113,3 +114,25 @@ UNTIL(C.flags_1 & INITIALIZED_1) //we want to make sure the value is calculated and not null. GLOB.coin_values[path] = C.value qdel(C) + +/proc/setupGenetics() + var/list/mutations = subtypesof(/datum/mutation/human) + shuffle_inplace(mutations) + for(var/A in subtypesof(/datum/generecipe)) + var/datum/generecipe/GR = A + GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result) + for(var/i in 1 to LAZYLEN(mutations)) + var/path = mutations[i] //byond gets pissy when we do it in one line + var/datum/mutation/human/B = new path () + B.alias = "Mutation #[i]" + GLOB.all_mutations[B.type] = B + GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks) + if(B.locked) + continue + if(B.quality == POSITIVE) + GLOB.good_mutations |= B + else if(B.quality == NEGATIVE) + GLOB.bad_mutations |= B + else if(B.quality == MINOR_NEGATIVE) + GLOB.not_good_mutations |= B + CHECK_TICK \ No newline at end of file diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index d93a89a179..e69052ad8d 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -317,7 +317,7 @@ GLOBAL_LIST_EMPTY(species_list) else return "unknown" -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS) if(!user || !target) return 0 var/user_loc = user.loc @@ -336,10 +336,10 @@ GLOBAL_LIST_EMPTY(species_list) var/endtime = world.time+time var/starttime = world.time . = 1 - while (world.time < endtime) + while (world.time + resume_time < endtime) stoplag(1) if (progress) - progbar.update(world.time - starttime) + progbar.update(world.time - starttime + resume_time) if(QDELETED(user) || QDELETED(target)) . = 0 break @@ -371,7 +371,7 @@ GLOBAL_LIST_EMPTY(species_list) checked_health["health"] = health return ..() -/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE)) +/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS) if(!user) return 0 var/atom/Tloc = null @@ -400,10 +400,10 @@ GLOBAL_LIST_EMPTY(species_list) var/starttime = world.time . = 1 var/mob/living/L = isliving(user) && user //evals to last thing eval'd - while (world.time < endtime) + while (world.time + resume_time < endtime) stoplag(1) if (progress) - progbar.update(world.time - starttime) + progbar.update(world.time - starttime + resume_time) if(drifting && !user.inertia_dir) drifting = 0 diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 7cbfbd040a..aa388d7413 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -563,7 +563,7 @@ if(objective.completable) var/completion = objective.check_completion() if(completion >= 1) - objective_parts += "Objective #[count]: [objective.explanation_text] Success!" + objective_parts += "Objective #[count]: [objective.explanation_text] Success!" else if(completion <= 0) objective_parts += "Objective #[count]: [objective.explanation_text] Fail." else diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index af483bf888..b039e6d114 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -33,3 +33,5 @@ GLOBAL_VAR(bible_icon_state) GLOBAL_VAR(bible_item_state) GLOBAL_VAR(holy_weapon_type) GLOBAL_VAR(holy_armor_type) + +GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 8772fac5af..db828539af 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -36,6 +36,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/sleep_delta = 1 + ///Only run ticker subsystems for the next n ticks. + var/skip_ticks = 0 + var/make_runtime = 0 var/initializations_finished_with_no_players_logged_in //I wonder what this could be? @@ -335,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new new/datum/controller/failsafe() // (re)Start the failsafe. //now do the actual stuff - if (!queue_head || !(iteration % 3)) + if (!skip_ticks) var/checking_runlevel = current_runlevel if(cached_runlevel != checking_runlevel) //resechedule subsystems @@ -381,6 +384,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration++ last_run = world.time + if (skip_ticks) + skip_ticks-- src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) current_ticklimit = TICK_LIMIT_RUNNING if (processing * sleep_delta <= world.tick_lag) @@ -444,10 +449,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new while (queue_node) if (ran && TICK_USAGE > TICK_LIMIT_RUNNING) break - queue_node_flags = queue_node.flags queue_node_priority = queue_node.queued_priority + if (!(queue_node_flags & SS_TICKER) && skip_ticks) + queue_node = queue_node.queue_next + continue //super special case, subsystems where we can't make them pause mid way through //if we can't run them this tick (without going over a tick) //we bump up their priority and attempt to run them next tick @@ -455,14 +462,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // in those cases, so we just let them run) if (queue_node_flags & SS_NO_TICK_CHECK) if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker) - queue_node.queued_priority += queue_priority_count * 0.1 - queue_priority_count -= queue_node_priority - queue_priority_count += queue_node.queued_priority - current_tick_budget -= queue_node_priority - queue_node = queue_node.queue_next + if (!(queue_node_flags & SS_BACKGROUND)) + queue_node.queued_priority += queue_priority_count * 0.1 + queue_priority_count -= queue_node_priority + queue_priority_count += queue_node.queued_priority + current_tick_budget -= queue_node_priority + queue_node = queue_node.queue_next continue - if ((queue_node_flags & SS_BACKGROUND) && !bg_calc) + if (!bg_calc && (queue_node_flags & SS_BACKGROUND)) current_tick_budget = queue_priority_count_bg bg_calc = TRUE @@ -515,7 +523,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.paused_ticks = 0 queue_node.paused_tick_usage = 0 - if (queue_node_flags & SS_BACKGROUND) //update our running total + if (bg_calc) //update our running total queue_priority_count_bg -= queue_node_priority else queue_priority_count -= queue_node_priority @@ -583,14 +591,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new log_world("MC: SoftReset: Finished.") . = 1 +/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick. +/datum/controller/master/proc/laggy_byond_map_update_incoming() + if (!skip_ticks) + skip_ticks = 1 /datum/controller/master/stat_entry() if(!statclick) statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))") - stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])")) + stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)") + stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (TickLimit: [round(Master.current_ticklimit, 0.1)])")) + /datum/controller/master/StartLoadingMap() //disallow more than one map to load at once, multithreading it will just cause race conditions diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 13facc7666..d8c7724333 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -1,11 +1,3 @@ -#define SSAIR_PIPENETS 1 -#define SSAIR_ATMOSMACHINERY 2 -#define SSAIR_REACTQUEUE 3 -#define SSAIR_EXCITEDGROUPS 4 -#define SSAIR_HIGHPRESSURE 5 -#define SSAIR_HOTSPOTS 6 -#define SSAIR_SUPERCONDUCTIVITY 7 - SUBSYSTEM_DEF(air) name = "Atmospherics" init_order = INIT_ORDER_AIR @@ -20,6 +12,7 @@ SUBSYSTEM_DEF(air) var/cost_hotspots = 0 var/cost_superconductivity = 0 var/cost_pipenets = 0 + var/cost_rebuilds = 0 var/cost_atmos_machinery = 0 var/list/excited_groups = list() @@ -27,6 +20,7 @@ SUBSYSTEM_DEF(air) var/list/turf_react_queue = list() var/list/hotspots = list() var/list/networks = list() + var/list/pipenets_needing_rebuilt = list() var/list/obj/machinery/atmos_machinery = list() var/list/pipe_init_dirs_cache = list() @@ -39,7 +33,7 @@ SUBSYSTEM_DEF(air) var/list/currentrun = list() - var/currentpart = SSAIR_PIPENETS + var/currentpart = SSAIR_REBUILD_PIPENETS var/map_loading = TRUE var/list/queued_for_activation @@ -52,6 +46,7 @@ SUBSYSTEM_DEF(air) msg += "HS:[round(cost_hotspots,1)]|" msg += "SC:[round(cost_superconductivity,1)]|" msg += "PN:[round(cost_pipenets,1)]|" + msg += "RB:[round(cost_rebuilds,1)]|" msg += "AM:[round(cost_atmos_machinery,1)]" msg += "} " msg += "AT:[active_turfs.len]|" @@ -77,6 +72,18 @@ SUBSYSTEM_DEF(air) /datum/controller/subsystem/air/fire(resumed = 0) var/timer = TICK_USAGE_REAL + if(currentpart == SSAIR_REBUILD_PIPENETS) + var/list/pipenet_rebuilds = pipenets_needing_rebuilt + for(var/thing in pipenet_rebuilds) + var/obj/machinery/atmospherics/AT = thing + AT.build_network() + cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + pipenets_needing_rebuilt.Cut() + if(state != SS_RUNNING) + return + resumed = FALSE + currentpart = SSAIR_PIPENETS + if(currentpart == SSAIR_PIPENETS || !resumed) process_pipenets(resumed) cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) @@ -137,9 +144,7 @@ SUBSYSTEM_DEF(air) if(state != SS_RUNNING) return resumed = 0 - currentpart = SSAIR_PIPENETS - - + currentpart = SSAIR_REBUILD_PIPENETS /datum/controller/subsystem/air/proc/process_pipenets(resumed = 0) if (!resumed) @@ -156,6 +161,9 @@ SUBSYSTEM_DEF(air) if(MC_TICK_CHECK) return +/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine) + if(istype(atmos_machine, /obj/machinery/atmospherics)) + pipenets_needing_rebuilt += atmos_machine /datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0) var/seconds = wait * 0.1 diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 35feab6648..c1644df9c9 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -16,7 +16,6 @@ SUBSYSTEM_DEF(atoms) /datum/controller/subsystem/atoms/Initialize(timeofday) GLOB.fire_overlay.appearance_flags = RESET_COLOR - setupGenetics() //to set the mutations' sequence. initialized = INITIALIZATION_INNEW_MAPLOAD InitializeAtoms() return ..() @@ -107,28 +106,6 @@ SUBSYSTEM_DEF(atoms) old_initialized = SSatoms.old_initialized BadInitializeCalls = SSatoms.BadInitializeCalls -/datum/controller/subsystem/atoms/proc/setupGenetics() - var/list/mutations = subtypesof(/datum/mutation/human) - shuffle_inplace(mutations) - for(var/A in subtypesof(/datum/generecipe)) - var/datum/generecipe/GR = A - GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result) - for(var/i in 1 to LAZYLEN(mutations)) - var/path = mutations[i] //byond gets pissy when we do it in one line - var/datum/mutation/human/B = new path () - B.alias = "Mutation #[i]" - GLOB.all_mutations[B.type] = B - GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks) - if(B.locked) - continue - if(B.quality == POSITIVE) - GLOB.good_mutations |= B - else if(B.quality == NEGATIVE) - GLOB.bad_mutations |= B - else if(B.quality == MINOR_NEGATIVE) - GLOB.not_good_mutations |= B - CHECK_TICK - /datum/controller/subsystem/atoms/proc/InitLog() . = "" for(var/path in BadInitializeCalls) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 89155cefb3..526e4b98e3 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -42,6 +42,13 @@ SUBSYSTEM_DEF(mapping) var/stat_map_name = "Loading..." + /// Lookup list for random generated IDs. + var/list/random_generated_ids_by_original = list() + /// next id for separating obfuscated ids. + var/obfuscation_next_id = 1 + /// "secret" key + var/obfuscation_secret + //dlete dis once #39770 is resolved /datum/controller/subsystem/mapping/proc/HACK_LoadMapConfig() if(!config) @@ -52,6 +59,10 @@ SUBSYSTEM_DEF(mapping) #endif stat_map_name = config.map_name +/datum/controller/subsystem/mapping/PreInit() + if(!obfuscation_secret) + obfuscation_secret = md5(GUID()) //HAH! Guess this! + /datum/controller/subsystem/mapping/Initialize(timeofday) HACK_LoadMapConfig() if(initialized) @@ -568,3 +579,15 @@ GLOBAL_LIST_EMPTY(the_station_areas) LM.load() if(GLOB.stationroom_landmarks.len) seedStation() //I'm sure we can trust everyone not to insert a 1x1 rooms which loads a landmark which loads a landmark which loads a la... + +/** + * Generates an obfuscated but constant id for an original id for cases where you don't want players codediving for an id. + * WARNING: MAKE SURE PLAYERS ARE NOT ABLE TO ACCESS THIS. To save performance, it's just secret + an incrementing number. Very guessable if you know what the secret is. + */ +/datum/controller/subsystem/mapping/proc/get_obfuscated_id(original, id_type = "GENERAL") + if(!original) + return //no. + var/key = "[original]%[id_type]" + if(random_generated_ids_by_original[key]) + return random_generated_ids_by_original[key] + . = random_generated_ids_by_original[key] = "[obfuscation_secret]%[obfuscation_next_id++]" diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 428f355c31..3f71d192e3 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -150,6 +150,8 @@ friend_talk(message) /mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source) + if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker))) + create_chat_message(speaker, message_language, raw_message, spans, message_mode) to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)) /mob/camera/imaginary_friend/proc/friend_talk(message) diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm new file mode 100644 index 0000000000..cbcb34ac5e --- /dev/null +++ b/code/datums/chatmessage.dm @@ -0,0 +1,236 @@ +#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS +#define CHAT_MESSAGE_LIFESPAN 5 SECONDS +#define CHAT_MESSAGE_EOL_FADE 0.7 SECONDS +#define CHAT_MESSAGE_EXP_DECAY 0.7 // Messages decay at pow(factor, idx in stack) +#define CHAT_MESSAGE_HEIGHT_DECAY 0.9 // Increase message decay based on the height of the message +#define CHAT_MESSAGE_APPROX_LHEIGHT 11 // Approximate height in pixels of an 'average' line, used for height decay +#define CHAT_MESSAGE_WIDTH 96 // pixels +#define CHAT_MESSAGE_MAX_LENGTH 110 // characters +#define WXH_TO_HEIGHT(x) text2num(copytext((x), findtextEx((x), "x") + 1)) // thanks lummox + +/** + * # Chat Message Overlay + * + * Datum for generating a message overlay on the map + */ +/datum/chatmessage + /// The visual element of the chat messsage + var/image/message + /// The location in which the message is appearing + var/atom/message_loc + /// The client who heard this message + var/client/owned_by + /// Contains the scheduled destruction time + var/scheduled_destruction + /// Contains the approximate amount of lines for height decay + var/approx_lines + +/** + * Constructs a chat message overlay + * + * Arguments: + * * text - The text content of the overlay + * * target - The target atom to display the overlay at + * * owner - The mob that owns this overlay, only this mob will be able to view it + * * extra_classes - Extra classes to apply to the span that holds the text + * * lifespan - The lifespan of the message in deciseconds + */ +/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = null, lifespan = CHAT_MESSAGE_LIFESPAN) + . = ..() + if (!istype(target)) + CRASH("Invalid target given for chatmessage") + if(QDELETED(owner) || !istype(owner) || !owner.client) + stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner") + qdel(src) + return + INVOKE_ASYNC(src, .proc/generate_image, text, target, owner, extra_classes, lifespan) + +/datum/chatmessage/Destroy() + if (owned_by) + if (owned_by.seen_messages) + LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src) + owned_by.images.Remove(message) + owned_by = null + message_loc = null + message = null + return ..() + +/** + * Generates a chat message image representation + * + * Arguments: + * * text - The text content of the overlay + * * target - The target atom to display the overlay at + * * owner - The mob that owns this overlay, only this mob will be able to view it + * * extra_classes - Extra classes to apply to the span that holds the text + * * lifespan - The lifespan of the message in deciseconds + */ +/datum/chatmessage/proc/generate_image(text, atom/target, mob/owner, list/extra_classes, lifespan) + // Register client who owns this message + owned_by = owner.client + RegisterSignal(owned_by, COMSIG_PARENT_QDELETING, .proc/qdel, src) + + // Clip message + var/maxlen = owned_by.prefs.max_chat_length + if (length_char(text) > maxlen) + text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment + + // Calculate target color if not already present + if (!target.chat_color || target.chat_color_name != target.name) + target.chat_color = colorize_string(target.name) + target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85) + target.chat_color_name = target.name + + // Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag + var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g") + text = replacetext(text, url_scheme, "") + + // Reject whitespace + var/static/regex/whitespace = new(@"^\s*$") + if (whitespace.Find(text)) + qdel(src) + return + + // Non mobs speakers can be small + if (!ismob(target)) + extra_classes |= "small" + + // Append radio icon if from a virtual speaker + if (extra_classes.Find("virtual-speaker")) + var/image/r_icon = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio") + text = "\icon[r_icon] " + text + + // We dim italicized text to make it more distinguishable from regular text + var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color + + // Approximate text height + // Note we have to replace HTML encoded metacharacters otherwise MeasureText will return a zero height + // BYOND Bug #2563917 + // Construct text + var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") + var/complete_text = "[text]" + var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(replacetext(complete_text, html_metachars, "m"), null, CHAT_MESSAGE_WIDTH)) + approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT) + + // Translate any existing messages upwards, apply exponential decay factors to timers + message_loc = target + if (owned_by.seen_messages) + var/idx = 1 + var/combined_height = approx_lines + for(var/msg in owned_by.seen_messages[message_loc]) + var/datum/chatmessage/m = msg + animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME) + combined_height += m.approx_lines + var/sched_remaining = m.scheduled_destruction - world.time + if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME) + var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height) + m.scheduled_destruction = world.time + remaining_time + addtimer(CALLBACK(m, .proc/end_of_life), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE) + + // Build message image + message = image(loc = message_loc, layer = CHAT_LAYER) + message.plane = GAME_PLANE + message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART + message.alpha = 0 + message.pixel_y = owner.bound_height * 0.95 + message.maptext_width = CHAT_MESSAGE_WIDTH + message.maptext_height = mheight + message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5 + message.maptext = complete_text + + // View the message + LAZYADDASSOC(owned_by.seen_messages, message_loc, src) + owned_by.images |= message + animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME) + + // Prepare for destruction + scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE) + addtimer(CALLBACK(src, .proc/end_of_life), lifespan - CHAT_MESSAGE_EOL_FADE, TIMER_UNIQUE|TIMER_OVERRIDE) + +/** + * Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion + */ +/datum/chatmessage/proc/end_of_life(fadetime = CHAT_MESSAGE_EOL_FADE) + animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL) + QDEL_IN(src, fadetime) + +/** + * Creates a message overlay at a defined location for a given speaker + * + * Arguments: + * * speaker - The atom who is saying this message + * * message_language - The language that the message is said in + * * raw_message - The text content of the message + * * spans - Additional classes to be added to the message + * * message_mode - Bitflags relating to the mode of the message + */ +/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, message_mode) + // Ensure the list we are using, if present, is a copy so we don't modify the list provided to us + spans = spans?.Copy() + + // Check for virtual speakers (aka hearing a message through a radio) + var/atom/movable/originalSpeaker = speaker + if (istype(speaker, /atom/movable/virtualspeaker)) + var/atom/movable/virtualspeaker/v = speaker + speaker = v.source + spans |= "virtual-speaker" + + // Ignore virtual speaker (most often radio messages) from ourself + if (originalSpeaker != src && speaker == src) + return + + // Display visual above source + new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans) + + +// Tweak these defines to change the available color ranges +#define CM_COLOR_SAT_MIN 0.6 +#define CM_COLOR_SAT_MAX 0.7 +#define CM_COLOR_LUM_MIN 0.65 +#define CM_COLOR_LUM_MAX 0.75 + +/** + * Gets a color for a name, will return the same color for a given string consistently within a round.atom + * + * Note that this proc aims to produce pastel-ish colors using the HSL colorspace. These seem to be favorable for displaying on the map. + * + * Arguments: + * * name - The name to generate a color for + * * sat_shift - A value between 0 and 1 that will be multiplied against the saturation + * * lum_shift - A value between 0 and 1 that will be multiplied against the luminescence + */ +/datum/chatmessage/proc/colorize_string(name, sat_shift = 1, lum_shift = 1) + // seed to help randomness + var/static/rseed = rand(1,26) + + // get hsl using the selected 6 characters of the md5 hash + var/hash = copytext(md5(name + GLOB.round_id), rseed, rseed + 6) + var/h = hex2num(copytext(hash, 1, 3)) * (360 / 255) + var/s = (hex2num(copytext(hash, 3, 5)) >> 2) * ((CM_COLOR_SAT_MAX - CM_COLOR_SAT_MIN) / 63) + CM_COLOR_SAT_MIN + var/l = (hex2num(copytext(hash, 5, 7)) >> 2) * ((CM_COLOR_LUM_MAX - CM_COLOR_LUM_MIN) / 63) + CM_COLOR_LUM_MIN + + // adjust for shifts + s *= clamp(sat_shift, 0, 1) + l *= clamp(lum_shift, 0, 1) + + // convert to rgb + var/h_int = round(h/60) // mapping each section of H to 60 degree sections + var/c = (1 - abs(2 * l - 1)) * s + var/x = c * (1 - abs((h / 60) % 2 - 1)) + var/m = l - c * 0.5 + x = (x + m) * 255 + c = (c + m) * 255 + m *= 255 + switch(h_int) + if(0) + return "#[num2hex(c, 2)][num2hex(x, 2)][num2hex(m, 2)]" + if(1) + return "#[num2hex(x, 2)][num2hex(c, 2)][num2hex(m, 2)]" + if(2) + return "#[num2hex(m, 2)][num2hex(c, 2)][num2hex(x, 2)]" + if(3) + return "#[num2hex(m, 2)][num2hex(x, 2)][num2hex(c, 2)]" + if(4) + return "#[num2hex(x, 2)][num2hex(m, 2)][num2hex(c, 2)]" + if(5) + return "#[num2hex(c, 2)][num2hex(m, 2)][num2hex(x, 2)]" diff --git a/code/datums/components/archaeology.dm b/code/datums/components/archaeology.dm deleted file mode 100644 index b5740650e9..0000000000 --- a/code/datums/components/archaeology.dm +++ /dev/null @@ -1,95 +0,0 @@ -/datum/component/archaeology - dupe_mode = COMPONENT_DUPE_UNIQUE - var/list/archdrops = list(/obj/item/bikehorn = list(ARCH_PROB = 100, ARCH_MAXDROP = 1)) // honk~ - var/prob2drop - var/dug - var/datum/callback/callback - -/datum/component/archaeology/Initialize(list/_archdrops = list(), datum/callback/_callback) - archdrops = _archdrops - for(var/i in archdrops) - if(isnull(archdrops[i][ARCH_MAXDROP])) - archdrops[i][ARCH_MAXDROP] = 1 - stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null max_drop value in [i].") - if(isnull(archdrops[i][ARCH_PROB])) - archdrops[i][ARCH_PROB] = 100 - stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null probability value in [i].") - callback = _callback - RegisterSignal(parent, COMSIG_PARENT_ATTACKBY,.proc/Dig) - RegisterSignal(parent, COMSIG_ATOM_EX_ACT, .proc/BombDig) - RegisterSignal(parent, COMSIG_ATOM_SING_PULL, .proc/SingDig) - -/datum/component/archaeology/InheritComponent(datum/component/archaeology/A, i_am_original) - var/list/other_archdrops = A.archdrops - var/list/_archdrops = archdrops - for(var/I in other_archdrops) - _archdrops[I] += other_archdrops[I] - -/datum/component/archaeology/proc/Dig(datum/source, obj/item/I, mob/living/user) - if(dug) - to_chat(user, "Looks like someone has dug here already.") - return - - if(!isturf(user.loc)) - return - - if(I.tool_behaviour == TOOL_SHOVEL || I.tool_behaviour == TOOL_MINING) - to_chat(user, "You start digging...") - - if(I.use_tool(parent, user, 40, volume=50)) - to_chat(user, "You dig a hole.") - gets_dug() - dug = TRUE - SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type) - return COMPONENT_NO_AFTERATTACK - -/datum/component/archaeology/proc/gets_dug() - if(dug) - return - else - var/turf/open/OT = get_turf(parent) - for(var/thing in archdrops) - var/maxtodrop = archdrops[thing][ARCH_MAXDROP] - for(var/i in 1 to maxtodrop) - if(prob(archdrops[thing][ARCH_PROB])) // can't win them all! - new thing(OT) - - if(isopenturf(OT)) - if(OT.postdig_icon_change) - if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon) - var/turf/open/floor/plating/asteroid/AOT = parent - AOT.icon_plating = "[AOT.environment_type]_dug" - AOT.icon_state = "[AOT.environment_type]_dug" - else - if(isplatingturf(OT)) - var/turf/open/floor/plating/POT = parent - POT.icon_plating = "[POT.postdig_icon]" - POT.icon_state = "[OT.postdig_icon]" - - if(OT.slowdown) //Things like snow slow you down until you dig them. - OT.slowdown = 0 - dug = TRUE - if(callback) - callback.Invoke() - -/datum/component/archaeology/proc/SingDig(datum/source, S, current_size) - switch(current_size) - if(STAGE_THREE) - if(prob(30)) - gets_dug() - if(STAGE_FOUR) - if(prob(50)) - gets_dug() - else - if(current_size >= STAGE_FIVE && prob(70)) - gets_dug() - -/datum/component/archaeology/proc/BombDig(datum/source, severity, target) - switch(severity) - if(3) - return - if(2) - if(prob(20)) - gets_dug() - if(1) - gets_dug() diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index 9eb3fcaaea..14ab751d2b 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -260,6 +260,15 @@ subcategory = CAT_TOOL category = CAT_MISC +/datum/crafting_recipe/electrochromatic_kit + name = "Electrochromatic Kit" + result = /obj/item/electronics/electrochromatic_kit + reqs = list(/obj/item/stack/sheet/metal = 1, + /obj/item/stack/cable_coil = 1) + time = 5 + subcategory = CAT_TOOL + category = CAT_MISC + //////////// //Vehicles// //////////// diff --git a/code/datums/components/mirage_border.dm b/code/datums/components/mirage_border.dm index de4425e208..56b41533f5 100644 --- a/code/datums/components/mirage_border.dm +++ b/code/datums/components/mirage_border.dm @@ -39,4 +39,5 @@ /obj/effect/abstract/mirage_holder name = "Mirage holder" anchored = TRUE + plane = PLANE_SPACE mouse_opacity = MOUSE_OPACITY_TRANSPARENT diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm index a5c6e2725d..d770e0f30d 100644 --- a/code/datums/elements/mob_holder.dm +++ b/code/datums/elements/mob_holder.dm @@ -164,7 +164,7 @@ release() /obj/item/clothing/head/mob_holder/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) - if(!ishuman(M)) //monkeys holding monkeys holding monkeys... + if(M == held_mob || !ishuman(M)) //monkeys holding monkeys holding monkeys... return FALSE return ..() diff --git a/code/datums/elements/scavenging.dm b/code/datums/elements/scavenging.dm new file mode 100644 index 0000000000..1993005be7 --- /dev/null +++ b/code/datums/elements/scavenging.dm @@ -0,0 +1,215 @@ + /* + * Scavenging element. Its scope shouldn't elude your imagination. + * Basically loot piles that can be searched through for some items. + * In my opinion, these are more engaging than normal maintenance loot spawners. + * The loot doesn't have to be strictly made of items and objects, you could also use it to invoke some "events" + * such as mice, rats, an halloween spook, persistent relics, traps, etcetera, go wild. + */ +/datum/element/scavenging + element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH + id_arg_index = 3 + + var/list/loot_left_per_atom = list() //loot left per attached atom. + var/list/loot_table //pickweight list of available loot. + var/list/unique_loot //limited loot, once the associated value reaches zero, its key is removed from loot_table + var/scavenge_time = 12 SECONDS //how much time it takes + var/can_use_hands = TRUE //bare handed scavenge time multiplier. If set to zero, only tools are usable. + var/list/tool_types //which tool types the player can use instead of scavenging by hand, associated value is their speed. + var/del_atom_on_depletion = FALSE //Will the atom be deleted when there is no loot left? + var/list/search_texts = list("starts searching through", "start searching through", "You hear rummaging...") + var/loot_restriction = NO_LOOT_RESTRICTION + var/maximum_loot_per_player = 1 //only relevant if there is a restriction. + var/list/scavenger_restriction_list //used for restrictions. + + var/mean_loot_weight = 0 + var/list/progress_per_atom = list() //seconds of ditched progress per atom, used to resume the work instead of starting over. + var/static/list/players_busy_scavenging = list() //players already busy scavenging. + +/datum/element/scavenging/Attach(atom/target, amount = 5, list/loot, list/unique, time = 12 SECONDS, hands = TRUE, list/tools, list/texts, \ + del_deplete = FALSE, restriction = NO_LOOT_RESTRICTION, max_per_player = 1) + . = ..() + if(. == ELEMENT_INCOMPATIBLE || !length(loot) || !amount || !istype(target) || isarea(target)) + return ELEMENT_INCOMPATIBLE + loot_left_per_atom[target] = amount + if(!loot_table) + loot_table = loot + for(var/A in loot_table) //tally the list weights + mean_loot_weight += loot_table[A] + mean_loot_weight /= length(loot_table) + if(!unique_loot) + unique_loot = unique || list() + scavenge_time = time + can_use_hands = hands + tool_types = tools + if(texts) + search_texts = texts + del_atom_on_depletion = del_deplete + loot_restriction = restriction + maximum_loot_per_player = max_per_player + if(can_use_hands) + RegisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), .proc/scavenge_barehanded) + if(tool_types) + RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/scavenge_tool) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine) + +/datum/element/scavenging/Detach(atom/target) + . = ..() + loot_left_per_atom -= target + progress_per_atom -= target + if(maximum_loot_per_player == LOOT_RESTRICTION_MIND_PILE || maximum_loot_per_player == LOOT_RESTRICTION_CKEY_PILE) + maximum_loot_per_player -= target + UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE)) + +/datum/element/scavenging/proc/on_examine(atom/source, mob/user, list/examine_list) + var/methods = tool_types.Copy() + if(can_use_hands) + methods += list("bare handed") + if(!length(methods)) + return + var/text = english_list(methods, "", " or ") + examine_list += "Looks like [source.p_they()] can be scavenged [length(tool_types) ? "with" : ""][length(methods == 1) ? "" : "either "][length(tool_types) ? "a " : ""][text]" + +/datum/element/scavenging/proc/scavenge_barehanded(atom/source, mob/user) + scavenge(source, user, 1) + return COMPONENT_NO_ATTACK_HAND + +/datum/element/scavenging/proc/scavenge_tool(atom/source, obj/item/I, mob/user, params) + if(user.a_intent == INTENT_HARM || !I.tool_behaviour) //Robust trash disposal techniques! + return + var/speed_multi = tool_types[I.tool_behaviour] + if(!speed_multi) + return + scavenge(source, user, speed_multi) + return COMPONENT_NO_AFTERATTACK + +/// This proc has to be asynced (cough cough, do_after) in order to return the comsig values in time to stop the attack chain. +/datum/element/scavenging/proc/scavenge(atom/source, mob/user, speed_multi = 1) + set waitfor = FALSE + + if(players_busy_scavenging[user]) + return + players_busy_scavenging[user] = TRUE + var/progress_done = progress_per_atom[source] + var/len_messages = length(search_texts) + var/msg_first_person + if(len_messages >= 2) + msg_first_person = "You [progress_done ? ", resume a ditched task and " : ""][search_texts[2]] [source]." + var/msg_blind + if(len_messages >= 3) + msg_blind = "[search_texts[3]]" + user.visible_message("[user] [search_texts[1]] [source].", msg_first_person, msg_blind) + if(do_after(user, scavenge_time * speed_multi, TRUE, source, TRUE, CALLBACK(src, .proc/set_progress, source, world.time), resume_time = progress_done * speed_multi)) + spawn_loot(source, user) + players_busy_scavenging -= user + +/datum/element/scavenging/proc/set_progress(atom/source, start_time) + progress_per_atom[source] = world.time - start_time + return TRUE + +/datum/element/scavenging/proc/spawn_loot(atom/source, mob/user) + progress_per_atom -= source + + var/loot = pickweight(loot_table) + var/special = TRUE + var/free = FALSE + if(!loot_left_per_atom[source]) + to_chat(user, "Looks likes there is nothing worth of interest left in [source], what a shame...") + return + + var/num_times = 0 + switch(loot_restriction) + if(LOOT_RESTRICTION_MIND) + num_times = LAZYACCESS(scavenger_restriction_list, user.mind) + if(LOOT_RESTRICTION_CKEY) + num_times = LAZYACCESS(scavenger_restriction_list, user.ckey) + if(LOOT_RESTRICTION_MIND_PILE) + var/list/L = LAZYACCESS(scavenger_restriction_list, source) + if(L) + num_times = LAZYACCESS(L, user.mind) + if(LOOT_RESTRICTION_CKEY_PILE) + var/list/L = LAZYACCESS(scavenger_restriction_list, source) + if(L) + num_times = LAZYACCESS(L, user.ckey) + if(num_times >= maximum_loot_per_player) + to_chat(user, "You can't find anything else vaguely useful in [source]. Another set of eyes might, however.") + return + + switch(loot) // TODO: datumize these out. + if(SCAVENGING_FOUND_NOTHING) + to_chat(user, "You found nothing, better luck next time.") + free = TRUE //doesn't consume the loot pile. + if(SCAVENGING_SPAWN_MOUSE) + var/nasty_rodent = pick("mouse", "rodent", "squeaky critter", "stupid pest", "annoying cable chewer", "nasty, ugly, evil, disease-ridden rodent") + to_chat(user, "You found something in [source]... no wait, that's just another [nasty_rodent].") + new /mob/living/simple_animal/mouse(source.loc) + if(SCAVENGING_SPAWN_MICE) + user.visible_message("A small gang of mice emerges from [source].", \ + "You found something in [source]... no wait, that's just another- no wait, that's a lot of damn mice.") + for(var/i in 1 to rand(4, 6)) + new /mob/living/simple_animal/mouse(source.loc) + if(SCAVENGING_SPAWN_TOM) + if(GLOB.tom_existed) //There can only be one. + to_chat(user, "You found nothing, better luck next time.") + free = TRUE + else + to_chat(user, "You found something in [source]... no wait, that's Tom, the mouse! What is he doing here?") + new /mob/living/simple_animal/mouse/brown/Tom(source.loc) + else + special = FALSE + + if(!special) //generic loot. Nothing too strange like more loot spawners anyway. + var/atom/A = new loot(source.loc) + if(isitem(A) && !user.get_active_held_item()) + user.put_in_hands(A) + var/rarity_append = "." + switch(loot_table[loot]/mean_loot_weight*100) + if(0 to 1) + rarity_append = "! AMAZING!" + if(1 to 2) + rarity_append = "! Woah!" + if(2 to 5) + rarity_append = ". Rare!" + if(5 to 10) + rarity_append = ". Great." + if(10 to 25) + rarity_append = ". Nice." + if(20 to 50) + rarity_append = ". Not bad." + to_chat(user, "You found something in [source]... it's \a [A][rarity_append]") + + if(unique_loot[loot]) + var/loot_left = --unique_loot[loot] + if(!loot_left) + loot_table -= loot + unique_loot -= loot + mean_loot_weight = 0 + for(var/A in loot_table) //re-tally the list weights + mean_loot_weight += loot_table[A] + mean_loot_weight /= length(loot_table) + + if(free) + return + + --loot_left_per_atom[source] + if(del_atom_on_depletion && !loot_left_per_atom[source]) + source.visible_message("[source] has been looted clean.") + qdel(source) + return + + if(!loot_restriction) + return + + LAZYINITLIST(scavenger_restriction_list) + switch(loot_restriction) + if(LOOT_RESTRICTION_MIND) + scavenger_restriction_list[user.mind]++ + if(LOOT_RESTRICTION_CKEY) + scavenger_restriction_list[user.ckey]++ + if(LOOT_RESTRICTION_MIND_PILE) + LAZYINITLIST(scavenger_restriction_list[source]) + var/list/L = scavenger_restriction_list[source] + L[user.mind]++ + if(LOOT_RESTRICTION_CKEY_PILE) + LAZYINITLIST(scavenger_restriction_list[source]) + var/list/L = scavenger_restriction_list[source] + L[user.ckey]++ diff --git a/code/datums/skills/_skill_holder.dm b/code/datums/skills/_skill_holder.dm index 352adc46ff..e16804ab7f 100644 --- a/code/datums/skills/_skill_holder.dm +++ b/code/datums/skills/_skill_holder.dm @@ -15,7 +15,7 @@ CRASH("Invalid get_skill_value call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this. if(!skills) return null - return skills[skill] + return LAZYACCESS(skills, skill) /** * Grabs our affinity for a skill. !!This is a multiplier!! @@ -25,7 +25,7 @@ CRASH("Invalid get_skill_affinity call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this. if(!skills) return 1 - var/affinity = skill_affinities[skill] + var/affinity = LAZYACCESS(skill_affinities, skill) if(isnull(affinity)) return 1 return affinity @@ -39,7 +39,7 @@ LAZYINITLIST(skills) value = sanitize_skill_value(skill, value) if(!isnull(value)) - skills[skill] = value + LAZYSET(skills, skill, value) return TRUE return FALSE diff --git a/code/datums/wires/autoylathe.dm b/code/datums/wires/autoylathe.dm deleted file mode 100644 index 736c9551ec..0000000000 --- a/code/datums/wires/autoylathe.dm +++ /dev/null @@ -1,48 +0,0 @@ -/datum/wires/autoylathe - holder_type = /obj/machinery/autoylathe - proper_name = "Autoylathe" - -/datum/wires/autoylathe/New(atom/holder) - wires = list( - WIRE_HACK, WIRE_DISABLE, - WIRE_SHOCK, WIRE_ZAP - ) - add_duds(6) - ..() - -/datum/wires/autoylathe/interactable(mob/user) - var/obj/machinery/autoylathe/A = holder - if(A.panel_open) - return TRUE - -/datum/wires/autoylathe/get_status() - var/obj/machinery/autoylathe/A = holder - var/list/status = list() - status += "The red light is [A.disabled ? "on" : "off"]." - status += "The blue light is [A.hacked ? "on" : "off"]." - return status - -/datum/wires/autoylathe/on_pulse(wire) - var/obj/machinery/autoylathe/A = holder - switch(wire) - if(WIRE_HACK) - A.adjust_hacked(!A.hacked) - addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60) - if(WIRE_SHOCK) - A.shocked = !A.shocked - addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60) - if(WIRE_DISABLE) - A.disabled = !A.disabled - addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60) - -/datum/wires/autoylathe/on_cut(wire, mend) - var/obj/machinery/autoylathe/A = holder - switch(wire) - if(WIRE_HACK) - A.adjust_hacked(!mend) - if(WIRE_HACK) - A.shocked = !mend - if(WIRE_DISABLE) - A.disabled = !mend - if(WIRE_ZAP) - A.shock(usr, 50) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index b71f51bd07..dbb6864028 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -50,6 +50,13 @@ var/list/blood_DNA var/list/suit_fibers + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + /atom/New(loc, ...) //atom creation method that preloads variables at creation if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() @@ -965,7 +972,7 @@ Proc for attack log creation, because really why not for(var/x in materials) var/datum/material/custom_material = SSmaterials.GetMaterialRef(x) - if(!(material_flags & MATERIAL_NO_EFFECTS)) + if(material_flags & MATERIAL_EFFECTS) custom_material.on_applied(src, materials[custom_material] * multiplier * material_modifier, material_flags) custom_materials[custom_material] += materials[x] * multiplier diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 64704d4771..edb31e9dbd 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -28,11 +28,11 @@ var/prod_coeff = 1 var/datum/design/being_built - var/datum/techweb/stored_research var/list/datum/design/matching_designs var/selected_category var/screen = 1 + var/datum/techweb/stored_research = /datum/techweb/specialized/autounlocking/autolathe var/list/categories = list( "Tools", "Electronics", @@ -45,9 +45,7 @@ "Dinnerware", "Imported" ) - -/obj/machinery/autolathe/Initialize() - var/static/list/allowed_types = list( + var/list/allowed_materials = list( /datum/material/iron, /datum/material/glass, /datum/material/gold, @@ -63,10 +61,12 @@ /datum/material/adamantine, /datum/material/mythril ) - AddComponent(/datum/component/material_container, allowed_types, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert)) + +/obj/machinery/autolathe/Initialize() + AddComponent(/datum/component/material_container, allowed_materials, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert)) . = ..() wires = new /datum/wires/autolathe(src) - stored_research = new /datum/techweb/specialized/autounlocking/autolathe + stored_research = new stored_research matching_designs = list() /obj/machinery/autolathe/Destroy() @@ -91,7 +91,7 @@ if(AUTOLATHE_SEARCH_MENU) dat = search_win(user) - var/datum/browser/popup = new(user, "autolathe", name, 400, 500) + var/datum/browser/popup = new(user, name, name, 400, 500) popup.set_content(dat) popup.open() @@ -439,3 +439,32 @@ desc = "An autolathe reprogrammed with security protocols to prevent hacking." hackable = FALSE circuit = /obj/item/circuitboard/machine/autolathe/secure + stored_research = /datum/techweb/specialized/autounlocking/autolathe/public + +/obj/machinery/autolathe/toy + name = "autoylathe" + desc = "It produces toys using plastic, metal and glass." + circuit = /obj/item/circuitboard/machine/autolathe/toy + + stored_research = /datum/techweb/specialized/autounlocking/autolathe/toy + categories = list( + "Toys", + "Figurines", + "Pistols", + "Rifles", + "Heavy", + "Melee", + "Armor", + "Adult", + "Misc", + "Imported" + ) + allowed_materials = list( + /datum/material/iron, + /datum/material/glass, + /datum/material/plastic + ) + +/obj/machinery/autolathe/toy/hacked/Initialize() + . = ..() + adjust_hacked(TRUE) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index ad3dd5d720..3a90707bbe 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -16,6 +16,9 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF /obj/machinery/button/Initialize(mapload, ndir = 0, built = 0) + if(istext(id) && mapload) + if(copytext(id, 1, 2) == "!") + id = SSmapping.get_obfuscated_id(id) . = ..() if(built) setDir(ndir) @@ -260,6 +263,11 @@ req_access = list() id = 1 +/obj/machinery/button/electrochromatic + name = "window dim control" + desc = "Controls linked electrochromatic windows" + device_type = /obj/item/assembly/control/electrochromatic + /obj/item/wallframe/button name = "button frame" desc = "Used for building buttons." diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 24fd956677..c26c66a55f 100755 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -53,6 +53,11 @@ using_power = FALSE update_icon() +/obj/machinery/recharger/Exited(atom/movable/M, atom/newloc) + . = ..() + if(charging == M) + setCharging() + /obj/machinery/recharger/attackby(obj/item/G, mob/user, params) if(istype(G, /obj/item/wrench)) if(charging) @@ -111,13 +116,11 @@ charging.update_icon() charging.forceMove(drop_location()) user.put_in_hands(charging) - setCharging(null) /obj/machinery/recharger/attack_tk(mob/user) if(charging) charging.update_icon() charging.forceMove(drop_location()) - setCharging(null) /obj/machinery/recharger/process() if(stat & (NOPOWER|BROKEN) || !anchored) diff --git a/code/game/machinery/toylathe.dm b/code/game/machinery/toylathe.dm deleted file mode 100644 index 4e039d304c..0000000000 --- a/code/game/machinery/toylathe.dm +++ /dev/null @@ -1,415 +0,0 @@ -#define AUTOYLATHE_MAIN_MENU 1 -#define AUTOYLATHE_CATEGORY_MENU 2 -#define AUTOYLATHE_SEARCH_MENU 3 - -/obj/machinery/autoylathe - name = "autoylathe" - desc = "It produces toys using plastic, metal and glass." - icon_state = "autolathe" - density = TRUE - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - circuit = /obj/item/circuitboard/machine/autoylathe - layer = BELOW_OBJ_LAYER - - var/operating = FALSE - var/list/L = list() - var/list/LL = list() - var/hacked = FALSE - var/disabled = 0 - var/shocked = FALSE - var/hack_wire - var/disable_wire - var/shock_wire - - var/busy = FALSE - var/prod_coeff = 1 - - var/datum/design/being_built - var/datum/techweb/stored_research - var/list/datum/design/matching_designs - var/selected_category - var/screen = 1 - - var/list/categories = list( - "Toys", - "Figurines", - "Pistols", - "Rifles", - "Heavy", - "Melee", - "Armor", - "Adult", - "Misc", - "Imported" - ) - -/obj/machinery/autoylathe/Initialize() - var/static/list/allowed_materials = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/plastic - ) - AddComponent(/datum/component/material_container, allowed_materials, 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) - . = ..() - - wires = new /datum/wires/autoylathe(src) - stored_research = new /datum/techweb/specialized/autounlocking/autoylathe - matching_designs = list() - -/obj/machinery/autoylathe/Destroy() - QDEL_NULL(wires) - return ..() - -/obj/machinery/autoylathe/ui_interact(mob/user) - . = ..() - if(!is_operational()) - return - - if(shocked && !(stat & NOPOWER)) - shock(user,50) - - var/dat - - switch(screen) - if(AUTOYLATHE_MAIN_MENU) - dat = main_win(user) - if(AUTOYLATHE_CATEGORY_MENU) - dat = category_win(user,selected_category) - if(AUTOYLATHE_SEARCH_MENU) - dat = search_win(user) - - var/datum/browser/popup = new(user, "Autoylathe", name, 400, 500) - popup.set_content(dat) - popup.open() - -/obj/machinery/autoylathe/on_deconstruction() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.retrieve_all() - -/obj/machinery/autoylathe/attackby(obj/item/O, mob/user, params) - if (busy) - to_chat(user, "The autoylathe is busy. Please wait for completion of previous operation.") - return TRUE - - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O)) - updateUsrDialog() - return TRUE - - if(default_deconstruction_crowbar(O)) - return TRUE - - if(panel_open && is_wire_tool(O)) - wires.interact(user) - return TRUE - - if(user.a_intent == INTENT_HARM) //so we can hit the machine - return ..() - - if(stat) - return TRUE - - if(istype(O, /obj/item/disk/design_disk)) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") - busy = TRUE - var/obj/item/disk/design_disk/D = O - if(do_after(user, 14.4, target = src)) - for(var/B in D.blueprints) - if(B) - stored_research.add_design(B) - busy = FALSE - return TRUE - - return ..() - -/obj/machinery/autoylathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted) - if(item_inserted.custom_materials?.len && item_inserted.custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) - flick("autolathe_r",src)//plays glass insertion animation by default otherwise - else - flick("autolathe_o",src)//plays metal insertion animation - - use_power(min(1000, amount_inserted / 100)) - updateUsrDialog() - -/obj/machinery/autoylathe/Topic(href, href_list) - if(..()) - return - if (!busy) - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - updateUsrDialog() - - if(href_list["category"]) - selected_category = href_list["category"] - updateUsrDialog() - - if(href_list["make"]) - - ///////////////// - //href protection - being_built = stored_research.isDesignResearchedID(href_list["make"]) - if(!being_built) - return - - var/multiplier = text2num(href_list["multiplier"]) - var/is_stack = ispath(being_built.build_path, /obj/item/stack) - multiplier = clamp(multiplier,1,50) - - ///////////////// - - var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient - var/total_amount = 0 - for(var/MAT in being_built.materials) - total_amount += being_built.materials[MAT] - var/power = max(2000, (total_amount)*multiplier/5) //Change this to use all materials - - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - - var/list/materials_used = list() - var/list/custom_materials = list() //These will apply their material effect, This should usually only be one. - - for(var/MAT in being_built.materials) - var/datum/material/used_material = MAT - var/amount_needed = being_built.materials[MAT] * coeff * multiplier - if(istext(used_material)) //This means its a category - var/list/list_to_show = list() - for(var/i in SSmaterials.materials_by_category[used_material]) - if(materials.materials[i] > 0) - list_to_show += i - - used_material = input("Choose [used_material]", "Custom Material") as null|anything in list_to_show - if(!used_material) - return //Didn't pick any material, so you can't build shit either. - custom_materials[used_material] += amount_needed - - materials_used[used_material] = amount_needed - - if(materials.has_materials(materials_used)) - busy = TRUE - use_power(power) - icon_state = "autolathe_n" - var/time = is_stack ? 32 : 32*coeff*multiplier - addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack), time) - else - to_chat(usr, "Not enough materials for this operation.") - - if(href_list["search"]) - matching_designs.Cut() - - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(findtext(D.name,href_list["to_search"])) - matching_designs.Add(D) - updateUsrDialog() - else - to_chat(usr, "The autoylathe is busy. Please wait for completion of previous operation.") - - updateUsrDialog() - - return - -/obj/machinery/autoylathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/atom/A = drop_location() - use_power(power) - materials.use_materials(materials_used) - - if(is_stack) - var/obj/item/stack/N = new being_built.build_path(A, multiplier) - N.update_icon() - N.autoylathe_crafted(src) - else - for(var/i=1, i<=multiplier, i++) - var/obj/item/new_item = new being_built.build_path(A) - new_item.autoylathe_crafted(src) - if(length(picked_materials)) - new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount - icon_state = "autolathe" - busy = FALSE - updateDialog() - -/obj/machinery/autoylathe/RefreshParts() - var/T = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - T += MB.rating*75000 - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.max_amount = T - T=1.2 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T -= M.rating*0.2 - prod_coeff = clamp(T,1,0) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4 - -/obj/machinery/autoylathe/proc/main_win(mob/user) - var/dat = "
| [C] | " - line_length++ - - dat += "