diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cff3b0870901..54ade9af8ca0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -99,20 +99,42 @@ jobs:
tools/ci/generate_maplist.sh
DreamMaker -DMULTIINSTANCE -DCIMAP paradise.dme
+ station_mapload_tests:
+ name: Station Tests
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false # Let all map tests run to completion
+ matrix:
+ station:
+ ['boxstation', 'deltastation', 'metastation', 'cerestation', 'emeraldstation']
+ byondtype: ['STABLE', 'BETA']
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Cache
+ uses: actions/cache@v4
+ with:
+ path: $HOME/BYOND
+ key: ${{ runner.os }}-byond
+ - name: Install RUST_G Deps
+ run: |
+ sudo dpkg --add-architecture i386
+ sudo apt update || true
+ sudo apt install zlib1g-dev:i386
+ tools/ci/install_rustg.sh
+ - name: Compile & Run Unit Tests
+ run: |
+ tools/ci/install_byond.sh '${{ matrix.byondtype }}'
+ source $HOME/BYOND/byond/bin/byondsetup
+ DreamMaker -DMAP_TESTS -DTEST_CONFIG_OVERRIDE=\"unit_tests\" -DMULTIINSTANCE -DCIBUILDING paradise.dme
+ echo '/datum/map/${{ matrix.station }}' > data/next_map.txt
+ tools/ci/run_server.sh
+
unit_tests_and_sql:
name: Unit Tests + SQL Validation
runs-on: ubuntu-22.04
strategy:
- fail-fast: false # Let all map tests run to completion
+ fail-fast: false
matrix:
- maptype:
- [
- '/datum/map/boxstation',
- '/datum/map/deltastation',
- '/datum/map/metastation',
- '/datum/map/cerestation',
- '/datum/map/emeraldstation',
- ]
byondtype: ['STABLE', 'BETA']
services:
mariadb:
@@ -144,8 +166,8 @@ jobs:
run: |
tools/ci/install_byond.sh '${{ matrix.byondtype }}'
source $HOME/BYOND/byond/bin/byondsetup
- DreamMaker -DMULTIINSTANCE -DCIBUILDING paradise.dme
- echo '${{ matrix.maptype }}' > data/next_map.txt
+ DreamMaker -DGAME_TESTS -DTEST_CONFIG_OVERRIDE=\"unit_tests\" -DMULTIINSTANCE -DCIBUILDING paradise.dme
+ echo '/datum/map/test_tiny' > data/next_map.txt
tools/ci/run_server.sh
windows_dll_tests:
diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 9bf589d9dcc5..705d7e1e1203 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -83,6 +83,7 @@ CREATE TABLE `characters` (
`runechat_color` VARCHAR(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#FFFFFF',
`cyborg_brain_type` ENUM('MMI', 'Robobrain', 'Positronic') NOT NULL DEFAULT 'MMI',
`pda_ringtone` VARCHAR(16) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
+ `species_subtype` VARCHAR(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'None',
PRIMARY KEY (`id`),
KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=125467 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/SQL/updates/63-64.sql b/SQL/updates/63-64.sql
new file mode 100644
index 000000000000..307d0940eb7e
--- /dev/null
+++ b/SQL/updates/63-64.sql
@@ -0,0 +1,5 @@
+# Updates the DB from 63 to 64 ~SpaghettiBit
+# Adds a subtype race to be stored on character saves
+# Add species_subtype after pda_ringtone
+ALTER TABLE `characters`
+ ADD COLUMN `species_subtype` VARCHAR(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'None' AFTER `pda_ringtone`;
diff --git a/_maps/__MAP_DEFINES.dm b/_maps/__MAP_DEFINES.dm
index 8f55a0aea114..a275d07a744c 100644
--- a/_maps/__MAP_DEFINES.dm
+++ b/_maps/__MAP_DEFINES.dm
@@ -13,6 +13,8 @@
#define STATION_CONTACT "Station Contact"
// A level dedicated to admin use
#define ADMIN_LEVEL "Admin Level"
+ // For Z-levels dedicated to auto-spawning stuff in
+ #define Z_FLAG_RESERVED "Reserved"
// A level that can be navigated to by the crew without admin intervention or the emergency shuttle.
#define REACHABLE_BY_CREW "Reachable"
// For away missions - used by some consoles
diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm
index 4eb1f89aea60..623d38612dc9 100644
--- a/_maps/map_files/stations/boxstation.dmm
+++ b/_maps/map_files/stations/boxstation.dmm
@@ -35,7 +35,6 @@
"aag" = (
/obj/machinery/hologram/holopad,
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel,
/area/station/maintenance/incinerator)
"aah" = (
@@ -5887,7 +5886,6 @@
"avE" = (
/obj/effect/decal/cleanable/dirt,
/obj/item/stack/cable_coil/random,
-/mob/living/simple_animal/mouse/white,
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint)
"avF" = (
@@ -10098,7 +10096,6 @@
/turf/simulated/floor/plating,
/area/shuttle/pod_2)
"aJW" = (
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
/obj/effect/mapping_helpers/turfs/damage,
/turf/simulated/floor/wood,
@@ -10109,11 +10106,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint2)
-"aJY" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"aJZ" = (
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
@@ -11123,10 +11115,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/fsmaint)
-"aNi" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fsmaint)
"aNj" = (
/obj/structure/table,
/turf/simulated/floor/plating,
@@ -12254,10 +12242,6 @@
icon_state = "arrival"
},
/area/station/hallway/secondary/entry/north)
-"aRg" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"aRh" = (
/obj/machinery/sleeper{
dir = 4
@@ -21821,11 +21805,6 @@
/obj/item/storage/toolbox/emergency,
/turf/simulated/floor/plating,
/area/station/public/storage/emergency/port)
-"buj" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/port)
"buk" = (
/obj/structure/sign/securearea{
desc = "A warning sign which reads 'KEEP CLEAR OF DOCKING AREA'.";
@@ -25808,10 +25787,6 @@
},
/turf/simulated/wall/r_wall,
/area/station/maintenance/port)
-"bKH" = (
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"bKI" = (
/obj/structure/cable{
icon_state = "0-2"
@@ -32451,7 +32426,6 @@
/turf/simulated/floor/plating/airless,
/area/station/science/toxins/test)
"clF" = (
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
dir = 4
},
@@ -33633,7 +33607,6 @@
/area/station/command/office/ntrep)
"cqK" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
},
@@ -34885,12 +34858,6 @@
/obj/item/stack/rods,
/turf/simulated/floor/plasteel,
/area/station/maintenance/apmaint)
-"cvl" = (
-/mob/living/simple_animal/mouse/gray,
-/obj/effect/decal/cleanable/dirt,
-/obj/structure/disposalpipe/segment,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/port)
"cvm" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/suit_storage_unit/expedition,
@@ -44097,7 +44064,6 @@
/area/station/maintenance/turbine)
"dev" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel,
/area/station/maintenance/turbine)
"dew" = (
@@ -45625,10 +45591,6 @@
/obj/effect/decal/cleanable/cobweb,
/turf/simulated/floor/plating,
/area/station/maintenance/aft)
-"dkv" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"dky" = (
/obj/machinery/camera{
c_tag = "Central Hallway North-East"
@@ -49233,10 +49195,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/hallway/primary/port/east)
-"egi" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"egq" = (
/obj/structure/closet/secure_closet/brig,
/obj/machinery/power/apc/directional/east,
@@ -53660,7 +53618,6 @@
/obj/structure/cable{
icon_state = "1-2"
},
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint2)
"gsl" = (
@@ -71961,22 +71918,6 @@
icon_state = "caution"
},
/area/station/engineering/break_room)
-"pUR" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 4
- },
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint2)
"pUU" = (
/obj/structure/filingcabinet,
/turf/simulated/floor/carpet,
@@ -80364,7 +80305,6 @@
},
/area/station/maintenance/asmaint)
"uql" = (
-/mob/living/simple_animal/mouse,
/obj/effect/turf_decal/stripes/line{
dir = 4
},
@@ -101858,7 +101798,7 @@ nxX
aGn
aPW
aQb
-aRg
+aHS
aHl
aGn
rPS
@@ -103391,7 +103331,7 @@ bat
aDA
aEH
aEJ
-aJY
+aHl
aIK
aGn
aHS
@@ -103423,7 +103363,7 @@ blM
bvq
cho
bwZ
-cvl
+pcH
bCr
pwm
cwA
@@ -106994,7 +106934,7 @@ aaa
aaa
loS
dEG
-aRg
+aHS
vbm
aJX
jQt
@@ -108053,7 +107993,7 @@ bnK
bnK
cJV
blQ
-bKH
+bnK
bxb
bIX
bQf
@@ -111127,7 +111067,7 @@ bkg
blX
blQ
bsR
-buj
+svp
bvw
bDJ
blQ
@@ -111674,7 +111614,7 @@ bBf
wrU
nIY
cqs
-egi
+coL
xxQ
omB
coL
@@ -131409,7 +131349,7 @@ aGX
aQI
aGY
aGX
-aNi
+aGY
aGY
aGX
aGX
@@ -132253,7 +132193,7 @@ bGG
qZN
oyY
oyY
-pUR
+upd
bGG
qZN
dQM
@@ -137415,7 +137355,7 @@ cNq
cga
ePu
ePu
-dkv
+csL
isD
csL
jPw
diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm
index e87ccdcfcbcc..deb6cb16f765 100644
--- a/_maps/map_files/stations/cerestation.dmm
+++ b/_maps/map_files/stations/cerestation.dmm
@@ -23636,13 +23636,6 @@
},
/turf/simulated/floor/plating,
/area/station/engineering/atmos/distribution)
-"deS" = (
-/mob/living/simple_animal/mouse/brown,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/port2)
"deW" = (
/obj/item/radio/intercom{
pixel_y = 28;
@@ -36622,7 +36615,6 @@
/obj/machinery/light/small{
dir = 8
},
-/mob/living/simple_animal/mouse/brown,
/turf/simulated/floor/plating/asteroid/ancient,
/area/station/maintenance/port2)
"gtH" = (
@@ -37921,12 +37913,6 @@
},
/turf/simulated/floor/transparent/glass/reinforced,
/area/station/maintenance/starboard)
-"gLX" = (
-/mob/living/simple_animal/mouse/brown,
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/fsmaint)
"gLY" = (
/obj/machinery/door/poddoor/shutters/preopen{
dir = 8;
@@ -38245,15 +38231,6 @@
/obj/machinery/space_heater,
/turf/simulated/floor/plating,
/area/station/maintenance/disposal/south)
-"gRw" = (
-/mob/living/simple_animal/mouse/white,
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/starboard)
"gRz" = (
/obj/effect/decal/cleanable/blood/tracks/mapped,
/obj/effect/mapping_helpers/turfs/burn,
@@ -38604,10 +38581,6 @@
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/plating/asteroid/ancient,
/area/station/maintenance/maintcentral)
-"gWv" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"gWO" = (
/obj/structure/closet/wardrobe/white,
/obj/item/clothing/shoes/jackboots,
@@ -41488,7 +41461,6 @@
/obj/machinery/light/small{
dir = 8
},
-/mob/living/simple_animal/mouse/brown,
/turf/simulated/floor/plating{
icon_state = "asteroidplating"
},
@@ -46695,13 +46667,6 @@
/obj/effect/decal/cleanable/cobweb,
/turf/simulated/floor/plating,
/area/station/maintenance/disposal/southwest)
-"iXy" = (
-/obj/structure/window/reinforced{
- dir = 8
- },
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating/asteroid/ancient,
-/area/station/maintenance/starboard)
"iXK" = (
/obj/machinery/atmospherics/pipe/simple/hidden{
dir = 10
@@ -53284,14 +53249,6 @@
icon_state = "green"
},
/area/station/public/quantum/service)
-"kAu" = (
-/mob/living/simple_animal/mouse/gray,
-/obj/effect/spawner/random/dirt/often,
-/turf/simulated/floor/plasteel{
- dir = 8;
- icon_state = "yellow"
- },
-/area/station/maintenance/fore)
"kAv" = (
/obj/machinery/hologram/holopad,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
@@ -55199,10 +55156,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/wall,
/area/station/hallway/primary/port/south)
-"laM" = (
-/mob/living/simple_animal/mouse/brown,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fore2)
"lbf" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -62945,10 +62898,6 @@
icon_state = "escape"
},
/area/station/maintenance/port2)
-"mXW" = (
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating/asteroid/ancient,
-/area/station/maintenance/asmaint)
"mYa" = (
/obj/machinery/flasher{
id = "Cell 2";
@@ -63472,7 +63421,6 @@
/area/station/maintenance/fore)
"neD" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
/obj/effect/turf_decal/delivery/partial{
dir = 4
},
@@ -66193,11 +66141,6 @@
icon_state = "neutralfull"
},
/area/station/hallway/primary/fore/west)
-"nQU" = (
-/mob/living/simple_animal/mouse/brown,
-/obj/effect/decal/cleanable/dirt,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fore2)
"nQW" = (
/obj/machinery/door/airlock/public/glass,
/obj/effect/mapping_helpers/airlock/autoname,
@@ -68334,7 +68277,6 @@
/area/station/science/test_chamber)
"ove" = (
/obj/effect/spawner/random/dirt/frequent,
-/mob/living/simple_animal/mouse/gray,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/plating,
@@ -70262,15 +70204,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/hallway/primary/fore/east)
-"oUR" = (
-/mob/living/simple_animal/mouse/gray,
-/obj/machinery/light/small{
- dir = 1
- },
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/apmaint)
"oVb" = (
/obj/structure/reagent_dispensers/watertank,
/turf/simulated/floor/plating,
@@ -72741,12 +72674,6 @@
icon_state = "bluered"
},
/area/station/hallway/secondary/entry/west)
-"pAV" = (
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/port)
"pBh" = (
/obj/structure/cable/orange{
icon_state = "4-8"
@@ -76374,13 +76301,6 @@
/obj/structure/flora/ausbushes/sunnybush,
/turf/simulated/floor/grass,
/area/station/hallway/spacebridge/scidock)
-"qvA" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/fore)
"qvJ" = (
/obj/structure/lattice/catwalk,
/turf/space,
@@ -81989,10 +81909,6 @@
icon_state = "asteroidplating"
},
/area/station/maintenance/starboard)
-"rVL" = (
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating/asteroid/ancient,
-/area/station/maintenance/apmaint)
"rVZ" = (
/obj/machinery/door/firedoor,
/obj/machinery/door_control{
@@ -86268,11 +86184,6 @@
icon_state = "cafeteria"
},
/area/station/medical/break_room)
-"sXA" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port2)
"sXG" = (
/obj/machinery/light{
dir = 8
@@ -106601,7 +106512,6 @@
/turf/simulated/floor/plating,
/area/station/maintenance/fore)
"xUz" = (
-/mob/living/simple_animal/mouse/gray,
/obj/effect/decal/cleanable/dirt,
/obj/structure/disposalpipe/segment/corner,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
@@ -106867,12 +106777,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/service/janitor)
-"xXN" = (
-/mob/living/simple_animal/mouse/brown,
-/turf/simulated/floor/plating{
- icon_state = "asteroidplating"
- },
-/area/station/maintenance/starboard)
"xXS" = (
/obj/structure/reagent_dispensers/watertank,
/turf/simulated/floor/plating,
@@ -119133,7 +119037,7 @@ aXn
lVV
qcE
tiE
-gWv
+jee
kZy
lVV
aXn
@@ -121184,7 +121088,7 @@ aXn
aXn
hvI
eTI
-pAV
+eCI
pXy
dHZ
wzC
@@ -121508,7 +121412,7 @@ wms
adg
fZa
fdG
-oUR
+qlW
sKh
sKh
wIq
@@ -128427,7 +128331,7 @@ ueB
rXS
lSc
reB
-deS
+reB
kZd
iaF
aXn
@@ -130608,7 +130512,7 @@ wDs
wDs
wDs
bDo
-nQU
+vXM
vrQ
wDs
afd
@@ -132267,7 +132171,7 @@ rNK
lzH
rNK
cKa
-sXA
+dQZ
dQZ
qpx
shL
@@ -132446,7 +132350,7 @@ wDs
wDs
uEX
tbe
-laM
+vrQ
bJW
tbe
wDs
@@ -132584,7 +132488,7 @@ koR
koR
dHN
xPU
-rVL
+wsR
wsR
cWF
wIq
@@ -138339,7 +138243,7 @@ vPt
vPt
vPt
vPt
-laM
+vrQ
aGg
bbc
bbc
@@ -143460,7 +143364,7 @@ fFd
gKc
cdz
rTz
-qvA
+bjF
bjF
bjF
bjF
@@ -144500,7 +144404,7 @@ bkp
bkp
mEg
scz
-kAu
+scz
vnu
qIa
qaI
@@ -147455,7 +147359,7 @@ plQ
plQ
plQ
rlz
-mXW
+imG
ggK
xON
mJk
@@ -159508,7 +159412,7 @@ xyF
sVv
yfZ
gFg
-gRw
+xAV
tvJ
aZN
aZM
@@ -161292,7 +161196,7 @@ bCq
bkH
qhP
fqw
-iXy
+bLj
gSQ
bLj
bLj
@@ -165357,7 +165261,7 @@ hqL
cpC
cpC
cpC
-gLX
+cpC
rNW
gGH
pMv
@@ -165402,7 +165306,7 @@ vHH
wyL
gSQ
gSQ
-xXN
+gFg
gFg
gSQ
gSQ
diff --git a/_maps/map_files/stations/deltastation.dmm b/_maps/map_files/stations/deltastation.dmm
index 20cd6300d0bc..3db4a9ae4a1e 100644
--- a/_maps/map_files/stations/deltastation.dmm
+++ b/_maps/map_files/stations/deltastation.dmm
@@ -14079,7 +14079,7 @@
},
/area/station/service/kitchen)
"aYD" = (
-/obj/machinery/atmospherics/unary/thermomachine/heater/on{
+/obj/machinery/atmospherics/unary/thermomachine/heater{
dir = 4
},
/turf/simulated/floor/plasteel{
@@ -16100,7 +16100,7 @@
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bfw" = (
/obj/machinery/light{
dir = 1
@@ -18501,7 +18501,7 @@
},
/obj/effect/spawner/window/reinforced,
/turf/simulated/floor/plating,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bmO" = (
/obj/structure/table,
/obj/item/storage/box/prisoner,
@@ -19413,7 +19413,7 @@
},
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bpS" = (
/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
dir = 1
@@ -19924,17 +19924,17 @@
/obj/machinery/space_heater,
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"brO" = (
/obj/machinery/atmospherics/portable/canister/sleeping_agent,
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"brP" = (
/obj/machinery/atmospherics/portable/canister/nitrogen,
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"brR" = (
/obj/machinery/atmospherics/portable/canister/air,
/obj/structure/sign/nosmoking_2{
@@ -19942,7 +19942,7 @@
},
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"brS" = (
/turf/simulated/floor/plasteel,
/area/station/maintenance/starboard2)
@@ -20384,7 +20384,7 @@
},
/obj/effect/turf_decal/delivery,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"btt" = (
/obj/machinery/atmospherics/portable/canister/sleeping_agent,
/obj/structure/cable{
@@ -20395,7 +20395,7 @@
},
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"btu" = (
/obj/machinery/atmospherics/portable/canister/nitrogen,
/obj/structure/cable{
@@ -20407,17 +20407,17 @@
},
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"btv" = (
/obj/machinery/atmospherics/portable/canister/oxygen,
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"btw" = (
/obj/machinery/atmospherics/portable/canister/air,
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"btx" = (
/obj/structure/window/reinforced,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
@@ -20701,14 +20701,11 @@
dir = 1
},
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"buE" = (
/obj/structure/cable{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/simple/visible/yellow{
- dir = 4
- },
/obj/machinery/atmospherics/pipe/simple/visible/cyan,
/obj/effect/turf_decal/stripes/line{
dir = 1
@@ -20717,7 +20714,7 @@
icon_state = "1-4"
},
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"buF" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -20740,23 +20737,14 @@
/obj/machinery/firealarm/directional/east,
/turf/simulated/floor/plasteel,
/area/station/engineering/control)
-"buI" = (
-/obj/machinery/atmospherics/pipe/simple/visible/yellow{
- dir = 4
- },
-/turf/simulated/floor/plasteel{
- dir = 1;
- icon_state = "yellowcorner"
- },
-/area/station/hallway/primary/port/north)
"buJ" = (
/obj/structure/cable{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/manifold/visible/yellow{
- dir = 1
- },
/obj/structure/disposalpipe/segment,
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 6
+ },
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
@@ -21017,15 +21005,18 @@
dir = 8;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bvO" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 5
+ },
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bvP" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -21034,22 +21025,31 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 4
+ },
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bvR" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/effect/turf_decal/delivery,
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 4
+ },
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bvT" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/effect/turf_decal/delivery,
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 4
+ },
/turf/simulated/floor/plasteel,
/area/station/hallway/primary/port/north)
"bvV" = (
@@ -21403,14 +21403,14 @@
dir = 8;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bxc" = (
/obj/item/kirbyplants/large,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bxe" = (
/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers,
/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
@@ -21858,7 +21858,7 @@
dir = 8;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"byB" = (
/obj/machinery/hologram/holopad,
/obj/structure/cable{
@@ -21872,7 +21872,7 @@
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"byC" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -21883,7 +21883,7 @@
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"byD" = (
/obj/structure/chair/office/dark{
dir = 4
@@ -21898,7 +21898,7 @@
dir = 4;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"byF" = (
/obj/machinery/atmospherics/pipe/simple/visible/cyan{
dir = 4
@@ -22349,7 +22349,7 @@
dir = 8;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bzY" = (
/obj/structure/chair/office/dark{
dir = 8
@@ -22357,13 +22357,13 @@
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bzZ" = (
/obj/structure/chair/office/dark,
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bAa" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -22381,7 +22381,7 @@
dir = 4;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bAd" = (
/obj/structure/urinal{
pixel_y = 28
@@ -22856,7 +22856,7 @@
dir = 10;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bBK" = (
/obj/machinery/alarm/directional/south,
/obj/machinery/computer/station_alert{
@@ -22865,7 +22865,7 @@
/turf/simulated/floor/plasteel{
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bBL" = (
/obj/machinery/light,
/obj/machinery/computer/atmos_alert{
@@ -22875,7 +22875,7 @@
/turf/simulated/floor/plasteel{
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bBM" = (
/obj/structure/table/reinforced,
/obj/item/tank/internals/emergency_oxygen,
@@ -22886,7 +22886,7 @@
/turf/simulated/floor/plasteel{
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bBN" = (
/obj/structure/table/reinforced,
/obj/item/paper_bin,
@@ -22899,7 +22899,7 @@
dir = 6;
icon_state = "caution"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"bBO" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -51173,7 +51173,7 @@
},
/obj/machinery/door/window{
dir = 8;
- name = "Atmospherics Desk"
+ name = "Atmospherics Cannister Storage"
},
/obj/effect/mapping_helpers/airlock/windoor/access/any/engineering/atmos{
dir = 8
@@ -51188,7 +51188,7 @@
icon_state = "1-8"
},
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"dxD" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -51982,6 +51982,10 @@
icon_state = "neutral"
},
/area/station/maintenance/apmaint)
+"dCK" = (
+/obj/machinery/status_display,
+/turf/simulated/wall/r_wall,
+/area/station/engineering/atmos/control)
"dCU" = (
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel,
@@ -58995,6 +58999,10 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/theatre)
+"fvB" = (
+/obj/effect/spawner/window/reinforced,
+/turf/simulated/floor/plating,
+/area/station/engineering/atmos/control)
"fvQ" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -59878,6 +59886,17 @@
/obj/machinery/alarm/directional/north,
/turf/simulated/floor/plasteel/dark,
/area/station/engineering/smes)
+"fQW" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/machinery/power/apc/important/directional/east,
+/turf/simulated/floor/plasteel,
+/area/station/engineering/atmos/control)
"fRa" = (
/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
@@ -59941,12 +59960,10 @@
/obj/structure/cable{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/simple/visible/yellow{
- dir = 5
- },
/obj/structure/disposalpipe/sortjunction{
sort_type_txt = "6"
},
+/obj/machinery/atmospherics/pipe/manifold/visible/yellow,
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
@@ -64542,16 +64559,6 @@
/obj/effect/mapping_helpers/turfs/damage,
/turf/simulated/floor/wood,
/area/station/maintenance/abandonedbar)
-"iFv" = (
-/obj/machinery/atmospherics/pipe/simple/visible/yellow{
- dir = 4
- },
-/obj/effect/spawner/window/reinforced/grilled,
-/obj/structure/cable{
- icon_state = "0-8"
- },
-/turf/simulated/floor/plating,
-/area/station/engineering/atmos)
"iFB" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -67339,6 +67346,11 @@
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/plating,
/area/station/maintenance/aft)
+"kmY" = (
+/turf/simulated/floor/plasteel{
+ icon_state = "neutralfull"
+ },
+/area/station/engineering/atmos/control)
"kna" = (
/obj/machinery/hologram/holopad,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
@@ -69334,8 +69346,11 @@
/obj/effect/mapping_helpers/airlock/windoor/access/any/engineering/atmos{
dir = 4
},
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 4
+ },
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"ltY" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -75207,7 +75222,7 @@
"oLu" = (
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/atmos/glass{
- name = "Atmospherics Desk"
+ name = "Atmospherics Cannister Storage"
},
/obj/effect/mapping_helpers/airlock/access/all/engineering/atmos,
/obj/structure/cable{
@@ -75217,7 +75232,7 @@
dir = 4
},
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"oLv" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 9
@@ -76956,7 +76971,7 @@
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"pHd" = (
/obj/structure/closet/emcloset,
/turf/simulated/floor/plating,
@@ -78930,7 +78945,7 @@
icon_state = "0-2"
},
/turf/simulated/floor/plating,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"qQv" = (
/obj/effect/turf_decal/delivery,
/obj/machinery/door/firedoor,
@@ -79785,9 +79800,6 @@
/turf/simulated/floor/plasteel,
/area/station/security/brig)
"rke" = (
-/obj/machinery/atmospherics/pipe/simple/visible/yellow{
- dir = 4
- },
/obj/effect/turf_decal/stripes/line{
dir = 1
},
@@ -79795,7 +79807,7 @@
icon_state = "4-8"
},
/turf/simulated/floor/plasteel,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"rkt" = (
/obj/machinery/atmospherics/unary/vent_pump/on,
/turf/simulated/floor/bluegrid{
@@ -80220,6 +80232,9 @@
icon_state = "dark"
},
/area/station/security/prisonershuttle)
+"rvv" = (
+/turf/simulated/wall/r_wall,
+/area/station/engineering/atmos/control)
"rvx" = (
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 8
@@ -82799,7 +82814,7 @@
/obj/structure/cable,
/obj/effect/spawner/window/reinforced/grilled,
/turf/simulated/floor/plating,
-/area/station/engineering/atmos)
+/area/station/engineering/atmos/control)
"sIB" = (
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 1
@@ -83137,13 +83152,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/solar_maintenance/aft_starboard)
-"sQr" = (
-/mob/living/simple_animal/mouse,
-/obj/machinery/light/small{
- dir = 1
- },
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"sQw" = (
/obj/structure/weightmachine/stacklifter,
/turf/simulated/floor/plasteel{
@@ -86307,6 +86315,17 @@
/obj/effect/spawner/window/reinforced/grilled,
/turf/simulated/floor/plating,
/area/station/service/chapel)
+"uvO" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 4
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "neutralfull"
+ },
+/area/station/engineering/atmos/control)
"uwm" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -89263,16 +89282,21 @@
icon_state = "neutralcorner"
},
/area/station/hallway/primary/central)
+"wbs" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/visible/yellow{
+ dir = 10
+ },
+/turf/simulated/floor/plasteel,
+/area/station/engineering/atmos/control)
"wbZ" = (
/obj/machinery/atmospherics/unary/portables_connector,
/turf/simulated/floor/plasteel{
icon_state = "whitepurple"
},
/area/station/science/toxins/mixing)
-"wce" = (
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating,
-/area/station/security/permabrig)
"wcu" = (
/obj/machinery/atmospherics/unary/cryo_cell,
/turf/simulated/floor/plasteel{
@@ -113466,7 +113490,7 @@ cWl
unt
lxh
cHA
-sQr
+ddg
sZp
nCh
cHA
@@ -120354,17 +120378,17 @@ jgG
mzZ
osF
bmI
-aOg
-aOg
-aOb
+rvv
+rvv
+fvB
oLu
bmN
-aOg
-aOg
-aOg
-aOg
-aOg
-aOg
+rvv
+rvv
+rvv
+rvv
+rvv
+rvv
bFb
bGI
bIA
@@ -120611,7 +120635,7 @@ emu
ban
osF
bmJ
-aOg
+rvv
brN
brN
bts
@@ -120621,7 +120645,7 @@ bxb
byz
bzX
bBJ
-aOg
+rvv
bFc
bGH
bIF
@@ -120868,17 +120892,17 @@ vnK
bhp
bkN
bmK
-aOg
+rvv
brO
brO
btt
-buD
+wbs
bvO
-ban
+kmY
bzY
bzY
bBK
-aOg
+rvv
byl
kWX
byl
@@ -121125,7 +121149,7 @@ jJc
biZ
bkO
aQZ
-aOg
+rvv
bpQ
brP
btu
@@ -121135,7 +121159,7 @@ bfv
byB
bzZ
bBL
-aOg
+rvv
bFd
bGK
bID
@@ -121382,17 +121406,17 @@ bhr
bja
bkP
bmM
-aOg
+rvv
btv
btv
btv
rke
-bvO
-ban
+uvO
+kmY
byC
pGT
bBM
-aOg
+rvv
bFe
bGM
kIs
@@ -121639,17 +121663,17 @@ rcz
qJW
aOg
keu
-aOg
+rvv
btw
brR
btw
-rke
+fQW
bvR
bxc
byD
bAb
bBN
-aOg
+rvv
bFf
bGL
bII
@@ -121896,17 +121920,17 @@ aSM
buX
bkQ
aOW
-aOg
-aOg
-aOg
-bvK
-iFv
+rvv
+rvv
+rvv
+dCK
+rvv
ltX
qPT
dxC
sIz
-aOg
-aOg
+rvv
+rvv
naB
xcs
mvr
@@ -122157,7 +122181,7 @@ bcP
bpU
bty
bty
-buI
+bty
bvT
bty
byF
@@ -147830,7 +147854,7 @@ aaa
aaa
ygH
coI
-wce
+aQx
aQx
mtL
aKU
diff --git a/_maps/map_files/stations/emeraldstation.dmm b/_maps/map_files/stations/emeraldstation.dmm
index a68fc3bff64c..9f2849174a69 100644
--- a/_maps/map_files/stations/emeraldstation.dmm
+++ b/_maps/map_files/stations/emeraldstation.dmm
@@ -2023,6 +2023,10 @@
"ave" = (
/turf/simulated/floor/plasteel,
/area/station/supply/storage)
+"avi" = (
+/obj/structure/sign/poster/official/random,
+/turf/simulated/wall,
+/area/station/public/dorms)
"avk" = (
/obj/item/cigbutt/roach,
/turf/simulated/floor/plating,
@@ -14361,11 +14365,6 @@
"cRi" = (
/turf/simulated/wall,
/area/station/security/main)
-"cRk" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint2)
"cRu" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -15265,6 +15264,12 @@
icon_state = "darkredcorners"
},
/area/station/hallway/spacebridge/security/south)
+"dbV" = (
+/obj/machinery/firealarm/directional/north,
+/turf/simulated/floor/plasteel{
+ icon_state = "neutral"
+ },
+/area/station/public/dorms)
"dbY" = (
/obj/machinery/atmospherics/pipe/simple/hidden/purple{
dir = 5
@@ -15571,7 +15576,6 @@
/turf/simulated/floor/plasteel,
/area/station/engineering/hardsuitstorage)
"dfs" = (
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/turf/simulated/floor/wood,
/area/station/maintenance/abandoned_office)
@@ -22315,7 +22319,6 @@
/obj/structure/cable{
icon_state = "2-8"
},
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
dir = 4
},
@@ -22647,6 +22650,7 @@
/area/station/service/bar)
"eBG" = (
/obj/structure/chair/sofa,
+/obj/machinery/firealarm/directional/north,
/turf/simulated/floor/carpet,
/area/station/public/dorms)
"eBM" = (
@@ -27696,6 +27700,9 @@
/obj/machinery/status_display{
pixel_y = 32
},
+/obj/machinery/light{
+ dir = 1
+ },
/turf/simulated/floor/plasteel{
icon_state = "neutral"
},
@@ -37950,7 +37957,6 @@
"hzk" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/generic,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/security/aft_starboard)
"hzA" = (
@@ -40382,11 +40388,6 @@
/obj/machinery/atmospherics/meter,
/turf/simulated/floor/engine,
/area/station/engineering/engine)
-"iam" = (
-/obj/effect/spawner/random/oil/maybe,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/security/fore)
"iaw" = (
/obj/structure/cable{
icon_state = "1-4"
@@ -53384,11 +53385,6 @@
icon_state = "dark"
},
/area/station/aisat/atmos)
-"kCz" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/apmaint2)
"kCA" = (
/turf/simulated/floor/plasteel{
dir = 4;
@@ -53530,18 +53526,6 @@
/obj/machinery/hologram/holopad,
/turf/simulated/floor/carpet,
/area/station/public/vacant_office)
-"kGm" = (
-/obj/machinery/light{
- dir = 1
- },
-/obj/structure/extinguisher_cabinet{
- name = "north bump";
- pixel_y = 30
- },
-/turf/simulated/floor/plasteel{
- icon_state = "neutral"
- },
-/area/station/public/dorms)
"kGu" = (
/obj/structure/table,
/obj/machinery/kitchen_machine/microwave{
@@ -55255,11 +55239,6 @@
/obj/machinery/economy/vending/boozeomat,
/turf/simulated/floor/wood,
/area/station/maintenance/abandonedbar)
-"kXa" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/dorms/port)
"kXf" = (
/obj/structure/cable{
icon_state = "1-4"
@@ -57417,15 +57396,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
/area/station/maintenance/dorms/starboard)
-"lut" = (
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/security/fore)
"luC" = (
/obj/effect/decal/cleanable/confetti,
/obj/effect/decal/cleanable/dirt,
@@ -60029,7 +59999,6 @@
/area/station/supply/lobby)
"lWJ" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/obj/item/trash/spentcasing/shotgun,
/turf/simulated/floor/wood,
/area/station/maintenance/dorms/starboard)
@@ -62111,6 +62080,10 @@
dir = 8
},
/obj/effect/landmark/start/assistant,
+/obj/item/radio/intercom{
+ name = "east bump";
+ pixel_x = 28
+ },
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "neutral"
@@ -64966,12 +64939,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
/area/station/maintenance/starboard)
-"mXx" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/effect/spawner/random/oil/maybe,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"mXy" = (
/obj/structure/closet/secure_closet/miner,
/obj/machinery/firealarm/directional/north,
@@ -67721,14 +67688,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint)
-"nzU" = (
-/mob/living/simple_animal/mouse,
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/apmaint2)
"nAc" = (
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
@@ -68402,12 +68361,6 @@
},
/turf/simulated/floor/plating,
/area/station/engineering/secure_storage)
-"nGt" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/starboard)
"nGw" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -69417,11 +69370,6 @@
icon_state = "white"
},
/area/station/science/toxins/mixing)
-"nPx" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/starboard)
"nPG" = (
/obj/item/kirbyplants/large,
/turf/simulated/floor/plasteel,
@@ -69709,11 +69657,6 @@
/obj/effect/spawner/window/reinforced,
/turf/simulated/floor/plating,
/area/station/medical/virology)
-"nSa" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/dorms/fore)
"nSe" = (
/turf/simulated/floor/plating/airless,
/area/station/aisat/hall)
@@ -74339,7 +74282,6 @@
/turf/simulated/wall,
/area/station/engineering/tech_storage)
"oMl" = (
-/mob/living/simple_animal/mouse/white,
/obj/effect/landmark/spawner/nukedisc_respawn,
/turf/simulated/floor/plating,
/area/station/maintenance/security/fore)
@@ -76854,7 +76796,6 @@
/area/station/engineering/break_room/secondary)
"piI" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
/obj/structure/cable{
icon_state = "4-8"
},
@@ -77545,11 +77486,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel,
/area/station/maintenance/dorms/port)
-"pqt" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fpmaint)
"pqI" = (
/obj/structure/chair{
dir = 1
@@ -79200,10 +79136,6 @@
icon_state = "dark"
},
/area/station/medical/morgue)
-"pHV" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/asmaint)
"pHX" = (
/obj/machinery/atmospherics/pipe/simple/visible/yellow{
dir = 4
@@ -81964,11 +81896,6 @@
},
/turf/simulated/floor/plating/airless,
/area/station/aisat/hall)
-"qmI" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint)
"qmJ" = (
/obj/machinery/power/apc/directional/south,
/obj/structure/cable,
@@ -95430,11 +95357,6 @@
"sTG" = (
/turf/simulated/wall,
/area/station/science/genetics)
-"sTI" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard)
"sTJ" = (
/obj/effect/spawner/random/barrier/grille_maybe,
/obj/effect/decal/cleanable/dirt,
@@ -97280,9 +97202,7 @@
/turf/simulated/floor/plasteel,
/area/station/hallway/primary/central/sw)
"tlu" = (
-/obj/structure/sign/poster/official/random{
- pixel_x = -32
- },
+/obj/machinery/firealarm/directional/west,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "neutral"
@@ -97503,9 +97423,9 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 8
},
-/obj/item/radio/intercom{
+/obj/structure/extinguisher_cabinet{
name = "east bump";
- pixel_x = 28
+ pixel_x = 30
},
/turf/simulated/floor/plasteel{
dir = 8;
@@ -104080,6 +104000,13 @@
icon_state = "cult"
},
/area/station/legal/lawoffice)
+"uDE" = (
+/obj/machinery/firealarm/directional/south,
+/turf/simulated/floor/plasteel{
+ dir = 1;
+ icon_state = "neutral"
+ },
+/area/station/public/dorms)
"uDO" = (
/obj/structure/table,
/obj/effect/spawner/random/maintenance,
@@ -106032,12 +105959,6 @@
icon_state = "cult"
},
/area/station/legal/magistrate)
-"uWS" = (
-/obj/effect/decal/cleanable/generic,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/dorms/starboard)
"uWU" = (
/obj/item/kirbyplants/large,
/turf/simulated/floor/plasteel{
@@ -106416,7 +106337,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
dir = 10
},
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "whiteblue"
@@ -109607,10 +109527,6 @@
icon_state = "dark"
},
/area/station/security/brig)
-"vJc" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft2)
"vJm" = (
/obj/structure/lattice/catwalk,
/obj/structure/cable{
@@ -112035,7 +111951,6 @@
"whf" = (
/obj/effect/mapping_helpers/turfs/damage,
/obj/effect/decal/cleanable/generic,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/security/aft_starboard)
"whh" = (
@@ -132942,7 +132857,7 @@ ipM
hcH
uPm
bqU
-nzU
+xQq
bGM
xKc
rPz
@@ -134484,7 +134399,7 @@ mIg
eyo
opq
yiE
-cRk
+opq
xQq
fMB
aco
@@ -138572,7 +138487,7 @@ qXp
xrG
feI
idv
-mXx
+eBv
sqZ
mQe
lQZ
@@ -140149,7 +140064,7 @@ oeh
pdx
vSD
cBD
-kCz
+fMB
lUy
eJa
sqB
@@ -146500,7 +146415,7 @@ ndJ
qZs
qci
lfn
-qmI
+vWJ
gmL
nwf
nwf
@@ -148266,7 +148181,7 @@ aSK
wIh
qci
vWJ
-pqt
+eMT
vWJ
vWJ
eMT
@@ -150115,7 +150030,7 @@ giS
giS
onz
rpb
-eUn
+tlu
eUn
eUn
qDZ
@@ -150359,7 +150274,7 @@ vIv
uJr
nXW
tyb
-bvA
+dbV
njj
kgg
kgg
@@ -151159,7 +151074,7 @@ dsL
twa
vIV
ril
-kXa
+ewj
xmm
rxd
iSc
@@ -154498,7 +154413,7 @@ bQA
vAZ
vAZ
vAZ
-vAZ
+avi
vAZ
obO
rxd
@@ -154743,7 +154658,7 @@ eUn
uIz
eUn
tfk
-eUn
+tlu
xzA
eEe
vfx
@@ -154752,7 +154667,7 @@ eUn
eUn
eUn
neS
-tlu
+eUn
xjx
tfk
eUn
@@ -155495,7 +155410,7 @@ vAZ
vAZ
vAZ
vAZ
-kGm
+dbV
xoK
nuI
eOw
@@ -158608,7 +158523,7 @@ egS
gsU
fRA
oPi
-uWS
+pIl
xmi
cPy
kcb
@@ -158840,7 +158755,7 @@ rjc
vAZ
xCR
urb
-sRm
+uDE
tZO
tZO
xYm
@@ -159084,7 +158999,7 @@ soN
dlV
iNN
syn
-nSa
+sul
ydG
rNs
rtR
@@ -159912,7 +159827,7 @@ lRd
snP
aIj
rWj
-vJc
+xaN
bVp
dtF
bTR
@@ -162123,7 +162038,7 @@ iBy
gbl
oei
iXk
-lut
+plQ
oIH
cwI
cwI
@@ -168812,7 +168727,7 @@ cwI
cwI
qTH
xEt
-iam
+cuO
fsJ
lVs
vKT
@@ -169161,7 +169076,7 @@ mXw
tVw
dGc
qFP
-nGt
+iPH
bpH
bpH
bpp
@@ -172792,7 +172707,7 @@ qDC
bNH
jnC
jnC
-pHV
+uMS
ugn
nuJ
kTf
@@ -172988,7 +172903,7 @@ fIk
stp
stp
oAs
-sTI
+mXw
bnw
tlf
eDe
@@ -174554,7 +174469,7 @@ oTL
mXw
mIH
nWG
-nPx
+jcy
aQl
caq
pga
diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm
index 4c6a3e2b3c25..fce3b01e5709 100644
--- a/_maps/map_files/stations/metastation.dmm
+++ b/_maps/map_files/stations/metastation.dmm
@@ -834,11 +834,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/solar_maintenance/fore_port)
-"ajD" = (
-/mob/living/simple_animal/mouse,
-/obj/effect/decal/cleanable/dirt,
-/turf/simulated/floor/catwalk,
-/area/station/maintenance/fore)
"ajE" = (
/obj/structure/cable{
icon_state = "0-4"
@@ -12476,7 +12471,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
-/mob/living/simple_animal/mouse,
/obj/structure/cable{
icon_state = "2-4"
},
@@ -15610,10 +15604,6 @@
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint)
-"bmA" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint)
"bmE" = (
/obj/machinery/newscaster/directional/east,
/obj/structure/disposalpipe/segment/corner{
@@ -27082,16 +27072,6 @@
icon_state = "C11"
},
/area/station/hallway/primary/central/south)
-"cbb" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/mob/living/simple_animal/mouse,
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fsmaint)
"cbc" = (
/obj/effect/spawner/random/fungus/maybe,
/turf/simulated/wall/r_wall,
@@ -29744,11 +29724,6 @@
/obj/structure/lattice,
/turf/space,
/area/space/nearstation)
-"clJ" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/turbine)
"clK" = (
/obj/structure/closet,
/obj/item/extinguisher,
@@ -36644,10 +36619,6 @@
},
/turf/simulated/floor/plating/airless,
/area/station/science/toxins/test)
-"cOU" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"cOV" = (
/obj/machinery/button/windowtint{
dir = 1;
@@ -36991,7 +36962,6 @@
"cQt" = (
/obj/effect/mapping_helpers/turfs/burn,
/obj/machinery/light/small,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/medmaint)
"cQu" = (
@@ -39260,10 +39230,6 @@
icon_state = "whiteblue"
},
/area/station/medical/exam_room)
-"dbH" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft)
"dbI" = (
/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
/obj/structure/cable{
@@ -47169,10 +47135,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/hallway/secondary/exit)
-"gof" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"goq" = (
/obj/structure/closet/secure_closet/hos,
/obj/item/cartridge/detective,
@@ -47934,7 +47896,6 @@
dir = 5
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel,
/area/station/maintenance/starboard2)
"gHh" = (
@@ -48677,11 +48638,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/maintenance/aft2)
-"gWz" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fsmaint)
"gWE" = (
/obj/machinery/atmospherics/pipe/simple/hidden{
dir = 4
@@ -51910,10 +51866,6 @@
},
/turf/simulated/floor/engine,
/area/station/engineering/control)
-"isf" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fsmaint)
"isB" = (
/obj/machinery/door/airlock/engineering/glass{
autoclose = 0;
@@ -57262,10 +57214,6 @@
icon_state = "red"
},
/area/station/security/checkpoint/secondary)
-"kQF" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/wood,
-/area/station/maintenance/starboard)
"kRc" = (
/obj/structure/cable{
icon_state = "1-8"
@@ -66844,11 +66792,6 @@
},
/turf/simulated/floor/plating,
/area/station/medical/virology)
-"peg" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/starboard2)
"pej" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -69577,10 +69520,6 @@
icon_state = "white"
},
/area/station/maintenance/aft)
-"qoK" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard2)
"qoQ" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -70757,7 +70696,6 @@
},
/area/station/security/interrogation)
"qRm" = (
-/mob/living/simple_animal/mouse,
/obj/effect/spawner/random/oil/maybe,
/turf/simulated/floor/plasteel,
/area/station/maintenance/fore)
@@ -76381,10 +76319,6 @@
icon_state = "whitepurplecorner"
},
/area/station/science/research)
-"tnW" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard)
"toc" = (
/obj/structure/sign/electricshock,
/turf/simulated/wall/r_wall,
@@ -79948,7 +79882,6 @@
dir = 6
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/obj/structure/cable{
icon_state = "2-4"
},
@@ -80308,7 +80241,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
dir = 4
},
@@ -83377,10 +83309,6 @@
/obj/structure/sign/biohazard,
/turf/simulated/wall/r_wall,
/area/station/science/research)
-"wLB" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fore)
"wMb" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -86663,7 +86591,6 @@
/area/station/engineering/atmos/distribution)
"yih" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/mob/living/simple_animal/mouse,
/obj/machinery/light/small{
dir = 4
},
@@ -86672,10 +86599,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/starboard2)
-"yiP" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fore)
"yiU" = (
/turf/simulated/floor/plasteel{
icon_state = "dark"
@@ -99426,7 +99349,7 @@ qCK
aOB
aOB
adY
-bmA
+adY
aOB
aLF
aML
@@ -103089,7 +103012,7 @@ cpD
crG
bZP
clK
-gof
+crG
crG
bZP
aaa
@@ -104864,7 +104787,7 @@ cdT
cdT
cdT
cdT
-cOU
+bTY
xOy
aSM
aSM
@@ -105587,7 +105510,7 @@ awh
hka
imw
adY
-bmA
+adY
aOB
bmK
bgA
@@ -106196,7 +106119,7 @@ chy
tPO
eJG
czw
-dbH
+chy
bZU
abq
aaa
@@ -118673,7 +118596,7 @@ nEV
nEV
awp
aDY
-wLB
+alW
cnV
nVQ
whf
@@ -119978,7 +119901,7 @@ nSs
acL
aWv
aFj
-yiP
+fpy
bne
ajg
iGR
@@ -121842,7 +121765,7 @@ dtC
cte
gnS
cFB
-peg
+gSO
ipj
cmB
qub
@@ -124109,7 +124032,7 @@ aLl
ayv
aLl
apL
-isf
+avA
eiI
aLl
bdt
@@ -124844,7 +124767,7 @@ aaa
aaa
aaa
ahH
-ajD
+oTt
azu
awp
air
@@ -124869,7 +124792,7 @@ gip
nET
blB
wYm
-cbb
+blB
beF
juq
apV
@@ -124925,7 +124848,7 @@ bPj
wdY
cte
cqE
-qoK
+cwG
dhw
dhw
bUz
@@ -125374,7 +125297,7 @@ air
aBZ
iAS
oyN
-gWz
+cxr
hgU
aLl
fpk
@@ -125410,7 +125333,7 @@ aoG
aoG
aoG
dSv
-tnW
+asJ
nWJ
oXK
aoG
@@ -126462,7 +126385,7 @@ cfL
ciu
ciH
aRQ
-clJ
+aRQ
biB
bul
clB
@@ -126707,7 +126630,7 @@ kbr
vud
axh
cyv
-kQF
+ovR
bZH
fTV
nXk
diff --git a/_maps/map_files220/stations/boxstation.dmm b/_maps/map_files220/stations/boxstation.dmm
index 11bc93b7d85c..cf04fd78f2e8 100644
--- a/_maps/map_files220/stations/boxstation.dmm
+++ b/_maps/map_files220/stations/boxstation.dmm
@@ -31,7 +31,6 @@
"aag" = (
/obj/machinery/hologram/holopad,
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel,
/area/station/maintenance/incinerator)
"aah" = (
@@ -3523,7 +3522,6 @@
dir = 8
},
/obj/machinery/light/small/directional/east,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/fore)
"aqu" = (
@@ -4916,7 +4914,6 @@
dir = 1
},
/obj/item/stack/cable_coil/random,
-/mob/living/simple_animal/mouse/white,
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint)
"avF" = (
@@ -7504,7 +7501,6 @@
"aDY" = (
/obj/item/stack/tile/plasteel,
/obj/effect/decal/cleanable/dust,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/abandonedbar)
"aDZ" = (
@@ -7690,7 +7686,6 @@
/area/station/maintenance/fpmaint2)
"aEK" = (
/obj/structure/chair,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/wood/oak,
/area/station/maintenance/fpmaint2)
"aEL" = (
@@ -8028,7 +8023,6 @@
/area/station/maintenance/fsmaint)
"aFN" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/brown,
/obj/effect/spawner/random/blood/maybe,
/turf/simulated/floor/plasteel{
icon_state = "stage_bleft"
@@ -8439,7 +8433,6 @@
"aHd" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "stage_bleft"
},
@@ -8780,7 +8773,6 @@
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 1
},
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/fsmaint)
"aIw" = (
@@ -9209,7 +9201,6 @@
/turf/simulated/floor/plating,
/area/shuttle/pod_2)
"aJW" = (
-/mob/living/simple_animal/mouse,
/obj/structure/cable{
icon_state = "1-2"
},
@@ -9223,11 +9214,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint2)
-"aJY" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"aKa" = (
/obj/structure/closet,
/obj/effect/spawner/random/maintenance,
@@ -11049,10 +11035,6 @@
icon_state = "arrival"
},
/area/station/hallway/secondary/entry)
-"aRg" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"aRh" = (
/obj/machinery/sleeper{
dir = 4
@@ -21766,10 +21748,6 @@
},
/turf/simulated/wall/r_wall,
/area/station/maintenance/port)
-"bKH" = (
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"bKI" = (
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 4
@@ -27832,13 +27810,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/asmaint2)
-"clF" = (
-/mob/living/simple_animal/mouse,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/apmaint)
"clH" = (
/obj/machinery/camera{
c_tag = "Research Toxins Test Chamber North";
@@ -29057,7 +29028,6 @@
/area/station/engineering/ai_transit_tube)
"cqK" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
},
@@ -30213,15 +30183,6 @@
/obj/item/stack/rods,
/turf/simulated/floor/plasteel,
/area/station/maintenance/apmaint)
-"cvl" = (
-/mob/living/simple_animal/mouse/gray,
-/obj/effect/decal/cleanable/dirt,
-/obj/structure/disposalpipe/segment,
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/port)
"cvm" = (
/obj/item/clothing/gloves/color/rainbow,
/obj/item/clothing/shoes/rainbow,
@@ -31387,7 +31348,6 @@
/obj/structure/sign/nosmoking_2{
pixel_x = -28
},
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/incinerator)
"czJ" = (
@@ -40983,10 +40943,6 @@
icon_state = "purple"
},
/area/station/maintenance/fsmaint)
-"dkv" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"dky" = (
/obj/machinery/camera{
c_tag = "Central Hallway North-East"
@@ -49432,13 +49388,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/hallway/secondary/entry)
-"fMK" = (
-/mob/living/simple_animal/mouse,
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"fMM" = (
/obj/structure/flora/grass/jungle,
/turf/simulated/floor/grass,
@@ -52646,13 +52595,6 @@
icon_state = "neutralfull"
},
/area/station/hallway/secondary/exit)
-"gUp" = (
-/obj/item/holder/mouse,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"gUq" = (
/obj/machinery/light/directional/north,
/turf/simulated/floor/plasteel{
@@ -54209,13 +54151,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/apmaint)
-"hwW" = (
-/obj/effect/decal/cleanable/dust,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- icon_state = "dark"
- },
-/area/station/security/interrogation)
"hxo" = (
/obj/machinery/atmospherics/unary/vent_pump/on,
/turf/simulated/floor/plasteel,
@@ -56898,10 +56833,6 @@
icon_state = "hydrofloor"
},
/area/station/public/toilet/unisex)
-"iBM" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"iBP" = (
/obj/structure/disposalpipe/segment,
/obj/structure/cable{
@@ -57136,10 +57067,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
/area/station/maintenance/asmaint2)
-"iFW" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/aft)
"iGq" = (
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 8
@@ -58993,10 +58920,6 @@
icon_state = "neutralfull"
},
/area/station/public/dorms)
-"jrA" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/aft)
"jrD" = (
/obj/effect/decal/cleanable/dust,
/obj/machinery/atmospherics/unary/vent_pump/on{
@@ -60388,13 +60311,6 @@
icon_state = "dark"
},
/area/station/security/armory/secure)
-"jUn" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/abandonedbar)
"jUq" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -61344,11 +61260,6 @@
/obj/effect/landmark/start/psychiatrist,
/turf/simulated/floor/carpet,
/area/station/medical/psych)
-"koT" = (
-/obj/structure/flora/grass/jungle,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/grass,
-/area/station/security/permabrig)
"kpx" = (
/obj/structure/closet/secure_closet/engineering_personal,
/obj/effect/turf_decal/delivery/hollow,
@@ -61684,7 +61595,6 @@
/obj/structure/chair/sofa/pew{
dir = 1
},
-/mob/living/simple_animal/mouse/gray,
/obj/effect/turf_decal/siding/wood/oak,
/turf/simulated/floor/wood/oak,
/area/station/maintenance/abandonedbar)
@@ -63133,7 +63043,6 @@
/obj/structure/cable{
icon_state = "1-8"
},
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -63674,12 +63583,6 @@
/obj/effect/landmark/start/assistant,
/turf/simulated/floor/transparent/glass/reinforced,
/area/station/public/dorms)
-"lhl" = (
-/obj/item/trash/pistachios,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"lhs" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/effect/turf_decal/stripes/corner{
@@ -65232,7 +65135,6 @@
/area/station/maintenance/turbine)
"lMP" = (
/obj/structure/sign/poster/contraband/random/north,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/wood/oak,
/area/station/maintenance/aft)
"lNb" = (
@@ -65871,7 +65773,6 @@
/turf/simulated/floor/wood,
/area/station/public/mrchangs)
"lXG" = (
-/mob/living/simple_animal/mouse,
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/stripes/line{
dir = 1
@@ -67746,17 +67647,6 @@
icon_state = "neutralfull"
},
/area/station/public/dorms)
-"mHb" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"mHh" = (
/obj/effect/spawner/window/reinforced/polarized/grilled{
id = "hos_room"
@@ -69614,10 +69504,6 @@
icon_state = "darkred"
},
/area/station/security/brig)
-"nte" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft)
"ntg" = (
/mob/living/simple_animal/hostile/retaliate/goat/chef,
/obj/structure/disposalpipe/segment,
@@ -70582,11 +70468,6 @@
icon_state = "whitepurple"
},
/area/station/science/toxins/mixing)
-"nMz" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/turbine)
"nME" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 10
@@ -74877,11 +74758,6 @@
icon_state = "dark"
},
/area/station/security/interrogation)
-"pnE" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft)
"pnG" = (
/obj/structure/railing,
/obj/effect/turf_decal/siding/yellow,
@@ -76624,22 +76500,6 @@
/obj/machinery/light_switch/east,
/turf/simulated/floor/plasteel,
/area/station/supply/office)
-"pUR" = (
-/mob/living/simple_animal/mouse,
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint2)
"pUS" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
@@ -78223,11 +78083,6 @@
},
/turf/simulated/floor/bluegrid,
/area/station/turret_protected/ai)
-"qyy" = (
-/obj/item/holder/mouse,
-/obj/effect/decal/cleanable/dirt,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"qyM" = (
/obj/structure/cable/yellow,
/obj/machinery/computer/monitor{
@@ -78431,17 +78286,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/maintenance/fpmaint)
-"qCJ" = (
-/obj/structure/curtain/open/shower,
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/shower{
- pixel_y = 20
- },
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
- },
-/area/station/maintenance/disposal)
"qCM" = (
/obj/machinery/atmospherics/air_sensor{
autolink_id = "sm_sen_cont";
@@ -83058,7 +82902,6 @@
"ssL" = (
/obj/effect/spawner/random/maintenance,
/obj/item/clothing/gloves/color/blue,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "freezerfloor"
},
@@ -83499,13 +83342,6 @@
icon_state = "darkgreen"
},
/area/station/procedure/trainer_office)
-"sAy" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
- dir = 4
- },
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint2)
"sAB" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/blood,
@@ -85476,11 +85312,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/turf/simulated/floor/plasteel,
/area/station/hallway/primary/central/sw)
-"tkN" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft)
"tkW" = (
/obj/machinery/atmospherics/pipe/manifold/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -89912,7 +89743,6 @@
/obj/effect/turf_decal/stripes/line{
dir = 5
},
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/disposal)
"uUf" = (
@@ -90155,7 +89985,6 @@
"uYz" = (
/obj/effect/decal/cleanable/dust,
/obj/machinery/atmospherics/unary/vent_pump/on,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/wood/oak,
/area/station/maintenance/abandonedbar)
"uYB" = (
@@ -90487,11 +90316,6 @@
icon_state = "rampbottom"
},
/area/station/service/bar/atrium)
-"veM" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"veP" = (
/turf/simulated/wall,
/area/station/legal/courtroom/gallery)
@@ -92415,15 +92239,6 @@
icon_state = "whitebluefull"
},
/area/station/medical/storage)
-"vON" = (
-/obj/machinery/shower{
- dir = 4
- },
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
- },
-/area/station/security/permabrig)
"vOR" = (
/obj/machinery/atmospherics/unary/vent_pump/on,
/turf/simulated/floor/plasteel{
@@ -94299,7 +94114,6 @@
"wAW" = (
/obj/structure/sink/directional/east,
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/apmaint)
"wBf" = (
@@ -97528,7 +97342,6 @@
dir = 6
},
/obj/effect/spawner/random/blood/maybe,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/fsmaint)
"xLj" = (
@@ -113085,14 +112898,14 @@ aEE
aFS
aHh
gnV
-fMK
+ngO
aVa
jgi
krJ
aGn
aPW
aQb
-aRg
+aHS
aHl
aGn
rPS
@@ -114625,11 +114438,11 @@ bat
aDA
aEH
aEJ
-aJY
+aHl
aIK
aGn
aHS
-mHb
+miW
aGn
aKW
aRp
@@ -114657,7 +114470,7 @@ blM
bvq
cho
iDG
-cvl
+jqo
wHo
pwm
ebP
@@ -117198,7 +117011,7 @@ oxe
wwc
uJB
uKQ
-sAy
+jjl
aHS
aHS
aHl
@@ -117259,7 +117072,7 @@ puo
vKe
fGI
bCf
-qCJ
+kwZ
dTL
cwx
cyc
@@ -117444,7 +117257,7 @@ oBb
rAe
luh
lEA
-vON
+luh
xPE
aaa
aaa
@@ -118228,7 +118041,7 @@ aaa
aaa
aII
dEG
-aRg
+aHS
aIx
aJX
nIM
@@ -119287,7 +119100,7 @@ bnK
dCN
pvp
blQ
-bKH
+bnK
bxb
bIX
bQf
@@ -119761,7 +119574,7 @@ ayx
gMl
kkv
aEO
-koT
+fMM
avi
aEO
sFE
@@ -121120,7 +120933,7 @@ kdL
qwJ
cgQ
vRG
-clF
+rhG
vyw
sJC
rMq
@@ -121371,7 +121184,7 @@ ctN
cMr
gYs
reE
-qyy
+cqs
gwb
xab
cgQ
@@ -122401,7 +122214,7 @@ cyg
cgQ
kyh
mCA
-gUp
+gfC
egK
cgQ
coL
@@ -123732,7 +123545,7 @@ rix
wHB
rPd
mms
-nMz
+qEo
bFN
rzy
pUp
@@ -136449,7 +136262,7 @@ rlu
qFK
xHC
pTX
-hwW
+pTX
pTX
rlu
qom
@@ -140168,7 +139981,7 @@ xlg
sDr
chf
csK
-pnE
+cyJ
bDr
cyJ
cAe
@@ -140421,7 +140234,7 @@ eGC
sug
vbQ
wtp
-tkN
+wtp
pmR
cuc
csK
@@ -141715,7 +141528,7 @@ cep
cDm
mlC
cep
-nte
+cep
cAv
cep
czq
@@ -142741,12 +142554,12 @@ cyJ
uqE
qiB
chf
-jrA
+czq
eZo
qTA
eMZ
dbr
-pnE
+cyJ
sAB
czr
mgJ
@@ -143248,10 +143061,10 @@ cJK
cJK
gGQ
cxO
-nte
+cep
bop
mmB
-pnE
+cyJ
cyJ
xxd
chf
@@ -143487,7 +143300,7 @@ wyU
bGG
cJQ
cuQ
-pUR
+bcb
bGG
qZN
dQM
@@ -144287,7 +144100,7 @@ fbp
uGf
guQ
mrc
-iFW
+cIm
cep
cIm
chf
@@ -144537,7 +144350,7 @@ cyJ
hbP
cDm
vNo
-pnE
+cyJ
uqE
chf
ntr
@@ -145306,7 +145119,7 @@ vIW
oJr
cKF
fyM
-iBM
+csL
ciY
ciY
vMe
@@ -146584,7 +146397,7 @@ vum
ojz
dci
lon
-veM
+cga
xsx
reO
rCg
@@ -146598,7 +146411,7 @@ hyk
csL
ciY
vZV
-iBM
+csL
cHf
cBQ
aaa
@@ -147095,7 +146908,7 @@ osm
oJr
ksj
mdX
-veM
+cga
ciY
hWu
xvs
@@ -147515,7 +147328,7 @@ aab
aab
aAf
aBe
-jUn
+aVD
aBg
aBg
aFa
@@ -147607,7 +147420,7 @@ ciY
vBC
ePu
lcL
-lhl
+ijg
iPb
skT
xII
@@ -148649,7 +148462,7 @@ bVd
cga
ePu
ePu
-dkv
+csL
isD
csL
jPw
@@ -150135,7 +149948,7 @@ ciY
rng
csL
cga
-iBM
+csL
pZe
ciY
ciY
@@ -151703,7 +151516,7 @@ ciY
csL
fmn
qth
-iBM
+csL
oXK
isD
ciY
diff --git a/_maps/map_files220/stations/deltastation.dmm b/_maps/map_files220/stations/deltastation.dmm
index 167566e8a71c..1ef63a65f58b 100644
--- a/_maps/map_files220/stations/deltastation.dmm
+++ b/_maps/map_files220/stations/deltastation.dmm
@@ -10907,7 +10907,6 @@
/turf/simulated/floor/plating,
/area/station/maintenance/fore2)
"aWJ" = (
-/mob/living/simple_animal/mouse/rat,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/dust,
/turf/simulated/floor/wood/oak,
@@ -35601,14 +35600,6 @@
icon_state = "whiteyellow"
},
/area/station/medical/chemistry)
-"cRv" = (
-/obj/effect/spawner/random/blood/maybe,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- icon_state = "showroomfloor"
- },
-/area/station/maintenance/old_kitchen)
"cRy" = (
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 10
@@ -35663,7 +35654,6 @@
/area/station/maintenance/port)
"cRI" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "neutralcorner"
@@ -48628,7 +48618,6 @@
/area/station/maintenance/fsmaint)
"ekU" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "redyellowfull"
},
@@ -51603,10 +51592,6 @@
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/wood/fancy/cherry,
/area/station/public/vacant_store)
-"fhp" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/electrical_shop)
"fhy" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/crate,
@@ -52686,7 +52671,6 @@
"fyS" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/alarm/directional/south,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "greenblue"
},
@@ -52982,11 +52966,6 @@
icon_state = "whiteblue"
},
/area/station/medical/surgery/secondary)
-"fEd" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating,
-/area/station/maintenance/old_kitchen)
"fEi" = (
/obj/effect/turf_decal/delivery,
/obj/machinery/plantgenes,
@@ -54751,11 +54730,6 @@
/obj/effect/turf_decal/delivery/hollow,
/turf/simulated/floor/plasteel/dark,
/area/station/public/fitness)
-"giU" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/brown,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard2)
"giY" = (
/obj/structure/table,
/obj/machinery/computer/med_data/laptop{
@@ -57296,13 +57270,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/port)
-"gZU" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- icon_state = "neutralfull"
- },
-/area/station/maintenance/fore)
"haa" = (
/obj/structure/sink/directional/north,
/obj/effect/decal/cleanable/dirt,
@@ -57436,7 +57403,6 @@
/obj/structure/cable{
icon_state = "4-8"
},
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/transparent/glass/reinforced,
/area/station/maintenance/starboard2)
"hcm" = (
@@ -57486,14 +57452,6 @@
icon_state = "dark_large"
},
/area/station/security/interrogation)
-"hcY" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel{
- dir = 4;
- icon_state = "chapel"
- },
-/area/station/maintenance/starboard2)
"hda" = (
/obj/effect/turf_decal/stripes/white/line{
color = "lightblue"
@@ -59491,19 +59449,6 @@
icon_state = "whitepurplefull"
},
/area/station/science/lobby)
-"hJM" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
- },
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard2)
"hKc" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/door/airlock/maintenance,
@@ -61632,10 +61577,6 @@
/obj/effect/spawner/window/reinforced/grilled,
/turf/simulated/floor/plating,
/area/station/security/prisonershuttle)
-"irV" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/abandonedbar)
"isc" = (
/obj/structure/statue/delta/se,
/obj/effect/turf_decal/siding/yellow/end{
@@ -65353,7 +65294,6 @@
dir = 4
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plating,
/area/station/maintenance/abandoned_garden)
"jym" = (
@@ -67305,10 +67245,6 @@
},
/turf/simulated/floor/wood/oak,
/area/station/medical/psych)
-"kcd" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/old_kitchen)
"kcj" = (
/obj/machinery/alarm/directional/east,
/turf/simulated/floor/plasteel{
@@ -68235,15 +68171,6 @@
},
/turf/simulated/floor/carpet,
/area/station/service/library)
-"ksQ" = (
-/obj/machinery/door_control/shutter/west{
- id = "restaurant";
- name = "Restaurant Shutters control"
- },
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plating,
-/area/station/maintenance/old_kitchen)
"ksS" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/chair{
@@ -74538,7 +74465,6 @@
/obj/effect/spawner/random/blood/maybe,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/light/small/directional/west,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -75927,17 +75853,6 @@
},
/turf/simulated/floor/plating,
/area/station/command/bridge)
-"mTK" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/obj/structure/disposalpipe/segment,
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/transparent/glass/reinforced,
-/area/station/maintenance/starboard2)
"mTQ" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -76577,13 +76492,6 @@
icon_state = "yellow"
},
/area/station/engineering/hallway)
-"ndH" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/gray,
-/turf/simulated/floor/plasteel{
- icon_state = "redyellowfull"
- },
-/area/station/maintenance/old_kitchen)
"ndS" = (
/obj/structure/shelf/security,
/obj/effect/turf_decal/delivery/red/hollow,
@@ -77338,10 +77246,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel,
/area/station/supply/sorting)
-"noV" = (
-/mob/living/simple_animal/mouse/brown,
-/turf/simulated/floor/plating,
-/area/station/maintenance/old_kitchen)
"noW" = (
/obj/structure/grille/broken,
/turf/simulated/floor/engine,
@@ -84469,7 +84373,6 @@
dir = 4
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -84961,23 +84864,6 @@
},
/turf/simulated/floor/wood/oak,
/area/station/maintenance/library)
-"pCj" = (
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 4
- },
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/aft)
"pCm" = (
/obj/structure/displaycase/labcage,
/obj/effect/turf_decal/delivery,
@@ -88336,7 +88222,6 @@
/area/station/hallway/primary/central)
"qFp" = (
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "showroomfloor"
},
@@ -88487,12 +88372,6 @@
icon_state = "neutralfull"
},
/area/station/hallway/primary/central)
-"qHN" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/white,
-/obj/effect/mapping_helpers/turfs/damage,
-/turf/simulated/floor/wood/oak,
-/area/station/maintenance/old_kitchen)
"qHO" = (
/obj/structure/bed{
dir = 1
@@ -92731,7 +92610,6 @@
"rXZ" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/light/small/nightshifted/south,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel,
/area/station/maintenance/abandoned_garden)
"rYh" = (
@@ -94315,11 +94193,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/engineering/mechanic)
-"swI" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel/dark,
-/area/station/maintenance/abandonedbar)
"swM" = (
/obj/structure/disposalpipe/junction/reversed{
dir = 4
@@ -96309,7 +96182,6 @@
icon_state = "1-4"
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/engine,
/area/station/maintenance/incinerator)
"tcZ" = (
@@ -97130,7 +97002,6 @@
/area/station/maintenance/fsmaint)
"toU" = (
/obj/item/storage/box/donkpockets,
-/mob/living/simple_animal/mouse/gray,
/turf/simulated/floor/plasteel{
icon_state = "redyellowfull"
},
@@ -97241,11 +97112,6 @@
icon_state = "neutralfull"
},
/area/station/maintenance/virology_maint)
-"tqO" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/old_kitchen)
"tqS" = (
/obj/structure/table,
/obj/machinery/kitchen_machine/microwave{
@@ -102611,7 +102477,6 @@
dir = 4
},
/obj/structure/sink/kitchen/north,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/plasteel{
icon_state = "redyellowfull"
},
@@ -103144,7 +103009,6 @@
icon_state = "0-8"
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/turf/simulated/floor/engine,
/area/station/maintenance/incinerator)
"vmP" = (
@@ -103864,10 +103728,6 @@
/obj/effect/spawner/window/reinforced/polarized/grilled,
/turf/simulated/floor/plating,
/area/station/public/vacant_office/secondary)
-"vBV" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fore)
"vCf" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -104340,7 +104200,6 @@
/obj/structure/cable{
icon_state = "4-8"
},
-/mob/living/simple_animal/mouse/gray,
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
@@ -108221,7 +108080,6 @@
icon_state = "1-4"
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plasteel{
icon_state = "neutral"
},
@@ -109291,10 +109149,6 @@
icon_state = "whitebluecorner"
},
/area/station/medical/medbay2)
-"xnq" = (
-/mob/living/simple_animal/mouse/rat,
-/turf/simulated/floor/plasteel/dark,
-/area/station/maintenance/abandonedbar)
"xnx" = (
/turf/simulated/floor/plasteel{
dir = 6;
@@ -110505,7 +110359,6 @@
dir = 4
},
/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse/rat,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
@@ -110975,7 +110828,6 @@
/obj/structure/cable{
icon_state = "0-2"
},
-/mob/living/simple_animal/mouse/rat,
/obj/machinery/light/small/directional/west,
/turf/simulated/floor/wood/oak,
/area/station/maintenance/old_kitchen)
@@ -129328,7 +129180,7 @@ rkv
dsr
cLz
rQi
-swI
+dnH
dcX
yeh
dYu
@@ -129337,7 +129189,7 @@ lTi
lTi
dYt
dnH
-swI
+dnH
dcX
dkr
dbz
@@ -130360,7 +130212,7 @@ uai
lTi
mAs
dcX
-swI
+dnH
yeh
dks
dcX
@@ -131650,7 +131502,7 @@ dja
iFl
lTi
dmQ
-irV
+lTi
dcX
drf
irt
@@ -131898,7 +131750,7 @@ doX
xsz
cLz
dan
-xnq
+dcX
dcW
dcX
dcX
@@ -140023,7 +139875,7 @@ abj
apG
aqy
arr
-fhp
+ars
atg
auk
apG
@@ -140293,7 +140145,7 @@ asq
aVK
nQL
azb
-gZU
+ayX
oDU
aCl
aCl
@@ -146194,7 +146046,7 @@ aqQ
aoo
azb
hoG
-vBV
+aor
tGN
oOi
fwQ
@@ -152990,7 +152842,7 @@ raz
klA
duk
jDc
-pCj
+dDk
nHK
dEo
abj
@@ -157120,7 +156972,7 @@ pnl
rzk
lCR
dqP
-mTK
+dqP
wWV
dfQ
hkN
@@ -160467,7 +160319,7 @@ jyi
tpc
jyJ
wGh
-hJM
+kwD
vzp
wwk
dpt
@@ -163035,7 +162887,7 @@ bws
qVD
prg
cqI
-giU
+cqI
cqI
xaX
tNU
@@ -164324,7 +164176,7 @@ bgs
adA
mfI
eAb
-hcY
+mfI
eAb
bws
aaa
@@ -165568,7 +165420,7 @@ tyW
lja
uVh
pKe
-qHN
+czX
uRC
kAm
rad
@@ -165578,7 +165430,7 @@ niP
sIu
wOg
pAx
-cRv
+wOg
gAT
sIu
rMZ
@@ -166081,12 +165933,12 @@ qpB
bfK
aXM
kAm
-noV
+uYi
nwu
wPb
qno
lHO
-ksQ
+pfl
kAm
txG
sIu
@@ -166607,7 +166459,7 @@ sIu
vdF
fZf
fzE
-ndH
+fzE
fzE
jEH
uDO
@@ -167885,7 +167737,7 @@ maL
gPu
pgX
rtS
-fEd
+qno
xRG
kAm
wZj
@@ -167894,7 +167746,7 @@ ipf
qvA
fzE
uYi
-tqO
+qno
wIZ
qPD
abj
@@ -168142,7 +167994,7 @@ maL
maL
ddo
kue
-kcd
+uYi
jQi
azo
iHc
diff --git a/_maps/map_files220/stations/metastation.dmm b/_maps/map_files220/stations/metastation.dmm
index b465d5671234..c851dcffbd31 100644
--- a/_maps/map_files220/stations/metastation.dmm
+++ b/_maps/map_files220/stations/metastation.dmm
@@ -935,11 +935,6 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/solar_maintenance/fore_port)
-"ajD" = (
-/mob/living/simple_animal/mouse,
-/obj/effect/decal/cleanable/dirt,
-/turf/simulated/floor/catwalk,
-/area/station/maintenance/fore)
"ajE" = (
/obj/structure/cable/yellow{
icon_state = "0-4"
@@ -12523,7 +12518,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
-/mob/living/simple_animal/mouse,
/obj/structure/cable/yellow{
icon_state = "1-4"
},
@@ -15538,10 +15532,6 @@
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/plating,
/area/station/maintenance/fpmaint)
-"bmA" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fpmaint)
"bmB" = (
/obj/machinery/door/airlock,
/obj/effect/mapping_helpers/airlock/autoname,
@@ -15769,11 +15759,6 @@
icon_state = "cautioncorner"
},
/area/station/hallway/primary/starboard)
-"bng" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/starboard2)
"bnh" = (
/obj/machinery/firealarm/directional/north,
/obj/machinery/light/directional/north,
@@ -27438,13 +27423,6 @@
icon_state = "greenblue"
},
/area/station/maintenance/starboard)
-"cbb" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/atmospherics/pipe/simple/hidden/cyan,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fsmaint)
"cbc" = (
/obj/effect/spawner/random/fungus/maybe,
/turf/simulated/wall/r_wall,
@@ -30341,11 +30319,6 @@
/obj/structure/lattice,
/turf/space,
/area/space/nearstation)
-"clJ" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/turbine)
"clK" = (
/obj/structure/closet,
/obj/item/extinguisher,
@@ -37672,10 +37645,6 @@
},
/turf/simulated/floor/plating/airless,
/area/station/science/toxins/test)
-"cOU" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/port)
"cOV" = (
/obj/machinery/button/windowtint/south{
id = "CHAP2"
@@ -38015,7 +37984,6 @@
"cQt" = (
/obj/effect/mapping_helpers/turfs/burn,
/obj/machinery/light/small/directional/south,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/medmaint)
"cQu" = (
@@ -40301,10 +40269,6 @@
icon_state = "whiteblue"
},
/area/station/medical/exam_room)
-"dbH" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/aft)
"dbI" = (
/obj/effect/spawner/random/maintenance,
/turf/simulated/floor/plating,
@@ -47343,10 +47307,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/hallway/secondary/exit)
-"gof" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/apmaint)
"goq" = (
/obj/structure/closet/secure_closet/hos,
/obj/item/cartridge/detective,
@@ -48647,11 +48607,6 @@
icon_state = "white"
},
/area/station/science/robotics)
-"gWz" = (
-/obj/effect/decal/cleanable/dirt,
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fsmaint)
"gWE" = (
/obj/structure/table/reinforced,
/obj/effect/turf_decal/delivery/hollow,
@@ -50875,10 +50830,6 @@
icon_state = "whitepurple"
},
/area/station/science/rnd)
-"hYD" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/asmaint)
"hYL" = (
/obj/machinery/light/directional/east,
/turf/simulated/floor/plasteel{
@@ -51627,10 +51578,6 @@
},
/turf/simulated/floor/engine,
/area/station/engineering/control)
-"isf" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fsmaint)
"isB" = (
/obj/machinery/door/airlock/engineering/glass{
autoclose = 0;
@@ -68497,7 +68444,6 @@
},
/area/station/security/interrogation)
"qRm" = (
-/mob/living/simple_animal/mouse,
/obj/effect/spawner/random/oil/maybe,
/turf/simulated/floor/plasteel,
/area/station/maintenance/fore)
@@ -69623,7 +69569,6 @@
/area/station/security/armory)
"ruY" = (
/obj/machinery/light/small/directional/east,
-/mob/living/simple_animal/mouse,
/turf/simulated/floor/plating,
/area/station/maintenance/starboard2)
"ruZ" = (
@@ -76863,7 +76808,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
-/mob/living/simple_animal/mouse,
/obj/machinery/atmospherics/pipe/simple/hidden/cyan{
dir = 4
},
@@ -79539,10 +79483,6 @@
/obj/structure/sign/biohazard,
/turf/simulated/wall/r_wall,
/area/station/science/misc_lab)
-"wLB" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plating,
-/area/station/maintenance/fore)
"wLY" = (
/obj/effect/landmark/start/prisoner,
/turf/simulated/floor/plasteel,
@@ -82515,10 +82455,6 @@
icon_state = "caution"
},
/area/station/engineering/atmos/distribution)
-"yiP" = (
-/mob/living/simple_animal/mouse,
-/turf/simulated/floor/plasteel,
-/area/station/maintenance/fore)
"yiU" = (
/turf/simulated/floor/plasteel{
icon_state = "dark"
@@ -95340,7 +95276,7 @@ qCK
aOB
aOB
adY
-bmA
+adY
aOB
aLF
aML
@@ -99003,7 +98939,7 @@ cpD
crG
bZP
clK
-gof
+crG
crG
bZP
aaa
@@ -100778,7 +100714,7 @@ cdT
cdT
cdT
cdT
-cOU
+bTY
xOy
aSM
aSM
@@ -101501,7 +101437,7 @@ awh
hka
aKd
adY
-bmA
+adY
aOB
bmK
bgA
@@ -102110,7 +102046,7 @@ chy
xxL
eJG
czw
-dbH
+chy
bZU
abq
aaa
@@ -114587,7 +114523,7 @@ tNe
tNe
tNe
aDY
-wLB
+alW
cnV
nVQ
whf
@@ -115699,7 +115635,7 @@ rJq
cwG
cwG
cwG
-bng
+kqi
nUa
kvG
cwW
@@ -115892,7 +115828,7 @@ nSs
acL
aWv
aFj
-yiP
+fpy
bne
ajg
iGR
@@ -117756,7 +117692,7 @@ cwG
cte
aYL
nUa
-bng
+kqi
cmB
cmB
qub
@@ -118544,7 +118480,7 @@ flh
xHr
emj
emj
-hYD
+emj
cKJ
cPc
emj
@@ -120023,7 +119959,7 @@ aBI
bNY
aBI
apL
-isf
+avA
eiI
aLl
bdt
@@ -120758,7 +120694,7 @@ aaa
aaa
aaa
ahH
-ajD
+oTt
azu
oWE
air
@@ -120783,7 +120719,7 @@ gip
tmM
blB
wYm
-cbb
+blB
beF
juq
apV
@@ -121288,7 +121224,7 @@ air
oWE
iAS
oyN
-gWz
+cxr
hgU
aLl
fpk
@@ -122376,7 +122312,7 @@ cfL
ciu
ciH
aRQ
-clJ
+aRQ
biB
bul
clB
diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm
index 083abd4108ef..f80661688148 100644
--- a/code/__DEFINES/_globals.dm
+++ b/code/__DEFINES/_globals.dm
@@ -16,23 +16,41 @@
#define GLOBAL_PROTECT(X)
#endif
+/// Standard BYOND global, seriously do not use without an earthshakingly good reason
#define GLOBAL_REAL_VAR(X) var/global/##X
+
+/// Standard typed BYOND global, seriously do not use without an earthshakingly good reason
#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X
+/// Defines a global var on the controller, do not use
#define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X
+/// Create an untyped global with an initializer expression
#define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue)
+/// Create a global const var, do not use
#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X)
+/// Create a list global with an initializer expression
#define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue)
+/// Create a list global that is initialized as an empty list
#define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list())
+/// Create a typed list global with an initializer expression
+#define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue)
+
+/// Create a typed list global that is initialized as an empty list
+#define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list())
+
+/// Create a typed global with an initializer expression
#define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue)
+/// Create an untyped null global
#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X)
+/// Create a null global list
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X)
+/// Create a typed null global
#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
diff --git a/code/__DEFINES/dna.dm b/code/__DEFINES/dna.dm
index 6f6b03b29ad8..39cd592983d0 100644
--- a/code/__DEFINES/dna.dm
+++ b/code/__DEFINES/dna.dm
@@ -69,3 +69,5 @@
#define DNA_GENDER_FEMALE 0
#define DNA_GENDER_MALE 1
#define DNA_GENDER_PLURAL 2
+
+#define ENCODE_DNA_BLOCK(value) num2hex(value, 3)
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 08c5087b4b09..b1ecfca2fe3d 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -110,9 +110,10 @@
#define HAS_ALT_HEADS (1<<11)
#define HAS_WING (1<<12)
#define HAS_BODYACC_COLOR (1<<13)
-#define BALD (1<<14)
-#define ALL_RPARTS (1<<15)
-#define SHAVED (1<<16)
+#define HAS_SPECIES_SUBTYPE (1<<14)
+#define BALD (1<<15)
+#define ALL_RPARTS (1<<16)
+#define SHAVED (1<<17)
//Pre-baked combinations of the above body flags
#define HAS_BODY_ACCESSORY (HAS_TAIL | HAS_WING)
@@ -144,12 +145,18 @@
#define PASSTAKE (1<<9)
#define PASSBARRICADE (1<<10)
-//turf-only flags
+//turf-only flags, under the flags variable
#define BLESSED_TILE (1<<0)
#define NO_LAVA_GEN (1<<1) //Blocks lava rivers being generated on the turf
#define NO_RUINS (1<<2)
#define LAVA_BRIDGE (1<<3) //! This turf has already been reserved for a lavaland bridge placement.
+// turf flags, under the turf_flags variable
+/// If a turf is an unused reservation turf awaiting assignment
+#define UNUSED_RESERVATION_TURF (1<<4)
+/// If a turf is a reserved turf
+#define RESERVATION_TURF (1<<5)
+
//ORGAN TYPE FLAGS
#define AFFECT_ROBOTIC_ORGAN 1
#define AFFECT_ORGANIC_ORGAN 2
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 76332f7fb738..a6b8da39714b 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -80,5 +80,7 @@
#define DYNAMIC_RULESET_BANNED "Banned"
#define RULESET_FAILURE_BUDGET "Not enough budget"
+#define RULESET_FAILURE_ANTAG_BUDGET "Not enough antag budget"
#define RULESET_FAILURE_NO_PLAYERS "No drafted players"
+#define RULESET_FAILURE_MUTUAL_RULESET "No banned mutual rulesets"
#define RULESET_FAILURE_CHANGELING_SECONDARY_RULESET "Needs a secondary ruleset in rotation"
diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm
index 227cae67ed84..10d7fab996aa 100644
--- a/code/__DEFINES/misc_defines.dm
+++ b/code/__DEFINES/misc_defines.dm
@@ -431,7 +431,7 @@
#define INVESTIGATE_HOTMIC "hotmic"
// The SQL version required by this version of the code
-#define SQL_VERSION 632207
+#define SQL_VERSION 642207
// Vending machine stuff
#define CAT_NORMAL (1<<0)
diff --git a/code/__DEFINES/rust.dm b/code/__DEFINES/rust.dm
index 6ac60f4de08e..a78d48c341f2 100644
--- a/code/__DEFINES/rust.dm
+++ b/code/__DEFINES/rust.dm
@@ -57,6 +57,8 @@
/proc/byondapi_stack_trace(err)
CRASH(err)
+// MARK: MILLA
+
/proc/milla_init_z(z)
return RUSTLIB_CALL(milla_initialize, z)
@@ -116,6 +118,11 @@
/proc/create_environment(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, temperature)
return RUSTLIB_CALL(milla_create_environment, oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, temperature)
+/proc/set_zlevel_freeze(z, bool_frozen)
+ return RUSTLIB_CALL(milla_set_zlevel_frozen, z, bool_frozen)
+
+// MARK: MapManip
+
/proc/mapmanip_read_dmm(mapname)
return RUSTLIB_CALL(mapmanip_read_dmm_file, mapname)
diff --git a/code/__DEFINES/shuttle_defines.dm b/code/__DEFINES/shuttle_defines.dm
index 43470086ce22..a656e661a9dd 100644
--- a/code/__DEFINES/shuttle_defines.dm
+++ b/code/__DEFINES/shuttle_defines.dm
@@ -14,3 +14,5 @@
#define SHUTTLE_DOCKER_LANDING_CLEAR 1
#define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2
#define SHUTTLE_DOCKER_BLOCKED 3
+
+#define SHUTTLE_TRANSIT_BORDER 16
diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm
index f383b4d5540c..f3149bb42aaa 100644
--- a/code/__DEFINES/turfs.dm
+++ b/code/__DEFINES/turfs.dm
@@ -13,3 +13,10 @@
#define MINERAL_PREVENT_DIG 0 //! A mineral turf should not be changed when mined.
#define MINERAL_ALLOW_DIG 1 //! A mineral turf should be dug out when mined.
+
+/// Returns an outline (neighboring turfs) of the given block
+#define CORNER_OUTLINE(corner, width, height) ( \
+ CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \
+ CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \
+ CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \
+ CORNER_BLOCK_OFFSET(corner, 1, height, width, 0))
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 47947cb245f1..8ff538f3d37f 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -740,6 +740,19 @@
LAZYINITLIST(lazy_list[key]); \
lazy_list[key] |= value;
+///Ensures the length of a list is at least I, prefilling it with V if needed. if V is a proc call, it is repeated for each new index so that list() can just make a new list for each item.
+#define LISTASSERTLEN(L, I, V...) \
+ if(length(L) < I) { \
+ var/_OLD_LENGTH = length(L); \
+ L.len = I; \
+ /* Convert the optional argument to a if check */ \
+ for(var/_USELESS_VAR in list(V)) { \
+ for(var/_INDEX_TO_ASSIGN_TO in _OLD_LENGTH+1 to I) { \
+ L[_INDEX_TO_ASSIGN_TO] = V; \
+ } \
+ } \
+ }
+
//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
diff --git a/code/__HELPERS/name_helpers.dm b/code/__HELPERS/name_helpers.dm
index 8ae3763c85d7..e270b5ff6cc1 100644
--- a/code/__HELPERS/name_helpers.dm
+++ b/code/__HELPERS/name_helpers.dm
@@ -89,7 +89,7 @@ GLOBAL_VAR(syndicate_name)
// Full
if(prob(60))
- name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive")
+ name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Interstellar", "Interplanetary", "Foundation", "Positronics", "Hive")
// Broken
else
name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive")
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 0990e5bd29c6..6731133b6fbb 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -14,16 +14,17 @@
#ifdef LOCAL_GAME_TESTS
#define GAME_TESTS
-#endif
-
-#ifdef CIBUILDING
-#define GAME_TESTS
+#define MAP_TESTS
#endif
#if defined(CIBUILDING) && defined(LOCAL_GAME_TESTS)
#error CIBUILDING and LOCAL_GAME_TESTS should not be enabled at the same time!
#endif
+#if defined(GAME_TESTS) || defined(MAP_TESTS)
+#define TEST_RUNNER
+#endif
+
/***** All toggles for the GC ref finder *****/
// #define REFERENCE_TRACKING // Uncomment to enable ref finding
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index e165eabdb705..3f1de6a67fde 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -112,6 +112,11 @@ DEFINE_BITFIELD(flags, list(
"NO_SCREENTIPS" = NO_SCREENTIPS,
))
+DEFINE_BITFIELD(turf_flags, list(
+ "UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF,
+ "RESERVATION_TURF" = RESERVATION_TURF
+))
+
DEFINE_BITFIELD(flags_2, list(
"SLOWS_WHILE_IN_HAND_2" = SLOWS_WHILE_IN_HAND_2,
"NO_EMP_WIRES_2" = NO_EMP_WIRES_2,
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index efa225f48b7c..66ce6b862f19 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(hierophant_walls)
GLOBAL_LIST_EMPTY(pandemics)
GLOBAL_LIST_EMPTY(all_areas)
-GLOBAL_LIST_EMPTY(all_unique_areas) // List of all unique areas. AKA areas with there_can_be_many = FALSE
+GLOBAL_LIST_EMPTY_TYPED(all_unique_areas, /area) // List of all unique areas. AKA areas with there_can_be_many = FALSE
GLOBAL_LIST_EMPTY(machines)
GLOBAL_LIST_EMPTY(telescreens) /// List of entertainment telescreens connected to the "news" cameranet
GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm
index 8619c03c26cc..1d11623e9599 100644
--- a/code/controllers/configuration/configuration_core.dm
+++ b/code/controllers/configuration/configuration_core.dm
@@ -139,13 +139,12 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
safe_load(asset_cache, "asset_cache_configuration")
// Proc to load up instance-specific overrides
-/datum/server_configuration/proc/load_overrides()
- var/override_file = "config/overrides_[world.port].toml"
+/datum/server_configuration/proc/load_overrides(override_file)
if(!fexists(override_file))
- DIRECT_OUTPUT(world.log, "Overrides not found for this instance.")
+ DIRECT_OUTPUT(world.log, "Override file [override_file] not found for this instance.")
return
- DIRECT_OUTPUT(world.log, "Overrides found for this instance. Loading them.")
+ DIRECT_OUTPUT(world.log, "Override file [override_file] found. Loading.")
var/start = start_watch() // Time tracking
raw_data = rustg_read_toml_file(override_file)
diff --git a/code/controllers/configuration/sections/database_configuration.dm b/code/controllers/configuration/sections/database_configuration.dm
index 73a396c9e29e..94f323ef6539 100644
--- a/code/controllers/configuration/sections/database_configuration.dm
+++ b/code/controllers/configuration/sections/database_configuration.dm
@@ -21,16 +21,6 @@
var/async_thread_limit = 50
/datum/configuration_section/database_configuration/load_data(list/data)
- // UNIT TESTS ARE DEFINED - USE CUSTOM CI VALUES
- #ifdef GAME_TESTS
-
- enabled = TRUE
- // This needs to happen in the CI environment to ensure the example SQL version gets updated.
- CONFIG_LOAD_NUM(version, data["sql_version"])
-
- #else
- // Load the normal config. Were not in CI mode
- // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enabled, data["sql_enabled"])
CONFIG_LOAD_NUM(version, data["sql_version"])
CONFIG_LOAD_STR(address, data["sql_address"])
@@ -40,4 +30,3 @@
CONFIG_LOAD_STR(db, data["sql_database"])
CONFIG_LOAD_NUM(async_query_timeout, data["async_query_timeout"])
CONFIG_LOAD_NUM(async_thread_limit, data["async_thread_limit"])
- #endif
diff --git a/code/controllers/configuration/sections/redis_configuration.dm b/code/controllers/configuration/sections/redis_configuration.dm
index b418441e7f99..dc25e88c69de 100644
--- a/code/controllers/configuration/sections/redis_configuration.dm
+++ b/code/controllers/configuration/sections/redis_configuration.dm
@@ -7,14 +7,5 @@
var/connstring = "redis://127.0.0.1/"
/datum/configuration_section/redis_configuration/load_data(list/data)
- // UNIT TESTS ARE DEFINED - USE CUSTOM CI VALUES
- #ifdef GAME_TESTS
-
- // enabled = TRUE
-
- #else
- // Load the normal config. Were not in CI mode
- // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enabled, data["redis_enabled"])
CONFIG_LOAD_STR(connstring, data["redis_connstring"])
- #endif
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index cf8763b32b68..ce39cfb87337 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -83,7 +83,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
/datum/controller/master/New()
if(!random_seed)
- #ifdef GAME_TESTS
+ #ifdef TEST_RUNNER
random_seed = 29051994
#else
random_seed = rand(1, 1e9)
diff --git a/code/controllers/subsystem/SSshuttles.dm b/code/controllers/subsystem/SSshuttles.dm
index 2065c39026f6..90368fbb8b0a 100644
--- a/code/controllers/subsystem/SSshuttles.dm
+++ b/code/controllers/subsystem/SSshuttles.dm
@@ -303,7 +303,7 @@ SUBSYSTEM_DEF(shuttle)
for(var/obj/machinery/requests_console/console in GLOB.allRequestConsoles)
if(console.department != "Cargo Bay")
continue
- console.createMessage("Messaging and Intergalactic Letters", "New Mail Crates ready to be ordered!", "A new mail crate is able to be shipped alongside your next orders!", RQ_NORMALPRIORITY)
+ console.createMessage("Nanotrasen Mail and Interstellar Logistics", "New Mail Crates ready to be ordered!", "A new mail crate is able to be shipped alongside your next orders!", RQ_NORMALPRIORITY)
if(!length(supply_shuttle_turfs))
for(var/turf/simulated/T in supply.areaInstance)
diff --git a/code/controllers/subsystem/SSticker.dm b/code/controllers/subsystem/SSticker.dm
index b51569cb54b2..8e923007cc01 100644
--- a/code/controllers/subsystem/SSticker.dm
+++ b/code/controllers/subsystem/SSticker.dm
@@ -366,10 +366,8 @@ SUBSYSTEM_DEF(ticker)
if(GLOB.configuration.general.enable_night_shifts)
SSnightshift.check_nightshift(TRUE)
- #ifdef GAME_TESTS
- // Run map tests first in case unit tests futz with map state
- GLOB.test_runner.RunMap()
- GLOB.test_runner.Run()
+ #ifdef TEST_RUNNER
+ GLOB.test_runner.RunAll()
#endif
// Do this 10 second after roundstart because of roundstart lag, and make it more visible
diff --git a/code/controllers/subsystem/SStimer.dm b/code/controllers/subsystem/SStimer.dm
index 9c9bbeed24a0..44191cb87fbd 100644
--- a/code/controllers/subsystem/SStimer.dm
+++ b/code/controllers/subsystem/SStimer.dm
@@ -134,6 +134,8 @@ SUBSYSTEM_DEF(timer)
callBack.InvokeAsync()
if(ctime_timer.flags & TIMER_LOOP)
+ if(QDELETED(ctime_timer))
+ continue
ctime_timer.spent = 0
ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait
BINARY_INSERT_TG(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY)
@@ -180,6 +182,8 @@ SUBSYSTEM_DEF(timer)
last_invoke_tick = world.time
if(timer.flags & TIMER_LOOP) // Prepare looping timers to re-enter the queue
+ if(QDELETED(timer))
+ continue
timer.spent = 0
timer.timeToRun = world.time + timer.wait
timer.bucketJoin()
diff --git a/code/controllers/subsystem/non_firing/SSlate_mapping.dm b/code/controllers/subsystem/non_firing/SSlate_mapping.dm
index 65679ca13781..db3111e9a41c 100644
--- a/code/controllers/subsystem/non_firing/SSlate_mapping.dm
+++ b/code/controllers/subsystem/non_firing/SSlate_mapping.dm
@@ -27,4 +27,45 @@ SUBSYSTEM_DEF(late_mapping)
var/duration = stop_watch(watch)
log_startup_progress("Generated [mgcount] mazes in [duration]s")
+ maintenance_mice()
+
GLOB.spawn_pool_manager.process_pools()
+
+/**
+ * Randomly spawns mice in maintenance instead of being purely fixed spawn points
+ */
+/datum/controller/subsystem/late_mapping/proc/maintenance_mice()
+ var/watch = start_watch()
+ log_startup_progress("Populating maintenance with mice...")
+
+ // Looking up for maintenance floors specifically as possible spawn points
+ var/list/maintenance_turfs = list()
+ for(var/area/station/maintenance/A in SSmapping.existing_station_areas)
+ for(var/turf/simulated/floor/F in A)
+ if(locate(/obj/structure/window) in F)
+ continue
+ maintenance_turfs.Add(F)
+
+ if(!length(maintenance_turfs))
+ log_debug("No valid turfs has been found for mice.")
+ return
+
+ // The ratio is based on turfs per mice. Using Boxstation as an example, it would average between 20 to 30 mice.
+ var/floor_tiles_per_one_mice = rand(125, 200)
+ var/mice_number = ceil(length(maintenance_turfs) / floor_tiles_per_one_mice)
+
+ // SS220 EDIT START - mouse diversity
+ var/static/list/mouse_pool = list(
+ /mob/living/simple_animal/mouse = 1,
+ /mob/living/simple_animal/mouse/brown = 1,
+ /mob/living/simple_animal/mouse/gray = 1,
+ /mob/living/simple_animal/mouse/white = 1,
+ /mob/living/simple_animal/mouse/rat = 1,
+ /mob/living/simple_animal/mouse/rat/white = 1,
+ )
+ for(var/i in 1 to mice_number)
+ var/mouse_type = pickweight(mouse_pool)
+ new mouse_type(pick_n_take(maintenance_turfs))
+ // SS220 EDIT END
+
+ log_debug("Spawned [mice_number] mice over in [stop_watch(watch)]s")
diff --git a/code/controllers/subsystem/non_firing/SSmapping.dm b/code/controllers/subsystem/non_firing/SSmapping.dm
index feee4abb3452..9d324ac50eba 100644
--- a/code/controllers/subsystem/non_firing/SSmapping.dm
+++ b/code/controllers/subsystem/non_firing/SSmapping.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING // 9
- flags = SS_NO_FIRE
+
/// What map datum are we using
var/datum/map/map_datum
/// What map will be used next round
@@ -31,6 +31,15 @@ SUBSYSTEM_DEF(mapping)
/// Ruin placement manager for lavaland levels.
var/datum/ruin_placer/lavaland/lavaland_ruins_placer
+ var/num_of_res_levels = 0
+ var/clearing_reserved_turfs = FALSE
+ var/list/datum/turf_reservations //list of turf reservations
+
+ var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
+ var/list/used_turfs = list() //list of turf = datum/turf_reservation
+ /// List of lists of turfs to reserve
+ var/list/lists_to_reserve = list()
+
// This has to be here because world/New() uses [station_name()], which looks this datum up
/datum/controller/subsystem/mapping/PreInit()
. = ..()
@@ -109,6 +118,8 @@ SUBSYSTEM_DEF(mapping)
// Makes a blank space level for the sake of randomness
GLOB.space_manager.add_new_zlevel("Empty Area", linkage = CROSSLINKED, traits = empty_z_traits)
+ // Add a reserved z-level
+ // add_reservation_zlevel() // CURRENTLY DISABLED, AS NOTHING USES IT. IF YOU WANT TO ADD LAZYLOADING TO ANYTHING, MAKE SURE TO REIMPLEMENT THIS
// Setup the Z-level linkage
GLOB.space_manager.do_transition_setup()
@@ -346,4 +357,120 @@ SUBSYSTEM_DEF(mapping)
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency station access", "disabled"))
/datum/controller/subsystem/mapping/Recover()
- flags |= SS_NO_INIT
+ num_of_res_levels = SSmapping.num_of_res_levels
+ clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
+ turf_reservations = SSmapping.turf_reservations
+ unused_turfs = SSmapping.unused_turfs
+ used_turfs = SSmapping.used_turfs
+ lists_to_reserve = SSmapping.lists_to_reserve
+
+/datum/controller/subsystem/mapping/proc/get_reservation_from_turf(turf/T)
+ RETURN_TYPE(/datum/turf_reservation)
+ return used_turfs[T]
+
+/// Requests a /datum/turf_reservation based on the given width, height.
+/datum/controller/subsystem/mapping/proc/request_turf_block_reservation(width, height)
+ UNTIL(!clearing_reserved_turfs)
+ log_debug("Reserving [width]x[height] turf reservation")
+ var/datum/turf_reservation/reserve = new /datum/turf_reservation
+ for(var/i in levels_by_trait(Z_FLAG_RESERVED))
+ if(reserve.reserve(width, height, i))
+ return reserve
+ //If we didn't return at this point, theres a good chance we ran out of room on the exisiting reserved z levels, so lets try a new one
+ var/z_level_num = add_reservation_zlevel()
+ if(reserve.reserve(width, height, z_level_num))
+ return reserve
+ qdel(reserve)
+
+/datum/controller/subsystem/mapping/proc/add_reservation_zlevel()
+ num_of_res_levels++
+ // . here is the z of the just added z-level
+ . = GLOB.space_manager.add_new_zlevel("Transit/Reserved #[num_of_res_levels]", traits = list(Z_FLAG_RESERVED, BLOCK_TELEPORT, IMPEDES_MAGIC))
+ initialize_reserved_level(.)
+ if(!initialized)
+ return
+ if(length(SSidlenpcpool.idle_mobs_by_zlevel) == . || !islist(SSidlenpcpool.idle_mobs_by_zlevel)) // arbitrary chosen from these lists that require the length of the z-levels
+ return
+ LISTASSERTLEN(SSidlenpcpool.idle_mobs_by_zlevel, ., list())
+ LISTASSERTLEN(SSmobs.clients_by_zlevel, ., list())
+ LISTASSERTLEN(SSmobs.dead_players_by_zlevel, ., list())
+
+///Sets up a z level as reserved
+///This is not for wiping reserved levels, use wipe_reservations() for that.
+///If this is called after SSatom init, it will call Initialize on all turfs on the passed z, as its name promises
+/datum/controller/subsystem/mapping/proc/initialize_reserved_level(z)
+ UNTIL(!clearing_reserved_turfs) //regardless, lets add a check just in case.
+ log_debug("Initializing new reserved Z-level")
+ clearing_reserved_turfs = TRUE //This operation will likely clear any existing reservations, so lets make sure nothing tries to make one while we're doing it.
+ if(!check_level_trait(z, Z_FLAG_RESERVED))
+ clearing_reserved_turfs = FALSE
+ CRASH("Invalid z level prepared for reservations.")
+ var/block = block(SHUTTLE_TRANSIT_BORDER, SHUTTLE_TRANSIT_BORDER, z, world.maxx - SHUTTLE_TRANSIT_BORDER, world.maxy - SHUTTLE_TRANSIT_BORDER)
+ for(var/turf/T as anything in block)
+ // No need to empty() these, because they just got created and are already /turf/space.
+ T.turf_flags |= UNUSED_RESERVATION_TURF
+ CHECK_TICK
+
+ // Gotta create these suckers if we've not done so already
+ if(SSatoms.initialized)
+ SSatoms.InitializeAtoms(block(1, 1, world.maxx, world.maxy, z), FALSE)
+
+ unused_turfs["[z]"] = block
+ clearing_reserved_turfs = FALSE
+
+/datum/controller/subsystem/mapping/fire(resumed)
+ // Cache for sonic speed
+ var/list/list/turf/unused_turfs = src.unused_turfs
+ var/list/world_contents = GLOB.all_unique_areas[world.area].contents
+ // var/list/world_turf_contents_by_z = GLOB.all_unique_areas[world.area].turfs_by_zlevel
+ var/list/lists_to_reserve = src.lists_to_reserve
+ var/index = 0
+ while(index < length(lists_to_reserve))
+ var/list/packet = lists_to_reserve[index + 1]
+ var/packetlen = length(packet)
+ while(packetlen)
+ if(MC_TICK_CHECK)
+ if(index)
+ lists_to_reserve.Cut(1, index)
+ return
+ var/turf/reserving_turf = packet[packetlen]
+ reserving_turf.empty(/turf/space)
+ LAZYINITLIST(unused_turfs["[reserving_turf.z]"])
+ if(!(reserving_turf in unused_turfs["[reserving_turf.z]"]))
+ unused_turfs["[reserving_turf.z]"].Insert(1, reserving_turf)
+ reserving_turf.turf_flags = UNUSED_RESERVATION_TURF
+
+ world_contents += reserving_turf
+ packet.len--
+ packetlen = length(packet)
+
+ index++
+ lists_to_reserve.Cut(1, index)
+
+/**
+ * Lazy loads a template on a lazy-loaded z-level
+ * If you want to use this as non-debug, make sure to uncomment add_reservation_zlevel in /datum/controller/subsystem/mapping/Initialize()
+ */
+/datum/controller/subsystem/mapping/proc/lazy_load_template(datum/map_template/template)
+ RETURN_TYPE(/datum/turf_reservation)
+
+ UNTIL(initialized)
+ var/static/lazy_loading = FALSE
+ UNTIL(!lazy_loading)
+
+ lazy_loading = TRUE
+ . = _lazy_load_template(template)
+ lazy_loading = FALSE
+
+/datum/controller/subsystem/mapping/proc/_lazy_load_template(datum/map_template/template)
+ PRIVATE_PROC(TRUE)
+ var/datum/turf_reservation/reservation = request_turf_block_reservation(template.width, template.height)
+ if(!istype(reservation))
+ return
+
+ template.load(reservation.bottom_left_turf)
+ return reservation
+
+/// Schedules a group of turfs to be handed back to the reservation system's control
+/datum/controller/subsystem/mapping/proc/unreserve_turfs(list/turfs)
+ lists_to_reserve += list(turfs)
diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm
index 60419bd5b25f..b1f56f177bec 100644
--- a/code/datums/diseases/advance/symptoms/hair.dm
+++ b/code/datums/diseases/advance/symptoms/hair.dm
@@ -37,13 +37,13 @@ BONUS
switch(A.stage)
if(1, 2, 3)
to_chat(H, "Your scalp itches.")
- head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
else
to_chat(H, "Hair bursts forth from your scalp!")
var/datum/sprite_accessory/tmp_hair_style = GLOB.hair_styles_full_list["Very Long Hair"]
- if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them.
+ if(head_organ.dna.species.sprite_sheet_name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them.
head_organ.h_style = "Very Long Hair"
else //Otherwise, give them a random hair style.
- head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
H.update_hair()
diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm
index 08437daa6097..5858f9b16211 100644
--- a/code/datums/helper_datums/map_template.dm
+++ b/code/datums/helper_datums/map_template.dm
@@ -51,6 +51,9 @@
// if given a multi-z template
// it might need to be adapted for that when that time comes
GLOB.space_manager.add_dirt(placement.z)
+ var/datum/milla_safe/freeze_z_level/milla_freeze = new()
+ milla_freeze.invoke_async(T.z)
+ UNTIL(milla_freeze.done)
try
var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, shouldCropMap = TRUE)
if(!bounds)
diff --git a/code/datums/outfits/outfit_debug.dm b/code/datums/outfits/outfit_debug.dm
index 559c9b8b9a21..f4d2d5ae57f4 100644
--- a/code/datums/outfits/outfit_debug.dm
+++ b/code/datums/outfits/outfit_debug.dm
@@ -272,6 +272,12 @@
/obj/item/storage/box/debug/debugtools
name = "debug tools"
+/obj/item/paper/debug_research
+ name = "debug reseach notes"
+ desc = "Your brain is melting just from looking at this endless knowledge."
+ info = "Information written here is beyond your understanding"
+ origin_tech = "materials=10;engineering=10;plasmatech=10;powerstorage=10;bluespace=10;biotech=10;combat=10;magnets=10;programming=10;toxins=10;syndicate=10;abductor=10"
+
/obj/item/storage/box/debug/debugtools/populate_contents()
new /obj/item/card/emag(src)
new /obj/item/rcd/combat/admin(src)
@@ -284,6 +290,7 @@
new /obj/item/storage/box/debug/misc_debug(src)
new /obj/item/storage/box/centcomofficer(src)
new /obj/item/radio/uplink/admin(src)
+ new /obj/item/paper/debug_research(src)
/obj/item/storage/box/debug/material
name = "box of materials"
diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm
index 165ba07208ef..6fa785eb31aa 100644
--- a/code/datums/spells/banana_touch.dm
+++ b/code/datums/spells/banana_touch.dm
@@ -72,3 +72,37 @@
if(!(iswizard(src) || (mind && mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE))) //Mutations are permanent on non-wizards. Can still be removed by genetics fuckery but not mutadone.
dna.default_blocks.Add(GLOB.clumsyblock)
dna.default_blocks.Add(GLOB.comicblock)
+
+/// Like bananatouched, but intended to be used for funny one-off scenarios which aren't meant to permanently cripple someone's round.
+/mob/living/carbon/human/proc/bananatouched_harmless()
+ to_chat(src, "HONK")
+ Weaken(14 SECONDS)
+ Stuttering(30 SECONDS)
+ do_jitter_animation(30 SECONDS)
+
+ var/obj/item/tank/internal_tank = internal
+ var/obj/item/clothing/mask/gas/clown_hat/clown_mask = new()
+ var/obj/item/clothing/under/rank/civilian/clown/clown_suit = new()
+ var/obj/item/clothing/shoes/clown_shoes/clown_shoes = new()
+ clown_mask.flags |= DROPDEL
+ clown_suit.flags |= DROPDEL
+ clown_shoes.flags |= DROPDEL
+
+ drop_item_to_ground(shoes, force = TRUE)
+ drop_item_to_ground(wear_mask, force = TRUE)
+ drop_item_to_ground(w_uniform, force = TRUE)
+ if(isplasmaman(src))
+ drop_item_to_ground(head, force = TRUE)
+ var/obj/item/clothing/head/helmet/space/plasmaman/clown/clown_helmet = new()
+ clown_suit = new /obj/item/clothing/under/plasmaman/clown
+ clown_helmet.flags |= DROPDEL
+ clown_suit.flags |= DROPDEL
+ equip_to_slot_if_possible(clown_helmet, ITEM_SLOT_HEAD, TRUE, TRUE)
+ equip_to_slot_if_possible(clown_suit, ITEM_SLOT_JUMPSUIT, TRUE, TRUE)
+ equip_to_slot_if_possible(clown_shoes, ITEM_SLOT_SHOES, TRUE, TRUE)
+ equip_to_slot_if_possible(clown_mask, ITEM_SLOT_MASK, TRUE, TRUE)
+
+ // Re-equips the internal tank if present
+ equip_to_appropriate_slot(internal_tank)
+ internal = internal_tank
+
diff --git a/code/game/area/misc_areas.dm b/code/game/area/misc_areas.dm
index 1eda79444595..6e5df61490d7 100644
--- a/code/game/area/misc_areas.dm
+++ b/code/game/area/misc_areas.dm
@@ -64,3 +64,11 @@
/area/syndicate_mothership/jail
name = "\improper Syndicate Jail"
+
+/area/cordon
+ name = "CORDON"
+ icon_state = "cordon"
+ requires_power = FALSE
+ always_unpowered = TRUE
+ dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+ valid_territory = FALSE
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 341067b016cf..4bef9732f156 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -259,7 +259,7 @@ GLOBAL_LIST_EMPTY(bad_blocks)
// Get a hex-encoded UI block.
/datum/dna/proc/GetUIBlock(block)
- return EncodeDNABlock(GetUIValue(block))
+ return ENCODE_DNA_BLOCK(GetUIValue(block))
// Do not use this unless you absolutely have to.
// Set a block from a hex string. This is inefficient. If you can, use SetUIValue().
@@ -356,7 +356,7 @@ GLOBAL_LIST_EMPTY(bad_blocks)
// Get hex-encoded SE block.
/datum/dna/proc/GetSEBlock(block)
- return EncodeDNABlock(GetSEValue(block))
+ return ENCODE_DNA_BLOCK(GetSEValue(block))
// Do not use this unless you absolutely have to.
// Set a block from a hex string. This is inefficient. If you can, use SetUIValue().
@@ -387,15 +387,11 @@ GLOBAL_LIST_EMPTY(bad_blocks)
//testing("SetSESubBlock([block],[subBlock],[newSubBlock],[defer]): [oldBlock] -> [newBlock]")
SetSEBlock(block, newBlock, defer)
-
-/proc/EncodeDNABlock(value)
- return num2hex(value, 3)
-
/datum/dna/proc/UpdateUI()
var/list/ui_text_list = list()
uni_identity = ""
for(var/block in UI)
- ui_text_list += EncodeDNABlock(block)
+ ui_text_list += ENCODE_DNA_BLOCK(block)
uni_identity = ui_text_list.Join("")
//testing("New UI: [uni_identity]")
dirtyUI = 0
@@ -405,7 +401,7 @@ GLOBAL_LIST_EMPTY(bad_blocks)
var/list/se_text_list = list()
struc_enzymes = ""
for(var/block in SE)
- se_text_list += EncodeDNABlock(block)
+ se_text_list += ENCODE_DNA_BLOCK(block)
struc_enzymes = se_text_list.Join("")
//testing("Old SE: [oldse]")
//testing("New SE: [struc_enzymes]")
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 73973a1944db..202e491e1d8c 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -241,7 +241,7 @@
var/list/available = list()
for(var/head_accessory in GLOB.head_accessory_styles_list)
var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory]
- if(!(head_organ.dna.species.name in S.species_allowed)) //If the user's head is not of a species the head accessory style allows, skip it. Otherwise, add it to the list.
+ if(!(head_organ.dna.species.sprite_sheet_name in S.species_allowed)) //If the user's head is not of a species the head accessory style allows, skip it. Otherwise, add it to the list.
continue
available += head_accessory
var/list/sorted = sortTim(available, GLOBAL_PROC_REF(cmp_text_asc))
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 417a8c303ff6..92d13eda81bf 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -358,7 +358,7 @@
/obj/machinery/computer/scan_consolenew/proc/all_dna_blocks(list/buffer)
var/list/arr = list()
for(var/i = 1, i <= length(buffer), i++)
- arr += "[i]:[EncodeDNABlock(buffer[i])]"
+ arr += "[i]:[ENCODE_DNA_BLOCK(buffer[i])]"
return arr
/obj/machinery/computer/scan_consolenew/proc/setInjectorBlock(obj/item/dnainjector/I, blk, datum/dna2_record/buffer)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 62293adf6cbf..ce30a5aa50e9 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -42,7 +42,10 @@
/// If true, the species blacklist is now a species whitelist
var/banned_species_only = FALSE
- // var/list/banned_mutual_rulesets = list() // UNIMPLEMENTED: could be used to prevent nukies rolling while theres cultists, or wizards, etc
+ /// Rulesets that cannot be rolled while this ruleset is active. Used to prevent traitors from rolling while theres cultists, etc.
+ var/list/banned_mutual_rulesets = list(
+ /datum/ruleset/team/cult,
+ )
/* This stuff changes, all stuff above is static */
/// How many antagonists to spawn
@@ -56,11 +59,17 @@
stack_trace("[src] ([type]) was destroyed.")
return ..()
-/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets)
+/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets, antag_budget)
if(ruleset_budget < ruleset_cost)
return RULESET_FAILURE_BUDGET
+ if(antag_budget < antag_cost)
+ return RULESET_FAILURE_ANTAG_BUDGET
if(!length(SSticker.mode.get_players_for_role(antagonist_type::job_rank))) // this specifically needs to be job_rank not special_rank
return RULESET_FAILURE_NO_PLAYERS
+ if(length(banned_mutual_rulesets) && length(rulesets))
+ for(var/datum/ruleset/ruleset in rulesets)
+ if(ruleset.type in banned_mutual_rulesets)
+ return RULESET_FAILURE_MUTUAL_RULESET
/datum/ruleset/proc/antagonist_possible(budget)
return budget >= antag_cost
@@ -148,8 +157,8 @@
/datum/ruleset/proc/latespawn(datum/game_mode/dynamic/dynamic)
// latespawning is only used by traitors at this point, so we're just going to be naive and allocate all budget when this proc is called.
- var/late_antag_amount = floor(dynamic.budget_overflow / antag_cost)
- dynamic.budget_overflow -= (late_antag_amount * antag_cost)
+ var/late_antag_amount = floor(dynamic.antag_budget / antag_cost)
+ dynamic.antag_budget -= (late_antag_amount * antag_cost)
var/list/datum/mind/possible_antags = get_latejoin_players()
for(var/i in 1 to late_antag_amount)
@@ -158,6 +167,13 @@
log_dynamic("Latespawned [late_antag_amount] [name]s.")
+/datum/ruleset/proc/automatic_deduct(budget)
+ . = antag_cost * antag_amount
+ log_dynamic("Automatic deduction: +[antag_amount] [name]\s. Remaining budget: [budget - .].")
+
+/datum/ruleset/proc/declare_completion()
+ return
+
/datum/ruleset/traitor
name = "Traitor"
ruleset_weight = 11
@@ -205,6 +221,7 @@
// This is the fucking worst, but its required to not change functionality with mindflayers. Cannot be rolled normally, this is applied by other methods.
/datum/ruleset/implied
+ name = "BASE IMPLIED RULESET"
// These 3 variables should never change
ruleset_cost = 0
ruleset_weight = 0
@@ -234,3 +251,57 @@
implier.antag_amount -= 1
antag_amount += 1
was_triggered = TRUE
+
+/datum/ruleset/team
+ name = "BASE TEAM RULESET"
+ ruleset_weight = 0
+ /// Whether there should only be one of this kind of team. This could be used for blood-brothers if false.
+ var/unique_team = TRUE
+ /// How many players on a team.
+ var/team_size = 1
+ /// Team datum to create.
+ var/datum/team/team_type
+
+/datum/ruleset/team/roundstart_post_setup(datum/game_mode/dynamic)
+ if(unique_team)
+ new team_type(pre_antags)
+ return
+ stack_trace("Undefined behavior for dynamic non-unique teams!")
+
+/datum/ruleset/team/automatic_deduct(budget)
+ antag_amount = team_size
+ . = antag_cost
+ log_dynamic("Automatic deduction: +[antag_amount] [name]\s. Remaining budget: [budget - .].")
+
+/datum/ruleset/team/antagonist_possible(budget)
+ if(unique_team) // we're given our size at the start, no more please!
+ return FALSE
+ return ..()
+
+/datum/ruleset/team/cult
+ name = "Cultist"
+ ruleset_cost = 1
+ ruleset_weight = 3
+ // antag_weight doesnt matter, since we've already allocated our budget for 4 cultists only
+ antag_cost = 30
+ antagonist_type = /datum/antagonist/cultist
+ banned_mutual_rulesets = list(
+ /datum/ruleset/traitor,
+ /datum/ruleset/vampire,
+ /datum/ruleset/changeling
+ )
+ banned_jobs = list("Cyborg", "AI", "Chaplain", "Head of Personnel")
+
+ team_size = 4
+ team_type = /datum/team/cult
+
+/datum/ruleset/team/cult/declare_completion()
+ if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_RISEN)
+ SSticker.mode_result = "cult win - cult win"
+ to_chat(world, "The cult wins! It has succeeded in summoning [GET_CULT_DATA(entity_name, "their god")]!")
+ else if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_FALLEN)
+ SSticker.mode_result = "cult draw - narsie died, nobody wins"
+ to_chat(world, "Nobody wins! [GET_CULT_DATA(entity_name, "the cult god")] was summoned, but banished!")
+ else
+ SSticker.mode_result = "cult loss - staff stopped the cult"
+ to_chat(world, "The staff managed to stop the cult!")
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index d16bea196080..b75cdc5fca93 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -10,7 +10,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/list/datum/ruleset/implied_rulesets = list()
/// How much budget is left after roundstart antagonists roll
- var/budget_overflow = 0
+ var/antag_budget = 0
/// Log for what happens in a dynamic round
var/list/dynamic_log = list()
@@ -27,7 +27,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]")
/datum/game_mode/dynamic/proc/allocate_ruleset_budget()
- var/ruleset_budget = text2num(GLOB.dynamic_forced_rulesets["budget"] || pickweight(list("0" = 3, "1" = 5, "2" = 12, "3" = 3))) // more likely to or 2
+ var/ruleset_budget = text2num(GLOB.dynamic_forced_rulesets["budget"] || pickweight(list("0" = 3, "1" = 8, "2" = 12, "3" = 3)))
+ antag_budget = num_players()
log_dynamic("Allocated gamemode budget: [ruleset_budget]")
var/list/possible_rulesets = list()
for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
@@ -68,9 +69,9 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(!ruleset)
return
if(!force)
- var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets)
+ var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets, antag_budget)
if(failure_reason)
- log_dynamic("Failed [ruleset.name] ruleset: [failure_reason]")
+ log_dynamic("Failed [ruleset.name] ruleset: [failure_reason].")
return
log_dynamic("Rolled ruleset: [ruleset.name]")
rulesets[ruleset] = ruleset.antag_weight
@@ -89,33 +90,30 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(!length(rulesets))
log_dynamic("No rulesets in play.")
return
- var/budget = num_players()
- log_dynamic("Allocated antagonist budget: [budget].")
+ log_dynamic("Allocated antagonist budget: [antag_budget].")
for(var/datum/ruleset/ruleset in rulesets)
ruleset.antag_amount = 1
- budget -= ruleset.antag_cost
- log_dynamic("Automatic deduction: +1 [ruleset.name]. Remaining budget: [budget].")
+ antag_budget -= ruleset.automatic_deduct(antag_budget)
log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]")
- apply_antag_budget(budget)
+ apply_antag_budget()
-/datum/game_mode/dynamic/proc/apply_antag_budget(budget) // todo, can be called later in the game to apply more budget. That also means there has to be shit done for latejoins.
+/datum/game_mode/dynamic/proc/apply_antag_budget() // todo, can be called later in the game to apply more budget. That also means there has to be shit done for latejoins.
var/list/temp_rulesets = rulesets.Copy()
- while(budget >= 0)
+ while(antag_budget >= 0)
var/datum/ruleset/ruleset = pickweight(temp_rulesets)
if(!ruleset)
- log_dynamic("No rulesets remaining. Remaining budget: [budget].")
- budget_overflow = budget
+ log_dynamic("No rulesets remaining. Remaining budget: [antag_budget].")
return
- if(!ruleset.antagonist_possible(budget))
+ if(!ruleset.antagonist_possible(antag_budget))
log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.")
temp_rulesets -= ruleset
continue
ruleset.antag_amount++
- budget -= ruleset.antag_cost
- log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
+ antag_budget -= ruleset.antag_cost
+ log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [antag_budget].")
log_dynamic("No more antagonist budget remaining.")
/datum/game_mode/dynamic/pre_setup()
@@ -128,10 +126,10 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
log_dynamic("Applying [ruleset.antag_amount] [ruleset.name]\s.")
- budget_overflow += ruleset.roundstart_pre_setup()
+ antag_budget += ruleset.roundstart_pre_setup()
- log_dynamic("Budget overflow: [budget_overflow].")
- // for the future, maybe try readding antagonists with apply_antag_budget(budget_overflow)
+ log_dynamic("Budget overflow: [antag_budget].")
+ // for the future, maybe try readding antagonists with apply_antag_budget(antag_budget)
log_dynamic("Finished dynamic setup in [stop_watch(watch)]s.")
return TRUE
@@ -147,19 +145,19 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
/datum/game_mode/dynamic/latespawn(mob)
. = ..()
- budget_overflow++
+ antag_budget++
/datum/game_mode/dynamic/on_mob_cryo(mob/sleepy_mob, obj/machinery/cryopod/cryopod)
var/turf/T = get_turf(cryopod)
if(!T || is_admin_level(T.z))
return
- budget_overflow--
+ antag_budget--
if(!sleepy_mob.mind || !length(sleepy_mob.mind.antag_datums))
return
for(var/datum/antagonist/antag in sleepy_mob.mind.antag_datums)
for(var/datum/ruleset/possible_ruleset as anything in subtypesof(/datum/ruleset))
if(istype(antag, possible_ruleset.antagonist_type))
- budget_overflow += possible_ruleset.antag_cost
+ antag_budget += possible_ruleset.antag_cost
log_dynamic("[possible_ruleset] cryo. +[possible_ruleset.antag_cost] budget.")
/datum/game_mode/dynamic/get_webhook_name()
@@ -169,6 +167,11 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
implied_and_used += implied
return "[name] ([english_list(rulesets + implied_and_used, nothing_text = "Extended")])"
+/datum/game_mode/dynamic/declare_completion()
+ for(var/datum/ruleset/ruleset in rulesets)
+ ruleset.declare_completion()
+ . = ..()
+
/proc/log_dynamic(text)
log_game("Dynamic: [text]")
var/datum/game_mode/dynamic/dynamic = SSticker.mode
diff --git a/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm b/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
index f91f1369bc9c..9c3bc0521400 100644
--- a/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
+++ b/code/game/gamemodes/miniantags/tourist/tourist_arrivals.dm
@@ -118,8 +118,8 @@
head_organ.sec_hair_colour = hair_c
M.change_eye_color(eye_c)
M.s_tone = skin_tone
- head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name)
- head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name)
+ head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.sprite_sheet_name)
+ head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.sprite_sheet_name)
M.regenerate_icons()
M.update_body()
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index e6b8638e5f05..32d9950aa5a6 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -127,24 +127,28 @@ FIRE ALARM
/obj/machinery/firealarm/item_interaction(mob/living/user, obj/item/used, list/modifiers)
add_fingerprint(user)
- if(wiresexposed)
- if(buildstage == FIRE_ALARM_UNWIRED && istype(used, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/coil = used
- if(!coil.use(5))
- to_chat(user, "You need a total of five cables to wire [src]!")
- return ITEM_INTERACT_COMPLETE
-
- buildstage = FIRE_ALARM_READY
- playsound(get_turf(src), used.usesound, 50, 1)
- to_chat(user, "You wire [src]!")
- update_icon()
- else if(buildstage == FIRE_ALARM_FRAME && istype(used, /obj/item/firealarm_electronics))
- to_chat(user, "You insert the circuit!")
- qdel(used)
- buildstage = FIRE_ALARM_UNWIRED
- update_icon()
+ if(!wiresexposed)
+ return ..()
+
+ if(buildstage == FIRE_ALARM_UNWIRED && istype(used, /obj/item/stack/cable_coil))
+ var/obj/item/stack/cable_coil/coil = used
+ if(!coil.use(5))
+ to_chat(user, "You need a total of five cables to wire [src]!")
+ return ITEM_INTERACT_COMPLETE
+ buildstage = FIRE_ALARM_READY
+ playsound(get_turf(src), used.usesound, 50, TRUE)
+ to_chat(user, "You wire [src]!")
+ update_icon()
return ITEM_INTERACT_COMPLETE
+
+ if(buildstage == FIRE_ALARM_FRAME && istype(used, /obj/item/firealarm_electronics))
+ to_chat(user, "You insert the circuit!")
+ qdel(used)
+ buildstage = FIRE_ALARM_UNWIRED
+ update_icon()
+ return ITEM_INTERACT_COMPLETE
+
return ..()
/obj/machinery/firealarm/crowbar_act(mob/user, obj/item/I)
@@ -198,6 +202,7 @@ FIRE ALARM
return
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
+ update_icon()
WIRECUTTER_SNIP_MESSAGE
var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(drop_location())
new_coil.amount = 5
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index f6a7070cbfa2..0d7c47d3048a 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -49,7 +49,10 @@
. = ..()
. += "The power light is [(stat & NOPOWER) ? "off" : "on"]."
. += "The operation light is [emergency_mode ? "off. [src] has detected a forbidden object with its sensors, and has shut down temporarily." : "on. [src] is active."]"
- . += "The safety sensor light is [emagged ? "off!" : "on."]"
+ if(HAS_TRAIT(src, TRAIT_CMAGGED))
+ . += "The safety sensor light is RGB."
+ else
+ . += "The safety sensor light is [emagged ? "off!" : "on."]"
. += "The recycler current accepts items from [dir2text(eat_dir)]."
/obj/machinery/recycler/power_change()
@@ -77,9 +80,23 @@
if(default_unfasten_wrench(user, I, time = 6 SECONDS))
return TRUE
-
+/obj/machinery/recycler/cmag_act(mob/user)
+ if(emagged)
+ to_chat(user, "The board is completely fried.")
+ return FALSE
+ if(!HAS_TRAIT(src, TRAIT_CMAGGED))
+ ADD_TRAIT(src, TRAIT_CMAGGED, CLOWN_EMAG)
+ if(emergency_mode)
+ emergency_mode = FALSE
+ update_icon(UPDATE_ICON_STATE)
+ playsound(src, "sparks", 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ to_chat(user, "You use the jestographic sequencer on [src].")
+ return TRUE
/obj/machinery/recycler/emag_act(mob/user)
+ if(HAS_TRAIT(src, TRAIT_CMAGGED))
+ to_chat(user, "The access panel is coated in yellow ooze...")
+ return FALSE
if(!emagged)
emagged = TRUE
if(emergency_mode)
@@ -127,6 +144,8 @@
else if(isliving(AM))
if(emagged)
crush_living(AM)
+ else if(HAS_TRAIT(src, TRAIT_CMAGGED))
+ bananafication(AM)
else
emergency_stop(AM)
else if(isitem(AM))
@@ -165,6 +184,15 @@
emergency_mode = FALSE
update_icon(UPDATE_ICON_STATE)
+/obj/machinery/recycler/proc/bananafication(mob/living/L)
+ L.loc = loc
+ if(!iscarbon(L))
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ return
+ var/mob/living/carbon/human/victim = L
+ playsound(src, 'sound/items/AirHorn.ogg', 100, TRUE, -1)
+ victim.bananatouched_harmless()
+
/obj/machinery/recycler/proc/crush_living(mob/living/L)
L.loc = loc
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index 73b80c850722..170fbfd6ba6d 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -561,3 +561,45 @@
if(length(M.equipment) < M.max_equip)
return TRUE
return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam
+ name = "exosuit medical beamgun"
+ desc = "Equipment for medical exosuits. A mounted medical nanite projector which will treat patients with a focused beam. Unlike its handheld counterpart, it is incapable of healing internal injuries."
+ icon_state = "mecha_medigun"
+ energy_drain = 20
+ range = MECHA_MELEE | MECHA_RANGED
+ equip_cooldown = 0
+ origin_tech = "combat=5;materials=6;powerstorage=6;biotech=6"
+ var/obj/item/gun/medbeam/mech/medigun
+ materials = list(MAT_METAL = 15000, MAT_GLASS = 8000, MAT_PLASMA = 3000, MAT_GOLD = 8000, MAT_DIAMOND = 2000)
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/Initialize(mapload)
+ . = ..()
+ medigun = new(src)
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/Destroy()
+ QDEL_NULL(medigun)
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/process()
+ if(..())
+ return
+ medigun.process()
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/action(atom/target)
+ medigun.process_fire(target, loc)
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/detach()
+ if(medigun)
+ medigun.LoseTarget()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/can_attach(obj/mecha/medical/M)
+ if(..())
+ for(var/obj/item/beamgun in M)
+ if(istype(beamgun, /obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam))
+ return FALSE //One beamgun per mech
+ if(istype(M))
+ return TRUE
+ return FALSE
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index d09f84a766f9..baf991f91326 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -211,6 +211,11 @@
delay = 5 SECONDS
start_effect_type = /obj/effect/temp_visual/rcd_effect/reverse
+/datum/rcd_act/remove_user/can_act(atom/A, obj/item/rcd/rcd)
+ if(!..())
+ return FALSE
+ return A == rcd.loc
+
/obj/item/rcd
name = "rapid-construction-device (RCD)"
desc = "A device used to rapidly build and deconstruct walls, floors and airlocks."
@@ -343,11 +348,10 @@
/obj/item/rcd/suicide_act(mob/living/user)
user.Immobilize(10 SECONDS) // You cannot move.
flags |= NODROP // You cannot drop. You commit to die.
- var/turf/suicide_tile = get_turf(src)
if(mode == MODE_DECON)
user.visible_message("[user] points [src] at [user.p_their()] chest and pulls the trigger. It looks like [user.p_theyre()] trying to commit suicide!")
var/datum/rcd_act/remove_user/act = new()
- if(!act.try_act(suicide_tile, src, user))
+ if(!act.try_act(user, src, user))
flags &= ~NODROP
return SHAME
user.visible_message("[user] deconstructs [user.p_themselves()] with [src]!")
@@ -358,7 +362,7 @@
return OBLITERATION
user.visible_message("[user] puts the barrel of [src] into [user.p_their()] mouth and pulls the trigger. It looks like [user.p_theyre()] trying to commit suicide!")
- if(!interact_with_atom(suicide_tile, user, TRUE))
+ if(!interact_with_atom(get_turf(src), user, TRUE))
flags &= ~NODROP
return SHAME
user.visible_message("[user] explodes as [src] builds a structure inside [user.p_them()]!")
diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm
index 88ffb6d24470..a41aacb953c6 100644
--- a/code/game/objects/items/weapons/extinguisher.dm
+++ b/code/game/objects/items/weapons/extinguisher.dm
@@ -17,12 +17,16 @@
attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed")
dog_fashion = /datum/dog_fashion/back
resistance_flags = FIRE_PROOF
+ new_attack_chain = TRUE
var/max_water = 50
- var/safety = TRUE
- var/refilling = FALSE
- /// FALSE by default, turfs picked from a spray are random, set to TRUE to make it always have at least one water effect per row
+ /// If `TRUE`, using in hand will toggle the extinguisher's safety. This must be set to `FALSE` for extinguishers with different firing modes (i.e. backpacks).
+ var/has_safety = TRUE
+ /// If `TRUE`, the extinguisher will not fire.
+ var/safety_active = TRUE
+ /// When `FALSE`, turfs picked from a spray are random. When `TRUE`, it always has at least one water effect per row.
var/precision = FALSE
- var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled
+ /// Sets the `cooling_temperature` of the water reagent datum inside of the extinguisher when it is refilled.
+ var/cooling_power = 2
COOLDOWN_DECLARE(last_use)
/obj/item/extinguisher/mini
@@ -31,8 +35,8 @@
icon_state = "miniFE0"
item_state = "miniFE"
base_icon_state = "miniFE"
- hitsound = null //it is much lighter, after all.
- flags = null //doesn't CONDUCT
+ hitsound = null // It is much lighter, after all.
+ flags = null // Non-conductive, not made of metal.
throwforce = 2
w_class = WEIGHT_CLASS_SMALL
force = 3
@@ -42,8 +46,8 @@
/obj/item/extinguisher/examine(mob/user)
. = ..()
- . += "The safety is [safety ? "on" : "off"]."
-
+ if(has_safety)
+ . += "The safety is [safety_active ? "on" : "off"]."
/obj/item/extinguisher/Initialize(mapload)
. = ..()
@@ -52,72 +56,81 @@
reagents.add_reagent("water", max_water)
ADD_TRAIT(src, TRAIT_CAN_POINT_WITH, ROUNDSTART_TRAIT)
-/obj/item/extinguisher/attack_self__legacy__attackchain(mob/user as mob)
- safety = !safety
- icon_state = "[base_icon_state][!safety]"
- to_chat(user, "You [safety ? "enable" : "disable"] [src]'s safety.")
+/obj/item/extinguisher/activate_self(mob/user)
+ if(..())
+ return
-/obj/item/extinguisher/attack_obj__legacy__attackchain(obj/O, mob/living/user, params)
- if(AttemptRefill(O, user))
- refilling = TRUE
- return FALSE
- else
- return ..()
+ // Backpack extinguishers have no safety mechanism.
+ if(!has_safety)
+ return
+
+ safety_active = !safety_active
+ icon_state = "[base_icon_state][!safety_active]"
+ to_chat(user, "You [safety_active ? "enable" : "disable"] [src]'s safety.")
+ return ITEM_INTERACT_COMPLETE
-/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user)
+/obj/item/extinguisher/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ . = ..()
+ if(attempt_refill(target, user))
+ return ITEM_INTERACT_COMPLETE
+
+ if(extinguisher_spray(target, user))
+ return ITEM_INTERACT_COMPLETE
+
+/obj/item/extinguisher/ranged_interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(extinguisher_spray(target, user))
+ return ITEM_INTERACT_COMPLETE
+
+/obj/item/extinguisher/proc/attempt_refill(atom/target, mob/user)
if(!istype(target, /obj/structure/reagent_dispensers/watertank) || !target.Adjacent(user))
return FALSE
- var/old_safety = safety
- safety = TRUE
+
if(reagents.total_volume == reagents.maximum_volume)
- to_chat(user, "\The [src] is already full!")
- safety = old_safety
+ to_chat(user, "[src] is already full.")
return TRUE
+
var/obj/structure/reagent_dispensers/watertank/W = target
var/transferred = W.reagents.trans_to(src, max_water)
- if(transferred > 0)
- to_chat(user, "\The [src] has been refilled by [transferred] units.")
- playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, -6)
- for(var/datum/reagent/water/R in reagents.reagent_list)
- R.cooling_temperature = cooling_power
- else
+ if(!transferred)
to_chat(user, "\The [W] is empty!")
- safety = old_safety
+ return TRUE
+
+ to_chat(user, "[src] has been refilled with [transferred] units.")
+ playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, -6)
+ for(var/datum/reagent/water/R in reagents.reagent_list)
+ R.cooling_temperature = cooling_power
return TRUE
-/obj/item/extinguisher/afterattack__legacy__attackchain(atom/target, mob/user, flag)
- . = ..()
- //TODO; Add support for reagents in water.
- if(target.loc == user)//No more spraying yourself when putting your extinguisher away
- return
+/obj/item/extinguisher/proc/extinguisher_spray(atom/A, mob/living/user)
+ . = TRUE
- if(refilling)
- refilling = FALSE
+ // Violence, please!
+ if(safety_active)
+ return FALSE
+
+ if(!COOLDOWN_FINISHED(src, last_use))
return
- if(safety)
- return ..()
if(reagents.total_volume < 1)
to_chat(user, "[src] is empty.")
return
- if(!COOLDOWN_FINISHED(src, last_use))
+ if(A.loc == user)
return
COOLDOWN_START(src, last_use, 2 SECONDS)
-
if(reagents.chem_temp > 300 || reagents.chem_temp < 280)
- add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
+ add_attack_logs(user, A, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3)
- var/direction = get_dir(src, target)
+ var/direction = get_dir(src, A)
if(isobj(user.buckled) && !user.buckled.anchored && !istype(user.buckled, /obj/vehicle))
INVOKE_ASYNC(src, PROC_REF(buckled_speed_move), user.buckled, direction)
else
user.newtonian_move(turn(direction, 180))
- var/turf/T = get_turf(target)
+ var/turf/T = get_turf(A)
var/turf/T1 = get_step(T, turn(direction, 90))
var/turf/T2 = get_step(T, turn(direction, -90))
var/list/the_targets = list(T, T1, T2)
diff --git a/code/game/objects/items/weapons/rpd.dm b/code/game/objects/items/weapons/rpd.dm
index a7795aff20a3..d85bda11ba25 100644
--- a/code/game/objects/items/weapons/rpd.dm
+++ b/code/game/objects/items/weapons/rpd.dm
@@ -342,7 +342,7 @@
// If we get here, then we're effectively acting on the turf, probably placing a pipe.
if(ranged) //woosh beam if bluespaced at a distance
- if(get_dist(src, T) >= (user.client.maxview() + 2))
+ if(get_dist(src, T) >= (user.client.maxview() / 2))
message_admins("\[EXPLOIT] [key_name_admin(user)] attempted to place pipes with a BRPD via a camera console. (Attempted range exploit)")
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "ERROR: \The [T] is out of [src]'s range!")
diff --git a/code/game/objects/items/weapons/stock_parts.dm b/code/game/objects/items/weapons/stock_parts.dm
index 5f8d009af33c..c24f6c4d1c4f 100644
--- a/code/game/objects/items/weapons/stock_parts.dm
+++ b/code/game/objects/items/weapons/stock_parts.dm
@@ -50,17 +50,16 @@
if(!proximity_flag)
if(!works_from_distance)
return
- if(get_dist(src, M) > (user.client.maxview() + 2))
+ if(get_dist(user, M) > (user.client.maxview() / 2))
+ message_admins("\[EXPLOIT] [key_name_admin(user)] attempted to upgrade machinery with a BRPED via a camera console (attempted range exploit).")
+ playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
+ to_chat(user, "ERROR: [M] is out of [src]'s range!")
return
if(M.component_parts)
M.exchange_parts(user, src)
if(works_from_distance)
user.Beam(M, icon_state="rped_upgrade", icon='icons/effects/effects.dmi', time=5)
- else
- message_admins("\[EXPLOIT] [key_name_admin(user)] attempted to upgrade machinery with a BRPED via a camera console (attempted range exploit).")
- playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
- to_chat(user, "ERROR: [M] is out of [src]'s range!")
/obj/item/storage/part_replacer/tier4/populate_contents()
for(var/amount in 1 to 30)
@@ -283,9 +282,3 @@
rating = 4
materials = list(MAT_METAL=80)
-/obj/item/research//Makes testing much less of a pain -Sieve
- name = "research"
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "capacitor"
- desc = "A debug item for research."
- origin_tech = "materials=8;programming=8;magnets=8;powerstorage=8;bluespace=8;combat=8;biotech=8;syndicate=8;engineering=8;plasmatech=8;abductor=8"
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index fb1dcaca5733..5162507e11b7 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -195,7 +195,7 @@
/// 80TC + modules + laser gun
/obj/item/storage/box/syndie_kit/bundle/metroid
name = "Modsuit Bundle"
- desc = "Don the equipment of an intergalactic bounty hunter and blast your way through the station!"
+ desc = "Don the equipment of an interstellar bounty hunter and blast your way through the station!"
items = list(
/obj/item/mod/control/pre_equipped/traitor_elite, // 45TC
/obj/item/mod/module/visor/thermal, // 15TC
diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm
index 4722417ad467..d8289c434da4 100644
--- a/code/game/objects/items/weapons/tanks/watertank.dm
+++ b/code/game/objects/items/weapons/tanks/watertank.dm
@@ -142,7 +142,7 @@
return
/proc/check_tank_exists(parent_tank, mob/living/carbon/human/M, obj/O)
- if(!parent_tank || !istype(parent_tank, /obj/item/watertank)) //To avoid weird issues from admin spawns
+ if(!parent_tank || (!istype(parent_tank, /obj/item/watertank) && !istype(parent_tank, /obj/item/mod/module/firefighting_tank))) //To avoid weird issues from admin spawns
return FALSE
else
return TRUE
@@ -218,11 +218,12 @@
name = "extinguisher nozzle"
desc = "A heavy duty nozzle attached to a firefighter's backpack tank."
icon = 'icons/obj/watertank.dmi'
- icon_state = "atmos_nozzle"
+ icon_state = "atmos_nozzle_1"
item_state = "nozzleatmos"
- safety = 0
+ has_safety = FALSE
+ safety_active = FALSE
max_water = 500
- precision = 1
+ precision = TRUE
cooling_power = 5
w_class = WEIGHT_CLASS_HUGE
flags = NODROP //Necessary to ensure that the nozzle and tank never seperate
@@ -230,10 +231,23 @@
var/obj/item/watertank/tank
/// What mode are we currently in?
var/nozzle_mode = EXTINGUISHER
- /// Are we overusing the metal synthesizer? can be used 5 times in quick succession, regains 1 use per 10 seconds
- var/metal_synthesis_cooldown = 0
- /// Is our nanofrost on cooldown?
- var/nanofrost_cooldown = FALSE
+ /// How many shots of metal foam do we have?
+ var/metal_synthesis_charge = 5
+ /// Time to refill 1 charge of metal foam.
+ var/metal_regen_time = 2 SECONDS
+ /// Refire delay for nanofrost chunks.
+ var/nanofrost_cooldown_time = 2 SECONDS
+ COOLDOWN_DECLARE(nanofrost_cooldown)
+
+/obj/item/extinguisher/mini/nozzle/examine(mob/user)
+ . = ..()
+ switch(nozzle_mode)
+ if(EXTINGUISHER)
+ . += "[src] is currently set to extinguishing mode."
+ if(NANOFROST)
+ . += "[src] is currently set to nanofrost mode."
+ if(METAL_FOAM)
+ . += "[src] is currently set to metal foam mode."
/obj/item/extinguisher/mini/nozzle/Initialize(mapload)
if(!check_tank_exists(loc, src))
@@ -255,67 +269,88 @@
if(tank && loc != tank.loc)
forceMove(tank)
-/obj/item/extinguisher/mini/nozzle/attack_self__legacy__attackchain(mob/user)
+/obj/item/extinguisher/mini/nozzle/activate_self(mob/user)
+ ..()
switch(nozzle_mode)
if(EXTINGUISHER)
nozzle_mode = NANOFROST
- tank.icon_state = "waterbackpackatmos_1"
to_chat(user, "Swapped to nanofrost launcher")
if(NANOFROST)
nozzle_mode = METAL_FOAM
- tank.icon_state = "waterbackpackatmos_2"
to_chat(user, "Swapped to metal foam synthesizer")
if(METAL_FOAM)
nozzle_mode = EXTINGUISHER
- tank.icon_state = "waterbackpackatmos_0"
to_chat(user, "Swapped to water extinguisher")
+ update_icon(UPDATE_ICON_STATE)
+ return ITEM_INTERACT_COMPLETE
+
+/obj/item/extinguisher/mini/nozzle/update_icon_state()
+ switch(nozzle_mode)
+ if(EXTINGUISHER)
+ icon_state = "atmos_nozzle_1"
+ tank.icon_state = "waterbackpackatmos_0"
+ if(NANOFROST)
+ icon_state = "atmos_nozzle_2"
+ tank.icon_state = "waterbackpackatmos_1"
+ if(METAL_FOAM)
+ icon_state = "atmos_nozzle_3"
+ tank.icon_state = "waterbackpackatmos_2"
/obj/item/extinguisher/mini/nozzle/dropped(mob/user)
..()
+ if(istype(tank, /obj/item/mod/module/firefighting_tank))
+ return
+
to_chat(user, "The nozzle snaps back onto the tank!")
tank.on = FALSE
loc = tank
-/obj/item/extinguisher/mini/nozzle/afterattack__legacy__attackchain(atom/target, mob/user)
+/obj/item/extinguisher/mini/nozzle/extinguisher_spray(atom/A, mob/living/user)
if(nozzle_mode == EXTINGUISHER)
- ..()
- return
- var/Adj = user.Adjacent(target)
- if(Adj)
- AttemptRefill(target, user)
+ return ..()
+ . = TRUE
switch(nozzle_mode)
if(NANOFROST)
- if(Adj)
- return //Safety check so you don't blast yourself trying to refill your tank
if(reagents.total_volume < 100)
- to_chat(user, "You need at least 100 units of water to use the nanofrost launcher!")
+ to_chat(user, "You need at least 100 units of water to use the nanofrost launcher!")
return
- if(nanofrost_cooldown)
- to_chat(user, "Nanofrost launcher is still recharging.")
+
+ if(COOLDOWN_TIMELEFT(src, nanofrost_cooldown))
+ to_chat(user, "The nanofrost launcher is still recharging!")
return
- nanofrost_cooldown = TRUE
+
+ COOLDOWN_START(src, nanofrost_cooldown, nanofrost_cooldown_time)
reagents.remove_any(100)
- var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src))
+ var/obj/effect/nanofrost_container/iceball = new /obj/effect/nanofrost_container(get_turf(src))
log_game("[key_name(user)] used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).")
playsound(src,'sound/items/syringeproj.ogg', 40, TRUE)
- A.throw_at(target, 6, 2, user)
+ iceball.throw_at(A, 6, 2, user)
sleep(2)
- A.Smoke()
- addtimer(VARSET_CALLBACK(src, nanofrost_cooldown, FALSE))
+ iceball.Smoke()
+ return
+
if(METAL_FOAM)
- if(!Adj)
+ if(!user.Adjacent(A) || !isturf(A))
return
- if(metal_synthesis_cooldown >= 5)
- to_chat(user, "Metal foam mix is still being synthesized.")
+
+ if(metal_synthesis_charge <= 0)
+ to_chat(user, "Metal foam mix is still being synthesized!")
return
- var/obj/effect/particle_effect/foam/metal/F = new /obj/effect/particle_effect/foam/metal(get_turf(target), TRUE)
- F.spread_amount = 0
- metal_synthesis_cooldown++
- addtimer(CALLBACK(src, PROC_REF(metal_cooldown)), 10 SECONDS)
-/obj/item/extinguisher/mini/nozzle/proc/metal_cooldown()
- metal_synthesis_cooldown--
+ if(reagents.total_volume < 10)
+ to_chat(user, "You need at least 10 units of water to use the metal foam synthesizer!")
+ return
+
+ var/obj/effect/particle_effect/foam/metal/foam = new /obj/effect/particle_effect/foam/metal(get_turf(A), TRUE)
+ foam.spread_amount = 0
+ reagents.remove_any(10)
+ metal_synthesis_charge--
+ addtimer(CALLBACK(src, PROC_REF(refill_metal_charge)), metal_regen_time)
+ return
+
+/obj/item/extinguisher/mini/nozzle/proc/refill_metal_charge()
+ metal_synthesis_charge++
/obj/effect/nanofrost_container
name = "nanofrost container"
diff --git a/code/game/objects/mail.dm b/code/game/objects/mail.dm
index 86ec5986d279..f0b0171f8cc0 100644
--- a/code/game/objects/mail.dm
+++ b/code/game/objects/mail.dm
@@ -230,7 +230,7 @@
/obj/item/mail_scanner
name = "mail scanner"
- desc = "Sponsored by Messaging and Intergalactic Letters, this device allows you to log mail deliveries in exchange for financial compensation."
+ desc = "A portable mail scanner, this device allows you to log mail deliveries in exchange for financial compensation."
force = 0
throwforce = 0
icon = 'icons/obj/device.dmi'
@@ -295,5 +295,5 @@
saved = null
to_chat(user, "Successful delivery acknowledged! [MAIL_DELIVERY_BONUS] credits added to Supply account!")
playsound(loc, 'sound/mail/mailapproved.ogg', 50, TRUE)
- GLOB.station_money_database.credit_account(SSeconomy.cargo_account, MAIL_DELIVERY_BONUS, "Mail Delivery Compensation", "Messaging and Intergalactic Letters", supress_log = FALSE)
+ GLOB.station_money_database.credit_account(SSeconomy.cargo_account, MAIL_DELIVERY_BONUS, "Mail Delivery Compensation", "Nanotrasen Mail and Interstellar Logistics", supress_log = FALSE)
SSblackbox.record_feedback("amount", "successful_mail_delivery", 1)
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index 5bc31b1d0502..e93f865cc7ed 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -21,7 +21,7 @@
var/list/valid_underwear = list()
for(var/underwear in GLOB.underwear_list)
var/datum/sprite_accessory/S = GLOB.underwear_list[underwear]
- if(!(H.dna.species.name in S.species_allowed))
+ if(!(H.dna.species.sprite_sheet_name in S.species_allowed))
continue
valid_underwear[underwear] = GLOB.underwear_list[underwear]
var/new_underwear = tgui_input_list(user, "Choose your underwear:", "Changing", valid_underwear)
@@ -32,7 +32,7 @@
var/list/valid_undershirts = list()
for(var/undershirt in GLOB.undershirt_list)
var/datum/sprite_accessory/S = GLOB.undershirt_list[undershirt]
- if(!(H.dna.species.name in S.species_allowed))
+ if(!(H.dna.species.sprite_sheet_name in S.species_allowed))
continue
valid_undershirts[undershirt] = GLOB.undershirt_list[undershirt]
var/new_undershirt = tgui_input_list(user, "Choose your undershirt:", "Changing", valid_undershirts)
@@ -43,7 +43,7 @@
var/list/valid_sockstyles = list()
for(var/sockstyle in GLOB.socks_list)
var/datum/sprite_accessory/S = GLOB.socks_list[sockstyle]
- if(!(H.dna.species.name in S.species_allowed))
+ if(!(H.dna.species.sprite_sheet_name in S.species_allowed))
continue
valid_sockstyles[sockstyle] = GLOB.socks_list[sockstyle]
var/new_socks = tgui_input_list(user, "Choose your socks:", "Changing", valid_sockstyles)
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index ff4636e30902..7e4b435c254c 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -201,7 +201,7 @@
/obj/structure/sign/greencross
name = "medbay"
- desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
+ desc = "The universal symbol of medical institutions across the Sector. You'll probably find help here."
icon_state = "greencross"
/obj/structure/sign/goldenplaque
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 07a9085f4871..fcea2b6c6661 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -34,7 +34,9 @@
var/blocks_air = FALSE
- flags = 0
+ flags = 0 // TODO, someday move all off the flags here to turf_flags
+
+ var/turf_flags = NONE
var/image/obscured //camerachunks
@@ -282,8 +284,9 @@
qdel(src) //Just get the side effects and call Destroy
var/list/old_comp_lookup = comp_lookup?.Copy()
var/list/old_signal_procs = signal_procs?.Copy()
-
+ var/carryover_turf_flags = turf_flags & (RESERVATION_TURF|UNUSED_RESERVATION_TURF)
var/turf/W = new path(src)
+ W.turf_flags |= carryover_turf_flags
if(old_comp_lookup)
LAZYOR(W.comp_lookup, old_comp_lookup)
if(old_signal_procs)
diff --git a/code/game/world.dm b/code/game/world.dm
index da8dac0955df..cb67ae8829e7 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -1,6 +1,6 @@
GLOBAL_LIST_INIT(map_transition_config, list(CC_TRANSITION_CONFIG))
-#ifdef GAME_TESTS
+#ifdef TEST_RUNNER
GLOBAL_DATUM(test_runner, /datum/test_runner)
#endif
@@ -26,7 +26,11 @@ GLOBAL_DATUM(test_runner, /datum/test_runner)
GLOB.configuration.load_configuration() // Load up the base config.toml
// Load up overrides for this specific instance, based on port
// If this instance is listening on port 6666, the server will look for config/overrides_6666.toml
- GLOB.configuration.load_overrides()
+ GLOB.configuration.load_overrides("config/overrides_[world.port].toml")
+
+ #ifdef TEST_CONFIG_OVERRIDE
+ GLOB.configuration.load_overrides("config/tests/config_[TEST_CONFIG_OVERRIDE].toml")
+ #endif
// Right off the bat, load up the DB
SSdbcore.CheckSchemaVersion() // This doesnt just check the schema version, it also connects to the db! This needs to happen super early! I cannot stress this enough!
@@ -51,8 +55,8 @@ GLOBAL_DATUM(test_runner, /datum/test_runner)
if(TgsAvailable())
world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them.
- #ifdef GAME_TESTS
- log_world("Unit Tests Are Enabled!")
+ #ifdef TEST_RUNNER
+ log_world("Test runner enabled.")
#endif
if(byond_version < MIN_COMPILER_VERSION || byond_build < MIN_COMPILER_BUILD)
@@ -69,7 +73,7 @@ GLOBAL_DATUM(test_runner, /datum/test_runner)
Master.Initialize(10, FALSE, TRUE)
- #ifdef GAME_TESTS
+ #ifdef TEST_RUNNER
GLOB.test_runner = new
GLOB.test_runner.Start()
#endif
@@ -144,7 +148,7 @@ GLOBAL_LIST_EMPTY(world_topic_handlers)
Master.Shutdown() // Shutdown subsystems
// If we were running unit tests, finish that run
- #ifdef GAME_TESTS
+ #ifdef TEST_RUNNER
GLOB.test_runner.Finalize()
return
#endif
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 8d1896340311..20975808c4d5 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -159,6 +159,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/proc/machine_upgrade,
/client/proc/map_template_load,
/client/proc/map_template_upload,
+ /client/proc/map_template_load_lazy,
/client/proc/view_runtimes,
/client/proc/admin_serialize,
/client/proc/uid_log,
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 334c608bbf54..cce16b25c8f3 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1056,7 +1056,7 @@
if(GLOB.master_mode != "dynamic" && !(GLOB.master_mode == "secret" && GLOB.secret_force_mode == "dynamic"))
return alert(usr, "The game mode has to be dynamic!", null, null, null, null)
var/dat = {"Possible Rulesets:
"}
- var/list/rulesets = subtypesof(/datum/ruleset) - typesof(/datum/ruleset/implied)
+ var/list/rulesets = subtypesof(/datum/ruleset) - typesof(/datum/ruleset/implied) - /datum/ruleset/team
dat += {"Budget: [isnull(GLOB.dynamic_forced_rulesets["budget"]) ? "Random" : GLOB.dynamic_forced_rulesets["budget"]]"}
for(var/datum/ruleset/ruleset as anything in rulesets)
dat += {"[ruleset.name]: [GLOB.dynamic_forced_rulesets[ruleset] || DYNAMIC_RULESET_NORMAL] "}
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 559b983db522..2dcf196bce8b 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -121,10 +121,10 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
if(current_caller && current_caller != ckey)
if(!GLOB.AdminProcCallSpamPrevention[ckey])
- to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.")
+ to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.")
GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
UNTIL(!GLOB.AdminProcCaller)
- to_chat(usr, "Running your proc")
+ to_chat(usr, "Running your proc")
GLOB.AdminProcCallSpamPrevention -= ckey
else
UNTIL(!GLOB.AdminProcCaller)
@@ -135,8 +135,8 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
++GLOB.AdminProcCallCount
try
. = world.WrapAdminProcCall(target, procname, arguments)
- catch
- to_chat(usr, "Your proc call failed to execute, likely from runtimes. You should be out of safety mode. If not, god help you.")
+ catch(var/exception/e)
+ to_chat(usr, "Your proc call failed to execute, likely from runtimes. You should be out of safety mode. If not, god help you. Runtime Info: [e.file]:[e.line]: [e.name]")
if(--GLOB.AdminProcCallCount == 0)
GLOB.AdminProcCaller = null
diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm
index 80d46e18550d..c05060131fcf 100644
--- a/code/modules/admin/verbs/map_template_loadverb.dm
+++ b/code/modules/admin/verbs/map_template_loadverb.dm
@@ -56,3 +56,23 @@
message_admins("[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.")
else
to_chat(usr, "Map template '[map]' failed to load properly")
+
+/client/proc/map_template_load_lazy()
+ set category = "Debug"
+ set name = "Map template - Lazy Load"
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ var/map = input(usr, "Choose a Map Template to place on the lazy load map level.","Place Map Template") as null|anything in GLOB.map_templates
+ if(!map)
+ return
+ var/datum/map_template/template = GLOB.map_templates[map]
+
+ message_admins("[key_name_admin(usr)] is lazyloading the map template ([template.name]).")
+ var/datum/turf_reservation/reserve = SSmapping.lazy_load_template(template)
+ if(!istype(reserve))
+ message_admins("Lazyloading [template.name] failed! You should report this as a bug.")
+ return
+ message_admins("[key_name_admin(usr)] has lazyloaded the map template ([template.name]) at [ADMIN_JMP(reserve.bottom_left_turf)]")
+
diff --git a/code/modules/antagonists/revolutionary/datum_headrev.dm b/code/modules/antagonists/revolutionary/datum_headrev.dm
index 3097fbc02298..64e9311226ed 100644
--- a/code/modules/antagonists/revolutionary/datum_headrev.dm
+++ b/code/modules/antagonists/revolutionary/datum_headrev.dm
@@ -46,9 +46,9 @@ RESTRICT_TYPE(/datum/antagonist/rev/head)
to_chat(revolutionary, "The flash in your [flashloc_name] will help you to persuade the crew to join your cause.")
if(give_hud)
- var/obj/item/clothing/glasses/hud/security/chameleon/C = new(get_turf(revolutionary))
- var/hudloc_name = revolutionary.equip_in_one_of_slots(C, slots)
- to_chat(revolutionary, "The chameleon security HUD in your [hudloc_name] will help you keep track of who is mindshield-implanted, and unable to be recruited.")
+ var/obj/item/organ/internal/cyberimp/eyes/hud/security/hidden/O = new /obj/item/organ/internal/cyberimp/eyes/hud/security/hidden
+ O.insert(revolutionary)
+ to_chat(revolutionary, "The security HUD implant in your head will help you keep track of who is mindshield-implanted, and unable to be recruited.")
revolutionary.update_icons()
return flashloc_name
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index 636bf12a99e3..50da9a4b15ad 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -1,8 +1,3 @@
-/turf/simulated/Initialize(mapload)
- . = ..()
- if(!blocks_air)
- blind_set_air(get_initial_air())
-
/turf/simulated/proc/get_initial_air()
var/datum/gas_mixture/air = new()
if(!blocks_air)
@@ -47,8 +42,12 @@
temperature -= heat/heat_capacity
sharer.temperature += heat/sharer.heat_capacity
-/turf/simulated/proc/update_visuals()
- var/datum/gas_mixture/air = get_readonly_air()
+/turf/simulated/proc/update_visuals(use_initial_air = FALSE)
+ var/datum/gas_mixture/air
+ if(use_initial_air)
+ air = get_initial_air()
+ else
+ air = get_readonly_air()
var/new_overlay_type = tile_graphic(air)
if(new_overlay_type == atmos_overlay_type)
return
@@ -177,6 +176,10 @@
var/list/air = list(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, temperature)
milla_data = connectivity[1] + list(atmos_mode, SSmapping.environments[atmos_environment]) + air + connectivity[2]
+/turf/simulated/Initialize_Atmos(milla_tick)
+ ..()
+ update_visuals(TRUE)
+
/turf/proc/recalculate_atmos_connectivity()
var/datum/milla_safe/recalculate_atmos_connectivity/milla = new()
milla.invoke_async(src)
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 5216ac330ebd..fec392083781 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -104,6 +104,9 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
var/report_danger_level = TRUE
+ /// Which MILLA tick were we initialized at?
+ var/init_tick
+
/obj/machinery/alarm/monitor
report_danger_level = FALSE
@@ -232,6 +235,8 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
if(!building)
first_run()
+ init_tick = SSair.milla_tick
+
/obj/machinery/alarm/Destroy()
SStgui.close_uis(wires)
GLOB.air_alarms -= src
@@ -246,7 +251,7 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
GLOB.air_alarm_repository.update_cache(src)
/obj/machinery/alarm/process()
- if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2)
+ if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2 || init_tick == SSair.milla_tick)
return
var/turf/simulated/location = loc
diff --git a/code/modules/awaymissions/cordon.dm b/code/modules/awaymissions/cordon.dm
new file mode 100644
index 000000000000..4d44766ac668
--- /dev/null
+++ b/code/modules/awaymissions/cordon.dm
@@ -0,0 +1,32 @@
+/// Turf type that appears to be a world border, completely impassable and non-interactable to all physical (alive) entities.
+/turf/cordon
+ name = "cordon"
+ icon = 'icons/turf/walls.dmi'
+ icon_state = "cordon"
+ invisibility = INVISIBILITY_ABSTRACT
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ explosion_block = 50
+ rad_insulation = RAD_FULL_INSULATION
+ opacity = TRUE
+ density = TRUE
+ blocks_air = TRUE
+ baseturf = /turf/cordon
+
+// /turf/cordon/rust_heretic_act()
+// return FALSE
+
+/turf/cordon/acid_act(acidpwr, acid_volume, acid_id)
+ return FALSE
+
+/turf/cordon/singularity_act()
+ return FALSE
+
+/turf/cordon/TerraformTurf(path, list/new_baseturfs, flags)
+ return
+
+/turf/cordon/bullet_act(obj/item/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) // Fuck you
+ return
+
+/turf/cordon/Adjacent(atom/neighbor, atom/target, atom/movable/mover)
+ return FALSE
diff --git a/code/modules/awaymissions/mob_spawn.dm b/code/modules/awaymissions/mob_spawn.dm
index 3836b267eef1..7914faf17620 100644
--- a/code/modules/awaymissions/mob_spawn.dm
+++ b/code/modules/awaymissions/mob_spawn.dm
@@ -247,12 +247,12 @@
if(hair_style)
D.h_style = hair_style
else
- D.h_style = random_hair_style(gender, D.dna.species.name)
+ D.h_style = random_hair_style(gender, D.dna.species.sprite_sheet_name)
D.hair_colour = rand_hex_color()
if(facial_hair_style)
D.f_style = facial_hair_style
else
- D.f_style = random_facial_hair_style(gender, D.dna.species.name)
+ D.f_style = random_facial_hair_style(gender, D.dna.species.sprite_sheet_name)
D.facial_colour = rand_hex_color()
if(skin_tone)
H.s_tone = skin_tone
diff --git a/code/modules/awaymissions/zlevel_helpers.dm b/code/modules/awaymissions/zlevel_helpers.dm
index 749abdf47a57..ca3cc60c1500 100644
--- a/code/modules/awaymissions/zlevel_helpers.dm
+++ b/code/modules/awaymissions/zlevel_helpers.dm
@@ -13,6 +13,8 @@
* turf in the world */
if(SSair && SSair.initialized)
SSair.setup_turfs(bot_left, top_right)
+ log_debug("Unfreezing atmos.")
+ set_zlevel_freeze(bot_left.z, FALSE)
log_debug("\tTook [stop_watch(subtimer)]s")
subtimer = start_watch()
@@ -39,3 +41,12 @@
for(var/otherthing in T)
qdel(otherthing)
T.ChangeTurf(T.baseturf)
+
+/datum/milla_safe/freeze_z_level
+ var/done = FALSE
+
+// Ensures that atmos is frozen before loading
+/datum/milla_safe/freeze_z_level/on_run(z)
+ log_debug("Freezing atmos.")
+ set_zlevel_freeze(z, TRUE)
+ done = TRUE
diff --git a/code/modules/client/login_processing/20-load_characters.dm b/code/modules/client/login_processing/20-load_characters.dm
index 0edf260ae8de..84fd4f15b828 100644
--- a/code/modules/client/login_processing/20-load_characters.dm
+++ b/code/modules/client/login_processing/20-load_characters.dm
@@ -65,6 +65,7 @@
cyborg_brain_type,
body_type,
pda_ringtone,
+ species_subtype,
tts_seed
FROM characters WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
diff --git a/code/modules/client/preference/character.dm b/code/modules/client/preference/character.dm
index 6649705181da..fd0051dea687 100644
--- a/code/modules/client/preference/character.dm
+++ b/code/modules/client/preference/character.dm
@@ -37,6 +37,8 @@
var/e_colour = "#000000" //Eye color
var/alt_head = "None" //Alt head style.
var/species = "Human"
+ /// Used for DNA species to allow select species to imitate / morph into different species.
+ var/species_subtype = "None"
var/language = "None" //Secondary language
var/autohiss_mode = AUTOHISS_OFF //Species autohiss level. OFF, BASIC, FULL.
/// If a spawned cyborg should have an MMI, a positronic, or a robobrain. MMI by default
@@ -203,6 +205,7 @@
cyborg_brain_type=:cyborg_brain_type,
body_type=:body_type,
pda_ringtone=:pda_ringtone,
+ species_subtype=:species_subtype,
tts_seed=:tts_seed
WHERE ckey=:ckey
AND slot=:slot"}, list(
@@ -267,6 +270,7 @@
"runechat_color" = runechat_color,
"cyborg_brain_type" = cyborg_brain_type,
"pda_ringtone" = pda_ringtone,
+ "species_subtype" = species_subtype,
"tts_seed" = tts_seed, // SS220 EDIT ADDITION - TTS220
"ckey" = C.ckey,
"slot" = slot_number
@@ -308,7 +312,7 @@
player_alt_titles,
disabilities, organ_data, rlimb_data, nanotrasen_relation, physique, height, speciesprefs,
socks, body_accessory, gear, autohiss,
- hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes, runechat_color, cyborg_brain_type, body_type, pda_ringtone, tts_seed)
+ hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes, runechat_color, cyborg_brain_type, body_type, pda_ringtone, species_subtype, tts_seed)
VALUES
(:ckey, :slot, :metadata, :name, :be_random_name, :gender,
:age, :species, :language,
@@ -335,7 +339,7 @@
:playertitlelist,
:disabilities, :organ_list, :rlimb_list, :nanotrasen_relation, :physique, :height, :speciesprefs,
:socks, :body_accessory, :gearlist, :autohiss_mode,
- :h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes, :runechat_color, :cyborg_brain_type, :body_type, :pda_ringtone, :tts_seed)
+ :h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes, :runechat_color, :cyborg_brain_type, :body_type, :pda_ringtone, :species_subtype, :tts_seed)
"}, list(
// This has too many params for anyone to look at this without going insae
"ckey" = C.ckey,
@@ -400,6 +404,7 @@
"runechat_color" = runechat_color,
"cyborg_brain_type" = cyborg_brain_type,
"pda_ringtone" = pda_ringtone,
+ "species_subtype" = species_subtype,
"tts_seed" = tts_seed // SS220 EDIT ADDITION - TTS220
))
@@ -425,7 +430,6 @@
age = text2num(query.item[5])
species = query.item[6]
language = query.item[7]
-
h_colour = query.item[8]
h_sec_colour = query.item[9]
f_colour = query.item[10]
@@ -496,7 +500,8 @@
cyborg_brain_type = query.item[59]
body_type = query.item[60]
pda_ringtone = query.item[61]
- tts_seed = query.item[62] // SS220 EDIT ADDITION - TTS220
+ species_subtype = query.item[62]
+ tts_seed = query.item[63] // SS220 EDIT ADDITION - TTS220
//Sanitize
var/datum/species/SP = GLOB.all_species[species]
@@ -511,6 +516,9 @@
species = "Human"
stack_trace("Character doesn't have a species, character name is [real_name]. Defaulting to human.")
+ if(isnull(species_subtype))
+ species_subtype = "None"
+
if(isnull(language))
language = "None"
@@ -834,53 +842,65 @@
//Icon-based species colour.
var/coloured_tail
if(current_species)
- if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race.
- var/mob/living/carbon/human/fake/H = new
- H.dna.species = current_species
+ var/mob/living/carbon/human/fake/H = new
+ H.dna.species = current_species
+ if(species_subtype != "None" && current_species.bodyflags & HAS_SPECIES_SUBTYPE)
+ var/datum/species/subtype_species = GLOB.all_species[species_subtype]
+ if(subtype_species) // Take certain attributes from our subtype to apply to our current species.
+ H.dna.species.updatespeciessubtype(H, subtype_species)
+ current_species = H.dna.species
+ else if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race.
H.s_tone = s_tone
H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive.
- var/obj/item/organ/external/chest/C = H.get_organ("chest")
- icobase = C.icobase ? C.icobase : C.dna.species.icobase
if(H.dna.species.bodyflags & HAS_TAIL)
coloured_tail = H.tail ? H.tail : H.dna.species.tail
-
- qdel(H)
- else
- icobase = current_species.icobase
+ icobase = current_species.icobase
+ qdel(H)
else
icobase = 'icons/mob/human_races/r_human.dmi'
preview_icon = new /icon(icobase, "torso_[g]")
preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY)
- var/head = "head"
- if(alt_head && current_species.bodyflags & HAS_ALT_HEADS)
- var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head]
- if(H.icon_state)
- head = H.icon_state
- preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY)
-
for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand"))
- if(organ_data[name] == "amputated") continue
- if(organ_data[name] == "cyborg")
+ if(organ_data[name] == "amputated")
+ continue
+ var/icon/bodypart = new /icon(icobase, "[name]")
+ if(name == "head") // Head nonsense.
+ var/head = "head"
+ if(alt_head && current_species.bodyflags & HAS_ALT_HEADS)
+ var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head]
+ if(H.icon_state)
+ head = H.icon_state
+ bodypart = new /icon(icobase, "[head]_[g]") // head_state _ gender
+ if(name in list("chest", "groin")) // Groin and Chest nonsense
+ if(name == "chest")
+ name = "torso"
+ bodypart = new /icon(icobase, "[name]_[g]") // groin/torso _ gender
+ if(organ_data[name] == "cyborg") // Robotic limbs.
var/datum/robolimb/R
if(rlimb_data[name]) R = GLOB.all_robolimbs[rlimb_data[name]]
if(!R) R = GLOB.basic_robolimb
if(name == "chest")
name = "torso"
- preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) // This doesn't check gendered_icon. Not an issue while only limbs can be robotic.
+ if(length(R.sprite_sheets) && R.sprite_sheets[current_species.sprite_sheet_name]) // Species specific augmented limbs
+ R.icon = R.sprite_sheets[current_species.sprite_sheet_name]
+ bodypart.Blend(new /icon(R.icon, "[name]"), ICON_OVERLAY)
+ preview_icon.Blend(bodypart, ICON_OVERLAY)
continue
- preview_icon.Blend(new /icon(icobase, "[name]"), ICON_OVERLAY)
-
- // Skin color
- if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR))
- preview_icon.Blend(s_colour, ICON_ADD)
-
- // Skin tone
- if(current_species && (current_species.bodyflags & HAS_SKIN_TONE))
- if(s_tone >= 0)
- preview_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD)
+ if(istype(current_species, /datum/species/slime) && current_species.species_subtype != "None") // Applies to limbs that are not robotic.
+ bodypart.GrayScale()
+ bodypart.Blend("[s_colour]DC", ICON_AND) //DC = 220 alpha.
else
- preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
+ // Skin color
+ if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR))
+ bodypart.Blend(s_colour, ICON_ADD)
+ // Skin tone
+ if(current_species && (current_species.bodyflags & HAS_SKIN_TONE))
+ if(s_tone >= 0)
+ bodypart.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD)
+ else
+ bodypart.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
+ preview_icon.Blend(bodypart, ICON_OVERLAY)
// Body accessory
if(current_species && (current_species.bodyflags & HAS_BODY_ACCESSORY))
@@ -1835,6 +1855,11 @@
/datum/character_save/proc/copy_to(mob/living/carbon/human/character)
var/datum/species/S = GLOB.all_species[species]
character.set_species(S.type, delay_icon_update = TRUE) // Yell at me if this causes everything to melt
+ var/datum/species/subtype = GLOB.all_species[species_subtype]
+ if(!isnull(subtype))
+ character.dna.species.updatespeciessubtype(character, new subtype.type(), TRUE, FALSE)
+ else
+ character.species_subtype = "None"
if(be_random_name)
real_name = random_name(gender, species)
diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm
index a26990d2df5a..38f383aca55b 100644
--- a/code/modules/client/preference/link_processing.dm
+++ b/code/modules/client/preference/link_processing.dm
@@ -3,6 +3,12 @@
return
var/datum/species/S = GLOB.all_species[active_character.species]
+ var/datum/species/subtype = GLOB.all_species[active_character.species_subtype]
+ if(isnull(subtype)) // Set the subtype to be the same as Species in the case we don't have one, saves alot of headaches when we're checking for valid markings etc.
+ subtype = S
+ else
+ S = new S.type()
+ S.bodyflags |= subtype.bodyflags
if(href_list["preference"] == "job")
switch(href_list["task"])
if("close")
@@ -197,73 +203,25 @@
to_chat(user, "Invalid species, please pick something else.")
return
if(prev_species != active_character.species)
- active_character.age = clamp(active_character.age, NS.min_age, NS.max_age)
- var/datum/robolimb/robohead
- if(NS.bodyflags & ALL_RPARTS)
- var/head_model = "[!active_character.rlimb_data["head"] ? "Morpheus Cyberkinetics" : active_character.rlimb_data["head"]]"
- robohead = GLOB.all_robolimbs[head_model]
- //grab one of the valid hair styles for the newly chosen species
- active_character.h_style = random_hair_style(active_character.gender, active_character.species, robohead)
-
- //grab one of the valid facial hair styles for the newly chosen species
- active_character.f_style = random_facial_hair_style(active_character.gender, active_character.species, robohead)
-
- if(NS.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
- active_character.ha_style = random_head_accessory(active_character.species)
- else
- active_character.ha_style = "None" // No Vulp ears on Unathi
- active_character.hacc_colour = rand_hex_color()
-
- if(NS.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- active_character.m_styles["head"] = random_marking_style("head", active_character.species, robohead, null, active_character.alt_head)
- else
- active_character.m_styles["head"] = "None"
- active_character.m_colours["head"] = "#000000"
-
- if(NS.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
- active_character.m_styles["body"] = random_marking_style("body", active_character.species)
- else
- active_character.m_styles["body"] = "None"
- active_character.m_colours["body"] = "#000000"
-
- if(NS.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- active_character.m_styles["tail"] = random_marking_style("tail", active_character.species, null, active_character.body_accessory)
- else
- active_character.m_styles["tail"] = "None"
- active_character.m_colours["tail"] = "#000000"
-
- // Don't wear another species' underwear!
- var/datum/sprite_accessory/SA = GLOB.underwear_list[active_character.underwear]
- if(!SA || !(active_character.species in SA.species_allowed))
- active_character.underwear = random_underwear(active_character.body_type, active_character.species)
-
- SA = GLOB.undershirt_list[active_character.undershirt]
- if(!SA || !(active_character.species in SA.species_allowed))
- active_character.undershirt = random_undershirt(active_character.body_type, active_character.species)
-
- SA = GLOB.socks_list[active_character.socks]
- if(!SA || !(active_character.species in SA.species_allowed))
- active_character.socks = random_socks(active_character.body_type, active_character.species)
-
- //reset skin tone and colour
- if(NS.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
- random_skin_tone(active_character.species)
- else
- active_character.s_tone = 0
-
- if(!(NS.bodyflags & HAS_SKIN_COLOR))
- active_character.s_colour = "#000000"
-
- active_character.alt_head = "None" //No alt heads on species that don't have them.
- active_character.speciesprefs = 0 //My Vox tank shouldn't change how my future Grey talks.
- active_character.body_accessory = random_body_accessory(NS.name, NS.optional_body_accessory)
-
- //Reset prosthetics.
- active_character.organ_data = list()
- active_character.rlimb_data = list()
-
- if(!(NS.autohiss_basic_map))
- active_character.autohiss_mode = AUTOHISS_OFF
+ S = new NS.type()
+ active_character.species_subtype = "None"
+ active_character.age = clamp(active_character.age, S.min_age, S.max_age)
+ NS = GLOB.all_species[active_character.species_subtype] // Changing species resets subtype.
+ if(isnull(NS))
+ NS = GLOB.all_species[active_character.species]
+ subtype = new NS.type()
+ reset_styles(S)
+ if("species_subtype")
+ if(S.bodyflags & HAS_SPECIES_SUBTYPE)
+ var/new_subtype = tgui_input_list(user, "Choose your character's species subtype:", "Character Preference", S.allowed_species_subtypes)
+ if(isnull(new_subtype) || active_character.species_subtype == new_subtype)
+ return
+ active_character.species_subtype = new_subtype
+ var/datum/species/NS = GLOB.all_species[active_character.species_subtype]
+ if(isnull(NS))
+ NS = GLOB.all_species[active_character.species]
+ subtype = new NS.type()
+ reset_styles(subtype, S)
if("speciesprefs")
active_character.speciesprefs = !active_character.speciesprefs //Starts 0, so if someone clicks the button up top there, this won't be 0 anymore. If they click it again, it'll go back to 0.
if("language")
@@ -335,7 +293,7 @@
But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */
valid_hairstyles += hairstyle
else //If the user is not a species who can have robotic heads, use the default handling.
- if(active_character.species in SA.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list.
+ if((active_character.species in SA.species_allowed) || (subtype.name in SA.species_allowed)) //If the user's head is of a species the hairstyle allows, add it to the list.
valid_hairstyles += hairstyle
sortTim(valid_hairstyles, GLOBAL_PROC_REF(cmp_text_asc)) //this alphabetizes the list
@@ -387,7 +345,7 @@
var/list/valid_head_accessory_styles = list()
for(var/head_accessory_style in GLOB.head_accessory_styles_list)
var/datum/sprite_accessory/H = GLOB.head_accessory_styles_list[head_accessory_style]
- if(!(active_character.species in H.species_allowed))
+ if(!(active_character.species in H.species_allowed) && !(subtype.name in H.species_allowed))
continue
valid_head_accessory_styles += head_accessory_style
@@ -404,7 +362,7 @@
var/list/valid_alt_heads = list()
for(var/alternate_head in GLOB.alt_heads_list)
var/datum/sprite_accessory/alt_heads/head = GLOB.alt_heads_list[alternate_head]
- if(!(active_character.species in head.species_allowed))
+ if(!(active_character.species in head.species_allowed) && !(subtype.name in head.species_allowed))
continue
valid_alt_heads += alternate_head
@@ -423,7 +381,7 @@
var/list/valid_markings = list()
for(var/markingstyle in GLOB.marking_styles_list)
var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[markingstyle]
- if(!(active_character.species in M.species_allowed))
+ if(!(active_character.species in M.species_allowed) && !(subtype.name in M.species_allowed))
continue
if(M.marking_location != "head")
continue
@@ -468,7 +426,7 @@
var/list/valid_markings = list()
for(var/markingstyle in GLOB.marking_styles_list)
var/datum/sprite_accessory/M = GLOB.marking_styles_list[markingstyle]
- if(!(active_character.species in M.species_allowed))
+ if(!(active_character.species in M.species_allowed) && !(subtype.name in M.species_allowed))
continue
if(M.marking_location != "body")
continue
@@ -493,7 +451,7 @@
var/datum/sprite_accessory/body_markings/tail/M = GLOB.marking_styles_list[markingstyle]
if(M.marking_location != "tail")
continue
- if(!(active_character.species in M.species_allowed))
+ if(!(active_character.species in M.species_allowed) && !(subtype.name in M.species_allowed))
continue
if(!active_character.body_accessory)
if(M.tails_allowed)
@@ -524,7 +482,7 @@
var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B]
if(isnull(accessory)) // None
continue
- if(active_character.species in accessory.allowed_species)
+ if((active_character.species in accessory.allowed_species) || (subtype.name in accessory.allowed_species))
possible_body_accessories += B
if(S.optional_body_accessory)
possible_body_accessories += "None" //the only null entry should be the "None" option
@@ -590,7 +548,7 @@
continue
if(active_character.body_type == FEMALE && SA.body_type == MALE)
continue
- if(!(active_character.species in SA.species_allowed))
+ if(!(active_character.species in SA.species_allowed) && !(subtype.name in SA.species_allowed))
continue
valid_underwear[underwear] = GLOB.underwear_list[underwear]
sortTim(valid_underwear, GLOBAL_PROC_REF(cmp_text_asc))
@@ -602,7 +560,7 @@
var/list/valid_undershirts = list()
for(var/undershirt in GLOB.undershirt_list)
var/datum/sprite_accessory/SA = GLOB.undershirt_list[undershirt]
- if(!(active_character.species in SA.species_allowed))
+ if(!(active_character.species in SA.species_allowed) && !(subtype.name in SA.species_allowed))
continue
if(active_character.body_type == MALE && SA.body_type == FEMALE)
continue
@@ -619,7 +577,7 @@
var/list/valid_sockstyles = list()
for(var/sockstyle in GLOB.socks_list)
var/datum/sprite_accessory/SA = GLOB.socks_list[sockstyle]
- if(!(active_character.species in SA.species_allowed))
+ if(!(active_character.species in SA.species_allowed) && !(subtype.name in SA.species_allowed))
continue
if(active_character.body_type == MALE && SA.body_type == FEMALE)
continue
@@ -1327,3 +1285,74 @@
ShowChoices(user)
return TRUE
+
+/// Reset the styles what is available to the NS (new species) parameter. Parent Species is for subtype changing.
+/datum/preferences/proc/reset_styles(datum/species/NS, datum/species/parent_species)
+ var/datum/robolimb/robohead
+ if(!isnull(parent_species))
+ NS.bodyflags |= parent_species.bodyflags
+ if(NS.bodyflags & ALL_RPARTS)
+ var/head_model = "[!active_character.rlimb_data["head"] ? "Morpheus Cyberkinetics" : active_character.rlimb_data["head"]]"
+ robohead = GLOB.all_robolimbs[head_model]
+ //grab one of the valid hair styles for the newly chosen species
+ active_character.h_style = random_hair_style(active_character.gender, NS, robohead)
+
+ //grab one of the valid facial hair styles for the newly chosen species
+ active_character.f_style = random_facial_hair_style(active_character.gender, NS, robohead)
+
+ if(NS.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
+ active_character.ha_style = random_head_accessory(NS)
+ else
+ active_character.ha_style = "None" // No Vulp ears on Unathi
+ active_character.hacc_colour = rand_hex_color()
+
+ if(NS.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ active_character.m_styles["head"] = random_marking_style("head", NS, robohead, null, active_character.alt_head)
+ else
+ active_character.m_styles["head"] = "None"
+ active_character.m_colours["head"] = "#000000"
+
+ if(NS.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
+ active_character.m_styles["body"] = random_marking_style("body", NS)
+ else
+ active_character.m_styles["body"] = "None"
+ active_character.m_colours["body"] = "#000000"
+
+ if(NS.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ active_character.m_styles["tail"] = random_marking_style("tail", NS, null, active_character.body_accessory)
+ else
+ active_character.m_styles["tail"] = "None"
+ active_character.m_colours["tail"] = "#000000"
+
+ // Don't wear another species' underwear!
+ var/datum/sprite_accessory/SA = GLOB.underwear_list[active_character.underwear]
+ if(!SA || !(NS in SA.species_allowed))
+ active_character.underwear = random_underwear(active_character.body_type, NS)
+
+ SA = GLOB.undershirt_list[active_character.undershirt]
+ if(!SA || !(NS in SA.species_allowed))
+ active_character.undershirt = random_undershirt(active_character.body_type, NS)
+
+ SA = GLOB.socks_list[active_character.socks]
+ if(!SA || !(NS in SA.species_allowed))
+ active_character.socks = random_socks(active_character.body_type, NS)
+
+ //reset skin tone and colour
+ if(NS.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
+ random_skin_tone(NS)
+ else
+ active_character.s_tone = 0
+
+ if(!(NS.bodyflags & HAS_SKIN_COLOR))
+ active_character.s_colour = "#000000"
+
+ active_character.alt_head = "None" //No alt heads on species that don't have them.
+ active_character.speciesprefs = 0 //My Vox tank shouldn't change how my future Grey talks.
+ active_character.body_accessory = random_body_accessory(NS.name, NS.optional_body_accessory)
+
+ //Reset prosthetics.
+ active_character.organ_data = list()
+ active_character.rlimb_data = list()
+
+ if(!(NS.autohiss_basic_map))
+ active_character.autohiss_mode = AUTOHISS_OFF
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index f2315c513511..845164754e8c 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -207,6 +207,8 @@ GLOBAL_LIST_INIT(special_role_times, list(
dat += "Возраст:[active_character.age] "
dat += "Тело:(Случайно) "
dat += "Раса:[active_character.species] "
+ if(S.bodyflags & HAS_SPECIES_SUBTYPE)
+ dat += "Подтип расы:[active_character.species_subtype] "
dat += "Пол:[active_character.gender == MALE ? "Male" : (active_character.gender == FEMALE ? "Female" : "Genderless")] "
dat += "Тип тела:[active_character.body_type == MALE ? "Masculine" : "Feminine"]"
dat += " "
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index de04584338be..17285e8e9baa 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -106,10 +106,10 @@
if(H.dna.species)
if(exclusive)
- if(!(H.dna.species.name in species_restricted))
+ if(!(H.dna.species.sprite_sheet_name in species_restricted))
wearable = TRUE
else
- if(H.dna.species.name in species_restricted)
+ if(H.dna.species.sprite_sheet_name in species_restricted)
wearable = TRUE
if(!wearable)
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index eab7f769fb5d..880cd06c900f 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -64,7 +64,7 @@
return
var/datum/sprite_accessory/body_markings/tattoo/temp_tatt = GLOB.marking_styles_list[tattoo_icon]
- if(!(target.dna.species.name in temp_tatt.species_allowed))
+ if(!(target.dna.species.sprite_sheet_name in temp_tatt.species_allowed))
to_chat(user, "You can't think of a way to make the [tattoo_name] design work on [target == user ? "your" : "[target]'s"] body type.")
return
diff --git a/code/modules/economy/economy_machinery/eftpos.dm b/code/modules/economy/economy_machinery/eftpos.dm
index d1f5cf0a956a..67dc65f98860 100644
--- a/code/modules/economy/economy_machinery/eftpos.dm
+++ b/code/modules/economy/economy_machinery/eftpos.dm
@@ -6,6 +6,7 @@
icon = 'icons/obj/device.dmi'
icon_state = "eftpos"
w_class = WEIGHT_CLASS_SMALL
+ materials = list(MAT_METAL = 300, MAT_GLASS = 140)
/// Unique identifying name of this EFTPOS for transaction tracking in money accounts
var/machine_name = ""
/// Whether or not the EFTPOS is locked into a transaction
@@ -252,6 +253,7 @@
throw_range = 7
anchored = TRUE
w_class = WEIGHT_CLASS_BULKY
+ materials = list()
hitsound = 'sound/weapons/ringslam.ogg'
drop_sound = 'sound/items/handling/register_drop.ogg'
pickup_sound = 'sound/items/handling/toolbox_pickup.ogg'
diff --git a/code/modules/fish/fish_eggs.dm b/code/modules/fish/fish_eggs.dm
index 756093fbd013..de9c133aabf0 100644
--- a/code/modules/fish/fish_eggs.dm
+++ b/code/modules/fish/fish_eggs.dm
@@ -27,7 +27,7 @@
/obj/item/fish_eggs/babycarp
name = "baby space carp eggs"
- desc = "Eggs from the substantially smaller form of the intergalactic terror."
+ desc = "Eggs from the substantially smaller form of the spaceborn terror."
icon_state = "babycarp_eggs"
fish_type = /datum/fish/babycarp
diff --git a/code/modules/hallucinations/effects/blind_rush_hallucination.dm b/code/modules/hallucinations/effects/blind_rush_hallucination.dm
index 88676e30316c..ab19d0a12da4 100644
--- a/code/modules/hallucinations/effects/blind_rush_hallucination.dm
+++ b/code/modules/hallucinations/effects/blind_rush_hallucination.dm
@@ -48,16 +48,12 @@
var/rush_timer = null
/obj/effect/hallucination/no_delete/blind_rusher/Initialize(mapload, mob/living/carbon/target)
- rush_timer = addtimer(CALLBACK(src, PROC_REF(rush)), rush_time, TIMER_LOOP | TIMER_STOPPABLE)
+ rush_timer = addtimer(CALLBACK(src, PROC_REF(rush)), rush_time, TIMER_LOOP | TIMER_DELETE_ME)
if(prob(50))
hallucination_icon = 'icons/mob/simple_human.dmi'
hallucination_icon_state = pick("clown", "skeleton_warden", "skeleton_warden_alt")
return ..()
-/obj/effect/hallucination/no_delete/blind_rusher/Destroy()
- deltimer(rush_timer)
- return ..()
-
/obj/effect/hallucination/no_delete/blind_rusher/proc/rush()
if(get_dist(src, target) > min_distance)
var/direction = get_dir(src, target) //making sure the hallucination is facing the player correctly.
diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm
index 18cbb6991cbb..0dc9de25b337 100644
--- a/code/modules/mob/living/carbon/human/appearance.dm
+++ b/code/modules/mob/living/carbon/human/appearance.dm
@@ -389,14 +389,14 @@
continue
if(H.dna.species.bodyflags & ALL_RPARTS) //If the user is a species who can have a robotic head...
var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model]
- if((H.dna.species.name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
+ if((H.dna.species.sprite_sheet_name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
valid_hairstyles += hairstyle //Give them their hairstyles if they do.
else
if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */
valid_hairstyles += hairstyle
else //If the user is not a species who can have robotic heads, use the default handling.
- if(H.dna.species.name in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list.
+ if(H.dna.species.sprite_sheet_name in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list.
valid_hairstyles += hairstyle
return sortTim(valid_hairstyles, GLOBAL_PROC_REF(cmp_text_asc))
@@ -415,15 +415,15 @@
continue
if(H.dna.species.bodyflags & ALL_RPARTS) //If the user is a species who can have a robotic head...
var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model]
- if(H.dna.species.name in S.species_allowed) //If this is a facial hair style native to the user's species...
- if((H.dna.species.name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
+ if(H.dna.species.sprite_sheet_name in S.species_allowed) //If this is a facial hair style native to the user's species...
+ if((H.dna.species.sprite_sheet_name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do.
else
if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */
valid_facial_hairstyles += facialhairstyle
else //If the user is not a species who can have robotic heads, use the default handling.
- if(H.dna.species.name in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list.
+ if(H.dna.species.sprite_sheet_name in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list.
valid_facial_hairstyles += facialhairstyle
return sortTim(valid_facial_hairstyles, GLOBAL_PROC_REF(cmp_text_asc))
@@ -437,7 +437,7 @@
for(var/head_accessory in GLOB.head_accessory_styles_list)
var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory]
- if(!(H.dna.species.name in S.species_allowed)) //If the user's head is not of a species the head accessory style allows, skip it. Otherwise, add it to the list.
+ if(!(H.dna.species.sprite_sheet_name in S.species_allowed)) //If the user's head is not of a species the head accessory style allows, skip it. Otherwise, add it to the list.
continue
valid_head_accessories += head_accessory
@@ -456,7 +456,7 @@
continue
if(S.marking_location != location) //If the marking isn't for the location we desire, skip.
continue
- if(!(dna.species.name in S.species_allowed)) //If the user is not of a species the marking style allows, skip it. Otherwise, add it to the list.
+ if(!(dna.species.sprite_sheet_name in S.species_allowed)) //If the user is not of a species the marking style allows, skip it. Otherwise, add it to the list.
continue
if(location == "tail")
if(!body_accessory)
@@ -490,7 +490,7 @@
else if(check_rights(R_ADMIN, FALSE, src))
valid_body_accessories = GLOB.body_accessory_by_name.Copy()
break
- else if(dna.species.name in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list.
+ else if(dna.species.sprite_sheet_name in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list.
valid_body_accessories += B
if(dna.species.optional_body_accessory)
valid_body_accessories += "None"
@@ -505,7 +505,7 @@
valid_alt_heads["None"] = GLOB.alt_heads_list["None"] //The only null entry should be the "None" option, and there should always be a "None" option.
for(var/alternate_head in GLOB.alt_heads_list)
var/datum/sprite_accessory/alt_heads/head = GLOB.alt_heads_list[alternate_head]
- if(!(H.dna.species.name in head.species_allowed))
+ if(!(H.dna.species.sprite_sheet_name in head.species_allowed))
continue
valid_alt_heads += alternate_head
diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm
index 364626c0dffb..ca3adfbdcca6 100644
--- a/code/modules/mob/living/carbon/human/body_accessories.dm
+++ b/code/modules/mob/living/carbon/human/body_accessories.dm
@@ -50,7 +50,7 @@ GLOBAL_LIST_EMPTY(body_accessory_by_species)
var/has_behind = FALSE
/datum/body_accessory/proc/try_restrictions(mob/living/carbon/human/H)
- return (H.dna.species.name in allowed_species)
+ return (H.dna.species.sprite_sheet_name in allowed_species)
/datum/body_accessory/proc/get_animated_icon() //return animated if it has it, return static if it does not.
if(animated_icon)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 2762d3e63cfb..7993bcbdbbb4 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -20,6 +20,8 @@
var/list/m_styles = DEFAULT_MARKING_STYLES //All markings set to None.
var/s_tone = 0 //Skin tone
+ /// Species Sub-Type - overrites the species_sheet_name when it's not "None", acts the same as a skin tone.
+ var/species_subtype = "None"
//Skin colour
var/skin_colour = "#000000"
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 69ce8b857ba2..54ac27d05ffa 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -172,9 +172,15 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/hulk = HAS_TRAIT(src, TRAIT_HULK)
var/skeleton = HAS_TRAIT(src, TRAIT_SKELETONIZED)
- if(dna.species && dna.species.bodyflags & HAS_ICON_SKIN_TONE)
- dna.species.updatespeciescolor(src)
-
+ if(dna.species)
+ if(dna.species.bodyflags & HAS_ICON_SKIN_TONE)
+ dna.species.updatespeciescolor(src)
+ if(dna.species.bodyflags & HAS_SPECIES_SUBTYPE)
+ var/datum/species/species_subtype = GLOB.all_species[dna.species.species_subtype]
+ if(isnull(species_subtype))
+ species_subtype = GLOB.all_species[dna.species.name]
+ if(dna.species.species_subtype != "None" && dna.species.species_subtype != species_subtype.name)
+ dna.species.updatespeciessubtype(src, new species_subtype.type(), TRUE, FALSE)
//CACHING: Generate an index key from visible bodyparts.
//0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic.
//Create a new, blank icon for our mob to use.
@@ -195,7 +201,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
//BEGIN CACHED ICON GENERATION.
var/obj/item/organ/external/chest = get_organ("chest")
base_icon = chest.get_icon(skeleton)
-
+ var/slimify = istype(src.dna.species, /datum/species/slime) && species_subtype != "None"
for(var/obj/item/organ/external/part in bodyparts)
var/icon/temp = part.get_icon(skeleton)
//That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST
@@ -208,15 +214,19 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
temp2.Insert(new/icon(temp,dir=EAST),dir=EAST)
if(!(part.icon_position & RIGHT))
temp2.Insert(new/icon(temp,dir=WEST),dir=WEST)
+ slimify_limb(part, temp2, slimify)
base_icon.Blend(temp2, ICON_OVERLAY)
if(part.icon_position & LEFT)
temp2.Insert(new/icon(temp,dir=EAST),dir=EAST)
if(part.icon_position & RIGHT)
temp2.Insert(new/icon(temp,dir=WEST),dir=WEST)
+ slimify_limb(part, temp2, slimify)
base_icon.Blend(temp2, ICON_UNDERLAY)
else
+ slimify_limb(part, temp, slimify)
base_icon.Blend(temp, ICON_OVERLAY)
+
if(!skeleton)
if(isgolem(src))
var/datum/species/golem/G = src.dna.species
@@ -296,20 +306,26 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
if(chest_organ && m_styles["body"])
var/body_marking = m_styles["body"]
var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking]
- if(body_marking_style && body_marking_style.species_allowed && (dna.species.name in body_marking_style.species_allowed))
+ if(body_marking_style && body_marking_style.species_allowed && (dna.species.sprite_sheet_name in body_marking_style.species_allowed))
var/icon/b_marking_s = icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s")
if(body_marking_style.do_colouration)
b_marking_s.Blend(m_colours["body"], ICON_ADD)
+ if(istype(src.dna.species, /datum/species/slime))
+ b_marking_s.GrayScale()
+ b_marking_s.Blend("[skin_colour]A0", ICON_AND)
markings_standing.Blend(b_marking_s, ICON_OVERLAY)
//Head markings.
var/obj/item/organ/external/head/head_organ = get_organ("head")
if(istype(head_organ) && m_styles["head"]) //If the head is destroyed, forget the head markings. This prevents floating optical markings on decapitated IPCs, for example.
var/head_marking = m_styles["head"]
var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking]
- if(head_marking_style && head_marking_style.species_allowed && (head_organ.dna.species.name in head_marking_style.species_allowed))
+ if(head_marking_style && head_marking_style.species_allowed && (head_organ.dna.species.sprite_sheet_name in head_marking_style.species_allowed))
var/icon/h_marking_s = icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s")
if(head_marking_style.do_colouration)
h_marking_s.Blend(m_colours["head"], ICON_ADD)
+ if(istype(src.dna.species, /datum/species/slime))
+ h_marking_s.GrayScale()
+ h_marking_s.Blend("[skin_colour]A0", ICON_AND)
markings_standing.Blend(h_marking_s, ICON_OVERLAY)
overlays_standing[MARKINGS_LAYER] = mutable_appearance(markings_standing, layer = -MARKINGS_LAYER)
@@ -334,10 +350,13 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
if(head_organ.ha_style && (head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY))
var/datum/sprite_accessory/head_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style]
if(head_accessory_style && head_accessory_style.species_allowed)
- if(head_organ.dna.species.name in head_accessory_style.species_allowed)
+ if(head_organ.dna.species.sprite_sheet_name in head_accessory_style.species_allowed)
var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s")
if(head_accessory_style.do_colouration)
head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD)
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies head acc
+ head_accessory_s.GrayScale()
+ head_accessory_s.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
if(HAS_TRAIT(src, TRAIT_I_WANT_BRAINS))
head_accessory_s.ColorTone(COLORTONE_DEAD_EXT_ORGAN)
head_accessory_s.SetIntensity(0.7)
@@ -369,7 +388,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
MA.layer = -HAIR_LAYER
if(O.h_style && !(head?.flags & BLOCKHEADHAIR && !ismachineperson(src)))
var/datum/sprite_accessory/hair/hair = GLOB.hair_styles_full_list[O.h_style]
- if(hair?.species_allowed && ((O.dna.species.name in hair.species_allowed) || (O.dna.species.bodyflags & ALL_RPARTS)))
+ if(hair?.species_allowed && ((O.dna.species.sprite_sheet_name in hair.species_allowed) || (O.dna.species.bodyflags & ALL_RPARTS)))
// Base hair
var/icon/hair_icon = new /icon(hair.icon, "[hair.icon_state]_s")
if(HAS_TRAIT(src, TRAIT_I_WANT_BRAINS))
@@ -418,8 +437,8 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
return
var/species_name = ""
- if(dna.species.name in list("Drask", "Grey", "Vox", "Kidan"))
- species_name = "_[lowertext(dna.species.name)]"
+ if(dna.species.sprite_sheet_name in list("Drask", "Grey", "Vox", "Kidan"))
+ species_name = "_[lowertext(dna.species.sprite_sheet_name)]"
var/icon/hands_mask = icon('icons/mob/body_accessory.dmi', "accessory_none_s") //Needs a blank icon, not actually related to markings at all
@@ -461,7 +480,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
if(head_organ.f_style)
var/datum/sprite_accessory/facial_hair/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style]
if(facial_hair_style && facial_hair_style.species_allowed)
- if((head_organ.dna.species.name in facial_hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics...
+ if((head_organ.dna.species.sprite_sheet_name in facial_hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics...
var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s")
if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos
facial_s.Blend("[skin_colour]A0", ICON_AND)
@@ -1369,6 +1388,9 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
if(HAS_TRAIT(src, TRAIT_I_WANT_BRAINS))
wings_icon.ColorTone(COLORTONE_DEAD_EXT_ORGAN)
wings_icon.SetIntensity(0.7)
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the wings
+ wings_icon.GrayScale()
+ wings_icon.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
var/mutable_appearance/wings = mutable_appearance(wings_icon, layer = -WING_LAYER)
wings.pixel_x = body_accessory.pixel_x_offset
wings.pixel_y = body_accessory.pixel_y_offset
@@ -1379,6 +1401,9 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
if(HAS_TRAIT(src, TRAIT_I_WANT_BRAINS))
under_wing_icon.ColorTone(COLORTONE_DEAD_EXT_ORGAN)
under_wing_icon.SetIntensity(0.7)
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the wings
+ under_wing_icon.GrayScale()
+ under_wing_icon.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
var/mutable_appearance/under_wing = mutable_appearance(under_wing_icon, layer = -WING_UNDERLIMBS_LAYER)
under_wing.pixel_x = body_accessory.pixel_x_offset
under_wing.pixel_y = body_accessory.pixel_y_offset
@@ -1413,6 +1438,11 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
accessory_s.Blend(skin_colour, body_accessory.blend_mode)
if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed))
accessory_s.Blend(tail_marking_icon, ICON_OVERLAY)
+
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the body acc
+ accessory_s.GrayScale()
+ accessory_s.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha. Less alpha here because the tail is generally stubborn...
+
if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this)
// Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway.
var/icon/under = new/icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s")
@@ -1451,6 +1481,11 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
tail_s.Blend(skin_colour, ICON_ADD)
if(tail_marking_icon && !tail_marking_style.tails_allowed)
tail_s.Blend(tail_marking_icon, ICON_OVERLAY)
+
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the tail
+ tail_s.GrayScale()
+ tail_s.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
+
if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this)
// Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway.
var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "blank")
@@ -1490,13 +1525,17 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/icon/accessory_s = new/icon("icon" = body_accessory.get_animated_icon(), "icon_state" = body_accessory.get_animated_icon_state())
if(dna.species.bodyflags & HAS_SKIN_COLOR)
accessory_s.Blend(skin_colour, body_accessory.blend_mode)
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the wings
+ accessory_s.GrayScale()
+ accessory_s.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed))
accessory_s.Blend(tail_marking_icon, ICON_OVERLAY)
if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this)
// Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway.
var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay")
- if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species))
- under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay")
+
+ if(body_accessory.allowed_species && (dna.species.sprite_sheet_name in body_accessory.allowed_species))
+ under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.sprite_sheet_name]_tail_delay")
under.Insert(new/icon(accessory_s, dir=SOUTH), dir=SOUTH)
under.Insert(new/icon(accessory_s, dir=EAST), dir=EAST)
under.Insert(new/icon(accessory_s, dir=WEST), dir=WEST)
@@ -1508,8 +1547,8 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
// Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such.
var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay")
- if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species)) // If the user's species is in the list of allowed species for the currently selected body accessory, use the appropriate animation timing blank
- over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay")
+ if(body_accessory.allowed_species && (dna.species.sprite_sheet_name in body_accessory.allowed_species)) // If the user's species is in the list of allowed species for the currently selected body accessory, use the appropriate animation timing blank
+ over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.sprite_sheet_name]_tail_delay")
over.Insert(new/icon(accessory_s, dir=NORTH), dir=NORTH)
var/mutable_appearance/tail = mutable_appearance(over, layer = -TAIL_LAYER)
@@ -1526,11 +1565,14 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/icon/tailw_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]w_s")
if(dna.species.bodyflags & HAS_SKIN_COLOR)
tailw_s.Blend(skin_colour, ICON_ADD)
+ if(istype(src.dna.species, /datum/species/slime)) // Slimifies the tail
+ tailw_s.GrayScale()
+ tailw_s.Blend("[skin_colour]A0", ICON_AND) //DC = 160 alpha.
if(tail_marking_icon && !tail_marking_style.tails_allowed)
tailw_s.Blend(tail_marking_icon, ICON_OVERLAY)
if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this)
// Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway.
- var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay")
+ var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.sprite_sheet_name]_tail_delay")
under.Insert(new/icon(tailw_s, dir=SOUTH), dir=SOUTH)
under.Insert(new/icon(tailw_s, dir=EAST), dir=EAST)
under.Insert(new/icon(tailw_s, dir=WEST), dir=WEST)
@@ -1538,7 +1580,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
overlays_standing[TAIL_UNDERLIMBS_LAYER] = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER)
// Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such.
- var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay")
+ var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.sprite_sheet_name]_tail_delay")
over.Insert(new/icon(tailw_s, dir=NORTH), dir=NORTH)
overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER)
@@ -1695,3 +1737,10 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
. += "[part.s_tone]"
. = "[.][!!husk][!!hulk][!!skeleton]"
+
+/mob/living/carbon/human/proc/slimify_limb(obj/item/organ/external/L, icon/I, slimify)
+ if(slimify) // Used for a when slime morphs into another species. Makes them slightly transparent.
+ if(!L.is_robotic())
+ I.GrayScale()
+ var/color = sanitize_hexcolor(skin_colour)
+ I.Blend("[color]C8", ICON_AND) //DC = 220 alpha. C8 = 200
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index e371ddff8646..30cd8e7c62fa 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -3,6 +3,10 @@
var/name
/// Pluralized name (since "[name]s" is not always valid)
var/name_plural
+ /// Sub-type of the species. Used for when slimes imitate a species or when an IPC has augments that look like another species. This will affect sprite_sheet_name
+ var/species_subtype = "None"
+ /// List of available sub-types for the species to imitate / morph into (Machine / Slime)
+ var/allowed_species_subtypes = list()
/// The corresponding key for spritesheets
var/sprite_sheet_name
/// Article to use when referring to an individual of the species, if pronunciation is different from expected.
@@ -201,7 +205,9 @@
/datum/species/New()
unarmed = new unarmed_type()
- if(!sprite_sheet_name)
+ if(!isnull(species_subtype) && species_subtype != "None")
+ sprite_sheet_name = species_subtype
+ else if(!sprite_sheet_name)
sprite_sheet_name = name
/datum/species/proc/get_random_name(gender)
@@ -404,6 +410,17 @@
/datum/species/proc/updatespeciescolor(mob/living/carbon/human/H) //Handles changing icobase for species that have multiple skin colors.
return
+/** Handles changing icobase for species that can imitate/morph into other species
+ * Arguments:
+ * - H: The human of which was are updating.
+ * - new_subtype: Our imitate species, by datum reference.
+ * - owner_sensitive: Always leave at TRUE, this is for updating our icon. (change_icobase)
+ * - reset_styles: If true, resets styles, hair, and other appearance styles.
+ */
+///
+/datum/species/proc/updatespeciessubtype(mob/living/carbon/human/H, datum/species/new_subtype, owner_sensitive = TRUE, reset_styles = TRUE)
+ return
+
// Do species-specific reagent handling here
// Return 1 if it should do normal processing too
// Return the parent value if processing does not explicitly stop
diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm
index 58dd3eb2433e..8b971c5dbd81 100644
--- a/code/modules/mob/living/carbon/human/species/machine.dm
+++ b/code/modules/mob/living/carbon/human/species/machine.dm
@@ -24,7 +24,7 @@
inherent_traits = list(TRAIT_VIRUSIMMUNE, TRAIT_NOBREATH, TRAIT_NOGERMS, TRAIT_NODECAY, TRAIT_NOPAIN, TRAIT_GENELESS) //Computers that don't decay? What a lie!
inherent_biotypes = MOB_ROBOTIC | MOB_HUMANOID
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS | SHAVED
+ bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS | SHAVED | HAS_SPECIES_SUBTYPE
dietflags = 0 //IPCs can't eat, so no diet
taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE
blood_color = COLOR_BLOOD_MACHINE
@@ -75,8 +75,65 @@
"is blocking their ventilation port!")
plushie_type = /obj/item/toy/plushie/ipcplushie
+ allowed_species_subtypes = list(
+ 1 = "None",
+ 2 = "Vox",
+ 3 = "Unathi",
+ 4 = "Tajaran",
+ 5 = "Nian",
+ 6 = "Vulpkanin",
+ 7 = "Kidan",
+ 8 = "Grey",
+ 9 = "Drask"
+ )
+ var/static_bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS | SHAVED | HAS_SPECIES_SUBTYPE
+/datum/species/machine/updatespeciessubtype(mob/living/carbon/human/H, datum/species/new_subtype, owner_sensitive = TRUE, reset_styles = TRUE) //Handling species-subtype and imitation
+ if(H.dna.species.bodyflags & HAS_SPECIES_SUBTYPE)
+ var/datum/species/temp_species = new type()
+ if(isnull(new_subtype) || temp_species.name == new_subtype.name) // Back to our original species.
+ H.species_subtype = "None"
+ temp_species.species_subtype = "None" // Update our species subtype to match the Mob's subtype.
+ var/datum/species/S = GLOB.all_species[temp_species.name]
+ new_subtype = new S.type() // Resets back to original. We use initial in the case the datum is var edited.
+ else
+ H.species_subtype = new_subtype.name
+ temp_species.species_subtype = H.species_subtype // Update our species subtype to match the Mob's subtype.
+
+ // Copy over new species variables to our temp holder.
+ temp_species.icobase = new_subtype.icobase
+ temp_species.tail = new_subtype.tail
+ temp_species.wing = new_subtype.wing
+ temp_species.default_headacc = new_subtype.default_headacc
+ temp_species.default_bodyacc = new_subtype.default_bodyacc
+ temp_species.bodyflags = new_subtype.bodyflags
+ temp_species.bodyflags |= static_bodyflags // Add our static bodyflags that slime must always have.
+ temp_species.sprite_sheet_name = new_subtype.sprite_sheet_name
+ temp_species.icon_template = new_subtype.icon_template
+ // Set our DNA to the temp holder.
+ H.dna.species = temp_species
+
+ for(var/obj/item/organ/external/limb in H.bodyparts)
+ limb.icobase = temp_species.icobase // update their icobase for when we apply the slimfy effect
+ limb.dna.species = temp_species // Update limb to match our newly modified species
+ limb.set_company(limb.model, temp_species.sprite_sheet_name) // Robotic limbs always update to our new subtype.
+
+ // Update misc parts that are stored as reference in species and used on the mob. Also resets stylings to none to prevent anything wacky...
+
+ if(reset_styles)
+ H.body_accessory = GLOB.body_accessory_by_name[temp_species.default_bodyacc]
+ H.tail = temp_species.tail
+ H.wing = temp_species.wing
+ var/obj/item/organ/external/head/head = H.get_organ("head")
+ head.h_style = "Bald"
+ head.f_style = "Shaved"
+ head.ha_style = "None"
+ H.s_tone = 0
+ H.m_styles = DEFAULT_MARKING_STYLES //Wipes out markings, setting them all to "None".
+ H.m_colours = DEFAULT_MARKING_COLOURS //Defaults colour to #00000 for all markings.
+ H.change_head_accessory(GLOB.head_accessory_styles_list[temp_species.default_headacc])
+ H.change_icobase(temp_species.icobase, owner_sensitive) //Update the icobase of all our organs, but make sure we don't mess with frankenstein limbs in doing so.
/datum/species/machine/on_species_gain(mob/living/carbon/human/H)
..()
var/datum/action/innate/change_monitor/monitor = new()
@@ -165,7 +222,7 @@
var/list/hair = list()
for(var/i in GLOB.hair_styles_public_list)
var/datum/sprite_accessory/hair/tmp_hair = GLOB.hair_styles_public_list[i]
- if((head_organ.dna.species.name in tmp_hair.species_allowed) && (robohead.company in tmp_hair.models_allowed)) //Populate the list of available monitor styles only with styles that the monitor-head is allowed to use.
+ if((head_organ.dna.species.sprite_sheet_name in tmp_hair.species_allowed) && (robohead.company in tmp_hair.models_allowed)) //Populate the list of available monitor styles only with styles that the monitor-head is allowed to use.
hair += i
diff --git a/code/modules/mob/living/carbon/human/species/slimepeople.dm b/code/modules/mob/living/carbon/human/species/slimepeople.dm
index 8e97d4f5583e..31fafb60b8a9 100644
--- a/code/modules/mob/living/carbon/human/species/slimepeople.dm
+++ b/code/modules/mob/living/carbon/human/species/slimepeople.dm
@@ -6,6 +6,8 @@
#define SLIMEPERSON_MINHUNGER 250
#define SLIMEPERSON_REGROWTHDELAY 450 // 45 seconds
+#define SLIMEPERSON_MORPH_FORM 10 SECONDS
+
/datum/species/slime
name = "Slime People"
name_plural = "Slime People"
@@ -36,7 +38,7 @@
species_traits = list(LIPS, NO_CLONESCAN, EXOTIC_COLOR)
inherent_traits = list(TRAIT_WATERBREATH, TRAIT_NO_BONES)
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_COLOR | NO_EYES
+ bodyflags = HAS_SKIN_COLOR | NO_EYES | HAS_SPECIES_SUBTYPE
dietflags = DIET_CARN
reagent_tag = PROCESS_ORG
@@ -57,7 +59,20 @@
"is ripping out their own core!",
"is turning a dull, brown color and melting into a puddle!")
+ allowed_species_subtypes = list(
+ 1 = "None",
+ 2 = "Vox",
+ 3 = "Unathi",
+ 4 = "Tajaran",
+ 5 = "Nian",
+ 6 = "Vulpkanin",
+ 7 = "Kidan",
+ 8 = "Grey",
+ 9 = "Drask"
+ )
+
var/reagent_skin_coloring = FALSE
+ var/static_bodyflags = HAS_SKIN_COLOR | NO_EYES | HAS_SPECIES_SUBTYPE
plushie_type = /obj/item/toy/plushie/slimeplushie
@@ -67,6 +82,8 @@
grow.Grant(H)
var/datum/action/innate/slimecolor/recolor = new()
recolor.Grant(H)
+ var/datum/action/innate/morphform/reform = new()
+ reform.Grant(H)
RegisterSignal(H, COMSIG_HUMAN_UPDATE_DNA, PROC_REF(blend))
blend(H)
@@ -78,8 +95,63 @@
i.Remove(H)
if(istype(i, /datum/action/innate/regrow))
i.Remove(H)
+ if(istype(i, /datum/action/innate/morphform))
+ i.Remove(H)
UnregisterSignal(H, COMSIG_HUMAN_UPDATE_DNA)
+/datum/species/slime/updatespeciessubtype(mob/living/carbon/human/H, datum/species/new_subtype, owner_sensitive = TRUE, reset_styles = TRUE) //Handling species-subtype and imitation
+ if(H.dna.species.bodyflags & HAS_SPECIES_SUBTYPE)
+ var/datum/species/temp_species = new type()
+ if(isnull(new_subtype) || temp_species.name == new_subtype.name) // Back to our original species.
+ H.species_subtype = "None"
+ temp_species.species_subtype = "None" // Update our species subtype to match the Mob's subtype.
+ var/datum/species/S = GLOB.all_species[temp_species.name]
+ new_subtype = new S.type() // Resets back to original. We use initial in the case the datum is var edited.
+ else
+ H.species_subtype = new_subtype.name
+ temp_species.species_subtype = H.species_subtype // Update our species subtype to match the Mob's subtype.
+
+ // Copy over new species variables to our current species.
+ temp_species.icobase = new_subtype.icobase
+ temp_species.tail = new_subtype.tail
+ temp_species.wing = new_subtype.wing
+ temp_species.eyes = new_subtype.eyes
+ temp_species.scream_verb = new_subtype.scream_verb
+ temp_species.male_scream_sound = new_subtype.male_scream_sound
+ temp_species.female_scream_sound = new_subtype.female_scream_sound
+ temp_species.default_headacc = new_subtype.default_headacc
+ temp_species.default_bodyacc = new_subtype.default_bodyacc
+ temp_species.male_cough_sounds = new_subtype.male_cough_sounds
+ temp_species.female_cough_sounds = new_subtype.female_cough_sounds
+ temp_species.male_sneeze_sound = new_subtype.male_sneeze_sound
+ temp_species.female_sneeze_sound = new_subtype.female_sneeze_sound
+ temp_species.bodyflags = new_subtype.bodyflags
+ temp_species.bodyflags |= static_bodyflags // Add our static bodyflags that slime must always have.
+ temp_species.sprite_sheet_name = new_subtype.sprite_sheet_name
+ temp_species.icon_template = new_subtype.icon_template
+ H.dna.species = temp_species
+
+ for(var/obj/item/organ/external/limb in H.bodyparts)
+ limb.icobase = temp_species.icobase // update their icobase for when we apply the slimfy effect
+ limb.dna.species = temp_species // Update limb to match our newly modified species
+ limb.set_company(limb.model, temp_species.sprite_sheet_name) // Robotic limbs always update to our new subtype.
+
+ // Update misc parts that are stored as reference in species and used on the mob. Also resets stylings to none to prevent anything wacky...
+
+ if(reset_styles)
+ H.body_accessory = GLOB.body_accessory_by_name[default_bodyacc]
+ H.tail = temp_species.tail
+ H.wing = temp_species.wing
+ var/obj/item/organ/external/head/head = H.get_organ("head")
+ head.h_style = "Bald"
+ head.f_style = "Shaved"
+ head.ha_style = "None"
+ H.s_tone = 0
+ H.m_styles = DEFAULT_MARKING_STYLES //Wipes out markings, setting them all to "None".
+ H.m_colours = DEFAULT_MARKING_COLOURS //Defaults colour to #00000 for all markings.
+ H.change_head_accessory(GLOB.head_accessory_styles_list[default_headacc])
+ H.change_icobase(temp_species.icobase, owner_sensitive) //Update the icobase of all our organs, but make sure we don't mess with frankenstein limbs in doing so.
+
/datum/species/slime/proc/blend(mob/living/carbon/human/H)
var/new_color = BlendRGB(H.skin_colour, "#acacac", 0.5) // Blends this to make it work better
if(H.dna.species.blood_color != new_color) // Put here, so if it's a roundstart, dyed, or CMA'd slime, their blood changes to match skin
@@ -102,8 +174,6 @@
blend(H)
..()
-
-
/datum/species/slime/can_hear(mob/living/carbon/human/H) // fucking snowflakes
. = FALSE
if(!HAS_TRAIT(H, TRAIT_DEAF))
@@ -201,6 +271,35 @@
else
to_chat(H, "You need to hold still in order to regrow a limb!")
+/datum/action/innate/morphform
+ name = "Morph Form"
+ check_flags = AB_CHECK_CONSCIOUS
+ button_overlay_icon = 'icons/effects/effects.dmi'
+ button_overlay_icon_state = "acid"
+
+/datum/action/innate/morphform/Activate()
+
+ var/mob/living/carbon/human/H = owner
+ if(H.nutrition < SLIMEPERSON_MINHUNGER)
+ to_chat(H, "You're too hungry to morph your form!")
+ return
+ var/new_subtype = tgui_input_list(H, "Choose a species to imitate", "Select Subtype", H.dna.species.allowed_species_subtypes)
+ if(H.species_subtype == new_subtype)
+ return to_chat(H, "You stand there as your body shifts and then returns to its original form.")
+ H.visible_message("[H] begins to hold still and concentrate on [H.p_their()] form as it begins to shift and contort...", "You begin to focus on changing your form... (This will take [round(SLIMEPERSON_MORPH_FORM/10)] seconds, and you must hold still.)")
+ if(do_after(H, SLIMEPERSON_MORPH_FORM, FALSE, H, extra_checks = list(CALLBACK(H, TYPE_PROC_REF(/mob/living, IsStunned))), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned
+ if(H.nutrition < SLIMEPERSON_MINHUNGER)
+ to_chat(H, "You're too hungry to morph your form!")
+ return
+ var/datum/species/species_subtype = GLOB.all_species[new_subtype]
+ if(isnull(species_subtype))
+ species_subtype = GLOB.all_species[H.dna.species.name]
+ H.dna.species.updatespeciessubtype(H, new species_subtype.type())
+ H.regenerate_icons()
+ H.adjust_nutrition(-SLIMEPERSON_HUNGERCOST)
+ else
+ to_chat(H, "You need to hold still in order to shift your form!")
+
#undef SLIMEPERSON_COLOR_SHIFT_TRIGGER
#undef SLIMEPERSON_ICON_UPDATE_PERIOD
#undef SLIMEPERSON_BLOOD_SCALING_FACTOR
@@ -208,3 +307,5 @@
#undef SLIMEPERSON_HUNGERCOST
#undef SLIMEPERSON_MINHUNGER
#undef SLIMEPERSON_REGROWTHDELAY
+
+#undef SLIMEPERSON_MORPH_FORM
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index 3abfbf56dc3f..07b75d1cb4a3 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -120,7 +120,7 @@
/mob/living/simple_animal/pet/dog/corgi/UnarmedAttack(atom/A)
if(istype(inventory_back, /obj/item/extinguisher))
var/obj/item/extinguisher/E = inventory_back
- if(E.AttemptRefill(A, src))
+ if(E.attempt_refill(A, src))
return
return ..()
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
index 2efd024af583..c5fcbd9c84d4 100644
--- a/code/modules/mod/modules/modules_engineering.dm
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -159,49 +159,31 @@
/// Atmos water tank module
+#define EXTINGUISHER 0
+#define NANOFROST 1
+#define METAL_FOAM 2
+
/obj/item/mod/module/firefighting_tank
name = "MOD firefighting tank"
- desc = "A refrigerated and pressurized module tank with an extinguisher nozzle, intended to fight fires. Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time."
+ desc = "A refrigerated and pressurized module tank with an extinguisher nozzle, intended to fight fires. \
+ Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time.\
+ The smaller volume compared to a dedicated firefighting backpack means that non-water modes suffer from longer cooldowns."
icon_state = "firefighting_tank"
module_type = MODULE_ACTIVE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 3
- device = /obj/item/extinguisher/mini/mod
-
-#define EXTINGUISHER 0
-#define NANOFROST 1
-#define METAL_FOAM 2
+ incompatible_modules = list(/obj/item/mod/module/firefighting_tank)
+ device = /obj/item/extinguisher/mini/nozzle/mod
+ // Used by nozzle code.
+ var/volume = 500
-/obj/item/extinguisher/mini/mod
+/obj/item/extinguisher/mini/nozzle/mod
name = "modsuit extinguisher nozzle"
desc = "A heavy duty nozzle attached to a modsuit's internal tank."
- icon = 'icons/obj/watertank.dmi'
- icon_state = "atmos_nozzle_1"
- item_state = "nozzleatmos"
- safety = 0
- max_water = 500
- precision = 1
- cooling_power = 5
- w_class = WEIGHT_CLASS_HUGE
- flags = NODROP // Necessary to ensure that the nozzle and tank never seperate
- var/nozzle_mode = EXTINGUISHER
- var/metal_synthesis_charge = 5
- COOLDOWN_DECLARE(nanofrost_cooldown)
+ metal_regen_time = 5 SECONDS
+ nanofrost_cooldown_time = 10 SECONDS
-/obj/item/extinguisher/mini/mod/attack_self__legacy__attackchain(mob/user)
- switch(nozzle_mode)
- if(EXTINGUISHER)
- nozzle_mode = NANOFROST
- to_chat(user, "Swapped to nanofrost launcher.")
- if(NANOFROST)
- nozzle_mode = METAL_FOAM
- to_chat(user, "Swapped to metal foam synthesizer.")
- if(METAL_FOAM)
- nozzle_mode = EXTINGUISHER
- to_chat(user, "Swapped to water extinguisher.")
- update_icon(UPDATE_ICON_STATE)
-
-/obj/item/extinguisher/mini/mod/update_icon_state()
+/obj/item/extinguisher/mini/nozzle/mod/update_icon_state()
switch(nozzle_mode)
if(EXTINGUISHER)
icon_state = "atmos_nozzle_1"
@@ -210,58 +192,6 @@
if(METAL_FOAM)
icon_state = "atmos_nozzle_3"
-/obj/item/extinguisher/mini/mod/examine(mob/user)
- . = ..()
- switch(nozzle_mode)
- if(EXTINGUISHER)
- . += "[src] is currently set to extinguishing mode."
- if(NANOFROST)
- . += "[src] is currently set to nanofrost mode."
- if(METAL_FOAM)
- . += "[src] is currently set to metal foam mode."
-
-/obj/item/extinguisher/mini/mod/afterattack__legacy__attackchain(atom/target, mob/user)
- var/is_adjacent = user.Adjacent(target)
- if(is_adjacent && AttemptRefill(target, user))
- return
- switch(nozzle_mode)
- if(EXTINGUISHER)
- ..()
-
- if(NANOFROST)
- if(reagents.total_volume < 100)
- to_chat(user, "You need at least 100 units of water to use the nanofrost launcher!")
- return
- if(!COOLDOWN_FINISHED(src, nanofrost_cooldown))
- to_chat(user, "Nanofrost launcher is still recharging.")
- return
- COOLDOWN_START(src, nanofrost_cooldown, 10 SECONDS)
- reagents.remove_any(100)
- var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src))
- log_game("[key_name(user)] used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).")
- playsound(src, 'sound/items/syringeproj.ogg', 40, TRUE)
- A.throw_at(target, 6, 2, user)
- sleep(2)
- A.Smoke()
-
- if(METAL_FOAM)
- if(!is_adjacent|| !isturf(target))
- return
- if(metal_synthesis_charge <= 0)
- to_chat(user, "Metal foam mix is still being synthesized.")
- return
- if(reagents.total_volume < 10)
- to_chat(user, "You need at least 10 units of water to use the metal foam synthesizer!")
- return
- var/obj/effect/particle_effect/foam/metal/F = new /obj/effect/particle_effect/foam/metal(get_turf(target), TRUE)
- F.spread_amount = 0
- reagents.remove_any(10)
- metal_synthesis_charge--
- addtimer(CALLBACK(src, PROC_REF(decrease_metal_charge)), 5 SECONDS)
-
-/obj/item/extinguisher/mini/mod/proc/decrease_metal_charge()
- metal_synthesis_charge++
-
#undef EXTINGUISHER
#undef NANOFROST
#undef METAL_FOAM
diff --git a/code/modules/projectiles/guns/medbeam.dm b/code/modules/projectiles/guns/medbeam.dm
index eb1ab9d1702a..992051934a29 100644
--- a/code/modules/projectiles/guns/medbeam.dm
+++ b/code/modules/projectiles/guns/medbeam.dm
@@ -12,6 +12,7 @@
var/max_range = 8
var/active = FALSE
var/beam_UID = null
+ var/mounted = FALSE
weapon_weight = WEAPON_MEDIUM
@@ -43,7 +44,8 @@
current_target = null
/obj/item/gun/medbeam/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override)
- add_fingerprint(user)
+ if(isliving(user) && isrobot(user))
+ add_fingerprint(user)
if(current_target)
LoseTarget()
@@ -60,7 +62,7 @@
/obj/item/gun/medbeam/process()
var/source = loc
- if(!ishuman(source) && !isrobot(source))
+ if(!mounted && !ishuman(source) && !isrobot(source))
LoseTarget()
return
@@ -75,20 +77,25 @@
if(get_dist(source,current_target)>max_range || !los_check(source,current_target))
LoseTarget()
- to_chat(source, "You lose control of the beam!")
+ if(ishuman(source) && isrobot(source))
+ to_chat(source, "You lose control of the beam!")
return
if(current_target)
on_beam_tick(source, current_target)
-/obj/item/gun/medbeam/proc/los_check(mob/user,mob/target)
+/obj/item/gun/medbeam/proc/los_check(atom/movable/user, mob/target)
var/turf/user_turf = user.loc
var/datum/beam/current_beam = locateUID(beam_UID)
- if(!istype(user_turf))
+ if(mounted)
+ user_turf = get_turf(user)
+ else if(!istype(user_turf))
return FALSE
var/obj/dummy = new(user_turf)
dummy.pass_flags |= PASSTABLE | PASSGLASS | PASSGRILLE | PASSFENCE //Grille/Glass so it can be used through common windows
for(var/turf/turf in get_line(user_turf,target))
+ if(mounted && turf == user_turf)
+ continue //Mechs are dense and thus fail the check
if(turf.density)
qdel(dummy)
return FALSE
@@ -111,7 +118,7 @@
H.adjustBruteLoss(-4, robotic = TRUE)
H.adjustFireLoss(-4, robotic = TRUE)
for(var/obj/item/organ/external/E in H.bodyparts)
- if(prob(10))
+ if(!mounted && prob(10))
E.mend_fracture()
E.fix_internal_bleeding()
E.fix_burn_wound()
@@ -360,3 +367,11 @@
name = "beamgun cell"
desc = "A cell that fell out of a beamgun. It cannot be reused until fully charged. Only this brand of battery is compatible with medical beamguns."
starting_charge = 0
+
+//////////////////////////////Mech Version///////////////////////////////
+/obj/item/gun/medbeam/mech
+ mounted = TRUE
+
+/obj/item/gun/medbeam/mech/Initialize(mapload)
+ . = ..()
+ STOP_PROCESSING(SSobj, src) //Mech mediguns do not process until installed, and are controlled by the holder obj
diff --git a/code/modules/reagents/chemistry/reagents/misc_reagents.dm b/code/modules/reagents/chemistry/reagents/misc_reagents.dm
index f39583d7136f..29f8e1964479 100644
--- a/code/modules/reagents/chemistry/reagents/misc_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/misc_reagents.dm
@@ -359,8 +359,8 @@
var/obj/item/organ/external/head/head_organ = H.get_organ("head")
if(!istype(head_organ))
return ..()
- head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
- head_organ.f_style = random_facial_hair_style(H.gender, head_organ.dna.species.name)
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
+ head_organ.f_style = random_facial_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
H.update_hair()
H.update_fhair()
..()
@@ -383,14 +383,14 @@
var/datum/sprite_accessory/tmp_hair_style = GLOB.hair_styles_full_list["Very Long Hair"]
var/datum/sprite_accessory/tmp_facial_hair_style = GLOB.facial_hair_styles_list["Very Long Beard"]
- if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them.
+ if(head_organ.dna.species.sprite_sheet_name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them.
head_organ.h_style = "Very Long Hair"
else //Otherwise, give them a random hair style.
- head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
- if(head_organ.dna.species.name in tmp_facial_hair_style.species_allowed) //If 'Very Long Beard' is a style the person's species can have, give it to them.
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
+ if(head_organ.dna.species.sprite_sheet_name in tmp_facial_hair_style.species_allowed) //If 'Very Long Beard' is a style the person's species can have, give it to them.
head_organ.f_style = "Very Long Beard"
else //Otherwise, give them a random facial hair style.
- head_organ.f_style = random_facial_hair_style(H.gender, head_organ.dna.species.name)
+ head_organ.f_style = random_facial_hair_style(H.gender, head_organ.dna.species.sprite_sheet_name)
H.update_hair()
H.update_fhair()
if(!H.wear_mask || H.wear_mask && !istype(H.wear_mask, /obj/item/clothing/mask/fakemoustache))
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 41e9955e16e6..24160309477c 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -186,6 +186,7 @@
materials = list(MAT_METAL = 500, MAT_GLASS = 500)
build_path = /obj/item/clothing/ears/earmuffs
category = list("initial", "Miscellaneous")
+
/datum/design/painter
name = "Modular Painter"
id = "mod_painter"
@@ -194,6 +195,14 @@
build_path = /obj/item/painter
category = list("initial", "Miscellaneous")
+/datum/design/eftpos
+ name = "EFTPOS"
+ id = "eftpos"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 300, MAT_GLASS = 140)
+ build_path = /obj/item/eftpos
+ category = list("initial","Miscellaneous")
+
/datum/design/metal
name = "Metal"
id = "metal"
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index fb709fa9dc98..a57fcea27ede 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -736,6 +736,16 @@
construction_time = 20 SECONDS
category = list("Exosuit Equipment")
+/datum/design/mech_medical_beamgun
+ name = "Exosuit Medical Equipment (Medical Beamgun)"
+ id = "mech_medi_beam"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam
+ req_tech = list("combat" = 5, "materials" = 7, "powerstorage" = 7, "biotech" = 7)
+ materials = list(MAT_METAL=15000,MAT_GLASS=8000,MAT_PLASMA=3000,MAT_GOLD=8000,MAT_DIAMOND=2000)
+ construction_time = 20 SECONDS
+ category = list("Exosuit Equipment")
+
/datum/design/mech_mop
name = "Exosuit Janitorial Equipment (Mega Mop)"
id = "mech_mop"
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 85e276091210..0217b23f5a25 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -196,8 +196,8 @@ GLOBAL_LIST_EMPTY(ert_request_messages)
M.change_eye_color(eye_c)
M.s_tone = skin_tone
head_organ.headacc_colour = pick("#1f138b", "#272525", "#07a035", "#8c00ff", "#a80c0c")
- head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name)
- head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name)
+ head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.sprite_sheet_name)
+ head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.sprite_sheet_name)
M.rename_character(null, "[pick("Капрал", "Сержант", "Старший Сержант", "Сержант 1-го Класса", "Мастер-Сержант", "Сержант-Майор")] [pick(GLOB.last_names)]")
M.age = rand(23,35)
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index a93f3dc1c2f8..f42e7eda4524 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -205,7 +205,7 @@
return
ERT_Announce(ert_reason, triggered_by, repeat_warning = FALSE)
- addtimer(CALLBACK(src, PROC_REF(remind_admins), ert_reason, triggered_by), 15 MINUTES)
+ addtimer(CALLBACK(src, PROC_REF(remind_admins), ert_reason, triggered_by), 5 MINUTES)
ert_reason = null
/obj/machinery/keycard_auth/proc/remind_admins(old_reason, the_triggerer) // im great at naming variables
diff --git a/code/modules/space_management/heap_space_level.dm b/code/modules/space_management/heap_space_level.dm
deleted file mode 100644
index 666bfa9fa821..000000000000
--- a/code/modules/space_management/heap_space_level.dm
+++ /dev/null
@@ -1,62 +0,0 @@
-// This represents a level we can carve up as we please, and hand out
-// chunks of to whatever requests it
-/datum/space_level/heap
- name = "Heap level #ERROR"
- var/datum/space_chunk/top
- linkage = UNAFFECTED
-
-/datum/space_level/heap/New(z, name, transition_type, traits)
- ..(z, "Heap level #[z]", UNAFFECTED, traits)
- top = new(1, 1, zpos, world.maxx, world.maxy)
- flags = traits
-
-/datum/space_level/heap/proc/request(width, height)
- return top.can_fit_space(width, height)
-
-// Returns a space chunk datum for some nerd to work with - tells them what's safe to write into, and such
-/datum/space_level/heap/proc/allocate(width, height)
- if(!request(width, height))
- return null
-
- var/datum/space_chunk/C = top.get_optimal_chunk(width, height)
- if(!C)
- return null
- C.children.Cut()
-
- if(C.width == width && C.height == height && C.is_empty)
- C.set_occupied(TRUE)
- return C
-
- // Split the chunk into 4 pieces
- var/datum/space_chunk/return_chunk = new(C.x, C.y, C.zpos, width, height)
- C.children += return_chunk
- C.children += new /datum/space_chunk(C.x+width, C.y, C.zpos, C.width-width, height)
- C.children += new /datum/space_chunk(C.x, C.y+height, C.zpos, width, C.height-height)
- C.children += new /datum/space_chunk(C.x+width, C.y+height, C.zpos, C.width-width, C.height-height)
-
- for(var/datum/space_chunk/C2 in C.children)
- C2.parent = C
- return_chunk.set_occupied(TRUE)
- return return_chunk
-
-/datum/space_level/heap/proc/free(datum/space_chunk/C)
- if(!istype(C))
- return
- if(C.zpos != zpos)
- return
- C.set_occupied(FALSE)
- for(var/turf/T in block(C.x, C.y, C.zpos, C.x + C.width - 1, C.y + C.height - 1, C.zpos))
- for(var/atom/movable/M in T)
- if(isobserver(M))
- continue
- M.loc = null
- qdel(M, TRUE)
- T.ChangeTurf(T.baseturf)
- var/datum/space_chunk/last_empty_parent = C
- while(last_empty_parent.parent && last_empty_parent.parent.is_empty)
- last_empty_parent = last_empty_parent.parent
- // Prevent a self-qdel from killing this proc
- src = null
- for(var/datum/space_chunk/child in last_empty_parent.children)
- qdel(child)
- last_empty_parent.children.Cut()
diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm
index 0634d9299902..aa02bdaa44c1 100644
--- a/code/modules/space_management/level_traits.dm
+++ b/code/modules/space_management/level_traits.dm
@@ -28,6 +28,8 @@
/proc/level_boosts_signal(z)
return check_level_trait(z, BOOSTS_SIGNAL)
+#define is_reserved_level(z) check_level_trait(z, ZTRAIT_RESERVED)
+
// Used for the nuke disk, or for checking if players survived through xenos
/proc/is_secure_level(z)
var/secure = check_level_trait(z, STATION_LEVEL)
diff --git a/code/modules/space_management/turf_reservation.dm b/code/modules/space_management/turf_reservation.dm
new file mode 100644
index 000000000000..febe4b34bba4
--- /dev/null
+++ b/code/modules/space_management/turf_reservation.dm
@@ -0,0 +1,153 @@
+/*
+ * Turf reservations are used to reserve specific areas of the reserved z-levels for various purposes, typically for late-loading maps.
+ * It ensures that reserved turfs are properly managed and can be released when no longer needed.
+ * Reservations are not automatically released, so they must be manually released when no longer needed.
+ *
+ * Usage:
+ * - To create a new reservation, call the `request_turf_block_reservation(width, height)` method from the mapping subsystem.
+ * - This will return a new instance of /datum/turf_reservation if the reservation is successful.
+ *
+ * Releasing:
+ * - Call the `Release` method on the /datum/turf_reservation instance to release the reserved turfs and cordon turfs.
+ * - This will return the used turfs to the mapping subsystem to allow for reuse of the turfs.
+ */
+
+/datum/turf_reservation
+ /// All turfs that we've reserved
+ var/list/reserved_turfs = list()
+
+ /// Turfs around the reservation for cordoning
+ var/list/cordon_turfs = list()
+
+ /// Area of turfs next to the cordon to fill with pre_cordon_area's
+ var/list/pre_cordon_turfs = list()
+
+ /// The width of the reservation
+ var/width = 0
+
+ /// The height of the reservation
+ var/height = 0
+
+ /// Bottom left turf of the reservation
+ var/turf/bottom_left_turf
+
+ /// Top right turf of the reservation
+ var/turf/top_right_turf
+
+ /// The turf type the reservation is initially made with
+ var/turf_type = /turf/space
+
+/datum/turf_reservation/New()
+ LAZYADD(SSmapping.turf_reservations, src)
+
+/datum/turf_reservation/Destroy()
+ Release()
+ LAZYREMOVE(SSmapping.turf_reservations, src)
+ return ..()
+
+/datum/turf_reservation/proc/Release()
+ bottom_left_turf = null
+ top_right_turf = null
+
+ var/list/reserved_copy = reserved_turfs.Copy()
+ SSmapping.used_turfs -= reserved_turfs
+ reserved_turfs = list()
+
+ var/list/cordon_copy = cordon_turfs.Copy()
+ SSmapping.used_turfs -= cordon_turfs
+ cordon_turfs = list()
+
+ var/release_turfs = reserved_copy + cordon_copy
+
+ SSmapping.unreserve_turfs(release_turfs)
+
+/// Attempts to calaculate and store a list of turfs around the reservation for cordoning. Returns whether a valid cordon was calculated
+/datum/turf_reservation/proc/calculate_cordon_turfs(turf/bottom_left, turf/top_right)
+ if(bottom_left.x < 2 || bottom_left.y < 2 || top_right.x > (world.maxx - 2) || top_right.y > (world.maxy - 2))
+ return FALSE // no space for a cordon here
+
+ var/list/possible_turfs = CORNER_OUTLINE(bottom_left, width, height)
+ // if they're our cordon turfs, accept them
+ possible_turfs -= cordon_turfs
+ for(var/turf/cordon_turf as anything in possible_turfs)
+ // if we changed this to check reservation turf instead of not unused, we could have overlapping cordons.
+ // Unfortunately, that means adding logic for cordons not being removed if they have multiple edges and I'm lazy
+ if(!(cordon_turf.turf_flags & UNUSED_RESERVATION_TURF))
+ return FALSE
+ cordon_turfs |= possible_turfs
+
+ return TRUE
+
+/// Actually generates the cordon around the reservation, and marking the cordon turfs as reserved
+/datum/turf_reservation/proc/generate_cordon()
+ for(var/turf/cordon_turf as anything in cordon_turfs)
+ var/area/cordon/cordon_area = GLOB.all_unique_areas[/area/cordon] || new /area/cordon
+
+ cordon_area.contents += cordon_turf
+
+ // Its no longer unused, but its also not "used"
+ cordon_turf.turf_flags &= ~UNUSED_RESERVATION_TURF
+ cordon_turf.empty(/turf/cordon)
+ SSmapping.unused_turfs["[cordon_turf.z]"] -= cordon_turf
+ // still gets linked to us though
+ SSmapping.used_turfs[cordon_turf] = src
+
+/// Internal proc which handles reserving the area for the reservation.
+/datum/turf_reservation/proc/_reserve_area(width, height, zlevel)
+ src.width = width
+ src.height = height
+ if(width > world.maxx || height > world.maxy || width < 1 || height < 1)
+ return FALSE
+ var/list/avail = SSmapping.unused_turfs["[zlevel]"]
+ var/turf/bottom_left
+ var/turf/top_right
+ var/list/turf/final_turfs = list()
+ var/passing = FALSE
+ for(var/i in avail)
+ CHECK_TICK
+ bottom_left = i
+ if(!(bottom_left.turf_flags & UNUSED_RESERVATION_TURF))
+ continue
+ if(bottom_left.x + width > world.maxx || bottom_left.y + height > world.maxy)
+ continue
+ top_right = locate(bottom_left.x + width - 1, bottom_left.y + height - 1, bottom_left.z)
+ if(!(top_right.turf_flags & UNUSED_RESERVATION_TURF))
+ continue
+ final_turfs = block(bottom_left, top_right)
+ if(!final_turfs)
+ continue
+ passing = TRUE
+ for(var/I in final_turfs)
+ var/turf/checking = I
+ if(!(checking.turf_flags & UNUSED_RESERVATION_TURF))
+ passing = FALSE
+ break
+ if(passing) // found a potentially valid area, now try to calculate its cordon
+ passing = calculate_cordon_turfs(bottom_left, top_right)
+ if(!passing)
+ continue
+ break
+ if(!passing || !istype(bottom_left) || !istype(top_right))
+ return FALSE
+ for(var/turf/T as anything in final_turfs)
+ reserved_turfs |= T
+ SSmapping.unused_turfs["[T.z]"] -= T
+ SSmapping.used_turfs[T] = src
+ T.turf_flags = (T.turf_flags | RESERVATION_TURF) & ~UNUSED_RESERVATION_TURF
+ T.empty(turf_type)
+
+ bottom_left_turf = bottom_left
+ top_right_turf = top_right
+ return TRUE
+
+/datum/turf_reservation/proc/reserve(width, height, z_reservation)
+
+ if(!_reserve_area(width, height, z_reservation))
+ log_debug("Failed turf reservation: releasing")
+ Release()
+ return FALSE
+
+ log_debug("Turf reservation successful, generating cordon")
+ generate_cordon()
+ return TRUE
+
diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm
index d2364652ee6a..4887257493c9 100644
--- a/code/modules/space_management/zlevel_manager.dm
+++ b/code/modules/space_management/zlevel_manager.dm
@@ -134,42 +134,3 @@ GLOBAL_DATUM_INIT(space_manager, /datum/zlev_manager, new())
z_list.Remove(S)
qdel(S)
world.maxz--
-
-
-// An internally-used proc used for heap-zlevel management
-/datum/zlev_manager/proc/add_new_heap()
- world.maxz++
- var/our_z = world.maxz
- var/datum/space_level/yup = new /datum/space_level/heap(our_z, traits = list(BLOCK_TELEPORT, ADMIN_LEVEL))
- z_list["[our_z]"] = yup
- return yup
-
-// This is what you can call to allocate a section of space
-// Later, I'll add an argument to let you define the flags on the region
-/datum/zlev_manager/proc/allocate_space(width, height)
- if(width > world.maxx || height > world.maxy)
- throw EXCEPTION("Too much space requested! \[[width],[height]\]")
- if(!length(heaps))
- heaps.len++
- heaps[length(heaps)] = add_new_heap()
- var/datum/space_level/heap/our_heap
- var/weve_got_vacancy = 0
- for(our_heap in heaps)
- weve_got_vacancy = our_heap.request(width, height)
- if(weve_got_vacancy)
- break // We're sticking with the present value of `our_heap` - it's got room
- // This loop will also run out if no vacancies are found
-
- if(!weve_got_vacancy)
- heaps.len++
- our_heap = add_new_heap()
- heaps[length(heaps)] = our_heap
- return our_heap.allocate(width, height)
-
-/datum/zlev_manager/proc/free_space(datum/space_chunk/C)
- if(!istype(C))
- return
- var/datum/space_level/heap/heap = z_list["[C.zpos]"]
- if(!istype(heap))
- throw EXCEPTION("Attempted to free chunk at invalid z-level ([C.x],[C.y],[C.zpos]) [C.width]x[C.height]")
- heap.free(C)
diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm
index a26301260540..5ff41d62e2b0 100644
--- a/code/modules/surgery/organs/augments_eyes.dm
+++ b/code/modules/surgery/organs/augments_eyes.dm
@@ -61,6 +61,9 @@
HUD_type = DATA_HUD_SECURITY_ADVANCED
examine_extensions = list(EXAMINE_HUD_SECURITY_READ)
+/obj/item/organ/internal/cyberimp/eyes/hud/security/hidden
+ stealth_level = 4 // Only surgery or a body scanner with the highest tier of stock parts can detect this.
+
/obj/item/organ/internal/cyberimp/eyes/hud/jani
name = "Janitor HUD implant"
desc = "These cybernetic eye implants will display a filth HUD over everything you see."
diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm
index 7704c6707c54..86a1bf4b5675 100644
--- a/code/modules/surgery/organs/organ_icon.dm
+++ b/code/modules/surgery/organs/organ_icon.dm
@@ -108,7 +108,7 @@
var/head_marking = owner.m_styles["head"]
if(head_marking)
var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking]
- if(head_marking_style && head_marking_style.species_allowed && (dna.species.name in head_marking_style.species_allowed) && head_marking_style.marking_location == "head")
+ if(head_marking_style && head_marking_style.species_allowed && (dna.species.sprite_sheet_name in head_marking_style.species_allowed) && head_marking_style.marking_location == "head")
var/icon/h_marking_s = new /icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s")
if(head_marking_style.do_colouration)
h_marking_s.Blend(owner.m_colours["head"], ICON_ADD)
@@ -117,7 +117,7 @@
if(!((owner.head && (owner.head.flags & BLOCKHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHAIR)))) //Common restriction for all the below features.
if(ha_style)
var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[ha_style]
- if(head_accessory_style && head_accessory_style.species_allowed && (dna.species.name in head_accessory_style.species_allowed))
+ if(head_accessory_style && head_accessory_style.species_allowed && (dna.species.sprite_sheet_name in head_accessory_style.species_allowed))
var/icon/head_accessory_s = new /icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s")
if(head_accessory_style.do_colouration)
head_accessory_s.Blend(headacc_colour, ICON_ADD)
@@ -125,7 +125,7 @@
if(f_style)
var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style]
- if(facial_hair_style && ((facial_hair_style.species_allowed && (dna.species.name in facial_hair_style.species_allowed)) || (dna.species.bodyflags & ALL_RPARTS)))
+ if(facial_hair_style && ((facial_hair_style.species_allowed && (dna.species.sprite_sheet_name in facial_hair_style.species_allowed)) || (dna.species.bodyflags & ALL_RPARTS)))
var/icon/facial_s = new /icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s")
if(istype(dna.species, /datum/species/slime)) // I am el worstos
facial_s.Blend("[owner.skin_colour]A0", ICON_AND) //A0 = 160 alpha.
@@ -136,7 +136,7 @@
if(h_style)
if(!ismachineperson(owner) || (ismachineperson(owner) && ((owner.head && (owner.head.flags & BLOCKHEADHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHEADHAIR)))))
var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style]
- if(hair_style && ((dna.species.name in hair_style.species_allowed) || (dna.species.bodyflags & ALL_RPARTS)))
+ if(hair_style && ((dna.species.sprite_sheet_name in hair_style.species_allowed) || (dna.species.bodyflags & ALL_RPARTS)))
var/icon/hair_s = new /icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s")
if(istype(dna.species, /datum/species/slime)) // I am el worstos
hair_s.Blend("[owner.skin_colour]A0", ICON_AND) //A0 = 160 alpha.
diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm
index ab38b16f36ff..07fb8745987a 100644
--- a/code/modules/surgery/robotics.dm
+++ b/code/modules/surgery/robotics.dm
@@ -750,13 +750,32 @@
else if(!target.Adjacent(user))
to_chat(user, "The multitool is out of range! Please try again.")
return SURGERY_STEP_INCOMPLETE
+
+ var/new_subtype = tgui_input_list(user, "Choose a species look-alike for this machine", "Select Subtype", target.dna.species.allowed_species_subtypes)
+ if(isnull(new_subtype))
+ to_chat(user, "You must choose a subtype! Please try again.")
+ return SURGERY_STEP_INCOMPLETE
+ else if(!target.Adjacent(user))
+ to_chat(user, "The multitool is out of range! Please try again.")
+ return SURGERY_STEP_INCOMPLETE
+
var/new_gender = gender_list[gender_key]
var/old_name = target.real_name
target.real_name = new_name
target.gender = new_gender
user.visible_message(
- "[user] edits [old_name]'s identity parameters with [tool]; [target.p_they()] [target.p_are()] now known as [new_name].",
- "You alter [old_name]'s identity parameters with [tool]; [target.p_they()] [target.p_are()] now known as [new_name].",
+ "[user] edits [old_name]'s identity parameters with [tool], changing their appearance; [target.p_they()] [target.p_are()] now known as [new_name].",
+ "You alter [old_name]'s identity parameters with [tool], changing their appearance; [target.p_they()] [target.p_are()] now known as [new_name].",
chat_message_type = MESSAGE_TYPE_COMBAT
)
+
+ if(!isnull(target.dna))
+ var/datum/species/subtype = GLOB.all_species[new_subtype]
+ if(isnull(subtype))
+ subtype = GLOB.all_species[target.dna.species.name]
+ target.dna.species.updatespeciessubtype(target, new subtype.type())
+ for(var/obj/item/organ/external/limb in target.bodyparts) // Update robotic limbs to match new sub species
+ limb.set_company(limb.model, target.dna.species.sprite_sheet_name) // Update the limbs to properly use their new sprite sheet.
+ target.regenerate_icons()
+
return SURGERY_STEP_CONTINUE
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index cacf106a7913..d82fcec4efb8 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -17,9 +17,13 @@
var/list/whitelist
var/list/blacklist
+ /// Temporary holder when we first initialize the TGUI, we store this as our owner's previous subtype so we can tell when it updates.
+ var/owner_subtype
+
/datum/ui_module/appearance_changer/New(datum/host, mob/living/carbon/human/H, check_species_whitelist = TRUE, list/species_whitelist = list(), list/species_blacklist = list())
..()
owner = H
+ owner_subtype = owner.dna.species.species_subtype
head_organ = owner.get_organ("head")
check_whitelist = check_species_whitelist
whitelist = species_whitelist
@@ -214,7 +218,11 @@
ui.open()
/datum/ui_module/appearance_changer/ui_data(mob/user)
- generate_data(check_whitelist, whitelist, blacklist)
+ if(user.dna.species.species_subtype != owner_subtype)
+ owner_subtype = user.dna.species.species_subtype
+ cut_and_generate_data()
+ else
+ generate_data(check_whitelist, whitelist, blacklist)
var/list/data = list()
data["specimen"] = owner.dna.species.name
diff --git a/code/tests/_map_per_tile_test.dm b/code/tests/_map_per_tile_test.dm
new file mode 100644
index 000000000000..db596d855178
--- /dev/null
+++ b/code/tests/_map_per_tile_test.dm
@@ -0,0 +1,20 @@
+/**
+ * Map per-tile test.
+ *
+ * Per-tile map tests iterate over each tile of a map to perform a check, and
+ * fails the test if a tile does not pass the check. A new test can be
+ * written by extending /datum/map_per_tile_test, and implementing the check
+ * in CheckTile.
+ */
+/datum/map_per_tile_test
+ var/succeeded = TRUE
+ var/list/fail_reasons
+ var/failure_count = 0
+
+/datum/map_per_tile_test/proc/CheckTile(turf/T)
+ Fail("CheckTile() called parent or not implemented")
+
+/datum/map_per_tile_test/proc/Fail(turf/T, reason)
+ succeeded = FALSE
+ LAZYADD(fail_reasons, "[T.x],[T.y],[T.z]: [reason]")
+ failure_count++
diff --git a/code/tests/game_tests.dm b/code/tests/game_tests.dm
index d482918253ff..81b45121d889 100644
--- a/code/tests/game_tests.dm
+++ b/code/tests/game_tests.dm
@@ -1,9 +1,14 @@
//include game test files in this module in this ifdef
//Keep this sorted alphabetically
-#ifdef GAME_TESTS
+#ifdef TEST_RUNNER
#include "_game_test_puppeteer.dm"
#include "_game_test.dm"
+#include "_map_per_tile_test.dm"
+#include "test_runner.dm"
+#endif
+
+#ifdef GAME_TESTS
#include "atmos\test_ventcrawl.dm"
#include "attack_chain\test_attack_chain_cult_dagger.dm"
#include "attack_chain\test_attack_chain_machinery.dm"
@@ -13,7 +18,6 @@
#include "jobs\test_job_globals.dm"
#include "test_aicard_icons.dm"
#include "test_announcements.dm"
-#include "test_areas_apcs.dm"
#include "test_components.dm"
#include "test_config_sanity.dm"
#include "test_crafting_lists.dm"
@@ -22,12 +26,10 @@
#include "test_init_sanity.dm"
#include "test_log_format.dm"
#include "test_map_templates.dm"
-#include "test_map_tests.dm"
#include "test_missing_icons.dm"
#include "test_origin_tech.dm"
#include "test_purchase_reference_test.dm"
#include "test_reagent_id_typos.dm"
-#include "test_runner.dm"
#include "test_rustg_version.dm"
#include "test_spawn_humans.dm"
#include "test_spell_targeting_test.dm"
@@ -37,3 +39,8 @@
#include "test_subsystem_metric_sanity.dm"
#include "test_timer_sanity.dm"
#endif
+
+#ifdef MAP_TESTS
+#include "test_areas_apcs.dm"
+#include "test_map_tests.dm"
+#endif
diff --git a/code/tests/test_map_tests.dm b/code/tests/test_map_tests.dm
index c4d756390bf2..76d1416ac7f6 100644
--- a/code/tests/test_map_tests.dm
+++ b/code/tests/test_map_tests.dm
@@ -1,24 +1,3 @@
-/**
- * Map per-tile test.
- *
- * Per-tile map tests iterate over each tile of a map to perform a check, and
- * fails the test if a tile does not pass the check. A new test can be
- * written by extending /datum/map_per_tile_test, and implementing the check
- * in CheckTile.
- */
-/datum/map_per_tile_test
- var/succeeded = TRUE
- var/list/fail_reasons
- var/failure_count = 0
-
-/datum/map_per_tile_test/proc/CheckTile(turf/T)
- Fail("CheckTile() called parent or not implemented")
-
-/datum/map_per_tile_test/proc/Fail(turf/T, reason)
- succeeded = FALSE
- LAZYADD(fail_reasons, "[T.x],[T.y],[T.z]: [reason]")
- failure_count++
-
/**
* Check to ensure that APCs have a cable node on their tile.
*/
diff --git a/code/tests/test_runner.dm b/code/tests/test_runner.dm
index 206d31a2205d..1bd2cf287812 100644
--- a/code/tests/test_runner.dm
+++ b/code/tests/test_runner.dm
@@ -22,38 +22,18 @@
// Running the tests is part of the ticker's start function, because I cant think of any better place to put it
SSticker.force_start = TRUE
-
-/datum/test_runner/proc/RunMap(z_level = 2)
- CHECK_TICK
-
- var/list/tests = list()
-
- for(var/I in subtypesof(/datum/map_per_tile_test))
- tests += new I
- test_logs[I] = list()
- durations[I] = 0
-
- for(var/turf/T in block(1, 1, z_level, world.maxx, world.maxy, z_level))
- for(var/datum/map_per_tile_test/test in tests)
- if(test.failure_count < MAX_MAP_TEST_FAILURE_COUNT)
- var/duration = REALTIMEOFDAY
- test.CheckTile(T)
- durations[test.type] += REALTIMEOFDAY - duration
-
- if(test.failure_count >= MAX_MAP_TEST_FAILURE_COUNT)
- test.Fail(T, "failure threshold reached at this tile")
-
- CHECK_TICK
-
- for(var/datum/map_per_tile_test/test in tests)
- if(!test.succeeded)
- failed_any_test = TRUE
- test_logs[test.type] += test.fail_reasons
-
- QDEL_LIST_CONTENTS(tests)
-
+/datum/test_runner/proc/RunAll()
+ #ifdef MAP_TESTS
+ // Run map tests first in case unit tests futz with map state
+ RunMap()
+ #endif
+ #ifdef GAME_TESTS
+ Run()
+ #endif
+ SSticker.reboot_helper("Unit Test Reboot", "tests ended", 0)
/datum/test_runner/proc/Run()
+ log_world("Test runner: game tests.")
CHECK_TICK
for(var/I in subtypesof(/datum/game_test))
@@ -84,10 +64,38 @@
CHECK_TICK
- SSticker.reboot_helper("Unit Test Reboot", "tests ended", 0)
+/datum/test_runner/proc/RunMap(z_level = 2)
+ log_world("Test runner: map tests.")
+ CHECK_TICK
+
+ var/list/tests = list()
+
+ for(var/I in subtypesof(/datum/map_per_tile_test))
+ tests += new I
+ test_logs[I] = list()
+ durations[I] = 0
+
+ for(var/turf/T in block(1, 1, z_level, world.maxx, world.maxy, z_level))
+ for(var/datum/map_per_tile_test/test in tests)
+ if(test.failure_count < MAX_MAP_TEST_FAILURE_COUNT)
+ var/duration = REALTIMEOFDAY
+ test.CheckTile(T)
+ durations[test.type] += REALTIMEOFDAY - duration
+ if(test.failure_count >= MAX_MAP_TEST_FAILURE_COUNT)
+ test.Fail(T, "failure threshold reached at this tile")
+
+ CHECK_TICK
+
+ for(var/datum/map_per_tile_test/test in tests)
+ if(!test.succeeded)
+ failed_any_test = TRUE
+ test_logs[test.type] += test.fail_reasons
+
+ QDEL_LIST_CONTENTS(tests)
/datum/test_runner/proc/Finalize(emit_failures = FALSE)
+ log_world("Test runner: finalizing.")
var/time = world.timeofday
set waitfor = FALSE
diff --git a/config/example/config.toml b/config/example/config.toml
index 014e63b393d5..161868b5fba5 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -41,7 +41,6 @@
################################################################
-
[admin_configuration]
# This section contains all information regarding admin setup. This includes ranks, rights, and if you are using the database-backed admin system
@@ -73,70 +72,68 @@ enable_localhost_autoadmin = true
# +MAINTAINER = Access to super dangerous debug verbs that can break an entire round. Use sparingly.
# +DEV_TEAM = Access to dev team chat
admin_ranks = [
- # Format: {name = "My Rank", rights = ["+LIST", "+OF", "+RIGHTS"]}
- { name = "Mentor", rights = [
- "+MENTOR",
- ] },
- { name = "Trial Admin", rights = [
- "+ADMIN",
- "+BAN",
- "+STEALTH",
- "+REJUVINATE",
- ] },
- { name = "Game Admin", rights = [
- "+ADMIN",
- "+BAN",
- "+STEALTH",
- "+REJUVINATE",
- "+DEBUG",
- "+BUILDMODE",
- "+EVENT",
- "+SERVER",
- "+POSSESS",
- "+PROCCALL",
- "+VAREDIT",
- "+SOUND",
- "+SPAWN",
- ] },
- { name = "Head of Staff", rights = [
- "+EVERYTHING",
- ] },
- { name = "Hosting Provider", rights = [
- "+EVERYTHING",
- ] },
- { name = "Maintainers", rights = [
- "+EVERYTHING",
- ] },
- { name = "Developer", rights = [
- "+DEV_TEAM",
- ] },
+ # Format: {name = "My Rank", rights = ["+LIST", "+OF", "+RIGHTS"]}
+ {name = "Mentor", rights = [
+ "+MENTOR",
+ ]},
+ {name = "Trial Admin", rights = [
+ "+ADMIN",
+ "+BAN",
+ "+STEALTH",
+ "+REJUVINATE",
+ ]},
+ {name = "Game Admin", rights = [
+ "+ADMIN",
+ "+BAN",
+ "+STEALTH",
+ "+REJUVINATE",
+ "+DEBUG",
+ "+BUILDMODE",
+ "+EVENT",
+ "+SERVER",
+ "+POSSESS",
+ "+PROCCALL",
+ "+VAREDIT",
+ "+SOUND",
+ "+SPAWN",
+ ]},
+ {name = "Head of Staff", rights = [
+ "+EVERYTHING",
+ ]},
+ {name = "Hosting Provider", rights = [
+ "+EVERYTHING",
+ ]},
+ {name = "Maintainers", rights = [
+ "+EVERYTHING",
+ ]},
+ {name = "Developer", rights = [
+ "+DEV_TEAM",
+ ]},
]
# List of people and the admin rank they are assigned to
admin_assignments = [
- #{ckey = "Your Name Here", rank = "Hosting Provider"}
+ #{ckey = "Your Name Here", rank = "Hosting Provider"}
]
# Allow admins to set their own OOC colour
allow_admin_ooc_colour = true
# Map of admin rank colours to their respective rank. Note you can use hex or colour name here.
admin_rank_colour_map = [
- { name = "Head of Staff", colour = "#e74c3c" },
- { name = "Maintainer", colour = "#992d22" },
- { name = "Server Dev", colour = "#1abc9c" },
- { name = "Community Manager", colour = "#e91e63" },
- { name = "Game Admin", colour = "#238afa" },
- { name = "Trial Admin", colour = "#7fb6fc" },
- { name = "Developer", colour = "#61b413" },
- { name = "Mentor", colour = "#f1c40f" },
+ {name = "Head of Staff", colour = "#e74c3c"},
+ {name = "Maintainer", colour = "#992d22"},
+ {name = "Server Dev", colour = "#1abc9c"},
+ {name = "Community Manager", colour = "#e91e63"},
+ {name = "Game Admin", colour = "#238afa"},
+ {name = "Trial Admin", colour = "#7fb6fc"},
+ {name = "Developer", colour = "#61b413"},
+ {name = "Mentor", colour = "#f1c40f"},
]
# Map of common CIDs that should not be banned, plus their reasons
common_cid_map = [
- { cid = "3923664137", reason = "Mass-Match related to Walmart branded prebuilt PCs" },
+ {cid = "3923664137", reason = "Mass-Match related to Walmart branded prebuilt PCs"},
]
-
################################################################
-
[afk_configuration]
# This section contains options for the auto AFK cryo system
@@ -149,10 +146,8 @@ afk_auto_despawn_minutes = 21
# Amount of time before SSD people are auto cryo'd
ssd_auto_cryo_minutes = 15
-
################################################################
-
[custom_sprites_configuration]
# This section contains a list of information for people with custom sprites for mobs, if they donated
# It is split by mob type, and screens have their own system
@@ -168,16 +163,14 @@ ai_hologram = ["ckeyhere"]
pai_holoform = ["ckeyhere"]
# List of dictionary entries for ckeys with a custom IPC screean
ipc_screens = [
- { ckey = "ckeyhere", screens = [
- "Icon State 1",
- "Icon State 2",
- ] },
+ {ckey = "ckeyhere", screens = [
+ "Icon State 1",
+ "Icon State 2",
+ ]},
]
-
################################################################
-
[database_configuration]
# This section contains all the settings for the ingame database
# If you are running in production, you will want to be using a database
@@ -185,7 +178,7 @@ ipc_screens = [
# Enable/disable the database on a whole
sql_enabled = false
# SQL version. If this is a mismatch, round start will be delayed
-sql_version = 632207
+sql_version = 642207
# SQL server address. Can be an IP or DNS name
sql_address = "127.0.0.1"
# SQL server port
@@ -201,10 +194,8 @@ async_query_timeout = 10
# How many threads is the async SQL engine allowed to open. 50 is normal. Trust me.
async_thread_limit = 50
-
################################################################
-
[discord_configuration]
# This section contains all the information related to Discord webhook sending from the code
# If you are using webhooks, please fill out this section in full
@@ -214,8 +205,8 @@ enable_discord_webhooks = false
main_webhook_urls = ["https://webhook.one", "https://webhook.two"]
# List of all webhook URLs for the mentor feed (mentorhelps relay)
mentor_webhook_urls = [
- "https://mentor.webhook.one",
- "https://mentor.webhook.two",
+ "https://mentor.webhook.one",
+ "https://mentor.webhook.two",
]
# List of all webhook URLs for the admin feed (round alerts, ahelps, etc)
admin_webhook_urls = ["https://admin.webhook.one", "https://admin.webhook.two"]
@@ -233,31 +224,28 @@ new_round_waiting_role_id = ""
################################################################
-
[event_configuration]
# This section contains settings for ingame random events
# Disable this to disable all random events
allow_random_events = true
# Map of lower bounds for event delays. In minutes.
-event_delay_lower_bounds = { mundane = 10, moderate = 30, major = 50 }
+event_delay_lower_bounds = {mundane = 10, moderate = 30, major = 50}
# Map of upper bounds for event delays. In minutes.
-event_delay_upper_bounds = { mundane = 15, moderate = 45, major = 70 }
+event_delay_upper_bounds = {mundane = 15, moderate = 45, major = 70}
# Expected round length in minutes. Changes event weights
expected_round_length = 120
# Initial delays for the first events firing off. If any of these are commented, a random value based on above thresholds is used.
# You must specify an upper and lower bound, or the code cries a lot. The events system is so bad good god.
# Values are in minutes
event_initial_delays = [
- #{severity = "mundane", lower_bound = 10, upper_bound = 15},
- { severity = "moderate", lower_bound = 25, upper_bound = 40 },
- { severity = "major", lower_bound = 55, upper_bound = 75 },
+ #{severity = "mundane", lower_bound = 10, upper_bound = 15},
+ {severity = "moderate", lower_bound = 25, upper_bound = 40},
+ {severity = "major", lower_bound = 55, upper_bound = 75},
]
-
################################################################
-
[gamemode_configuration]
# This section contains setups for the gamemodes, as well as their probabilities
@@ -267,7 +255,7 @@ antag_account_age_restrictions = false
gamemode_probabilities = [
{ gamemode = "abduction", probability = 0 },
{ gamemode = "changeling", probability = 0 },
- { gamemode = "cult", probability = 3 },
+ { gamemode = "cult", probability = 0 },
{ gamemode = "extend-a-traitormongous", probability = 2 }, # Autotraitor
{ gamemode = "extended", probability = 0 },
{ gamemode = "nuclear", probability = 2 },
@@ -296,10 +284,8 @@ enable_gamemode_player_limit = true
# Enable to generate zero or more random station traits on game start.
add_random_station_traits = false
-
################################################################
-
[general_configuration]
# General setup for the server (branding, options, etc)
@@ -391,10 +377,8 @@ random_ai_lawset = true
# Enable weather events initialized by SSweather. New weather events can still be added during the round if this is disabled.
enable_default_weather_events = true
-
################################################################
-
[lighting_effects_configuration]
# Settings that allow you to change light effects (bloom / exposure intensity)
@@ -415,10 +399,8 @@ exposure_contrast_base = 9.5
# Contrast of exposure effect that depends on light_power
exposure_contrast_power = 0
-
################################################################
-
[ipintel_configuration]
# This section contains all the information for IPIntel (The Anti VPN system)
@@ -441,10 +423,8 @@ playtime_ignore_threshold = 10
# Details URL for more info on an IP (such as ASN). IP is tacked on the end.
details_url = "https://iphub.info/?ip="
-
################################################################
-
[job_configuration]
# This section contains information on job configuartion, as well as the jon amounts
@@ -473,55 +453,45 @@ assistant_security_ratio = 2
enable_job_amount_overrides = true
# Job slot amount map. These are overrides. If you dont specify a value here, the code will be used as default. -1 is infinite
job_slot_amounts = [
- # Commmand
- { name = "Captain", lowpop = 1, highpop = 1 },
- { name = "Head of Personnel", lowpop = 1, highpop = 1 },
- { name = "Head of Security", lowpop = 1, highpop = 1 },
- { name = "Chief Engineer", lowpop = 1, highpop = 1 },
- { name = "Research Director", lowpop = 1, highpop = 1 },
- { name = "Chief Medical Officer", lowpop = 1, highpop = 1 },
- # Engineering
- { name = "Life Support Specialist", lowpop = 3, highpop = 4 },
- { name = "Station Engineer", lowpop = 5, highpop = 6 },
- { name = "Trainee Engineer", lowpop = 0, highpop = 0 },
- # Medical
- { name = "Chemist", lowpop = 2, highpop = 2 },
- { name = "Geneticist", lowpop = 2, highpop = 2 },
- { name = "Medical Doctor", lowpop = 5, highpop = 6 },
- { name = "Medical Intern", lowpop = 0, highpop = 0 },
- { name = "Virologist", lowpop = 1, highpop = 1 },
- # Science
- { name = "Roboticist", lowpop = 2, highpop = 2 },
- { name = "Scientist", lowpop = 6, highpop = 7 },
- { name = "Student Scientist", lowpop = 0, highpop = 0 },
- # Security
- { name = "Detective", lowpop = 1, highpop = 1 },
- { name = "Security Officer", lowpop = 8, highpop = 9 },
- { name = "Security Cadet", lowpop = 0, highpop = 0 },
- { name = "Warden", lowpop = 1, highpop = 1 },
- { name = "Internal Affairs Agent", lowpop = 2, highpop = 2 },
- # Service
- { name = "Bartender", lowpop = 1, highpop = 1 },
- { name = "Botanist", lowpop = 2, highpop = 2 },
- { name = "Chaplain", lowpop = 1, highpop = 1 },
- { name = "Chef", lowpop = 1, highpop = 1 },
- { name = "Janitor", lowpop = 1, highpop = 2 },
- { name = "Librarian", lowpop = 1, highpop = 1 },
- # Cargo/Supply
- { name = "Quartermaster", lowpop = 1, highpop = 1 },
- { name = "Shaft Miner", lowpop = 6, highpop = 6 },
- { name = "Cargo Technician", lowpop = 3, highpop = 4 },
- # Silicon
- { name = "AI", lowpop = 1, highpop = 1 },
- { name = "Cyborg", lowpop = 1, highpop = 1 },
- # Misc
- { name = "Assistant", lowpop = -1, highpop = -1 },
+ # Commmand
+ {name = "Captain", lowpop = 1, highpop = 1},
+ {name = "Head of Personnel", lowpop = 1, highpop = 1},
+ {name = "Head of Security", lowpop = 1, highpop = 1},
+ {name = "Chief Engineer", lowpop = 1, highpop = 1},
+ {name = "Research Director", lowpop = 1, highpop = 1},
+ {name = "Chief Medical Officer", lowpop = 1, highpop = 1}, # Engineering
+ {name = "Life Support Specialist", lowpop = 3, highpop = 4},
+ {name = "Station Engineer", lowpop = 5, highpop = 6},
+ {name = "Trainee Engineer", lowpop = 0, highpop = 0}, # Medical
+ {name = "Chemist", lowpop = 2, highpop = 2},
+ {name = "Geneticist", lowpop = 2, highpop = 2},
+ {name = "Medical Doctor", lowpop = 5, highpop = 6},
+ {name = "Medical Intern", lowpop = 0, highpop = 0},
+ {name = "Virologist", lowpop = 1, highpop = 1}, # Science
+ {name = "Roboticist", lowpop = 2, highpop = 2},
+ {name = "Scientist", lowpop = 6, highpop = 7},
+ {name = "Student Scientist", lowpop = 0, highpop = 0}, # Security
+ {name = "Detective", lowpop = 1, highpop = 1},
+ {name = "Security Officer", lowpop = 8, highpop = 9},
+ {name = "Security Cadet", lowpop = 0, highpop = 0},
+ {name = "Warden", lowpop = 1, highpop = 1},
+ {name = "Internal Affairs Agent", lowpop = 2, highpop = 2}, # Service
+ {name = "Bartender", lowpop = 1, highpop = 1},
+ {name = "Botanist", lowpop = 2, highpop = 2},
+ {name = "Chaplain", lowpop = 1, highpop = 1},
+ {name = "Chef", lowpop = 1, highpop = 1},
+ {name = "Janitor", lowpop = 1, highpop = 2},
+ {name = "Librarian", lowpop = 1, highpop = 1}, # Cargo/Supply
+ {name = "Quartermaster", lowpop = 1, highpop = 1},
+ {name = "Shaft Miner", lowpop = 6, highpop = 6},
+ {name = "Cargo Technician", lowpop = 3, highpop = 4}, # Silicon
+ {name = "AI", lowpop = 1, highpop = 1},
+ {name = "Cyborg", lowpop = 1, highpop = 1}, # Misc
+ {name = "Assistant", lowpop = -1, highpop = -1},
]
-
################################################################
-
[logging_configuration]
# Configuration for all things related to logging data to disk
# If you are in production you will want most of these on
@@ -559,10 +529,8 @@ enable_adminchat_logging = true
# Log debug messages
enable_debug_logging = true
-
################################################################
-
[metrics_configuration]
# This section contains values for all metrics storing.
# This is designed for ElasticSearch but you might be able to make it work with other things.
@@ -575,10 +543,8 @@ metrics_endpoint = "http://elastic-host:9200/your_index/_doc"
# Metrics API token. Placed into the auth header
metrics_api_token = "your_base64_encoded_thingy=="
-
################################################################
-
[mc_configuration]
# This section is settings for the MC
# Dont change these unless you know what you are doing
@@ -596,10 +562,8 @@ mc_highpop_threshold_enable = 65
# Threshold to disable MC highpop mode
mc_highpop_threshold_disable = 60
-
################################################################
-
[movement_configuration]
# This section contains values for all mob movement stuff.
# We suggest VVing ingame till you get values you like.
@@ -622,10 +586,8 @@ slime_delay = 1.5
# Move delay for other simple animals
animal_delay = 2.5
-
################################################################
-
[overflow_configuration]
# This contains information for re-routing players to an overflow server
# We dont use this in production, but you may find it useful
@@ -644,7 +606,6 @@ reservation_time = 10
################################################################
-
[redis_configuration]
# This section contains all the settings for using redis.
# This is heavily tied into the Paradise production architecture and you probably dont need it.
@@ -654,10 +615,8 @@ redis_enabled = false
# Redis connection string. Include your passphrase if needed
redis_connstring = "redis://127.0.0.1/"
-
################################################################
-
[ruin_configuration]
# This section contains configuration for all space ruins and lava ruins
@@ -680,130 +639,110 @@ lavaland_ruin_budget_max = 325
# List of all space ruins that can generate in the world
# Commenting something out in here DISABLES IT FROM SPAWNING
active_space_ruins = [
- "_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/meatpackers.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/oldstation.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/voyager.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/casino.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/abandoned_sec_shuttle.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/freighter.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/unathi_skiff.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/clockwork_monastery.dmm",
- ## SS220 Ruins
- "_maps/map_files220/RandomRuins/SpaceRuins/mechtransport_new.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/destroyed_infiltrator.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/transit_bar.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/infected_ship.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/convoy_ambush.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/voxraiders_1.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/spacehotel.dmm",
- "_maps/map_files220/RandomRuins/SpaceRuins/sierra_1_4.dmm",
-
- ### The following ruins are based from past pre-spawned Zlevel content ###
- "_maps/map_files/RandomRuins/SpaceRuins/abandonedtele.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/blowntcommsat.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/clownmime.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/dj.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/druglab.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/ussp_tele.dmm",
- "_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm",
-
-
- # The following is the white ship ruin. Its force-spawned and is required to stop SSshuttle runtiming on startup
- # Its also important incase a white-ship console is ever built midround
- # DO NOT DISABLE THIS UNLESS YOU HAVE A GOOD REASON
- "_maps/map_files220/RandomRuins/SpaceRuins/whiteship.dmm",
-
- # The following is a force-spawned ruin consisting mostly of empty space with a shuttle docking port for the free golem shuttle
- # Disabling it will lead to the free golem shuttle sometimes being stuck on lavaland.
- "_maps/map_files/RandomRuins/SpaceRuins/golemtarget.dmm",
-] # List of all space ruins that can generate in the world
-# Commenting something out in here DISABLES IT FROM SPAWNING
+ "_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/meatpackers.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/oldstation.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/voyager.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/abandoned_engi_sat.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/casino.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/rocky_motel.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/abandoned_sec_shuttle.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/freighter.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/unathi_skiff.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/clockwork_monastery.dmm", ### The following ruins are based from past pre-spawned Zlevel content ###
+ ## SS220 Ruins
+ "_maps/map_files220/RandomRuins/SpaceRuins/mechtransport_new.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/destroyed_infiltrator.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/transit_bar.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/infected_ship.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/convoy_ambush.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/voxraiders_1.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/spacehotel.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/sierra_1_4.dmm",
+
+ "_maps/map_files/RandomRuins/SpaceRuins/abandonedtele.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/blowntcommsat.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/clownmime.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/dj.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/druglab.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/ussp_tele.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm", # The following is the white ship ruin. Its force-spawned and is required to stop SSshuttle runtiming on startup # Its also important incase a white-ship console is ever built midround # DO NOT DISABLE THIS UNLESS YOU HAVE A GOOD REASON
+ "_maps/map_files220/RandomRuins/SpaceRuins/whiteship.dmm", # The following is a force-spawned ruin consisting mostly of empty space with a shuttle docking port for the free golem shuttle
+ # Disabling it will lead to the free golem shuttle sometimes being stuck on lavaland.
+ "_maps/map_files/RandomRuins/SpaceRuins/golemtarget.dmm",
+]
# List of all ruins that can generate on lavaland
# Commenting something out in here DISABLES IT FROM SPAWNING
active_lava_ruins = [
- ##BIODOMES
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm",
- ##RESPAWN
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm",
- ##SIN
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm",
- # Greed blacklisted on production because its reward (dice) has a 1/20 chance of making you a wizard
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm",
- ##MEGAFAUNA
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk2.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk3.dmm",
- # Re-removed 7/24/2020, I ran a month-long test of hierophant Jun-Jul and the staff was still too strong. https://github.com/ParadiseSS13/Paradise/pull/13542
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_hierophant.dmm",
-
- ##MISC
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_althland_excavation.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_althland_facility.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_elite_tumor.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_fountain_hall.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pizzaparty.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_shuttlecrash.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ufo_crash.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_watcher_grave.dmm",
- "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm",
- "_maps/map_files220/RandomRuins/LavaRuins/scp_facility.dmm",
- "_maps/map_files220/RandomRuins/LavaRuins/cheesus_temple.dmm",
- "_maps/map_files220/RandomRuins/LavaRuins/ash_old.dmm",
- "_maps/map_files220/RandomRuins/LavaRuins/old_outpost.dmm",
+ ##BIODOMES
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm", ##RESPAWN
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm", ##SIN
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm", # Greed blacklisted on production because its reward (dice) has a 1/20 chance of making you a wizard
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm", ##MEGAFAUNA
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk2.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk3.dmm", # Re-removed 7/24/2020, I ran a month-long test of hierophant Jun-Jul and the staff was still too strong. https://github.com/ParadiseSS13/Paradise/pull/13542
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_hierophant.dmm", ##MISC
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_althland_excavation.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_althland_facility.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_elite_tumor.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_fountain_hall.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pizzaparty.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_shuttlecrash.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ufo_crash.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_watcher_grave.dmm",
+ "_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm",
+ "_maps/map_files220/RandomRuins/LavaRuins/scp_facility.dmm",
+ "_maps/map_files220/RandomRuins/LavaRuins/cheesus_temple.dmm",
+ "_maps/map_files220/RandomRuins/LavaRuins/ash_old.dmm",
+ "_maps/map_files220/RandomRuins/LavaRuins/old_outpost.dmm",
]
-# Budget for lavaland ruins. A higher number means more will spawn
-lavaland_ruin_budget = 100
-
################################################################
-
[system_configuration]
# This contains stuff that the host should fill out, relating to backend stuff
@@ -840,15 +779,13 @@ external_tos_handler = false
# This is not for separate DD instances per region, but if you have a geo-routing setup deployed to optimise routing for players
# I am 99.9% certain you will never need to fill this out
regional_servers = [
- #{name = "UK", ip = "byond://uk.paradisestation.org:6666"},
+ #{name = "UK", ip = "byond://uk.paradisestation.org:6666"},
]
# Send a toast on server init completion. You probably dont need this on in production
toast_on_init_complete = true
-
################################################################
-
[url_configuration]
# Configuration for all the URLs used by the server
# If you are in production you will want to tweak these to your servers
@@ -888,10 +825,8 @@ centcomm_ban_db_url = "https://centcom.melonmesa.com/ban/search/"
# URL for the game stats page, if applicable. Round ID is appended right on the end. Include a trailing slash if necessary.
#round_stats_url = "https://affectedarc07.github.io/ParaStats/#/round/"
-
################################################################
-
[voting_configuration]
# This section contains all settings relating to the ingame voting system
@@ -919,19 +854,17 @@ non_repeating_maps = true
# - "FPTP" = A vote that excludes the map from the current round, and last round, then uses a weighted pick on the remaining maps, from preferences.
# If you dont enter one correctly, it will whine on startup
map_vote_day_types = [
- { day_number = 1, rotation_type = "Vote" }, # Monday
- { day_number = 2, rotation_type = "Random" }, # Tuesday
- { day_number = 3, rotation_type = "FPTP" }, # Wednesday
- { day_number = 4, rotation_type = "Random" }, # Thursday
- { day_number = 5, rotation_type = "Vote" }, # Friday
- { day_number = 6, rotation_type = "FPTP" }, # Saturday
- { day_number = 7, rotation_type = "FPTP" }, # Sunday
+ {day_number = 1, rotation_type = "Vote"}, # Monday
+ {day_number = 2, rotation_type = "Random"}, # Tuesday
+ {day_number = 3, rotation_type = "FPTP"}, # Wednesday
+ {day_number = 4, rotation_type = "Random"}, # Thursday
+ {day_number = 5, rotation_type = "Vote"}, # Friday
+ {day_number = 6, rotation_type = "FPTP"}, # Saturday
+ {day_number = 7, rotation_type = "FPTP"}, # Sunday
]
-
################################################################
-
[asset_cache_configuration]
# Browser assets are any file included in interfaces. css, images, javascript, etc.
# This handles configuring how we get these to the player so interfaces can access them.
@@ -950,7 +883,6 @@ asset_simple_preload = true
# if you want to test this locally, you simpily run the `localhost-asset-webroot-server.py` python3 script to host assets stored in `data/asset-store/` via http://localhost:58715/
# asset_cdn_url = "http://localhost:58715/"
-
# Congratulations. You reached the end. heres a cookie.
# And some SS220 configs ratge.
diff --git a/config/tests/config_unit_tests.toml b/config/tests/config_unit_tests.toml
new file mode 100644
index 000000000000..dee4ef090fc4
--- /dev/null
+++ b/config/tests/config_unit_tests.toml
@@ -0,0 +1,11 @@
+[ruin_configuration]
+enable_lavaland = false
+enable_space_ruins = false
+minimum_zlevels = 0
+maximum_zlevels = 0
+
+[redis_configuration]
+enabled = false
+
+[database_configuration]
+enabled = true
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
index af8bc67a6938..d2cd183798c8 100644
--- a/docs/CODE_OF_CONDUCT.md
+++ b/docs/CODE_OF_CONDUCT.md
@@ -58,11 +58,6 @@ made by people actually in-touch with the server atmosphere.
### PR Expectations
-Contributors may only have a maximum of **2** feature or balance Pull Requests
-open at any given time. Any additional Pull Requests beyond this limit will be
-closed at the discretion of the Headcoders. The Headcoders may grant an
-exemption to this limit on a case-by-case basis, as the need arises.
-
All Pull Requests are expected to be tested prior to submission. If a submitted
Pull Request fails to pass CI checks, the likelihood of it being merged will be
significantly lower. If you can't take the time to compile/test your Pull
diff --git a/docs/references/autodoc.md b/docs/references/autodoc.md
index bb1278becf86..3eff9a7501fe 100644
--- a/docs/references/autodoc.md
+++ b/docs/references/autodoc.md
@@ -17,33 +17,33 @@ reference.
[dmdoc]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc
## Documenting Code On Paradise
-We use block comments to document procs and classes, and we use `///` line
+We use block comments to document procs and types, and we use `///` line
comments when documenting individual variables.
Documentation is not required at Paradise, but it is highly recommended that all
new code be covered with DMdoc code, according to the
[Specifications](#specification).
-We also recommend that when you touch older code, you document the functions that you
+We also recommend that when you touch older code, you document the procs that you
have touched in the process of updating that code
### Specification
-A class *should* always be auto-documented, and all public functions *should* be
+A type *should* always be auto-documented, and all public procs *should* be
documented.
-All class level defined variables *should* be documented.
+All type level defined variables *should* be documented.
-Internal functions *can* be documented, but may not be.
+Internal procs *can* be documented, but may not be.
-A public function is any function that a developer might reasonably call while using
-or interacting with your object. Internal functions are helper functions that your
-public functions rely on to implement logic.
+A public proc is any function that a developer might reasonably call while using
+or interacting with your object. Internal procs are helper functions that your
+public procs rely on to implement logic.
### Documenting a proc
When documenting a proc, we give a short one line description (as this is shown
next to the proc definition in the list of all procs for a type or global
namespace), then a longer paragraph which will be shown when the user clicks on
-the proc to jump to it's definition
+the proc to jump to its definition
```dm
/**
@@ -57,36 +57,36 @@ the proc to jump to it's definition
*/
```
-### Documenting Classes
-We first give the name of the class as a header, this can be omitted if the name
-is just going to be the typepath of the class, as dmdoc uses that by default.
-Then we give a short one-line description of the class. Finally we give a longer
-multi paragraph description of the class and it's details.
+### Documenting types
+We first give the name of the type as a header, this can be omitted if the name
+is just going to be the typepath of the type, as dmdoc uses that by default.
+Then we give a short one-line description of the type. Finally we give a longer
+multi paragraph description of the type and its details.
```dm
/**
- * # Classname (Can be omitted if it's just going to be the typepath)
+ * # type name (Can be omitted if it's just going to be the typepath)
*
* The short overview
*
* A longer
- * paragraph of functionality about the class
+ * paragraph of functionality about the type
* including any assumptions/special cases
*
*/
```
### Documenting a variable/define
-Give a short explanation of what the variable, in the context of the class, or define is.
+Give a short explanation of what the variable, in the context of the type, or define is.
```dm
-/// Type path of item to go in suit slot
+/// Typepath of item to go in suit slot
var/suit = null
```
## Module level description of code
Modules are the best way to describe the structure/intent of a package of code
-where you don't want to be tied to the formal layout of the class structure.
+where you don't want to be tied to the formal layout of the type structure.
On Paradise we do this by adding markdown files inside the `code` directory
that will also be rendered and added to the modules tree. The structure for
@@ -98,10 +98,10 @@ You can use certain special template variables in DM DOC comments and they will
be expanded.
- `[DEFINE_NAME]` expands to a link to the define definition if documented.
-- `[/mob]` expands to a link to the docs for the /mob class.
-- `[/mob/proc/Dizzy]` expands to a link that will take you to the /mob class and
+- `[/mob]` expands to a link to the docs for the /mob type.
+- `[/mob/proc/Dizzy]` expands to a link that will take you to the /mob type and
anchor you to the dizzy proc docs.
-- `[/mob/var/stat]` expands to a link that will take you to the /mob class and
+- `[/mob/var/stat]` expands to a link that will take you to the /mob type and
anchor you to the stat var docs
You can customise the link name by using `[link name][link shorthand]`.
diff --git a/icons/_nanomaps/DeltaStation_nanomap_z1.png b/icons/_nanomaps/DeltaStation_nanomap_z1.png
index 30e35087458b..2f0f2991c768 100644
Binary files a/icons/_nanomaps/DeltaStation_nanomap_z1.png and b/icons/_nanomaps/DeltaStation_nanomap_z1.png differ
diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi
index f26012f32596..3771518bb5e5 100644
Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ
diff --git a/icons/mob/accessories.dmi b/icons/mob/accessories.dmi
index 40f038739aee..87a9713317e5 100644
Binary files a/icons/mob/accessories.dmi and b/icons/mob/accessories.dmi differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index 0e34c8a1d89a..e81e06ad10a5 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi
index 0dda8c42a910..2752a947e897 100644
Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ
diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi
index ac7094fea86e..6aba3c61e03a 100644
Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ
diff --git a/icons/obj/clothing/accessories_overlay.dmi b/icons/obj/clothing/accessories_overlay.dmi
index f667b8dc04e9..62a4c3baaa53 100644
Binary files a/icons/obj/clothing/accessories_overlay.dmi and b/icons/obj/clothing/accessories_overlay.dmi differ
diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi
index 336c85dedd7a..d0cbf2deb20f 100644
Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ
diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi
index d27c76b49de6..69aca13c76e9 100755
Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ
diff --git a/icons/turf/walls.dmi b/icons/turf/walls.dmi
index d8d4d8fdd790..fb71d66c556c 100644
Binary files a/icons/turf/walls.dmi and b/icons/turf/walls.dmi differ
diff --git a/modular_ss220/camera_nanomap/code/camera.dm b/modular_ss220/camera_nanomap/code/camera.dm
index bb903e6d06d6..d657c88f306e 100644
--- a/modular_ss220/camera_nanomap/code/camera.dm
+++ b/modular_ss220/camera_nanomap/code/camera.dm
@@ -3,10 +3,12 @@
/obj/machinery/camera/Initialize(mapload, should_add_to_cameranet)
. = ..()
+#if !defined(MAP_TESTS) && !defined(GAME_TESTS)
if(z == level_name_to_num(MAIN_STATION))
nanomap_png = "[SSmapping.map_datum.technical_name]_nanomap_z1.png"
else if(z == level_name_to_num(MINING))
nanomap_png = "[MINING]_nanomap_z1.png"
+#endif
/obj/machinery/computer/security
var/list/z_levels = list() // Assoc list, "z_level":"nanomap.png"
diff --git a/paradise.dme b/paradise.dme
index fbcdb95bfce4..007fd9c82c7e 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1696,6 +1696,7 @@
#include "code\modules\atmospherics\machinery\portable\portable_atmospherics.dm"
#include "code\modules\atmospherics\machinery\portable\portable_pump.dm"
#include "code\modules\atmospherics\machinery\portable\scrubber.dm"
+#include "code\modules\awaymissions\cordon.dm"
#include "code\modules\awaymissions\mob_spawn.dm"
#include "code\modules\awaymissions\zlevel_helpers.dm"
#include "code\modules\awaymissions\maploader\dmm_suite.dm"
@@ -2878,12 +2879,12 @@
#include "code\modules\shuttle\shuttle_rotate.dm"
#include "code\modules\shuttle\supply.dm"
#include "code\modules\shuttle\syndicate_shuttles.dm"
-#include "code\modules\space_management\heap_space_level.dm"
#include "code\modules\space_management\level_check.dm"
#include "code\modules\space_management\level_traits.dm"
#include "code\modules\space_management\space_chunk.dm"
#include "code\modules\space_management\space_level.dm"
#include "code\modules\space_management\space_transition.dm"
+#include "code\modules\space_management\turf_reservation.dm"
#include "code\modules\space_management\zlevel_manager.dm"
#include "code\modules\station_goals\bluespace_tap.dm"
#include "code\modules\station_goals\bluespace_tap_events.dm"
diff --git a/rust/src/milla/api.rs b/rust/src/milla/api.rs
index 8239cfc1d955..a92b648732eb 100644
--- a/rust/src/milla/api.rs
+++ b/rust/src/milla/api.rs
@@ -703,6 +703,27 @@ fn milla_get_tick_time() -> eyre::Result {
))
}
+/// BYOND API for freezing a specific z-level.
+#[byondapi::bind]
+fn milla_set_zlevel_frozen(
+ byond_z: ByondValue,
+ byond_frozen: ByondValue,
+) -> eyre::Result {
+ let z = f32::try_from(byond_z)? as i32 - 1;
+ let frozen = bool::try_from(byond_frozen)?;
+ let buffers = BUFFERS.get().ok_or(eyre!("BUFFERS not initialized."))?;
+ let active = buffers.get_active().read().unwrap();
+ let maybe_z_level = active.0[z as usize].try_write();
+ if maybe_z_level.is_err() {
+ return Err(eyre!(
+ "Tried to freeze or unfreeze during asynchronous, read-only atmos. Use a /datum/milla_safe/..."
+ ));
+ }
+ let mut z_level = maybe_z_level.unwrap();
+ z_level.frozen = frozen;
+ Ok(ByondValue::null())
+}
+
// Yay, tests!
#[cfg(test)]
mod tests {
diff --git a/rust/src/milla/constants.rs b/rust/src/milla/constants.rs
index d0f4d8b9160e..057af66f7fab 100644
--- a/rust/src/milla/constants.rs
+++ b/rust/src/milla/constants.rs
@@ -1,6 +1,5 @@
/// How many Z levels we allow before being suspicious that the wrong number was sent.
-pub(crate) const MAX_Z_LEVELS: i32 = 14;
-
+pub(crate) const MAX_Z_LEVELS: i32 = 19; // SS220 EDIT - upstream value +4
/// How big is the map? Assumed square.
pub(crate) const MAP_SIZE: usize = 255;
@@ -120,7 +119,7 @@ pub(crate) const PLASMA_BURN_MIN_TEMP: f32 = 100.0 + T0C;
pub(crate) const PLASMA_BURN_OPTIMAL_TEMP: f32 = 1370.0 + T0C;
/// How much of the plasma are we willing to burn each tick?
-pub(crate) const PLASMA_BURN_MAX_RATIO: f32 = 0.05;
+pub(crate) const PLASMA_BURN_MAX_RATIO: f32 = 0.01;
/// How much of the plasma do we burn anyway if the ratio would make it really small?
pub(crate) const PLASMA_BURN_MIN_MOLES: f32 = 0.001;
@@ -148,10 +147,10 @@ pub(crate) const TEST_TOLERANCE: f32 = 0.1;
pub(crate) const SPACE_COOLING_THRESHOLD: f32 = T20C;
/// Lose this amount of heat energy per tick if above SPACE_COOLING_THRESHOLD.
-pub(crate) const SPACE_COOLING_FLAT: f32 = 2000.0;
+pub(crate) const SPACE_COOLING_FLAT: f32 = 200.0;
-/// Lose this ratio of heat energy per tick if above SPACE_COOLING_THRESHOLD.
-pub(crate) const SPACE_COOLING_RATIO: f32 = 0.002;
+/// Lose this ratio of heat energy per Kelvin per tick if above SPACE_COOLING_THRESHOLD.
+pub(crate) const SPACE_COOLING_TEMPERATURE_RATIO: f32 = 0.4;
/// Tiles with less than this much gas will become empty.
pub(crate) const MINIMUM_NONZERO_MOLES: f32 = 0.1;
diff --git a/rust/src/milla/model.rs b/rust/src/milla/model.rs
index 4b7ac10036eb..838bb090cce2 100644
--- a/rust/src/milla/model.rs
+++ b/rust/src/milla/model.rs
@@ -401,6 +401,7 @@ impl From<&InterestingTile> for Vec {
pub(crate) struct ZLevel {
tiles: Box<[Tile; MAP_SIZE * MAP_SIZE]>,
pub(crate) active_pressure_chunks: HashSet<(u8, u8)>,
+ pub(crate) frozen: bool,
}
impl ZLevel {
@@ -412,6 +413,7 @@ impl ZLevel {
ZLevel {
tiles: unbuilt.into_boxed_slice().try_into().unwrap(),
active_pressure_chunks: HashSet::new(),
+ frozen: false,
}
}
@@ -455,6 +457,7 @@ impl ZLevel {
self.tiles[i].copy_from(&other.tiles[i]);
}
self.active_pressure_chunks = other.active_pressure_chunks.clone();
+ self.frozen = other.frozen;
}
}
diff --git a/rust/src/milla/simulate.rs b/rust/src/milla/simulate.rs
index befff52558eb..2cdc5ab964d1 100644
--- a/rust/src/milla/simulate.rs
+++ b/rust/src/milla/simulate.rs
@@ -681,7 +681,7 @@ pub(crate) fn apply_tile_mode(
if my_next_tile.temperature() > SPACE_COOLING_THRESHOLD {
let excess_thermal_energy = my_next_tile.thermal_energy
- SPACE_COOLING_THRESHOLD * my_next_tile.heat_capacity();
- let cooling = (SPACE_COOLING_FLAT + SPACE_COOLING_RATIO * excess_thermal_energy)
+ let cooling = (SPACE_COOLING_FLAT + SPACE_COOLING_TEMPERATURE_RATIO * my_next_tile.temperature())
.min(excess_thermal_energy);
my_next_tile.thermal_energy -= cooling;
}
diff --git a/rust/src/milla/tick.rs b/rust/src/milla/tick.rs
index ee197e5788fb..ca0f126beae6 100644
--- a/rust/src/milla/tick.rs
+++ b/rust/src/milla/tick.rs
@@ -95,12 +95,14 @@ pub(crate) fn tick_z_level(
// Initialize the new frame as a copy of the old one.
next.copy_from(&prev);
- simulate::find_walls(&mut next);
- simulate::update_wind(&prev, &mut next);
- simulate::flow_air(&prev, &mut next)?;
- simulate::post_process(&prev, &mut next, &environments, new_interesting_tiles, z)?;
+ if !prev.frozen {
+ simulate::find_walls(&mut next);
+ simulate::update_wind(&prev, &mut next);
+ simulate::flow_air(&prev, &mut next)?;
+ simulate::post_process(&prev, &mut next, &environments, new_interesting_tiles, z)?;
- next.active_pressure_chunks.clear();
+ next.active_pressure_chunks.clear();
+ }
Ok(())
}
diff --git a/rustlibs.dll b/rustlibs.dll
index fd0fde8a12b5..3f6c5876c8d5 100644
Binary files a/rustlibs.dll and b/rustlibs.dll differ
diff --git a/rustlibs_prod.dll b/rustlibs_prod.dll
index e91053a07f08..f24ea8c3c993 100644
Binary files a/rustlibs_prod.dll and b/rustlibs_prod.dll differ
diff --git a/tgui/packages/tgui/interfaces/Autolathe.js b/tgui/packages/tgui/interfaces/Autolathe.js
index 89950bbc6217..5e60f91c1ca6 100644
--- a/tgui/packages/tgui/interfaces/Autolathe.js
+++ b/tgui/packages/tgui/interfaces/Autolathe.js
@@ -123,7 +123,7 @@ export const Autolathe = (props, context) => {
})
}
>
- {toTitleCase(recipe.name)}
+ {recipe.name}
{recipe.max_multiplier >= 10 && (